diff --git a/.claude/skills/generate-v9-models/SKILL.md b/.claude/skills/generate-v9-models/SKILL.md new file mode 100644 index 000000000..cc637ee72 --- /dev/null +++ b/.claude/skills/generate-v9-models/SKILL.md @@ -0,0 +1,132 @@ +--- +description: Generate pyatlan_v9 msgspec model files by cloning the models repo and running the Pkl code generator +--- + +# Generate v9 Models + +Generates pyatlan_v9 msgspec model files from Pkl type definitions in the atlanhq/models repo. + +## Usage + +- `/generate-v9-models` — Clone models@master, generate and sync v9 models +- `/generate-v9-models ` — Clone models@ instead of master +- `/generate-v9-models test` — Also run tests after sync +- `/generate-v9-models test` — Clone models@master and run tests after sync + +## Instructions + +### 1. Clone or update the models repo + +Parse args to determine the branch (default: `master`) and whether to run tests (args contain "test"). + +The models repo should be cloned as a sibling directory of this repo (atlan-python): + +```bash +# Determine paths +SDK_DIR="$(pwd)" # atlan-python root +MODELS_DIR="$(cd .. && pwd)/models" +BRANCH="master" # override with first non-"test" arg + +if [ -d "$MODELS_DIR" ]; then + cd "$MODELS_DIR" && git fetch origin && git checkout "$BRANCH" && git pull origin "$BRANCH" +else + git clone --branch "$BRANCH" --single-branch git@github.com:atlanhq/models.git "$MODELS_DIR" +fi +``` + +### 2. Run Pkl evaluation + +From the models repo root, run the Pkl code generator in **SDK-only mode** (`-p sdkOnly=true`). This generates only Python SDK files — no JSON typedefs, no frontend code, no samples. + +Use a temp directory for staging so no generated files land in the models repo: + +```bash +cd "$MODELS_DIR" +STAGING_DIR="$(mktemp -d)" +OVERLAYS_PATH="${SDK_DIR}/pyatlan_v9/model/assets/_overlays/" + +pkl eval typedefs/*.pkl -m "$STAGING_DIR" -p sdkOnly=true -p sdk=true \ + -p targetOutputDir=pyatlan_v9/model/assets/ \ + -p internalPackage=pyatlan_v9.model \ + -p sdkOverlaysBasePath="$OVERLAYS_PATH" +``` + +- `-p sdkOnly=true` skips JSON typedef generation — only Python SDK files are produced +- `sdkOverlaysBasePath` must be an absolute path — Pkl resolves `read?()` relative to the module file, not CWD +- Output goes to a temp staging directory (nothing written to models repo) + +### 3. Selective sync + +Copy generated files from the staging dir to the SDK, **excluding** these files that have manual patches or are hand-written: + +| File | Reason | +|------|--------| +| `__init__.py` | Hand-written init with `__all__` | +| `entity.py` | Patched: `_metadata_proxies`, `type_name: Any`, `SaveSemantic` | +| `referenceable.py` | Patched: `InternalKeywordField`, field descriptors, helper exports | +| `atlas_glossary.py` | Patched: GTC anchor-in-attributes handling | +| `atlas_glossary_term.py` | Patched: GTC anchor-in-attributes handling | +| `atlas_glossary_category.py` | Patched: GTC anchor-in-attributes handling | +| `quick_sight_dataset.py` | Patched: `useLocalTypeAsPrefix` field naming | +| `quick_sight_dataset_field.py` | Patched: `useLocalTypeAsPrefix` field naming | +| `quick_sight_folder.py` | Patched: `useLocalTypeAsPrefix` field naming | +| `data_quality_rule.py` | Hand-written, not yet generated correctly | + +```bash +rsync -av \ + --exclude='__init__.py' \ + --exclude='entity.py' \ + --exclude='referenceable.py' \ + --exclude='atlas_glossary.py' \ + --exclude='atlas_glossary_term.py' \ + --exclude='atlas_glossary_category.py' \ + --exclude='quick_sight_dataset.py' \ + --exclude='quick_sight_dataset_field.py' \ + --exclude='quick_sight_folder.py' \ + --exclude='data_quality_rule.py' \ + "${STAGING_DIR}/pyatlan_v9/model/assets/" \ + "${SDK_DIR}/pyatlan_v9/model/assets/" + +# Clean up staging dir +rm -rf "$STAGING_DIR" +``` + +16 additional types in pyatlan_v9 (persona.py, purpose.py, badge.py, access_control.py, etc.) are hand-written and NOT generated by Pkl — rsync won't touch them since they don't exist in staging. + +### 4. Post-sync patches + +**related_entity.py** — ensure `relationship_attributes` field exists after `unique_attributes`. This file is not generated in sdkOnly mode, so it persists across regens. If starting from scratch, add: +```python + # Relationship-specific attributes + relationship_attributes: Union[dict[str, Any], None, UnsetType] = UNSET + """Attributes of the relationship itself (e.g., description, status, etc.).""" +``` + +### 5. Run ruff auto-fix and format + +After syncing and patching, run ruff to fix unused imports and format the generated files: + +```bash +cd "${SDK_DIR}" +uv run ruff check --fix --select F401,F811 pyatlan_v9/ +uv run ruff format pyatlan_v9/ +``` + +### 6. Run tests (if args contain "test") + +```bash +cd "${SDK_DIR}" && python -m pytest tests_v9/unit/ -x -q +``` + +### 7. Report summary + +Report: how many files were generated, how many synced, how many excluded, and test results if applicable. + +## Notes + +- The models repo is cloned from `git@github.com:atlanhq/models.git` +- If `../models` already exists, it fetches and checks out the requested branch instead of re-cloning +- `-p sdkOnly=true` ensures only Python SDK files are generated (no JSON typedefs written to models repo) +- Generated files go to a temp staging dir, then are selectively synced to `atlan-python/pyatlan_v9/model/assets/` +- Fields with `useSetType=true` in the Pkl typedefs generate `set[str]` instead of `list[str]` (used for user/group/role fields) +- Overlay files (custom methods like `creator()`, `updater()`, policy helpers) live at `pyatlan_v9/model/assets/_overlays/` in this repo diff --git a/.github/workflows/pyatlan-pr.yaml b/.github/workflows/pyatlan-pr.yaml index 82df00d46..c775fe5f4 100644 --- a/.github/workflows/pyatlan-pr.yaml +++ b/.github/workflows/pyatlan-pr.yaml @@ -1,11 +1,10 @@ name: Pyatlan Pull Request Build # This workflow runs both sync and async integration tests intelligently: -# - Sync integration tests: Always run on every PR -# - Async integration tests: Only run when: -# 1. Changes detected in pyatlan/*/aio/ or tests/*/aio/ paths -# 2. PR has the "run-async-tests" label (manual trigger) -# This prevents adding 12+ minutes to every PR while ensuring async tests run when needed. +# - Legacy sync integration tests: Always run on every PR with code changes +# - Legacy async integration tests: Only run when AIO changes detected or "run-async-tests" label +# - V9 unit tests: Always run on every PR with code changes +# - V9 integration tests (sync + async): Only run when PR has the "run_pyatlan_v9_integration_tests" label on: pull_request: @@ -117,6 +116,28 @@ jobs: echo "⏭️ No AIO changes detected and no manual trigger label found" fi + check-v9-integration-label: + runs-on: ubuntu-latest + outputs: + run-v9-integration: ${{ steps.check-label.outputs.run-v9-integration }} + steps: + - name: Check for v9 integration test label + id: check-label + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + echo "run-v9-integration=true" >> $GITHUB_OUTPUT + echo "Manual trigger: running v9 integration tests" + exit 0 + fi + + if echo '${{ toJson(github.event.pull_request.labels.*.name) }}' | grep -q "run_pyatlan_v9_integration_tests"; then + echo "run-v9-integration=true" >> $GITHUB_OUTPUT + echo "Found 'run_pyatlan_v9_integration_tests' label" + else + echo "run-v9-integration=false" >> $GITHUB_OUTPUT + echo "No 'run_pyatlan_v9_integration_tests' label found, skipping v9 integration tests" + fi + qa-checks-and-unit-tests: needs: [check-code-changes, vulnerability-scan] if: needs.check-code-changes.outputs.has-code-changes == 'true' @@ -260,3 +281,153 @@ jobs: # Run the async integration test file using `pytest-timer` plugin # to display only the durations of the 10 slowest tests with `pytest-sugar` command: uv run pytest ${{ matrix.test_file }} -p name_of_plugin --timer-top-n 10 --force-sugar -vv + + # ========================================================================= + # V9 (msgspec) Jobs + # ========================================================================= + + v9-qa-checks-and-unit-tests: + needs: [check-code-changes, vulnerability-scan] + if: needs.check-code-changes.outputs.has-code-changes == 'true' + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install uv + uses: astral-sh/setup-uv@v6 + + - name: Install dependencies + run: uv sync --group dev + + - name: QA checks (ruff-format, ruff-lint, mypy) + run: uv run ./qa-checks + + - name: Run v9 unit tests + env: + ATLAN_API_KEY: ${{ secrets.ATLAN_API_KEY }} + ATLAN_BASE_URL: ${{ secrets.ATLAN_BASE_URL }} + run: uv run pytest tests_v9/unit --force-sugar -vv + + v9-prepare-integration-tests: + needs: [check-code-changes, vulnerability-scan, check-v9-integration-label] + if: >- + needs.check-code-changes.outputs.has-code-changes == 'true' && + needs.check-v9-integration-label.outputs.run-v9-integration == 'true' + runs-on: ubuntu-latest + outputs: + v9-files: ${{ steps.distribute-v9-files.outputs.v9-files }} + v9-aio-files: ${{ steps.distribute-v9-aio-files.outputs.v9-aio-files }} + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Prepare v9 sync integration tests distribution + id: distribute-v9-files + run: | + files=$(find tests_v9/integration -maxdepth 1 \( -name "test_*.py" -o -name "*_test.py" \) | sort | tr '\n' ' ') + if [ -n "$files" ]; then + json_files=$(echo "${files[@]}" | jq -R -c 'split(" ")[:-1]') + else + json_files="[]" + fi + echo "v9-files=$json_files" >> $GITHUB_OUTPUT + echo "V9 sync integration test files: $json_files" + + - name: Prepare v9 async integration tests distribution + id: distribute-v9-aio-files + run: | + if [ -d "tests_v9/integration/aio" ]; then + aio_files=$(find tests_v9/integration/aio -name "test_*.py" | sort | tr '\n' ' ') + if [ -n "$aio_files" ]; then + json_aio_files=$(echo "${aio_files[@]}" | jq -R -c 'split(" ")[:-1]') + else + json_aio_files="[]" + fi + else + json_aio_files="[]" + fi + echo "v9-aio-files=$json_aio_files" >> $GITHUB_OUTPUT + echo "V9 async integration test files: $json_aio_files" + + v9-integration-tests: + needs: [v9-prepare-integration-tests] + if: needs.v9-prepare-integration-tests.outputs.v9-files != '[]' + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + test_file: ${{fromJson(needs.v9-prepare-integration-tests.outputs.v9-files)}} + concurrency: + group: v9-${{ matrix.test_file }} + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install uv + uses: astral-sh/setup-uv@v6 + + - name: Install dependencies + run: uv sync --group dev + + - name: Run v9 integration test + env: + ATLAN_API_KEY: ${{ secrets.ATLAN_API_KEY }} + ATLAN_BASE_URL: ${{ secrets.ATLAN_BASE_URL }} + uses: nick-fields/retry@v3 + with: + max_attempts: 3 + timeout_minutes: 10 + command: uv run pytest ${{ matrix.test_file }} -p name_of_plugin --timer-top-n 10 --force-sugar -vv + + v9-async-integration-tests: + needs: [v9-prepare-integration-tests] + if: needs.v9-prepare-integration-tests.outputs.v9-aio-files != '[]' + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + test_file: ${{fromJson(needs.v9-prepare-integration-tests.outputs.v9-aio-files)}} + concurrency: + group: v9-async-${{ matrix.test_file }} + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install uv + uses: astral-sh/setup-uv@v6 + + - name: Install dependencies + run: uv sync --group dev + + - name: Run v9 async integration test + env: + ATLAN_API_KEY: ${{ secrets.ATLAN_API_KEY }} + ATLAN_BASE_URL: ${{ secrets.ATLAN_BASE_URL }} + uses: nick-fields/retry@v3 + with: + max_attempts: 3 + timeout_minutes: 15 + command: uv run pytest ${{ matrix.test_file }} -p name_of_plugin --timer-top-n 10 --force-sugar -vv diff --git a/README.md b/README.md index 8101c1ec6..41fe55ab8 100644 --- a/README.md +++ b/README.md @@ -218,6 +218,44 @@ This will: - 🎨 Format code automatically - ⚡ Support incremental updates +## 🏗️ pyatlan_v9 Model Generation (msgspec) + +The `pyatlan_v9` package uses [msgspec](https://jcristharris.com/msgspec/) `Struct`-based models generated from Pkl type definitions in the [atlanhq/models](https://github.com/atlanhq/models) repo. + +### Using Claude Code + +The recommended way to regenerate models is via the Claude Code skill: + +```bash +# From the atlan-python repo root: +/generate-v9-models # Generate from models@master +/generate-v9-models # Generate from a specific models branch +/generate-v9-models test # Generate and run tests +/generate-v9-models test +``` + +The skill will: +1. Clone/update `atlanhq/models` at `../models` +2. Run the Pkl code generator with SDK mode (`pkl eval typedefs/*.pkl -m . -p sdk=true`) +3. Selectively sync generated files to `pyatlan_v9/model/assets/` (excluding hand-written types) +4. Apply post-sync patches (e.g., `set[str]` fields in `asset.py`) +5. Optionally run `tests_v9/unit/` tests + +### Overlay Files + +Custom methods (`creator()`, `updater()`, policy helpers, etc.) live in `pyatlan_v9/model/assets/_overlays/`. These are Python files read by the Pkl renderer and injected into generated classes. Each overlay file uses import directives: + +- `# IMPORT:` — external imports (not remapped) +- `# INTERNAL_IMPORT:` — internal imports (remapped to `pyatlan_v9.*`) +- `# STDLIB_IMPORT:` — standard library imports + +### Hand-written Types + +Some types are not yet fully generated and are maintained by hand: +- Infrastructure: `__init__.py`, `entity.py`, `referenceable.py` +- GTC types: `atlas_glossary.py`, `atlas_glossary_term.py`, `atlas_glossary_category.py` +- Others: `persona.py`, `purpose.py`, `badge.py`, `access_control.py`, `auth_policy.py`, etc. + ## 📁 Project Structure Understanding the codebase layout will help you navigate and contribute effectively: diff --git a/pyatlan/test_utils/base_vcr.py b/pyatlan/test_utils/base_vcr.py index 07a0bf9c4..f6b79b81c 100644 --- a/pyatlan/test_utils/base_vcr.py +++ b/pyatlan/test_utils/base_vcr.py @@ -265,7 +265,11 @@ def vcr_cassette_dir(self, request): :returns: directory path for storing cassettes """ - # Set self._CASSETTES_DIR or use the default directory path based on the test module name - return self._CASSETTES_DIR or os.path.join( - "tests/vcr_cassettes", request.module.__name__ + # Set self._CASSETTES_DIR or use the default directory path based on the test module name. + # V9 tests (module name starting with tests_v9) use tests_v9/vcr_cassettes; legacy use tests/vcr_cassettes. + root = ( + "tests_v9/vcr_cassettes" + if request.module.__name__.startswith("tests_v9") + else "tests/vcr_cassettes" ) + return self._CASSETTES_DIR or os.path.join(root, request.module.__name__) diff --git a/pyatlan_v9/__init__.py b/pyatlan_v9/__init__.py new file mode 100644 index 000000000..411e5d9ea --- /dev/null +++ b/pyatlan_v9/__init__.py @@ -0,0 +1,48 @@ +# Auto-generated by PythonMsgspecRenderer.pkl +# NOTE: Additional exports (CheckpointStore, compute_content_hash) added manually +""" +PyAtlan V9 - Python SDK for Atlan's Atlas API using msgspec. + +This SDK provides type-safe access to Atlan's Atlas API using msgspec Structs +with flattened attributes for a more Pythonic developer experience. + +Example: + from pyatlan_v9.model import Database, Table + + db = Database( + name="test_database", + qualified_name="default/snowflake/123/test_database", + ) + json_str = db.to_json() +""" + +__all__ = [] +__version__ = "0.1.0" + +# Optional imports - these may have additional dependencies (re-exported via __all__) +try: + from pyatlan_v9.checkpoint import ( # noqa: F401 + BatchContext, + CheckpointStore, + checkpoint_exists, + cleanup_incomplete_checkpoint, + compute_content_hash, + copy_checkpoint, + parse_epoch_value, + swap_checkpoints, + ) + + __all__.extend( + [ + "BatchContext", + "CheckpointStore", + "checkpoint_exists", + "cleanup_incomplete_checkpoint", + "compute_content_hash", + "copy_checkpoint", + "parse_epoch_value", + "swap_checkpoints", + ] + ) +except ImportError: + pass # lmdb not installed diff --git a/pyatlan_v9/client/__init__.py b/pyatlan_v9/client/__init__.py new file mode 100644 index 000000000..ea79d82cf --- /dev/null +++ b/pyatlan_v9/client/__init__.py @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +""" +PyAtlan V9 Client - HTTP client for Atlan's Atlas API. + +This module provides the AtlanClient class (plain Python, no Pydantic BaseSettings) +that manages HTTP sessions, proxy/SSL configuration, and sub-client access. +""" + +from pyatlan_v9.client.atlan import AtlanClient, client_connection + +__all__ = [ + "AtlanClient", + "client_connection", +] diff --git a/pyatlan_v9/client/admin.py b/pyatlan_v9/client/admin.py new file mode 100644 index 000000000..2d3256843 --- /dev/null +++ b/pyatlan_v9/client/admin.py @@ -0,0 +1,87 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +from __future__ import annotations + +import msgspec + +from pyatlan.client.common import AdminGetAdminEvents, AdminGetKeycloakEvents, ApiCaller +from pyatlan.errors import ErrorCode +from pyatlan_v9.model.keycloak_events import ( + AdminEvent, + AdminEventRequest, + AdminEventResponse, + KeycloakEvent, + KeycloakEventRequest, + KeycloakEventResponse, +) +from pyatlan_v9.validate import validate_arguments + + +class V9AdminClient: + """ + This class can be used to retrieve keycloak and admin events. This class does not need to be instantiated + directly but can be obtained through the admin property of AtlanClient. + """ + + def __init__(self, client: ApiCaller): + if not isinstance(client, ApiCaller): + raise ErrorCode.INVALID_PARAMETER_TYPE.exception_with_parameters( + "client", "ApiCaller" + ) + self._client = client + + @validate_arguments + def get_keycloak_events( + self, keycloak_request: KeycloakEventRequest + ) -> KeycloakEventResponse: + """ + Retrieve all events, based on the supplied filters. + + :param keycloak_request: details of the filters to apply when retrieving events + :returns: the events that match the supplied filters + :raises AtlanError: on any API communication issue + """ + endpoint, query_params = AdminGetKeycloakEvents.prepare_request( + keycloak_request + ) + raw_json = self._client._call_api( + endpoint, + query_params=query_params, + ) + if raw_json: + events = msgspec.convert(raw_json, list[KeycloakEvent], strict=False) + else: + events = [] + + return KeycloakEventResponse( + client=self._client, + criteria=keycloak_request, + start=keycloak_request.offset or 0, + size=keycloak_request.size or 100, + events=events, + ) + + @validate_arguments + def get_admin_events(self, admin_request: AdminEventRequest) -> AdminEventResponse: + """ + Retrieve admin events based on the supplied filters. + + :param admin_request: details of the filters to apply when retrieving admin events + :returns: the admin events that match the supplied filters + :raises AtlanError: on any API communication issue + """ + endpoint, query_params = AdminGetAdminEvents.prepare_request(admin_request) + raw_json = self._client._call_api(endpoint, query_params=query_params) + if raw_json: + events = msgspec.convert(raw_json, list[AdminEvent], strict=False) + else: + events = [] + + return AdminEventResponse( + client=self._client, + criteria=admin_request, + start=admin_request.offset or 0, + size=admin_request.size or 100, + events=events, + ) diff --git a/pyatlan_v9/client/aio/__init__.py b/pyatlan_v9/client/aio/__init__.py new file mode 100644 index 000000000..8bb6d8f85 --- /dev/null +++ b/pyatlan_v9/client/aio/__init__.py @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. +"""v9 async client wrappers.""" + +from pyatlan_v9.client.aio.asset import V9AsyncAssetClient +from pyatlan_v9.client.aio.atlan import AsyncAtlanClient +from pyatlan_v9.client.aio.group import V9AsyncGroupClient + +__all__ = ["AsyncAtlanClient", "V9AsyncAssetClient", "V9AsyncGroupClient"] diff --git a/pyatlan_v9/client/aio/admin.py b/pyatlan_v9/client/aio/admin.py new file mode 100644 index 000000000..dde6539d7 --- /dev/null +++ b/pyatlan_v9/client/aio/admin.py @@ -0,0 +1,95 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +from __future__ import annotations + +import msgspec + +from pyatlan.client.common import ( + AdminGetAdminEvents, + AdminGetKeycloakEvents, + AsyncApiCaller, +) +from pyatlan.errors import ErrorCode +from pyatlan_v9.model.aio.keycloak_events import ( + AsyncAdminEventResponse, + AsyncKeycloakEventResponse, +) +from pyatlan_v9.model.keycloak_events import ( + AdminEvent, + AdminEventRequest, + KeycloakEvent, + KeycloakEventRequest, +) +from pyatlan_v9.validate import validate_arguments + + +class V9AsyncAdminClient: + """ + Async version of AdminClient for retrieving keycloak and admin events. This class does not need to be instantiated + directly but can be obtained through the admin property of the async Atlan client. + """ + + def __init__(self, client: AsyncApiCaller): + if not isinstance(client, AsyncApiCaller): + raise ErrorCode.INVALID_PARAMETER_TYPE.exception_with_parameters( + "client", "AsyncApiCaller" + ) + self._client = client + + @validate_arguments + async def get_keycloak_events( + self, keycloak_request: KeycloakEventRequest + ) -> AsyncKeycloakEventResponse: + """ + Retrieve all events, based on the supplied filters. + + :param keycloak_request: details of the filters to apply when retrieving events + :returns: the events that match the supplied filters + :raises AtlanError: on any API communication issue + """ + endpoint, query_params = AdminGetKeycloakEvents.prepare_request( + keycloak_request + ) + raw_json = await self._client._call_api( + endpoint, + query_params=query_params, + ) + if raw_json: + events = msgspec.convert(raw_json, list[KeycloakEvent], strict=False) + else: + events = [] + + return AsyncKeycloakEventResponse( + client=self._client, + criteria=keycloak_request, + start=keycloak_request.offset or 0, + size=keycloak_request.size or 100, + events=events, + ) + + @validate_arguments + async def get_admin_events( + self, admin_request: AdminEventRequest + ) -> AsyncAdminEventResponse: + """ + Retrieve admin events based on the supplied filters. + + :param admin_request: details of the filters to apply when retrieving admin events + :returns: the admin events that match the supplied filters + :raises AtlanError: on any API communication issue + """ + endpoint, query_params = AdminGetAdminEvents.prepare_request(admin_request) + raw_json = await self._client._call_api(endpoint, query_params=query_params) + if raw_json: + events = msgspec.convert(raw_json, list[AdminEvent], strict=False) + else: + events = [] + + return AsyncAdminEventResponse( + client=self._client, + criteria=admin_request, + start=admin_request.offset or 0, + size=admin_request.size or 100, + events=events, + ) diff --git a/pyatlan_v9/client/aio/asset.py b/pyatlan_v9/client/aio/asset.py new file mode 100644 index 000000000..cd78ca019 --- /dev/null +++ b/pyatlan_v9/client/aio/asset.py @@ -0,0 +1,2105 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +from __future__ import annotations + +import asyncio +import json +import logging +from typing import ( + TYPE_CHECKING, + Awaitable, + Callable, + List, + Optional, + Type, + TypeVar, + Union, + overload, +) +from warnings import warn + +import msgspec +from tenacity import ( + retry, + retry_if_exception_type, + stop_after_attempt, + wait_exponential, +) + +from pyatlan.client.asset import CategoryHierarchy +from pyatlan.client.common import ( + DeleteByGuid, + FindCategoryFastByName, + FindConnectionsByName, + FindDomainByName, + FindGlossaryByName, + FindProductByName, + FindTermFastByName, + GetByGuid, + GetByQualifiedName, + GetHierarchy, + GetLineageList, + ManageCustomMetadata, + ManageTerms, + PurgeByGuid, + RemoveAnnouncement, + RemoveCertificate, + RemoveCustomMetadata, + ReplaceCustomMetadata, + RestoreAsset, + Search, + UpdateAnnouncement, + UpdateAsset, + UpdateAssetByAttribute, + UpdateCertificate, + UpdateCustomMetadataAttributes, +) +from pyatlan.client.constants import BULK_UPDATE, DELETE_ENTITIES_BY_GUIDS +from pyatlan.errors import ErrorCode, NotFoundError, PermissionError +from pyatlan.model.aio import AsyncIndexSearchResults, AsyncLineageListResults +from pyatlan.model.fields.atlan_fields import AtlanField +from pyatlan.utils import unflatten_custom_metadata_for_entity +from pyatlan_v9.client.asset import ( + _handle_v9_glossary_anchor, + _is_glossary_category, + _matches_asset_type, + _process_find_response_v9, +) +from pyatlan_v9.model.aggregation import Aggregations +from pyatlan_v9.model.aio.core import AsyncAtlanRequest +from pyatlan_v9.model.assets import ( + Asset, + AtlasGlossary, + AtlasGlossaryCategory, + AtlasGlossaryTerm, + Connection, + DataDomain, + DataProduct, + Persona, + Purpose, +) +from pyatlan_v9.model.core import ( + Announcement, + AtlanRequest, + AtlanTag, + AtlanTagName, + BulkRequest, +) +from pyatlan_v9.model.custom_metadata import CustomMetadataDict +from pyatlan_v9.model.enums import ( + AtlanConnectorType, + AtlanDeleteType, + CertificateStatus, + DataQualityScheduleType, + EntityStatus, + SaveSemantic, + SortOrder, +) +from pyatlan_v9.model.lineage import LineageListRequest +from pyatlan_v9.model.response import AssetMutationResponse, MutatedEntities +from pyatlan_v9.model.search import IndexSearchRequest, Query +from pyatlan_v9.model.transform import from_atlas_format +from pyatlan_v9.validate import validate_arguments + +if TYPE_CHECKING: + pass + +LOGGER = logging.getLogger(__name__) + +A = TypeVar("A", bound=Asset) + + +async def _process_search_results_v9_async( + results, name: str, asset_type, allow_multiple: bool = False +): + """Async v9-aware replacement for ``SearchForAssetWithName.process_async_search_results``.""" + import logging + + _LOGGER = logging.getLogger(__name__) + + if results and results.count > 0: + current_page = ( + results.current_page() if hasattr(results, "current_page") else None + ) + if current_page: + assets = [ + asset + for asset in current_page + if _matches_asset_type(asset, asset_type) + ] + else: + assets = [] + async for asset in results: + if _matches_asset_type(asset, asset_type): + assets.append(asset) + + if assets: + if not allow_multiple and len(assets) > 1: + _LOGGER.warning( + "More than 1 %s found with the name '%s', returning only the first.", + asset_type.__name__, + name, + ) + return assets + + raise ErrorCode.ASSET_NOT_FOUND_BY_NAME.exception_with_parameters( + asset_type.__name__, name + ) + + +async def _process_hierarchy_v9_async(response, glossary) -> CategoryHierarchy: + """Async v9-aware replacement for ``GetHierarchy.process_async_search_results``.""" + top_categories: set = set() + category_dict = {} + + if hasattr(response, "current_page") and response.current_page(): + categories = [ + asset for asset in response.current_page() if _is_glossary_category(asset) + ] + else: + categories = [] + async for asset in response: + if _is_glossary_category(asset): + categories.append(asset) + + for category in categories: + guid = category.guid + if not getattr(category, "children_categories", None): + category.children_categories = None + category_dict[guid] = category + if not category.parent_category: + top_categories.add(guid) + + if not top_categories: + raise ErrorCode.NO_CATEGORIES.exception_with_parameters( + glossary.guid, glossary.qualified_name + ) + + return CategoryHierarchy(top_level=top_categories, stub_dict=category_dict) + + +# --------------------------------------------------------------------------- +# v9-native response helpers (raw JSON -> v9 msgspec assets) +# --------------------------------------------------------------------------- + + +def _custom_metadata_payload(custom_metadata_request): + """Normalize custom metadata request wrappers to raw payload dictionaries.""" + if hasattr(custom_metadata_request, "to_dict") and callable( + custom_metadata_request.to_dict + ): + return custom_metadata_request.to_dict() + root_payload = getattr(custom_metadata_request, "__root__", None) + if root_payload is not None: + return root_payload + if hasattr(custom_metadata_request, "dict") and callable( + custom_metadata_request.dict + ): + return custom_metadata_request.dict(by_alias=True, exclude_none=True) + return custom_metadata_request + + +def _parse_entities_v9(entities: list, criteria=None) -> list: + """Parse raw entity dicts into v9 msgspec assets.""" + attributes = getattr(criteria, "attributes", None) + for entity in entities: + unflatten_custom_metadata_for_entity(entity=entity, attributes=attributes) + return [from_atlas_format(e) for e in entities] + + +def _parse_mutation_response(raw_json: dict) -> AssetMutationResponse: + """Build a v9 ``AssetMutationResponse`` from raw API JSON.""" + mutated = None + if me_raw := raw_json.get("mutatedEntities"): + mutated = MutatedEntities( + CREATE=( + _parse_entities_v9(me_raw["CREATE"]) if me_raw.get("CREATE") else None + ), + UPDATE=( + _parse_entities_v9(me_raw["UPDATE"]) if me_raw.get("UPDATE") else None + ), + DELETE=( + _parse_entities_v9(me_raw["DELETE"]) if me_raw.get("DELETE") else None + ), + PARTIAL_UPDATE=( + _parse_entities_v9(me_raw["PARTIAL_UPDATE"]) + if me_raw.get("PARTIAL_UPDATE") + else None + ), + ) + return AssetMutationResponse( + guid_assignments=raw_json.get("guidAssignments"), + mutated_entities=mutated, + partial_updated_entities=( + _parse_entities_v9(raw_json["partialUpdatedEntities"]) + if raw_json.get("partialUpdatedEntities") + else None + ), + ) + + +def _parse_aggregations_v9(raw: dict) -> Aggregations: + """Convert raw aggregation JSON into a v9 ``Aggregations`` wrapper.""" + from pyatlan_v9.model.aggregation import ( + AggregationBucketResult, + AggregationHitsResult, + AggregationMetricResult, + ) + + def _parse_nested(bucket_dict: dict) -> "Aggregations | None": + """Parse nested aggregation results inside a bucket, recursively.""" + nested: dict = {} + known_keys = { + "key", + "doc_count", + "key_as_string", + "max_matching_length", + "to", + "to_as_string", + "from", + "from_as_string", + } + for k, v in bucket_dict.items(): + if k in known_keys or not isinstance(v, dict): + continue + try: + if "buckets" in v: + result = msgspec.convert(v, AggregationBucketResult, strict=False) + raw_inner = v.get("buckets", []) + for i, inner in enumerate(result.buckets): + if i < len(raw_inner): + try: + inner.nested_results = _parse_nested(raw_inner[i]) + except Exception: + pass + nested[k] = result + elif "hits" in v: + nested[k] = msgspec.convert(v, AggregationHitsResult, strict=False) + elif "value" in v: + nested[k] = msgspec.convert( + v, AggregationMetricResult, strict=False + ) + except Exception: + pass + return Aggregations(data=nested) if nested else None + + parsed: dict = {} + for key, value in raw.items(): + if not isinstance(value, dict): + continue + try: + if "buckets" in value: + result = msgspec.convert(value, AggregationBucketResult, strict=False) + raw_buckets = value.get("buckets", []) + for i, bucket in enumerate(result.buckets): + if i < len(raw_buckets): + try: + bucket.nested_results = _parse_nested(raw_buckets[i]) + except Exception: + pass + parsed[key] = result + elif "hits" in value: + parsed[key] = msgspec.convert( + value, AggregationHitsResult, strict=False + ) + elif "value" in value: + parsed[key] = msgspec.convert( + value, AggregationMetricResult, strict=False + ) + except Exception: + pass + return Aggregations(data=parsed) + + +def _process_search_response_v9(raw_json: dict, criteria) -> dict: + """Process a search API response into v9 msgspec assets.""" + if "entities" in raw_json: + assets = _parse_entities_v9(raw_json["entities"], criteria) + else: + assets = [] + + aggregations = None + if "aggregations" in raw_json: + try: + aggs = _parse_aggregations_v9(raw_json["aggregations"]) + if aggs._data: + aggregations = aggs + except Exception: + pass + + return { + "assets": assets, + "aggregations": aggregations, + "count": raw_json.get("approximateCount", 0), + } + + +def _process_get_response_v9( + raw_json: dict, identifier: str, asset_type, *, by_guid: bool = False +): + """Process a get-by-guid/get-by-qualified-name API response into a v9 asset.""" + entity = raw_json["entity"] + + if not by_guid and entity.get("typeName") != asset_type.__name__: + raise ErrorCode.ASSET_NOT_FOUND_BY_NAME.exception_with_parameters( + asset_type.__name__, identifier + ) + + if entity.get("relationshipAttributes"): + entity.setdefault("attributes", {}).update(entity["relationshipAttributes"]) + entity["relationshipAttributes"] = {} + + asset = from_atlas_format(entity) + asset.is_incomplete = False + + if not isinstance(asset, asset_type): + if by_guid: + raise ErrorCode.ASSET_NOT_TYPE_REQUESTED.exception_with_parameters( + identifier, asset_type.__name__ + ) + else: + raise ErrorCode.ASSET_NOT_FOUND_BY_NAME.exception_with_parameters( + asset_type.__name__, identifier + ) + return asset + + +def _process_lineage_response_v9(raw_json: dict, lineage_request) -> dict: + """Process a lineage list API response into v9 msgspec assets.""" + if "entities" in raw_json: + assets = _parse_entities_v9(raw_json["entities"], lineage_request) + has_more = bool(raw_json.get("hasMore", False)) + else: + assets = [] + has_more = False + return {"assets": assets, "has_more": has_more} + + +# --------------------------------------------------------------------------- +# V9-native async search result subclasses +# --------------------------------------------------------------------------- + + +class V9AsyncIndexSearchResults(AsyncIndexSearchResults): + """AsyncIndexSearchResults that deserializes pages into v9 msgspec assets.""" + + def _process_entities(self, entities): + self._assets = _parse_entities_v9(entities, self._criteria) + + +class V9AsyncLineageListResults(AsyncLineageListResults): + """AsyncLineageListResults that deserializes pages into v9 msgspec assets.""" + + async def next_page(self, start=None, size=None) -> bool: + if not self._has_more: + return False + + self._start = start or self._start + self._size + if size: + self._size = size + + self._criteria.offset = self._start + self._criteria.size = self._size + + endpoint, request_obj = GetLineageList.prepare_request(self._criteria) + raw_json = await self._client._call_api(endpoint, request_obj=request_obj) + + if "entities" in raw_json: + self._assets = _parse_entities_v9(raw_json["entities"], self._criteria) + self._has_more = bool(raw_json.get("hasMore", False)) + else: + self._assets = [] + self._has_more = False + + return self._has_more + + +def _make_bulk_request_payload(entities: list, client) -> dict: + """Serialize a list of Asset entities into an API-ready dict, + applying AtlanTag retranslation (human names -> internal IDs). + """ + bulk = BulkRequest(entities=entities) + request_dict = bulk.to_dict() + retranslated = AtlanRequest(instance=request_dict, client=client) + return retranslated.translated + + +async def _make_bulk_request_payload_async(entities: list, client) -> dict: + """Async version: serialize entities into API-ready dict with tag retranslation.""" + from pyatlan_v9.client.asset import _normalize_meanings_for_mutation + + bulk = BulkRequest(entities=entities) + request_dict = bulk.to_dict() + for entity in request_dict.get("entities", []): + _normalize_meanings_for_mutation(entity) + async_request = AsyncAtlanRequest(instance=request_dict, client=client) + await async_request.retranslate() + return async_request.translated + + +def _make_asset_request_payload(asset: Asset, client) -> dict: + """Serialize a single Asset entity into an API-ready dict, + applying AtlanTag retranslation. + """ + asset_dict = {"entity": json.loads(asset.to_json(nested=True))} + retranslated = AtlanRequest(instance=asset_dict, client=client) + return retranslated.translated + + +async def _make_asset_request_payload_async(asset: Asset, client) -> dict: + """Async version: serialize a single Asset entity into API-ready dict.""" + asset_dict = {"entity": json.loads(asset.to_json(nested=True))} + async_request = AsyncAtlanRequest(instance=asset_dict, client=client) + await async_request.retranslate() + return async_request.translated + + +# --------------------------------------------------------------------------- +# V9 Async Asset Client (standalone) +# --------------------------------------------------------------------------- + + +class V9AsyncAssetClient: + """ + Async asset client for the v9 SDK. + + All methods return v9 msgspec asset types. This is a standalone + implementation — no wrapping or delegation to the legacy async client. + """ + + def __init__(self, client): + self._client = client + + # ------------------------------------------------------------------ + # Search + # ------------------------------------------------------------------ + + async def search( + self, criteria: IndexSearchRequest, bulk=False + ) -> V9AsyncIndexSearchResults: + """ + Search for assets using the provided criteria. + + :param criteria: detailing the search query, parameters, and so on to run + :param bulk: whether to run the search to retrieve assets that match the supplied criteria, + for large numbers of results (> ``100,000``), defaults to ``False``. + :raises InvalidRequestError: if bulk search is enabled and user-specified sorting is found + :raises AtlanError: on any API communication issue + :returns: the results of the search + """ + endpoint, request_obj = Search.prepare_request(criteria, bulk) + raw_json = await self._client._call_api( + endpoint, + request_obj=request_obj, + ) + response = _process_search_response_v9(raw_json, criteria) + + if Search._check_for_bulk_search( + criteria, response["count"], bulk, V9AsyncIndexSearchResults + ): + return await self.search(criteria) + + return V9AsyncIndexSearchResults( + self._client, + criteria, + criteria.dsl.from_, + criteria.dsl.size, + response["count"], + response["assets"], + response["aggregations"], + bulk, + ) + + # ------------------------------------------------------------------ + # Lineage + # ------------------------------------------------------------------ + + async def get_lineage_list( + self, lineage_request: LineageListRequest + ) -> V9AsyncLineageListResults: + """ + Retrieve lineage using the higher-performance "list" API. + + :param lineage_request: detailing the lineage query, parameters, and so on to run + :returns: the results of the lineage request + :raises InvalidRequestError: if the requested lineage direction is 'BOTH' + :raises AtlanError: on any API communication issue + """ + endpoint, request_obj = GetLineageList.prepare_request(lineage_request) + raw_json = await self._client._call_api(endpoint, request_obj=request_obj) + response = _process_lineage_response_v9(raw_json, lineage_request) + return V9AsyncLineageListResults( + client=self._client, + criteria=lineage_request, + start=lineage_request.offset or 0, + size=lineage_request.size or 10, + has_more=response["has_more"], + assets=response["assets"], + ) + + # ------------------------------------------------------------------ + # Find by name helpers + # ------------------------------------------------------------------ + + def _prepare_fluent_search( + self, + wheres: List[Query], + attributes: Optional[List[str]] = None, + related_attributes: Optional[List[str]] = None, + ): + from pyatlan_v9.model.fluent_search import FluentSearch + + search = FluentSearch() + for w in wheres: + search = search.where(w) + for attr in attributes or []: + search = search.include_on_results(attr) + for rel_attr in related_attributes or []: + search = search.include_on_relations(rel_attr) + return search + + def _build_find_request( + self, + name: str, + type_name: str, + attributes: Optional[List[str]] = None, + ) -> IndexSearchRequest: + from pyatlan.model.search import Term + from pyatlan_v9.model.search import DSL as V9DSL + + if attributes is None: + attributes = [] + query = ( + Term.with_state("ACTIVE") + + Term.with_type_name(type_name) + + Term.with_name(name) + ) + dsl = V9DSL(query=query) + return IndexSearchRequest( + dsl=dsl, attributes=attributes, relation_attributes=["name"] + ) + + @validate_arguments + async def find_personas_by_name( + self, + name: str, + attributes: Optional[List[str]] = None, + ) -> List[Persona]: + """ + Find a persona by its human-readable name. + + :param name: of the persona + :param attributes: (optional) collection of attributes to retrieve for the persona + :returns: all personas with that name, if found + :raises NotFoundError: if no persona with the provided name exists + """ + search_request = self._build_find_request(name, "PERSONA", attributes) + search_results = await self.search(search_request) + return _process_find_response_v9( + search_results, name, Persona, allow_multiple=True + ) + + @validate_arguments + async def find_purposes_by_name( + self, + name: str, + attributes: Optional[List[str]] = None, + ) -> List[Purpose]: + """ + Find a purpose by its human-readable name. + + :param name: of the purpose + :param attributes: (optional) collection of attributes to retrieve for the purpose + :returns: all purposes with that name, if found + :raises NotFoundError: if no purpose with the provided name exists + """ + search_request = self._build_find_request(name, "PURPOSE", attributes) + search_results = await self.search(search_request) + return _process_find_response_v9( + search_results, name, Purpose, allow_multiple=True + ) + + # ------------------------------------------------------------------ + # Get by qualified name / GUID + # ------------------------------------------------------------------ + + @validate_arguments(config=dict(arbitrary_types_allowed=True)) + async def get_by_qualified_name( + self, + qualified_name: str, + asset_type: Type[A], + min_ext_info: bool = False, + ignore_relationships: bool = True, + attributes: Optional[Union[List[str], List[AtlanField]]] = None, + related_attributes: Optional[Union[List[str], List[AtlanField]]] = None, + ) -> A: + """ + Retrieves an asset by its qualified_name. + + :param qualified_name: qualified_name of the asset to be retrieved + :param asset_type: type of asset to be retrieved + :param min_ext_info: whether to minimize extra info (True) or not (False) + :param ignore_relationships: whether to include relationships (False) or exclude them (True) + :param attributes: a specific list of attributes to retrieve for the asset + :param related_attributes: a specific list of relationships attributes to retrieve for the asset + :returns: the requested asset + :raises NotFoundError: if the asset does not exist + :raises AtlanError: on any API communication issue + """ + normalized_attributes = GetByQualifiedName.normalize_search_fields(attributes) + normalized_related_attributes = GetByQualifiedName.normalize_search_fields( + related_attributes + ) + + if (normalized_attributes and len(normalized_attributes)) or ( + normalized_related_attributes and len(normalized_related_attributes) + ): + search = self._prepare_fluent_search( + wheres=[ + Asset.QUALIFIED_NAME.eq(qualified_name), + Asset.TYPE_NAME.eq(asset_type.__name__), + ], + attributes=normalized_attributes, + related_attributes=normalized_related_attributes, + ) + results = await search.execute_async(client=self._client) + if results and results.current_page(): + first_result = results.current_page()[0] + if isinstance(first_result, asset_type): + return first_result + raise ErrorCode.ASSET_NOT_FOUND_BY_NAME.exception_with_parameters( + asset_type.__name__, qualified_name + ) + raise ErrorCode.ASSET_NOT_FOUND_BY_QN.exception_with_parameters( + qualified_name, asset_type.__name__ + ) + + endpoint_path, query_params = GetByQualifiedName.prepare_direct_api_request( + qualified_name, asset_type, min_ext_info, ignore_relationships + ) + raw_json = await self._client._call_api(endpoint_path, query_params) + return _process_get_response_v9( + raw_json, qualified_name, asset_type, by_guid=False + ) + + @validate_arguments(config=dict(arbitrary_types_allowed=True)) + async def get_by_guid( + self, + guid: str, + asset_type: Type[A] = Asset, # type: ignore[assignment] + min_ext_info: bool = False, + ignore_relationships: bool = True, + attributes: Optional[Union[List[str], List[AtlanField]]] = None, + related_attributes: Optional[Union[List[str], List[AtlanField]]] = None, + ) -> A: + """ + Retrieves an asset by its GUID. + + :param guid: unique identifier (GUID) of the asset to retrieve + :param asset_type: type of asset to be retrieved, defaults to ``Asset`` + :param min_ext_info: whether to minimize extra info (True) or not (False) + :param ignore_relationships: whether to include relationships (False) or exclude them (True) + :param attributes: a specific list of attributes to retrieve for the asset + :param related_attributes: a specific list of relationships attributes to retrieve for the asset + :returns: the requested asset + :raises NotFoundError: if the asset does not exist, or is not of the type requested + :raises AtlanError: on any API communication issue + """ + normalized_attributes = GetByQualifiedName.normalize_search_fields(attributes) + normalized_related_attributes = GetByQualifiedName.normalize_search_fields( + related_attributes + ) + + if (normalized_attributes and len(normalized_attributes)) or ( + normalized_related_attributes and len(normalized_related_attributes) + ): + search = self._prepare_fluent_search( + wheres=[ + Asset.GUID.eq(guid), + Asset.TYPE_NAME.eq(asset_type.__name__), + ], + attributes=normalized_attributes, + related_attributes=normalized_related_attributes, + ) + results = await search.execute_async(client=self._client) + if results and results.current_page(): + first_result = results.current_page()[0] + if isinstance(first_result, asset_type): + return first_result + raise ErrorCode.ASSET_NOT_TYPE_REQUESTED.exception_with_parameters( + guid, asset_type.__name__ + ) + raise ErrorCode.ASSET_NOT_FOUND_BY_GUID.exception_with_parameters(guid) + + endpoint_path, query_params = GetByGuid.prepare_direct_api_request( + guid, min_ext_info, ignore_relationships + ) + raw_json = await self._client._call_api(endpoint_path, query_params) + return _process_get_response_v9(raw_json, guid, asset_type, by_guid=True) + + @validate_arguments + async def retrieve_minimal( + self, + guid: str, + asset_type: Type[A] = Asset, # type: ignore[assignment] + ) -> A: + """ + Retrieves an asset by its GUID, without any of its relationships. + + :param guid: unique identifier (GUID) of the asset to retrieve + :param asset_type: type of asset to be retrieved, defaults to ``Asset`` + :returns: the asset, without any of its relationships + :raises NotFoundError: if the asset does not exist + """ + return await self.get_by_guid( + guid=guid, + asset_type=asset_type, + min_ext_info=True, + ignore_relationships=True, + ) + + # ------------------------------------------------------------------ + # Save / Upsert + # ------------------------------------------------------------------ + + @validate_arguments + async def upsert( + self, + entity: Union[Asset, List[Asset]], + replace_atlan_tags: bool = False, + replace_custom_metadata: bool = False, + overwrite_custom_metadata: bool = False, + ) -> AssetMutationResponse: + """Deprecated - use save() instead.""" + warn( + "This method is deprecated, please use 'save' instead, which offers identical functionality.", + DeprecationWarning, + stacklevel=2, + ) + return await self.save( + entity=entity, + replace_atlan_tags=replace_atlan_tags, + replace_custom_metadata=replace_custom_metadata, + overwrite_custom_metadata=overwrite_custom_metadata, + ) + + @validate_arguments + async def save( + self, + entity: Union[Asset, List[Asset]], + replace_atlan_tags: bool = False, + replace_custom_metadata: bool = False, + overwrite_custom_metadata: bool = False, + append_atlan_tags: bool = False, + ) -> AssetMutationResponse: + """ + If an asset with the same qualified_name exists, updates the existing asset. + Otherwise, creates the asset. + + :param entity: one or more assets to save + :param replace_atlan_tags: whether to replace AtlanTags during an update (True) or not (False) + :param replace_custom_metadata: replaces any custom metadata with non-empty values provided + :param overwrite_custom_metadata: overwrites any custom metadata, even with empty values + :param append_atlan_tags: whether to add/update/remove AtlanTags during an update (True) or not (False) + :returns: the result of the save + :raises AtlanError: on any API communication issue + """ + query_params = { + "replaceTags": replace_atlan_tags, + "appendTags": append_atlan_tags, + "replaceBusinessAttributes": replace_custom_metadata, + "overwriteBusinessAttributes": overwrite_custom_metadata, + } + + entities: List[Asset] = [] + if isinstance(entity, list): + entities.extend(entity) + else: + entities.append(entity) + + for asset in entities: + asset.validate_required() + await asset.flush_custom_metadata_async(client=self._client) + + request_payload = await _make_bulk_request_payload_async(entities, self._client) + raw_json = await self._client._call_api( + BULK_UPDATE, query_params, request_payload + ) + response = _parse_mutation_response(raw_json) + + if connections_created := response.assets_created(Connection): + await self._wait_for_connections_to_be_created(connections_created) + return response + + async def _wait_for_connections_to_be_created(self, connections_created): + guids = [c.guid for c in connections_created] + LOGGER.debug("Waiting for connections") + + @retry( + retry=retry_if_exception_type(PermissionError), + wait=wait_exponential(multiplier=1, min=1, max=8), + stop=stop_after_attempt(10), + reraise=True, + ) + async def _retrieve_connection_with_retry(guid): + await self.retrieve_minimal(guid=guid, asset_type=Connection) + + for guid in guids: + await _retrieve_connection_with_retry(guid) + + LOGGER.debug("Finished waiting for connections") + + @validate_arguments + async def upsert_merging_cm( + self, entity: Union[Asset, List[Asset]], replace_atlan_tags: bool = False + ) -> AssetMutationResponse: + """Deprecated - use save_merging_cm() instead.""" + warn( + "This method is deprecated, please use 'save_merging_cm' instead, which offers identical functionality.", + DeprecationWarning, + stacklevel=2, + ) + return await self.save_merging_cm( + entity=entity, replace_atlan_tags=replace_atlan_tags + ) + + @validate_arguments + async def save_merging_cm( + self, entity: Union[Asset, List[Asset]], replace_atlan_tags: bool = False + ) -> AssetMutationResponse: + """ + If no asset exists, has the same behavior as save(), while also setting + any custom metadata provided. If an asset does exist, optionally overwrites any Atlan tags. + Will merge any provided custom metadata with any custom metadata that already exists on the asset. + + :param entity: one or more assets to save + :param replace_atlan_tags: whether to replace AtlanTags during an update (True) or not (False) + :returns: details of the created or updated assets + """ + return await self.save( + entity=entity, + replace_atlan_tags=replace_atlan_tags, + replace_custom_metadata=True, + overwrite_custom_metadata=False, + ) + + @validate_arguments + async def update_merging_cm( + self, entity: Asset, replace_atlan_tags: bool = False + ) -> AssetMutationResponse: + """ + If no asset exists, fails with a NotFoundError. Will merge any provided + custom metadata with any custom metadata that already exists on the asset. + + :param entity: the asset to update + :param replace_atlan_tags: whether to replace AtlanTags during an update (True) or not (False) + :returns: details of the updated asset + :raises NotFoundError: if the asset does not exist (will not create it) + """ + await UpdateAsset.validate_asset_exists_async( + qualified_name=entity.qualified_name or "", + asset_type=type(entity), + get_by_qualified_name_func=self.get_by_qualified_name, + ) + return await self.save_merging_cm( + entity=entity, replace_atlan_tags=replace_atlan_tags + ) + + @validate_arguments + async def upsert_replacing_cm( + self, entity: Union[Asset, List[Asset]], replace_atlan_tags: bool = False + ) -> AssetMutationResponse: + """Deprecated - use save_replacing_cm() instead.""" + warn( + "This method is deprecated, please use 'save_replacing_cm' instead, which offers identical functionality.", + DeprecationWarning, + stacklevel=2, + ) + return await self.save_replacing_cm( + entity=entity, replace_atlan_tags=replace_atlan_tags + ) + + @validate_arguments + async def save_replacing_cm( + self, entity: Union[Asset, List[Asset]], replace_atlan_tags: bool = False + ) -> AssetMutationResponse: + """ + If no asset exists, has the same behavior as save(), while also setting + any custom metadata provided. + If an asset does exist, optionally overwrites any Atlan tags. + Will overwrite all custom metadata on any existing asset with only the + custom metadata provided. + + :param entity: one or more assets to save + :param replace_atlan_tags: whether to replace AtlanTags during an update (True) or not (False) + :returns: details of the created or updated assets + :raises AtlanError: on any API communication issue + """ + query_params = { + "replaceClassifications": replace_atlan_tags, + "replaceBusinessAttributes": True, + "overwriteBusinessAttributes": True, + } + + entities: List[Asset] = [] + if isinstance(entity, list): + entities.extend(entity) + else: + entities.append(entity) + + for asset in entities: + asset.validate_required() + await asset.flush_custom_metadata_async(self._client) + + request_payload = await _make_bulk_request_payload_async(entities, self._client) + raw_json = await self._client._call_api( + BULK_UPDATE, query_params, request_payload + ) + return _parse_mutation_response(raw_json) + + @validate_arguments + async def update_replacing_cm( + self, entity: Asset, replace_atlan_tags: bool = False + ) -> AssetMutationResponse: + """ + If no asset exists, fails with a NotFoundError. + Will overwrite all custom metadata on any existing asset with only the + custom metadata provided. + + :param entity: the asset to update + :param replace_atlan_tags: whether to replace AtlanTags during an update (True) or not (False) + :returns: details of the updated asset + :raises NotFoundError: if the asset does not exist (will not create it) + """ + await UpdateAsset.validate_asset_exists_async( + qualified_name=entity.qualified_name or "", + asset_type=type(entity), + get_by_qualified_name_func=self.get_by_qualified_name, + ) + return await self.save_replacing_cm( + entity=entity, replace_atlan_tags=replace_atlan_tags + ) + + # ------------------------------------------------------------------ + # Delete / Purge / Restore + # ------------------------------------------------------------------ + + @validate_arguments + async def purge_by_guid( + self, + guid: Union[str, List[str]], + delete_type: AtlanDeleteType = AtlanDeleteType.PURGE, + ) -> AssetMutationResponse: + """ + Deletes one or more assets by their unique identifier (GUID) using the specified delete type. + + :param guid: unique identifier(s) (GUIDs) of one or more assets to delete + :param delete_type: type of deletion to perform (PURGE or HARD) + :returns: details of the deleted asset(s) + :raises AtlanError: on any API communication issue + """ + query_params = PurgeByGuid.prepare_request(guid, delete_type) + raw_json = await self._client._call_api( + DELETE_ENTITIES_BY_GUIDS, query_params=query_params + ) + return _parse_mutation_response(raw_json) + + @validate_arguments + async def delete_by_guid( + self, guid: Union[str, List[str]] + ) -> AssetMutationResponse: + """ + Soft-deletes (archives) one or more assets by their unique identifier (GUID). + + :param guid: unique identifier(s) (GUIDs) of one or more assets to soft-delete + :returns: details of the soft-deleted asset(s) + :raises AtlanError: on any API communication issue + """ + guids = DeleteByGuid.prepare_request(guid) + + assets = [] + for single_guid in guids: + asset = await self.retrieve_minimal(guid=single_guid, asset_type=Asset) + assets.append(asset) + DeleteByGuid.validate_assets_can_be_archived(assets) + + query_params = DeleteByGuid.prepare_delete_request(guids) + raw_json = await self._client._call_api( + DELETE_ENTITIES_BY_GUIDS, query_params=query_params + ) + response = _parse_mutation_response(raw_json) + + for asset in response.assets_deleted(asset_type=Asset): + await self._wait_till_deleted(asset) + return response + + async def _wait_till_deleted(self, asset: Asset): + max_attempts = 20 + for attempt in range(max_attempts): + try: + retrieved = await self.retrieve_minimal( + guid=asset.guid, asset_type=Asset + ) + if getattr(retrieved, "status", None) == EntityStatus.DELETED: + return + except Exception as e: + if attempt == max_attempts - 1: + raise ErrorCode.RETRY_OVERRUN.exception_with_parameters() from e + await asyncio.sleep(1) + raise ErrorCode.RETRY_OVERRUN.exception_with_parameters() + + @validate_arguments + async def restore(self, asset_type: Type[A], qualified_name: str) -> bool: + """ + Restore an archived (soft-deleted) asset to active. + + :param asset_type: type of the asset to restore + :param qualified_name: of the asset to restore + :returns: True if the asset is now restored, or False if not + :raises AtlanError: on any API communication issue + """ + return await self._restore(asset_type, qualified_name, 0) + + async def _restore( + self, asset_type: Type[A], qualified_name: str, retries: int + ) -> bool: + if not RestoreAsset.can_asset_type_be_archived(asset_type): + return False + + existing = await self.get_by_qualified_name( + asset_type=asset_type, + qualified_name=qualified_name, + ignore_relationships=False, + ) + if not existing: + return False + elif RestoreAsset.is_asset_active(existing): + if retries < 10: + await asyncio.sleep(2) + return await self._restore(asset_type, qualified_name, retries + 1) + else: + return True + else: + response = await self._restore_asset(existing) + return RestoreAsset.is_restore_successful(response) + + async def _restore_asset(self, asset: Asset) -> AssetMutationResponse: + to_restore = asset.trim_to_required() + to_restore.status = EntityStatus.ACTIVE + + query_params = { + "replaceClassifications": False, + "replaceBusinessAttributes": False, + "overwriteBusinessAttributes": False, + } + + entities = [to_restore] + for restored in entities: + await restored.flush_custom_metadata_async(self._client) + + request_payload = await _make_bulk_request_payload_async(entities, self._client) + raw_json = await self._client._call_api( + BULK_UPDATE, query_params, request_payload + ) + return _parse_mutation_response(raw_json) + + # ------------------------------------------------------------------ + # Atlan Tags + # ------------------------------------------------------------------ + + async def _modify_tags( + self, + asset_type: Type[A], + qualified_name: str, + atlan_tag_names: List[str], + propagate: bool = False, + remove_propagation_on_delete: bool = True, + restrict_lineage_propagation: bool = False, + restrict_propagation_through_hierarchy: bool = False, + modification_type: str = "add", + save_parameters: Optional[dict] = None, + ) -> A: + if save_parameters is None: + save_parameters = {} + + @retry( + reraise=True, + retry=retry_if_exception_type(NotFoundError), + stop=stop_after_attempt(10), + wait=wait_exponential(multiplier=1, min=1, max=5), + ) + async def _get_asset_with_retry(): + return await self.get_by_qualified_name( + qualified_name=qualified_name, + asset_type=asset_type, + attributes=["anchor"], + ) + + retrieved_asset = await _get_asset_with_retry() + + if asset_type in (AtlasGlossaryTerm, AtlasGlossaryCategory): + updated_asset = asset_type.updater( + qualified_name=qualified_name, + name=retrieved_asset.name, + glossary_guid=retrieved_asset.anchor.guid, + ) + else: + updated_asset = asset_type.updater( + qualified_name=qualified_name, name=retrieved_asset.name + ) + + tags = [ + AtlanTag( + type_name=AtlanTagName(display_text=name), + propagate=propagate, + remove_propagations_on_entity_delete=remove_propagation_on_delete, + restrict_propagation_through_lineage=restrict_lineage_propagation, + restrict_propagation_through_hierarchy=restrict_propagation_through_hierarchy, + ) + for name in atlan_tag_names + ] + + if modification_type in ("add", "update"): + updated_asset.add_or_update_classifications = tags + elif modification_type == "remove": + updated_asset.remove_classifications = tags + elif modification_type == "replace": + updated_asset.classifications = tags + + response = await self.save(entity=updated_asset, **save_parameters) + if assets := response.assets_updated(asset_type=asset_type): + return assets[0] + return updated_asset + + @validate_arguments + async def add_atlan_tags( + self, + asset_type: Type[A], + qualified_name: str, + atlan_tag_names: List[str], + propagate: bool = False, + remove_propagation_on_delete: bool = True, + restrict_lineage_propagation: bool = False, + restrict_propagation_through_hierarchy: bool = False, + ) -> A: + """ + Add one or more Atlan tags to the provided asset. + + :param asset_type: type of asset to which to add the Atlan tags + :param qualified_name: qualified_name of the asset + :param atlan_tag_names: human-readable names of the Atlan tags to add + :param propagate: whether to propagate the Atlan tag + :param remove_propagation_on_delete: whether to remove propagated tags on deletion + :param restrict_lineage_propagation: whether to avoid propagating through lineage + :param restrict_propagation_through_hierarchy: whether to prevent hierarchy propagation + :returns: the asset that was updated + :raises AtlanError: on any API communication issue + """ + return await self._modify_tags( + asset_type=asset_type, + qualified_name=qualified_name, + atlan_tag_names=atlan_tag_names, + propagate=propagate, + remove_propagation_on_delete=remove_propagation_on_delete, + restrict_lineage_propagation=restrict_lineage_propagation, + restrict_propagation_through_hierarchy=restrict_propagation_through_hierarchy, + modification_type="add", + save_parameters={ + "replace_atlan_tags": False, + "append_atlan_tags": True, + }, + ) + + @validate_arguments + async def update_atlan_tags( + self, + asset_type: Type[A], + qualified_name: str, + atlan_tag_names: List[str], + propagate: bool = False, + remove_propagation_on_delete: bool = True, + restrict_lineage_propagation: bool = True, + restrict_propagation_through_hierarchy: bool = False, + ) -> A: + """ + Update one or more Atlan tags to the provided asset. + + :param asset_type: type of asset to which to update the Atlan tags + :param qualified_name: qualified_name of the asset + :param atlan_tag_names: human-readable names of the Atlan tags to update + :param propagate: whether to propagate the Atlan tag + :param remove_propagation_on_delete: whether to remove propagated tags on deletion + :param restrict_lineage_propagation: whether to avoid propagating through lineage + :param restrict_propagation_through_hierarchy: whether to prevent hierarchy propagation + :returns: the asset that was updated + :raises AtlanError: on any API communication issue + """ + return await self._modify_tags( + asset_type=asset_type, + qualified_name=qualified_name, + atlan_tag_names=atlan_tag_names, + propagate=propagate, + remove_propagation_on_delete=remove_propagation_on_delete, + restrict_lineage_propagation=restrict_lineage_propagation, + restrict_propagation_through_hierarchy=restrict_propagation_through_hierarchy, + modification_type="update", + save_parameters={ + "replace_atlan_tags": False, + "append_atlan_tags": True, + }, + ) + + @validate_arguments + async def remove_atlan_tag( + self, + asset_type: Type[A], + qualified_name: str, + atlan_tag_name: str, + ) -> A: + """ + Removes a single Atlan tag from the provided asset. + + :param asset_type: type of asset from which to remove the Atlan tag + :param qualified_name: qualified_name of the asset + :param atlan_tag_name: human-readable name of the Atlan tag to remove + :returns: the asset that was updated + :raises AtlanError: on any API communication issue + """ + return await self._modify_tags( + asset_type=asset_type, + qualified_name=qualified_name, + atlan_tag_names=[atlan_tag_name], + modification_type="remove", + save_parameters={ + "replace_atlan_tags": False, + "append_atlan_tags": True, + }, + ) + + @validate_arguments + async def remove_atlan_tags( + self, + asset_type: Type[A], + qualified_name: str, + atlan_tag_names: List[str], + ) -> A: + """ + Removes one or more Atlan tags from the provided asset. + + :param asset_type: type of asset from which to remove the Atlan tags + :param qualified_name: qualified_name of the asset + :param atlan_tag_names: human-readable names of the Atlan tags to remove + :returns: the asset that was updated + :raises AtlanError: on any API communication issue + """ + return await self._modify_tags( + asset_type=asset_type, + qualified_name=qualified_name, + atlan_tag_names=atlan_tag_names, + modification_type="remove", + save_parameters={ + "replace_atlan_tags": False, + "append_atlan_tags": True, + }, + ) + + # ------------------------------------------------------------------ + # Update asset by attribute (certificate, announcement, etc.) + # ------------------------------------------------------------------ + + async def _update_asset_by_attribute( + self, asset: A, asset_type: Type[A], qualified_name: str + ) -> Optional[A]: + query_params = UpdateAssetByAttribute.prepare_request_params(qualified_name) + await asset.flush_custom_metadata_async(client=self._client) + endpoint = UpdateAssetByAttribute.get_api_endpoint(asset_type) + asset_dict = {"entity": json.loads(asset.to_json(nested=True))} + raw_json = await self._client._call_api(endpoint, query_params, asset_dict) + response = _parse_mutation_response(raw_json) + if assets := response.assets_partially_updated(asset_type=asset_type): + return assets[0] + if assets := response.assets_updated(asset_type=asset_type): + return assets[0] + return None + + # ------------------------------------------------------------------ + # Certificates + # ------------------------------------------------------------------ + + @overload + async def update_certificate( + self, + asset_type: Type[AtlasGlossaryTerm], + qualified_name: str, + name: str, + certificate_status: CertificateStatus, + glossary_guid: str, + message: Optional[str] = None, + ) -> Optional[AtlasGlossaryTerm]: ... + + @overload + async def update_certificate( + self, + asset_type: Type[AtlasGlossaryCategory], + qualified_name: str, + name: str, + certificate_status: CertificateStatus, + glossary_guid: str, + message: Optional[str] = None, + ) -> Optional[AtlasGlossaryCategory]: ... + + @overload + async def update_certificate( + self, + asset_type: Type[A], + qualified_name: str, + name: str, + certificate_status: CertificateStatus, + glossary_guid: Optional[str] = None, + message: Optional[str] = None, + ) -> Optional[A]: ... + + @validate_arguments + async def update_certificate( + self, + asset_type: Type[A], + qualified_name: str, + name: str, + certificate_status: CertificateStatus, + glossary_guid: Optional[str] = None, + message: Optional[str] = None, + ) -> Optional[A]: + """ + Update the certificate on an asset. + + :param asset_type: type of asset on which to update the certificate + :param qualified_name: the qualified_name of the asset + :param name: the name of the asset + :param certificate_status: specific certificate to set on the asset + :param glossary_guid: unique identifier of the glossary (required for glossary types) + :param message: (optional) message to set + :returns: the result of the update, or None if the update failed + :raises AtlanError: on any API communication issue + """ + asset = UpdateCertificate.prepare_asset_with_certificate( + asset_type=asset_type, + qualified_name=qualified_name, + name=name, + certificate_status=certificate_status, + message=message, + glossary_guid=glossary_guid, + ) + _handle_v9_glossary_anchor(asset, asset_type.__name__, glossary_guid) + return await self._update_asset_by_attribute(asset, asset_type, qualified_name) + + @overload + async def remove_certificate( + self, + asset_type: Type[AtlasGlossaryTerm], + qualified_name: str, + name: str, + glossary_guid: str, + ) -> Optional[AtlasGlossaryTerm]: ... + + @overload + async def remove_certificate( + self, + asset_type: Type[AtlasGlossaryCategory], + qualified_name: str, + name: str, + glossary_guid: str, + ) -> Optional[AtlasGlossaryCategory]: ... + + @overload + async def remove_certificate( + self, + asset_type: Type[A], + qualified_name: str, + name: str, + glossary_guid: Optional[str] = None, + ) -> Optional[A]: ... + + @validate_arguments + async def remove_certificate( + self, + asset_type: Type[A], + qualified_name: str, + name: str, + glossary_guid: Optional[str] = None, + ) -> Optional[A]: + """ + Remove the certificate from an asset. + + :param asset_type: type of asset from which to remove the certificate + :param qualified_name: the qualified_name of the asset + :param name: the name of the asset + :param glossary_guid: unique identifier of the glossary (required for glossary types) + :returns: the result of the removal, or None if the removal failed + """ + asset = RemoveCertificate.prepare_asset_for_certificate_removal( + asset_type=asset_type, + qualified_name=qualified_name, + name=name, + glossary_guid=glossary_guid, + ) + _handle_v9_glossary_anchor(asset, asset_type.__name__, glossary_guid) + return await self._update_asset_by_attribute(asset, asset_type, qualified_name) + + # ------------------------------------------------------------------ + # Announcements + # ------------------------------------------------------------------ + + @overload + async def update_announcement( + self, + asset_type: Type[AtlasGlossaryTerm], + qualified_name: str, + name: str, + announcement: Announcement, + glossary_guid: str, + ) -> Optional[AtlasGlossaryTerm]: ... + + @overload + async def update_announcement( + self, + asset_type: Type[AtlasGlossaryCategory], + qualified_name: str, + name: str, + announcement: Announcement, + glossary_guid: str, + ) -> Optional[AtlasGlossaryCategory]: ... + + @overload + async def update_announcement( + self, + asset_type: Type[A], + qualified_name: str, + name: str, + announcement: Announcement, + glossary_guid: Optional[str] = None, + ) -> Optional[A]: ... + + @validate_arguments(config=dict(arbitrary_types_allowed=True)) + async def update_announcement( + self, + asset_type: Type[A], + qualified_name: str, + name: str, + announcement: Announcement, + glossary_guid: Optional[str] = None, + ) -> Optional[A]: + """ + Update the announcement on an asset. + + :param asset_type: type of asset on which to update the announcement + :param qualified_name: the qualified_name of the asset + :param name: the name of the asset + :param announcement: to apply to the asset + :param glossary_guid: unique identifier of the glossary (required for glossary types) + :returns: the result of the update, or None if the update failed + """ + asset = UpdateAnnouncement.prepare_asset_with_announcement( + asset_type=asset_type, + qualified_name=qualified_name, + name=name, + announcement=announcement, + glossary_guid=glossary_guid, + ) + _handle_v9_glossary_anchor(asset, asset_type.__name__, glossary_guid) + return await self._update_asset_by_attribute(asset, asset_type, qualified_name) + + @overload + async def remove_announcement( + self, + asset_type: Type[AtlasGlossaryTerm], + qualified_name: str, + name: str, + glossary_guid: str, + ) -> Optional[AtlasGlossaryTerm]: ... + + @overload + async def remove_announcement( + self, + asset_type: Type[AtlasGlossaryCategory], + qualified_name: str, + name: str, + glossary_guid: str, + ) -> Optional[AtlasGlossaryCategory]: ... + + @overload + async def remove_announcement( + self, + asset_type: Type[A], + qualified_name: str, + name: str, + glossary_guid: Optional[str] = None, + ) -> Optional[A]: ... + + @validate_arguments + async def remove_announcement( + self, + asset_type: Type[A], + qualified_name: str, + name: str, + glossary_guid: Optional[str] = None, + ) -> Optional[A]: + """ + Remove the announcement from an asset. + + :param asset_type: type of asset from which to remove the announcement + :param qualified_name: the qualified_name of the asset + :param glossary_guid: unique identifier of the glossary (required for glossary types) + :returns: the result of the removal, or None if the removal failed + """ + asset = RemoveAnnouncement.prepare_asset_for_announcement_removal( + asset_type=asset_type, + qualified_name=qualified_name, + name=name, + glossary_guid=glossary_guid, + ) + _handle_v9_glossary_anchor(asset, asset_type.__name__, glossary_guid) + return await self._update_asset_by_attribute(asset, asset_type, qualified_name) + + # ------------------------------------------------------------------ + # Custom metadata + # ------------------------------------------------------------------ + + @validate_arguments(config=dict(arbitrary_types_allowed=True)) + async def update_custom_metadata_attributes( + self, guid: str, custom_metadata: CustomMetadataDict + ): + """ + Update only the provided custom metadata attributes on the asset. + + :param guid: unique identifier (GUID) of the asset + :param custom_metadata: custom metadata to update + :raises AtlanError: on any API communication issue + """ + custom_metadata_request = UpdateCustomMetadataAttributes.prepare_request( + custom_metadata + ) + endpoint = ManageCustomMetadata.get_api_endpoint( + guid, custom_metadata_request.custom_metadata_set_id + ) + payload = _custom_metadata_payload(custom_metadata_request) + await self._client._call_api(endpoint, None, payload) + + @validate_arguments(config=dict(arbitrary_types_allowed=True)) + async def replace_custom_metadata( + self, guid: str, custom_metadata: CustomMetadataDict + ): + """ + Replace specific custom metadata on the asset. + + :param guid: unique identifier (GUID) of the asset + :param custom_metadata: custom metadata to replace + :raises AtlanError: on any API communication issue + """ + custom_metadata_request = ReplaceCustomMetadata.prepare_request(custom_metadata) + endpoint = ManageCustomMetadata.get_api_endpoint( + guid, custom_metadata_request.custom_metadata_set_id + ) + payload = _custom_metadata_payload(custom_metadata_request) + await self._client._call_api(endpoint, None, payload) + + @validate_arguments + async def remove_custom_metadata(self, guid: str, cm_name: str): + """ + Remove specific custom metadata from an asset. + + :param guid: unique identifier (GUID) of the asset + :param cm_name: human-readable name of the custom metadata to remove + :raises AtlanError: on any API communication issue + """ + custom_metadata_request = RemoveCustomMetadata.prepare_request( + cm_name, self._client + ) + endpoint = ManageCustomMetadata.get_api_endpoint( + guid, custom_metadata_request.custom_metadata_set_id + ) + payload = _custom_metadata_payload(custom_metadata_request) + await self._client._call_api(endpoint, None, payload) + + # ------------------------------------------------------------------ + # Terms management + # ------------------------------------------------------------------ + + async def _search_for_asset_with_name( + self, + query: Query, + name: str, + asset_type: Type[A], + attributes: Optional[List], + allow_multiple: bool = False, + ) -> List[A]: + from pyatlan_v9.model.search import DSL as V9DSL + + dsl = V9DSL(query=query) + search_request = IndexSearchRequest( + dsl=dsl, + attributes=attributes or [], + relation_attributes=["name"], + ) + results = await self.search(search_request) + return await _process_search_results_v9_async( + results, name, asset_type, allow_multiple + ) + + async def _manage_terms( + self, + asset_type: Type[A], + terms: List[AtlasGlossaryTerm], + save_semantic: SaveSemantic, + guid: Optional[str] = None, + qualified_name: Optional[str] = None, + ) -> A: + from pyatlan_v9.model.fluent_search import FluentSearch + + ManageTerms.validate_guid_and_qualified_name(guid, qualified_name) + + if guid: + search_query = ( + FluentSearch() + .select() + .where(Asset.TYPE_NAME.eq(asset_type.__name__)) + .where(asset_type.GUID.eq(guid)) + ) + else: + if qualified_name is None: + raise ValueError( + "qualified_name cannot be None when guid is not provided" + ) + search_query = ( + FluentSearch() + .select() + .where(Asset.TYPE_NAME.eq(asset_type.__name__)) + .where(asset_type.QUALIFIED_NAME.eq(qualified_name)) + ) + + results = await search_query.execute_async(client=self._client) + first_result = ManageTerms.validate_search_results( + results, asset_type, guid, qualified_name + ) + updated_asset = asset_type.updater( + qualified_name=first_result.qualified_name, name=first_result.name + ) + processed_terms: list[AtlasGlossaryTerm] = [] + for term in terms: + if getattr(term, "guid", None): + processed_terms.append( + AtlasGlossaryTerm.ref_by_guid( + guid=term.guid, semantic=save_semantic + ) + ) + elif getattr(term, "qualified_name", None): + processed_terms.append( + AtlasGlossaryTerm.ref_by_qualified_name( + qualified_name=term.qualified_name, + semantic=save_semantic, + ) + ) + updated_asset.assigned_terms = processed_terms + response = await self.save(entity=updated_asset) + return ManageTerms.process_save_response(response, asset_type, updated_asset) + + @validate_arguments + async def append_terms( + self, + asset_type: Type[A], + terms: List[AtlasGlossaryTerm], + guid: Optional[str] = None, + qualified_name: Optional[str] = None, + ) -> A: + """ + Link additional terms to an asset, without replacing existing terms. + + :param asset_type: type of the asset + :param terms: the list of terms to append to the asset + :param guid: unique identifier (GUID) of the asset + :param qualified_name: the qualified_name of the asset + :returns: the asset that was updated + """ + return await self._manage_terms( + asset_type=asset_type, + terms=terms, + save_semantic=SaveSemantic.APPEND, + guid=guid, + qualified_name=qualified_name, + ) + + @validate_arguments + async def replace_terms( + self, + asset_type: Type[A], + terms: List[AtlasGlossaryTerm], + guid: Optional[str] = None, + qualified_name: Optional[str] = None, + ) -> A: + """ + Replace the terms linked to an asset. + + :param asset_type: type of the asset + :param terms: the list of terms to replace on the asset + :param guid: unique identifier (GUID) of the asset + :param qualified_name: the qualified_name of the asset + :returns: the asset that was updated + """ + return await self._manage_terms( + asset_type=asset_type, + terms=terms, + save_semantic=SaveSemantic.REPLACE, + guid=guid, + qualified_name=qualified_name, + ) + + @validate_arguments + async def remove_terms( + self, + asset_type: Type[A], + terms: List[AtlasGlossaryTerm], + guid: Optional[str] = None, + qualified_name: Optional[str] = None, + ) -> A: + """ + Remove terms from an asset, without replacing all existing terms. + + :param asset_type: type of the asset + :param terms: the list of terms to remove from the asset + :param guid: unique identifier (GUID) of the asset + :param qualified_name: the qualified_name of the asset + :returns: the asset that was updated + """ + return await self._manage_terms( + asset_type=asset_type, + terms=terms, + save_semantic=SaveSemantic.REMOVE, + guid=guid, + qualified_name=qualified_name, + ) + + # ------------------------------------------------------------------ + # Find by name + # ------------------------------------------------------------------ + + @validate_arguments + async def find_connections_by_name( + self, + name: str, + connector_type: AtlanConnectorType, + attributes: Optional[List[str]] = None, + ) -> List[Connection]: + """ + Find a connection by its human-readable name and type. + + :param name: of the connection + :param connector_type: of the connection + :param attributes: (optional) collection of attributes to retrieve for the connection + :returns: all connections with that name and type, if found + :raises NotFoundError: if the connection does not exist + """ + if attributes is None: + attributes = [] + query = FindConnectionsByName.build_query(name, connector_type) + return await self._search_for_asset_with_name( + query=query, + name=name, + asset_type=Connection, + attributes=attributes, + allow_multiple=True, + ) + + @validate_arguments + async def find_glossary_by_name( + self, + name: str, + attributes: Optional[List[str]] = None, + ) -> AtlasGlossary: + """ + Find a glossary by its human-readable name. + + :param name: of the glossary + :param attributes: (optional) collection of attributes to retrieve for the glossary + :returns: the glossary, if found + :raises NotFoundError: if no glossary with the provided name exists + """ + if attributes is None: + attributes = [] + query = FindGlossaryByName.build_query(name) + results = await self._search_for_asset_with_name( + query=query, name=name, asset_type=AtlasGlossary, attributes=attributes + ) + return results[0] + + @validate_arguments + async def find_category_fast_by_name( + self, + name: str, + glossary_qualified_name: str, + attributes: Optional[List[str]] = None, + ) -> List[AtlasGlossaryCategory]: + """ + Find a category by its human-readable name. + + :param name: of the category + :param glossary_qualified_name: qualified_name of the glossary + :param attributes: (optional) collection of attributes to retrieve for the category + :returns: the category, if found + :raises NotFoundError: if no category with the provided name exists + """ + if attributes is None: + attributes = [] + query = FindCategoryFastByName.build_query(name, glossary_qualified_name) + return await self._search_for_asset_with_name( + query=query, + name=name, + asset_type=AtlasGlossaryCategory, + attributes=attributes, + allow_multiple=True, + ) + + @validate_arguments + async def find_category_by_name( + self, + name: str, + glossary_name: str, + attributes: Optional[List[str]] = None, + ) -> List[AtlasGlossaryCategory]: + """ + Find a category by its human-readable name. + + :param name: of the category + :param glossary_name: human-readable name of the glossary + :param attributes: (optional) collection of attributes to retrieve for the category + :returns: the category, if found + :raises NotFoundError: if no category with the provided name exists + """ + glossary = await self.find_glossary_by_name(name=glossary_name) + return await self.find_category_fast_by_name( + name=name, + glossary_qualified_name=glossary.qualified_name, + attributes=attributes, + ) + + @validate_arguments + async def find_term_fast_by_name( + self, + name: str, + glossary_qualified_name: str, + attributes: Optional[List[str]] = None, + ) -> AtlasGlossaryTerm: + """ + Find a term by its human-readable name. + + :param name: of the term + :param glossary_qualified_name: qualified_name of the glossary + :param attributes: (optional) collection of attributes to retrieve for the term + :returns: the term, if found + :raises NotFoundError: if no term with the provided name exists + """ + if attributes is None: + attributes = [] + query = FindTermFastByName.build_query(name, glossary_qualified_name) + results = await self._search_for_asset_with_name( + query=query, name=name, asset_type=AtlasGlossaryTerm, attributes=attributes + ) + return results[0] + + @validate_arguments + async def find_term_by_name( + self, + name: str, + glossary_name: str, + attributes: Optional[List[str]] = None, + ) -> AtlasGlossaryTerm: + """ + Find a term by its human-readable name. + + :param name: of the term + :param glossary_name: human-readable name of the glossary + :param attributes: (optional) collection of attributes to retrieve for the term + :returns: the term, if found + :raises NotFoundError: if no term with the provided name exists + """ + glossary = await self.find_glossary_by_name(name=glossary_name) + return await self.find_term_fast_by_name( + name=name, + glossary_qualified_name=glossary.qualified_name, + attributes=attributes, + ) + + @validate_arguments + async def find_domain_by_name( + self, + name: str, + attributes: Optional[List[str]] = None, + ) -> DataDomain: + """ + Find a data domain by its human-readable name. + + :param name: of the domain + :param attributes: (optional) collection of attributes to retrieve for the domain + :returns: the domain, if found + :raises NotFoundError: if no domain with the provided name exists + """ + attributes = attributes or [] + query = FindDomainByName.build_query(name) + results = await self._search_for_asset_with_name( + query=query, name=name, asset_type=DataDomain, attributes=attributes + ) + return results[0] + + @validate_arguments + async def find_product_by_name( + self, + name: str, + attributes: Optional[List[str]] = None, + ) -> DataProduct: + """ + Find a data product by its human-readable name. + + :param name: of the product + :param attributes: (optional) collection of attributes to retrieve for the product + :returns: the product, if found + :raises NotFoundError: if no product with the provided name exists + """ + attributes = attributes or [] + query = FindProductByName.build_query(name) + results = await self._search_for_asset_with_name( + query=query, name=name, asset_type=DataProduct, attributes=attributes + ) + return results[0] + + # ------------------------------------------------------------------ + # Hierarchy + # ------------------------------------------------------------------ + + async def get_hierarchy( + self, + glossary: AtlasGlossary, + attributes: Optional[List[Union[AtlanField, str]]] = None, + related_attributes: Optional[List[Union[AtlanField, str]]] = None, + ) -> CategoryHierarchy: + """ + Retrieve category hierarchy in this Glossary, in a traversable form. + + :param glossary: the glossary to retrieve the category hierarchy for + :param attributes: attributes to retrieve for each category in the hierarchy + :param related_attributes: attributes to retrieve for each related asset in the hierarchy + :returns: a traversable category hierarchy + """ + from pyatlan.model.search import Term as SearchTerm + from pyatlan_v9.model.fluent_search import FluentSearch + + GetHierarchy.validate_glossary(glossary) + if attributes is None: + attributes = [] + if related_attributes is None: + related_attributes = [] + search = ( + FluentSearch.select() + .where(AtlasGlossaryCategory.ANCHOR.eq(glossary.qualified_name)) + .where(SearchTerm.with_type_name("AtlasGlossaryCategory")) + .include_on_results(AtlasGlossaryCategory.PARENT_CATEGORY) + .page_size(20) + .sort(AtlasGlossaryCategory.NAME.order(SortOrder.ASCENDING)) + ) + for field in attributes: + search = search.include_on_results(field) + for field in related_attributes: + search = search.include_on_relations(field) + request = search.to_request() + response = await self.search(request) + return await _process_hierarchy_v9_async(response, glossary) + + # ------------------------------------------------------------------ + # Bulk processing + # ------------------------------------------------------------------ + + async def process_assets( + self, + search, + func: Callable[[Asset], Awaitable[None]], + ) -> int: + """ + Process assets matching a search query and apply a processing function + to each unique asset. + + :param search: the search provider that generates search queries + :param func: an async callable function that processes each unique asset + :returns: the total number of unique assets that have been processed + """ + guids_processed: set[str] = set() + has_assets_to_process: bool = True + iteration_count = 0 + while has_assets_to_process: + iteration_count += 1 + has_assets_to_process = False + response = await self.search(search.to_request()) + LOGGER.debug( + "Iteration %d found %d assets.", iteration_count, response.count + ) + async for asset in response: + if asset.guid not in guids_processed: + guids_processed.add(asset.guid) + has_assets_to_process = True + await func(asset) + return len(guids_processed) + + # ------------------------------------------------------------------ + # DQ helpers + # ------------------------------------------------------------------ + + @validate_arguments + async def add_dq_rule_schedule( + self, + asset_type: Type[A], + asset_name: str, + asset_qualified_name: str, + schedule_crontab: str, + schedule_time_zone: str, + ) -> AssetMutationResponse: + """ + Add a data quality rule schedule to an asset. + + :param asset_type: the type of asset to update (e.g., Table) + :param asset_name: the name of the asset to update + :param asset_qualified_name: the qualified name of the asset to update + :param schedule_crontab: cron expression string defining the schedule + :param schedule_time_zone: timezone for the schedule + :returns: the result of the save + :raises AtlanError: on any API communication issue + """ + updated_asset = asset_type.updater( + qualified_name=asset_qualified_name, name=asset_name + ) + updated_asset.asset_d_q_schedule_time_zone = schedule_time_zone + updated_asset.asset_d_q_schedule_crontab = schedule_crontab + updated_asset.asset_d_q_schedule_type = DataQualityScheduleType.CRON + return await self.save(updated_asset) + + @validate_arguments + async def set_dq_row_scope_filter_column( + self, + asset_type: Type[A], + asset_name: str, + asset_qualified_name: str, + row_scope_filter_column_qualified_name: str, + ) -> AssetMutationResponse: + """ + Set the row scope filter column for data quality rules on an asset. + + :param asset_type: the type of asset to update (e.g., Table) + :param asset_name: the name of the asset to update + :param asset_qualified_name: the qualified name of the asset to update + :param row_scope_filter_column_qualified_name: the qualified name of the column + :returns: the result of the save + :raises AtlanError: on any API communication issue + """ + updated_asset = asset_type.updater( + qualified_name=asset_qualified_name, name=asset_name + ) + updated_asset.asset_d_q_row_scope_filter_column_qualified_name = ( + row_scope_filter_column_qualified_name + ) + return await self.save(updated_asset) diff --git a/pyatlan_v9/client/aio/atlan.py b/pyatlan_v9/client/aio/atlan.py new file mode 100644 index 000000000..117610fa9 --- /dev/null +++ b/pyatlan_v9/client/aio/atlan.py @@ -0,0 +1,878 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. +from __future__ import annotations + +import asyncio +import contextlib +import copy +import json +import logging +import os +import uuid +from contextlib import _AsyncGeneratorContextManager +from contextvars import ContextVar +from http import HTTPStatus +from importlib.resources import read_text +from types import SimpleNamespace +from typing import Any, AsyncGenerator, Dict, Optional, Union +from urllib.parse import urljoin + +import httpx +import msgspec +from httpx_retries import Retry +from msgspec import UNSET, UnsetType + +from pyatlan.cache.aio import ( + AsyncAtlanTagCache, + AsyncConnectionCache, + AsyncCustomMetadataCache, + AsyncDQTemplateConfigCache, + AsyncEnumCache, + AsyncGroupCache, + AsyncRoleCache, + AsyncSourceTagCache, + AsyncUserCache, +) +from pyatlan.client.aio.oauth import AsyncOAuthTokenManager +from pyatlan.client.common import CONNECTION_RETRY +from pyatlan.client.constants import EVENT_STREAM, PARSE_QUERY, UPLOAD_IMAGE +from pyatlan.errors import ERROR_CODE_FOR_HTTP_STATUS, AtlanError, ErrorCode +from pyatlan.model.core import AtlanObject as LegacyAtlanObject +from pyatlan.model.core import AtlanRequest as LegacyAtlanRequest +from pyatlan.multipart_data_generator import MultipartDataGenerator +from pyatlan.utils import ( + API, + APPLICATION_ENCODED_FORM, + AuthorizationFilter, + RequestIdAdapter, + get_python_version, +) +from pyatlan_v9.client.aio.admin import V9AsyncAdminClient +from pyatlan_v9.client.aio.asset import V9AsyncAssetClient +from pyatlan_v9.client.aio.audit import V9AsyncAuditClient +from pyatlan_v9.client.aio.contract import V9AsyncContractClient +from pyatlan_v9.client.aio.credential import V9AsyncCredentialClient +from pyatlan_v9.client.aio.file import V9AsyncFileClient +from pyatlan_v9.client.aio.group import V9AsyncGroupClient +from pyatlan_v9.client.aio.impersonate import V9AsyncImpersonationClient +from pyatlan_v9.client.aio.oauth_client import V9AsyncOAuthClient +from pyatlan_v9.client.aio.open_lineage import V9AsyncOpenLineageClient +from pyatlan_v9.client.aio.query import V9AsyncQueryClient +from pyatlan_v9.client.aio.role import V9AsyncRoleClient +from pyatlan_v9.client.aio.search_log import V9AsyncSearchLogClient +from pyatlan_v9.client.aio.sso import V9AsyncSSOClient +from pyatlan_v9.client.aio.task import V9AsyncTaskClient +from pyatlan_v9.client.aio.token import V9AsyncTokenClient +from pyatlan_v9.client.aio.typedef import V9AsyncTypeDefClient +from pyatlan_v9.client.aio.user import V9AsyncUserClient +from pyatlan_v9.client.aio.workflow import V9AsyncWorkflowClient +from pyatlan_v9.client.transport import PyatlanAsyncTransport +from pyatlan_v9.model.aio.core import AsyncAtlanRequest, AsyncAtlanResponse +from pyatlan_v9.model.atlan_image import AtlanImage +from pyatlan_v9.model.enums import AtlanTypeCategory +from pyatlan_v9.model.query import ParsedQuery, QueryParserRequest + +request_id_var = ContextVar("request_id", default=None) + + +def _get_adapter() -> logging.LoggerAdapter: + logger = logging.getLogger(__name__) + logger.addFilter(AuthorizationFilter()) + return RequestIdAdapter(logger=logger, contextvar=request_id_var) + + +LOGGER = _get_adapter() + +DEFAULT_RETRY = Retry( + total=5, + backoff_factor=1, + status_forcelist=[302, 403, 429, 500, 502, 503, 504], + allowed_methods=["HEAD", "GET", "OPTIONS", "POST", "PUT", "DELETE"], + respect_retry_after_header=True, +) + +VERSION = read_text("pyatlan", "version.txt").strip() + + +async def _log_response(response, *args, **kwargs): + LOGGER.debug("HTTP Status: %s", response.status_code) + LOGGER.debug("URL: %s", response.request.url) + + +class AsyncAtlanClient(msgspec.Struct, kw_only=True): + """ + Standalone async client for the Atlan v9 API. + + Mirrors the sync V9 ``AtlanClient`` structure but with async/await support. + Does **not** inherit from or delegate to any legacy client. + + Environment variables (with ATLAN_ prefix): + ATLAN_BASE_URL: Base URL for the Atlan service + ATLAN_API_KEY: API key for authentication + ATLAN_OAUTH_CLIENT_ID: OAuth client ID + ATLAN_OAUTH_CLIENT_SECRET: OAuth client secret + + Example:: + + async with AsyncAtlanClient( + base_url="https://myinstance.atlan.com", + api_key="my-api-key", + ) as client: + results = await client.asset.search(criteria) + """ + + # --- Configuration fields --- + base_url: Union[str, None] = None + api_key: Union[str, None] = None + oauth_client_id: Union[str, None] = None + oauth_client_secret: Union[str, None] = None + connect_timeout: float = 30.0 + read_timeout: float = 900.0 + retry: Any = None + proxy: Any = None + verify: Union[Any, UnsetType] = UNSET + + # --- Internal state --- + _async_session: Any = None + _request_params: Any = None + _401_has_retried: Any = None + _user_id: Union[str, None] = None + _async_oauth_token_manager: Any = None + _clients: Any = None + _caches: Any = None + + def __post_init__(self): + if self.retry is None: + self.retry = DEFAULT_RETRY + + _verify_explicit = self.verify is not UNSET + if not _verify_explicit: + self.verify = True + + if self.base_url is None: + self.base_url = os.environ.get("ATLAN_BASE_URL", "INTERNAL") + if self.api_key is None: + self.api_key = os.environ.get("ATLAN_API_KEY") + if self.oauth_client_id is None: + self.oauth_client_id = os.environ.get("ATLAN_OAUTH_CLIENT_ID") + if self.oauth_client_secret is None: + self.oauth_client_secret = os.environ.get("ATLAN_OAUTH_CLIENT_SECRET") + + self._401_has_retried = ContextVar("_401_has_retried", default=False) + self._clients = {} + self._caches = {} + + if self.oauth_client_id and self.oauth_client_secret and self.api_key is None: + LOGGER.debug( + "API KEY not provided. Using async OAuth flow for authentication" + ) + self._async_oauth_token_manager = AsyncOAuthTokenManager( + base_url=self.base_url, + client_id=self.oauth_client_id, + client_secret=self.oauth_client_secret, + connect_timeout=self.connect_timeout, + read_timeout=self.read_timeout, + ) + self._request_params = {"headers": {}} + else: + self._request_params = ( + {"headers": {"authorization": f"Bearer {self.api_key}"}} + if self.api_key and self.api_key.strip() + else {"headers": {}} + ) + + if self.proxy is None: + env_proxy = ( + os.environ.get("HTTPS_PROXY") + or os.environ.get("https_proxy") + or os.environ.get("HTTP_PROXY") + or os.environ.get("http_proxy") + ) + if env_proxy: + self.proxy = env_proxy + + if not _verify_explicit: + ssl_cert_file = os.environ.get("SSL_CERT_FILE") or os.environ.get( + "REQUESTS_CA_BUNDLE" + ) + if ssl_cert_file: + self.verify = ssl_cert_file + + transport_kwargs: Dict[str, Any] = {} + if self.proxy is not None: + transport_kwargs["proxy"] = self.proxy + if _verify_explicit or self.verify is not True: + transport_kwargs["verify"] = self.verify + + self._async_session = httpx.AsyncClient( + transport=PyatlanAsyncTransport(retry=self.retry, **transport_kwargs), + headers={ + "x-atlan-agent": "sdk", + "x-atlan-agent-id": "python", + "x-atlan-client-origin": "product_sdk", + "x-atlan-python-version": get_python_version(), + "x-atlan-client-type": "async", + "User-Agent": f"Atlan-PythonSDK/{VERSION}", + }, + event_hooks={"response": [_log_response]}, + ) + self._401_has_retried.set(False) + + # ------------------------------------------------------------------ + # Sub-client properties + # ------------------------------------------------------------------ + + def _get_client(self, key: str, factory): + if key not in self._clients: + self._clients[key] = factory(client=self) + return self._clients[key] + + def _get_cache(self, key: str, factory): + if key not in self._caches: + self._caches[key] = factory(client=self) + return self._caches[key] + + @property + def admin(self) -> V9AsyncAdminClient: + return self._get_client("admin", V9AsyncAdminClient) + + @property + def asset(self) -> V9AsyncAssetClient: + return self._get_client("asset", V9AsyncAssetClient) + + @property + def audit(self) -> V9AsyncAuditClient: + return self._get_client("audit", V9AsyncAuditClient) + + @property + def contracts(self) -> V9AsyncContractClient: + return self._get_client("contracts", V9AsyncContractClient) + + @property + def credentials(self) -> V9AsyncCredentialClient: + return self._get_client("credentials", V9AsyncCredentialClient) + + @property + def files(self) -> V9AsyncFileClient: + return self._get_client("files", V9AsyncFileClient) + + @property + def group(self) -> V9AsyncGroupClient: + return self._get_client("group", V9AsyncGroupClient) + + @property + def impersonate(self) -> V9AsyncImpersonationClient: + return self._get_client("impersonate", V9AsyncImpersonationClient) + + @property + def oauth_client(self) -> V9AsyncOAuthClient: + return self._get_client("oauth_client", V9AsyncOAuthClient) + + @property + def open_lineage(self) -> V9AsyncOpenLineageClient: + return self._get_client("open_lineage", V9AsyncOpenLineageClient) + + @property + def queries(self) -> V9AsyncQueryClient: + return self._get_client("queries", V9AsyncQueryClient) + + @property + def role(self) -> V9AsyncRoleClient: + return self._get_client("role", V9AsyncRoleClient) + + @property + def search_log(self) -> V9AsyncSearchLogClient: + return self._get_client("search_log", V9AsyncSearchLogClient) + + @property + def sso(self) -> V9AsyncSSOClient: + return self._get_client("sso", V9AsyncSSOClient) + + @property + def tasks(self) -> V9AsyncTaskClient: + return self._get_client("tasks", V9AsyncTaskClient) + + @property + def token(self) -> V9AsyncTokenClient: + return self._get_client("token", V9AsyncTokenClient) + + @property + def typedef(self) -> V9AsyncTypeDefClient: + return self._get_client("typedef", V9AsyncTypeDefClient) + + @property + def user(self) -> V9AsyncUserClient: + return self._get_client("user", V9AsyncUserClient) + + @property + def workflow(self) -> V9AsyncWorkflowClient: + return self._get_client("workflow", V9AsyncWorkflowClient) + + # ------------------------------------------------------------------ + # Cache properties (async caches) + # ------------------------------------------------------------------ + + @property + def atlan_tag_cache(self) -> AsyncAtlanTagCache: + return self._get_cache("atlan_tag", AsyncAtlanTagCache) + + @property + def enum_cache(self) -> AsyncEnumCache: + return self._get_cache("enum", AsyncEnumCache) + + @property + def group_cache(self) -> AsyncGroupCache: + return self._get_cache("group", AsyncGroupCache) + + @property + def role_cache(self) -> AsyncRoleCache: + return self._get_cache("role", AsyncRoleCache) + + @property + def user_cache(self) -> AsyncUserCache: + return self._get_cache("user", AsyncUserCache) + + @property + def custom_metadata_cache(self) -> AsyncCustomMetadataCache: + return self._get_cache("custom_metadata", AsyncCustomMetadataCache) + + @property + def connection_cache(self) -> AsyncConnectionCache: + return self._get_cache("connection", AsyncConnectionCache) + + @property + def source_tag_cache(self) -> AsyncSourceTagCache: + return self._get_cache("source_tag", AsyncSourceTagCache) + + @property + def dq_template_config_cache(self) -> AsyncDQTemplateConfigCache: + return self._get_cache("dq_template_config", AsyncDQTemplateConfigCache) + + # ------------------------------------------------------------------ + # Core API methods + # ------------------------------------------------------------------ + + def update_headers(self, header: Dict[str, str]): + self._async_session.headers.update(header) + + def _create_path(self, api: API) -> str: + if self.base_url == "INTERNAL": + return urljoin(api.endpoint.service, api.path) + return urljoin(urljoin(self.base_url, api.endpoint.prefix), api.path) + + async def _create_params( + self, api: API, query_params, request_obj + ) -> Dict[str, Any]: + params = copy.deepcopy(self._request_params) + if self._async_oauth_token_manager: + token = await self._async_oauth_token_manager.get_token() + params["headers"]["authorization"] = f"Bearer {token}" + params["headers"]["Accept"] = api.consumes + params["headers"]["content-type"] = api.produces + if query_params is not None: + params["params"] = query_params + if request_obj is not None: + if api.consumes == APPLICATION_ENCODED_FORM: + params["data"] = request_obj + elif isinstance(request_obj, LegacyAtlanObject): + # Use legacy serialization so request body matches legacy client exactly + params["data"] = LegacyAtlanRequest( + instance=request_obj, client=self + ).json() + elif hasattr(request_obj, "to_dict") and callable(request_obj.to_dict): + params["data"] = json.dumps(request_obj.to_dict()) + elif isinstance(request_obj, (dict, list)): + params["data"] = json.dumps(request_obj) + elif isinstance(request_obj, msgspec.Struct): + async_request = AsyncAtlanRequest( + instance=request_obj, + client=self, # type: ignore[arg-type] + ) + params["data"] = await async_request.json() + elif hasattr(request_obj, "to_json") and callable(request_obj.to_json): + async_request = AsyncAtlanRequest( + instance=request_obj, + client=self, # type: ignore[arg-type] + ) + params["data"] = await async_request.json() + elif hasattr(request_obj, "__root__"): + params["data"] = json.dumps(request_obj.__root__) + elif hasattr(request_obj, "json") and hasattr(request_obj, "__fields__"): + params["data"] = request_obj.json(by_alias=True, exclude_none=True) + else: + params["data"] = json.dumps(request_obj) + return params + + def _api_logger(self, api: API, path: str): + LOGGER.debug("------------------------------------------------------") + LOGGER.debug("Call : %s %s", api.method, path) + LOGGER.debug("Content-type_ : %s", api.consumes) + LOGGER.debug("Accept : %s", api.produces) + LOGGER.debug("Client-Type : %s", "ASYNC") + LOGGER.debug("Python-Version: %s", get_python_version()) + LOGGER.debug("User-Agent : %s", f"Atlan-PythonSDK/{VERSION}") + + async def _call_api( + self, + api, + query_params=None, + request_obj=None, + text_response=False, + ): + path = self._create_path(api) + params = await self._create_params(api, query_params, request_obj) + if LOGGER.isEnabledFor(logging.DEBUG): + self._api_logger(api, path) + return await self._call_api_internal( + api, path, params, text_response=text_response + ) + + async def _handle_file_download(self, raw_response: Any, file_path: str) -> str: + try: + with open(file_path, "wb") as download_file: + async for chunk in raw_response.aiter_raw(): + download_file.write(chunk) + except Exception as err: + raise ErrorCode.UNABLE_TO_DOWNLOAD_FILE.exception_with_parameters( + str((hasattr(err, "strerror") and err.strerror) or err), file_path + ) + return file_path + + async def _call_api_internal( + self, + api, + path, + params, + binary_data=None, + download_file_path=None, + text_response=False, + ): + token = request_id_var.set(str(uuid.uuid4())) + try: + params["headers"]["X-Atlan-Request-Id"] = request_id_var.get() + timeout = httpx.Timeout( + None, connect=self.connect_timeout, read=self.read_timeout + ) + if binary_data: + response = await self._async_session.request( + api.method.value, + path, + data=binary_data, + **params, + timeout=timeout, + ) + elif api.consumes == EVENT_STREAM and api.produces == EVENT_STREAM: + async with self._async_session.stream( + api.method.value, + path, + **params, + timeout=timeout, + ) as stream_response: + if download_file_path: + return await self._handle_file_download( + stream_response, download_file_path + ) + + content = await stream_response.aread() + text = content.decode("utf-8") if content else "" + lines = [] + + if stream_response.status_code == api.expected_status: + lines = text.splitlines() if text else [] + + response = SimpleNamespace( + status_code=stream_response.status_code, + headers=stream_response.headers, + text=text, + content=content, + _stream_lines=lines, + json=lambda: json.loads(text) if text else {}, + ) + else: + response = await self._async_session.request( + api.method.value, + path, + **params, + timeout=timeout, + ) + if response is not None: + LOGGER.debug("HTTP Status: %s", response.status_code) + if response is None: + return None + + if ( + self._401_has_retried.get() + and response.status_code + != ErrorCode.AUTHENTICATION_PASSTHROUGH.http_error_code + ): + self._401_has_retried.set(False) + + if response.status_code == api.expected_status: + try: + if ( + response.content is None + or response.content == "null" + or len(response.content) == 0 + or response.status_code == HTTPStatus.NO_CONTENT + ): + return None + events = [] + if LOGGER.isEnabledFor(logging.DEBUG): + LOGGER.debug( + "<== __call_api(%s,%s), result = %s", + vars(api), + params, + response, + ) + if api.consumes == EVENT_STREAM and api.produces == EVENT_STREAM: + if hasattr(response, "_stream_lines"): + for line in response._stream_lines: + if not line: + continue + if not line.startswith("data: "): + raise ErrorCode.UNABLE_TO_DESERIALIZE.exception_with_parameters( + line + ) + events.append(json.loads(line.split("data: ")[1])) + if text_response: + response_ = response.text + else: + response_ = ( + events + if events + else await AsyncAtlanResponse( + raw_json=response.json(), + client=self, # type: ignore[arg-type] + ).to_dict() + ) + LOGGER.debug("response: %s", response_) + return response_ + except (json.decoder.JSONDecodeError,) as e: + raise ErrorCode.JSON_ERROR.exception_with_parameters( + response.text, response.status_code, str(e) + ) from e + elif response.status_code == HTTPStatus.SERVICE_UNAVAILABLE: + LOGGER.error( + "Atlas Service unavailable. HTTP Status: %s", + HTTPStatus.SERVICE_UNAVAILABLE, + ) + return None + else: + with contextlib.suppress(ValueError, json.decoder.JSONDecodeError): + error_info = json.loads(response.text) + error_code = ( + error_info.get("errorCode", 0) + or error_info.get("code", 0) + or error_info.get("status") + ) + error_message = error_info.get( + "errorMessage", "" + ) or error_info.get("message", "") + error_doc = ( + error_info.get("doc") + or error_info.get("errorDoc") + or error_info.get("errorDocument") + or error_info.get("errorDocumentation") + ) + error_cause = error_info.get("errorCause", []) + causes = error_info.get("causes", []) + backend_error_id = error_info.get("errorId") + + error_cause_details = [ + f"ErrorType: {cause.get('errorType', 'Unknown')}, " + f"Message: {cause.get('errorMessage', 'No additional information provided')}, " + f"Location: {cause.get('location', 'Unknown location')}" + for cause in causes + ] + error_cause_details_str = ( + "\n".join(error_cause_details) if error_cause_details else "" + ) + + if ( + (self._user_id or self._async_oauth_token_manager) + and not self._401_has_retried.get() + and response.status_code + == ErrorCode.AUTHENTICATION_PASSTHROUGH.http_error_code + ): + try: + LOGGER.debug("Starting async 401 automatic token refresh.") + return await self._handle_401_token_refresh( + api, + path, + params, + binary_data=binary_data, + download_file_path=download_file_path, + text_response=text_response, + ) + except Exception as e: + LOGGER.debug( + "Async API call failed after a successful 401 " + "token refresh. Error details: %s", + e, + ) + raise + + if error_code and error_message: + error = ERROR_CODE_FOR_HTTP_STATUS.get( + response.status_code, ErrorCode.ERROR_PASSTHROUGH + ) + raise error.exception_with_parameters( + error_code, + error_message, + error_cause_details_str, + error_cause=error_cause, + backend_error_id=backend_error_id, + error_doc=error_doc, + ) + raise AtlanError( + SimpleNamespace( + http_error_code=response.status_code, + error_id=f"ATLAN-PYTHON-{response.status_code}-000", + error_message=response.text, + user_action=ErrorCode.ERROR_PASSTHROUGH.user_action, + ) + ) + finally: + request_id_var.reset(token) + + async def _handle_401_token_refresh( + self, + api, + path, + params, + binary_data=None, + download_file_path=None, + text_response=False, + ): + if self._async_oauth_token_manager: + await self._async_oauth_token_manager.invalidate_token() + token = await self._async_oauth_token_manager.get_token() + params["headers"]["authorization"] = f"Bearer {token}" + self._401_has_retried.set(True) + LOGGER.debug("Successfully refreshed async OAuth token after 401.") + return await self._call_api_internal( + api, + path, + params, + binary_data=binary_data, + download_file_path=download_file_path, + text_response=text_response, + ) + + try: + new_token = await self.impersonate.user(user_id=self._user_id) + except Exception as e: + LOGGER.debug( + "Failed to impersonate user %s for async 401 token refresh. " + "Not retrying. Error: %s", + self._user_id, + e, + ) + raise + self.api_key = new_token + self._401_has_retried.set(True) + params["headers"]["authorization"] = f"Bearer {self.api_key}" + self._request_params["headers"]["authorization"] = f"Bearer {self.api_key}" + LOGGER.debug("Successfully completed async 401 automatic token refresh.") + + retry_count = 1 + while retry_count <= self.retry.total: + try: + response = await self.typedef.get( + type_category=[AtlanTypeCategory.STRUCT] + ) + if response and response.struct_defs: + break + except Exception as e: + LOGGER.debug( + "Retrying async to get typedefs (to ensure token is active) " + "after token refresh failed: %s", + e, + ) + await asyncio.sleep(retry_count) + retry_count += 1 + + return await self._call_api_internal( + api, + path, + params, + binary_data=binary_data, + download_file_path=download_file_path, + text_response=text_response, + ) + + # ------------------------------------------------------------------ + # File upload / download helpers + # ------------------------------------------------------------------ + + async def _upload_file(self, api, file=None, filename=None): + generator = MultipartDataGenerator() + generator.add_file(file=file, filename=filename) + post_data = generator.get_post_data() + api.produces = f"multipart/form-data; boundary={generator.boundary}" + path = self._create_path(api) + params = await self._create_params(api, query_params=None, request_obj=None) + if LOGGER.isEnabledFor(logging.DEBUG): + self._api_logger(api, path) + return await self._call_api_internal(api, path, params, binary_data=post_data) + + async def _s3_presigned_url_file_upload(self, api: API, upload_file: Any): + path = self._create_path(api) + params = copy.deepcopy(self._request_params) + params["headers"].pop("authorization", None) + return await self._call_api_internal(api, path, params, binary_data=upload_file) + + async def _azure_blob_presigned_url_file_upload(self, api: API, upload_file: Any): + path = self._create_path(api) + params = copy.deepcopy(self._request_params) + params["headers"].pop("authorization", None) + params["headers"]["x-ms-blob-type"] = "BlockBlob" + return await self._call_api_internal(api, path, params, binary_data=upload_file) + + async def _gcs_presigned_url_file_upload(self, api: API, upload_file: Any): + path = self._create_path(api) + params = copy.deepcopy(self._request_params) + params["headers"].pop("authorization", None) + return await self._call_api_internal(api, path, params, binary_data=upload_file) + + async def _presigned_url_file_download(self, api: API, file_path: str): + path = self._create_path(api) + params = copy.deepcopy(self._request_params) + params["headers"].pop("authorization", None) + return await self._call_api_internal( + api, path, params, download_file_path=file_path + ) + + # ------------------------------------------------------------------ + # High-level convenience methods + # ------------------------------------------------------------------ + + async def upload_image(self, file, filename: str) -> AtlanImage: + """ + Uploads an image from the provided local file. + + :param file: local file to upload + :param filename: name of the file to be uploaded + :returns: details of the uploaded image + :raises AtlanError: on any API communication issue + """ + raw_json = await self._upload_file(UPLOAD_IMAGE, file=file, filename=filename) + return msgspec.convert(raw_json, AtlanImage, strict=False) + + async def search(self, criteria): + """Search assets. Delegates to asset.search().""" + from warnings import warn + + warn( + "This method is deprecated, please use 'asset.search' instead, which offers identical functionality.", + DeprecationWarning, + stacklevel=2, + ) + return await self.asset.search(criteria=criteria) + + async def parse_query(self, query: QueryParserRequest) -> Optional[ParsedQuery]: + """ + Parses the provided query to describe its component parts. + + :param query: query to parse and configuration options + :returns: parsed explanation of the query + :raises AtlanError: on any API communication issue + """ + raw_json = await self._call_api( + PARSE_QUERY, + request_obj=query, + ) + return msgspec.convert(raw_json, ParsedQuery, strict=False) + + # ------------------------------------------------------------------ + # Retry context manager + # ------------------------------------------------------------------ + + @contextlib.asynccontextmanager # type: ignore[arg-type] + async def max_retries( + self, max_retries: Retry = CONNECTION_RETRY + ) -> _AsyncGeneratorContextManager[None]: # type: ignore[misc] + """ + Async context manager that temporarily changes retry parameters. + + The original Retry configuration is restored when the context exits. + """ + current_transport = self._async_session._transport + + transport_kwargs: Dict[str, Any] = {} + if self.proxy: + transport_kwargs["proxy"] = self.proxy + if self.verify is not None: + transport_kwargs["verify"] = self.verify + + new_transport = PyatlanAsyncTransport(retry=max_retries, **transport_kwargs) + self._async_session._transport = new_transport + + LOGGER.debug( + "max_retries set to total: %s force_list: %s", + max_retries.total, + max_retries.status_forcelist, + ) + try: + LOGGER.debug("Entering max_retries") + yield None # type: ignore[misc] + LOGGER.debug("Exiting max_retries") + except httpx.TransportError as err: + LOGGER.exception("Exception in max retries") + raise ErrorCode.RETRY_OVERRUN.exception_with_parameters() from err + finally: + self._async_session._transport = current_transport + LOGGER.debug( + "max_retries restored %s", + self._async_session._transport.retry, # type: ignore[attr-defined] + ) + + # ------------------------------------------------------------------ + # Lifecycle + # ------------------------------------------------------------------ + + async def aclose(self): + """Close the async HTTP session and clean up resources.""" + if self._async_session: + await self._async_session.aclose() + self._async_session = None + if self._async_oauth_token_manager: + await self._async_oauth_token_manager.aclose() + self._async_oauth_token_manager = None + self._clients = {} + self._caches = {} + + async def __aenter__(self): + return self + + async def __aexit__(self, exc_type, exc_val, exc_tb): + await self.aclose() + + +@contextlib.asynccontextmanager +async def client_connection( + client: AsyncAtlanClient, + base_url: Optional[str] = None, + api_key: Optional[str] = None, + connect_timeout: float = 30.0, + read_timeout: float = 120.0, + retry: Retry = DEFAULT_RETRY, +) -> AsyncGenerator[AsyncAtlanClient, None]: + """ + Creates a temporary async client with the given base_url and/or api_key. + + :param client: existing client to clone settings from + :param base_url: the base_url for the new connection (uses current if not specified) + :param api_key: the api_key for the new connection (uses current if not specified) + """ + tmp_client = AsyncAtlanClient( + base_url=base_url or client.base_url, + api_key=api_key or client.api_key, + connect_timeout=connect_timeout, + read_timeout=read_timeout, + retry=retry, + ) + try: + yield tmp_client + finally: + await tmp_client.aclose() diff --git a/pyatlan_v9/client/aio/audit.py b/pyatlan_v9/client/aio/audit.py new file mode 100644 index 000000000..44be49171 --- /dev/null +++ b/pyatlan_v9/client/aio/audit.py @@ -0,0 +1,73 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +from __future__ import annotations + +from pyatlan.client.common import AsyncApiCaller, AuditSearch +from pyatlan.errors import ErrorCode +from pyatlan_v9.client.audit import _parse_entity_audits +from pyatlan_v9.model.aio.audit import AsyncAuditSearchResults +from pyatlan_v9.model.audit import AuditSearchRequest +from pyatlan_v9.validate import validate_arguments + + +class V9AsyncAuditClient: + """ + Async version of AuditClient that can be used to configure and run a search + against Atlan's activity log. This class does not need to be instantiated + directly but can be obtained through the audit property of AsyncAtlanClient. + """ + + def __init__(self, client: AsyncApiCaller): + if not isinstance(client, AsyncApiCaller): + raise ErrorCode.INVALID_PARAMETER_TYPE.exception_with_parameters( + "client", "AsyncApiCaller" + ) + self._client = client + + @validate_arguments + async def search( + self, criteria: AuditSearchRequest, bulk=False + ) -> AsyncAuditSearchResults: + """ + Search for assets using the provided criteria (async version). + `Note:` if the number of results exceeds the predefined threshold + (10,000 assets) this will be automatically converted into an audit `bulk` search. + + :param criteria: detailing the search query, parameters, and so on to run + :param bulk: whether to run the search to retrieve assets that match the supplied criteria, + for large numbers of results (> `10,000`), defaults to `False`. Note: this will reorder the results + (based on creation timestamp) in order to iterate through a large number (more than `10,000`) results. + :raises InvalidRequestError: + + - if audit bulk search is enabled (`bulk=True`) and any + user-specified sorting options are found in the search request. + - if audit bulk search is disabled (`bulk=False`) and the number of results + exceeds the predefined threshold (i.e: `10,000` assets) + and any user-specified sorting options are found in the search request. + + :raises AtlanError: on any API communication issue + :returns: the results of the search + """ + endpoint, request_obj = AuditSearch.prepare_request(criteria, bulk) + raw_json = await self._client._call_api(endpoint, request_obj=request_obj) + + entity_audits = _parse_entity_audits(raw_json) + count = raw_json.get("totalCount", 0) + aggregations = raw_json.get("aggregations") + + if AuditSearch.check_for_bulk_search( + count, criteria, bulk, AsyncAuditSearchResults + ): + return await self.search(criteria) + + return AsyncAuditSearchResults( + client=self._client, + criteria=criteria, + start=criteria.dsl.from_, + size=criteria.dsl.size, + entity_audits=entity_audits, + count=count, + bulk=bulk, + aggregations=aggregations, + ) diff --git a/pyatlan_v9/client/aio/batch.py b/pyatlan_v9/client/aio/batch.py new file mode 100644 index 000000000..3a31c8fe9 --- /dev/null +++ b/pyatlan_v9/client/aio/batch.py @@ -0,0 +1,35 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. +from __future__ import annotations + +from typing import Optional, cast + +from pyatlan.client.aio.batch import AsyncBatch as _LegacyAsyncBatch +from pyatlan_v9.model.assets import Asset, AtlasGlossaryTerm +from pyatlan_v9.model.response import AssetMutationResponse + + +class AsyncBatch(_LegacyAsyncBatch): + """V9 wrapper around the legacy ``AsyncBatch`` class. + + Overrides ``add()`` to accept v9 ``msgspec.Struct`` assets without + going through Pydantic's ``_convert_to_real_type_`` validator, and + overrides the tracking helper so v9 ``AtlasGlossaryTerm`` instances + are handled correctly. + """ + + async def add(self, single) -> Optional[AssetMutationResponse]: + self._batch.append(single) + return await self._process() + + @staticmethod + def __track(tracker, candidate): + if ( + isinstance(candidate, AtlasGlossaryTerm) + or getattr(candidate, "type_name", None) == "AtlasGlossaryTerm" + ): + asset = cast(Asset, type(candidate).ref_by_guid(candidate.guid)) + else: + asset = candidate.trim_to_required() + asset.name = candidate.name + tracker.append(asset) diff --git a/pyatlan_v9/client/aio/contract.py b/pyatlan_v9/client/aio/contract.py new file mode 100644 index 000000000..5de2be5b2 --- /dev/null +++ b/pyatlan_v9/client/aio/contract.py @@ -0,0 +1,50 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. +from __future__ import annotations + +from typing import Optional + +from pyatlan.client.common import AsyncApiCaller +from pyatlan.client.constants import CONTRACT_INIT_API +from pyatlan.errors import ErrorCode +from pyatlan_v9.model.assets import Asset +from pyatlan_v9.model.contract import InitRequest +from pyatlan_v9.validate import validate_arguments + + +class V9AsyncContractClient: + """ + Async version of ContractClient for data contract-specific operations. + This class does not need to be instantiated directly but can be obtained + through the contracts property of AsyncAtlanClient. + """ + + def __init__(self, client: AsyncApiCaller): + if not isinstance(client, AsyncApiCaller): + raise ErrorCode.INVALID_PARAMETER_TYPE.exception_with_parameters( + "client", "AsyncApiCaller" + ) + self._client = client + + @validate_arguments + async def generate_initial_spec( + self, + asset: Asset, + ) -> Optional[str]: + """ + Generate an initial contract spec for the provided asset (async version). + The asset must have at least its `qualifiedName` (and `typeName`) populated. + + :param asset: for which to generate the initial contract spec + + :raises AtlanError: if there is an issue interacting with the API + :returns: YAML for the initial contract spec for the provided asset + """ + request_obj = InitRequest( + asset_type=asset.type_name, + asset_qualified_name=asset.qualified_name, + ) + response = await self._client._call_api( + CONTRACT_INIT_API, request_obj=request_obj + ) + return response.get("contract") diff --git a/pyatlan_v9/client/aio/credential.py b/pyatlan_v9/client/aio/credential.py new file mode 100644 index 000000000..01f672d3a --- /dev/null +++ b/pyatlan_v9/client/aio/credential.py @@ -0,0 +1,158 @@ +from __future__ import annotations + +from typing import Any, Dict, Optional + +import msgspec + +from pyatlan.client.common import ( + AsyncApiCaller, + CredentialCreate, + CredentialGet, + CredentialGetAll, + CredentialPurge, + CredentialTestAndUpdate, +) +from pyatlan.client.constants import TEST_CREDENTIAL +from pyatlan.errors import ErrorCode +from pyatlan_v9.model.credential import ( + Credential, + CredentialListResponse, + CredentialResponse, + CredentialTestResponse, +) +from pyatlan_v9.validate import validate_arguments + + +class V9AsyncCredentialClient: + """ + Async version of CredentialClient for managing credentials within the Atlan platform. + This class does not need to be instantiated directly but can be obtained through the credentials property of AsyncAtlanClient. + """ + + def __init__(self, client: AsyncApiCaller): + if not isinstance(client, AsyncApiCaller): + raise ErrorCode.INVALID_PARAMETER_TYPE.exception_with_parameters( + "client", "AsyncApiCaller" + ) + self._client = client + + @validate_arguments + async def creator( + self, credential: Credential, test: bool = True + ) -> CredentialResponse: + """ + Create a new credential (async version). + + :param credential: provide full details of the credential's to be created. + :param test: whether to validate the credentials (`True`) or skip validation + (`False`) before creation, defaults to `True`. + :returns: A CredentialResponse instance. + :raises ValidationError: If the provided `credential` is invalid. + :raises InvalidRequestError: If `test` is `False` and the credential contains a `username` or `password`. + """ + CredentialCreate.validate_request(credential, test) + endpoint, query_params = CredentialCreate.prepare_request(test) + raw_json = await self._client._call_api( + api=endpoint, + query_params=query_params, + request_obj=credential, + ) + return msgspec.convert(raw_json, CredentialResponse, strict=False) + + @validate_arguments + async def get(self, guid: str) -> CredentialResponse: + """ + Retrieves a credential by its unique identifier (GUID) (async version). + Note that this will never contain sensitive information + in the credential, such as usernames, passwords or client secrets or keys. + + :param guid: GUID of the credential. + :returns: A CredentialResponse instance. + :raises: AtlanError on any error during API invocation. + """ + endpoint = CredentialGet.prepare_request(guid) + raw_json = await self._client._call_api(endpoint) + if not isinstance(raw_json, dict): + return raw_json + return msgspec.convert(raw_json, CredentialResponse, strict=False) + + @validate_arguments + async def get_all( + self, + filter: Optional[Dict[str, Any]] = None, + limit: Optional[int] = None, + offset: Optional[int] = None, + workflow_name: Optional[str] = None, + ) -> CredentialListResponse: + """ + Retrieves all credentials (async version). + + :param filter: (optional) dictionary specifying the filter criteria. + :param limit: (optional) maximum number of credentials to retrieve. + :param offset: (optional) number of credentials to skip before starting retrieval. + :param workflow_name: (optional) name of the workflow to retrieve credentials for. + :returns: CredentialListResponse instance. + :raises: AtlanError on any error during API invocation. + """ + endpoint, params = CredentialGetAll.prepare_request( + filter, limit, offset, workflow_name + ) + raw_json = await self._client._call_api(endpoint, query_params=params) + if not isinstance(raw_json, dict) or "records" not in raw_json: + raise ErrorCode.JSON_ERROR.exception_with_parameters( + "No records found in response", + 400, + "API response did not contain the expected 'records' key", + ) + if raw_json.get("records") is None: + raw_json["records"] = [] + return msgspec.convert(raw_json, CredentialListResponse, strict=False) + + @validate_arguments + async def purge_by_guid(self, guid: str) -> CredentialResponse: + """ + Hard-deletes (purges) credential by their unique identifier (GUID) (async version). + This operation is irreversible. + + :param guid: unique identifier(s) (GUIDs) of credential to hard-delete + :returns: details of the hard-deleted asset(s) + :raises AtlanError: on any API communication issue + """ + endpoint = CredentialPurge.prepare_request(guid) + raw_json = await self._client._call_api(endpoint) + return raw_json + + @validate_arguments + async def test(self, credential: Credential) -> CredentialTestResponse: + """ + Tests the given credential by sending it to Atlan for validation (async version). + + :param credential: The credential to be tested. + :type credential: A CredentialTestResponse instance. + :returns: The response indicating the test result. + :raises ValidationError: If the provided credential is invalid type. + :raises AtlanError: On any error during API invocation. + """ + raw_json = await self._client._call_api(TEST_CREDENTIAL, request_obj=credential) + return msgspec.convert(raw_json, CredentialTestResponse, strict=False) + + @validate_arguments + async def test_and_update(self, credential: Credential) -> CredentialResponse: + """ + Updates this credential in Atlan after first + testing it to confirm its successful validation (async version). + + :param credential: The credential to be tested and updated. + :returns: An updated CredentialResponse instance. + :raises ValidationError: If the provided credential is invalid type. + :raises InvalidRequestException: if the provided credentials + cannot be validated successfully. + :raises InvalidRequestException: If the provided credential + does not have an ID. + :raises AtlanError: on any error during API invocation. + """ + test_response = await self.test(credential=credential) + CredentialTestAndUpdate.validate_test_response(test_response, credential) + endpoint = CredentialTestAndUpdate.prepare_request(credential) + raw_json = await self._client._call_api(endpoint, request_obj=credential) + return msgspec.convert(raw_json, CredentialResponse, strict=False) diff --git a/pyatlan_v9/client/aio/file.py b/pyatlan_v9/client/aio/file.py new file mode 100644 index 000000000..bcc8b6d54 --- /dev/null +++ b/pyatlan_v9/client/aio/file.py @@ -0,0 +1,92 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +from __future__ import annotations + +from pyatlan.client.common import ( + AsyncApiCaller, + FileDownload, + FilePresignedUrl, + FileUpload, +) +from pyatlan.errors import ErrorCode +from pyatlan_v9.model.file import PresignedURLRequest +from pyatlan_v9.validate import validate_arguments + + +class V9AsyncFileClient: + """ + Async version of FileClient for operating on Atlan's tenant object storage. + This class does not need to be instantiated directly but can be obtained + through the files property of AsyncAtlanClient. + """ + + def __init__(self, client: AsyncApiCaller): + if not isinstance(client, AsyncApiCaller): + raise ErrorCode.INVALID_PARAMETER_TYPE.exception_with_parameters( + "client", "AsyncApiCaller" + ) + self._client = client + + @validate_arguments + async def generate_presigned_url(self, request: PresignedURLRequest) -> str: + """ + Generates a presigned URL based on Atlan's tenant object store. + + :param request: instance containing object key, + expiry, and method (PUT: upload, GET: download). + :raises AtlanError: on any error during API invocation. + :returns: a response object containing a presigned URL with its cloud provider. + """ + endpoint, request_obj = FilePresignedUrl.prepare_request(request) + raw_json = await self._client._call_api(endpoint, request_obj=request_obj) + return FilePresignedUrl.process_response(raw_json) + + @validate_arguments + async def upload_file(self, presigned_url: str, file_path: str) -> None: + """ + Uploads a file to Atlan's object storage. + + :param presigned_url: any valid presigned URL. + :param file_path: path to the file to be uploaded. + :raises AtlanError: on any error during API invocation. + :raises InvalidRequestException: if the upload file path is invalid, + or when the presigned URL cloud provider is unsupported. + """ + upload_file = FileUpload.validate_file_path(file_path) + provider = FileUpload.identify_cloud_provider(presigned_url) + if provider == "s3": + endpoint = FileUpload.prepare_s3_request(presigned_url) + return await self._client._s3_presigned_url_file_upload( + upload_file=upload_file, api=endpoint + ) + elif provider == "azure_blob": + endpoint = FileUpload.prepare_azure_request(presigned_url) + return await self._client._azure_blob_presigned_url_file_upload( + upload_file=upload_file, api=endpoint + ) + elif provider == "gcs": + endpoint = FileUpload.prepare_gcs_request(presigned_url) + return await self._client._gcs_presigned_url_file_upload( + upload_file=upload_file, api=endpoint + ) + + @validate_arguments + async def download_file( + self, + presigned_url: str, + file_path: str, + ) -> str: + """ + Downloads a file from Atlan's tenant object storage. + + :param presigned_url: any valid presigned URL. + :param file_path: path to the file where you want to download the file. + :raises InvalidRequestException: if unable to download the file. + :raises AtlanError: on any error during API invocation. + :returns: full path to the downloaded file. + """ + endpoint = FileDownload.prepare_request(presigned_url) + return await self._client._presigned_url_file_download( + file_path=file_path, api=endpoint + ) diff --git a/pyatlan_v9/client/aio/group.py b/pyatlan_v9/client/aio/group.py new file mode 100644 index 000000000..d9e6ce338 --- /dev/null +++ b/pyatlan_v9/client/aio/group.py @@ -0,0 +1,224 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. +from __future__ import annotations + +from typing import List, Optional + +import msgspec + +from pyatlan.client.common import AsyncApiCaller +from pyatlan.client.constants import ( + CREATE_GROUP, + DELETE_GROUP, + GET_GROUP_MEMBERS, + GET_GROUPS, + REMOVE_USERS_FROM_GROUP, + UPDATE_GROUP, +) +from pyatlan.errors import ErrorCode +from pyatlan_v9.model.aio.group import AsyncGroupResponse +from pyatlan_v9.model.aio.user import AsyncUserResponse +from pyatlan_v9.model.group import ( + AtlanGroup, + CreateGroupRequest, + CreateGroupResponse, + GroupRequest, + RemoveFromGroupRequest, +) +from pyatlan_v9.model.user import AtlanUser, UserRequest +from pyatlan_v9.validate import validate_arguments + + +class V9AsyncGroupClient: + """ + Async version of GroupClient for retrieving information about groups. + This class does not need to be instantiated directly but can be obtained + through the group property of AsyncAtlanClient. + """ + + def __init__(self, client: AsyncApiCaller): + if not isinstance(client, AsyncApiCaller): + raise ErrorCode.INVALID_PARAMETER_TYPE.exception_with_parameters( + "client", "AsyncApiCaller" + ) + self._client = client + + @validate_arguments + async def creator( + self, + group: AtlanGroup, + user_ids: Optional[List[str]] = None, + ) -> CreateGroupResponse: + """ + Create a new group. + + :param group: details of the new group + :param user_ids: list of unique identifiers (GUIDs) of users to associate with the group + :returns: details of the created group and user association + :raises AtlanError: on any API communication issue + """ + payload = CreateGroupRequest(group=group) + if user_ids: + payload.users = user_ids + raw_json = await self._client._call_api(CREATE_GROUP, request_obj=payload) + return msgspec.convert(raw_json, CreateGroupResponse, strict=False) + + @validate_arguments + async def updater(self, group: AtlanGroup) -> None: + """ + Update a group. Note that the provided 'group' must have its id populated. + + :param group: details to update on the group + :raises AtlanError: on any API communication issue + """ + endpoint = UPDATE_GROUP.format_path_with_params(group.id) + await self._client._call_api(endpoint, request_obj=group) + + @validate_arguments + async def purge(self, guid: str) -> None: + """ + Delete a group. + + :param guid: unique identifier (GUID) of the group to delete + :raises AtlanError: on any API communication issue + """ + endpoint = DELETE_GROUP.format_path({"group_guid": guid}) + await self._client._call_api(endpoint) + + @validate_arguments + async def get( + self, + limit: Optional[int] = 20, + post_filter: Optional[str] = None, + sort: Optional[str] = None, + count: bool = True, + offset: int = 0, + columns: Optional[List[str]] = None, + ) -> AsyncGroupResponse: + """ + Retrieves an AsyncGroupResponse which contains a list of the groups defined in Atlan. + + :param limit: maximum number of results to be returned + :param post_filter: which groups to retrieve + :param sort: property by which to sort the results + :param count: whether to return the total number of records (True) or not (False) + :param offset: starting point for results to return, for paging + :param columns: provides columns projection support for groups endpoint + :returns: an AsyncGroupResponse which contains a list of groups that match the provided criteria + :raises AtlanError: on any API communication issue + """ + request = GroupRequest( + post_filter=post_filter, + limit=limit, + sort=sort, + count=count, + offset=offset, + columns=columns, + ) + endpoint = GET_GROUPS.format_path_with_params() + raw_json = await self._client._call_api( + api=endpoint, query_params=request.query_params + ) + records = None + if raw_records := raw_json.get("records"): + records = msgspec.convert(raw_records, list[AtlanGroup], strict=False) + response = AsyncGroupResponse( + total_record=raw_json.get("totalRecord"), + filter_record=raw_json.get("filterRecord"), + records=records, + ) + response._size = limit or 20 + response._start = offset + response._endpoint = GET_GROUPS + response._client = self._client + response._criteria = request + return response + + @validate_arguments + async def get_all( + self, + limit: int = 20, + offset: int = 0, + sort: Optional[str] = "name", + columns: Optional[List[str]] = None, + ) -> AsyncGroupResponse: + """ + Retrieve an AsyncGroupResponse containing a list of all groups defined in Atlan. + + :param limit: maximum number of results to be returned + :param offset: starting point for the list of groups when paging + :param sort: property by which to sort the results, by default : name + :param columns: provides columns projection support for groups endpoint + :returns: an AsyncGroupResponse with all groups based on the parameters; results are iterable. + """ + return await self.get(offset=offset, limit=limit, sort=sort, columns=columns) + + @validate_arguments + async def get_by_name( + self, + alias: str, + limit: int = 20, + offset: int = 0, + ) -> Optional[AsyncGroupResponse]: + """ + Retrieves an AsyncGroupResponse containing a list of groups that match the specified string. + + :param alias: name (as it appears in the UI) on which to filter the groups + :param limit: maximum number of groups to retrieve + :param offset: starting point for the list of groups when paging + :returns: an AsyncGroupResponse containing a list of groups whose UI names include the given string; the results are iterable. + """ + return await self.get( + offset=offset, + limit=limit, + post_filter='{"$and":[{"alias":{"$ilike":"%' + alias + '%"}}]}', + ) + + @validate_arguments + async def get_members( + self, guid: str, request: Optional[UserRequest] = None + ) -> AsyncUserResponse: + """ + Retrieves an AsyncUserResponse object which contains a list of the members (users) of a group. + + :param guid: unique identifier (GUID) of the group from which to retrieve members + :param request: request containing details about which members to retrieve + :returns: an AsyncUserResponse object which contains a list of users that are members of the group + :raises AtlanError: on any API communication issue + """ + if not request: + request = UserRequest() + endpoint_obj = GET_GROUP_MEMBERS.format_path({"group_guid": guid}) + raw_json = await self._client._call_api( + api=endpoint_obj.format_path_with_params(), + query_params=request.query_params, + ) + records = None + if raw_records := raw_json.get("records"): + records = msgspec.convert(raw_records, list[AtlanUser], strict=False) + response = AsyncUserResponse( + total_record=raw_json.get("totalRecord"), + filter_record=raw_json.get("filterRecord"), + records=records, + ) + response._size = request.limit or 20 + response._start = request.offset or 0 + response._endpoint = endpoint_obj + response._client = self._client + response._criteria = request + return response + + @validate_arguments + async def remove_users( + self, guid: str, user_ids: Optional[List[str]] = None + ) -> None: + """ + Remove one or more users from a group. + + :param guid: unique identifier (GUID) of the group from which to remove users + :param user_ids: unique identifiers (GUIDs) of the users to remove from the group + :raises AtlanError: on any API communication issue + """ + rfgr = RemoveFromGroupRequest(users=user_ids or []) + endpoint = REMOVE_USERS_FROM_GROUP.format_path({"group_guid": guid}) + await self._client._call_api(endpoint, request_obj=rfgr) diff --git a/pyatlan_v9/client/aio/impersonate.py b/pyatlan_v9/client/aio/impersonate.py new file mode 100644 index 000000000..7c60d36e3 --- /dev/null +++ b/pyatlan_v9/client/aio/impersonate.py @@ -0,0 +1,127 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +from __future__ import annotations + +import logging +from typing import Union + +import msgspec + +from pyatlan.client.common import ( + AsyncApiCaller, + ImpersonateEscalate, + ImpersonateGetClientSecret, + ImpersonateGetUserId, + ImpersonateUser, +) +from pyatlan.errors import AtlanError, ErrorCode +from pyatlan_v9.model.response import AccessTokenResponse + +LOGGER = logging.getLogger(__name__) + + +class V9AsyncImpersonationClient: + """ + Async version of ImpersonationClient for impersonating users as part of Atlan automations. + Note: this will only work when run as part of Atlan's packaged workflow ecosystem (running in the cluster back-end). + """ + + def __init__(self, client: AsyncApiCaller): + if not isinstance(client, AsyncApiCaller): + raise ErrorCode.INVALID_PARAMETER_TYPE.exception_with_parameters( + "client", "AsyncApiCaller" + ) + self._client = client + + async def user(self, user_id: str) -> str: + """ + Retrieves a bearer token that impersonates the provided user. + + :param user_id: unique identifier of the user to impersonate + :returns: a bearer token that impersonates the provided user + :raises AtlanError: on any API communication issue + """ + client_info = ImpersonateUser.get_client_info() + endpoint, credentials = ImpersonateUser.prepare_request(client_info) + + LOGGER.debug("Getting token with client id and secret") + try: + raw_json = await self._client._call_api(endpoint, request_obj=credentials) + argo_token = msgspec.convert( + raw_json, AccessTokenResponse, strict=False + ).access_token + except AtlanError as atlan_err: + raise ErrorCode.UNABLE_TO_ESCALATE.exception_with_parameters() from atlan_err + + LOGGER.debug("Getting token with subject token") + try: + endpoint, user_credentials = ImpersonateUser.prepare_impersonation_request( + client_info, argo_token, user_id + ) + raw_json = await self._client._call_api( + endpoint, request_obj=user_credentials + ) + return msgspec.convert( + raw_json, AccessTokenResponse, strict=False + ).access_token + except AtlanError as atlan_err: + raise ErrorCode.UNABLE_TO_IMPERSONATE.exception_with_parameters() from atlan_err + + async def escalate(self) -> str: + """ + Escalate to a privileged user on a short-term basis. + Note: this is only possible from within the Atlan tenant, and only when given the appropriate credentials. + + :returns: a short-lived bearer token with escalated privileges + :raises AtlanError: on any API communication issue + """ + client_info = ImpersonateEscalate.get_client_info() + endpoint, credentials = ImpersonateEscalate.prepare_request(client_info) + + try: + raw_json = await self._client._call_api(endpoint, request_obj=credentials) + return msgspec.convert( + raw_json, AccessTokenResponse, strict=False + ).access_token + except AtlanError as atlan_err: + raise ErrorCode.UNABLE_TO_ESCALATE.exception_with_parameters() from atlan_err + + async def get_client_secret(self, client_guid: str) -> Union[str, None]: + """ + Retrieves the client secret associated with the given client GUID + + :param client_guid: GUID of the client whose secret is to be retrieved + :returns: client secret if available, otherwise `None` + :raises: + - AtlanError: If an API error occurs. + - InvalidRequestError: If the provided GUID is invalid or retrieval fails. + """ + try: + endpoint = ImpersonateGetClientSecret.prepare_request(client_guid) + raw_json = await self._client._call_api(endpoint) + return ImpersonateGetClientSecret.process_response(raw_json) + except AtlanError as e: + raise ErrorCode.UNABLE_TO_RETRIEVE_CLIENT_SECRET.exception_with_parameters( + client_guid + ) from e + + async def get_user_id(self, username: str) -> Union[str, None]: + """ + Retrieves the user ID from Keycloak for the specified username. + This method is particularly useful for impersonating API tokens. + + :param username: username of the user whose ID needs to be retrieved. + :returns: Keycloak user ID + :raises: + - AtlanError: If an API error occurs. + - InvalidRequestError: If an error occurs while fetching the user ID from Keycloak. + """ + try: + endpoint, query_params = ImpersonateGetUserId.prepare_request(username) + raw_json = await self._client._call_api(endpoint, query_params=query_params) + return ImpersonateGetUserId.process_response(raw_json) + except AtlanError as e: + raise ErrorCode.UNABLE_TO_RETRIEVE_USER_GUID.exception_with_parameters( + username + ) from e diff --git a/pyatlan_v9/client/aio/oauth_client.py b/pyatlan_v9/client/aio/oauth_client.py new file mode 100644 index 000000000..175f5ae14 --- /dev/null +++ b/pyatlan_v9/client/aio/oauth_client.py @@ -0,0 +1,170 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2026 Atlan Pte. Ltd. +from __future__ import annotations + +from typing import List, Optional + +import msgspec + +from pyatlan.client.common import ( + AsyncApiCaller, + OAuthClientCreate, + OAuthClientGet, + OAuthClientGetById, + OAuthClientPurge, + OAuthClientUpdate, + RoleGet, +) +from pyatlan.client.constants import CREATE_OAUTH_CLIENT +from pyatlan.errors import ErrorCode +from pyatlan_v9.model.aio.oauth_client import AsyncOAuthClientListResponse +from pyatlan_v9.model.oauth_client import ( + OAuthClientCreateResponse, + OAuthClientRequest, + OAuthClientResponse, +) +from pyatlan_v9.validate import validate_arguments + + +class V9AsyncOAuthClient: + """ + Async client for managing OAuth client credentials. + This class does not need to be instantiated directly but can be + obtained through the oauth_client property of AsyncAtlanClient. + """ + + def __init__(self, client: AsyncApiCaller): + if not isinstance(client, AsyncApiCaller): + raise ErrorCode.INVALID_PARAMETER_TYPE.exception_with_parameters( + "client", "AsyncApiCaller" + ) + self._client = client + + @validate_arguments + async def get( + self, + limit: int = 20, + offset: int = 0, + sort: Optional[str] = None, + ) -> AsyncOAuthClientListResponse: + """ + Retrieves OAuth clients defined in Atlan with async pagination support. + + :param limit: maximum number of results to be returned per page (default: 20) + :param offset: starting point for results to return, for paging + :param sort: property by which to sort the results (e.g., 'createdAt' for descending) + :returns: an AsyncOAuthClientListResponse containing records and pagination info + :raises AtlanError: on any API communication issue + """ + endpoint, query_params = OAuthClientGet.prepare_request(limit, offset, sort) + raw_json = await self._client._call_api(endpoint, query_params) + records = None + if raw_records := raw_json.get("records"): + records = msgspec.convert( + raw_records, list[OAuthClientResponse], strict=False + ) + response = AsyncOAuthClientListResponse( + total_record=raw_json.get("totalRecord"), + filter_record=raw_json.get("filterRecord"), + records=records, + ) + response._size = limit + response._start = offset + response._endpoint = endpoint + response._client = self._client + response._sort = sort + return response + + @validate_arguments + async def get_by_id(self, client_id: str) -> OAuthClientResponse: + """ + Retrieves the OAuth client with the specified client ID. + + :param client_id: unique client identifier (e.g., 'oauth-client-xxx') + :returns: the OAuthClientResponse with the specified client ID + :raises AtlanError: on any API communication issue + """ + endpoint, query_params = OAuthClientGetById.prepare_request(client_id) + raw_json = await self._client._call_api(endpoint, query_params) + return msgspec.convert(raw_json, OAuthClientResponse, strict=False) + + @validate_arguments + async def updater( + self, + client_id: str, + display_name: Optional[str] = None, + description: Optional[str] = None, + ) -> OAuthClientResponse: + """ + Update an existing OAuth client with the provided settings. + + :param client_id: unique client identifier (e.g., 'oauth-client-xxx') + :param display_name: human-readable name for the OAuth client + :param description: optional explanation of the OAuth client + :returns: the updated OAuthClientResponse + :raises AtlanError: on any API communication issue + """ + endpoint, request_obj = OAuthClientUpdate.prepare_request( + client_id, display_name, description + ) + raw_json = await self._client._call_api(endpoint, request_obj=request_obj) + return msgspec.convert(raw_json, OAuthClientResponse, strict=False) + + @validate_arguments + async def purge(self, client_id: str) -> None: + """ + Delete (purge) the specified OAuth client. + + :param client_id: unique client identifier (e.g., 'oauth-client-xxx') + :raises AtlanError: on any API communication issue + """ + endpoint, _ = OAuthClientPurge.prepare_request(client_id) + await self._client._call_api(endpoint) + + async def _fetch_available_roles(self): + """ + Fetch all available roles (workspace and admin-subrole levels). + + :returns: list of AtlanRole objects + """ + filter_str = OAuthClientCreate.build_roles_filter() + endpoint, query_params = RoleGet.prepare_request( + limit=100, + post_filter=filter_str, + ) + raw_json = await self._client._call_api(endpoint, query_params) + response = RoleGet.process_response(raw_json) + return response.records or [] + + @validate_arguments + async def creator( + self, + name: str, + role: str, + description: Optional[str] = None, + persona_qualified_names: Optional[List[str]] = None, + ) -> OAuthClientCreateResponse: + """ + Create a new OAuth client with the provided settings. + + :param name: human-readable name for the OAuth client (displayed in UI) + :param role: role description to assign to the OAuth client (e.g., 'Admin', 'Member'). + :param description: optional explanation of the OAuth client + :param persona_qualified_names: qualified names of personas to associate with the OAuth client + :returns: the created OAuthClientCreateResponse (includes client_id and client_secret) + :raises AtlanError: on any API communication issue + :raises NotFoundError: if the specified role description is not found + """ + available_roles = await self._fetch_available_roles() + resolved_role = OAuthClientCreate.resolve_role_name(role, available_roles) + + request = OAuthClientRequest( + display_name=name, + role=resolved_role, + description=description or "", + persona_qualified_names=persona_qualified_names or [], + ) + raw_json = await self._client._call_api( + CREATE_OAUTH_CLIENT.format_path_with_params(), request_obj=request + ) + return msgspec.convert(raw_json, OAuthClientCreateResponse, strict=False) diff --git a/pyatlan_v9/client/aio/open_lineage.py b/pyatlan_v9/client/aio/open_lineage.py new file mode 100644 index 000000000..697db901e --- /dev/null +++ b/pyatlan_v9/client/aio/open_lineage.py @@ -0,0 +1,121 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +from __future__ import annotations + +from typing import Any, Dict, List, Optional, Union + +from pyatlan.client.common import ( + AsyncApiCaller, + OpenLineageCreateCredential, + OpenLineageSend, +) +from pyatlan.errors import AtlanError, ErrorCode +from pyatlan.utils import validate_type +from pyatlan_v9.model.assets.connection import Connection +from pyatlan_v9.model.credential import Credential +from pyatlan_v9.model.enums import AtlanConnectorType +from pyatlan_v9.model.open_lineage.event import OpenLineageEvent, OpenLineageRawEvent +from pyatlan_v9.model.response import AssetMutationResponse +from pyatlan_v9.validate import validate_arguments + + +class V9AsyncOpenLineageClient: + """ + Async version of OpenLineageClient for interacting with OpenLineage. + """ + + def __init__(self, client: AsyncApiCaller): + if not isinstance(client, AsyncApiCaller): + raise ErrorCode.INVALID_PARAMETER_TYPE.exception_with_parameters( + "client", "AsyncApiCaller" + ) + self._client = client + + @validate_arguments + async def create_connection( + self, + name: str, + connector_type: AtlanConnectorType = AtlanConnectorType.SPARK, + admin_users: Optional[List[str]] = None, + admin_roles: Optional[List[str]] = None, + admin_groups: Optional[List[str]] = None, + ) -> AssetMutationResponse: + """ + Creates a connection for OpenLineage. + + :param name: name for the new connection + :param connector_type: for the new connection to be associated with + :param admin_users: list of admin users to associate with this connection + :param admin_roles: list of admin roles to associate with this connection + :param admin_groups:list of admin groups to associate with this connection + :return: details of the connection created + """ + legacy_credential = OpenLineageCreateCredential.prepare_request(connector_type) + v9_credential = Credential( + auth_type=legacy_credential.auth_type, + name=legacy_credential.name, + connector=legacy_credential.connector, + connector_config_name=legacy_credential.connector_config_name, + connector_type=legacy_credential.connector_type, + extras=legacy_credential.extras, + ) + credential_response = await self._client.credentials.creator( # type: ignore[attr-defined] + credential=v9_credential + ) + + connection = Connection.creator( + client=self._client, + name=name, + connector_type=connector_type, + admin_users=admin_users, + admin_groups=admin_groups, + admin_roles=admin_roles, + ) + connection.default_credential_guid = credential_response.id + + return await self._client.asset.save(connection) # type: ignore[attr-defined] + + async def send( + self, + request: Union[ + OpenLineageEvent, + OpenLineageRawEvent, + List[Dict[str, Any]], + Dict[str, Any], + str, + ], + connector_type: AtlanConnectorType, + ) -> None: + """ + Sends the OpenLineage event to Atlan to be consumed. + + :param request: OpenLineage event to send - can be an OpenLineageEvent, OpenLineageRawEvent, list of dicts, dict, or JSON string + :param connector_type: of the connection that should receive the OpenLineage event + :raises AtlanError: when OpenLineage is not configured OR on any issues with API communication + """ + validate_type( + name="request", + _type=(OpenLineageEvent, OpenLineageRawEvent, list, dict, str), + value=request, + ) + validate_type( + name="connector_type", + _type=(AtlanConnectorType), + value=connector_type, + ) + try: + if isinstance(request, (dict, str, list)): + if isinstance(request, str): + request = OpenLineageRawEvent.parse_raw(request) + else: + request = OpenLineageRawEvent.parse_obj(request) + + api_endpoint, request_obj, api_options = OpenLineageSend.prepare_request( + request, connector_type + ) + await self._client._call_api( + request_obj=request_obj, api=api_endpoint, **api_options + ) + except AtlanError as e: + OpenLineageSend.validate_response(e, connector_type) diff --git a/pyatlan_v9/client/aio/query.py b/pyatlan_v9/client/aio/query.py new file mode 100644 index 000000000..db98db2df --- /dev/null +++ b/pyatlan_v9/client/aio/query.py @@ -0,0 +1,35 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +from __future__ import annotations + +from pyatlan.client.common import AsyncApiCaller, QueryStream +from pyatlan.errors import ErrorCode +from pyatlan_v9.model.query import QueryRequest, QueryResponse +from pyatlan_v9.validate import validate_arguments + + +class V9AsyncQueryClient: + """ + Async client for running SQL queries. + """ + + def __init__(self, client: AsyncApiCaller): + if not isinstance(client, AsyncApiCaller): + raise ErrorCode.INVALID_PARAMETER_TYPE.exception_with_parameters( + "client", "AsyncApiCaller" + ) + self._client = client + + @validate_arguments + async def stream(self, request: QueryRequest) -> QueryResponse: + """ + Runs the provided query and returns its results. + + :param: request query to run. + :returns: results of the query. + :raises : AtlanError on any issues with API communication. + """ + endpoint, request_obj = QueryStream.prepare_request(request) + raw_json = await self._client._call_api(endpoint, request_obj=request_obj) + return QueryResponse(events=raw_json) diff --git a/pyatlan_v9/client/aio/role.py b/pyatlan_v9/client/aio/role.py new file mode 100644 index 000000000..eb71fc6f1 --- /dev/null +++ b/pyatlan_v9/client/aio/role.py @@ -0,0 +1,64 @@ +from __future__ import annotations + +from typing import Optional + +import msgspec + +from pyatlan.client.common import AsyncApiCaller, RoleGet, RoleGetAll +from pyatlan.errors import ErrorCode +from pyatlan_v9.model.role import RoleResponse +from pyatlan_v9.validate import validate_arguments + + +class V9AsyncRoleClient: + """ + Async client for retrieving information about roles. + """ + + def __init__(self, client: AsyncApiCaller): + if not isinstance(client, AsyncApiCaller): + raise ErrorCode.INVALID_PARAMETER_TYPE.exception_with_parameters( + "client", "AsyncApiCaller" + ) + self._client = client + + @validate_arguments + async def get( + self, + limit: int, + post_filter: Optional[str] = None, + sort: Optional[str] = None, + count: bool = True, + offset: int = 0, + ) -> RoleResponse: + """ + Retrieves a RoleResponse which contains a list of the roles defined in Atlan. + + :param limit: maximum number of results to be returned + :param post_filter: which roles to retrieve + :param sort: property by which to sort the results + :param count: whether to return the total number of records (True) or not (False) + :param offset: starting point for results to return, for paging + :returns: None or a RoleResponse object which contains list of roles that match the provided criteria + :raises AtlanError: on any API communication issue + """ + endpoint, query_params = RoleGet.prepare_request( + limit=limit, + post_filter=post_filter, + sort=sort, + count=count, + offset=offset, + ) + raw_json = await self._client._call_api(endpoint, query_params) + return msgspec.convert(raw_json, RoleResponse, strict=False) + + async def get_all(self) -> RoleResponse: + """ + Retrieves a RoleResponse which contains a list of all the roles defined in Atlan. + + :returns: a RoleResponse which contains a list of all the roles defined in Atlan + :raises AtlanError: on any API communication issue + """ + endpoint = RoleGetAll.prepare_request() + raw_json = await self._client._call_api(endpoint) + return msgspec.convert(raw_json, RoleResponse, strict=False) diff --git a/pyatlan_v9/client/aio/search_log.py b/pyatlan_v9/client/aio/search_log.py new file mode 100644 index 000000000..1cf9e0049 --- /dev/null +++ b/pyatlan_v9/client/aio/search_log.py @@ -0,0 +1,90 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +from __future__ import annotations + +from typing import Union + +from pyatlan.client.common import AsyncApiCaller, SearchLogSearch +from pyatlan.errors import ErrorCode +from pyatlan_v9.client.search_log import ( + UNIQUE_ASSETS, + UNIQUE_USERS, + _parse_asset_views, + _parse_log_entries, + _parse_user_views, +) +from pyatlan_v9.model.aio.search_log import AsyncSearchLogResults +from pyatlan_v9.model.search_log import SearchLogRequest, SearchLogViewResults +from pyatlan_v9.validate import validate_arguments + + +class V9AsyncSearchLogClient: + """ + Async client for configuring and running searches against Atlan's search log. + """ + + def __init__(self, client: AsyncApiCaller): + if not isinstance(client, AsyncApiCaller): + raise ErrorCode.INVALID_PARAMETER_TYPE.exception_with_parameters( + "client", "AsyncApiCaller" + ) + self._client = client + + @validate_arguments + async def search( + self, criteria: SearchLogRequest, bulk=False + ) -> Union[SearchLogViewResults, AsyncSearchLogResults]: + """ + Search for search logs using the provided criteria. + `Note:` if the number of results exceeds the predefined threshold + (10,000 search logs) this will be automatically converted into an search log `bulk` search. + + :param criteria: detailing the search query, parameters, and so on to run + :param bulk: whether to run the search to retrieve search logs that match the supplied criteria, + for large numbers of results (> `10,000`), defaults to `False`. Note: this will reorder the results + (based on creation timestamp) in order to iterate through a large number (more than `10,000`) results. + :raises InvalidRequestError: + + - if search log bulk search is enabled (`bulk=True`) and any + user-specified sorting options are found in the search request. + - if search log bulk search is disabled (`bulk=False`) and the number of results + exceeds the predefined threshold (i.e: `10,000` assets) + and any user-specified sorting options are found in the search request. + + :raises AtlanError: on any API communication issue + :returns: the results of the search + """ + endpoint, request_obj = SearchLogSearch.prepare_request(criteria, bulk) + raw_json = await self._client._call_api(endpoint, request_obj=request_obj) + + count = raw_json.get("approximateCount", 0) + aggregations = raw_json.get("aggregations", {}) + + if aggregations and UNIQUE_USERS in aggregations: + user_views = _parse_user_views(raw_json) + return SearchLogViewResults(count=count, user_views=user_views) + + if aggregations and UNIQUE_ASSETS in aggregations: + asset_views = _parse_asset_views(raw_json) + return SearchLogViewResults(count=count, asset_views=asset_views) + + log_entries = _parse_log_entries(raw_json) + results = AsyncSearchLogResults( + client=self._client, + criteria=criteria, + start=criteria.dsl.from_, + size=criteria.dsl.size, + count=count, + log_entries=log_entries, + aggregations=aggregations, + bulk=bulk, + processed_log_entries_count=len(log_entries), + ) + + if SearchLogSearch.check_for_bulk_search( + results.count, criteria, bulk, AsyncSearchLogResults + ): + return await self.search(criteria) + + return results diff --git a/pyatlan_v9/client/aio/sso.py b/pyatlan_v9/client/aio/sso.py new file mode 100644 index 000000000..c7ab80d0e --- /dev/null +++ b/pyatlan_v9/client/aio/sso.py @@ -0,0 +1,201 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +from __future__ import annotations + +from typing import List + +import msgspec + +from pyatlan.client.common import AsyncApiCaller +from pyatlan.client.constants import ( + CREATE_SSO_GROUP_MAPPING, + DELETE_SSO_GROUP_MAPPING, + GET_ALL_SSO_GROUP_MAPPING, + GET_SSO_GROUP_MAPPING, + UPDATE_SSO_GROUP_MAPPING, +) +from pyatlan.errors import AtlanError, ErrorCode +from pyatlan_v9.client.sso import ( + GROUP_MAPPER_ATTRIBUTE, + GROUP_MAPPER_SYNC_MODE, + IDP_GROUP_MAPPER, + _generate_group_mapper_name, + _group_name_for_sso, + _resolve_sso_alias, +) +from pyatlan_v9.model.group import AtlanGroup +from pyatlan_v9.model.sso import SSOMapper, SSOMapperConfig +from pyatlan_v9.validate import validate_arguments + + +class V9AsyncSSOClient: + """ + Async client for operating on Atlan's single sign-on (SSO). + """ + + def __init__(self, client: AsyncApiCaller): + if not isinstance(client, AsyncApiCaller): + raise ErrorCode.INVALID_PARAMETER_TYPE.exception_with_parameters( + "client", "AsyncApiCaller" + ) + self._client = client + + @staticmethod + def _parse_sso_mapper(raw_json): + try: + if isinstance(raw_json, list): + return msgspec.convert(raw_json, List[SSOMapper], strict=False) + return msgspec.convert(raw_json, SSOMapper, strict=False) + except msgspec.ValidationError as err: + raise ErrorCode.JSON_ERROR.exception_with_parameters( + raw_json, 200, str(err) + ) from err + + async def _check_existing_group_mappings( + self, sso_alias: str, atlan_group: AtlanGroup + ) -> None: + """ + Check if an SSO group mapping already exists within Atlan. + + :raises AtlanError: on any error during API invocation. + :raises InvalidRequestException: if the provided group mapping already exists. + """ + existing_group_maps = await self.get_all_group_mappings(sso_alias=sso_alias) + for group_map in existing_group_maps: + if group_map.name and str(atlan_group.id) in group_map.name: + raise ErrorCode.SSO_GROUP_MAPPING_ALREADY_EXISTS.exception_with_parameters( + atlan_group.alias, group_map.config.attribute_value + ) + + @validate_arguments + async def create_group_mapping( + self, sso_alias: str, atlan_group: AtlanGroup, sso_group_name: str + ) -> SSOMapper: + """ + Creates a new Atlan SSO group mapping. + + :param sso_alias: name of the SSO provider. + :param atlan_group: existing Atlan group. + :param sso_group_name: name of the SSO group. + :raises AtlanError: on any error during API invocation. + :returns: created SSO group mapping instance. + """ + sso_alias_str = _resolve_sso_alias(sso_alias) + await self._check_existing_group_mappings(sso_alias_str, atlan_group) + group_name = _group_name_for_sso(atlan_group) + mapper = SSOMapper( + name=_generate_group_mapper_name(atlan_group.id), + config=SSOMapperConfig( + attributes="[]", + sync_mode=GROUP_MAPPER_SYNC_MODE, + attribute_values_regex="", + attribute_name=GROUP_MAPPER_ATTRIBUTE, + attribute_value=sso_group_name, + group_name=group_name, + ), + identity_provider_alias=sso_alias_str, + identity_provider_mapper=IDP_GROUP_MAPPER, + ) + endpoint = CREATE_SSO_GROUP_MAPPING.format_path({"sso_alias": sso_alias_str}) + raw_json = await self._client._call_api(endpoint, request_obj=mapper) + return self._parse_sso_mapper(raw_json) + + @validate_arguments + async def update_group_mapping( + self, + sso_alias: str, + atlan_group: AtlanGroup, + group_map_id: str, + group_map_name: str, + sso_group_name: str, + ) -> SSOMapper: + """ + Update an existing Atlan SSO group mapping. + + :param sso_alias: name of the SSO provider. + :param atlan_group: existing Atlan group. + :param group_map_id: existing SSO group map identifier. + :param group_map_name: existing SSO group map name. + :param sso_group_name: new SSO group name. + :raises AtlanError: on any error during API invocation. + :returns: updated SSO group mapping instance. + """ + sso_alias_str = _resolve_sso_alias(sso_alias) + group_name = _group_name_for_sso(atlan_group) + mapper = SSOMapper( + id=group_map_id, + name=group_map_name, + config=SSOMapperConfig( + attributes="[]", + sync_mode=GROUP_MAPPER_SYNC_MODE, + group_name=group_name, + attribute_name=GROUP_MAPPER_ATTRIBUTE, + attribute_value=sso_group_name, + ), + identity_provider_alias=sso_alias_str, + identity_provider_mapper=IDP_GROUP_MAPPER, + ) + endpoint = UPDATE_SSO_GROUP_MAPPING.format_path( + {"sso_alias": sso_alias_str, "group_map_id": group_map_id} + ) + raw_json = await self._client._call_api(endpoint, request_obj=mapper) + return self._parse_sso_mapper(raw_json) + + @validate_arguments + async def get_all_group_mappings(self, sso_alias: str) -> List[SSOMapper]: + """ + Retrieves all existing Atlan SSO group mappings. + + :param sso_alias: name of the SSO provider. + :raises AtlanError: on any error during API invocation (other than 404). + :returns: list of existing SSO group mapping instances. Returns [] if the + endpoint returns 404 (e.g. SSO not configured). + """ + endpoint = GET_ALL_SSO_GROUP_MAPPING.format_path( + {"sso_alias": _resolve_sso_alias(sso_alias)} + ) + try: + raw_json = await self._client._call_api(endpoint) + except AtlanError as e: + if "404" in str(e): + return [] + raise + group_mappings = [ + mapping + for mapping in raw_json + if mapping.get("identityProviderMapper") == IDP_GROUP_MAPPER + ] + return self._parse_sso_mapper(group_mappings) + + @validate_arguments + async def get_group_mapping(self, sso_alias: str, group_map_id: str) -> SSOMapper: + """ + Retrieves an existing Atlan SSO group mapping. + + :param sso_alias: name of the SSO provider. + :param group_map_id: existing SSO group map identifier. + :raises AtlanError: on any error during API invocation. + :returns: existing SSO group mapping instance. + """ + endpoint = GET_SSO_GROUP_MAPPING.format_path( + {"sso_alias": _resolve_sso_alias(sso_alias), "group_map_id": group_map_id} + ) + raw_json = await self._client._call_api(endpoint) + return self._parse_sso_mapper(raw_json) + + @validate_arguments + async def delete_group_mapping(self, sso_alias: str, group_map_id: str) -> None: + """ + Deletes an existing Atlan SSO group mapping. + + :param sso_alias: name of the SSO provider. + :param group_map_id: existing SSO group map identifier. + :raises AtlanError: on any error during API invocation. + :returns: an empty response (`None`). + """ + endpoint = DELETE_SSO_GROUP_MAPPING.format_path( + {"sso_alias": _resolve_sso_alias(sso_alias), "group_map_id": group_map_id} + ) + raw_json = await self._client._call_api(endpoint) + return raw_json diff --git a/pyatlan_v9/client/aio/task.py b/pyatlan_v9/client/aio/task.py new file mode 100644 index 000000000..45ac8ac29 --- /dev/null +++ b/pyatlan_v9/client/aio/task.py @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +from __future__ import annotations + +from pyatlan.client.common import AsyncApiCaller, TaskSearch +from pyatlan.errors import ErrorCode +from pyatlan_v9.client.task import _parse_tasks +from pyatlan_v9.model.aio.task import AsyncTaskSearchResponse +from pyatlan_v9.model.task import TaskSearchRequest +from pyatlan_v9.validate import validate_arguments + + +class V9AsyncTaskClient: + """ + Async client for operating on tasks. + """ + + def __init__(self, client: AsyncApiCaller): + if not isinstance(client, AsyncApiCaller): + raise ErrorCode.INVALID_PARAMETER_TYPE.exception_with_parameters( + "client", "AsyncApiCaller" + ) + self._client = client + + @validate_arguments + async def search(self, request: TaskSearchRequest) -> AsyncTaskSearchResponse: + """ + Search for tasks using the provided criteria. + + :param request: search request for tasks + :returns: search results for tasks + """ + endpoint, request_obj = TaskSearch.prepare_request(request) + raw_json = await self._client._call_api(endpoint, request_obj=request_obj) + count = raw_json.get("approximateCount", 0) + aggregations = raw_json.get("aggregations") + tasks = _parse_tasks(raw_json) + + return AsyncTaskSearchResponse( + client=self._client, + endpoint=endpoint, + criteria=request, + start=request.dsl.from_, + size=request.dsl.size, + count=count, + tasks=tasks, + aggregations=aggregations, + ) diff --git a/pyatlan_v9/client/aio/token.py b/pyatlan_v9/client/aio/token.py new file mode 100644 index 000000000..daa783b74 --- /dev/null +++ b/pyatlan_v9/client/aio/token.py @@ -0,0 +1,176 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. +from __future__ import annotations + +from typing import Optional, Set + +import msgspec + +from pyatlan.client.common import ( + AsyncApiCaller, + TokenGet, + TokenGetByGuid, + TokenGetById, + TokenGetByName, + TokenPurge, +) +from pyatlan.client.constants import UPSERT_API_TOKEN +from pyatlan.errors import ErrorCode +from pyatlan_v9.model.api_tokens import ApiToken, ApiTokenRequest, ApiTokenResponse +from pyatlan_v9.validate import validate_arguments + + +class V9AsyncTokenClient: + """ + This class can be used to retrieve information pertaining to API tokens. This class does not need to be instantiated + directly but can be obtained through the token property of AsyncAtlanClient. + """ + + def __init__(self, client: AsyncApiCaller): + if not isinstance(client, AsyncApiCaller): + raise ErrorCode.INVALID_PARAMETER_TYPE.exception_with_parameters( + "client", "AsyncApiCaller" + ) + self._client = client + + @validate_arguments + async def get( + self, + limit: Optional[int] = None, + post_filter: Optional[str] = None, + sort: Optional[str] = None, + count: bool = True, + offset: int = 0, + ) -> ApiTokenResponse: + """ + Retrieves an ApiTokenResponse which contains a list of API tokens defined in Atlan. + + :param limit: maximum number of results to be returned + :param post_filter: which API tokens to retrieve + :param sort: property by which to sort the results + :param count: whether to return the total number of records (True) or not (False) + :param offset: starting point for results to return, for paging + :returns: an ApiTokenResponse which contains a list of API tokens that match the provided criteria + :raises AtlanError: on any API communication issue + """ + endpoint, query_params = TokenGet.prepare_request( + limit, post_filter, sort, count, offset + ) + raw_json = await self._client._call_api(endpoint, query_params) + return msgspec.convert(raw_json, ApiTokenResponse, strict=False) + + @validate_arguments + async def get_by_name(self, display_name: str) -> Optional[ApiToken]: + """ + Retrieves the API token with a name that exactly matches the provided string. + + :param display_name: name (as it appears in the UI) by which to retrieve the API token + :returns: the API token whose name (in the UI) matches the provided string, or None if there is none + :raises AtlanError: on any API communication issue + """ + endpoint, query_params = TokenGetByName.prepare_request(display_name) + raw_json = await self._client._call_api(endpoint, query_params) + response = msgspec.convert(raw_json, ApiTokenResponse, strict=False) + if response.records and len(response.records) >= 1: + return response.records[0] + return None + + @validate_arguments + async def get_by_id(self, client_id: str) -> Optional[ApiToken]: + """ + Retrieves the API token with a client ID that exactly matches the provided string. + + :param client_id: unique client identifier by which to retrieve the API token + :returns: the API token whose clientId matches the provided string, or None if there is none + :raises AtlanError: on any API communication issue + """ + endpoint, query_params = TokenGetById.prepare_request(client_id) + raw_json = await self._client._call_api(endpoint, query_params) + response = msgspec.convert(raw_json, ApiTokenResponse, strict=False) + if response.records and len(response.records) >= 1: + return response.records[0] + return None + + @validate_arguments + async def get_by_guid(self, guid: str) -> Optional[ApiToken]: + """ + Retrieves the API token with a unique ID (GUID) that exactly matches the provided string. + + :param guid: unique identifier by which to retrieve the API token + :returns: the API token whose GUID matches the provided string, or None if there is none + :raises AtlanError: on any API communication issue + """ + endpoint, query_params = TokenGetByGuid.prepare_request(guid) + raw_json = await self._client._call_api(endpoint, query_params) + response = msgspec.convert(raw_json, ApiTokenResponse, strict=False) + if response.records and len(response.records) >= 1: + return response.records[0] + return None + + @validate_arguments + async def creator( + self, + display_name: str, + description: str = "", + personas: Optional[Set[str]] = None, + validity_seconds: int = -1, + ) -> ApiToken: + """ + Create a new API token with the provided settings. + + :param display_name: human-readable name for the API token + :param description: optional explanation of the API token + :param personas: qualified_names of personas that should be linked to the token + :param validity_seconds: time in seconds after which the token should expire (negative numbers are treated as + infinite) + :returns: the created API token + :raises AtlanError: on any API communication issue + """ + request = ApiTokenRequest( + display_name=display_name, + description=description, + persona_qualified_names=personas or set(), + validity_seconds=validity_seconds, + ) + raw_json = await self._client._call_api(UPSERT_API_TOKEN, request_obj=request) + return msgspec.convert(raw_json, ApiToken, strict=False) + + @validate_arguments + async def updater( + self, + guid: str, + display_name: str, + description: str = "", + personas: Optional[Set[str]] = None, + ) -> ApiToken: + """ + Update an existing API token with the provided settings. + + :param guid: unique identifier (GUID) of the API token + :param display_name: human-readable name for the API token + :param description: optional explanation of the API token + :param personas: qualified_names of personas that should be linked to the token, note that you MUST + provide the complete list on any update (any not included in the list will be removed, + so if you do not specify any personas then ALL personas will be unlinked from the API token) + :returns: the updated API token + :raises AtlanError: on any API communication issue + """ + request = ApiTokenRequest( + display_name=display_name, + description=description, + persona_qualified_names=personas or set(), + ) + endpoint = UPSERT_API_TOKEN.format_path_with_params(guid) + raw_json = await self._client._call_api(endpoint, request_obj=request) + return msgspec.convert(raw_json, ApiToken, strict=False) + + @validate_arguments + async def purge(self, guid: str) -> None: + """ + Delete (purge) the specified API token. + + :param guid: unique identifier (GUID) of the API token to delete + :raises AtlanError: on any API communication issue + """ + endpoint, _ = TokenPurge.prepare_request(guid) + await self._client._call_api(endpoint) diff --git a/pyatlan_v9/client/aio/typedef.py b/pyatlan_v9/client/aio/typedef.py new file mode 100644 index 000000000..806c1565f --- /dev/null +++ b/pyatlan_v9/client/aio/typedef.py @@ -0,0 +1,175 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. +from __future__ import annotations + +from typing import List, Union + +import msgspec + +from pyatlan.client.common import AsyncApiCaller +from pyatlan.client.constants import ( + CREATE_TYPE_DEFS, + DELETE_TYPE_DEF_BY_NAME, + GET_ALL_TYPE_DEFS, + GET_TYPE_DEF_BY_NAME, + UPDATE_TYPE_DEFS, +) +from pyatlan.errors import ErrorCode +from pyatlan_v9.client.typedef import _build_typedef_request, _create_typedef_from_json +from pyatlan_v9.model.enums import AtlanTypeCategory +from pyatlan_v9.model.typedef import ( + AtlanTagDef, + CustomMetadataDef, + EnumDef, + TypeDef, + TypeDefResponse, +) +from pyatlan_v9.validate import validate_arguments + + +class V9AsyncTypeDefClient: + """ + Async client for operating on type definitions. This class does not need to be + instantiated directly but can be obtained through the typedef property of AsyncAtlanClient. + """ + + def __init__(self, client: AsyncApiCaller): + if not isinstance(client, AsyncApiCaller): + raise ErrorCode.INVALID_PARAMETER_TYPE.exception_with_parameters( + "client", "AsyncApiCaller" + ) + self._client = client + + async def _refresh_caches(self, typedef: TypeDef) -> None: + """Refresh appropriate caches after creating or updating a type definition.""" + if isinstance(typedef, AtlanTagDef): + await self._client.atlan_tag_cache.refresh_cache() # type: ignore[attr-defined] + if isinstance(typedef, CustomMetadataDef): + await self._client.custom_metadata_cache.refresh_cache() # type: ignore[attr-defined] + if isinstance(typedef, EnumDef): + await self._client.enum_cache.refresh_cache() # type: ignore[attr-defined] + + async def get_all(self) -> TypeDefResponse: + """ + Retrieves a TypeDefResponse object that contains a list of all the type definitions in Atlan. + + :returns: TypeDefResponse object that contains a list of all the type definitions in Atlan + :raises AtlanError: on any API communication issue + """ + raw_json = await self._client._call_api(GET_ALL_TYPE_DEFS, None) + return msgspec.convert(raw_json, TypeDefResponse, strict=False) + + @validate_arguments + async def get( + self, type_category: Union[AtlanTypeCategory, List[AtlanTypeCategory]] + ) -> TypeDefResponse: + """ + Retrieves a TypeDefResponse object that contain a list of the specified category type definitions in Atlan. + + :param type_category: category of type definitions to retrieve + :returns: TypeDefResponse object that contain a list that contains the requested list of type definitions + :raises AtlanError: on any API communication issue + """ + categories: List[str] = [] + if isinstance(type_category, list): + categories.extend(map(lambda x: x.value, type_category)) + else: + categories.append(type_category.value) + query_params = {"type": categories} + raw_json = await self._client._call_api( + GET_ALL_TYPE_DEFS.format_path_with_params(), query_params + ) + return msgspec.convert(raw_json, TypeDefResponse, strict=False) + + @validate_arguments + async def get_by_name(self, name: str) -> TypeDef: + """ + Retrieves a specific type definition from Atlan. + + :name: internal (hashed-string, if used) name of the type definition + :returns: details of that specific type definition + :raises ApiError: on receiving an unsupported type definition + category or when unable to produce a valid response + :raises AtlanError: on any API communication issue + """ + endpoint = GET_TYPE_DEF_BY_NAME.format_path_with_params(name) + raw_json = await self._client._call_api(endpoint, None) + return _create_typedef_from_json(raw_json) + + @validate_arguments + async def creator(self, typedef: TypeDef) -> TypeDefResponse: + """ + Create a new type definition in Atlan. + Note: only custom metadata, enumerations (options), and Atlan tag type + definitions are currently supported. Furthermore, if any of these are + created their respective cache will be force-refreshed. + + :param typedef: type definition to create + :returns: the resulting type definition that was created + :raises InvalidRequestError: if the typedef you are + trying to create is not one of the allowed types + :raises AtlanError: on any API communication issue + """ + payload = _build_typedef_request(typedef) + raw_json = await self._client._call_api(CREATE_TYPE_DEFS, request_obj=payload) + await self._refresh_caches(typedef) + return msgspec.convert(raw_json, TypeDefResponse, strict=False) + + @validate_arguments + async def updater(self, typedef: TypeDef) -> TypeDefResponse: + """ + Update an existing type definition in Atlan. + Note: only custom metadata, enumerations (options), and Atlan tag type + definitions are currently supported. Furthermore, if any of these are + updated their respective cache will be force-refreshed. + + :param typedef: type definition to update + :returns: the resulting type definition that was updated + :raises InvalidRequestError: if the typedef you are + trying to update is not one of the allowed types + :raises AtlanError: on any API communication issue + """ + payload = _build_typedef_request(typedef) + raw_json = await self._client._call_api(UPDATE_TYPE_DEFS, request_obj=payload) + await self._refresh_caches(typedef) + return msgspec.convert(raw_json, TypeDefResponse, strict=False) + + @validate_arguments + async def purge(self, name: str, typedef_type: type) -> None: + """ + Delete the type definition. + Furthermore, if an Atlan tag, enumeration or custom metadata is deleted their + respective cache will be force-refreshed. + + :param name: internal hashed-string name of the type definition + :param typedef_type: type of the type definition that is being deleted + :raises InvalidRequestError: if the typedef you are trying to delete is not one of the allowed types + :raises NotFoundError: if the typedef you are trying to delete cannot be found + :raises AtlanError: on any API communication issue + """ + if typedef_type == CustomMetadataDef: + internal_name = await self._client.custom_metadata_cache.get_id_for_name( + name + ) # type: ignore[attr-defined] + elif typedef_type == EnumDef: + internal_name = name + elif typedef_type == AtlanTagDef: + internal_name = str( + await self._client.atlan_tag_cache.get_id_for_name(name) + ) # type: ignore[attr-defined] + else: + raise ErrorCode.UNABLE_TO_PURGE_TYPEDEF_OF_TYPE.exception_with_parameters( + typedef_type + ) + if internal_name: + endpoint = DELETE_TYPE_DEF_BY_NAME.format_path_with_params(internal_name) + await self._client._call_api(endpoint, None) + else: + raise ErrorCode.TYPEDEF_NOT_FOUND_BY_NAME.exception_with_parameters(name) + + if typedef_type == CustomMetadataDef: + await self._client.custom_metadata_cache.refresh_cache() # type: ignore[attr-defined] + elif typedef_type == EnumDef: + await self._client.enum_cache.refresh_cache() # type: ignore[attr-defined] + elif typedef_type == AtlanTagDef: + await self._client.atlan_tag_cache.refresh_cache() # type: ignore[attr-defined] diff --git a/pyatlan_v9/client/aio/user.py b/pyatlan_v9/client/aio/user.py new file mode 100644 index 000000000..905c17e01 --- /dev/null +++ b/pyatlan_v9/client/aio/user.py @@ -0,0 +1,436 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. +from __future__ import annotations + +import json +from typing import List, Optional + +import msgspec + +from pyatlan.client.common import AsyncApiCaller +from pyatlan.client.constants import ( + ADD_USER_TO_GROUPS, + CHANGE_USER_ROLE, + CREATE_USERS, + GET_CURRENT_USER, + GET_USER_GROUPS, + GET_USERS, + UPDATE_USER, +) +from pyatlan.errors import ErrorCode +from pyatlan.model.fields.atlan_fields import KeywordField +from pyatlan_v9.model.aio.group import AsyncGroupResponse +from pyatlan_v9.model.aio.user import AsyncUserResponse +from pyatlan_v9.model.assets import Asset +from pyatlan_v9.model.fluent_search import FluentSearch +from pyatlan_v9.model.group import AtlanGroup, GroupRequest +from pyatlan_v9.model.response import AssetMutationResponse +from pyatlan_v9.model.user import ( + AddToGroupsRequest, + AtlanUser, + ChangeRoleRequest, + CreateUser, + CreateUserRequest, + UserMinimalResponse, + UserRequest, +) +from pyatlan_v9.validate import validate_arguments + +_USER_COLUMNS = [ + "firstName", + "lastName", + "username", + "id", + "email", + "emailVerified", + "enabled", + "roles", + "defaultRoles", + "groupCount", + "attributes", + "personas", + "createdTimestamp", + "lastLoginTime", + "loginEvents", + "isLocked", + "workspaceRole", +] + + +class V9AsyncUserClient: + """ + Async client for operating on users. + """ + + def __init__(self, client: AsyncApiCaller): + if not isinstance(client, AsyncApiCaller): + raise ErrorCode.INVALID_PARAMETER_TYPE.exception_with_parameters( + "client", "AsyncApiCaller" + ) + self._client = client + + def _build_user_response( + self, + raw_json: dict, + request: UserRequest, + offset: int, + limit: Optional[int], + ) -> AsyncUserResponse: + records = None + if raw_records := raw_json.get("records"): + records = msgspec.convert(raw_records, list[AtlanUser], strict=False) + response = AsyncUserResponse( + total_record=raw_json.get("totalRecord"), + filter_record=raw_json.get("filterRecord"), + records=records, + ) + response._size = limit or 20 + response._start = offset + response._endpoint = GET_USERS + response._client = self._client + response._criteria = request + return response + + @validate_arguments + async def creator( + self, users: List[AtlanUser], return_info: bool = False + ) -> Optional[AsyncUserResponse]: + """ + Create one or more new users. + + :param users: the details of the new users + :param return_info: whether to return the details of created users, defaults to `False` + :raises AtlanError: on any API communication issue + :returns: a AsyncUserResponse object which contains the list of details of created users if `return_info` is `True`, otherwise `None` + """ + to_create: list[CreateUser] = [] + for user in users: + role_name = str(user.workspace_role) + if ( + role_id := self._client.role_cache.get_id_for_name(role_name) + ) and user.email: + to_create.append( + CreateUser(email=user.email, role_name=role_name, role_id=role_id) + ) + payload = CreateUserRequest(users=to_create) + await self._client._call_api(CREATE_USERS, request_obj=payload) + if return_info: + emails = [cu.email for cu in to_create] + return await self.get_by_emails(emails=emails) + return None + + @validate_arguments + async def updater( + self, + guid: str, + user: AtlanUser, + ) -> UserMinimalResponse: + """ + Update a user. + Note: you can only update users that have already signed up to Atlan. Users that are + only invited (but have not yet logged in) cannot be updated. + + :param guid: unique identifier (GUID) of the user to update + :param user: details to update on the user + :returns: basic details about the updated user + :raises AtlanError: on any API communication issue + """ + endpoint = UPDATE_USER.format_path_with_params(guid) + raw_json = await self._client._call_api(endpoint, request_obj=user) + return msgspec.convert(raw_json, UserMinimalResponse, strict=False) + + @validate_arguments + async def change_role( + self, + guid: str, + role_id: str, + ) -> None: + """ + Change the role of a user. + + :param guid: unique identifier (GUID) of the user whose role should be changed + :param role_id: unique identifier (GUID) of the role to move the user into + :raises AtlanError: on any API communication issue + """ + payload = ChangeRoleRequest(role_id=role_id) + endpoint = CHANGE_USER_ROLE.format_path({"user_guid": guid}) + await self._client._call_api(endpoint, request_obj=payload) + + async def get_current( + self, + ) -> UserMinimalResponse: + """ + Retrieve the current user (representing the API token). + + :returns: basic details about the current user (API token) + :raises AtlanError: on any API communication issue + """ + raw_json = await self._client._call_api(GET_CURRENT_USER, None) + return msgspec.convert(raw_json, UserMinimalResponse, strict=False) + + @validate_arguments + async def get( + self, + limit: Optional[int] = 20, + post_filter: Optional[str] = None, + sort: Optional[str] = None, + count: bool = True, + offset: int = 0, + ) -> AsyncUserResponse: + """ + Retrieves a AsyncUserResponse which contains a list of users defined in Atlan. + + :param limit: maximum number of results to be returned + :param post_filter: which users to retrieve + :param sort: property by which to sort the results + :param count: whether to return the total number of records (True) or not (False) + :param offset: starting point for results to return, for paging + :returns: a AsyncUserResponse which contains a list of users that match the provided criteria + :raises AtlanError: on any API communication issue + """ + request = UserRequest( + post_filter=post_filter, + limit=limit, + sort=sort, + count=count, + offset=offset, + columns=_USER_COLUMNS, + ) + endpoint = GET_USERS.format_path_with_params() + raw_json = await self._client._call_api( + api=endpoint, query_params=request.query_params + ) + return self._build_user_response(raw_json, request, offset, limit) + + @validate_arguments + async def get_all( + self, + limit: int = 20, + offset: int = 0, + sort: Optional[str] = "username", + ) -> AsyncUserResponse: + """ + Retrieve a AsyncUserResponse object containing a list of all users defined in Atlan. + + :param limit: maximum number of users to retrieve + :param offset: starting point for the list of users when paging + :param sort: property by which to sort the results, by default : `username` + :returns: a AsyncUserResponse object with all users based on the parameters; results are iterable. + """ + response: AsyncUserResponse = await self.get( + offset=offset, limit=limit, sort=sort + ) + return response + + @validate_arguments + async def get_by_email( + self, + email: str, + limit: int = 20, + offset: int = 0, + ) -> Optional[AsyncUserResponse]: + """ + Retrieves a AsyncUserResponse object containing a list of users with email addresses that contain the provided email. + (This could include a complete email address, in which case there should be at + most a single item in the returned list, or could be a partial email address + such as "@example.com" to retrieve all users with that domain in their email + address.) + + :param email: on which to filter the users + :param limit: maximum number of users to retrieve + :param offset: starting point for the list of users when pagin + :returns: a AsyncUserResponse object containing a list of users whose email addresses contain the provided string + """ + post_filter = '{"email":{"$ilike":"%' + email + '%"}}' + return await self.get(offset=offset, limit=limit, post_filter=post_filter) + + @validate_arguments + async def get_by_emails( + self, + emails: List[str], + limit: int = 20, + offset: int = 0, + ) -> Optional[AsyncUserResponse]: + """ + Retrieves a AsyncUserResponse object containing a list of users with email addresses that match the provided list of emails. + + :param emails: list of email addresses to filter the users + :param limit: maximum number of users to retrieve + :param offset: starting point for the list of users when paginating + :returns: a AsyncUserResponse object containing a list of users whose email addresses match the provided list + """ + email_filter = '{"email":{"$in":' + json.dumps(emails or [""]) + "}}" + return await self.get(offset=offset, limit=limit, post_filter=email_filter) + + @validate_arguments + async def get_by_username(self, username: str) -> Optional[AtlanUser]: + """ + Retrieves a user based on the username. (This attempts an exact match on username + rather than a contains search.) + + :param username: the username by which to find the user + :returns: the with that username + """ + post_filter = '{"username":"' + username + '"}' + response = await self.get(offset=0, limit=5, post_filter=post_filter) + if response and response.records and len(response.records) >= 1: + return response.records[0] + return None + + @validate_arguments + async def get_by_usernames( + self, usernames: List[str], limit: int = 5, offset: int = 0 + ) -> Optional[AsyncUserResponse]: + """ + Retrieves a AsyncUserResponse object containing a list of users based on their usernames. + + :param usernames: the list of usernames by which to find the users + :param limit: maximum number of users to retrieve + :param offset: starting point for the list of users when paginating + :returns: a AsyncUserResponse object containing list of users with the specified usernames + """ + username_filter = '{"username":{"$in":' + json.dumps(usernames or [""]) + "}}" + return await self.get(offset=offset, limit=limit, post_filter=username_filter) + + @validate_arguments + async def add_to_groups( + self, + guid: str, + group_ids: List[str], + ) -> None: + """ + Add a user to one or more groups. + + :param guid: unique identifier (GUID) of the user to add into groups + :param group_ids: unique identifiers (GUIDs) of the groups to add the user into + :raises AtlanError: on any API communication issue + """ + payload = AddToGroupsRequest(groups=group_ids) + endpoint = ADD_USER_TO_GROUPS.format_path({"user_guid": guid}) + await self._client._call_api(endpoint, request_obj=payload) + + @validate_arguments + async def get_groups( + self, guid: str, request: Optional[GroupRequest] = None + ) -> AsyncGroupResponse: + """ + Retrieve the groups this user belongs to. + + :param guid: unique identifier (GUID) of the user + :param request: request containing details about which groups to retrieve + :returns: an AsyncGroupResponse which contains the groups this user belongs to + :raises AtlanError: on any API communication issue + """ + if not request: + request = GroupRequest() + endpoint_obj = GET_USER_GROUPS.format_path({"user_guid": guid}) + raw_json = await self._client._call_api( + api=endpoint_obj.format_path_with_params(), + query_params=request.query_params, + ) + records = None + if raw_records := raw_json.get("records"): + records = msgspec.convert(raw_records, list[AtlanGroup], strict=False) + response = AsyncGroupResponse( + total_record=raw_json.get("totalRecord"), + filter_record=raw_json.get("filterRecord"), + records=records, + ) + response._size = request.limit or 20 + response._start = request.offset + response._endpoint = endpoint_obj + response._client = self._client + response._criteria = request + return response + + @validate_arguments + async def add_as_admin( + self, asset_guid: str, impersonation_token: str + ) -> Optional[AssetMutationResponse]: + """ + Add the API token configured for the default client as an admin to the asset with the provided GUID. + This is primarily useful for connections, to allow the API token to manage policies for the connection, and + for query collections, to allow the API token to manage the queries in a collection or the collection itself. + + :param asset_guid: unique identifier (GUID) of the asset to which we should add this API token as an admin + :param impersonation_token: a bearer token for an actual user who is already an admin for the asset, + NOT an API token + :returns: a AssetMutationResponse which contains the results of the operation + :raises NotFoundError: if the asset to which to add the API token as an admin cannot be found + """ + return await self._add_as( + asset_guid=asset_guid, + impersonation_token=impersonation_token, + keyword_field=Asset.ADMIN_USERS, + ) + + @validate_arguments + async def add_as_viewer( + self, asset_guid: str, impersonation_token: str + ) -> Optional[AssetMutationResponse]: + """ + Add the API token configured for the default client as a viewer to the asset with the provided GUID. + This is primarily useful for query collections, to allow the API token to view or run queries within the + collection, but not make any changes to them. + + :param asset_guid: unique identifier (GUID) of the asset to which we should add this API token as an admin + :param impersonation_token: a bearer token for an actual user who is already an admin for the asset, + NOT an API token + :returns: a AssetMutationResponse which contains the results of the operation + :raises NotFoundError: if the asset to which to add the API token as a viewer cannot be found + """ + return await self._add_as( + asset_guid=asset_guid, + impersonation_token=impersonation_token, + keyword_field=Asset.VIEWER_USERS, + ) + + async def _add_as( + self, asset_guid: str, impersonation_token: str, keyword_field: KeywordField + ) -> Optional[AssetMutationResponse]: + """ + Add the API token configured for the default client as a viewer or admin to the asset with the provided GUID. + + :param asset_guid: unique identifier (GUID) of the asset to which we should add this API token as an admin + :param impersonation_token: a bearer token for an actual user who is already an admin for the asset, + NOT an API token + :param keyword_field: must be either Asset.ADMIN_USERS or Asset.VIEWER_USERS + :returns: a AssetMutationResponse which contains the results of the operation + :raises NotFoundError: if the asset to which to add the API token as a viewer cannot be found + """ + from pyatlan_v9.client.aio.atlan import client_connection + + if keyword_field not in [Asset.ADMIN_USERS, Asset.VIEWER_USERS]: + raise ValueError( + f"keyword_field should be {Asset.VIEWER_USERS} or {Asset.ADMIN_USERS}" + ) + + token_user = (await self.get_current()).username or "" + async with client_connection( + client=self._client, # type: ignore[arg-type] + api_key=impersonation_token, + ) as tmp: + request = ( + FluentSearch() + .where(Asset.GUID.eq(asset_guid)) + .include_on_results(keyword_field) + .page_size(1) + ).to_request() + results = await tmp.asset.search(request) + if not results.current_page(): + raise ErrorCode.ASSET_NOT_FOUND_BY_GUID.exception_with_parameters( + asset_guid + ) + asset = results.current_page()[0] + if keyword_field == Asset.VIEWER_USERS: + existing_viewers = asset.viewer_users or set() + existing_viewers.add(token_user) + else: + existing_admins = asset.admin_users or set() + existing_admins.add(token_user) + to_update = asset.trim_to_required() + if keyword_field == Asset.VIEWER_USERS: + to_update.viewer_users = existing_viewers + else: + to_update.admin_users = existing_admins + return await tmp.asset.save(to_update) diff --git a/pyatlan_v9/client/aio/workflow.py b/pyatlan_v9/client/aio/workflow.py new file mode 100644 index 000000000..ce12769b9 --- /dev/null +++ b/pyatlan_v9/client/aio/workflow.py @@ -0,0 +1,852 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +from __future__ import annotations + +import asyncio +import json +from logging import Logger +from typing import List, Optional, Union, overload + +import msgspec + +from pyatlan.client.common import ( + AsyncApiCaller, + WorkflowDelete, + WorkflowFindScheduleQueryBetween, + WorkflowGetAllScheduledRuns, + WorkflowGetScheduledRun, + WorkflowStop, + WorkflowUpdateOwner, +) +from pyatlan.client.constants import ( + PACKAGE_WORKFLOW_RERUN, + PACKAGE_WORKFLOW_RUN, + PACKAGE_WORKFLOW_UPDATE, + WORKFLOW_INDEX_RUN_SEARCH, + WORKFLOW_INDEX_SEARCH, + WORKFLOW_OWNER_RERUN, + WORKFLOW_RERUN, + WORKFLOW_RUN, + WORKFLOW_UPDATE, +) +from pyatlan.errors import ErrorCode +from pyatlan.utils import validate_type +from pyatlan_v9.model.aio.workflow import AsyncWorkflowSearchResponse +from pyatlan_v9.model.enums import AtlanWorkflowPhase, WorkflowPackage +from pyatlan_v9.model.search import ( + Bool, + Exists, + NestedQuery, + Prefix, + Range, + Regexp, + Term, + Terms, +) +from pyatlan_v9.model.workflow import ( + ReRunRequest, + ScheduleQueriesSearchRequest, + Workflow, + WorkflowResponse, + WorkflowRunResponse, + WorkflowSchedule, + WorkflowScheduleResponse, + WorkflowSearchRequest, + WorkflowSearchResponse, + WorkflowSearchResult, + WorkflowSearchResultDetail, +) +from pyatlan_v9.validate import validate_arguments + +MONITOR_SLEEP_SECONDS = 5 + +_DEFAULT_SORT = [ + {"metadata.creationTimestamp": {"order": "desc", "nested": {"path": "metadata"}}} +] + +_LATEST_RUN_SORT = [{"status.startedAt": {"order": "desc"}}] + + +class V9AsyncWorkflowClient: + """ + This class can be used to retrieve information and rerun workflows. This class does not need to be instantiated + directly but can be obtained through the workflow property of AsyncAtlanClient. + """ + + _WORKFLOW_RUN_SCHEDULE = "orchestration.atlan.com/schedule" + _WORKFLOW_RUN_TIMEZONE = "orchestration.atlan.com/timezone" + + def __init__(self, client: AsyncApiCaller): + if not isinstance(client, AsyncApiCaller): + raise ErrorCode.INVALID_PARAMETER_TYPE.exception_with_parameters( + "client", "AsyncApiCaller" + ) + self._client = client + + @validate_arguments + async def find_by_type( + self, prefix: WorkflowPackage, max_results: int = 10 + ) -> List[WorkflowSearchResult]: + """ + Find workflows based on their type (prefix). Note: Only workflows that have been run will be found. + + :param prefix: name of the specific workflow to find (for example CONNECTION_DELETE) + :param max_results: the maximum number of results to retrieve + :returns: the list of workflows of the provided type, with the most-recently created first + :raises ValidationError: If the provided prefix is invalid workflow package + :raises AtlanError: on any API communication issue + """ + regex = prefix.value.replace("-", "[-]") + "[-][0-9]{10}" + query = Bool( + filter=[ + NestedQuery( + query=Regexp(field="metadata.name.keyword", value=regex), + path="metadata", + ) + ] + ) + request = WorkflowSearchRequest( + query=query.to_dict(), size=max_results, sort=_DEFAULT_SORT + ) + raw_json = await self._client._call_api( + WORKFLOW_INDEX_SEARCH, request_obj=request + ) + response = msgspec.convert(raw_json, WorkflowSearchResponse, strict=False) + return response.hits and response.hits.hits or [] + + @validate_arguments + async def find_by_id(self, id: str) -> Optional[WorkflowSearchResult]: + """ + Find workflows based on their ID (e.g: `atlan-snowflake-miner-1714638976`) + Note: Only workflows that have been run will be found + + :param id: the ID of the workflow to find + :returns: the workflow with the provided ID, or None if none is found + :raises AtlanError: on any API communication issue + """ + query = Bool( + filter=[ + NestedQuery( + query=Bool(must=[Term(field="metadata.name.keyword", value=id)]), + path="metadata", + ) + ] + ) + request = WorkflowSearchRequest( + query=query.to_dict(), size=1, sort=_DEFAULT_SORT + ) + raw_json = await self._client._call_api( + WORKFLOW_INDEX_SEARCH, request_obj=request + ) + response = msgspec.convert(raw_json, WorkflowSearchResponse, strict=False) + results = response.hits and response.hits.hits + return results[0] if results else None + + @validate_arguments + async def find_run_by_id(self, id: str) -> Optional[WorkflowSearchResult]: + """ + Find workflows runs based on their ID (e.g: `atlan-snowflake-miner-1714638976-t7s8b`) + Note: Only workflow runs will be found + + :param id: the ID of the workflow run to find + :returns: the workflow run with the provided ID, or None if none is found + :raises AtlanError: on any API communication issue + """ + query = Bool( + filter=[ + Term( + field="_id", + value=id, + ), + ] + ) + response = await self._find_runs(query, size=1) + return results[0] if (results := response.hits and response.hits.hits) else None + + @validate_arguments + async def find_runs_by_status_and_time_range( + self, + status: List[AtlanWorkflowPhase], + started_at: Optional[str] = None, + finished_at: Optional[str] = None, + from_: int = 0, + size: int = 100, + ) -> AsyncWorkflowSearchResponse: + """ + Retrieves a WorkflowSearchResponse object containing workflow runs based on their status and time range. + + :param status: list of the workflow statuses to filter + :param started_at: (optional) lower bound on 'status.startedAt' (e.g 'now-2h') + :param finished_at: (optional) lower bound on 'status.finishedAt' (e.g 'now-1h') + :param from_:(optional) starting index of the search results (default: `0`). + :param size: (optional) maximum number of search results to return (default: `100`). + :returns: a WorkflowSearchResponse object containing a list of workflows matching the filters + :raises ValidationError: if inputs are invalid + :raises AtlanError: on any API communication issue + """ + time_filters = [] + if started_at: + time_filters.append(Range(field="status.startedAt", gte=started_at)) + if finished_at: + time_filters.append(Range(field="status.finishedAt", lte=finished_at)) + + run_lookup_query = Bool( + must=[ + NestedQuery( + query=Terms( + field="metadata.labels.workflows.argoproj.io/phase.keyword", + values=[s.value for s in status], + ), + path="metadata", + ), + *time_filters, + NestedQuery( + query=Exists(field="metadata.labels.workflows.argoproj.io/creator"), + path="metadata", + ), + ], + ) + return await self._find_runs(query=run_lookup_query, from_=from_, size=size) + + async def _find_latest_run( + self, workflow_name: str + ) -> Optional[WorkflowSearchResult]: + """ + Find the most recent run for a given workflow + + :param name: name of the workflow for which to find the current run + :returns: the singular result giving the latest run of the workflow + :raises AtlanError: on any API communication issue + """ + query = Bool( + filter=[ + NestedQuery( + query=Term( + field="spec.workflowTemplateRef.name.keyword", + value=workflow_name, + ), + path="spec", + ) + ] + ) + query_dict = query.to_dict() + request = WorkflowSearchRequest( + query=query_dict, from_=0, size=1, sort=_LATEST_RUN_SORT + ) + raw_json = await self._client._call_api( + WORKFLOW_INDEX_RUN_SEARCH, request_obj=request + ) + response = msgspec.convert(raw_json, AsyncWorkflowSearchResponse, strict=False) + response._client = self._client + response._endpoint = WORKFLOW_INDEX_RUN_SEARCH + response._criteria = query_dict + response._start = 0 + response._size = 1 + return response.hits.hits[0] if response.hits and response.hits.hits else None + + async def _find_current_run( + self, workflow_name: str + ) -> Optional[WorkflowSearchResult]: + """ + Find the most current, still-running run of a given workflow + + :param name: name of the workflow for which to find the current run + :returns: the singular result giving the latest currently-running + run of the workflow, or `None` if it is not currently running + :raises AtlanError: on any API communication issue + """ + query = Bool( + filter=[ + NestedQuery( + query=Term( + field="spec.workflowTemplateRef.name.keyword", + value=workflow_name, + ), + path="spec", + ) + ] + ) + query_dict = query.to_dict() + request = WorkflowSearchRequest( + query=query_dict, from_=0, size=50, sort=_DEFAULT_SORT + ) + raw_json = await self._client._call_api( + WORKFLOW_INDEX_RUN_SEARCH, request_obj=request + ) + response = msgspec.convert(raw_json, AsyncWorkflowSearchResponse, strict=False) + response._client = self._client + response._endpoint = WORKFLOW_INDEX_RUN_SEARCH + response._criteria = query_dict + response._start = 0 + response._size = 50 + if results := response.hits and response.hits.hits: + for result in results: + if result.status in { + AtlanWorkflowPhase.PENDING, + AtlanWorkflowPhase.RUNNING, + }: + return result + return None + + async def _find_runs( + self, + query, + from_: int = 0, + size: int = 100, + ) -> AsyncWorkflowSearchResponse: + """ + Retrieve existing workflow runs. + + :param query: query object to filter workflow runs. + :param from_: starting point for pagination + :param size: maximum number of results to retrieve + :returns: the workflow runs + :raises AtlanError: on any API communication issue + """ + query_dict = query.to_dict() if hasattr(query, "to_dict") else query + request = WorkflowSearchRequest( + query=query_dict, from_=from_, size=size, sort=_DEFAULT_SORT + ) + raw_json = await self._client._call_api( + WORKFLOW_INDEX_RUN_SEARCH, request_obj=request + ) + response = msgspec.convert(raw_json, AsyncWorkflowSearchResponse, strict=False) + response._client = self._client + response._endpoint = WORKFLOW_INDEX_RUN_SEARCH + response._criteria = query_dict + response._start = from_ + response._size = size + return response + + def _add_schedule( + self, + workflow, + workflow_schedule: WorkflowSchedule, + ): + """ + Adds required schedule parameters to the workflow object. + """ + if workflow.metadata and workflow.metadata.annotations: + workflow.metadata.annotations.update( + { + self._WORKFLOW_RUN_SCHEDULE: workflow_schedule.cron_schedule, + self._WORKFLOW_RUN_TIMEZONE: workflow_schedule.timezone, + } + ) + + async def _handle_workflow_types(self, workflow): + """Handle different workflow types and return the appropriate workflow object.""" + if isinstance(workflow, WorkflowPackage): + if results := await self.find_by_type(workflow): + detail = results[0].source + else: + raise ErrorCode.NO_PRIOR_RUN_AVAILABLE.exception_with_parameters( + workflow.value + ) + elif isinstance(workflow, WorkflowSearchResult): + detail = workflow.source + else: + detail = workflow + return detail + + @overload + async def rerun( + self, workflow: WorkflowPackage, idempotent: bool = False + ) -> WorkflowRunResponse: ... + + @overload + async def rerun( + self, workflow: WorkflowSearchResultDetail, idempotent: bool = False + ) -> WorkflowRunResponse: ... + + @overload + async def rerun( + self, workflow: WorkflowSearchResult, idempotent: bool = False + ) -> WorkflowRunResponse: ... + + async def rerun( + self, + workflow: Union[ + WorkflowPackage, WorkflowSearchResultDetail, WorkflowSearchResult + ], + idempotent: bool = False, + ) -> WorkflowRunResponse: + """ + Rerun the workflow immediately. + Note: this must be a workflow that was previously run. + + :param workflow: The workflow to rerun. + :param idempotent: If `True`, the workflow will only be rerun if it is not already currently running + :returns: the details of the workflow run (if `idempotent`, will return details of the already-running workflow) + :raises ValidationError: If the provided workflow is invalid + :raises InvalidRequestException: If no prior runs are available for the provided workflow + :raises AtlanError: on any API communication issue + """ + validate_type( + name="workflow", + _type=(WorkflowPackage, WorkflowSearchResultDetail, WorkflowSearchResult), + value=workflow, + ) + detail = await self._handle_workflow_types(workflow) + + if idempotent and detail and detail.metadata and detail.metadata.name: + await asyncio.sleep(10) + if ( + ( + current_run_details := await self._find_current_run( + workflow_name=detail.metadata.name + ) + ) + and current_run_details.source + and current_run_details.source.metadata + and current_run_details.source.spec + and current_run_details.source.status + ): + return WorkflowRunResponse( + metadata=current_run_details.source.metadata, + spec=current_run_details.source.spec, + status=current_run_details.source.status, + ) + use_package_endpoint = not await self._client.role_cache.is_api_token_user() # type: ignore[attr-defined] + request = None + if detail and detail.metadata: + request = ReRunRequest( + namespace=detail.metadata.namespace, + resource_name=detail.metadata.name, + ) + endpoint = PACKAGE_WORKFLOW_RERUN if use_package_endpoint else WORKFLOW_RERUN + raw_json = await self._client._call_api(endpoint, request_obj=request) + return msgspec.convert(raw_json, WorkflowRunResponse, strict=False) + + @overload + async def run( + self, workflow: Workflow, workflow_schedule: Optional[WorkflowSchedule] = None + ) -> WorkflowResponse: ... + + @overload + async def run( + self, workflow: str, workflow_schedule: Optional[WorkflowSchedule] = None + ) -> WorkflowResponse: ... + + async def run( + self, + workflow: Union[Workflow, str], + workflow_schedule: Optional[WorkflowSchedule] = None, + ) -> WorkflowResponse: + """ + Run the Atlan workflow with a specific configuration. + + Note: This method should only be used to create the workflow for the first time. + Each invocation creates a new connection and new assets within that connection. + Running the workflow multiple times with the same configuration may lead to duplicate assets. + Consider using the "rerun()" method instead to re-execute an existing workflow. + + :param workflow: workflow object to run or a raw workflow JSON string. + :param workflow_schedule: (Optional) a WorkflowSchedule object containing: + - A cron schedule expression, e.g: `5 4 * * *`. + - The time zone for the cron schedule, e.g: `Europe/Paris`. + + :returns: Details of the workflow run. + :raises ValidationError: If the provided `workflow` is invalid. + :raises AtlanError: on any API communication issue. + """ + validate_type(name="workflow", _type=(Workflow, str), value=workflow) + validate_type( + name="workflow_schedule", + _type=(WorkflowSchedule, None), + value=workflow_schedule, + ) + if isinstance(workflow, str): + workflow = msgspec.convert(json.loads(workflow), Workflow, strict=False) + if workflow_schedule: + self._add_schedule(workflow, workflow_schedule) + use_package_endpoint = not await self._client.role_cache.is_api_token_user() # type: ignore[attr-defined] + endpoint = PACKAGE_WORKFLOW_RUN if use_package_endpoint else WORKFLOW_RUN + raw_json = await self._client._call_api(endpoint, request_obj=workflow) + return msgspec.convert(raw_json, WorkflowResponse, strict=False) + + @validate_arguments + async def updater(self, workflow: Workflow) -> WorkflowResponse: + """ + Update a given workflow's configuration. + + :param workflow: request full details of the workflow's revised configuration. + :returns: the updated workflow configuration. + :raises ValidationError: If the provided `workflow` is invalid. + :raises AtlanError: on any API communication issue + """ + use_package_endpoint = not await self._client.role_cache.is_api_token_user() # type: ignore[attr-defined] + workflow_name = workflow.metadata and workflow.metadata.name + if use_package_endpoint: + endpoint = PACKAGE_WORKFLOW_UPDATE.format_path( + {"workflow_name": workflow_name} + ) + else: + endpoint = WORKFLOW_UPDATE.format_path({"workflow_name": workflow_name}) + raw_json = await self._client._call_api(endpoint, request_obj=workflow) + return msgspec.convert(raw_json, WorkflowResponse, strict=False) + + @validate_arguments + async def update_owner(self, workflow_name: str, username: str) -> WorkflowResponse: + """ + Update the owner of a workflow. + + :param workflow_name: name of the workflow for which we want to update owner + :param username: new username of the user who should own the workflow + :returns: workflow response details + :raises AtlanError: on any API communication issue + """ + endpoint, request_obj = WorkflowUpdateOwner.prepare_request( + workflow_name, username + ) + raw_json = await self._client._call_api(endpoint, request_obj=request_obj) + return msgspec.convert(raw_json, WorkflowResponse, strict=False) + + @validate_arguments(config=dict(arbitrary_types_allowed=True)) + async def monitor( + self, + workflow_response: Optional[WorkflowResponse] = None, + logger: Optional[Logger] = None, + workflow_name: Optional[str] = None, + ) -> Optional[AtlanWorkflowPhase]: + """ + Monitor a workflow until its completion (or the script terminates). + + :param workflow_response: The workflow_response returned from running the workflow + :param logger: the logger to log status information + (logging.INFO for summary info. logging.DEBUG for detail info) + :param workflow_name: name of the workflow to be monitored + :returns: the status at completion or None if the workflow wasn't run + :raises ValidationError: If the provided `workflow_response`, `logger` is invalid + :raises AtlanError: on any API communication issue + """ + name = workflow_name or ( + workflow_response.metadata.name + if workflow_response and workflow_response.metadata + else None + ) + + if not name: + if logger: + logger.info("Skipping workflow monitoring — nothing to monitor.") + return None + + status: Optional[AtlanWorkflowPhase] = None + while status not in { + AtlanWorkflowPhase.SUCCESS, + AtlanWorkflowPhase.ERROR, + AtlanWorkflowPhase.FAILED, + }: + await asyncio.sleep(MONITOR_SLEEP_SECONDS) + if run_details := await self._find_latest_run(workflow_name=name): + status = run_details.status + if logger: + logger.debug("Workflow status: %s", status) + + if logger: + logger.info("Workflow completion status: %s", status) + return status + + async def get_runs( + self, + workflow_name: str, + workflow_phase: AtlanWorkflowPhase, + from_: int = 0, + size: int = 100, + ) -> Optional[AsyncWorkflowSearchResponse]: + """ + Retrieves all workflow runs. + + :param workflow_name: name of the workflow as displayed + in the UI (e.g: `atlan-snowflake-miner-1714638976`). + :param workflow_phase: phase of the given workflow (e.g: Succeeded, Running, Failed, etc). + :param from_: starting index of the search results (default: `0`). + :param size: maximum number of search results to return (default: `100`). + :returns: a list of runs of the given workflow. + :raises AtlanError: on any API communication issue. + """ + query = Bool( + must=[ + NestedQuery( + query=Term( + field="spec.workflowTemplateRef.name.keyword", + value=workflow_name, + ), + path="spec", + ) + ], + filter=[Term(field="status.phase.keyword", value=workflow_phase.value)], + ) + return await self._find_runs(query, from_=from_, size=size) + + @validate_arguments + async def stop( + self, + workflow_run_id: str, + ) -> WorkflowRunResponse: + """ + Stop the provided, running workflow. + + :param workflow_run_id: identifier of the specific workflow run + :returns: the stopped workflow run + :raises AtlanError: on any API communication issue + """ + endpoint, _ = WorkflowStop.prepare_request(workflow_run_id) + raw_json = await self._client._call_api(endpoint, request_obj=None) + return msgspec.convert(raw_json, WorkflowRunResponse, strict=False) + + @validate_arguments + async def delete( + self, + workflow_name: str, + ) -> None: + """ + Archive (delete) the provided workflow. + + :param workflow_name: name of the workflow as displayed + in the UI (e.g: `atlan-snowflake-miner-1714638976`). + :raises AtlanError: on any API communication issue. + """ + use_package_endpoint = not await self._client.role_cache.is_api_token_user() # type: ignore[attr-defined] + endpoint, _ = WorkflowDelete.prepare_request( + workflow_name, use_package_endpoint + ) + await self._client._call_api(endpoint, request_obj=None) + + @overload + async def add_schedule( + self, workflow: WorkflowResponse, workflow_schedule: WorkflowSchedule + ) -> WorkflowResponse: ... + + @overload + async def add_schedule( + self, workflow: WorkflowPackage, workflow_schedule: WorkflowSchedule + ) -> WorkflowResponse: ... + + @overload + async def add_schedule( + self, workflow: WorkflowSearchResult, workflow_schedule: WorkflowSchedule + ) -> WorkflowResponse: ... + + @overload + async def add_schedule( + self, workflow: WorkflowSearchResultDetail, workflow_schedule: WorkflowSchedule + ) -> WorkflowResponse: ... + + async def add_schedule( + self, + workflow: Union[ + WorkflowResponse, + WorkflowPackage, + WorkflowSearchResult, + WorkflowSearchResultDetail, + ], + workflow_schedule: WorkflowSchedule, + ) -> WorkflowResponse: + """ + Add a schedule for an existing workflow run. + + :param workflow: existing workflow run to schedule. + :param workflow_schedule: a WorkflowSchedule object containing: + - A cron schedule expression, e.g: `5 4 * * *`. + - The time zone for the cron schedule, e.g: `Europe/Paris`. + + :returns: a scheduled workflow. + :raises AtlanError: on any API communication issue. + """ + validate_type( + name="workflow", + _type=( + WorkflowResponse, + WorkflowPackage, + WorkflowSearchResult, + WorkflowSearchResultDetail, + ), + value=workflow, + ) + workflow_to_update = await self._handle_workflow_types(workflow) + + self._add_schedule(workflow_to_update, workflow_schedule) + use_package_endpoint = not await self._client.role_cache.is_api_token_user() # type: ignore[attr-defined] + workflow_name = workflow_to_update.metadata and workflow_to_update.metadata.name + if use_package_endpoint: + endpoint = PACKAGE_WORKFLOW_UPDATE.format_path( + {"workflow_name": workflow_name} + ) + else: + endpoint = WORKFLOW_UPDATE.format_path({"workflow_name": workflow_name}) + raw_json = await self._client._call_api( + endpoint, request_obj=workflow_to_update + ) + return msgspec.convert(raw_json, WorkflowResponse, strict=False) + + @overload + async def remove_schedule(self, workflow: WorkflowResponse) -> WorkflowResponse: ... + + @overload + async def remove_schedule(self, workflow: WorkflowPackage) -> WorkflowResponse: ... + + @overload + async def remove_schedule( + self, workflow: WorkflowSearchResult + ) -> WorkflowResponse: ... + + @overload + async def remove_schedule( + self, workflow: WorkflowSearchResultDetail + ) -> WorkflowResponse: ... + + async def remove_schedule( + self, + workflow: Union[ + WorkflowResponse, + WorkflowPackage, + WorkflowSearchResult, + WorkflowSearchResultDetail, + ], + ) -> WorkflowResponse: + """ + Remove a scheduled run from an existing workflow run. + + :param workflow_run: existing workflow run to remove the schedule from. + :returns: a workflow. + :raises AtlanError: on any API communication issue. + """ + validate_type( + name="workflow", + _type=( + WorkflowResponse, + WorkflowPackage, + WorkflowSearchResult, + WorkflowSearchResultDetail, + ), + value=workflow, + ) + workflow_to_update = await self._handle_workflow_types(workflow) + + if workflow_to_update.metadata and workflow_to_update.metadata.annotations: + workflow_to_update.metadata.annotations.pop( + self._WORKFLOW_RUN_SCHEDULE, None + ) + use_package_endpoint = not await self._client.role_cache.is_api_token_user() # type: ignore[attr-defined] + workflow_name = workflow_to_update.metadata and workflow_to_update.metadata.name + if use_package_endpoint: + endpoint = PACKAGE_WORKFLOW_UPDATE.format_path( + {"workflow_name": workflow_name} + ) + else: + endpoint = WORKFLOW_UPDATE.format_path({"workflow_name": workflow_name}) + raw_json = await self._client._call_api( + endpoint, request_obj=workflow_to_update + ) + return msgspec.convert(raw_json, WorkflowResponse, strict=False) + + async def get_all_scheduled_runs(self) -> List[WorkflowScheduleResponse]: + """ + Get the details of scheduled run for all workflow. + + :returns: list of all the workflow schedules + :raises AtlanError: on any API communication issue + """ + endpoint, _ = WorkflowGetAllScheduledRuns.prepare_request() + raw_json = await self._client._call_api(endpoint, request_obj=None) + items = raw_json.get("items") if raw_json else None + if not items: + return [] + return msgspec.convert(items, list[WorkflowScheduleResponse], strict=False) + + @validate_arguments + async def get_scheduled_run(self, workflow_name: str) -> WorkflowScheduleResponse: + """ + Get the details of scheduled run for a specific workflow. + + :param workflow_name: name of the workflow for which we want the scheduled run details + :returns: details of the workflow schedule + :raises AtlanError: on any API communication issue + """ + endpoint, _ = WorkflowGetScheduledRun.prepare_request(workflow_name) + raw_json = await self._client._call_api(endpoint, request_obj=None) + return msgspec.convert(raw_json, WorkflowScheduleResponse, strict=False) + + @validate_arguments + async def find_schedule_query( + self, saved_query_id: str, max_results: int = 10 + ) -> List[WorkflowSearchResult]: + """ + Find scheduled query workflows by their saved query identifier. + + :param saved_query_id: identifier of the saved query. + :param max_results: maximum number of results to retrieve. Defaults to `10`. + :raises AtlanError: on any API communication issue. + :returns: a list of scheduled query workflows. + """ + query = Bool( + filter=[ + NestedQuery( + path="metadata", + query=Prefix( + field="metadata.name.keyword", + value=f"asq-{saved_query_id}", + ), + ), + NestedQuery( + path="metadata", + query=Term( + field="metadata.annotations.package.argoproj.io/name.keyword", + value="@atlan/schedule-query", + ), + ), + ] + ) + request = WorkflowSearchRequest( + query=query.to_dict(), size=max_results, sort=_DEFAULT_SORT + ) + raw_json = await self._client._call_api( + WORKFLOW_INDEX_SEARCH, request_obj=request + ) + response = msgspec.convert(raw_json, WorkflowSearchResponse, strict=False) + return response.hits and response.hits.hits or [] + + @validate_arguments + async def re_run_schedule_query( + self, schedule_query_id: str + ) -> WorkflowRunResponse: + """ + Re-run a scheduled query. + + :param schedule_query_id: ID of the scheduled query to re-run + :returns: the workflow run response + :raises AtlanError: on any API communication issue + """ + request = ReRunRequest(namespace="default", resource_name=schedule_query_id) + raw_json = await self._client._call_api( + WORKFLOW_OWNER_RERUN, request_obj=request + ) + return msgspec.convert(raw_json, WorkflowRunResponse, strict=False) + + @validate_arguments + async def find_schedule_query_between( + self, + request: ScheduleQueriesSearchRequest, + missed: bool = False, + ) -> Optional[List[WorkflowRunResponse]]: + """ + Find scheduled query workflows within the specified duration. + + :param request: a `ScheduleQueriesSearchRequest` object containing + start and end dates in ISO 8601 format (e.g: `2024-03-25T16:30:00.000+05:30`). + :param missed: if `True`, perform a search for missed + scheduled query workflows. Defaults to `False`. + :raises AtlanError: on any API communication issue. + :returns: a list of scheduled query workflows found within the specified duration. + """ + endpoint, query_params = WorkflowFindScheduleQueryBetween.prepare_request( + request, missed + ) + raw_json = await self._client._call_api(endpoint, query_params=query_params) + if not raw_json: + return None + if isinstance(raw_json, list): + return msgspec.convert(raw_json, list[WorkflowRunResponse], strict=False) + return msgspec.convert(raw_json, WorkflowRunResponse, strict=False) diff --git a/pyatlan_v9/client/asset.py b/pyatlan_v9/client/asset.py new file mode 100644 index 000000000..19e64e073 --- /dev/null +++ b/pyatlan_v9/client/asset.py @@ -0,0 +1,2298 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +from __future__ import annotations + +import json +import logging +import time +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Dict, + List, + Optional, + Type, + TypeVar, + Union, + cast, + overload, +) +from warnings import warn + +import msgspec +from tenacity import ( + RetryError, + retry, + retry_if_exception_type, + stop_after_attempt, + wait_exponential, + wait_fixed, +) + +# --------------------------------------------------------------------------- +# Re-export legacy utility classes that don't need migration +# (they are plain Python classes, not Pydantic models) +# --------------------------------------------------------------------------- +from pyatlan.client.asset import ( # noqa: F401 + AssetIdentity, + CategoryHierarchy, + CustomMetadataHandling, + FailedBatch, + IndexSearchResults, + LineageListResults, + SearchResults, +) +from pyatlan.client.asset import Batch as _LegacyBatch +from pyatlan.client.common import ( + ApiCaller, + DeleteByGuid, + FindCategoryFastByName, + FindConnectionsByName, + FindDomainByName, + FindGlossaryByName, + FindProductByName, + FindTermFastByName, + GetByGuid, + GetByQualifiedName, + GetHierarchy, + GetLineageList, + ManageCustomMetadata, + ManageTerms, + PurgeByGuid, + RemoveAnnouncement, + RemoveCertificate, + RemoveCustomMetadata, + ReplaceCustomMetadata, + RestoreAsset, + Search, + UpdateAnnouncement, + UpdateAsset, + UpdateAssetByAttribute, + UpdateCertificate, + UpdateCustomMetadataAttributes, +) +from pyatlan.client.constants import BULK_UPDATE, DELETE_ENTITIES_BY_GUIDS +from pyatlan.errors import AtlanError, ErrorCode, NotFoundError, PermissionError +from pyatlan.model.fields.atlan_fields import AtlanField +from pyatlan.utils import unflatten_custom_metadata_for_entity +from pyatlan_v9.model.aggregation import Aggregations +from pyatlan_v9.model.assets import ( + Asset, + AtlasGlossary, + AtlasGlossaryCategory, + AtlasGlossaryTerm, + Connection, + DataDomain, + DataProduct, + Persona, + Purpose, +) +from pyatlan_v9.model.core import ( + Announcement, + AtlanRequest, + AtlanTag, + AtlanTagName, + BulkRequest, +) +from pyatlan_v9.model.custom_metadata import CustomMetadataDict +from pyatlan_v9.model.enums import ( + AtlanConnectorType, + AtlanDeleteType, + CertificateStatus, + DataQualityScheduleType, + EntityStatus, + SaveSemantic, + SortOrder, +) +from pyatlan_v9.model.lineage import LineageListRequest +from pyatlan_v9.model.response import AssetMutationResponse, MutatedEntities +from pyatlan_v9.model.search import IndexSearchRequest, Query +from pyatlan_v9.model.transform import from_atlas_format +from pyatlan_v9.validate import validate_arguments + +if TYPE_CHECKING: + from pyatlan_v9.client.atlan import AtlanClient + +LOGGER = logging.getLogger(__name__) + +A = TypeVar("A", bound=Asset) + + +def _custom_metadata_payload(custom_metadata_request: Any) -> Any: + """Normalize custom metadata request wrappers to raw payload dictionaries.""" + if hasattr(custom_metadata_request, "to_dict") and callable( + custom_metadata_request.to_dict + ): + return custom_metadata_request.to_dict() + root_payload = getattr(custom_metadata_request, "__root__", None) + if root_payload is not None: + return root_payload + if hasattr(custom_metadata_request, "dict") and callable( + custom_metadata_request.dict + ): + return custom_metadata_request.dict(by_alias=True, exclude_none=True) + return custom_metadata_request + + +_GLOSSARY_ASSET_TYPES = {"AtlasGlossaryTerm", "AtlasGlossaryCategory"} + + +def _handle_v9_glossary_anchor(asset, asset_type_name: str, glossary_guid): + """Set glossary anchor on v9 glossary assets. + + The legacy ``ManageAssetAttributes.handle_glossary_anchor`` uses + ``isinstance`` against legacy Pydantic models, which doesn't recognise v9 + ``msgspec.Struct`` assets. This helper performs the same check using the + asset's ``type_name`` attribute so it works regardless of model layer. + """ + if getattr(asset, "type_name", None) in _GLOSSARY_ASSET_TYPES: + if not glossary_guid: + raise ErrorCode.MISSING_GLOSSARY_GUID.exception_with_parameters( + asset_type_name + ) + asset.anchor = AtlasGlossary.ref_by_guid(glossary_guid) + + +def _matches_asset_type(asset, asset_type) -> bool: + """Check if *asset* matches *asset_type*, supporting both legacy and v9 models.""" + return ( + isinstance(asset, asset_type) + or getattr(asset, "type_name", None) == asset_type.__name__ + ) + + +def _is_glossary_category(asset) -> bool: + return ( + isinstance(asset, AtlasGlossaryCategory) + or getattr(asset, "type_name", None) == "AtlasGlossaryCategory" + ) + + +def _process_search_results_v9( + results, name: str, asset_type, allow_multiple: bool = False +): + """v9-aware replacement for ``SearchForAssetWithName.process_search_results``.""" + if ( + results + and results.count > 0 + and ( + assets := [ + asset + for asset in (results.current_page() or results) + if _matches_asset_type(asset, asset_type) + ] + ) + ): + if not allow_multiple and len(assets) > 1: + LOGGER.warning( + "More than 1 %s found with the name '%s', returning only the first.", + asset_type.__name__, + name, + ) + return assets + raise ErrorCode.ASSET_NOT_FOUND_BY_NAME.exception_with_parameters( + asset_type.__name__, name + ) + + +def _process_find_response_v9( + search_results, name: str, asset_type, allow_multiple: bool = True +): + """v9-aware replacement for ``FindAssetsByName.process_response``.""" + if ( + search_results + and search_results.count > 0 + and ( + assets := [ + asset + for asset in (search_results.current_page() or search_results) + if _matches_asset_type(asset, asset_type) + ] + ) + ): + if not allow_multiple and len(assets) > 1: + LOGGER.warning( + "More than 1 %s found with the name '%s', returning only the first.", + asset_type.__name__, + name, + ) + return assets + raise ErrorCode.ASSET_NOT_FOUND_BY_NAME.exception_with_parameters( + asset_type.__name__, name + ) + + +def _process_hierarchy_v9(response, glossary) -> CategoryHierarchy: + """v9-aware replacement for ``GetHierarchy.process_search_results``. + + Uses ``type_name`` attribute instead of ``isinstance`` to recognise + v9 ``msgspec.Struct`` categories. + """ + top_categories: set = set() + category_dict = {} + + for category in filter(_is_glossary_category, response): + guid = category.guid + if not getattr(category, "children_categories", None): + category.children_categories = None + category_dict[guid] = category + if not category.parent_category: + top_categories.add(guid) + + if not top_categories: + raise ErrorCode.NO_CATEGORIES.exception_with_parameters( + glossary.guid, glossary.qualified_name + ) + + return CategoryHierarchy(top_level=top_categories, stub_dict=category_dict) + + +# --------------------------------------------------------------------------- +# v9-native response helpers (raw JSON -> v9 msgspec assets) +# --------------------------------------------------------------------------- + + +def _parse_entities_v9(entities: List[Dict], criteria=None) -> list: + """Parse raw entity dicts into v9 msgspec assets. + + Applies custom-metadata unflattening (if *criteria* carries an + ``attributes`` list) and then converts each entity dict via + ``from_atlas_format``. + """ + attributes = getattr(criteria, "attributes", None) + for entity in entities: + unflatten_custom_metadata_for_entity(entity=entity, attributes=attributes) + return [from_atlas_format(e) for e in entities] + + +def _parse_mutation_response(raw_json: Dict) -> AssetMutationResponse: + """Build a v9 ``AssetMutationResponse`` from raw API JSON. + + Entity lists are parsed directly into v9 msgspec asset types via + ``from_atlas_format`` -- no Pydantic parsing involved. + """ + mutated = None + if me_raw := raw_json.get("mutatedEntities"): + mutated = MutatedEntities( + CREATE=( + _parse_entities_v9(me_raw["CREATE"]) if me_raw.get("CREATE") else None + ), + UPDATE=( + _parse_entities_v9(me_raw["UPDATE"]) if me_raw.get("UPDATE") else None + ), + DELETE=( + _parse_entities_v9(me_raw["DELETE"]) if me_raw.get("DELETE") else None + ), + PARTIAL_UPDATE=( + _parse_entities_v9(me_raw["PARTIAL_UPDATE"]) + if me_raw.get("PARTIAL_UPDATE") + else None + ), + ) + return AssetMutationResponse( + guid_assignments=raw_json.get("guidAssignments"), + mutated_entities=mutated, + partial_updated_entities=( + _parse_entities_v9(raw_json["partialUpdatedEntities"]) + if raw_json.get("partialUpdatedEntities") + else None + ), + ) + + +def _parse_aggregations_v9(raw: Dict) -> Optional[Aggregations]: + """Convert raw aggregation JSON into a v9 ``Aggregations`` wrapper. + + Each entry is discriminated by its keys: ``buckets`` -> bucket result, + ``hits`` -> hits result, ``value`` -> metric result. + Nested aggregations inside buckets are recursively parsed. + """ + from pyatlan_v9.model.aggregation import ( + AggregationBucketResult, + AggregationHitsResult, + AggregationMetricResult, + ) + + def _parse_nested(bucket_dict: dict) -> Optional[Aggregations]: + """Parse nested aggregation results inside a bucket, recursively.""" + nested: Dict = {} + known_keys = { + "key", + "doc_count", + "key_as_string", + "max_matching_length", + "to", + "to_as_string", + "from", + "from_as_string", + } + for k, v in bucket_dict.items(): + if k in known_keys or not isinstance(v, dict): + continue + try: + if "buckets" in v: + result = msgspec.convert(v, AggregationBucketResult, strict=False) + raw_inner_buckets = v.get("buckets", []) + for i, inner_bucket in enumerate(result.buckets): + if i < len(raw_inner_buckets): + try: + inner_bucket.nested_results = _parse_nested( + raw_inner_buckets[i] + ) + except Exception: + pass + nested[k] = result + elif "hits" in v: + nested[k] = msgspec.convert(v, AggregationHitsResult, strict=False) + elif "value" in v: + nested[k] = msgspec.convert( + v, AggregationMetricResult, strict=False + ) + except Exception: + pass + return Aggregations(data=nested) if nested else None + + parsed: Dict = {} + for key, value in raw.items(): + if not isinstance(value, dict): + continue + try: + if "buckets" in value: + result = msgspec.convert(value, AggregationBucketResult, strict=False) + raw_buckets = value.get("buckets", []) + for i, bucket in enumerate(result.buckets): + if i < len(raw_buckets): + try: + bucket.nested_results = _parse_nested(raw_buckets[i]) + except Exception: + pass + parsed[key] = result + elif "hits" in value: + parsed[key] = msgspec.convert( + value, AggregationHitsResult, strict=False + ) + elif "value" in value: + parsed[key] = msgspec.convert( + value, AggregationMetricResult, strict=False + ) + except Exception: + pass + return Aggregations(data=parsed) if parsed else None + + +def _process_search_response_v9(raw_json: Dict, criteria) -> Dict: + """Process a search API response into v9 msgspec assets.""" + if "entities" in raw_json: + assets = _parse_entities_v9(raw_json["entities"], criteria) + else: + assets = [] + + aggregations = None + if "aggregations" in raw_json: + try: + aggregations = _parse_aggregations_v9(raw_json["aggregations"]) + except Exception: + pass + + approximate_count = raw_json.get("approximateCount", 0) + return { + "assets": assets, + "aggregations": aggregations, + "count": approximate_count, + } + + +def _process_lineage_response_v9(raw_json: Dict, lineage_request) -> Dict: + """Process a lineage list API response into v9 msgspec assets.""" + if "entities" in raw_json: + assets = _parse_entities_v9(raw_json["entities"], lineage_request) + has_more = bool(raw_json.get("hasMore", False)) + else: + assets = [] + has_more = False + return {"assets": assets, "has_more": has_more} + + +def _process_get_response_v9( + raw_json: Dict, identifier: str, asset_type, *, by_guid: bool = False +) -> Any: + """Process a get-by-guid or get-by-qualified-name API response. + + Merges relationship attributes into the entity dict, converts to a + v9 msgspec asset via ``from_atlas_format``, and validates the result + type. + """ + import logging + + LOGGER = logging.getLogger(__name__) + + entity = raw_json["entity"] + + # DEBUG: Log what we received from API + LOGGER.debug(f"Entity keys from API: {entity.keys()}") + LOGGER.debug(f"Has meanings: {'meanings' in entity}") + if "meanings" in entity: + LOGGER.debug(f"Meanings value: {entity['meanings']}") + + if not by_guid and entity.get("typeName") != asset_type.__name__: + raise ErrorCode.ASSET_NOT_FOUND_BY_NAME.exception_with_parameters( + asset_type.__name__, identifier + ) + + if entity.get("relationshipAttributes"): + entity.setdefault("attributes", {}).update(entity["relationshipAttributes"]) + entity["relationshipAttributes"] = {} + + asset = from_atlas_format(entity) + asset.is_incomplete = False + + # DEBUG: Log what we got after deserialization + LOGGER.debug( + f"After deserialization, meanings: {asset.meanings if hasattr(asset, 'meanings') else 'NO ATTR'}" + ) + LOGGER.debug( + f"After deserialization, assigned_terms: {asset.assigned_terms if hasattr(asset, 'assigned_terms') else 'NO ATTR'}" + ) + + if not isinstance(asset, asset_type): + if by_guid: + raise ErrorCode.ASSET_NOT_TYPE_REQUESTED.exception_with_parameters( + identifier, asset_type.__name__ + ) + else: + raise ErrorCode.ASSET_NOT_FOUND_BY_NAME.exception_with_parameters( + asset_type.__name__, identifier + ) + return asset + + +# --------------------------------------------------------------------------- +# V9-native search result subclasses (override entity parsing for pagination) +# --------------------------------------------------------------------------- + + +class V9IndexSearchResults(IndexSearchResults): + """IndexSearchResults that deserializes pages into v9 msgspec assets.""" + + def _process_entities(self, entities): + self._assets = _parse_entities_v9(entities, self._criteria) + + +class V9LineageListResults(LineageListResults): + """LineageListResults that deserializes pages into v9 msgspec assets.""" + + def _process_entities(self, entities): + self._assets = _parse_entities_v9(entities, self._criteria) + + +def _make_bulk_request_payload(entities: List[Asset], client: "AtlanClient") -> dict: + """ + Serialize a list of Asset entities into an API-ready dict, + applying AtlanTag retranslation (human names -> internal IDs). + """ + bulk = BulkRequest(entities=entities) + request_dict = bulk.to_dict() + for entity in request_dict.get("entities", []): + _normalize_meanings_for_mutation(entity) + retranslated = AtlanRequest(instance=request_dict, client=client) + return retranslated.translated + + +def _make_asset_request_payload(asset: Asset, client: "AtlanClient") -> dict: + """ + Serialize a single Asset entity into an API-ready dict, + applying AtlanTag retranslation. + """ + asset_dict = {"entity": json.loads(asset.to_json(nested=True))} + _normalize_meanings_for_mutation(asset_dict["entity"]) + retranslated = AtlanRequest(instance=asset_dict, client=client) + return retranslated.translated + + +def _normalize_meanings_for_mutation(entity: dict[str, Any]) -> None: + """ + Normalize term assignment payloads for mutation APIs. + + Legacy API payloads send term links under `attributes.meanings`. + v9 nested serialization exposes `meanings` as a top-level field, so + move it into attributes before submission to preserve parity. + """ + if "meanings" not in entity: + return + meanings = entity.pop("meanings") + if not isinstance(meanings, list): + meanings = [meanings] + + replace_meanings: list[dict[str, Any]] = [] + append_meanings: list[dict[str, Any]] = [] + remove_meanings: list[dict[str, Any]] = [] + + for meaning in meanings: + if not isinstance(meaning, dict): + replace_meanings.append(meaning) + continue + semantic = meaning.get("semantic") + normalized = {k: v for k, v in meaning.items() if k != "semantic"} + if semantic == "APPEND": + append_meanings.append(normalized) + elif semantic == "REMOVE": + remove_meanings.append(normalized) + else: + replace_meanings.append(normalized) + + if append_meanings: + append_rels = entity.get("appendRelationshipAttributes") + if not isinstance(append_rels, dict): + append_rels = {} + append_rels["meanings"] = append_meanings + entity["appendRelationshipAttributes"] = append_rels + + if remove_meanings: + remove_rels = entity.get("removeRelationshipAttributes") + if not isinstance(remove_rels, dict): + remove_rels = {} + remove_rels["meanings"] = remove_meanings + entity["removeRelationshipAttributes"] = remove_rels + + if replace_meanings or (not append_meanings and not remove_meanings): + attrs = entity.get("attributes") + if not isinstance(attrs, dict): + attrs = {} + attrs["meanings"] = replace_meanings + entity["attributes"] = attrs + + +# --------------------------------------------------------------------------- +# V9 Asset Client +# --------------------------------------------------------------------------- + + +class V9AssetClient: + """ + This class can be used to retrieve information about assets. This class does not need to be instantiated + directly but can be obtained through the asset property of AtlanClient. + """ + + def __init__(self, client: ApiCaller): + if not isinstance(client, ApiCaller): + raise ErrorCode.INVALID_PARAMETER_TYPE.exception_with_parameters( + "client", "ApiCaller" + ) + self._client = client + + # ------------------------------------------------------------------ + # Search + # ------------------------------------------------------------------ + + def search(self, criteria: IndexSearchRequest, bulk=False) -> IndexSearchResults: + """ + Search for assets using the provided criteria. + `Note:` if the number of results exceeds the predefined threshold + (100,000 assets) this will be automatically converted into a `bulk` search. + + :param criteria: detailing the search query, parameters, and so on to run + :param bulk: whether to run the search to retrieve assets that match the supplied criteria, + for large numbers of results (> `100,000`), defaults to `False`. Note: this will reorder the results + (based on creation timestamp) in order to iterate through a large number (more than `100,000`) results. + :raises InvalidRequestError: + + - if bulk search is enabled (`bulk=True`) and any + user-specified sorting options are found in the search request. + - if bulk search is disabled (`bulk=False`) and the number of results + exceeds the predefined threshold (i.e: `100,000` assets) + and any user-specified sorting options are found in the search request. + + :raises AtlanError: on any API communication issue + :returns: the results of the search + """ + endpoint, request_obj = Search.prepare_request(criteria, bulk) + raw_json = self._client._call_api( + endpoint, + request_obj=request_obj, + ) + response = _process_search_response_v9(raw_json, criteria) + if Search._check_for_bulk_search(criteria, response["count"], bulk): + return self.search(criteria) + return V9IndexSearchResults( + client=self._client, + criteria=criteria, + start=criteria.dsl.from_, + size=criteria.dsl.size, + count=response["count"], + assets=response["assets"], + aggregations=response["aggregations"], + bulk=bulk, + ) + + # ------------------------------------------------------------------ + # Lineage + # ------------------------------------------------------------------ + + def get_lineage_list( + self, lineage_request: LineageListRequest + ) -> LineageListResults: + """ + Retrieve lineage using the higher-performance "list" API. + + :param lineage_request: detailing the lineage query, parameters, and so on to run + :returns: the results of the lineage request + :raises InvalidRequestError: if the requested lineage direction is 'BOTH' (unsupported for this operation) + :raises AtlanError: on any API communication issue + """ + endpoint, request_obj = GetLineageList.prepare_request(lineage_request) + raw_json = self._client._call_api(endpoint, request_obj=request_obj) + response = _process_lineage_response_v9(raw_json, lineage_request) + return V9LineageListResults( + client=self._client, + criteria=lineage_request, + start=lineage_request.offset or 0, + size=lineage_request.size or 10, + has_more=response["has_more"], + assets=response["assets"], + ) + + # ------------------------------------------------------------------ + # Find by name helpers + # ------------------------------------------------------------------ + + def _prepare_fluent_search( + self, + wheres: List[Query], + attributes: Optional[List[str]] = None, + related_attributes: Optional[List[str]] = None, + ): + from pyatlan_v9.model.fluent_search import FluentSearch + + search = FluentSearch() + for w in wheres: + search = search.where(w) + for attr in attributes or []: + search = search.include_on_results(attr) + for rel_attr in related_attributes or []: + search = search.include_on_relations(rel_attr) + return search + + def _build_find_request( + self, + name: str, + type_name: str, + attributes: Optional[List[str]] = None, + ) -> IndexSearchRequest: + from pyatlan.model.search import Term + from pyatlan_v9.model.search import DSL as V9DSL + + if attributes is None: + attributes = [] + query = ( + Term.with_state("ACTIVE") + + Term.with_type_name(type_name) + + Term.with_name(name) + ) + dsl = V9DSL(query=query) + return IndexSearchRequest( + dsl=dsl, attributes=attributes, relation_attributes=["name"] + ) + + @validate_arguments + def find_personas_by_name( + self, + name: str, + attributes: Optional[List[str]] = None, + ) -> List[Persona]: + """ + Find a persona by its human-readable name. + + :param name: of the persona + :param attributes: (optional) collection of attributes to retrieve for the persona + :returns: all personas with that name, if found + :raises NotFoundError: if no persona with the provided name exists + """ + search_request = self._build_find_request(name, "PERSONA", attributes) + search_results = self.search(search_request) + return _process_find_response_v9( + search_results, name, Persona, allow_multiple=True + ) + + @validate_arguments + def find_purposes_by_name( + self, + name: str, + attributes: Optional[List[str]] = None, + ) -> List[Purpose]: + """ + Find a purpose by its human-readable name. + + :param name: of the purpose + :param attributes: (optional) collection of attributes to retrieve for the purpose + :returns: all purposes with that name, if found + :raises NotFoundError: if no purpose with the provided name exists + """ + search_request = self._build_find_request(name, "PURPOSE", attributes) + search_results = self.search(search_request) + return _process_find_response_v9( + search_results, name, Purpose, allow_multiple=True + ) + + # ------------------------------------------------------------------ + # Get by qualified name / GUID + # ------------------------------------------------------------------ + + @validate_arguments(config=dict(arbitrary_types_allowed=True)) + def get_by_qualified_name( + self, + qualified_name: str, + asset_type: Type[A], + min_ext_info: bool = False, + ignore_relationships: bool = True, + attributes: Optional[Union[List[str], List[AtlanField]]] = None, + related_attributes: Optional[Union[List[str], List[AtlanField]]] = None, + ) -> A: + """ + Retrieves an asset by its qualified_name. + + :param qualified_name: qualified_name of the asset to be retrieved + :param asset_type: type of asset to be retrieved ( must be the actual asset type not a super type) + :param min_ext_info: whether to minimize extra info (True) or not (False) + :param ignore_relationships: whether to include relationships (False) or exclude them (True) + :param attributes: a specific list of attributes to retrieve for the asset + :param related_attributes: a specific list of relationships attributes to retrieve for the asset + :returns: the requested asset + :raises NotFoundError: if the asset does not exist + :raises AtlanError: on any API communication issue + """ + normalized_attributes = GetByQualifiedName.normalize_search_fields(attributes) + normalized_related_attributes = GetByQualifiedName.normalize_search_fields( + related_attributes + ) + + if (normalized_attributes and len(normalized_attributes)) or ( + normalized_related_attributes and len(normalized_related_attributes) + ): + search = self._prepare_fluent_search( + wheres=[ + Asset.QUALIFIED_NAME.eq(qualified_name), + Asset.TYPE_NAME.eq(asset_type.__name__), + ], + attributes=normalized_attributes, + related_attributes=normalized_related_attributes, + ) + results = self.search(search.to_request()) + if results and results.current_page(): + first_result = results.current_page()[0] + if isinstance(first_result, asset_type): + return first_result + raise ErrorCode.ASSET_NOT_FOUND_BY_NAME.exception_with_parameters( + asset_type.__name__, qualified_name + ) + raise ErrorCode.ASSET_NOT_FOUND_BY_QN.exception_with_parameters( + qualified_name, asset_type.__name__ + ) + + endpoint_path, query_params = GetByQualifiedName.prepare_direct_api_request( + qualified_name, asset_type, min_ext_info, ignore_relationships + ) + raw_json = self._client._call_api(endpoint_path, query_params) + return _process_get_response_v9( + raw_json, qualified_name, asset_type, by_guid=False + ) + + @validate_arguments(config=dict(arbitrary_types_allowed=True)) + def get_by_guid( + self, + guid: str, + asset_type: Type[A] = Asset, # type: ignore[assignment] + min_ext_info: bool = False, + ignore_relationships: bool = True, + attributes: Optional[Union[List[str], List[AtlanField]]] = None, + related_attributes: Optional[Union[List[str], List[AtlanField]]] = None, + ) -> A: + """ + Retrieves an asset by its GUID. + + :param guid: unique identifier (GUID) of the asset to retrieve + :param asset_type: type of asset to be retrieved, defaults to `Asset` + :param min_ext_info: whether to minimize extra info (True) or not (False) + :param ignore_relationships: whether to include relationships (False) or exclude them (True) + :param attributes: a specific list of attributes to retrieve for the asset + :param related_attributes: a specific list of relationships attributes to retrieve for the asset + :returns: the requested asset + :raises NotFoundError: if the asset does not exist, or is not of the type requested + :raises AtlanError: on any API communication issue + """ + normalized_attributes = GetByQualifiedName.normalize_search_fields(attributes) + normalized_related_attributes = GetByQualifiedName.normalize_search_fields( + related_attributes + ) + + if (normalized_attributes and len(normalized_attributes)) or ( + normalized_related_attributes and len(normalized_related_attributes) + ): + search = self._prepare_fluent_search( + wheres=[ + Asset.GUID.eq(guid), + Asset.TYPE_NAME.eq(asset_type.__name__), + ], + attributes=normalized_attributes, + related_attributes=normalized_related_attributes, + ) + results = self.search(search.to_request()) + if results and results.current_page(): + first_result = results.current_page()[0] + if isinstance(first_result, asset_type): + return first_result + raise ErrorCode.ASSET_NOT_TYPE_REQUESTED.exception_with_parameters( + guid, asset_type.__name__ + ) + raise ErrorCode.ASSET_NOT_FOUND_BY_GUID.exception_with_parameters(guid) + + endpoint_path, query_params = GetByGuid.prepare_direct_api_request( + guid, min_ext_info, ignore_relationships + ) + raw_json = self._client._call_api(endpoint_path, query_params) + return _process_get_response_v9(raw_json, guid, asset_type, by_guid=True) + + @validate_arguments + def retrieve_minimal( + self, + guid: str, + asset_type: Type[A] = Asset, # type: ignore[assignment] + ) -> A: + """ + Retrieves an asset by its GUID, without any of its relationships. + + :param guid: unique identifier (GUID) of the asset to retrieve + :param asset_type: type of asset to be retrieved, defaults to `Asset` + :returns: the asset, without any of its relationships + :raises NotFoundError: if the asset does not exist + """ + return self.get_by_guid( + guid=guid, + asset_type=asset_type, + min_ext_info=True, + ignore_relationships=True, + ) + + # ------------------------------------------------------------------ + # Save / Upsert + # ------------------------------------------------------------------ + + @validate_arguments + def upsert( + self, + entity: Union[Asset, List[Asset]], + replace_atlan_tags: bool = False, + replace_custom_metadata: bool = False, + overwrite_custom_metadata: bool = False, + ) -> AssetMutationResponse: + """Deprecated - use save() instead.""" + warn( + "This method is deprecated, please use 'save' instead, which offers identical functionality.", + DeprecationWarning, + stacklevel=2, + ) + return self.save( + entity=entity, + replace_atlan_tags=replace_atlan_tags, + replace_custom_metadata=replace_custom_metadata, + overwrite_custom_metadata=overwrite_custom_metadata, + ) + + @validate_arguments + def save( + self, + entity: Union[Asset, List[Asset]], + replace_atlan_tags: bool = False, + replace_custom_metadata: bool = False, + overwrite_custom_metadata: bool = False, + append_atlan_tags: bool = False, + ) -> AssetMutationResponse: + """ + If an asset with the same qualified_name exists, updates the existing asset. Otherwise, creates the asset. + If an asset does exist, opertionally overwrites any Atlan tags. Custom metadata will either be + overwritten or merged depending on the options provided. + + :param entity: one or more assets to save + :param replace_atlan_tags: whether to replace AtlanTags during an update (True) or not (False) + :param replace_custom_metadata: replaces any custom metadata with non-empty values provided + :param overwrite_custom_metadata: overwrites any custom metadata, even with empty values + :param append_atlan_tags: whether to add/update/remove AtlanTags during an update (True) or not (False) + :returns: the result of the save + :raises AtlanError: on any API communication issue + :raises ApiError: if a connection was created and blocking until policies are synced overruns the retry limit + """ + query_params = { + "replaceTags": replace_atlan_tags, + "appendTags": append_atlan_tags, + "replaceBusinessAttributes": replace_custom_metadata, + "overwriteBusinessAttributes": overwrite_custom_metadata, + } + + entities: List[Asset] = [] + if isinstance(entity, list): + entities.extend(entity) + else: + entities.append(entity) + + for asset in entities: + asset.validate_required() + asset.flush_custom_metadata(client=self._client) + + request_payload = _make_bulk_request_payload(entities, self._client) + raw_json = self._client._call_api(BULK_UPDATE, query_params, request_payload) + response = _parse_mutation_response(raw_json) + + if connections_created := response.assets_created(Connection): + self._wait_for_connections_to_be_created(connections_created) + return response + + def _wait_for_connections_to_be_created(self, connections_created): + guids = [] + LOGGER.debug("Waiting for connections") + for connection in connections_created: + LOGGER.debug( + "Attempting to retrieve connection with guid: %s", connection.guid + ) + guids.append(connection.guid) + + @retry( + retry=retry_if_exception_type(PermissionError), + wait=wait_exponential(multiplier=1, min=1, max=8), + stop=stop_after_attempt(10), + reraise=True, + ) + def _retrieve_connection_with_retry(guid): + self.retrieve_minimal(guid=guid, asset_type=Connection) + + for guid in guids: + _retrieve_connection_with_retry(guid) + + LOGGER.debug("Finished waiting for connections") + + @validate_arguments + def upsert_merging_cm( + self, entity: Union[Asset, List[Asset]], replace_atlan_tags: bool = False + ) -> AssetMutationResponse: + """Deprecated - use save_merging_cm() instead.""" + warn( + "This method is deprecated, please use 'save_merging_cm' instead, which offers identical functionality.", + DeprecationWarning, + stacklevel=2, + ) + return self.save_merging_cm( + entity=entity, replace_atlan_tags=replace_atlan_tags + ) + + @validate_arguments + def save_merging_cm( + self, entity: Union[Asset, List[Asset]], replace_atlan_tags: bool = False + ) -> AssetMutationResponse: + """ + If no asset exists, has the same behavior as the upsert() method, while also setting + any custom metadata provided. If an asset does exist, optionally overwrites any Atlan tags. + Will merge any provided custom metadata with any custom metadata that already exists on the asset. + + :param entity: one or more assets to save + :param replace_atlan_tags: whether to replace AtlanTags during an update (True) or not (False) + :returns: details of the created or updated assets + """ + return self.save( + entity=entity, + replace_atlan_tags=replace_atlan_tags, + replace_custom_metadata=True, + overwrite_custom_metadata=False, + ) + + @validate_arguments + def update_merging_cm( + self, entity: Asset, replace_atlan_tags: bool = False + ) -> AssetMutationResponse: + """ + If no asset exists, fails with a NotFoundError. Will merge any provided + custom metadata with any custom metadata that already exists on the asset. + If an asset does exist, optionally overwrites any Atlan tags. + + :param entity: the asset to update + :param replace_atlan_tags: whether to replace AtlanTags during an update (True) or not (False) + :returns: details of the updated asset + :raises NotFoundError: if the asset does not exist (will not create it) + """ + UpdateAsset.validate_asset_exists( + qualified_name=entity.qualified_name or "", + asset_type=type(entity), + get_by_qualified_name_func=self.get_by_qualified_name, + ) + return self.save_merging_cm( + entity=entity, replace_atlan_tags=replace_atlan_tags + ) + + @validate_arguments + def upsert_replacing_cm( + self, entity: Union[Asset, List[Asset]], replace_atlan_tags: bool = False + ) -> AssetMutationResponse: + """Deprecated - use save_replacing_cm() instead.""" + warn( + "This method is deprecated, please use 'save_replacing_cm' instead, which offers identical functionality.", + DeprecationWarning, + stacklevel=2, + ) + return self.save_replacing_cm( + entity=entity, replace_atlan_tags=replace_atlan_tags + ) + + @validate_arguments + def save_replacing_cm( + self, entity: Union[Asset, List[Asset]], replace_atlan_tags: bool = False + ) -> AssetMutationResponse: + """ + If no asset exists, has the same behavior as the upsert() method, while also setting + any custom metadata provided. + If an asset does exist, optionally overwrites any Atlan tags. + Will overwrite all custom metadata on any existing asset with only the custom metadata provided + (wiping out any other custom metadata on an existing asset that is not provided in the request). + + :param entity: one or more assets to save + :param replace_atlan_tags: whether to replace AtlanTags during an update (True) or not (False) + :returns: details of the created or updated assets + :raises AtlanError: on any API communication issue + """ + query_params = { + "replaceClassifications": replace_atlan_tags, + "replaceBusinessAttributes": True, + "overwriteBusinessAttributes": True, + } + + entities: List[Asset] = [] + if isinstance(entity, list): + entities.extend(entity) + else: + entities.append(entity) + + for asset in entities: + asset.validate_required() + asset.flush_custom_metadata(client=self._client) + + request_payload = _make_bulk_request_payload(entities, self._client) + raw_json = self._client._call_api(BULK_UPDATE, query_params, request_payload) + return _parse_mutation_response(raw_json) + + @validate_arguments + def update_replacing_cm( + self, entity: Asset, replace_atlan_tags: bool = False + ) -> AssetMutationResponse: + """ + If no asset exists, fails with a NotFoundError. + Will overwrite all custom metadata on any existing asset with only the custom metadata provided + (wiping out any other custom metadata on an existing asset that is not provided in the request). + If an asset does exist, optionally overwrites any Atlan tags. + + :param entity: the asset to update + :param replace_atlan_tags: whether to replace AtlanTags during an update (True) or not (False) + :returns: details of the updated asset + :raises NotFoundError: if the asset does not exist (will not create it) + """ + UpdateAsset.validate_asset_exists( + qualified_name=entity.qualified_name or "", + asset_type=type(entity), + get_by_qualified_name_func=self.get_by_qualified_name, + ) + return self.save_replacing_cm( + entity=entity, replace_atlan_tags=replace_atlan_tags + ) + + # ------------------------------------------------------------------ + # Delete / Purge / Restore + # ------------------------------------------------------------------ + + @validate_arguments + def purge_by_guid( + self, + guid: Union[str, List[str]], + delete_type: AtlanDeleteType = AtlanDeleteType.PURGE, + ) -> AssetMutationResponse: + """ + Deletes one or more assets by their unique identifier (GUID) using the specified delete type. + + :param guid: unique identifier(s) (GUIDs) of one or more assets to delete + :param delete_type: type of deletion to perform: + + - PURGE: completely removes entity and all audit/history traces (default, irreversible) + - HARD: physically removes entity but keeps audit history (irreversible) + + :returns: details of the deleted asset(s) + :raises AtlanError: on any API communication issue + + .. warning:: + PURGE and HARD deletions are irreversible operations. Use with caution. + """ + query_params = PurgeByGuid.prepare_request(guid, delete_type) + raw_json = self._client._call_api( + DELETE_ENTITIES_BY_GUIDS, query_params=query_params + ) + return _parse_mutation_response(raw_json) + + @validate_arguments + def delete_by_guid(self, guid: Union[str, List[str]]) -> AssetMutationResponse: + """ + Soft-deletes (archives) one or more assets by their unique identifier (GUID). + This operation can be reversed by updating the asset and its status to ACTIVE. + + :param guid: unique identifier(s) (GUIDs) of one or more assets to soft-delete + :returns: details of the soft-deleted asset(s) + :raises AtlanError: on any API communication issue + :raises ApiError: if the retry limit is overrun waiting for confirmation the asset is deleted + :raises InvalidRequestError: if an asset does not support archiving + """ + guids = DeleteByGuid.prepare_request(guid) + + assets = [] + for single_guid in guids: + asset = self.retrieve_minimal(guid=single_guid, asset_type=Asset) + assets.append(asset) + DeleteByGuid.validate_assets_can_be_archived(assets) + + query_params = DeleteByGuid.prepare_delete_request(guids) + raw_json = self._client._call_api( + DELETE_ENTITIES_BY_GUIDS, query_params=query_params + ) + response = _parse_mutation_response(raw_json) + + for asset in response.assets_deleted(asset_type=Asset): + try: + self._wait_till_deleted(asset) + except RetryError as err: + raise ErrorCode.RETRY_OVERRUN.exception_with_parameters() from err + return response + + @retry( + reraise=True, + retry=(retry_if_exception_type(AtlanError)), + stop=stop_after_attempt(20), + wait=wait_fixed(1), + ) + def _wait_till_deleted(self, asset: Asset): + asset = self.retrieve_minimal(guid=asset.guid, asset_type=Asset) + if asset.status == EntityStatus.DELETED: + return + + @validate_arguments + def restore(self, asset_type: Type[A], qualified_name: str) -> bool: + """ + Restore an archived (soft-deleted) asset to active. + + :param asset_type: type of the asset to restore + :param qualified_name: of the asset to restore + :returns: True if the asset is now restored, or False if not + :raises AtlanError: on any API communication issue + """ + return self._restore(asset_type, qualified_name, 0) + + def _restore(self, asset_type: Type[A], qualified_name: str, retries: int) -> bool: + if not RestoreAsset.can_asset_type_be_archived(asset_type): + return False + + existing = self.get_by_qualified_name( + asset_type=asset_type, + qualified_name=qualified_name, + ignore_relationships=False, + ) + if not existing: + return False + elif RestoreAsset.is_asset_active(existing): + if retries < 10: + time.sleep(2) + return self._restore(asset_type, qualified_name, retries + 1) + else: + return True + else: + response = self._restore_asset(existing) + return RestoreAsset.is_restore_successful(response) + + def _restore_asset(self, asset: Asset) -> AssetMutationResponse: + to_restore = asset.trim_to_required() + to_restore.status = EntityStatus.ACTIVE + + query_params = { + "replaceClassifications": False, + "replaceBusinessAttributes": False, + "overwriteBusinessAttributes": False, + } + + entities = [to_restore] + for restored in entities: + restored.flush_custom_metadata(self._client) + + request_payload = _make_bulk_request_payload(entities, self._client) + raw_json = self._client._call_api(BULK_UPDATE, query_params, request_payload) + return _parse_mutation_response(raw_json) + + # ------------------------------------------------------------------ + # Atlan Tags + # ------------------------------------------------------------------ + + def _modify_tags( + self, + asset_type: Type[A], + qualified_name: str, + atlan_tag_names: List[str], + propagate: bool = False, + remove_propagation_on_delete: bool = True, + restrict_lineage_propagation: bool = False, + restrict_propagation_through_hierarchy: bool = False, + modification_type: str = "add", + save_parameters: Optional[dict] = None, + ) -> A: + if save_parameters is None: + save_parameters = {} + + @retry( + reraise=True, + retry=retry_if_exception_type(NotFoundError), + stop=stop_after_attempt(10), + wait=wait_exponential(multiplier=1, min=1, max=5), + ) + def _get_asset_with_retry(): + return self.get_by_qualified_name( + qualified_name=qualified_name, + asset_type=asset_type, + attributes=["anchor"], + ) + + retrieved_asset = _get_asset_with_retry() + + # Prepare the asset updater using the v9 model directly + from pyatlan_v9.model.assets import AtlasGlossaryCategory, AtlasGlossaryTerm + + if asset_type in (AtlasGlossaryTerm, AtlasGlossaryCategory): + updated_asset = asset_type.updater( + qualified_name=qualified_name, + name=retrieved_asset.name, + glossary_guid=retrieved_asset.anchor.guid, + ) + else: + updated_asset = asset_type.updater( + qualified_name=qualified_name, name=retrieved_asset.name + ) + + # Create v9 msgspec AtlanTag objects directly + tags = [ + AtlanTag( + type_name=AtlanTagName(display_text=name), + propagate=propagate, + remove_propagations_on_entity_delete=remove_propagation_on_delete, + restrict_propagation_through_lineage=restrict_lineage_propagation, + restrict_propagation_through_hierarchy=restrict_propagation_through_hierarchy, + ) + for name in atlan_tag_names + ] + + # Apply the modification to the v9 entity + if modification_type in ("add", "update"): + updated_asset.add_or_update_classifications = tags + elif modification_type == "remove": + updated_asset.remove_classifications = tags + elif modification_type == "replace": + updated_asset.classifications = tags + + response = self.save(entity=updated_asset, **save_parameters) + if assets := response.assets_updated(asset_type=asset_type): + return assets[0] + return updated_asset + + @validate_arguments + def add_atlan_tags( + self, + asset_type: Type[A], + qualified_name: str, + atlan_tag_names: List[str], + propagate: bool = False, + remove_propagation_on_delete: bool = True, + restrict_lineage_propagation: bool = False, + restrict_propagation_through_hierarchy: bool = False, + ) -> A: + """ + Add one or more Atlan tags to the provided asset. + + :param asset_type: type of asset to which to add the Atlan tags + :param qualified_name: qualified_name of the asset to which to add the Atlan tags + :param atlan_tag_names: human-readable names of the Atlan tags to add to the asset + :param propagate: whether to propagate the Atlan tag (True) or not (False) + :param remove_propagation_on_delete: whether to remove the propagated Atlan tags + when the Atlan tag is removed from this asset (True) or not (False) + :param restrict_lineage_propagation: whether to avoid propagating + through lineage (True) or do propagate through lineage (False) + :param restrict_propagation_through_hierarchy: whether to prevent this Atlan tag from + propagating through hierarchy (True) or allow it to propagate through hierarchy (False) + :returns: the asset that was updated (note that it will NOT contain details of the added Atlan tags) + :raises AtlanError: on any API communication issue + """ + return self._modify_tags( + asset_type=asset_type, + qualified_name=qualified_name, + atlan_tag_names=atlan_tag_names, + propagate=propagate, + remove_propagation_on_delete=remove_propagation_on_delete, + restrict_lineage_propagation=restrict_lineage_propagation, + restrict_propagation_through_hierarchy=restrict_propagation_through_hierarchy, + modification_type="add", + save_parameters={ + "replace_atlan_tags": False, + "append_atlan_tags": True, + }, + ) + + @validate_arguments + def update_atlan_tags( + self, + asset_type: Type[A], + qualified_name: str, + atlan_tag_names: List[str], + propagate: bool = False, + remove_propagation_on_delete: bool = True, + restrict_lineage_propagation: bool = True, + restrict_propagation_through_hierarchy: bool = False, + ) -> A: + """ + Update one or more Atlan tags to the provided asset. + + :param asset_type: type of asset to which to update the Atlan tags + :param qualified_name: qualified_name of the asset to which to update the Atlan tags + :param atlan_tag_names: human-readable names of the Atlan tags to update to the asset + :param propagate: whether to propagate the Atlan tag (True) or not (False) + :param remove_propagation_on_delete: whether to remove the propagated Atlan tags + when the Atlan tag is removed from this asset (True) or not (False) + :param restrict_lineage_propagation: whether to avoid propagating + through lineage (True) or do propagate through lineage (False) + :param restrict_propagation_through_hierarchy: whether to prevent this Atlan tag from + propagating through hierarchy (True) or allow it to propagate through hierarchy (False) + :returns: the asset that was updated (note that it will NOT contain details of the updated Atlan tags) + :raises AtlanError: on any API communication issue + """ + return self._modify_tags( + asset_type=asset_type, + qualified_name=qualified_name, + atlan_tag_names=atlan_tag_names, + propagate=propagate, + remove_propagation_on_delete=remove_propagation_on_delete, + restrict_lineage_propagation=restrict_lineage_propagation, + restrict_propagation_through_hierarchy=restrict_propagation_through_hierarchy, + modification_type="update", + save_parameters={ + "replace_atlan_tags": False, + "append_atlan_tags": True, + }, + ) + + @validate_arguments + def remove_atlan_tag( + self, + asset_type: Type[A], + qualified_name: str, + atlan_tag_name: str, + ) -> A: + """ + Removes a single Atlan tag from the provided asset. + + :param asset_type: type of asset to which to add the Atlan tags + :param qualified_name: qualified_name of the asset to which to add the Atlan tags + :param atlan_tag_name: human-readable name of the Atlan tag to remove from the asset + :returns: the asset that was updated (note that it will NOT contain details of the deleted Atlan tag) + :raises AtlanError: on any API communication issue + """ + return self._modify_tags( + asset_type=asset_type, + qualified_name=qualified_name, + atlan_tag_names=[atlan_tag_name], + modification_type="remove", + save_parameters={ + "replace_atlan_tags": False, + "append_atlan_tags": True, + }, + ) + + @validate_arguments + def remove_atlan_tags( + self, + asset_type: Type[A], + qualified_name: str, + atlan_tag_names: List[str], + ) -> A: + """ + Removes one or more Atlan tag from the provided asset. + + :param asset_type: type of asset to which to add the Atlan tags + :param qualified_name: qualified_name of the asset to which to add the Atlan tags + :param atlan_tag_names: human-readable name of the Atlan tag to remove from the asset + :returns: the asset that was updated (note that it will NOT contain details of the deleted Atlan tags) + :raises AtlanError: on any API communication issue + """ + return self._modify_tags( + asset_type=asset_type, + qualified_name=qualified_name, + atlan_tag_names=atlan_tag_names, + modification_type="remove", + save_parameters={ + "replace_atlan_tags": False, + "append_atlan_tags": True, + }, + ) + + # ------------------------------------------------------------------ + # Update asset by attribute (certificate, announcement, etc.) + # ------------------------------------------------------------------ + + def _update_asset_by_attribute( + self, asset: A, asset_type: Type[A], qualified_name: str + ) -> Optional[A]: + query_params = UpdateAssetByAttribute.prepare_request_params(qualified_name) + asset.flush_custom_metadata(client=self._client) + endpoint = UpdateAssetByAttribute.get_api_endpoint(asset_type) + request_payload = _make_asset_request_payload(asset, self._client) + raw_json = self._client._call_api(endpoint, query_params, request_payload) + response = _parse_mutation_response(raw_json) + if assets := response.assets_partially_updated(asset_type=asset_type): + return assets[0] + if assets := response.assets_updated(asset_type=asset_type): + return assets[0] + return None + + # ------------------------------------------------------------------ + # Certificates + # ------------------------------------------------------------------ + + @overload + def update_certificate( + self, + asset_type: Type[AtlasGlossaryTerm], + qualified_name: str, + name: str, + certificate_status: CertificateStatus, + glossary_guid: str, + message: Optional[str] = None, + ) -> Optional[AtlasGlossaryTerm]: ... + + @overload + def update_certificate( + self, + asset_type: Type[AtlasGlossaryCategory], + qualified_name: str, + name: str, + certificate_status: CertificateStatus, + glossary_guid: str, + message: Optional[str] = None, + ) -> Optional[AtlasGlossaryCategory]: ... + + @overload + def update_certificate( + self, + asset_type: Type[A], + qualified_name: str, + name: str, + certificate_status: CertificateStatus, + glossary_guid: Optional[str] = None, + message: Optional[str] = None, + ) -> Optional[A]: ... + + @validate_arguments + def update_certificate( + self, + asset_type: Type[A], + qualified_name: str, + name: str, + certificate_status: CertificateStatus, + glossary_guid: Optional[str] = None, + message: Optional[str] = None, + ) -> Optional[A]: + """ + Update the certificate on an asset. + + :param asset_type: type of asset on which to update the certificate + :param qualified_name: the qualified_name of the asset on which to update the certificate + :param name: the name of the asset on which to update the certificate + :param certificate_status: specific certificate to set on the asset + :param glossary_guid: unique identifier of the glossary, required + only when the asset type is `AtlasGlossaryTerm` or `AtlasGlossaryCategory` + :param message: (optional) message to set (or None for no message) + :returns: the result of the update, or None if the update failed + :raises AtlanError: on any API communication issue + """ + asset = UpdateCertificate.prepare_asset_with_certificate( + asset_type=asset_type, + qualified_name=qualified_name, + name=name, + certificate_status=certificate_status, + message=message, + glossary_guid=glossary_guid, + ) + _handle_v9_glossary_anchor(asset, asset_type.__name__, glossary_guid) + return self._update_asset_by_attribute(asset, asset_type, qualified_name) + + @overload + def remove_certificate( + self, + asset_type: Type[AtlasGlossaryTerm], + qualified_name: str, + name: str, + glossary_guid: str, + ) -> Optional[AtlasGlossaryTerm]: ... + + @overload + def remove_certificate( + self, + asset_type: Type[AtlasGlossaryCategory], + qualified_name: str, + name: str, + glossary_guid: str, + ) -> Optional[AtlasGlossaryCategory]: ... + + @overload + def remove_certificate( + self, + asset_type: Type[A], + qualified_name: str, + name: str, + glossary_guid: Optional[str] = None, + ) -> Optional[A]: ... + + @validate_arguments + def remove_certificate( + self, + asset_type: Type[A], + qualified_name: str, + name: str, + glossary_guid: Optional[str] = None, + ) -> Optional[A]: + """ + Remove the certificate from an asset. + + :param asset_type: type of asset from which to remove the certificate + :param qualified_name: the qualified_name of the asset from which to remove the certificate + :param name: the name of the asset from which to remove the certificate + :param glossary_guid: unique identifier of the glossary, required + only when the asset type is `AtlasGlossaryTerm` or `AtlasGlossaryCategory` + :returns: the result of the removal, or None if the removal failed + """ + asset = RemoveCertificate.prepare_asset_for_certificate_removal( + asset_type=asset_type, + qualified_name=qualified_name, + name=name, + glossary_guid=glossary_guid, + ) + _handle_v9_glossary_anchor(asset, asset_type.__name__, glossary_guid) + return self._update_asset_by_attribute(asset, asset_type, qualified_name) + + # ------------------------------------------------------------------ + # Announcements + # ------------------------------------------------------------------ + + @overload + def update_announcement( + self, + asset_type: Type[AtlasGlossaryTerm], + qualified_name: str, + name: str, + announcement: Announcement, + glossary_guid: str, + ) -> Optional[AtlasGlossaryTerm]: ... + + @overload + def update_announcement( + self, + asset_type: Type[AtlasGlossaryCategory], + qualified_name: str, + name: str, + announcement: Announcement, + glossary_guid: str, + ) -> Optional[AtlasGlossaryCategory]: ... + + @overload + def update_announcement( + self, + asset_type: Type[A], + qualified_name: str, + name: str, + announcement: Announcement, + glossary_guid: Optional[str] = None, + ) -> Optional[A]: ... + + @validate_arguments(config=dict(arbitrary_types_allowed=True)) + def update_announcement( + self, + asset_type: Type[A], + qualified_name: str, + name: str, + announcement: Announcement, + glossary_guid: Optional[str] = None, + ) -> Optional[A]: + """ + Update the announcement on an asset. + + :param asset_type: type of asset on which to update the announcement + :param qualified_name: the qualified_name of the asset on which to update the announcement + :param name: the name of the asset on which to update the announcement + :param announcement: to apply to the asset + :param glossary_guid: unique identifier of the glossary, required + only when the asset type is `AtlasGlossaryTerm` or `AtlasGlossaryCategory` + :returns: the result of the update, or None if the update failed + """ + asset = UpdateAnnouncement.prepare_asset_with_announcement( + asset_type=asset_type, + qualified_name=qualified_name, + name=name, + announcement=announcement, + glossary_guid=glossary_guid, + ) + _handle_v9_glossary_anchor(asset, asset_type.__name__, glossary_guid) + return self._update_asset_by_attribute(asset, asset_type, qualified_name) + + @overload + def remove_announcement( + self, + asset_type: Type[AtlasGlossaryTerm], + qualified_name: str, + name: str, + glossary_guid: str, + ) -> Optional[AtlasGlossaryTerm]: ... + + @overload + def remove_announcement( + self, + asset_type: Type[AtlasGlossaryCategory], + qualified_name: str, + name: str, + glossary_guid: str, + ) -> Optional[AtlasGlossaryCategory]: ... + + @overload + def remove_announcement( + self, + asset_type: Type[A], + qualified_name: str, + name: str, + glossary_guid: Optional[str] = None, + ) -> Optional[A]: ... + + @validate_arguments + def remove_announcement( + self, + asset_type: Type[A], + qualified_name: str, + name: str, + glossary_guid: Optional[str] = None, + ) -> Optional[A]: + """ + Remove the announcement from an asset. + + :param asset_type: type of asset from which to remove the announcement + :param qualified_name: the qualified_name of the asset from which to remove the announcement + :param glossary_guid: unique identifier of the glossary, required + only when the asset type is `AtlasGlossaryTerm` or `AtlasGlossaryCategory` + :returns: the result of the removal, or None if the removal failed + """ + asset = RemoveAnnouncement.prepare_asset_for_announcement_removal( + asset_type=asset_type, + qualified_name=qualified_name, + name=name, + glossary_guid=glossary_guid, + ) + _handle_v9_glossary_anchor(asset, asset_type.__name__, glossary_guid) + return self._update_asset_by_attribute(asset, asset_type, qualified_name) + + # ------------------------------------------------------------------ + # Custom metadata + # ------------------------------------------------------------------ + + @validate_arguments(config=dict(arbitrary_types_allowed=True)) + def update_custom_metadata_attributes( + self, guid: str, custom_metadata: CustomMetadataDict + ): + """ + Update only the provided custom metadata attributes on the asset. This will leave all + other custom metadata attributes, even within the same named custom metadata, unchanged. + + :param guid: unique identifier (GUID) of the asset + :param custom_metadata: custom metadata to update, as human-readable names mapped to values + :raises AtlanError: on any API communication issue + """ + custom_metadata_request = UpdateCustomMetadataAttributes.prepare_request( + custom_metadata + ) + endpoint = ManageCustomMetadata.get_api_endpoint( + guid, custom_metadata_request.custom_metadata_set_id + ) + payload = _custom_metadata_payload(custom_metadata_request) + self._client._call_api(endpoint, None, payload) + + @validate_arguments(config=dict(arbitrary_types_allowed=True)) + def replace_custom_metadata(self, guid: str, custom_metadata: CustomMetadataDict): + """ + Replace specific custom metadata on the asset. This will replace everything within the named + custom metadata, but will not change any of hte other named custom metadata on the asset. + + :param guid: unique identifier (GUID) of the asset + :param custom_metadata: custom metadata to replace, as human-readable names mapped to values + :raises AtlanError: on any API communication issue + """ + custom_metadata_request = ReplaceCustomMetadata.prepare_request(custom_metadata) + endpoint = ManageCustomMetadata.get_api_endpoint( + guid, custom_metadata_request.custom_metadata_set_id + ) + payload = _custom_metadata_payload(custom_metadata_request) + self._client._call_api(endpoint, None, payload) + + @validate_arguments + def remove_custom_metadata(self, guid: str, cm_name: str): + """ + Remove specific custom metadata from an asset. + + :param guid: unique identifier (GUID) of the asset + :param cm_name: human-readable name of the custom metadata to remove + :raises AtlanError: on any API communication issue + """ + custom_metadata_request = RemoveCustomMetadata.prepare_request( + cm_name, self._client + ) + endpoint = ManageCustomMetadata.get_api_endpoint( + guid, custom_metadata_request.custom_metadata_set_id + ) + payload = _custom_metadata_payload(custom_metadata_request) + self._client._call_api(endpoint, None, payload) + + # ------------------------------------------------------------------ + # Terms management + # ------------------------------------------------------------------ + + def _search_for_asset_with_name( + self, + query: Query, + name: str, + asset_type: Type[A], + attributes: Optional[List], + allow_multiple: bool = False, + ) -> List[A]: + from pyatlan_v9.model.search import DSL as V9DSL + + dsl = V9DSL(query=query) + search_request = IndexSearchRequest( + dsl=dsl, + attributes=attributes or [], + relation_attributes=["name"], + ) + results = self.search(search_request) + return _process_search_results_v9(results, name, asset_type, allow_multiple) + + def _manage_terms( + self, + asset_type: Type[A], + terms: List[AtlasGlossaryTerm], + save_semantic: SaveSemantic, + guid: Optional[str] = None, + qualified_name: Optional[str] = None, + ) -> A: + from pyatlan_v9.model.fluent_search import FluentSearch + + ManageTerms.validate_guid_and_qualified_name(guid, qualified_name) + + if guid: + search_query = ( + FluentSearch() + .select() + .where(Asset.TYPE_NAME.eq(asset_type.__name__)) + .where(asset_type.GUID.eq(guid)) + ) + else: + if qualified_name is None: + raise ValueError( + "qualified_name cannot be None when guid is not provided" + ) + search_query = ( + FluentSearch() + .select() + .where(Asset.TYPE_NAME.eq(asset_type.__name__)) + .where(asset_type.QUALIFIED_NAME.eq(qualified_name)) + ) + + results = search_query.execute(client=self._client) + first_result = ManageTerms.validate_search_results( + results, asset_type, guid, qualified_name + ) + updated_asset = asset_type.updater( + qualified_name=first_result.qualified_name, name=first_result.name + ) + processed_terms: list[AtlasGlossaryTerm] = [] + for term in terms: + if getattr(term, "guid", None): + processed_terms.append( + AtlasGlossaryTerm.ref_by_guid( + guid=term.guid, semantic=save_semantic + ) + ) + elif getattr(term, "qualified_name", None): + processed_terms.append( + AtlasGlossaryTerm.ref_by_qualified_name( + qualified_name=term.qualified_name, semantic=save_semantic + ) + ) + updated_asset.assigned_terms = processed_terms + response = self.save(entity=updated_asset) + return ManageTerms.process_save_response(response, asset_type, updated_asset) + + @validate_arguments + def append_terms( + self, + asset_type: Type[A], + terms: List[AtlasGlossaryTerm], + guid: Optional[str] = None, + qualified_name: Optional[str] = None, + ) -> A: + """ + Link additional terms to an asset, without replacing existing terms linked to the asset. + Note: this operation must make two API calls — one to retrieve the asset's existing terms, + and a second to append the new terms. (At least one of the GUID or qualified_name must be + supplied, but both are not necessary.) + + :param asset_type: type of the asset + :param terms: the list of terms to append to the asset + :param guid: unique identifier (GUID) of the asset to which to link the terms + :param qualified_name: the qualified_name of the asset to which to link the terms + :returns: the asset that was updated (note that it will NOT contain details of the appended terms) + """ + return self._manage_terms( + asset_type=asset_type, + terms=terms, + save_semantic=SaveSemantic.APPEND, + guid=guid, + qualified_name=qualified_name, + ) + + @validate_arguments + def replace_terms( + self, + asset_type: Type[A], + terms: List[AtlasGlossaryTerm], + guid: Optional[str] = None, + qualified_name: Optional[str] = None, + ) -> A: + """ + Replace the terms linked to an asset. + (At least one of the GUID or qualified_name must be supplied, but both are not necessary.) + + :param asset_type: type of the asset + :param terms: the list of terms to replace on the asset, or an empty list to remove all terms from an asset + :param guid: unique identifier (GUID) of the asset to which to replace the terms + :param qualified_name: the qualified_name of the asset to which to replace the terms + :returns: the asset that was updated (note that it will NOT contain details of the replaced terms) + """ + return self._manage_terms( + asset_type=asset_type, + terms=terms, + save_semantic=SaveSemantic.REPLACE, + guid=guid, + qualified_name=qualified_name, + ) + + @validate_arguments + def remove_terms( + self, + asset_type: Type[A], + terms: List[AtlasGlossaryTerm], + guid: Optional[str] = None, + qualified_name: Optional[str] = None, + ) -> A: + """ + Remove terms from an asset, without replacing all existing terms linked to the asset. + Note: this operation must make two API calls — one to retrieve the asset's existing terms, + and a second to remove the provided terms. + + :param asset_type: type of the asset + :param terms: the list of terms to remove from the asset (note: these must be references by GUID to efficiently + remove any existing terms) + :param guid: unique identifier (GUID) of the asset from which to remove the terms + :param qualified_name: the qualified_name of the asset from which to remove the terms + :returns: the asset that was updated (note that it will NOT contain details of the resulting terms) + """ + return self._manage_terms( + asset_type=asset_type, + terms=terms, + save_semantic=SaveSemantic.REMOVE, + guid=guid, + qualified_name=qualified_name, + ) + + # ------------------------------------------------------------------ + # Find by name + # ------------------------------------------------------------------ + + @validate_arguments + def find_connections_by_name( + self, + name: str, + connector_type: AtlanConnectorType, + attributes: Optional[List[str]] = None, + ) -> List[Connection]: + """ + Find a connection by its human-readable name and type. + + :param name: of the connection + :param connector_type: of the connection + :param attributes: (optional) collection of attributes to retrieve for the connection + :returns: all connections with that name and type, if found + :raises NotFoundError: if the connection does not exist + """ + if attributes is None: + attributes = [] + query = FindConnectionsByName.build_query(name, connector_type) + return self._search_for_asset_with_name( + query=query, + name=name, + asset_type=Connection, + attributes=attributes, + allow_multiple=True, + ) + + @validate_arguments + def find_glossary_by_name( + self, + name: str, + attributes: Optional[List[str]] = None, + ) -> AtlasGlossary: + """ + Find a glossary by its human-readable name. + + :param name: of the glossary + :param attributes: (optional) collection of attributes to retrieve for the glossary + :returns: the glossary, if found + :raises NotFoundError: if no glossary with the provided name exists + """ + if attributes is None: + attributes = [] + query = FindGlossaryByName.build_query(name) + return self._search_for_asset_with_name( + query=query, name=name, asset_type=AtlasGlossary, attributes=attributes + )[0] + + @validate_arguments + def find_category_fast_by_name( + self, + name: str, + glossary_qualified_name: str, + attributes: Optional[List[str]] = None, + ) -> List[AtlasGlossaryCategory]: + """ + Find a category by its human-readable name. + Note: this operation requires first knowing the qualified_name of the glossary in which the + category exists. Note that categories are not unique by name, so there may be + multiple results. + + :param name: of the category + :param glossary_qualified_name: qualified_name of the glossary in which the category exists + :param attributes: (optional) collection of attributes to retrieve for the category + :returns: the category, if found + :raises NotFoundError: if no category with the provided name exists in the glossary + """ + if attributes is None: + attributes = [] + query = FindCategoryFastByName.build_query(name, glossary_qualified_name) + return self._search_for_asset_with_name( + query=query, + name=name, + asset_type=AtlasGlossaryCategory, + attributes=attributes, + allow_multiple=True, + ) + + @validate_arguments + def find_category_by_name( + self, + name: str, + glossary_name: str, + attributes: Optional[List[str]] = None, + ) -> List[AtlasGlossaryCategory]: + """ + Find a category by its human-readable name. + Note: this operation must run two separate queries to first resolve the qualified_name of the + glossary, so will be somewhat slower. If you already have the qualified_name of the glossary, use + find_category_by_name_fast instead. Note that categories are not unique by name, so there may be + multiple results. + + :param name: of the category + :param glossary_name: human-readable name of the glossary in which the category exists + :param attributes: (optional) collection of attributes to retrieve for the category + :returns: the category, if found + :raises NotFoundError: if no category with the provided name exists in the glossary + """ + glossary = self.find_glossary_by_name(name=glossary_name) + return self.find_category_fast_by_name( + name=name, + glossary_qualified_name=glossary.qualified_name, + attributes=attributes, + ) + + @validate_arguments + def find_term_fast_by_name( + self, + name: str, + glossary_qualified_name: str, + attributes: Optional[List[str]] = None, + ) -> AtlasGlossaryTerm: + """ + Find a term by its human-readable name. + Note: this operation requires first knowing the qualified_name of the glossary in which the + term exists. + + :param name: of the term + :param glossary_qualified_name: qualified_name of the glossary in which the term exists + :param attributes: (optional) collection of attributes to retrieve for the term + :returns: the term, if found + :raises NotFoundError: if no term with the provided name exists in the glossary + """ + if attributes is None: + attributes = [] + query = FindTermFastByName.build_query(name, glossary_qualified_name) + return self._search_for_asset_with_name( + query=query, name=name, asset_type=AtlasGlossaryTerm, attributes=attributes + )[0] + + @validate_arguments + def find_term_by_name( + self, + name: str, + glossary_name: str, + attributes: Optional[List[str]] = None, + ) -> AtlasGlossaryTerm: + """ + Find a term by its human-readable name. + Note: this operation must run two separate queries to first resolve the qualified_name of the + glossary, so will be somewhat slower. If you already have the qualified_name of the glossary, use + find_term_by_name_fast instead. + + :param name: of the term + :param glossary_name: human-readable name of the glossary in which the term exists + :param attributes: (optional) collection of attributes to retrieve for the term + :returns: the term, if found + :raises NotFoundError: if no term with the provided name exists in the glossary + """ + glossary = self.find_glossary_by_name(name=glossary_name) + return self.find_term_fast_by_name( + name=name, + glossary_qualified_name=glossary.qualified_name, + attributes=attributes, + ) + + @validate_arguments + def find_domain_by_name( + self, + name: str, + attributes: Optional[List[str]] = None, + ) -> DataDomain: + """ + Find a data domain by its human-readable name. + + :param name: of the domain + :param attributes: (optional) collection of attributes to retrieve for the domain + :returns: the domain, if found + :raises NotFoundError: if no domain with the provided name exists + """ + attributes = attributes or [] + query = FindDomainByName.build_query(name) + return self._search_for_asset_with_name( + query=query, name=name, asset_type=DataDomain, attributes=attributes + )[0] + + @validate_arguments + def find_product_by_name( + self, + name: str, + attributes: Optional[List[str]] = None, + ) -> DataProduct: + """ + Find a data product by its human-readable name. + + :param name: of the product + :param attributes: (optional) collection of attributes to retrieve for the product + :returns: the product, if found + :raises NotFoundError: if no product with the provided name exists + """ + attributes = attributes or [] + query = FindProductByName.build_query(name) + return self._search_for_asset_with_name( + query=query, name=name, asset_type=DataProduct, attributes=attributes + )[0] + + # ------------------------------------------------------------------ + # Hierarchy + # ------------------------------------------------------------------ + + def get_hierarchy( + self, + glossary: AtlasGlossary, + attributes: Optional[List[Union[AtlanField, str]]] = None, + related_attributes: Optional[List[Union[AtlanField, str]]] = None, + ) -> CategoryHierarchy: + """ + Retrieve category hierarchy in this Glossary, in a traversable form. + + :param glossary: the glossary to retrieve the category hierarchy for + :param attributes: attributes to retrieve for each category in the hierarchy + :param related_attributes: attributes to retrieve for each related asset in the hierarchy + :returns: a traversable category hierarchy + """ + from pyatlan.model.search import Term as SearchTerm + from pyatlan_v9.model.fluent_search import FluentSearch + + GetHierarchy.validate_glossary(glossary) + if attributes is None: + attributes = [] + if related_attributes is None: + related_attributes = [] + search = ( + FluentSearch.select() + .where(AtlasGlossaryCategory.ANCHOR.eq(glossary.qualified_name)) + .where(SearchTerm.with_type_name("AtlasGlossaryCategory")) + .include_on_results(AtlasGlossaryCategory.PARENT_CATEGORY) + .page_size(20) + .sort(AtlasGlossaryCategory.NAME.order(SortOrder.ASCENDING)) + ) + for field in attributes: + search = search.include_on_results(field) + for field in related_attributes: + search = search.include_on_relations(field) + request = search.to_request() + response = self.search(request) + return _process_hierarchy_v9(response, glossary) + + # ------------------------------------------------------------------ + # Bulk processing + # ------------------------------------------------------------------ + + def process_assets( + self, + search, + func: Callable[[Asset], None], + ) -> int: + """ + Process assets matching a search query and apply a processing function to each unique asset. + + This function iteratively searches for assets using the search provider and processes each + unique asset using the provided callable function. The uniqueness of assets is determined + based on their GUIDs. If new assets are found in later iterations that haven't been + processed yet, the process continues until no more new assets are available to process. + + Arguments: + search: IndexSearchRequestProvider + The search provider that generates search queries and contains the criteria for + searching the assets such as a FluentSearch. + func: Callable[[Asset], None] + A callable function that receives each unique asset as its parameter and performs + the required operations on it. + + Returns: + int: The total number of unique assets that have been processed. + """ + guids_processed: set[str] = set() + has_assets_to_process: bool = True + iteration_count = 0 + while has_assets_to_process: + iteration_count += 1 + has_assets_to_process = False + response = self.search(search.to_request()) + LOGGER.debug( + "Iteration %d found %d assets.", iteration_count, response.count + ) + for asset in response: + if asset.guid not in guids_processed: + guids_processed.add(asset.guid) + has_assets_to_process = True + func(asset) + return len(guids_processed) + + # ------------------------------------------------------------------ + # DQ helpers + # ------------------------------------------------------------------ + + @validate_arguments + def add_dq_rule_schedule( + self, + asset_type: Type[A], + asset_name: str, + asset_qualified_name: str, + schedule_crontab: str, + schedule_time_zone: str, + ) -> AssetMutationResponse: + """ + Add a data quality rule schedule to an asset. + + :param asset_type: the type of asset to update (e.g., Table) + :param asset_name: the name of the asset to update + :param asset_qualified_name: the qualified name of the asset to update + :param schedule_crontab: cron expression string defining the schedule for the DQ rules, e.g: `5 4 * * *`. + :param schedule_time_zone: timezone for the schedule, e.g: `Europe/Paris`. + :returns: the result of the save + :raises AtlanError: on any API communication issue + """ + updated_asset = asset_type.updater( + qualified_name=asset_qualified_name, name=asset_name + ) + updated_asset.asset_d_q_schedule_time_zone = schedule_time_zone + updated_asset.asset_d_q_schedule_crontab = schedule_crontab + updated_asset.asset_d_q_schedule_type = DataQualityScheduleType.CRON + return self.save(updated_asset) + + @validate_arguments + def set_dq_row_scope_filter_column( + self, + asset_type: Type[A], + asset_name: str, + asset_qualified_name: str, + row_scope_filter_column_qualified_name: str, + ) -> AssetMutationResponse: + """ + Set the row scope filter column for data quality rules on an asset. + + :param asset_type: the type of asset to update (e.g., Table) + :param asset_name: the name of the asset to update + :param asset_qualified_name: the qualified name of the asset to update + :param row_scope_filter_column_qualified_name: the qualified name of the column to use for row scope filtering + :returns: the result of the save + :raises AtlanError: on any API communication issue + """ + updated_asset = asset_type.updater( + qualified_name=asset_qualified_name, name=asset_name + ) + updated_asset.asset_d_q_row_scope_filter_column_qualified_name = ( + row_scope_filter_column_qualified_name + ) + return self.save(updated_asset) + + +# --------------------------------------------------------------------------- +# V9-aware Batch (bypasses Pydantic's _convert_to_real_type_ validator and +# recognises v9 msgspec glossary terms in the tracking logic) +# --------------------------------------------------------------------------- + + +class Batch(_LegacyBatch): + """V9 wrapper around the legacy ``Batch`` class. + + Overrides ``add()`` to accept v9 ``msgspec.Struct`` assets without + going through Pydantic's ``_convert_to_real_type_`` validator, and + overrides the tracking helper so v9 ``AtlasGlossaryTerm`` instances + are handled correctly. + """ + + def add(self, single) -> Optional[AssetMutationResponse]: + self._batch.append(single) + return self._process() + + @staticmethod + def __track(tracker, candidate): + if ( + isinstance(candidate, AtlasGlossaryTerm) + or getattr(candidate, "type_name", None) == "AtlasGlossaryTerm" + ): + asset = cast(Asset, type(candidate).ref_by_guid(candidate.guid)) + else: + asset = candidate.trim_to_required() + asset.name = candidate.name + tracker.append(asset) diff --git a/pyatlan_v9/client/atlan.py b/pyatlan_v9/client/atlan.py new file mode 100644 index 000000000..2d782087f --- /dev/null +++ b/pyatlan_v9/client/atlan.py @@ -0,0 +1,936 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. +from __future__ import annotations + +import contextlib +import copy +import json +import logging +import os +import uuid +from contextlib import _GeneratorContextManager +from contextvars import ContextVar +from http import HTTPStatus +from importlib.resources import read_text +from types import SimpleNamespace +from typing import TYPE_CHECKING, Any, Dict, Generator, List, Optional, Union +from urllib.parse import urljoin + +import httpx +import msgspec +from httpx_retries import Retry +from msgspec import UNSET, UnsetType + +from pyatlan.cache.atlan_tag_cache import AtlanTagCache +from pyatlan.cache.connection_cache import ConnectionCache +from pyatlan.cache.custom_metadata_cache import CustomMetadataCache +from pyatlan.cache.dq_template_config_cache import DQTemplateConfigCache +from pyatlan.cache.enum_cache import EnumCache +from pyatlan.cache.group_cache import GroupCache +from pyatlan.cache.role_cache import RoleCache +from pyatlan.cache.source_tag_cache import SourceTagCache +from pyatlan.cache.user_cache import UserCache +from pyatlan.client.common import CONNECTION_RETRY +from pyatlan.client.constants import EVENT_STREAM, PARSE_QUERY, UPLOAD_IMAGE +from pyatlan.client.oauth import OAuthTokenManager +from pyatlan.errors import ERROR_CODE_FOR_HTTP_STATUS, AtlanError, ErrorCode +from pyatlan.model.core import AtlanObject as LegacyAtlanObject +from pyatlan.model.core import AtlanRequest as LegacyAtlanRequest +from pyatlan.multipart_data_generator import MultipartDataGenerator +from pyatlan.utils import ( + API, + APPLICATION_ENCODED_FORM, + AuthorizationFilter, + RequestIdAdapter, + get_python_version, +) +from pyatlan_v9.client.admin import V9AdminClient +from pyatlan_v9.client.asset import V9AssetClient +from pyatlan_v9.client.audit import V9AuditClient +from pyatlan_v9.client.contract import V9ContractClient +from pyatlan_v9.client.credential import V9CredentialClient +from pyatlan_v9.client.file import V9FileClient +from pyatlan_v9.client.group import V9GroupClient +from pyatlan_v9.client.impersonate import V9ImpersonationClient +from pyatlan_v9.client.oauth_client import V9OAuthClient +from pyatlan_v9.client.open_lineage import V9OpenLineageClient +from pyatlan_v9.client.query import V9QueryClient +from pyatlan_v9.client.role import V9RoleClient +from pyatlan_v9.client.search_log import V9SearchLogClient +from pyatlan_v9.client.sso import V9SSOClient +from pyatlan_v9.client.task import V9TaskClient +from pyatlan_v9.client.token import V9TokenClient +from pyatlan_v9.client.transport import PyatlanSyncTransport +from pyatlan_v9.client.typedef import V9TypeDefClient +from pyatlan_v9.client.user import V9UserClient +from pyatlan_v9.client.workflow import V9WorkflowClient +from pyatlan_v9.model.atlan_image import AtlanImage +from pyatlan_v9.model.core import AtlanRequest, AtlanResponse +from pyatlan_v9.model.enums import AtlanTypeCategory +from pyatlan_v9.model.query import ParsedQuery, QueryParserRequest + +if TYPE_CHECKING: + from pyatlan_v9.model.assets import Asset + from pyatlan_v9.model.core import AssetMutationResponse + +request_id_var = ContextVar("request_id", default=None) + + +def get_adapter() -> logging.LoggerAdapter: + """ + Creates a LoggerAdapter that provides the requestid from the ContextVar. + + :returns: the LogAdapter + """ + logger = logging.getLogger(__name__) + logger.addFilter(AuthorizationFilter()) + return RequestIdAdapter(logger=logger, contextvar=request_id_var) + + +LOGGER = get_adapter() + +DEFAULT_RETRY = Retry( + total=5, + backoff_factor=1, + status_forcelist=[302, 403, 429, 500, 502, 503, 504], + allowed_methods=["HEAD", "GET", "OPTIONS", "POST", "PUT", "DELETE"], + respect_retry_after_header=True, +) + +VERSION = read_text("pyatlan", "version.txt").strip() + + +def log_response(response, *args, **kwargs): + LOGGER.debug("HTTP Status: %s", response.status_code) + LOGGER.debug("URL: %s", response.request.url) + + +class AtlanClient(msgspec.Struct, kw_only=True): + """ + AtlanClient for Atlan's Atlas API using msgspec.Struct. + + Migrated from Pydantic BaseSettings to msgspec.Struct for consistency + with the v9 model layer. Configuration is read from constructor + arguments with environment variable fallbacks. + + Environment variables (with ATLAN_ prefix): + ATLAN_BASE_URL: Base URL for the Atlan service + ATLAN_API_KEY: API key for authentication + ATLAN_OAUTH_CLIENT_ID: OAuth client ID + ATLAN_OAUTH_CLIENT_SECRET: OAuth client secret + + Example:: + + client = AtlanClient( + base_url="https://myinstance.atlan.com", + api_key="my-api-key", + ) + # Or with environment variables: + client = AtlanClient() + """ + + # --- Configuration fields (user-facing) --- + base_url: Union[str, None] = None + api_key: Union[str, None] = None + oauth_client_id: Union[str, None] = None + oauth_client_secret: Union[str, None] = None + connect_timeout: float = 30.0 + read_timeout: float = 900.0 + retry: Any = None # Defaults to DEFAULT_RETRY in __post_init__ + proxy: Any = None # None = no proxy (may be overridden by env vars) + verify: Union[Any, UnsetType] = UNSET # UNSET = not provided → default True + + # --- Internal state (initialized in __post_init__) --- + _session: Any = None + _request_params: Any = None + _401_has_retried: Any = None + _user_id: Union[str, None] = None + _user_client: Any = None + _oauth_token_manager: Any = None + _clients: Any = None # Lazy dict of sub-clients + _caches: Any = None # Lazy dict of caches + + def __post_init__(self): + # Apply defaults + if self.retry is None: + self.retry = DEFAULT_RETRY + + # Track whether verify was explicitly provided before resolving + _verify_explicit = self.verify is not UNSET + if not _verify_explicit: + self.verify = True # Default + + # Read from environment variables (matching legacy BaseSettings behavior) + if self.base_url is None: + self.base_url = os.environ.get("ATLAN_BASE_URL", "INTERNAL") + if self.api_key is None: + self.api_key = os.environ.get("ATLAN_API_KEY") + if self.oauth_client_id is None: + self.oauth_client_id = os.environ.get("ATLAN_OAUTH_CLIENT_ID") + if self.oauth_client_secret is None: + self.oauth_client_secret = os.environ.get("ATLAN_OAUTH_CLIENT_SECRET") + + # Initialize internal state + self._401_has_retried = ContextVar("_401_has_retried", default=False) + self._clients = {} + self._caches = {} + + # Setup authentication + if self.oauth_client_id and self.oauth_client_secret and self.api_key is None: + LOGGER.debug("API KEY not provided. Using OAuth flow for authentication") + self._oauth_token_manager = OAuthTokenManager( + base_url=self.base_url, + client_id=self.oauth_client_id, + client_secret=self.oauth_client_secret, + connect_timeout=self.connect_timeout, + read_timeout=self.read_timeout, + ) + self._request_params = {"headers": {}} + else: + self._request_params = ( + {"headers": {"authorization": f"Bearer {self.api_key}"}} + if self.api_key and self.api_key.strip() + else {"headers": {}} + ) + + # Resolve proxy from environment variables if not explicitly provided + if self.proxy is None: + env_proxy = ( + os.environ.get("HTTPS_PROXY") + or os.environ.get("https_proxy") + or os.environ.get("HTTP_PROXY") + or os.environ.get("http_proxy") + ) + if env_proxy: + self.proxy = env_proxy + + # Resolve verify from environment variables if not explicitly provided + if not _verify_explicit: + ssl_cert_file = os.environ.get("SSL_CERT_FILE") or os.environ.get( + "REQUESTS_CA_BUNDLE" + ) + if ssl_cert_file: + self.verify = ssl_cert_file + + # Build transport kwargs + transport_kwargs: Dict[str, Any] = {} + if self.proxy is not None: + transport_kwargs["proxy"] = self.proxy + if _verify_explicit or self.verify is not True: + transport_kwargs["verify"] = self.verify + + # Create httpx session with custom transport + self._session = httpx.Client( + transport=PyatlanSyncTransport(retry=self.retry, **transport_kwargs), + headers={ + "x-atlan-agent": "sdk", + "x-atlan-agent-id": "python", + "x-atlan-client-origin": "product_sdk", + "x-atlan-python-version": get_python_version(), + "x-atlan-client-type": "sync", + "User-Agent": f"Atlan-PythonSDK/{VERSION}", + }, + event_hooks={"response": [log_response]}, + ) + self._401_has_retried.set(False) + + # --- Sub-client properties (lazy-initialized via _clients dict) --- + + def _get_client(self, key: str, factory): + if key not in self._clients: + self._clients[key] = factory(client=self) + return self._clients[key] + + def _get_cache(self, key: str, factory): + if key not in self._caches: + self._caches[key] = factory(client=self) + return self._caches[key] + + @property + def admin(self) -> V9AdminClient: + return self._get_client("admin", V9AdminClient) + + @property + def audit(self) -> V9AuditClient: + return self._get_client("audit", V9AuditClient) + + @property + def search_log(self) -> V9SearchLogClient: + return self._get_client("search_log", V9SearchLogClient) + + @property + def workflow(self) -> V9WorkflowClient: + return self._get_client("workflow", V9WorkflowClient) + + @property + def credentials(self) -> V9CredentialClient: + return self._get_client("credentials", V9CredentialClient) + + @property + def group(self) -> V9GroupClient: + return self._get_client("group", V9GroupClient) + + @property + def role(self) -> V9RoleClient: + return self._get_client("role", V9RoleClient) + + @property + def asset(self) -> V9AssetClient: + return self._get_client("asset", V9AssetClient) + + @property + def impersonate(self) -> V9ImpersonationClient: + return self._get_client("impersonate", V9ImpersonationClient) + + @property + def queries(self) -> V9QueryClient: + return self._get_client("queries", V9QueryClient) + + @property + def token(self) -> V9TokenClient: + return self._get_client("token", V9TokenClient) + + @property + def oauth_client(self) -> V9OAuthClient: + return self._get_client("oauth_client", V9OAuthClient) + + @property + def typedef(self) -> V9TypeDefClient: + return self._get_client("typedef", V9TypeDefClient) + + @property + def user(self) -> V9UserClient: + return self._get_client("user", V9UserClient) + + @property + def tasks(self) -> V9TaskClient: + return self._get_client("tasks", V9TaskClient) + + @property + def sso(self) -> V9SSOClient: + return self._get_client("sso", V9SSOClient) + + @property + def open_lineage(self) -> V9OpenLineageClient: + return self._get_client("open_lineage", V9OpenLineageClient) + + @property + def files(self) -> V9FileClient: + return self._get_client("files", V9FileClient) + + @property + def contracts(self) -> V9ContractClient: + return self._get_client("contracts", V9ContractClient) + + # --- Cache properties --- + + @property + def atlan_tag_cache(self) -> AtlanTagCache: + return self._get_cache("atlan_tag", AtlanTagCache) + + @property + def enum_cache(self) -> EnumCache: + return self._get_cache("enum", EnumCache) + + @property + def group_cache(self) -> GroupCache: + return self._get_cache("group", GroupCache) + + @property + def role_cache(self) -> RoleCache: + return self._get_cache("role", RoleCache) + + @property + def user_cache(self) -> UserCache: + return self._get_cache("user", UserCache) + + @property + def custom_metadata_cache(self) -> CustomMetadataCache: + return self._get_cache("custom_metadata", CustomMetadataCache) + + @property + def connection_cache(self) -> ConnectionCache: + return self._get_cache("connection", ConnectionCache) + + @property + def source_tag_cache(self) -> SourceTagCache: + return self._get_cache("source_tag", SourceTagCache) + + @property + def dq_template_config_cache(self) -> DQTemplateConfigCache: + return self._get_cache("dq_template_config", DQTemplateConfigCache) + + # --- Class methods --- + + @classmethod + def from_token_guid( + cls, + guid: str, + base_url: Optional[str] = None, + client_id: Optional[str] = None, + client_secret: Optional[str] = None, + ) -> "AtlanClient": + from pyatlan.client.common.impersonate import ImpersonateUser + from pyatlan.client.constants import GET_TOKEN + from pyatlan.model.response import AccessTokenResponse + + final_base_url = base_url or os.environ.get("ATLAN_BASE_URL", "INTERNAL") + client = cls(base_url=final_base_url, api_key="") + client_info = ImpersonateUser.get_client_info( + client_id=client_id, client_secret=client_secret + ) + argo_credentials = { + "grant_type": "client_credentials", + "client_id": client_info.client_id, + "client_secret": client_info.client_secret, + "scope": "openid", + } + from pyatlan.errors import AtlanError, ErrorCode + + try: + raw_json = client._call_api(GET_TOKEN, request_obj=argo_credentials) + argo_token = AccessTokenResponse(**raw_json).access_token + temp_argo_client = cls(base_url=final_base_url, api_key=argo_token) + except AtlanError as atlan_err: + raise ErrorCode.UNABLE_TO_ESCALATE_WITH_PARAM.exception_with_parameters( + "Failed to obtain Atlan-Argo token" + ) from atlan_err + token_secret = temp_argo_client.impersonate.get_client_secret(client_guid=guid) + token_client_id = temp_argo_client.token.get_by_guid(guid=guid).client_id + token_credentials = { + "grant_type": "client_credentials", + "client_id": token_client_id, + "client_secret": token_secret, + "scope": "openid", + } + try: + raw_json = client._call_api(GET_TOKEN, request_obj=token_credentials) + token_api_key = AccessTokenResponse(**raw_json).access_token + return cls(base_url=final_base_url, api_key=token_api_key) + except AtlanError as atlan_err: + raise ErrorCode.UNABLE_TO_ESCALATE_WITH_PARAM.exception_with_parameters( + "Failed to obtain access token for API token" + ) from atlan_err + + # --- Core API methods --- + + def update_headers(self, header: Dict[str, str]): + self._session.headers.update(header) + + def _handle_file_download(self, raw_response: Any, file_path: str) -> str: + try: + with open(file_path, "wb") as download_file: + for chunk in raw_response: + download_file.write(chunk) + except Exception as err: + raise ErrorCode.UNABLE_TO_DOWNLOAD_FILE.exception_with_parameters( + str((hasattr(err, "strerror") and err.strerror) or err), file_path + ) + return file_path + + def _call_api_internal( + self, + api, + path, + params, + binary_data=None, + download_file_path=None, + text_response=False, + ): + token = request_id_var.set(str(uuid.uuid4())) + try: + params["headers"]["X-Atlan-Request-Id"] = request_id_var.get() + timeout = httpx.Timeout( + None, connect=self.connect_timeout, read=self.read_timeout + ) + if binary_data: + response = self._session.request( + api.method.value, + path, + data=binary_data, + **params, + timeout=timeout, + ) + elif api.consumes == EVENT_STREAM and api.produces == EVENT_STREAM: + with self._session.stream( + api.method.value, + path, + **params, + timeout=timeout, + ) as stream_response: + if download_file_path: + return self._handle_file_download( + stream_response.iter_raw(), download_file_path + ) + + content = stream_response.read() + text = content.decode("utf-8") if content else "" + lines = [] + + if stream_response.status_code == api.expected_status: + lines = text.splitlines() if text else [] + + response_data = { + "status_code": stream_response.status_code, + "headers": stream_response.headers, + "text": text, + "content": content, + "lines": lines, + } + + response = SimpleNamespace( + status_code=response_data["status_code"], + headers=response_data["headers"], + text=response_data["text"], + content=response_data["content"], + _stream_lines=response_data["lines"], + json=lambda: json.loads(response_data["text"]) + if response_data["text"] + else {}, + ) + else: + response = self._session.request( + api.method.value, + path, + **params, + timeout=timeout, + ) + if response is not None: + LOGGER.debug("HTTP Status: %s", response.status_code) + if response is None: + return None + + if ( + self._401_has_retried.get() + and response.status_code + != ErrorCode.AUTHENTICATION_PASSTHROUGH.http_error_code + ): + self._401_has_retried.set(False) + + if response.status_code == api.expected_status: + try: + if ( + response.content is None + or response.content == "null" + or len(response.content) == 0 + or response.status_code == HTTPStatus.NO_CONTENT + ): + return None + events = [] + if LOGGER.isEnabledFor(logging.DEBUG): + LOGGER.debug( + "<== __call_api(%s,%s), result = %s", + vars(api), + params, + response, + ) + if api.consumes == EVENT_STREAM and api.produces == EVENT_STREAM: + if hasattr(response, "_stream_lines"): + for line in response._stream_lines: + if not line: + continue + if not line.startswith("data: "): + raise ErrorCode.UNABLE_TO_DESERIALIZE.exception_with_parameters( + line + ) + events.append(json.loads(line.split("data: ")[1])) + if text_response: + response_ = response.text + else: + response_ = ( + events + if events + else AtlanResponse( + raw_json=response.json(), client=self + ).to_dict() + ) + LOGGER.debug("response: %s", response_) + return response_ + except (json.decoder.JSONDecodeError,) as e: + raise ErrorCode.JSON_ERROR.exception_with_parameters( + response.text, response.status_code, str(e) + ) from e + elif response.status_code == HTTPStatus.SERVICE_UNAVAILABLE: + LOGGER.error( + "Atlas Service unavailable. HTTP Status: %s", + HTTPStatus.SERVICE_UNAVAILABLE, + ) + return None + else: + with contextlib.suppress(ValueError, json.decoder.JSONDecodeError): + error_info = json.loads(response.text) + error_code = ( + error_info.get("errorCode", 0) + or error_info.get("code", 0) + or error_info.get("status") + ) + error_message = error_info.get( + "errorMessage", "" + ) or error_info.get("message", "") + error_doc = ( + error_info.get("doc") + or error_info.get("errorDoc") + or error_info.get("errorDocument") + or error_info.get("errorDocumentation") + ) + error_cause = error_info.get("errorCause", []) + causes = error_info.get("causes", []) + backend_error_id = error_info.get("errorId") + + error_cause_details = [ + f"ErrorType: {cause.get('errorType', 'Unknown')}, " + f"Message: {cause.get('errorMessage', 'No additional information provided')}, " + f"Location: {cause.get('location', 'Unknown location')}" + for cause in causes + ] + error_cause_details_str = ( + "\n".join(error_cause_details) if error_cause_details else "" + ) + + if ( + (self._user_id or self._oauth_token_manager) + and not self._401_has_retried.get() + and response.status_code + == ErrorCode.AUTHENTICATION_PASSTHROUGH.http_error_code + ): + try: + LOGGER.debug("Starting 401 automatic token refresh.") + return self._handle_401_token_refresh( + api, + path, + params, + binary_data=binary_data, + download_file_path=download_file_path, + text_response=text_response, + ) + except Exception as e: + LOGGER.debug( + "API call failed after a successful 401 token refresh. Error details: %s", + e, + ) + raise + + if error_code and error_message: + error = ERROR_CODE_FOR_HTTP_STATUS.get( + response.status_code, ErrorCode.ERROR_PASSTHROUGH + ) + raise error.exception_with_parameters( + error_code, + error_message, + error_cause_details_str, + error_cause=error_cause, + backend_error_id=backend_error_id, + error_doc=error_doc, + ) + raise AtlanError( + SimpleNamespace( + http_error_code=response.status_code, + error_id=f"ATLAN-PYTHON-{response.status_code}-000", + error_message=response.text, + user_action=ErrorCode.ERROR_PASSTHROUGH.user_action, + ) + ) + finally: + request_id_var.reset(token) + + def _api_logger(self, api: API, path: str): + LOGGER.debug("------------------------------------------------------") + LOGGER.debug("Call : %s %s", api.method, path) + LOGGER.debug("Content-type_ : %s", api.consumes) + LOGGER.debug("Accept : %s", api.produces) + LOGGER.debug("Client-Type : %s", "SYNC") + LOGGER.debug("Python-Version: %s", get_python_version()) + LOGGER.debug("User-Agent : %s", f"Atlan-PythonSDK/{VERSION}") + + def _call_api( + self, + api, + query_params=None, + request_obj=None, + text_response=False, + ): + path = self._create_path(api) + params = self._create_params(api, query_params, request_obj) + if LOGGER.isEnabledFor(logging.DEBUG): + self._api_logger(api, path) + return self._call_api_internal(api, path, params, text_response=text_response) + + def _create_path(self, api: API): + if self.base_url == "INTERNAL": + return urljoin(api.endpoint.service, api.path) + else: + return urljoin(urljoin(self.base_url, api.endpoint.prefix), api.path) + + def _upload_file(self, api, file=None, filename=None): + generator = MultipartDataGenerator() + generator.add_file(file=file, filename=filename) + post_data = generator.get_post_data() + api.produces = f"multipart/form-data; boundary={generator.boundary}" + path = self._create_path(api) + params = self._create_params(api, query_params=None, request_obj=None) + if LOGGER.isEnabledFor(logging.DEBUG): + self._api_logger(api, path) + return self._call_api_internal(api, path, params, binary_data=post_data) + + def _s3_presigned_url_file_upload(self, api: API, upload_file: Any): + path = self._create_path(api) + params = copy.deepcopy(self._request_params) + params["headers"].pop("authorization", None) + return self._call_api_internal(api, path, params, binary_data=upload_file) + + def _azure_blob_presigned_url_file_upload(self, api: API, upload_file: Any): + path = self._create_path(api) + params = copy.deepcopy(self._request_params) + params["headers"].pop("authorization", None) + params["headers"]["x-ms-blob-type"] = "BlockBlob" + return self._call_api_internal(api, path, params, binary_data=upload_file) + + def _gcs_presigned_url_file_upload(self, api: API, upload_file: Any): + path = self._create_path(api) + params = copy.deepcopy(self._request_params) + params["headers"].pop("authorization", None) + return self._call_api_internal(api, path, params, binary_data=upload_file) + + def _presigned_url_file_download(self, api: API, file_path: str): + path = self._create_path(api) + params = copy.deepcopy(self._request_params) + params["headers"].pop("authorization", None) + return self._call_api_internal(api, path, params, download_file_path=file_path) + + def _create_params(self, api: API, query_params, request_obj): + params = copy.deepcopy(self._request_params) + if self._oauth_token_manager: + token = self._oauth_token_manager.get_token() + params["headers"]["authorization"] = f"Bearer {token}" + params["headers"]["Accept"] = api.consumes + params["headers"]["content-type"] = api.produces + if query_params is not None: + params["params"] = query_params + if request_obj is not None: + if api.consumes == APPLICATION_ENCODED_FORM: + params["data"] = request_obj + elif isinstance(request_obj, LegacyAtlanObject): + # Use legacy serialization so request body matches legacy client exactly + params["data"] = LegacyAtlanRequest( + instance=request_obj, client=self + ).json() + elif hasattr(request_obj, "json") and callable(request_obj.json): + # Prefer json() method if available (handles nested serialization properly) + params["data"] = request_obj.json(by_alias=True, exclude_none=True) + elif hasattr(request_obj, "to_dict") and callable(request_obj.to_dict): + params["data"] = json.dumps(request_obj.to_dict()) + elif isinstance(request_obj, (dict, list)): + params["data"] = json.dumps(request_obj) + elif isinstance(request_obj, msgspec.Struct): + params["data"] = AtlanRequest(instance=request_obj, client=self).json() + elif hasattr(request_obj, "to_json") and callable(request_obj.to_json): + params["data"] = AtlanRequest(instance=request_obj, client=self).json() + elif hasattr(request_obj, "dict") and callable(request_obj.dict): + # Pydantic v1 models (old pyatlan models) + params["data"] = json.dumps( + request_obj.dict(by_alias=True, exclude_none=True) + ) + elif hasattr(request_obj, "model_dump") and callable( + request_obj.model_dump + ): + # Pydantic v2 models + params["data"] = json.dumps( + request_obj.model_dump(by_alias=True, exclude_none=True) + ) + elif hasattr(request_obj, "__root__"): + params["data"] = json.dumps(request_obj.__root__) + else: + params["data"] = json.dumps(request_obj) + return params + + def _handle_401_token_refresh( + self, + api, + path, + params, + binary_data=None, + download_file_path=None, + text_response=False, + ): + """Handle token refresh and retry the API request upon a 401 Unauthorized.""" + if self._oauth_token_manager: + self._oauth_token_manager.invalidate_token() + token = self._oauth_token_manager.get_token() + params["headers"]["authorization"] = f"Bearer {token}" + self._401_has_retried.set(True) + LOGGER.debug("Successfully refreshed OAuth token after 401.") + return self._call_api_internal( + api, + path, + params, + binary_data=binary_data, + download_file_path=download_file_path, + text_response=text_response, + ) + + try: + new_token = self.impersonate.user(user_id=self._user_id) + except Exception as e: + LOGGER.debug( + "Failed to impersonate user %s for 401 token refresh. Not retrying. Error: %s", + self._user_id, + e, + ) + raise + self.api_key = new_token + self._401_has_retried.set(True) + params["headers"]["authorization"] = f"Bearer {self.api_key}" + self._request_params["headers"]["authorization"] = f"Bearer {self.api_key}" + LOGGER.debug("Successfully completed 401 automatic token refresh.") + + import time + + retry_count = 1 + while retry_count <= self.retry.total: + try: + response = self.typedef.get(type_category=[AtlanTypeCategory.STRUCT]) + if response and response.struct_defs: + break + except Exception as e: + LOGGER.debug( + "Retrying to get typedefs (to ensure token is active) after token refresh failed: %s", + e, + ) + time.sleep(retry_count) + retry_count += 1 + + return self._call_api_internal( + api, + path, + params, + binary_data=binary_data, + download_file_path=download_file_path, + text_response=text_response, + ) + + def upload_image(self, file, filename: str) -> AtlanImage: + """ + Uploads an image from the provided local file. + + :param file: local file to upload + :param filename: name of the file to be uploaded + :returns: details of the uploaded image + :raises AtlanError: on any API communication issue + """ + raw_json = self._upload_file(UPLOAD_IMAGE, file=file, filename=filename) + return msgspec.convert(raw_json, AtlanImage, strict=False) + + def search(self, criteria): + """Search assets. Delegates to asset.search().""" + from warnings import warn + + warn( + "This method is deprecated, please use 'asset.search' instead, which offers identical functionality.", + DeprecationWarning, + stacklevel=2, + ) + return self.asset.search(criteria=criteria) + + def parse_query(self, query: QueryParserRequest) -> Optional[ParsedQuery]: + """ + Parses the provided query to describe its component parts. + + :param query: query to parse and configuration options + :returns: parsed explanation of the query + :raises AtlanError: on any API communication issue + """ + raw_json = self._call_api( + PARSE_QUERY, + request_obj=query, + ) + return msgspec.convert(raw_json, ParsedQuery, strict=False) + + def save( + self, + entity: Union["Asset", List["Asset"]], + replace_atlan_tags: bool = False, + replace_custom_metadata: bool = False, + overwrite_custom_metadata: bool = False, + append_atlan_tags: bool = False, + ) -> "AssetMutationResponse": + """ + Convenience method that delegates to asset.save(). + If an asset with the same qualified_name exists, updates the existing asset. Otherwise, creates the asset. + + :param entity: one or more assets to save + :param replace_atlan_tags: whether to replace AtlanTags during an update (True) or not (False) + :param replace_custom_metadata: replaces any custom metadata with non-empty values provided + :param overwrite_custom_metadata: overwrites any custom metadata, even with empty values + :param append_atlan_tags: whether to add/update/remove AtlanTags during an update (True) or not (False) + :returns: the result of the save + :raises AtlanError: on any API communication issue + """ + return self.asset.save( + entity=entity, + replace_atlan_tags=replace_atlan_tags, + replace_custom_metadata=replace_custom_metadata, + overwrite_custom_metadata=overwrite_custom_metadata, + append_atlan_tags=append_atlan_tags, + ) + + @contextlib.contextmanager + def max_retries( + self, max_retries: Retry = CONNECTION_RETRY + ) -> _GeneratorContextManager[None]: + """ + Creates a context manager that temporarily changes retry parameters. + + The original Retry information will be restored when the context is exited. + """ + current_transport = self._session._transport + + transport_kwargs: Dict[str, Any] = {} + if self.proxy: + transport_kwargs["proxy"] = self.proxy + if self.verify is not None: + transport_kwargs["verify"] = self.verify + + new_transport = PyatlanSyncTransport(retry=max_retries, **transport_kwargs) + self._session._transport = new_transport + + LOGGER.debug( + "max_retries set to total: %s force_list: %s", + max_retries.total, + max_retries.status_forcelist, + ) + try: + LOGGER.debug("Entering max_retries") + yield None # type: ignore[misc] + LOGGER.debug("Exiting max_retries") + except httpx.TransportError as err: + LOGGER.exception("Exception in max retries") + raise ErrorCode.RETRY_OVERRUN.exception_with_parameters() from err + finally: + self._session._transport = current_transport + LOGGER.debug("max_retries restored %s", self._session._transport.retry) # type: ignore[attr-defined] + + +@contextlib.contextmanager +def client_connection( + client: AtlanClient, + base_url: Optional[str] = None, + api_key: Optional[str] = None, + connect_timeout: float = 30.0, + read_timeout: float = 120.0, + retry: Retry = DEFAULT_RETRY, +) -> Generator[AtlanClient, None, None]: + """ + Creates a new client with the given base_url and/or api_key. + + :param client: existing client to clone settings from + :param base_url: the base_url for the new connection (uses current if not specified) + :param api_key: the api_key for the new connection (uses current if not specified) + """ + tmp_client = AtlanClient( + base_url=base_url or client.base_url, + api_key=api_key or client.api_key, + connect_timeout=connect_timeout, + read_timeout=read_timeout, + retry=retry, + ) + yield tmp_client diff --git a/pyatlan_v9/client/audit.py b/pyatlan_v9/client/audit.py new file mode 100644 index 000000000..808a7068c --- /dev/null +++ b/pyatlan_v9/client/audit.py @@ -0,0 +1,96 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +from __future__ import annotations + +from typing import List + +import msgspec + +from pyatlan.client.common import ApiCaller, AuditSearch +from pyatlan.errors import ErrorCode +from pyatlan_v9.model.audit import AuditSearchRequest, AuditSearchResults, EntityAudit +from pyatlan_v9.validate import validate_arguments + +ENTITY_AUDITS = "entityAudits" +_MS_TIMESTAMP_THRESHOLD = 1e12 +_AUDIT_TS_FIELDS = ("timestamp", "created") + + +def _normalize_ms_timestamps(record: dict, fields: tuple) -> dict: + """Return a shallow copy with millisecond epoch timestamps converted to seconds.""" + out = dict(record) + for field in fields: + val = out.get(field) + if isinstance(val, (int, float)) and val > _MS_TIMESTAMP_THRESHOLD: + out[field] = val / 1000 + return out + + +def _parse_entity_audits(raw_json: dict) -> List[EntityAudit]: + """Parse entity audits from raw JSON response using msgspec.""" + if ENTITY_AUDITS in raw_json and raw_json[ENTITY_AUDITS]: + audits = [ + _normalize_ms_timestamps(a, _AUDIT_TS_FIELDS) + for a in raw_json[ENTITY_AUDITS] + ] + return msgspec.convert(audits, list[EntityAudit], strict=False) + return [] + + +class V9AuditClient: + """ + This class can be used to configure and run a search against Atlan's activity log. + This class does not need to be instantiated directly but can be obtained + through the audit property of AtlanClient. + """ + + def __init__(self, client: ApiCaller): + if not isinstance(client, ApiCaller): + raise ErrorCode.INVALID_PARAMETER_TYPE.exception_with_parameters( + "client", "ApiCaller" + ) + self._client = client + + @validate_arguments + def search(self, criteria: AuditSearchRequest, bulk=False) -> AuditSearchResults: + """ + Search for assets using the provided criteria. + `Note:` if the number of results exceeds the predefined threshold + (10,000 assets) this will be automatically converted into an audit `bulk` search. + + :param criteria: detailing the search query, parameters, and so on to run + :param bulk: whether to run the search to retrieve assets that match the supplied criteria, + for large numbers of results (> `10,000`), defaults to `False`. Note: this will reorder the results + (based on creation timestamp) in order to iterate through a large number (more than `10,000`) results. + :raises InvalidRequestError: + + - if audit bulk search is enabled (`bulk=True`) and any + user-specified sorting options are found in the search request. + - if audit bulk search is disabled (`bulk=False`) and the number of results + exceeds the predefined threshold (i.e: `10,000` assets) + and any user-specified sorting options are found in the search request. + + :raises AtlanError: on any API communication issue + :returns: the results of the search + """ + endpoint, request_obj = AuditSearch.prepare_request(criteria, bulk) + raw_json = self._client._call_api(endpoint, request_obj=request_obj) + + entity_audits = _parse_entity_audits(raw_json) + count = raw_json.get("totalCount", 0) + aggregations = raw_json.get("aggregations") + + if AuditSearch.check_for_bulk_search(count, criteria, bulk, AuditSearchResults): + return self.search(criteria) + + return AuditSearchResults( + client=self._client, + criteria=criteria, + start=criteria.dsl.from_, + size=criteria.dsl.size, + entity_audits=entity_audits, + count=count, + bulk=bulk, + aggregations=aggregations, + ) diff --git a/pyatlan_v9/client/contract.py b/pyatlan_v9/client/contract.py new file mode 100644 index 000000000..4676f126a --- /dev/null +++ b/pyatlan_v9/client/contract.py @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. +from typing import Optional + +from pyatlan.client.common import ApiCaller +from pyatlan.client.constants import CONTRACT_INIT_API +from pyatlan.errors import ErrorCode +from pyatlan_v9.model.assets import Asset +from pyatlan_v9.model.contract import InitRequest +from pyatlan_v9.validate import validate_arguments + + +class V9ContractClient: + """ + A client for data contract-specific operations. + """ + + def __init__(self, client: ApiCaller): + if not isinstance(client, ApiCaller): + raise ErrorCode.INVALID_PARAMETER_TYPE.exception_with_parameters( + "client", "ApiCaller" + ) + self._client = client + + @validate_arguments + def generate_initial_spec( + self, + asset: Asset, + ) -> Optional[str]: + """ + Generate an initial contract spec for the provided asset. + The asset must have at least its `qualifiedName` (and `typeName`) populated. + + :param asset: for which to generate the initial contract spec + + :raises AtlanError: if there is an issue interacting with the API + :returns: YAML for the initial contract spec for the provided asset + """ + request_obj = InitRequest( + asset_type=asset.type_name, + asset_qualified_name=asset.qualified_name, + ) + response = self._client._call_api(CONTRACT_INIT_API, request_obj=request_obj) + return response.get("contract") diff --git a/pyatlan_v9/client/credential.py b/pyatlan_v9/client/credential.py new file mode 100644 index 000000000..e2782726c --- /dev/null +++ b/pyatlan_v9/client/credential.py @@ -0,0 +1,158 @@ +from __future__ import annotations + +from typing import Any, Dict, Optional + +import msgspec + +from pyatlan.client.common import ( + ApiCaller, + CredentialCreate, + CredentialGet, + CredentialGetAll, + CredentialPurge, + CredentialTestAndUpdate, +) +from pyatlan.client.constants import TEST_CREDENTIAL +from pyatlan.errors import ErrorCode +from pyatlan_v9.model.credential import ( + Credential, + CredentialListResponse, + CredentialResponse, + CredentialTestResponse, +) +from pyatlan_v9.validate import validate_arguments + + +class V9CredentialClient: + """ + A client for managing credentials within the Atlan platform. + + This class provides functionality for interacting with Atlan's credential objects. + It allows you to perform operations such as retrieving, testing, and updating given credentials. + """ + + def __init__(self, client: ApiCaller): + if not isinstance(client, ApiCaller): + raise ErrorCode.INVALID_PARAMETER_TYPE.exception_with_parameters( + "client", "ApiCaller" + ) + self._client = client + + @validate_arguments + def creator(self, credential: Credential, test: bool = True) -> CredentialResponse: + """ + Create a new credential. + + :param credential: provide full details of the credential's to be created. + :param test: whether to validate the credentials (`True`) or skip validation + (`False`) before creation, defaults to `True`. + :returns: A CredentialResponse instance. + :raises ValidationError: If the provided `credential` is invalid. + :raises InvalidRequestError: If `test` is `False` and the credential contains a `username` or `password`. + """ + CredentialCreate.validate_request(credential, test) + endpoint, query_params = CredentialCreate.prepare_request(test) + raw_json = self._client._call_api( + api=endpoint, + query_params=query_params, + request_obj=credential, + ) + return msgspec.convert(raw_json, CredentialResponse, strict=False) + + @validate_arguments + def get(self, guid: str) -> CredentialResponse: + """ + Retrieves a credential by its unique identifier (GUID). + Note that this will never contain sensitive information + in the credential, such as usernames, passwords or client secrets or keys. + + :param guid: GUID of the credential. + :returns: A CredentialResponse instance. + :raises: AtlanError on any error during API invocation. + """ + endpoint = CredentialGet.prepare_request(guid) + raw_json = self._client._call_api(endpoint) + if not isinstance(raw_json, dict): + return raw_json + return msgspec.convert(raw_json, CredentialResponse, strict=False) + + @validate_arguments + def get_all( + self, + filter: Optional[Dict[str, Any]] = None, + limit: Optional[int] = None, + offset: Optional[int] = None, + workflow_name: Optional[str] = None, + ) -> CredentialListResponse: + """ + Retrieves all credentials. + + :param filter: (optional) dictionary specifying the filter criteria. + :param limit: (optional) maximum number of credentials to retrieve. + :param offset: (optional) number of credentials to skip before starting retrieval. + :param workflow_name: (optional) name of the workflow to retrieve credentials for. + :returns: CredentialListResponse instance. + :raises: AtlanError on any error during API invocation. + """ + endpoint, params = CredentialGetAll.prepare_request( + filter, limit, offset, workflow_name + ) + raw_json = self._client._call_api(endpoint, query_params=params) + if not isinstance(raw_json, dict) or "records" not in raw_json: + raise ErrorCode.JSON_ERROR.exception_with_parameters( + "No records found in response", + 400, + "API response did not contain the expected 'records' key", + ) + if raw_json.get("records") is None: + raw_json["records"] = [] + return msgspec.convert(raw_json, CredentialListResponse, strict=False) + + @validate_arguments + def purge_by_guid(self, guid: str) -> CredentialResponse: + """ + Hard-deletes (purges) credential by their unique identifier (GUID). + This operation is irreversible. + + :param guid: unique identifier(s) (GUIDs) of credential to hard-delete + :returns: details of the hard-deleted asset(s) + :raises AtlanError: on any API communication issue + """ + endpoint = CredentialPurge.prepare_request(guid) + raw_json = self._client._call_api(endpoint) + return raw_json + + @validate_arguments + def test(self, credential: Credential) -> CredentialTestResponse: + """ + Tests the given credential by sending it to Atlan for validation. + + :param credential: The credential to be tested. + :type credential: A CredentialTestResponse instance. + :returns: The response indicating the test result. + :raises ValidationError: If the provided credential is invalid type. + :raises AtlanError: On any error during API invocation. + """ + raw_json = self._client._call_api(TEST_CREDENTIAL, request_obj=credential) + return msgspec.convert(raw_json, CredentialTestResponse, strict=False) + + @validate_arguments + def test_and_update(self, credential: Credential) -> CredentialResponse: + """ + Updates this credential in Atlan after first + testing it to confirm its successful validation. + + :param credential: The credential to be tested and updated. + :returns: An updated CredentialResponse instance. + :raises ValidationError: If the provided credential is invalid type. + :raises InvalidRequestException: if the provided credentials + cannot be validated successfully. + :raises InvalidRequestException: If the provided credential + does not have an ID. + :raises AtlanError: on any error during API invocation. + """ + test_response = self.test(credential=credential) + CredentialTestAndUpdate.validate_test_response(test_response, credential) + endpoint = CredentialTestAndUpdate.prepare_request(credential) + raw_json = self._client._call_api(endpoint, request_obj=credential) + return msgspec.convert(raw_json, CredentialResponse, strict=False) diff --git a/pyatlan_v9/client/file.py b/pyatlan_v9/client/file.py new file mode 100644 index 000000000..1a84995ab --- /dev/null +++ b/pyatlan_v9/client/file.py @@ -0,0 +1,85 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +from __future__ import annotations + +from pyatlan.client.common import ApiCaller, FileDownload, FilePresignedUrl, FileUpload +from pyatlan.errors import ErrorCode +from pyatlan_v9.model.file import PresignedURLRequest +from pyatlan_v9.validate import validate_arguments + + +class V9FileClient: + """ + A client for operating on Atlan's tenant object storage. + """ + + def __init__(self, client: ApiCaller): + if not isinstance(client, ApiCaller): + raise ErrorCode.INVALID_PARAMETER_TYPE.exception_with_parameters( + "client", "ApiCaller" + ) + self._client = client + + @validate_arguments + def generate_presigned_url(self, request: PresignedURLRequest) -> str: + """ + Generates a presigned URL based on Atlan's tenant object store. + + :param request: instance containing object key, + expiry, and method (PUT: upload, GET: download). + :raises AtlanError: on any error during API invocation. + :returns: a response object containing a presigned URL with its cloud provider. + """ + endpoint, request_obj = FilePresignedUrl.prepare_request(request) + raw_json = self._client._call_api(endpoint, request_obj=request_obj) + return FilePresignedUrl.process_response(raw_json) + + @validate_arguments + def upload_file(self, presigned_url: str, file_path: str) -> None: + """ + Uploads a file to Atlan's object storage. + + :param presigned_url: any valid presigned URL. + :param file_path: path to the file to be uploaded. + :raises AtlanError: on any error during API invocation. + :raises InvalidRequestException: if the upload file path is invalid, + or when the presigned URL cloud provider is unsupported. + """ + upload_file = FileUpload.validate_file_path(file_path) + provider = FileUpload.identify_cloud_provider(presigned_url) + if provider == "s3": + endpoint = FileUpload.prepare_s3_request(presigned_url) + return self._client._s3_presigned_url_file_upload( + upload_file=upload_file, api=endpoint + ) + elif provider == "azure_blob": + endpoint = FileUpload.prepare_azure_request(presigned_url) + return self._client._azure_blob_presigned_url_file_upload( + upload_file=upload_file, api=endpoint + ) + elif provider == "gcs": + endpoint = FileUpload.prepare_gcs_request(presigned_url) + return self._client._gcs_presigned_url_file_upload( + upload_file=upload_file, api=endpoint + ) + + @validate_arguments + def download_file( + self, + presigned_url: str, + file_path: str, + ) -> str: + """ + Downloads a file from Atlan's tenant object storage. + + :param presigned_url: any valid presigned URL. + :param file_path: path to the file where you want to download the file. + :raises InvalidRequestException: if unable to download the file. + :raises AtlanError: on any error during API invocation. + :returns: full path to the downloaded file. + """ + endpoint = FileDownload.prepare_request(presigned_url) + return self._client._presigned_url_file_download( + file_path=file_path, api=endpoint + ) diff --git a/pyatlan_v9/client/group.py b/pyatlan_v9/client/group.py new file mode 100644 index 000000000..2e2061e8a --- /dev/null +++ b/pyatlan_v9/client/group.py @@ -0,0 +1,220 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. +from __future__ import annotations + +from typing import List, Optional + +import msgspec + +from pyatlan.client.common import ApiCaller +from pyatlan.client.constants import ( + CREATE_GROUP, + DELETE_GROUP, + GET_GROUP_MEMBERS, + GET_GROUPS, + REMOVE_USERS_FROM_GROUP, + UPDATE_GROUP, +) +from pyatlan.errors import ErrorCode +from pyatlan_v9.model.group import ( + AtlanGroup, + CreateGroupRequest, + CreateGroupResponse, + GroupRequest, + GroupResponse, + RemoveFromGroupRequest, +) +from pyatlan_v9.model.user import AtlanUser, UserRequest, UserResponse +from pyatlan_v9.validate import validate_arguments + + +class V9GroupClient: + """ + This class can be used to retrieve information about groups. This class does not need to be instantiated + directly but can be obtained through the group property of AtlanClient. + """ + + def __init__(self, client: ApiCaller): + if not isinstance(client, ApiCaller): + raise ErrorCode.INVALID_PARAMETER_TYPE.exception_with_parameters( + "client", "ApiCaller" + ) + self._client = client + + @validate_arguments + def creator( + self, + group: AtlanGroup, + user_ids: Optional[List[str]] = None, + ) -> CreateGroupResponse: + """ + Create a new group. + + :param group: details of the new group + :param user_ids: list of unique identifiers (GUIDs) of users to associate with the group + :returns: details of the created group and user association + :raises AtlanError: on any API communication issue + """ + payload = CreateGroupRequest(group=group) + if user_ids: + payload.users = user_ids + raw_json = self._client._call_api(CREATE_GROUP, request_obj=payload) + return msgspec.convert(raw_json, CreateGroupResponse, strict=False) + + @validate_arguments + def updater(self, group: AtlanGroup) -> None: + """ + Update a group. Note that the provided 'group' must have its id populated. + + :param group: details to update on the group + :raises AtlanError: on any API communication issue + """ + endpoint = UPDATE_GROUP.format_path_with_params(group.id) + self._client._call_api(endpoint, request_obj=group) + + @validate_arguments + def purge(self, guid: str) -> None: + """ + Delete a group. + + :param guid: unique identifier (GUID) of the group to delete + :raises AtlanError: on any API communication issue + """ + endpoint = DELETE_GROUP.format_path({"group_guid": guid}) + self._client._call_api(endpoint) + + @validate_arguments + def get( + self, + limit: Optional[int] = 20, + post_filter: Optional[str] = None, + sort: Optional[str] = None, + count: bool = True, + offset: int = 0, + columns: Optional[List[str]] = None, + ) -> GroupResponse: + """ + Retrieves a GroupResponse object which contains a list of the groups defined in Atlan. + + :param limit: maximum number of results to be returned + :param post_filter: which groups to retrieve + :param sort: property by which to sort the results + :param count: whether to return the total number of records (True) or not (False) + :param offset: starting point for results to return, for paging + :param columns: provides columns projection support for groups endpoint + :returns: a GroupResponse object which contains a list of groups that match the provided criteria + :raises AtlanError: on any API communication issue + """ + request = GroupRequest( + post_filter=post_filter, + limit=limit, + sort=sort, + count=count, + offset=offset, + columns=columns, + ) + endpoint = GET_GROUPS.format_path_with_params() + raw_json = self._client._call_api( + api=endpoint, query_params=request.query_params + ) + records: list[AtlanGroup] = [] + if raw_records := raw_json.get("records"): + records = msgspec.convert(raw_records, list[AtlanGroup], strict=False) + response = GroupResponse( + total_record=raw_json.get("totalRecord"), + filter_record=raw_json.get("filterRecord"), + records=records, + ) + response._size = limit or 20 + response._start = offset + response._endpoint = GET_GROUPS + response._client = self._client + response._criteria = request + return response + + @validate_arguments + def get_all( + self, + limit: int = 20, + offset: int = 0, + sort: Optional[str] = "name", + columns: Optional[List[str]] = None, + ) -> GroupResponse: + """ + Retrieve a GroupResponse object containing a list of all groups defined in Atlan. + + :param limit: maximum number of results to be returned + :param offset: starting point for the list of groups when paging + :param sort: property by which to sort the results, by default : name + :param columns: provides columns projection support for groups endpoint + :returns: a GroupResponse object with all groups based on the parameters; results are iterable. + """ + return self.get(offset=offset, limit=limit, sort=sort, columns=columns) + + @validate_arguments + def get_by_name( + self, + alias: str, + limit: int = 20, + offset: int = 0, + ) -> Optional[GroupResponse]: + """ + Retrieves a GroupResponse object containing a list of groups that match the specified string. + + :param alias: name (as it appears in the UI) on which to filter the groups + :param limit: maximum number of groups to retrieve + :param offset: starting point for the list of groups when paging + :returns: a GroupResponse object containing a list of groups whose UI names include the given string; the results are iterable. + """ + return self.get( + offset=offset, + limit=limit, + post_filter='{"$and":[{"alias":{"$ilike":"%' + alias + '%"}}]}', + ) + + @validate_arguments + def get_members( + self, guid: str, request: Optional[UserRequest] = None + ) -> UserResponse: + """ + Retrieves a UserResponse object which contains a list of the members (users) of a group. + + :param guid: unique identifier (GUID) of the group from which to retrieve members + :param request: request containing details about which members to retrieve + :returns: a UserResponse object which contains a list of users that are members of the group + :raises AtlanError: on any API communication issue + """ + if not request: + request = UserRequest() + endpoint_obj = GET_GROUP_MEMBERS.format_path({"group_guid": guid}) + raw_json = self._client._call_api( + api=endpoint_obj.format_path_with_params(), + query_params=request.query_params, + ) + records = None + if raw_records := raw_json.get("records"): + records = msgspec.convert(raw_records, list[AtlanUser], strict=False) + response = UserResponse( + total_record=raw_json.get("totalRecord"), + filter_record=raw_json.get("filterRecord"), + records=records, + ) + response._size = request.limit or 20 + response._start = request.offset or 0 + response._endpoint = endpoint_obj + response._client = self._client + response._criteria = request + return response + + @validate_arguments + def remove_users(self, guid: str, user_ids: Optional[List[str]] = None) -> None: + """ + Remove one or more users from a group. + + :param guid: unique identifier (GUID) of the group from which to remove users + :param user_ids: unique identifiers (GUIDs) of the users to remove from the group + :raises AtlanError: on any API communication issue + """ + rfgr = RemoveFromGroupRequest(users=user_ids or []) + endpoint = REMOVE_USERS_FROM_GROUP.format_path({"group_guid": guid}) + self._client._call_api(endpoint, request_obj=rfgr) diff --git a/pyatlan_v9/client/impersonate.py b/pyatlan_v9/client/impersonate.py new file mode 100644 index 000000000..d1e3a2ae7 --- /dev/null +++ b/pyatlan_v9/client/impersonate.py @@ -0,0 +1,125 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +from __future__ import annotations + +import logging +from typing import Union + +import msgspec + +from pyatlan.client.common import ( + ApiCaller, + ImpersonateEscalate, + ImpersonateGetClientSecret, + ImpersonateGetUserId, + ImpersonateUser, +) +from pyatlan.errors import AtlanError, ErrorCode +from pyatlan_v9.model.response import AccessTokenResponse + +LOGGER = logging.getLogger(__name__) + + +class V9ImpersonationClient: + """ + This class can be used for impersonating users as part of Atlan automations (if desired). + Note: this will only work when run as part of Atlan's packaged workflow ecosystem (running in the cluster back-end). + """ + + def __init__(self, client: ApiCaller): + if not isinstance(client, ApiCaller): + raise ErrorCode.INVALID_PARAMETER_TYPE.exception_with_parameters( + "client", "ApiCaller" + ) + self._client = client + + def user(self, user_id: str) -> str: + """ + Retrieves a bearer token that impersonates the provided user. + + :param user_id: unique identifier of the user to impersonate + :returns: a bearer token that impersonates the provided user + :raises AtlanError: on any API communication issue + """ + client_info = ImpersonateUser.get_client_info() + endpoint, credentials = ImpersonateUser.prepare_request(client_info) + + LOGGER.debug("Getting token with client id and secret") + try: + raw_json = self._client._call_api(endpoint, request_obj=credentials) + argo_token = msgspec.convert( + raw_json, AccessTokenResponse, strict=False + ).access_token + except AtlanError as atlan_err: + raise ErrorCode.UNABLE_TO_ESCALATE.exception_with_parameters() from atlan_err + + LOGGER.debug("Getting token with subject token") + try: + endpoint, user_credentials = ImpersonateUser.prepare_impersonation_request( + client_info, argo_token, user_id + ) + raw_json = self._client._call_api(endpoint, request_obj=user_credentials) + return msgspec.convert( + raw_json, AccessTokenResponse, strict=False + ).access_token + except AtlanError as atlan_err: + raise ErrorCode.UNABLE_TO_IMPERSONATE.exception_with_parameters() from atlan_err + + def escalate(self) -> str: + """ + Escalate to a privileged user on a short-term basis. + Note: this is only possible from within the Atlan tenant, and only when given the appropriate credentials. + + :returns: a short-lived bearer token with escalated privileges + :raises AtlanError: on any API communication issue + """ + client_info = ImpersonateEscalate.get_client_info() + endpoint, credentials = ImpersonateEscalate.prepare_request(client_info) + + try: + raw_json = self._client._call_api(endpoint, request_obj=credentials) + return msgspec.convert( + raw_json, AccessTokenResponse, strict=False + ).access_token + except AtlanError as atlan_err: + raise ErrorCode.UNABLE_TO_ESCALATE.exception_with_parameters() from atlan_err + + def get_client_secret(self, client_guid: str) -> Union[str, None]: + """ + Retrieves the client secret associated with the given client GUID + + :param client_guid: GUID of the client whose secret is to be retrieved + :returns: client secret if available, otherwise `None` + :raises: + - AtlanError: If an API error occurs. + - InvalidRequestError: If the provided GUID is invalid or retrieval fails. + """ + try: + endpoint = ImpersonateGetClientSecret.prepare_request(client_guid) + raw_json = self._client._call_api(endpoint) + return ImpersonateGetClientSecret.process_response(raw_json) + except AtlanError as e: + raise ErrorCode.UNABLE_TO_RETRIEVE_CLIENT_SECRET.exception_with_parameters( + client_guid + ) from e + + def get_user_id(self, username: str) -> Union[str, None]: + """ + Retrieves the user ID from Keycloak for the specified username. + This method is particularly useful for impersonating API tokens. + + :param username: username of the user whose ID needs to be retrieved. + :returns: Keycloak user ID + :raises: + - AtlanError: If an API error occurs. + - InvalidRequestError: If an error occurs while fetching the user ID from Keycloak. + """ + try: + endpoint, query_params = ImpersonateGetUserId.prepare_request(username) + raw_json = self._client._call_api(endpoint, query_params=query_params) + return ImpersonateGetUserId.process_response(raw_json) + except AtlanError as e: + raise ErrorCode.UNABLE_TO_RETRIEVE_USER_GUID.exception_with_parameters( + username + ) from e diff --git a/pyatlan_v9/client/oauth_client.py b/pyatlan_v9/client/oauth_client.py new file mode 100644 index 000000000..822a8e238 --- /dev/null +++ b/pyatlan_v9/client/oauth_client.py @@ -0,0 +1,170 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2026 Atlan Pte. Ltd. +from __future__ import annotations + +from typing import List, Optional + +import msgspec + +from pyatlan.client.common import ( + ApiCaller, + OAuthClientCreate, + OAuthClientGet, + OAuthClientGetById, + OAuthClientPurge, + OAuthClientUpdate, + RoleGet, +) +from pyatlan.client.constants import CREATE_OAUTH_CLIENT +from pyatlan.errors import ErrorCode +from pyatlan_v9.model.oauth_client import ( + OAuthClientCreateResponse, + OAuthClientListResponse, + OAuthClientRequest, + OAuthClientResponse, +) +from pyatlan_v9.validate import validate_arguments + + +class V9OAuthClient: + """ + This class can be used to manage OAuth client credentials. + This class does not need to be instantiated directly but can be + obtained through the oauth_client property of AtlanClient. + """ + + def __init__(self, client: ApiCaller): + if not isinstance(client, ApiCaller): + raise ErrorCode.INVALID_PARAMETER_TYPE.exception_with_parameters( + "client", "ApiCaller" + ) + self._client = client + + @validate_arguments + def get( + self, + limit: int = 20, + offset: int = 0, + sort: Optional[str] = None, + ) -> OAuthClientListResponse: + """ + Retrieves OAuth clients defined in Atlan with pagination support. + + :param limit: maximum number of results to be returned per page (default: 20) + :param offset: starting point for results to return, for paging + :param sort: property by which to sort the results (e.g., 'createdAt' for descending) + :returns: an OAuthClientListResponse containing records and pagination info + :raises AtlanError: on any API communication issue + """ + endpoint, query_params = OAuthClientGet.prepare_request(limit, offset, sort) + raw_json = self._client._call_api(endpoint, query_params) + records = None + if raw_records := raw_json.get("records"): + records = msgspec.convert( + raw_records, list[OAuthClientResponse], strict=False + ) + response = OAuthClientListResponse( + total_record=raw_json.get("totalRecord"), + filter_record=raw_json.get("filterRecord"), + records=records, + ) + response._size = limit + response._start = offset + response._endpoint = endpoint + response._client = self._client + response._sort = sort + return response + + @validate_arguments + def get_by_id(self, client_id: str) -> OAuthClientResponse: + """ + Retrieves the OAuth client with the specified client ID. + + :param client_id: unique client identifier (e.g., 'oauth-client-xxx') + :returns: the OAuthClientResponse with the specified client ID + :raises AtlanError: on any API communication issue + """ + endpoint, query_params = OAuthClientGetById.prepare_request(client_id) + raw_json = self._client._call_api(endpoint, query_params) + return msgspec.convert(raw_json, OAuthClientResponse, strict=False) + + @validate_arguments + def updater( + self, + client_id: str, + display_name: Optional[str] = None, + description: Optional[str] = None, + ) -> OAuthClientResponse: + """ + Update an existing OAuth client with the provided settings. + + :param client_id: unique client identifier (e.g., 'oauth-client-xxx') + :param display_name: human-readable name for the OAuth client + :param description: optional explanation of the OAuth client + :returns: the updated OAuthClientResponse + :raises AtlanError: on any API communication issue + """ + endpoint, request_obj = OAuthClientUpdate.prepare_request( + client_id, display_name, description + ) + raw_json = self._client._call_api(endpoint, request_obj=request_obj) + return msgspec.convert(raw_json, OAuthClientResponse, strict=False) + + @validate_arguments + def purge(self, client_id: str) -> None: + """ + Delete (purge) the specified OAuth client. + + :param client_id: unique client identifier (e.g., 'oauth-client-xxx') + :raises AtlanError: on any API communication issue + """ + endpoint, _ = OAuthClientPurge.prepare_request(client_id) + self._client._call_api(endpoint) + + def _fetch_available_roles(self): + """ + Fetch all available roles (workspace and admin-subrole levels). + + :returns: list of AtlanRole objects + """ + filter_str = OAuthClientCreate.build_roles_filter() + endpoint, query_params = RoleGet.prepare_request( + limit=100, + post_filter=filter_str, + ) + raw_json = self._client._call_api(endpoint, query_params) + response = RoleGet.process_response(raw_json) + return response.records or [] + + @validate_arguments + def creator( + self, + name: str, + role: str, + description: Optional[str] = None, + persona_qualified_names: Optional[List[str]] = None, + ) -> OAuthClientCreateResponse: + """ + Create a new OAuth client with the provided settings. + + :param name: human-readable name for the OAuth client (displayed in UI) + :param role: role description to assign to the OAuth client (e.g., 'Admin', 'Member'). + :param description: optional explanation of the OAuth client + :param persona_qualified_names: qualified names of personas to associate with the OAuth client + :returns: the created OAuthClientCreateResponse (includes client_id and client_secret) + :raises AtlanError: on any API communication issue + :raises NotFoundError: if the specified role description is not found + """ + available_roles = self._fetch_available_roles() + resolved_role = OAuthClientCreate.resolve_role_name(role, available_roles) + + request = OAuthClientRequest( + display_name=name, + role=resolved_role, + description=description or "", + persona_qualified_names=persona_qualified_names or [], + ) + raw_json = self._client._call_api( + CREATE_OAUTH_CLIENT.format_path_with_params(), request_obj=request + ) + return msgspec.convert(raw_json, OAuthClientCreateResponse, strict=False) diff --git a/pyatlan_v9/client/open_lineage.py b/pyatlan_v9/client/open_lineage.py new file mode 100644 index 000000000..cddad145e --- /dev/null +++ b/pyatlan_v9/client/open_lineage.py @@ -0,0 +1,121 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +from __future__ import annotations + +from typing import Any, Dict, List, Optional, Union + +from pyatlan.client.common import ( + ApiCaller, + OpenLineageCreateCredential, + OpenLineageSend, +) +from pyatlan.errors import AtlanError, ErrorCode +from pyatlan.utils import validate_type +from pyatlan_v9.model.assets.connection import Connection +from pyatlan_v9.model.credential import Credential +from pyatlan_v9.model.enums import AtlanConnectorType +from pyatlan_v9.model.open_lineage.event import OpenLineageEvent, OpenLineageRawEvent +from pyatlan_v9.model.response import AssetMutationResponse +from pyatlan_v9.validate import validate_arguments + + +class V9OpenLineageClient: + """ + A client for interacting with OpenLineage. + """ + + def __init__(self, client: ApiCaller): + if not isinstance(client, ApiCaller): + raise ErrorCode.INVALID_PARAMETER_TYPE.exception_with_parameters( + "client", "ApiCaller" + ) + self._client = client + + @validate_arguments + def create_connection( + self, + name: str, + connector_type: AtlanConnectorType = AtlanConnectorType.SPARK, + admin_users: Optional[List[str]] = None, + admin_roles: Optional[List[str]] = None, + admin_groups: Optional[List[str]] = None, + ) -> AssetMutationResponse: + """ + Creates a connection for OpenLineage. + + :param name: name for the new connection + :param connector_type: for the new connection to be associated with + :param admin_users: list of admin users to associate with this connection + :param admin_roles: list of admin roles to associate with this connection + :param admin_groups:list of admin groups to associate with this connection + :return: details of the connection created + """ + legacy_credential = OpenLineageCreateCredential.prepare_request(connector_type) + v9_credential = Credential( + auth_type=legacy_credential.auth_type, + name=legacy_credential.name, + connector=legacy_credential.connector, + connector_config_name=legacy_credential.connector_config_name, + connector_type=legacy_credential.connector_type, + extras=legacy_credential.extras, + ) + credential_response = self._client.credentials.creator( # type: ignore[attr-defined] + credential=v9_credential + ) + + connection = Connection.creator( + client=self._client, + name=name, + connector_type=connector_type, + admin_users=admin_users, + admin_groups=admin_groups, + admin_roles=admin_roles, + ) + connection.default_credential_guid = credential_response.id + + return self._client.asset.save(connection) # type: ignore[attr-defined] + + def send( + self, + request: Union[ + OpenLineageEvent, + OpenLineageRawEvent, + List[Dict[str, Any]], + Dict[str, Any], + str, + ], + connector_type: AtlanConnectorType, + ) -> None: + """ + Sends the OpenLineage event to Atlan to be consumed. + + :param request: OpenLineage event to send - can be an OpenLineageEvent, OpenLineageRawEvent, list of dicts, dict, or JSON string + :param connector_type: of the connection that should receive the OpenLineage event + :raises AtlanError: when OpenLineage is not configured OR on any issues with API communication + """ + validate_type( + name="request", + _type=(OpenLineageEvent, OpenLineageRawEvent, list, dict, str), + value=request, + ) + validate_type( + name="connector_type", + _type=(AtlanConnectorType), + value=connector_type, + ) + try: + if isinstance(request, (dict, str, list)): + if isinstance(request, str): + request = OpenLineageRawEvent.parse_raw(request) + else: + request = OpenLineageRawEvent.parse_obj(request) + + api_endpoint, request_obj, api_options = OpenLineageSend.prepare_request( + request, connector_type + ) + self._client._call_api( + request_obj=request_obj, api=api_endpoint, **api_options + ) + except AtlanError as e: + OpenLineageSend.validate_response(e, connector_type) diff --git a/pyatlan_v9/client/query.py b/pyatlan_v9/client/query.py new file mode 100644 index 000000000..6832d5ffd --- /dev/null +++ b/pyatlan_v9/client/query.py @@ -0,0 +1,35 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +from __future__ import annotations + +from pyatlan.client.common import ApiCaller, QueryStream +from pyatlan.errors import ErrorCode +from pyatlan_v9.model.query import QueryRequest, QueryResponse +from pyatlan_v9.validate import validate_arguments + + +class V9QueryClient: + """ + A client for running SQL queries. + """ + + def __init__(self, client: ApiCaller): + if not isinstance(client, ApiCaller): + raise ErrorCode.INVALID_PARAMETER_TYPE.exception_with_parameters( + "client", "ApiCaller" + ) + self._client = client + + @validate_arguments + def stream(self, request: QueryRequest) -> QueryResponse: + """ + Runs the provided query and returns its results. + + :param: request query to run. + :returns: results of the query. + :raises : AtlanError on any issues with API communication. + """ + endpoint, request_obj = QueryStream.prepare_request(request) + raw_json = self._client._call_api(endpoint, request_obj=request_obj) + return QueryResponse(events=raw_json) diff --git a/pyatlan_v9/client/role.py b/pyatlan_v9/client/role.py new file mode 100644 index 000000000..26717a1d6 --- /dev/null +++ b/pyatlan_v9/client/role.py @@ -0,0 +1,65 @@ +from __future__ import annotations + +from typing import Optional + +import msgspec + +from pyatlan.client.common import ApiCaller, RoleGet, RoleGetAll +from pyatlan.errors import ErrorCode +from pyatlan_v9.model.role import RoleResponse +from pyatlan_v9.validate import validate_arguments + + +class V9RoleClient: + """ + This class can be used to retrieve information about roles. This class does not need to be instantiated + directly but can be obtained through the role property of AtlanClient. + """ + + def __init__(self, client: ApiCaller): + if not isinstance(client, ApiCaller): + raise ErrorCode.INVALID_PARAMETER_TYPE.exception_with_parameters( + "client", "ApiCaller" + ) + self._client = client + + @validate_arguments + def get( + self, + limit: int, + post_filter: Optional[str] = None, + sort: Optional[str] = None, + count: bool = True, + offset: int = 0, + ) -> RoleResponse: + """ + Retrieves a RoleResponse which contains a list of the roles defined in Atlan. + + :param limit: maximum number of results to be returned + :param post_filter: which roles to retrieve + :param sort: property by which to sort the results + :param count: whether to return the total number of records (True) or not (False) + :param offset: starting point for results to return, for paging + :returns: None or a RoleResponse object which contains list of roles that match the provided criteria + :raises AtlanError: on any API communication issue + """ + endpoint, query_params = RoleGet.prepare_request( + limit=limit, + post_filter=post_filter, + sort=sort, + count=count, + offset=offset, + ) + raw_json = self._client._call_api(endpoint, query_params) + return msgspec.convert(raw_json, RoleResponse, strict=False) + + def get_all(self) -> RoleResponse: + """ + Retrieves a RoleResponse which contains a list of all the roles defined in Atlan. + + :returns: a RoleResponse which contains a list of all the roles defined in Atlan + :raises AtlanError: on any API communication issue + """ + endpoint = RoleGetAll.prepare_request() + raw_json = self._client._call_api(endpoint) + return msgspec.convert(raw_json, RoleResponse, strict=False) diff --git a/pyatlan_v9/client/search_log.py b/pyatlan_v9/client/search_log.py new file mode 100644 index 000000000..958f862c4 --- /dev/null +++ b/pyatlan_v9/client/search_log.py @@ -0,0 +1,158 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +from __future__ import annotations + +from typing import List, Union + +import msgspec + +from pyatlan.client.common import ApiCaller, SearchLogSearch +from pyatlan.errors import ErrorCode +from pyatlan_v9.model.search_log import ( + AssetViews, + SearchLogEntry, + SearchLogRequest, + SearchLogResults, + SearchLogViewResults, + UserViews, +) +from pyatlan_v9.validate import validate_arguments + +UNIQUE_USERS = "uniqueUsers" +UNIQUE_ASSETS = "uniqueAssets" + + +_MS_TIMESTAMP_THRESHOLD = 1e12 +_LOG_TS_FIELDS = ("createdAt", "timestamp") + + +def _normalize_ms_timestamp(val): + """Convert a millisecond epoch timestamp to seconds for msgspec datetime parsing.""" + if isinstance(val, (int, float)) and val > _MS_TIMESTAMP_THRESHOLD: + return val / 1000 + return val + + +def _parse_user_views(raw_json: dict) -> List[UserViews]: + """Parse user views from aggregation buckets using msgspec.""" + buckets = raw_json.get("aggregations", {}).get(UNIQUE_USERS, {}).get("buckets", []) + mapped = [ + { + "username": b.get("key", ""), + "view_count": b.get("doc_count", 0), + "most_recent_view": _normalize_ms_timestamp( + b.get("latest_timestamp", {}).get("value", 0) + ), + } + for b in buckets + if b and isinstance(b, dict) + ] + return msgspec.convert(mapped, list[UserViews], strict=False) + + +def _parse_asset_views(raw_json: dict) -> List[AssetViews]: + """Parse asset views from aggregation buckets using msgspec.""" + buckets = raw_json.get("aggregations", {}).get(UNIQUE_ASSETS, {}).get("buckets", []) + mapped = [ + { + "guid": b.get("key", ""), + "total_views": b.get("doc_count", 0), + "distinct_users": b.get(UNIQUE_USERS, {}).get("value", 0), + } + for b in buckets + if b and isinstance(b, dict) + ] + return msgspec.convert(mapped, list[AssetViews], strict=False) + + +def _normalize_ms_timestamps_copy(record: dict, fields: tuple) -> dict: + """Return a shallow copy with millisecond epoch timestamps converted to seconds.""" + out = dict(record) + for field in fields: + val = out.get(field) + if isinstance(val, (int, float)) and val > _MS_TIMESTAMP_THRESHOLD: + out[field] = val / 1000 + return out + + +def _parse_log_entries(raw_json: dict) -> List[SearchLogEntry]: + """Parse log entries from raw JSON response using msgspec.""" + logs = raw_json.get("logs", []) + if logs: + normalized = [_normalize_ms_timestamps_copy(e, _LOG_TS_FIELDS) for e in logs] + return msgspec.convert(normalized, list[SearchLogEntry], strict=False) + return [] + + +class V9SearchLogClient: + """ + This class can be used to configure and run a search against Atlan's search log. + This class does not need to be instantiated directly but can be obtained + through the search_log property of AtlanClient. + """ + + def __init__(self, client: ApiCaller): + if not isinstance(client, ApiCaller): + raise ErrorCode.INVALID_PARAMETER_TYPE.exception_with_parameters( + "client", "ApiCaller" + ) + self._client = client + + @validate_arguments + def search( + self, criteria: SearchLogRequest, bulk=False + ) -> Union[SearchLogViewResults, SearchLogResults]: + """ + Search for search logs using the provided criteria. + `Note:` if the number of results exceeds the predefined threshold + (10,000 search logs) this will be automatically converted into an search log `bulk` search. + + :param criteria: detailing the search query, parameters, and so on to run + :param bulk: whether to run the search to retrieve search logs that match the supplied criteria, + for large numbers of results (> `10,000`), defaults to `False`. Note: this will reorder the results + (based on creation timestamp) in order to iterate through a large number (more than `10,000`) results. + :raises InvalidRequestError: + + - if search log bulk search is enabled (`bulk=True`) and any + user-specified sorting options are found in the search request. + - if search log bulk search is disabled (`bulk=False`) and the number of results + exceeds the predefined threshold (i.e: `10,000` assets) + and any user-specified sorting options are found in the search request. + + :raises AtlanError: on any API communication issue + :returns: the results of the search + """ + endpoint, request_obj = SearchLogSearch.prepare_request(criteria, bulk) + raw_json = self._client._call_api(endpoint, request_obj=request_obj) + + count = raw_json.get("approximateCount", 0) + aggregations = raw_json.get("aggregations", {}) + + if aggregations and UNIQUE_USERS in aggregations: + user_views = _parse_user_views(raw_json) + return SearchLogViewResults(count=count, user_views=user_views) + + if aggregations and UNIQUE_ASSETS in aggregations: + asset_views = _parse_asset_views(raw_json) + return SearchLogViewResults(count=count, asset_views=asset_views) + + log_entries = _parse_log_entries(raw_json) + results = SearchLogResults( + client=self._client, + criteria=criteria, + start=criteria.dsl.from_, + size=criteria.dsl.size, + count=count, + log_entries=log_entries, + aggregations=aggregations, + bulk=bulk, + processed_log_entries_count=len(log_entries), + ) + + if SearchLogSearch.check_for_bulk_search( + results.count, criteria, bulk, SearchLogResults + ): + return self.search(criteria) + + return results diff --git a/pyatlan_v9/client/sso.py b/pyatlan_v9/client/sso.py new file mode 100644 index 000000000..b19d2264e --- /dev/null +++ b/pyatlan_v9/client/sso.py @@ -0,0 +1,225 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +from __future__ import annotations + +from enum import Enum +from typing import List + +import msgspec + +from pyatlan.client.common import ApiCaller +from pyatlan.client.constants import ( + CREATE_SSO_GROUP_MAPPING, + DELETE_SSO_GROUP_MAPPING, + GET_ALL_SSO_GROUP_MAPPING, + GET_SSO_GROUP_MAPPING, + UPDATE_SSO_GROUP_MAPPING, +) +from pyatlan.errors import AtlanError, ErrorCode +from pyatlan.utils import get_epoch_timestamp +from pyatlan_v9.model.group import AtlanGroup +from pyatlan_v9.model.sso import SSOMapper, SSOMapperConfig +from pyatlan_v9.validate import validate_arguments + +GROUP_MAPPER_ATTRIBUTE = "memberOf" +GROUP_MAPPER_SYNC_MODE = "FORCE" +IDP_GROUP_MAPPER = "saml-group-idp-mapper" + + +def _resolve_sso_alias(sso_alias) -> str: + """Extract the string value from an SSO alias, handling enums properly.""" + if isinstance(sso_alias, Enum): + return sso_alias.value + return str(sso_alias) + + +def _group_name_for_sso(atlan_group: AtlanGroup) -> str: + """Resolve group name for SSO payload; API list response may omit top-level name.""" + if atlan_group.name: + return atlan_group.name + if atlan_group.alias: + return atlan_group.alias + if ( + atlan_group.attributes + and atlan_group.attributes.alias + and len(atlan_group.attributes.alias) > 0 + ): + return atlan_group.attributes.alias[0] + return atlan_group.id or "" + + +def _generate_group_mapper_name(atlan_group_id) -> str: + return f"{atlan_group_id}--{int(get_epoch_timestamp() * 1000)}" + + +class V9SSOClient: + """ + A client for operating on Atlan's single sign-on (SSO). + """ + + def __init__(self, client: ApiCaller): + if not isinstance(client, ApiCaller): + raise ErrorCode.INVALID_PARAMETER_TYPE.exception_with_parameters( + "client", "ApiCaller" + ) + self._client = client + + @staticmethod + def _parse_sso_mapper(raw_json): + try: + if isinstance(raw_json, list): + return msgspec.convert(raw_json, List[SSOMapper], strict=False) + return msgspec.convert(raw_json, SSOMapper, strict=False) + except msgspec.ValidationError as err: + raise ErrorCode.JSON_ERROR.exception_with_parameters( + raw_json, 200, str(err) + ) from err + + def _check_existing_group_mappings( + self, sso_alias: str, atlan_group: AtlanGroup + ) -> None: + """ + Check if an SSO group mapping already exists within Atlan. + + :raises AtlanError: on any error during API invocation. + :raises InvalidRequestException: if the provided group mapping already exists. + """ + existing_group_maps = self.get_all_group_mappings(sso_alias=sso_alias) + for group_map in existing_group_maps: + if group_map.name and str(atlan_group.id) in group_map.name: + raise ErrorCode.SSO_GROUP_MAPPING_ALREADY_EXISTS.exception_with_parameters( + atlan_group.alias, group_map.config.attribute_value + ) + + @validate_arguments + def create_group_mapping( + self, sso_alias: str, atlan_group: AtlanGroup, sso_group_name: str + ) -> SSOMapper: + """ + Creates a new Atlan SSO group mapping. + + :param sso_alias: name of the SSO provider. + :param atlan_group: existing Atlan group. + :param sso_group_name: name of the SSO group. + :raises AtlanError: on any error during API invocation. + :returns: created SSO group mapping instance. + """ + sso_alias_str = _resolve_sso_alias(sso_alias) + self._check_existing_group_mappings(sso_alias_str, atlan_group) + group_name = _group_name_for_sso(atlan_group) + mapper = SSOMapper( + name=_generate_group_mapper_name(atlan_group.id), + config=SSOMapperConfig( + attributes="[]", + sync_mode=GROUP_MAPPER_SYNC_MODE, + attribute_values_regex="", + attribute_name=GROUP_MAPPER_ATTRIBUTE, + attribute_value=sso_group_name, + group_name=group_name, + ), + identity_provider_alias=sso_alias_str, + identity_provider_mapper=IDP_GROUP_MAPPER, + ) + endpoint = CREATE_SSO_GROUP_MAPPING.format_path({"sso_alias": sso_alias_str}) + raw_json = self._client._call_api(endpoint, request_obj=mapper) + return self._parse_sso_mapper(raw_json) + + @validate_arguments + def update_group_mapping( + self, + sso_alias: str, + atlan_group: AtlanGroup, + group_map_id: str, + group_map_name: str, + sso_group_name: str, + ) -> SSOMapper: + """ + Update an existing Atlan SSO group mapping. + + :param sso_alias: name of the SSO provider. + :param atlan_group: existing Atlan group. + :param group_map_id: existing SSO group map identifier. + :param group_map_name: existing SSO group map name. + :param sso_group_name: new SSO group name. + :raises AtlanError: on any error during API invocation. + :returns: updated SSO group mapping instance. + """ + sso_alias_str = _resolve_sso_alias(sso_alias) + group_name = _group_name_for_sso(atlan_group) + mapper = SSOMapper( + id=group_map_id, + name=group_map_name, + config=SSOMapperConfig( + attributes="[]", + sync_mode=GROUP_MAPPER_SYNC_MODE, + group_name=group_name, + attribute_name=GROUP_MAPPER_ATTRIBUTE, + attribute_value=sso_group_name, + ), + identity_provider_alias=sso_alias_str, + identity_provider_mapper=IDP_GROUP_MAPPER, + ) + endpoint = UPDATE_SSO_GROUP_MAPPING.format_path( + {"sso_alias": sso_alias_str, "group_map_id": group_map_id} + ) + raw_json = self._client._call_api(endpoint, request_obj=mapper) + return self._parse_sso_mapper(raw_json) + + @validate_arguments + def get_all_group_mappings(self, sso_alias: str) -> List[SSOMapper]: + """ + Retrieves all existing Atlan SSO group mappings. + + :param sso_alias: name of the SSO provider. + :raises AtlanError: on any error during API invocation (other than 404). + :returns: list of existing SSO group mapping instances. Returns [] if the + endpoint returns 404 (e.g. SSO not configured). + """ + endpoint = GET_ALL_SSO_GROUP_MAPPING.format_path( + {"sso_alias": _resolve_sso_alias(sso_alias)} + ) + try: + raw_json = self._client._call_api(endpoint) + except AtlanError as e: + if "404" in str(e): + return [] + raise + group_mappings = [ + mapping + for mapping in raw_json + if mapping.get("identityProviderMapper") == IDP_GROUP_MAPPER + ] + return self._parse_sso_mapper(group_mappings) + + @validate_arguments + def get_group_mapping(self, sso_alias: str, group_map_id: str) -> SSOMapper: + """ + Retrieves an existing Atlan SSO group mapping. + + :param sso_alias: name of the SSO provider. + :param group_map_id: existing SSO group map identifier. + :raises AtlanError: on any error during API invocation. + :returns: existing SSO group mapping instance. + """ + endpoint = GET_SSO_GROUP_MAPPING.format_path( + {"sso_alias": _resolve_sso_alias(sso_alias), "group_map_id": group_map_id} + ) + raw_json = self._client._call_api(endpoint) + return self._parse_sso_mapper(raw_json) + + @validate_arguments + def delete_group_mapping(self, sso_alias: str, group_map_id: str) -> None: + """ + Deletes an existing Atlan SSO group mapping. + + :param sso_alias: name of the SSO provider. + :param group_map_id: existing SSO group map identifier. + :raises AtlanError: on any error during API invocation. + :returns: an empty response (`None`). + """ + endpoint = DELETE_SSO_GROUP_MAPPING.format_path( + {"sso_alias": _resolve_sso_alias(sso_alias), "group_map_id": group_map_id} + ) + raw_json = self._client._call_api(endpoint) + return raw_json diff --git a/pyatlan_v9/client/task.py b/pyatlan_v9/client/task.py new file mode 100644 index 000000000..a58982704 --- /dev/null +++ b/pyatlan_v9/client/task.py @@ -0,0 +1,59 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +from __future__ import annotations + +from typing import List + +import msgspec + +from pyatlan.client.common import ApiCaller, TaskSearch +from pyatlan.errors import ErrorCode +from pyatlan_v9.model.task import AtlanTask, TaskSearchRequest, TaskSearchResponse +from pyatlan_v9.validate import validate_arguments + + +def _parse_tasks(raw_json: dict) -> List[AtlanTask]: + """Parse tasks from the raw API response using msgspec.""" + tasks = raw_json.get("tasks", []) + if tasks: + return msgspec.convert(tasks, list[AtlanTask], strict=False) + return [] + + +class V9TaskClient: + """ + A client for operating on tasks. + """ + + def __init__(self, client: ApiCaller): + if not isinstance(client, ApiCaller): + raise ErrorCode.INVALID_PARAMETER_TYPE.exception_with_parameters( + "client", "ApiCaller" + ) + self._client = client + + @validate_arguments + def search(self, request: TaskSearchRequest) -> TaskSearchResponse: + """ + Search for tasks using the provided criteria. + + :param request: search request for tasks + :returns: search results for tasks + """ + endpoint, request_obj = TaskSearch.prepare_request(request) + raw_json = self._client._call_api(endpoint, request_obj=request_obj) + count = raw_json.get("approximateCount", 0) + aggregations = raw_json.get("aggregations") + tasks = _parse_tasks(raw_json) + + return TaskSearchResponse( + client=self._client, + endpoint=endpoint, + criteria=request, + start=request.dsl.from_, + size=request.dsl.size, + count=count, + tasks=tasks, + aggregations=aggregations, + ) diff --git a/pyatlan_v9/client/token.py b/pyatlan_v9/client/token.py new file mode 100644 index 000000000..6e6797494 --- /dev/null +++ b/pyatlan_v9/client/token.py @@ -0,0 +1,176 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. +from __future__ import annotations + +from typing import Optional, Set + +import msgspec + +from pyatlan.client.common import ( + ApiCaller, + TokenGet, + TokenGetByGuid, + TokenGetById, + TokenGetByName, + TokenPurge, +) +from pyatlan.client.constants import UPSERT_API_TOKEN +from pyatlan.errors import ErrorCode +from pyatlan_v9.model.api_tokens import ApiToken, ApiTokenRequest, ApiTokenResponse +from pyatlan_v9.validate import validate_arguments + + +class V9TokenClient: + """ + This class can be used to retrieve information pertaining to API tokens. This class does not need to be instantiated + directly but can be obtained through the token property of AtlanClient. + """ + + def __init__(self, client: ApiCaller): + if not isinstance(client, ApiCaller): + raise ErrorCode.INVALID_PARAMETER_TYPE.exception_with_parameters( + "client", "ApiCaller" + ) + self._client = client + + @validate_arguments + def get( + self, + limit: Optional[int] = None, + post_filter: Optional[str] = None, + sort: Optional[str] = None, + count: bool = True, + offset: int = 0, + ) -> ApiTokenResponse: + """ + Retrieves an ApiTokenResponse which contains a list of API tokens defined in Atlan. + + :param limit: maximum number of results to be returned + :param post_filter: which API tokens to retrieve + :param sort: property by which to sort the results + :param count: whether to return the total number of records (True) or not (False) + :param offset: starting point for results to return, for paging + :returns: an ApiTokenResponse which contains a list of API tokens that match the provided criteria + :raises AtlanError: on any API communication issue + """ + endpoint, query_params = TokenGet.prepare_request( + limit, post_filter, sort, count, offset + ) + raw_json = self._client._call_api(endpoint, query_params) + return msgspec.convert(raw_json, ApiTokenResponse, strict=False) + + @validate_arguments + def get_by_name(self, display_name: str) -> Optional[ApiToken]: + """ + Retrieves the API token with a name that exactly matches the provided string. + + :param display_name: name (as it appears in the UI) by which to retrieve the API token + :returns: the API token whose name (in the UI) matches the provided string, or None if there is none + :raises AtlanError: on any API communication issue + """ + endpoint, query_params = TokenGetByName.prepare_request(display_name) + raw_json = self._client._call_api(endpoint, query_params) + response = msgspec.convert(raw_json, ApiTokenResponse, strict=False) + if response.records and len(response.records) >= 1: + return response.records[0] + return None + + @validate_arguments + def get_by_id(self, client_id: str) -> Optional[ApiToken]: + """ + Retrieves the API token with a client ID that exactly matches the provided string. + + :param client_id: unique client identifier by which to retrieve the API token + :returns: the API token whose clientId matches the provided string, or None if there is none + :raises AtlanError: on any API communication issue + """ + endpoint, query_params = TokenGetById.prepare_request(client_id) + raw_json = self._client._call_api(endpoint, query_params) + response = msgspec.convert(raw_json, ApiTokenResponse, strict=False) + if response.records and len(response.records) >= 1: + return response.records[0] + return None + + @validate_arguments + def get_by_guid(self, guid: str) -> Optional[ApiToken]: + """ + Retrieves the API token with a unique ID (GUID) that exactly matches the provided string. + + :param guid: unique identifier by which to retrieve the API token + :returns: the API token whose GUID matches the provided string, or None if there is none + :raises AtlanError: on any API communication issue + """ + endpoint, query_params = TokenGetByGuid.prepare_request(guid) + raw_json = self._client._call_api(endpoint, query_params) + response = msgspec.convert(raw_json, ApiTokenResponse, strict=False) + if response.records and len(response.records) >= 1: + return response.records[0] + return None + + @validate_arguments + def creator( + self, + display_name: str, + description: str = "", + personas: Optional[Set[str]] = None, + validity_seconds: int = -1, + ) -> ApiToken: + """ + Create a new API token with the provided settings. + + :param display_name: human-readable name for the API token + :param description: optional explanation of the API token + :param personas: qualified_names of personas that should be linked to the token + :param validity_seconds: time in seconds after which the token should expire (negative numbers are treated as + infinite) + :returns: the created API token + :raises AtlanError: on any API communication issue + """ + request = ApiTokenRequest( + display_name=display_name, + description=description, + persona_qualified_names=personas or set(), + validity_seconds=validity_seconds, + ) + raw_json = self._client._call_api(UPSERT_API_TOKEN, request_obj=request) + return msgspec.convert(raw_json, ApiToken, strict=False) + + @validate_arguments + def updater( + self, + guid: str, + display_name: str, + description: str = "", + personas: Optional[Set[str]] = None, + ) -> ApiToken: + """ + Update an existing API token with the provided settings. + + :param guid: unique identifier (GUID) of the API token + :param display_name: human-readable name for the API token + :param description: optional explanation of the API token + :param personas: qualified_names of personas that should be linked to the token, note that you MUST + provide the complete list on any update (any not included in the list will be removed, + so if you do not specify any personas then ALL personas will be unlinked from the API token) + :returns: the updated API token + :raises AtlanError: on any API communication issue + """ + request = ApiTokenRequest( + display_name=display_name, + description=description, + persona_qualified_names=personas or set(), + ) + endpoint = UPSERT_API_TOKEN.format_path_with_params(guid) + raw_json = self._client._call_api(endpoint, request_obj=request) + return msgspec.convert(raw_json, ApiToken, strict=False) + + @validate_arguments + def purge(self, guid: str) -> None: + """ + Delete (purge) the specified API token. + + :param guid: unique identifier (GUID) of the API token to delete + :raises AtlanError: on any API communication issue + """ + endpoint, _ = TokenPurge.prepare_request(guid) + self._client._call_api(endpoint) diff --git a/pyatlan_v9/client/transport.py b/pyatlan_v9/client/transport.py new file mode 100644 index 000000000..9a5c5ee09 --- /dev/null +++ b/pyatlan_v9/client/transport.py @@ -0,0 +1,224 @@ +# type: ignore +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. +""" +Custom HTTP transport with retry support for Atlan Python SDK. + +This module provides transport classes that properly integrate retry logic +with httpx's HTTPTransport while respecting proxy and SSL configurations. +""" + +import logging +from functools import partial +from typing import Any, Optional, Union + +import httpx +from httpx_retries import Retry + +logger = logging.getLogger(__name__) + + +class PyatlanSyncTransport(httpx.BaseTransport): + """ + A synchronous transport that wraps httpx.HTTPTransport with retry logic. + + This transport properly handles proxy and SSL configurations by passing + them directly to the underlying HTTPTransport, unlike the default + RetryTransport which creates its own transport instances. + + Example: + ```python + transport = PyatlanSyncTransport( + retry=Retry(total=5), + proxy="http://proxy.example.com:8080", + verify="/path/to/cert.pem" + ) + client = httpx.Client(transport=transport) + ``` + + Args: + retry: The retry configuration. Defaults to Retry() if not provided. + **kwargs: All other arguments are passed to httpx.HTTPTransport, + including proxy, verify, cert, trust_env, http1, http2, limits, etc. + """ + + def __init__( + self, + retry: Optional[Retry] = None, + **kwargs: Any, + ) -> None: + self.retry = retry or Retry() + # Ensure trust_env is True by default to respect environment variables + # unless explicitly overridden + if "trust_env" not in kwargs: + kwargs["trust_env"] = True + # Create the underlying HTTPTransport with all proxy/SSL config + self._transport = httpx.HTTPTransport(**kwargs) + + def __enter__(self): + self._transport.__enter__() + return self + + def __exit__(self, *args): + return self._transport.__exit__(*args) + + def handle_request(self, request: httpx.Request) -> httpx.Response: + """ + Sends an HTTP request, possibly with retries. + + Args: + request: The request to send. + + Returns: + The final response. + """ + logger.debug("handle_request started request=%s", request) + + if self.retry.is_retryable_method(request.method): + send_method = partial(self._transport.handle_request) + response = self._retry_operation(request, send_method) + else: + response = self._transport.handle_request(request) + + logger.debug( + "handle_request finished request=%s response=%s", request, response + ) + return response + + def _retry_operation( + self, + request: httpx.Request, + send_method: partial, + ) -> httpx.Response: + """Execute a request with retry logic.""" + retry = self.retry + response: Union[httpx.Response, httpx.HTTPError, None] = None + + while True: + if response is not None: + logger.debug( + "_retry_operation retrying response=%s retry=%s", response, retry + ) + retry = retry.increment() + retry.sleep(response) + + try: + response = send_method(request) + except httpx.HTTPError as e: + if retry.is_exhausted() or not retry.is_retryable_exception(e): + raise + response = e + continue + + if retry.is_exhausted() or not retry.is_retryable_status_code( + response.status_code + ): + return response + + def close(self) -> None: + """Close the underlying transport.""" + self._transport.close() + + +class PyatlanAsyncTransport(httpx.AsyncBaseTransport): + """ + An asynchronous transport that wraps httpx.AsyncHTTPTransport with retry logic. + + This transport properly handles proxy and SSL configurations by passing + them directly to the underlying AsyncHTTPTransport. + + Example: + ```python + transport = PyatlanAsyncTransport( + retry=Retry(total=5), + proxy="http://proxy.example.com:8080", + verify="/path/to/cert.pem" + ) + async with httpx.AsyncClient(transport=transport) as client: + response = await client.get("https://example.com") + ``` + + Args: + retry: The retry configuration. Defaults to Retry() if not provided. + **kwargs: All other arguments are passed to httpx.AsyncHTTPTransport, + including proxy, verify, cert, trust_env, http1, http2, limits, etc. + """ + + def __init__( + self, + retry: Optional[Retry] = None, + **kwargs: Any, + ) -> None: + self.retry = retry or Retry() + # Ensure trust_env is True by default to respect environment variables + # unless explicitly overridden + if "trust_env" not in kwargs: + kwargs["trust_env"] = True + # Create the underlying AsyncHTTPTransport with all proxy/SSL config + self._transport = httpx.AsyncHTTPTransport(**kwargs) + + async def __aenter__(self): + await self._transport.__aenter__() + return self + + async def __aexit__(self, *args): + return await self._transport.__aexit__(*args) + + async def handle_async_request(self, request: httpx.Request) -> httpx.Response: + """ + Sends an HTTP request asynchronously, possibly with retries. + + Args: + request: The request to perform. + + Returns: + The final response. + """ + logger.debug("handle_async_request started request=%s", request) + + if self.retry.is_retryable_method(request.method): + send_method = partial(self._transport.handle_async_request) + response = await self._retry_operation_async(request, send_method) + else: + response = await self._transport.handle_async_request(request) + + logger.debug( + "handle_async_request finished request=%s response=%s", request, response + ) + return response + + async def _retry_operation_async( + self, + request: httpx.Request, + send_method: partial, + ) -> httpx.Response: + """Execute an async request with retry logic.""" + retry = self.retry + response: Union[httpx.Response, httpx.HTTPError, None] = None + + while True: + if response is not None: + logger.debug( + "_retry_operation_async retrying response=%s retry=%s", + response, + retry, + ) + retry = retry.increment() + await retry.asleep(response) + + try: + response = await send_method(request) + except httpx.HTTPError as e: + if retry.is_exhausted() or not retry.is_retryable_exception(e): + raise + response = e + continue + + if retry.is_exhausted() or not retry.is_retryable_status_code( + response.status_code + ): + return response + + async def aclose(self) -> None: + """Close the underlying transport.""" + await self._transport.aclose() diff --git a/pyatlan_v9/client/typedef.py b/pyatlan_v9/client/typedef.py new file mode 100644 index 000000000..dda80da32 --- /dev/null +++ b/pyatlan_v9/client/typedef.py @@ -0,0 +1,212 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. +from __future__ import annotations + +from typing import List, Union + +import msgspec + +from pyatlan.client.common import ApiCaller +from pyatlan.client.constants import ( + CREATE_TYPE_DEFS, + DELETE_TYPE_DEF_BY_NAME, + GET_ALL_TYPE_DEFS, + GET_TYPE_DEF_BY_NAME, + UPDATE_TYPE_DEFS, +) +from pyatlan.errors import ErrorCode +from pyatlan_v9.model.enums import AtlanTypeCategory +from pyatlan_v9.model.typedef import ( + AtlanTagDef, + CustomMetadataDef, + EntityDef, + EnumDef, + RelationshipDef, + StructDef, + TypeDef, + TypeDefResponse, +) +from pyatlan_v9.validate import validate_arguments + +_TYPE_DEF_MAP = { + AtlanTypeCategory.ENUM: EnumDef, + AtlanTypeCategory.STRUCT: StructDef, + AtlanTypeCategory.CLASSIFICATION: AtlanTagDef, + AtlanTypeCategory.ENTITY: EntityDef, + AtlanTypeCategory.RELATIONSHIP: RelationshipDef, + AtlanTypeCategory.CUSTOM_METADATA: CustomMetadataDef, +} + + +def _build_typedef_request(typedef: TypeDef) -> TypeDefResponse: + """Build a TypeDefResponse request payload from a TypeDef.""" + if isinstance(typedef, AtlanTagDef): + return TypeDefResponse(atlan_tag_defs=[typedef]) + elif isinstance(typedef, CustomMetadataDef): + return TypeDefResponse(custom_metadata_defs=[typedef]) + elif isinstance(typedef, EnumDef): + return TypeDefResponse(enum_defs=[typedef]) + else: + raise ErrorCode.UNABLE_TO_UPDATE_TYPEDEF_CATEGORY.exception_with_parameters( + typedef.category.value + ) + + +def _create_typedef_from_json(raw_json) -> TypeDef: + """Create a specific TypeDef subclass from raw JSON based on category.""" + try: + category = raw_json.get("category") + type_def_cls = category and _TYPE_DEF_MAP.get(category) + if type_def_cls: + return msgspec.convert(raw_json, type_def_cls, strict=False) + raise ErrorCode.JSON_ERROR.exception_with_parameters( + raw_json, 200, f"Unsupported type definition category: {category}" + ) + except (msgspec.ValidationError, AttributeError) as err: + raise ErrorCode.JSON_ERROR.exception_with_parameters( + raw_json, 200, str(err) + ) from err + + +class V9TypeDefClient: + """ + This class can be used to retrieve information pertaining to TypeDefs. This class does not need to be instantiated + directly but can be obtained through the typedef property of AtlanClient. + """ + + def __init__(self, client: ApiCaller): + if not isinstance(client, ApiCaller): + raise ErrorCode.INVALID_PARAMETER_TYPE.exception_with_parameters( + "client", "ApiCaller" + ) + self._client = client + + def _refresh_caches(self, typedef: TypeDef) -> None: + """Refresh appropriate caches after creating or updating a type definition.""" + if isinstance(typedef, AtlanTagDef): + self._client.atlan_tag_cache.refresh_cache() # type: ignore[attr-defined] + if isinstance(typedef, CustomMetadataDef): + self._client.custom_metadata_cache.refresh_cache() # type: ignore[attr-defined] + if isinstance(typedef, EnumDef): + self._client.enum_cache.refresh_cache() # type: ignore[attr-defined] + + def get_all(self) -> TypeDefResponse: + """ + Retrieves a TypeDefResponse object that contains a list of all the type definitions in Atlan. + + :returns: TypeDefResponse object that contains a list of all the type definitions in Atlan + :raises AtlanError: on any API communication issue + """ + raw_json = self._client._call_api(GET_ALL_TYPE_DEFS, None) + return msgspec.convert(raw_json, TypeDefResponse, strict=False) + + @validate_arguments + def get( + self, type_category: Union[AtlanTypeCategory, List[AtlanTypeCategory]] + ) -> TypeDefResponse: + """ + Retrieves a TypeDefResponse object that contain a list of the specified category type definitions in Atlan. + + :param type_category: category of type definitions to retrieve + :returns: TypeDefResponse object that contain a list that contains the requested list of type definitions + :raises AtlanError: on any API communication issue + """ + categories: List[str] = [] + if isinstance(type_category, list): + categories.extend(map(lambda x: x.value, type_category)) + else: + categories.append(type_category.value) + query_params = {"type": categories} + raw_json = self._client._call_api( + GET_ALL_TYPE_DEFS.format_path_with_params(), query_params + ) + return msgspec.convert(raw_json, TypeDefResponse, strict=False) + + @validate_arguments + def get_by_name(self, name: str) -> TypeDef: + """ + Retrieves a specific type definition from Atlan. + + :name: internal (hashed-string, if used) name of the type definition + :returns: details of that specific type definition + :raises ApiError: on receiving an unsupported type definition + category or when unable to produce a valid response + :raises AtlanError: on any API communication issue + """ + endpoint = GET_TYPE_DEF_BY_NAME.format_path_with_params(name) + raw_json = self._client._call_api(endpoint, None) + return _create_typedef_from_json(raw_json) + + @validate_arguments + def creator(self, typedef: TypeDef) -> TypeDefResponse: + """ + Create a new type definition in Atlan. + Note: only custom metadata, enumerations (options), and Atlan tag type + definitions are currently supported. Furthermore, if any of these are + created their respective cache will be force-refreshed. + + :param typedef: type definition to create + :returns: the resulting type definition that was created + :raises InvalidRequestError: if the typedef you are + trying to create is not one of the allowed types + :raises AtlanError: on any API communication issue + """ + payload = _build_typedef_request(typedef) + raw_json = self._client._call_api(CREATE_TYPE_DEFS, request_obj=payload) + self._refresh_caches(typedef) + return msgspec.convert(raw_json, TypeDefResponse, strict=False) + + @validate_arguments + def updater(self, typedef: TypeDef) -> TypeDefResponse: + """ + Update an existing type definition in Atlan. + Note: only custom metadata, enumerations (options), and Atlan tag type + definitions are currently supported. Furthermore, if any of these are + updated their respective cache will be force-refreshed. + + :param typedef: type definition to update + :returns: the resulting type definition that was updated + :raises InvalidRequestError: if the typedef you are + trying to update is not one of the allowed types + :raises AtlanError: on any API communication issue + """ + payload = _build_typedef_request(typedef) + raw_json = self._client._call_api(UPDATE_TYPE_DEFS, request_obj=payload) + self._refresh_caches(typedef) + return msgspec.convert(raw_json, TypeDefResponse, strict=False) + + @validate_arguments + def purge(self, name: str, typedef_type: type) -> None: + """ + Delete the type definition. + Furthermore, if an Atlan tag, enumeration or custom metadata is deleted their + respective cache will be force-refreshed. + + :param name: internal hashed-string name of the type definition + :param typedef_type: type of the type definition that is being deleted + :raises InvalidRequestError: if the typedef you are trying to delete is not one of the allowed types + :raises NotFoundError: if the typedef you are trying to delete cannot be found + :raises AtlanError: on any API communication issue + """ + if typedef_type == CustomMetadataDef: + internal_name = self._client.custom_metadata_cache.get_id_for_name(name) # type: ignore[attr-defined] + elif typedef_type == EnumDef: + internal_name = name + elif typedef_type == AtlanTagDef: + internal_name = str(self._client.atlan_tag_cache.get_id_for_name(name)) # type: ignore[attr-defined] + else: + raise ErrorCode.UNABLE_TO_PURGE_TYPEDEF_OF_TYPE.exception_with_parameters( + typedef_type + ) + if internal_name: + endpoint = DELETE_TYPE_DEF_BY_NAME.format_path_with_params(internal_name) + self._client._call_api(endpoint, None) + else: + raise ErrorCode.TYPEDEF_NOT_FOUND_BY_NAME.exception_with_parameters(name) + + if typedef_type == CustomMetadataDef: + self._client.custom_metadata_cache.refresh_cache() # type: ignore[attr-defined] + elif typedef_type == EnumDef: + self._client.enum_cache.refresh_cache() # type: ignore[attr-defined] + elif typedef_type == AtlanTagDef: + self._client.atlan_tag_cache.refresh_cache() # type: ignore[attr-defined] diff --git a/pyatlan_v9/client/user.py b/pyatlan_v9/client/user.py new file mode 100644 index 000000000..169769cb4 --- /dev/null +++ b/pyatlan_v9/client/user.py @@ -0,0 +1,431 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. +from __future__ import annotations + +import json +from typing import List, Optional + +import msgspec + +from pyatlan.client.common import ApiCaller +from pyatlan.client.constants import ( + ADD_USER_TO_GROUPS, + CHANGE_USER_ROLE, + CREATE_USERS, + GET_CURRENT_USER, + GET_USER_GROUPS, + GET_USERS, + UPDATE_USER, +) +from pyatlan.errors import ErrorCode +from pyatlan.model.fields.atlan_fields import KeywordField +from pyatlan_v9.model.assets import Asset +from pyatlan_v9.model.fluent_search import FluentSearch +from pyatlan_v9.model.group import AtlanGroup, GroupRequest, GroupResponse +from pyatlan_v9.model.response import AssetMutationResponse +from pyatlan_v9.model.user import ( + AddToGroupsRequest, + AtlanUser, + ChangeRoleRequest, + CreateUser, + CreateUserRequest, + UserMinimalResponse, + UserRequest, + UserResponse, +) +from pyatlan_v9.validate import validate_arguments + +_USER_COLUMNS = [ + "firstName", + "lastName", + "username", + "id", + "email", + "emailVerified", + "enabled", + "roles", + "defaultRoles", + "groupCount", + "attributes", + "personas", + "createdTimestamp", + "lastLoginTime", + "loginEvents", + "isLocked", + "workspaceRole", +] + + +class V9UserClient: + """ + This class can be used to retrieve information pertaining to users. This class does not need to be instantiated + directly but can be obtained through the user property of AtlanClient. + """ + + def __init__(self, client: ApiCaller): + if not isinstance(client, ApiCaller): + raise ErrorCode.INVALID_PARAMETER_TYPE.exception_with_parameters( + "client", "ApiCaller" + ) + self._client = client + + def _build_user_response( + self, + raw_json: dict, + request: UserRequest, + offset: int, + limit: Optional[int], + ) -> UserResponse: + records: list[AtlanUser] = [] + if raw_records := raw_json.get("records"): + records = msgspec.convert(raw_records, list[AtlanUser], strict=False) + response = UserResponse( + total_record=raw_json.get("totalRecord"), + filter_record=raw_json.get("filterRecord"), + records=records, + ) + response._size = limit or 20 + response._start = offset + response._endpoint = GET_USERS + response._client = self._client + response._criteria = request + return response + + @validate_arguments + def creator( + self, users: List[AtlanUser], return_info: bool = False + ) -> Optional[UserResponse]: + """ + Create one or more new users. + + :param users: the details of the new users + :param return_info: whether to return the details of created users, defaults to `False` + :raises AtlanError: on any API communication issue + :returns: a UserResponse object which contains the list of details of created users if `return_info` is `True`, otherwise `None` + """ + to_create: list[CreateUser] = [] + for user in users: + role_name = str(user.workspace_role) + if ( + role_id := self._client.role_cache.get_id_for_name(role_name) + ) and user.email: + to_create.append( + CreateUser(email=user.email, role_name=role_name, role_id=role_id) + ) + payload = CreateUserRequest(users=to_create) + self._client._call_api(CREATE_USERS, request_obj=payload) + if return_info: + emails = [cu.email for cu in to_create] + return self.get_by_emails(emails=emails) + return None + + @validate_arguments + def updater( + self, + guid: str, + user: AtlanUser, + ) -> UserMinimalResponse: + """ + Update a user. + Note: you can only update users that have already signed up to Atlan. Users that are + only invited (but have not yet logged in) cannot be updated. + + :param guid: unique identifier (GUID) of the user to update + :param user: details to update on the user + :returns: basic details about the updated user + :raises AtlanError: on any API communication issue + """ + endpoint = UPDATE_USER.format_path_with_params(guid) + raw_json = self._client._call_api(endpoint, request_obj=user) + return msgspec.convert(raw_json, UserMinimalResponse, strict=False) + + @validate_arguments + def change_role( + self, + guid: str, + role_id: str, + ) -> None: + """ + Change the role of a user. + + :param guid: unique identifier (GUID) of the user whose role should be changed + :param role_id: unique identifier (GUID) of the role to move the user into + :raises AtlanError: on any API communication issue + """ + payload = ChangeRoleRequest(role_id=role_id) + endpoint = CHANGE_USER_ROLE.format_path({"user_guid": guid}) + self._client._call_api(endpoint, request_obj=payload) + + def get_current( + self, + ) -> UserMinimalResponse: + """ + Retrieve the current user (representing the API token). + + :returns: basic details about the current user (API token) + :raises AtlanError: on any API communication issue + """ + raw_json = self._client._call_api(GET_CURRENT_USER, None) + return msgspec.convert(raw_json, UserMinimalResponse, strict=False) + + @validate_arguments + def get( + self, + limit: Optional[int] = 20, + post_filter: Optional[str] = None, + sort: Optional[str] = None, + count: bool = True, + offset: int = 0, + ) -> UserResponse: + """ + Retrieves a UserResponse which contains a list of users defined in Atlan. + + :param limit: maximum number of results to be returned + :param post_filter: which users to retrieve + :param sort: property by which to sort the results + :param count: whether to return the total number of records (True) or not (False) + :param offset: starting point for results to return, for paging + :returns: a UserResponse which contains a list of users that match the provided criteria + :raises AtlanError: on any API communication issue + """ + request = UserRequest( + post_filter=post_filter, + limit=limit, + sort=sort, + count=count, + offset=offset, + columns=_USER_COLUMNS, + ) + endpoint = GET_USERS.format_path_with_params() + raw_json = self._client._call_api( + api=endpoint, query_params=request.query_params + ) + return self._build_user_response(raw_json, request, offset, limit) + + @validate_arguments + def get_all( + self, + limit: int = 20, + offset: int = 0, + sort: Optional[str] = "username", + ) -> UserResponse: + """ + Retrieve a UserResponse object containing a list of all users defined in Atlan. + + :param limit: maximum number of users to retrieve + :param offset: starting point for the list of users when paging + :param sort: property by which to sort the results, by default : `username` + :returns: a UserResponse object with all users based on the parameters; results are iterable. + """ + response: UserResponse = self.get(offset=offset, limit=limit, sort=sort) + return response + + @validate_arguments + def get_by_email( + self, + email: str, + limit: int = 20, + offset: int = 0, + ) -> Optional[UserResponse]: + """ + Retrieves a UserResponse object containing a list of users with email addresses that contain the provided email. + (This could include a complete email address, in which case there should be at + most a single item in the returned list, or could be a partial email address + such as "@example.com" to retrieve all users with that domain in their email + address.) + + :param email: on which to filter the users + :param limit: maximum number of users to retrieve + :param offset: starting point for the list of users when pagin + :returns: a UserResponse object containing a list of users whose email addresses contain the provided string + """ + post_filter = '{"email":{"$ilike":"%' + email + '%"}}' + return self.get(offset=offset, limit=limit, post_filter=post_filter) + + @validate_arguments + def get_by_emails( + self, + emails: List[str], + limit: int = 20, + offset: int = 0, + ) -> Optional[UserResponse]: + """ + Retrieves a UserResponse object containing a list of users with email addresses that match the provided list of emails. + + :param emails: list of email addresses to filter the users + :param limit: maximum number of users to retrieve + :param offset: starting point for the list of users when paginating + :returns: a UserResponse object containing a list of users whose email addresses match the provided list + """ + email_filter = '{"email":{"$in":' + json.dumps(emails or [""]) + "}}" + return self.get(offset=offset, limit=limit, post_filter=email_filter) + + @validate_arguments + def get_by_username(self, username: str) -> Optional[AtlanUser]: + """ + Retrieves a user based on the username. (This attempts an exact match on username + rather than a contains search.) + + :param username: the username by which to find the user + :returns: the with that username + """ + post_filter = '{"username":"' + username + '"}' + response = self.get(offset=0, limit=5, post_filter=post_filter) + if response and response.records and len(response.records) >= 1: + return response.records[0] + return None + + @validate_arguments + def get_by_usernames( + self, usernames: List[str], limit: int = 5, offset: int = 0 + ) -> Optional[UserResponse]: + """ + Retrieves a UserResponse object containing a list of users based on their usernames. + + :param usernames: the list of usernames by which to find the users + :param limit: maximum number of users to retrieve + :param offset: starting point for the list of users when paginating + :returns: a UserResponse object containing list of users with the specified usernames + """ + username_filter = '{"username":{"$in":' + json.dumps(usernames or [""]) + "}}" + return self.get(offset=offset, limit=limit, post_filter=username_filter) + + @validate_arguments + def add_to_groups( + self, + guid: str, + group_ids: List[str], + ) -> None: + """ + Add a user to one or more groups. + + :param guid: unique identifier (GUID) of the user to add into groups + :param group_ids: unique identifiers (GUIDs) of the groups to add the user into + :raises AtlanError: on any API communication issue + """ + payload = AddToGroupsRequest(groups=group_ids) + endpoint = ADD_USER_TO_GROUPS.format_path({"user_guid": guid}) + self._client._call_api(endpoint, request_obj=payload) + + @validate_arguments + def get_groups( + self, guid: str, request: Optional[GroupRequest] = None + ) -> GroupResponse: + """ + Retrieve the groups this user belongs to. + + :param guid: unique identifier (GUID) of the user + :param request: request containing details about which groups to retrieve + :returns: a GroupResponse which contains the groups this user belongs to + :raises AtlanError: on any API communication issue + """ + if not request: + request = GroupRequest() + endpoint_obj = GET_USER_GROUPS.format_path({"user_guid": guid}) + raw_json = self._client._call_api( + api=endpoint_obj.format_path_with_params(), + query_params=request.query_params, + ) + records = None + if raw_records := raw_json.get("records"): + records = msgspec.convert(raw_records, list[AtlanGroup], strict=False) + response = GroupResponse( + total_record=raw_json.get("totalRecord"), + filter_record=raw_json.get("filterRecord"), + records=records, + ) + response._size = request.limit or 20 + response._start = request.offset + response._endpoint = endpoint_obj + response._client = self._client + response._criteria = request + return response + + @validate_arguments + def add_as_admin( + self, asset_guid: str, impersonation_token: str + ) -> Optional[AssetMutationResponse]: + """ + Add the API token configured for the default client as an admin to the asset with the provided GUID. + This is primarily useful for connections, to allow the API token to manage policies for the connection, and + for query collections, to allow the API token to manage the queries in a collection or the collection itself. + + :param asset_guid: unique identifier (GUID) of the asset to which we should add this API token as an admin + :param impersonation_token: a bearer token for an actual user who is already an admin for the asset, + NOT an API token + :returns: a AssetMutationResponse which contains the results of the operation + :raises NotFoundError: if the asset to which to add the API token as an admin cannot be found + """ + return self._add_as( + asset_guid=asset_guid, + impersonation_token=impersonation_token, + keyword_field=Asset.ADMIN_USERS, + ) + + @validate_arguments + def add_as_viewer( + self, asset_guid: str, impersonation_token: str + ) -> Optional[AssetMutationResponse]: + """ + Add the API token configured for the default client as a viewer to the asset with the provided GUID. + This is primarily useful for query collections, to allow the API token to view or run queries within the + collection, but not make any changes to them. + + :param asset_guid: unique identifier (GUID) of the asset to which we should add this API token as an admin + :param impersonation_token: a bearer token for an actual user who is already an admin for the asset, + NOT an API token + :returns: a AssetMutationResponse which contains the results of the operation + :raises NotFoundError: if the asset to which to add the API token as a viewer cannot be found + """ + return self._add_as( + asset_guid=asset_guid, + impersonation_token=impersonation_token, + keyword_field=Asset.VIEWER_USERS, + ) + + def _add_as( + self, asset_guid: str, impersonation_token: str, keyword_field: KeywordField + ) -> Optional[AssetMutationResponse]: + """ + Add the API token configured for the default client as a viewer or admin to the asset with the provided GUID. + + :param asset_guid: unique identifier (GUID) of the asset to which we should add this API token as an admin + :param impersonation_token: a bearer token for an actual user who is already an admin for the asset, + NOT an API token + :param keyword_field: must be either Asset.ADMIN_USERS or Asset.VIEWER_USERS + :returns: a AssetMutationResponse which contains the results of the operation + :raises NotFoundError: if the asset to which to add the API token as a viewer cannot be found + """ + from pyatlan_v9.client.atlan import client_connection + + if keyword_field not in [Asset.ADMIN_USERS, Asset.VIEWER_USERS]: + raise ValueError( + f"keyword_field should be {Asset.VIEWER_USERS} or {Asset.ADMIN_USERS}" + ) + + token_user = self.get_current().username or "" + with client_connection(client=self._client, api_key=impersonation_token) as tmp: # type: ignore[arg-type] + request = ( + FluentSearch() + .where(Asset.GUID.eq(asset_guid)) + .include_on_results(keyword_field) + .page_size(1) + ).to_request() + results = tmp.asset.search(request) + if not results.current_page(): + raise ErrorCode.ASSET_NOT_FOUND_BY_GUID.exception_with_parameters( + asset_guid + ) + asset = results.current_page()[0] + if keyword_field == Asset.VIEWER_USERS: + existing_viewers = asset.viewer_users or set() + existing_viewers.add(token_user) + else: + existing_admins = asset.admin_users or set() + existing_admins.add(token_user) + to_update = asset.trim_to_required() + if keyword_field == Asset.VIEWER_USERS: + to_update.viewer_users = existing_viewers + else: + to_update.admin_users = existing_admins + return tmp.asset.save(to_update) diff --git a/pyatlan_v9/client/workflow.py b/pyatlan_v9/client/workflow.py new file mode 100644 index 000000000..10bda351d --- /dev/null +++ b/pyatlan_v9/client/workflow.py @@ -0,0 +1,827 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +from __future__ import annotations + +import json +from logging import Logger +from time import sleep +from typing import List, Optional, Union, overload + +import msgspec + +from pyatlan.client.common import ( + ApiCaller, + WorkflowDelete, + WorkflowFindScheduleQueryBetween, + WorkflowGetAllScheduledRuns, + WorkflowGetScheduledRun, + WorkflowStop, + WorkflowUpdateOwner, +) +from pyatlan.client.constants import ( + PACKAGE_WORKFLOW_RERUN, + PACKAGE_WORKFLOW_RUN, + PACKAGE_WORKFLOW_UPDATE, + WORKFLOW_INDEX_RUN_SEARCH, + WORKFLOW_INDEX_SEARCH, + WORKFLOW_OWNER_RERUN, + WORKFLOW_RERUN, + WORKFLOW_RUN, + WORKFLOW_UPDATE, +) +from pyatlan.errors import ErrorCode +from pyatlan.utils import validate_type +from pyatlan_v9.model.enums import AtlanWorkflowPhase, WorkflowPackage +from pyatlan_v9.model.search import ( + Bool, + Exists, + NestedQuery, + Prefix, + Range, + Regexp, + Term, + Terms, +) +from pyatlan_v9.model.workflow import ( + ReRunRequest, + ScheduleQueriesSearchRequest, + Workflow, + WorkflowResponse, + WorkflowRunResponse, + WorkflowSchedule, + WorkflowScheduleResponse, + WorkflowSearchRequest, + WorkflowSearchResponse, + WorkflowSearchResult, + WorkflowSearchResultDetail, +) +from pyatlan_v9.validate import validate_arguments + +MONITOR_SLEEP_SECONDS = 5 + +_DEFAULT_SORT = [ + {"metadata.creationTimestamp": {"order": "desc", "nested": {"path": "metadata"}}} +] + +_LATEST_RUN_SORT = [{"status.startedAt": {"order": "desc"}}] + + +class V9WorkflowClient: + """ + This class can be used to retrieve information and rerun workflows. This class does not need to be instantiated + directly but can be obtained through the workflow property of AtlanClient. + """ + + _WORKFLOW_RUN_SCHEDULE = "orchestration.atlan.com/schedule" + _WORKFLOW_RUN_TIMEZONE = "orchestration.atlan.com/timezone" + + def __init__(self, client: ApiCaller): + if not isinstance(client, ApiCaller): + raise ErrorCode.INVALID_PARAMETER_TYPE.exception_with_parameters( + "client", "ApiCaller" + ) + self._client = client + + @validate_arguments + def find_by_type( + self, prefix: WorkflowPackage, max_results: int = 10 + ) -> List[WorkflowSearchResult]: + """ + Find workflows based on their type (prefix). Note: Only workflows that have been run will be found. + + :param prefix: name of the specific workflow to find (for example CONNECTION_DELETE) + :param max_results: the maximum number of results to retrieve + :returns: the list of workflows of the provided type, with the most-recently created first + :raises ValidationError: If the provided prefix is invalid workflow package + :raises AtlanError: on any API communication issue + """ + regex = prefix.value.replace("-", "[-]") + "[-][0-9]{10}" + query = Bool( + filter=[ + NestedQuery( + query=Regexp(field="metadata.name.keyword", value=regex), + path="metadata", + ) + ] + ) + request = WorkflowSearchRequest( + query=query.to_dict(), size=max_results, sort=_DEFAULT_SORT + ) + raw_json = self._client._call_api(WORKFLOW_INDEX_SEARCH, request_obj=request) + response = msgspec.convert(raw_json, WorkflowSearchResponse, strict=False) + return response.hits and response.hits.hits or [] + + @validate_arguments + def find_by_id(self, id: str) -> Optional[WorkflowSearchResult]: + """ + Find workflows based on their ID (e.g: `atlan-snowflake-miner-1714638976`) + Note: Only workflows that have been run will be found + + :param id: the ID of the workflow to find + :returns: the workflow with the provided ID, or None if none is found + :raises AtlanError: on any API communication issue + """ + query = Bool( + filter=[ + NestedQuery( + query=Bool(must=[Term(field="metadata.name.keyword", value=id)]), + path="metadata", + ) + ] + ) + request = WorkflowSearchRequest( + query=query.to_dict(), size=1, sort=_DEFAULT_SORT + ) + raw_json = self._client._call_api(WORKFLOW_INDEX_SEARCH, request_obj=request) + response = msgspec.convert(raw_json, WorkflowSearchResponse, strict=False) + results = response.hits and response.hits.hits + return results[0] if results else None + + @validate_arguments + def find_run_by_id(self, id: str) -> Optional[WorkflowSearchResult]: + """ + Find workflows runs based on their ID (e.g: `atlan-snowflake-miner-1714638976-t7s8b`) + Note: Only workflow runs will be found + + :param id: the ID of the workflow run to find + :returns: the workflow run with the provided ID, or None if none is found + :raises AtlanError: on any API communication issue + """ + query = Bool( + filter=[ + Term( + field="_id", + value=id, + ), + ] + ) + response = self._find_runs(query, size=1) + return results[0] if (results := response.hits and response.hits.hits) else None + + @validate_arguments + def find_runs_by_status_and_time_range( + self, + status: List[AtlanWorkflowPhase], + started_at: Optional[str] = None, + finished_at: Optional[str] = None, + from_: int = 0, + size: int = 100, + ) -> WorkflowSearchResponse: + """ + Retrieves a WorkflowSearchResponse object containing workflow runs based on their status and time range. + + :param status: list of the workflow statuses to filter + :param started_at: (optional) lower bound on 'status.startedAt' (e.g 'now-2h') + :param finished_at: (optional) lower bound on 'status.finishedAt' (e.g 'now-1h') + :param from_:(optional) starting index of the search results (default: `0`). + :param size: (optional) maximum number of search results to return (default: `100`). + :returns: a WorkflowSearchResponse object containing a list of workflows matching the filters + :raises ValidationError: if inputs are invalid + :raises AtlanError: on any API communication issue + """ + time_filters = [] + if started_at: + time_filters.append(Range(field="status.startedAt", gte=started_at)) + if finished_at: + time_filters.append(Range(field="status.finishedAt", lte=finished_at)) + + run_lookup_query = Bool( + must=[ + NestedQuery( + query=Terms( + field="metadata.labels.workflows.argoproj.io/phase.keyword", + values=[s.value for s in status], + ), + path="metadata", + ), + *time_filters, + NestedQuery( + query=Exists(field="metadata.labels.workflows.argoproj.io/creator"), + path="metadata", + ), + ], + ) + return self._find_runs(query=run_lookup_query, from_=from_, size=size) + + def _find_latest_run(self, workflow_name: str) -> Optional[WorkflowSearchResult]: + """ + Find the most recent run for a given workflow + + :param name: name of the workflow for which to find the current run + :returns: the singular result giving the latest run of the workflow + :raises AtlanError: on any API communication issue + """ + query = Bool( + filter=[ + NestedQuery( + query=Term( + field="spec.workflowTemplateRef.name.keyword", + value=workflow_name, + ), + path="spec", + ) + ] + ) + query_dict = query.to_dict() + request = WorkflowSearchRequest( + query=query_dict, from_=0, size=1, sort=_LATEST_RUN_SORT + ) + raw_json = self._client._call_api( + WORKFLOW_INDEX_RUN_SEARCH, request_obj=request + ) + response = msgspec.convert(raw_json, WorkflowSearchResponse, strict=False) + response._client = self._client + response._endpoint = WORKFLOW_INDEX_RUN_SEARCH + response._criteria = query_dict + response._start = 0 + response._size = 1 + return response.hits.hits[0] if response.hits and response.hits.hits else None + + def _find_current_run(self, workflow_name: str) -> Optional[WorkflowSearchResult]: + """ + Find the most current, still-running run of a given workflow + + :param name: name of the workflow for which to find the current run + :returns: the singular result giving the latest currently-running + run of the workflow, or `None` if it is not currently running + :raises AtlanError: on any API communication issue + """ + query = Bool( + filter=[ + NestedQuery( + query=Term( + field="spec.workflowTemplateRef.name.keyword", + value=workflow_name, + ), + path="spec", + ) + ] + ) + query_dict = query.to_dict() + request = WorkflowSearchRequest( + query=query_dict, from_=0, size=50, sort=_DEFAULT_SORT + ) + raw_json = self._client._call_api( + WORKFLOW_INDEX_RUN_SEARCH, request_obj=request + ) + response = msgspec.convert(raw_json, WorkflowSearchResponse, strict=False) + response._client = self._client + response._endpoint = WORKFLOW_INDEX_RUN_SEARCH + response._criteria = query_dict + response._start = 0 + response._size = 50 + if results := response.hits and response.hits.hits: + for result in results: + if result.status in { + AtlanWorkflowPhase.PENDING, + AtlanWorkflowPhase.RUNNING, + }: + return result + return None + + def _find_runs( + self, + query, + from_: int = 0, + size: int = 100, + ) -> WorkflowSearchResponse: + """ + Retrieve existing workflow runs. + + :param query: query object to filter workflow runs. + :param from_: starting point for pagination + :param size: maximum number of results to retrieve + :returns: the workflow runs + :raises AtlanError: on any API communication issue + """ + query_dict = query.to_dict() if hasattr(query, "to_dict") else query + request = WorkflowSearchRequest( + query=query_dict, from_=from_, size=size, sort=_DEFAULT_SORT + ) + raw_json = self._client._call_api( + WORKFLOW_INDEX_RUN_SEARCH, request_obj=request + ) + response = msgspec.convert(raw_json, WorkflowSearchResponse, strict=False) + response._client = self._client + response._endpoint = WORKFLOW_INDEX_RUN_SEARCH + response._criteria = query_dict + response._start = from_ + response._size = size + return response + + def _add_schedule( + self, + workflow, + workflow_schedule: WorkflowSchedule, + ): + """ + Adds required schedule parameters to the workflow object. + """ + if workflow.metadata and workflow.metadata.annotations: + workflow.metadata.annotations.update( + { + self._WORKFLOW_RUN_SCHEDULE: workflow_schedule.cron_schedule, + self._WORKFLOW_RUN_TIMEZONE: workflow_schedule.timezone, + } + ) + + def _handle_workflow_types(self, workflow): + if isinstance(workflow, WorkflowPackage): + if results := self.find_by_type(workflow): + detail = results[0].source + else: + raise ErrorCode.NO_PRIOR_RUN_AVAILABLE.exception_with_parameters( + workflow.value + ) + elif isinstance(workflow, WorkflowSearchResult): + detail = workflow.source + else: + detail = workflow + return detail + + @overload + def rerun( + self, workflow: WorkflowPackage, idempotent: bool = False + ) -> WorkflowRunResponse: ... + + @overload + def rerun( + self, workflow: WorkflowSearchResultDetail, idempotent: bool = False + ) -> WorkflowRunResponse: ... + + @overload + def rerun( + self, workflow: WorkflowSearchResult, idempotent: bool = False + ) -> WorkflowRunResponse: ... + + def rerun( + self, + workflow: Union[ + WorkflowPackage, WorkflowSearchResultDetail, WorkflowSearchResult + ], + idempotent: bool = False, + ) -> WorkflowRunResponse: + """ + Rerun the workflow immediately. + Note: this must be a workflow that was previously run. + + :param workflow: The workflow to rerun. + :param idempotent: If `True`, the workflow will only be rerun if it is not already currently running + :returns: the details of the workflow run (if `idempotent`, will return details of the already-running workflow) + :raises ValidationError: If the provided workflow is invalid + :raises InvalidRequestException: If no prior runs are available for the provided workflow + :raises AtlanError: on any API communication issue + """ + validate_type( + name="workflow", + _type=(WorkflowPackage, WorkflowSearchResultDetail, WorkflowSearchResult), + value=workflow, + ) + detail = self._handle_workflow_types(workflow) + if idempotent and detail and detail.metadata and detail.metadata.name: + sleep(10) + if ( + ( + current_run_details := self._find_current_run( + workflow_name=detail.metadata.name + ) + ) + and current_run_details.source + and current_run_details.source.metadata + and current_run_details.source.spec + and current_run_details.source.status + ): + return WorkflowRunResponse( + metadata=current_run_details.source.metadata, + spec=current_run_details.source.spec, + status=current_run_details.source.status, + ) + use_package_endpoint = not self._client.role_cache.is_api_token_user() # type: ignore[attr-defined] + request = None + if detail and detail.metadata: + request = ReRunRequest( + namespace=detail.metadata.namespace, + resource_name=detail.metadata.name, + ) + endpoint = PACKAGE_WORKFLOW_RERUN if use_package_endpoint else WORKFLOW_RERUN + raw_json = self._client._call_api(endpoint, request_obj=request) + return msgspec.convert(raw_json, WorkflowRunResponse, strict=False) + + @overload + def run( + self, workflow: Workflow, workflow_schedule: Optional[WorkflowSchedule] = None + ) -> WorkflowResponse: ... + + @overload + def run( + self, workflow: str, workflow_schedule: Optional[WorkflowSchedule] = None + ) -> WorkflowResponse: ... + + def run( + self, + workflow: Union[Workflow, str], + workflow_schedule: Optional[WorkflowSchedule] = None, + ) -> WorkflowResponse: + """ + Run the Atlan workflow with a specific configuration. + + Note: This method should only be used to create the workflow for the first time. + Each invocation creates a new connection and new assets within that connection. + Running the workflow multiple times with the same configuration may lead to duplicate assets. + Consider using the "rerun()" method instead to re-execute an existing workflow. + + :param workflow: workflow object to run or a raw workflow JSON string. + :param workflow_schedule: (Optional) a WorkflowSchedule object containing: + - A cron schedule expression, e.g: `5 4 * * *`. + - The time zone for the cron schedule, e.g: `Europe/Paris`. + + :returns: Details of the workflow run. + :raises ValidationError: If the provided `workflow` is invalid. + :raises AtlanError: on any API communication issue. + """ + validate_type(name="workflow", _type=(Workflow, str), value=workflow) + validate_type( + name="workflow_schedule", + _type=(WorkflowSchedule, None), + value=workflow_schedule, + ) + if isinstance(workflow, str): + workflow = msgspec.convert(json.loads(workflow), Workflow, strict=False) + if workflow_schedule: + self._add_schedule(workflow, workflow_schedule) + use_package_endpoint = not self._client.role_cache.is_api_token_user() # type: ignore[attr-defined] + endpoint = PACKAGE_WORKFLOW_RUN if use_package_endpoint else WORKFLOW_RUN + raw_json = self._client._call_api(endpoint, request_obj=workflow) + return msgspec.convert(raw_json, WorkflowResponse, strict=False) + + @validate_arguments + def updater(self, workflow: Workflow) -> WorkflowResponse: + """ + Update a given workflow's configuration. + + :param workflow: request full details of the workflow's revised configuration. + :returns: the updated workflow configuration. + :raises ValidationError: If the provided `workflow` is invalid. + :raises AtlanError: on any API communication issue + """ + use_package_endpoint = not self._client.role_cache.is_api_token_user() # type: ignore[attr-defined] + workflow_name = workflow.metadata and workflow.metadata.name + if use_package_endpoint: + endpoint = PACKAGE_WORKFLOW_UPDATE.format_path( + {"workflow_name": workflow_name} + ) + else: + endpoint = WORKFLOW_UPDATE.format_path({"workflow_name": workflow_name}) + raw_json = self._client._call_api(endpoint, request_obj=workflow) + return msgspec.convert(raw_json, WorkflowResponse, strict=False) + + @validate_arguments + def update_owner(self, workflow_name: str, username: str) -> WorkflowResponse: + """ + Update the owner of a workflow. + + :param workflow_name: name of the workflow for which we want to update owner + :param username: new username of the user who should own the workflow + :returns: workflow response details + :raises AtlanError: on any API communication issue + """ + endpoint, request_obj = WorkflowUpdateOwner.prepare_request( + workflow_name, username + ) + raw_json = self._client._call_api(endpoint, request_obj=request_obj) + return msgspec.convert(raw_json, WorkflowResponse, strict=False) + + @validate_arguments(config=dict(arbitrary_types_allowed=True)) + def monitor( + self, + workflow_response: Optional[WorkflowResponse] = None, + logger: Optional[Logger] = None, + workflow_name: Optional[str] = None, + ) -> Optional[AtlanWorkflowPhase]: + """ + Monitor a workflow until its completion (or the script terminates). + + :param workflow_response: The workflow_response returned from running the workflow + :param logger: the logger to log status information + (logging.INFO for summary info. logging.DEBUG for detail info) + :param workflow_name: name of the workflow to be monitored + :returns: the status at completion or None if the workflow wasn't run + :raises ValidationError: If the provided `workflow_response`, `logger` is invalid + :raises AtlanError: on any API communication issue + """ + name = workflow_name or ( + workflow_response.metadata.name + if workflow_response and workflow_response.metadata + else None + ) + + if not name: + if logger: + logger.info("Skipping workflow monitoring — nothing to monitor.") + return None + + status: Optional[AtlanWorkflowPhase] = None + while status not in { + AtlanWorkflowPhase.SUCCESS, + AtlanWorkflowPhase.ERROR, + AtlanWorkflowPhase.FAILED, + }: + sleep(MONITOR_SLEEP_SECONDS) + if run_details := self._find_latest_run(workflow_name=name): + status = run_details.status + if logger: + logger.debug("Workflow status: %s", status) + + if logger: + logger.info("Workflow completion status: %s", status) + return status + + def get_runs( + self, + workflow_name: str, + workflow_phase: AtlanWorkflowPhase, + from_: int = 0, + size: int = 100, + ) -> Optional[WorkflowSearchResponse]: + """ + Retrieves all workflow runs. + + :param workflow_name: name of the workflow as displayed + in the UI (e.g: `atlan-snowflake-miner-1714638976`). + :param workflow_phase: phase of the given workflow (e.g: Succeeded, Running, Failed, etc). + :param from_: starting index of the search results (default: `0`). + :param size: maximum number of search results to return (default: `100`). + :returns: a list of runs of the given workflow. + :raises AtlanError: on any API communication issue. + """ + query = Bool( + must=[ + NestedQuery( + query=Term( + field="spec.workflowTemplateRef.name.keyword", + value=workflow_name, + ), + path="spec", + ) + ], + filter=[Term(field="status.phase.keyword", value=workflow_phase.value)], + ) + return self._find_runs(query, from_=from_, size=size) + + @validate_arguments + def stop( + self, + workflow_run_id: str, + ) -> WorkflowRunResponse: + """ + Stop the provided, running workflow. + + :param workflow_run_id: identifier of the specific workflow run + :returns: the stopped workflow run + :raises AtlanError: on any API communication issue + """ + endpoint, _ = WorkflowStop.prepare_request(workflow_run_id) + raw_json = self._client._call_api(endpoint, request_obj=None) + return msgspec.convert(raw_json, WorkflowRunResponse, strict=False) + + @validate_arguments + def delete( + self, + workflow_name: str, + ) -> None: + """ + Archive (delete) the provided workflow. + + :param workflow_name: name of the workflow as displayed + in the UI (e.g: `atlan-snowflake-miner-1714638976`). + :raises AtlanError: on any API communication issue. + """ + use_package_endpoint = not self._client.role_cache.is_api_token_user() # type: ignore[attr-defined] + endpoint, _ = WorkflowDelete.prepare_request( + workflow_name, use_package_endpoint + ) + self._client._call_api(endpoint, request_obj=None) + + @overload + def add_schedule( + self, workflow: WorkflowResponse, workflow_schedule: WorkflowSchedule + ) -> WorkflowResponse: ... + + @overload + def add_schedule( + self, workflow: WorkflowPackage, workflow_schedule: WorkflowSchedule + ) -> WorkflowResponse: ... + + @overload + def add_schedule( + self, workflow: WorkflowSearchResult, workflow_schedule: WorkflowSchedule + ) -> WorkflowResponse: ... + + @overload + def add_schedule( + self, workflow: WorkflowSearchResultDetail, workflow_schedule: WorkflowSchedule + ) -> WorkflowResponse: ... + + def add_schedule( + self, + workflow: Union[ + WorkflowResponse, + WorkflowPackage, + WorkflowSearchResult, + WorkflowSearchResultDetail, + ], + workflow_schedule: WorkflowSchedule, + ) -> WorkflowResponse: + """ + Add a schedule for an existing workflow run. + + :param workflow: existing workflow run to schedule. + :param workflow_schedule: a WorkflowSchedule object containing: + - A cron schedule expression, e.g: `5 4 * * *`. + - The time zone for the cron schedule, e.g: `Europe/Paris`. + + :returns: a scheduled workflow. + :raises AtlanError: on any API communication issue. + """ + validate_type( + name="workflow", + _type=( + WorkflowResponse, + WorkflowPackage, + WorkflowSearchResult, + WorkflowSearchResultDetail, + ), + value=workflow, + ) + workflow_to_update = self._handle_workflow_types(workflow) + self._add_schedule(workflow_to_update, workflow_schedule) + use_package_endpoint = not self._client.role_cache.is_api_token_user() # type: ignore[attr-defined] + workflow_name = workflow_to_update.metadata and workflow_to_update.metadata.name + if use_package_endpoint: + endpoint = PACKAGE_WORKFLOW_UPDATE.format_path( + {"workflow_name": workflow_name} + ) + else: + endpoint = WORKFLOW_UPDATE.format_path({"workflow_name": workflow_name}) + raw_json = self._client._call_api(endpoint, request_obj=workflow_to_update) + return msgspec.convert(raw_json, WorkflowResponse, strict=False) + + @overload + def remove_schedule(self, workflow: WorkflowResponse) -> WorkflowResponse: ... + + @overload + def remove_schedule(self, workflow: WorkflowPackage) -> WorkflowResponse: ... + + @overload + def remove_schedule(self, workflow: WorkflowSearchResult) -> WorkflowResponse: ... + + @overload + def remove_schedule( + self, workflow: WorkflowSearchResultDetail + ) -> WorkflowResponse: ... + + def remove_schedule( + self, + workflow: Union[ + WorkflowResponse, + WorkflowPackage, + WorkflowSearchResult, + WorkflowSearchResultDetail, + ], + ) -> WorkflowResponse: + """ + Remove a scheduled run from an existing workflow run. + + :param workflow_run: existing workflow run to remove the schedule from. + :returns: a workflow. + :raises AtlanError: on any API communication issue. + """ + validate_type( + name="workflow", + _type=( + WorkflowResponse, + WorkflowPackage, + WorkflowSearchResult, + WorkflowSearchResultDetail, + ), + value=workflow, + ) + workflow_to_update = self._handle_workflow_types(workflow) + if workflow_to_update.metadata and workflow_to_update.metadata.annotations: + workflow_to_update.metadata.annotations.pop( + self._WORKFLOW_RUN_SCHEDULE, None + ) + use_package_endpoint = not self._client.role_cache.is_api_token_user() # type: ignore[attr-defined] + workflow_name = workflow_to_update.metadata and workflow_to_update.metadata.name + if use_package_endpoint: + endpoint = PACKAGE_WORKFLOW_UPDATE.format_path( + {"workflow_name": workflow_name} + ) + else: + endpoint = WORKFLOW_UPDATE.format_path({"workflow_name": workflow_name}) + raw_json = self._client._call_api(endpoint, request_obj=workflow_to_update) + return msgspec.convert(raw_json, WorkflowResponse, strict=False) + + def get_all_scheduled_runs(self) -> List[WorkflowScheduleResponse]: + """ + Get the details of scheduled run for all workflow. + + :returns: list of all the workflow schedules + :raises AtlanError: on any API communication issue + """ + endpoint, _ = WorkflowGetAllScheduledRuns.prepare_request() + raw_json = self._client._call_api(endpoint, request_obj=None) + items = raw_json.get("items") if raw_json else None + if not items: + return [] + return msgspec.convert(items, list[WorkflowScheduleResponse], strict=False) + + @validate_arguments + def get_scheduled_run(self, workflow_name: str) -> WorkflowScheduleResponse: + """ + Get the details of scheduled run for a specific workflow. + + :param workflow_name: name of the workflow for which we want the scheduled run details + :returns: details of the workflow schedule + :raises AtlanError: on any API communication issue + """ + endpoint, _ = WorkflowGetScheduledRun.prepare_request(workflow_name) + raw_json = self._client._call_api(endpoint, request_obj=None) + return msgspec.convert(raw_json, WorkflowScheduleResponse, strict=False) + + @validate_arguments + def find_schedule_query( + self, saved_query_id: str, max_results: int = 10 + ) -> List[WorkflowSearchResult]: + """ + Find scheduled query workflows by their saved query identifier. + + :param saved_query_id: identifier of the saved query. + :param max_results: maximum number of results to retrieve. Defaults to `10`. + :raises AtlanError: on any API communication issue. + :returns: a list of scheduled query workflows. + """ + query = Bool( + filter=[ + NestedQuery( + path="metadata", + query=Prefix( + field="metadata.name.keyword", + value=f"asq-{saved_query_id}", + ), + ), + NestedQuery( + path="metadata", + query=Term( + field="metadata.annotations.package.argoproj.io/name.keyword", + value="@atlan/schedule-query", + ), + ), + ] + ) + request = WorkflowSearchRequest( + query=query.to_dict(), size=max_results, sort=_DEFAULT_SORT + ) + raw_json = self._client._call_api(WORKFLOW_INDEX_SEARCH, request_obj=request) + response = msgspec.convert(raw_json, WorkflowSearchResponse, strict=False) + return response.hits and response.hits.hits or [] + + @validate_arguments + def re_run_schedule_query(self, schedule_query_id: str) -> WorkflowRunResponse: + """ + Re-run a scheduled query. + + :param schedule_query_id: ID of the scheduled query to re-run + :returns: the workflow run response + :raises AtlanError: on any API communication issue + """ + request = ReRunRequest(namespace="default", resource_name=schedule_query_id) + raw_json = self._client._call_api(WORKFLOW_OWNER_RERUN, request_obj=request) + return msgspec.convert(raw_json, WorkflowRunResponse, strict=False) + + @validate_arguments + def find_schedule_query_between( + self, + request: ScheduleQueriesSearchRequest, + missed: bool = False, + ) -> Optional[List[WorkflowRunResponse]]: + """ + Find scheduled query workflows within the specified duration. + + :param request: a `ScheduleQueriesSearchRequest` object containing + start and end dates in ISO 8601 format (e.g: `2024-03-25T16:30:00.000+05:30`). + :param missed: if `True`, perform a search for missed + scheduled query workflows. Defaults to `False`. + :raises AtlanError: on any API communication issue. + :returns: a list of scheduled query workflows found within the specified duration. + """ + endpoint, query_params = WorkflowFindScheduleQueryBetween.prepare_request( + request, missed + ) + raw_json = self._client._call_api(endpoint, query_params=query_params) + if not raw_json: + return None + if isinstance(raw_json, list): + return msgspec.convert(raw_json, list[WorkflowRunResponse], strict=False) + return msgspec.convert(raw_json, WorkflowRunResponse, strict=False) diff --git a/pyatlan_v9/errors.py b/pyatlan_v9/errors.py new file mode 100644 index 000000000..79be0c1b3 --- /dev/null +++ b/pyatlan_v9/errors.py @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. +"""Re-export all error classes from the legacy pyatlan.errors module.""" + +from pyatlan.errors import * # noqa: F401,F403 +from pyatlan.errors import ( # noqa: F401 — explicit re-exports for type checkers + ApiError, + AtlanError, + AuthenticationError, + ConflictError, + ErrorCode, + InvalidRequestError, + LogicError, + NotFoundError, + PermissionError, +) diff --git a/pyatlan_v9/model/__init__.py b/pyatlan_v9/model/__init__.py new file mode 100644 index 000000000..99fa88e4a --- /dev/null +++ b/pyatlan_v9/model/__init__.py @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +from __future__ import annotations + +# Re-export all asset models from the assets subpackage +from .assets import * # noqa: F401, F403 + +# Re-export all names from the assets __all__ +from .assets import __all__ as _assets_all + +__all__ = list(_assets_all) diff --git a/pyatlan_v9/model/aggregation.py b/pyatlan_v9/model/aggregation.py new file mode 100644 index 000000000..94ca32555 --- /dev/null +++ b/pyatlan_v9/model/aggregation.py @@ -0,0 +1,168 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2023 Atlan Pte. Ltd. + +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Union + +import msgspec + +from pyatlan.utils import validate_type + +if TYPE_CHECKING: + from pyatlan.model.fields.atlan_fields import AtlanField + + +class AggregationHitsResult(msgspec.Struct, kw_only=True): + """Captures the hit results from a bucket aggregation.""" + + class Stats(msgspec.Struct, kw_only=True): + """Statistics about the hits.""" + + value: Union[int, None] = None + """Number of search results that matched the hit value.""" + relation: Union[str, None] = None + """Comparison operation used to determine whether the values match.""" + + class Details(msgspec.Struct, kw_only=True): + """Details of an individual hit.""" + + index: Union[str, None] = msgspec.field(default=None, name="_index") + type: Union[str, None] = msgspec.field(default=None, name="_type") + id: Union[str, None] = msgspec.field(default=None, name="_id") + score: Union[int, None] = msgspec.field(default=None, name="_score") + source: Union[dict[str, Any], None] = msgspec.field( + default=None, name="_source" + ) + + class Hits(msgspec.Struct, kw_only=True): + """Details of the hits requested.""" + + total: Union[AggregationHitsResult.Stats, None] = None + max_score: Union[float, None] = None + hits: list[AggregationHitsResult.Details] = msgspec.field(default_factory=list) + + hits: AggregationHitsResult.Hits + + +class AggregationMetricResult(msgspec.Struct, kw_only=True): + """Captures the results from a metric aggregation.""" + + value: float + + +class AggregationBucketDetails(msgspec.Struct, kw_only=True): + """Captures the results of a single bucket within an aggregation.""" + + key: Any + doc_count: int + key_as_string: Union[str, None] = None + max_matching_length: Union[int, None] = None + to: Union[Any, None] = None + to_as_string: Union[str, None] = None + from_: Union[Any, None] = msgspec.field(default=None, name="from") + from_as_string: Union[str, None] = None + nested_results: Union[Aggregations, None] = None + + def __post_init__(self) -> None: + """Populate nested results from extra fields in the raw data.""" + # Note: In msgspec, nested aggregation results need to be handled + # during deserialization with a custom decoder hook. + pass + + def get_source_value(self, field: AtlanField) -> Union[str, None]: + """ + Returns the source value of the specified field for this bucket. + + :param field: in Atlan for which to retrieve the value + :returns: the value of the field in Atlan that + is represented within this bucket otherwise None + """ + from pyatlan.model.fields.atlan_fields import ( + AtlanField, + CustomMetadataField, + SearchableField, + ) + + validate_type(name="field", _type=AtlanField, value=field) + + if ( + self.nested_results + and SearchableField.EMBEDDED_SOURCE_VALUE in self.nested_results + ): + result = self.nested_results[SearchableField.EMBEDDED_SOURCE_VALUE] + if ( + isinstance(result, AggregationHitsResult) + and result.hits + and result.hits.hits + ): + details = result.hits.hits[0] + if details and details.source: + if isinstance(field, CustomMetadataField): + return details.source.get(field.elastic_field_name) + else: + return details.source.get(field.atlan_field_name) + return None + + +class AggregationBucketResult(msgspec.Struct, kw_only=True): + """Captures the results from a bucket aggregation.""" + + doc_count_error_upper_bound: int + sum_other_doc_count: int + buckets: list[AggregationBucketDetails] + + +class Aggregation(msgspec.Struct, kw_only=True): + """Single aggregation result.""" + + data: dict[str, Any] = msgspec.field(default_factory=dict) + + +class Aggregations: + """ + Aggregation results from a search. + + This is a dict-like wrapper around aggregation results that supports + iteration and key-based access, replacing the Pydantic __root__ pattern. + """ + + def __init__( + self, + data: Union[ + dict[ + str, + Union[ + AggregationMetricResult, + AggregationBucketResult, + AggregationHitsResult, + ], + ], + None, + ] = None, + ): + self._data: dict[ + str, + Union[ + AggregationMetricResult, + AggregationBucketResult, + AggregationHitsResult, + ], + ] = data or {} + + def __iter__(self): + return iter(self._data) + + def __getitem__(self, item): + return self._data[item] + + def __contains__(self, item): + return item in self._data + + def get( + self, key: str, default=None + ) -> Union[ + AggregationMetricResult, AggregationBucketResult, AggregationHitsResult, None + ]: + """Get an aggregation result by key.""" + return self._data.get(key, default) diff --git a/pyatlan_v9/model/aio/__init__.py b/pyatlan_v9/model/aio/__init__.py new file mode 100644 index 000000000..67668b18d --- /dev/null +++ b/pyatlan_v9/model/aio/__init__.py @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. +"""V9 async model wrappers using msgspec.""" diff --git a/pyatlan_v9/model/aio/audit.py b/pyatlan_v9/model/aio/audit.py new file mode 100644 index 000000000..242ef12e5 --- /dev/null +++ b/pyatlan_v9/model/aio/audit.py @@ -0,0 +1,207 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +from __future__ import annotations + +from typing import Any, AsyncGenerator, List, Optional, Set + +import msgspec + +from pyatlan.client.constants import AUDIT_SEARCH +from pyatlan.errors import ErrorCode +from pyatlan.model.enums import SortOrder +from pyatlan_v9.model.audit import AuditSearchRequest, EntityAudit +from pyatlan_v9.model.search import Bool, Query, Range, SortItem + +TOTAL_COUNT = "totalCount" +ENTITY_AUDITS = "entityAudits" + + +class AsyncAuditSearchResults: + """Async version of AuditSearchResults for paginated audit search.""" + + _DEFAULT_SIZE = 300 + _MASS_EXTRACT_THRESHOLD = 10000 - _DEFAULT_SIZE + + def __init__( + self, + client: Any, + criteria: AuditSearchRequest, + start: int, + size: int, + entity_audits: List[EntityAudit], + count: int, + bulk: bool = False, + aggregations: Optional[Any] = None, + ): + self._client = client + self._endpoint = AUDIT_SEARCH + self._criteria = criteria + self._start = start + self._size = size + self._entity_audits = entity_audits + self._count = count + self._approximate_count = count + self._bulk = bulk + self._aggregations = aggregations + self._first_record_creation_time = -2 + self._last_record_creation_time = -2 + self._processed_entity_keys: Set[str] = set() + + @property + def aggregations(self) -> Optional[Any]: + return self._aggregations + + @property + def total_count(self) -> int: + return self._count + + def current_page(self) -> List[EntityAudit]: + """Retrieve the current page of results.""" + return self._entity_audits + + async def next_page(self, start=None, size=None) -> bool: + """Indicates whether there is a next page of results.""" + self._start = start or self._start + self._size + is_bulk_search = ( + self._bulk or self._approximate_count > self._MASS_EXTRACT_THRESHOLD + ) + if size: + self._size = size + if is_bulk_search: + self._processed_entity_keys.update( + entity.event_key for entity in self._entity_audits + ) + return await self._get_next_page() if self._entity_audits else False + + async def _get_next_page(self): + query = self._criteria.dsl.query + self._criteria.dsl.size = self._size + self._criteria.dsl.from_ = self._start + is_bulk_search = ( + self._bulk or self._approximate_count > self._MASS_EXTRACT_THRESHOLD + ) + if is_bulk_search: + self._prepare_query_for_timestamp_paging(query) + if raw_json := await self._get_next_page_json(is_bulk_search): + self._count = raw_json.get(TOTAL_COUNT, 0) + return True + return False + + async def _get_next_page_json(self, is_bulk_search: bool = False): + raw_json = await self._client._call_api( + self._endpoint, + request_obj=self._criteria, + ) + if ENTITY_AUDITS not in raw_json or not raw_json[ENTITY_AUDITS]: + self._entity_audits = [] + return None + try: + from pyatlan_v9.client.audit import ( + _AUDIT_TS_FIELDS, + _normalize_ms_timestamps, + ) + + self._entity_audits = [ + msgspec.convert( + _normalize_ms_timestamps(audit, _AUDIT_TS_FIELDS), + EntityAudit, + strict=False, + ) + for audit in raw_json[ENTITY_AUDITS] + ] + if is_bulk_search: + self._filter_processed_entities() + self._update_first_last_record_creation_times() + return raw_json + except Exception as err: + raise ErrorCode.JSON_ERROR.exception_with_parameters( + raw_json, 200, str(err) + ) from err + + def _prepare_query_for_timestamp_paging(self, query: Query): + rewritten_filters = [] + if isinstance(query, Bool): + for filter_ in query.filter: + if self._is_paging_timestamp_query(filter_): + continue + rewritten_filters.append(filter_) + + if self._first_record_creation_time != self._last_record_creation_time: + rewritten_filters.append( + self._get_paging_timestamp_query(self._last_record_creation_time) + ) + if isinstance(query, Bool): + rewritten_query = Bool( + filter=rewritten_filters, + must=query.must, + must_not=query.must_not, + should=query.should, + boost=query.boost, + minimum_should_match=query.minimum_should_match, + ) + else: + rewritten_filters.append(query) + rewritten_query = Bool(filter=rewritten_filters) + self._criteria.dsl.from_ = 0 + self._criteria.dsl.query = rewritten_query + else: + if isinstance(query, Bool): + for filter_ in query.filter: + if self._is_paging_timestamp_query(filter_): + query.filter.remove(filter_) + self._criteria.dsl.from_ = len(self._processed_entity_keys) + + @staticmethod + def _get_paging_timestamp_query(last_timestamp: int) -> Query: + return Range(field="created", gte=last_timestamp) + + @staticmethod + def _is_paging_timestamp_query(filter_: Query) -> bool: + return ( + isinstance(filter_, Range) + and filter_.field == "created" + and filter_.gte is not None + ) + + def _update_first_last_record_creation_times(self): + self._first_record_creation_time = self._last_record_creation_time = -2 + if not isinstance(self._entity_audits, list) or len(self._entity_audits) <= 1: + return + first_audit, last_audit = self._entity_audits[0], self._entity_audits[-1] + if first_audit: + self._first_record_creation_time = first_audit.created + if last_audit: + self._last_record_creation_time = last_audit.created + + def _filter_processed_entities(self): + self._entity_audits = [ + entity + for entity in self._entity_audits + if entity is not None + and entity.event_key not in self._processed_entity_keys + ] + + @staticmethod + def presorted_by_timestamp(sorts) -> bool: + if sorts and isinstance(sorts[0], SortItem): + return sorts[0].field == "created" and sorts[0].order == SortOrder.ASCENDING + return False + + @staticmethod + def sort_by_timestamp_first(sorts) -> List[SortItem]: + creation_asc_sort = [SortItem("created", order=SortOrder.ASCENDING)] + if not sorts: + return creation_asc_sort + rewritten_sorts = [ + sort for sort in sorts if (not sort.field) or (sort.field != "__timestamp") + ] + return creation_asc_sort + rewritten_sorts + + async def __aiter__(self) -> AsyncGenerator[EntityAudit, None]: + """Iterate through all pages of results.""" + while True: + for audit in self.current_page(): + yield audit + if not await self.next_page(): + break diff --git a/pyatlan_v9/model/aio/core.py b/pyatlan_v9/model/aio/core.py new file mode 100644 index 000000000..4a39c1605 --- /dev/null +++ b/pyatlan_v9/model/aio/core.py @@ -0,0 +1,124 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. +""" +V9 async core models using msgspec. + +Uses v9-native async translators/retranslators that produce AtlanTagName +objects and camelCase keys directly, without post-processing workarounds. +""" + +from __future__ import annotations + +import json as json_lib +from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union + +import msgspec + +from pyatlan_v9.model.aio.retranslators import AsyncAtlanTagRetranslator +from pyatlan_v9.model.aio.translators import AsyncAtlanTagTranslator +from pyatlan_v9.model.core import AtlanTagName + +if TYPE_CHECKING: + from pyatlan_v9.client.aio.atlan import AsyncAtlanClient + + +def _enc_hook(obj: Any) -> Any: + """Handle custom types that msgspec cannot natively encode.""" + import datetime + + if isinstance(obj, AtlanTagName): + return str(obj) + from enum import Enum + + if isinstance(obj, Enum): + return obj.value + if isinstance(obj, datetime.date): + # Convert date to timestamp in milliseconds (epoch time) + dt = datetime.datetime.combine(obj, datetime.time.min) + return int(dt.timestamp() * 1000) + if isinstance(obj, datetime.datetime): + # Convert datetime to timestamp in milliseconds + return int(obj.timestamp() * 1000) + raise NotImplementedError(f"Cannot serialize {type(obj)}") + + +class AsyncAtlanResponse: + """ + Async wrapper for API responses with tag ID -> AtlanTagName translation. + """ + + def __init__(self, raw_json: Dict[str, Any], client: AsyncAtlanClient): + self.raw_json = raw_json + self.client = client + self.translators = [ + AsyncAtlanTagTranslator(client), + ] + self.translated: Optional[Union[Dict[str, Any], List[Any], Any]] = None + + async def translate(self) -> Union[Dict[str, Any], List[Any], Any]: + self.translated = await self._deep_translate(self.raw_json) + return self.translated + + async def _deep_translate( + self, data: Union[Dict[str, Any], List[Any], Any] + ) -> Union[Dict[str, Any], List[Any], Any]: + if isinstance(data, dict): + for translator in self.translators: + if translator.applies_to(data): + data = await translator.translate(data) + return { + key: await self._deep_translate(value) for key, value in data.items() + } + elif isinstance(data, list): + return [await self._deep_translate(item) for item in data] + else: + return data + + async def to_dict(self) -> Union[Dict[str, Any], List[Any], Any]: + if self.translated is None: + await self.translate() + return self.translated + + +class AsyncAtlanRequest: + """ + Async wrapper for requests with AtlanTagName -> tag ID retranslation. + """ + + def __init__(self, instance: Any, client: AsyncAtlanClient): + self.client = client + self.instance = instance + self.retranslators = [ + AsyncAtlanTagRetranslator(client), + ] + self.translated = None + + async def retranslate(self) -> Any: + if isinstance(self.instance, (dict, list)): + parsed = self.instance + elif hasattr(self.instance, "to_json") and callable(self.instance.to_json): + parsed = json_lib.loads(self.instance.to_json(nested=True)) + elif hasattr(self.instance, "to_dict") and callable(self.instance.to_dict): + parsed = self.instance.to_dict() + else: + parsed = msgspec.to_builtins(self.instance, enc_hook=_enc_hook) + + self.translated = await self._deep_retranslate(parsed) + return self.translated + + async def _deep_retranslate(self, data: Any) -> Any: + if isinstance(data, dict): + for retranslator in self.retranslators: + if retranslator.applies_to(data): + data = await retranslator.retranslate(data) + return { + key: await self._deep_retranslate(value) for key, value in data.items() + } + elif isinstance(data, list): + return [await self._deep_retranslate(item) for item in data] + return data + + async def json(self, **kwargs) -> str: + if self.translated is None: + await self.retranslate() + return json_lib.dumps(self.translated, **kwargs) diff --git a/pyatlan_v9/model/aio/custom_metadata.py b/pyatlan_v9/model/aio/custom_metadata.py new file mode 100644 index 000000000..d158c93fb --- /dev/null +++ b/pyatlan_v9/model/aio/custom_metadata.py @@ -0,0 +1,231 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +from __future__ import annotations + +from collections import UserDict +from typing import TYPE_CHECKING, Any, Union + +from pyatlan.errors import NotFoundError +from pyatlan.model.constants import DELETED_, DELETED_SENTINEL + +if TYPE_CHECKING: + from pyatlan.client.aio.client import AsyncAtlanClient + + +class AsyncCustomMetadataDict(UserDict): + """ + Async version of CustomMetadataDict for manipulating custom metadata + attributes using human-readable names. + + Recommended usage: + custom_metadata = await AsyncCustomMetadataDict.creator(client=client, name="set_name") + custom_metadata["attribute_name"] = "value" + """ + + _sentinel: Union[AsyncCustomMetadataDict, None] = None + + def __new__(cls, *args, **kwargs): + if args and args[0] == DELETED_SENTINEL and cls._sentinel: + return cls._sentinel + obj = super().__new__(cls) + super().__init__(obj) + if args and args[0] == DELETED_SENTINEL: + obj._name = DELETED_ + obj._modified = False + obj._names: set[str] = set() + cls._sentinel = obj + return obj + + @property + def attribute_names(self) -> set[str]: + """Names of all attributes in this custom metadata set.""" + return self._names + + async def __ainit__(self, client: AsyncAtlanClient, name: str): + """Async init with human-readable name of custom metadata set.""" + super().__init__() + self._name = name + self._modified = False + self._client = client + _id = await self._client.custom_metadata_cache.get_id_for_name(name) + attr_map = await self._client.custom_metadata_cache.get_attr_map_for_id(_id) + self._names = { + value + for key, value in attr_map.items() + if not await self._client.custom_metadata_cache.is_attr_archived( + attr_id=key + ) + } + + @classmethod + async def creator( + cls, client: AsyncAtlanClient, name: str + ) -> AsyncCustomMetadataDict: + """Create and initialize an AsyncCustomMetadataDict instance.""" + instance = cls() + await instance.__ainit__(client, name) + return instance + + @classmethod + def get_deleted_sentinel(cls) -> AsyncCustomMetadataDict: + """Return a sentinel representing deleted custom metadata.""" + if cls._sentinel is not None: + return cls._sentinel + return cls.__new__(cls, DELETED_SENTINEL) + + @property + def modified(self) -> bool: + """Whether the set has been modified from its initial values.""" + return self._modified + + def __setitem__(self, key: str, value): + if key not in self._names: + raise KeyError(f"'{key}' is not a valid property name for {self._name}") + self._modified = True + self.data[key] = value + + def __getitem__(self, key: str): + if key not in self._names: + raise KeyError(f"'{key}' is not a valid property name for {self._name}") + return None if key not in self.data else self.data[key] + + def clear_all(self): + """Set all available properties explicitly to None.""" + for attribute_name in self._names: + self.data[attribute_name] = None + self._modified = True + + def clear_unset(self): + """Set all properties that haven't been set to None.""" + for name in self.attribute_names: + if name not in self.data: + self.data[name] = None + + def is_set(self, key: str) -> bool: + """Whether the given property has been set in the metadata set.""" + if key not in self._names: + raise KeyError(f"'{key}' is not a valid property name for {self._name}") + return key in self.data + + async def business_attributes(self) -> dict[str, Any]: + """Return the metadata set with names resolved to their internal values.""" + result = {} + for key, value in self.data.items(): + attr_id = await self._client.custom_metadata_cache.get_attr_id_for_name( + self._name, key + ) + result[attr_id] = value + return result + + +class AsyncCustomMetadataProxy: + """Async proxy for accessing and managing custom metadata on an asset.""" + + def __init__( + self, + client: AsyncAtlanClient, + business_attributes: Union[dict[str, Any], None], + ): + self._client = client + self._metadata: Union[dict[str, AsyncCustomMetadataDict], None] = None + self._business_attributes = business_attributes + self._modified = False + + async def _initialize_metadata(self): + """Initialize metadata from business_attributes if needed.""" + if self._business_attributes is None or self._metadata is not None: + return + + self._metadata = {} + for cm_id, cm_attributes in self._business_attributes.items(): + try: + cm_name = await self._client.custom_metadata_cache.get_name_for_id( + cm_id + ) + attribs = AsyncCustomMetadataDict() + await attribs.__ainit__(name=cm_name, client=self._client) + for attr_id, properties in cm_attributes.items(): + attr_name = ( + await self._client.custom_metadata_cache.get_attr_name_for_id( + cm_id, attr_id + ) + ) + if not await self._client.custom_metadata_cache.is_attr_archived( + attr_id=attr_id + ): + attribs[attr_name] = properties + attribs._modified = False + except NotFoundError: + cm_name = DELETED_ + attribs = AsyncCustomMetadataDict.get_deleted_sentinel() + self._metadata[cm_name] = attribs + + async def get_custom_metadata(self, name: str) -> AsyncCustomMetadataDict: + """Get or create a custom metadata set by name.""" + await self._initialize_metadata() + if self._metadata is None: + self._metadata = {} + if name not in self._metadata: + attribs = AsyncCustomMetadataDict() + await attribs.__ainit__(name=name, client=self._client) + self._metadata[name] = attribs + return self._metadata[name] + + async def set_custom_metadata(self, custom_metadata: AsyncCustomMetadataDict): + """Set a custom metadata set.""" + await self._initialize_metadata() + if self._metadata is None: + self._metadata = {} + self._metadata[custom_metadata._name] = custom_metadata + self._modified = True + + @property + def modified(self) -> bool: + """Whether any custom metadata has been modified.""" + if self._modified: + return True + if self._metadata is None: + return False + return any(metadata_dict.modified for metadata_dict in self._metadata.values()) + + async def business_attributes(self) -> Union[dict[str, Any], None]: + """Return the business attributes in internal format.""" + await self._initialize_metadata() + if self.modified and self._metadata is not None: + result = {} + for key, value in self._metadata.items(): + cm_id = await self._client.custom_metadata_cache.get_id_for_name(key) + result[cm_id] = await value.business_attributes() + return result + return self._business_attributes + + +class AsyncCustomMetadataRequest: + """Async request to update custom metadata on an asset.""" + + def __init__(self, data: dict[str, Any], set_id: str): + self._data = data + self._set_id = set_id + + @classmethod + async def create( + cls, custom_metadata_dict: AsyncCustomMetadataDict + ) -> AsyncCustomMetadataRequest: + """Create a request from an AsyncCustomMetadataDict.""" + business_attrs = await custom_metadata_dict.business_attributes() + set_id = await ( + custom_metadata_dict._client.custom_metadata_cache.get_id_for_name( + custom_metadata_dict._name + ) + ) + return cls(data=business_attrs, set_id=set_id) + + @property + def custom_metadata_set_id(self) -> str: + """Unique identifier of the custom metadata set.""" + return self._set_id + + def to_dict(self) -> dict[str, Any]: + """Return the underlying data dict.""" + return self._data diff --git a/pyatlan_v9/model/aio/group.py b/pyatlan_v9/model/aio/group.py new file mode 100644 index 000000000..3e37f658e --- /dev/null +++ b/pyatlan_v9/model/aio/group.py @@ -0,0 +1,68 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +from __future__ import annotations + +from typing import Any, AsyncGenerator, Union + +import msgspec + +from pyatlan.errors import ErrorCode +from pyatlan_v9.model.group import AtlanGroup + + +class AsyncGroupResponse(msgspec.Struct, kw_only=True, rename="camel"): + """Async version of GroupResponse with async pagination support.""" + + total_record: Union[int, None] = None + """Total number of groups.""" + filter_record: Union[int, None] = None + """Number of groups in the filtered response.""" + records: Union[list[AtlanGroup], None] = msgspec.field(default_factory=list) + """Details of each group included in the response.""" + + _size: int = 20 + _start: int = 0 + _endpoint: Any = None + _client: Any = None + _criteria: Any = None + + def current_page(self) -> list[AtlanGroup]: + """Return the current page of group results.""" + return self.records or [] + + async def next_page(self, start=None, size=None) -> bool: + """Advance to the next page of results.""" + self._start = start or self._start + self._size + if size: + self._size = size + return await self._get_next_page() if self.records else False + + async def _get_next_page(self) -> bool: + """Fetch the next page of results.""" + self._criteria.offset = self._start + self._criteria.limit = self._size + raw_json = await self._client._call_api( + api=self._endpoint.format_path_with_params(), + query_params=self._criteria.query_params, + ) + if not raw_json.get("records"): + self.records = [] + return False + try: + self.records = msgspec.convert( + raw_json.get("records"), list[AtlanGroup], strict=False + ) + except Exception as err: + raise ErrorCode.JSON_ERROR.exception_with_parameters( + raw_json, 200, str(err) + ) from err + return True + + async def __aiter__(self) -> AsyncGenerator[AtlanGroup, None]: + """Async iterator for groups across all pages.""" + while self.records: + for group in self.records: + yield group + if not await self.next_page(): + break diff --git a/pyatlan_v9/model/aio/keycloak_events.py b/pyatlan_v9/model/aio/keycloak_events.py new file mode 100644 index 000000000..5dd9e4f50 --- /dev/null +++ b/pyatlan_v9/model/aio/keycloak_events.py @@ -0,0 +1,118 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +from __future__ import annotations + +from typing import AsyncGenerator + +import msgspec + +from pyatlan.client.constants import ADMIN_EVENTS, KEYCLOAK_EVENTS +from pyatlan_v9.model.keycloak_events import ( + AdminEvent, + AdminEventRequest, + KeycloakEvent, + KeycloakEventRequest, +) + + +class AsyncKeycloakEventResponse: + """Async paginated response for Keycloak events.""" + + def __init__( + self, + client, + criteria: KeycloakEventRequest, + start: int, + size: int, + events: list[KeycloakEvent], + ): + self._client = client + self._criteria = criteria + self._start = start + self._size = size + self._events = events + + def current_page(self) -> list[KeycloakEvent]: + """Return the current page of events.""" + return self._events + + async def next_page(self, start=None, size=None) -> bool: + """Advance to the next page of results.""" + self._start = start or self._start + self._size + if size: + self._size = size + return await self._get_next_page() if self._events else False + + async def _get_next_page(self) -> bool: + """Fetch the next page of results.""" + self._criteria.offset = self._start + self._criteria.size = self._size + raw_json = await self._client._call_api( + KEYCLOAK_EVENTS, + query_params=self._criteria.query_params, + ) + if not raw_json: + self._events = [] + return False + self._events = msgspec.convert(raw_json, list[KeycloakEvent], strict=False) + return True + + async def __aiter__(self) -> AsyncGenerator[KeycloakEvent, None]: + """Iterate through all pages of results.""" + while True: + for event in self.current_page(): + yield event + if not await self.next_page(): + break + + +class AsyncAdminEventResponse: + """Async paginated response for admin events.""" + + def __init__( + self, + client, + criteria: AdminEventRequest, + start: int, + size: int, + events: list[AdminEvent], + ): + self._client = client + self._criteria = criteria + self._start = start + self._size = size + self._events = events + + def current_page(self) -> list[AdminEvent]: + """Return the current page of events.""" + return self._events + + async def next_page(self, start=None, size=None) -> bool: + """Advance to the next page of results.""" + self._start = start or self._start + self._size + if size: + self._size = size + return await self._get_next_page() if self._events else False + + async def _get_next_page(self) -> bool: + """Fetch the next page of results.""" + self._criteria.offset = self._start + self._criteria.size = self._size + raw_json = await self._client._call_api( + ADMIN_EVENTS, + query_params=self._criteria.query_params, + ) + if not raw_json: + self._events = [] + return False + self._events = msgspec.convert(raw_json, list[AdminEvent], strict=False) + return True + + async def __aiter__(self) -> AsyncGenerator[AdminEvent, None]: + """Iterate through all pages of results.""" + while True: + for event in self.current_page(): + yield event + if not await self.next_page(): + break diff --git a/pyatlan_v9/model/aio/oauth_client.py b/pyatlan_v9/model/aio/oauth_client.py new file mode 100644 index 000000000..73473eb41 --- /dev/null +++ b/pyatlan_v9/model/aio/oauth_client.py @@ -0,0 +1,75 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2026 Atlan Pte. Ltd. + +from __future__ import annotations + +from typing import Any, AsyncGenerator, Union + +import msgspec + +from pyatlan.errors import ErrorCode +from pyatlan_v9.model.oauth_client import OAuthClientResponse + + +class AsyncOAuthClientListResponse(msgspec.Struct, kw_only=True, rename="camel"): + """Async version of OAuthClientListResponse with async pagination support.""" + + total_record: Union[int, None] = None + """Total number of OAuth clients.""" + filter_record: Union[int, None] = None + """Number of OAuth clients that matched the specified filters.""" + records: Union[list[OAuthClientResponse], None] = None + """List of OAuth clients.""" + + _size: int = 20 + _start: int = 0 + _endpoint: Any = None + _client: Any = None + _sort: Any = None + + def current_page(self) -> list[OAuthClientResponse]: + """Get the current page of OAuth clients.""" + return self.records or [] + + async def next_page( + self, start: Union[int, None] = None, size: Union[int, None] = None + ) -> bool: + """Advance to the next page of results.""" + self._start = start or self._start + self._size + if size: + self._size = size + return await self._get_next_page() if self.records else False + + async def _get_next_page(self) -> bool: + """Fetch the next page of results.""" + query_params: dict[str, str] = { + "count": "true", + "offset": str(self._start), + "limit": str(self._size), + } + if self._sort is not None: + query_params["sort"] = self._sort + raw_json = await self._client._call_api( + api=self._endpoint, + query_params=query_params, + ) + if not raw_json.get("records"): + self.records = [] + return False + try: + self.records = msgspec.convert( + raw_json.get("records"), list[OAuthClientResponse], strict=False + ) + except Exception as err: + raise ErrorCode.JSON_ERROR.exception_with_parameters( + raw_json, 200, str(err) + ) from err + return True + + async def __aiter__(self) -> AsyncGenerator[OAuthClientResponse, None]: + """Async iterate over all OAuth clients across all pages.""" + while self.records: + for record in self.records: + yield record + if not await self.next_page(): + break diff --git a/pyatlan_v9/model/aio/retranslators.py b/pyatlan_v9/model/aio/retranslators.py new file mode 100644 index 000000000..37f58b432 --- /dev/null +++ b/pyatlan_v9/model/aio/retranslators.py @@ -0,0 +1,97 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2026 Atlan Pte. Ltd. + +"""Async request retranslators for pyatlan_v9.""" + +from __future__ import annotations + +from abc import ABC, abstractmethod +from typing import Any + +import msgspec + +from pyatlan.model.constants import DELETED_ +from pyatlan_v9.model.structs import SourceTagAttachment + + +class AsyncBaseRetranslator(ABC): + """Abstract async request retranslator.""" + + @abstractmethod + def applies_to(self, data: dict[str, Any]) -> bool: + """Return whether this retranslator should process this dictionary.""" + + @abstractmethod + async def retranslate(self, data: dict[str, Any]) -> dict[str, Any]: + """Retranslate dictionary values back into backend-compatible format.""" + + +class AsyncAtlanTagRetranslator(AsyncBaseRetranslator): + """ + Async retranslator that converts human-readable tag names back to tag IDs, + with camelCase keys for msgspec structs. + """ + + _TYPE_NAME = "typeName" + _SOURCE_ATTACHMENTS = "sourceTagAttachments" + _CLASSIFICATION_NAMES = {"classificationNames", "purposeClassifications"} + _CLASSIFICATION_KEYS = { + "classifications", + "addOrUpdateClassifications", + "removeClassifications", + } + + def __init__(self, client: Any): + self.client = client + + def applies_to(self, data: dict[str, Any]) -> bool: + return any(key in data for key in self._CLASSIFICATION_NAMES) or any( + key in data for key in self._CLASSIFICATION_KEYS + ) + + def _attachment_to_dict(self, attachment: Any) -> dict[str, Any]: + if isinstance(attachment, SourceTagAttachment): + attrs = msgspec.to_builtins(attachment) + else: + attrs = msgspec.convert(attachment, type=dict[str, Any]) + return { + "typeName": "SourceTagAttachment", + "attributes": attrs, + } + + async def retranslate(self, data: dict[str, Any]) -> dict[str, Any]: + translated = data.copy() + + for key in self._CLASSIFICATION_NAMES: + if key in translated: + tag_ids = [] + for name in translated[key]: + tag_id = await self.client.atlan_tag_cache.get_id_for_name( + str(name) + ) + tag_ids.append(tag_id or DELETED_) + translated[key] = tag_ids + + for key in self._CLASSIFICATION_KEYS: + if key not in translated: + continue + for classification in translated[key]: + tag_name = str(classification.get(self._TYPE_NAME)) + if not tag_name: + continue + tag_id = await self.client.atlan_tag_cache.get_id_for_name(tag_name) + classification[self._TYPE_NAME] = tag_id if tag_id else DELETED_ + + attachments = classification.pop(self._SOURCE_ATTACHMENTS, None) + if not attachments or not tag_id: + continue + attr_id = await self.client.atlan_tag_cache.get_source_tags_attr_id( + tag_id + ) + if not attr_id: + continue + classification.setdefault("attributes", {})[attr_id] = [ + self._attachment_to_dict(attachment) for attachment in attachments + ] + + return translated diff --git a/pyatlan_v9/model/aio/search_log.py b/pyatlan_v9/model/aio/search_log.py new file mode 100644 index 000000000..8ed7f2395 --- /dev/null +++ b/pyatlan_v9/model/aio/search_log.py @@ -0,0 +1,195 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +from __future__ import annotations + +from typing import Any, AsyncGenerator, Dict, List, Optional + +import msgspec + +from pyatlan.client.constants import SEARCH_LOG +from pyatlan.errors import ErrorCode +from pyatlan.model.enums import SortOrder +from pyatlan_v9.model.search import Bool, Query, Range, SortItem +from pyatlan_v9.model.search_log import SearchLogEntry, SearchLogRequest + + +class AsyncSearchLogResults: + """Async version of SearchLogResults for paginated search log results.""" + + _DEFAULT_SIZE = 300 + _MASS_EXTRACT_THRESHOLD = 10000 - _DEFAULT_SIZE + + def __init__( + self, + client: Any, + criteria: SearchLogRequest, + start: int, + size: int, + count: int, + log_entries: List[SearchLogEntry], + aggregations: Dict, + bulk: bool = False, + processed_log_entries_count: int = 0, + ): + self._client = client + self._endpoint = SEARCH_LOG + self._criteria = criteria + self._start = start + self._size = size + self._log_entries = log_entries + self._count = count + self._approximate_count = count + self._aggregations = aggregations + self._bulk = bulk + self._first_record_creation_time = -2 + self._last_record_creation_time = -2 + self._duplicate_timestamp_page_count: int = 0 + self._processed_log_entries_count: int = processed_log_entries_count + + @property + def count(self) -> int: + return self._count + + def current_page(self) -> List[SearchLogEntry]: + """Retrieve the current page of results.""" + return self._log_entries + + async def next_page(self, start=None, size=None) -> bool: + """Indicates whether there is a next page of results.""" + self._start = start or self._start + self._size + if size: + self._size = size + return await self._get_next_page() if self._log_entries else False + + async def _get_next_page(self): + query = self._criteria.dsl.query + self._criteria.dsl.from_ = self._start + self._criteria.dsl.size = self._size + is_bulk_search = ( + self._bulk or self._approximate_count > self._MASS_EXTRACT_THRESHOLD + ) + if is_bulk_search: + self._prepare_query_for_timestamp_paging(query) + if raw_json := await self._get_next_page_json(is_bulk_search): + self._count = raw_json.get("approximateCount", 0) + return True + return False + + async def _get_next_page_json(self, is_bulk_search: bool = False): + raw_json = await self._client._call_api( + self._endpoint, + request_obj=self._criteria, + ) + if "logs" not in raw_json or not raw_json["logs"]: + self._log_entries = [] + return None + try: + from pyatlan_v9.client.search_log import ( + _LOG_TS_FIELDS, + _normalize_ms_timestamps_copy, + ) + + self._log_entries = [ + msgspec.convert( + _normalize_ms_timestamps_copy(entry, _LOG_TS_FIELDS), + SearchLogEntry, + strict=False, + ) + for entry in raw_json["logs"] + ] + self._processed_log_entries_count += len(self._log_entries) + if is_bulk_search: + self._update_first_last_record_creation_times() + return raw_json + except Exception as err: + raise ErrorCode.JSON_ERROR.exception_with_parameters( + raw_json, 200, str(err) + ) from err + + def _prepare_query_for_timestamp_paging(self, query: Query): + self._criteria.dsl.from_ = 0 + rewritten_filters = [] + if isinstance(query, Bool): + for filter_ in query.filter: + if self._is_paging_timestamp_query(filter_): + continue + rewritten_filters.append(filter_) + + if self._first_record_creation_time != self._last_record_creation_time: + self._duplicate_timestamp_page_count = 0 + rewritten_filters.append( + self._get_paging_timestamp_query(self._last_record_creation_time) + ) + if isinstance(query, Bool): + rewritten_query = Bool( + filter=rewritten_filters, + must=query.must, + must_not=query.must_not, + should=query.should, + boost=query.boost, + minimum_should_match=query.minimum_should_match, + ) + else: + rewritten_filters.append(query) + rewritten_query = Bool(filter=rewritten_filters) + self._criteria.dsl.query = rewritten_query + else: + self._criteria.dsl.from_ = self._size * ( + self._duplicate_timestamp_page_count + 1 + ) + self._criteria.dsl.size = self._size + self._duplicate_timestamp_page_count += 1 + + @staticmethod + def _get_paging_timestamp_query(last_timestamp: int) -> Query: + return Range(field="createdAt", gt=last_timestamp) + + @staticmethod + def _is_paging_timestamp_query(filter_: Query) -> bool: + return ( + isinstance(filter_, Range) + and filter_.field == "createdAt" + and filter_.gt is not None + ) + + def _update_first_last_record_creation_times(self): + self._first_record_creation_time = self._last_record_creation_time = -2 + if not isinstance(self._log_entries, list) or len(self._log_entries) <= 1: + return + first_entry, last_entry = self._log_entries[0], self._log_entries[-1] + if first_entry: + self._first_record_creation_time = first_entry.created_at + if last_entry: + self._last_record_creation_time = last_entry.created_at + + @staticmethod + def presorted_by_timestamp(sorts: Optional[list]) -> bool: + if sorts and isinstance(sorts[0], SortItem): + return ( + sorts[0].field == "createdAt" and sorts[0].order == SortOrder.ASCENDING + ) + return False + + @staticmethod + def sort_by_timestamp_first(sorts: Optional[list]) -> List[SortItem]: + from pyatlan_v9.model.search_log import BY_TIMESTAMP + + creation_asc_sort = [SortItem("createdAt", order=SortOrder.ASCENDING)] + if not sorts: + return creation_asc_sort + rewritten_sorts = [ + sort + for sort in sorts + if ((not sort.field) or (sort.field != "__timestamp")) + and (sort not in BY_TIMESTAMP) + ] + return creation_asc_sort + rewritten_sorts + + async def __aiter__(self) -> AsyncGenerator[SearchLogEntry, None]: + """Iterate through all pages of results.""" + while True: + for entry in self.current_page(): + yield entry + if not await self.next_page(): + break diff --git a/pyatlan_v9/model/aio/task.py b/pyatlan_v9/model/aio/task.py new file mode 100644 index 000000000..95a40996a --- /dev/null +++ b/pyatlan_v9/model/aio/task.py @@ -0,0 +1,87 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +from __future__ import annotations + +from typing import Any, AsyncGenerator, Union + +import msgspec + +from pyatlan.errors import ErrorCode +from pyatlan_v9.model.task import AtlanTask, TaskSearchRequest + + +class AsyncTaskSearchResponse: + """Async version of TaskSearchResponse for paginated task results.""" + + def __init__( + self, + client: Any, + endpoint: Any, + criteria: TaskSearchRequest, + start: int, + size: int, + count: int, + tasks: list[AtlanTask], + aggregations: Any, + ): + self._client = client + self._endpoint = endpoint + self._criteria = criteria + self._start = start + self._size = size + self._count = count + self._tasks = tasks + self._aggregations = aggregations + + @property + def count(self) -> int: + """Total count of matching tasks.""" + return self._count + + def current_page(self) -> list[AtlanTask]: + """Retrieve the current page of results.""" + return self._tasks + + async def next_page(self, start=None, size=None) -> bool: + """Advance to the next page of results.""" + self._start = start or self._start + self._size + if size: + self._size = size + return await self._get_next_page() if self._tasks else False + + async def _get_next_page(self) -> bool: + """Fetch the next page of results.""" + self._criteria.dsl.from_ = self._start + self._criteria.dsl.size = self._size + if raw_json := await self._get_next_page_json(): + self._count = raw_json.get("approximateCount", 0) + return True + return False + + async def _get_next_page_json(self) -> Union[dict, None]: + """Fetch the next page of results and return raw JSON.""" + raw_json = await self._client._call_api( + self._endpoint, + request_obj=self._criteria, + ) + if "tasks" not in raw_json or not raw_json["tasks"]: + self._tasks = [] + return None + try: + self._tasks = msgspec.convert( + raw_json["tasks"], list[AtlanTask], strict=False + ) + return raw_json + except Exception as err: + raise ErrorCode.JSON_ERROR.exception_with_parameters( + raw_json, 200, str(err) + ) from err + + async def __aiter__(self) -> AsyncGenerator[AtlanTask, None]: + """Iterate through all pages of results.""" + while True: + for task in self.current_page(): + yield task + if not await self.next_page(): + break diff --git a/pyatlan_v9/model/aio/translators.py b/pyatlan_v9/model/aio/translators.py new file mode 100644 index 000000000..c3791365d --- /dev/null +++ b/pyatlan_v9/model/aio/translators.py @@ -0,0 +1,93 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2026 Atlan Pte. Ltd. + +"""Async response translators for pyatlan_v9.""" + +from __future__ import annotations + +from abc import ABC, abstractmethod +from typing import Any + +import msgspec + +from pyatlan.model.constants import DELETED_ +from pyatlan_v9.model.structs import SourceTagAttachment + + +class AsyncBaseTranslator(ABC): + """Abstract async response translator.""" + + @abstractmethod + def applies_to(self, data: dict[str, Any]) -> bool: + """Return whether this translator should process this dictionary.""" + + @abstractmethod + async def translate(self, data: dict[str, Any]) -> dict[str, Any]: + """Translate the dictionary to a more user-friendly representation.""" + + +class AsyncAtlanTagTranslator(AsyncBaseTranslator): + """ + Async translator that converts tag IDs into human-readable Atlan tag names + wrapped in AtlanTagName objects, with camelCase keys for msgspec structs. + """ + + _TAG_ID = "tagId" + _TYPE_NAME = "typeName" + _SOURCE_ATTACHMENTS = "sourceTagAttachments" + _CLASSIFICATION_NAMES = {"classificationNames", "purposeClassifications"} + _CLASSIFICATION_KEYS = { + "classifications", + "addOrUpdateClassifications", + "removeClassifications", + } + + def __init__(self, client: Any): + self.client = client + + def applies_to(self, data: dict[str, Any]) -> bool: + return any(key in data for key in self._CLASSIFICATION_NAMES) or any( + key in data for key in self._CLASSIFICATION_KEYS + ) + + async def translate(self, data: dict[str, Any]) -> dict[str, Any]: + from pyatlan_v9.model.core import AtlanTagName + + raw_json = data.copy() + + for key in self._CLASSIFICATION_NAMES: + if key in raw_json: + tag_names = [] + for tag_id in raw_json[key]: + name = await self.client.atlan_tag_cache.get_name_for_id(tag_id) + tag_names.append(AtlanTagName(name or DELETED_)) + raw_json[key] = tag_names + + for key in self._CLASSIFICATION_KEYS: + if key not in raw_json: + continue + for classification in raw_json[key]: + tag_id = classification.get(self._TYPE_NAME) + if not tag_id: + continue + tag_name = await self.client.atlan_tag_cache.get_name_for_id(tag_id) + classification[self._TYPE_NAME] = AtlanTagName( + tag_name if tag_name else DELETED_ + ) + classification[self._TAG_ID] = tag_id + + attr_id = await self.client.atlan_tag_cache.get_source_tags_attr_id( + tag_id + ) + if not attr_id: + continue + attributes = classification.get("attributes") + if not attributes or not attributes.get(attr_id): + continue + classification[self._SOURCE_ATTACHMENTS] = [ + msgspec.convert(source_tag["attributes"], type=SourceTagAttachment) + for source_tag in attributes.get(attr_id) + if isinstance(source_tag, dict) and source_tag.get("attributes") + ] + + return raw_json diff --git a/pyatlan_v9/model/aio/user.py b/pyatlan_v9/model/aio/user.py new file mode 100644 index 000000000..4d8912aa5 --- /dev/null +++ b/pyatlan_v9/model/aio/user.py @@ -0,0 +1,68 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +from __future__ import annotations + +from typing import Any, AsyncGenerator, Union + +import msgspec + +from pyatlan.errors import ErrorCode +from pyatlan_v9.model.user import AtlanUser + + +class AsyncUserResponse(msgspec.Struct, kw_only=True, rename="camel"): + """Async version of UserResponse with async pagination support.""" + + total_record: Union[int, None] = None + """Total number of users.""" + filter_record: Union[int, None] = None + """Number of users in the filtered response.""" + records: Union[list[AtlanUser], None] = msgspec.field(default_factory=list) + """Details of each user included in the response.""" + + _size: int = 20 + _start: int = 0 + _endpoint: Any = None + _client: Any = None + _criteria: Any = None + + def current_page(self) -> list[AtlanUser]: + """Return the current page of user results.""" + return self.records or [] + + async def next_page(self, start=None, size=None) -> bool: + """Advance to the next page of results.""" + self._start = start or self._start + self._size + if size: + self._size = size + return await self._get_next_page() if self.records else False + + async def _get_next_page(self) -> bool: + """Fetch the next page of results.""" + self._criteria.offset = self._start + self._criteria.limit = self._size + raw_json = await self._client._call_api( + api=self._endpoint.format_path_with_params(), + query_params=self._criteria.query_params, + ) + if not raw_json.get("records"): + self.records = [] + return False + try: + self.records = msgspec.convert( + raw_json.get("records"), list[AtlanUser], strict=False + ) + except Exception as err: + raise ErrorCode.JSON_ERROR.exception_with_parameters( + raw_json, 200, str(err) + ) from err + return True + + async def __aiter__(self) -> AsyncGenerator[AtlanUser, None]: + """Async iterator for users across all pages.""" + while self.records: + for user in self.records: + yield user + if not await self.next_page(): + break diff --git a/pyatlan_v9/model/aio/workflow.py b/pyatlan_v9/model/aio/workflow.py new file mode 100644 index 000000000..b43eb9de2 --- /dev/null +++ b/pyatlan_v9/model/aio/workflow.py @@ -0,0 +1,81 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +from __future__ import annotations + +from typing import Any, AsyncGenerator, Union + +import msgspec + +from pyatlan.errors import ErrorCode +from pyatlan_v9.model.workflow import ( + WorkflowSearchHits, + WorkflowSearchRequest, + WorkflowSearchResult, +) + + +class AsyncWorkflowSearchResponse(msgspec.Struct, kw_only=True, rename="camel"): + """Async version of WorkflowSearchResponse with async pagination support.""" + + took: Union[int, None] = None + hits: Union[WorkflowSearchHits, None] = None + shards: Union[dict[str, Any], None] = msgspec.field(default=None, name="_shards") + + _size: int = 10 + _start: int = 0 + _endpoint: Any = None + _client: Any = None + _criteria: Any = None + + @property + def count(self) -> int: + """Total count of workflow search results.""" + return self.hits.total.get("value", 0) if self.hits and self.hits.total else 0 + + def current_page(self) -> Union[list[WorkflowSearchResult], None]: + """Return the current page of results.""" + return self.hits.hits if self.hits else None + + async def next_page(self, start=None, size=None) -> bool: + """Advance to the next page of results.""" + self._start = start or self._start + self._size + if size: + self._size = size + if self.hits and self.hits.hits: + return await self._get_next_page() + return False + + async def _get_next_page(self) -> bool: + """Fetch the next page of results.""" + request = WorkflowSearchRequest( + query=self._criteria, from_=self._start, size=self._size + ) + raw_json = await self._client._call_api( + api=self._endpoint, + request_obj=request, + ) + if not raw_json.get("hits", {}).get("hits"): + if self.hits: + self.hits.hits = [] + return False + try: + if self.hits: + self.hits.hits = msgspec.convert( + raw_json["hits"]["hits"], + list[WorkflowSearchResult], + strict=False, + ) + except Exception as err: + raise ErrorCode.JSON_ERROR.exception_with_parameters( + raw_json, 200, str(err) + ) from err + return True + + async def __aiter__(self) -> AsyncGenerator[WorkflowSearchResult, None]: + """Iterate through all pages of results.""" + while True: + for item in self.current_page() or []: + yield item + if not await self.next_page(): + break diff --git a/pyatlan_v9/model/api_tokens.py b/pyatlan_v9/model/api_tokens.py new file mode 100644 index 000000000..dec168a56 --- /dev/null +++ b/pyatlan_v9/model/api_tokens.py @@ -0,0 +1,154 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2023 Atlan Pte. Ltd. + +from __future__ import annotations + +import json +from typing import Any, ClassVar, Union + +import msgspec + +from pyatlan.model.constants import SERVICE_ACCOUNT_ + + +class ApiTokenPersona(msgspec.Struct, kw_only=True, frozen=True, rename="camel"): + """Persona linked to an API token.""" + + guid: Union[str, None] = msgspec.field(default=None, name="id") + """Unique identifier (GUID) of the linked persona.""" + + persona: Union[str, None] = None + """Unique name of the linked persona.""" + + persona_qualified_name: Union[str, None] = None + """Unique qualified_name of the persona.""" + + +class ApiTokenAttributes(msgspec.Struct, kw_only=True, rename="camel"): + """Detailed characteristics of an API token.""" + + access_token_lifespan: Union[int, None] = msgspec.field( + default=None, name="access.token.lifespan" + ) + """Time, in seconds, from created_at after which the token will expire.""" + + access_token: Union[str, None] = None + """The actual API token that can be used as a bearer token.""" + + client_id: Union[str, None] = None + """Unique client identifier (GUID) of the API token.""" + + created_at: Union[int, None] = None + """Epoch time, in milliseconds, at which the API token was created.""" + + created_by: Union[str, None] = None + """User who created the API token.""" + + description: Union[str, None] = None + """Explanation of the API token.""" + + display_name: Union[str, None] = None + """Human-readable name provided when creating the token.""" + + personas: Union[Any, None] = None + """Deprecated (now unused): personas associated with the API token.""" + + persona_qualified_name: Union[Any, None] = None + """Personas associated with the API token (may arrive as JSON string from API).""" + + purposes: Union[Any, None] = None + """Possible future placeholder for purposes associated with the token.""" + + workspace_permissions: Union[Any, None] = None + """Detailed permissions given to the API token.""" + + def __post_init__(self) -> None: + """Handle JSON string values for embedded objects.""" + if isinstance(self.workspace_permissions, str): + self.workspace_permissions = set(json.loads(self.workspace_permissions)) + if isinstance(self.personas, str): + self.personas = json.loads(self.personas) + if isinstance(self.persona_qualified_name, str): + persona_qns = json.loads(self.persona_qualified_name) + self.persona_qualified_name = { + ApiTokenPersona(persona_qualified_name=qn) for qn in persona_qns + } + elif isinstance(self.persona_qualified_name, list): + self.persona_qualified_name = { + msgspec.convert(item, ApiTokenPersona, strict=False) + if isinstance(item, dict) + else item + for item in self.persona_qualified_name + } + + +class ApiToken(msgspec.Struct, kw_only=True, rename="camel"): + """Representation of an API token in Atlan.""" + + guid: Union[str, None] = msgspec.field(default=None, name="id") + """Unique identifier (GUID) of the API token.""" + + client_id: Union[str, None] = msgspec.field(default=None, name="clientId") + """Unique client identifier (GUID) of the API token.""" + + display_name: Union[str, None] = msgspec.field(default=None, name="displayName") + """Human-readable name provided when creating the token.""" + + attributes: Union[ApiTokenAttributes, None] = None + """Detailed characteristics of the API token.""" + + def __post_init__(self) -> None: + """Copy values from attributes to top-level fields.""" + if self.attributes: + if self.attributes.display_name and not self.display_name: + self.display_name = self.attributes.display_name + if self.attributes.client_id and not self.client_id: + self.client_id = self.attributes.client_id + + @property + def username(self) -> str: + """Username for the API token (service account format).""" + cid = self.client_id or (self.attributes.client_id if self.attributes else None) + return SERVICE_ACCOUNT_ + cid if cid else "" + + +class ApiTokenRequest(msgspec.Struct, kw_only=True, rename="camel"): + """Request to create an API token.""" + + _MAX_VALIDITY: ClassVar[int] = 157680000 + + display_name: Union[str, None] = None + """Human-readable name provided when creating the token.""" + + description: str = "" + """Explanation of the token.""" + + personas: set[str] = msgspec.field(default_factory=set) + """Deprecated (now unused): GUIDs of personas associated with the token.""" + + persona_qualified_names: set[str] = msgspec.field(default_factory=set) + """Unique qualified_names of personas associated with the token.""" + + validity_seconds: Union[int, None] = None + """Length of time, in seconds, after which the token will expire.""" + + def __post_init__(self) -> None: + """Validate and clamp validity_seconds.""" + if self.validity_seconds is not None: + if self.validity_seconds < 0: + self.validity_seconds = self._MAX_VALIDITY + else: + self.validity_seconds = min(self.validity_seconds, self._MAX_VALIDITY) + + +class ApiTokenResponse(msgspec.Struct, kw_only=True, rename="camel"): + """Response containing API token information.""" + + total_record: Union[int, None] = None + """Total number of API tokens.""" + + filter_record: Union[int, None] = None + """Number of API records that matched the specified filters.""" + + records: Union[list[ApiToken], None] = None + """Actual API tokens that matched the specified filters.""" diff --git a/pyatlan_v9/model/assets/__init__.py b/pyatlan_v9/model/assets/__init__.py new file mode 100644 index 000000000..192e4115d --- /dev/null +++ b/pyatlan_v9/model/assets/__init__.py @@ -0,0 +1,1717 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +from __future__ import annotations + +from .access_control import AccessControl +from .adf import ADF +from .adf_activity import AdfActivity +from .adf_dataflow import AdfDataflow +from .adf_dataset import AdfDataset +from .adf_linkedservice import AdfLinkedservice +from .adf_pipeline import AdfPipeline +from .adf_related import ( + RelatedADF, + RelatedAdfActivity, + RelatedAdfDataflow, + RelatedAdfDataset, + RelatedAdfLinkedservice, + RelatedAdfPipeline, +) +from .adls import ADLS +from .adls_account import ADLSAccount +from .adls_container import ADLSContainer +from .adls_object import ADLSObject +from .adls_related import ( + RelatedADLS, + RelatedADLSAccount, + RelatedADLSContainer, + RelatedADLSObject, +) +from .ai import AI +from .ai_application import AIApplication +from .ai_model import AIModel +from .ai_model_version import AIModelVersion +from .ai_related import ( + RelatedAI, + RelatedAIApplication, + RelatedAIModel, + RelatedAIModelVersion, +) +from .airflow import Airflow +from .airflow_dag import AirflowDag +from .airflow_related import RelatedAirflow, RelatedAirflowDag, RelatedAirflowTask +from .airflow_task import AirflowTask +from .anaplan import Anaplan +from .anaplan_app import AnaplanApp +from .anaplan_dimension import AnaplanDimension +from .anaplan_line_item import AnaplanLineItem +from .anaplan_list import AnaplanList +from .anaplan_model import AnaplanModel +from .anaplan_module import AnaplanModule +from .anaplan_page import AnaplanPage +from .anaplan_related import ( + RelatedAnaplan, + RelatedAnaplanApp, + RelatedAnaplanDimension, + RelatedAnaplanLineItem, + RelatedAnaplanList, + RelatedAnaplanModel, + RelatedAnaplanModule, + RelatedAnaplanPage, + RelatedAnaplanSystemDimension, + RelatedAnaplanView, + RelatedAnaplanWorkspace, +) +from .anaplan_system_dimension import AnaplanSystemDimension +from .anaplan_view import AnaplanView +from .anaplan_workspace import AnaplanWorkspace +from .anomalo import Anomalo +from .anomalo_check import AnomaloCheck +from .anomalo_related import RelatedAnomalo, RelatedAnomaloCheck +from .api import API +from .api_field import APIField +from .api_object import APIObject +from .api_path import APIPath +from .api_query import APIQuery +from .api_related import ( + RelatedAPI, + RelatedAPIField, + RelatedAPIObject, + RelatedAPIPath, + RelatedAPIQuery, + RelatedAPISpec, +) +from .api_spec import APISpec +from .app import App +from .app_related import RelatedApp, RelatedApplication, RelatedApplicationField +from .app_workflow_run import AppWorkflowRun +from .app_workflow_run_related import RelatedAppWorkflowRun +from .application import Application +from .application_field import ApplicationField +from .asset import Asset +from .asset_related import ( + RelatedAsset, + RelatedDataSet, + RelatedIncident, + RelatedInfrastructure, + RelatedProcessExecution, +) +from .atlan_app import AtlanApp +from .atlan_app_deployment import AtlanAppDeployment +from .atlan_app_installed import AtlanAppInstalled +from .atlan_app_related import ( + RelatedAtlanApp, + RelatedAtlanAppDeployment, + RelatedAtlanAppInstalled, + RelatedAtlanAppTool, + RelatedAtlanAppWorkflow, +) +from .atlan_app_tool import AtlanAppTool +from .atlan_app_workflow import AtlanAppWorkflow +from .atlas_glossary import AtlasGlossary +from .atlas_glossary_category import AtlasGlossaryCategory +from .atlas_glossary_term import AtlasGlossaryTerm +from .auth_policy import AuthPolicy +from .aws import AWS +from .azure import Azure +from .azure_event_consumer_group import AzureEventHubConsumerGroup +from .azure_event_hub import AzureEventHub +from .azure_service_bus import AzureServiceBus +from .azure_service_bus_namespace import AzureServiceBusNamespace +from .azure_service_bus_related import ( + RelatedAzureServiceBus, + RelatedAzureServiceBusNamespace, + RelatedAzureServiceBusSchema, + RelatedAzureServiceBusTopic, +) +from .azure_service_bus_schema import AzureServiceBusSchema +from .azure_service_bus_topic import AzureServiceBusTopic +from .badge import Badge +from .badge_condition import BadgeCondition +from .bi import BI +from .bi_process import BIProcess +from .bigquery_related import RelatedBigqueryRoutine, RelatedBigqueryTag +from .bigquery_routine import BigqueryRoutine +from .business_policy import BusinessPolicy +from .business_policy_related import ( + RelatedBusinessPolicy, + RelatedBusinessPolicyException, + RelatedBusinessPolicyIncident, + RelatedBusinessPolicyLog, +) +from .calculation_view import CalculationView +from .cassandra import Cassandra +from .cassandra_column import CassandraColumn +from .cassandra_index import CassandraIndex +from .cassandra_keyspace import CassandraKeyspace +from .cassandra_related import ( + RelatedCassandra, + RelatedCassandraColumn, + RelatedCassandraIndex, + RelatedCassandraKeyspace, + RelatedCassandraTable, + RelatedCassandraView, +) +from .cassandra_table import CassandraTable +from .cassandra_view import CassandraView +from .catalog import Catalog +from .catalog_related import ( + RelatedBI, + RelatedCatalog, + RelatedEventStore, + RelatedInsight, + RelatedNoSQL, + RelatedObjectStore, + RelatedSaaS, +) +from .cloud import Cloud +from .cloud_related import RelatedAWS, RelatedAzure, RelatedCloud, RelatedGoogle +from .cognite import Cognite +from .cognite3_d_model import Cognite3DModel +from .cognite_asset import CogniteAsset +from .cognite_event import CogniteEvent +from .cognite_file import CogniteFile +from .cognite_related import ( + RelatedCognite, + RelatedCognite3DModel, + RelatedCogniteAsset, + RelatedCogniteEvent, + RelatedCogniteFile, + RelatedCogniteSequence, + RelatedCogniteTimeSeries, +) +from .cognite_sequence import CogniteSequence +from .cognite_time_series import CogniteTimeSeries +from .cognos import Cognos +from .cognos_column import CognosColumn +from .cognos_dashboard import CognosDashboard +from .cognos_dataset import CognosDataset +from .cognos_datasource import CognosDatasource +from .cognos_exploration import CognosExploration +from .cognos_file import CognosFile +from .cognos_folder import CognosFolder +from .cognos_module import CognosModule +from .cognos_package import CognosPackage +from .cognos_related import ( + RelatedCognos, + RelatedCognosColumn, + RelatedCognosDashboard, + RelatedCognosDataset, + RelatedCognosDatasource, + RelatedCognosExploration, + RelatedCognosFile, + RelatedCognosFolder, + RelatedCognosModule, + RelatedCognosPackage, + RelatedCognosReport, +) +from .cognos_report import CognosReport +from .collection import Collection +from .column import Column +from .column_process import ColumnProcess +from .connection import Connection +from .connection_related import RelatedConnection +from .cosmos_mongo_db import CosmosMongoDB +from .cosmos_mongo_db_account import CosmosMongoDBAccount +from .cosmos_mongo_db_collection import CosmosMongoDBCollection +from .cosmos_mongo_db_database import CosmosMongoDBDatabase +from .cosmos_mongo_db_related import ( + RelatedCosmosMongoDB, + RelatedCosmosMongoDBAccount, + RelatedCosmosMongoDBCollection, + RelatedCosmosMongoDBDatabase, +) +from .cube import Cube +from .cube_dimension import CubeDimension +from .cube_field import CubeField +from .cube_hierarchy import CubeHierarchy +from .cube_related import ( + RelatedCube, + RelatedCubeDimension, + RelatedCubeField, + RelatedCubeHierarchy, + RelatedMultiDimensionalDataset, +) +from .custom import Custom +from .custom_entity import CustomEntity +from .custom_related import RelatedCustom, RelatedCustomEntity +from .data_contract import DataContract +from .data_domain import DataDomain +from .data_mesh import DataMesh +from .data_mesh_related import ( + RelatedDataDomain, + RelatedDataMesh, + RelatedDataProduct, + RelatedStakeholder, + RelatedStakeholderTitle, +) +from .data_product import DataProduct +from .data_quality import DataQuality +from .data_quality_related import ( + RelatedDataQuality, + RelatedDataQualityRule, + RelatedDataQualityRuleTemplate, + RelatedMetric, +) +from .data_quality_rule import DataQualityRule +from .data_quality_rule_template import DataQualityRuleTemplate +from .data_set import DataSet +from .data_studio import DataStudio +from .data_studio_asset import DataStudioAsset +from .data_studio_related import RelatedDataStudio, RelatedDataStudioAsset +from .database import Database +from .databricks import Databricks +from .databricks_ai_model_context import DatabricksAIModelContext +from .databricks_ai_model_version import DatabricksAIModelVersion +from .databricks_external_location import DatabricksExternalLocation +from .databricks_external_location_path import DatabricksExternalLocationPath +from .databricks_metric_view import DatabricksMetricView +from .databricks_notebook import DatabricksNotebook +from .databricks_related import ( + RelatedDatabricks, + RelatedDatabricksAIModelContext, + RelatedDatabricksAIModelVersion, + RelatedDatabricksExternalLocation, + RelatedDatabricksExternalLocationPath, + RelatedDatabricksMetricView, + RelatedDatabricksNotebook, + RelatedDatabricksUnityCatalogTag, + RelatedDatabricksVolume, + RelatedDatabricksVolumePath, +) +from .databricks_volume import DatabricksVolume +from .databricks_volume_path import DatabricksVolumePath +from .dataverse import Dataverse +from .dataverse_attribute import DataverseAttribute +from .dataverse_entity import DataverseEntity +from .dataverse_related import ( + RelatedDataverse, + RelatedDataverseAttribute, + RelatedDataverseEntity, +) +from .dbt import Dbt +from .dbt_column_process import DbtColumnProcess +from .dbt_dimension import DbtDimension +from .dbt_entity import DbtEntity +from .dbt_measure import DbtMeasure +from .dbt_metric import DbtMetric +from .dbt_model import DbtModel +from .dbt_model_column import DbtModelColumn +from .dbt_process import DbtProcess +from .dbt_related import ( + RelatedDbt, + RelatedDbtColumnProcess, + RelatedDbtDimension, + RelatedDbtEntity, + RelatedDbtMeasure, + RelatedDbtMetric, + RelatedDbtModel, + RelatedDbtModelColumn, + RelatedDbtProcess, + RelatedDbtSeed, + RelatedDbtSemanticModel, + RelatedDbtSource, + RelatedDbtTag, + RelatedDbtTest, +) +from .dbt_seed import DbtSeed +from .dbt_semantic_model import DbtSemanticModel +from .dbt_source import DbtSource +from .dbt_tag import DbtTag +from .dbt_test import DbtTest +from .document_db import DocumentDB +from .document_db_collection import DocumentDBCollection +from .document_db_database import DocumentDBDatabase +from .document_db_related import ( + RelatedDocumentDB, + RelatedDocumentDBCollection, + RelatedDocumentDBDatabase, +) +from .domo import Domo +from .domo_card import DomoCard +from .domo_dashboard import DomoDashboard +from .domo_dataset import DomoDataset +from .domo_dataset_column import DomoDatasetColumn +from .domo_related import ( + RelatedDomo, + RelatedDomoCard, + RelatedDomoDashboard, + RelatedDomoDataset, + RelatedDomoDatasetColumn, +) +from .dremio import Dremio +from .dremio_column import DremioColumn +from .dremio_folder import DremioFolder +from .dremio_physical_dataset import DremioPhysicalDataset +from .dremio_related import ( + RelatedDremio, + RelatedDremioColumn, + RelatedDremioFolder, + RelatedDremioPhysicalDataset, + RelatedDremioSource, + RelatedDremioSpace, + RelatedDremioVirtualDataset, +) +from .dremio_source import DremioSource +from .dremio_space import DremioSpace +from .dremio_virtual_dataset import DremioVirtualDataset +from .dynamo_db import DynamoDB +from .dynamo_db_related import ( + RelatedDynamoDB, + RelatedDynamoDBGlobalSecondaryIndex, + RelatedDynamoDBLocalSecondaryIndex, + RelatedDynamoDBSecondaryIndex, + RelatedDynamoDBTable, +) +from .dynamo_db_secondary_index import DynamoDBSecondaryIndex +from .dynamo_db_table import DynamoDBTable + +# Base classes +from .entity import AtlasClassification, Entity, TermAssignment +from .event_store import EventStore +from .fabric import Fabric +from .fabric_activity import FabricActivity +from .fabric_dashboard import FabricDashboard +from .fabric_data_pipeline import FabricDataPipeline +from .fabric_dataflow import FabricDataflow +from .fabric_dataflow_entity_column import FabricDataflowEntityColumn +from .fabric_page import FabricPage +from .fabric_related import ( + RelatedFabric, + RelatedFabricActivity, + RelatedFabricDashboard, + RelatedFabricDataflow, + RelatedFabricDataflowEntityColumn, + RelatedFabricDataPipeline, + RelatedFabricPage, + RelatedFabricReport, + RelatedFabricSemanticModel, + RelatedFabricSemanticModelTable, + RelatedFabricSemanticModelTableColumn, + RelatedFabricVisual, + RelatedFabricWorkspace, +) +from .fabric_report import FabricReport +from .fabric_semantic_model import FabricSemanticModel +from .fabric_semantic_model_table import FabricSemanticModelTable +from .fabric_semantic_model_table_column import FabricSemanticModelTableColumn +from .fabric_visual import FabricVisual +from .fabric_workspace import FabricWorkspace +from .file import File +from .fivetran import Fivetran +from .fivetran_connector import FivetranConnector +from .fivetran_related import RelatedFivetran, RelatedFivetranConnector +from .flow import Flow +from .flow_control_operation import FlowControlOperation +from .flow_dataset import FlowDataset +from .flow_dataset_operation import FlowDatasetOperation +from .flow_field import FlowField +from .flow_field_operation import FlowFieldOperation +from .flow_folder import FlowFolder +from .flow_project import FlowProject +from .flow_related import ( + RelatedFlow, + RelatedFlowControlOperation, + RelatedFlowDataset, + RelatedFlowDatasetOperation, + RelatedFlowField, + RelatedFlowFieldOperation, + RelatedFlowFolder, + RelatedFlowProject, + RelatedFlowReusableUnit, +) +from .flow_reusable_unit import FlowReusableUnit +from .folder import Folder +from .form import Form +from .form_related import RelatedForm, RelatedResponse +from .function import Function +from .gcs import GCS +from .gcs_bucket import GCSBucket +from .gcs_object import GCSObject +from .gcs_related import RelatedGCS, RelatedGCSBucket, RelatedGCSObject +from .google import Google +from .gtc_related import ( + RelatedAtlasGlossary, + RelatedAtlasGlossaryCategory, + RelatedAtlasGlossaryTerm, +) +from .incident import Incident +from .infrastructure import Infrastructure +from .insight import Insight +from .kafka import Kafka +from .kafka_consumer_group import KafkaConsumerGroup +from .kafka_related import ( + RelatedAzureEventHub, + RelatedAzureEventHubConsumerGroup, + RelatedKafka, + RelatedKafkaConsumerGroup, + RelatedKafkaTopic, +) +from .kafka_topic import KafkaTopic +from .link import Link +from .looker import Looker +from .looker_dashboard import LookerDashboard +from .looker_explore import LookerExplore +from .looker_field import LookerField +from .looker_folder import LookerFolder +from .looker_look import LookerLook +from .looker_model import LookerModel +from .looker_project import LookerProject +from .looker_query import LookerQuery +from .looker_related import ( + RelatedLooker, + RelatedLookerDashboard, + RelatedLookerExplore, + RelatedLookerField, + RelatedLookerFolder, + RelatedLookerLook, + RelatedLookerModel, + RelatedLookerProject, + RelatedLookerQuery, + RelatedLookerTile, + RelatedLookerView, +) +from .looker_tile import LookerTile +from .looker_view import LookerView +from .materialised_view import MaterialisedView +from .matillion import Matillion +from .matillion_component import MatillionComponent +from .matillion_group import MatillionGroup +from .matillion_job import MatillionJob +from .matillion_project import MatillionProject +from .matillion_related import ( + RelatedMatillion, + RelatedMatillionComponent, + RelatedMatillionGroup, + RelatedMatillionJob, + RelatedMatillionProject, +) +from .mc_incident import MCIncident +from .mc_monitor import MCMonitor +from .metabase import Metabase +from .metabase_collection import MetabaseCollection +from .metabase_dashboard import MetabaseDashboard +from .metabase_question import MetabaseQuestion +from .metabase_related import ( + RelatedMetabase, + RelatedMetabaseCollection, + RelatedMetabaseDashboard, + RelatedMetabaseQuestion, +) +from .metric import Metric +from .micro_strategy import MicroStrategy +from .micro_strategy_attribute import MicroStrategyAttribute +from .micro_strategy_column import MicroStrategyColumn +from .micro_strategy_cube import MicroStrategyCube +from .micro_strategy_document import MicroStrategyDocument +from .micro_strategy_dossier import MicroStrategyDossier +from .micro_strategy_fact import MicroStrategyFact +from .micro_strategy_metric import MicroStrategyMetric +from .micro_strategy_project import MicroStrategyProject +from .micro_strategy_related import ( + RelatedMicroStrategy, + RelatedMicroStrategyAttribute, + RelatedMicroStrategyColumn, + RelatedMicroStrategyCube, + RelatedMicroStrategyDocument, + RelatedMicroStrategyDossier, + RelatedMicroStrategyFact, + RelatedMicroStrategyMetric, + RelatedMicroStrategyProject, + RelatedMicroStrategyReport, + RelatedMicroStrategyVisualization, +) +from .micro_strategy_report import MicroStrategyReport +from .micro_strategy_visualization import MicroStrategyVisualization +from .mode import Mode +from .mode_chart import ModeChart +from .mode_collection import ModeCollection +from .mode_query import ModeQuery +from .mode_related import ( + RelatedMode, + RelatedModeChart, + RelatedModeCollection, + RelatedModeQuery, + RelatedModeReport, + RelatedModeWorkspace, +) +from .mode_report import ModeReport +from .mode_workspace import ModeWorkspace +from .model import Model +from .model_attribute import ModelAttribute +from .model_attribute_association import ModelAttributeAssociation +from .model_data_model import ModelDataModel +from .model_entity import ModelEntity +from .model_entity_association import ModelEntityAssociation +from .model_related import ( + RelatedModel, + RelatedModelAttribute, + RelatedModelAttributeAssociation, + RelatedModelDataModel, + RelatedModelEntity, + RelatedModelEntityAssociation, + RelatedModelVersion, +) +from .model_version import ModelVersion +from .mongo_db import MongoDB +from .mongo_db_collection import MongoDBCollection +from .mongo_db_database import MongoDBDatabase +from .mongo_db_related import ( + RelatedMongoDB, + RelatedMongoDBCollection, + RelatedMongoDBDatabase, +) +from .monte_carlo import MonteCarlo +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor, RelatedMonteCarlo +from .multi_dimensional_dataset import MultiDimensionalDataset +from .namespace import Namespace +from .namespace_related import RelatedCollection, RelatedFolder, RelatedNamespace +from .no_sql import NoSQL +from .notebook import Notebook +from .notebook_related import RelatedNotebook +from .object_store import ObjectStore +from .partial import Partial +from .partial_field import PartialField +from .partial_object import PartialObject +from .partial_related import RelatedPartial, RelatedPartialField, RelatedPartialObject +from .persona import Persona +from .power_bi import PowerBI +from .power_bi_app import PowerBIApp +from .power_bi_column import PowerBIColumn +from .power_bi_dashboard import PowerBIDashboard +from .power_bi_dataflow import PowerBIDataflow +from .power_bi_dataflow_entity_column import PowerBIDataflowEntityColumn +from .power_bi_dataset import PowerBIDataset +from .power_bi_datasource import PowerBIDatasource +from .power_bi_measure import PowerBIMeasure +from .power_bi_page import PowerBIPage +from .power_bi_related import ( + RelatedPowerBI, + RelatedPowerBIApp, + RelatedPowerBIColumn, + RelatedPowerBIDashboard, + RelatedPowerBIDataflow, + RelatedPowerBIDataflowEntityColumn, + RelatedPowerBIDataset, + RelatedPowerBIDatasource, + RelatedPowerBIMeasure, + RelatedPowerBIPage, + RelatedPowerBIReport, + RelatedPowerBITable, + RelatedPowerBITile, + RelatedPowerBIWorkspace, +) +from .power_bi_report import PowerBIReport +from .power_bi_table import PowerBITable +from .power_bi_tile import PowerBITile +from .power_bi_workspace import PowerBIWorkspace +from .preset import Preset +from .preset_chart import PresetChart +from .preset_dashboard import PresetDashboard +from .preset_dataset import PresetDataset +from .preset_related import ( + RelatedPreset, + RelatedPresetChart, + RelatedPresetDashboard, + RelatedPresetDataset, + RelatedPresetWorkspace, +) +from .preset_workspace import PresetWorkspace +from .procedure import Procedure +from .process import Process +from .process_execution import ProcessExecution +from .process_related import ( + RelatedBIProcess, + RelatedColumnProcess, + RelatedConnectionProcess, + RelatedProcess, +) +from .purpose import Purpose +from .qlik import Qlik +from .qlik_app import QlikApp +from .qlik_chart import QlikChart +from .qlik_column import QlikColumn +from .qlik_dataset import QlikDataset +from .qlik_related import ( + RelatedQlik, + RelatedQlikApp, + RelatedQlikChart, + RelatedQlikColumn, + RelatedQlikDataset, + RelatedQlikSheet, + RelatedQlikSpace, + RelatedQlikStream, +) +from .qlik_sheet import QlikSheet +from .qlik_space import QlikSpace +from .query import Query +from .quick_sight import QuickSight +from .quick_sight_analysis import QuickSightAnalysis +from .quick_sight_analysis_visual import QuickSightAnalysisVisual +from .quick_sight_dashboard import QuickSightDashboard +from .quick_sight_dashboard_visual import QuickSightDashboardVisual +from .quick_sight_dataset import QuickSightDataset +from .quick_sight_dataset_field import QuickSightDatasetField +from .quick_sight_folder import QuickSightFolder +from .quick_sight_related import ( + RelatedQuickSight, + RelatedQuickSightAnalysis, + RelatedQuickSightAnalysisVisual, + RelatedQuickSightDashboard, + RelatedQuickSightDashboardVisual, + RelatedQuickSightDataset, + RelatedQuickSightDatasetField, + RelatedQuickSightFolder, +) +from .readme import Readme +from .readme_template import ReadmeTemplate +from .redash import Redash +from .redash_dashboard import RedashDashboard +from .redash_query import RedashQuery +from .redash_related import ( + RelatedRedash, + RelatedRedashDashboard, + RelatedRedashQuery, + RelatedRedashVisualization, +) +from .redash_visualization import RedashVisualization +from .referenceable import Referenceable +from .referenceable_related import RelatedReferenceable +from .related_entity import RelatedEntity, SaveSemantic +from .resource import Resource +from .resource_related import ( + Related__internal, + RelatedBadge, + RelatedFile, + RelatedLink, + RelatedReadme, + RelatedReadmeTemplate, + RelatedResource, +) +from .s3 import S3 +from .s3_bucket import S3Bucket +from .s3_object import S3Object +from .s3_prefix import S3Prefix +from .s3_related import RelatedS3, RelatedS3Bucket, RelatedS3Object, RelatedS3Prefix +from .saa_s import SaaS +from .sage_maker_unified_studio import SageMakerUnifiedStudio +from .sage_maker_unified_studio_asset import SageMakerUnifiedStudioAsset +from .sage_maker_unified_studio_asset_schema import SageMakerUnifiedStudioAssetSchema +from .sage_maker_unified_studio_project import SageMakerUnifiedStudioProject +from .sage_maker_unified_studio_published_asset import ( + SageMakerUnifiedStudioPublishedAsset, +) +from .sage_maker_unified_studio_related import ( + RelatedSageMakerUnifiedStudio, + RelatedSageMakerUnifiedStudioAsset, + RelatedSageMakerUnifiedStudioAssetSchema, + RelatedSageMakerUnifiedStudioProject, + RelatedSageMakerUnifiedStudioPublishedAsset, + RelatedSageMakerUnifiedStudioSubscribedAsset, +) +from .sage_maker_unified_studio_subscribed_asset import ( + SageMakerUnifiedStudioSubscribedAsset, +) +from .salesforce import Salesforce +from .salesforce_dashboard import SalesforceDashboard +from .salesforce_field import SalesforceField +from .salesforce_object import SalesforceObject +from .salesforce_organization import SalesforceOrganization +from .salesforce_related import ( + RelatedSalesforce, + RelatedSalesforceDashboard, + RelatedSalesforceField, + RelatedSalesforceObject, + RelatedSalesforceOrganization, + RelatedSalesforceReport, +) +from .salesforce_report import SalesforceReport +from .sap import SAP +from .sap_erp_abap_program import SapErpAbapProgram +from .sap_erp_cds_view import SapErpCdsView +from .sap_erp_column import SapErpColumn +from .sap_erp_component import SapErpComponent +from .sap_erp_function_module import SapErpFunctionModule +from .sap_erp_table import SapErpTable +from .sap_erp_transaction_code import SapErpTransactionCode +from .sap_erp_view import SapErpView +from .sap_related import ( + RelatedSAP, + RelatedSapErpAbapProgram, + RelatedSapErpCdsView, + RelatedSapErpColumn, + RelatedSapErpComponent, + RelatedSapErpFunctionModule, + RelatedSapErpTable, + RelatedSapErpTransactionCode, + RelatedSapErpView, +) +from .schema import Schema +from .schema_registry import SchemaRegistry +from .schema_registry_related import RelatedSchemaRegistry, RelatedSchemaRegistrySubject +from .schema_registry_subject import SchemaRegistrySubject +from .semantic import Semantic +from .semantic_dimension import SemanticDimension +from .semantic_entity import SemanticEntity +from .semantic_field import SemanticField +from .semantic_measure import SemanticMeasure +from .semantic_model import SemanticModel +from .semantic_related import ( + RelatedSemantic, + RelatedSemanticDimension, + RelatedSemanticEntity, + RelatedSemanticField, + RelatedSemanticMeasure, + RelatedSemanticModel, +) +from .sigma import Sigma +from .sigma_data_element import SigmaDataElement +from .sigma_data_element_field import SigmaDataElementField +from .sigma_dataset import SigmaDataset +from .sigma_dataset_column import SigmaDatasetColumn +from .sigma_page import SigmaPage +from .sigma_related import ( + RelatedSigma, + RelatedSigmaDataElement, + RelatedSigmaDataElementField, + RelatedSigmaDataset, + RelatedSigmaDatasetColumn, + RelatedSigmaPage, + RelatedSigmaWorkbook, +) +from .sigma_workbook import SigmaWorkbook +from .sisense import Sisense +from .sisense_dashboard import SisenseDashboard +from .sisense_datamodel import SisenseDatamodel +from .sisense_datamodel_table import SisenseDatamodelTable +from .sisense_folder import SisenseFolder +from .sisense_related import ( + RelatedSisense, + RelatedSisenseDashboard, + RelatedSisenseDatamodel, + RelatedSisenseDatamodelTable, + RelatedSisenseFolder, + RelatedSisenseWidget, +) +from .sisense_widget import SisenseWidget +from .snowflake import Snowflake +from .snowflake_ai_model_context import SnowflakeAIModelContext +from .snowflake_ai_model_version import SnowflakeAIModelVersion +from .snowflake_dynamic_table import SnowflakeDynamicTable +from .snowflake_related import ( + RelatedSnowflake, + RelatedSnowflakeAIModelContext, + RelatedSnowflakeAIModelVersion, + RelatedSnowflakeDynamicTable, + RelatedSnowflakePipe, + RelatedSnowflakeStage, + RelatedSnowflakeStream, + RelatedSnowflakeTag, +) +from .soda import Soda +from .soda_check import SodaCheck +from .soda_related import RelatedSoda, RelatedSodaCheck +from .source_tag import SourceTag +from .spark import Spark +from .spark_job import SparkJob +from .spark_related import RelatedSpark, RelatedSparkJob +from .sql import SQL +from .sql_related import ( + RelatedCalculationView, + RelatedColumn, + RelatedDatabase, + RelatedFunction, + RelatedMaterialisedView, + RelatedProcedure, + RelatedQuery, + RelatedSchema, + RelatedSQL, + RelatedTable, + RelatedTablePartition, + RelatedView, +) +from .superset import Superset +from .superset_chart import SupersetChart +from .superset_dashboard import SupersetDashboard +from .superset_dataset import SupersetDataset +from .superset_related import ( + RelatedSuperset, + RelatedSupersetChart, + RelatedSupersetDashboard, + RelatedSupersetDataset, +) +from .table import Table +from .table_partition import TablePartition +from .tableau import Tableau +from .tableau_calculated_field import TableauCalculatedField +from .tableau_dashboard import TableauDashboard +from .tableau_dashboard_field import TableauDashboardField +from .tableau_datasource import TableauDatasource +from .tableau_datasource_field import TableauDatasourceField +from .tableau_flow import TableauFlow +from .tableau_metric import TableauMetric +from .tableau_project import TableauProject +from .tableau_related import ( + RelatedTableau, + RelatedTableauCalculatedField, + RelatedTableauDashboard, + RelatedTableauDashboardField, + RelatedTableauDatasource, + RelatedTableauDatasourceField, + RelatedTableauFlow, + RelatedTableauMetric, + RelatedTableauProject, + RelatedTableauSite, + RelatedTableauWorkbook, + RelatedTableauWorksheet, + RelatedTableauWorksheetField, +) +from .tableau_site import TableauSite +from .tableau_workbook import TableauWorkbook +from .tableau_worksheet import TableauWorksheet +from .tableau_worksheet_field import TableauWorksheetField +from .tag import Tag +from .tag_related import RelatedSourceTag, RelatedTag, RelatedTagAttachment +from .task import Task +from .task_related import RelatedTask +from .thoughtspot import Thoughtspot +from .thoughtspot_answer import ThoughtspotAnswer +from .thoughtspot_column import ThoughtspotColumn +from .thoughtspot_dashlet import ThoughtspotDashlet +from .thoughtspot_liveboard import ThoughtspotLiveboard +from .thoughtspot_related import ( + RelatedThoughtspot, + RelatedThoughtspotAnswer, + RelatedThoughtspotColumn, + RelatedThoughtspotDashlet, + RelatedThoughtspotLiveboard, + RelatedThoughtspotTable, + RelatedThoughtspotView, + RelatedThoughtspotWorksheet, +) +from .thoughtspot_table import ThoughtspotTable +from .thoughtspot_view import ThoughtspotView +from .thoughtspot_worksheet import ThoughtspotWorksheet +from .view import View +from .workflow import Workflow +from .workflow_related import RelatedWorkflow, RelatedWorkflowRun + +__all__ = [ + "AccessControl", + "ADF", + "ADLS", + "ADLSAccount", + "ADLSContainer", + "ADLSObject", + "AI", + "AIApplication", + "AIModel", + "AIModelVersion", + "API", + "APIField", + "APIObject", + "APIPath", + "APIQuery", + "APISpec", + "AWS", + "Badge", + "BadgeCondition", + "AdfActivity", + "AdfDataflow", + "AdfDataset", + "AdfLinkedservice", + "AdfPipeline", + "Airflow", + "AirflowDag", + "AirflowTask", + "Anaplan", + "AnaplanApp", + "AnaplanDimension", + "AnaplanLineItem", + "AnaplanList", + "AnaplanModel", + "AnaplanModule", + "AnaplanPage", + "AnaplanSystemDimension", + "AnaplanView", + "AnaplanWorkspace", + "Anomalo", + "AnomaloCheck", + "App", + "AppWorkflowRun", + "Application", + "ApplicationField", + "Asset", + "AtlanApp", + "AtlanAppDeployment", + "AtlanAppInstalled", + "AtlanAppTool", + "AtlanAppWorkflow", + "AtlasClassification", + "AtlasGlossary", + "AtlasGlossaryCategory", + "AtlasGlossaryTerm", + "AuthPolicy", + "Azure", + "AzureServiceBus", + "AzureServiceBusNamespace", + "AzureServiceBusSchema", + "AzureServiceBusTopic", + "Badge", + "BadgeCondition", + "BI", + "BIProcess", + "BigqueryRoutine", + "BusinessPolicy", + "CalculationView", + "Cassandra", + "CassandraColumn", + "CassandraIndex", + "CassandraKeyspace", + "CassandraTable", + "CassandraView", + "Catalog", + "Cloud", + "Cognite", + "Cognite3DModel", + "CogniteAsset", + "CogniteEvent", + "CogniteFile", + "CogniteSequence", + "CogniteTimeSeries", + "Cognos", + "CognosColumn", + "CognosDashboard", + "CognosDataset", + "CognosDatasource", + "CognosExploration", + "CognosFile", + "CognosFolder", + "CognosModule", + "CognosPackage", + "CognosReport", + "Collection", + "Column", + "ColumnProcess", + "Connection", + "CosmosMongoDB", + "CosmosMongoDBAccount", + "CosmosMongoDBCollection", + "CosmosMongoDBDatabase", + "Cube", + "CubeDimension", + "CubeField", + "CubeHierarchy", + "Custom", + "CustomEntity", + "DataContract", + "DataDomain", + "DataMesh", + "DataProduct", + "DataQuality", + "DataQualityRule", + "DataQualityRuleTemplate", + "DataSet", + "DataStudio", + "DataStudioAsset", + "Database", + "Databricks", + "DatabricksAIModelContext", + "DatabricksAIModelVersion", + "DatabricksExternalLocation", + "DatabricksExternalLocationPath", + "DatabricksMetricView", + "DatabricksNotebook", + "DatabricksVolume", + "DatabricksVolumePath", + "Dataverse", + "DataverseAttribute", + "DataverseEntity", + "Dbt", + "DbtColumnProcess", + "DbtDimension", + "DbtEntity", + "DbtMeasure", + "DbtMetric", + "DbtModel", + "DbtModelColumn", + "DbtProcess", + "DbtSeed", + "DbtSemanticModel", + "DbtSource", + "DbtTag", + "DbtTest", + "DocumentDB", + "DocumentDBCollection", + "DocumentDBDatabase", + "Domo", + "DomoCard", + "DomoDashboard", + "DomoDataset", + "DomoDatasetColumn", + "Dremio", + "DremioColumn", + "DremioFolder", + "DremioPhysicalDataset", + "DremioSource", + "DremioSpace", + "DremioVirtualDataset", + "DynamoDB", + "DynamoDBSecondaryIndex", + "DynamoDBTable", + "Entity", + "EventStore", + "Fabric", + "FabricActivity", + "FabricDashboard", + "FabricDataPipeline", + "FabricDataflow", + "FabricDataflowEntityColumn", + "FabricPage", + "FabricReport", + "FabricSemanticModel", + "FabricSemanticModelTable", + "FabricSemanticModelTableColumn", + "FabricVisual", + "FabricWorkspace", + "File", + "Fivetran", + "FivetranConnector", + "Flow", + "FlowControlOperation", + "FlowDataset", + "FlowDatasetOperation", + "FlowField", + "FlowFieldOperation", + "FlowFolder", + "FlowProject", + "FlowReusableUnit", + "Folder", + "Form", + "Function", + "GCS", + "GCSBucket", + "GCSObject", + "Google", + "Incident", + "Infrastructure", + "Insight", + "AzureEventHub", + "AzureEventHubConsumerGroup", + "Kafka", + "KafkaConsumerGroup", + "KafkaTopic", + "Link", + "Looker", + "LookerDashboard", + "LookerExplore", + "LookerField", + "LookerFolder", + "LookerLook", + "LookerModel", + "LookerProject", + "LookerQuery", + "LookerTile", + "LookerView", + "MCIncident", + "MCMonitor", + "MaterialisedView", + "Matillion", + "MatillionComponent", + "MatillionGroup", + "MatillionJob", + "MatillionProject", + "Metabase", + "MetabaseCollection", + "MetabaseDashboard", + "MetabaseQuestion", + "Metric", + "MicroStrategy", + "MicroStrategyAttribute", + "MicroStrategyColumn", + "MicroStrategyCube", + "MicroStrategyDocument", + "MicroStrategyDossier", + "MicroStrategyFact", + "MicroStrategyMetric", + "MicroStrategyProject", + "MicroStrategyReport", + "MicroStrategyVisualization", + "Mode", + "ModeChart", + "ModeCollection", + "ModeQuery", + "ModeReport", + "ModeWorkspace", + "Model", + "ModelAttribute", + "ModelAttributeAssociation", + "ModelDataModel", + "ModelEntity", + "ModelEntityAssociation", + "ModelVersion", + "MongoDB", + "MongoDBCollection", + "MongoDBDatabase", + "MonteCarlo", + "MultiDimensionalDataset", + "Namespace", + "NoSQL", + "Notebook", + "ObjectStore", + "Partial", + "PartialField", + "PartialObject", + "PowerBI", + "PowerBIApp", + "PowerBIColumn", + "PowerBIDashboard", + "PowerBIDataflow", + "PowerBIDataflowEntityColumn", + "PowerBIDataset", + "PowerBIDatasource", + "PowerBIMeasure", + "PowerBIPage", + "PowerBIReport", + "PowerBITable", + "PowerBITile", + "PowerBIWorkspace", + "Preset", + "PresetChart", + "PresetDashboard", + "PresetDataset", + "PresetWorkspace", + "Procedure", + "Process", + "ProcessExecution", + "Persona", + "Purpose", + "Qlik", + "QlikApp", + "QlikChart", + "QlikColumn", + "QlikDataset", + "QlikSheet", + "QlikSpace", + "Query", + "QuickSight", + "QuickSightAnalysis", + "QuickSightAnalysisVisual", + "QuickSightDashboard", + "QuickSightDashboardVisual", + "QuickSightDataset", + "QuickSightDatasetField", + "QuickSightFolder", + "Readme", + "ReadmeTemplate", + "Redash", + "RedashDashboard", + "RedashQuery", + "RedashVisualization", + "Referenceable", + "RelatedADF", + "RelatedADLS", + "RelatedADLSAccount", + "RelatedADLSContainer", + "RelatedADLSObject", + "RelatedAI", + "RelatedAIApplication", + "RelatedAIModel", + "RelatedAIModelVersion", + "RelatedAPI", + "RelatedAPIField", + "RelatedAPIObject", + "RelatedAPIPath", + "RelatedAPIQuery", + "RelatedAPISpec", + "RelatedAWS", + "RelatedAdfActivity", + "RelatedAdfDataflow", + "RelatedAdfDataset", + "RelatedAdfLinkedservice", + "RelatedAdfPipeline", + "RelatedAirflow", + "RelatedAirflowDag", + "RelatedAirflowTask", + "RelatedAnaplan", + "RelatedAnaplanApp", + "RelatedAnaplanDimension", + "RelatedAnaplanLineItem", + "RelatedAnaplanList", + "RelatedAnaplanModel", + "RelatedAnaplanModule", + "RelatedAnaplanPage", + "RelatedAnaplanSystemDimension", + "RelatedAnaplanView", + "RelatedAnaplanWorkspace", + "RelatedAnomalo", + "RelatedAnomaloCheck", + "RelatedApp", + "RelatedAppWorkflowRun", + "RelatedApplication", + "RelatedApplicationField", + "RelatedAsset", + "RelatedAtlanApp", + "RelatedAtlanAppDeployment", + "RelatedAtlanAppInstalled", + "RelatedAtlanAppTool", + "RelatedAtlanAppWorkflow", + "RelatedAtlasGlossary", + "RelatedAtlasGlossaryCategory", + "RelatedAtlasGlossaryTerm", + "RelatedAzure", + "RelatedAzureEventHub", + "RelatedAzureEventHubConsumerGroup", + "RelatedAzureServiceBus", + "RelatedAzureServiceBusNamespace", + "RelatedAzureServiceBusSchema", + "RelatedAzureServiceBusTopic", + "RelatedBI", + "RelatedBIProcess", + "RelatedBadge", + "RelatedBigqueryRoutine", + "RelatedBigqueryTag", + "RelatedBusinessPolicy", + "RelatedBusinessPolicyException", + "RelatedBusinessPolicyIncident", + "RelatedBusinessPolicyLog", + "RelatedCalculationView", + "RelatedCassandra", + "RelatedCassandraColumn", + "RelatedCassandraIndex", + "RelatedCassandraKeyspace", + "RelatedCassandraTable", + "RelatedCassandraView", + "RelatedCatalog", + "RelatedCloud", + "RelatedCognite", + "RelatedCognite3DModel", + "RelatedCogniteAsset", + "RelatedCogniteEvent", + "RelatedCogniteFile", + "RelatedCogniteSequence", + "RelatedCogniteTimeSeries", + "RelatedCognos", + "RelatedCognosColumn", + "RelatedCognosDashboard", + "RelatedCognosDataset", + "RelatedCognosDatasource", + "RelatedCognosExploration", + "RelatedCognosFile", + "RelatedCognosFolder", + "RelatedCognosModule", + "RelatedCognosPackage", + "RelatedCognosReport", + "RelatedCollection", + "RelatedColumn", + "RelatedColumnProcess", + "RelatedConnection", + "RelatedConnectionProcess", + "RelatedCosmosMongoDB", + "RelatedCosmosMongoDBAccount", + "RelatedCosmosMongoDBCollection", + "RelatedCosmosMongoDBDatabase", + "RelatedCube", + "RelatedCubeDimension", + "RelatedCubeField", + "RelatedCubeHierarchy", + "RelatedCustom", + "RelatedCustomEntity", + "RelatedDataDomain", + "RelatedDataMesh", + "RelatedDataProduct", + "RelatedDataQuality", + "RelatedDataQualityRule", + "RelatedDataQualityRuleTemplate", + "RelatedDataSet", + "RelatedDataStudio", + "RelatedDataStudioAsset", + "RelatedDatabase", + "RelatedDatabricks", + "RelatedDatabricksAIModelContext", + "RelatedDatabricksAIModelVersion", + "RelatedDatabricksExternalLocation", + "RelatedDatabricksExternalLocationPath", + "RelatedDatabricksMetricView", + "RelatedDatabricksNotebook", + "RelatedDatabricksUnityCatalogTag", + "RelatedDatabricksVolume", + "RelatedDatabricksVolumePath", + "RelatedDataverse", + "RelatedDataverseAttribute", + "RelatedDataverseEntity", + "RelatedDbt", + "RelatedDbtColumnProcess", + "RelatedDbtDimension", + "RelatedDbtEntity", + "RelatedDbtMeasure", + "RelatedDbtMetric", + "RelatedDbtModel", + "RelatedDbtModelColumn", + "RelatedDbtProcess", + "RelatedDbtSeed", + "RelatedDbtSemanticModel", + "RelatedDbtSource", + "RelatedDbtTag", + "RelatedDbtTest", + "RelatedDocumentDB", + "RelatedDocumentDBCollection", + "RelatedDocumentDBDatabase", + "RelatedDomo", + "RelatedDomoCard", + "RelatedDomoDashboard", + "RelatedDomoDataset", + "RelatedDomoDatasetColumn", + "RelatedDremio", + "RelatedDremioColumn", + "RelatedDremioFolder", + "RelatedDremioPhysicalDataset", + "RelatedDremioSource", + "RelatedDremioSpace", + "RelatedDremioVirtualDataset", + "RelatedDynamoDB", + "RelatedDynamoDBGlobalSecondaryIndex", + "RelatedDynamoDBLocalSecondaryIndex", + "RelatedDynamoDBSecondaryIndex", + "RelatedDynamoDBTable", + "RelatedEntity", + "RelatedEventStore", + "RelatedFabric", + "RelatedFabricActivity", + "RelatedFabricDashboard", + "RelatedFabricDataPipeline", + "RelatedFabricDataflow", + "RelatedFabricDataflowEntityColumn", + "RelatedFabricPage", + "RelatedFabricReport", + "RelatedFabricSemanticModel", + "RelatedFabricSemanticModelTable", + "RelatedFabricSemanticModelTableColumn", + "RelatedFabricVisual", + "RelatedFabricWorkspace", + "RelatedFile", + "RelatedFivetran", + "RelatedFivetranConnector", + "RelatedFlow", + "RelatedFlowControlOperation", + "RelatedFlowDataset", + "RelatedFlowDatasetOperation", + "RelatedFlowField", + "RelatedFlowFieldOperation", + "RelatedFlowFolder", + "RelatedFlowProject", + "RelatedFlowReusableUnit", + "RelatedFolder", + "RelatedForm", + "RelatedFunction", + "RelatedGCS", + "RelatedGCSBucket", + "RelatedGCSObject", + "RelatedGoogle", + "RelatedIncident", + "RelatedInfrastructure", + "RelatedInsight", + "RelatedKafka", + "RelatedKafkaConsumerGroup", + "RelatedKafkaTopic", + "RelatedLink", + "RelatedLooker", + "RelatedLookerDashboard", + "RelatedLookerExplore", + "RelatedLookerField", + "RelatedLookerFolder", + "RelatedLookerLook", + "RelatedLookerModel", + "RelatedLookerProject", + "RelatedLookerQuery", + "RelatedLookerTile", + "RelatedLookerView", + "RelatedMCIncident", + "RelatedMCMonitor", + "RelatedMaterialisedView", + "RelatedMatillion", + "RelatedMatillionComponent", + "RelatedMatillionGroup", + "RelatedMatillionJob", + "RelatedMatillionProject", + "RelatedMetabase", + "RelatedMetabaseCollection", + "RelatedMetabaseDashboard", + "RelatedMetabaseQuestion", + "RelatedMetric", + "RelatedMicroStrategy", + "RelatedMicroStrategyAttribute", + "RelatedMicroStrategyColumn", + "RelatedMicroStrategyCube", + "RelatedMicroStrategyDocument", + "RelatedMicroStrategyDossier", + "RelatedMicroStrategyFact", + "RelatedMicroStrategyMetric", + "RelatedMicroStrategyProject", + "RelatedMicroStrategyReport", + "RelatedMicroStrategyVisualization", + "RelatedMode", + "RelatedModeChart", + "RelatedModeCollection", + "RelatedModeQuery", + "RelatedModeReport", + "RelatedModeWorkspace", + "RelatedModel", + "RelatedModelAttribute", + "RelatedModelAttributeAssociation", + "RelatedModelDataModel", + "RelatedModelEntity", + "RelatedModelEntityAssociation", + "RelatedModelVersion", + "RelatedMongoDB", + "RelatedMongoDBCollection", + "RelatedMongoDBDatabase", + "RelatedMonteCarlo", + "RelatedMultiDimensionalDataset", + "RelatedNamespace", + "RelatedNoSQL", + "RelatedNotebook", + "RelatedObjectStore", + "RelatedPartial", + "RelatedPartialField", + "RelatedPartialObject", + "RelatedPowerBI", + "RelatedPowerBIApp", + "RelatedPowerBIColumn", + "RelatedPowerBIDashboard", + "RelatedPowerBIDataflow", + "RelatedPowerBIDataflowEntityColumn", + "RelatedPowerBIDataset", + "RelatedPowerBIDatasource", + "RelatedPowerBIMeasure", + "RelatedPowerBIPage", + "RelatedPowerBIReport", + "RelatedPowerBITable", + "RelatedPowerBITile", + "RelatedPowerBIWorkspace", + "RelatedPreset", + "RelatedPresetChart", + "RelatedPresetDashboard", + "RelatedPresetDataset", + "RelatedPresetWorkspace", + "RelatedProcedure", + "RelatedProcess", + "RelatedProcessExecution", + "RelatedQlik", + "RelatedQlikApp", + "RelatedQlikChart", + "RelatedQlikColumn", + "RelatedQlikDataset", + "RelatedQlikSheet", + "RelatedQlikSpace", + "RelatedQlikStream", + "RelatedQuery", + "RelatedQuickSight", + "RelatedQuickSightAnalysis", + "RelatedQuickSightAnalysisVisual", + "RelatedQuickSightDashboard", + "RelatedQuickSightDashboardVisual", + "RelatedQuickSightDataset", + "RelatedQuickSightDatasetField", + "RelatedQuickSightFolder", + "RelatedReadme", + "RelatedReadmeTemplate", + "RelatedRedash", + "RelatedRedashDashboard", + "RelatedRedashQuery", + "RelatedRedashVisualization", + "RelatedReferenceable", + "RelatedResource", + "RelatedResponse", + "RelatedS3", + "RelatedS3Bucket", + "RelatedS3Object", + "RelatedS3Prefix", + "RelatedSAP", + "RelatedSQL", + "RelatedSaaS", + "RelatedSageMakerUnifiedStudio", + "RelatedSageMakerUnifiedStudioAsset", + "RelatedSageMakerUnifiedStudioAssetSchema", + "RelatedSageMakerUnifiedStudioProject", + "RelatedSageMakerUnifiedStudioPublishedAsset", + "RelatedSageMakerUnifiedStudioSubscribedAsset", + "RelatedSalesforce", + "RelatedSalesforceDashboard", + "RelatedSalesforceField", + "RelatedSalesforceObject", + "RelatedSalesforceOrganization", + "RelatedSalesforceReport", + "RelatedSapErpAbapProgram", + "RelatedSapErpCdsView", + "RelatedSapErpColumn", + "RelatedSapErpComponent", + "RelatedSapErpFunctionModule", + "RelatedSapErpTable", + "RelatedSapErpTransactionCode", + "RelatedSapErpView", + "RelatedSchema", + "RelatedSchemaRegistry", + "RelatedSchemaRegistrySubject", + "RelatedSemantic", + "RelatedSemanticDimension", + "RelatedSemanticEntity", + "RelatedSemanticField", + "RelatedSemanticMeasure", + "RelatedSemanticModel", + "RelatedSigma", + "RelatedSigmaDataElement", + "RelatedSigmaDataElementField", + "RelatedSigmaDataset", + "RelatedSigmaDatasetColumn", + "RelatedSigmaPage", + "RelatedSigmaWorkbook", + "RelatedSisense", + "RelatedSisenseDashboard", + "RelatedSisenseDatamodel", + "RelatedSisenseDatamodelTable", + "RelatedSisenseFolder", + "RelatedSisenseWidget", + "RelatedSnowflake", + "RelatedSnowflakeAIModelContext", + "RelatedSnowflakeAIModelVersion", + "RelatedSnowflakeDynamicTable", + "RelatedSnowflakePipe", + "RelatedSnowflakeStage", + "RelatedSnowflakeStream", + "RelatedSnowflakeTag", + "RelatedSoda", + "RelatedSodaCheck", + "RelatedSourceTag", + "RelatedSpark", + "RelatedSparkJob", + "RelatedSuperset", + "RelatedSupersetChart", + "RelatedSupersetDashboard", + "RelatedSupersetDataset", + "RelatedStakeholder", + "RelatedStakeholderTitle", + "RelatedTable", + "RelatedTablePartition", + "RelatedTableau", + "RelatedTableauCalculatedField", + "RelatedTableauDashboard", + "RelatedTableauDashboardField", + "RelatedTableauDatasource", + "RelatedTableauDatasourceField", + "RelatedTableauFlow", + "RelatedTableauMetric", + "RelatedTableauProject", + "RelatedTableauSite", + "RelatedTableauWorkbook", + "RelatedTableauWorksheet", + "RelatedTableauWorksheetField", + "RelatedTag", + "RelatedTagAttachment", + "RelatedTask", + "RelatedThoughtspot", + "RelatedThoughtspotAnswer", + "RelatedThoughtspotColumn", + "RelatedThoughtspotDashlet", + "RelatedThoughtspotLiveboard", + "RelatedThoughtspotTable", + "RelatedThoughtspotView", + "RelatedThoughtspotWorksheet", + "RelatedView", + "RelatedWorkflow", + "RelatedWorkflowRun", + "Related__internal", + "Resource", + "S3", + "S3Bucket", + "S3Object", + "S3Prefix", + "SAP", + "SQL", + "SaaS", + "SageMakerUnifiedStudio", + "SageMakerUnifiedStudioAsset", + "SageMakerUnifiedStudioAssetSchema", + "SageMakerUnifiedStudioProject", + "SageMakerUnifiedStudioPublishedAsset", + "SageMakerUnifiedStudioSubscribedAsset", + "Salesforce", + "SalesforceDashboard", + "SalesforceField", + "SalesforceObject", + "SalesforceOrganization", + "SalesforceReport", + "SapErpAbapProgram", + "SapErpCdsView", + "SapErpColumn", + "SapErpComponent", + "SapErpFunctionModule", + "SapErpTable", + "SapErpTransactionCode", + "SapErpView", + "SaveSemantic", + "Schema", + "SchemaRegistry", + "SchemaRegistrySubject", + "Semantic", + "SemanticDimension", + "SemanticEntity", + "SemanticField", + "SemanticMeasure", + "SemanticModel", + "Sigma", + "SigmaDataElement", + "SigmaDataElementField", + "SigmaDataset", + "SigmaDatasetColumn", + "SigmaPage", + "SigmaWorkbook", + "Sisense", + "SisenseDashboard", + "SisenseDatamodel", + "SisenseDatamodelTable", + "SisenseFolder", + "SisenseWidget", + "Snowflake", + "SnowflakeAIModelContext", + "SnowflakeAIModelVersion", + "SnowflakeDynamicTable", + "Soda", + "SodaCheck", + "SourceTag", + "Spark", + "SparkJob", + "Superset", + "SupersetChart", + "SupersetDashboard", + "SupersetDataset", + "Table", + "TablePartition", + "Tableau", + "TableauCalculatedField", + "TableauDashboard", + "TableauDashboardField", + "TableauDatasource", + "TableauDatasourceField", + "TableauFlow", + "TableauMetric", + "TableauProject", + "TableauSite", + "TableauWorkbook", + "TableauWorksheet", + "TableauWorksheetField", + "Tag", + "Task", + "TermAssignment", + "Thoughtspot", + "ThoughtspotAnswer", + "ThoughtspotColumn", + "ThoughtspotDashlet", + "ThoughtspotLiveboard", + "ThoughtspotTable", + "ThoughtspotView", + "ThoughtspotWorksheet", + "View", + "Workflow", +] diff --git a/pyatlan_v9/model/assets/_init_adf.py b/pyatlan_v9/model/assets/_init_adf.py new file mode 100644 index 000000000..5c407d66c --- /dev/null +++ b/pyatlan_v9/model/assets/_init_adf.py @@ -0,0 +1,39 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +ADF module exports. + +This module provides convenient imports for all ADF types and their Related variants. +""" + +from .adf_related import ( + RelatedADF, + RelatedAdfActivity, + RelatedAdfDataflow, + RelatedAdfDataset, + RelatedAdfLinkedservice, + RelatedAdfPipeline, +) +from .adf import ADF +from .adf_activity import AdfActivity +from .adf_dataflow import AdfDataflow +from .adf_dataset import AdfDataset +from .adf_linkedservice import AdfLinkedservice +from .adf_pipeline import AdfPipeline + +__all__ = [ + "ADF", + "AdfActivity", + "AdfDataflow", + "AdfDataset", + "AdfLinkedservice", + "AdfPipeline", + "RelatedADF", + "RelatedAdfActivity", + "RelatedAdfDataflow", + "RelatedAdfDataset", + "RelatedAdfLinkedservice", + "RelatedAdfPipeline", +] diff --git a/pyatlan_v9/model/assets/_init_adls.py b/pyatlan_v9/model/assets/_init_adls.py new file mode 100644 index 000000000..5b2547812 --- /dev/null +++ b/pyatlan_v9/model/assets/_init_adls.py @@ -0,0 +1,31 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +ADLS module exports. + +This module provides convenient imports for all ADLS types and their Related variants. +""" + +from .adls_related import ( + RelatedADLS, + RelatedADLSAccount, + RelatedADLSContainer, + RelatedADLSObject, +) +from .adls import ADLS +from .adls_account import ADLSAccount +from .adls_container import ADLSContainer +from .adls_object import ADLSObject + +__all__ = [ + "ADLS", + "ADLSAccount", + "ADLSContainer", + "ADLSObject", + "RelatedADLS", + "RelatedADLSAccount", + "RelatedADLSContainer", + "RelatedADLSObject", +] diff --git a/pyatlan_v9/model/assets/_init_ai.py b/pyatlan_v9/model/assets/_init_ai.py new file mode 100644 index 000000000..e1faa5415 --- /dev/null +++ b/pyatlan_v9/model/assets/_init_ai.py @@ -0,0 +1,31 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +AI module exports. + +This module provides convenient imports for all AI types and their Related variants. +""" + +from .ai_related import ( + RelatedAI, + RelatedAIApplication, + RelatedAIModel, + RelatedAIModelVersion, +) +from .ai import AI +from .ai_application import AIApplication +from .ai_model import AIModel +from .ai_model_version import AIModelVersion + +__all__ = [ + "AI", + "AIApplication", + "AIModel", + "AIModelVersion", + "RelatedAI", + "RelatedAIApplication", + "RelatedAIModel", + "RelatedAIModelVersion", +] diff --git a/pyatlan_v9/model/assets/_init_airflow.py b/pyatlan_v9/model/assets/_init_airflow.py new file mode 100644 index 000000000..5d046f8d5 --- /dev/null +++ b/pyatlan_v9/model/assets/_init_airflow.py @@ -0,0 +1,27 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Airflow module exports. + +This module provides convenient imports for all Airflow types and their Related variants. +""" + +from .airflow_related import ( + RelatedAirflow, + RelatedAirflowDag, + RelatedAirflowTask, +) +from .airflow import Airflow +from .airflow_dag import AirflowDag +from .airflow_task import AirflowTask + +__all__ = [ + "Airflow", + "AirflowDag", + "AirflowTask", + "RelatedAirflow", + "RelatedAirflowDag", + "RelatedAirflowTask", +] diff --git a/pyatlan_v9/model/assets/_init_anaplan.py b/pyatlan_v9/model/assets/_init_anaplan.py new file mode 100644 index 000000000..1871726ec --- /dev/null +++ b/pyatlan_v9/model/assets/_init_anaplan.py @@ -0,0 +1,59 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Anaplan module exports. + +This module provides convenient imports for all Anaplan types and their Related variants. +""" + +from .anaplan_related import ( + RelatedAnaplan, + RelatedAnaplanApp, + RelatedAnaplanDimension, + RelatedAnaplanLineItem, + RelatedAnaplanList, + RelatedAnaplanModel, + RelatedAnaplanModule, + RelatedAnaplanPage, + RelatedAnaplanSystemDimension, + RelatedAnaplanView, + RelatedAnaplanWorkspace, +) +from .anaplan import Anaplan +from .anaplan_app import AnaplanApp +from .anaplan_dimension import AnaplanDimension +from .anaplan_line_item import AnaplanLineItem +from .anaplan_list import AnaplanList +from .anaplan_model import AnaplanModel +from .anaplan_module import AnaplanModule +from .anaplan_page import AnaplanPage +from .anaplan_system_dimension import AnaplanSystemDimension +from .anaplan_view import AnaplanView +from .anaplan_workspace import AnaplanWorkspace + +__all__ = [ + "Anaplan", + "AnaplanApp", + "AnaplanDimension", + "AnaplanLineItem", + "AnaplanList", + "AnaplanModel", + "AnaplanModule", + "AnaplanPage", + "AnaplanSystemDimension", + "AnaplanView", + "AnaplanWorkspace", + "RelatedAnaplan", + "RelatedAnaplanApp", + "RelatedAnaplanDimension", + "RelatedAnaplanLineItem", + "RelatedAnaplanList", + "RelatedAnaplanModel", + "RelatedAnaplanModule", + "RelatedAnaplanPage", + "RelatedAnaplanSystemDimension", + "RelatedAnaplanView", + "RelatedAnaplanWorkspace", +] diff --git a/pyatlan_v9/model/assets/_init_anomalo.py b/pyatlan_v9/model/assets/_init_anomalo.py new file mode 100644 index 000000000..b462dc927 --- /dev/null +++ b/pyatlan_v9/model/assets/_init_anomalo.py @@ -0,0 +1,23 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Anomalo module exports. + +This module provides convenient imports for all Anomalo types and their Related variants. +""" + +from .anomalo_related import ( + RelatedAnomalo, + RelatedAnomaloCheck, +) +from .anomalo import Anomalo +from .anomalo_check import AnomaloCheck + +__all__ = [ + "Anomalo", + "AnomaloCheck", + "RelatedAnomalo", + "RelatedAnomaloCheck", +] diff --git a/pyatlan_v9/model/assets/_init_api.py b/pyatlan_v9/model/assets/_init_api.py new file mode 100644 index 000000000..cca1d78b8 --- /dev/null +++ b/pyatlan_v9/model/assets/_init_api.py @@ -0,0 +1,39 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +API module exports. + +This module provides convenient imports for all API types and their Related variants. +""" + +from .api_related import ( + RelatedAPI, + RelatedAPIField, + RelatedAPIObject, + RelatedAPIPath, + RelatedAPIQuery, + RelatedAPISpec, +) +from .api import API +from .api_field import APIField +from .api_object import APIObject +from .api_path import APIPath +from .api_query import APIQuery +from .api_spec import APISpec + +__all__ = [ + "API", + "APIField", + "APIObject", + "APIPath", + "APIQuery", + "APISpec", + "RelatedAPI", + "RelatedAPIField", + "RelatedAPIObject", + "RelatedAPIPath", + "RelatedAPIQuery", + "RelatedAPISpec", +] diff --git a/pyatlan_v9/model/assets/_init_app.py b/pyatlan_v9/model/assets/_init_app.py new file mode 100644 index 000000000..4c26a6910 --- /dev/null +++ b/pyatlan_v9/model/assets/_init_app.py @@ -0,0 +1,27 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +App module exports. + +This module provides convenient imports for all App types and their Related variants. +""" + +from .app_related import ( + RelatedApp, + RelatedApplication, + RelatedApplicationField, +) +from .app import App +from .application import Application +from .application_field import ApplicationField + +__all__ = [ + "App", + "Application", + "ApplicationField", + "RelatedApp", + "RelatedApplication", + "RelatedApplicationField", +] diff --git a/pyatlan_v9/model/assets/_init_app_workflow_run.py b/pyatlan_v9/model/assets/_init_app_workflow_run.py new file mode 100644 index 000000000..4c178f2de --- /dev/null +++ b/pyatlan_v9/model/assets/_init_app_workflow_run.py @@ -0,0 +1,17 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +AppWorkflowRun module exports. + +This module provides convenient imports for all AppWorkflowRun types and their Related variants. +""" + +from .app_workflow_run_related import RelatedAppWorkflowRun +from .app_workflow_run import AppWorkflowRun + +__all__ = [ + "AppWorkflowRun", + "RelatedAppWorkflowRun", +] diff --git a/pyatlan_v9/model/assets/_init_asset.py b/pyatlan_v9/model/assets/_init_asset.py new file mode 100644 index 000000000..ffb0e1ecd --- /dev/null +++ b/pyatlan_v9/model/assets/_init_asset.py @@ -0,0 +1,35 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Asset module exports. + +This module provides convenient imports for all Asset types and their Related variants. +""" + +from .asset_related import ( + RelatedAsset, + RelatedDataSet, + RelatedIncident, + RelatedInfrastructure, + RelatedProcessExecution, +) +from .asset import Asset +from .data_set import DataSet +from .incident import Incident +from .infrastructure import Infrastructure +from .process_execution import ProcessExecution + +__all__ = [ + "Asset", + "DataSet", + "Incident", + "Infrastructure", + "ProcessExecution", + "RelatedAsset", + "RelatedDataSet", + "RelatedIncident", + "RelatedInfrastructure", + "RelatedProcessExecution", +] diff --git a/pyatlan_v9/model/assets/_init_asset_grouping.py b/pyatlan_v9/model/assets/_init_asset_grouping.py new file mode 100644 index 000000000..e175d1263 --- /dev/null +++ b/pyatlan_v9/model/assets/_init_asset_grouping.py @@ -0,0 +1,27 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +AssetGrouping module exports. + +This module provides convenient imports for all AssetGrouping types and their Related variants. +""" + +from .asset_grouping_related import ( + RelatedAssetGrouping, + RelatedAssetGroupingCollection, + RelatedAssetGroupingStrategy, +) +from .asset_grouping import AssetGrouping +from .asset_grouping_collection import AssetGroupingCollection +from .asset_grouping_strategy import AssetGroupingStrategy + +__all__ = [ + "AssetGrouping", + "AssetGroupingCollection", + "AssetGroupingStrategy", + "RelatedAssetGrouping", + "RelatedAssetGroupingCollection", + "RelatedAssetGroupingStrategy", +] diff --git a/pyatlan_v9/model/assets/_init_atlan_app.py b/pyatlan_v9/model/assets/_init_atlan_app.py new file mode 100644 index 000000000..ed558ee8d --- /dev/null +++ b/pyatlan_v9/model/assets/_init_atlan_app.py @@ -0,0 +1,35 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +AtlanApp module exports. + +This module provides convenient imports for all AtlanApp types and their Related variants. +""" + +from .atlan_app_related import ( + RelatedAtlanApp, + RelatedAtlanAppDeployment, + RelatedAtlanAppInstalled, + RelatedAtlanAppTool, + RelatedAtlanAppWorkflow, +) +from .atlan_app import AtlanApp +from .atlan_app_deployment import AtlanAppDeployment +from .atlan_app_installed import AtlanAppInstalled +from .atlan_app_tool import AtlanAppTool +from .atlan_app_workflow import AtlanAppWorkflow + +__all__ = [ + "AtlanApp", + "AtlanAppDeployment", + "AtlanAppInstalled", + "AtlanAppTool", + "AtlanAppWorkflow", + "RelatedAtlanApp", + "RelatedAtlanAppDeployment", + "RelatedAtlanAppInstalled", + "RelatedAtlanAppTool", + "RelatedAtlanAppWorkflow", +] diff --git a/pyatlan_v9/model/assets/_init_azure_service_bus.py b/pyatlan_v9/model/assets/_init_azure_service_bus.py new file mode 100644 index 000000000..f2298f636 --- /dev/null +++ b/pyatlan_v9/model/assets/_init_azure_service_bus.py @@ -0,0 +1,31 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +AzureServiceBus module exports. + +This module provides convenient imports for all AzureServiceBus types and their Related variants. +""" + +from .azure_service_bus_related import ( + RelatedAzureServiceBus, + RelatedAzureServiceBusNamespace, + RelatedAzureServiceBusSchema, + RelatedAzureServiceBusTopic, +) +from .azure_service_bus import AzureServiceBus +from .azure_service_bus_namespace import AzureServiceBusNamespace +from .azure_service_bus_schema import AzureServiceBusSchema +from .azure_service_bus_topic import AzureServiceBusTopic + +__all__ = [ + "AzureServiceBus", + "AzureServiceBusNamespace", + "AzureServiceBusSchema", + "AzureServiceBusTopic", + "RelatedAzureServiceBus", + "RelatedAzureServiceBusNamespace", + "RelatedAzureServiceBusSchema", + "RelatedAzureServiceBusTopic", +] diff --git a/pyatlan_v9/model/assets/_init_bigquery.py b/pyatlan_v9/model/assets/_init_bigquery.py new file mode 100644 index 000000000..0376c1575 --- /dev/null +++ b/pyatlan_v9/model/assets/_init_bigquery.py @@ -0,0 +1,21 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Bigquery module exports. + +This module provides convenient imports for all Bigquery types and their Related variants. +""" + +from .bigquery_related import ( + RelatedBigqueryRoutine, + RelatedBigqueryTag, +) +from .bigquery_routine import BigqueryRoutine + +__all__ = [ + "BigqueryRoutine", + "RelatedBigqueryRoutine", + "RelatedBigqueryTag", +] diff --git a/pyatlan_v9/model/assets/_init_business_policy.py b/pyatlan_v9/model/assets/_init_business_policy.py new file mode 100644 index 000000000..95fb820e8 --- /dev/null +++ b/pyatlan_v9/model/assets/_init_business_policy.py @@ -0,0 +1,25 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +BusinessPolicy module exports. + +This module provides convenient imports for all BusinessPolicy types and their Related variants. +""" + +from .business_policy_related import ( + RelatedBusinessPolicy, + RelatedBusinessPolicyException, + RelatedBusinessPolicyIncident, + RelatedBusinessPolicyLog, +) +from .business_policy import BusinessPolicy + +__all__ = [ + "BusinessPolicy", + "RelatedBusinessPolicy", + "RelatedBusinessPolicyException", + "RelatedBusinessPolicyIncident", + "RelatedBusinessPolicyLog", +] diff --git a/pyatlan_v9/model/assets/_init_cassandra.py b/pyatlan_v9/model/assets/_init_cassandra.py new file mode 100644 index 000000000..015654821 --- /dev/null +++ b/pyatlan_v9/model/assets/_init_cassandra.py @@ -0,0 +1,39 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Cassandra module exports. + +This module provides convenient imports for all Cassandra types and their Related variants. +""" + +from .cassandra_related import ( + RelatedCassandra, + RelatedCassandraColumn, + RelatedCassandraIndex, + RelatedCassandraKeyspace, + RelatedCassandraTable, + RelatedCassandraView, +) +from .cassandra import Cassandra +from .cassandra_column import CassandraColumn +from .cassandra_index import CassandraIndex +from .cassandra_keyspace import CassandraKeyspace +from .cassandra_table import CassandraTable +from .cassandra_view import CassandraView + +__all__ = [ + "Cassandra", + "CassandraColumn", + "CassandraIndex", + "CassandraKeyspace", + "CassandraTable", + "CassandraView", + "RelatedCassandra", + "RelatedCassandraColumn", + "RelatedCassandraIndex", + "RelatedCassandraKeyspace", + "RelatedCassandraTable", + "RelatedCassandraView", +] diff --git a/pyatlan_v9/model/assets/_init_catalog.py b/pyatlan_v9/model/assets/_init_catalog.py new file mode 100644 index 000000000..aebc7a055 --- /dev/null +++ b/pyatlan_v9/model/assets/_init_catalog.py @@ -0,0 +1,43 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Catalog module exports. + +This module provides convenient imports for all Catalog types and their Related variants. +""" + +from .catalog_related import ( + RelatedBI, + RelatedCatalog, + RelatedEventStore, + RelatedInsight, + RelatedNoSQL, + RelatedObjectStore, + RelatedSaaS, +) +from .catalog import Catalog +from .bi import BI +from .event_store import EventStore +from .insight import Insight +from .no_sql import NoSQL +from .object_store import ObjectStore +from .saa_s import SaaS + +__all__ = [ + "BI", + "Catalog", + "EventStore", + "Insight", + "NoSQL", + "ObjectStore", + "RelatedBI", + "RelatedCatalog", + "RelatedEventStore", + "RelatedInsight", + "RelatedNoSQL", + "RelatedObjectStore", + "RelatedSaaS", + "SaaS", +] diff --git a/pyatlan_v9/model/assets/_init_cloud.py b/pyatlan_v9/model/assets/_init_cloud.py new file mode 100644 index 000000000..369d1d1e1 --- /dev/null +++ b/pyatlan_v9/model/assets/_init_cloud.py @@ -0,0 +1,31 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Cloud module exports. + +This module provides convenient imports for all Cloud types and their Related variants. +""" + +from .cloud_related import ( + RelatedAWS, + RelatedAzure, + RelatedCloud, + RelatedGoogle, +) +from .cloud import Cloud +from .aws import AWS +from .azure import Azure +from .google import Google + +__all__ = [ + "AWS", + "Azure", + "Cloud", + "Google", + "RelatedAWS", + "RelatedAzure", + "RelatedCloud", + "RelatedGoogle", +] diff --git a/pyatlan_v9/model/assets/_init_cognite.py b/pyatlan_v9/model/assets/_init_cognite.py new file mode 100644 index 000000000..1b899d01a --- /dev/null +++ b/pyatlan_v9/model/assets/_init_cognite.py @@ -0,0 +1,43 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Cognite module exports. + +This module provides convenient imports for all Cognite types and their Related variants. +""" + +from .cognite_related import ( + RelatedCognite, + RelatedCognite3DModel, + RelatedCogniteAsset, + RelatedCogniteEvent, + RelatedCogniteFile, + RelatedCogniteSequence, + RelatedCogniteTimeSeries, +) +from .cognite import Cognite +from .cognite3d_model import Cognite3DModel +from .cognite_asset import CogniteAsset +from .cognite_event import CogniteEvent +from .cognite_file import CogniteFile +from .cognite_sequence import CogniteSequence +from .cognite_time_series import CogniteTimeSeries + +__all__ = [ + "Cognite", + "Cognite3DModel", + "CogniteAsset", + "CogniteEvent", + "CogniteFile", + "CogniteSequence", + "CogniteTimeSeries", + "RelatedCognite", + "RelatedCognite3DModel", + "RelatedCogniteAsset", + "RelatedCogniteEvent", + "RelatedCogniteFile", + "RelatedCogniteSequence", + "RelatedCogniteTimeSeries", +] diff --git a/pyatlan_v9/model/assets/_init_cognos.py b/pyatlan_v9/model/assets/_init_cognos.py new file mode 100644 index 000000000..9a58f06be --- /dev/null +++ b/pyatlan_v9/model/assets/_init_cognos.py @@ -0,0 +1,59 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Cognos module exports. + +This module provides convenient imports for all Cognos types and their Related variants. +""" + +from .cognos_related import ( + RelatedCognos, + RelatedCognosColumn, + RelatedCognosDashboard, + RelatedCognosDataset, + RelatedCognosDatasource, + RelatedCognosExploration, + RelatedCognosFile, + RelatedCognosFolder, + RelatedCognosModule, + RelatedCognosPackage, + RelatedCognosReport, +) +from .cognos import Cognos +from .cognos_column import CognosColumn +from .cognos_dashboard import CognosDashboard +from .cognos_dataset import CognosDataset +from .cognos_datasource import CognosDatasource +from .cognos_exploration import CognosExploration +from .cognos_file import CognosFile +from .cognos_folder import CognosFolder +from .cognos_module import CognosModule +from .cognos_package import CognosPackage +from .cognos_report import CognosReport + +__all__ = [ + "Cognos", + "CognosColumn", + "CognosDashboard", + "CognosDataset", + "CognosDatasource", + "CognosExploration", + "CognosFile", + "CognosFolder", + "CognosModule", + "CognosPackage", + "CognosReport", + "RelatedCognos", + "RelatedCognosColumn", + "RelatedCognosDashboard", + "RelatedCognosDataset", + "RelatedCognosDatasource", + "RelatedCognosExploration", + "RelatedCognosFile", + "RelatedCognosFolder", + "RelatedCognosModule", + "RelatedCognosPackage", + "RelatedCognosReport", +] diff --git a/pyatlan_v9/model/assets/_init_connection.py b/pyatlan_v9/model/assets/_init_connection.py new file mode 100644 index 000000000..3ee148c44 --- /dev/null +++ b/pyatlan_v9/model/assets/_init_connection.py @@ -0,0 +1,17 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Connection module exports. + +This module provides convenient imports for all Connection types and their Related variants. +""" + +from .connection_related import RelatedConnection +from .connection import Connection + +__all__ = [ + "Connection", + "RelatedConnection", +] diff --git a/pyatlan_v9/model/assets/_init_cosmos_mongo_db.py b/pyatlan_v9/model/assets/_init_cosmos_mongo_db.py new file mode 100644 index 000000000..d8dfc4deb --- /dev/null +++ b/pyatlan_v9/model/assets/_init_cosmos_mongo_db.py @@ -0,0 +1,31 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +CosmosMongoDB module exports. + +This module provides convenient imports for all CosmosMongoDB types and their Related variants. +""" + +from .cosmos_mongo_db_related import ( + RelatedCosmosMongoDB, + RelatedCosmosMongoDBAccount, + RelatedCosmosMongoDBCollection, + RelatedCosmosMongoDBDatabase, +) +from .cosmos_mongo_db import CosmosMongoDB +from .cosmos_mongo_db_account import CosmosMongoDBAccount +from .cosmos_mongo_db_collection import CosmosMongoDBCollection +from .cosmos_mongo_db_database import CosmosMongoDBDatabase + +__all__ = [ + "CosmosMongoDB", + "CosmosMongoDBAccount", + "CosmosMongoDBCollection", + "CosmosMongoDBDatabase", + "RelatedCosmosMongoDB", + "RelatedCosmosMongoDBAccount", + "RelatedCosmosMongoDBCollection", + "RelatedCosmosMongoDBDatabase", +] diff --git a/pyatlan_v9/model/assets/_init_cube.py b/pyatlan_v9/model/assets/_init_cube.py new file mode 100644 index 000000000..87d85adff --- /dev/null +++ b/pyatlan_v9/model/assets/_init_cube.py @@ -0,0 +1,35 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Cube module exports. + +This module provides convenient imports for all Cube types and their Related variants. +""" + +from .cube_related import ( + RelatedCube, + RelatedCubeDimension, + RelatedCubeField, + RelatedCubeHierarchy, + RelatedMultiDimensionalDataset, +) +from .multi_dimensional_dataset import MultiDimensionalDataset +from .cube import Cube +from .cube_dimension import CubeDimension +from .cube_field import CubeField +from .cube_hierarchy import CubeHierarchy + +__all__ = [ + "Cube", + "CubeDimension", + "CubeField", + "CubeHierarchy", + "MultiDimensionalDataset", + "RelatedCube", + "RelatedCubeDimension", + "RelatedCubeField", + "RelatedCubeHierarchy", + "RelatedMultiDimensionalDataset", +] diff --git a/pyatlan_v9/model/assets/_init_custom.py b/pyatlan_v9/model/assets/_init_custom.py new file mode 100644 index 000000000..de8e87ff6 --- /dev/null +++ b/pyatlan_v9/model/assets/_init_custom.py @@ -0,0 +1,23 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Custom module exports. + +This module provides convenient imports for all Custom types and their Related variants. +""" + +from .custom_related import ( + RelatedCustom, + RelatedCustomEntity, +) +from .custom import Custom +from .custom_entity import CustomEntity + +__all__ = [ + "Custom", + "CustomEntity", + "RelatedCustom", + "RelatedCustomEntity", +] diff --git a/pyatlan_v9/model/assets/_init_data_mesh.py b/pyatlan_v9/model/assets/_init_data_mesh.py new file mode 100644 index 000000000..bea55fb4c --- /dev/null +++ b/pyatlan_v9/model/assets/_init_data_mesh.py @@ -0,0 +1,31 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DataMesh module exports. + +This module provides convenient imports for all DataMesh types and their Related variants. +""" + +from .data_mesh_related import ( + RelatedDataDomain, + RelatedDataMesh, + RelatedDataProduct, + RelatedStakeholder, + RelatedStakeholderTitle, +) +from .data_mesh import DataMesh +from .data_domain import DataDomain +from .data_product import DataProduct + +__all__ = [ + "DataDomain", + "DataMesh", + "DataProduct", + "RelatedDataDomain", + "RelatedDataMesh", + "RelatedDataProduct", + "RelatedStakeholder", + "RelatedStakeholderTitle", +] diff --git a/pyatlan_v9/model/assets/_init_data_quality.py b/pyatlan_v9/model/assets/_init_data_quality.py new file mode 100644 index 000000000..92d561b12 --- /dev/null +++ b/pyatlan_v9/model/assets/_init_data_quality.py @@ -0,0 +1,31 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DataQuality module exports. + +This module provides convenient imports for all DataQuality types and their Related variants. +""" + +from .data_quality_related import ( + RelatedDataQuality, + RelatedDataQualityRule, + RelatedDataQualityRuleTemplate, + RelatedMetric, +) +from .data_quality import DataQuality +from .data_quality_rule import DataQualityRule +from .data_quality_rule_template import DataQualityRuleTemplate +from .metric import Metric + +__all__ = [ + "DataQuality", + "DataQualityRule", + "DataQualityRuleTemplate", + "Metric", + "RelatedDataQuality", + "RelatedDataQualityRule", + "RelatedDataQualityRuleTemplate", + "RelatedMetric", +] diff --git a/pyatlan_v9/model/assets/_init_data_studio.py b/pyatlan_v9/model/assets/_init_data_studio.py new file mode 100644 index 000000000..95e10d2c3 --- /dev/null +++ b/pyatlan_v9/model/assets/_init_data_studio.py @@ -0,0 +1,23 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DataStudio module exports. + +This module provides convenient imports for all DataStudio types and their Related variants. +""" + +from .data_studio_related import ( + RelatedDataStudio, + RelatedDataStudioAsset, +) +from .data_studio import DataStudio +from .data_studio_asset import DataStudioAsset + +__all__ = [ + "DataStudio", + "DataStudioAsset", + "RelatedDataStudio", + "RelatedDataStudioAsset", +] diff --git a/pyatlan_v9/model/assets/_init_databricks.py b/pyatlan_v9/model/assets/_init_databricks.py new file mode 100644 index 000000000..ba50add44 --- /dev/null +++ b/pyatlan_v9/model/assets/_init_databricks.py @@ -0,0 +1,53 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Databricks module exports. + +This module provides convenient imports for all Databricks types and their Related variants. +""" + +from .databricks_related import ( + RelatedDatabricks, + RelatedDatabricksAIModelContext, + RelatedDatabricksAIModelVersion, + RelatedDatabricksExternalLocation, + RelatedDatabricksExternalLocationPath, + RelatedDatabricksMetricView, + RelatedDatabricksNotebook, + RelatedDatabricksUnityCatalogTag, + RelatedDatabricksVolume, + RelatedDatabricksVolumePath, +) +from .databricks import Databricks +from .databricks_ai_model_context import DatabricksAIModelContext +from .databricks_ai_model_version import DatabricksAIModelVersion +from .databricks_external_location import DatabricksExternalLocation +from .databricks_external_location_path import DatabricksExternalLocationPath +from .databricks_metric_view import DatabricksMetricView +from .databricks_notebook import DatabricksNotebook +from .databricks_volume import DatabricksVolume +from .databricks_volume_path import DatabricksVolumePath + +__all__ = [ + "Databricks", + "DatabricksAIModelContext", + "DatabricksAIModelVersion", + "DatabricksExternalLocation", + "DatabricksExternalLocationPath", + "DatabricksMetricView", + "DatabricksNotebook", + "DatabricksVolume", + "DatabricksVolumePath", + "RelatedDatabricks", + "RelatedDatabricksAIModelContext", + "RelatedDatabricksAIModelVersion", + "RelatedDatabricksExternalLocation", + "RelatedDatabricksExternalLocationPath", + "RelatedDatabricksMetricView", + "RelatedDatabricksNotebook", + "RelatedDatabricksUnityCatalogTag", + "RelatedDatabricksVolume", + "RelatedDatabricksVolumePath", +] diff --git a/pyatlan_v9/model/assets/_init_dataverse.py b/pyatlan_v9/model/assets/_init_dataverse.py new file mode 100644 index 000000000..019cfb607 --- /dev/null +++ b/pyatlan_v9/model/assets/_init_dataverse.py @@ -0,0 +1,27 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Dataverse module exports. + +This module provides convenient imports for all Dataverse types and their Related variants. +""" + +from .dataverse_related import ( + RelatedDataverse, + RelatedDataverseAttribute, + RelatedDataverseEntity, +) +from .dataverse import Dataverse +from .dataverse_attribute import DataverseAttribute +from .dataverse_entity import DataverseEntity + +__all__ = [ + "Dataverse", + "DataverseAttribute", + "DataverseEntity", + "RelatedDataverse", + "RelatedDataverseAttribute", + "RelatedDataverseEntity", +] diff --git a/pyatlan_v9/model/assets/_init_dbt.py b/pyatlan_v9/model/assets/_init_dbt.py new file mode 100644 index 000000000..7cb4f04de --- /dev/null +++ b/pyatlan_v9/model/assets/_init_dbt.py @@ -0,0 +1,71 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Dbt module exports. + +This module provides convenient imports for all Dbt types and their Related variants. +""" + +from .dbt_related import ( + RelatedDbt, + RelatedDbtColumnProcess, + RelatedDbtDimension, + RelatedDbtEntity, + RelatedDbtMeasure, + RelatedDbtMetric, + RelatedDbtModel, + RelatedDbtModelColumn, + RelatedDbtProcess, + RelatedDbtSeed, + RelatedDbtSemanticModel, + RelatedDbtSource, + RelatedDbtTag, + RelatedDbtTest, +) +from .dbt import Dbt +from .dbt_column_process import DbtColumnProcess +from .dbt_dimension import DbtDimension +from .dbt_entity import DbtEntity +from .dbt_measure import DbtMeasure +from .dbt_metric import DbtMetric +from .dbt_model import DbtModel +from .dbt_model_column import DbtModelColumn +from .dbt_process import DbtProcess +from .dbt_seed import DbtSeed +from .dbt_semantic_model import DbtSemanticModel +from .dbt_source import DbtSource +from .dbt_tag import DbtTag +from .dbt_test import DbtTest + +__all__ = [ + "Dbt", + "DbtColumnProcess", + "DbtDimension", + "DbtEntity", + "DbtMeasure", + "DbtMetric", + "DbtModel", + "DbtModelColumn", + "DbtProcess", + "DbtSeed", + "DbtSemanticModel", + "DbtSource", + "DbtTag", + "DbtTest", + "RelatedDbt", + "RelatedDbtColumnProcess", + "RelatedDbtDimension", + "RelatedDbtEntity", + "RelatedDbtMeasure", + "RelatedDbtMetric", + "RelatedDbtModel", + "RelatedDbtModelColumn", + "RelatedDbtProcess", + "RelatedDbtSeed", + "RelatedDbtSemanticModel", + "RelatedDbtSource", + "RelatedDbtTag", + "RelatedDbtTest", +] diff --git a/pyatlan_v9/model/assets/_init_document_db.py b/pyatlan_v9/model/assets/_init_document_db.py new file mode 100644 index 000000000..fe0c74c68 --- /dev/null +++ b/pyatlan_v9/model/assets/_init_document_db.py @@ -0,0 +1,27 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DocumentDB module exports. + +This module provides convenient imports for all DocumentDB types and their Related variants. +""" + +from .document_db_related import ( + RelatedDocumentDB, + RelatedDocumentDBCollection, + RelatedDocumentDBDatabase, +) +from .document_db import DocumentDB +from .document_db_collection import DocumentDBCollection +from .document_db_database import DocumentDBDatabase + +__all__ = [ + "DocumentDB", + "DocumentDBCollection", + "DocumentDBDatabase", + "RelatedDocumentDB", + "RelatedDocumentDBCollection", + "RelatedDocumentDBDatabase", +] diff --git a/pyatlan_v9/model/assets/_init_domo.py b/pyatlan_v9/model/assets/_init_domo.py new file mode 100644 index 000000000..46b372286 --- /dev/null +++ b/pyatlan_v9/model/assets/_init_domo.py @@ -0,0 +1,35 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Domo module exports. + +This module provides convenient imports for all Domo types and their Related variants. +""" + +from .domo_related import ( + RelatedDomo, + RelatedDomoCard, + RelatedDomoDashboard, + RelatedDomoDataset, + RelatedDomoDatasetColumn, +) +from .domo import Domo +from .domo_card import DomoCard +from .domo_dashboard import DomoDashboard +from .domo_dataset import DomoDataset +from .domo_dataset_column import DomoDatasetColumn + +__all__ = [ + "Domo", + "DomoCard", + "DomoDashboard", + "DomoDataset", + "DomoDatasetColumn", + "RelatedDomo", + "RelatedDomoCard", + "RelatedDomoDashboard", + "RelatedDomoDataset", + "RelatedDomoDatasetColumn", +] diff --git a/pyatlan_v9/model/assets/_init_dremio.py b/pyatlan_v9/model/assets/_init_dremio.py new file mode 100644 index 000000000..24355d454 --- /dev/null +++ b/pyatlan_v9/model/assets/_init_dremio.py @@ -0,0 +1,43 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Dremio module exports. + +This module provides convenient imports for all Dremio types and their Related variants. +""" + +from .dremio_related import ( + RelatedDremio, + RelatedDremioColumn, + RelatedDremioFolder, + RelatedDremioPhysicalDataset, + RelatedDremioSource, + RelatedDremioSpace, + RelatedDremioVirtualDataset, +) +from .dremio import Dremio +from .dremio_column import DremioColumn +from .dremio_folder import DremioFolder +from .dremio_physical_dataset import DremioPhysicalDataset +from .dremio_source import DremioSource +from .dremio_space import DremioSpace +from .dremio_virtual_dataset import DremioVirtualDataset + +__all__ = [ + "Dremio", + "DremioColumn", + "DremioFolder", + "DremioPhysicalDataset", + "DremioSource", + "DremioSpace", + "DremioVirtualDataset", + "RelatedDremio", + "RelatedDremioColumn", + "RelatedDremioFolder", + "RelatedDremioPhysicalDataset", + "RelatedDremioSource", + "RelatedDremioSpace", + "RelatedDremioVirtualDataset", +] diff --git a/pyatlan_v9/model/assets/_init_dynamo_db.py b/pyatlan_v9/model/assets/_init_dynamo_db.py new file mode 100644 index 000000000..74fe7e66d --- /dev/null +++ b/pyatlan_v9/model/assets/_init_dynamo_db.py @@ -0,0 +1,35 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DynamoDB module exports. + +This module provides convenient imports for all DynamoDB types and their Related variants. +""" + +from .dynamo_db_related import ( + RelatedDynamoDB, + RelatedDynamoDBAttribute, + RelatedDynamoDBGlobalSecondaryIndex, + RelatedDynamoDBLocalSecondaryIndex, + RelatedDynamoDBSecondaryIndex, + RelatedDynamoDBTable, +) +from .dynamo_db import DynamoDB +from .dynamo_db_attribute import DynamoDBAttribute +from .dynamo_db_secondary_index import DynamoDBSecondaryIndex +from .dynamo_db_table import DynamoDBTable + +__all__ = [ + "DynamoDB", + "DynamoDBAttribute", + "DynamoDBSecondaryIndex", + "DynamoDBTable", + "RelatedDynamoDB", + "RelatedDynamoDBAttribute", + "RelatedDynamoDBGlobalSecondaryIndex", + "RelatedDynamoDBLocalSecondaryIndex", + "RelatedDynamoDBSecondaryIndex", + "RelatedDynamoDBTable", +] diff --git a/pyatlan_v9/model/assets/_init_fabric.py b/pyatlan_v9/model/assets/_init_fabric.py new file mode 100644 index 000000000..cda01e1cd --- /dev/null +++ b/pyatlan_v9/model/assets/_init_fabric.py @@ -0,0 +1,67 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Fabric module exports. + +This module provides convenient imports for all Fabric types and their Related variants. +""" + +from .fabric_related import ( + RelatedFabric, + RelatedFabricActivity, + RelatedFabricDashboard, + RelatedFabricDataPipeline, + RelatedFabricDataflow, + RelatedFabricDataflowEntityColumn, + RelatedFabricPage, + RelatedFabricReport, + RelatedFabricSemanticModel, + RelatedFabricSemanticModelTable, + RelatedFabricSemanticModelTableColumn, + RelatedFabricVisual, + RelatedFabricWorkspace, +) +from .fabric import Fabric +from .fabric_activity import FabricActivity +from .fabric_dashboard import FabricDashboard +from .fabric_data_pipeline import FabricDataPipeline +from .fabric_dataflow import FabricDataflow +from .fabric_dataflow_entity_column import FabricDataflowEntityColumn +from .fabric_page import FabricPage +from .fabric_report import FabricReport +from .fabric_semantic_model import FabricSemanticModel +from .fabric_semantic_model_table import FabricSemanticModelTable +from .fabric_semantic_model_table_column import FabricSemanticModelTableColumn +from .fabric_visual import FabricVisual +from .fabric_workspace import FabricWorkspace + +__all__ = [ + "Fabric", + "FabricActivity", + "FabricDashboard", + "FabricDataPipeline", + "FabricDataflow", + "FabricDataflowEntityColumn", + "FabricPage", + "FabricReport", + "FabricSemanticModel", + "FabricSemanticModelTable", + "FabricSemanticModelTableColumn", + "FabricVisual", + "FabricWorkspace", + "RelatedFabric", + "RelatedFabricActivity", + "RelatedFabricDashboard", + "RelatedFabricDataPipeline", + "RelatedFabricDataflow", + "RelatedFabricDataflowEntityColumn", + "RelatedFabricPage", + "RelatedFabricReport", + "RelatedFabricSemanticModel", + "RelatedFabricSemanticModelTable", + "RelatedFabricSemanticModelTableColumn", + "RelatedFabricVisual", + "RelatedFabricWorkspace", +] diff --git a/pyatlan_v9/model/assets/_init_fivetran.py b/pyatlan_v9/model/assets/_init_fivetran.py new file mode 100644 index 000000000..2d8d373da --- /dev/null +++ b/pyatlan_v9/model/assets/_init_fivetran.py @@ -0,0 +1,23 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Fivetran module exports. + +This module provides convenient imports for all Fivetran types and their Related variants. +""" + +from .fivetran_related import ( + RelatedFivetran, + RelatedFivetranConnector, +) +from .fivetran import Fivetran +from .fivetran_connector import FivetranConnector + +__all__ = [ + "Fivetran", + "FivetranConnector", + "RelatedFivetran", + "RelatedFivetranConnector", +] diff --git a/pyatlan_v9/model/assets/_init_flow.py b/pyatlan_v9/model/assets/_init_flow.py new file mode 100644 index 000000000..8d9c57373 --- /dev/null +++ b/pyatlan_v9/model/assets/_init_flow.py @@ -0,0 +1,51 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Flow module exports. + +This module provides convenient imports for all Flow types and their Related variants. +""" + +from .flow_related import ( + RelatedFlow, + RelatedFlowControlOperation, + RelatedFlowDataset, + RelatedFlowDatasetOperation, + RelatedFlowField, + RelatedFlowFieldOperation, + RelatedFlowFolder, + RelatedFlowProject, + RelatedFlowReusableUnit, +) +from .flow import Flow +from .flow_control_operation import FlowControlOperation +from .flow_dataset import FlowDataset +from .flow_dataset_operation import FlowDatasetOperation +from .flow_field import FlowField +from .flow_field_operation import FlowFieldOperation +from .flow_folder import FlowFolder +from .flow_project import FlowProject +from .flow_reusable_unit import FlowReusableUnit + +__all__ = [ + "Flow", + "FlowControlOperation", + "FlowDataset", + "FlowDatasetOperation", + "FlowField", + "FlowFieldOperation", + "FlowFolder", + "FlowProject", + "FlowReusableUnit", + "RelatedFlow", + "RelatedFlowControlOperation", + "RelatedFlowDataset", + "RelatedFlowDatasetOperation", + "RelatedFlowField", + "RelatedFlowFieldOperation", + "RelatedFlowFolder", + "RelatedFlowProject", + "RelatedFlowReusableUnit", +] diff --git a/pyatlan_v9/model/assets/_init_form.py b/pyatlan_v9/model/assets/_init_form.py new file mode 100644 index 000000000..38611274d --- /dev/null +++ b/pyatlan_v9/model/assets/_init_form.py @@ -0,0 +1,21 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Form module exports. + +This module provides convenient imports for all Form types and their Related variants. +""" + +from .form_related import ( + RelatedForm, + RelatedResponse, +) +from .form import Form + +__all__ = [ + "Form", + "RelatedForm", + "RelatedResponse", +] diff --git a/pyatlan_v9/model/assets/_init_gcs.py b/pyatlan_v9/model/assets/_init_gcs.py new file mode 100644 index 000000000..fb25f1ed5 --- /dev/null +++ b/pyatlan_v9/model/assets/_init_gcs.py @@ -0,0 +1,27 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +GCS module exports. + +This module provides convenient imports for all GCS types and their Related variants. +""" + +from .gcs_related import ( + RelatedGCS, + RelatedGCSBucket, + RelatedGCSObject, +) +from .gcs import GCS +from .gcs_bucket import GCSBucket +from .gcs_object import GCSObject + +__all__ = [ + "GCS", + "GCSBucket", + "GCSObject", + "RelatedGCS", + "RelatedGCSBucket", + "RelatedGCSObject", +] diff --git a/pyatlan_v9/model/assets/_init_gtc.py b/pyatlan_v9/model/assets/_init_gtc.py new file mode 100644 index 000000000..2d8b5b332 --- /dev/null +++ b/pyatlan_v9/model/assets/_init_gtc.py @@ -0,0 +1,27 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +GTC module exports. + +This module provides convenient imports for all GTC types and their Related variants. +""" + +from .gtc_related import ( + RelatedAtlasGlossary, + RelatedAtlasGlossaryCategory, + RelatedAtlasGlossaryTerm, +) +from .atlas_glossary import AtlasGlossary +from .atlas_glossary_category import AtlasGlossaryCategory +from .atlas_glossary_term import AtlasGlossaryTerm + +__all__ = [ + "AtlasGlossary", + "AtlasGlossaryCategory", + "AtlasGlossaryTerm", + "RelatedAtlasGlossary", + "RelatedAtlasGlossaryCategory", + "RelatedAtlasGlossaryTerm", +] diff --git a/pyatlan_v9/model/assets/_init_iceberg.py b/pyatlan_v9/model/assets/_init_iceberg.py new file mode 100644 index 000000000..68dcf0354 --- /dev/null +++ b/pyatlan_v9/model/assets/_init_iceberg.py @@ -0,0 +1,35 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Iceberg module exports. + +This module provides convenient imports for all Iceberg types and their Related variants. +""" + +from .iceberg_related import ( + RelatedIceberg, + RelatedIcebergCatalog, + RelatedIcebergColumn, + RelatedIcebergNamespace, + RelatedIcebergTable, +) +from .iceberg import Iceberg +from .iceberg_catalog import IcebergCatalog +from .iceberg_column import IcebergColumn +from .iceberg_namespace import IcebergNamespace +from .iceberg_table import IcebergTable + +__all__ = [ + "Iceberg", + "IcebergCatalog", + "IcebergColumn", + "IcebergNamespace", + "IcebergTable", + "RelatedIceberg", + "RelatedIcebergCatalog", + "RelatedIcebergColumn", + "RelatedIcebergNamespace", + "RelatedIcebergTable", +] diff --git a/pyatlan_v9/model/assets/_init_kafka.py b/pyatlan_v9/model/assets/_init_kafka.py new file mode 100644 index 000000000..1367e5ea9 --- /dev/null +++ b/pyatlan_v9/model/assets/_init_kafka.py @@ -0,0 +1,31 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Kafka module exports. + +This module provides convenient imports for all Kafka types and their Related variants. +""" + +from .kafka_related import ( + RelatedAzureEventHub, + RelatedAzureEventHubConsumerGroup, + RelatedKafka, + RelatedKafkaConsumerGroup, + RelatedKafkaTopic, +) +from .kafka import Kafka +from .kafka_consumer_group import KafkaConsumerGroup +from .kafka_topic import KafkaTopic + +__all__ = [ + "Kafka", + "KafkaConsumerGroup", + "KafkaTopic", + "RelatedAzureEventHub", + "RelatedAzureEventHubConsumerGroup", + "RelatedKafka", + "RelatedKafkaConsumerGroup", + "RelatedKafkaTopic", +] diff --git a/pyatlan_v9/model/assets/_init_looker.py b/pyatlan_v9/model/assets/_init_looker.py new file mode 100644 index 000000000..ebbbd0bd9 --- /dev/null +++ b/pyatlan_v9/model/assets/_init_looker.py @@ -0,0 +1,59 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Looker module exports. + +This module provides convenient imports for all Looker types and their Related variants. +""" + +from .looker_related import ( + RelatedLooker, + RelatedLookerDashboard, + RelatedLookerExplore, + RelatedLookerField, + RelatedLookerFolder, + RelatedLookerLook, + RelatedLookerModel, + RelatedLookerProject, + RelatedLookerQuery, + RelatedLookerTile, + RelatedLookerView, +) +from .looker import Looker +from .looker_dashboard import LookerDashboard +from .looker_explore import LookerExplore +from .looker_field import LookerField +from .looker_folder import LookerFolder +from .looker_look import LookerLook +from .looker_model import LookerModel +from .looker_project import LookerProject +from .looker_query import LookerQuery +from .looker_tile import LookerTile +from .looker_view import LookerView + +__all__ = [ + "Looker", + "LookerDashboard", + "LookerExplore", + "LookerField", + "LookerFolder", + "LookerLook", + "LookerModel", + "LookerProject", + "LookerQuery", + "LookerTile", + "LookerView", + "RelatedLooker", + "RelatedLookerDashboard", + "RelatedLookerExplore", + "RelatedLookerField", + "RelatedLookerFolder", + "RelatedLookerLook", + "RelatedLookerModel", + "RelatedLookerProject", + "RelatedLookerQuery", + "RelatedLookerTile", + "RelatedLookerView", +] diff --git a/pyatlan_v9/model/assets/_init_matillion.py b/pyatlan_v9/model/assets/_init_matillion.py new file mode 100644 index 000000000..fdd5ff9bc --- /dev/null +++ b/pyatlan_v9/model/assets/_init_matillion.py @@ -0,0 +1,35 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Matillion module exports. + +This module provides convenient imports for all Matillion types and their Related variants. +""" + +from .matillion_related import ( + RelatedMatillion, + RelatedMatillionComponent, + RelatedMatillionGroup, + RelatedMatillionJob, + RelatedMatillionProject, +) +from .matillion import Matillion +from .matillion_component import MatillionComponent +from .matillion_group import MatillionGroup +from .matillion_job import MatillionJob +from .matillion_project import MatillionProject + +__all__ = [ + "Matillion", + "MatillionComponent", + "MatillionGroup", + "MatillionJob", + "MatillionProject", + "RelatedMatillion", + "RelatedMatillionComponent", + "RelatedMatillionGroup", + "RelatedMatillionJob", + "RelatedMatillionProject", +] diff --git a/pyatlan_v9/model/assets/_init_metabase.py b/pyatlan_v9/model/assets/_init_metabase.py new file mode 100644 index 000000000..3366c0c7f --- /dev/null +++ b/pyatlan_v9/model/assets/_init_metabase.py @@ -0,0 +1,31 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Metabase module exports. + +This module provides convenient imports for all Metabase types and their Related variants. +""" + +from .metabase_related import ( + RelatedMetabase, + RelatedMetabaseCollection, + RelatedMetabaseDashboard, + RelatedMetabaseQuestion, +) +from .metabase import Metabase +from .metabase_collection import MetabaseCollection +from .metabase_dashboard import MetabaseDashboard +from .metabase_question import MetabaseQuestion + +__all__ = [ + "Metabase", + "MetabaseCollection", + "MetabaseDashboard", + "MetabaseQuestion", + "RelatedMetabase", + "RelatedMetabaseCollection", + "RelatedMetabaseDashboard", + "RelatedMetabaseQuestion", +] diff --git a/pyatlan_v9/model/assets/_init_micro_strategy.py b/pyatlan_v9/model/assets/_init_micro_strategy.py new file mode 100644 index 000000000..cb83792df --- /dev/null +++ b/pyatlan_v9/model/assets/_init_micro_strategy.py @@ -0,0 +1,59 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +MicroStrategy module exports. + +This module provides convenient imports for all MicroStrategy types and their Related variants. +""" + +from .micro_strategy_related import ( + RelatedMicroStrategy, + RelatedMicroStrategyAttribute, + RelatedMicroStrategyColumn, + RelatedMicroStrategyCube, + RelatedMicroStrategyDocument, + RelatedMicroStrategyDossier, + RelatedMicroStrategyFact, + RelatedMicroStrategyMetric, + RelatedMicroStrategyProject, + RelatedMicroStrategyReport, + RelatedMicroStrategyVisualization, +) +from .micro_strategy import MicroStrategy +from .micro_strategy_attribute import MicroStrategyAttribute +from .micro_strategy_column import MicroStrategyColumn +from .micro_strategy_cube import MicroStrategyCube +from .micro_strategy_document import MicroStrategyDocument +from .micro_strategy_dossier import MicroStrategyDossier +from .micro_strategy_fact import MicroStrategyFact +from .micro_strategy_metric import MicroStrategyMetric +from .micro_strategy_project import MicroStrategyProject +from .micro_strategy_report import MicroStrategyReport +from .micro_strategy_visualization import MicroStrategyVisualization + +__all__ = [ + "MicroStrategy", + "MicroStrategyAttribute", + "MicroStrategyColumn", + "MicroStrategyCube", + "MicroStrategyDocument", + "MicroStrategyDossier", + "MicroStrategyFact", + "MicroStrategyMetric", + "MicroStrategyProject", + "MicroStrategyReport", + "MicroStrategyVisualization", + "RelatedMicroStrategy", + "RelatedMicroStrategyAttribute", + "RelatedMicroStrategyColumn", + "RelatedMicroStrategyCube", + "RelatedMicroStrategyDocument", + "RelatedMicroStrategyDossier", + "RelatedMicroStrategyFact", + "RelatedMicroStrategyMetric", + "RelatedMicroStrategyProject", + "RelatedMicroStrategyReport", + "RelatedMicroStrategyVisualization", +] diff --git a/pyatlan_v9/model/assets/_init_mode.py b/pyatlan_v9/model/assets/_init_mode.py new file mode 100644 index 000000000..4ab2b2d3f --- /dev/null +++ b/pyatlan_v9/model/assets/_init_mode.py @@ -0,0 +1,39 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Mode module exports. + +This module provides convenient imports for all Mode types and their Related variants. +""" + +from .mode_related import ( + RelatedMode, + RelatedModeChart, + RelatedModeCollection, + RelatedModeQuery, + RelatedModeReport, + RelatedModeWorkspace, +) +from .mode import Mode +from .mode_chart import ModeChart +from .mode_collection import ModeCollection +from .mode_query import ModeQuery +from .mode_report import ModeReport +from .mode_workspace import ModeWorkspace + +__all__ = [ + "Mode", + "ModeChart", + "ModeCollection", + "ModeQuery", + "ModeReport", + "ModeWorkspace", + "RelatedMode", + "RelatedModeChart", + "RelatedModeCollection", + "RelatedModeQuery", + "RelatedModeReport", + "RelatedModeWorkspace", +] diff --git a/pyatlan_v9/model/assets/_init_model.py b/pyatlan_v9/model/assets/_init_model.py new file mode 100644 index 000000000..62b08c361 --- /dev/null +++ b/pyatlan_v9/model/assets/_init_model.py @@ -0,0 +1,43 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Model module exports. + +This module provides convenient imports for all Model types and their Related variants. +""" + +from .model_related import ( + RelatedModel, + RelatedModelAttribute, + RelatedModelAttributeAssociation, + RelatedModelDataModel, + RelatedModelEntity, + RelatedModelEntityAssociation, + RelatedModelVersion, +) +from .model import Model +from .model_attribute import ModelAttribute +from .model_attribute_association import ModelAttributeAssociation +from .model_data_model import ModelDataModel +from .model_entity import ModelEntity +from .model_entity_association import ModelEntityAssociation +from .model_version import ModelVersion + +__all__ = [ + "Model", + "ModelAttribute", + "ModelAttributeAssociation", + "ModelDataModel", + "ModelEntity", + "ModelEntityAssociation", + "ModelVersion", + "RelatedModel", + "RelatedModelAttribute", + "RelatedModelAttributeAssociation", + "RelatedModelDataModel", + "RelatedModelEntity", + "RelatedModelEntityAssociation", + "RelatedModelVersion", +] diff --git a/pyatlan_v9/model/assets/_init_mongo_db.py b/pyatlan_v9/model/assets/_init_mongo_db.py new file mode 100644 index 000000000..4bf44c463 --- /dev/null +++ b/pyatlan_v9/model/assets/_init_mongo_db.py @@ -0,0 +1,27 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +MongoDB module exports. + +This module provides convenient imports for all MongoDB types and their Related variants. +""" + +from .mongo_db_related import ( + RelatedMongoDB, + RelatedMongoDBCollection, + RelatedMongoDBDatabase, +) +from .mongo_db import MongoDB +from .mongo_db_collection import MongoDBCollection +from .mongo_db_database import MongoDBDatabase + +__all__ = [ + "MongoDB", + "MongoDBCollection", + "MongoDBDatabase", + "RelatedMongoDB", + "RelatedMongoDBCollection", + "RelatedMongoDBDatabase", +] diff --git a/pyatlan_v9/model/assets/_init_monte_carlo.py b/pyatlan_v9/model/assets/_init_monte_carlo.py new file mode 100644 index 000000000..2e1b21264 --- /dev/null +++ b/pyatlan_v9/model/assets/_init_monte_carlo.py @@ -0,0 +1,27 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +MonteCarlo module exports. + +This module provides convenient imports for all MonteCarlo types and their Related variants. +""" + +from .monte_carlo_related import ( + RelatedMCIncident, + RelatedMCMonitor, + RelatedMonteCarlo, +) +from .monte_carlo import MonteCarlo +from .mc_incident import MCIncident +from .mc_monitor import MCMonitor + +__all__ = [ + "MCIncident", + "MCMonitor", + "MonteCarlo", + "RelatedMCIncident", + "RelatedMCMonitor", + "RelatedMonteCarlo", +] diff --git a/pyatlan_v9/model/assets/_init_namespace.py b/pyatlan_v9/model/assets/_init_namespace.py new file mode 100644 index 000000000..46415bb8b --- /dev/null +++ b/pyatlan_v9/model/assets/_init_namespace.py @@ -0,0 +1,27 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Namespace module exports. + +This module provides convenient imports for all Namespace types and their Related variants. +""" + +from .namespace_related import ( + RelatedCollection, + RelatedFolder, + RelatedNamespace, +) +from .namespace import Namespace +from .collection import Collection +from .folder import Folder + +__all__ = [ + "Collection", + "Folder", + "Namespace", + "RelatedCollection", + "RelatedFolder", + "RelatedNamespace", +] diff --git a/pyatlan_v9/model/assets/_init_notebook.py b/pyatlan_v9/model/assets/_init_notebook.py new file mode 100644 index 000000000..1ad01bd46 --- /dev/null +++ b/pyatlan_v9/model/assets/_init_notebook.py @@ -0,0 +1,17 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Notebook module exports. + +This module provides convenient imports for all Notebook types and their Related variants. +""" + +from .notebook_related import RelatedNotebook +from .notebook import Notebook + +__all__ = [ + "Notebook", + "RelatedNotebook", +] diff --git a/pyatlan_v9/model/assets/_init_orchestration.py b/pyatlan_v9/model/assets/_init_orchestration.py new file mode 100644 index 000000000..c890577cc --- /dev/null +++ b/pyatlan_v9/model/assets/_init_orchestration.py @@ -0,0 +1,11 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Orchestration module exports. + +This module provides convenient imports for all Orchestration types and their Related variants. +""" + +__all__ = [] diff --git a/pyatlan_v9/model/assets/_init_partial.py b/pyatlan_v9/model/assets/_init_partial.py new file mode 100644 index 000000000..fb90e59ed --- /dev/null +++ b/pyatlan_v9/model/assets/_init_partial.py @@ -0,0 +1,27 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Partial module exports. + +This module provides convenient imports for all Partial types and their Related variants. +""" + +from .partial_related import ( + RelatedPartial, + RelatedPartialField, + RelatedPartialObject, +) +from .partial import Partial +from .partial_field import PartialField +from .partial_object import PartialObject + +__all__ = [ + "Partial", + "PartialField", + "PartialObject", + "RelatedPartial", + "RelatedPartialField", + "RelatedPartialObject", +] diff --git a/pyatlan_v9/model/assets/_init_power_bi.py b/pyatlan_v9/model/assets/_init_power_bi.py new file mode 100644 index 000000000..2e2dfe261 --- /dev/null +++ b/pyatlan_v9/model/assets/_init_power_bi.py @@ -0,0 +1,71 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +PowerBI module exports. + +This module provides convenient imports for all PowerBI types and their Related variants. +""" + +from .power_bi_related import ( + RelatedPowerBI, + RelatedPowerBIApp, + RelatedPowerBIColumn, + RelatedPowerBIDashboard, + RelatedPowerBIDataflow, + RelatedPowerBIDataflowEntityColumn, + RelatedPowerBIDataset, + RelatedPowerBIDatasource, + RelatedPowerBIMeasure, + RelatedPowerBIPage, + RelatedPowerBIReport, + RelatedPowerBITable, + RelatedPowerBITile, + RelatedPowerBIWorkspace, +) +from .power_bi import PowerBI +from .power_bi_app import PowerBIApp +from .power_bi_column import PowerBIColumn +from .power_bi_dashboard import PowerBIDashboard +from .power_bi_dataflow import PowerBIDataflow +from .power_bi_dataflow_entity_column import PowerBIDataflowEntityColumn +from .power_bi_dataset import PowerBIDataset +from .power_bi_datasource import PowerBIDatasource +from .power_bi_measure import PowerBIMeasure +from .power_bi_page import PowerBIPage +from .power_bi_report import PowerBIReport +from .power_bi_table import PowerBITable +from .power_bi_tile import PowerBITile +from .power_bi_workspace import PowerBIWorkspace + +__all__ = [ + "PowerBI", + "PowerBIApp", + "PowerBIColumn", + "PowerBIDashboard", + "PowerBIDataflow", + "PowerBIDataflowEntityColumn", + "PowerBIDataset", + "PowerBIDatasource", + "PowerBIMeasure", + "PowerBIPage", + "PowerBIReport", + "PowerBITable", + "PowerBITile", + "PowerBIWorkspace", + "RelatedPowerBI", + "RelatedPowerBIApp", + "RelatedPowerBIColumn", + "RelatedPowerBIDashboard", + "RelatedPowerBIDataflow", + "RelatedPowerBIDataflowEntityColumn", + "RelatedPowerBIDataset", + "RelatedPowerBIDatasource", + "RelatedPowerBIMeasure", + "RelatedPowerBIPage", + "RelatedPowerBIReport", + "RelatedPowerBITable", + "RelatedPowerBITile", + "RelatedPowerBIWorkspace", +] diff --git a/pyatlan_v9/model/assets/_init_preset.py b/pyatlan_v9/model/assets/_init_preset.py new file mode 100644 index 000000000..4d14090b0 --- /dev/null +++ b/pyatlan_v9/model/assets/_init_preset.py @@ -0,0 +1,35 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Preset module exports. + +This module provides convenient imports for all Preset types and their Related variants. +""" + +from .preset_related import ( + RelatedPreset, + RelatedPresetChart, + RelatedPresetDashboard, + RelatedPresetDataset, + RelatedPresetWorkspace, +) +from .preset import Preset +from .preset_chart import PresetChart +from .preset_dashboard import PresetDashboard +from .preset_dataset import PresetDataset +from .preset_workspace import PresetWorkspace + +__all__ = [ + "Preset", + "PresetChart", + "PresetDashboard", + "PresetDataset", + "PresetWorkspace", + "RelatedPreset", + "RelatedPresetChart", + "RelatedPresetDashboard", + "RelatedPresetDataset", + "RelatedPresetWorkspace", +] diff --git a/pyatlan_v9/model/assets/_init_process.py b/pyatlan_v9/model/assets/_init_process.py new file mode 100644 index 000000000..93878a4bf --- /dev/null +++ b/pyatlan_v9/model/assets/_init_process.py @@ -0,0 +1,29 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Process module exports. + +This module provides convenient imports for all Process types and their Related variants. +""" + +from .process_related import ( + RelatedBIProcess, + RelatedColumnProcess, + RelatedConnectionProcess, + RelatedProcess, +) +from .process import Process +from .bi_process import BIProcess +from .column_process import ColumnProcess + +__all__ = [ + "BIProcess", + "ColumnProcess", + "Process", + "RelatedBIProcess", + "RelatedColumnProcess", + "RelatedConnectionProcess", + "RelatedProcess", +] diff --git a/pyatlan_v9/model/assets/_init_qlik.py b/pyatlan_v9/model/assets/_init_qlik.py new file mode 100644 index 000000000..26ef5438b --- /dev/null +++ b/pyatlan_v9/model/assets/_init_qlik.py @@ -0,0 +1,45 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Qlik module exports. + +This module provides convenient imports for all Qlik types and their Related variants. +""" + +from .qlik_related import ( + RelatedQlik, + RelatedQlikApp, + RelatedQlikChart, + RelatedQlikColumn, + RelatedQlikDataset, + RelatedQlikSheet, + RelatedQlikSpace, + RelatedQlikStream, +) +from .qlik import Qlik +from .qlik_app import QlikApp +from .qlik_chart import QlikChart +from .qlik_column import QlikColumn +from .qlik_dataset import QlikDataset +from .qlik_sheet import QlikSheet +from .qlik_space import QlikSpace + +__all__ = [ + "Qlik", + "QlikApp", + "QlikChart", + "QlikColumn", + "QlikDataset", + "QlikSheet", + "QlikSpace", + "RelatedQlik", + "RelatedQlikApp", + "RelatedQlikChart", + "RelatedQlikColumn", + "RelatedQlikDataset", + "RelatedQlikSheet", + "RelatedQlikSpace", + "RelatedQlikStream", +] diff --git a/pyatlan_v9/model/assets/_init_quick_sight.py b/pyatlan_v9/model/assets/_init_quick_sight.py new file mode 100644 index 000000000..844246682 --- /dev/null +++ b/pyatlan_v9/model/assets/_init_quick_sight.py @@ -0,0 +1,47 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +QuickSight module exports. + +This module provides convenient imports for all QuickSight types and their Related variants. +""" + +from .quick_sight_related import ( + RelatedQuickSight, + RelatedQuickSightAnalysis, + RelatedQuickSightAnalysisVisual, + RelatedQuickSightDashboard, + RelatedQuickSightDashboardVisual, + RelatedQuickSightDataset, + RelatedQuickSightDatasetField, + RelatedQuickSightFolder, +) +from .quick_sight import QuickSight +from .quick_sight_analysis import QuickSightAnalysis +from .quick_sight_analysis_visual import QuickSightAnalysisVisual +from .quick_sight_dashboard import QuickSightDashboard +from .quick_sight_dashboard_visual import QuickSightDashboardVisual +from .quick_sight_dataset import QuickSightDataset +from .quick_sight_dataset_field import QuickSightDatasetField +from .quick_sight_folder import QuickSightFolder + +__all__ = [ + "QuickSight", + "QuickSightAnalysis", + "QuickSightAnalysisVisual", + "QuickSightDashboard", + "QuickSightDashboardVisual", + "QuickSightDataset", + "QuickSightDatasetField", + "QuickSightFolder", + "RelatedQuickSight", + "RelatedQuickSightAnalysis", + "RelatedQuickSightAnalysisVisual", + "RelatedQuickSightDashboard", + "RelatedQuickSightDashboardVisual", + "RelatedQuickSightDataset", + "RelatedQuickSightDatasetField", + "RelatedQuickSightFolder", +] diff --git a/pyatlan_v9/model/assets/_init_redash.py b/pyatlan_v9/model/assets/_init_redash.py new file mode 100644 index 000000000..489f8eae2 --- /dev/null +++ b/pyatlan_v9/model/assets/_init_redash.py @@ -0,0 +1,31 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Redash module exports. + +This module provides convenient imports for all Redash types and their Related variants. +""" + +from .redash_related import ( + RelatedRedash, + RelatedRedashDashboard, + RelatedRedashQuery, + RelatedRedashVisualization, +) +from .redash import Redash +from .redash_dashboard import RedashDashboard +from .redash_query import RedashQuery +from .redash_visualization import RedashVisualization + +__all__ = [ + "Redash", + "RedashDashboard", + "RedashQuery", + "RedashVisualization", + "RelatedRedash", + "RelatedRedashDashboard", + "RelatedRedashQuery", + "RelatedRedashVisualization", +] diff --git a/pyatlan_v9/model/assets/_init_referenceable.py b/pyatlan_v9/model/assets/_init_referenceable.py new file mode 100644 index 000000000..7fe9cf852 --- /dev/null +++ b/pyatlan_v9/model/assets/_init_referenceable.py @@ -0,0 +1,21 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Referenceable module exports. + +This module provides convenient imports for all Referenceable types and their Related variants. +""" + +from .entity import Entity, AtlasClassification, TermAssignment +from .referenceable_related import RelatedReferenceable +from .referenceable import Referenceable + +__all__ = [ + "AtlasClassification", + "Entity", + "Referenceable", + "RelatedReferenceable", + "TermAssignment", +] diff --git a/pyatlan_v9/model/assets/_init_resource.py b/pyatlan_v9/model/assets/_init_resource.py new file mode 100644 index 000000000..afd9befac --- /dev/null +++ b/pyatlan_v9/model/assets/_init_resource.py @@ -0,0 +1,39 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Resource module exports. + +This module provides convenient imports for all Resource types and their Related variants. +""" + +from .resource_related import ( + RelatedBadge, + RelatedFile, + RelatedLink, + RelatedReadme, + RelatedReadmeTemplate, + RelatedResource, + Related__internal, +) +from .resource import Resource +from .file import File +from .link import Link +from .readme import Readme +from .readme_template import ReadmeTemplate + +__all__ = [ + "File", + "Link", + "Readme", + "ReadmeTemplate", + "RelatedBadge", + "RelatedFile", + "RelatedLink", + "RelatedReadme", + "RelatedReadmeTemplate", + "RelatedResource", + "Related__internal", + "Resource", +] diff --git a/pyatlan_v9/model/assets/_init_s3.py b/pyatlan_v9/model/assets/_init_s3.py new file mode 100644 index 000000000..1c4800ec7 --- /dev/null +++ b/pyatlan_v9/model/assets/_init_s3.py @@ -0,0 +1,31 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +S3 module exports. + +This module provides convenient imports for all S3 types and their Related variants. +""" + +from .s3_related import ( + RelatedS3, + RelatedS3Bucket, + RelatedS3Object, + RelatedS3Prefix, +) +from .s3 import S3 +from .s3_bucket import S3Bucket +from .s3_object import S3Object +from .s3_prefix import S3Prefix + +__all__ = [ + "RelatedS3", + "RelatedS3Bucket", + "RelatedS3Object", + "RelatedS3Prefix", + "S3", + "S3Bucket", + "S3Object", + "S3Prefix", +] diff --git a/pyatlan_v9/model/assets/_init_sage_maker.py b/pyatlan_v9/model/assets/_init_sage_maker.py new file mode 100644 index 000000000..136b334f4 --- /dev/null +++ b/pyatlan_v9/model/assets/_init_sage_maker.py @@ -0,0 +1,39 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SageMaker module exports. + +This module provides convenient imports for all SageMaker types and their Related variants. +""" + +from .sage_maker_related import ( + RelatedSageMaker, + RelatedSageMakerFeature, + RelatedSageMakerFeatureGroup, + RelatedSageMakerModel, + RelatedSageMakerModelDeployment, + RelatedSageMakerModelGroup, +) +from .sage_maker import SageMaker +from .sage_maker_feature import SageMakerFeature +from .sage_maker_feature_group import SageMakerFeatureGroup +from .sage_maker_model import SageMakerModel +from .sage_maker_model_deployment import SageMakerModelDeployment +from .sage_maker_model_group import SageMakerModelGroup + +__all__ = [ + "RelatedSageMaker", + "RelatedSageMakerFeature", + "RelatedSageMakerFeatureGroup", + "RelatedSageMakerModel", + "RelatedSageMakerModelDeployment", + "RelatedSageMakerModelGroup", + "SageMaker", + "SageMakerFeature", + "SageMakerFeatureGroup", + "SageMakerModel", + "SageMakerModelDeployment", + "SageMakerModelGroup", +] diff --git a/pyatlan_v9/model/assets/_init_sage_maker_unified_studio.py b/pyatlan_v9/model/assets/_init_sage_maker_unified_studio.py new file mode 100644 index 000000000..f14f8da3f --- /dev/null +++ b/pyatlan_v9/model/assets/_init_sage_maker_unified_studio.py @@ -0,0 +1,43 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SageMakerUnifiedStudio module exports. + +This module provides convenient imports for all SageMakerUnifiedStudio types and their Related variants. +""" + +from .sage_maker_unified_studio_related import ( + RelatedSageMakerUnifiedStudio, + RelatedSageMakerUnifiedStudioAsset, + RelatedSageMakerUnifiedStudioAssetSchema, + RelatedSageMakerUnifiedStudioProject, + RelatedSageMakerUnifiedStudioPublishedAsset, + RelatedSageMakerUnifiedStudioSubscribedAsset, +) +from .sage_maker_unified_studio import SageMakerUnifiedStudio +from .sage_maker_unified_studio_asset import SageMakerUnifiedStudioAsset +from .sage_maker_unified_studio_asset_schema import SageMakerUnifiedStudioAssetSchema +from .sage_maker_unified_studio_project import SageMakerUnifiedStudioProject +from .sage_maker_unified_studio_published_asset import ( + SageMakerUnifiedStudioPublishedAsset, +) +from .sage_maker_unified_studio_subscribed_asset import ( + SageMakerUnifiedStudioSubscribedAsset, +) + +__all__ = [ + "RelatedSageMakerUnifiedStudio", + "RelatedSageMakerUnifiedStudioAsset", + "RelatedSageMakerUnifiedStudioAssetSchema", + "RelatedSageMakerUnifiedStudioProject", + "RelatedSageMakerUnifiedStudioPublishedAsset", + "RelatedSageMakerUnifiedStudioSubscribedAsset", + "SageMakerUnifiedStudio", + "SageMakerUnifiedStudioAsset", + "SageMakerUnifiedStudioAssetSchema", + "SageMakerUnifiedStudioProject", + "SageMakerUnifiedStudioPublishedAsset", + "SageMakerUnifiedStudioSubscribedAsset", +] diff --git a/pyatlan_v9/model/assets/_init_salesforce.py b/pyatlan_v9/model/assets/_init_salesforce.py new file mode 100644 index 000000000..c27552f52 --- /dev/null +++ b/pyatlan_v9/model/assets/_init_salesforce.py @@ -0,0 +1,39 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Salesforce module exports. + +This module provides convenient imports for all Salesforce types and their Related variants. +""" + +from .salesforce_related import ( + RelatedSalesforce, + RelatedSalesforceDashboard, + RelatedSalesforceField, + RelatedSalesforceObject, + RelatedSalesforceOrganization, + RelatedSalesforceReport, +) +from .salesforce import Salesforce +from .salesforce_dashboard import SalesforceDashboard +from .salesforce_field import SalesforceField +from .salesforce_object import SalesforceObject +from .salesforce_organization import SalesforceOrganization +from .salesforce_report import SalesforceReport + +__all__ = [ + "RelatedSalesforce", + "RelatedSalesforceDashboard", + "RelatedSalesforceField", + "RelatedSalesforceObject", + "RelatedSalesforceOrganization", + "RelatedSalesforceReport", + "Salesforce", + "SalesforceDashboard", + "SalesforceField", + "SalesforceObject", + "SalesforceOrganization", + "SalesforceReport", +] diff --git a/pyatlan_v9/model/assets/_init_sap.py b/pyatlan_v9/model/assets/_init_sap.py new file mode 100644 index 000000000..0491005ba --- /dev/null +++ b/pyatlan_v9/model/assets/_init_sap.py @@ -0,0 +1,51 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SAP module exports. + +This module provides convenient imports for all SAP types and their Related variants. +""" + +from .sap_related import ( + RelatedSAP, + RelatedSapErpAbapProgram, + RelatedSapErpCdsView, + RelatedSapErpColumn, + RelatedSapErpComponent, + RelatedSapErpFunctionModule, + RelatedSapErpTable, + RelatedSapErpTransactionCode, + RelatedSapErpView, +) +from .sap import SAP +from .sap_erp_abap_program import SapErpAbapProgram +from .sap_erp_cds_view import SapErpCdsView +from .sap_erp_column import SapErpColumn +from .sap_erp_component import SapErpComponent +from .sap_erp_function_module import SapErpFunctionModule +from .sap_erp_table import SapErpTable +from .sap_erp_transaction_code import SapErpTransactionCode +from .sap_erp_view import SapErpView + +__all__ = [ + "RelatedSAP", + "RelatedSapErpAbapProgram", + "RelatedSapErpCdsView", + "RelatedSapErpColumn", + "RelatedSapErpComponent", + "RelatedSapErpFunctionModule", + "RelatedSapErpTable", + "RelatedSapErpTransactionCode", + "RelatedSapErpView", + "SAP", + "SapErpAbapProgram", + "SapErpCdsView", + "SapErpColumn", + "SapErpComponent", + "SapErpFunctionModule", + "SapErpTable", + "SapErpTransactionCode", + "SapErpView", +] diff --git a/pyatlan_v9/model/assets/_init_schema_registry.py b/pyatlan_v9/model/assets/_init_schema_registry.py new file mode 100644 index 000000000..a9449f5dd --- /dev/null +++ b/pyatlan_v9/model/assets/_init_schema_registry.py @@ -0,0 +1,23 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SchemaRegistry module exports. + +This module provides convenient imports for all SchemaRegistry types and their Related variants. +""" + +from .schema_registry_related import ( + RelatedSchemaRegistry, + RelatedSchemaRegistrySubject, +) +from .schema_registry import SchemaRegistry +from .schema_registry_subject import SchemaRegistrySubject + +__all__ = [ + "RelatedSchemaRegistry", + "RelatedSchemaRegistrySubject", + "SchemaRegistry", + "SchemaRegistrySubject", +] diff --git a/pyatlan_v9/model/assets/_init_semantic.py b/pyatlan_v9/model/assets/_init_semantic.py new file mode 100644 index 000000000..3ad039cc2 --- /dev/null +++ b/pyatlan_v9/model/assets/_init_semantic.py @@ -0,0 +1,39 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Semantic module exports. + +This module provides convenient imports for all Semantic types and their Related variants. +""" + +from .semantic_related import ( + RelatedSemantic, + RelatedSemanticDimension, + RelatedSemanticEntity, + RelatedSemanticField, + RelatedSemanticMeasure, + RelatedSemanticModel, +) +from .semantic import Semantic +from .semantic_dimension import SemanticDimension +from .semantic_entity import SemanticEntity +from .semantic_field import SemanticField +from .semantic_measure import SemanticMeasure +from .semantic_model import SemanticModel + +__all__ = [ + "RelatedSemantic", + "RelatedSemanticDimension", + "RelatedSemanticEntity", + "RelatedSemanticField", + "RelatedSemanticMeasure", + "RelatedSemanticModel", + "Semantic", + "SemanticDimension", + "SemanticEntity", + "SemanticField", + "SemanticMeasure", + "SemanticModel", +] diff --git a/pyatlan_v9/model/assets/_init_sigma.py b/pyatlan_v9/model/assets/_init_sigma.py new file mode 100644 index 000000000..7d891e06d --- /dev/null +++ b/pyatlan_v9/model/assets/_init_sigma.py @@ -0,0 +1,43 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Sigma module exports. + +This module provides convenient imports for all Sigma types and their Related variants. +""" + +from .sigma_related import ( + RelatedSigma, + RelatedSigmaDataElement, + RelatedSigmaDataElementField, + RelatedSigmaDataset, + RelatedSigmaDatasetColumn, + RelatedSigmaPage, + RelatedSigmaWorkbook, +) +from .sigma import Sigma +from .sigma_data_element import SigmaDataElement +from .sigma_data_element_field import SigmaDataElementField +from .sigma_dataset import SigmaDataset +from .sigma_dataset_column import SigmaDatasetColumn +from .sigma_page import SigmaPage +from .sigma_workbook import SigmaWorkbook + +__all__ = [ + "RelatedSigma", + "RelatedSigmaDataElement", + "RelatedSigmaDataElementField", + "RelatedSigmaDataset", + "RelatedSigmaDatasetColumn", + "RelatedSigmaPage", + "RelatedSigmaWorkbook", + "Sigma", + "SigmaDataElement", + "SigmaDataElementField", + "SigmaDataset", + "SigmaDatasetColumn", + "SigmaPage", + "SigmaWorkbook", +] diff --git a/pyatlan_v9/model/assets/_init_sisense.py b/pyatlan_v9/model/assets/_init_sisense.py new file mode 100644 index 000000000..efc477a7e --- /dev/null +++ b/pyatlan_v9/model/assets/_init_sisense.py @@ -0,0 +1,39 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Sisense module exports. + +This module provides convenient imports for all Sisense types and their Related variants. +""" + +from .sisense_related import ( + RelatedSisense, + RelatedSisenseDashboard, + RelatedSisenseDatamodel, + RelatedSisenseDatamodelTable, + RelatedSisenseFolder, + RelatedSisenseWidget, +) +from .sisense import Sisense +from .sisense_dashboard import SisenseDashboard +from .sisense_datamodel import SisenseDatamodel +from .sisense_datamodel_table import SisenseDatamodelTable +from .sisense_folder import SisenseFolder +from .sisense_widget import SisenseWidget + +__all__ = [ + "RelatedSisense", + "RelatedSisenseDashboard", + "RelatedSisenseDatamodel", + "RelatedSisenseDatamodelTable", + "RelatedSisenseFolder", + "RelatedSisenseWidget", + "Sisense", + "SisenseDashboard", + "SisenseDatamodel", + "SisenseDatamodelTable", + "SisenseFolder", + "SisenseWidget", +] diff --git a/pyatlan_v9/model/assets/_init_snowflake.py b/pyatlan_v9/model/assets/_init_snowflake.py new file mode 100644 index 000000000..af8f4b97b --- /dev/null +++ b/pyatlan_v9/model/assets/_init_snowflake.py @@ -0,0 +1,57 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Snowflake module exports. + +This module provides convenient imports for all Snowflake types and their Related variants. +""" + +from .snowflake_related import ( + RelatedSnowflake, + RelatedSnowflakeAIModelContext, + RelatedSnowflakeAIModelVersion, + RelatedSnowflakeDynamicTable, + RelatedSnowflakePipe, + RelatedSnowflakeSemanticDimension, + RelatedSnowflakeSemanticFact, + RelatedSnowflakeSemanticLogicalTable, + RelatedSnowflakeSemanticMetric, + RelatedSnowflakeSemanticView, + RelatedSnowflakeStage, + RelatedSnowflakeStream, + RelatedSnowflakeTag, +) +from .snowflake import Snowflake +from .snowflake_ai_model_context import SnowflakeAIModelContext +from .snowflake_ai_model_version import SnowflakeAIModelVersion +from .snowflake_semantic_dimension import SnowflakeSemanticDimension +from .snowflake_semantic_fact import SnowflakeSemanticFact +from .snowflake_semantic_logical_table import SnowflakeSemanticLogicalTable +from .snowflake_semantic_metric import SnowflakeSemanticMetric +from .snowflake_semantic_view import SnowflakeSemanticView + +__all__ = [ + "RelatedSnowflake", + "RelatedSnowflakeAIModelContext", + "RelatedSnowflakeAIModelVersion", + "RelatedSnowflakeDynamicTable", + "RelatedSnowflakePipe", + "RelatedSnowflakeSemanticDimension", + "RelatedSnowflakeSemanticFact", + "RelatedSnowflakeSemanticLogicalTable", + "RelatedSnowflakeSemanticMetric", + "RelatedSnowflakeSemanticView", + "RelatedSnowflakeStage", + "RelatedSnowflakeStream", + "RelatedSnowflakeTag", + "Snowflake", + "SnowflakeAIModelContext", + "SnowflakeAIModelVersion", + "SnowflakeSemanticDimension", + "SnowflakeSemanticFact", + "SnowflakeSemanticLogicalTable", + "SnowflakeSemanticMetric", + "SnowflakeSemanticView", +] diff --git a/pyatlan_v9/model/assets/_init_soda.py b/pyatlan_v9/model/assets/_init_soda.py new file mode 100644 index 000000000..60bcc499c --- /dev/null +++ b/pyatlan_v9/model/assets/_init_soda.py @@ -0,0 +1,23 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Soda module exports. + +This module provides convenient imports for all Soda types and their Related variants. +""" + +from .soda_related import ( + RelatedSoda, + RelatedSodaCheck, +) +from .soda import Soda +from .soda_check import SodaCheck + +__all__ = [ + "RelatedSoda", + "RelatedSodaCheck", + "Soda", + "SodaCheck", +] diff --git a/pyatlan_v9/model/assets/_init_spark.py b/pyatlan_v9/model/assets/_init_spark.py new file mode 100644 index 000000000..76aa5d940 --- /dev/null +++ b/pyatlan_v9/model/assets/_init_spark.py @@ -0,0 +1,23 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Spark module exports. + +This module provides convenient imports for all Spark types and their Related variants. +""" + +from .spark_related import ( + RelatedSpark, + RelatedSparkJob, +) +from .spark import Spark +from .spark_job import SparkJob + +__all__ = [ + "RelatedSpark", + "RelatedSparkJob", + "Spark", + "SparkJob", +] diff --git a/pyatlan_v9/model/assets/_init_sql.py b/pyatlan_v9/model/assets/_init_sql.py new file mode 100644 index 000000000..54db192a6 --- /dev/null +++ b/pyatlan_v9/model/assets/_init_sql.py @@ -0,0 +1,63 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SQL module exports. + +This module provides convenient imports for all SQL types and their Related variants. +""" + +from .sql_related import ( + RelatedCalculationView, + RelatedColumn, + RelatedDatabase, + RelatedFunction, + RelatedMaterialisedView, + RelatedProcedure, + RelatedQuery, + RelatedSQL, + RelatedSchema, + RelatedTable, + RelatedTablePartition, + RelatedView, +) +from .sql import SQL +from .calculation_view import CalculationView +from .column import Column +from .database import Database +from .function import Function +from .materialised_view import MaterialisedView +from .procedure import Procedure +from .query import Query +from .schema import Schema +from .table import Table +from .table_partition import TablePartition +from .view import View + +__all__ = [ + "CalculationView", + "Column", + "Database", + "Function", + "MaterialisedView", + "Procedure", + "Query", + "RelatedCalculationView", + "RelatedColumn", + "RelatedDatabase", + "RelatedFunction", + "RelatedMaterialisedView", + "RelatedProcedure", + "RelatedQuery", + "RelatedSQL", + "RelatedSchema", + "RelatedTable", + "RelatedTablePartition", + "RelatedView", + "SQL", + "Schema", + "Table", + "TablePartition", + "View", +] diff --git a/pyatlan_v9/model/assets/_init_starburst.py b/pyatlan_v9/model/assets/_init_starburst.py new file mode 100644 index 000000000..0579647e2 --- /dev/null +++ b/pyatlan_v9/model/assets/_init_starburst.py @@ -0,0 +1,27 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Starburst module exports. + +This module provides convenient imports for all Starburst types and their Related variants. +""" + +from .starburst_related import ( + RelatedStarburst, + RelatedStarburstDataset, + RelatedStarburstDatasetColumn, +) +from .starburst import Starburst +from .starburst_dataset import StarburstDataset +from .starburst_dataset_column import StarburstDatasetColumn + +__all__ = [ + "RelatedStarburst", + "RelatedStarburstDataset", + "RelatedStarburstDatasetColumn", + "Starburst", + "StarburstDataset", + "StarburstDatasetColumn", +] diff --git a/pyatlan_v9/model/assets/_init_tableau.py b/pyatlan_v9/model/assets/_init_tableau.py new file mode 100644 index 000000000..bf172b445 --- /dev/null +++ b/pyatlan_v9/model/assets/_init_tableau.py @@ -0,0 +1,67 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Tableau module exports. + +This module provides convenient imports for all Tableau types and their Related variants. +""" + +from .tableau_related import ( + RelatedTableau, + RelatedTableauCalculatedField, + RelatedTableauDashboard, + RelatedTableauDashboardField, + RelatedTableauDatasource, + RelatedTableauDatasourceField, + RelatedTableauFlow, + RelatedTableauMetric, + RelatedTableauProject, + RelatedTableauSite, + RelatedTableauWorkbook, + RelatedTableauWorksheet, + RelatedTableauWorksheetField, +) +from .tableau import Tableau +from .tableau_calculated_field import TableauCalculatedField +from .tableau_dashboard import TableauDashboard +from .tableau_dashboard_field import TableauDashboardField +from .tableau_datasource import TableauDatasource +from .tableau_datasource_field import TableauDatasourceField +from .tableau_flow import TableauFlow +from .tableau_metric import TableauMetric +from .tableau_project import TableauProject +from .tableau_site import TableauSite +from .tableau_workbook import TableauWorkbook +from .tableau_worksheet import TableauWorksheet +from .tableau_worksheet_field import TableauWorksheetField + +__all__ = [ + "RelatedTableau", + "RelatedTableauCalculatedField", + "RelatedTableauDashboard", + "RelatedTableauDashboardField", + "RelatedTableauDatasource", + "RelatedTableauDatasourceField", + "RelatedTableauFlow", + "RelatedTableauMetric", + "RelatedTableauProject", + "RelatedTableauSite", + "RelatedTableauWorkbook", + "RelatedTableauWorksheet", + "RelatedTableauWorksheetField", + "Tableau", + "TableauCalculatedField", + "TableauDashboard", + "TableauDashboardField", + "TableauDatasource", + "TableauDatasourceField", + "TableauFlow", + "TableauMetric", + "TableauProject", + "TableauSite", + "TableauWorkbook", + "TableauWorksheet", + "TableauWorksheetField", +] diff --git a/pyatlan_v9/model/assets/_init_tag.py b/pyatlan_v9/model/assets/_init_tag.py new file mode 100644 index 000000000..8fdefd194 --- /dev/null +++ b/pyatlan_v9/model/assets/_init_tag.py @@ -0,0 +1,25 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Tag module exports. + +This module provides convenient imports for all Tag types and their Related variants. +""" + +from .tag_related import ( + RelatedSourceTag, + RelatedTag, + RelatedTagAttachment, +) +from .tag import Tag +from .source_tag import SourceTag + +__all__ = [ + "RelatedSourceTag", + "RelatedTag", + "RelatedTagAttachment", + "SourceTag", + "Tag", +] diff --git a/pyatlan_v9/model/assets/_init_task.py b/pyatlan_v9/model/assets/_init_task.py new file mode 100644 index 000000000..70a6a7c43 --- /dev/null +++ b/pyatlan_v9/model/assets/_init_task.py @@ -0,0 +1,17 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Task module exports. + +This module provides convenient imports for all Task types and their Related variants. +""" + +from .task_related import RelatedTask +from .task import Task + +__all__ = [ + "RelatedTask", + "Task", +] diff --git a/pyatlan_v9/model/assets/_init_thoughtspot.py b/pyatlan_v9/model/assets/_init_thoughtspot.py new file mode 100644 index 000000000..245c87e3f --- /dev/null +++ b/pyatlan_v9/model/assets/_init_thoughtspot.py @@ -0,0 +1,47 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Thoughtspot module exports. + +This module provides convenient imports for all Thoughtspot types and their Related variants. +""" + +from .thoughtspot_related import ( + RelatedThoughtspot, + RelatedThoughtspotAnswer, + RelatedThoughtspotColumn, + RelatedThoughtspotDashlet, + RelatedThoughtspotLiveboard, + RelatedThoughtspotTable, + RelatedThoughtspotView, + RelatedThoughtspotWorksheet, +) +from .thoughtspot import Thoughtspot +from .thoughtspot_answer import ThoughtspotAnswer +from .thoughtspot_column import ThoughtspotColumn +from .thoughtspot_dashlet import ThoughtspotDashlet +from .thoughtspot_liveboard import ThoughtspotLiveboard +from .thoughtspot_table import ThoughtspotTable +from .thoughtspot_view import ThoughtspotView +from .thoughtspot_worksheet import ThoughtspotWorksheet + +__all__ = [ + "RelatedThoughtspot", + "RelatedThoughtspotAnswer", + "RelatedThoughtspotColumn", + "RelatedThoughtspotDashlet", + "RelatedThoughtspotLiveboard", + "RelatedThoughtspotTable", + "RelatedThoughtspotView", + "RelatedThoughtspotWorksheet", + "Thoughtspot", + "ThoughtspotAnswer", + "ThoughtspotColumn", + "ThoughtspotDashlet", + "ThoughtspotLiveboard", + "ThoughtspotTable", + "ThoughtspotView", + "ThoughtspotWorksheet", +] diff --git a/pyatlan_v9/model/assets/_init_workflow.py b/pyatlan_v9/model/assets/_init_workflow.py new file mode 100644 index 000000000..81e8a1c0e --- /dev/null +++ b/pyatlan_v9/model/assets/_init_workflow.py @@ -0,0 +1,21 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Workflow module exports. + +This module provides convenient imports for all Workflow types and their Related variants. +""" + +from .workflow_related import ( + RelatedWorkflow, + RelatedWorkflowRun, +) +from .workflow import Workflow + +__all__ = [ + "RelatedWorkflow", + "RelatedWorkflowRun", + "Workflow", +] diff --git a/pyatlan_v9/model/assets/_overlays/adls_account.py b/pyatlan_v9/model/assets/_overlays/adls_account.py new file mode 100644 index 000000000..e158807ff --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/adls_account.py @@ -0,0 +1,33 @@ +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + connection_qualified_name: str, + ) -> "ADLSAccount": + validate_required_fields( + ["name", "connection_qualified_name"], + [name, connection_qualified_name], + ) + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + qualified_name = f"{connection_qualified_name}/{name}" + return cls( + name=name, + qualified_name=qualified_name, + connection_qualified_name=connection_qualified_name, + connector_name=connector_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "ADLSAccount": + """Create an ADLSAccount instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "ADLSAccount": + """Return only fields required for update operations.""" + return ADLSAccount.updater(qualified_name=self.qualified_name, name=self.name) diff --git a/pyatlan_v9/model/assets/_overlays/adls_container.py b/pyatlan_v9/model/assets/_overlays/adls_container.py new file mode 100644 index 000000000..db0798444 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/adls_container.py @@ -0,0 +1,47 @@ +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + adls_account_qualified_name: str, + connection_qualified_name: str | None = None, + ) -> "ADLSContainer": + validate_required_fields( + ["name", "adls_account_qualified_name"], + [name, adls_account_qualified_name], + ) + if connection_qualified_name: + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + else: + fields = adls_account_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + connection_qualified_name = ( + "/".join(fields[:3]) + if len(fields) >= 3 + else adls_account_qualified_name + ) + + adls_account_name = adls_account_qualified_name.rsplit("/", 1)[-1] + qualified_name = f"{adls_account_qualified_name}/{name}" + return cls( + name=name, + qualified_name=qualified_name, + adls_account_qualified_name=adls_account_qualified_name, + adls_account_name=adls_account_name, + connector_name=connector_name, + connection_qualified_name=connection_qualified_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "ADLSContainer": + """Create an ADLSContainer instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "ADLSContainer": + """Return only fields required for update operations.""" + return ADLSContainer.updater(qualified_name=self.qualified_name, name=self.name) diff --git a/pyatlan_v9/model/assets/_overlays/adls_object.py b/pyatlan_v9/model/assets/_overlays/adls_object.py new file mode 100644 index 000000000..4d2b319c2 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/adls_object.py @@ -0,0 +1,115 @@ +# IMPORT: from pyatlan.model.utils import construct_object_key +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + adls_container_name: str, + adls_container_qualified_name: str, + adls_account_qualified_name: str | None = None, + connection_qualified_name: str | None = None, + ) -> "ADLSObject": + validate_required_fields( + ["name", "adls_container_name", "adls_container_qualified_name"], + [name, adls_container_name, adls_container_qualified_name], + ) + if connection_qualified_name: + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + else: + fields = adls_container_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + connection_qualified_name = ( + "/".join(fields[:3]) + if len(fields) >= 3 + else adls_container_qualified_name + ) + + # Derive account qualified name from container qualified name + if not adls_account_qualified_name: + parts = adls_container_qualified_name.rsplit("/", 1) + adls_account_qualified_name = ( + parts[0] if len(parts) > 1 else adls_container_qualified_name + ) + + qualified_name = f"{adls_container_qualified_name}/{name}" + return cls( + name=name, + qualified_name=qualified_name, + adls_container_qualified_name=adls_container_qualified_name, + adls_container_name=adls_container_name, + connector_name=connector_name, + connection_qualified_name=connection_qualified_name, + adls_account_qualified_name=adls_account_qualified_name, + adls_account_name=adls_account_qualified_name.rsplit("/", 1)[-1], + ) + + @classmethod + @init_guid + def creator_with_prefix( + cls, + *, + name: str, + connection_qualified_name: str, + adls_container_name: str, + adls_container_qualified_name: str, + adls_account_qualified_name: str | None = None, + prefix: str = "", + ) -> "ADLSObject": + validate_required_fields( + [ + "name", + "connection_qualified_name", + "adls_container_name", + "adls_container_qualified_name", + ], + [ + name, + connection_qualified_name, + adls_container_name, + adls_container_qualified_name, + ], + ) + from pyatlan.model.utils import construct_object_key + + fields = connection_qualified_name.split("/") + if len(fields) != 3: + raise ValueError("Invalid connection_qualified_name") + if fields[0].replace(" ", "") == "" or fields[2].replace(" ", "") == "": + raise ValueError("Invalid connection_qualified_name") + if fields[1].lower() != "adls": + raise ValueError("Invalid connection_qualified_name") + connector_name = fields[1] + + if not adls_account_qualified_name: + parts = adls_container_qualified_name.rsplit("/", 1) + adls_account_qualified_name = ( + parts[0] if len(parts) > 1 else adls_container_qualified_name + ) + + object_key = construct_object_key(prefix, name) + qualified_name = f"{adls_container_qualified_name}/{object_key}" + return cls( + name=name, + qualified_name=qualified_name, + adls_object_key=object_key, + adls_container_qualified_name=adls_container_qualified_name, + adls_container_name=adls_container_name, + connector_name=connector_name, + connection_qualified_name=connection_qualified_name, + adls_account_qualified_name=adls_account_qualified_name, + adls_account_name=adls_account_qualified_name.rsplit("/", 1)[-1], + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "ADLSObject": + """Create an ADLSObject instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "ADLSObject": + """Return only fields required for update operations.""" + return ADLSObject.updater(qualified_name=self.qualified_name, name=self.name) diff --git a/pyatlan_v9/model/assets/_overlays/ai_application.py b/pyatlan_v9/model/assets/_overlays/ai_application.py new file mode 100644 index 000000000..fc74ba409 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/ai_application.py @@ -0,0 +1,40 @@ +# IMPORT: from pyatlan.model.enums import AtlanConnectorType +# IMPORT: from pyatlan.utils import to_camel_case +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + ai_application_version: str, + ai_application_development_stage: str, + owner_groups: Union[set[str], None] = None, + owner_users: Union[set[str], None] = None, + ) -> "AIApplication": + """Create a new AIApplication asset.""" + validate_required_fields( + ["name", "ai_application_version", "ai_application_development_stage"], + [name, ai_application_version, ai_application_development_stage], + ) + name_camel_case = to_camel_case(name) + return cls( + name=name, + qualified_name=f"default/ai/aiapplication/{name_camel_case}", + connector_name=AtlanConnectorType.AI.value, + ai_application_version=ai_application_version, + ai_application_development_stage=ai_application_development_stage, + owner_groups=owner_groups if owner_groups is not None else UNSET, + owner_users=owner_users if owner_users is not None else UNSET, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "AIApplication": + """Create an AIApplication instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "AIApplication": + """Return only fields required for update operations.""" + return AIApplication.updater(qualified_name=self.qualified_name, name=self.name) diff --git a/pyatlan_v9/model/assets/_overlays/ai_model.py b/pyatlan_v9/model/assets/_overlays/ai_model.py new file mode 100644 index 000000000..c3904f08e --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/ai_model.py @@ -0,0 +1,100 @@ +# STDLIB_IMPORT: from typing import Dict, List +# IMPORT: from pyatlan.model.enums import AIDatasetType, AtlanConnectorType +# IMPORT: from pyatlan.utils import to_camel_case +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields +# INTERNAL_IMPORT: from pyatlan.model.transform import get_type +# INTERNAL_IMPORT: from pyatlan.model.assets.process import Process + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + ai_model_status: str, + owner_groups: Union[set[str], None] = None, + owner_users: Union[set[str], None] = None, + ai_model_version: Union[str, None] = None, + ) -> "AIModel": + """Create a new AIModel asset.""" + validate_required_fields(["name", "ai_model_status"], [name, ai_model_status]) + name_camel_case = to_camel_case(name) + return cls( + name=name, + qualified_name=f"default/ai/aiapplication/{name_camel_case}", + connector_name=AtlanConnectorType.AI.value, + ai_model_status=ai_model_status, + ai_model_version=ai_model_version + if ai_model_version is not None + else UNSET, + owner_groups=owner_groups if owner_groups is not None else UNSET, + owner_users=owner_users if owner_users is not None else UNSET, + ) + + @classmethod + def processes_creator( + cls, + ai_model: "AIModel", + dataset_dict: Dict[AIDatasetType, list], + ) -> List[Process]: + """ + Create Process assets representing AI model lineage with dataset assets. + """ + if not ai_model.guid or not ai_model.name: + raise ValueError("AI model must have both guid and name attributes") + + process_list: List[Process] = [] + for dataset_type, assets in dataset_dict.items(): + for asset in assets: + asset_cls = get_type(getattr(asset, "type_name", "Asset")) + asset_guid = getattr(asset, "guid", None) + asset_name = getattr(asset, "name", None) + if not asset_guid or not asset_name: + continue + + if dataset_type == AIDatasetType.OUTPUT: + process_name = f"{ai_model.name} -> {asset_name}" + process_created = Process.creator( + name=process_name, + connection_qualified_name="default/ai/dataset", + inputs=[AIModel.ref_by_guid(guid=ai_model.guid)], + outputs=[asset_cls.ref_by_guid(guid=asset_guid)], + extra_hash_params={dataset_type.value}, + ) + else: + process_name = f"{asset_name} -> {ai_model.name}" + process_created = Process.creator( + name=process_name, + connection_qualified_name="default/ai/dataset", + inputs=[asset_cls.ref_by_guid(guid=asset_guid)], + outputs=[AIModel.ref_by_guid(guid=ai_model.guid)], + extra_hash_params={dataset_type.value}, + ) + + process_created.ai_dataset_type = dataset_type + process_list.append(process_created) + + return process_list + + @classmethod + def processes_batch_save( + cls, client: Any, process_list: List[Process] + ) -> List[Any]: + """ + Save Process assets in batches to reduce API payload size. + """ + batch_size = 20 + responses: List[Any] = [] + for i in range(0, len(process_list), batch_size): + responses.append(client.asset.save(process_list[i : i + batch_size])) + return responses + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "AIModel": + """Create an AIModel instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "AIModel": + """Return only fields required for update operations.""" + return AIModel.updater(qualified_name=self.qualified_name, name=self.name) diff --git a/pyatlan_v9/model/assets/_overlays/airflow_dag.py b/pyatlan_v9/model/assets/_overlays/airflow_dag.py new file mode 100644 index 000000000..b8199bc5e --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/airflow_dag.py @@ -0,0 +1,31 @@ +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + connection_qualified_name: str, + ) -> "AirflowDag": + validate_required_fields( + ["name", "connection_qualified_name"], + [name, connection_qualified_name], + ) + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + qualified_name = f"{connection_qualified_name}/{name}" + return cls( + name=name, + qualified_name=qualified_name, + connector_name=connector_name, + connection_qualified_name=connection_qualified_name, + ) + + @classmethod + def create(cls, **kwargs) -> "AirflowDag": + return cls.creator(**kwargs) + + @classmethod + def create_for_modification(cls, **kwargs) -> "AirflowDag": + return cls.updater(**kwargs) diff --git a/pyatlan_v9/model/assets/_overlays/airflow_task.py b/pyatlan_v9/model/assets/_overlays/airflow_task.py new file mode 100644 index 000000000..7bd24f59a --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/airflow_task.py @@ -0,0 +1,37 @@ +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + airflow_dag_qualified_name: str, + connection_qualified_name: str | None = None, + ) -> "AirflowTask": + validate_required_fields( + ["name", "airflow_dag_qualified_name"], + [name, airflow_dag_qualified_name], + ) + fields = airflow_dag_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + connection_qn = connection_qualified_name or ( + f"{fields[0]}/{fields[1]}/{fields[2]}" if len(fields) >= 3 else None + ) + qualified_name = f"{airflow_dag_qualified_name}/{name}" + return cls( + name=name, + qualified_name=qualified_name, + connector_name=connector_name, + connection_qualified_name=connection_qn, + airflow_dag_qualified_name=airflow_dag_qualified_name, + airflow_dag=RelatedAirflowDag(qualified_name=airflow_dag_qualified_name), + ) + + @classmethod + def create(cls, **kwargs) -> "AirflowTask": + return cls.creator(**kwargs) + + @classmethod + def create_for_modification(cls, **kwargs) -> "AirflowTask": + return cls.updater(**kwargs) diff --git a/pyatlan_v9/model/assets/_overlays/anaplan_app.py b/pyatlan_v9/model/assets/_overlays/anaplan_app.py new file mode 100644 index 000000000..e3b0b9a61 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/anaplan_app.py @@ -0,0 +1,27 @@ +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator(cls, *, name: str, connection_qualified_name: str) -> "AnaplanApp": + """Create a new AnaplanApp asset.""" + validate_required_fields( + ["name", "connection_qualified_name"], [name, connection_qualified_name] + ) + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + return cls( + name=name, + qualified_name=f"{connection_qualified_name}/{name}", + connection_qualified_name=connection_qualified_name, + connector_name=connector_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "AnaplanApp": + """Create an AnaplanApp instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "AnaplanApp": + """Return only fields required for update operations.""" + return AnaplanApp.updater(qualified_name=self.qualified_name, name=self.name) diff --git a/pyatlan_v9/model/assets/_overlays/anaplan_dimension.py b/pyatlan_v9/model/assets/_overlays/anaplan_dimension.py new file mode 100644 index 000000000..963c2548b --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/anaplan_dimension.py @@ -0,0 +1,51 @@ +# IMPORT: from pyatlan.model.enums import AtlanConnectorType +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + model_qualified_name: str, + connection_qualified_name: str | None = None, + ) -> "AnaplanDimension": + """Create a new AnaplanDimension asset.""" + validate_required_fields( + ["name", "model_qualified_name"], [name, model_qualified_name] + ) + fields = model_qualified_name.split("/") + connection_qn: Union[str, None, UnsetType] = UNSET + if connection_qualified_name is not None: + connector_name = str( + AtlanConnectorType.get_connector_name(connection_qualified_name) + ) + else: + connection_qn, connector_name = AtlanConnectorType.get_connector_name( + model_qualified_name, "model_qualified_name", 5 + ) + workspace_qualified_name = "/".join(fields[:4]) if len(fields) >= 4 else UNSET + workspace_name = fields[3] if len(fields) > 3 else UNSET + model_name = fields[4] if len(fields) > 4 else UNSET + return cls( + name=name, + qualified_name=f"{model_qualified_name}/{name}", + connection_qualified_name=connection_qualified_name or connection_qn, + connector_name=connector_name, + anaplan_workspace_qualified_name=workspace_qualified_name, + anaplan_workspace_name=workspace_name, + anaplan_model_qualified_name=model_qualified_name, + anaplan_model_name=model_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "AnaplanDimension": + """Create an AnaplanDimension instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "AnaplanDimension": + """Return only fields required for update operations.""" + return AnaplanDimension.updater( + qualified_name=self.qualified_name, name=self.name + ) diff --git a/pyatlan_v9/model/assets/_overlays/anaplan_line_item.py b/pyatlan_v9/model/assets/_overlays/anaplan_line_item.py new file mode 100644 index 000000000..ad0c8e39d --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/anaplan_line_item.py @@ -0,0 +1,55 @@ +# IMPORT: from pyatlan.model.enums import AtlanConnectorType +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + module_qualified_name: str, + connection_qualified_name: str | None = None, + ) -> "AnaplanLineItem": + """Create a new AnaplanLineItem asset.""" + validate_required_fields( + ["name", "module_qualified_name"], [name, module_qualified_name] + ) + fields = module_qualified_name.split("/") + connection_qn: Union[str, None, UnsetType] = UNSET + if connection_qualified_name is not None: + connector_name = str( + AtlanConnectorType.get_connector_name(connection_qualified_name) + ) + else: + connection_qn, connector_name = AtlanConnectorType.get_connector_name( + module_qualified_name, "module_qualified_name", 6 + ) + workspace_qualified_name = "/".join(fields[:4]) if len(fields) >= 4 else UNSET + workspace_name = fields[3] if len(fields) > 3 else UNSET + model_qualified_name = "/".join(fields[:5]) if len(fields) >= 5 else UNSET + model_name = fields[4] if len(fields) > 4 else UNSET + module_name = fields[5] if len(fields) > 5 else UNSET + return cls( + name=name, + qualified_name=f"{module_qualified_name}/{name}", + connection_qualified_name=connection_qualified_name or connection_qn, + connector_name=connector_name, + anaplan_workspace_qualified_name=workspace_qualified_name, + anaplan_workspace_name=workspace_name, + anaplan_model_qualified_name=model_qualified_name, + anaplan_model_name=model_name, + anaplan_module_qualified_name=module_qualified_name, + anaplan_module_name=module_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "AnaplanLineItem": + """Create an AnaplanLineItem instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "AnaplanLineItem": + """Return only fields required for update operations.""" + return AnaplanLineItem.updater( + qualified_name=self.qualified_name, name=self.name + ) diff --git a/pyatlan_v9/model/assets/_overlays/anaplan_list.py b/pyatlan_v9/model/assets/_overlays/anaplan_list.py new file mode 100644 index 000000000..e5683c4ba --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/anaplan_list.py @@ -0,0 +1,49 @@ +# IMPORT: from pyatlan.model.enums import AtlanConnectorType +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + model_qualified_name: str, + connection_qualified_name: str | None = None, + ) -> "AnaplanList": + """Create a new AnaplanList asset.""" + validate_required_fields( + ["name", "model_qualified_name"], [name, model_qualified_name] + ) + fields = model_qualified_name.split("/") + connection_qn: Union[str, None, UnsetType] = UNSET + if connection_qualified_name is not None: + connector_name = str( + AtlanConnectorType.get_connector_name(connection_qualified_name) + ) + else: + connection_qn, connector_name = AtlanConnectorType.get_connector_name( + model_qualified_name, "model_qualified_name", 5 + ) + workspace_qualified_name = "/".join(fields[:4]) if len(fields) >= 4 else UNSET + workspace_name = fields[3] if len(fields) > 3 else UNSET + model_name = fields[4] if len(fields) > 4 else UNSET + return cls( + name=name, + qualified_name=f"{model_qualified_name}/{name}", + connection_qualified_name=connection_qualified_name or connection_qn, + connector_name=connector_name, + anaplan_workspace_qualified_name=workspace_qualified_name, + anaplan_workspace_name=workspace_name, + anaplan_model_qualified_name=model_qualified_name, + anaplan_model_name=model_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "AnaplanList": + """Create an AnaplanList instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "AnaplanList": + """Return only fields required for update operations.""" + return AnaplanList.updater(qualified_name=self.qualified_name, name=self.name) diff --git a/pyatlan_v9/model/assets/_overlays/anaplan_model.py b/pyatlan_v9/model/assets/_overlays/anaplan_model.py new file mode 100644 index 000000000..e7769f592 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/anaplan_model.py @@ -0,0 +1,42 @@ +# IMPORT: from pyatlan.model.enums import AtlanConnectorType +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + workspace_qualified_name: str, + connection_qualified_name: str | None = None, + ) -> "AnaplanModel": + """Create a new AnaplanModel asset.""" + validate_required_fields( + ["name", "workspace_qualified_name"], [name, workspace_qualified_name] + ) + connection_qn: Union[str, None, UnsetType] = UNSET + if connection_qualified_name is not None: + connector_name = str( + AtlanConnectorType.get_connector_name(connection_qualified_name) + ) + else: + connection_qn, connector_name = AtlanConnectorType.get_connector_name( + workspace_qualified_name, "workspace_qualified_name", 4 + ) + return cls( + name=name, + qualified_name=f"{workspace_qualified_name}/{name}", + connection_qualified_name=connection_qualified_name or connection_qn, + connector_name=connector_name, + anaplan_workspace_qualified_name=workspace_qualified_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "AnaplanModel": + """Create an AnaplanModel instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "AnaplanModel": + """Return only fields required for update operations.""" + return AnaplanModel.updater(qualified_name=self.qualified_name, name=self.name) diff --git a/pyatlan_v9/model/assets/_overlays/anaplan_module.py b/pyatlan_v9/model/assets/_overlays/anaplan_module.py new file mode 100644 index 000000000..462868624 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/anaplan_module.py @@ -0,0 +1,42 @@ +# IMPORT: from pyatlan.model.enums import AtlanConnectorType +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + model_qualified_name: str, + connection_qualified_name: str | None = None, + ) -> "AnaplanModule": + """Create a new AnaplanModule asset.""" + validate_required_fields( + ["name", "model_qualified_name"], [name, model_qualified_name] + ) + connection_qn: Union[str, None, UnsetType] = UNSET + if connection_qualified_name is not None: + connector_name = str( + AtlanConnectorType.get_connector_name(connection_qualified_name) + ) + else: + connection_qn, connector_name = AtlanConnectorType.get_connector_name( + model_qualified_name, "model_qualified_name", 5 + ) + return cls( + name=name, + qualified_name=f"{model_qualified_name}/{name}", + connection_qualified_name=connection_qualified_name or connection_qn, + connector_name=connector_name, + anaplan_model_qualified_name=model_qualified_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "AnaplanModule": + """Create an AnaplanModule instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "AnaplanModule": + """Return only fields required for update operations.""" + return AnaplanModule.updater(qualified_name=self.qualified_name, name=self.name) diff --git a/pyatlan_v9/model/assets/_overlays/anaplan_page.py b/pyatlan_v9/model/assets/_overlays/anaplan_page.py new file mode 100644 index 000000000..ed0af44e1 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/anaplan_page.py @@ -0,0 +1,42 @@ +# IMPORT: from pyatlan.model.enums import AtlanConnectorType +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + app_qualified_name: str, + connection_qualified_name: str | None = None, + ) -> "AnaplanPage": + """Create a new AnaplanPage asset.""" + validate_required_fields( + ["name", "app_qualified_name"], [name, app_qualified_name] + ) + connection_qn: Union[str, None, UnsetType] = UNSET + if connection_qualified_name is not None: + connector_name = str( + AtlanConnectorType.get_connector_name(connection_qualified_name) + ) + else: + connection_qn, connector_name = AtlanConnectorType.get_connector_name( + app_qualified_name, "app_qualified_name", 4 + ) + return cls( + name=name, + qualified_name=f"{app_qualified_name}/{name}", + connection_qualified_name=connection_qualified_name or connection_qn, + connector_name=connector_name, + anaplan_app_qualified_name=app_qualified_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "AnaplanPage": + """Create an AnaplanPage instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "AnaplanPage": + """Return only fields required for update operations.""" + return AnaplanPage.updater(qualified_name=self.qualified_name, name=self.name) diff --git a/pyatlan_v9/model/assets/_overlays/anaplan_system_dimension.py b/pyatlan_v9/model/assets/_overlays/anaplan_system_dimension.py new file mode 100644 index 000000000..fbec7456c --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/anaplan_system_dimension.py @@ -0,0 +1,31 @@ +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, *, name: str, connection_qualified_name: str + ) -> "AnaplanSystemDimension": + """Create a new AnaplanSystemDimension asset.""" + validate_required_fields( + ["name", "connection_qualified_name"], [name, connection_qualified_name] + ) + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + return cls( + name=name, + qualified_name=f"{connection_qualified_name}/{name}", + connection_qualified_name=connection_qualified_name, + connector_name=connector_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "AnaplanSystemDimension": + """Create an AnaplanSystemDimension instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "AnaplanSystemDimension": + """Return only fields required for update operations.""" + return AnaplanSystemDimension.updater( + qualified_name=self.qualified_name, name=self.name + ) diff --git a/pyatlan_v9/model/assets/_overlays/anaplan_view.py b/pyatlan_v9/model/assets/_overlays/anaplan_view.py new file mode 100644 index 000000000..b390bda0e --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/anaplan_view.py @@ -0,0 +1,53 @@ +# IMPORT: from pyatlan.model.enums import AtlanConnectorType +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + module_qualified_name: str, + connection_qualified_name: str | None = None, + ) -> "AnaplanView": + """Create a new AnaplanView asset.""" + validate_required_fields( + ["name", "module_qualified_name"], [name, module_qualified_name] + ) + fields = module_qualified_name.split("/") + connection_qn: Union[str, None, UnsetType] = UNSET + if connection_qualified_name is not None: + connector_name = str( + AtlanConnectorType.get_connector_name(connection_qualified_name) + ) + else: + connection_qn, connector_name = AtlanConnectorType.get_connector_name( + module_qualified_name, "module_qualified_name", 6 + ) + workspace_qualified_name = "/".join(fields[:4]) if len(fields) >= 4 else UNSET + workspace_name = fields[3] if len(fields) > 3 else UNSET + model_qualified_name = "/".join(fields[:5]) if len(fields) >= 5 else UNSET + model_name = fields[4] if len(fields) > 4 else UNSET + module_name = fields[5] if len(fields) > 5 else UNSET + return cls( + name=name, + qualified_name=f"{module_qualified_name}/{name}", + connection_qualified_name=connection_qualified_name or connection_qn, + connector_name=connector_name, + anaplan_workspace_qualified_name=workspace_qualified_name, + anaplan_workspace_name=workspace_name, + anaplan_model_qualified_name=model_qualified_name, + anaplan_model_name=model_name, + anaplan_module_qualified_name=module_qualified_name, + anaplan_module_name=module_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "AnaplanView": + """Create an AnaplanView instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "AnaplanView": + """Return only fields required for update operations.""" + return AnaplanView.updater(qualified_name=self.qualified_name, name=self.name) diff --git a/pyatlan_v9/model/assets/_overlays/anaplan_workspace.py b/pyatlan_v9/model/assets/_overlays/anaplan_workspace.py new file mode 100644 index 000000000..a2ae49cec --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/anaplan_workspace.py @@ -0,0 +1,31 @@ +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, *, name: str, connection_qualified_name: str + ) -> "AnaplanWorkspace": + """Create a new AnaplanWorkspace asset.""" + validate_required_fields( + ["name", "connection_qualified_name"], [name, connection_qualified_name] + ) + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + return cls( + name=name, + qualified_name=f"{connection_qualified_name}/{name}", + connection_qualified_name=connection_qualified_name, + connector_name=connector_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "AnaplanWorkspace": + """Create an AnaplanWorkspace instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "AnaplanWorkspace": + """Return only fields required for update operations.""" + return AnaplanWorkspace.updater( + qualified_name=self.qualified_name, name=self.name + ) diff --git a/pyatlan_v9/model/assets/_overlays/api_field.py b/pyatlan_v9/model/assets/_overlays/api_field.py new file mode 100644 index 000000000..5cb9d0771 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/api_field.py @@ -0,0 +1,124 @@ +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + parent_api_object_qualified_name: Union[str, None] = None, + parent_api_query_qualified_name: Union[str, None] = None, + connection_qualified_name: Union[str, None] = None, + api_field_type: Union[str, None] = None, + api_field_type_secondary: Union[str, None] = None, + is_api_object_reference: bool = False, + reference_api_object_qualified_name: Union[str, None] = None, + api_query_param_type: Union[str, None] = None, + ) -> "APIField": + """Create a new APIField asset.""" + validate_required_fields(["name"], [name]) + if parent_api_object_qualified_name is None or ( + isinstance(parent_api_object_qualified_name, str) + and not parent_api_object_qualified_name.strip() + ): + if parent_api_query_qualified_name is None or ( + isinstance(parent_api_query_qualified_name, str) + and not parent_api_query_qualified_name.strip() + ): + raise ValueError( + "Either parent_api_object_qualified_name or parent_api_query_qualified_name requires a valid value" + ) + elif ( + isinstance(parent_api_query_qualified_name, str) + and parent_api_query_qualified_name.strip() + ): + raise ValueError( + "Both parent_api_object_qualified_name and parent_api_query_qualified_name cannot be valid" + ) + + if is_api_object_reference: + if not reference_api_object_qualified_name or ( + isinstance(reference_api_object_qualified_name, str) + and not reference_api_object_qualified_name.strip() + ): + raise ValueError( + "Set valid qualified name for reference_api_object_qualified_name" + ) + elif ( + reference_api_object_qualified_name + and isinstance(reference_api_object_qualified_name, str) + and reference_api_object_qualified_name.strip() + ): + raise ValueError( + "Set is_api_object_reference to true to set reference_api_object_qualified_name" + ) + + if connection_qualified_name: + connection_qn = connection_qualified_name + elif parent_api_object_qualified_name: + parts = parent_api_object_qualified_name.split("/") + connection_qn = ( + "/".join(parts[:3]) + if len(parts) >= 3 + else parent_api_object_qualified_name + ) + else: + parts = (parent_api_query_qualified_name or "").split("/") + connection_qn = ( + "/".join(parts[:3]) + if len(parts) >= 3 + else parent_api_query_qualified_name + ) + + conn_parts = (connection_qn or "").split("/") + connector_name = conn_parts[1] if len(conn_parts) > 1 else None + + if parent_api_object_qualified_name: + return cls( + name=name, + qualified_name=f"{parent_api_object_qualified_name}/{name}", + connection_qualified_name=connection_qn, + connector_name=connector_name, + api_field_type=api_field_type, + api_field_type_secondary=api_field_type_secondary, + api_is_object_reference=is_api_object_reference, + api_object_qualified_name=( + reference_api_object_qualified_name + if is_api_object_reference + else None + ), + api_object=RelatedAPIObject( + qualified_name=parent_api_object_qualified_name, + unique_attributes={ + "qualifiedName": parent_api_object_qualified_name + }, + ), + api_query_param_type=api_query_param_type, + ) + return cls( + name=name, + qualified_name=f"{parent_api_query_qualified_name}/{name}", + connection_qualified_name=connection_qn, + connector_name=connector_name, + api_field_type=api_field_type, + api_field_type_secondary=api_field_type_secondary, + api_is_object_reference=is_api_object_reference, + api_object_qualified_name=( + reference_api_object_qualified_name if is_api_object_reference else None + ), + api_query=RelatedAPIQuery( + qualified_name=parent_api_query_qualified_name, + unique_attributes={"qualifiedName": parent_api_query_qualified_name}, + ), + api_query_param_type=api_query_param_type, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "APIField": + """Create an APIField instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "APIField": + """Return only fields required for update operations.""" + return APIField.updater(qualified_name=self.qualified_name, name=self.name) diff --git a/pyatlan_v9/model/assets/_overlays/api_object.py b/pyatlan_v9/model/assets/_overlays/api_object.py new file mode 100644 index 000000000..addd59911 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/api_object.py @@ -0,0 +1,34 @@ +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + connection_qualified_name: str, + api_field_count: Union[int, None] = None, + ) -> "APIObject": + """Create a new APIObject asset.""" + validate_required_fields( + ["name", "connection_qualified_name"], [name, connection_qualified_name] + ) + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + return cls( + name=name, + qualified_name=f"{connection_qualified_name}/{name}", + connection_qualified_name=connection_qualified_name, + connector_name=connector_name, + api_field_count=api_field_count, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "APIObject": + """Create an APIObject instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "APIObject": + """Return only fields required for update operations.""" + return APIObject.updater(qualified_name=self.qualified_name, name=self.name) diff --git a/pyatlan_v9/model/assets/_overlays/api_path.py b/pyatlan_v9/model/assets/_overlays/api_path.py new file mode 100644 index 000000000..c2a9cbd51 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/api_path.py @@ -0,0 +1,55 @@ +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @property + def api_path_raw_u_r_i(self) -> Union[str, None, UnsetType]: + return self.api_path_raw_uri + + @api_path_raw_u_r_i.setter + def api_path_raw_u_r_i(self, value: Union[str, None, UnsetType]) -> None: + self.api_path_raw_uri = value + + @classmethod + @init_guid + def creator( + cls, + *, + path_raw_uri: str, + spec_qualified_name: str, + connection_qualified_name: Union[str, None] = None, + ) -> "APIPath": + """Create a new APIPath asset.""" + validate_required_fields( + ["path_raw_uri", "spec_qualified_name"], [path_raw_uri, spec_qualified_name] + ) + if connection_qualified_name: + connection_qn = connection_qualified_name + else: + spec_parts = spec_qualified_name.split("/") + connection_qn = ( + "/".join(spec_parts[:3]) + if len(spec_parts) >= 3 + else spec_qualified_name + ) + conn_parts = connection_qn.split("/") + connector_name = conn_parts[1] if len(conn_parts) > 1 else None + return cls( + name=path_raw_uri, + qualified_name=f"{spec_qualified_name}{path_raw_uri}", + api_path_raw_uri=path_raw_uri, + api_spec_qualified_name=spec_qualified_name, + connection_qualified_name=connection_qn, + connector_name=connector_name, + api_spec=RelatedAPISpec( + unique_attributes={"qualifiedName": spec_qualified_name} + ), + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "APIPath": + """Create an APIPath instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "APIPath": + """Return only fields required for update operations.""" + return APIPath.updater(qualified_name=self.qualified_name, name=self.name) diff --git a/pyatlan_v9/model/assets/_overlays/api_query.py b/pyatlan_v9/model/assets/_overlays/api_query.py new file mode 100644 index 000000000..90ee421e9 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/api_query.py @@ -0,0 +1,61 @@ +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + connection_qualified_name: str, + api_input_field_count: Union[int, None] = None, + api_query_output_type: Union[str, None] = None, + api_query_output_type_secondary: Union[str, None] = None, + is_object_reference: bool = False, + reference_api_object_qualified_name: Union[str, None] = None, + ) -> "APIQuery": + """Create a new APIQuery asset.""" + validate_required_fields( + ["name", "connection_qualified_name"], [name, connection_qualified_name] + ) + if is_object_reference: + if not reference_api_object_qualified_name or ( + isinstance(reference_api_object_qualified_name, str) + and not reference_api_object_qualified_name.strip() + ): + raise ValueError( + "Set valid qualified name for reference_api_object_qualified_name when is_object_reference is true" + ) + elif ( + reference_api_object_qualified_name + and isinstance(reference_api_object_qualified_name, str) + and reference_api_object_qualified_name.strip() + ): + raise ValueError( + "Set is_object_reference to true to set reference_api_object_qualified_name" + ) + + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + return cls( + name=name, + qualified_name=f"{connection_qualified_name}/{name}", + connection_qualified_name=connection_qualified_name, + connector_name=connector_name, + api_input_field_count=api_input_field_count, + api_query_output_type=api_query_output_type, + api_query_output_type_secondary=api_query_output_type_secondary, + api_is_object_reference=is_object_reference, + api_object_qualified_name=( + reference_api_object_qualified_name if is_object_reference else None + ), + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "APIQuery": + """Create an APIQuery instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "APIQuery": + """Return only fields required for update operations.""" + return APIQuery.updater(qualified_name=self.qualified_name, name=self.name) diff --git a/pyatlan_v9/model/assets/_overlays/api_spec.py b/pyatlan_v9/model/assets/_overlays/api_spec.py new file mode 100644 index 000000000..02cb0042c --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/api_spec.py @@ -0,0 +1,27 @@ +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator(cls, *, name: str, connection_qualified_name: str) -> "APISpec": + """Create a new APISpec asset.""" + validate_required_fields( + ["name", "connection_qualified_name"], [name, connection_qualified_name] + ) + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + return cls( + name=name, + qualified_name=f"{connection_qualified_name}/{name}", + connection_qualified_name=connection_qualified_name, + connector_name=connector_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "APISpec": + """Create an APISpec instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "APISpec": + """Return only fields required for update operations.""" + return APISpec.updater(qualified_name=self.qualified_name, name=self.name) diff --git a/pyatlan_v9/model/assets/_overlays/application.py b/pyatlan_v9/model/assets/_overlays/application.py new file mode 100644 index 000000000..8729dfad8 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/application.py @@ -0,0 +1,35 @@ +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + connection_qualified_name: str, + ) -> "Application": + """Create a new Application asset.""" + validate_required_fields( + ["name", "connection_qualified_name"], [name, connection_qualified_name] + ) + connector_name = ( + connection_qualified_name.split("/")[1] + if len(connection_qualified_name.split("/")) > 1 + else "" + ) + return cls( + name=name, + qualified_name=f"{connection_qualified_name}/{name}", + connection_qualified_name=connection_qualified_name, + connector_name=connector_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "Application": + """Create an Application instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "Application": + """Return only fields required for update operations.""" + return Application.updater(qualified_name=self.qualified_name, name=self.name) diff --git a/pyatlan_v9/model/assets/_overlays/application_field.py b/pyatlan_v9/model/assets/_overlays/application_field.py new file mode 100644 index 000000000..d636baa45 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/application_field.py @@ -0,0 +1,50 @@ +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + application_qualified_name: str, + connection_qualified_name: str | None = None, + ) -> "ApplicationField": + """Create a new ApplicationField asset.""" + validate_required_fields( + ["name", "application_qualified_name"], [name, application_qualified_name] + ) + if connection_qualified_name: + connector_name = ( + connection_qualified_name.split("/")[1] + if len(connection_qualified_name.split("/")) > 1 + else "" + ) + else: + fields = application_qualified_name.split("/") + if len(fields) < 3: + raise ValueError("application_qualified_name is invalid") + connection_qualified_name = "/".join(fields[:3]) + connector_name = fields[1] + return cls( + name=name, + qualified_name=f"{application_qualified_name}/{name}", + connection_qualified_name=connection_qualified_name, + connector_name=connector_name, + application_parent_qualified_name=application_qualified_name, + application_parent=RelatedApplication( + unique_attributes={"qualifiedName": application_qualified_name} + ), + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "ApplicationField": + """Create an ApplicationField instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "ApplicationField": + """Return only fields required for update operations.""" + return ApplicationField.updater( + qualified_name=self.qualified_name, + name=self.name, + ) diff --git a/pyatlan_v9/model/assets/_overlays/asset.py b/pyatlan_v9/model/assets/_overlays/asset.py new file mode 100644 index 000000000..8b6d6fca5 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/asset.py @@ -0,0 +1,184 @@ +# INTERNAL_IMPORT: from pyatlan.model.assets.related_entity import SaveSemantic +# INTERNAL_IMPORT: from pyatlan.model.core import Announcement +# INTERNAL_IMPORT: from pyatlan.model.enums import AnnouncementType + + @classmethod + def ref_by_guid( + cls, guid: str, semantic: "SaveSemantic | str" = SaveSemantic.REPLACE + ) -> "Asset": + """ + Create a minimal reference to this asset type by its GUID. + + Args: + guid: Globally unique identifier of the asset + semantic: Save semantic (REPLACE, APPEND, REMOVE) + + Returns: + Asset reference instance + """ + if isinstance(semantic, str): + semantic = SaveSemantic(semantic) + return cls(guid=guid, type_name=cls.__name__, semantic=semantic) + + @classmethod + def ref_by_qualified_name( + cls, qualified_name: str, semantic: "SaveSemantic | str" = SaveSemantic.REPLACE + ) -> "Asset": + """ + Create a minimal reference to this asset type by its qualifiedName. + + Args: + qualified_name: Unique fully-qualified name of the asset + semantic: Save semantic (REPLACE, APPEND, REMOVE) + + Returns: + Asset reference instance + """ + if isinstance(semantic, str): + semantic = SaveSemantic(semantic) + return cls( + qualified_name=qualified_name, type_name=cls.__name__, semantic=semantic + ) + + def set_announcement(self, announcement) -> None: + """ + Set an announcement on this asset. + + Args: + announcement: Announcement object with type, title, and message + """ + self.announcement_type = announcement.announcement_type.value + self.announcement_title = announcement.announcement_title + self.announcement_message = announcement.announcement_message + + def remove_announcement(self) -> "Asset": + """ + Remove the announcement from this asset. + + Returns: + Self for fluent chaining + """ + self.announcement_type = None + self.announcement_title = None + self.announcement_message = None + return self + + def get_announcment(self): + """Return an Announcement object for this asset, or None if no announcement is set.""" + from pyatlan_v9.model.core import Announcement + from pyatlan_v9.model.enums import AnnouncementType + + ann_type = self.announcement_type + ann_title = self.announcement_title + if ann_type and ann_title and ann_type is not UNSET and ann_title is not UNSET: + return Announcement( + announcement_type=AnnouncementType[str(ann_type).upper()], + announcement_title=ann_title, + announcement_message=self.announcement_message + if self.announcement_message is not UNSET + else None, + ) + return None + + def remove_certificate(self) -> "Asset": + """ + Remove the certificate from this asset. + + Returns: + Self for fluent chaining + """ + self.certificate_status = None + self.certificate_status_message = None + return self + + def remove_description(self) -> "Asset": + """ + Remove the description from this asset. + + Returns: + Self for fluent chaining + """ + self.description = None + return self + + def remove_user_description(self) -> "Asset": + """ + Remove the user description from this asset. + + Returns: + Self for fluent chaining + """ + self.user_description = None + return self + + def remove_owners(self) -> "Asset": + """ + Remove the owners from this asset. + + Returns: + Self for fluent chaining + """ + self.owner_groups = None + self.owner_users = None + return self + + def flush_custom_metadata(self, client=None) -> None: + """ + Flush (clear) all custom metadata on this asset. + + Args: + client: AtlanClient instance (for compatibility with legacy API) + """ + self.business_attributes = {} + + @classmethod + def updater(cls, qualified_name: str = "", name: str = "") -> "Asset": + """ + Create an asset for modification (update operations). + + Args: + qualified_name: Unique name of the asset + name: Name of the asset + + Returns: + Asset instance configured for update operations + + Raises: + ValueError: If required parameters are missing + """ + if not qualified_name: + raise ValueError("qualified_name is required") + if not name: + raise ValueError("name is required") + + return cls(qualified_name=qualified_name, name=name) + + @classmethod + def create_for_modification( + cls, qualified_name: str = "", name: str = "" + ) -> "Asset": + """ + Create an asset for modification (deprecated - use updater instead). + + Args: + qualified_name: Unique name of the asset + name: Name of the asset + + Returns: + Asset instance configured for update operations + """ + return cls.updater(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "Asset": + """ + Trim this asset to only the required fields for an update. + + Returns: + Asset with only qualified_name and name set + """ + return self.__class__.updater( + qualified_name=self.qualified_name + if self.qualified_name is not UNSET + else "", + name=self.name if self.name is not UNSET else "", + ) diff --git a/pyatlan_v9/model/assets/_overlays/atlas_glossary.py b/pyatlan_v9/model/assets/_overlays/atlas_glossary.py new file mode 100644 index 000000000..3f3be3f69 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/atlas_glossary.py @@ -0,0 +1,40 @@ +# STDLIB_IMPORT: import uuid +# INTERNAL_IMPORT: from pyatlan.utils import init_guid + + @classmethod + @init_guid + def creator(cls, *, name: str) -> "AtlasGlossary": + """ + Create a new AtlasGlossary asset. + + Args: + name: Name of the glossary + + Returns: + AtlasGlossary instance ready to be created + + Raises: + ValueError: If name is not provided + """ + if not name: + raise ValueError("name is required") + + # Generate a unique qualified name using a simple ID generator + import uuid + + qualified_name = str(uuid.uuid4().hex[:16]) + + return AtlasGlossary(name=name, qualified_name=qualified_name) + + @classmethod + def create(cls, *, name: str) -> "AtlasGlossary": + """ + Create a new AtlasGlossary asset (deprecated - use creator instead). + + Args: + name: Name of the glossary + + Returns: + AtlasGlossary instance ready to be created + """ + return cls.creator(name=name) diff --git a/pyatlan_v9/model/assets/_overlays/atlas_glossary_category.py b/pyatlan_v9/model/assets/_overlays/atlas_glossary_category.py new file mode 100644 index 000000000..5a1fd28a1 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/atlas_glossary_category.py @@ -0,0 +1,170 @@ +# STDLIB_IMPORT: import uuid +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + def can_be_archived(cls) -> bool: + return False + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + anchor: "Asset | None" = None, + glossary_qualified_name: str | None = None, + glossary_guid: str | None = None, + parent_category: "AtlasGlossaryCategory | None" = None, + ) -> "AtlasGlossaryCategory": + """ + Create a new AtlasGlossaryCategory asset. + + Args: + name: Simple name of the category + anchor: Glossary object in which this category is contained (mutually exclusive with glossary_qualified_name and glossary_guid) + glossary_qualified_name: Qualified name of the glossary (mutually exclusive with anchor and glossary_guid) + glossary_guid: GUID of the glossary (mutually exclusive with anchor and glossary_qualified_name) + parent_category: Optional parent category for this category + + Returns: + New AtlasGlossaryCategory instance + + Raises: + ValueError: If required parameters are missing or if multiple glossary identifiers are provided + """ + validate_required_fields(["name"], [name]) + + provided_params = [ + p for p in [anchor, glossary_qualified_name, glossary_guid] if p is not None + ] + if len(provided_params) == 0: + raise ValueError( + "One of the following parameters are required: anchor, glossary_qualified_name, glossary_guid" + ) + if len(provided_params) > 1: + param_names = [] + if anchor is not None: + param_names.append("anchor") + if glossary_qualified_name is not None: + param_names.append("glossary_qualified_name") + if glossary_guid is not None: + param_names.append("glossary_guid") + raise ValueError( + f"Only one of the following parameters are allowed: {', '.join(param_names)}" + ) + + import uuid + + qualified_name = f"{name}@{uuid.uuid4()}" + + from msgspec import UNSET as MSGSPEC_UNSET + + if anchor is not None: + if hasattr(anchor, "trim_to_reference") and callable( + anchor.trim_to_reference + ): + anchor_ref = anchor.trim_to_reference() + else: + anchor_ref = RelatedAtlasGlossary( + guid=anchor.guid + if hasattr(anchor, "guid") and anchor.guid is not MSGSPEC_UNSET + else None, + qualified_name=anchor.qualified_name + if hasattr(anchor, "qualified_name") + and anchor.qualified_name is not MSGSPEC_UNSET + else None, + ) + elif glossary_qualified_name is not None: + anchor_ref = RelatedAtlasGlossary(qualified_name=glossary_qualified_name) + else: # glossary_guid is not None + anchor_ref = RelatedAtlasGlossary(guid=glossary_guid) + + parent_ref = None + if parent_category is not None: + if hasattr(parent_category, "trim_to_reference") and callable( + parent_category.trim_to_reference + ): + parent_ref = parent_category.trim_to_reference() + else: + parent_ref = RelatedAtlasGlossaryCategory( + guid=parent_category.guid + if hasattr(parent_category, "guid") + and parent_category.guid is not MSGSPEC_UNSET + else None, + qualified_name=parent_category.qualified_name + if hasattr(parent_category, "qualified_name") + and parent_category.qualified_name is not MSGSPEC_UNSET + else None, + ) + + kwargs: dict = dict( + name=name, + qualified_name=qualified_name, + anchor=anchor_ref, + ) + if parent_ref is not None: + kwargs["parent_category"] = parent_ref + return cls(**kwargs) + + @classmethod + def updater( + cls, *, qualified_name: str, name: str, glossary_guid: str + ) -> "AtlasGlossaryCategory": + """ + Create an AtlasGlossaryCategory instance for updating an existing category. + + Args: + qualified_name: Unique name of the category to update + name: Simple name of the category + glossary_guid: GUID of the glossary containing this category + + Returns: + AtlasGlossaryCategory instance configured for updates + + Raises: + ValueError: If required parameters are missing + """ + validate_required_fields( + ["qualified_name", "name", "glossary_guid"], + [qualified_name, name, glossary_guid], + ) + return cls( + qualified_name=qualified_name, + name=name, + anchor=RelatedAtlasGlossary(guid=glossary_guid), + ) + + def trim_to_required(self) -> "AtlasGlossaryCategory": + """ + Return an AtlasGlossaryCategory with only required fields for reference. + + Returns: + AtlasGlossaryCategory instance with only required fields set + + Raises: + ValueError: If anchor or anchor.guid is not available + """ + if self.anchor is None or self.anchor is UNSET: + raise ValueError("anchor.guid must be available") + if ( + not hasattr(self.anchor, "guid") + or self.anchor.guid is None + or self.anchor.guid is UNSET + ): + raise ValueError("anchor.guid must be available") + + return AtlasGlossaryCategory( + qualified_name=self.qualified_name, + name=self.name, + anchor=RelatedAtlasGlossary(guid=self.anchor.guid), + ) + + @classmethod + def create(cls, **kwargs) -> "AtlasGlossaryCategory": + """Backward compatibility alias for creator().""" + return cls.creator(**kwargs) + + @classmethod + def create_for_modification(cls, **kwargs) -> "AtlasGlossaryCategory": + """Backward compatibility alias for updater().""" + return cls.updater(**kwargs) diff --git a/pyatlan_v9/model/assets/_overlays/atlas_glossary_term.py b/pyatlan_v9/model/assets/_overlays/atlas_glossary_term.py new file mode 100644 index 000000000..4e0548844 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/atlas_glossary_term.py @@ -0,0 +1,151 @@ +# STDLIB_IMPORT: import uuid +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + anchor: "Asset | None" = None, + glossary_qualified_name: str | None = None, + glossary_guid: str | None = None, + categories: list["RelatedAtlasGlossaryCategory"] | None = None, + ) -> "AtlasGlossaryTerm": + """ + Create a new AtlasGlossaryTerm asset. + + Args: + name: Simple name of the term + anchor: Glossary object in which this term is contained (mutually exclusive with glossary_qualified_name and glossary_guid) + glossary_qualified_name: Qualified name of the glossary (mutually exclusive with anchor and glossary_guid) + glossary_guid: GUID of the glossary (mutually exclusive with anchor and glossary_qualified_name) + categories: Optional list of categories to which this term belongs + + Returns: + New AtlasGlossaryTerm instance + + Raises: + ValueError: If required parameters are missing or if multiple glossary identifiers are provided + """ + validate_required_fields(["name"], [name]) + + # Validate exactly one glossary identifier is provided + provided_params = [ + p for p in [anchor, glossary_qualified_name, glossary_guid] if p is not None + ] + if len(provided_params) == 0: + raise ValueError( + "One of the following parameters are required: anchor, glossary_qualified_name, glossary_guid" + ) + if len(provided_params) > 1: + param_names = [] + if anchor is not None: + param_names.append("anchor") + if glossary_qualified_name is not None: + param_names.append("glossary_qualified_name") + if glossary_guid is not None: + param_names.append("glossary_guid") + raise ValueError( + f"Only one of the following parameters are allowed: {', '.join(param_names)}" + ) + + # Generate qualified name + import uuid + + qualified_name = f"{name}@{uuid.uuid4()}" + + # Create anchor reference based on which parameter was provided + if anchor is not None: + # Use provided anchor object + from msgspec import UNSET as MSGSPEC_UNSET + + if hasattr(anchor, "trim_to_reference") and callable( + anchor.trim_to_reference + ): + anchor_ref = anchor.trim_to_reference() + else: + # Fallback: create RelatedAtlasGlossary from anchor attributes + anchor_ref = RelatedAtlasGlossary( + guid=anchor.guid + if hasattr(anchor, "guid") and anchor.guid is not MSGSPEC_UNSET + else None, + qualified_name=anchor.qualified_name + if hasattr(anchor, "qualified_name") + and anchor.qualified_name is not MSGSPEC_UNSET + else None, + ) + elif glossary_qualified_name is not None: + anchor_ref = RelatedAtlasGlossary(qualified_name=glossary_qualified_name) + else: # glossary_guid is not None + anchor_ref = RelatedAtlasGlossary(guid=glossary_guid) + + return cls( + name=name, + qualified_name=qualified_name, + anchor=anchor_ref, + categories=categories, + ) + + @classmethod + def updater( + cls, *, qualified_name: str, name: str, glossary_guid: str + ) -> "AtlasGlossaryTerm": + """ + Create an AtlasGlossaryTerm instance for updating an existing term. + + Args: + qualified_name: Unique name of the term to update + name: Simple name of the term + glossary_guid: GUID of the glossary containing this term + + Returns: + AtlasGlossaryTerm instance configured for updates + + Raises: + ValueError: If required parameters are missing + """ + validate_required_fields( + ["qualified_name", "name", "glossary_guid"], + [qualified_name, name, glossary_guid], + ) + return cls( + qualified_name=qualified_name, + name=name, + anchor=RelatedAtlasGlossary(guid=glossary_guid), + ) + + def trim_to_required(self) -> "AtlasGlossaryTerm": + """ + Return an AtlasGlossaryTerm with only required fields for reference. + + Returns: + AtlasGlossaryTerm instance with only required fields set + + Raises: + ValueError: If anchor or anchor.guid is not available + """ + if self.anchor is None or self.anchor is UNSET: + raise ValueError("anchor.guid must be available") + if ( + not hasattr(self.anchor, "guid") + or self.anchor.guid is None + or self.anchor.guid is UNSET + ): + raise ValueError("anchor.guid must be available") + + return AtlasGlossaryTerm( + qualified_name=self.qualified_name, + name=self.name, + anchor=RelatedAtlasGlossary(guid=self.anchor.guid), + ) + + @classmethod + def create(cls, **kwargs) -> "AtlasGlossaryTerm": + """Backward compatibility alias for creator().""" + return cls.creator(**kwargs) + + @classmethod + def create_for_modification(cls, **kwargs) -> "AtlasGlossaryTerm": + """Backward compatibility alias for updater().""" + return cls.updater(**kwargs) diff --git a/pyatlan_v9/model/assets/_overlays/auth_policy.py b/pyatlan_v9/model/assets/_overlays/auth_policy.py new file mode 100644 index 000000000..73e0fb6d1 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/auth_policy.py @@ -0,0 +1,7 @@ +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def _create(cls, *, name: str) -> "AuthPolicy": + validate_required_fields(["name"], [name]) + return cls(qualified_name=name, name=name, display_name="") diff --git a/pyatlan_v9/model/assets/_overlays/azure_event_consumer_group.py b/pyatlan_v9/model/assets/_overlays/azure_event_consumer_group.py new file mode 100644 index 000000000..017230d44 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/azure_event_consumer_group.py @@ -0,0 +1,37 @@ +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, *, name: str, event_hub_qualified_names: list[str] + ) -> "AzureEventHubConsumerGroup": + """Create a new AzureEventHubConsumerGroup asset.""" + validate_required_fields( + ["name", "event_hub_qualified_names"], [name, event_hub_qualified_names] + ) + first_event_hub_qn = event_hub_qualified_names[0] + fields = first_event_hub_qn.split("/") + connector_name = fields[1] if len(fields) > 1 else None + connection_qualified_name = ( + "/".join(fields[:3]) if len(fields) >= 3 else first_event_hub_qn + ) + first_event_hub_name = fields[4] if len(fields) > 4 else fields[-1] + return cls( + name=name, + connector_name=connector_name, + connection_qualified_name=connection_qualified_name, + kafka_topic_qualified_names=set(event_hub_qualified_names), + qualified_name=f"{connection_qualified_name}/consumer-group/{first_event_hub_name}/{name}", + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "AzureEventHubConsumerGroup": + """Create an AzureEventHubConsumerGroup instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "AzureEventHubConsumerGroup": + """Return only fields required for update operations.""" + return AzureEventHubConsumerGroup.updater( + qualified_name=self.qualified_name, name=self.name + ) diff --git a/pyatlan_v9/model/assets/_overlays/azure_event_hub.py b/pyatlan_v9/model/assets/_overlays/azure_event_hub.py new file mode 100644 index 000000000..3248dec13 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/azure_event_hub.py @@ -0,0 +1,27 @@ +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator(cls, *, name: str, connection_qualified_name: str) -> "AzureEventHub": + """Create a new AzureEventHub asset.""" + validate_required_fields( + ["name", "connection_qualified_name"], [name, connection_qualified_name] + ) + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + return cls( + name=name, + qualified_name=f"{connection_qualified_name}/topic/{name}", + connection_qualified_name=connection_qualified_name, + connector_name=connector_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "AzureEventHub": + """Create an AzureEventHub instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "AzureEventHub": + """Return only fields required for update operations.""" + return AzureEventHub.updater(qualified_name=self.qualified_name, name=self.name) diff --git a/pyatlan_v9/model/assets/_overlays/badge.py b/pyatlan_v9/model/assets/_overlays/badge.py new file mode 100644 index 000000000..68b7daea8 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/badge.py @@ -0,0 +1,71 @@ +# IMPORT: from pyatlan.model.enums import EntityStatus +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + client: Any, + name: str, + cm_name: str, + cm_attribute: str, + badge_conditions: list[BadgeCondition], + ) -> "Badge": + """Create a new Badge asset.""" + validate_required_fields( + ["client", "name", "cm_name", "cm_attribute", "badge_conditions"], + [client, name, cm_name, cm_attribute, badge_conditions], + ) + cm_id = client.custom_metadata_cache.get_id_for_name(cm_name) + cm_attr_id = client.custom_metadata_cache.get_attr_id_for_name( + set_name=cm_name, attr_name=cm_attribute + ) + from pyatlan.model.enums import EntityStatus + + return cls( + name=name, + qualified_name=f"badges/global/{cm_id}.{cm_attr_id}", + badge_metadata_attribute=f"{cm_id}.{cm_attr_id}", + badge_conditions=badge_conditions, + status=EntityStatus.ACTIVE, + ) + + @classmethod + async def creator_async( + cls, + *, + client: Any, + name: str, + cm_name: str, + cm_attribute: str, + badge_conditions: list[BadgeCondition], + ) -> "Badge": + """Create a new Badge asset (async version).""" + validate_required_fields( + ["client", "name", "cm_name", "cm_attribute", "badge_conditions"], + [client, name, cm_name, cm_attribute, badge_conditions], + ) + cm_id = await client.custom_metadata_cache.get_id_for_name(cm_name) + cm_attr_id = await client.custom_metadata_cache.get_attr_id_for_name( + set_name=cm_name, attr_name=cm_attribute + ) + from pyatlan.model.enums import EntityStatus + + return cls( + name=name, + qualified_name=f"badges/global/{cm_id}.{cm_attr_id}", + badge_metadata_attribute=f"{cm_id}.{cm_attr_id}", + badge_conditions=badge_conditions, + status=EntityStatus.ACTIVE, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "Badge": + """Create a Badge instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "Badge": + """Return only fields required for update operations.""" + return Badge.updater(qualified_name=self.qualified_name, name=self.name) diff --git a/pyatlan_v9/model/assets/_overlays/badge_condition.py b/pyatlan_v9/model/assets/_overlays/badge_condition.py new file mode 100644 index 000000000..f50db4b8c --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/badge_condition.py @@ -0,0 +1,29 @@ +# IMPORT: from pyatlan.model.enums import BadgeComparisonOperator, BadgeConditionColor +# INTERNAL_IMPORT: from pyatlan.utils import validate_required_fields + + @classmethod + def creator( + cls, + *, + badge_condition_operator: BadgeComparisonOperator, + badge_condition_value: str, + badge_condition_colorhex: Union[BadgeConditionColor, str], + ) -> "BadgeCondition": + """Create a badge condition.""" + validate_required_fields( + [ + "badge_condition_operator", + "badge_condition_value", + "badge_condition_colorhex", + ], + [badge_condition_operator, badge_condition_value, badge_condition_colorhex], + ) + return cls( + badge_condition_operator=badge_condition_operator.value, + badge_condition_value=badge_condition_value, + badge_condition_colorhex=( + badge_condition_colorhex.value + if isinstance(badge_condition_colorhex, BadgeConditionColor) + else badge_condition_colorhex + ), + ) diff --git a/pyatlan_v9/model/assets/_overlays/collection.py b/pyatlan_v9/model/assets/_overlays/collection.py new file mode 100644 index 000000000..059873bd3 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/collection.py @@ -0,0 +1,26 @@ +# STDLIB_IMPORT: from typing import TYPE_CHECKING +# STDLIB_IMPORT: from uuid import uuid4 +# IMPORT: from pyatlan.errors import AtlanError +# IMPORT: from pyatlan.errors import ErrorCode +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator(cls, *, client: "AtlanClient", name: str) -> "Collection": + validate_required_fields(["client", "name"], [client, name]) + return cls( + name=name, + qualified_name=cls._generate_qualified_name(client), + ) + + @classmethod + def _generate_qualified_name(cls, client: "AtlanClient") -> str: + from pyatlan.errors import AtlanError + + try: + username = client.user.get_current().username + return f"default/collection/{username}/{uuid4()}" + except AtlanError as e: + raise ErrorCode.UNABLE_TO_GENERATE_QN.exception_with_parameters( + cls.__name__, e + ) from e diff --git a/pyatlan_v9/model/assets/_overlays/column.py b/pyatlan_v9/model/assets/_overlays/column.py new file mode 100644 index 000000000..12dfdb800 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/column.py @@ -0,0 +1,200 @@ +# STDLIB_IMPORT: from warnings import warn +# IMPORT: from pyatlan.model.enums import AtlanConnectorType +# IMPORT: from pyatlan.utils import validate_required_fields +# INTERNAL_IMPORT: from pyatlan.utils import init_guid + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + parent_qualified_name: str, + parent_type: type, + order: int, + parent_name: str | None = None, + database_name: str | None = None, + database_qualified_name: str | None = None, + schema_name: str | None = None, + schema_qualified_name: str | None = None, + table_name: str | None = None, + table_qualified_name: str | None = None, + connection_qualified_name: str | None = None, + ) -> "Column": + """ + Create a new Column asset. + + Args: + name: Name of the column + parent_qualified_name: Unique name of the parent (table/view/etc) + parent_type: Type of parent (Table, View, MaterialisedView, etc) + order: Order of the column in the parent + parent_name: Simple name of the parent + database_name: Simple name of the database + database_qualified_name: Unique name of the database + schema_name: Simple name of the schema + schema_qualified_name: Unique name of the schema + table_name: (deprecated) Simple name of the table + table_qualified_name: (deprecated) Unique name of the table + connection_qualified_name: Unique name of the connection + + Returns: + Column instance ready to be created + + Raises: + ValueError: If required parameters are missing or invalid + """ + if table_name: + warn( + ("`table_name` is deprecated, please use `parent_name` instead"), + DeprecationWarning, + stacklevel=2, + ) + if table_qualified_name: + warn( + ( + "`table_qualified_name` is deprecated, please use `parent_qualified_name` instead" + ), + DeprecationWarning, + stacklevel=2, + ) + + validate_required_fields( + ["name", "parent_qualified_name", "parent_type", "order"], + [name, parent_qualified_name, parent_type, order], + ) + + # Use AtlanConnectorType.get_connector_name for validation (exact parity with pydantic) + connection_qn: str | None = None + if connection_qualified_name: + connector_name = str( + AtlanConnectorType.get_connector_name(connection_qualified_name) + ) + else: + result = AtlanConnectorType.get_connector_name( + parent_qualified_name, "parent_qualified_name", 6 + ) + connection_qn = str(result[0]) + connector_name = str(result[1]) + if order < 0: + raise ValueError("Order must be be a positive integer") + + # Get the type name from the parent_type class + parent_type_name = getattr(parent_type, "__name__", None) + + # Validate parent type + valid_types = [ + "Table", + "View", + "MaterialisedView", + "TablePartition", + "SnowflakeDynamicTable", + "Column", + ] + if parent_type_name not in valid_types: + raise ValueError( + "parent_type must be either Table, SnowflakeDynamicTable, View, MaterializeView or TablePartition" + ) + + if parent_type_name == "Column": + raise ValueError( + "parent_type must be either Table, SnowflakeDynamicTable, View, MaterializeView or TablePartition" + ) + + # Parse parent_qualified_name to derive fields + fields = parent_qualified_name.split("/") + + connection_qualified_name = connection_qualified_name or connection_qn + database_name = database_name or fields[3] + schema_name = schema_name or fields[4] + parent_name = parent_name or fields[5] + database_qualified_name = ( + database_qualified_name or f"{connection_qualified_name}/{database_name}" + ) + schema_qualified_name = ( + schema_qualified_name or f"{database_qualified_name}/{schema_name}" + ) + + database_qualified_name = ( + database_qualified_name + or f"{fields[0]}/{fields[1]}/{fields[2]}/{database_name}" + ) + schema_qualified_name = ( + schema_qualified_name or f"{database_qualified_name}/{schema_name}" + ) + + connection_qualified_name = ( + connection_qualified_name or f"{fields[0]}/{fields[1]}/{fields[2]}" + ) + + qualified_name = f"{parent_qualified_name}/{name}" + + # Build the column + col = cls( + name=name, + qualified_name=qualified_name, + connector_name=connector_name, + connection_qualified_name=connection_qualified_name, + schema_name=schema_name, + schema_qualified_name=schema_qualified_name, + database_name=database_name, + database_qualified_name=database_qualified_name, + order=order, + ) + + # Set parent-specific fields + if parent_type_name == "Table": + col.table_qualified_name = parent_qualified_name + col.table = RelatedTable( + qualified_name=parent_qualified_name, type_name="Table" + ) + col.table_name = parent_name + elif parent_type_name == "View": + col.view_qualified_name = parent_qualified_name + col.view = RelatedView( + qualified_name=parent_qualified_name, type_name="View" + ) + col.view_name = parent_name + elif parent_type_name == "MaterialisedView": + col.view_qualified_name = parent_qualified_name + col.materialised_view = RelatedMaterialisedView( + qualified_name=parent_qualified_name, type_name="MaterialisedView" + ) + col.view_name = parent_name + elif parent_type_name == "TablePartition": + col.table_qualified_name = parent_qualified_name + col.table_partition = RelatedTablePartition( + qualified_name=parent_qualified_name, type_name="TablePartition" + ) + col.table_name = parent_name + elif parent_type_name == "SnowflakeDynamicTable": + col.table_qualified_name = parent_qualified_name + col.snowflake_dynamic_table = RelatedSnowflakeDynamicTable( + qualified_name=parent_qualified_name, + type_name="SnowflakeDynamicTable", + ) + col.table_name = parent_name + + return col + + @classmethod + def updater(cls, qualified_name: str = "", name: str = "") -> "Column": + """ + Create a Column instance for modification. + + Args: + qualified_name: Unique name of the column + name: Name of the column + + Returns: + Column instance for modification + + Raises: + ValueError: If required parameters are missing + """ + if not qualified_name: + raise ValueError("qualified_name is required") + if not name: + raise ValueError("name is required") + + return cls(qualified_name=qualified_name, name=name) diff --git a/pyatlan_v9/model/assets/_overlays/column_process.py b/pyatlan_v9/model/assets/_overlays/column_process.py new file mode 100644 index 000000000..c94906f6e --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/column_process.py @@ -0,0 +1,137 @@ +# STDLIB_IMPORT: import hashlib +# STDLIB_IMPORT: from io import StringIO +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @staticmethod + def _extract_guid(relationship: Any) -> Union[str, None]: + """Extract guid from a relationship-like object.""" + if relationship is None: + return None + guid = getattr(relationship, "guid", UNSET) + if guid is UNSET or not guid: + return None + return guid + + @staticmethod + def _to_related_catalog(value: Any) -> RelatedCatalog: + """Convert any relationship-like value to a RelatedCatalog reference.""" + if isinstance(value, RelatedCatalog): + return value + guid = getattr(value, "guid", UNSET) + type_name = getattr(value, "type_name", UNSET) + if guid is not UNSET and guid: + kwargs: dict[str, Any] = {"guid": guid} + if type_name is not UNSET and type_name: + kwargs["type_name"] = type_name + return RelatedCatalog(**kwargs) + qualified_name = getattr(value, "qualified_name", UNSET) + if qualified_name is not UNSET and qualified_name: + kwargs = {"unique_attributes": {"qualifiedName": qualified_name}} + if type_name is not UNSET and type_name: + kwargs["type_name"] = type_name + return RelatedCatalog(**kwargs) + return RelatedCatalog() + + @staticmethod + def _to_related_process(value: Any) -> RelatedProcess: + """Convert any relationship-like value to a RelatedProcess reference.""" + if isinstance(value, RelatedProcess): + return value + guid = getattr(value, "guid", UNSET) + type_name = getattr(value, "type_name", UNSET) + if guid is not UNSET and guid: + kwargs: dict[str, Any] = {"guid": guid} + if type_name is not UNSET and type_name: + kwargs["type_name"] = type_name + return RelatedProcess(**kwargs) + qualified_name = getattr(value, "qualified_name", UNSET) + if qualified_name is not UNSET and qualified_name: + kwargs = {"unique_attributes": {"qualifiedName": qualified_name}} + if type_name is not UNSET and type_name: + kwargs["type_name"] = type_name + return RelatedProcess(**kwargs) + return RelatedProcess() + + @staticmethod + def generate_qualified_name( + *, + name: str, + connection_qualified_name: str, + inputs: list[Any], + outputs: list[Any], + parent: Any, + process_id: Union[str, None] = None, + ) -> str: + """Generate column process qualified name using explicit process_id or deterministic hash.""" + validate_required_fields( + ["name", "connection_qualified_name", "inputs", "outputs", "parent"], + [name, connection_qualified_name, inputs, outputs, parent], + ) + if process_id and process_id.strip(): + return f"{connection_qualified_name}/{process_id}" + buffer = StringIO() + buffer.write(name) + buffer.write(connection_qualified_name) + parent_guid = ColumnProcess._extract_guid(parent) + if parent_guid: + buffer.write(parent_guid) + for relationship in inputs: + guid = ColumnProcess._extract_guid(relationship) + if guid: + buffer.write(guid) + for relationship in outputs: + guid = ColumnProcess._extract_guid(relationship) + if guid: + buffer.write(guid) + hash_seed = buffer.getvalue() + buffer.close() + # deepcode ignore InsecureHash/test: this is not used for generating security keys + return ( + f"{connection_qualified_name}/{hashlib.md5(hash_seed.encode()).hexdigest()}" # noqa: S324 + ) + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + connection_qualified_name: str, + inputs: list[Any], + outputs: list[Any], + parent: Any, + process_id: Union[str, None] = None, + ) -> "ColumnProcess": + """Create a new ColumnProcess asset.""" + qualified_name = cls.generate_qualified_name( + name=name, + connection_qualified_name=connection_qualified_name, + inputs=inputs, + outputs=outputs, + parent=parent, + process_id=process_id, + ) + connector_name = ( + connection_qualified_name.split("/")[1] + if len(connection_qualified_name.split("/")) > 1 + else "" + ) + return cls( + name=name, + qualified_name=qualified_name, + connector_name=connector_name, + connection_qualified_name=connection_qualified_name, + inputs=[cls._to_related_catalog(item) for item in inputs], + outputs=[cls._to_related_catalog(item) for item in outputs], + process=cls._to_related_process(parent), + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "ColumnProcess": + """Create a ColumnProcess instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "ColumnProcess": + """Return only fields required for update operations.""" + return ColumnProcess.updater(qualified_name=self.qualified_name, name=self.name) diff --git a/pyatlan_v9/model/assets/_overlays/connection.py b/pyatlan_v9/model/assets/_overlays/connection.py new file mode 100644 index 000000000..95f815543 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/connection.py @@ -0,0 +1,153 @@ +# STDLIB_IMPORT: from typing import TYPE_CHECKING, List, Optional +# IMPORT: from pyatlan.model.enums import AtlanConnectorType +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + client: AtlanClient, + name: str, + connector_type: AtlanConnectorType, + admin_users: Optional[List[str]] = None, + admin_groups: Optional[List[str]] = None, + admin_roles: Optional[List[str]] = None, + host: Optional[str] = None, + port: Optional[int] = None, + ) -> "Connection": + """ + Create a new Connection asset. + + Args: + client: AtlanClient for cache validation + name: Simple name of the connection + connector_type: Type of connector for the connection + admin_users: List of admin usernames + admin_groups: List of admin group names + admin_roles: List of admin role GUIDs + host: Optional hostname for the connection + port: Optional port number for the connection + + Returns: + New Connection instance with all fields populated + + Raises: + ValueError: If required parameters are missing or invalid + """ + validate_required_fields( + ["client", "name", "connector_type"], [client, name, connector_type] + ) + if not admin_users and not admin_groups and not admin_roles: + raise ValueError( + "One of admin_user, admin_groups or admin_roles is required" + ) + client.user_cache.validate_names(names=admin_users or []) + client.role_cache.validate_idstrs(idstrs=admin_roles or []) + client.group_cache.validate_aliases(aliases=admin_groups or []) + + kwargs: dict = dict( + name=name, + qualified_name=connector_type.to_qualified_name(), + connector_name=connector_type.value, + category=connector_type.category.value, + admin_users=set() if admin_users is None else set(admin_users), + admin_groups=set() if admin_groups is None else set(admin_groups), + admin_roles=set() if admin_roles is None else set(admin_roles), + ) + if host is not None: + kwargs["host"] = host + if port is not None: + kwargs["port"] = port + return cls(**kwargs) + + @classmethod + @init_guid + async def creator_async( + cls, + *, + client: Any, + name: str, + connector_type: AtlanConnectorType, + admin_users: Optional[List[str]] = None, + admin_groups: Optional[List[str]] = None, + admin_roles: Optional[List[str]] = None, + host: Optional[str] = None, + port: Optional[int] = None, + ) -> "Connection": + """ + Async version of creator() for creating a new Connection asset. + + :param client: async Atlan client for cache validation + :param name: name for the connection + :param connector_type: type of connector + :param admin_users: list of admin usernames + :param admin_groups: list of admin group names + :param admin_roles: list of admin role GUIDs + :param host: optional hostname + :param port: optional port number + :returns: the new connection object + :raises ValueError: if required parameters are missing or invalid + """ + validate_required_fields( + ["client", "name", "connector_type"], [client, name, connector_type] + ) + if not admin_users and not admin_groups and not admin_roles: + raise ValueError( + "One of admin_user, admin_groups or admin_roles is required" + ) + await client.user_cache.validate_names(names=admin_users or []) + await client.role_cache.validate_idstrs(idstrs=admin_roles or []) + await client.group_cache.validate_aliases(aliases=admin_groups or []) + + kwargs: dict = dict( + name=name, + qualified_name=connector_type.to_qualified_name(), + connector_name=connector_type.value, + category=connector_type.category.value, + admin_users=set() if admin_users is None else set(admin_users), + admin_groups=set() if admin_groups is None else set(admin_groups), + admin_roles=set() if admin_roles is None else set(admin_roles), + ) + if host is not None: + kwargs["host"] = host + if port is not None: + kwargs["port"] = port + return cls(**kwargs) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "Connection": + """ + Create a Connection instance for updating an existing asset. + + Args: + qualified_name: Unique name of the connection to update + name: Simple name of the connection + + Returns: + Connection instance configured for updates + + Raises: + ValueError: If required parameters are missing + """ + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "Connection": + """ + Return a Connection with only required fields for reference. + + Returns: + Connection instance with only qualified_name and name set + """ + return Connection(qualified_name=self.qualified_name, name=self.name) + + @classmethod + def create(cls, **kwargs) -> "Connection": + """Backward compatibility alias for creator().""" + return cls.creator(**kwargs) + + @classmethod + def create_for_modification(cls, **kwargs) -> "Connection": + """Backward compatibility alias for updater().""" + return cls.updater(**kwargs) diff --git a/pyatlan_v9/model/assets/_overlays/custom_entity.py b/pyatlan_v9/model/assets/_overlays/custom_entity.py new file mode 100644 index 000000000..b8516b80b --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/custom_entity.py @@ -0,0 +1,31 @@ +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + connection_qualified_name: str, + ) -> "CustomEntity": + validate_required_fields( + ["name", "connection_qualified_name"], + [name, connection_qualified_name], + ) + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + qualified_name = f"{connection_qualified_name}/{name}" + return cls( + name=name, + qualified_name=qualified_name, + connector_name=connector_name, + connection_qualified_name=connection_qualified_name, + ) + + @classmethod + def create(cls, **kwargs) -> "CustomEntity": + return cls.creator(**kwargs) + + @classmethod + def create_for_modification(cls, **kwargs) -> "CustomEntity": + return cls.updater(**kwargs) diff --git a/pyatlan_v9/model/assets/_overlays/data_contract.py b/pyatlan_v9/model/assets/_overlays/data_contract.py new file mode 100644 index 000000000..220df1175 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/data_contract.py @@ -0,0 +1,36 @@ +# STDLIB_IMPORT: import re +# IMPORT: from pyatlan.errors import ErrorCode +# INTERNAL_IMPORT: from pyatlan.model.contract import DataContractSpec +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + asset_qualified_name: str, + contract_json: Union[str, None] = None, + contract_spec: Union[DataContractSpec, str, None] = None, + ) -> "DataContract": + """Create a new DataContract asset.""" + attrs = DataContract.Attributes.creator( + asset_qualified_name=asset_qualified_name, + contract_json=contract_json, + contract_spec=contract_spec, + ) + return cls( + name=attrs.name, + qualified_name=attrs.qualified_name, + data_contract_json=attrs.data_contract_json, + data_contract_spec=attrs.data_contract_spec, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "DataContract": + """Create a DataContract instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "DataContract": + """Return only required fields for update operations.""" + return DataContract.updater(qualified_name=self.qualified_name, name=self.name) diff --git a/pyatlan_v9/model/assets/_overlays/data_domain.py b/pyatlan_v9/model/assets/_overlays/data_domain.py new file mode 100644 index 000000000..d3026cbaa --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/data_domain.py @@ -0,0 +1,63 @@ +# STDLIB_IMPORT: import re +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + def _get_super_domain_qualified_name( + cls, domain_qualified_name: str + ) -> Union[str, None]: + """Extract the top-most ancestor domain qualified name.""" + domain_qn_prefix = re.compile(r"(default/domain/[a-zA-Z0-9-]+/super)/.*") + if domain_qualified_name: + match = domain_qn_prefix.match(domain_qualified_name) + if match and match.group(1): + return match.group(1) + if domain_qualified_name.startswith("default/domain/"): + return domain_qualified_name + return None + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + parent_domain_qualified_name: Union[str, None] = None, + ) -> "DataDomain": + """Create a new DataDomain asset.""" + validate_required_fields(["name"], [name]) + parent_domain = ( + RelatedDataDomain( + unique_attributes={"qualifiedName": parent_domain_qualified_name} + ) + if parent_domain_qualified_name + else None + ) + super_domain_qualified_name = ( + cls._get_super_domain_qualified_name(parent_domain_qualified_name) + if parent_domain_qualified_name + else None + ) + return cls( + name=name, + qualified_name=name, + parent_domain=parent_domain, + parent_domain_qualified_name=parent_domain_qualified_name, + super_domain_qualified_name=super_domain_qualified_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "DataDomain": + """Create a DataDomain instance for update operations.""" + validate_required_fields(["name", "qualified_name"], [name, qualified_name]) + fields = qualified_name.split("/") + if len(fields) < 3: + raise ValueError(f"Invalid data domain qualified_name: {qualified_name}") + return cls( + qualified_name=qualified_name, + name=name, + parent_domain_qualified_name=None, + ) + + def trim_to_required(self) -> "DataDomain": + """Return only the required fields for updates.""" + return DataDomain.updater(qualified_name=self.qualified_name, name=self.name) diff --git a/pyatlan_v9/model/assets/_overlays/data_product.py b/pyatlan_v9/model/assets/_overlays/data_product.py new file mode 100644 index 000000000..990098a5d --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/data_product.py @@ -0,0 +1,86 @@ +# STDLIB_IMPORT: import re +# IMPORT: from pyatlan.errors import ErrorCode +# IMPORT: from pyatlan.model.enums import DataProductStatus +# INTERNAL_IMPORT: from pyatlan.model.data_mesh import DataProductsAssetsDSL +# INTERNAL_IMPORT: from pyatlan.model.search import IndexSearchRequest +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + def _get_super_domain_qualified_name( + cls, domain_qualified_name: str + ) -> Union[str, None]: + """Extract the top-most ancestor domain qualified name.""" + domain_qn_prefix = re.compile(r"(default/domain/[a-zA-Z0-9-]+/super)/.*") + if domain_qualified_name: + match = domain_qn_prefix.match(domain_qualified_name) + if match and match.group(1): + return match.group(1) + if domain_qualified_name.startswith("default/domain/"): + return domain_qualified_name + return None + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + domain_qualified_name: str, + asset_selection: IndexSearchRequest, + ) -> "DataProduct": + """Create a new DataProduct asset.""" + validate_required_fields( + ["name", "domain_qualified_name", "asset_selection"], + [name, domain_qualified_name, asset_selection], + ) + assets_playbook_filter = '{"condition":"AND","isGroupLocked":false,"rules":[]}' + return cls( + name=name, + data_product_assets_dsl=DataProductsAssetsDSL.get_asset_selection( + asset_selection + ), + data_domain=RelatedDataDomain( + unique_attributes={"qualifiedName": domain_qualified_name} + ), + qualified_name=f"{domain_qualified_name}/product/{name}", + data_product_assets_playbook_filter=assets_playbook_filter, + parent_domain_qualified_name=domain_qualified_name, + super_domain_qualified_name=cls._get_super_domain_qualified_name( + domain_qualified_name + ), + daap_status=DataProductStatus.ACTIVE, + ) + + @classmethod + @init_guid + def updater( + cls, + *, + qualified_name: str, + name: str, + asset_selection: Union[IndexSearchRequest, None] = None, + ) -> "DataProduct": + """Create a DataProduct instance for update operations.""" + validate_required_fields(["name", "qualified_name"], [name, qualified_name]) + fields = qualified_name.split("/") + if len(fields) < 5: + raise ValueError(f"Invalid data product qualified_name: {qualified_name}") + product = cls(qualified_name=qualified_name, name=name) + if asset_selection: + product.data_product_assets_dsl = DataProductsAssetsDSL.get_asset_selection( + asset_selection + ) + return product + + def trim_to_required(self) -> "DataProduct": + """Return only the required fields for updates.""" + return DataProduct.updater(qualified_name=self.qualified_name, name=self.name) + + def get_assets(self, client: "AtlanClient"): + """Retrieve assets linked to this data product.""" + dp_dsl = self.data_product_assets_dsl + if not dp_dsl: + raise ErrorCode.MISSING_DATA_PRODUCT_ASSET_DSL.exception_with_parameters() + query_data = msgspec.json.decode(dp_dsl).get("query", {}) + request = msgspec.convert(query_data, IndexSearchRequest, strict=False) + return client.asset.search(request) diff --git a/pyatlan_v9/model/assets/_overlays/data_quality_rule.py b/pyatlan_v9/model/assets/_overlays/data_quality_rule.py new file mode 100644 index 000000000..2ca75ba28 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/data_quality_rule.py @@ -0,0 +1,672 @@ +# STDLIB_IMPORT: import json +# STDLIB_IMPORT: import time +# STDLIB_IMPORT: import uuid +# IMPORT: from pyatlan.errors import ErrorCode +# IMPORT: from pyatlan.model.enums import DataQualityDimension, DataQualityRuleAlertPriority, DataQualityRuleCustomSQLReturnType, DataQualityRuleStatus, DataQualityRuleTemplateType, DataQualityRuleThresholdCompareOperator, DataQualityRuleThresholdUnit, DataQualitySourceSyncStatus +# INTERNAL_IMPORT: from pyatlan.model.structs import DataQualityRuleConfigArguments, DataQualityRuleThresholdObject +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def custom_sql_creator( + cls, + *, + client: "AtlanClient", + rule_name: str, + asset: Asset, + custom_sql: str, + threshold_compare_operator: DataQualityRuleThresholdCompareOperator, + threshold_value: int, + alert_priority: DataQualityRuleAlertPriority, + dimension: DataQualityDimension, + custom_sql_return_type: Optional[DataQualityRuleCustomSQLReturnType] = None, + description: Optional[str] = None, + ) -> "DataQualityRule": + validate_required_fields( + [ + "client", + "rule_name", + "asset", + "threshold_compare_operator", + "threshold_value", + "alert_priority", + "dimension", + "custom_sql", + ], + [ + client, + rule_name, + asset, + threshold_compare_operator, + threshold_value, + alert_priority, + dimension, + custom_sql, + ], + ) + return cls._build_rule( + client=client, + rule_name=rule_name, + rule_type=DataQualityRuleTemplateType.CUSTOM_SQL, + asset=asset, + threshold_compare_operator=threshold_compare_operator, + threshold_value=threshold_value, + alert_priority=alert_priority, + dimension=dimension, + custom_sql=custom_sql, + custom_sql_return_type=custom_sql_return_type, + description=description, + column=None, + threshold_unit=None, + ) + + @classmethod + @init_guid + def table_level_rule_creator( + cls, + *, + client: "AtlanClient", + rule_type: DataQualityRuleTemplateType, + asset: Asset, + threshold_value: int, + alert_priority: DataQualityRuleAlertPriority, + threshold_compare_operator: Optional[ + DataQualityRuleThresholdCompareOperator + ] = None, + threshold_unit: Optional[DataQualityRuleThresholdUnit] = None, + rule_conditions: Optional[str] = None, + row_scope_filtering_enabled: Optional[bool] = False, + ) -> "DataQualityRule": + validate_required_fields( + ["client", "rule_type", "asset", "threshold_value", "alert_priority"], + [client, rule_type, asset, threshold_value, alert_priority], + ) + template_config = client.dq_template_config_cache.get_template_config( + rule_type.value + ) + asset_for_validation, target_table_asset = ( + cls._fetch_assets_for_row_scope_validation( + client, asset, rule_conditions, row_scope_filtering_enabled or False + ) + ) + validated_threshold_operator = cls._validate_template_features( + rule_type, + rule_conditions, + row_scope_filtering_enabled, + template_config, + threshold_compare_operator, + asset_for_validation, + target_table_asset, + ) + final_threshold_compare_operator = ( + validated_threshold_operator + or threshold_compare_operator + or DataQualityRuleThresholdCompareOperator.LESS_THAN_EQUAL + ) + return cls._build_rule( + client=client, + rule_type=rule_type, + asset=asset, + threshold_compare_operator=final_threshold_compare_operator, + threshold_value=threshold_value, + alert_priority=alert_priority, + rule_name=None, + column=None, + threshold_unit=threshold_unit, + dimension=None, + custom_sql=None, + description=None, + rule_conditions=rule_conditions, + row_scope_filtering_enabled=row_scope_filtering_enabled, + ) + + @classmethod + @init_guid + def column_level_rule_creator( + cls, + *, + client: "AtlanClient", + rule_type: DataQualityRuleTemplateType, + asset: Asset, + column: Asset, + threshold_value: int, + alert_priority: DataQualityRuleAlertPriority, + threshold_compare_operator: Optional[ + DataQualityRuleThresholdCompareOperator + ] = None, + threshold_unit: Optional[DataQualityRuleThresholdUnit] = None, + rule_conditions: Optional[str] = None, + row_scope_filtering_enabled: Optional[bool] = False, + ) -> "DataQualityRule": + validate_required_fields( + [ + "client", + "rule_type", + "asset", + "column", + "threshold_value", + "alert_priority", + ], + [client, rule_type, asset, column, threshold_value, alert_priority], + ) + template_config = client.dq_template_config_cache.get_template_config( + rule_type.value + ) + asset_for_validation, target_table_asset = ( + cls._fetch_assets_for_row_scope_validation( + client, asset, rule_conditions, row_scope_filtering_enabled or False + ) + ) + validated_threshold_operator = cls._validate_template_features( + rule_type, + rule_conditions, + row_scope_filtering_enabled, + template_config, + threshold_compare_operator, + asset_for_validation, + target_table_asset, + ) + final_threshold_compare_operator = ( + validated_threshold_operator + or threshold_compare_operator + or DataQualityRuleThresholdCompareOperator.LESS_THAN_EQUAL + ) + return cls._build_rule( + client=client, + rule_type=rule_type, + asset=asset, + column=column, + threshold_compare_operator=final_threshold_compare_operator, + threshold_value=threshold_value, + alert_priority=alert_priority, + threshold_unit=threshold_unit, + rule_name=None, + dimension=None, + custom_sql=None, + description=None, + rule_conditions=rule_conditions, + row_scope_filtering_enabled=row_scope_filtering_enabled, + ) + + @classmethod + @init_guid + def updater( + cls, + client: "AtlanClient", + qualified_name: str, + threshold_compare_operator: Optional[ + DataQualityRuleThresholdCompareOperator + ] = None, + threshold_value: Optional[int] = None, + alert_priority: Optional[DataQualityRuleAlertPriority] = None, + threshold_unit: Optional[DataQualityRuleThresholdUnit] = None, + dimension: Optional[DataQualityDimension] = None, + custom_sql: Optional[str] = None, + custom_sql_return_type: Optional[DataQualityRuleCustomSQLReturnType] = None, + rule_name: Optional[str] = None, + description: Optional[str] = None, + rule_conditions: Optional[str] = None, + row_scope_filtering_enabled: Optional[bool] = False, + ) -> "DataQualityRule": + from pyatlan_v9.model.fluent_search import FluentSearch + + validate_required_fields( + ["client", "qualified_name"], + [client, qualified_name], + ) + request = ( + FluentSearch() + .where(DataQualityRule.QUALIFIED_NAME.eq(qualified_name)) + .include_on_results(DataQualityRule.NAME) + .include_on_results(DataQualityRule.DQ_RULE_TEMPLATE_NAME) + .include_on_results(DataQualityRule.DQ_RULE_TEMPLATE) + .include_on_results(DataQualityRule.DQ_RULE_BASE_DATASET) + .include_on_results(DataQualityRule.DQ_RULE_BASE_COLUMN) + .include_on_results(DataQualityRule.DQ_RULE_ALERT_PRIORITY) + .include_on_results(DataQualityRule.DISPLAY_NAME) + .include_on_results(DataQualityRule.DQ_RULE_CUSTOM_SQL) + .include_on_results(DataQualityRule.DQ_RULE_CUSTOM_SQL_RETURN_TYPE) + .include_on_results(DataQualityRule.USER_DESCRIPTION) + .include_on_results(DataQualityRule.DQ_RULE_DIMENSION) + .include_on_results(DataQualityRule.DQ_RULE_CONFIG_ARGUMENTS) + .include_on_results(DataQualityRule.DQ_RULE_ROW_SCOPE_FILTERING_ENABLED) + .include_on_results(DataQualityRule.DQ_RULE_SOURCE_SYNC_STATUS) + .include_on_results(DataQualityRule.DQ_RULE_STATUS) + ).to_request() + + results = client.asset.search(request) + + if results.count != 1: + raise ValueError( + f"Expected exactly 1 asset for qualified_name: {qualified_name}, " + f"but found: {results.count}" + ) + search_result = results.current_page()[0] + + retrieved_custom_sql = getattr(search_result, "dq_rule_custom_sql", None) + retrieved_custom_sql_return_type = getattr( + search_result, "dq_rule_custom_sql_return_type", None + ) + retrieved_rule_name = getattr(search_result, "display_name", None) + retrieved_dimension = getattr(search_result, "dq_rule_dimension", None) + retrieved_column = getattr(search_result, "dq_rule_base_column", None) + retrieved_alert_priority = getattr( + search_result, "dq_rule_alert_priority", None + ) + retrieved_row_scope_filtering_enabled = getattr( + search_result, "dq_rule_row_scope_filtering_enabled", None + ) + retrieved_description = getattr(search_result, "user_description", None) + retrieved_asset = getattr(search_result, "dq_rule_base_dataset", None) + retrieved_template_rule_name = getattr( + search_result, "dq_rule_template_name", None + ) + retrieved_template = getattr(search_result, "dq_rule_template", None) + + config_args = getattr(search_result, "dq_rule_config_arguments", None) + threshold_obj = ( + getattr(config_args, "dq_rule_threshold_object", None) + if config_args + else None + ) + retrieved_threshold_compare_operator = ( + getattr(threshold_obj, "dq_rule_threshold_compare_operator", None) + if threshold_obj + else None + ) + retrieved_threshold_value = ( + getattr(threshold_obj, "dq_rule_threshold_value", None) + if threshold_obj + else None + ) + retrieved_threshold_unit = ( + getattr(threshold_obj, "dq_rule_threshold_unit", None) + if threshold_obj + else None + ) + + template_config = None + if retrieved_template_rule_name: + template_config = client.dq_template_config_cache.get_template_config( + retrieved_template_rule_name + ) + + if rule_conditions: + final_rule_conditions = rule_conditions + elif config_args is not None: + final_rule_conditions = getattr( + config_args, "dq_rule_config_rule_conditions", None + ) + else: + final_rule_conditions = None + + final_row_scope_filtering_enabled = ( + row_scope_filtering_enabled or retrieved_row_scope_filtering_enabled + ) + if retrieved_asset: + retrieved_asset, target_table_asset = ( + cls._fetch_assets_for_row_scope_validation( + client, + retrieved_asset, + final_rule_conditions, + final_row_scope_filtering_enabled, + ) + ) + else: + target_table_asset = None + + validated_threshold_operator = None + if retrieved_template_rule_name and template_config: + try: + retrieved_rule_type = DataQualityRuleTemplateType( + retrieved_template_rule_name + ) + validated_threshold_operator = cls._validate_template_features( + retrieved_rule_type, + final_rule_conditions, + final_row_scope_filtering_enabled, + template_config, + threshold_compare_operator or retrieved_threshold_compare_operator, + retrieved_asset, + target_table_asset, + ) + except ValueError: + pass + + final_compare_operator = ( + validated_threshold_operator + or threshold_compare_operator + or retrieved_threshold_compare_operator + or DataQualityRuleThresholdCompareOperator.LESS_THAN_EQUAL + ) + + rule = cls( + name="", + dq_rule_config_arguments=DataQualityRuleConfigArguments( + dq_rule_threshold_object=DataQualityRuleThresholdObject( + dq_rule_threshold_compare_operator=final_compare_operator, + dq_rule_threshold_value=threshold_value + or retrieved_threshold_value, + dq_rule_threshold_unit=threshold_unit or retrieved_threshold_unit, + ), + dq_rule_config_rule_conditions=final_rule_conditions, + ), + dq_rule_base_dataset_qualified_name=( + retrieved_asset.qualified_name if retrieved_asset else None + ), + dq_rule_alert_priority=alert_priority or retrieved_alert_priority, + dq_rule_row_scope_filtering_enabled=final_row_scope_filtering_enabled, + dq_rule_base_dataset=retrieved_asset, + qualified_name=qualified_name, + dq_rule_dimension=dimension or retrieved_dimension, + dq_rule_template_name=retrieved_template_rule_name, + dq_rule_template=( + DataQualityRuleTemplate.ref_by_qualified_name( + qualified_name=retrieved_template.qualified_name + ) + if retrieved_template + else None + ), + ) + + if retrieved_column is not None: + rule.dq_rule_base_column_qualified_name = retrieved_column.qualified_name + rule.dq_rule_base_column = retrieved_column + + final_custom_sql = custom_sql or retrieved_custom_sql + if final_custom_sql is not None: + rule.dq_rule_custom_sql = final_custom_sql + rule.display_name = rule_name or retrieved_rule_name + rule.dq_rule_custom_sql_return_type = ( + custom_sql_return_type or retrieved_custom_sql_return_type + ) + if description is not None: + rule.user_description = description or retrieved_description + + return rule + + @classmethod + def _build_rule( + cls, + *, + client: "AtlanClient", + rule_type: DataQualityRuleTemplateType, + asset: Asset, + threshold_compare_operator: DataQualityRuleThresholdCompareOperator, + threshold_value: int, + alert_priority: DataQualityRuleAlertPriority, + rule_name: Optional[str] = None, + column: Optional[Asset] = None, + threshold_unit: Optional[DataQualityRuleThresholdUnit] = None, + dimension: Optional[DataQualityDimension] = None, + custom_sql: Optional[str] = None, + custom_sql_return_type: Optional[DataQualityRuleCustomSQLReturnType] = None, + description: Optional[str] = None, + rule_conditions: Optional[str] = None, + row_scope_filtering_enabled: Optional[bool] = False, + ) -> "DataQualityRule": + """Internal helper that mirrors the legacy ``Attributes.creator`` logic.""" + template_config = client.dq_template_config_cache.get_template_config( + rule_type.value + ) + if template_config is None: + raise ErrorCode.DQ_RULE_NOT_FOUND.exception_with_parameters(rule_type.value) + + template_rule_name = template_config.get("name") + template_qualified_name = template_config.get("qualified_name") + + if dimension is None: + dimension = template_config.get("dimension") + + if threshold_unit is None: + config = template_config.get("config") + if config is not None: + threshold_unit = cls._get_template_config_value( + config.dq_rule_template_config_threshold_object, + "dqRuleTemplateConfigThresholdUnit", + "default", + ) + + rule = cls( + name="", + dq_rule_config_arguments=DataQualityRuleConfigArguments( + dq_rule_threshold_object=DataQualityRuleThresholdObject( + dq_rule_threshold_compare_operator=threshold_compare_operator, + dq_rule_threshold_value=threshold_value, + dq_rule_threshold_unit=threshold_unit, + ), + dq_rule_config_rule_conditions=rule_conditions, + ), + dq_rule_base_dataset_qualified_name=asset.qualified_name, + dq_rule_alert_priority=alert_priority, + dq_rule_row_scope_filtering_enabled=row_scope_filtering_enabled, + dq_rule_source_sync_status=DataQualitySourceSyncStatus.IN_PROGRESS, + dq_rule_status=DataQualityRuleStatus.ACTIVE, + dq_rule_base_dataset=asset, + qualified_name=f"{asset.qualified_name}/rule/{cls._generate_uuid()}", + dq_rule_dimension=dimension, + dq_rule_template_name=template_rule_name, + dq_rule_template=DataQualityRuleTemplate.ref_by_qualified_name( + qualified_name=template_qualified_name, + ), + ) + + if column is not None: + rule.dq_rule_base_column_qualified_name = column.qualified_name + rule.dq_rule_base_column = column + + if custom_sql is not None: + rule.dq_rule_custom_sql = custom_sql + rule.display_name = rule_name + if custom_sql_return_type is not None: + rule.dq_rule_custom_sql_return_type = custom_sql_return_type + if description is not None: + rule.user_description = description + + return rule + + @staticmethod + def _generate_uuid() -> str: + d = int(time.time() * 1000) + random_bytes = uuid.uuid4().bytes + rand_index = 0 + + def replace_char(c: str) -> str: + nonlocal d, rand_index + r = (d + random_bytes[rand_index % 16]) % 16 + rand_index += 1 + d = d // 16 + if c == "x": + return hex(r)[2:] + elif c == "y": + return hex((r & 0x3) | 0x8)[2:] + else: + return c + + template = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx" + return "".join(replace_char(c) if c in "xy" else c for c in template) + + @staticmethod + def _get_template_config_value( + config_value: str, + property_name: Optional[str] = None, + value_key: str = "default", + ): + if not config_value: + return None + try: + config_json = json.loads(config_value) + if property_name: + properties = config_json.get("properties", {}) + field = properties.get(property_name, {}) + return field.get(value_key) + else: + return config_json.get(value_key) + except (json.JSONDecodeError, KeyError): + return None + + @staticmethod + def _validate_template_features( + rule_type: DataQualityRuleTemplateType, + rule_conditions: Optional[str], + row_scope_filtering_enabled: Optional[bool], + template_config: Optional[dict], + threshold_compare_operator: Optional[ + DataQualityRuleThresholdCompareOperator + ] = None, + asset: Optional[Asset] = None, + target_table_asset: Optional[Asset] = None, + ) -> Optional[DataQualityRuleThresholdCompareOperator]: + if not template_config or not template_config.get("config"): + return None + + config = template_config["config"] + + if rule_conditions and config.dq_rule_template_config_rule_conditions is None: + raise ErrorCode.DQ_RULE_TYPE_NOT_SUPPORTED.exception_with_parameters( + rule_type.value, "rule conditions" + ) + + if row_scope_filtering_enabled: + advanced_settings = config.dq_rule_template_config_advanced_settings or "" + if "dqRuleRowScopeFilteringEnabled" not in str(advanced_settings): + raise ErrorCode.DQ_RULE_TYPE_NOT_SUPPORTED.exception_with_parameters( + rule_type.value, "row scope filtering" + ) + if asset and not getattr( + asset, + "asset_dq_row_scope_filter_column_qualified_name", + None, + ): + raise ErrorCode.DQ_ROW_SCOPE_FILTER_COLUMN_MISSING.exception_with_parameters( + getattr(asset, "qualified_name", "unknown") + ) + if target_table_asset: + if not getattr( + target_table_asset, + "asset_dq_row_scope_filter_column_qualified_name", + None, + ): + raise ErrorCode.DQ_ROW_SCOPE_FILTER_COLUMN_MISSING.exception_with_parameters( + getattr(target_table_asset, "qualified_name", "unknown") + ) + + if rule_conditions: + allowed_rule_conditions = DataQualityRule._get_template_config_value( + config.dq_rule_template_config_rule_conditions or "", + None, + "enum", + ) + if allowed_rule_conditions: + try: + rule_conditions_json = json.loads(rule_conditions) + conditions = rule_conditions_json.get("conditions", []) + if len(conditions) != 1: + raise ErrorCode.DQ_RULE_CONDITIONS_INVALID.exception_with_parameters( + f"exactly one condition required, found {len(conditions)}" + ) + condition_type = conditions[0].get("type") + except json.JSONDecodeError: + condition_type = rule_conditions + + if condition_type not in allowed_rule_conditions: + raise ErrorCode.DQ_RULE_CONDITIONS_INVALID.exception_with_parameters( + f"condition type '{condition_type}' not supported, allowed: {allowed_rule_conditions}" + ) + + if threshold_compare_operator is None: + return DataQualityRuleThresholdCompareOperator.EQUAL + elif ( + threshold_compare_operator + != DataQualityRuleThresholdCompareOperator.EQUAL + ): + raise ErrorCode.INVALID_PARAMETER_VALUE.exception_with_parameters( + f"threshold_compare_operator={threshold_compare_operator.value}", + "threshold_compare_operator", + "EQUAL when rule_conditions are provided", + ) + + if threshold_compare_operator is not None: + allowed_operators = DataQualityRule._get_template_config_value( + config.dq_rule_template_config_threshold_object, + "dqRuleTemplateConfigThresholdCompareOperator", + "enum", + ) + if ( + allowed_operators + and threshold_compare_operator.value not in allowed_operators + ): + raise ErrorCode.INVALID_PARAMETER_VALUE.exception_with_parameters( + f"threshold_compare_operator={threshold_compare_operator.value}", + "threshold_compare_operator", + f"must be one of {allowed_operators}", + ) + elif threshold_compare_operator is None: + default_value = DataQualityRule._get_template_config_value( + config.dq_rule_template_config_threshold_object, + "dqRuleTemplateConfigThresholdCompareOperator", + "default", + ) + if default_value: + threshold_compare_operator = DataQualityRuleThresholdCompareOperator( + default_value + ) + + return ( + threshold_compare_operator + or DataQualityRuleThresholdCompareOperator.LESS_THAN_EQUAL + ) + + @staticmethod + def _fetch_assets_for_row_scope_validation( + client: "AtlanClient", + base_asset: Asset, + rule_conditions: Optional[str], + row_scope_filtering_enabled: bool, + ) -> tuple[Asset, Optional[Asset]]: + asset_for_validation = base_asset + target_table_asset = None + + if not row_scope_filtering_enabled: + return asset_for_validation, target_table_asset + + # Extract target_table from rule_conditions + target_table_qualified_name = None + if rule_conditions: + try: + rule_conditions_json = json.loads(rule_conditions) + conditions = rule_conditions_json.get("conditions", []) + if conditions: + condition_value = conditions[0].get("value", {}) + target_table_qualified_name = condition_value.get("target_table") + except (json.JSONDecodeError, KeyError, TypeError, AttributeError): + pass + + qualified_names_to_search = [] + if base_asset.qualified_name: + qualified_names_to_search.append(base_asset.qualified_name) + if target_table_qualified_name: + qualified_names_to_search.append(target_table_qualified_name) + + if qualified_names_to_search: + from pyatlan_v9.model.fluent_search import FluentSearch + + search_request = ( + FluentSearch() + .where(Asset.QUALIFIED_NAME.within(qualified_names_to_search)) + .include_on_results( + Asset.ASSET_DQ_ROW_SCOPE_FILTER_COLUMN_QUALIFIED_NAME + ) + ).to_request() + results = client.asset.search(search_request) + + for result in results.current_page(): + if result.qualified_name == base_asset.qualified_name: + asset_for_validation = result + elif ( + target_table_qualified_name + and result.qualified_name == target_table_qualified_name + ): + target_table_asset = result + + return asset_for_validation, target_table_asset diff --git a/pyatlan_v9/model/assets/_overlays/data_studio_asset.py b/pyatlan_v9/model/assets/_overlays/data_studio_asset.py new file mode 100644 index 000000000..2ce75aed7 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/data_studio_asset.py @@ -0,0 +1,38 @@ +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + connection_qualified_name: str, + data_studio_asset_type: str, + ) -> "DataStudioAsset": + """Create a new DataStudioAsset asset.""" + validate_required_fields( + ["name", "connection_qualified_name", "data_studio_asset_type"], + [name, connection_qualified_name, data_studio_asset_type], + ) + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + return cls( + name=name, + connection_qualified_name=connection_qualified_name, + qualified_name=f"{connection_qualified_name}/{name}", + connector_name=connector_name, + data_studio_asset_type=data_studio_asset_type, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "DataStudioAsset": + """Create a DataStudioAsset instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "DataStudioAsset": + """Return only fields required for update operations.""" + return DataStudioAsset.updater( + qualified_name=self.qualified_name, + name=self.name, + ) diff --git a/pyatlan_v9/model/assets/_overlays/database.py b/pyatlan_v9/model/assets/_overlays/database.py new file mode 100644 index 000000000..cf48ba815 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/database.py @@ -0,0 +1,59 @@ +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + connection_qualified_name: str, + ) -> "Database": + """ + Create a new Database asset. + + Args: + name: Name of the database + connection_qualified_name: Unique name of the connection in which this database exists + + Returns: + Database instance ready to be created + + Raises: + ValueError: If required parameters are missing or invalid + """ + validate_required_fields( + ["name", "connection_qualified_name"], [name, connection_qualified_name] + ) + + fields = connection_qualified_name.split("/") + if len(fields) != 3: + raise ValueError( + f"Invalid connection_qualified_name: {connection_qualified_name}. " + "Expected format: default/connector/connection_id" + ) + + connector_name = fields[1] + qualified_name = f"{connection_qualified_name}/{name}" + + return cls( + name=name, + qualified_name=qualified_name, + connection_qualified_name=connection_qualified_name, + connector_name=connector_name, + ) + + @classmethod + def create(cls, *, name: str, connection_qualified_name: str) -> "Database": + """ + Create a new Database asset (deprecated - use creator instead). + + Args: + name: Name of the database + connection_qualified_name: Unique name of the connection in which this database exists + + Returns: + Database instance ready to be created + """ + return cls.creator( + name=name, connection_qualified_name=connection_qualified_name + ) diff --git a/pyatlan_v9/model/assets/_overlays/dataverse_attribute.py b/pyatlan_v9/model/assets/_overlays/dataverse_attribute.py new file mode 100644 index 000000000..470710cb5 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/dataverse_attribute.py @@ -0,0 +1,51 @@ +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + dataverse_entity_qualified_name: str, + connection_qualified_name: str | None = None, + ) -> "DataverseAttribute": + """Create a new DataverseAttribute asset.""" + validate_required_fields( + ["name", "dataverse_entity_qualified_name"], + [name, dataverse_entity_qualified_name], + ) + if connection_qualified_name: + connector_name = ( + connection_qualified_name.split("/")[1] + if len(connection_qualified_name.split("/")) > 1 + else "" + ) + else: + fields = dataverse_entity_qualified_name.split("/") + if len(fields) < 3: + raise ValueError("dataverse_entity_qualified_name is invalid") + connection_qualified_name = "/".join(fields[:3]) + connector_name = fields[1] + return cls( + name=name, + qualified_name=f"{dataverse_entity_qualified_name}/{name}", + connection_qualified_name=connection_qualified_name, + connector_name=connector_name, + dataverse_entity_qualified_name=dataverse_entity_qualified_name, + dataverse_entity=RelatedDataverseEntity( + unique_attributes={"qualifiedName": dataverse_entity_qualified_name} + ), + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "DataverseAttribute": + """Create a DataverseAttribute instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "DataverseAttribute": + """Return only fields required for update operations.""" + return DataverseAttribute.updater( + qualified_name=self.qualified_name, + name=self.name, + ) diff --git a/pyatlan_v9/model/assets/_overlays/dataverse_entity.py b/pyatlan_v9/model/assets/_overlays/dataverse_entity.py new file mode 100644 index 000000000..d99424bcd --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/dataverse_entity.py @@ -0,0 +1,37 @@ +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + connection_qualified_name: str, + ) -> "DataverseEntity": + """Create a new DataverseEntity asset.""" + validate_required_fields( + ["name", "connection_qualified_name"], [name, connection_qualified_name] + ) + connector_name = ( + connection_qualified_name.split("/")[1] + if len(connection_qualified_name.split("/")) > 1 + else "" + ) + return cls( + name=name, + qualified_name=f"{connection_qualified_name}/{name}", + connection_qualified_name=connection_qualified_name, + connector_name=connector_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "DataverseEntity": + """Create a DataverseEntity instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "DataverseEntity": + """Return only fields required for update operations.""" + return DataverseEntity.updater( + qualified_name=self.qualified_name, name=self.name + ) diff --git a/pyatlan_v9/model/assets/_overlays/document_db_collection.py b/pyatlan_v9/model/assets/_overlays/document_db_collection.py new file mode 100644 index 000000000..bf2250895 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/document_db_collection.py @@ -0,0 +1,47 @@ +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + database_qualified_name: str, + connection_qualified_name: str | None = None, + ) -> "DocumentDBCollection": + """Create a new DocumentDBCollection asset.""" + validate_required_fields( + ["name", "database_qualified_name"], [name, database_qualified_name] + ) + if connection_qualified_name: + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + else: + parts = database_qualified_name.split("/") + if len(parts) < 3: + raise ValueError("database_qualified_name is invalid") + connection_qualified_name = "/".join(parts[:3]) + connector_name = parts[1] + return cls( + name=name, + database_qualified_name=database_qualified_name, + connection_qualified_name=connection_qualified_name, + qualified_name=f"{database_qualified_name}/{name}", + connector_name=connector_name, + document_db_database=RelatedDocumentDBDatabase( + unique_attributes={"qualifiedName": database_qualified_name} + ), + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "DocumentDBCollection": + """Create a DocumentDBCollection instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "DocumentDBCollection": + """Return only fields required for update operations.""" + return DocumentDBCollection.updater( + qualified_name=self.qualified_name, + name=self.name, + ) diff --git a/pyatlan_v9/model/assets/_overlays/document_db_database.py b/pyatlan_v9/model/assets/_overlays/document_db_database.py new file mode 100644 index 000000000..9f0378458 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/document_db_database.py @@ -0,0 +1,35 @@ +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + connection_qualified_name: str, + ) -> "DocumentDBDatabase": + """Create a new DocumentDBDatabase asset.""" + validate_required_fields( + ["name", "connection_qualified_name"], [name, connection_qualified_name] + ) + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + return cls( + name=name, + connection_qualified_name=connection_qualified_name, + qualified_name=f"{connection_qualified_name}/{name}", + connector_name=connector_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "DocumentDBDatabase": + """Create a DocumentDBDatabase instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "DocumentDBDatabase": + """Return only fields required for update operations.""" + return DocumentDBDatabase.updater( + qualified_name=self.qualified_name, + name=self.name, + ) diff --git a/pyatlan_v9/model/assets/_overlays/file.py b/pyatlan_v9/model/assets/_overlays/file.py new file mode 100644 index 000000000..30fc4fdc0 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/file.py @@ -0,0 +1,87 @@ +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + connection_qualified_name: str, + file_type: str, + ) -> "File": + """ + Create a new File asset. + + Args: + name: Simple name of the file + connection_qualified_name: Unique name of the connection in which this file exists + file_type: Type of the file (e.g., PDF, CSV) + + Returns: + New File instance with all fields populated + + Raises: + ValueError: If required parameters are missing or blank + """ + if isinstance(name, str) and name.strip() == "": + raise ValueError("name cannot be blank") + if ( + isinstance(connection_qualified_name, str) + and connection_qualified_name.strip() == "" + ): + raise ValueError("connection_qualified_name cannot be blank") + if isinstance(file_type, str) and file_type.strip() == "": + raise ValueError("file_type cannot be blank") + validate_required_fields( + ["name", "connection_qualified_name", "file_type"], + [name, connection_qualified_name, file_type], + ) + + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + qualified_name = f"{connection_qualified_name}/{name}" + + return cls( + name=name, + qualified_name=qualified_name, + file_type=file_type, + connector_name=connector_name, + connection_qualified_name=connection_qualified_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "File": + """ + Create a File instance for updating an existing asset. + + Args: + qualified_name: Unique name of the file to update + name: Simple name of the file + + Returns: + File instance configured for updates + + Raises: + ValueError: If required parameters are missing + """ + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "File": + """ + Return a File with only required fields for reference. + + Returns: + File instance with only qualified_name and name set + """ + return File(qualified_name=self.qualified_name, name=self.name) + + @classmethod + def create(cls, **kwargs) -> "File": + """Backward compatibility alias for creator().""" + return cls.creator(**kwargs) + + @classmethod + def create_for_modification(cls, **kwargs) -> "File": + """Backward compatibility alias for updater().""" + return cls.updater(**kwargs) diff --git a/pyatlan_v9/model/assets/_overlays/folder.py b/pyatlan_v9/model/assets/_overlays/folder.py new file mode 100644 index 000000000..ddf829f5c --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/folder.py @@ -0,0 +1,45 @@ +# IMPORT: from pyatlan.utils import validate_required_fields +# INTERNAL_IMPORT: from pyatlan.model.assets import Collection + + @classmethod + def creator( + cls, + *, + name: str, + collection_qualified_name: str | None = None, + parent_folder_qualified_name: str | None = None, + ) -> "Folder": + from pyatlan.utils import validate_required_fields + + validate_required_fields(["name"], [name]) + if not (parent_folder_qualified_name or collection_qualified_name): + raise ValueError( + "Either 'collection_qualified_name' or 'parent_folder_qualified_name' must be specified." + ) + + if not parent_folder_qualified_name: + qualified_name = f"{collection_qualified_name}/{name}" + parent_qn = collection_qualified_name + from pyatlan_v9.model.assets import Collection + + parent_ref = Collection.ref_by_qualified_name( + collection_qualified_name or "" + ) + else: + tokens = parent_folder_qualified_name.split("/") + if len(tokens) < 4: + raise ValueError("Invalid parent_folder_qualified_name") + collection_qualified_name = ( + f"{tokens[0]}/{tokens[1]}/{tokens[2]}/{tokens[3]}" + ) + qualified_name = f"{parent_folder_qualified_name}/{name}" + parent_qn = parent_folder_qualified_name + parent_ref = Folder.ref_by_qualified_name(parent_folder_qualified_name) + + return Folder( + name=name, + qualified_name=qualified_name, + collection_qualified_name=collection_qualified_name, + parent=parent_ref, + parent_qualified_name=parent_qn, + ) diff --git a/pyatlan_v9/model/assets/_overlays/gcs_bucket.py b/pyatlan_v9/model/assets/_overlays/gcs_bucket.py new file mode 100644 index 000000000..27f81fc08 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/gcs_bucket.py @@ -0,0 +1,56 @@ +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator(cls, *, name: str, connection_qualified_name: str) -> "GCSBucket": + """ + Create a new GCSBucket asset. + + Args: + name: Name of the bucket + connection_qualified_name: Unique name of the connection + + Returns: + GCSBucket instance ready to be created + + Raises: + ValueError: If required parameters are missing + """ + validate_required_fields( + ["name", "connection_qualified_name"], [name, connection_qualified_name] + ) + # Extract connector name from the connection_qualified_name + connector_name = connection_qualified_name.split("/")[1] + return cls( + name=name, + qualified_name=f"{connection_qualified_name}/{name}", + connection_qualified_name=connection_qualified_name, + connector_name=connector_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "GCSBucket": + """ + Create a GCSBucket instance for modification. + + Args: + qualified_name: Unique name of the GCSBucket to update + name: Human-readable name of the GCSBucket + + Returns: + GCSBucket instance ready for update + + Raises: + ValueError: If required parameters are missing + """ + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "GCSBucket": + """ + Return a copy of this GCSBucket with only the minimum required fields for update. + + Returns: + GCSBucket with only qualified_name and name set + """ + return GCSBucket.updater(qualified_name=self.qualified_name, name=self.name) diff --git a/pyatlan_v9/model/assets/_overlays/gcs_object.py b/pyatlan_v9/model/assets/_overlays/gcs_object.py new file mode 100644 index 000000000..2fe7582f6 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/gcs_object.py @@ -0,0 +1,138 @@ +# IMPORT: from pyatlan.model.utils import construct_object_key +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + gcs_bucket_name: str, + gcs_bucket_qualified_name: str, + connection_qualified_name: str | None = None, + ) -> "GCSObject": + """ + Create a new GCSObject asset. + + Args: + name: Name of the object + gcs_bucket_name: Simple name of the bucket + gcs_bucket_qualified_name: Unique name of the bucket + connection_qualified_name: Unique name of the connection (optional, + derived from gcs_bucket_qualified_name if not provided) + + Returns: + GCSObject instance ready to be created + + Raises: + ValueError: If required parameters are missing + """ + validate_required_fields( + ["name", "gcs_bucket_name", "gcs_bucket_qualified_name"], + [name, gcs_bucket_name, gcs_bucket_qualified_name], + ) + if connection_qualified_name: + connector_name = connection_qualified_name.split("/")[1] + else: + # Derive connection_qualified_name from gcs_bucket_qualified_name + # gcs_bucket_qualified_name format: "default/gcs/123456789/mybucket" + parts = gcs_bucket_qualified_name.split("/") + connection_qualified_name = "/".join(parts[:3]) + connector_name = parts[1] + + return cls( + name=name, + connection_qualified_name=connection_qualified_name, + qualified_name=f"{gcs_bucket_qualified_name}/{name}", + connector_name=connector_name, + gcs_bucket_name=gcs_bucket_name, + gcs_bucket_qualified_name=gcs_bucket_qualified_name, + ) + + @classmethod + @init_guid + def creator_with_prefix( + cls, + *, + name: str, + connection_qualified_name: str, + gcs_bucket_name: str, + gcs_bucket_qualified_name: str, + prefix: str = "", + ) -> "GCSObject": + """ + Create a new GCSObject asset using a prefix-based object key. + + Args: + name: Name of the object + connection_qualified_name: Unique name of the connection + gcs_bucket_name: Simple name of the bucket + gcs_bucket_qualified_name: Unique name of the bucket + prefix: Prefix (folder path) for the object + + Returns: + GCSObject instance ready to be created + + Raises: + ValueError: If required parameters are missing or invalid + """ + validate_required_fields( + [ + "name", + "connection_qualified_name", + "gcs_bucket_name", + "gcs_bucket_qualified_name", + ], + [ + name, + connection_qualified_name, + gcs_bucket_name, + gcs_bucket_qualified_name, + ], + ) + fields = connection_qualified_name.split("/") + if len(fields) != 3: + raise ValueError("Invalid connection_qualified_name") + if fields[0].replace(" ", "") == "" or fields[2].replace(" ", "") == "": + raise ValueError("Invalid connection_qualified_name") + if fields[1].lower() != "gcs": + raise ValueError("Invalid connection_qualified_name") + + connector_name = fields[1] + object_key = construct_object_key(prefix, name) + return cls( + name=name, + gcs_object_key=object_key, + connection_qualified_name=connection_qualified_name, + qualified_name=f"{gcs_bucket_qualified_name}/{object_key}", + connector_name=connector_name, + gcs_bucket_name=gcs_bucket_name, + gcs_bucket_qualified_name=gcs_bucket_qualified_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "GCSObject": + """ + Create a GCSObject instance for modification. + + Args: + qualified_name: Unique name of the GCSObject to update + name: Human-readable name of the GCSObject + + Returns: + GCSObject instance ready for update + + Raises: + ValueError: If required parameters are missing + """ + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "GCSObject": + """ + Return a copy of this GCSObject with only the minimum required fields for update. + + Returns: + GCSObject with only qualified_name and name set + """ + return GCSObject.updater(qualified_name=self.qualified_name, name=self.name) diff --git a/pyatlan_v9/model/assets/_overlays/kafka_consumer_group.py b/pyatlan_v9/model/assets/_overlays/kafka_consumer_group.py new file mode 100644 index 000000000..3d8713a60 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/kafka_consumer_group.py @@ -0,0 +1,37 @@ +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + kafka_topic_qualified_names: list[str], + ) -> "KafkaConsumerGroup": + validate_required_fields( + ["name", "kafka_topic_qualified_names"], + [name, kafka_topic_qualified_names], + ) + # Extract connection from the first topic qualified name + first_topic_qn = kafka_topic_qualified_names[0] + fields = first_topic_qn.split("/") + connector_name = fields[1] if len(fields) > 1 else None + connection_qn = ( + f"{fields[0]}/{fields[1]}/{fields[2]}" if len(fields) >= 3 else None + ) + qualified_name = f"{connection_qn}/consumer-group/{name}" + return cls( + name=name, + qualified_name=qualified_name, + connector_name=connector_name, + connection_qualified_name=connection_qn, + kafka_topic_qualified_names=set(kafka_topic_qualified_names), + ) + + @classmethod + def create(cls, **kwargs) -> "KafkaConsumerGroup": + return cls.creator(**kwargs) + + @classmethod + def create_for_modification(cls, **kwargs) -> "KafkaConsumerGroup": + return cls.updater(**kwargs) diff --git a/pyatlan_v9/model/assets/_overlays/kafka_topic.py b/pyatlan_v9/model/assets/_overlays/kafka_topic.py new file mode 100644 index 000000000..82ebd3591 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/kafka_topic.py @@ -0,0 +1,31 @@ +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + connection_qualified_name: str, + ) -> "KafkaTopic": + validate_required_fields( + ["name", "connection_qualified_name"], + [name, connection_qualified_name], + ) + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + qualified_name = f"{connection_qualified_name}/topic/{name}" + return cls( + name=name, + qualified_name=qualified_name, + connector_name=connector_name, + connection_qualified_name=connection_qualified_name, + ) + + @classmethod + def create(cls, **kwargs) -> "KafkaTopic": + return cls.creator(**kwargs) + + @classmethod + def create_for_modification(cls, **kwargs) -> "KafkaTopic": + return cls.updater(**kwargs) diff --git a/pyatlan_v9/model/assets/_overlays/materialised_view.py b/pyatlan_v9/model/assets/_overlays/materialised_view.py new file mode 100644 index 000000000..2379c6eb4 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/materialised_view.py @@ -0,0 +1,99 @@ +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + schema_qualified_name: str, + schema_name: str | None = None, + database_name: str | None = None, + database_qualified_name: str | None = None, + connection_qualified_name: str | None = None, + ) -> "MaterialisedView": + """ + Create a new MaterialisedView asset with auto-derived fields. + + Args: + name: Simple name of the materialized view + schema_qualified_name: Unique name of the schema in which this materialized view exists + schema_name: Simple name of the schema (auto-derived if not provided) + database_name: Simple name of the database (auto-derived if not provided) + database_qualified_name: Unique name of the database (auto-derived if not provided) + connection_qualified_name: Unique name of the connection (auto-derived if not provided) + + Returns: + New MaterialisedView instance with all fields populated + + Raises: + ValueError: If required parameters are missing or invalid + """ + validate_required_fields( + ["name", "schema_qualified_name"], [name, schema_qualified_name] + ) + + fields = schema_qualified_name.split("/") + if len(fields) != 5: + raise ValueError( + f"Invalid schema_qualified_name: {schema_qualified_name}. " + "Expected format: default/connector/connection_id/database/schema" + ) + + connector_name = fields[1] + connection_qn = ( + connection_qualified_name or f"{fields[0]}/{fields[1]}/{fields[2]}" + ) + db_name = database_name or fields[3] + sch_name = schema_name or fields[4] + db_qualified_name = database_qualified_name or f"{connection_qn}/{db_name}" + qualified_name = f"{schema_qualified_name}/{name}" + + return cls( + name=name, + qualified_name=qualified_name, + database_name=db_name, + database_qualified_name=db_qualified_name, + schema_name=sch_name, + schema_qualified_name=schema_qualified_name, + connector_name=connector_name, + connection_qualified_name=connection_qn, + atlan_schema=RelatedSchema(qualified_name=schema_qualified_name), + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "MaterialisedView": + """ + Create a MaterialisedView instance for updating an existing asset. + + Args: + qualified_name: Unique name of the materialized view to update + name: Simple name of the materialized view + + Returns: + MaterialisedView instance configured for updates + + Raises: + ValueError: If required parameters are missing + """ + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "MaterialisedView": + """ + Return a MaterialisedView with only required fields for reference. + + Returns: + MaterialisedView instance with only qualified_name and name set + """ + return MaterialisedView(qualified_name=self.qualified_name, name=self.name) + + @classmethod + def create(cls, **kwargs) -> "MaterialisedView": + """Backward compatibility alias for creator().""" + return cls.creator(**kwargs) + + @classmethod + def create_for_modification(cls, **kwargs) -> "MaterialisedView": + """Backward compatibility alias for updater().""" + return cls.updater(**kwargs) diff --git a/pyatlan_v9/model/assets/_overlays/persona.py b/pyatlan_v9/model/assets/_overlays/persona.py new file mode 100644 index 000000000..cf009c8ae --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/persona.py @@ -0,0 +1,161 @@ +# IMPORT: from pyatlan.model.enums import AuthPolicyCategory, AuthPolicyResourceCategory, AuthPolicyType, DataAction, PersonaDomainAction, PersonaGlossaryAction, PersonaMetadataAction +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator(cls, *, name: str) -> "Persona": + validate_required_fields(["name"], [name]) + return cls( + qualified_name=name, + name=name, + display_name=name, + is_access_control_enabled=True, + description="", + ) + + @classmethod + def updater( + cls, *, qualified_name: str, name: str, is_enabled: bool = True + ) -> "Persona": + validate_required_fields( + ["name", "qualified_name", "is_enabled"], + [name, qualified_name, is_enabled], + ) + return cls( + qualified_name=qualified_name, + name=name, + is_access_control_enabled=is_enabled, + ) + + @classmethod + def create_for_modification( + cls, + qualified_name: str = "", + name: str = "", + is_enabled: bool = True, + ) -> "Persona": + warn( + ( + "This method is deprecated, please use 'updater' " + "instead, which offers identical functionality." + ), + DeprecationWarning, + stacklevel=2, + ) + return cls.updater( + qualified_name=qualified_name, name=name, is_enabled=is_enabled + ) + + @classmethod + def create_metadata_policy( + cls, + *, + name: str, + persona_id: str, + policy_type: AuthPolicyType, + actions: Set[PersonaMetadataAction], + connection_qualified_name: str, + resources: Set[str], + ) -> AuthPolicy: + validate_required_fields( + ["name", "persona_id", "policy_type", "actions", "resources"], + [name, persona_id, policy_type, actions, resources], + ) + policy = AuthPolicy._create(name=name) + policy.policy_actions = {x.value for x in actions} + policy.policy_category = AuthPolicyCategory.PERSONA.value + policy.policy_type = policy_type + policy.connection_qualified_name = connection_qualified_name + policy.policy_resources = resources + policy.policy_resource_category = AuthPolicyResourceCategory.CUSTOM.value + policy.policy_service_name = "atlas" + policy.policy_sub_category = "metadata" + persona = Persona() + persona.guid = persona_id + policy.access_control = persona + return policy + + @classmethod + def create_data_policy( + cls, + *, + name: str, + persona_id: str, + policy_type: AuthPolicyType, + connection_qualified_name: str, + resources: Set[str], + ) -> AuthPolicy: + validate_required_fields( + ["name", "persona_id", "policy_type", "resources"], + [name, persona_id, policy_type, resources], + ) + policy = AuthPolicy._create(name=name) + policy.policy_actions = {DataAction.SELECT.value} + policy.policy_category = AuthPolicyCategory.PERSONA.value + policy.policy_type = policy_type + policy.connection_qualified_name = connection_qualified_name + policy.policy_resources = resources + policy.policy_resources.add("entity-type:*") + policy.policy_resource_category = AuthPolicyResourceCategory.ENTITY.value + policy.policy_service_name = "heka" + policy.policy_sub_category = "data" + persona = Persona() + persona.guid = persona_id + policy.access_control = persona + return policy + + @classmethod + def create_glossary_policy( + cls, + *, + name: str, + persona_id: str, + policy_type: AuthPolicyType, + actions: Set[PersonaGlossaryAction], + resources: Set[str], + ) -> AuthPolicy: + validate_required_fields( + ["name", "persona_id", "policy_type", "actions", "resources"], + [name, persona_id, policy_type, actions, resources], + ) + policy = AuthPolicy._create(name=name) + policy.policy_actions = {x.value for x in actions} + policy.policy_category = AuthPolicyCategory.PERSONA.value + policy.policy_type = policy_type + policy.policy_resources = resources + policy.policy_resource_category = AuthPolicyResourceCategory.CUSTOM.value + policy.policy_service_name = "atlas" + policy.policy_sub_category = "glossary" + persona = Persona() + persona.guid = persona_id + policy.access_control = persona + return policy + + @classmethod + def create_domain_policy( + cls, + *, + name: str, + persona_id: str, + actions: Set[PersonaDomainAction], + resources: Set[str], + ) -> AuthPolicy: + validate_required_fields( + ["name", "persona_id", "actions", "resources"], + [name, persona_id, actions, resources], + ) + policy = AuthPolicy._create(name=name) + policy.policy_actions = {x.value for x in actions} + policy.policy_category = AuthPolicyCategory.PERSONA.value + policy.policy_type = AuthPolicyType.ALLOW + policy.policy_resources = resources + policy.policy_resource_category = AuthPolicyResourceCategory.CUSTOM.value + policy.policy_service_name = "atlas" + policy.policy_sub_category = "domain" + persona = Persona() + persona.guid = persona_id + policy.access_control = persona + return policy + + def trim_to_required(self) -> "Persona": + return Persona.updater(qualified_name=self.qualified_name, name=self.name) diff --git a/pyatlan_v9/model/assets/_overlays/preset_chart.py b/pyatlan_v9/model/assets/_overlays/preset_chart.py new file mode 100644 index 000000000..81222d6e6 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/preset_chart.py @@ -0,0 +1,30 @@ +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + preset_dashboard_qualified_name: str, + connection_qualified_name: str | None = None, + ) -> "PresetChart": + validate_required_fields( + ["name", "preset_dashboard_qualified_name"], + [name, preset_dashboard_qualified_name], + ) + fields = preset_dashboard_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + connection_qn = connection_qualified_name or ( + f"{fields[0]}/{fields[1]}/{fields[2]}" if len(fields) >= 3 else None + ) + return cls( + name=name, + qualified_name=f"{preset_dashboard_qualified_name}/{name}", + preset_dashboard_qualified_name=preset_dashboard_qualified_name, + connection_qualified_name=connection_qn, + connector_name=connector_name, + preset_dashboard=RelatedPresetDashboard( + qualified_name=preset_dashboard_qualified_name + ), + ) diff --git a/pyatlan_v9/model/assets/_overlays/preset_dashboard.py b/pyatlan_v9/model/assets/_overlays/preset_dashboard.py new file mode 100644 index 000000000..b50e9ff2c --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/preset_dashboard.py @@ -0,0 +1,30 @@ +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + preset_workspace_qualified_name: str, + connection_qualified_name: str | None = None, + ) -> "PresetDashboard": + validate_required_fields( + ["name", "preset_workspace_qualified_name"], + [name, preset_workspace_qualified_name], + ) + fields = preset_workspace_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + connection_qn = connection_qualified_name or ( + f"{fields[0]}/{fields[1]}/{fields[2]}" if len(fields) >= 3 else None + ) + return cls( + name=name, + qualified_name=f"{preset_workspace_qualified_name}/{name}", + preset_workspace_qualified_name=preset_workspace_qualified_name, + connection_qualified_name=connection_qn, + connector_name=connector_name, + preset_workspace=RelatedPresetWorkspace( + qualified_name=preset_workspace_qualified_name + ), + ) diff --git a/pyatlan_v9/model/assets/_overlays/preset_dataset.py b/pyatlan_v9/model/assets/_overlays/preset_dataset.py new file mode 100644 index 000000000..39514ed9e --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/preset_dataset.py @@ -0,0 +1,30 @@ +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + preset_dashboard_qualified_name: str, + connection_qualified_name: str | None = None, + ) -> "PresetDataset": + validate_required_fields( + ["name", "preset_dashboard_qualified_name"], + [name, preset_dashboard_qualified_name], + ) + fields = preset_dashboard_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + connection_qn = connection_qualified_name or ( + f"{fields[0]}/{fields[1]}/{fields[2]}" if len(fields) >= 3 else None + ) + return cls( + name=name, + qualified_name=f"{preset_dashboard_qualified_name}/{name}", + preset_dashboard_qualified_name=preset_dashboard_qualified_name, + connection_qualified_name=connection_qn, + connector_name=connector_name, + preset_dashboard=RelatedPresetDashboard( + qualified_name=preset_dashboard_qualified_name + ), + ) diff --git a/pyatlan_v9/model/assets/_overlays/preset_workspace.py b/pyatlan_v9/model/assets/_overlays/preset_workspace.py new file mode 100644 index 000000000..d84a77040 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/preset_workspace.py @@ -0,0 +1,30 @@ +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + connection_qualified_name: str, + ) -> "PresetWorkspace": + validate_required_fields( + ["name", "connection_qualified_name"], + [name, connection_qualified_name], + ) + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + return cls( + name=name, + qualified_name=f"{connection_qualified_name}/{name}", + connection_qualified_name=connection_qualified_name, + connector_name=connector_name, + ) + + @classmethod + def create(cls, **kwargs) -> "PresetWorkspace": + return cls.creator(**kwargs) + + @classmethod + def create_for_modification(cls, **kwargs) -> "PresetWorkspace": + return cls.updater(**kwargs) diff --git a/pyatlan_v9/model/assets/_overlays/procedure.py b/pyatlan_v9/model/assets/_overlays/procedure.py new file mode 100644 index 000000000..af0b69837 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/procedure.py @@ -0,0 +1,80 @@ +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + definition: str, + schema_qualified_name: str, + schema_name: str | None = None, + database_name: str | None = None, + database_qualified_name: str | None = None, + connection_qualified_name: str | None = None, + ) -> "Procedure": + validate_required_fields( + ["name", "definition", "schema_qualified_name"], + [name, definition, schema_qualified_name], + ) + + fields = schema_qualified_name.split("/") + if len(fields) != 5: + raise ValueError( + f"Invalid schema_qualified_name: {schema_qualified_name}. " + "Expected format: default/connector/connection_id/database/schema" + ) + + connector_name = fields[1] + connection_qn = ( + connection_qualified_name or f"{fields[0]}/{fields[1]}/{fields[2]}" + ) + db_name = database_name or fields[3] + sch_name = schema_name or fields[4] + db_qualified_name = database_qualified_name or f"{connection_qn}/{db_name}" + qualified_name = f"{schema_qualified_name}/_procedures_/{name}" + + return cls( + name=name, + definition=definition, + qualified_name=qualified_name, + database_name=db_name, + database_qualified_name=db_qualified_name, + schema_name=sch_name, + schema_qualified_name=schema_qualified_name, + connector_name=connector_name, + connection_qualified_name=connection_qn, + atlan_schema=RelatedSchema(qualified_name=schema_qualified_name), + ) + + @classmethod + def updater( + cls, + *, + qualified_name: str, + name: str, + definition: str = "", + ) -> "Procedure": + validate_required_fields( + ["qualified_name", "name"], + [qualified_name, name], + ) + proc = cls(qualified_name=qualified_name, name=name) + if definition: + proc.definition = definition + return proc + + def trim_to_required(self) -> "Procedure": + return Procedure.updater( + qualified_name=self.qualified_name or "", + name=self.name or "", + definition=self.definition or "", + ) + + @classmethod + def create(cls, **kwargs) -> "Procedure": + return cls.creator(**kwargs) + + @classmethod + def create_for_modification(cls, **kwargs) -> "Procedure": + return cls.updater(**kwargs) diff --git a/pyatlan_v9/model/assets/_overlays/process.py b/pyatlan_v9/model/assets/_overlays/process.py new file mode 100644 index 000000000..7fe864c0e --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/process.py @@ -0,0 +1,122 @@ +# STDLIB_IMPORT: import hashlib +# STDLIB_IMPORT: from io import StringIO +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @staticmethod + def _extract_guid(relationship: Any) -> Union[str, None]: + """Extract guid from a relationship-like object.""" + if relationship is None: + return None + guid = getattr(relationship, "guid", UNSET) + if guid is UNSET or not guid: + return None + return guid + + @staticmethod + def generate_qualified_name( + *, + name: str, + connection_qualified_name: str, + inputs: list[Any], + outputs: list[Any], + parent: Union[Any, None] = None, + process_id: Union[str, None] = None, + extra_hash_params: Union[set[str], None] = None, + ) -> str: + """Generate process qualified name using explicit process_id or deterministic hash.""" + validate_required_fields( + ["name", "connection_qualified_name", "inputs", "outputs"], + [name, connection_qualified_name, inputs, outputs], + ) + if process_id and process_id.strip(): + return f"{connection_qualified_name}/{process_id}" + buffer = StringIO() + buffer.write(name) + buffer.write(connection_qualified_name) + parent_guid = Process._extract_guid(parent) + if parent_guid: + buffer.write(parent_guid) + for relationship in inputs: + guid = Process._extract_guid(relationship) + if guid: + buffer.write(guid) + for relationship in outputs: + guid = Process._extract_guid(relationship) + if guid: + buffer.write(guid) + if extra_hash_params: + for param in extra_hash_params: + buffer.write(param) + hash_seed = buffer.getvalue() + buffer.close() + # deepcode ignore InsecureHash/test: this is not used for generating security keys + return ( + f"{connection_qualified_name}/{hashlib.md5(hash_seed.encode()).hexdigest()}" # noqa: S324 + ) + + @staticmethod + def _to_related_catalog(value: Any) -> RelatedCatalog: + """Convert any relationship-like value to a RelatedCatalog reference.""" + if isinstance(value, RelatedCatalog): + return value + guid = getattr(value, "guid", UNSET) + type_name = getattr(value, "type_name", UNSET) + if guid is not UNSET and guid: + kwargs: dict[str, Any] = {"guid": guid} + if type_name is not UNSET and type_name: + kwargs["type_name"] = type_name + return RelatedCatalog(**kwargs) + qualified_name = getattr(value, "qualified_name", UNSET) + if qualified_name is not UNSET and qualified_name: + kwargs = {"unique_attributes": {"qualifiedName": qualified_name}} + if type_name is not UNSET and type_name: + kwargs["type_name"] = type_name + return RelatedCatalog(**kwargs) + return RelatedCatalog() + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + connection_qualified_name: str, + inputs: list[Any], + outputs: list[Any], + process_id: Union[str, None] = None, + parent: Union[Any, None] = None, + extra_hash_params: Union[set[str], None] = None, + ) -> "Process": + """Create a new Process asset.""" + qualified_name = cls.generate_qualified_name( + name=name, + connection_qualified_name=connection_qualified_name, + process_id=process_id, + inputs=inputs, + outputs=outputs, + parent=parent, + extra_hash_params=extra_hash_params, + ) + connector_name = ( + connection_qualified_name.split("/")[1] + if len(connection_qualified_name.split("/")) > 1 + else "" + ) + return cls( + name=name, + qualified_name=qualified_name, + connector_name=connector_name, + connection_qualified_name=connection_qualified_name, + inputs=[cls._to_related_catalog(item) for item in inputs], + outputs=[cls._to_related_catalog(item) for item in outputs], + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "Process": + """Create a Process instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "Process": + """Return only fields required for update operations.""" + return Process.updater(qualified_name=self.qualified_name, name=self.name) diff --git a/pyatlan_v9/model/assets/_overlays/purpose.py b/pyatlan_v9/model/assets/_overlays/purpose.py new file mode 100644 index 000000000..daf0a75b3 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/purpose.py @@ -0,0 +1,185 @@ +# IMPORT: from pyatlan.model.enums import AuthPolicyCategory, AuthPolicyResourceCategory, AuthPolicyType, DataAction, PurposeMetadataAction +# INTERNAL_IMPORT: from pyatlan.model.core import AtlanTagName +# INTERNAL_IMPORT: from pyatlan.model.structs import SourceTagAttachment +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @property + def purpose_atlan_tags(self) -> Union[list[AtlanTagName], None]: + """Expose purpose classifications as AtlanTagName objects for parity.""" + if self.purpose_classifications in (UNSET, None): + return None + return [ + tag if isinstance(tag, AtlanTagName) else AtlanTagName(str(tag)) + for tag in self.purpose_classifications + ] + + @purpose_atlan_tags.setter + def purpose_atlan_tags(self, value: Union[list[AtlanTagName], None]) -> None: + if value is None: + self.purpose_classifications = None + else: + self.purpose_classifications = [str(tag) for tag in value] + + @classmethod + @init_guid + def creator(cls, *, name: str, atlan_tags: list[AtlanTagName]) -> "Purpose": + """Create a new Purpose asset.""" + validate_required_fields(["name", "atlan_tags"], [name, atlan_tags]) + return cls( + name=name, + qualified_name=name, + display_name=name, + description="", + is_access_control_enabled=True, + purpose_classifications=[str(tag) for tag in atlan_tags], + ) + + @classmethod + def updater( + cls, *, qualified_name: str, name: str, is_enabled: bool = True + ) -> "Purpose": + """Create a Purpose asset for update operations.""" + validate_required_fields( + ["qualified_name", "name", "is_enabled"], + [qualified_name, name, is_enabled], + ) + return cls( + qualified_name=qualified_name, + name=name, + is_access_control_enabled=is_enabled, + ) + + @classmethod + def create_for_modification( + cls, + qualified_name: str = "", + name: str = "", + is_enabled: bool = True, + ) -> "Purpose": + warn( + ( + "This method is deprecated, please use 'updater' " + "instead, which offers identical functionality." + ), + DeprecationWarning, + stacklevel=2, + ) + return cls.updater( + qualified_name=qualified_name, name=name, is_enabled=is_enabled + ) + + @classmethod + def create_metadata_policy( + cls, + *, + client: "AtlanClient", + name: str, + purpose_id: str, + policy_type: AuthPolicyType, + actions: Set[PurposeMetadataAction], + policy_groups: Optional[Set[str]] = None, + policy_users: Optional[Set[str]] = None, + all_users: bool = False, + ) -> AuthPolicy: + validate_required_fields( + ["client", "name", "purpose_id", "policy_type", "actions"], + [client, name, purpose_id, policy_type, actions], + ) + target_found = False + policy = AuthPolicy._create(name=name) + policy.policy_actions = {x.value for x in actions} + policy.policy_category = AuthPolicyCategory.PURPOSE.value + policy.policy_type = policy_type + policy.policy_resource_category = AuthPolicyResourceCategory.TAG.value + policy.policy_service_name = "atlas_tag" + policy.policy_sub_category = "metadata" + purpose = Purpose() + purpose.guid = purpose_id + policy.access_control = purpose + if all_users: + target_found = True + policy.policy_groups = {"public"} + else: + if policy_groups: + for group_name in policy_groups: + if not client.group_cache.get_id_for_name(group_name): + raise ValueError( + f"Provided group name {group_name} was not found in Atlan." + ) + target_found = True + policy.policy_groups = policy_groups + else: + policy.policy_groups = None + if policy_users: + for username in policy_users: + if not client.user_cache.get_id_for_name(username): + raise ValueError( + f"Provided username {username} was not found in Atlan." + ) + target_found = True + policy.policy_users = policy_users + else: + policy.policy_users = None + if target_found: + return policy + else: + raise ValueError("No user or group specified for the policy.") + + @classmethod + def create_data_policy( + cls, + *, + client: "AtlanClient", + name: str, + purpose_id: str, + policy_type: AuthPolicyType, + policy_groups: Optional[Set[str]] = None, + policy_users: Optional[Set[str]] = None, + all_users: bool = False, + ) -> AuthPolicy: + validate_required_fields( + ["client", "name", "purpose_id", "policy_type"], + [client, name, purpose_id, policy_type], + ) + policy = AuthPolicy._create(name=name) + policy.policy_actions = {DataAction.SELECT.value} + policy.policy_category = AuthPolicyCategory.PURPOSE.value + policy.policy_type = policy_type + policy.policy_resource_category = AuthPolicyResourceCategory.TAG.value + policy.policy_service_name = "atlas_tag" + policy.policy_sub_category = "data" + purpose = Purpose() + purpose.guid = purpose_id + policy.access_control = purpose + if all_users: + target_found = True + policy.policy_groups = {"public"} + else: + if policy_groups: + for group_name in policy_groups: + if not client.group_cache.get_id_for_name(group_name): + raise ValueError( + f"Provided group name {group_name} was not found in Atlan." + ) + target_found = True + policy.policy_groups = policy_groups + else: + policy.policy_groups = None + if policy_users: + for username in policy_users: + if not client.user_cache.get_id_for_name(username): + raise ValueError( + f"Provided username {username} was not found in Atlan." + ) + target_found = True + policy.policy_users = policy_users + else: + policy.policy_users = None + if target_found: + return policy + else: + raise ValueError("No user or group specified for the policy.") + + def trim_to_required(self) -> "Purpose": + """Return only required fields for updates.""" + return Purpose.updater(qualified_name=self.qualified_name, name=self.name) diff --git a/pyatlan_v9/model/assets/_overlays/query.py b/pyatlan_v9/model/assets/_overlays/query.py new file mode 100644 index 000000000..c32b99bd3 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/query.py @@ -0,0 +1,111 @@ +# IMPORT: from pyatlan.model.enums import AtlanConnectorType +# IMPORT: from pyatlan.utils import validate_required_fields +# INTERNAL_IMPORT: from pyatlan.model.assets import Collection +# INTERNAL_IMPORT: from pyatlan.model.assets import Folder + + @classmethod + def creator( + cls, + *, + name: str, + collection_qualified_name: str | None = None, + parent_folder_qualified_name: str | None = None, + ) -> "Query": + from pyatlan.utils import validate_required_fields + + validate_required_fields(["name"], [name]) + if not (parent_folder_qualified_name or collection_qualified_name): + raise ValueError( + "Either 'collection_qualified_name' or 'parent_folder_qualified_name' must be specified." + ) + + if not parent_folder_qualified_name: + qualified_name = f"{collection_qualified_name}/{name}" + parent_qn = collection_qualified_name + from pyatlan_v9.model.assets import Collection + + parent_ref = Collection.ref_by_qualified_name( + collection_qualified_name or "" + ) + else: + tokens = parent_folder_qualified_name.split("/") + if len(tokens) < 4: + raise ValueError("Invalid parent_folder_qualified_name") + collection_qualified_name = ( + f"{tokens[0]}/{tokens[1]}/{tokens[2]}/{tokens[3]}" + ) + qualified_name = f"{parent_folder_qualified_name}/{name}" + parent_qn = parent_folder_qualified_name + from pyatlan_v9.model.assets import Folder + + parent_ref = Folder.ref_by_qualified_name(parent_folder_qualified_name) + + return Query( + name=name, + qualified_name=qualified_name, + collection_qualified_name=collection_qualified_name, + parent=parent_ref, + parent_qualified_name=parent_qn, + ) + + @classmethod + def updater( + cls, + *, + name: str, + qualified_name: str, + collection_qualified_name: str, + parent_qualified_name: str, + ) -> "Query": + from pyatlan.utils import validate_required_fields + + validate_required_fields( + ["name", "collection_qualified_name", "parent_qualified_name"], + [name, collection_qualified_name, parent_qualified_name], + ) + if collection_qualified_name == parent_qualified_name: + from pyatlan_v9.model.assets import Collection + + parent = Collection.ref_by_qualified_name(collection_qualified_name) + else: + from pyatlan_v9.model.assets import Folder + + parent = Folder.ref_by_qualified_name(parent_qualified_name) + + return Query( + qualified_name=qualified_name, + name=name, + parent=parent, + collection_qualified_name=collection_qualified_name, + parent_qualified_name=parent_qualified_name, + ) + + def with_raw_query(self, schema_qualified_name: str, query: str): + from base64 import b64encode + from json import dumps + + from pyatlan.model.enums import AtlanConnectorType + + _DEFAULT_VARIABLE_SCHEMA = dumps( + { + "customvariablesDateTimeFormat": { + "defaultDateFormat": "YYYY-MM-DD", + "defaultTimeFormat": "HH:mm", + }, + "customVariables": [], + } + ) + connection_qn, connector_name = AtlanConnectorType.get_connector_name( + schema_qualified_name, "schema_qualified_name", 5 + ) + tokens = schema_qualified_name.split("/") + database_qn = f"{tokens[0]}/{tokens[1]}/{tokens[2]}/{tokens[3]}" + self.connection_name = connector_name + self.connection_qualified_name = connection_qn + self.default_database_qualified_name = database_qn + self.default_schema_qualified_name = schema_qualified_name + self.is_visual_query = False + self.raw_query_text = query + self.variables_schema_base64 = b64encode( + _DEFAULT_VARIABLE_SCHEMA.encode("utf-8") + ).decode("utf-8") diff --git a/pyatlan_v9/model/assets/_overlays/quick_sight_analysis.py b/pyatlan_v9/model/assets/_overlays/quick_sight_analysis.py new file mode 100644 index 000000000..fb9ce7f87 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/quick_sight_analysis.py @@ -0,0 +1,38 @@ +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + connection_qualified_name: str, + quick_sight_id: str, + quick_sight_analysis_folders: Union[list[str], None] = None, + ) -> "QuickSightAnalysis": + validate_required_fields( + ["name", "connection_qualified_name", "quick_sight_id"], + [name, connection_qualified_name, quick_sight_id], + ) + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + qualified_name = f"{connection_qualified_name}/{quick_sight_id}" + return cls( + name=name, + quick_sight_id=quick_sight_id, + qualified_name=qualified_name, + connection_qualified_name=connection_qualified_name, + connector_name=connector_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "QuickSightAnalysis": + """Create a QuickSightAnalysis instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "QuickSightAnalysis": + """Return only fields required for update operations.""" + return QuickSightAnalysis.updater( + qualified_name=self.qualified_name, name=self.name + ) diff --git a/pyatlan_v9/model/assets/_overlays/quick_sight_analysis_visual.py b/pyatlan_v9/model/assets/_overlays/quick_sight_analysis_visual.py new file mode 100644 index 000000000..7c80fba34 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/quick_sight_analysis_visual.py @@ -0,0 +1,64 @@ +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + quick_sight_id: str, + quick_sight_sheet_id: str, + quick_sight_sheet_name: str, + quick_sight_analysis_qualified_name: str, + connection_qualified_name: Union[str, None] = None, + ) -> "QuickSightAnalysisVisual": + validate_required_fields( + [ + "name", + "quick_sight_id", + "quick_sight_sheet_id", + "quick_sight_sheet_name", + "quick_sight_analysis_qualified_name", + ], + [ + name, + quick_sight_id, + quick_sight_sheet_id, + quick_sight_sheet_name, + quick_sight_analysis_qualified_name, + ], + ) + if connection_qualified_name: + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + else: + parts = quick_sight_analysis_qualified_name.split("/") + connector_name = parts[1] if len(parts) > 1 else None + connection_qualified_name = ( + "/".join(parts[:3]) + if len(parts) >= 3 + else quick_sight_analysis_qualified_name + ) + qualified_name = f"{quick_sight_analysis_qualified_name}/{quick_sight_sheet_id}/{quick_sight_id}" + return cls( + name=name, + quick_sight_id=quick_sight_id, + quick_sight_sheet_id=quick_sight_sheet_id, + quick_sight_sheet_name=quick_sight_sheet_name, + quick_sight_analysis_qualified_name=quick_sight_analysis_qualified_name, + qualified_name=qualified_name, + connection_qualified_name=connection_qualified_name, + connector_name=connector_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "QuickSightAnalysisVisual": + """Create a QuickSightAnalysisVisual instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "QuickSightAnalysisVisual": + """Return only fields required for update operations.""" + return QuickSightAnalysisVisual.updater( + qualified_name=self.qualified_name, name=self.name + ) diff --git a/pyatlan_v9/model/assets/_overlays/quick_sight_dashboard.py b/pyatlan_v9/model/assets/_overlays/quick_sight_dashboard.py new file mode 100644 index 000000000..0d54cc7d1 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/quick_sight_dashboard.py @@ -0,0 +1,47 @@ +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + connection_qualified_name: str, + quick_sight_id: str, + quick_sight_dashboard_folders: Union[list[str], None] = None, + ) -> "QuickSightDashboard": + """Create a new QuickSightDashboard asset.""" + validate_required_fields( + ["name", "connection_qualified_name", "quick_sight_id"], + [name, connection_qualified_name, quick_sight_id], + ) + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + folder_refs = ( + [ + RelatedQuickSightFolder(unique_attributes={"qualifiedName": folder_qn}) + for folder_qn in quick_sight_dashboard_folders + ] + if quick_sight_dashboard_folders + else UNSET + ) + return cls( + name=name, + quick_sight_id=quick_sight_id, + qualified_name=f"{connection_qualified_name}/{quick_sight_id}", + connection_qualified_name=connection_qualified_name, + connector_name=connector_name, + quick_sight_dashboard_folders=folder_refs, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "QuickSightDashboard": + """Create a QuickSightDashboard instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "QuickSightDashboard": + """Return only fields required for update operations.""" + return QuickSightDashboard.updater( + qualified_name=self.qualified_name, name=self.name + ) diff --git a/pyatlan_v9/model/assets/_overlays/quick_sight_dashboard_visual.py b/pyatlan_v9/model/assets/_overlays/quick_sight_dashboard_visual.py new file mode 100644 index 000000000..6029d0c82 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/quick_sight_dashboard_visual.py @@ -0,0 +1,64 @@ +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + quick_sight_id: str, + quick_sight_sheet_id: str, + quick_sight_sheet_name: str, + quick_sight_dashboard_qualified_name: str, + connection_qualified_name: Union[str, None] = None, + ) -> "QuickSightDashboardVisual": + validate_required_fields( + [ + "name", + "quick_sight_id", + "quick_sight_sheet_id", + "quick_sight_sheet_name", + "quick_sight_dashboard_qualified_name", + ], + [ + name, + quick_sight_id, + quick_sight_sheet_id, + quick_sight_sheet_name, + quick_sight_dashboard_qualified_name, + ], + ) + if connection_qualified_name: + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + else: + parts = quick_sight_dashboard_qualified_name.split("/") + connector_name = parts[1] if len(parts) > 1 else None + connection_qualified_name = ( + "/".join(parts[:3]) + if len(parts) >= 3 + else quick_sight_dashboard_qualified_name + ) + qualified_name = f"{quick_sight_dashboard_qualified_name}/{quick_sight_sheet_id}/{quick_sight_id}" + return cls( + name=name, + quick_sight_id=quick_sight_id, + quick_sight_sheet_id=quick_sight_sheet_id, + quick_sight_sheet_name=quick_sight_sheet_name, + quick_sight_dashboard_qualified_name=quick_sight_dashboard_qualified_name, + qualified_name=qualified_name, + connection_qualified_name=connection_qualified_name, + connector_name=connector_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "QuickSightDashboardVisual": + """Create a QuickSightDashboardVisual instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "QuickSightDashboardVisual": + """Return only fields required for update operations.""" + return QuickSightDashboardVisual.updater( + qualified_name=self.qualified_name, name=self.name + ) diff --git a/pyatlan_v9/model/assets/_overlays/quick_sight_dataset.py b/pyatlan_v9/model/assets/_overlays/quick_sight_dataset.py new file mode 100644 index 000000000..86d75faa9 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/quick_sight_dataset.py @@ -0,0 +1,51 @@ +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + connection_qualified_name: str, + quick_sight_id: str, + quick_sight_dataset_import_mode: Union[str, None] = None, + quick_sight_dataset_folders: Union[list[str], None] = None, + ) -> "QuickSightDataset": + """Create a new QuickSightDataset asset.""" + validate_required_fields( + ["name", "connection_qualified_name", "quick_sight_id"], + [name, connection_qualified_name, quick_sight_id], + ) + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + folder_refs = ( + [ + RelatedQuickSightFolder(unique_attributes={"qualifiedName": folder_qn}) + for folder_qn in quick_sight_dataset_folders + ] + if quick_sight_dataset_folders + else UNSET + ) + return cls( + name=name, + quick_sight_id=quick_sight_id, + qualified_name=f"{connection_qualified_name}/{quick_sight_id}", + connection_qualified_name=connection_qualified_name, + connector_name=connector_name, + quick_sight_dataset_import_mode=quick_sight_dataset_import_mode + if quick_sight_dataset_import_mode is not None + else UNSET, + quick_sight_dataset_folders=folder_refs, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "QuickSightDataset": + """Create a QuickSightDataset instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "QuickSightDataset": + """Return only fields required for update operations.""" + return QuickSightDataset.updater( + qualified_name=self.qualified_name, name=self.name + ) diff --git a/pyatlan_v9/model/assets/_overlays/quick_sight_dataset_field.py b/pyatlan_v9/model/assets/_overlays/quick_sight_dataset_field.py new file mode 100644 index 000000000..01f0c1b6b --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/quick_sight_dataset_field.py @@ -0,0 +1,56 @@ +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + quick_sight_dataset_qualified_name: str, + quick_sight_id: str, + quick_sight_dataset_field_type: Union[str, None] = None, + connection_qualified_name: Union[str, None] = None, + ) -> "QuickSightDatasetField": + """Create a new QuickSightDatasetField asset.""" + validate_required_fields( + ["name", "quick_sight_dataset_qualified_name", "quick_sight_id"], + [name, quick_sight_dataset_qualified_name, quick_sight_id], + ) + if connection_qualified_name: + connector_name = ( + connection_qualified_name.split("/")[1] + if len(connection_qualified_name.split("/")) > 1 + else "" + ) + else: + fields = quick_sight_dataset_qualified_name.split("/") + if len(fields) < 3: + raise ValueError("quick_sight_dataset_qualified_name is invalid") + connection_qualified_name = "/".join(fields[:3]) + connector_name = fields[1] + return cls( + name=name, + quick_sight_id=quick_sight_id, + quick_sight_dataset_qualified_name=quick_sight_dataset_qualified_name, + qualified_name=f"{quick_sight_dataset_qualified_name}/{quick_sight_id}", + connection_qualified_name=connection_qualified_name, + connector_name=connector_name, + quick_sight_dataset_field_type=quick_sight_dataset_field_type + if quick_sight_dataset_field_type is not None + else UNSET, + quick_sight_dataset=RelatedQuickSightDataset( + unique_attributes={"qualifiedName": quick_sight_dataset_qualified_name} + ), + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "QuickSightDatasetField": + """Create a QuickSightDatasetField instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "QuickSightDatasetField": + """Return only fields required for update operations.""" + return QuickSightDatasetField.updater( + qualified_name=self.qualified_name, name=self.name + ) diff --git a/pyatlan_v9/model/assets/_overlays/quick_sight_folder.py b/pyatlan_v9/model/assets/_overlays/quick_sight_folder.py new file mode 100644 index 000000000..f66a1b48f --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/quick_sight_folder.py @@ -0,0 +1,41 @@ +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + connection_qualified_name: str, + quick_sight_id: str, + quick_sight_folder_type: Union[str, None] = None, + ) -> "QuickSightFolder": + validate_required_fields( + ["name", "connection_qualified_name", "quick_sight_id"], + [name, connection_qualified_name, quick_sight_id], + ) + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + qualified_name = f"{connection_qualified_name}/{quick_sight_id}" + return cls( + name=name, + quick_sight_id=quick_sight_id, + qualified_name=qualified_name, + connection_qualified_name=connection_qualified_name, + connector_name=connector_name, + quick_sight_folder_type=quick_sight_folder_type + if quick_sight_folder_type is not None + else UNSET, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "QuickSightFolder": + """Create a QuickSightFolder instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "QuickSightFolder": + """Return only fields required for update operations.""" + return QuickSightFolder.updater( + qualified_name=self.qualified_name, name=self.name + ) diff --git a/pyatlan_v9/model/assets/_overlays/readme.py b/pyatlan_v9/model/assets/_overlays/readme.py new file mode 100644 index 000000000..58d308550 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/readme.py @@ -0,0 +1,70 @@ +# STDLIB_IMPORT: from urllib.parse import quote, unquote +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @property + def description(self) -> Union[str, None, UnsetType]: + """Decode URL-encoded description content for parity with legacy models.""" + if self.user_description is not UNSET: + return ( + unquote(self.user_description) + if self.user_description is not None + else None + ) + if self.asset_source_readme is not UNSET: + return ( + unquote(self.asset_source_readme) + if self.asset_source_readme is not None + else None + ) + return UNSET + + @description.setter + def description(self, description: Union[str, None, UnsetType]) -> None: + """Store README content in user_description with URL encoding.""" + if description is UNSET: + self.user_description = UNSET + return + self.user_description = quote(description) if description is not None else None + + @classmethod + @init_guid + def creator( + cls, + *, + asset: Asset, + content: str, + asset_name: Union[str, None] = None, + ) -> "Readme": + """Create a new Readme asset.""" + validate_required_fields(["asset", "content"], [asset, content]) + actual_asset_name = asset.name if asset.name is not UNSET else None + if actual_asset_name: + if asset_name: + raise ValueError( + "asset_name can not be given when name is available from asset" + ) + asset_name = actual_asset_name + elif not asset_name: + raise ValueError( + "asset_name is required when name is not available from asset" + ) + if asset.guid is UNSET or not asset.guid: + raise ValueError( + "asset guid must be present, use the client.asset.ref_by_guid() method to retrieve an asset by its GUID" + ) + return cls( + qualified_name=f"{asset.guid}/readme", + name=f"{asset_name} Readme", + asset=RelatedAsset(guid=asset.guid), + user_description=quote(content), + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "Readme": + """Create a Readme instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "Readme": + """Return only fields required for update operations.""" + return Readme.updater(qualified_name=self.qualified_name, name=self.name) diff --git a/pyatlan_v9/model/assets/_overlays/referenceable.py b/pyatlan_v9/model/assets/_overlays/referenceable.py new file mode 100644 index 000000000..61688f3e1 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/referenceable.py @@ -0,0 +1,24 @@ +# INTERNAL_IMPORT: from pyatlan.model.lineage_ref import LineageRef + + @classmethod + def can_be_archived(cls) -> bool: + """ + Indicates if an asset can be archived via the asset.delete_by_guid method. + :returns: True if archiving is supported + """ + return True + + @property + def assigned_terms(self): + """ + Get assigned glossary terms (maps to Entity.meanings). + + In legacy models, assigned_terms was a property that mapped to + attributes.meanings. In v9, meanings is a direct field on Entity. + """ + return self.meanings if self.meanings is not UNSET else None + + @assigned_terms.setter + def assigned_terms(self, value): + """Set assigned glossary terms.""" + self.meanings = value diff --git a/pyatlan_v9/model/assets/_overlays/s3_bucket.py b/pyatlan_v9/model/assets/_overlays/s3_bucket.py new file mode 100644 index 000000000..861eb8629 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/s3_bucket.py @@ -0,0 +1,55 @@ +# INTERNAL_IMPORT: from pyatlan.utils import init_guid + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + connection_qualified_name: str, + aws_arn: str | None = None, + ) -> "S3Bucket": + """ + Create a new S3Bucket asset. + + Args: + name: Name of the bucket + connection_qualified_name: Unique name of the connection in which this bucket exists + aws_arn: Amazon Resource Name (ARN) for the bucket (optional) + + Returns: + S3Bucket instance ready to be created + + Raises: + ValueError: If required parameters are missing or invalid + """ + if name is None: + raise ValueError("name is required") + if connection_qualified_name is None: + raise ValueError("connection_qualified_name is required") + + if name.strip() == "": + raise ValueError("name cannot be blank") + if connection_qualified_name.strip() == "": + raise ValueError("connection_qualified_name cannot be blank") + + fields = connection_qualified_name.split("/") + if len(fields) != 3: + raise ValueError("Invalid connection_qualified_name") + + if fields[0].replace(" ", "") == "" or fields[2].replace(" ", "") == "": + raise ValueError("Invalid connection_qualified_name") + + if fields[1].lower() != "s3": + raise ValueError("Invalid connection_qualified_name") + + connector_name = fields[1] + qualified_name = f"{connection_qualified_name}/{aws_arn if aws_arn else name}" + + return cls( + name=name, + qualified_name=qualified_name, + connection_qualified_name=connection_qualified_name, + connector_name=connector_name, + aws_arn=aws_arn, + ) diff --git a/pyatlan_v9/model/assets/_overlays/s3_object.py b/pyatlan_v9/model/assets/_overlays/s3_object.py new file mode 100644 index 000000000..c7492799d --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/s3_object.py @@ -0,0 +1,152 @@ +# IMPORT: from pyatlan.model.utils import construct_object_key +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + connection_qualified_name: str, + aws_arn: str, + s3_bucket_name: str, + s3_bucket_qualified_name: str, + ) -> "S3Object": + """ + Create a new S3Object asset with an AWS ARN. + + Args: + name: Name of the object + connection_qualified_name: Unique name of the connection + aws_arn: Amazon Resource Name (ARN) for the object + s3_bucket_name: Simple name of the bucket + s3_bucket_qualified_name: Unique name of the bucket + + Returns: + S3Object instance ready to be created + + Raises: + ValueError: If required parameters are missing or invalid + """ + validate_required_fields( + [ + "name", + "connection_qualified_name", + "aws_arn", + "s3_bucket_name", + "s3_bucket_qualified_name", + ], + [ + name, + connection_qualified_name, + aws_arn, + s3_bucket_name, + s3_bucket_qualified_name, + ], + ) + fields = connection_qualified_name.split("/") + if len(fields) != 3: + raise ValueError("Invalid connection_qualified_name") + if fields[0].replace(" ", "") == "" or fields[2].replace(" ", "") == "": + raise ValueError("Invalid connection_qualified_name") + if fields[1].lower() != "s3": + raise ValueError("Invalid connection_qualified_name") + + connector_name = fields[1] + return cls( + name=name, + connection_qualified_name=connection_qualified_name, + qualified_name=f"{connection_qualified_name}/{aws_arn}", + connector_name=connector_name, + aws_arn=aws_arn, + s3_bucket_name=s3_bucket_name, + s3_bucket_qualified_name=s3_bucket_qualified_name, + ) + + @classmethod + @init_guid + def creator_with_prefix( + cls, + *, + name: str, + connection_qualified_name: str, + s3_bucket_name: str, + s3_bucket_qualified_name: str, + prefix: str = "", + ) -> "S3Object": + """ + Create a new S3Object asset using a prefix-based object key. + + Args: + name: Name of the object + connection_qualified_name: Unique name of the connection + s3_bucket_name: Simple name of the bucket + s3_bucket_qualified_name: Unique name of the bucket + prefix: Prefix (folder path) for the object + + Returns: + S3Object instance ready to be created + + Raises: + ValueError: If required parameters are missing or invalid + """ + validate_required_fields( + [ + "name", + "connection_qualified_name", + "s3_bucket_name", + "s3_bucket_qualified_name", + ], + [ + name, + connection_qualified_name, + s3_bucket_name, + s3_bucket_qualified_name, + ], + ) + fields = connection_qualified_name.split("/") + if len(fields) != 3: + raise ValueError("Invalid connection_qualified_name") + if fields[0].replace(" ", "") == "" or fields[2].replace(" ", "") == "": + raise ValueError("Invalid connection_qualified_name") + if fields[1].lower() != "s3": + raise ValueError("Invalid connection_qualified_name") + + connector_name = fields[1] + object_key = construct_object_key(prefix, name) + return cls( + name=name, + s3_object_key=object_key, + connection_qualified_name=connection_qualified_name, + qualified_name=f"{connection_qualified_name}/{s3_bucket_name}/{object_key}", + connector_name=connector_name, + s3_bucket_name=s3_bucket_name, + s3_bucket_qualified_name=s3_bucket_qualified_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "S3Object": + """ + Create an S3Object instance for modification. + + Args: + qualified_name: Unique name of the S3Object to update + name: Human-readable name of the S3Object + + Returns: + S3Object instance ready for update + + Raises: + ValueError: If required parameters are missing + """ + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "S3Object": + """ + Return a copy of this S3Object with only the minimum required fields for update. + + Returns: + S3Object with only qualified_name and name set + """ + return S3Object.updater(qualified_name=self.qualified_name, name=self.name) diff --git a/pyatlan_v9/model/assets/_overlays/schema.py b/pyatlan_v9/model/assets/_overlays/schema.py new file mode 100644 index 000000000..77aeed470 --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/schema.py @@ -0,0 +1,93 @@ +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + database_qualified_name: str, + database_name: str | None = None, + connection_qualified_name: str | None = None, + ) -> "Schema": + """ + Create a new Schema asset with auto-derived fields. + + Args: + name: Simple name of the schema + database_qualified_name: Unique name of the database in which this schema exists + database_name: Simple name of the database (auto-derived if not provided) + connection_qualified_name: Unique name of the connection (auto-derived if not provided) + + Returns: + New Schema instance with all fields populated + + Raises: + ValueError: If required parameters are missing or invalid + """ + validate_required_fields( + ["name", "database_qualified_name"], [name, database_qualified_name] + ) + + # Validate database_qualified_name format: default/connector/connection_id/database + fields = database_qualified_name.split("/") + if len(fields) != 4: + raise ValueError( + f"Invalid database_qualified_name: {database_qualified_name}. " + "Expected format: default/connector/connection_id/database" + ) + + # Derive other fields from database_qualified_name + connector_name = fields[1] + connection_qn = ( + connection_qualified_name or f"{fields[0]}/{fields[1]}/{fields[2]}" + ) + db_name = database_name or fields[3] + qualified_name = f"{database_qualified_name}/{name}" + + return cls( + name=name, + qualified_name=qualified_name, + database_name=db_name, + database_qualified_name=database_qualified_name, + connector_name=connector_name, + connection_qualified_name=connection_qn, + database=RelatedDatabase(qualified_name=database_qualified_name), + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "Schema": + """ + Create a Schema instance for updating an existing asset. + + Args: + qualified_name: Unique name of the schema to update + name: Simple name of the schema + + Returns: + Schema instance configured for updates + + Raises: + ValueError: If required parameters are missing + """ + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "Schema": + """ + Return a Schema with only required fields for reference. + + Returns: + Schema instance with only qualified_name and name set + """ + return Schema(qualified_name=self.qualified_name, name=self.name) + + @classmethod + def create(cls, **kwargs) -> "Schema": + """Backward compatibility alias for creator().""" + return cls.creator(**kwargs) + + @classmethod + def create_for_modification(cls, **kwargs) -> "Schema": + """Backward compatibility alias for updater().""" + return cls.updater(**kwargs) diff --git a/pyatlan_v9/model/assets/_overlays/superset_chart.py b/pyatlan_v9/model/assets/_overlays/superset_chart.py new file mode 100644 index 000000000..ce0734dfa --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/superset_chart.py @@ -0,0 +1,47 @@ +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + superset_dashboard_qualified_name: str, + connection_qualified_name: Union[str, None] = None, + ) -> "SupersetChart": + """Create a new SupersetChart asset.""" + validate_required_fields( + ["name", "superset_dashboard_qualified_name"], + [name, superset_dashboard_qualified_name], + ) + if connection_qualified_name: + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + else: + fields = superset_dashboard_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + connection_qualified_name = ( + "/".join(fields[:3]) + if len(fields) >= 3 + else superset_dashboard_qualified_name + ) + return cls( + name=name, + superset_dashboard_qualified_name=superset_dashboard_qualified_name, + connection_qualified_name=connection_qualified_name, + qualified_name=f"{superset_dashboard_qualified_name}/{name}", + connector_name=connector_name, + superset_dashboard=RelatedSupersetDashboard( + unique_attributes={"qualifiedName": superset_dashboard_qualified_name} + ), + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "SupersetChart": + """Create a SupersetChart instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "SupersetChart": + """Return only fields required for update operations.""" + return SupersetChart.updater(qualified_name=self.qualified_name, name=self.name) diff --git a/pyatlan_v9/model/assets/_overlays/superset_dashboard.py b/pyatlan_v9/model/assets/_overlays/superset_dashboard.py new file mode 100644 index 000000000..1d6bbc68a --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/superset_dashboard.py @@ -0,0 +1,31 @@ +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, *, name: str, connection_qualified_name: str + ) -> "SupersetDashboard": + """Create a new SupersetDashboard asset.""" + validate_required_fields( + ["name", "connection_qualified_name"], [name, connection_qualified_name] + ) + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + return cls( + name=name, + qualified_name=f"{connection_qualified_name}/{name}", + connection_qualified_name=connection_qualified_name, + connector_name=connector_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "SupersetDashboard": + """Create a SupersetDashboard instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "SupersetDashboard": + """Return only fields required for update operations.""" + return SupersetDashboard.updater( + qualified_name=self.qualified_name, name=self.name + ) diff --git a/pyatlan_v9/model/assets/_overlays/superset_dataset.py b/pyatlan_v9/model/assets/_overlays/superset_dataset.py new file mode 100644 index 000000000..c0812e22d --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/superset_dataset.py @@ -0,0 +1,49 @@ +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + superset_dashboard_qualified_name: str, + connection_qualified_name: Union[str, None] = None, + ) -> "SupersetDataset": + """Create a new SupersetDataset asset.""" + validate_required_fields( + ["name", "superset_dashboard_qualified_name"], + [name, superset_dashboard_qualified_name], + ) + if connection_qualified_name: + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + else: + fields = superset_dashboard_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + connection_qualified_name = ( + "/".join(fields[:3]) + if len(fields) >= 3 + else superset_dashboard_qualified_name + ) + return cls( + name=name, + superset_dashboard_qualified_name=superset_dashboard_qualified_name, + connection_qualified_name=connection_qualified_name, + qualified_name=f"{superset_dashboard_qualified_name}/{name}", + connector_name=connector_name, + superset_dashboard=RelatedSupersetDashboard( + unique_attributes={"qualifiedName": superset_dashboard_qualified_name} + ), + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "SupersetDataset": + """Create a SupersetDataset instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "SupersetDataset": + """Return only fields required for update operations.""" + return SupersetDataset.updater( + qualified_name=self.qualified_name, name=self.name + ) diff --git a/pyatlan_v9/model/assets/_overlays/table.py b/pyatlan_v9/model/assets/_overlays/table.py new file mode 100644 index 000000000..9dd9a77db --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/table.py @@ -0,0 +1,76 @@ +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + schema_qualified_name: str, + schema_name: str | None = None, + database_name: str | None = None, + database_qualified_name: str | None = None, + connection_qualified_name: str | None = None, + ) -> "Table": + """ + Create a new Table asset. + + Args: + name: Name of the table + schema_qualified_name: Unique name of the schema in which this table exists + schema_name: Simple name of the schema (optional, will be derived if not provided) + database_name: Simple name of the database (optional, will be derived if not provided) + database_qualified_name: Unique name of the database (optional, will be derived if not provided) + connection_qualified_name: Unique name of the connection (optional, will be derived if not provided) + + Returns: + Table instance ready to be created + + Raises: + ValueError: If required parameters are missing or invalid + """ + validate_required_fields( + ["name", "schema_qualified_name"], [name, schema_qualified_name] + ) + + fields = schema_qualified_name.split("/") + if len(fields) != 5: + raise ValueError( + f"Invalid schema_qualified_name: {schema_qualified_name}. " + "Expected format: default/connector/connection_id/database/schema" + ) + + connector_name = fields[1] + connection_qn = ( + connection_qualified_name or f"{fields[0]}/{fields[1]}/{fields[2]}" + ) + db_name = database_name or fields[3] + sch_name = schema_name or fields[4] + db_qualified_name = database_qualified_name or f"{connection_qn}/{db_name}" + qualified_name = f"{schema_qualified_name}/{name}" + + return cls( + name=name, + qualified_name=qualified_name, + database_name=db_name, + database_qualified_name=db_qualified_name, + schema_name=sch_name, + schema_qualified_name=schema_qualified_name, + connector_name=connector_name, + connection_qualified_name=connection_qn, + atlan_schema=RelatedSchema(qualified_name=schema_qualified_name), + ) + + @classmethod + def create(cls, *, name: str, schema_qualified_name: str) -> "Table": + """ + Create a new Table asset (deprecated - use creator instead). + + Args: + name: Name of the table + schema_qualified_name: Unique name of the schema in which this table exists + + Returns: + Table instance ready to be created + """ + return cls.creator(name=name, schema_qualified_name=schema_qualified_name) diff --git a/pyatlan_v9/model/assets/_overlays/table_partition.py b/pyatlan_v9/model/assets/_overlays/table_partition.py new file mode 100644 index 000000000..fd3721b0f --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/table_partition.py @@ -0,0 +1,107 @@ +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + table_qualified_name: str, + table_name: str | None = None, + schema_name: str | None = None, + schema_qualified_name: str | None = None, + database_name: str | None = None, + database_qualified_name: str | None = None, + connection_qualified_name: str | None = None, + ) -> "TablePartition": + """ + Create a new TablePartition asset with auto-derived fields. + + Args: + name: Simple name of the table partition + table_qualified_name: Unique name of the table in which this partition exists + table_name: Simple name of the table (auto-derived if not provided) + schema_name: Simple name of the schema (auto-derived if not provided) + schema_qualified_name: Unique name of the schema (auto-derived if not provided) + database_name: Simple name of the database (auto-derived if not provided) + database_qualified_name: Unique name of the database (auto-derived if not provided) + connection_qualified_name: Unique name of the connection (auto-derived if not provided) + + Returns: + New TablePartition instance with all fields populated + + Raises: + ValueError: If required parameters are missing or invalid + """ + validate_required_fields( + ["name", "table_qualified_name"], [name, table_qualified_name] + ) + + fields = table_qualified_name.split("/") + if len(fields) != 6: + raise ValueError( + f"Invalid table_qualified_name: {table_qualified_name}. " + "Expected format: default/connector/connection_id/database/schema/table" + ) + + connector_name = fields[1] + connection_qn = ( + connection_qualified_name or f"{fields[0]}/{fields[1]}/{fields[2]}" + ) + db_name = database_name or fields[3] + sch_name = schema_name or fields[4] + tbl_name = table_name or fields[5] + db_qualified_name = database_qualified_name or f"{connection_qn}/{db_name}" + sch_qualified_name = schema_qualified_name or f"{db_qualified_name}/{sch_name}" + qualified_name = f"{sch_qualified_name}/{name}" + + return cls( + name=name, + qualified_name=qualified_name, + table_name=tbl_name, + table_qualified_name=table_qualified_name, + schema_name=sch_name, + schema_qualified_name=sch_qualified_name, + database_name=db_name, + database_qualified_name=db_qualified_name, + connector_name=connector_name, + connection_qualified_name=connection_qn, + parent_table=RelatedTable(qualified_name=table_qualified_name), + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "TablePartition": + """ + Create a TablePartition instance for updating an existing asset. + + Args: + qualified_name: Unique name of the table partition to update + name: Simple name of the table partition + + Returns: + TablePartition instance configured for updates + + Raises: + ValueError: If required parameters are missing + """ + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "TablePartition": + """ + Return a TablePartition with only required fields for reference. + + Returns: + TablePartition instance with only qualified_name and name set + """ + return TablePartition(qualified_name=self.qualified_name, name=self.name) + + @classmethod + def create(cls, **kwargs) -> "TablePartition": + """Backward compatibility alias for creator().""" + return cls.creator(**kwargs) + + @classmethod + def create_for_modification(cls, **kwargs) -> "TablePartition": + """Backward compatibility alias for updater().""" + return cls.updater(**kwargs) diff --git a/pyatlan_v9/model/assets/_overlays/view.py b/pyatlan_v9/model/assets/_overlays/view.py new file mode 100644 index 000000000..794fed31a --- /dev/null +++ b/pyatlan_v9/model/assets/_overlays/view.py @@ -0,0 +1,101 @@ +# INTERNAL_IMPORT: from pyatlan.utils import init_guid, validate_required_fields + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + schema_qualified_name: str, + schema_name: str | None = None, + database_name: str | None = None, + database_qualified_name: str | None = None, + connection_qualified_name: str | None = None, + ) -> "View": + """ + Create a new View asset with auto-derived fields. + + Args: + name: Simple name of the view + schema_qualified_name: Unique name of the schema in which this view exists + schema_name: Simple name of the schema (auto-derived if not provided) + database_name: Simple name of the database (auto-derived if not provided) + database_qualified_name: Unique name of the database (auto-derived if not provided) + connection_qualified_name: Unique name of the connection (auto-derived if not provided) + + Returns: + New View instance with all fields populated + + Raises: + ValueError: If required parameters are missing or invalid + """ + validate_required_fields( + ["name", "schema_qualified_name"], [name, schema_qualified_name] + ) + + # Validate schema_qualified_name format: default/connector/connection_id/database/schema + fields = schema_qualified_name.split("/") + if len(fields) != 5: + raise ValueError( + f"Invalid schema_qualified_name: {schema_qualified_name}. " + "Expected format: default/connector/connection_id/database/schema" + ) + + # Derive other fields from schema_qualified_name + connector_name = fields[1] + connection_qn = ( + connection_qualified_name or f"{fields[0]}/{fields[1]}/{fields[2]}" + ) + db_name = database_name or fields[3] + sch_name = schema_name or fields[4] + db_qualified_name = database_qualified_name or f"{connection_qn}/{db_name}" + qualified_name = f"{schema_qualified_name}/{name}" + + return cls( + name=name, + qualified_name=qualified_name, + database_name=db_name, + database_qualified_name=db_qualified_name, + schema_name=sch_name, + schema_qualified_name=schema_qualified_name, + connector_name=connector_name, + connection_qualified_name=connection_qn, + atlan_schema=RelatedSchema(qualified_name=schema_qualified_name), + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "View": + """ + Create a View instance for updating an existing asset. + + Args: + qualified_name: Unique name of the view to update + name: Simple name of the view + + Returns: + View instance configured for updates + + Raises: + ValueError: If required parameters are missing + """ + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "View": + """ + Return a View with only required fields for reference. + + Returns: + View instance with only qualified_name and name set + """ + return View(qualified_name=self.qualified_name, name=self.name) + + @classmethod + def create(cls, **kwargs) -> "View": + """Backward compatibility alias for creator().""" + return cls.creator(**kwargs) + + @classmethod + def create_for_modification(cls, **kwargs) -> "View": + """Backward compatibility alias for updater().""" + return cls.updater(**kwargs) diff --git a/pyatlan_v9/model/assets/access_control.py b/pyatlan_v9/model/assets/access_control.py new file mode 100644 index 000000000..f0eb6a2a5 --- /dev/null +++ b/pyatlan_v9/model/assets/access_control.py @@ -0,0 +1,182 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2026 Atlan Pte. Ltd. + +"""AccessControl asset model for pyatlan_v9.""" + +from __future__ import annotations + +from typing import Any, ClassVar, Set, Union + +from msgspec import UNSET, UnsetType + +from pyatlan_v9.model.conversion_utils import ( + build_attributes_kwargs, + build_flat_kwargs, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .asset import Asset, AssetAttributes, AssetNested +from .auth_policy import AuthPolicy + + +@register_asset +class AccessControl(Asset): + """AccessControl asset — base type for Persona and Purpose access policies.""" + + IS_ACCESS_CONTROL_ENABLED: ClassVar[Any] = None + DENY_SIDEBAR_TABS: ClassVar[Any] = None + DENY_CUSTOM_METADATA_GUIDS: ClassVar[Any] = None + DENY_ASSET_METADATA_TYPES: ClassVar[Any] = None + DENY_ASSET_TABS: ClassVar[Any] = None + DENY_ASSET_FILTERS: ClassVar[Any] = None + CHANNEL_LINK: ClassVar[Any] = None + DENY_ASSET_TYPES: ClassVar[Any] = None + DENY_NAVIGATION_PAGES: ClassVar[Any] = None + DEFAULT_NAVIGATION: ClassVar[Any] = None + DISPLAY_PREFERENCES: ClassVar[Any] = None + POLICIES: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "AccessControl" + is_access_control_enabled: Union[bool, None, UnsetType] = UNSET + deny_sidebar_tabs: Union[Set[str], None, UnsetType] = UNSET + deny_custom_metadata_guids: Union[Set[str], None, UnsetType] = UNSET + deny_asset_metadata_types: Union[Set[str], None, UnsetType] = UNSET + deny_asset_tabs: Union[Set[str], None, UnsetType] = UNSET + deny_asset_filters: Union[Set[str], None, UnsetType] = UNSET + channel_link: Union[str, None, UnsetType] = UNSET + deny_asset_types: Union[Set[str], None, UnsetType] = UNSET + deny_navigation_pages: Union[Set[str], None, UnsetType] = UNSET + default_navigation: Union[str, None, UnsetType] = UNSET + display_preferences: Union[Set[str], None, UnsetType] = UNSET + policies: Union[list[AuthPolicy], None, UnsetType] = UNSET + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + if serde is None: + serde = get_serde() + if nested: + return _access_control_to_nested_bytes(self, serde).decode("utf-8") + return serde.encode(self).decode("utf-8") + + @staticmethod + def from_json( + json_data: Union[str, bytes], serde: Serde | None = None + ) -> "AccessControl": + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _access_control_from_nested_bytes(json_data, serde) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( + BooleanField, + KeywordField, + RelationField, + TextField, +) + +AccessControl.IS_ACCESS_CONTROL_ENABLED = BooleanField( + "isAccessControlEnabled", "isAccessControlEnabled" +) +AccessControl.DENY_SIDEBAR_TABS = KeywordField("denySidebarTabs", "denySidebarTabs") +AccessControl.DENY_CUSTOM_METADATA_GUIDS = KeywordField( + "denyCustomMetadataGuids", "denyCustomMetadataGuids" +) +AccessControl.DENY_ASSET_METADATA_TYPES = KeywordField( + "denyAssetMetadataTypes", "denyAssetMetadataTypes" +) +AccessControl.DENY_ASSET_TABS = KeywordField("denyAssetTabs", "denyAssetTabs") +AccessControl.DENY_ASSET_FILTERS = TextField("denyAssetFilters", "denyAssetFilters") +AccessControl.CHANNEL_LINK = TextField("channelLink", "channelLink") +AccessControl.DENY_ASSET_TYPES = TextField("denyAssetTypes", "denyAssetTypes") +AccessControl.DENY_NAVIGATION_PAGES = TextField( + "denyNavigationPages", "denyNavigationPages" +) +AccessControl.DEFAULT_NAVIGATION = TextField("defaultNavigation", "defaultNavigation") +AccessControl.DISPLAY_PREFERENCES = KeywordField( + "displayPreferences", "displayPreferences" +) +AccessControl.POLICIES = RelationField("policies") + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class AccessControlAttributes(AssetAttributes): + is_access_control_enabled: Union[bool, None, UnsetType] = UNSET + deny_sidebar_tabs: Union[Set[str], None, UnsetType] = UNSET + deny_custom_metadata_guids: Union[Set[str], None, UnsetType] = UNSET + deny_asset_metadata_types: Union[Set[str], None, UnsetType] = UNSET + deny_asset_tabs: Union[Set[str], None, UnsetType] = UNSET + deny_asset_filters: Union[Set[str], None, UnsetType] = UNSET + channel_link: Union[str, None, UnsetType] = UNSET + deny_asset_types: Union[Set[str], None, UnsetType] = UNSET + deny_navigation_pages: Union[Set[str], None, UnsetType] = UNSET + default_navigation: Union[str, None, UnsetType] = UNSET + display_preferences: Union[Set[str], None, UnsetType] = UNSET + + +class AccessControlNested(AssetNested): + attributes: Union[AccessControlAttributes, UnsetType] = UNSET + + +def _access_control_to_nested(ac: AccessControl) -> AccessControlNested: + attrs_kwargs = build_attributes_kwargs(ac, AccessControlAttributes) + attrs = AccessControlAttributes(**attrs_kwargs) + return AccessControlNested( + guid=ac.guid, + type_name=ac.type_name, + status=ac.status, + version=ac.version, + create_time=ac.create_time, + update_time=ac.update_time, + created_by=ac.created_by, + updated_by=ac.updated_by, + classifications=ac.classifications, + classification_names=ac.classification_names, + meanings=ac.meanings, + labels=ac.labels, + business_attributes=ac.business_attributes, + custom_attributes=ac.custom_attributes, + pending_tasks=ac.pending_tasks, + proxy=ac.proxy, + is_incomplete=ac.is_incomplete, + provenance_type=ac.provenance_type, + home_id=ac.home_id, + attributes=attrs, + ) + + +def _access_control_from_nested(nested: AccessControlNested) -> AccessControl: + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else AccessControlAttributes() + ) + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + [], + object, + ) + kwargs = build_flat_kwargs( + nested, attrs, merged_rels, AssetNested, AccessControlAttributes + ) + return AccessControl(**kwargs) + + +def _access_control_to_nested_bytes(ac: AccessControl, serde: Serde) -> bytes: + return serde.encode(_access_control_to_nested(ac)) + + +def _access_control_from_nested_bytes(data: bytes, serde: Serde) -> AccessControl: + nested = serde.decode(data, AccessControlNested) + return _access_control_from_nested(nested) diff --git a/pyatlan_v9/model/assets/adf.py b/pyatlan_v9/model/assets/adf.py new file mode 100644 index 000000000..a448f5838 --- /dev/null +++ b/pyatlan_v9/model/assets/adf.py @@ -0,0 +1,541 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +ADF asset model with flattened inheritance. + +This module provides: +- ADF: Flat asset class (easy to use) +- ADFAttributes: Nested attributes struct (extends AssetAttributes) +- ADFNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class ADF(Asset): + """ + Base class for ADF assets. + """ + + ADF_FACTORY_NAME: ClassVar[Any] = None + ADF_ASSET_FOLDER_PATH: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "ADF" + + adf_factory_name: Union[str, None, UnsetType] = UNSET + """Defines the name of the factory in which this asset exists.""" + + adf_asset_folder_path: Union[str, None, UnsetType] = UNSET + """Defines the folder path in which this ADF asset exists.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "ADF" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _adf_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> ADF: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + ADF instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _adf_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class ADFAttributes(AssetAttributes): + """ADF-specific attributes for nested API format.""" + + adf_factory_name: Union[str, None, UnsetType] = UNSET + """Defines the name of the factory in which this asset exists.""" + + adf_asset_folder_path: Union[str, None, UnsetType] = UNSET + """Defines the folder path in which this ADF asset exists.""" + + +class ADFRelationshipAttributes(AssetRelationshipAttributes): + """ADF-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class ADFNested(AssetNested): + """ADF in nested API format for high-performance serialization.""" + + attributes: Union[ADFAttributes, UnsetType] = UNSET + relationship_attributes: Union[ADFRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ADFRelationshipAttributes, UnsetType] = UNSET + remove_relationship_attributes: Union[ADFRelationshipAttributes, UnsetType] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_ADF_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_adf_attrs(attrs: ADFAttributes, obj: ADF) -> None: + """Populate ADF-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.adf_factory_name = obj.adf_factory_name + attrs.adf_asset_folder_path = obj.adf_asset_folder_path + + +def _extract_adf_attrs(attrs: ADFAttributes) -> dict: + """Extract all ADF attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["adf_factory_name"] = attrs.adf_factory_name + result["adf_asset_folder_path"] = attrs.adf_asset_folder_path + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _adf_to_nested(adf: ADF) -> ADFNested: + """Convert flat ADF to nested format.""" + attrs = ADFAttributes() + _populate_adf_attrs(attrs, adf) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + adf, _ADF_REL_FIELDS, ADFRelationshipAttributes + ) + return ADFNested( + guid=adf.guid, + type_name=adf.type_name, + status=adf.status, + version=adf.version, + create_time=adf.create_time, + update_time=adf.update_time, + created_by=adf.created_by, + updated_by=adf.updated_by, + classifications=adf.classifications, + classification_names=adf.classification_names, + meanings=adf.meanings, + labels=adf.labels, + business_attributes=adf.business_attributes, + custom_attributes=adf.custom_attributes, + pending_tasks=adf.pending_tasks, + proxy=adf.proxy, + is_incomplete=adf.is_incomplete, + provenance_type=adf.provenance_type, + home_id=adf.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _adf_from_nested(nested: ADFNested) -> ADF: + """Convert nested format to flat ADF.""" + attrs = nested.attributes if nested.attributes is not UNSET else ADFAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _ADF_REL_FIELDS, + ADFRelationshipAttributes, + ) + return ADF( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_adf_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _adf_to_nested_bytes(adf: ADF, serde: Serde) -> bytes: + """Convert flat ADF to nested JSON bytes.""" + return serde.encode(_adf_to_nested(adf)) + + +def _adf_from_nested_bytes(data: bytes, serde: Serde) -> ADF: + """Convert nested JSON bytes to flat ADF.""" + nested = serde.decode(data, ADFNested) + return _adf_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +ADF.ADF_FACTORY_NAME = KeywordField("adfFactoryName", "adfFactoryName") +ADF.ADF_ASSET_FOLDER_PATH = KeywordField("adfAssetFolderPath", "adfAssetFolderPath") +ADF.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +ADF.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +ADF.ANOMALO_CHECKS = RelationField("anomaloChecks") +ADF.APPLICATION = RelationField("application") +ADF.APPLICATION_FIELD = RelationField("applicationField") +ADF.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +ADF.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +ADF.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +ADF.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +ADF.METRICS = RelationField("metrics") +ADF.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +ADF.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +ADF.MEANINGS = RelationField("meanings") +ADF.MC_MONITORS = RelationField("mcMonitors") +ADF.MC_INCIDENTS = RelationField("mcIncidents") +ADF.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +ADF.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +ADF.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +ADF.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +ADF.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +ADF.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +ADF.FILES = RelationField("files") +ADF.LINKS = RelationField("links") +ADF.README = RelationField("readme") +ADF.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +ADF.SODA_CHECKS = RelationField("sodaChecks") +ADF.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +ADF.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/adf_activity.py b/pyatlan_v9/model/assets/adf_activity.py new file mode 100644 index 000000000..ba393fb99 --- /dev/null +++ b/pyatlan_v9/model/assets/adf_activity.py @@ -0,0 +1,839 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +AdfActivity asset model with flattened inheritance. + +This module provides: +- AdfActivity: Flat asset class (easy to use) +- AdfActivityAttributes: Nested attributes struct (extends AssetAttributes) +- AdfActivityNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .adf_related import ( + RelatedAdfDataflow, + RelatedAdfDataset, + RelatedAdfLinkedservice, + RelatedAdfPipeline, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class AdfActivity(Asset): + """ + Base class for ADF Activities. It is a processing or transformation step that performs a specific task within a pipeline to manipulate or move data + """ + + ADF_ACTIVITY_TYPE: ClassVar[Any] = None + ADF_ACTIVITY_PRECEDING_DEPENDENCY: ClassVar[Any] = None + ADF_ACTIVITY_POLICY_TIMEOUT: ClassVar[Any] = None + ADF_ACTIVITY_POLICT_RETRY_INTERVAL: ClassVar[Any] = None + ADF_ACTIVITY_STATE: ClassVar[Any] = None + ADF_ACTIVITY_SOURCES: ClassVar[Any] = None + ADF_ACTIVITY_SINKS: ClassVar[Any] = None + ADF_ACTIVITY_SOURCE_TYPE: ClassVar[Any] = None + ADF_ACTIVITY_SINK_TYPE: ClassVar[Any] = None + ADF_ACTIVITY_RUNS: ClassVar[Any] = None + ADF_ACTIVITY_NOTEBOOK_PATH: ClassVar[Any] = None + ADF_ACTIVITY_MAIN_CLASS_NAME: ClassVar[Any] = None + ADF_ACTIVITY_PYTHON_FILE_PATH: ClassVar[Any] = None + ADF_ACTIVITY_FIRST_ROW_ONLY: ClassVar[Any] = None + ADF_ACTIVITY_BATCH_COUNT: ClassVar[Any] = None + ADF_ACTIVITY_IS_SEQUENTIAL: ClassVar[Any] = None + ADF_ACTIVITY_SUB_ACTIVITIES: ClassVar[Any] = None + ADF_ACTIVITY_REFERENCE_DATAFLOW: ClassVar[Any] = None + ADF_PIPELINE_QUALIFIED_NAME: ClassVar[Any] = None + ADF_FACTORY_NAME: ClassVar[Any] = None + ADF_ASSET_FOLDER_PATH: ClassVar[Any] = None + ADF_LINKEDSERVICES: ClassVar[Any] = None + ADF_DATASETS: ClassVar[Any] = None + ADF_DATAFLOW: ClassVar[Any] = None + ADF_PIPELINE: ClassVar[Any] = None + PROCESSES: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "AdfActivity" + + adf_activity_type: Union[str, None, UnsetType] = UNSET + """The type of the ADF activity.""" + + adf_activity_preceding_dependency: Union[List[str], None, UnsetType] = UNSET + """The list of ADF activities on which this ADF activity depends on.""" + + adf_activity_policy_timeout: Union[str, None, UnsetType] = UNSET + """The timout defined for the ADF activity.""" + + adf_activity_polict_retry_interval: Union[int, None, UnsetType] = UNSET + """The retry interval in seconds for the ADF activity.""" + + adf_activity_state: Union[str, None, UnsetType] = UNSET + """Defines the state (Active or Inactive) of an ADF activity whether it is active or not.""" + + adf_activity_sources: Union[List[str], None, UnsetType] = UNSET + """The list of names of sources for the ADF activity.""" + + adf_activity_sinks: Union[List[str], None, UnsetType] = UNSET + """The list of names of sinks for the ADF activity.""" + + adf_activity_source_type: Union[str, None, UnsetType] = UNSET + """Defines the type of the source of the ADF activtity.""" + + adf_activity_sink_type: Union[str, None, UnsetType] = UNSET + """Defines the type of the sink of the ADF activtity.""" + + adf_activity_runs: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of objects of activity runs for a particular activity.""" + + adf_activity_notebook_path: Union[str, None, UnsetType] = UNSET + """Defines the path of the notebook in the databricks notebook activity.""" + + adf_activity_main_class_name: Union[str, None, UnsetType] = UNSET + """Defines the main class of the databricks spark activity.""" + + adf_activity_python_file_path: Union[str, None, UnsetType] = UNSET + """Defines the python file path for databricks python activity.""" + + adf_activity_first_row_only: Union[bool, None, UnsetType] = UNSET + """Indicates whether to import only first row only or not in Lookup activity.""" + + adf_activity_batch_count: Union[int, None, UnsetType] = UNSET + """Defines the batch count of activity to runs in ForEach activity.""" + + adf_activity_is_sequential: Union[bool, None, UnsetType] = UNSET + """Indicates whether the activity processing is sequential or not inside the ForEach activity.""" + + adf_activity_sub_activities: Union[List[str], None, UnsetType] = UNSET + """The list of activities to be run inside a ForEach activity.""" + + adf_activity_reference_dataflow: Union[str, None, UnsetType] = UNSET + """Defines the dataflow that is to be used in dataflow activity.""" + + adf_pipeline_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the pipeline in which this activity exists.""" + + adf_factory_name: Union[str, None, UnsetType] = UNSET + """Defines the name of the factory in which this asset exists.""" + + adf_asset_folder_path: Union[str, None, UnsetType] = UNSET + """Defines the folder path in which this ADF asset exists.""" + + adf_linkedservices: Union[List[RelatedAdfLinkedservice], None, UnsetType] = UNSET + """ADF activities that are associated with this ADF Linkedservice.""" + + adf_datasets: Union[List[RelatedAdfDataset], None, UnsetType] = UNSET + """ADF activities that are associated with this ADF Dataset.""" + + adf_dataflow: Union[RelatedAdfDataflow, None, UnsetType] = UNSET + """ADF activities that are associated with this ADF Dataflow.""" + + adf_pipeline: Union[RelatedAdfPipeline, None, UnsetType] = UNSET + """ADF Activity that is associated with this ADF Pipeline.""" + + processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Lineage process that associates this ADF Activity.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "AdfActivity" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _adf_activity_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> AdfActivity: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + AdfActivity instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _adf_activity_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class AdfActivityAttributes(AssetAttributes): + """AdfActivity-specific attributes for nested API format.""" + + adf_activity_type: Union[str, None, UnsetType] = UNSET + """The type of the ADF activity.""" + + adf_activity_preceding_dependency: Union[List[str], None, UnsetType] = UNSET + """The list of ADF activities on which this ADF activity depends on.""" + + adf_activity_policy_timeout: Union[str, None, UnsetType] = UNSET + """The timout defined for the ADF activity.""" + + adf_activity_polict_retry_interval: Union[int, None, UnsetType] = UNSET + """The retry interval in seconds for the ADF activity.""" + + adf_activity_state: Union[str, None, UnsetType] = UNSET + """Defines the state (Active or Inactive) of an ADF activity whether it is active or not.""" + + adf_activity_sources: Union[List[str], None, UnsetType] = UNSET + """The list of names of sources for the ADF activity.""" + + adf_activity_sinks: Union[List[str], None, UnsetType] = UNSET + """The list of names of sinks for the ADF activity.""" + + adf_activity_source_type: Union[str, None, UnsetType] = UNSET + """Defines the type of the source of the ADF activtity.""" + + adf_activity_sink_type: Union[str, None, UnsetType] = UNSET + """Defines the type of the sink of the ADF activtity.""" + + adf_activity_runs: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of objects of activity runs for a particular activity.""" + + adf_activity_notebook_path: Union[str, None, UnsetType] = UNSET + """Defines the path of the notebook in the databricks notebook activity.""" + + adf_activity_main_class_name: Union[str, None, UnsetType] = UNSET + """Defines the main class of the databricks spark activity.""" + + adf_activity_python_file_path: Union[str, None, UnsetType] = UNSET + """Defines the python file path for databricks python activity.""" + + adf_activity_first_row_only: Union[bool, None, UnsetType] = UNSET + """Indicates whether to import only first row only or not in Lookup activity.""" + + adf_activity_batch_count: Union[int, None, UnsetType] = UNSET + """Defines the batch count of activity to runs in ForEach activity.""" + + adf_activity_is_sequential: Union[bool, None, UnsetType] = UNSET + """Indicates whether the activity processing is sequential or not inside the ForEach activity.""" + + adf_activity_sub_activities: Union[List[str], None, UnsetType] = UNSET + """The list of activities to be run inside a ForEach activity.""" + + adf_activity_reference_dataflow: Union[str, None, UnsetType] = UNSET + """Defines the dataflow that is to be used in dataflow activity.""" + + adf_pipeline_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the pipeline in which this activity exists.""" + + adf_factory_name: Union[str, None, UnsetType] = UNSET + """Defines the name of the factory in which this asset exists.""" + + adf_asset_folder_path: Union[str, None, UnsetType] = UNSET + """Defines the folder path in which this ADF asset exists.""" + + +class AdfActivityRelationshipAttributes(AssetRelationshipAttributes): + """AdfActivity-specific relationship attributes for nested API format.""" + + adf_linkedservices: Union[List[RelatedAdfLinkedservice], None, UnsetType] = UNSET + """ADF activities that are associated with this ADF Linkedservice.""" + + adf_datasets: Union[List[RelatedAdfDataset], None, UnsetType] = UNSET + """ADF activities that are associated with this ADF Dataset.""" + + adf_dataflow: Union[RelatedAdfDataflow, None, UnsetType] = UNSET + """ADF activities that are associated with this ADF Dataflow.""" + + adf_pipeline: Union[RelatedAdfPipeline, None, UnsetType] = UNSET + """ADF Activity that is associated with this ADF Pipeline.""" + + processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Lineage process that associates this ADF Activity.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class AdfActivityNested(AssetNested): + """AdfActivity in nested API format for high-performance serialization.""" + + attributes: Union[AdfActivityAttributes, UnsetType] = UNSET + relationship_attributes: Union[AdfActivityRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + AdfActivityRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + AdfActivityRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_ADF_ACTIVITY_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "adf_linkedservices", + "adf_datasets", + "adf_dataflow", + "adf_pipeline", + "processes", + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_adf_activity_attrs( + attrs: AdfActivityAttributes, obj: AdfActivity +) -> None: + """Populate AdfActivity-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.adf_activity_type = obj.adf_activity_type + attrs.adf_activity_preceding_dependency = obj.adf_activity_preceding_dependency + attrs.adf_activity_policy_timeout = obj.adf_activity_policy_timeout + attrs.adf_activity_polict_retry_interval = obj.adf_activity_polict_retry_interval + attrs.adf_activity_state = obj.adf_activity_state + attrs.adf_activity_sources = obj.adf_activity_sources + attrs.adf_activity_sinks = obj.adf_activity_sinks + attrs.adf_activity_source_type = obj.adf_activity_source_type + attrs.adf_activity_sink_type = obj.adf_activity_sink_type + attrs.adf_activity_runs = obj.adf_activity_runs + attrs.adf_activity_notebook_path = obj.adf_activity_notebook_path + attrs.adf_activity_main_class_name = obj.adf_activity_main_class_name + attrs.adf_activity_python_file_path = obj.adf_activity_python_file_path + attrs.adf_activity_first_row_only = obj.adf_activity_first_row_only + attrs.adf_activity_batch_count = obj.adf_activity_batch_count + attrs.adf_activity_is_sequential = obj.adf_activity_is_sequential + attrs.adf_activity_sub_activities = obj.adf_activity_sub_activities + attrs.adf_activity_reference_dataflow = obj.adf_activity_reference_dataflow + attrs.adf_pipeline_qualified_name = obj.adf_pipeline_qualified_name + attrs.adf_factory_name = obj.adf_factory_name + attrs.adf_asset_folder_path = obj.adf_asset_folder_path + + +def _extract_adf_activity_attrs(attrs: AdfActivityAttributes) -> dict: + """Extract all AdfActivity attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["adf_activity_type"] = attrs.adf_activity_type + result["adf_activity_preceding_dependency"] = ( + attrs.adf_activity_preceding_dependency + ) + result["adf_activity_policy_timeout"] = attrs.adf_activity_policy_timeout + result["adf_activity_polict_retry_interval"] = ( + attrs.adf_activity_polict_retry_interval + ) + result["adf_activity_state"] = attrs.adf_activity_state + result["adf_activity_sources"] = attrs.adf_activity_sources + result["adf_activity_sinks"] = attrs.adf_activity_sinks + result["adf_activity_source_type"] = attrs.adf_activity_source_type + result["adf_activity_sink_type"] = attrs.adf_activity_sink_type + result["adf_activity_runs"] = attrs.adf_activity_runs + result["adf_activity_notebook_path"] = attrs.adf_activity_notebook_path + result["adf_activity_main_class_name"] = attrs.adf_activity_main_class_name + result["adf_activity_python_file_path"] = attrs.adf_activity_python_file_path + result["adf_activity_first_row_only"] = attrs.adf_activity_first_row_only + result["adf_activity_batch_count"] = attrs.adf_activity_batch_count + result["adf_activity_is_sequential"] = attrs.adf_activity_is_sequential + result["adf_activity_sub_activities"] = attrs.adf_activity_sub_activities + result["adf_activity_reference_dataflow"] = attrs.adf_activity_reference_dataflow + result["adf_pipeline_qualified_name"] = attrs.adf_pipeline_qualified_name + result["adf_factory_name"] = attrs.adf_factory_name + result["adf_asset_folder_path"] = attrs.adf_asset_folder_path + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _adf_activity_to_nested(adf_activity: AdfActivity) -> AdfActivityNested: + """Convert flat AdfActivity to nested format.""" + attrs = AdfActivityAttributes() + _populate_adf_activity_attrs(attrs, adf_activity) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + adf_activity, _ADF_ACTIVITY_REL_FIELDS, AdfActivityRelationshipAttributes + ) + return AdfActivityNested( + guid=adf_activity.guid, + type_name=adf_activity.type_name, + status=adf_activity.status, + version=adf_activity.version, + create_time=adf_activity.create_time, + update_time=adf_activity.update_time, + created_by=adf_activity.created_by, + updated_by=adf_activity.updated_by, + classifications=adf_activity.classifications, + classification_names=adf_activity.classification_names, + meanings=adf_activity.meanings, + labels=adf_activity.labels, + business_attributes=adf_activity.business_attributes, + custom_attributes=adf_activity.custom_attributes, + pending_tasks=adf_activity.pending_tasks, + proxy=adf_activity.proxy, + is_incomplete=adf_activity.is_incomplete, + provenance_type=adf_activity.provenance_type, + home_id=adf_activity.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _adf_activity_from_nested(nested: AdfActivityNested) -> AdfActivity: + """Convert nested format to flat AdfActivity.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else AdfActivityAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _ADF_ACTIVITY_REL_FIELDS, + AdfActivityRelationshipAttributes, + ) + return AdfActivity( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_adf_activity_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _adf_activity_to_nested_bytes(adf_activity: AdfActivity, serde: Serde) -> bytes: + """Convert flat AdfActivity to nested JSON bytes.""" + return serde.encode(_adf_activity_to_nested(adf_activity)) + + +def _adf_activity_from_nested_bytes(data: bytes, serde: Serde) -> AdfActivity: + """Convert nested JSON bytes to flat AdfActivity.""" + nested = serde.decode(data, AdfActivityNested) + return _adf_activity_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +AdfActivity.ADF_ACTIVITY_TYPE = KeywordField("adfActivityType", "adfActivityType") +AdfActivity.ADF_ACTIVITY_PRECEDING_DEPENDENCY = KeywordField( + "adfActivityPrecedingDependency", "adfActivityPrecedingDependency" +) +AdfActivity.ADF_ACTIVITY_POLICY_TIMEOUT = KeywordField( + "adfActivityPolicyTimeout", "adfActivityPolicyTimeout" +) +AdfActivity.ADF_ACTIVITY_POLICT_RETRY_INTERVAL = NumericField( + "adfActivityPolictRetryInterval", "adfActivityPolictRetryInterval" +) +AdfActivity.ADF_ACTIVITY_STATE = KeywordField("adfActivityState", "adfActivityState") +AdfActivity.ADF_ACTIVITY_SOURCES = KeywordField( + "adfActivitySources", "adfActivitySources" +) +AdfActivity.ADF_ACTIVITY_SINKS = KeywordField("adfActivitySinks", "adfActivitySinks") +AdfActivity.ADF_ACTIVITY_SOURCE_TYPE = KeywordField( + "adfActivitySourceType", "adfActivitySourceType" +) +AdfActivity.ADF_ACTIVITY_SINK_TYPE = KeywordField( + "adfActivitySinkType", "adfActivitySinkType" +) +AdfActivity.ADF_ACTIVITY_RUNS = KeywordField("adfActivityRuns", "adfActivityRuns") +AdfActivity.ADF_ACTIVITY_NOTEBOOK_PATH = KeywordField( + "adfActivityNotebookPath", "adfActivityNotebookPath" +) +AdfActivity.ADF_ACTIVITY_MAIN_CLASS_NAME = KeywordField( + "adfActivityMainClassName", "adfActivityMainClassName" +) +AdfActivity.ADF_ACTIVITY_PYTHON_FILE_PATH = KeywordField( + "adfActivityPythonFilePath", "adfActivityPythonFilePath" +) +AdfActivity.ADF_ACTIVITY_FIRST_ROW_ONLY = BooleanField( + "adfActivityFirstRowOnly", "adfActivityFirstRowOnly" +) +AdfActivity.ADF_ACTIVITY_BATCH_COUNT = NumericField( + "adfActivityBatchCount", "adfActivityBatchCount" +) +AdfActivity.ADF_ACTIVITY_IS_SEQUENTIAL = BooleanField( + "adfActivityIsSequential", "adfActivityIsSequential" +) +AdfActivity.ADF_ACTIVITY_SUB_ACTIVITIES = KeywordField( + "adfActivitySubActivities", "adfActivitySubActivities" +) +AdfActivity.ADF_ACTIVITY_REFERENCE_DATAFLOW = KeywordField( + "adfActivityReferenceDataflow", "adfActivityReferenceDataflow" +) +AdfActivity.ADF_PIPELINE_QUALIFIED_NAME = KeywordTextField( + "adfPipelineQualifiedName", + "adfPipelineQualifiedName", + "adfPipelineQualifiedName.text", +) +AdfActivity.ADF_FACTORY_NAME = KeywordField("adfFactoryName", "adfFactoryName") +AdfActivity.ADF_ASSET_FOLDER_PATH = KeywordField( + "adfAssetFolderPath", "adfAssetFolderPath" +) +AdfActivity.ADF_LINKEDSERVICES = RelationField("adfLinkedservices") +AdfActivity.ADF_DATASETS = RelationField("adfDatasets") +AdfActivity.ADF_DATAFLOW = RelationField("adfDataflow") +AdfActivity.ADF_PIPELINE = RelationField("adfPipeline") +AdfActivity.PROCESSES = RelationField("processes") +AdfActivity.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +AdfActivity.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +AdfActivity.ANOMALO_CHECKS = RelationField("anomaloChecks") +AdfActivity.APPLICATION = RelationField("application") +AdfActivity.APPLICATION_FIELD = RelationField("applicationField") +AdfActivity.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +AdfActivity.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +AdfActivity.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +AdfActivity.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +AdfActivity.METRICS = RelationField("metrics") +AdfActivity.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +AdfActivity.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +AdfActivity.MEANINGS = RelationField("meanings") +AdfActivity.MC_MONITORS = RelationField("mcMonitors") +AdfActivity.MC_INCIDENTS = RelationField("mcIncidents") +AdfActivity.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +AdfActivity.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +AdfActivity.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +AdfActivity.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +AdfActivity.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +AdfActivity.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +AdfActivity.FILES = RelationField("files") +AdfActivity.LINKS = RelationField("links") +AdfActivity.README = RelationField("readme") +AdfActivity.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +AdfActivity.SODA_CHECKS = RelationField("sodaChecks") +AdfActivity.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +AdfActivity.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/adf_dataflow.py b/pyatlan_v9/model/assets/adf_dataflow.py new file mode 100644 index 000000000..3c5b35be5 --- /dev/null +++ b/pyatlan_v9/model/assets/adf_dataflow.py @@ -0,0 +1,626 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +AdfDataflow asset model with flattened inheritance. + +This module provides: +- AdfDataflow: Flat asset class (easy to use) +- AdfDataflowAttributes: Nested attributes struct (extends AssetAttributes) +- AdfDataflowNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .adf_related import ( + RelatedAdfActivity, + RelatedAdfDataset, + RelatedAdfLinkedservice, + RelatedAdfPipeline, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class AdfDataflow(Asset): + """ + Base class for ADF Dataflows. It is a visually designed data transformation logic. + """ + + ADF_DATAFLOW_SOURCES: ClassVar[Any] = None + ADF_DATAFLOW_SINKS: ClassVar[Any] = None + ADF_DATAFLOW_SCRIPT: ClassVar[Any] = None + ADF_FACTORY_NAME: ClassVar[Any] = None + ADF_ASSET_FOLDER_PATH: ClassVar[Any] = None + ADF_ACTIVITIES: ClassVar[Any] = None + ADF_DATASETS: ClassVar[Any] = None + ADF_LINKEDSERVICES: ClassVar[Any] = None + ADF_PIPELINES: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "AdfDataflow" + + adf_dataflow_sources: Union[List[str], None, UnsetType] = UNSET + """The list of names of sources for this dataflow.""" + + adf_dataflow_sinks: Union[List[str], None, UnsetType] = UNSET + """The list of names of sinks for this dataflow.""" + + adf_dataflow_script: Union[str, None, UnsetType] = UNSET + """The gererated script for the dataflow.""" + + adf_factory_name: Union[str, None, UnsetType] = UNSET + """Defines the name of the factory in which this asset exists.""" + + adf_asset_folder_path: Union[str, None, UnsetType] = UNSET + """Defines the folder path in which this ADF asset exists.""" + + adf_activities: Union[List[RelatedAdfActivity], None, UnsetType] = UNSET + """ADF Dataflow that is associated with these ADF activities.""" + + adf_datasets: Union[List[RelatedAdfDataset], None, UnsetType] = UNSET + """ADF Dataflows that is associated with this ADF Datasets.""" + + adf_linkedservices: Union[List[RelatedAdfLinkedservice], None, UnsetType] = UNSET + """ADF Dataflows that is associated with this ADF Linkedservices.""" + + adf_pipelines: Union[List[RelatedAdfPipeline], None, UnsetType] = UNSET + """ADF Dataflows that are associated with this ADF pipelines.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "AdfDataflow" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _adf_dataflow_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> AdfDataflow: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + AdfDataflow instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _adf_dataflow_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class AdfDataflowAttributes(AssetAttributes): + """AdfDataflow-specific attributes for nested API format.""" + + adf_dataflow_sources: Union[List[str], None, UnsetType] = UNSET + """The list of names of sources for this dataflow.""" + + adf_dataflow_sinks: Union[List[str], None, UnsetType] = UNSET + """The list of names of sinks for this dataflow.""" + + adf_dataflow_script: Union[str, None, UnsetType] = UNSET + """The gererated script for the dataflow.""" + + adf_factory_name: Union[str, None, UnsetType] = UNSET + """Defines the name of the factory in which this asset exists.""" + + adf_asset_folder_path: Union[str, None, UnsetType] = UNSET + """Defines the folder path in which this ADF asset exists.""" + + +class AdfDataflowRelationshipAttributes(AssetRelationshipAttributes): + """AdfDataflow-specific relationship attributes for nested API format.""" + + adf_activities: Union[List[RelatedAdfActivity], None, UnsetType] = UNSET + """ADF Dataflow that is associated with these ADF activities.""" + + adf_datasets: Union[List[RelatedAdfDataset], None, UnsetType] = UNSET + """ADF Dataflows that is associated with this ADF Datasets.""" + + adf_linkedservices: Union[List[RelatedAdfLinkedservice], None, UnsetType] = UNSET + """ADF Dataflows that is associated with this ADF Linkedservices.""" + + adf_pipelines: Union[List[RelatedAdfPipeline], None, UnsetType] = UNSET + """ADF Dataflows that are associated with this ADF pipelines.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class AdfDataflowNested(AssetNested): + """AdfDataflow in nested API format for high-performance serialization.""" + + attributes: Union[AdfDataflowAttributes, UnsetType] = UNSET + relationship_attributes: Union[AdfDataflowRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + AdfDataflowRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + AdfDataflowRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_ADF_DATAFLOW_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "adf_activities", + "adf_datasets", + "adf_linkedservices", + "adf_pipelines", + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_adf_dataflow_attrs( + attrs: AdfDataflowAttributes, obj: AdfDataflow +) -> None: + """Populate AdfDataflow-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.adf_dataflow_sources = obj.adf_dataflow_sources + attrs.adf_dataflow_sinks = obj.adf_dataflow_sinks + attrs.adf_dataflow_script = obj.adf_dataflow_script + attrs.adf_factory_name = obj.adf_factory_name + attrs.adf_asset_folder_path = obj.adf_asset_folder_path + + +def _extract_adf_dataflow_attrs(attrs: AdfDataflowAttributes) -> dict: + """Extract all AdfDataflow attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["adf_dataflow_sources"] = attrs.adf_dataflow_sources + result["adf_dataflow_sinks"] = attrs.adf_dataflow_sinks + result["adf_dataflow_script"] = attrs.adf_dataflow_script + result["adf_factory_name"] = attrs.adf_factory_name + result["adf_asset_folder_path"] = attrs.adf_asset_folder_path + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _adf_dataflow_to_nested(adf_dataflow: AdfDataflow) -> AdfDataflowNested: + """Convert flat AdfDataflow to nested format.""" + attrs = AdfDataflowAttributes() + _populate_adf_dataflow_attrs(attrs, adf_dataflow) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + adf_dataflow, _ADF_DATAFLOW_REL_FIELDS, AdfDataflowRelationshipAttributes + ) + return AdfDataflowNested( + guid=adf_dataflow.guid, + type_name=adf_dataflow.type_name, + status=adf_dataflow.status, + version=adf_dataflow.version, + create_time=adf_dataflow.create_time, + update_time=adf_dataflow.update_time, + created_by=adf_dataflow.created_by, + updated_by=adf_dataflow.updated_by, + classifications=adf_dataflow.classifications, + classification_names=adf_dataflow.classification_names, + meanings=adf_dataflow.meanings, + labels=adf_dataflow.labels, + business_attributes=adf_dataflow.business_attributes, + custom_attributes=adf_dataflow.custom_attributes, + pending_tasks=adf_dataflow.pending_tasks, + proxy=adf_dataflow.proxy, + is_incomplete=adf_dataflow.is_incomplete, + provenance_type=adf_dataflow.provenance_type, + home_id=adf_dataflow.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _adf_dataflow_from_nested(nested: AdfDataflowNested) -> AdfDataflow: + """Convert nested format to flat AdfDataflow.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else AdfDataflowAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _ADF_DATAFLOW_REL_FIELDS, + AdfDataflowRelationshipAttributes, + ) + return AdfDataflow( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_adf_dataflow_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _adf_dataflow_to_nested_bytes(adf_dataflow: AdfDataflow, serde: Serde) -> bytes: + """Convert flat AdfDataflow to nested JSON bytes.""" + return serde.encode(_adf_dataflow_to_nested(adf_dataflow)) + + +def _adf_dataflow_from_nested_bytes(data: bytes, serde: Serde) -> AdfDataflow: + """Convert nested JSON bytes to flat AdfDataflow.""" + nested = serde.decode(data, AdfDataflowNested) + return _adf_dataflow_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +AdfDataflow.ADF_DATAFLOW_SOURCES = KeywordField( + "adfDataflowSources", "adfDataflowSources" +) +AdfDataflow.ADF_DATAFLOW_SINKS = KeywordField("adfDataflowSinks", "adfDataflowSinks") +AdfDataflow.ADF_DATAFLOW_SCRIPT = KeywordField("adfDataflowScript", "adfDataflowScript") +AdfDataflow.ADF_FACTORY_NAME = KeywordField("adfFactoryName", "adfFactoryName") +AdfDataflow.ADF_ASSET_FOLDER_PATH = KeywordField( + "adfAssetFolderPath", "adfAssetFolderPath" +) +AdfDataflow.ADF_ACTIVITIES = RelationField("adfActivities") +AdfDataflow.ADF_DATASETS = RelationField("adfDatasets") +AdfDataflow.ADF_LINKEDSERVICES = RelationField("adfLinkedservices") +AdfDataflow.ADF_PIPELINES = RelationField("adfPipelines") +AdfDataflow.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +AdfDataflow.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +AdfDataflow.ANOMALO_CHECKS = RelationField("anomaloChecks") +AdfDataflow.APPLICATION = RelationField("application") +AdfDataflow.APPLICATION_FIELD = RelationField("applicationField") +AdfDataflow.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +AdfDataflow.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +AdfDataflow.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +AdfDataflow.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +AdfDataflow.METRICS = RelationField("metrics") +AdfDataflow.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +AdfDataflow.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +AdfDataflow.MEANINGS = RelationField("meanings") +AdfDataflow.MC_MONITORS = RelationField("mcMonitors") +AdfDataflow.MC_INCIDENTS = RelationField("mcIncidents") +AdfDataflow.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +AdfDataflow.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +AdfDataflow.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +AdfDataflow.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +AdfDataflow.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +AdfDataflow.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +AdfDataflow.FILES = RelationField("files") +AdfDataflow.LINKS = RelationField("links") +AdfDataflow.README = RelationField("readme") +AdfDataflow.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +AdfDataflow.SODA_CHECKS = RelationField("sodaChecks") +AdfDataflow.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +AdfDataflow.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/adf_dataset.py b/pyatlan_v9/model/assets/adf_dataset.py new file mode 100644 index 000000000..d3379737d --- /dev/null +++ b/pyatlan_v9/model/assets/adf_dataset.py @@ -0,0 +1,722 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +AdfDataset asset model with flattened inheritance. + +This module provides: +- AdfDataset: Flat asset class (easy to use) +- AdfDatasetAttributes: Nested attributes struct (extends AssetAttributes) +- AdfDatasetNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .adf_related import ( + RelatedAdfActivity, + RelatedAdfDataflow, + RelatedAdfLinkedservice, + RelatedAdfPipeline, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class AdfDataset(Asset): + """ + Base class for ADF Datasets. It is a named view of data that references or points to the data you want to use in activities. + """ + + ADF_DATASET_TYPE: ClassVar[Any] = None + ADF_DATASET_ANNOTATIONS: ClassVar[Any] = None + ADF_DATASET_LINKED_SERVICE: ClassVar[Any] = None + ADF_DATASET_COLLECTION_NAME: ClassVar[Any] = None + ADF_DATASET_STORAGE_TYPE: ClassVar[Any] = None + ADF_DATASET_FILE_NAME: ClassVar[Any] = None + ADF_DATASET_FILE_FOLDER_PATH: ClassVar[Any] = None + ADF_DATASET_CONTAINER_NAME: ClassVar[Any] = None + ADF_DATASET_SCHEMA_NAME: ClassVar[Any] = None + ADF_DATASET_TABLE_NAME: ClassVar[Any] = None + ADF_DATASET_DATABASE_NAME: ClassVar[Any] = None + ADF_FACTORY_NAME: ClassVar[Any] = None + ADF_ASSET_FOLDER_PATH: ClassVar[Any] = None + ADF_ACTIVITIES: ClassVar[Any] = None + ADF_DATAFLOWS: ClassVar[Any] = None + ADF_LINKEDSERVICE: ClassVar[Any] = None + ADF_PIPELINES: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "AdfDataset" + + adf_dataset_type: Union[str, None, UnsetType] = UNSET + """Defines the type of the dataset.""" + + adf_dataset_annotations: Union[List[str], None, UnsetType] = UNSET + """The list of annotation assigned to a dataset.""" + + adf_dataset_linked_service: Union[str, None, UnsetType] = UNSET + """Defines the name of the linked service used to create this dataset.""" + + adf_dataset_collection_name: Union[str, None, UnsetType] = UNSET + """Defines the name collection in the cosmos dataset.""" + + adf_dataset_storage_type: Union[str, None, UnsetType] = UNSET + """Defines the storage type of storage file system dataset.""" + + adf_dataset_file_name: Union[str, None, UnsetType] = UNSET + """Defines the name of the file in the storage file system dataset.""" + + adf_dataset_file_folder_path: Union[str, None, UnsetType] = UNSET + """Defines the folder path of the file in the storage file system dataset.""" + + adf_dataset_container_name: Union[str, None, UnsetType] = UNSET + """Defines the container or bucket name in the storage file system dataset.""" + + adf_dataset_schema_name: Union[str, None, UnsetType] = UNSET + """Defines the name of the schema used in the snowflake, mssql, azure sql database type of dataset.""" + + adf_dataset_table_name: Union[str, None, UnsetType] = UNSET + """Defines the name of the table used in the snowflake, mssql, azure sql database type of dataset.""" + + adf_dataset_database_name: Union[str, None, UnsetType] = UNSET + """Defines the name of the database used in the azure delta lake type of dataset.""" + + adf_factory_name: Union[str, None, UnsetType] = UNSET + """Defines the name of the factory in which this asset exists.""" + + adf_asset_folder_path: Union[str, None, UnsetType] = UNSET + """Defines the folder path in which this ADF asset exists.""" + + adf_activities: Union[List[RelatedAdfActivity], None, UnsetType] = UNSET + """ADF Dataset that is associated with these ADF activities.""" + + adf_dataflows: Union[List[RelatedAdfDataflow], None, UnsetType] = UNSET + """ADF Datasets that are associated with this ADF Dataflows.""" + + adf_linkedservice: Union[RelatedAdfLinkedservice, None, UnsetType] = UNSET + """ADF datasets that are associated with this ADF Linkedservice.""" + + adf_pipelines: Union[List[RelatedAdfPipeline], None, UnsetType] = UNSET + """ADF Datasets that are associated with this ADF pipelines.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "AdfDataset" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _adf_dataset_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> AdfDataset: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + AdfDataset instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _adf_dataset_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class AdfDatasetAttributes(AssetAttributes): + """AdfDataset-specific attributes for nested API format.""" + + adf_dataset_type: Union[str, None, UnsetType] = UNSET + """Defines the type of the dataset.""" + + adf_dataset_annotations: Union[List[str], None, UnsetType] = UNSET + """The list of annotation assigned to a dataset.""" + + adf_dataset_linked_service: Union[str, None, UnsetType] = UNSET + """Defines the name of the linked service used to create this dataset.""" + + adf_dataset_collection_name: Union[str, None, UnsetType] = UNSET + """Defines the name collection in the cosmos dataset.""" + + adf_dataset_storage_type: Union[str, None, UnsetType] = UNSET + """Defines the storage type of storage file system dataset.""" + + adf_dataset_file_name: Union[str, None, UnsetType] = UNSET + """Defines the name of the file in the storage file system dataset.""" + + adf_dataset_file_folder_path: Union[str, None, UnsetType] = UNSET + """Defines the folder path of the file in the storage file system dataset.""" + + adf_dataset_container_name: Union[str, None, UnsetType] = UNSET + """Defines the container or bucket name in the storage file system dataset.""" + + adf_dataset_schema_name: Union[str, None, UnsetType] = UNSET + """Defines the name of the schema used in the snowflake, mssql, azure sql database type of dataset.""" + + adf_dataset_table_name: Union[str, None, UnsetType] = UNSET + """Defines the name of the table used in the snowflake, mssql, azure sql database type of dataset.""" + + adf_dataset_database_name: Union[str, None, UnsetType] = UNSET + """Defines the name of the database used in the azure delta lake type of dataset.""" + + adf_factory_name: Union[str, None, UnsetType] = UNSET + """Defines the name of the factory in which this asset exists.""" + + adf_asset_folder_path: Union[str, None, UnsetType] = UNSET + """Defines the folder path in which this ADF asset exists.""" + + +class AdfDatasetRelationshipAttributes(AssetRelationshipAttributes): + """AdfDataset-specific relationship attributes for nested API format.""" + + adf_activities: Union[List[RelatedAdfActivity], None, UnsetType] = UNSET + """ADF Dataset that is associated with these ADF activities.""" + + adf_dataflows: Union[List[RelatedAdfDataflow], None, UnsetType] = UNSET + """ADF Datasets that are associated with this ADF Dataflows.""" + + adf_linkedservice: Union[RelatedAdfLinkedservice, None, UnsetType] = UNSET + """ADF datasets that are associated with this ADF Linkedservice.""" + + adf_pipelines: Union[List[RelatedAdfPipeline], None, UnsetType] = UNSET + """ADF Datasets that are associated with this ADF pipelines.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class AdfDatasetNested(AssetNested): + """AdfDataset in nested API format for high-performance serialization.""" + + attributes: Union[AdfDatasetAttributes, UnsetType] = UNSET + relationship_attributes: Union[AdfDatasetRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + AdfDatasetRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + AdfDatasetRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_ADF_DATASET_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "adf_activities", + "adf_dataflows", + "adf_linkedservice", + "adf_pipelines", + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_adf_dataset_attrs(attrs: AdfDatasetAttributes, obj: AdfDataset) -> None: + """Populate AdfDataset-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.adf_dataset_type = obj.adf_dataset_type + attrs.adf_dataset_annotations = obj.adf_dataset_annotations + attrs.adf_dataset_linked_service = obj.adf_dataset_linked_service + attrs.adf_dataset_collection_name = obj.adf_dataset_collection_name + attrs.adf_dataset_storage_type = obj.adf_dataset_storage_type + attrs.adf_dataset_file_name = obj.adf_dataset_file_name + attrs.adf_dataset_file_folder_path = obj.adf_dataset_file_folder_path + attrs.adf_dataset_container_name = obj.adf_dataset_container_name + attrs.adf_dataset_schema_name = obj.adf_dataset_schema_name + attrs.adf_dataset_table_name = obj.adf_dataset_table_name + attrs.adf_dataset_database_name = obj.adf_dataset_database_name + attrs.adf_factory_name = obj.adf_factory_name + attrs.adf_asset_folder_path = obj.adf_asset_folder_path + + +def _extract_adf_dataset_attrs(attrs: AdfDatasetAttributes) -> dict: + """Extract all AdfDataset attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["adf_dataset_type"] = attrs.adf_dataset_type + result["adf_dataset_annotations"] = attrs.adf_dataset_annotations + result["adf_dataset_linked_service"] = attrs.adf_dataset_linked_service + result["adf_dataset_collection_name"] = attrs.adf_dataset_collection_name + result["adf_dataset_storage_type"] = attrs.adf_dataset_storage_type + result["adf_dataset_file_name"] = attrs.adf_dataset_file_name + result["adf_dataset_file_folder_path"] = attrs.adf_dataset_file_folder_path + result["adf_dataset_container_name"] = attrs.adf_dataset_container_name + result["adf_dataset_schema_name"] = attrs.adf_dataset_schema_name + result["adf_dataset_table_name"] = attrs.adf_dataset_table_name + result["adf_dataset_database_name"] = attrs.adf_dataset_database_name + result["adf_factory_name"] = attrs.adf_factory_name + result["adf_asset_folder_path"] = attrs.adf_asset_folder_path + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _adf_dataset_to_nested(adf_dataset: AdfDataset) -> AdfDatasetNested: + """Convert flat AdfDataset to nested format.""" + attrs = AdfDatasetAttributes() + _populate_adf_dataset_attrs(attrs, adf_dataset) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + adf_dataset, _ADF_DATASET_REL_FIELDS, AdfDatasetRelationshipAttributes + ) + return AdfDatasetNested( + guid=adf_dataset.guid, + type_name=adf_dataset.type_name, + status=adf_dataset.status, + version=adf_dataset.version, + create_time=adf_dataset.create_time, + update_time=adf_dataset.update_time, + created_by=adf_dataset.created_by, + updated_by=adf_dataset.updated_by, + classifications=adf_dataset.classifications, + classification_names=adf_dataset.classification_names, + meanings=adf_dataset.meanings, + labels=adf_dataset.labels, + business_attributes=adf_dataset.business_attributes, + custom_attributes=adf_dataset.custom_attributes, + pending_tasks=adf_dataset.pending_tasks, + proxy=adf_dataset.proxy, + is_incomplete=adf_dataset.is_incomplete, + provenance_type=adf_dataset.provenance_type, + home_id=adf_dataset.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _adf_dataset_from_nested(nested: AdfDatasetNested) -> AdfDataset: + """Convert nested format to flat AdfDataset.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else AdfDatasetAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _ADF_DATASET_REL_FIELDS, + AdfDatasetRelationshipAttributes, + ) + return AdfDataset( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_adf_dataset_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _adf_dataset_to_nested_bytes(adf_dataset: AdfDataset, serde: Serde) -> bytes: + """Convert flat AdfDataset to nested JSON bytes.""" + return serde.encode(_adf_dataset_to_nested(adf_dataset)) + + +def _adf_dataset_from_nested_bytes(data: bytes, serde: Serde) -> AdfDataset: + """Convert nested JSON bytes to flat AdfDataset.""" + nested = serde.decode(data, AdfDatasetNested) + return _adf_dataset_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +AdfDataset.ADF_DATASET_TYPE = KeywordField("adfDatasetType", "adfDatasetType") +AdfDataset.ADF_DATASET_ANNOTATIONS = KeywordField( + "adfDatasetAnnotations", "adfDatasetAnnotations" +) +AdfDataset.ADF_DATASET_LINKED_SERVICE = KeywordField( + "adfDatasetLinkedService", "adfDatasetLinkedService" +) +AdfDataset.ADF_DATASET_COLLECTION_NAME = KeywordField( + "adfDatasetCollectionName", "adfDatasetCollectionName" +) +AdfDataset.ADF_DATASET_STORAGE_TYPE = KeywordField( + "adfDatasetStorageType", "adfDatasetStorageType" +) +AdfDataset.ADF_DATASET_FILE_NAME = KeywordField( + "adfDatasetFileName", "adfDatasetFileName" +) +AdfDataset.ADF_DATASET_FILE_FOLDER_PATH = KeywordField( + "adfDatasetFileFolderPath", "adfDatasetFileFolderPath" +) +AdfDataset.ADF_DATASET_CONTAINER_NAME = KeywordField( + "adfDatasetContainerName", "adfDatasetContainerName" +) +AdfDataset.ADF_DATASET_SCHEMA_NAME = KeywordField( + "adfDatasetSchemaName", "adfDatasetSchemaName" +) +AdfDataset.ADF_DATASET_TABLE_NAME = KeywordField( + "adfDatasetTableName", "adfDatasetTableName" +) +AdfDataset.ADF_DATASET_DATABASE_NAME = KeywordField( + "adfDatasetDatabaseName", "adfDatasetDatabaseName" +) +AdfDataset.ADF_FACTORY_NAME = KeywordField("adfFactoryName", "adfFactoryName") +AdfDataset.ADF_ASSET_FOLDER_PATH = KeywordField( + "adfAssetFolderPath", "adfAssetFolderPath" +) +AdfDataset.ADF_ACTIVITIES = RelationField("adfActivities") +AdfDataset.ADF_DATAFLOWS = RelationField("adfDataflows") +AdfDataset.ADF_LINKEDSERVICE = RelationField("adfLinkedservice") +AdfDataset.ADF_PIPELINES = RelationField("adfPipelines") +AdfDataset.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +AdfDataset.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +AdfDataset.ANOMALO_CHECKS = RelationField("anomaloChecks") +AdfDataset.APPLICATION = RelationField("application") +AdfDataset.APPLICATION_FIELD = RelationField("applicationField") +AdfDataset.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +AdfDataset.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +AdfDataset.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +AdfDataset.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +AdfDataset.METRICS = RelationField("metrics") +AdfDataset.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +AdfDataset.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +AdfDataset.MEANINGS = RelationField("meanings") +AdfDataset.MC_MONITORS = RelationField("mcMonitors") +AdfDataset.MC_INCIDENTS = RelationField("mcIncidents") +AdfDataset.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +AdfDataset.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +AdfDataset.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +AdfDataset.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +AdfDataset.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +AdfDataset.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +AdfDataset.FILES = RelationField("files") +AdfDataset.LINKS = RelationField("links") +AdfDataset.README = RelationField("readme") +AdfDataset.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +AdfDataset.SODA_CHECKS = RelationField("sodaChecks") +AdfDataset.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +AdfDataset.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/adf_linkedservice.py b/pyatlan_v9/model/assets/adf_linkedservice.py new file mode 100644 index 000000000..2fb4cfe08 --- /dev/null +++ b/pyatlan_v9/model/assets/adf_linkedservice.py @@ -0,0 +1,795 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +AdfLinkedservice asset model with flattened inheritance. + +This module provides: +- AdfLinkedservice: Flat asset class (easy to use) +- AdfLinkedserviceAttributes: Nested attributes struct (extends AssetAttributes) +- AdfLinkedserviceNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .adf_related import ( + RelatedAdfActivity, + RelatedAdfDataflow, + RelatedAdfDataset, + RelatedAdfPipeline, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class AdfLinkedservice(Asset): + """ + Base class for ADF Linkedservices. It is a connection to a data source or compute resource used by Azure Data Factory. + """ + + ADF_LINKEDSERVICE_TYPE: ClassVar[Any] = None + ADF_LINKEDSERVICE_ANNOTATIONS: ClassVar[Any] = None + ADF_LINKEDSERVICE_ACCOUNT_NAME: ClassVar[Any] = None + ADF_LINKEDSERVICE_DATABASE_NAME: ClassVar[Any] = None + ADF_LINKEDSERVICE_VERSION_ABOVE: ClassVar[Any] = None + ADF_LINKEDSERVICE_VERSION: ClassVar[Any] = None + ADF_LINKEDSERVICE_AZURE_CLOUD_TYPE: ClassVar[Any] = None + ADF_LINKEDSERVICE_CREDENTIAL_TYPE: ClassVar[Any] = None + ADF_LINKEDSERVICE_TENANT: ClassVar[Any] = None + ADF_LINKEDSERVICE_DOMAIN_ENDPOINT: ClassVar[Any] = None + ADF_LINKEDSERVICE_CLUSTER_ID: ClassVar[Any] = None + ADF_LINKEDSERVICE_RESOURCE_ID: ClassVar[Any] = None + ADF_LINKEDSERVICE_USER_NAME: ClassVar[Any] = None + ADF_LINKEDSERVICE_WAREHOUSE_NAME: ClassVar[Any] = None + ADF_LINKEDSERVICE_ROLE_NAME: ClassVar[Any] = None + ADF_FACTORY_NAME: ClassVar[Any] = None + ADF_ASSET_FOLDER_PATH: ClassVar[Any] = None + ADF_ACTIVITIES: ClassVar[Any] = None + ADF_DATAFLOWS: ClassVar[Any] = None + ADF_DATASETS: ClassVar[Any] = None + ADF_PIPELINES: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "AdfLinkedservice" + + adf_linkedservice_type: Union[str, None, UnsetType] = UNSET + """Defines the type of the linked service.""" + + adf_linkedservice_annotations: Union[List[str], None, UnsetType] = UNSET + """The list of annotation assigned to a linked service.""" + + adf_linkedservice_account_name: Union[str, None, UnsetType] = UNSET + """Defines the name of the account used in the cosmos linked service.""" + + adf_linkedservice_database_name: Union[str, None, UnsetType] = UNSET + """Defines the name of the database used in the cosmos, snowflake linked service.""" + + adf_linkedservice_version_above: Union[bool, None, UnsetType] = UNSET + """Indicates whether the service version is above 3.2 or not in the cosmos linked service.""" + + adf_linkedservice_version: Union[str, None, UnsetType] = UNSET + """Defines the version of the linked service in the cosmos linked service.""" + + adf_linkedservice_azure_cloud_type: Union[str, None, UnsetType] = UNSET + """Defines the type of cloud being used in the ADLS linked service.""" + + adf_linkedservice_credential_type: Union[str, None, UnsetType] = UNSET + """Defines the type of credential, authentication being used in the ADLS, snowflake, azure sql linked service.""" + + adf_linkedservice_tenant: Union[str, None, UnsetType] = UNSET + """Defines the tenant of cloud being used in the ADLS linked service.""" + + adf_linkedservice_domain_endpoint: Union[str, None, UnsetType] = UNSET + """Defines the url, domain, account_identifier, server in the ADLS, Azure databricks delta lake, snowflake, azure sql linked service.""" + + adf_linkedservice_cluster_id: Union[str, None, UnsetType] = UNSET + """Defines the cluster id in the Azure databricks delta lake linked service.""" + + adf_linkedservice_resource_id: Union[str, None, UnsetType] = UNSET + """Defines the resource id in the Azure databricks delta lake linked service.""" + + adf_linkedservice_user_name: Union[str, None, UnsetType] = UNSET + """Defines the name of the db user in the snowflake linked service.""" + + adf_linkedservice_warehouse_name: Union[str, None, UnsetType] = UNSET + """Defines the name of the warehouse in the snowflake linked service.""" + + adf_linkedservice_role_name: Union[str, None, UnsetType] = UNSET + """Defines the name of the role in the snowflake linked service.""" + + adf_factory_name: Union[str, None, UnsetType] = UNSET + """Defines the name of the factory in which this asset exists.""" + + adf_asset_folder_path: Union[str, None, UnsetType] = UNSET + """Defines the folder path in which this ADF asset exists.""" + + adf_activities: Union[List[RelatedAdfActivity], None, UnsetType] = UNSET + """ADF Linkedservice that is associated with these ADF activities.""" + + adf_dataflows: Union[List[RelatedAdfDataflow], None, UnsetType] = UNSET + """ADF Linkedservices that are associated with this ADF Dataflows.""" + + adf_datasets: Union[List[RelatedAdfDataset], None, UnsetType] = UNSET + """ADF Linkedservice that is associated with these ADF datasets.""" + + adf_pipelines: Union[List[RelatedAdfPipeline], None, UnsetType] = UNSET + """ADF Linkedservices that are associated with this ADF pipelines.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "AdfLinkedservice" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _adf_linkedservice_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> AdfLinkedservice: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + AdfLinkedservice instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _adf_linkedservice_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class AdfLinkedserviceAttributes(AssetAttributes): + """AdfLinkedservice-specific attributes for nested API format.""" + + adf_linkedservice_type: Union[str, None, UnsetType] = UNSET + """Defines the type of the linked service.""" + + adf_linkedservice_annotations: Union[List[str], None, UnsetType] = UNSET + """The list of annotation assigned to a linked service.""" + + adf_linkedservice_account_name: Union[str, None, UnsetType] = UNSET + """Defines the name of the account used in the cosmos linked service.""" + + adf_linkedservice_database_name: Union[str, None, UnsetType] = UNSET + """Defines the name of the database used in the cosmos, snowflake linked service.""" + + adf_linkedservice_version_above: Union[bool, None, UnsetType] = UNSET + """Indicates whether the service version is above 3.2 or not in the cosmos linked service.""" + + adf_linkedservice_version: Union[str, None, UnsetType] = UNSET + """Defines the version of the linked service in the cosmos linked service.""" + + adf_linkedservice_azure_cloud_type: Union[str, None, UnsetType] = UNSET + """Defines the type of cloud being used in the ADLS linked service.""" + + adf_linkedservice_credential_type: Union[str, None, UnsetType] = UNSET + """Defines the type of credential, authentication being used in the ADLS, snowflake, azure sql linked service.""" + + adf_linkedservice_tenant: Union[str, None, UnsetType] = UNSET + """Defines the tenant of cloud being used in the ADLS linked service.""" + + adf_linkedservice_domain_endpoint: Union[str, None, UnsetType] = UNSET + """Defines the url, domain, account_identifier, server in the ADLS, Azure databricks delta lake, snowflake, azure sql linked service.""" + + adf_linkedservice_cluster_id: Union[str, None, UnsetType] = UNSET + """Defines the cluster id in the Azure databricks delta lake linked service.""" + + adf_linkedservice_resource_id: Union[str, None, UnsetType] = UNSET + """Defines the resource id in the Azure databricks delta lake linked service.""" + + adf_linkedservice_user_name: Union[str, None, UnsetType] = UNSET + """Defines the name of the db user in the snowflake linked service.""" + + adf_linkedservice_warehouse_name: Union[str, None, UnsetType] = UNSET + """Defines the name of the warehouse in the snowflake linked service.""" + + adf_linkedservice_role_name: Union[str, None, UnsetType] = UNSET + """Defines the name of the role in the snowflake linked service.""" + + adf_factory_name: Union[str, None, UnsetType] = UNSET + """Defines the name of the factory in which this asset exists.""" + + adf_asset_folder_path: Union[str, None, UnsetType] = UNSET + """Defines the folder path in which this ADF asset exists.""" + + +class AdfLinkedserviceRelationshipAttributes(AssetRelationshipAttributes): + """AdfLinkedservice-specific relationship attributes for nested API format.""" + + adf_activities: Union[List[RelatedAdfActivity], None, UnsetType] = UNSET + """ADF Linkedservice that is associated with these ADF activities.""" + + adf_dataflows: Union[List[RelatedAdfDataflow], None, UnsetType] = UNSET + """ADF Linkedservices that are associated with this ADF Dataflows.""" + + adf_datasets: Union[List[RelatedAdfDataset], None, UnsetType] = UNSET + """ADF Linkedservice that is associated with these ADF datasets.""" + + adf_pipelines: Union[List[RelatedAdfPipeline], None, UnsetType] = UNSET + """ADF Linkedservices that are associated with this ADF pipelines.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class AdfLinkedserviceNested(AssetNested): + """AdfLinkedservice in nested API format for high-performance serialization.""" + + attributes: Union[AdfLinkedserviceAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + AdfLinkedserviceRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + AdfLinkedserviceRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + AdfLinkedserviceRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_ADF_LINKEDSERVICE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "adf_activities", + "adf_dataflows", + "adf_datasets", + "adf_pipelines", + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_adf_linkedservice_attrs( + attrs: AdfLinkedserviceAttributes, obj: AdfLinkedservice +) -> None: + """Populate AdfLinkedservice-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.adf_linkedservice_type = obj.adf_linkedservice_type + attrs.adf_linkedservice_annotations = obj.adf_linkedservice_annotations + attrs.adf_linkedservice_account_name = obj.adf_linkedservice_account_name + attrs.adf_linkedservice_database_name = obj.adf_linkedservice_database_name + attrs.adf_linkedservice_version_above = obj.adf_linkedservice_version_above + attrs.adf_linkedservice_version = obj.adf_linkedservice_version + attrs.adf_linkedservice_azure_cloud_type = obj.adf_linkedservice_azure_cloud_type + attrs.adf_linkedservice_credential_type = obj.adf_linkedservice_credential_type + attrs.adf_linkedservice_tenant = obj.adf_linkedservice_tenant + attrs.adf_linkedservice_domain_endpoint = obj.adf_linkedservice_domain_endpoint + attrs.adf_linkedservice_cluster_id = obj.adf_linkedservice_cluster_id + attrs.adf_linkedservice_resource_id = obj.adf_linkedservice_resource_id + attrs.adf_linkedservice_user_name = obj.adf_linkedservice_user_name + attrs.adf_linkedservice_warehouse_name = obj.adf_linkedservice_warehouse_name + attrs.adf_linkedservice_role_name = obj.adf_linkedservice_role_name + attrs.adf_factory_name = obj.adf_factory_name + attrs.adf_asset_folder_path = obj.adf_asset_folder_path + + +def _extract_adf_linkedservice_attrs(attrs: AdfLinkedserviceAttributes) -> dict: + """Extract all AdfLinkedservice attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["adf_linkedservice_type"] = attrs.adf_linkedservice_type + result["adf_linkedservice_annotations"] = attrs.adf_linkedservice_annotations + result["adf_linkedservice_account_name"] = attrs.adf_linkedservice_account_name + result["adf_linkedservice_database_name"] = attrs.adf_linkedservice_database_name + result["adf_linkedservice_version_above"] = attrs.adf_linkedservice_version_above + result["adf_linkedservice_version"] = attrs.adf_linkedservice_version + result["adf_linkedservice_azure_cloud_type"] = ( + attrs.adf_linkedservice_azure_cloud_type + ) + result["adf_linkedservice_credential_type"] = ( + attrs.adf_linkedservice_credential_type + ) + result["adf_linkedservice_tenant"] = attrs.adf_linkedservice_tenant + result["adf_linkedservice_domain_endpoint"] = ( + attrs.adf_linkedservice_domain_endpoint + ) + result["adf_linkedservice_cluster_id"] = attrs.adf_linkedservice_cluster_id + result["adf_linkedservice_resource_id"] = attrs.adf_linkedservice_resource_id + result["adf_linkedservice_user_name"] = attrs.adf_linkedservice_user_name + result["adf_linkedservice_warehouse_name"] = attrs.adf_linkedservice_warehouse_name + result["adf_linkedservice_role_name"] = attrs.adf_linkedservice_role_name + result["adf_factory_name"] = attrs.adf_factory_name + result["adf_asset_folder_path"] = attrs.adf_asset_folder_path + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _adf_linkedservice_to_nested( + adf_linkedservice: AdfLinkedservice, +) -> AdfLinkedserviceNested: + """Convert flat AdfLinkedservice to nested format.""" + attrs = AdfLinkedserviceAttributes() + _populate_adf_linkedservice_attrs(attrs, adf_linkedservice) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + adf_linkedservice, + _ADF_LINKEDSERVICE_REL_FIELDS, + AdfLinkedserviceRelationshipAttributes, + ) + return AdfLinkedserviceNested( + guid=adf_linkedservice.guid, + type_name=adf_linkedservice.type_name, + status=adf_linkedservice.status, + version=adf_linkedservice.version, + create_time=adf_linkedservice.create_time, + update_time=adf_linkedservice.update_time, + created_by=adf_linkedservice.created_by, + updated_by=adf_linkedservice.updated_by, + classifications=adf_linkedservice.classifications, + classification_names=adf_linkedservice.classification_names, + meanings=adf_linkedservice.meanings, + labels=adf_linkedservice.labels, + business_attributes=adf_linkedservice.business_attributes, + custom_attributes=adf_linkedservice.custom_attributes, + pending_tasks=adf_linkedservice.pending_tasks, + proxy=adf_linkedservice.proxy, + is_incomplete=adf_linkedservice.is_incomplete, + provenance_type=adf_linkedservice.provenance_type, + home_id=adf_linkedservice.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _adf_linkedservice_from_nested(nested: AdfLinkedserviceNested) -> AdfLinkedservice: + """Convert nested format to flat AdfLinkedservice.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else AdfLinkedserviceAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _ADF_LINKEDSERVICE_REL_FIELDS, + AdfLinkedserviceRelationshipAttributes, + ) + return AdfLinkedservice( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_adf_linkedservice_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _adf_linkedservice_to_nested_bytes( + adf_linkedservice: AdfLinkedservice, serde: Serde +) -> bytes: + """Convert flat AdfLinkedservice to nested JSON bytes.""" + return serde.encode(_adf_linkedservice_to_nested(adf_linkedservice)) + + +def _adf_linkedservice_from_nested_bytes(data: bytes, serde: Serde) -> AdfLinkedservice: + """Convert nested JSON bytes to flat AdfLinkedservice.""" + nested = serde.decode(data, AdfLinkedserviceNested) + return _adf_linkedservice_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + RelationField, +) + +AdfLinkedservice.ADF_LINKEDSERVICE_TYPE = KeywordField( + "adfLinkedserviceType", "adfLinkedserviceType" +) +AdfLinkedservice.ADF_LINKEDSERVICE_ANNOTATIONS = KeywordField( + "adfLinkedserviceAnnotations", "adfLinkedserviceAnnotations" +) +AdfLinkedservice.ADF_LINKEDSERVICE_ACCOUNT_NAME = KeywordField( + "adfLinkedserviceAccountName", "adfLinkedserviceAccountName" +) +AdfLinkedservice.ADF_LINKEDSERVICE_DATABASE_NAME = KeywordField( + "adfLinkedserviceDatabaseName", "adfLinkedserviceDatabaseName" +) +AdfLinkedservice.ADF_LINKEDSERVICE_VERSION_ABOVE = BooleanField( + "adfLinkedserviceVersionAbove", "adfLinkedserviceVersionAbove" +) +AdfLinkedservice.ADF_LINKEDSERVICE_VERSION = KeywordField( + "adfLinkedserviceVersion", "adfLinkedserviceVersion" +) +AdfLinkedservice.ADF_LINKEDSERVICE_AZURE_CLOUD_TYPE = KeywordField( + "adfLinkedserviceAzureCloudType", "adfLinkedserviceAzureCloudType" +) +AdfLinkedservice.ADF_LINKEDSERVICE_CREDENTIAL_TYPE = KeywordField( + "adfLinkedserviceCredentialType", "adfLinkedserviceCredentialType" +) +AdfLinkedservice.ADF_LINKEDSERVICE_TENANT = KeywordField( + "adfLinkedserviceTenant", "adfLinkedserviceTenant" +) +AdfLinkedservice.ADF_LINKEDSERVICE_DOMAIN_ENDPOINT = KeywordField( + "adfLinkedserviceDomainEndpoint", "adfLinkedserviceDomainEndpoint" +) +AdfLinkedservice.ADF_LINKEDSERVICE_CLUSTER_ID = KeywordField( + "adfLinkedserviceClusterId", "adfLinkedserviceClusterId" +) +AdfLinkedservice.ADF_LINKEDSERVICE_RESOURCE_ID = KeywordField( + "adfLinkedserviceResourceId", "adfLinkedserviceResourceId" +) +AdfLinkedservice.ADF_LINKEDSERVICE_USER_NAME = KeywordField( + "adfLinkedserviceUserName", "adfLinkedserviceUserName" +) +AdfLinkedservice.ADF_LINKEDSERVICE_WAREHOUSE_NAME = KeywordField( + "adfLinkedserviceWarehouseName", "adfLinkedserviceWarehouseName" +) +AdfLinkedservice.ADF_LINKEDSERVICE_ROLE_NAME = KeywordField( + "adfLinkedserviceRoleName", "adfLinkedserviceRoleName" +) +AdfLinkedservice.ADF_FACTORY_NAME = KeywordField("adfFactoryName", "adfFactoryName") +AdfLinkedservice.ADF_ASSET_FOLDER_PATH = KeywordField( + "adfAssetFolderPath", "adfAssetFolderPath" +) +AdfLinkedservice.ADF_ACTIVITIES = RelationField("adfActivities") +AdfLinkedservice.ADF_DATAFLOWS = RelationField("adfDataflows") +AdfLinkedservice.ADF_DATASETS = RelationField("adfDatasets") +AdfLinkedservice.ADF_PIPELINES = RelationField("adfPipelines") +AdfLinkedservice.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +AdfLinkedservice.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +AdfLinkedservice.ANOMALO_CHECKS = RelationField("anomaloChecks") +AdfLinkedservice.APPLICATION = RelationField("application") +AdfLinkedservice.APPLICATION_FIELD = RelationField("applicationField") +AdfLinkedservice.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +AdfLinkedservice.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +AdfLinkedservice.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +AdfLinkedservice.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +AdfLinkedservice.METRICS = RelationField("metrics") +AdfLinkedservice.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +AdfLinkedservice.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +AdfLinkedservice.MEANINGS = RelationField("meanings") +AdfLinkedservice.MC_MONITORS = RelationField("mcMonitors") +AdfLinkedservice.MC_INCIDENTS = RelationField("mcIncidents") +AdfLinkedservice.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +AdfLinkedservice.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +AdfLinkedservice.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +AdfLinkedservice.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +AdfLinkedservice.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +AdfLinkedservice.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +AdfLinkedservice.FILES = RelationField("files") +AdfLinkedservice.LINKS = RelationField("links") +AdfLinkedservice.README = RelationField("readme") +AdfLinkedservice.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +AdfLinkedservice.SODA_CHECKS = RelationField("sodaChecks") +AdfLinkedservice.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +AdfLinkedservice.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/adf_pipeline.py b/pyatlan_v9/model/assets/adf_pipeline.py new file mode 100644 index 000000000..10e424d26 --- /dev/null +++ b/pyatlan_v9/model/assets/adf_pipeline.py @@ -0,0 +1,629 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +AdfPipeline asset model with flattened inheritance. + +This module provides: +- AdfPipeline: Flat asset class (easy to use) +- AdfPipelineAttributes: Nested attributes struct (extends AssetAttributes) +- AdfPipelineNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .adf_related import ( + RelatedAdfActivity, + RelatedAdfDataflow, + RelatedAdfDataset, + RelatedAdfLinkedservice, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class AdfPipeline(Asset): + """ + Base class for ADF Pipelines. It is a logical grouping of activities that together perform a specific data processing task. + """ + + ADF_PIPELINE_ACTIVITY_COUNT: ClassVar[Any] = None + ADF_PIPELINE_RUNS: ClassVar[Any] = None + ADF_PIPELINE_ANNOTATIONS: ClassVar[Any] = None + ADF_FACTORY_NAME: ClassVar[Any] = None + ADF_ASSET_FOLDER_PATH: ClassVar[Any] = None + ADF_ACTIVITIES: ClassVar[Any] = None + ADF_DATASETS: ClassVar[Any] = None + ADF_LINKEDSERVICES: ClassVar[Any] = None + ADF_DATAFLOWS: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "AdfPipeline" + + adf_pipeline_activity_count: Union[int, None, UnsetType] = UNSET + """Defines the count of activities in the pipline.""" + + adf_pipeline_runs: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of objects of pipeline runs for a particular pipeline.""" + + adf_pipeline_annotations: Union[List[str], None, UnsetType] = UNSET + """The list of annotation assigned to a pipeline.""" + + adf_factory_name: Union[str, None, UnsetType] = UNSET + """Defines the name of the factory in which this asset exists.""" + + adf_asset_folder_path: Union[str, None, UnsetType] = UNSET + """Defines the folder path in which this ADF asset exists.""" + + adf_activities: Union[List[RelatedAdfActivity], None, UnsetType] = UNSET + """ADF Pipeline that is associated with these ADF Activities.""" + + adf_datasets: Union[List[RelatedAdfDataset], None, UnsetType] = UNSET + """ADF pipelines that is associated with this ADF Datasets.""" + + adf_linkedservices: Union[List[RelatedAdfLinkedservice], None, UnsetType] = UNSET + """ADF pipelines that is associated with this ADF Linkedservices.""" + + adf_dataflows: Union[List[RelatedAdfDataflow], None, UnsetType] = UNSET + """ADF pipelines that is associated with this ADF Dataflows.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "AdfPipeline" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _adf_pipeline_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> AdfPipeline: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + AdfPipeline instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _adf_pipeline_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class AdfPipelineAttributes(AssetAttributes): + """AdfPipeline-specific attributes for nested API format.""" + + adf_pipeline_activity_count: Union[int, None, UnsetType] = UNSET + """Defines the count of activities in the pipline.""" + + adf_pipeline_runs: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of objects of pipeline runs for a particular pipeline.""" + + adf_pipeline_annotations: Union[List[str], None, UnsetType] = UNSET + """The list of annotation assigned to a pipeline.""" + + adf_factory_name: Union[str, None, UnsetType] = UNSET + """Defines the name of the factory in which this asset exists.""" + + adf_asset_folder_path: Union[str, None, UnsetType] = UNSET + """Defines the folder path in which this ADF asset exists.""" + + +class AdfPipelineRelationshipAttributes(AssetRelationshipAttributes): + """AdfPipeline-specific relationship attributes for nested API format.""" + + adf_activities: Union[List[RelatedAdfActivity], None, UnsetType] = UNSET + """ADF Pipeline that is associated with these ADF Activities.""" + + adf_datasets: Union[List[RelatedAdfDataset], None, UnsetType] = UNSET + """ADF pipelines that is associated with this ADF Datasets.""" + + adf_linkedservices: Union[List[RelatedAdfLinkedservice], None, UnsetType] = UNSET + """ADF pipelines that is associated with this ADF Linkedservices.""" + + adf_dataflows: Union[List[RelatedAdfDataflow], None, UnsetType] = UNSET + """ADF pipelines that is associated with this ADF Dataflows.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class AdfPipelineNested(AssetNested): + """AdfPipeline in nested API format for high-performance serialization.""" + + attributes: Union[AdfPipelineAttributes, UnsetType] = UNSET + relationship_attributes: Union[AdfPipelineRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + AdfPipelineRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + AdfPipelineRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_ADF_PIPELINE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "adf_activities", + "adf_datasets", + "adf_linkedservices", + "adf_dataflows", + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_adf_pipeline_attrs( + attrs: AdfPipelineAttributes, obj: AdfPipeline +) -> None: + """Populate AdfPipeline-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.adf_pipeline_activity_count = obj.adf_pipeline_activity_count + attrs.adf_pipeline_runs = obj.adf_pipeline_runs + attrs.adf_pipeline_annotations = obj.adf_pipeline_annotations + attrs.adf_factory_name = obj.adf_factory_name + attrs.adf_asset_folder_path = obj.adf_asset_folder_path + + +def _extract_adf_pipeline_attrs(attrs: AdfPipelineAttributes) -> dict: + """Extract all AdfPipeline attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["adf_pipeline_activity_count"] = attrs.adf_pipeline_activity_count + result["adf_pipeline_runs"] = attrs.adf_pipeline_runs + result["adf_pipeline_annotations"] = attrs.adf_pipeline_annotations + result["adf_factory_name"] = attrs.adf_factory_name + result["adf_asset_folder_path"] = attrs.adf_asset_folder_path + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _adf_pipeline_to_nested(adf_pipeline: AdfPipeline) -> AdfPipelineNested: + """Convert flat AdfPipeline to nested format.""" + attrs = AdfPipelineAttributes() + _populate_adf_pipeline_attrs(attrs, adf_pipeline) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + adf_pipeline, _ADF_PIPELINE_REL_FIELDS, AdfPipelineRelationshipAttributes + ) + return AdfPipelineNested( + guid=adf_pipeline.guid, + type_name=adf_pipeline.type_name, + status=adf_pipeline.status, + version=adf_pipeline.version, + create_time=adf_pipeline.create_time, + update_time=adf_pipeline.update_time, + created_by=adf_pipeline.created_by, + updated_by=adf_pipeline.updated_by, + classifications=adf_pipeline.classifications, + classification_names=adf_pipeline.classification_names, + meanings=adf_pipeline.meanings, + labels=adf_pipeline.labels, + business_attributes=adf_pipeline.business_attributes, + custom_attributes=adf_pipeline.custom_attributes, + pending_tasks=adf_pipeline.pending_tasks, + proxy=adf_pipeline.proxy, + is_incomplete=adf_pipeline.is_incomplete, + provenance_type=adf_pipeline.provenance_type, + home_id=adf_pipeline.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _adf_pipeline_from_nested(nested: AdfPipelineNested) -> AdfPipeline: + """Convert nested format to flat AdfPipeline.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else AdfPipelineAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _ADF_PIPELINE_REL_FIELDS, + AdfPipelineRelationshipAttributes, + ) + return AdfPipeline( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_adf_pipeline_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _adf_pipeline_to_nested_bytes(adf_pipeline: AdfPipeline, serde: Serde) -> bytes: + """Convert flat AdfPipeline to nested JSON bytes.""" + return serde.encode(_adf_pipeline_to_nested(adf_pipeline)) + + +def _adf_pipeline_from_nested_bytes(data: bytes, serde: Serde) -> AdfPipeline: + """Convert nested JSON bytes to flat AdfPipeline.""" + nested = serde.decode(data, AdfPipelineNested) + return _adf_pipeline_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +AdfPipeline.ADF_PIPELINE_ACTIVITY_COUNT = NumericField( + "adfPipelineActivityCount", "adfPipelineActivityCount" +) +AdfPipeline.ADF_PIPELINE_RUNS = KeywordField("adfPipelineRuns", "adfPipelineRuns") +AdfPipeline.ADF_PIPELINE_ANNOTATIONS = KeywordField( + "adfPipelineAnnotations", "adfPipelineAnnotations" +) +AdfPipeline.ADF_FACTORY_NAME = KeywordField("adfFactoryName", "adfFactoryName") +AdfPipeline.ADF_ASSET_FOLDER_PATH = KeywordField( + "adfAssetFolderPath", "adfAssetFolderPath" +) +AdfPipeline.ADF_ACTIVITIES = RelationField("adfActivities") +AdfPipeline.ADF_DATASETS = RelationField("adfDatasets") +AdfPipeline.ADF_LINKEDSERVICES = RelationField("adfLinkedservices") +AdfPipeline.ADF_DATAFLOWS = RelationField("adfDataflows") +AdfPipeline.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +AdfPipeline.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +AdfPipeline.ANOMALO_CHECKS = RelationField("anomaloChecks") +AdfPipeline.APPLICATION = RelationField("application") +AdfPipeline.APPLICATION_FIELD = RelationField("applicationField") +AdfPipeline.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +AdfPipeline.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +AdfPipeline.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +AdfPipeline.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +AdfPipeline.METRICS = RelationField("metrics") +AdfPipeline.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +AdfPipeline.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +AdfPipeline.MEANINGS = RelationField("meanings") +AdfPipeline.MC_MONITORS = RelationField("mcMonitors") +AdfPipeline.MC_INCIDENTS = RelationField("mcIncidents") +AdfPipeline.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +AdfPipeline.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +AdfPipeline.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +AdfPipeline.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +AdfPipeline.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +AdfPipeline.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +AdfPipeline.FILES = RelationField("files") +AdfPipeline.LINKS = RelationField("links") +AdfPipeline.README = RelationField("readme") +AdfPipeline.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +AdfPipeline.SODA_CHECKS = RelationField("sodaChecks") +AdfPipeline.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +AdfPipeline.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/adf_related.py b/pyatlan_v9/model/assets/adf_related.py new file mode 100644 index 000000000..3b491b07f --- /dev/null +++ b/pyatlan_v9/model/assets/adf_related.py @@ -0,0 +1,277 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for ADF module. + +This module contains all Related{Type} classes for the ADF type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedCatalog +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedADF", + "RelatedAdfActivity", + "RelatedAdfDataflow", + "RelatedAdfDataset", + "RelatedAdfLinkedservice", + "RelatedAdfPipeline", +] + + +class RelatedADF(RelatedCatalog): + """ + Related entity reference for ADF assets. + + Extends RelatedCatalog with ADF-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "ADF" so it serializes correctly + + adf_factory_name: Union[str, None, UnsetType] = UNSET + """Defines the name of the factory in which this asset exists.""" + + adf_asset_folder_path: Union[str, None, UnsetType] = UNSET + """Defines the folder path in which this ADF asset exists.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "ADF" + + +class RelatedAdfActivity(RelatedADF): + """ + Related entity reference for AdfActivity assets. + + Extends RelatedADF with AdfActivity-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "AdfActivity" so it serializes correctly + + adf_activity_type: Union[str, None, UnsetType] = UNSET + """The type of the ADF activity.""" + + adf_activity_preceding_dependency: Union[List[str], None, UnsetType] = UNSET + """The list of ADF activities on which this ADF activity depends on.""" + + adf_activity_policy_timeout: Union[str, None, UnsetType] = UNSET + """The timout defined for the ADF activity.""" + + adf_activity_polict_retry_interval: Union[int, None, UnsetType] = UNSET + """The retry interval in seconds for the ADF activity.""" + + adf_activity_state: Union[str, None, UnsetType] = UNSET + """Defines the state (Active or Inactive) of an ADF activity whether it is active or not.""" + + adf_activity_sources: Union[List[str], None, UnsetType] = UNSET + """The list of names of sources for the ADF activity.""" + + adf_activity_sinks: Union[List[str], None, UnsetType] = UNSET + """The list of names of sinks for the ADF activity.""" + + adf_activity_source_type: Union[str, None, UnsetType] = UNSET + """Defines the type of the source of the ADF activtity.""" + + adf_activity_sink_type: Union[str, None, UnsetType] = UNSET + """Defines the type of the sink of the ADF activtity.""" + + adf_activity_runs: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of objects of activity runs for a particular activity.""" + + adf_activity_notebook_path: Union[str, None, UnsetType] = UNSET + """Defines the path of the notebook in the databricks notebook activity.""" + + adf_activity_main_class_name: Union[str, None, UnsetType] = UNSET + """Defines the main class of the databricks spark activity.""" + + adf_activity_python_file_path: Union[str, None, UnsetType] = UNSET + """Defines the python file path for databricks python activity.""" + + adf_activity_first_row_only: Union[bool, None, UnsetType] = UNSET + """Indicates whether to import only first row only or not in Lookup activity.""" + + adf_activity_batch_count: Union[int, None, UnsetType] = UNSET + """Defines the batch count of activity to runs in ForEach activity.""" + + adf_activity_is_sequential: Union[bool, None, UnsetType] = UNSET + """Indicates whether the activity processing is sequential or not inside the ForEach activity.""" + + adf_activity_sub_activities: Union[List[str], None, UnsetType] = UNSET + """The list of activities to be run inside a ForEach activity.""" + + adf_activity_reference_dataflow: Union[str, None, UnsetType] = UNSET + """Defines the dataflow that is to be used in dataflow activity.""" + + adf_pipeline_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the pipeline in which this activity exists.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "AdfActivity" + + +class RelatedAdfDataflow(RelatedADF): + """ + Related entity reference for AdfDataflow assets. + + Extends RelatedADF with AdfDataflow-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "AdfDataflow" so it serializes correctly + + adf_dataflow_sources: Union[List[str], None, UnsetType] = UNSET + """The list of names of sources for this dataflow.""" + + adf_dataflow_sinks: Union[List[str], None, UnsetType] = UNSET + """The list of names of sinks for this dataflow.""" + + adf_dataflow_script: Union[str, None, UnsetType] = UNSET + """The gererated script for the dataflow.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "AdfDataflow" + + +class RelatedAdfDataset(RelatedADF): + """ + Related entity reference for AdfDataset assets. + + Extends RelatedADF with AdfDataset-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "AdfDataset" so it serializes correctly + + adf_dataset_type: Union[str, None, UnsetType] = UNSET + """Defines the type of the dataset.""" + + adf_dataset_annotations: Union[List[str], None, UnsetType] = UNSET + """The list of annotation assigned to a dataset.""" + + adf_dataset_linked_service: Union[str, None, UnsetType] = UNSET + """Defines the name of the linked service used to create this dataset.""" + + adf_dataset_collection_name: Union[str, None, UnsetType] = UNSET + """Defines the name collection in the cosmos dataset.""" + + adf_dataset_storage_type: Union[str, None, UnsetType] = UNSET + """Defines the storage type of storage file system dataset.""" + + adf_dataset_file_name: Union[str, None, UnsetType] = UNSET + """Defines the name of the file in the storage file system dataset.""" + + adf_dataset_file_folder_path: Union[str, None, UnsetType] = UNSET + """Defines the folder path of the file in the storage file system dataset.""" + + adf_dataset_container_name: Union[str, None, UnsetType] = UNSET + """Defines the container or bucket name in the storage file system dataset.""" + + adf_dataset_schema_name: Union[str, None, UnsetType] = UNSET + """Defines the name of the schema used in the snowflake, mssql, azure sql database type of dataset.""" + + adf_dataset_table_name: Union[str, None, UnsetType] = UNSET + """Defines the name of the table used in the snowflake, mssql, azure sql database type of dataset.""" + + adf_dataset_database_name: Union[str, None, UnsetType] = UNSET + """Defines the name of the database used in the azure delta lake type of dataset.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "AdfDataset" + + +class RelatedAdfLinkedservice(RelatedADF): + """ + Related entity reference for AdfLinkedservice assets. + + Extends RelatedADF with AdfLinkedservice-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "AdfLinkedservice" so it serializes correctly + + adf_linkedservice_type: Union[str, None, UnsetType] = UNSET + """Defines the type of the linked service.""" + + adf_linkedservice_annotations: Union[List[str], None, UnsetType] = UNSET + """The list of annotation assigned to a linked service.""" + + adf_linkedservice_account_name: Union[str, None, UnsetType] = UNSET + """Defines the name of the account used in the cosmos linked service.""" + + adf_linkedservice_database_name: Union[str, None, UnsetType] = UNSET + """Defines the name of the database used in the cosmos, snowflake linked service.""" + + adf_linkedservice_version_above: Union[bool, None, UnsetType] = UNSET + """Indicates whether the service version is above 3.2 or not in the cosmos linked service.""" + + adf_linkedservice_version: Union[str, None, UnsetType] = UNSET + """Defines the version of the linked service in the cosmos linked service.""" + + adf_linkedservice_azure_cloud_type: Union[str, None, UnsetType] = UNSET + """Defines the type of cloud being used in the ADLS linked service.""" + + adf_linkedservice_credential_type: Union[str, None, UnsetType] = UNSET + """Defines the type of credential, authentication being used in the ADLS, snowflake, azure sql linked service.""" + + adf_linkedservice_tenant: Union[str, None, UnsetType] = UNSET + """Defines the tenant of cloud being used in the ADLS linked service.""" + + adf_linkedservice_domain_endpoint: Union[str, None, UnsetType] = UNSET + """Defines the url, domain, account_identifier, server in the ADLS, Azure databricks delta lake, snowflake, azure sql linked service.""" + + adf_linkedservice_cluster_id: Union[str, None, UnsetType] = UNSET + """Defines the cluster id in the Azure databricks delta lake linked service.""" + + adf_linkedservice_resource_id: Union[str, None, UnsetType] = UNSET + """Defines the resource id in the Azure databricks delta lake linked service.""" + + adf_linkedservice_user_name: Union[str, None, UnsetType] = UNSET + """Defines the name of the db user in the snowflake linked service.""" + + adf_linkedservice_warehouse_name: Union[str, None, UnsetType] = UNSET + """Defines the name of the warehouse in the snowflake linked service.""" + + adf_linkedservice_role_name: Union[str, None, UnsetType] = UNSET + """Defines the name of the role in the snowflake linked service.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "AdfLinkedservice" + + +class RelatedAdfPipeline(RelatedADF): + """ + Related entity reference for AdfPipeline assets. + + Extends RelatedADF with AdfPipeline-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "AdfPipeline" so it serializes correctly + + adf_pipeline_activity_count: Union[int, None, UnsetType] = UNSET + """Defines the count of activities in the pipline.""" + + adf_pipeline_runs: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of objects of pipeline runs for a particular pipeline.""" + + adf_pipeline_annotations: Union[List[str], None, UnsetType] = UNSET + """The list of annotation assigned to a pipeline.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "AdfPipeline" diff --git a/pyatlan_v9/model/assets/adls.py b/pyatlan_v9/model/assets/adls.py new file mode 100644 index 000000000..229c3cc7d --- /dev/null +++ b/pyatlan_v9/model/assets/adls.py @@ -0,0 +1,602 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +ADLS asset model with flattened inheritance. + +This module provides: +- ADLS: Flat asset class (easy to use) +- ADLSAttributes: Nested attributes struct (extends AssetAttributes) +- ADLSNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class ADLS(Asset): + """ + Base class for Azure Data Lake Storage (ADLS) assets. + """ + + ADLS_ACCOUNT_QUALIFIED_NAME: ClassVar[Any] = None + ADLS_ACCOUNT_NAME: ClassVar[Any] = None + AZURE_RESOURCE_ID: ClassVar[Any] = None + AZURE_LOCATION: ClassVar[Any] = None + ADLS_ACCOUNT_SECONDARY_LOCATION: ClassVar[Any] = None + AZURE_TAGS: ClassVar[Any] = None + CLOUD_UNIFORM_RESOURCE_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "ADLS" + + adls_account_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the account for this ADLS asset.""" + + adls_account_name: Union[str, None, UnsetType] = UNSET + """Name of the account for this ADLS asset.""" + + azure_resource_id: Union[str, None, UnsetType] = UNSET + """Resource identifier of this asset in Azure.""" + + azure_location: Union[str, None, UnsetType] = UNSET + """Location of this asset in Azure.""" + + adls_account_secondary_location: Union[str, None, UnsetType] = UNSET + """Secondary location of the ADLS account.""" + + azure_tags: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """Tags that have been applied to this asset in Azure.""" + + cloud_uniform_resource_name: Union[str, None, UnsetType] = UNSET + """Uniform resource name (URN) for the asset: AWS ARN, Google Cloud URI, Azure resource ID, Oracle OCID, and so on.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "ADLS" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _adls_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> ADLS: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + ADLS instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _adls_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class ADLSAttributes(AssetAttributes): + """ADLS-specific attributes for nested API format.""" + + adls_account_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the account for this ADLS asset.""" + + adls_account_name: Union[str, None, UnsetType] = UNSET + """Name of the account for this ADLS asset.""" + + azure_resource_id: Union[str, None, UnsetType] = UNSET + """Resource identifier of this asset in Azure.""" + + azure_location: Union[str, None, UnsetType] = UNSET + """Location of this asset in Azure.""" + + adls_account_secondary_location: Union[str, None, UnsetType] = UNSET + """Secondary location of the ADLS account.""" + + azure_tags: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """Tags that have been applied to this asset in Azure.""" + + cloud_uniform_resource_name: Union[str, None, UnsetType] = UNSET + """Uniform resource name (URN) for the asset: AWS ARN, Google Cloud URI, Azure resource ID, Oracle OCID, and so on.""" + + +class ADLSRelationshipAttributes(AssetRelationshipAttributes): + """ADLS-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class ADLSNested(AssetNested): + """ADLS in nested API format for high-performance serialization.""" + + attributes: Union[ADLSAttributes, UnsetType] = UNSET + relationship_attributes: Union[ADLSRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ADLSRelationshipAttributes, UnsetType] = UNSET + remove_relationship_attributes: Union[ADLSRelationshipAttributes, UnsetType] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_ADLS_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_adls_attrs(attrs: ADLSAttributes, obj: ADLS) -> None: + """Populate ADLS-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.adls_account_qualified_name = obj.adls_account_qualified_name + attrs.adls_account_name = obj.adls_account_name + attrs.azure_resource_id = obj.azure_resource_id + attrs.azure_location = obj.azure_location + attrs.adls_account_secondary_location = obj.adls_account_secondary_location + attrs.azure_tags = obj.azure_tags + attrs.cloud_uniform_resource_name = obj.cloud_uniform_resource_name + + +def _extract_adls_attrs(attrs: ADLSAttributes) -> dict: + """Extract all ADLS attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["adls_account_qualified_name"] = attrs.adls_account_qualified_name + result["adls_account_name"] = attrs.adls_account_name + result["azure_resource_id"] = attrs.azure_resource_id + result["azure_location"] = attrs.azure_location + result["adls_account_secondary_location"] = attrs.adls_account_secondary_location + result["azure_tags"] = attrs.azure_tags + result["cloud_uniform_resource_name"] = attrs.cloud_uniform_resource_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _adls_to_nested(adls: ADLS) -> ADLSNested: + """Convert flat ADLS to nested format.""" + attrs = ADLSAttributes() + _populate_adls_attrs(attrs, adls) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + adls, _ADLS_REL_FIELDS, ADLSRelationshipAttributes + ) + return ADLSNested( + guid=adls.guid, + type_name=adls.type_name, + status=adls.status, + version=adls.version, + create_time=adls.create_time, + update_time=adls.update_time, + created_by=adls.created_by, + updated_by=adls.updated_by, + classifications=adls.classifications, + classification_names=adls.classification_names, + meanings=adls.meanings, + labels=adls.labels, + business_attributes=adls.business_attributes, + custom_attributes=adls.custom_attributes, + pending_tasks=adls.pending_tasks, + proxy=adls.proxy, + is_incomplete=adls.is_incomplete, + provenance_type=adls.provenance_type, + home_id=adls.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _adls_from_nested(nested: ADLSNested) -> ADLS: + """Convert nested format to flat ADLS.""" + attrs = nested.attributes if nested.attributes is not UNSET else ADLSAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _ADLS_REL_FIELDS, + ADLSRelationshipAttributes, + ) + return ADLS( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_adls_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _adls_to_nested_bytes(adls: ADLS, serde: Serde) -> bytes: + """Convert flat ADLS to nested JSON bytes.""" + return serde.encode(_adls_to_nested(adls)) + + +def _adls_from_nested_bytes(data: bytes, serde: Serde) -> ADLS: + """Convert nested JSON bytes to flat ADLS.""" + nested = serde.decode(data, ADLSNested) + return _adls_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + RelationField, +) + +ADLS.ADLS_ACCOUNT_QUALIFIED_NAME = KeywordTextField( + "adlsAccountQualifiedName", + "adlsAccountQualifiedName", + "adlsAccountQualifiedName.text", +) +ADLS.ADLS_ACCOUNT_NAME = KeywordField("adlsAccountName", "adlsAccountName") +ADLS.AZURE_RESOURCE_ID = KeywordTextField( + "azureResourceId", "azureResourceId", "azureResourceId.text" +) +ADLS.AZURE_LOCATION = KeywordField("azureLocation", "azureLocation") +ADLS.ADLS_ACCOUNT_SECONDARY_LOCATION = KeywordField( + "adlsAccountSecondaryLocation", "adlsAccountSecondaryLocation" +) +ADLS.AZURE_TAGS = KeywordField("azureTags", "azureTags") +ADLS.CLOUD_UNIFORM_RESOURCE_NAME = KeywordField( + "cloudUniformResourceName", "cloudUniformResourceName" +) +ADLS.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +ADLS.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +ADLS.ANOMALO_CHECKS = RelationField("anomaloChecks") +ADLS.APPLICATION = RelationField("application") +ADLS.APPLICATION_FIELD = RelationField("applicationField") +ADLS.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +ADLS.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +ADLS.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +ADLS.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +ADLS.METRICS = RelationField("metrics") +ADLS.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +ADLS.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +ADLS.MEANINGS = RelationField("meanings") +ADLS.MC_MONITORS = RelationField("mcMonitors") +ADLS.MC_INCIDENTS = RelationField("mcIncidents") +ADLS.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +ADLS.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +ADLS.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +ADLS.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +ADLS.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +ADLS.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +ADLS.FILES = RelationField("files") +ADLS.LINKS = RelationField("links") +ADLS.README = RelationField("readme") +ADLS.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +ADLS.SODA_CHECKS = RelationField("sodaChecks") +ADLS.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +ADLS.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/adls_account.py b/pyatlan_v9/model/assets/adls_account.py new file mode 100644 index 000000000..304dea1d0 --- /dev/null +++ b/pyatlan_v9/model/assets/adls_account.py @@ -0,0 +1,777 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +ADLSAccount asset model with flattened inheritance. + +This module provides: +- ADLSAccount: Flat asset class (easy to use) +- ADLSAccountAttributes: Nested attributes struct (extends AssetAttributes) +- ADLSAccountNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .adls_related import RelatedADLSContainer + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class ADLSAccount(Asset): + """ + Instance of an Azure Data Lake Storage (ADLS) account in Atlan. + """ + + ADLS_ETAG: ClassVar[Any] = None + ADLS_ENCRYPTION_TYPE: ClassVar[Any] = None + ADLS_ACCOUNT_RESOURCE_GROUP: ClassVar[Any] = None + ADLS_ACCOUNT_SUBSCRIPTION: ClassVar[Any] = None + ADLS_ACCOUNT_PERFORMANCE: ClassVar[Any] = None + ADLS_ACCOUNT_REPLICATION: ClassVar[Any] = None + ADLS_ACCOUNT_KIND: ClassVar[Any] = None + ADLS_PRIMARY_DISK_STATE: ClassVar[Any] = None + ADLS_ACCOUNT_PROVISION_STATE: ClassVar[Any] = None + ADLS_ACCOUNT_ACCESS_TIER: ClassVar[Any] = None + ADLS_ACCOUNT_QUALIFIED_NAME: ClassVar[Any] = None + ADLS_ACCOUNT_NAME: ClassVar[Any] = None + AZURE_RESOURCE_ID: ClassVar[Any] = None + AZURE_LOCATION: ClassVar[Any] = None + ADLS_ACCOUNT_SECONDARY_LOCATION: ClassVar[Any] = None + AZURE_TAGS: ClassVar[Any] = None + CLOUD_UNIFORM_RESOURCE_NAME: ClassVar[Any] = None + ADLS_CONTAINERS: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "ADLSAccount" + + adls_etag: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="adlsETag" + ) + """Entity tag for the asset. An entity tag is a hash of the object and represents changes to the contents of an object only, not its metadata.""" + + adls_encryption_type: Union[str, None, UnsetType] = UNSET + """Type of encryption for this account.""" + + adls_account_resource_group: Union[str, None, UnsetType] = UNSET + """Resource group for this account.""" + + adls_account_subscription: Union[str, None, UnsetType] = UNSET + """Subscription for this account.""" + + adls_account_performance: Union[str, None, UnsetType] = UNSET + """Performance of this account.""" + + adls_account_replication: Union[str, None, UnsetType] = UNSET + """Replication of this account.""" + + adls_account_kind: Union[str, None, UnsetType] = UNSET + """Kind of this account.""" + + adls_primary_disk_state: Union[str, None, UnsetType] = UNSET + """Primary disk state of this account.""" + + adls_account_provision_state: Union[str, None, UnsetType] = UNSET + """Provision state of this account.""" + + adls_account_access_tier: Union[str, None, UnsetType] = UNSET + """Access tier of this account.""" + + adls_account_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the account for this ADLS asset.""" + + adls_account_name: Union[str, None, UnsetType] = UNSET + """Name of the account for this ADLS asset.""" + + azure_resource_id: Union[str, None, UnsetType] = UNSET + """Resource identifier of this asset in Azure.""" + + azure_location: Union[str, None, UnsetType] = UNSET + """Location of this asset in Azure.""" + + adls_account_secondary_location: Union[str, None, UnsetType] = UNSET + """Secondary location of the ADLS account.""" + + azure_tags: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """Tags that have been applied to this asset in Azure.""" + + cloud_uniform_resource_name: Union[str, None, UnsetType] = UNSET + """Uniform resource name (URN) for the asset: AWS ARN, Google Cloud URI, Azure resource ID, Oracle OCID, and so on.""" + + adls_containers: Union[List[RelatedADLSContainer], None, UnsetType] = UNSET + """Containers that exist within this account.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "ADLSAccount" + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + connection_qualified_name: str, + ) -> "ADLSAccount": + validate_required_fields( + ["name", "connection_qualified_name"], + [name, connection_qualified_name], + ) + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + qualified_name = f"{connection_qualified_name}/{name}" + return cls( + name=name, + qualified_name=qualified_name, + connection_qualified_name=connection_qualified_name, + connector_name=connector_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "ADLSAccount": + """Create an ADLSAccount instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "ADLSAccount": + """Return only fields required for update operations.""" + return ADLSAccount.updater(qualified_name=self.qualified_name, name=self.name) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _adls_account_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> ADLSAccount: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + ADLSAccount instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _adls_account_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class ADLSAccountAttributes(AssetAttributes): + """ADLSAccount-specific attributes for nested API format.""" + + adls_etag: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="adlsETag" + ) + """Entity tag for the asset. An entity tag is a hash of the object and represents changes to the contents of an object only, not its metadata.""" + + adls_encryption_type: Union[str, None, UnsetType] = UNSET + """Type of encryption for this account.""" + + adls_account_resource_group: Union[str, None, UnsetType] = UNSET + """Resource group for this account.""" + + adls_account_subscription: Union[str, None, UnsetType] = UNSET + """Subscription for this account.""" + + adls_account_performance: Union[str, None, UnsetType] = UNSET + """Performance of this account.""" + + adls_account_replication: Union[str, None, UnsetType] = UNSET + """Replication of this account.""" + + adls_account_kind: Union[str, None, UnsetType] = UNSET + """Kind of this account.""" + + adls_primary_disk_state: Union[str, None, UnsetType] = UNSET + """Primary disk state of this account.""" + + adls_account_provision_state: Union[str, None, UnsetType] = UNSET + """Provision state of this account.""" + + adls_account_access_tier: Union[str, None, UnsetType] = UNSET + """Access tier of this account.""" + + adls_account_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the account for this ADLS asset.""" + + adls_account_name: Union[str, None, UnsetType] = UNSET + """Name of the account for this ADLS asset.""" + + azure_resource_id: Union[str, None, UnsetType] = UNSET + """Resource identifier of this asset in Azure.""" + + azure_location: Union[str, None, UnsetType] = UNSET + """Location of this asset in Azure.""" + + adls_account_secondary_location: Union[str, None, UnsetType] = UNSET + """Secondary location of the ADLS account.""" + + azure_tags: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """Tags that have been applied to this asset in Azure.""" + + cloud_uniform_resource_name: Union[str, None, UnsetType] = UNSET + """Uniform resource name (URN) for the asset: AWS ARN, Google Cloud URI, Azure resource ID, Oracle OCID, and so on.""" + + +class ADLSAccountRelationshipAttributes(AssetRelationshipAttributes): + """ADLSAccount-specific relationship attributes for nested API format.""" + + adls_containers: Union[List[RelatedADLSContainer], None, UnsetType] = UNSET + """Containers that exist within this account.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class ADLSAccountNested(AssetNested): + """ADLSAccount in nested API format for high-performance serialization.""" + + attributes: Union[ADLSAccountAttributes, UnsetType] = UNSET + relationship_attributes: Union[ADLSAccountRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + ADLSAccountRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + ADLSAccountRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_ADLS_ACCOUNT_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "adls_containers", + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_adls_account_attrs( + attrs: ADLSAccountAttributes, obj: ADLSAccount +) -> None: + """Populate ADLSAccount-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.adls_etag = obj.adls_etag + attrs.adls_encryption_type = obj.adls_encryption_type + attrs.adls_account_resource_group = obj.adls_account_resource_group + attrs.adls_account_subscription = obj.adls_account_subscription + attrs.adls_account_performance = obj.adls_account_performance + attrs.adls_account_replication = obj.adls_account_replication + attrs.adls_account_kind = obj.adls_account_kind + attrs.adls_primary_disk_state = obj.adls_primary_disk_state + attrs.adls_account_provision_state = obj.adls_account_provision_state + attrs.adls_account_access_tier = obj.adls_account_access_tier + attrs.adls_account_qualified_name = obj.adls_account_qualified_name + attrs.adls_account_name = obj.adls_account_name + attrs.azure_resource_id = obj.azure_resource_id + attrs.azure_location = obj.azure_location + attrs.adls_account_secondary_location = obj.adls_account_secondary_location + attrs.azure_tags = obj.azure_tags + attrs.cloud_uniform_resource_name = obj.cloud_uniform_resource_name + + +def _extract_adls_account_attrs(attrs: ADLSAccountAttributes) -> dict: + """Extract all ADLSAccount attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["adls_etag"] = attrs.adls_etag + result["adls_encryption_type"] = attrs.adls_encryption_type + result["adls_account_resource_group"] = attrs.adls_account_resource_group + result["adls_account_subscription"] = attrs.adls_account_subscription + result["adls_account_performance"] = attrs.adls_account_performance + result["adls_account_replication"] = attrs.adls_account_replication + result["adls_account_kind"] = attrs.adls_account_kind + result["adls_primary_disk_state"] = attrs.adls_primary_disk_state + result["adls_account_provision_state"] = attrs.adls_account_provision_state + result["adls_account_access_tier"] = attrs.adls_account_access_tier + result["adls_account_qualified_name"] = attrs.adls_account_qualified_name + result["adls_account_name"] = attrs.adls_account_name + result["azure_resource_id"] = attrs.azure_resource_id + result["azure_location"] = attrs.azure_location + result["adls_account_secondary_location"] = attrs.adls_account_secondary_location + result["azure_tags"] = attrs.azure_tags + result["cloud_uniform_resource_name"] = attrs.cloud_uniform_resource_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _adls_account_to_nested(adls_account: ADLSAccount) -> ADLSAccountNested: + """Convert flat ADLSAccount to nested format.""" + attrs = ADLSAccountAttributes() + _populate_adls_account_attrs(attrs, adls_account) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + adls_account, _ADLS_ACCOUNT_REL_FIELDS, ADLSAccountRelationshipAttributes + ) + return ADLSAccountNested( + guid=adls_account.guid, + type_name=adls_account.type_name, + status=adls_account.status, + version=adls_account.version, + create_time=adls_account.create_time, + update_time=adls_account.update_time, + created_by=adls_account.created_by, + updated_by=adls_account.updated_by, + classifications=adls_account.classifications, + classification_names=adls_account.classification_names, + meanings=adls_account.meanings, + labels=adls_account.labels, + business_attributes=adls_account.business_attributes, + custom_attributes=adls_account.custom_attributes, + pending_tasks=adls_account.pending_tasks, + proxy=adls_account.proxy, + is_incomplete=adls_account.is_incomplete, + provenance_type=adls_account.provenance_type, + home_id=adls_account.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _adls_account_from_nested(nested: ADLSAccountNested) -> ADLSAccount: + """Convert nested format to flat ADLSAccount.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else ADLSAccountAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _ADLS_ACCOUNT_REL_FIELDS, + ADLSAccountRelationshipAttributes, + ) + return ADLSAccount( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_adls_account_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _adls_account_to_nested_bytes(adls_account: ADLSAccount, serde: Serde) -> bytes: + """Convert flat ADLSAccount to nested JSON bytes.""" + return serde.encode(_adls_account_to_nested(adls_account)) + + +def _adls_account_from_nested_bytes(data: bytes, serde: Serde) -> ADLSAccount: + """Convert nested JSON bytes to flat ADLSAccount.""" + nested = serde.decode(data, ADLSAccountNested) + return _adls_account_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + RelationField, +) + +ADLSAccount.ADLS_ETAG = KeywordField("adlsETag", "adlsETag") +ADLSAccount.ADLS_ENCRYPTION_TYPE = KeywordField( + "adlsEncryptionType", "adlsEncryptionType" +) +ADLSAccount.ADLS_ACCOUNT_RESOURCE_GROUP = KeywordTextField( + "adlsAccountResourceGroup", + "adlsAccountResourceGroup", + "adlsAccountResourceGroup.text", +) +ADLSAccount.ADLS_ACCOUNT_SUBSCRIPTION = KeywordTextField( + "adlsAccountSubscription", "adlsAccountSubscription", "adlsAccountSubscription.text" +) +ADLSAccount.ADLS_ACCOUNT_PERFORMANCE = KeywordField( + "adlsAccountPerformance", "adlsAccountPerformance" +) +ADLSAccount.ADLS_ACCOUNT_REPLICATION = KeywordField( + "adlsAccountReplication", "adlsAccountReplication" +) +ADLSAccount.ADLS_ACCOUNT_KIND = KeywordField("adlsAccountKind", "adlsAccountKind") +ADLSAccount.ADLS_PRIMARY_DISK_STATE = KeywordField( + "adlsPrimaryDiskState", "adlsPrimaryDiskState" +) +ADLSAccount.ADLS_ACCOUNT_PROVISION_STATE = KeywordField( + "adlsAccountProvisionState", "adlsAccountProvisionState" +) +ADLSAccount.ADLS_ACCOUNT_ACCESS_TIER = KeywordField( + "adlsAccountAccessTier", "adlsAccountAccessTier" +) +ADLSAccount.ADLS_ACCOUNT_QUALIFIED_NAME = KeywordTextField( + "adlsAccountQualifiedName", + "adlsAccountQualifiedName", + "adlsAccountQualifiedName.text", +) +ADLSAccount.ADLS_ACCOUNT_NAME = KeywordField("adlsAccountName", "adlsAccountName") +ADLSAccount.AZURE_RESOURCE_ID = KeywordTextField( + "azureResourceId", "azureResourceId", "azureResourceId.text" +) +ADLSAccount.AZURE_LOCATION = KeywordField("azureLocation", "azureLocation") +ADLSAccount.ADLS_ACCOUNT_SECONDARY_LOCATION = KeywordField( + "adlsAccountSecondaryLocation", "adlsAccountSecondaryLocation" +) +ADLSAccount.AZURE_TAGS = KeywordField("azureTags", "azureTags") +ADLSAccount.CLOUD_UNIFORM_RESOURCE_NAME = KeywordField( + "cloudUniformResourceName", "cloudUniformResourceName" +) +ADLSAccount.ADLS_CONTAINERS = RelationField("adlsContainers") +ADLSAccount.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +ADLSAccount.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +ADLSAccount.ANOMALO_CHECKS = RelationField("anomaloChecks") +ADLSAccount.APPLICATION = RelationField("application") +ADLSAccount.APPLICATION_FIELD = RelationField("applicationField") +ADLSAccount.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +ADLSAccount.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +ADLSAccount.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +ADLSAccount.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +ADLSAccount.METRICS = RelationField("metrics") +ADLSAccount.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +ADLSAccount.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +ADLSAccount.MEANINGS = RelationField("meanings") +ADLSAccount.MC_MONITORS = RelationField("mcMonitors") +ADLSAccount.MC_INCIDENTS = RelationField("mcIncidents") +ADLSAccount.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +ADLSAccount.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +ADLSAccount.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +ADLSAccount.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +ADLSAccount.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +ADLSAccount.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +ADLSAccount.FILES = RelationField("files") +ADLSAccount.LINKS = RelationField("links") +ADLSAccount.README = RelationField("readme") +ADLSAccount.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +ADLSAccount.SODA_CHECKS = RelationField("sodaChecks") +ADLSAccount.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +ADLSAccount.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/adls_container.py b/pyatlan_v9/model/assets/adls_container.py new file mode 100644 index 000000000..3a9a19220 --- /dev/null +++ b/pyatlan_v9/model/assets/adls_container.py @@ -0,0 +1,771 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +ADLSContainer asset model with flattened inheritance. + +This module provides: +- ADLSContainer: Flat asset class (easy to use) +- ADLSContainerAttributes: Nested attributes struct (extends AssetAttributes) +- ADLSContainerNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .adls_related import RelatedADLSAccount, RelatedADLSObject + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class ADLSContainer(Asset): + """ + Instance of an Azure Data Lake Storage (ADLS) container in Atlan. + """ + + ADLS_CONTAINER_URL: ClassVar[Any] = None + ADLS_CONTAINER_LEASE_STATE: ClassVar[Any] = None + ADLS_CONTAINER_LEASE_STATUS: ClassVar[Any] = None + ADLS_CONTAINER_ENCRYPTION_SCOPE: ClassVar[Any] = None + ADLS_CONTAINER_VERSION_LEVEL_IMMUTABILITY_SUPPORT: ClassVar[Any] = None + ADLS_OBJECT_COUNT: ClassVar[Any] = None + ADLS_ACCOUNT_QUALIFIED_NAME: ClassVar[Any] = None + ADLS_ACCOUNT_NAME: ClassVar[Any] = None + AZURE_RESOURCE_ID: ClassVar[Any] = None + AZURE_LOCATION: ClassVar[Any] = None + ADLS_ACCOUNT_SECONDARY_LOCATION: ClassVar[Any] = None + AZURE_TAGS: ClassVar[Any] = None + CLOUD_UNIFORM_RESOURCE_NAME: ClassVar[Any] = None + ADLS_ACCOUNT: ClassVar[Any] = None + ADLS_OBJECTS: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "ADLSContainer" + + adls_container_url: Union[str, None, UnsetType] = UNSET + """URL of this container.""" + + adls_container_lease_state: Union[str, None, UnsetType] = UNSET + """Lease state of this container.""" + + adls_container_lease_status: Union[str, None, UnsetType] = UNSET + """Lease status of this container.""" + + adls_container_encryption_scope: Union[str, None, UnsetType] = UNSET + """Encryption scope of this container.""" + + adls_container_version_level_immutability_support: Union[bool, None, UnsetType] = ( + UNSET + ) + """Whether this container supports version-level immutability (true) or not (false).""" + + adls_object_count: Union[int, None, UnsetType] = UNSET + """Number of objects that exist within this container.""" + + adls_account_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the account for this ADLS asset.""" + + adls_account_name: Union[str, None, UnsetType] = UNSET + """Name of the account for this ADLS asset.""" + + azure_resource_id: Union[str, None, UnsetType] = UNSET + """Resource identifier of this asset in Azure.""" + + azure_location: Union[str, None, UnsetType] = UNSET + """Location of this asset in Azure.""" + + adls_account_secondary_location: Union[str, None, UnsetType] = UNSET + """Secondary location of the ADLS account.""" + + azure_tags: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """Tags that have been applied to this asset in Azure.""" + + cloud_uniform_resource_name: Union[str, None, UnsetType] = UNSET + """Uniform resource name (URN) for the asset: AWS ARN, Google Cloud URI, Azure resource ID, Oracle OCID, and so on.""" + + adls_account: Union[RelatedADLSAccount, None, UnsetType] = UNSET + """Account in which this container exists.""" + + adls_objects: Union[List[RelatedADLSObject], None, UnsetType] = UNSET + """Objects that exist within this container.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "ADLSContainer" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + adls_account_qualified_name: str, + connection_qualified_name: str | None = None, + ) -> "ADLSContainer": + validate_required_fields( + ["name", "adls_account_qualified_name"], + [name, adls_account_qualified_name], + ) + if connection_qualified_name: + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + else: + fields = adls_account_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + connection_qualified_name = ( + "/".join(fields[:3]) + if len(fields) >= 3 + else adls_account_qualified_name + ) + + adls_account_name = adls_account_qualified_name.rsplit("/", 1)[-1] + qualified_name = f"{adls_account_qualified_name}/{name}" + return cls( + name=name, + qualified_name=qualified_name, + adls_account_qualified_name=adls_account_qualified_name, + adls_account_name=adls_account_name, + connector_name=connector_name, + connection_qualified_name=connection_qualified_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "ADLSContainer": + """Create an ADLSContainer instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "ADLSContainer": + """Return only fields required for update operations.""" + return ADLSContainer.updater(qualified_name=self.qualified_name, name=self.name) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _adls_container_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> ADLSContainer: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + ADLSContainer instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _adls_container_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class ADLSContainerAttributes(AssetAttributes): + """ADLSContainer-specific attributes for nested API format.""" + + adls_container_url: Union[str, None, UnsetType] = UNSET + """URL of this container.""" + + adls_container_lease_state: Union[str, None, UnsetType] = UNSET + """Lease state of this container.""" + + adls_container_lease_status: Union[str, None, UnsetType] = UNSET + """Lease status of this container.""" + + adls_container_encryption_scope: Union[str, None, UnsetType] = UNSET + """Encryption scope of this container.""" + + adls_container_version_level_immutability_support: Union[bool, None, UnsetType] = ( + UNSET + ) + """Whether this container supports version-level immutability (true) or not (false).""" + + adls_object_count: Union[int, None, UnsetType] = UNSET + """Number of objects that exist within this container.""" + + adls_account_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the account for this ADLS asset.""" + + adls_account_name: Union[str, None, UnsetType] = UNSET + """Name of the account for this ADLS asset.""" + + azure_resource_id: Union[str, None, UnsetType] = UNSET + """Resource identifier of this asset in Azure.""" + + azure_location: Union[str, None, UnsetType] = UNSET + """Location of this asset in Azure.""" + + adls_account_secondary_location: Union[str, None, UnsetType] = UNSET + """Secondary location of the ADLS account.""" + + azure_tags: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """Tags that have been applied to this asset in Azure.""" + + cloud_uniform_resource_name: Union[str, None, UnsetType] = UNSET + """Uniform resource name (URN) for the asset: AWS ARN, Google Cloud URI, Azure resource ID, Oracle OCID, and so on.""" + + +class ADLSContainerRelationshipAttributes(AssetRelationshipAttributes): + """ADLSContainer-specific relationship attributes for nested API format.""" + + adls_account: Union[RelatedADLSAccount, None, UnsetType] = UNSET + """Account in which this container exists.""" + + adls_objects: Union[List[RelatedADLSObject], None, UnsetType] = UNSET + """Objects that exist within this container.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class ADLSContainerNested(AssetNested): + """ADLSContainer in nested API format for high-performance serialization.""" + + attributes: Union[ADLSContainerAttributes, UnsetType] = UNSET + relationship_attributes: Union[ADLSContainerRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + ADLSContainerRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + ADLSContainerRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_ADLS_CONTAINER_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "adls_account", + "adls_objects", + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_adls_container_attrs( + attrs: ADLSContainerAttributes, obj: ADLSContainer +) -> None: + """Populate ADLSContainer-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.adls_container_url = obj.adls_container_url + attrs.adls_container_lease_state = obj.adls_container_lease_state + attrs.adls_container_lease_status = obj.adls_container_lease_status + attrs.adls_container_encryption_scope = obj.adls_container_encryption_scope + attrs.adls_container_version_level_immutability_support = ( + obj.adls_container_version_level_immutability_support + ) + attrs.adls_object_count = obj.adls_object_count + attrs.adls_account_qualified_name = obj.adls_account_qualified_name + attrs.adls_account_name = obj.adls_account_name + attrs.azure_resource_id = obj.azure_resource_id + attrs.azure_location = obj.azure_location + attrs.adls_account_secondary_location = obj.adls_account_secondary_location + attrs.azure_tags = obj.azure_tags + attrs.cloud_uniform_resource_name = obj.cloud_uniform_resource_name + + +def _extract_adls_container_attrs(attrs: ADLSContainerAttributes) -> dict: + """Extract all ADLSContainer attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["adls_container_url"] = attrs.adls_container_url + result["adls_container_lease_state"] = attrs.adls_container_lease_state + result["adls_container_lease_status"] = attrs.adls_container_lease_status + result["adls_container_encryption_scope"] = attrs.adls_container_encryption_scope + result["adls_container_version_level_immutability_support"] = ( + attrs.adls_container_version_level_immutability_support + ) + result["adls_object_count"] = attrs.adls_object_count + result["adls_account_qualified_name"] = attrs.adls_account_qualified_name + result["adls_account_name"] = attrs.adls_account_name + result["azure_resource_id"] = attrs.azure_resource_id + result["azure_location"] = attrs.azure_location + result["adls_account_secondary_location"] = attrs.adls_account_secondary_location + result["azure_tags"] = attrs.azure_tags + result["cloud_uniform_resource_name"] = attrs.cloud_uniform_resource_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _adls_container_to_nested(adls_container: ADLSContainer) -> ADLSContainerNested: + """Convert flat ADLSContainer to nested format.""" + attrs = ADLSContainerAttributes() + _populate_adls_container_attrs(attrs, adls_container) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + adls_container, _ADLS_CONTAINER_REL_FIELDS, ADLSContainerRelationshipAttributes + ) + return ADLSContainerNested( + guid=adls_container.guid, + type_name=adls_container.type_name, + status=adls_container.status, + version=adls_container.version, + create_time=adls_container.create_time, + update_time=adls_container.update_time, + created_by=adls_container.created_by, + updated_by=adls_container.updated_by, + classifications=adls_container.classifications, + classification_names=adls_container.classification_names, + meanings=adls_container.meanings, + labels=adls_container.labels, + business_attributes=adls_container.business_attributes, + custom_attributes=adls_container.custom_attributes, + pending_tasks=adls_container.pending_tasks, + proxy=adls_container.proxy, + is_incomplete=adls_container.is_incomplete, + provenance_type=adls_container.provenance_type, + home_id=adls_container.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _adls_container_from_nested(nested: ADLSContainerNested) -> ADLSContainer: + """Convert nested format to flat ADLSContainer.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else ADLSContainerAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _ADLS_CONTAINER_REL_FIELDS, + ADLSContainerRelationshipAttributes, + ) + return ADLSContainer( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_adls_container_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _adls_container_to_nested_bytes( + adls_container: ADLSContainer, serde: Serde +) -> bytes: + """Convert flat ADLSContainer to nested JSON bytes.""" + return serde.encode(_adls_container_to_nested(adls_container)) + + +def _adls_container_from_nested_bytes(data: bytes, serde: Serde) -> ADLSContainer: + """Convert nested JSON bytes to flat ADLSContainer.""" + nested = serde.decode(data, ADLSContainerNested) + return _adls_container_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +ADLSContainer.ADLS_CONTAINER_URL = KeywordTextField( + "adlsContainerUrl", "adlsContainerUrl", "adlsContainerUrl.text" +) +ADLSContainer.ADLS_CONTAINER_LEASE_STATE = KeywordField( + "adlsContainerLeaseState", "adlsContainerLeaseState" +) +ADLSContainer.ADLS_CONTAINER_LEASE_STATUS = KeywordField( + "adlsContainerLeaseStatus", "adlsContainerLeaseStatus" +) +ADLSContainer.ADLS_CONTAINER_ENCRYPTION_SCOPE = KeywordField( + "adlsContainerEncryptionScope", "adlsContainerEncryptionScope" +) +ADLSContainer.ADLS_CONTAINER_VERSION_LEVEL_IMMUTABILITY_SUPPORT = BooleanField( + "adlsContainerVersionLevelImmutabilitySupport", + "adlsContainerVersionLevelImmutabilitySupport", +) +ADLSContainer.ADLS_OBJECT_COUNT = NumericField("adlsObjectCount", "adlsObjectCount") +ADLSContainer.ADLS_ACCOUNT_QUALIFIED_NAME = KeywordTextField( + "adlsAccountQualifiedName", + "adlsAccountQualifiedName", + "adlsAccountQualifiedName.text", +) +ADLSContainer.ADLS_ACCOUNT_NAME = KeywordField("adlsAccountName", "adlsAccountName") +ADLSContainer.AZURE_RESOURCE_ID = KeywordTextField( + "azureResourceId", "azureResourceId", "azureResourceId.text" +) +ADLSContainer.AZURE_LOCATION = KeywordField("azureLocation", "azureLocation") +ADLSContainer.ADLS_ACCOUNT_SECONDARY_LOCATION = KeywordField( + "adlsAccountSecondaryLocation", "adlsAccountSecondaryLocation" +) +ADLSContainer.AZURE_TAGS = KeywordField("azureTags", "azureTags") +ADLSContainer.CLOUD_UNIFORM_RESOURCE_NAME = KeywordField( + "cloudUniformResourceName", "cloudUniformResourceName" +) +ADLSContainer.ADLS_ACCOUNT = RelationField("adlsAccount") +ADLSContainer.ADLS_OBJECTS = RelationField("adlsObjects") +ADLSContainer.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +ADLSContainer.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +ADLSContainer.ANOMALO_CHECKS = RelationField("anomaloChecks") +ADLSContainer.APPLICATION = RelationField("application") +ADLSContainer.APPLICATION_FIELD = RelationField("applicationField") +ADLSContainer.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +ADLSContainer.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +ADLSContainer.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +ADLSContainer.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +ADLSContainer.METRICS = RelationField("metrics") +ADLSContainer.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +ADLSContainer.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +ADLSContainer.MEANINGS = RelationField("meanings") +ADLSContainer.MC_MONITORS = RelationField("mcMonitors") +ADLSContainer.MC_INCIDENTS = RelationField("mcIncidents") +ADLSContainer.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +ADLSContainer.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +ADLSContainer.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +ADLSContainer.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +ADLSContainer.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +ADLSContainer.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +ADLSContainer.FILES = RelationField("files") +ADLSContainer.LINKS = RelationField("links") +ADLSContainer.README = RelationField("readme") +ADLSContainer.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +ADLSContainer.SODA_CHECKS = RelationField("sodaChecks") +ADLSContainer.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +ADLSContainer.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/adls_object.py b/pyatlan_v9/model/assets/adls_object.py new file mode 100644 index 000000000..218627fc4 --- /dev/null +++ b/pyatlan_v9/model/assets/adls_object.py @@ -0,0 +1,984 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +ADLSObject asset model with flattened inheritance. + +This module provides: +- ADLSObject: Flat asset class (easy to use) +- ADLSObjectAttributes: Nested attributes struct (extends AssetAttributes) +- ADLSObjectNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan.model.utils import construct_object_key +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .adls_related import RelatedADLSContainer + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class ADLSObject(Asset): + """ + Instance of an Azure Data Lake Storage (ADLS) blob / object in Atlan. + """ + + ADLS_OBJECT_URL: ClassVar[Any] = None + ADLS_OBJECT_VERSION_ID: ClassVar[Any] = None + ADLS_OBJECT_TYPE: ClassVar[Any] = None + ADLS_OBJECT_SIZE: ClassVar[Any] = None + ADLS_OBJECT_KEY: ClassVar[Any] = None + ADLS_OBJECT_ACCESS_TIER: ClassVar[Any] = None + ADLS_OBJECT_ACCESS_TIER_LAST_MODIFIED_TIME: ClassVar[Any] = None + ADLS_OBJECT_ARCHIVE_STATUS: ClassVar[Any] = None + ADLS_OBJECT_SERVER_ENCRYPTED: ClassVar[Any] = None + ADLS_OBJECT_VERSION_LEVEL_IMMUTABILITY_SUPPORT: ClassVar[Any] = None + ADLS_OBJECT_CACHE_CONTROL: ClassVar[Any] = None + ADLS_OBJECT_CONTENT_TYPE: ClassVar[Any] = None + ADLS_OBJECT_CONTENT_MD5_HASH: ClassVar[Any] = None + ADLS_OBJECT_CONTENT_LANGUAGE: ClassVar[Any] = None + ADLS_OBJECT_LEASE_STATUS: ClassVar[Any] = None + ADLS_OBJECT_LEASE_STATE: ClassVar[Any] = None + ADLS_OBJECT_METADATA: ClassVar[Any] = None + ADLS_CONTAINER_QUALIFIED_NAME: ClassVar[Any] = None + ADLS_CONTAINER_NAME: ClassVar[Any] = None + ADLS_ACCOUNT_QUALIFIED_NAME: ClassVar[Any] = None + ADLS_ACCOUNT_NAME: ClassVar[Any] = None + AZURE_RESOURCE_ID: ClassVar[Any] = None + AZURE_LOCATION: ClassVar[Any] = None + ADLS_ACCOUNT_SECONDARY_LOCATION: ClassVar[Any] = None + AZURE_TAGS: ClassVar[Any] = None + CLOUD_UNIFORM_RESOURCE_NAME: ClassVar[Any] = None + ADLS_CONTAINER: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "ADLSObject" + + adls_object_url: Union[str, None, UnsetType] = UNSET + """URL of this object.""" + + adls_object_version_id: Union[str, None, UnsetType] = UNSET + """Identifier of the version of this object, from ADLS.""" + + adls_object_type: Union[str, None, UnsetType] = UNSET + """Type of this object.""" + + adls_object_size: Union[int, None, UnsetType] = UNSET + """Size of this object.""" + + adls_object_key: Union[str, None, UnsetType] = UNSET + """Key of this object, in ADLS.""" + + adls_object_access_tier: Union[str, None, UnsetType] = UNSET + """Access tier of this object.""" + + adls_object_access_tier_last_modified_time: Union[int, None, UnsetType] = UNSET + """Time (epoch) when the acccess tier for this object was last modified, in milliseconds.""" + + adls_object_archive_status: Union[str, None, UnsetType] = UNSET + """Archive status of this object.""" + + adls_object_server_encrypted: Union[bool, None, UnsetType] = UNSET + """Whether this object is server encrypted (true) or not (false).""" + + adls_object_version_level_immutability_support: Union[bool, None, UnsetType] = UNSET + """Whether this object supports version-level immutability (true) or not (false).""" + + adls_object_cache_control: Union[str, None, UnsetType] = UNSET + """Cache control of this object.""" + + adls_object_content_type: Union[str, None, UnsetType] = UNSET + """Content type of this object.""" + + adls_object_content_md5_hash: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="adlsObjectContentMD5Hash" + ) + """MD5 hash of this object's contents.""" + + adls_object_content_language: Union[str, None, UnsetType] = UNSET + """Language of this object's contents.""" + + adls_object_lease_status: Union[str, None, UnsetType] = UNSET + """Status of this object's lease.""" + + adls_object_lease_state: Union[str, None, UnsetType] = UNSET + """State of this object's lease.""" + + adls_object_metadata: Union[Dict[str, str], None, UnsetType] = UNSET + """Metadata associated with this object, from ADLS.""" + + adls_container_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the container this object exists within.""" + + adls_container_name: Union[str, None, UnsetType] = UNSET + """Name of the container this object exists within.""" + + adls_account_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the account for this ADLS asset.""" + + adls_account_name: Union[str, None, UnsetType] = UNSET + """Name of the account for this ADLS asset.""" + + azure_resource_id: Union[str, None, UnsetType] = UNSET + """Resource identifier of this asset in Azure.""" + + azure_location: Union[str, None, UnsetType] = UNSET + """Location of this asset in Azure.""" + + adls_account_secondary_location: Union[str, None, UnsetType] = UNSET + """Secondary location of the ADLS account.""" + + azure_tags: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """Tags that have been applied to this asset in Azure.""" + + cloud_uniform_resource_name: Union[str, None, UnsetType] = UNSET + """Uniform resource name (URN) for the asset: AWS ARN, Google Cloud URI, Azure resource ID, Oracle OCID, and so on.""" + + adls_container: Union[RelatedADLSContainer, None, UnsetType] = UNSET + """Container this object exists within.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "ADLSObject" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + adls_container_name: str, + adls_container_qualified_name: str, + adls_account_qualified_name: str | None = None, + connection_qualified_name: str | None = None, + ) -> "ADLSObject": + validate_required_fields( + ["name", "adls_container_name", "adls_container_qualified_name"], + [name, adls_container_name, adls_container_qualified_name], + ) + if connection_qualified_name: + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + else: + fields = adls_container_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + connection_qualified_name = ( + "/".join(fields[:3]) + if len(fields) >= 3 + else adls_container_qualified_name + ) + + # Derive account qualified name from container qualified name + if not adls_account_qualified_name: + parts = adls_container_qualified_name.rsplit("/", 1) + adls_account_qualified_name = ( + parts[0] if len(parts) > 1 else adls_container_qualified_name + ) + + qualified_name = f"{adls_container_qualified_name}/{name}" + return cls( + name=name, + qualified_name=qualified_name, + adls_container_qualified_name=adls_container_qualified_name, + adls_container_name=adls_container_name, + connector_name=connector_name, + connection_qualified_name=connection_qualified_name, + adls_account_qualified_name=adls_account_qualified_name, + adls_account_name=adls_account_qualified_name.rsplit("/", 1)[-1], + ) + + @classmethod + @init_guid + def creator_with_prefix( + cls, + *, + name: str, + connection_qualified_name: str, + adls_container_name: str, + adls_container_qualified_name: str, + adls_account_qualified_name: str | None = None, + prefix: str = "", + ) -> "ADLSObject": + validate_required_fields( + [ + "name", + "connection_qualified_name", + "adls_container_name", + "adls_container_qualified_name", + ], + [ + name, + connection_qualified_name, + adls_container_name, + adls_container_qualified_name, + ], + ) + + fields = connection_qualified_name.split("/") + if len(fields) != 3: + raise ValueError("Invalid connection_qualified_name") + if fields[0].replace(" ", "") == "" or fields[2].replace(" ", "") == "": + raise ValueError("Invalid connection_qualified_name") + if fields[1].lower() != "adls": + raise ValueError("Invalid connection_qualified_name") + connector_name = fields[1] + + if not adls_account_qualified_name: + parts = adls_container_qualified_name.rsplit("/", 1) + adls_account_qualified_name = ( + parts[0] if len(parts) > 1 else adls_container_qualified_name + ) + + object_key = construct_object_key(prefix, name) + qualified_name = f"{adls_container_qualified_name}/{object_key}" + return cls( + name=name, + qualified_name=qualified_name, + adls_object_key=object_key, + adls_container_qualified_name=adls_container_qualified_name, + adls_container_name=adls_container_name, + connector_name=connector_name, + connection_qualified_name=connection_qualified_name, + adls_account_qualified_name=adls_account_qualified_name, + adls_account_name=adls_account_qualified_name.rsplit("/", 1)[-1], + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "ADLSObject": + """Create an ADLSObject instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "ADLSObject": + """Return only fields required for update operations.""" + return ADLSObject.updater(qualified_name=self.qualified_name, name=self.name) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _adls_object_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> ADLSObject: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + ADLSObject instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _adls_object_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class ADLSObjectAttributes(AssetAttributes): + """ADLSObject-specific attributes for nested API format.""" + + adls_object_url: Union[str, None, UnsetType] = UNSET + """URL of this object.""" + + adls_object_version_id: Union[str, None, UnsetType] = UNSET + """Identifier of the version of this object, from ADLS.""" + + adls_object_type: Union[str, None, UnsetType] = UNSET + """Type of this object.""" + + adls_object_size: Union[int, None, UnsetType] = UNSET + """Size of this object.""" + + adls_object_key: Union[str, None, UnsetType] = UNSET + """Key of this object, in ADLS.""" + + adls_object_access_tier: Union[str, None, UnsetType] = UNSET + """Access tier of this object.""" + + adls_object_access_tier_last_modified_time: Union[int, None, UnsetType] = UNSET + """Time (epoch) when the acccess tier for this object was last modified, in milliseconds.""" + + adls_object_archive_status: Union[str, None, UnsetType] = UNSET + """Archive status of this object.""" + + adls_object_server_encrypted: Union[bool, None, UnsetType] = UNSET + """Whether this object is server encrypted (true) or not (false).""" + + adls_object_version_level_immutability_support: Union[bool, None, UnsetType] = UNSET + """Whether this object supports version-level immutability (true) or not (false).""" + + adls_object_cache_control: Union[str, None, UnsetType] = UNSET + """Cache control of this object.""" + + adls_object_content_type: Union[str, None, UnsetType] = UNSET + """Content type of this object.""" + + adls_object_content_md5_hash: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="adlsObjectContentMD5Hash" + ) + """MD5 hash of this object's contents.""" + + adls_object_content_language: Union[str, None, UnsetType] = UNSET + """Language of this object's contents.""" + + adls_object_lease_status: Union[str, None, UnsetType] = UNSET + """Status of this object's lease.""" + + adls_object_lease_state: Union[str, None, UnsetType] = UNSET + """State of this object's lease.""" + + adls_object_metadata: Union[Dict[str, str], None, UnsetType] = UNSET + """Metadata associated with this object, from ADLS.""" + + adls_container_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the container this object exists within.""" + + adls_container_name: Union[str, None, UnsetType] = UNSET + """Name of the container this object exists within.""" + + adls_account_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the account for this ADLS asset.""" + + adls_account_name: Union[str, None, UnsetType] = UNSET + """Name of the account for this ADLS asset.""" + + azure_resource_id: Union[str, None, UnsetType] = UNSET + """Resource identifier of this asset in Azure.""" + + azure_location: Union[str, None, UnsetType] = UNSET + """Location of this asset in Azure.""" + + adls_account_secondary_location: Union[str, None, UnsetType] = UNSET + """Secondary location of the ADLS account.""" + + azure_tags: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """Tags that have been applied to this asset in Azure.""" + + cloud_uniform_resource_name: Union[str, None, UnsetType] = UNSET + """Uniform resource name (URN) for the asset: AWS ARN, Google Cloud URI, Azure resource ID, Oracle OCID, and so on.""" + + +class ADLSObjectRelationshipAttributes(AssetRelationshipAttributes): + """ADLSObject-specific relationship attributes for nested API format.""" + + adls_container: Union[RelatedADLSContainer, None, UnsetType] = UNSET + """Container this object exists within.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class ADLSObjectNested(AssetNested): + """ADLSObject in nested API format for high-performance serialization.""" + + attributes: Union[ADLSObjectAttributes, UnsetType] = UNSET + relationship_attributes: Union[ADLSObjectRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + ADLSObjectRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + ADLSObjectRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_ADLS_OBJECT_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "adls_container", + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_adls_object_attrs(attrs: ADLSObjectAttributes, obj: ADLSObject) -> None: + """Populate ADLSObject-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.adls_object_url = obj.adls_object_url + attrs.adls_object_version_id = obj.adls_object_version_id + attrs.adls_object_type = obj.adls_object_type + attrs.adls_object_size = obj.adls_object_size + attrs.adls_object_key = obj.adls_object_key + attrs.adls_object_access_tier = obj.adls_object_access_tier + attrs.adls_object_access_tier_last_modified_time = ( + obj.adls_object_access_tier_last_modified_time + ) + attrs.adls_object_archive_status = obj.adls_object_archive_status + attrs.adls_object_server_encrypted = obj.adls_object_server_encrypted + attrs.adls_object_version_level_immutability_support = ( + obj.adls_object_version_level_immutability_support + ) + attrs.adls_object_cache_control = obj.adls_object_cache_control + attrs.adls_object_content_type = obj.adls_object_content_type + attrs.adls_object_content_md5_hash = obj.adls_object_content_md5_hash + attrs.adls_object_content_language = obj.adls_object_content_language + attrs.adls_object_lease_status = obj.adls_object_lease_status + attrs.adls_object_lease_state = obj.adls_object_lease_state + attrs.adls_object_metadata = obj.adls_object_metadata + attrs.adls_container_qualified_name = obj.adls_container_qualified_name + attrs.adls_container_name = obj.adls_container_name + attrs.adls_account_qualified_name = obj.adls_account_qualified_name + attrs.adls_account_name = obj.adls_account_name + attrs.azure_resource_id = obj.azure_resource_id + attrs.azure_location = obj.azure_location + attrs.adls_account_secondary_location = obj.adls_account_secondary_location + attrs.azure_tags = obj.azure_tags + attrs.cloud_uniform_resource_name = obj.cloud_uniform_resource_name + + +def _extract_adls_object_attrs(attrs: ADLSObjectAttributes) -> dict: + """Extract all ADLSObject attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["adls_object_url"] = attrs.adls_object_url + result["adls_object_version_id"] = attrs.adls_object_version_id + result["adls_object_type"] = attrs.adls_object_type + result["adls_object_size"] = attrs.adls_object_size + result["adls_object_key"] = attrs.adls_object_key + result["adls_object_access_tier"] = attrs.adls_object_access_tier + result["adls_object_access_tier_last_modified_time"] = ( + attrs.adls_object_access_tier_last_modified_time + ) + result["adls_object_archive_status"] = attrs.adls_object_archive_status + result["adls_object_server_encrypted"] = attrs.adls_object_server_encrypted + result["adls_object_version_level_immutability_support"] = ( + attrs.adls_object_version_level_immutability_support + ) + result["adls_object_cache_control"] = attrs.adls_object_cache_control + result["adls_object_content_type"] = attrs.adls_object_content_type + result["adls_object_content_md5_hash"] = attrs.adls_object_content_md5_hash + result["adls_object_content_language"] = attrs.adls_object_content_language + result["adls_object_lease_status"] = attrs.adls_object_lease_status + result["adls_object_lease_state"] = attrs.adls_object_lease_state + result["adls_object_metadata"] = attrs.adls_object_metadata + result["adls_container_qualified_name"] = attrs.adls_container_qualified_name + result["adls_container_name"] = attrs.adls_container_name + result["adls_account_qualified_name"] = attrs.adls_account_qualified_name + result["adls_account_name"] = attrs.adls_account_name + result["azure_resource_id"] = attrs.azure_resource_id + result["azure_location"] = attrs.azure_location + result["adls_account_secondary_location"] = attrs.adls_account_secondary_location + result["azure_tags"] = attrs.azure_tags + result["cloud_uniform_resource_name"] = attrs.cloud_uniform_resource_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _adls_object_to_nested(adls_object: ADLSObject) -> ADLSObjectNested: + """Convert flat ADLSObject to nested format.""" + attrs = ADLSObjectAttributes() + _populate_adls_object_attrs(attrs, adls_object) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + adls_object, _ADLS_OBJECT_REL_FIELDS, ADLSObjectRelationshipAttributes + ) + return ADLSObjectNested( + guid=adls_object.guid, + type_name=adls_object.type_name, + status=adls_object.status, + version=adls_object.version, + create_time=adls_object.create_time, + update_time=adls_object.update_time, + created_by=adls_object.created_by, + updated_by=adls_object.updated_by, + classifications=adls_object.classifications, + classification_names=adls_object.classification_names, + meanings=adls_object.meanings, + labels=adls_object.labels, + business_attributes=adls_object.business_attributes, + custom_attributes=adls_object.custom_attributes, + pending_tasks=adls_object.pending_tasks, + proxy=adls_object.proxy, + is_incomplete=adls_object.is_incomplete, + provenance_type=adls_object.provenance_type, + home_id=adls_object.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _adls_object_from_nested(nested: ADLSObjectNested) -> ADLSObject: + """Convert nested format to flat ADLSObject.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else ADLSObjectAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _ADLS_OBJECT_REL_FIELDS, + ADLSObjectRelationshipAttributes, + ) + return ADLSObject( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_adls_object_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _adls_object_to_nested_bytes(adls_object: ADLSObject, serde: Serde) -> bytes: + """Convert flat ADLSObject to nested JSON bytes.""" + return serde.encode(_adls_object_to_nested(adls_object)) + + +def _adls_object_from_nested_bytes(data: bytes, serde: Serde) -> ADLSObject: + """Convert nested JSON bytes to flat ADLSObject.""" + nested = serde.decode(data, ADLSObjectNested) + return _adls_object_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +ADLSObject.ADLS_OBJECT_URL = KeywordTextField( + "adlsObjectUrl", "adlsObjectUrl", "adlsObjectUrl.text" +) +ADLSObject.ADLS_OBJECT_VERSION_ID = KeywordField( + "adlsObjectVersionId", "adlsObjectVersionId" +) +ADLSObject.ADLS_OBJECT_TYPE = KeywordField("adlsObjectType", "adlsObjectType") +ADLSObject.ADLS_OBJECT_SIZE = NumericField("adlsObjectSize", "adlsObjectSize") +ADLSObject.ADLS_OBJECT_KEY = KeywordTextField( + "adlsObjectKey", "adlsObjectKey", "adlsObjectKey.text" +) +ADLSObject.ADLS_OBJECT_ACCESS_TIER = KeywordField( + "adlsObjectAccessTier", "adlsObjectAccessTier" +) +ADLSObject.ADLS_OBJECT_ACCESS_TIER_LAST_MODIFIED_TIME = NumericField( + "adlsObjectAccessTierLastModifiedTime", "adlsObjectAccessTierLastModifiedTime" +) +ADLSObject.ADLS_OBJECT_ARCHIVE_STATUS = KeywordField( + "adlsObjectArchiveStatus", "adlsObjectArchiveStatus" +) +ADLSObject.ADLS_OBJECT_SERVER_ENCRYPTED = BooleanField( + "adlsObjectServerEncrypted", "adlsObjectServerEncrypted" +) +ADLSObject.ADLS_OBJECT_VERSION_LEVEL_IMMUTABILITY_SUPPORT = BooleanField( + "adlsObjectVersionLevelImmutabilitySupport", + "adlsObjectVersionLevelImmutabilitySupport", +) +ADLSObject.ADLS_OBJECT_CACHE_CONTROL = KeywordField( + "adlsObjectCacheControl", "adlsObjectCacheControl" +) +ADLSObject.ADLS_OBJECT_CONTENT_TYPE = KeywordField( + "adlsObjectContentType", "adlsObjectContentType" +) +ADLSObject.ADLS_OBJECT_CONTENT_MD5_HASH = KeywordField( + "adlsObjectContentMD5Hash", "adlsObjectContentMD5Hash" +) +ADLSObject.ADLS_OBJECT_CONTENT_LANGUAGE = KeywordTextField( + "adlsObjectContentLanguage", + "adlsObjectContentLanguage", + "adlsObjectContentLanguage.text", +) +ADLSObject.ADLS_OBJECT_LEASE_STATUS = KeywordField( + "adlsObjectLeaseStatus", "adlsObjectLeaseStatus" +) +ADLSObject.ADLS_OBJECT_LEASE_STATE = KeywordField( + "adlsObjectLeaseState", "adlsObjectLeaseState" +) +ADLSObject.ADLS_OBJECT_METADATA = KeywordField( + "adlsObjectMetadata", "adlsObjectMetadata" +) +ADLSObject.ADLS_CONTAINER_QUALIFIED_NAME = KeywordTextField( + "adlsContainerQualifiedName", + "adlsContainerQualifiedName", + "adlsContainerQualifiedName.text", +) +ADLSObject.ADLS_CONTAINER_NAME = KeywordField("adlsContainerName", "adlsContainerName") +ADLSObject.ADLS_ACCOUNT_QUALIFIED_NAME = KeywordTextField( + "adlsAccountQualifiedName", + "adlsAccountQualifiedName", + "adlsAccountQualifiedName.text", +) +ADLSObject.ADLS_ACCOUNT_NAME = KeywordField("adlsAccountName", "adlsAccountName") +ADLSObject.AZURE_RESOURCE_ID = KeywordTextField( + "azureResourceId", "azureResourceId", "azureResourceId.text" +) +ADLSObject.AZURE_LOCATION = KeywordField("azureLocation", "azureLocation") +ADLSObject.ADLS_ACCOUNT_SECONDARY_LOCATION = KeywordField( + "adlsAccountSecondaryLocation", "adlsAccountSecondaryLocation" +) +ADLSObject.AZURE_TAGS = KeywordField("azureTags", "azureTags") +ADLSObject.CLOUD_UNIFORM_RESOURCE_NAME = KeywordField( + "cloudUniformResourceName", "cloudUniformResourceName" +) +ADLSObject.ADLS_CONTAINER = RelationField("adlsContainer") +ADLSObject.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +ADLSObject.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +ADLSObject.ANOMALO_CHECKS = RelationField("anomaloChecks") +ADLSObject.APPLICATION = RelationField("application") +ADLSObject.APPLICATION_FIELD = RelationField("applicationField") +ADLSObject.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +ADLSObject.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +ADLSObject.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +ADLSObject.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +ADLSObject.METRICS = RelationField("metrics") +ADLSObject.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +ADLSObject.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +ADLSObject.MEANINGS = RelationField("meanings") +ADLSObject.MC_MONITORS = RelationField("mcMonitors") +ADLSObject.MC_INCIDENTS = RelationField("mcIncidents") +ADLSObject.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +ADLSObject.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +ADLSObject.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +ADLSObject.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +ADLSObject.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +ADLSObject.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +ADLSObject.FILES = RelationField("files") +ADLSObject.LINKS = RelationField("links") +ADLSObject.README = RelationField("readme") +ADLSObject.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +ADLSObject.SODA_CHECKS = RelationField("sodaChecks") +ADLSObject.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +ADLSObject.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/adls_related.py b/pyatlan_v9/model/assets/adls_related.py new file mode 100644 index 000000000..a047dc67a --- /dev/null +++ b/pyatlan_v9/model/assets/adls_related.py @@ -0,0 +1,204 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for ADLS module. + +This module contains all Related{Type} classes for the ADLS type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Dict, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedObjectStore +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedADLS", + "RelatedADLSAccount", + "RelatedADLSContainer", + "RelatedADLSObject", +] + + +class RelatedADLS(RelatedObjectStore): + """ + Related entity reference for ADLS assets. + + Extends RelatedObjectStore with ADLS-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "ADLS" so it serializes correctly + + adls_account_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the account for this ADLS asset.""" + + adls_account_name: Union[str, None, UnsetType] = UNSET + """Name of the account for this ADLS asset.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "ADLS" + + +class RelatedADLSAccount(RelatedADLS): + """ + Related entity reference for ADLSAccount assets. + + Extends RelatedADLS with ADLSAccount-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "ADLSAccount" so it serializes correctly + + adls_etag: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="adlsETag" + ) + """Entity tag for the asset. An entity tag is a hash of the object and represents changes to the contents of an object only, not its metadata.""" + + adls_encryption_type: Union[str, None, UnsetType] = UNSET + """Type of encryption for this account.""" + + adls_account_resource_group: Union[str, None, UnsetType] = UNSET + """Resource group for this account.""" + + adls_account_subscription: Union[str, None, UnsetType] = UNSET + """Subscription for this account.""" + + adls_account_performance: Union[str, None, UnsetType] = UNSET + """Performance of this account.""" + + adls_account_replication: Union[str, None, UnsetType] = UNSET + """Replication of this account.""" + + adls_account_kind: Union[str, None, UnsetType] = UNSET + """Kind of this account.""" + + adls_primary_disk_state: Union[str, None, UnsetType] = UNSET + """Primary disk state of this account.""" + + adls_account_provision_state: Union[str, None, UnsetType] = UNSET + """Provision state of this account.""" + + adls_account_access_tier: Union[str, None, UnsetType] = UNSET + """Access tier of this account.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "ADLSAccount" + + +class RelatedADLSContainer(RelatedADLS): + """ + Related entity reference for ADLSContainer assets. + + Extends RelatedADLS with ADLSContainer-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "ADLSContainer" so it serializes correctly + + adls_container_url: Union[str, None, UnsetType] = UNSET + """URL of this container.""" + + adls_container_lease_state: Union[str, None, UnsetType] = UNSET + """Lease state of this container.""" + + adls_container_lease_status: Union[str, None, UnsetType] = UNSET + """Lease status of this container.""" + + adls_container_encryption_scope: Union[str, None, UnsetType] = UNSET + """Encryption scope of this container.""" + + adls_container_version_level_immutability_support: Union[bool, None, UnsetType] = ( + UNSET + ) + """Whether this container supports version-level immutability (true) or not (false).""" + + adls_object_count: Union[int, None, UnsetType] = UNSET + """Number of objects that exist within this container.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "ADLSContainer" + + +class RelatedADLSObject(RelatedADLS): + """ + Related entity reference for ADLSObject assets. + + Extends RelatedADLS with ADLSObject-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "ADLSObject" so it serializes correctly + + adls_object_url: Union[str, None, UnsetType] = UNSET + """URL of this object.""" + + adls_object_version_id: Union[str, None, UnsetType] = UNSET + """Identifier of the version of this object, from ADLS.""" + + adls_object_type: Union[str, None, UnsetType] = UNSET + """Type of this object.""" + + adls_object_size: Union[int, None, UnsetType] = UNSET + """Size of this object.""" + + adls_object_key: Union[str, None, UnsetType] = UNSET + """Key of this object, in ADLS.""" + + adls_object_access_tier: Union[str, None, UnsetType] = UNSET + """Access tier of this object.""" + + adls_object_access_tier_last_modified_time: Union[int, None, UnsetType] = UNSET + """Time (epoch) when the acccess tier for this object was last modified, in milliseconds.""" + + adls_object_archive_status: Union[str, None, UnsetType] = UNSET + """Archive status of this object.""" + + adls_object_server_encrypted: Union[bool, None, UnsetType] = UNSET + """Whether this object is server encrypted (true) or not (false).""" + + adls_object_version_level_immutability_support: Union[bool, None, UnsetType] = UNSET + """Whether this object supports version-level immutability (true) or not (false).""" + + adls_object_cache_control: Union[str, None, UnsetType] = UNSET + """Cache control of this object.""" + + adls_object_content_type: Union[str, None, UnsetType] = UNSET + """Content type of this object.""" + + adls_object_content_md5_hash: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="adlsObjectContentMD5Hash" + ) + """MD5 hash of this object's contents.""" + + adls_object_content_language: Union[str, None, UnsetType] = UNSET + """Language of this object's contents.""" + + adls_object_lease_status: Union[str, None, UnsetType] = UNSET + """Status of this object's lease.""" + + adls_object_lease_state: Union[str, None, UnsetType] = UNSET + """State of this object's lease.""" + + adls_object_metadata: Union[Dict[str, str], None, UnsetType] = UNSET + """Metadata associated with this object, from ADLS.""" + + adls_container_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the container this object exists within.""" + + adls_container_name: Union[str, None, UnsetType] = UNSET + """Name of the container this object exists within.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "ADLSObject" diff --git a/pyatlan_v9/model/assets/ai.py b/pyatlan_v9/model/assets/ai.py new file mode 100644 index 000000000..3ad4d2144 --- /dev/null +++ b/pyatlan_v9/model/assets/ai.py @@ -0,0 +1,645 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +AI asset model with flattened inheritance. + +This module provides: +- AI: Flat asset class (easy to use) +- AIAttributes: Nested attributes struct (extends AssetAttributes) +- AINested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class AI(Asset): + """ + Base class for AI assets. + """ + + ETHICAL_AI_PRIVACY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_FAIRNESS_CONFIG: ClassVar[Any] = None + ETHICAL_AI_BIAS_MITIGATION_CONFIG: ClassVar[Any] = None + ETHICAL_AI_RELIABILITY_AND_SAFETY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_TRANSPARENCY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_ACCOUNTABILITY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_ENVIRONMENTAL_CONSCIOUSNESS_CONFIG: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "AI" + + ethical_ai_privacy_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIPrivacyConfig" + ) + """Privacy configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_fairness_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIFairnessConfig" + ) + """Fairness configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_bias_mitigation_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIBiasMitigationConfig" + ) + """Bias mitigation configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_reliability_and_safety_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIReliabilityAndSafetyConfig") + ) + """Reliability and safety configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_transparency_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAITransparencyConfig" + ) + """Transparency configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_accountability_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIAccountabilityConfig" + ) + """Accountability configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_environmental_consciousness_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIEnvironmentalConsciousnessConfig") + ) + """Environmental consciousness configuration for ensuring the ethical use of an AI asset""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "AI" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _ai_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> AI: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + AI instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _ai_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class AIAttributes(AssetAttributes): + """AI-specific attributes for nested API format.""" + + ethical_ai_privacy_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIPrivacyConfig" + ) + """Privacy configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_fairness_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIFairnessConfig" + ) + """Fairness configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_bias_mitigation_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIBiasMitigationConfig" + ) + """Bias mitigation configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_reliability_and_safety_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIReliabilityAndSafetyConfig") + ) + """Reliability and safety configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_transparency_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAITransparencyConfig" + ) + """Transparency configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_accountability_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIAccountabilityConfig" + ) + """Accountability configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_environmental_consciousness_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIEnvironmentalConsciousnessConfig") + ) + """Environmental consciousness configuration for ensuring the ethical use of an AI asset""" + + +class AIRelationshipAttributes(AssetRelationshipAttributes): + """AI-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class AINested(AssetNested): + """AI in nested API format for high-performance serialization.""" + + attributes: Union[AIAttributes, UnsetType] = UNSET + relationship_attributes: Union[AIRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[AIRelationshipAttributes, UnsetType] = UNSET + remove_relationship_attributes: Union[AIRelationshipAttributes, UnsetType] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_AI_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_ai_attrs(attrs: AIAttributes, obj: AI) -> None: + """Populate AI-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.ethical_ai_privacy_config = obj.ethical_ai_privacy_config + attrs.ethical_ai_fairness_config = obj.ethical_ai_fairness_config + attrs.ethical_ai_bias_mitigation_config = obj.ethical_ai_bias_mitigation_config + attrs.ethical_ai_reliability_and_safety_config = ( + obj.ethical_ai_reliability_and_safety_config + ) + attrs.ethical_ai_transparency_config = obj.ethical_ai_transparency_config + attrs.ethical_ai_accountability_config = obj.ethical_ai_accountability_config + attrs.ethical_ai_environmental_consciousness_config = ( + obj.ethical_ai_environmental_consciousness_config + ) + + +def _extract_ai_attrs(attrs: AIAttributes) -> dict: + """Extract all AI attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["ethical_ai_privacy_config"] = attrs.ethical_ai_privacy_config + result["ethical_ai_fairness_config"] = attrs.ethical_ai_fairness_config + result["ethical_ai_bias_mitigation_config"] = ( + attrs.ethical_ai_bias_mitigation_config + ) + result["ethical_ai_reliability_and_safety_config"] = ( + attrs.ethical_ai_reliability_and_safety_config + ) + result["ethical_ai_transparency_config"] = attrs.ethical_ai_transparency_config + result["ethical_ai_accountability_config"] = attrs.ethical_ai_accountability_config + result["ethical_ai_environmental_consciousness_config"] = ( + attrs.ethical_ai_environmental_consciousness_config + ) + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _ai_to_nested(ai: AI) -> AINested: + """Convert flat AI to nested format.""" + attrs = AIAttributes() + _populate_ai_attrs(attrs, ai) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + ai, _AI_REL_FIELDS, AIRelationshipAttributes + ) + return AINested( + guid=ai.guid, + type_name=ai.type_name, + status=ai.status, + version=ai.version, + create_time=ai.create_time, + update_time=ai.update_time, + created_by=ai.created_by, + updated_by=ai.updated_by, + classifications=ai.classifications, + classification_names=ai.classification_names, + meanings=ai.meanings, + labels=ai.labels, + business_attributes=ai.business_attributes, + custom_attributes=ai.custom_attributes, + pending_tasks=ai.pending_tasks, + proxy=ai.proxy, + is_incomplete=ai.is_incomplete, + provenance_type=ai.provenance_type, + home_id=ai.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _ai_from_nested(nested: AINested) -> AI: + """Convert nested format to flat AI.""" + attrs = nested.attributes if nested.attributes is not UNSET else AIAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _AI_REL_FIELDS, + AIRelationshipAttributes, + ) + return AI( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_ai_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _ai_to_nested_bytes(ai: AI, serde: Serde) -> bytes: + """Convert flat AI to nested JSON bytes.""" + return serde.encode(_ai_to_nested(ai)) + + +def _ai_from_nested_bytes(data: bytes, serde: Serde) -> AI: + """Convert nested JSON bytes to flat AI.""" + nested = serde.decode(data, AINested) + return _ai_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +AI.ETHICAL_AI_PRIVACY_CONFIG = KeywordField( + "ethicalAIPrivacyConfig", "ethicalAIPrivacyConfig" +) +AI.ETHICAL_AI_FAIRNESS_CONFIG = KeywordField( + "ethicalAIFairnessConfig", "ethicalAIFairnessConfig" +) +AI.ETHICAL_AI_BIAS_MITIGATION_CONFIG = KeywordField( + "ethicalAIBiasMitigationConfig", "ethicalAIBiasMitigationConfig" +) +AI.ETHICAL_AI_RELIABILITY_AND_SAFETY_CONFIG = KeywordField( + "ethicalAIReliabilityAndSafetyConfig", "ethicalAIReliabilityAndSafetyConfig" +) +AI.ETHICAL_AI_TRANSPARENCY_CONFIG = KeywordField( + "ethicalAITransparencyConfig", "ethicalAITransparencyConfig" +) +AI.ETHICAL_AI_ACCOUNTABILITY_CONFIG = KeywordField( + "ethicalAIAccountabilityConfig", "ethicalAIAccountabilityConfig" +) +AI.ETHICAL_AI_ENVIRONMENTAL_CONSCIOUSNESS_CONFIG = KeywordField( + "ethicalAIEnvironmentalConsciousnessConfig", + "ethicalAIEnvironmentalConsciousnessConfig", +) +AI.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +AI.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +AI.ANOMALO_CHECKS = RelationField("anomaloChecks") +AI.APPLICATION = RelationField("application") +AI.APPLICATION_FIELD = RelationField("applicationField") +AI.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +AI.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +AI.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +AI.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +AI.METRICS = RelationField("metrics") +AI.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +AI.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +AI.MEANINGS = RelationField("meanings") +AI.MC_MONITORS = RelationField("mcMonitors") +AI.MC_INCIDENTS = RelationField("mcIncidents") +AI.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +AI.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +AI.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +AI.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +AI.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +AI.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +AI.FILES = RelationField("files") +AI.LINKS = RelationField("links") +AI.README = RelationField("readme") +AI.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +AI.SODA_CHECKS = RelationField("sodaChecks") +AI.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +AI.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/ai_application.py b/pyatlan_v9/model/assets/ai_application.py new file mode 100644 index 000000000..90046e1bd --- /dev/null +++ b/pyatlan_v9/model/assets/ai_application.py @@ -0,0 +1,734 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +AIApplication asset model with flattened inheritance. + +This module provides: +- AIApplication: Flat asset class (easy to use) +- AIApplicationAttributes: Nested attributes struct (extends AssetAttributes) +- AIApplicationNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan.model.enums import AtlanConnectorType +from pyatlan.utils import to_camel_case +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .ai_related import RelatedAIModel + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class AIApplication(Asset): + """ + Instance of an AI application in Atlan. + """ + + AI_APPLICATION_VERSION: ClassVar[Any] = None + AI_APPLICATION_DEVELOPMENT_STAGE: ClassVar[Any] = None + ETHICAL_AI_PRIVACY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_FAIRNESS_CONFIG: ClassVar[Any] = None + ETHICAL_AI_BIAS_MITIGATION_CONFIG: ClassVar[Any] = None + ETHICAL_AI_RELIABILITY_AND_SAFETY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_TRANSPARENCY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_ACCOUNTABILITY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_ENVIRONMENTAL_CONSCIOUSNESS_CONFIG: ClassVar[Any] = None + MODELS: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "AIApplication" + + ai_application_version: Union[str, None, UnsetType] = UNSET + """Version of the AI application""" + + ai_application_development_stage: Union[str, None, UnsetType] = UNSET + """Development stage of the AI application""" + + ethical_ai_privacy_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIPrivacyConfig" + ) + """Privacy configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_fairness_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIFairnessConfig" + ) + """Fairness configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_bias_mitigation_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIBiasMitigationConfig" + ) + """Bias mitigation configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_reliability_and_safety_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIReliabilityAndSafetyConfig") + ) + """Reliability and safety configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_transparency_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAITransparencyConfig" + ) + """Transparency configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_accountability_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIAccountabilityConfig" + ) + """Accountability configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_environmental_consciousness_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIEnvironmentalConsciousnessConfig") + ) + """Environmental consciousness configuration for ensuring the ethical use of an AI asset""" + + models: Union[List[RelatedAIModel], None, UnsetType] = UNSET + """AI models that are used in this AI application.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "AIApplication" + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + ai_application_version: str, + ai_application_development_stage: str, + owner_groups: Union[set[str], None] = None, + owner_users: Union[set[str], None] = None, + ) -> "AIApplication": + """Create a new AIApplication asset.""" + validate_required_fields( + ["name", "ai_application_version", "ai_application_development_stage"], + [name, ai_application_version, ai_application_development_stage], + ) + name_camel_case = to_camel_case(name) + return cls( + name=name, + qualified_name=f"default/ai/aiapplication/{name_camel_case}", + connector_name=AtlanConnectorType.AI.value, + ai_application_version=ai_application_version, + ai_application_development_stage=ai_application_development_stage, + owner_groups=owner_groups if owner_groups is not None else UNSET, + owner_users=owner_users if owner_users is not None else UNSET, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "AIApplication": + """Create an AIApplication instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "AIApplication": + """Return only fields required for update operations.""" + return AIApplication.updater(qualified_name=self.qualified_name, name=self.name) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _ai_application_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> AIApplication: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + AIApplication instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _ai_application_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class AIApplicationAttributes(AssetAttributes): + """AIApplication-specific attributes for nested API format.""" + + ai_application_version: Union[str, None, UnsetType] = UNSET + """Version of the AI application""" + + ai_application_development_stage: Union[str, None, UnsetType] = UNSET + """Development stage of the AI application""" + + ethical_ai_privacy_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIPrivacyConfig" + ) + """Privacy configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_fairness_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIFairnessConfig" + ) + """Fairness configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_bias_mitigation_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIBiasMitigationConfig" + ) + """Bias mitigation configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_reliability_and_safety_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIReliabilityAndSafetyConfig") + ) + """Reliability and safety configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_transparency_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAITransparencyConfig" + ) + """Transparency configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_accountability_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIAccountabilityConfig" + ) + """Accountability configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_environmental_consciousness_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIEnvironmentalConsciousnessConfig") + ) + """Environmental consciousness configuration for ensuring the ethical use of an AI asset""" + + +class AIApplicationRelationshipAttributes(AssetRelationshipAttributes): + """AIApplication-specific relationship attributes for nested API format.""" + + models: Union[List[RelatedAIModel], None, UnsetType] = UNSET + """AI models that are used in this AI application.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class AIApplicationNested(AssetNested): + """AIApplication in nested API format for high-performance serialization.""" + + attributes: Union[AIApplicationAttributes, UnsetType] = UNSET + relationship_attributes: Union[AIApplicationRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + AIApplicationRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + AIApplicationRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_AI_APPLICATION_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "models", + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_ai_application_attrs( + attrs: AIApplicationAttributes, obj: AIApplication +) -> None: + """Populate AIApplication-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.ai_application_version = obj.ai_application_version + attrs.ai_application_development_stage = obj.ai_application_development_stage + attrs.ethical_ai_privacy_config = obj.ethical_ai_privacy_config + attrs.ethical_ai_fairness_config = obj.ethical_ai_fairness_config + attrs.ethical_ai_bias_mitigation_config = obj.ethical_ai_bias_mitigation_config + attrs.ethical_ai_reliability_and_safety_config = ( + obj.ethical_ai_reliability_and_safety_config + ) + attrs.ethical_ai_transparency_config = obj.ethical_ai_transparency_config + attrs.ethical_ai_accountability_config = obj.ethical_ai_accountability_config + attrs.ethical_ai_environmental_consciousness_config = ( + obj.ethical_ai_environmental_consciousness_config + ) + + +def _extract_ai_application_attrs(attrs: AIApplicationAttributes) -> dict: + """Extract all AIApplication attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["ai_application_version"] = attrs.ai_application_version + result["ai_application_development_stage"] = attrs.ai_application_development_stage + result["ethical_ai_privacy_config"] = attrs.ethical_ai_privacy_config + result["ethical_ai_fairness_config"] = attrs.ethical_ai_fairness_config + result["ethical_ai_bias_mitigation_config"] = ( + attrs.ethical_ai_bias_mitigation_config + ) + result["ethical_ai_reliability_and_safety_config"] = ( + attrs.ethical_ai_reliability_and_safety_config + ) + result["ethical_ai_transparency_config"] = attrs.ethical_ai_transparency_config + result["ethical_ai_accountability_config"] = attrs.ethical_ai_accountability_config + result["ethical_ai_environmental_consciousness_config"] = ( + attrs.ethical_ai_environmental_consciousness_config + ) + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _ai_application_to_nested(ai_application: AIApplication) -> AIApplicationNested: + """Convert flat AIApplication to nested format.""" + attrs = AIApplicationAttributes() + _populate_ai_application_attrs(attrs, ai_application) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + ai_application, _AI_APPLICATION_REL_FIELDS, AIApplicationRelationshipAttributes + ) + return AIApplicationNested( + guid=ai_application.guid, + type_name=ai_application.type_name, + status=ai_application.status, + version=ai_application.version, + create_time=ai_application.create_time, + update_time=ai_application.update_time, + created_by=ai_application.created_by, + updated_by=ai_application.updated_by, + classifications=ai_application.classifications, + classification_names=ai_application.classification_names, + meanings=ai_application.meanings, + labels=ai_application.labels, + business_attributes=ai_application.business_attributes, + custom_attributes=ai_application.custom_attributes, + pending_tasks=ai_application.pending_tasks, + proxy=ai_application.proxy, + is_incomplete=ai_application.is_incomplete, + provenance_type=ai_application.provenance_type, + home_id=ai_application.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _ai_application_from_nested(nested: AIApplicationNested) -> AIApplication: + """Convert nested format to flat AIApplication.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else AIApplicationAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _AI_APPLICATION_REL_FIELDS, + AIApplicationRelationshipAttributes, + ) + return AIApplication( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_ai_application_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _ai_application_to_nested_bytes( + ai_application: AIApplication, serde: Serde +) -> bytes: + """Convert flat AIApplication to nested JSON bytes.""" + return serde.encode(_ai_application_to_nested(ai_application)) + + +def _ai_application_from_nested_bytes(data: bytes, serde: Serde) -> AIApplication: + """Convert nested JSON bytes to flat AIApplication.""" + nested = serde.decode(data, AIApplicationNested) + return _ai_application_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +AIApplication.AI_APPLICATION_VERSION = KeywordField( + "aiApplicationVersion", "aiApplicationVersion" +) +AIApplication.AI_APPLICATION_DEVELOPMENT_STAGE = KeywordField( + "aiApplicationDevelopmentStage", "aiApplicationDevelopmentStage" +) +AIApplication.ETHICAL_AI_PRIVACY_CONFIG = KeywordField( + "ethicalAIPrivacyConfig", "ethicalAIPrivacyConfig" +) +AIApplication.ETHICAL_AI_FAIRNESS_CONFIG = KeywordField( + "ethicalAIFairnessConfig", "ethicalAIFairnessConfig" +) +AIApplication.ETHICAL_AI_BIAS_MITIGATION_CONFIG = KeywordField( + "ethicalAIBiasMitigationConfig", "ethicalAIBiasMitigationConfig" +) +AIApplication.ETHICAL_AI_RELIABILITY_AND_SAFETY_CONFIG = KeywordField( + "ethicalAIReliabilityAndSafetyConfig", "ethicalAIReliabilityAndSafetyConfig" +) +AIApplication.ETHICAL_AI_TRANSPARENCY_CONFIG = KeywordField( + "ethicalAITransparencyConfig", "ethicalAITransparencyConfig" +) +AIApplication.ETHICAL_AI_ACCOUNTABILITY_CONFIG = KeywordField( + "ethicalAIAccountabilityConfig", "ethicalAIAccountabilityConfig" +) +AIApplication.ETHICAL_AI_ENVIRONMENTAL_CONSCIOUSNESS_CONFIG = KeywordField( + "ethicalAIEnvironmentalConsciousnessConfig", + "ethicalAIEnvironmentalConsciousnessConfig", +) +AIApplication.MODELS = RelationField("models") +AIApplication.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +AIApplication.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +AIApplication.ANOMALO_CHECKS = RelationField("anomaloChecks") +AIApplication.APPLICATION = RelationField("application") +AIApplication.APPLICATION_FIELD = RelationField("applicationField") +AIApplication.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +AIApplication.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +AIApplication.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +AIApplication.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +AIApplication.METRICS = RelationField("metrics") +AIApplication.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +AIApplication.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +AIApplication.MEANINGS = RelationField("meanings") +AIApplication.MC_MONITORS = RelationField("mcMonitors") +AIApplication.MC_INCIDENTS = RelationField("mcIncidents") +AIApplication.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +AIApplication.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +AIApplication.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +AIApplication.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +AIApplication.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +AIApplication.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +AIApplication.FILES = RelationField("files") +AIApplication.LINKS = RelationField("links") +AIApplication.README = RelationField("readme") +AIApplication.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +AIApplication.SODA_CHECKS = RelationField("sodaChecks") +AIApplication.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +AIApplication.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/ai_model.py b/pyatlan_v9/model/assets/ai_model.py new file mode 100644 index 000000000..09033c3d9 --- /dev/null +++ b/pyatlan_v9/model/assets/ai_model.py @@ -0,0 +1,802 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +AIModel asset model with flattened inheritance. + +This module provides: +- AIModel: Flat asset class (easy to use) +- AIModelAttributes: Nested attributes struct (extends AssetAttributes) +- AIModelNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan.model.enums import AIDatasetType, AtlanConnectorType +from pyatlan.utils import to_camel_case +from pyatlan_v9.model.assets.process import Process +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import get_type +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .ai_related import RelatedAIApplication, RelatedAIModelVersion + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class AIModel(Asset): + """ + Instance of an AI model in Atlan. + """ + + AI_MODEL_DATASETS_DSL: ClassVar[Any] = None + AI_MODEL_STATUS: ClassVar[Any] = None + AI_MODEL_VERSION: ClassVar[Any] = None + ETHICAL_AI_PRIVACY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_FAIRNESS_CONFIG: ClassVar[Any] = None + ETHICAL_AI_BIAS_MITIGATION_CONFIG: ClassVar[Any] = None + ETHICAL_AI_RELIABILITY_AND_SAFETY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_TRANSPARENCY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_ACCOUNTABILITY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_ENVIRONMENTAL_CONSCIOUSNESS_CONFIG: ClassVar[Any] = None + APPLICATIONS: ClassVar[Any] = None + AI_MODEL_VERSIONS: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "AIModel" + + ai_model_datasets_dsl: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="aiModelDatasetsDSL" + ) + """Search DSL used to define which assets/datasets are part of the AI model.""" + + ai_model_status: Union[str, None, UnsetType] = UNSET + """Status of the AI model.""" + + ai_model_version: Union[str, None, UnsetType] = UNSET + """Version of the AI model.""" + + ethical_ai_privacy_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIPrivacyConfig" + ) + """Privacy configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_fairness_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIFairnessConfig" + ) + """Fairness configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_bias_mitigation_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIBiasMitigationConfig" + ) + """Bias mitigation configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_reliability_and_safety_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIReliabilityAndSafetyConfig") + ) + """Reliability and safety configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_transparency_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAITransparencyConfig" + ) + """Transparency configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_accountability_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIAccountabilityConfig" + ) + """Accountability configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_environmental_consciousness_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIEnvironmentalConsciousnessConfig") + ) + """Environmental consciousness configuration for ensuring the ethical use of an AI asset""" + + applications: Union[List[RelatedAIApplication], None, UnsetType] = UNSET + """AI applications that are created using this AI model.""" + + ai_model_versions: Union[List[RelatedAIModelVersion], None, UnsetType] = UNSET + """Versions contained within the model.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "AIModel" + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + ai_model_status: str, + owner_groups: Union[set[str], None] = None, + owner_users: Union[set[str], None] = None, + ai_model_version: Union[str, None] = None, + ) -> "AIModel": + """Create a new AIModel asset.""" + validate_required_fields(["name", "ai_model_status"], [name, ai_model_status]) + name_camel_case = to_camel_case(name) + return cls( + name=name, + qualified_name=f"default/ai/aiapplication/{name_camel_case}", + connector_name=AtlanConnectorType.AI.value, + ai_model_status=ai_model_status, + ai_model_version=ai_model_version + if ai_model_version is not None + else UNSET, + owner_groups=owner_groups if owner_groups is not None else UNSET, + owner_users=owner_users if owner_users is not None else UNSET, + ) + + @classmethod + def processes_creator( + cls, + ai_model: "AIModel", + dataset_dict: Dict[AIDatasetType, list], + ) -> List[Process]: + """ + Create Process assets representing AI model lineage with dataset assets. + """ + if not ai_model.guid or not ai_model.name: + raise ValueError("AI model must have both guid and name attributes") + + process_list: List[Process] = [] + for dataset_type, assets in dataset_dict.items(): + for asset in assets: + asset_cls = get_type(getattr(asset, "type_name", "Asset")) + asset_guid = getattr(asset, "guid", None) + asset_name = getattr(asset, "name", None) + if not asset_guid or not asset_name: + continue + + if dataset_type == AIDatasetType.OUTPUT: + process_name = f"{ai_model.name} -> {asset_name}" + process_created = Process.creator( + name=process_name, + connection_qualified_name="default/ai/dataset", + inputs=[AIModel.ref_by_guid(guid=ai_model.guid)], + outputs=[asset_cls.ref_by_guid(guid=asset_guid)], + extra_hash_params={dataset_type.value}, + ) + else: + process_name = f"{asset_name} -> {ai_model.name}" + process_created = Process.creator( + name=process_name, + connection_qualified_name="default/ai/dataset", + inputs=[asset_cls.ref_by_guid(guid=asset_guid)], + outputs=[AIModel.ref_by_guid(guid=ai_model.guid)], + extra_hash_params={dataset_type.value}, + ) + + process_created.ai_dataset_type = dataset_type + process_list.append(process_created) + + return process_list + + @classmethod + def processes_batch_save( + cls, client: Any, process_list: List[Process] + ) -> List[Any]: + """ + Save Process assets in batches to reduce API payload size. + """ + batch_size = 20 + responses: List[Any] = [] + for i in range(0, len(process_list), batch_size): + responses.append(client.asset.save(process_list[i : i + batch_size])) + return responses + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "AIModel": + """Create an AIModel instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "AIModel": + """Return only fields required for update operations.""" + return AIModel.updater(qualified_name=self.qualified_name, name=self.name) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _ai_model_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> AIModel: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + AIModel instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _ai_model_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class AIModelAttributes(AssetAttributes): + """AIModel-specific attributes for nested API format.""" + + ai_model_datasets_dsl: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="aiModelDatasetsDSL" + ) + """Search DSL used to define which assets/datasets are part of the AI model.""" + + ai_model_status: Union[str, None, UnsetType] = UNSET + """Status of the AI model.""" + + ai_model_version: Union[str, None, UnsetType] = UNSET + """Version of the AI model.""" + + ethical_ai_privacy_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIPrivacyConfig" + ) + """Privacy configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_fairness_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIFairnessConfig" + ) + """Fairness configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_bias_mitigation_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIBiasMitigationConfig" + ) + """Bias mitigation configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_reliability_and_safety_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIReliabilityAndSafetyConfig") + ) + """Reliability and safety configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_transparency_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAITransparencyConfig" + ) + """Transparency configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_accountability_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIAccountabilityConfig" + ) + """Accountability configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_environmental_consciousness_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIEnvironmentalConsciousnessConfig") + ) + """Environmental consciousness configuration for ensuring the ethical use of an AI asset""" + + +class AIModelRelationshipAttributes(AssetRelationshipAttributes): + """AIModel-specific relationship attributes for nested API format.""" + + applications: Union[List[RelatedAIApplication], None, UnsetType] = UNSET + """AI applications that are created using this AI model.""" + + ai_model_versions: Union[List[RelatedAIModelVersion], None, UnsetType] = UNSET + """Versions contained within the model.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class AIModelNested(AssetNested): + """AIModel in nested API format for high-performance serialization.""" + + attributes: Union[AIModelAttributes, UnsetType] = UNSET + relationship_attributes: Union[AIModelRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[AIModelRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[AIModelRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_AI_MODEL_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "applications", + "ai_model_versions", + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_ai_model_attrs(attrs: AIModelAttributes, obj: AIModel) -> None: + """Populate AIModel-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.ai_model_datasets_dsl = obj.ai_model_datasets_dsl + attrs.ai_model_status = obj.ai_model_status + attrs.ai_model_version = obj.ai_model_version + attrs.ethical_ai_privacy_config = obj.ethical_ai_privacy_config + attrs.ethical_ai_fairness_config = obj.ethical_ai_fairness_config + attrs.ethical_ai_bias_mitigation_config = obj.ethical_ai_bias_mitigation_config + attrs.ethical_ai_reliability_and_safety_config = ( + obj.ethical_ai_reliability_and_safety_config + ) + attrs.ethical_ai_transparency_config = obj.ethical_ai_transparency_config + attrs.ethical_ai_accountability_config = obj.ethical_ai_accountability_config + attrs.ethical_ai_environmental_consciousness_config = ( + obj.ethical_ai_environmental_consciousness_config + ) + + +def _extract_ai_model_attrs(attrs: AIModelAttributes) -> dict: + """Extract all AIModel attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["ai_model_datasets_dsl"] = attrs.ai_model_datasets_dsl + result["ai_model_status"] = attrs.ai_model_status + result["ai_model_version"] = attrs.ai_model_version + result["ethical_ai_privacy_config"] = attrs.ethical_ai_privacy_config + result["ethical_ai_fairness_config"] = attrs.ethical_ai_fairness_config + result["ethical_ai_bias_mitigation_config"] = ( + attrs.ethical_ai_bias_mitigation_config + ) + result["ethical_ai_reliability_and_safety_config"] = ( + attrs.ethical_ai_reliability_and_safety_config + ) + result["ethical_ai_transparency_config"] = attrs.ethical_ai_transparency_config + result["ethical_ai_accountability_config"] = attrs.ethical_ai_accountability_config + result["ethical_ai_environmental_consciousness_config"] = ( + attrs.ethical_ai_environmental_consciousness_config + ) + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _ai_model_to_nested(ai_model: AIModel) -> AIModelNested: + """Convert flat AIModel to nested format.""" + attrs = AIModelAttributes() + _populate_ai_model_attrs(attrs, ai_model) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + ai_model, _AI_MODEL_REL_FIELDS, AIModelRelationshipAttributes + ) + return AIModelNested( + guid=ai_model.guid, + type_name=ai_model.type_name, + status=ai_model.status, + version=ai_model.version, + create_time=ai_model.create_time, + update_time=ai_model.update_time, + created_by=ai_model.created_by, + updated_by=ai_model.updated_by, + classifications=ai_model.classifications, + classification_names=ai_model.classification_names, + meanings=ai_model.meanings, + labels=ai_model.labels, + business_attributes=ai_model.business_attributes, + custom_attributes=ai_model.custom_attributes, + pending_tasks=ai_model.pending_tasks, + proxy=ai_model.proxy, + is_incomplete=ai_model.is_incomplete, + provenance_type=ai_model.provenance_type, + home_id=ai_model.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _ai_model_from_nested(nested: AIModelNested) -> AIModel: + """Convert nested format to flat AIModel.""" + attrs = nested.attributes if nested.attributes is not UNSET else AIModelAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _AI_MODEL_REL_FIELDS, + AIModelRelationshipAttributes, + ) + return AIModel( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_ai_model_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _ai_model_to_nested_bytes(ai_model: AIModel, serde: Serde) -> bytes: + """Convert flat AIModel to nested JSON bytes.""" + return serde.encode(_ai_model_to_nested(ai_model)) + + +def _ai_model_from_nested_bytes(data: bytes, serde: Serde) -> AIModel: + """Convert nested JSON bytes to flat AIModel.""" + nested = serde.decode(data, AIModelNested) + return _ai_model_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +AIModel.AI_MODEL_DATASETS_DSL = KeywordField("aiModelDatasetsDSL", "aiModelDatasetsDSL") +AIModel.AI_MODEL_STATUS = KeywordField("aiModelStatus", "aiModelStatus") +AIModel.AI_MODEL_VERSION = KeywordField("aiModelVersion", "aiModelVersion") +AIModel.ETHICAL_AI_PRIVACY_CONFIG = KeywordField( + "ethicalAIPrivacyConfig", "ethicalAIPrivacyConfig" +) +AIModel.ETHICAL_AI_FAIRNESS_CONFIG = KeywordField( + "ethicalAIFairnessConfig", "ethicalAIFairnessConfig" +) +AIModel.ETHICAL_AI_BIAS_MITIGATION_CONFIG = KeywordField( + "ethicalAIBiasMitigationConfig", "ethicalAIBiasMitigationConfig" +) +AIModel.ETHICAL_AI_RELIABILITY_AND_SAFETY_CONFIG = KeywordField( + "ethicalAIReliabilityAndSafetyConfig", "ethicalAIReliabilityAndSafetyConfig" +) +AIModel.ETHICAL_AI_TRANSPARENCY_CONFIG = KeywordField( + "ethicalAITransparencyConfig", "ethicalAITransparencyConfig" +) +AIModel.ETHICAL_AI_ACCOUNTABILITY_CONFIG = KeywordField( + "ethicalAIAccountabilityConfig", "ethicalAIAccountabilityConfig" +) +AIModel.ETHICAL_AI_ENVIRONMENTAL_CONSCIOUSNESS_CONFIG = KeywordField( + "ethicalAIEnvironmentalConsciousnessConfig", + "ethicalAIEnvironmentalConsciousnessConfig", +) +AIModel.APPLICATIONS = RelationField("applications") +AIModel.AI_MODEL_VERSIONS = RelationField("aiModelVersions") +AIModel.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +AIModel.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +AIModel.ANOMALO_CHECKS = RelationField("anomaloChecks") +AIModel.APPLICATION = RelationField("application") +AIModel.APPLICATION_FIELD = RelationField("applicationField") +AIModel.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +AIModel.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +AIModel.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +AIModel.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +AIModel.METRICS = RelationField("metrics") +AIModel.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +AIModel.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +AIModel.MEANINGS = RelationField("meanings") +AIModel.MC_MONITORS = RelationField("mcMonitors") +AIModel.MC_INCIDENTS = RelationField("mcIncidents") +AIModel.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +AIModel.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +AIModel.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +AIModel.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +AIModel.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +AIModel.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +AIModel.FILES = RelationField("files") +AIModel.LINKS = RelationField("links") +AIModel.README = RelationField("readme") +AIModel.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +AIModel.SODA_CHECKS = RelationField("sodaChecks") +AIModel.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +AIModel.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/ai_model_version.py b/pyatlan_v9/model/assets/ai_model_version.py new file mode 100644 index 000000000..54e5f3256 --- /dev/null +++ b/pyatlan_v9/model/assets/ai_model_version.py @@ -0,0 +1,683 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +AIModelVersion asset model with flattened inheritance. + +This module provides: +- AIModelVersion: Flat asset class (easy to use) +- AIModelVersionAttributes: Nested attributes struct (extends AssetAttributes) +- AIModelVersionNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .ai_related import RelatedAIModel + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class AIModelVersion(Asset): + """ + Base class for all AIModelVersion types. + """ + + ETHICAL_AI_PRIVACY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_FAIRNESS_CONFIG: ClassVar[Any] = None + ETHICAL_AI_BIAS_MITIGATION_CONFIG: ClassVar[Any] = None + ETHICAL_AI_RELIABILITY_AND_SAFETY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_TRANSPARENCY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_ACCOUNTABILITY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_ENVIRONMENTAL_CONSCIOUSNESS_CONFIG: ClassVar[Any] = None + AI_MODEL: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "AIModelVersion" + + ethical_ai_privacy_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIPrivacyConfig" + ) + """Privacy configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_fairness_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIFairnessConfig" + ) + """Fairness configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_bias_mitigation_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIBiasMitigationConfig" + ) + """Bias mitigation configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_reliability_and_safety_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIReliabilityAndSafetyConfig") + ) + """Reliability and safety configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_transparency_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAITransparencyConfig" + ) + """Transparency configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_accountability_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIAccountabilityConfig" + ) + """Accountability configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_environmental_consciousness_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIEnvironmentalConsciousnessConfig") + ) + """Environmental consciousness configuration for ensuring the ethical use of an AI asset""" + + ai_model: Union[RelatedAIModel, None, UnsetType] = UNSET + """Model containing the versions.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "AIModelVersion" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _ai_model_version_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> AIModelVersion: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + AIModelVersion instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _ai_model_version_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class AIModelVersionAttributes(AssetAttributes): + """AIModelVersion-specific attributes for nested API format.""" + + ethical_ai_privacy_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIPrivacyConfig" + ) + """Privacy configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_fairness_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIFairnessConfig" + ) + """Fairness configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_bias_mitigation_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIBiasMitigationConfig" + ) + """Bias mitigation configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_reliability_and_safety_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIReliabilityAndSafetyConfig") + ) + """Reliability and safety configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_transparency_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAITransparencyConfig" + ) + """Transparency configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_accountability_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIAccountabilityConfig" + ) + """Accountability configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_environmental_consciousness_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIEnvironmentalConsciousnessConfig") + ) + """Environmental consciousness configuration for ensuring the ethical use of an AI asset""" + + +class AIModelVersionRelationshipAttributes(AssetRelationshipAttributes): + """AIModelVersion-specific relationship attributes for nested API format.""" + + ai_model: Union[RelatedAIModel, None, UnsetType] = UNSET + """Model containing the versions.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class AIModelVersionNested(AssetNested): + """AIModelVersion in nested API format for high-performance serialization.""" + + attributes: Union[AIModelVersionAttributes, UnsetType] = UNSET + relationship_attributes: Union[AIModelVersionRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + AIModelVersionRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + AIModelVersionRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_AI_MODEL_VERSION_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "ai_model", + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_ai_model_version_attrs( + attrs: AIModelVersionAttributes, obj: AIModelVersion +) -> None: + """Populate AIModelVersion-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.ethical_ai_privacy_config = obj.ethical_ai_privacy_config + attrs.ethical_ai_fairness_config = obj.ethical_ai_fairness_config + attrs.ethical_ai_bias_mitigation_config = obj.ethical_ai_bias_mitigation_config + attrs.ethical_ai_reliability_and_safety_config = ( + obj.ethical_ai_reliability_and_safety_config + ) + attrs.ethical_ai_transparency_config = obj.ethical_ai_transparency_config + attrs.ethical_ai_accountability_config = obj.ethical_ai_accountability_config + attrs.ethical_ai_environmental_consciousness_config = ( + obj.ethical_ai_environmental_consciousness_config + ) + + +def _extract_ai_model_version_attrs(attrs: AIModelVersionAttributes) -> dict: + """Extract all AIModelVersion attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["ethical_ai_privacy_config"] = attrs.ethical_ai_privacy_config + result["ethical_ai_fairness_config"] = attrs.ethical_ai_fairness_config + result["ethical_ai_bias_mitigation_config"] = ( + attrs.ethical_ai_bias_mitigation_config + ) + result["ethical_ai_reliability_and_safety_config"] = ( + attrs.ethical_ai_reliability_and_safety_config + ) + result["ethical_ai_transparency_config"] = attrs.ethical_ai_transparency_config + result["ethical_ai_accountability_config"] = attrs.ethical_ai_accountability_config + result["ethical_ai_environmental_consciousness_config"] = ( + attrs.ethical_ai_environmental_consciousness_config + ) + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _ai_model_version_to_nested( + ai_model_version: AIModelVersion, +) -> AIModelVersionNested: + """Convert flat AIModelVersion to nested format.""" + attrs = AIModelVersionAttributes() + _populate_ai_model_version_attrs(attrs, ai_model_version) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + ai_model_version, + _AI_MODEL_VERSION_REL_FIELDS, + AIModelVersionRelationshipAttributes, + ) + return AIModelVersionNested( + guid=ai_model_version.guid, + type_name=ai_model_version.type_name, + status=ai_model_version.status, + version=ai_model_version.version, + create_time=ai_model_version.create_time, + update_time=ai_model_version.update_time, + created_by=ai_model_version.created_by, + updated_by=ai_model_version.updated_by, + classifications=ai_model_version.classifications, + classification_names=ai_model_version.classification_names, + meanings=ai_model_version.meanings, + labels=ai_model_version.labels, + business_attributes=ai_model_version.business_attributes, + custom_attributes=ai_model_version.custom_attributes, + pending_tasks=ai_model_version.pending_tasks, + proxy=ai_model_version.proxy, + is_incomplete=ai_model_version.is_incomplete, + provenance_type=ai_model_version.provenance_type, + home_id=ai_model_version.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _ai_model_version_from_nested(nested: AIModelVersionNested) -> AIModelVersion: + """Convert nested format to flat AIModelVersion.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else AIModelVersionAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _AI_MODEL_VERSION_REL_FIELDS, + AIModelVersionRelationshipAttributes, + ) + return AIModelVersion( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_ai_model_version_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _ai_model_version_to_nested_bytes( + ai_model_version: AIModelVersion, serde: Serde +) -> bytes: + """Convert flat AIModelVersion to nested JSON bytes.""" + return serde.encode(_ai_model_version_to_nested(ai_model_version)) + + +def _ai_model_version_from_nested_bytes(data: bytes, serde: Serde) -> AIModelVersion: + """Convert nested JSON bytes to flat AIModelVersion.""" + nested = serde.decode(data, AIModelVersionNested) + return _ai_model_version_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +AIModelVersion.ETHICAL_AI_PRIVACY_CONFIG = KeywordField( + "ethicalAIPrivacyConfig", "ethicalAIPrivacyConfig" +) +AIModelVersion.ETHICAL_AI_FAIRNESS_CONFIG = KeywordField( + "ethicalAIFairnessConfig", "ethicalAIFairnessConfig" +) +AIModelVersion.ETHICAL_AI_BIAS_MITIGATION_CONFIG = KeywordField( + "ethicalAIBiasMitigationConfig", "ethicalAIBiasMitigationConfig" +) +AIModelVersion.ETHICAL_AI_RELIABILITY_AND_SAFETY_CONFIG = KeywordField( + "ethicalAIReliabilityAndSafetyConfig", "ethicalAIReliabilityAndSafetyConfig" +) +AIModelVersion.ETHICAL_AI_TRANSPARENCY_CONFIG = KeywordField( + "ethicalAITransparencyConfig", "ethicalAITransparencyConfig" +) +AIModelVersion.ETHICAL_AI_ACCOUNTABILITY_CONFIG = KeywordField( + "ethicalAIAccountabilityConfig", "ethicalAIAccountabilityConfig" +) +AIModelVersion.ETHICAL_AI_ENVIRONMENTAL_CONSCIOUSNESS_CONFIG = KeywordField( + "ethicalAIEnvironmentalConsciousnessConfig", + "ethicalAIEnvironmentalConsciousnessConfig", +) +AIModelVersion.AI_MODEL = RelationField("aiModel") +AIModelVersion.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +AIModelVersion.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +AIModelVersion.ANOMALO_CHECKS = RelationField("anomaloChecks") +AIModelVersion.APPLICATION = RelationField("application") +AIModelVersion.APPLICATION_FIELD = RelationField("applicationField") +AIModelVersion.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +AIModelVersion.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +AIModelVersion.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +AIModelVersion.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +AIModelVersion.METRICS = RelationField("metrics") +AIModelVersion.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +AIModelVersion.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +AIModelVersion.MEANINGS = RelationField("meanings") +AIModelVersion.MC_MONITORS = RelationField("mcMonitors") +AIModelVersion.MC_INCIDENTS = RelationField("mcIncidents") +AIModelVersion.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +AIModelVersion.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +AIModelVersion.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +AIModelVersion.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +AIModelVersion.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +AIModelVersion.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +AIModelVersion.FILES = RelationField("files") +AIModelVersion.LINKS = RelationField("links") +AIModelVersion.README = RelationField("readme") +AIModelVersion.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +AIModelVersion.SODA_CHECKS = RelationField("sodaChecks") +AIModelVersion.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +AIModelVersion.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/ai_related.py b/pyatlan_v9/model/assets/ai_related.py new file mode 100644 index 000000000..2437212fc --- /dev/null +++ b/pyatlan_v9/model/assets/ai_related.py @@ -0,0 +1,139 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for AI module. + +This module contains all Related{Type} classes for the AI type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedCatalog +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedAI", + "RelatedAIApplication", + "RelatedAIModel", + "RelatedAIModelVersion", +] + + +class RelatedAI(RelatedCatalog): + """ + Related entity reference for AI assets. + + Extends RelatedCatalog with AI-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "AI" so it serializes correctly + + ethical_ai_privacy_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIPrivacyConfig" + ) + """Privacy configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_fairness_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIFairnessConfig" + ) + """Fairness configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_bias_mitigation_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIBiasMitigationConfig" + ) + """Bias mitigation configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_reliability_and_safety_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIReliabilityAndSafetyConfig") + ) + """Reliability and safety configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_transparency_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAITransparencyConfig" + ) + """Transparency configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_accountability_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIAccountabilityConfig" + ) + """Accountability configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_environmental_consciousness_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIEnvironmentalConsciousnessConfig") + ) + """Environmental consciousness configuration for ensuring the ethical use of an AI asset""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "AI" + + +class RelatedAIApplication(RelatedAI): + """ + Related entity reference for AIApplication assets. + + Extends RelatedAI with AIApplication-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "AIApplication" so it serializes correctly + + ai_application_version: Union[str, None, UnsetType] = UNSET + """Version of the AI application""" + + ai_application_development_stage: Union[str, None, UnsetType] = UNSET + """Development stage of the AI application""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "AIApplication" + + +class RelatedAIModel(RelatedAI): + """ + Related entity reference for AIModel assets. + + Extends RelatedAI with AIModel-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "AIModel" so it serializes correctly + + ai_model_datasets_dsl: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="aiModelDatasetsDSL" + ) + """Search DSL used to define which assets/datasets are part of the AI model.""" + + ai_model_status: Union[str, None, UnsetType] = UNSET + """Status of the AI model.""" + + ai_model_version: Union[str, None, UnsetType] = UNSET + """Version of the AI model.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "AIModel" + + +class RelatedAIModelVersion(RelatedAI): + """ + Related entity reference for AIModelVersion assets. + + Extends RelatedAI with AIModelVersion-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "AIModelVersion" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "AIModelVersion" diff --git a/pyatlan_v9/model/assets/airflow.py b/pyatlan_v9/model/assets/airflow.py new file mode 100644 index 000000000..4f2533bb9 --- /dev/null +++ b/pyatlan_v9/model/assets/airflow.py @@ -0,0 +1,622 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Airflow asset model with flattened inheritance. + +This module provides: +- Airflow: Flat asset class (easy to use) +- AirflowAttributes: Nested attributes struct (extends AssetAttributes) +- AirflowNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSpark, RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .airflow_related import RelatedAirflowTask + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Airflow(Asset): + """ + Base class for Airflow assets. + """ + + AIRFLOW_TAGS: ClassVar[Any] = None + AIRFLOW_RUN_VERSION: ClassVar[Any] = None + AIRFLOW_RUN_OPEN_LINEAGE_VERSION: ClassVar[Any] = None + AIRFLOW_RUN_NAME: ClassVar[Any] = None + AIRFLOW_RUN_TYPE: ClassVar[Any] = None + AIRFLOW_RUN_START_TIME: ClassVar[Any] = None + AIRFLOW_RUN_END_TIME: ClassVar[Any] = None + AIRFLOW_RUN_OPEN_LINEAGE_STATE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + SPARK_ORCHESTRATED_ASSETS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Airflow" + + airflow_tags: Union[List[str], None, UnsetType] = UNSET + """Tags assigned to the asset in Airflow.""" + + airflow_run_version: Union[str, None, UnsetType] = UNSET + """Version of the run in Airflow.""" + + airflow_run_open_lineage_version: Union[str, None, UnsetType] = UNSET + """Version of the run in OpenLineage.""" + + airflow_run_name: Union[str, None, UnsetType] = UNSET + """Name of the run.""" + + airflow_run_type: Union[str, None, UnsetType] = UNSET + """Type of the run.""" + + airflow_run_start_time: Union[int, None, UnsetType] = UNSET + """Start time of the run.""" + + airflow_run_end_time: Union[int, None, UnsetType] = UNSET + """End time of the run.""" + + airflow_run_open_lineage_state: Union[str, None, UnsetType] = UNSET + """State of the run in OpenLineage.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + spark_orchestrated_assets: Union[List[RelatedSpark], None, UnsetType] = UNSET + """Spark assets that are executed by this airflow asset.""" + + def __post_init__(self) -> None: + self.type_name = "Airflow" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _airflow_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Airflow: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Airflow instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _airflow_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class AirflowAttributes(AssetAttributes): + """Airflow-specific attributes for nested API format.""" + + airflow_tags: Union[List[str], None, UnsetType] = UNSET + """Tags assigned to the asset in Airflow.""" + + airflow_run_version: Union[str, None, UnsetType] = UNSET + """Version of the run in Airflow.""" + + airflow_run_open_lineage_version: Union[str, None, UnsetType] = UNSET + """Version of the run in OpenLineage.""" + + airflow_run_name: Union[str, None, UnsetType] = UNSET + """Name of the run.""" + + airflow_run_type: Union[str, None, UnsetType] = UNSET + """Type of the run.""" + + airflow_run_start_time: Union[int, None, UnsetType] = UNSET + """Start time of the run.""" + + airflow_run_end_time: Union[int, None, UnsetType] = UNSET + """End time of the run.""" + + airflow_run_open_lineage_state: Union[str, None, UnsetType] = UNSET + """State of the run in OpenLineage.""" + + +class AirflowRelationshipAttributes(AssetRelationshipAttributes): + """Airflow-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + spark_orchestrated_assets: Union[List[RelatedSpark], None, UnsetType] = UNSET + """Spark assets that are executed by this airflow asset.""" + + +class AirflowNested(AssetNested): + """Airflow in nested API format for high-performance serialization.""" + + attributes: Union[AirflowAttributes, UnsetType] = UNSET + relationship_attributes: Union[AirflowRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[AirflowRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[AirflowRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_AIRFLOW_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", + "spark_orchestrated_assets", +] + + +def _populate_airflow_attrs(attrs: AirflowAttributes, obj: Airflow) -> None: + """Populate Airflow-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.airflow_tags = obj.airflow_tags + attrs.airflow_run_version = obj.airflow_run_version + attrs.airflow_run_open_lineage_version = obj.airflow_run_open_lineage_version + attrs.airflow_run_name = obj.airflow_run_name + attrs.airflow_run_type = obj.airflow_run_type + attrs.airflow_run_start_time = obj.airflow_run_start_time + attrs.airflow_run_end_time = obj.airflow_run_end_time + attrs.airflow_run_open_lineage_state = obj.airflow_run_open_lineage_state + + +def _extract_airflow_attrs(attrs: AirflowAttributes) -> dict: + """Extract all Airflow attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["airflow_tags"] = attrs.airflow_tags + result["airflow_run_version"] = attrs.airflow_run_version + result["airflow_run_open_lineage_version"] = attrs.airflow_run_open_lineage_version + result["airflow_run_name"] = attrs.airflow_run_name + result["airflow_run_type"] = attrs.airflow_run_type + result["airflow_run_start_time"] = attrs.airflow_run_start_time + result["airflow_run_end_time"] = attrs.airflow_run_end_time + result["airflow_run_open_lineage_state"] = attrs.airflow_run_open_lineage_state + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _airflow_to_nested(airflow: Airflow) -> AirflowNested: + """Convert flat Airflow to nested format.""" + attrs = AirflowAttributes() + _populate_airflow_attrs(attrs, airflow) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + airflow, _AIRFLOW_REL_FIELDS, AirflowRelationshipAttributes + ) + return AirflowNested( + guid=airflow.guid, + type_name=airflow.type_name, + status=airflow.status, + version=airflow.version, + create_time=airflow.create_time, + update_time=airflow.update_time, + created_by=airflow.created_by, + updated_by=airflow.updated_by, + classifications=airflow.classifications, + classification_names=airflow.classification_names, + meanings=airflow.meanings, + labels=airflow.labels, + business_attributes=airflow.business_attributes, + custom_attributes=airflow.custom_attributes, + pending_tasks=airflow.pending_tasks, + proxy=airflow.proxy, + is_incomplete=airflow.is_incomplete, + provenance_type=airflow.provenance_type, + home_id=airflow.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _airflow_from_nested(nested: AirflowNested) -> Airflow: + """Convert nested format to flat Airflow.""" + attrs = nested.attributes if nested.attributes is not UNSET else AirflowAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _AIRFLOW_REL_FIELDS, + AirflowRelationshipAttributes, + ) + return Airflow( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_airflow_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _airflow_to_nested_bytes(airflow: Airflow, serde: Serde) -> bytes: + """Convert flat Airflow to nested JSON bytes.""" + return serde.encode(_airflow_to_nested(airflow)) + + +def _airflow_from_nested_bytes(data: bytes, serde: Serde) -> Airflow: + """Convert nested JSON bytes to flat Airflow.""" + nested = serde.decode(data, AirflowNested) + return _airflow_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +Airflow.AIRFLOW_TAGS = KeywordField("airflowTags", "airflowTags") +Airflow.AIRFLOW_RUN_VERSION = KeywordField("airflowRunVersion", "airflowRunVersion") +Airflow.AIRFLOW_RUN_OPEN_LINEAGE_VERSION = KeywordField( + "airflowRunOpenLineageVersion", "airflowRunOpenLineageVersion" +) +Airflow.AIRFLOW_RUN_NAME = KeywordField("airflowRunName", "airflowRunName") +Airflow.AIRFLOW_RUN_TYPE = KeywordField("airflowRunType", "airflowRunType") +Airflow.AIRFLOW_RUN_START_TIME = NumericField( + "airflowRunStartTime", "airflowRunStartTime" +) +Airflow.AIRFLOW_RUN_END_TIME = NumericField("airflowRunEndTime", "airflowRunEndTime") +Airflow.AIRFLOW_RUN_OPEN_LINEAGE_STATE = KeywordField( + "airflowRunOpenLineageState", "airflowRunOpenLineageState" +) +Airflow.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Airflow.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Airflow.ANOMALO_CHECKS = RelationField("anomaloChecks") +Airflow.APPLICATION = RelationField("application") +Airflow.APPLICATION_FIELD = RelationField("applicationField") +Airflow.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Airflow.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Airflow.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Airflow.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Airflow.METRICS = RelationField("metrics") +Airflow.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Airflow.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Airflow.MEANINGS = RelationField("meanings") +Airflow.MC_MONITORS = RelationField("mcMonitors") +Airflow.MC_INCIDENTS = RelationField("mcIncidents") +Airflow.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Airflow.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Airflow.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Airflow.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Airflow.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Airflow.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Airflow.FILES = RelationField("files") +Airflow.LINKS = RelationField("links") +Airflow.README = RelationField("readme") +Airflow.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Airflow.SODA_CHECKS = RelationField("sodaChecks") +Airflow.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Airflow.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") +Airflow.SPARK_ORCHESTRATED_ASSETS = RelationField("sparkOrchestratedAssets") diff --git a/pyatlan_v9/model/assets/airflow_dag.py b/pyatlan_v9/model/assets/airflow_dag.py new file mode 100644 index 000000000..fb48ec041 --- /dev/null +++ b/pyatlan_v9/model/assets/airflow_dag.py @@ -0,0 +1,688 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +AirflowDag asset model with flattened inheritance. + +This module provides: +- AirflowDag: Flat asset class (easy to use) +- AirflowDagAttributes: Nested attributes struct (extends AssetAttributes) +- AirflowDagNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSpark, RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .airflow_related import RelatedAirflowTask + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class AirflowDag(Asset): + """ + Instance of an Airflow DAG in Atlan. + """ + + AIRFLOW_DAG_SCHEDULE: ClassVar[Any] = None + AIRFLOW_DAG_SCHEDULE_DELTA: ClassVar[Any] = None + AIRFLOW_TAGS: ClassVar[Any] = None + AIRFLOW_RUN_VERSION: ClassVar[Any] = None + AIRFLOW_RUN_OPEN_LINEAGE_VERSION: ClassVar[Any] = None + AIRFLOW_RUN_NAME: ClassVar[Any] = None + AIRFLOW_RUN_TYPE: ClassVar[Any] = None + AIRFLOW_RUN_START_TIME: ClassVar[Any] = None + AIRFLOW_RUN_END_TIME: ClassVar[Any] = None + AIRFLOW_RUN_OPEN_LINEAGE_STATE: ClassVar[Any] = None + AIRFLOW_TASKS: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + SPARK_ORCHESTRATED_ASSETS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "AirflowDag" + + airflow_dag_schedule: Union[str, None, UnsetType] = UNSET + """Schedule for the DAG.""" + + airflow_dag_schedule_delta: Union[int, None, UnsetType] = UNSET + """Duration between scheduled runs, in seconds.""" + + airflow_tags: Union[List[str], None, UnsetType] = UNSET + """Tags assigned to the asset in Airflow.""" + + airflow_run_version: Union[str, None, UnsetType] = UNSET + """Version of the run in Airflow.""" + + airflow_run_open_lineage_version: Union[str, None, UnsetType] = UNSET + """Version of the run in OpenLineage.""" + + airflow_run_name: Union[str, None, UnsetType] = UNSET + """Name of the run.""" + + airflow_run_type: Union[str, None, UnsetType] = UNSET + """Type of the run.""" + + airflow_run_start_time: Union[int, None, UnsetType] = UNSET + """Start time of the run.""" + + airflow_run_end_time: Union[int, None, UnsetType] = UNSET + """End time of the run.""" + + airflow_run_open_lineage_state: Union[str, None, UnsetType] = UNSET + """State of the run in OpenLineage.""" + + airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks that exist within this DAG.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + spark_orchestrated_assets: Union[List[RelatedSpark], None, UnsetType] = UNSET + """Spark assets that are executed by this airflow asset.""" + + def __post_init__(self) -> None: + self.type_name = "AirflowDag" + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + connection_qualified_name: str, + ) -> "AirflowDag": + validate_required_fields( + ["name", "connection_qualified_name"], + [name, connection_qualified_name], + ) + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + qualified_name = f"{connection_qualified_name}/{name}" + return cls( + name=name, + qualified_name=qualified_name, + connector_name=connector_name, + connection_qualified_name=connection_qualified_name, + ) + + @classmethod + def create(cls, **kwargs) -> "AirflowDag": + return cls.creator(**kwargs) + + @classmethod + def create_for_modification(cls, **kwargs) -> "AirflowDag": + return cls.updater(**kwargs) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _airflow_dag_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> AirflowDag: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + AirflowDag instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _airflow_dag_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class AirflowDagAttributes(AssetAttributes): + """AirflowDag-specific attributes for nested API format.""" + + airflow_dag_schedule: Union[str, None, UnsetType] = UNSET + """Schedule for the DAG.""" + + airflow_dag_schedule_delta: Union[int, None, UnsetType] = UNSET + """Duration between scheduled runs, in seconds.""" + + airflow_tags: Union[List[str], None, UnsetType] = UNSET + """Tags assigned to the asset in Airflow.""" + + airflow_run_version: Union[str, None, UnsetType] = UNSET + """Version of the run in Airflow.""" + + airflow_run_open_lineage_version: Union[str, None, UnsetType] = UNSET + """Version of the run in OpenLineage.""" + + airflow_run_name: Union[str, None, UnsetType] = UNSET + """Name of the run.""" + + airflow_run_type: Union[str, None, UnsetType] = UNSET + """Type of the run.""" + + airflow_run_start_time: Union[int, None, UnsetType] = UNSET + """Start time of the run.""" + + airflow_run_end_time: Union[int, None, UnsetType] = UNSET + """End time of the run.""" + + airflow_run_open_lineage_state: Union[str, None, UnsetType] = UNSET + """State of the run in OpenLineage.""" + + +class AirflowDagRelationshipAttributes(AssetRelationshipAttributes): + """AirflowDag-specific relationship attributes for nested API format.""" + + airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks that exist within this DAG.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + spark_orchestrated_assets: Union[List[RelatedSpark], None, UnsetType] = UNSET + """Spark assets that are executed by this airflow asset.""" + + +class AirflowDagNested(AssetNested): + """AirflowDag in nested API format for high-performance serialization.""" + + attributes: Union[AirflowDagAttributes, UnsetType] = UNSET + relationship_attributes: Union[AirflowDagRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + AirflowDagRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + AirflowDagRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_AIRFLOW_DAG_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "airflow_tasks", + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", + "spark_orchestrated_assets", +] + + +def _populate_airflow_dag_attrs(attrs: AirflowDagAttributes, obj: AirflowDag) -> None: + """Populate AirflowDag-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.airflow_dag_schedule = obj.airflow_dag_schedule + attrs.airflow_dag_schedule_delta = obj.airflow_dag_schedule_delta + attrs.airflow_tags = obj.airflow_tags + attrs.airflow_run_version = obj.airflow_run_version + attrs.airflow_run_open_lineage_version = obj.airflow_run_open_lineage_version + attrs.airflow_run_name = obj.airflow_run_name + attrs.airflow_run_type = obj.airflow_run_type + attrs.airflow_run_start_time = obj.airflow_run_start_time + attrs.airflow_run_end_time = obj.airflow_run_end_time + attrs.airflow_run_open_lineage_state = obj.airflow_run_open_lineage_state + + +def _extract_airflow_dag_attrs(attrs: AirflowDagAttributes) -> dict: + """Extract all AirflowDag attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["airflow_dag_schedule"] = attrs.airflow_dag_schedule + result["airflow_dag_schedule_delta"] = attrs.airflow_dag_schedule_delta + result["airflow_tags"] = attrs.airflow_tags + result["airflow_run_version"] = attrs.airflow_run_version + result["airflow_run_open_lineage_version"] = attrs.airflow_run_open_lineage_version + result["airflow_run_name"] = attrs.airflow_run_name + result["airflow_run_type"] = attrs.airflow_run_type + result["airflow_run_start_time"] = attrs.airflow_run_start_time + result["airflow_run_end_time"] = attrs.airflow_run_end_time + result["airflow_run_open_lineage_state"] = attrs.airflow_run_open_lineage_state + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _airflow_dag_to_nested(airflow_dag: AirflowDag) -> AirflowDagNested: + """Convert flat AirflowDag to nested format.""" + attrs = AirflowDagAttributes() + _populate_airflow_dag_attrs(attrs, airflow_dag) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + airflow_dag, _AIRFLOW_DAG_REL_FIELDS, AirflowDagRelationshipAttributes + ) + return AirflowDagNested( + guid=airflow_dag.guid, + type_name=airflow_dag.type_name, + status=airflow_dag.status, + version=airflow_dag.version, + create_time=airflow_dag.create_time, + update_time=airflow_dag.update_time, + created_by=airflow_dag.created_by, + updated_by=airflow_dag.updated_by, + classifications=airflow_dag.classifications, + classification_names=airflow_dag.classification_names, + meanings=airflow_dag.meanings, + labels=airflow_dag.labels, + business_attributes=airflow_dag.business_attributes, + custom_attributes=airflow_dag.custom_attributes, + pending_tasks=airflow_dag.pending_tasks, + proxy=airflow_dag.proxy, + is_incomplete=airflow_dag.is_incomplete, + provenance_type=airflow_dag.provenance_type, + home_id=airflow_dag.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _airflow_dag_from_nested(nested: AirflowDagNested) -> AirflowDag: + """Convert nested format to flat AirflowDag.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else AirflowDagAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _AIRFLOW_DAG_REL_FIELDS, + AirflowDagRelationshipAttributes, + ) + return AirflowDag( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_airflow_dag_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _airflow_dag_to_nested_bytes(airflow_dag: AirflowDag, serde: Serde) -> bytes: + """Convert flat AirflowDag to nested JSON bytes.""" + return serde.encode(_airflow_dag_to_nested(airflow_dag)) + + +def _airflow_dag_from_nested_bytes(data: bytes, serde: Serde) -> AirflowDag: + """Convert nested JSON bytes to flat AirflowDag.""" + nested = serde.decode(data, AirflowDagNested) + return _airflow_dag_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +AirflowDag.AIRFLOW_DAG_SCHEDULE = KeywordField( + "airflowDagSchedule", "airflowDagSchedule" +) +AirflowDag.AIRFLOW_DAG_SCHEDULE_DELTA = NumericField( + "airflowDagScheduleDelta", "airflowDagScheduleDelta" +) +AirflowDag.AIRFLOW_TAGS = KeywordField("airflowTags", "airflowTags") +AirflowDag.AIRFLOW_RUN_VERSION = KeywordField("airflowRunVersion", "airflowRunVersion") +AirflowDag.AIRFLOW_RUN_OPEN_LINEAGE_VERSION = KeywordField( + "airflowRunOpenLineageVersion", "airflowRunOpenLineageVersion" +) +AirflowDag.AIRFLOW_RUN_NAME = KeywordField("airflowRunName", "airflowRunName") +AirflowDag.AIRFLOW_RUN_TYPE = KeywordField("airflowRunType", "airflowRunType") +AirflowDag.AIRFLOW_RUN_START_TIME = NumericField( + "airflowRunStartTime", "airflowRunStartTime" +) +AirflowDag.AIRFLOW_RUN_END_TIME = NumericField("airflowRunEndTime", "airflowRunEndTime") +AirflowDag.AIRFLOW_RUN_OPEN_LINEAGE_STATE = KeywordField( + "airflowRunOpenLineageState", "airflowRunOpenLineageState" +) +AirflowDag.AIRFLOW_TASKS = RelationField("airflowTasks") +AirflowDag.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +AirflowDag.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +AirflowDag.ANOMALO_CHECKS = RelationField("anomaloChecks") +AirflowDag.APPLICATION = RelationField("application") +AirflowDag.APPLICATION_FIELD = RelationField("applicationField") +AirflowDag.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +AirflowDag.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +AirflowDag.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +AirflowDag.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +AirflowDag.METRICS = RelationField("metrics") +AirflowDag.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +AirflowDag.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +AirflowDag.MEANINGS = RelationField("meanings") +AirflowDag.MC_MONITORS = RelationField("mcMonitors") +AirflowDag.MC_INCIDENTS = RelationField("mcIncidents") +AirflowDag.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +AirflowDag.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +AirflowDag.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +AirflowDag.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +AirflowDag.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +AirflowDag.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +AirflowDag.FILES = RelationField("files") +AirflowDag.LINKS = RelationField("links") +AirflowDag.README = RelationField("readme") +AirflowDag.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +AirflowDag.SODA_CHECKS = RelationField("sodaChecks") +AirflowDag.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +AirflowDag.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") +AirflowDag.SPARK_ORCHESTRATED_ASSETS = RelationField("sparkOrchestratedAssets") diff --git a/pyatlan_v9/model/assets/airflow_related.py b/pyatlan_v9/model/assets/airflow_related.py new file mode 100644 index 000000000..b90ab894e --- /dev/null +++ b/pyatlan_v9/model/assets/airflow_related.py @@ -0,0 +1,136 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Airflow module. + +This module contains all Related{Type} classes for the Airflow type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import List, Union + +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedCatalog +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedAirflow", + "RelatedAirflowDag", + "RelatedAirflowTask", +] + + +class RelatedAirflow(RelatedCatalog): + """ + Related entity reference for Airflow assets. + + Extends RelatedCatalog with Airflow-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Airflow" so it serializes correctly + + airflow_tags: Union[List[str], None, UnsetType] = UNSET + """Tags assigned to the asset in Airflow.""" + + airflow_run_version: Union[str, None, UnsetType] = UNSET + """Version of the run in Airflow.""" + + airflow_run_open_lineage_version: Union[str, None, UnsetType] = UNSET + """Version of the run in OpenLineage.""" + + airflow_run_name: Union[str, None, UnsetType] = UNSET + """Name of the run.""" + + airflow_run_type: Union[str, None, UnsetType] = UNSET + """Type of the run.""" + + airflow_run_start_time: Union[int, None, UnsetType] = UNSET + """Start time of the run.""" + + airflow_run_end_time: Union[int, None, UnsetType] = UNSET + """End time of the run.""" + + airflow_run_open_lineage_state: Union[str, None, UnsetType] = UNSET + """State of the run in OpenLineage.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Airflow" + + +class RelatedAirflowDag(RelatedAirflow): + """ + Related entity reference for AirflowDag assets. + + Extends RelatedAirflow with AirflowDag-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "AirflowDag" so it serializes correctly + + airflow_dag_schedule: Union[str, None, UnsetType] = UNSET + """Schedule for the DAG.""" + + airflow_dag_schedule_delta: Union[int, None, UnsetType] = UNSET + """Duration between scheduled runs, in seconds.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "AirflowDag" + + +class RelatedAirflowTask(RelatedAirflow): + """ + Related entity reference for AirflowTask assets. + + Extends RelatedAirflow with AirflowTask-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "AirflowTask" so it serializes correctly + + airflow_task_operator_class: Union[str, None, UnsetType] = UNSET + """Class name for the operator this task uses.""" + + airflow_dag_name: Union[str, None, UnsetType] = UNSET + """Simple name of the DAG this task is contained within.""" + + airflow_dag_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the DAG this task is contained within.""" + + airflow_task_connection_id: Union[str, None, UnsetType] = UNSET + """Identifier for the connection this task accesses.""" + + airflow_task_sql: Union[str, None, UnsetType] = UNSET + """SQL code that executes through this task.""" + + airflow_task_retry_number: Union[int, None, UnsetType] = UNSET + """Retry count for this task running.""" + + airflow_task_pool: Union[str, None, UnsetType] = UNSET + """Pool on which this run happened.""" + + airflow_task_pool_slots: Union[int, None, UnsetType] = UNSET + """Pool slots used for the run.""" + + airflow_task_queue: Union[str, None, UnsetType] = UNSET + """Queue on which this run happened.""" + + airflow_task_priority_weight: Union[int, None, UnsetType] = UNSET + """Priority of the run.""" + + airflow_task_trigger_rule: Union[str, None, UnsetType] = UNSET + """Trigger for the run.""" + + airflow_task_group_name: Union[str, None, UnsetType] = UNSET + """Group name for the task.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "AirflowTask" diff --git a/pyatlan_v9/model/assets/airflow_task.py b/pyatlan_v9/model/assets/airflow_task.py new file mode 100644 index 000000000..546d0285b --- /dev/null +++ b/pyatlan_v9/model/assets/airflow_task.py @@ -0,0 +1,850 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +AirflowTask asset model with flattened inheritance. + +This module provides: +- AirflowTask: Flat asset class (easy to use) +- AirflowTaskAttributes: Nested attributes struct (extends AssetAttributes) +- AirflowTaskNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .catalog_related import RelatedCatalog +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSpark, RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .airflow_related import RelatedAirflowDag, RelatedAirflowTask + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class AirflowTask(Asset): + """ + Instance of an Airflow task in Atlan. + """ + + AIRFLOW_TASK_OPERATOR_CLASS: ClassVar[Any] = None + AIRFLOW_DAG_NAME: ClassVar[Any] = None + AIRFLOW_DAG_QUALIFIED_NAME: ClassVar[Any] = None + AIRFLOW_TASK_CONNECTION_ID: ClassVar[Any] = None + AIRFLOW_TASK_SQL: ClassVar[Any] = None + AIRFLOW_TASK_RETRY_NUMBER: ClassVar[Any] = None + AIRFLOW_TASK_POOL: ClassVar[Any] = None + AIRFLOW_TASK_POOL_SLOTS: ClassVar[Any] = None + AIRFLOW_TASK_QUEUE: ClassVar[Any] = None + AIRFLOW_TASK_PRIORITY_WEIGHT: ClassVar[Any] = None + AIRFLOW_TASK_TRIGGER_RULE: ClassVar[Any] = None + AIRFLOW_TASK_GROUP_NAME: ClassVar[Any] = None + AIRFLOW_TAGS: ClassVar[Any] = None + AIRFLOW_RUN_VERSION: ClassVar[Any] = None + AIRFLOW_RUN_OPEN_LINEAGE_VERSION: ClassVar[Any] = None + AIRFLOW_RUN_NAME: ClassVar[Any] = None + AIRFLOW_RUN_TYPE: ClassVar[Any] = None + AIRFLOW_RUN_START_TIME: ClassVar[Any] = None + AIRFLOW_RUN_END_TIME: ClassVar[Any] = None + AIRFLOW_RUN_OPEN_LINEAGE_STATE: ClassVar[Any] = None + AIRFLOW_DAG: ClassVar[Any] = None + PROCESS: ClassVar[Any] = None + INPUTS: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUTS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + SPARK_ORCHESTRATED_ASSETS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "AirflowTask" + + airflow_task_operator_class: Union[str, None, UnsetType] = UNSET + """Class name for the operator this task uses.""" + + airflow_dag_name: Union[str, None, UnsetType] = UNSET + """Simple name of the DAG this task is contained within.""" + + airflow_dag_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the DAG this task is contained within.""" + + airflow_task_connection_id: Union[str, None, UnsetType] = UNSET + """Identifier for the connection this task accesses.""" + + airflow_task_sql: Union[str, None, UnsetType] = UNSET + """SQL code that executes through this task.""" + + airflow_task_retry_number: Union[int, None, UnsetType] = UNSET + """Retry count for this task running.""" + + airflow_task_pool: Union[str, None, UnsetType] = UNSET + """Pool on which this run happened.""" + + airflow_task_pool_slots: Union[int, None, UnsetType] = UNSET + """Pool slots used for the run.""" + + airflow_task_queue: Union[str, None, UnsetType] = UNSET + """Queue on which this run happened.""" + + airflow_task_priority_weight: Union[int, None, UnsetType] = UNSET + """Priority of the run.""" + + airflow_task_trigger_rule: Union[str, None, UnsetType] = UNSET + """Trigger for the run.""" + + airflow_task_group_name: Union[str, None, UnsetType] = UNSET + """Group name for the task.""" + + airflow_tags: Union[List[str], None, UnsetType] = UNSET + """Tags assigned to the asset in Airflow.""" + + airflow_run_version: Union[str, None, UnsetType] = UNSET + """Version of the run in Airflow.""" + + airflow_run_open_lineage_version: Union[str, None, UnsetType] = UNSET + """Version of the run in OpenLineage.""" + + airflow_run_name: Union[str, None, UnsetType] = UNSET + """Name of the run.""" + + airflow_run_type: Union[str, None, UnsetType] = UNSET + """Type of the run.""" + + airflow_run_start_time: Union[int, None, UnsetType] = UNSET + """Start time of the run.""" + + airflow_run_end_time: Union[int, None, UnsetType] = UNSET + """End time of the run.""" + + airflow_run_open_lineage_state: Union[str, None, UnsetType] = UNSET + """State of the run in OpenLineage.""" + + airflow_dag: Union[RelatedAirflowDag, None, UnsetType] = UNSET + """DAG in which this task exists.""" + + process: Union[RelatedProcess, None, UnsetType] = UNSET + """Process in which this task exists.""" + + inputs: Union[List[RelatedCatalog], None, UnsetType] = UNSET + """Assets that are inputs to this task.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + outputs: Union[List[RelatedCatalog], None, UnsetType] = UNSET + """Assets that are outputs from this task.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + spark_orchestrated_assets: Union[List[RelatedSpark], None, UnsetType] = UNSET + """Spark assets that are executed by this airflow asset.""" + + def __post_init__(self) -> None: + self.type_name = "AirflowTask" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + airflow_dag_qualified_name: str, + connection_qualified_name: str | None = None, + ) -> "AirflowTask": + validate_required_fields( + ["name", "airflow_dag_qualified_name"], + [name, airflow_dag_qualified_name], + ) + fields = airflow_dag_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + connection_qn = connection_qualified_name or ( + f"{fields[0]}/{fields[1]}/{fields[2]}" if len(fields) >= 3 else None + ) + qualified_name = f"{airflow_dag_qualified_name}/{name}" + return cls( + name=name, + qualified_name=qualified_name, + connector_name=connector_name, + connection_qualified_name=connection_qn, + airflow_dag_qualified_name=airflow_dag_qualified_name, + airflow_dag=RelatedAirflowDag(qualified_name=airflow_dag_qualified_name), + ) + + @classmethod + def create(cls, **kwargs) -> "AirflowTask": + return cls.creator(**kwargs) + + @classmethod + def create_for_modification(cls, **kwargs) -> "AirflowTask": + return cls.updater(**kwargs) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _airflow_task_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> AirflowTask: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + AirflowTask instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _airflow_task_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class AirflowTaskAttributes(AssetAttributes): + """AirflowTask-specific attributes for nested API format.""" + + airflow_task_operator_class: Union[str, None, UnsetType] = UNSET + """Class name for the operator this task uses.""" + + airflow_dag_name: Union[str, None, UnsetType] = UNSET + """Simple name of the DAG this task is contained within.""" + + airflow_dag_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the DAG this task is contained within.""" + + airflow_task_connection_id: Union[str, None, UnsetType] = UNSET + """Identifier for the connection this task accesses.""" + + airflow_task_sql: Union[str, None, UnsetType] = UNSET + """SQL code that executes through this task.""" + + airflow_task_retry_number: Union[int, None, UnsetType] = UNSET + """Retry count for this task running.""" + + airflow_task_pool: Union[str, None, UnsetType] = UNSET + """Pool on which this run happened.""" + + airflow_task_pool_slots: Union[int, None, UnsetType] = UNSET + """Pool slots used for the run.""" + + airflow_task_queue: Union[str, None, UnsetType] = UNSET + """Queue on which this run happened.""" + + airflow_task_priority_weight: Union[int, None, UnsetType] = UNSET + """Priority of the run.""" + + airflow_task_trigger_rule: Union[str, None, UnsetType] = UNSET + """Trigger for the run.""" + + airflow_task_group_name: Union[str, None, UnsetType] = UNSET + """Group name for the task.""" + + airflow_tags: Union[List[str], None, UnsetType] = UNSET + """Tags assigned to the asset in Airflow.""" + + airflow_run_version: Union[str, None, UnsetType] = UNSET + """Version of the run in Airflow.""" + + airflow_run_open_lineage_version: Union[str, None, UnsetType] = UNSET + """Version of the run in OpenLineage.""" + + airflow_run_name: Union[str, None, UnsetType] = UNSET + """Name of the run.""" + + airflow_run_type: Union[str, None, UnsetType] = UNSET + """Type of the run.""" + + airflow_run_start_time: Union[int, None, UnsetType] = UNSET + """Start time of the run.""" + + airflow_run_end_time: Union[int, None, UnsetType] = UNSET + """End time of the run.""" + + airflow_run_open_lineage_state: Union[str, None, UnsetType] = UNSET + """State of the run in OpenLineage.""" + + +class AirflowTaskRelationshipAttributes(AssetRelationshipAttributes): + """AirflowTask-specific relationship attributes for nested API format.""" + + airflow_dag: Union[RelatedAirflowDag, None, UnsetType] = UNSET + """DAG in which this task exists.""" + + process: Union[RelatedProcess, None, UnsetType] = UNSET + """Process in which this task exists.""" + + inputs: Union[List[RelatedCatalog], None, UnsetType] = UNSET + """Assets that are inputs to this task.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + outputs: Union[List[RelatedCatalog], None, UnsetType] = UNSET + """Assets that are outputs from this task.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + spark_orchestrated_assets: Union[List[RelatedSpark], None, UnsetType] = UNSET + """Spark assets that are executed by this airflow asset.""" + + +class AirflowTaskNested(AssetNested): + """AirflowTask in nested API format for high-performance serialization.""" + + attributes: Union[AirflowTaskAttributes, UnsetType] = UNSET + relationship_attributes: Union[AirflowTaskRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + AirflowTaskRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + AirflowTaskRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_AIRFLOW_TASK_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "airflow_dag", + "process", + "inputs", + "input_to_airflow_tasks", + "outputs", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", + "spark_orchestrated_assets", +] + + +def _populate_airflow_task_attrs( + attrs: AirflowTaskAttributes, obj: AirflowTask +) -> None: + """Populate AirflowTask-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.airflow_task_operator_class = obj.airflow_task_operator_class + attrs.airflow_dag_name = obj.airflow_dag_name + attrs.airflow_dag_qualified_name = obj.airflow_dag_qualified_name + attrs.airflow_task_connection_id = obj.airflow_task_connection_id + attrs.airflow_task_sql = obj.airflow_task_sql + attrs.airflow_task_retry_number = obj.airflow_task_retry_number + attrs.airflow_task_pool = obj.airflow_task_pool + attrs.airflow_task_pool_slots = obj.airflow_task_pool_slots + attrs.airflow_task_queue = obj.airflow_task_queue + attrs.airflow_task_priority_weight = obj.airflow_task_priority_weight + attrs.airflow_task_trigger_rule = obj.airflow_task_trigger_rule + attrs.airflow_task_group_name = obj.airflow_task_group_name + attrs.airflow_tags = obj.airflow_tags + attrs.airflow_run_version = obj.airflow_run_version + attrs.airflow_run_open_lineage_version = obj.airflow_run_open_lineage_version + attrs.airflow_run_name = obj.airflow_run_name + attrs.airflow_run_type = obj.airflow_run_type + attrs.airflow_run_start_time = obj.airflow_run_start_time + attrs.airflow_run_end_time = obj.airflow_run_end_time + attrs.airflow_run_open_lineage_state = obj.airflow_run_open_lineage_state + + +def _extract_airflow_task_attrs(attrs: AirflowTaskAttributes) -> dict: + """Extract all AirflowTask attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["airflow_task_operator_class"] = attrs.airflow_task_operator_class + result["airflow_dag_name"] = attrs.airflow_dag_name + result["airflow_dag_qualified_name"] = attrs.airflow_dag_qualified_name + result["airflow_task_connection_id"] = attrs.airflow_task_connection_id + result["airflow_task_sql"] = attrs.airflow_task_sql + result["airflow_task_retry_number"] = attrs.airflow_task_retry_number + result["airflow_task_pool"] = attrs.airflow_task_pool + result["airflow_task_pool_slots"] = attrs.airflow_task_pool_slots + result["airflow_task_queue"] = attrs.airflow_task_queue + result["airflow_task_priority_weight"] = attrs.airflow_task_priority_weight + result["airflow_task_trigger_rule"] = attrs.airflow_task_trigger_rule + result["airflow_task_group_name"] = attrs.airflow_task_group_name + result["airflow_tags"] = attrs.airflow_tags + result["airflow_run_version"] = attrs.airflow_run_version + result["airflow_run_open_lineage_version"] = attrs.airflow_run_open_lineage_version + result["airflow_run_name"] = attrs.airflow_run_name + result["airflow_run_type"] = attrs.airflow_run_type + result["airflow_run_start_time"] = attrs.airflow_run_start_time + result["airflow_run_end_time"] = attrs.airflow_run_end_time + result["airflow_run_open_lineage_state"] = attrs.airflow_run_open_lineage_state + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _airflow_task_to_nested(airflow_task: AirflowTask) -> AirflowTaskNested: + """Convert flat AirflowTask to nested format.""" + attrs = AirflowTaskAttributes() + _populate_airflow_task_attrs(attrs, airflow_task) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + airflow_task, _AIRFLOW_TASK_REL_FIELDS, AirflowTaskRelationshipAttributes + ) + return AirflowTaskNested( + guid=airflow_task.guid, + type_name=airflow_task.type_name, + status=airflow_task.status, + version=airflow_task.version, + create_time=airflow_task.create_time, + update_time=airflow_task.update_time, + created_by=airflow_task.created_by, + updated_by=airflow_task.updated_by, + classifications=airflow_task.classifications, + classification_names=airflow_task.classification_names, + meanings=airflow_task.meanings, + labels=airflow_task.labels, + business_attributes=airflow_task.business_attributes, + custom_attributes=airflow_task.custom_attributes, + pending_tasks=airflow_task.pending_tasks, + proxy=airflow_task.proxy, + is_incomplete=airflow_task.is_incomplete, + provenance_type=airflow_task.provenance_type, + home_id=airflow_task.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _airflow_task_from_nested(nested: AirflowTaskNested) -> AirflowTask: + """Convert nested format to flat AirflowTask.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else AirflowTaskAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _AIRFLOW_TASK_REL_FIELDS, + AirflowTaskRelationshipAttributes, + ) + return AirflowTask( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_airflow_task_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _airflow_task_to_nested_bytes(airflow_task: AirflowTask, serde: Serde) -> bytes: + """Convert flat AirflowTask to nested JSON bytes.""" + return serde.encode(_airflow_task_to_nested(airflow_task)) + + +def _airflow_task_from_nested_bytes(data: bytes, serde: Serde) -> AirflowTask: + """Convert nested JSON bytes to flat AirflowTask.""" + nested = serde.decode(data, AirflowTaskNested) + return _airflow_task_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +AirflowTask.AIRFLOW_TASK_OPERATOR_CLASS = KeywordTextField( + "airflowTaskOperatorClass", + "airflowTaskOperatorClass", + "airflowTaskOperatorClass.text", +) +AirflowTask.AIRFLOW_DAG_NAME = KeywordTextField( + "airflowDagName", "airflowDagName", "airflowDagName.text" +) +AirflowTask.AIRFLOW_DAG_QUALIFIED_NAME = KeywordField( + "airflowDagQualifiedName", "airflowDagQualifiedName" +) +AirflowTask.AIRFLOW_TASK_CONNECTION_ID = KeywordTextField( + "airflowTaskConnectionId", "airflowTaskConnectionId", "airflowTaskConnectionId.text" +) +AirflowTask.AIRFLOW_TASK_SQL = KeywordField("airflowTaskSql", "airflowTaskSql") +AirflowTask.AIRFLOW_TASK_RETRY_NUMBER = NumericField( + "airflowTaskRetryNumber", "airflowTaskRetryNumber" +) +AirflowTask.AIRFLOW_TASK_POOL = KeywordField("airflowTaskPool", "airflowTaskPool") +AirflowTask.AIRFLOW_TASK_POOL_SLOTS = NumericField( + "airflowTaskPoolSlots", "airflowTaskPoolSlots" +) +AirflowTask.AIRFLOW_TASK_QUEUE = KeywordField("airflowTaskQueue", "airflowTaskQueue") +AirflowTask.AIRFLOW_TASK_PRIORITY_WEIGHT = NumericField( + "airflowTaskPriorityWeight", "airflowTaskPriorityWeight" +) +AirflowTask.AIRFLOW_TASK_TRIGGER_RULE = KeywordField( + "airflowTaskTriggerRule", "airflowTaskTriggerRule" +) +AirflowTask.AIRFLOW_TASK_GROUP_NAME = KeywordField( + "airflowTaskGroupName", "airflowTaskGroupName" +) +AirflowTask.AIRFLOW_TAGS = KeywordField("airflowTags", "airflowTags") +AirflowTask.AIRFLOW_RUN_VERSION = KeywordField("airflowRunVersion", "airflowRunVersion") +AirflowTask.AIRFLOW_RUN_OPEN_LINEAGE_VERSION = KeywordField( + "airflowRunOpenLineageVersion", "airflowRunOpenLineageVersion" +) +AirflowTask.AIRFLOW_RUN_NAME = KeywordField("airflowRunName", "airflowRunName") +AirflowTask.AIRFLOW_RUN_TYPE = KeywordField("airflowRunType", "airflowRunType") +AirflowTask.AIRFLOW_RUN_START_TIME = NumericField( + "airflowRunStartTime", "airflowRunStartTime" +) +AirflowTask.AIRFLOW_RUN_END_TIME = NumericField( + "airflowRunEndTime", "airflowRunEndTime" +) +AirflowTask.AIRFLOW_RUN_OPEN_LINEAGE_STATE = KeywordField( + "airflowRunOpenLineageState", "airflowRunOpenLineageState" +) +AirflowTask.AIRFLOW_DAG = RelationField("airflowDag") +AirflowTask.PROCESS = RelationField("process") +AirflowTask.INPUTS = RelationField("inputs") +AirflowTask.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +AirflowTask.OUTPUTS = RelationField("outputs") +AirflowTask.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +AirflowTask.ANOMALO_CHECKS = RelationField("anomaloChecks") +AirflowTask.APPLICATION = RelationField("application") +AirflowTask.APPLICATION_FIELD = RelationField("applicationField") +AirflowTask.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +AirflowTask.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +AirflowTask.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +AirflowTask.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +AirflowTask.METRICS = RelationField("metrics") +AirflowTask.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +AirflowTask.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +AirflowTask.MEANINGS = RelationField("meanings") +AirflowTask.MC_MONITORS = RelationField("mcMonitors") +AirflowTask.MC_INCIDENTS = RelationField("mcIncidents") +AirflowTask.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +AirflowTask.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +AirflowTask.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +AirflowTask.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +AirflowTask.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +AirflowTask.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +AirflowTask.FILES = RelationField("files") +AirflowTask.LINKS = RelationField("links") +AirflowTask.README = RelationField("readme") +AirflowTask.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +AirflowTask.SODA_CHECKS = RelationField("sodaChecks") +AirflowTask.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +AirflowTask.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") +AirflowTask.SPARK_ORCHESTRATED_ASSETS = RelationField("sparkOrchestratedAssets") diff --git a/pyatlan_v9/model/assets/anaplan.py b/pyatlan_v9/model/assets/anaplan.py new file mode 100644 index 000000000..3f78da5fe --- /dev/null +++ b/pyatlan_v9/model/assets/anaplan.py @@ -0,0 +1,603 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Anaplan asset model with flattened inheritance. + +This module provides: +- Anaplan: Flat asset class (easy to use) +- AnaplanAttributes: Nested attributes struct (extends AssetAttributes) +- AnaplanNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Anaplan(Asset): + """ + Base class for all Anaplan types. + """ + + ANAPLAN_WORKSPACE_QUALIFIED_NAME: ClassVar[Any] = None + ANAPLAN_WORKSPACE_NAME: ClassVar[Any] = None + ANAPLAN_MODEL_QUALIFIED_NAME: ClassVar[Any] = None + ANAPLAN_MODEL_NAME: ClassVar[Any] = None + ANAPLAN_MODULE_QUALIFIED_NAME: ClassVar[Any] = None + ANAPLAN_MODULE_NAME: ClassVar[Any] = None + ANAPLAN_SOURCE_ID: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Anaplan" + + anaplan_workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanWorkspace asset that contains this asset (AnaplanModel and everything under its hierarchy).""" + + anaplan_workspace_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanWorkspace asset that contains this asset (AnaplanModel and everything under its hierarchy).""" + + anaplan_model_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanModel asset that contains this asset (AnaplanModule and everything under its hierarchy).""" + + anaplan_model_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanModel asset that contains this asset (AnaplanModule and everything under its hierarchy).""" + + anaplan_module_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanModule asset that contains this asset (AnaplanLineItem, AnaplanList, AnaplanView and everything under their hierarchy).""" + + anaplan_module_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanModule asset that contains this asset (AnaplanLineItem, AnaplanList, AnaplanView and everything under their hierarchy).""" + + anaplan_source_id: Union[str, None, UnsetType] = UNSET + """Id/Guid of the Anaplan asset in the source system.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Anaplan" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _anaplan_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Anaplan: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Anaplan instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _anaplan_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class AnaplanAttributes(AssetAttributes): + """Anaplan-specific attributes for nested API format.""" + + anaplan_workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanWorkspace asset that contains this asset (AnaplanModel and everything under its hierarchy).""" + + anaplan_workspace_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanWorkspace asset that contains this asset (AnaplanModel and everything under its hierarchy).""" + + anaplan_model_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanModel asset that contains this asset (AnaplanModule and everything under its hierarchy).""" + + anaplan_model_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanModel asset that contains this asset (AnaplanModule and everything under its hierarchy).""" + + anaplan_module_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanModule asset that contains this asset (AnaplanLineItem, AnaplanList, AnaplanView and everything under their hierarchy).""" + + anaplan_module_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanModule asset that contains this asset (AnaplanLineItem, AnaplanList, AnaplanView and everything under their hierarchy).""" + + anaplan_source_id: Union[str, None, UnsetType] = UNSET + """Id/Guid of the Anaplan asset in the source system.""" + + +class AnaplanRelationshipAttributes(AssetRelationshipAttributes): + """Anaplan-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class AnaplanNested(AssetNested): + """Anaplan in nested API format for high-performance serialization.""" + + attributes: Union[AnaplanAttributes, UnsetType] = UNSET + relationship_attributes: Union[AnaplanRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[AnaplanRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[AnaplanRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_ANAPLAN_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_anaplan_attrs(attrs: AnaplanAttributes, obj: Anaplan) -> None: + """Populate Anaplan-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.anaplan_workspace_qualified_name = obj.anaplan_workspace_qualified_name + attrs.anaplan_workspace_name = obj.anaplan_workspace_name + attrs.anaplan_model_qualified_name = obj.anaplan_model_qualified_name + attrs.anaplan_model_name = obj.anaplan_model_name + attrs.anaplan_module_qualified_name = obj.anaplan_module_qualified_name + attrs.anaplan_module_name = obj.anaplan_module_name + attrs.anaplan_source_id = obj.anaplan_source_id + + +def _extract_anaplan_attrs(attrs: AnaplanAttributes) -> dict: + """Extract all Anaplan attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["anaplan_workspace_qualified_name"] = attrs.anaplan_workspace_qualified_name + result["anaplan_workspace_name"] = attrs.anaplan_workspace_name + result["anaplan_model_qualified_name"] = attrs.anaplan_model_qualified_name + result["anaplan_model_name"] = attrs.anaplan_model_name + result["anaplan_module_qualified_name"] = attrs.anaplan_module_qualified_name + result["anaplan_module_name"] = attrs.anaplan_module_name + result["anaplan_source_id"] = attrs.anaplan_source_id + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _anaplan_to_nested(anaplan: Anaplan) -> AnaplanNested: + """Convert flat Anaplan to nested format.""" + attrs = AnaplanAttributes() + _populate_anaplan_attrs(attrs, anaplan) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + anaplan, _ANAPLAN_REL_FIELDS, AnaplanRelationshipAttributes + ) + return AnaplanNested( + guid=anaplan.guid, + type_name=anaplan.type_name, + status=anaplan.status, + version=anaplan.version, + create_time=anaplan.create_time, + update_time=anaplan.update_time, + created_by=anaplan.created_by, + updated_by=anaplan.updated_by, + classifications=anaplan.classifications, + classification_names=anaplan.classification_names, + meanings=anaplan.meanings, + labels=anaplan.labels, + business_attributes=anaplan.business_attributes, + custom_attributes=anaplan.custom_attributes, + pending_tasks=anaplan.pending_tasks, + proxy=anaplan.proxy, + is_incomplete=anaplan.is_incomplete, + provenance_type=anaplan.provenance_type, + home_id=anaplan.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _anaplan_from_nested(nested: AnaplanNested) -> Anaplan: + """Convert nested format to flat Anaplan.""" + attrs = nested.attributes if nested.attributes is not UNSET else AnaplanAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _ANAPLAN_REL_FIELDS, + AnaplanRelationshipAttributes, + ) + return Anaplan( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_anaplan_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _anaplan_to_nested_bytes(anaplan: Anaplan, serde: Serde) -> bytes: + """Convert flat Anaplan to nested JSON bytes.""" + return serde.encode(_anaplan_to_nested(anaplan)) + + +def _anaplan_from_nested_bytes(data: bytes, serde: Serde) -> Anaplan: + """Convert nested JSON bytes to flat Anaplan.""" + nested = serde.decode(data, AnaplanNested) + return _anaplan_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +Anaplan.ANAPLAN_WORKSPACE_QUALIFIED_NAME = KeywordField( + "anaplanWorkspaceQualifiedName", "anaplanWorkspaceQualifiedName" +) +Anaplan.ANAPLAN_WORKSPACE_NAME = KeywordField( + "anaplanWorkspaceName", "anaplanWorkspaceName" +) +Anaplan.ANAPLAN_MODEL_QUALIFIED_NAME = KeywordField( + "anaplanModelQualifiedName", "anaplanModelQualifiedName" +) +Anaplan.ANAPLAN_MODEL_NAME = KeywordField("anaplanModelName", "anaplanModelName") +Anaplan.ANAPLAN_MODULE_QUALIFIED_NAME = KeywordField( + "anaplanModuleQualifiedName", "anaplanModuleQualifiedName" +) +Anaplan.ANAPLAN_MODULE_NAME = KeywordField("anaplanModuleName", "anaplanModuleName") +Anaplan.ANAPLAN_SOURCE_ID = KeywordField("anaplanSourceId", "anaplanSourceId") +Anaplan.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Anaplan.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Anaplan.ANOMALO_CHECKS = RelationField("anomaloChecks") +Anaplan.APPLICATION = RelationField("application") +Anaplan.APPLICATION_FIELD = RelationField("applicationField") +Anaplan.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Anaplan.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Anaplan.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Anaplan.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Anaplan.METRICS = RelationField("metrics") +Anaplan.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Anaplan.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Anaplan.MEANINGS = RelationField("meanings") +Anaplan.MC_MONITORS = RelationField("mcMonitors") +Anaplan.MC_INCIDENTS = RelationField("mcIncidents") +Anaplan.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Anaplan.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Anaplan.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Anaplan.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Anaplan.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Anaplan.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Anaplan.FILES = RelationField("files") +Anaplan.LINKS = RelationField("links") +Anaplan.README = RelationField("readme") +Anaplan.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Anaplan.SODA_CHECKS = RelationField("sodaChecks") +Anaplan.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Anaplan.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/anaplan_app.py b/pyatlan_v9/model/assets/anaplan_app.py new file mode 100644 index 000000000..1ec2f74ba --- /dev/null +++ b/pyatlan_v9/model/assets/anaplan_app.py @@ -0,0 +1,643 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +AnaplanApp asset model with flattened inheritance. + +This module provides: +- AnaplanApp: Flat asset class (easy to use) +- AnaplanAppAttributes: Nested attributes struct (extends AssetAttributes) +- AnaplanAppNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .anaplan_related import RelatedAnaplanPage + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class AnaplanApp(Asset): + """ + Instances of an AnaplanApp in Atlan. + """ + + ANAPLAN_WORKSPACE_QUALIFIED_NAME: ClassVar[Any] = None + ANAPLAN_WORKSPACE_NAME: ClassVar[Any] = None + ANAPLAN_MODEL_QUALIFIED_NAME: ClassVar[Any] = None + ANAPLAN_MODEL_NAME: ClassVar[Any] = None + ANAPLAN_MODULE_QUALIFIED_NAME: ClassVar[Any] = None + ANAPLAN_MODULE_NAME: ClassVar[Any] = None + ANAPLAN_SOURCE_ID: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANAPLAN_PAGES: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "AnaplanApp" + + anaplan_workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanWorkspace asset that contains this asset (AnaplanModel and everything under its hierarchy).""" + + anaplan_workspace_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanWorkspace asset that contains this asset (AnaplanModel and everything under its hierarchy).""" + + anaplan_model_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanModel asset that contains this asset (AnaplanModule and everything under its hierarchy).""" + + anaplan_model_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanModel asset that contains this asset (AnaplanModule and everything under its hierarchy).""" + + anaplan_module_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanModule asset that contains this asset (AnaplanLineItem, AnaplanList, AnaplanView and everything under their hierarchy).""" + + anaplan_module_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanModule asset that contains this asset (AnaplanLineItem, AnaplanList, AnaplanView and everything under their hierarchy).""" + + anaplan_source_id: Union[str, None, UnsetType] = UNSET + """Id/Guid of the Anaplan asset in the source system.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anaplan_pages: Union[List[RelatedAnaplanPage], None, UnsetType] = UNSET + """Indidivual pages contained in the app.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "AnaplanApp" + + @classmethod + @init_guid + def creator(cls, *, name: str, connection_qualified_name: str) -> "AnaplanApp": + """Create a new AnaplanApp asset.""" + validate_required_fields( + ["name", "connection_qualified_name"], [name, connection_qualified_name] + ) + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + return cls( + name=name, + qualified_name=f"{connection_qualified_name}/{name}", + connection_qualified_name=connection_qualified_name, + connector_name=connector_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "AnaplanApp": + """Create an AnaplanApp instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "AnaplanApp": + """Return only fields required for update operations.""" + return AnaplanApp.updater(qualified_name=self.qualified_name, name=self.name) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _anaplan_app_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> AnaplanApp: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + AnaplanApp instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _anaplan_app_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class AnaplanAppAttributes(AssetAttributes): + """AnaplanApp-specific attributes for nested API format.""" + + anaplan_workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanWorkspace asset that contains this asset (AnaplanModel and everything under its hierarchy).""" + + anaplan_workspace_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanWorkspace asset that contains this asset (AnaplanModel and everything under its hierarchy).""" + + anaplan_model_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanModel asset that contains this asset (AnaplanModule and everything under its hierarchy).""" + + anaplan_model_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanModel asset that contains this asset (AnaplanModule and everything under its hierarchy).""" + + anaplan_module_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanModule asset that contains this asset (AnaplanLineItem, AnaplanList, AnaplanView and everything under their hierarchy).""" + + anaplan_module_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanModule asset that contains this asset (AnaplanLineItem, AnaplanList, AnaplanView and everything under their hierarchy).""" + + anaplan_source_id: Union[str, None, UnsetType] = UNSET + """Id/Guid of the Anaplan asset in the source system.""" + + +class AnaplanAppRelationshipAttributes(AssetRelationshipAttributes): + """AnaplanApp-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anaplan_pages: Union[List[RelatedAnaplanPage], None, UnsetType] = UNSET + """Indidivual pages contained in the app.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class AnaplanAppNested(AssetNested): + """AnaplanApp in nested API format for high-performance serialization.""" + + attributes: Union[AnaplanAppAttributes, UnsetType] = UNSET + relationship_attributes: Union[AnaplanAppRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + AnaplanAppRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + AnaplanAppRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_ANAPLAN_APP_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anaplan_pages", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_anaplan_app_attrs(attrs: AnaplanAppAttributes, obj: AnaplanApp) -> None: + """Populate AnaplanApp-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.anaplan_workspace_qualified_name = obj.anaplan_workspace_qualified_name + attrs.anaplan_workspace_name = obj.anaplan_workspace_name + attrs.anaplan_model_qualified_name = obj.anaplan_model_qualified_name + attrs.anaplan_model_name = obj.anaplan_model_name + attrs.anaplan_module_qualified_name = obj.anaplan_module_qualified_name + attrs.anaplan_module_name = obj.anaplan_module_name + attrs.anaplan_source_id = obj.anaplan_source_id + + +def _extract_anaplan_app_attrs(attrs: AnaplanAppAttributes) -> dict: + """Extract all AnaplanApp attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["anaplan_workspace_qualified_name"] = attrs.anaplan_workspace_qualified_name + result["anaplan_workspace_name"] = attrs.anaplan_workspace_name + result["anaplan_model_qualified_name"] = attrs.anaplan_model_qualified_name + result["anaplan_model_name"] = attrs.anaplan_model_name + result["anaplan_module_qualified_name"] = attrs.anaplan_module_qualified_name + result["anaplan_module_name"] = attrs.anaplan_module_name + result["anaplan_source_id"] = attrs.anaplan_source_id + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _anaplan_app_to_nested(anaplan_app: AnaplanApp) -> AnaplanAppNested: + """Convert flat AnaplanApp to nested format.""" + attrs = AnaplanAppAttributes() + _populate_anaplan_app_attrs(attrs, anaplan_app) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + anaplan_app, _ANAPLAN_APP_REL_FIELDS, AnaplanAppRelationshipAttributes + ) + return AnaplanAppNested( + guid=anaplan_app.guid, + type_name=anaplan_app.type_name, + status=anaplan_app.status, + version=anaplan_app.version, + create_time=anaplan_app.create_time, + update_time=anaplan_app.update_time, + created_by=anaplan_app.created_by, + updated_by=anaplan_app.updated_by, + classifications=anaplan_app.classifications, + classification_names=anaplan_app.classification_names, + meanings=anaplan_app.meanings, + labels=anaplan_app.labels, + business_attributes=anaplan_app.business_attributes, + custom_attributes=anaplan_app.custom_attributes, + pending_tasks=anaplan_app.pending_tasks, + proxy=anaplan_app.proxy, + is_incomplete=anaplan_app.is_incomplete, + provenance_type=anaplan_app.provenance_type, + home_id=anaplan_app.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _anaplan_app_from_nested(nested: AnaplanAppNested) -> AnaplanApp: + """Convert nested format to flat AnaplanApp.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else AnaplanAppAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _ANAPLAN_APP_REL_FIELDS, + AnaplanAppRelationshipAttributes, + ) + return AnaplanApp( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_anaplan_app_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _anaplan_app_to_nested_bytes(anaplan_app: AnaplanApp, serde: Serde) -> bytes: + """Convert flat AnaplanApp to nested JSON bytes.""" + return serde.encode(_anaplan_app_to_nested(anaplan_app)) + + +def _anaplan_app_from_nested_bytes(data: bytes, serde: Serde) -> AnaplanApp: + """Convert nested JSON bytes to flat AnaplanApp.""" + nested = serde.decode(data, AnaplanAppNested) + return _anaplan_app_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +AnaplanApp.ANAPLAN_WORKSPACE_QUALIFIED_NAME = KeywordField( + "anaplanWorkspaceQualifiedName", "anaplanWorkspaceQualifiedName" +) +AnaplanApp.ANAPLAN_WORKSPACE_NAME = KeywordField( + "anaplanWorkspaceName", "anaplanWorkspaceName" +) +AnaplanApp.ANAPLAN_MODEL_QUALIFIED_NAME = KeywordField( + "anaplanModelQualifiedName", "anaplanModelQualifiedName" +) +AnaplanApp.ANAPLAN_MODEL_NAME = KeywordField("anaplanModelName", "anaplanModelName") +AnaplanApp.ANAPLAN_MODULE_QUALIFIED_NAME = KeywordField( + "anaplanModuleQualifiedName", "anaplanModuleQualifiedName" +) +AnaplanApp.ANAPLAN_MODULE_NAME = KeywordField("anaplanModuleName", "anaplanModuleName") +AnaplanApp.ANAPLAN_SOURCE_ID = KeywordField("anaplanSourceId", "anaplanSourceId") +AnaplanApp.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +AnaplanApp.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +AnaplanApp.ANAPLAN_PAGES = RelationField("anaplanPages") +AnaplanApp.ANOMALO_CHECKS = RelationField("anomaloChecks") +AnaplanApp.APPLICATION = RelationField("application") +AnaplanApp.APPLICATION_FIELD = RelationField("applicationField") +AnaplanApp.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +AnaplanApp.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +AnaplanApp.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +AnaplanApp.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +AnaplanApp.METRICS = RelationField("metrics") +AnaplanApp.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +AnaplanApp.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +AnaplanApp.MEANINGS = RelationField("meanings") +AnaplanApp.MC_MONITORS = RelationField("mcMonitors") +AnaplanApp.MC_INCIDENTS = RelationField("mcIncidents") +AnaplanApp.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +AnaplanApp.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +AnaplanApp.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +AnaplanApp.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +AnaplanApp.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +AnaplanApp.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +AnaplanApp.FILES = RelationField("files") +AnaplanApp.LINKS = RelationField("links") +AnaplanApp.README = RelationField("readme") +AnaplanApp.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +AnaplanApp.SODA_CHECKS = RelationField("sodaChecks") +AnaplanApp.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +AnaplanApp.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/anaplan_dimension.py b/pyatlan_v9/model/assets/anaplan_dimension.py new file mode 100644 index 000000000..2d0ce22e4 --- /dev/null +++ b/pyatlan_v9/model/assets/anaplan_dimension.py @@ -0,0 +1,736 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +AnaplanDimension asset model with flattened inheritance. + +This module provides: +- AnaplanDimension: Flat asset class (easy to use) +- AnaplanDimensionAttributes: Nested attributes struct (extends AssetAttributes) +- AnaplanDimensionNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan.model.enums import AtlanConnectorType +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .anaplan_related import ( + RelatedAnaplanLineItem, + RelatedAnaplanModel, + RelatedAnaplanView, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class AnaplanDimension(Asset): + """ + Instances of an AnaplanDimension in Atlan. + """ + + ANAPLAN_WORKSPACE_QUALIFIED_NAME: ClassVar[Any] = None + ANAPLAN_WORKSPACE_NAME: ClassVar[Any] = None + ANAPLAN_MODEL_QUALIFIED_NAME: ClassVar[Any] = None + ANAPLAN_MODEL_NAME: ClassVar[Any] = None + ANAPLAN_MODULE_QUALIFIED_NAME: ClassVar[Any] = None + ANAPLAN_MODULE_NAME: ClassVar[Any] = None + ANAPLAN_SOURCE_ID: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANAPLAN_MODEL: ClassVar[Any] = None + ANAPLAN_LINE_ITEMS: ClassVar[Any] = None + ANAPLAN_ROW_VIEWS: ClassVar[Any] = None + ANAPLAN_COLUMN_VIEWS: ClassVar[Any] = None + ANAPLAN_PAGE_VIEWS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "AnaplanDimension" + + anaplan_workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanWorkspace asset that contains this asset (AnaplanModel and everything under its hierarchy).""" + + anaplan_workspace_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanWorkspace asset that contains this asset (AnaplanModel and everything under its hierarchy).""" + + anaplan_model_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanModel asset that contains this asset (AnaplanModule and everything under its hierarchy).""" + + anaplan_model_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanModel asset that contains this asset (AnaplanModule and everything under its hierarchy).""" + + anaplan_module_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanModule asset that contains this asset (AnaplanLineItem, AnaplanList, AnaplanView and everything under their hierarchy).""" + + anaplan_module_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanModule asset that contains this asset (AnaplanLineItem, AnaplanList, AnaplanView and everything under their hierarchy).""" + + anaplan_source_id: Union[str, None, UnsetType] = UNSET + """Id/Guid of the Anaplan asset in the source system.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anaplan_model: Union[RelatedAnaplanModel, None, UnsetType] = UNSET + """Model containing the dimension.""" + + anaplan_line_items: Union[List[RelatedAnaplanLineItem], None, UnsetType] = UNSET + """Line items related to the dimension.""" + + anaplan_row_views: Union[List[RelatedAnaplanView], None, UnsetType] = UNSET + """Views related to the row dimension.""" + + anaplan_column_views: Union[List[RelatedAnaplanView], None, UnsetType] = UNSET + """Views related to the column dimension.""" + + anaplan_page_views: Union[List[RelatedAnaplanView], None, UnsetType] = UNSET + """Views related to the page dimension.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "AnaplanDimension" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + model_qualified_name: str, + connection_qualified_name: str | None = None, + ) -> "AnaplanDimension": + """Create a new AnaplanDimension asset.""" + validate_required_fields( + ["name", "model_qualified_name"], [name, model_qualified_name] + ) + fields = model_qualified_name.split("/") + connection_qn: Union[str, None, UnsetType] = UNSET + if connection_qualified_name is not None: + connector_name = str( + AtlanConnectorType.get_connector_name(connection_qualified_name) + ) + else: + connection_qn, connector_name = AtlanConnectorType.get_connector_name( + model_qualified_name, "model_qualified_name", 5 + ) + workspace_qualified_name = "/".join(fields[:4]) if len(fields) >= 4 else UNSET + workspace_name = fields[3] if len(fields) > 3 else UNSET + model_name = fields[4] if len(fields) > 4 else UNSET + return cls( + name=name, + qualified_name=f"{model_qualified_name}/{name}", + connection_qualified_name=connection_qualified_name or connection_qn, + connector_name=connector_name, + anaplan_workspace_qualified_name=workspace_qualified_name, + anaplan_workspace_name=workspace_name, + anaplan_model_qualified_name=model_qualified_name, + anaplan_model_name=model_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "AnaplanDimension": + """Create an AnaplanDimension instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "AnaplanDimension": + """Return only fields required for update operations.""" + return AnaplanDimension.updater( + qualified_name=self.qualified_name, name=self.name + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _anaplan_dimension_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> AnaplanDimension: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + AnaplanDimension instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _anaplan_dimension_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class AnaplanDimensionAttributes(AssetAttributes): + """AnaplanDimension-specific attributes for nested API format.""" + + anaplan_workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanWorkspace asset that contains this asset (AnaplanModel and everything under its hierarchy).""" + + anaplan_workspace_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanWorkspace asset that contains this asset (AnaplanModel and everything under its hierarchy).""" + + anaplan_model_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanModel asset that contains this asset (AnaplanModule and everything under its hierarchy).""" + + anaplan_model_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanModel asset that contains this asset (AnaplanModule and everything under its hierarchy).""" + + anaplan_module_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanModule asset that contains this asset (AnaplanLineItem, AnaplanList, AnaplanView and everything under their hierarchy).""" + + anaplan_module_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanModule asset that contains this asset (AnaplanLineItem, AnaplanList, AnaplanView and everything under their hierarchy).""" + + anaplan_source_id: Union[str, None, UnsetType] = UNSET + """Id/Guid of the Anaplan asset in the source system.""" + + +class AnaplanDimensionRelationshipAttributes(AssetRelationshipAttributes): + """AnaplanDimension-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anaplan_model: Union[RelatedAnaplanModel, None, UnsetType] = UNSET + """Model containing the dimension.""" + + anaplan_line_items: Union[List[RelatedAnaplanLineItem], None, UnsetType] = UNSET + """Line items related to the dimension.""" + + anaplan_row_views: Union[List[RelatedAnaplanView], None, UnsetType] = UNSET + """Views related to the row dimension.""" + + anaplan_column_views: Union[List[RelatedAnaplanView], None, UnsetType] = UNSET + """Views related to the column dimension.""" + + anaplan_page_views: Union[List[RelatedAnaplanView], None, UnsetType] = UNSET + """Views related to the page dimension.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class AnaplanDimensionNested(AssetNested): + """AnaplanDimension in nested API format for high-performance serialization.""" + + attributes: Union[AnaplanDimensionAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + AnaplanDimensionRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + AnaplanDimensionRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + AnaplanDimensionRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_ANAPLAN_DIMENSION_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anaplan_model", + "anaplan_line_items", + "anaplan_row_views", + "anaplan_column_views", + "anaplan_page_views", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_anaplan_dimension_attrs( + attrs: AnaplanDimensionAttributes, obj: AnaplanDimension +) -> None: + """Populate AnaplanDimension-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.anaplan_workspace_qualified_name = obj.anaplan_workspace_qualified_name + attrs.anaplan_workspace_name = obj.anaplan_workspace_name + attrs.anaplan_model_qualified_name = obj.anaplan_model_qualified_name + attrs.anaplan_model_name = obj.anaplan_model_name + attrs.anaplan_module_qualified_name = obj.anaplan_module_qualified_name + attrs.anaplan_module_name = obj.anaplan_module_name + attrs.anaplan_source_id = obj.anaplan_source_id + + +def _extract_anaplan_dimension_attrs(attrs: AnaplanDimensionAttributes) -> dict: + """Extract all AnaplanDimension attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["anaplan_workspace_qualified_name"] = attrs.anaplan_workspace_qualified_name + result["anaplan_workspace_name"] = attrs.anaplan_workspace_name + result["anaplan_model_qualified_name"] = attrs.anaplan_model_qualified_name + result["anaplan_model_name"] = attrs.anaplan_model_name + result["anaplan_module_qualified_name"] = attrs.anaplan_module_qualified_name + result["anaplan_module_name"] = attrs.anaplan_module_name + result["anaplan_source_id"] = attrs.anaplan_source_id + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _anaplan_dimension_to_nested( + anaplan_dimension: AnaplanDimension, +) -> AnaplanDimensionNested: + """Convert flat AnaplanDimension to nested format.""" + attrs = AnaplanDimensionAttributes() + _populate_anaplan_dimension_attrs(attrs, anaplan_dimension) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + anaplan_dimension, + _ANAPLAN_DIMENSION_REL_FIELDS, + AnaplanDimensionRelationshipAttributes, + ) + return AnaplanDimensionNested( + guid=anaplan_dimension.guid, + type_name=anaplan_dimension.type_name, + status=anaplan_dimension.status, + version=anaplan_dimension.version, + create_time=anaplan_dimension.create_time, + update_time=anaplan_dimension.update_time, + created_by=anaplan_dimension.created_by, + updated_by=anaplan_dimension.updated_by, + classifications=anaplan_dimension.classifications, + classification_names=anaplan_dimension.classification_names, + meanings=anaplan_dimension.meanings, + labels=anaplan_dimension.labels, + business_attributes=anaplan_dimension.business_attributes, + custom_attributes=anaplan_dimension.custom_attributes, + pending_tasks=anaplan_dimension.pending_tasks, + proxy=anaplan_dimension.proxy, + is_incomplete=anaplan_dimension.is_incomplete, + provenance_type=anaplan_dimension.provenance_type, + home_id=anaplan_dimension.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _anaplan_dimension_from_nested(nested: AnaplanDimensionNested) -> AnaplanDimension: + """Convert nested format to flat AnaplanDimension.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else AnaplanDimensionAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _ANAPLAN_DIMENSION_REL_FIELDS, + AnaplanDimensionRelationshipAttributes, + ) + return AnaplanDimension( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_anaplan_dimension_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _anaplan_dimension_to_nested_bytes( + anaplan_dimension: AnaplanDimension, serde: Serde +) -> bytes: + """Convert flat AnaplanDimension to nested JSON bytes.""" + return serde.encode(_anaplan_dimension_to_nested(anaplan_dimension)) + + +def _anaplan_dimension_from_nested_bytes(data: bytes, serde: Serde) -> AnaplanDimension: + """Convert nested JSON bytes to flat AnaplanDimension.""" + nested = serde.decode(data, AnaplanDimensionNested) + return _anaplan_dimension_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +AnaplanDimension.ANAPLAN_WORKSPACE_QUALIFIED_NAME = KeywordField( + "anaplanWorkspaceQualifiedName", "anaplanWorkspaceQualifiedName" +) +AnaplanDimension.ANAPLAN_WORKSPACE_NAME = KeywordField( + "anaplanWorkspaceName", "anaplanWorkspaceName" +) +AnaplanDimension.ANAPLAN_MODEL_QUALIFIED_NAME = KeywordField( + "anaplanModelQualifiedName", "anaplanModelQualifiedName" +) +AnaplanDimension.ANAPLAN_MODEL_NAME = KeywordField( + "anaplanModelName", "anaplanModelName" +) +AnaplanDimension.ANAPLAN_MODULE_QUALIFIED_NAME = KeywordField( + "anaplanModuleQualifiedName", "anaplanModuleQualifiedName" +) +AnaplanDimension.ANAPLAN_MODULE_NAME = KeywordField( + "anaplanModuleName", "anaplanModuleName" +) +AnaplanDimension.ANAPLAN_SOURCE_ID = KeywordField("anaplanSourceId", "anaplanSourceId") +AnaplanDimension.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +AnaplanDimension.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +AnaplanDimension.ANAPLAN_MODEL = RelationField("anaplanModel") +AnaplanDimension.ANAPLAN_LINE_ITEMS = RelationField("anaplanLineItems") +AnaplanDimension.ANAPLAN_ROW_VIEWS = RelationField("anaplanRowViews") +AnaplanDimension.ANAPLAN_COLUMN_VIEWS = RelationField("anaplanColumnViews") +AnaplanDimension.ANAPLAN_PAGE_VIEWS = RelationField("anaplanPageViews") +AnaplanDimension.ANOMALO_CHECKS = RelationField("anomaloChecks") +AnaplanDimension.APPLICATION = RelationField("application") +AnaplanDimension.APPLICATION_FIELD = RelationField("applicationField") +AnaplanDimension.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +AnaplanDimension.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +AnaplanDimension.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +AnaplanDimension.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +AnaplanDimension.METRICS = RelationField("metrics") +AnaplanDimension.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +AnaplanDimension.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +AnaplanDimension.MEANINGS = RelationField("meanings") +AnaplanDimension.MC_MONITORS = RelationField("mcMonitors") +AnaplanDimension.MC_INCIDENTS = RelationField("mcIncidents") +AnaplanDimension.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +AnaplanDimension.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +AnaplanDimension.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +AnaplanDimension.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +AnaplanDimension.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +AnaplanDimension.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +AnaplanDimension.FILES = RelationField("files") +AnaplanDimension.LINKS = RelationField("links") +AnaplanDimension.README = RelationField("readme") +AnaplanDimension.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +AnaplanDimension.SODA_CHECKS = RelationField("sodaChecks") +AnaplanDimension.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +AnaplanDimension.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/anaplan_line_item.py b/pyatlan_v9/model/assets/anaplan_line_item.py new file mode 100644 index 000000000..f354e858a --- /dev/null +++ b/pyatlan_v9/model/assets/anaplan_line_item.py @@ -0,0 +1,734 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +AnaplanLineItem asset model with flattened inheritance. + +This module provides: +- AnaplanLineItem: Flat asset class (easy to use) +- AnaplanLineItemAttributes: Nested attributes struct (extends AssetAttributes) +- AnaplanLineItemNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan.model.enums import AtlanConnectorType +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .anaplan_related import ( + RelatedAnaplanDimension, + RelatedAnaplanList, + RelatedAnaplanModule, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class AnaplanLineItem(Asset): + """ + Instances of an AnaplanLineItem in Atlan. + """ + + ANAPLAN_LINE_ITEM_FORMULA: ClassVar[Any] = None + ANAPLAN_WORKSPACE_QUALIFIED_NAME: ClassVar[Any] = None + ANAPLAN_WORKSPACE_NAME: ClassVar[Any] = None + ANAPLAN_MODEL_QUALIFIED_NAME: ClassVar[Any] = None + ANAPLAN_MODEL_NAME: ClassVar[Any] = None + ANAPLAN_MODULE_QUALIFIED_NAME: ClassVar[Any] = None + ANAPLAN_MODULE_NAME: ClassVar[Any] = None + ANAPLAN_SOURCE_ID: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANAPLAN_LISTS: ClassVar[Any] = None + ANAPLAN_MODULE: ClassVar[Any] = None + ANAPLAN_DIMENSIONS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "AnaplanLineItem" + + anaplan_line_item_formula: Union[str, None, UnsetType] = UNSET + """Formula of the AnaplanLineItem from the source system.""" + + anaplan_workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanWorkspace asset that contains this asset (AnaplanModel and everything under its hierarchy).""" + + anaplan_workspace_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanWorkspace asset that contains this asset (AnaplanModel and everything under its hierarchy).""" + + anaplan_model_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanModel asset that contains this asset (AnaplanModule and everything under its hierarchy).""" + + anaplan_model_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanModel asset that contains this asset (AnaplanModule and everything under its hierarchy).""" + + anaplan_module_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanModule asset that contains this asset (AnaplanLineItem, AnaplanList, AnaplanView and everything under their hierarchy).""" + + anaplan_module_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanModule asset that contains this asset (AnaplanLineItem, AnaplanList, AnaplanView and everything under their hierarchy).""" + + anaplan_source_id: Union[str, None, UnsetType] = UNSET + """Id/Guid of the Anaplan asset in the source system.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anaplan_lists: Union[List[RelatedAnaplanList], None, UnsetType] = UNSET + """Lists related to the line item.""" + + anaplan_module: Union[RelatedAnaplanModule, None, UnsetType] = UNSET + """Module containing the line item.""" + + anaplan_dimensions: Union[List[RelatedAnaplanDimension], None, UnsetType] = UNSET + """Dimensions related to the line item.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "AnaplanLineItem" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+/[^/]+$" + ) + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + module_qualified_name: str, + connection_qualified_name: str | None = None, + ) -> "AnaplanLineItem": + """Create a new AnaplanLineItem asset.""" + validate_required_fields( + ["name", "module_qualified_name"], [name, module_qualified_name] + ) + fields = module_qualified_name.split("/") + connection_qn: Union[str, None, UnsetType] = UNSET + if connection_qualified_name is not None: + connector_name = str( + AtlanConnectorType.get_connector_name(connection_qualified_name) + ) + else: + connection_qn, connector_name = AtlanConnectorType.get_connector_name( + module_qualified_name, "module_qualified_name", 6 + ) + workspace_qualified_name = "/".join(fields[:4]) if len(fields) >= 4 else UNSET + workspace_name = fields[3] if len(fields) > 3 else UNSET + model_qualified_name = "/".join(fields[:5]) if len(fields) >= 5 else UNSET + model_name = fields[4] if len(fields) > 4 else UNSET + module_name = fields[5] if len(fields) > 5 else UNSET + return cls( + name=name, + qualified_name=f"{module_qualified_name}/{name}", + connection_qualified_name=connection_qualified_name or connection_qn, + connector_name=connector_name, + anaplan_workspace_qualified_name=workspace_qualified_name, + anaplan_workspace_name=workspace_name, + anaplan_model_qualified_name=model_qualified_name, + anaplan_model_name=model_name, + anaplan_module_qualified_name=module_qualified_name, + anaplan_module_name=module_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "AnaplanLineItem": + """Create an AnaplanLineItem instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "AnaplanLineItem": + """Return only fields required for update operations.""" + return AnaplanLineItem.updater( + qualified_name=self.qualified_name, name=self.name + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _anaplan_line_item_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> AnaplanLineItem: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + AnaplanLineItem instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _anaplan_line_item_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class AnaplanLineItemAttributes(AssetAttributes): + """AnaplanLineItem-specific attributes for nested API format.""" + + anaplan_line_item_formula: Union[str, None, UnsetType] = UNSET + """Formula of the AnaplanLineItem from the source system.""" + + anaplan_workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanWorkspace asset that contains this asset (AnaplanModel and everything under its hierarchy).""" + + anaplan_workspace_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanWorkspace asset that contains this asset (AnaplanModel and everything under its hierarchy).""" + + anaplan_model_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanModel asset that contains this asset (AnaplanModule and everything under its hierarchy).""" + + anaplan_model_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanModel asset that contains this asset (AnaplanModule and everything under its hierarchy).""" + + anaplan_module_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanModule asset that contains this asset (AnaplanLineItem, AnaplanList, AnaplanView and everything under their hierarchy).""" + + anaplan_module_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanModule asset that contains this asset (AnaplanLineItem, AnaplanList, AnaplanView and everything under their hierarchy).""" + + anaplan_source_id: Union[str, None, UnsetType] = UNSET + """Id/Guid of the Anaplan asset in the source system.""" + + +class AnaplanLineItemRelationshipAttributes(AssetRelationshipAttributes): + """AnaplanLineItem-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anaplan_lists: Union[List[RelatedAnaplanList], None, UnsetType] = UNSET + """Lists related to the line item.""" + + anaplan_module: Union[RelatedAnaplanModule, None, UnsetType] = UNSET + """Module containing the line item.""" + + anaplan_dimensions: Union[List[RelatedAnaplanDimension], None, UnsetType] = UNSET + """Dimensions related to the line item.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class AnaplanLineItemNested(AssetNested): + """AnaplanLineItem in nested API format for high-performance serialization.""" + + attributes: Union[AnaplanLineItemAttributes, UnsetType] = UNSET + relationship_attributes: Union[AnaplanLineItemRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + AnaplanLineItemRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + AnaplanLineItemRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_ANAPLAN_LINE_ITEM_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anaplan_lists", + "anaplan_module", + "anaplan_dimensions", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_anaplan_line_item_attrs( + attrs: AnaplanLineItemAttributes, obj: AnaplanLineItem +) -> None: + """Populate AnaplanLineItem-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.anaplan_line_item_formula = obj.anaplan_line_item_formula + attrs.anaplan_workspace_qualified_name = obj.anaplan_workspace_qualified_name + attrs.anaplan_workspace_name = obj.anaplan_workspace_name + attrs.anaplan_model_qualified_name = obj.anaplan_model_qualified_name + attrs.anaplan_model_name = obj.anaplan_model_name + attrs.anaplan_module_qualified_name = obj.anaplan_module_qualified_name + attrs.anaplan_module_name = obj.anaplan_module_name + attrs.anaplan_source_id = obj.anaplan_source_id + + +def _extract_anaplan_line_item_attrs(attrs: AnaplanLineItemAttributes) -> dict: + """Extract all AnaplanLineItem attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["anaplan_line_item_formula"] = attrs.anaplan_line_item_formula + result["anaplan_workspace_qualified_name"] = attrs.anaplan_workspace_qualified_name + result["anaplan_workspace_name"] = attrs.anaplan_workspace_name + result["anaplan_model_qualified_name"] = attrs.anaplan_model_qualified_name + result["anaplan_model_name"] = attrs.anaplan_model_name + result["anaplan_module_qualified_name"] = attrs.anaplan_module_qualified_name + result["anaplan_module_name"] = attrs.anaplan_module_name + result["anaplan_source_id"] = attrs.anaplan_source_id + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _anaplan_line_item_to_nested( + anaplan_line_item: AnaplanLineItem, +) -> AnaplanLineItemNested: + """Convert flat AnaplanLineItem to nested format.""" + attrs = AnaplanLineItemAttributes() + _populate_anaplan_line_item_attrs(attrs, anaplan_line_item) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + anaplan_line_item, + _ANAPLAN_LINE_ITEM_REL_FIELDS, + AnaplanLineItemRelationshipAttributes, + ) + return AnaplanLineItemNested( + guid=anaplan_line_item.guid, + type_name=anaplan_line_item.type_name, + status=anaplan_line_item.status, + version=anaplan_line_item.version, + create_time=anaplan_line_item.create_time, + update_time=anaplan_line_item.update_time, + created_by=anaplan_line_item.created_by, + updated_by=anaplan_line_item.updated_by, + classifications=anaplan_line_item.classifications, + classification_names=anaplan_line_item.classification_names, + meanings=anaplan_line_item.meanings, + labels=anaplan_line_item.labels, + business_attributes=anaplan_line_item.business_attributes, + custom_attributes=anaplan_line_item.custom_attributes, + pending_tasks=anaplan_line_item.pending_tasks, + proxy=anaplan_line_item.proxy, + is_incomplete=anaplan_line_item.is_incomplete, + provenance_type=anaplan_line_item.provenance_type, + home_id=anaplan_line_item.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _anaplan_line_item_from_nested(nested: AnaplanLineItemNested) -> AnaplanLineItem: + """Convert nested format to flat AnaplanLineItem.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else AnaplanLineItemAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _ANAPLAN_LINE_ITEM_REL_FIELDS, + AnaplanLineItemRelationshipAttributes, + ) + return AnaplanLineItem( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_anaplan_line_item_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _anaplan_line_item_to_nested_bytes( + anaplan_line_item: AnaplanLineItem, serde: Serde +) -> bytes: + """Convert flat AnaplanLineItem to nested JSON bytes.""" + return serde.encode(_anaplan_line_item_to_nested(anaplan_line_item)) + + +def _anaplan_line_item_from_nested_bytes(data: bytes, serde: Serde) -> AnaplanLineItem: + """Convert nested JSON bytes to flat AnaplanLineItem.""" + nested = serde.decode(data, AnaplanLineItemNested) + return _anaplan_line_item_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +AnaplanLineItem.ANAPLAN_LINE_ITEM_FORMULA = KeywordField( + "anaplanLineItemFormula", "anaplanLineItemFormula" +) +AnaplanLineItem.ANAPLAN_WORKSPACE_QUALIFIED_NAME = KeywordField( + "anaplanWorkspaceQualifiedName", "anaplanWorkspaceQualifiedName" +) +AnaplanLineItem.ANAPLAN_WORKSPACE_NAME = KeywordField( + "anaplanWorkspaceName", "anaplanWorkspaceName" +) +AnaplanLineItem.ANAPLAN_MODEL_QUALIFIED_NAME = KeywordField( + "anaplanModelQualifiedName", "anaplanModelQualifiedName" +) +AnaplanLineItem.ANAPLAN_MODEL_NAME = KeywordField( + "anaplanModelName", "anaplanModelName" +) +AnaplanLineItem.ANAPLAN_MODULE_QUALIFIED_NAME = KeywordField( + "anaplanModuleQualifiedName", "anaplanModuleQualifiedName" +) +AnaplanLineItem.ANAPLAN_MODULE_NAME = KeywordField( + "anaplanModuleName", "anaplanModuleName" +) +AnaplanLineItem.ANAPLAN_SOURCE_ID = KeywordField("anaplanSourceId", "anaplanSourceId") +AnaplanLineItem.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +AnaplanLineItem.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +AnaplanLineItem.ANAPLAN_LISTS = RelationField("anaplanLists") +AnaplanLineItem.ANAPLAN_MODULE = RelationField("anaplanModule") +AnaplanLineItem.ANAPLAN_DIMENSIONS = RelationField("anaplanDimensions") +AnaplanLineItem.ANOMALO_CHECKS = RelationField("anomaloChecks") +AnaplanLineItem.APPLICATION = RelationField("application") +AnaplanLineItem.APPLICATION_FIELD = RelationField("applicationField") +AnaplanLineItem.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +AnaplanLineItem.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +AnaplanLineItem.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +AnaplanLineItem.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +AnaplanLineItem.METRICS = RelationField("metrics") +AnaplanLineItem.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +AnaplanLineItem.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +AnaplanLineItem.MEANINGS = RelationField("meanings") +AnaplanLineItem.MC_MONITORS = RelationField("mcMonitors") +AnaplanLineItem.MC_INCIDENTS = RelationField("mcIncidents") +AnaplanLineItem.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +AnaplanLineItem.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +AnaplanLineItem.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +AnaplanLineItem.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +AnaplanLineItem.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +AnaplanLineItem.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +AnaplanLineItem.FILES = RelationField("files") +AnaplanLineItem.LINKS = RelationField("links") +AnaplanLineItem.README = RelationField("readme") +AnaplanLineItem.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +AnaplanLineItem.SODA_CHECKS = RelationField("sodaChecks") +AnaplanLineItem.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +AnaplanLineItem.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/anaplan_list.py b/pyatlan_v9/model/assets/anaplan_list.py new file mode 100644 index 000000000..abcff54f0 --- /dev/null +++ b/pyatlan_v9/model/assets/anaplan_list.py @@ -0,0 +1,698 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +AnaplanList asset model with flattened inheritance. + +This module provides: +- AnaplanList: Flat asset class (easy to use) +- AnaplanListAttributes: Nested attributes struct (extends AssetAttributes) +- AnaplanListNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan.model.enums import AtlanConnectorType +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .anaplan_related import RelatedAnaplanLineItem, RelatedAnaplanModel + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class AnaplanList(Asset): + """ + Instances of an AnaplanList in Atlan. + """ + + ANAPLAN_LIST_ITEM_COUNT: ClassVar[Any] = None + ANAPLAN_WORKSPACE_QUALIFIED_NAME: ClassVar[Any] = None + ANAPLAN_WORKSPACE_NAME: ClassVar[Any] = None + ANAPLAN_MODEL_QUALIFIED_NAME: ClassVar[Any] = None + ANAPLAN_MODEL_NAME: ClassVar[Any] = None + ANAPLAN_MODULE_QUALIFIED_NAME: ClassVar[Any] = None + ANAPLAN_MODULE_NAME: ClassVar[Any] = None + ANAPLAN_SOURCE_ID: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANAPLAN_MODEL: ClassVar[Any] = None + ANAPLAN_LINE_ITEMS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "AnaplanList" + + anaplan_list_item_count: Union[int, None, UnsetType] = UNSET + """Item Count of the AnaplanList from the source system.""" + + anaplan_workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanWorkspace asset that contains this asset (AnaplanModel and everything under its hierarchy).""" + + anaplan_workspace_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanWorkspace asset that contains this asset (AnaplanModel and everything under its hierarchy).""" + + anaplan_model_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanModel asset that contains this asset (AnaplanModule and everything under its hierarchy).""" + + anaplan_model_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanModel asset that contains this asset (AnaplanModule and everything under its hierarchy).""" + + anaplan_module_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanModule asset that contains this asset (AnaplanLineItem, AnaplanList, AnaplanView and everything under their hierarchy).""" + + anaplan_module_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanModule asset that contains this asset (AnaplanLineItem, AnaplanList, AnaplanView and everything under their hierarchy).""" + + anaplan_source_id: Union[str, None, UnsetType] = UNSET + """Id/Guid of the Anaplan asset in the source system.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anaplan_model: Union[RelatedAnaplanModel, None, UnsetType] = UNSET + """Model containing the list.""" + + anaplan_line_items: Union[List[RelatedAnaplanLineItem], None, UnsetType] = UNSET + """Line items related to the list.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "AnaplanList" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + model_qualified_name: str, + connection_qualified_name: str | None = None, + ) -> "AnaplanList": + """Create a new AnaplanList asset.""" + validate_required_fields( + ["name", "model_qualified_name"], [name, model_qualified_name] + ) + fields = model_qualified_name.split("/") + connection_qn: Union[str, None, UnsetType] = UNSET + if connection_qualified_name is not None: + connector_name = str( + AtlanConnectorType.get_connector_name(connection_qualified_name) + ) + else: + connection_qn, connector_name = AtlanConnectorType.get_connector_name( + model_qualified_name, "model_qualified_name", 5 + ) + workspace_qualified_name = "/".join(fields[:4]) if len(fields) >= 4 else UNSET + workspace_name = fields[3] if len(fields) > 3 else UNSET + model_name = fields[4] if len(fields) > 4 else UNSET + return cls( + name=name, + qualified_name=f"{model_qualified_name}/{name}", + connection_qualified_name=connection_qualified_name or connection_qn, + connector_name=connector_name, + anaplan_workspace_qualified_name=workspace_qualified_name, + anaplan_workspace_name=workspace_name, + anaplan_model_qualified_name=model_qualified_name, + anaplan_model_name=model_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "AnaplanList": + """Create an AnaplanList instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "AnaplanList": + """Return only fields required for update operations.""" + return AnaplanList.updater(qualified_name=self.qualified_name, name=self.name) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _anaplan_list_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> AnaplanList: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + AnaplanList instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _anaplan_list_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class AnaplanListAttributes(AssetAttributes): + """AnaplanList-specific attributes for nested API format.""" + + anaplan_list_item_count: Union[int, None, UnsetType] = UNSET + """Item Count of the AnaplanList from the source system.""" + + anaplan_workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanWorkspace asset that contains this asset (AnaplanModel and everything under its hierarchy).""" + + anaplan_workspace_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanWorkspace asset that contains this asset (AnaplanModel and everything under its hierarchy).""" + + anaplan_model_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanModel asset that contains this asset (AnaplanModule and everything under its hierarchy).""" + + anaplan_model_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanModel asset that contains this asset (AnaplanModule and everything under its hierarchy).""" + + anaplan_module_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanModule asset that contains this asset (AnaplanLineItem, AnaplanList, AnaplanView and everything under their hierarchy).""" + + anaplan_module_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanModule asset that contains this asset (AnaplanLineItem, AnaplanList, AnaplanView and everything under their hierarchy).""" + + anaplan_source_id: Union[str, None, UnsetType] = UNSET + """Id/Guid of the Anaplan asset in the source system.""" + + +class AnaplanListRelationshipAttributes(AssetRelationshipAttributes): + """AnaplanList-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anaplan_model: Union[RelatedAnaplanModel, None, UnsetType] = UNSET + """Model containing the list.""" + + anaplan_line_items: Union[List[RelatedAnaplanLineItem], None, UnsetType] = UNSET + """Line items related to the list.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class AnaplanListNested(AssetNested): + """AnaplanList in nested API format for high-performance serialization.""" + + attributes: Union[AnaplanListAttributes, UnsetType] = UNSET + relationship_attributes: Union[AnaplanListRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + AnaplanListRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + AnaplanListRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_ANAPLAN_LIST_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anaplan_model", + "anaplan_line_items", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_anaplan_list_attrs( + attrs: AnaplanListAttributes, obj: AnaplanList +) -> None: + """Populate AnaplanList-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.anaplan_list_item_count = obj.anaplan_list_item_count + attrs.anaplan_workspace_qualified_name = obj.anaplan_workspace_qualified_name + attrs.anaplan_workspace_name = obj.anaplan_workspace_name + attrs.anaplan_model_qualified_name = obj.anaplan_model_qualified_name + attrs.anaplan_model_name = obj.anaplan_model_name + attrs.anaplan_module_qualified_name = obj.anaplan_module_qualified_name + attrs.anaplan_module_name = obj.anaplan_module_name + attrs.anaplan_source_id = obj.anaplan_source_id + + +def _extract_anaplan_list_attrs(attrs: AnaplanListAttributes) -> dict: + """Extract all AnaplanList attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["anaplan_list_item_count"] = attrs.anaplan_list_item_count + result["anaplan_workspace_qualified_name"] = attrs.anaplan_workspace_qualified_name + result["anaplan_workspace_name"] = attrs.anaplan_workspace_name + result["anaplan_model_qualified_name"] = attrs.anaplan_model_qualified_name + result["anaplan_model_name"] = attrs.anaplan_model_name + result["anaplan_module_qualified_name"] = attrs.anaplan_module_qualified_name + result["anaplan_module_name"] = attrs.anaplan_module_name + result["anaplan_source_id"] = attrs.anaplan_source_id + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _anaplan_list_to_nested(anaplan_list: AnaplanList) -> AnaplanListNested: + """Convert flat AnaplanList to nested format.""" + attrs = AnaplanListAttributes() + _populate_anaplan_list_attrs(attrs, anaplan_list) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + anaplan_list, _ANAPLAN_LIST_REL_FIELDS, AnaplanListRelationshipAttributes + ) + return AnaplanListNested( + guid=anaplan_list.guid, + type_name=anaplan_list.type_name, + status=anaplan_list.status, + version=anaplan_list.version, + create_time=anaplan_list.create_time, + update_time=anaplan_list.update_time, + created_by=anaplan_list.created_by, + updated_by=anaplan_list.updated_by, + classifications=anaplan_list.classifications, + classification_names=anaplan_list.classification_names, + meanings=anaplan_list.meanings, + labels=anaplan_list.labels, + business_attributes=anaplan_list.business_attributes, + custom_attributes=anaplan_list.custom_attributes, + pending_tasks=anaplan_list.pending_tasks, + proxy=anaplan_list.proxy, + is_incomplete=anaplan_list.is_incomplete, + provenance_type=anaplan_list.provenance_type, + home_id=anaplan_list.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _anaplan_list_from_nested(nested: AnaplanListNested) -> AnaplanList: + """Convert nested format to flat AnaplanList.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else AnaplanListAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _ANAPLAN_LIST_REL_FIELDS, + AnaplanListRelationshipAttributes, + ) + return AnaplanList( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_anaplan_list_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _anaplan_list_to_nested_bytes(anaplan_list: AnaplanList, serde: Serde) -> bytes: + """Convert flat AnaplanList to nested JSON bytes.""" + return serde.encode(_anaplan_list_to_nested(anaplan_list)) + + +def _anaplan_list_from_nested_bytes(data: bytes, serde: Serde) -> AnaplanList: + """Convert nested JSON bytes to flat AnaplanList.""" + nested = serde.decode(data, AnaplanListNested) + return _anaplan_list_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +AnaplanList.ANAPLAN_LIST_ITEM_COUNT = NumericField( + "anaplanListItemCount", "anaplanListItemCount" +) +AnaplanList.ANAPLAN_WORKSPACE_QUALIFIED_NAME = KeywordField( + "anaplanWorkspaceQualifiedName", "anaplanWorkspaceQualifiedName" +) +AnaplanList.ANAPLAN_WORKSPACE_NAME = KeywordField( + "anaplanWorkspaceName", "anaplanWorkspaceName" +) +AnaplanList.ANAPLAN_MODEL_QUALIFIED_NAME = KeywordField( + "anaplanModelQualifiedName", "anaplanModelQualifiedName" +) +AnaplanList.ANAPLAN_MODEL_NAME = KeywordField("anaplanModelName", "anaplanModelName") +AnaplanList.ANAPLAN_MODULE_QUALIFIED_NAME = KeywordField( + "anaplanModuleQualifiedName", "anaplanModuleQualifiedName" +) +AnaplanList.ANAPLAN_MODULE_NAME = KeywordField("anaplanModuleName", "anaplanModuleName") +AnaplanList.ANAPLAN_SOURCE_ID = KeywordField("anaplanSourceId", "anaplanSourceId") +AnaplanList.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +AnaplanList.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +AnaplanList.ANAPLAN_MODEL = RelationField("anaplanModel") +AnaplanList.ANAPLAN_LINE_ITEMS = RelationField("anaplanLineItems") +AnaplanList.ANOMALO_CHECKS = RelationField("anomaloChecks") +AnaplanList.APPLICATION = RelationField("application") +AnaplanList.APPLICATION_FIELD = RelationField("applicationField") +AnaplanList.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +AnaplanList.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +AnaplanList.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +AnaplanList.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +AnaplanList.METRICS = RelationField("metrics") +AnaplanList.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +AnaplanList.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +AnaplanList.MEANINGS = RelationField("meanings") +AnaplanList.MC_MONITORS = RelationField("mcMonitors") +AnaplanList.MC_INCIDENTS = RelationField("mcIncidents") +AnaplanList.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +AnaplanList.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +AnaplanList.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +AnaplanList.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +AnaplanList.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +AnaplanList.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +AnaplanList.FILES = RelationField("files") +AnaplanList.LINKS = RelationField("links") +AnaplanList.README = RelationField("readme") +AnaplanList.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +AnaplanList.SODA_CHECKS = RelationField("sodaChecks") +AnaplanList.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +AnaplanList.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/anaplan_model.py b/pyatlan_v9/model/assets/anaplan_model.py new file mode 100644 index 000000000..1f4607ef0 --- /dev/null +++ b/pyatlan_v9/model/assets/anaplan_model.py @@ -0,0 +1,715 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +AnaplanModel asset model with flattened inheritance. + +This module provides: +- AnaplanModel: Flat asset class (easy to use) +- AnaplanModelAttributes: Nested attributes struct (extends AssetAttributes) +- AnaplanModelNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan.model.enums import AtlanConnectorType +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .anaplan_related import ( + RelatedAnaplanDimension, + RelatedAnaplanList, + RelatedAnaplanModule, + RelatedAnaplanPage, + RelatedAnaplanWorkspace, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class AnaplanModel(Asset): + """ + Instances of an AnaplanModel in Atlan. + """ + + ANAPLAN_WORKSPACE_QUALIFIED_NAME: ClassVar[Any] = None + ANAPLAN_WORKSPACE_NAME: ClassVar[Any] = None + ANAPLAN_MODEL_QUALIFIED_NAME: ClassVar[Any] = None + ANAPLAN_MODEL_NAME: ClassVar[Any] = None + ANAPLAN_MODULE_QUALIFIED_NAME: ClassVar[Any] = None + ANAPLAN_MODULE_NAME: ClassVar[Any] = None + ANAPLAN_SOURCE_ID: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANAPLAN_WORKSPACE: ClassVar[Any] = None + ANAPLAN_PAGES: ClassVar[Any] = None + ANAPLAN_MODULES: ClassVar[Any] = None + ANAPLAN_DIMENSIONS: ClassVar[Any] = None + ANAPLAN_LISTS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "AnaplanModel" + + anaplan_workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanWorkspace asset that contains this asset (AnaplanModel and everything under its hierarchy).""" + + anaplan_workspace_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanWorkspace asset that contains this asset (AnaplanModel and everything under its hierarchy).""" + + anaplan_model_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanModel asset that contains this asset (AnaplanModule and everything under its hierarchy).""" + + anaplan_model_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanModel asset that contains this asset (AnaplanModule and everything under its hierarchy).""" + + anaplan_module_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanModule asset that contains this asset (AnaplanLineItem, AnaplanList, AnaplanView and everything under their hierarchy).""" + + anaplan_module_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanModule asset that contains this asset (AnaplanLineItem, AnaplanList, AnaplanView and everything under their hierarchy).""" + + anaplan_source_id: Union[str, None, UnsetType] = UNSET + """Id/Guid of the Anaplan asset in the source system.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anaplan_workspace: Union[RelatedAnaplanWorkspace, None, UnsetType] = UNSET + """Workspace containing the model.""" + + anaplan_pages: Union[List[RelatedAnaplanPage], None, UnsetType] = UNSET + """Pages related to the model.""" + + anaplan_modules: Union[List[RelatedAnaplanModule], None, UnsetType] = UNSET + """Individual modules contained in the model.""" + + anaplan_dimensions: Union[List[RelatedAnaplanDimension], None, UnsetType] = UNSET + """Individual dimensions contained in the model.""" + + anaplan_lists: Union[List[RelatedAnaplanList], None, UnsetType] = UNSET + """Individual lists contained in the model.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "AnaplanModel" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + workspace_qualified_name: str, + connection_qualified_name: str | None = None, + ) -> "AnaplanModel": + """Create a new AnaplanModel asset.""" + validate_required_fields( + ["name", "workspace_qualified_name"], [name, workspace_qualified_name] + ) + connection_qn: Union[str, None, UnsetType] = UNSET + if connection_qualified_name is not None: + connector_name = str( + AtlanConnectorType.get_connector_name(connection_qualified_name) + ) + else: + connection_qn, connector_name = AtlanConnectorType.get_connector_name( + workspace_qualified_name, "workspace_qualified_name", 4 + ) + return cls( + name=name, + qualified_name=f"{workspace_qualified_name}/{name}", + connection_qualified_name=connection_qualified_name or connection_qn, + connector_name=connector_name, + anaplan_workspace_qualified_name=workspace_qualified_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "AnaplanModel": + """Create an AnaplanModel instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "AnaplanModel": + """Return only fields required for update operations.""" + return AnaplanModel.updater(qualified_name=self.qualified_name, name=self.name) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _anaplan_model_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> AnaplanModel: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + AnaplanModel instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _anaplan_model_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class AnaplanModelAttributes(AssetAttributes): + """AnaplanModel-specific attributes for nested API format.""" + + anaplan_workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanWorkspace asset that contains this asset (AnaplanModel and everything under its hierarchy).""" + + anaplan_workspace_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanWorkspace asset that contains this asset (AnaplanModel and everything under its hierarchy).""" + + anaplan_model_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanModel asset that contains this asset (AnaplanModule and everything under its hierarchy).""" + + anaplan_model_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanModel asset that contains this asset (AnaplanModule and everything under its hierarchy).""" + + anaplan_module_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanModule asset that contains this asset (AnaplanLineItem, AnaplanList, AnaplanView and everything under their hierarchy).""" + + anaplan_module_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanModule asset that contains this asset (AnaplanLineItem, AnaplanList, AnaplanView and everything under their hierarchy).""" + + anaplan_source_id: Union[str, None, UnsetType] = UNSET + """Id/Guid of the Anaplan asset in the source system.""" + + +class AnaplanModelRelationshipAttributes(AssetRelationshipAttributes): + """AnaplanModel-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anaplan_workspace: Union[RelatedAnaplanWorkspace, None, UnsetType] = UNSET + """Workspace containing the model.""" + + anaplan_pages: Union[List[RelatedAnaplanPage], None, UnsetType] = UNSET + """Pages related to the model.""" + + anaplan_modules: Union[List[RelatedAnaplanModule], None, UnsetType] = UNSET + """Individual modules contained in the model.""" + + anaplan_dimensions: Union[List[RelatedAnaplanDimension], None, UnsetType] = UNSET + """Individual dimensions contained in the model.""" + + anaplan_lists: Union[List[RelatedAnaplanList], None, UnsetType] = UNSET + """Individual lists contained in the model.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class AnaplanModelNested(AssetNested): + """AnaplanModel in nested API format for high-performance serialization.""" + + attributes: Union[AnaplanModelAttributes, UnsetType] = UNSET + relationship_attributes: Union[AnaplanModelRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + AnaplanModelRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + AnaplanModelRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_ANAPLAN_MODEL_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anaplan_workspace", + "anaplan_pages", + "anaplan_modules", + "anaplan_dimensions", + "anaplan_lists", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_anaplan_model_attrs( + attrs: AnaplanModelAttributes, obj: AnaplanModel +) -> None: + """Populate AnaplanModel-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.anaplan_workspace_qualified_name = obj.anaplan_workspace_qualified_name + attrs.anaplan_workspace_name = obj.anaplan_workspace_name + attrs.anaplan_model_qualified_name = obj.anaplan_model_qualified_name + attrs.anaplan_model_name = obj.anaplan_model_name + attrs.anaplan_module_qualified_name = obj.anaplan_module_qualified_name + attrs.anaplan_module_name = obj.anaplan_module_name + attrs.anaplan_source_id = obj.anaplan_source_id + + +def _extract_anaplan_model_attrs(attrs: AnaplanModelAttributes) -> dict: + """Extract all AnaplanModel attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["anaplan_workspace_qualified_name"] = attrs.anaplan_workspace_qualified_name + result["anaplan_workspace_name"] = attrs.anaplan_workspace_name + result["anaplan_model_qualified_name"] = attrs.anaplan_model_qualified_name + result["anaplan_model_name"] = attrs.anaplan_model_name + result["anaplan_module_qualified_name"] = attrs.anaplan_module_qualified_name + result["anaplan_module_name"] = attrs.anaplan_module_name + result["anaplan_source_id"] = attrs.anaplan_source_id + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _anaplan_model_to_nested(anaplan_model: AnaplanModel) -> AnaplanModelNested: + """Convert flat AnaplanModel to nested format.""" + attrs = AnaplanModelAttributes() + _populate_anaplan_model_attrs(attrs, anaplan_model) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + anaplan_model, _ANAPLAN_MODEL_REL_FIELDS, AnaplanModelRelationshipAttributes + ) + return AnaplanModelNested( + guid=anaplan_model.guid, + type_name=anaplan_model.type_name, + status=anaplan_model.status, + version=anaplan_model.version, + create_time=anaplan_model.create_time, + update_time=anaplan_model.update_time, + created_by=anaplan_model.created_by, + updated_by=anaplan_model.updated_by, + classifications=anaplan_model.classifications, + classification_names=anaplan_model.classification_names, + meanings=anaplan_model.meanings, + labels=anaplan_model.labels, + business_attributes=anaplan_model.business_attributes, + custom_attributes=anaplan_model.custom_attributes, + pending_tasks=anaplan_model.pending_tasks, + proxy=anaplan_model.proxy, + is_incomplete=anaplan_model.is_incomplete, + provenance_type=anaplan_model.provenance_type, + home_id=anaplan_model.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _anaplan_model_from_nested(nested: AnaplanModelNested) -> AnaplanModel: + """Convert nested format to flat AnaplanModel.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else AnaplanModelAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _ANAPLAN_MODEL_REL_FIELDS, + AnaplanModelRelationshipAttributes, + ) + return AnaplanModel( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_anaplan_model_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _anaplan_model_to_nested_bytes(anaplan_model: AnaplanModel, serde: Serde) -> bytes: + """Convert flat AnaplanModel to nested JSON bytes.""" + return serde.encode(_anaplan_model_to_nested(anaplan_model)) + + +def _anaplan_model_from_nested_bytes(data: bytes, serde: Serde) -> AnaplanModel: + """Convert nested JSON bytes to flat AnaplanModel.""" + nested = serde.decode(data, AnaplanModelNested) + return _anaplan_model_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +AnaplanModel.ANAPLAN_WORKSPACE_QUALIFIED_NAME = KeywordField( + "anaplanWorkspaceQualifiedName", "anaplanWorkspaceQualifiedName" +) +AnaplanModel.ANAPLAN_WORKSPACE_NAME = KeywordField( + "anaplanWorkspaceName", "anaplanWorkspaceName" +) +AnaplanModel.ANAPLAN_MODEL_QUALIFIED_NAME = KeywordField( + "anaplanModelQualifiedName", "anaplanModelQualifiedName" +) +AnaplanModel.ANAPLAN_MODEL_NAME = KeywordField("anaplanModelName", "anaplanModelName") +AnaplanModel.ANAPLAN_MODULE_QUALIFIED_NAME = KeywordField( + "anaplanModuleQualifiedName", "anaplanModuleQualifiedName" +) +AnaplanModel.ANAPLAN_MODULE_NAME = KeywordField( + "anaplanModuleName", "anaplanModuleName" +) +AnaplanModel.ANAPLAN_SOURCE_ID = KeywordField("anaplanSourceId", "anaplanSourceId") +AnaplanModel.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +AnaplanModel.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +AnaplanModel.ANAPLAN_WORKSPACE = RelationField("anaplanWorkspace") +AnaplanModel.ANAPLAN_PAGES = RelationField("anaplanPages") +AnaplanModel.ANAPLAN_MODULES = RelationField("anaplanModules") +AnaplanModel.ANAPLAN_DIMENSIONS = RelationField("anaplanDimensions") +AnaplanModel.ANAPLAN_LISTS = RelationField("anaplanLists") +AnaplanModel.ANOMALO_CHECKS = RelationField("anomaloChecks") +AnaplanModel.APPLICATION = RelationField("application") +AnaplanModel.APPLICATION_FIELD = RelationField("applicationField") +AnaplanModel.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +AnaplanModel.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +AnaplanModel.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +AnaplanModel.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +AnaplanModel.METRICS = RelationField("metrics") +AnaplanModel.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +AnaplanModel.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +AnaplanModel.MEANINGS = RelationField("meanings") +AnaplanModel.MC_MONITORS = RelationField("mcMonitors") +AnaplanModel.MC_INCIDENTS = RelationField("mcIncidents") +AnaplanModel.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +AnaplanModel.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +AnaplanModel.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +AnaplanModel.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +AnaplanModel.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +AnaplanModel.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +AnaplanModel.FILES = RelationField("files") +AnaplanModel.LINKS = RelationField("links") +AnaplanModel.README = RelationField("readme") +AnaplanModel.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +AnaplanModel.SODA_CHECKS = RelationField("sodaChecks") +AnaplanModel.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +AnaplanModel.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/anaplan_module.py b/pyatlan_v9/model/assets/anaplan_module.py new file mode 100644 index 000000000..b29846da4 --- /dev/null +++ b/pyatlan_v9/model/assets/anaplan_module.py @@ -0,0 +1,699 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +AnaplanModule asset model with flattened inheritance. + +This module provides: +- AnaplanModule: Flat asset class (easy to use) +- AnaplanModuleAttributes: Nested attributes struct (extends AssetAttributes) +- AnaplanModuleNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan.model.enums import AtlanConnectorType +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .anaplan_related import ( + RelatedAnaplanLineItem, + RelatedAnaplanModel, + RelatedAnaplanView, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class AnaplanModule(Asset): + """ + Instances of an AnaplanModule in Atlan. + """ + + ANAPLAN_WORKSPACE_QUALIFIED_NAME: ClassVar[Any] = None + ANAPLAN_WORKSPACE_NAME: ClassVar[Any] = None + ANAPLAN_MODEL_QUALIFIED_NAME: ClassVar[Any] = None + ANAPLAN_MODEL_NAME: ClassVar[Any] = None + ANAPLAN_MODULE_QUALIFIED_NAME: ClassVar[Any] = None + ANAPLAN_MODULE_NAME: ClassVar[Any] = None + ANAPLAN_SOURCE_ID: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANAPLAN_MODEL: ClassVar[Any] = None + ANAPLAN_LINE_ITEMS: ClassVar[Any] = None + ANAPLAN_VIEWS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "AnaplanModule" + + anaplan_workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanWorkspace asset that contains this asset (AnaplanModel and everything under its hierarchy).""" + + anaplan_workspace_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanWorkspace asset that contains this asset (AnaplanModel and everything under its hierarchy).""" + + anaplan_model_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanModel asset that contains this asset (AnaplanModule and everything under its hierarchy).""" + + anaplan_model_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanModel asset that contains this asset (AnaplanModule and everything under its hierarchy).""" + + anaplan_module_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanModule asset that contains this asset (AnaplanLineItem, AnaplanList, AnaplanView and everything under their hierarchy).""" + + anaplan_module_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanModule asset that contains this asset (AnaplanLineItem, AnaplanList, AnaplanView and everything under their hierarchy).""" + + anaplan_source_id: Union[str, None, UnsetType] = UNSET + """Id/Guid of the Anaplan asset in the source system.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anaplan_model: Union[RelatedAnaplanModel, None, UnsetType] = UNSET + """Model containing the module.""" + + anaplan_line_items: Union[List[RelatedAnaplanLineItem], None, UnsetType] = UNSET + """Individual line items contained in the module.""" + + anaplan_views: Union[List[RelatedAnaplanView], None, UnsetType] = UNSET + """Individual views contained in the module.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "AnaplanModule" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + model_qualified_name: str, + connection_qualified_name: str | None = None, + ) -> "AnaplanModule": + """Create a new AnaplanModule asset.""" + validate_required_fields( + ["name", "model_qualified_name"], [name, model_qualified_name] + ) + connection_qn: Union[str, None, UnsetType] = UNSET + if connection_qualified_name is not None: + connector_name = str( + AtlanConnectorType.get_connector_name(connection_qualified_name) + ) + else: + connection_qn, connector_name = AtlanConnectorType.get_connector_name( + model_qualified_name, "model_qualified_name", 5 + ) + return cls( + name=name, + qualified_name=f"{model_qualified_name}/{name}", + connection_qualified_name=connection_qualified_name or connection_qn, + connector_name=connector_name, + anaplan_model_qualified_name=model_qualified_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "AnaplanModule": + """Create an AnaplanModule instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "AnaplanModule": + """Return only fields required for update operations.""" + return AnaplanModule.updater(qualified_name=self.qualified_name, name=self.name) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _anaplan_module_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> AnaplanModule: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + AnaplanModule instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _anaplan_module_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class AnaplanModuleAttributes(AssetAttributes): + """AnaplanModule-specific attributes for nested API format.""" + + anaplan_workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanWorkspace asset that contains this asset (AnaplanModel and everything under its hierarchy).""" + + anaplan_workspace_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanWorkspace asset that contains this asset (AnaplanModel and everything under its hierarchy).""" + + anaplan_model_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanModel asset that contains this asset (AnaplanModule and everything under its hierarchy).""" + + anaplan_model_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanModel asset that contains this asset (AnaplanModule and everything under its hierarchy).""" + + anaplan_module_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanModule asset that contains this asset (AnaplanLineItem, AnaplanList, AnaplanView and everything under their hierarchy).""" + + anaplan_module_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanModule asset that contains this asset (AnaplanLineItem, AnaplanList, AnaplanView and everything under their hierarchy).""" + + anaplan_source_id: Union[str, None, UnsetType] = UNSET + """Id/Guid of the Anaplan asset in the source system.""" + + +class AnaplanModuleRelationshipAttributes(AssetRelationshipAttributes): + """AnaplanModule-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anaplan_model: Union[RelatedAnaplanModel, None, UnsetType] = UNSET + """Model containing the module.""" + + anaplan_line_items: Union[List[RelatedAnaplanLineItem], None, UnsetType] = UNSET + """Individual line items contained in the module.""" + + anaplan_views: Union[List[RelatedAnaplanView], None, UnsetType] = UNSET + """Individual views contained in the module.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class AnaplanModuleNested(AssetNested): + """AnaplanModule in nested API format for high-performance serialization.""" + + attributes: Union[AnaplanModuleAttributes, UnsetType] = UNSET + relationship_attributes: Union[AnaplanModuleRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + AnaplanModuleRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + AnaplanModuleRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_ANAPLAN_MODULE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anaplan_model", + "anaplan_line_items", + "anaplan_views", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_anaplan_module_attrs( + attrs: AnaplanModuleAttributes, obj: AnaplanModule +) -> None: + """Populate AnaplanModule-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.anaplan_workspace_qualified_name = obj.anaplan_workspace_qualified_name + attrs.anaplan_workspace_name = obj.anaplan_workspace_name + attrs.anaplan_model_qualified_name = obj.anaplan_model_qualified_name + attrs.anaplan_model_name = obj.anaplan_model_name + attrs.anaplan_module_qualified_name = obj.anaplan_module_qualified_name + attrs.anaplan_module_name = obj.anaplan_module_name + attrs.anaplan_source_id = obj.anaplan_source_id + + +def _extract_anaplan_module_attrs(attrs: AnaplanModuleAttributes) -> dict: + """Extract all AnaplanModule attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["anaplan_workspace_qualified_name"] = attrs.anaplan_workspace_qualified_name + result["anaplan_workspace_name"] = attrs.anaplan_workspace_name + result["anaplan_model_qualified_name"] = attrs.anaplan_model_qualified_name + result["anaplan_model_name"] = attrs.anaplan_model_name + result["anaplan_module_qualified_name"] = attrs.anaplan_module_qualified_name + result["anaplan_module_name"] = attrs.anaplan_module_name + result["anaplan_source_id"] = attrs.anaplan_source_id + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _anaplan_module_to_nested(anaplan_module: AnaplanModule) -> AnaplanModuleNested: + """Convert flat AnaplanModule to nested format.""" + attrs = AnaplanModuleAttributes() + _populate_anaplan_module_attrs(attrs, anaplan_module) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + anaplan_module, _ANAPLAN_MODULE_REL_FIELDS, AnaplanModuleRelationshipAttributes + ) + return AnaplanModuleNested( + guid=anaplan_module.guid, + type_name=anaplan_module.type_name, + status=anaplan_module.status, + version=anaplan_module.version, + create_time=anaplan_module.create_time, + update_time=anaplan_module.update_time, + created_by=anaplan_module.created_by, + updated_by=anaplan_module.updated_by, + classifications=anaplan_module.classifications, + classification_names=anaplan_module.classification_names, + meanings=anaplan_module.meanings, + labels=anaplan_module.labels, + business_attributes=anaplan_module.business_attributes, + custom_attributes=anaplan_module.custom_attributes, + pending_tasks=anaplan_module.pending_tasks, + proxy=anaplan_module.proxy, + is_incomplete=anaplan_module.is_incomplete, + provenance_type=anaplan_module.provenance_type, + home_id=anaplan_module.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _anaplan_module_from_nested(nested: AnaplanModuleNested) -> AnaplanModule: + """Convert nested format to flat AnaplanModule.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else AnaplanModuleAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _ANAPLAN_MODULE_REL_FIELDS, + AnaplanModuleRelationshipAttributes, + ) + return AnaplanModule( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_anaplan_module_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _anaplan_module_to_nested_bytes( + anaplan_module: AnaplanModule, serde: Serde +) -> bytes: + """Convert flat AnaplanModule to nested JSON bytes.""" + return serde.encode(_anaplan_module_to_nested(anaplan_module)) + + +def _anaplan_module_from_nested_bytes(data: bytes, serde: Serde) -> AnaplanModule: + """Convert nested JSON bytes to flat AnaplanModule.""" + nested = serde.decode(data, AnaplanModuleNested) + return _anaplan_module_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +AnaplanModule.ANAPLAN_WORKSPACE_QUALIFIED_NAME = KeywordField( + "anaplanWorkspaceQualifiedName", "anaplanWorkspaceQualifiedName" +) +AnaplanModule.ANAPLAN_WORKSPACE_NAME = KeywordField( + "anaplanWorkspaceName", "anaplanWorkspaceName" +) +AnaplanModule.ANAPLAN_MODEL_QUALIFIED_NAME = KeywordField( + "anaplanModelQualifiedName", "anaplanModelQualifiedName" +) +AnaplanModule.ANAPLAN_MODEL_NAME = KeywordField("anaplanModelName", "anaplanModelName") +AnaplanModule.ANAPLAN_MODULE_QUALIFIED_NAME = KeywordField( + "anaplanModuleQualifiedName", "anaplanModuleQualifiedName" +) +AnaplanModule.ANAPLAN_MODULE_NAME = KeywordField( + "anaplanModuleName", "anaplanModuleName" +) +AnaplanModule.ANAPLAN_SOURCE_ID = KeywordField("anaplanSourceId", "anaplanSourceId") +AnaplanModule.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +AnaplanModule.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +AnaplanModule.ANAPLAN_MODEL = RelationField("anaplanModel") +AnaplanModule.ANAPLAN_LINE_ITEMS = RelationField("anaplanLineItems") +AnaplanModule.ANAPLAN_VIEWS = RelationField("anaplanViews") +AnaplanModule.ANOMALO_CHECKS = RelationField("anomaloChecks") +AnaplanModule.APPLICATION = RelationField("application") +AnaplanModule.APPLICATION_FIELD = RelationField("applicationField") +AnaplanModule.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +AnaplanModule.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +AnaplanModule.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +AnaplanModule.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +AnaplanModule.METRICS = RelationField("metrics") +AnaplanModule.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +AnaplanModule.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +AnaplanModule.MEANINGS = RelationField("meanings") +AnaplanModule.MC_MONITORS = RelationField("mcMonitors") +AnaplanModule.MC_INCIDENTS = RelationField("mcIncidents") +AnaplanModule.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +AnaplanModule.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +AnaplanModule.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +AnaplanModule.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +AnaplanModule.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +AnaplanModule.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +AnaplanModule.FILES = RelationField("files") +AnaplanModule.LINKS = RelationField("links") +AnaplanModule.README = RelationField("readme") +AnaplanModule.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +AnaplanModule.SODA_CHECKS = RelationField("sodaChecks") +AnaplanModule.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +AnaplanModule.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/anaplan_page.py b/pyatlan_v9/model/assets/anaplan_page.py new file mode 100644 index 000000000..9e2497f1c --- /dev/null +++ b/pyatlan_v9/model/assets/anaplan_page.py @@ -0,0 +1,710 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +AnaplanPage asset model with flattened inheritance. + +This module provides: +- AnaplanPage: Flat asset class (easy to use) +- AnaplanPageAttributes: Nested attributes struct (extends AssetAttributes) +- AnaplanPageNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan.model.enums import AtlanConnectorType +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .anaplan_related import RelatedAnaplanApp, RelatedAnaplanModel + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class AnaplanPage(Asset): + """ + Instances of an AnaplanPage in Atlan. + """ + + ANAPLAN_APP_QUALIFIED_NAME: ClassVar[Any] = None + ANAPLAN_PAGE_CATEGORY_NAME: ClassVar[Any] = None + ANAPLAN_PAGE_TYPE: ClassVar[Any] = None + ANAPLAN_WORKSPACE_QUALIFIED_NAME: ClassVar[Any] = None + ANAPLAN_WORKSPACE_NAME: ClassVar[Any] = None + ANAPLAN_MODEL_QUALIFIED_NAME: ClassVar[Any] = None + ANAPLAN_MODEL_NAME: ClassVar[Any] = None + ANAPLAN_MODULE_QUALIFIED_NAME: ClassVar[Any] = None + ANAPLAN_MODULE_NAME: ClassVar[Any] = None + ANAPLAN_SOURCE_ID: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANAPLAN_APP: ClassVar[Any] = None + ANAPLAN_MODELS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "AnaplanPage" + + anaplan_app_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanApp asset that contains this asset.""" + + anaplan_page_category_name: Union[str, None, UnsetType] = UNSET + """Category name of the AnaplanPage from the source system.""" + + anaplan_page_type: Union[str, None, UnsetType] = UNSET + """Type of the AnaplanPage from the source system.""" + + anaplan_workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanWorkspace asset that contains this asset (AnaplanModel and everything under its hierarchy).""" + + anaplan_workspace_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanWorkspace asset that contains this asset (AnaplanModel and everything under its hierarchy).""" + + anaplan_model_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanModel asset that contains this asset (AnaplanModule and everything under its hierarchy).""" + + anaplan_model_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanModel asset that contains this asset (AnaplanModule and everything under its hierarchy).""" + + anaplan_module_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanModule asset that contains this asset (AnaplanLineItem, AnaplanList, AnaplanView and everything under their hierarchy).""" + + anaplan_module_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanModule asset that contains this asset (AnaplanLineItem, AnaplanList, AnaplanView and everything under their hierarchy).""" + + anaplan_source_id: Union[str, None, UnsetType] = UNSET + """Id/Guid of the Anaplan asset in the source system.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anaplan_app: Union[RelatedAnaplanApp, None, UnsetType] = UNSET + """App containing the page.""" + + anaplan_models: Union[List[RelatedAnaplanModel], None, UnsetType] = UNSET + """Models related to the page.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "AnaplanPage" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + app_qualified_name: str, + connection_qualified_name: str | None = None, + ) -> "AnaplanPage": + """Create a new AnaplanPage asset.""" + validate_required_fields( + ["name", "app_qualified_name"], [name, app_qualified_name] + ) + connection_qn: Union[str, None, UnsetType] = UNSET + if connection_qualified_name is not None: + connector_name = str( + AtlanConnectorType.get_connector_name(connection_qualified_name) + ) + else: + connection_qn, connector_name = AtlanConnectorType.get_connector_name( + app_qualified_name, "app_qualified_name", 4 + ) + return cls( + name=name, + qualified_name=f"{app_qualified_name}/{name}", + connection_qualified_name=connection_qualified_name or connection_qn, + connector_name=connector_name, + anaplan_app_qualified_name=app_qualified_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "AnaplanPage": + """Create an AnaplanPage instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "AnaplanPage": + """Return only fields required for update operations.""" + return AnaplanPage.updater(qualified_name=self.qualified_name, name=self.name) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _anaplan_page_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> AnaplanPage: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + AnaplanPage instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _anaplan_page_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class AnaplanPageAttributes(AssetAttributes): + """AnaplanPage-specific attributes for nested API format.""" + + anaplan_app_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanApp asset that contains this asset.""" + + anaplan_page_category_name: Union[str, None, UnsetType] = UNSET + """Category name of the AnaplanPage from the source system.""" + + anaplan_page_type: Union[str, None, UnsetType] = UNSET + """Type of the AnaplanPage from the source system.""" + + anaplan_workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanWorkspace asset that contains this asset (AnaplanModel and everything under its hierarchy).""" + + anaplan_workspace_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanWorkspace asset that contains this asset (AnaplanModel and everything under its hierarchy).""" + + anaplan_model_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanModel asset that contains this asset (AnaplanModule and everything under its hierarchy).""" + + anaplan_model_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanModel asset that contains this asset (AnaplanModule and everything under its hierarchy).""" + + anaplan_module_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanModule asset that contains this asset (AnaplanLineItem, AnaplanList, AnaplanView and everything under their hierarchy).""" + + anaplan_module_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanModule asset that contains this asset (AnaplanLineItem, AnaplanList, AnaplanView and everything under their hierarchy).""" + + anaplan_source_id: Union[str, None, UnsetType] = UNSET + """Id/Guid of the Anaplan asset in the source system.""" + + +class AnaplanPageRelationshipAttributes(AssetRelationshipAttributes): + """AnaplanPage-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anaplan_app: Union[RelatedAnaplanApp, None, UnsetType] = UNSET + """App containing the page.""" + + anaplan_models: Union[List[RelatedAnaplanModel], None, UnsetType] = UNSET + """Models related to the page.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class AnaplanPageNested(AssetNested): + """AnaplanPage in nested API format for high-performance serialization.""" + + attributes: Union[AnaplanPageAttributes, UnsetType] = UNSET + relationship_attributes: Union[AnaplanPageRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + AnaplanPageRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + AnaplanPageRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_ANAPLAN_PAGE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anaplan_app", + "anaplan_models", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_anaplan_page_attrs( + attrs: AnaplanPageAttributes, obj: AnaplanPage +) -> None: + """Populate AnaplanPage-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.anaplan_app_qualified_name = obj.anaplan_app_qualified_name + attrs.anaplan_page_category_name = obj.anaplan_page_category_name + attrs.anaplan_page_type = obj.anaplan_page_type + attrs.anaplan_workspace_qualified_name = obj.anaplan_workspace_qualified_name + attrs.anaplan_workspace_name = obj.anaplan_workspace_name + attrs.anaplan_model_qualified_name = obj.anaplan_model_qualified_name + attrs.anaplan_model_name = obj.anaplan_model_name + attrs.anaplan_module_qualified_name = obj.anaplan_module_qualified_name + attrs.anaplan_module_name = obj.anaplan_module_name + attrs.anaplan_source_id = obj.anaplan_source_id + + +def _extract_anaplan_page_attrs(attrs: AnaplanPageAttributes) -> dict: + """Extract all AnaplanPage attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["anaplan_app_qualified_name"] = attrs.anaplan_app_qualified_name + result["anaplan_page_category_name"] = attrs.anaplan_page_category_name + result["anaplan_page_type"] = attrs.anaplan_page_type + result["anaplan_workspace_qualified_name"] = attrs.anaplan_workspace_qualified_name + result["anaplan_workspace_name"] = attrs.anaplan_workspace_name + result["anaplan_model_qualified_name"] = attrs.anaplan_model_qualified_name + result["anaplan_model_name"] = attrs.anaplan_model_name + result["anaplan_module_qualified_name"] = attrs.anaplan_module_qualified_name + result["anaplan_module_name"] = attrs.anaplan_module_name + result["anaplan_source_id"] = attrs.anaplan_source_id + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _anaplan_page_to_nested(anaplan_page: AnaplanPage) -> AnaplanPageNested: + """Convert flat AnaplanPage to nested format.""" + attrs = AnaplanPageAttributes() + _populate_anaplan_page_attrs(attrs, anaplan_page) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + anaplan_page, _ANAPLAN_PAGE_REL_FIELDS, AnaplanPageRelationshipAttributes + ) + return AnaplanPageNested( + guid=anaplan_page.guid, + type_name=anaplan_page.type_name, + status=anaplan_page.status, + version=anaplan_page.version, + create_time=anaplan_page.create_time, + update_time=anaplan_page.update_time, + created_by=anaplan_page.created_by, + updated_by=anaplan_page.updated_by, + classifications=anaplan_page.classifications, + classification_names=anaplan_page.classification_names, + meanings=anaplan_page.meanings, + labels=anaplan_page.labels, + business_attributes=anaplan_page.business_attributes, + custom_attributes=anaplan_page.custom_attributes, + pending_tasks=anaplan_page.pending_tasks, + proxy=anaplan_page.proxy, + is_incomplete=anaplan_page.is_incomplete, + provenance_type=anaplan_page.provenance_type, + home_id=anaplan_page.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _anaplan_page_from_nested(nested: AnaplanPageNested) -> AnaplanPage: + """Convert nested format to flat AnaplanPage.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else AnaplanPageAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _ANAPLAN_PAGE_REL_FIELDS, + AnaplanPageRelationshipAttributes, + ) + return AnaplanPage( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_anaplan_page_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _anaplan_page_to_nested_bytes(anaplan_page: AnaplanPage, serde: Serde) -> bytes: + """Convert flat AnaplanPage to nested JSON bytes.""" + return serde.encode(_anaplan_page_to_nested(anaplan_page)) + + +def _anaplan_page_from_nested_bytes(data: bytes, serde: Serde) -> AnaplanPage: + """Convert nested JSON bytes to flat AnaplanPage.""" + nested = serde.decode(data, AnaplanPageNested) + return _anaplan_page_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +AnaplanPage.ANAPLAN_APP_QUALIFIED_NAME = KeywordField( + "anaplanAppQualifiedName", "anaplanAppQualifiedName" +) +AnaplanPage.ANAPLAN_PAGE_CATEGORY_NAME = KeywordField( + "anaplanPageCategoryName", "anaplanPageCategoryName" +) +AnaplanPage.ANAPLAN_PAGE_TYPE = KeywordField("anaplanPageType", "anaplanPageType") +AnaplanPage.ANAPLAN_WORKSPACE_QUALIFIED_NAME = KeywordField( + "anaplanWorkspaceQualifiedName", "anaplanWorkspaceQualifiedName" +) +AnaplanPage.ANAPLAN_WORKSPACE_NAME = KeywordField( + "anaplanWorkspaceName", "anaplanWorkspaceName" +) +AnaplanPage.ANAPLAN_MODEL_QUALIFIED_NAME = KeywordField( + "anaplanModelQualifiedName", "anaplanModelQualifiedName" +) +AnaplanPage.ANAPLAN_MODEL_NAME = KeywordField("anaplanModelName", "anaplanModelName") +AnaplanPage.ANAPLAN_MODULE_QUALIFIED_NAME = KeywordField( + "anaplanModuleQualifiedName", "anaplanModuleQualifiedName" +) +AnaplanPage.ANAPLAN_MODULE_NAME = KeywordField("anaplanModuleName", "anaplanModuleName") +AnaplanPage.ANAPLAN_SOURCE_ID = KeywordField("anaplanSourceId", "anaplanSourceId") +AnaplanPage.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +AnaplanPage.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +AnaplanPage.ANAPLAN_APP = RelationField("anaplanApp") +AnaplanPage.ANAPLAN_MODELS = RelationField("anaplanModels") +AnaplanPage.ANOMALO_CHECKS = RelationField("anomaloChecks") +AnaplanPage.APPLICATION = RelationField("application") +AnaplanPage.APPLICATION_FIELD = RelationField("applicationField") +AnaplanPage.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +AnaplanPage.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +AnaplanPage.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +AnaplanPage.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +AnaplanPage.METRICS = RelationField("metrics") +AnaplanPage.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +AnaplanPage.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +AnaplanPage.MEANINGS = RelationField("meanings") +AnaplanPage.MC_MONITORS = RelationField("mcMonitors") +AnaplanPage.MC_INCIDENTS = RelationField("mcIncidents") +AnaplanPage.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +AnaplanPage.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +AnaplanPage.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +AnaplanPage.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +AnaplanPage.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +AnaplanPage.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +AnaplanPage.FILES = RelationField("files") +AnaplanPage.LINKS = RelationField("links") +AnaplanPage.README = RelationField("readme") +AnaplanPage.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +AnaplanPage.SODA_CHECKS = RelationField("sodaChecks") +AnaplanPage.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +AnaplanPage.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/anaplan_related.py b/pyatlan_v9/model/assets/anaplan_related.py new file mode 100644 index 000000000..263e1e70a --- /dev/null +++ b/pyatlan_v9/model/assets/anaplan_related.py @@ -0,0 +1,240 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Anaplan module. + +This module contains all Related{Type} classes for the Anaplan type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Union + +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedBI +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedAnaplan", + "RelatedAnaplanWorkspace", + "RelatedAnaplanApp", + "RelatedAnaplanPage", + "RelatedAnaplanModel", + "RelatedAnaplanModule", + "RelatedAnaplanList", + "RelatedAnaplanSystemDimension", + "RelatedAnaplanDimension", + "RelatedAnaplanLineItem", + "RelatedAnaplanView", +] + + +class RelatedAnaplan(RelatedBI): + """ + Related entity reference for Anaplan assets. + + Extends RelatedBI with Anaplan-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Anaplan" so it serializes correctly + + anaplan_workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanWorkspace asset that contains this asset (AnaplanModel and everything under its hierarchy).""" + + anaplan_workspace_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanWorkspace asset that contains this asset (AnaplanModel and everything under its hierarchy).""" + + anaplan_model_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanModel asset that contains this asset (AnaplanModule and everything under its hierarchy).""" + + anaplan_model_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanModel asset that contains this asset (AnaplanModule and everything under its hierarchy).""" + + anaplan_module_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanModule asset that contains this asset (AnaplanLineItem, AnaplanList, AnaplanView and everything under their hierarchy).""" + + anaplan_module_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanModule asset that contains this asset (AnaplanLineItem, AnaplanList, AnaplanView and everything under their hierarchy).""" + + anaplan_source_id: Union[str, None, UnsetType] = UNSET + """Id/Guid of the Anaplan asset in the source system.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Anaplan" + + +class RelatedAnaplanWorkspace(RelatedAnaplan): + """ + Related entity reference for AnaplanWorkspace assets. + + Extends RelatedAnaplan with AnaplanWorkspace-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "AnaplanWorkspace" so it serializes correctly + + anaplan_workspace_current_size: Union[int, None, UnsetType] = UNSET + """Current size of the AnaplanWorkspace from the source system, estimated in MB.""" + + anaplan_workspace_allowance_size: Union[int, None, UnsetType] = UNSET + """Alloted size quota for the AnaplanWorkspace from the source system, estimated in MB.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "AnaplanWorkspace" + + +class RelatedAnaplanApp(RelatedAnaplan): + """ + Related entity reference for AnaplanApp assets. + + Extends RelatedAnaplan with AnaplanApp-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "AnaplanApp" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "AnaplanApp" + + +class RelatedAnaplanPage(RelatedAnaplan): + """ + Related entity reference for AnaplanPage assets. + + Extends RelatedAnaplan with AnaplanPage-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "AnaplanPage" so it serializes correctly + + anaplan_app_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanApp asset that contains this asset.""" + + anaplan_page_category_name: Union[str, None, UnsetType] = UNSET + """Category name of the AnaplanPage from the source system.""" + + anaplan_page_type: Union[str, None, UnsetType] = UNSET + """Type of the AnaplanPage from the source system.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "AnaplanPage" + + +class RelatedAnaplanModel(RelatedAnaplan): + """ + Related entity reference for AnaplanModel assets. + + Extends RelatedAnaplan with AnaplanModel-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "AnaplanModel" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "AnaplanModel" + + +class RelatedAnaplanModule(RelatedAnaplan): + """ + Related entity reference for AnaplanModule assets. + + Extends RelatedAnaplan with AnaplanModule-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "AnaplanModule" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "AnaplanModule" + + +class RelatedAnaplanList(RelatedAnaplan): + """ + Related entity reference for AnaplanList assets. + + Extends RelatedAnaplan with AnaplanList-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "AnaplanList" so it serializes correctly + + anaplan_list_item_count: Union[int, None, UnsetType] = UNSET + """Item Count of the AnaplanList from the source system.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "AnaplanList" + + +class RelatedAnaplanSystemDimension(RelatedAnaplan): + """ + Related entity reference for AnaplanSystemDimension assets. + + Extends RelatedAnaplan with AnaplanSystemDimension-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "AnaplanSystemDimension" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "AnaplanSystemDimension" + + +class RelatedAnaplanDimension(RelatedAnaplan): + """ + Related entity reference for AnaplanDimension assets. + + Extends RelatedAnaplan with AnaplanDimension-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "AnaplanDimension" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "AnaplanDimension" + + +class RelatedAnaplanLineItem(RelatedAnaplan): + """ + Related entity reference for AnaplanLineItem assets. + + Extends RelatedAnaplan with AnaplanLineItem-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "AnaplanLineItem" so it serializes correctly + + anaplan_line_item_formula: Union[str, None, UnsetType] = UNSET + """Formula of the AnaplanLineItem from the source system.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "AnaplanLineItem" + + +class RelatedAnaplanView(RelatedAnaplan): + """ + Related entity reference for AnaplanView assets. + + Extends RelatedAnaplan with AnaplanView-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "AnaplanView" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "AnaplanView" diff --git a/pyatlan_v9/model/assets/anaplan_system_dimension.py b/pyatlan_v9/model/assets/anaplan_system_dimension.py new file mode 100644 index 000000000..607f66641 --- /dev/null +++ b/pyatlan_v9/model/assets/anaplan_system_dimension.py @@ -0,0 +1,676 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +AnaplanSystemDimension asset model with flattened inheritance. + +This module provides: +- AnaplanSystemDimension: Flat asset class (easy to use) +- AnaplanSystemDimensionAttributes: Nested attributes struct (extends AssetAttributes) +- AnaplanSystemDimensionNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class AnaplanSystemDimension(Asset): + """ + Instances of an AnaplanSystemDimension in Atlan. + """ + + ANAPLAN_WORKSPACE_QUALIFIED_NAME: ClassVar[Any] = None + ANAPLAN_WORKSPACE_NAME: ClassVar[Any] = None + ANAPLAN_MODEL_QUALIFIED_NAME: ClassVar[Any] = None + ANAPLAN_MODEL_NAME: ClassVar[Any] = None + ANAPLAN_MODULE_QUALIFIED_NAME: ClassVar[Any] = None + ANAPLAN_MODULE_NAME: ClassVar[Any] = None + ANAPLAN_SOURCE_ID: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "AnaplanSystemDimension" + + anaplan_workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanWorkspace asset that contains this asset (AnaplanModel and everything under its hierarchy).""" + + anaplan_workspace_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanWorkspace asset that contains this asset (AnaplanModel and everything under its hierarchy).""" + + anaplan_model_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanModel asset that contains this asset (AnaplanModule and everything under its hierarchy).""" + + anaplan_model_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanModel asset that contains this asset (AnaplanModule and everything under its hierarchy).""" + + anaplan_module_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanModule asset that contains this asset (AnaplanLineItem, AnaplanList, AnaplanView and everything under their hierarchy).""" + + anaplan_module_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanModule asset that contains this asset (AnaplanLineItem, AnaplanList, AnaplanView and everything under their hierarchy).""" + + anaplan_source_id: Union[str, None, UnsetType] = UNSET + """Id/Guid of the Anaplan asset in the source system.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "AnaplanSystemDimension" + + @classmethod + @init_guid + def creator( + cls, *, name: str, connection_qualified_name: str + ) -> "AnaplanSystemDimension": + """Create a new AnaplanSystemDimension asset.""" + validate_required_fields( + ["name", "connection_qualified_name"], [name, connection_qualified_name] + ) + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + return cls( + name=name, + qualified_name=f"{connection_qualified_name}/{name}", + connection_qualified_name=connection_qualified_name, + connector_name=connector_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "AnaplanSystemDimension": + """Create an AnaplanSystemDimension instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "AnaplanSystemDimension": + """Return only fields required for update operations.""" + return AnaplanSystemDimension.updater( + qualified_name=self.qualified_name, name=self.name + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _anaplan_system_dimension_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> AnaplanSystemDimension: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + AnaplanSystemDimension instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _anaplan_system_dimension_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class AnaplanSystemDimensionAttributes(AssetAttributes): + """AnaplanSystemDimension-specific attributes for nested API format.""" + + anaplan_workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanWorkspace asset that contains this asset (AnaplanModel and everything under its hierarchy).""" + + anaplan_workspace_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanWorkspace asset that contains this asset (AnaplanModel and everything under its hierarchy).""" + + anaplan_model_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanModel asset that contains this asset (AnaplanModule and everything under its hierarchy).""" + + anaplan_model_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanModel asset that contains this asset (AnaplanModule and everything under its hierarchy).""" + + anaplan_module_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanModule asset that contains this asset (AnaplanLineItem, AnaplanList, AnaplanView and everything under their hierarchy).""" + + anaplan_module_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanModule asset that contains this asset (AnaplanLineItem, AnaplanList, AnaplanView and everything under their hierarchy).""" + + anaplan_source_id: Union[str, None, UnsetType] = UNSET + """Id/Guid of the Anaplan asset in the source system.""" + + +class AnaplanSystemDimensionRelationshipAttributes(AssetRelationshipAttributes): + """AnaplanSystemDimension-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class AnaplanSystemDimensionNested(AssetNested): + """AnaplanSystemDimension in nested API format for high-performance serialization.""" + + attributes: Union[AnaplanSystemDimensionAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + AnaplanSystemDimensionRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + AnaplanSystemDimensionRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + AnaplanSystemDimensionRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_ANAPLAN_SYSTEM_DIMENSION_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_anaplan_system_dimension_attrs( + attrs: AnaplanSystemDimensionAttributes, obj: AnaplanSystemDimension +) -> None: + """Populate AnaplanSystemDimension-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.anaplan_workspace_qualified_name = obj.anaplan_workspace_qualified_name + attrs.anaplan_workspace_name = obj.anaplan_workspace_name + attrs.anaplan_model_qualified_name = obj.anaplan_model_qualified_name + attrs.anaplan_model_name = obj.anaplan_model_name + attrs.anaplan_module_qualified_name = obj.anaplan_module_qualified_name + attrs.anaplan_module_name = obj.anaplan_module_name + attrs.anaplan_source_id = obj.anaplan_source_id + + +def _extract_anaplan_system_dimension_attrs( + attrs: AnaplanSystemDimensionAttributes, +) -> dict: + """Extract all AnaplanSystemDimension attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["anaplan_workspace_qualified_name"] = attrs.anaplan_workspace_qualified_name + result["anaplan_workspace_name"] = attrs.anaplan_workspace_name + result["anaplan_model_qualified_name"] = attrs.anaplan_model_qualified_name + result["anaplan_model_name"] = attrs.anaplan_model_name + result["anaplan_module_qualified_name"] = attrs.anaplan_module_qualified_name + result["anaplan_module_name"] = attrs.anaplan_module_name + result["anaplan_source_id"] = attrs.anaplan_source_id + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _anaplan_system_dimension_to_nested( + anaplan_system_dimension: AnaplanSystemDimension, +) -> AnaplanSystemDimensionNested: + """Convert flat AnaplanSystemDimension to nested format.""" + attrs = AnaplanSystemDimensionAttributes() + _populate_anaplan_system_dimension_attrs(attrs, anaplan_system_dimension) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + anaplan_system_dimension, + _ANAPLAN_SYSTEM_DIMENSION_REL_FIELDS, + AnaplanSystemDimensionRelationshipAttributes, + ) + return AnaplanSystemDimensionNested( + guid=anaplan_system_dimension.guid, + type_name=anaplan_system_dimension.type_name, + status=anaplan_system_dimension.status, + version=anaplan_system_dimension.version, + create_time=anaplan_system_dimension.create_time, + update_time=anaplan_system_dimension.update_time, + created_by=anaplan_system_dimension.created_by, + updated_by=anaplan_system_dimension.updated_by, + classifications=anaplan_system_dimension.classifications, + classification_names=anaplan_system_dimension.classification_names, + meanings=anaplan_system_dimension.meanings, + labels=anaplan_system_dimension.labels, + business_attributes=anaplan_system_dimension.business_attributes, + custom_attributes=anaplan_system_dimension.custom_attributes, + pending_tasks=anaplan_system_dimension.pending_tasks, + proxy=anaplan_system_dimension.proxy, + is_incomplete=anaplan_system_dimension.is_incomplete, + provenance_type=anaplan_system_dimension.provenance_type, + home_id=anaplan_system_dimension.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _anaplan_system_dimension_from_nested( + nested: AnaplanSystemDimensionNested, +) -> AnaplanSystemDimension: + """Convert nested format to flat AnaplanSystemDimension.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else AnaplanSystemDimensionAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _ANAPLAN_SYSTEM_DIMENSION_REL_FIELDS, + AnaplanSystemDimensionRelationshipAttributes, + ) + return AnaplanSystemDimension( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_anaplan_system_dimension_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _anaplan_system_dimension_to_nested_bytes( + anaplan_system_dimension: AnaplanSystemDimension, serde: Serde +) -> bytes: + """Convert flat AnaplanSystemDimension to nested JSON bytes.""" + return serde.encode(_anaplan_system_dimension_to_nested(anaplan_system_dimension)) + + +def _anaplan_system_dimension_from_nested_bytes( + data: bytes, serde: Serde +) -> AnaplanSystemDimension: + """Convert nested JSON bytes to flat AnaplanSystemDimension.""" + nested = serde.decode(data, AnaplanSystemDimensionNested) + return _anaplan_system_dimension_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +AnaplanSystemDimension.ANAPLAN_WORKSPACE_QUALIFIED_NAME = KeywordField( + "anaplanWorkspaceQualifiedName", "anaplanWorkspaceQualifiedName" +) +AnaplanSystemDimension.ANAPLAN_WORKSPACE_NAME = KeywordField( + "anaplanWorkspaceName", "anaplanWorkspaceName" +) +AnaplanSystemDimension.ANAPLAN_MODEL_QUALIFIED_NAME = KeywordField( + "anaplanModelQualifiedName", "anaplanModelQualifiedName" +) +AnaplanSystemDimension.ANAPLAN_MODEL_NAME = KeywordField( + "anaplanModelName", "anaplanModelName" +) +AnaplanSystemDimension.ANAPLAN_MODULE_QUALIFIED_NAME = KeywordField( + "anaplanModuleQualifiedName", "anaplanModuleQualifiedName" +) +AnaplanSystemDimension.ANAPLAN_MODULE_NAME = KeywordField( + "anaplanModuleName", "anaplanModuleName" +) +AnaplanSystemDimension.ANAPLAN_SOURCE_ID = KeywordField( + "anaplanSourceId", "anaplanSourceId" +) +AnaplanSystemDimension.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +AnaplanSystemDimension.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +AnaplanSystemDimension.ANOMALO_CHECKS = RelationField("anomaloChecks") +AnaplanSystemDimension.APPLICATION = RelationField("application") +AnaplanSystemDimension.APPLICATION_FIELD = RelationField("applicationField") +AnaplanSystemDimension.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +AnaplanSystemDimension.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +AnaplanSystemDimension.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +AnaplanSystemDimension.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +AnaplanSystemDimension.METRICS = RelationField("metrics") +AnaplanSystemDimension.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +AnaplanSystemDimension.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +AnaplanSystemDimension.MEANINGS = RelationField("meanings") +AnaplanSystemDimension.MC_MONITORS = RelationField("mcMonitors") +AnaplanSystemDimension.MC_INCIDENTS = RelationField("mcIncidents") +AnaplanSystemDimension.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +AnaplanSystemDimension.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +AnaplanSystemDimension.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +AnaplanSystemDimension.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +AnaplanSystemDimension.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +AnaplanSystemDimension.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +AnaplanSystemDimension.FILES = RelationField("files") +AnaplanSystemDimension.LINKS = RelationField("links") +AnaplanSystemDimension.README = RelationField("readme") +AnaplanSystemDimension.SCHEMA_REGISTRY_SUBJECTS = RelationField( + "schemaRegistrySubjects" +) +AnaplanSystemDimension.SODA_CHECKS = RelationField("sodaChecks") +AnaplanSystemDimension.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +AnaplanSystemDimension.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/anaplan_view.py b/pyatlan_v9/model/assets/anaplan_view.py new file mode 100644 index 000000000..f008b3580 --- /dev/null +++ b/pyatlan_v9/model/assets/anaplan_view.py @@ -0,0 +1,719 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +AnaplanView asset model with flattened inheritance. + +This module provides: +- AnaplanView: Flat asset class (easy to use) +- AnaplanViewAttributes: Nested attributes struct (extends AssetAttributes) +- AnaplanViewNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan.model.enums import AtlanConnectorType +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .anaplan_related import RelatedAnaplanDimension, RelatedAnaplanModule + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class AnaplanView(Asset): + """ + Instances of an AnaplanView in Atlan. + """ + + ANAPLAN_WORKSPACE_QUALIFIED_NAME: ClassVar[Any] = None + ANAPLAN_WORKSPACE_NAME: ClassVar[Any] = None + ANAPLAN_MODEL_QUALIFIED_NAME: ClassVar[Any] = None + ANAPLAN_MODEL_NAME: ClassVar[Any] = None + ANAPLAN_MODULE_QUALIFIED_NAME: ClassVar[Any] = None + ANAPLAN_MODULE_NAME: ClassVar[Any] = None + ANAPLAN_SOURCE_ID: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANAPLAN_MODULE: ClassVar[Any] = None + ANAPLAN_ROW_DIMENSIONS: ClassVar[Any] = None + ANAPLAN_COLUMN_DIMENSIONS: ClassVar[Any] = None + ANAPLAN_PAGE_DIMENSIONS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "AnaplanView" + + anaplan_workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanWorkspace asset that contains this asset (AnaplanModel and everything under its hierarchy).""" + + anaplan_workspace_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanWorkspace asset that contains this asset (AnaplanModel and everything under its hierarchy).""" + + anaplan_model_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanModel asset that contains this asset (AnaplanModule and everything under its hierarchy).""" + + anaplan_model_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanModel asset that contains this asset (AnaplanModule and everything under its hierarchy).""" + + anaplan_module_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanModule asset that contains this asset (AnaplanLineItem, AnaplanList, AnaplanView and everything under their hierarchy).""" + + anaplan_module_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanModule asset that contains this asset (AnaplanLineItem, AnaplanList, AnaplanView and everything under their hierarchy).""" + + anaplan_source_id: Union[str, None, UnsetType] = UNSET + """Id/Guid of the Anaplan asset in the source system.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anaplan_module: Union[RelatedAnaplanModule, None, UnsetType] = UNSET + """Module containing the view.""" + + anaplan_row_dimensions: Union[List[RelatedAnaplanDimension], None, UnsetType] = ( + UNSET + ) + """Row dimensions related to the view.""" + + anaplan_column_dimensions: Union[List[RelatedAnaplanDimension], None, UnsetType] = ( + UNSET + ) + """Column dimensions related to the view.""" + + anaplan_page_dimensions: Union[List[RelatedAnaplanDimension], None, UnsetType] = ( + UNSET + ) + """Page dimensions related to the view.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "AnaplanView" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+/[^/]+$" + ) + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + module_qualified_name: str, + connection_qualified_name: str | None = None, + ) -> "AnaplanView": + """Create a new AnaplanView asset.""" + validate_required_fields( + ["name", "module_qualified_name"], [name, module_qualified_name] + ) + fields = module_qualified_name.split("/") + connection_qn: Union[str, None, UnsetType] = UNSET + if connection_qualified_name is not None: + connector_name = str( + AtlanConnectorType.get_connector_name(connection_qualified_name) + ) + else: + connection_qn, connector_name = AtlanConnectorType.get_connector_name( + module_qualified_name, "module_qualified_name", 6 + ) + workspace_qualified_name = "/".join(fields[:4]) if len(fields) >= 4 else UNSET + workspace_name = fields[3] if len(fields) > 3 else UNSET + model_qualified_name = "/".join(fields[:5]) if len(fields) >= 5 else UNSET + model_name = fields[4] if len(fields) > 4 else UNSET + module_name = fields[5] if len(fields) > 5 else UNSET + return cls( + name=name, + qualified_name=f"{module_qualified_name}/{name}", + connection_qualified_name=connection_qualified_name or connection_qn, + connector_name=connector_name, + anaplan_workspace_qualified_name=workspace_qualified_name, + anaplan_workspace_name=workspace_name, + anaplan_model_qualified_name=model_qualified_name, + anaplan_model_name=model_name, + anaplan_module_qualified_name=module_qualified_name, + anaplan_module_name=module_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "AnaplanView": + """Create an AnaplanView instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "AnaplanView": + """Return only fields required for update operations.""" + return AnaplanView.updater(qualified_name=self.qualified_name, name=self.name) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _anaplan_view_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> AnaplanView: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + AnaplanView instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _anaplan_view_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class AnaplanViewAttributes(AssetAttributes): + """AnaplanView-specific attributes for nested API format.""" + + anaplan_workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanWorkspace asset that contains this asset (AnaplanModel and everything under its hierarchy).""" + + anaplan_workspace_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanWorkspace asset that contains this asset (AnaplanModel and everything under its hierarchy).""" + + anaplan_model_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanModel asset that contains this asset (AnaplanModule and everything under its hierarchy).""" + + anaplan_model_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanModel asset that contains this asset (AnaplanModule and everything under its hierarchy).""" + + anaplan_module_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanModule asset that contains this asset (AnaplanLineItem, AnaplanList, AnaplanView and everything under their hierarchy).""" + + anaplan_module_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanModule asset that contains this asset (AnaplanLineItem, AnaplanList, AnaplanView and everything under their hierarchy).""" + + anaplan_source_id: Union[str, None, UnsetType] = UNSET + """Id/Guid of the Anaplan asset in the source system.""" + + +class AnaplanViewRelationshipAttributes(AssetRelationshipAttributes): + """AnaplanView-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anaplan_module: Union[RelatedAnaplanModule, None, UnsetType] = UNSET + """Module containing the view.""" + + anaplan_row_dimensions: Union[List[RelatedAnaplanDimension], None, UnsetType] = ( + UNSET + ) + """Row dimensions related to the view.""" + + anaplan_column_dimensions: Union[List[RelatedAnaplanDimension], None, UnsetType] = ( + UNSET + ) + """Column dimensions related to the view.""" + + anaplan_page_dimensions: Union[List[RelatedAnaplanDimension], None, UnsetType] = ( + UNSET + ) + """Page dimensions related to the view.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class AnaplanViewNested(AssetNested): + """AnaplanView in nested API format for high-performance serialization.""" + + attributes: Union[AnaplanViewAttributes, UnsetType] = UNSET + relationship_attributes: Union[AnaplanViewRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + AnaplanViewRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + AnaplanViewRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_ANAPLAN_VIEW_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anaplan_module", + "anaplan_row_dimensions", + "anaplan_column_dimensions", + "anaplan_page_dimensions", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_anaplan_view_attrs( + attrs: AnaplanViewAttributes, obj: AnaplanView +) -> None: + """Populate AnaplanView-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.anaplan_workspace_qualified_name = obj.anaplan_workspace_qualified_name + attrs.anaplan_workspace_name = obj.anaplan_workspace_name + attrs.anaplan_model_qualified_name = obj.anaplan_model_qualified_name + attrs.anaplan_model_name = obj.anaplan_model_name + attrs.anaplan_module_qualified_name = obj.anaplan_module_qualified_name + attrs.anaplan_module_name = obj.anaplan_module_name + attrs.anaplan_source_id = obj.anaplan_source_id + + +def _extract_anaplan_view_attrs(attrs: AnaplanViewAttributes) -> dict: + """Extract all AnaplanView attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["anaplan_workspace_qualified_name"] = attrs.anaplan_workspace_qualified_name + result["anaplan_workspace_name"] = attrs.anaplan_workspace_name + result["anaplan_model_qualified_name"] = attrs.anaplan_model_qualified_name + result["anaplan_model_name"] = attrs.anaplan_model_name + result["anaplan_module_qualified_name"] = attrs.anaplan_module_qualified_name + result["anaplan_module_name"] = attrs.anaplan_module_name + result["anaplan_source_id"] = attrs.anaplan_source_id + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _anaplan_view_to_nested(anaplan_view: AnaplanView) -> AnaplanViewNested: + """Convert flat AnaplanView to nested format.""" + attrs = AnaplanViewAttributes() + _populate_anaplan_view_attrs(attrs, anaplan_view) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + anaplan_view, _ANAPLAN_VIEW_REL_FIELDS, AnaplanViewRelationshipAttributes + ) + return AnaplanViewNested( + guid=anaplan_view.guid, + type_name=anaplan_view.type_name, + status=anaplan_view.status, + version=anaplan_view.version, + create_time=anaplan_view.create_time, + update_time=anaplan_view.update_time, + created_by=anaplan_view.created_by, + updated_by=anaplan_view.updated_by, + classifications=anaplan_view.classifications, + classification_names=anaplan_view.classification_names, + meanings=anaplan_view.meanings, + labels=anaplan_view.labels, + business_attributes=anaplan_view.business_attributes, + custom_attributes=anaplan_view.custom_attributes, + pending_tasks=anaplan_view.pending_tasks, + proxy=anaplan_view.proxy, + is_incomplete=anaplan_view.is_incomplete, + provenance_type=anaplan_view.provenance_type, + home_id=anaplan_view.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _anaplan_view_from_nested(nested: AnaplanViewNested) -> AnaplanView: + """Convert nested format to flat AnaplanView.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else AnaplanViewAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _ANAPLAN_VIEW_REL_FIELDS, + AnaplanViewRelationshipAttributes, + ) + return AnaplanView( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_anaplan_view_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _anaplan_view_to_nested_bytes(anaplan_view: AnaplanView, serde: Serde) -> bytes: + """Convert flat AnaplanView to nested JSON bytes.""" + return serde.encode(_anaplan_view_to_nested(anaplan_view)) + + +def _anaplan_view_from_nested_bytes(data: bytes, serde: Serde) -> AnaplanView: + """Convert nested JSON bytes to flat AnaplanView.""" + nested = serde.decode(data, AnaplanViewNested) + return _anaplan_view_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +AnaplanView.ANAPLAN_WORKSPACE_QUALIFIED_NAME = KeywordField( + "anaplanWorkspaceQualifiedName", "anaplanWorkspaceQualifiedName" +) +AnaplanView.ANAPLAN_WORKSPACE_NAME = KeywordField( + "anaplanWorkspaceName", "anaplanWorkspaceName" +) +AnaplanView.ANAPLAN_MODEL_QUALIFIED_NAME = KeywordField( + "anaplanModelQualifiedName", "anaplanModelQualifiedName" +) +AnaplanView.ANAPLAN_MODEL_NAME = KeywordField("anaplanModelName", "anaplanModelName") +AnaplanView.ANAPLAN_MODULE_QUALIFIED_NAME = KeywordField( + "anaplanModuleQualifiedName", "anaplanModuleQualifiedName" +) +AnaplanView.ANAPLAN_MODULE_NAME = KeywordField("anaplanModuleName", "anaplanModuleName") +AnaplanView.ANAPLAN_SOURCE_ID = KeywordField("anaplanSourceId", "anaplanSourceId") +AnaplanView.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +AnaplanView.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +AnaplanView.ANAPLAN_MODULE = RelationField("anaplanModule") +AnaplanView.ANAPLAN_ROW_DIMENSIONS = RelationField("anaplanRowDimensions") +AnaplanView.ANAPLAN_COLUMN_DIMENSIONS = RelationField("anaplanColumnDimensions") +AnaplanView.ANAPLAN_PAGE_DIMENSIONS = RelationField("anaplanPageDimensions") +AnaplanView.ANOMALO_CHECKS = RelationField("anomaloChecks") +AnaplanView.APPLICATION = RelationField("application") +AnaplanView.APPLICATION_FIELD = RelationField("applicationField") +AnaplanView.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +AnaplanView.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +AnaplanView.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +AnaplanView.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +AnaplanView.METRICS = RelationField("metrics") +AnaplanView.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +AnaplanView.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +AnaplanView.MEANINGS = RelationField("meanings") +AnaplanView.MC_MONITORS = RelationField("mcMonitors") +AnaplanView.MC_INCIDENTS = RelationField("mcIncidents") +AnaplanView.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +AnaplanView.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +AnaplanView.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +AnaplanView.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +AnaplanView.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +AnaplanView.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +AnaplanView.FILES = RelationField("files") +AnaplanView.LINKS = RelationField("links") +AnaplanView.README = RelationField("readme") +AnaplanView.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +AnaplanView.SODA_CHECKS = RelationField("sodaChecks") +AnaplanView.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +AnaplanView.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/anaplan_workspace.py b/pyatlan_v9/model/assets/anaplan_workspace.py new file mode 100644 index 000000000..b5267f912 --- /dev/null +++ b/pyatlan_v9/model/assets/anaplan_workspace.py @@ -0,0 +1,692 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +AnaplanWorkspace asset model with flattened inheritance. + +This module provides: +- AnaplanWorkspace: Flat asset class (easy to use) +- AnaplanWorkspaceAttributes: Nested attributes struct (extends AssetAttributes) +- AnaplanWorkspaceNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .anaplan_related import RelatedAnaplanModel + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class AnaplanWorkspace(Asset): + """ + Instances of an AnaplanWorkspace in Atlan. + """ + + ANAPLAN_WORKSPACE_CURRENT_SIZE: ClassVar[Any] = None + ANAPLAN_WORKSPACE_ALLOWANCE_SIZE: ClassVar[Any] = None + ANAPLAN_WORKSPACE_QUALIFIED_NAME: ClassVar[Any] = None + ANAPLAN_WORKSPACE_NAME: ClassVar[Any] = None + ANAPLAN_MODEL_QUALIFIED_NAME: ClassVar[Any] = None + ANAPLAN_MODEL_NAME: ClassVar[Any] = None + ANAPLAN_MODULE_QUALIFIED_NAME: ClassVar[Any] = None + ANAPLAN_MODULE_NAME: ClassVar[Any] = None + ANAPLAN_SOURCE_ID: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANAPLAN_MODELS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "AnaplanWorkspace" + + anaplan_workspace_current_size: Union[int, None, UnsetType] = UNSET + """Current size of the AnaplanWorkspace from the source system, estimated in MB.""" + + anaplan_workspace_allowance_size: Union[int, None, UnsetType] = UNSET + """Alloted size quota for the AnaplanWorkspace from the source system, estimated in MB.""" + + anaplan_workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanWorkspace asset that contains this asset (AnaplanModel and everything under its hierarchy).""" + + anaplan_workspace_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanWorkspace asset that contains this asset (AnaplanModel and everything under its hierarchy).""" + + anaplan_model_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanModel asset that contains this asset (AnaplanModule and everything under its hierarchy).""" + + anaplan_model_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanModel asset that contains this asset (AnaplanModule and everything under its hierarchy).""" + + anaplan_module_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanModule asset that contains this asset (AnaplanLineItem, AnaplanList, AnaplanView and everything under their hierarchy).""" + + anaplan_module_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanModule asset that contains this asset (AnaplanLineItem, AnaplanList, AnaplanView and everything under their hierarchy).""" + + anaplan_source_id: Union[str, None, UnsetType] = UNSET + """Id/Guid of the Anaplan asset in the source system.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anaplan_models: Union[List[RelatedAnaplanModel], None, UnsetType] = UNSET + """Individual models contained in the workspace.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "AnaplanWorkspace" + + @classmethod + @init_guid + def creator( + cls, *, name: str, connection_qualified_name: str + ) -> "AnaplanWorkspace": + """Create a new AnaplanWorkspace asset.""" + validate_required_fields( + ["name", "connection_qualified_name"], [name, connection_qualified_name] + ) + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + return cls( + name=name, + qualified_name=f"{connection_qualified_name}/{name}", + connection_qualified_name=connection_qualified_name, + connector_name=connector_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "AnaplanWorkspace": + """Create an AnaplanWorkspace instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "AnaplanWorkspace": + """Return only fields required for update operations.""" + return AnaplanWorkspace.updater( + qualified_name=self.qualified_name, name=self.name + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _anaplan_workspace_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> AnaplanWorkspace: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + AnaplanWorkspace instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _anaplan_workspace_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class AnaplanWorkspaceAttributes(AssetAttributes): + """AnaplanWorkspace-specific attributes for nested API format.""" + + anaplan_workspace_current_size: Union[int, None, UnsetType] = UNSET + """Current size of the AnaplanWorkspace from the source system, estimated in MB.""" + + anaplan_workspace_allowance_size: Union[int, None, UnsetType] = UNSET + """Alloted size quota for the AnaplanWorkspace from the source system, estimated in MB.""" + + anaplan_workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanWorkspace asset that contains this asset (AnaplanModel and everything under its hierarchy).""" + + anaplan_workspace_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanWorkspace asset that contains this asset (AnaplanModel and everything under its hierarchy).""" + + anaplan_model_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanModel asset that contains this asset (AnaplanModule and everything under its hierarchy).""" + + anaplan_model_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanModel asset that contains this asset (AnaplanModule and everything under its hierarchy).""" + + anaplan_module_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AnaplanModule asset that contains this asset (AnaplanLineItem, AnaplanList, AnaplanView and everything under their hierarchy).""" + + anaplan_module_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AnaplanModule asset that contains this asset (AnaplanLineItem, AnaplanList, AnaplanView and everything under their hierarchy).""" + + anaplan_source_id: Union[str, None, UnsetType] = UNSET + """Id/Guid of the Anaplan asset in the source system.""" + + +class AnaplanWorkspaceRelationshipAttributes(AssetRelationshipAttributes): + """AnaplanWorkspace-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anaplan_models: Union[List[RelatedAnaplanModel], None, UnsetType] = UNSET + """Individual models contained in the workspace.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class AnaplanWorkspaceNested(AssetNested): + """AnaplanWorkspace in nested API format for high-performance serialization.""" + + attributes: Union[AnaplanWorkspaceAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + AnaplanWorkspaceRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + AnaplanWorkspaceRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + AnaplanWorkspaceRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_ANAPLAN_WORKSPACE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anaplan_models", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_anaplan_workspace_attrs( + attrs: AnaplanWorkspaceAttributes, obj: AnaplanWorkspace +) -> None: + """Populate AnaplanWorkspace-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.anaplan_workspace_current_size = obj.anaplan_workspace_current_size + attrs.anaplan_workspace_allowance_size = obj.anaplan_workspace_allowance_size + attrs.anaplan_workspace_qualified_name = obj.anaplan_workspace_qualified_name + attrs.anaplan_workspace_name = obj.anaplan_workspace_name + attrs.anaplan_model_qualified_name = obj.anaplan_model_qualified_name + attrs.anaplan_model_name = obj.anaplan_model_name + attrs.anaplan_module_qualified_name = obj.anaplan_module_qualified_name + attrs.anaplan_module_name = obj.anaplan_module_name + attrs.anaplan_source_id = obj.anaplan_source_id + + +def _extract_anaplan_workspace_attrs(attrs: AnaplanWorkspaceAttributes) -> dict: + """Extract all AnaplanWorkspace attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["anaplan_workspace_current_size"] = attrs.anaplan_workspace_current_size + result["anaplan_workspace_allowance_size"] = attrs.anaplan_workspace_allowance_size + result["anaplan_workspace_qualified_name"] = attrs.anaplan_workspace_qualified_name + result["anaplan_workspace_name"] = attrs.anaplan_workspace_name + result["anaplan_model_qualified_name"] = attrs.anaplan_model_qualified_name + result["anaplan_model_name"] = attrs.anaplan_model_name + result["anaplan_module_qualified_name"] = attrs.anaplan_module_qualified_name + result["anaplan_module_name"] = attrs.anaplan_module_name + result["anaplan_source_id"] = attrs.anaplan_source_id + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _anaplan_workspace_to_nested( + anaplan_workspace: AnaplanWorkspace, +) -> AnaplanWorkspaceNested: + """Convert flat AnaplanWorkspace to nested format.""" + attrs = AnaplanWorkspaceAttributes() + _populate_anaplan_workspace_attrs(attrs, anaplan_workspace) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + anaplan_workspace, + _ANAPLAN_WORKSPACE_REL_FIELDS, + AnaplanWorkspaceRelationshipAttributes, + ) + return AnaplanWorkspaceNested( + guid=anaplan_workspace.guid, + type_name=anaplan_workspace.type_name, + status=anaplan_workspace.status, + version=anaplan_workspace.version, + create_time=anaplan_workspace.create_time, + update_time=anaplan_workspace.update_time, + created_by=anaplan_workspace.created_by, + updated_by=anaplan_workspace.updated_by, + classifications=anaplan_workspace.classifications, + classification_names=anaplan_workspace.classification_names, + meanings=anaplan_workspace.meanings, + labels=anaplan_workspace.labels, + business_attributes=anaplan_workspace.business_attributes, + custom_attributes=anaplan_workspace.custom_attributes, + pending_tasks=anaplan_workspace.pending_tasks, + proxy=anaplan_workspace.proxy, + is_incomplete=anaplan_workspace.is_incomplete, + provenance_type=anaplan_workspace.provenance_type, + home_id=anaplan_workspace.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _anaplan_workspace_from_nested(nested: AnaplanWorkspaceNested) -> AnaplanWorkspace: + """Convert nested format to flat AnaplanWorkspace.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else AnaplanWorkspaceAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _ANAPLAN_WORKSPACE_REL_FIELDS, + AnaplanWorkspaceRelationshipAttributes, + ) + return AnaplanWorkspace( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_anaplan_workspace_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _anaplan_workspace_to_nested_bytes( + anaplan_workspace: AnaplanWorkspace, serde: Serde +) -> bytes: + """Convert flat AnaplanWorkspace to nested JSON bytes.""" + return serde.encode(_anaplan_workspace_to_nested(anaplan_workspace)) + + +def _anaplan_workspace_from_nested_bytes(data: bytes, serde: Serde) -> AnaplanWorkspace: + """Convert nested JSON bytes to flat AnaplanWorkspace.""" + nested = serde.decode(data, AnaplanWorkspaceNested) + return _anaplan_workspace_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +AnaplanWorkspace.ANAPLAN_WORKSPACE_CURRENT_SIZE = NumericField( + "anaplanWorkspaceCurrentSize", "anaplanWorkspaceCurrentSize" +) +AnaplanWorkspace.ANAPLAN_WORKSPACE_ALLOWANCE_SIZE = NumericField( + "anaplanWorkspaceAllowanceSize", "anaplanWorkspaceAllowanceSize" +) +AnaplanWorkspace.ANAPLAN_WORKSPACE_QUALIFIED_NAME = KeywordField( + "anaplanWorkspaceQualifiedName", "anaplanWorkspaceQualifiedName" +) +AnaplanWorkspace.ANAPLAN_WORKSPACE_NAME = KeywordField( + "anaplanWorkspaceName", "anaplanWorkspaceName" +) +AnaplanWorkspace.ANAPLAN_MODEL_QUALIFIED_NAME = KeywordField( + "anaplanModelQualifiedName", "anaplanModelQualifiedName" +) +AnaplanWorkspace.ANAPLAN_MODEL_NAME = KeywordField( + "anaplanModelName", "anaplanModelName" +) +AnaplanWorkspace.ANAPLAN_MODULE_QUALIFIED_NAME = KeywordField( + "anaplanModuleQualifiedName", "anaplanModuleQualifiedName" +) +AnaplanWorkspace.ANAPLAN_MODULE_NAME = KeywordField( + "anaplanModuleName", "anaplanModuleName" +) +AnaplanWorkspace.ANAPLAN_SOURCE_ID = KeywordField("anaplanSourceId", "anaplanSourceId") +AnaplanWorkspace.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +AnaplanWorkspace.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +AnaplanWorkspace.ANAPLAN_MODELS = RelationField("anaplanModels") +AnaplanWorkspace.ANOMALO_CHECKS = RelationField("anomaloChecks") +AnaplanWorkspace.APPLICATION = RelationField("application") +AnaplanWorkspace.APPLICATION_FIELD = RelationField("applicationField") +AnaplanWorkspace.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +AnaplanWorkspace.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +AnaplanWorkspace.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +AnaplanWorkspace.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +AnaplanWorkspace.METRICS = RelationField("metrics") +AnaplanWorkspace.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +AnaplanWorkspace.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +AnaplanWorkspace.MEANINGS = RelationField("meanings") +AnaplanWorkspace.MC_MONITORS = RelationField("mcMonitors") +AnaplanWorkspace.MC_INCIDENTS = RelationField("mcIncidents") +AnaplanWorkspace.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +AnaplanWorkspace.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +AnaplanWorkspace.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +AnaplanWorkspace.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +AnaplanWorkspace.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +AnaplanWorkspace.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +AnaplanWorkspace.FILES = RelationField("files") +AnaplanWorkspace.LINKS = RelationField("links") +AnaplanWorkspace.README = RelationField("readme") +AnaplanWorkspace.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +AnaplanWorkspace.SODA_CHECKS = RelationField("sodaChecks") +AnaplanWorkspace.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +AnaplanWorkspace.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/anomalo.py b/pyatlan_v9/model/assets/anomalo.py new file mode 100644 index 000000000..6064bf4ed --- /dev/null +++ b/pyatlan_v9/model/assets/anomalo.py @@ -0,0 +1,538 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Anomalo asset model with flattened inheritance. + +This module provides: +- Anomalo: Flat asset class (easy to use) +- AnomaloAttributes: Nested attributes struct (extends AssetAttributes) +- AnomaloNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .anomalo_related import RelatedAnomaloCheck + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Anomalo(Asset): + """ + Base class for Anomalo assets. + """ + + DQ_IS_PART_OF_CONTRACT: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Anomalo" + + dq_is_part_of_contract: Union[bool, None, UnsetType] = UNSET + """Whether this data quality is part of contract (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Anomalo" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _anomalo_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Anomalo: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Anomalo instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _anomalo_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class AnomaloAttributes(AssetAttributes): + """Anomalo-specific attributes for nested API format.""" + + dq_is_part_of_contract: Union[bool, None, UnsetType] = UNSET + """Whether this data quality is part of contract (true) or not (false).""" + + +class AnomaloRelationshipAttributes(AssetRelationshipAttributes): + """Anomalo-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class AnomaloNested(AssetNested): + """Anomalo in nested API format for high-performance serialization.""" + + attributes: Union[AnomaloAttributes, UnsetType] = UNSET + relationship_attributes: Union[AnomaloRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[AnomaloRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[AnomaloRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_ANOMALO_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_anomalo_attrs(attrs: AnomaloAttributes, obj: Anomalo) -> None: + """Populate Anomalo-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.dq_is_part_of_contract = obj.dq_is_part_of_contract + + +def _extract_anomalo_attrs(attrs: AnomaloAttributes) -> dict: + """Extract all Anomalo attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["dq_is_part_of_contract"] = attrs.dq_is_part_of_contract + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _anomalo_to_nested(anomalo: Anomalo) -> AnomaloNested: + """Convert flat Anomalo to nested format.""" + attrs = AnomaloAttributes() + _populate_anomalo_attrs(attrs, anomalo) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + anomalo, _ANOMALO_REL_FIELDS, AnomaloRelationshipAttributes + ) + return AnomaloNested( + guid=anomalo.guid, + type_name=anomalo.type_name, + status=anomalo.status, + version=anomalo.version, + create_time=anomalo.create_time, + update_time=anomalo.update_time, + created_by=anomalo.created_by, + updated_by=anomalo.updated_by, + classifications=anomalo.classifications, + classification_names=anomalo.classification_names, + meanings=anomalo.meanings, + labels=anomalo.labels, + business_attributes=anomalo.business_attributes, + custom_attributes=anomalo.custom_attributes, + pending_tasks=anomalo.pending_tasks, + proxy=anomalo.proxy, + is_incomplete=anomalo.is_incomplete, + provenance_type=anomalo.provenance_type, + home_id=anomalo.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _anomalo_from_nested(nested: AnomaloNested) -> Anomalo: + """Convert nested format to flat Anomalo.""" + attrs = nested.attributes if nested.attributes is not UNSET else AnomaloAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _ANOMALO_REL_FIELDS, + AnomaloRelationshipAttributes, + ) + return Anomalo( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_anomalo_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _anomalo_to_nested_bytes(anomalo: Anomalo, serde: Serde) -> bytes: + """Convert flat Anomalo to nested JSON bytes.""" + return serde.encode(_anomalo_to_nested(anomalo)) + + +def _anomalo_from_nested_bytes(data: bytes, serde: Serde) -> Anomalo: + """Convert nested JSON bytes to flat Anomalo.""" + nested = serde.decode(data, AnomaloNested) + return _anomalo_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + RelationField, +) + +Anomalo.DQ_IS_PART_OF_CONTRACT = BooleanField( + "dqIsPartOfContract", "dqIsPartOfContract" +) +Anomalo.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Anomalo.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Anomalo.ANOMALO_CHECKS = RelationField("anomaloChecks") +Anomalo.APPLICATION = RelationField("application") +Anomalo.APPLICATION_FIELD = RelationField("applicationField") +Anomalo.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Anomalo.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Anomalo.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Anomalo.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Anomalo.METRICS = RelationField("metrics") +Anomalo.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Anomalo.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Anomalo.MEANINGS = RelationField("meanings") +Anomalo.MC_MONITORS = RelationField("mcMonitors") +Anomalo.MC_INCIDENTS = RelationField("mcIncidents") +Anomalo.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Anomalo.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Anomalo.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Anomalo.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Anomalo.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Anomalo.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Anomalo.FILES = RelationField("files") +Anomalo.LINKS = RelationField("links") +Anomalo.README = RelationField("readme") +Anomalo.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Anomalo.SODA_CHECKS = RelationField("sodaChecks") +Anomalo.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Anomalo.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/anomalo_check.py b/pyatlan_v9/model/assets/anomalo_check.py new file mode 100644 index 000000000..1e71877e6 --- /dev/null +++ b/pyatlan_v9/model/assets/anomalo_check.py @@ -0,0 +1,700 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +AnomaloCheck asset model with flattened inheritance. + +This module provides: +- AnomaloCheck: Flat asset class (easy to use) +- AnomaloCheckAttributes: Nested attributes struct (extends AssetAttributes) +- AnomaloCheckNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .asset_related import RelatedAsset +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .anomalo_related import RelatedAnomaloCheck + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class AnomaloCheck(Asset): + """ + Instance of a Anomalo Check in Atlan. + """ + + ANOMALO_CHECK_LINKED_ASSET_QUALIFIED_NAME: ClassVar[Any] = None + ANOMALO_CHECK_CATEGORY_TYPE: ClassVar[Any] = None + ANOMALO_CHECK_TYPE: ClassVar[Any] = None + ANOMALO_CHECK_PRIORITY_LEVEL: ClassVar[Any] = None + ANOMALO_CHECK_IS_SYSTEM_ADDED: ClassVar[Any] = None + ANOMALO_CHECK_STATUS: ClassVar[Any] = None + ANOMALO_CHECK_STATUS_IMAGE_URL: ClassVar[Any] = None + ANOMALO_CHECK_LAST_RUN_COMPLETED_AT: ClassVar[Any] = None + ANOMALO_CHECK_LAST_RUN_EVALUATED_MESSAGE: ClassVar[Any] = None + ANOMALO_CHECK_LAST_RUN_URL: ClassVar[Any] = None + ANOMALO_CHECK_HISTORIC_RUN_STATUS: ClassVar[Any] = None + DQ_IS_PART_OF_CONTRACT: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECK_ASSET: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "AnomaloCheck" + + anomalo_check_linked_asset_qualified_name: Union[str, None, UnsetType] = UNSET + """QualifiedName of the asset associated with the check""" + + anomalo_check_category_type: Union[str, None, UnsetType] = UNSET + """Category type of the check in Anomalo""" + + anomalo_check_type: Union[str, None, UnsetType] = UNSET + """Type of check in Anomalo""" + + anomalo_check_priority_level: Union[str, None, UnsetType] = UNSET + """Priority level of the check in Anomalo""" + + anomalo_check_is_system_added: Union[bool, None, UnsetType] = UNSET + """Flag to indicate if the check is an out of the box available check""" + + anomalo_check_status: Union[str, None, UnsetType] = UNSET + """Status of the check in Anomalo""" + + anomalo_check_status_image_url: Union[str, None, UnsetType] = UNSET + """Image URL for the status of the check in Anomalo""" + + anomalo_check_last_run_completed_at: Union[int, None, UnsetType] = UNSET + """Timestamp when the check was last run""" + + anomalo_check_last_run_evaluated_message: Union[str, None, UnsetType] = UNSET + """Evaluated message of the latest check run.""" + + anomalo_check_last_run_url: Union[str, None, UnsetType] = UNSET + """URL to the latest check run.""" + + anomalo_check_historic_run_status: Union[str, None, UnsetType] = UNSET + """Historic run status of the check in Anomalo""" + + dq_is_part_of_contract: Union[bool, None, UnsetType] = UNSET + """Whether this data quality is part of contract (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_check_asset: Union[RelatedAsset, None, UnsetType] = UNSET + """The asset this Check is linked to.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "AnomaloCheck" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _anomalo_check_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> AnomaloCheck: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + AnomaloCheck instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _anomalo_check_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class AnomaloCheckAttributes(AssetAttributes): + """AnomaloCheck-specific attributes for nested API format.""" + + anomalo_check_linked_asset_qualified_name: Union[str, None, UnsetType] = UNSET + """QualifiedName of the asset associated with the check""" + + anomalo_check_category_type: Union[str, None, UnsetType] = UNSET + """Category type of the check in Anomalo""" + + anomalo_check_type: Union[str, None, UnsetType] = UNSET + """Type of check in Anomalo""" + + anomalo_check_priority_level: Union[str, None, UnsetType] = UNSET + """Priority level of the check in Anomalo""" + + anomalo_check_is_system_added: Union[bool, None, UnsetType] = UNSET + """Flag to indicate if the check is an out of the box available check""" + + anomalo_check_status: Union[str, None, UnsetType] = UNSET + """Status of the check in Anomalo""" + + anomalo_check_status_image_url: Union[str, None, UnsetType] = UNSET + """Image URL for the status of the check in Anomalo""" + + anomalo_check_last_run_completed_at: Union[int, None, UnsetType] = UNSET + """Timestamp when the check was last run""" + + anomalo_check_last_run_evaluated_message: Union[str, None, UnsetType] = UNSET + """Evaluated message of the latest check run.""" + + anomalo_check_last_run_url: Union[str, None, UnsetType] = UNSET + """URL to the latest check run.""" + + anomalo_check_historic_run_status: Union[str, None, UnsetType] = UNSET + """Historic run status of the check in Anomalo""" + + dq_is_part_of_contract: Union[bool, None, UnsetType] = UNSET + """Whether this data quality is part of contract (true) or not (false).""" + + +class AnomaloCheckRelationshipAttributes(AssetRelationshipAttributes): + """AnomaloCheck-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_check_asset: Union[RelatedAsset, None, UnsetType] = UNSET + """The asset this Check is linked to.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class AnomaloCheckNested(AssetNested): + """AnomaloCheck in nested API format for high-performance serialization.""" + + attributes: Union[AnomaloCheckAttributes, UnsetType] = UNSET + relationship_attributes: Union[AnomaloCheckRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + AnomaloCheckRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + AnomaloCheckRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_ANOMALO_CHECK_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_check_asset", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_anomalo_check_attrs( + attrs: AnomaloCheckAttributes, obj: AnomaloCheck +) -> None: + """Populate AnomaloCheck-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.anomalo_check_linked_asset_qualified_name = ( + obj.anomalo_check_linked_asset_qualified_name + ) + attrs.anomalo_check_category_type = obj.anomalo_check_category_type + attrs.anomalo_check_type = obj.anomalo_check_type + attrs.anomalo_check_priority_level = obj.anomalo_check_priority_level + attrs.anomalo_check_is_system_added = obj.anomalo_check_is_system_added + attrs.anomalo_check_status = obj.anomalo_check_status + attrs.anomalo_check_status_image_url = obj.anomalo_check_status_image_url + attrs.anomalo_check_last_run_completed_at = obj.anomalo_check_last_run_completed_at + attrs.anomalo_check_last_run_evaluated_message = ( + obj.anomalo_check_last_run_evaluated_message + ) + attrs.anomalo_check_last_run_url = obj.anomalo_check_last_run_url + attrs.anomalo_check_historic_run_status = obj.anomalo_check_historic_run_status + attrs.dq_is_part_of_contract = obj.dq_is_part_of_contract + + +def _extract_anomalo_check_attrs(attrs: AnomaloCheckAttributes) -> dict: + """Extract all AnomaloCheck attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["anomalo_check_linked_asset_qualified_name"] = ( + attrs.anomalo_check_linked_asset_qualified_name + ) + result["anomalo_check_category_type"] = attrs.anomalo_check_category_type + result["anomalo_check_type"] = attrs.anomalo_check_type + result["anomalo_check_priority_level"] = attrs.anomalo_check_priority_level + result["anomalo_check_is_system_added"] = attrs.anomalo_check_is_system_added + result["anomalo_check_status"] = attrs.anomalo_check_status + result["anomalo_check_status_image_url"] = attrs.anomalo_check_status_image_url + result["anomalo_check_last_run_completed_at"] = ( + attrs.anomalo_check_last_run_completed_at + ) + result["anomalo_check_last_run_evaluated_message"] = ( + attrs.anomalo_check_last_run_evaluated_message + ) + result["anomalo_check_last_run_url"] = attrs.anomalo_check_last_run_url + result["anomalo_check_historic_run_status"] = ( + attrs.anomalo_check_historic_run_status + ) + result["dq_is_part_of_contract"] = attrs.dq_is_part_of_contract + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _anomalo_check_to_nested(anomalo_check: AnomaloCheck) -> AnomaloCheckNested: + """Convert flat AnomaloCheck to nested format.""" + attrs = AnomaloCheckAttributes() + _populate_anomalo_check_attrs(attrs, anomalo_check) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + anomalo_check, _ANOMALO_CHECK_REL_FIELDS, AnomaloCheckRelationshipAttributes + ) + return AnomaloCheckNested( + guid=anomalo_check.guid, + type_name=anomalo_check.type_name, + status=anomalo_check.status, + version=anomalo_check.version, + create_time=anomalo_check.create_time, + update_time=anomalo_check.update_time, + created_by=anomalo_check.created_by, + updated_by=anomalo_check.updated_by, + classifications=anomalo_check.classifications, + classification_names=anomalo_check.classification_names, + meanings=anomalo_check.meanings, + labels=anomalo_check.labels, + business_attributes=anomalo_check.business_attributes, + custom_attributes=anomalo_check.custom_attributes, + pending_tasks=anomalo_check.pending_tasks, + proxy=anomalo_check.proxy, + is_incomplete=anomalo_check.is_incomplete, + provenance_type=anomalo_check.provenance_type, + home_id=anomalo_check.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _anomalo_check_from_nested(nested: AnomaloCheckNested) -> AnomaloCheck: + """Convert nested format to flat AnomaloCheck.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else AnomaloCheckAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _ANOMALO_CHECK_REL_FIELDS, + AnomaloCheckRelationshipAttributes, + ) + return AnomaloCheck( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_anomalo_check_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _anomalo_check_to_nested_bytes(anomalo_check: AnomaloCheck, serde: Serde) -> bytes: + """Convert flat AnomaloCheck to nested JSON bytes.""" + return serde.encode(_anomalo_check_to_nested(anomalo_check)) + + +def _anomalo_check_from_nested_bytes(data: bytes, serde: Serde) -> AnomaloCheck: + """Convert nested JSON bytes to flat AnomaloCheck.""" + nested = serde.decode(data, AnomaloCheckNested) + return _anomalo_check_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, +) + +AnomaloCheck.ANOMALO_CHECK_LINKED_ASSET_QUALIFIED_NAME = KeywordField( + "anomaloCheckLinkedAssetQualifiedName", "anomaloCheckLinkedAssetQualifiedName" +) +AnomaloCheck.ANOMALO_CHECK_CATEGORY_TYPE = KeywordField( + "anomaloCheckCategoryType", "anomaloCheckCategoryType" +) +AnomaloCheck.ANOMALO_CHECK_TYPE = KeywordField("anomaloCheckType", "anomaloCheckType") +AnomaloCheck.ANOMALO_CHECK_PRIORITY_LEVEL = KeywordField( + "anomaloCheckPriorityLevel", "anomaloCheckPriorityLevel" +) +AnomaloCheck.ANOMALO_CHECK_IS_SYSTEM_ADDED = BooleanField( + "anomaloCheckIsSystemAdded", "anomaloCheckIsSystemAdded" +) +AnomaloCheck.ANOMALO_CHECK_STATUS = KeywordField( + "anomaloCheckStatus", "anomaloCheckStatus" +) +AnomaloCheck.ANOMALO_CHECK_STATUS_IMAGE_URL = KeywordField( + "anomaloCheckStatusImageUrl", "anomaloCheckStatusImageUrl" +) +AnomaloCheck.ANOMALO_CHECK_LAST_RUN_COMPLETED_AT = NumericField( + "anomaloCheckLastRunCompletedAt", "anomaloCheckLastRunCompletedAt" +) +AnomaloCheck.ANOMALO_CHECK_LAST_RUN_EVALUATED_MESSAGE = KeywordField( + "anomaloCheckLastRunEvaluatedMessage", "anomaloCheckLastRunEvaluatedMessage" +) +AnomaloCheck.ANOMALO_CHECK_LAST_RUN_URL = KeywordField( + "anomaloCheckLastRunUrl", "anomaloCheckLastRunUrl" +) +AnomaloCheck.ANOMALO_CHECK_HISTORIC_RUN_STATUS = KeywordField( + "anomaloCheckHistoricRunStatus", "anomaloCheckHistoricRunStatus" +) +AnomaloCheck.DQ_IS_PART_OF_CONTRACT = BooleanField( + "dqIsPartOfContract", "dqIsPartOfContract" +) +AnomaloCheck.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +AnomaloCheck.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +AnomaloCheck.ANOMALO_CHECK_ASSET = RelationField("anomaloCheckAsset") +AnomaloCheck.ANOMALO_CHECKS = RelationField("anomaloChecks") +AnomaloCheck.APPLICATION = RelationField("application") +AnomaloCheck.APPLICATION_FIELD = RelationField("applicationField") +AnomaloCheck.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +AnomaloCheck.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +AnomaloCheck.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +AnomaloCheck.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +AnomaloCheck.METRICS = RelationField("metrics") +AnomaloCheck.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +AnomaloCheck.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +AnomaloCheck.MEANINGS = RelationField("meanings") +AnomaloCheck.MC_MONITORS = RelationField("mcMonitors") +AnomaloCheck.MC_INCIDENTS = RelationField("mcIncidents") +AnomaloCheck.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +AnomaloCheck.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +AnomaloCheck.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +AnomaloCheck.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +AnomaloCheck.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +AnomaloCheck.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +AnomaloCheck.FILES = RelationField("files") +AnomaloCheck.LINKS = RelationField("links") +AnomaloCheck.README = RelationField("readme") +AnomaloCheck.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +AnomaloCheck.SODA_CHECKS = RelationField("sodaChecks") +AnomaloCheck.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +AnomaloCheck.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/anomalo_related.py b/pyatlan_v9/model/assets/anomalo_related.py new file mode 100644 index 000000000..64c71d166 --- /dev/null +++ b/pyatlan_v9/model/assets/anomalo_related.py @@ -0,0 +1,87 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Anomalo module. + +This module contains all Related{Type} classes for the Anomalo type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Union + +from msgspec import UNSET, UnsetType + +from .data_quality_related import RelatedDataQuality +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedAnomalo", + "RelatedAnomaloCheck", +] + + +class RelatedAnomalo(RelatedDataQuality): + """ + Related entity reference for Anomalo assets. + + Extends RelatedDataQuality with Anomalo-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Anomalo" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Anomalo" + + +class RelatedAnomaloCheck(RelatedAnomalo): + """ + Related entity reference for AnomaloCheck assets. + + Extends RelatedAnomalo with AnomaloCheck-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "AnomaloCheck" so it serializes correctly + + anomalo_check_linked_asset_qualified_name: Union[str, None, UnsetType] = UNSET + """QualifiedName of the asset associated with the check""" + + anomalo_check_category_type: Union[str, None, UnsetType] = UNSET + """Category type of the check in Anomalo""" + + anomalo_check_type: Union[str, None, UnsetType] = UNSET + """Type of check in Anomalo""" + + anomalo_check_priority_level: Union[str, None, UnsetType] = UNSET + """Priority level of the check in Anomalo""" + + anomalo_check_is_system_added: Union[bool, None, UnsetType] = UNSET + """Flag to indicate if the check is an out of the box available check""" + + anomalo_check_status: Union[str, None, UnsetType] = UNSET + """Status of the check in Anomalo""" + + anomalo_check_status_image_url: Union[str, None, UnsetType] = UNSET + """Image URL for the status of the check in Anomalo""" + + anomalo_check_last_run_completed_at: Union[int, None, UnsetType] = UNSET + """Timestamp when the check was last run""" + + anomalo_check_last_run_evaluated_message: Union[str, None, UnsetType] = UNSET + """Evaluated message of the latest check run.""" + + anomalo_check_last_run_url: Union[str, None, UnsetType] = UNSET + """URL to the latest check run.""" + + anomalo_check_historic_run_status: Union[str, None, UnsetType] = UNSET + """Historic run status of the check in Anomalo""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "AnomaloCheck" diff --git a/pyatlan_v9/model/assets/api.py b/pyatlan_v9/model/assets/api.py new file mode 100644 index 000000000..18a0974e7 --- /dev/null +++ b/pyatlan_v9/model/assets/api.py @@ -0,0 +1,609 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +API asset model with flattened inheritance. + +This module provides: +- API: Flat asset class (easy to use) +- APIAttributes: Nested attributes struct (extends AssetAttributes) +- APINested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class API(Asset): + """ + Base class for API assets. + """ + + API_SPEC_TYPE: ClassVar[Any] = None + API_SPEC_VERSION: ClassVar[Any] = None + API_SPEC_NAME: ClassVar[Any] = None + API_SPEC_QUALIFIED_NAME: ClassVar[Any] = None + API_EXTERNAL_DOCS: ClassVar[Any] = None + API_IS_AUTH_OPTIONAL: ClassVar[Any] = None + API_IS_OBJECT_REFERENCE: ClassVar[Any] = None + API_OBJECT_QUALIFIED_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "API" + + api_spec_type: Union[str, None, UnsetType] = UNSET + """Type of API, for example: OpenAPI, GraphQL, etc.""" + + api_spec_version: Union[str, None, UnsetType] = UNSET + """Version of the API specification.""" + + api_spec_name: Union[str, None, UnsetType] = UNSET + """Simple name of the API spec, if this asset is contained in an API spec.""" + + api_spec_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the API spec, if this asset is contained in an API spec.""" + + api_external_docs: Union[Dict[str, str], None, UnsetType] = UNSET + """External documentation of the API.""" + + api_is_auth_optional: Union[bool, None, UnsetType] = UNSET + """Whether authentication is optional (true) or required (false).""" + + api_is_object_reference: Union[bool, None, UnsetType] = UNSET + """If this asset refers to an APIObject""" + + api_object_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the APIObject that is referred to by this asset. When apiIsObjectReference is true.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "API" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _api_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> API: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + API instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _api_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class APIAttributes(AssetAttributes): + """API-specific attributes for nested API format.""" + + api_spec_type: Union[str, None, UnsetType] = UNSET + """Type of API, for example: OpenAPI, GraphQL, etc.""" + + api_spec_version: Union[str, None, UnsetType] = UNSET + """Version of the API specification.""" + + api_spec_name: Union[str, None, UnsetType] = UNSET + """Simple name of the API spec, if this asset is contained in an API spec.""" + + api_spec_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the API spec, if this asset is contained in an API spec.""" + + api_external_docs: Union[Dict[str, str], None, UnsetType] = UNSET + """External documentation of the API.""" + + api_is_auth_optional: Union[bool, None, UnsetType] = UNSET + """Whether authentication is optional (true) or required (false).""" + + api_is_object_reference: Union[bool, None, UnsetType] = UNSET + """If this asset refers to an APIObject""" + + api_object_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the APIObject that is referred to by this asset. When apiIsObjectReference is true.""" + + +class APIRelationshipAttributes(AssetRelationshipAttributes): + """API-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class APINested(AssetNested): + """API in nested API format for high-performance serialization.""" + + attributes: Union[APIAttributes, UnsetType] = UNSET + relationship_attributes: Union[APIRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[APIRelationshipAttributes, UnsetType] = UNSET + remove_relationship_attributes: Union[APIRelationshipAttributes, UnsetType] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_API_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_api_attrs(attrs: APIAttributes, obj: API) -> None: + """Populate API-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.api_spec_type = obj.api_spec_type + attrs.api_spec_version = obj.api_spec_version + attrs.api_spec_name = obj.api_spec_name + attrs.api_spec_qualified_name = obj.api_spec_qualified_name + attrs.api_external_docs = obj.api_external_docs + attrs.api_is_auth_optional = obj.api_is_auth_optional + attrs.api_is_object_reference = obj.api_is_object_reference + attrs.api_object_qualified_name = obj.api_object_qualified_name + + +def _extract_api_attrs(attrs: APIAttributes) -> dict: + """Extract all API attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["api_spec_type"] = attrs.api_spec_type + result["api_spec_version"] = attrs.api_spec_version + result["api_spec_name"] = attrs.api_spec_name + result["api_spec_qualified_name"] = attrs.api_spec_qualified_name + result["api_external_docs"] = attrs.api_external_docs + result["api_is_auth_optional"] = attrs.api_is_auth_optional + result["api_is_object_reference"] = attrs.api_is_object_reference + result["api_object_qualified_name"] = attrs.api_object_qualified_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _api_to_nested(api: API) -> APINested: + """Convert flat API to nested format.""" + attrs = APIAttributes() + _populate_api_attrs(attrs, api) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + api, _API_REL_FIELDS, APIRelationshipAttributes + ) + return APINested( + guid=api.guid, + type_name=api.type_name, + status=api.status, + version=api.version, + create_time=api.create_time, + update_time=api.update_time, + created_by=api.created_by, + updated_by=api.updated_by, + classifications=api.classifications, + classification_names=api.classification_names, + meanings=api.meanings, + labels=api.labels, + business_attributes=api.business_attributes, + custom_attributes=api.custom_attributes, + pending_tasks=api.pending_tasks, + proxy=api.proxy, + is_incomplete=api.is_incomplete, + provenance_type=api.provenance_type, + home_id=api.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _api_from_nested(nested: APINested) -> API: + """Convert nested format to flat API.""" + attrs = nested.attributes if nested.attributes is not UNSET else APIAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _API_REL_FIELDS, + APIRelationshipAttributes, + ) + return API( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_api_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _api_to_nested_bytes(api: API, serde: Serde) -> bytes: + """Convert flat API to nested JSON bytes.""" + return serde.encode(_api_to_nested(api)) + + +def _api_from_nested_bytes(data: bytes, serde: Serde) -> API: + """Convert nested JSON bytes to flat API.""" + nested = serde.decode(data, APINested) + return _api_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + RelationField, +) + +API.API_SPEC_TYPE = KeywordField("apiSpecType", "apiSpecType") +API.API_SPEC_VERSION = KeywordField("apiSpecVersion", "apiSpecVersion") +API.API_SPEC_NAME = KeywordField("apiSpecName", "apiSpecName") +API.API_SPEC_QUALIFIED_NAME = KeywordTextField( + "apiSpecQualifiedName", "apiSpecQualifiedName", "apiSpecQualifiedName.text" +) +API.API_EXTERNAL_DOCS = KeywordField("apiExternalDocs", "apiExternalDocs") +API.API_IS_AUTH_OPTIONAL = BooleanField("apiIsAuthOptional", "apiIsAuthOptional") +API.API_IS_OBJECT_REFERENCE = BooleanField( + "apiIsObjectReference", "apiIsObjectReference" +) +API.API_OBJECT_QUALIFIED_NAME = KeywordField( + "apiObjectQualifiedName", "apiObjectQualifiedName" +) +API.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +API.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +API.ANOMALO_CHECKS = RelationField("anomaloChecks") +API.APPLICATION = RelationField("application") +API.APPLICATION_FIELD = RelationField("applicationField") +API.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +API.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +API.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +API.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +API.METRICS = RelationField("metrics") +API.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +API.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +API.MEANINGS = RelationField("meanings") +API.MC_MONITORS = RelationField("mcMonitors") +API.MC_INCIDENTS = RelationField("mcIncidents") +API.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +API.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +API.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +API.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +API.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +API.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +API.FILES = RelationField("files") +API.LINKS = RelationField("links") +API.README = RelationField("readme") +API.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +API.SODA_CHECKS = RelationField("sodaChecks") +API.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +API.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/api_field.py b/pyatlan_v9/model/assets/api_field.py new file mode 100644 index 000000000..a48701cc9 --- /dev/null +++ b/pyatlan_v9/model/assets/api_field.py @@ -0,0 +1,798 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +APIField asset model with flattened inheritance. + +This module provides: +- APIField: Flat asset class (easy to use) +- APIFieldAttributes: Nested attributes struct (extends AssetAttributes) +- APIFieldNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .api_related import RelatedAPIObject, RelatedAPIQuery + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class APIField(Asset): + """ + Instances of APIField in Atlan. + """ + + API_FIELD_TYPE: ClassVar[Any] = None + API_FIELD_TYPE_SECONDARY: ClassVar[Any] = None + API_QUERY_PARAM_TYPE: ClassVar[Any] = None + API_SPEC_TYPE: ClassVar[Any] = None + API_SPEC_VERSION: ClassVar[Any] = None + API_SPEC_NAME: ClassVar[Any] = None + API_SPEC_QUALIFIED_NAME: ClassVar[Any] = None + API_EXTERNAL_DOCS: ClassVar[Any] = None + API_IS_AUTH_OPTIONAL: ClassVar[Any] = None + API_IS_OBJECT_REFERENCE: ClassVar[Any] = None + API_OBJECT_QUALIFIED_NAME: ClassVar[Any] = None + API_OBJECT: ClassVar[Any] = None + API_QUERY: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "APIField" + + api_field_type: Union[str, None, UnsetType] = UNSET + """Type of APIField, as free text (e.g. STRING, NUMBER etc).""" + + api_field_type_secondary: Union[str, None, UnsetType] = UNSET + """Secondary type of APIField (e.g. LIST/STRING, then LIST would be the secondary type).""" + + api_query_param_type: Union[str, None, UnsetType] = UNSET + """If parent relationship type is APIQuery, then this attribute denotes if this is input or output parameter.""" + + api_spec_type: Union[str, None, UnsetType] = UNSET + """Type of API, for example: OpenAPI, GraphQL, etc.""" + + api_spec_version: Union[str, None, UnsetType] = UNSET + """Version of the API specification.""" + + api_spec_name: Union[str, None, UnsetType] = UNSET + """Simple name of the API spec, if this asset is contained in an API spec.""" + + api_spec_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the API spec, if this asset is contained in an API spec.""" + + api_external_docs: Union[Dict[str, str], None, UnsetType] = UNSET + """External documentation of the API.""" + + api_is_auth_optional: Union[bool, None, UnsetType] = UNSET + """Whether authentication is optional (true) or required (false).""" + + api_is_object_reference: Union[bool, None, UnsetType] = UNSET + """If this asset refers to an APIObject""" + + api_object_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the APIObject that is referred to by this asset. When apiIsObjectReference is true.""" + + api_object: Union[RelatedAPIObject, None, UnsetType] = UNSET + """APIObject asset containing this APIField.""" + + api_query: Union[RelatedAPIQuery, None, UnsetType] = UNSET + """APIQuery asset containing this APIField.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "APIField" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + parent_api_object_qualified_name: Union[str, None] = None, + parent_api_query_qualified_name: Union[str, None] = None, + connection_qualified_name: Union[str, None] = None, + api_field_type: Union[str, None] = None, + api_field_type_secondary: Union[str, None] = None, + is_api_object_reference: bool = False, + reference_api_object_qualified_name: Union[str, None] = None, + api_query_param_type: Union[str, None] = None, + ) -> "APIField": + """Create a new APIField asset.""" + validate_required_fields(["name"], [name]) + if parent_api_object_qualified_name is None or ( + isinstance(parent_api_object_qualified_name, str) + and not parent_api_object_qualified_name.strip() + ): + if parent_api_query_qualified_name is None or ( + isinstance(parent_api_query_qualified_name, str) + and not parent_api_query_qualified_name.strip() + ): + raise ValueError( + "Either parent_api_object_qualified_name or parent_api_query_qualified_name requires a valid value" + ) + elif ( + isinstance(parent_api_query_qualified_name, str) + and parent_api_query_qualified_name.strip() + ): + raise ValueError( + "Both parent_api_object_qualified_name and parent_api_query_qualified_name cannot be valid" + ) + + if is_api_object_reference: + if not reference_api_object_qualified_name or ( + isinstance(reference_api_object_qualified_name, str) + and not reference_api_object_qualified_name.strip() + ): + raise ValueError( + "Set valid qualified name for reference_api_object_qualified_name" + ) + elif ( + reference_api_object_qualified_name + and isinstance(reference_api_object_qualified_name, str) + and reference_api_object_qualified_name.strip() + ): + raise ValueError( + "Set is_api_object_reference to true to set reference_api_object_qualified_name" + ) + + if connection_qualified_name: + connection_qn = connection_qualified_name + elif parent_api_object_qualified_name: + parts = parent_api_object_qualified_name.split("/") + connection_qn = ( + "/".join(parts[:3]) + if len(parts) >= 3 + else parent_api_object_qualified_name + ) + else: + parts = (parent_api_query_qualified_name or "").split("/") + connection_qn = ( + "/".join(parts[:3]) + if len(parts) >= 3 + else parent_api_query_qualified_name + ) + + conn_parts = (connection_qn or "").split("/") + connector_name = conn_parts[1] if len(conn_parts) > 1 else None + + if parent_api_object_qualified_name: + return cls( + name=name, + qualified_name=f"{parent_api_object_qualified_name}/{name}", + connection_qualified_name=connection_qn, + connector_name=connector_name, + api_field_type=api_field_type, + api_field_type_secondary=api_field_type_secondary, + api_is_object_reference=is_api_object_reference, + api_object_qualified_name=( + reference_api_object_qualified_name + if is_api_object_reference + else None + ), + api_object=RelatedAPIObject( + qualified_name=parent_api_object_qualified_name, + unique_attributes={ + "qualifiedName": parent_api_object_qualified_name + }, + ), + api_query_param_type=api_query_param_type, + ) + return cls( + name=name, + qualified_name=f"{parent_api_query_qualified_name}/{name}", + connection_qualified_name=connection_qn, + connector_name=connector_name, + api_field_type=api_field_type, + api_field_type_secondary=api_field_type_secondary, + api_is_object_reference=is_api_object_reference, + api_object_qualified_name=( + reference_api_object_qualified_name if is_api_object_reference else None + ), + api_query=RelatedAPIQuery( + qualified_name=parent_api_query_qualified_name, + unique_attributes={"qualifiedName": parent_api_query_qualified_name}, + ), + api_query_param_type=api_query_param_type, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "APIField": + """Create an APIField instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "APIField": + """Return only fields required for update operations.""" + return APIField.updater(qualified_name=self.qualified_name, name=self.name) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _api_field_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> APIField: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + APIField instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _api_field_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class APIFieldAttributes(AssetAttributes): + """APIField-specific attributes for nested API format.""" + + api_field_type: Union[str, None, UnsetType] = UNSET + """Type of APIField, as free text (e.g. STRING, NUMBER etc).""" + + api_field_type_secondary: Union[str, None, UnsetType] = UNSET + """Secondary type of APIField (e.g. LIST/STRING, then LIST would be the secondary type).""" + + api_query_param_type: Union[str, None, UnsetType] = UNSET + """If parent relationship type is APIQuery, then this attribute denotes if this is input or output parameter.""" + + api_spec_type: Union[str, None, UnsetType] = UNSET + """Type of API, for example: OpenAPI, GraphQL, etc.""" + + api_spec_version: Union[str, None, UnsetType] = UNSET + """Version of the API specification.""" + + api_spec_name: Union[str, None, UnsetType] = UNSET + """Simple name of the API spec, if this asset is contained in an API spec.""" + + api_spec_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the API spec, if this asset is contained in an API spec.""" + + api_external_docs: Union[Dict[str, str], None, UnsetType] = UNSET + """External documentation of the API.""" + + api_is_auth_optional: Union[bool, None, UnsetType] = UNSET + """Whether authentication is optional (true) or required (false).""" + + api_is_object_reference: Union[bool, None, UnsetType] = UNSET + """If this asset refers to an APIObject""" + + api_object_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the APIObject that is referred to by this asset. When apiIsObjectReference is true.""" + + +class APIFieldRelationshipAttributes(AssetRelationshipAttributes): + """APIField-specific relationship attributes for nested API format.""" + + api_object: Union[RelatedAPIObject, None, UnsetType] = UNSET + """APIObject asset containing this APIField.""" + + api_query: Union[RelatedAPIQuery, None, UnsetType] = UNSET + """APIQuery asset containing this APIField.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class APIFieldNested(AssetNested): + """APIField in nested API format for high-performance serialization.""" + + attributes: Union[APIFieldAttributes, UnsetType] = UNSET + relationship_attributes: Union[APIFieldRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[APIFieldRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[APIFieldRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_API_FIELD_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "api_object", + "api_query", + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_api_field_attrs(attrs: APIFieldAttributes, obj: APIField) -> None: + """Populate APIField-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.api_field_type = obj.api_field_type + attrs.api_field_type_secondary = obj.api_field_type_secondary + attrs.api_query_param_type = obj.api_query_param_type + attrs.api_spec_type = obj.api_spec_type + attrs.api_spec_version = obj.api_spec_version + attrs.api_spec_name = obj.api_spec_name + attrs.api_spec_qualified_name = obj.api_spec_qualified_name + attrs.api_external_docs = obj.api_external_docs + attrs.api_is_auth_optional = obj.api_is_auth_optional + attrs.api_is_object_reference = obj.api_is_object_reference + attrs.api_object_qualified_name = obj.api_object_qualified_name + + +def _extract_api_field_attrs(attrs: APIFieldAttributes) -> dict: + """Extract all APIField attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["api_field_type"] = attrs.api_field_type + result["api_field_type_secondary"] = attrs.api_field_type_secondary + result["api_query_param_type"] = attrs.api_query_param_type + result["api_spec_type"] = attrs.api_spec_type + result["api_spec_version"] = attrs.api_spec_version + result["api_spec_name"] = attrs.api_spec_name + result["api_spec_qualified_name"] = attrs.api_spec_qualified_name + result["api_external_docs"] = attrs.api_external_docs + result["api_is_auth_optional"] = attrs.api_is_auth_optional + result["api_is_object_reference"] = attrs.api_is_object_reference + result["api_object_qualified_name"] = attrs.api_object_qualified_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _api_field_to_nested(api_field: APIField) -> APIFieldNested: + """Convert flat APIField to nested format.""" + attrs = APIFieldAttributes() + _populate_api_field_attrs(attrs, api_field) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + api_field, _API_FIELD_REL_FIELDS, APIFieldRelationshipAttributes + ) + return APIFieldNested( + guid=api_field.guid, + type_name=api_field.type_name, + status=api_field.status, + version=api_field.version, + create_time=api_field.create_time, + update_time=api_field.update_time, + created_by=api_field.created_by, + updated_by=api_field.updated_by, + classifications=api_field.classifications, + classification_names=api_field.classification_names, + meanings=api_field.meanings, + labels=api_field.labels, + business_attributes=api_field.business_attributes, + custom_attributes=api_field.custom_attributes, + pending_tasks=api_field.pending_tasks, + proxy=api_field.proxy, + is_incomplete=api_field.is_incomplete, + provenance_type=api_field.provenance_type, + home_id=api_field.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _api_field_from_nested(nested: APIFieldNested) -> APIField: + """Convert nested format to flat APIField.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else APIFieldAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _API_FIELD_REL_FIELDS, + APIFieldRelationshipAttributes, + ) + return APIField( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_api_field_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _api_field_to_nested_bytes(api_field: APIField, serde: Serde) -> bytes: + """Convert flat APIField to nested JSON bytes.""" + return serde.encode(_api_field_to_nested(api_field)) + + +def _api_field_from_nested_bytes(data: bytes, serde: Serde) -> APIField: + """Convert nested JSON bytes to flat APIField.""" + nested = serde.decode(data, APIFieldNested) + return _api_field_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + RelationField, +) + +APIField.API_FIELD_TYPE = KeywordField("apiFieldType", "apiFieldType") +APIField.API_FIELD_TYPE_SECONDARY = KeywordField( + "apiFieldTypeSecondary", "apiFieldTypeSecondary" +) +APIField.API_QUERY_PARAM_TYPE = KeywordField("apiQueryParamType", "apiQueryParamType") +APIField.API_SPEC_TYPE = KeywordField("apiSpecType", "apiSpecType") +APIField.API_SPEC_VERSION = KeywordField("apiSpecVersion", "apiSpecVersion") +APIField.API_SPEC_NAME = KeywordField("apiSpecName", "apiSpecName") +APIField.API_SPEC_QUALIFIED_NAME = KeywordTextField( + "apiSpecQualifiedName", "apiSpecQualifiedName", "apiSpecQualifiedName.text" +) +APIField.API_EXTERNAL_DOCS = KeywordField("apiExternalDocs", "apiExternalDocs") +APIField.API_IS_AUTH_OPTIONAL = BooleanField("apiIsAuthOptional", "apiIsAuthOptional") +APIField.API_IS_OBJECT_REFERENCE = BooleanField( + "apiIsObjectReference", "apiIsObjectReference" +) +APIField.API_OBJECT_QUALIFIED_NAME = KeywordField( + "apiObjectQualifiedName", "apiObjectQualifiedName" +) +APIField.API_OBJECT = RelationField("apiObject") +APIField.API_QUERY = RelationField("apiQuery") +APIField.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +APIField.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +APIField.ANOMALO_CHECKS = RelationField("anomaloChecks") +APIField.APPLICATION = RelationField("application") +APIField.APPLICATION_FIELD = RelationField("applicationField") +APIField.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +APIField.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +APIField.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +APIField.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +APIField.METRICS = RelationField("metrics") +APIField.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +APIField.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +APIField.MEANINGS = RelationField("meanings") +APIField.MC_MONITORS = RelationField("mcMonitors") +APIField.MC_INCIDENTS = RelationField("mcIncidents") +APIField.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +APIField.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +APIField.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +APIField.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +APIField.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +APIField.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +APIField.FILES = RelationField("files") +APIField.LINKS = RelationField("links") +APIField.README = RelationField("readme") +APIField.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +APIField.SODA_CHECKS = RelationField("sodaChecks") +APIField.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +APIField.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/api_object.py b/pyatlan_v9/model/assets/api_object.py new file mode 100644 index 000000000..7b9fb4e62 --- /dev/null +++ b/pyatlan_v9/model/assets/api_object.py @@ -0,0 +1,671 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +APIObject asset model with flattened inheritance. + +This module provides: +- APIObject: Flat asset class (easy to use) +- APIObjectAttributes: Nested attributes struct (extends AssetAttributes) +- APIObjectNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .api_related import RelatedAPIField + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class APIObject(Asset): + """ + Instances of APIObject in Atlan. + """ + + API_FIELD_COUNT: ClassVar[Any] = None + API_SPEC_TYPE: ClassVar[Any] = None + API_SPEC_VERSION: ClassVar[Any] = None + API_SPEC_NAME: ClassVar[Any] = None + API_SPEC_QUALIFIED_NAME: ClassVar[Any] = None + API_EXTERNAL_DOCS: ClassVar[Any] = None + API_IS_AUTH_OPTIONAL: ClassVar[Any] = None + API_IS_OBJECT_REFERENCE: ClassVar[Any] = None + API_OBJECT_QUALIFIED_NAME: ClassVar[Any] = None + API_FIELDS: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "APIObject" + + api_field_count: Union[int, None, UnsetType] = UNSET + """Count of the APIField of this object.""" + + api_spec_type: Union[str, None, UnsetType] = UNSET + """Type of API, for example: OpenAPI, GraphQL, etc.""" + + api_spec_version: Union[str, None, UnsetType] = UNSET + """Version of the API specification.""" + + api_spec_name: Union[str, None, UnsetType] = UNSET + """Simple name of the API spec, if this asset is contained in an API spec.""" + + api_spec_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the API spec, if this asset is contained in an API spec.""" + + api_external_docs: Union[Dict[str, str], None, UnsetType] = UNSET + """External documentation of the API.""" + + api_is_auth_optional: Union[bool, None, UnsetType] = UNSET + """Whether authentication is optional (true) or required (false).""" + + api_is_object_reference: Union[bool, None, UnsetType] = UNSET + """If this asset refers to an APIObject""" + + api_object_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the APIObject that is referred to by this asset. When apiIsObjectReference is true.""" + + api_fields: Union[List[RelatedAPIField], None, UnsetType] = UNSET + """APIField assets contained within this APIObject.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "APIObject" + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + connection_qualified_name: str, + api_field_count: Union[int, None] = None, + ) -> "APIObject": + """Create a new APIObject asset.""" + validate_required_fields( + ["name", "connection_qualified_name"], [name, connection_qualified_name] + ) + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + return cls( + name=name, + qualified_name=f"{connection_qualified_name}/{name}", + connection_qualified_name=connection_qualified_name, + connector_name=connector_name, + api_field_count=api_field_count, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "APIObject": + """Create an APIObject instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "APIObject": + """Return only fields required for update operations.""" + return APIObject.updater(qualified_name=self.qualified_name, name=self.name) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _api_object_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> APIObject: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + APIObject instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _api_object_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class APIObjectAttributes(AssetAttributes): + """APIObject-specific attributes for nested API format.""" + + api_field_count: Union[int, None, UnsetType] = UNSET + """Count of the APIField of this object.""" + + api_spec_type: Union[str, None, UnsetType] = UNSET + """Type of API, for example: OpenAPI, GraphQL, etc.""" + + api_spec_version: Union[str, None, UnsetType] = UNSET + """Version of the API specification.""" + + api_spec_name: Union[str, None, UnsetType] = UNSET + """Simple name of the API spec, if this asset is contained in an API spec.""" + + api_spec_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the API spec, if this asset is contained in an API spec.""" + + api_external_docs: Union[Dict[str, str], None, UnsetType] = UNSET + """External documentation of the API.""" + + api_is_auth_optional: Union[bool, None, UnsetType] = UNSET + """Whether authentication is optional (true) or required (false).""" + + api_is_object_reference: Union[bool, None, UnsetType] = UNSET + """If this asset refers to an APIObject""" + + api_object_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the APIObject that is referred to by this asset. When apiIsObjectReference is true.""" + + +class APIObjectRelationshipAttributes(AssetRelationshipAttributes): + """APIObject-specific relationship attributes for nested API format.""" + + api_fields: Union[List[RelatedAPIField], None, UnsetType] = UNSET + """APIField assets contained within this APIObject.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class APIObjectNested(AssetNested): + """APIObject in nested API format for high-performance serialization.""" + + attributes: Union[APIObjectAttributes, UnsetType] = UNSET + relationship_attributes: Union[APIObjectRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + APIObjectRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + APIObjectRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_API_OBJECT_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "api_fields", + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_api_object_attrs(attrs: APIObjectAttributes, obj: APIObject) -> None: + """Populate APIObject-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.api_field_count = obj.api_field_count + attrs.api_spec_type = obj.api_spec_type + attrs.api_spec_version = obj.api_spec_version + attrs.api_spec_name = obj.api_spec_name + attrs.api_spec_qualified_name = obj.api_spec_qualified_name + attrs.api_external_docs = obj.api_external_docs + attrs.api_is_auth_optional = obj.api_is_auth_optional + attrs.api_is_object_reference = obj.api_is_object_reference + attrs.api_object_qualified_name = obj.api_object_qualified_name + + +def _extract_api_object_attrs(attrs: APIObjectAttributes) -> dict: + """Extract all APIObject attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["api_field_count"] = attrs.api_field_count + result["api_spec_type"] = attrs.api_spec_type + result["api_spec_version"] = attrs.api_spec_version + result["api_spec_name"] = attrs.api_spec_name + result["api_spec_qualified_name"] = attrs.api_spec_qualified_name + result["api_external_docs"] = attrs.api_external_docs + result["api_is_auth_optional"] = attrs.api_is_auth_optional + result["api_is_object_reference"] = attrs.api_is_object_reference + result["api_object_qualified_name"] = attrs.api_object_qualified_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _api_object_to_nested(api_object: APIObject) -> APIObjectNested: + """Convert flat APIObject to nested format.""" + attrs = APIObjectAttributes() + _populate_api_object_attrs(attrs, api_object) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + api_object, _API_OBJECT_REL_FIELDS, APIObjectRelationshipAttributes + ) + return APIObjectNested( + guid=api_object.guid, + type_name=api_object.type_name, + status=api_object.status, + version=api_object.version, + create_time=api_object.create_time, + update_time=api_object.update_time, + created_by=api_object.created_by, + updated_by=api_object.updated_by, + classifications=api_object.classifications, + classification_names=api_object.classification_names, + meanings=api_object.meanings, + labels=api_object.labels, + business_attributes=api_object.business_attributes, + custom_attributes=api_object.custom_attributes, + pending_tasks=api_object.pending_tasks, + proxy=api_object.proxy, + is_incomplete=api_object.is_incomplete, + provenance_type=api_object.provenance_type, + home_id=api_object.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _api_object_from_nested(nested: APIObjectNested) -> APIObject: + """Convert nested format to flat APIObject.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else APIObjectAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _API_OBJECT_REL_FIELDS, + APIObjectRelationshipAttributes, + ) + return APIObject( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_api_object_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _api_object_to_nested_bytes(api_object: APIObject, serde: Serde) -> bytes: + """Convert flat APIObject to nested JSON bytes.""" + return serde.encode(_api_object_to_nested(api_object)) + + +def _api_object_from_nested_bytes(data: bytes, serde: Serde) -> APIObject: + """Convert nested JSON bytes to flat APIObject.""" + nested = serde.decode(data, APIObjectNested) + return _api_object_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +APIObject.API_FIELD_COUNT = NumericField("apiFieldCount", "apiFieldCount") +APIObject.API_SPEC_TYPE = KeywordField("apiSpecType", "apiSpecType") +APIObject.API_SPEC_VERSION = KeywordField("apiSpecVersion", "apiSpecVersion") +APIObject.API_SPEC_NAME = KeywordField("apiSpecName", "apiSpecName") +APIObject.API_SPEC_QUALIFIED_NAME = KeywordTextField( + "apiSpecQualifiedName", "apiSpecQualifiedName", "apiSpecQualifiedName.text" +) +APIObject.API_EXTERNAL_DOCS = KeywordField("apiExternalDocs", "apiExternalDocs") +APIObject.API_IS_AUTH_OPTIONAL = BooleanField("apiIsAuthOptional", "apiIsAuthOptional") +APIObject.API_IS_OBJECT_REFERENCE = BooleanField( + "apiIsObjectReference", "apiIsObjectReference" +) +APIObject.API_OBJECT_QUALIFIED_NAME = KeywordField( + "apiObjectQualifiedName", "apiObjectQualifiedName" +) +APIObject.API_FIELDS = RelationField("apiFields") +APIObject.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +APIObject.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +APIObject.ANOMALO_CHECKS = RelationField("anomaloChecks") +APIObject.APPLICATION = RelationField("application") +APIObject.APPLICATION_FIELD = RelationField("applicationField") +APIObject.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +APIObject.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +APIObject.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +APIObject.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +APIObject.METRICS = RelationField("metrics") +APIObject.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +APIObject.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +APIObject.MEANINGS = RelationField("meanings") +APIObject.MC_MONITORS = RelationField("mcMonitors") +APIObject.MC_INCIDENTS = RelationField("mcIncidents") +APIObject.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +APIObject.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +APIObject.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +APIObject.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +APIObject.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +APIObject.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +APIObject.FILES = RelationField("files") +APIObject.LINKS = RelationField("links") +APIObject.README = RelationField("readme") +APIObject.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +APIObject.SODA_CHECKS = RelationField("sodaChecks") +APIObject.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +APIObject.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/api_path.py b/pyatlan_v9/model/assets/api_path.py new file mode 100644 index 000000000..b1b1fa369 --- /dev/null +++ b/pyatlan_v9/model/assets/api_path.py @@ -0,0 +1,761 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +APIPath asset model with flattened inheritance. + +This module provides: +- APIPath: Flat asset class (easy to use) +- APIPathAttributes: Nested attributes struct (extends AssetAttributes) +- APIPathNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .api_related import RelatedAPISpec + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class APIPath(Asset): + """ + Instance of an API path that could contain one or more endpoints in Atlan. + """ + + API_PATH_SUMMARY: ClassVar[Any] = None + API_PATH_RAW_URI: ClassVar[Any] = None + API_PATH_IS_TEMPLATED: ClassVar[Any] = None + API_PATH_AVAILABLE_OPERATIONS: ClassVar[Any] = None + API_PATH_AVAILABLE_RESPONSE_CODES: ClassVar[Any] = None + API_PATH_IS_INGRESS_EXPOSED: ClassVar[Any] = None + API_SPEC_TYPE: ClassVar[Any] = None + API_SPEC_VERSION: ClassVar[Any] = None + API_SPEC_NAME: ClassVar[Any] = None + API_SPEC_QUALIFIED_NAME: ClassVar[Any] = None + API_EXTERNAL_DOCS: ClassVar[Any] = None + API_IS_AUTH_OPTIONAL: ClassVar[Any] = None + API_IS_OBJECT_REFERENCE: ClassVar[Any] = None + API_OBJECT_QUALIFIED_NAME: ClassVar[Any] = None + API_SPEC: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "APIPath" + + api_path_summary: Union[str, None, UnsetType] = UNSET + """Descriptive summary intended to apply to all operations in this path.""" + + api_path_raw_uri: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="apiPathRawURI" + ) + """Absolute path to an individual endpoint.""" + + api_path_is_templated: Union[bool, None, UnsetType] = UNSET + """Whether the endpoint's path contains replaceable parameters (true) or not (false).""" + + api_path_available_operations: Union[List[str], None, UnsetType] = UNSET + """List of the operations available on the endpoint.""" + + api_path_available_response_codes: Union[Dict[str, str], None, UnsetType] = UNSET + """Response codes available on the path across all operations.""" + + api_path_is_ingress_exposed: Union[bool, None, UnsetType] = UNSET + """Whether the path is exposed as an ingress (true) or not (false).""" + + api_spec_type: Union[str, None, UnsetType] = UNSET + """Type of API, for example: OpenAPI, GraphQL, etc.""" + + api_spec_version: Union[str, None, UnsetType] = UNSET + """Version of the API specification.""" + + api_spec_name: Union[str, None, UnsetType] = UNSET + """Simple name of the API spec, if this asset is contained in an API spec.""" + + api_spec_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the API spec, if this asset is contained in an API spec.""" + + api_external_docs: Union[Dict[str, str], None, UnsetType] = UNSET + """External documentation of the API.""" + + api_is_auth_optional: Union[bool, None, UnsetType] = UNSET + """Whether authentication is optional (true) or required (false).""" + + api_is_object_reference: Union[bool, None, UnsetType] = UNSET + """If this asset refers to an APIObject""" + + api_object_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the APIObject that is referred to by this asset. When apiIsObjectReference is true.""" + + api_spec: Union[RelatedAPISpec, None, UnsetType] = UNSET + """API specification in which this path exists.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "APIPath" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + @property + def api_path_raw_u_r_i(self) -> Union[str, None, UnsetType]: + return self.api_path_raw_uri + + @api_path_raw_u_r_i.setter + def api_path_raw_u_r_i(self, value: Union[str, None, UnsetType]) -> None: + self.api_path_raw_uri = value + + @classmethod + @init_guid + def creator( + cls, + *, + path_raw_uri: str, + spec_qualified_name: str, + connection_qualified_name: Union[str, None] = None, + ) -> "APIPath": + """Create a new APIPath asset.""" + validate_required_fields( + ["path_raw_uri", "spec_qualified_name"], [path_raw_uri, spec_qualified_name] + ) + if connection_qualified_name: + connection_qn = connection_qualified_name + else: + spec_parts = spec_qualified_name.split("/") + connection_qn = ( + "/".join(spec_parts[:3]) + if len(spec_parts) >= 3 + else spec_qualified_name + ) + conn_parts = connection_qn.split("/") + connector_name = conn_parts[1] if len(conn_parts) > 1 else None + return cls( + name=path_raw_uri, + qualified_name=f"{spec_qualified_name}{path_raw_uri}", + api_path_raw_uri=path_raw_uri, + api_spec_qualified_name=spec_qualified_name, + connection_qualified_name=connection_qn, + connector_name=connector_name, + api_spec=RelatedAPISpec( + unique_attributes={"qualifiedName": spec_qualified_name} + ), + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "APIPath": + """Create an APIPath instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "APIPath": + """Return only fields required for update operations.""" + return APIPath.updater(qualified_name=self.qualified_name, name=self.name) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _api_path_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> APIPath: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + APIPath instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _api_path_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class APIPathAttributes(AssetAttributes): + """APIPath-specific attributes for nested API format.""" + + api_path_summary: Union[str, None, UnsetType] = UNSET + """Descriptive summary intended to apply to all operations in this path.""" + + api_path_raw_uri: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="apiPathRawURI" + ) + """Absolute path to an individual endpoint.""" + + api_path_is_templated: Union[bool, None, UnsetType] = UNSET + """Whether the endpoint's path contains replaceable parameters (true) or not (false).""" + + api_path_available_operations: Union[List[str], None, UnsetType] = UNSET + """List of the operations available on the endpoint.""" + + api_path_available_response_codes: Union[Dict[str, str], None, UnsetType] = UNSET + """Response codes available on the path across all operations.""" + + api_path_is_ingress_exposed: Union[bool, None, UnsetType] = UNSET + """Whether the path is exposed as an ingress (true) or not (false).""" + + api_spec_type: Union[str, None, UnsetType] = UNSET + """Type of API, for example: OpenAPI, GraphQL, etc.""" + + api_spec_version: Union[str, None, UnsetType] = UNSET + """Version of the API specification.""" + + api_spec_name: Union[str, None, UnsetType] = UNSET + """Simple name of the API spec, if this asset is contained in an API spec.""" + + api_spec_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the API spec, if this asset is contained in an API spec.""" + + api_external_docs: Union[Dict[str, str], None, UnsetType] = UNSET + """External documentation of the API.""" + + api_is_auth_optional: Union[bool, None, UnsetType] = UNSET + """Whether authentication is optional (true) or required (false).""" + + api_is_object_reference: Union[bool, None, UnsetType] = UNSET + """If this asset refers to an APIObject""" + + api_object_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the APIObject that is referred to by this asset. When apiIsObjectReference is true.""" + + +class APIPathRelationshipAttributes(AssetRelationshipAttributes): + """APIPath-specific relationship attributes for nested API format.""" + + api_spec: Union[RelatedAPISpec, None, UnsetType] = UNSET + """API specification in which this path exists.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class APIPathNested(AssetNested): + """APIPath in nested API format for high-performance serialization.""" + + attributes: Union[APIPathAttributes, UnsetType] = UNSET + relationship_attributes: Union[APIPathRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[APIPathRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[APIPathRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_API_PATH_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "api_spec", + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_api_path_attrs(attrs: APIPathAttributes, obj: APIPath) -> None: + """Populate APIPath-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.api_path_summary = obj.api_path_summary + attrs.api_path_raw_uri = obj.api_path_raw_uri + attrs.api_path_is_templated = obj.api_path_is_templated + attrs.api_path_available_operations = obj.api_path_available_operations + attrs.api_path_available_response_codes = obj.api_path_available_response_codes + attrs.api_path_is_ingress_exposed = obj.api_path_is_ingress_exposed + attrs.api_spec_type = obj.api_spec_type + attrs.api_spec_version = obj.api_spec_version + attrs.api_spec_name = obj.api_spec_name + attrs.api_spec_qualified_name = obj.api_spec_qualified_name + attrs.api_external_docs = obj.api_external_docs + attrs.api_is_auth_optional = obj.api_is_auth_optional + attrs.api_is_object_reference = obj.api_is_object_reference + attrs.api_object_qualified_name = obj.api_object_qualified_name + + +def _extract_api_path_attrs(attrs: APIPathAttributes) -> dict: + """Extract all APIPath attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["api_path_summary"] = attrs.api_path_summary + result["api_path_raw_uri"] = attrs.api_path_raw_uri + result["api_path_is_templated"] = attrs.api_path_is_templated + result["api_path_available_operations"] = attrs.api_path_available_operations + result["api_path_available_response_codes"] = ( + attrs.api_path_available_response_codes + ) + result["api_path_is_ingress_exposed"] = attrs.api_path_is_ingress_exposed + result["api_spec_type"] = attrs.api_spec_type + result["api_spec_version"] = attrs.api_spec_version + result["api_spec_name"] = attrs.api_spec_name + result["api_spec_qualified_name"] = attrs.api_spec_qualified_name + result["api_external_docs"] = attrs.api_external_docs + result["api_is_auth_optional"] = attrs.api_is_auth_optional + result["api_is_object_reference"] = attrs.api_is_object_reference + result["api_object_qualified_name"] = attrs.api_object_qualified_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _api_path_to_nested(api_path: APIPath) -> APIPathNested: + """Convert flat APIPath to nested format.""" + attrs = APIPathAttributes() + _populate_api_path_attrs(attrs, api_path) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + api_path, _API_PATH_REL_FIELDS, APIPathRelationshipAttributes + ) + return APIPathNested( + guid=api_path.guid, + type_name=api_path.type_name, + status=api_path.status, + version=api_path.version, + create_time=api_path.create_time, + update_time=api_path.update_time, + created_by=api_path.created_by, + updated_by=api_path.updated_by, + classifications=api_path.classifications, + classification_names=api_path.classification_names, + meanings=api_path.meanings, + labels=api_path.labels, + business_attributes=api_path.business_attributes, + custom_attributes=api_path.custom_attributes, + pending_tasks=api_path.pending_tasks, + proxy=api_path.proxy, + is_incomplete=api_path.is_incomplete, + provenance_type=api_path.provenance_type, + home_id=api_path.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _api_path_from_nested(nested: APIPathNested) -> APIPath: + """Convert nested format to flat APIPath.""" + attrs = nested.attributes if nested.attributes is not UNSET else APIPathAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _API_PATH_REL_FIELDS, + APIPathRelationshipAttributes, + ) + return APIPath( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_api_path_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _api_path_to_nested_bytes(api_path: APIPath, serde: Serde) -> bytes: + """Convert flat APIPath to nested JSON bytes.""" + return serde.encode(_api_path_to_nested(api_path)) + + +def _api_path_from_nested_bytes(data: bytes, serde: Serde) -> APIPath: + """Convert nested JSON bytes to flat APIPath.""" + nested = serde.decode(data, APIPathNested) + return _api_path_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + RelationField, +) + +APIPath.API_PATH_SUMMARY = KeywordField("apiPathSummary", "apiPathSummary") +APIPath.API_PATH_RAW_URI = KeywordTextField( + "apiPathRawURI", "apiPathRawURI", "apiPathRawURI.text" +) +APIPath.API_PATH_IS_TEMPLATED = BooleanField("apiPathIsTemplated", "apiPathIsTemplated") +APIPath.API_PATH_AVAILABLE_OPERATIONS = KeywordField( + "apiPathAvailableOperations", "apiPathAvailableOperations" +) +APIPath.API_PATH_AVAILABLE_RESPONSE_CODES = KeywordField( + "apiPathAvailableResponseCodes", "apiPathAvailableResponseCodes" +) +APIPath.API_PATH_IS_INGRESS_EXPOSED = BooleanField( + "apiPathIsIngressExposed", "apiPathIsIngressExposed" +) +APIPath.API_SPEC_TYPE = KeywordField("apiSpecType", "apiSpecType") +APIPath.API_SPEC_VERSION = KeywordField("apiSpecVersion", "apiSpecVersion") +APIPath.API_SPEC_NAME = KeywordField("apiSpecName", "apiSpecName") +APIPath.API_SPEC_QUALIFIED_NAME = KeywordTextField( + "apiSpecQualifiedName", "apiSpecQualifiedName", "apiSpecQualifiedName.text" +) +APIPath.API_EXTERNAL_DOCS = KeywordField("apiExternalDocs", "apiExternalDocs") +APIPath.API_IS_AUTH_OPTIONAL = BooleanField("apiIsAuthOptional", "apiIsAuthOptional") +APIPath.API_IS_OBJECT_REFERENCE = BooleanField( + "apiIsObjectReference", "apiIsObjectReference" +) +APIPath.API_OBJECT_QUALIFIED_NAME = KeywordField( + "apiObjectQualifiedName", "apiObjectQualifiedName" +) +APIPath.API_SPEC = RelationField("apiSpec") +APIPath.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +APIPath.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +APIPath.ANOMALO_CHECKS = RelationField("anomaloChecks") +APIPath.APPLICATION = RelationField("application") +APIPath.APPLICATION_FIELD = RelationField("applicationField") +APIPath.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +APIPath.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +APIPath.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +APIPath.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +APIPath.METRICS = RelationField("metrics") +APIPath.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +APIPath.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +APIPath.MEANINGS = RelationField("meanings") +APIPath.MC_MONITORS = RelationField("mcMonitors") +APIPath.MC_INCIDENTS = RelationField("mcIncidents") +APIPath.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +APIPath.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +APIPath.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +APIPath.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +APIPath.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +APIPath.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +APIPath.FILES = RelationField("files") +APIPath.LINKS = RelationField("links") +APIPath.README = RelationField("readme") +APIPath.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +APIPath.SODA_CHECKS = RelationField("sodaChecks") +APIPath.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +APIPath.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/api_query.py b/pyatlan_v9/model/assets/api_query.py new file mode 100644 index 000000000..695c17c66 --- /dev/null +++ b/pyatlan_v9/model/assets/api_query.py @@ -0,0 +1,724 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +APIQuery asset model with flattened inheritance. + +This module provides: +- APIQuery: Flat asset class (easy to use) +- APIQueryAttributes: Nested attributes struct (extends AssetAttributes) +- APIQueryNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .api_related import RelatedAPIField + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class APIQuery(Asset): + """ + Instances of APIQuery in Atlan. + """ + + API_INPUT_FIELD_COUNT: ClassVar[Any] = None + API_QUERY_OUTPUT_TYPE: ClassVar[Any] = None + API_QUERY_OUTPUT_TYPE_SECONDARY: ClassVar[Any] = None + API_SPEC_TYPE: ClassVar[Any] = None + API_SPEC_VERSION: ClassVar[Any] = None + API_SPEC_NAME: ClassVar[Any] = None + API_SPEC_QUALIFIED_NAME: ClassVar[Any] = None + API_EXTERNAL_DOCS: ClassVar[Any] = None + API_IS_AUTH_OPTIONAL: ClassVar[Any] = None + API_IS_OBJECT_REFERENCE: ClassVar[Any] = None + API_OBJECT_QUALIFIED_NAME: ClassVar[Any] = None + API_FIELDS: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "APIQuery" + + api_input_field_count: Union[int, None, UnsetType] = UNSET + """Count of the APIField of this query that are input to it.""" + + api_query_output_type: Union[str, None, UnsetType] = UNSET + """Type of APIQueryOutput, as free text (e.g. STRING, NUMBER etc).""" + + api_query_output_type_secondary: Union[str, None, UnsetType] = UNSET + """Secondary Type of APIQueryOutput (e.g. LIST/STRING then LIST would be the secondary type).""" + + api_spec_type: Union[str, None, UnsetType] = UNSET + """Type of API, for example: OpenAPI, GraphQL, etc.""" + + api_spec_version: Union[str, None, UnsetType] = UNSET + """Version of the API specification.""" + + api_spec_name: Union[str, None, UnsetType] = UNSET + """Simple name of the API spec, if this asset is contained in an API spec.""" + + api_spec_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the API spec, if this asset is contained in an API spec.""" + + api_external_docs: Union[Dict[str, str], None, UnsetType] = UNSET + """External documentation of the API.""" + + api_is_auth_optional: Union[bool, None, UnsetType] = UNSET + """Whether authentication is optional (true) or required (false).""" + + api_is_object_reference: Union[bool, None, UnsetType] = UNSET + """If this asset refers to an APIObject""" + + api_object_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the APIObject that is referred to by this asset. When apiIsObjectReference is true.""" + + api_fields: Union[List[RelatedAPIField], None, UnsetType] = UNSET + """APIField assets contained within this APIQuery.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "APIQuery" + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + connection_qualified_name: str, + api_input_field_count: Union[int, None] = None, + api_query_output_type: Union[str, None] = None, + api_query_output_type_secondary: Union[str, None] = None, + is_object_reference: bool = False, + reference_api_object_qualified_name: Union[str, None] = None, + ) -> "APIQuery": + """Create a new APIQuery asset.""" + validate_required_fields( + ["name", "connection_qualified_name"], [name, connection_qualified_name] + ) + if is_object_reference: + if not reference_api_object_qualified_name or ( + isinstance(reference_api_object_qualified_name, str) + and not reference_api_object_qualified_name.strip() + ): + raise ValueError( + "Set valid qualified name for reference_api_object_qualified_name when is_object_reference is true" + ) + elif ( + reference_api_object_qualified_name + and isinstance(reference_api_object_qualified_name, str) + and reference_api_object_qualified_name.strip() + ): + raise ValueError( + "Set is_object_reference to true to set reference_api_object_qualified_name" + ) + + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + return cls( + name=name, + qualified_name=f"{connection_qualified_name}/{name}", + connection_qualified_name=connection_qualified_name, + connector_name=connector_name, + api_input_field_count=api_input_field_count, + api_query_output_type=api_query_output_type, + api_query_output_type_secondary=api_query_output_type_secondary, + api_is_object_reference=is_object_reference, + api_object_qualified_name=( + reference_api_object_qualified_name if is_object_reference else None + ), + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "APIQuery": + """Create an APIQuery instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "APIQuery": + """Return only fields required for update operations.""" + return APIQuery.updater(qualified_name=self.qualified_name, name=self.name) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _api_query_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> APIQuery: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + APIQuery instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _api_query_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class APIQueryAttributes(AssetAttributes): + """APIQuery-specific attributes for nested API format.""" + + api_input_field_count: Union[int, None, UnsetType] = UNSET + """Count of the APIField of this query that are input to it.""" + + api_query_output_type: Union[str, None, UnsetType] = UNSET + """Type of APIQueryOutput, as free text (e.g. STRING, NUMBER etc).""" + + api_query_output_type_secondary: Union[str, None, UnsetType] = UNSET + """Secondary Type of APIQueryOutput (e.g. LIST/STRING then LIST would be the secondary type).""" + + api_spec_type: Union[str, None, UnsetType] = UNSET + """Type of API, for example: OpenAPI, GraphQL, etc.""" + + api_spec_version: Union[str, None, UnsetType] = UNSET + """Version of the API specification.""" + + api_spec_name: Union[str, None, UnsetType] = UNSET + """Simple name of the API spec, if this asset is contained in an API spec.""" + + api_spec_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the API spec, if this asset is contained in an API spec.""" + + api_external_docs: Union[Dict[str, str], None, UnsetType] = UNSET + """External documentation of the API.""" + + api_is_auth_optional: Union[bool, None, UnsetType] = UNSET + """Whether authentication is optional (true) or required (false).""" + + api_is_object_reference: Union[bool, None, UnsetType] = UNSET + """If this asset refers to an APIObject""" + + api_object_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the APIObject that is referred to by this asset. When apiIsObjectReference is true.""" + + +class APIQueryRelationshipAttributes(AssetRelationshipAttributes): + """APIQuery-specific relationship attributes for nested API format.""" + + api_fields: Union[List[RelatedAPIField], None, UnsetType] = UNSET + """APIField assets contained within this APIQuery.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class APIQueryNested(AssetNested): + """APIQuery in nested API format for high-performance serialization.""" + + attributes: Union[APIQueryAttributes, UnsetType] = UNSET + relationship_attributes: Union[APIQueryRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[APIQueryRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[APIQueryRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_API_QUERY_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "api_fields", + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_api_query_attrs(attrs: APIQueryAttributes, obj: APIQuery) -> None: + """Populate APIQuery-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.api_input_field_count = obj.api_input_field_count + attrs.api_query_output_type = obj.api_query_output_type + attrs.api_query_output_type_secondary = obj.api_query_output_type_secondary + attrs.api_spec_type = obj.api_spec_type + attrs.api_spec_version = obj.api_spec_version + attrs.api_spec_name = obj.api_spec_name + attrs.api_spec_qualified_name = obj.api_spec_qualified_name + attrs.api_external_docs = obj.api_external_docs + attrs.api_is_auth_optional = obj.api_is_auth_optional + attrs.api_is_object_reference = obj.api_is_object_reference + attrs.api_object_qualified_name = obj.api_object_qualified_name + + +def _extract_api_query_attrs(attrs: APIQueryAttributes) -> dict: + """Extract all APIQuery attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["api_input_field_count"] = attrs.api_input_field_count + result["api_query_output_type"] = attrs.api_query_output_type + result["api_query_output_type_secondary"] = attrs.api_query_output_type_secondary + result["api_spec_type"] = attrs.api_spec_type + result["api_spec_version"] = attrs.api_spec_version + result["api_spec_name"] = attrs.api_spec_name + result["api_spec_qualified_name"] = attrs.api_spec_qualified_name + result["api_external_docs"] = attrs.api_external_docs + result["api_is_auth_optional"] = attrs.api_is_auth_optional + result["api_is_object_reference"] = attrs.api_is_object_reference + result["api_object_qualified_name"] = attrs.api_object_qualified_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _api_query_to_nested(api_query: APIQuery) -> APIQueryNested: + """Convert flat APIQuery to nested format.""" + attrs = APIQueryAttributes() + _populate_api_query_attrs(attrs, api_query) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + api_query, _API_QUERY_REL_FIELDS, APIQueryRelationshipAttributes + ) + return APIQueryNested( + guid=api_query.guid, + type_name=api_query.type_name, + status=api_query.status, + version=api_query.version, + create_time=api_query.create_time, + update_time=api_query.update_time, + created_by=api_query.created_by, + updated_by=api_query.updated_by, + classifications=api_query.classifications, + classification_names=api_query.classification_names, + meanings=api_query.meanings, + labels=api_query.labels, + business_attributes=api_query.business_attributes, + custom_attributes=api_query.custom_attributes, + pending_tasks=api_query.pending_tasks, + proxy=api_query.proxy, + is_incomplete=api_query.is_incomplete, + provenance_type=api_query.provenance_type, + home_id=api_query.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _api_query_from_nested(nested: APIQueryNested) -> APIQuery: + """Convert nested format to flat APIQuery.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else APIQueryAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _API_QUERY_REL_FIELDS, + APIQueryRelationshipAttributes, + ) + return APIQuery( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_api_query_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _api_query_to_nested_bytes(api_query: APIQuery, serde: Serde) -> bytes: + """Convert flat APIQuery to nested JSON bytes.""" + return serde.encode(_api_query_to_nested(api_query)) + + +def _api_query_from_nested_bytes(data: bytes, serde: Serde) -> APIQuery: + """Convert nested JSON bytes to flat APIQuery.""" + nested = serde.decode(data, APIQueryNested) + return _api_query_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +APIQuery.API_INPUT_FIELD_COUNT = NumericField( + "apiInputFieldCount", "apiInputFieldCount" +) +APIQuery.API_QUERY_OUTPUT_TYPE = KeywordField( + "apiQueryOutputType", "apiQueryOutputType" +) +APIQuery.API_QUERY_OUTPUT_TYPE_SECONDARY = KeywordField( + "apiQueryOutputTypeSecondary", "apiQueryOutputTypeSecondary" +) +APIQuery.API_SPEC_TYPE = KeywordField("apiSpecType", "apiSpecType") +APIQuery.API_SPEC_VERSION = KeywordField("apiSpecVersion", "apiSpecVersion") +APIQuery.API_SPEC_NAME = KeywordField("apiSpecName", "apiSpecName") +APIQuery.API_SPEC_QUALIFIED_NAME = KeywordTextField( + "apiSpecQualifiedName", "apiSpecQualifiedName", "apiSpecQualifiedName.text" +) +APIQuery.API_EXTERNAL_DOCS = KeywordField("apiExternalDocs", "apiExternalDocs") +APIQuery.API_IS_AUTH_OPTIONAL = BooleanField("apiIsAuthOptional", "apiIsAuthOptional") +APIQuery.API_IS_OBJECT_REFERENCE = BooleanField( + "apiIsObjectReference", "apiIsObjectReference" +) +APIQuery.API_OBJECT_QUALIFIED_NAME = KeywordField( + "apiObjectQualifiedName", "apiObjectQualifiedName" +) +APIQuery.API_FIELDS = RelationField("apiFields") +APIQuery.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +APIQuery.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +APIQuery.ANOMALO_CHECKS = RelationField("anomaloChecks") +APIQuery.APPLICATION = RelationField("application") +APIQuery.APPLICATION_FIELD = RelationField("applicationField") +APIQuery.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +APIQuery.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +APIQuery.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +APIQuery.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +APIQuery.METRICS = RelationField("metrics") +APIQuery.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +APIQuery.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +APIQuery.MEANINGS = RelationField("meanings") +APIQuery.MC_MONITORS = RelationField("mcMonitors") +APIQuery.MC_INCIDENTS = RelationField("mcIncidents") +APIQuery.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +APIQuery.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +APIQuery.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +APIQuery.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +APIQuery.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +APIQuery.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +APIQuery.FILES = RelationField("files") +APIQuery.LINKS = RelationField("links") +APIQuery.README = RelationField("readme") +APIQuery.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +APIQuery.SODA_CHECKS = RelationField("sodaChecks") +APIQuery.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +APIQuery.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/api_related.py b/pyatlan_v9/model/assets/api_related.py new file mode 100644 index 000000000..ea243e62e --- /dev/null +++ b/pyatlan_v9/model/assets/api_related.py @@ -0,0 +1,214 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for API module. + +This module contains all Related{Type} classes for the API type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedCatalog +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedAPI", + "RelatedAPISpec", + "RelatedAPIPath", + "RelatedAPIField", + "RelatedAPIObject", + "RelatedAPIQuery", +] + + +class RelatedAPI(RelatedCatalog): + """ + Related entity reference for API assets. + + Extends RelatedCatalog with API-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "API" so it serializes correctly + + api_spec_type: Union[str, None, UnsetType] = UNSET + """Type of API, for example: OpenAPI, GraphQL, etc.""" + + api_spec_version: Union[str, None, UnsetType] = UNSET + """Version of the API specification.""" + + api_spec_name: Union[str, None, UnsetType] = UNSET + """Simple name of the API spec, if this asset is contained in an API spec.""" + + api_spec_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the API spec, if this asset is contained in an API spec.""" + + api_external_docs: Union[Dict[str, str], None, UnsetType] = UNSET + """External documentation of the API.""" + + api_is_auth_optional: Union[bool, None, UnsetType] = UNSET + """Whether authentication is optional (true) or required (false).""" + + api_is_object_reference: Union[bool, None, UnsetType] = UNSET + """If this asset refers to an APIObject""" + + api_object_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the APIObject that is referred to by this asset. When apiIsObjectReference is true.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "API" + + +class RelatedAPISpec(RelatedAPI): + """ + Related entity reference for APISpec assets. + + Extends RelatedAPI with APISpec-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "APISpec" so it serializes correctly + + api_spec_terms_of_service_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="apiSpecTermsOfServiceURL" + ) + """URL to the terms of service for the API specification.""" + + api_spec_contact_email: Union[str, None, UnsetType] = UNSET + """Email address for a contact responsible for the API specification.""" + + api_spec_contact_name: Union[str, None, UnsetType] = UNSET + """Name of the contact responsible for the API specification.""" + + api_spec_contact_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="apiSpecContactURL" + ) + """URL pointing to the contact information.""" + + api_spec_license_name: Union[str, None, UnsetType] = UNSET + """Name of the license under which the API specification is available.""" + + api_spec_license_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="apiSpecLicenseURL" + ) + """URL to the license under which the API specification is available.""" + + api_spec_contract_version: Union[str, None, UnsetType] = UNSET + """Version of the contract for the API specification.""" + + api_spec_service_alias: Union[str, None, UnsetType] = UNSET + """Service alias for the API specification.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "APISpec" + + +class RelatedAPIPath(RelatedAPI): + """ + Related entity reference for APIPath assets. + + Extends RelatedAPI with APIPath-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "APIPath" so it serializes correctly + + api_path_summary: Union[str, None, UnsetType] = UNSET + """Descriptive summary intended to apply to all operations in this path.""" + + api_path_raw_uri: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="apiPathRawURI" + ) + """Absolute path to an individual endpoint.""" + + api_path_is_templated: Union[bool, None, UnsetType] = UNSET + """Whether the endpoint's path contains replaceable parameters (true) or not (false).""" + + api_path_available_operations: Union[List[str], None, UnsetType] = UNSET + """List of the operations available on the endpoint.""" + + api_path_available_response_codes: Union[Dict[str, str], None, UnsetType] = UNSET + """Response codes available on the path across all operations.""" + + api_path_is_ingress_exposed: Union[bool, None, UnsetType] = UNSET + """Whether the path is exposed as an ingress (true) or not (false).""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "APIPath" + + +class RelatedAPIField(RelatedAPI): + """ + Related entity reference for APIField assets. + + Extends RelatedAPI with APIField-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "APIField" so it serializes correctly + + api_field_type: Union[str, None, UnsetType] = UNSET + """Type of APIField, as free text (e.g. STRING, NUMBER etc).""" + + api_field_type_secondary: Union[str, None, UnsetType] = UNSET + """Secondary type of APIField (e.g. LIST/STRING, then LIST would be the secondary type).""" + + api_query_param_type: Union[str, None, UnsetType] = UNSET + """If parent relationship type is APIQuery, then this attribute denotes if this is input or output parameter.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "APIField" + + +class RelatedAPIObject(RelatedAPI): + """ + Related entity reference for APIObject assets. + + Extends RelatedAPI with APIObject-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "APIObject" so it serializes correctly + + api_field_count: Union[int, None, UnsetType] = UNSET + """Count of the APIField of this object.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "APIObject" + + +class RelatedAPIQuery(RelatedAPI): + """ + Related entity reference for APIQuery assets. + + Extends RelatedAPI with APIQuery-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "APIQuery" so it serializes correctly + + api_input_field_count: Union[int, None, UnsetType] = UNSET + """Count of the APIField of this query that are input to it.""" + + api_query_output_type: Union[str, None, UnsetType] = UNSET + """Type of APIQueryOutput, as free text (e.g. STRING, NUMBER etc).""" + + api_query_output_type_secondary: Union[str, None, UnsetType] = UNSET + """Secondary Type of APIQueryOutput (e.g. LIST/STRING then LIST would be the secondary type).""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "APIQuery" diff --git a/pyatlan_v9/model/assets/api_spec.py b/pyatlan_v9/model/assets/api_spec.py new file mode 100644 index 000000000..ead19021b --- /dev/null +++ b/pyatlan_v9/model/assets/api_spec.py @@ -0,0 +1,760 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +APISpec asset model with flattened inheritance. + +This module provides: +- APISpec: Flat asset class (easy to use) +- APISpecAttributes: Nested attributes struct (extends AssetAttributes) +- APISpecNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .api_related import RelatedAPIPath + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class APISpec(Asset): + """ + Instance of an API specification in Atlan. + """ + + API_SPEC_TERMS_OF_SERVICE_URL: ClassVar[Any] = None + API_SPEC_CONTACT_EMAIL: ClassVar[Any] = None + API_SPEC_CONTACT_NAME: ClassVar[Any] = None + API_SPEC_CONTACT_URL: ClassVar[Any] = None + API_SPEC_LICENSE_NAME: ClassVar[Any] = None + API_SPEC_LICENSE_URL: ClassVar[Any] = None + API_SPEC_CONTRACT_VERSION: ClassVar[Any] = None + API_SPEC_SERVICE_ALIAS: ClassVar[Any] = None + API_SPEC_TYPE: ClassVar[Any] = None + API_SPEC_VERSION: ClassVar[Any] = None + API_SPEC_NAME: ClassVar[Any] = None + API_SPEC_QUALIFIED_NAME: ClassVar[Any] = None + API_EXTERNAL_DOCS: ClassVar[Any] = None + API_IS_AUTH_OPTIONAL: ClassVar[Any] = None + API_IS_OBJECT_REFERENCE: ClassVar[Any] = None + API_OBJECT_QUALIFIED_NAME: ClassVar[Any] = None + API_PATHS: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "APISpec" + + api_spec_terms_of_service_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="apiSpecTermsOfServiceURL" + ) + """URL to the terms of service for the API specification.""" + + api_spec_contact_email: Union[str, None, UnsetType] = UNSET + """Email address for a contact responsible for the API specification.""" + + api_spec_contact_name: Union[str, None, UnsetType] = UNSET + """Name of the contact responsible for the API specification.""" + + api_spec_contact_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="apiSpecContactURL" + ) + """URL pointing to the contact information.""" + + api_spec_license_name: Union[str, None, UnsetType] = UNSET + """Name of the license under which the API specification is available.""" + + api_spec_license_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="apiSpecLicenseURL" + ) + """URL to the license under which the API specification is available.""" + + api_spec_contract_version: Union[str, None, UnsetType] = UNSET + """Version of the contract for the API specification.""" + + api_spec_service_alias: Union[str, None, UnsetType] = UNSET + """Service alias for the API specification.""" + + api_spec_type: Union[str, None, UnsetType] = UNSET + """Type of API, for example: OpenAPI, GraphQL, etc.""" + + api_spec_version: Union[str, None, UnsetType] = UNSET + """Version of the API specification.""" + + api_spec_name: Union[str, None, UnsetType] = UNSET + """Simple name of the API spec, if this asset is contained in an API spec.""" + + api_spec_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the API spec, if this asset is contained in an API spec.""" + + api_external_docs: Union[Dict[str, str], None, UnsetType] = UNSET + """External documentation of the API.""" + + api_is_auth_optional: Union[bool, None, UnsetType] = UNSET + """Whether authentication is optional (true) or required (false).""" + + api_is_object_reference: Union[bool, None, UnsetType] = UNSET + """If this asset refers to an APIObject""" + + api_object_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the APIObject that is referred to by this asset. When apiIsObjectReference is true.""" + + api_paths: Union[List[RelatedAPIPath], None, UnsetType] = UNSET + """Paths that exist within this API specification.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "APISpec" + + @classmethod + @init_guid + def creator(cls, *, name: str, connection_qualified_name: str) -> "APISpec": + """Create a new APISpec asset.""" + validate_required_fields( + ["name", "connection_qualified_name"], [name, connection_qualified_name] + ) + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + return cls( + name=name, + qualified_name=f"{connection_qualified_name}/{name}", + connection_qualified_name=connection_qualified_name, + connector_name=connector_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "APISpec": + """Create an APISpec instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "APISpec": + """Return only fields required for update operations.""" + return APISpec.updater(qualified_name=self.qualified_name, name=self.name) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _api_spec_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> APISpec: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + APISpec instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _api_spec_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class APISpecAttributes(AssetAttributes): + """APISpec-specific attributes for nested API format.""" + + api_spec_terms_of_service_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="apiSpecTermsOfServiceURL" + ) + """URL to the terms of service for the API specification.""" + + api_spec_contact_email: Union[str, None, UnsetType] = UNSET + """Email address for a contact responsible for the API specification.""" + + api_spec_contact_name: Union[str, None, UnsetType] = UNSET + """Name of the contact responsible for the API specification.""" + + api_spec_contact_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="apiSpecContactURL" + ) + """URL pointing to the contact information.""" + + api_spec_license_name: Union[str, None, UnsetType] = UNSET + """Name of the license under which the API specification is available.""" + + api_spec_license_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="apiSpecLicenseURL" + ) + """URL to the license under which the API specification is available.""" + + api_spec_contract_version: Union[str, None, UnsetType] = UNSET + """Version of the contract for the API specification.""" + + api_spec_service_alias: Union[str, None, UnsetType] = UNSET + """Service alias for the API specification.""" + + api_spec_type: Union[str, None, UnsetType] = UNSET + """Type of API, for example: OpenAPI, GraphQL, etc.""" + + api_spec_version: Union[str, None, UnsetType] = UNSET + """Version of the API specification.""" + + api_spec_name: Union[str, None, UnsetType] = UNSET + """Simple name of the API spec, if this asset is contained in an API spec.""" + + api_spec_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the API spec, if this asset is contained in an API spec.""" + + api_external_docs: Union[Dict[str, str], None, UnsetType] = UNSET + """External documentation of the API.""" + + api_is_auth_optional: Union[bool, None, UnsetType] = UNSET + """Whether authentication is optional (true) or required (false).""" + + api_is_object_reference: Union[bool, None, UnsetType] = UNSET + """If this asset refers to an APIObject""" + + api_object_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the APIObject that is referred to by this asset. When apiIsObjectReference is true.""" + + +class APISpecRelationshipAttributes(AssetRelationshipAttributes): + """APISpec-specific relationship attributes for nested API format.""" + + api_paths: Union[List[RelatedAPIPath], None, UnsetType] = UNSET + """Paths that exist within this API specification.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class APISpecNested(AssetNested): + """APISpec in nested API format for high-performance serialization.""" + + attributes: Union[APISpecAttributes, UnsetType] = UNSET + relationship_attributes: Union[APISpecRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[APISpecRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[APISpecRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_API_SPEC_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "api_paths", + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_api_spec_attrs(attrs: APISpecAttributes, obj: APISpec) -> None: + """Populate APISpec-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.api_spec_terms_of_service_url = obj.api_spec_terms_of_service_url + attrs.api_spec_contact_email = obj.api_spec_contact_email + attrs.api_spec_contact_name = obj.api_spec_contact_name + attrs.api_spec_contact_url = obj.api_spec_contact_url + attrs.api_spec_license_name = obj.api_spec_license_name + attrs.api_spec_license_url = obj.api_spec_license_url + attrs.api_spec_contract_version = obj.api_spec_contract_version + attrs.api_spec_service_alias = obj.api_spec_service_alias + attrs.api_spec_type = obj.api_spec_type + attrs.api_spec_version = obj.api_spec_version + attrs.api_spec_name = obj.api_spec_name + attrs.api_spec_qualified_name = obj.api_spec_qualified_name + attrs.api_external_docs = obj.api_external_docs + attrs.api_is_auth_optional = obj.api_is_auth_optional + attrs.api_is_object_reference = obj.api_is_object_reference + attrs.api_object_qualified_name = obj.api_object_qualified_name + + +def _extract_api_spec_attrs(attrs: APISpecAttributes) -> dict: + """Extract all APISpec attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["api_spec_terms_of_service_url"] = attrs.api_spec_terms_of_service_url + result["api_spec_contact_email"] = attrs.api_spec_contact_email + result["api_spec_contact_name"] = attrs.api_spec_contact_name + result["api_spec_contact_url"] = attrs.api_spec_contact_url + result["api_spec_license_name"] = attrs.api_spec_license_name + result["api_spec_license_url"] = attrs.api_spec_license_url + result["api_spec_contract_version"] = attrs.api_spec_contract_version + result["api_spec_service_alias"] = attrs.api_spec_service_alias + result["api_spec_type"] = attrs.api_spec_type + result["api_spec_version"] = attrs.api_spec_version + result["api_spec_name"] = attrs.api_spec_name + result["api_spec_qualified_name"] = attrs.api_spec_qualified_name + result["api_external_docs"] = attrs.api_external_docs + result["api_is_auth_optional"] = attrs.api_is_auth_optional + result["api_is_object_reference"] = attrs.api_is_object_reference + result["api_object_qualified_name"] = attrs.api_object_qualified_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _api_spec_to_nested(api_spec: APISpec) -> APISpecNested: + """Convert flat APISpec to nested format.""" + attrs = APISpecAttributes() + _populate_api_spec_attrs(attrs, api_spec) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + api_spec, _API_SPEC_REL_FIELDS, APISpecRelationshipAttributes + ) + return APISpecNested( + guid=api_spec.guid, + type_name=api_spec.type_name, + status=api_spec.status, + version=api_spec.version, + create_time=api_spec.create_time, + update_time=api_spec.update_time, + created_by=api_spec.created_by, + updated_by=api_spec.updated_by, + classifications=api_spec.classifications, + classification_names=api_spec.classification_names, + meanings=api_spec.meanings, + labels=api_spec.labels, + business_attributes=api_spec.business_attributes, + custom_attributes=api_spec.custom_attributes, + pending_tasks=api_spec.pending_tasks, + proxy=api_spec.proxy, + is_incomplete=api_spec.is_incomplete, + provenance_type=api_spec.provenance_type, + home_id=api_spec.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _api_spec_from_nested(nested: APISpecNested) -> APISpec: + """Convert nested format to flat APISpec.""" + attrs = nested.attributes if nested.attributes is not UNSET else APISpecAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _API_SPEC_REL_FIELDS, + APISpecRelationshipAttributes, + ) + return APISpec( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_api_spec_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _api_spec_to_nested_bytes(api_spec: APISpec, serde: Serde) -> bytes: + """Convert flat APISpec to nested JSON bytes.""" + return serde.encode(_api_spec_to_nested(api_spec)) + + +def _api_spec_from_nested_bytes(data: bytes, serde: Serde) -> APISpec: + """Convert nested JSON bytes to flat APISpec.""" + nested = serde.decode(data, APISpecNested) + return _api_spec_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + RelationField, +) + +APISpec.API_SPEC_TERMS_OF_SERVICE_URL = KeywordTextField( + "apiSpecTermsOfServiceURL", + "apiSpecTermsOfServiceURL", + "apiSpecTermsOfServiceURL.text", +) +APISpec.API_SPEC_CONTACT_EMAIL = KeywordTextField( + "apiSpecContactEmail", "apiSpecContactEmail", "apiSpecContactEmail.text" +) +APISpec.API_SPEC_CONTACT_NAME = KeywordTextField( + "apiSpecContactName", "apiSpecContactName", "apiSpecContactName.text" +) +APISpec.API_SPEC_CONTACT_URL = KeywordTextField( + "apiSpecContactURL", "apiSpecContactURL", "apiSpecContactURL.text" +) +APISpec.API_SPEC_LICENSE_NAME = KeywordField("apiSpecLicenseName", "apiSpecLicenseName") +APISpec.API_SPEC_LICENSE_URL = KeywordTextField( + "apiSpecLicenseURL", "apiSpecLicenseURL", "apiSpecLicenseURL.text" +) +APISpec.API_SPEC_CONTRACT_VERSION = KeywordField( + "apiSpecContractVersion", "apiSpecContractVersion" +) +APISpec.API_SPEC_SERVICE_ALIAS = KeywordTextField( + "apiSpecServiceAlias", "apiSpecServiceAlias", "apiSpecServiceAlias.text" +) +APISpec.API_SPEC_TYPE = KeywordField("apiSpecType", "apiSpecType") +APISpec.API_SPEC_VERSION = KeywordField("apiSpecVersion", "apiSpecVersion") +APISpec.API_SPEC_NAME = KeywordField("apiSpecName", "apiSpecName") +APISpec.API_SPEC_QUALIFIED_NAME = KeywordTextField( + "apiSpecQualifiedName", "apiSpecQualifiedName", "apiSpecQualifiedName.text" +) +APISpec.API_EXTERNAL_DOCS = KeywordField("apiExternalDocs", "apiExternalDocs") +APISpec.API_IS_AUTH_OPTIONAL = BooleanField("apiIsAuthOptional", "apiIsAuthOptional") +APISpec.API_IS_OBJECT_REFERENCE = BooleanField( + "apiIsObjectReference", "apiIsObjectReference" +) +APISpec.API_OBJECT_QUALIFIED_NAME = KeywordField( + "apiObjectQualifiedName", "apiObjectQualifiedName" +) +APISpec.API_PATHS = RelationField("apiPaths") +APISpec.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +APISpec.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +APISpec.ANOMALO_CHECKS = RelationField("anomaloChecks") +APISpec.APPLICATION = RelationField("application") +APISpec.APPLICATION_FIELD = RelationField("applicationField") +APISpec.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +APISpec.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +APISpec.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +APISpec.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +APISpec.METRICS = RelationField("metrics") +APISpec.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +APISpec.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +APISpec.MEANINGS = RelationField("meanings") +APISpec.MC_MONITORS = RelationField("mcMonitors") +APISpec.MC_INCIDENTS = RelationField("mcIncidents") +APISpec.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +APISpec.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +APISpec.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +APISpec.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +APISpec.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +APISpec.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +APISpec.FILES = RelationField("files") +APISpec.LINKS = RelationField("links") +APISpec.README = RelationField("readme") +APISpec.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +APISpec.SODA_CHECKS = RelationField("sodaChecks") +APISpec.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +APISpec.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/app.py b/pyatlan_v9/model/assets/app.py new file mode 100644 index 000000000..13b3dac62 --- /dev/null +++ b/pyatlan_v9/model/assets/app.py @@ -0,0 +1,532 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +App asset model with flattened inheritance. + +This module provides: +- App: Flat asset class (easy to use) +- AppAttributes: Nested attributes struct (extends AssetAttributes) +- AppNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .app_related import RelatedApplication, RelatedApplicationField + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class App(Asset): + """ + Base class for all App types. + """ + + APP_ID: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "App" + + app_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the application asset from the source system.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "App" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _app_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> App: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + App instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _app_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class AppAttributes(AssetAttributes): + """App-specific attributes for nested API format.""" + + app_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the application asset from the source system.""" + + +class AppRelationshipAttributes(AssetRelationshipAttributes): + """App-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class AppNested(AssetNested): + """App in nested API format for high-performance serialization.""" + + attributes: Union[AppAttributes, UnsetType] = UNSET + relationship_attributes: Union[AppRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[AppRelationshipAttributes, UnsetType] = UNSET + remove_relationship_attributes: Union[AppRelationshipAttributes, UnsetType] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_APP_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_app_attrs(attrs: AppAttributes, obj: App) -> None: + """Populate App-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.app_id = obj.app_id + + +def _extract_app_attrs(attrs: AppAttributes) -> dict: + """Extract all App attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["app_id"] = attrs.app_id + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _app_to_nested(app: App) -> AppNested: + """Convert flat App to nested format.""" + attrs = AppAttributes() + _populate_app_attrs(attrs, app) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + app, _APP_REL_FIELDS, AppRelationshipAttributes + ) + return AppNested( + guid=app.guid, + type_name=app.type_name, + status=app.status, + version=app.version, + create_time=app.create_time, + update_time=app.update_time, + created_by=app.created_by, + updated_by=app.updated_by, + classifications=app.classifications, + classification_names=app.classification_names, + meanings=app.meanings, + labels=app.labels, + business_attributes=app.business_attributes, + custom_attributes=app.custom_attributes, + pending_tasks=app.pending_tasks, + proxy=app.proxy, + is_incomplete=app.is_incomplete, + provenance_type=app.provenance_type, + home_id=app.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _app_from_nested(nested: AppNested) -> App: + """Convert nested format to flat App.""" + attrs = nested.attributes if nested.attributes is not UNSET else AppAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _APP_REL_FIELDS, + AppRelationshipAttributes, + ) + return App( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_app_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _app_to_nested_bytes(app: App, serde: Serde) -> bytes: + """Convert flat App to nested JSON bytes.""" + return serde.encode(_app_to_nested(app)) + + +def _app_from_nested_bytes(data: bytes, serde: Serde) -> App: + """Convert nested JSON bytes to flat App.""" + nested = serde.decode(data, AppNested) + return _app_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +App.APP_ID = KeywordField("appId", "appId") +App.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +App.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +App.ANOMALO_CHECKS = RelationField("anomaloChecks") +App.APPLICATION = RelationField("application") +App.APPLICATION_FIELD = RelationField("applicationField") +App.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +App.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +App.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +App.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +App.METRICS = RelationField("metrics") +App.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +App.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +App.MEANINGS = RelationField("meanings") +App.MC_MONITORS = RelationField("mcMonitors") +App.MC_INCIDENTS = RelationField("mcIncidents") +App.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +App.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +App.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +App.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +App.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +App.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +App.FILES = RelationField("files") +App.LINKS = RelationField("links") +App.README = RelationField("readme") +App.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +App.SODA_CHECKS = RelationField("sodaChecks") +App.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +App.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/app_related.py b/pyatlan_v9/model/assets/app_related.py new file mode 100644 index 000000000..55abaaf92 --- /dev/null +++ b/pyatlan_v9/model/assets/app_related.py @@ -0,0 +1,76 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for App module. + +This module contains all Related{Type} classes for the App type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Union + +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedCatalog +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedApp", + "RelatedApplication", + "RelatedApplicationField", +] + + +class RelatedApp(RelatedCatalog): + """ + Related entity reference for App assets. + + Extends RelatedCatalog with App-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "App" so it serializes correctly + + app_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the application asset from the source system.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "App" + + +class RelatedApplication(RelatedApp): + """ + Related entity reference for Application assets. + + Extends RelatedApp with Application-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Application" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Application" + + +class RelatedApplicationField(RelatedApp): + """ + Related entity reference for ApplicationField assets. + + Extends RelatedApp with ApplicationField-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "ApplicationField" so it serializes correctly + + application_parent_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the parent Application asset that contains this ApplicationField asset.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "ApplicationField" diff --git a/pyatlan_v9/model/assets/app_workflow_run.py b/pyatlan_v9/model/assets/app_workflow_run.py new file mode 100644 index 000000000..949b1fb05 --- /dev/null +++ b/pyatlan_v9/model/assets/app_workflow_run.py @@ -0,0 +1,772 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +AppWorkflowRun asset model with flattened inheritance. + +This module provides: +- AppWorkflowRun: Flat asset class (easy to use) +- AppWorkflowRunAttributes: Nested attributes struct (extends AssetAttributes) +- AppWorkflowRunNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .atlan_app_related import RelatedAtlanAppWorkflow +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class AppWorkflowRun(Asset): + """ + Represents an execution instance of a workflow run. + """ + + APP_WORKFLOW_RUN_LABEL: ClassVar[Any] = None + APP_WORKFLOW_RUN_STATUS: ClassVar[Any] = None + APP_WORKFLOW_RUN_STARTED_AT: ClassVar[Any] = None + APP_WORKFLOW_RUN_STARTED_BY: ClassVar[Any] = None + APP_WORKFLOW_RUN_COMPLETED_AT: ClassVar[Any] = None + APP_WORKFLOW_RUN_OUTPUTS: ClassVar[Any] = None + APP_WORKFLOW_RUN_STEPS: ClassVar[Any] = None + APP_WORKFLOW_RUN_APP_QUALIFIED_NAME: ClassVar[Any] = None + APP_WORKFLOW_RUN_APP_NAME: ClassVar[Any] = None + APP_WORKFLOW_RUN_APP_WORKFLOW_QUALIFIED_NAME: ClassVar[Any] = None + APP_WORKFLOW_RUN_APP_WORKFLOW_NAME: ClassVar[Any] = None + APP_WORKFLOW_RUN_APP_WORKFLOW_SLUG: ClassVar[Any] = None + APP_WORKFLOW_RUN_APP_WORKFLOW_VERSION: ClassVar[Any] = None + APP_WORKFLOW_RUN_TEMPORAL_RUN_ID: ClassVar[Any] = None + APP_WORKFLOW_RUN_IS_TEST_RUN: ClassVar[Any] = None + APP_WORKFLOW_RUN_DAG: ClassVar[Any] = None + APP_WORKFLOW_RUN_ERROR_HANDLING: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + ATLAN_APP_WORKFLOW: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "AppWorkflowRun" + + app_workflow_run_label: Union[str, None, UnsetType] = UNSET + """Root name for the workflow run.""" + + app_workflow_run_status: Union[str, None, UnsetType] = UNSET + """Overall execution status of the entire workflow run.""" + + app_workflow_run_started_at: Union[int, None, UnsetType] = UNSET + """Timestamp when the workflow run began execution.""" + + app_workflow_run_started_by: Union[str, None, UnsetType] = UNSET + """Username of the user who started the workflow run.""" + + app_workflow_run_completed_at: Union[int, None, UnsetType] = UNSET + """Timestamp when the workflow run finished execution.""" + + app_workflow_run_outputs: Union[Dict[str, str], None, UnsetType] = UNSET + """Final results produced by the workflow run.""" + + app_workflow_run_steps: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """Collection of individual workflow steps in this run.""" + + app_workflow_run_app_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the application this workflow run belongs to.""" + + app_workflow_run_app_name: Union[str, None, UnsetType] = UNSET + """Name of the application this workflow run belongs to.""" + + app_workflow_run_app_workflow_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the parent workflow.""" + + app_workflow_run_app_workflow_name: Union[str, None, UnsetType] = UNSET + """Name of the parent workflow.""" + + app_workflow_run_app_workflow_slug: Union[str, None, UnsetType] = UNSET + """Slug of the parent workflow.""" + + app_workflow_run_app_workflow_version: Union[str, None, UnsetType] = UNSET + """Version of the parent workflow.""" + + app_workflow_run_temporal_run_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the temporal run associated with this workflow execution.""" + + app_workflow_run_is_test_run: Union[bool, None, UnsetType] = UNSET + """Whether the workflow run is a test run.""" + + app_workflow_run_dag: Union[str, None, UnsetType] = UNSET + """Map of all activity steps for the workflow run (escaped JSON string).""" + + app_workflow_run_error_handling: Union[Dict[str, Any], None, UnsetType] = UNSET + """Error handling strategy for the workflow run.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + atlan_app_workflow: Union[RelatedAtlanAppWorkflow, None, UnsetType] = UNSET + """The workflow that contains the workflow run.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "AppWorkflowRun" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _app_workflow_run_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> AppWorkflowRun: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + AppWorkflowRun instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _app_workflow_run_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class AppWorkflowRunAttributes(AssetAttributes): + """AppWorkflowRun-specific attributes for nested API format.""" + + app_workflow_run_label: Union[str, None, UnsetType] = UNSET + """Root name for the workflow run.""" + + app_workflow_run_status: Union[str, None, UnsetType] = UNSET + """Overall execution status of the entire workflow run.""" + + app_workflow_run_started_at: Union[int, None, UnsetType] = UNSET + """Timestamp when the workflow run began execution.""" + + app_workflow_run_started_by: Union[str, None, UnsetType] = UNSET + """Username of the user who started the workflow run.""" + + app_workflow_run_completed_at: Union[int, None, UnsetType] = UNSET + """Timestamp when the workflow run finished execution.""" + + app_workflow_run_outputs: Union[Dict[str, str], None, UnsetType] = UNSET + """Final results produced by the workflow run.""" + + app_workflow_run_steps: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """Collection of individual workflow steps in this run.""" + + app_workflow_run_app_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the application this workflow run belongs to.""" + + app_workflow_run_app_name: Union[str, None, UnsetType] = UNSET + """Name of the application this workflow run belongs to.""" + + app_workflow_run_app_workflow_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the parent workflow.""" + + app_workflow_run_app_workflow_name: Union[str, None, UnsetType] = UNSET + """Name of the parent workflow.""" + + app_workflow_run_app_workflow_slug: Union[str, None, UnsetType] = UNSET + """Slug of the parent workflow.""" + + app_workflow_run_app_workflow_version: Union[str, None, UnsetType] = UNSET + """Version of the parent workflow.""" + + app_workflow_run_temporal_run_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the temporal run associated with this workflow execution.""" + + app_workflow_run_is_test_run: Union[bool, None, UnsetType] = UNSET + """Whether the workflow run is a test run.""" + + app_workflow_run_dag: Union[str, None, UnsetType] = UNSET + """Map of all activity steps for the workflow run (escaped JSON string).""" + + app_workflow_run_error_handling: Union[Dict[str, Any], None, UnsetType] = UNSET + """Error handling strategy for the workflow run.""" + + +class AppWorkflowRunRelationshipAttributes(AssetRelationshipAttributes): + """AppWorkflowRun-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + atlan_app_workflow: Union[RelatedAtlanAppWorkflow, None, UnsetType] = UNSET + """The workflow that contains the workflow run.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class AppWorkflowRunNested(AssetNested): + """AppWorkflowRun in nested API format for high-performance serialization.""" + + attributes: Union[AppWorkflowRunAttributes, UnsetType] = UNSET + relationship_attributes: Union[AppWorkflowRunRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + AppWorkflowRunRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + AppWorkflowRunRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_APP_WORKFLOW_RUN_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "atlan_app_workflow", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_app_workflow_run_attrs( + attrs: AppWorkflowRunAttributes, obj: AppWorkflowRun +) -> None: + """Populate AppWorkflowRun-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.app_workflow_run_label = obj.app_workflow_run_label + attrs.app_workflow_run_status = obj.app_workflow_run_status + attrs.app_workflow_run_started_at = obj.app_workflow_run_started_at + attrs.app_workflow_run_started_by = obj.app_workflow_run_started_by + attrs.app_workflow_run_completed_at = obj.app_workflow_run_completed_at + attrs.app_workflow_run_outputs = obj.app_workflow_run_outputs + attrs.app_workflow_run_steps = obj.app_workflow_run_steps + attrs.app_workflow_run_app_qualified_name = obj.app_workflow_run_app_qualified_name + attrs.app_workflow_run_app_name = obj.app_workflow_run_app_name + attrs.app_workflow_run_app_workflow_qualified_name = ( + obj.app_workflow_run_app_workflow_qualified_name + ) + attrs.app_workflow_run_app_workflow_name = obj.app_workflow_run_app_workflow_name + attrs.app_workflow_run_app_workflow_slug = obj.app_workflow_run_app_workflow_slug + attrs.app_workflow_run_app_workflow_version = ( + obj.app_workflow_run_app_workflow_version + ) + attrs.app_workflow_run_temporal_run_id = obj.app_workflow_run_temporal_run_id + attrs.app_workflow_run_is_test_run = obj.app_workflow_run_is_test_run + attrs.app_workflow_run_dag = obj.app_workflow_run_dag + attrs.app_workflow_run_error_handling = obj.app_workflow_run_error_handling + + +def _extract_app_workflow_run_attrs(attrs: AppWorkflowRunAttributes) -> dict: + """Extract all AppWorkflowRun attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["app_workflow_run_label"] = attrs.app_workflow_run_label + result["app_workflow_run_status"] = attrs.app_workflow_run_status + result["app_workflow_run_started_at"] = attrs.app_workflow_run_started_at + result["app_workflow_run_started_by"] = attrs.app_workflow_run_started_by + result["app_workflow_run_completed_at"] = attrs.app_workflow_run_completed_at + result["app_workflow_run_outputs"] = attrs.app_workflow_run_outputs + result["app_workflow_run_steps"] = attrs.app_workflow_run_steps + result["app_workflow_run_app_qualified_name"] = ( + attrs.app_workflow_run_app_qualified_name + ) + result["app_workflow_run_app_name"] = attrs.app_workflow_run_app_name + result["app_workflow_run_app_workflow_qualified_name"] = ( + attrs.app_workflow_run_app_workflow_qualified_name + ) + result["app_workflow_run_app_workflow_name"] = ( + attrs.app_workflow_run_app_workflow_name + ) + result["app_workflow_run_app_workflow_slug"] = ( + attrs.app_workflow_run_app_workflow_slug + ) + result["app_workflow_run_app_workflow_version"] = ( + attrs.app_workflow_run_app_workflow_version + ) + result["app_workflow_run_temporal_run_id"] = attrs.app_workflow_run_temporal_run_id + result["app_workflow_run_is_test_run"] = attrs.app_workflow_run_is_test_run + result["app_workflow_run_dag"] = attrs.app_workflow_run_dag + result["app_workflow_run_error_handling"] = attrs.app_workflow_run_error_handling + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _app_workflow_run_to_nested( + app_workflow_run: AppWorkflowRun, +) -> AppWorkflowRunNested: + """Convert flat AppWorkflowRun to nested format.""" + attrs = AppWorkflowRunAttributes() + _populate_app_workflow_run_attrs(attrs, app_workflow_run) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + app_workflow_run, + _APP_WORKFLOW_RUN_REL_FIELDS, + AppWorkflowRunRelationshipAttributes, + ) + return AppWorkflowRunNested( + guid=app_workflow_run.guid, + type_name=app_workflow_run.type_name, + status=app_workflow_run.status, + version=app_workflow_run.version, + create_time=app_workflow_run.create_time, + update_time=app_workflow_run.update_time, + created_by=app_workflow_run.created_by, + updated_by=app_workflow_run.updated_by, + classifications=app_workflow_run.classifications, + classification_names=app_workflow_run.classification_names, + meanings=app_workflow_run.meanings, + labels=app_workflow_run.labels, + business_attributes=app_workflow_run.business_attributes, + custom_attributes=app_workflow_run.custom_attributes, + pending_tasks=app_workflow_run.pending_tasks, + proxy=app_workflow_run.proxy, + is_incomplete=app_workflow_run.is_incomplete, + provenance_type=app_workflow_run.provenance_type, + home_id=app_workflow_run.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _app_workflow_run_from_nested(nested: AppWorkflowRunNested) -> AppWorkflowRun: + """Convert nested format to flat AppWorkflowRun.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else AppWorkflowRunAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _APP_WORKFLOW_RUN_REL_FIELDS, + AppWorkflowRunRelationshipAttributes, + ) + return AppWorkflowRun( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_app_workflow_run_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _app_workflow_run_to_nested_bytes( + app_workflow_run: AppWorkflowRun, serde: Serde +) -> bytes: + """Convert flat AppWorkflowRun to nested JSON bytes.""" + return serde.encode(_app_workflow_run_to_nested(app_workflow_run)) + + +def _app_workflow_run_from_nested_bytes(data: bytes, serde: Serde) -> AppWorkflowRun: + """Convert nested JSON bytes to flat AppWorkflowRun.""" + nested = serde.decode(data, AppWorkflowRunNested) + return _app_workflow_run_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, + TextField, +) + +AppWorkflowRun.APP_WORKFLOW_RUN_LABEL = KeywordField( + "appWorkflowRunLabel", "appWorkflowRunLabel" +) +AppWorkflowRun.APP_WORKFLOW_RUN_STATUS = KeywordField( + "appWorkflowRunStatus", "appWorkflowRunStatus" +) +AppWorkflowRun.APP_WORKFLOW_RUN_STARTED_AT = NumericField( + "appWorkflowRunStartedAt", "appWorkflowRunStartedAt" +) +AppWorkflowRun.APP_WORKFLOW_RUN_STARTED_BY = KeywordField( + "appWorkflowRunStartedBy", "appWorkflowRunStartedBy" +) +AppWorkflowRun.APP_WORKFLOW_RUN_COMPLETED_AT = NumericField( + "appWorkflowRunCompletedAt", "appWorkflowRunCompletedAt" +) +AppWorkflowRun.APP_WORKFLOW_RUN_OUTPUTS = KeywordField( + "appWorkflowRunOutputs", "appWorkflowRunOutputs" +) +AppWorkflowRun.APP_WORKFLOW_RUN_STEPS = KeywordField( + "appWorkflowRunSteps", "appWorkflowRunSteps" +) +AppWorkflowRun.APP_WORKFLOW_RUN_APP_QUALIFIED_NAME = KeywordField( + "appWorkflowRunAppQualifiedName", "appWorkflowRunAppQualifiedName" +) +AppWorkflowRun.APP_WORKFLOW_RUN_APP_NAME = KeywordField( + "appWorkflowRunAppName", "appWorkflowRunAppName" +) +AppWorkflowRun.APP_WORKFLOW_RUN_APP_WORKFLOW_QUALIFIED_NAME = KeywordField( + "appWorkflowRunAppWorkflowQualifiedName", "appWorkflowRunAppWorkflowQualifiedName" +) +AppWorkflowRun.APP_WORKFLOW_RUN_APP_WORKFLOW_NAME = KeywordField( + "appWorkflowRunAppWorkflowName", "appWorkflowRunAppWorkflowName" +) +AppWorkflowRun.APP_WORKFLOW_RUN_APP_WORKFLOW_SLUG = KeywordField( + "appWorkflowRunAppWorkflowSlug", "appWorkflowRunAppWorkflowSlug" +) +AppWorkflowRun.APP_WORKFLOW_RUN_APP_WORKFLOW_VERSION = KeywordField( + "appWorkflowRunAppWorkflowVersion", "appWorkflowRunAppWorkflowVersion" +) +AppWorkflowRun.APP_WORKFLOW_RUN_TEMPORAL_RUN_ID = KeywordField( + "appWorkflowRunTemporalRunId", "appWorkflowRunTemporalRunId" +) +AppWorkflowRun.APP_WORKFLOW_RUN_IS_TEST_RUN = BooleanField( + "appWorkflowRunIsTestRun", "appWorkflowRunIsTestRun" +) +AppWorkflowRun.APP_WORKFLOW_RUN_DAG = TextField( + "appWorkflowRunDag", "appWorkflowRunDag" +) +AppWorkflowRun.APP_WORKFLOW_RUN_ERROR_HANDLING = KeywordField( + "appWorkflowRunErrorHandling", "appWorkflowRunErrorHandling" +) +AppWorkflowRun.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +AppWorkflowRun.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +AppWorkflowRun.ANOMALO_CHECKS = RelationField("anomaloChecks") +AppWorkflowRun.APPLICATION = RelationField("application") +AppWorkflowRun.APPLICATION_FIELD = RelationField("applicationField") +AppWorkflowRun.ATLAN_APP_WORKFLOW = RelationField("atlanAppWorkflow") +AppWorkflowRun.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +AppWorkflowRun.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +AppWorkflowRun.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +AppWorkflowRun.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +AppWorkflowRun.METRICS = RelationField("metrics") +AppWorkflowRun.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +AppWorkflowRun.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +AppWorkflowRun.MEANINGS = RelationField("meanings") +AppWorkflowRun.MC_MONITORS = RelationField("mcMonitors") +AppWorkflowRun.MC_INCIDENTS = RelationField("mcIncidents") +AppWorkflowRun.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +AppWorkflowRun.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +AppWorkflowRun.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +AppWorkflowRun.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +AppWorkflowRun.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +AppWorkflowRun.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +AppWorkflowRun.FILES = RelationField("files") +AppWorkflowRun.LINKS = RelationField("links") +AppWorkflowRun.README = RelationField("readme") +AppWorkflowRun.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +AppWorkflowRun.SODA_CHECKS = RelationField("sodaChecks") +AppWorkflowRun.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +AppWorkflowRun.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/app_workflow_run_related.py b/pyatlan_v9/model/assets/app_workflow_run_related.py new file mode 100644 index 000000000..41fc91c14 --- /dev/null +++ b/pyatlan_v9/model/assets/app_workflow_run_related.py @@ -0,0 +1,89 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for AppWorkflowRun module. + +This module contains all Related{Type} classes for the AppWorkflowRun type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Any, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedCatalog +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedAppWorkflowRun", +] + + +class RelatedAppWorkflowRun(RelatedCatalog): + """ + Related entity reference for AppWorkflowRun assets. + + Extends RelatedCatalog with AppWorkflowRun-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "AppWorkflowRun" so it serializes correctly + + app_workflow_run_label: Union[str, None, UnsetType] = UNSET + """Root name for the workflow run.""" + + app_workflow_run_status: Union[str, None, UnsetType] = UNSET + """Overall execution status of the entire workflow run.""" + + app_workflow_run_started_at: Union[int, None, UnsetType] = UNSET + """Timestamp when the workflow run began execution.""" + + app_workflow_run_started_by: Union[str, None, UnsetType] = UNSET + """Username of the user who started the workflow run.""" + + app_workflow_run_completed_at: Union[int, None, UnsetType] = UNSET + """Timestamp when the workflow run finished execution.""" + + app_workflow_run_outputs: Union[Dict[str, str], None, UnsetType] = UNSET + """Final results produced by the workflow run.""" + + app_workflow_run_steps: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """Collection of individual workflow steps in this run.""" + + app_workflow_run_app_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the application this workflow run belongs to.""" + + app_workflow_run_app_name: Union[str, None, UnsetType] = UNSET + """Name of the application this workflow run belongs to.""" + + app_workflow_run_app_workflow_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the parent workflow.""" + + app_workflow_run_app_workflow_name: Union[str, None, UnsetType] = UNSET + """Name of the parent workflow.""" + + app_workflow_run_app_workflow_slug: Union[str, None, UnsetType] = UNSET + """Slug of the parent workflow.""" + + app_workflow_run_app_workflow_version: Union[str, None, UnsetType] = UNSET + """Version of the parent workflow.""" + + app_workflow_run_temporal_run_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the temporal run associated with this workflow execution.""" + + app_workflow_run_is_test_run: Union[bool, None, UnsetType] = UNSET + """Whether the workflow run is a test run.""" + + app_workflow_run_dag: Union[str, None, UnsetType] = UNSET + """Map of all activity steps for the workflow run (escaped JSON string).""" + + app_workflow_run_error_handling: Union[Dict[str, Any], None, UnsetType] = UNSET + """Error handling strategy for the workflow run.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "AppWorkflowRun" diff --git a/pyatlan_v9/model/assets/application.py b/pyatlan_v9/model/assets/application.py new file mode 100644 index 000000000..289b0fbd3 --- /dev/null +++ b/pyatlan_v9/model/assets/application.py @@ -0,0 +1,596 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Application asset model with flattened inheritance. + +This module provides: +- Application: Flat asset class (easy to use) +- ApplicationAttributes: Nested attributes struct (extends AssetAttributes) +- ApplicationNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .asset_related import RelatedAsset +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .app_related import RelatedApplication, RelatedApplicationField + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Application(Asset): + """ + Instances of Application in Atlan. + """ + + APP_ID: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION_OWNED_ASSETS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + APPLICATION_CHILD_FIELDS: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Application" + + app_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the application asset from the source system.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application_owned_assets: Union[List[RelatedAsset], None, UnsetType] = UNSET + """Assets owned by the Application.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + application_child_fields: Union[List[RelatedApplicationField], None, UnsetType] = ( + UNSET + ) + """ApplicationFields owned by the Application.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Application" + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + connection_qualified_name: str, + ) -> "Application": + """Create a new Application asset.""" + validate_required_fields( + ["name", "connection_qualified_name"], [name, connection_qualified_name] + ) + connector_name = ( + connection_qualified_name.split("/")[1] + if len(connection_qualified_name.split("/")) > 1 + else "" + ) + return cls( + name=name, + qualified_name=f"{connection_qualified_name}/{name}", + connection_qualified_name=connection_qualified_name, + connector_name=connector_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "Application": + """Create an Application instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "Application": + """Return only fields required for update operations.""" + return Application.updater(qualified_name=self.qualified_name, name=self.name) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _application_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Application: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Application instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _application_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class ApplicationAttributes(AssetAttributes): + """Application-specific attributes for nested API format.""" + + app_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the application asset from the source system.""" + + +class ApplicationRelationshipAttributes(AssetRelationshipAttributes): + """Application-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application_owned_assets: Union[List[RelatedAsset], None, UnsetType] = UNSET + """Assets owned by the Application.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + application_child_fields: Union[List[RelatedApplicationField], None, UnsetType] = ( + UNSET + ) + """ApplicationFields owned by the Application.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class ApplicationNested(AssetNested): + """Application in nested API format for high-performance serialization.""" + + attributes: Union[ApplicationAttributes, UnsetType] = UNSET + relationship_attributes: Union[ApplicationRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + ApplicationRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + ApplicationRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_APPLICATION_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application_owned_assets", + "application", + "application_field", + "application_child_fields", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_application_attrs(attrs: ApplicationAttributes, obj: Application) -> None: + """Populate Application-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.app_id = obj.app_id + + +def _extract_application_attrs(attrs: ApplicationAttributes) -> dict: + """Extract all Application attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["app_id"] = attrs.app_id + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _application_to_nested(application: Application) -> ApplicationNested: + """Convert flat Application to nested format.""" + attrs = ApplicationAttributes() + _populate_application_attrs(attrs, application) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + application, _APPLICATION_REL_FIELDS, ApplicationRelationshipAttributes + ) + return ApplicationNested( + guid=application.guid, + type_name=application.type_name, + status=application.status, + version=application.version, + create_time=application.create_time, + update_time=application.update_time, + created_by=application.created_by, + updated_by=application.updated_by, + classifications=application.classifications, + classification_names=application.classification_names, + meanings=application.meanings, + labels=application.labels, + business_attributes=application.business_attributes, + custom_attributes=application.custom_attributes, + pending_tasks=application.pending_tasks, + proxy=application.proxy, + is_incomplete=application.is_incomplete, + provenance_type=application.provenance_type, + home_id=application.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _application_from_nested(nested: ApplicationNested) -> Application: + """Convert nested format to flat Application.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else ApplicationAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _APPLICATION_REL_FIELDS, + ApplicationRelationshipAttributes, + ) + return Application( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_application_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _application_to_nested_bytes(application: Application, serde: Serde) -> bytes: + """Convert flat Application to nested JSON bytes.""" + return serde.encode(_application_to_nested(application)) + + +def _application_from_nested_bytes(data: bytes, serde: Serde) -> Application: + """Convert nested JSON bytes to flat Application.""" + nested = serde.decode(data, ApplicationNested) + return _application_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +Application.APP_ID = KeywordField("appId", "appId") +Application.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Application.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Application.ANOMALO_CHECKS = RelationField("anomaloChecks") +Application.APPLICATION_OWNED_ASSETS = RelationField("applicationOwnedAssets") +Application.APPLICATION = RelationField("application") +Application.APPLICATION_FIELD = RelationField("applicationField") +Application.APPLICATION_CHILD_FIELDS = RelationField("applicationChildFields") +Application.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Application.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Application.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Application.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Application.METRICS = RelationField("metrics") +Application.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Application.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Application.MEANINGS = RelationField("meanings") +Application.MC_MONITORS = RelationField("mcMonitors") +Application.MC_INCIDENTS = RelationField("mcIncidents") +Application.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Application.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Application.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Application.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Application.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Application.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Application.FILES = RelationField("files") +Application.LINKS = RelationField("links") +Application.README = RelationField("readme") +Application.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Application.SODA_CHECKS = RelationField("sodaChecks") +Application.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Application.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/application_field.py b/pyatlan_v9/model/assets/application_field.py new file mode 100644 index 000000000..4c80fcae9 --- /dev/null +++ b/pyatlan_v9/model/assets/application_field.py @@ -0,0 +1,646 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +ApplicationField asset model with flattened inheritance. + +This module provides: +- ApplicationField: Flat asset class (easy to use) +- ApplicationFieldAttributes: Nested attributes struct (extends AssetAttributes) +- ApplicationFieldNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .asset_related import RelatedAsset +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .app_related import RelatedApplication, RelatedApplicationField + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class ApplicationField(Asset): + """ + Instances of ApplicationField in Atlan. + """ + + APPLICATION_PARENT_QUALIFIED_NAME: ClassVar[Any] = None + APP_ID: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD_OWNED_ASSETS: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + APPLICATION_PARENT: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "ApplicationField" + + application_parent_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the parent Application asset that contains this ApplicationField asset.""" + + app_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the application asset from the source system.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field_owned_assets: Union[List[RelatedAsset], None, UnsetType] = UNSET + """Assets owned by the ApplicationField.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + application_parent: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the ApplicationField.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "ApplicationField" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + application_qualified_name: str, + connection_qualified_name: str | None = None, + ) -> "ApplicationField": + """Create a new ApplicationField asset.""" + validate_required_fields( + ["name", "application_qualified_name"], [name, application_qualified_name] + ) + if connection_qualified_name: + connector_name = ( + connection_qualified_name.split("/")[1] + if len(connection_qualified_name.split("/")) > 1 + else "" + ) + else: + fields = application_qualified_name.split("/") + if len(fields) < 3: + raise ValueError("application_qualified_name is invalid") + connection_qualified_name = "/".join(fields[:3]) + connector_name = fields[1] + return cls( + name=name, + qualified_name=f"{application_qualified_name}/{name}", + connection_qualified_name=connection_qualified_name, + connector_name=connector_name, + application_parent_qualified_name=application_qualified_name, + application_parent=RelatedApplication( + unique_attributes={"qualifiedName": application_qualified_name} + ), + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "ApplicationField": + """Create an ApplicationField instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "ApplicationField": + """Return only fields required for update operations.""" + return ApplicationField.updater( + qualified_name=self.qualified_name, + name=self.name, + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _application_field_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> ApplicationField: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + ApplicationField instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _application_field_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class ApplicationFieldAttributes(AssetAttributes): + """ApplicationField-specific attributes for nested API format.""" + + application_parent_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the parent Application asset that contains this ApplicationField asset.""" + + app_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the application asset from the source system.""" + + +class ApplicationFieldRelationshipAttributes(AssetRelationshipAttributes): + """ApplicationField-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field_owned_assets: Union[List[RelatedAsset], None, UnsetType] = UNSET + """Assets owned by the ApplicationField.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + application_parent: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the ApplicationField.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class ApplicationFieldNested(AssetNested): + """ApplicationField in nested API format for high-performance serialization.""" + + attributes: Union[ApplicationFieldAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + ApplicationFieldRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + ApplicationFieldRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + ApplicationFieldRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_APPLICATION_FIELD_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field_owned_assets", + "application_field", + "application_parent", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_application_field_attrs( + attrs: ApplicationFieldAttributes, obj: ApplicationField +) -> None: + """Populate ApplicationField-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.application_parent_qualified_name = obj.application_parent_qualified_name + attrs.app_id = obj.app_id + + +def _extract_application_field_attrs(attrs: ApplicationFieldAttributes) -> dict: + """Extract all ApplicationField attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["application_parent_qualified_name"] = ( + attrs.application_parent_qualified_name + ) + result["app_id"] = attrs.app_id + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _application_field_to_nested( + application_field: ApplicationField, +) -> ApplicationFieldNested: + """Convert flat ApplicationField to nested format.""" + attrs = ApplicationFieldAttributes() + _populate_application_field_attrs(attrs, application_field) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + application_field, + _APPLICATION_FIELD_REL_FIELDS, + ApplicationFieldRelationshipAttributes, + ) + return ApplicationFieldNested( + guid=application_field.guid, + type_name=application_field.type_name, + status=application_field.status, + version=application_field.version, + create_time=application_field.create_time, + update_time=application_field.update_time, + created_by=application_field.created_by, + updated_by=application_field.updated_by, + classifications=application_field.classifications, + classification_names=application_field.classification_names, + meanings=application_field.meanings, + labels=application_field.labels, + business_attributes=application_field.business_attributes, + custom_attributes=application_field.custom_attributes, + pending_tasks=application_field.pending_tasks, + proxy=application_field.proxy, + is_incomplete=application_field.is_incomplete, + provenance_type=application_field.provenance_type, + home_id=application_field.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _application_field_from_nested(nested: ApplicationFieldNested) -> ApplicationField: + """Convert nested format to flat ApplicationField.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else ApplicationFieldAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _APPLICATION_FIELD_REL_FIELDS, + ApplicationFieldRelationshipAttributes, + ) + return ApplicationField( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_application_field_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _application_field_to_nested_bytes( + application_field: ApplicationField, serde: Serde +) -> bytes: + """Convert flat ApplicationField to nested JSON bytes.""" + return serde.encode(_application_field_to_nested(application_field)) + + +def _application_field_from_nested_bytes(data: bytes, serde: Serde) -> ApplicationField: + """Convert nested JSON bytes to flat ApplicationField.""" + nested = serde.decode(data, ApplicationFieldNested) + return _application_field_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +ApplicationField.APPLICATION_PARENT_QUALIFIED_NAME = KeywordField( + "applicationParentQualifiedName", "applicationParentQualifiedName" +) +ApplicationField.APP_ID = KeywordField("appId", "appId") +ApplicationField.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +ApplicationField.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +ApplicationField.ANOMALO_CHECKS = RelationField("anomaloChecks") +ApplicationField.APPLICATION = RelationField("application") +ApplicationField.APPLICATION_FIELD_OWNED_ASSETS = RelationField( + "applicationFieldOwnedAssets" +) +ApplicationField.APPLICATION_FIELD = RelationField("applicationField") +ApplicationField.APPLICATION_PARENT = RelationField("applicationParent") +ApplicationField.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +ApplicationField.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +ApplicationField.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +ApplicationField.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +ApplicationField.METRICS = RelationField("metrics") +ApplicationField.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +ApplicationField.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +ApplicationField.MEANINGS = RelationField("meanings") +ApplicationField.MC_MONITORS = RelationField("mcMonitors") +ApplicationField.MC_INCIDENTS = RelationField("mcIncidents") +ApplicationField.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +ApplicationField.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +ApplicationField.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +ApplicationField.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +ApplicationField.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +ApplicationField.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +ApplicationField.FILES = RelationField("files") +ApplicationField.LINKS = RelationField("links") +ApplicationField.README = RelationField("readme") +ApplicationField.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +ApplicationField.SODA_CHECKS = RelationField("sodaChecks") +ApplicationField.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +ApplicationField.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/asset.py b/pyatlan_v9/model/assets/asset.py new file mode 100644 index 000000000..f74ea0a4b --- /dev/null +++ b/pyatlan_v9/model/assets/asset.py @@ -0,0 +1,3090 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Asset asset model with flattened inheritance. + +This module provides: +- Asset: Flat asset class (easy to use) +- AssetAttributes: Nested attributes struct (extends AssetAttributes) +- AssetNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Set, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .referenceable import ( + _REFERENCEABLE_REL_FIELDS, + Referenceable, + ReferenceableAttributes, + ReferenceableNested, + ReferenceableRelationshipAttributes, + _extract_referenceable_attrs, + _populate_referenceable_attrs, +) +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from pyatlan_v9.model.assets.related_entity import SaveSemantic +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.core import Announcement +from pyatlan_v9.model.enums import AnnouncementType +from pyatlan_v9.model.serde import Serde, get_serde + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +class Asset(Referenceable): + """ + Base class for all assets. + """ + + NAME: ClassVar[Any] = None + DISPLAY_NAME: ClassVar[Any] = None + DESCRIPTION: ClassVar[Any] = None + ASSET_SOURCE_README: ClassVar[Any] = None + USER_DESCRIPTION: ClassVar[Any] = None + ASSET_AI_GENERATED_DESCRIPTION: ClassVar[Any] = None + ASSET_AI_GENERATED_DESCRIPTION_CONFIDENCE: ClassVar[Any] = None + ASSET_AI_GENERATED_DESCRIPTION_REASONING: ClassVar[Any] = None + TENANT_ID: ClassVar[Any] = None + CERTIFICATE_STATUS: ClassVar[Any] = None + CERTIFICATE_STATUS_MESSAGE: ClassVar[Any] = None + CERTIFICATE_UPDATED_BY: ClassVar[Any] = None + CERTIFICATE_UPDATED_AT: ClassVar[Any] = None + ANNOUNCEMENT_TITLE: ClassVar[Any] = None + ANNOUNCEMENT_MESSAGE: ClassVar[Any] = None + ANNOUNCEMENT_TYPE: ClassVar[Any] = None + ANNOUNCEMENT_UPDATED_AT: ClassVar[Any] = None + ANNOUNCEMENT_UPDATED_BY: ClassVar[Any] = None + OWNER_USERS: ClassVar[Any] = None + OWNER_GROUPS: ClassVar[Any] = None + ADMIN_USERS: ClassVar[Any] = None + ADMIN_GROUPS: ClassVar[Any] = None + VIEWER_USERS: ClassVar[Any] = None + VIEWER_GROUPS: ClassVar[Any] = None + CONNECTOR_NAME: ClassVar[Any] = None + CONNECTION_NAME: ClassVar[Any] = None + CONNECTION_QUALIFIED_NAME: ClassVar[Any] = None + HAS_LINEAGE: ClassVar[Any] = None + IS_DISCOVERABLE: ClassVar[Any] = None + IS_EDITABLE: ClassVar[Any] = None + SUB_TYPE: ClassVar[Any] = None + VIEW_SCORE: ClassVar[Any] = None + POPULARITY_SCORE: ClassVar[Any] = None + SOURCE_OWNERS: ClassVar[Any] = None + ASSET_SOURCE_ID: ClassVar[Any] = None + SOURCE_CREATED_BY: ClassVar[Any] = None + SOURCE_CREATED_AT: ClassVar[Any] = None + SOURCE_UPDATED_AT: ClassVar[Any] = None + SOURCE_UPDATED_BY: ClassVar[Any] = None + SOURCE_URL: ClassVar[Any] = None + SOURCE_EMBED_URL: ClassVar[Any] = None + LAST_SYNC_WORKFLOW_NAME: ClassVar[Any] = None + LAST_SYNC_RUN_AT: ClassVar[Any] = None + LAST_SYNC_RUN: ClassVar[Any] = None + ADMIN_ROLES: ClassVar[Any] = None + SOURCE_READ_COUNT: ClassVar[Any] = None + SOURCE_READ_USER_COUNT: ClassVar[Any] = None + SOURCE_LAST_READ_AT: ClassVar[Any] = None + LAST_ROW_CHANGED_AT: ClassVar[Any] = None + SOURCE_TOTAL_COST: ClassVar[Any] = None + SOURCE_COST_UNIT: ClassVar[Any] = None + SOURCE_READ_QUERY_COST: ClassVar[Any] = None + SOURCE_READ_RECENT_USER_LIST: ClassVar[Any] = None + SOURCE_READ_RECENT_USER_RECORD_LIST: ClassVar[Any] = None + SOURCE_READ_TOP_USER_LIST: ClassVar[Any] = None + SOURCE_READ_TOP_USER_RECORD_LIST: ClassVar[Any] = None + SOURCE_READ_POPULAR_QUERY_RECORD_LIST: ClassVar[Any] = None + SOURCE_READ_EXPENSIVE_QUERY_RECORD_LIST: ClassVar[Any] = None + SOURCE_READ_SLOW_QUERY_RECORD_LIST: ClassVar[Any] = None + SOURCE_QUERY_COMPUTE_COST_LIST: ClassVar[Any] = None + SOURCE_QUERY_COMPUTE_COST_RECORD_LIST: ClassVar[Any] = None + DBT_QUALIFIED_NAME: ClassVar[Any] = None + ASSET_DBT_WORKFLOW_LAST_UPDATED: ClassVar[Any] = None + ASSET_DBT_ALIAS: ClassVar[Any] = None + ASSET_DBT_META: ClassVar[Any] = None + ASSET_DBT_UNIQUE_ID: ClassVar[Any] = None + ASSET_DBT_ACCOUNT_NAME: ClassVar[Any] = None + ASSET_DBT_PROJECT_NAME: ClassVar[Any] = None + ASSET_DBT_PACKAGE_NAME: ClassVar[Any] = None + ASSET_DBT_JOB_NAME: ClassVar[Any] = None + ASSET_DBT_JOB_SCHEDULE: ClassVar[Any] = None + ASSET_DBT_JOB_STATUS: ClassVar[Any] = None + ASSET_DBT_TEST_STATUS: ClassVar[Any] = None + ASSET_DBT_JOB_SCHEDULE_CRON_HUMANIZED: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_URL: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_CREATED_AT: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_UPDATED_AT: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_DEQUED_AT: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_STARTED_AT: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_TOTAL_DURATION: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_TOTAL_DURATION_HUMANIZED: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_QUEUED_DURATION: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_QUEUED_DURATION_HUMANIZED: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_RUN_DURATION: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_RUN_DURATION_HUMANIZED: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_GIT_BRANCH: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_GIT_SHA: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_STATUS_MESSAGE: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_OWNER_THREAD_ID: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_EXECUTED_BY_THREAD_ID: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_ARTIFACTS_SAVED: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_ARTIFACT_S3_PATH: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_HAS_DOCS_GENERATED: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_HAS_SOURCES_GENERATED: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_NOTIFICATIONS_SENT: ClassVar[Any] = None + ASSET_DBT_JOB_NEXT_RUN: ClassVar[Any] = None + ASSET_DBT_JOB_NEXT_RUN_HUMANIZED: ClassVar[Any] = None + ASSET_DBT_ENVIRONMENT_NAME: ClassVar[Any] = None + ASSET_DBT_ENVIRONMENT_DBT_VERSION: ClassVar[Any] = None + ASSET_DBT_TAGS: ClassVar[Any] = None + ASSET_DBT_SEMANTIC_LAYER_PROXY_URL: ClassVar[Any] = None + ASSET_DBT_SOURCE_FRESHNESS_CRITERIA: ClassVar[Any] = None + SAMPLE_DATA_URL: ClassVar[Any] = None + ASSET_TAGS: ClassVar[Any] = None + ASSET_MC_INCIDENT_NAMES: ClassVar[Any] = None + ASSET_MC_INCIDENT_QUALIFIED_NAMES: ClassVar[Any] = None + ASSET_MC_ALERT_QUALIFIED_NAMES: ClassVar[Any] = None + ASSET_MC_MONITOR_NAMES: ClassVar[Any] = None + ASSET_MC_MONITOR_QUALIFIED_NAMES: ClassVar[Any] = None + ASSET_MC_MONITOR_STATUSES: ClassVar[Any] = None + ASSET_MC_MONITOR_TYPES: ClassVar[Any] = None + ASSET_MC_MONITOR_SCHEDULE_TYPES: ClassVar[Any] = None + ASSET_MC_INCIDENT_TYPES: ClassVar[Any] = None + ASSET_MC_INCIDENT_SUB_TYPES: ClassVar[Any] = None + ASSET_MC_INCIDENT_SEVERITIES: ClassVar[Any] = None + ASSET_MC_INCIDENT_PRIORITIES: ClassVar[Any] = None + ASSET_MC_INCIDENT_STATES: ClassVar[Any] = None + ASSET_MC_IS_MONITORED: ClassVar[Any] = None + ASSET_MC_LAST_SYNC_RUN_AT: ClassVar[Any] = None + STARRED_BY: ClassVar[Any] = None + STARRED_DETAILS_LIST: ClassVar[Any] = None + STARRED_COUNT: ClassVar[Any] = None + ASSET_ANOMALO_DQ_STATUS: ClassVar[Any] = None + ASSET_ANOMALO_CHECK_COUNT: ClassVar[Any] = None + ASSET_ANOMALO_FAILED_CHECK_COUNT: ClassVar[Any] = None + ASSET_ANOMALO_CHECK_STATUSES: ClassVar[Any] = None + ASSET_ANOMALO_LAST_CHECK_RUN_AT: ClassVar[Any] = None + ASSET_ANOMALO_APPLIED_CHECK_TYPES: ClassVar[Any] = None + ASSET_ANOMALO_FAILED_CHECK_TYPES: ClassVar[Any] = None + ASSET_ANOMALO_SOURCE_URL: ClassVar[Any] = None + ASSET_SODA_DQ_STATUS: ClassVar[Any] = None + ASSET_SODA_CHECK_COUNT: ClassVar[Any] = None + ASSET_SODA_LAST_SYNC_RUN_AT: ClassVar[Any] = None + ASSET_SODA_LAST_SCAN_AT: ClassVar[Any] = None + ASSET_SODA_CHECK_STATUSES: ClassVar[Any] = None + ASSET_SODA_SOURCE_URL: ClassVar[Any] = None + ASSET_ICON: ClassVar[Any] = None + ASSET_EXTERNAL_DQ_METADATA_DETAILS: ClassVar[Any] = None + IS_PARTIAL: ClassVar[Any] = None + IS_AI_GENERATED: ClassVar[Any] = None + ASSET_COVER_IMAGE: ClassVar[Any] = None + ASSET_THEME_HEX: ClassVar[Any] = None + LEXICOGRAPHICAL_SORT_ORDER: ClassVar[Any] = None + HAS_CONTRACT: ClassVar[Any] = None + ASSET_REDIRECT_GUIDS: ClassVar[Any] = None + ASSET_POLICY_GUIDS: ClassVar[Any] = None + ASSET_POLICIES_COUNT: ClassVar[Any] = None + DOMAIN_GUIDS: ClassVar[Any] = None + NON_COMPLIANT_ASSET_POLICY_GUIDS: ClassVar[Any] = None + PRODUCT_GUIDS: ClassVar[Any] = None + OUTPUT_PRODUCT_GUIDS: ClassVar[Any] = None + APPLICATION_QUALIFIED_NAME: ClassVar[Any] = None + APPLICATION_FIELD_QUALIFIED_NAME: ClassVar[Any] = None + ASSET_USER_DEFINED_TYPE: ClassVar[Any] = None + ASSET_INTERNAL_POPULARITY_SCORE: ClassVar[Any] = None + ASSET_DQ_SCHEDULE_TYPE: ClassVar[Any] = None + ASSET_DQ_SCHEDULE_CRONTAB: ClassVar[Any] = None + ASSET_DQ_SCHEDULE_TIME_ZONE: ClassVar[Any] = None + ASSET_DQ_SCHEDULE_SOURCE_SYNC_STATUS: ClassVar[Any] = None + ASSET_DQ_SCHEDULE_SOURCE_SYNCED_AT: ClassVar[Any] = None + ASSET_DQ_SCHEDULE_SOURCE_SYNC_ERROR_MESSAGE: ClassVar[Any] = None + ASSET_DQ_SCHEDULE_SOURCE_SYNC_ERROR_CODE: ClassVar[Any] = None + ASSET_DQ_SCHEDULE_SOURCE_SYNC_RAW_ERROR: ClassVar[Any] = None + ASSET_DQ_RULE_ATTACHED_DIMENSIONS: ClassVar[Any] = None + ASSET_DQ_RULE_FAILED_DIMENSIONS: ClassVar[Any] = None + ASSET_DQ_RULE_PASSED_DIMENSIONS: ClassVar[Any] = None + ASSET_DQ_RULE_ATTACHED_RULE_TYPES: ClassVar[Any] = None + ASSET_DQ_RULE_FAILED_RULE_TYPES: ClassVar[Any] = None + ASSET_DQ_RULE_PASSED_RULE_TYPES: ClassVar[Any] = None + ASSET_DQ_RULE_RESULT_TAGS: ClassVar[Any] = None + ASSET_DQ_RULE_LAST_RUN_AT: ClassVar[Any] = None + ASSET_DQ_MANUAL_RUN_STATUS: ClassVar[Any] = None + ASSET_DQ_RULE_TOTAL_COUNT: ClassVar[Any] = None + ASSET_DQ_RULE_FAILED_COUNT: ClassVar[Any] = None + ASSET_DQ_RULE_PASSED_COUNT: ClassVar[Any] = None + ASSET_DQ_RESULT: ClassVar[Any] = None + ASSET_DQ_FRESHNESS_VALUE: ClassVar[Any] = None + ASSET_DQ_FRESHNESS_EXPECTATION: ClassVar[Any] = None + ASSET_DQ_ROW_SCOPE_FILTER_COLUMN_QUALIFIED_NAME: ClassVar[Any] = None + ASSET_SPACE_QUALIFIED_NAME: ClassVar[Any] = None + ASSET_SPACE_NAME: ClassVar[Any] = None + ASSET_GCP_DATAPLEX_METADATA_DETAILS: ClassVar[Any] = None + ASSET_GCP_DATAPLEX_ASPECT_LIST: ClassVar[Any] = None + ASSET_GCP_DATAPLEX_ASPECT_FIELD_LIST: ClassVar[Any] = None + ASSET_SMUS_METADATA_FORM_NAMES: ClassVar[Any] = None + ASSET_SMUS_METADATA_FORM_KEY_VALUE_DETAILS: ClassVar[Any] = None + ASSET_SMUS_METADATA_FORM_DETAILS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + + name: Union[str, None, UnsetType] = UNSET + """Name of this asset. Fallback for display purposes, if displayName is empty.""" + + display_name: Union[str, None, UnsetType] = UNSET + """Human-readable name of this asset used for display purposes (in user interface).""" + + description: Union[str, None, UnsetType] = UNSET + """Description of this asset, for example as crawled from a source. Fallback for display purposes, if userDescription is empty.""" + + asset_source_readme: Union[str, None, UnsetType] = UNSET + """Readme of this asset, as extracted from source. If present, this will be used for the readme in user interface.""" + + user_description: Union[str, None, UnsetType] = UNSET + """Description of this asset, as provided by a user. If present, this will be used for the description in user interface.""" + + asset_ai_generated_description: Union[str, None, UnsetType] = UNSET + """Description of this asset, generated by AI based on the asset's context. Displayed separately in the UI and can be used to overwrite existing descriptions.""" + + asset_ai_generated_description_confidence: Union[float, None, UnsetType] = UNSET + """Confidence score of the AI-generated description, ranging from 0.0 to 1.0.""" + + asset_ai_generated_description_reasoning: Union[str, None, UnsetType] = UNSET + """Reasoning behind the AI-generated description, explaining how the description was derived from the asset's context.""" + + tenant_id: Union[str, None, UnsetType] = UNSET + """Name of the Atlan workspace in which this asset exists.""" + + certificate_status: Union[str, None, UnsetType] = UNSET + """Status of this asset's certification.""" + + certificate_status_message: Union[str, None, UnsetType] = UNSET + """Human-readable descriptive message used to provide further detail to certificateStatus.""" + + certificate_updated_by: Union[str, None, UnsetType] = UNSET + """Name of the user who last updated the certification of this asset.""" + + certificate_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the certification was last updated, in milliseconds.""" + + announcement_title: Union[str, None, UnsetType] = UNSET + """Brief title for the announcement on this asset. Required when announcementType is specified.""" + + announcement_message: Union[str, None, UnsetType] = UNSET + """Detailed message to include in the announcement on this asset.""" + + announcement_type: Union[str, None, UnsetType] = UNSET + """Type of announcement on this asset.""" + + announcement_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the announcement was last updated, in milliseconds.""" + + announcement_updated_by: Union[str, None, UnsetType] = UNSET + """Name of the user who last updated the announcement.""" + + owner_users: Union[Set[str], None, UnsetType] = UNSET + """List of users who own this asset.""" + + owner_groups: Union[Set[str], None, UnsetType] = UNSET + """List of groups who own this asset.""" + + admin_users: Union[Set[str], None, UnsetType] = UNSET + """List of users who administer this asset. (This is only used for certain asset types.)""" + + admin_groups: Union[Set[str], None, UnsetType] = UNSET + """List of groups who administer this asset. (This is only used for certain asset types.)""" + + viewer_users: Union[Set[str], None, UnsetType] = UNSET + """List of users who can view assets contained in a collection. (This is only used for certain asset types.)""" + + viewer_groups: Union[Set[str], None, UnsetType] = UNSET + """List of groups who can view assets contained in a collection. (This is only used for certain asset types.)""" + + connector_name: Union[str, None, UnsetType] = UNSET + """Type of the connector through which this asset is accessible.""" + + connection_name: Union[str, None, UnsetType] = UNSET + """Simple name of the connection through which this asset is accessible.""" + + connection_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the connection through which this asset is accessible.""" + + has_lineage: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="__hasLineage" + ) + """Whether this asset has lineage (true) or not (false).""" + + is_discoverable: Union[bool, None, UnsetType] = UNSET + """Whether this asset is discoverable through the UI (true) or not (false).""" + + is_editable: Union[bool, None, UnsetType] = UNSET + """Whether this asset can be edited in the UI (true) or not (false).""" + + sub_type: Union[str, None, UnsetType] = UNSET + """Subtype of this asset.""" + + view_score: Union[float, None, UnsetType] = UNSET + """View score for this asset.""" + + popularity_score: Union[float, None, UnsetType] = UNSET + """Popularity score for this asset.""" + + source_owners: Union[str, None, UnsetType] = UNSET + """List of owners of this asset, in the source system.""" + + asset_source_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for this asset in the system from which it was sourced.""" + + source_created_by: Union[str, None, UnsetType] = UNSET + """Name of the user who created this asset, in the source system.""" + + source_created_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was created in the source system, in milliseconds.""" + + source_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last updated in the source system, in milliseconds.""" + + source_updated_by: Union[str, None, UnsetType] = UNSET + """Name of the user who last updated this asset, in the source system.""" + + source_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sourceURL" + ) + """URL to the resource within the source application, used to create a button to view this asset in the source application.""" + + source_embed_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sourceEmbedURL" + ) + """URL to create an embed for a resource (for example, an image of a dashboard) within Atlan.""" + + last_sync_workflow_name: Union[str, None, UnsetType] = UNSET + """Name of the crawler that last synchronized this asset.""" + + last_sync_run_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last crawled, in milliseconds.""" + + last_sync_run: Union[str, None, UnsetType] = UNSET + """Name of the last run of the crawler that last synchronized this asset.""" + + admin_roles: Union[Set[str], None, UnsetType] = UNSET + """List of roles who administer this asset. (This is only used for Connection assets.)""" + + source_read_count: Union[int, None, UnsetType] = UNSET + """Total count of all read operations at source.""" + + source_read_user_count: Union[int, None, UnsetType] = UNSET + """Total number of unique users that read data from asset.""" + + source_last_read_at: Union[int, None, UnsetType] = UNSET + """Timestamp of most recent read operation.""" + + last_row_changed_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) of the last operation that inserted, updated, or deleted rows, in milliseconds.""" + + source_total_cost: Union[float, None, UnsetType] = UNSET + """Total cost of all operations at source.""" + + source_cost_unit: Union[str, None, UnsetType] = UNSET + """The unit of measure for sourceTotalCost.""" + + source_read_query_cost: Union[float, None, UnsetType] = UNSET + """Total cost of read queries at source.""" + + source_read_recent_user_list: Union[List[str], None, UnsetType] = UNSET + """List of usernames of the most recent users who read this asset.""" + + source_read_recent_user_record_list: Union[ + List[Dict[str, Any]], None, UnsetType + ] = UNSET + """List of usernames with extra insights for the most recent users who read this asset.""" + + source_read_top_user_list: Union[List[str], None, UnsetType] = UNSET + """List of usernames of the users who read this asset the most.""" + + source_read_top_user_record_list: Union[List[Dict[str, Any]], None, UnsetType] = ( + UNSET + ) + """List of usernames with extra insights for the users who read this asset the most.""" + + source_read_popular_query_record_list: Union[ + List[Dict[str, Any]], None, UnsetType + ] = UNSET + """List of the most popular queries that accessed this asset.""" + + source_read_expensive_query_record_list: Union[ + List[Dict[str, Any]], None, UnsetType + ] = UNSET + """List of the most expensive queries that accessed this asset.""" + + source_read_slow_query_record_list: Union[List[Dict[str, Any]], None, UnsetType] = ( + UNSET + ) + """List of the slowest queries that accessed this asset.""" + + source_query_compute_cost_list: Union[List[str], None, UnsetType] = UNSET + """List of most expensive warehouse names.""" + + source_query_compute_cost_record_list: Union[ + List[Dict[str, Any]], None, UnsetType + ] = UNSET + """List of most expensive warehouses with extra insights.""" + + dbt_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of this asset in dbt.""" + + asset_dbt_workflow_last_updated: Union[str, None, UnsetType] = UNSET + """Name of the DBT workflow in Atlan that last updated the asset.""" + + asset_dbt_alias: Union[str, None, UnsetType] = UNSET + """Alias of this asset in dbt.""" + + asset_dbt_meta: Union[str, None, UnsetType] = UNSET + """Metadata for this asset in dbt, specifically everything under the 'meta' key in the dbt object.""" + + asset_dbt_unique_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of this asset in dbt.""" + + asset_dbt_account_name: Union[str, None, UnsetType] = UNSET + """Name of the account in which this asset exists in dbt.""" + + asset_dbt_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which this asset exists in dbt.""" + + asset_dbt_package_name: Union[str, None, UnsetType] = UNSET + """Name of the package in which this asset exists in dbt.""" + + asset_dbt_job_name: Union[str, None, UnsetType] = UNSET + """Name of the job that materialized this asset in dbt.""" + + asset_dbt_job_schedule: Union[str, None, UnsetType] = UNSET + """Schedule of the job that materialized this asset in dbt.""" + + asset_dbt_job_status: Union[str, None, UnsetType] = UNSET + """Status of the job that materialized this asset in dbt.""" + + asset_dbt_test_status: Union[str, None, UnsetType] = UNSET + """All associated dbt test statuses.""" + + asset_dbt_job_schedule_cron_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable cron schedule of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt last ran, in milliseconds.""" + + asset_dbt_job_last_run_url: Union[str, None, UnsetType] = UNSET + """URL of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_created_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt was last created, in milliseconds.""" + + asset_dbt_job_last_run_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt was last updated, in milliseconds.""" + + asset_dbt_job_last_run_dequed_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt was dequeued, in milliseconds.""" + + asset_dbt_job_last_run_started_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt was started running, in milliseconds.""" + + asset_dbt_job_last_run_total_duration: Union[str, None, UnsetType] = UNSET + """Total duration of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_total_duration_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable total duration of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_queued_duration: Union[str, None, UnsetType] = UNSET + """Total duration the job that materialized this asset in dbt spent being queued.""" + + asset_dbt_job_last_run_queued_duration_humanized: Union[str, None, UnsetType] = ( + UNSET + ) + """Human-readable total duration of the last run of the job that materialized this asset in dbt spend being queued.""" + + asset_dbt_job_last_run_run_duration: Union[str, None, UnsetType] = UNSET + """Run duration of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_run_duration_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable run duration of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_git_branch: Union[str, None, UnsetType] = UNSET + """Branch in git from which the last run of the job that materialized this asset in dbt ran.""" + + asset_dbt_job_last_run_git_sha: Union[str, None, UnsetType] = UNSET + """SHA hash in git for the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_status_message: Union[str, None, UnsetType] = UNSET + """Status message of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_owner_thread_id: Union[str, None, UnsetType] = UNSET + """Thread ID of the owner of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_executed_by_thread_id: Union[str, None, UnsetType] = UNSET + """Thread ID of the user who executed the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_artifacts_saved: Union[bool, None, UnsetType] = UNSET + """Whether artifacts were saved from the last run of the job that materialized this asset in dbt (true) or not (false).""" + + asset_dbt_job_last_run_artifact_s3_path: Union[str, None, UnsetType] = UNSET + """Path in S3 to the artifacts saved from the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_has_docs_generated: Union[bool, None, UnsetType] = UNSET + """Whether docs were generated from the last run of the job that materialized this asset in dbt (true) or not (false).""" + + asset_dbt_job_last_run_has_sources_generated: Union[bool, None, UnsetType] = UNSET + """Whether sources were generated from the last run of the job that materialized this asset in dbt (true) or not (false).""" + + asset_dbt_job_last_run_notifications_sent: Union[bool, None, UnsetType] = UNSET + """Whether notifications were sent from the last run of the job that materialized this asset in dbt (true) or not (false).""" + + asset_dbt_job_next_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) when the next run of the job that materializes this asset in dbt is scheduled.""" + + asset_dbt_job_next_run_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable time when the next run of the job that materializes this asset in dbt is scheduled.""" + + asset_dbt_environment_name: Union[str, None, UnsetType] = UNSET + """Name of the environment in which this asset is materialized in dbt.""" + + asset_dbt_environment_dbt_version: Union[str, None, UnsetType] = UNSET + """Version of the environment in which this asset is materialized in dbt.""" + + asset_dbt_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset in dbt.""" + + asset_dbt_semantic_layer_proxy_url: Union[str, None, UnsetType] = UNSET + """URL of the semantic layer proxy for this asset in dbt.""" + + asset_dbt_source_freshness_criteria: Union[str, None, UnsetType] = UNSET + """Freshness criteria for the source of this asset in dbt.""" + + sample_data_url: Union[str, None, UnsetType] = UNSET + """URL for sample data for this asset.""" + + asset_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset.""" + + asset_mc_incident_names: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident names attached to this asset.""" + + asset_mc_incident_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of unique Monte Carlo incident names attached to this asset.""" + + asset_mc_alert_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of unique Monte Carlo alert names attached to this asset.""" + + asset_mc_monitor_names: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo monitor names attached to this asset.""" + + asset_mc_monitor_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of unique Monte Carlo monitor names attached to this asset.""" + + asset_mc_monitor_statuses: Union[List[str], None, UnsetType] = UNSET + """Statuses of all associated Monte Carlo monitors.""" + + asset_mc_monitor_types: Union[List[str], None, UnsetType] = UNSET + """Types of all associated Monte Carlo monitors.""" + + asset_mc_monitor_schedule_types: Union[List[str], None, UnsetType] = UNSET + """Schedules of all associated Monte Carlo monitors.""" + + asset_mc_incident_types: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident types associated with this asset.""" + + asset_mc_incident_sub_types: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident sub-types associated with this asset.""" + + asset_mc_incident_severities: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident severities associated with this asset.""" + + asset_mc_incident_priorities: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident priorities associated with this asset.""" + + asset_mc_incident_states: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident states associated with this asset.""" + + asset_mc_is_monitored: Union[bool, None, UnsetType] = UNSET + """Tracks whether this asset is monitored by MC or not""" + + asset_mc_last_sync_run_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last synced from Monte Carlo.""" + + starred_by: Union[List[str], None, UnsetType] = UNSET + """Users who have starred this asset.""" + + starred_details_list: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of usernames with extra information of the users who have starred an asset.""" + + starred_count: Union[int, None, UnsetType] = UNSET + """Number of users who have starred this asset.""" + + asset_anomalo_dq_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetAnomaloDQStatus" + ) + """Status of data quality from Anomalo.""" + + asset_anomalo_check_count: Union[int, None, UnsetType] = UNSET + """Total number of checks present in Anomalo for this asset.""" + + asset_anomalo_failed_check_count: Union[int, None, UnsetType] = UNSET + """Total number of checks failed in Anomalo for this asset.""" + + asset_anomalo_check_statuses: Union[str, None, UnsetType] = UNSET + """Stringified JSON object containing status of all Anomalo checks associated to this asset.""" + + asset_anomalo_last_check_run_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the last check was run via Anomalo.""" + + asset_anomalo_applied_check_types: Union[List[str], None, UnsetType] = UNSET + """All associated Anomalo check types.""" + + asset_anomalo_failed_check_types: Union[List[str], None, UnsetType] = UNSET + """All associated Anomalo failed check types.""" + + asset_anomalo_source_url: Union[str, None, UnsetType] = UNSET + """URL of the source in Anomalo.""" + + asset_soda_dq_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetSodaDQStatus" + ) + """Status of data quality from Soda.""" + + asset_soda_check_count: Union[int, None, UnsetType] = UNSET + """Number of checks done via Soda.""" + + asset_soda_last_sync_run_at: Union[int, None, UnsetType] = UNSET + """""" + + asset_soda_last_scan_at: Union[int, None, UnsetType] = UNSET + """""" + + asset_soda_check_statuses: Union[str, None, UnsetType] = UNSET + """All associated Soda check statuses.""" + + asset_soda_source_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetSodaSourceURL" + ) + """""" + + asset_icon: Union[str, None, UnsetType] = UNSET + """Name of the icon to use for this asset. (Only applies to glossaries, currently.)""" + + asset_external_dq_metadata_details: Union[ + Dict[str, Dict[str, Any]], None, UnsetType + ] = msgspec.field(default=UNSET, name="assetExternalDQMetadataDetails") + """DQ metadata captured for asset from external DQ tool(s).""" + + is_partial: Union[bool, None, UnsetType] = UNSET + """Indicates this asset is not fully-known, if true.""" + + is_ai_generated: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="isAIGenerated" + ) + """""" + + asset_cover_image: Union[str, None, UnsetType] = UNSET + """Cover image to use for this asset in the UI (applicable to only a few asset types).""" + + asset_theme_hex: Union[str, None, UnsetType] = UNSET + """Color (in hexadecimal RGB) to use to represent this asset.""" + + lexicographical_sort_order: Union[str, None, UnsetType] = UNSET + """Custom order for sorting purpose, managed by client""" + + has_contract: Union[bool, None, UnsetType] = UNSET + """Whether this asset has contract (true) or not (false).""" + + asset_redirect_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetRedirectGUIDs" + ) + """Array of asset ids that equivalent to this asset.""" + + asset_policy_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetPolicyGUIDs" + ) + """Array of policy ids governing this asset""" + + asset_policies_count: Union[int, None, UnsetType] = UNSET + """Count of policies inside the asset""" + + domain_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="domainGUIDs" + ) + """Array of domain guids linked to this asset""" + + non_compliant_asset_policy_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="nonCompliantAssetPolicyGUIDs" + ) + """Array of policy ids non-compliant to this asset""" + + product_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="productGUIDs" + ) + """Array of product guids linked to this asset""" + + output_product_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="outputProductGUIDs" + ) + """Array of product guids which have this asset as outputPort""" + + application_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the Application that contains this asset.""" + + application_field_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the ApplicationField that contains this asset.""" + + asset_user_defined_type: Union[str, None, UnsetType] = UNSET + """Name to use for this type of asset, as a subtype of the actual typeName.""" + + asset_internal_popularity_score: Union[float, None, UnsetType] = UNSET + """Internal Popularity score for this asset.""" + + asset_dq_schedule_type: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleType" + ) + """Type of schedule of the DQ rule that will run at datasource.""" + + asset_dq_schedule_crontab: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleCrontab" + ) + """Crontab of the DQ rule that will run at datasource.""" + + asset_dq_schedule_time_zone: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleTimeZone" + ) + """Timezone of the DQ rule schedule that will run at datasource""" + + asset_dq_schedule_source_sync_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleSourceSyncStatus" + ) + """Latest sync status of the schedule to the source.""" + + asset_dq_schedule_source_synced_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleSourceSyncedAt" + ) + """Time (epoch) at which the schedule synced to the source.""" + + asset_dq_schedule_source_sync_error_message: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQScheduleSourceSyncErrorMessage") + ) + """Error message in the case of sync state being "error".""" + + asset_dq_schedule_source_sync_error_code: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQScheduleSourceSyncErrorCode") + ) + """Error code in the case of sync state being "error".""" + + asset_dq_schedule_source_sync_raw_error: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQScheduleSourceSyncRawError") + ) + """Raw error message from the source.""" + + asset_dq_rule_attached_dimensions: Union[List[str], None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQRuleAttachedDimensions") + ) + """List of all the dimensions of attached rules.""" + + asset_dq_rule_failed_dimensions: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleFailedDimensions" + ) + """List of all the dimensions of failed rules.""" + + asset_dq_rule_passed_dimensions: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRulePassedDimensions" + ) + """List of all the dimensions for which all the rules passed.""" + + asset_dq_rule_attached_rule_types: Union[List[str], None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQRuleAttachedRuleTypes") + ) + """List of all the types of attached rules.""" + + asset_dq_rule_failed_rule_types: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleFailedRuleTypes" + ) + """List of all the types of failed rules.""" + + asset_dq_rule_passed_rule_types: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRulePassedRuleTypes" + ) + """List of all the types of rules for which all the rules passed.""" + + asset_dq_rule_result_tags: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleResultTags" + ) + """Tag for the result of the DQ rules. Eg, rule_pass:completeness:null_count.""" + + asset_dq_rule_last_run_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleLastRunAt" + ) + """Time (epoch) at which the last dq rule ran.""" + + asset_dq_manual_run_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQManualRunStatus" + ) + """Status of the latest manual DQ run triggered for this asset.""" + + asset_dq_rule_total_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleTotalCount" + ) + """Count of DQ rules attached to this asset.""" + + asset_dq_rule_failed_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleFailedCount" + ) + """Count of failed DQ rules attached to this asset.""" + + asset_dq_rule_passed_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRulePassedCount" + ) + """Count of passed DQ rules attached to this asset.""" + + asset_dq_result: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQResult" + ) + """Overall result of all the dq rules. If any one rule failed, then fail else pass.""" + + asset_dq_freshness_value: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQFreshnessValue" + ) + """Value of data freshness from Source.""" + + asset_dq_freshness_expectation: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQFreshnessExpectation" + ) + """Expectation of data freshness from Source.""" + + asset_dq_row_scope_filter_column_qualified_name: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQRowScopeFilterColumnQualifiedName") + ) + """Qualified name of the column used for row scope filtering in DQ rules for this asset.""" + + asset_space_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the space that contains this asset.""" + + asset_space_name: Union[str, None, UnsetType] = UNSET + """Name of the space that contains this asset.""" + + asset_gcp_dataplex_metadata_details: Union[Dict[str, Any], None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetGCPDataplexMetadataDetails") + ) + """Metrics captured by GCP Dataplex for objects associated with GCP services.""" + + asset_gcp_dataplex_aspect_list: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetGCPDataplexAspectList" + ) + """List of names of all Aspects linked to this asset.""" + + asset_gcp_dataplex_aspect_field_list: Union[List[str], None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetGCPDataplexAspectFieldList") + ) + """List of field key-values associated with all Aspects linked to this asset.""" + + asset_smus_metadata_form_names: Union[List[str], None, UnsetType] = UNSET + """List of AWS SMUS MetadataForm Names. This is mainly used for filtering purpose.""" + + asset_smus_metadata_form_key_value_details: Union[List[str], None, UnsetType] = ( + UNSET + ) + """List of AWS SMUS MetadataForm Key:Value Details. This is mainly used for filtering purpose.""" + + asset_smus_metadata_form_details: Union[List[Dict[str, Any]], None, UnsetType] = ( + UNSET + ) + """AWS SMUS Asset MetadataForm details""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + if self.type_name is UNSET: + self.type_name = "Asset" + + @classmethod + def ref_by_guid( + cls, guid: str, semantic: "SaveSemantic | str" = SaveSemantic.REPLACE + ) -> "Asset": + """ + Create a minimal reference to this asset type by its GUID. + + Args: + guid: Globally unique identifier of the asset + semantic: Save semantic (REPLACE, APPEND, REMOVE) + + Returns: + Asset reference instance + """ + if isinstance(semantic, str): + semantic = SaveSemantic(semantic) + return cls(guid=guid, type_name=cls.__name__, semantic=semantic) + + @classmethod + def ref_by_qualified_name( + cls, qualified_name: str, semantic: "SaveSemantic | str" = SaveSemantic.REPLACE + ) -> "Asset": + """ + Create a minimal reference to this asset type by its qualifiedName. + + Args: + qualified_name: Unique fully-qualified name of the asset + semantic: Save semantic (REPLACE, APPEND, REMOVE) + + Returns: + Asset reference instance + """ + if isinstance(semantic, str): + semantic = SaveSemantic(semantic) + return cls( + qualified_name=qualified_name, type_name=cls.__name__, semantic=semantic + ) + + def set_announcement(self, announcement) -> None: + """ + Set an announcement on this asset. + + Args: + announcement: Announcement object with type, title, and message + """ + self.announcement_type = announcement.announcement_type.value + self.announcement_title = announcement.announcement_title + self.announcement_message = announcement.announcement_message + + def remove_announcement(self) -> "Asset": + """ + Remove the announcement from this asset. + + Returns: + Self for fluent chaining + """ + self.announcement_type = None + self.announcement_title = None + self.announcement_message = None + return self + + def get_announcment(self): + """Return an Announcement object for this asset, or None if no announcement is set.""" + + ann_type = self.announcement_type + ann_title = self.announcement_title + if ann_type and ann_title and ann_type is not UNSET and ann_title is not UNSET: + return Announcement( + announcement_type=AnnouncementType[str(ann_type).upper()], + announcement_title=ann_title, + announcement_message=self.announcement_message + if self.announcement_message is not UNSET + else None, + ) + return None + + def remove_certificate(self) -> "Asset": + """ + Remove the certificate from this asset. + + Returns: + Self for fluent chaining + """ + self.certificate_status = None + self.certificate_status_message = None + return self + + def remove_description(self) -> "Asset": + """ + Remove the description from this asset. + + Returns: + Self for fluent chaining + """ + self.description = None + return self + + def remove_user_description(self) -> "Asset": + """ + Remove the user description from this asset. + + Returns: + Self for fluent chaining + """ + self.user_description = None + return self + + def remove_owners(self) -> "Asset": + """ + Remove the owners from this asset. + + Returns: + Self for fluent chaining + """ + self.owner_groups = None + self.owner_users = None + return self + + def flush_custom_metadata(self, client=None) -> None: + """ + Flush (clear) all custom metadata on this asset. + + Args: + client: AtlanClient instance (for compatibility with legacy API) + """ + self.business_attributes = {} + + @classmethod + def updater(cls, qualified_name: str = "", name: str = "") -> "Asset": + """ + Create an asset for modification (update operations). + + Args: + qualified_name: Unique name of the asset + name: Name of the asset + + Returns: + Asset instance configured for update operations + + Raises: + ValueError: If required parameters are missing + """ + if not qualified_name: + raise ValueError("qualified_name is required") + if not name: + raise ValueError("name is required") + + return cls(qualified_name=qualified_name, name=name) + + @classmethod + def create_for_modification( + cls, qualified_name: str = "", name: str = "" + ) -> "Asset": + """ + Create an asset for modification (deprecated - use updater instead). + + Args: + qualified_name: Unique name of the asset + name: Name of the asset + + Returns: + Asset instance configured for update operations + """ + return cls.updater(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "Asset": + """ + Trim this asset to only the required fields for an update. + + Returns: + Asset with only qualified_name and name set + """ + return self.__class__.updater( + qualified_name=self.qualified_name + if self.qualified_name is not UNSET + else "", + name=self.name if self.name is not UNSET else "", + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _asset_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Asset: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Asset instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _asset_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class AssetAttributes(ReferenceableAttributes): + """Asset-specific attributes for nested API format.""" + + name: Union[str, None, UnsetType] = UNSET + """Name of this asset. Fallback for display purposes, if displayName is empty.""" + + display_name: Union[str, None, UnsetType] = UNSET + """Human-readable name of this asset used for display purposes (in user interface).""" + + description: Union[str, None, UnsetType] = UNSET + """Description of this asset, for example as crawled from a source. Fallback for display purposes, if userDescription is empty.""" + + asset_source_readme: Union[str, None, UnsetType] = UNSET + """Readme of this asset, as extracted from source. If present, this will be used for the readme in user interface.""" + + user_description: Union[str, None, UnsetType] = UNSET + """Description of this asset, as provided by a user. If present, this will be used for the description in user interface.""" + + asset_ai_generated_description: Union[str, None, UnsetType] = UNSET + """Description of this asset, generated by AI based on the asset's context. Displayed separately in the UI and can be used to overwrite existing descriptions.""" + + asset_ai_generated_description_confidence: Union[float, None, UnsetType] = UNSET + """Confidence score of the AI-generated description, ranging from 0.0 to 1.0.""" + + asset_ai_generated_description_reasoning: Union[str, None, UnsetType] = UNSET + """Reasoning behind the AI-generated description, explaining how the description was derived from the asset's context.""" + + tenant_id: Union[str, None, UnsetType] = UNSET + """Name of the Atlan workspace in which this asset exists.""" + + certificate_status: Union[str, None, UnsetType] = UNSET + """Status of this asset's certification.""" + + certificate_status_message: Union[str, None, UnsetType] = UNSET + """Human-readable descriptive message used to provide further detail to certificateStatus.""" + + certificate_updated_by: Union[str, None, UnsetType] = UNSET + """Name of the user who last updated the certification of this asset.""" + + certificate_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the certification was last updated, in milliseconds.""" + + announcement_title: Union[str, None, UnsetType] = UNSET + """Brief title for the announcement on this asset. Required when announcementType is specified.""" + + announcement_message: Union[str, None, UnsetType] = UNSET + """Detailed message to include in the announcement on this asset.""" + + announcement_type: Union[str, None, UnsetType] = UNSET + """Type of announcement on this asset.""" + + announcement_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the announcement was last updated, in milliseconds.""" + + announcement_updated_by: Union[str, None, UnsetType] = UNSET + """Name of the user who last updated the announcement.""" + + owner_users: Union[Set[str], None, UnsetType] = UNSET + """List of users who own this asset.""" + + owner_groups: Union[Set[str], None, UnsetType] = UNSET + """List of groups who own this asset.""" + + admin_users: Union[Set[str], None, UnsetType] = UNSET + """List of users who administer this asset. (This is only used for certain asset types.)""" + + admin_groups: Union[Set[str], None, UnsetType] = UNSET + """List of groups who administer this asset. (This is only used for certain asset types.)""" + + viewer_users: Union[Set[str], None, UnsetType] = UNSET + """List of users who can view assets contained in a collection. (This is only used for certain asset types.)""" + + viewer_groups: Union[Set[str], None, UnsetType] = UNSET + """List of groups who can view assets contained in a collection. (This is only used for certain asset types.)""" + + connector_name: Union[str, None, UnsetType] = UNSET + """Type of the connector through which this asset is accessible.""" + + connection_name: Union[str, None, UnsetType] = UNSET + """Simple name of the connection through which this asset is accessible.""" + + connection_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the connection through which this asset is accessible.""" + + has_lineage: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="__hasLineage" + ) + """Whether this asset has lineage (true) or not (false).""" + + is_discoverable: Union[bool, None, UnsetType] = UNSET + """Whether this asset is discoverable through the UI (true) or not (false).""" + + is_editable: Union[bool, None, UnsetType] = UNSET + """Whether this asset can be edited in the UI (true) or not (false).""" + + sub_type: Union[str, None, UnsetType] = UNSET + """Subtype of this asset.""" + + view_score: Union[float, None, UnsetType] = UNSET + """View score for this asset.""" + + popularity_score: Union[float, None, UnsetType] = UNSET + """Popularity score for this asset.""" + + source_owners: Union[str, None, UnsetType] = UNSET + """List of owners of this asset, in the source system.""" + + asset_source_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for this asset in the system from which it was sourced.""" + + source_created_by: Union[str, None, UnsetType] = UNSET + """Name of the user who created this asset, in the source system.""" + + source_created_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was created in the source system, in milliseconds.""" + + source_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last updated in the source system, in milliseconds.""" + + source_updated_by: Union[str, None, UnsetType] = UNSET + """Name of the user who last updated this asset, in the source system.""" + + source_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sourceURL" + ) + """URL to the resource within the source application, used to create a button to view this asset in the source application.""" + + source_embed_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sourceEmbedURL" + ) + """URL to create an embed for a resource (for example, an image of a dashboard) within Atlan.""" + + last_sync_workflow_name: Union[str, None, UnsetType] = UNSET + """Name of the crawler that last synchronized this asset.""" + + last_sync_run_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last crawled, in milliseconds.""" + + last_sync_run: Union[str, None, UnsetType] = UNSET + """Name of the last run of the crawler that last synchronized this asset.""" + + admin_roles: Union[Set[str], None, UnsetType] = UNSET + """List of roles who administer this asset. (This is only used for Connection assets.)""" + + source_read_count: Union[int, None, UnsetType] = UNSET + """Total count of all read operations at source.""" + + source_read_user_count: Union[int, None, UnsetType] = UNSET + """Total number of unique users that read data from asset.""" + + source_last_read_at: Union[int, None, UnsetType] = UNSET + """Timestamp of most recent read operation.""" + + last_row_changed_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) of the last operation that inserted, updated, or deleted rows, in milliseconds.""" + + source_total_cost: Union[float, None, UnsetType] = UNSET + """Total cost of all operations at source.""" + + source_cost_unit: Union[str, None, UnsetType] = UNSET + """The unit of measure for sourceTotalCost.""" + + source_read_query_cost: Union[float, None, UnsetType] = UNSET + """Total cost of read queries at source.""" + + source_read_recent_user_list: Union[List[str], None, UnsetType] = UNSET + """List of usernames of the most recent users who read this asset.""" + + source_read_recent_user_record_list: Union[ + List[Dict[str, Any]], None, UnsetType + ] = UNSET + """List of usernames with extra insights for the most recent users who read this asset.""" + + source_read_top_user_list: Union[List[str], None, UnsetType] = UNSET + """List of usernames of the users who read this asset the most.""" + + source_read_top_user_record_list: Union[List[Dict[str, Any]], None, UnsetType] = ( + UNSET + ) + """List of usernames with extra insights for the users who read this asset the most.""" + + source_read_popular_query_record_list: Union[ + List[Dict[str, Any]], None, UnsetType + ] = UNSET + """List of the most popular queries that accessed this asset.""" + + source_read_expensive_query_record_list: Union[ + List[Dict[str, Any]], None, UnsetType + ] = UNSET + """List of the most expensive queries that accessed this asset.""" + + source_read_slow_query_record_list: Union[List[Dict[str, Any]], None, UnsetType] = ( + UNSET + ) + """List of the slowest queries that accessed this asset.""" + + source_query_compute_cost_list: Union[List[str], None, UnsetType] = UNSET + """List of most expensive warehouse names.""" + + source_query_compute_cost_record_list: Union[ + List[Dict[str, Any]], None, UnsetType + ] = UNSET + """List of most expensive warehouses with extra insights.""" + + dbt_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of this asset in dbt.""" + + asset_dbt_workflow_last_updated: Union[str, None, UnsetType] = UNSET + """Name of the DBT workflow in Atlan that last updated the asset.""" + + asset_dbt_alias: Union[str, None, UnsetType] = UNSET + """Alias of this asset in dbt.""" + + asset_dbt_meta: Union[str, None, UnsetType] = UNSET + """Metadata for this asset in dbt, specifically everything under the 'meta' key in the dbt object.""" + + asset_dbt_unique_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of this asset in dbt.""" + + asset_dbt_account_name: Union[str, None, UnsetType] = UNSET + """Name of the account in which this asset exists in dbt.""" + + asset_dbt_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which this asset exists in dbt.""" + + asset_dbt_package_name: Union[str, None, UnsetType] = UNSET + """Name of the package in which this asset exists in dbt.""" + + asset_dbt_job_name: Union[str, None, UnsetType] = UNSET + """Name of the job that materialized this asset in dbt.""" + + asset_dbt_job_schedule: Union[str, None, UnsetType] = UNSET + """Schedule of the job that materialized this asset in dbt.""" + + asset_dbt_job_status: Union[str, None, UnsetType] = UNSET + """Status of the job that materialized this asset in dbt.""" + + asset_dbt_test_status: Union[str, None, UnsetType] = UNSET + """All associated dbt test statuses.""" + + asset_dbt_job_schedule_cron_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable cron schedule of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt last ran, in milliseconds.""" + + asset_dbt_job_last_run_url: Union[str, None, UnsetType] = UNSET + """URL of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_created_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt was last created, in milliseconds.""" + + asset_dbt_job_last_run_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt was last updated, in milliseconds.""" + + asset_dbt_job_last_run_dequed_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt was dequeued, in milliseconds.""" + + asset_dbt_job_last_run_started_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt was started running, in milliseconds.""" + + asset_dbt_job_last_run_total_duration: Union[str, None, UnsetType] = UNSET + """Total duration of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_total_duration_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable total duration of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_queued_duration: Union[str, None, UnsetType] = UNSET + """Total duration the job that materialized this asset in dbt spent being queued.""" + + asset_dbt_job_last_run_queued_duration_humanized: Union[str, None, UnsetType] = ( + UNSET + ) + """Human-readable total duration of the last run of the job that materialized this asset in dbt spend being queued.""" + + asset_dbt_job_last_run_run_duration: Union[str, None, UnsetType] = UNSET + """Run duration of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_run_duration_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable run duration of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_git_branch: Union[str, None, UnsetType] = UNSET + """Branch in git from which the last run of the job that materialized this asset in dbt ran.""" + + asset_dbt_job_last_run_git_sha: Union[str, None, UnsetType] = UNSET + """SHA hash in git for the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_status_message: Union[str, None, UnsetType] = UNSET + """Status message of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_owner_thread_id: Union[str, None, UnsetType] = UNSET + """Thread ID of the owner of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_executed_by_thread_id: Union[str, None, UnsetType] = UNSET + """Thread ID of the user who executed the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_artifacts_saved: Union[bool, None, UnsetType] = UNSET + """Whether artifacts were saved from the last run of the job that materialized this asset in dbt (true) or not (false).""" + + asset_dbt_job_last_run_artifact_s3_path: Union[str, None, UnsetType] = UNSET + """Path in S3 to the artifacts saved from the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_has_docs_generated: Union[bool, None, UnsetType] = UNSET + """Whether docs were generated from the last run of the job that materialized this asset in dbt (true) or not (false).""" + + asset_dbt_job_last_run_has_sources_generated: Union[bool, None, UnsetType] = UNSET + """Whether sources were generated from the last run of the job that materialized this asset in dbt (true) or not (false).""" + + asset_dbt_job_last_run_notifications_sent: Union[bool, None, UnsetType] = UNSET + """Whether notifications were sent from the last run of the job that materialized this asset in dbt (true) or not (false).""" + + asset_dbt_job_next_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) when the next run of the job that materializes this asset in dbt is scheduled.""" + + asset_dbt_job_next_run_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable time when the next run of the job that materializes this asset in dbt is scheduled.""" + + asset_dbt_environment_name: Union[str, None, UnsetType] = UNSET + """Name of the environment in which this asset is materialized in dbt.""" + + asset_dbt_environment_dbt_version: Union[str, None, UnsetType] = UNSET + """Version of the environment in which this asset is materialized in dbt.""" + + asset_dbt_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset in dbt.""" + + asset_dbt_semantic_layer_proxy_url: Union[str, None, UnsetType] = UNSET + """URL of the semantic layer proxy for this asset in dbt.""" + + asset_dbt_source_freshness_criteria: Union[str, None, UnsetType] = UNSET + """Freshness criteria for the source of this asset in dbt.""" + + sample_data_url: Union[str, None, UnsetType] = UNSET + """URL for sample data for this asset.""" + + asset_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset.""" + + asset_mc_incident_names: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident names attached to this asset.""" + + asset_mc_incident_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of unique Monte Carlo incident names attached to this asset.""" + + asset_mc_alert_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of unique Monte Carlo alert names attached to this asset.""" + + asset_mc_monitor_names: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo monitor names attached to this asset.""" + + asset_mc_monitor_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of unique Monte Carlo monitor names attached to this asset.""" + + asset_mc_monitor_statuses: Union[List[str], None, UnsetType] = UNSET + """Statuses of all associated Monte Carlo monitors.""" + + asset_mc_monitor_types: Union[List[str], None, UnsetType] = UNSET + """Types of all associated Monte Carlo monitors.""" + + asset_mc_monitor_schedule_types: Union[List[str], None, UnsetType] = UNSET + """Schedules of all associated Monte Carlo monitors.""" + + asset_mc_incident_types: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident types associated with this asset.""" + + asset_mc_incident_sub_types: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident sub-types associated with this asset.""" + + asset_mc_incident_severities: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident severities associated with this asset.""" + + asset_mc_incident_priorities: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident priorities associated with this asset.""" + + asset_mc_incident_states: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident states associated with this asset.""" + + asset_mc_is_monitored: Union[bool, None, UnsetType] = UNSET + """Tracks whether this asset is monitored by MC or not""" + + asset_mc_last_sync_run_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last synced from Monte Carlo.""" + + starred_by: Union[List[str], None, UnsetType] = UNSET + """Users who have starred this asset.""" + + starred_details_list: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of usernames with extra information of the users who have starred an asset.""" + + starred_count: Union[int, None, UnsetType] = UNSET + """Number of users who have starred this asset.""" + + asset_anomalo_dq_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetAnomaloDQStatus" + ) + """Status of data quality from Anomalo.""" + + asset_anomalo_check_count: Union[int, None, UnsetType] = UNSET + """Total number of checks present in Anomalo for this asset.""" + + asset_anomalo_failed_check_count: Union[int, None, UnsetType] = UNSET + """Total number of checks failed in Anomalo for this asset.""" + + asset_anomalo_check_statuses: Union[str, None, UnsetType] = UNSET + """Stringified JSON object containing status of all Anomalo checks associated to this asset.""" + + asset_anomalo_last_check_run_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the last check was run via Anomalo.""" + + asset_anomalo_applied_check_types: Union[List[str], None, UnsetType] = UNSET + """All associated Anomalo check types.""" + + asset_anomalo_failed_check_types: Union[List[str], None, UnsetType] = UNSET + """All associated Anomalo failed check types.""" + + asset_anomalo_source_url: Union[str, None, UnsetType] = UNSET + """URL of the source in Anomalo.""" + + asset_soda_dq_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetSodaDQStatus" + ) + """Status of data quality from Soda.""" + + asset_soda_check_count: Union[int, None, UnsetType] = UNSET + """Number of checks done via Soda.""" + + asset_soda_last_sync_run_at: Union[int, None, UnsetType] = UNSET + """""" + + asset_soda_last_scan_at: Union[int, None, UnsetType] = UNSET + """""" + + asset_soda_check_statuses: Union[str, None, UnsetType] = UNSET + """All associated Soda check statuses.""" + + asset_soda_source_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetSodaSourceURL" + ) + """""" + + asset_icon: Union[str, None, UnsetType] = UNSET + """Name of the icon to use for this asset. (Only applies to glossaries, currently.)""" + + asset_external_dq_metadata_details: Union[ + Dict[str, Dict[str, Any]], None, UnsetType + ] = msgspec.field(default=UNSET, name="assetExternalDQMetadataDetails") + """DQ metadata captured for asset from external DQ tool(s).""" + + is_partial: Union[bool, None, UnsetType] = UNSET + """Indicates this asset is not fully-known, if true.""" + + is_ai_generated: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="isAIGenerated" + ) + """""" + + asset_cover_image: Union[str, None, UnsetType] = UNSET + """Cover image to use for this asset in the UI (applicable to only a few asset types).""" + + asset_theme_hex: Union[str, None, UnsetType] = UNSET + """Color (in hexadecimal RGB) to use to represent this asset.""" + + lexicographical_sort_order: Union[str, None, UnsetType] = UNSET + """Custom order for sorting purpose, managed by client""" + + has_contract: Union[bool, None, UnsetType] = UNSET + """Whether this asset has contract (true) or not (false).""" + + asset_redirect_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetRedirectGUIDs" + ) + """Array of asset ids that equivalent to this asset.""" + + asset_policy_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetPolicyGUIDs" + ) + """Array of policy ids governing this asset""" + + asset_policies_count: Union[int, None, UnsetType] = UNSET + """Count of policies inside the asset""" + + domain_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="domainGUIDs" + ) + """Array of domain guids linked to this asset""" + + non_compliant_asset_policy_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="nonCompliantAssetPolicyGUIDs" + ) + """Array of policy ids non-compliant to this asset""" + + product_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="productGUIDs" + ) + """Array of product guids linked to this asset""" + + output_product_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="outputProductGUIDs" + ) + """Array of product guids which have this asset as outputPort""" + + application_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the Application that contains this asset.""" + + application_field_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the ApplicationField that contains this asset.""" + + asset_user_defined_type: Union[str, None, UnsetType] = UNSET + """Name to use for this type of asset, as a subtype of the actual typeName.""" + + asset_internal_popularity_score: Union[float, None, UnsetType] = UNSET + """Internal Popularity score for this asset.""" + + asset_dq_schedule_type: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleType" + ) + """Type of schedule of the DQ rule that will run at datasource.""" + + asset_dq_schedule_crontab: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleCrontab" + ) + """Crontab of the DQ rule that will run at datasource.""" + + asset_dq_schedule_time_zone: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleTimeZone" + ) + """Timezone of the DQ rule schedule that will run at datasource""" + + asset_dq_schedule_source_sync_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleSourceSyncStatus" + ) + """Latest sync status of the schedule to the source.""" + + asset_dq_schedule_source_synced_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleSourceSyncedAt" + ) + """Time (epoch) at which the schedule synced to the source.""" + + asset_dq_schedule_source_sync_error_message: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQScheduleSourceSyncErrorMessage") + ) + """Error message in the case of sync state being "error".""" + + asset_dq_schedule_source_sync_error_code: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQScheduleSourceSyncErrorCode") + ) + """Error code in the case of sync state being "error".""" + + asset_dq_schedule_source_sync_raw_error: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQScheduleSourceSyncRawError") + ) + """Raw error message from the source.""" + + asset_dq_rule_attached_dimensions: Union[List[str], None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQRuleAttachedDimensions") + ) + """List of all the dimensions of attached rules.""" + + asset_dq_rule_failed_dimensions: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleFailedDimensions" + ) + """List of all the dimensions of failed rules.""" + + asset_dq_rule_passed_dimensions: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRulePassedDimensions" + ) + """List of all the dimensions for which all the rules passed.""" + + asset_dq_rule_attached_rule_types: Union[List[str], None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQRuleAttachedRuleTypes") + ) + """List of all the types of attached rules.""" + + asset_dq_rule_failed_rule_types: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleFailedRuleTypes" + ) + """List of all the types of failed rules.""" + + asset_dq_rule_passed_rule_types: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRulePassedRuleTypes" + ) + """List of all the types of rules for which all the rules passed.""" + + asset_dq_rule_result_tags: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleResultTags" + ) + """Tag for the result of the DQ rules. Eg, rule_pass:completeness:null_count.""" + + asset_dq_rule_last_run_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleLastRunAt" + ) + """Time (epoch) at which the last dq rule ran.""" + + asset_dq_manual_run_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQManualRunStatus" + ) + """Status of the latest manual DQ run triggered for this asset.""" + + asset_dq_rule_total_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleTotalCount" + ) + """Count of DQ rules attached to this asset.""" + + asset_dq_rule_failed_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleFailedCount" + ) + """Count of failed DQ rules attached to this asset.""" + + asset_dq_rule_passed_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRulePassedCount" + ) + """Count of passed DQ rules attached to this asset.""" + + asset_dq_result: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQResult" + ) + """Overall result of all the dq rules. If any one rule failed, then fail else pass.""" + + asset_dq_freshness_value: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQFreshnessValue" + ) + """Value of data freshness from Source.""" + + asset_dq_freshness_expectation: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQFreshnessExpectation" + ) + """Expectation of data freshness from Source.""" + + asset_dq_row_scope_filter_column_qualified_name: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQRowScopeFilterColumnQualifiedName") + ) + """Qualified name of the column used for row scope filtering in DQ rules for this asset.""" + + asset_space_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the space that contains this asset.""" + + asset_space_name: Union[str, None, UnsetType] = UNSET + """Name of the space that contains this asset.""" + + asset_gcp_dataplex_metadata_details: Union[Dict[str, Any], None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetGCPDataplexMetadataDetails") + ) + """Metrics captured by GCP Dataplex for objects associated with GCP services.""" + + asset_gcp_dataplex_aspect_list: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetGCPDataplexAspectList" + ) + """List of names of all Aspects linked to this asset.""" + + asset_gcp_dataplex_aspect_field_list: Union[List[str], None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetGCPDataplexAspectFieldList") + ) + """List of field key-values associated with all Aspects linked to this asset.""" + + asset_smus_metadata_form_names: Union[List[str], None, UnsetType] = UNSET + """List of AWS SMUS MetadataForm Names. This is mainly used for filtering purpose.""" + + asset_smus_metadata_form_key_value_details: Union[List[str], None, UnsetType] = ( + UNSET + ) + """List of AWS SMUS MetadataForm Key:Value Details. This is mainly used for filtering purpose.""" + + asset_smus_metadata_form_details: Union[List[Dict[str, Any]], None, UnsetType] = ( + UNSET + ) + """AWS SMUS Asset MetadataForm details""" + + +class AssetRelationshipAttributes(ReferenceableRelationshipAttributes): + """Asset-specific relationship attributes for nested API format.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + +class AssetNested(ReferenceableNested): + """Asset in nested API format for high-performance serialization.""" + + attributes: Union[AssetAttributes, UnsetType] = UNSET + relationship_attributes: Union[AssetRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[AssetRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[AssetRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_ASSET_REL_FIELDS: List[str] = [ + *_REFERENCEABLE_REL_FIELDS, + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", +] + + +def _populate_asset_attrs(attrs: AssetAttributes, obj: Asset) -> None: + """Populate Asset-specific attributes on the attrs struct.""" + _populate_referenceable_attrs(attrs, obj) + attrs.name = obj.name + attrs.display_name = obj.display_name + attrs.description = obj.description + attrs.asset_source_readme = obj.asset_source_readme + attrs.user_description = obj.user_description + attrs.asset_ai_generated_description = obj.asset_ai_generated_description + attrs.asset_ai_generated_description_confidence = ( + obj.asset_ai_generated_description_confidence + ) + attrs.asset_ai_generated_description_reasoning = ( + obj.asset_ai_generated_description_reasoning + ) + attrs.tenant_id = obj.tenant_id + attrs.certificate_status = obj.certificate_status + attrs.certificate_status_message = obj.certificate_status_message + attrs.certificate_updated_by = obj.certificate_updated_by + attrs.certificate_updated_at = obj.certificate_updated_at + attrs.announcement_title = obj.announcement_title + attrs.announcement_message = obj.announcement_message + attrs.announcement_type = obj.announcement_type + attrs.announcement_updated_at = obj.announcement_updated_at + attrs.announcement_updated_by = obj.announcement_updated_by + attrs.owner_users = obj.owner_users + attrs.owner_groups = obj.owner_groups + attrs.admin_users = obj.admin_users + attrs.admin_groups = obj.admin_groups + attrs.viewer_users = obj.viewer_users + attrs.viewer_groups = obj.viewer_groups + attrs.connector_name = obj.connector_name + attrs.connection_name = obj.connection_name + attrs.connection_qualified_name = obj.connection_qualified_name + attrs.has_lineage = obj.has_lineage + attrs.is_discoverable = obj.is_discoverable + attrs.is_editable = obj.is_editable + attrs.sub_type = obj.sub_type + attrs.view_score = obj.view_score + attrs.popularity_score = obj.popularity_score + attrs.source_owners = obj.source_owners + attrs.asset_source_id = obj.asset_source_id + attrs.source_created_by = obj.source_created_by + attrs.source_created_at = obj.source_created_at + attrs.source_updated_at = obj.source_updated_at + attrs.source_updated_by = obj.source_updated_by + attrs.source_url = obj.source_url + attrs.source_embed_url = obj.source_embed_url + attrs.last_sync_workflow_name = obj.last_sync_workflow_name + attrs.last_sync_run_at = obj.last_sync_run_at + attrs.last_sync_run = obj.last_sync_run + attrs.admin_roles = obj.admin_roles + attrs.source_read_count = obj.source_read_count + attrs.source_read_user_count = obj.source_read_user_count + attrs.source_last_read_at = obj.source_last_read_at + attrs.last_row_changed_at = obj.last_row_changed_at + attrs.source_total_cost = obj.source_total_cost + attrs.source_cost_unit = obj.source_cost_unit + attrs.source_read_query_cost = obj.source_read_query_cost + attrs.source_read_recent_user_list = obj.source_read_recent_user_list + attrs.source_read_recent_user_record_list = obj.source_read_recent_user_record_list + attrs.source_read_top_user_list = obj.source_read_top_user_list + attrs.source_read_top_user_record_list = obj.source_read_top_user_record_list + attrs.source_read_popular_query_record_list = ( + obj.source_read_popular_query_record_list + ) + attrs.source_read_expensive_query_record_list = ( + obj.source_read_expensive_query_record_list + ) + attrs.source_read_slow_query_record_list = obj.source_read_slow_query_record_list + attrs.source_query_compute_cost_list = obj.source_query_compute_cost_list + attrs.source_query_compute_cost_record_list = ( + obj.source_query_compute_cost_record_list + ) + attrs.dbt_qualified_name = obj.dbt_qualified_name + attrs.asset_dbt_workflow_last_updated = obj.asset_dbt_workflow_last_updated + attrs.asset_dbt_alias = obj.asset_dbt_alias + attrs.asset_dbt_meta = obj.asset_dbt_meta + attrs.asset_dbt_unique_id = obj.asset_dbt_unique_id + attrs.asset_dbt_account_name = obj.asset_dbt_account_name + attrs.asset_dbt_project_name = obj.asset_dbt_project_name + attrs.asset_dbt_package_name = obj.asset_dbt_package_name + attrs.asset_dbt_job_name = obj.asset_dbt_job_name + attrs.asset_dbt_job_schedule = obj.asset_dbt_job_schedule + attrs.asset_dbt_job_status = obj.asset_dbt_job_status + attrs.asset_dbt_test_status = obj.asset_dbt_test_status + attrs.asset_dbt_job_schedule_cron_humanized = ( + obj.asset_dbt_job_schedule_cron_humanized + ) + attrs.asset_dbt_job_last_run = obj.asset_dbt_job_last_run + attrs.asset_dbt_job_last_run_url = obj.asset_dbt_job_last_run_url + attrs.asset_dbt_job_last_run_created_at = obj.asset_dbt_job_last_run_created_at + attrs.asset_dbt_job_last_run_updated_at = obj.asset_dbt_job_last_run_updated_at + attrs.asset_dbt_job_last_run_dequed_at = obj.asset_dbt_job_last_run_dequed_at + attrs.asset_dbt_job_last_run_started_at = obj.asset_dbt_job_last_run_started_at + attrs.asset_dbt_job_last_run_total_duration = ( + obj.asset_dbt_job_last_run_total_duration + ) + attrs.asset_dbt_job_last_run_total_duration_humanized = ( + obj.asset_dbt_job_last_run_total_duration_humanized + ) + attrs.asset_dbt_job_last_run_queued_duration = ( + obj.asset_dbt_job_last_run_queued_duration + ) + attrs.asset_dbt_job_last_run_queued_duration_humanized = ( + obj.asset_dbt_job_last_run_queued_duration_humanized + ) + attrs.asset_dbt_job_last_run_run_duration = obj.asset_dbt_job_last_run_run_duration + attrs.asset_dbt_job_last_run_run_duration_humanized = ( + obj.asset_dbt_job_last_run_run_duration_humanized + ) + attrs.asset_dbt_job_last_run_git_branch = obj.asset_dbt_job_last_run_git_branch + attrs.asset_dbt_job_last_run_git_sha = obj.asset_dbt_job_last_run_git_sha + attrs.asset_dbt_job_last_run_status_message = ( + obj.asset_dbt_job_last_run_status_message + ) + attrs.asset_dbt_job_last_run_owner_thread_id = ( + obj.asset_dbt_job_last_run_owner_thread_id + ) + attrs.asset_dbt_job_last_run_executed_by_thread_id = ( + obj.asset_dbt_job_last_run_executed_by_thread_id + ) + attrs.asset_dbt_job_last_run_artifacts_saved = ( + obj.asset_dbt_job_last_run_artifacts_saved + ) + attrs.asset_dbt_job_last_run_artifact_s3_path = ( + obj.asset_dbt_job_last_run_artifact_s3_path + ) + attrs.asset_dbt_job_last_run_has_docs_generated = ( + obj.asset_dbt_job_last_run_has_docs_generated + ) + attrs.asset_dbt_job_last_run_has_sources_generated = ( + obj.asset_dbt_job_last_run_has_sources_generated + ) + attrs.asset_dbt_job_last_run_notifications_sent = ( + obj.asset_dbt_job_last_run_notifications_sent + ) + attrs.asset_dbt_job_next_run = obj.asset_dbt_job_next_run + attrs.asset_dbt_job_next_run_humanized = obj.asset_dbt_job_next_run_humanized + attrs.asset_dbt_environment_name = obj.asset_dbt_environment_name + attrs.asset_dbt_environment_dbt_version = obj.asset_dbt_environment_dbt_version + attrs.asset_dbt_tags = obj.asset_dbt_tags + attrs.asset_dbt_semantic_layer_proxy_url = obj.asset_dbt_semantic_layer_proxy_url + attrs.asset_dbt_source_freshness_criteria = obj.asset_dbt_source_freshness_criteria + attrs.sample_data_url = obj.sample_data_url + attrs.asset_tags = obj.asset_tags + attrs.asset_mc_incident_names = obj.asset_mc_incident_names + attrs.asset_mc_incident_qualified_names = obj.asset_mc_incident_qualified_names + attrs.asset_mc_alert_qualified_names = obj.asset_mc_alert_qualified_names + attrs.asset_mc_monitor_names = obj.asset_mc_monitor_names + attrs.asset_mc_monitor_qualified_names = obj.asset_mc_monitor_qualified_names + attrs.asset_mc_monitor_statuses = obj.asset_mc_monitor_statuses + attrs.asset_mc_monitor_types = obj.asset_mc_monitor_types + attrs.asset_mc_monitor_schedule_types = obj.asset_mc_monitor_schedule_types + attrs.asset_mc_incident_types = obj.asset_mc_incident_types + attrs.asset_mc_incident_sub_types = obj.asset_mc_incident_sub_types + attrs.asset_mc_incident_severities = obj.asset_mc_incident_severities + attrs.asset_mc_incident_priorities = obj.asset_mc_incident_priorities + attrs.asset_mc_incident_states = obj.asset_mc_incident_states + attrs.asset_mc_is_monitored = obj.asset_mc_is_monitored + attrs.asset_mc_last_sync_run_at = obj.asset_mc_last_sync_run_at + attrs.starred_by = obj.starred_by + attrs.starred_details_list = obj.starred_details_list + attrs.starred_count = obj.starred_count + attrs.asset_anomalo_dq_status = obj.asset_anomalo_dq_status + attrs.asset_anomalo_check_count = obj.asset_anomalo_check_count + attrs.asset_anomalo_failed_check_count = obj.asset_anomalo_failed_check_count + attrs.asset_anomalo_check_statuses = obj.asset_anomalo_check_statuses + attrs.asset_anomalo_last_check_run_at = obj.asset_anomalo_last_check_run_at + attrs.asset_anomalo_applied_check_types = obj.asset_anomalo_applied_check_types + attrs.asset_anomalo_failed_check_types = obj.asset_anomalo_failed_check_types + attrs.asset_anomalo_source_url = obj.asset_anomalo_source_url + attrs.asset_soda_dq_status = obj.asset_soda_dq_status + attrs.asset_soda_check_count = obj.asset_soda_check_count + attrs.asset_soda_last_sync_run_at = obj.asset_soda_last_sync_run_at + attrs.asset_soda_last_scan_at = obj.asset_soda_last_scan_at + attrs.asset_soda_check_statuses = obj.asset_soda_check_statuses + attrs.asset_soda_source_url = obj.asset_soda_source_url + attrs.asset_icon = obj.asset_icon + attrs.asset_external_dq_metadata_details = obj.asset_external_dq_metadata_details + attrs.is_partial = obj.is_partial + attrs.is_ai_generated = obj.is_ai_generated + attrs.asset_cover_image = obj.asset_cover_image + attrs.asset_theme_hex = obj.asset_theme_hex + attrs.lexicographical_sort_order = obj.lexicographical_sort_order + attrs.has_contract = obj.has_contract + attrs.asset_redirect_guids = obj.asset_redirect_guids + attrs.asset_policy_guids = obj.asset_policy_guids + attrs.asset_policies_count = obj.asset_policies_count + attrs.domain_guids = obj.domain_guids + attrs.non_compliant_asset_policy_guids = obj.non_compliant_asset_policy_guids + attrs.product_guids = obj.product_guids + attrs.output_product_guids = obj.output_product_guids + attrs.application_qualified_name = obj.application_qualified_name + attrs.application_field_qualified_name = obj.application_field_qualified_name + attrs.asset_user_defined_type = obj.asset_user_defined_type + attrs.asset_internal_popularity_score = obj.asset_internal_popularity_score + attrs.asset_dq_schedule_type = obj.asset_dq_schedule_type + attrs.asset_dq_schedule_crontab = obj.asset_dq_schedule_crontab + attrs.asset_dq_schedule_time_zone = obj.asset_dq_schedule_time_zone + attrs.asset_dq_schedule_source_sync_status = ( + obj.asset_dq_schedule_source_sync_status + ) + attrs.asset_dq_schedule_source_synced_at = obj.asset_dq_schedule_source_synced_at + attrs.asset_dq_schedule_source_sync_error_message = ( + obj.asset_dq_schedule_source_sync_error_message + ) + attrs.asset_dq_schedule_source_sync_error_code = ( + obj.asset_dq_schedule_source_sync_error_code + ) + attrs.asset_dq_schedule_source_sync_raw_error = ( + obj.asset_dq_schedule_source_sync_raw_error + ) + attrs.asset_dq_rule_attached_dimensions = obj.asset_dq_rule_attached_dimensions + attrs.asset_dq_rule_failed_dimensions = obj.asset_dq_rule_failed_dimensions + attrs.asset_dq_rule_passed_dimensions = obj.asset_dq_rule_passed_dimensions + attrs.asset_dq_rule_attached_rule_types = obj.asset_dq_rule_attached_rule_types + attrs.asset_dq_rule_failed_rule_types = obj.asset_dq_rule_failed_rule_types + attrs.asset_dq_rule_passed_rule_types = obj.asset_dq_rule_passed_rule_types + attrs.asset_dq_rule_result_tags = obj.asset_dq_rule_result_tags + attrs.asset_dq_rule_last_run_at = obj.asset_dq_rule_last_run_at + attrs.asset_dq_manual_run_status = obj.asset_dq_manual_run_status + attrs.asset_dq_rule_total_count = obj.asset_dq_rule_total_count + attrs.asset_dq_rule_failed_count = obj.asset_dq_rule_failed_count + attrs.asset_dq_rule_passed_count = obj.asset_dq_rule_passed_count + attrs.asset_dq_result = obj.asset_dq_result + attrs.asset_dq_freshness_value = obj.asset_dq_freshness_value + attrs.asset_dq_freshness_expectation = obj.asset_dq_freshness_expectation + attrs.asset_dq_row_scope_filter_column_qualified_name = ( + obj.asset_dq_row_scope_filter_column_qualified_name + ) + attrs.asset_space_qualified_name = obj.asset_space_qualified_name + attrs.asset_space_name = obj.asset_space_name + attrs.asset_gcp_dataplex_metadata_details = obj.asset_gcp_dataplex_metadata_details + attrs.asset_gcp_dataplex_aspect_list = obj.asset_gcp_dataplex_aspect_list + attrs.asset_gcp_dataplex_aspect_field_list = ( + obj.asset_gcp_dataplex_aspect_field_list + ) + attrs.asset_smus_metadata_form_names = obj.asset_smus_metadata_form_names + attrs.asset_smus_metadata_form_key_value_details = ( + obj.asset_smus_metadata_form_key_value_details + ) + attrs.asset_smus_metadata_form_details = obj.asset_smus_metadata_form_details + + +def _extract_asset_attrs(attrs: AssetAttributes) -> dict: + """Extract all Asset attributes from the attrs struct into a flat dict.""" + result = _extract_referenceable_attrs(attrs) + result["name"] = attrs.name + result["display_name"] = attrs.display_name + result["description"] = attrs.description + result["asset_source_readme"] = attrs.asset_source_readme + result["user_description"] = attrs.user_description + result["asset_ai_generated_description"] = attrs.asset_ai_generated_description + result["asset_ai_generated_description_confidence"] = ( + attrs.asset_ai_generated_description_confidence + ) + result["asset_ai_generated_description_reasoning"] = ( + attrs.asset_ai_generated_description_reasoning + ) + result["tenant_id"] = attrs.tenant_id + result["certificate_status"] = attrs.certificate_status + result["certificate_status_message"] = attrs.certificate_status_message + result["certificate_updated_by"] = attrs.certificate_updated_by + result["certificate_updated_at"] = attrs.certificate_updated_at + result["announcement_title"] = attrs.announcement_title + result["announcement_message"] = attrs.announcement_message + result["announcement_type"] = attrs.announcement_type + result["announcement_updated_at"] = attrs.announcement_updated_at + result["announcement_updated_by"] = attrs.announcement_updated_by + result["owner_users"] = attrs.owner_users + result["owner_groups"] = attrs.owner_groups + result["admin_users"] = attrs.admin_users + result["admin_groups"] = attrs.admin_groups + result["viewer_users"] = attrs.viewer_users + result["viewer_groups"] = attrs.viewer_groups + result["connector_name"] = attrs.connector_name + result["connection_name"] = attrs.connection_name + result["connection_qualified_name"] = attrs.connection_qualified_name + result["has_lineage"] = attrs.has_lineage + result["is_discoverable"] = attrs.is_discoverable + result["is_editable"] = attrs.is_editable + result["sub_type"] = attrs.sub_type + result["view_score"] = attrs.view_score + result["popularity_score"] = attrs.popularity_score + result["source_owners"] = attrs.source_owners + result["asset_source_id"] = attrs.asset_source_id + result["source_created_by"] = attrs.source_created_by + result["source_created_at"] = attrs.source_created_at + result["source_updated_at"] = attrs.source_updated_at + result["source_updated_by"] = attrs.source_updated_by + result["source_url"] = attrs.source_url + result["source_embed_url"] = attrs.source_embed_url + result["last_sync_workflow_name"] = attrs.last_sync_workflow_name + result["last_sync_run_at"] = attrs.last_sync_run_at + result["last_sync_run"] = attrs.last_sync_run + result["admin_roles"] = attrs.admin_roles + result["source_read_count"] = attrs.source_read_count + result["source_read_user_count"] = attrs.source_read_user_count + result["source_last_read_at"] = attrs.source_last_read_at + result["last_row_changed_at"] = attrs.last_row_changed_at + result["source_total_cost"] = attrs.source_total_cost + result["source_cost_unit"] = attrs.source_cost_unit + result["source_read_query_cost"] = attrs.source_read_query_cost + result["source_read_recent_user_list"] = attrs.source_read_recent_user_list + result["source_read_recent_user_record_list"] = ( + attrs.source_read_recent_user_record_list + ) + result["source_read_top_user_list"] = attrs.source_read_top_user_list + result["source_read_top_user_record_list"] = attrs.source_read_top_user_record_list + result["source_read_popular_query_record_list"] = ( + attrs.source_read_popular_query_record_list + ) + result["source_read_expensive_query_record_list"] = ( + attrs.source_read_expensive_query_record_list + ) + result["source_read_slow_query_record_list"] = ( + attrs.source_read_slow_query_record_list + ) + result["source_query_compute_cost_list"] = attrs.source_query_compute_cost_list + result["source_query_compute_cost_record_list"] = ( + attrs.source_query_compute_cost_record_list + ) + result["dbt_qualified_name"] = attrs.dbt_qualified_name + result["asset_dbt_workflow_last_updated"] = attrs.asset_dbt_workflow_last_updated + result["asset_dbt_alias"] = attrs.asset_dbt_alias + result["asset_dbt_meta"] = attrs.asset_dbt_meta + result["asset_dbt_unique_id"] = attrs.asset_dbt_unique_id + result["asset_dbt_account_name"] = attrs.asset_dbt_account_name + result["asset_dbt_project_name"] = attrs.asset_dbt_project_name + result["asset_dbt_package_name"] = attrs.asset_dbt_package_name + result["asset_dbt_job_name"] = attrs.asset_dbt_job_name + result["asset_dbt_job_schedule"] = attrs.asset_dbt_job_schedule + result["asset_dbt_job_status"] = attrs.asset_dbt_job_status + result["asset_dbt_test_status"] = attrs.asset_dbt_test_status + result["asset_dbt_job_schedule_cron_humanized"] = ( + attrs.asset_dbt_job_schedule_cron_humanized + ) + result["asset_dbt_job_last_run"] = attrs.asset_dbt_job_last_run + result["asset_dbt_job_last_run_url"] = attrs.asset_dbt_job_last_run_url + result["asset_dbt_job_last_run_created_at"] = ( + attrs.asset_dbt_job_last_run_created_at + ) + result["asset_dbt_job_last_run_updated_at"] = ( + attrs.asset_dbt_job_last_run_updated_at + ) + result["asset_dbt_job_last_run_dequed_at"] = attrs.asset_dbt_job_last_run_dequed_at + result["asset_dbt_job_last_run_started_at"] = ( + attrs.asset_dbt_job_last_run_started_at + ) + result["asset_dbt_job_last_run_total_duration"] = ( + attrs.asset_dbt_job_last_run_total_duration + ) + result["asset_dbt_job_last_run_total_duration_humanized"] = ( + attrs.asset_dbt_job_last_run_total_duration_humanized + ) + result["asset_dbt_job_last_run_queued_duration"] = ( + attrs.asset_dbt_job_last_run_queued_duration + ) + result["asset_dbt_job_last_run_queued_duration_humanized"] = ( + attrs.asset_dbt_job_last_run_queued_duration_humanized + ) + result["asset_dbt_job_last_run_run_duration"] = ( + attrs.asset_dbt_job_last_run_run_duration + ) + result["asset_dbt_job_last_run_run_duration_humanized"] = ( + attrs.asset_dbt_job_last_run_run_duration_humanized + ) + result["asset_dbt_job_last_run_git_branch"] = ( + attrs.asset_dbt_job_last_run_git_branch + ) + result["asset_dbt_job_last_run_git_sha"] = attrs.asset_dbt_job_last_run_git_sha + result["asset_dbt_job_last_run_status_message"] = ( + attrs.asset_dbt_job_last_run_status_message + ) + result["asset_dbt_job_last_run_owner_thread_id"] = ( + attrs.asset_dbt_job_last_run_owner_thread_id + ) + result["asset_dbt_job_last_run_executed_by_thread_id"] = ( + attrs.asset_dbt_job_last_run_executed_by_thread_id + ) + result["asset_dbt_job_last_run_artifacts_saved"] = ( + attrs.asset_dbt_job_last_run_artifacts_saved + ) + result["asset_dbt_job_last_run_artifact_s3_path"] = ( + attrs.asset_dbt_job_last_run_artifact_s3_path + ) + result["asset_dbt_job_last_run_has_docs_generated"] = ( + attrs.asset_dbt_job_last_run_has_docs_generated + ) + result["asset_dbt_job_last_run_has_sources_generated"] = ( + attrs.asset_dbt_job_last_run_has_sources_generated + ) + result["asset_dbt_job_last_run_notifications_sent"] = ( + attrs.asset_dbt_job_last_run_notifications_sent + ) + result["asset_dbt_job_next_run"] = attrs.asset_dbt_job_next_run + result["asset_dbt_job_next_run_humanized"] = attrs.asset_dbt_job_next_run_humanized + result["asset_dbt_environment_name"] = attrs.asset_dbt_environment_name + result["asset_dbt_environment_dbt_version"] = ( + attrs.asset_dbt_environment_dbt_version + ) + result["asset_dbt_tags"] = attrs.asset_dbt_tags + result["asset_dbt_semantic_layer_proxy_url"] = ( + attrs.asset_dbt_semantic_layer_proxy_url + ) + result["asset_dbt_source_freshness_criteria"] = ( + attrs.asset_dbt_source_freshness_criteria + ) + result["sample_data_url"] = attrs.sample_data_url + result["asset_tags"] = attrs.asset_tags + result["asset_mc_incident_names"] = attrs.asset_mc_incident_names + result["asset_mc_incident_qualified_names"] = ( + attrs.asset_mc_incident_qualified_names + ) + result["asset_mc_alert_qualified_names"] = attrs.asset_mc_alert_qualified_names + result["asset_mc_monitor_names"] = attrs.asset_mc_monitor_names + result["asset_mc_monitor_qualified_names"] = attrs.asset_mc_monitor_qualified_names + result["asset_mc_monitor_statuses"] = attrs.asset_mc_monitor_statuses + result["asset_mc_monitor_types"] = attrs.asset_mc_monitor_types + result["asset_mc_monitor_schedule_types"] = attrs.asset_mc_monitor_schedule_types + result["asset_mc_incident_types"] = attrs.asset_mc_incident_types + result["asset_mc_incident_sub_types"] = attrs.asset_mc_incident_sub_types + result["asset_mc_incident_severities"] = attrs.asset_mc_incident_severities + result["asset_mc_incident_priorities"] = attrs.asset_mc_incident_priorities + result["asset_mc_incident_states"] = attrs.asset_mc_incident_states + result["asset_mc_is_monitored"] = attrs.asset_mc_is_monitored + result["asset_mc_last_sync_run_at"] = attrs.asset_mc_last_sync_run_at + result["starred_by"] = attrs.starred_by + result["starred_details_list"] = attrs.starred_details_list + result["starred_count"] = attrs.starred_count + result["asset_anomalo_dq_status"] = attrs.asset_anomalo_dq_status + result["asset_anomalo_check_count"] = attrs.asset_anomalo_check_count + result["asset_anomalo_failed_check_count"] = attrs.asset_anomalo_failed_check_count + result["asset_anomalo_check_statuses"] = attrs.asset_anomalo_check_statuses + result["asset_anomalo_last_check_run_at"] = attrs.asset_anomalo_last_check_run_at + result["asset_anomalo_applied_check_types"] = ( + attrs.asset_anomalo_applied_check_types + ) + result["asset_anomalo_failed_check_types"] = attrs.asset_anomalo_failed_check_types + result["asset_anomalo_source_url"] = attrs.asset_anomalo_source_url + result["asset_soda_dq_status"] = attrs.asset_soda_dq_status + result["asset_soda_check_count"] = attrs.asset_soda_check_count + result["asset_soda_last_sync_run_at"] = attrs.asset_soda_last_sync_run_at + result["asset_soda_last_scan_at"] = attrs.asset_soda_last_scan_at + result["asset_soda_check_statuses"] = attrs.asset_soda_check_statuses + result["asset_soda_source_url"] = attrs.asset_soda_source_url + result["asset_icon"] = attrs.asset_icon + result["asset_external_dq_metadata_details"] = ( + attrs.asset_external_dq_metadata_details + ) + result["is_partial"] = attrs.is_partial + result["is_ai_generated"] = attrs.is_ai_generated + result["asset_cover_image"] = attrs.asset_cover_image + result["asset_theme_hex"] = attrs.asset_theme_hex + result["lexicographical_sort_order"] = attrs.lexicographical_sort_order + result["has_contract"] = attrs.has_contract + result["asset_redirect_guids"] = attrs.asset_redirect_guids + result["asset_policy_guids"] = attrs.asset_policy_guids + result["asset_policies_count"] = attrs.asset_policies_count + result["domain_guids"] = attrs.domain_guids + result["non_compliant_asset_policy_guids"] = attrs.non_compliant_asset_policy_guids + result["product_guids"] = attrs.product_guids + result["output_product_guids"] = attrs.output_product_guids + result["application_qualified_name"] = attrs.application_qualified_name + result["application_field_qualified_name"] = attrs.application_field_qualified_name + result["asset_user_defined_type"] = attrs.asset_user_defined_type + result["asset_internal_popularity_score"] = attrs.asset_internal_popularity_score + result["asset_dq_schedule_type"] = attrs.asset_dq_schedule_type + result["asset_dq_schedule_crontab"] = attrs.asset_dq_schedule_crontab + result["asset_dq_schedule_time_zone"] = attrs.asset_dq_schedule_time_zone + result["asset_dq_schedule_source_sync_status"] = ( + attrs.asset_dq_schedule_source_sync_status + ) + result["asset_dq_schedule_source_synced_at"] = ( + attrs.asset_dq_schedule_source_synced_at + ) + result["asset_dq_schedule_source_sync_error_message"] = ( + attrs.asset_dq_schedule_source_sync_error_message + ) + result["asset_dq_schedule_source_sync_error_code"] = ( + attrs.asset_dq_schedule_source_sync_error_code + ) + result["asset_dq_schedule_source_sync_raw_error"] = ( + attrs.asset_dq_schedule_source_sync_raw_error + ) + result["asset_dq_rule_attached_dimensions"] = ( + attrs.asset_dq_rule_attached_dimensions + ) + result["asset_dq_rule_failed_dimensions"] = attrs.asset_dq_rule_failed_dimensions + result["asset_dq_rule_passed_dimensions"] = attrs.asset_dq_rule_passed_dimensions + result["asset_dq_rule_attached_rule_types"] = ( + attrs.asset_dq_rule_attached_rule_types + ) + result["asset_dq_rule_failed_rule_types"] = attrs.asset_dq_rule_failed_rule_types + result["asset_dq_rule_passed_rule_types"] = attrs.asset_dq_rule_passed_rule_types + result["asset_dq_rule_result_tags"] = attrs.asset_dq_rule_result_tags + result["asset_dq_rule_last_run_at"] = attrs.asset_dq_rule_last_run_at + result["asset_dq_manual_run_status"] = attrs.asset_dq_manual_run_status + result["asset_dq_rule_total_count"] = attrs.asset_dq_rule_total_count + result["asset_dq_rule_failed_count"] = attrs.asset_dq_rule_failed_count + result["asset_dq_rule_passed_count"] = attrs.asset_dq_rule_passed_count + result["asset_dq_result"] = attrs.asset_dq_result + result["asset_dq_freshness_value"] = attrs.asset_dq_freshness_value + result["asset_dq_freshness_expectation"] = attrs.asset_dq_freshness_expectation + result["asset_dq_row_scope_filter_column_qualified_name"] = ( + attrs.asset_dq_row_scope_filter_column_qualified_name + ) + result["asset_space_qualified_name"] = attrs.asset_space_qualified_name + result["asset_space_name"] = attrs.asset_space_name + result["asset_gcp_dataplex_metadata_details"] = ( + attrs.asset_gcp_dataplex_metadata_details + ) + result["asset_gcp_dataplex_aspect_list"] = attrs.asset_gcp_dataplex_aspect_list + result["asset_gcp_dataplex_aspect_field_list"] = ( + attrs.asset_gcp_dataplex_aspect_field_list + ) + result["asset_smus_metadata_form_names"] = attrs.asset_smus_metadata_form_names + result["asset_smus_metadata_form_key_value_details"] = ( + attrs.asset_smus_metadata_form_key_value_details + ) + result["asset_smus_metadata_form_details"] = attrs.asset_smus_metadata_form_details + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _asset_to_nested(asset: Asset) -> AssetNested: + """Convert flat Asset to nested format.""" + attrs = AssetAttributes() + _populate_asset_attrs(attrs, asset) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + asset, _ASSET_REL_FIELDS, AssetRelationshipAttributes + ) + return AssetNested( + guid=asset.guid, + type_name=asset.type_name, + status=asset.status, + version=asset.version, + create_time=asset.create_time, + update_time=asset.update_time, + created_by=asset.created_by, + updated_by=asset.updated_by, + classifications=asset.classifications, + classification_names=asset.classification_names, + meanings=asset.meanings, + labels=asset.labels, + business_attributes=asset.business_attributes, + custom_attributes=asset.custom_attributes, + pending_tasks=asset.pending_tasks, + proxy=asset.proxy, + is_incomplete=asset.is_incomplete, + provenance_type=asset.provenance_type, + home_id=asset.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _asset_from_nested(nested: AssetNested) -> Asset: + """Convert nested format to flat Asset.""" + attrs = nested.attributes if nested.attributes is not UNSET else AssetAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _ASSET_REL_FIELDS, + AssetRelationshipAttributes, + ) + return Asset( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_asset_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _asset_to_nested_bytes(asset: Asset, serde: Serde) -> bytes: + """Convert flat Asset to nested JSON bytes.""" + return serde.encode(_asset_to_nested(asset)) + + +def _asset_from_nested_bytes(data: bytes, serde: Serde) -> Asset: + """Convert nested JSON bytes to flat Asset.""" + nested = serde.decode(data, AssetNested) + return _asset_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + NumericRankField, + RelationField, + TextField, +) + +Asset.NAME = KeywordField("name", "name") +Asset.DISPLAY_NAME = KeywordField("displayName", "displayName") +Asset.DESCRIPTION = KeywordField("description", "description") +Asset.ASSET_SOURCE_README = KeywordTextField( + "assetSourceReadme", "assetSourceReadme", "assetSourceReadme.text" +) +Asset.USER_DESCRIPTION = KeywordField("userDescription", "userDescription") +Asset.ASSET_AI_GENERATED_DESCRIPTION = TextField( + "assetAiGeneratedDescription", "assetAiGeneratedDescription" +) +Asset.ASSET_AI_GENERATED_DESCRIPTION_CONFIDENCE = NumericField( + "assetAiGeneratedDescriptionConfidence", "assetAiGeneratedDescriptionConfidence" +) +Asset.ASSET_AI_GENERATED_DESCRIPTION_REASONING = KeywordField( + "assetAiGeneratedDescriptionReasoning", "assetAiGeneratedDescriptionReasoning" +) +Asset.TENANT_ID = KeywordField("tenantId", "tenantId") +Asset.CERTIFICATE_STATUS = KeywordTextField( + "certificateStatus", "certificateStatus", "certificateStatus.text" +) +Asset.CERTIFICATE_STATUS_MESSAGE = KeywordField( + "certificateStatusMessage", "certificateStatusMessage" +) +Asset.CERTIFICATE_UPDATED_BY = KeywordField( + "certificateUpdatedBy", "certificateUpdatedBy" +) +Asset.CERTIFICATE_UPDATED_AT = NumericField( + "certificateUpdatedAt", "certificateUpdatedAt" +) +Asset.ANNOUNCEMENT_TITLE = KeywordField("announcementTitle", "announcementTitle") +Asset.ANNOUNCEMENT_MESSAGE = KeywordField("announcementMessage", "announcementMessage") +Asset.ANNOUNCEMENT_TYPE = KeywordField("announcementType", "announcementType") +Asset.ANNOUNCEMENT_UPDATED_AT = NumericField( + "announcementUpdatedAt", "announcementUpdatedAt" +) +Asset.ANNOUNCEMENT_UPDATED_BY = KeywordField( + "announcementUpdatedBy", "announcementUpdatedBy" +) +Asset.OWNER_USERS = KeywordField("ownerUsers", "ownerUsers") +Asset.OWNER_GROUPS = KeywordField("ownerGroups", "ownerGroups") +Asset.ADMIN_USERS = KeywordField("adminUsers", "adminUsers") +Asset.ADMIN_GROUPS = KeywordField("adminGroups", "adminGroups") +Asset.VIEWER_USERS = KeywordField("viewerUsers", "viewerUsers") +Asset.VIEWER_GROUPS = KeywordField("viewerGroups", "viewerGroups") +Asset.CONNECTOR_NAME = KeywordField("connectorName", "connectorName") +Asset.CONNECTION_NAME = KeywordTextField( + "connectionName", "connectionName", "connectionName.text" +) +Asset.CONNECTION_QUALIFIED_NAME = KeywordTextField( + "connectionQualifiedName", "connectionQualifiedName", "connectionQualifiedName.text" +) +Asset.HAS_LINEAGE = BooleanField("__hasLineage", "__hasLineage") +Asset.IS_DISCOVERABLE = BooleanField("isDiscoverable", "isDiscoverable") +Asset.IS_EDITABLE = BooleanField("isEditable", "isEditable") +Asset.SUB_TYPE = KeywordField("subType", "subType") +Asset.VIEW_SCORE = NumericField("viewScore", "viewScore") +Asset.POPULARITY_SCORE = NumericField("popularityScore", "popularityScore") +Asset.SOURCE_OWNERS = KeywordField("sourceOwners", "sourceOwners") +Asset.ASSET_SOURCE_ID = KeywordField("assetSourceId", "assetSourceId") +Asset.SOURCE_CREATED_BY = KeywordField("sourceCreatedBy", "sourceCreatedBy") +Asset.SOURCE_CREATED_AT = NumericField("sourceCreatedAt", "sourceCreatedAt") +Asset.SOURCE_UPDATED_AT = NumericField("sourceUpdatedAt", "sourceUpdatedAt") +Asset.SOURCE_UPDATED_BY = KeywordField("sourceUpdatedBy", "sourceUpdatedBy") +Asset.SOURCE_URL = KeywordField("sourceURL", "sourceURL") +Asset.SOURCE_EMBED_URL = KeywordField("sourceEmbedURL", "sourceEmbedURL") +Asset.LAST_SYNC_WORKFLOW_NAME = KeywordField( + "lastSyncWorkflowName", "lastSyncWorkflowName" +) +Asset.LAST_SYNC_RUN_AT = NumericField("lastSyncRunAt", "lastSyncRunAt") +Asset.LAST_SYNC_RUN = KeywordField("lastSyncRun", "lastSyncRun") +Asset.ADMIN_ROLES = KeywordField("adminRoles", "adminRoles") +Asset.SOURCE_READ_COUNT = NumericField("sourceReadCount", "sourceReadCount") +Asset.SOURCE_READ_USER_COUNT = NumericField( + "sourceReadUserCount", "sourceReadUserCount" +) +Asset.SOURCE_LAST_READ_AT = NumericField("sourceLastReadAt", "sourceLastReadAt") +Asset.LAST_ROW_CHANGED_AT = NumericField("lastRowChangedAt", "lastRowChangedAt") +Asset.SOURCE_TOTAL_COST = NumericField("sourceTotalCost", "sourceTotalCost") +Asset.SOURCE_COST_UNIT = KeywordField("sourceCostUnit", "sourceCostUnit") +Asset.SOURCE_READ_QUERY_COST = NumericField( + "sourceReadQueryCost", "sourceReadQueryCost" +) +Asset.SOURCE_READ_RECENT_USER_LIST = KeywordField( + "sourceReadRecentUserList", "sourceReadRecentUserList" +) +Asset.SOURCE_READ_RECENT_USER_RECORD_LIST = KeywordField( + "sourceReadRecentUserRecordList", "sourceReadRecentUserRecordList" +) +Asset.SOURCE_READ_TOP_USER_LIST = KeywordField( + "sourceReadTopUserList", "sourceReadTopUserList" +) +Asset.SOURCE_READ_TOP_USER_RECORD_LIST = KeywordField( + "sourceReadTopUserRecordList", "sourceReadTopUserRecordList" +) +Asset.SOURCE_READ_POPULAR_QUERY_RECORD_LIST = KeywordField( + "sourceReadPopularQueryRecordList", "sourceReadPopularQueryRecordList" +) +Asset.SOURCE_READ_EXPENSIVE_QUERY_RECORD_LIST = KeywordField( + "sourceReadExpensiveQueryRecordList", "sourceReadExpensiveQueryRecordList" +) +Asset.SOURCE_READ_SLOW_QUERY_RECORD_LIST = KeywordField( + "sourceReadSlowQueryRecordList", "sourceReadSlowQueryRecordList" +) +Asset.SOURCE_QUERY_COMPUTE_COST_LIST = KeywordField( + "sourceQueryComputeCostList", "sourceQueryComputeCostList" +) +Asset.SOURCE_QUERY_COMPUTE_COST_RECORD_LIST = KeywordField( + "sourceQueryComputeCostRecordList", "sourceQueryComputeCostRecordList" +) +Asset.DBT_QUALIFIED_NAME = KeywordTextField( + "dbtQualifiedName", "dbtQualifiedName", "dbtQualifiedName.text" +) +Asset.ASSET_DBT_WORKFLOW_LAST_UPDATED = KeywordField( + "assetDbtWorkflowLastUpdated", "assetDbtWorkflowLastUpdated" +) +Asset.ASSET_DBT_ALIAS = KeywordField("assetDbtAlias", "assetDbtAlias") +Asset.ASSET_DBT_META = KeywordField("assetDbtMeta", "assetDbtMeta") +Asset.ASSET_DBT_UNIQUE_ID = KeywordField("assetDbtUniqueId", "assetDbtUniqueId") +Asset.ASSET_DBT_ACCOUNT_NAME = KeywordField( + "assetDbtAccountName", "assetDbtAccountName" +) +Asset.ASSET_DBT_PROJECT_NAME = KeywordField( + "assetDbtProjectName", "assetDbtProjectName" +) +Asset.ASSET_DBT_PACKAGE_NAME = KeywordField( + "assetDbtPackageName", "assetDbtPackageName" +) +Asset.ASSET_DBT_JOB_NAME = KeywordField("assetDbtJobName", "assetDbtJobName") +Asset.ASSET_DBT_JOB_SCHEDULE = KeywordField( + "assetDbtJobSchedule", "assetDbtJobSchedule" +) +Asset.ASSET_DBT_JOB_STATUS = KeywordField("assetDbtJobStatus", "assetDbtJobStatus") +Asset.ASSET_DBT_TEST_STATUS = KeywordField("assetDbtTestStatus", "assetDbtTestStatus") +Asset.ASSET_DBT_JOB_SCHEDULE_CRON_HUMANIZED = KeywordField( + "assetDbtJobScheduleCronHumanized", "assetDbtJobScheduleCronHumanized" +) +Asset.ASSET_DBT_JOB_LAST_RUN = NumericField("assetDbtJobLastRun", "assetDbtJobLastRun") +Asset.ASSET_DBT_JOB_LAST_RUN_URL = KeywordField( + "assetDbtJobLastRunUrl", "assetDbtJobLastRunUrl" +) +Asset.ASSET_DBT_JOB_LAST_RUN_CREATED_AT = NumericField( + "assetDbtJobLastRunCreatedAt", "assetDbtJobLastRunCreatedAt" +) +Asset.ASSET_DBT_JOB_LAST_RUN_UPDATED_AT = NumericField( + "assetDbtJobLastRunUpdatedAt", "assetDbtJobLastRunUpdatedAt" +) +Asset.ASSET_DBT_JOB_LAST_RUN_DEQUED_AT = NumericField( + "assetDbtJobLastRunDequedAt", "assetDbtJobLastRunDequedAt" +) +Asset.ASSET_DBT_JOB_LAST_RUN_STARTED_AT = NumericField( + "assetDbtJobLastRunStartedAt", "assetDbtJobLastRunStartedAt" +) +Asset.ASSET_DBT_JOB_LAST_RUN_TOTAL_DURATION = KeywordField( + "assetDbtJobLastRunTotalDuration", "assetDbtJobLastRunTotalDuration" +) +Asset.ASSET_DBT_JOB_LAST_RUN_TOTAL_DURATION_HUMANIZED = KeywordField( + "assetDbtJobLastRunTotalDurationHumanized", + "assetDbtJobLastRunTotalDurationHumanized", +) +Asset.ASSET_DBT_JOB_LAST_RUN_QUEUED_DURATION = KeywordField( + "assetDbtJobLastRunQueuedDuration", "assetDbtJobLastRunQueuedDuration" +) +Asset.ASSET_DBT_JOB_LAST_RUN_QUEUED_DURATION_HUMANIZED = KeywordField( + "assetDbtJobLastRunQueuedDurationHumanized", + "assetDbtJobLastRunQueuedDurationHumanized", +) +Asset.ASSET_DBT_JOB_LAST_RUN_RUN_DURATION = KeywordField( + "assetDbtJobLastRunRunDuration", "assetDbtJobLastRunRunDuration" +) +Asset.ASSET_DBT_JOB_LAST_RUN_RUN_DURATION_HUMANIZED = KeywordField( + "assetDbtJobLastRunRunDurationHumanized", "assetDbtJobLastRunRunDurationHumanized" +) +Asset.ASSET_DBT_JOB_LAST_RUN_GIT_BRANCH = KeywordTextField( + "assetDbtJobLastRunGitBranch", + "assetDbtJobLastRunGitBranch", + "assetDbtJobLastRunGitBranch.text", +) +Asset.ASSET_DBT_JOB_LAST_RUN_GIT_SHA = KeywordField( + "assetDbtJobLastRunGitSha", "assetDbtJobLastRunGitSha" +) +Asset.ASSET_DBT_JOB_LAST_RUN_STATUS_MESSAGE = KeywordField( + "assetDbtJobLastRunStatusMessage", "assetDbtJobLastRunStatusMessage" +) +Asset.ASSET_DBT_JOB_LAST_RUN_OWNER_THREAD_ID = KeywordField( + "assetDbtJobLastRunOwnerThreadId", "assetDbtJobLastRunOwnerThreadId" +) +Asset.ASSET_DBT_JOB_LAST_RUN_EXECUTED_BY_THREAD_ID = KeywordField( + "assetDbtJobLastRunExecutedByThreadId", "assetDbtJobLastRunExecutedByThreadId" +) +Asset.ASSET_DBT_JOB_LAST_RUN_ARTIFACTS_SAVED = BooleanField( + "assetDbtJobLastRunArtifactsSaved", "assetDbtJobLastRunArtifactsSaved" +) +Asset.ASSET_DBT_JOB_LAST_RUN_ARTIFACT_S3_PATH = KeywordField( + "assetDbtJobLastRunArtifactS3Path", "assetDbtJobLastRunArtifactS3Path" +) +Asset.ASSET_DBT_JOB_LAST_RUN_HAS_DOCS_GENERATED = BooleanField( + "assetDbtJobLastRunHasDocsGenerated", "assetDbtJobLastRunHasDocsGenerated" +) +Asset.ASSET_DBT_JOB_LAST_RUN_HAS_SOURCES_GENERATED = BooleanField( + "assetDbtJobLastRunHasSourcesGenerated", "assetDbtJobLastRunHasSourcesGenerated" +) +Asset.ASSET_DBT_JOB_LAST_RUN_NOTIFICATIONS_SENT = BooleanField( + "assetDbtJobLastRunNotificationsSent", "assetDbtJobLastRunNotificationsSent" +) +Asset.ASSET_DBT_JOB_NEXT_RUN = NumericField("assetDbtJobNextRun", "assetDbtJobNextRun") +Asset.ASSET_DBT_JOB_NEXT_RUN_HUMANIZED = KeywordField( + "assetDbtJobNextRunHumanized", "assetDbtJobNextRunHumanized" +) +Asset.ASSET_DBT_ENVIRONMENT_NAME = KeywordField( + "assetDbtEnvironmentName", "assetDbtEnvironmentName" +) +Asset.ASSET_DBT_ENVIRONMENT_DBT_VERSION = KeywordField( + "assetDbtEnvironmentDbtVersion", "assetDbtEnvironmentDbtVersion" +) +Asset.ASSET_DBT_TAGS = KeywordTextField( + "assetDbtTags", "assetDbtTags", "assetDbtTags.text" +) +Asset.ASSET_DBT_SEMANTIC_LAYER_PROXY_URL = KeywordField( + "assetDbtSemanticLayerProxyUrl", "assetDbtSemanticLayerProxyUrl" +) +Asset.ASSET_DBT_SOURCE_FRESHNESS_CRITERIA = KeywordField( + "assetDbtSourceFreshnessCriteria", "assetDbtSourceFreshnessCriteria" +) +Asset.SAMPLE_DATA_URL = KeywordTextField( + "sampleDataUrl", "sampleDataUrl", "sampleDataUrl.text" +) +Asset.ASSET_TAGS = KeywordTextField("assetTags", "assetTags", "assetTags.text") +Asset.ASSET_MC_INCIDENT_NAMES = KeywordField( + "assetMcIncidentNames", "assetMcIncidentNames" +) +Asset.ASSET_MC_INCIDENT_QUALIFIED_NAMES = KeywordTextField( + "assetMcIncidentQualifiedNames", + "assetMcIncidentQualifiedNames", + "assetMcIncidentQualifiedNames.text", +) +Asset.ASSET_MC_ALERT_QUALIFIED_NAMES = KeywordTextField( + "assetMcAlertQualifiedNames", + "assetMcAlertQualifiedNames", + "assetMcAlertQualifiedNames.text", +) +Asset.ASSET_MC_MONITOR_NAMES = KeywordField( + "assetMcMonitorNames", "assetMcMonitorNames" +) +Asset.ASSET_MC_MONITOR_QUALIFIED_NAMES = KeywordTextField( + "assetMcMonitorQualifiedNames", + "assetMcMonitorQualifiedNames", + "assetMcMonitorQualifiedNames.text", +) +Asset.ASSET_MC_MONITOR_STATUSES = KeywordField( + "assetMcMonitorStatuses", "assetMcMonitorStatuses" +) +Asset.ASSET_MC_MONITOR_TYPES = KeywordField( + "assetMcMonitorTypes", "assetMcMonitorTypes" +) +Asset.ASSET_MC_MONITOR_SCHEDULE_TYPES = KeywordField( + "assetMcMonitorScheduleTypes", "assetMcMonitorScheduleTypes" +) +Asset.ASSET_MC_INCIDENT_TYPES = KeywordField( + "assetMcIncidentTypes", "assetMcIncidentTypes" +) +Asset.ASSET_MC_INCIDENT_SUB_TYPES = KeywordField( + "assetMcIncidentSubTypes", "assetMcIncidentSubTypes" +) +Asset.ASSET_MC_INCIDENT_SEVERITIES = KeywordField( + "assetMcIncidentSeverities", "assetMcIncidentSeverities" +) +Asset.ASSET_MC_INCIDENT_PRIORITIES = KeywordField( + "assetMcIncidentPriorities", "assetMcIncidentPriorities" +) +Asset.ASSET_MC_INCIDENT_STATES = KeywordField( + "assetMcIncidentStates", "assetMcIncidentStates" +) +Asset.ASSET_MC_IS_MONITORED = BooleanField("assetMcIsMonitored", "assetMcIsMonitored") +Asset.ASSET_MC_LAST_SYNC_RUN_AT = NumericField( + "assetMcLastSyncRunAt", "assetMcLastSyncRunAt" +) +Asset.STARRED_BY = KeywordField("starredBy", "starredBy") +Asset.STARRED_DETAILS_LIST = KeywordField("starredDetailsList", "starredDetailsList") +Asset.STARRED_COUNT = NumericField("starredCount", "starredCount") +Asset.ASSET_ANOMALO_DQ_STATUS = KeywordField( + "assetAnomaloDQStatus", "assetAnomaloDQStatus" +) +Asset.ASSET_ANOMALO_CHECK_COUNT = NumericField( + "assetAnomaloCheckCount", "assetAnomaloCheckCount" +) +Asset.ASSET_ANOMALO_FAILED_CHECK_COUNT = NumericField( + "assetAnomaloFailedCheckCount", "assetAnomaloFailedCheckCount" +) +Asset.ASSET_ANOMALO_CHECK_STATUSES = KeywordField( + "assetAnomaloCheckStatuses", "assetAnomaloCheckStatuses" +) +Asset.ASSET_ANOMALO_LAST_CHECK_RUN_AT = NumericField( + "assetAnomaloLastCheckRunAt", "assetAnomaloLastCheckRunAt" +) +Asset.ASSET_ANOMALO_APPLIED_CHECK_TYPES = KeywordField( + "assetAnomaloAppliedCheckTypes", "assetAnomaloAppliedCheckTypes" +) +Asset.ASSET_ANOMALO_FAILED_CHECK_TYPES = KeywordField( + "assetAnomaloFailedCheckTypes", "assetAnomaloFailedCheckTypes" +) +Asset.ASSET_ANOMALO_SOURCE_URL = KeywordField( + "assetAnomaloSourceUrl", "assetAnomaloSourceUrl" +) +Asset.ASSET_SODA_DQ_STATUS = KeywordField("assetSodaDQStatus", "assetSodaDQStatus") +Asset.ASSET_SODA_CHECK_COUNT = NumericField( + "assetSodaCheckCount", "assetSodaCheckCount" +) +Asset.ASSET_SODA_LAST_SYNC_RUN_AT = NumericField( + "assetSodaLastSyncRunAt", "assetSodaLastSyncRunAt" +) +Asset.ASSET_SODA_LAST_SCAN_AT = NumericField( + "assetSodaLastScanAt", "assetSodaLastScanAt" +) +Asset.ASSET_SODA_CHECK_STATUSES = KeywordField( + "assetSodaCheckStatuses", "assetSodaCheckStatuses" +) +Asset.ASSET_SODA_SOURCE_URL = KeywordField("assetSodaSourceURL", "assetSodaSourceURL") +Asset.ASSET_ICON = KeywordField("assetIcon", "assetIcon") +Asset.ASSET_EXTERNAL_DQ_METADATA_DETAILS = KeywordField( + "assetExternalDQMetadataDetails", "assetExternalDQMetadataDetails" +) +Asset.IS_PARTIAL = BooleanField("isPartial", "isPartial") +Asset.IS_AI_GENERATED = BooleanField("isAIGenerated", "isAIGenerated") +Asset.ASSET_COVER_IMAGE = KeywordField("assetCoverImage", "assetCoverImage") +Asset.ASSET_THEME_HEX = KeywordField("assetThemeHex", "assetThemeHex") +Asset.LEXICOGRAPHICAL_SORT_ORDER = KeywordField( + "lexicographicalSortOrder", "lexicographicalSortOrder" +) +Asset.HAS_CONTRACT = BooleanField("hasContract", "hasContract") +Asset.ASSET_REDIRECT_GUIDS = KeywordField("assetRedirectGUIDs", "assetRedirectGUIDs") +Asset.ASSET_POLICY_GUIDS = KeywordField("assetPolicyGUIDs", "assetPolicyGUIDs") +Asset.ASSET_POLICIES_COUNT = NumericField("assetPoliciesCount", "assetPoliciesCount") +Asset.DOMAIN_GUIDS = KeywordField("domainGUIDs", "domainGUIDs") +Asset.NON_COMPLIANT_ASSET_POLICY_GUIDS = KeywordField( + "nonCompliantAssetPolicyGUIDs", "nonCompliantAssetPolicyGUIDs" +) +Asset.PRODUCT_GUIDS = KeywordField("productGUIDs", "productGUIDs") +Asset.OUTPUT_PRODUCT_GUIDS = KeywordField("outputProductGUIDs", "outputProductGUIDs") +Asset.APPLICATION_QUALIFIED_NAME = KeywordField( + "applicationQualifiedName", "applicationQualifiedName" +) +Asset.APPLICATION_FIELD_QUALIFIED_NAME = KeywordField( + "applicationFieldQualifiedName", "applicationFieldQualifiedName" +) +Asset.ASSET_USER_DEFINED_TYPE = KeywordField( + "assetUserDefinedType", "assetUserDefinedType" +) +Asset.ASSET_INTERNAL_POPULARITY_SCORE = NumericRankField( + "assetInternalPopularityScore", + "assetInternalPopularityScore", + "assetInternalPopularityScore.rank", +) +Asset.ASSET_DQ_SCHEDULE_TYPE = KeywordField( + "assetDQScheduleType", "assetDQScheduleType" +) +Asset.ASSET_DQ_SCHEDULE_CRONTAB = KeywordField( + "assetDQScheduleCrontab", "assetDQScheduleCrontab" +) +Asset.ASSET_DQ_SCHEDULE_TIME_ZONE = KeywordField( + "assetDQScheduleTimeZone", "assetDQScheduleTimeZone" +) +Asset.ASSET_DQ_SCHEDULE_SOURCE_SYNC_STATUS = KeywordField( + "assetDQScheduleSourceSyncStatus", "assetDQScheduleSourceSyncStatus" +) +Asset.ASSET_DQ_SCHEDULE_SOURCE_SYNCED_AT = NumericField( + "assetDQScheduleSourceSyncedAt", "assetDQScheduleSourceSyncedAt" +) +Asset.ASSET_DQ_SCHEDULE_SOURCE_SYNC_ERROR_MESSAGE = TextField( + "assetDQScheduleSourceSyncErrorMessage", "assetDQScheduleSourceSyncErrorMessage" +) +Asset.ASSET_DQ_SCHEDULE_SOURCE_SYNC_ERROR_CODE = KeywordField( + "assetDQScheduleSourceSyncErrorCode", "assetDQScheduleSourceSyncErrorCode" +) +Asset.ASSET_DQ_SCHEDULE_SOURCE_SYNC_RAW_ERROR = TextField( + "assetDQScheduleSourceSyncRawError", "assetDQScheduleSourceSyncRawError" +) +Asset.ASSET_DQ_RULE_ATTACHED_DIMENSIONS = KeywordField( + "assetDQRuleAttachedDimensions", "assetDQRuleAttachedDimensions" +) +Asset.ASSET_DQ_RULE_FAILED_DIMENSIONS = KeywordField( + "assetDQRuleFailedDimensions", "assetDQRuleFailedDimensions" +) +Asset.ASSET_DQ_RULE_PASSED_DIMENSIONS = KeywordField( + "assetDQRulePassedDimensions", "assetDQRulePassedDimensions" +) +Asset.ASSET_DQ_RULE_ATTACHED_RULE_TYPES = KeywordField( + "assetDQRuleAttachedRuleTypes", "assetDQRuleAttachedRuleTypes" +) +Asset.ASSET_DQ_RULE_FAILED_RULE_TYPES = KeywordField( + "assetDQRuleFailedRuleTypes", "assetDQRuleFailedRuleTypes" +) +Asset.ASSET_DQ_RULE_PASSED_RULE_TYPES = KeywordField( + "assetDQRulePassedRuleTypes", "assetDQRulePassedRuleTypes" +) +Asset.ASSET_DQ_RULE_RESULT_TAGS = KeywordField( + "assetDQRuleResultTags", "assetDQRuleResultTags" +) +Asset.ASSET_DQ_RULE_LAST_RUN_AT = NumericField( + "assetDQRuleLastRunAt", "assetDQRuleLastRunAt" +) +Asset.ASSET_DQ_MANUAL_RUN_STATUS = KeywordField( + "assetDQManualRunStatus", "assetDQManualRunStatus" +) +Asset.ASSET_DQ_RULE_TOTAL_COUNT = NumericField( + "assetDQRuleTotalCount", "assetDQRuleTotalCount" +) +Asset.ASSET_DQ_RULE_FAILED_COUNT = NumericField( + "assetDQRuleFailedCount", "assetDQRuleFailedCount" +) +Asset.ASSET_DQ_RULE_PASSED_COUNT = NumericField( + "assetDQRulePassedCount", "assetDQRulePassedCount" +) +Asset.ASSET_DQ_RESULT = KeywordField("assetDQResult", "assetDQResult") +Asset.ASSET_DQ_FRESHNESS_VALUE = NumericField( + "assetDQFreshnessValue", "assetDQFreshnessValue" +) +Asset.ASSET_DQ_FRESHNESS_EXPECTATION = NumericField( + "assetDQFreshnessExpectation", "assetDQFreshnessExpectation" +) +Asset.ASSET_DQ_ROW_SCOPE_FILTER_COLUMN_QUALIFIED_NAME = KeywordField( + "assetDQRowScopeFilterColumnQualifiedName", + "assetDQRowScopeFilterColumnQualifiedName", +) +Asset.ASSET_SPACE_QUALIFIED_NAME = KeywordField( + "assetSpaceQualifiedName", "assetSpaceQualifiedName" +) +Asset.ASSET_SPACE_NAME = KeywordField("assetSpaceName", "assetSpaceName") +Asset.ASSET_GCP_DATAPLEX_METADATA_DETAILS = KeywordField( + "assetGCPDataplexMetadataDetails", "assetGCPDataplexMetadataDetails" +) +Asset.ASSET_GCP_DATAPLEX_ASPECT_LIST = KeywordField( + "assetGCPDataplexAspectList", "assetGCPDataplexAspectList" +) +Asset.ASSET_GCP_DATAPLEX_ASPECT_FIELD_LIST = KeywordField( + "assetGCPDataplexAspectFieldList", "assetGCPDataplexAspectFieldList" +) +Asset.ASSET_SMUS_METADATA_FORM_NAMES = KeywordTextField( + "assetSmusMetadataFormNames", + "assetSmusMetadataFormNames", + "assetSmusMetadataFormNames.text", +) +Asset.ASSET_SMUS_METADATA_FORM_KEY_VALUE_DETAILS = KeywordTextField( + "assetSmusMetadataFormKeyValueDetails", + "assetSmusMetadataFormKeyValueDetails", + "assetSmusMetadataFormKeyValueDetails.text", +) +Asset.ASSET_SMUS_METADATA_FORM_DETAILS = KeywordField( + "assetSmusMetadataFormDetails", "assetSmusMetadataFormDetails" +) +Asset.ANOMALO_CHECKS = RelationField("anomaloChecks") +Asset.APPLICATION = RelationField("application") +Asset.APPLICATION_FIELD = RelationField("applicationField") +Asset.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Asset.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Asset.METRICS = RelationField("metrics") +Asset.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Asset.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Asset.MEANINGS = RelationField("meanings") +Asset.MC_MONITORS = RelationField("mcMonitors") +Asset.MC_INCIDENTS = RelationField("mcIncidents") +Asset.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Asset.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Asset.FILES = RelationField("files") +Asset.LINKS = RelationField("links") +Asset.README = RelationField("readme") +Asset.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Asset.SODA_CHECKS = RelationField("sodaChecks") diff --git a/pyatlan_v9/model/assets/asset_grouping.py b/pyatlan_v9/model/assets/asset_grouping.py new file mode 100644 index 000000000..1f6eb87ca --- /dev/null +++ b/pyatlan_v9/model/assets/asset_grouping.py @@ -0,0 +1,533 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +AssetGrouping asset model with flattened inheritance. + +This module provides: +- AssetGrouping: Flat asset class (easy to use) +- AssetGroupingAttributes: Nested attributes struct (extends AssetAttributes) +- AssetGroupingNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class AssetGrouping(Asset): + """ + Base class for asset grouping entities. + """ + + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "AssetGrouping" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "AssetGrouping" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _asset_grouping_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> AssetGrouping: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + AssetGrouping instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _asset_grouping_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class AssetGroupingAttributes(AssetAttributes): + """AssetGrouping-specific attributes for nested API format.""" + + pass + + +class AssetGroupingRelationshipAttributes(AssetRelationshipAttributes): + """AssetGrouping-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class AssetGroupingNested(AssetNested): + """AssetGrouping in nested API format for high-performance serialization.""" + + attributes: Union[AssetGroupingAttributes, UnsetType] = UNSET + relationship_attributes: Union[AssetGroupingRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + AssetGroupingRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + AssetGroupingRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_ASSET_GROUPING_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_asset_grouping_attrs( + attrs: AssetGroupingAttributes, obj: AssetGrouping +) -> None: + """Populate AssetGrouping-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + + +def _extract_asset_grouping_attrs(attrs: AssetGroupingAttributes) -> dict: + """Extract all AssetGrouping attributes from the attrs struct into a flat dict.""" + return _extract_asset_attrs(attrs) + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _asset_grouping_to_nested(asset_grouping: AssetGrouping) -> AssetGroupingNested: + """Convert flat AssetGrouping to nested format.""" + attrs = AssetGroupingAttributes() + _populate_asset_grouping_attrs(attrs, asset_grouping) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + asset_grouping, _ASSET_GROUPING_REL_FIELDS, AssetGroupingRelationshipAttributes + ) + return AssetGroupingNested( + guid=asset_grouping.guid, + type_name=asset_grouping.type_name, + status=asset_grouping.status, + version=asset_grouping.version, + create_time=asset_grouping.create_time, + update_time=asset_grouping.update_time, + created_by=asset_grouping.created_by, + updated_by=asset_grouping.updated_by, + classifications=asset_grouping.classifications, + classification_names=asset_grouping.classification_names, + meanings=asset_grouping.meanings, + labels=asset_grouping.labels, + business_attributes=asset_grouping.business_attributes, + custom_attributes=asset_grouping.custom_attributes, + pending_tasks=asset_grouping.pending_tasks, + proxy=asset_grouping.proxy, + is_incomplete=asset_grouping.is_incomplete, + provenance_type=asset_grouping.provenance_type, + home_id=asset_grouping.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _asset_grouping_from_nested(nested: AssetGroupingNested) -> AssetGrouping: + """Convert nested format to flat AssetGrouping.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else AssetGroupingAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _ASSET_GROUPING_REL_FIELDS, + AssetGroupingRelationshipAttributes, + ) + return AssetGrouping( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_asset_grouping_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _asset_grouping_to_nested_bytes( + asset_grouping: AssetGrouping, serde: Serde +) -> bytes: + """Convert flat AssetGrouping to nested JSON bytes.""" + return serde.encode(_asset_grouping_to_nested(asset_grouping)) + + +def _asset_grouping_from_nested_bytes(data: bytes, serde: Serde) -> AssetGrouping: + """Convert nested JSON bytes to flat AssetGrouping.""" + nested = serde.decode(data, AssetGroupingNested) + return _asset_grouping_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import RelationField # noqa: E402 + +AssetGrouping.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +AssetGrouping.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +AssetGrouping.ANOMALO_CHECKS = RelationField("anomaloChecks") +AssetGrouping.APPLICATION = RelationField("application") +AssetGrouping.APPLICATION_FIELD = RelationField("applicationField") +AssetGrouping.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +AssetGrouping.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +AssetGrouping.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +AssetGrouping.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +AssetGrouping.METRICS = RelationField("metrics") +AssetGrouping.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +AssetGrouping.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +AssetGrouping.MEANINGS = RelationField("meanings") +AssetGrouping.MC_MONITORS = RelationField("mcMonitors") +AssetGrouping.MC_INCIDENTS = RelationField("mcIncidents") +AssetGrouping.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +AssetGrouping.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +AssetGrouping.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +AssetGrouping.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +AssetGrouping.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +AssetGrouping.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +AssetGrouping.FILES = RelationField("files") +AssetGrouping.LINKS = RelationField("links") +AssetGrouping.README = RelationField("readme") +AssetGrouping.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +AssetGrouping.SODA_CHECKS = RelationField("sodaChecks") +AssetGrouping.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +AssetGrouping.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/asset_grouping_collection.py b/pyatlan_v9/model/assets/asset_grouping_collection.py new file mode 100644 index 000000000..e96f034c9 --- /dev/null +++ b/pyatlan_v9/model/assets/asset_grouping_collection.py @@ -0,0 +1,585 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +AssetGroupingCollection asset model with flattened inheritance. + +This module provides: +- AssetGroupingCollection: Flat asset class (easy to use) +- AssetGroupingCollectionAttributes: Nested attributes struct (extends AssetAttributes) +- AssetGroupingCollectionNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .asset_grouping_related import RelatedAssetGroupingStrategy + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class AssetGroupingCollection(Asset): + """ + User-created collection of assets derived from a grouping strategy. + """ + + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + ASSET_GROUPING_STRATEGY: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "AssetGroupingCollection" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + asset_grouping_strategy: Union[RelatedAssetGroupingStrategy, None, UnsetType] = ( + UNSET + ) + """Grouping strategy from which this collection was created.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "AssetGroupingCollection" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _asset_grouping_collection_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> AssetGroupingCollection: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + AssetGroupingCollection instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _asset_grouping_collection_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class AssetGroupingCollectionAttributes(AssetAttributes): + """AssetGroupingCollection-specific attributes for nested API format.""" + + pass + + +class AssetGroupingCollectionRelationshipAttributes(AssetRelationshipAttributes): + """AssetGroupingCollection-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + asset_grouping_strategy: Union[RelatedAssetGroupingStrategy, None, UnsetType] = ( + UNSET + ) + """Grouping strategy from which this collection was created.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class AssetGroupingCollectionNested(AssetNested): + """AssetGroupingCollection in nested API format for high-performance serialization.""" + + attributes: Union[AssetGroupingCollectionAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + AssetGroupingCollectionRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + AssetGroupingCollectionRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + AssetGroupingCollectionRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_ASSET_GROUPING_COLLECTION_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "asset_grouping_strategy", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_asset_grouping_collection_attrs( + attrs: AssetGroupingCollectionAttributes, obj: AssetGroupingCollection +) -> None: + """Populate AssetGroupingCollection-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + + +def _extract_asset_grouping_collection_attrs( + attrs: AssetGroupingCollectionAttributes, +) -> dict: + """Extract all AssetGroupingCollection attributes from the attrs struct into a flat dict.""" + return _extract_asset_attrs(attrs) + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _asset_grouping_collection_to_nested( + asset_grouping_collection: AssetGroupingCollection, +) -> AssetGroupingCollectionNested: + """Convert flat AssetGroupingCollection to nested format.""" + attrs = AssetGroupingCollectionAttributes() + _populate_asset_grouping_collection_attrs(attrs, asset_grouping_collection) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + asset_grouping_collection, + _ASSET_GROUPING_COLLECTION_REL_FIELDS, + AssetGroupingCollectionRelationshipAttributes, + ) + return AssetGroupingCollectionNested( + guid=asset_grouping_collection.guid, + type_name=asset_grouping_collection.type_name, + status=asset_grouping_collection.status, + version=asset_grouping_collection.version, + create_time=asset_grouping_collection.create_time, + update_time=asset_grouping_collection.update_time, + created_by=asset_grouping_collection.created_by, + updated_by=asset_grouping_collection.updated_by, + classifications=asset_grouping_collection.classifications, + classification_names=asset_grouping_collection.classification_names, + meanings=asset_grouping_collection.meanings, + labels=asset_grouping_collection.labels, + business_attributes=asset_grouping_collection.business_attributes, + custom_attributes=asset_grouping_collection.custom_attributes, + pending_tasks=asset_grouping_collection.pending_tasks, + proxy=asset_grouping_collection.proxy, + is_incomplete=asset_grouping_collection.is_incomplete, + provenance_type=asset_grouping_collection.provenance_type, + home_id=asset_grouping_collection.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _asset_grouping_collection_from_nested( + nested: AssetGroupingCollectionNested, +) -> AssetGroupingCollection: + """Convert nested format to flat AssetGroupingCollection.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else AssetGroupingCollectionAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _ASSET_GROUPING_COLLECTION_REL_FIELDS, + AssetGroupingCollectionRelationshipAttributes, + ) + return AssetGroupingCollection( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_asset_grouping_collection_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _asset_grouping_collection_to_nested_bytes( + asset_grouping_collection: AssetGroupingCollection, serde: Serde +) -> bytes: + """Convert flat AssetGroupingCollection to nested JSON bytes.""" + return serde.encode(_asset_grouping_collection_to_nested(asset_grouping_collection)) + + +def _asset_grouping_collection_from_nested_bytes( + data: bytes, serde: Serde +) -> AssetGroupingCollection: + """Convert nested JSON bytes to flat AssetGroupingCollection.""" + nested = serde.decode(data, AssetGroupingCollectionNested) + return _asset_grouping_collection_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import RelationField # noqa: E402 + +AssetGroupingCollection.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +AssetGroupingCollection.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +AssetGroupingCollection.ANOMALO_CHECKS = RelationField("anomaloChecks") +AssetGroupingCollection.APPLICATION = RelationField("application") +AssetGroupingCollection.APPLICATION_FIELD = RelationField("applicationField") +AssetGroupingCollection.ASSET_GROUPING_STRATEGY = RelationField("assetGroupingStrategy") +AssetGroupingCollection.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +AssetGroupingCollection.INPUT_PORT_DATA_PRODUCTS = RelationField( + "inputPortDataProducts" +) +AssetGroupingCollection.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +AssetGroupingCollection.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +AssetGroupingCollection.METRICS = RelationField("metrics") +AssetGroupingCollection.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +AssetGroupingCollection.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +AssetGroupingCollection.MEANINGS = RelationField("meanings") +AssetGroupingCollection.MC_MONITORS = RelationField("mcMonitors") +AssetGroupingCollection.MC_INCIDENTS = RelationField("mcIncidents") +AssetGroupingCollection.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +AssetGroupingCollection.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +AssetGroupingCollection.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +AssetGroupingCollection.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +AssetGroupingCollection.USER_DEF_RELATIONSHIP_TO = RelationField( + "userDefRelationshipTo" +) +AssetGroupingCollection.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +AssetGroupingCollection.FILES = RelationField("files") +AssetGroupingCollection.LINKS = RelationField("links") +AssetGroupingCollection.README = RelationField("readme") +AssetGroupingCollection.SCHEMA_REGISTRY_SUBJECTS = RelationField( + "schemaRegistrySubjects" +) +AssetGroupingCollection.SODA_CHECKS = RelationField("sodaChecks") +AssetGroupingCollection.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +AssetGroupingCollection.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/asset_grouping_related.py b/pyatlan_v9/model/assets/asset_grouping_related.py new file mode 100644 index 000000000..d5c5ace80 --- /dev/null +++ b/pyatlan_v9/model/assets/asset_grouping_related.py @@ -0,0 +1,67 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for AssetGrouping module. + +This module contains all Related{Type} classes for the AssetGrouping type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + + +from .catalog_related import RelatedCatalog +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedAssetGrouping", + "RelatedAssetGroupingStrategy", + "RelatedAssetGroupingCollection", +] + + +class RelatedAssetGrouping(RelatedCatalog): + """ + Related entity reference for AssetGrouping assets. + + Extends RelatedCatalog with AssetGrouping-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "AssetGrouping" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "AssetGrouping" + + +class RelatedAssetGroupingStrategy(RelatedAssetGrouping): + """ + Related entity reference for AssetGroupingStrategy assets. + + Extends RelatedAssetGrouping with AssetGroupingStrategy-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "AssetGroupingStrategy" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "AssetGroupingStrategy" + + +class RelatedAssetGroupingCollection(RelatedAssetGrouping): + """ + Related entity reference for AssetGroupingCollection assets. + + Extends RelatedAssetGrouping with AssetGroupingCollection-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "AssetGroupingCollection" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "AssetGroupingCollection" diff --git a/pyatlan_v9/model/assets/asset_grouping_strategy.py b/pyatlan_v9/model/assets/asset_grouping_strategy.py new file mode 100644 index 000000000..450a027d0 --- /dev/null +++ b/pyatlan_v9/model/assets/asset_grouping_strategy.py @@ -0,0 +1,574 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +AssetGroupingStrategy asset model with flattened inheritance. + +This module provides: +- AssetGroupingStrategy: Flat asset class (easy to use) +- AssetGroupingStrategyAttributes: Nested attributes struct (extends AssetAttributes) +- AssetGroupingStrategyNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .asset_grouping_related import RelatedAssetGroupingCollection + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class AssetGroupingStrategy(Asset): + """ + Reusable strategy for identifying a dynamic set of assets. + """ + + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + ASSET_GROUPING_COLLECTIONS: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "AssetGroupingStrategy" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + asset_grouping_collections: Union[ + List[RelatedAssetGroupingCollection], None, UnsetType + ] = UNSET + """Collections derived from this grouping strategy.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "AssetGroupingStrategy" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _asset_grouping_strategy_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> AssetGroupingStrategy: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + AssetGroupingStrategy instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _asset_grouping_strategy_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class AssetGroupingStrategyAttributes(AssetAttributes): + """AssetGroupingStrategy-specific attributes for nested API format.""" + + pass + + +class AssetGroupingStrategyRelationshipAttributes(AssetRelationshipAttributes): + """AssetGroupingStrategy-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + asset_grouping_collections: Union[ + List[RelatedAssetGroupingCollection], None, UnsetType + ] = UNSET + """Collections derived from this grouping strategy.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class AssetGroupingStrategyNested(AssetNested): + """AssetGroupingStrategy in nested API format for high-performance serialization.""" + + attributes: Union[AssetGroupingStrategyAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + AssetGroupingStrategyRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + AssetGroupingStrategyRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + AssetGroupingStrategyRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_ASSET_GROUPING_STRATEGY_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "asset_grouping_collections", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_asset_grouping_strategy_attrs( + attrs: AssetGroupingStrategyAttributes, obj: AssetGroupingStrategy +) -> None: + """Populate AssetGroupingStrategy-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + + +def _extract_asset_grouping_strategy_attrs( + attrs: AssetGroupingStrategyAttributes, +) -> dict: + """Extract all AssetGroupingStrategy attributes from the attrs struct into a flat dict.""" + return _extract_asset_attrs(attrs) + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _asset_grouping_strategy_to_nested( + asset_grouping_strategy: AssetGroupingStrategy, +) -> AssetGroupingStrategyNested: + """Convert flat AssetGroupingStrategy to nested format.""" + attrs = AssetGroupingStrategyAttributes() + _populate_asset_grouping_strategy_attrs(attrs, asset_grouping_strategy) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + asset_grouping_strategy, + _ASSET_GROUPING_STRATEGY_REL_FIELDS, + AssetGroupingStrategyRelationshipAttributes, + ) + return AssetGroupingStrategyNested( + guid=asset_grouping_strategy.guid, + type_name=asset_grouping_strategy.type_name, + status=asset_grouping_strategy.status, + version=asset_grouping_strategy.version, + create_time=asset_grouping_strategy.create_time, + update_time=asset_grouping_strategy.update_time, + created_by=asset_grouping_strategy.created_by, + updated_by=asset_grouping_strategy.updated_by, + classifications=asset_grouping_strategy.classifications, + classification_names=asset_grouping_strategy.classification_names, + meanings=asset_grouping_strategy.meanings, + labels=asset_grouping_strategy.labels, + business_attributes=asset_grouping_strategy.business_attributes, + custom_attributes=asset_grouping_strategy.custom_attributes, + pending_tasks=asset_grouping_strategy.pending_tasks, + proxy=asset_grouping_strategy.proxy, + is_incomplete=asset_grouping_strategy.is_incomplete, + provenance_type=asset_grouping_strategy.provenance_type, + home_id=asset_grouping_strategy.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _asset_grouping_strategy_from_nested( + nested: AssetGroupingStrategyNested, +) -> AssetGroupingStrategy: + """Convert nested format to flat AssetGroupingStrategy.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else AssetGroupingStrategyAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _ASSET_GROUPING_STRATEGY_REL_FIELDS, + AssetGroupingStrategyRelationshipAttributes, + ) + return AssetGroupingStrategy( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_asset_grouping_strategy_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _asset_grouping_strategy_to_nested_bytes( + asset_grouping_strategy: AssetGroupingStrategy, serde: Serde +) -> bytes: + """Convert flat AssetGroupingStrategy to nested JSON bytes.""" + return serde.encode(_asset_grouping_strategy_to_nested(asset_grouping_strategy)) + + +def _asset_grouping_strategy_from_nested_bytes( + data: bytes, serde: Serde +) -> AssetGroupingStrategy: + """Convert nested JSON bytes to flat AssetGroupingStrategy.""" + nested = serde.decode(data, AssetGroupingStrategyNested) + return _asset_grouping_strategy_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import RelationField # noqa: E402 + +AssetGroupingStrategy.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +AssetGroupingStrategy.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +AssetGroupingStrategy.ANOMALO_CHECKS = RelationField("anomaloChecks") +AssetGroupingStrategy.APPLICATION = RelationField("application") +AssetGroupingStrategy.APPLICATION_FIELD = RelationField("applicationField") +AssetGroupingStrategy.ASSET_GROUPING_COLLECTIONS = RelationField( + "assetGroupingCollections" +) +AssetGroupingStrategy.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +AssetGroupingStrategy.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +AssetGroupingStrategy.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +AssetGroupingStrategy.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +AssetGroupingStrategy.METRICS = RelationField("metrics") +AssetGroupingStrategy.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +AssetGroupingStrategy.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +AssetGroupingStrategy.MEANINGS = RelationField("meanings") +AssetGroupingStrategy.MC_MONITORS = RelationField("mcMonitors") +AssetGroupingStrategy.MC_INCIDENTS = RelationField("mcIncidents") +AssetGroupingStrategy.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +AssetGroupingStrategy.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +AssetGroupingStrategy.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +AssetGroupingStrategy.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +AssetGroupingStrategy.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +AssetGroupingStrategy.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +AssetGroupingStrategy.FILES = RelationField("files") +AssetGroupingStrategy.LINKS = RelationField("links") +AssetGroupingStrategy.README = RelationField("readme") +AssetGroupingStrategy.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +AssetGroupingStrategy.SODA_CHECKS = RelationField("sodaChecks") +AssetGroupingStrategy.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +AssetGroupingStrategy.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/asset_related.py b/pyatlan_v9/model/assets/asset_related.py new file mode 100644 index 000000000..de751cc1b --- /dev/null +++ b/pyatlan_v9/model/assets/asset_related.py @@ -0,0 +1,769 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Asset module. + +This module contains all Related{Type} classes for the Asset type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Any, Dict, List, Set, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedAsset", + "RelatedDataSet", + "RelatedInfrastructure", + "RelatedProcessExecution", + "RelatedIncident", +] + + +class RelatedAsset(RelatedReferenceable): + """ + Related entity reference for Asset assets. + + Extends RelatedReferenceable with Asset-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Asset" so it serializes correctly + + name: Union[str, None, UnsetType] = UNSET + """Name of this asset. Fallback for display purposes, if displayName is empty.""" + + display_name: Union[str, None, UnsetType] = UNSET + """Human-readable name of this asset used for display purposes (in user interface).""" + + description: Union[str, None, UnsetType] = UNSET + """Description of this asset, for example as crawled from a source. Fallback for display purposes, if userDescription is empty.""" + + asset_source_readme: Union[str, None, UnsetType] = UNSET + """Readme of this asset, as extracted from source. If present, this will be used for the readme in user interface.""" + + user_description: Union[str, None, UnsetType] = UNSET + """Description of this asset, as provided by a user. If present, this will be used for the description in user interface.""" + + asset_ai_generated_description: Union[str, None, UnsetType] = UNSET + """Description of this asset, generated by AI based on the asset's context. Displayed separately in the UI and can be used to overwrite existing descriptions.""" + + asset_ai_generated_description_confidence: Union[float, None, UnsetType] = UNSET + """Confidence score of the AI-generated description, ranging from 0.0 to 1.0.""" + + asset_ai_generated_description_reasoning: Union[str, None, UnsetType] = UNSET + """Reasoning behind the AI-generated description, explaining how the description was derived from the asset's context.""" + + tenant_id: Union[str, None, UnsetType] = UNSET + """Name of the Atlan workspace in which this asset exists.""" + + certificate_status: Union[str, None, UnsetType] = UNSET + """Status of this asset's certification.""" + + certificate_status_message: Union[str, None, UnsetType] = UNSET + """Human-readable descriptive message used to provide further detail to certificateStatus.""" + + certificate_updated_by: Union[str, None, UnsetType] = UNSET + """Name of the user who last updated the certification of this asset.""" + + certificate_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the certification was last updated, in milliseconds.""" + + announcement_title: Union[str, None, UnsetType] = UNSET + """Brief title for the announcement on this asset. Required when announcementType is specified.""" + + announcement_message: Union[str, None, UnsetType] = UNSET + """Detailed message to include in the announcement on this asset.""" + + announcement_type: Union[str, None, UnsetType] = UNSET + """Type of announcement on this asset.""" + + announcement_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the announcement was last updated, in milliseconds.""" + + announcement_updated_by: Union[str, None, UnsetType] = UNSET + """Name of the user who last updated the announcement.""" + + owner_users: Union[Set[str], None, UnsetType] = UNSET + """List of users who own this asset.""" + + owner_groups: Union[Set[str], None, UnsetType] = UNSET + """List of groups who own this asset.""" + + admin_users: Union[Set[str], None, UnsetType] = UNSET + """List of users who administer this asset. (This is only used for certain asset types.)""" + + admin_groups: Union[Set[str], None, UnsetType] = UNSET + """List of groups who administer this asset. (This is only used for certain asset types.)""" + + viewer_users: Union[Set[str], None, UnsetType] = UNSET + """List of users who can view assets contained in a collection. (This is only used for certain asset types.)""" + + viewer_groups: Union[Set[str], None, UnsetType] = UNSET + """List of groups who can view assets contained in a collection. (This is only used for certain asset types.)""" + + connector_name: Union[str, None, UnsetType] = UNSET + """Type of the connector through which this asset is accessible.""" + + connection_name: Union[str, None, UnsetType] = UNSET + """Simple name of the connection through which this asset is accessible.""" + + connection_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the connection through which this asset is accessible.""" + + has_lineage: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="__hasLineage" + ) + """Whether this asset has lineage (true) or not (false).""" + + is_discoverable: Union[bool, None, UnsetType] = UNSET + """Whether this asset is discoverable through the UI (true) or not (false).""" + + is_editable: Union[bool, None, UnsetType] = UNSET + """Whether this asset can be edited in the UI (true) or not (false).""" + + sub_type: Union[str, None, UnsetType] = UNSET + """Subtype of this asset.""" + + view_score: Union[float, None, UnsetType] = UNSET + """View score for this asset.""" + + popularity_score: Union[float, None, UnsetType] = UNSET + """Popularity score for this asset.""" + + source_owners: Union[str, None, UnsetType] = UNSET + """List of owners of this asset, in the source system.""" + + asset_source_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for this asset in the system from which it was sourced.""" + + source_created_by: Union[str, None, UnsetType] = UNSET + """Name of the user who created this asset, in the source system.""" + + source_created_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was created in the source system, in milliseconds.""" + + source_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last updated in the source system, in milliseconds.""" + + source_updated_by: Union[str, None, UnsetType] = UNSET + """Name of the user who last updated this asset, in the source system.""" + + source_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sourceURL" + ) + """URL to the resource within the source application, used to create a button to view this asset in the source application.""" + + source_embed_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sourceEmbedURL" + ) + """URL to create an embed for a resource (for example, an image of a dashboard) within Atlan.""" + + last_sync_workflow_name: Union[str, None, UnsetType] = UNSET + """Name of the crawler that last synchronized this asset.""" + + last_sync_run_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last crawled, in milliseconds.""" + + last_sync_run: Union[str, None, UnsetType] = UNSET + """Name of the last run of the crawler that last synchronized this asset.""" + + admin_roles: Union[Set[str], None, UnsetType] = UNSET + """List of roles who administer this asset. (This is only used for Connection assets.)""" + + source_read_count: Union[int, None, UnsetType] = UNSET + """Total count of all read operations at source.""" + + source_read_user_count: Union[int, None, UnsetType] = UNSET + """Total number of unique users that read data from asset.""" + + source_last_read_at: Union[int, None, UnsetType] = UNSET + """Timestamp of most recent read operation.""" + + last_row_changed_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) of the last operation that inserted, updated, or deleted rows, in milliseconds.""" + + source_total_cost: Union[float, None, UnsetType] = UNSET + """Total cost of all operations at source.""" + + source_cost_unit: Union[str, None, UnsetType] = UNSET + """The unit of measure for sourceTotalCost.""" + + source_read_query_cost: Union[float, None, UnsetType] = UNSET + """Total cost of read queries at source.""" + + source_read_recent_user_list: Union[List[str], None, UnsetType] = UNSET + """List of usernames of the most recent users who read this asset.""" + + source_read_recent_user_record_list: Union[ + List[Dict[str, Any]], None, UnsetType + ] = UNSET + """List of usernames with extra insights for the most recent users who read this asset.""" + + source_read_top_user_list: Union[List[str], None, UnsetType] = UNSET + """List of usernames of the users who read this asset the most.""" + + source_read_top_user_record_list: Union[List[Dict[str, Any]], None, UnsetType] = ( + UNSET + ) + """List of usernames with extra insights for the users who read this asset the most.""" + + source_read_popular_query_record_list: Union[ + List[Dict[str, Any]], None, UnsetType + ] = UNSET + """List of the most popular queries that accessed this asset.""" + + source_read_expensive_query_record_list: Union[ + List[Dict[str, Any]], None, UnsetType + ] = UNSET + """List of the most expensive queries that accessed this asset.""" + + source_read_slow_query_record_list: Union[List[Dict[str, Any]], None, UnsetType] = ( + UNSET + ) + """List of the slowest queries that accessed this asset.""" + + source_query_compute_cost_list: Union[List[str], None, UnsetType] = UNSET + """List of most expensive warehouse names.""" + + source_query_compute_cost_record_list: Union[ + List[Dict[str, Any]], None, UnsetType + ] = UNSET + """List of most expensive warehouses with extra insights.""" + + dbt_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of this asset in dbt.""" + + asset_dbt_workflow_last_updated: Union[str, None, UnsetType] = UNSET + """Name of the DBT workflow in Atlan that last updated the asset.""" + + asset_dbt_alias: Union[str, None, UnsetType] = UNSET + """Alias of this asset in dbt.""" + + asset_dbt_meta: Union[str, None, UnsetType] = UNSET + """Metadata for this asset in dbt, specifically everything under the 'meta' key in the dbt object.""" + + asset_dbt_unique_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of this asset in dbt.""" + + asset_dbt_account_name: Union[str, None, UnsetType] = UNSET + """Name of the account in which this asset exists in dbt.""" + + asset_dbt_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which this asset exists in dbt.""" + + asset_dbt_package_name: Union[str, None, UnsetType] = UNSET + """Name of the package in which this asset exists in dbt.""" + + asset_dbt_job_name: Union[str, None, UnsetType] = UNSET + """Name of the job that materialized this asset in dbt.""" + + asset_dbt_job_schedule: Union[str, None, UnsetType] = UNSET + """Schedule of the job that materialized this asset in dbt.""" + + asset_dbt_job_status: Union[str, None, UnsetType] = UNSET + """Status of the job that materialized this asset in dbt.""" + + asset_dbt_test_status: Union[str, None, UnsetType] = UNSET + """All associated dbt test statuses.""" + + asset_dbt_job_schedule_cron_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable cron schedule of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt last ran, in milliseconds.""" + + asset_dbt_job_last_run_url: Union[str, None, UnsetType] = UNSET + """URL of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_created_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt was last created, in milliseconds.""" + + asset_dbt_job_last_run_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt was last updated, in milliseconds.""" + + asset_dbt_job_last_run_dequed_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt was dequeued, in milliseconds.""" + + asset_dbt_job_last_run_started_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt was started running, in milliseconds.""" + + asset_dbt_job_last_run_total_duration: Union[str, None, UnsetType] = UNSET + """Total duration of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_total_duration_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable total duration of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_queued_duration: Union[str, None, UnsetType] = UNSET + """Total duration the job that materialized this asset in dbt spent being queued.""" + + asset_dbt_job_last_run_queued_duration_humanized: Union[str, None, UnsetType] = ( + UNSET + ) + """Human-readable total duration of the last run of the job that materialized this asset in dbt spend being queued.""" + + asset_dbt_job_last_run_run_duration: Union[str, None, UnsetType] = UNSET + """Run duration of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_run_duration_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable run duration of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_git_branch: Union[str, None, UnsetType] = UNSET + """Branch in git from which the last run of the job that materialized this asset in dbt ran.""" + + asset_dbt_job_last_run_git_sha: Union[str, None, UnsetType] = UNSET + """SHA hash in git for the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_status_message: Union[str, None, UnsetType] = UNSET + """Status message of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_owner_thread_id: Union[str, None, UnsetType] = UNSET + """Thread ID of the owner of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_executed_by_thread_id: Union[str, None, UnsetType] = UNSET + """Thread ID of the user who executed the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_artifacts_saved: Union[bool, None, UnsetType] = UNSET + """Whether artifacts were saved from the last run of the job that materialized this asset in dbt (true) or not (false).""" + + asset_dbt_job_last_run_artifact_s3_path: Union[str, None, UnsetType] = UNSET + """Path in S3 to the artifacts saved from the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_has_docs_generated: Union[bool, None, UnsetType] = UNSET + """Whether docs were generated from the last run of the job that materialized this asset in dbt (true) or not (false).""" + + asset_dbt_job_last_run_has_sources_generated: Union[bool, None, UnsetType] = UNSET + """Whether sources were generated from the last run of the job that materialized this asset in dbt (true) or not (false).""" + + asset_dbt_job_last_run_notifications_sent: Union[bool, None, UnsetType] = UNSET + """Whether notifications were sent from the last run of the job that materialized this asset in dbt (true) or not (false).""" + + asset_dbt_job_next_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) when the next run of the job that materializes this asset in dbt is scheduled.""" + + asset_dbt_job_next_run_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable time when the next run of the job that materializes this asset in dbt is scheduled.""" + + asset_dbt_environment_name: Union[str, None, UnsetType] = UNSET + """Name of the environment in which this asset is materialized in dbt.""" + + asset_dbt_environment_dbt_version: Union[str, None, UnsetType] = UNSET + """Version of the environment in which this asset is materialized in dbt.""" + + asset_dbt_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset in dbt.""" + + asset_dbt_semantic_layer_proxy_url: Union[str, None, UnsetType] = UNSET + """URL of the semantic layer proxy for this asset in dbt.""" + + asset_dbt_source_freshness_criteria: Union[str, None, UnsetType] = UNSET + """Freshness criteria for the source of this asset in dbt.""" + + sample_data_url: Union[str, None, UnsetType] = UNSET + """URL for sample data for this asset.""" + + asset_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset.""" + + asset_mc_incident_names: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident names attached to this asset.""" + + asset_mc_incident_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of unique Monte Carlo incident names attached to this asset.""" + + asset_mc_alert_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of unique Monte Carlo alert names attached to this asset.""" + + asset_mc_monitor_names: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo monitor names attached to this asset.""" + + asset_mc_monitor_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of unique Monte Carlo monitor names attached to this asset.""" + + asset_mc_monitor_statuses: Union[List[str], None, UnsetType] = UNSET + """Statuses of all associated Monte Carlo monitors.""" + + asset_mc_monitor_types: Union[List[str], None, UnsetType] = UNSET + """Types of all associated Monte Carlo monitors.""" + + asset_mc_monitor_schedule_types: Union[List[str], None, UnsetType] = UNSET + """Schedules of all associated Monte Carlo monitors.""" + + asset_mc_incident_types: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident types associated with this asset.""" + + asset_mc_incident_sub_types: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident sub-types associated with this asset.""" + + asset_mc_incident_severities: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident severities associated with this asset.""" + + asset_mc_incident_priorities: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident priorities associated with this asset.""" + + asset_mc_incident_states: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident states associated with this asset.""" + + asset_mc_is_monitored: Union[bool, None, UnsetType] = UNSET + """Tracks whether this asset is monitored by MC or not""" + + asset_mc_last_sync_run_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last synced from Monte Carlo.""" + + starred_by: Union[List[str], None, UnsetType] = UNSET + """Users who have starred this asset.""" + + starred_details_list: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of usernames with extra information of the users who have starred an asset.""" + + starred_count: Union[int, None, UnsetType] = UNSET + """Number of users who have starred this asset.""" + + asset_anomalo_dq_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetAnomaloDQStatus" + ) + """Status of data quality from Anomalo.""" + + asset_anomalo_check_count: Union[int, None, UnsetType] = UNSET + """Total number of checks present in Anomalo for this asset.""" + + asset_anomalo_failed_check_count: Union[int, None, UnsetType] = UNSET + """Total number of checks failed in Anomalo for this asset.""" + + asset_anomalo_check_statuses: Union[str, None, UnsetType] = UNSET + """Stringified JSON object containing status of all Anomalo checks associated to this asset.""" + + asset_anomalo_last_check_run_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the last check was run via Anomalo.""" + + asset_anomalo_applied_check_types: Union[List[str], None, UnsetType] = UNSET + """All associated Anomalo check types.""" + + asset_anomalo_failed_check_types: Union[List[str], None, UnsetType] = UNSET + """All associated Anomalo failed check types.""" + + asset_anomalo_source_url: Union[str, None, UnsetType] = UNSET + """URL of the source in Anomalo.""" + + asset_soda_dq_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetSodaDQStatus" + ) + """Status of data quality from Soda.""" + + asset_soda_check_count: Union[int, None, UnsetType] = UNSET + """Number of checks done via Soda.""" + + asset_soda_last_sync_run_at: Union[int, None, UnsetType] = UNSET + """""" + + asset_soda_last_scan_at: Union[int, None, UnsetType] = UNSET + """""" + + asset_soda_check_statuses: Union[str, None, UnsetType] = UNSET + """All associated Soda check statuses.""" + + asset_soda_source_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetSodaSourceURL" + ) + """""" + + asset_icon: Union[str, None, UnsetType] = UNSET + """Name of the icon to use for this asset. (Only applies to glossaries, currently.)""" + + asset_external_dq_metadata_details: Union[ + Dict[str, Dict[str, Any]], None, UnsetType + ] = msgspec.field(default=UNSET, name="assetExternalDQMetadataDetails") + """DQ metadata captured for asset from external DQ tool(s).""" + + is_partial: Union[bool, None, UnsetType] = UNSET + """Indicates this asset is not fully-known, if true.""" + + is_ai_generated: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="isAIGenerated" + ) + """""" + + asset_cover_image: Union[str, None, UnsetType] = UNSET + """Cover image to use for this asset in the UI (applicable to only a few asset types).""" + + asset_theme_hex: Union[str, None, UnsetType] = UNSET + """Color (in hexadecimal RGB) to use to represent this asset.""" + + lexicographical_sort_order: Union[str, None, UnsetType] = UNSET + """Custom order for sorting purpose, managed by client""" + + has_contract: Union[bool, None, UnsetType] = UNSET + """Whether this asset has contract (true) or not (false).""" + + asset_redirect_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetRedirectGUIDs" + ) + """Array of asset ids that equivalent to this asset.""" + + asset_policy_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetPolicyGUIDs" + ) + """Array of policy ids governing this asset""" + + asset_policies_count: Union[int, None, UnsetType] = UNSET + """Count of policies inside the asset""" + + domain_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="domainGUIDs" + ) + """Array of domain guids linked to this asset""" + + non_compliant_asset_policy_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="nonCompliantAssetPolicyGUIDs" + ) + """Array of policy ids non-compliant to this asset""" + + product_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="productGUIDs" + ) + """Array of product guids linked to this asset""" + + output_product_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="outputProductGUIDs" + ) + """Array of product guids which have this asset as outputPort""" + + application_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the Application that contains this asset.""" + + application_field_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the ApplicationField that contains this asset.""" + + asset_user_defined_type: Union[str, None, UnsetType] = UNSET + """Name to use for this type of asset, as a subtype of the actual typeName.""" + + asset_internal_popularity_score: Union[float, None, UnsetType] = UNSET + """Internal Popularity score for this asset.""" + + asset_dq_schedule_type: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleType" + ) + """Type of schedule of the DQ rule that will run at datasource.""" + + asset_dq_schedule_crontab: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleCrontab" + ) + """Crontab of the DQ rule that will run at datasource.""" + + asset_dq_schedule_time_zone: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleTimeZone" + ) + """Timezone of the DQ rule schedule that will run at datasource""" + + asset_dq_schedule_source_sync_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleSourceSyncStatus" + ) + """Latest sync status of the schedule to the source.""" + + asset_dq_schedule_source_synced_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleSourceSyncedAt" + ) + """Time (epoch) at which the schedule synced to the source.""" + + asset_dq_schedule_source_sync_error_message: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQScheduleSourceSyncErrorMessage") + ) + """Error message in the case of sync state being "error".""" + + asset_dq_schedule_source_sync_error_code: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQScheduleSourceSyncErrorCode") + ) + """Error code in the case of sync state being "error".""" + + asset_dq_schedule_source_sync_raw_error: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQScheduleSourceSyncRawError") + ) + """Raw error message from the source.""" + + asset_dq_rule_attached_dimensions: Union[List[str], None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQRuleAttachedDimensions") + ) + """List of all the dimensions of attached rules.""" + + asset_dq_rule_failed_dimensions: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleFailedDimensions" + ) + """List of all the dimensions of failed rules.""" + + asset_dq_rule_passed_dimensions: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRulePassedDimensions" + ) + """List of all the dimensions for which all the rules passed.""" + + asset_dq_rule_attached_rule_types: Union[List[str], None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQRuleAttachedRuleTypes") + ) + """List of all the types of attached rules.""" + + asset_dq_rule_failed_rule_types: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleFailedRuleTypes" + ) + """List of all the types of failed rules.""" + + asset_dq_rule_passed_rule_types: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRulePassedRuleTypes" + ) + """List of all the types of rules for which all the rules passed.""" + + asset_dq_rule_result_tags: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleResultTags" + ) + """Tag for the result of the DQ rules. Eg, rule_pass:completeness:null_count.""" + + asset_dq_rule_last_run_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleLastRunAt" + ) + """Time (epoch) at which the last dq rule ran.""" + + asset_dq_manual_run_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQManualRunStatus" + ) + """Status of the latest manual DQ run triggered for this asset.""" + + asset_dq_rule_total_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleTotalCount" + ) + """Count of DQ rules attached to this asset.""" + + asset_dq_rule_failed_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleFailedCount" + ) + """Count of failed DQ rules attached to this asset.""" + + asset_dq_rule_passed_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRulePassedCount" + ) + """Count of passed DQ rules attached to this asset.""" + + asset_dq_result: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQResult" + ) + """Overall result of all the dq rules. If any one rule failed, then fail else pass.""" + + asset_dq_freshness_value: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQFreshnessValue" + ) + """Value of data freshness from Source.""" + + asset_dq_freshness_expectation: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQFreshnessExpectation" + ) + """Expectation of data freshness from Source.""" + + asset_dq_row_scope_filter_column_qualified_name: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQRowScopeFilterColumnQualifiedName") + ) + """Qualified name of the column used for row scope filtering in DQ rules for this asset.""" + + asset_space_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the space that contains this asset.""" + + asset_space_name: Union[str, None, UnsetType] = UNSET + """Name of the space that contains this asset.""" + + asset_gcp_dataplex_metadata_details: Union[Dict[str, Any], None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetGCPDataplexMetadataDetails") + ) + """Metrics captured by GCP Dataplex for objects associated with GCP services.""" + + asset_gcp_dataplex_aspect_list: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetGCPDataplexAspectList" + ) + """List of names of all Aspects linked to this asset.""" + + asset_gcp_dataplex_aspect_field_list: Union[List[str], None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetGCPDataplexAspectFieldList") + ) + """List of field key-values associated with all Aspects linked to this asset.""" + + asset_smus_metadata_form_names: Union[List[str], None, UnsetType] = UNSET + """List of AWS SMUS MetadataForm Names. This is mainly used for filtering purpose.""" + + asset_smus_metadata_form_key_value_details: Union[List[str], None, UnsetType] = ( + UNSET + ) + """List of AWS SMUS MetadataForm Key:Value Details. This is mainly used for filtering purpose.""" + + asset_smus_metadata_form_details: Union[List[Dict[str, Any]], None, UnsetType] = ( + UNSET + ) + """AWS SMUS Asset MetadataForm details""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Asset" + + +class RelatedDataSet(RelatedAsset): + """ + Related entity reference for DataSet assets. + + Extends RelatedAsset with DataSet-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DataSet" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DataSet" + + +class RelatedInfrastructure(RelatedAsset): + """ + Related entity reference for Infrastructure assets. + + Extends RelatedAsset with Infrastructure-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Infrastructure" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Infrastructure" + + +class RelatedProcessExecution(RelatedAsset): + """ + Related entity reference for ProcessExecution assets. + + Extends RelatedAsset with ProcessExecution-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "ProcessExecution" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "ProcessExecution" + + +class RelatedIncident(RelatedAsset): + """ + Related entity reference for Incident assets. + + Extends RelatedAsset with Incident-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Incident" so it serializes correctly + + asset_severity: Union[str, None, UnsetType] = UNSET + """Status of this asset's severity.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Incident" diff --git a/pyatlan_v9/model/assets/atlan_app.py b/pyatlan_v9/model/assets/atlan_app.py new file mode 100644 index 000000000..0d4ab6f2a --- /dev/null +++ b/pyatlan_v9/model/assets/atlan_app.py @@ -0,0 +1,590 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +AtlanApp asset model with flattened inheritance. + +This module provides: +- AtlanApp: Flat asset class (easy to use) +- AtlanAppAttributes: Nested attributes struct (extends AssetAttributes) +- AtlanAppNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .atlan_app_related import RelatedAtlanAppTool, RelatedAtlanAppWorkflow + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class AtlanApp(Asset): + """ + Base class for all tenant atlan apps types. + """ + + ATLAN_APP_QUALIFIED_NAME: ClassVar[Any] = None + ATLAN_APP_NAME: ClassVar[Any] = None + ATLAN_APP_METADATA: ClassVar[Any] = None + APP_ID: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + ATLAN_APP_TOOLS: ClassVar[Any] = None + ATLAN_APP_WORKFLOWS: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "AtlanApp" + + atlan_app_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the Atlan application this asset belongs to.""" + + atlan_app_name: Union[str, None, UnsetType] = UNSET + """Name of the Atlan application this asset belongs to.""" + + atlan_app_metadata: Union[str, None, UnsetType] = UNSET + """Metadata for the Atlan application (escaped JSON string).""" + + app_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the application asset from the source system.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + atlan_app_tools: Union[List[RelatedAtlanAppTool], None, UnsetType] = UNSET + """Tools that exist within this Atlan application.""" + + atlan_app_workflows: Union[List[RelatedAtlanAppWorkflow], None, UnsetType] = UNSET + """Workflows that exist within this Atlan application.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "AtlanApp" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _atlan_app_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> AtlanApp: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + AtlanApp instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _atlan_app_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class AtlanAppAttributes(AssetAttributes): + """AtlanApp-specific attributes for nested API format.""" + + atlan_app_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the Atlan application this asset belongs to.""" + + atlan_app_name: Union[str, None, UnsetType] = UNSET + """Name of the Atlan application this asset belongs to.""" + + atlan_app_metadata: Union[str, None, UnsetType] = UNSET + """Metadata for the Atlan application (escaped JSON string).""" + + app_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the application asset from the source system.""" + + +class AtlanAppRelationshipAttributes(AssetRelationshipAttributes): + """AtlanApp-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + atlan_app_tools: Union[List[RelatedAtlanAppTool], None, UnsetType] = UNSET + """Tools that exist within this Atlan application.""" + + atlan_app_workflows: Union[List[RelatedAtlanAppWorkflow], None, UnsetType] = UNSET + """Workflows that exist within this Atlan application.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class AtlanAppNested(AssetNested): + """AtlanApp in nested API format for high-performance serialization.""" + + attributes: Union[AtlanAppAttributes, UnsetType] = UNSET + relationship_attributes: Union[AtlanAppRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[AtlanAppRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[AtlanAppRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_ATLAN_APP_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "atlan_app_tools", + "atlan_app_workflows", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_atlan_app_attrs(attrs: AtlanAppAttributes, obj: AtlanApp) -> None: + """Populate AtlanApp-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.atlan_app_qualified_name = obj.atlan_app_qualified_name + attrs.atlan_app_name = obj.atlan_app_name + attrs.atlan_app_metadata = obj.atlan_app_metadata + attrs.app_id = obj.app_id + + +def _extract_atlan_app_attrs(attrs: AtlanAppAttributes) -> dict: + """Extract all AtlanApp attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["atlan_app_qualified_name"] = attrs.atlan_app_qualified_name + result["atlan_app_name"] = attrs.atlan_app_name + result["atlan_app_metadata"] = attrs.atlan_app_metadata + result["app_id"] = attrs.app_id + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _atlan_app_to_nested(atlan_app: AtlanApp) -> AtlanAppNested: + """Convert flat AtlanApp to nested format.""" + attrs = AtlanAppAttributes() + _populate_atlan_app_attrs(attrs, atlan_app) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + atlan_app, _ATLAN_APP_REL_FIELDS, AtlanAppRelationshipAttributes + ) + return AtlanAppNested( + guid=atlan_app.guid, + type_name=atlan_app.type_name, + status=atlan_app.status, + version=atlan_app.version, + create_time=atlan_app.create_time, + update_time=atlan_app.update_time, + created_by=atlan_app.created_by, + updated_by=atlan_app.updated_by, + classifications=atlan_app.classifications, + classification_names=atlan_app.classification_names, + meanings=atlan_app.meanings, + labels=atlan_app.labels, + business_attributes=atlan_app.business_attributes, + custom_attributes=atlan_app.custom_attributes, + pending_tasks=atlan_app.pending_tasks, + proxy=atlan_app.proxy, + is_incomplete=atlan_app.is_incomplete, + provenance_type=atlan_app.provenance_type, + home_id=atlan_app.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _atlan_app_from_nested(nested: AtlanAppNested) -> AtlanApp: + """Convert nested format to flat AtlanApp.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else AtlanAppAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _ATLAN_APP_REL_FIELDS, + AtlanAppRelationshipAttributes, + ) + return AtlanApp( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_atlan_app_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _atlan_app_to_nested_bytes(atlan_app: AtlanApp, serde: Serde) -> bytes: + """Convert flat AtlanApp to nested JSON bytes.""" + return serde.encode(_atlan_app_to_nested(atlan_app)) + + +def _atlan_app_from_nested_bytes(data: bytes, serde: Serde) -> AtlanApp: + """Convert nested JSON bytes to flat AtlanApp.""" + nested = serde.decode(data, AtlanAppNested) + return _atlan_app_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, + TextField, +) + +AtlanApp.ATLAN_APP_QUALIFIED_NAME = KeywordField( + "atlanAppQualifiedName", "atlanAppQualifiedName" +) +AtlanApp.ATLAN_APP_NAME = KeywordField("atlanAppName", "atlanAppName") +AtlanApp.ATLAN_APP_METADATA = TextField("atlanAppMetadata", "atlanAppMetadata") +AtlanApp.APP_ID = KeywordField("appId", "appId") +AtlanApp.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +AtlanApp.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +AtlanApp.ANOMALO_CHECKS = RelationField("anomaloChecks") +AtlanApp.APPLICATION = RelationField("application") +AtlanApp.APPLICATION_FIELD = RelationField("applicationField") +AtlanApp.ATLAN_APP_TOOLS = RelationField("atlanAppTools") +AtlanApp.ATLAN_APP_WORKFLOWS = RelationField("atlanAppWorkflows") +AtlanApp.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +AtlanApp.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +AtlanApp.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +AtlanApp.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +AtlanApp.METRICS = RelationField("metrics") +AtlanApp.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +AtlanApp.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +AtlanApp.MEANINGS = RelationField("meanings") +AtlanApp.MC_MONITORS = RelationField("mcMonitors") +AtlanApp.MC_INCIDENTS = RelationField("mcIncidents") +AtlanApp.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +AtlanApp.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +AtlanApp.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +AtlanApp.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +AtlanApp.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +AtlanApp.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +AtlanApp.FILES = RelationField("files") +AtlanApp.LINKS = RelationField("links") +AtlanApp.README = RelationField("readme") +AtlanApp.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +AtlanApp.SODA_CHECKS = RelationField("sodaChecks") +AtlanApp.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +AtlanApp.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/atlan_app_deployment.py b/pyatlan_v9/model/assets/atlan_app_deployment.py new file mode 100644 index 000000000..07ce730f7 --- /dev/null +++ b/pyatlan_v9/model/assets/atlan_app_deployment.py @@ -0,0 +1,678 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +AtlanAppDeployment asset model with flattened inheritance. + +This module provides: +- AtlanAppDeployment: Flat asset class (easy to use) +- AtlanAppDeploymentAttributes: Nested attributes struct (extends AssetAttributes) +- AtlanAppDeploymentNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .atlan_app_related import RelatedAtlanAppTool, RelatedAtlanAppWorkflow + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class AtlanAppDeployment(Asset): + """ + Tracks pending or completed deployment requests for an atlan application. + """ + + ATLAN_APP_VERSION_ID: ClassVar[Any] = None + ATLAN_APP_VERSION_UUID: ClassVar[Any] = None + ATLAN_APP_STATUS: ClassVar[Any] = None + ATLAN_APP_OPERATION: ClassVar[Any] = None + ATLAN_APP_ERROR_DETAILS: ClassVar[Any] = None + ATLAN_APP_QUALIFIED_NAME: ClassVar[Any] = None + ATLAN_APP_NAME: ClassVar[Any] = None + ATLAN_APP_METADATA: ClassVar[Any] = None + APP_ID: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + ATLAN_APP_TOOLS: ClassVar[Any] = None + ATLAN_APP_WORKFLOWS: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "AtlanAppDeployment" + + atlan_app_version_id: Union[int, None, UnsetType] = UNSET + """Version identifier for deployment.""" + + atlan_app_version_uuid: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="atlanAppVersionUUID" + ) + """Version uuid for deployment. This is externally exposed information.""" + + atlan_app_status: Union[str, None, UnsetType] = UNSET + """Status of deployment.""" + + atlan_app_operation: Union[str, None, UnsetType] = UNSET + """Type of operation requested.""" + + atlan_app_error_details: Union[str, None, UnsetType] = UNSET + """Detailed error message explaining why the deployment failed. Should only be populated when status = FAILED.""" + + atlan_app_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the Atlan application this asset belongs to.""" + + atlan_app_name: Union[str, None, UnsetType] = UNSET + """Name of the Atlan application this asset belongs to.""" + + atlan_app_metadata: Union[str, None, UnsetType] = UNSET + """Metadata for the Atlan application (escaped JSON string).""" + + app_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the application asset from the source system.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + atlan_app_tools: Union[List[RelatedAtlanAppTool], None, UnsetType] = UNSET + """Tools that exist within this Atlan application.""" + + atlan_app_workflows: Union[List[RelatedAtlanAppWorkflow], None, UnsetType] = UNSET + """Workflows that exist within this Atlan application.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "AtlanAppDeployment" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _atlan_app_deployment_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> AtlanAppDeployment: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + AtlanAppDeployment instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _atlan_app_deployment_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class AtlanAppDeploymentAttributes(AssetAttributes): + """AtlanAppDeployment-specific attributes for nested API format.""" + + atlan_app_version_id: Union[int, None, UnsetType] = UNSET + """Version identifier for deployment.""" + + atlan_app_version_uuid: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="atlanAppVersionUUID" + ) + """Version uuid for deployment. This is externally exposed information.""" + + atlan_app_status: Union[str, None, UnsetType] = UNSET + """Status of deployment.""" + + atlan_app_operation: Union[str, None, UnsetType] = UNSET + """Type of operation requested.""" + + atlan_app_error_details: Union[str, None, UnsetType] = UNSET + """Detailed error message explaining why the deployment failed. Should only be populated when status = FAILED.""" + + atlan_app_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the Atlan application this asset belongs to.""" + + atlan_app_name: Union[str, None, UnsetType] = UNSET + """Name of the Atlan application this asset belongs to.""" + + atlan_app_metadata: Union[str, None, UnsetType] = UNSET + """Metadata for the Atlan application (escaped JSON string).""" + + app_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the application asset from the source system.""" + + +class AtlanAppDeploymentRelationshipAttributes(AssetRelationshipAttributes): + """AtlanAppDeployment-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + atlan_app_tools: Union[List[RelatedAtlanAppTool], None, UnsetType] = UNSET + """Tools that exist within this Atlan application.""" + + atlan_app_workflows: Union[List[RelatedAtlanAppWorkflow], None, UnsetType] = UNSET + """Workflows that exist within this Atlan application.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class AtlanAppDeploymentNested(AssetNested): + """AtlanAppDeployment in nested API format for high-performance serialization.""" + + attributes: Union[AtlanAppDeploymentAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + AtlanAppDeploymentRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + AtlanAppDeploymentRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + AtlanAppDeploymentRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_ATLAN_APP_DEPLOYMENT_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "atlan_app_tools", + "atlan_app_workflows", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_atlan_app_deployment_attrs( + attrs: AtlanAppDeploymentAttributes, obj: AtlanAppDeployment +) -> None: + """Populate AtlanAppDeployment-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.atlan_app_version_id = obj.atlan_app_version_id + attrs.atlan_app_version_uuid = obj.atlan_app_version_uuid + attrs.atlan_app_status = obj.atlan_app_status + attrs.atlan_app_operation = obj.atlan_app_operation + attrs.atlan_app_error_details = obj.atlan_app_error_details + attrs.atlan_app_qualified_name = obj.atlan_app_qualified_name + attrs.atlan_app_name = obj.atlan_app_name + attrs.atlan_app_metadata = obj.atlan_app_metadata + attrs.app_id = obj.app_id + + +def _extract_atlan_app_deployment_attrs(attrs: AtlanAppDeploymentAttributes) -> dict: + """Extract all AtlanAppDeployment attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["atlan_app_version_id"] = attrs.atlan_app_version_id + result["atlan_app_version_uuid"] = attrs.atlan_app_version_uuid + result["atlan_app_status"] = attrs.atlan_app_status + result["atlan_app_operation"] = attrs.atlan_app_operation + result["atlan_app_error_details"] = attrs.atlan_app_error_details + result["atlan_app_qualified_name"] = attrs.atlan_app_qualified_name + result["atlan_app_name"] = attrs.atlan_app_name + result["atlan_app_metadata"] = attrs.atlan_app_metadata + result["app_id"] = attrs.app_id + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _atlan_app_deployment_to_nested( + atlan_app_deployment: AtlanAppDeployment, +) -> AtlanAppDeploymentNested: + """Convert flat AtlanAppDeployment to nested format.""" + attrs = AtlanAppDeploymentAttributes() + _populate_atlan_app_deployment_attrs(attrs, atlan_app_deployment) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + atlan_app_deployment, + _ATLAN_APP_DEPLOYMENT_REL_FIELDS, + AtlanAppDeploymentRelationshipAttributes, + ) + return AtlanAppDeploymentNested( + guid=atlan_app_deployment.guid, + type_name=atlan_app_deployment.type_name, + status=atlan_app_deployment.status, + version=atlan_app_deployment.version, + create_time=atlan_app_deployment.create_time, + update_time=atlan_app_deployment.update_time, + created_by=atlan_app_deployment.created_by, + updated_by=atlan_app_deployment.updated_by, + classifications=atlan_app_deployment.classifications, + classification_names=atlan_app_deployment.classification_names, + meanings=atlan_app_deployment.meanings, + labels=atlan_app_deployment.labels, + business_attributes=atlan_app_deployment.business_attributes, + custom_attributes=atlan_app_deployment.custom_attributes, + pending_tasks=atlan_app_deployment.pending_tasks, + proxy=atlan_app_deployment.proxy, + is_incomplete=atlan_app_deployment.is_incomplete, + provenance_type=atlan_app_deployment.provenance_type, + home_id=atlan_app_deployment.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _atlan_app_deployment_from_nested( + nested: AtlanAppDeploymentNested, +) -> AtlanAppDeployment: + """Convert nested format to flat AtlanAppDeployment.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else AtlanAppDeploymentAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _ATLAN_APP_DEPLOYMENT_REL_FIELDS, + AtlanAppDeploymentRelationshipAttributes, + ) + return AtlanAppDeployment( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_atlan_app_deployment_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _atlan_app_deployment_to_nested_bytes( + atlan_app_deployment: AtlanAppDeployment, serde: Serde +) -> bytes: + """Convert flat AtlanAppDeployment to nested JSON bytes.""" + return serde.encode(_atlan_app_deployment_to_nested(atlan_app_deployment)) + + +def _atlan_app_deployment_from_nested_bytes( + data: bytes, serde: Serde +) -> AtlanAppDeployment: + """Convert nested JSON bytes to flat AtlanAppDeployment.""" + nested = serde.decode(data, AtlanAppDeploymentNested) + return _atlan_app_deployment_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, + TextField, +) + +AtlanAppDeployment.ATLAN_APP_VERSION_ID = NumericField( + "atlanAppVersionId", "atlanAppVersionId" +) +AtlanAppDeployment.ATLAN_APP_VERSION_UUID = KeywordField( + "atlanAppVersionUUID", "atlanAppVersionUUID" +) +AtlanAppDeployment.ATLAN_APP_STATUS = KeywordField("atlanAppStatus", "atlanAppStatus") +AtlanAppDeployment.ATLAN_APP_OPERATION = KeywordField( + "atlanAppOperation", "atlanAppOperation" +) +AtlanAppDeployment.ATLAN_APP_ERROR_DETAILS = KeywordField( + "atlanAppErrorDetails", "atlanAppErrorDetails" +) +AtlanAppDeployment.ATLAN_APP_QUALIFIED_NAME = KeywordField( + "atlanAppQualifiedName", "atlanAppQualifiedName" +) +AtlanAppDeployment.ATLAN_APP_NAME = KeywordField("atlanAppName", "atlanAppName") +AtlanAppDeployment.ATLAN_APP_METADATA = TextField( + "atlanAppMetadata", "atlanAppMetadata" +) +AtlanAppDeployment.APP_ID = KeywordField("appId", "appId") +AtlanAppDeployment.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +AtlanAppDeployment.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +AtlanAppDeployment.ANOMALO_CHECKS = RelationField("anomaloChecks") +AtlanAppDeployment.APPLICATION = RelationField("application") +AtlanAppDeployment.APPLICATION_FIELD = RelationField("applicationField") +AtlanAppDeployment.ATLAN_APP_TOOLS = RelationField("atlanAppTools") +AtlanAppDeployment.ATLAN_APP_WORKFLOWS = RelationField("atlanAppWorkflows") +AtlanAppDeployment.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +AtlanAppDeployment.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +AtlanAppDeployment.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +AtlanAppDeployment.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +AtlanAppDeployment.METRICS = RelationField("metrics") +AtlanAppDeployment.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +AtlanAppDeployment.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +AtlanAppDeployment.MEANINGS = RelationField("meanings") +AtlanAppDeployment.MC_MONITORS = RelationField("mcMonitors") +AtlanAppDeployment.MC_INCIDENTS = RelationField("mcIncidents") +AtlanAppDeployment.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +AtlanAppDeployment.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +AtlanAppDeployment.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +AtlanAppDeployment.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +AtlanAppDeployment.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +AtlanAppDeployment.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +AtlanAppDeployment.FILES = RelationField("files") +AtlanAppDeployment.LINKS = RelationField("links") +AtlanAppDeployment.README = RelationField("readme") +AtlanAppDeployment.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +AtlanAppDeployment.SODA_CHECKS = RelationField("sodaChecks") +AtlanAppDeployment.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +AtlanAppDeployment.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/atlan_app_installed.py b/pyatlan_v9/model/assets/atlan_app_installed.py new file mode 100644 index 000000000..5df80ac16 --- /dev/null +++ b/pyatlan_v9/model/assets/atlan_app_installed.py @@ -0,0 +1,652 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +AtlanAppInstalled asset model with flattened inheritance. + +This module provides: +- AtlanAppInstalled: Flat asset class (easy to use) +- AtlanAppInstalledAttributes: Nested attributes struct (extends AssetAttributes) +- AtlanAppInstalledNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .atlan_app_related import RelatedAtlanAppTool, RelatedAtlanAppWorkflow + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class AtlanAppInstalled(Asset): + """ + Represents the currently installed version of an atlan application for a tenant. + """ + + ATLAN_APP_CURRENT_VERSION_ID: ClassVar[Any] = None + ATLAN_APP_CURRENT_VERSION_UUID: ClassVar[Any] = None + ATLAN_APP_DEPLOYMENT_CONFIG: ClassVar[Any] = None + ATLAN_APP_QUALIFIED_NAME: ClassVar[Any] = None + ATLAN_APP_NAME: ClassVar[Any] = None + ATLAN_APP_METADATA: ClassVar[Any] = None + APP_ID: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + ATLAN_APP_TOOLS: ClassVar[Any] = None + ATLAN_APP_WORKFLOWS: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "AtlanAppInstalled" + + atlan_app_current_version_id: Union[int, None, UnsetType] = UNSET + """Current version identifier for the atlan application.""" + + atlan_app_current_version_uuid: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="atlanAppCurrentVersionUUID" + ) + """Current version uuid for the atlan application. This is externally exposed information.""" + + atlan_app_deployment_config: Union[str, None, UnsetType] = UNSET + """Configuration settings used by the atlan application.""" + + atlan_app_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the Atlan application this asset belongs to.""" + + atlan_app_name: Union[str, None, UnsetType] = UNSET + """Name of the Atlan application this asset belongs to.""" + + atlan_app_metadata: Union[str, None, UnsetType] = UNSET + """Metadata for the Atlan application (escaped JSON string).""" + + app_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the application asset from the source system.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + atlan_app_tools: Union[List[RelatedAtlanAppTool], None, UnsetType] = UNSET + """Tools that exist within this Atlan application.""" + + atlan_app_workflows: Union[List[RelatedAtlanAppWorkflow], None, UnsetType] = UNSET + """Workflows that exist within this Atlan application.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "AtlanAppInstalled" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _atlan_app_installed_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> AtlanAppInstalled: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + AtlanAppInstalled instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _atlan_app_installed_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class AtlanAppInstalledAttributes(AssetAttributes): + """AtlanAppInstalled-specific attributes for nested API format.""" + + atlan_app_current_version_id: Union[int, None, UnsetType] = UNSET + """Current version identifier for the atlan application.""" + + atlan_app_current_version_uuid: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="atlanAppCurrentVersionUUID" + ) + """Current version uuid for the atlan application. This is externally exposed information.""" + + atlan_app_deployment_config: Union[str, None, UnsetType] = UNSET + """Configuration settings used by the atlan application.""" + + atlan_app_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the Atlan application this asset belongs to.""" + + atlan_app_name: Union[str, None, UnsetType] = UNSET + """Name of the Atlan application this asset belongs to.""" + + atlan_app_metadata: Union[str, None, UnsetType] = UNSET + """Metadata for the Atlan application (escaped JSON string).""" + + app_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the application asset from the source system.""" + + +class AtlanAppInstalledRelationshipAttributes(AssetRelationshipAttributes): + """AtlanAppInstalled-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + atlan_app_tools: Union[List[RelatedAtlanAppTool], None, UnsetType] = UNSET + """Tools that exist within this Atlan application.""" + + atlan_app_workflows: Union[List[RelatedAtlanAppWorkflow], None, UnsetType] = UNSET + """Workflows that exist within this Atlan application.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class AtlanAppInstalledNested(AssetNested): + """AtlanAppInstalled in nested API format for high-performance serialization.""" + + attributes: Union[AtlanAppInstalledAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + AtlanAppInstalledRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + AtlanAppInstalledRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + AtlanAppInstalledRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_ATLAN_APP_INSTALLED_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "atlan_app_tools", + "atlan_app_workflows", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_atlan_app_installed_attrs( + attrs: AtlanAppInstalledAttributes, obj: AtlanAppInstalled +) -> None: + """Populate AtlanAppInstalled-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.atlan_app_current_version_id = obj.atlan_app_current_version_id + attrs.atlan_app_current_version_uuid = obj.atlan_app_current_version_uuid + attrs.atlan_app_deployment_config = obj.atlan_app_deployment_config + attrs.atlan_app_qualified_name = obj.atlan_app_qualified_name + attrs.atlan_app_name = obj.atlan_app_name + attrs.atlan_app_metadata = obj.atlan_app_metadata + attrs.app_id = obj.app_id + + +def _extract_atlan_app_installed_attrs(attrs: AtlanAppInstalledAttributes) -> dict: + """Extract all AtlanAppInstalled attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["atlan_app_current_version_id"] = attrs.atlan_app_current_version_id + result["atlan_app_current_version_uuid"] = attrs.atlan_app_current_version_uuid + result["atlan_app_deployment_config"] = attrs.atlan_app_deployment_config + result["atlan_app_qualified_name"] = attrs.atlan_app_qualified_name + result["atlan_app_name"] = attrs.atlan_app_name + result["atlan_app_metadata"] = attrs.atlan_app_metadata + result["app_id"] = attrs.app_id + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _atlan_app_installed_to_nested( + atlan_app_installed: AtlanAppInstalled, +) -> AtlanAppInstalledNested: + """Convert flat AtlanAppInstalled to nested format.""" + attrs = AtlanAppInstalledAttributes() + _populate_atlan_app_installed_attrs(attrs, atlan_app_installed) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + atlan_app_installed, + _ATLAN_APP_INSTALLED_REL_FIELDS, + AtlanAppInstalledRelationshipAttributes, + ) + return AtlanAppInstalledNested( + guid=atlan_app_installed.guid, + type_name=atlan_app_installed.type_name, + status=atlan_app_installed.status, + version=atlan_app_installed.version, + create_time=atlan_app_installed.create_time, + update_time=atlan_app_installed.update_time, + created_by=atlan_app_installed.created_by, + updated_by=atlan_app_installed.updated_by, + classifications=atlan_app_installed.classifications, + classification_names=atlan_app_installed.classification_names, + meanings=atlan_app_installed.meanings, + labels=atlan_app_installed.labels, + business_attributes=atlan_app_installed.business_attributes, + custom_attributes=atlan_app_installed.custom_attributes, + pending_tasks=atlan_app_installed.pending_tasks, + proxy=atlan_app_installed.proxy, + is_incomplete=atlan_app_installed.is_incomplete, + provenance_type=atlan_app_installed.provenance_type, + home_id=atlan_app_installed.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _atlan_app_installed_from_nested( + nested: AtlanAppInstalledNested, +) -> AtlanAppInstalled: + """Convert nested format to flat AtlanAppInstalled.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else AtlanAppInstalledAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _ATLAN_APP_INSTALLED_REL_FIELDS, + AtlanAppInstalledRelationshipAttributes, + ) + return AtlanAppInstalled( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_atlan_app_installed_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _atlan_app_installed_to_nested_bytes( + atlan_app_installed: AtlanAppInstalled, serde: Serde +) -> bytes: + """Convert flat AtlanAppInstalled to nested JSON bytes.""" + return serde.encode(_atlan_app_installed_to_nested(atlan_app_installed)) + + +def _atlan_app_installed_from_nested_bytes( + data: bytes, serde: Serde +) -> AtlanAppInstalled: + """Convert nested JSON bytes to flat AtlanAppInstalled.""" + nested = serde.decode(data, AtlanAppInstalledNested) + return _atlan_app_installed_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, + TextField, +) + +AtlanAppInstalled.ATLAN_APP_CURRENT_VERSION_ID = NumericField( + "atlanAppCurrentVersionId", "atlanAppCurrentVersionId" +) +AtlanAppInstalled.ATLAN_APP_CURRENT_VERSION_UUID = KeywordField( + "atlanAppCurrentVersionUUID", "atlanAppCurrentVersionUUID" +) +AtlanAppInstalled.ATLAN_APP_DEPLOYMENT_CONFIG = KeywordField( + "atlanAppDeploymentConfig", "atlanAppDeploymentConfig" +) +AtlanAppInstalled.ATLAN_APP_QUALIFIED_NAME = KeywordField( + "atlanAppQualifiedName", "atlanAppQualifiedName" +) +AtlanAppInstalled.ATLAN_APP_NAME = KeywordField("atlanAppName", "atlanAppName") +AtlanAppInstalled.ATLAN_APP_METADATA = TextField("atlanAppMetadata", "atlanAppMetadata") +AtlanAppInstalled.APP_ID = KeywordField("appId", "appId") +AtlanAppInstalled.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +AtlanAppInstalled.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +AtlanAppInstalled.ANOMALO_CHECKS = RelationField("anomaloChecks") +AtlanAppInstalled.APPLICATION = RelationField("application") +AtlanAppInstalled.APPLICATION_FIELD = RelationField("applicationField") +AtlanAppInstalled.ATLAN_APP_TOOLS = RelationField("atlanAppTools") +AtlanAppInstalled.ATLAN_APP_WORKFLOWS = RelationField("atlanAppWorkflows") +AtlanAppInstalled.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +AtlanAppInstalled.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +AtlanAppInstalled.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +AtlanAppInstalled.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +AtlanAppInstalled.METRICS = RelationField("metrics") +AtlanAppInstalled.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +AtlanAppInstalled.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +AtlanAppInstalled.MEANINGS = RelationField("meanings") +AtlanAppInstalled.MC_MONITORS = RelationField("mcMonitors") +AtlanAppInstalled.MC_INCIDENTS = RelationField("mcIncidents") +AtlanAppInstalled.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +AtlanAppInstalled.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +AtlanAppInstalled.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +AtlanAppInstalled.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +AtlanAppInstalled.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +AtlanAppInstalled.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +AtlanAppInstalled.FILES = RelationField("files") +AtlanAppInstalled.LINKS = RelationField("links") +AtlanAppInstalled.README = RelationField("readme") +AtlanAppInstalled.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +AtlanAppInstalled.SODA_CHECKS = RelationField("sodaChecks") +AtlanAppInstalled.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +AtlanAppInstalled.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/atlan_app_related.py b/pyatlan_v9/model/assets/atlan_app_related.py new file mode 100644 index 000000000..9d400c71f --- /dev/null +++ b/pyatlan_v9/model/assets/atlan_app_related.py @@ -0,0 +1,173 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for AtlanApp module. + +This module contains all Related{Type} classes for the AtlanApp type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Any, Dict, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .app_related import RelatedApp +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedAtlanApp", + "RelatedAtlanAppInstalled", + "RelatedAtlanAppDeployment", + "RelatedAtlanAppTool", + "RelatedAtlanAppWorkflow", +] + + +class RelatedAtlanApp(RelatedApp): + """ + Related entity reference for AtlanApp assets. + + Extends RelatedApp with AtlanApp-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "AtlanApp" so it serializes correctly + + atlan_app_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the Atlan application this asset belongs to.""" + + atlan_app_name: Union[str, None, UnsetType] = UNSET + """Name of the Atlan application this asset belongs to.""" + + atlan_app_metadata: Union[str, None, UnsetType] = UNSET + """Metadata for the Atlan application (escaped JSON string).""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "AtlanApp" + + +class RelatedAtlanAppInstalled(RelatedAtlanApp): + """ + Related entity reference for AtlanAppInstalled assets. + + Extends RelatedAtlanApp with AtlanAppInstalled-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "AtlanAppInstalled" so it serializes correctly + + atlan_app_current_version_id: Union[int, None, UnsetType] = UNSET + """Current version identifier for the atlan application.""" + + atlan_app_current_version_uuid: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="atlanAppCurrentVersionUUID" + ) + """Current version uuid for the atlan application. This is externally exposed information.""" + + atlan_app_deployment_config: Union[str, None, UnsetType] = UNSET + """Configuration settings used by the atlan application.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "AtlanAppInstalled" + + +class RelatedAtlanAppDeployment(RelatedAtlanApp): + """ + Related entity reference for AtlanAppDeployment assets. + + Extends RelatedAtlanApp with AtlanAppDeployment-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "AtlanAppDeployment" so it serializes correctly + + atlan_app_version_id: Union[int, None, UnsetType] = UNSET + """Version identifier for deployment.""" + + atlan_app_version_uuid: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="atlanAppVersionUUID" + ) + """Version uuid for deployment. This is externally exposed information.""" + + atlan_app_status: Union[str, None, UnsetType] = UNSET + """Status of deployment.""" + + atlan_app_operation: Union[str, None, UnsetType] = UNSET + """Type of operation requested.""" + + atlan_app_error_details: Union[str, None, UnsetType] = UNSET + """Detailed error message explaining why the deployment failed. Should only be populated when status = FAILED.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "AtlanAppDeployment" + + +class RelatedAtlanAppTool(RelatedAtlanApp): + """ + Related entity reference for AtlanAppTool assets. + + Extends RelatedAtlanApp with AtlanAppTool-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "AtlanAppTool" so it serializes correctly + + atlan_app_input_schema: Union[str, None, UnsetType] = UNSET + """Input schema for the Atlan application tool (escaped JSON string of JSONSchema).""" + + atlan_app_output_schema: Union[str, None, UnsetType] = UNSET + """Output schema for the Atlan application tool (escaped JSON string of JSONSchema).""" + + atlan_app_task_queue: Union[str, None, UnsetType] = UNSET + """Name of the Temporal task queue for the Atlan application tool.""" + + atlan_app_category: Union[str, None, UnsetType] = UNSET + """Category of the tool.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "AtlanAppTool" + + +class RelatedAtlanAppWorkflow(RelatedAtlanApp): + """ + Related entity reference for AtlanAppWorkflow assets. + + Extends RelatedAtlanApp with AtlanAppWorkflow-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "AtlanAppWorkflow" so it serializes correctly + + atlan_app_version: Union[str, None, UnsetType] = UNSET + """Version of the workflow.""" + + atlan_app_slug: Union[str, None, UnsetType] = UNSET + """Slug of the workflow.""" + + atlan_app_dag: Union[str, None, UnsetType] = UNSET + """Map of all activity steps for the workflow (escaped JSON string).""" + + atlan_app_status: Union[str, None, UnsetType] = UNSET + """Status of the workflow.""" + + atlan_app_error_handling: Union[Dict[str, Any], None, UnsetType] = UNSET + """Error handling strategy for the workflow.""" + + atlan_app_ownership: Union[str, None, UnsetType] = UNSET + """Ownership type of the workflow, indicating whether it is managed by Atlan or by a user.""" + + atlan_app_triggers: Union[str, None, UnsetType] = UNSET + """Triggers configured for this workflow (escaped JSON string).""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "AtlanAppWorkflow" diff --git a/pyatlan_v9/model/assets/atlan_app_tool.py b/pyatlan_v9/model/assets/atlan_app_tool.py new file mode 100644 index 000000000..4b160262a --- /dev/null +++ b/pyatlan_v9/model/assets/atlan_app_tool.py @@ -0,0 +1,664 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +AtlanAppTool asset model with flattened inheritance. + +This module provides: +- AtlanAppTool: Flat asset class (easy to use) +- AtlanAppToolAttributes: Nested attributes struct (extends AssetAttributes) +- AtlanAppToolNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .atlan_app_related import ( + RelatedAtlanApp, + RelatedAtlanAppTool, + RelatedAtlanAppWorkflow, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class AtlanAppTool(Asset): + """ + Instance of a tool defined in an Atlan application. + """ + + ATLAN_APP_INPUT_SCHEMA: ClassVar[Any] = None + ATLAN_APP_OUTPUT_SCHEMA: ClassVar[Any] = None + ATLAN_APP_TASK_QUEUE: ClassVar[Any] = None + ATLAN_APP_CATEGORY: ClassVar[Any] = None + ATLAN_APP_QUALIFIED_NAME: ClassVar[Any] = None + ATLAN_APP_NAME: ClassVar[Any] = None + ATLAN_APP_METADATA: ClassVar[Any] = None + APP_ID: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + ATLAN_APP_TOOLS: ClassVar[Any] = None + ATLAN_APP: ClassVar[Any] = None + ATLAN_APP_WORKFLOWS: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "AtlanAppTool" + + atlan_app_input_schema: Union[str, None, UnsetType] = UNSET + """Input schema for the Atlan application tool (escaped JSON string of JSONSchema).""" + + atlan_app_output_schema: Union[str, None, UnsetType] = UNSET + """Output schema for the Atlan application tool (escaped JSON string of JSONSchema).""" + + atlan_app_task_queue: Union[str, None, UnsetType] = UNSET + """Name of the Temporal task queue for the Atlan application tool.""" + + atlan_app_category: Union[str, None, UnsetType] = UNSET + """Category of the tool.""" + + atlan_app_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the Atlan application this asset belongs to.""" + + atlan_app_name: Union[str, None, UnsetType] = UNSET + """Name of the Atlan application this asset belongs to.""" + + atlan_app_metadata: Union[str, None, UnsetType] = UNSET + """Metadata for the Atlan application (escaped JSON string).""" + + app_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the application asset from the source system.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + atlan_app_tools: Union[List[RelatedAtlanAppTool], None, UnsetType] = UNSET + """Tools that exist within this Atlan application.""" + + atlan_app: Union[RelatedAtlanApp, None, UnsetType] = UNSET + """Atlan application containing the tool.""" + + atlan_app_workflows: Union[List[RelatedAtlanAppWorkflow], None, UnsetType] = UNSET + """Workflows that exist within this Atlan application.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "AtlanAppTool" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _atlan_app_tool_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> AtlanAppTool: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + AtlanAppTool instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _atlan_app_tool_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class AtlanAppToolAttributes(AssetAttributes): + """AtlanAppTool-specific attributes for nested API format.""" + + atlan_app_input_schema: Union[str, None, UnsetType] = UNSET + """Input schema for the Atlan application tool (escaped JSON string of JSONSchema).""" + + atlan_app_output_schema: Union[str, None, UnsetType] = UNSET + """Output schema for the Atlan application tool (escaped JSON string of JSONSchema).""" + + atlan_app_task_queue: Union[str, None, UnsetType] = UNSET + """Name of the Temporal task queue for the Atlan application tool.""" + + atlan_app_category: Union[str, None, UnsetType] = UNSET + """Category of the tool.""" + + atlan_app_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the Atlan application this asset belongs to.""" + + atlan_app_name: Union[str, None, UnsetType] = UNSET + """Name of the Atlan application this asset belongs to.""" + + atlan_app_metadata: Union[str, None, UnsetType] = UNSET + """Metadata for the Atlan application (escaped JSON string).""" + + app_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the application asset from the source system.""" + + +class AtlanAppToolRelationshipAttributes(AssetRelationshipAttributes): + """AtlanAppTool-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + atlan_app_tools: Union[List[RelatedAtlanAppTool], None, UnsetType] = UNSET + """Tools that exist within this Atlan application.""" + + atlan_app: Union[RelatedAtlanApp, None, UnsetType] = UNSET + """Atlan application containing the tool.""" + + atlan_app_workflows: Union[List[RelatedAtlanAppWorkflow], None, UnsetType] = UNSET + """Workflows that exist within this Atlan application.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class AtlanAppToolNested(AssetNested): + """AtlanAppTool in nested API format for high-performance serialization.""" + + attributes: Union[AtlanAppToolAttributes, UnsetType] = UNSET + relationship_attributes: Union[AtlanAppToolRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + AtlanAppToolRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + AtlanAppToolRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_ATLAN_APP_TOOL_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "atlan_app_tools", + "atlan_app", + "atlan_app_workflows", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_atlan_app_tool_attrs( + attrs: AtlanAppToolAttributes, obj: AtlanAppTool +) -> None: + """Populate AtlanAppTool-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.atlan_app_input_schema = obj.atlan_app_input_schema + attrs.atlan_app_output_schema = obj.atlan_app_output_schema + attrs.atlan_app_task_queue = obj.atlan_app_task_queue + attrs.atlan_app_category = obj.atlan_app_category + attrs.atlan_app_qualified_name = obj.atlan_app_qualified_name + attrs.atlan_app_name = obj.atlan_app_name + attrs.atlan_app_metadata = obj.atlan_app_metadata + attrs.app_id = obj.app_id + + +def _extract_atlan_app_tool_attrs(attrs: AtlanAppToolAttributes) -> dict: + """Extract all AtlanAppTool attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["atlan_app_input_schema"] = attrs.atlan_app_input_schema + result["atlan_app_output_schema"] = attrs.atlan_app_output_schema + result["atlan_app_task_queue"] = attrs.atlan_app_task_queue + result["atlan_app_category"] = attrs.atlan_app_category + result["atlan_app_qualified_name"] = attrs.atlan_app_qualified_name + result["atlan_app_name"] = attrs.atlan_app_name + result["atlan_app_metadata"] = attrs.atlan_app_metadata + result["app_id"] = attrs.app_id + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _atlan_app_tool_to_nested(atlan_app_tool: AtlanAppTool) -> AtlanAppToolNested: + """Convert flat AtlanAppTool to nested format.""" + attrs = AtlanAppToolAttributes() + _populate_atlan_app_tool_attrs(attrs, atlan_app_tool) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + atlan_app_tool, _ATLAN_APP_TOOL_REL_FIELDS, AtlanAppToolRelationshipAttributes + ) + return AtlanAppToolNested( + guid=atlan_app_tool.guid, + type_name=atlan_app_tool.type_name, + status=atlan_app_tool.status, + version=atlan_app_tool.version, + create_time=atlan_app_tool.create_time, + update_time=atlan_app_tool.update_time, + created_by=atlan_app_tool.created_by, + updated_by=atlan_app_tool.updated_by, + classifications=atlan_app_tool.classifications, + classification_names=atlan_app_tool.classification_names, + meanings=atlan_app_tool.meanings, + labels=atlan_app_tool.labels, + business_attributes=atlan_app_tool.business_attributes, + custom_attributes=atlan_app_tool.custom_attributes, + pending_tasks=atlan_app_tool.pending_tasks, + proxy=atlan_app_tool.proxy, + is_incomplete=atlan_app_tool.is_incomplete, + provenance_type=atlan_app_tool.provenance_type, + home_id=atlan_app_tool.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _atlan_app_tool_from_nested(nested: AtlanAppToolNested) -> AtlanAppTool: + """Convert nested format to flat AtlanAppTool.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else AtlanAppToolAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _ATLAN_APP_TOOL_REL_FIELDS, + AtlanAppToolRelationshipAttributes, + ) + return AtlanAppTool( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_atlan_app_tool_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _atlan_app_tool_to_nested_bytes( + atlan_app_tool: AtlanAppTool, serde: Serde +) -> bytes: + """Convert flat AtlanAppTool to nested JSON bytes.""" + return serde.encode(_atlan_app_tool_to_nested(atlan_app_tool)) + + +def _atlan_app_tool_from_nested_bytes(data: bytes, serde: Serde) -> AtlanAppTool: + """Convert nested JSON bytes to flat AtlanAppTool.""" + nested = serde.decode(data, AtlanAppToolNested) + return _atlan_app_tool_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, + TextField, +) + +AtlanAppTool.ATLAN_APP_INPUT_SCHEMA = TextField( + "atlanAppInputSchema", "atlanAppInputSchema" +) +AtlanAppTool.ATLAN_APP_OUTPUT_SCHEMA = TextField( + "atlanAppOutputSchema", "atlanAppOutputSchema" +) +AtlanAppTool.ATLAN_APP_TASK_QUEUE = KeywordField( + "atlanAppTaskQueue", "atlanAppTaskQueue" +) +AtlanAppTool.ATLAN_APP_CATEGORY = KeywordField("atlanAppCategory", "atlanAppCategory") +AtlanAppTool.ATLAN_APP_QUALIFIED_NAME = KeywordField( + "atlanAppQualifiedName", "atlanAppQualifiedName" +) +AtlanAppTool.ATLAN_APP_NAME = KeywordField("atlanAppName", "atlanAppName") +AtlanAppTool.ATLAN_APP_METADATA = TextField("atlanAppMetadata", "atlanAppMetadata") +AtlanAppTool.APP_ID = KeywordField("appId", "appId") +AtlanAppTool.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +AtlanAppTool.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +AtlanAppTool.ANOMALO_CHECKS = RelationField("anomaloChecks") +AtlanAppTool.APPLICATION = RelationField("application") +AtlanAppTool.APPLICATION_FIELD = RelationField("applicationField") +AtlanAppTool.ATLAN_APP_TOOLS = RelationField("atlanAppTools") +AtlanAppTool.ATLAN_APP = RelationField("atlanApp") +AtlanAppTool.ATLAN_APP_WORKFLOWS = RelationField("atlanAppWorkflows") +AtlanAppTool.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +AtlanAppTool.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +AtlanAppTool.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +AtlanAppTool.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +AtlanAppTool.METRICS = RelationField("metrics") +AtlanAppTool.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +AtlanAppTool.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +AtlanAppTool.MEANINGS = RelationField("meanings") +AtlanAppTool.MC_MONITORS = RelationField("mcMonitors") +AtlanAppTool.MC_INCIDENTS = RelationField("mcIncidents") +AtlanAppTool.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +AtlanAppTool.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +AtlanAppTool.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +AtlanAppTool.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +AtlanAppTool.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +AtlanAppTool.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +AtlanAppTool.FILES = RelationField("files") +AtlanAppTool.LINKS = RelationField("links") +AtlanAppTool.README = RelationField("readme") +AtlanAppTool.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +AtlanAppTool.SODA_CHECKS = RelationField("sodaChecks") +AtlanAppTool.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +AtlanAppTool.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/atlan_app_workflow.py b/pyatlan_v9/model/assets/atlan_app_workflow.py new file mode 100644 index 000000000..e4233981f --- /dev/null +++ b/pyatlan_v9/model/assets/atlan_app_workflow.py @@ -0,0 +1,712 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +AtlanAppWorkflow asset model with flattened inheritance. + +This module provides: +- AtlanAppWorkflow: Flat asset class (easy to use) +- AtlanAppWorkflowAttributes: Nested attributes struct (extends AssetAttributes) +- AtlanAppWorkflowNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .app_workflow_run_related import RelatedAppWorkflowRun +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .atlan_app_related import ( + RelatedAtlanApp, + RelatedAtlanAppTool, + RelatedAtlanAppWorkflow, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class AtlanAppWorkflow(Asset): + """ + Instance of a workflow in an Atlan application. + """ + + ATLAN_APP_VERSION: ClassVar[Any] = None + ATLAN_APP_SLUG: ClassVar[Any] = None + ATLAN_APP_DAG: ClassVar[Any] = None + ATLAN_APP_STATUS: ClassVar[Any] = None + ATLAN_APP_ERROR_HANDLING: ClassVar[Any] = None + ATLAN_APP_OWNERSHIP: ClassVar[Any] = None + ATLAN_APP_TRIGGERS: ClassVar[Any] = None + ATLAN_APP_QUALIFIED_NAME: ClassVar[Any] = None + ATLAN_APP_NAME: ClassVar[Any] = None + ATLAN_APP_METADATA: ClassVar[Any] = None + APP_ID: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + ATLAN_APP_TOOLS: ClassVar[Any] = None + ATLAN_APP_WORKFLOWS: ClassVar[Any] = None + ATLAN_APP: ClassVar[Any] = None + ATLAN_APP_WORKFLOW_RUNS: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "AtlanAppWorkflow" + + atlan_app_version: Union[str, None, UnsetType] = UNSET + """Version of the workflow.""" + + atlan_app_slug: Union[str, None, UnsetType] = UNSET + """Slug of the workflow.""" + + atlan_app_dag: Union[str, None, UnsetType] = UNSET + """Map of all activity steps for the workflow (escaped JSON string).""" + + atlan_app_status: Union[str, None, UnsetType] = UNSET + """Status of the workflow.""" + + atlan_app_error_handling: Union[Dict[str, Any], None, UnsetType] = UNSET + """Error handling strategy for the workflow.""" + + atlan_app_ownership: Union[str, None, UnsetType] = UNSET + """Ownership type of the workflow, indicating whether it is managed by Atlan or by a user.""" + + atlan_app_triggers: Union[str, None, UnsetType] = UNSET + """Triggers configured for this workflow (escaped JSON string).""" + + atlan_app_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the Atlan application this asset belongs to.""" + + atlan_app_name: Union[str, None, UnsetType] = UNSET + """Name of the Atlan application this asset belongs to.""" + + atlan_app_metadata: Union[str, None, UnsetType] = UNSET + """Metadata for the Atlan application (escaped JSON string).""" + + app_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the application asset from the source system.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + atlan_app_tools: Union[List[RelatedAtlanAppTool], None, UnsetType] = UNSET + """Tools that exist within this Atlan application.""" + + atlan_app_workflows: Union[List[RelatedAtlanAppWorkflow], None, UnsetType] = UNSET + """Workflows that exist within this Atlan application.""" + + atlan_app: Union[RelatedAtlanApp, None, UnsetType] = UNSET + """Atlan application containing the workflow.""" + + atlan_app_workflow_runs: Union[List[RelatedAppWorkflowRun], None, UnsetType] = UNSET + """The workflow runs contained within the workflow.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "AtlanAppWorkflow" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _atlan_app_workflow_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> AtlanAppWorkflow: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + AtlanAppWorkflow instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _atlan_app_workflow_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class AtlanAppWorkflowAttributes(AssetAttributes): + """AtlanAppWorkflow-specific attributes for nested API format.""" + + atlan_app_version: Union[str, None, UnsetType] = UNSET + """Version of the workflow.""" + + atlan_app_slug: Union[str, None, UnsetType] = UNSET + """Slug of the workflow.""" + + atlan_app_dag: Union[str, None, UnsetType] = UNSET + """Map of all activity steps for the workflow (escaped JSON string).""" + + atlan_app_status: Union[str, None, UnsetType] = UNSET + """Status of the workflow.""" + + atlan_app_error_handling: Union[Dict[str, Any], None, UnsetType] = UNSET + """Error handling strategy for the workflow.""" + + atlan_app_ownership: Union[str, None, UnsetType] = UNSET + """Ownership type of the workflow, indicating whether it is managed by Atlan or by a user.""" + + atlan_app_triggers: Union[str, None, UnsetType] = UNSET + """Triggers configured for this workflow (escaped JSON string).""" + + atlan_app_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the Atlan application this asset belongs to.""" + + atlan_app_name: Union[str, None, UnsetType] = UNSET + """Name of the Atlan application this asset belongs to.""" + + atlan_app_metadata: Union[str, None, UnsetType] = UNSET + """Metadata for the Atlan application (escaped JSON string).""" + + app_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the application asset from the source system.""" + + +class AtlanAppWorkflowRelationshipAttributes(AssetRelationshipAttributes): + """AtlanAppWorkflow-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + atlan_app_tools: Union[List[RelatedAtlanAppTool], None, UnsetType] = UNSET + """Tools that exist within this Atlan application.""" + + atlan_app_workflows: Union[List[RelatedAtlanAppWorkflow], None, UnsetType] = UNSET + """Workflows that exist within this Atlan application.""" + + atlan_app: Union[RelatedAtlanApp, None, UnsetType] = UNSET + """Atlan application containing the workflow.""" + + atlan_app_workflow_runs: Union[List[RelatedAppWorkflowRun], None, UnsetType] = UNSET + """The workflow runs contained within the workflow.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class AtlanAppWorkflowNested(AssetNested): + """AtlanAppWorkflow in nested API format for high-performance serialization.""" + + attributes: Union[AtlanAppWorkflowAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + AtlanAppWorkflowRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + AtlanAppWorkflowRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + AtlanAppWorkflowRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_ATLAN_APP_WORKFLOW_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "atlan_app_tools", + "atlan_app_workflows", + "atlan_app", + "atlan_app_workflow_runs", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_atlan_app_workflow_attrs( + attrs: AtlanAppWorkflowAttributes, obj: AtlanAppWorkflow +) -> None: + """Populate AtlanAppWorkflow-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.atlan_app_version = obj.atlan_app_version + attrs.atlan_app_slug = obj.atlan_app_slug + attrs.atlan_app_dag = obj.atlan_app_dag + attrs.atlan_app_status = obj.atlan_app_status + attrs.atlan_app_error_handling = obj.atlan_app_error_handling + attrs.atlan_app_ownership = obj.atlan_app_ownership + attrs.atlan_app_triggers = obj.atlan_app_triggers + attrs.atlan_app_qualified_name = obj.atlan_app_qualified_name + attrs.atlan_app_name = obj.atlan_app_name + attrs.atlan_app_metadata = obj.atlan_app_metadata + attrs.app_id = obj.app_id + + +def _extract_atlan_app_workflow_attrs(attrs: AtlanAppWorkflowAttributes) -> dict: + """Extract all AtlanAppWorkflow attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["atlan_app_version"] = attrs.atlan_app_version + result["atlan_app_slug"] = attrs.atlan_app_slug + result["atlan_app_dag"] = attrs.atlan_app_dag + result["atlan_app_status"] = attrs.atlan_app_status + result["atlan_app_error_handling"] = attrs.atlan_app_error_handling + result["atlan_app_ownership"] = attrs.atlan_app_ownership + result["atlan_app_triggers"] = attrs.atlan_app_triggers + result["atlan_app_qualified_name"] = attrs.atlan_app_qualified_name + result["atlan_app_name"] = attrs.atlan_app_name + result["atlan_app_metadata"] = attrs.atlan_app_metadata + result["app_id"] = attrs.app_id + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _atlan_app_workflow_to_nested( + atlan_app_workflow: AtlanAppWorkflow, +) -> AtlanAppWorkflowNested: + """Convert flat AtlanAppWorkflow to nested format.""" + attrs = AtlanAppWorkflowAttributes() + _populate_atlan_app_workflow_attrs(attrs, atlan_app_workflow) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + atlan_app_workflow, + _ATLAN_APP_WORKFLOW_REL_FIELDS, + AtlanAppWorkflowRelationshipAttributes, + ) + return AtlanAppWorkflowNested( + guid=atlan_app_workflow.guid, + type_name=atlan_app_workflow.type_name, + status=atlan_app_workflow.status, + version=atlan_app_workflow.version, + create_time=atlan_app_workflow.create_time, + update_time=atlan_app_workflow.update_time, + created_by=atlan_app_workflow.created_by, + updated_by=atlan_app_workflow.updated_by, + classifications=atlan_app_workflow.classifications, + classification_names=atlan_app_workflow.classification_names, + meanings=atlan_app_workflow.meanings, + labels=atlan_app_workflow.labels, + business_attributes=atlan_app_workflow.business_attributes, + custom_attributes=atlan_app_workflow.custom_attributes, + pending_tasks=atlan_app_workflow.pending_tasks, + proxy=atlan_app_workflow.proxy, + is_incomplete=atlan_app_workflow.is_incomplete, + provenance_type=atlan_app_workflow.provenance_type, + home_id=atlan_app_workflow.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _atlan_app_workflow_from_nested(nested: AtlanAppWorkflowNested) -> AtlanAppWorkflow: + """Convert nested format to flat AtlanAppWorkflow.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else AtlanAppWorkflowAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _ATLAN_APP_WORKFLOW_REL_FIELDS, + AtlanAppWorkflowRelationshipAttributes, + ) + return AtlanAppWorkflow( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_atlan_app_workflow_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _atlan_app_workflow_to_nested_bytes( + atlan_app_workflow: AtlanAppWorkflow, serde: Serde +) -> bytes: + """Convert flat AtlanAppWorkflow to nested JSON bytes.""" + return serde.encode(_atlan_app_workflow_to_nested(atlan_app_workflow)) + + +def _atlan_app_workflow_from_nested_bytes( + data: bytes, serde: Serde +) -> AtlanAppWorkflow: + """Convert nested JSON bytes to flat AtlanAppWorkflow.""" + nested = serde.decode(data, AtlanAppWorkflowNested) + return _atlan_app_workflow_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, + TextField, +) + +AtlanAppWorkflow.ATLAN_APP_VERSION = KeywordField("atlanAppVersion", "atlanAppVersion") +AtlanAppWorkflow.ATLAN_APP_SLUG = KeywordField("atlanAppSlug", "atlanAppSlug") +AtlanAppWorkflow.ATLAN_APP_DAG = TextField("atlanAppDag", "atlanAppDag") +AtlanAppWorkflow.ATLAN_APP_STATUS = KeywordField("atlanAppStatus", "atlanAppStatus") +AtlanAppWorkflow.ATLAN_APP_ERROR_HANDLING = KeywordField( + "atlanAppErrorHandling", "atlanAppErrorHandling" +) +AtlanAppWorkflow.ATLAN_APP_OWNERSHIP = KeywordField( + "atlanAppOwnership", "atlanAppOwnership" +) +AtlanAppWorkflow.ATLAN_APP_TRIGGERS = TextField("atlanAppTriggers", "atlanAppTriggers") +AtlanAppWorkflow.ATLAN_APP_QUALIFIED_NAME = KeywordField( + "atlanAppQualifiedName", "atlanAppQualifiedName" +) +AtlanAppWorkflow.ATLAN_APP_NAME = KeywordField("atlanAppName", "atlanAppName") +AtlanAppWorkflow.ATLAN_APP_METADATA = TextField("atlanAppMetadata", "atlanAppMetadata") +AtlanAppWorkflow.APP_ID = KeywordField("appId", "appId") +AtlanAppWorkflow.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +AtlanAppWorkflow.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +AtlanAppWorkflow.ANOMALO_CHECKS = RelationField("anomaloChecks") +AtlanAppWorkflow.APPLICATION = RelationField("application") +AtlanAppWorkflow.APPLICATION_FIELD = RelationField("applicationField") +AtlanAppWorkflow.ATLAN_APP_TOOLS = RelationField("atlanAppTools") +AtlanAppWorkflow.ATLAN_APP_WORKFLOWS = RelationField("atlanAppWorkflows") +AtlanAppWorkflow.ATLAN_APP = RelationField("atlanApp") +AtlanAppWorkflow.ATLAN_APP_WORKFLOW_RUNS = RelationField("atlanAppWorkflowRuns") +AtlanAppWorkflow.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +AtlanAppWorkflow.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +AtlanAppWorkflow.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +AtlanAppWorkflow.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +AtlanAppWorkflow.METRICS = RelationField("metrics") +AtlanAppWorkflow.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +AtlanAppWorkflow.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +AtlanAppWorkflow.MEANINGS = RelationField("meanings") +AtlanAppWorkflow.MC_MONITORS = RelationField("mcMonitors") +AtlanAppWorkflow.MC_INCIDENTS = RelationField("mcIncidents") +AtlanAppWorkflow.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +AtlanAppWorkflow.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +AtlanAppWorkflow.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +AtlanAppWorkflow.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +AtlanAppWorkflow.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +AtlanAppWorkflow.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +AtlanAppWorkflow.FILES = RelationField("files") +AtlanAppWorkflow.LINKS = RelationField("links") +AtlanAppWorkflow.README = RelationField("readme") +AtlanAppWorkflow.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +AtlanAppWorkflow.SODA_CHECKS = RelationField("sodaChecks") +AtlanAppWorkflow.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +AtlanAppWorkflow.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/atlas_glossary.py b/pyatlan_v9/model/assets/atlas_glossary.py new file mode 100644 index 000000000..fd8321d01 --- /dev/null +++ b/pyatlan_v9/model/assets/atlas_glossary.py @@ -0,0 +1,319 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +AtlasGlossary asset model with flattened inheritance. + +This module provides: +- AtlasGlossary: Flat asset class (easy to use) +- AtlasGlossaryAttributes: Nested attributes struct (extends AssetAttributes) +- AtlasGlossaryNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Union + +import msgspec +from msgspec import UNSET, UnsetType + +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid + +from .asset import Asset, AssetAttributes, AssetNested, AssetRelationshipAttributes +from .gtc_related import RelatedAtlasGlossaryCategory, RelatedAtlasGlossaryTerm + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class AtlasGlossary(Asset): + """ + Instance of a glossary in Atlan. + """ + + # Override type_name with AtlasGlossary-specific default + type_name: Union[str, UnsetType] = "AtlasGlossary" + + short_description: Union[str, None, UnsetType] = UNSET + """Unused. A short definition of the glossary. See 'description' and 'userDescription' instead.""" + + long_description: Union[str, None, UnsetType] = UNSET + """Unused. A longer description of the glossary. See 'readme' instead.""" + + language: Union[str, None, UnsetType] = UNSET + """Unused. Language of the glossary's contents.""" + + usage: Union[str, None, UnsetType] = UNSET + """Unused. Inteded usage for the glossary.""" + + additional_attributes: Union[dict[str, str], None, UnsetType] = UNSET + """Unused. Arbitrary set of additional attributes associated with this glossary.""" + + glossary_type: Union[str, None, UnsetType] = UNSET + """""" + + terms: Union[list[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Terms contained within this glossary.""" + + categories: Union[list[RelatedAtlasGlossaryCategory], None, UnsetType] = UNSET + """Categories contained within this glossary.""" + + # ========================================================================= + # Creator Methods + # ========================================================================= + + @classmethod + @init_guid + def creator(cls, *, name: str) -> "AtlasGlossary": + """ + Create a new AtlasGlossary asset. + + Args: + name: Name of the glossary + + Returns: + AtlasGlossary instance ready to be created + + Raises: + ValueError: If name is not provided + """ + if not name: + raise ValueError("name is required") + + # Generate a unique qualified name using a simple ID generator + import uuid + + qualified_name = str(uuid.uuid4().hex[:16]) + + return AtlasGlossary(name=name, qualified_name=qualified_name) + + @classmethod + def create(cls, *, name: str) -> "AtlasGlossary": + """ + Create a new AtlasGlossary asset (deprecated - use creator instead). + + Args: + name: Name of the glossary + + Returns: + AtlasGlossary instance ready to be created + """ + return cls.creator(name=name) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return _atlas_glossary_to_nested_bytes(self, serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + @staticmethod + def from_json( + json_data: Union[str, bytes], serde: Serde | None = None + ) -> "AtlasGlossary": + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + AtlasGlossary instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _atlas_glossary_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class AtlasGlossaryAttributes(AssetAttributes): + """AtlasGlossary-specific attributes for nested API format.""" + + short_description: Union[str, None, UnsetType] = UNSET + """Unused. A short definition of the glossary. See 'description' and 'userDescription' instead.""" + + long_description: Union[str, None, UnsetType] = UNSET + """Unused. A longer description of the glossary. See 'readme' instead.""" + + language: Union[str, None, UnsetType] = UNSET + """Unused. Language of the glossary's contents.""" + + usage: Union[str, None, UnsetType] = UNSET + """Unused. Inteded usage for the glossary.""" + + additional_attributes: Union[dict[str, str], None, UnsetType] = UNSET + """Unused. Arbitrary set of additional attributes associated with this glossary.""" + + glossary_type: Union[str, None, UnsetType] = UNSET + """""" + + +class AtlasGlossaryRelationshipAttributes(AssetRelationshipAttributes): + """AtlasGlossary-specific relationship attributes for nested API format.""" + + terms: Union[list[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Terms contained within this glossary.""" + + categories: Union[list[RelatedAtlasGlossaryCategory], None, UnsetType] = UNSET + """Categories contained within this glossary.""" + + +class AtlasGlossaryNested(AssetNested): + """AtlasGlossary in nested API format for high-performance serialization.""" + + attributes: Union[AtlasGlossaryAttributes, UnsetType] = UNSET + relationship_attributes: Union[AtlasGlossaryRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + AtlasGlossaryRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + AtlasGlossaryRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _atlas_glossary_to_nested(atlas_glossary: AtlasGlossary) -> AtlasGlossaryNested: + """Convert flat AtlasGlossary to nested format.""" + # Get all attribute field names dynamically + attr_field_names = {f.name for f in msgspec.structs.fields(AtlasGlossaryAttributes)} + + # Build attributes dict with only the fields that exist in AtlasGlossaryAttributes + attrs_kwargs = { + name: getattr(atlas_glossary, name) + for name in attr_field_names + if hasattr(atlas_glossary, name) + } + attrs = AtlasGlossaryAttributes(**attrs_kwargs) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + rel_fields: list[str] = ["terms", "categories"] + replace_rels, append_rels, remove_rels = categorize_relationships( + atlas_glossary, rel_fields, AtlasGlossaryRelationshipAttributes + ) + return AtlasGlossaryNested( + guid=atlas_glossary.guid, + type_name=atlas_glossary.type_name, + status=atlas_glossary.status, + version=atlas_glossary.version, + create_time=atlas_glossary.create_time, + update_time=atlas_glossary.update_time, + created_by=atlas_glossary.created_by, + updated_by=atlas_glossary.updated_by, + classifications=atlas_glossary.classifications, + classification_names=atlas_glossary.classification_names, + meanings=atlas_glossary.meanings, + labels=atlas_glossary.labels, + business_attributes=atlas_glossary.business_attributes, + custom_attributes=atlas_glossary.custom_attributes, + pending_tasks=atlas_glossary.pending_tasks, + proxy=atlas_glossary.proxy, + is_incomplete=atlas_glossary.is_incomplete, + provenance_type=atlas_glossary.provenance_type, + home_id=atlas_glossary.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _atlas_glossary_from_nested(nested: AtlasGlossaryNested) -> AtlasGlossary: + """Convert nested format to flat AtlasGlossary.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else AtlasGlossaryAttributes() + ) + + # Merge relationships from all three buckets + rel_fields: list[str] = ["terms", "categories"] + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + rel_fields, + AtlasGlossaryRelationshipAttributes, + ) + + # Get top-level field names (exclude attributes and relationship fields) + top_level_fields = { + f.name + for f in msgspec.structs.fields(AssetNested) + if f.name + not in ( + "attributes", + "relationship_attributes", + "append_relationship_attributes", + "remove_relationship_attributes", + ) + } + + # Get attribute field names + attr_field_names = {f.name for f in msgspec.structs.fields(AtlasGlossaryAttributes)} + + # Build kwargs: top-level fields + attribute fields + relationships + kwargs = {} + + # Add top-level fields from nested + for name in top_level_fields: + if hasattr(nested, name): + kwargs[name] = getattr(nested, name) + + # Add attribute fields from attrs + for name in attr_field_names: + if hasattr(attrs, name): + kwargs[name] = getattr(attrs, name) + + # Add merged relationships + kwargs.update(merged_rels) + + return AtlasGlossary(**kwargs) + + +def _atlas_glossary_to_nested_bytes( + atlas_glossary: AtlasGlossary, serde: Serde +) -> bytes: + """Convert flat AtlasGlossary to nested JSON bytes.""" + return serde.encode(_atlas_glossary_to_nested(atlas_glossary)) + + +def _atlas_glossary_from_nested_bytes(data: bytes, serde: Serde) -> AtlasGlossary: + """Convert nested JSON bytes to flat AtlasGlossary.""" + nested = serde.decode(data, AtlasGlossaryNested) + return _atlas_glossary_from_nested(nested) diff --git a/pyatlan_v9/model/assets/atlas_glossary_category.py b/pyatlan_v9/model/assets/atlas_glossary_category.py new file mode 100644 index 000000000..afdebfb4d --- /dev/null +++ b/pyatlan_v9/model/assets/atlas_glossary_category.py @@ -0,0 +1,471 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +AtlasGlossaryCategory asset model with flattened inheritance. + +This module provides: +- AtlasGlossaryCategory: Flat asset class (easy to use) +- AtlasGlossaryCategoryAttributes: Nested attributes struct (extends AssetAttributes) +- AtlasGlossaryCategoryNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Union + +from msgspec import UNSET, UnsetType + +from pyatlan_v9.model.conversion_utils import ( + build_attributes_kwargs, + build_flat_kwargs, + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .asset import Asset, AssetAttributes, AssetNested, AssetRelationshipAttributes +from .gtc_related import ( + RelatedAtlasGlossary, + RelatedAtlasGlossaryCategory, + RelatedAtlasGlossaryTerm, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class AtlasGlossaryCategory(Asset): + """ + Instance of a category in Atlan, an organizational construct for glossary terms. + """ + + ANCHOR: ClassVar[Any] = None + PARENT_CATEGORY: ClassVar[Any] = None + TERMS: ClassVar[Any] = None + CHILDREN_CATEGORIES: ClassVar[Any] = None + SHORT_DESCRIPTION: ClassVar[Any] = None + LONG_DESCRIPTION: ClassVar[Any] = None + ADDITIONAL_ATTRIBUTES: ClassVar[Any] = None + CATEGORY_TYPE: ClassVar[Any] = None + + # Override type_name with AtlasGlossaryCategory-specific default + type_name: Union[str, UnsetType] = "AtlasGlossaryCategory" + + short_description: Union[str, None, UnsetType] = UNSET + """Unused. Brief summary of the category. See 'description' and 'userDescription' instead.""" + + long_description: Union[str, None, UnsetType] = UNSET + """Unused. Detailed description of the category. See 'readme' instead.""" + + additional_attributes: Union[dict[str, str], None, UnsetType] = UNSET + """Unused. Arbitrary set of additional attributes associated with the category.""" + + category_type: Union[str, None, UnsetType] = UNSET + """""" + + terms: Union[list[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Terms organized within this category.""" + + anchor: Union[RelatedAtlasGlossary, None, UnsetType] = None + """Glossary in which this category is contained.""" + + children_categories: Union[list[RelatedAtlasGlossaryCategory], None, UnsetType] = ( + UNSET + ) + """Child categories organized within this category.""" + + parent_category: Union[RelatedAtlasGlossaryCategory, None, UnsetType] = UNSET + """Parent category in which this category is located (or empty if this is a root-level category).""" + + @classmethod + def can_be_archived(cls) -> bool: + return False + + # ========================================================================= + # Convenience Methods + # ========================================================================= + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + anchor: "Asset | None" = None, + glossary_qualified_name: str | None = None, + glossary_guid: str | None = None, + parent_category: "AtlasGlossaryCategory | None" = None, + ) -> "AtlasGlossaryCategory": + """ + Create a new AtlasGlossaryCategory asset. + + Args: + name: Simple name of the category + anchor: Glossary object in which this category is contained (mutually exclusive with glossary_qualified_name and glossary_guid) + glossary_qualified_name: Qualified name of the glossary (mutually exclusive with anchor and glossary_guid) + glossary_guid: GUID of the glossary (mutually exclusive with anchor and glossary_qualified_name) + parent_category: Optional parent category for this category + + Returns: + New AtlasGlossaryCategory instance + + Raises: + ValueError: If required parameters are missing or if multiple glossary identifiers are provided + """ + validate_required_fields(["name"], [name]) + + provided_params = [ + p for p in [anchor, glossary_qualified_name, glossary_guid] if p is not None + ] + if len(provided_params) == 0: + raise ValueError( + "One of the following parameters are required: anchor, glossary_qualified_name, glossary_guid" + ) + if len(provided_params) > 1: + param_names = [] + if anchor is not None: + param_names.append("anchor") + if glossary_qualified_name is not None: + param_names.append("glossary_qualified_name") + if glossary_guid is not None: + param_names.append("glossary_guid") + raise ValueError( + f"Only one of the following parameters are allowed: {', '.join(param_names)}" + ) + + import uuid + + qualified_name = f"{name}@{uuid.uuid4()}" + + from msgspec import UNSET as MSGSPEC_UNSET + + if anchor is not None: + if hasattr(anchor, "trim_to_reference") and callable( + anchor.trim_to_reference + ): + anchor_ref = anchor.trim_to_reference() + else: + anchor_ref = RelatedAtlasGlossary( + guid=anchor.guid + if hasattr(anchor, "guid") and anchor.guid is not MSGSPEC_UNSET + else None, + qualified_name=anchor.qualified_name + if hasattr(anchor, "qualified_name") + and anchor.qualified_name is not MSGSPEC_UNSET + else None, + ) + elif glossary_qualified_name is not None: + anchor_ref = RelatedAtlasGlossary(qualified_name=glossary_qualified_name) + else: # glossary_guid is not None + anchor_ref = RelatedAtlasGlossary(guid=glossary_guid) + + parent_ref = None + if parent_category is not None: + if hasattr(parent_category, "trim_to_reference") and callable( + parent_category.trim_to_reference + ): + parent_ref = parent_category.trim_to_reference() + else: + parent_ref = RelatedAtlasGlossaryCategory( + guid=parent_category.guid + if hasattr(parent_category, "guid") + and parent_category.guid is not MSGSPEC_UNSET + else None, + qualified_name=parent_category.qualified_name + if hasattr(parent_category, "qualified_name") + and parent_category.qualified_name is not MSGSPEC_UNSET + else None, + ) + + kwargs: dict = dict( + name=name, + qualified_name=qualified_name, + anchor=anchor_ref, + ) + if parent_ref is not None: + kwargs["parent_category"] = parent_ref + return cls(**kwargs) + + @classmethod + def updater( + cls, *, qualified_name: str, name: str, glossary_guid: str + ) -> "AtlasGlossaryCategory": + """ + Create an AtlasGlossaryCategory instance for updating an existing category. + + Args: + qualified_name: Unique name of the category to update + name: Simple name of the category + glossary_guid: GUID of the glossary containing this category + + Returns: + AtlasGlossaryCategory instance configured for updates + + Raises: + ValueError: If required parameters are missing + """ + validate_required_fields( + ["qualified_name", "name", "glossary_guid"], + [qualified_name, name, glossary_guid], + ) + return cls( + qualified_name=qualified_name, + name=name, + anchor=RelatedAtlasGlossary(guid=glossary_guid), + ) + + def trim_to_required(self) -> "AtlasGlossaryCategory": + """ + Return an AtlasGlossaryCategory with only required fields for reference. + + Returns: + AtlasGlossaryCategory instance with only required fields set + + Raises: + ValueError: If anchor or anchor.guid is not available + """ + if self.anchor is None or self.anchor is UNSET: + raise ValueError("anchor.guid must be available") + if ( + not hasattr(self.anchor, "guid") + or self.anchor.guid is None + or self.anchor.guid is UNSET + ): + raise ValueError("anchor.guid must be available") + + return AtlasGlossaryCategory( + qualified_name=self.qualified_name, + name=self.name, + anchor=RelatedAtlasGlossary(guid=self.anchor.guid), + ) + + # Backward compatibility aliases + @classmethod + def create(cls, **kwargs) -> "AtlasGlossaryCategory": + """Backward compatibility alias for creator().""" + return cls.creator(**kwargs) + + @classmethod + def create_for_modification(cls, **kwargs) -> "AtlasGlossaryCategory": + """Backward compatibility alias for updater().""" + return cls.updater(**kwargs) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return _atlas_glossary_category_to_nested_bytes(self, serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + @staticmethod + def from_json( + json_data: Union[str, bytes], serde: Serde | None = None + ) -> "AtlasGlossaryCategory": + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + AtlasGlossaryCategory instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _atlas_glossary_category_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class AtlasGlossaryCategoryAttributes(AssetAttributes): + """AtlasGlossaryCategory-specific attributes for nested API format.""" + + short_description: Union[str, None, UnsetType] = UNSET + """Unused. Brief summary of the category. See 'description' and 'userDescription' instead.""" + + long_description: Union[str, None, UnsetType] = UNSET + """Unused. Detailed description of the category. See 'readme' instead.""" + + additional_attributes: Union[dict[str, str], None, UnsetType] = UNSET + """Unused. Arbitrary set of additional attributes associated with the category.""" + + category_type: Union[str, None, UnsetType] = UNSET + """""" + + anchor: Union[RelatedAtlasGlossary, None, UnsetType] = None + """Glossary in which this category is contained.""" + + parent_category: Union[RelatedAtlasGlossaryCategory, None, UnsetType] = UNSET + """Parent category in which this category is located (or empty if this is a root-level category).""" + + +class AtlasGlossaryCategoryRelationshipAttributes(AssetRelationshipAttributes): + """AtlasGlossaryCategory-specific relationship attributes for nested API format.""" + + terms: Union[list[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Terms organized within this category.""" + + children_categories: Union[list[RelatedAtlasGlossaryCategory], None, UnsetType] = ( + UNSET + ) + """Child categories organized within this category.""" + + +class AtlasGlossaryCategoryNested(AssetNested): + """AtlasGlossaryCategory in nested API format for high-performance serialization.""" + + attributes: Union[AtlasGlossaryCategoryAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + AtlasGlossaryCategoryRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + AtlasGlossaryCategoryRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + AtlasGlossaryCategoryRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _atlas_glossary_category_to_nested( + atlas_glossary_category: AtlasGlossaryCategory, +) -> AtlasGlossaryCategoryNested: + """Convert flat AtlasGlossaryCategory to nested format using dynamic field extraction.""" + # Build attributes using dynamic field extraction + attrs_kwargs = build_attributes_kwargs( + atlas_glossary_category, AtlasGlossaryCategoryAttributes + ) + attrs = AtlasGlossaryCategoryAttributes(**attrs_kwargs) + + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + rel_fields: list[str] = [ + "terms", + "children_categories", + ] + replace_rels, append_rels, remove_rels = categorize_relationships( + atlas_glossary_category, rel_fields, AtlasGlossaryCategoryRelationshipAttributes + ) + + return AtlasGlossaryCategoryNested( + guid=atlas_glossary_category.guid, + type_name=atlas_glossary_category.type_name, + status=atlas_glossary_category.status, + version=atlas_glossary_category.version, + create_time=atlas_glossary_category.create_time, + update_time=atlas_glossary_category.update_time, + created_by=atlas_glossary_category.created_by, + updated_by=atlas_glossary_category.updated_by, + classifications=atlas_glossary_category.classifications, + classification_names=atlas_glossary_category.classification_names, + meanings=atlas_glossary_category.meanings, + labels=atlas_glossary_category.labels, + business_attributes=atlas_glossary_category.business_attributes, + custom_attributes=atlas_glossary_category.custom_attributes, + pending_tasks=atlas_glossary_category.pending_tasks, + proxy=atlas_glossary_category.proxy, + is_incomplete=atlas_glossary_category.is_incomplete, + provenance_type=atlas_glossary_category.provenance_type, + home_id=atlas_glossary_category.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _atlas_glossary_category_from_nested( + nested: AtlasGlossaryCategoryNested, +) -> AtlasGlossaryCategory: + """Convert nested format to flat AtlasGlossaryCategory using dynamic field extraction.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else AtlasGlossaryCategoryAttributes() + ) + + # Merge relationships from all three buckets + rel_fields: list[str] = [ + "terms", + "children_categories", + ] + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + rel_fields, + AtlasGlossaryCategoryRelationshipAttributes, + ) + + # Build flat kwargs using dynamic field extraction + kwargs = build_flat_kwargs( + nested, attrs, merged_rels, AssetNested, AtlasGlossaryCategoryAttributes + ) + + return AtlasGlossaryCategory(**kwargs) + + +def _atlas_glossary_category_to_nested_bytes( + atlas_glossary_category: AtlasGlossaryCategory, serde: Serde +) -> bytes: + """Convert flat AtlasGlossaryCategory to nested JSON bytes.""" + return serde.encode(_atlas_glossary_category_to_nested(atlas_glossary_category)) + + +def _atlas_glossary_category_from_nested_bytes( + data: bytes, serde: Serde +) -> AtlasGlossaryCategory: + """Convert nested JSON bytes to flat AtlasGlossaryCategory.""" + nested = serde.decode(data, AtlasGlossaryCategoryNested) + return _atlas_glossary_category_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import KeywordField, RelationField, TextField + +AtlasGlossaryCategory.ANCHOR = KeywordField("anchor", "__glossary") +AtlasGlossaryCategory.PARENT_CATEGORY = KeywordField( + "parentCategory", "__parentCategory" +) +AtlasGlossaryCategory.TERMS = RelationField("terms") +AtlasGlossaryCategory.CHILDREN_CATEGORIES = RelationField("childrenCategories") +AtlasGlossaryCategory.SHORT_DESCRIPTION = TextField( + "shortDescription", "shortDescription" +) +AtlasGlossaryCategory.LONG_DESCRIPTION = TextField("longDescription", "longDescription") +AtlasGlossaryCategory.ADDITIONAL_ATTRIBUTES = KeywordField( + "additionalAttributes", "additionalAttributes" +) +AtlasGlossaryCategory.CATEGORY_TYPE = KeywordField("categoryType", "categoryType") diff --git a/pyatlan_v9/model/assets/atlas_glossary_term.py b/pyatlan_v9/model/assets/atlas_glossary_term.py new file mode 100644 index 000000000..b1d861476 --- /dev/null +++ b/pyatlan_v9/model/assets/atlas_glossary_term.py @@ -0,0 +1,597 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +AtlasGlossaryTerm asset model with flattened inheritance. + +This module provides: +- AtlasGlossaryTerm: Flat asset class (easy to use) +- AtlasGlossaryTermAttributes: Nested attributes struct (extends AssetAttributes) +- AtlasGlossaryTermNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Union + +from msgspec import UNSET, UnsetType + +from pyatlan_v9.model.conversion_utils import ( + build_attributes_kwargs, + build_flat_kwargs, + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .asset import Asset, AssetAttributes, AssetNested, AssetRelationshipAttributes +from .gtc_related import ( + RelatedAtlasGlossary, + RelatedAtlasGlossaryCategory, + RelatedAtlasGlossaryTerm, +) +from .referenceable_related import RelatedReferenceable + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class AtlasGlossaryTerm(Asset): + """ + Instance of a term in Atlan. Terms define concepts in natural language that can be associated with other assets to provide meaning. + """ + + ANCHOR: ClassVar[Any] = None + CATEGORIES: ClassVar[Any] = None + SHORT_DESCRIPTION: ClassVar[Any] = None + LONG_DESCRIPTION: ClassVar[Any] = None + EXAMPLES: ClassVar[Any] = None + ABBREVIATION: ClassVar[Any] = None + USAGE: ClassVar[Any] = None + ADDITIONAL_ATTRIBUTES: ClassVar[Any] = None + TERM_TYPE: ClassVar[Any] = None + VALID_VALUES_FOR: ClassVar[Any] = None + VALID_VALUES: ClassVar[Any] = None + SEE_ALSO: ClassVar[Any] = None + IS_A: ClassVar[Any] = None + ANTONYMS: ClassVar[Any] = None + ASSIGNED_ENTITIES: ClassVar[Any] = None + CLASSIFIES: ClassVar[Any] = None + PREFERRED_TO_TERMS: ClassVar[Any] = None + PREFERRED_TERMS: ClassVar[Any] = None + TRANSLATION_TERMS: ClassVar[Any] = None + SYNONYMS: ClassVar[Any] = None + REPLACED_BY: ClassVar[Any] = None + REPLACEMENT_TERMS: ClassVar[Any] = None + TRANSLATED_TERMS: ClassVar[Any] = None + + # Override type_name with AtlasGlossaryTerm-specific default + type_name: Union[str, UnsetType] = "AtlasGlossaryTerm" + + short_description: Union[str, None, UnsetType] = UNSET + """Unused. Brief summary of the term. See 'description' and 'userDescription' instead.""" + + long_description: Union[str, None, UnsetType] = UNSET + """Unused. Detailed definition of the term. See 'readme' instead.""" + + examples: Union[list[str], None, UnsetType] = UNSET + """Unused. Exmaples of the term.""" + + abbreviation: Union[str, None, UnsetType] = UNSET + """Unused. Abbreviation of the term.""" + + usage: Union[str, None, UnsetType] = UNSET + """Unused. Intended usage for the term.""" + + additional_attributes: Union[dict[str, str], None, UnsetType] = UNSET + """Unused. Arbitrary set of additional attributes for the terrm.""" + + term_type: Union[str, None, UnsetType] = UNSET + """""" + + assigned_entities: Union[list[RelatedReferenceable], None, UnsetType] = UNSET + """Assets assigned this term.""" + + anchor: Union[RelatedAtlasGlossary, None, UnsetType] = None + """Glossary in which this term is contained.""" + + categories: Union[list[RelatedAtlasGlossaryCategory], None, UnsetType] = UNSET + """Categories within which this term is organized.""" + + see_also: Union[list[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Linked terms that may also be of interest.""" + + synonyms: Union[list[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Terms that have the same (or a very similar) meaning, in the same language.""" + + antonyms: Union[list[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Terms that have the opposite (or near opposite) meaning, in the same language.""" + + preferred_terms: Union[list[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Preferred term(s) to use instead of this term.""" + + preferred_to_terms: Union[list[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Other term(s) that are less common or less preferred than this term.""" + + replaced_by: Union[list[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Term(s) that must no longer be used.""" + + replacement_terms: Union[list[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Term(s) that must be used instead.""" + + translated_terms: Union[list[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Term(s) that are a translation of this term.""" + + translation_terms: Union[list[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Term(s) for which this term is a translation.""" + + classifies: Union[list[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """More general term that defines a group of terms, for example: 'animal'.""" + + is_a: Union[list[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """More specific term that is a sub-class of another term, for example: 'cat'.""" + + valid_values_for: Union[list[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Term for which this is a valid value.""" + + valid_values: Union[list[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Valid values for this term.""" + + # ========================================================================= + # Convenience Methods + # ========================================================================= + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + anchor: "Asset | None" = None, + glossary_qualified_name: str | None = None, + glossary_guid: str | None = None, + categories: list["RelatedAtlasGlossaryCategory"] | None = None, + ) -> "AtlasGlossaryTerm": + """ + Create a new AtlasGlossaryTerm asset. + + Args: + name: Simple name of the term + anchor: Glossary object in which this term is contained (mutually exclusive with glossary_qualified_name and glossary_guid) + glossary_qualified_name: Qualified name of the glossary (mutually exclusive with anchor and glossary_guid) + glossary_guid: GUID of the glossary (mutually exclusive with anchor and glossary_qualified_name) + categories: Optional list of categories to which this term belongs + + Returns: + New AtlasGlossaryTerm instance + + Raises: + ValueError: If required parameters are missing or if multiple glossary identifiers are provided + """ + validate_required_fields(["name"], [name]) + + # Validate exactly one glossary identifier is provided + provided_params = [ + p for p in [anchor, glossary_qualified_name, glossary_guid] if p is not None + ] + if len(provided_params) == 0: + raise ValueError( + "One of the following parameters are required: anchor, glossary_qualified_name, glossary_guid" + ) + if len(provided_params) > 1: + param_names = [] + if anchor is not None: + param_names.append("anchor") + if glossary_qualified_name is not None: + param_names.append("glossary_qualified_name") + if glossary_guid is not None: + param_names.append("glossary_guid") + raise ValueError( + f"Only one of the following parameters are allowed: {', '.join(param_names)}" + ) + + # Generate qualified name + import uuid + + qualified_name = f"{name}@{uuid.uuid4()}" + + # Create anchor reference based on which parameter was provided + if anchor is not None: + # Use provided anchor object + from msgspec import UNSET as MSGSPEC_UNSET + + if hasattr(anchor, "trim_to_reference") and callable( + anchor.trim_to_reference + ): + anchor_ref = anchor.trim_to_reference() + else: + # Fallback: create RelatedAtlasGlossary from anchor attributes + anchor_ref = RelatedAtlasGlossary( + guid=anchor.guid + if hasattr(anchor, "guid") and anchor.guid is not MSGSPEC_UNSET + else None, + qualified_name=anchor.qualified_name + if hasattr(anchor, "qualified_name") + and anchor.qualified_name is not MSGSPEC_UNSET + else None, + ) + elif glossary_qualified_name is not None: + anchor_ref = RelatedAtlasGlossary(qualified_name=glossary_qualified_name) + else: # glossary_guid is not None + anchor_ref = RelatedAtlasGlossary(guid=glossary_guid) + + return cls( + name=name, + qualified_name=qualified_name, + anchor=anchor_ref, + categories=categories, + ) + + @classmethod + def updater( + cls, *, qualified_name: str, name: str, glossary_guid: str + ) -> "AtlasGlossaryTerm": + """ + Create an AtlasGlossaryTerm instance for updating an existing term. + + Args: + qualified_name: Unique name of the term to update + name: Simple name of the term + glossary_guid: GUID of the glossary containing this term + + Returns: + AtlasGlossaryTerm instance configured for updates + + Raises: + ValueError: If required parameters are missing + """ + validate_required_fields( + ["qualified_name", "name", "glossary_guid"], + [qualified_name, name, glossary_guid], + ) + return cls( + qualified_name=qualified_name, + name=name, + anchor=RelatedAtlasGlossary(guid=glossary_guid), + ) + + def trim_to_required(self) -> "AtlasGlossaryTerm": + """ + Return an AtlasGlossaryTerm with only required fields for reference. + + Returns: + AtlasGlossaryTerm instance with only required fields set + + Raises: + ValueError: If anchor or anchor.guid is not available + """ + if self.anchor is None or self.anchor is UNSET: + raise ValueError("anchor.guid must be available") + if ( + not hasattr(self.anchor, "guid") + or self.anchor.guid is None + or self.anchor.guid is UNSET + ): + raise ValueError("anchor.guid must be available") + + return AtlasGlossaryTerm( + qualified_name=self.qualified_name, + name=self.name, + anchor=RelatedAtlasGlossary(guid=self.anchor.guid), + ) + + # Backward compatibility aliases + @classmethod + def create(cls, **kwargs) -> "AtlasGlossaryTerm": + """Backward compatibility alias for creator().""" + return cls.creator(**kwargs) + + @classmethod + def create_for_modification(cls, **kwargs) -> "AtlasGlossaryTerm": + """Backward compatibility alias for updater().""" + return cls.updater(**kwargs) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return _atlas_glossary_term_to_nested_bytes(self, serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + @staticmethod + def from_json( + json_data: Union[str, bytes], serde: Serde | None = None + ) -> "AtlasGlossaryTerm": + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + AtlasGlossaryTerm instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _atlas_glossary_term_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class AtlasGlossaryTermAttributes(AssetAttributes): + """AtlasGlossaryTerm-specific attributes for nested API format.""" + + short_description: Union[str, None, UnsetType] = UNSET + """Unused. Brief summary of the term. See 'description' and 'userDescription' instead.""" + + long_description: Union[str, None, UnsetType] = UNSET + """Unused. Detailed definition of the term. See 'readme' instead.""" + + examples: Union[list[str], None, UnsetType] = UNSET + """Unused. Exmaples of the term.""" + + abbreviation: Union[str, None, UnsetType] = UNSET + """Unused. Abbreviation of the term.""" + + usage: Union[str, None, UnsetType] = UNSET + """Unused. Intended usage for the term.""" + + additional_attributes: Union[dict[str, str], None, UnsetType] = UNSET + """Unused. Arbitrary set of additional attributes for the terrm.""" + + term_type: Union[str, None, UnsetType] = UNSET + """""" + + anchor: Union[RelatedAtlasGlossary, None, UnsetType] = None + """Glossary in which this term is contained.""" + + +class AtlasGlossaryTermRelationshipAttributes(AssetRelationshipAttributes): + """AtlasGlossaryTerm-specific relationship attributes for nested API format.""" + + assigned_entities: Union[list[RelatedReferenceable], None, UnsetType] = UNSET + """Assets assigned this term.""" + + categories: Union[list[RelatedAtlasGlossaryCategory], None, UnsetType] = UNSET + """Categories within which this term is organized.""" + + see_also: Union[list[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Linked terms that may also be of interest.""" + + synonyms: Union[list[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Terms that have the same (or a very similar) meaning, in the same language.""" + + antonyms: Union[list[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Terms that have the opposite (or near opposite) meaning, in the same language.""" + + preferred_terms: Union[list[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Preferred term(s) to use instead of this term.""" + + preferred_to_terms: Union[list[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Other term(s) that are less common or less preferred than this term.""" + + replaced_by: Union[list[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Term(s) that must no longer be used.""" + + replacement_terms: Union[list[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Term(s) that must be used instead.""" + + translated_terms: Union[list[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Term(s) that are a translation of this term.""" + + translation_terms: Union[list[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Term(s) for which this term is a translation.""" + + classifies: Union[list[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """More general term that defines a group of terms, for example: 'animal'.""" + + is_a: Union[list[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """More specific term that is a sub-class of another term, for example: 'cat'.""" + + valid_values_for: Union[list[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Term for which this is a valid value.""" + + valid_values: Union[list[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Valid values for this term.""" + + +class AtlasGlossaryTermNested(AssetNested): + """AtlasGlossaryTerm in nested API format for high-performance serialization.""" + + attributes: Union[AtlasGlossaryTermAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + AtlasGlossaryTermRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + AtlasGlossaryTermRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + AtlasGlossaryTermRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _atlas_glossary_term_to_nested( + atlas_glossary_term: AtlasGlossaryTerm, +) -> AtlasGlossaryTermNested: + """Convert flat AtlasGlossaryTerm to nested format using dynamic field extraction.""" + # Build attributes using dynamic field extraction + attrs_kwargs = build_attributes_kwargs( + atlas_glossary_term, AtlasGlossaryTermAttributes + ) + attrs = AtlasGlossaryTermAttributes(**attrs_kwargs) + + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + rel_fields: list[str] = [ + "assigned_entities", + "categories", + "see_also", + "synonyms", + "antonyms", + "preferred_terms", + "preferred_to_terms", + "replaced_by", + "replacement_terms", + "translated_terms", + "translation_terms", + "classifies", + "is_a", + "valid_values_for", + "valid_values", + # Inherited from Referenceable + "user_def_relationship_to", + "user_def_relationship_from", + ] + replace_rels, append_rels, remove_rels = categorize_relationships( + atlas_glossary_term, rel_fields, AtlasGlossaryTermRelationshipAttributes + ) + + return AtlasGlossaryTermNested( + guid=atlas_glossary_term.guid, + type_name=atlas_glossary_term.type_name, + status=atlas_glossary_term.status, + version=atlas_glossary_term.version, + create_time=atlas_glossary_term.create_time, + update_time=atlas_glossary_term.update_time, + created_by=atlas_glossary_term.created_by, + updated_by=atlas_glossary_term.updated_by, + classifications=atlas_glossary_term.classifications, + classification_names=atlas_glossary_term.classification_names, + meanings=atlas_glossary_term.meanings, + labels=atlas_glossary_term.labels, + business_attributes=atlas_glossary_term.business_attributes, + custom_attributes=atlas_glossary_term.custom_attributes, + pending_tasks=atlas_glossary_term.pending_tasks, + proxy=atlas_glossary_term.proxy, + is_incomplete=atlas_glossary_term.is_incomplete, + provenance_type=atlas_glossary_term.provenance_type, + home_id=atlas_glossary_term.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _atlas_glossary_term_from_nested( + nested: AtlasGlossaryTermNested, +) -> AtlasGlossaryTerm: + """Convert nested format to flat AtlasGlossaryTerm using dynamic field extraction.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else AtlasGlossaryTermAttributes() + ) + + # Merge relationships from all three buckets + rel_fields: list[str] = [ + "assigned_entities", + "categories", + "see_also", + "synonyms", + "antonyms", + "preferred_terms", + "preferred_to_terms", + "replaced_by", + "replacement_terms", + "translated_terms", + "translation_terms", + "classifies", + "is_a", + "valid_values_for", + "valid_values", + # Inherited from Referenceable + "user_def_relationship_to", + "user_def_relationship_from", + ] + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + rel_fields, + AtlasGlossaryTermRelationshipAttributes, + ) + + # Build flat kwargs using dynamic field extraction + kwargs = build_flat_kwargs( + nested, attrs, merged_rels, AssetNested, AtlasGlossaryTermAttributes + ) + + return AtlasGlossaryTerm(**kwargs) + + +def _atlas_glossary_term_to_nested_bytes( + atlas_glossary_term: AtlasGlossaryTerm, serde: Serde +) -> bytes: + """Convert flat AtlasGlossaryTerm to nested JSON bytes.""" + return serde.encode(_atlas_glossary_term_to_nested(atlas_glossary_term)) + + +def _atlas_glossary_term_from_nested_bytes( + data: bytes, serde: Serde +) -> AtlasGlossaryTerm: + """Convert nested JSON bytes to flat AtlasGlossaryTerm.""" + nested = serde.decode(data, AtlasGlossaryTermNested) + return _atlas_glossary_term_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import KeywordField, RelationField, TextField + +AtlasGlossaryTerm.ANCHOR = KeywordField("anchor", "__glossary") +AtlasGlossaryTerm.CATEGORIES = KeywordField("categories", "__categories") +AtlasGlossaryTerm.SHORT_DESCRIPTION = TextField("shortDescription", "shortDescription") +AtlasGlossaryTerm.LONG_DESCRIPTION = TextField("longDescription", "longDescription") +AtlasGlossaryTerm.EXAMPLES = TextField("examples", "examples") +AtlasGlossaryTerm.ABBREVIATION = TextField("abbreviation", "abbreviation") +AtlasGlossaryTerm.USAGE = TextField("usage", "usage") +AtlasGlossaryTerm.ADDITIONAL_ATTRIBUTES = KeywordField( + "additionalAttributes", "additionalAttributes" +) +AtlasGlossaryTerm.TERM_TYPE = KeywordField("termType", "termType") +AtlasGlossaryTerm.VALID_VALUES_FOR = RelationField("validValuesFor") +AtlasGlossaryTerm.VALID_VALUES = RelationField("validValues") +AtlasGlossaryTerm.SEE_ALSO = RelationField("seeAlso") +AtlasGlossaryTerm.IS_A = RelationField("isA") +AtlasGlossaryTerm.ANTONYMS = RelationField("antonyms") +AtlasGlossaryTerm.ASSIGNED_ENTITIES = RelationField("assignedEntities") +AtlasGlossaryTerm.CLASSIFIES = RelationField("classifies") +AtlasGlossaryTerm.PREFERRED_TO_TERMS = RelationField("preferredToTerms") +AtlasGlossaryTerm.PREFERRED_TERMS = RelationField("preferredTerms") +AtlasGlossaryTerm.TRANSLATION_TERMS = RelationField("translationTerms") +AtlasGlossaryTerm.SYNONYMS = RelationField("synonyms") +AtlasGlossaryTerm.REPLACED_BY = RelationField("replacedBy") +AtlasGlossaryTerm.REPLACEMENT_TERMS = RelationField("replacementTerms") +AtlasGlossaryTerm.TRANSLATED_TERMS = RelationField("translatedTerms") diff --git a/pyatlan_v9/model/assets/auth_policy.py b/pyatlan_v9/model/assets/auth_policy.py new file mode 100644 index 000000000..4ea969f63 --- /dev/null +++ b/pyatlan_v9/model/assets/auth_policy.py @@ -0,0 +1,217 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2026 Atlan Pte. Ltd. + +"""AuthPolicy asset model for pyatlan_v9.""" + +from __future__ import annotations + +from typing import Any, ClassVar, Set, Union + +from msgspec import UNSET, UnsetType + +from pyatlan_v9.model.conversion_utils import ( + build_attributes_kwargs, + build_flat_kwargs, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .asset import Asset, AssetAttributes, AssetNested + + +@register_asset +class AuthPolicy(Asset): + """AuthPolicy asset — defines access policies for Persona and Purpose.""" + + POLICY_FILTER_CRITERIA: ClassVar[Any] = None + POLICY_TYPE: ClassVar[Any] = None + POLICY_SERVICE_NAME: ClassVar[Any] = None + POLICY_CATEGORY: ClassVar[Any] = None + POLICY_SUB_CATEGORY: ClassVar[Any] = None + POLICY_USERS: ClassVar[Any] = None + POLICY_GROUPS: ClassVar[Any] = None + POLICY_ROLES: ClassVar[Any] = None + POLICY_ACTIONS: ClassVar[Any] = None + POLICY_RESOURCES: ClassVar[Any] = None + POLICY_RESOURCE_CATEGORY: ClassVar[Any] = None + POLICY_PRIORITY: ClassVar[Any] = None + IS_POLICY_ENABLED: ClassVar[Any] = None + POLICY_MASK_TYPE: ClassVar[Any] = None + POLICY_VALIDITY_SCHEDULE: ClassVar[Any] = None + POLICY_RESOURCE_SIGNATURE: ClassVar[Any] = None + POLICY_DELEGATE_ADMIN: ClassVar[Any] = None + POLICY_CONDITIONS: ClassVar[Any] = None + ACCESS_CONTROL: ClassVar[Any] = None + + @classmethod + @init_guid + def _create(cls, *, name: str) -> "AuthPolicy": + validate_required_fields(["name"], [name]) + return cls(qualified_name=name, name=name, display_name="") + + type_name: Union[str, UnsetType] = "AuthPolicy" + policy_filter_criteria: Union[str, None, UnsetType] = UNSET + policy_type: Union[str, None, UnsetType] = UNSET + policy_service_name: Union[str, None, UnsetType] = UNSET + policy_category: Union[str, None, UnsetType] = UNSET + policy_sub_category: Union[str, None, UnsetType] = UNSET + policy_users: Union[Set[str], None, UnsetType] = UNSET + policy_groups: Union[Set[str], None, UnsetType] = UNSET + policy_roles: Union[Set[str], None, UnsetType] = UNSET + policy_actions: Union[Set[str], None, UnsetType] = UNSET + policy_resources: Union[Set[str], None, UnsetType] = UNSET + policy_resource_category: Union[str, None, UnsetType] = UNSET + policy_priority: Union[int, None, UnsetType] = UNSET + is_policy_enabled: Union[bool, None, UnsetType] = UNSET + policy_mask_type: Union[str, None, UnsetType] = UNSET + policy_validity_schedule: Union[list[Any], None, UnsetType] = UNSET + policy_resource_signature: Union[str, None, UnsetType] = UNSET + policy_delegate_admin: Union[bool, None, UnsetType] = UNSET + policy_conditions: Union[list[Any], None, UnsetType] = UNSET + access_control: Union[Any, None, UnsetType] = UNSET + connection_qualified_name: Union[str, None, UnsetType] = UNSET + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + if serde is None: + serde = get_serde() + if nested: + return _auth_policy_to_nested_bytes(self, serde).decode("utf-8") + return serde.encode(self).decode("utf-8") + + @staticmethod + def from_json( + json_data: Union[str, bytes], serde: Serde | None = None + ) -> "AuthPolicy": + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _auth_policy_from_nested_bytes(json_data, serde) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( + BooleanField, + KeywordField, + NumericField, + RelationField, + TextField, +) + +AuthPolicy.POLICY_FILTER_CRITERIA = TextField( + "policyFilterCriteria", "policyFilterCriteria" +) +AuthPolicy.POLICY_TYPE = KeywordField("policyType", "policyType") +AuthPolicy.POLICY_SERVICE_NAME = KeywordField("policyServiceName", "policyServiceName") +AuthPolicy.POLICY_CATEGORY = KeywordField("policyCategory", "policyCategory") +AuthPolicy.POLICY_SUB_CATEGORY = KeywordField("policySubCategory", "policySubCategory") +AuthPolicy.POLICY_USERS = KeywordField("policyUsers", "policyUsers") +AuthPolicy.POLICY_GROUPS = KeywordField("policyGroups", "policyGroups") +AuthPolicy.POLICY_ROLES = KeywordField("policyRoles", "policyRoles") +AuthPolicy.POLICY_ACTIONS = KeywordField("policyActions", "policyActions") +AuthPolicy.POLICY_RESOURCES = KeywordField("policyResources", "policyResources") +AuthPolicy.POLICY_RESOURCE_CATEGORY = KeywordField( + "policyResourceCategory", "policyResourceCategory" +) +AuthPolicy.POLICY_PRIORITY = NumericField("policyPriority", "policyPriority") +AuthPolicy.IS_POLICY_ENABLED = BooleanField("isPolicyEnabled", "isPolicyEnabled") +AuthPolicy.POLICY_MASK_TYPE = KeywordField("policyMaskType", "policyMaskType") +AuthPolicy.POLICY_VALIDITY_SCHEDULE = KeywordField( + "policyValiditySchedule", "policyValiditySchedule" +) +AuthPolicy.POLICY_RESOURCE_SIGNATURE = KeywordField( + "policyResourceSignature", "policyResourceSignature" +) +AuthPolicy.POLICY_DELEGATE_ADMIN = BooleanField( + "policyDelegateAdmin", "policyDelegateAdmin" +) +AuthPolicy.POLICY_CONDITIONS = KeywordField("policyConditions", "policyConditions") +AuthPolicy.ACCESS_CONTROL = RelationField("accessControl") + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class AuthPolicyAttributes(AssetAttributes): + policy_filter_criteria: Union[str, None, UnsetType] = UNSET + policy_type: Union[str, None, UnsetType] = UNSET + policy_service_name: Union[str, None, UnsetType] = UNSET + policy_category: Union[str, None, UnsetType] = UNSET + policy_sub_category: Union[str, None, UnsetType] = UNSET + policy_users: Union[Set[str], None, UnsetType] = UNSET + policy_groups: Union[Set[str], None, UnsetType] = UNSET + policy_roles: Union[Set[str], None, UnsetType] = UNSET + policy_actions: Union[Set[str], None, UnsetType] = UNSET + policy_resources: Union[Set[str], None, UnsetType] = UNSET + policy_resource_category: Union[str, None, UnsetType] = UNSET + policy_priority: Union[int, None, UnsetType] = UNSET + is_policy_enabled: Union[bool, None, UnsetType] = UNSET + policy_mask_type: Union[str, None, UnsetType] = UNSET + policy_validity_schedule: Union[list[Any], None, UnsetType] = UNSET + policy_resource_signature: Union[str, None, UnsetType] = UNSET + policy_delegate_admin: Union[bool, None, UnsetType] = UNSET + policy_conditions: Union[list[Any], None, UnsetType] = UNSET + connection_qualified_name: Union[str, None, UnsetType] = UNSET + + +class AuthPolicyNested(AssetNested): + attributes: Union[AuthPolicyAttributes, UnsetType] = UNSET + + +def _auth_policy_to_nested(ap: AuthPolicy) -> AuthPolicyNested: + attrs_kwargs = build_attributes_kwargs(ap, AuthPolicyAttributes) + attrs = AuthPolicyAttributes(**attrs_kwargs) + return AuthPolicyNested( + guid=ap.guid, + type_name=ap.type_name, + status=ap.status, + version=ap.version, + create_time=ap.create_time, + update_time=ap.update_time, + created_by=ap.created_by, + updated_by=ap.updated_by, + classifications=ap.classifications, + classification_names=ap.classification_names, + meanings=ap.meanings, + labels=ap.labels, + business_attributes=ap.business_attributes, + custom_attributes=ap.custom_attributes, + pending_tasks=ap.pending_tasks, + proxy=ap.proxy, + is_incomplete=ap.is_incomplete, + provenance_type=ap.provenance_type, + home_id=ap.home_id, + attributes=attrs, + ) + + +def _auth_policy_from_nested(nested: AuthPolicyNested) -> AuthPolicy: + attrs = ( + nested.attributes if nested.attributes is not UNSET else AuthPolicyAttributes() + ) + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + [], + object, + ) + kwargs = build_flat_kwargs( + nested, attrs, merged_rels, AssetNested, AuthPolicyAttributes + ) + return AuthPolicy(**kwargs) + + +def _auth_policy_to_nested_bytes(ap: AuthPolicy, serde: Serde) -> bytes: + return serde.encode(_auth_policy_to_nested(ap)) + + +def _auth_policy_from_nested_bytes(data: bytes, serde: Serde) -> AuthPolicy: + nested = serde.decode(data, AuthPolicyNested) + return _auth_policy_from_nested(nested) diff --git a/pyatlan_v9/model/assets/aws.py b/pyatlan_v9/model/assets/aws.py new file mode 100644 index 000000000..8e559e4a7 --- /dev/null +++ b/pyatlan_v9/model/assets/aws.py @@ -0,0 +1,527 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +AWS asset model with flattened inheritance. + +This module provides: +- AWS: Flat asset class (easy to use) +- AWSAttributes: Nested attributes struct (extends AssetAttributes) +- AWSNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class AWS(Asset): + """ + Base class for AWS assets. + """ + + AWS_ARN: ClassVar[Any] = None + AWS_PARTITION: ClassVar[Any] = None + AWS_SERVICE: ClassVar[Any] = None + AWS_REGION: ClassVar[Any] = None + AWS_ACCOUNT_ID: ClassVar[Any] = None + AWS_RESOURCE_ID: ClassVar[Any] = None + AWS_OWNER_NAME: ClassVar[Any] = None + AWS_OWNER_ID: ClassVar[Any] = None + AWS_TAGS: ClassVar[Any] = None + CLOUD_UNIFORM_RESOURCE_NAME: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "AWS" + + aws_arn: Union[str, None, UnsetType] = UNSET + """DEPRECATED: This legacy attribute must be unique across all AWS asset instances. This can create non-obvious edge cases for creating / updating assets, and we therefore recommended NOT using it. See and use cloudResourceName instead.""" + + aws_partition: Union[str, None, UnsetType] = UNSET + """Group of AWS region and service objects.""" + + aws_service: Union[str, None, UnsetType] = UNSET + """Type of service in which the asset exists.""" + + aws_region: Union[str, None, UnsetType] = UNSET + """Physical region where the data center in which the asset exists is clustered.""" + + aws_account_id: Union[str, None, UnsetType] = UNSET + """12-digit number that uniquely identifies an AWS account.""" + + aws_resource_id: Union[str, None, UnsetType] = UNSET + """Unique resource ID assigned when a new resource is created.""" + + aws_owner_name: Union[str, None, UnsetType] = UNSET + """Root user's name.""" + + aws_owner_id: Union[str, None, UnsetType] = UNSET + """Root user's ID.""" + + aws_tags: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of tags that have been applied to the asset in AWS.""" + + cloud_uniform_resource_name: Union[str, None, UnsetType] = UNSET + """Uniform resource name (URN) for the asset: AWS ARN, Google Cloud URI, Azure resource ID, Oracle OCID, and so on.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "AWS" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _aws_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> AWS: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + AWS instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _aws_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class AWSAttributes(AssetAttributes): + """AWS-specific attributes for nested API format.""" + + aws_arn: Union[str, None, UnsetType] = UNSET + """DEPRECATED: This legacy attribute must be unique across all AWS asset instances. This can create non-obvious edge cases for creating / updating assets, and we therefore recommended NOT using it. See and use cloudResourceName instead.""" + + aws_partition: Union[str, None, UnsetType] = UNSET + """Group of AWS region and service objects.""" + + aws_service: Union[str, None, UnsetType] = UNSET + """Type of service in which the asset exists.""" + + aws_region: Union[str, None, UnsetType] = UNSET + """Physical region where the data center in which the asset exists is clustered.""" + + aws_account_id: Union[str, None, UnsetType] = UNSET + """12-digit number that uniquely identifies an AWS account.""" + + aws_resource_id: Union[str, None, UnsetType] = UNSET + """Unique resource ID assigned when a new resource is created.""" + + aws_owner_name: Union[str, None, UnsetType] = UNSET + """Root user's name.""" + + aws_owner_id: Union[str, None, UnsetType] = UNSET + """Root user's ID.""" + + aws_tags: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of tags that have been applied to the asset in AWS.""" + + cloud_uniform_resource_name: Union[str, None, UnsetType] = UNSET + """Uniform resource name (URN) for the asset: AWS ARN, Google Cloud URI, Azure resource ID, Oracle OCID, and so on.""" + + +class AWSRelationshipAttributes(AssetRelationshipAttributes): + """AWS-specific relationship attributes for nested API format.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + +class AWSNested(AssetNested): + """AWS in nested API format for high-performance serialization.""" + + attributes: Union[AWSAttributes, UnsetType] = UNSET + relationship_attributes: Union[AWSRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[AWSRelationshipAttributes, UnsetType] = UNSET + remove_relationship_attributes: Union[AWSRelationshipAttributes, UnsetType] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_AWS_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", +] + + +def _populate_aws_attrs(attrs: AWSAttributes, obj: AWS) -> None: + """Populate AWS-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.aws_arn = obj.aws_arn + attrs.aws_partition = obj.aws_partition + attrs.aws_service = obj.aws_service + attrs.aws_region = obj.aws_region + attrs.aws_account_id = obj.aws_account_id + attrs.aws_resource_id = obj.aws_resource_id + attrs.aws_owner_name = obj.aws_owner_name + attrs.aws_owner_id = obj.aws_owner_id + attrs.aws_tags = obj.aws_tags + attrs.cloud_uniform_resource_name = obj.cloud_uniform_resource_name + + +def _extract_aws_attrs(attrs: AWSAttributes) -> dict: + """Extract all AWS attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["aws_arn"] = attrs.aws_arn + result["aws_partition"] = attrs.aws_partition + result["aws_service"] = attrs.aws_service + result["aws_region"] = attrs.aws_region + result["aws_account_id"] = attrs.aws_account_id + result["aws_resource_id"] = attrs.aws_resource_id + result["aws_owner_name"] = attrs.aws_owner_name + result["aws_owner_id"] = attrs.aws_owner_id + result["aws_tags"] = attrs.aws_tags + result["cloud_uniform_resource_name"] = attrs.cloud_uniform_resource_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _aws_to_nested(aws: AWS) -> AWSNested: + """Convert flat AWS to nested format.""" + attrs = AWSAttributes() + _populate_aws_attrs(attrs, aws) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + aws, _AWS_REL_FIELDS, AWSRelationshipAttributes + ) + return AWSNested( + guid=aws.guid, + type_name=aws.type_name, + status=aws.status, + version=aws.version, + create_time=aws.create_time, + update_time=aws.update_time, + created_by=aws.created_by, + updated_by=aws.updated_by, + classifications=aws.classifications, + classification_names=aws.classification_names, + meanings=aws.meanings, + labels=aws.labels, + business_attributes=aws.business_attributes, + custom_attributes=aws.custom_attributes, + pending_tasks=aws.pending_tasks, + proxy=aws.proxy, + is_incomplete=aws.is_incomplete, + provenance_type=aws.provenance_type, + home_id=aws.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _aws_from_nested(nested: AWSNested) -> AWS: + """Convert nested format to flat AWS.""" + attrs = nested.attributes if nested.attributes is not UNSET else AWSAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _AWS_REL_FIELDS, + AWSRelationshipAttributes, + ) + return AWS( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_aws_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _aws_to_nested_bytes(aws: AWS, serde: Serde) -> bytes: + """Convert flat AWS to nested JSON bytes.""" + return serde.encode(_aws_to_nested(aws)) + + +def _aws_from_nested_bytes(data: bytes, serde: Serde) -> AWS: + """Convert nested JSON bytes to flat AWS.""" + nested = serde.decode(data, AWSNested) + return _aws_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + RelationField, +) + +AWS.AWS_ARN = KeywordTextField("awsArn", "awsArn", "awsArn.text") +AWS.AWS_PARTITION = KeywordField("awsPartition", "awsPartition") +AWS.AWS_SERVICE = KeywordField("awsService", "awsService") +AWS.AWS_REGION = KeywordField("awsRegion", "awsRegion") +AWS.AWS_ACCOUNT_ID = KeywordField("awsAccountId", "awsAccountId") +AWS.AWS_RESOURCE_ID = KeywordField("awsResourceId", "awsResourceId") +AWS.AWS_OWNER_NAME = KeywordTextField( + "awsOwnerName", "awsOwnerName", "awsOwnerName.text" +) +AWS.AWS_OWNER_ID = KeywordField("awsOwnerId", "awsOwnerId") +AWS.AWS_TAGS = KeywordField("awsTags", "awsTags") +AWS.CLOUD_UNIFORM_RESOURCE_NAME = KeywordField( + "cloudUniformResourceName", "cloudUniformResourceName" +) +AWS.ANOMALO_CHECKS = RelationField("anomaloChecks") +AWS.APPLICATION = RelationField("application") +AWS.APPLICATION_FIELD = RelationField("applicationField") +AWS.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +AWS.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +AWS.METRICS = RelationField("metrics") +AWS.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +AWS.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +AWS.MEANINGS = RelationField("meanings") +AWS.MC_MONITORS = RelationField("mcMonitors") +AWS.MC_INCIDENTS = RelationField("mcIncidents") +AWS.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +AWS.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +AWS.FILES = RelationField("files") +AWS.LINKS = RelationField("links") +AWS.README = RelationField("readme") +AWS.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +AWS.SODA_CHECKS = RelationField("sodaChecks") diff --git a/pyatlan_v9/model/assets/azure.py b/pyatlan_v9/model/assets/azure.py new file mode 100644 index 000000000..8f2f44e88 --- /dev/null +++ b/pyatlan_v9/model/assets/azure.py @@ -0,0 +1,483 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Azure asset model with flattened inheritance. + +This module provides: +- Azure: Flat asset class (easy to use) +- AzureAttributes: Nested attributes struct (extends AssetAttributes) +- AzureNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Azure(Asset): + """ + Base class for Azure assets. + """ + + AZURE_RESOURCE_ID: ClassVar[Any] = None + AZURE_LOCATION: ClassVar[Any] = None + ADLS_ACCOUNT_SECONDARY_LOCATION: ClassVar[Any] = None + AZURE_TAGS: ClassVar[Any] = None + CLOUD_UNIFORM_RESOURCE_NAME: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Azure" + + azure_resource_id: Union[str, None, UnsetType] = UNSET + """Resource identifier of this asset in Azure.""" + + azure_location: Union[str, None, UnsetType] = UNSET + """Location of this asset in Azure.""" + + adls_account_secondary_location: Union[str, None, UnsetType] = UNSET + """Secondary location of the ADLS account.""" + + azure_tags: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """Tags that have been applied to this asset in Azure.""" + + cloud_uniform_resource_name: Union[str, None, UnsetType] = UNSET + """Uniform resource name (URN) for the asset: AWS ARN, Google Cloud URI, Azure resource ID, Oracle OCID, and so on.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Azure" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _azure_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Azure: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Azure instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _azure_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class AzureAttributes(AssetAttributes): + """Azure-specific attributes for nested API format.""" + + azure_resource_id: Union[str, None, UnsetType] = UNSET + """Resource identifier of this asset in Azure.""" + + azure_location: Union[str, None, UnsetType] = UNSET + """Location of this asset in Azure.""" + + adls_account_secondary_location: Union[str, None, UnsetType] = UNSET + """Secondary location of the ADLS account.""" + + azure_tags: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """Tags that have been applied to this asset in Azure.""" + + cloud_uniform_resource_name: Union[str, None, UnsetType] = UNSET + """Uniform resource name (URN) for the asset: AWS ARN, Google Cloud URI, Azure resource ID, Oracle OCID, and so on.""" + + +class AzureRelationshipAttributes(AssetRelationshipAttributes): + """Azure-specific relationship attributes for nested API format.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + +class AzureNested(AssetNested): + """Azure in nested API format for high-performance serialization.""" + + attributes: Union[AzureAttributes, UnsetType] = UNSET + relationship_attributes: Union[AzureRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[AzureRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[AzureRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_AZURE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", +] + + +def _populate_azure_attrs(attrs: AzureAttributes, obj: Azure) -> None: + """Populate Azure-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.azure_resource_id = obj.azure_resource_id + attrs.azure_location = obj.azure_location + attrs.adls_account_secondary_location = obj.adls_account_secondary_location + attrs.azure_tags = obj.azure_tags + attrs.cloud_uniform_resource_name = obj.cloud_uniform_resource_name + + +def _extract_azure_attrs(attrs: AzureAttributes) -> dict: + """Extract all Azure attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["azure_resource_id"] = attrs.azure_resource_id + result["azure_location"] = attrs.azure_location + result["adls_account_secondary_location"] = attrs.adls_account_secondary_location + result["azure_tags"] = attrs.azure_tags + result["cloud_uniform_resource_name"] = attrs.cloud_uniform_resource_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _azure_to_nested(azure: Azure) -> AzureNested: + """Convert flat Azure to nested format.""" + attrs = AzureAttributes() + _populate_azure_attrs(attrs, azure) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + azure, _AZURE_REL_FIELDS, AzureRelationshipAttributes + ) + return AzureNested( + guid=azure.guid, + type_name=azure.type_name, + status=azure.status, + version=azure.version, + create_time=azure.create_time, + update_time=azure.update_time, + created_by=azure.created_by, + updated_by=azure.updated_by, + classifications=azure.classifications, + classification_names=azure.classification_names, + meanings=azure.meanings, + labels=azure.labels, + business_attributes=azure.business_attributes, + custom_attributes=azure.custom_attributes, + pending_tasks=azure.pending_tasks, + proxy=azure.proxy, + is_incomplete=azure.is_incomplete, + provenance_type=azure.provenance_type, + home_id=azure.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _azure_from_nested(nested: AzureNested) -> Azure: + """Convert nested format to flat Azure.""" + attrs = nested.attributes if nested.attributes is not UNSET else AzureAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _AZURE_REL_FIELDS, + AzureRelationshipAttributes, + ) + return Azure( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_azure_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _azure_to_nested_bytes(azure: Azure, serde: Serde) -> bytes: + """Convert flat Azure to nested JSON bytes.""" + return serde.encode(_azure_to_nested(azure)) + + +def _azure_from_nested_bytes(data: bytes, serde: Serde) -> Azure: + """Convert nested JSON bytes to flat Azure.""" + nested = serde.decode(data, AzureNested) + return _azure_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + RelationField, +) + +Azure.AZURE_RESOURCE_ID = KeywordTextField( + "azureResourceId", "azureResourceId", "azureResourceId.text" +) +Azure.AZURE_LOCATION = KeywordField("azureLocation", "azureLocation") +Azure.ADLS_ACCOUNT_SECONDARY_LOCATION = KeywordField( + "adlsAccountSecondaryLocation", "adlsAccountSecondaryLocation" +) +Azure.AZURE_TAGS = KeywordField("azureTags", "azureTags") +Azure.CLOUD_UNIFORM_RESOURCE_NAME = KeywordField( + "cloudUniformResourceName", "cloudUniformResourceName" +) +Azure.ANOMALO_CHECKS = RelationField("anomaloChecks") +Azure.APPLICATION = RelationField("application") +Azure.APPLICATION_FIELD = RelationField("applicationField") +Azure.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Azure.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Azure.METRICS = RelationField("metrics") +Azure.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Azure.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Azure.MEANINGS = RelationField("meanings") +Azure.MC_MONITORS = RelationField("mcMonitors") +Azure.MC_INCIDENTS = RelationField("mcIncidents") +Azure.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Azure.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Azure.FILES = RelationField("files") +Azure.LINKS = RelationField("links") +Azure.README = RelationField("readme") +Azure.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Azure.SODA_CHECKS = RelationField("sodaChecks") diff --git a/pyatlan_v9/model/assets/azure_event_consumer_group.py b/pyatlan_v9/model/assets/azure_event_consumer_group.py new file mode 100644 index 000000000..3fa7428b9 --- /dev/null +++ b/pyatlan_v9/model/assets/azure_event_consumer_group.py @@ -0,0 +1,54 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""AzureEventHubConsumerGroup asset model for pyatlan_v9.""" + +from __future__ import annotations + +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .kafka_consumer_group import KafkaConsumerGroup + + +@register_asset +class AzureEventHubConsumerGroup(KafkaConsumerGroup): + """Instance of an Azure Event Hub consumer group in Atlan.""" + + type_name: str = "AzureEventHubConsumerGroup" + + @classmethod + @init_guid + def creator( + cls, *, name: str, event_hub_qualified_names: list[str] + ) -> "AzureEventHubConsumerGroup": + """Create a new AzureEventHubConsumerGroup asset.""" + validate_required_fields( + ["name", "event_hub_qualified_names"], [name, event_hub_qualified_names] + ) + first_event_hub_qn = event_hub_qualified_names[0] + fields = first_event_hub_qn.split("/") + connector_name = fields[1] if len(fields) > 1 else None + connection_qualified_name = ( + "/".join(fields[:3]) if len(fields) >= 3 else first_event_hub_qn + ) + first_event_hub_name = fields[4] if len(fields) > 4 else fields[-1] + return cls( + name=name, + connector_name=connector_name, + connection_qualified_name=connection_qualified_name, + kafka_topic_qualified_names=set(event_hub_qualified_names), + qualified_name=f"{connection_qualified_name}/consumer-group/{first_event_hub_name}/{name}", + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "AzureEventHubConsumerGroup": + """Create an AzureEventHubConsumerGroup instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "AzureEventHubConsumerGroup": + """Return only fields required for update operations.""" + return AzureEventHubConsumerGroup.updater( + qualified_name=self.qualified_name, name=self.name + ) diff --git a/pyatlan_v9/model/assets/azure_event_hub.py b/pyatlan_v9/model/assets/azure_event_hub.py new file mode 100644 index 000000000..89f7c7c64 --- /dev/null +++ b/pyatlan_v9/model/assets/azure_event_hub.py @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Azure Event Hub asset model.""" + +from __future__ import annotations + +from typing import Union + +from msgspec import UNSET, UnsetType + +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .kafka_topic import KafkaTopic + + +@register_asset +class AzureEventHub(KafkaTopic): + """Instance of an Azure Event Hub topic in Atlan.""" + + type_name: Union[str, UnsetType] = "AzureEventHub" + azure_event_hub_status: Union[str, None, UnsetType] = UNSET + + @classmethod + @init_guid + def creator(cls, *, name: str, connection_qualified_name: str) -> "AzureEventHub": + """Create a new AzureEventHub asset.""" + validate_required_fields( + ["name", "connection_qualified_name"], [name, connection_qualified_name] + ) + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + return cls( + name=name, + qualified_name=f"{connection_qualified_name}/topic/{name}", + connection_qualified_name=connection_qualified_name, + connector_name=connector_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "AzureEventHub": + """Create an AzureEventHub instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "AzureEventHub": + """Return only fields required for update operations.""" + return AzureEventHub.updater(qualified_name=self.qualified_name, name=self.name) diff --git a/pyatlan_v9/model/assets/azure_service_bus.py b/pyatlan_v9/model/assets/azure_service_bus.py new file mode 100644 index 000000000..edd8a8b95 --- /dev/null +++ b/pyatlan_v9/model/assets/azure_service_bus.py @@ -0,0 +1,590 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +AzureServiceBus asset model with flattened inheritance. + +This module provides: +- AzureServiceBus: Flat asset class (easy to use) +- AzureServiceBusAttributes: Nested attributes struct (extends AssetAttributes) +- AzureServiceBusNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class AzureServiceBus(Asset): + """ + Base class for all AzureServiceBus types. + """ + + AZURE_SERVICE_BUS_NAMESPACE_QUALIFIED_NAME: ClassVar[Any] = None + AZURE_SERVICE_BUS_NAMESPACE_NAME: ClassVar[Any] = None + AZURE_SERVICE_BUS_SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "AzureServiceBus" + + azure_service_bus_namespace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AzureServiceBus Namespace in which this asset exists.""" + + azure_service_bus_namespace_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AzureServiceBus Namespace in which this asset exists.""" + + azure_service_bus_schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AzureServiceBus Schema in which this asset exists.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "AzureServiceBus" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _azure_service_bus_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> AzureServiceBus: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + AzureServiceBus instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _azure_service_bus_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class AzureServiceBusAttributes(AssetAttributes): + """AzureServiceBus-specific attributes for nested API format.""" + + azure_service_bus_namespace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AzureServiceBus Namespace in which this asset exists.""" + + azure_service_bus_namespace_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AzureServiceBus Namespace in which this asset exists.""" + + azure_service_bus_schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AzureServiceBus Schema in which this asset exists.""" + + +class AzureServiceBusRelationshipAttributes(AssetRelationshipAttributes): + """AzureServiceBus-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class AzureServiceBusNested(AssetNested): + """AzureServiceBus in nested API format for high-performance serialization.""" + + attributes: Union[AzureServiceBusAttributes, UnsetType] = UNSET + relationship_attributes: Union[AzureServiceBusRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + AzureServiceBusRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + AzureServiceBusRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_AZURE_SERVICE_BUS_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_azure_service_bus_attrs( + attrs: AzureServiceBusAttributes, obj: AzureServiceBus +) -> None: + """Populate AzureServiceBus-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.azure_service_bus_namespace_qualified_name = ( + obj.azure_service_bus_namespace_qualified_name + ) + attrs.azure_service_bus_namespace_name = obj.azure_service_bus_namespace_name + attrs.azure_service_bus_schema_qualified_name = ( + obj.azure_service_bus_schema_qualified_name + ) + + +def _extract_azure_service_bus_attrs(attrs: AzureServiceBusAttributes) -> dict: + """Extract all AzureServiceBus attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["azure_service_bus_namespace_qualified_name"] = ( + attrs.azure_service_bus_namespace_qualified_name + ) + result["azure_service_bus_namespace_name"] = attrs.azure_service_bus_namespace_name + result["azure_service_bus_schema_qualified_name"] = ( + attrs.azure_service_bus_schema_qualified_name + ) + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _azure_service_bus_to_nested( + azure_service_bus: AzureServiceBus, +) -> AzureServiceBusNested: + """Convert flat AzureServiceBus to nested format.""" + attrs = AzureServiceBusAttributes() + _populate_azure_service_bus_attrs(attrs, azure_service_bus) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + azure_service_bus, + _AZURE_SERVICE_BUS_REL_FIELDS, + AzureServiceBusRelationshipAttributes, + ) + return AzureServiceBusNested( + guid=azure_service_bus.guid, + type_name=azure_service_bus.type_name, + status=azure_service_bus.status, + version=azure_service_bus.version, + create_time=azure_service_bus.create_time, + update_time=azure_service_bus.update_time, + created_by=azure_service_bus.created_by, + updated_by=azure_service_bus.updated_by, + classifications=azure_service_bus.classifications, + classification_names=azure_service_bus.classification_names, + meanings=azure_service_bus.meanings, + labels=azure_service_bus.labels, + business_attributes=azure_service_bus.business_attributes, + custom_attributes=azure_service_bus.custom_attributes, + pending_tasks=azure_service_bus.pending_tasks, + proxy=azure_service_bus.proxy, + is_incomplete=azure_service_bus.is_incomplete, + provenance_type=azure_service_bus.provenance_type, + home_id=azure_service_bus.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _azure_service_bus_from_nested(nested: AzureServiceBusNested) -> AzureServiceBus: + """Convert nested format to flat AzureServiceBus.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else AzureServiceBusAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _AZURE_SERVICE_BUS_REL_FIELDS, + AzureServiceBusRelationshipAttributes, + ) + return AzureServiceBus( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_azure_service_bus_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _azure_service_bus_to_nested_bytes( + azure_service_bus: AzureServiceBus, serde: Serde +) -> bytes: + """Convert flat AzureServiceBus to nested JSON bytes.""" + return serde.encode(_azure_service_bus_to_nested(azure_service_bus)) + + +def _azure_service_bus_from_nested_bytes(data: bytes, serde: Serde) -> AzureServiceBus: + """Convert nested JSON bytes to flat AzureServiceBus.""" + nested = serde.decode(data, AzureServiceBusNested) + return _azure_service_bus_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + RelationField, +) + +AzureServiceBus.AZURE_SERVICE_BUS_NAMESPACE_QUALIFIED_NAME = KeywordField( + "azureServiceBusNamespaceQualifiedName", "azureServiceBusNamespaceQualifiedName" +) +AzureServiceBus.AZURE_SERVICE_BUS_NAMESPACE_NAME = KeywordTextField( + "azureServiceBusNamespaceName", + "azureServiceBusNamespaceName", + "azureServiceBusNamespaceName.text", +) +AzureServiceBus.AZURE_SERVICE_BUS_SCHEMA_QUALIFIED_NAME = KeywordField( + "azureServiceBusSchemaQualifiedName", "azureServiceBusSchemaQualifiedName" +) +AzureServiceBus.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +AzureServiceBus.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +AzureServiceBus.ANOMALO_CHECKS = RelationField("anomaloChecks") +AzureServiceBus.APPLICATION = RelationField("application") +AzureServiceBus.APPLICATION_FIELD = RelationField("applicationField") +AzureServiceBus.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +AzureServiceBus.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +AzureServiceBus.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +AzureServiceBus.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +AzureServiceBus.METRICS = RelationField("metrics") +AzureServiceBus.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +AzureServiceBus.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +AzureServiceBus.MEANINGS = RelationField("meanings") +AzureServiceBus.MC_MONITORS = RelationField("mcMonitors") +AzureServiceBus.MC_INCIDENTS = RelationField("mcIncidents") +AzureServiceBus.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +AzureServiceBus.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +AzureServiceBus.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +AzureServiceBus.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +AzureServiceBus.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +AzureServiceBus.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +AzureServiceBus.FILES = RelationField("files") +AzureServiceBus.LINKS = RelationField("links") +AzureServiceBus.README = RelationField("readme") +AzureServiceBus.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +AzureServiceBus.SODA_CHECKS = RelationField("sodaChecks") +AzureServiceBus.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +AzureServiceBus.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/azure_service_bus_namespace.py b/pyatlan_v9/model/assets/azure_service_bus_namespace.py new file mode 100644 index 000000000..f706b7d1c --- /dev/null +++ b/pyatlan_v9/model/assets/azure_service_bus_namespace.py @@ -0,0 +1,631 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +AzureServiceBusNamespace asset model with flattened inheritance. + +This module provides: +- AzureServiceBusNamespace: Flat asset class (easy to use) +- AzureServiceBusNamespaceAttributes: Nested attributes struct (extends AssetAttributes) +- AzureServiceBusNamespaceNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .azure_service_bus_related import RelatedAzureServiceBusTopic + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class AzureServiceBusNamespace(Asset): + """ + Instances of AzureServiceBusNamespace in Atlan. + """ + + AZURE_SERVICE_BUS_NAMESPACE_QUALIFIED_NAME: ClassVar[Any] = None + AZURE_SERVICE_BUS_NAMESPACE_NAME: ClassVar[Any] = None + AZURE_SERVICE_BUS_SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + AZURE_SERVICE_BUS_TOPICS: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "AzureServiceBusNamespace" + + azure_service_bus_namespace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AzureServiceBus Namespace in which this asset exists.""" + + azure_service_bus_namespace_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AzureServiceBus Namespace in which this asset exists.""" + + azure_service_bus_schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AzureServiceBus Schema in which this asset exists.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + azure_service_bus_topics: Union[ + List[RelatedAzureServiceBusTopic], None, UnsetType + ] = UNSET + """AzureServiceBusTopic assets contained within this AzureServiceBusNamespace.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "AzureServiceBusNamespace" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _azure_service_bus_namespace_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> AzureServiceBusNamespace: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + AzureServiceBusNamespace instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _azure_service_bus_namespace_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class AzureServiceBusNamespaceAttributes(AssetAttributes): + """AzureServiceBusNamespace-specific attributes for nested API format.""" + + azure_service_bus_namespace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AzureServiceBus Namespace in which this asset exists.""" + + azure_service_bus_namespace_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AzureServiceBus Namespace in which this asset exists.""" + + azure_service_bus_schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AzureServiceBus Schema in which this asset exists.""" + + +class AzureServiceBusNamespaceRelationshipAttributes(AssetRelationshipAttributes): + """AzureServiceBusNamespace-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + azure_service_bus_topics: Union[ + List[RelatedAzureServiceBusTopic], None, UnsetType + ] = UNSET + """AzureServiceBusTopic assets contained within this AzureServiceBusNamespace.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class AzureServiceBusNamespaceNested(AssetNested): + """AzureServiceBusNamespace in nested API format for high-performance serialization.""" + + attributes: Union[AzureServiceBusNamespaceAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + AzureServiceBusNamespaceRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + AzureServiceBusNamespaceRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + AzureServiceBusNamespaceRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_AZURE_SERVICE_BUS_NAMESPACE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "azure_service_bus_topics", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_azure_service_bus_namespace_attrs( + attrs: AzureServiceBusNamespaceAttributes, obj: AzureServiceBusNamespace +) -> None: + """Populate AzureServiceBusNamespace-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.azure_service_bus_namespace_qualified_name = ( + obj.azure_service_bus_namespace_qualified_name + ) + attrs.azure_service_bus_namespace_name = obj.azure_service_bus_namespace_name + attrs.azure_service_bus_schema_qualified_name = ( + obj.azure_service_bus_schema_qualified_name + ) + + +def _extract_azure_service_bus_namespace_attrs( + attrs: AzureServiceBusNamespaceAttributes, +) -> dict: + """Extract all AzureServiceBusNamespace attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["azure_service_bus_namespace_qualified_name"] = ( + attrs.azure_service_bus_namespace_qualified_name + ) + result["azure_service_bus_namespace_name"] = attrs.azure_service_bus_namespace_name + result["azure_service_bus_schema_qualified_name"] = ( + attrs.azure_service_bus_schema_qualified_name + ) + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _azure_service_bus_namespace_to_nested( + azure_service_bus_namespace: AzureServiceBusNamespace, +) -> AzureServiceBusNamespaceNested: + """Convert flat AzureServiceBusNamespace to nested format.""" + attrs = AzureServiceBusNamespaceAttributes() + _populate_azure_service_bus_namespace_attrs(attrs, azure_service_bus_namespace) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + azure_service_bus_namespace, + _AZURE_SERVICE_BUS_NAMESPACE_REL_FIELDS, + AzureServiceBusNamespaceRelationshipAttributes, + ) + return AzureServiceBusNamespaceNested( + guid=azure_service_bus_namespace.guid, + type_name=azure_service_bus_namespace.type_name, + status=azure_service_bus_namespace.status, + version=azure_service_bus_namespace.version, + create_time=azure_service_bus_namespace.create_time, + update_time=azure_service_bus_namespace.update_time, + created_by=azure_service_bus_namespace.created_by, + updated_by=azure_service_bus_namespace.updated_by, + classifications=azure_service_bus_namespace.classifications, + classification_names=azure_service_bus_namespace.classification_names, + meanings=azure_service_bus_namespace.meanings, + labels=azure_service_bus_namespace.labels, + business_attributes=azure_service_bus_namespace.business_attributes, + custom_attributes=azure_service_bus_namespace.custom_attributes, + pending_tasks=azure_service_bus_namespace.pending_tasks, + proxy=azure_service_bus_namespace.proxy, + is_incomplete=azure_service_bus_namespace.is_incomplete, + provenance_type=azure_service_bus_namespace.provenance_type, + home_id=azure_service_bus_namespace.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _azure_service_bus_namespace_from_nested( + nested: AzureServiceBusNamespaceNested, +) -> AzureServiceBusNamespace: + """Convert nested format to flat AzureServiceBusNamespace.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else AzureServiceBusNamespaceAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _AZURE_SERVICE_BUS_NAMESPACE_REL_FIELDS, + AzureServiceBusNamespaceRelationshipAttributes, + ) + return AzureServiceBusNamespace( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_azure_service_bus_namespace_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _azure_service_bus_namespace_to_nested_bytes( + azure_service_bus_namespace: AzureServiceBusNamespace, serde: Serde +) -> bytes: + """Convert flat AzureServiceBusNamespace to nested JSON bytes.""" + return serde.encode( + _azure_service_bus_namespace_to_nested(azure_service_bus_namespace) + ) + + +def _azure_service_bus_namespace_from_nested_bytes( + data: bytes, serde: Serde +) -> AzureServiceBusNamespace: + """Convert nested JSON bytes to flat AzureServiceBusNamespace.""" + nested = serde.decode(data, AzureServiceBusNamespaceNested) + return _azure_service_bus_namespace_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + RelationField, +) + +AzureServiceBusNamespace.AZURE_SERVICE_BUS_NAMESPACE_QUALIFIED_NAME = KeywordField( + "azureServiceBusNamespaceQualifiedName", "azureServiceBusNamespaceQualifiedName" +) +AzureServiceBusNamespace.AZURE_SERVICE_BUS_NAMESPACE_NAME = KeywordTextField( + "azureServiceBusNamespaceName", + "azureServiceBusNamespaceName", + "azureServiceBusNamespaceName.text", +) +AzureServiceBusNamespace.AZURE_SERVICE_BUS_SCHEMA_QUALIFIED_NAME = KeywordField( + "azureServiceBusSchemaQualifiedName", "azureServiceBusSchemaQualifiedName" +) +AzureServiceBusNamespace.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +AzureServiceBusNamespace.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +AzureServiceBusNamespace.ANOMALO_CHECKS = RelationField("anomaloChecks") +AzureServiceBusNamespace.APPLICATION = RelationField("application") +AzureServiceBusNamespace.APPLICATION_FIELD = RelationField("applicationField") +AzureServiceBusNamespace.AZURE_SERVICE_BUS_TOPICS = RelationField( + "azureServiceBusTopics" +) +AzureServiceBusNamespace.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +AzureServiceBusNamespace.INPUT_PORT_DATA_PRODUCTS = RelationField( + "inputPortDataProducts" +) +AzureServiceBusNamespace.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +AzureServiceBusNamespace.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +AzureServiceBusNamespace.METRICS = RelationField("metrics") +AzureServiceBusNamespace.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +AzureServiceBusNamespace.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +AzureServiceBusNamespace.MEANINGS = RelationField("meanings") +AzureServiceBusNamespace.MC_MONITORS = RelationField("mcMonitors") +AzureServiceBusNamespace.MC_INCIDENTS = RelationField("mcIncidents") +AzureServiceBusNamespace.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +AzureServiceBusNamespace.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +AzureServiceBusNamespace.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +AzureServiceBusNamespace.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +AzureServiceBusNamespace.USER_DEF_RELATIONSHIP_TO = RelationField( + "userDefRelationshipTo" +) +AzureServiceBusNamespace.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +AzureServiceBusNamespace.FILES = RelationField("files") +AzureServiceBusNamespace.LINKS = RelationField("links") +AzureServiceBusNamespace.README = RelationField("readme") +AzureServiceBusNamespace.SCHEMA_REGISTRY_SUBJECTS = RelationField( + "schemaRegistrySubjects" +) +AzureServiceBusNamespace.SODA_CHECKS = RelationField("sodaChecks") +AzureServiceBusNamespace.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +AzureServiceBusNamespace.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/azure_service_bus_related.py b/pyatlan_v9/model/assets/azure_service_bus_related.py new file mode 100644 index 000000000..59435d564 --- /dev/null +++ b/pyatlan_v9/model/assets/azure_service_bus_related.py @@ -0,0 +1,95 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for AzureServiceBus module. + +This module contains all Related{Type} classes for the AzureServiceBus type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Union + +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedEventStore +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedAzureServiceBus", + "RelatedAzureServiceBusNamespace", + "RelatedAzureServiceBusSchema", + "RelatedAzureServiceBusTopic", +] + + +class RelatedAzureServiceBus(RelatedEventStore): + """ + Related entity reference for AzureServiceBus assets. + + Extends RelatedEventStore with AzureServiceBus-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "AzureServiceBus" so it serializes correctly + + azure_service_bus_namespace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AzureServiceBus Namespace in which this asset exists.""" + + azure_service_bus_namespace_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AzureServiceBus Namespace in which this asset exists.""" + + azure_service_bus_schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AzureServiceBus Schema in which this asset exists.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "AzureServiceBus" + + +class RelatedAzureServiceBusNamespace(RelatedAzureServiceBus): + """ + Related entity reference for AzureServiceBusNamespace assets. + + Extends RelatedAzureServiceBus with AzureServiceBusNamespace-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "AzureServiceBusNamespace" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "AzureServiceBusNamespace" + + +class RelatedAzureServiceBusSchema(RelatedAzureServiceBus): + """ + Related entity reference for AzureServiceBusSchema assets. + + Extends RelatedAzureServiceBus with AzureServiceBusSchema-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "AzureServiceBusSchema" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "AzureServiceBusSchema" + + +class RelatedAzureServiceBusTopic(RelatedAzureServiceBus): + """ + Related entity reference for AzureServiceBusTopic assets. + + Extends RelatedAzureServiceBus with AzureServiceBusTopic-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "AzureServiceBusTopic" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "AzureServiceBusTopic" diff --git a/pyatlan_v9/model/assets/azure_service_bus_schema.py b/pyatlan_v9/model/assets/azure_service_bus_schema.py new file mode 100644 index 000000000..ebcfc6e62 --- /dev/null +++ b/pyatlan_v9/model/assets/azure_service_bus_schema.py @@ -0,0 +1,621 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +AzureServiceBusSchema asset model with flattened inheritance. + +This module provides: +- AzureServiceBusSchema: Flat asset class (easy to use) +- AzureServiceBusSchemaAttributes: Nested attributes struct (extends AssetAttributes) +- AzureServiceBusSchemaNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .azure_service_bus_related import RelatedAzureServiceBusTopic + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class AzureServiceBusSchema(Asset): + """ + Instances of AzureServiceBusSchema in Atlan. + """ + + AZURE_SERVICE_BUS_NAMESPACE_QUALIFIED_NAME: ClassVar[Any] = None + AZURE_SERVICE_BUS_NAMESPACE_NAME: ClassVar[Any] = None + AZURE_SERVICE_BUS_SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + AZURE_SERVICE_BUS_TOPICS: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "AzureServiceBusSchema" + + azure_service_bus_namespace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AzureServiceBus Namespace in which this asset exists.""" + + azure_service_bus_namespace_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AzureServiceBus Namespace in which this asset exists.""" + + azure_service_bus_schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AzureServiceBus Schema in which this asset exists.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + azure_service_bus_topics: Union[ + List[RelatedAzureServiceBusTopic], None, UnsetType + ] = UNSET + """AzureServiceBusTopic assets containing this AzureServiceBusSchema.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "AzureServiceBusSchema" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _azure_service_bus_schema_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> AzureServiceBusSchema: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + AzureServiceBusSchema instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _azure_service_bus_schema_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class AzureServiceBusSchemaAttributes(AssetAttributes): + """AzureServiceBusSchema-specific attributes for nested API format.""" + + azure_service_bus_namespace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AzureServiceBus Namespace in which this asset exists.""" + + azure_service_bus_namespace_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AzureServiceBus Namespace in which this asset exists.""" + + azure_service_bus_schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AzureServiceBus Schema in which this asset exists.""" + + +class AzureServiceBusSchemaRelationshipAttributes(AssetRelationshipAttributes): + """AzureServiceBusSchema-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + azure_service_bus_topics: Union[ + List[RelatedAzureServiceBusTopic], None, UnsetType + ] = UNSET + """AzureServiceBusTopic assets containing this AzureServiceBusSchema.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class AzureServiceBusSchemaNested(AssetNested): + """AzureServiceBusSchema in nested API format for high-performance serialization.""" + + attributes: Union[AzureServiceBusSchemaAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + AzureServiceBusSchemaRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + AzureServiceBusSchemaRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + AzureServiceBusSchemaRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_AZURE_SERVICE_BUS_SCHEMA_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "azure_service_bus_topics", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_azure_service_bus_schema_attrs( + attrs: AzureServiceBusSchemaAttributes, obj: AzureServiceBusSchema +) -> None: + """Populate AzureServiceBusSchema-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.azure_service_bus_namespace_qualified_name = ( + obj.azure_service_bus_namespace_qualified_name + ) + attrs.azure_service_bus_namespace_name = obj.azure_service_bus_namespace_name + attrs.azure_service_bus_schema_qualified_name = ( + obj.azure_service_bus_schema_qualified_name + ) + + +def _extract_azure_service_bus_schema_attrs( + attrs: AzureServiceBusSchemaAttributes, +) -> dict: + """Extract all AzureServiceBusSchema attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["azure_service_bus_namespace_qualified_name"] = ( + attrs.azure_service_bus_namespace_qualified_name + ) + result["azure_service_bus_namespace_name"] = attrs.azure_service_bus_namespace_name + result["azure_service_bus_schema_qualified_name"] = ( + attrs.azure_service_bus_schema_qualified_name + ) + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _azure_service_bus_schema_to_nested( + azure_service_bus_schema: AzureServiceBusSchema, +) -> AzureServiceBusSchemaNested: + """Convert flat AzureServiceBusSchema to nested format.""" + attrs = AzureServiceBusSchemaAttributes() + _populate_azure_service_bus_schema_attrs(attrs, azure_service_bus_schema) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + azure_service_bus_schema, + _AZURE_SERVICE_BUS_SCHEMA_REL_FIELDS, + AzureServiceBusSchemaRelationshipAttributes, + ) + return AzureServiceBusSchemaNested( + guid=azure_service_bus_schema.guid, + type_name=azure_service_bus_schema.type_name, + status=azure_service_bus_schema.status, + version=azure_service_bus_schema.version, + create_time=azure_service_bus_schema.create_time, + update_time=azure_service_bus_schema.update_time, + created_by=azure_service_bus_schema.created_by, + updated_by=azure_service_bus_schema.updated_by, + classifications=azure_service_bus_schema.classifications, + classification_names=azure_service_bus_schema.classification_names, + meanings=azure_service_bus_schema.meanings, + labels=azure_service_bus_schema.labels, + business_attributes=azure_service_bus_schema.business_attributes, + custom_attributes=azure_service_bus_schema.custom_attributes, + pending_tasks=azure_service_bus_schema.pending_tasks, + proxy=azure_service_bus_schema.proxy, + is_incomplete=azure_service_bus_schema.is_incomplete, + provenance_type=azure_service_bus_schema.provenance_type, + home_id=azure_service_bus_schema.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _azure_service_bus_schema_from_nested( + nested: AzureServiceBusSchemaNested, +) -> AzureServiceBusSchema: + """Convert nested format to flat AzureServiceBusSchema.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else AzureServiceBusSchemaAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _AZURE_SERVICE_BUS_SCHEMA_REL_FIELDS, + AzureServiceBusSchemaRelationshipAttributes, + ) + return AzureServiceBusSchema( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_azure_service_bus_schema_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _azure_service_bus_schema_to_nested_bytes( + azure_service_bus_schema: AzureServiceBusSchema, serde: Serde +) -> bytes: + """Convert flat AzureServiceBusSchema to nested JSON bytes.""" + return serde.encode(_azure_service_bus_schema_to_nested(azure_service_bus_schema)) + + +def _azure_service_bus_schema_from_nested_bytes( + data: bytes, serde: Serde +) -> AzureServiceBusSchema: + """Convert nested JSON bytes to flat AzureServiceBusSchema.""" + nested = serde.decode(data, AzureServiceBusSchemaNested) + return _azure_service_bus_schema_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + RelationField, +) + +AzureServiceBusSchema.AZURE_SERVICE_BUS_NAMESPACE_QUALIFIED_NAME = KeywordField( + "azureServiceBusNamespaceQualifiedName", "azureServiceBusNamespaceQualifiedName" +) +AzureServiceBusSchema.AZURE_SERVICE_BUS_NAMESPACE_NAME = KeywordTextField( + "azureServiceBusNamespaceName", + "azureServiceBusNamespaceName", + "azureServiceBusNamespaceName.text", +) +AzureServiceBusSchema.AZURE_SERVICE_BUS_SCHEMA_QUALIFIED_NAME = KeywordField( + "azureServiceBusSchemaQualifiedName", "azureServiceBusSchemaQualifiedName" +) +AzureServiceBusSchema.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +AzureServiceBusSchema.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +AzureServiceBusSchema.ANOMALO_CHECKS = RelationField("anomaloChecks") +AzureServiceBusSchema.APPLICATION = RelationField("application") +AzureServiceBusSchema.APPLICATION_FIELD = RelationField("applicationField") +AzureServiceBusSchema.AZURE_SERVICE_BUS_TOPICS = RelationField("azureServiceBusTopics") +AzureServiceBusSchema.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +AzureServiceBusSchema.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +AzureServiceBusSchema.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +AzureServiceBusSchema.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +AzureServiceBusSchema.METRICS = RelationField("metrics") +AzureServiceBusSchema.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +AzureServiceBusSchema.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +AzureServiceBusSchema.MEANINGS = RelationField("meanings") +AzureServiceBusSchema.MC_MONITORS = RelationField("mcMonitors") +AzureServiceBusSchema.MC_INCIDENTS = RelationField("mcIncidents") +AzureServiceBusSchema.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +AzureServiceBusSchema.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +AzureServiceBusSchema.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +AzureServiceBusSchema.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +AzureServiceBusSchema.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +AzureServiceBusSchema.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +AzureServiceBusSchema.FILES = RelationField("files") +AzureServiceBusSchema.LINKS = RelationField("links") +AzureServiceBusSchema.README = RelationField("readme") +AzureServiceBusSchema.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +AzureServiceBusSchema.SODA_CHECKS = RelationField("sodaChecks") +AzureServiceBusSchema.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +AzureServiceBusSchema.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/azure_service_bus_topic.py b/pyatlan_v9/model/assets/azure_service_bus_topic.py new file mode 100644 index 000000000..d6a2ca73c --- /dev/null +++ b/pyatlan_v9/model/assets/azure_service_bus_topic.py @@ -0,0 +1,642 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +AzureServiceBusTopic asset model with flattened inheritance. + +This module provides: +- AzureServiceBusTopic: Flat asset class (easy to use) +- AzureServiceBusTopicAttributes: Nested attributes struct (extends AssetAttributes) +- AzureServiceBusTopicNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .azure_service_bus_related import ( + RelatedAzureServiceBusNamespace, + RelatedAzureServiceBusSchema, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class AzureServiceBusTopic(Asset): + """ + Instances of AzureServiceBusField in Atlan. + """ + + AZURE_SERVICE_BUS_NAMESPACE_QUALIFIED_NAME: ClassVar[Any] = None + AZURE_SERVICE_BUS_NAMESPACE_NAME: ClassVar[Any] = None + AZURE_SERVICE_BUS_SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + AZURE_SERVICE_BUS_SCHEMAS: ClassVar[Any] = None + AZURE_SERVICE_BUS_NAMESPACE: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "AzureServiceBusTopic" + + azure_service_bus_namespace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AzureServiceBus Namespace in which this asset exists.""" + + azure_service_bus_namespace_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AzureServiceBus Namespace in which this asset exists.""" + + azure_service_bus_schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AzureServiceBus Schema in which this asset exists.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + azure_service_bus_schemas: Union[ + List[RelatedAzureServiceBusSchema], None, UnsetType + ] = UNSET + """AzureServiceBusSchema assets contained within this AzureServiceBusTopic.""" + + azure_service_bus_namespace: Union[ + RelatedAzureServiceBusNamespace, None, UnsetType + ] = UNSET + """AzureServiceBusNamespace asset containing this AzureServiceBusTopic.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "AzureServiceBusTopic" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _azure_service_bus_topic_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> AzureServiceBusTopic: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + AzureServiceBusTopic instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _azure_service_bus_topic_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class AzureServiceBusTopicAttributes(AssetAttributes): + """AzureServiceBusTopic-specific attributes for nested API format.""" + + azure_service_bus_namespace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AzureServiceBus Namespace in which this asset exists.""" + + azure_service_bus_namespace_name: Union[str, None, UnsetType] = UNSET + """Simple name of the AzureServiceBus Namespace in which this asset exists.""" + + azure_service_bus_schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the AzureServiceBus Schema in which this asset exists.""" + + +class AzureServiceBusTopicRelationshipAttributes(AssetRelationshipAttributes): + """AzureServiceBusTopic-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + azure_service_bus_schemas: Union[ + List[RelatedAzureServiceBusSchema], None, UnsetType + ] = UNSET + """AzureServiceBusSchema assets contained within this AzureServiceBusTopic.""" + + azure_service_bus_namespace: Union[ + RelatedAzureServiceBusNamespace, None, UnsetType + ] = UNSET + """AzureServiceBusNamespace asset containing this AzureServiceBusTopic.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class AzureServiceBusTopicNested(AssetNested): + """AzureServiceBusTopic in nested API format for high-performance serialization.""" + + attributes: Union[AzureServiceBusTopicAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + AzureServiceBusTopicRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + AzureServiceBusTopicRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + AzureServiceBusTopicRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_AZURE_SERVICE_BUS_TOPIC_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "azure_service_bus_schemas", + "azure_service_bus_namespace", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_azure_service_bus_topic_attrs( + attrs: AzureServiceBusTopicAttributes, obj: AzureServiceBusTopic +) -> None: + """Populate AzureServiceBusTopic-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.azure_service_bus_namespace_qualified_name = ( + obj.azure_service_bus_namespace_qualified_name + ) + attrs.azure_service_bus_namespace_name = obj.azure_service_bus_namespace_name + attrs.azure_service_bus_schema_qualified_name = ( + obj.azure_service_bus_schema_qualified_name + ) + + +def _extract_azure_service_bus_topic_attrs( + attrs: AzureServiceBusTopicAttributes, +) -> dict: + """Extract all AzureServiceBusTopic attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["azure_service_bus_namespace_qualified_name"] = ( + attrs.azure_service_bus_namespace_qualified_name + ) + result["azure_service_bus_namespace_name"] = attrs.azure_service_bus_namespace_name + result["azure_service_bus_schema_qualified_name"] = ( + attrs.azure_service_bus_schema_qualified_name + ) + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _azure_service_bus_topic_to_nested( + azure_service_bus_topic: AzureServiceBusTopic, +) -> AzureServiceBusTopicNested: + """Convert flat AzureServiceBusTopic to nested format.""" + attrs = AzureServiceBusTopicAttributes() + _populate_azure_service_bus_topic_attrs(attrs, azure_service_bus_topic) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + azure_service_bus_topic, + _AZURE_SERVICE_BUS_TOPIC_REL_FIELDS, + AzureServiceBusTopicRelationshipAttributes, + ) + return AzureServiceBusTopicNested( + guid=azure_service_bus_topic.guid, + type_name=azure_service_bus_topic.type_name, + status=azure_service_bus_topic.status, + version=azure_service_bus_topic.version, + create_time=azure_service_bus_topic.create_time, + update_time=azure_service_bus_topic.update_time, + created_by=azure_service_bus_topic.created_by, + updated_by=azure_service_bus_topic.updated_by, + classifications=azure_service_bus_topic.classifications, + classification_names=azure_service_bus_topic.classification_names, + meanings=azure_service_bus_topic.meanings, + labels=azure_service_bus_topic.labels, + business_attributes=azure_service_bus_topic.business_attributes, + custom_attributes=azure_service_bus_topic.custom_attributes, + pending_tasks=azure_service_bus_topic.pending_tasks, + proxy=azure_service_bus_topic.proxy, + is_incomplete=azure_service_bus_topic.is_incomplete, + provenance_type=azure_service_bus_topic.provenance_type, + home_id=azure_service_bus_topic.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _azure_service_bus_topic_from_nested( + nested: AzureServiceBusTopicNested, +) -> AzureServiceBusTopic: + """Convert nested format to flat AzureServiceBusTopic.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else AzureServiceBusTopicAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _AZURE_SERVICE_BUS_TOPIC_REL_FIELDS, + AzureServiceBusTopicRelationshipAttributes, + ) + return AzureServiceBusTopic( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_azure_service_bus_topic_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _azure_service_bus_topic_to_nested_bytes( + azure_service_bus_topic: AzureServiceBusTopic, serde: Serde +) -> bytes: + """Convert flat AzureServiceBusTopic to nested JSON bytes.""" + return serde.encode(_azure_service_bus_topic_to_nested(azure_service_bus_topic)) + + +def _azure_service_bus_topic_from_nested_bytes( + data: bytes, serde: Serde +) -> AzureServiceBusTopic: + """Convert nested JSON bytes to flat AzureServiceBusTopic.""" + nested = serde.decode(data, AzureServiceBusTopicNested) + return _azure_service_bus_topic_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + RelationField, +) + +AzureServiceBusTopic.AZURE_SERVICE_BUS_NAMESPACE_QUALIFIED_NAME = KeywordField( + "azureServiceBusNamespaceQualifiedName", "azureServiceBusNamespaceQualifiedName" +) +AzureServiceBusTopic.AZURE_SERVICE_BUS_NAMESPACE_NAME = KeywordTextField( + "azureServiceBusNamespaceName", + "azureServiceBusNamespaceName", + "azureServiceBusNamespaceName.text", +) +AzureServiceBusTopic.AZURE_SERVICE_BUS_SCHEMA_QUALIFIED_NAME = KeywordField( + "azureServiceBusSchemaQualifiedName", "azureServiceBusSchemaQualifiedName" +) +AzureServiceBusTopic.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +AzureServiceBusTopic.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +AzureServiceBusTopic.ANOMALO_CHECKS = RelationField("anomaloChecks") +AzureServiceBusTopic.APPLICATION = RelationField("application") +AzureServiceBusTopic.APPLICATION_FIELD = RelationField("applicationField") +AzureServiceBusTopic.AZURE_SERVICE_BUS_SCHEMAS = RelationField("azureServiceBusSchemas") +AzureServiceBusTopic.AZURE_SERVICE_BUS_NAMESPACE = RelationField( + "azureServiceBusNamespace" +) +AzureServiceBusTopic.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +AzureServiceBusTopic.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +AzureServiceBusTopic.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +AzureServiceBusTopic.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +AzureServiceBusTopic.METRICS = RelationField("metrics") +AzureServiceBusTopic.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +AzureServiceBusTopic.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +AzureServiceBusTopic.MEANINGS = RelationField("meanings") +AzureServiceBusTopic.MC_MONITORS = RelationField("mcMonitors") +AzureServiceBusTopic.MC_INCIDENTS = RelationField("mcIncidents") +AzureServiceBusTopic.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +AzureServiceBusTopic.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +AzureServiceBusTopic.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +AzureServiceBusTopic.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +AzureServiceBusTopic.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +AzureServiceBusTopic.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +AzureServiceBusTopic.FILES = RelationField("files") +AzureServiceBusTopic.LINKS = RelationField("links") +AzureServiceBusTopic.README = RelationField("readme") +AzureServiceBusTopic.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +AzureServiceBusTopic.SODA_CHECKS = RelationField("sodaChecks") +AzureServiceBusTopic.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +AzureServiceBusTopic.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/badge.py b/pyatlan_v9/model/assets/badge.py new file mode 100644 index 000000000..0a10883bc --- /dev/null +++ b/pyatlan_v9/model/assets/badge.py @@ -0,0 +1,95 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Badge asset model for pyatlan_v9.""" + +from __future__ import annotations + +from typing import Any, Union + +from msgspec import UNSET, UnsetType + +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .asset import Asset +from .badge_condition import BadgeCondition + + +@register_asset +class Badge(Asset): + """Custom metadata badge asset.""" + + type_name: Union[str, UnsetType] = "Badge" + + badge_conditions: Union[list[BadgeCondition], None, UnsetType] = UNSET + badge_metadata_attribute: Union[str, None, UnsetType] = UNSET + + @classmethod + @init_guid + def creator( + cls, + *, + client: Any, + name: str, + cm_name: str, + cm_attribute: str, + badge_conditions: list[BadgeCondition], + ) -> "Badge": + """Create a new Badge asset.""" + validate_required_fields( + ["client", "name", "cm_name", "cm_attribute", "badge_conditions"], + [client, name, cm_name, cm_attribute, badge_conditions], + ) + cm_id = client.custom_metadata_cache.get_id_for_name(cm_name) + cm_attr_id = client.custom_metadata_cache.get_attr_id_for_name( + set_name=cm_name, attr_name=cm_attribute + ) + from pyatlan.model.enums import EntityStatus + + return cls( + name=name, + qualified_name=f"badges/global/{cm_id}.{cm_attr_id}", + badge_metadata_attribute=f"{cm_id}.{cm_attr_id}", + badge_conditions=badge_conditions, + status=EntityStatus.ACTIVE, + ) + + @classmethod + async def creator_async( + cls, + *, + client: Any, + name: str, + cm_name: str, + cm_attribute: str, + badge_conditions: list[BadgeCondition], + ) -> "Badge": + """Create a new Badge asset (async version).""" + validate_required_fields( + ["client", "name", "cm_name", "cm_attribute", "badge_conditions"], + [client, name, cm_name, cm_attribute, badge_conditions], + ) + cm_id = await client.custom_metadata_cache.get_id_for_name(cm_name) + cm_attr_id = await client.custom_metadata_cache.get_attr_id_for_name( + set_name=cm_name, attr_name=cm_attribute + ) + from pyatlan.model.enums import EntityStatus + + return cls( + name=name, + qualified_name=f"badges/global/{cm_id}.{cm_attr_id}", + badge_metadata_attribute=f"{cm_id}.{cm_attr_id}", + badge_conditions=badge_conditions, + status=EntityStatus.ACTIVE, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "Badge": + """Create a Badge instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "Badge": + """Return only fields required for update operations.""" + return Badge.updater(qualified_name=self.qualified_name, name=self.name) diff --git a/pyatlan_v9/model/assets/badge_condition.py b/pyatlan_v9/model/assets/badge_condition.py new file mode 100644 index 000000000..b9118f84a --- /dev/null +++ b/pyatlan_v9/model/assets/badge_condition.py @@ -0,0 +1,48 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Badge condition struct for pyatlan_v9.""" + +from __future__ import annotations + +from typing import Union + +import msgspec + +from pyatlan.model.enums import BadgeComparisonOperator, BadgeConditionColor +from pyatlan_v9.utils import validate_required_fields + + +class BadgeCondition(msgspec.Struct, kw_only=True): + """Condition used to derive a badge color for a value.""" + + badge_condition_operator: Union[str, None] = None + badge_condition_value: Union[str, None] = None + badge_condition_colorhex: Union[str, None] = None + + @classmethod + def creator( + cls, + *, + badge_condition_operator: BadgeComparisonOperator, + badge_condition_value: str, + badge_condition_colorhex: Union[BadgeConditionColor, str], + ) -> "BadgeCondition": + """Create a badge condition.""" + validate_required_fields( + [ + "badge_condition_operator", + "badge_condition_value", + "badge_condition_colorhex", + ], + [badge_condition_operator, badge_condition_value, badge_condition_colorhex], + ) + return cls( + badge_condition_operator=badge_condition_operator.value, + badge_condition_value=badge_condition_value, + badge_condition_colorhex=( + badge_condition_colorhex.value + if isinstance(badge_condition_colorhex, BadgeConditionColor) + else badge_condition_colorhex + ), + ) diff --git a/pyatlan_v9/model/assets/bi.py b/pyatlan_v9/model/assets/bi.py new file mode 100644 index 000000000..a3040a490 --- /dev/null +++ b/pyatlan_v9/model/assets/bi.py @@ -0,0 +1,519 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +BI asset model with flattened inheritance. + +This module provides: +- BI: Flat asset class (easy to use) +- BIAttributes: Nested attributes struct (extends AssetAttributes) +- BINested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class BI(Asset): + """ + Base class for business intelligence assets. + """ + + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "BI" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "BI" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _bi_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> BI: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + BI instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _bi_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class BIAttributes(AssetAttributes): + """BI-specific attributes for nested API format.""" + + pass + + +class BIRelationshipAttributes(AssetRelationshipAttributes): + """BI-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class BINested(AssetNested): + """BI in nested API format for high-performance serialization.""" + + attributes: Union[BIAttributes, UnsetType] = UNSET + relationship_attributes: Union[BIRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[BIRelationshipAttributes, UnsetType] = UNSET + remove_relationship_attributes: Union[BIRelationshipAttributes, UnsetType] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_BI_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_bi_attrs(attrs: BIAttributes, obj: BI) -> None: + """Populate BI-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + + +def _extract_bi_attrs(attrs: BIAttributes) -> dict: + """Extract all BI attributes from the attrs struct into a flat dict.""" + return _extract_asset_attrs(attrs) + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _bi_to_nested(bi: BI) -> BINested: + """Convert flat BI to nested format.""" + attrs = BIAttributes() + _populate_bi_attrs(attrs, bi) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + bi, _BI_REL_FIELDS, BIRelationshipAttributes + ) + return BINested( + guid=bi.guid, + type_name=bi.type_name, + status=bi.status, + version=bi.version, + create_time=bi.create_time, + update_time=bi.update_time, + created_by=bi.created_by, + updated_by=bi.updated_by, + classifications=bi.classifications, + classification_names=bi.classification_names, + meanings=bi.meanings, + labels=bi.labels, + business_attributes=bi.business_attributes, + custom_attributes=bi.custom_attributes, + pending_tasks=bi.pending_tasks, + proxy=bi.proxy, + is_incomplete=bi.is_incomplete, + provenance_type=bi.provenance_type, + home_id=bi.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _bi_from_nested(nested: BINested) -> BI: + """Convert nested format to flat BI.""" + attrs = nested.attributes if nested.attributes is not UNSET else BIAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _BI_REL_FIELDS, + BIRelationshipAttributes, + ) + return BI( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_bi_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _bi_to_nested_bytes(bi: BI, serde: Serde) -> bytes: + """Convert flat BI to nested JSON bytes.""" + return serde.encode(_bi_to_nested(bi)) + + +def _bi_from_nested_bytes(data: bytes, serde: Serde) -> BI: + """Convert nested JSON bytes to flat BI.""" + nested = serde.decode(data, BINested) + return _bi_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import RelationField # noqa: E402 + +BI.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +BI.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +BI.ANOMALO_CHECKS = RelationField("anomaloChecks") +BI.APPLICATION = RelationField("application") +BI.APPLICATION_FIELD = RelationField("applicationField") +BI.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +BI.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +BI.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +BI.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +BI.METRICS = RelationField("metrics") +BI.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +BI.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +BI.MEANINGS = RelationField("meanings") +BI.MC_MONITORS = RelationField("mcMonitors") +BI.MC_INCIDENTS = RelationField("mcIncidents") +BI.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +BI.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +BI.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +BI.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +BI.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +BI.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +BI.FILES = RelationField("files") +BI.LINKS = RelationField("links") +BI.README = RelationField("readme") +BI.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +BI.SODA_CHECKS = RelationField("sodaChecks") +BI.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +BI.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/bi_process.py b/pyatlan_v9/model/assets/bi_process.py new file mode 100644 index 000000000..2a4ac2e54 --- /dev/null +++ b/pyatlan_v9/model/assets/bi_process.py @@ -0,0 +1,630 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +BIProcess asset model with flattened inheritance. + +This module provides: +- BIProcess: Flat asset class (easy to use) +- BIProcessAttributes: Nested attributes struct (extends AssetAttributes) +- BIProcessNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .adf_related import RelatedAdfActivity +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .catalog_related import RelatedCatalog +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .fabric_related import RelatedFabricActivity +from .fivetran_related import RelatedFivetranConnector +from .flow_related import RelatedFlowControlOperation +from .gtc_related import RelatedAtlasGlossaryTerm +from .matillion_related import RelatedMatillionComponent +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .power_bi_related import RelatedPowerBIDataflow +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from .sql_related import RelatedFunction, RelatedProcedure +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .process_related import RelatedColumnProcess + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class BIProcess(Asset): + """ + Instance of business intelligence lineage in Atlan. These are used to short-circuit lineage from table-like assets directly to dashboard-like assets. + """ + + CODE: ClassVar[Any] = None + SQL: ClassVar[Any] = None + PARENT_CONNECTION_PROCESS_QUALIFIED_NAME: ClassVar[Any] = None + AST: ClassVar[Any] = None + ADDITIONAL_ETL_CONTEXT: ClassVar[Any] = None + AI_DATASET_TYPE: ClassVar[Any] = None + ADF_ACTIVITY: ClassVar[Any] = None + AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + FABRIC_ACTIVITIES: ClassVar[Any] = None + FIVETRAN_CONNECTOR: ClassVar[Any] = None + FLOW_ORCHESTRATED_BY: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MATILLION_COMPONENT: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + POWER_BI_DATAFLOW: ClassVar[Any] = None + INPUTS: ClassVar[Any] = None + OUTPUTS: ClassVar[Any] = None + COLUMN_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SQL_PROCEDURES: ClassVar[Any] = None + SQL_FUNCTIONS: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "BIProcess" + + code: Union[str, None, UnsetType] = UNSET + """Code that ran within the process.""" + + sql: Union[str, None, UnsetType] = UNSET + """SQL query that ran to produce the outputs.""" + + parent_connection_process_qualified_name: Union[List[str], None, UnsetType] = UNSET + """""" + + ast: Union[str, None, UnsetType] = UNSET + """Parsed AST of the code or SQL statements that describe the logic of this process.""" + + additional_etl_context: Union[str, None, UnsetType] = UNSET + """Additional Context of the ETL pipeline/notebook which creates the process.""" + + ai_dataset_type: Union[str, None, UnsetType] = UNSET + """Dataset type for AI Model - dataset process.""" + + adf_activity: Union[RelatedAdfActivity, None, UnsetType] = UNSET + """ADF Activity that is associated with this lineage process.""" + + airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks that exist within this process.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + fabric_activities: Union[List[RelatedFabricActivity], None, UnsetType] = UNSET + """Individual Fabric activities contained in the process.""" + + fivetran_connector: Union[RelatedFivetranConnector, None, UnsetType] = UNSET + """fivetranConnector in which this process exists.""" + + flow_orchestrated_by: Union[RelatedFlowControlOperation, None, UnsetType] = UNSET + """Orchestrated control operation that ran these data flows (process).""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + matillion_component: Union[RelatedMatillionComponent, None, UnsetType] = UNSET + """Matillion component that contains the logic for this lineage process.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + power_bi_dataflow: Union[RelatedPowerBIDataflow, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIDataflow" + ) + """PowerBI Dataflow that is associated with this lineage process.""" + + inputs: Union[List[RelatedCatalog], None, UnsetType] = UNSET + """Assets that are inputs to this process.""" + + outputs: Union[List[RelatedCatalog], None, UnsetType] = UNSET + """Assets that are outputs from this process.""" + + column_processes: Union[List[RelatedColumnProcess], None, UnsetType] = UNSET + """Processes that detail column-level lineage for this process.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + sql_procedures: Union[List[RelatedProcedure], None, UnsetType] = UNSET + """Procedures used by this process.""" + + sql_functions: Union[List[RelatedFunction], None, UnsetType] = UNSET + """Functions used by this process.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "BIProcess" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _bi_process_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> BIProcess: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + BIProcess instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _bi_process_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class BIProcessAttributes(AssetAttributes): + """BIProcess-specific attributes for nested API format.""" + + code: Union[str, None, UnsetType] = UNSET + """Code that ran within the process.""" + + sql: Union[str, None, UnsetType] = UNSET + """SQL query that ran to produce the outputs.""" + + parent_connection_process_qualified_name: Union[List[str], None, UnsetType] = UNSET + """""" + + ast: Union[str, None, UnsetType] = UNSET + """Parsed AST of the code or SQL statements that describe the logic of this process.""" + + additional_etl_context: Union[str, None, UnsetType] = UNSET + """Additional Context of the ETL pipeline/notebook which creates the process.""" + + ai_dataset_type: Union[str, None, UnsetType] = UNSET + """Dataset type for AI Model - dataset process.""" + + +class BIProcessRelationshipAttributes(AssetRelationshipAttributes): + """BIProcess-specific relationship attributes for nested API format.""" + + adf_activity: Union[RelatedAdfActivity, None, UnsetType] = UNSET + """ADF Activity that is associated with this lineage process.""" + + airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks that exist within this process.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + fabric_activities: Union[List[RelatedFabricActivity], None, UnsetType] = UNSET + """Individual Fabric activities contained in the process.""" + + fivetran_connector: Union[RelatedFivetranConnector, None, UnsetType] = UNSET + """fivetranConnector in which this process exists.""" + + flow_orchestrated_by: Union[RelatedFlowControlOperation, None, UnsetType] = UNSET + """Orchestrated control operation that ran these data flows (process).""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + matillion_component: Union[RelatedMatillionComponent, None, UnsetType] = UNSET + """Matillion component that contains the logic for this lineage process.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + power_bi_dataflow: Union[RelatedPowerBIDataflow, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIDataflow" + ) + """PowerBI Dataflow that is associated with this lineage process.""" + + inputs: Union[List[RelatedCatalog], None, UnsetType] = UNSET + """Assets that are inputs to this process.""" + + outputs: Union[List[RelatedCatalog], None, UnsetType] = UNSET + """Assets that are outputs from this process.""" + + column_processes: Union[List[RelatedColumnProcess], None, UnsetType] = UNSET + """Processes that detail column-level lineage for this process.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + sql_procedures: Union[List[RelatedProcedure], None, UnsetType] = UNSET + """Procedures used by this process.""" + + sql_functions: Union[List[RelatedFunction], None, UnsetType] = UNSET + """Functions used by this process.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class BIProcessNested(AssetNested): + """BIProcess in nested API format for high-performance serialization.""" + + attributes: Union[BIProcessAttributes, UnsetType] = UNSET + relationship_attributes: Union[BIProcessRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + BIProcessRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + BIProcessRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_BI_PROCESS_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "adf_activity", + "airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "fabric_activities", + "fivetran_connector", + "flow_orchestrated_by", + "meanings", + "matillion_component", + "mc_monitors", + "mc_incidents", + "power_bi_dataflow", + "inputs", + "outputs", + "column_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "sql_procedures", + "sql_functions", + "schema_registry_subjects", + "soda_checks", + "spark_jobs", +] + + +def _populate_bi_process_attrs(attrs: BIProcessAttributes, obj: BIProcess) -> None: + """Populate BIProcess-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.code = obj.code + attrs.sql = obj.sql + attrs.parent_connection_process_qualified_name = ( + obj.parent_connection_process_qualified_name + ) + attrs.ast = obj.ast + attrs.additional_etl_context = obj.additional_etl_context + attrs.ai_dataset_type = obj.ai_dataset_type + + +def _extract_bi_process_attrs(attrs: BIProcessAttributes) -> dict: + """Extract all BIProcess attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["code"] = attrs.code + result["sql"] = attrs.sql + result["parent_connection_process_qualified_name"] = ( + attrs.parent_connection_process_qualified_name + ) + result["ast"] = attrs.ast + result["additional_etl_context"] = attrs.additional_etl_context + result["ai_dataset_type"] = attrs.ai_dataset_type + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _bi_process_to_nested(bi_process: BIProcess) -> BIProcessNested: + """Convert flat BIProcess to nested format.""" + attrs = BIProcessAttributes() + _populate_bi_process_attrs(attrs, bi_process) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + bi_process, _BI_PROCESS_REL_FIELDS, BIProcessRelationshipAttributes + ) + return BIProcessNested( + guid=bi_process.guid, + type_name=bi_process.type_name, + status=bi_process.status, + version=bi_process.version, + create_time=bi_process.create_time, + update_time=bi_process.update_time, + created_by=bi_process.created_by, + updated_by=bi_process.updated_by, + classifications=bi_process.classifications, + classification_names=bi_process.classification_names, + meanings=bi_process.meanings, + labels=bi_process.labels, + business_attributes=bi_process.business_attributes, + custom_attributes=bi_process.custom_attributes, + pending_tasks=bi_process.pending_tasks, + proxy=bi_process.proxy, + is_incomplete=bi_process.is_incomplete, + provenance_type=bi_process.provenance_type, + home_id=bi_process.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _bi_process_from_nested(nested: BIProcessNested) -> BIProcess: + """Convert nested format to flat BIProcess.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else BIProcessAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _BI_PROCESS_REL_FIELDS, + BIProcessRelationshipAttributes, + ) + return BIProcess( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_bi_process_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _bi_process_to_nested_bytes(bi_process: BIProcess, serde: Serde) -> bytes: + """Convert flat BIProcess to nested JSON bytes.""" + return serde.encode(_bi_process_to_nested(bi_process)) + + +def _bi_process_from_nested_bytes(data: bytes, serde: Serde) -> BIProcess: + """Convert nested JSON bytes to flat BIProcess.""" + nested = serde.decode(data, BIProcessNested) + return _bi_process_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +BIProcess.CODE = KeywordField("code", "code") +BIProcess.SQL = KeywordField("sql", "sql") +BIProcess.PARENT_CONNECTION_PROCESS_QUALIFIED_NAME = KeywordField( + "parentConnectionProcessQualifiedName", "parentConnectionProcessQualifiedName" +) +BIProcess.AST = KeywordField("ast", "ast") +BIProcess.ADDITIONAL_ETL_CONTEXT = KeywordField( + "additionalEtlContext", "additionalEtlContext" +) +BIProcess.AI_DATASET_TYPE = KeywordField("aiDatasetType", "aiDatasetType") +BIProcess.ADF_ACTIVITY = RelationField("adfActivity") +BIProcess.AIRFLOW_TASKS = RelationField("airflowTasks") +BIProcess.ANOMALO_CHECKS = RelationField("anomaloChecks") +BIProcess.APPLICATION = RelationField("application") +BIProcess.APPLICATION_FIELD = RelationField("applicationField") +BIProcess.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +BIProcess.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +BIProcess.METRICS = RelationField("metrics") +BIProcess.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +BIProcess.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +BIProcess.FABRIC_ACTIVITIES = RelationField("fabricActivities") +BIProcess.FIVETRAN_CONNECTOR = RelationField("fivetranConnector") +BIProcess.FLOW_ORCHESTRATED_BY = RelationField("flowOrchestratedBy") +BIProcess.MEANINGS = RelationField("meanings") +BIProcess.MATILLION_COMPONENT = RelationField("matillionComponent") +BIProcess.MC_MONITORS = RelationField("mcMonitors") +BIProcess.MC_INCIDENTS = RelationField("mcIncidents") +BIProcess.POWER_BI_DATAFLOW = RelationField("powerBIDataflow") +BIProcess.INPUTS = RelationField("inputs") +BIProcess.OUTPUTS = RelationField("outputs") +BIProcess.COLUMN_PROCESSES = RelationField("columnProcesses") +BIProcess.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +BIProcess.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +BIProcess.FILES = RelationField("files") +BIProcess.LINKS = RelationField("links") +BIProcess.README = RelationField("readme") +BIProcess.SQL_PROCEDURES = RelationField("sqlProcedures") +BIProcess.SQL_FUNCTIONS = RelationField("sqlFunctions") +BIProcess.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +BIProcess.SODA_CHECKS = RelationField("sodaChecks") +BIProcess.SPARK_JOBS = RelationField("sparkJobs") diff --git a/pyatlan_v9/model/assets/bigquery_related.py b/pyatlan_v9/model/assets/bigquery_related.py new file mode 100644 index 000000000..c140f1815 --- /dev/null +++ b/pyatlan_v9/model/assets/bigquery_related.py @@ -0,0 +1,79 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Bigquery module. + +This module contains all Related{Type} classes for the Bigquery type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .referenceable_related import RelatedReferenceable +from .sql_related import RelatedProcedure +from .tag_related import RelatedTag + +__all__ = [ + "RelatedBigqueryTag", + "RelatedBigqueryRoutine", +] + + +class RelatedBigqueryTag(RelatedTag): + """ + Related entity reference for BigqueryTag assets. + + Extends RelatedTag with BigqueryTag-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "BigqueryTag" so it serializes correctly + + bigquery_tag_type: Union[str, None, UnsetType] = UNSET + """The specific type or category of the Bigquery tag, which can be used for classification and organization of Bigquery assets.""" + + bigquery_tag_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of top-level upstream nested bigquery tags.""" + + bigquery_tag_taxonomy_properties: Union[Dict[str, str], None, UnsetType] = UNSET + """Properties of the bigquery tag taxonomy attribute.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "BigqueryTag" + + +class RelatedBigqueryRoutine(RelatedProcedure): + """ + Related entity reference for BigqueryRoutine assets. + + Extends RelatedProcedure with BigqueryRoutine-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "BigqueryRoutine" so it serializes correctly + + bigquery_type: Union[str, None, UnsetType] = UNSET + """Type of bigquery routine (sp, udf, or tvf).""" + + bigquery_arguments: Union[List[str], None, UnsetType] = UNSET + """Arguments that are passed in to the routine.""" + + bigquery_return_type: Union[str, None, UnsetType] = UNSET + """Return data type of the bigquery routine (null for stored procedures).""" + + bigquery_security_type: Union[str, None, UnsetType] = UNSET + """Security type of the routine, always null.""" + + bigquery_ddl: Union[str, None, UnsetType] = UNSET + """The ddl statement used to create the bigquery routine.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "BigqueryRoutine" diff --git a/pyatlan_v9/model/assets/bigquery_routine.py b/pyatlan_v9/model/assets/bigquery_routine.py new file mode 100644 index 000000000..babda541b --- /dev/null +++ b/pyatlan_v9/model/assets/bigquery_routine.py @@ -0,0 +1,1036 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +BigqueryRoutine asset model with flattened inheritance. + +This module provides: +- BigqueryRoutine: Flat asset class (easy to use) +- BigqueryRoutineAttributes: Nested attributes struct (extends AssetAttributes) +- BigqueryRoutineNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .snowflake_related import RelatedSnowflakeSemanticLogicalTable +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from .sql_related import RelatedSchema +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class BigqueryRoutine(Asset): + """ + Instance of a bigquery routine in atlan. Can be a stored procedure, udf, or tvf. + """ + + BIGQUERY_TYPE: ClassVar[Any] = None + BIGQUERY_ARGUMENTS: ClassVar[Any] = None + BIGQUERY_RETURN_TYPE: ClassVar[Any] = None + BIGQUERY_SECURITY_TYPE: ClassVar[Any] = None + BIGQUERY_DDL: ClassVar[Any] = None + DEFINITION: ClassVar[Any] = None + SQL_LANGUAGE: ClassVar[Any] = None + SQL_RUNTIME_VERSION: ClassVar[Any] = None + SQL_OWNER_ROLE_TYPE: ClassVar[Any] = None + SQL_ARGUMENTS: ClassVar[Any] = None + SQL_PROCEDURE_RETURN: ClassVar[Any] = None + SQL_EXTERNAL_ACCESS_INTEGRATIONS: ClassVar[Any] = None + SQL_SECRETS: ClassVar[Any] = None + SQL_PACKAGES: ClassVar[Any] = None + SQL_INSTALLED_PACKAGES: ClassVar[Any] = None + SQL_SCHEMA_ID: ClassVar[Any] = None + SQL_CATALOG_ID: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + ATLAN_SCHEMA: ClassVar[Any] = None + SQL_PROCESSES: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "BigqueryRoutine" + + bigquery_type: Union[str, None, UnsetType] = UNSET + """Type of bigquery routine (sp, udf, or tvf).""" + + bigquery_arguments: Union[List[str], None, UnsetType] = UNSET + """Arguments that are passed in to the routine.""" + + bigquery_return_type: Union[str, None, UnsetType] = UNSET + """Return data type of the bigquery routine (null for stored procedures).""" + + bigquery_security_type: Union[str, None, UnsetType] = UNSET + """Security type of the routine, always null.""" + + bigquery_ddl: Union[str, None, UnsetType] = UNSET + """The ddl statement used to create the bigquery routine.""" + + definition: Union[str, None, UnsetType] = UNSET + """SQL definition of the procedure.""" + + sql_language: Union[str, None, UnsetType] = UNSET + """Programming language used for the procedure (e.g., SQL, JavaScript, Python, Scala).""" + + sql_runtime_version: Union[str, None, UnsetType] = UNSET + """Version of the language runtime used by the procedure.""" + + sql_owner_role_type: Union[str, None, UnsetType] = UNSET + """Type of role that owns the procedure.""" + + sql_arguments: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of procedure arguments with name and type information.""" + + sql_procedure_return: Union[Dict[str, Any], None, UnsetType] = UNSET + """Detailed information about the procedure's return type.""" + + sql_external_access_integrations: Union[str, None, UnsetType] = UNSET + """Names of external access integrations used by the procedure.""" + + sql_secrets: Union[str, None, UnsetType] = UNSET + """Secret variables used by the procedure.""" + + sql_packages: Union[str, None, UnsetType] = UNSET + """Packages requested by the procedure.""" + + sql_installed_packages: Union[str, None, UnsetType] = UNSET + """Packages actually installed for the procedure.""" + + sql_schema_id: Union[str, None, UnsetType] = UNSET + """Internal ID for the schema containing the procedure.""" + + sql_catalog_id: Union[str, None, UnsetType] = UNSET + """Internal ID for the database containing the procedure.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + atlan_schema: Union[RelatedSchema, None, UnsetType] = UNSET + """Schema in which this stored procedure exists.""" + + sql_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes that utilize this procedure.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "BigqueryRoutine" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _bigquery_routine_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> BigqueryRoutine: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + BigqueryRoutine instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _bigquery_routine_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class BigqueryRoutineAttributes(AssetAttributes): + """BigqueryRoutine-specific attributes for nested API format.""" + + bigquery_type: Union[str, None, UnsetType] = UNSET + """Type of bigquery routine (sp, udf, or tvf).""" + + bigquery_arguments: Union[List[str], None, UnsetType] = UNSET + """Arguments that are passed in to the routine.""" + + bigquery_return_type: Union[str, None, UnsetType] = UNSET + """Return data type of the bigquery routine (null for stored procedures).""" + + bigquery_security_type: Union[str, None, UnsetType] = UNSET + """Security type of the routine, always null.""" + + bigquery_ddl: Union[str, None, UnsetType] = UNSET + """The ddl statement used to create the bigquery routine.""" + + definition: Union[str, None, UnsetType] = UNSET + """SQL definition of the procedure.""" + + sql_language: Union[str, None, UnsetType] = UNSET + """Programming language used for the procedure (e.g., SQL, JavaScript, Python, Scala).""" + + sql_runtime_version: Union[str, None, UnsetType] = UNSET + """Version of the language runtime used by the procedure.""" + + sql_owner_role_type: Union[str, None, UnsetType] = UNSET + """Type of role that owns the procedure.""" + + sql_arguments: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of procedure arguments with name and type information.""" + + sql_procedure_return: Union[Dict[str, Any], None, UnsetType] = UNSET + """Detailed information about the procedure's return type.""" + + sql_external_access_integrations: Union[str, None, UnsetType] = UNSET + """Names of external access integrations used by the procedure.""" + + sql_secrets: Union[str, None, UnsetType] = UNSET + """Secret variables used by the procedure.""" + + sql_packages: Union[str, None, UnsetType] = UNSET + """Packages requested by the procedure.""" + + sql_installed_packages: Union[str, None, UnsetType] = UNSET + """Packages actually installed for the procedure.""" + + sql_schema_id: Union[str, None, UnsetType] = UNSET + """Internal ID for the schema containing the procedure.""" + + sql_catalog_id: Union[str, None, UnsetType] = UNSET + """Internal ID for the database containing the procedure.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + +class BigqueryRoutineRelationshipAttributes(AssetRelationshipAttributes): + """BigqueryRoutine-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + atlan_schema: Union[RelatedSchema, None, UnsetType] = UNSET + """Schema in which this stored procedure exists.""" + + sql_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes that utilize this procedure.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class BigqueryRoutineNested(AssetNested): + """BigqueryRoutine in nested API format for high-performance serialization.""" + + attributes: Union[BigqueryRoutineAttributes, UnsetType] = UNSET + relationship_attributes: Union[BigqueryRoutineRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + BigqueryRoutineRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + BigqueryRoutineRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_BIGQUERY_ROUTINE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "atlan_schema", + "sql_processes", + "schema_registry_subjects", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_bigquery_routine_attrs( + attrs: BigqueryRoutineAttributes, obj: BigqueryRoutine +) -> None: + """Populate BigqueryRoutine-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.bigquery_type = obj.bigquery_type + attrs.bigquery_arguments = obj.bigquery_arguments + attrs.bigquery_return_type = obj.bigquery_return_type + attrs.bigquery_security_type = obj.bigquery_security_type + attrs.bigquery_ddl = obj.bigquery_ddl + attrs.definition = obj.definition + attrs.sql_language = obj.sql_language + attrs.sql_runtime_version = obj.sql_runtime_version + attrs.sql_owner_role_type = obj.sql_owner_role_type + attrs.sql_arguments = obj.sql_arguments + attrs.sql_procedure_return = obj.sql_procedure_return + attrs.sql_external_access_integrations = obj.sql_external_access_integrations + attrs.sql_secrets = obj.sql_secrets + attrs.sql_packages = obj.sql_packages + attrs.sql_installed_packages = obj.sql_installed_packages + attrs.sql_schema_id = obj.sql_schema_id + attrs.sql_catalog_id = obj.sql_catalog_id + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + + +def _extract_bigquery_routine_attrs(attrs: BigqueryRoutineAttributes) -> dict: + """Extract all BigqueryRoutine attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["bigquery_type"] = attrs.bigquery_type + result["bigquery_arguments"] = attrs.bigquery_arguments + result["bigquery_return_type"] = attrs.bigquery_return_type + result["bigquery_security_type"] = attrs.bigquery_security_type + result["bigquery_ddl"] = attrs.bigquery_ddl + result["definition"] = attrs.definition + result["sql_language"] = attrs.sql_language + result["sql_runtime_version"] = attrs.sql_runtime_version + result["sql_owner_role_type"] = attrs.sql_owner_role_type + result["sql_arguments"] = attrs.sql_arguments + result["sql_procedure_return"] = attrs.sql_procedure_return + result["sql_external_access_integrations"] = attrs.sql_external_access_integrations + result["sql_secrets"] = attrs.sql_secrets + result["sql_packages"] = attrs.sql_packages + result["sql_installed_packages"] = attrs.sql_installed_packages + result["sql_schema_id"] = attrs.sql_schema_id + result["sql_catalog_id"] = attrs.sql_catalog_id + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _bigquery_routine_to_nested( + bigquery_routine: BigqueryRoutine, +) -> BigqueryRoutineNested: + """Convert flat BigqueryRoutine to nested format.""" + attrs = BigqueryRoutineAttributes() + _populate_bigquery_routine_attrs(attrs, bigquery_routine) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + bigquery_routine, + _BIGQUERY_ROUTINE_REL_FIELDS, + BigqueryRoutineRelationshipAttributes, + ) + return BigqueryRoutineNested( + guid=bigquery_routine.guid, + type_name=bigquery_routine.type_name, + status=bigquery_routine.status, + version=bigquery_routine.version, + create_time=bigquery_routine.create_time, + update_time=bigquery_routine.update_time, + created_by=bigquery_routine.created_by, + updated_by=bigquery_routine.updated_by, + classifications=bigquery_routine.classifications, + classification_names=bigquery_routine.classification_names, + meanings=bigquery_routine.meanings, + labels=bigquery_routine.labels, + business_attributes=bigquery_routine.business_attributes, + custom_attributes=bigquery_routine.custom_attributes, + pending_tasks=bigquery_routine.pending_tasks, + proxy=bigquery_routine.proxy, + is_incomplete=bigquery_routine.is_incomplete, + provenance_type=bigquery_routine.provenance_type, + home_id=bigquery_routine.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _bigquery_routine_from_nested(nested: BigqueryRoutineNested) -> BigqueryRoutine: + """Convert nested format to flat BigqueryRoutine.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else BigqueryRoutineAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _BIGQUERY_ROUTINE_REL_FIELDS, + BigqueryRoutineRelationshipAttributes, + ) + return BigqueryRoutine( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_bigquery_routine_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _bigquery_routine_to_nested_bytes( + bigquery_routine: BigqueryRoutine, serde: Serde +) -> bytes: + """Convert flat BigqueryRoutine to nested JSON bytes.""" + return serde.encode(_bigquery_routine_to_nested(bigquery_routine)) + + +def _bigquery_routine_from_nested_bytes(data: bytes, serde: Serde) -> BigqueryRoutine: + """Convert nested JSON bytes to flat BigqueryRoutine.""" + nested = serde.decode(data, BigqueryRoutineNested) + return _bigquery_routine_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +BigqueryRoutine.BIGQUERY_TYPE = KeywordField("bigqueryType", "bigqueryType") +BigqueryRoutine.BIGQUERY_ARGUMENTS = KeywordField( + "bigqueryArguments", "bigqueryArguments" +) +BigqueryRoutine.BIGQUERY_RETURN_TYPE = KeywordField( + "bigqueryReturnType", "bigqueryReturnType" +) +BigqueryRoutine.BIGQUERY_SECURITY_TYPE = KeywordField( + "bigquerySecurityType", "bigquerySecurityType" +) +BigqueryRoutine.BIGQUERY_DDL = KeywordField("bigqueryDdl", "bigqueryDdl") +BigqueryRoutine.DEFINITION = KeywordField("definition", "definition") +BigqueryRoutine.SQL_LANGUAGE = KeywordTextField( + "sqlLanguage", "sqlLanguage", "sqlLanguage.text" +) +BigqueryRoutine.SQL_RUNTIME_VERSION = KeywordTextField( + "sqlRuntimeVersion", "sqlRuntimeVersion", "sqlRuntimeVersion.text" +) +BigqueryRoutine.SQL_OWNER_ROLE_TYPE = KeywordTextField( + "sqlOwnerRoleType", "sqlOwnerRoleType", "sqlOwnerRoleType.text" +) +BigqueryRoutine.SQL_ARGUMENTS = KeywordField("sqlArguments", "sqlArguments") +BigqueryRoutine.SQL_PROCEDURE_RETURN = KeywordField( + "sqlProcedureReturn", "sqlProcedureReturn" +) +BigqueryRoutine.SQL_EXTERNAL_ACCESS_INTEGRATIONS = KeywordField( + "sqlExternalAccessIntegrations", "sqlExternalAccessIntegrations" +) +BigqueryRoutine.SQL_SECRETS = KeywordField("sqlSecrets", "sqlSecrets") +BigqueryRoutine.SQL_PACKAGES = KeywordField("sqlPackages", "sqlPackages") +BigqueryRoutine.SQL_INSTALLED_PACKAGES = KeywordField( + "sqlInstalledPackages", "sqlInstalledPackages" +) +BigqueryRoutine.SQL_SCHEMA_ID = KeywordField("sqlSchemaId", "sqlSchemaId") +BigqueryRoutine.SQL_CATALOG_ID = KeywordField("sqlCatalogId", "sqlCatalogId") +BigqueryRoutine.QUERY_COUNT = NumericField("queryCount", "queryCount") +BigqueryRoutine.QUERY_USER_COUNT = NumericField("queryUserCount", "queryUserCount") +BigqueryRoutine.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +BigqueryRoutine.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +BigqueryRoutine.DATABASE_NAME = KeywordField("databaseName", "databaseName") +BigqueryRoutine.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +BigqueryRoutine.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +BigqueryRoutine.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +BigqueryRoutine.TABLE_NAME = KeywordField("tableName", "tableName") +BigqueryRoutine.TABLE_QUALIFIED_NAME = KeywordField( + "tableQualifiedName", "tableQualifiedName" +) +BigqueryRoutine.VIEW_NAME = KeywordField("viewName", "viewName") +BigqueryRoutine.VIEW_QUALIFIED_NAME = KeywordField( + "viewQualifiedName", "viewQualifiedName" +) +BigqueryRoutine.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +BigqueryRoutine.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +BigqueryRoutine.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +BigqueryRoutine.LAST_PROFILED_AT = NumericField("lastProfiledAt", "lastProfiledAt") +BigqueryRoutine.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +BigqueryRoutine.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +BigqueryRoutine.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +BigqueryRoutine.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +BigqueryRoutine.ANOMALO_CHECKS = RelationField("anomaloChecks") +BigqueryRoutine.APPLICATION = RelationField("application") +BigqueryRoutine.APPLICATION_FIELD = RelationField("applicationField") +BigqueryRoutine.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +BigqueryRoutine.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +BigqueryRoutine.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +BigqueryRoutine.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +BigqueryRoutine.METRICS = RelationField("metrics") +BigqueryRoutine.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +BigqueryRoutine.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +BigqueryRoutine.DBT_MODELS = RelationField("dbtModels") +BigqueryRoutine.SQL_DBT_MODELS = RelationField("sqlDbtModels") +BigqueryRoutine.DBT_TESTS = RelationField("dbtTests") +BigqueryRoutine.DBT_SOURCES = RelationField("dbtSources") +BigqueryRoutine.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +BigqueryRoutine.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +BigqueryRoutine.MEANINGS = RelationField("meanings") +BigqueryRoutine.MC_MONITORS = RelationField("mcMonitors") +BigqueryRoutine.MC_INCIDENTS = RelationField("mcIncidents") +BigqueryRoutine.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +BigqueryRoutine.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +BigqueryRoutine.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +BigqueryRoutine.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +BigqueryRoutine.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +BigqueryRoutine.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +BigqueryRoutine.FILES = RelationField("files") +BigqueryRoutine.LINKS = RelationField("links") +BigqueryRoutine.README = RelationField("readme") +BigqueryRoutine.ATLAN_SCHEMA = RelationField("atlanSchema") +BigqueryRoutine.SQL_PROCESSES = RelationField("sqlProcesses") +BigqueryRoutine.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +BigqueryRoutine.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +BigqueryRoutine.SODA_CHECKS = RelationField("sodaChecks") +BigqueryRoutine.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +BigqueryRoutine.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/business_policy.py b/pyatlan_v9/model/assets/business_policy.py new file mode 100644 index 000000000..68626bc38 --- /dev/null +++ b/pyatlan_v9/model/assets/business_policy.py @@ -0,0 +1,605 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +BusinessPolicy asset model with flattened inheritance. + +This module provides: +- BusinessPolicy: Flat asset class (easy to use) +- BusinessPolicyAttributes: Nested attributes struct (extends AssetAttributes) +- BusinessPolicyNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .business_policy_related import ( + RelatedBusinessPolicy, + RelatedBusinessPolicyException, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class BusinessPolicy(Asset): + """ + Instance of a business policy template in Atlan. + """ + + BUSINESS_POLICY_TYPE: ClassVar[Any] = None + BUSINESS_POLICY_LONG_DESCRIPTION: ClassVar[Any] = None + BUSINESS_POLICY_VALID_TILL: ClassVar[Any] = None + BUSINESS_POLICY_VALID_FROM: ClassVar[Any] = None + BUSINESS_POLICY_VERSION: ClassVar[Any] = None + BUSINESS_POLICY_REVIEW_PERIOD: ClassVar[Any] = None + BUSINESS_POLICY_FILTER_DSL: ClassVar[Any] = None + BUSINESS_POLICY_BASE_PARENT_GUID: ClassVar[Any] = None + BUSINESS_POLICY_SELECTED_APPROVAL_WF: ClassVar[Any] = None + BUSINESS_POLICY_RULES: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + RELATED_BUSINESS_POLICIES: ClassVar[Any] = None + EXCEPTIONS_FOR_BUSINESS_POLICY: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "BusinessPolicy" + + business_policy_type: Union[str, None, UnsetType] = UNSET + """Type of business policy""" + + business_policy_long_description: Union[str, None, UnsetType] = UNSET + """Body of the business policy, a long readme like document""" + + business_policy_valid_till: Union[int, None, UnsetType] = UNSET + """Validity end date of the policy""" + + business_policy_valid_from: Union[int, None, UnsetType] = UNSET + """Validity start date of the policy""" + + business_policy_version: Union[int, None, UnsetType] = UNSET + """Version of the policy""" + + business_policy_review_period: Union[str, None, UnsetType] = UNSET + """Duration for the business policy to complete review.""" + + business_policy_filter_dsl: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="businessPolicyFilterDSL" + ) + """Business Policy Filter ES DSL to denote the associate asset/s involved.""" + + business_policy_base_parent_guid: Union[str, None, UnsetType] = UNSET + """Base parent Guid for policy used in version""" + + business_policy_selected_approval_wf: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="businessPolicySelectedApprovalWF" + ) + """Selected approval workflow id for business policy""" + + business_policy_rules: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of rules applied to this business policy.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + related_business_policies: Union[List[RelatedBusinessPolicy], None, UnsetType] = ( + UNSET + ) + """BusinessPolicy that have the same (or relatable) compliance""" + + exceptions_for_business_policy: Union[ + List[RelatedBusinessPolicyException], None, UnsetType + ] = UNSET + """Exception assigned to business polices""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "BusinessPolicy" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _business_policy_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> BusinessPolicy: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + BusinessPolicy instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _business_policy_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class BusinessPolicyAttributes(AssetAttributes): + """BusinessPolicy-specific attributes for nested API format.""" + + business_policy_type: Union[str, None, UnsetType] = UNSET + """Type of business policy""" + + business_policy_long_description: Union[str, None, UnsetType] = UNSET + """Body of the business policy, a long readme like document""" + + business_policy_valid_till: Union[int, None, UnsetType] = UNSET + """Validity end date of the policy""" + + business_policy_valid_from: Union[int, None, UnsetType] = UNSET + """Validity start date of the policy""" + + business_policy_version: Union[int, None, UnsetType] = UNSET + """Version of the policy""" + + business_policy_review_period: Union[str, None, UnsetType] = UNSET + """Duration for the business policy to complete review.""" + + business_policy_filter_dsl: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="businessPolicyFilterDSL" + ) + """Business Policy Filter ES DSL to denote the associate asset/s involved.""" + + business_policy_base_parent_guid: Union[str, None, UnsetType] = UNSET + """Base parent Guid for policy used in version""" + + business_policy_selected_approval_wf: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="businessPolicySelectedApprovalWF" + ) + """Selected approval workflow id for business policy""" + + business_policy_rules: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of rules applied to this business policy.""" + + +class BusinessPolicyRelationshipAttributes(AssetRelationshipAttributes): + """BusinessPolicy-specific relationship attributes for nested API format.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + related_business_policies: Union[List[RelatedBusinessPolicy], None, UnsetType] = ( + UNSET + ) + """BusinessPolicy that have the same (or relatable) compliance""" + + exceptions_for_business_policy: Union[ + List[RelatedBusinessPolicyException], None, UnsetType + ] = UNSET + """Exception assigned to business polices""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + +class BusinessPolicyNested(AssetNested): + """BusinessPolicy in nested API format for high-performance serialization.""" + + attributes: Union[BusinessPolicyAttributes, UnsetType] = UNSET + relationship_attributes: Union[BusinessPolicyRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + BusinessPolicyRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + BusinessPolicyRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_BUSINESS_POLICY_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "anomalo_checks", + "application", + "application_field", + "related_business_policies", + "exceptions_for_business_policy", + "output_port_data_products", + "input_port_data_products", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", +] + + +def _populate_business_policy_attrs( + attrs: BusinessPolicyAttributes, obj: BusinessPolicy +) -> None: + """Populate BusinessPolicy-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.business_policy_type = obj.business_policy_type + attrs.business_policy_long_description = obj.business_policy_long_description + attrs.business_policy_valid_till = obj.business_policy_valid_till + attrs.business_policy_valid_from = obj.business_policy_valid_from + attrs.business_policy_version = obj.business_policy_version + attrs.business_policy_review_period = obj.business_policy_review_period + attrs.business_policy_filter_dsl = obj.business_policy_filter_dsl + attrs.business_policy_base_parent_guid = obj.business_policy_base_parent_guid + attrs.business_policy_selected_approval_wf = ( + obj.business_policy_selected_approval_wf + ) + attrs.business_policy_rules = obj.business_policy_rules + + +def _extract_business_policy_attrs(attrs: BusinessPolicyAttributes) -> dict: + """Extract all BusinessPolicy attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["business_policy_type"] = attrs.business_policy_type + result["business_policy_long_description"] = attrs.business_policy_long_description + result["business_policy_valid_till"] = attrs.business_policy_valid_till + result["business_policy_valid_from"] = attrs.business_policy_valid_from + result["business_policy_version"] = attrs.business_policy_version + result["business_policy_review_period"] = attrs.business_policy_review_period + result["business_policy_filter_dsl"] = attrs.business_policy_filter_dsl + result["business_policy_base_parent_guid"] = attrs.business_policy_base_parent_guid + result["business_policy_selected_approval_wf"] = ( + attrs.business_policy_selected_approval_wf + ) + result["business_policy_rules"] = attrs.business_policy_rules + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _business_policy_to_nested(business_policy: BusinessPolicy) -> BusinessPolicyNested: + """Convert flat BusinessPolicy to nested format.""" + attrs = BusinessPolicyAttributes() + _populate_business_policy_attrs(attrs, business_policy) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + business_policy, + _BUSINESS_POLICY_REL_FIELDS, + BusinessPolicyRelationshipAttributes, + ) + return BusinessPolicyNested( + guid=business_policy.guid, + type_name=business_policy.type_name, + status=business_policy.status, + version=business_policy.version, + create_time=business_policy.create_time, + update_time=business_policy.update_time, + created_by=business_policy.created_by, + updated_by=business_policy.updated_by, + classifications=business_policy.classifications, + classification_names=business_policy.classification_names, + meanings=business_policy.meanings, + labels=business_policy.labels, + business_attributes=business_policy.business_attributes, + custom_attributes=business_policy.custom_attributes, + pending_tasks=business_policy.pending_tasks, + proxy=business_policy.proxy, + is_incomplete=business_policy.is_incomplete, + provenance_type=business_policy.provenance_type, + home_id=business_policy.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _business_policy_from_nested(nested: BusinessPolicyNested) -> BusinessPolicy: + """Convert nested format to flat BusinessPolicy.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else BusinessPolicyAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _BUSINESS_POLICY_REL_FIELDS, + BusinessPolicyRelationshipAttributes, + ) + return BusinessPolicy( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_business_policy_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _business_policy_to_nested_bytes( + business_policy: BusinessPolicy, serde: Serde +) -> bytes: + """Convert flat BusinessPolicy to nested JSON bytes.""" + return serde.encode(_business_policy_to_nested(business_policy)) + + +def _business_policy_from_nested_bytes(data: bytes, serde: Serde) -> BusinessPolicy: + """Convert nested JSON bytes to flat BusinessPolicy.""" + nested = serde.decode(data, BusinessPolicyNested) + return _business_policy_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +BusinessPolicy.BUSINESS_POLICY_TYPE = KeywordField( + "businessPolicyType", "businessPolicyType" +) +BusinessPolicy.BUSINESS_POLICY_LONG_DESCRIPTION = KeywordField( + "businessPolicyLongDescription", "businessPolicyLongDescription" +) +BusinessPolicy.BUSINESS_POLICY_VALID_TILL = NumericField( + "businessPolicyValidTill", "businessPolicyValidTill" +) +BusinessPolicy.BUSINESS_POLICY_VALID_FROM = NumericField( + "businessPolicyValidFrom", "businessPolicyValidFrom" +) +BusinessPolicy.BUSINESS_POLICY_VERSION = NumericField( + "businessPolicyVersion", "businessPolicyVersion" +) +BusinessPolicy.BUSINESS_POLICY_REVIEW_PERIOD = KeywordField( + "businessPolicyReviewPeriod", "businessPolicyReviewPeriod" +) +BusinessPolicy.BUSINESS_POLICY_FILTER_DSL = KeywordField( + "businessPolicyFilterDSL", "businessPolicyFilterDSL" +) +BusinessPolicy.BUSINESS_POLICY_BASE_PARENT_GUID = KeywordField( + "businessPolicyBaseParentGuid", "businessPolicyBaseParentGuid" +) +BusinessPolicy.BUSINESS_POLICY_SELECTED_APPROVAL_WF = KeywordField( + "businessPolicySelectedApprovalWF", "businessPolicySelectedApprovalWF" +) +BusinessPolicy.BUSINESS_POLICY_RULES = KeywordField( + "businessPolicyRules", "businessPolicyRules" +) +BusinessPolicy.ANOMALO_CHECKS = RelationField("anomaloChecks") +BusinessPolicy.APPLICATION = RelationField("application") +BusinessPolicy.APPLICATION_FIELD = RelationField("applicationField") +BusinessPolicy.RELATED_BUSINESS_POLICIES = RelationField("relatedBusinessPolicies") +BusinessPolicy.EXCEPTIONS_FOR_BUSINESS_POLICY = RelationField( + "exceptionsForBusinessPolicy" +) +BusinessPolicy.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +BusinessPolicy.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +BusinessPolicy.METRICS = RelationField("metrics") +BusinessPolicy.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +BusinessPolicy.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +BusinessPolicy.MEANINGS = RelationField("meanings") +BusinessPolicy.MC_MONITORS = RelationField("mcMonitors") +BusinessPolicy.MC_INCIDENTS = RelationField("mcIncidents") +BusinessPolicy.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +BusinessPolicy.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +BusinessPolicy.FILES = RelationField("files") +BusinessPolicy.LINKS = RelationField("links") +BusinessPolicy.README = RelationField("readme") +BusinessPolicy.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +BusinessPolicy.SODA_CHECKS = RelationField("sodaChecks") diff --git a/pyatlan_v9/model/assets/business_policy_related.py b/pyatlan_v9/model/assets/business_policy_related.py new file mode 100644 index 000000000..843d9e3b0 --- /dev/null +++ b/pyatlan_v9/model/assets/business_policy_related.py @@ -0,0 +1,166 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for BusinessPolicy module. + +This module contains all Related{Type} classes for the BusinessPolicy type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Any, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .asset_related import RelatedAsset +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedBusinessPolicy", + "RelatedBusinessPolicyException", + "RelatedBusinessPolicyIncident", + "RelatedBusinessPolicyLog", +] + + +class RelatedBusinessPolicy(RelatedAsset): + """ + Related entity reference for BusinessPolicy assets. + + Extends RelatedAsset with BusinessPolicy-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "BusinessPolicy" so it serializes correctly + + business_policy_type: Union[str, None, UnsetType] = UNSET + """Type of business policy""" + + business_policy_long_description: Union[str, None, UnsetType] = UNSET + """Body of the business policy, a long readme like document""" + + business_policy_valid_till: Union[int, None, UnsetType] = UNSET + """Validity end date of the policy""" + + business_policy_valid_from: Union[int, None, UnsetType] = UNSET + """Validity start date of the policy""" + + business_policy_version: Union[int, None, UnsetType] = UNSET + """Version of the policy""" + + business_policy_review_period: Union[str, None, UnsetType] = UNSET + """Duration for the business policy to complete review.""" + + business_policy_filter_dsl: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="businessPolicyFilterDSL" + ) + """Business Policy Filter ES DSL to denote the associate asset/s involved.""" + + business_policy_base_parent_guid: Union[str, None, UnsetType] = UNSET + """Base parent Guid for policy used in version""" + + business_policy_selected_approval_wf: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="businessPolicySelectedApprovalWF" + ) + """Selected approval workflow id for business policy""" + + business_policy_rules: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of rules applied to this business policy.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "BusinessPolicy" + + +class RelatedBusinessPolicyException(RelatedBusinessPolicy): + """ + Related entity reference for BusinessPolicyException assets. + + Extends RelatedBusinessPolicy with BusinessPolicyException-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "BusinessPolicyException" so it serializes correctly + + business_policy_exception_users: Union[List[str], None, UnsetType] = UNSET + """List of users who are part of this exception""" + + business_policy_exception_groups: Union[List[str], None, UnsetType] = UNSET + """List of groups who are part of this exception""" + + business_policy_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the business policy through which this asset is accessible.""" + + business_policy_exception_filter_dsl: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="businessPolicyExceptionFilterDSL" + ) + """Business Policy Exception Filter ES DSL to denote the associate asset/s involved.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "BusinessPolicyException" + + +class RelatedBusinessPolicyIncident(RelatedBusinessPolicy): + """ + Related entity reference for BusinessPolicyIncident assets. + + Extends RelatedBusinessPolicy with BusinessPolicyIncident-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "BusinessPolicyIncident" so it serializes correctly + + business_policy_incident_noncompliant_count: Union[int, None, UnsetType] = UNSET + """count of noncompliant assets in the incident""" + + business_policy_incident_related_policy_guids: Union[List[str], None, UnsetType] = ( + msgspec.field(default=UNSET, name="businessPolicyIncidentRelatedPolicyGUIDs") + ) + """policy ids related to this incident""" + + business_policy_incident_filter_dsl: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="businessPolicyIncidentFilterDSL" + ) + """Filter ES DSL to denote the associate asset/s involved.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "BusinessPolicyIncident" + + +class RelatedBusinessPolicyLog(RelatedBusinessPolicy): + """ + Related entity reference for BusinessPolicyLog assets. + + Extends RelatedBusinessPolicy with BusinessPolicyLog-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "BusinessPolicyLog" so it serializes correctly + + business_policy_id: Union[str, None, UnsetType] = UNSET + """business policy guid for which log are created""" + + business_policy_log_policy_type: Union[str, None, UnsetType] = UNSET + """business policy type for which log are created""" + + governed_assets_count: Union[int, None, UnsetType] = UNSET + """number of governed assets in the policy""" + + non_governed_assets_count: Union[int, None, UnsetType] = UNSET + """number of non governed assets in the policy""" + + compliant_assets_count: Union[int, None, UnsetType] = UNSET + """number of compliant assets in the policy""" + + non_compliant_assets_count: Union[int, None, UnsetType] = UNSET + """number of non compliant assets in the policy""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "BusinessPolicyLog" diff --git a/pyatlan_v9/model/assets/calculation_view.py b/pyatlan_v9/model/assets/calculation_view.py new file mode 100644 index 000000000..12a5c83ab --- /dev/null +++ b/pyatlan_v9/model/assets/calculation_view.py @@ -0,0 +1,907 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +CalculationView asset model with flattened inheritance. + +This module provides: +- CalculationView: Flat asset class (easy to use) +- CalculationViewAttributes: Nested attributes struct (extends AssetAttributes) +- CalculationViewNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .snowflake_related import RelatedSnowflakeSemanticLogicalTable +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .sql_related import RelatedColumn, RelatedSchema + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class CalculationView(Asset): + """ + Instance of a calculation view in Atlan. + """ + + COLUMN_COUNT: ClassVar[Any] = None + SQL_VERSION_ID: ClassVar[Any] = None + SQL_ACTIVATED_BY: ClassVar[Any] = None + SQL_ACTIVATED_AT: ClassVar[Any] = None + SQL_PACKAGE_ID: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + ATLAN_SCHEMA: ClassVar[Any] = None + COLUMNS: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "CalculationView" + + column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this calculation view.""" + + sql_version_id: Union[int, None, UnsetType] = UNSET + """The version ID of this calculation view.""" + + sql_activated_by: Union[str, None, UnsetType] = UNSET + """The owner who activated the calculation view""" + + sql_activated_at: Union[int, None, UnsetType] = UNSET + """Time at which this calculation view was activated at""" + + sql_package_id: Union[str, None, UnsetType] = UNSET + """The full package id path to which a calculation view belongs/resides in the repository.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + atlan_schema: Union[RelatedSchema, None, UnsetType] = UNSET + """Schema in which this calculation view exists.""" + + columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Columns that exist within this sap calculate view.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "CalculationView" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _calculation_view_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> CalculationView: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + CalculationView instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _calculation_view_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class CalculationViewAttributes(AssetAttributes): + """CalculationView-specific attributes for nested API format.""" + + column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this calculation view.""" + + sql_version_id: Union[int, None, UnsetType] = UNSET + """The version ID of this calculation view.""" + + sql_activated_by: Union[str, None, UnsetType] = UNSET + """The owner who activated the calculation view""" + + sql_activated_at: Union[int, None, UnsetType] = UNSET + """Time at which this calculation view was activated at""" + + sql_package_id: Union[str, None, UnsetType] = UNSET + """The full package id path to which a calculation view belongs/resides in the repository.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + +class CalculationViewRelationshipAttributes(AssetRelationshipAttributes): + """CalculationView-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + atlan_schema: Union[RelatedSchema, None, UnsetType] = UNSET + """Schema in which this calculation view exists.""" + + columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Columns that exist within this sap calculate view.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class CalculationViewNested(AssetNested): + """CalculationView in nested API format for high-performance serialization.""" + + attributes: Union[CalculationViewAttributes, UnsetType] = UNSET + relationship_attributes: Union[CalculationViewRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + CalculationViewRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + CalculationViewRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_CALCULATION_VIEW_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "atlan_schema", + "columns", + "schema_registry_subjects", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_calculation_view_attrs( + attrs: CalculationViewAttributes, obj: CalculationView +) -> None: + """Populate CalculationView-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.column_count = obj.column_count + attrs.sql_version_id = obj.sql_version_id + attrs.sql_activated_by = obj.sql_activated_by + attrs.sql_activated_at = obj.sql_activated_at + attrs.sql_package_id = obj.sql_package_id + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + + +def _extract_calculation_view_attrs(attrs: CalculationViewAttributes) -> dict: + """Extract all CalculationView attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["column_count"] = attrs.column_count + result["sql_version_id"] = attrs.sql_version_id + result["sql_activated_by"] = attrs.sql_activated_by + result["sql_activated_at"] = attrs.sql_activated_at + result["sql_package_id"] = attrs.sql_package_id + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _calculation_view_to_nested( + calculation_view: CalculationView, +) -> CalculationViewNested: + """Convert flat CalculationView to nested format.""" + attrs = CalculationViewAttributes() + _populate_calculation_view_attrs(attrs, calculation_view) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + calculation_view, + _CALCULATION_VIEW_REL_FIELDS, + CalculationViewRelationshipAttributes, + ) + return CalculationViewNested( + guid=calculation_view.guid, + type_name=calculation_view.type_name, + status=calculation_view.status, + version=calculation_view.version, + create_time=calculation_view.create_time, + update_time=calculation_view.update_time, + created_by=calculation_view.created_by, + updated_by=calculation_view.updated_by, + classifications=calculation_view.classifications, + classification_names=calculation_view.classification_names, + meanings=calculation_view.meanings, + labels=calculation_view.labels, + business_attributes=calculation_view.business_attributes, + custom_attributes=calculation_view.custom_attributes, + pending_tasks=calculation_view.pending_tasks, + proxy=calculation_view.proxy, + is_incomplete=calculation_view.is_incomplete, + provenance_type=calculation_view.provenance_type, + home_id=calculation_view.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _calculation_view_from_nested(nested: CalculationViewNested) -> CalculationView: + """Convert nested format to flat CalculationView.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else CalculationViewAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _CALCULATION_VIEW_REL_FIELDS, + CalculationViewRelationshipAttributes, + ) + return CalculationView( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_calculation_view_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _calculation_view_to_nested_bytes( + calculation_view: CalculationView, serde: Serde +) -> bytes: + """Convert flat CalculationView to nested JSON bytes.""" + return serde.encode(_calculation_view_to_nested(calculation_view)) + + +def _calculation_view_from_nested_bytes(data: bytes, serde: Serde) -> CalculationView: + """Convert nested JSON bytes to flat CalculationView.""" + nested = serde.decode(data, CalculationViewNested) + return _calculation_view_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, +) + +CalculationView.COLUMN_COUNT = NumericField("columnCount", "columnCount") +CalculationView.SQL_VERSION_ID = NumericField("sqlVersionId", "sqlVersionId") +CalculationView.SQL_ACTIVATED_BY = KeywordField("sqlActivatedBy", "sqlActivatedBy") +CalculationView.SQL_ACTIVATED_AT = NumericField("sqlActivatedAt", "sqlActivatedAt") +CalculationView.SQL_PACKAGE_ID = KeywordField("sqlPackageId", "sqlPackageId") +CalculationView.QUERY_COUNT = NumericField("queryCount", "queryCount") +CalculationView.QUERY_USER_COUNT = NumericField("queryUserCount", "queryUserCount") +CalculationView.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +CalculationView.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +CalculationView.DATABASE_NAME = KeywordField("databaseName", "databaseName") +CalculationView.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +CalculationView.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +CalculationView.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +CalculationView.TABLE_NAME = KeywordField("tableName", "tableName") +CalculationView.TABLE_QUALIFIED_NAME = KeywordField( + "tableQualifiedName", "tableQualifiedName" +) +CalculationView.VIEW_NAME = KeywordField("viewName", "viewName") +CalculationView.VIEW_QUALIFIED_NAME = KeywordField( + "viewQualifiedName", "viewQualifiedName" +) +CalculationView.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +CalculationView.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +CalculationView.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +CalculationView.LAST_PROFILED_AT = NumericField("lastProfiledAt", "lastProfiledAt") +CalculationView.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +CalculationView.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +CalculationView.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +CalculationView.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +CalculationView.ANOMALO_CHECKS = RelationField("anomaloChecks") +CalculationView.APPLICATION = RelationField("application") +CalculationView.APPLICATION_FIELD = RelationField("applicationField") +CalculationView.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +CalculationView.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +CalculationView.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +CalculationView.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +CalculationView.METRICS = RelationField("metrics") +CalculationView.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +CalculationView.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +CalculationView.DBT_MODELS = RelationField("dbtModels") +CalculationView.SQL_DBT_MODELS = RelationField("sqlDbtModels") +CalculationView.DBT_TESTS = RelationField("dbtTests") +CalculationView.DBT_SOURCES = RelationField("dbtSources") +CalculationView.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +CalculationView.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +CalculationView.MEANINGS = RelationField("meanings") +CalculationView.MC_MONITORS = RelationField("mcMonitors") +CalculationView.MC_INCIDENTS = RelationField("mcIncidents") +CalculationView.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +CalculationView.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +CalculationView.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +CalculationView.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +CalculationView.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +CalculationView.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +CalculationView.FILES = RelationField("files") +CalculationView.LINKS = RelationField("links") +CalculationView.README = RelationField("readme") +CalculationView.ATLAN_SCHEMA = RelationField("atlanSchema") +CalculationView.COLUMNS = RelationField("columns") +CalculationView.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +CalculationView.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +CalculationView.SODA_CHECKS = RelationField("sodaChecks") +CalculationView.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +CalculationView.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/cassandra.py b/pyatlan_v9/model/assets/cassandra.py new file mode 100644 index 000000000..98c102db4 --- /dev/null +++ b/pyatlan_v9/model/assets/cassandra.py @@ -0,0 +1,602 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Cassandra asset model with flattened inheritance. + +This module provides: +- Cassandra: Flat asset class (easy to use) +- CassandraAttributes: Nested attributes struct (extends AssetAttributes) +- CassandraNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Cassandra(Asset): + """ + Base class for all Cassandra types. + """ + + CASSANDRA_KEYSPACE_NAME: ClassVar[Any] = None + CASSANDRA_TABLE_NAME: ClassVar[Any] = None + CASSANDRA_VIEW_NAME: ClassVar[Any] = None + CASSANDRA_TABLE_QUALIFIED_NAME: ClassVar[Any] = None + CASSANDRA_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + NO_SQL_SCHEMA_DEFINITION: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Cassandra" + + cassandra_keyspace_name: Union[str, None, UnsetType] = UNSET + """Name of the keyspace for the Cassandra asset.""" + + cassandra_table_name: Union[str, None, UnsetType] = UNSET + """Name of the table for the Cassandra asset.""" + + cassandra_view_name: Union[str, None, UnsetType] = UNSET + """Name of view for Cassandra asset""" + + cassandra_table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of table for Cassandra asset""" + + cassandra_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of view for Cassandra asset""" + + no_sql_schema_definition: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="noSQLSchemaDefinition" + ) + """Represents attributes for describing the key schema for the table and indexes.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Cassandra" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _cassandra_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Cassandra: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Cassandra instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _cassandra_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class CassandraAttributes(AssetAttributes): + """Cassandra-specific attributes for nested API format.""" + + cassandra_keyspace_name: Union[str, None, UnsetType] = UNSET + """Name of the keyspace for the Cassandra asset.""" + + cassandra_table_name: Union[str, None, UnsetType] = UNSET + """Name of the table for the Cassandra asset.""" + + cassandra_view_name: Union[str, None, UnsetType] = UNSET + """Name of view for Cassandra asset""" + + cassandra_table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of table for Cassandra asset""" + + cassandra_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of view for Cassandra asset""" + + no_sql_schema_definition: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="noSQLSchemaDefinition" + ) + """Represents attributes for describing the key schema for the table and indexes.""" + + +class CassandraRelationshipAttributes(AssetRelationshipAttributes): + """Cassandra-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class CassandraNested(AssetNested): + """Cassandra in nested API format for high-performance serialization.""" + + attributes: Union[CassandraAttributes, UnsetType] = UNSET + relationship_attributes: Union[CassandraRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + CassandraRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + CassandraRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_CASSANDRA_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_cassandra_attrs(attrs: CassandraAttributes, obj: Cassandra) -> None: + """Populate Cassandra-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.cassandra_keyspace_name = obj.cassandra_keyspace_name + attrs.cassandra_table_name = obj.cassandra_table_name + attrs.cassandra_view_name = obj.cassandra_view_name + attrs.cassandra_table_qualified_name = obj.cassandra_table_qualified_name + attrs.cassandra_view_qualified_name = obj.cassandra_view_qualified_name + attrs.no_sql_schema_definition = obj.no_sql_schema_definition + + +def _extract_cassandra_attrs(attrs: CassandraAttributes) -> dict: + """Extract all Cassandra attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["cassandra_keyspace_name"] = attrs.cassandra_keyspace_name + result["cassandra_table_name"] = attrs.cassandra_table_name + result["cassandra_view_name"] = attrs.cassandra_view_name + result["cassandra_table_qualified_name"] = attrs.cassandra_table_qualified_name + result["cassandra_view_qualified_name"] = attrs.cassandra_view_qualified_name + result["no_sql_schema_definition"] = attrs.no_sql_schema_definition + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _cassandra_to_nested(cassandra: Cassandra) -> CassandraNested: + """Convert flat Cassandra to nested format.""" + attrs = CassandraAttributes() + _populate_cassandra_attrs(attrs, cassandra) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + cassandra, _CASSANDRA_REL_FIELDS, CassandraRelationshipAttributes + ) + return CassandraNested( + guid=cassandra.guid, + type_name=cassandra.type_name, + status=cassandra.status, + version=cassandra.version, + create_time=cassandra.create_time, + update_time=cassandra.update_time, + created_by=cassandra.created_by, + updated_by=cassandra.updated_by, + classifications=cassandra.classifications, + classification_names=cassandra.classification_names, + meanings=cassandra.meanings, + labels=cassandra.labels, + business_attributes=cassandra.business_attributes, + custom_attributes=cassandra.custom_attributes, + pending_tasks=cassandra.pending_tasks, + proxy=cassandra.proxy, + is_incomplete=cassandra.is_incomplete, + provenance_type=cassandra.provenance_type, + home_id=cassandra.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _cassandra_from_nested(nested: CassandraNested) -> Cassandra: + """Convert nested format to flat Cassandra.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else CassandraAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _CASSANDRA_REL_FIELDS, + CassandraRelationshipAttributes, + ) + return Cassandra( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_cassandra_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _cassandra_to_nested_bytes(cassandra: Cassandra, serde: Serde) -> bytes: + """Convert flat Cassandra to nested JSON bytes.""" + return serde.encode(_cassandra_to_nested(cassandra)) + + +def _cassandra_from_nested_bytes(data: bytes, serde: Serde) -> Cassandra: + """Convert nested JSON bytes to flat Cassandra.""" + nested = serde.decode(data, CassandraNested) + return _cassandra_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +Cassandra.CASSANDRA_KEYSPACE_NAME = KeywordField( + "cassandraKeyspaceName", "cassandraKeyspaceName" +) +Cassandra.CASSANDRA_TABLE_NAME = KeywordField( + "cassandraTableName", "cassandraTableName" +) +Cassandra.CASSANDRA_VIEW_NAME = KeywordField("cassandraViewName", "cassandraViewName") +Cassandra.CASSANDRA_TABLE_QUALIFIED_NAME = KeywordField( + "cassandraTableQualifiedName", "cassandraTableQualifiedName" +) +Cassandra.CASSANDRA_VIEW_QUALIFIED_NAME = KeywordField( + "cassandraViewQualifiedName", "cassandraViewQualifiedName" +) +Cassandra.NO_SQL_SCHEMA_DEFINITION = KeywordField( + "noSQLSchemaDefinition", "noSQLSchemaDefinition" +) +Cassandra.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Cassandra.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Cassandra.ANOMALO_CHECKS = RelationField("anomaloChecks") +Cassandra.APPLICATION = RelationField("application") +Cassandra.APPLICATION_FIELD = RelationField("applicationField") +Cassandra.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Cassandra.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Cassandra.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Cassandra.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Cassandra.METRICS = RelationField("metrics") +Cassandra.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Cassandra.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Cassandra.MEANINGS = RelationField("meanings") +Cassandra.MC_MONITORS = RelationField("mcMonitors") +Cassandra.MC_INCIDENTS = RelationField("mcIncidents") +Cassandra.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Cassandra.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Cassandra.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Cassandra.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Cassandra.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Cassandra.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Cassandra.FILES = RelationField("files") +Cassandra.LINKS = RelationField("links") +Cassandra.README = RelationField("readme") +Cassandra.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Cassandra.SODA_CHECKS = RelationField("sodaChecks") +Cassandra.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Cassandra.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/cassandra_column.py b/pyatlan_v9/model/assets/cassandra_column.py new file mode 100644 index 000000000..e78e3df4e --- /dev/null +++ b/pyatlan_v9/model/assets/cassandra_column.py @@ -0,0 +1,742 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +CassandraColumn asset model with flattened inheritance. + +This module provides: +- CassandraColumn: Flat asset class (easy to use) +- CassandraColumnAttributes: Nested attributes struct (extends AssetAttributes) +- CassandraColumnNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .cassandra_related import RelatedCassandraTable, RelatedCassandraView + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class CassandraColumn(Asset): + """ + Instances of a CassandraColumn in Atlan. + """ + + CASSANDRA_COLUMN_CLUSTERING_ORDER: ClassVar[Any] = None + CASSANDRA_COLUMN_IS_PARTITION_KEY: ClassVar[Any] = None + CASSANDRA_COLUMN_IS_CLUSTERING_KEY: ClassVar[Any] = None + CASSANDRA_COLUMN_KIND: ClassVar[Any] = None + CASSANDRA_COLUMN_POSITION: ClassVar[Any] = None + CASSANDRA_COLUMN_TYPE: ClassVar[Any] = None + CASSANDRA_COLUMN_IS_STATIC: ClassVar[Any] = None + CASSANDRA_KEYSPACE_NAME: ClassVar[Any] = None + CASSANDRA_TABLE_NAME: ClassVar[Any] = None + CASSANDRA_VIEW_NAME: ClassVar[Any] = None + CASSANDRA_TABLE_QUALIFIED_NAME: ClassVar[Any] = None + CASSANDRA_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + NO_SQL_SCHEMA_DEFINITION: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + CASSANDRA_TABLE: ClassVar[Any] = None + CASSANDRA_VIEW: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "CassandraColumn" + + cassandra_column_clustering_order: Union[str, None, UnsetType] = UNSET + """Clustering order of the CassandraColumn.""" + + cassandra_column_is_partition_key: Union[bool, None, UnsetType] = UNSET + """Is the CassandraColumn partition key.""" + + cassandra_column_is_clustering_key: Union[bool, None, UnsetType] = UNSET + """Is the CassandraColumn clustering key.""" + + cassandra_column_kind: Union[str, None, UnsetType] = UNSET + """Kind of CassandraColumn (e.g. partition key, clustering column, etc).""" + + cassandra_column_position: Union[int, None, UnsetType] = UNSET + """Position of the CassandraColumn.""" + + cassandra_column_type: Union[str, None, UnsetType] = UNSET + """Type of the CassandraColumn.""" + + cassandra_column_is_static: Union[bool, None, UnsetType] = UNSET + """Indicates whether the CassandraColumn is static.""" + + cassandra_keyspace_name: Union[str, None, UnsetType] = UNSET + """Name of the keyspace for the Cassandra asset.""" + + cassandra_table_name: Union[str, None, UnsetType] = UNSET + """Name of the table for the Cassandra asset.""" + + cassandra_view_name: Union[str, None, UnsetType] = UNSET + """Name of view for Cassandra asset""" + + cassandra_table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of table for Cassandra asset""" + + cassandra_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of view for Cassandra asset""" + + no_sql_schema_definition: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="noSQLSchemaDefinition" + ) + """Represents attributes for describing the key schema for the table and indexes.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cassandra_table: Union[RelatedCassandraTable, None, UnsetType] = UNSET + """Table containing the column.""" + + cassandra_view: Union[RelatedCassandraView, None, UnsetType] = UNSET + """View containing the column.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "CassandraColumn" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _cassandra_column_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> CassandraColumn: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + CassandraColumn instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _cassandra_column_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class CassandraColumnAttributes(AssetAttributes): + """CassandraColumn-specific attributes for nested API format.""" + + cassandra_column_clustering_order: Union[str, None, UnsetType] = UNSET + """Clustering order of the CassandraColumn.""" + + cassandra_column_is_partition_key: Union[bool, None, UnsetType] = UNSET + """Is the CassandraColumn partition key.""" + + cassandra_column_is_clustering_key: Union[bool, None, UnsetType] = UNSET + """Is the CassandraColumn clustering key.""" + + cassandra_column_kind: Union[str, None, UnsetType] = UNSET + """Kind of CassandraColumn (e.g. partition key, clustering column, etc).""" + + cassandra_column_position: Union[int, None, UnsetType] = UNSET + """Position of the CassandraColumn.""" + + cassandra_column_type: Union[str, None, UnsetType] = UNSET + """Type of the CassandraColumn.""" + + cassandra_column_is_static: Union[bool, None, UnsetType] = UNSET + """Indicates whether the CassandraColumn is static.""" + + cassandra_keyspace_name: Union[str, None, UnsetType] = UNSET + """Name of the keyspace for the Cassandra asset.""" + + cassandra_table_name: Union[str, None, UnsetType] = UNSET + """Name of the table for the Cassandra asset.""" + + cassandra_view_name: Union[str, None, UnsetType] = UNSET + """Name of view for Cassandra asset""" + + cassandra_table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of table for Cassandra asset""" + + cassandra_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of view for Cassandra asset""" + + no_sql_schema_definition: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="noSQLSchemaDefinition" + ) + """Represents attributes for describing the key schema for the table and indexes.""" + + +class CassandraColumnRelationshipAttributes(AssetRelationshipAttributes): + """CassandraColumn-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cassandra_table: Union[RelatedCassandraTable, None, UnsetType] = UNSET + """Table containing the column.""" + + cassandra_view: Union[RelatedCassandraView, None, UnsetType] = UNSET + """View containing the column.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class CassandraColumnNested(AssetNested): + """CassandraColumn in nested API format for high-performance serialization.""" + + attributes: Union[CassandraColumnAttributes, UnsetType] = UNSET + relationship_attributes: Union[CassandraColumnRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + CassandraColumnRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + CassandraColumnRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_CASSANDRA_COLUMN_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "cassandra_table", + "cassandra_view", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_cassandra_column_attrs( + attrs: CassandraColumnAttributes, obj: CassandraColumn +) -> None: + """Populate CassandraColumn-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.cassandra_column_clustering_order = obj.cassandra_column_clustering_order + attrs.cassandra_column_is_partition_key = obj.cassandra_column_is_partition_key + attrs.cassandra_column_is_clustering_key = obj.cassandra_column_is_clustering_key + attrs.cassandra_column_kind = obj.cassandra_column_kind + attrs.cassandra_column_position = obj.cassandra_column_position + attrs.cassandra_column_type = obj.cassandra_column_type + attrs.cassandra_column_is_static = obj.cassandra_column_is_static + attrs.cassandra_keyspace_name = obj.cassandra_keyspace_name + attrs.cassandra_table_name = obj.cassandra_table_name + attrs.cassandra_view_name = obj.cassandra_view_name + attrs.cassandra_table_qualified_name = obj.cassandra_table_qualified_name + attrs.cassandra_view_qualified_name = obj.cassandra_view_qualified_name + attrs.no_sql_schema_definition = obj.no_sql_schema_definition + + +def _extract_cassandra_column_attrs(attrs: CassandraColumnAttributes) -> dict: + """Extract all CassandraColumn attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["cassandra_column_clustering_order"] = ( + attrs.cassandra_column_clustering_order + ) + result["cassandra_column_is_partition_key"] = ( + attrs.cassandra_column_is_partition_key + ) + result["cassandra_column_is_clustering_key"] = ( + attrs.cassandra_column_is_clustering_key + ) + result["cassandra_column_kind"] = attrs.cassandra_column_kind + result["cassandra_column_position"] = attrs.cassandra_column_position + result["cassandra_column_type"] = attrs.cassandra_column_type + result["cassandra_column_is_static"] = attrs.cassandra_column_is_static + result["cassandra_keyspace_name"] = attrs.cassandra_keyspace_name + result["cassandra_table_name"] = attrs.cassandra_table_name + result["cassandra_view_name"] = attrs.cassandra_view_name + result["cassandra_table_qualified_name"] = attrs.cassandra_table_qualified_name + result["cassandra_view_qualified_name"] = attrs.cassandra_view_qualified_name + result["no_sql_schema_definition"] = attrs.no_sql_schema_definition + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _cassandra_column_to_nested( + cassandra_column: CassandraColumn, +) -> CassandraColumnNested: + """Convert flat CassandraColumn to nested format.""" + attrs = CassandraColumnAttributes() + _populate_cassandra_column_attrs(attrs, cassandra_column) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + cassandra_column, + _CASSANDRA_COLUMN_REL_FIELDS, + CassandraColumnRelationshipAttributes, + ) + return CassandraColumnNested( + guid=cassandra_column.guid, + type_name=cassandra_column.type_name, + status=cassandra_column.status, + version=cassandra_column.version, + create_time=cassandra_column.create_time, + update_time=cassandra_column.update_time, + created_by=cassandra_column.created_by, + updated_by=cassandra_column.updated_by, + classifications=cassandra_column.classifications, + classification_names=cassandra_column.classification_names, + meanings=cassandra_column.meanings, + labels=cassandra_column.labels, + business_attributes=cassandra_column.business_attributes, + custom_attributes=cassandra_column.custom_attributes, + pending_tasks=cassandra_column.pending_tasks, + proxy=cassandra_column.proxy, + is_incomplete=cassandra_column.is_incomplete, + provenance_type=cassandra_column.provenance_type, + home_id=cassandra_column.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _cassandra_column_from_nested(nested: CassandraColumnNested) -> CassandraColumn: + """Convert nested format to flat CassandraColumn.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else CassandraColumnAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _CASSANDRA_COLUMN_REL_FIELDS, + CassandraColumnRelationshipAttributes, + ) + return CassandraColumn( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_cassandra_column_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _cassandra_column_to_nested_bytes( + cassandra_column: CassandraColumn, serde: Serde +) -> bytes: + """Convert flat CassandraColumn to nested JSON bytes.""" + return serde.encode(_cassandra_column_to_nested(cassandra_column)) + + +def _cassandra_column_from_nested_bytes(data: bytes, serde: Serde) -> CassandraColumn: + """Convert nested JSON bytes to flat CassandraColumn.""" + nested = serde.decode(data, CassandraColumnNested) + return _cassandra_column_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +CassandraColumn.CASSANDRA_COLUMN_CLUSTERING_ORDER = KeywordField( + "cassandraColumnClusteringOrder", "cassandraColumnClusteringOrder" +) +CassandraColumn.CASSANDRA_COLUMN_IS_PARTITION_KEY = BooleanField( + "cassandraColumnIsPartitionKey", "cassandraColumnIsPartitionKey" +) +CassandraColumn.CASSANDRA_COLUMN_IS_CLUSTERING_KEY = BooleanField( + "cassandraColumnIsClusteringKey", "cassandraColumnIsClusteringKey" +) +CassandraColumn.CASSANDRA_COLUMN_KIND = KeywordField( + "cassandraColumnKind", "cassandraColumnKind" +) +CassandraColumn.CASSANDRA_COLUMN_POSITION = NumericField( + "cassandraColumnPosition", "cassandraColumnPosition" +) +CassandraColumn.CASSANDRA_COLUMN_TYPE = KeywordTextField( + "cassandraColumnType", "cassandraColumnType", "cassandraColumnType.text" +) +CassandraColumn.CASSANDRA_COLUMN_IS_STATIC = BooleanField( + "cassandraColumnIsStatic", "cassandraColumnIsStatic" +) +CassandraColumn.CASSANDRA_KEYSPACE_NAME = KeywordField( + "cassandraKeyspaceName", "cassandraKeyspaceName" +) +CassandraColumn.CASSANDRA_TABLE_NAME = KeywordField( + "cassandraTableName", "cassandraTableName" +) +CassandraColumn.CASSANDRA_VIEW_NAME = KeywordField( + "cassandraViewName", "cassandraViewName" +) +CassandraColumn.CASSANDRA_TABLE_QUALIFIED_NAME = KeywordField( + "cassandraTableQualifiedName", "cassandraTableQualifiedName" +) +CassandraColumn.CASSANDRA_VIEW_QUALIFIED_NAME = KeywordField( + "cassandraViewQualifiedName", "cassandraViewQualifiedName" +) +CassandraColumn.NO_SQL_SCHEMA_DEFINITION = KeywordField( + "noSQLSchemaDefinition", "noSQLSchemaDefinition" +) +CassandraColumn.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +CassandraColumn.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +CassandraColumn.ANOMALO_CHECKS = RelationField("anomaloChecks") +CassandraColumn.APPLICATION = RelationField("application") +CassandraColumn.APPLICATION_FIELD = RelationField("applicationField") +CassandraColumn.CASSANDRA_TABLE = RelationField("cassandraTable") +CassandraColumn.CASSANDRA_VIEW = RelationField("cassandraView") +CassandraColumn.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +CassandraColumn.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +CassandraColumn.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +CassandraColumn.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +CassandraColumn.METRICS = RelationField("metrics") +CassandraColumn.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +CassandraColumn.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +CassandraColumn.MEANINGS = RelationField("meanings") +CassandraColumn.MC_MONITORS = RelationField("mcMonitors") +CassandraColumn.MC_INCIDENTS = RelationField("mcIncidents") +CassandraColumn.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +CassandraColumn.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +CassandraColumn.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +CassandraColumn.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +CassandraColumn.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +CassandraColumn.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +CassandraColumn.FILES = RelationField("files") +CassandraColumn.LINKS = RelationField("links") +CassandraColumn.README = RelationField("readme") +CassandraColumn.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +CassandraColumn.SODA_CHECKS = RelationField("sodaChecks") +CassandraColumn.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +CassandraColumn.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/cassandra_index.py b/pyatlan_v9/model/assets/cassandra_index.py new file mode 100644 index 000000000..befeafa7e --- /dev/null +++ b/pyatlan_v9/model/assets/cassandra_index.py @@ -0,0 +1,672 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +CassandraIndex asset model with flattened inheritance. + +This module provides: +- CassandraIndex: Flat asset class (easy to use) +- CassandraIndexAttributes: Nested attributes struct (extends AssetAttributes) +- CassandraIndexNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .cassandra_related import RelatedCassandraTable + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class CassandraIndex(Asset): + """ + Instances of a CassandraIndex in Atlan. + """ + + CASSANDRA_INDEX_KIND: ClassVar[Any] = None + CASSANDRA_INDEX_OPTIONS: ClassVar[Any] = None + CASSANDRA_INDEX_QUERY: ClassVar[Any] = None + CASSANDRA_KEYSPACE_NAME: ClassVar[Any] = None + CASSANDRA_TABLE_NAME: ClassVar[Any] = None + CASSANDRA_VIEW_NAME: ClassVar[Any] = None + CASSANDRA_TABLE_QUALIFIED_NAME: ClassVar[Any] = None + CASSANDRA_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + NO_SQL_SCHEMA_DEFINITION: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + CASSANDRA_TABLE: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "CassandraIndex" + + cassandra_index_kind: Union[str, None, UnsetType] = UNSET + """Kind of index (e.g. COMPOSITES).""" + + cassandra_index_options: Union[Dict[str, str], None, UnsetType] = UNSET + """Options for the index.""" + + cassandra_index_query: Union[str, None, UnsetType] = UNSET + """Query used to create the index.""" + + cassandra_keyspace_name: Union[str, None, UnsetType] = UNSET + """Name of the keyspace for the Cassandra asset.""" + + cassandra_table_name: Union[str, None, UnsetType] = UNSET + """Name of the table for the Cassandra asset.""" + + cassandra_view_name: Union[str, None, UnsetType] = UNSET + """Name of view for Cassandra asset""" + + cassandra_table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of table for Cassandra asset""" + + cassandra_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of view for Cassandra asset""" + + no_sql_schema_definition: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="noSQLSchemaDefinition" + ) + """Represents attributes for describing the key schema for the table and indexes.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cassandra_table: Union[RelatedCassandraTable, None, UnsetType] = UNSET + """Table containing the index.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "CassandraIndex" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _cassandra_index_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> CassandraIndex: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + CassandraIndex instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _cassandra_index_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class CassandraIndexAttributes(AssetAttributes): + """CassandraIndex-specific attributes for nested API format.""" + + cassandra_index_kind: Union[str, None, UnsetType] = UNSET + """Kind of index (e.g. COMPOSITES).""" + + cassandra_index_options: Union[Dict[str, str], None, UnsetType] = UNSET + """Options for the index.""" + + cassandra_index_query: Union[str, None, UnsetType] = UNSET + """Query used to create the index.""" + + cassandra_keyspace_name: Union[str, None, UnsetType] = UNSET + """Name of the keyspace for the Cassandra asset.""" + + cassandra_table_name: Union[str, None, UnsetType] = UNSET + """Name of the table for the Cassandra asset.""" + + cassandra_view_name: Union[str, None, UnsetType] = UNSET + """Name of view for Cassandra asset""" + + cassandra_table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of table for Cassandra asset""" + + cassandra_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of view for Cassandra asset""" + + no_sql_schema_definition: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="noSQLSchemaDefinition" + ) + """Represents attributes for describing the key schema for the table and indexes.""" + + +class CassandraIndexRelationshipAttributes(AssetRelationshipAttributes): + """CassandraIndex-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cassandra_table: Union[RelatedCassandraTable, None, UnsetType] = UNSET + """Table containing the index.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class CassandraIndexNested(AssetNested): + """CassandraIndex in nested API format for high-performance serialization.""" + + attributes: Union[CassandraIndexAttributes, UnsetType] = UNSET + relationship_attributes: Union[CassandraIndexRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + CassandraIndexRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + CassandraIndexRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_CASSANDRA_INDEX_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "cassandra_table", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_cassandra_index_attrs( + attrs: CassandraIndexAttributes, obj: CassandraIndex +) -> None: + """Populate CassandraIndex-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.cassandra_index_kind = obj.cassandra_index_kind + attrs.cassandra_index_options = obj.cassandra_index_options + attrs.cassandra_index_query = obj.cassandra_index_query + attrs.cassandra_keyspace_name = obj.cassandra_keyspace_name + attrs.cassandra_table_name = obj.cassandra_table_name + attrs.cassandra_view_name = obj.cassandra_view_name + attrs.cassandra_table_qualified_name = obj.cassandra_table_qualified_name + attrs.cassandra_view_qualified_name = obj.cassandra_view_qualified_name + attrs.no_sql_schema_definition = obj.no_sql_schema_definition + + +def _extract_cassandra_index_attrs(attrs: CassandraIndexAttributes) -> dict: + """Extract all CassandraIndex attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["cassandra_index_kind"] = attrs.cassandra_index_kind + result["cassandra_index_options"] = attrs.cassandra_index_options + result["cassandra_index_query"] = attrs.cassandra_index_query + result["cassandra_keyspace_name"] = attrs.cassandra_keyspace_name + result["cassandra_table_name"] = attrs.cassandra_table_name + result["cassandra_view_name"] = attrs.cassandra_view_name + result["cassandra_table_qualified_name"] = attrs.cassandra_table_qualified_name + result["cassandra_view_qualified_name"] = attrs.cassandra_view_qualified_name + result["no_sql_schema_definition"] = attrs.no_sql_schema_definition + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _cassandra_index_to_nested(cassandra_index: CassandraIndex) -> CassandraIndexNested: + """Convert flat CassandraIndex to nested format.""" + attrs = CassandraIndexAttributes() + _populate_cassandra_index_attrs(attrs, cassandra_index) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + cassandra_index, + _CASSANDRA_INDEX_REL_FIELDS, + CassandraIndexRelationshipAttributes, + ) + return CassandraIndexNested( + guid=cassandra_index.guid, + type_name=cassandra_index.type_name, + status=cassandra_index.status, + version=cassandra_index.version, + create_time=cassandra_index.create_time, + update_time=cassandra_index.update_time, + created_by=cassandra_index.created_by, + updated_by=cassandra_index.updated_by, + classifications=cassandra_index.classifications, + classification_names=cassandra_index.classification_names, + meanings=cassandra_index.meanings, + labels=cassandra_index.labels, + business_attributes=cassandra_index.business_attributes, + custom_attributes=cassandra_index.custom_attributes, + pending_tasks=cassandra_index.pending_tasks, + proxy=cassandra_index.proxy, + is_incomplete=cassandra_index.is_incomplete, + provenance_type=cassandra_index.provenance_type, + home_id=cassandra_index.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _cassandra_index_from_nested(nested: CassandraIndexNested) -> CassandraIndex: + """Convert nested format to flat CassandraIndex.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else CassandraIndexAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _CASSANDRA_INDEX_REL_FIELDS, + CassandraIndexRelationshipAttributes, + ) + return CassandraIndex( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_cassandra_index_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _cassandra_index_to_nested_bytes( + cassandra_index: CassandraIndex, serde: Serde +) -> bytes: + """Convert flat CassandraIndex to nested JSON bytes.""" + return serde.encode(_cassandra_index_to_nested(cassandra_index)) + + +def _cassandra_index_from_nested_bytes(data: bytes, serde: Serde) -> CassandraIndex: + """Convert nested JSON bytes to flat CassandraIndex.""" + nested = serde.decode(data, CassandraIndexNested) + return _cassandra_index_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +CassandraIndex.CASSANDRA_INDEX_KIND = KeywordField( + "cassandraIndexKind", "cassandraIndexKind" +) +CassandraIndex.CASSANDRA_INDEX_OPTIONS = KeywordField( + "cassandraIndexOptions", "cassandraIndexOptions" +) +CassandraIndex.CASSANDRA_INDEX_QUERY = KeywordField( + "cassandraIndexQuery", "cassandraIndexQuery" +) +CassandraIndex.CASSANDRA_KEYSPACE_NAME = KeywordField( + "cassandraKeyspaceName", "cassandraKeyspaceName" +) +CassandraIndex.CASSANDRA_TABLE_NAME = KeywordField( + "cassandraTableName", "cassandraTableName" +) +CassandraIndex.CASSANDRA_VIEW_NAME = KeywordField( + "cassandraViewName", "cassandraViewName" +) +CassandraIndex.CASSANDRA_TABLE_QUALIFIED_NAME = KeywordField( + "cassandraTableQualifiedName", "cassandraTableQualifiedName" +) +CassandraIndex.CASSANDRA_VIEW_QUALIFIED_NAME = KeywordField( + "cassandraViewQualifiedName", "cassandraViewQualifiedName" +) +CassandraIndex.NO_SQL_SCHEMA_DEFINITION = KeywordField( + "noSQLSchemaDefinition", "noSQLSchemaDefinition" +) +CassandraIndex.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +CassandraIndex.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +CassandraIndex.ANOMALO_CHECKS = RelationField("anomaloChecks") +CassandraIndex.APPLICATION = RelationField("application") +CassandraIndex.APPLICATION_FIELD = RelationField("applicationField") +CassandraIndex.CASSANDRA_TABLE = RelationField("cassandraTable") +CassandraIndex.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +CassandraIndex.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +CassandraIndex.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +CassandraIndex.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +CassandraIndex.METRICS = RelationField("metrics") +CassandraIndex.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +CassandraIndex.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +CassandraIndex.MEANINGS = RelationField("meanings") +CassandraIndex.MC_MONITORS = RelationField("mcMonitors") +CassandraIndex.MC_INCIDENTS = RelationField("mcIncidents") +CassandraIndex.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +CassandraIndex.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +CassandraIndex.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +CassandraIndex.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +CassandraIndex.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +CassandraIndex.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +CassandraIndex.FILES = RelationField("files") +CassandraIndex.LINKS = RelationField("links") +CassandraIndex.README = RelationField("readme") +CassandraIndex.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +CassandraIndex.SODA_CHECKS = RelationField("sodaChecks") +CassandraIndex.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +CassandraIndex.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/cassandra_keyspace.py b/pyatlan_v9/model/assets/cassandra_keyspace.py new file mode 100644 index 000000000..fe7fae513 --- /dev/null +++ b/pyatlan_v9/model/assets/cassandra_keyspace.py @@ -0,0 +1,695 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +CassandraKeyspace asset model with flattened inheritance. + +This module provides: +- CassandraKeyspace: Flat asset class (easy to use) +- CassandraKeyspaceAttributes: Nested attributes struct (extends AssetAttributes) +- CassandraKeyspaceNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .cassandra_related import RelatedCassandraTable, RelatedCassandraView + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class CassandraKeyspace(Asset): + """ + Instances of a CassandraKeyspace in Atlan. + """ + + CASSANDRA_KEYSPACE_DURABLE_WRITES: ClassVar[Any] = None + CASSANDRA_KEYSPACE_REPLICATION: ClassVar[Any] = None + CASSANDRA_KEYSPACE_VIRTUAL: ClassVar[Any] = None + CASSANDRA_KEYSPACE_QUERY: ClassVar[Any] = None + CASSANDRA_KEYSPACE_NAME: ClassVar[Any] = None + CASSANDRA_TABLE_NAME: ClassVar[Any] = None + CASSANDRA_VIEW_NAME: ClassVar[Any] = None + CASSANDRA_TABLE_QUALIFIED_NAME: ClassVar[Any] = None + CASSANDRA_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + NO_SQL_SCHEMA_DEFINITION: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + CASSANDRA_TABLES: ClassVar[Any] = None + CASSANDRA_VIEWS: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "CassandraKeyspace" + + cassandra_keyspace_durable_writes: Union[bool, None, UnsetType] = UNSET + """Indicates whether durable writes are enabled for the CassandraKeyspace.""" + + cassandra_keyspace_replication: Union[Dict[str, str], None, UnsetType] = UNSET + """Replication class for the CassandraKeyspace.""" + + cassandra_keyspace_virtual: Union[bool, None, UnsetType] = UNSET + """Indicates whether the CassandraKeyspace is virtual.""" + + cassandra_keyspace_query: Union[str, None, UnsetType] = UNSET + """Query associated with the CassandraKeyspace.""" + + cassandra_keyspace_name: Union[str, None, UnsetType] = UNSET + """Name of the keyspace for the Cassandra asset.""" + + cassandra_table_name: Union[str, None, UnsetType] = UNSET + """Name of the table for the Cassandra asset.""" + + cassandra_view_name: Union[str, None, UnsetType] = UNSET + """Name of view for Cassandra asset""" + + cassandra_table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of table for Cassandra asset""" + + cassandra_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of view for Cassandra asset""" + + no_sql_schema_definition: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="noSQLSchemaDefinition" + ) + """Represents attributes for describing the key schema for the table and indexes.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cassandra_tables: Union[List[RelatedCassandraTable], None, UnsetType] = UNSET + """Individual tables contained in the keyspace.""" + + cassandra_views: Union[List[RelatedCassandraView], None, UnsetType] = UNSET + """Individual views contained in the keyspace.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "CassandraKeyspace" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _cassandra_keyspace_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> CassandraKeyspace: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + CassandraKeyspace instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _cassandra_keyspace_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class CassandraKeyspaceAttributes(AssetAttributes): + """CassandraKeyspace-specific attributes for nested API format.""" + + cassandra_keyspace_durable_writes: Union[bool, None, UnsetType] = UNSET + """Indicates whether durable writes are enabled for the CassandraKeyspace.""" + + cassandra_keyspace_replication: Union[Dict[str, str], None, UnsetType] = UNSET + """Replication class for the CassandraKeyspace.""" + + cassandra_keyspace_virtual: Union[bool, None, UnsetType] = UNSET + """Indicates whether the CassandraKeyspace is virtual.""" + + cassandra_keyspace_query: Union[str, None, UnsetType] = UNSET + """Query associated with the CassandraKeyspace.""" + + cassandra_keyspace_name: Union[str, None, UnsetType] = UNSET + """Name of the keyspace for the Cassandra asset.""" + + cassandra_table_name: Union[str, None, UnsetType] = UNSET + """Name of the table for the Cassandra asset.""" + + cassandra_view_name: Union[str, None, UnsetType] = UNSET + """Name of view for Cassandra asset""" + + cassandra_table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of table for Cassandra asset""" + + cassandra_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of view for Cassandra asset""" + + no_sql_schema_definition: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="noSQLSchemaDefinition" + ) + """Represents attributes for describing the key schema for the table and indexes.""" + + +class CassandraKeyspaceRelationshipAttributes(AssetRelationshipAttributes): + """CassandraKeyspace-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cassandra_tables: Union[List[RelatedCassandraTable], None, UnsetType] = UNSET + """Individual tables contained in the keyspace.""" + + cassandra_views: Union[List[RelatedCassandraView], None, UnsetType] = UNSET + """Individual views contained in the keyspace.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class CassandraKeyspaceNested(AssetNested): + """CassandraKeyspace in nested API format for high-performance serialization.""" + + attributes: Union[CassandraKeyspaceAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + CassandraKeyspaceRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + CassandraKeyspaceRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + CassandraKeyspaceRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_CASSANDRA_KEYSPACE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "cassandra_tables", + "cassandra_views", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_cassandra_keyspace_attrs( + attrs: CassandraKeyspaceAttributes, obj: CassandraKeyspace +) -> None: + """Populate CassandraKeyspace-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.cassandra_keyspace_durable_writes = obj.cassandra_keyspace_durable_writes + attrs.cassandra_keyspace_replication = obj.cassandra_keyspace_replication + attrs.cassandra_keyspace_virtual = obj.cassandra_keyspace_virtual + attrs.cassandra_keyspace_query = obj.cassandra_keyspace_query + attrs.cassandra_keyspace_name = obj.cassandra_keyspace_name + attrs.cassandra_table_name = obj.cassandra_table_name + attrs.cassandra_view_name = obj.cassandra_view_name + attrs.cassandra_table_qualified_name = obj.cassandra_table_qualified_name + attrs.cassandra_view_qualified_name = obj.cassandra_view_qualified_name + attrs.no_sql_schema_definition = obj.no_sql_schema_definition + + +def _extract_cassandra_keyspace_attrs(attrs: CassandraKeyspaceAttributes) -> dict: + """Extract all CassandraKeyspace attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["cassandra_keyspace_durable_writes"] = ( + attrs.cassandra_keyspace_durable_writes + ) + result["cassandra_keyspace_replication"] = attrs.cassandra_keyspace_replication + result["cassandra_keyspace_virtual"] = attrs.cassandra_keyspace_virtual + result["cassandra_keyspace_query"] = attrs.cassandra_keyspace_query + result["cassandra_keyspace_name"] = attrs.cassandra_keyspace_name + result["cassandra_table_name"] = attrs.cassandra_table_name + result["cassandra_view_name"] = attrs.cassandra_view_name + result["cassandra_table_qualified_name"] = attrs.cassandra_table_qualified_name + result["cassandra_view_qualified_name"] = attrs.cassandra_view_qualified_name + result["no_sql_schema_definition"] = attrs.no_sql_schema_definition + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _cassandra_keyspace_to_nested( + cassandra_keyspace: CassandraKeyspace, +) -> CassandraKeyspaceNested: + """Convert flat CassandraKeyspace to nested format.""" + attrs = CassandraKeyspaceAttributes() + _populate_cassandra_keyspace_attrs(attrs, cassandra_keyspace) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + cassandra_keyspace, + _CASSANDRA_KEYSPACE_REL_FIELDS, + CassandraKeyspaceRelationshipAttributes, + ) + return CassandraKeyspaceNested( + guid=cassandra_keyspace.guid, + type_name=cassandra_keyspace.type_name, + status=cassandra_keyspace.status, + version=cassandra_keyspace.version, + create_time=cassandra_keyspace.create_time, + update_time=cassandra_keyspace.update_time, + created_by=cassandra_keyspace.created_by, + updated_by=cassandra_keyspace.updated_by, + classifications=cassandra_keyspace.classifications, + classification_names=cassandra_keyspace.classification_names, + meanings=cassandra_keyspace.meanings, + labels=cassandra_keyspace.labels, + business_attributes=cassandra_keyspace.business_attributes, + custom_attributes=cassandra_keyspace.custom_attributes, + pending_tasks=cassandra_keyspace.pending_tasks, + proxy=cassandra_keyspace.proxy, + is_incomplete=cassandra_keyspace.is_incomplete, + provenance_type=cassandra_keyspace.provenance_type, + home_id=cassandra_keyspace.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _cassandra_keyspace_from_nested( + nested: CassandraKeyspaceNested, +) -> CassandraKeyspace: + """Convert nested format to flat CassandraKeyspace.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else CassandraKeyspaceAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _CASSANDRA_KEYSPACE_REL_FIELDS, + CassandraKeyspaceRelationshipAttributes, + ) + return CassandraKeyspace( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_cassandra_keyspace_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _cassandra_keyspace_to_nested_bytes( + cassandra_keyspace: CassandraKeyspace, serde: Serde +) -> bytes: + """Convert flat CassandraKeyspace to nested JSON bytes.""" + return serde.encode(_cassandra_keyspace_to_nested(cassandra_keyspace)) + + +def _cassandra_keyspace_from_nested_bytes( + data: bytes, serde: Serde +) -> CassandraKeyspace: + """Convert nested JSON bytes to flat CassandraKeyspace.""" + nested = serde.decode(data, CassandraKeyspaceNested) + return _cassandra_keyspace_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + RelationField, +) + +CassandraKeyspace.CASSANDRA_KEYSPACE_DURABLE_WRITES = BooleanField( + "cassandraKeyspaceDurableWrites", "cassandraKeyspaceDurableWrites" +) +CassandraKeyspace.CASSANDRA_KEYSPACE_REPLICATION = KeywordField( + "cassandraKeyspaceReplication", "cassandraKeyspaceReplication" +) +CassandraKeyspace.CASSANDRA_KEYSPACE_VIRTUAL = BooleanField( + "cassandraKeyspaceVirtual", "cassandraKeyspaceVirtual" +) +CassandraKeyspace.CASSANDRA_KEYSPACE_QUERY = KeywordField( + "cassandraKeyspaceQuery", "cassandraKeyspaceQuery" +) +CassandraKeyspace.CASSANDRA_KEYSPACE_NAME = KeywordField( + "cassandraKeyspaceName", "cassandraKeyspaceName" +) +CassandraKeyspace.CASSANDRA_TABLE_NAME = KeywordField( + "cassandraTableName", "cassandraTableName" +) +CassandraKeyspace.CASSANDRA_VIEW_NAME = KeywordField( + "cassandraViewName", "cassandraViewName" +) +CassandraKeyspace.CASSANDRA_TABLE_QUALIFIED_NAME = KeywordField( + "cassandraTableQualifiedName", "cassandraTableQualifiedName" +) +CassandraKeyspace.CASSANDRA_VIEW_QUALIFIED_NAME = KeywordField( + "cassandraViewQualifiedName", "cassandraViewQualifiedName" +) +CassandraKeyspace.NO_SQL_SCHEMA_DEFINITION = KeywordField( + "noSQLSchemaDefinition", "noSQLSchemaDefinition" +) +CassandraKeyspace.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +CassandraKeyspace.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +CassandraKeyspace.ANOMALO_CHECKS = RelationField("anomaloChecks") +CassandraKeyspace.APPLICATION = RelationField("application") +CassandraKeyspace.APPLICATION_FIELD = RelationField("applicationField") +CassandraKeyspace.CASSANDRA_TABLES = RelationField("cassandraTables") +CassandraKeyspace.CASSANDRA_VIEWS = RelationField("cassandraViews") +CassandraKeyspace.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +CassandraKeyspace.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +CassandraKeyspace.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +CassandraKeyspace.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +CassandraKeyspace.METRICS = RelationField("metrics") +CassandraKeyspace.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +CassandraKeyspace.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +CassandraKeyspace.MEANINGS = RelationField("meanings") +CassandraKeyspace.MC_MONITORS = RelationField("mcMonitors") +CassandraKeyspace.MC_INCIDENTS = RelationField("mcIncidents") +CassandraKeyspace.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +CassandraKeyspace.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +CassandraKeyspace.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +CassandraKeyspace.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +CassandraKeyspace.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +CassandraKeyspace.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +CassandraKeyspace.FILES = RelationField("files") +CassandraKeyspace.LINKS = RelationField("links") +CassandraKeyspace.README = RelationField("readme") +CassandraKeyspace.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +CassandraKeyspace.SODA_CHECKS = RelationField("sodaChecks") +CassandraKeyspace.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +CassandraKeyspace.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/cassandra_related.py b/pyatlan_v9/model/assets/cassandra_related.py new file mode 100644 index 000000000..c52b696e6 --- /dev/null +++ b/pyatlan_v9/model/assets/cassandra_related.py @@ -0,0 +1,303 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Cassandra module. + +This module contains all Related{Type} classes for the Cassandra type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedNoSQL +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedCassandra", + "RelatedCassandraColumn", + "RelatedCassandraIndex", + "RelatedCassandraKeyspace", + "RelatedCassandraTable", + "RelatedCassandraView", +] + + +class RelatedCassandra(RelatedNoSQL): + """ + Related entity reference for Cassandra assets. + + Extends RelatedNoSQL with Cassandra-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Cassandra" so it serializes correctly + + cassandra_keyspace_name: Union[str, None, UnsetType] = UNSET + """Name of the keyspace for the Cassandra asset.""" + + cassandra_table_name: Union[str, None, UnsetType] = UNSET + """Name of the table for the Cassandra asset.""" + + cassandra_view_name: Union[str, None, UnsetType] = UNSET + """Name of view for Cassandra asset""" + + cassandra_table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of table for Cassandra asset""" + + cassandra_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of view for Cassandra asset""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Cassandra" + + +class RelatedCassandraColumn(RelatedCassandra): + """ + Related entity reference for CassandraColumn assets. + + Extends RelatedCassandra with CassandraColumn-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "CassandraColumn" so it serializes correctly + + cassandra_column_clustering_order: Union[str, None, UnsetType] = UNSET + """Clustering order of the CassandraColumn.""" + + cassandra_column_is_partition_key: Union[bool, None, UnsetType] = UNSET + """Is the CassandraColumn partition key.""" + + cassandra_column_is_clustering_key: Union[bool, None, UnsetType] = UNSET + """Is the CassandraColumn clustering key.""" + + cassandra_column_kind: Union[str, None, UnsetType] = UNSET + """Kind of CassandraColumn (e.g. partition key, clustering column, etc).""" + + cassandra_column_position: Union[int, None, UnsetType] = UNSET + """Position of the CassandraColumn.""" + + cassandra_column_type: Union[str, None, UnsetType] = UNSET + """Type of the CassandraColumn.""" + + cassandra_column_is_static: Union[bool, None, UnsetType] = UNSET + """Indicates whether the CassandraColumn is static.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "CassandraColumn" + + +class RelatedCassandraIndex(RelatedCassandra): + """ + Related entity reference for CassandraIndex assets. + + Extends RelatedCassandra with CassandraIndex-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "CassandraIndex" so it serializes correctly + + cassandra_index_kind: Union[str, None, UnsetType] = UNSET + """Kind of index (e.g. COMPOSITES).""" + + cassandra_index_options: Union[Dict[str, str], None, UnsetType] = UNSET + """Options for the index.""" + + cassandra_index_query: Union[str, None, UnsetType] = UNSET + """Query used to create the index.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "CassandraIndex" + + +class RelatedCassandraKeyspace(RelatedCassandra): + """ + Related entity reference for CassandraKeyspace assets. + + Extends RelatedCassandra with CassandraKeyspace-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "CassandraKeyspace" so it serializes correctly + + cassandra_keyspace_durable_writes: Union[bool, None, UnsetType] = UNSET + """Indicates whether durable writes are enabled for the CassandraKeyspace.""" + + cassandra_keyspace_replication: Union[Dict[str, str], None, UnsetType] = UNSET + """Replication class for the CassandraKeyspace.""" + + cassandra_keyspace_virtual: Union[bool, None, UnsetType] = UNSET + """Indicates whether the CassandraKeyspace is virtual.""" + + cassandra_keyspace_query: Union[str, None, UnsetType] = UNSET + """Query associated with the CassandraKeyspace.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "CassandraKeyspace" + + +class RelatedCassandraTable(RelatedCassandra): + """ + Related entity reference for CassandraTable assets. + + Extends RelatedCassandra with CassandraTable-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "CassandraTable" so it serializes correctly + + cassandra_table_bloom_filter_fp_chance: Union[float, None, UnsetType] = ( + msgspec.field(default=UNSET, name="cassandraTableBloomFilterFPChance") + ) + """Bloom filter false positive chance for the CassandraTable.""" + + cassandra_table_caching: Union[Dict[str, str], None, UnsetType] = UNSET + """Caching behavior in Cassandra.""" + + cassandra_table_comment: Union[str, None, UnsetType] = UNSET + """Comment describing the CassandraTable's purpose or usage in Cassandra.""" + + cassandra_table_compaction: Union[Dict[str, str], None, UnsetType] = UNSET + """Compaction used for the CassandraTable in Cassandra.""" + + cassandra_table_compression: Union[Dict[str, str], None, UnsetType] = UNSET + """Compression used for the CassandraTable in Cassandra.""" + + cassandra_table_crc_check_chance: Union[float, None, UnsetType] = msgspec.field( + default=UNSET, name="cassandraTableCRCCheckChance" + ) + """CRC check chance for the CassandraTable.""" + + cassandra_table_dc_local_read_repair_chance: Union[float, None, UnsetType] = ( + msgspec.field(default=UNSET, name="cassandraTableDCLocalReadRepairChance") + ) + """Local read repair chance in Cassandra.""" + + cassandra_table_default_ttl: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="cassandraTableDefaultTTL" + ) + """Default time-to-live for the CassandraTable in Cassandra.""" + + cassandra_table_flags: Union[List[str], None, UnsetType] = UNSET + """Flags associated with the CassandraTable.""" + + cassandra_table_gc_grace_seconds: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="cassandraTableGCGraceSeconds" + ) + """Grace period for garbage collection in the CassandraTable.""" + + cassandra_table_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the CassandraTable.""" + + cassandra_table_max_index_interval: Union[int, None, UnsetType] = UNSET + """Maximum index interval for the CassandraTable.""" + + cassandra_table_memtable_flush_period_in_ms: Union[int, None, UnsetType] = UNSET + """Memtable flush period for the CassandraTable (in milliseconds).""" + + cassandra_table_min_index_interval: Union[int, None, UnsetType] = UNSET + """Minimum index interval for the CassandraTable.""" + + cassandra_table_read_repair_chance: Union[float, None, UnsetType] = UNSET + """Read repair chance for the CassandraTable.""" + + cassandra_table_speculative_retry: Union[str, None, UnsetType] = UNSET + """Speculative retry setting for the CassandraTable.""" + + cassandra_table_virtual: Union[bool, None, UnsetType] = UNSET + """Indicates whether the CassandraTable is virtual.""" + + cassandra_table_query: Union[str, None, UnsetType] = UNSET + """Query used to create the CassandraTable in Cassandra.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "CassandraTable" + + +class RelatedCassandraView(RelatedCassandra): + """ + Related entity reference for CassandraView assets. + + Extends RelatedCassandra with CassandraView-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "CassandraView" so it serializes correctly + + cassandra_view_table_id: Union[str, None, UnsetType] = UNSET + """ID of the base table in the CassandraView.""" + + cassandra_view_bloom_filter_fp_chance: Union[float, None, UnsetType] = ( + msgspec.field(default=UNSET, name="cassandraViewBloomFilterFPChance") + ) + """False positive chance for the Bloom filter in the CassandraView.""" + + cassandra_view_caching: Union[Dict[str, str], None, UnsetType] = UNSET + """Caching configuration in the CassandraView.""" + + cassandra_view_comment: Union[str, None, UnsetType] = UNSET + """Comment describing the CassandraView.""" + + cassandra_view_compaction: Union[Dict[str, str], None, UnsetType] = UNSET + """Compaction for the CassandraView.""" + + cassandra_view_crc_check_chance: Union[float, None, UnsetType] = msgspec.field( + default=UNSET, name="cassandraViewCRCCheckChance" + ) + """CRC check chance for the CassandraView.""" + + cassandra_view_dc_local_read_repair_chance: Union[float, None, UnsetType] = ( + msgspec.field(default=UNSET, name="cassandraViewDCLocalReadRepairChance") + ) + """DC-local read repair chance for the CassandraView.""" + + cassandra_view_default_ttl: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="cassandraViewDefaultTTL" + ) + """Default time-to-live (TTL) for the CassandraView.""" + + cassandra_view_gc_grace_seconds: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="cassandraViewGCGraceSeconds" + ) + """Grace period for garbage collection in the CassandraView.""" + + cassandra_view_include_all_columns: Union[bool, None, UnsetType] = UNSET + """Whether to include all columns in the CassandraView.""" + + cassandra_view_max_index_interval: Union[int, None, UnsetType] = UNSET + """Maximum index interval for the CassandraView.""" + + cassandra_view_membtable_flush_period_in_ms: Union[int, None, UnsetType] = ( + msgspec.field(default=UNSET, name="cassandraViewMembtableFlushPeriodInMS") + ) + """Memtable flush period (in milliseconds) for the CassandraView.""" + + cassandra_view_min_index_interval: Union[int, None, UnsetType] = UNSET + """Minimum index interval for the CassandraView.""" + + cassandra_view_read_repair_interval: Union[int, None, UnsetType] = UNSET + """Read repair interval for the CassandraView.""" + + cassandra_view_query: Union[str, None, UnsetType] = UNSET + """Query used in the CassandraView.""" + + cassandra_view_where_clause: Union[str, None, UnsetType] = UNSET + """Where clause used for the CassandraView query.""" + + cassandra_view_speculative_retry: Union[str, None, UnsetType] = UNSET + """SpeculativeRetry setting for the CassandraView.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "CassandraView" diff --git a/pyatlan_v9/model/assets/cassandra_table.py b/pyatlan_v9/model/assets/cassandra_table.py new file mode 100644 index 000000000..916adf2e5 --- /dev/null +++ b/pyatlan_v9/model/assets/cassandra_table.py @@ -0,0 +1,912 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +CassandraTable asset model with flattened inheritance. + +This module provides: +- CassandraTable: Flat asset class (easy to use) +- CassandraTableAttributes: Nested attributes struct (extends AssetAttributes) +- CassandraTableNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .cassandra_related import ( + RelatedCassandraColumn, + RelatedCassandraIndex, + RelatedCassandraKeyspace, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class CassandraTable(Asset): + """ + Instances of a CassandraTable in Atlan. + """ + + CASSANDRA_TABLE_BLOOM_FILTER_FP_CHANCE: ClassVar[Any] = None + CASSANDRA_TABLE_CACHING: ClassVar[Any] = None + CASSANDRA_TABLE_COMMENT: ClassVar[Any] = None + CASSANDRA_TABLE_COMPACTION: ClassVar[Any] = None + CASSANDRA_TABLE_COMPRESSION: ClassVar[Any] = None + CASSANDRA_TABLE_CRC_CHECK_CHANCE: ClassVar[Any] = None + CASSANDRA_TABLE_DC_LOCAL_READ_REPAIR_CHANCE: ClassVar[Any] = None + CASSANDRA_TABLE_DEFAULT_TTL: ClassVar[Any] = None + CASSANDRA_TABLE_FLAGS: ClassVar[Any] = None + CASSANDRA_TABLE_GC_GRACE_SECONDS: ClassVar[Any] = None + CASSANDRA_TABLE_ID: ClassVar[Any] = None + CASSANDRA_TABLE_MAX_INDEX_INTERVAL: ClassVar[Any] = None + CASSANDRA_TABLE_MEMTABLE_FLUSH_PERIOD_IN_MS: ClassVar[Any] = None + CASSANDRA_TABLE_MIN_INDEX_INTERVAL: ClassVar[Any] = None + CASSANDRA_TABLE_READ_REPAIR_CHANCE: ClassVar[Any] = None + CASSANDRA_TABLE_SPECULATIVE_RETRY: ClassVar[Any] = None + CASSANDRA_TABLE_VIRTUAL: ClassVar[Any] = None + CASSANDRA_TABLE_QUERY: ClassVar[Any] = None + CASSANDRA_KEYSPACE_NAME: ClassVar[Any] = None + CASSANDRA_TABLE_NAME: ClassVar[Any] = None + CASSANDRA_VIEW_NAME: ClassVar[Any] = None + CASSANDRA_TABLE_QUALIFIED_NAME: ClassVar[Any] = None + CASSANDRA_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + NO_SQL_SCHEMA_DEFINITION: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + CASSANDRA_COLUMNS: ClassVar[Any] = None + CASSANDRA_INDEXES: ClassVar[Any] = None + CASSANDRA_KEYSPACE: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "CassandraTable" + + cassandra_table_bloom_filter_fp_chance: Union[float, None, UnsetType] = ( + msgspec.field(default=UNSET, name="cassandraTableBloomFilterFPChance") + ) + """Bloom filter false positive chance for the CassandraTable.""" + + cassandra_table_caching: Union[Dict[str, str], None, UnsetType] = UNSET + """Caching behavior in Cassandra.""" + + cassandra_table_comment: Union[str, None, UnsetType] = UNSET + """Comment describing the CassandraTable's purpose or usage in Cassandra.""" + + cassandra_table_compaction: Union[Dict[str, str], None, UnsetType] = UNSET + """Compaction used for the CassandraTable in Cassandra.""" + + cassandra_table_compression: Union[Dict[str, str], None, UnsetType] = UNSET + """Compression used for the CassandraTable in Cassandra.""" + + cassandra_table_crc_check_chance: Union[float, None, UnsetType] = msgspec.field( + default=UNSET, name="cassandraTableCRCCheckChance" + ) + """CRC check chance for the CassandraTable.""" + + cassandra_table_dc_local_read_repair_chance: Union[float, None, UnsetType] = ( + msgspec.field(default=UNSET, name="cassandraTableDCLocalReadRepairChance") + ) + """Local read repair chance in Cassandra.""" + + cassandra_table_default_ttl: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="cassandraTableDefaultTTL" + ) + """Default time-to-live for the CassandraTable in Cassandra.""" + + cassandra_table_flags: Union[List[str], None, UnsetType] = UNSET + """Flags associated with the CassandraTable.""" + + cassandra_table_gc_grace_seconds: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="cassandraTableGCGraceSeconds" + ) + """Grace period for garbage collection in the CassandraTable.""" + + cassandra_table_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the CassandraTable.""" + + cassandra_table_max_index_interval: Union[int, None, UnsetType] = UNSET + """Maximum index interval for the CassandraTable.""" + + cassandra_table_memtable_flush_period_in_ms: Union[int, None, UnsetType] = UNSET + """Memtable flush period for the CassandraTable (in milliseconds).""" + + cassandra_table_min_index_interval: Union[int, None, UnsetType] = UNSET + """Minimum index interval for the CassandraTable.""" + + cassandra_table_read_repair_chance: Union[float, None, UnsetType] = UNSET + """Read repair chance for the CassandraTable.""" + + cassandra_table_speculative_retry: Union[str, None, UnsetType] = UNSET + """Speculative retry setting for the CassandraTable.""" + + cassandra_table_virtual: Union[bool, None, UnsetType] = UNSET + """Indicates whether the CassandraTable is virtual.""" + + cassandra_table_query: Union[str, None, UnsetType] = UNSET + """Query used to create the CassandraTable in Cassandra.""" + + cassandra_keyspace_name: Union[str, None, UnsetType] = UNSET + """Name of the keyspace for the Cassandra asset.""" + + cassandra_table_name: Union[str, None, UnsetType] = UNSET + """Name of the table for the Cassandra asset.""" + + cassandra_view_name: Union[str, None, UnsetType] = UNSET + """Name of view for Cassandra asset""" + + cassandra_table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of table for Cassandra asset""" + + cassandra_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of view for Cassandra asset""" + + no_sql_schema_definition: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="noSQLSchemaDefinition" + ) + """Represents attributes for describing the key schema for the table and indexes.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cassandra_columns: Union[List[RelatedCassandraColumn], None, UnsetType] = UNSET + """Indidivual columns contained in the table.""" + + cassandra_indexes: Union[List[RelatedCassandraIndex], None, UnsetType] = UNSET + """Individual indexes contained within the table.""" + + cassandra_keyspace: Union[RelatedCassandraKeyspace, None, UnsetType] = UNSET + """Keyspace containing the table.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "CassandraTable" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _cassandra_table_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> CassandraTable: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + CassandraTable instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _cassandra_table_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class CassandraTableAttributes(AssetAttributes): + """CassandraTable-specific attributes for nested API format.""" + + cassandra_table_bloom_filter_fp_chance: Union[float, None, UnsetType] = ( + msgspec.field(default=UNSET, name="cassandraTableBloomFilterFPChance") + ) + """Bloom filter false positive chance for the CassandraTable.""" + + cassandra_table_caching: Union[Dict[str, str], None, UnsetType] = UNSET + """Caching behavior in Cassandra.""" + + cassandra_table_comment: Union[str, None, UnsetType] = UNSET + """Comment describing the CassandraTable's purpose or usage in Cassandra.""" + + cassandra_table_compaction: Union[Dict[str, str], None, UnsetType] = UNSET + """Compaction used for the CassandraTable in Cassandra.""" + + cassandra_table_compression: Union[Dict[str, str], None, UnsetType] = UNSET + """Compression used for the CassandraTable in Cassandra.""" + + cassandra_table_crc_check_chance: Union[float, None, UnsetType] = msgspec.field( + default=UNSET, name="cassandraTableCRCCheckChance" + ) + """CRC check chance for the CassandraTable.""" + + cassandra_table_dc_local_read_repair_chance: Union[float, None, UnsetType] = ( + msgspec.field(default=UNSET, name="cassandraTableDCLocalReadRepairChance") + ) + """Local read repair chance in Cassandra.""" + + cassandra_table_default_ttl: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="cassandraTableDefaultTTL" + ) + """Default time-to-live for the CassandraTable in Cassandra.""" + + cassandra_table_flags: Union[List[str], None, UnsetType] = UNSET + """Flags associated with the CassandraTable.""" + + cassandra_table_gc_grace_seconds: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="cassandraTableGCGraceSeconds" + ) + """Grace period for garbage collection in the CassandraTable.""" + + cassandra_table_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the CassandraTable.""" + + cassandra_table_max_index_interval: Union[int, None, UnsetType] = UNSET + """Maximum index interval for the CassandraTable.""" + + cassandra_table_memtable_flush_period_in_ms: Union[int, None, UnsetType] = UNSET + """Memtable flush period for the CassandraTable (in milliseconds).""" + + cassandra_table_min_index_interval: Union[int, None, UnsetType] = UNSET + """Minimum index interval for the CassandraTable.""" + + cassandra_table_read_repair_chance: Union[float, None, UnsetType] = UNSET + """Read repair chance for the CassandraTable.""" + + cassandra_table_speculative_retry: Union[str, None, UnsetType] = UNSET + """Speculative retry setting for the CassandraTable.""" + + cassandra_table_virtual: Union[bool, None, UnsetType] = UNSET + """Indicates whether the CassandraTable is virtual.""" + + cassandra_table_query: Union[str, None, UnsetType] = UNSET + """Query used to create the CassandraTable in Cassandra.""" + + cassandra_keyspace_name: Union[str, None, UnsetType] = UNSET + """Name of the keyspace for the Cassandra asset.""" + + cassandra_table_name: Union[str, None, UnsetType] = UNSET + """Name of the table for the Cassandra asset.""" + + cassandra_view_name: Union[str, None, UnsetType] = UNSET + """Name of view for Cassandra asset""" + + cassandra_table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of table for Cassandra asset""" + + cassandra_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of view for Cassandra asset""" + + no_sql_schema_definition: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="noSQLSchemaDefinition" + ) + """Represents attributes for describing the key schema for the table and indexes.""" + + +class CassandraTableRelationshipAttributes(AssetRelationshipAttributes): + """CassandraTable-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cassandra_columns: Union[List[RelatedCassandraColumn], None, UnsetType] = UNSET + """Indidivual columns contained in the table.""" + + cassandra_indexes: Union[List[RelatedCassandraIndex], None, UnsetType] = UNSET + """Individual indexes contained within the table.""" + + cassandra_keyspace: Union[RelatedCassandraKeyspace, None, UnsetType] = UNSET + """Keyspace containing the table.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class CassandraTableNested(AssetNested): + """CassandraTable in nested API format for high-performance serialization.""" + + attributes: Union[CassandraTableAttributes, UnsetType] = UNSET + relationship_attributes: Union[CassandraTableRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + CassandraTableRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + CassandraTableRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_CASSANDRA_TABLE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "cassandra_columns", + "cassandra_indexes", + "cassandra_keyspace", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_cassandra_table_attrs( + attrs: CassandraTableAttributes, obj: CassandraTable +) -> None: + """Populate CassandraTable-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.cassandra_table_bloom_filter_fp_chance = ( + obj.cassandra_table_bloom_filter_fp_chance + ) + attrs.cassandra_table_caching = obj.cassandra_table_caching + attrs.cassandra_table_comment = obj.cassandra_table_comment + attrs.cassandra_table_compaction = obj.cassandra_table_compaction + attrs.cassandra_table_compression = obj.cassandra_table_compression + attrs.cassandra_table_crc_check_chance = obj.cassandra_table_crc_check_chance + attrs.cassandra_table_dc_local_read_repair_chance = ( + obj.cassandra_table_dc_local_read_repair_chance + ) + attrs.cassandra_table_default_ttl = obj.cassandra_table_default_ttl + attrs.cassandra_table_flags = obj.cassandra_table_flags + attrs.cassandra_table_gc_grace_seconds = obj.cassandra_table_gc_grace_seconds + attrs.cassandra_table_id = obj.cassandra_table_id + attrs.cassandra_table_max_index_interval = obj.cassandra_table_max_index_interval + attrs.cassandra_table_memtable_flush_period_in_ms = ( + obj.cassandra_table_memtable_flush_period_in_ms + ) + attrs.cassandra_table_min_index_interval = obj.cassandra_table_min_index_interval + attrs.cassandra_table_read_repair_chance = obj.cassandra_table_read_repair_chance + attrs.cassandra_table_speculative_retry = obj.cassandra_table_speculative_retry + attrs.cassandra_table_virtual = obj.cassandra_table_virtual + attrs.cassandra_table_query = obj.cassandra_table_query + attrs.cassandra_keyspace_name = obj.cassandra_keyspace_name + attrs.cassandra_table_name = obj.cassandra_table_name + attrs.cassandra_view_name = obj.cassandra_view_name + attrs.cassandra_table_qualified_name = obj.cassandra_table_qualified_name + attrs.cassandra_view_qualified_name = obj.cassandra_view_qualified_name + attrs.no_sql_schema_definition = obj.no_sql_schema_definition + + +def _extract_cassandra_table_attrs(attrs: CassandraTableAttributes) -> dict: + """Extract all CassandraTable attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["cassandra_table_bloom_filter_fp_chance"] = ( + attrs.cassandra_table_bloom_filter_fp_chance + ) + result["cassandra_table_caching"] = attrs.cassandra_table_caching + result["cassandra_table_comment"] = attrs.cassandra_table_comment + result["cassandra_table_compaction"] = attrs.cassandra_table_compaction + result["cassandra_table_compression"] = attrs.cassandra_table_compression + result["cassandra_table_crc_check_chance"] = attrs.cassandra_table_crc_check_chance + result["cassandra_table_dc_local_read_repair_chance"] = ( + attrs.cassandra_table_dc_local_read_repair_chance + ) + result["cassandra_table_default_ttl"] = attrs.cassandra_table_default_ttl + result["cassandra_table_flags"] = attrs.cassandra_table_flags + result["cassandra_table_gc_grace_seconds"] = attrs.cassandra_table_gc_grace_seconds + result["cassandra_table_id"] = attrs.cassandra_table_id + result["cassandra_table_max_index_interval"] = ( + attrs.cassandra_table_max_index_interval + ) + result["cassandra_table_memtable_flush_period_in_ms"] = ( + attrs.cassandra_table_memtable_flush_period_in_ms + ) + result["cassandra_table_min_index_interval"] = ( + attrs.cassandra_table_min_index_interval + ) + result["cassandra_table_read_repair_chance"] = ( + attrs.cassandra_table_read_repair_chance + ) + result["cassandra_table_speculative_retry"] = ( + attrs.cassandra_table_speculative_retry + ) + result["cassandra_table_virtual"] = attrs.cassandra_table_virtual + result["cassandra_table_query"] = attrs.cassandra_table_query + result["cassandra_keyspace_name"] = attrs.cassandra_keyspace_name + result["cassandra_table_name"] = attrs.cassandra_table_name + result["cassandra_view_name"] = attrs.cassandra_view_name + result["cassandra_table_qualified_name"] = attrs.cassandra_table_qualified_name + result["cassandra_view_qualified_name"] = attrs.cassandra_view_qualified_name + result["no_sql_schema_definition"] = attrs.no_sql_schema_definition + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _cassandra_table_to_nested(cassandra_table: CassandraTable) -> CassandraTableNested: + """Convert flat CassandraTable to nested format.""" + attrs = CassandraTableAttributes() + _populate_cassandra_table_attrs(attrs, cassandra_table) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + cassandra_table, + _CASSANDRA_TABLE_REL_FIELDS, + CassandraTableRelationshipAttributes, + ) + return CassandraTableNested( + guid=cassandra_table.guid, + type_name=cassandra_table.type_name, + status=cassandra_table.status, + version=cassandra_table.version, + create_time=cassandra_table.create_time, + update_time=cassandra_table.update_time, + created_by=cassandra_table.created_by, + updated_by=cassandra_table.updated_by, + classifications=cassandra_table.classifications, + classification_names=cassandra_table.classification_names, + meanings=cassandra_table.meanings, + labels=cassandra_table.labels, + business_attributes=cassandra_table.business_attributes, + custom_attributes=cassandra_table.custom_attributes, + pending_tasks=cassandra_table.pending_tasks, + proxy=cassandra_table.proxy, + is_incomplete=cassandra_table.is_incomplete, + provenance_type=cassandra_table.provenance_type, + home_id=cassandra_table.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _cassandra_table_from_nested(nested: CassandraTableNested) -> CassandraTable: + """Convert nested format to flat CassandraTable.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else CassandraTableAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _CASSANDRA_TABLE_REL_FIELDS, + CassandraTableRelationshipAttributes, + ) + return CassandraTable( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_cassandra_table_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _cassandra_table_to_nested_bytes( + cassandra_table: CassandraTable, serde: Serde +) -> bytes: + """Convert flat CassandraTable to nested JSON bytes.""" + return serde.encode(_cassandra_table_to_nested(cassandra_table)) + + +def _cassandra_table_from_nested_bytes(data: bytes, serde: Serde) -> CassandraTable: + """Convert nested JSON bytes to flat CassandraTable.""" + nested = serde.decode(data, CassandraTableNested) + return _cassandra_table_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, +) + +CassandraTable.CASSANDRA_TABLE_BLOOM_FILTER_FP_CHANCE = NumericField( + "cassandraTableBloomFilterFPChance", "cassandraTableBloomFilterFPChance" +) +CassandraTable.CASSANDRA_TABLE_CACHING = KeywordField( + "cassandraTableCaching", "cassandraTableCaching" +) +CassandraTable.CASSANDRA_TABLE_COMMENT = KeywordField( + "cassandraTableComment", "cassandraTableComment" +) +CassandraTable.CASSANDRA_TABLE_COMPACTION = KeywordField( + "cassandraTableCompaction", "cassandraTableCompaction" +) +CassandraTable.CASSANDRA_TABLE_COMPRESSION = KeywordField( + "cassandraTableCompression", "cassandraTableCompression" +) +CassandraTable.CASSANDRA_TABLE_CRC_CHECK_CHANCE = NumericField( + "cassandraTableCRCCheckChance", "cassandraTableCRCCheckChance" +) +CassandraTable.CASSANDRA_TABLE_DC_LOCAL_READ_REPAIR_CHANCE = NumericField( + "cassandraTableDCLocalReadRepairChance", "cassandraTableDCLocalReadRepairChance" +) +CassandraTable.CASSANDRA_TABLE_DEFAULT_TTL = NumericField( + "cassandraTableDefaultTTL", "cassandraTableDefaultTTL" +) +CassandraTable.CASSANDRA_TABLE_FLAGS = KeywordField( + "cassandraTableFlags", "cassandraTableFlags" +) +CassandraTable.CASSANDRA_TABLE_GC_GRACE_SECONDS = NumericField( + "cassandraTableGCGraceSeconds", "cassandraTableGCGraceSeconds" +) +CassandraTable.CASSANDRA_TABLE_ID = KeywordField("cassandraTableId", "cassandraTableId") +CassandraTable.CASSANDRA_TABLE_MAX_INDEX_INTERVAL = NumericField( + "cassandraTableMaxIndexInterval", "cassandraTableMaxIndexInterval" +) +CassandraTable.CASSANDRA_TABLE_MEMTABLE_FLUSH_PERIOD_IN_MS = NumericField( + "cassandraTableMemtableFlushPeriodInMs", "cassandraTableMemtableFlushPeriodInMs" +) +CassandraTable.CASSANDRA_TABLE_MIN_INDEX_INTERVAL = NumericField( + "cassandraTableMinIndexInterval", "cassandraTableMinIndexInterval" +) +CassandraTable.CASSANDRA_TABLE_READ_REPAIR_CHANCE = NumericField( + "cassandraTableReadRepairChance", "cassandraTableReadRepairChance" +) +CassandraTable.CASSANDRA_TABLE_SPECULATIVE_RETRY = KeywordField( + "cassandraTableSpeculativeRetry", "cassandraTableSpeculativeRetry" +) +CassandraTable.CASSANDRA_TABLE_VIRTUAL = BooleanField( + "cassandraTableVirtual", "cassandraTableVirtual" +) +CassandraTable.CASSANDRA_TABLE_QUERY = KeywordField( + "cassandraTableQuery", "cassandraTableQuery" +) +CassandraTable.CASSANDRA_KEYSPACE_NAME = KeywordField( + "cassandraKeyspaceName", "cassandraKeyspaceName" +) +CassandraTable.CASSANDRA_TABLE_NAME = KeywordField( + "cassandraTableName", "cassandraTableName" +) +CassandraTable.CASSANDRA_VIEW_NAME = KeywordField( + "cassandraViewName", "cassandraViewName" +) +CassandraTable.CASSANDRA_TABLE_QUALIFIED_NAME = KeywordField( + "cassandraTableQualifiedName", "cassandraTableQualifiedName" +) +CassandraTable.CASSANDRA_VIEW_QUALIFIED_NAME = KeywordField( + "cassandraViewQualifiedName", "cassandraViewQualifiedName" +) +CassandraTable.NO_SQL_SCHEMA_DEFINITION = KeywordField( + "noSQLSchemaDefinition", "noSQLSchemaDefinition" +) +CassandraTable.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +CassandraTable.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +CassandraTable.ANOMALO_CHECKS = RelationField("anomaloChecks") +CassandraTable.APPLICATION = RelationField("application") +CassandraTable.APPLICATION_FIELD = RelationField("applicationField") +CassandraTable.CASSANDRA_COLUMNS = RelationField("cassandraColumns") +CassandraTable.CASSANDRA_INDEXES = RelationField("cassandraIndexes") +CassandraTable.CASSANDRA_KEYSPACE = RelationField("cassandraKeyspace") +CassandraTable.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +CassandraTable.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +CassandraTable.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +CassandraTable.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +CassandraTable.METRICS = RelationField("metrics") +CassandraTable.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +CassandraTable.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +CassandraTable.MEANINGS = RelationField("meanings") +CassandraTable.MC_MONITORS = RelationField("mcMonitors") +CassandraTable.MC_INCIDENTS = RelationField("mcIncidents") +CassandraTable.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +CassandraTable.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +CassandraTable.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +CassandraTable.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +CassandraTable.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +CassandraTable.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +CassandraTable.FILES = RelationField("files") +CassandraTable.LINKS = RelationField("links") +CassandraTable.README = RelationField("readme") +CassandraTable.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +CassandraTable.SODA_CHECKS = RelationField("sodaChecks") +CassandraTable.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +CassandraTable.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/cassandra_view.py b/pyatlan_v9/model/assets/cassandra_view.py new file mode 100644 index 000000000..3932044c9 --- /dev/null +++ b/pyatlan_v9/model/assets/cassandra_view.py @@ -0,0 +1,889 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +CassandraView asset model with flattened inheritance. + +This module provides: +- CassandraView: Flat asset class (easy to use) +- CassandraViewAttributes: Nested attributes struct (extends AssetAttributes) +- CassandraViewNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .cassandra_related import RelatedCassandraColumn, RelatedCassandraKeyspace + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class CassandraView(Asset): + """ + Instances of an Module in Atlan. + """ + + CASSANDRA_VIEW_TABLE_ID: ClassVar[Any] = None + CASSANDRA_VIEW_BLOOM_FILTER_FP_CHANCE: ClassVar[Any] = None + CASSANDRA_VIEW_CACHING: ClassVar[Any] = None + CASSANDRA_VIEW_COMMENT: ClassVar[Any] = None + CASSANDRA_VIEW_COMPACTION: ClassVar[Any] = None + CASSANDRA_VIEW_CRC_CHECK_CHANCE: ClassVar[Any] = None + CASSANDRA_VIEW_DC_LOCAL_READ_REPAIR_CHANCE: ClassVar[Any] = None + CASSANDRA_VIEW_DEFAULT_TTL: ClassVar[Any] = None + CASSANDRA_VIEW_GC_GRACE_SECONDS: ClassVar[Any] = None + CASSANDRA_VIEW_INCLUDE_ALL_COLUMNS: ClassVar[Any] = None + CASSANDRA_VIEW_MAX_INDEX_INTERVAL: ClassVar[Any] = None + CASSANDRA_VIEW_MEMBTABLE_FLUSH_PERIOD_IN_MS: ClassVar[Any] = None + CASSANDRA_VIEW_MIN_INDEX_INTERVAL: ClassVar[Any] = None + CASSANDRA_VIEW_READ_REPAIR_INTERVAL: ClassVar[Any] = None + CASSANDRA_VIEW_QUERY: ClassVar[Any] = None + CASSANDRA_VIEW_WHERE_CLAUSE: ClassVar[Any] = None + CASSANDRA_VIEW_SPECULATIVE_RETRY: ClassVar[Any] = None + CASSANDRA_KEYSPACE_NAME: ClassVar[Any] = None + CASSANDRA_TABLE_NAME: ClassVar[Any] = None + CASSANDRA_VIEW_NAME: ClassVar[Any] = None + CASSANDRA_TABLE_QUALIFIED_NAME: ClassVar[Any] = None + CASSANDRA_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + NO_SQL_SCHEMA_DEFINITION: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + CASSANDRA_COLUMNS: ClassVar[Any] = None + CASSANDRA_KEYSPACE: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "CassandraView" + + cassandra_view_table_id: Union[str, None, UnsetType] = UNSET + """ID of the base table in the CassandraView.""" + + cassandra_view_bloom_filter_fp_chance: Union[float, None, UnsetType] = ( + msgspec.field(default=UNSET, name="cassandraViewBloomFilterFPChance") + ) + """False positive chance for the Bloom filter in the CassandraView.""" + + cassandra_view_caching: Union[Dict[str, str], None, UnsetType] = UNSET + """Caching configuration in the CassandraView.""" + + cassandra_view_comment: Union[str, None, UnsetType] = UNSET + """Comment describing the CassandraView.""" + + cassandra_view_compaction: Union[Dict[str, str], None, UnsetType] = UNSET + """Compaction for the CassandraView.""" + + cassandra_view_crc_check_chance: Union[float, None, UnsetType] = msgspec.field( + default=UNSET, name="cassandraViewCRCCheckChance" + ) + """CRC check chance for the CassandraView.""" + + cassandra_view_dc_local_read_repair_chance: Union[float, None, UnsetType] = ( + msgspec.field(default=UNSET, name="cassandraViewDCLocalReadRepairChance") + ) + """DC-local read repair chance for the CassandraView.""" + + cassandra_view_default_ttl: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="cassandraViewDefaultTTL" + ) + """Default time-to-live (TTL) for the CassandraView.""" + + cassandra_view_gc_grace_seconds: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="cassandraViewGCGraceSeconds" + ) + """Grace period for garbage collection in the CassandraView.""" + + cassandra_view_include_all_columns: Union[bool, None, UnsetType] = UNSET + """Whether to include all columns in the CassandraView.""" + + cassandra_view_max_index_interval: Union[int, None, UnsetType] = UNSET + """Maximum index interval for the CassandraView.""" + + cassandra_view_membtable_flush_period_in_ms: Union[int, None, UnsetType] = ( + msgspec.field(default=UNSET, name="cassandraViewMembtableFlushPeriodInMS") + ) + """Memtable flush period (in milliseconds) for the CassandraView.""" + + cassandra_view_min_index_interval: Union[int, None, UnsetType] = UNSET + """Minimum index interval for the CassandraView.""" + + cassandra_view_read_repair_interval: Union[int, None, UnsetType] = UNSET + """Read repair interval for the CassandraView.""" + + cassandra_view_query: Union[str, None, UnsetType] = UNSET + """Query used in the CassandraView.""" + + cassandra_view_where_clause: Union[str, None, UnsetType] = UNSET + """Where clause used for the CassandraView query.""" + + cassandra_view_speculative_retry: Union[str, None, UnsetType] = UNSET + """SpeculativeRetry setting for the CassandraView.""" + + cassandra_keyspace_name: Union[str, None, UnsetType] = UNSET + """Name of the keyspace for the Cassandra asset.""" + + cassandra_table_name: Union[str, None, UnsetType] = UNSET + """Name of the table for the Cassandra asset.""" + + cassandra_view_name: Union[str, None, UnsetType] = UNSET + """Name of view for Cassandra asset""" + + cassandra_table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of table for Cassandra asset""" + + cassandra_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of view for Cassandra asset""" + + no_sql_schema_definition: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="noSQLSchemaDefinition" + ) + """Represents attributes for describing the key schema for the table and indexes.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cassandra_columns: Union[List[RelatedCassandraColumn], None, UnsetType] = UNSET + """Individual columns contained in the view.""" + + cassandra_keyspace: Union[RelatedCassandraKeyspace, None, UnsetType] = UNSET + """Keyspace containing the view.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "CassandraView" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _cassandra_view_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> CassandraView: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + CassandraView instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _cassandra_view_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class CassandraViewAttributes(AssetAttributes): + """CassandraView-specific attributes for nested API format.""" + + cassandra_view_table_id: Union[str, None, UnsetType] = UNSET + """ID of the base table in the CassandraView.""" + + cassandra_view_bloom_filter_fp_chance: Union[float, None, UnsetType] = ( + msgspec.field(default=UNSET, name="cassandraViewBloomFilterFPChance") + ) + """False positive chance for the Bloom filter in the CassandraView.""" + + cassandra_view_caching: Union[Dict[str, str], None, UnsetType] = UNSET + """Caching configuration in the CassandraView.""" + + cassandra_view_comment: Union[str, None, UnsetType] = UNSET + """Comment describing the CassandraView.""" + + cassandra_view_compaction: Union[Dict[str, str], None, UnsetType] = UNSET + """Compaction for the CassandraView.""" + + cassandra_view_crc_check_chance: Union[float, None, UnsetType] = msgspec.field( + default=UNSET, name="cassandraViewCRCCheckChance" + ) + """CRC check chance for the CassandraView.""" + + cassandra_view_dc_local_read_repair_chance: Union[float, None, UnsetType] = ( + msgspec.field(default=UNSET, name="cassandraViewDCLocalReadRepairChance") + ) + """DC-local read repair chance for the CassandraView.""" + + cassandra_view_default_ttl: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="cassandraViewDefaultTTL" + ) + """Default time-to-live (TTL) for the CassandraView.""" + + cassandra_view_gc_grace_seconds: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="cassandraViewGCGraceSeconds" + ) + """Grace period for garbage collection in the CassandraView.""" + + cassandra_view_include_all_columns: Union[bool, None, UnsetType] = UNSET + """Whether to include all columns in the CassandraView.""" + + cassandra_view_max_index_interval: Union[int, None, UnsetType] = UNSET + """Maximum index interval for the CassandraView.""" + + cassandra_view_membtable_flush_period_in_ms: Union[int, None, UnsetType] = ( + msgspec.field(default=UNSET, name="cassandraViewMembtableFlushPeriodInMS") + ) + """Memtable flush period (in milliseconds) for the CassandraView.""" + + cassandra_view_min_index_interval: Union[int, None, UnsetType] = UNSET + """Minimum index interval for the CassandraView.""" + + cassandra_view_read_repair_interval: Union[int, None, UnsetType] = UNSET + """Read repair interval for the CassandraView.""" + + cassandra_view_query: Union[str, None, UnsetType] = UNSET + """Query used in the CassandraView.""" + + cassandra_view_where_clause: Union[str, None, UnsetType] = UNSET + """Where clause used for the CassandraView query.""" + + cassandra_view_speculative_retry: Union[str, None, UnsetType] = UNSET + """SpeculativeRetry setting for the CassandraView.""" + + cassandra_keyspace_name: Union[str, None, UnsetType] = UNSET + """Name of the keyspace for the Cassandra asset.""" + + cassandra_table_name: Union[str, None, UnsetType] = UNSET + """Name of the table for the Cassandra asset.""" + + cassandra_view_name: Union[str, None, UnsetType] = UNSET + """Name of view for Cassandra asset""" + + cassandra_table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of table for Cassandra asset""" + + cassandra_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of view for Cassandra asset""" + + no_sql_schema_definition: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="noSQLSchemaDefinition" + ) + """Represents attributes for describing the key schema for the table and indexes.""" + + +class CassandraViewRelationshipAttributes(AssetRelationshipAttributes): + """CassandraView-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cassandra_columns: Union[List[RelatedCassandraColumn], None, UnsetType] = UNSET + """Individual columns contained in the view.""" + + cassandra_keyspace: Union[RelatedCassandraKeyspace, None, UnsetType] = UNSET + """Keyspace containing the view.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class CassandraViewNested(AssetNested): + """CassandraView in nested API format for high-performance serialization.""" + + attributes: Union[CassandraViewAttributes, UnsetType] = UNSET + relationship_attributes: Union[CassandraViewRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + CassandraViewRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + CassandraViewRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_CASSANDRA_VIEW_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "cassandra_columns", + "cassandra_keyspace", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_cassandra_view_attrs( + attrs: CassandraViewAttributes, obj: CassandraView +) -> None: + """Populate CassandraView-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.cassandra_view_table_id = obj.cassandra_view_table_id + attrs.cassandra_view_bloom_filter_fp_chance = ( + obj.cassandra_view_bloom_filter_fp_chance + ) + attrs.cassandra_view_caching = obj.cassandra_view_caching + attrs.cassandra_view_comment = obj.cassandra_view_comment + attrs.cassandra_view_compaction = obj.cassandra_view_compaction + attrs.cassandra_view_crc_check_chance = obj.cassandra_view_crc_check_chance + attrs.cassandra_view_dc_local_read_repair_chance = ( + obj.cassandra_view_dc_local_read_repair_chance + ) + attrs.cassandra_view_default_ttl = obj.cassandra_view_default_ttl + attrs.cassandra_view_gc_grace_seconds = obj.cassandra_view_gc_grace_seconds + attrs.cassandra_view_include_all_columns = obj.cassandra_view_include_all_columns + attrs.cassandra_view_max_index_interval = obj.cassandra_view_max_index_interval + attrs.cassandra_view_membtable_flush_period_in_ms = ( + obj.cassandra_view_membtable_flush_period_in_ms + ) + attrs.cassandra_view_min_index_interval = obj.cassandra_view_min_index_interval + attrs.cassandra_view_read_repair_interval = obj.cassandra_view_read_repair_interval + attrs.cassandra_view_query = obj.cassandra_view_query + attrs.cassandra_view_where_clause = obj.cassandra_view_where_clause + attrs.cassandra_view_speculative_retry = obj.cassandra_view_speculative_retry + attrs.cassandra_keyspace_name = obj.cassandra_keyspace_name + attrs.cassandra_table_name = obj.cassandra_table_name + attrs.cassandra_view_name = obj.cassandra_view_name + attrs.cassandra_table_qualified_name = obj.cassandra_table_qualified_name + attrs.cassandra_view_qualified_name = obj.cassandra_view_qualified_name + attrs.no_sql_schema_definition = obj.no_sql_schema_definition + + +def _extract_cassandra_view_attrs(attrs: CassandraViewAttributes) -> dict: + """Extract all CassandraView attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["cassandra_view_table_id"] = attrs.cassandra_view_table_id + result["cassandra_view_bloom_filter_fp_chance"] = ( + attrs.cassandra_view_bloom_filter_fp_chance + ) + result["cassandra_view_caching"] = attrs.cassandra_view_caching + result["cassandra_view_comment"] = attrs.cassandra_view_comment + result["cassandra_view_compaction"] = attrs.cassandra_view_compaction + result["cassandra_view_crc_check_chance"] = attrs.cassandra_view_crc_check_chance + result["cassandra_view_dc_local_read_repair_chance"] = ( + attrs.cassandra_view_dc_local_read_repair_chance + ) + result["cassandra_view_default_ttl"] = attrs.cassandra_view_default_ttl + result["cassandra_view_gc_grace_seconds"] = attrs.cassandra_view_gc_grace_seconds + result["cassandra_view_include_all_columns"] = ( + attrs.cassandra_view_include_all_columns + ) + result["cassandra_view_max_index_interval"] = ( + attrs.cassandra_view_max_index_interval + ) + result["cassandra_view_membtable_flush_period_in_ms"] = ( + attrs.cassandra_view_membtable_flush_period_in_ms + ) + result["cassandra_view_min_index_interval"] = ( + attrs.cassandra_view_min_index_interval + ) + result["cassandra_view_read_repair_interval"] = ( + attrs.cassandra_view_read_repair_interval + ) + result["cassandra_view_query"] = attrs.cassandra_view_query + result["cassandra_view_where_clause"] = attrs.cassandra_view_where_clause + result["cassandra_view_speculative_retry"] = attrs.cassandra_view_speculative_retry + result["cassandra_keyspace_name"] = attrs.cassandra_keyspace_name + result["cassandra_table_name"] = attrs.cassandra_table_name + result["cassandra_view_name"] = attrs.cassandra_view_name + result["cassandra_table_qualified_name"] = attrs.cassandra_table_qualified_name + result["cassandra_view_qualified_name"] = attrs.cassandra_view_qualified_name + result["no_sql_schema_definition"] = attrs.no_sql_schema_definition + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _cassandra_view_to_nested(cassandra_view: CassandraView) -> CassandraViewNested: + """Convert flat CassandraView to nested format.""" + attrs = CassandraViewAttributes() + _populate_cassandra_view_attrs(attrs, cassandra_view) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + cassandra_view, _CASSANDRA_VIEW_REL_FIELDS, CassandraViewRelationshipAttributes + ) + return CassandraViewNested( + guid=cassandra_view.guid, + type_name=cassandra_view.type_name, + status=cassandra_view.status, + version=cassandra_view.version, + create_time=cassandra_view.create_time, + update_time=cassandra_view.update_time, + created_by=cassandra_view.created_by, + updated_by=cassandra_view.updated_by, + classifications=cassandra_view.classifications, + classification_names=cassandra_view.classification_names, + meanings=cassandra_view.meanings, + labels=cassandra_view.labels, + business_attributes=cassandra_view.business_attributes, + custom_attributes=cassandra_view.custom_attributes, + pending_tasks=cassandra_view.pending_tasks, + proxy=cassandra_view.proxy, + is_incomplete=cassandra_view.is_incomplete, + provenance_type=cassandra_view.provenance_type, + home_id=cassandra_view.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _cassandra_view_from_nested(nested: CassandraViewNested) -> CassandraView: + """Convert nested format to flat CassandraView.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else CassandraViewAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _CASSANDRA_VIEW_REL_FIELDS, + CassandraViewRelationshipAttributes, + ) + return CassandraView( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_cassandra_view_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _cassandra_view_to_nested_bytes( + cassandra_view: CassandraView, serde: Serde +) -> bytes: + """Convert flat CassandraView to nested JSON bytes.""" + return serde.encode(_cassandra_view_to_nested(cassandra_view)) + + +def _cassandra_view_from_nested_bytes(data: bytes, serde: Serde) -> CassandraView: + """Convert nested JSON bytes to flat CassandraView.""" + nested = serde.decode(data, CassandraViewNested) + return _cassandra_view_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, +) + +CassandraView.CASSANDRA_VIEW_TABLE_ID = KeywordField( + "cassandraViewTableId", "cassandraViewTableId" +) +CassandraView.CASSANDRA_VIEW_BLOOM_FILTER_FP_CHANCE = NumericField( + "cassandraViewBloomFilterFPChance", "cassandraViewBloomFilterFPChance" +) +CassandraView.CASSANDRA_VIEW_CACHING = KeywordField( + "cassandraViewCaching", "cassandraViewCaching" +) +CassandraView.CASSANDRA_VIEW_COMMENT = KeywordField( + "cassandraViewComment", "cassandraViewComment" +) +CassandraView.CASSANDRA_VIEW_COMPACTION = KeywordField( + "cassandraViewCompaction", "cassandraViewCompaction" +) +CassandraView.CASSANDRA_VIEW_CRC_CHECK_CHANCE = NumericField( + "cassandraViewCRCCheckChance", "cassandraViewCRCCheckChance" +) +CassandraView.CASSANDRA_VIEW_DC_LOCAL_READ_REPAIR_CHANCE = NumericField( + "cassandraViewDCLocalReadRepairChance", "cassandraViewDCLocalReadRepairChance" +) +CassandraView.CASSANDRA_VIEW_DEFAULT_TTL = NumericField( + "cassandraViewDefaultTTL", "cassandraViewDefaultTTL" +) +CassandraView.CASSANDRA_VIEW_GC_GRACE_SECONDS = NumericField( + "cassandraViewGCGraceSeconds", "cassandraViewGCGraceSeconds" +) +CassandraView.CASSANDRA_VIEW_INCLUDE_ALL_COLUMNS = BooleanField( + "cassandraViewIncludeAllColumns", "cassandraViewIncludeAllColumns" +) +CassandraView.CASSANDRA_VIEW_MAX_INDEX_INTERVAL = NumericField( + "cassandraViewMaxIndexInterval", "cassandraViewMaxIndexInterval" +) +CassandraView.CASSANDRA_VIEW_MEMBTABLE_FLUSH_PERIOD_IN_MS = NumericField( + "cassandraViewMembtableFlushPeriodInMS", "cassandraViewMembtableFlushPeriodInMS" +) +CassandraView.CASSANDRA_VIEW_MIN_INDEX_INTERVAL = NumericField( + "cassandraViewMinIndexInterval", "cassandraViewMinIndexInterval" +) +CassandraView.CASSANDRA_VIEW_READ_REPAIR_INTERVAL = NumericField( + "cassandraViewReadRepairInterval", "cassandraViewReadRepairInterval" +) +CassandraView.CASSANDRA_VIEW_QUERY = KeywordField( + "cassandraViewQuery", "cassandraViewQuery" +) +CassandraView.CASSANDRA_VIEW_WHERE_CLAUSE = KeywordField( + "cassandraViewWhereClause", "cassandraViewWhereClause" +) +CassandraView.CASSANDRA_VIEW_SPECULATIVE_RETRY = KeywordField( + "cassandraViewSpeculativeRetry", "cassandraViewSpeculativeRetry" +) +CassandraView.CASSANDRA_KEYSPACE_NAME = KeywordField( + "cassandraKeyspaceName", "cassandraKeyspaceName" +) +CassandraView.CASSANDRA_TABLE_NAME = KeywordField( + "cassandraTableName", "cassandraTableName" +) +CassandraView.CASSANDRA_VIEW_NAME = KeywordField( + "cassandraViewName", "cassandraViewName" +) +CassandraView.CASSANDRA_TABLE_QUALIFIED_NAME = KeywordField( + "cassandraTableQualifiedName", "cassandraTableQualifiedName" +) +CassandraView.CASSANDRA_VIEW_QUALIFIED_NAME = KeywordField( + "cassandraViewQualifiedName", "cassandraViewQualifiedName" +) +CassandraView.NO_SQL_SCHEMA_DEFINITION = KeywordField( + "noSQLSchemaDefinition", "noSQLSchemaDefinition" +) +CassandraView.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +CassandraView.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +CassandraView.ANOMALO_CHECKS = RelationField("anomaloChecks") +CassandraView.APPLICATION = RelationField("application") +CassandraView.APPLICATION_FIELD = RelationField("applicationField") +CassandraView.CASSANDRA_COLUMNS = RelationField("cassandraColumns") +CassandraView.CASSANDRA_KEYSPACE = RelationField("cassandraKeyspace") +CassandraView.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +CassandraView.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +CassandraView.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +CassandraView.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +CassandraView.METRICS = RelationField("metrics") +CassandraView.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +CassandraView.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +CassandraView.MEANINGS = RelationField("meanings") +CassandraView.MC_MONITORS = RelationField("mcMonitors") +CassandraView.MC_INCIDENTS = RelationField("mcIncidents") +CassandraView.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +CassandraView.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +CassandraView.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +CassandraView.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +CassandraView.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +CassandraView.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +CassandraView.FILES = RelationField("files") +CassandraView.LINKS = RelationField("links") +CassandraView.README = RelationField("readme") +CassandraView.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +CassandraView.SODA_CHECKS = RelationField("sodaChecks") +CassandraView.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +CassandraView.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/catalog.py b/pyatlan_v9/model/assets/catalog.py new file mode 100644 index 000000000..bc737620e --- /dev/null +++ b/pyatlan_v9/model/assets/catalog.py @@ -0,0 +1,523 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Catalog asset model with flattened inheritance. + +This module provides: +- Catalog: Flat asset class (easy to use) +- CatalogAttributes: Nested attributes struct (extends AssetAttributes) +- CatalogNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Catalog(Asset): + """ + Base class for catalog assets. Catalog assets include any asset that can participate in lineage. + """ + + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Catalog" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Catalog" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _catalog_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Catalog: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Catalog instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _catalog_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class CatalogAttributes(AssetAttributes): + """Catalog-specific attributes for nested API format.""" + + pass + + +class CatalogRelationshipAttributes(AssetRelationshipAttributes): + """Catalog-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class CatalogNested(AssetNested): + """Catalog in nested API format for high-performance serialization.""" + + attributes: Union[CatalogAttributes, UnsetType] = UNSET + relationship_attributes: Union[CatalogRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[CatalogRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[CatalogRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_CATALOG_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_catalog_attrs(attrs: CatalogAttributes, obj: Catalog) -> None: + """Populate Catalog-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + + +def _extract_catalog_attrs(attrs: CatalogAttributes) -> dict: + """Extract all Catalog attributes from the attrs struct into a flat dict.""" + return _extract_asset_attrs(attrs) + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _catalog_to_nested(catalog: Catalog) -> CatalogNested: + """Convert flat Catalog to nested format.""" + attrs = CatalogAttributes() + _populate_catalog_attrs(attrs, catalog) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + catalog, _CATALOG_REL_FIELDS, CatalogRelationshipAttributes + ) + return CatalogNested( + guid=catalog.guid, + type_name=catalog.type_name, + status=catalog.status, + version=catalog.version, + create_time=catalog.create_time, + update_time=catalog.update_time, + created_by=catalog.created_by, + updated_by=catalog.updated_by, + classifications=catalog.classifications, + classification_names=catalog.classification_names, + meanings=catalog.meanings, + labels=catalog.labels, + business_attributes=catalog.business_attributes, + custom_attributes=catalog.custom_attributes, + pending_tasks=catalog.pending_tasks, + proxy=catalog.proxy, + is_incomplete=catalog.is_incomplete, + provenance_type=catalog.provenance_type, + home_id=catalog.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _catalog_from_nested(nested: CatalogNested) -> Catalog: + """Convert nested format to flat Catalog.""" + attrs = nested.attributes if nested.attributes is not UNSET else CatalogAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _CATALOG_REL_FIELDS, + CatalogRelationshipAttributes, + ) + return Catalog( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_catalog_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _catalog_to_nested_bytes(catalog: Catalog, serde: Serde) -> bytes: + """Convert flat Catalog to nested JSON bytes.""" + return serde.encode(_catalog_to_nested(catalog)) + + +def _catalog_from_nested_bytes(data: bytes, serde: Serde) -> Catalog: + """Convert nested JSON bytes to flat Catalog.""" + nested = serde.decode(data, CatalogNested) + return _catalog_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import RelationField # noqa: E402 + +Catalog.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Catalog.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Catalog.ANOMALO_CHECKS = RelationField("anomaloChecks") +Catalog.APPLICATION = RelationField("application") +Catalog.APPLICATION_FIELD = RelationField("applicationField") +Catalog.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Catalog.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Catalog.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Catalog.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Catalog.METRICS = RelationField("metrics") +Catalog.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Catalog.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Catalog.MEANINGS = RelationField("meanings") +Catalog.MC_MONITORS = RelationField("mcMonitors") +Catalog.MC_INCIDENTS = RelationField("mcIncidents") +Catalog.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Catalog.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Catalog.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Catalog.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Catalog.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Catalog.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Catalog.FILES = RelationField("files") +Catalog.LINKS = RelationField("links") +Catalog.README = RelationField("readme") +Catalog.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Catalog.SODA_CHECKS = RelationField("sodaChecks") +Catalog.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Catalog.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/catalog_related.py b/pyatlan_v9/model/assets/catalog_related.py new file mode 100644 index 000000000..31c3f3b60 --- /dev/null +++ b/pyatlan_v9/model/assets/catalog_related.py @@ -0,0 +1,140 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Catalog module. + +This module contains all Related{Type} classes for the Catalog type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .asset_related import RelatedAsset +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedCatalog", + "RelatedBI", + "RelatedEventStore", + "RelatedInsight", + "RelatedNoSQL", + "RelatedObjectStore", + "RelatedSaaS", +] + + +class RelatedCatalog(RelatedAsset): + """ + Related entity reference for Catalog assets. + + Extends RelatedAsset with Catalog-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Catalog" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Catalog" + + +class RelatedBI(RelatedCatalog): + """ + Related entity reference for BI assets. + + Extends RelatedCatalog with BI-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "BI" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "BI" + + +class RelatedEventStore(RelatedCatalog): + """ + Related entity reference for EventStore assets. + + Extends RelatedCatalog with EventStore-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "EventStore" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "EventStore" + + +class RelatedInsight(RelatedCatalog): + """ + Related entity reference for Insight assets. + + Extends RelatedCatalog with Insight-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Insight" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Insight" + + +class RelatedNoSQL(RelatedCatalog): + """ + Related entity reference for NoSQL assets. + + Extends RelatedCatalog with NoSQL-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "NoSQL" so it serializes correctly + + no_sql_schema_definition: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="noSQLSchemaDefinition" + ) + """Represents attributes for describing the key schema for the table and indexes.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "NoSQL" + + +class RelatedObjectStore(RelatedCatalog): + """ + Related entity reference for ObjectStore assets. + + Extends RelatedCatalog with ObjectStore-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "ObjectStore" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "ObjectStore" + + +class RelatedSaaS(RelatedCatalog): + """ + Related entity reference for SaaS assets. + + Extends RelatedCatalog with SaaS-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SaaS" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SaaS" diff --git a/pyatlan_v9/model/assets/cloud.py b/pyatlan_v9/model/assets/cloud.py new file mode 100644 index 000000000..79e970e44 --- /dev/null +++ b/pyatlan_v9/model/assets/cloud.py @@ -0,0 +1,438 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Cloud asset model with flattened inheritance. + +This module provides: +- Cloud: Flat asset class (easy to use) +- CloudAttributes: Nested attributes struct (extends AssetAttributes) +- CloudNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Cloud(Asset): + """ + Base class for cloud assets. + """ + + CLOUD_UNIFORM_RESOURCE_NAME: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Cloud" + + cloud_uniform_resource_name: Union[str, None, UnsetType] = UNSET + """Uniform resource name (URN) for the asset: AWS ARN, Google Cloud URI, Azure resource ID, Oracle OCID, and so on.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Cloud" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _cloud_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Cloud: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Cloud instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _cloud_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class CloudAttributes(AssetAttributes): + """Cloud-specific attributes for nested API format.""" + + cloud_uniform_resource_name: Union[str, None, UnsetType] = UNSET + """Uniform resource name (URN) for the asset: AWS ARN, Google Cloud URI, Azure resource ID, Oracle OCID, and so on.""" + + +class CloudRelationshipAttributes(AssetRelationshipAttributes): + """Cloud-specific relationship attributes for nested API format.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + +class CloudNested(AssetNested): + """Cloud in nested API format for high-performance serialization.""" + + attributes: Union[CloudAttributes, UnsetType] = UNSET + relationship_attributes: Union[CloudRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[CloudRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[CloudRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_CLOUD_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", +] + + +def _populate_cloud_attrs(attrs: CloudAttributes, obj: Cloud) -> None: + """Populate Cloud-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.cloud_uniform_resource_name = obj.cloud_uniform_resource_name + + +def _extract_cloud_attrs(attrs: CloudAttributes) -> dict: + """Extract all Cloud attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["cloud_uniform_resource_name"] = attrs.cloud_uniform_resource_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _cloud_to_nested(cloud: Cloud) -> CloudNested: + """Convert flat Cloud to nested format.""" + attrs = CloudAttributes() + _populate_cloud_attrs(attrs, cloud) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + cloud, _CLOUD_REL_FIELDS, CloudRelationshipAttributes + ) + return CloudNested( + guid=cloud.guid, + type_name=cloud.type_name, + status=cloud.status, + version=cloud.version, + create_time=cloud.create_time, + update_time=cloud.update_time, + created_by=cloud.created_by, + updated_by=cloud.updated_by, + classifications=cloud.classifications, + classification_names=cloud.classification_names, + meanings=cloud.meanings, + labels=cloud.labels, + business_attributes=cloud.business_attributes, + custom_attributes=cloud.custom_attributes, + pending_tasks=cloud.pending_tasks, + proxy=cloud.proxy, + is_incomplete=cloud.is_incomplete, + provenance_type=cloud.provenance_type, + home_id=cloud.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _cloud_from_nested(nested: CloudNested) -> Cloud: + """Convert nested format to flat Cloud.""" + attrs = nested.attributes if nested.attributes is not UNSET else CloudAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _CLOUD_REL_FIELDS, + CloudRelationshipAttributes, + ) + return Cloud( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_cloud_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _cloud_to_nested_bytes(cloud: Cloud, serde: Serde) -> bytes: + """Convert flat Cloud to nested JSON bytes.""" + return serde.encode(_cloud_to_nested(cloud)) + + +def _cloud_from_nested_bytes(data: bytes, serde: Serde) -> Cloud: + """Convert nested JSON bytes to flat Cloud.""" + nested = serde.decode(data, CloudNested) + return _cloud_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +Cloud.CLOUD_UNIFORM_RESOURCE_NAME = KeywordField( + "cloudUniformResourceName", "cloudUniformResourceName" +) +Cloud.ANOMALO_CHECKS = RelationField("anomaloChecks") +Cloud.APPLICATION = RelationField("application") +Cloud.APPLICATION_FIELD = RelationField("applicationField") +Cloud.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Cloud.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Cloud.METRICS = RelationField("metrics") +Cloud.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Cloud.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Cloud.MEANINGS = RelationField("meanings") +Cloud.MC_MONITORS = RelationField("mcMonitors") +Cloud.MC_INCIDENTS = RelationField("mcIncidents") +Cloud.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Cloud.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Cloud.FILES = RelationField("files") +Cloud.LINKS = RelationField("links") +Cloud.README = RelationField("readme") +Cloud.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Cloud.SODA_CHECKS = RelationField("sodaChecks") diff --git a/pyatlan_v9/model/assets/cloud_related.py b/pyatlan_v9/model/assets/cloud_related.py new file mode 100644 index 000000000..ab23076ed --- /dev/null +++ b/pyatlan_v9/model/assets/cloud_related.py @@ -0,0 +1,152 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Cloud module. + +This module contains all Related{Type} classes for the Cloud type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Any, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .asset_related import RelatedAsset +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedCloud", + "RelatedAWS", + "RelatedAzure", + "RelatedGoogle", +] + + +class RelatedCloud(RelatedAsset): + """ + Related entity reference for Cloud assets. + + Extends RelatedAsset with Cloud-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Cloud" so it serializes correctly + + cloud_uniform_resource_name: Union[str, None, UnsetType] = UNSET + """Uniform resource name (URN) for the asset: AWS ARN, Google Cloud URI, Azure resource ID, Oracle OCID, and so on.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Cloud" + + +class RelatedAWS(RelatedCloud): + """ + Related entity reference for AWS assets. + + Extends RelatedCloud with AWS-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "AWS" so it serializes correctly + + aws_arn: Union[str, None, UnsetType] = UNSET + """DEPRECATED: This legacy attribute must be unique across all AWS asset instances. This can create non-obvious edge cases for creating / updating assets, and we therefore recommended NOT using it. See and use cloudResourceName instead.""" + + aws_partition: Union[str, None, UnsetType] = UNSET + """Group of AWS region and service objects.""" + + aws_service: Union[str, None, UnsetType] = UNSET + """Type of service in which the asset exists.""" + + aws_region: Union[str, None, UnsetType] = UNSET + """Physical region where the data center in which the asset exists is clustered.""" + + aws_account_id: Union[str, None, UnsetType] = UNSET + """12-digit number that uniquely identifies an AWS account.""" + + aws_resource_id: Union[str, None, UnsetType] = UNSET + """Unique resource ID assigned when a new resource is created.""" + + aws_owner_name: Union[str, None, UnsetType] = UNSET + """Root user's name.""" + + aws_owner_id: Union[str, None, UnsetType] = UNSET + """Root user's ID.""" + + aws_tags: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of tags that have been applied to the asset in AWS.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "AWS" + + +class RelatedAzure(RelatedCloud): + """ + Related entity reference for Azure assets. + + Extends RelatedCloud with Azure-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Azure" so it serializes correctly + + azure_resource_id: Union[str, None, UnsetType] = UNSET + """Resource identifier of this asset in Azure.""" + + azure_location: Union[str, None, UnsetType] = UNSET + """Location of this asset in Azure.""" + + adls_account_secondary_location: Union[str, None, UnsetType] = UNSET + """Secondary location of the ADLS account.""" + + azure_tags: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """Tags that have been applied to this asset in Azure.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Azure" + + +class RelatedGoogle(RelatedCloud): + """ + Related entity reference for Google assets. + + Extends RelatedCloud with Google-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Google" so it serializes correctly + + google_service: Union[str, None, UnsetType] = UNSET + """Service in Google in which the asset exists.""" + + google_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which the asset exists.""" + + google_project_id: Union[str, None, UnsetType] = UNSET + """ID of the project in which the asset exists.""" + + cloud_project_number: Union[int, None, UnsetType] = UNSET + """Number of the project in which the asset exists.""" + + google_location: Union[str, None, UnsetType] = UNSET + """Location of this asset in Google.""" + + google_location_type: Union[str, None, UnsetType] = UNSET + """Type of location of this asset in Google.""" + + google_labels: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of labels that have been applied to the asset in Google.""" + + google_tags: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of tags that have been applied to the asset in Google.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Google" diff --git a/pyatlan_v9/model/assets/cognite.py b/pyatlan_v9/model/assets/cognite.py new file mode 100644 index 000000000..f9e52ed33 --- /dev/null +++ b/pyatlan_v9/model/assets/cognite.py @@ -0,0 +1,523 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Cognite asset model with flattened inheritance. + +This module provides: +- Cognite: Flat asset class (easy to use) +- CogniteAttributes: Nested attributes struct (extends AssetAttributes) +- CogniteNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Cognite(Asset): + """ + Base class for Cognite assets. + """ + + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Cognite" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Cognite" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _cognite_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Cognite: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Cognite instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _cognite_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class CogniteAttributes(AssetAttributes): + """Cognite-specific attributes for nested API format.""" + + pass + + +class CogniteRelationshipAttributes(AssetRelationshipAttributes): + """Cognite-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class CogniteNested(AssetNested): + """Cognite in nested API format for high-performance serialization.""" + + attributes: Union[CogniteAttributes, UnsetType] = UNSET + relationship_attributes: Union[CogniteRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[CogniteRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[CogniteRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_COGNITE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_cognite_attrs(attrs: CogniteAttributes, obj: Cognite) -> None: + """Populate Cognite-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + + +def _extract_cognite_attrs(attrs: CogniteAttributes) -> dict: + """Extract all Cognite attributes from the attrs struct into a flat dict.""" + return _extract_asset_attrs(attrs) + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _cognite_to_nested(cognite: Cognite) -> CogniteNested: + """Convert flat Cognite to nested format.""" + attrs = CogniteAttributes() + _populate_cognite_attrs(attrs, cognite) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + cognite, _COGNITE_REL_FIELDS, CogniteRelationshipAttributes + ) + return CogniteNested( + guid=cognite.guid, + type_name=cognite.type_name, + status=cognite.status, + version=cognite.version, + create_time=cognite.create_time, + update_time=cognite.update_time, + created_by=cognite.created_by, + updated_by=cognite.updated_by, + classifications=cognite.classifications, + classification_names=cognite.classification_names, + meanings=cognite.meanings, + labels=cognite.labels, + business_attributes=cognite.business_attributes, + custom_attributes=cognite.custom_attributes, + pending_tasks=cognite.pending_tasks, + proxy=cognite.proxy, + is_incomplete=cognite.is_incomplete, + provenance_type=cognite.provenance_type, + home_id=cognite.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _cognite_from_nested(nested: CogniteNested) -> Cognite: + """Convert nested format to flat Cognite.""" + attrs = nested.attributes if nested.attributes is not UNSET else CogniteAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _COGNITE_REL_FIELDS, + CogniteRelationshipAttributes, + ) + return Cognite( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_cognite_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _cognite_to_nested_bytes(cognite: Cognite, serde: Serde) -> bytes: + """Convert flat Cognite to nested JSON bytes.""" + return serde.encode(_cognite_to_nested(cognite)) + + +def _cognite_from_nested_bytes(data: bytes, serde: Serde) -> Cognite: + """Convert nested JSON bytes to flat Cognite.""" + nested = serde.decode(data, CogniteNested) + return _cognite_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import RelationField # noqa: E402 + +Cognite.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Cognite.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Cognite.ANOMALO_CHECKS = RelationField("anomaloChecks") +Cognite.APPLICATION = RelationField("application") +Cognite.APPLICATION_FIELD = RelationField("applicationField") +Cognite.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Cognite.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Cognite.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Cognite.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Cognite.METRICS = RelationField("metrics") +Cognite.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Cognite.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Cognite.MEANINGS = RelationField("meanings") +Cognite.MC_MONITORS = RelationField("mcMonitors") +Cognite.MC_INCIDENTS = RelationField("mcIncidents") +Cognite.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Cognite.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Cognite.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Cognite.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Cognite.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Cognite.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Cognite.FILES = RelationField("files") +Cognite.LINKS = RelationField("links") +Cognite.README = RelationField("readme") +Cognite.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Cognite.SODA_CHECKS = RelationField("sodaChecks") +Cognite.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Cognite.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/cognite3_d_model.py b/pyatlan_v9/model/assets/cognite3_d_model.py new file mode 100644 index 000000000..836081a5d --- /dev/null +++ b/pyatlan_v9/model/assets/cognite3_d_model.py @@ -0,0 +1,211 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Cognite3DModel asset model with flattened inheritance. + +This module provides: +- Cognite3DModel: Flat asset class (easy to use) +- Cognite3DModelAttributes: Nested attributes struct (extends AssetAttributes) +- Cognite3DModelNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Union + +from msgspec import UNSET, UnsetType + +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .asset import Asset, AssetAttributes, AssetNested, AssetRelationshipAttributes +from .cognite_related import RelatedCogniteAsset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Cognite3DModel(Asset): + """ + Instance of a Cognite 3D model in Atlan. + """ + + # Override type_name with Cognite3DModel-specific default + type_name: Union[str, UnsetType] = "Cognite3DModel" + + cognite_asset: Union[RelatedCogniteAsset, None, UnsetType] = UNSET + """Asset in which this 3D model exists.""" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return _cognite3_d_model_to_nested_bytes(self, serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + @staticmethod + def from_json( + json_data: Union[str, bytes], serde: Serde | None = None + ) -> "Cognite3DModel": + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Cognite3DModel instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _cognite3_d_model_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class Cognite3DModelAttributes(AssetAttributes): + """Cognite3DModel-specific attributes for nested API format.""" + + pass + + +class Cognite3DModelRelationshipAttributes(AssetRelationshipAttributes): + """Cognite3DModel-specific relationship attributes for nested API format.""" + + cognite_asset: Union[RelatedCogniteAsset, None, UnsetType] = UNSET + """Asset in which this 3D model exists.""" + + +class Cognite3DModelNested(AssetNested): + """Cognite3DModel in nested API format for high-performance serialization.""" + + attributes: Union[Cognite3DModelAttributes, UnsetType] = UNSET + relationship_attributes: Union[Cognite3DModelRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + Cognite3DModelRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + Cognite3DModelRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _cognite3_d_model_to_nested( + cognite3_d_model: Cognite3DModel, +) -> Cognite3DModelNested: + """Convert flat Cognite3DModel to nested format.""" + attrs = Cognite3DModelAttributes() + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + rel_fields: list[str] = ["cognite_asset"] + replace_rels, append_rels, remove_rels = categorize_relationships( + cognite3_d_model, rel_fields, Cognite3DModelRelationshipAttributes + ) + return Cognite3DModelNested( + guid=cognite3_d_model.guid, + type_name=cognite3_d_model.type_name, + status=cognite3_d_model.status, + version=cognite3_d_model.version, + create_time=cognite3_d_model.create_time, + update_time=cognite3_d_model.update_time, + created_by=cognite3_d_model.created_by, + updated_by=cognite3_d_model.updated_by, + classifications=cognite3_d_model.classifications, + classification_names=cognite3_d_model.classification_names, + meanings=cognite3_d_model.meanings, + labels=cognite3_d_model.labels, + business_attributes=cognite3_d_model.business_attributes, + custom_attributes=cognite3_d_model.custom_attributes, + pending_tasks=cognite3_d_model.pending_tasks, + proxy=cognite3_d_model.proxy, + is_incomplete=cognite3_d_model.is_incomplete, + provenance_type=cognite3_d_model.provenance_type, + home_id=cognite3_d_model.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _cognite3_d_model_from_nested(nested: Cognite3DModelNested) -> Cognite3DModel: + """Convert nested format to flat Cognite3DModel.""" + # Merge relationships from all three buckets + rel_fields: list[str] = ["cognite_asset"] + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + rel_fields, + Cognite3DModelRelationshipAttributes, + ) + return Cognite3DModel( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + # Merged relationship attributes + **merged_rels, + ) + + +def _cognite3_d_model_to_nested_bytes( + cognite3_d_model: Cognite3DModel, serde: Serde +) -> bytes: + """Convert flat Cognite3DModel to nested JSON bytes.""" + return serde.encode(_cognite3_d_model_to_nested(cognite3_d_model)) + + +def _cognite3_d_model_from_nested_bytes(data: bytes, serde: Serde) -> Cognite3DModel: + """Convert nested JSON bytes to flat Cognite3DModel.""" + nested = serde.decode(data, Cognite3DModelNested) + return _cognite3_d_model_from_nested(nested) diff --git a/pyatlan_v9/model/assets/cognite3d_model.py b/pyatlan_v9/model/assets/cognite3d_model.py new file mode 100644 index 000000000..a4df7ae0a --- /dev/null +++ b/pyatlan_v9/model/assets/cognite3d_model.py @@ -0,0 +1,555 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Cognite3DModel asset model with flattened inheritance. + +This module provides: +- Cognite3DModel: Flat asset class (easy to use) +- Cognite3DModelAttributes: Nested attributes struct (extends AssetAttributes) +- Cognite3DModelNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .cognite_related import RelatedCogniteAsset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Cognite3DModel(Asset): + """ + Instance of a Cognite 3D model in Atlan. + """ + + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + COGNITE_ASSET: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Cognite3DModel" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cognite_asset: Union[RelatedCogniteAsset, None, UnsetType] = UNSET + """Asset in which this 3D model exists.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Cognite3DModel" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _cognite3d_model_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Cognite3DModel: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Cognite3DModel instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _cognite3d_model_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class Cognite3DModelAttributes(AssetAttributes): + """Cognite3DModel-specific attributes for nested API format.""" + + pass + + +class Cognite3DModelRelationshipAttributes(AssetRelationshipAttributes): + """Cognite3DModel-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cognite_asset: Union[RelatedCogniteAsset, None, UnsetType] = UNSET + """Asset in which this 3D model exists.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class Cognite3DModelNested(AssetNested): + """Cognite3DModel in nested API format for high-performance serialization.""" + + attributes: Union[Cognite3DModelAttributes, UnsetType] = UNSET + relationship_attributes: Union[Cognite3DModelRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + Cognite3DModelRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + Cognite3DModelRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_COGNITE3D_MODEL_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "cognite_asset", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_cognite3d_model_attrs( + attrs: Cognite3DModelAttributes, obj: Cognite3DModel +) -> None: + """Populate Cognite3DModel-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + + +def _extract_cognite3d_model_attrs(attrs: Cognite3DModelAttributes) -> dict: + """Extract all Cognite3DModel attributes from the attrs struct into a flat dict.""" + return _extract_asset_attrs(attrs) + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _cognite3d_model_to_nested(cognite3d_model: Cognite3DModel) -> Cognite3DModelNested: + """Convert flat Cognite3DModel to nested format.""" + attrs = Cognite3DModelAttributes() + _populate_cognite3d_model_attrs(attrs, cognite3d_model) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + cognite3d_model, + _COGNITE3D_MODEL_REL_FIELDS, + Cognite3DModelRelationshipAttributes, + ) + return Cognite3DModelNested( + guid=cognite3d_model.guid, + type_name=cognite3d_model.type_name, + status=cognite3d_model.status, + version=cognite3d_model.version, + create_time=cognite3d_model.create_time, + update_time=cognite3d_model.update_time, + created_by=cognite3d_model.created_by, + updated_by=cognite3d_model.updated_by, + classifications=cognite3d_model.classifications, + classification_names=cognite3d_model.classification_names, + meanings=cognite3d_model.meanings, + labels=cognite3d_model.labels, + business_attributes=cognite3d_model.business_attributes, + custom_attributes=cognite3d_model.custom_attributes, + pending_tasks=cognite3d_model.pending_tasks, + proxy=cognite3d_model.proxy, + is_incomplete=cognite3d_model.is_incomplete, + provenance_type=cognite3d_model.provenance_type, + home_id=cognite3d_model.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _cognite3d_model_from_nested(nested: Cognite3DModelNested) -> Cognite3DModel: + """Convert nested format to flat Cognite3DModel.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else Cognite3DModelAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _COGNITE3D_MODEL_REL_FIELDS, + Cognite3DModelRelationshipAttributes, + ) + return Cognite3DModel( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_cognite3d_model_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _cognite3d_model_to_nested_bytes( + cognite3d_model: Cognite3DModel, serde: Serde +) -> bytes: + """Convert flat Cognite3DModel to nested JSON bytes.""" + return serde.encode(_cognite3d_model_to_nested(cognite3d_model)) + + +def _cognite3d_model_from_nested_bytes(data: bytes, serde: Serde) -> Cognite3DModel: + """Convert nested JSON bytes to flat Cognite3DModel.""" + nested = serde.decode(data, Cognite3DModelNested) + return _cognite3d_model_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import RelationField # noqa: E402 + +Cognite3DModel.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Cognite3DModel.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Cognite3DModel.ANOMALO_CHECKS = RelationField("anomaloChecks") +Cognite3DModel.APPLICATION = RelationField("application") +Cognite3DModel.APPLICATION_FIELD = RelationField("applicationField") +Cognite3DModel.COGNITE_ASSET = RelationField("cogniteAsset") +Cognite3DModel.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Cognite3DModel.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Cognite3DModel.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Cognite3DModel.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +Cognite3DModel.METRICS = RelationField("metrics") +Cognite3DModel.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Cognite3DModel.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Cognite3DModel.MEANINGS = RelationField("meanings") +Cognite3DModel.MC_MONITORS = RelationField("mcMonitors") +Cognite3DModel.MC_INCIDENTS = RelationField("mcIncidents") +Cognite3DModel.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Cognite3DModel.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Cognite3DModel.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Cognite3DModel.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Cognite3DModel.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Cognite3DModel.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Cognite3DModel.FILES = RelationField("files") +Cognite3DModel.LINKS = RelationField("links") +Cognite3DModel.README = RelationField("readme") +Cognite3DModel.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Cognite3DModel.SODA_CHECKS = RelationField("sodaChecks") +Cognite3DModel.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Cognite3DModel.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/cognite_asset.py b/pyatlan_v9/model/assets/cognite_asset.py new file mode 100644 index 000000000..6dad740b2 --- /dev/null +++ b/pyatlan_v9/model/assets/cognite_asset.py @@ -0,0 +1,584 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +CogniteAsset asset model with flattened inheritance. + +This module provides: +- CogniteAsset: Flat asset class (easy to use) +- CogniteAssetAttributes: Nested attributes struct (extends AssetAttributes) +- CogniteAssetNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .cognite_related import ( + RelatedCognite3DModel, + RelatedCogniteEvent, + RelatedCogniteFile, + RelatedCogniteSequence, + RelatedCogniteTimeSeries, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class CogniteAsset(Asset): + """ + Instance of a Cognite asset in Atlan. + """ + + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + COGNITE_EVENTS: ClassVar[Any] = None + COGNITE_FILES: ClassVar[Any] = None + COGNITE_SEQUENCES: ClassVar[Any] = None + COGNITE_TIMESERIES: ClassVar[Any] = None + COGNITE3DMODELS: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "CogniteAsset" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cognite_events: Union[List[RelatedCogniteEvent], None, UnsetType] = UNSET + """Events that exist within this asset.""" + + cognite_files: Union[List[RelatedCogniteFile], None, UnsetType] = UNSET + """Files that exist within this asset.""" + + cognite_sequences: Union[List[RelatedCogniteSequence], None, UnsetType] = UNSET + """Sequences that exist within this asset.""" + + cognite_timeseries: Union[List[RelatedCogniteTimeSeries], None, UnsetType] = UNSET + """Time series that exist within this asset.""" + + cognite3dmodels: Union[List[RelatedCognite3DModel], None, UnsetType] = UNSET + """3D models that exist within this asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "CogniteAsset" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _cognite_asset_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> CogniteAsset: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + CogniteAsset instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _cognite_asset_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class CogniteAssetAttributes(AssetAttributes): + """CogniteAsset-specific attributes for nested API format.""" + + pass + + +class CogniteAssetRelationshipAttributes(AssetRelationshipAttributes): + """CogniteAsset-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cognite_events: Union[List[RelatedCogniteEvent], None, UnsetType] = UNSET + """Events that exist within this asset.""" + + cognite_files: Union[List[RelatedCogniteFile], None, UnsetType] = UNSET + """Files that exist within this asset.""" + + cognite_sequences: Union[List[RelatedCogniteSequence], None, UnsetType] = UNSET + """Sequences that exist within this asset.""" + + cognite_timeseries: Union[List[RelatedCogniteTimeSeries], None, UnsetType] = UNSET + """Time series that exist within this asset.""" + + cognite3dmodels: Union[List[RelatedCognite3DModel], None, UnsetType] = UNSET + """3D models that exist within this asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class CogniteAssetNested(AssetNested): + """CogniteAsset in nested API format for high-performance serialization.""" + + attributes: Union[CogniteAssetAttributes, UnsetType] = UNSET + relationship_attributes: Union[CogniteAssetRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + CogniteAssetRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + CogniteAssetRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_COGNITE_ASSET_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "cognite_events", + "cognite_files", + "cognite_sequences", + "cognite_timeseries", + "cognite3dmodels", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_cognite_asset_attrs( + attrs: CogniteAssetAttributes, obj: CogniteAsset +) -> None: + """Populate CogniteAsset-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + + +def _extract_cognite_asset_attrs(attrs: CogniteAssetAttributes) -> dict: + """Extract all CogniteAsset attributes from the attrs struct into a flat dict.""" + return _extract_asset_attrs(attrs) + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _cognite_asset_to_nested(cognite_asset: CogniteAsset) -> CogniteAssetNested: + """Convert flat CogniteAsset to nested format.""" + attrs = CogniteAssetAttributes() + _populate_cognite_asset_attrs(attrs, cognite_asset) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + cognite_asset, _COGNITE_ASSET_REL_FIELDS, CogniteAssetRelationshipAttributes + ) + return CogniteAssetNested( + guid=cognite_asset.guid, + type_name=cognite_asset.type_name, + status=cognite_asset.status, + version=cognite_asset.version, + create_time=cognite_asset.create_time, + update_time=cognite_asset.update_time, + created_by=cognite_asset.created_by, + updated_by=cognite_asset.updated_by, + classifications=cognite_asset.classifications, + classification_names=cognite_asset.classification_names, + meanings=cognite_asset.meanings, + labels=cognite_asset.labels, + business_attributes=cognite_asset.business_attributes, + custom_attributes=cognite_asset.custom_attributes, + pending_tasks=cognite_asset.pending_tasks, + proxy=cognite_asset.proxy, + is_incomplete=cognite_asset.is_incomplete, + provenance_type=cognite_asset.provenance_type, + home_id=cognite_asset.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _cognite_asset_from_nested(nested: CogniteAssetNested) -> CogniteAsset: + """Convert nested format to flat CogniteAsset.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else CogniteAssetAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _COGNITE_ASSET_REL_FIELDS, + CogniteAssetRelationshipAttributes, + ) + return CogniteAsset( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_cognite_asset_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _cognite_asset_to_nested_bytes(cognite_asset: CogniteAsset, serde: Serde) -> bytes: + """Convert flat CogniteAsset to nested JSON bytes.""" + return serde.encode(_cognite_asset_to_nested(cognite_asset)) + + +def _cognite_asset_from_nested_bytes(data: bytes, serde: Serde) -> CogniteAsset: + """Convert nested JSON bytes to flat CogniteAsset.""" + nested = serde.decode(data, CogniteAssetNested) + return _cognite_asset_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import RelationField # noqa: E402 + +CogniteAsset.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +CogniteAsset.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +CogniteAsset.ANOMALO_CHECKS = RelationField("anomaloChecks") +CogniteAsset.APPLICATION = RelationField("application") +CogniteAsset.APPLICATION_FIELD = RelationField("applicationField") +CogniteAsset.COGNITE_EVENTS = RelationField("cogniteEvents") +CogniteAsset.COGNITE_FILES = RelationField("cogniteFiles") +CogniteAsset.COGNITE_SEQUENCES = RelationField("cogniteSequences") +CogniteAsset.COGNITE_TIMESERIES = RelationField("cogniteTimeseries") +CogniteAsset.COGNITE3DMODELS = RelationField("cognite3dmodels") +CogniteAsset.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +CogniteAsset.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +CogniteAsset.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +CogniteAsset.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +CogniteAsset.METRICS = RelationField("metrics") +CogniteAsset.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +CogniteAsset.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +CogniteAsset.MEANINGS = RelationField("meanings") +CogniteAsset.MC_MONITORS = RelationField("mcMonitors") +CogniteAsset.MC_INCIDENTS = RelationField("mcIncidents") +CogniteAsset.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +CogniteAsset.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +CogniteAsset.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +CogniteAsset.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +CogniteAsset.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +CogniteAsset.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +CogniteAsset.FILES = RelationField("files") +CogniteAsset.LINKS = RelationField("links") +CogniteAsset.README = RelationField("readme") +CogniteAsset.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +CogniteAsset.SODA_CHECKS = RelationField("sodaChecks") +CogniteAsset.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +CogniteAsset.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/cognite_event.py b/pyatlan_v9/model/assets/cognite_event.py new file mode 100644 index 000000000..f65e1cf1d --- /dev/null +++ b/pyatlan_v9/model/assets/cognite_event.py @@ -0,0 +1,549 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +CogniteEvent asset model with flattened inheritance. + +This module provides: +- CogniteEvent: Flat asset class (easy to use) +- CogniteEventAttributes: Nested attributes struct (extends AssetAttributes) +- CogniteEventNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .cognite_related import RelatedCogniteAsset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class CogniteEvent(Asset): + """ + Instance of a Cognite event in Atlan. + """ + + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + COGNITE_ASSET: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "CogniteEvent" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cognite_asset: Union[RelatedCogniteAsset, None, UnsetType] = UNSET + """Asset in which this event exists.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "CogniteEvent" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _cognite_event_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> CogniteEvent: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + CogniteEvent instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _cognite_event_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class CogniteEventAttributes(AssetAttributes): + """CogniteEvent-specific attributes for nested API format.""" + + pass + + +class CogniteEventRelationshipAttributes(AssetRelationshipAttributes): + """CogniteEvent-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cognite_asset: Union[RelatedCogniteAsset, None, UnsetType] = UNSET + """Asset in which this event exists.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class CogniteEventNested(AssetNested): + """CogniteEvent in nested API format for high-performance serialization.""" + + attributes: Union[CogniteEventAttributes, UnsetType] = UNSET + relationship_attributes: Union[CogniteEventRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + CogniteEventRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + CogniteEventRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_COGNITE_EVENT_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "cognite_asset", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_cognite_event_attrs( + attrs: CogniteEventAttributes, obj: CogniteEvent +) -> None: + """Populate CogniteEvent-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + + +def _extract_cognite_event_attrs(attrs: CogniteEventAttributes) -> dict: + """Extract all CogniteEvent attributes from the attrs struct into a flat dict.""" + return _extract_asset_attrs(attrs) + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _cognite_event_to_nested(cognite_event: CogniteEvent) -> CogniteEventNested: + """Convert flat CogniteEvent to nested format.""" + attrs = CogniteEventAttributes() + _populate_cognite_event_attrs(attrs, cognite_event) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + cognite_event, _COGNITE_EVENT_REL_FIELDS, CogniteEventRelationshipAttributes + ) + return CogniteEventNested( + guid=cognite_event.guid, + type_name=cognite_event.type_name, + status=cognite_event.status, + version=cognite_event.version, + create_time=cognite_event.create_time, + update_time=cognite_event.update_time, + created_by=cognite_event.created_by, + updated_by=cognite_event.updated_by, + classifications=cognite_event.classifications, + classification_names=cognite_event.classification_names, + meanings=cognite_event.meanings, + labels=cognite_event.labels, + business_attributes=cognite_event.business_attributes, + custom_attributes=cognite_event.custom_attributes, + pending_tasks=cognite_event.pending_tasks, + proxy=cognite_event.proxy, + is_incomplete=cognite_event.is_incomplete, + provenance_type=cognite_event.provenance_type, + home_id=cognite_event.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _cognite_event_from_nested(nested: CogniteEventNested) -> CogniteEvent: + """Convert nested format to flat CogniteEvent.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else CogniteEventAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _COGNITE_EVENT_REL_FIELDS, + CogniteEventRelationshipAttributes, + ) + return CogniteEvent( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_cognite_event_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _cognite_event_to_nested_bytes(cognite_event: CogniteEvent, serde: Serde) -> bytes: + """Convert flat CogniteEvent to nested JSON bytes.""" + return serde.encode(_cognite_event_to_nested(cognite_event)) + + +def _cognite_event_from_nested_bytes(data: bytes, serde: Serde) -> CogniteEvent: + """Convert nested JSON bytes to flat CogniteEvent.""" + nested = serde.decode(data, CogniteEventNested) + return _cognite_event_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import RelationField # noqa: E402 + +CogniteEvent.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +CogniteEvent.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +CogniteEvent.ANOMALO_CHECKS = RelationField("anomaloChecks") +CogniteEvent.APPLICATION = RelationField("application") +CogniteEvent.APPLICATION_FIELD = RelationField("applicationField") +CogniteEvent.COGNITE_ASSET = RelationField("cogniteAsset") +CogniteEvent.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +CogniteEvent.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +CogniteEvent.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +CogniteEvent.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +CogniteEvent.METRICS = RelationField("metrics") +CogniteEvent.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +CogniteEvent.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +CogniteEvent.MEANINGS = RelationField("meanings") +CogniteEvent.MC_MONITORS = RelationField("mcMonitors") +CogniteEvent.MC_INCIDENTS = RelationField("mcIncidents") +CogniteEvent.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +CogniteEvent.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +CogniteEvent.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +CogniteEvent.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +CogniteEvent.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +CogniteEvent.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +CogniteEvent.FILES = RelationField("files") +CogniteEvent.LINKS = RelationField("links") +CogniteEvent.README = RelationField("readme") +CogniteEvent.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +CogniteEvent.SODA_CHECKS = RelationField("sodaChecks") +CogniteEvent.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +CogniteEvent.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/cognite_file.py b/pyatlan_v9/model/assets/cognite_file.py new file mode 100644 index 000000000..f3d30d6d3 --- /dev/null +++ b/pyatlan_v9/model/assets/cognite_file.py @@ -0,0 +1,545 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +CogniteFile asset model with flattened inheritance. + +This module provides: +- CogniteFile: Flat asset class (easy to use) +- CogniteFileAttributes: Nested attributes struct (extends AssetAttributes) +- CogniteFileNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .cognite_related import RelatedCogniteAsset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class CogniteFile(Asset): + """ + Instance of a Cognite file in Atlan. + """ + + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + COGNITE_ASSET: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "CogniteFile" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cognite_asset: Union[RelatedCogniteAsset, None, UnsetType] = UNSET + """Asset in which this file exists.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "CogniteFile" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _cognite_file_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> CogniteFile: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + CogniteFile instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _cognite_file_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class CogniteFileAttributes(AssetAttributes): + """CogniteFile-specific attributes for nested API format.""" + + pass + + +class CogniteFileRelationshipAttributes(AssetRelationshipAttributes): + """CogniteFile-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cognite_asset: Union[RelatedCogniteAsset, None, UnsetType] = UNSET + """Asset in which this file exists.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class CogniteFileNested(AssetNested): + """CogniteFile in nested API format for high-performance serialization.""" + + attributes: Union[CogniteFileAttributes, UnsetType] = UNSET + relationship_attributes: Union[CogniteFileRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + CogniteFileRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + CogniteFileRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_COGNITE_FILE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "cognite_asset", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_cognite_file_attrs( + attrs: CogniteFileAttributes, obj: CogniteFile +) -> None: + """Populate CogniteFile-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + + +def _extract_cognite_file_attrs(attrs: CogniteFileAttributes) -> dict: + """Extract all CogniteFile attributes from the attrs struct into a flat dict.""" + return _extract_asset_attrs(attrs) + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _cognite_file_to_nested(cognite_file: CogniteFile) -> CogniteFileNested: + """Convert flat CogniteFile to nested format.""" + attrs = CogniteFileAttributes() + _populate_cognite_file_attrs(attrs, cognite_file) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + cognite_file, _COGNITE_FILE_REL_FIELDS, CogniteFileRelationshipAttributes + ) + return CogniteFileNested( + guid=cognite_file.guid, + type_name=cognite_file.type_name, + status=cognite_file.status, + version=cognite_file.version, + create_time=cognite_file.create_time, + update_time=cognite_file.update_time, + created_by=cognite_file.created_by, + updated_by=cognite_file.updated_by, + classifications=cognite_file.classifications, + classification_names=cognite_file.classification_names, + meanings=cognite_file.meanings, + labels=cognite_file.labels, + business_attributes=cognite_file.business_attributes, + custom_attributes=cognite_file.custom_attributes, + pending_tasks=cognite_file.pending_tasks, + proxy=cognite_file.proxy, + is_incomplete=cognite_file.is_incomplete, + provenance_type=cognite_file.provenance_type, + home_id=cognite_file.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _cognite_file_from_nested(nested: CogniteFileNested) -> CogniteFile: + """Convert nested format to flat CogniteFile.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else CogniteFileAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _COGNITE_FILE_REL_FIELDS, + CogniteFileRelationshipAttributes, + ) + return CogniteFile( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_cognite_file_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _cognite_file_to_nested_bytes(cognite_file: CogniteFile, serde: Serde) -> bytes: + """Convert flat CogniteFile to nested JSON bytes.""" + return serde.encode(_cognite_file_to_nested(cognite_file)) + + +def _cognite_file_from_nested_bytes(data: bytes, serde: Serde) -> CogniteFile: + """Convert nested JSON bytes to flat CogniteFile.""" + nested = serde.decode(data, CogniteFileNested) + return _cognite_file_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import RelationField # noqa: E402 + +CogniteFile.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +CogniteFile.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +CogniteFile.ANOMALO_CHECKS = RelationField("anomaloChecks") +CogniteFile.APPLICATION = RelationField("application") +CogniteFile.APPLICATION_FIELD = RelationField("applicationField") +CogniteFile.COGNITE_ASSET = RelationField("cogniteAsset") +CogniteFile.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +CogniteFile.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +CogniteFile.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +CogniteFile.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +CogniteFile.METRICS = RelationField("metrics") +CogniteFile.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +CogniteFile.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +CogniteFile.MEANINGS = RelationField("meanings") +CogniteFile.MC_MONITORS = RelationField("mcMonitors") +CogniteFile.MC_INCIDENTS = RelationField("mcIncidents") +CogniteFile.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +CogniteFile.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +CogniteFile.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +CogniteFile.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +CogniteFile.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +CogniteFile.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +CogniteFile.FILES = RelationField("files") +CogniteFile.LINKS = RelationField("links") +CogniteFile.README = RelationField("readme") +CogniteFile.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +CogniteFile.SODA_CHECKS = RelationField("sodaChecks") +CogniteFile.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +CogniteFile.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/cognite_related.py b/pyatlan_v9/model/assets/cognite_related.py new file mode 100644 index 000000000..24256dc47 --- /dev/null +++ b/pyatlan_v9/model/assets/cognite_related.py @@ -0,0 +1,131 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Cognite module. + +This module contains all Related{Type} classes for the Cognite type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + + +from .catalog_related import RelatedSaaS +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedCognite", + "RelatedCogniteEvent", + "RelatedCogniteFile", + "RelatedCogniteSequence", + "RelatedCogniteTimeSeries", + "RelatedCognite3DModel", + "RelatedCogniteAsset", +] + + +class RelatedCognite(RelatedSaaS): + """ + Related entity reference for Cognite assets. + + Extends RelatedSaaS with Cognite-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Cognite" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Cognite" + + +class RelatedCogniteEvent(RelatedCognite): + """ + Related entity reference for CogniteEvent assets. + + Extends RelatedCognite with CogniteEvent-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "CogniteEvent" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "CogniteEvent" + + +class RelatedCogniteFile(RelatedCognite): + """ + Related entity reference for CogniteFile assets. + + Extends RelatedCognite with CogniteFile-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "CogniteFile" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "CogniteFile" + + +class RelatedCogniteSequence(RelatedCognite): + """ + Related entity reference for CogniteSequence assets. + + Extends RelatedCognite with CogniteSequence-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "CogniteSequence" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "CogniteSequence" + + +class RelatedCogniteTimeSeries(RelatedCognite): + """ + Related entity reference for CogniteTimeSeries assets. + + Extends RelatedCognite with CogniteTimeSeries-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "CogniteTimeSeries" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "CogniteTimeSeries" + + +class RelatedCognite3DModel(RelatedCognite): + """ + Related entity reference for Cognite3DModel assets. + + Extends RelatedCognite with Cognite3DModel-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Cognite3DModel" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Cognite3DModel" + + +class RelatedCogniteAsset(RelatedCognite): + """ + Related entity reference for CogniteAsset assets. + + Extends RelatedCognite with CogniteAsset-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "CogniteAsset" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "CogniteAsset" diff --git a/pyatlan_v9/model/assets/cognite_sequence.py b/pyatlan_v9/model/assets/cognite_sequence.py new file mode 100644 index 000000000..45ea49e73 --- /dev/null +++ b/pyatlan_v9/model/assets/cognite_sequence.py @@ -0,0 +1,559 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +CogniteSequence asset model with flattened inheritance. + +This module provides: +- CogniteSequence: Flat asset class (easy to use) +- CogniteSequenceAttributes: Nested attributes struct (extends AssetAttributes) +- CogniteSequenceNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .cognite_related import RelatedCogniteAsset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class CogniteSequence(Asset): + """ + Instance of a Cognite sequence in Atlan. + """ + + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + COGNITE_ASSET: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "CogniteSequence" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cognite_asset: Union[RelatedCogniteAsset, None, UnsetType] = UNSET + """Asset in which this sequence exists.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "CogniteSequence" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _cognite_sequence_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> CogniteSequence: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + CogniteSequence instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _cognite_sequence_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class CogniteSequenceAttributes(AssetAttributes): + """CogniteSequence-specific attributes for nested API format.""" + + pass + + +class CogniteSequenceRelationshipAttributes(AssetRelationshipAttributes): + """CogniteSequence-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cognite_asset: Union[RelatedCogniteAsset, None, UnsetType] = UNSET + """Asset in which this sequence exists.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class CogniteSequenceNested(AssetNested): + """CogniteSequence in nested API format for high-performance serialization.""" + + attributes: Union[CogniteSequenceAttributes, UnsetType] = UNSET + relationship_attributes: Union[CogniteSequenceRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + CogniteSequenceRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + CogniteSequenceRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_COGNITE_SEQUENCE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "cognite_asset", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_cognite_sequence_attrs( + attrs: CogniteSequenceAttributes, obj: CogniteSequence +) -> None: + """Populate CogniteSequence-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + + +def _extract_cognite_sequence_attrs(attrs: CogniteSequenceAttributes) -> dict: + """Extract all CogniteSequence attributes from the attrs struct into a flat dict.""" + return _extract_asset_attrs(attrs) + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _cognite_sequence_to_nested( + cognite_sequence: CogniteSequence, +) -> CogniteSequenceNested: + """Convert flat CogniteSequence to nested format.""" + attrs = CogniteSequenceAttributes() + _populate_cognite_sequence_attrs(attrs, cognite_sequence) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + cognite_sequence, + _COGNITE_SEQUENCE_REL_FIELDS, + CogniteSequenceRelationshipAttributes, + ) + return CogniteSequenceNested( + guid=cognite_sequence.guid, + type_name=cognite_sequence.type_name, + status=cognite_sequence.status, + version=cognite_sequence.version, + create_time=cognite_sequence.create_time, + update_time=cognite_sequence.update_time, + created_by=cognite_sequence.created_by, + updated_by=cognite_sequence.updated_by, + classifications=cognite_sequence.classifications, + classification_names=cognite_sequence.classification_names, + meanings=cognite_sequence.meanings, + labels=cognite_sequence.labels, + business_attributes=cognite_sequence.business_attributes, + custom_attributes=cognite_sequence.custom_attributes, + pending_tasks=cognite_sequence.pending_tasks, + proxy=cognite_sequence.proxy, + is_incomplete=cognite_sequence.is_incomplete, + provenance_type=cognite_sequence.provenance_type, + home_id=cognite_sequence.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _cognite_sequence_from_nested(nested: CogniteSequenceNested) -> CogniteSequence: + """Convert nested format to flat CogniteSequence.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else CogniteSequenceAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _COGNITE_SEQUENCE_REL_FIELDS, + CogniteSequenceRelationshipAttributes, + ) + return CogniteSequence( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_cognite_sequence_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _cognite_sequence_to_nested_bytes( + cognite_sequence: CogniteSequence, serde: Serde +) -> bytes: + """Convert flat CogniteSequence to nested JSON bytes.""" + return serde.encode(_cognite_sequence_to_nested(cognite_sequence)) + + +def _cognite_sequence_from_nested_bytes(data: bytes, serde: Serde) -> CogniteSequence: + """Convert nested JSON bytes to flat CogniteSequence.""" + nested = serde.decode(data, CogniteSequenceNested) + return _cognite_sequence_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import RelationField # noqa: E402 + +CogniteSequence.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +CogniteSequence.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +CogniteSequence.ANOMALO_CHECKS = RelationField("anomaloChecks") +CogniteSequence.APPLICATION = RelationField("application") +CogniteSequence.APPLICATION_FIELD = RelationField("applicationField") +CogniteSequence.COGNITE_ASSET = RelationField("cogniteAsset") +CogniteSequence.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +CogniteSequence.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +CogniteSequence.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +CogniteSequence.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +CogniteSequence.METRICS = RelationField("metrics") +CogniteSequence.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +CogniteSequence.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +CogniteSequence.MEANINGS = RelationField("meanings") +CogniteSequence.MC_MONITORS = RelationField("mcMonitors") +CogniteSequence.MC_INCIDENTS = RelationField("mcIncidents") +CogniteSequence.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +CogniteSequence.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +CogniteSequence.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +CogniteSequence.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +CogniteSequence.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +CogniteSequence.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +CogniteSequence.FILES = RelationField("files") +CogniteSequence.LINKS = RelationField("links") +CogniteSequence.README = RelationField("readme") +CogniteSequence.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +CogniteSequence.SODA_CHECKS = RelationField("sodaChecks") +CogniteSequence.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +CogniteSequence.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/cognite_time_series.py b/pyatlan_v9/model/assets/cognite_time_series.py new file mode 100644 index 000000000..ca4aa6f21 --- /dev/null +++ b/pyatlan_v9/model/assets/cognite_time_series.py @@ -0,0 +1,563 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +CogniteTimeSeries asset model with flattened inheritance. + +This module provides: +- CogniteTimeSeries: Flat asset class (easy to use) +- CogniteTimeSeriesAttributes: Nested attributes struct (extends AssetAttributes) +- CogniteTimeSeriesNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .cognite_related import RelatedCogniteAsset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class CogniteTimeSeries(Asset): + """ + Instance of a Cognite time series in Atlan. + """ + + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + COGNITE_ASSET: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "CogniteTimeSeries" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cognite_asset: Union[RelatedCogniteAsset, None, UnsetType] = UNSET + """Asset in which this time series exists.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "CogniteTimeSeries" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _cognite_time_series_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> CogniteTimeSeries: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + CogniteTimeSeries instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _cognite_time_series_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class CogniteTimeSeriesAttributes(AssetAttributes): + """CogniteTimeSeries-specific attributes for nested API format.""" + + pass + + +class CogniteTimeSeriesRelationshipAttributes(AssetRelationshipAttributes): + """CogniteTimeSeries-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cognite_asset: Union[RelatedCogniteAsset, None, UnsetType] = UNSET + """Asset in which this time series exists.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class CogniteTimeSeriesNested(AssetNested): + """CogniteTimeSeries in nested API format for high-performance serialization.""" + + attributes: Union[CogniteTimeSeriesAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + CogniteTimeSeriesRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + CogniteTimeSeriesRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + CogniteTimeSeriesRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_COGNITE_TIME_SERIES_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "cognite_asset", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_cognite_time_series_attrs( + attrs: CogniteTimeSeriesAttributes, obj: CogniteTimeSeries +) -> None: + """Populate CogniteTimeSeries-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + + +def _extract_cognite_time_series_attrs(attrs: CogniteTimeSeriesAttributes) -> dict: + """Extract all CogniteTimeSeries attributes from the attrs struct into a flat dict.""" + return _extract_asset_attrs(attrs) + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _cognite_time_series_to_nested( + cognite_time_series: CogniteTimeSeries, +) -> CogniteTimeSeriesNested: + """Convert flat CogniteTimeSeries to nested format.""" + attrs = CogniteTimeSeriesAttributes() + _populate_cognite_time_series_attrs(attrs, cognite_time_series) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + cognite_time_series, + _COGNITE_TIME_SERIES_REL_FIELDS, + CogniteTimeSeriesRelationshipAttributes, + ) + return CogniteTimeSeriesNested( + guid=cognite_time_series.guid, + type_name=cognite_time_series.type_name, + status=cognite_time_series.status, + version=cognite_time_series.version, + create_time=cognite_time_series.create_time, + update_time=cognite_time_series.update_time, + created_by=cognite_time_series.created_by, + updated_by=cognite_time_series.updated_by, + classifications=cognite_time_series.classifications, + classification_names=cognite_time_series.classification_names, + meanings=cognite_time_series.meanings, + labels=cognite_time_series.labels, + business_attributes=cognite_time_series.business_attributes, + custom_attributes=cognite_time_series.custom_attributes, + pending_tasks=cognite_time_series.pending_tasks, + proxy=cognite_time_series.proxy, + is_incomplete=cognite_time_series.is_incomplete, + provenance_type=cognite_time_series.provenance_type, + home_id=cognite_time_series.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _cognite_time_series_from_nested( + nested: CogniteTimeSeriesNested, +) -> CogniteTimeSeries: + """Convert nested format to flat CogniteTimeSeries.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else CogniteTimeSeriesAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _COGNITE_TIME_SERIES_REL_FIELDS, + CogniteTimeSeriesRelationshipAttributes, + ) + return CogniteTimeSeries( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_cognite_time_series_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _cognite_time_series_to_nested_bytes( + cognite_time_series: CogniteTimeSeries, serde: Serde +) -> bytes: + """Convert flat CogniteTimeSeries to nested JSON bytes.""" + return serde.encode(_cognite_time_series_to_nested(cognite_time_series)) + + +def _cognite_time_series_from_nested_bytes( + data: bytes, serde: Serde +) -> CogniteTimeSeries: + """Convert nested JSON bytes to flat CogniteTimeSeries.""" + nested = serde.decode(data, CogniteTimeSeriesNested) + return _cognite_time_series_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import RelationField # noqa: E402 + +CogniteTimeSeries.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +CogniteTimeSeries.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +CogniteTimeSeries.ANOMALO_CHECKS = RelationField("anomaloChecks") +CogniteTimeSeries.APPLICATION = RelationField("application") +CogniteTimeSeries.APPLICATION_FIELD = RelationField("applicationField") +CogniteTimeSeries.COGNITE_ASSET = RelationField("cogniteAsset") +CogniteTimeSeries.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +CogniteTimeSeries.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +CogniteTimeSeries.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +CogniteTimeSeries.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +CogniteTimeSeries.METRICS = RelationField("metrics") +CogniteTimeSeries.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +CogniteTimeSeries.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +CogniteTimeSeries.MEANINGS = RelationField("meanings") +CogniteTimeSeries.MC_MONITORS = RelationField("mcMonitors") +CogniteTimeSeries.MC_INCIDENTS = RelationField("mcIncidents") +CogniteTimeSeries.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +CogniteTimeSeries.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +CogniteTimeSeries.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +CogniteTimeSeries.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +CogniteTimeSeries.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +CogniteTimeSeries.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +CogniteTimeSeries.FILES = RelationField("files") +CogniteTimeSeries.LINKS = RelationField("links") +CogniteTimeSeries.README = RelationField("readme") +CogniteTimeSeries.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +CogniteTimeSeries.SODA_CHECKS = RelationField("sodaChecks") +CogniteTimeSeries.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +CogniteTimeSeries.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/cognos.py b/pyatlan_v9/model/assets/cognos.py new file mode 100644 index 000000000..5929f4fe0 --- /dev/null +++ b/pyatlan_v9/model/assets/cognos.py @@ -0,0 +1,623 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Cognos asset model with flattened inheritance. + +This module provides: +- Cognos: Flat asset class (easy to use) +- CognosAttributes: Nested attributes struct (extends AssetAttributes) +- CognosNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Cognos(Asset): + """ + Base class for Cognos assets. + """ + + COGNOS_ID: ClassVar[Any] = None + COGNOS_PATH: ClassVar[Any] = None + COGNOS_PARENT_NAME: ClassVar[Any] = None + COGNOS_PARENT_QUALIFIED_NAME: ClassVar[Any] = None + COGNOS_VERSION: ClassVar[Any] = None + COGNOS_TYPE: ClassVar[Any] = None + COGNOS_IS_HIDDEN: ClassVar[Any] = None + COGNOS_IS_DISABLED: ClassVar[Any] = None + COGNOS_DEFAULT_SCREEN_TIP: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Cognos" + + cognos_id: Union[str, None, UnsetType] = UNSET + """ID of the asset in Cognos.""" + + cognos_path: Union[str, None, UnsetType] = UNSET + """Path of the asset in Cognos (e.g. /content/folder[@name='Folder Name']).""" + + cognos_parent_name: Union[str, None, UnsetType] = UNSET + """Name of the parent of the asset in Cognos.""" + + cognos_parent_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the parent asset in Cognos.""" + + cognos_version: Union[str, None, UnsetType] = UNSET + """Version of the Cognos asset.""" + + cognos_type: Union[str, None, UnsetType] = UNSET + """Type of the Cognos asset (e.g. report, dashboard, package, etc).""" + + cognos_is_hidden: Union[bool, None, UnsetType] = UNSET + """Whether the Cognos asset is hidden from the UI.""" + + cognos_is_disabled: Union[bool, None, UnsetType] = UNSET + """Whether the Cognos asset is disabled.""" + + cognos_default_screen_tip: Union[str, None, UnsetType] = UNSET + """Tooltip text present for the Cognos asset.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Cognos" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _cognos_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Cognos: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Cognos instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _cognos_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class CognosAttributes(AssetAttributes): + """Cognos-specific attributes for nested API format.""" + + cognos_id: Union[str, None, UnsetType] = UNSET + """ID of the asset in Cognos.""" + + cognos_path: Union[str, None, UnsetType] = UNSET + """Path of the asset in Cognos (e.g. /content/folder[@name='Folder Name']).""" + + cognos_parent_name: Union[str, None, UnsetType] = UNSET + """Name of the parent of the asset in Cognos.""" + + cognos_parent_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the parent asset in Cognos.""" + + cognos_version: Union[str, None, UnsetType] = UNSET + """Version of the Cognos asset.""" + + cognos_type: Union[str, None, UnsetType] = UNSET + """Type of the Cognos asset (e.g. report, dashboard, package, etc).""" + + cognos_is_hidden: Union[bool, None, UnsetType] = UNSET + """Whether the Cognos asset is hidden from the UI.""" + + cognos_is_disabled: Union[bool, None, UnsetType] = UNSET + """Whether the Cognos asset is disabled.""" + + cognos_default_screen_tip: Union[str, None, UnsetType] = UNSET + """Tooltip text present for the Cognos asset.""" + + +class CognosRelationshipAttributes(AssetRelationshipAttributes): + """Cognos-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class CognosNested(AssetNested): + """Cognos in nested API format for high-performance serialization.""" + + attributes: Union[CognosAttributes, UnsetType] = UNSET + relationship_attributes: Union[CognosRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[CognosRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[CognosRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_COGNOS_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_cognos_attrs(attrs: CognosAttributes, obj: Cognos) -> None: + """Populate Cognos-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.cognos_id = obj.cognos_id + attrs.cognos_path = obj.cognos_path + attrs.cognos_parent_name = obj.cognos_parent_name + attrs.cognos_parent_qualified_name = obj.cognos_parent_qualified_name + attrs.cognos_version = obj.cognos_version + attrs.cognos_type = obj.cognos_type + attrs.cognos_is_hidden = obj.cognos_is_hidden + attrs.cognos_is_disabled = obj.cognos_is_disabled + attrs.cognos_default_screen_tip = obj.cognos_default_screen_tip + + +def _extract_cognos_attrs(attrs: CognosAttributes) -> dict: + """Extract all Cognos attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["cognos_id"] = attrs.cognos_id + result["cognos_path"] = attrs.cognos_path + result["cognos_parent_name"] = attrs.cognos_parent_name + result["cognos_parent_qualified_name"] = attrs.cognos_parent_qualified_name + result["cognos_version"] = attrs.cognos_version + result["cognos_type"] = attrs.cognos_type + result["cognos_is_hidden"] = attrs.cognos_is_hidden + result["cognos_is_disabled"] = attrs.cognos_is_disabled + result["cognos_default_screen_tip"] = attrs.cognos_default_screen_tip + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _cognos_to_nested(cognos: Cognos) -> CognosNested: + """Convert flat Cognos to nested format.""" + attrs = CognosAttributes() + _populate_cognos_attrs(attrs, cognos) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + cognos, _COGNOS_REL_FIELDS, CognosRelationshipAttributes + ) + return CognosNested( + guid=cognos.guid, + type_name=cognos.type_name, + status=cognos.status, + version=cognos.version, + create_time=cognos.create_time, + update_time=cognos.update_time, + created_by=cognos.created_by, + updated_by=cognos.updated_by, + classifications=cognos.classifications, + classification_names=cognos.classification_names, + meanings=cognos.meanings, + labels=cognos.labels, + business_attributes=cognos.business_attributes, + custom_attributes=cognos.custom_attributes, + pending_tasks=cognos.pending_tasks, + proxy=cognos.proxy, + is_incomplete=cognos.is_incomplete, + provenance_type=cognos.provenance_type, + home_id=cognos.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _cognos_from_nested(nested: CognosNested) -> Cognos: + """Convert nested format to flat Cognos.""" + attrs = nested.attributes if nested.attributes is not UNSET else CognosAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _COGNOS_REL_FIELDS, + CognosRelationshipAttributes, + ) + return Cognos( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_cognos_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _cognos_to_nested_bytes(cognos: Cognos, serde: Serde) -> bytes: + """Convert flat Cognos to nested JSON bytes.""" + return serde.encode(_cognos_to_nested(cognos)) + + +def _cognos_from_nested_bytes(data: bytes, serde: Serde) -> Cognos: + """Convert nested JSON bytes to flat Cognos.""" + nested = serde.decode(data, CognosNested) + return _cognos_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + RelationField, +) + +Cognos.COGNOS_ID = KeywordField("cognosId", "cognosId") +Cognos.COGNOS_PATH = KeywordField("cognosPath", "cognosPath") +Cognos.COGNOS_PARENT_NAME = KeywordTextField( + "cognosParentName", "cognosParentName", "cognosParentName.text" +) +Cognos.COGNOS_PARENT_QUALIFIED_NAME = KeywordField( + "cognosParentQualifiedName", "cognosParentQualifiedName" +) +Cognos.COGNOS_VERSION = KeywordField("cognosVersion", "cognosVersion") +Cognos.COGNOS_TYPE = KeywordField("cognosType", "cognosType") +Cognos.COGNOS_IS_HIDDEN = BooleanField("cognosIsHidden", "cognosIsHidden") +Cognos.COGNOS_IS_DISABLED = BooleanField("cognosIsDisabled", "cognosIsDisabled") +Cognos.COGNOS_DEFAULT_SCREEN_TIP = KeywordField( + "cognosDefaultScreenTip", "cognosDefaultScreenTip" +) +Cognos.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Cognos.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Cognos.ANOMALO_CHECKS = RelationField("anomaloChecks") +Cognos.APPLICATION = RelationField("application") +Cognos.APPLICATION_FIELD = RelationField("applicationField") +Cognos.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Cognos.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Cognos.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Cognos.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Cognos.METRICS = RelationField("metrics") +Cognos.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Cognos.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Cognos.MEANINGS = RelationField("meanings") +Cognos.MC_MONITORS = RelationField("mcMonitors") +Cognos.MC_INCIDENTS = RelationField("mcIncidents") +Cognos.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Cognos.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Cognos.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Cognos.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Cognos.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Cognos.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Cognos.FILES = RelationField("files") +Cognos.LINKS = RelationField("links") +Cognos.README = RelationField("readme") +Cognos.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Cognos.SODA_CHECKS = RelationField("sodaChecks") +Cognos.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Cognos.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/cognos_column.py b/pyatlan_v9/model/assets/cognos_column.py new file mode 100644 index 000000000..a1cd8918e --- /dev/null +++ b/pyatlan_v9/model/assets/cognos_column.py @@ -0,0 +1,735 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +CognosColumn asset model with flattened inheritance. + +This module provides: +- CognosColumn: Flat asset class (easy to use) +- CognosColumnAttributes: Nested attributes struct (extends AssetAttributes) +- CognosColumnNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .cognos_related import ( + RelatedCognosDashboard, + RelatedCognosDataset, + RelatedCognosExploration, + RelatedCognosFile, + RelatedCognosModule, + RelatedCognosPackage, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class CognosColumn(Asset): + """ + Instance of a Cognos column in Atlan. + """ + + COGNOS_DATATYPE: ClassVar[Any] = None + COGNOS_NULLABLE: ClassVar[Any] = None + COGNOS_REGULAR_AGGREGATE: ClassVar[Any] = None + COGNOS_ID: ClassVar[Any] = None + COGNOS_PATH: ClassVar[Any] = None + COGNOS_PARENT_NAME: ClassVar[Any] = None + COGNOS_PARENT_QUALIFIED_NAME: ClassVar[Any] = None + COGNOS_VERSION: ClassVar[Any] = None + COGNOS_TYPE: ClassVar[Any] = None + COGNOS_IS_HIDDEN: ClassVar[Any] = None + COGNOS_IS_DISABLED: ClassVar[Any] = None + COGNOS_DEFAULT_SCREEN_TIP: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + COGNOS_DATASET: ClassVar[Any] = None + COGNOS_FILE: ClassVar[Any] = None + COGNOS_MODULE: ClassVar[Any] = None + COGNOS_PACKAGE: ClassVar[Any] = None + COGNOS_DASHBOARD: ClassVar[Any] = None + COGNOS_EXPLORATION: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "CognosColumn" + + cognos_datatype: Union[str, None, UnsetType] = UNSET + """Data type of the CognosColumn.""" + + cognos_nullable: Union[str, None, UnsetType] = UNSET + """Whether the CognosColumn is nullable.""" + + cognos_regular_aggregate: Union[str, None, UnsetType] = UNSET + """How data should be summarized when aggregated across different dimensions or groupings.""" + + cognos_id: Union[str, None, UnsetType] = UNSET + """ID of the asset in Cognos.""" + + cognos_path: Union[str, None, UnsetType] = UNSET + """Path of the asset in Cognos (e.g. /content/folder[@name='Folder Name']).""" + + cognos_parent_name: Union[str, None, UnsetType] = UNSET + """Name of the parent of the asset in Cognos.""" + + cognos_parent_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the parent asset in Cognos.""" + + cognos_version: Union[str, None, UnsetType] = UNSET + """Version of the Cognos asset.""" + + cognos_type: Union[str, None, UnsetType] = UNSET + """Type of the Cognos asset (e.g. report, dashboard, package, etc).""" + + cognos_is_hidden: Union[bool, None, UnsetType] = UNSET + """Whether the Cognos asset is hidden from the UI.""" + + cognos_is_disabled: Union[bool, None, UnsetType] = UNSET + """Whether the Cognos asset is disabled.""" + + cognos_default_screen_tip: Union[str, None, UnsetType] = UNSET + """Tooltip text present for the Cognos asset.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cognos_dataset: Union[RelatedCognosDataset, None, UnsetType] = UNSET + """Parent dataset containing the columns.""" + + cognos_file: Union[RelatedCognosFile, None, UnsetType] = UNSET + """Parent file containing the columns.""" + + cognos_module: Union[RelatedCognosModule, None, UnsetType] = UNSET + """Parent module containing the columns.""" + + cognos_package: Union[RelatedCognosPackage, None, UnsetType] = UNSET + """Parent package containing the columns.""" + + cognos_dashboard: Union[RelatedCognosDashboard, None, UnsetType] = UNSET + """Parent dashboard containing the columns.""" + + cognos_exploration: Union[RelatedCognosExploration, None, UnsetType] = UNSET + """Parent exploration containing the columns.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "CognosColumn" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _cognos_column_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> CognosColumn: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + CognosColumn instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _cognos_column_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class CognosColumnAttributes(AssetAttributes): + """CognosColumn-specific attributes for nested API format.""" + + cognos_datatype: Union[str, None, UnsetType] = UNSET + """Data type of the CognosColumn.""" + + cognos_nullable: Union[str, None, UnsetType] = UNSET + """Whether the CognosColumn is nullable.""" + + cognos_regular_aggregate: Union[str, None, UnsetType] = UNSET + """How data should be summarized when aggregated across different dimensions or groupings.""" + + cognos_id: Union[str, None, UnsetType] = UNSET + """ID of the asset in Cognos.""" + + cognos_path: Union[str, None, UnsetType] = UNSET + """Path of the asset in Cognos (e.g. /content/folder[@name='Folder Name']).""" + + cognos_parent_name: Union[str, None, UnsetType] = UNSET + """Name of the parent of the asset in Cognos.""" + + cognos_parent_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the parent asset in Cognos.""" + + cognos_version: Union[str, None, UnsetType] = UNSET + """Version of the Cognos asset.""" + + cognos_type: Union[str, None, UnsetType] = UNSET + """Type of the Cognos asset (e.g. report, dashboard, package, etc).""" + + cognos_is_hidden: Union[bool, None, UnsetType] = UNSET + """Whether the Cognos asset is hidden from the UI.""" + + cognos_is_disabled: Union[bool, None, UnsetType] = UNSET + """Whether the Cognos asset is disabled.""" + + cognos_default_screen_tip: Union[str, None, UnsetType] = UNSET + """Tooltip text present for the Cognos asset.""" + + +class CognosColumnRelationshipAttributes(AssetRelationshipAttributes): + """CognosColumn-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cognos_dataset: Union[RelatedCognosDataset, None, UnsetType] = UNSET + """Parent dataset containing the columns.""" + + cognos_file: Union[RelatedCognosFile, None, UnsetType] = UNSET + """Parent file containing the columns.""" + + cognos_module: Union[RelatedCognosModule, None, UnsetType] = UNSET + """Parent module containing the columns.""" + + cognos_package: Union[RelatedCognosPackage, None, UnsetType] = UNSET + """Parent package containing the columns.""" + + cognos_dashboard: Union[RelatedCognosDashboard, None, UnsetType] = UNSET + """Parent dashboard containing the columns.""" + + cognos_exploration: Union[RelatedCognosExploration, None, UnsetType] = UNSET + """Parent exploration containing the columns.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class CognosColumnNested(AssetNested): + """CognosColumn in nested API format for high-performance serialization.""" + + attributes: Union[CognosColumnAttributes, UnsetType] = UNSET + relationship_attributes: Union[CognosColumnRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + CognosColumnRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + CognosColumnRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_COGNOS_COLUMN_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "cognos_dataset", + "cognos_file", + "cognos_module", + "cognos_package", + "cognos_dashboard", + "cognos_exploration", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_cognos_column_attrs( + attrs: CognosColumnAttributes, obj: CognosColumn +) -> None: + """Populate CognosColumn-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.cognos_datatype = obj.cognos_datatype + attrs.cognos_nullable = obj.cognos_nullable + attrs.cognos_regular_aggregate = obj.cognos_regular_aggregate + attrs.cognos_id = obj.cognos_id + attrs.cognos_path = obj.cognos_path + attrs.cognos_parent_name = obj.cognos_parent_name + attrs.cognos_parent_qualified_name = obj.cognos_parent_qualified_name + attrs.cognos_version = obj.cognos_version + attrs.cognos_type = obj.cognos_type + attrs.cognos_is_hidden = obj.cognos_is_hidden + attrs.cognos_is_disabled = obj.cognos_is_disabled + attrs.cognos_default_screen_tip = obj.cognos_default_screen_tip + + +def _extract_cognos_column_attrs(attrs: CognosColumnAttributes) -> dict: + """Extract all CognosColumn attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["cognos_datatype"] = attrs.cognos_datatype + result["cognos_nullable"] = attrs.cognos_nullable + result["cognos_regular_aggregate"] = attrs.cognos_regular_aggregate + result["cognos_id"] = attrs.cognos_id + result["cognos_path"] = attrs.cognos_path + result["cognos_parent_name"] = attrs.cognos_parent_name + result["cognos_parent_qualified_name"] = attrs.cognos_parent_qualified_name + result["cognos_version"] = attrs.cognos_version + result["cognos_type"] = attrs.cognos_type + result["cognos_is_hidden"] = attrs.cognos_is_hidden + result["cognos_is_disabled"] = attrs.cognos_is_disabled + result["cognos_default_screen_tip"] = attrs.cognos_default_screen_tip + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _cognos_column_to_nested(cognos_column: CognosColumn) -> CognosColumnNested: + """Convert flat CognosColumn to nested format.""" + attrs = CognosColumnAttributes() + _populate_cognos_column_attrs(attrs, cognos_column) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + cognos_column, _COGNOS_COLUMN_REL_FIELDS, CognosColumnRelationshipAttributes + ) + return CognosColumnNested( + guid=cognos_column.guid, + type_name=cognos_column.type_name, + status=cognos_column.status, + version=cognos_column.version, + create_time=cognos_column.create_time, + update_time=cognos_column.update_time, + created_by=cognos_column.created_by, + updated_by=cognos_column.updated_by, + classifications=cognos_column.classifications, + classification_names=cognos_column.classification_names, + meanings=cognos_column.meanings, + labels=cognos_column.labels, + business_attributes=cognos_column.business_attributes, + custom_attributes=cognos_column.custom_attributes, + pending_tasks=cognos_column.pending_tasks, + proxy=cognos_column.proxy, + is_incomplete=cognos_column.is_incomplete, + provenance_type=cognos_column.provenance_type, + home_id=cognos_column.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _cognos_column_from_nested(nested: CognosColumnNested) -> CognosColumn: + """Convert nested format to flat CognosColumn.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else CognosColumnAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _COGNOS_COLUMN_REL_FIELDS, + CognosColumnRelationshipAttributes, + ) + return CognosColumn( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_cognos_column_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _cognos_column_to_nested_bytes(cognos_column: CognosColumn, serde: Serde) -> bytes: + """Convert flat CognosColumn to nested JSON bytes.""" + return serde.encode(_cognos_column_to_nested(cognos_column)) + + +def _cognos_column_from_nested_bytes(data: bytes, serde: Serde) -> CognosColumn: + """Convert nested JSON bytes to flat CognosColumn.""" + nested = serde.decode(data, CognosColumnNested) + return _cognos_column_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + RelationField, +) + +CognosColumn.COGNOS_DATATYPE = KeywordField("cognosDatatype", "cognosDatatype") +CognosColumn.COGNOS_NULLABLE = KeywordField("cognosNullable", "cognosNullable") +CognosColumn.COGNOS_REGULAR_AGGREGATE = KeywordField( + "cognosRegularAggregate", "cognosRegularAggregate" +) +CognosColumn.COGNOS_ID = KeywordField("cognosId", "cognosId") +CognosColumn.COGNOS_PATH = KeywordField("cognosPath", "cognosPath") +CognosColumn.COGNOS_PARENT_NAME = KeywordTextField( + "cognosParentName", "cognosParentName", "cognosParentName.text" +) +CognosColumn.COGNOS_PARENT_QUALIFIED_NAME = KeywordField( + "cognosParentQualifiedName", "cognosParentQualifiedName" +) +CognosColumn.COGNOS_VERSION = KeywordField("cognosVersion", "cognosVersion") +CognosColumn.COGNOS_TYPE = KeywordField("cognosType", "cognosType") +CognosColumn.COGNOS_IS_HIDDEN = BooleanField("cognosIsHidden", "cognosIsHidden") +CognosColumn.COGNOS_IS_DISABLED = BooleanField("cognosIsDisabled", "cognosIsDisabled") +CognosColumn.COGNOS_DEFAULT_SCREEN_TIP = KeywordField( + "cognosDefaultScreenTip", "cognosDefaultScreenTip" +) +CognosColumn.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +CognosColumn.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +CognosColumn.ANOMALO_CHECKS = RelationField("anomaloChecks") +CognosColumn.APPLICATION = RelationField("application") +CognosColumn.APPLICATION_FIELD = RelationField("applicationField") +CognosColumn.COGNOS_DATASET = RelationField("cognosDataset") +CognosColumn.COGNOS_FILE = RelationField("cognosFile") +CognosColumn.COGNOS_MODULE = RelationField("cognosModule") +CognosColumn.COGNOS_PACKAGE = RelationField("cognosPackage") +CognosColumn.COGNOS_DASHBOARD = RelationField("cognosDashboard") +CognosColumn.COGNOS_EXPLORATION = RelationField("cognosExploration") +CognosColumn.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +CognosColumn.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +CognosColumn.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +CognosColumn.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +CognosColumn.METRICS = RelationField("metrics") +CognosColumn.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +CognosColumn.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +CognosColumn.MEANINGS = RelationField("meanings") +CognosColumn.MC_MONITORS = RelationField("mcMonitors") +CognosColumn.MC_INCIDENTS = RelationField("mcIncidents") +CognosColumn.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +CognosColumn.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +CognosColumn.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +CognosColumn.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +CognosColumn.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +CognosColumn.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +CognosColumn.FILES = RelationField("files") +CognosColumn.LINKS = RelationField("links") +CognosColumn.README = RelationField("readme") +CognosColumn.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +CognosColumn.SODA_CHECKS = RelationField("sodaChecks") +CognosColumn.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +CognosColumn.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/cognos_dashboard.py b/pyatlan_v9/model/assets/cognos_dashboard.py new file mode 100644 index 000000000..d76f6a18d --- /dev/null +++ b/pyatlan_v9/model/assets/cognos_dashboard.py @@ -0,0 +1,672 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +CognosDashboard asset model with flattened inheritance. + +This module provides: +- CognosDashboard: Flat asset class (easy to use) +- CognosDashboardAttributes: Nested attributes struct (extends AssetAttributes) +- CognosDashboardNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .cognos_related import RelatedCognosColumn, RelatedCognosFolder + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class CognosDashboard(Asset): + """ + Instance of a Cognos Dashboard in Atlan. + """ + + COGNOS_ID: ClassVar[Any] = None + COGNOS_PATH: ClassVar[Any] = None + COGNOS_PARENT_NAME: ClassVar[Any] = None + COGNOS_PARENT_QUALIFIED_NAME: ClassVar[Any] = None + COGNOS_VERSION: ClassVar[Any] = None + COGNOS_TYPE: ClassVar[Any] = None + COGNOS_IS_HIDDEN: ClassVar[Any] = None + COGNOS_IS_DISABLED: ClassVar[Any] = None + COGNOS_DEFAULT_SCREEN_TIP: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + COGNOS_FOLDER: ClassVar[Any] = None + COGNOS_COLUMNS: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "CognosDashboard" + + cognos_id: Union[str, None, UnsetType] = UNSET + """ID of the asset in Cognos.""" + + cognos_path: Union[str, None, UnsetType] = UNSET + """Path of the asset in Cognos (e.g. /content/folder[@name='Folder Name']).""" + + cognos_parent_name: Union[str, None, UnsetType] = UNSET + """Name of the parent of the asset in Cognos.""" + + cognos_parent_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the parent asset in Cognos.""" + + cognos_version: Union[str, None, UnsetType] = UNSET + """Version of the Cognos asset.""" + + cognos_type: Union[str, None, UnsetType] = UNSET + """Type of the Cognos asset (e.g. report, dashboard, package, etc).""" + + cognos_is_hidden: Union[bool, None, UnsetType] = UNSET + """Whether the Cognos asset is hidden from the UI.""" + + cognos_is_disabled: Union[bool, None, UnsetType] = UNSET + """Whether the Cognos asset is disabled.""" + + cognos_default_screen_tip: Union[str, None, UnsetType] = UNSET + """Tooltip text present for the Cognos asset.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cognos_folder: Union[RelatedCognosFolder, None, UnsetType] = UNSET + """Folder containing the dashboard.""" + + cognos_columns: Union[List[RelatedCognosColumn], None, UnsetType] = UNSET + """Columns contained in the dashboard.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "CognosDashboard" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _cognos_dashboard_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> CognosDashboard: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + CognosDashboard instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _cognos_dashboard_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class CognosDashboardAttributes(AssetAttributes): + """CognosDashboard-specific attributes for nested API format.""" + + cognos_id: Union[str, None, UnsetType] = UNSET + """ID of the asset in Cognos.""" + + cognos_path: Union[str, None, UnsetType] = UNSET + """Path of the asset in Cognos (e.g. /content/folder[@name='Folder Name']).""" + + cognos_parent_name: Union[str, None, UnsetType] = UNSET + """Name of the parent of the asset in Cognos.""" + + cognos_parent_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the parent asset in Cognos.""" + + cognos_version: Union[str, None, UnsetType] = UNSET + """Version of the Cognos asset.""" + + cognos_type: Union[str, None, UnsetType] = UNSET + """Type of the Cognos asset (e.g. report, dashboard, package, etc).""" + + cognos_is_hidden: Union[bool, None, UnsetType] = UNSET + """Whether the Cognos asset is hidden from the UI.""" + + cognos_is_disabled: Union[bool, None, UnsetType] = UNSET + """Whether the Cognos asset is disabled.""" + + cognos_default_screen_tip: Union[str, None, UnsetType] = UNSET + """Tooltip text present for the Cognos asset.""" + + +class CognosDashboardRelationshipAttributes(AssetRelationshipAttributes): + """CognosDashboard-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cognos_folder: Union[RelatedCognosFolder, None, UnsetType] = UNSET + """Folder containing the dashboard.""" + + cognos_columns: Union[List[RelatedCognosColumn], None, UnsetType] = UNSET + """Columns contained in the dashboard.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class CognosDashboardNested(AssetNested): + """CognosDashboard in nested API format for high-performance serialization.""" + + attributes: Union[CognosDashboardAttributes, UnsetType] = UNSET + relationship_attributes: Union[CognosDashboardRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + CognosDashboardRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + CognosDashboardRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_COGNOS_DASHBOARD_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "cognos_folder", + "cognos_columns", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_cognos_dashboard_attrs( + attrs: CognosDashboardAttributes, obj: CognosDashboard +) -> None: + """Populate CognosDashboard-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.cognos_id = obj.cognos_id + attrs.cognos_path = obj.cognos_path + attrs.cognos_parent_name = obj.cognos_parent_name + attrs.cognos_parent_qualified_name = obj.cognos_parent_qualified_name + attrs.cognos_version = obj.cognos_version + attrs.cognos_type = obj.cognos_type + attrs.cognos_is_hidden = obj.cognos_is_hidden + attrs.cognos_is_disabled = obj.cognos_is_disabled + attrs.cognos_default_screen_tip = obj.cognos_default_screen_tip + + +def _extract_cognos_dashboard_attrs(attrs: CognosDashboardAttributes) -> dict: + """Extract all CognosDashboard attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["cognos_id"] = attrs.cognos_id + result["cognos_path"] = attrs.cognos_path + result["cognos_parent_name"] = attrs.cognos_parent_name + result["cognos_parent_qualified_name"] = attrs.cognos_parent_qualified_name + result["cognos_version"] = attrs.cognos_version + result["cognos_type"] = attrs.cognos_type + result["cognos_is_hidden"] = attrs.cognos_is_hidden + result["cognos_is_disabled"] = attrs.cognos_is_disabled + result["cognos_default_screen_tip"] = attrs.cognos_default_screen_tip + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _cognos_dashboard_to_nested( + cognos_dashboard: CognosDashboard, +) -> CognosDashboardNested: + """Convert flat CognosDashboard to nested format.""" + attrs = CognosDashboardAttributes() + _populate_cognos_dashboard_attrs(attrs, cognos_dashboard) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + cognos_dashboard, + _COGNOS_DASHBOARD_REL_FIELDS, + CognosDashboardRelationshipAttributes, + ) + return CognosDashboardNested( + guid=cognos_dashboard.guid, + type_name=cognos_dashboard.type_name, + status=cognos_dashboard.status, + version=cognos_dashboard.version, + create_time=cognos_dashboard.create_time, + update_time=cognos_dashboard.update_time, + created_by=cognos_dashboard.created_by, + updated_by=cognos_dashboard.updated_by, + classifications=cognos_dashboard.classifications, + classification_names=cognos_dashboard.classification_names, + meanings=cognos_dashboard.meanings, + labels=cognos_dashboard.labels, + business_attributes=cognos_dashboard.business_attributes, + custom_attributes=cognos_dashboard.custom_attributes, + pending_tasks=cognos_dashboard.pending_tasks, + proxy=cognos_dashboard.proxy, + is_incomplete=cognos_dashboard.is_incomplete, + provenance_type=cognos_dashboard.provenance_type, + home_id=cognos_dashboard.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _cognos_dashboard_from_nested(nested: CognosDashboardNested) -> CognosDashboard: + """Convert nested format to flat CognosDashboard.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else CognosDashboardAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _COGNOS_DASHBOARD_REL_FIELDS, + CognosDashboardRelationshipAttributes, + ) + return CognosDashboard( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_cognos_dashboard_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _cognos_dashboard_to_nested_bytes( + cognos_dashboard: CognosDashboard, serde: Serde +) -> bytes: + """Convert flat CognosDashboard to nested JSON bytes.""" + return serde.encode(_cognos_dashboard_to_nested(cognos_dashboard)) + + +def _cognos_dashboard_from_nested_bytes(data: bytes, serde: Serde) -> CognosDashboard: + """Convert nested JSON bytes to flat CognosDashboard.""" + nested = serde.decode(data, CognosDashboardNested) + return _cognos_dashboard_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + RelationField, +) + +CognosDashboard.COGNOS_ID = KeywordField("cognosId", "cognosId") +CognosDashboard.COGNOS_PATH = KeywordField("cognosPath", "cognosPath") +CognosDashboard.COGNOS_PARENT_NAME = KeywordTextField( + "cognosParentName", "cognosParentName", "cognosParentName.text" +) +CognosDashboard.COGNOS_PARENT_QUALIFIED_NAME = KeywordField( + "cognosParentQualifiedName", "cognosParentQualifiedName" +) +CognosDashboard.COGNOS_VERSION = KeywordField("cognosVersion", "cognosVersion") +CognosDashboard.COGNOS_TYPE = KeywordField("cognosType", "cognosType") +CognosDashboard.COGNOS_IS_HIDDEN = BooleanField("cognosIsHidden", "cognosIsHidden") +CognosDashboard.COGNOS_IS_DISABLED = BooleanField( + "cognosIsDisabled", "cognosIsDisabled" +) +CognosDashboard.COGNOS_DEFAULT_SCREEN_TIP = KeywordField( + "cognosDefaultScreenTip", "cognosDefaultScreenTip" +) +CognosDashboard.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +CognosDashboard.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +CognosDashboard.ANOMALO_CHECKS = RelationField("anomaloChecks") +CognosDashboard.APPLICATION = RelationField("application") +CognosDashboard.APPLICATION_FIELD = RelationField("applicationField") +CognosDashboard.COGNOS_FOLDER = RelationField("cognosFolder") +CognosDashboard.COGNOS_COLUMNS = RelationField("cognosColumns") +CognosDashboard.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +CognosDashboard.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +CognosDashboard.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +CognosDashboard.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +CognosDashboard.METRICS = RelationField("metrics") +CognosDashboard.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +CognosDashboard.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +CognosDashboard.MEANINGS = RelationField("meanings") +CognosDashboard.MC_MONITORS = RelationField("mcMonitors") +CognosDashboard.MC_INCIDENTS = RelationField("mcIncidents") +CognosDashboard.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +CognosDashboard.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +CognosDashboard.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +CognosDashboard.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +CognosDashboard.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +CognosDashboard.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +CognosDashboard.FILES = RelationField("files") +CognosDashboard.LINKS = RelationField("links") +CognosDashboard.README = RelationField("readme") +CognosDashboard.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +CognosDashboard.SODA_CHECKS = RelationField("sodaChecks") +CognosDashboard.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +CognosDashboard.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/cognos_dataset.py b/pyatlan_v9/model/assets/cognos_dataset.py new file mode 100644 index 000000000..5e7dfa3c7 --- /dev/null +++ b/pyatlan_v9/model/assets/cognos_dataset.py @@ -0,0 +1,662 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +CognosDataset asset model with flattened inheritance. + +This module provides: +- CognosDataset: Flat asset class (easy to use) +- CognosDatasetAttributes: Nested attributes struct (extends AssetAttributes) +- CognosDatasetNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .cognos_related import RelatedCognosColumn, RelatedCognosFolder + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class CognosDataset(Asset): + """ + Instance of a Cognos dataset in Atlan. + """ + + COGNOS_ID: ClassVar[Any] = None + COGNOS_PATH: ClassVar[Any] = None + COGNOS_PARENT_NAME: ClassVar[Any] = None + COGNOS_PARENT_QUALIFIED_NAME: ClassVar[Any] = None + COGNOS_VERSION: ClassVar[Any] = None + COGNOS_TYPE: ClassVar[Any] = None + COGNOS_IS_HIDDEN: ClassVar[Any] = None + COGNOS_IS_DISABLED: ClassVar[Any] = None + COGNOS_DEFAULT_SCREEN_TIP: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + COGNOS_FOLDER: ClassVar[Any] = None + COGNOS_COLUMNS: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "CognosDataset" + + cognos_id: Union[str, None, UnsetType] = UNSET + """ID of the asset in Cognos.""" + + cognos_path: Union[str, None, UnsetType] = UNSET + """Path of the asset in Cognos (e.g. /content/folder[@name='Folder Name']).""" + + cognos_parent_name: Union[str, None, UnsetType] = UNSET + """Name of the parent of the asset in Cognos.""" + + cognos_parent_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the parent asset in Cognos.""" + + cognos_version: Union[str, None, UnsetType] = UNSET + """Version of the Cognos asset.""" + + cognos_type: Union[str, None, UnsetType] = UNSET + """Type of the Cognos asset (e.g. report, dashboard, package, etc).""" + + cognos_is_hidden: Union[bool, None, UnsetType] = UNSET + """Whether the Cognos asset is hidden from the UI.""" + + cognos_is_disabled: Union[bool, None, UnsetType] = UNSET + """Whether the Cognos asset is disabled.""" + + cognos_default_screen_tip: Union[str, None, UnsetType] = UNSET + """Tooltip text present for the Cognos asset.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cognos_folder: Union[RelatedCognosFolder, None, UnsetType] = UNSET + """Folder containing the dataset.""" + + cognos_columns: Union[List[RelatedCognosColumn], None, UnsetType] = UNSET + """Columns contained in the dataset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "CognosDataset" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _cognos_dataset_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> CognosDataset: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + CognosDataset instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _cognos_dataset_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class CognosDatasetAttributes(AssetAttributes): + """CognosDataset-specific attributes for nested API format.""" + + cognos_id: Union[str, None, UnsetType] = UNSET + """ID of the asset in Cognos.""" + + cognos_path: Union[str, None, UnsetType] = UNSET + """Path of the asset in Cognos (e.g. /content/folder[@name='Folder Name']).""" + + cognos_parent_name: Union[str, None, UnsetType] = UNSET + """Name of the parent of the asset in Cognos.""" + + cognos_parent_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the parent asset in Cognos.""" + + cognos_version: Union[str, None, UnsetType] = UNSET + """Version of the Cognos asset.""" + + cognos_type: Union[str, None, UnsetType] = UNSET + """Type of the Cognos asset (e.g. report, dashboard, package, etc).""" + + cognos_is_hidden: Union[bool, None, UnsetType] = UNSET + """Whether the Cognos asset is hidden from the UI.""" + + cognos_is_disabled: Union[bool, None, UnsetType] = UNSET + """Whether the Cognos asset is disabled.""" + + cognos_default_screen_tip: Union[str, None, UnsetType] = UNSET + """Tooltip text present for the Cognos asset.""" + + +class CognosDatasetRelationshipAttributes(AssetRelationshipAttributes): + """CognosDataset-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cognos_folder: Union[RelatedCognosFolder, None, UnsetType] = UNSET + """Folder containing the dataset.""" + + cognos_columns: Union[List[RelatedCognosColumn], None, UnsetType] = UNSET + """Columns contained in the dataset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class CognosDatasetNested(AssetNested): + """CognosDataset in nested API format for high-performance serialization.""" + + attributes: Union[CognosDatasetAttributes, UnsetType] = UNSET + relationship_attributes: Union[CognosDatasetRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + CognosDatasetRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + CognosDatasetRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_COGNOS_DATASET_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "cognos_folder", + "cognos_columns", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_cognos_dataset_attrs( + attrs: CognosDatasetAttributes, obj: CognosDataset +) -> None: + """Populate CognosDataset-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.cognos_id = obj.cognos_id + attrs.cognos_path = obj.cognos_path + attrs.cognos_parent_name = obj.cognos_parent_name + attrs.cognos_parent_qualified_name = obj.cognos_parent_qualified_name + attrs.cognos_version = obj.cognos_version + attrs.cognos_type = obj.cognos_type + attrs.cognos_is_hidden = obj.cognos_is_hidden + attrs.cognos_is_disabled = obj.cognos_is_disabled + attrs.cognos_default_screen_tip = obj.cognos_default_screen_tip + + +def _extract_cognos_dataset_attrs(attrs: CognosDatasetAttributes) -> dict: + """Extract all CognosDataset attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["cognos_id"] = attrs.cognos_id + result["cognos_path"] = attrs.cognos_path + result["cognos_parent_name"] = attrs.cognos_parent_name + result["cognos_parent_qualified_name"] = attrs.cognos_parent_qualified_name + result["cognos_version"] = attrs.cognos_version + result["cognos_type"] = attrs.cognos_type + result["cognos_is_hidden"] = attrs.cognos_is_hidden + result["cognos_is_disabled"] = attrs.cognos_is_disabled + result["cognos_default_screen_tip"] = attrs.cognos_default_screen_tip + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _cognos_dataset_to_nested(cognos_dataset: CognosDataset) -> CognosDatasetNested: + """Convert flat CognosDataset to nested format.""" + attrs = CognosDatasetAttributes() + _populate_cognos_dataset_attrs(attrs, cognos_dataset) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + cognos_dataset, _COGNOS_DATASET_REL_FIELDS, CognosDatasetRelationshipAttributes + ) + return CognosDatasetNested( + guid=cognos_dataset.guid, + type_name=cognos_dataset.type_name, + status=cognos_dataset.status, + version=cognos_dataset.version, + create_time=cognos_dataset.create_time, + update_time=cognos_dataset.update_time, + created_by=cognos_dataset.created_by, + updated_by=cognos_dataset.updated_by, + classifications=cognos_dataset.classifications, + classification_names=cognos_dataset.classification_names, + meanings=cognos_dataset.meanings, + labels=cognos_dataset.labels, + business_attributes=cognos_dataset.business_attributes, + custom_attributes=cognos_dataset.custom_attributes, + pending_tasks=cognos_dataset.pending_tasks, + proxy=cognos_dataset.proxy, + is_incomplete=cognos_dataset.is_incomplete, + provenance_type=cognos_dataset.provenance_type, + home_id=cognos_dataset.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _cognos_dataset_from_nested(nested: CognosDatasetNested) -> CognosDataset: + """Convert nested format to flat CognosDataset.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else CognosDatasetAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _COGNOS_DATASET_REL_FIELDS, + CognosDatasetRelationshipAttributes, + ) + return CognosDataset( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_cognos_dataset_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _cognos_dataset_to_nested_bytes( + cognos_dataset: CognosDataset, serde: Serde +) -> bytes: + """Convert flat CognosDataset to nested JSON bytes.""" + return serde.encode(_cognos_dataset_to_nested(cognos_dataset)) + + +def _cognos_dataset_from_nested_bytes(data: bytes, serde: Serde) -> CognosDataset: + """Convert nested JSON bytes to flat CognosDataset.""" + nested = serde.decode(data, CognosDatasetNested) + return _cognos_dataset_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + RelationField, +) + +CognosDataset.COGNOS_ID = KeywordField("cognosId", "cognosId") +CognosDataset.COGNOS_PATH = KeywordField("cognosPath", "cognosPath") +CognosDataset.COGNOS_PARENT_NAME = KeywordTextField( + "cognosParentName", "cognosParentName", "cognosParentName.text" +) +CognosDataset.COGNOS_PARENT_QUALIFIED_NAME = KeywordField( + "cognosParentQualifiedName", "cognosParentQualifiedName" +) +CognosDataset.COGNOS_VERSION = KeywordField("cognosVersion", "cognosVersion") +CognosDataset.COGNOS_TYPE = KeywordField("cognosType", "cognosType") +CognosDataset.COGNOS_IS_HIDDEN = BooleanField("cognosIsHidden", "cognosIsHidden") +CognosDataset.COGNOS_IS_DISABLED = BooleanField("cognosIsDisabled", "cognosIsDisabled") +CognosDataset.COGNOS_DEFAULT_SCREEN_TIP = KeywordField( + "cognosDefaultScreenTip", "cognosDefaultScreenTip" +) +CognosDataset.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +CognosDataset.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +CognosDataset.ANOMALO_CHECKS = RelationField("anomaloChecks") +CognosDataset.APPLICATION = RelationField("application") +CognosDataset.APPLICATION_FIELD = RelationField("applicationField") +CognosDataset.COGNOS_FOLDER = RelationField("cognosFolder") +CognosDataset.COGNOS_COLUMNS = RelationField("cognosColumns") +CognosDataset.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +CognosDataset.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +CognosDataset.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +CognosDataset.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +CognosDataset.METRICS = RelationField("metrics") +CognosDataset.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +CognosDataset.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +CognosDataset.MEANINGS = RelationField("meanings") +CognosDataset.MC_MONITORS = RelationField("mcMonitors") +CognosDataset.MC_INCIDENTS = RelationField("mcIncidents") +CognosDataset.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +CognosDataset.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +CognosDataset.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +CognosDataset.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +CognosDataset.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +CognosDataset.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +CognosDataset.FILES = RelationField("files") +CognosDataset.LINKS = RelationField("links") +CognosDataset.README = RelationField("readme") +CognosDataset.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +CognosDataset.SODA_CHECKS = RelationField("sodaChecks") +CognosDataset.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +CognosDataset.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/cognos_datasource.py b/pyatlan_v9/model/assets/cognos_datasource.py new file mode 100644 index 000000000..45b9a2aa4 --- /dev/null +++ b/pyatlan_v9/model/assets/cognos_datasource.py @@ -0,0 +1,655 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +CognosDatasource asset model with flattened inheritance. + +This module provides: +- CognosDatasource: Flat asset class (easy to use) +- CognosDatasourceAttributes: Nested attributes struct (extends AssetAttributes) +- CognosDatasourceNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class CognosDatasource(Asset): + """ + Instance of a Cognos datasource in Atlan. + """ + + COGNOS_CONNECTION_STRING: ClassVar[Any] = None + COGNOS_ID: ClassVar[Any] = None + COGNOS_PATH: ClassVar[Any] = None + COGNOS_PARENT_NAME: ClassVar[Any] = None + COGNOS_PARENT_QUALIFIED_NAME: ClassVar[Any] = None + COGNOS_VERSION: ClassVar[Any] = None + COGNOS_TYPE: ClassVar[Any] = None + COGNOS_IS_HIDDEN: ClassVar[Any] = None + COGNOS_IS_DISABLED: ClassVar[Any] = None + COGNOS_DEFAULT_SCREEN_TIP: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "CognosDatasource" + + cognos_connection_string: Union[str, None, UnsetType] = UNSET + """Connection string of a Cognos datasource.""" + + cognos_id: Union[str, None, UnsetType] = UNSET + """ID of the asset in Cognos.""" + + cognos_path: Union[str, None, UnsetType] = UNSET + """Path of the asset in Cognos (e.g. /content/folder[@name='Folder Name']).""" + + cognos_parent_name: Union[str, None, UnsetType] = UNSET + """Name of the parent of the asset in Cognos.""" + + cognos_parent_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the parent asset in Cognos.""" + + cognos_version: Union[str, None, UnsetType] = UNSET + """Version of the Cognos asset.""" + + cognos_type: Union[str, None, UnsetType] = UNSET + """Type of the Cognos asset (e.g. report, dashboard, package, etc).""" + + cognos_is_hidden: Union[bool, None, UnsetType] = UNSET + """Whether the Cognos asset is hidden from the UI.""" + + cognos_is_disabled: Union[bool, None, UnsetType] = UNSET + """Whether the Cognos asset is disabled.""" + + cognos_default_screen_tip: Union[str, None, UnsetType] = UNSET + """Tooltip text present for the Cognos asset.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "CognosDatasource" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _cognos_datasource_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> CognosDatasource: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + CognosDatasource instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _cognos_datasource_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class CognosDatasourceAttributes(AssetAttributes): + """CognosDatasource-specific attributes for nested API format.""" + + cognos_connection_string: Union[str, None, UnsetType] = UNSET + """Connection string of a Cognos datasource.""" + + cognos_id: Union[str, None, UnsetType] = UNSET + """ID of the asset in Cognos.""" + + cognos_path: Union[str, None, UnsetType] = UNSET + """Path of the asset in Cognos (e.g. /content/folder[@name='Folder Name']).""" + + cognos_parent_name: Union[str, None, UnsetType] = UNSET + """Name of the parent of the asset in Cognos.""" + + cognos_parent_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the parent asset in Cognos.""" + + cognos_version: Union[str, None, UnsetType] = UNSET + """Version of the Cognos asset.""" + + cognos_type: Union[str, None, UnsetType] = UNSET + """Type of the Cognos asset (e.g. report, dashboard, package, etc).""" + + cognos_is_hidden: Union[bool, None, UnsetType] = UNSET + """Whether the Cognos asset is hidden from the UI.""" + + cognos_is_disabled: Union[bool, None, UnsetType] = UNSET + """Whether the Cognos asset is disabled.""" + + cognos_default_screen_tip: Union[str, None, UnsetType] = UNSET + """Tooltip text present for the Cognos asset.""" + + +class CognosDatasourceRelationshipAttributes(AssetRelationshipAttributes): + """CognosDatasource-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class CognosDatasourceNested(AssetNested): + """CognosDatasource in nested API format for high-performance serialization.""" + + attributes: Union[CognosDatasourceAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + CognosDatasourceRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + CognosDatasourceRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + CognosDatasourceRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_COGNOS_DATASOURCE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_cognos_datasource_attrs( + attrs: CognosDatasourceAttributes, obj: CognosDatasource +) -> None: + """Populate CognosDatasource-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.cognos_connection_string = obj.cognos_connection_string + attrs.cognos_id = obj.cognos_id + attrs.cognos_path = obj.cognos_path + attrs.cognos_parent_name = obj.cognos_parent_name + attrs.cognos_parent_qualified_name = obj.cognos_parent_qualified_name + attrs.cognos_version = obj.cognos_version + attrs.cognos_type = obj.cognos_type + attrs.cognos_is_hidden = obj.cognos_is_hidden + attrs.cognos_is_disabled = obj.cognos_is_disabled + attrs.cognos_default_screen_tip = obj.cognos_default_screen_tip + + +def _extract_cognos_datasource_attrs(attrs: CognosDatasourceAttributes) -> dict: + """Extract all CognosDatasource attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["cognos_connection_string"] = attrs.cognos_connection_string + result["cognos_id"] = attrs.cognos_id + result["cognos_path"] = attrs.cognos_path + result["cognos_parent_name"] = attrs.cognos_parent_name + result["cognos_parent_qualified_name"] = attrs.cognos_parent_qualified_name + result["cognos_version"] = attrs.cognos_version + result["cognos_type"] = attrs.cognos_type + result["cognos_is_hidden"] = attrs.cognos_is_hidden + result["cognos_is_disabled"] = attrs.cognos_is_disabled + result["cognos_default_screen_tip"] = attrs.cognos_default_screen_tip + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _cognos_datasource_to_nested( + cognos_datasource: CognosDatasource, +) -> CognosDatasourceNested: + """Convert flat CognosDatasource to nested format.""" + attrs = CognosDatasourceAttributes() + _populate_cognos_datasource_attrs(attrs, cognos_datasource) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + cognos_datasource, + _COGNOS_DATASOURCE_REL_FIELDS, + CognosDatasourceRelationshipAttributes, + ) + return CognosDatasourceNested( + guid=cognos_datasource.guid, + type_name=cognos_datasource.type_name, + status=cognos_datasource.status, + version=cognos_datasource.version, + create_time=cognos_datasource.create_time, + update_time=cognos_datasource.update_time, + created_by=cognos_datasource.created_by, + updated_by=cognos_datasource.updated_by, + classifications=cognos_datasource.classifications, + classification_names=cognos_datasource.classification_names, + meanings=cognos_datasource.meanings, + labels=cognos_datasource.labels, + business_attributes=cognos_datasource.business_attributes, + custom_attributes=cognos_datasource.custom_attributes, + pending_tasks=cognos_datasource.pending_tasks, + proxy=cognos_datasource.proxy, + is_incomplete=cognos_datasource.is_incomplete, + provenance_type=cognos_datasource.provenance_type, + home_id=cognos_datasource.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _cognos_datasource_from_nested(nested: CognosDatasourceNested) -> CognosDatasource: + """Convert nested format to flat CognosDatasource.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else CognosDatasourceAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _COGNOS_DATASOURCE_REL_FIELDS, + CognosDatasourceRelationshipAttributes, + ) + return CognosDatasource( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_cognos_datasource_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _cognos_datasource_to_nested_bytes( + cognos_datasource: CognosDatasource, serde: Serde +) -> bytes: + """Convert flat CognosDatasource to nested JSON bytes.""" + return serde.encode(_cognos_datasource_to_nested(cognos_datasource)) + + +def _cognos_datasource_from_nested_bytes(data: bytes, serde: Serde) -> CognosDatasource: + """Convert nested JSON bytes to flat CognosDatasource.""" + nested = serde.decode(data, CognosDatasourceNested) + return _cognos_datasource_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + RelationField, +) + +CognosDatasource.COGNOS_CONNECTION_STRING = KeywordField( + "cognosConnectionString", "cognosConnectionString" +) +CognosDatasource.COGNOS_ID = KeywordField("cognosId", "cognosId") +CognosDatasource.COGNOS_PATH = KeywordField("cognosPath", "cognosPath") +CognosDatasource.COGNOS_PARENT_NAME = KeywordTextField( + "cognosParentName", "cognosParentName", "cognosParentName.text" +) +CognosDatasource.COGNOS_PARENT_QUALIFIED_NAME = KeywordField( + "cognosParentQualifiedName", "cognosParentQualifiedName" +) +CognosDatasource.COGNOS_VERSION = KeywordField("cognosVersion", "cognosVersion") +CognosDatasource.COGNOS_TYPE = KeywordField("cognosType", "cognosType") +CognosDatasource.COGNOS_IS_HIDDEN = BooleanField("cognosIsHidden", "cognosIsHidden") +CognosDatasource.COGNOS_IS_DISABLED = BooleanField( + "cognosIsDisabled", "cognosIsDisabled" +) +CognosDatasource.COGNOS_DEFAULT_SCREEN_TIP = KeywordField( + "cognosDefaultScreenTip", "cognosDefaultScreenTip" +) +CognosDatasource.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +CognosDatasource.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +CognosDatasource.ANOMALO_CHECKS = RelationField("anomaloChecks") +CognosDatasource.APPLICATION = RelationField("application") +CognosDatasource.APPLICATION_FIELD = RelationField("applicationField") +CognosDatasource.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +CognosDatasource.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +CognosDatasource.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +CognosDatasource.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +CognosDatasource.METRICS = RelationField("metrics") +CognosDatasource.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +CognosDatasource.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +CognosDatasource.MEANINGS = RelationField("meanings") +CognosDatasource.MC_MONITORS = RelationField("mcMonitors") +CognosDatasource.MC_INCIDENTS = RelationField("mcIncidents") +CognosDatasource.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +CognosDatasource.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +CognosDatasource.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +CognosDatasource.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +CognosDatasource.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +CognosDatasource.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +CognosDatasource.FILES = RelationField("files") +CognosDatasource.LINKS = RelationField("links") +CognosDatasource.README = RelationField("readme") +CognosDatasource.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +CognosDatasource.SODA_CHECKS = RelationField("sodaChecks") +CognosDatasource.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +CognosDatasource.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/cognos_exploration.py b/pyatlan_v9/model/assets/cognos_exploration.py new file mode 100644 index 000000000..a7f88e0c0 --- /dev/null +++ b/pyatlan_v9/model/assets/cognos_exploration.py @@ -0,0 +1,676 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +CognosExploration asset model with flattened inheritance. + +This module provides: +- CognosExploration: Flat asset class (easy to use) +- CognosExplorationAttributes: Nested attributes struct (extends AssetAttributes) +- CognosExplorationNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .cognos_related import RelatedCognosColumn, RelatedCognosFolder + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class CognosExploration(Asset): + """ + Instance of a Cognos Exploration in Atlan. + """ + + COGNOS_ID: ClassVar[Any] = None + COGNOS_PATH: ClassVar[Any] = None + COGNOS_PARENT_NAME: ClassVar[Any] = None + COGNOS_PARENT_QUALIFIED_NAME: ClassVar[Any] = None + COGNOS_VERSION: ClassVar[Any] = None + COGNOS_TYPE: ClassVar[Any] = None + COGNOS_IS_HIDDEN: ClassVar[Any] = None + COGNOS_IS_DISABLED: ClassVar[Any] = None + COGNOS_DEFAULT_SCREEN_TIP: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + COGNOS_FOLDER: ClassVar[Any] = None + COGNOS_COLUMNS: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "CognosExploration" + + cognos_id: Union[str, None, UnsetType] = UNSET + """ID of the asset in Cognos.""" + + cognos_path: Union[str, None, UnsetType] = UNSET + """Path of the asset in Cognos (e.g. /content/folder[@name='Folder Name']).""" + + cognos_parent_name: Union[str, None, UnsetType] = UNSET + """Name of the parent of the asset in Cognos.""" + + cognos_parent_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the parent asset in Cognos.""" + + cognos_version: Union[str, None, UnsetType] = UNSET + """Version of the Cognos asset.""" + + cognos_type: Union[str, None, UnsetType] = UNSET + """Type of the Cognos asset (e.g. report, dashboard, package, etc).""" + + cognos_is_hidden: Union[bool, None, UnsetType] = UNSET + """Whether the Cognos asset is hidden from the UI.""" + + cognos_is_disabled: Union[bool, None, UnsetType] = UNSET + """Whether the Cognos asset is disabled.""" + + cognos_default_screen_tip: Union[str, None, UnsetType] = UNSET + """Tooltip text present for the Cognos asset.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cognos_folder: Union[RelatedCognosFolder, None, UnsetType] = UNSET + """Folder containing the exploration.""" + + cognos_columns: Union[List[RelatedCognosColumn], None, UnsetType] = UNSET + """Columns contained in the exploration.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "CognosExploration" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _cognos_exploration_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> CognosExploration: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + CognosExploration instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _cognos_exploration_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class CognosExplorationAttributes(AssetAttributes): + """CognosExploration-specific attributes for nested API format.""" + + cognos_id: Union[str, None, UnsetType] = UNSET + """ID of the asset in Cognos.""" + + cognos_path: Union[str, None, UnsetType] = UNSET + """Path of the asset in Cognos (e.g. /content/folder[@name='Folder Name']).""" + + cognos_parent_name: Union[str, None, UnsetType] = UNSET + """Name of the parent of the asset in Cognos.""" + + cognos_parent_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the parent asset in Cognos.""" + + cognos_version: Union[str, None, UnsetType] = UNSET + """Version of the Cognos asset.""" + + cognos_type: Union[str, None, UnsetType] = UNSET + """Type of the Cognos asset (e.g. report, dashboard, package, etc).""" + + cognos_is_hidden: Union[bool, None, UnsetType] = UNSET + """Whether the Cognos asset is hidden from the UI.""" + + cognos_is_disabled: Union[bool, None, UnsetType] = UNSET + """Whether the Cognos asset is disabled.""" + + cognos_default_screen_tip: Union[str, None, UnsetType] = UNSET + """Tooltip text present for the Cognos asset.""" + + +class CognosExplorationRelationshipAttributes(AssetRelationshipAttributes): + """CognosExploration-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cognos_folder: Union[RelatedCognosFolder, None, UnsetType] = UNSET + """Folder containing the exploration.""" + + cognos_columns: Union[List[RelatedCognosColumn], None, UnsetType] = UNSET + """Columns contained in the exploration.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class CognosExplorationNested(AssetNested): + """CognosExploration in nested API format for high-performance serialization.""" + + attributes: Union[CognosExplorationAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + CognosExplorationRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + CognosExplorationRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + CognosExplorationRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_COGNOS_EXPLORATION_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "cognos_folder", + "cognos_columns", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_cognos_exploration_attrs( + attrs: CognosExplorationAttributes, obj: CognosExploration +) -> None: + """Populate CognosExploration-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.cognos_id = obj.cognos_id + attrs.cognos_path = obj.cognos_path + attrs.cognos_parent_name = obj.cognos_parent_name + attrs.cognos_parent_qualified_name = obj.cognos_parent_qualified_name + attrs.cognos_version = obj.cognos_version + attrs.cognos_type = obj.cognos_type + attrs.cognos_is_hidden = obj.cognos_is_hidden + attrs.cognos_is_disabled = obj.cognos_is_disabled + attrs.cognos_default_screen_tip = obj.cognos_default_screen_tip + + +def _extract_cognos_exploration_attrs(attrs: CognosExplorationAttributes) -> dict: + """Extract all CognosExploration attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["cognos_id"] = attrs.cognos_id + result["cognos_path"] = attrs.cognos_path + result["cognos_parent_name"] = attrs.cognos_parent_name + result["cognos_parent_qualified_name"] = attrs.cognos_parent_qualified_name + result["cognos_version"] = attrs.cognos_version + result["cognos_type"] = attrs.cognos_type + result["cognos_is_hidden"] = attrs.cognos_is_hidden + result["cognos_is_disabled"] = attrs.cognos_is_disabled + result["cognos_default_screen_tip"] = attrs.cognos_default_screen_tip + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _cognos_exploration_to_nested( + cognos_exploration: CognosExploration, +) -> CognosExplorationNested: + """Convert flat CognosExploration to nested format.""" + attrs = CognosExplorationAttributes() + _populate_cognos_exploration_attrs(attrs, cognos_exploration) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + cognos_exploration, + _COGNOS_EXPLORATION_REL_FIELDS, + CognosExplorationRelationshipAttributes, + ) + return CognosExplorationNested( + guid=cognos_exploration.guid, + type_name=cognos_exploration.type_name, + status=cognos_exploration.status, + version=cognos_exploration.version, + create_time=cognos_exploration.create_time, + update_time=cognos_exploration.update_time, + created_by=cognos_exploration.created_by, + updated_by=cognos_exploration.updated_by, + classifications=cognos_exploration.classifications, + classification_names=cognos_exploration.classification_names, + meanings=cognos_exploration.meanings, + labels=cognos_exploration.labels, + business_attributes=cognos_exploration.business_attributes, + custom_attributes=cognos_exploration.custom_attributes, + pending_tasks=cognos_exploration.pending_tasks, + proxy=cognos_exploration.proxy, + is_incomplete=cognos_exploration.is_incomplete, + provenance_type=cognos_exploration.provenance_type, + home_id=cognos_exploration.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _cognos_exploration_from_nested( + nested: CognosExplorationNested, +) -> CognosExploration: + """Convert nested format to flat CognosExploration.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else CognosExplorationAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _COGNOS_EXPLORATION_REL_FIELDS, + CognosExplorationRelationshipAttributes, + ) + return CognosExploration( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_cognos_exploration_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _cognos_exploration_to_nested_bytes( + cognos_exploration: CognosExploration, serde: Serde +) -> bytes: + """Convert flat CognosExploration to nested JSON bytes.""" + return serde.encode(_cognos_exploration_to_nested(cognos_exploration)) + + +def _cognos_exploration_from_nested_bytes( + data: bytes, serde: Serde +) -> CognosExploration: + """Convert nested JSON bytes to flat CognosExploration.""" + nested = serde.decode(data, CognosExplorationNested) + return _cognos_exploration_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + RelationField, +) + +CognosExploration.COGNOS_ID = KeywordField("cognosId", "cognosId") +CognosExploration.COGNOS_PATH = KeywordField("cognosPath", "cognosPath") +CognosExploration.COGNOS_PARENT_NAME = KeywordTextField( + "cognosParentName", "cognosParentName", "cognosParentName.text" +) +CognosExploration.COGNOS_PARENT_QUALIFIED_NAME = KeywordField( + "cognosParentQualifiedName", "cognosParentQualifiedName" +) +CognosExploration.COGNOS_VERSION = KeywordField("cognosVersion", "cognosVersion") +CognosExploration.COGNOS_TYPE = KeywordField("cognosType", "cognosType") +CognosExploration.COGNOS_IS_HIDDEN = BooleanField("cognosIsHidden", "cognosIsHidden") +CognosExploration.COGNOS_IS_DISABLED = BooleanField( + "cognosIsDisabled", "cognosIsDisabled" +) +CognosExploration.COGNOS_DEFAULT_SCREEN_TIP = KeywordField( + "cognosDefaultScreenTip", "cognosDefaultScreenTip" +) +CognosExploration.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +CognosExploration.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +CognosExploration.ANOMALO_CHECKS = RelationField("anomaloChecks") +CognosExploration.APPLICATION = RelationField("application") +CognosExploration.APPLICATION_FIELD = RelationField("applicationField") +CognosExploration.COGNOS_FOLDER = RelationField("cognosFolder") +CognosExploration.COGNOS_COLUMNS = RelationField("cognosColumns") +CognosExploration.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +CognosExploration.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +CognosExploration.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +CognosExploration.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +CognosExploration.METRICS = RelationField("metrics") +CognosExploration.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +CognosExploration.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +CognosExploration.MEANINGS = RelationField("meanings") +CognosExploration.MC_MONITORS = RelationField("mcMonitors") +CognosExploration.MC_INCIDENTS = RelationField("mcIncidents") +CognosExploration.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +CognosExploration.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +CognosExploration.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +CognosExploration.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +CognosExploration.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +CognosExploration.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +CognosExploration.FILES = RelationField("files") +CognosExploration.LINKS = RelationField("links") +CognosExploration.README = RelationField("readme") +CognosExploration.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +CognosExploration.SODA_CHECKS = RelationField("sodaChecks") +CognosExploration.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +CognosExploration.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/cognos_file.py b/pyatlan_v9/model/assets/cognos_file.py new file mode 100644 index 000000000..73991b194 --- /dev/null +++ b/pyatlan_v9/model/assets/cognos_file.py @@ -0,0 +1,654 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +CognosFile asset model with flattened inheritance. + +This module provides: +- CognosFile: Flat asset class (easy to use) +- CognosFileAttributes: Nested attributes struct (extends AssetAttributes) +- CognosFileNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .cognos_related import RelatedCognosColumn, RelatedCognosFolder + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class CognosFile(Asset): + """ + Instance of a Cognos file in Atlan. + """ + + COGNOS_ID: ClassVar[Any] = None + COGNOS_PATH: ClassVar[Any] = None + COGNOS_PARENT_NAME: ClassVar[Any] = None + COGNOS_PARENT_QUALIFIED_NAME: ClassVar[Any] = None + COGNOS_VERSION: ClassVar[Any] = None + COGNOS_TYPE: ClassVar[Any] = None + COGNOS_IS_HIDDEN: ClassVar[Any] = None + COGNOS_IS_DISABLED: ClassVar[Any] = None + COGNOS_DEFAULT_SCREEN_TIP: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + COGNOS_FOLDER: ClassVar[Any] = None + COGNOS_COLUMNS: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "CognosFile" + + cognos_id: Union[str, None, UnsetType] = UNSET + """ID of the asset in Cognos.""" + + cognos_path: Union[str, None, UnsetType] = UNSET + """Path of the asset in Cognos (e.g. /content/folder[@name='Folder Name']).""" + + cognos_parent_name: Union[str, None, UnsetType] = UNSET + """Name of the parent of the asset in Cognos.""" + + cognos_parent_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the parent asset in Cognos.""" + + cognos_version: Union[str, None, UnsetType] = UNSET + """Version of the Cognos asset.""" + + cognos_type: Union[str, None, UnsetType] = UNSET + """Type of the Cognos asset (e.g. report, dashboard, package, etc).""" + + cognos_is_hidden: Union[bool, None, UnsetType] = UNSET + """Whether the Cognos asset is hidden from the UI.""" + + cognos_is_disabled: Union[bool, None, UnsetType] = UNSET + """Whether the Cognos asset is disabled.""" + + cognos_default_screen_tip: Union[str, None, UnsetType] = UNSET + """Tooltip text present for the Cognos asset.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cognos_folder: Union[RelatedCognosFolder, None, UnsetType] = UNSET + """Folder containing the file.""" + + cognos_columns: Union[List[RelatedCognosColumn], None, UnsetType] = UNSET + """Columns contained in the file.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "CognosFile" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _cognos_file_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> CognosFile: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + CognosFile instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _cognos_file_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class CognosFileAttributes(AssetAttributes): + """CognosFile-specific attributes for nested API format.""" + + cognos_id: Union[str, None, UnsetType] = UNSET + """ID of the asset in Cognos.""" + + cognos_path: Union[str, None, UnsetType] = UNSET + """Path of the asset in Cognos (e.g. /content/folder[@name='Folder Name']).""" + + cognos_parent_name: Union[str, None, UnsetType] = UNSET + """Name of the parent of the asset in Cognos.""" + + cognos_parent_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the parent asset in Cognos.""" + + cognos_version: Union[str, None, UnsetType] = UNSET + """Version of the Cognos asset.""" + + cognos_type: Union[str, None, UnsetType] = UNSET + """Type of the Cognos asset (e.g. report, dashboard, package, etc).""" + + cognos_is_hidden: Union[bool, None, UnsetType] = UNSET + """Whether the Cognos asset is hidden from the UI.""" + + cognos_is_disabled: Union[bool, None, UnsetType] = UNSET + """Whether the Cognos asset is disabled.""" + + cognos_default_screen_tip: Union[str, None, UnsetType] = UNSET + """Tooltip text present for the Cognos asset.""" + + +class CognosFileRelationshipAttributes(AssetRelationshipAttributes): + """CognosFile-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cognos_folder: Union[RelatedCognosFolder, None, UnsetType] = UNSET + """Folder containing the file.""" + + cognos_columns: Union[List[RelatedCognosColumn], None, UnsetType] = UNSET + """Columns contained in the file.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class CognosFileNested(AssetNested): + """CognosFile in nested API format for high-performance serialization.""" + + attributes: Union[CognosFileAttributes, UnsetType] = UNSET + relationship_attributes: Union[CognosFileRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + CognosFileRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + CognosFileRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_COGNOS_FILE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "cognos_folder", + "cognos_columns", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_cognos_file_attrs(attrs: CognosFileAttributes, obj: CognosFile) -> None: + """Populate CognosFile-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.cognos_id = obj.cognos_id + attrs.cognos_path = obj.cognos_path + attrs.cognos_parent_name = obj.cognos_parent_name + attrs.cognos_parent_qualified_name = obj.cognos_parent_qualified_name + attrs.cognos_version = obj.cognos_version + attrs.cognos_type = obj.cognos_type + attrs.cognos_is_hidden = obj.cognos_is_hidden + attrs.cognos_is_disabled = obj.cognos_is_disabled + attrs.cognos_default_screen_tip = obj.cognos_default_screen_tip + + +def _extract_cognos_file_attrs(attrs: CognosFileAttributes) -> dict: + """Extract all CognosFile attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["cognos_id"] = attrs.cognos_id + result["cognos_path"] = attrs.cognos_path + result["cognos_parent_name"] = attrs.cognos_parent_name + result["cognos_parent_qualified_name"] = attrs.cognos_parent_qualified_name + result["cognos_version"] = attrs.cognos_version + result["cognos_type"] = attrs.cognos_type + result["cognos_is_hidden"] = attrs.cognos_is_hidden + result["cognos_is_disabled"] = attrs.cognos_is_disabled + result["cognos_default_screen_tip"] = attrs.cognos_default_screen_tip + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _cognos_file_to_nested(cognos_file: CognosFile) -> CognosFileNested: + """Convert flat CognosFile to nested format.""" + attrs = CognosFileAttributes() + _populate_cognos_file_attrs(attrs, cognos_file) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + cognos_file, _COGNOS_FILE_REL_FIELDS, CognosFileRelationshipAttributes + ) + return CognosFileNested( + guid=cognos_file.guid, + type_name=cognos_file.type_name, + status=cognos_file.status, + version=cognos_file.version, + create_time=cognos_file.create_time, + update_time=cognos_file.update_time, + created_by=cognos_file.created_by, + updated_by=cognos_file.updated_by, + classifications=cognos_file.classifications, + classification_names=cognos_file.classification_names, + meanings=cognos_file.meanings, + labels=cognos_file.labels, + business_attributes=cognos_file.business_attributes, + custom_attributes=cognos_file.custom_attributes, + pending_tasks=cognos_file.pending_tasks, + proxy=cognos_file.proxy, + is_incomplete=cognos_file.is_incomplete, + provenance_type=cognos_file.provenance_type, + home_id=cognos_file.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _cognos_file_from_nested(nested: CognosFileNested) -> CognosFile: + """Convert nested format to flat CognosFile.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else CognosFileAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _COGNOS_FILE_REL_FIELDS, + CognosFileRelationshipAttributes, + ) + return CognosFile( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_cognos_file_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _cognos_file_to_nested_bytes(cognos_file: CognosFile, serde: Serde) -> bytes: + """Convert flat CognosFile to nested JSON bytes.""" + return serde.encode(_cognos_file_to_nested(cognos_file)) + + +def _cognos_file_from_nested_bytes(data: bytes, serde: Serde) -> CognosFile: + """Convert nested JSON bytes to flat CognosFile.""" + nested = serde.decode(data, CognosFileNested) + return _cognos_file_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + RelationField, +) + +CognosFile.COGNOS_ID = KeywordField("cognosId", "cognosId") +CognosFile.COGNOS_PATH = KeywordField("cognosPath", "cognosPath") +CognosFile.COGNOS_PARENT_NAME = KeywordTextField( + "cognosParentName", "cognosParentName", "cognosParentName.text" +) +CognosFile.COGNOS_PARENT_QUALIFIED_NAME = KeywordField( + "cognosParentQualifiedName", "cognosParentQualifiedName" +) +CognosFile.COGNOS_VERSION = KeywordField("cognosVersion", "cognosVersion") +CognosFile.COGNOS_TYPE = KeywordField("cognosType", "cognosType") +CognosFile.COGNOS_IS_HIDDEN = BooleanField("cognosIsHidden", "cognosIsHidden") +CognosFile.COGNOS_IS_DISABLED = BooleanField("cognosIsDisabled", "cognosIsDisabled") +CognosFile.COGNOS_DEFAULT_SCREEN_TIP = KeywordField( + "cognosDefaultScreenTip", "cognosDefaultScreenTip" +) +CognosFile.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +CognosFile.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +CognosFile.ANOMALO_CHECKS = RelationField("anomaloChecks") +CognosFile.APPLICATION = RelationField("application") +CognosFile.APPLICATION_FIELD = RelationField("applicationField") +CognosFile.COGNOS_FOLDER = RelationField("cognosFolder") +CognosFile.COGNOS_COLUMNS = RelationField("cognosColumns") +CognosFile.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +CognosFile.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +CognosFile.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +CognosFile.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +CognosFile.METRICS = RelationField("metrics") +CognosFile.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +CognosFile.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +CognosFile.MEANINGS = RelationField("meanings") +CognosFile.MC_MONITORS = RelationField("mcMonitors") +CognosFile.MC_INCIDENTS = RelationField("mcIncidents") +CognosFile.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +CognosFile.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +CognosFile.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +CognosFile.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +CognosFile.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +CognosFile.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +CognosFile.FILES = RelationField("files") +CognosFile.LINKS = RelationField("links") +CognosFile.README = RelationField("readme") +CognosFile.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +CognosFile.SODA_CHECKS = RelationField("sodaChecks") +CognosFile.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +CognosFile.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/cognos_folder.py b/pyatlan_v9/model/assets/cognos_folder.py new file mode 100644 index 000000000..848576e9b --- /dev/null +++ b/pyatlan_v9/model/assets/cognos_folder.py @@ -0,0 +1,755 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +CognosFolder asset model with flattened inheritance. + +This module provides: +- CognosFolder: Flat asset class (easy to use) +- CognosFolderAttributes: Nested attributes struct (extends AssetAttributes) +- CognosFolderNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .cognos_related import ( + RelatedCognosDashboard, + RelatedCognosDataset, + RelatedCognosExploration, + RelatedCognosFile, + RelatedCognosFolder, + RelatedCognosModule, + RelatedCognosPackage, + RelatedCognosReport, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class CognosFolder(Asset): + """ + Instance of a Cognos folder in Atlan. + """ + + COGNOS_SUB_FOLDER_COUNT: ClassVar[Any] = None + COGNOS_CHILD_OBJECTS_COUNT: ClassVar[Any] = None + COGNOS_ID: ClassVar[Any] = None + COGNOS_PATH: ClassVar[Any] = None + COGNOS_PARENT_NAME: ClassVar[Any] = None + COGNOS_PARENT_QUALIFIED_NAME: ClassVar[Any] = None + COGNOS_VERSION: ClassVar[Any] = None + COGNOS_TYPE: ClassVar[Any] = None + COGNOS_IS_HIDDEN: ClassVar[Any] = None + COGNOS_IS_DISABLED: ClassVar[Any] = None + COGNOS_DEFAULT_SCREEN_TIP: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + COGNOS_REPORTS: ClassVar[Any] = None + COGNOS_DASHBOARDS: ClassVar[Any] = None + COGNOS_EXPLORATIONS: ClassVar[Any] = None + COGNOS_FILES: ClassVar[Any] = None + COGNOS_MODULES: ClassVar[Any] = None + COGNOS_PACKAGES: ClassVar[Any] = None + COGNOS_DATASETS: ClassVar[Any] = None + COGNOS_SUB_FOLDERS: ClassVar[Any] = None + COGNOS_FOLDER: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "CognosFolder" + + cognos_sub_folder_count: Union[int, None, UnsetType] = UNSET + """Number of sub-folders in the folder.""" + + cognos_child_objects_count: Union[int, None, UnsetType] = UNSET + """Number of children in the folder (excluding subfolders).""" + + cognos_id: Union[str, None, UnsetType] = UNSET + """ID of the asset in Cognos.""" + + cognos_path: Union[str, None, UnsetType] = UNSET + """Path of the asset in Cognos (e.g. /content/folder[@name='Folder Name']).""" + + cognos_parent_name: Union[str, None, UnsetType] = UNSET + """Name of the parent of the asset in Cognos.""" + + cognos_parent_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the parent asset in Cognos.""" + + cognos_version: Union[str, None, UnsetType] = UNSET + """Version of the Cognos asset.""" + + cognos_type: Union[str, None, UnsetType] = UNSET + """Type of the Cognos asset (e.g. report, dashboard, package, etc).""" + + cognos_is_hidden: Union[bool, None, UnsetType] = UNSET + """Whether the Cognos asset is hidden from the UI.""" + + cognos_is_disabled: Union[bool, None, UnsetType] = UNSET + """Whether the Cognos asset is disabled.""" + + cognos_default_screen_tip: Union[str, None, UnsetType] = UNSET + """Tooltip text present for the Cognos asset.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cognos_reports: Union[List[RelatedCognosReport], None, UnsetType] = UNSET + """Reports contained in the folder.""" + + cognos_dashboards: Union[List[RelatedCognosDashboard], None, UnsetType] = UNSET + """Dashboards contained in the folder.""" + + cognos_explorations: Union[List[RelatedCognosExploration], None, UnsetType] = UNSET + """Explorations contained in the folder.""" + + cognos_files: Union[List[RelatedCognosFile], None, UnsetType] = UNSET + """Files contained in the folder.""" + + cognos_modules: Union[List[RelatedCognosModule], None, UnsetType] = UNSET + """Modules contained in the folder.""" + + cognos_packages: Union[List[RelatedCognosPackage], None, UnsetType] = UNSET + """Packages contained in the folder.""" + + cognos_datasets: Union[List[RelatedCognosDataset], None, UnsetType] = UNSET + """Datasets contained in the folder.""" + + cognos_sub_folders: Union[List[RelatedCognosFolder], None, UnsetType] = UNSET + """Subfolders contained in the folder.""" + + cognos_folder: Union[RelatedCognosFolder, None, UnsetType] = UNSET + """Parent folder containing this folder.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "CognosFolder" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _cognos_folder_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> CognosFolder: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + CognosFolder instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _cognos_folder_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class CognosFolderAttributes(AssetAttributes): + """CognosFolder-specific attributes for nested API format.""" + + cognos_sub_folder_count: Union[int, None, UnsetType] = UNSET + """Number of sub-folders in the folder.""" + + cognos_child_objects_count: Union[int, None, UnsetType] = UNSET + """Number of children in the folder (excluding subfolders).""" + + cognos_id: Union[str, None, UnsetType] = UNSET + """ID of the asset in Cognos.""" + + cognos_path: Union[str, None, UnsetType] = UNSET + """Path of the asset in Cognos (e.g. /content/folder[@name='Folder Name']).""" + + cognos_parent_name: Union[str, None, UnsetType] = UNSET + """Name of the parent of the asset in Cognos.""" + + cognos_parent_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the parent asset in Cognos.""" + + cognos_version: Union[str, None, UnsetType] = UNSET + """Version of the Cognos asset.""" + + cognos_type: Union[str, None, UnsetType] = UNSET + """Type of the Cognos asset (e.g. report, dashboard, package, etc).""" + + cognos_is_hidden: Union[bool, None, UnsetType] = UNSET + """Whether the Cognos asset is hidden from the UI.""" + + cognos_is_disabled: Union[bool, None, UnsetType] = UNSET + """Whether the Cognos asset is disabled.""" + + cognos_default_screen_tip: Union[str, None, UnsetType] = UNSET + """Tooltip text present for the Cognos asset.""" + + +class CognosFolderRelationshipAttributes(AssetRelationshipAttributes): + """CognosFolder-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cognos_reports: Union[List[RelatedCognosReport], None, UnsetType] = UNSET + """Reports contained in the folder.""" + + cognos_dashboards: Union[List[RelatedCognosDashboard], None, UnsetType] = UNSET + """Dashboards contained in the folder.""" + + cognos_explorations: Union[List[RelatedCognosExploration], None, UnsetType] = UNSET + """Explorations contained in the folder.""" + + cognos_files: Union[List[RelatedCognosFile], None, UnsetType] = UNSET + """Files contained in the folder.""" + + cognos_modules: Union[List[RelatedCognosModule], None, UnsetType] = UNSET + """Modules contained in the folder.""" + + cognos_packages: Union[List[RelatedCognosPackage], None, UnsetType] = UNSET + """Packages contained in the folder.""" + + cognos_datasets: Union[List[RelatedCognosDataset], None, UnsetType] = UNSET + """Datasets contained in the folder.""" + + cognos_sub_folders: Union[List[RelatedCognosFolder], None, UnsetType] = UNSET + """Subfolders contained in the folder.""" + + cognos_folder: Union[RelatedCognosFolder, None, UnsetType] = UNSET + """Parent folder containing this folder.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class CognosFolderNested(AssetNested): + """CognosFolder in nested API format for high-performance serialization.""" + + attributes: Union[CognosFolderAttributes, UnsetType] = UNSET + relationship_attributes: Union[CognosFolderRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + CognosFolderRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + CognosFolderRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_COGNOS_FOLDER_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "cognos_reports", + "cognos_dashboards", + "cognos_explorations", + "cognos_files", + "cognos_modules", + "cognos_packages", + "cognos_datasets", + "cognos_sub_folders", + "cognos_folder", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_cognos_folder_attrs( + attrs: CognosFolderAttributes, obj: CognosFolder +) -> None: + """Populate CognosFolder-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.cognos_sub_folder_count = obj.cognos_sub_folder_count + attrs.cognos_child_objects_count = obj.cognos_child_objects_count + attrs.cognos_id = obj.cognos_id + attrs.cognos_path = obj.cognos_path + attrs.cognos_parent_name = obj.cognos_parent_name + attrs.cognos_parent_qualified_name = obj.cognos_parent_qualified_name + attrs.cognos_version = obj.cognos_version + attrs.cognos_type = obj.cognos_type + attrs.cognos_is_hidden = obj.cognos_is_hidden + attrs.cognos_is_disabled = obj.cognos_is_disabled + attrs.cognos_default_screen_tip = obj.cognos_default_screen_tip + + +def _extract_cognos_folder_attrs(attrs: CognosFolderAttributes) -> dict: + """Extract all CognosFolder attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["cognos_sub_folder_count"] = attrs.cognos_sub_folder_count + result["cognos_child_objects_count"] = attrs.cognos_child_objects_count + result["cognos_id"] = attrs.cognos_id + result["cognos_path"] = attrs.cognos_path + result["cognos_parent_name"] = attrs.cognos_parent_name + result["cognos_parent_qualified_name"] = attrs.cognos_parent_qualified_name + result["cognos_version"] = attrs.cognos_version + result["cognos_type"] = attrs.cognos_type + result["cognos_is_hidden"] = attrs.cognos_is_hidden + result["cognos_is_disabled"] = attrs.cognos_is_disabled + result["cognos_default_screen_tip"] = attrs.cognos_default_screen_tip + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _cognos_folder_to_nested(cognos_folder: CognosFolder) -> CognosFolderNested: + """Convert flat CognosFolder to nested format.""" + attrs = CognosFolderAttributes() + _populate_cognos_folder_attrs(attrs, cognos_folder) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + cognos_folder, _COGNOS_FOLDER_REL_FIELDS, CognosFolderRelationshipAttributes + ) + return CognosFolderNested( + guid=cognos_folder.guid, + type_name=cognos_folder.type_name, + status=cognos_folder.status, + version=cognos_folder.version, + create_time=cognos_folder.create_time, + update_time=cognos_folder.update_time, + created_by=cognos_folder.created_by, + updated_by=cognos_folder.updated_by, + classifications=cognos_folder.classifications, + classification_names=cognos_folder.classification_names, + meanings=cognos_folder.meanings, + labels=cognos_folder.labels, + business_attributes=cognos_folder.business_attributes, + custom_attributes=cognos_folder.custom_attributes, + pending_tasks=cognos_folder.pending_tasks, + proxy=cognos_folder.proxy, + is_incomplete=cognos_folder.is_incomplete, + provenance_type=cognos_folder.provenance_type, + home_id=cognos_folder.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _cognos_folder_from_nested(nested: CognosFolderNested) -> CognosFolder: + """Convert nested format to flat CognosFolder.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else CognosFolderAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _COGNOS_FOLDER_REL_FIELDS, + CognosFolderRelationshipAttributes, + ) + return CognosFolder( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_cognos_folder_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _cognos_folder_to_nested_bytes(cognos_folder: CognosFolder, serde: Serde) -> bytes: + """Convert flat CognosFolder to nested JSON bytes.""" + return serde.encode(_cognos_folder_to_nested(cognos_folder)) + + +def _cognos_folder_from_nested_bytes(data: bytes, serde: Serde) -> CognosFolder: + """Convert nested JSON bytes to flat CognosFolder.""" + nested = serde.decode(data, CognosFolderNested) + return _cognos_folder_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +CognosFolder.COGNOS_SUB_FOLDER_COUNT = NumericField( + "cognosSubFolderCount", "cognosSubFolderCount" +) +CognosFolder.COGNOS_CHILD_OBJECTS_COUNT = NumericField( + "cognosChildObjectsCount", "cognosChildObjectsCount" +) +CognosFolder.COGNOS_ID = KeywordField("cognosId", "cognosId") +CognosFolder.COGNOS_PATH = KeywordField("cognosPath", "cognosPath") +CognosFolder.COGNOS_PARENT_NAME = KeywordTextField( + "cognosParentName", "cognosParentName", "cognosParentName.text" +) +CognosFolder.COGNOS_PARENT_QUALIFIED_NAME = KeywordField( + "cognosParentQualifiedName", "cognosParentQualifiedName" +) +CognosFolder.COGNOS_VERSION = KeywordField("cognosVersion", "cognosVersion") +CognosFolder.COGNOS_TYPE = KeywordField("cognosType", "cognosType") +CognosFolder.COGNOS_IS_HIDDEN = BooleanField("cognosIsHidden", "cognosIsHidden") +CognosFolder.COGNOS_IS_DISABLED = BooleanField("cognosIsDisabled", "cognosIsDisabled") +CognosFolder.COGNOS_DEFAULT_SCREEN_TIP = KeywordField( + "cognosDefaultScreenTip", "cognosDefaultScreenTip" +) +CognosFolder.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +CognosFolder.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +CognosFolder.ANOMALO_CHECKS = RelationField("anomaloChecks") +CognosFolder.APPLICATION = RelationField("application") +CognosFolder.APPLICATION_FIELD = RelationField("applicationField") +CognosFolder.COGNOS_REPORTS = RelationField("cognosReports") +CognosFolder.COGNOS_DASHBOARDS = RelationField("cognosDashboards") +CognosFolder.COGNOS_EXPLORATIONS = RelationField("cognosExplorations") +CognosFolder.COGNOS_FILES = RelationField("cognosFiles") +CognosFolder.COGNOS_MODULES = RelationField("cognosModules") +CognosFolder.COGNOS_PACKAGES = RelationField("cognosPackages") +CognosFolder.COGNOS_DATASETS = RelationField("cognosDatasets") +CognosFolder.COGNOS_SUB_FOLDERS = RelationField("cognosSubFolders") +CognosFolder.COGNOS_FOLDER = RelationField("cognosFolder") +CognosFolder.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +CognosFolder.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +CognosFolder.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +CognosFolder.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +CognosFolder.METRICS = RelationField("metrics") +CognosFolder.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +CognosFolder.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +CognosFolder.MEANINGS = RelationField("meanings") +CognosFolder.MC_MONITORS = RelationField("mcMonitors") +CognosFolder.MC_INCIDENTS = RelationField("mcIncidents") +CognosFolder.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +CognosFolder.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +CognosFolder.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +CognosFolder.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +CognosFolder.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +CognosFolder.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +CognosFolder.FILES = RelationField("files") +CognosFolder.LINKS = RelationField("links") +CognosFolder.README = RelationField("readme") +CognosFolder.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +CognosFolder.SODA_CHECKS = RelationField("sodaChecks") +CognosFolder.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +CognosFolder.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/cognos_module.py b/pyatlan_v9/model/assets/cognos_module.py new file mode 100644 index 000000000..82c430d30 --- /dev/null +++ b/pyatlan_v9/model/assets/cognos_module.py @@ -0,0 +1,660 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +CognosModule asset model with flattened inheritance. + +This module provides: +- CognosModule: Flat asset class (easy to use) +- CognosModuleAttributes: Nested attributes struct (extends AssetAttributes) +- CognosModuleNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .cognos_related import RelatedCognosColumn, RelatedCognosFolder + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class CognosModule(Asset): + """ + Instance of a Cognos module in Atlan. + """ + + COGNOS_ID: ClassVar[Any] = None + COGNOS_PATH: ClassVar[Any] = None + COGNOS_PARENT_NAME: ClassVar[Any] = None + COGNOS_PARENT_QUALIFIED_NAME: ClassVar[Any] = None + COGNOS_VERSION: ClassVar[Any] = None + COGNOS_TYPE: ClassVar[Any] = None + COGNOS_IS_HIDDEN: ClassVar[Any] = None + COGNOS_IS_DISABLED: ClassVar[Any] = None + COGNOS_DEFAULT_SCREEN_TIP: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + COGNOS_FOLDER: ClassVar[Any] = None + COGNOS_COLUMNS: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "CognosModule" + + cognos_id: Union[str, None, UnsetType] = UNSET + """ID of the asset in Cognos.""" + + cognos_path: Union[str, None, UnsetType] = UNSET + """Path of the asset in Cognos (e.g. /content/folder[@name='Folder Name']).""" + + cognos_parent_name: Union[str, None, UnsetType] = UNSET + """Name of the parent of the asset in Cognos.""" + + cognos_parent_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the parent asset in Cognos.""" + + cognos_version: Union[str, None, UnsetType] = UNSET + """Version of the Cognos asset.""" + + cognos_type: Union[str, None, UnsetType] = UNSET + """Type of the Cognos asset (e.g. report, dashboard, package, etc).""" + + cognos_is_hidden: Union[bool, None, UnsetType] = UNSET + """Whether the Cognos asset is hidden from the UI.""" + + cognos_is_disabled: Union[bool, None, UnsetType] = UNSET + """Whether the Cognos asset is disabled.""" + + cognos_default_screen_tip: Union[str, None, UnsetType] = UNSET + """Tooltip text present for the Cognos asset.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cognos_folder: Union[RelatedCognosFolder, None, UnsetType] = UNSET + """Folder containing the module.""" + + cognos_columns: Union[List[RelatedCognosColumn], None, UnsetType] = UNSET + """Columns contained in the module.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "CognosModule" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _cognos_module_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> CognosModule: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + CognosModule instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _cognos_module_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class CognosModuleAttributes(AssetAttributes): + """CognosModule-specific attributes for nested API format.""" + + cognos_id: Union[str, None, UnsetType] = UNSET + """ID of the asset in Cognos.""" + + cognos_path: Union[str, None, UnsetType] = UNSET + """Path of the asset in Cognos (e.g. /content/folder[@name='Folder Name']).""" + + cognos_parent_name: Union[str, None, UnsetType] = UNSET + """Name of the parent of the asset in Cognos.""" + + cognos_parent_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the parent asset in Cognos.""" + + cognos_version: Union[str, None, UnsetType] = UNSET + """Version of the Cognos asset.""" + + cognos_type: Union[str, None, UnsetType] = UNSET + """Type of the Cognos asset (e.g. report, dashboard, package, etc).""" + + cognos_is_hidden: Union[bool, None, UnsetType] = UNSET + """Whether the Cognos asset is hidden from the UI.""" + + cognos_is_disabled: Union[bool, None, UnsetType] = UNSET + """Whether the Cognos asset is disabled.""" + + cognos_default_screen_tip: Union[str, None, UnsetType] = UNSET + """Tooltip text present for the Cognos asset.""" + + +class CognosModuleRelationshipAttributes(AssetRelationshipAttributes): + """CognosModule-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cognos_folder: Union[RelatedCognosFolder, None, UnsetType] = UNSET + """Folder containing the module.""" + + cognos_columns: Union[List[RelatedCognosColumn], None, UnsetType] = UNSET + """Columns contained in the module.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class CognosModuleNested(AssetNested): + """CognosModule in nested API format for high-performance serialization.""" + + attributes: Union[CognosModuleAttributes, UnsetType] = UNSET + relationship_attributes: Union[CognosModuleRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + CognosModuleRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + CognosModuleRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_COGNOS_MODULE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "cognos_folder", + "cognos_columns", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_cognos_module_attrs( + attrs: CognosModuleAttributes, obj: CognosModule +) -> None: + """Populate CognosModule-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.cognos_id = obj.cognos_id + attrs.cognos_path = obj.cognos_path + attrs.cognos_parent_name = obj.cognos_parent_name + attrs.cognos_parent_qualified_name = obj.cognos_parent_qualified_name + attrs.cognos_version = obj.cognos_version + attrs.cognos_type = obj.cognos_type + attrs.cognos_is_hidden = obj.cognos_is_hidden + attrs.cognos_is_disabled = obj.cognos_is_disabled + attrs.cognos_default_screen_tip = obj.cognos_default_screen_tip + + +def _extract_cognos_module_attrs(attrs: CognosModuleAttributes) -> dict: + """Extract all CognosModule attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["cognos_id"] = attrs.cognos_id + result["cognos_path"] = attrs.cognos_path + result["cognos_parent_name"] = attrs.cognos_parent_name + result["cognos_parent_qualified_name"] = attrs.cognos_parent_qualified_name + result["cognos_version"] = attrs.cognos_version + result["cognos_type"] = attrs.cognos_type + result["cognos_is_hidden"] = attrs.cognos_is_hidden + result["cognos_is_disabled"] = attrs.cognos_is_disabled + result["cognos_default_screen_tip"] = attrs.cognos_default_screen_tip + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _cognos_module_to_nested(cognos_module: CognosModule) -> CognosModuleNested: + """Convert flat CognosModule to nested format.""" + attrs = CognosModuleAttributes() + _populate_cognos_module_attrs(attrs, cognos_module) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + cognos_module, _COGNOS_MODULE_REL_FIELDS, CognosModuleRelationshipAttributes + ) + return CognosModuleNested( + guid=cognos_module.guid, + type_name=cognos_module.type_name, + status=cognos_module.status, + version=cognos_module.version, + create_time=cognos_module.create_time, + update_time=cognos_module.update_time, + created_by=cognos_module.created_by, + updated_by=cognos_module.updated_by, + classifications=cognos_module.classifications, + classification_names=cognos_module.classification_names, + meanings=cognos_module.meanings, + labels=cognos_module.labels, + business_attributes=cognos_module.business_attributes, + custom_attributes=cognos_module.custom_attributes, + pending_tasks=cognos_module.pending_tasks, + proxy=cognos_module.proxy, + is_incomplete=cognos_module.is_incomplete, + provenance_type=cognos_module.provenance_type, + home_id=cognos_module.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _cognos_module_from_nested(nested: CognosModuleNested) -> CognosModule: + """Convert nested format to flat CognosModule.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else CognosModuleAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _COGNOS_MODULE_REL_FIELDS, + CognosModuleRelationshipAttributes, + ) + return CognosModule( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_cognos_module_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _cognos_module_to_nested_bytes(cognos_module: CognosModule, serde: Serde) -> bytes: + """Convert flat CognosModule to nested JSON bytes.""" + return serde.encode(_cognos_module_to_nested(cognos_module)) + + +def _cognos_module_from_nested_bytes(data: bytes, serde: Serde) -> CognosModule: + """Convert nested JSON bytes to flat CognosModule.""" + nested = serde.decode(data, CognosModuleNested) + return _cognos_module_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + RelationField, +) + +CognosModule.COGNOS_ID = KeywordField("cognosId", "cognosId") +CognosModule.COGNOS_PATH = KeywordField("cognosPath", "cognosPath") +CognosModule.COGNOS_PARENT_NAME = KeywordTextField( + "cognosParentName", "cognosParentName", "cognosParentName.text" +) +CognosModule.COGNOS_PARENT_QUALIFIED_NAME = KeywordField( + "cognosParentQualifiedName", "cognosParentQualifiedName" +) +CognosModule.COGNOS_VERSION = KeywordField("cognosVersion", "cognosVersion") +CognosModule.COGNOS_TYPE = KeywordField("cognosType", "cognosType") +CognosModule.COGNOS_IS_HIDDEN = BooleanField("cognosIsHidden", "cognosIsHidden") +CognosModule.COGNOS_IS_DISABLED = BooleanField("cognosIsDisabled", "cognosIsDisabled") +CognosModule.COGNOS_DEFAULT_SCREEN_TIP = KeywordField( + "cognosDefaultScreenTip", "cognosDefaultScreenTip" +) +CognosModule.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +CognosModule.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +CognosModule.ANOMALO_CHECKS = RelationField("anomaloChecks") +CognosModule.APPLICATION = RelationField("application") +CognosModule.APPLICATION_FIELD = RelationField("applicationField") +CognosModule.COGNOS_FOLDER = RelationField("cognosFolder") +CognosModule.COGNOS_COLUMNS = RelationField("cognosColumns") +CognosModule.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +CognosModule.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +CognosModule.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +CognosModule.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +CognosModule.METRICS = RelationField("metrics") +CognosModule.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +CognosModule.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +CognosModule.MEANINGS = RelationField("meanings") +CognosModule.MC_MONITORS = RelationField("mcMonitors") +CognosModule.MC_INCIDENTS = RelationField("mcIncidents") +CognosModule.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +CognosModule.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +CognosModule.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +CognosModule.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +CognosModule.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +CognosModule.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +CognosModule.FILES = RelationField("files") +CognosModule.LINKS = RelationField("links") +CognosModule.README = RelationField("readme") +CognosModule.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +CognosModule.SODA_CHECKS = RelationField("sodaChecks") +CognosModule.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +CognosModule.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/cognos_package.py b/pyatlan_v9/model/assets/cognos_package.py new file mode 100644 index 000000000..6491222fb --- /dev/null +++ b/pyatlan_v9/model/assets/cognos_package.py @@ -0,0 +1,662 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +CognosPackage asset model with flattened inheritance. + +This module provides: +- CognosPackage: Flat asset class (easy to use) +- CognosPackageAttributes: Nested attributes struct (extends AssetAttributes) +- CognosPackageNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .cognos_related import RelatedCognosColumn, RelatedCognosFolder + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class CognosPackage(Asset): + """ + Instance of a Cognos Package in Atlan. + """ + + COGNOS_ID: ClassVar[Any] = None + COGNOS_PATH: ClassVar[Any] = None + COGNOS_PARENT_NAME: ClassVar[Any] = None + COGNOS_PARENT_QUALIFIED_NAME: ClassVar[Any] = None + COGNOS_VERSION: ClassVar[Any] = None + COGNOS_TYPE: ClassVar[Any] = None + COGNOS_IS_HIDDEN: ClassVar[Any] = None + COGNOS_IS_DISABLED: ClassVar[Any] = None + COGNOS_DEFAULT_SCREEN_TIP: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + COGNOS_FOLDER: ClassVar[Any] = None + COGNOS_COLUMNS: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "CognosPackage" + + cognos_id: Union[str, None, UnsetType] = UNSET + """ID of the asset in Cognos.""" + + cognos_path: Union[str, None, UnsetType] = UNSET + """Path of the asset in Cognos (e.g. /content/folder[@name='Folder Name']).""" + + cognos_parent_name: Union[str, None, UnsetType] = UNSET + """Name of the parent of the asset in Cognos.""" + + cognos_parent_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the parent asset in Cognos.""" + + cognos_version: Union[str, None, UnsetType] = UNSET + """Version of the Cognos asset.""" + + cognos_type: Union[str, None, UnsetType] = UNSET + """Type of the Cognos asset (e.g. report, dashboard, package, etc).""" + + cognos_is_hidden: Union[bool, None, UnsetType] = UNSET + """Whether the Cognos asset is hidden from the UI.""" + + cognos_is_disabled: Union[bool, None, UnsetType] = UNSET + """Whether the Cognos asset is disabled.""" + + cognos_default_screen_tip: Union[str, None, UnsetType] = UNSET + """Tooltip text present for the Cognos asset.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cognos_folder: Union[RelatedCognosFolder, None, UnsetType] = UNSET + """Folder containing the package.""" + + cognos_columns: Union[List[RelatedCognosColumn], None, UnsetType] = UNSET + """Columns contained in the package.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "CognosPackage" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _cognos_package_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> CognosPackage: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + CognosPackage instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _cognos_package_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class CognosPackageAttributes(AssetAttributes): + """CognosPackage-specific attributes for nested API format.""" + + cognos_id: Union[str, None, UnsetType] = UNSET + """ID of the asset in Cognos.""" + + cognos_path: Union[str, None, UnsetType] = UNSET + """Path of the asset in Cognos (e.g. /content/folder[@name='Folder Name']).""" + + cognos_parent_name: Union[str, None, UnsetType] = UNSET + """Name of the parent of the asset in Cognos.""" + + cognos_parent_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the parent asset in Cognos.""" + + cognos_version: Union[str, None, UnsetType] = UNSET + """Version of the Cognos asset.""" + + cognos_type: Union[str, None, UnsetType] = UNSET + """Type of the Cognos asset (e.g. report, dashboard, package, etc).""" + + cognos_is_hidden: Union[bool, None, UnsetType] = UNSET + """Whether the Cognos asset is hidden from the UI.""" + + cognos_is_disabled: Union[bool, None, UnsetType] = UNSET + """Whether the Cognos asset is disabled.""" + + cognos_default_screen_tip: Union[str, None, UnsetType] = UNSET + """Tooltip text present for the Cognos asset.""" + + +class CognosPackageRelationshipAttributes(AssetRelationshipAttributes): + """CognosPackage-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cognos_folder: Union[RelatedCognosFolder, None, UnsetType] = UNSET + """Folder containing the package.""" + + cognos_columns: Union[List[RelatedCognosColumn], None, UnsetType] = UNSET + """Columns contained in the package.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class CognosPackageNested(AssetNested): + """CognosPackage in nested API format for high-performance serialization.""" + + attributes: Union[CognosPackageAttributes, UnsetType] = UNSET + relationship_attributes: Union[CognosPackageRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + CognosPackageRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + CognosPackageRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_COGNOS_PACKAGE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "cognos_folder", + "cognos_columns", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_cognos_package_attrs( + attrs: CognosPackageAttributes, obj: CognosPackage +) -> None: + """Populate CognosPackage-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.cognos_id = obj.cognos_id + attrs.cognos_path = obj.cognos_path + attrs.cognos_parent_name = obj.cognos_parent_name + attrs.cognos_parent_qualified_name = obj.cognos_parent_qualified_name + attrs.cognos_version = obj.cognos_version + attrs.cognos_type = obj.cognos_type + attrs.cognos_is_hidden = obj.cognos_is_hidden + attrs.cognos_is_disabled = obj.cognos_is_disabled + attrs.cognos_default_screen_tip = obj.cognos_default_screen_tip + + +def _extract_cognos_package_attrs(attrs: CognosPackageAttributes) -> dict: + """Extract all CognosPackage attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["cognos_id"] = attrs.cognos_id + result["cognos_path"] = attrs.cognos_path + result["cognos_parent_name"] = attrs.cognos_parent_name + result["cognos_parent_qualified_name"] = attrs.cognos_parent_qualified_name + result["cognos_version"] = attrs.cognos_version + result["cognos_type"] = attrs.cognos_type + result["cognos_is_hidden"] = attrs.cognos_is_hidden + result["cognos_is_disabled"] = attrs.cognos_is_disabled + result["cognos_default_screen_tip"] = attrs.cognos_default_screen_tip + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _cognos_package_to_nested(cognos_package: CognosPackage) -> CognosPackageNested: + """Convert flat CognosPackage to nested format.""" + attrs = CognosPackageAttributes() + _populate_cognos_package_attrs(attrs, cognos_package) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + cognos_package, _COGNOS_PACKAGE_REL_FIELDS, CognosPackageRelationshipAttributes + ) + return CognosPackageNested( + guid=cognos_package.guid, + type_name=cognos_package.type_name, + status=cognos_package.status, + version=cognos_package.version, + create_time=cognos_package.create_time, + update_time=cognos_package.update_time, + created_by=cognos_package.created_by, + updated_by=cognos_package.updated_by, + classifications=cognos_package.classifications, + classification_names=cognos_package.classification_names, + meanings=cognos_package.meanings, + labels=cognos_package.labels, + business_attributes=cognos_package.business_attributes, + custom_attributes=cognos_package.custom_attributes, + pending_tasks=cognos_package.pending_tasks, + proxy=cognos_package.proxy, + is_incomplete=cognos_package.is_incomplete, + provenance_type=cognos_package.provenance_type, + home_id=cognos_package.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _cognos_package_from_nested(nested: CognosPackageNested) -> CognosPackage: + """Convert nested format to flat CognosPackage.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else CognosPackageAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _COGNOS_PACKAGE_REL_FIELDS, + CognosPackageRelationshipAttributes, + ) + return CognosPackage( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_cognos_package_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _cognos_package_to_nested_bytes( + cognos_package: CognosPackage, serde: Serde +) -> bytes: + """Convert flat CognosPackage to nested JSON bytes.""" + return serde.encode(_cognos_package_to_nested(cognos_package)) + + +def _cognos_package_from_nested_bytes(data: bytes, serde: Serde) -> CognosPackage: + """Convert nested JSON bytes to flat CognosPackage.""" + nested = serde.decode(data, CognosPackageNested) + return _cognos_package_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + RelationField, +) + +CognosPackage.COGNOS_ID = KeywordField("cognosId", "cognosId") +CognosPackage.COGNOS_PATH = KeywordField("cognosPath", "cognosPath") +CognosPackage.COGNOS_PARENT_NAME = KeywordTextField( + "cognosParentName", "cognosParentName", "cognosParentName.text" +) +CognosPackage.COGNOS_PARENT_QUALIFIED_NAME = KeywordField( + "cognosParentQualifiedName", "cognosParentQualifiedName" +) +CognosPackage.COGNOS_VERSION = KeywordField("cognosVersion", "cognosVersion") +CognosPackage.COGNOS_TYPE = KeywordField("cognosType", "cognosType") +CognosPackage.COGNOS_IS_HIDDEN = BooleanField("cognosIsHidden", "cognosIsHidden") +CognosPackage.COGNOS_IS_DISABLED = BooleanField("cognosIsDisabled", "cognosIsDisabled") +CognosPackage.COGNOS_DEFAULT_SCREEN_TIP = KeywordField( + "cognosDefaultScreenTip", "cognosDefaultScreenTip" +) +CognosPackage.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +CognosPackage.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +CognosPackage.ANOMALO_CHECKS = RelationField("anomaloChecks") +CognosPackage.APPLICATION = RelationField("application") +CognosPackage.APPLICATION_FIELD = RelationField("applicationField") +CognosPackage.COGNOS_FOLDER = RelationField("cognosFolder") +CognosPackage.COGNOS_COLUMNS = RelationField("cognosColumns") +CognosPackage.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +CognosPackage.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +CognosPackage.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +CognosPackage.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +CognosPackage.METRICS = RelationField("metrics") +CognosPackage.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +CognosPackage.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +CognosPackage.MEANINGS = RelationField("meanings") +CognosPackage.MC_MONITORS = RelationField("mcMonitors") +CognosPackage.MC_INCIDENTS = RelationField("mcIncidents") +CognosPackage.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +CognosPackage.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +CognosPackage.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +CognosPackage.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +CognosPackage.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +CognosPackage.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +CognosPackage.FILES = RelationField("files") +CognosPackage.LINKS = RelationField("links") +CognosPackage.README = RelationField("readme") +CognosPackage.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +CognosPackage.SODA_CHECKS = RelationField("sodaChecks") +CognosPackage.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +CognosPackage.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/cognos_related.py b/pyatlan_v9/model/assets/cognos_related.py new file mode 100644 index 000000000..1f3936cea --- /dev/null +++ b/pyatlan_v9/model/assets/cognos_related.py @@ -0,0 +1,243 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Cognos module. + +This module contains all Related{Type} classes for the Cognos type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Union + +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedBI +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedCognos", + "RelatedCognosDashboard", + "RelatedCognosDatasource", + "RelatedCognosExploration", + "RelatedCognosFile", + "RelatedCognosFolder", + "RelatedCognosModule", + "RelatedCognosPackage", + "RelatedCognosReport", + "RelatedCognosColumn", + "RelatedCognosDataset", +] + + +class RelatedCognos(RelatedBI): + """ + Related entity reference for Cognos assets. + + Extends RelatedBI with Cognos-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Cognos" so it serializes correctly + + cognos_id: Union[str, None, UnsetType] = UNSET + """ID of the asset in Cognos.""" + + cognos_path: Union[str, None, UnsetType] = UNSET + """Path of the asset in Cognos (e.g. /content/folder[@name='Folder Name']).""" + + cognos_parent_name: Union[str, None, UnsetType] = UNSET + """Name of the parent of the asset in Cognos.""" + + cognos_parent_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the parent asset in Cognos.""" + + cognos_version: Union[str, None, UnsetType] = UNSET + """Version of the Cognos asset.""" + + cognos_type: Union[str, None, UnsetType] = UNSET + """Type of the Cognos asset (e.g. report, dashboard, package, etc).""" + + cognos_is_hidden: Union[bool, None, UnsetType] = UNSET + """Whether the Cognos asset is hidden from the UI.""" + + cognos_is_disabled: Union[bool, None, UnsetType] = UNSET + """Whether the Cognos asset is disabled.""" + + cognos_default_screen_tip: Union[str, None, UnsetType] = UNSET + """Tooltip text present for the Cognos asset.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Cognos" + + +class RelatedCognosDashboard(RelatedCognos): + """ + Related entity reference for CognosDashboard assets. + + Extends RelatedCognos with CognosDashboard-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "CognosDashboard" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "CognosDashboard" + + +class RelatedCognosDatasource(RelatedCognos): + """ + Related entity reference for CognosDatasource assets. + + Extends RelatedCognos with CognosDatasource-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "CognosDatasource" so it serializes correctly + + cognos_connection_string: Union[str, None, UnsetType] = UNSET + """Connection string of a Cognos datasource.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "CognosDatasource" + + +class RelatedCognosExploration(RelatedCognos): + """ + Related entity reference for CognosExploration assets. + + Extends RelatedCognos with CognosExploration-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "CognosExploration" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "CognosExploration" + + +class RelatedCognosFile(RelatedCognos): + """ + Related entity reference for CognosFile assets. + + Extends RelatedCognos with CognosFile-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "CognosFile" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "CognosFile" + + +class RelatedCognosFolder(RelatedCognos): + """ + Related entity reference for CognosFolder assets. + + Extends RelatedCognos with CognosFolder-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "CognosFolder" so it serializes correctly + + cognos_sub_folder_count: Union[int, None, UnsetType] = UNSET + """Number of sub-folders in the folder.""" + + cognos_child_objects_count: Union[int, None, UnsetType] = UNSET + """Number of children in the folder (excluding subfolders).""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "CognosFolder" + + +class RelatedCognosModule(RelatedCognos): + """ + Related entity reference for CognosModule assets. + + Extends RelatedCognos with CognosModule-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "CognosModule" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "CognosModule" + + +class RelatedCognosPackage(RelatedCognos): + """ + Related entity reference for CognosPackage assets. + + Extends RelatedCognos with CognosPackage-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "CognosPackage" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "CognosPackage" + + +class RelatedCognosReport(RelatedCognos): + """ + Related entity reference for CognosReport assets. + + Extends RelatedCognos with CognosReport-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "CognosReport" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "CognosReport" + + +class RelatedCognosColumn(RelatedCognos): + """ + Related entity reference for CognosColumn assets. + + Extends RelatedCognos with CognosColumn-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "CognosColumn" so it serializes correctly + + cognos_datatype: Union[str, None, UnsetType] = UNSET + """Data type of the CognosColumn.""" + + cognos_nullable: Union[str, None, UnsetType] = UNSET + """Whether the CognosColumn is nullable.""" + + cognos_regular_aggregate: Union[str, None, UnsetType] = UNSET + """How data should be summarized when aggregated across different dimensions or groupings.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "CognosColumn" + + +class RelatedCognosDataset(RelatedCognos): + """ + Related entity reference for CognosDataset assets. + + Extends RelatedCognos with CognosDataset-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "CognosDataset" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "CognosDataset" diff --git a/pyatlan_v9/model/assets/cognos_report.py b/pyatlan_v9/model/assets/cognos_report.py new file mode 100644 index 000000000..d0280185c --- /dev/null +++ b/pyatlan_v9/model/assets/cognos_report.py @@ -0,0 +1,651 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +CognosReport asset model with flattened inheritance. + +This module provides: +- CognosReport: Flat asset class (easy to use) +- CognosReportAttributes: Nested attributes struct (extends AssetAttributes) +- CognosReportNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .cognos_related import RelatedCognosFolder + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class CognosReport(Asset): + """ + Instance of a Cognos report in Atlan. + """ + + COGNOS_ID: ClassVar[Any] = None + COGNOS_PATH: ClassVar[Any] = None + COGNOS_PARENT_NAME: ClassVar[Any] = None + COGNOS_PARENT_QUALIFIED_NAME: ClassVar[Any] = None + COGNOS_VERSION: ClassVar[Any] = None + COGNOS_TYPE: ClassVar[Any] = None + COGNOS_IS_HIDDEN: ClassVar[Any] = None + COGNOS_IS_DISABLED: ClassVar[Any] = None + COGNOS_DEFAULT_SCREEN_TIP: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + COGNOS_FOLDER: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "CognosReport" + + cognos_id: Union[str, None, UnsetType] = UNSET + """ID of the asset in Cognos.""" + + cognos_path: Union[str, None, UnsetType] = UNSET + """Path of the asset in Cognos (e.g. /content/folder[@name='Folder Name']).""" + + cognos_parent_name: Union[str, None, UnsetType] = UNSET + """Name of the parent of the asset in Cognos.""" + + cognos_parent_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the parent asset in Cognos.""" + + cognos_version: Union[str, None, UnsetType] = UNSET + """Version of the Cognos asset.""" + + cognos_type: Union[str, None, UnsetType] = UNSET + """Type of the Cognos asset (e.g. report, dashboard, package, etc).""" + + cognos_is_hidden: Union[bool, None, UnsetType] = UNSET + """Whether the Cognos asset is hidden from the UI.""" + + cognos_is_disabled: Union[bool, None, UnsetType] = UNSET + """Whether the Cognos asset is disabled.""" + + cognos_default_screen_tip: Union[str, None, UnsetType] = UNSET + """Tooltip text present for the Cognos asset.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cognos_folder: Union[RelatedCognosFolder, None, UnsetType] = UNSET + """Folder containing the report.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "CognosReport" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _cognos_report_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> CognosReport: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + CognosReport instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _cognos_report_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class CognosReportAttributes(AssetAttributes): + """CognosReport-specific attributes for nested API format.""" + + cognos_id: Union[str, None, UnsetType] = UNSET + """ID of the asset in Cognos.""" + + cognos_path: Union[str, None, UnsetType] = UNSET + """Path of the asset in Cognos (e.g. /content/folder[@name='Folder Name']).""" + + cognos_parent_name: Union[str, None, UnsetType] = UNSET + """Name of the parent of the asset in Cognos.""" + + cognos_parent_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the parent asset in Cognos.""" + + cognos_version: Union[str, None, UnsetType] = UNSET + """Version of the Cognos asset.""" + + cognos_type: Union[str, None, UnsetType] = UNSET + """Type of the Cognos asset (e.g. report, dashboard, package, etc).""" + + cognos_is_hidden: Union[bool, None, UnsetType] = UNSET + """Whether the Cognos asset is hidden from the UI.""" + + cognos_is_disabled: Union[bool, None, UnsetType] = UNSET + """Whether the Cognos asset is disabled.""" + + cognos_default_screen_tip: Union[str, None, UnsetType] = UNSET + """Tooltip text present for the Cognos asset.""" + + +class CognosReportRelationshipAttributes(AssetRelationshipAttributes): + """CognosReport-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cognos_folder: Union[RelatedCognosFolder, None, UnsetType] = UNSET + """Folder containing the report.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class CognosReportNested(AssetNested): + """CognosReport in nested API format for high-performance serialization.""" + + attributes: Union[CognosReportAttributes, UnsetType] = UNSET + relationship_attributes: Union[CognosReportRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + CognosReportRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + CognosReportRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_COGNOS_REPORT_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "cognos_folder", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_cognos_report_attrs( + attrs: CognosReportAttributes, obj: CognosReport +) -> None: + """Populate CognosReport-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.cognos_id = obj.cognos_id + attrs.cognos_path = obj.cognos_path + attrs.cognos_parent_name = obj.cognos_parent_name + attrs.cognos_parent_qualified_name = obj.cognos_parent_qualified_name + attrs.cognos_version = obj.cognos_version + attrs.cognos_type = obj.cognos_type + attrs.cognos_is_hidden = obj.cognos_is_hidden + attrs.cognos_is_disabled = obj.cognos_is_disabled + attrs.cognos_default_screen_tip = obj.cognos_default_screen_tip + + +def _extract_cognos_report_attrs(attrs: CognosReportAttributes) -> dict: + """Extract all CognosReport attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["cognos_id"] = attrs.cognos_id + result["cognos_path"] = attrs.cognos_path + result["cognos_parent_name"] = attrs.cognos_parent_name + result["cognos_parent_qualified_name"] = attrs.cognos_parent_qualified_name + result["cognos_version"] = attrs.cognos_version + result["cognos_type"] = attrs.cognos_type + result["cognos_is_hidden"] = attrs.cognos_is_hidden + result["cognos_is_disabled"] = attrs.cognos_is_disabled + result["cognos_default_screen_tip"] = attrs.cognos_default_screen_tip + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _cognos_report_to_nested(cognos_report: CognosReport) -> CognosReportNested: + """Convert flat CognosReport to nested format.""" + attrs = CognosReportAttributes() + _populate_cognos_report_attrs(attrs, cognos_report) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + cognos_report, _COGNOS_REPORT_REL_FIELDS, CognosReportRelationshipAttributes + ) + return CognosReportNested( + guid=cognos_report.guid, + type_name=cognos_report.type_name, + status=cognos_report.status, + version=cognos_report.version, + create_time=cognos_report.create_time, + update_time=cognos_report.update_time, + created_by=cognos_report.created_by, + updated_by=cognos_report.updated_by, + classifications=cognos_report.classifications, + classification_names=cognos_report.classification_names, + meanings=cognos_report.meanings, + labels=cognos_report.labels, + business_attributes=cognos_report.business_attributes, + custom_attributes=cognos_report.custom_attributes, + pending_tasks=cognos_report.pending_tasks, + proxy=cognos_report.proxy, + is_incomplete=cognos_report.is_incomplete, + provenance_type=cognos_report.provenance_type, + home_id=cognos_report.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _cognos_report_from_nested(nested: CognosReportNested) -> CognosReport: + """Convert nested format to flat CognosReport.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else CognosReportAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _COGNOS_REPORT_REL_FIELDS, + CognosReportRelationshipAttributes, + ) + return CognosReport( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_cognos_report_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _cognos_report_to_nested_bytes(cognos_report: CognosReport, serde: Serde) -> bytes: + """Convert flat CognosReport to nested JSON bytes.""" + return serde.encode(_cognos_report_to_nested(cognos_report)) + + +def _cognos_report_from_nested_bytes(data: bytes, serde: Serde) -> CognosReport: + """Convert nested JSON bytes to flat CognosReport.""" + nested = serde.decode(data, CognosReportNested) + return _cognos_report_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + RelationField, +) + +CognosReport.COGNOS_ID = KeywordField("cognosId", "cognosId") +CognosReport.COGNOS_PATH = KeywordField("cognosPath", "cognosPath") +CognosReport.COGNOS_PARENT_NAME = KeywordTextField( + "cognosParentName", "cognosParentName", "cognosParentName.text" +) +CognosReport.COGNOS_PARENT_QUALIFIED_NAME = KeywordField( + "cognosParentQualifiedName", "cognosParentQualifiedName" +) +CognosReport.COGNOS_VERSION = KeywordField("cognosVersion", "cognosVersion") +CognosReport.COGNOS_TYPE = KeywordField("cognosType", "cognosType") +CognosReport.COGNOS_IS_HIDDEN = BooleanField("cognosIsHidden", "cognosIsHidden") +CognosReport.COGNOS_IS_DISABLED = BooleanField("cognosIsDisabled", "cognosIsDisabled") +CognosReport.COGNOS_DEFAULT_SCREEN_TIP = KeywordField( + "cognosDefaultScreenTip", "cognosDefaultScreenTip" +) +CognosReport.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +CognosReport.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +CognosReport.ANOMALO_CHECKS = RelationField("anomaloChecks") +CognosReport.APPLICATION = RelationField("application") +CognosReport.APPLICATION_FIELD = RelationField("applicationField") +CognosReport.COGNOS_FOLDER = RelationField("cognosFolder") +CognosReport.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +CognosReport.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +CognosReport.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +CognosReport.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +CognosReport.METRICS = RelationField("metrics") +CognosReport.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +CognosReport.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +CognosReport.MEANINGS = RelationField("meanings") +CognosReport.MC_MONITORS = RelationField("mcMonitors") +CognosReport.MC_INCIDENTS = RelationField("mcIncidents") +CognosReport.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +CognosReport.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +CognosReport.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +CognosReport.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +CognosReport.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +CognosReport.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +CognosReport.FILES = RelationField("files") +CognosReport.LINKS = RelationField("links") +CognosReport.README = RelationField("readme") +CognosReport.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +CognosReport.SODA_CHECKS = RelationField("sodaChecks") +CognosReport.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +CognosReport.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/collection.py b/pyatlan_v9/model/assets/collection.py new file mode 100644 index 000000000..897c322c9 --- /dev/null +++ b/pyatlan_v9/model/assets/collection.py @@ -0,0 +1,492 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Collection asset model with flattened inheritance. + +This module provides: +- Collection: Flat asset class (easy to use) +- CollectionAttributes: Nested attributes struct (extends AssetAttributes) +- CollectionNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union +from uuid import uuid4 + +from msgspec import UNSET, UnsetType + +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .sql_related import RelatedQuery +from pyatlan.errors import AtlanError +from pyatlan.errors import ErrorCode +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .namespace_related import RelatedFolder + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Collection(Asset): + """ + Instance of a query collection in Atlan. + """ + + ICON: ClassVar[Any] = None + ICON_TYPE: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + CHILDREN_FOLDERS: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + CHILDREN_QUERIES: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Collection" + + icon: Union[str, None, UnsetType] = UNSET + """Image used to represent this collection.""" + + icon_type: Union[str, None, UnsetType] = UNSET + """Type of image used to represent the collection (for example, an emoji).""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + children_folders: Union[List[RelatedFolder], None, UnsetType] = UNSET + """Folders that exist within this namespace.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + children_queries: Union[List[RelatedQuery], None, UnsetType] = UNSET + """Queries that exist within this namespace.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Collection" + + @classmethod + @init_guid + def creator(cls, *, client: "AtlanClient", name: str) -> "Collection": + validate_required_fields(["client", "name"], [client, name]) + return cls( + name=name, + qualified_name=cls._generate_qualified_name(client), + ) + + @classmethod + def _generate_qualified_name(cls, client: "AtlanClient") -> str: + try: + username = client.user.get_current().username + return f"default/collection/{username}/{uuid4()}" + except AtlanError as e: + raise ErrorCode.UNABLE_TO_GENERATE_QN.exception_with_parameters( + cls.__name__, e + ) from e + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _collection_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Collection: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Collection instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _collection_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class CollectionAttributes(AssetAttributes): + """Collection-specific attributes for nested API format.""" + + icon: Union[str, None, UnsetType] = UNSET + """Image used to represent this collection.""" + + icon_type: Union[str, None, UnsetType] = UNSET + """Type of image used to represent the collection (for example, an emoji).""" + + +class CollectionRelationshipAttributes(AssetRelationshipAttributes): + """Collection-specific relationship attributes for nested API format.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + children_folders: Union[List[RelatedFolder], None, UnsetType] = UNSET + """Folders that exist within this namespace.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + children_queries: Union[List[RelatedQuery], None, UnsetType] = UNSET + """Queries that exist within this namespace.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + +class CollectionNested(AssetNested): + """Collection in nested API format for high-performance serialization.""" + + attributes: Union[CollectionAttributes, UnsetType] = UNSET + relationship_attributes: Union[CollectionRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + CollectionRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + CollectionRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_COLLECTION_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "children_folders", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "children_queries", + "schema_registry_subjects", + "soda_checks", +] + + +def _populate_collection_attrs(attrs: CollectionAttributes, obj: Collection) -> None: + """Populate Collection-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.icon = obj.icon + attrs.icon_type = obj.icon_type + + +def _extract_collection_attrs(attrs: CollectionAttributes) -> dict: + """Extract all Collection attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["icon"] = attrs.icon + result["icon_type"] = attrs.icon_type + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _collection_to_nested(collection: Collection) -> CollectionNested: + """Convert flat Collection to nested format.""" + attrs = CollectionAttributes() + _populate_collection_attrs(attrs, collection) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + collection, _COLLECTION_REL_FIELDS, CollectionRelationshipAttributes + ) + return CollectionNested( + guid=collection.guid, + type_name=collection.type_name, + status=collection.status, + version=collection.version, + create_time=collection.create_time, + update_time=collection.update_time, + created_by=collection.created_by, + updated_by=collection.updated_by, + classifications=collection.classifications, + classification_names=collection.classification_names, + meanings=collection.meanings, + labels=collection.labels, + business_attributes=collection.business_attributes, + custom_attributes=collection.custom_attributes, + pending_tasks=collection.pending_tasks, + proxy=collection.proxy, + is_incomplete=collection.is_incomplete, + provenance_type=collection.provenance_type, + home_id=collection.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _collection_from_nested(nested: CollectionNested) -> Collection: + """Convert nested format to flat Collection.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else CollectionAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _COLLECTION_REL_FIELDS, + CollectionRelationshipAttributes, + ) + return Collection( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_collection_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _collection_to_nested_bytes(collection: Collection, serde: Serde) -> bytes: + """Convert flat Collection to nested JSON bytes.""" + return serde.encode(_collection_to_nested(collection)) + + +def _collection_from_nested_bytes(data: bytes, serde: Serde) -> Collection: + """Convert nested JSON bytes to flat Collection.""" + nested = serde.decode(data, CollectionNested) + return _collection_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +Collection.ICON = KeywordField("icon", "icon") +Collection.ICON_TYPE = KeywordField("iconType", "iconType") +Collection.ANOMALO_CHECKS = RelationField("anomaloChecks") +Collection.APPLICATION = RelationField("application") +Collection.APPLICATION_FIELD = RelationField("applicationField") +Collection.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Collection.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Collection.METRICS = RelationField("metrics") +Collection.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Collection.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Collection.MEANINGS = RelationField("meanings") +Collection.MC_MONITORS = RelationField("mcMonitors") +Collection.MC_INCIDENTS = RelationField("mcIncidents") +Collection.CHILDREN_FOLDERS = RelationField("childrenFolders") +Collection.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Collection.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Collection.FILES = RelationField("files") +Collection.LINKS = RelationField("links") +Collection.README = RelationField("readme") +Collection.CHILDREN_QUERIES = RelationField("childrenQueries") +Collection.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Collection.SODA_CHECKS = RelationField("sodaChecks") diff --git a/pyatlan_v9/model/assets/column.py b/pyatlan_v9/model/assets/column.py new file mode 100644 index 000000000..5414a0888 --- /dev/null +++ b/pyatlan_v9/model/assets/column.py @@ -0,0 +1,1957 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Column asset model with flattened inheritance. + +This module provides: +- Column: Flat asset class (easy to use) +- ColumnAttributes: Nested attributes struct (extends AssetAttributes) +- ColumnNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union +from warnings import warn + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .cosmos_mongo_db_related import RelatedCosmosMongoDBCollection +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtMetric, + RelatedDbtModel, + RelatedDbtModelColumn, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .mongo_db_related import RelatedMongoDBCollection +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .snowflake_related import ( + RelatedSnowflakeDynamicTable, + RelatedSnowflakeSemanticLogicalTable, +) +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan.model.enums import AtlanConnectorType +from pyatlan.utils import validate_required_fields +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid + +from .sql_related import ( + RelatedCalculationView, + RelatedColumn, + RelatedMaterialisedView, + RelatedQuery, + RelatedTable, + RelatedTablePartition, + RelatedView, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Column(Asset): + """ + Instance of a column in Atlan. + """ + + DATA_TYPE: ClassVar[Any] = None + SUB_DATA_TYPE: ClassVar[Any] = None + SQL_COMPRESSION: ClassVar[Any] = None + SQL_ENCODING: ClassVar[Any] = None + RAW_DATA_TYPE_DEFINITION: ClassVar[Any] = None + ORDER: ClassVar[Any] = None + NESTED_COLUMN_ORDER: ClassVar[Any] = None + NESTED_COLUMN_COUNT: ClassVar[Any] = None + COLUMN_HIERARCHY: ClassVar[Any] = None + IS_PARTITION: ClassVar[Any] = None + PARTITION_ORDER: ClassVar[Any] = None + IS_CLUSTERED: ClassVar[Any] = None + IS_PRIMARY: ClassVar[Any] = None + IS_FOREIGN: ClassVar[Any] = None + IS_INDEXED: ClassVar[Any] = None + IS_SORT: ClassVar[Any] = None + IS_DIST: ClassVar[Any] = None + IS_PINNED: ClassVar[Any] = None + PINNED_BY: ClassVar[Any] = None + PINNED_AT: ClassVar[Any] = None + PRECISION: ClassVar[Any] = None + DEFAULT_VALUE: ClassVar[Any] = None + IS_NULLABLE: ClassVar[Any] = None + NUMERIC_SCALE: ClassVar[Any] = None + MAX_LENGTH: ClassVar[Any] = None + VALIDATIONS: ClassVar[Any] = None + PARENT_COLUMN_QUALIFIED_NAME: ClassVar[Any] = None + PARENT_COLUMN_NAME: ClassVar[Any] = None + SQL_DISTINCT_VALUES_COUNT: ClassVar[Any] = None + SQL_DISTINCT_VALUES_COUNT_LONG: ClassVar[Any] = None + SQL_HISTOGRAM: ClassVar[Any] = None + SQL_MAX: ClassVar[Any] = None + SQL_MIN: ClassVar[Any] = None + SQL_MEAN: ClassVar[Any] = None + SQL_SUM: ClassVar[Any] = None + SQL_MEDIAN: ClassVar[Any] = None + SQL_STANDARD_DEVIATION: ClassVar[Any] = None + SQL_UNIQUE_VALUES_COUNT: ClassVar[Any] = None + SQL_UNIQUE_VALUES_COUNT_LONG: ClassVar[Any] = None + SQL_AVERAGE: ClassVar[Any] = None + SQL_AVERAGE_LENGTH: ClassVar[Any] = None + SQL_DUPLICATE_VALUES_COUNT: ClassVar[Any] = None + SQL_DUPLICATE_VALUES_COUNT_LONG: ClassVar[Any] = None + SQL_MAXIMUM_STRING_LENGTH: ClassVar[Any] = None + COLUMN_MAXS: ClassVar[Any] = None + SQL_MINIMUM_STRING_LENGTH: ClassVar[Any] = None + COLUMN_MINS: ClassVar[Any] = None + SQL_MISSING_VALUES_COUNT: ClassVar[Any] = None + SQL_MISSING_VALUES_COUNT_LONG: ClassVar[Any] = None + SQL_MISSING_VALUES_PERCENTAGE: ClassVar[Any] = None + SQL_UNIQUENESS_PERCENTAGE: ClassVar[Any] = None + SQL_VARIANCE: ClassVar[Any] = None + COLUMN_TOP_VALUES: ClassVar[Any] = None + SQL_MAX_VALUE: ClassVar[Any] = None + SQL_MIN_VALUE: ClassVar[Any] = None + SQL_MEAN_VALUE: ClassVar[Any] = None + SQL_SUM_VALUE: ClassVar[Any] = None + SQL_MEDIAN_VALUE: ClassVar[Any] = None + SQL_STANDARD_DEVIATION_VALUE: ClassVar[Any] = None + SQL_AVERAGE_VALUE: ClassVar[Any] = None + SQL_VARIANCE_VALUE: ClassVar[Any] = None + SQL_AVERAGE_LENGTH_VALUE: ClassVar[Any] = None + SQL_DISTRIBUTION_HISTOGRAM: ClassVar[Any] = None + SQL_DEPTH_LEVEL: ClassVar[Any] = None + NOSQL_COLLECTION_NAME: ClassVar[Any] = None + NOSQL_COLLECTION_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_MEASURE: ClassVar[Any] = None + SQL_MEASURE_TYPE: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + COSMOS_MONGO_DB_COLLECTION: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + METRIC_TIMESTAMPS: ClassVar[Any] = None + DATA_QUALITY_METRIC_DIMENSIONS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_BASE_COLUMN_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_COLUMN_RULES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_METRICS: ClassVar[Any] = None + DBT_MODEL_COLUMNS: ClassVar[Any] = None + COLUMN_DBT_MODEL_COLUMNS: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MONGO_DB_COLLECTION: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + TABLE: ClassVar[Any] = None + NESTED_COLUMNS: ClassVar[Any] = None + PARENT_COLUMN: ClassVar[Any] = None + TABLE_PARTITION: ClassVar[Any] = None + VIEW: ClassVar[Any] = None + CALCULATION_VIEW: ClassVar[Any] = None + MATERIALISED_VIEW: ClassVar[Any] = None + FOREIGN_KEY_TO: ClassVar[Any] = None + FOREIGN_KEY_FROM: ClassVar[Any] = None + QUERIES: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_DYNAMIC_TABLE: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Column" + + data_type: Union[str, None, UnsetType] = UNSET + """Data type of values in this column.""" + + sub_data_type: Union[str, None, UnsetType] = UNSET + """Sub-data type of this column.""" + + sql_compression: Union[str, None, UnsetType] = UNSET + """Compression type of this column.""" + + sql_encoding: Union[str, None, UnsetType] = UNSET + """Encoding type of this column.""" + + raw_data_type_definition: Union[str, None, UnsetType] = UNSET + """Raw data type definition of this column.""" + + order: Union[int, None, UnsetType] = UNSET + """Order (position) in which this column appears in the table (starting at 1).""" + + nested_column_order: Union[str, None, UnsetType] = UNSET + """Order (position) in which this column appears in the nested Column (nest level starts at 1).""" + + nested_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns nested within this (STRUCT or NESTED) column.""" + + column_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of top-level upstream nested columns.""" + + is_partition: Union[bool, None, UnsetType] = UNSET + """Whether this column is a partition column (true) or not (false).""" + + partition_order: Union[int, None, UnsetType] = UNSET + """Order (position) of this partition column in the table.""" + + is_clustered: Union[bool, None, UnsetType] = UNSET + """Whether this column is a clustered column (true) or not (false).""" + + is_primary: Union[bool, None, UnsetType] = UNSET + """When true, this column is the primary key for the table.""" + + is_foreign: Union[bool, None, UnsetType] = UNSET + """When true, this column is a foreign key to another table. NOTE: this must be true when using the foreignKeyTo relationship to specify columns that refer to this column as a foreign key.""" + + is_indexed: Union[bool, None, UnsetType] = UNSET + """When true, this column is indexed in the database.""" + + is_sort: Union[bool, None, UnsetType] = UNSET + """Whether this column is a sort column (true) or not (false).""" + + is_dist: Union[bool, None, UnsetType] = UNSET + """Whether this column is a distribution column (true) or not (false).""" + + is_pinned: Union[bool, None, UnsetType] = UNSET + """Whether this column is pinned (true) or not (false).""" + + pinned_by: Union[str, None, UnsetType] = UNSET + """User who pinned this column.""" + + pinned_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this column was pinned, in milliseconds.""" + + precision: Union[int, None, UnsetType] = UNSET + """Total number of digits allowed, when the dataType is numeric.""" + + default_value: Union[str, None, UnsetType] = UNSET + """Default value for this column.""" + + is_nullable: Union[bool, None, UnsetType] = UNSET + """When true, the values in this column can be null.""" + + numeric_scale: Union[float, None, UnsetType] = UNSET + """Number of digits allowed to the right of the decimal point.""" + + max_length: Union[int, None, UnsetType] = UNSET + """Maximum length of a value in this column.""" + + validations: Union[Dict[str, str], None, UnsetType] = UNSET + """Validations for this column.""" + + parent_column_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the column this column is nested within, for STRUCT and NESTED columns.""" + + parent_column_name: Union[str, None, UnsetType] = UNSET + """Simple name of the column this column is nested within, for STRUCT and NESTED columns.""" + + sql_distinct_values_count: Union[int, None, UnsetType] = UNSET + """Number of rows that contain distinct values.""" + + sql_distinct_values_count_long: Union[int, None, UnsetType] = UNSET + """Number of rows that contain distinct values.""" + + sql_histogram: Union[Dict[str, Any], None, UnsetType] = UNSET + """List of values in a histogram that represents the contents of this column.""" + + sql_max: Union[float, None, UnsetType] = UNSET + """Greatest value in a numeric column.""" + + sql_min: Union[float, None, UnsetType] = UNSET + """Least value in a numeric column.""" + + sql_mean: Union[float, None, UnsetType] = UNSET + """Arithmetic mean of the values in a numeric column.""" + + sql_sum: Union[float, None, UnsetType] = UNSET + """Calculated sum of the values in a numeric column.""" + + sql_median: Union[float, None, UnsetType] = UNSET + """Calculated median of the values in a numeric column.""" + + sql_standard_deviation: Union[float, None, UnsetType] = UNSET + """Calculated standard deviation of the values in a numeric column.""" + + sql_unique_values_count: Union[int, None, UnsetType] = UNSET + """Number of rows in which a value in this column appears only once.""" + + sql_unique_values_count_long: Union[int, None, UnsetType] = UNSET + """Number of rows in which a value in this column appears only once.""" + + sql_average: Union[float, None, UnsetType] = UNSET + """Average value in this column.""" + + sql_average_length: Union[float, None, UnsetType] = UNSET + """Average length of values in a string column.""" + + sql_duplicate_values_count: Union[int, None, UnsetType] = UNSET + """Number of rows that contain duplicate values.""" + + sql_duplicate_values_count_long: Union[int, None, UnsetType] = UNSET + """Number of rows that contain duplicate values.""" + + sql_maximum_string_length: Union[int, None, UnsetType] = UNSET + """Length of the longest value in a string column.""" + + column_maxs: Union[List[str], None, UnsetType] = UNSET + """List of the greatest values in a column.""" + + sql_minimum_string_length: Union[int, None, UnsetType] = UNSET + """Length of the shortest value in a string column.""" + + column_mins: Union[List[str], None, UnsetType] = UNSET + """List of the least values in a column.""" + + sql_missing_values_count: Union[int, None, UnsetType] = UNSET + """Number of rows in a column that do not contain content.""" + + sql_missing_values_count_long: Union[int, None, UnsetType] = UNSET + """Number of rows in a column that do not contain content.""" + + sql_missing_values_percentage: Union[float, None, UnsetType] = UNSET + """Percentage of rows in a column that do not contain content.""" + + sql_uniqueness_percentage: Union[float, None, UnsetType] = UNSET + """Ratio indicating how unique data in this column is: 0 indicates that all values are the same, 100 indicates that all values in this column are unique.""" + + sql_variance: Union[float, None, UnsetType] = UNSET + """Calculated variance of the values in a numeric column.""" + + column_top_values: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of top values in this column.""" + + sql_max_value: Union[float, None, UnsetType] = UNSET + """Greatest value in a numeric column.""" + + sql_min_value: Union[float, None, UnsetType] = UNSET + """Least value in a numeric column.""" + + sql_mean_value: Union[float, None, UnsetType] = UNSET + """Arithmetic mean of the values in a numeric column.""" + + sql_sum_value: Union[float, None, UnsetType] = UNSET + """Calculated sum of the values in a numeric column.""" + + sql_median_value: Union[float, None, UnsetType] = UNSET + """Calculated median of the values in a numeric column.""" + + sql_standard_deviation_value: Union[float, None, UnsetType] = UNSET + """Calculated standard deviation of the values in a numeric column.""" + + sql_average_value: Union[float, None, UnsetType] = UNSET + """Average value in this column.""" + + sql_variance_value: Union[float, None, UnsetType] = UNSET + """Calculated variance of the values in a numeric column.""" + + sql_average_length_value: Union[float, None, UnsetType] = UNSET + """Average length of values in a string column.""" + + sql_distribution_histogram: Union[Dict[str, Any], None, UnsetType] = UNSET + """Detailed information representing a histogram of values for a column.""" + + sql_depth_level: Union[int, None, UnsetType] = UNSET + """Level of nesting of this column, used for STRUCT and NESTED columns.""" + + nosql_collection_name: Union[str, None, UnsetType] = UNSET + """Simple name of the cosmos/mongo collection in which this SQL asset (column) exists, or empty if it does not exist within a cosmos/mongo collection.""" + + nosql_collection_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the cosmos/mongo collection in which this SQL asset (column) exists, or empty if it does not exist within a cosmos/mongo collection.""" + + sql_is_measure: Union[bool, None, UnsetType] = UNSET + """When true, this column is of type measure/calculated.""" + + sql_measure_type: Union[str, None, UnsetType] = UNSET + """The type of measure/calculated column this is, eg: base, calculated, derived.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cosmos_mongo_db_collection: Union[ + RelatedCosmosMongoDBCollection, None, UnsetType + ] = msgspec.field(default=UNSET, name="cosmosMongoDBCollection") + """Cosmos collection in which this column exists.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + metric_timestamps: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + data_quality_metric_dimensions: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_base_column_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this column.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dq_reference_column_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this column is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_metrics: Union[List[RelatedDbtMetric], None, UnsetType] = UNSET + """Metrics related to this model column.""" + + dbt_model_columns: Union[List[RelatedDbtModelColumn], None, UnsetType] = UNSET + """(Deprecated) Model columns related to this model column.""" + + column_dbt_model_columns: Union[List[RelatedDbtModelColumn], None, UnsetType] = ( + UNSET + ) + """Model columns related to this column.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mongo_db_collection: Union[RelatedMongoDBCollection, None, UnsetType] = ( + msgspec.field(default=UNSET, name="mongoDBCollection") + ) + """Collection in which the columns exist.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + table: Union[RelatedTable, None, UnsetType] = UNSET + """Table in which this column exists.""" + + nested_columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Nested columns that exist within this column.""" + + parent_column: Union[RelatedColumn, None, UnsetType] = UNSET + """Column in which this sub-column is nested.""" + + table_partition: Union[RelatedTablePartition, None, UnsetType] = UNSET + """Table partition that contains this column.""" + + view: Union[RelatedView, None, UnsetType] = UNSET + """View in which this column exists.""" + + calculation_view: Union[RelatedCalculationView, None, UnsetType] = UNSET + """Calculate view in which this column exists.""" + + materialised_view: Union[RelatedMaterialisedView, None, UnsetType] = UNSET + """Materialized view in which this column exists.""" + + foreign_key_to: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Columns that use this column as a foreign key.""" + + foreign_key_from: Union[RelatedColumn, None, UnsetType] = UNSET + """Column this foreign key column refers to.""" + + queries: Union[List[RelatedQuery], None, UnsetType] = UNSET + """Queries that access this column.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_dynamic_table: Union[RelatedSnowflakeDynamicTable, None, UnsetType] = ( + UNSET + ) + """Snowflake dynamic table in which this column exists.""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Column" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+/[^/]+$" + ) + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + parent_qualified_name: str, + parent_type: type, + order: int, + parent_name: str | None = None, + database_name: str | None = None, + database_qualified_name: str | None = None, + schema_name: str | None = None, + schema_qualified_name: str | None = None, + table_name: str | None = None, + table_qualified_name: str | None = None, + connection_qualified_name: str | None = None, + ) -> "Column": + """ + Create a new Column asset. + + Args: + name: Name of the column + parent_qualified_name: Unique name of the parent (table/view/etc) + parent_type: Type of parent (Table, View, MaterialisedView, etc) + order: Order of the column in the parent + parent_name: Simple name of the parent + database_name: Simple name of the database + database_qualified_name: Unique name of the database + schema_name: Simple name of the schema + schema_qualified_name: Unique name of the schema + table_name: (deprecated) Simple name of the table + table_qualified_name: (deprecated) Unique name of the table + connection_qualified_name: Unique name of the connection + + Returns: + Column instance ready to be created + + Raises: + ValueError: If required parameters are missing or invalid + """ + if table_name: + warn( + ("`table_name` is deprecated, please use `parent_name` instead"), + DeprecationWarning, + stacklevel=2, + ) + if table_qualified_name: + warn( + ( + "`table_qualified_name` is deprecated, please use `parent_qualified_name` instead" + ), + DeprecationWarning, + stacklevel=2, + ) + + validate_required_fields( + ["name", "parent_qualified_name", "parent_type", "order"], + [name, parent_qualified_name, parent_type, order], + ) + + # Use AtlanConnectorType.get_connector_name for validation (exact parity with pydantic) + connection_qn: str | None = None + if connection_qualified_name: + connector_name = str( + AtlanConnectorType.get_connector_name(connection_qualified_name) + ) + else: + result = AtlanConnectorType.get_connector_name( + parent_qualified_name, "parent_qualified_name", 6 + ) + connection_qn = str(result[0]) + connector_name = str(result[1]) + if order < 0: + raise ValueError("Order must be be a positive integer") + + # Get the type name from the parent_type class + parent_type_name = getattr(parent_type, "__name__", None) + + # Validate parent type + valid_types = [ + "Table", + "View", + "MaterialisedView", + "TablePartition", + "SnowflakeDynamicTable", + "Column", + ] + if parent_type_name not in valid_types: + raise ValueError( + "parent_type must be either Table, SnowflakeDynamicTable, View, MaterializeView or TablePartition" + ) + + if parent_type_name == "Column": + raise ValueError( + "parent_type must be either Table, SnowflakeDynamicTable, View, MaterializeView or TablePartition" + ) + + # Parse parent_qualified_name to derive fields + fields = parent_qualified_name.split("/") + + connection_qualified_name = connection_qualified_name or connection_qn + database_name = database_name or fields[3] + schema_name = schema_name or fields[4] + parent_name = parent_name or fields[5] + database_qualified_name = ( + database_qualified_name or f"{connection_qualified_name}/{database_name}" + ) + schema_qualified_name = ( + schema_qualified_name or f"{database_qualified_name}/{schema_name}" + ) + + database_qualified_name = ( + database_qualified_name + or f"{fields[0]}/{fields[1]}/{fields[2]}/{database_name}" + ) + schema_qualified_name = ( + schema_qualified_name or f"{database_qualified_name}/{schema_name}" + ) + + connection_qualified_name = ( + connection_qualified_name or f"{fields[0]}/{fields[1]}/{fields[2]}" + ) + + qualified_name = f"{parent_qualified_name}/{name}" + + # Build the column + col = cls( + name=name, + qualified_name=qualified_name, + connector_name=connector_name, + connection_qualified_name=connection_qualified_name, + schema_name=schema_name, + schema_qualified_name=schema_qualified_name, + database_name=database_name, + database_qualified_name=database_qualified_name, + order=order, + ) + + # Set parent-specific fields + if parent_type_name == "Table": + col.table_qualified_name = parent_qualified_name + col.table = RelatedTable( + qualified_name=parent_qualified_name, type_name="Table" + ) + col.table_name = parent_name + elif parent_type_name == "View": + col.view_qualified_name = parent_qualified_name + col.view = RelatedView( + qualified_name=parent_qualified_name, type_name="View" + ) + col.view_name = parent_name + elif parent_type_name == "MaterialisedView": + col.view_qualified_name = parent_qualified_name + col.materialised_view = RelatedMaterialisedView( + qualified_name=parent_qualified_name, type_name="MaterialisedView" + ) + col.view_name = parent_name + elif parent_type_name == "TablePartition": + col.table_qualified_name = parent_qualified_name + col.table_partition = RelatedTablePartition( + qualified_name=parent_qualified_name, type_name="TablePartition" + ) + col.table_name = parent_name + elif parent_type_name == "SnowflakeDynamicTable": + col.table_qualified_name = parent_qualified_name + col.snowflake_dynamic_table = RelatedSnowflakeDynamicTable( + qualified_name=parent_qualified_name, + type_name="SnowflakeDynamicTable", + ) + col.table_name = parent_name + + return col + + @classmethod + def updater(cls, qualified_name: str = "", name: str = "") -> "Column": + """ + Create a Column instance for modification. + + Args: + qualified_name: Unique name of the column + name: Name of the column + + Returns: + Column instance for modification + + Raises: + ValueError: If required parameters are missing + """ + if not qualified_name: + raise ValueError("qualified_name is required") + if not name: + raise ValueError("name is required") + + return cls(qualified_name=qualified_name, name=name) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _column_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Column: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Column instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _column_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class ColumnAttributes(AssetAttributes): + """Column-specific attributes for nested API format.""" + + data_type: Union[str, None, UnsetType] = UNSET + """Data type of values in this column.""" + + sub_data_type: Union[str, None, UnsetType] = UNSET + """Sub-data type of this column.""" + + sql_compression: Union[str, None, UnsetType] = UNSET + """Compression type of this column.""" + + sql_encoding: Union[str, None, UnsetType] = UNSET + """Encoding type of this column.""" + + raw_data_type_definition: Union[str, None, UnsetType] = UNSET + """Raw data type definition of this column.""" + + order: Union[int, None, UnsetType] = UNSET + """Order (position) in which this column appears in the table (starting at 1).""" + + nested_column_order: Union[str, None, UnsetType] = UNSET + """Order (position) in which this column appears in the nested Column (nest level starts at 1).""" + + nested_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns nested within this (STRUCT or NESTED) column.""" + + column_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of top-level upstream nested columns.""" + + is_partition: Union[bool, None, UnsetType] = UNSET + """Whether this column is a partition column (true) or not (false).""" + + partition_order: Union[int, None, UnsetType] = UNSET + """Order (position) of this partition column in the table.""" + + is_clustered: Union[bool, None, UnsetType] = UNSET + """Whether this column is a clustered column (true) or not (false).""" + + is_primary: Union[bool, None, UnsetType] = UNSET + """When true, this column is the primary key for the table.""" + + is_foreign: Union[bool, None, UnsetType] = UNSET + """When true, this column is a foreign key to another table. NOTE: this must be true when using the foreignKeyTo relationship to specify columns that refer to this column as a foreign key.""" + + is_indexed: Union[bool, None, UnsetType] = UNSET + """When true, this column is indexed in the database.""" + + is_sort: Union[bool, None, UnsetType] = UNSET + """Whether this column is a sort column (true) or not (false).""" + + is_dist: Union[bool, None, UnsetType] = UNSET + """Whether this column is a distribution column (true) or not (false).""" + + is_pinned: Union[bool, None, UnsetType] = UNSET + """Whether this column is pinned (true) or not (false).""" + + pinned_by: Union[str, None, UnsetType] = UNSET + """User who pinned this column.""" + + pinned_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this column was pinned, in milliseconds.""" + + precision: Union[int, None, UnsetType] = UNSET + """Total number of digits allowed, when the dataType is numeric.""" + + default_value: Union[str, None, UnsetType] = UNSET + """Default value for this column.""" + + is_nullable: Union[bool, None, UnsetType] = UNSET + """When true, the values in this column can be null.""" + + numeric_scale: Union[float, None, UnsetType] = UNSET + """Number of digits allowed to the right of the decimal point.""" + + max_length: Union[int, None, UnsetType] = UNSET + """Maximum length of a value in this column.""" + + validations: Union[Dict[str, str], None, UnsetType] = UNSET + """Validations for this column.""" + + parent_column_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the column this column is nested within, for STRUCT and NESTED columns.""" + + parent_column_name: Union[str, None, UnsetType] = UNSET + """Simple name of the column this column is nested within, for STRUCT and NESTED columns.""" + + sql_distinct_values_count: Union[int, None, UnsetType] = UNSET + """Number of rows that contain distinct values.""" + + sql_distinct_values_count_long: Union[int, None, UnsetType] = UNSET + """Number of rows that contain distinct values.""" + + sql_histogram: Union[Dict[str, Any], None, UnsetType] = UNSET + """List of values in a histogram that represents the contents of this column.""" + + sql_max: Union[float, None, UnsetType] = UNSET + """Greatest value in a numeric column.""" + + sql_min: Union[float, None, UnsetType] = UNSET + """Least value in a numeric column.""" + + sql_mean: Union[float, None, UnsetType] = UNSET + """Arithmetic mean of the values in a numeric column.""" + + sql_sum: Union[float, None, UnsetType] = UNSET + """Calculated sum of the values in a numeric column.""" + + sql_median: Union[float, None, UnsetType] = UNSET + """Calculated median of the values in a numeric column.""" + + sql_standard_deviation: Union[float, None, UnsetType] = UNSET + """Calculated standard deviation of the values in a numeric column.""" + + sql_unique_values_count: Union[int, None, UnsetType] = UNSET + """Number of rows in which a value in this column appears only once.""" + + sql_unique_values_count_long: Union[int, None, UnsetType] = UNSET + """Number of rows in which a value in this column appears only once.""" + + sql_average: Union[float, None, UnsetType] = UNSET + """Average value in this column.""" + + sql_average_length: Union[float, None, UnsetType] = UNSET + """Average length of values in a string column.""" + + sql_duplicate_values_count: Union[int, None, UnsetType] = UNSET + """Number of rows that contain duplicate values.""" + + sql_duplicate_values_count_long: Union[int, None, UnsetType] = UNSET + """Number of rows that contain duplicate values.""" + + sql_maximum_string_length: Union[int, None, UnsetType] = UNSET + """Length of the longest value in a string column.""" + + column_maxs: Union[List[str], None, UnsetType] = UNSET + """List of the greatest values in a column.""" + + sql_minimum_string_length: Union[int, None, UnsetType] = UNSET + """Length of the shortest value in a string column.""" + + column_mins: Union[List[str], None, UnsetType] = UNSET + """List of the least values in a column.""" + + sql_missing_values_count: Union[int, None, UnsetType] = UNSET + """Number of rows in a column that do not contain content.""" + + sql_missing_values_count_long: Union[int, None, UnsetType] = UNSET + """Number of rows in a column that do not contain content.""" + + sql_missing_values_percentage: Union[float, None, UnsetType] = UNSET + """Percentage of rows in a column that do not contain content.""" + + sql_uniqueness_percentage: Union[float, None, UnsetType] = UNSET + """Ratio indicating how unique data in this column is: 0 indicates that all values are the same, 100 indicates that all values in this column are unique.""" + + sql_variance: Union[float, None, UnsetType] = UNSET + """Calculated variance of the values in a numeric column.""" + + column_top_values: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of top values in this column.""" + + sql_max_value: Union[float, None, UnsetType] = UNSET + """Greatest value in a numeric column.""" + + sql_min_value: Union[float, None, UnsetType] = UNSET + """Least value in a numeric column.""" + + sql_mean_value: Union[float, None, UnsetType] = UNSET + """Arithmetic mean of the values in a numeric column.""" + + sql_sum_value: Union[float, None, UnsetType] = UNSET + """Calculated sum of the values in a numeric column.""" + + sql_median_value: Union[float, None, UnsetType] = UNSET + """Calculated median of the values in a numeric column.""" + + sql_standard_deviation_value: Union[float, None, UnsetType] = UNSET + """Calculated standard deviation of the values in a numeric column.""" + + sql_average_value: Union[float, None, UnsetType] = UNSET + """Average value in this column.""" + + sql_variance_value: Union[float, None, UnsetType] = UNSET + """Calculated variance of the values in a numeric column.""" + + sql_average_length_value: Union[float, None, UnsetType] = UNSET + """Average length of values in a string column.""" + + sql_distribution_histogram: Union[Dict[str, Any], None, UnsetType] = UNSET + """Detailed information representing a histogram of values for a column.""" + + sql_depth_level: Union[int, None, UnsetType] = UNSET + """Level of nesting of this column, used for STRUCT and NESTED columns.""" + + nosql_collection_name: Union[str, None, UnsetType] = UNSET + """Simple name of the cosmos/mongo collection in which this SQL asset (column) exists, or empty if it does not exist within a cosmos/mongo collection.""" + + nosql_collection_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the cosmos/mongo collection in which this SQL asset (column) exists, or empty if it does not exist within a cosmos/mongo collection.""" + + sql_is_measure: Union[bool, None, UnsetType] = UNSET + """When true, this column is of type measure/calculated.""" + + sql_measure_type: Union[str, None, UnsetType] = UNSET + """The type of measure/calculated column this is, eg: base, calculated, derived.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + +class ColumnRelationshipAttributes(AssetRelationshipAttributes): + """Column-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cosmos_mongo_db_collection: Union[ + RelatedCosmosMongoDBCollection, None, UnsetType + ] = msgspec.field(default=UNSET, name="cosmosMongoDBCollection") + """Cosmos collection in which this column exists.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + metric_timestamps: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + data_quality_metric_dimensions: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_base_column_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this column.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dq_reference_column_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this column is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_metrics: Union[List[RelatedDbtMetric], None, UnsetType] = UNSET + """Metrics related to this model column.""" + + dbt_model_columns: Union[List[RelatedDbtModelColumn], None, UnsetType] = UNSET + """(Deprecated) Model columns related to this model column.""" + + column_dbt_model_columns: Union[List[RelatedDbtModelColumn], None, UnsetType] = ( + UNSET + ) + """Model columns related to this column.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mongo_db_collection: Union[RelatedMongoDBCollection, None, UnsetType] = ( + msgspec.field(default=UNSET, name="mongoDBCollection") + ) + """Collection in which the columns exist.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + table: Union[RelatedTable, None, UnsetType] = UNSET + """Table in which this column exists.""" + + nested_columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Nested columns that exist within this column.""" + + parent_column: Union[RelatedColumn, None, UnsetType] = UNSET + """Column in which this sub-column is nested.""" + + table_partition: Union[RelatedTablePartition, None, UnsetType] = UNSET + """Table partition that contains this column.""" + + view: Union[RelatedView, None, UnsetType] = UNSET + """View in which this column exists.""" + + calculation_view: Union[RelatedCalculationView, None, UnsetType] = UNSET + """Calculate view in which this column exists.""" + + materialised_view: Union[RelatedMaterialisedView, None, UnsetType] = UNSET + """Materialized view in which this column exists.""" + + foreign_key_to: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Columns that use this column as a foreign key.""" + + foreign_key_from: Union[RelatedColumn, None, UnsetType] = UNSET + """Column this foreign key column refers to.""" + + queries: Union[List[RelatedQuery], None, UnsetType] = UNSET + """Queries that access this column.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_dynamic_table: Union[RelatedSnowflakeDynamicTable, None, UnsetType] = ( + UNSET + ) + """Snowflake dynamic table in which this column exists.""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class ColumnNested(AssetNested): + """Column in nested API format for high-performance serialization.""" + + attributes: Union[ColumnAttributes, UnsetType] = UNSET + relationship_attributes: Union[ColumnRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ColumnRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[ColumnRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_COLUMN_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "cosmos_mongo_db_collection", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "metric_timestamps", + "data_quality_metric_dimensions", + "dq_base_dataset_rules", + "dq_base_column_rules", + "dq_reference_dataset_rules", + "dq_reference_column_rules", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_metrics", + "dbt_model_columns", + "column_dbt_model_columns", + "dbt_seed_assets", + "meanings", + "mongo_db_collection", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "table", + "nested_columns", + "parent_column", + "table_partition", + "view", + "calculation_view", + "materialised_view", + "foreign_key_to", + "foreign_key_from", + "queries", + "schema_registry_subjects", + "snowflake_dynamic_table", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_column_attrs(attrs: ColumnAttributes, obj: Column) -> None: + """Populate Column-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.data_type = obj.data_type + attrs.sub_data_type = obj.sub_data_type + attrs.sql_compression = obj.sql_compression + attrs.sql_encoding = obj.sql_encoding + attrs.raw_data_type_definition = obj.raw_data_type_definition + attrs.order = obj.order + attrs.nested_column_order = obj.nested_column_order + attrs.nested_column_count = obj.nested_column_count + attrs.column_hierarchy = obj.column_hierarchy + attrs.is_partition = obj.is_partition + attrs.partition_order = obj.partition_order + attrs.is_clustered = obj.is_clustered + attrs.is_primary = obj.is_primary + attrs.is_foreign = obj.is_foreign + attrs.is_indexed = obj.is_indexed + attrs.is_sort = obj.is_sort + attrs.is_dist = obj.is_dist + attrs.is_pinned = obj.is_pinned + attrs.pinned_by = obj.pinned_by + attrs.pinned_at = obj.pinned_at + attrs.precision = obj.precision + attrs.default_value = obj.default_value + attrs.is_nullable = obj.is_nullable + attrs.numeric_scale = obj.numeric_scale + attrs.max_length = obj.max_length + attrs.validations = obj.validations + attrs.parent_column_qualified_name = obj.parent_column_qualified_name + attrs.parent_column_name = obj.parent_column_name + attrs.sql_distinct_values_count = obj.sql_distinct_values_count + attrs.sql_distinct_values_count_long = obj.sql_distinct_values_count_long + attrs.sql_histogram = obj.sql_histogram + attrs.sql_max = obj.sql_max + attrs.sql_min = obj.sql_min + attrs.sql_mean = obj.sql_mean + attrs.sql_sum = obj.sql_sum + attrs.sql_median = obj.sql_median + attrs.sql_standard_deviation = obj.sql_standard_deviation + attrs.sql_unique_values_count = obj.sql_unique_values_count + attrs.sql_unique_values_count_long = obj.sql_unique_values_count_long + attrs.sql_average = obj.sql_average + attrs.sql_average_length = obj.sql_average_length + attrs.sql_duplicate_values_count = obj.sql_duplicate_values_count + attrs.sql_duplicate_values_count_long = obj.sql_duplicate_values_count_long + attrs.sql_maximum_string_length = obj.sql_maximum_string_length + attrs.column_maxs = obj.column_maxs + attrs.sql_minimum_string_length = obj.sql_minimum_string_length + attrs.column_mins = obj.column_mins + attrs.sql_missing_values_count = obj.sql_missing_values_count + attrs.sql_missing_values_count_long = obj.sql_missing_values_count_long + attrs.sql_missing_values_percentage = obj.sql_missing_values_percentage + attrs.sql_uniqueness_percentage = obj.sql_uniqueness_percentage + attrs.sql_variance = obj.sql_variance + attrs.column_top_values = obj.column_top_values + attrs.sql_max_value = obj.sql_max_value + attrs.sql_min_value = obj.sql_min_value + attrs.sql_mean_value = obj.sql_mean_value + attrs.sql_sum_value = obj.sql_sum_value + attrs.sql_median_value = obj.sql_median_value + attrs.sql_standard_deviation_value = obj.sql_standard_deviation_value + attrs.sql_average_value = obj.sql_average_value + attrs.sql_variance_value = obj.sql_variance_value + attrs.sql_average_length_value = obj.sql_average_length_value + attrs.sql_distribution_histogram = obj.sql_distribution_histogram + attrs.sql_depth_level = obj.sql_depth_level + attrs.nosql_collection_name = obj.nosql_collection_name + attrs.nosql_collection_qualified_name = obj.nosql_collection_qualified_name + attrs.sql_is_measure = obj.sql_is_measure + attrs.sql_measure_type = obj.sql_measure_type + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + + +def _extract_column_attrs(attrs: ColumnAttributes) -> dict: + """Extract all Column attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["data_type"] = attrs.data_type + result["sub_data_type"] = attrs.sub_data_type + result["sql_compression"] = attrs.sql_compression + result["sql_encoding"] = attrs.sql_encoding + result["raw_data_type_definition"] = attrs.raw_data_type_definition + result["order"] = attrs.order + result["nested_column_order"] = attrs.nested_column_order + result["nested_column_count"] = attrs.nested_column_count + result["column_hierarchy"] = attrs.column_hierarchy + result["is_partition"] = attrs.is_partition + result["partition_order"] = attrs.partition_order + result["is_clustered"] = attrs.is_clustered + result["is_primary"] = attrs.is_primary + result["is_foreign"] = attrs.is_foreign + result["is_indexed"] = attrs.is_indexed + result["is_sort"] = attrs.is_sort + result["is_dist"] = attrs.is_dist + result["is_pinned"] = attrs.is_pinned + result["pinned_by"] = attrs.pinned_by + result["pinned_at"] = attrs.pinned_at + result["precision"] = attrs.precision + result["default_value"] = attrs.default_value + result["is_nullable"] = attrs.is_nullable + result["numeric_scale"] = attrs.numeric_scale + result["max_length"] = attrs.max_length + result["validations"] = attrs.validations + result["parent_column_qualified_name"] = attrs.parent_column_qualified_name + result["parent_column_name"] = attrs.parent_column_name + result["sql_distinct_values_count"] = attrs.sql_distinct_values_count + result["sql_distinct_values_count_long"] = attrs.sql_distinct_values_count_long + result["sql_histogram"] = attrs.sql_histogram + result["sql_max"] = attrs.sql_max + result["sql_min"] = attrs.sql_min + result["sql_mean"] = attrs.sql_mean + result["sql_sum"] = attrs.sql_sum + result["sql_median"] = attrs.sql_median + result["sql_standard_deviation"] = attrs.sql_standard_deviation + result["sql_unique_values_count"] = attrs.sql_unique_values_count + result["sql_unique_values_count_long"] = attrs.sql_unique_values_count_long + result["sql_average"] = attrs.sql_average + result["sql_average_length"] = attrs.sql_average_length + result["sql_duplicate_values_count"] = attrs.sql_duplicate_values_count + result["sql_duplicate_values_count_long"] = attrs.sql_duplicate_values_count_long + result["sql_maximum_string_length"] = attrs.sql_maximum_string_length + result["column_maxs"] = attrs.column_maxs + result["sql_minimum_string_length"] = attrs.sql_minimum_string_length + result["column_mins"] = attrs.column_mins + result["sql_missing_values_count"] = attrs.sql_missing_values_count + result["sql_missing_values_count_long"] = attrs.sql_missing_values_count_long + result["sql_missing_values_percentage"] = attrs.sql_missing_values_percentage + result["sql_uniqueness_percentage"] = attrs.sql_uniqueness_percentage + result["sql_variance"] = attrs.sql_variance + result["column_top_values"] = attrs.column_top_values + result["sql_max_value"] = attrs.sql_max_value + result["sql_min_value"] = attrs.sql_min_value + result["sql_mean_value"] = attrs.sql_mean_value + result["sql_sum_value"] = attrs.sql_sum_value + result["sql_median_value"] = attrs.sql_median_value + result["sql_standard_deviation_value"] = attrs.sql_standard_deviation_value + result["sql_average_value"] = attrs.sql_average_value + result["sql_variance_value"] = attrs.sql_variance_value + result["sql_average_length_value"] = attrs.sql_average_length_value + result["sql_distribution_histogram"] = attrs.sql_distribution_histogram + result["sql_depth_level"] = attrs.sql_depth_level + result["nosql_collection_name"] = attrs.nosql_collection_name + result["nosql_collection_qualified_name"] = attrs.nosql_collection_qualified_name + result["sql_is_measure"] = attrs.sql_is_measure + result["sql_measure_type"] = attrs.sql_measure_type + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _column_to_nested(column: Column) -> ColumnNested: + """Convert flat Column to nested format.""" + attrs = ColumnAttributes() + _populate_column_attrs(attrs, column) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + column, _COLUMN_REL_FIELDS, ColumnRelationshipAttributes + ) + return ColumnNested( + guid=column.guid, + type_name=column.type_name, + status=column.status, + version=column.version, + create_time=column.create_time, + update_time=column.update_time, + created_by=column.created_by, + updated_by=column.updated_by, + classifications=column.classifications, + classification_names=column.classification_names, + meanings=column.meanings, + labels=column.labels, + business_attributes=column.business_attributes, + custom_attributes=column.custom_attributes, + pending_tasks=column.pending_tasks, + proxy=column.proxy, + is_incomplete=column.is_incomplete, + provenance_type=column.provenance_type, + home_id=column.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _column_from_nested(nested: ColumnNested) -> Column: + """Convert nested format to flat Column.""" + attrs = nested.attributes if nested.attributes is not UNSET else ColumnAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _COLUMN_REL_FIELDS, + ColumnRelationshipAttributes, + ) + return Column( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_column_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _column_to_nested_bytes(column: Column, serde: Serde) -> bytes: + """Convert flat Column to nested JSON bytes.""" + return serde.encode(_column_to_nested(column)) + + +def _column_from_nested_bytes(data: bytes, serde: Serde) -> Column: + """Convert nested JSON bytes to flat Column.""" + nested = serde.decode(data, ColumnNested) + return _column_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +Column.DATA_TYPE = KeywordTextField("dataType", "dataType", "dataType.text") +Column.SUB_DATA_TYPE = KeywordField("subDataType", "subDataType") +Column.SQL_COMPRESSION = KeywordField("sqlCompression", "sqlCompression") +Column.SQL_ENCODING = KeywordField("sqlEncoding", "sqlEncoding") +Column.RAW_DATA_TYPE_DEFINITION = KeywordField( + "rawDataTypeDefinition", "rawDataTypeDefinition" +) +Column.ORDER = NumericField("order", "order") +Column.NESTED_COLUMN_ORDER = KeywordTextField( + "nestedColumnOrder", "nestedColumnOrder", "nestedColumnOrder.text" +) +Column.NESTED_COLUMN_COUNT = NumericField("nestedColumnCount", "nestedColumnCount") +Column.COLUMN_HIERARCHY = KeywordField("columnHierarchy", "columnHierarchy") +Column.IS_PARTITION = BooleanField("isPartition", "isPartition") +Column.PARTITION_ORDER = NumericField("partitionOrder", "partitionOrder") +Column.IS_CLUSTERED = BooleanField("isClustered", "isClustered") +Column.IS_PRIMARY = BooleanField("isPrimary", "isPrimary") +Column.IS_FOREIGN = BooleanField("isForeign", "isForeign") +Column.IS_INDEXED = BooleanField("isIndexed", "isIndexed") +Column.IS_SORT = BooleanField("isSort", "isSort") +Column.IS_DIST = BooleanField("isDist", "isDist") +Column.IS_PINNED = BooleanField("isPinned", "isPinned") +Column.PINNED_BY = KeywordField("pinnedBy", "pinnedBy") +Column.PINNED_AT = NumericField("pinnedAt", "pinnedAt") +Column.PRECISION = NumericField("precision", "precision") +Column.DEFAULT_VALUE = KeywordField("defaultValue", "defaultValue") +Column.IS_NULLABLE = BooleanField("isNullable", "isNullable") +Column.NUMERIC_SCALE = NumericField("numericScale", "numericScale") +Column.MAX_LENGTH = NumericField("maxLength", "maxLength") +Column.VALIDATIONS = KeywordField("validations", "validations") +Column.PARENT_COLUMN_QUALIFIED_NAME = KeywordTextField( + "parentColumnQualifiedName", + "parentColumnQualifiedName", + "parentColumnQualifiedName.text", +) +Column.PARENT_COLUMN_NAME = KeywordField("parentColumnName", "parentColumnName") +Column.SQL_DISTINCT_VALUES_COUNT = NumericField( + "sqlDistinctValuesCount", "sqlDistinctValuesCount" +) +Column.SQL_DISTINCT_VALUES_COUNT_LONG = NumericField( + "sqlDistinctValuesCountLong", "sqlDistinctValuesCountLong" +) +Column.SQL_HISTOGRAM = KeywordField("sqlHistogram", "sqlHistogram") +Column.SQL_MAX = NumericField("sqlMax", "sqlMax") +Column.SQL_MIN = NumericField("sqlMin", "sqlMin") +Column.SQL_MEAN = NumericField("sqlMean", "sqlMean") +Column.SQL_SUM = NumericField("sqlSum", "sqlSum") +Column.SQL_MEDIAN = NumericField("sqlMedian", "sqlMedian") +Column.SQL_STANDARD_DEVIATION = NumericField( + "sqlStandardDeviation", "sqlStandardDeviation" +) +Column.SQL_UNIQUE_VALUES_COUNT = NumericField( + "sqlUniqueValuesCount", "sqlUniqueValuesCount" +) +Column.SQL_UNIQUE_VALUES_COUNT_LONG = NumericField( + "sqlUniqueValuesCountLong", "sqlUniqueValuesCountLong" +) +Column.SQL_AVERAGE = NumericField("sqlAverage", "sqlAverage") +Column.SQL_AVERAGE_LENGTH = NumericField("sqlAverageLength", "sqlAverageLength") +Column.SQL_DUPLICATE_VALUES_COUNT = NumericField( + "sqlDuplicateValuesCount", "sqlDuplicateValuesCount" +) +Column.SQL_DUPLICATE_VALUES_COUNT_LONG = NumericField( + "sqlDuplicateValuesCountLong", "sqlDuplicateValuesCountLong" +) +Column.SQL_MAXIMUM_STRING_LENGTH = NumericField( + "sqlMaximumStringLength", "sqlMaximumStringLength" +) +Column.COLUMN_MAXS = KeywordField("columnMaxs", "columnMaxs") +Column.SQL_MINIMUM_STRING_LENGTH = NumericField( + "sqlMinimumStringLength", "sqlMinimumStringLength" +) +Column.COLUMN_MINS = KeywordField("columnMins", "columnMins") +Column.SQL_MISSING_VALUES_COUNT = NumericField( + "sqlMissingValuesCount", "sqlMissingValuesCount" +) +Column.SQL_MISSING_VALUES_COUNT_LONG = NumericField( + "sqlMissingValuesCountLong", "sqlMissingValuesCountLong" +) +Column.SQL_MISSING_VALUES_PERCENTAGE = NumericField( + "sqlMissingValuesPercentage", "sqlMissingValuesPercentage" +) +Column.SQL_UNIQUENESS_PERCENTAGE = NumericField( + "sqlUniquenessPercentage", "sqlUniquenessPercentage" +) +Column.SQL_VARIANCE = NumericField("sqlVariance", "sqlVariance") +Column.COLUMN_TOP_VALUES = KeywordField("columnTopValues", "columnTopValues") +Column.SQL_MAX_VALUE = NumericField("sqlMaxValue", "sqlMaxValue") +Column.SQL_MIN_VALUE = NumericField("sqlMinValue", "sqlMinValue") +Column.SQL_MEAN_VALUE = NumericField("sqlMeanValue", "sqlMeanValue") +Column.SQL_SUM_VALUE = NumericField("sqlSumValue", "sqlSumValue") +Column.SQL_MEDIAN_VALUE = NumericField("sqlMedianValue", "sqlMedianValue") +Column.SQL_STANDARD_DEVIATION_VALUE = NumericField( + "sqlStandardDeviationValue", "sqlStandardDeviationValue" +) +Column.SQL_AVERAGE_VALUE = NumericField("sqlAverageValue", "sqlAverageValue") +Column.SQL_VARIANCE_VALUE = NumericField("sqlVarianceValue", "sqlVarianceValue") +Column.SQL_AVERAGE_LENGTH_VALUE = NumericField( + "sqlAverageLengthValue", "sqlAverageLengthValue" +) +Column.SQL_DISTRIBUTION_HISTOGRAM = KeywordField( + "sqlDistributionHistogram", "sqlDistributionHistogram" +) +Column.SQL_DEPTH_LEVEL = NumericField("sqlDepthLevel", "sqlDepthLevel") +Column.NOSQL_COLLECTION_NAME = KeywordField( + "nosqlCollectionName", "nosqlCollectionName" +) +Column.NOSQL_COLLECTION_QUALIFIED_NAME = KeywordField( + "nosqlCollectionQualifiedName", "nosqlCollectionQualifiedName" +) +Column.SQL_IS_MEASURE = BooleanField("sqlIsMeasure", "sqlIsMeasure") +Column.SQL_MEASURE_TYPE = KeywordField("sqlMeasureType", "sqlMeasureType") +Column.QUERY_COUNT = NumericField("queryCount", "queryCount") +Column.QUERY_USER_COUNT = NumericField("queryUserCount", "queryUserCount") +Column.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +Column.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +Column.DATABASE_NAME = KeywordField("databaseName", "databaseName") +Column.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +Column.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +Column.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +Column.TABLE_NAME = KeywordField("tableName", "tableName") +Column.TABLE_QUALIFIED_NAME = KeywordField("tableQualifiedName", "tableQualifiedName") +Column.VIEW_NAME = KeywordField("viewName", "viewName") +Column.VIEW_QUALIFIED_NAME = KeywordField("viewQualifiedName", "viewQualifiedName") +Column.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +Column.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +Column.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +Column.LAST_PROFILED_AT = NumericField("lastProfiledAt", "lastProfiledAt") +Column.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +Column.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +Column.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Column.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Column.ANOMALO_CHECKS = RelationField("anomaloChecks") +Column.APPLICATION = RelationField("application") +Column.APPLICATION_FIELD = RelationField("applicationField") +Column.COSMOS_MONGO_DB_COLLECTION = RelationField("cosmosMongoDBCollection") +Column.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Column.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Column.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Column.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Column.METRICS = RelationField("metrics") +Column.METRIC_TIMESTAMPS = RelationField("metricTimestamps") +Column.DATA_QUALITY_METRIC_DIMENSIONS = RelationField("dataQualityMetricDimensions") +Column.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Column.DQ_BASE_COLUMN_RULES = RelationField("dqBaseColumnRules") +Column.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Column.DQ_REFERENCE_COLUMN_RULES = RelationField("dqReferenceColumnRules") +Column.DBT_MODELS = RelationField("dbtModels") +Column.SQL_DBT_MODELS = RelationField("sqlDbtModels") +Column.DBT_TESTS = RelationField("dbtTests") +Column.DBT_SOURCES = RelationField("dbtSources") +Column.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +Column.DBT_METRICS = RelationField("dbtMetrics") +Column.DBT_MODEL_COLUMNS = RelationField("dbtModelColumns") +Column.COLUMN_DBT_MODEL_COLUMNS = RelationField("columnDbtModelColumns") +Column.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +Column.MEANINGS = RelationField("meanings") +Column.MONGO_DB_COLLECTION = RelationField("mongoDBCollection") +Column.MC_MONITORS = RelationField("mcMonitors") +Column.MC_INCIDENTS = RelationField("mcIncidents") +Column.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Column.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Column.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Column.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Column.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Column.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Column.FILES = RelationField("files") +Column.LINKS = RelationField("links") +Column.README = RelationField("readme") +Column.TABLE = RelationField("table") +Column.NESTED_COLUMNS = RelationField("nestedColumns") +Column.PARENT_COLUMN = RelationField("parentColumn") +Column.TABLE_PARTITION = RelationField("tablePartition") +Column.VIEW = RelationField("view") +Column.CALCULATION_VIEW = RelationField("calculationView") +Column.MATERIALISED_VIEW = RelationField("materialisedView") +Column.FOREIGN_KEY_TO = RelationField("foreignKeyTo") +Column.FOREIGN_KEY_FROM = RelationField("foreignKeyFrom") +Column.QUERIES = RelationField("queries") +Column.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Column.SNOWFLAKE_DYNAMIC_TABLE = RelationField("snowflakeDynamicTable") +Column.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +Column.SODA_CHECKS = RelationField("sodaChecks") +Column.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Column.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/column_process.py b/pyatlan_v9/model/assets/column_process.py new file mode 100644 index 000000000..0ac45de00 --- /dev/null +++ b/pyatlan_v9/model/assets/column_process.py @@ -0,0 +1,791 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +ColumnProcess asset model with flattened inheritance. + +This module provides: +- ColumnProcess: Flat asset class (easy to use) +- ColumnProcessAttributes: Nested attributes struct (extends AssetAttributes) +- ColumnProcessNested: Nested API format struct +""" + +from __future__ import annotations + +import hashlib +import re +from io import StringIO +from typing import Any, ClassVar, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .adf_related import RelatedAdfActivity +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .catalog_related import RelatedCatalog +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .fabric_related import RelatedFabricActivity +from .fivetran_related import RelatedFivetranConnector +from .flow_related import RelatedFlowControlOperation +from .gtc_related import RelatedAtlasGlossaryTerm +from .matillion_related import RelatedMatillionComponent +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .power_bi_related import RelatedPowerBIDataflow +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from .sql_related import RelatedFunction, RelatedProcedure +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .process_related import RelatedColumnProcess, RelatedProcess + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class ColumnProcess(Asset): + """ + Instance of a column-level lineage process in Atlan. Inputs and outputs of these processes should be columns. + """ + + CODE: ClassVar[Any] = None + SQL: ClassVar[Any] = None + PARENT_CONNECTION_PROCESS_QUALIFIED_NAME: ClassVar[Any] = None + AST: ClassVar[Any] = None + ADDITIONAL_ETL_CONTEXT: ClassVar[Any] = None + AI_DATASET_TYPE: ClassVar[Any] = None + ADF_ACTIVITY: ClassVar[Any] = None + AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + FABRIC_ACTIVITIES: ClassVar[Any] = None + FIVETRAN_CONNECTOR: ClassVar[Any] = None + FLOW_ORCHESTRATED_BY: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MATILLION_COMPONENT: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + POWER_BI_DATAFLOW: ClassVar[Any] = None + INPUTS: ClassVar[Any] = None + OUTPUTS: ClassVar[Any] = None + COLUMN_PROCESSES: ClassVar[Any] = None + PROCESS: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SQL_PROCEDURES: ClassVar[Any] = None + SQL_FUNCTIONS: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "ColumnProcess" + + code: Union[str, None, UnsetType] = UNSET + """Code that ran within the process.""" + + sql: Union[str, None, UnsetType] = UNSET + """SQL query that ran to produce the outputs.""" + + parent_connection_process_qualified_name: Union[List[str], None, UnsetType] = UNSET + """""" + + ast: Union[str, None, UnsetType] = UNSET + """Parsed AST of the code or SQL statements that describe the logic of this process.""" + + additional_etl_context: Union[str, None, UnsetType] = UNSET + """Additional Context of the ETL pipeline/notebook which creates the process.""" + + ai_dataset_type: Union[str, None, UnsetType] = UNSET + """Dataset type for AI Model - dataset process.""" + + adf_activity: Union[RelatedAdfActivity, None, UnsetType] = UNSET + """ADF Activity that is associated with this lineage process.""" + + airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks that exist within this process.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + fabric_activities: Union[List[RelatedFabricActivity], None, UnsetType] = UNSET + """Individual Fabric activities contained in the process.""" + + fivetran_connector: Union[RelatedFivetranConnector, None, UnsetType] = UNSET + """fivetranConnector in which this process exists.""" + + flow_orchestrated_by: Union[RelatedFlowControlOperation, None, UnsetType] = UNSET + """Orchestrated control operation that ran these data flows (process).""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + matillion_component: Union[RelatedMatillionComponent, None, UnsetType] = UNSET + """Matillion component that contains the logic for this lineage process.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + power_bi_dataflow: Union[RelatedPowerBIDataflow, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIDataflow" + ) + """PowerBI Dataflow that is associated with this lineage process.""" + + inputs: Union[List[RelatedCatalog], None, UnsetType] = UNSET + """Assets that are inputs to this process.""" + + outputs: Union[List[RelatedCatalog], None, UnsetType] = UNSET + """Assets that are outputs from this process.""" + + column_processes: Union[List[RelatedColumnProcess], None, UnsetType] = UNSET + """Processes that detail column-level lineage for this process.""" + + process: Union[RelatedProcess, None, UnsetType] = UNSET + """Parent process that contains this column-level process.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + sql_procedures: Union[List[RelatedProcedure], None, UnsetType] = UNSET + """Procedures used by this process.""" + + sql_functions: Union[List[RelatedFunction], None, UnsetType] = UNSET + """Functions used by this process.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "ColumnProcess" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + @staticmethod + def _extract_guid(relationship: Any) -> Union[str, None]: + """Extract guid from a relationship-like object.""" + if relationship is None: + return None + guid = getattr(relationship, "guid", UNSET) + if guid is UNSET or not guid: + return None + return guid + + @staticmethod + def _to_related_catalog(value: Any) -> RelatedCatalog: + """Convert any relationship-like value to a RelatedCatalog reference.""" + if isinstance(value, RelatedCatalog): + return value + guid = getattr(value, "guid", UNSET) + type_name = getattr(value, "type_name", UNSET) + if guid is not UNSET and guid: + kwargs: dict[str, Any] = {"guid": guid} + if type_name is not UNSET and type_name: + kwargs["type_name"] = type_name + return RelatedCatalog(**kwargs) + qualified_name = getattr(value, "qualified_name", UNSET) + if qualified_name is not UNSET and qualified_name: + kwargs = {"unique_attributes": {"qualifiedName": qualified_name}} + if type_name is not UNSET and type_name: + kwargs["type_name"] = type_name + return RelatedCatalog(**kwargs) + return RelatedCatalog() + + @staticmethod + def _to_related_process(value: Any) -> RelatedProcess: + """Convert any relationship-like value to a RelatedProcess reference.""" + if isinstance(value, RelatedProcess): + return value + guid = getattr(value, "guid", UNSET) + type_name = getattr(value, "type_name", UNSET) + if guid is not UNSET and guid: + kwargs: dict[str, Any] = {"guid": guid} + if type_name is not UNSET and type_name: + kwargs["type_name"] = type_name + return RelatedProcess(**kwargs) + qualified_name = getattr(value, "qualified_name", UNSET) + if qualified_name is not UNSET and qualified_name: + kwargs = {"unique_attributes": {"qualifiedName": qualified_name}} + if type_name is not UNSET and type_name: + kwargs["type_name"] = type_name + return RelatedProcess(**kwargs) + return RelatedProcess() + + @staticmethod + def generate_qualified_name( + *, + name: str, + connection_qualified_name: str, + inputs: list[Any], + outputs: list[Any], + parent: Any, + process_id: Union[str, None] = None, + ) -> str: + """Generate column process qualified name using explicit process_id or deterministic hash.""" + validate_required_fields( + ["name", "connection_qualified_name", "inputs", "outputs", "parent"], + [name, connection_qualified_name, inputs, outputs, parent], + ) + if process_id and process_id.strip(): + return f"{connection_qualified_name}/{process_id}" + buffer = StringIO() + buffer.write(name) + buffer.write(connection_qualified_name) + parent_guid = ColumnProcess._extract_guid(parent) + if parent_guid: + buffer.write(parent_guid) + for relationship in inputs: + guid = ColumnProcess._extract_guid(relationship) + if guid: + buffer.write(guid) + for relationship in outputs: + guid = ColumnProcess._extract_guid(relationship) + if guid: + buffer.write(guid) + hash_seed = buffer.getvalue() + buffer.close() + # deepcode ignore InsecureHash/test: this is not used for generating security keys + return ( + f"{connection_qualified_name}/{hashlib.md5(hash_seed.encode()).hexdigest()}" # noqa: S324 + ) + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + connection_qualified_name: str, + inputs: list[Any], + outputs: list[Any], + parent: Any, + process_id: Union[str, None] = None, + ) -> "ColumnProcess": + """Create a new ColumnProcess asset.""" + qualified_name = cls.generate_qualified_name( + name=name, + connection_qualified_name=connection_qualified_name, + inputs=inputs, + outputs=outputs, + parent=parent, + process_id=process_id, + ) + connector_name = ( + connection_qualified_name.split("/")[1] + if len(connection_qualified_name.split("/")) > 1 + else "" + ) + return cls( + name=name, + qualified_name=qualified_name, + connector_name=connector_name, + connection_qualified_name=connection_qualified_name, + inputs=[cls._to_related_catalog(item) for item in inputs], + outputs=[cls._to_related_catalog(item) for item in outputs], + process=cls._to_related_process(parent), + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "ColumnProcess": + """Create a ColumnProcess instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "ColumnProcess": + """Return only fields required for update operations.""" + return ColumnProcess.updater(qualified_name=self.qualified_name, name=self.name) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _column_process_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> ColumnProcess: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + ColumnProcess instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _column_process_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class ColumnProcessAttributes(AssetAttributes): + """ColumnProcess-specific attributes for nested API format.""" + + code: Union[str, None, UnsetType] = UNSET + """Code that ran within the process.""" + + sql: Union[str, None, UnsetType] = UNSET + """SQL query that ran to produce the outputs.""" + + parent_connection_process_qualified_name: Union[List[str], None, UnsetType] = UNSET + """""" + + ast: Union[str, None, UnsetType] = UNSET + """Parsed AST of the code or SQL statements that describe the logic of this process.""" + + additional_etl_context: Union[str, None, UnsetType] = UNSET + """Additional Context of the ETL pipeline/notebook which creates the process.""" + + ai_dataset_type: Union[str, None, UnsetType] = UNSET + """Dataset type for AI Model - dataset process.""" + + +class ColumnProcessRelationshipAttributes(AssetRelationshipAttributes): + """ColumnProcess-specific relationship attributes for nested API format.""" + + adf_activity: Union[RelatedAdfActivity, None, UnsetType] = UNSET + """ADF Activity that is associated with this lineage process.""" + + airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks that exist within this process.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + fabric_activities: Union[List[RelatedFabricActivity], None, UnsetType] = UNSET + """Individual Fabric activities contained in the process.""" + + fivetran_connector: Union[RelatedFivetranConnector, None, UnsetType] = UNSET + """fivetranConnector in which this process exists.""" + + flow_orchestrated_by: Union[RelatedFlowControlOperation, None, UnsetType] = UNSET + """Orchestrated control operation that ran these data flows (process).""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + matillion_component: Union[RelatedMatillionComponent, None, UnsetType] = UNSET + """Matillion component that contains the logic for this lineage process.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + power_bi_dataflow: Union[RelatedPowerBIDataflow, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIDataflow" + ) + """PowerBI Dataflow that is associated with this lineage process.""" + + inputs: Union[List[RelatedCatalog], None, UnsetType] = UNSET + """Assets that are inputs to this process.""" + + outputs: Union[List[RelatedCatalog], None, UnsetType] = UNSET + """Assets that are outputs from this process.""" + + column_processes: Union[List[RelatedColumnProcess], None, UnsetType] = UNSET + """Processes that detail column-level lineage for this process.""" + + process: Union[RelatedProcess, None, UnsetType] = UNSET + """Parent process that contains this column-level process.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + sql_procedures: Union[List[RelatedProcedure], None, UnsetType] = UNSET + """Procedures used by this process.""" + + sql_functions: Union[List[RelatedFunction], None, UnsetType] = UNSET + """Functions used by this process.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class ColumnProcessNested(AssetNested): + """ColumnProcess in nested API format for high-performance serialization.""" + + attributes: Union[ColumnProcessAttributes, UnsetType] = UNSET + relationship_attributes: Union[ColumnProcessRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + ColumnProcessRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + ColumnProcessRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_COLUMN_PROCESS_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "adf_activity", + "airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "fabric_activities", + "fivetran_connector", + "flow_orchestrated_by", + "meanings", + "matillion_component", + "mc_monitors", + "mc_incidents", + "power_bi_dataflow", + "inputs", + "outputs", + "column_processes", + "process", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "sql_procedures", + "sql_functions", + "schema_registry_subjects", + "soda_checks", + "spark_jobs", +] + + +def _populate_column_process_attrs( + attrs: ColumnProcessAttributes, obj: ColumnProcess +) -> None: + """Populate ColumnProcess-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.code = obj.code + attrs.sql = obj.sql + attrs.parent_connection_process_qualified_name = ( + obj.parent_connection_process_qualified_name + ) + attrs.ast = obj.ast + attrs.additional_etl_context = obj.additional_etl_context + attrs.ai_dataset_type = obj.ai_dataset_type + + +def _extract_column_process_attrs(attrs: ColumnProcessAttributes) -> dict: + """Extract all ColumnProcess attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["code"] = attrs.code + result["sql"] = attrs.sql + result["parent_connection_process_qualified_name"] = ( + attrs.parent_connection_process_qualified_name + ) + result["ast"] = attrs.ast + result["additional_etl_context"] = attrs.additional_etl_context + result["ai_dataset_type"] = attrs.ai_dataset_type + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _column_process_to_nested(column_process: ColumnProcess) -> ColumnProcessNested: + """Convert flat ColumnProcess to nested format.""" + attrs = ColumnProcessAttributes() + _populate_column_process_attrs(attrs, column_process) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + column_process, _COLUMN_PROCESS_REL_FIELDS, ColumnProcessRelationshipAttributes + ) + return ColumnProcessNested( + guid=column_process.guid, + type_name=column_process.type_name, + status=column_process.status, + version=column_process.version, + create_time=column_process.create_time, + update_time=column_process.update_time, + created_by=column_process.created_by, + updated_by=column_process.updated_by, + classifications=column_process.classifications, + classification_names=column_process.classification_names, + meanings=column_process.meanings, + labels=column_process.labels, + business_attributes=column_process.business_attributes, + custom_attributes=column_process.custom_attributes, + pending_tasks=column_process.pending_tasks, + proxy=column_process.proxy, + is_incomplete=column_process.is_incomplete, + provenance_type=column_process.provenance_type, + home_id=column_process.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _column_process_from_nested(nested: ColumnProcessNested) -> ColumnProcess: + """Convert nested format to flat ColumnProcess.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else ColumnProcessAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _COLUMN_PROCESS_REL_FIELDS, + ColumnProcessRelationshipAttributes, + ) + return ColumnProcess( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_column_process_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _column_process_to_nested_bytes( + column_process: ColumnProcess, serde: Serde +) -> bytes: + """Convert flat ColumnProcess to nested JSON bytes.""" + return serde.encode(_column_process_to_nested(column_process)) + + +def _column_process_from_nested_bytes(data: bytes, serde: Serde) -> ColumnProcess: + """Convert nested JSON bytes to flat ColumnProcess.""" + nested = serde.decode(data, ColumnProcessNested) + return _column_process_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +ColumnProcess.CODE = KeywordField("code", "code") +ColumnProcess.SQL = KeywordField("sql", "sql") +ColumnProcess.PARENT_CONNECTION_PROCESS_QUALIFIED_NAME = KeywordField( + "parentConnectionProcessQualifiedName", "parentConnectionProcessQualifiedName" +) +ColumnProcess.AST = KeywordField("ast", "ast") +ColumnProcess.ADDITIONAL_ETL_CONTEXT = KeywordField( + "additionalEtlContext", "additionalEtlContext" +) +ColumnProcess.AI_DATASET_TYPE = KeywordField("aiDatasetType", "aiDatasetType") +ColumnProcess.ADF_ACTIVITY = RelationField("adfActivity") +ColumnProcess.AIRFLOW_TASKS = RelationField("airflowTasks") +ColumnProcess.ANOMALO_CHECKS = RelationField("anomaloChecks") +ColumnProcess.APPLICATION = RelationField("application") +ColumnProcess.APPLICATION_FIELD = RelationField("applicationField") +ColumnProcess.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +ColumnProcess.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +ColumnProcess.METRICS = RelationField("metrics") +ColumnProcess.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +ColumnProcess.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +ColumnProcess.FABRIC_ACTIVITIES = RelationField("fabricActivities") +ColumnProcess.FIVETRAN_CONNECTOR = RelationField("fivetranConnector") +ColumnProcess.FLOW_ORCHESTRATED_BY = RelationField("flowOrchestratedBy") +ColumnProcess.MEANINGS = RelationField("meanings") +ColumnProcess.MATILLION_COMPONENT = RelationField("matillionComponent") +ColumnProcess.MC_MONITORS = RelationField("mcMonitors") +ColumnProcess.MC_INCIDENTS = RelationField("mcIncidents") +ColumnProcess.POWER_BI_DATAFLOW = RelationField("powerBIDataflow") +ColumnProcess.INPUTS = RelationField("inputs") +ColumnProcess.OUTPUTS = RelationField("outputs") +ColumnProcess.COLUMN_PROCESSES = RelationField("columnProcesses") +ColumnProcess.PROCESS = RelationField("process") +ColumnProcess.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +ColumnProcess.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +ColumnProcess.FILES = RelationField("files") +ColumnProcess.LINKS = RelationField("links") +ColumnProcess.README = RelationField("readme") +ColumnProcess.SQL_PROCEDURES = RelationField("sqlProcedures") +ColumnProcess.SQL_FUNCTIONS = RelationField("sqlFunctions") +ColumnProcess.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +ColumnProcess.SODA_CHECKS = RelationField("sodaChecks") +ColumnProcess.SPARK_JOBS = RelationField("sparkJobs") diff --git a/pyatlan_v9/model/assets/connection.py b/pyatlan_v9/model/assets/connection.py new file mode 100644 index 000000000..efe905518 --- /dev/null +++ b/pyatlan_v9/model/assets/connection.py @@ -0,0 +1,1084 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Connection asset model with flattened inheritance. + +This module provides: +- Connection: Flat asset class (easy to use) +- ConnectionAttributes: Nested attributes struct (extends AssetAttributes) +- ConnectionNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union +from typing import Optional + +import msgspec +from msgspec import UNSET, UnsetType + +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .process_related import RelatedConnectionProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from pyatlan.model.enums import AtlanConnectorType +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Connection(Asset): + """ + Instance of a connection to a data source in Atlan. + """ + + CATEGORY: ClassVar[Any] = None + SUB_CATEGORY: ClassVar[Any] = None + HOST: ClassVar[Any] = None + PORT: ClassVar[Any] = None + ALLOW_QUERY: ClassVar[Any] = None + ALLOW_QUERY_PREVIEW: ClassVar[Any] = None + QUERY_PREVIEW_CONFIG: ClassVar[Any] = None + CONNECTION_WORKFLOW_CONFIGURATION: ClassVar[Any] = None + QUERY_CONFIG: ClassVar[Any] = None + CREDENTIAL_STRATEGY: ClassVar[Any] = None + PREVIEW_CREDENTIAL_STRATEGY: ClassVar[Any] = None + POLICY_STRATEGY: ClassVar[Any] = None + POLICY_STRATEGY_FOR_SAMPLE_PREVIEW: ClassVar[Any] = None + QUERY_USERNAME_STRATEGY: ClassVar[Any] = None + ROW_LIMIT: ClassVar[Any] = None + QUERY_TIMEOUT: ClassVar[Any] = None + DEFAULT_CREDENTIAL_GUID: ClassVar[Any] = None + CONNECTION_DQ_CREDENTIAL_GUID: ClassVar[Any] = None + CONNECTION_IS_DQ_ENABLED: ClassVar[Any] = None + CONNECTION_DQ_ENVIRONMENT_SETUP_STATUS: ClassVar[Any] = None + CONNECTION_DQ_ENVIRONMENT_SETUP_ERROR_MESSAGE: ClassVar[Any] = None + CONNECTION_DQ_ENVIRONMENT_SETUP_STATUS_UPDATED_AT: ClassVar[Any] = None + CONNECTION_DQ_ENVIRONMENT_SOURCE_DATABASE_NAME: ClassVar[Any] = None + CONNECTOR_ICON: ClassVar[Any] = None + CONNECTOR_IMAGE: ClassVar[Any] = None + SOURCE_LOGO: ClassVar[Any] = None + IS_SAMPLE_DATA_PREVIEW_ENABLED: ClassVar[Any] = None + POPULARITY_INSIGHTS_TIMEFRAME: ClassVar[Any] = None + HAS_POPULARITY_INSIGHTS: ClassVar[Any] = None + CONNECTION_DBT_ENVIRONMENTS: ClassVar[Any] = None + CONNECTION_SSO_CREDENTIAL_GUID: ClassVar[Any] = None + USE_OBJECT_STORAGE: ClassVar[Any] = None + CONNECTION_INSIGHTS_VIA_OAUTH_COOKIE: ClassVar[Any] = None + OBJECT_STORAGE_UPLOAD_THRESHOLD: ClassVar[Any] = None + VECTOR_EMBEDDINGS_ENABLED: ClassVar[Any] = None + VECTOR_EMBEDDINGS_UPDATED_AT: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + INPUT_TO_CONNECTION_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_CONNECTION_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Connection" + + category: Union[str, None, UnsetType] = UNSET + """Type of connection, for example WAREHOUSE, RDBMS, etc.""" + + sub_category: Union[str, None, UnsetType] = UNSET + """Subcategory of this connection.""" + + host: Union[str, None, UnsetType] = UNSET + """Host name of this connection's source.""" + + port: Union[int, None, UnsetType] = UNSET + """Port number to this connection's source.""" + + allow_query: Union[bool, None, UnsetType] = UNSET + """Whether using this connection to run queries on the source is allowed (true) or not (false).""" + + allow_query_preview: Union[bool, None, UnsetType] = UNSET + """Whether using this connection to run preview queries on the source is allowed (true) or not (false).""" + + query_preview_config: Union[Dict[str, str], None, UnsetType] = UNSET + """Configuration for preview queries.""" + + connection_workflow_configuration: Union[Dict[str, str], None, UnsetType] = UNSET + """Configuration for a workflow run.""" + + query_config: Union[str, None, UnsetType] = UNSET + """Query config for this connection.""" + + credential_strategy: Union[str, None, UnsetType] = UNSET + """Credential strategy to use for this connection for queries.""" + + preview_credential_strategy: Union[str, None, UnsetType] = UNSET + """Credential strategy to use for this connection for preview queries.""" + + policy_strategy: Union[str, None, UnsetType] = UNSET + """Policy strategy is a configuration that determines whether the Atlan policy will be applied to the results of insight queries and whether the query will be rewritten, applicable for stream api call made from insight screen""" + + policy_strategy_for_sample_preview: Union[str, None, UnsetType] = UNSET + """Policy strategy is a configuration that determines whether the Atlan policy will be applied to the results of insight queries and whether the query will be rewritten. policyStrategyForSamplePreview config is applicable for sample preview call from assets screen""" + + query_username_strategy: Union[str, None, UnsetType] = UNSET + """Username strategy to use for this connection for queries.""" + + row_limit: Union[int, None, UnsetType] = UNSET + """Maximum number of rows that can be returned for the source.""" + + query_timeout: Union[int, None, UnsetType] = UNSET + """Maximum time a query should be allowed to run before timing out.""" + + default_credential_guid: Union[str, None, UnsetType] = UNSET + """Unique identifier (GUID) for the default credentials to use for this connection.""" + + connection_dq_credential_guid: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="connectionDQCredentialGuid" + ) + """Unique identifier (GUID) for the data quality credentials to use for this connection.""" + + connection_is_dq_enabled: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="connectionIsDQEnabled" + ) + """Whether data quality is enabled for this connection (true) or not (false).""" + + connection_dq_environment_setup_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="connectionDQEnvironmentSetupStatus" + ) + """Status of the data quality environment setup for this connection.""" + + connection_dq_environment_setup_error_message: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="connectionDQEnvironmentSetupErrorMessage") + ) + """Error message if data quality environment setup failed for this connection.""" + + connection_dq_environment_setup_status_updated_at: Union[int, None, UnsetType] = ( + msgspec.field(default=UNSET, name="connectionDQEnvironmentSetupStatusUpdatedAt") + ) + """Timestamp when the data quality environment setup status was last updated.""" + + connection_dq_environment_source_database_name: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="connectionDQEnvironmentSourceDatabaseName") + ) + """Name of the database in the source environment for data quality.""" + + connector_icon: Union[str, None, UnsetType] = UNSET + """Unused. Only the value of connectorType impacts icons.""" + + connector_image: Union[str, None, UnsetType] = UNSET + """Unused. Only the value of connectorType impacts icons.""" + + source_logo: Union[str, None, UnsetType] = UNSET + """Unused. Only the value of connectorType impacts icons.""" + + is_sample_data_preview_enabled: Union[bool, None, UnsetType] = UNSET + """Whether sample data can be previewed for this connection (true) or not (false).""" + + popularity_insights_timeframe: Union[int, None, UnsetType] = UNSET + """Number of days over which popularity is calculated, for example 30 days.""" + + has_popularity_insights: Union[bool, None, UnsetType] = UNSET + """Whether this connection has popularity insights (true) or not (false).""" + + connection_dbt_environments: Union[List[str], None, UnsetType] = UNSET + """""" + + connection_sso_credential_guid: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="connectionSSOCredentialGuid" + ) + """Unique identifier (GUID) for the SSO credentials to use for this connection.""" + + use_object_storage: Union[bool, None, UnsetType] = UNSET + """Whether to upload to S3, GCP, or another storage location (true) or not (false).""" + + connection_insights_via_oauth_cookie: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="connectionInsightsViaOAuthCookie" + ) + """Whether cookie based OAuth is enabled in Insights for this connection (true) or not (false).""" + + object_storage_upload_threshold: Union[int, None, UnsetType] = UNSET + """Number of rows after which results should be uploaded to storage.""" + + vector_embeddings_enabled: Union[bool, None, UnsetType] = UNSET + """""" + + vector_embeddings_updated_at: Union[int, None, UnsetType] = UNSET + """""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + input_to_connection_processes: Union[ + List[RelatedConnectionProcess], None, UnsetType + ] = UNSET + """Connection process to which this asset provides input.""" + + output_from_connection_processes: Union[ + List[RelatedConnectionProcess], None, UnsetType + ] = UNSET + """Connection processs from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Connection" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^default/[^/]+/[^/]+$") + + @classmethod + @init_guid + def creator( + cls, + *, + client: AtlanClient, + name: str, + connector_type: AtlanConnectorType, + admin_users: Optional[List[str]] = None, + admin_groups: Optional[List[str]] = None, + admin_roles: Optional[List[str]] = None, + host: Optional[str] = None, + port: Optional[int] = None, + ) -> "Connection": + """ + Create a new Connection asset. + + Args: + client: AtlanClient for cache validation + name: Simple name of the connection + connector_type: Type of connector for the connection + admin_users: List of admin usernames + admin_groups: List of admin group names + admin_roles: List of admin role GUIDs + host: Optional hostname for the connection + port: Optional port number for the connection + + Returns: + New Connection instance with all fields populated + + Raises: + ValueError: If required parameters are missing or invalid + """ + validate_required_fields( + ["client", "name", "connector_type"], [client, name, connector_type] + ) + if not admin_users and not admin_groups and not admin_roles: + raise ValueError( + "One of admin_user, admin_groups or admin_roles is required" + ) + client.user_cache.validate_names(names=admin_users or []) + client.role_cache.validate_idstrs(idstrs=admin_roles or []) + client.group_cache.validate_aliases(aliases=admin_groups or []) + + kwargs: dict = dict( + name=name, + qualified_name=connector_type.to_qualified_name(), + connector_name=connector_type.value, + category=connector_type.category.value, + admin_users=set() if admin_users is None else set(admin_users), + admin_groups=set() if admin_groups is None else set(admin_groups), + admin_roles=set() if admin_roles is None else set(admin_roles), + ) + if host is not None: + kwargs["host"] = host + if port is not None: + kwargs["port"] = port + return cls(**kwargs) + + @classmethod + @init_guid + async def creator_async( + cls, + *, + client: Any, + name: str, + connector_type: AtlanConnectorType, + admin_users: Optional[List[str]] = None, + admin_groups: Optional[List[str]] = None, + admin_roles: Optional[List[str]] = None, + host: Optional[str] = None, + port: Optional[int] = None, + ) -> "Connection": + """ + Async version of creator() for creating a new Connection asset. + + :param client: async Atlan client for cache validation + :param name: name for the connection + :param connector_type: type of connector + :param admin_users: list of admin usernames + :param admin_groups: list of admin group names + :param admin_roles: list of admin role GUIDs + :param host: optional hostname + :param port: optional port number + :returns: the new connection object + :raises ValueError: if required parameters are missing or invalid + """ + validate_required_fields( + ["client", "name", "connector_type"], [client, name, connector_type] + ) + if not admin_users and not admin_groups and not admin_roles: + raise ValueError( + "One of admin_user, admin_groups or admin_roles is required" + ) + await client.user_cache.validate_names(names=admin_users or []) + await client.role_cache.validate_idstrs(idstrs=admin_roles or []) + await client.group_cache.validate_aliases(aliases=admin_groups or []) + + kwargs: dict = dict( + name=name, + qualified_name=connector_type.to_qualified_name(), + connector_name=connector_type.value, + category=connector_type.category.value, + admin_users=set() if admin_users is None else set(admin_users), + admin_groups=set() if admin_groups is None else set(admin_groups), + admin_roles=set() if admin_roles is None else set(admin_roles), + ) + if host is not None: + kwargs["host"] = host + if port is not None: + kwargs["port"] = port + return cls(**kwargs) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "Connection": + """ + Create a Connection instance for updating an existing asset. + + Args: + qualified_name: Unique name of the connection to update + name: Simple name of the connection + + Returns: + Connection instance configured for updates + + Raises: + ValueError: If required parameters are missing + """ + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "Connection": + """ + Return a Connection with only required fields for reference. + + Returns: + Connection instance with only qualified_name and name set + """ + return Connection(qualified_name=self.qualified_name, name=self.name) + + @classmethod + def create(cls, **kwargs) -> "Connection": + """Backward compatibility alias for creator().""" + return cls.creator(**kwargs) + + @classmethod + def create_for_modification(cls, **kwargs) -> "Connection": + """Backward compatibility alias for updater().""" + return cls.updater(**kwargs) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _connection_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Connection: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Connection instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _connection_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class ConnectionAttributes(AssetAttributes): + """Connection-specific attributes for nested API format.""" + + category: Union[str, None, UnsetType] = UNSET + """Type of connection, for example WAREHOUSE, RDBMS, etc.""" + + sub_category: Union[str, None, UnsetType] = UNSET + """Subcategory of this connection.""" + + host: Union[str, None, UnsetType] = UNSET + """Host name of this connection's source.""" + + port: Union[int, None, UnsetType] = UNSET + """Port number to this connection's source.""" + + allow_query: Union[bool, None, UnsetType] = UNSET + """Whether using this connection to run queries on the source is allowed (true) or not (false).""" + + allow_query_preview: Union[bool, None, UnsetType] = UNSET + """Whether using this connection to run preview queries on the source is allowed (true) or not (false).""" + + query_preview_config: Union[Dict[str, str], None, UnsetType] = UNSET + """Configuration for preview queries.""" + + connection_workflow_configuration: Union[Dict[str, str], None, UnsetType] = UNSET + """Configuration for a workflow run.""" + + query_config: Union[str, None, UnsetType] = UNSET + """Query config for this connection.""" + + credential_strategy: Union[str, None, UnsetType] = UNSET + """Credential strategy to use for this connection for queries.""" + + preview_credential_strategy: Union[str, None, UnsetType] = UNSET + """Credential strategy to use for this connection for preview queries.""" + + policy_strategy: Union[str, None, UnsetType] = UNSET + """Policy strategy is a configuration that determines whether the Atlan policy will be applied to the results of insight queries and whether the query will be rewritten, applicable for stream api call made from insight screen""" + + policy_strategy_for_sample_preview: Union[str, None, UnsetType] = UNSET + """Policy strategy is a configuration that determines whether the Atlan policy will be applied to the results of insight queries and whether the query will be rewritten. policyStrategyForSamplePreview config is applicable for sample preview call from assets screen""" + + query_username_strategy: Union[str, None, UnsetType] = UNSET + """Username strategy to use for this connection for queries.""" + + row_limit: Union[int, None, UnsetType] = UNSET + """Maximum number of rows that can be returned for the source.""" + + query_timeout: Union[int, None, UnsetType] = UNSET + """Maximum time a query should be allowed to run before timing out.""" + + default_credential_guid: Union[str, None, UnsetType] = UNSET + """Unique identifier (GUID) for the default credentials to use for this connection.""" + + connection_dq_credential_guid: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="connectionDQCredentialGuid" + ) + """Unique identifier (GUID) for the data quality credentials to use for this connection.""" + + connection_is_dq_enabled: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="connectionIsDQEnabled" + ) + """Whether data quality is enabled for this connection (true) or not (false).""" + + connection_dq_environment_setup_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="connectionDQEnvironmentSetupStatus" + ) + """Status of the data quality environment setup for this connection.""" + + connection_dq_environment_setup_error_message: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="connectionDQEnvironmentSetupErrorMessage") + ) + """Error message if data quality environment setup failed for this connection.""" + + connection_dq_environment_setup_status_updated_at: Union[int, None, UnsetType] = ( + msgspec.field(default=UNSET, name="connectionDQEnvironmentSetupStatusUpdatedAt") + ) + """Timestamp when the data quality environment setup status was last updated.""" + + connection_dq_environment_source_database_name: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="connectionDQEnvironmentSourceDatabaseName") + ) + """Name of the database in the source environment for data quality.""" + + connector_icon: Union[str, None, UnsetType] = UNSET + """Unused. Only the value of connectorType impacts icons.""" + + connector_image: Union[str, None, UnsetType] = UNSET + """Unused. Only the value of connectorType impacts icons.""" + + source_logo: Union[str, None, UnsetType] = UNSET + """Unused. Only the value of connectorType impacts icons.""" + + is_sample_data_preview_enabled: Union[bool, None, UnsetType] = UNSET + """Whether sample data can be previewed for this connection (true) or not (false).""" + + popularity_insights_timeframe: Union[int, None, UnsetType] = UNSET + """Number of days over which popularity is calculated, for example 30 days.""" + + has_popularity_insights: Union[bool, None, UnsetType] = UNSET + """Whether this connection has popularity insights (true) or not (false).""" + + connection_dbt_environments: Union[List[str], None, UnsetType] = UNSET + """""" + + connection_sso_credential_guid: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="connectionSSOCredentialGuid" + ) + """Unique identifier (GUID) for the SSO credentials to use for this connection.""" + + use_object_storage: Union[bool, None, UnsetType] = UNSET + """Whether to upload to S3, GCP, or another storage location (true) or not (false).""" + + connection_insights_via_oauth_cookie: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="connectionInsightsViaOAuthCookie" + ) + """Whether cookie based OAuth is enabled in Insights for this connection (true) or not (false).""" + + object_storage_upload_threshold: Union[int, None, UnsetType] = UNSET + """Number of rows after which results should be uploaded to storage.""" + + vector_embeddings_enabled: Union[bool, None, UnsetType] = UNSET + """""" + + vector_embeddings_updated_at: Union[int, None, UnsetType] = UNSET + """""" + + +class ConnectionRelationshipAttributes(AssetRelationshipAttributes): + """Connection-specific relationship attributes for nested API format.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + input_to_connection_processes: Union[ + List[RelatedConnectionProcess], None, UnsetType + ] = UNSET + """Connection process to which this asset provides input.""" + + output_from_connection_processes: Union[ + List[RelatedConnectionProcess], None, UnsetType + ] = UNSET + """Connection processs from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + +class ConnectionNested(AssetNested): + """Connection in nested API format for high-performance serialization.""" + + attributes: Union[ConnectionAttributes, UnsetType] = UNSET + relationship_attributes: Union[ConnectionRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + ConnectionRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + ConnectionRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_CONNECTION_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "input_to_connection_processes", + "output_from_connection_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", +] + + +def _populate_connection_attrs(attrs: ConnectionAttributes, obj: Connection) -> None: + """Populate Connection-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.category = obj.category + attrs.sub_category = obj.sub_category + attrs.host = obj.host + attrs.port = obj.port + attrs.allow_query = obj.allow_query + attrs.allow_query_preview = obj.allow_query_preview + attrs.query_preview_config = obj.query_preview_config + attrs.connection_workflow_configuration = obj.connection_workflow_configuration + attrs.query_config = obj.query_config + attrs.credential_strategy = obj.credential_strategy + attrs.preview_credential_strategy = obj.preview_credential_strategy + attrs.policy_strategy = obj.policy_strategy + attrs.policy_strategy_for_sample_preview = obj.policy_strategy_for_sample_preview + attrs.query_username_strategy = obj.query_username_strategy + attrs.row_limit = obj.row_limit + attrs.query_timeout = obj.query_timeout + attrs.default_credential_guid = obj.default_credential_guid + attrs.connection_dq_credential_guid = obj.connection_dq_credential_guid + attrs.connection_is_dq_enabled = obj.connection_is_dq_enabled + attrs.connection_dq_environment_setup_status = ( + obj.connection_dq_environment_setup_status + ) + attrs.connection_dq_environment_setup_error_message = ( + obj.connection_dq_environment_setup_error_message + ) + attrs.connection_dq_environment_setup_status_updated_at = ( + obj.connection_dq_environment_setup_status_updated_at + ) + attrs.connection_dq_environment_source_database_name = ( + obj.connection_dq_environment_source_database_name + ) + attrs.connector_icon = obj.connector_icon + attrs.connector_image = obj.connector_image + attrs.source_logo = obj.source_logo + attrs.is_sample_data_preview_enabled = obj.is_sample_data_preview_enabled + attrs.popularity_insights_timeframe = obj.popularity_insights_timeframe + attrs.has_popularity_insights = obj.has_popularity_insights + attrs.connection_dbt_environments = obj.connection_dbt_environments + attrs.connection_sso_credential_guid = obj.connection_sso_credential_guid + attrs.use_object_storage = obj.use_object_storage + attrs.connection_insights_via_oauth_cookie = ( + obj.connection_insights_via_oauth_cookie + ) + attrs.object_storage_upload_threshold = obj.object_storage_upload_threshold + attrs.vector_embeddings_enabled = obj.vector_embeddings_enabled + attrs.vector_embeddings_updated_at = obj.vector_embeddings_updated_at + + +def _extract_connection_attrs(attrs: ConnectionAttributes) -> dict: + """Extract all Connection attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["category"] = attrs.category + result["sub_category"] = attrs.sub_category + result["host"] = attrs.host + result["port"] = attrs.port + result["allow_query"] = attrs.allow_query + result["allow_query_preview"] = attrs.allow_query_preview + result["query_preview_config"] = attrs.query_preview_config + result["connection_workflow_configuration"] = ( + attrs.connection_workflow_configuration + ) + result["query_config"] = attrs.query_config + result["credential_strategy"] = attrs.credential_strategy + result["preview_credential_strategy"] = attrs.preview_credential_strategy + result["policy_strategy"] = attrs.policy_strategy + result["policy_strategy_for_sample_preview"] = ( + attrs.policy_strategy_for_sample_preview + ) + result["query_username_strategy"] = attrs.query_username_strategy + result["row_limit"] = attrs.row_limit + result["query_timeout"] = attrs.query_timeout + result["default_credential_guid"] = attrs.default_credential_guid + result["connection_dq_credential_guid"] = attrs.connection_dq_credential_guid + result["connection_is_dq_enabled"] = attrs.connection_is_dq_enabled + result["connection_dq_environment_setup_status"] = ( + attrs.connection_dq_environment_setup_status + ) + result["connection_dq_environment_setup_error_message"] = ( + attrs.connection_dq_environment_setup_error_message + ) + result["connection_dq_environment_setup_status_updated_at"] = ( + attrs.connection_dq_environment_setup_status_updated_at + ) + result["connection_dq_environment_source_database_name"] = ( + attrs.connection_dq_environment_source_database_name + ) + result["connector_icon"] = attrs.connector_icon + result["connector_image"] = attrs.connector_image + result["source_logo"] = attrs.source_logo + result["is_sample_data_preview_enabled"] = attrs.is_sample_data_preview_enabled + result["popularity_insights_timeframe"] = attrs.popularity_insights_timeframe + result["has_popularity_insights"] = attrs.has_popularity_insights + result["connection_dbt_environments"] = attrs.connection_dbt_environments + result["connection_sso_credential_guid"] = attrs.connection_sso_credential_guid + result["use_object_storage"] = attrs.use_object_storage + result["connection_insights_via_oauth_cookie"] = ( + attrs.connection_insights_via_oauth_cookie + ) + result["object_storage_upload_threshold"] = attrs.object_storage_upload_threshold + result["vector_embeddings_enabled"] = attrs.vector_embeddings_enabled + result["vector_embeddings_updated_at"] = attrs.vector_embeddings_updated_at + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _connection_to_nested(connection: Connection) -> ConnectionNested: + """Convert flat Connection to nested format.""" + attrs = ConnectionAttributes() + _populate_connection_attrs(attrs, connection) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + connection, _CONNECTION_REL_FIELDS, ConnectionRelationshipAttributes + ) + return ConnectionNested( + guid=connection.guid, + type_name=connection.type_name, + status=connection.status, + version=connection.version, + create_time=connection.create_time, + update_time=connection.update_time, + created_by=connection.created_by, + updated_by=connection.updated_by, + classifications=connection.classifications, + classification_names=connection.classification_names, + meanings=connection.meanings, + labels=connection.labels, + business_attributes=connection.business_attributes, + custom_attributes=connection.custom_attributes, + pending_tasks=connection.pending_tasks, + proxy=connection.proxy, + is_incomplete=connection.is_incomplete, + provenance_type=connection.provenance_type, + home_id=connection.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _connection_from_nested(nested: ConnectionNested) -> Connection: + """Convert nested format to flat Connection.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else ConnectionAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _CONNECTION_REL_FIELDS, + ConnectionRelationshipAttributes, + ) + return Connection( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_connection_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _connection_to_nested_bytes(connection: Connection, serde: Serde) -> bytes: + """Convert flat Connection to nested JSON bytes.""" + return serde.encode(_connection_to_nested(connection)) + + +def _connection_from_nested_bytes(data: bytes, serde: Serde) -> Connection: + """Convert nested JSON bytes to flat Connection.""" + nested = serde.decode(data, ConnectionNested) + return _connection_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, + TextField, +) + +Connection.CATEGORY = KeywordField("category", "category") +Connection.SUB_CATEGORY = KeywordField("subCategory", "subCategory") +Connection.HOST = KeywordField("host", "host") +Connection.PORT = NumericField("port", "port") +Connection.ALLOW_QUERY = BooleanField("allowQuery", "allowQuery") +Connection.ALLOW_QUERY_PREVIEW = BooleanField("allowQueryPreview", "allowQueryPreview") +Connection.QUERY_PREVIEW_CONFIG = KeywordField( + "queryPreviewConfig", "queryPreviewConfig" +) +Connection.CONNECTION_WORKFLOW_CONFIGURATION = KeywordField( + "connectionWorkflowConfiguration", "connectionWorkflowConfiguration" +) +Connection.QUERY_CONFIG = KeywordField("queryConfig", "queryConfig") +Connection.CREDENTIAL_STRATEGY = KeywordField( + "credentialStrategy", "credentialStrategy" +) +Connection.PREVIEW_CREDENTIAL_STRATEGY = KeywordField( + "previewCredentialStrategy", "previewCredentialStrategy" +) +Connection.POLICY_STRATEGY = KeywordField("policyStrategy", "policyStrategy") +Connection.POLICY_STRATEGY_FOR_SAMPLE_PREVIEW = KeywordField( + "policyStrategyForSamplePreview", "policyStrategyForSamplePreview" +) +Connection.QUERY_USERNAME_STRATEGY = KeywordField( + "queryUsernameStrategy", "queryUsernameStrategy" +) +Connection.ROW_LIMIT = NumericField("rowLimit", "rowLimit") +Connection.QUERY_TIMEOUT = NumericField("queryTimeout", "queryTimeout") +Connection.DEFAULT_CREDENTIAL_GUID = KeywordField( + "defaultCredentialGuid", "defaultCredentialGuid" +) +Connection.CONNECTION_DQ_CREDENTIAL_GUID = KeywordField( + "connectionDQCredentialGuid", "connectionDQCredentialGuid" +) +Connection.CONNECTION_IS_DQ_ENABLED = BooleanField( + "connectionIsDQEnabled", "connectionIsDQEnabled" +) +Connection.CONNECTION_DQ_ENVIRONMENT_SETUP_STATUS = KeywordField( + "connectionDQEnvironmentSetupStatus", "connectionDQEnvironmentSetupStatus" +) +Connection.CONNECTION_DQ_ENVIRONMENT_SETUP_ERROR_MESSAGE = TextField( + "connectionDQEnvironmentSetupErrorMessage", + "connectionDQEnvironmentSetupErrorMessage", +) +Connection.CONNECTION_DQ_ENVIRONMENT_SETUP_STATUS_UPDATED_AT = NumericField( + "connectionDQEnvironmentSetupStatusUpdatedAt", + "connectionDQEnvironmentSetupStatusUpdatedAt", +) +Connection.CONNECTION_DQ_ENVIRONMENT_SOURCE_DATABASE_NAME = KeywordField( + "connectionDQEnvironmentSourceDatabaseName", + "connectionDQEnvironmentSourceDatabaseName", +) +Connection.CONNECTOR_ICON = KeywordField("connectorIcon", "connectorIcon") +Connection.CONNECTOR_IMAGE = KeywordField("connectorImage", "connectorImage") +Connection.SOURCE_LOGO = KeywordField("sourceLogo", "sourceLogo") +Connection.IS_SAMPLE_DATA_PREVIEW_ENABLED = BooleanField( + "isSampleDataPreviewEnabled", "isSampleDataPreviewEnabled" +) +Connection.POPULARITY_INSIGHTS_TIMEFRAME = NumericField( + "popularityInsightsTimeframe", "popularityInsightsTimeframe" +) +Connection.HAS_POPULARITY_INSIGHTS = BooleanField( + "hasPopularityInsights", "hasPopularityInsights" +) +Connection.CONNECTION_DBT_ENVIRONMENTS = KeywordField( + "connectionDbtEnvironments", "connectionDbtEnvironments" +) +Connection.CONNECTION_SSO_CREDENTIAL_GUID = KeywordField( + "connectionSSOCredentialGuid", "connectionSSOCredentialGuid" +) +Connection.USE_OBJECT_STORAGE = BooleanField("useObjectStorage", "useObjectStorage") +Connection.CONNECTION_INSIGHTS_VIA_OAUTH_COOKIE = BooleanField( + "connectionInsightsViaOAuthCookie", "connectionInsightsViaOAuthCookie" +) +Connection.OBJECT_STORAGE_UPLOAD_THRESHOLD = NumericField( + "objectStorageUploadThreshold", "objectStorageUploadThreshold" +) +Connection.VECTOR_EMBEDDINGS_ENABLED = BooleanField( + "vectorEmbeddingsEnabled", "vectorEmbeddingsEnabled" +) +Connection.VECTOR_EMBEDDINGS_UPDATED_AT = NumericField( + "vectorEmbeddingsUpdatedAt", "vectorEmbeddingsUpdatedAt" +) +Connection.ANOMALO_CHECKS = RelationField("anomaloChecks") +Connection.APPLICATION = RelationField("application") +Connection.APPLICATION_FIELD = RelationField("applicationField") +Connection.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Connection.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Connection.METRICS = RelationField("metrics") +Connection.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Connection.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Connection.MEANINGS = RelationField("meanings") +Connection.MC_MONITORS = RelationField("mcMonitors") +Connection.MC_INCIDENTS = RelationField("mcIncidents") +Connection.INPUT_TO_CONNECTION_PROCESSES = RelationField("inputToConnectionProcesses") +Connection.OUTPUT_FROM_CONNECTION_PROCESSES = RelationField( + "outputFromConnectionProcesses" +) +Connection.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Connection.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Connection.FILES = RelationField("files") +Connection.LINKS = RelationField("links") +Connection.README = RelationField("readme") +Connection.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Connection.SODA_CHECKS = RelationField("sodaChecks") diff --git a/pyatlan_v9/model/assets/connection_related.py b/pyatlan_v9/model/assets/connection_related.py new file mode 100644 index 000000000..f46574ea8 --- /dev/null +++ b/pyatlan_v9/model/assets/connection_related.py @@ -0,0 +1,163 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Connection module. + +This module contains all Related{Type} classes for the Connection type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .asset_related import RelatedAsset +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedConnection", +] + + +class RelatedConnection(RelatedAsset): + """ + Related entity reference for Connection assets. + + Extends RelatedAsset with Connection-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Connection" so it serializes correctly + + category: Union[str, None, UnsetType] = UNSET + """Type of connection, for example WAREHOUSE, RDBMS, etc.""" + + sub_category: Union[str, None, UnsetType] = UNSET + """Subcategory of this connection.""" + + host: Union[str, None, UnsetType] = UNSET + """Host name of this connection's source.""" + + port: Union[int, None, UnsetType] = UNSET + """Port number to this connection's source.""" + + allow_query: Union[bool, None, UnsetType] = UNSET + """Whether using this connection to run queries on the source is allowed (true) or not (false).""" + + allow_query_preview: Union[bool, None, UnsetType] = UNSET + """Whether using this connection to run preview queries on the source is allowed (true) or not (false).""" + + query_preview_config: Union[Dict[str, str], None, UnsetType] = UNSET + """Configuration for preview queries.""" + + connection_workflow_configuration: Union[Dict[str, str], None, UnsetType] = UNSET + """Configuration for a workflow run.""" + + query_config: Union[str, None, UnsetType] = UNSET + """Query config for this connection.""" + + credential_strategy: Union[str, None, UnsetType] = UNSET + """Credential strategy to use for this connection for queries.""" + + preview_credential_strategy: Union[str, None, UnsetType] = UNSET + """Credential strategy to use for this connection for preview queries.""" + + policy_strategy: Union[str, None, UnsetType] = UNSET + """Policy strategy is a configuration that determines whether the Atlan policy will be applied to the results of insight queries and whether the query will be rewritten, applicable for stream api call made from insight screen""" + + policy_strategy_for_sample_preview: Union[str, None, UnsetType] = UNSET + """Policy strategy is a configuration that determines whether the Atlan policy will be applied to the results of insight queries and whether the query will be rewritten. policyStrategyForSamplePreview config is applicable for sample preview call from assets screen""" + + query_username_strategy: Union[str, None, UnsetType] = UNSET + """Username strategy to use for this connection for queries.""" + + row_limit: Union[int, None, UnsetType] = UNSET + """Maximum number of rows that can be returned for the source.""" + + query_timeout: Union[int, None, UnsetType] = UNSET + """Maximum time a query should be allowed to run before timing out.""" + + default_credential_guid: Union[str, None, UnsetType] = UNSET + """Unique identifier (GUID) for the default credentials to use for this connection.""" + + connection_dq_credential_guid: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="connectionDQCredentialGuid" + ) + """Unique identifier (GUID) for the data quality credentials to use for this connection.""" + + connection_is_dq_enabled: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="connectionIsDQEnabled" + ) + """Whether data quality is enabled for this connection (true) or not (false).""" + + connection_dq_environment_setup_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="connectionDQEnvironmentSetupStatus" + ) + """Status of the data quality environment setup for this connection.""" + + connection_dq_environment_setup_error_message: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="connectionDQEnvironmentSetupErrorMessage") + ) + """Error message if data quality environment setup failed for this connection.""" + + connection_dq_environment_setup_status_updated_at: Union[int, None, UnsetType] = ( + msgspec.field(default=UNSET, name="connectionDQEnvironmentSetupStatusUpdatedAt") + ) + """Timestamp when the data quality environment setup status was last updated.""" + + connection_dq_environment_source_database_name: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="connectionDQEnvironmentSourceDatabaseName") + ) + """Name of the database in the source environment for data quality.""" + + connector_icon: Union[str, None, UnsetType] = UNSET + """Unused. Only the value of connectorType impacts icons.""" + + connector_image: Union[str, None, UnsetType] = UNSET + """Unused. Only the value of connectorType impacts icons.""" + + source_logo: Union[str, None, UnsetType] = UNSET + """Unused. Only the value of connectorType impacts icons.""" + + is_sample_data_preview_enabled: Union[bool, None, UnsetType] = UNSET + """Whether sample data can be previewed for this connection (true) or not (false).""" + + popularity_insights_timeframe: Union[int, None, UnsetType] = UNSET + """Number of days over which popularity is calculated, for example 30 days.""" + + has_popularity_insights: Union[bool, None, UnsetType] = UNSET + """Whether this connection has popularity insights (true) or not (false).""" + + connection_dbt_environments: Union[List[str], None, UnsetType] = UNSET + """""" + + connection_sso_credential_guid: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="connectionSSOCredentialGuid" + ) + """Unique identifier (GUID) for the SSO credentials to use for this connection.""" + + use_object_storage: Union[bool, None, UnsetType] = UNSET + """Whether to upload to S3, GCP, or another storage location (true) or not (false).""" + + connection_insights_via_oauth_cookie: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="connectionInsightsViaOAuthCookie" + ) + """Whether cookie based OAuth is enabled in Insights for this connection (true) or not (false).""" + + object_storage_upload_threshold: Union[int, None, UnsetType] = UNSET + """Number of rows after which results should be uploaded to storage.""" + + vector_embeddings_enabled: Union[bool, None, UnsetType] = UNSET + """""" + + vector_embeddings_updated_at: Union[int, None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Connection" diff --git a/pyatlan_v9/model/assets/cosmos_mongo_db.py b/pyatlan_v9/model/assets/cosmos_mongo_db.py new file mode 100644 index 000000000..194bda7e9 --- /dev/null +++ b/pyatlan_v9/model/assets/cosmos_mongo_db.py @@ -0,0 +1,554 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +CosmosMongoDB asset model with flattened inheritance. + +This module provides: +- CosmosMongoDB: Flat asset class (easy to use) +- CosmosMongoDBAttributes: Nested attributes struct (extends AssetAttributes) +- CosmosMongoDBNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class CosmosMongoDB(Asset): + """ + Base class for Cosmos MongoDB assets. + """ + + NO_SQL_SCHEMA_DEFINITION: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "CosmosMongoDB" + + no_sql_schema_definition: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="noSQLSchemaDefinition" + ) + """Represents attributes for describing the key schema for the table and indexes.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "CosmosMongoDB" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _cosmos_mongo_db_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> CosmosMongoDB: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + CosmosMongoDB instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _cosmos_mongo_db_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class CosmosMongoDBAttributes(AssetAttributes): + """CosmosMongoDB-specific attributes for nested API format.""" + + no_sql_schema_definition: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="noSQLSchemaDefinition" + ) + """Represents attributes for describing the key schema for the table and indexes.""" + + +class CosmosMongoDBRelationshipAttributes(AssetRelationshipAttributes): + """CosmosMongoDB-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class CosmosMongoDBNested(AssetNested): + """CosmosMongoDB in nested API format for high-performance serialization.""" + + attributes: Union[CosmosMongoDBAttributes, UnsetType] = UNSET + relationship_attributes: Union[CosmosMongoDBRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + CosmosMongoDBRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + CosmosMongoDBRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_COSMOS_MONGO_DB_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_cosmos_mongo_db_attrs( + attrs: CosmosMongoDBAttributes, obj: CosmosMongoDB +) -> None: + """Populate CosmosMongoDB-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.no_sql_schema_definition = obj.no_sql_schema_definition + + +def _extract_cosmos_mongo_db_attrs(attrs: CosmosMongoDBAttributes) -> dict: + """Extract all CosmosMongoDB attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["no_sql_schema_definition"] = attrs.no_sql_schema_definition + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _cosmos_mongo_db_to_nested(cosmos_mongo_db: CosmosMongoDB) -> CosmosMongoDBNested: + """Convert flat CosmosMongoDB to nested format.""" + attrs = CosmosMongoDBAttributes() + _populate_cosmos_mongo_db_attrs(attrs, cosmos_mongo_db) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + cosmos_mongo_db, + _COSMOS_MONGO_DB_REL_FIELDS, + CosmosMongoDBRelationshipAttributes, + ) + return CosmosMongoDBNested( + guid=cosmos_mongo_db.guid, + type_name=cosmos_mongo_db.type_name, + status=cosmos_mongo_db.status, + version=cosmos_mongo_db.version, + create_time=cosmos_mongo_db.create_time, + update_time=cosmos_mongo_db.update_time, + created_by=cosmos_mongo_db.created_by, + updated_by=cosmos_mongo_db.updated_by, + classifications=cosmos_mongo_db.classifications, + classification_names=cosmos_mongo_db.classification_names, + meanings=cosmos_mongo_db.meanings, + labels=cosmos_mongo_db.labels, + business_attributes=cosmos_mongo_db.business_attributes, + custom_attributes=cosmos_mongo_db.custom_attributes, + pending_tasks=cosmos_mongo_db.pending_tasks, + proxy=cosmos_mongo_db.proxy, + is_incomplete=cosmos_mongo_db.is_incomplete, + provenance_type=cosmos_mongo_db.provenance_type, + home_id=cosmos_mongo_db.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _cosmos_mongo_db_from_nested(nested: CosmosMongoDBNested) -> CosmosMongoDB: + """Convert nested format to flat CosmosMongoDB.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else CosmosMongoDBAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _COSMOS_MONGO_DB_REL_FIELDS, + CosmosMongoDBRelationshipAttributes, + ) + return CosmosMongoDB( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_cosmos_mongo_db_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _cosmos_mongo_db_to_nested_bytes( + cosmos_mongo_db: CosmosMongoDB, serde: Serde +) -> bytes: + """Convert flat CosmosMongoDB to nested JSON bytes.""" + return serde.encode(_cosmos_mongo_db_to_nested(cosmos_mongo_db)) + + +def _cosmos_mongo_db_from_nested_bytes(data: bytes, serde: Serde) -> CosmosMongoDB: + """Convert nested JSON bytes to flat CosmosMongoDB.""" + nested = serde.decode(data, CosmosMongoDBNested) + return _cosmos_mongo_db_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +CosmosMongoDB.NO_SQL_SCHEMA_DEFINITION = KeywordField( + "noSQLSchemaDefinition", "noSQLSchemaDefinition" +) +CosmosMongoDB.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +CosmosMongoDB.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +CosmosMongoDB.ANOMALO_CHECKS = RelationField("anomaloChecks") +CosmosMongoDB.APPLICATION = RelationField("application") +CosmosMongoDB.APPLICATION_FIELD = RelationField("applicationField") +CosmosMongoDB.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +CosmosMongoDB.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +CosmosMongoDB.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +CosmosMongoDB.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +CosmosMongoDB.METRICS = RelationField("metrics") +CosmosMongoDB.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +CosmosMongoDB.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +CosmosMongoDB.MEANINGS = RelationField("meanings") +CosmosMongoDB.MC_MONITORS = RelationField("mcMonitors") +CosmosMongoDB.MC_INCIDENTS = RelationField("mcIncidents") +CosmosMongoDB.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +CosmosMongoDB.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +CosmosMongoDB.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +CosmosMongoDB.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +CosmosMongoDB.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +CosmosMongoDB.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +CosmosMongoDB.FILES = RelationField("files") +CosmosMongoDB.LINKS = RelationField("links") +CosmosMongoDB.README = RelationField("readme") +CosmosMongoDB.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +CosmosMongoDB.SODA_CHECKS = RelationField("sodaChecks") +CosmosMongoDB.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +CosmosMongoDB.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/cosmos_mongo_db_account.py b/pyatlan_v9/model/assets/cosmos_mongo_db_account.py new file mode 100644 index 000000000..4aea19574 --- /dev/null +++ b/pyatlan_v9/model/assets/cosmos_mongo_db_account.py @@ -0,0 +1,919 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +CosmosMongoDBAccount asset model with flattened inheritance. + +This module provides: +- CosmosMongoDBAccount: Flat asset class (easy to use) +- CosmosMongoDBAccountAttributes: Nested attributes struct (extends AssetAttributes) +- CosmosMongoDBAccountNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .cosmos_mongo_db_related import RelatedCosmosMongoDBDatabase + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class CosmosMongoDBAccount(Asset): + """ + Instance of a Cosmos MongoDB account in Atlan. + """ + + COSMOS_MONGO_DB_ACCOUNT_INSTANCE_ID: ClassVar[Any] = None + COSMOS_MONGO_DB_DATABASE_COUNT: ClassVar[Any] = None + COSMOS_MONGO_DB_ACCOUNT_TYPE: ClassVar[Any] = None + COSMOS_MONGO_DB_ACCOUNT_SUBSCRIPTION_ID: ClassVar[Any] = None + COSMOS_MONGO_DB_ACCOUNT_RESOURCE_GROUP: ClassVar[Any] = None + COSMOS_MONGO_DB_ACCOUNT_DOCUMENT_ENDPOINT: ClassVar[Any] = None + COSMOS_MONGO_DB_ACCOUNT_MONGO_ENDPOINT: ClassVar[Any] = None + COSMOS_MONGO_DB_ACCOUNT_PUBLIC_NETWORK_ACCESS: ClassVar[Any] = None + COSMOS_MONGO_DB_ACCOUNT_ENABLE_AUTOMATIC_FAILOVER: ClassVar[Any] = None + COSMOS_MONGO_DB_ACCOUNT_ENABLE_MULTIPLE_WRITE_LOCATIONS: ClassVar[Any] = None + COSMOS_MONGO_DB_ACCOUNT_ENABLE_PARTITION_KEY_MONITOR: ClassVar[Any] = None + COSMOS_MONGO_DB_ACCOUNT_IS_VIRTUAL_NETWORK_FILTER_ENABLED: ClassVar[Any] = None + COSMOS_MONGO_DB_ACCOUNT_CONSISTENCY_POLICY: ClassVar[Any] = None + COSMOS_MONGO_DB_ACCOUNT_LOCATIONS: ClassVar[Any] = None + COSMOS_MONGO_DB_ACCOUNT_READ_LOCATIONS: ClassVar[Any] = None + COSMOS_MONGO_DB_ACCOUNT_WRITE_LOCATIONS: ClassVar[Any] = None + NO_SQL_SCHEMA_DEFINITION: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + COSMOS_MONGO_DB_DATABASES: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "CosmosMongoDBAccount" + + cosmos_mongo_db_account_instance_id: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="cosmosMongoDBAccountInstanceId" + ) + """The unique identifier for the Cosmos MongoDB account.""" + + cosmos_mongo_db_database_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="cosmosMongoDBDatabaseCount" + ) + """Number of databases in this Cosmos MongoDB account.""" + + cosmos_mongo_db_account_type: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="cosmosMongoDBAccountType" + ) + """The type of the Cosmos MongoDB account, such as RU or VCORE.""" + + cosmos_mongo_db_account_subscription_id: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="cosmosMongoDBAccountSubscriptionId") + ) + """The ID of the subscription to which the Cosmos MongoDB account belongs.""" + + cosmos_mongo_db_account_resource_group: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="cosmosMongoDBAccountResourceGroup" + ) + """The resource group that contains the Cosmos MongoDB account.""" + + cosmos_mongo_db_account_document_endpoint: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="cosmosMongoDBAccountDocumentEndpoint") + ) + """The Document Endpoint URL for the Cosmos MongoDB account.""" + + cosmos_mongo_db_account_mongo_endpoint: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="cosmosMongoDBAccountMongoEndpoint" + ) + """The MongoDB connection endpoint for the Cosmos MongoDB account.""" + + cosmos_mongo_db_account_public_network_access: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="cosmosMongoDBAccountPublicNetworkAccess") + ) + """The status of public network access for the Cosmos MongoDB account.""" + + cosmos_mongo_db_account_enable_automatic_failover: Union[bool, None, UnsetType] = ( + msgspec.field(default=UNSET, name="cosmosMongoDBAccountEnableAutomaticFailover") + ) + """Indicates whether automatic failover is enabled for the Cosmos MongoDB account.""" + + cosmos_mongo_db_account_enable_multiple_write_locations: Union[ + bool, None, UnsetType + ] = msgspec.field( + default=UNSET, name="cosmosMongoDBAccountEnableMultipleWriteLocations" + ) + """Indicates whether multiple write locations are enabled for the Cosmos MongoDB account.""" + + cosmos_mongo_db_account_enable_partition_key_monitor: Union[ + bool, None, UnsetType + ] = msgspec.field( + default=UNSET, name="cosmosMongoDBAccountEnablePartitionKeyMonitor" + ) + """Indicates whether partition key monitoring is enabled for the Cosmos MongoDB account.""" + + cosmos_mongo_db_account_is_virtual_network_filter_enabled: Union[ + bool, None, UnsetType + ] = msgspec.field( + default=UNSET, name="cosmosMongoDBAccountIsVirtualNetworkFilterEnabled" + ) + """Indicates whether the virtual network filter is enabled for the Cosmos MongoDB account.""" + + cosmos_mongo_db_account_consistency_policy: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="cosmosMongoDBAccountConsistencyPolicy") + ) + """The consistency policy configured for the Cosmos MongoDB account.""" + + cosmos_mongo_db_account_locations: Union[List[str], None, UnsetType] = ( + msgspec.field(default=UNSET, name="cosmosMongoDBAccountLocations") + ) + """The locations where the Cosmos MongoDB account is available.""" + + cosmos_mongo_db_account_read_locations: Union[List[str], None, UnsetType] = ( + msgspec.field(default=UNSET, name="cosmosMongoDBAccountReadLocations") + ) + """The read locations configured for the Cosmos MongoDB account.""" + + cosmos_mongo_db_account_write_locations: Union[List[str], None, UnsetType] = ( + msgspec.field(default=UNSET, name="cosmosMongoDBAccountWriteLocations") + ) + """The write locations configured for the Cosmos MongoDB account.""" + + no_sql_schema_definition: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="noSQLSchemaDefinition" + ) + """Represents attributes for describing the key schema for the table and indexes.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cosmos_mongo_db_databases: Union[ + List[RelatedCosmosMongoDBDatabase], None, UnsetType + ] = msgspec.field(default=UNSET, name="cosmosMongoDBDatabases") + """Databases that exist within this account.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "CosmosMongoDBAccount" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _cosmos_mongo_db_account_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> CosmosMongoDBAccount: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + CosmosMongoDBAccount instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _cosmos_mongo_db_account_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class CosmosMongoDBAccountAttributes(AssetAttributes): + """CosmosMongoDBAccount-specific attributes for nested API format.""" + + cosmos_mongo_db_account_instance_id: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="cosmosMongoDBAccountInstanceId" + ) + """The unique identifier for the Cosmos MongoDB account.""" + + cosmos_mongo_db_database_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="cosmosMongoDBDatabaseCount" + ) + """Number of databases in this Cosmos MongoDB account.""" + + cosmos_mongo_db_account_type: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="cosmosMongoDBAccountType" + ) + """The type of the Cosmos MongoDB account, such as RU or VCORE.""" + + cosmos_mongo_db_account_subscription_id: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="cosmosMongoDBAccountSubscriptionId") + ) + """The ID of the subscription to which the Cosmos MongoDB account belongs.""" + + cosmos_mongo_db_account_resource_group: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="cosmosMongoDBAccountResourceGroup" + ) + """The resource group that contains the Cosmos MongoDB account.""" + + cosmos_mongo_db_account_document_endpoint: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="cosmosMongoDBAccountDocumentEndpoint") + ) + """The Document Endpoint URL for the Cosmos MongoDB account.""" + + cosmos_mongo_db_account_mongo_endpoint: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="cosmosMongoDBAccountMongoEndpoint" + ) + """The MongoDB connection endpoint for the Cosmos MongoDB account.""" + + cosmos_mongo_db_account_public_network_access: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="cosmosMongoDBAccountPublicNetworkAccess") + ) + """The status of public network access for the Cosmos MongoDB account.""" + + cosmos_mongo_db_account_enable_automatic_failover: Union[bool, None, UnsetType] = ( + msgspec.field(default=UNSET, name="cosmosMongoDBAccountEnableAutomaticFailover") + ) + """Indicates whether automatic failover is enabled for the Cosmos MongoDB account.""" + + cosmos_mongo_db_account_enable_multiple_write_locations: Union[ + bool, None, UnsetType + ] = msgspec.field( + default=UNSET, name="cosmosMongoDBAccountEnableMultipleWriteLocations" + ) + """Indicates whether multiple write locations are enabled for the Cosmos MongoDB account.""" + + cosmos_mongo_db_account_enable_partition_key_monitor: Union[ + bool, None, UnsetType + ] = msgspec.field( + default=UNSET, name="cosmosMongoDBAccountEnablePartitionKeyMonitor" + ) + """Indicates whether partition key monitoring is enabled for the Cosmos MongoDB account.""" + + cosmos_mongo_db_account_is_virtual_network_filter_enabled: Union[ + bool, None, UnsetType + ] = msgspec.field( + default=UNSET, name="cosmosMongoDBAccountIsVirtualNetworkFilterEnabled" + ) + """Indicates whether the virtual network filter is enabled for the Cosmos MongoDB account.""" + + cosmos_mongo_db_account_consistency_policy: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="cosmosMongoDBAccountConsistencyPolicy") + ) + """The consistency policy configured for the Cosmos MongoDB account.""" + + cosmos_mongo_db_account_locations: Union[List[str], None, UnsetType] = ( + msgspec.field(default=UNSET, name="cosmosMongoDBAccountLocations") + ) + """The locations where the Cosmos MongoDB account is available.""" + + cosmos_mongo_db_account_read_locations: Union[List[str], None, UnsetType] = ( + msgspec.field(default=UNSET, name="cosmosMongoDBAccountReadLocations") + ) + """The read locations configured for the Cosmos MongoDB account.""" + + cosmos_mongo_db_account_write_locations: Union[List[str], None, UnsetType] = ( + msgspec.field(default=UNSET, name="cosmosMongoDBAccountWriteLocations") + ) + """The write locations configured for the Cosmos MongoDB account.""" + + no_sql_schema_definition: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="noSQLSchemaDefinition" + ) + """Represents attributes for describing the key schema for the table and indexes.""" + + +class CosmosMongoDBAccountRelationshipAttributes(AssetRelationshipAttributes): + """CosmosMongoDBAccount-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cosmos_mongo_db_databases: Union[ + List[RelatedCosmosMongoDBDatabase], None, UnsetType + ] = msgspec.field(default=UNSET, name="cosmosMongoDBDatabases") + """Databases that exist within this account.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class CosmosMongoDBAccountNested(AssetNested): + """CosmosMongoDBAccount in nested API format for high-performance serialization.""" + + attributes: Union[CosmosMongoDBAccountAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + CosmosMongoDBAccountRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + CosmosMongoDBAccountRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + CosmosMongoDBAccountRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_COSMOS_MONGO_DB_ACCOUNT_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "cosmos_mongo_db_databases", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_cosmos_mongo_db_account_attrs( + attrs: CosmosMongoDBAccountAttributes, obj: CosmosMongoDBAccount +) -> None: + """Populate CosmosMongoDBAccount-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.cosmos_mongo_db_account_instance_id = obj.cosmos_mongo_db_account_instance_id + attrs.cosmos_mongo_db_database_count = obj.cosmos_mongo_db_database_count + attrs.cosmos_mongo_db_account_type = obj.cosmos_mongo_db_account_type + attrs.cosmos_mongo_db_account_subscription_id = ( + obj.cosmos_mongo_db_account_subscription_id + ) + attrs.cosmos_mongo_db_account_resource_group = ( + obj.cosmos_mongo_db_account_resource_group + ) + attrs.cosmos_mongo_db_account_document_endpoint = ( + obj.cosmos_mongo_db_account_document_endpoint + ) + attrs.cosmos_mongo_db_account_mongo_endpoint = ( + obj.cosmos_mongo_db_account_mongo_endpoint + ) + attrs.cosmos_mongo_db_account_public_network_access = ( + obj.cosmos_mongo_db_account_public_network_access + ) + attrs.cosmos_mongo_db_account_enable_automatic_failover = ( + obj.cosmos_mongo_db_account_enable_automatic_failover + ) + attrs.cosmos_mongo_db_account_enable_multiple_write_locations = ( + obj.cosmos_mongo_db_account_enable_multiple_write_locations + ) + attrs.cosmos_mongo_db_account_enable_partition_key_monitor = ( + obj.cosmos_mongo_db_account_enable_partition_key_monitor + ) + attrs.cosmos_mongo_db_account_is_virtual_network_filter_enabled = ( + obj.cosmos_mongo_db_account_is_virtual_network_filter_enabled + ) + attrs.cosmos_mongo_db_account_consistency_policy = ( + obj.cosmos_mongo_db_account_consistency_policy + ) + attrs.cosmos_mongo_db_account_locations = obj.cosmos_mongo_db_account_locations + attrs.cosmos_mongo_db_account_read_locations = ( + obj.cosmos_mongo_db_account_read_locations + ) + attrs.cosmos_mongo_db_account_write_locations = ( + obj.cosmos_mongo_db_account_write_locations + ) + attrs.no_sql_schema_definition = obj.no_sql_schema_definition + + +def _extract_cosmos_mongo_db_account_attrs( + attrs: CosmosMongoDBAccountAttributes, +) -> dict: + """Extract all CosmosMongoDBAccount attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["cosmos_mongo_db_account_instance_id"] = ( + attrs.cosmos_mongo_db_account_instance_id + ) + result["cosmos_mongo_db_database_count"] = attrs.cosmos_mongo_db_database_count + result["cosmos_mongo_db_account_type"] = attrs.cosmos_mongo_db_account_type + result["cosmos_mongo_db_account_subscription_id"] = ( + attrs.cosmos_mongo_db_account_subscription_id + ) + result["cosmos_mongo_db_account_resource_group"] = ( + attrs.cosmos_mongo_db_account_resource_group + ) + result["cosmos_mongo_db_account_document_endpoint"] = ( + attrs.cosmos_mongo_db_account_document_endpoint + ) + result["cosmos_mongo_db_account_mongo_endpoint"] = ( + attrs.cosmos_mongo_db_account_mongo_endpoint + ) + result["cosmos_mongo_db_account_public_network_access"] = ( + attrs.cosmos_mongo_db_account_public_network_access + ) + result["cosmos_mongo_db_account_enable_automatic_failover"] = ( + attrs.cosmos_mongo_db_account_enable_automatic_failover + ) + result["cosmos_mongo_db_account_enable_multiple_write_locations"] = ( + attrs.cosmos_mongo_db_account_enable_multiple_write_locations + ) + result["cosmos_mongo_db_account_enable_partition_key_monitor"] = ( + attrs.cosmos_mongo_db_account_enable_partition_key_monitor + ) + result["cosmos_mongo_db_account_is_virtual_network_filter_enabled"] = ( + attrs.cosmos_mongo_db_account_is_virtual_network_filter_enabled + ) + result["cosmos_mongo_db_account_consistency_policy"] = ( + attrs.cosmos_mongo_db_account_consistency_policy + ) + result["cosmos_mongo_db_account_locations"] = ( + attrs.cosmos_mongo_db_account_locations + ) + result["cosmos_mongo_db_account_read_locations"] = ( + attrs.cosmos_mongo_db_account_read_locations + ) + result["cosmos_mongo_db_account_write_locations"] = ( + attrs.cosmos_mongo_db_account_write_locations + ) + result["no_sql_schema_definition"] = attrs.no_sql_schema_definition + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _cosmos_mongo_db_account_to_nested( + cosmos_mongo_db_account: CosmosMongoDBAccount, +) -> CosmosMongoDBAccountNested: + """Convert flat CosmosMongoDBAccount to nested format.""" + attrs = CosmosMongoDBAccountAttributes() + _populate_cosmos_mongo_db_account_attrs(attrs, cosmos_mongo_db_account) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + cosmos_mongo_db_account, + _COSMOS_MONGO_DB_ACCOUNT_REL_FIELDS, + CosmosMongoDBAccountRelationshipAttributes, + ) + return CosmosMongoDBAccountNested( + guid=cosmos_mongo_db_account.guid, + type_name=cosmos_mongo_db_account.type_name, + status=cosmos_mongo_db_account.status, + version=cosmos_mongo_db_account.version, + create_time=cosmos_mongo_db_account.create_time, + update_time=cosmos_mongo_db_account.update_time, + created_by=cosmos_mongo_db_account.created_by, + updated_by=cosmos_mongo_db_account.updated_by, + classifications=cosmos_mongo_db_account.classifications, + classification_names=cosmos_mongo_db_account.classification_names, + meanings=cosmos_mongo_db_account.meanings, + labels=cosmos_mongo_db_account.labels, + business_attributes=cosmos_mongo_db_account.business_attributes, + custom_attributes=cosmos_mongo_db_account.custom_attributes, + pending_tasks=cosmos_mongo_db_account.pending_tasks, + proxy=cosmos_mongo_db_account.proxy, + is_incomplete=cosmos_mongo_db_account.is_incomplete, + provenance_type=cosmos_mongo_db_account.provenance_type, + home_id=cosmos_mongo_db_account.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _cosmos_mongo_db_account_from_nested( + nested: CosmosMongoDBAccountNested, +) -> CosmosMongoDBAccount: + """Convert nested format to flat CosmosMongoDBAccount.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else CosmosMongoDBAccountAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _COSMOS_MONGO_DB_ACCOUNT_REL_FIELDS, + CosmosMongoDBAccountRelationshipAttributes, + ) + return CosmosMongoDBAccount( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_cosmos_mongo_db_account_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _cosmos_mongo_db_account_to_nested_bytes( + cosmos_mongo_db_account: CosmosMongoDBAccount, serde: Serde +) -> bytes: + """Convert flat CosmosMongoDBAccount to nested JSON bytes.""" + return serde.encode(_cosmos_mongo_db_account_to_nested(cosmos_mongo_db_account)) + + +def _cosmos_mongo_db_account_from_nested_bytes( + data: bytes, serde: Serde +) -> CosmosMongoDBAccount: + """Convert nested JSON bytes to flat CosmosMongoDBAccount.""" + nested = serde.decode(data, CosmosMongoDBAccountNested) + return _cosmos_mongo_db_account_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, +) + +CosmosMongoDBAccount.COSMOS_MONGO_DB_ACCOUNT_INSTANCE_ID = KeywordField( + "cosmosMongoDBAccountInstanceId", "cosmosMongoDBAccountInstanceId" +) +CosmosMongoDBAccount.COSMOS_MONGO_DB_DATABASE_COUNT = NumericField( + "cosmosMongoDBDatabaseCount", "cosmosMongoDBDatabaseCount" +) +CosmosMongoDBAccount.COSMOS_MONGO_DB_ACCOUNT_TYPE = KeywordField( + "cosmosMongoDBAccountType", "cosmosMongoDBAccountType" +) +CosmosMongoDBAccount.COSMOS_MONGO_DB_ACCOUNT_SUBSCRIPTION_ID = KeywordField( + "cosmosMongoDBAccountSubscriptionId", "cosmosMongoDBAccountSubscriptionId" +) +CosmosMongoDBAccount.COSMOS_MONGO_DB_ACCOUNT_RESOURCE_GROUP = KeywordField( + "cosmosMongoDBAccountResourceGroup", "cosmosMongoDBAccountResourceGroup" +) +CosmosMongoDBAccount.COSMOS_MONGO_DB_ACCOUNT_DOCUMENT_ENDPOINT = KeywordField( + "cosmosMongoDBAccountDocumentEndpoint", "cosmosMongoDBAccountDocumentEndpoint" +) +CosmosMongoDBAccount.COSMOS_MONGO_DB_ACCOUNT_MONGO_ENDPOINT = KeywordField( + "cosmosMongoDBAccountMongoEndpoint", "cosmosMongoDBAccountMongoEndpoint" +) +CosmosMongoDBAccount.COSMOS_MONGO_DB_ACCOUNT_PUBLIC_NETWORK_ACCESS = KeywordField( + "cosmosMongoDBAccountPublicNetworkAccess", "cosmosMongoDBAccountPublicNetworkAccess" +) +CosmosMongoDBAccount.COSMOS_MONGO_DB_ACCOUNT_ENABLE_AUTOMATIC_FAILOVER = BooleanField( + "cosmosMongoDBAccountEnableAutomaticFailover", + "cosmosMongoDBAccountEnableAutomaticFailover", +) +CosmosMongoDBAccount.COSMOS_MONGO_DB_ACCOUNT_ENABLE_MULTIPLE_WRITE_LOCATIONS = ( + BooleanField( + "cosmosMongoDBAccountEnableMultipleWriteLocations", + "cosmosMongoDBAccountEnableMultipleWriteLocations", + ) +) +CosmosMongoDBAccount.COSMOS_MONGO_DB_ACCOUNT_ENABLE_PARTITION_KEY_MONITOR = ( + BooleanField( + "cosmosMongoDBAccountEnablePartitionKeyMonitor", + "cosmosMongoDBAccountEnablePartitionKeyMonitor", + ) +) +CosmosMongoDBAccount.COSMOS_MONGO_DB_ACCOUNT_IS_VIRTUAL_NETWORK_FILTER_ENABLED = ( + BooleanField( + "cosmosMongoDBAccountIsVirtualNetworkFilterEnabled", + "cosmosMongoDBAccountIsVirtualNetworkFilterEnabled", + ) +) +CosmosMongoDBAccount.COSMOS_MONGO_DB_ACCOUNT_CONSISTENCY_POLICY = KeywordField( + "cosmosMongoDBAccountConsistencyPolicy", "cosmosMongoDBAccountConsistencyPolicy" +) +CosmosMongoDBAccount.COSMOS_MONGO_DB_ACCOUNT_LOCATIONS = KeywordField( + "cosmosMongoDBAccountLocations", "cosmosMongoDBAccountLocations" +) +CosmosMongoDBAccount.COSMOS_MONGO_DB_ACCOUNT_READ_LOCATIONS = KeywordField( + "cosmosMongoDBAccountReadLocations", "cosmosMongoDBAccountReadLocations" +) +CosmosMongoDBAccount.COSMOS_MONGO_DB_ACCOUNT_WRITE_LOCATIONS = KeywordField( + "cosmosMongoDBAccountWriteLocations", "cosmosMongoDBAccountWriteLocations" +) +CosmosMongoDBAccount.NO_SQL_SCHEMA_DEFINITION = KeywordField( + "noSQLSchemaDefinition", "noSQLSchemaDefinition" +) +CosmosMongoDBAccount.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +CosmosMongoDBAccount.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +CosmosMongoDBAccount.ANOMALO_CHECKS = RelationField("anomaloChecks") +CosmosMongoDBAccount.APPLICATION = RelationField("application") +CosmosMongoDBAccount.APPLICATION_FIELD = RelationField("applicationField") +CosmosMongoDBAccount.COSMOS_MONGO_DB_DATABASES = RelationField("cosmosMongoDBDatabases") +CosmosMongoDBAccount.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +CosmosMongoDBAccount.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +CosmosMongoDBAccount.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +CosmosMongoDBAccount.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +CosmosMongoDBAccount.METRICS = RelationField("metrics") +CosmosMongoDBAccount.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +CosmosMongoDBAccount.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +CosmosMongoDBAccount.MEANINGS = RelationField("meanings") +CosmosMongoDBAccount.MC_MONITORS = RelationField("mcMonitors") +CosmosMongoDBAccount.MC_INCIDENTS = RelationField("mcIncidents") +CosmosMongoDBAccount.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +CosmosMongoDBAccount.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +CosmosMongoDBAccount.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +CosmosMongoDBAccount.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +CosmosMongoDBAccount.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +CosmosMongoDBAccount.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +CosmosMongoDBAccount.FILES = RelationField("files") +CosmosMongoDBAccount.LINKS = RelationField("links") +CosmosMongoDBAccount.README = RelationField("readme") +CosmosMongoDBAccount.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +CosmosMongoDBAccount.SODA_CHECKS = RelationField("sodaChecks") +CosmosMongoDBAccount.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +CosmosMongoDBAccount.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/cosmos_mongo_db_collection.py b/pyatlan_v9/model/assets/cosmos_mongo_db_collection.py new file mode 100644 index 000000000..6c10c7c1d --- /dev/null +++ b/pyatlan_v9/model/assets/cosmos_mongo_db_collection.py @@ -0,0 +1,1535 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +CosmosMongoDBCollection asset model with flattened inheritance. + +This module provides: +- CosmosMongoDBCollection: Flat asset class (easy to use) +- CosmosMongoDBCollectionAttributes: Nested attributes struct (extends AssetAttributes) +- CosmosMongoDBCollectionNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .mongo_db_related import RelatedMongoDBDatabase +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .snowflake_related import RelatedSnowflakeSemanticLogicalTable +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from .sql_related import ( + RelatedColumn, + RelatedQuery, + RelatedSchema, + RelatedTable, + RelatedTablePartition, +) +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .cosmos_mongo_db_related import RelatedCosmosMongoDBDatabase + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class CosmosMongoDBCollection(Asset): + """ + Instance of a Cosmos MongoDB collection in Atlan. + """ + + COSMOS_MONGO_DB_DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + NO_SQL_SCHEMA_DEFINITION: ClassVar[Any] = None + MONGO_DB_COLLECTION_SUBTYPE: ClassVar[Any] = None + MONGO_DB_COLLECTION_IS_CAPPED: ClassVar[Any] = None + MONGO_DB_COLLECTION_TIME_FIELD: ClassVar[Any] = None + MONGO_DB_COLLECTION_TIME_GRANULARITY: ClassVar[Any] = None + MONGO_DB_COLLECTION_EXPIRE_AFTER_SECONDS: ClassVar[Any] = None + MONGO_DB_COLLECTION_MAXIMUM_DOCUMENT_COUNT: ClassVar[Any] = None + MONGO_DB_COLLECTION_MAX_SIZE: ClassVar[Any] = None + MONGO_DB_COLLECTION_NUM_ORPHAN_DOCS: ClassVar[Any] = None + MONGO_DB_COLLECTION_NUM_INDEXES: ClassVar[Any] = None + MONGO_DB_COLLECTION_TOTAL_INDEX_SIZE: ClassVar[Any] = None + MONGO_DB_COLLECTION_AVERAGE_OBJECT_SIZE: ClassVar[Any] = None + MONGO_DB_COLLECTION_SCHEMA_DEFINITION: ClassVar[Any] = None + COLUMN_COUNT: ClassVar[Any] = None + ROW_COUNT: ClassVar[Any] = None + SIZE_BYTES: ClassVar[Any] = None + TABLE_OBJECT_COUNT: ClassVar[Any] = None + ALIAS: ClassVar[Any] = None + IS_TEMPORARY: ClassVar[Any] = None + IS_QUERY_PREVIEW: ClassVar[Any] = None + QUERY_PREVIEW_CONFIG: ClassVar[Any] = None + EXTERNAL_LOCATION: ClassVar[Any] = None + EXTERNAL_LOCATION_REGION: ClassVar[Any] = None + EXTERNAL_LOCATION_FORMAT: ClassVar[Any] = None + IS_PARTITIONED: ClassVar[Any] = None + PARTITION_STRATEGY: ClassVar[Any] = None + PARTITION_COUNT: ClassVar[Any] = None + TABLE_DEFINITION: ClassVar[Any] = None + PARTITION_LIST: ClassVar[Any] = None + IS_SHARDED: ClassVar[Any] = None + TABLE_TYPE: ClassVar[Any] = None + ICEBERG_CATALOG_NAME: ClassVar[Any] = None + ICEBERG_TABLE_TYPE: ClassVar[Any] = None + ICEBERG_CATALOG_SOURCE: ClassVar[Any] = None + ICEBERG_CATALOG_TABLE_NAME: ClassVar[Any] = None + TABLE_IMPALA_PARAMETERS: ClassVar[Any] = None + ICEBERG_CATALOG_TABLE_NAMESPACE: ClassVar[Any] = None + TABLE_EXTERNAL_VOLUME_NAME: ClassVar[Any] = None + ICEBERG_TABLE_BASE_LOCATION: ClassVar[Any] = None + TABLE_RETENTION_TIME: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + COSMOS_MONGO_DB_DATABASE: ClassVar[Any] = None + COLUMNS: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MONGO_DB_DATABASE: ClassVar[Any] = None + MONGO_DB_COLUMNS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + QUERIES: ClassVar[Any] = None + ATLAN_SCHEMA: ClassVar[Any] = None + DIMENSIONS: ClassVar[Any] = None + FACTS: ClassVar[Any] = None + PARTITIONS: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "CosmosMongoDBCollection" + + cosmos_mongo_db_database_qualified_name: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="cosmosMongoDBDatabaseQualifiedName") + ) + """Unique name of the database in which this collection exists.""" + + no_sql_schema_definition: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="noSQLSchemaDefinition" + ) + """Represents attributes for describing the key schema for the table and indexes.""" + + mongo_db_collection_subtype: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBCollectionSubtype" + ) + """Subtype of a MongoDB collection, for example: Capped, Time Series, etc.""" + + mongo_db_collection_is_capped: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBCollectionIsCapped" + ) + """Whether the collection is capped (true) or not (false).""" + + mongo_db_collection_time_field: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBCollectionTimeField" + ) + """Name of the field containing the date in each time series document.""" + + mongo_db_collection_time_granularity: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBCollectionTimeGranularity" + ) + """Closest match to the time span between consecutive incoming measurements.""" + + mongo_db_collection_expire_after_seconds: Union[int, None, UnsetType] = ( + msgspec.field(default=UNSET, name="mongoDBCollectionExpireAfterSeconds") + ) + """Seconds after which documents in a time series collection or clustered collection expire.""" + + mongo_db_collection_maximum_document_count: Union[int, None, UnsetType] = ( + msgspec.field(default=UNSET, name="mongoDBCollectionMaximumDocumentCount") + ) + """Maximum number of documents allowed in a capped collection.""" + + mongo_db_collection_max_size: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBCollectionMaxSize" + ) + """Maximum size allowed in a capped collection.""" + + mongo_db_collection_num_orphan_docs: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBCollectionNumOrphanDocs" + ) + """Number of orphaned documents in the collection.""" + + mongo_db_collection_num_indexes: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBCollectionNumIndexes" + ) + """Number of indexes on the collection.""" + + mongo_db_collection_total_index_size: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBCollectionTotalIndexSize" + ) + """Total size of all indexes.""" + + mongo_db_collection_average_object_size: Union[int, None, UnsetType] = ( + msgspec.field(default=UNSET, name="mongoDBCollectionAverageObjectSize") + ) + """Average size of an object in the collection.""" + + mongo_db_collection_schema_definition: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBCollectionSchemaDefinition" + ) + """Definition of the schema applicable for the collection.""" + + column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this table.""" + + row_count: Union[int, None, UnsetType] = UNSET + """Number of rows in this table.""" + + size_bytes: Union[int, None, UnsetType] = UNSET + """Size of this table, in bytes.""" + + table_object_count: Union[int, None, UnsetType] = UNSET + """Number of objects in this table.""" + + alias: Union[str, None, UnsetType] = UNSET + """Alias for this table.""" + + is_temporary: Union[bool, None, UnsetType] = UNSET + """Whether this table is temporary (true) or not (false).""" + + is_query_preview: Union[bool, None, UnsetType] = UNSET + """Whether preview queries are allowed for this table (true) or not (false).""" + + query_preview_config: Union[Dict[str, str], None, UnsetType] = UNSET + """Configuration for preview queries.""" + + external_location: Union[str, None, UnsetType] = UNSET + """External location of this table, for example: an S3 object location.""" + + external_location_region: Union[str, None, UnsetType] = UNSET + """Region of the external location of this table, for example: S3 region.""" + + external_location_format: Union[str, None, UnsetType] = UNSET + """Format of the external location of this table, for example: JSON, CSV, PARQUET, etc.""" + + is_partitioned: Union[bool, None, UnsetType] = UNSET + """Whether this table is partitioned (true) or not (false).""" + + partition_strategy: Union[str, None, UnsetType] = UNSET + """Partition strategy for this table.""" + + partition_count: Union[int, None, UnsetType] = UNSET + """Number of partitions in this table.""" + + table_definition: Union[str, None, UnsetType] = UNSET + """Definition of the table.""" + + partition_list: Union[str, None, UnsetType] = UNSET + """List of partitions in this table.""" + + is_sharded: Union[bool, None, UnsetType] = UNSET + """Whether this table is a sharded table (true) or not (false).""" + + table_type: Union[str, None, UnsetType] = UNSET + """Type of the table.""" + + iceberg_catalog_name: Union[str, None, UnsetType] = UNSET + """Iceberg table catalog name (can be any user defined name)""" + + iceberg_table_type: Union[str, None, UnsetType] = UNSET + """Iceberg table type (managed vs unmanaged)""" + + iceberg_catalog_source: Union[str, None, UnsetType] = UNSET + """Iceberg table catalog type (glue, polaris, snowflake)""" + + iceberg_catalog_table_name: Union[str, None, UnsetType] = UNSET + """Catalog table name (actual table name on the catalog side).""" + + table_impala_parameters: Union[Dict[str, str], None, UnsetType] = UNSET + """Extra attributes for Impala""" + + iceberg_catalog_table_namespace: Union[str, None, UnsetType] = UNSET + """Catalog table namespace (actual database name on the catalog side).""" + + table_external_volume_name: Union[str, None, UnsetType] = UNSET + """External volume name for the table.""" + + iceberg_table_base_location: Union[str, None, UnsetType] = UNSET + """Iceberg table base location inside the external volume.""" + + table_retention_time: Union[int, None, UnsetType] = UNSET + """Data retention time in days.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cosmos_mongo_db_database: Union[RelatedCosmosMongoDBDatabase, None, UnsetType] = ( + msgspec.field(default=UNSET, name="cosmosMongoDBDatabase") + ) + """Database in which the collection exists.""" + + columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Columns that exist within this cosmos collection.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mongo_db_database: Union[RelatedMongoDBDatabase, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBDatabase" + ) + """Database in which the collection exists.""" + + mongo_db_columns: Union[List[RelatedColumn], None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBColumns" + ) + """Columns that exist within this collection.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + queries: Union[List[RelatedQuery], None, UnsetType] = UNSET + """Queries that access this table.""" + + atlan_schema: Union[RelatedSchema, None, UnsetType] = UNSET + """Schema in which this table exists.""" + + dimensions: Union[List[RelatedTable], None, UnsetType] = UNSET + """""" + + facts: Union[List[RelatedTable], None, UnsetType] = UNSET + """""" + + partitions: Union[List[RelatedTablePartition], None, UnsetType] = UNSET + """Partitions that exist within this table.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "CosmosMongoDBCollection" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _cosmos_mongo_db_collection_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> CosmosMongoDBCollection: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + CosmosMongoDBCollection instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _cosmos_mongo_db_collection_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class CosmosMongoDBCollectionAttributes(AssetAttributes): + """CosmosMongoDBCollection-specific attributes for nested API format.""" + + cosmos_mongo_db_database_qualified_name: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="cosmosMongoDBDatabaseQualifiedName") + ) + """Unique name of the database in which this collection exists.""" + + no_sql_schema_definition: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="noSQLSchemaDefinition" + ) + """Represents attributes for describing the key schema for the table and indexes.""" + + mongo_db_collection_subtype: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBCollectionSubtype" + ) + """Subtype of a MongoDB collection, for example: Capped, Time Series, etc.""" + + mongo_db_collection_is_capped: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBCollectionIsCapped" + ) + """Whether the collection is capped (true) or not (false).""" + + mongo_db_collection_time_field: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBCollectionTimeField" + ) + """Name of the field containing the date in each time series document.""" + + mongo_db_collection_time_granularity: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBCollectionTimeGranularity" + ) + """Closest match to the time span between consecutive incoming measurements.""" + + mongo_db_collection_expire_after_seconds: Union[int, None, UnsetType] = ( + msgspec.field(default=UNSET, name="mongoDBCollectionExpireAfterSeconds") + ) + """Seconds after which documents in a time series collection or clustered collection expire.""" + + mongo_db_collection_maximum_document_count: Union[int, None, UnsetType] = ( + msgspec.field(default=UNSET, name="mongoDBCollectionMaximumDocumentCount") + ) + """Maximum number of documents allowed in a capped collection.""" + + mongo_db_collection_max_size: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBCollectionMaxSize" + ) + """Maximum size allowed in a capped collection.""" + + mongo_db_collection_num_orphan_docs: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBCollectionNumOrphanDocs" + ) + """Number of orphaned documents in the collection.""" + + mongo_db_collection_num_indexes: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBCollectionNumIndexes" + ) + """Number of indexes on the collection.""" + + mongo_db_collection_total_index_size: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBCollectionTotalIndexSize" + ) + """Total size of all indexes.""" + + mongo_db_collection_average_object_size: Union[int, None, UnsetType] = ( + msgspec.field(default=UNSET, name="mongoDBCollectionAverageObjectSize") + ) + """Average size of an object in the collection.""" + + mongo_db_collection_schema_definition: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBCollectionSchemaDefinition" + ) + """Definition of the schema applicable for the collection.""" + + column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this table.""" + + row_count: Union[int, None, UnsetType] = UNSET + """Number of rows in this table.""" + + size_bytes: Union[int, None, UnsetType] = UNSET + """Size of this table, in bytes.""" + + table_object_count: Union[int, None, UnsetType] = UNSET + """Number of objects in this table.""" + + alias: Union[str, None, UnsetType] = UNSET + """Alias for this table.""" + + is_temporary: Union[bool, None, UnsetType] = UNSET + """Whether this table is temporary (true) or not (false).""" + + is_query_preview: Union[bool, None, UnsetType] = UNSET + """Whether preview queries are allowed for this table (true) or not (false).""" + + query_preview_config: Union[Dict[str, str], None, UnsetType] = UNSET + """Configuration for preview queries.""" + + external_location: Union[str, None, UnsetType] = UNSET + """External location of this table, for example: an S3 object location.""" + + external_location_region: Union[str, None, UnsetType] = UNSET + """Region of the external location of this table, for example: S3 region.""" + + external_location_format: Union[str, None, UnsetType] = UNSET + """Format of the external location of this table, for example: JSON, CSV, PARQUET, etc.""" + + is_partitioned: Union[bool, None, UnsetType] = UNSET + """Whether this table is partitioned (true) or not (false).""" + + partition_strategy: Union[str, None, UnsetType] = UNSET + """Partition strategy for this table.""" + + partition_count: Union[int, None, UnsetType] = UNSET + """Number of partitions in this table.""" + + table_definition: Union[str, None, UnsetType] = UNSET + """Definition of the table.""" + + partition_list: Union[str, None, UnsetType] = UNSET + """List of partitions in this table.""" + + is_sharded: Union[bool, None, UnsetType] = UNSET + """Whether this table is a sharded table (true) or not (false).""" + + table_type: Union[str, None, UnsetType] = UNSET + """Type of the table.""" + + iceberg_catalog_name: Union[str, None, UnsetType] = UNSET + """Iceberg table catalog name (can be any user defined name)""" + + iceberg_table_type: Union[str, None, UnsetType] = UNSET + """Iceberg table type (managed vs unmanaged)""" + + iceberg_catalog_source: Union[str, None, UnsetType] = UNSET + """Iceberg table catalog type (glue, polaris, snowflake)""" + + iceberg_catalog_table_name: Union[str, None, UnsetType] = UNSET + """Catalog table name (actual table name on the catalog side).""" + + table_impala_parameters: Union[Dict[str, str], None, UnsetType] = UNSET + """Extra attributes for Impala""" + + iceberg_catalog_table_namespace: Union[str, None, UnsetType] = UNSET + """Catalog table namespace (actual database name on the catalog side).""" + + table_external_volume_name: Union[str, None, UnsetType] = UNSET + """External volume name for the table.""" + + iceberg_table_base_location: Union[str, None, UnsetType] = UNSET + """Iceberg table base location inside the external volume.""" + + table_retention_time: Union[int, None, UnsetType] = UNSET + """Data retention time in days.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + +class CosmosMongoDBCollectionRelationshipAttributes(AssetRelationshipAttributes): + """CosmosMongoDBCollection-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cosmos_mongo_db_database: Union[RelatedCosmosMongoDBDatabase, None, UnsetType] = ( + msgspec.field(default=UNSET, name="cosmosMongoDBDatabase") + ) + """Database in which the collection exists.""" + + columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Columns that exist within this cosmos collection.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mongo_db_database: Union[RelatedMongoDBDatabase, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBDatabase" + ) + """Database in which the collection exists.""" + + mongo_db_columns: Union[List[RelatedColumn], None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBColumns" + ) + """Columns that exist within this collection.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + queries: Union[List[RelatedQuery], None, UnsetType] = UNSET + """Queries that access this table.""" + + atlan_schema: Union[RelatedSchema, None, UnsetType] = UNSET + """Schema in which this table exists.""" + + dimensions: Union[List[RelatedTable], None, UnsetType] = UNSET + """""" + + facts: Union[List[RelatedTable], None, UnsetType] = UNSET + """""" + + partitions: Union[List[RelatedTablePartition], None, UnsetType] = UNSET + """Partitions that exist within this table.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class CosmosMongoDBCollectionNested(AssetNested): + """CosmosMongoDBCollection in nested API format for high-performance serialization.""" + + attributes: Union[CosmosMongoDBCollectionAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + CosmosMongoDBCollectionRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + CosmosMongoDBCollectionRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + CosmosMongoDBCollectionRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_COSMOS_MONGO_DB_COLLECTION_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "cosmos_mongo_db_database", + "columns", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "meanings", + "mongo_db_database", + "mongo_db_columns", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "queries", + "atlan_schema", + "dimensions", + "facts", + "partitions", + "schema_registry_subjects", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_cosmos_mongo_db_collection_attrs( + attrs: CosmosMongoDBCollectionAttributes, obj: CosmosMongoDBCollection +) -> None: + """Populate CosmosMongoDBCollection-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.cosmos_mongo_db_database_qualified_name = ( + obj.cosmos_mongo_db_database_qualified_name + ) + attrs.no_sql_schema_definition = obj.no_sql_schema_definition + attrs.mongo_db_collection_subtype = obj.mongo_db_collection_subtype + attrs.mongo_db_collection_is_capped = obj.mongo_db_collection_is_capped + attrs.mongo_db_collection_time_field = obj.mongo_db_collection_time_field + attrs.mongo_db_collection_time_granularity = ( + obj.mongo_db_collection_time_granularity + ) + attrs.mongo_db_collection_expire_after_seconds = ( + obj.mongo_db_collection_expire_after_seconds + ) + attrs.mongo_db_collection_maximum_document_count = ( + obj.mongo_db_collection_maximum_document_count + ) + attrs.mongo_db_collection_max_size = obj.mongo_db_collection_max_size + attrs.mongo_db_collection_num_orphan_docs = obj.mongo_db_collection_num_orphan_docs + attrs.mongo_db_collection_num_indexes = obj.mongo_db_collection_num_indexes + attrs.mongo_db_collection_total_index_size = ( + obj.mongo_db_collection_total_index_size + ) + attrs.mongo_db_collection_average_object_size = ( + obj.mongo_db_collection_average_object_size + ) + attrs.mongo_db_collection_schema_definition = ( + obj.mongo_db_collection_schema_definition + ) + attrs.column_count = obj.column_count + attrs.row_count = obj.row_count + attrs.size_bytes = obj.size_bytes + attrs.table_object_count = obj.table_object_count + attrs.alias = obj.alias + attrs.is_temporary = obj.is_temporary + attrs.is_query_preview = obj.is_query_preview + attrs.query_preview_config = obj.query_preview_config + attrs.external_location = obj.external_location + attrs.external_location_region = obj.external_location_region + attrs.external_location_format = obj.external_location_format + attrs.is_partitioned = obj.is_partitioned + attrs.partition_strategy = obj.partition_strategy + attrs.partition_count = obj.partition_count + attrs.table_definition = obj.table_definition + attrs.partition_list = obj.partition_list + attrs.is_sharded = obj.is_sharded + attrs.table_type = obj.table_type + attrs.iceberg_catalog_name = obj.iceberg_catalog_name + attrs.iceberg_table_type = obj.iceberg_table_type + attrs.iceberg_catalog_source = obj.iceberg_catalog_source + attrs.iceberg_catalog_table_name = obj.iceberg_catalog_table_name + attrs.table_impala_parameters = obj.table_impala_parameters + attrs.iceberg_catalog_table_namespace = obj.iceberg_catalog_table_namespace + attrs.table_external_volume_name = obj.table_external_volume_name + attrs.iceberg_table_base_location = obj.iceberg_table_base_location + attrs.table_retention_time = obj.table_retention_time + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + + +def _extract_cosmos_mongo_db_collection_attrs( + attrs: CosmosMongoDBCollectionAttributes, +) -> dict: + """Extract all CosmosMongoDBCollection attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["cosmos_mongo_db_database_qualified_name"] = ( + attrs.cosmos_mongo_db_database_qualified_name + ) + result["no_sql_schema_definition"] = attrs.no_sql_schema_definition + result["mongo_db_collection_subtype"] = attrs.mongo_db_collection_subtype + result["mongo_db_collection_is_capped"] = attrs.mongo_db_collection_is_capped + result["mongo_db_collection_time_field"] = attrs.mongo_db_collection_time_field + result["mongo_db_collection_time_granularity"] = ( + attrs.mongo_db_collection_time_granularity + ) + result["mongo_db_collection_expire_after_seconds"] = ( + attrs.mongo_db_collection_expire_after_seconds + ) + result["mongo_db_collection_maximum_document_count"] = ( + attrs.mongo_db_collection_maximum_document_count + ) + result["mongo_db_collection_max_size"] = attrs.mongo_db_collection_max_size + result["mongo_db_collection_num_orphan_docs"] = ( + attrs.mongo_db_collection_num_orphan_docs + ) + result["mongo_db_collection_num_indexes"] = attrs.mongo_db_collection_num_indexes + result["mongo_db_collection_total_index_size"] = ( + attrs.mongo_db_collection_total_index_size + ) + result["mongo_db_collection_average_object_size"] = ( + attrs.mongo_db_collection_average_object_size + ) + result["mongo_db_collection_schema_definition"] = ( + attrs.mongo_db_collection_schema_definition + ) + result["column_count"] = attrs.column_count + result["row_count"] = attrs.row_count + result["size_bytes"] = attrs.size_bytes + result["table_object_count"] = attrs.table_object_count + result["alias"] = attrs.alias + result["is_temporary"] = attrs.is_temporary + result["is_query_preview"] = attrs.is_query_preview + result["query_preview_config"] = attrs.query_preview_config + result["external_location"] = attrs.external_location + result["external_location_region"] = attrs.external_location_region + result["external_location_format"] = attrs.external_location_format + result["is_partitioned"] = attrs.is_partitioned + result["partition_strategy"] = attrs.partition_strategy + result["partition_count"] = attrs.partition_count + result["table_definition"] = attrs.table_definition + result["partition_list"] = attrs.partition_list + result["is_sharded"] = attrs.is_sharded + result["table_type"] = attrs.table_type + result["iceberg_catalog_name"] = attrs.iceberg_catalog_name + result["iceberg_table_type"] = attrs.iceberg_table_type + result["iceberg_catalog_source"] = attrs.iceberg_catalog_source + result["iceberg_catalog_table_name"] = attrs.iceberg_catalog_table_name + result["table_impala_parameters"] = attrs.table_impala_parameters + result["iceberg_catalog_table_namespace"] = attrs.iceberg_catalog_table_namespace + result["table_external_volume_name"] = attrs.table_external_volume_name + result["iceberg_table_base_location"] = attrs.iceberg_table_base_location + result["table_retention_time"] = attrs.table_retention_time + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _cosmos_mongo_db_collection_to_nested( + cosmos_mongo_db_collection: CosmosMongoDBCollection, +) -> CosmosMongoDBCollectionNested: + """Convert flat CosmosMongoDBCollection to nested format.""" + attrs = CosmosMongoDBCollectionAttributes() + _populate_cosmos_mongo_db_collection_attrs(attrs, cosmos_mongo_db_collection) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + cosmos_mongo_db_collection, + _COSMOS_MONGO_DB_COLLECTION_REL_FIELDS, + CosmosMongoDBCollectionRelationshipAttributes, + ) + return CosmosMongoDBCollectionNested( + guid=cosmos_mongo_db_collection.guid, + type_name=cosmos_mongo_db_collection.type_name, + status=cosmos_mongo_db_collection.status, + version=cosmos_mongo_db_collection.version, + create_time=cosmos_mongo_db_collection.create_time, + update_time=cosmos_mongo_db_collection.update_time, + created_by=cosmos_mongo_db_collection.created_by, + updated_by=cosmos_mongo_db_collection.updated_by, + classifications=cosmos_mongo_db_collection.classifications, + classification_names=cosmos_mongo_db_collection.classification_names, + meanings=cosmos_mongo_db_collection.meanings, + labels=cosmos_mongo_db_collection.labels, + business_attributes=cosmos_mongo_db_collection.business_attributes, + custom_attributes=cosmos_mongo_db_collection.custom_attributes, + pending_tasks=cosmos_mongo_db_collection.pending_tasks, + proxy=cosmos_mongo_db_collection.proxy, + is_incomplete=cosmos_mongo_db_collection.is_incomplete, + provenance_type=cosmos_mongo_db_collection.provenance_type, + home_id=cosmos_mongo_db_collection.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _cosmos_mongo_db_collection_from_nested( + nested: CosmosMongoDBCollectionNested, +) -> CosmosMongoDBCollection: + """Convert nested format to flat CosmosMongoDBCollection.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else CosmosMongoDBCollectionAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _COSMOS_MONGO_DB_COLLECTION_REL_FIELDS, + CosmosMongoDBCollectionRelationshipAttributes, + ) + return CosmosMongoDBCollection( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_cosmos_mongo_db_collection_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _cosmos_mongo_db_collection_to_nested_bytes( + cosmos_mongo_db_collection: CosmosMongoDBCollection, serde: Serde +) -> bytes: + """Convert flat CosmosMongoDBCollection to nested JSON bytes.""" + return serde.encode( + _cosmos_mongo_db_collection_to_nested(cosmos_mongo_db_collection) + ) + + +def _cosmos_mongo_db_collection_from_nested_bytes( + data: bytes, serde: Serde +) -> CosmosMongoDBCollection: + """Convert nested JSON bytes to flat CosmosMongoDBCollection.""" + nested = serde.decode(data, CosmosMongoDBCollectionNested) + return _cosmos_mongo_db_collection_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +CosmosMongoDBCollection.COSMOS_MONGO_DB_DATABASE_QUALIFIED_NAME = KeywordTextField( + "cosmosMongoDBDatabaseQualifiedName", + "cosmosMongoDBDatabaseQualifiedName", + "cosmosMongoDBDatabaseQualifiedName.text", +) +CosmosMongoDBCollection.NO_SQL_SCHEMA_DEFINITION = KeywordField( + "noSQLSchemaDefinition", "noSQLSchemaDefinition" +) +CosmosMongoDBCollection.MONGO_DB_COLLECTION_SUBTYPE = KeywordTextField( + "mongoDBCollectionSubtype", + "mongoDBCollectionSubtype", + "mongoDBCollectionSubtype.text", +) +CosmosMongoDBCollection.MONGO_DB_COLLECTION_IS_CAPPED = BooleanField( + "mongoDBCollectionIsCapped", "mongoDBCollectionIsCapped" +) +CosmosMongoDBCollection.MONGO_DB_COLLECTION_TIME_FIELD = KeywordField( + "mongoDBCollectionTimeField", "mongoDBCollectionTimeField" +) +CosmosMongoDBCollection.MONGO_DB_COLLECTION_TIME_GRANULARITY = KeywordField( + "mongoDBCollectionTimeGranularity", "mongoDBCollectionTimeGranularity" +) +CosmosMongoDBCollection.MONGO_DB_COLLECTION_EXPIRE_AFTER_SECONDS = NumericField( + "mongoDBCollectionExpireAfterSeconds", "mongoDBCollectionExpireAfterSeconds" +) +CosmosMongoDBCollection.MONGO_DB_COLLECTION_MAXIMUM_DOCUMENT_COUNT = NumericField( + "mongoDBCollectionMaximumDocumentCount", "mongoDBCollectionMaximumDocumentCount" +) +CosmosMongoDBCollection.MONGO_DB_COLLECTION_MAX_SIZE = NumericField( + "mongoDBCollectionMaxSize", "mongoDBCollectionMaxSize" +) +CosmosMongoDBCollection.MONGO_DB_COLLECTION_NUM_ORPHAN_DOCS = NumericField( + "mongoDBCollectionNumOrphanDocs", "mongoDBCollectionNumOrphanDocs" +) +CosmosMongoDBCollection.MONGO_DB_COLLECTION_NUM_INDEXES = NumericField( + "mongoDBCollectionNumIndexes", "mongoDBCollectionNumIndexes" +) +CosmosMongoDBCollection.MONGO_DB_COLLECTION_TOTAL_INDEX_SIZE = NumericField( + "mongoDBCollectionTotalIndexSize", "mongoDBCollectionTotalIndexSize" +) +CosmosMongoDBCollection.MONGO_DB_COLLECTION_AVERAGE_OBJECT_SIZE = NumericField( + "mongoDBCollectionAverageObjectSize", "mongoDBCollectionAverageObjectSize" +) +CosmosMongoDBCollection.MONGO_DB_COLLECTION_SCHEMA_DEFINITION = KeywordField( + "mongoDBCollectionSchemaDefinition", "mongoDBCollectionSchemaDefinition" +) +CosmosMongoDBCollection.COLUMN_COUNT = NumericField("columnCount", "columnCount") +CosmosMongoDBCollection.ROW_COUNT = NumericField("rowCount", "rowCount") +CosmosMongoDBCollection.SIZE_BYTES = NumericField("sizeBytes", "sizeBytes") +CosmosMongoDBCollection.TABLE_OBJECT_COUNT = NumericField( + "tableObjectCount", "tableObjectCount" +) +CosmosMongoDBCollection.ALIAS = KeywordField("alias", "alias") +CosmosMongoDBCollection.IS_TEMPORARY = BooleanField("isTemporary", "isTemporary") +CosmosMongoDBCollection.IS_QUERY_PREVIEW = BooleanField( + "isQueryPreview", "isQueryPreview" +) +CosmosMongoDBCollection.QUERY_PREVIEW_CONFIG = KeywordField( + "queryPreviewConfig", "queryPreviewConfig" +) +CosmosMongoDBCollection.EXTERNAL_LOCATION = KeywordField( + "externalLocation", "externalLocation" +) +CosmosMongoDBCollection.EXTERNAL_LOCATION_REGION = KeywordField( + "externalLocationRegion", "externalLocationRegion" +) +CosmosMongoDBCollection.EXTERNAL_LOCATION_FORMAT = KeywordField( + "externalLocationFormat", "externalLocationFormat" +) +CosmosMongoDBCollection.IS_PARTITIONED = BooleanField("isPartitioned", "isPartitioned") +CosmosMongoDBCollection.PARTITION_STRATEGY = KeywordField( + "partitionStrategy", "partitionStrategy" +) +CosmosMongoDBCollection.PARTITION_COUNT = NumericField( + "partitionCount", "partitionCount" +) +CosmosMongoDBCollection.TABLE_DEFINITION = KeywordField( + "tableDefinition", "tableDefinition" +) +CosmosMongoDBCollection.PARTITION_LIST = KeywordField("partitionList", "partitionList") +CosmosMongoDBCollection.IS_SHARDED = BooleanField("isSharded", "isSharded") +CosmosMongoDBCollection.TABLE_TYPE = KeywordField("tableType", "tableType") +CosmosMongoDBCollection.ICEBERG_CATALOG_NAME = KeywordField( + "icebergCatalogName", "icebergCatalogName" +) +CosmosMongoDBCollection.ICEBERG_TABLE_TYPE = KeywordField( + "icebergTableType", "icebergTableType" +) +CosmosMongoDBCollection.ICEBERG_CATALOG_SOURCE = KeywordField( + "icebergCatalogSource", "icebergCatalogSource" +) +CosmosMongoDBCollection.ICEBERG_CATALOG_TABLE_NAME = KeywordField( + "icebergCatalogTableName", "icebergCatalogTableName" +) +CosmosMongoDBCollection.TABLE_IMPALA_PARAMETERS = KeywordField( + "tableImpalaParameters", "tableImpalaParameters" +) +CosmosMongoDBCollection.ICEBERG_CATALOG_TABLE_NAMESPACE = KeywordField( + "icebergCatalogTableNamespace", "icebergCatalogTableNamespace" +) +CosmosMongoDBCollection.TABLE_EXTERNAL_VOLUME_NAME = KeywordField( + "tableExternalVolumeName", "tableExternalVolumeName" +) +CosmosMongoDBCollection.ICEBERG_TABLE_BASE_LOCATION = KeywordField( + "icebergTableBaseLocation", "icebergTableBaseLocation" +) +CosmosMongoDBCollection.TABLE_RETENTION_TIME = NumericField( + "tableRetentionTime", "tableRetentionTime" +) +CosmosMongoDBCollection.QUERY_COUNT = NumericField("queryCount", "queryCount") +CosmosMongoDBCollection.QUERY_USER_COUNT = NumericField( + "queryUserCount", "queryUserCount" +) +CosmosMongoDBCollection.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +CosmosMongoDBCollection.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +CosmosMongoDBCollection.DATABASE_NAME = KeywordField("databaseName", "databaseName") +CosmosMongoDBCollection.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +CosmosMongoDBCollection.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +CosmosMongoDBCollection.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +CosmosMongoDBCollection.TABLE_NAME = KeywordField("tableName", "tableName") +CosmosMongoDBCollection.TABLE_QUALIFIED_NAME = KeywordField( + "tableQualifiedName", "tableQualifiedName" +) +CosmosMongoDBCollection.VIEW_NAME = KeywordField("viewName", "viewName") +CosmosMongoDBCollection.VIEW_QUALIFIED_NAME = KeywordField( + "viewQualifiedName", "viewQualifiedName" +) +CosmosMongoDBCollection.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +CosmosMongoDBCollection.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +CosmosMongoDBCollection.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +CosmosMongoDBCollection.LAST_PROFILED_AT = NumericField( + "lastProfiledAt", "lastProfiledAt" +) +CosmosMongoDBCollection.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +CosmosMongoDBCollection.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +CosmosMongoDBCollection.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +CosmosMongoDBCollection.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +CosmosMongoDBCollection.ANOMALO_CHECKS = RelationField("anomaloChecks") +CosmosMongoDBCollection.APPLICATION = RelationField("application") +CosmosMongoDBCollection.APPLICATION_FIELD = RelationField("applicationField") +CosmosMongoDBCollection.COSMOS_MONGO_DB_DATABASE = RelationField( + "cosmosMongoDBDatabase" +) +CosmosMongoDBCollection.COLUMNS = RelationField("columns") +CosmosMongoDBCollection.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +CosmosMongoDBCollection.INPUT_PORT_DATA_PRODUCTS = RelationField( + "inputPortDataProducts" +) +CosmosMongoDBCollection.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +CosmosMongoDBCollection.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +CosmosMongoDBCollection.METRICS = RelationField("metrics") +CosmosMongoDBCollection.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +CosmosMongoDBCollection.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +CosmosMongoDBCollection.DBT_MODELS = RelationField("dbtModels") +CosmosMongoDBCollection.SQL_DBT_MODELS = RelationField("sqlDbtModels") +CosmosMongoDBCollection.DBT_TESTS = RelationField("dbtTests") +CosmosMongoDBCollection.DBT_SOURCES = RelationField("dbtSources") +CosmosMongoDBCollection.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +CosmosMongoDBCollection.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +CosmosMongoDBCollection.MEANINGS = RelationField("meanings") +CosmosMongoDBCollection.MONGO_DB_DATABASE = RelationField("mongoDBDatabase") +CosmosMongoDBCollection.MONGO_DB_COLUMNS = RelationField("mongoDBColumns") +CosmosMongoDBCollection.MC_MONITORS = RelationField("mcMonitors") +CosmosMongoDBCollection.MC_INCIDENTS = RelationField("mcIncidents") +CosmosMongoDBCollection.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +CosmosMongoDBCollection.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +CosmosMongoDBCollection.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +CosmosMongoDBCollection.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +CosmosMongoDBCollection.USER_DEF_RELATIONSHIP_TO = RelationField( + "userDefRelationshipTo" +) +CosmosMongoDBCollection.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +CosmosMongoDBCollection.FILES = RelationField("files") +CosmosMongoDBCollection.LINKS = RelationField("links") +CosmosMongoDBCollection.README = RelationField("readme") +CosmosMongoDBCollection.QUERIES = RelationField("queries") +CosmosMongoDBCollection.ATLAN_SCHEMA = RelationField("atlanSchema") +CosmosMongoDBCollection.DIMENSIONS = RelationField("dimensions") +CosmosMongoDBCollection.FACTS = RelationField("facts") +CosmosMongoDBCollection.PARTITIONS = RelationField("partitions") +CosmosMongoDBCollection.SCHEMA_REGISTRY_SUBJECTS = RelationField( + "schemaRegistrySubjects" +) +CosmosMongoDBCollection.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +CosmosMongoDBCollection.SODA_CHECKS = RelationField("sodaChecks") +CosmosMongoDBCollection.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +CosmosMongoDBCollection.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/cosmos_mongo_db_database.py b/pyatlan_v9/model/assets/cosmos_mongo_db_database.py new file mode 100644 index 000000000..5759a7727 --- /dev/null +++ b/pyatlan_v9/model/assets/cosmos_mongo_db_database.py @@ -0,0 +1,989 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +CosmosMongoDBDatabase asset model with flattened inheritance. + +This module provides: +- CosmosMongoDBDatabase: Flat asset class (easy to use) +- CosmosMongoDBDatabaseAttributes: Nested attributes struct (extends AssetAttributes) +- CosmosMongoDBDatabaseNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .fabric_related import RelatedFabricWorkspace +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .mongo_db_related import RelatedMongoDBCollection +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .snowflake_related import RelatedSnowflakeSemanticLogicalTable +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from .sql_related import RelatedSchema +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .cosmos_mongo_db_related import ( + RelatedCosmosMongoDBAccount, + RelatedCosmosMongoDBCollection, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class CosmosMongoDBDatabase(Asset): + """ + Instance of a Cosmos MongoDB database in Atlan. + """ + + COSMOS_MONGO_DB_ACCOUNT_QUALIFIED_NAME: ClassVar[Any] = None + NO_SQL_SCHEMA_DEFINITION: ClassVar[Any] = None + MONGO_DB_DATABASE_COLLECTION_COUNT: ClassVar[Any] = None + SCHEMA_COUNT: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + COSMOS_MONGO_DB_ACCOUNT: ClassVar[Any] = None + COSMOS_MONGO_DB_COLLECTIONS: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + FABRIC_WORKSPACE: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MONGO_DB_COLLECTIONS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMAS: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "CosmosMongoDBDatabase" + + cosmos_mongo_db_account_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="cosmosMongoDBAccountQualifiedName" + ) + """Unique name of the account in which this database exists.""" + + no_sql_schema_definition: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="noSQLSchemaDefinition" + ) + """Represents attributes for describing the key schema for the table and indexes.""" + + mongo_db_database_collection_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBDatabaseCollectionCount" + ) + """Number of collections in the database.""" + + schema_count: Union[int, None, UnsetType] = UNSET + """Number of schemas in this database.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cosmos_mongo_db_account: Union[RelatedCosmosMongoDBAccount, None, UnsetType] = ( + msgspec.field(default=UNSET, name="cosmosMongoDBAccount") + ) + """Account in which the database exists.""" + + cosmos_mongo_db_collections: Union[ + List[RelatedCosmosMongoDBCollection], None, UnsetType + ] = msgspec.field(default=UNSET, name="cosmosMongoDBCollections") + """Collections that exist within this database.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + fabric_workspace: Union[RelatedFabricWorkspace, None, UnsetType] = UNSET + """Workspace containing the database.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mongo_db_collections: Union[List[RelatedMongoDBCollection], None, UnsetType] = ( + msgspec.field(default=UNSET, name="mongoDBCollections") + ) + """Collections that exist within this database.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schemas: Union[List[RelatedSchema], None, UnsetType] = UNSET + """Schemas that exist within this database.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "CosmosMongoDBDatabase" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _cosmos_mongo_db_database_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> CosmosMongoDBDatabase: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + CosmosMongoDBDatabase instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _cosmos_mongo_db_database_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class CosmosMongoDBDatabaseAttributes(AssetAttributes): + """CosmosMongoDBDatabase-specific attributes for nested API format.""" + + cosmos_mongo_db_account_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="cosmosMongoDBAccountQualifiedName" + ) + """Unique name of the account in which this database exists.""" + + no_sql_schema_definition: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="noSQLSchemaDefinition" + ) + """Represents attributes for describing the key schema for the table and indexes.""" + + mongo_db_database_collection_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBDatabaseCollectionCount" + ) + """Number of collections in the database.""" + + schema_count: Union[int, None, UnsetType] = UNSET + """Number of schemas in this database.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + +class CosmosMongoDBDatabaseRelationshipAttributes(AssetRelationshipAttributes): + """CosmosMongoDBDatabase-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cosmos_mongo_db_account: Union[RelatedCosmosMongoDBAccount, None, UnsetType] = ( + msgspec.field(default=UNSET, name="cosmosMongoDBAccount") + ) + """Account in which the database exists.""" + + cosmos_mongo_db_collections: Union[ + List[RelatedCosmosMongoDBCollection], None, UnsetType + ] = msgspec.field(default=UNSET, name="cosmosMongoDBCollections") + """Collections that exist within this database.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + fabric_workspace: Union[RelatedFabricWorkspace, None, UnsetType] = UNSET + """Workspace containing the database.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mongo_db_collections: Union[List[RelatedMongoDBCollection], None, UnsetType] = ( + msgspec.field(default=UNSET, name="mongoDBCollections") + ) + """Collections that exist within this database.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schemas: Union[List[RelatedSchema], None, UnsetType] = UNSET + """Schemas that exist within this database.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class CosmosMongoDBDatabaseNested(AssetNested): + """CosmosMongoDBDatabase in nested API format for high-performance serialization.""" + + attributes: Union[CosmosMongoDBDatabaseAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + CosmosMongoDBDatabaseRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + CosmosMongoDBDatabaseRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + CosmosMongoDBDatabaseRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_COSMOS_MONGO_DB_DATABASE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "cosmos_mongo_db_account", + "cosmos_mongo_db_collections", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "fabric_workspace", + "meanings", + "mongo_db_collections", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schemas", + "schema_registry_subjects", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_cosmos_mongo_db_database_attrs( + attrs: CosmosMongoDBDatabaseAttributes, obj: CosmosMongoDBDatabase +) -> None: + """Populate CosmosMongoDBDatabase-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.cosmos_mongo_db_account_qualified_name = ( + obj.cosmos_mongo_db_account_qualified_name + ) + attrs.no_sql_schema_definition = obj.no_sql_schema_definition + attrs.mongo_db_database_collection_count = obj.mongo_db_database_collection_count + attrs.schema_count = obj.schema_count + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + + +def _extract_cosmos_mongo_db_database_attrs( + attrs: CosmosMongoDBDatabaseAttributes, +) -> dict: + """Extract all CosmosMongoDBDatabase attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["cosmos_mongo_db_account_qualified_name"] = ( + attrs.cosmos_mongo_db_account_qualified_name + ) + result["no_sql_schema_definition"] = attrs.no_sql_schema_definition + result["mongo_db_database_collection_count"] = ( + attrs.mongo_db_database_collection_count + ) + result["schema_count"] = attrs.schema_count + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _cosmos_mongo_db_database_to_nested( + cosmos_mongo_db_database: CosmosMongoDBDatabase, +) -> CosmosMongoDBDatabaseNested: + """Convert flat CosmosMongoDBDatabase to nested format.""" + attrs = CosmosMongoDBDatabaseAttributes() + _populate_cosmos_mongo_db_database_attrs(attrs, cosmos_mongo_db_database) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + cosmos_mongo_db_database, + _COSMOS_MONGO_DB_DATABASE_REL_FIELDS, + CosmosMongoDBDatabaseRelationshipAttributes, + ) + return CosmosMongoDBDatabaseNested( + guid=cosmos_mongo_db_database.guid, + type_name=cosmos_mongo_db_database.type_name, + status=cosmos_mongo_db_database.status, + version=cosmos_mongo_db_database.version, + create_time=cosmos_mongo_db_database.create_time, + update_time=cosmos_mongo_db_database.update_time, + created_by=cosmos_mongo_db_database.created_by, + updated_by=cosmos_mongo_db_database.updated_by, + classifications=cosmos_mongo_db_database.classifications, + classification_names=cosmos_mongo_db_database.classification_names, + meanings=cosmos_mongo_db_database.meanings, + labels=cosmos_mongo_db_database.labels, + business_attributes=cosmos_mongo_db_database.business_attributes, + custom_attributes=cosmos_mongo_db_database.custom_attributes, + pending_tasks=cosmos_mongo_db_database.pending_tasks, + proxy=cosmos_mongo_db_database.proxy, + is_incomplete=cosmos_mongo_db_database.is_incomplete, + provenance_type=cosmos_mongo_db_database.provenance_type, + home_id=cosmos_mongo_db_database.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _cosmos_mongo_db_database_from_nested( + nested: CosmosMongoDBDatabaseNested, +) -> CosmosMongoDBDatabase: + """Convert nested format to flat CosmosMongoDBDatabase.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else CosmosMongoDBDatabaseAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _COSMOS_MONGO_DB_DATABASE_REL_FIELDS, + CosmosMongoDBDatabaseRelationshipAttributes, + ) + return CosmosMongoDBDatabase( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_cosmos_mongo_db_database_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _cosmos_mongo_db_database_to_nested_bytes( + cosmos_mongo_db_database: CosmosMongoDBDatabase, serde: Serde +) -> bytes: + """Convert flat CosmosMongoDBDatabase to nested JSON bytes.""" + return serde.encode(_cosmos_mongo_db_database_to_nested(cosmos_mongo_db_database)) + + +def _cosmos_mongo_db_database_from_nested_bytes( + data: bytes, serde: Serde +) -> CosmosMongoDBDatabase: + """Convert nested JSON bytes to flat CosmosMongoDBDatabase.""" + nested = serde.decode(data, CosmosMongoDBDatabaseNested) + return _cosmos_mongo_db_database_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +CosmosMongoDBDatabase.COSMOS_MONGO_DB_ACCOUNT_QUALIFIED_NAME = KeywordTextField( + "cosmosMongoDBAccountQualifiedName", + "cosmosMongoDBAccountQualifiedName", + "cosmosMongoDBAccountQualifiedName.text", +) +CosmosMongoDBDatabase.NO_SQL_SCHEMA_DEFINITION = KeywordField( + "noSQLSchemaDefinition", "noSQLSchemaDefinition" +) +CosmosMongoDBDatabase.MONGO_DB_DATABASE_COLLECTION_COUNT = NumericField( + "mongoDBDatabaseCollectionCount", "mongoDBDatabaseCollectionCount" +) +CosmosMongoDBDatabase.SCHEMA_COUNT = NumericField("schemaCount", "schemaCount") +CosmosMongoDBDatabase.QUERY_COUNT = NumericField("queryCount", "queryCount") +CosmosMongoDBDatabase.QUERY_USER_COUNT = NumericField( + "queryUserCount", "queryUserCount" +) +CosmosMongoDBDatabase.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +CosmosMongoDBDatabase.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +CosmosMongoDBDatabase.DATABASE_NAME = KeywordField("databaseName", "databaseName") +CosmosMongoDBDatabase.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +CosmosMongoDBDatabase.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +CosmosMongoDBDatabase.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +CosmosMongoDBDatabase.TABLE_NAME = KeywordField("tableName", "tableName") +CosmosMongoDBDatabase.TABLE_QUALIFIED_NAME = KeywordField( + "tableQualifiedName", "tableQualifiedName" +) +CosmosMongoDBDatabase.VIEW_NAME = KeywordField("viewName", "viewName") +CosmosMongoDBDatabase.VIEW_QUALIFIED_NAME = KeywordField( + "viewQualifiedName", "viewQualifiedName" +) +CosmosMongoDBDatabase.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +CosmosMongoDBDatabase.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +CosmosMongoDBDatabase.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +CosmosMongoDBDatabase.LAST_PROFILED_AT = NumericField( + "lastProfiledAt", "lastProfiledAt" +) +CosmosMongoDBDatabase.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +CosmosMongoDBDatabase.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +CosmosMongoDBDatabase.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +CosmosMongoDBDatabase.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +CosmosMongoDBDatabase.ANOMALO_CHECKS = RelationField("anomaloChecks") +CosmosMongoDBDatabase.APPLICATION = RelationField("application") +CosmosMongoDBDatabase.APPLICATION_FIELD = RelationField("applicationField") +CosmosMongoDBDatabase.COSMOS_MONGO_DB_ACCOUNT = RelationField("cosmosMongoDBAccount") +CosmosMongoDBDatabase.COSMOS_MONGO_DB_COLLECTIONS = RelationField( + "cosmosMongoDBCollections" +) +CosmosMongoDBDatabase.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +CosmosMongoDBDatabase.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +CosmosMongoDBDatabase.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +CosmosMongoDBDatabase.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +CosmosMongoDBDatabase.METRICS = RelationField("metrics") +CosmosMongoDBDatabase.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +CosmosMongoDBDatabase.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +CosmosMongoDBDatabase.DBT_MODELS = RelationField("dbtModels") +CosmosMongoDBDatabase.SQL_DBT_MODELS = RelationField("sqlDbtModels") +CosmosMongoDBDatabase.DBT_TESTS = RelationField("dbtTests") +CosmosMongoDBDatabase.DBT_SOURCES = RelationField("dbtSources") +CosmosMongoDBDatabase.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +CosmosMongoDBDatabase.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +CosmosMongoDBDatabase.FABRIC_WORKSPACE = RelationField("fabricWorkspace") +CosmosMongoDBDatabase.MEANINGS = RelationField("meanings") +CosmosMongoDBDatabase.MONGO_DB_COLLECTIONS = RelationField("mongoDBCollections") +CosmosMongoDBDatabase.MC_MONITORS = RelationField("mcMonitors") +CosmosMongoDBDatabase.MC_INCIDENTS = RelationField("mcIncidents") +CosmosMongoDBDatabase.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +CosmosMongoDBDatabase.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +CosmosMongoDBDatabase.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +CosmosMongoDBDatabase.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +CosmosMongoDBDatabase.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +CosmosMongoDBDatabase.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +CosmosMongoDBDatabase.FILES = RelationField("files") +CosmosMongoDBDatabase.LINKS = RelationField("links") +CosmosMongoDBDatabase.README = RelationField("readme") +CosmosMongoDBDatabase.SCHEMAS = RelationField("schemas") +CosmosMongoDBDatabase.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +CosmosMongoDBDatabase.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +CosmosMongoDBDatabase.SODA_CHECKS = RelationField("sodaChecks") +CosmosMongoDBDatabase.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +CosmosMongoDBDatabase.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/cosmos_mongo_db_related.py b/pyatlan_v9/model/assets/cosmos_mongo_db_related.py new file mode 100644 index 000000000..9519096eb --- /dev/null +++ b/pyatlan_v9/model/assets/cosmos_mongo_db_related.py @@ -0,0 +1,183 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for CosmosMongoDB module. + +This module contains all Related{Type} classes for the CosmosMongoDB type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedNoSQL +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedCosmosMongoDB", + "RelatedCosmosMongoDBAccount", + "RelatedCosmosMongoDBDatabase", + "RelatedCosmosMongoDBCollection", +] + + +class RelatedCosmosMongoDB(RelatedNoSQL): + """ + Related entity reference for CosmosMongoDB assets. + + Extends RelatedNoSQL with CosmosMongoDB-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "CosmosMongoDB" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "CosmosMongoDB" + + +class RelatedCosmosMongoDBAccount(RelatedCosmosMongoDB): + """ + Related entity reference for CosmosMongoDBAccount assets. + + Extends RelatedCosmosMongoDB with CosmosMongoDBAccount-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "CosmosMongoDBAccount" so it serializes correctly + + cosmos_mongo_db_account_instance_id: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="cosmosMongoDBAccountInstanceId" + ) + """The unique identifier for the Cosmos MongoDB account.""" + + cosmos_mongo_db_database_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="cosmosMongoDBDatabaseCount" + ) + """Number of databases in this Cosmos MongoDB account.""" + + cosmos_mongo_db_account_type: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="cosmosMongoDBAccountType" + ) + """The type of the Cosmos MongoDB account, such as RU or VCORE.""" + + cosmos_mongo_db_account_subscription_id: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="cosmosMongoDBAccountSubscriptionId") + ) + """The ID of the subscription to which the Cosmos MongoDB account belongs.""" + + cosmos_mongo_db_account_resource_group: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="cosmosMongoDBAccountResourceGroup" + ) + """The resource group that contains the Cosmos MongoDB account.""" + + cosmos_mongo_db_account_document_endpoint: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="cosmosMongoDBAccountDocumentEndpoint") + ) + """The Document Endpoint URL for the Cosmos MongoDB account.""" + + cosmos_mongo_db_account_mongo_endpoint: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="cosmosMongoDBAccountMongoEndpoint" + ) + """The MongoDB connection endpoint for the Cosmos MongoDB account.""" + + cosmos_mongo_db_account_public_network_access: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="cosmosMongoDBAccountPublicNetworkAccess") + ) + """The status of public network access for the Cosmos MongoDB account.""" + + cosmos_mongo_db_account_enable_automatic_failover: Union[bool, None, UnsetType] = ( + msgspec.field(default=UNSET, name="cosmosMongoDBAccountEnableAutomaticFailover") + ) + """Indicates whether automatic failover is enabled for the Cosmos MongoDB account.""" + + cosmos_mongo_db_account_enable_multiple_write_locations: Union[ + bool, None, UnsetType + ] = msgspec.field( + default=UNSET, name="cosmosMongoDBAccountEnableMultipleWriteLocations" + ) + """Indicates whether multiple write locations are enabled for the Cosmos MongoDB account.""" + + cosmos_mongo_db_account_enable_partition_key_monitor: Union[ + bool, None, UnsetType + ] = msgspec.field( + default=UNSET, name="cosmosMongoDBAccountEnablePartitionKeyMonitor" + ) + """Indicates whether partition key monitoring is enabled for the Cosmos MongoDB account.""" + + cosmos_mongo_db_account_is_virtual_network_filter_enabled: Union[ + bool, None, UnsetType + ] = msgspec.field( + default=UNSET, name="cosmosMongoDBAccountIsVirtualNetworkFilterEnabled" + ) + """Indicates whether the virtual network filter is enabled for the Cosmos MongoDB account.""" + + cosmos_mongo_db_account_consistency_policy: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="cosmosMongoDBAccountConsistencyPolicy") + ) + """The consistency policy configured for the Cosmos MongoDB account.""" + + cosmos_mongo_db_account_locations: Union[List[str], None, UnsetType] = ( + msgspec.field(default=UNSET, name="cosmosMongoDBAccountLocations") + ) + """The locations where the Cosmos MongoDB account is available.""" + + cosmos_mongo_db_account_read_locations: Union[List[str], None, UnsetType] = ( + msgspec.field(default=UNSET, name="cosmosMongoDBAccountReadLocations") + ) + """The read locations configured for the Cosmos MongoDB account.""" + + cosmos_mongo_db_account_write_locations: Union[List[str], None, UnsetType] = ( + msgspec.field(default=UNSET, name="cosmosMongoDBAccountWriteLocations") + ) + """The write locations configured for the Cosmos MongoDB account.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "CosmosMongoDBAccount" + + +class RelatedCosmosMongoDBDatabase(RelatedCosmosMongoDB): + """ + Related entity reference for CosmosMongoDBDatabase assets. + + Extends RelatedCosmosMongoDB with CosmosMongoDBDatabase-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "CosmosMongoDBDatabase" so it serializes correctly + + cosmos_mongo_db_account_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="cosmosMongoDBAccountQualifiedName" + ) + """Unique name of the account in which this database exists.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "CosmosMongoDBDatabase" + + +class RelatedCosmosMongoDBCollection(RelatedCosmosMongoDB): + """ + Related entity reference for CosmosMongoDBCollection assets. + + Extends RelatedCosmosMongoDB with CosmosMongoDBCollection-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "CosmosMongoDBCollection" so it serializes correctly + + cosmos_mongo_db_database_qualified_name: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="cosmosMongoDBDatabaseQualifiedName") + ) + """Unique name of the database in which this collection exists.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "CosmosMongoDBCollection" diff --git a/pyatlan_v9/model/assets/cube.py b/pyatlan_v9/model/assets/cube.py new file mode 100644 index 000000000..0f6ffd67c --- /dev/null +++ b/pyatlan_v9/model/assets/cube.py @@ -0,0 +1,612 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Cube asset model with flattened inheritance. + +This module provides: +- Cube: Flat asset class (easy to use) +- CubeAttributes: Nested attributes struct (extends AssetAttributes) +- CubeNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .cube_related import RelatedCubeDimension + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Cube(Asset): + """ + Instance of a Cube in Atlan. + """ + + CUBE_DIMENSION_COUNT: ClassVar[Any] = None + CUBE_NAME: ClassVar[Any] = None + CUBE_QUALIFIED_NAME: ClassVar[Any] = None + CUBE_DIMENSION_NAME: ClassVar[Any] = None + CUBE_DIMENSION_QUALIFIED_NAME: ClassVar[Any] = None + CUBE_HIERARCHY_NAME: ClassVar[Any] = None + CUBE_HIERARCHY_QUALIFIED_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + CUBE_DIMENSIONS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Cube" + + cube_dimension_count: Union[int, None, UnsetType] = UNSET + """Number of dimensions in the cube.""" + + cube_name: Union[str, None, UnsetType] = UNSET + """Simple name of the cube in which this asset exists, or empty if it is itself a cube.""" + + cube_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the cube in which this asset exists, or empty if it is itself a cube.""" + + cube_dimension_name: Union[str, None, UnsetType] = UNSET + """Simple name of the cube dimension in which this asset exists, or empty if it is itself a dimension.""" + + cube_dimension_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the cube dimension in which this asset exists, or empty if it is itself a dimension.""" + + cube_hierarchy_name: Union[str, None, UnsetType] = UNSET + """Simple name of the dimension hierarchy in which this asset exists, or empty if it is itself a hierarchy.""" + + cube_hierarchy_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dimension hierarchy in which this asset exists, or empty if it is itself a hierarchy.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + cube_dimensions: Union[List[RelatedCubeDimension], None, UnsetType] = UNSET + """Individual dimensions contained in the cube.""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Cube" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _cube_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Cube: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Cube instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _cube_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class CubeAttributes(AssetAttributes): + """Cube-specific attributes for nested API format.""" + + cube_dimension_count: Union[int, None, UnsetType] = UNSET + """Number of dimensions in the cube.""" + + cube_name: Union[str, None, UnsetType] = UNSET + """Simple name of the cube in which this asset exists, or empty if it is itself a cube.""" + + cube_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the cube in which this asset exists, or empty if it is itself a cube.""" + + cube_dimension_name: Union[str, None, UnsetType] = UNSET + """Simple name of the cube dimension in which this asset exists, or empty if it is itself a dimension.""" + + cube_dimension_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the cube dimension in which this asset exists, or empty if it is itself a dimension.""" + + cube_hierarchy_name: Union[str, None, UnsetType] = UNSET + """Simple name of the dimension hierarchy in which this asset exists, or empty if it is itself a hierarchy.""" + + cube_hierarchy_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dimension hierarchy in which this asset exists, or empty if it is itself a hierarchy.""" + + +class CubeRelationshipAttributes(AssetRelationshipAttributes): + """Cube-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + cube_dimensions: Union[List[RelatedCubeDimension], None, UnsetType] = UNSET + """Individual dimensions contained in the cube.""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class CubeNested(AssetNested): + """Cube in nested API format for high-performance serialization.""" + + attributes: Union[CubeAttributes, UnsetType] = UNSET + relationship_attributes: Union[CubeRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[CubeRelationshipAttributes, UnsetType] = UNSET + remove_relationship_attributes: Union[CubeRelationshipAttributes, UnsetType] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_CUBE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "cube_dimensions", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_cube_attrs(attrs: CubeAttributes, obj: Cube) -> None: + """Populate Cube-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.cube_dimension_count = obj.cube_dimension_count + attrs.cube_name = obj.cube_name + attrs.cube_qualified_name = obj.cube_qualified_name + attrs.cube_dimension_name = obj.cube_dimension_name + attrs.cube_dimension_qualified_name = obj.cube_dimension_qualified_name + attrs.cube_hierarchy_name = obj.cube_hierarchy_name + attrs.cube_hierarchy_qualified_name = obj.cube_hierarchy_qualified_name + + +def _extract_cube_attrs(attrs: CubeAttributes) -> dict: + """Extract all Cube attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["cube_dimension_count"] = attrs.cube_dimension_count + result["cube_name"] = attrs.cube_name + result["cube_qualified_name"] = attrs.cube_qualified_name + result["cube_dimension_name"] = attrs.cube_dimension_name + result["cube_dimension_qualified_name"] = attrs.cube_dimension_qualified_name + result["cube_hierarchy_name"] = attrs.cube_hierarchy_name + result["cube_hierarchy_qualified_name"] = attrs.cube_hierarchy_qualified_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _cube_to_nested(cube: Cube) -> CubeNested: + """Convert flat Cube to nested format.""" + attrs = CubeAttributes() + _populate_cube_attrs(attrs, cube) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + cube, _CUBE_REL_FIELDS, CubeRelationshipAttributes + ) + return CubeNested( + guid=cube.guid, + type_name=cube.type_name, + status=cube.status, + version=cube.version, + create_time=cube.create_time, + update_time=cube.update_time, + created_by=cube.created_by, + updated_by=cube.updated_by, + classifications=cube.classifications, + classification_names=cube.classification_names, + meanings=cube.meanings, + labels=cube.labels, + business_attributes=cube.business_attributes, + custom_attributes=cube.custom_attributes, + pending_tasks=cube.pending_tasks, + proxy=cube.proxy, + is_incomplete=cube.is_incomplete, + provenance_type=cube.provenance_type, + home_id=cube.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _cube_from_nested(nested: CubeNested) -> Cube: + """Convert nested format to flat Cube.""" + attrs = nested.attributes if nested.attributes is not UNSET else CubeAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _CUBE_REL_FIELDS, + CubeRelationshipAttributes, + ) + return Cube( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_cube_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _cube_to_nested_bytes(cube: Cube, serde: Serde) -> bytes: + """Convert flat Cube to nested JSON bytes.""" + return serde.encode(_cube_to_nested(cube)) + + +def _cube_from_nested_bytes(data: bytes, serde: Serde) -> Cube: + """Convert nested JSON bytes to flat Cube.""" + nested = serde.decode(data, CubeNested) + return _cube_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +Cube.CUBE_DIMENSION_COUNT = NumericField("cubeDimensionCount", "cubeDimensionCount") +Cube.CUBE_NAME = KeywordTextField("cubeName", "cubeName", "cubeName.text") +Cube.CUBE_QUALIFIED_NAME = KeywordField("cubeQualifiedName", "cubeQualifiedName") +Cube.CUBE_DIMENSION_NAME = KeywordTextField( + "cubeDimensionName", "cubeDimensionName", "cubeDimensionName.text" +) +Cube.CUBE_DIMENSION_QUALIFIED_NAME = KeywordField( + "cubeDimensionQualifiedName", "cubeDimensionQualifiedName" +) +Cube.CUBE_HIERARCHY_NAME = KeywordTextField( + "cubeHierarchyName", "cubeHierarchyName", "cubeHierarchyName.text" +) +Cube.CUBE_HIERARCHY_QUALIFIED_NAME = KeywordField( + "cubeHierarchyQualifiedName", "cubeHierarchyQualifiedName" +) +Cube.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Cube.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Cube.ANOMALO_CHECKS = RelationField("anomaloChecks") +Cube.APPLICATION = RelationField("application") +Cube.APPLICATION_FIELD = RelationField("applicationField") +Cube.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Cube.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Cube.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Cube.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Cube.METRICS = RelationField("metrics") +Cube.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Cube.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Cube.MEANINGS = RelationField("meanings") +Cube.MC_MONITORS = RelationField("mcMonitors") +Cube.MC_INCIDENTS = RelationField("mcIncidents") +Cube.CUBE_DIMENSIONS = RelationField("cubeDimensions") +Cube.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Cube.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Cube.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Cube.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Cube.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Cube.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Cube.FILES = RelationField("files") +Cube.LINKS = RelationField("links") +Cube.README = RelationField("readme") +Cube.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Cube.SODA_CHECKS = RelationField("sodaChecks") +Cube.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Cube.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/cube_dimension.py b/pyatlan_v9/model/assets/cube_dimension.py new file mode 100644 index 000000000..ebf1c5fea --- /dev/null +++ b/pyatlan_v9/model/assets/cube_dimension.py @@ -0,0 +1,655 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +CubeDimension asset model with flattened inheritance. + +This module provides: +- CubeDimension: Flat asset class (easy to use) +- CubeDimensionAttributes: Nested attributes struct (extends AssetAttributes) +- CubeDimensionNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .cube_related import RelatedCube, RelatedCubeDimension, RelatedCubeHierarchy + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class CubeDimension(Asset): + """ + Instance of a cube dimension in Atlan. + """ + + CUBE_HIERARCHY_COUNT: ClassVar[Any] = None + CUBE_NAME: ClassVar[Any] = None + CUBE_QUALIFIED_NAME: ClassVar[Any] = None + CUBE_DIMENSION_NAME: ClassVar[Any] = None + CUBE_DIMENSION_QUALIFIED_NAME: ClassVar[Any] = None + CUBE_HIERARCHY_NAME: ClassVar[Any] = None + CUBE_HIERARCHY_QUALIFIED_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + CUBE_DIMENSIONS: ClassVar[Any] = None + CUBE: ClassVar[Any] = None + CUBE_HIERARCHIES: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "CubeDimension" + + cube_hierarchy_count: Union[int, None, UnsetType] = UNSET + """Number of hierarchies in the cube dimension.""" + + cube_name: Union[str, None, UnsetType] = UNSET + """Simple name of the cube in which this asset exists, or empty if it is itself a cube.""" + + cube_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the cube in which this asset exists, or empty if it is itself a cube.""" + + cube_dimension_name: Union[str, None, UnsetType] = UNSET + """Simple name of the cube dimension in which this asset exists, or empty if it is itself a dimension.""" + + cube_dimension_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the cube dimension in which this asset exists, or empty if it is itself a dimension.""" + + cube_hierarchy_name: Union[str, None, UnsetType] = UNSET + """Simple name of the dimension hierarchy in which this asset exists, or empty if it is itself a hierarchy.""" + + cube_hierarchy_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dimension hierarchy in which this asset exists, or empty if it is itself a hierarchy.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + cube_dimensions: Union[List[RelatedCubeDimension], None, UnsetType] = UNSET + """Individual dimensions contained in the cube.""" + + cube: Union[RelatedCube, None, UnsetType] = UNSET + """Cube containing the dimension.""" + + cube_hierarchies: Union[List[RelatedCubeHierarchy], None, UnsetType] = UNSET + """Individual hierarchies contained in the dimension.""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "CubeDimension" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _cube_dimension_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> CubeDimension: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + CubeDimension instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _cube_dimension_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class CubeDimensionAttributes(AssetAttributes): + """CubeDimension-specific attributes for nested API format.""" + + cube_hierarchy_count: Union[int, None, UnsetType] = UNSET + """Number of hierarchies in the cube dimension.""" + + cube_name: Union[str, None, UnsetType] = UNSET + """Simple name of the cube in which this asset exists, or empty if it is itself a cube.""" + + cube_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the cube in which this asset exists, or empty if it is itself a cube.""" + + cube_dimension_name: Union[str, None, UnsetType] = UNSET + """Simple name of the cube dimension in which this asset exists, or empty if it is itself a dimension.""" + + cube_dimension_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the cube dimension in which this asset exists, or empty if it is itself a dimension.""" + + cube_hierarchy_name: Union[str, None, UnsetType] = UNSET + """Simple name of the dimension hierarchy in which this asset exists, or empty if it is itself a hierarchy.""" + + cube_hierarchy_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dimension hierarchy in which this asset exists, or empty if it is itself a hierarchy.""" + + +class CubeDimensionRelationshipAttributes(AssetRelationshipAttributes): + """CubeDimension-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + cube_dimensions: Union[List[RelatedCubeDimension], None, UnsetType] = UNSET + """Individual dimensions contained in the cube.""" + + cube: Union[RelatedCube, None, UnsetType] = UNSET + """Cube containing the dimension.""" + + cube_hierarchies: Union[List[RelatedCubeHierarchy], None, UnsetType] = UNSET + """Individual hierarchies contained in the dimension.""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class CubeDimensionNested(AssetNested): + """CubeDimension in nested API format for high-performance serialization.""" + + attributes: Union[CubeDimensionAttributes, UnsetType] = UNSET + relationship_attributes: Union[CubeDimensionRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + CubeDimensionRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + CubeDimensionRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_CUBE_DIMENSION_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "cube_dimensions", + "cube", + "cube_hierarchies", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_cube_dimension_attrs( + attrs: CubeDimensionAttributes, obj: CubeDimension +) -> None: + """Populate CubeDimension-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.cube_hierarchy_count = obj.cube_hierarchy_count + attrs.cube_name = obj.cube_name + attrs.cube_qualified_name = obj.cube_qualified_name + attrs.cube_dimension_name = obj.cube_dimension_name + attrs.cube_dimension_qualified_name = obj.cube_dimension_qualified_name + attrs.cube_hierarchy_name = obj.cube_hierarchy_name + attrs.cube_hierarchy_qualified_name = obj.cube_hierarchy_qualified_name + + +def _extract_cube_dimension_attrs(attrs: CubeDimensionAttributes) -> dict: + """Extract all CubeDimension attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["cube_hierarchy_count"] = attrs.cube_hierarchy_count + result["cube_name"] = attrs.cube_name + result["cube_qualified_name"] = attrs.cube_qualified_name + result["cube_dimension_name"] = attrs.cube_dimension_name + result["cube_dimension_qualified_name"] = attrs.cube_dimension_qualified_name + result["cube_hierarchy_name"] = attrs.cube_hierarchy_name + result["cube_hierarchy_qualified_name"] = attrs.cube_hierarchy_qualified_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _cube_dimension_to_nested(cube_dimension: CubeDimension) -> CubeDimensionNested: + """Convert flat CubeDimension to nested format.""" + attrs = CubeDimensionAttributes() + _populate_cube_dimension_attrs(attrs, cube_dimension) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + cube_dimension, _CUBE_DIMENSION_REL_FIELDS, CubeDimensionRelationshipAttributes + ) + return CubeDimensionNested( + guid=cube_dimension.guid, + type_name=cube_dimension.type_name, + status=cube_dimension.status, + version=cube_dimension.version, + create_time=cube_dimension.create_time, + update_time=cube_dimension.update_time, + created_by=cube_dimension.created_by, + updated_by=cube_dimension.updated_by, + classifications=cube_dimension.classifications, + classification_names=cube_dimension.classification_names, + meanings=cube_dimension.meanings, + labels=cube_dimension.labels, + business_attributes=cube_dimension.business_attributes, + custom_attributes=cube_dimension.custom_attributes, + pending_tasks=cube_dimension.pending_tasks, + proxy=cube_dimension.proxy, + is_incomplete=cube_dimension.is_incomplete, + provenance_type=cube_dimension.provenance_type, + home_id=cube_dimension.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _cube_dimension_from_nested(nested: CubeDimensionNested) -> CubeDimension: + """Convert nested format to flat CubeDimension.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else CubeDimensionAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _CUBE_DIMENSION_REL_FIELDS, + CubeDimensionRelationshipAttributes, + ) + return CubeDimension( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_cube_dimension_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _cube_dimension_to_nested_bytes( + cube_dimension: CubeDimension, serde: Serde +) -> bytes: + """Convert flat CubeDimension to nested JSON bytes.""" + return serde.encode(_cube_dimension_to_nested(cube_dimension)) + + +def _cube_dimension_from_nested_bytes(data: bytes, serde: Serde) -> CubeDimension: + """Convert nested JSON bytes to flat CubeDimension.""" + nested = serde.decode(data, CubeDimensionNested) + return _cube_dimension_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +CubeDimension.CUBE_HIERARCHY_COUNT = NumericField( + "cubeHierarchyCount", "cubeHierarchyCount" +) +CubeDimension.CUBE_NAME = KeywordTextField("cubeName", "cubeName", "cubeName.text") +CubeDimension.CUBE_QUALIFIED_NAME = KeywordField( + "cubeQualifiedName", "cubeQualifiedName" +) +CubeDimension.CUBE_DIMENSION_NAME = KeywordTextField( + "cubeDimensionName", "cubeDimensionName", "cubeDimensionName.text" +) +CubeDimension.CUBE_DIMENSION_QUALIFIED_NAME = KeywordField( + "cubeDimensionQualifiedName", "cubeDimensionQualifiedName" +) +CubeDimension.CUBE_HIERARCHY_NAME = KeywordTextField( + "cubeHierarchyName", "cubeHierarchyName", "cubeHierarchyName.text" +) +CubeDimension.CUBE_HIERARCHY_QUALIFIED_NAME = KeywordField( + "cubeHierarchyQualifiedName", "cubeHierarchyQualifiedName" +) +CubeDimension.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +CubeDimension.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +CubeDimension.ANOMALO_CHECKS = RelationField("anomaloChecks") +CubeDimension.APPLICATION = RelationField("application") +CubeDimension.APPLICATION_FIELD = RelationField("applicationField") +CubeDimension.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +CubeDimension.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +CubeDimension.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +CubeDimension.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +CubeDimension.METRICS = RelationField("metrics") +CubeDimension.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +CubeDimension.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +CubeDimension.MEANINGS = RelationField("meanings") +CubeDimension.MC_MONITORS = RelationField("mcMonitors") +CubeDimension.MC_INCIDENTS = RelationField("mcIncidents") +CubeDimension.CUBE_DIMENSIONS = RelationField("cubeDimensions") +CubeDimension.CUBE = RelationField("cube") +CubeDimension.CUBE_HIERARCHIES = RelationField("cubeHierarchies") +CubeDimension.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +CubeDimension.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +CubeDimension.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +CubeDimension.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +CubeDimension.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +CubeDimension.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +CubeDimension.FILES = RelationField("files") +CubeDimension.LINKS = RelationField("links") +CubeDimension.README = RelationField("readme") +CubeDimension.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +CubeDimension.SODA_CHECKS = RelationField("sodaChecks") +CubeDimension.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +CubeDimension.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/cube_field.py b/pyatlan_v9/model/assets/cube_field.py new file mode 100644 index 000000000..b3e2fd35a --- /dev/null +++ b/pyatlan_v9/model/assets/cube_field.py @@ -0,0 +1,714 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +CubeField asset model with flattened inheritance. + +This module provides: +- CubeField: Flat asset class (easy to use) +- CubeFieldAttributes: Nested attributes struct (extends AssetAttributes) +- CubeFieldNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .cube_related import RelatedCubeDimension, RelatedCubeField, RelatedCubeHierarchy + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class CubeField(Asset): + """ + Instance of a cube field in Atlan. + """ + + CUBE_PARENT_FIELD_NAME: ClassVar[Any] = None + CUBE_PARENT_FIELD_QUALIFIED_NAME: ClassVar[Any] = None + CUBE_FIELD_LEVEL: ClassVar[Any] = None + CUBE_FIELD_GENERATION: ClassVar[Any] = None + CUBE_FIELD_MEASURE_EXPRESSION: ClassVar[Any] = None + CUBE_SUB_FIELD_COUNT: ClassVar[Any] = None + CUBE_NAME: ClassVar[Any] = None + CUBE_QUALIFIED_NAME: ClassVar[Any] = None + CUBE_DIMENSION_NAME: ClassVar[Any] = None + CUBE_DIMENSION_QUALIFIED_NAME: ClassVar[Any] = None + CUBE_HIERARCHY_NAME: ClassVar[Any] = None + CUBE_HIERARCHY_QUALIFIED_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + CUBE_DIMENSIONS: ClassVar[Any] = None + CUBE_HIERARCHY: ClassVar[Any] = None + CUBE_NESTED_FIELDS: ClassVar[Any] = None + CUBE_PARENT_FIELD: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "CubeField" + + cube_parent_field_name: Union[str, None, UnsetType] = UNSET + """Name of the parent field in which this field is nested.""" + + cube_parent_field_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the parent field in which this field is nested.""" + + cube_field_level: Union[int, None, UnsetType] = UNSET + """Level of the field in the cube hierarchy.""" + + cube_field_generation: Union[int, None, UnsetType] = UNSET + """Generation of the field in the cube hierarchy.""" + + cube_field_measure_expression: Union[str, None, UnsetType] = UNSET + """Expression used to calculate this measure.""" + + cube_sub_field_count: Union[int, None, UnsetType] = UNSET + """Number of sub-fields that are direct children of this field.""" + + cube_name: Union[str, None, UnsetType] = UNSET + """Simple name of the cube in which this asset exists, or empty if it is itself a cube.""" + + cube_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the cube in which this asset exists, or empty if it is itself a cube.""" + + cube_dimension_name: Union[str, None, UnsetType] = UNSET + """Simple name of the cube dimension in which this asset exists, or empty if it is itself a dimension.""" + + cube_dimension_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the cube dimension in which this asset exists, or empty if it is itself a dimension.""" + + cube_hierarchy_name: Union[str, None, UnsetType] = UNSET + """Simple name of the dimension hierarchy in which this asset exists, or empty if it is itself a hierarchy.""" + + cube_hierarchy_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dimension hierarchy in which this asset exists, or empty if it is itself a hierarchy.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + cube_dimensions: Union[List[RelatedCubeDimension], None, UnsetType] = UNSET + """Individual dimensions contained in the cube.""" + + cube_hierarchy: Union[RelatedCubeHierarchy, None, UnsetType] = UNSET + """Hierarchy containing the field.""" + + cube_nested_fields: Union[List[RelatedCubeField], None, UnsetType] = UNSET + """Individual fields contained in the parent field.""" + + cube_parent_field: Union[RelatedCubeField, None, UnsetType] = UNSET + """Parent field containing the field.""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "CubeField" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _cube_field_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> CubeField: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + CubeField instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _cube_field_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class CubeFieldAttributes(AssetAttributes): + """CubeField-specific attributes for nested API format.""" + + cube_parent_field_name: Union[str, None, UnsetType] = UNSET + """Name of the parent field in which this field is nested.""" + + cube_parent_field_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the parent field in which this field is nested.""" + + cube_field_level: Union[int, None, UnsetType] = UNSET + """Level of the field in the cube hierarchy.""" + + cube_field_generation: Union[int, None, UnsetType] = UNSET + """Generation of the field in the cube hierarchy.""" + + cube_field_measure_expression: Union[str, None, UnsetType] = UNSET + """Expression used to calculate this measure.""" + + cube_sub_field_count: Union[int, None, UnsetType] = UNSET + """Number of sub-fields that are direct children of this field.""" + + cube_name: Union[str, None, UnsetType] = UNSET + """Simple name of the cube in which this asset exists, or empty if it is itself a cube.""" + + cube_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the cube in which this asset exists, or empty if it is itself a cube.""" + + cube_dimension_name: Union[str, None, UnsetType] = UNSET + """Simple name of the cube dimension in which this asset exists, or empty if it is itself a dimension.""" + + cube_dimension_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the cube dimension in which this asset exists, or empty if it is itself a dimension.""" + + cube_hierarchy_name: Union[str, None, UnsetType] = UNSET + """Simple name of the dimension hierarchy in which this asset exists, or empty if it is itself a hierarchy.""" + + cube_hierarchy_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dimension hierarchy in which this asset exists, or empty if it is itself a hierarchy.""" + + +class CubeFieldRelationshipAttributes(AssetRelationshipAttributes): + """CubeField-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + cube_dimensions: Union[List[RelatedCubeDimension], None, UnsetType] = UNSET + """Individual dimensions contained in the cube.""" + + cube_hierarchy: Union[RelatedCubeHierarchy, None, UnsetType] = UNSET + """Hierarchy containing the field.""" + + cube_nested_fields: Union[List[RelatedCubeField], None, UnsetType] = UNSET + """Individual fields contained in the parent field.""" + + cube_parent_field: Union[RelatedCubeField, None, UnsetType] = UNSET + """Parent field containing the field.""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class CubeFieldNested(AssetNested): + """CubeField in nested API format for high-performance serialization.""" + + attributes: Union[CubeFieldAttributes, UnsetType] = UNSET + relationship_attributes: Union[CubeFieldRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + CubeFieldRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + CubeFieldRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_CUBE_FIELD_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "cube_dimensions", + "cube_hierarchy", + "cube_nested_fields", + "cube_parent_field", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_cube_field_attrs(attrs: CubeFieldAttributes, obj: CubeField) -> None: + """Populate CubeField-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.cube_parent_field_name = obj.cube_parent_field_name + attrs.cube_parent_field_qualified_name = obj.cube_parent_field_qualified_name + attrs.cube_field_level = obj.cube_field_level + attrs.cube_field_generation = obj.cube_field_generation + attrs.cube_field_measure_expression = obj.cube_field_measure_expression + attrs.cube_sub_field_count = obj.cube_sub_field_count + attrs.cube_name = obj.cube_name + attrs.cube_qualified_name = obj.cube_qualified_name + attrs.cube_dimension_name = obj.cube_dimension_name + attrs.cube_dimension_qualified_name = obj.cube_dimension_qualified_name + attrs.cube_hierarchy_name = obj.cube_hierarchy_name + attrs.cube_hierarchy_qualified_name = obj.cube_hierarchy_qualified_name + + +def _extract_cube_field_attrs(attrs: CubeFieldAttributes) -> dict: + """Extract all CubeField attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["cube_parent_field_name"] = attrs.cube_parent_field_name + result["cube_parent_field_qualified_name"] = attrs.cube_parent_field_qualified_name + result["cube_field_level"] = attrs.cube_field_level + result["cube_field_generation"] = attrs.cube_field_generation + result["cube_field_measure_expression"] = attrs.cube_field_measure_expression + result["cube_sub_field_count"] = attrs.cube_sub_field_count + result["cube_name"] = attrs.cube_name + result["cube_qualified_name"] = attrs.cube_qualified_name + result["cube_dimension_name"] = attrs.cube_dimension_name + result["cube_dimension_qualified_name"] = attrs.cube_dimension_qualified_name + result["cube_hierarchy_name"] = attrs.cube_hierarchy_name + result["cube_hierarchy_qualified_name"] = attrs.cube_hierarchy_qualified_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _cube_field_to_nested(cube_field: CubeField) -> CubeFieldNested: + """Convert flat CubeField to nested format.""" + attrs = CubeFieldAttributes() + _populate_cube_field_attrs(attrs, cube_field) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + cube_field, _CUBE_FIELD_REL_FIELDS, CubeFieldRelationshipAttributes + ) + return CubeFieldNested( + guid=cube_field.guid, + type_name=cube_field.type_name, + status=cube_field.status, + version=cube_field.version, + create_time=cube_field.create_time, + update_time=cube_field.update_time, + created_by=cube_field.created_by, + updated_by=cube_field.updated_by, + classifications=cube_field.classifications, + classification_names=cube_field.classification_names, + meanings=cube_field.meanings, + labels=cube_field.labels, + business_attributes=cube_field.business_attributes, + custom_attributes=cube_field.custom_attributes, + pending_tasks=cube_field.pending_tasks, + proxy=cube_field.proxy, + is_incomplete=cube_field.is_incomplete, + provenance_type=cube_field.provenance_type, + home_id=cube_field.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _cube_field_from_nested(nested: CubeFieldNested) -> CubeField: + """Convert nested format to flat CubeField.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else CubeFieldAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _CUBE_FIELD_REL_FIELDS, + CubeFieldRelationshipAttributes, + ) + return CubeField( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_cube_field_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _cube_field_to_nested_bytes(cube_field: CubeField, serde: Serde) -> bytes: + """Convert flat CubeField to nested JSON bytes.""" + return serde.encode(_cube_field_to_nested(cube_field)) + + +def _cube_field_from_nested_bytes(data: bytes, serde: Serde) -> CubeField: + """Convert nested JSON bytes to flat CubeField.""" + nested = serde.decode(data, CubeFieldNested) + return _cube_field_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +CubeField.CUBE_PARENT_FIELD_NAME = KeywordTextField( + "cubeParentFieldName", "cubeParentFieldName", "cubeParentFieldName.text" +) +CubeField.CUBE_PARENT_FIELD_QUALIFIED_NAME = KeywordField( + "cubeParentFieldQualifiedName", "cubeParentFieldQualifiedName" +) +CubeField.CUBE_FIELD_LEVEL = NumericField("cubeFieldLevel", "cubeFieldLevel") +CubeField.CUBE_FIELD_GENERATION = NumericField( + "cubeFieldGeneration", "cubeFieldGeneration" +) +CubeField.CUBE_FIELD_MEASURE_EXPRESSION = KeywordTextField( + "cubeFieldMeasureExpression", + "cubeFieldMeasureExpression", + "cubeFieldMeasureExpression.text", +) +CubeField.CUBE_SUB_FIELD_COUNT = NumericField("cubeSubFieldCount", "cubeSubFieldCount") +CubeField.CUBE_NAME = KeywordTextField("cubeName", "cubeName", "cubeName.text") +CubeField.CUBE_QUALIFIED_NAME = KeywordField("cubeQualifiedName", "cubeQualifiedName") +CubeField.CUBE_DIMENSION_NAME = KeywordTextField( + "cubeDimensionName", "cubeDimensionName", "cubeDimensionName.text" +) +CubeField.CUBE_DIMENSION_QUALIFIED_NAME = KeywordField( + "cubeDimensionQualifiedName", "cubeDimensionQualifiedName" +) +CubeField.CUBE_HIERARCHY_NAME = KeywordTextField( + "cubeHierarchyName", "cubeHierarchyName", "cubeHierarchyName.text" +) +CubeField.CUBE_HIERARCHY_QUALIFIED_NAME = KeywordField( + "cubeHierarchyQualifiedName", "cubeHierarchyQualifiedName" +) +CubeField.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +CubeField.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +CubeField.ANOMALO_CHECKS = RelationField("anomaloChecks") +CubeField.APPLICATION = RelationField("application") +CubeField.APPLICATION_FIELD = RelationField("applicationField") +CubeField.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +CubeField.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +CubeField.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +CubeField.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +CubeField.METRICS = RelationField("metrics") +CubeField.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +CubeField.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +CubeField.MEANINGS = RelationField("meanings") +CubeField.MC_MONITORS = RelationField("mcMonitors") +CubeField.MC_INCIDENTS = RelationField("mcIncidents") +CubeField.CUBE_DIMENSIONS = RelationField("cubeDimensions") +CubeField.CUBE_HIERARCHY = RelationField("cubeHierarchy") +CubeField.CUBE_NESTED_FIELDS = RelationField("cubeNestedFields") +CubeField.CUBE_PARENT_FIELD = RelationField("cubeParentField") +CubeField.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +CubeField.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +CubeField.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +CubeField.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +CubeField.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +CubeField.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +CubeField.FILES = RelationField("files") +CubeField.LINKS = RelationField("links") +CubeField.README = RelationField("readme") +CubeField.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +CubeField.SODA_CHECKS = RelationField("sodaChecks") +CubeField.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +CubeField.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/cube_hierarchy.py b/pyatlan_v9/model/assets/cube_hierarchy.py new file mode 100644 index 000000000..226793452 --- /dev/null +++ b/pyatlan_v9/model/assets/cube_hierarchy.py @@ -0,0 +1,655 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +CubeHierarchy asset model with flattened inheritance. + +This module provides: +- CubeHierarchy: Flat asset class (easy to use) +- CubeHierarchyAttributes: Nested attributes struct (extends AssetAttributes) +- CubeHierarchyNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .cube_related import RelatedCubeDimension, RelatedCubeField + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class CubeHierarchy(Asset): + """ + Instance of a cube hierarchy in Atlan. + """ + + CUBE_FIELD_COUNT: ClassVar[Any] = None + CUBE_NAME: ClassVar[Any] = None + CUBE_QUALIFIED_NAME: ClassVar[Any] = None + CUBE_DIMENSION_NAME: ClassVar[Any] = None + CUBE_DIMENSION_QUALIFIED_NAME: ClassVar[Any] = None + CUBE_HIERARCHY_NAME: ClassVar[Any] = None + CUBE_HIERARCHY_QUALIFIED_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + CUBE_DIMENSIONS: ClassVar[Any] = None + CUBE_DIMENSION: ClassVar[Any] = None + CUBE_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "CubeHierarchy" + + cube_field_count: Union[int, None, UnsetType] = UNSET + """Number of total fields in the cube hierarchy.""" + + cube_name: Union[str, None, UnsetType] = UNSET + """Simple name of the cube in which this asset exists, or empty if it is itself a cube.""" + + cube_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the cube in which this asset exists, or empty if it is itself a cube.""" + + cube_dimension_name: Union[str, None, UnsetType] = UNSET + """Simple name of the cube dimension in which this asset exists, or empty if it is itself a dimension.""" + + cube_dimension_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the cube dimension in which this asset exists, or empty if it is itself a dimension.""" + + cube_hierarchy_name: Union[str, None, UnsetType] = UNSET + """Simple name of the dimension hierarchy in which this asset exists, or empty if it is itself a hierarchy.""" + + cube_hierarchy_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dimension hierarchy in which this asset exists, or empty if it is itself a hierarchy.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + cube_dimensions: Union[List[RelatedCubeDimension], None, UnsetType] = UNSET + """Individual dimensions contained in the cube.""" + + cube_dimension: Union[RelatedCubeDimension, None, UnsetType] = UNSET + """Dimension containing the hierarchy.""" + + cube_fields: Union[List[RelatedCubeField], None, UnsetType] = UNSET + """Individual fields contained in the hierarchy.""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "CubeHierarchy" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _cube_hierarchy_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> CubeHierarchy: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + CubeHierarchy instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _cube_hierarchy_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class CubeHierarchyAttributes(AssetAttributes): + """CubeHierarchy-specific attributes for nested API format.""" + + cube_field_count: Union[int, None, UnsetType] = UNSET + """Number of total fields in the cube hierarchy.""" + + cube_name: Union[str, None, UnsetType] = UNSET + """Simple name of the cube in which this asset exists, or empty if it is itself a cube.""" + + cube_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the cube in which this asset exists, or empty if it is itself a cube.""" + + cube_dimension_name: Union[str, None, UnsetType] = UNSET + """Simple name of the cube dimension in which this asset exists, or empty if it is itself a dimension.""" + + cube_dimension_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the cube dimension in which this asset exists, or empty if it is itself a dimension.""" + + cube_hierarchy_name: Union[str, None, UnsetType] = UNSET + """Simple name of the dimension hierarchy in which this asset exists, or empty if it is itself a hierarchy.""" + + cube_hierarchy_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dimension hierarchy in which this asset exists, or empty if it is itself a hierarchy.""" + + +class CubeHierarchyRelationshipAttributes(AssetRelationshipAttributes): + """CubeHierarchy-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + cube_dimensions: Union[List[RelatedCubeDimension], None, UnsetType] = UNSET + """Individual dimensions contained in the cube.""" + + cube_dimension: Union[RelatedCubeDimension, None, UnsetType] = UNSET + """Dimension containing the hierarchy.""" + + cube_fields: Union[List[RelatedCubeField], None, UnsetType] = UNSET + """Individual fields contained in the hierarchy.""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class CubeHierarchyNested(AssetNested): + """CubeHierarchy in nested API format for high-performance serialization.""" + + attributes: Union[CubeHierarchyAttributes, UnsetType] = UNSET + relationship_attributes: Union[CubeHierarchyRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + CubeHierarchyRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + CubeHierarchyRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_CUBE_HIERARCHY_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "cube_dimensions", + "cube_dimension", + "cube_fields", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_cube_hierarchy_attrs( + attrs: CubeHierarchyAttributes, obj: CubeHierarchy +) -> None: + """Populate CubeHierarchy-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.cube_field_count = obj.cube_field_count + attrs.cube_name = obj.cube_name + attrs.cube_qualified_name = obj.cube_qualified_name + attrs.cube_dimension_name = obj.cube_dimension_name + attrs.cube_dimension_qualified_name = obj.cube_dimension_qualified_name + attrs.cube_hierarchy_name = obj.cube_hierarchy_name + attrs.cube_hierarchy_qualified_name = obj.cube_hierarchy_qualified_name + + +def _extract_cube_hierarchy_attrs(attrs: CubeHierarchyAttributes) -> dict: + """Extract all CubeHierarchy attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["cube_field_count"] = attrs.cube_field_count + result["cube_name"] = attrs.cube_name + result["cube_qualified_name"] = attrs.cube_qualified_name + result["cube_dimension_name"] = attrs.cube_dimension_name + result["cube_dimension_qualified_name"] = attrs.cube_dimension_qualified_name + result["cube_hierarchy_name"] = attrs.cube_hierarchy_name + result["cube_hierarchy_qualified_name"] = attrs.cube_hierarchy_qualified_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _cube_hierarchy_to_nested(cube_hierarchy: CubeHierarchy) -> CubeHierarchyNested: + """Convert flat CubeHierarchy to nested format.""" + attrs = CubeHierarchyAttributes() + _populate_cube_hierarchy_attrs(attrs, cube_hierarchy) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + cube_hierarchy, _CUBE_HIERARCHY_REL_FIELDS, CubeHierarchyRelationshipAttributes + ) + return CubeHierarchyNested( + guid=cube_hierarchy.guid, + type_name=cube_hierarchy.type_name, + status=cube_hierarchy.status, + version=cube_hierarchy.version, + create_time=cube_hierarchy.create_time, + update_time=cube_hierarchy.update_time, + created_by=cube_hierarchy.created_by, + updated_by=cube_hierarchy.updated_by, + classifications=cube_hierarchy.classifications, + classification_names=cube_hierarchy.classification_names, + meanings=cube_hierarchy.meanings, + labels=cube_hierarchy.labels, + business_attributes=cube_hierarchy.business_attributes, + custom_attributes=cube_hierarchy.custom_attributes, + pending_tasks=cube_hierarchy.pending_tasks, + proxy=cube_hierarchy.proxy, + is_incomplete=cube_hierarchy.is_incomplete, + provenance_type=cube_hierarchy.provenance_type, + home_id=cube_hierarchy.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _cube_hierarchy_from_nested(nested: CubeHierarchyNested) -> CubeHierarchy: + """Convert nested format to flat CubeHierarchy.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else CubeHierarchyAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _CUBE_HIERARCHY_REL_FIELDS, + CubeHierarchyRelationshipAttributes, + ) + return CubeHierarchy( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_cube_hierarchy_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _cube_hierarchy_to_nested_bytes( + cube_hierarchy: CubeHierarchy, serde: Serde +) -> bytes: + """Convert flat CubeHierarchy to nested JSON bytes.""" + return serde.encode(_cube_hierarchy_to_nested(cube_hierarchy)) + + +def _cube_hierarchy_from_nested_bytes(data: bytes, serde: Serde) -> CubeHierarchy: + """Convert nested JSON bytes to flat CubeHierarchy.""" + nested = serde.decode(data, CubeHierarchyNested) + return _cube_hierarchy_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +CubeHierarchy.CUBE_FIELD_COUNT = NumericField("cubeFieldCount", "cubeFieldCount") +CubeHierarchy.CUBE_NAME = KeywordTextField("cubeName", "cubeName", "cubeName.text") +CubeHierarchy.CUBE_QUALIFIED_NAME = KeywordField( + "cubeQualifiedName", "cubeQualifiedName" +) +CubeHierarchy.CUBE_DIMENSION_NAME = KeywordTextField( + "cubeDimensionName", "cubeDimensionName", "cubeDimensionName.text" +) +CubeHierarchy.CUBE_DIMENSION_QUALIFIED_NAME = KeywordField( + "cubeDimensionQualifiedName", "cubeDimensionQualifiedName" +) +CubeHierarchy.CUBE_HIERARCHY_NAME = KeywordTextField( + "cubeHierarchyName", "cubeHierarchyName", "cubeHierarchyName.text" +) +CubeHierarchy.CUBE_HIERARCHY_QUALIFIED_NAME = KeywordField( + "cubeHierarchyQualifiedName", "cubeHierarchyQualifiedName" +) +CubeHierarchy.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +CubeHierarchy.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +CubeHierarchy.ANOMALO_CHECKS = RelationField("anomaloChecks") +CubeHierarchy.APPLICATION = RelationField("application") +CubeHierarchy.APPLICATION_FIELD = RelationField("applicationField") +CubeHierarchy.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +CubeHierarchy.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +CubeHierarchy.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +CubeHierarchy.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +CubeHierarchy.METRICS = RelationField("metrics") +CubeHierarchy.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +CubeHierarchy.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +CubeHierarchy.MEANINGS = RelationField("meanings") +CubeHierarchy.MC_MONITORS = RelationField("mcMonitors") +CubeHierarchy.MC_INCIDENTS = RelationField("mcIncidents") +CubeHierarchy.CUBE_DIMENSIONS = RelationField("cubeDimensions") +CubeHierarchy.CUBE_DIMENSION = RelationField("cubeDimension") +CubeHierarchy.CUBE_FIELDS = RelationField("cubeFields") +CubeHierarchy.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +CubeHierarchy.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +CubeHierarchy.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +CubeHierarchy.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +CubeHierarchy.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +CubeHierarchy.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +CubeHierarchy.FILES = RelationField("files") +CubeHierarchy.LINKS = RelationField("links") +CubeHierarchy.README = RelationField("readme") +CubeHierarchy.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +CubeHierarchy.SODA_CHECKS = RelationField("sodaChecks") +CubeHierarchy.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +CubeHierarchy.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/cube_related.py b/pyatlan_v9/model/assets/cube_related.py new file mode 100644 index 000000000..b5764cf54 --- /dev/null +++ b/pyatlan_v9/model/assets/cube_related.py @@ -0,0 +1,147 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Cube module. + +This module contains all Related{Type} classes for the Cube type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Union + +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedCatalog +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedMultiDimensionalDataset", + "RelatedCube", + "RelatedCubeDimension", + "RelatedCubeHierarchy", + "RelatedCubeField", +] + + +class RelatedMultiDimensionalDataset(RelatedCatalog): + """ + Related entity reference for MultiDimensionalDataset assets. + + Extends RelatedCatalog with MultiDimensionalDataset-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "MultiDimensionalDataset" so it serializes correctly + + cube_name: Union[str, None, UnsetType] = UNSET + """Simple name of the cube in which this asset exists, or empty if it is itself a cube.""" + + cube_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the cube in which this asset exists, or empty if it is itself a cube.""" + + cube_dimension_name: Union[str, None, UnsetType] = UNSET + """Simple name of the cube dimension in which this asset exists, or empty if it is itself a dimension.""" + + cube_dimension_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the cube dimension in which this asset exists, or empty if it is itself a dimension.""" + + cube_hierarchy_name: Union[str, None, UnsetType] = UNSET + """Simple name of the dimension hierarchy in which this asset exists, or empty if it is itself a hierarchy.""" + + cube_hierarchy_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dimension hierarchy in which this asset exists, or empty if it is itself a hierarchy.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "MultiDimensionalDataset" + + +class RelatedCube(RelatedMultiDimensionalDataset): + """ + Related entity reference for Cube assets. + + Extends RelatedMultiDimensionalDataset with Cube-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Cube" so it serializes correctly + + cube_dimension_count: Union[int, None, UnsetType] = UNSET + """Number of dimensions in the cube.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Cube" + + +class RelatedCubeDimension(RelatedMultiDimensionalDataset): + """ + Related entity reference for CubeDimension assets. + + Extends RelatedMultiDimensionalDataset with CubeDimension-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "CubeDimension" so it serializes correctly + + cube_hierarchy_count: Union[int, None, UnsetType] = UNSET + """Number of hierarchies in the cube dimension.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "CubeDimension" + + +class RelatedCubeHierarchy(RelatedMultiDimensionalDataset): + """ + Related entity reference for CubeHierarchy assets. + + Extends RelatedMultiDimensionalDataset with CubeHierarchy-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "CubeHierarchy" so it serializes correctly + + cube_field_count: Union[int, None, UnsetType] = UNSET + """Number of total fields in the cube hierarchy.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "CubeHierarchy" + + +class RelatedCubeField(RelatedMultiDimensionalDataset): + """ + Related entity reference for CubeField assets. + + Extends RelatedMultiDimensionalDataset with CubeField-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "CubeField" so it serializes correctly + + cube_parent_field_name: Union[str, None, UnsetType] = UNSET + """Name of the parent field in which this field is nested.""" + + cube_parent_field_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the parent field in which this field is nested.""" + + cube_field_level: Union[int, None, UnsetType] = UNSET + """Level of the field in the cube hierarchy.""" + + cube_field_generation: Union[int, None, UnsetType] = UNSET + """Generation of the field in the cube hierarchy.""" + + cube_field_measure_expression: Union[str, None, UnsetType] = UNSET + """Expression used to calculate this measure.""" + + cube_sub_field_count: Union[int, None, UnsetType] = UNSET + """Number of sub-fields that are direct children of this field.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "CubeField" diff --git a/pyatlan_v9/model/assets/custom.py b/pyatlan_v9/model/assets/custom.py new file mode 100644 index 000000000..e54582dd2 --- /dev/null +++ b/pyatlan_v9/model/assets/custom.py @@ -0,0 +1,523 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Custom asset model with flattened inheritance. + +This module provides: +- Custom: Flat asset class (easy to use) +- CustomAttributes: Nested attributes struct (extends AssetAttributes) +- CustomNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Custom(Asset): + """ + Base class for all Custom types. + """ + + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Custom" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Custom" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _custom_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Custom: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Custom instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _custom_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class CustomAttributes(AssetAttributes): + """Custom-specific attributes for nested API format.""" + + pass + + +class CustomRelationshipAttributes(AssetRelationshipAttributes): + """Custom-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class CustomNested(AssetNested): + """Custom in nested API format for high-performance serialization.""" + + attributes: Union[CustomAttributes, UnsetType] = UNSET + relationship_attributes: Union[CustomRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[CustomRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[CustomRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_CUSTOM_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_custom_attrs(attrs: CustomAttributes, obj: Custom) -> None: + """Populate Custom-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + + +def _extract_custom_attrs(attrs: CustomAttributes) -> dict: + """Extract all Custom attributes from the attrs struct into a flat dict.""" + return _extract_asset_attrs(attrs) + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _custom_to_nested(custom: Custom) -> CustomNested: + """Convert flat Custom to nested format.""" + attrs = CustomAttributes() + _populate_custom_attrs(attrs, custom) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + custom, _CUSTOM_REL_FIELDS, CustomRelationshipAttributes + ) + return CustomNested( + guid=custom.guid, + type_name=custom.type_name, + status=custom.status, + version=custom.version, + create_time=custom.create_time, + update_time=custom.update_time, + created_by=custom.created_by, + updated_by=custom.updated_by, + classifications=custom.classifications, + classification_names=custom.classification_names, + meanings=custom.meanings, + labels=custom.labels, + business_attributes=custom.business_attributes, + custom_attributes=custom.custom_attributes, + pending_tasks=custom.pending_tasks, + proxy=custom.proxy, + is_incomplete=custom.is_incomplete, + provenance_type=custom.provenance_type, + home_id=custom.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _custom_from_nested(nested: CustomNested) -> Custom: + """Convert nested format to flat Custom.""" + attrs = nested.attributes if nested.attributes is not UNSET else CustomAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _CUSTOM_REL_FIELDS, + CustomRelationshipAttributes, + ) + return Custom( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_custom_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _custom_to_nested_bytes(custom: Custom, serde: Serde) -> bytes: + """Convert flat Custom to nested JSON bytes.""" + return serde.encode(_custom_to_nested(custom)) + + +def _custom_from_nested_bytes(data: bytes, serde: Serde) -> Custom: + """Convert nested JSON bytes to flat Custom.""" + nested = serde.decode(data, CustomNested) + return _custom_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import RelationField # noqa: E402 + +Custom.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Custom.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Custom.ANOMALO_CHECKS = RelationField("anomaloChecks") +Custom.APPLICATION = RelationField("application") +Custom.APPLICATION_FIELD = RelationField("applicationField") +Custom.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Custom.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Custom.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Custom.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Custom.METRICS = RelationField("metrics") +Custom.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Custom.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Custom.MEANINGS = RelationField("meanings") +Custom.MC_MONITORS = RelationField("mcMonitors") +Custom.MC_INCIDENTS = RelationField("mcIncidents") +Custom.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Custom.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Custom.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Custom.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Custom.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Custom.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Custom.FILES = RelationField("files") +Custom.LINKS = RelationField("links") +Custom.README = RelationField("readme") +Custom.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Custom.SODA_CHECKS = RelationField("sodaChecks") +Custom.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Custom.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/custom_entity.py b/pyatlan_v9/model/assets/custom_entity.py new file mode 100644 index 000000000..e3124c4e1 --- /dev/null +++ b/pyatlan_v9/model/assets/custom_entity.py @@ -0,0 +1,629 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +CustomEntity asset model with flattened inheritance. + +This module provides: +- CustomEntity: Flat asset class (easy to use) +- CustomEntityAttributes: Nested attributes struct (extends AssetAttributes) +- CustomEntityNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .custom_related import RelatedCustomEntity + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class CustomEntity(Asset): + """ + Instances of CustomEntity in Atlan. + """ + + CUSTOM_CHILDREN_SUBTYPE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + CUSTOM_CHILD_ENTITIES: ClassVar[Any] = None + CUSTOM_PARENT_ENTITY: ClassVar[Any] = None + CUSTOM_RELATED_TO_ENTITIES: ClassVar[Any] = None + CUSTOM_RELATED_FROM_ENTITIES: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "CustomEntity" + + custom_children_subtype: Union[str, None, UnsetType] = UNSET + """Label of the children column for this asset type.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + custom_child_entities: Union[List[RelatedCustomEntity], None, UnsetType] = UNSET + """Custom entities contained within the parent entity.""" + + custom_parent_entity: Union[RelatedCustomEntity, None, UnsetType] = UNSET + """Custom entity in which the child entities are contained.""" + + custom_related_to_entities: Union[List[RelatedCustomEntity], None, UnsetType] = ( + UNSET + ) + """Target custom entity indicating where the relationship is directed.""" + + custom_related_from_entities: Union[List[RelatedCustomEntity], None, UnsetType] = ( + UNSET + ) + """Source custom entity indicating where the relationship originates.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "CustomEntity" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + connection_qualified_name: str, + ) -> "CustomEntity": + validate_required_fields( + ["name", "connection_qualified_name"], + [name, connection_qualified_name], + ) + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + qualified_name = f"{connection_qualified_name}/{name}" + return cls( + name=name, + qualified_name=qualified_name, + connector_name=connector_name, + connection_qualified_name=connection_qualified_name, + ) + + @classmethod + def create(cls, **kwargs) -> "CustomEntity": + return cls.creator(**kwargs) + + @classmethod + def create_for_modification(cls, **kwargs) -> "CustomEntity": + return cls.updater(**kwargs) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _custom_entity_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> CustomEntity: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + CustomEntity instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _custom_entity_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class CustomEntityAttributes(AssetAttributes): + """CustomEntity-specific attributes for nested API format.""" + + custom_children_subtype: Union[str, None, UnsetType] = UNSET + """Label of the children column for this asset type.""" + + +class CustomEntityRelationshipAttributes(AssetRelationshipAttributes): + """CustomEntity-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + custom_child_entities: Union[List[RelatedCustomEntity], None, UnsetType] = UNSET + """Custom entities contained within the parent entity.""" + + custom_parent_entity: Union[RelatedCustomEntity, None, UnsetType] = UNSET + """Custom entity in which the child entities are contained.""" + + custom_related_to_entities: Union[List[RelatedCustomEntity], None, UnsetType] = ( + UNSET + ) + """Target custom entity indicating where the relationship is directed.""" + + custom_related_from_entities: Union[List[RelatedCustomEntity], None, UnsetType] = ( + UNSET + ) + """Source custom entity indicating where the relationship originates.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class CustomEntityNested(AssetNested): + """CustomEntity in nested API format for high-performance serialization.""" + + attributes: Union[CustomEntityAttributes, UnsetType] = UNSET + relationship_attributes: Union[CustomEntityRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + CustomEntityRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + CustomEntityRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_CUSTOM_ENTITY_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "custom_child_entities", + "custom_parent_entity", + "custom_related_to_entities", + "custom_related_from_entities", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_custom_entity_attrs( + attrs: CustomEntityAttributes, obj: CustomEntity +) -> None: + """Populate CustomEntity-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.custom_children_subtype = obj.custom_children_subtype + + +def _extract_custom_entity_attrs(attrs: CustomEntityAttributes) -> dict: + """Extract all CustomEntity attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["custom_children_subtype"] = attrs.custom_children_subtype + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _custom_entity_to_nested(custom_entity: CustomEntity) -> CustomEntityNested: + """Convert flat CustomEntity to nested format.""" + attrs = CustomEntityAttributes() + _populate_custom_entity_attrs(attrs, custom_entity) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + custom_entity, _CUSTOM_ENTITY_REL_FIELDS, CustomEntityRelationshipAttributes + ) + return CustomEntityNested( + guid=custom_entity.guid, + type_name=custom_entity.type_name, + status=custom_entity.status, + version=custom_entity.version, + create_time=custom_entity.create_time, + update_time=custom_entity.update_time, + created_by=custom_entity.created_by, + updated_by=custom_entity.updated_by, + classifications=custom_entity.classifications, + classification_names=custom_entity.classification_names, + meanings=custom_entity.meanings, + labels=custom_entity.labels, + business_attributes=custom_entity.business_attributes, + custom_attributes=custom_entity.custom_attributes, + pending_tasks=custom_entity.pending_tasks, + proxy=custom_entity.proxy, + is_incomplete=custom_entity.is_incomplete, + provenance_type=custom_entity.provenance_type, + home_id=custom_entity.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _custom_entity_from_nested(nested: CustomEntityNested) -> CustomEntity: + """Convert nested format to flat CustomEntity.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else CustomEntityAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _CUSTOM_ENTITY_REL_FIELDS, + CustomEntityRelationshipAttributes, + ) + return CustomEntity( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_custom_entity_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _custom_entity_to_nested_bytes(custom_entity: CustomEntity, serde: Serde) -> bytes: + """Convert flat CustomEntity to nested JSON bytes.""" + return serde.encode(_custom_entity_to_nested(custom_entity)) + + +def _custom_entity_from_nested_bytes(data: bytes, serde: Serde) -> CustomEntity: + """Convert nested JSON bytes to flat CustomEntity.""" + nested = serde.decode(data, CustomEntityNested) + return _custom_entity_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +CustomEntity.CUSTOM_CHILDREN_SUBTYPE = KeywordField( + "customChildrenSubtype", "customChildrenSubtype" +) +CustomEntity.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +CustomEntity.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +CustomEntity.ANOMALO_CHECKS = RelationField("anomaloChecks") +CustomEntity.APPLICATION = RelationField("application") +CustomEntity.APPLICATION_FIELD = RelationField("applicationField") +CustomEntity.CUSTOM_CHILD_ENTITIES = RelationField("customChildEntities") +CustomEntity.CUSTOM_PARENT_ENTITY = RelationField("customParentEntity") +CustomEntity.CUSTOM_RELATED_TO_ENTITIES = RelationField("customRelatedToEntities") +CustomEntity.CUSTOM_RELATED_FROM_ENTITIES = RelationField("customRelatedFromEntities") +CustomEntity.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +CustomEntity.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +CustomEntity.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +CustomEntity.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +CustomEntity.METRICS = RelationField("metrics") +CustomEntity.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +CustomEntity.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +CustomEntity.MEANINGS = RelationField("meanings") +CustomEntity.MC_MONITORS = RelationField("mcMonitors") +CustomEntity.MC_INCIDENTS = RelationField("mcIncidents") +CustomEntity.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +CustomEntity.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +CustomEntity.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +CustomEntity.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +CustomEntity.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +CustomEntity.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +CustomEntity.FILES = RelationField("files") +CustomEntity.LINKS = RelationField("links") +CustomEntity.README = RelationField("readme") +CustomEntity.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +CustomEntity.SODA_CHECKS = RelationField("sodaChecks") +CustomEntity.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +CustomEntity.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/custom_related.py b/pyatlan_v9/model/assets/custom_related.py new file mode 100644 index 000000000..82f98e00a --- /dev/null +++ b/pyatlan_v9/model/assets/custom_related.py @@ -0,0 +1,57 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Custom module. + +This module contains all Related{Type} classes for the Custom type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Union + +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedCatalog +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedCustom", + "RelatedCustomEntity", +] + + +class RelatedCustom(RelatedCatalog): + """ + Related entity reference for Custom assets. + + Extends RelatedCatalog with Custom-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Custom" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Custom" + + +class RelatedCustomEntity(RelatedCustom): + """ + Related entity reference for CustomEntity assets. + + Extends RelatedCustom with CustomEntity-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "CustomEntity" so it serializes correctly + + custom_children_subtype: Union[str, None, UnsetType] = UNSET + """Label of the children column for this asset type.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "CustomEntity" diff --git a/pyatlan_v9/model/assets/data_contract.py b/pyatlan_v9/model/assets/data_contract.py new file mode 100644 index 000000000..7230be37e --- /dev/null +++ b/pyatlan_v9/model/assets/data_contract.py @@ -0,0 +1,281 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""DataContract asset model for pyatlan_v9.""" + +from __future__ import annotations + +import re +from json import JSONDecodeError, loads +from typing import Union + +from msgspec import UNSET, UnsetType + +from pyatlan.errors import ErrorCode +from pyatlan_v9.model.contract import DataContractSpec +from pyatlan_v9.model.conversion_utils import ( + build_attributes_kwargs, + build_flat_kwargs, + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .asset_related import RelatedAsset +from .catalog import ( + Catalog, + CatalogAttributes, + CatalogNested, + CatalogRelationshipAttributes, +) +from .catalog_related import RelatedCatalog + + +@register_asset +class DataContract(Catalog): + """Instance of a data contract in Atlan.""" + + type_name: Union[str, UnsetType] = "DataContract" + + data_contract_json: Union[str, None, UnsetType] = UNSET + """Deprecated JSON representation of the data contract.""" + + data_contract_spec: Union[str, None, UnsetType] = UNSET + """YAML representation of the data contract.""" + + data_contract_version: Union[int, None, UnsetType] = UNSET + """Version number of the data contract.""" + + data_contract_asset_guid: Union[str, None, UnsetType] = UNSET + """GUID of the governed asset.""" + + data_contract_asset_certified: Union[RelatedAsset, None, UnsetType] = UNSET + """Certified target asset for this contract.""" + + data_contract_next_version: Union[RelatedCatalog, None, UnsetType] = UNSET + """Next version in this contract chain.""" + + data_contract_asset_latest: Union[RelatedAsset, None, UnsetType] = UNSET + """Latest version of this contract.""" + + data_contract_previous_version: Union[RelatedCatalog, None, UnsetType] = UNSET + """Previous version in this contract chain.""" + + @classmethod + @init_guid + def creator( + cls, + *, + asset_qualified_name: str, + contract_json: Union[str, None] = None, + contract_spec: Union[DataContractSpec, str, None] = None, + ) -> "DataContract": + """Create a new DataContract asset.""" + attrs = DataContract.Attributes.creator( + asset_qualified_name=asset_qualified_name, + contract_json=contract_json, + contract_spec=contract_spec, + ) + return cls( + name=attrs.name, + qualified_name=attrs.qualified_name, + data_contract_json=attrs.data_contract_json, + data_contract_spec=attrs.data_contract_spec, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "DataContract": + """Create a DataContract instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "DataContract": + """Return only required fields for update operations.""" + return DataContract.updater(qualified_name=self.qualified_name, name=self.name) + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """Convert to JSON string.""" + if serde is None: + serde = get_serde() + if nested: + return _data_contract_to_nested_bytes(self, serde).decode("utf-8") + return serde.encode(self).decode("utf-8") + + @staticmethod + def from_json( + json_data: Union[str, bytes], serde: Serde | None = None + ) -> "DataContract": + """Create from JSON string or bytes.""" + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _data_contract_from_nested_bytes(json_data, serde) + + +class DataContractAttributes(CatalogAttributes): + """DataContract-specific attributes for nested API format.""" + + data_contract_json: Union[str, None, UnsetType] = UNSET + data_contract_spec: Union[str, None, UnsetType] = UNSET + data_contract_version: Union[int, None, UnsetType] = UNSET + data_contract_asset_guid: Union[str, None, UnsetType] = UNSET + + @classmethod + def creator( + cls, + *, + asset_qualified_name: str, + contract_json: Union[str, None] = None, + contract_spec: Union[DataContractSpec, str, None] = None, + ) -> "DataContractAttributes": + """Create DataContract attributes from JSON or YAML contract content.""" + validate_required_fields(["asset_qualified_name"], [asset_qualified_name]) + if not (contract_json or contract_spec): + raise ValueError( + "At least one of `contract_json` or `contract_spec` must be provided to create a contract." + ) + if contract_json and contract_spec: + raise ValueError( + "Both `contract_json` and `contract_spec` cannot be provided simultaneously to create a contract." + ) + + default_dataset = asset_qualified_name[asset_qualified_name.rfind("/") + 1 :] + contract_name: str + contract_spec_value: Union[str, None] = None + + if contract_json: + try: + payload = loads(contract_json) + dataset = payload.get("dataset") + if not dataset: + raise KeyError("dataset") + contract_name = f"Data contract for {dataset}" + except (JSONDecodeError, KeyError): + raise ErrorCode.INVALID_CONTRACT_JSON.exception_with_parameters() + else: + if isinstance(contract_spec, DataContractSpec): + contract_name = ( + f"Data contract for {contract_spec.dataset or default_dataset}" + ) + contract_spec_value = contract_spec.to_yaml() + else: + spec_str = contract_spec or "" + match = re.search(r"dataset:\s*([^\s#]+)", spec_str) + dataset = match.group(1) if match else default_dataset + contract_name = f"Data contract for {dataset}" + contract_spec_value = spec_str + + return cls( + name=contract_name, + qualified_name=f"{asset_qualified_name}/contract", + data_contract_json=contract_json, + data_contract_spec=contract_spec_value, + ) + + +DataContract.Attributes = DataContractAttributes # type: ignore[attr-defined] + + +class DataContractRelationshipAttributes(CatalogRelationshipAttributes): + """DataContract-specific relationship attributes for nested API format.""" + + data_contract_asset_certified: Union[RelatedAsset, None, UnsetType] = UNSET + data_contract_next_version: Union[RelatedCatalog, None, UnsetType] = UNSET + data_contract_asset_latest: Union[RelatedAsset, None, UnsetType] = UNSET + data_contract_previous_version: Union[RelatedCatalog, None, UnsetType] = UNSET + + +class DataContractNested(CatalogNested): + """DataContract in nested API format for high-performance serialization.""" + + attributes: Union[DataContractAttributes, UnsetType] = UNSET + relationship_attributes: Union[DataContractRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + DataContractRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + DataContractRelationshipAttributes, UnsetType + ] = UNSET + + +def _data_contract_to_nested(data_contract: DataContract) -> DataContractNested: + """Convert flat DataContract to nested format.""" + attrs_kwargs = build_attributes_kwargs(data_contract, DataContractAttributes) + attrs = DataContractAttributes(**attrs_kwargs) + rel_fields: list[str] = [ + "data_contract_asset_certified", + "data_contract_next_version", + "data_contract_asset_latest", + "data_contract_previous_version", + ] + replace_rels, append_rels, remove_rels = categorize_relationships( + data_contract, rel_fields, DataContractRelationshipAttributes + ) + return DataContractNested( + guid=data_contract.guid, + type_name=data_contract.type_name, + status=data_contract.status, + delete_handler=data_contract.delete_handler, + version=data_contract.version, + create_time=data_contract.create_time, + update_time=data_contract.update_time, + created_by=data_contract.created_by, + updated_by=data_contract.updated_by, + classifications=data_contract.classifications, + classification_names=data_contract.classification_names, + meanings=data_contract.meanings, + labels=data_contract.labels, + business_attributes=data_contract.business_attributes, + custom_attributes=data_contract.custom_attributes, + pending_tasks=data_contract.pending_tasks, + proxy=data_contract.proxy, + is_incomplete=data_contract.is_incomplete, + provenance_type=data_contract.provenance_type, + home_id=data_contract.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _data_contract_from_nested(nested: DataContractNested) -> DataContract: + """Convert nested format to flat DataContract.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else DataContractAttributes() + ) + rel_fields: list[str] = [ + "data_contract_asset_certified", + "data_contract_next_version", + "data_contract_asset_latest", + "data_contract_previous_version", + ] + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + rel_fields, + DataContractRelationshipAttributes, + ) + kwargs = build_flat_kwargs( + nested, attrs, merged_rels, CatalogNested, DataContractAttributes + ) + return DataContract(**kwargs) + + +def _data_contract_to_nested_bytes(data_contract: DataContract, serde: Serde) -> bytes: + """Convert flat DataContract to nested JSON bytes.""" + return serde.encode(_data_contract_to_nested(data_contract)) + + +def _data_contract_from_nested_bytes(data: bytes, serde: Serde) -> DataContract: + """Convert nested JSON bytes to flat DataContract.""" + nested = serde.decode(data, DataContractNested) + return _data_contract_from_nested(nested) diff --git a/pyatlan_v9/model/assets/data_domain.py b/pyatlan_v9/model/assets/data_domain.py new file mode 100644 index 000000000..abcf8f6a9 --- /dev/null +++ b/pyatlan_v9/model/assets/data_domain.py @@ -0,0 +1,655 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DataDomain asset model with flattened inheritance. + +This module provides: +- DataDomain: Flat asset class (easy to use) +- DataDomainAttributes: Nested attributes struct (extends AssetAttributes) +- DataDomainNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .data_mesh_related import RelatedDataDomain, RelatedDataProduct, RelatedStakeholder + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class DataDomain(Asset): + """ + Instance of a data domain in Atlan. + """ + + PARENT_DOMAIN_QUALIFIED_NAME: ClassVar[Any] = None + SUPER_DOMAIN_QUALIFIED_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + SUB_DOMAINS: ClassVar[Any] = None + PARENT_DOMAIN: ClassVar[Any] = None + DATA_PRODUCTS: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + STAKEHOLDERS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "DataDomain" + + parent_domain_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the parent domain in which this asset exists.""" + + super_domain_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the top-level domain in which this asset exists.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + sub_domains: Union[List[RelatedDataDomain], None, UnsetType] = UNSET + """Sub-data domains that exist within this data domain.""" + + parent_domain: Union[RelatedDataDomain, None, UnsetType] = UNSET + """Parent data domain in which this sub-data domain exists.""" + + data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products that exist within this data domain.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + stakeholders: Union[List[RelatedStakeholder], None, UnsetType] = UNSET + """Stakeholder assigned to the Domain""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "DataDomain" + + @classmethod + def _get_super_domain_qualified_name( + cls, domain_qualified_name: str + ) -> Union[str, None]: + """Extract the top-most ancestor domain qualified name.""" + domain_qn_prefix = re.compile(r"(default/domain/[a-zA-Z0-9-]+/super)/.*") + if domain_qualified_name: + match = domain_qn_prefix.match(domain_qualified_name) + if match and match.group(1): + return match.group(1) + if domain_qualified_name.startswith("default/domain/"): + return domain_qualified_name + return None + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + parent_domain_qualified_name: Union[str, None] = None, + ) -> "DataDomain": + """Create a new DataDomain asset.""" + validate_required_fields(["name"], [name]) + parent_domain = ( + RelatedDataDomain( + unique_attributes={"qualifiedName": parent_domain_qualified_name} + ) + if parent_domain_qualified_name + else None + ) + super_domain_qualified_name = ( + cls._get_super_domain_qualified_name(parent_domain_qualified_name) + if parent_domain_qualified_name + else None + ) + return cls( + name=name, + qualified_name=name, + parent_domain=parent_domain, + parent_domain_qualified_name=parent_domain_qualified_name, + super_domain_qualified_name=super_domain_qualified_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "DataDomain": + """Create a DataDomain instance for update operations.""" + validate_required_fields(["name", "qualified_name"], [name, qualified_name]) + fields = qualified_name.split("/") + if len(fields) < 3: + raise ValueError(f"Invalid data domain qualified_name: {qualified_name}") + return cls( + qualified_name=qualified_name, + name=name, + parent_domain_qualified_name=None, + ) + + def trim_to_required(self) -> "DataDomain": + """Return only the required fields for updates.""" + return DataDomain.updater(qualified_name=self.qualified_name, name=self.name) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _data_domain_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> DataDomain: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + DataDomain instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _data_domain_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DataDomainAttributes(AssetAttributes): + """DataDomain-specific attributes for nested API format.""" + + parent_domain_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the parent domain in which this asset exists.""" + + super_domain_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the top-level domain in which this asset exists.""" + + +class DataDomainRelationshipAttributes(AssetRelationshipAttributes): + """DataDomain-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + sub_domains: Union[List[RelatedDataDomain], None, UnsetType] = UNSET + """Sub-data domains that exist within this data domain.""" + + parent_domain: Union[RelatedDataDomain, None, UnsetType] = UNSET + """Parent data domain in which this sub-data domain exists.""" + + data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products that exist within this data domain.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + stakeholders: Union[List[RelatedStakeholder], None, UnsetType] = UNSET + """Stakeholder assigned to the Domain""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DataDomainNested(AssetNested): + """DataDomain in nested API format for high-performance serialization.""" + + attributes: Union[DataDomainAttributes, UnsetType] = UNSET + relationship_attributes: Union[DataDomainRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + DataDomainRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + DataDomainRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DATA_DOMAIN_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "sub_domains", + "parent_domain", + "data_products", + "output_port_data_products", + "input_port_data_products", + "stakeholders", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_data_domain_attrs(attrs: DataDomainAttributes, obj: DataDomain) -> None: + """Populate DataDomain-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.parent_domain_qualified_name = obj.parent_domain_qualified_name + attrs.super_domain_qualified_name = obj.super_domain_qualified_name + + +def _extract_data_domain_attrs(attrs: DataDomainAttributes) -> dict: + """Extract all DataDomain attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["parent_domain_qualified_name"] = attrs.parent_domain_qualified_name + result["super_domain_qualified_name"] = attrs.super_domain_qualified_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _data_domain_to_nested(data_domain: DataDomain) -> DataDomainNested: + """Convert flat DataDomain to nested format.""" + attrs = DataDomainAttributes() + _populate_data_domain_attrs(attrs, data_domain) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + data_domain, _DATA_DOMAIN_REL_FIELDS, DataDomainRelationshipAttributes + ) + return DataDomainNested( + guid=data_domain.guid, + type_name=data_domain.type_name, + status=data_domain.status, + version=data_domain.version, + create_time=data_domain.create_time, + update_time=data_domain.update_time, + created_by=data_domain.created_by, + updated_by=data_domain.updated_by, + classifications=data_domain.classifications, + classification_names=data_domain.classification_names, + meanings=data_domain.meanings, + labels=data_domain.labels, + business_attributes=data_domain.business_attributes, + custom_attributes=data_domain.custom_attributes, + pending_tasks=data_domain.pending_tasks, + proxy=data_domain.proxy, + is_incomplete=data_domain.is_incomplete, + provenance_type=data_domain.provenance_type, + home_id=data_domain.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _data_domain_from_nested(nested: DataDomainNested) -> DataDomain: + """Convert nested format to flat DataDomain.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else DataDomainAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DATA_DOMAIN_REL_FIELDS, + DataDomainRelationshipAttributes, + ) + return DataDomain( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_data_domain_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _data_domain_to_nested_bytes(data_domain: DataDomain, serde: Serde) -> bytes: + """Convert flat DataDomain to nested JSON bytes.""" + return serde.encode(_data_domain_to_nested(data_domain)) + + +def _data_domain_from_nested_bytes(data: bytes, serde: Serde) -> DataDomain: + """Convert nested JSON bytes to flat DataDomain.""" + nested = serde.decode(data, DataDomainNested) + return _data_domain_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordTextField, + RelationField, +) + +DataDomain.PARENT_DOMAIN_QUALIFIED_NAME = KeywordTextField( + "parentDomainQualifiedName", + "parentDomainQualifiedName", + "parentDomainQualifiedName.text", +) +DataDomain.SUPER_DOMAIN_QUALIFIED_NAME = KeywordTextField( + "superDomainQualifiedName", + "superDomainQualifiedName", + "superDomainQualifiedName.text", +) +DataDomain.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +DataDomain.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +DataDomain.ANOMALO_CHECKS = RelationField("anomaloChecks") +DataDomain.APPLICATION = RelationField("application") +DataDomain.APPLICATION_FIELD = RelationField("applicationField") +DataDomain.SUB_DOMAINS = RelationField("subDomains") +DataDomain.PARENT_DOMAIN = RelationField("parentDomain") +DataDomain.DATA_PRODUCTS = RelationField("dataProducts") +DataDomain.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +DataDomain.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +DataDomain.STAKEHOLDERS = RelationField("stakeholders") +DataDomain.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +DataDomain.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +DataDomain.METRICS = RelationField("metrics") +DataDomain.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +DataDomain.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +DataDomain.MEANINGS = RelationField("meanings") +DataDomain.MC_MONITORS = RelationField("mcMonitors") +DataDomain.MC_INCIDENTS = RelationField("mcIncidents") +DataDomain.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +DataDomain.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +DataDomain.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +DataDomain.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +DataDomain.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +DataDomain.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +DataDomain.FILES = RelationField("files") +DataDomain.LINKS = RelationField("links") +DataDomain.README = RelationField("readme") +DataDomain.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +DataDomain.SODA_CHECKS = RelationField("sodaChecks") +DataDomain.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +DataDomain.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/data_mesh.py b/pyatlan_v9/model/assets/data_mesh.py new file mode 100644 index 000000000..792f19c7f --- /dev/null +++ b/pyatlan_v9/model/assets/data_mesh.py @@ -0,0 +1,556 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DataMesh asset model with flattened inheritance. + +This module provides: +- DataMesh: Flat asset class (easy to use) +- DataMeshAttributes: Nested attributes struct (extends AssetAttributes) +- DataMeshNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .data_mesh_related import RelatedDataProduct + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class DataMesh(Asset): + """ + Base class for data mesh assets. + """ + + PARENT_DOMAIN_QUALIFIED_NAME: ClassVar[Any] = None + SUPER_DOMAIN_QUALIFIED_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "DataMesh" + + parent_domain_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the parent domain in which this asset exists.""" + + super_domain_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the top-level domain in which this asset exists.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "DataMesh" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _data_mesh_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> DataMesh: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + DataMesh instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _data_mesh_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DataMeshAttributes(AssetAttributes): + """DataMesh-specific attributes for nested API format.""" + + parent_domain_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the parent domain in which this asset exists.""" + + super_domain_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the top-level domain in which this asset exists.""" + + +class DataMeshRelationshipAttributes(AssetRelationshipAttributes): + """DataMesh-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DataMeshNested(AssetNested): + """DataMesh in nested API format for high-performance serialization.""" + + attributes: Union[DataMeshAttributes, UnsetType] = UNSET + relationship_attributes: Union[DataMeshRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[DataMeshRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[DataMeshRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DATA_MESH_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_data_mesh_attrs(attrs: DataMeshAttributes, obj: DataMesh) -> None: + """Populate DataMesh-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.parent_domain_qualified_name = obj.parent_domain_qualified_name + attrs.super_domain_qualified_name = obj.super_domain_qualified_name + + +def _extract_data_mesh_attrs(attrs: DataMeshAttributes) -> dict: + """Extract all DataMesh attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["parent_domain_qualified_name"] = attrs.parent_domain_qualified_name + result["super_domain_qualified_name"] = attrs.super_domain_qualified_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _data_mesh_to_nested(data_mesh: DataMesh) -> DataMeshNested: + """Convert flat DataMesh to nested format.""" + attrs = DataMeshAttributes() + _populate_data_mesh_attrs(attrs, data_mesh) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + data_mesh, _DATA_MESH_REL_FIELDS, DataMeshRelationshipAttributes + ) + return DataMeshNested( + guid=data_mesh.guid, + type_name=data_mesh.type_name, + status=data_mesh.status, + version=data_mesh.version, + create_time=data_mesh.create_time, + update_time=data_mesh.update_time, + created_by=data_mesh.created_by, + updated_by=data_mesh.updated_by, + classifications=data_mesh.classifications, + classification_names=data_mesh.classification_names, + meanings=data_mesh.meanings, + labels=data_mesh.labels, + business_attributes=data_mesh.business_attributes, + custom_attributes=data_mesh.custom_attributes, + pending_tasks=data_mesh.pending_tasks, + proxy=data_mesh.proxy, + is_incomplete=data_mesh.is_incomplete, + provenance_type=data_mesh.provenance_type, + home_id=data_mesh.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _data_mesh_from_nested(nested: DataMeshNested) -> DataMesh: + """Convert nested format to flat DataMesh.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else DataMeshAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DATA_MESH_REL_FIELDS, + DataMeshRelationshipAttributes, + ) + return DataMesh( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_data_mesh_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _data_mesh_to_nested_bytes(data_mesh: DataMesh, serde: Serde) -> bytes: + """Convert flat DataMesh to nested JSON bytes.""" + return serde.encode(_data_mesh_to_nested(data_mesh)) + + +def _data_mesh_from_nested_bytes(data: bytes, serde: Serde) -> DataMesh: + """Convert nested JSON bytes to flat DataMesh.""" + nested = serde.decode(data, DataMeshNested) + return _data_mesh_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordTextField, + RelationField, +) + +DataMesh.PARENT_DOMAIN_QUALIFIED_NAME = KeywordTextField( + "parentDomainQualifiedName", + "parentDomainQualifiedName", + "parentDomainQualifiedName.text", +) +DataMesh.SUPER_DOMAIN_QUALIFIED_NAME = KeywordTextField( + "superDomainQualifiedName", + "superDomainQualifiedName", + "superDomainQualifiedName.text", +) +DataMesh.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +DataMesh.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +DataMesh.ANOMALO_CHECKS = RelationField("anomaloChecks") +DataMesh.APPLICATION = RelationField("application") +DataMesh.APPLICATION_FIELD = RelationField("applicationField") +DataMesh.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +DataMesh.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +DataMesh.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +DataMesh.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +DataMesh.METRICS = RelationField("metrics") +DataMesh.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +DataMesh.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +DataMesh.MEANINGS = RelationField("meanings") +DataMesh.MC_MONITORS = RelationField("mcMonitors") +DataMesh.MC_INCIDENTS = RelationField("mcIncidents") +DataMesh.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +DataMesh.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +DataMesh.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +DataMesh.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +DataMesh.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +DataMesh.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +DataMesh.FILES = RelationField("files") +DataMesh.LINKS = RelationField("links") +DataMesh.README = RelationField("readme") +DataMesh.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +DataMesh.SODA_CHECKS = RelationField("sodaChecks") +DataMesh.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +DataMesh.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/data_mesh_related.py b/pyatlan_v9/model/assets/data_mesh_related.py new file mode 100644 index 000000000..23ebce2d1 --- /dev/null +++ b/pyatlan_v9/model/assets/data_mesh_related.py @@ -0,0 +1,168 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for DataMesh module. + +This module contains all Related{Type} classes for the DataMesh type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedCatalog +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedDataMesh", + "RelatedStakeholderTitle", + "RelatedDataDomain", + "RelatedDataProduct", + "RelatedStakeholder", +] + + +class RelatedDataMesh(RelatedCatalog): + """ + Related entity reference for DataMesh assets. + + Extends RelatedCatalog with DataMesh-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DataMesh" so it serializes correctly + + parent_domain_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the parent domain in which this asset exists.""" + + super_domain_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the top-level domain in which this asset exists.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DataMesh" + + +class RelatedStakeholderTitle(RelatedDataMesh): + """ + Related entity reference for StakeholderTitle assets. + + Extends RelatedDataMesh with StakeholderTitle-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "StakeholderTitle" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "StakeholderTitle" + + +class RelatedDataDomain(RelatedDataMesh): + """ + Related entity reference for DataDomain assets. + + Extends RelatedDataMesh with DataDomain-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DataDomain" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DataDomain" + + +class RelatedDataProduct(RelatedDataMesh): + """ + Related entity reference for DataProduct assets. + + Extends RelatedDataMesh with DataProduct-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DataProduct" so it serializes correctly + + data_product_status: Union[str, None, UnsetType] = UNSET + """Status of this data product.""" + + daap_status: Union[str, None, UnsetType] = UNSET + """Status of this data product.""" + + data_product_criticality: Union[str, None, UnsetType] = UNSET + """Criticality of this data product.""" + + daap_criticality: Union[str, None, UnsetType] = UNSET + """Criticality of this data product.""" + + data_product_sensitivity: Union[str, None, UnsetType] = UNSET + """Information sensitivity of this data product.""" + + daap_sensitivity: Union[str, None, UnsetType] = UNSET + """Information sensitivity of this data product.""" + + data_product_visibility: Union[str, None, UnsetType] = UNSET + """Visibility of a data product.""" + + daap_visibility: Union[str, None, UnsetType] = UNSET + """Visibility of a data product.""" + + data_product_assets_dsl: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="dataProductAssetsDSL" + ) + """Search DSL used to define which assets are part of this data product.""" + + data_product_assets_playbook_filter: Union[str, None, UnsetType] = UNSET + """Playbook filter to define which assets are part of this data product.""" + + data_product_score_value: Union[float, None, UnsetType] = UNSET + """Score of this data product.""" + + data_mesh_score_updated_at: Union[int, None, UnsetType] = UNSET + """Timestamp when the score of this data product was last updated.""" + + daap_visibility_users: Union[List[str], None, UnsetType] = UNSET + """list of users for product visibility control""" + + daap_visibility_groups: Union[List[str], None, UnsetType] = UNSET + """list of groups for product visibility control""" + + daap_output_port_guids: Union[List[str], None, UnsetType] = UNSET + """Output ports guids for this data product.""" + + daap_input_port_guids: Union[List[str], None, UnsetType] = UNSET + """Input ports guids for this data product.""" + + daap_lineage_status: Union[str, None, UnsetType] = UNSET + """Status of this data product lineage.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DataProduct" + + +class RelatedStakeholder(RelatedDataMesh): + """ + Related entity reference for Stakeholder assets. + + Extends RelatedDataMesh with Stakeholder-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Stakeholder" so it serializes correctly + + stakeholder_domain_qualified_name: Union[str, None, UnsetType] = UNSET + """""" + + stakeholder_title_guid: Union[str, None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Stakeholder" diff --git a/pyatlan_v9/model/assets/data_product.py b/pyatlan_v9/model/assets/data_product.py new file mode 100644 index 000000000..ce8ed0061 --- /dev/null +++ b/pyatlan_v9/model/assets/data_product.py @@ -0,0 +1,889 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DataProduct asset model with flattened inheritance. + +This module provides: +- DataProduct: Flat asset class (easy to use) +- DataProductAttributes: Nested attributes struct (extends AssetAttributes) +- DataProductNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .asset_related import RelatedAsset +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from .starburst_related import RelatedStarburstDataset +from pyatlan.errors import ErrorCode +from pyatlan.model.enums import DataProductStatus +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.data_mesh import DataProductsAssetsDSL +from pyatlan_v9.model.search import IndexSearchRequest +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .data_mesh_related import RelatedDataDomain, RelatedDataProduct + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class DataProduct(Asset): + """ + Instance of a data product in Atlan. + """ + + DATA_PRODUCT_STATUS: ClassVar[Any] = None + DAAP_STATUS: ClassVar[Any] = None + DATA_PRODUCT_CRITICALITY: ClassVar[Any] = None + DAAP_CRITICALITY: ClassVar[Any] = None + DATA_PRODUCT_SENSITIVITY: ClassVar[Any] = None + DAAP_SENSITIVITY: ClassVar[Any] = None + DATA_PRODUCT_VISIBILITY: ClassVar[Any] = None + DAAP_VISIBILITY: ClassVar[Any] = None + DATA_PRODUCT_ASSETS_DSL: ClassVar[Any] = None + DATA_PRODUCT_ASSETS_PLAYBOOK_FILTER: ClassVar[Any] = None + DATA_PRODUCT_SCORE_VALUE: ClassVar[Any] = None + DATA_MESH_SCORE_UPDATED_AT: ClassVar[Any] = None + DAAP_VISIBILITY_USERS: ClassVar[Any] = None + DAAP_VISIBILITY_GROUPS: ClassVar[Any] = None + DAAP_OUTPUT_PORT_GUIDS: ClassVar[Any] = None + DAAP_INPUT_PORT_GUIDS: ClassVar[Any] = None + DAAP_LINEAGE_STATUS: ClassVar[Any] = None + PARENT_DOMAIN_QUALIFIED_NAME: ClassVar[Any] = None + SUPER_DOMAIN_QUALIFIED_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + DATA_DOMAIN: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + OUTPUT_PORTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + STARBURST_DATASETS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "DataProduct" + + data_product_status: Union[str, None, UnsetType] = UNSET + """Status of this data product.""" + + daap_status: Union[str, None, UnsetType] = UNSET + """Status of this data product.""" + + data_product_criticality: Union[str, None, UnsetType] = UNSET + """Criticality of this data product.""" + + daap_criticality: Union[str, None, UnsetType] = UNSET + """Criticality of this data product.""" + + data_product_sensitivity: Union[str, None, UnsetType] = UNSET + """Information sensitivity of this data product.""" + + daap_sensitivity: Union[str, None, UnsetType] = UNSET + """Information sensitivity of this data product.""" + + data_product_visibility: Union[str, None, UnsetType] = UNSET + """Visibility of a data product.""" + + daap_visibility: Union[str, None, UnsetType] = UNSET + """Visibility of a data product.""" + + data_product_assets_dsl: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="dataProductAssetsDSL" + ) + """Search DSL used to define which assets are part of this data product.""" + + data_product_assets_playbook_filter: Union[str, None, UnsetType] = UNSET + """Playbook filter to define which assets are part of this data product.""" + + data_product_score_value: Union[float, None, UnsetType] = UNSET + """Score of this data product.""" + + data_mesh_score_updated_at: Union[int, None, UnsetType] = UNSET + """Timestamp when the score of this data product was last updated.""" + + daap_visibility_users: Union[List[str], None, UnsetType] = UNSET + """list of users for product visibility control""" + + daap_visibility_groups: Union[List[str], None, UnsetType] = UNSET + """list of groups for product visibility control""" + + daap_output_port_guids: Union[List[str], None, UnsetType] = UNSET + """Output ports guids for this data product.""" + + daap_input_port_guids: Union[List[str], None, UnsetType] = UNSET + """Input ports guids for this data product.""" + + daap_lineage_status: Union[str, None, UnsetType] = UNSET + """Status of this data product lineage.""" + + parent_domain_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the parent domain in which this asset exists.""" + + super_domain_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the top-level domain in which this asset exists.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + data_domain: Union[RelatedDataDomain, None, UnsetType] = UNSET + """Data domain in which this data product exists.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + output_ports: Union[List[RelatedAsset], None, UnsetType] = UNSET + """Output ports for this data product.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + input_ports: Union[List[RelatedAsset], None, UnsetType] = UNSET + """Input ports for this data product.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + starburst_datasets: Union[List[RelatedStarburstDataset], None, UnsetType] = UNSET + """Starburst datasets published by this data product.""" + + def __post_init__(self) -> None: + self.type_name = "DataProduct" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/product/[^/]+$") + + @classmethod + def _get_super_domain_qualified_name( + cls, domain_qualified_name: str + ) -> Union[str, None]: + """Extract the top-most ancestor domain qualified name.""" + domain_qn_prefix = re.compile(r"(default/domain/[a-zA-Z0-9-]+/super)/.*") + if domain_qualified_name: + match = domain_qn_prefix.match(domain_qualified_name) + if match and match.group(1): + return match.group(1) + if domain_qualified_name.startswith("default/domain/"): + return domain_qualified_name + return None + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + domain_qualified_name: str, + asset_selection: IndexSearchRequest, + ) -> "DataProduct": + """Create a new DataProduct asset.""" + validate_required_fields( + ["name", "domain_qualified_name", "asset_selection"], + [name, domain_qualified_name, asset_selection], + ) + assets_playbook_filter = '{"condition":"AND","isGroupLocked":false,"rules":[]}' + return cls( + name=name, + data_product_assets_dsl=DataProductsAssetsDSL.get_asset_selection( + asset_selection + ), + data_domain=RelatedDataDomain( + unique_attributes={"qualifiedName": domain_qualified_name} + ), + qualified_name=f"{domain_qualified_name}/product/{name}", + data_product_assets_playbook_filter=assets_playbook_filter, + parent_domain_qualified_name=domain_qualified_name, + super_domain_qualified_name=cls._get_super_domain_qualified_name( + domain_qualified_name + ), + daap_status=DataProductStatus.ACTIVE, + ) + + @classmethod + @init_guid + def updater( + cls, + *, + qualified_name: str, + name: str, + asset_selection: Union[IndexSearchRequest, None] = None, + ) -> "DataProduct": + """Create a DataProduct instance for update operations.""" + validate_required_fields(["name", "qualified_name"], [name, qualified_name]) + fields = qualified_name.split("/") + if len(fields) < 5: + raise ValueError(f"Invalid data product qualified_name: {qualified_name}") + product = cls(qualified_name=qualified_name, name=name) + if asset_selection: + product.data_product_assets_dsl = DataProductsAssetsDSL.get_asset_selection( + asset_selection + ) + return product + + def trim_to_required(self) -> "DataProduct": + """Return only the required fields for updates.""" + return DataProduct.updater(qualified_name=self.qualified_name, name=self.name) + + def get_assets(self, client: "AtlanClient"): + """Retrieve assets linked to this data product.""" + dp_dsl = self.data_product_assets_dsl + if not dp_dsl: + raise ErrorCode.MISSING_DATA_PRODUCT_ASSET_DSL.exception_with_parameters() + query_data = msgspec.json.decode(dp_dsl).get("query", {}) + request = msgspec.convert(query_data, IndexSearchRequest, strict=False) + return client.asset.search(request) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _data_product_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> DataProduct: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + DataProduct instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _data_product_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DataProductAttributes(AssetAttributes): + """DataProduct-specific attributes for nested API format.""" + + data_product_status: Union[str, None, UnsetType] = UNSET + """Status of this data product.""" + + daap_status: Union[str, None, UnsetType] = UNSET + """Status of this data product.""" + + data_product_criticality: Union[str, None, UnsetType] = UNSET + """Criticality of this data product.""" + + daap_criticality: Union[str, None, UnsetType] = UNSET + """Criticality of this data product.""" + + data_product_sensitivity: Union[str, None, UnsetType] = UNSET + """Information sensitivity of this data product.""" + + daap_sensitivity: Union[str, None, UnsetType] = UNSET + """Information sensitivity of this data product.""" + + data_product_visibility: Union[str, None, UnsetType] = UNSET + """Visibility of a data product.""" + + daap_visibility: Union[str, None, UnsetType] = UNSET + """Visibility of a data product.""" + + data_product_assets_dsl: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="dataProductAssetsDSL" + ) + """Search DSL used to define which assets are part of this data product.""" + + data_product_assets_playbook_filter: Union[str, None, UnsetType] = UNSET + """Playbook filter to define which assets are part of this data product.""" + + data_product_score_value: Union[float, None, UnsetType] = UNSET + """Score of this data product.""" + + data_mesh_score_updated_at: Union[int, None, UnsetType] = UNSET + """Timestamp when the score of this data product was last updated.""" + + daap_visibility_users: Union[List[str], None, UnsetType] = UNSET + """list of users for product visibility control""" + + daap_visibility_groups: Union[List[str], None, UnsetType] = UNSET + """list of groups for product visibility control""" + + daap_output_port_guids: Union[List[str], None, UnsetType] = UNSET + """Output ports guids for this data product.""" + + daap_input_port_guids: Union[List[str], None, UnsetType] = UNSET + """Input ports guids for this data product.""" + + daap_lineage_status: Union[str, None, UnsetType] = UNSET + """Status of this data product lineage.""" + + parent_domain_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the parent domain in which this asset exists.""" + + super_domain_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the top-level domain in which this asset exists.""" + + +class DataProductRelationshipAttributes(AssetRelationshipAttributes): + """DataProduct-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + data_domain: Union[RelatedDataDomain, None, UnsetType] = UNSET + """Data domain in which this data product exists.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + output_ports: Union[List[RelatedAsset], None, UnsetType] = UNSET + """Output ports for this data product.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + input_ports: Union[List[RelatedAsset], None, UnsetType] = UNSET + """Input ports for this data product.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + starburst_datasets: Union[List[RelatedStarburstDataset], None, UnsetType] = UNSET + """Starburst datasets published by this data product.""" + + +class DataProductNested(AssetNested): + """DataProduct in nested API format for high-performance serialization.""" + + attributes: Union[DataProductAttributes, UnsetType] = UNSET + relationship_attributes: Union[DataProductRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + DataProductRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + DataProductRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DATA_PRODUCT_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "data_domain", + "output_port_data_products", + "output_ports", + "input_port_data_products", + "input_ports", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", + "starburst_datasets", +] + + +def _populate_data_product_attrs( + attrs: DataProductAttributes, obj: DataProduct +) -> None: + """Populate DataProduct-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.data_product_status = obj.data_product_status + attrs.daap_status = obj.daap_status + attrs.data_product_criticality = obj.data_product_criticality + attrs.daap_criticality = obj.daap_criticality + attrs.data_product_sensitivity = obj.data_product_sensitivity + attrs.daap_sensitivity = obj.daap_sensitivity + attrs.data_product_visibility = obj.data_product_visibility + attrs.daap_visibility = obj.daap_visibility + attrs.data_product_assets_dsl = obj.data_product_assets_dsl + attrs.data_product_assets_playbook_filter = obj.data_product_assets_playbook_filter + attrs.data_product_score_value = obj.data_product_score_value + attrs.data_mesh_score_updated_at = obj.data_mesh_score_updated_at + attrs.daap_visibility_users = obj.daap_visibility_users + attrs.daap_visibility_groups = obj.daap_visibility_groups + attrs.daap_output_port_guids = obj.daap_output_port_guids + attrs.daap_input_port_guids = obj.daap_input_port_guids + attrs.daap_lineage_status = obj.daap_lineage_status + attrs.parent_domain_qualified_name = obj.parent_domain_qualified_name + attrs.super_domain_qualified_name = obj.super_domain_qualified_name + + +def _extract_data_product_attrs(attrs: DataProductAttributes) -> dict: + """Extract all DataProduct attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["data_product_status"] = attrs.data_product_status + result["daap_status"] = attrs.daap_status + result["data_product_criticality"] = attrs.data_product_criticality + result["daap_criticality"] = attrs.daap_criticality + result["data_product_sensitivity"] = attrs.data_product_sensitivity + result["daap_sensitivity"] = attrs.daap_sensitivity + result["data_product_visibility"] = attrs.data_product_visibility + result["daap_visibility"] = attrs.daap_visibility + result["data_product_assets_dsl"] = attrs.data_product_assets_dsl + result["data_product_assets_playbook_filter"] = ( + attrs.data_product_assets_playbook_filter + ) + result["data_product_score_value"] = attrs.data_product_score_value + result["data_mesh_score_updated_at"] = attrs.data_mesh_score_updated_at + result["daap_visibility_users"] = attrs.daap_visibility_users + result["daap_visibility_groups"] = attrs.daap_visibility_groups + result["daap_output_port_guids"] = attrs.daap_output_port_guids + result["daap_input_port_guids"] = attrs.daap_input_port_guids + result["daap_lineage_status"] = attrs.daap_lineage_status + result["parent_domain_qualified_name"] = attrs.parent_domain_qualified_name + result["super_domain_qualified_name"] = attrs.super_domain_qualified_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _data_product_to_nested(data_product: DataProduct) -> DataProductNested: + """Convert flat DataProduct to nested format.""" + attrs = DataProductAttributes() + _populate_data_product_attrs(attrs, data_product) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + data_product, _DATA_PRODUCT_REL_FIELDS, DataProductRelationshipAttributes + ) + return DataProductNested( + guid=data_product.guid, + type_name=data_product.type_name, + status=data_product.status, + version=data_product.version, + create_time=data_product.create_time, + update_time=data_product.update_time, + created_by=data_product.created_by, + updated_by=data_product.updated_by, + classifications=data_product.classifications, + classification_names=data_product.classification_names, + meanings=data_product.meanings, + labels=data_product.labels, + business_attributes=data_product.business_attributes, + custom_attributes=data_product.custom_attributes, + pending_tasks=data_product.pending_tasks, + proxy=data_product.proxy, + is_incomplete=data_product.is_incomplete, + provenance_type=data_product.provenance_type, + home_id=data_product.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _data_product_from_nested(nested: DataProductNested) -> DataProduct: + """Convert nested format to flat DataProduct.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else DataProductAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DATA_PRODUCT_REL_FIELDS, + DataProductRelationshipAttributes, + ) + return DataProduct( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_data_product_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _data_product_to_nested_bytes(data_product: DataProduct, serde: Serde) -> bytes: + """Convert flat DataProduct to nested JSON bytes.""" + return serde.encode(_data_product_to_nested(data_product)) + + +def _data_product_from_nested_bytes(data: bytes, serde: Serde) -> DataProduct: + """Convert nested JSON bytes to flat DataProduct.""" + nested = serde.decode(data, DataProductNested) + return _data_product_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +DataProduct.DATA_PRODUCT_STATUS = KeywordField("dataProductStatus", "dataProductStatus") +DataProduct.DAAP_STATUS = KeywordField("daapStatus", "daapStatus") +DataProduct.DATA_PRODUCT_CRITICALITY = KeywordField( + "dataProductCriticality", "dataProductCriticality" +) +DataProduct.DAAP_CRITICALITY = KeywordField("daapCriticality", "daapCriticality") +DataProduct.DATA_PRODUCT_SENSITIVITY = KeywordField( + "dataProductSensitivity", "dataProductSensitivity" +) +DataProduct.DAAP_SENSITIVITY = KeywordField("daapSensitivity", "daapSensitivity") +DataProduct.DATA_PRODUCT_VISIBILITY = KeywordField( + "dataProductVisibility", "dataProductVisibility" +) +DataProduct.DAAP_VISIBILITY = KeywordField("daapVisibility", "daapVisibility") +DataProduct.DATA_PRODUCT_ASSETS_DSL = KeywordField( + "dataProductAssetsDSL", "dataProductAssetsDSL" +) +DataProduct.DATA_PRODUCT_ASSETS_PLAYBOOK_FILTER = KeywordField( + "dataProductAssetsPlaybookFilter", "dataProductAssetsPlaybookFilter" +) +DataProduct.DATA_PRODUCT_SCORE_VALUE = NumericField( + "dataProductScoreValue", "dataProductScoreValue" +) +DataProduct.DATA_MESH_SCORE_UPDATED_AT = NumericField( + "dataMeshScoreUpdatedAt", "dataMeshScoreUpdatedAt" +) +DataProduct.DAAP_VISIBILITY_USERS = KeywordField( + "daapVisibilityUsers", "daapVisibilityUsers" +) +DataProduct.DAAP_VISIBILITY_GROUPS = KeywordField( + "daapVisibilityGroups", "daapVisibilityGroups" +) +DataProduct.DAAP_OUTPUT_PORT_GUIDS = KeywordField( + "daapOutputPortGuids", "daapOutputPortGuids" +) +DataProduct.DAAP_INPUT_PORT_GUIDS = KeywordField( + "daapInputPortGuids", "daapInputPortGuids" +) +DataProduct.DAAP_LINEAGE_STATUS = KeywordField("daapLineageStatus", "daapLineageStatus") +DataProduct.PARENT_DOMAIN_QUALIFIED_NAME = KeywordTextField( + "parentDomainQualifiedName", + "parentDomainQualifiedName", + "parentDomainQualifiedName.text", +) +DataProduct.SUPER_DOMAIN_QUALIFIED_NAME = KeywordTextField( + "superDomainQualifiedName", + "superDomainQualifiedName", + "superDomainQualifiedName.text", +) +DataProduct.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +DataProduct.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +DataProduct.ANOMALO_CHECKS = RelationField("anomaloChecks") +DataProduct.APPLICATION = RelationField("application") +DataProduct.APPLICATION_FIELD = RelationField("applicationField") +DataProduct.DATA_DOMAIN = RelationField("dataDomain") +DataProduct.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +DataProduct.OUTPUT_PORTS = RelationField("outputPorts") +DataProduct.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +DataProduct.INPUT_PORTS = RelationField("inputPorts") +DataProduct.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +DataProduct.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +DataProduct.METRICS = RelationField("metrics") +DataProduct.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +DataProduct.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +DataProduct.MEANINGS = RelationField("meanings") +DataProduct.MC_MONITORS = RelationField("mcMonitors") +DataProduct.MC_INCIDENTS = RelationField("mcIncidents") +DataProduct.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +DataProduct.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +DataProduct.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +DataProduct.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +DataProduct.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +DataProduct.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +DataProduct.FILES = RelationField("files") +DataProduct.LINKS = RelationField("links") +DataProduct.README = RelationField("readme") +DataProduct.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +DataProduct.SODA_CHECKS = RelationField("sodaChecks") +DataProduct.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +DataProduct.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") +DataProduct.STARBURST_DATASETS = RelationField("starburstDatasets") diff --git a/pyatlan_v9/model/assets/data_quality.py b/pyatlan_v9/model/assets/data_quality.py new file mode 100644 index 000000000..b11eded2b --- /dev/null +++ b/pyatlan_v9/model/assets/data_quality.py @@ -0,0 +1,542 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DataQuality asset model with flattened inheritance. + +This module provides: +- DataQuality: Flat asset class (easy to use) +- DataQualityAttributes: Nested attributes struct (extends AssetAttributes) +- DataQualityNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .data_quality_related import RelatedDataQualityRule, RelatedMetric + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class DataQuality(Asset): + """ + Base class for data quality assets. + """ + + DQ_IS_PART_OF_CONTRACT: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "DataQuality" + + dq_is_part_of_contract: Union[bool, None, UnsetType] = UNSET + """Whether this data quality is part of contract (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "DataQuality" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _data_quality_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> DataQuality: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + DataQuality instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _data_quality_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DataQualityAttributes(AssetAttributes): + """DataQuality-specific attributes for nested API format.""" + + dq_is_part_of_contract: Union[bool, None, UnsetType] = UNSET + """Whether this data quality is part of contract (true) or not (false).""" + + +class DataQualityRelationshipAttributes(AssetRelationshipAttributes): + """DataQuality-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DataQualityNested(AssetNested): + """DataQuality in nested API format for high-performance serialization.""" + + attributes: Union[DataQualityAttributes, UnsetType] = UNSET + relationship_attributes: Union[DataQualityRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + DataQualityRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + DataQualityRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DATA_QUALITY_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_data_quality_attrs( + attrs: DataQualityAttributes, obj: DataQuality +) -> None: + """Populate DataQuality-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.dq_is_part_of_contract = obj.dq_is_part_of_contract + + +def _extract_data_quality_attrs(attrs: DataQualityAttributes) -> dict: + """Extract all DataQuality attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["dq_is_part_of_contract"] = attrs.dq_is_part_of_contract + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _data_quality_to_nested(data_quality: DataQuality) -> DataQualityNested: + """Convert flat DataQuality to nested format.""" + attrs = DataQualityAttributes() + _populate_data_quality_attrs(attrs, data_quality) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + data_quality, _DATA_QUALITY_REL_FIELDS, DataQualityRelationshipAttributes + ) + return DataQualityNested( + guid=data_quality.guid, + type_name=data_quality.type_name, + status=data_quality.status, + version=data_quality.version, + create_time=data_quality.create_time, + update_time=data_quality.update_time, + created_by=data_quality.created_by, + updated_by=data_quality.updated_by, + classifications=data_quality.classifications, + classification_names=data_quality.classification_names, + meanings=data_quality.meanings, + labels=data_quality.labels, + business_attributes=data_quality.business_attributes, + custom_attributes=data_quality.custom_attributes, + pending_tasks=data_quality.pending_tasks, + proxy=data_quality.proxy, + is_incomplete=data_quality.is_incomplete, + provenance_type=data_quality.provenance_type, + home_id=data_quality.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _data_quality_from_nested(nested: DataQualityNested) -> DataQuality: + """Convert nested format to flat DataQuality.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else DataQualityAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DATA_QUALITY_REL_FIELDS, + DataQualityRelationshipAttributes, + ) + return DataQuality( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_data_quality_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _data_quality_to_nested_bytes(data_quality: DataQuality, serde: Serde) -> bytes: + """Convert flat DataQuality to nested JSON bytes.""" + return serde.encode(_data_quality_to_nested(data_quality)) + + +def _data_quality_from_nested_bytes(data: bytes, serde: Serde) -> DataQuality: + """Convert nested JSON bytes to flat DataQuality.""" + nested = serde.decode(data, DataQualityNested) + return _data_quality_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + RelationField, +) + +DataQuality.DQ_IS_PART_OF_CONTRACT = BooleanField( + "dqIsPartOfContract", "dqIsPartOfContract" +) +DataQuality.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +DataQuality.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +DataQuality.ANOMALO_CHECKS = RelationField("anomaloChecks") +DataQuality.APPLICATION = RelationField("application") +DataQuality.APPLICATION_FIELD = RelationField("applicationField") +DataQuality.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +DataQuality.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +DataQuality.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +DataQuality.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +DataQuality.METRICS = RelationField("metrics") +DataQuality.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +DataQuality.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +DataQuality.MEANINGS = RelationField("meanings") +DataQuality.MC_MONITORS = RelationField("mcMonitors") +DataQuality.MC_INCIDENTS = RelationField("mcIncidents") +DataQuality.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +DataQuality.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +DataQuality.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +DataQuality.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +DataQuality.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +DataQuality.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +DataQuality.FILES = RelationField("files") +DataQuality.LINKS = RelationField("links") +DataQuality.README = RelationField("readme") +DataQuality.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +DataQuality.SODA_CHECKS = RelationField("sodaChecks") +DataQuality.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +DataQuality.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/data_quality_related.py b/pyatlan_v9/model/assets/data_quality_related.py new file mode 100644 index 000000000..30a413de5 --- /dev/null +++ b/pyatlan_v9/model/assets/data_quality_related.py @@ -0,0 +1,188 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for DataQuality module. + +This module contains all Related{Type} classes for the DataQuality type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Any, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedCatalog +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedDataQuality", + "RelatedDataQualityRule", + "RelatedDataQualityRuleTemplate", + "RelatedMetric", +] + + +class RelatedDataQuality(RelatedCatalog): + """ + Related entity reference for DataQuality assets. + + Extends RelatedCatalog with DataQuality-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DataQuality" so it serializes correctly + + dq_is_part_of_contract: Union[bool, None, UnsetType] = UNSET + """Whether this data quality is part of contract (true) or not (false).""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DataQuality" + + +class RelatedDataQualityRule(RelatedDataQuality): + """ + Related entity reference for DataQualityRule assets. + + Extends RelatedDataQuality with DataQualityRule-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DataQualityRule" so it serializes correctly + + dq_rule_base_dataset_qualified_name: Union[str, None, UnsetType] = UNSET + """Base dataset qualified name that attached to this rule.""" + + dq_rule_base_column_qualified_name: Union[str, None, UnsetType] = UNSET + """Base column qualified name that attached to this rule.""" + + dq_rule_reference_dataset_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of unique reference dataset's qualified names related to this rule.""" + + dq_rule_reference_column_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of unique reference column's qualified names related to this rule.""" + + dq_rule_source_sync_status: Union[str, None, UnsetType] = UNSET + """Latest sync status of the rule to the source.""" + + dq_rule_source_sync_error_code: Union[str, None, UnsetType] = UNSET + """Error code in the case of state being "failure".""" + + dq_rule_source_sync_error_message: Union[str, None, UnsetType] = UNSET + """Error message in the case of state being "error".""" + + dq_rule_source_sync_raw_error: Union[str, None, UnsetType] = UNSET + """Raw error message from the source.""" + + dq_rule_source_synced_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the rule synced to the source.""" + + dq_rule_latest_result: Union[str, None, UnsetType] = UNSET + """Latest result of the rule.""" + + dq_rule_latest_result_computed_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the latest rule result was evaluated.""" + + dq_rule_latest_result_fetched_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the latest rule result was fetched.""" + + dq_rule_latest_metric_value: Union[str, None, UnsetType] = UNSET + """Last result metrics value of the rule.""" + + dq_rule_latest_metric_value_computed_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the latest metric value was evaluated in the source.""" + + dq_rule_dimension: Union[str, None, UnsetType] = UNSET + """Dimension of the data quality rule.""" + + dq_rule_template_name: Union[str, None, UnsetType] = UNSET + """Name of the rule template corresponding to the rule.""" + + dq_rule_status: Union[str, None, UnsetType] = UNSET + """Status of the rule.""" + + dq_rule_alert_priority: Union[str, None, UnsetType] = UNSET + """Default priority level for alerts involving this rule.""" + + dq_rule_config_arguments: Union[Dict[str, Any], None, UnsetType] = UNSET + """Json string of the rule config that contains the rule definitions.""" + + dq_rule_custom_sql: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="dqRuleCustomSQL" + ) + """SQL code for custom SQL rules.""" + + dq_rule_custom_sql_return_type: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="dqRuleCustomSQLReturnType" + ) + """Type of result returned by the custom SQL (number of rows or numeric value).""" + + dq_rule_failed_rows_sql: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="dqRuleFailedRowsSQL" + ) + """SQL query used to retrieve failed rows.""" + + dq_rule_row_scope_filtering_enabled: Union[bool, None, UnsetType] = UNSET + """Whether row scope filtering is enabled for this data quality rule (true) or not (false).""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DataQualityRule" + + +class RelatedDataQualityRuleTemplate(RelatedDataQuality): + """ + Related entity reference for DataQualityRuleTemplate assets. + + Extends RelatedDataQuality with DataQualityRuleTemplate-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DataQualityRuleTemplate" so it serializes correctly + + dq_rule_template_dimension: Union[str, None, UnsetType] = UNSET + """Name of the dimension the rule belongs to.""" + + dq_rule_template_config: Union[Dict[str, Any], None, UnsetType] = UNSET + """Rule config that will help render the form and define the rule.""" + + dq_rule_template_metric_value_type: Union[str, None, UnsetType] = UNSET + """Type of the metric value returned by the rule(absolute, percentage, time etc.).""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DataQualityRuleTemplate" + + +class RelatedMetric(RelatedDataQuality): + """ + Related entity reference for Metric assets. + + Extends RelatedDataQuality with Metric-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Metric" so it serializes correctly + + metric_type: Union[str, None, UnsetType] = UNSET + """Type of the metric.""" + + metric_sql: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="metricSQL" + ) + """SQL query used to compute the metric.""" + + metric_filters: Union[str, None, UnsetType] = UNSET + """Filters to be applied to the metric query.""" + + metric_time_grains: Union[List[str], None, UnsetType] = UNSET + """List of time grains to be applied to the metric query.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Metric" diff --git a/pyatlan_v9/model/assets/data_quality_rule.py b/pyatlan_v9/model/assets/data_quality_rule.py new file mode 100644 index 000000000..b40384928 --- /dev/null +++ b/pyatlan_v9/model/assets/data_quality_rule.py @@ -0,0 +1,1196 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DataQualityRule asset model with flattened inheritance. + +This module provides: +- DataQualityRule: Flat asset class (easy to use) +- DataQualityRuleAttributes: Nested attributes struct (extends AssetAttributes) +- DataQualityRuleNested: Nested API format struct +""" + +from __future__ import annotations + +import json +import time +import uuid +from typing import TYPE_CHECKING, ClassVar, Optional, Union + +from msgspec import UNSET, UnsetType + +from pyatlan.errors import ErrorCode +from pyatlan.model.enums import ( + DataQualityDimension, + DataQualityRuleAlertPriority, + DataQualityRuleCustomSQLReturnType, + DataQualityRuleStatus, + DataQualityRuleTemplateType, + DataQualityRuleThresholdCompareOperator, + DataQualityRuleThresholdUnit, + DataQualitySourceSyncStatus, +) +from pyatlan.model.fields.atlan_fields import BooleanField, KeywordField, RelationField +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.structs import ( + DataQualityRuleConfigArguments, + DataQualityRuleThresholdObject, +) +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .asset import Asset, AssetAttributes, AssetNested, AssetRelationshipAttributes +from .asset_related import RelatedAsset +from .data_quality_related import RelatedDataQualityRuleTemplate +from .sql_related import RelatedColumn + +if TYPE_CHECKING: + from pyatlan_v9.client.atlan import AtlanClient + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class DataQualityRule(Asset): + """ + Class to define a rule for the given asset in Atlan. + """ + + # ========================================================================= + # Field Descriptors (class-level, for search query building) + # ========================================================================= + + DQ_RULE_BASE_DATASET_QUALIFIED_NAME: ClassVar[KeywordField] = KeywordField( + "dqRuleBaseDatasetQualifiedName", "dqRuleBaseDatasetQualifiedName" + ) + DQ_RULE_BASE_COLUMN_QUALIFIED_NAME: ClassVar[KeywordField] = KeywordField( + "dqRuleBaseColumnQualifiedName", "dqRuleBaseColumnQualifiedName" + ) + DQ_RULE_SOURCE_SYNC_STATUS: ClassVar[KeywordField] = KeywordField( + "dqRuleSourceSyncStatus", "dqRuleSourceSyncStatus" + ) + DQ_RULE_LATEST_RESULT: ClassVar[KeywordField] = KeywordField( + "dqRuleLatestResult", "dqRuleLatestResult" + ) + DQ_RULE_DIMENSION: ClassVar[KeywordField] = KeywordField( + "dqRuleDimension", "dqRuleDimension" + ) + DQ_RULE_TEMPLATE_NAME: ClassVar[KeywordField] = KeywordField( + "dqRuleTemplateName", "dqRuleTemplateName" + ) + DQ_RULE_STATUS: ClassVar[KeywordField] = KeywordField( + "dqRuleStatus", "dqRuleStatus" + ) + DQ_RULE_ALERT_PRIORITY: ClassVar[KeywordField] = KeywordField( + "dqRuleAlertPriority", "dqRuleAlertPriority" + ) + DQ_RULE_CONFIG_ARGUMENTS: ClassVar[KeywordField] = KeywordField( + "dqRuleConfigArguments", "dqRuleConfigArguments" + ) + DQ_RULE_CUSTOM_SQL: ClassVar[KeywordField] = KeywordField( + "dqRuleCustomSQL", "dqRuleCustomSQL" + ) + DQ_RULE_CUSTOM_SQL_RETURN_TYPE: ClassVar[KeywordField] = KeywordField( + "dqRuleCustomSQLReturnType", "dqRuleCustomSQLReturnType" + ) + DQ_RULE_ROW_SCOPE_FILTERING_ENABLED: ClassVar[BooleanField] = BooleanField( + "dqRuleRowScopeFilteringEnabled", "dqRuleRowScopeFilteringEnabled" + ) + DQ_RULE_TEMPLATE: ClassVar[RelationField] = RelationField("dqRuleTemplate") + DQ_RULE_BASE_DATASET: ClassVar[RelationField] = RelationField("dqRuleBaseDataset") + DQ_RULE_BASE_COLUMN: ClassVar[RelationField] = RelationField("dqRuleBaseColumn") + + # ========================================================================= + # Instance Fields + # ========================================================================= + + # Override type_name with DataQualityRule-specific default + type_name: Union[str, UnsetType] = "DataQualityRule" + + dq_rule_base_dataset_qualified_name: Union[str, None, UnsetType] = UNSET + """Base dataset qualified name that attached to this rule.""" + + dq_rule_base_column_qualified_name: Union[str, None, UnsetType] = UNSET + """Base column qualified name that attached to this rule.""" + + dq_rule_reference_dataset_qualified_names: Union[list[str], None, UnsetType] = UNSET + """List of unique reference dataset's qualified names related to this rule.""" + + dq_rule_reference_column_qualified_names: Union[list[str], None, UnsetType] = UNSET + """List of unique reference column's qualified names related to this rule.""" + + dq_rule_source_sync_status: Union[str, None, UnsetType] = UNSET + """Latest sync status of the rule to the source.""" + + dq_rule_source_sync_error_code: Union[str, None, UnsetType] = UNSET + """Error code in the case of state being "failure".""" + + dq_rule_source_sync_error_message: Union[str, None, UnsetType] = UNSET + """Error message in the case of state being "error".""" + + dq_rule_source_sync_raw_error: Union[str, None, UnsetType] = UNSET + """Raw error message from the source.""" + + dq_rule_source_synced_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the rule synced to the source.""" + + dq_rule_latest_result: Union[str, None, UnsetType] = UNSET + """Latest result of the rule.""" + + dq_rule_latest_result_computed_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the latest rule result was evaluated.""" + + dq_rule_latest_result_fetched_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the latest rule result was fetched.""" + + dq_rule_latest_metric_value: Union[str, None, UnsetType] = UNSET + """Last result metrics value of the rule.""" + + dq_rule_latest_metric_value_computed_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the latest metric value was evaluated in the source.""" + + dq_rule_dimension: Union[str, None, UnsetType] = UNSET + """Dimension of the data quality rule.""" + + dq_rule_template_name: Union[str, None, UnsetType] = UNSET + """Name of the rule template corresponding to the rule.""" + + dq_rule_status: Union[str, None, UnsetType] = UNSET + """Status of the rule.""" + + dq_rule_alert_priority: Union[str, None, UnsetType] = UNSET + """Default priority level for alerts involving this rule.""" + + dq_rule_config_arguments: Union[DataQualityRuleConfigArguments, None, UnsetType] = ( + UNSET + ) + """Rule config that contains the rule definitions.""" + + dq_rule_custom_sql: Union[str, None, UnsetType] = UNSET + """SQL code for custom SQL rules.""" + + dq_rule_custom_sql_return_type: Union[str, None, UnsetType] = UNSET + """Type of result returned by the custom SQL (number of rows or numeric value).""" + + dq_rule_failed_rows_sql: Union[str, None, UnsetType] = UNSET + """SQL query used to retrieve failed rows.""" + + dq_rule_row_scope_filtering_enabled: Union[bool, None, UnsetType] = UNSET + """Whether row scope filtering is enabled for this data quality rule (true) or not (false).""" + + dq_is_part_of_contract: Union[bool, None, UnsetType] = UNSET + """Whether this data quality is part of contract (true) or not (false).""" + + dq_rule_template: Union[RelatedDataQualityRuleTemplate, None, UnsetType] = UNSET + """Template used to create this rule.""" + + dq_rule_base_dataset: Union[RelatedAsset, None, UnsetType] = UNSET + """Base dataset attached to this rule.""" + + dq_rule_base_column: Union[RelatedColumn, None, UnsetType] = UNSET + """Base column attached to this rule.""" + + dq_rule_reference_datasets: Union[list[RelatedAsset], None, UnsetType] = UNSET + """Datasets referenced in this rule.""" + + dq_rule_reference_columns: Union[list[RelatedColumn], None, UnsetType] = UNSET + """Columns referenced in this rule.""" + + # ========================================================================= + # Creator / Factory Methods + # ========================================================================= + + @classmethod + @init_guid + def custom_sql_creator( + cls, + *, + client: "AtlanClient", + rule_name: str, + asset: Asset, + custom_sql: str, + threshold_compare_operator: DataQualityRuleThresholdCompareOperator, + threshold_value: int, + alert_priority: DataQualityRuleAlertPriority, + dimension: DataQualityDimension, + custom_sql_return_type: Optional[DataQualityRuleCustomSQLReturnType] = None, + description: Optional[str] = None, + ) -> "DataQualityRule": + validate_required_fields( + [ + "client", + "rule_name", + "asset", + "threshold_compare_operator", + "threshold_value", + "alert_priority", + "dimension", + "custom_sql", + ], + [ + client, + rule_name, + asset, + threshold_compare_operator, + threshold_value, + alert_priority, + dimension, + custom_sql, + ], + ) + return cls._build_rule( + client=client, + rule_name=rule_name, + rule_type=DataQualityRuleTemplateType.CUSTOM_SQL, + asset=asset, + threshold_compare_operator=threshold_compare_operator, + threshold_value=threshold_value, + alert_priority=alert_priority, + dimension=dimension, + custom_sql=custom_sql, + custom_sql_return_type=custom_sql_return_type, + description=description, + column=None, + threshold_unit=None, + ) + + @classmethod + @init_guid + def table_level_rule_creator( + cls, + *, + client: "AtlanClient", + rule_type: DataQualityRuleTemplateType, + asset: Asset, + threshold_value: int, + alert_priority: DataQualityRuleAlertPriority, + threshold_compare_operator: Optional[ + DataQualityRuleThresholdCompareOperator + ] = None, + threshold_unit: Optional[DataQualityRuleThresholdUnit] = None, + rule_conditions: Optional[str] = None, + row_scope_filtering_enabled: Optional[bool] = False, + ) -> "DataQualityRule": + validate_required_fields( + ["client", "rule_type", "asset", "threshold_value", "alert_priority"], + [client, rule_type, asset, threshold_value, alert_priority], + ) + template_config = client.dq_template_config_cache.get_template_config( + rule_type.value + ) + asset_for_validation, target_table_asset = ( + cls._fetch_assets_for_row_scope_validation( + client, asset, rule_conditions, row_scope_filtering_enabled or False + ) + ) + validated_threshold_operator = cls._validate_template_features( + rule_type, + rule_conditions, + row_scope_filtering_enabled, + template_config, + threshold_compare_operator, + asset_for_validation, + target_table_asset, + ) + final_threshold_compare_operator = ( + validated_threshold_operator + or threshold_compare_operator + or DataQualityRuleThresholdCompareOperator.LESS_THAN_EQUAL + ) + return cls._build_rule( + client=client, + rule_type=rule_type, + asset=asset, + threshold_compare_operator=final_threshold_compare_operator, + threshold_value=threshold_value, + alert_priority=alert_priority, + rule_name=None, + column=None, + threshold_unit=threshold_unit, + dimension=None, + custom_sql=None, + description=None, + rule_conditions=rule_conditions, + row_scope_filtering_enabled=row_scope_filtering_enabled, + ) + + @classmethod + @init_guid + def column_level_rule_creator( + cls, + *, + client: "AtlanClient", + rule_type: DataQualityRuleTemplateType, + asset: Asset, + column: Asset, + threshold_value: int, + alert_priority: DataQualityRuleAlertPriority, + threshold_compare_operator: Optional[ + DataQualityRuleThresholdCompareOperator + ] = None, + threshold_unit: Optional[DataQualityRuleThresholdUnit] = None, + rule_conditions: Optional[str] = None, + row_scope_filtering_enabled: Optional[bool] = False, + ) -> "DataQualityRule": + validate_required_fields( + [ + "client", + "rule_type", + "asset", + "column", + "threshold_value", + "alert_priority", + ], + [client, rule_type, asset, column, threshold_value, alert_priority], + ) + template_config = client.dq_template_config_cache.get_template_config( + rule_type.value + ) + asset_for_validation, target_table_asset = ( + cls._fetch_assets_for_row_scope_validation( + client, asset, rule_conditions, row_scope_filtering_enabled or False + ) + ) + validated_threshold_operator = cls._validate_template_features( + rule_type, + rule_conditions, + row_scope_filtering_enabled, + template_config, + threshold_compare_operator, + asset_for_validation, + target_table_asset, + ) + final_threshold_compare_operator = ( + validated_threshold_operator + or threshold_compare_operator + or DataQualityRuleThresholdCompareOperator.LESS_THAN_EQUAL + ) + return cls._build_rule( + client=client, + rule_type=rule_type, + asset=asset, + column=column, + threshold_compare_operator=final_threshold_compare_operator, + threshold_value=threshold_value, + alert_priority=alert_priority, + threshold_unit=threshold_unit, + rule_name=None, + dimension=None, + custom_sql=None, + description=None, + rule_conditions=rule_conditions, + row_scope_filtering_enabled=row_scope_filtering_enabled, + ) + + @classmethod + @init_guid + def updater( + cls, + client: "AtlanClient", + qualified_name: str, + threshold_compare_operator: Optional[ + DataQualityRuleThresholdCompareOperator + ] = None, + threshold_value: Optional[int] = None, + alert_priority: Optional[DataQualityRuleAlertPriority] = None, + threshold_unit: Optional[DataQualityRuleThresholdUnit] = None, + dimension: Optional[DataQualityDimension] = None, + custom_sql: Optional[str] = None, + custom_sql_return_type: Optional[DataQualityRuleCustomSQLReturnType] = None, + rule_name: Optional[str] = None, + description: Optional[str] = None, + rule_conditions: Optional[str] = None, + row_scope_filtering_enabled: Optional[bool] = False, + ) -> "DataQualityRule": + from pyatlan_v9.model.fluent_search import FluentSearch + + validate_required_fields( + ["client", "qualified_name"], + [client, qualified_name], + ) + request = ( + FluentSearch() + .where(DataQualityRule.QUALIFIED_NAME.eq(qualified_name)) + .include_on_results(DataQualityRule.NAME) + .include_on_results(DataQualityRule.DQ_RULE_TEMPLATE_NAME) + .include_on_results(DataQualityRule.DQ_RULE_TEMPLATE) + .include_on_results(DataQualityRule.DQ_RULE_BASE_DATASET) + .include_on_results(DataQualityRule.DQ_RULE_BASE_COLUMN) + .include_on_results(DataQualityRule.DQ_RULE_ALERT_PRIORITY) + .include_on_results(DataQualityRule.DISPLAY_NAME) + .include_on_results(DataQualityRule.DQ_RULE_CUSTOM_SQL) + .include_on_results(DataQualityRule.DQ_RULE_CUSTOM_SQL_RETURN_TYPE) + .include_on_results(DataQualityRule.USER_DESCRIPTION) + .include_on_results(DataQualityRule.DQ_RULE_DIMENSION) + .include_on_results(DataQualityRule.DQ_RULE_CONFIG_ARGUMENTS) + .include_on_results(DataQualityRule.DQ_RULE_ROW_SCOPE_FILTERING_ENABLED) + .include_on_results(DataQualityRule.DQ_RULE_SOURCE_SYNC_STATUS) + .include_on_results(DataQualityRule.DQ_RULE_STATUS) + ).to_request() + + results = client.asset.search(request) + + if results.count != 1: + raise ValueError( + f"Expected exactly 1 asset for qualified_name: {qualified_name}, " + f"but found: {results.count}" + ) + search_result = results.current_page()[0] + + retrieved_custom_sql = getattr(search_result, "dq_rule_custom_sql", None) + retrieved_custom_sql_return_type = getattr( + search_result, "dq_rule_custom_sql_return_type", None + ) + retrieved_rule_name = getattr(search_result, "display_name", None) + retrieved_dimension = getattr(search_result, "dq_rule_dimension", None) + retrieved_column = getattr(search_result, "dq_rule_base_column", None) + retrieved_alert_priority = getattr( + search_result, "dq_rule_alert_priority", None + ) + retrieved_row_scope_filtering_enabled = getattr( + search_result, "dq_rule_row_scope_filtering_enabled", None + ) + retrieved_description = getattr(search_result, "user_description", None) + retrieved_asset = getattr(search_result, "dq_rule_base_dataset", None) + retrieved_template_rule_name = getattr( + search_result, "dq_rule_template_name", None + ) + retrieved_template = getattr(search_result, "dq_rule_template", None) + + config_args = getattr(search_result, "dq_rule_config_arguments", None) + threshold_obj = ( + getattr(config_args, "dq_rule_threshold_object", None) + if config_args + else None + ) + retrieved_threshold_compare_operator = ( + getattr(threshold_obj, "dq_rule_threshold_compare_operator", None) + if threshold_obj + else None + ) + retrieved_threshold_value = ( + getattr(threshold_obj, "dq_rule_threshold_value", None) + if threshold_obj + else None + ) + retrieved_threshold_unit = ( + getattr(threshold_obj, "dq_rule_threshold_unit", None) + if threshold_obj + else None + ) + + template_config = None + if retrieved_template_rule_name: + template_config = client.dq_template_config_cache.get_template_config( + retrieved_template_rule_name + ) + + if rule_conditions: + final_rule_conditions = rule_conditions + elif config_args is not None: + final_rule_conditions = getattr( + config_args, "dq_rule_config_rule_conditions", None + ) + else: + final_rule_conditions = None + + final_row_scope_filtering_enabled = ( + row_scope_filtering_enabled or retrieved_row_scope_filtering_enabled + ) + if retrieved_asset: + retrieved_asset, target_table_asset = ( + cls._fetch_assets_for_row_scope_validation( + client, + retrieved_asset, + final_rule_conditions, + final_row_scope_filtering_enabled, + ) + ) + else: + target_table_asset = None + + validated_threshold_operator = None + if retrieved_template_rule_name and template_config: + try: + retrieved_rule_type = DataQualityRuleTemplateType( + retrieved_template_rule_name + ) + validated_threshold_operator = cls._validate_template_features( + retrieved_rule_type, + final_rule_conditions, + final_row_scope_filtering_enabled, + template_config, + threshold_compare_operator or retrieved_threshold_compare_operator, + retrieved_asset, + target_table_asset, + ) + except ValueError: + pass + + final_compare_operator = ( + validated_threshold_operator + or threshold_compare_operator + or retrieved_threshold_compare_operator + or DataQualityRuleThresholdCompareOperator.LESS_THAN_EQUAL + ) + + rule = cls( + name="", + dq_rule_config_arguments=DataQualityRuleConfigArguments( + dq_rule_threshold_object=DataQualityRuleThresholdObject( + dq_rule_threshold_compare_operator=final_compare_operator, + dq_rule_threshold_value=threshold_value + or retrieved_threshold_value, + dq_rule_threshold_unit=threshold_unit or retrieved_threshold_unit, + ), + dq_rule_config_rule_conditions=final_rule_conditions, + ), + dq_rule_base_dataset_qualified_name=( + retrieved_asset.qualified_name if retrieved_asset else None + ), + dq_rule_alert_priority=alert_priority or retrieved_alert_priority, + dq_rule_row_scope_filtering_enabled=final_row_scope_filtering_enabled, + dq_rule_base_dataset=retrieved_asset, + qualified_name=qualified_name, + dq_rule_dimension=dimension or retrieved_dimension, + dq_rule_template_name=retrieved_template_rule_name, + dq_rule_template=( + DataQualityRuleTemplate.ref_by_qualified_name( + qualified_name=retrieved_template.qualified_name + ) + if retrieved_template + else None + ), + ) + + if retrieved_column is not None: + rule.dq_rule_base_column_qualified_name = retrieved_column.qualified_name + rule.dq_rule_base_column = retrieved_column + + final_custom_sql = custom_sql or retrieved_custom_sql + if final_custom_sql is not None: + rule.dq_rule_custom_sql = final_custom_sql + rule.display_name = rule_name or retrieved_rule_name + rule.dq_rule_custom_sql_return_type = ( + custom_sql_return_type or retrieved_custom_sql_return_type + ) + if description is not None: + rule.user_description = description or retrieved_description + + return rule + + # ========================================================================= + # Internal Builder & Validation Helpers + # ========================================================================= + + @classmethod + def _build_rule( + cls, + *, + client: "AtlanClient", + rule_type: DataQualityRuleTemplateType, + asset: Asset, + threshold_compare_operator: DataQualityRuleThresholdCompareOperator, + threshold_value: int, + alert_priority: DataQualityRuleAlertPriority, + rule_name: Optional[str] = None, + column: Optional[Asset] = None, + threshold_unit: Optional[DataQualityRuleThresholdUnit] = None, + dimension: Optional[DataQualityDimension] = None, + custom_sql: Optional[str] = None, + custom_sql_return_type: Optional[DataQualityRuleCustomSQLReturnType] = None, + description: Optional[str] = None, + rule_conditions: Optional[str] = None, + row_scope_filtering_enabled: Optional[bool] = False, + ) -> "DataQualityRule": + """Internal helper that mirrors the legacy ``Attributes.creator`` logic.""" + template_config = client.dq_template_config_cache.get_template_config( + rule_type.value + ) + if template_config is None: + raise ErrorCode.DQ_RULE_NOT_FOUND.exception_with_parameters(rule_type.value) + + template_rule_name = template_config.get("name") + template_qualified_name = template_config.get("qualified_name") + + if dimension is None: + dimension = template_config.get("dimension") + + if threshold_unit is None: + config = template_config.get("config") + if config is not None: + threshold_unit = cls._get_template_config_value( + config.dq_rule_template_config_threshold_object, + "dqRuleTemplateConfigThresholdUnit", + "default", + ) + + rule = cls( + name="", + dq_rule_config_arguments=DataQualityRuleConfigArguments( + dq_rule_threshold_object=DataQualityRuleThresholdObject( + dq_rule_threshold_compare_operator=threshold_compare_operator, + dq_rule_threshold_value=threshold_value, + dq_rule_threshold_unit=threshold_unit, + ), + dq_rule_config_rule_conditions=rule_conditions, + ), + dq_rule_base_dataset_qualified_name=asset.qualified_name, + dq_rule_alert_priority=alert_priority, + dq_rule_row_scope_filtering_enabled=row_scope_filtering_enabled, + dq_rule_source_sync_status=DataQualitySourceSyncStatus.IN_PROGRESS, + dq_rule_status=DataQualityRuleStatus.ACTIVE, + dq_rule_base_dataset=asset, + qualified_name=f"{asset.qualified_name}/rule/{cls._generate_uuid()}", + dq_rule_dimension=dimension, + dq_rule_template_name=template_rule_name, + dq_rule_template=DataQualityRuleTemplate.ref_by_qualified_name( + qualified_name=template_qualified_name, + ), + ) + + if column is not None: + rule.dq_rule_base_column_qualified_name = column.qualified_name + rule.dq_rule_base_column = column + + if custom_sql is not None: + rule.dq_rule_custom_sql = custom_sql + rule.display_name = rule_name + if custom_sql_return_type is not None: + rule.dq_rule_custom_sql_return_type = custom_sql_return_type + if description is not None: + rule.user_description = description + + return rule + + @staticmethod + def _generate_uuid() -> str: + d = int(time.time() * 1000) + random_bytes = uuid.uuid4().bytes + rand_index = 0 + + def replace_char(c: str) -> str: + nonlocal d, rand_index + r = (d + random_bytes[rand_index % 16]) % 16 + rand_index += 1 + d = d // 16 + if c == "x": + return hex(r)[2:] + elif c == "y": + return hex((r & 0x3) | 0x8)[2:] + else: + return c + + template = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx" + return "".join(replace_char(c) if c in "xy" else c for c in template) + + @staticmethod + def _get_template_config_value( + config_value: str, + property_name: Optional[str] = None, + value_key: str = "default", + ): + if not config_value: + return None + try: + config_json = json.loads(config_value) + if property_name: + properties = config_json.get("properties", {}) + field = properties.get(property_name, {}) + return field.get(value_key) + else: + return config_json.get(value_key) + except (json.JSONDecodeError, KeyError): + return None + + @staticmethod + def _validate_template_features( + rule_type: DataQualityRuleTemplateType, + rule_conditions: Optional[str], + row_scope_filtering_enabled: Optional[bool], + template_config: Optional[dict], + threshold_compare_operator: Optional[ + DataQualityRuleThresholdCompareOperator + ] = None, + asset: Optional[Asset] = None, + target_table_asset: Optional[Asset] = None, + ) -> Optional[DataQualityRuleThresholdCompareOperator]: + if not template_config or not template_config.get("config"): + return None + + config = template_config["config"] + + if rule_conditions and config.dq_rule_template_config_rule_conditions is None: + raise ErrorCode.DQ_RULE_TYPE_NOT_SUPPORTED.exception_with_parameters( + rule_type.value, "rule conditions" + ) + + if row_scope_filtering_enabled: + advanced_settings = config.dq_rule_template_config_advanced_settings or "" + if "dqRuleRowScopeFilteringEnabled" not in str(advanced_settings): + raise ErrorCode.DQ_RULE_TYPE_NOT_SUPPORTED.exception_with_parameters( + rule_type.value, "row scope filtering" + ) + if asset and not getattr( + asset, + "asset_dq_row_scope_filter_column_qualified_name", + None, + ): + raise ErrorCode.DQ_ROW_SCOPE_FILTER_COLUMN_MISSING.exception_with_parameters( + getattr(asset, "qualified_name", "unknown") + ) + if target_table_asset: + if not getattr( + target_table_asset, + "asset_dq_row_scope_filter_column_qualified_name", + None, + ): + raise ErrorCode.DQ_ROW_SCOPE_FILTER_COLUMN_MISSING.exception_with_parameters( + getattr(target_table_asset, "qualified_name", "unknown") + ) + + if rule_conditions: + allowed_rule_conditions = DataQualityRule._get_template_config_value( + config.dq_rule_template_config_rule_conditions or "", + None, + "enum", + ) + if allowed_rule_conditions: + try: + rule_conditions_json = json.loads(rule_conditions) + conditions = rule_conditions_json.get("conditions", []) + if len(conditions) != 1: + raise ErrorCode.DQ_RULE_CONDITIONS_INVALID.exception_with_parameters( + f"exactly one condition required, found {len(conditions)}" + ) + condition_type = conditions[0].get("type") + except json.JSONDecodeError: + condition_type = rule_conditions + + if condition_type not in allowed_rule_conditions: + raise ErrorCode.DQ_RULE_CONDITIONS_INVALID.exception_with_parameters( + f"condition type '{condition_type}' not supported, allowed: {allowed_rule_conditions}" + ) + + if threshold_compare_operator is None: + return DataQualityRuleThresholdCompareOperator.EQUAL + elif ( + threshold_compare_operator + != DataQualityRuleThresholdCompareOperator.EQUAL + ): + raise ErrorCode.INVALID_PARAMETER_VALUE.exception_with_parameters( + f"threshold_compare_operator={threshold_compare_operator.value}", + "threshold_compare_operator", + "EQUAL when rule_conditions are provided", + ) + + if threshold_compare_operator is not None: + allowed_operators = DataQualityRule._get_template_config_value( + config.dq_rule_template_config_threshold_object, + "dqRuleTemplateConfigThresholdCompareOperator", + "enum", + ) + if ( + allowed_operators + and threshold_compare_operator.value not in allowed_operators + ): + raise ErrorCode.INVALID_PARAMETER_VALUE.exception_with_parameters( + f"threshold_compare_operator={threshold_compare_operator.value}", + "threshold_compare_operator", + f"must be one of {allowed_operators}", + ) + elif threshold_compare_operator is None: + default_value = DataQualityRule._get_template_config_value( + config.dq_rule_template_config_threshold_object, + "dqRuleTemplateConfigThresholdCompareOperator", + "default", + ) + if default_value: + threshold_compare_operator = DataQualityRuleThresholdCompareOperator( + default_value + ) + + return ( + threshold_compare_operator + or DataQualityRuleThresholdCompareOperator.LESS_THAN_EQUAL + ) + + @staticmethod + def _fetch_assets_for_row_scope_validation( + client: "AtlanClient", + base_asset: Asset, + rule_conditions: Optional[str], + row_scope_filtering_enabled: bool, + ) -> tuple[Asset, Optional[Asset]]: + asset_for_validation = base_asset + target_table_asset = None + + if not row_scope_filtering_enabled: + return asset_for_validation, target_table_asset + + # Extract target_table from rule_conditions + target_table_qualified_name = None + if rule_conditions: + try: + rule_conditions_json = json.loads(rule_conditions) + conditions = rule_conditions_json.get("conditions", []) + if conditions: + condition_value = conditions[0].get("value", {}) + target_table_qualified_name = condition_value.get("target_table") + except (json.JSONDecodeError, KeyError, TypeError, AttributeError): + pass + + qualified_names_to_search = [] + if base_asset.qualified_name: + qualified_names_to_search.append(base_asset.qualified_name) + if target_table_qualified_name: + qualified_names_to_search.append(target_table_qualified_name) + + if qualified_names_to_search: + from pyatlan_v9.model.fluent_search import FluentSearch + + search_request = ( + FluentSearch() + .where(Asset.QUALIFIED_NAME.within(qualified_names_to_search)) + .include_on_results( + Asset.ASSET_DQ_ROW_SCOPE_FILTER_COLUMN_QUALIFIED_NAME + ) + ).to_request() + results = client.asset.search(search_request) + + for result in results.current_page(): + if result.qualified_name == base_asset.qualified_name: + asset_for_validation = result + elif ( + target_table_qualified_name + and result.qualified_name == target_table_qualified_name + ): + target_table_asset = result + + return asset_for_validation, target_table_asset + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return _data_quality_rule_to_nested_bytes(self, serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + @staticmethod + def from_json( + json_data: Union[str, bytes], serde: Serde | None = None + ) -> "DataQualityRule": + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + DataQualityRule instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _data_quality_rule_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DataQualityRuleAttributes(AssetAttributes): + """DataQualityRule-specific attributes for nested API format.""" + + dq_rule_base_dataset_qualified_name: Union[str, None, UnsetType] = UNSET + """Base dataset qualified name that attached to this rule.""" + + dq_rule_base_column_qualified_name: Union[str, None, UnsetType] = UNSET + """Base column qualified name that attached to this rule.""" + + dq_rule_reference_dataset_qualified_names: Union[list[str], None, UnsetType] = UNSET + """List of unique reference dataset's qualified names related to this rule.""" + + dq_rule_reference_column_qualified_names: Union[list[str], None, UnsetType] = UNSET + """List of unique reference column's qualified names related to this rule.""" + + dq_rule_source_sync_status: Union[str, None, UnsetType] = UNSET + """Latest sync status of the rule to the source.""" + + dq_rule_source_sync_error_code: Union[str, None, UnsetType] = UNSET + """Error code in the case of state being "failure".""" + + dq_rule_source_sync_error_message: Union[str, None, UnsetType] = UNSET + """Error message in the case of state being "error".""" + + dq_rule_source_sync_raw_error: Union[str, None, UnsetType] = UNSET + """Raw error message from the source.""" + + dq_rule_source_synced_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the rule synced to the source.""" + + dq_rule_latest_result: Union[str, None, UnsetType] = UNSET + """Latest result of the rule.""" + + dq_rule_latest_result_computed_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the latest rule result was evaluated.""" + + dq_rule_latest_result_fetched_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the latest rule result was fetched.""" + + dq_rule_latest_metric_value: Union[str, None, UnsetType] = UNSET + """Last result metrics value of the rule.""" + + dq_rule_latest_metric_value_computed_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the latest metric value was evaluated in the source.""" + + dq_rule_dimension: Union[str, None, UnsetType] = UNSET + """Dimension of the data quality rule.""" + + dq_rule_template_name: Union[str, None, UnsetType] = UNSET + """Name of the rule template corresponding to the rule.""" + + dq_rule_status: Union[str, None, UnsetType] = UNSET + """Status of the rule.""" + + dq_rule_alert_priority: Union[str, None, UnsetType] = UNSET + """Default priority level for alerts involving this rule.""" + + dq_rule_config_arguments: Union[DataQualityRuleConfigArguments, None, UnsetType] = ( + UNSET + ) + """Rule config that contains the rule definitions.""" + + dq_rule_custom_sql: Union[str, None, UnsetType] = UNSET + """SQL code for custom SQL rules.""" + + dq_rule_custom_sql_return_type: Union[str, None, UnsetType] = UNSET + """Type of result returned by the custom SQL (number of rows or numeric value).""" + + dq_rule_failed_rows_sql: Union[str, None, UnsetType] = UNSET + """SQL query used to retrieve failed rows.""" + + dq_rule_row_scope_filtering_enabled: Union[bool, None, UnsetType] = UNSET + """Whether row scope filtering is enabled for this data quality rule (true) or not (false).""" + + dq_is_part_of_contract: Union[bool, None, UnsetType] = UNSET + """Whether this data quality is part of contract (true) or not (false).""" + + +class DataQualityRuleRelationshipAttributes(AssetRelationshipAttributes): + """DataQualityRule-specific relationship attributes for nested API format.""" + + dq_rule_template: Union[RelatedDataQualityRuleTemplate, None, UnsetType] = UNSET + """Template used to create this rule.""" + + dq_rule_base_dataset: Union[RelatedAsset, None, UnsetType] = UNSET + """Base dataset attached to this rule.""" + + dq_rule_base_column: Union[RelatedColumn, None, UnsetType] = UNSET + """Base column attached to this rule.""" + + dq_rule_reference_datasets: Union[list[RelatedAsset], None, UnsetType] = UNSET + """Datasets referenced in this rule.""" + + dq_rule_reference_columns: Union[list[RelatedColumn], None, UnsetType] = UNSET + """Columns referenced in this rule.""" + + +class DataQualityRuleNested(AssetNested): + """DataQualityRule in nested API format for high-performance serialization.""" + + attributes: Union[DataQualityRuleAttributes, UnsetType] = UNSET + relationship_attributes: Union[DataQualityRuleRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + DataQualityRuleRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + DataQualityRuleRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _data_quality_rule_to_nested( + data_quality_rule: DataQualityRule, +) -> DataQualityRuleNested: + """Convert flat DataQualityRule to nested format.""" + attrs = DataQualityRuleAttributes( + dq_rule_base_dataset_qualified_name=data_quality_rule.dq_rule_base_dataset_qualified_name, + dq_rule_base_column_qualified_name=data_quality_rule.dq_rule_base_column_qualified_name, + dq_rule_reference_dataset_qualified_names=data_quality_rule.dq_rule_reference_dataset_qualified_names, + dq_rule_reference_column_qualified_names=data_quality_rule.dq_rule_reference_column_qualified_names, + dq_rule_source_sync_status=data_quality_rule.dq_rule_source_sync_status, + dq_rule_source_sync_error_code=data_quality_rule.dq_rule_source_sync_error_code, + dq_rule_source_sync_error_message=data_quality_rule.dq_rule_source_sync_error_message, + dq_rule_source_sync_raw_error=data_quality_rule.dq_rule_source_sync_raw_error, + dq_rule_source_synced_at=data_quality_rule.dq_rule_source_synced_at, + dq_rule_latest_result=data_quality_rule.dq_rule_latest_result, + dq_rule_latest_result_computed_at=data_quality_rule.dq_rule_latest_result_computed_at, + dq_rule_latest_result_fetched_at=data_quality_rule.dq_rule_latest_result_fetched_at, + dq_rule_latest_metric_value=data_quality_rule.dq_rule_latest_metric_value, + dq_rule_latest_metric_value_computed_at=data_quality_rule.dq_rule_latest_metric_value_computed_at, + dq_rule_dimension=data_quality_rule.dq_rule_dimension, + dq_rule_template_name=data_quality_rule.dq_rule_template_name, + dq_rule_status=data_quality_rule.dq_rule_status, + dq_rule_alert_priority=data_quality_rule.dq_rule_alert_priority, + dq_rule_config_arguments=data_quality_rule.dq_rule_config_arguments, + dq_rule_custom_sql=data_quality_rule.dq_rule_custom_sql, + dq_rule_custom_sql_return_type=data_quality_rule.dq_rule_custom_sql_return_type, + dq_rule_failed_rows_sql=data_quality_rule.dq_rule_failed_rows_sql, + dq_rule_row_scope_filtering_enabled=data_quality_rule.dq_rule_row_scope_filtering_enabled, + dq_is_part_of_contract=data_quality_rule.dq_is_part_of_contract, + ) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + rel_fields: list[str] = [ + "dq_rule_template", + "dq_rule_base_dataset", + "dq_rule_base_column", + "dq_rule_reference_datasets", + "dq_rule_reference_columns", + ] + replace_rels, append_rels, remove_rels = categorize_relationships( + data_quality_rule, rel_fields, DataQualityRuleRelationshipAttributes + ) + return DataQualityRuleNested( + guid=data_quality_rule.guid, + type_name=data_quality_rule.type_name, + status=data_quality_rule.status, + version=data_quality_rule.version, + create_time=data_quality_rule.create_time, + update_time=data_quality_rule.update_time, + created_by=data_quality_rule.created_by, + updated_by=data_quality_rule.updated_by, + classifications=data_quality_rule.classifications, + classification_names=data_quality_rule.classification_names, + meanings=data_quality_rule.meanings, + labels=data_quality_rule.labels, + business_attributes=data_quality_rule.business_attributes, + custom_attributes=data_quality_rule.custom_attributes, + pending_tasks=data_quality_rule.pending_tasks, + proxy=data_quality_rule.proxy, + is_incomplete=data_quality_rule.is_incomplete, + provenance_type=data_quality_rule.provenance_type, + home_id=data_quality_rule.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _data_quality_rule_from_nested(nested: DataQualityRuleNested) -> DataQualityRule: + """Convert nested format to flat DataQualityRule.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else DataQualityRuleAttributes() + ) + # Merge relationships from all three buckets + rel_fields: list[str] = [ + "dq_rule_template", + "dq_rule_base_dataset", + "dq_rule_base_column", + "dq_rule_reference_datasets", + "dq_rule_reference_columns", + ] + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + rel_fields, + DataQualityRuleRelationshipAttributes, + ) + return DataQualityRule( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + dq_rule_base_dataset_qualified_name=attrs.dq_rule_base_dataset_qualified_name, + dq_rule_base_column_qualified_name=attrs.dq_rule_base_column_qualified_name, + dq_rule_reference_dataset_qualified_names=attrs.dq_rule_reference_dataset_qualified_names, + dq_rule_reference_column_qualified_names=attrs.dq_rule_reference_column_qualified_names, + dq_rule_source_sync_status=attrs.dq_rule_source_sync_status, + dq_rule_source_sync_error_code=attrs.dq_rule_source_sync_error_code, + dq_rule_source_sync_error_message=attrs.dq_rule_source_sync_error_message, + dq_rule_source_sync_raw_error=attrs.dq_rule_source_sync_raw_error, + dq_rule_source_synced_at=attrs.dq_rule_source_synced_at, + dq_rule_latest_result=attrs.dq_rule_latest_result, + dq_rule_latest_result_computed_at=attrs.dq_rule_latest_result_computed_at, + dq_rule_latest_result_fetched_at=attrs.dq_rule_latest_result_fetched_at, + dq_rule_latest_metric_value=attrs.dq_rule_latest_metric_value, + dq_rule_latest_metric_value_computed_at=attrs.dq_rule_latest_metric_value_computed_at, + dq_rule_dimension=attrs.dq_rule_dimension, + dq_rule_template_name=attrs.dq_rule_template_name, + dq_rule_status=attrs.dq_rule_status, + dq_rule_alert_priority=attrs.dq_rule_alert_priority, + dq_rule_config_arguments=attrs.dq_rule_config_arguments, + dq_rule_custom_sql=attrs.dq_rule_custom_sql, + dq_rule_custom_sql_return_type=attrs.dq_rule_custom_sql_return_type, + dq_rule_failed_rows_sql=attrs.dq_rule_failed_rows_sql, + dq_rule_row_scope_filtering_enabled=attrs.dq_rule_row_scope_filtering_enabled, + dq_is_part_of_contract=attrs.dq_is_part_of_contract, + # Merged relationship attributes + **merged_rels, + ) + + +def _data_quality_rule_to_nested_bytes( + data_quality_rule: DataQualityRule, serde: Serde +) -> bytes: + """Convert flat DataQualityRule to nested JSON bytes.""" + return serde.encode(_data_quality_rule_to_nested(data_quality_rule)) + + +def _data_quality_rule_from_nested_bytes(data: bytes, serde: Serde) -> DataQualityRule: + """Convert nested JSON bytes to flat DataQualityRule.""" + nested = serde.decode(data, DataQualityRuleNested) + return _data_quality_rule_from_nested(nested) + + +# Deferred import to avoid circular dependency +from .data_quality_rule_template import DataQualityRuleTemplate # noqa: E402, F401 diff --git a/pyatlan_v9/model/assets/data_quality_rule_template.py b/pyatlan_v9/model/assets/data_quality_rule_template.py new file mode 100644 index 000000000..3ff60196b --- /dev/null +++ b/pyatlan_v9/model/assets/data_quality_rule_template.py @@ -0,0 +1,628 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DataQualityRuleTemplate asset model with flattened inheritance. + +This module provides: +- DataQualityRuleTemplate: Flat asset class (easy to use) +- DataQualityRuleTemplateAttributes: Nested attributes struct (extends AssetAttributes) +- DataQualityRuleTemplateNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .data_quality_related import RelatedDataQualityRule, RelatedMetric + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class DataQualityRuleTemplate(Asset): + """ + Class for defining a rule template that are available to set against any asset. + """ + + DQ_RULE_TEMPLATE_DIMENSION: ClassVar[Any] = None + DQ_RULE_TEMPLATE_CONFIG: ClassVar[Any] = None + DQ_RULE_TEMPLATE_METRIC_VALUE_TYPE: ClassVar[Any] = None + DQ_IS_PART_OF_CONTRACT: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_RULES: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "DataQualityRuleTemplate" + + dq_rule_template_dimension: Union[str, None, UnsetType] = UNSET + """Name of the dimension the rule belongs to.""" + + dq_rule_template_config: Union[Dict[str, Any], None, UnsetType] = UNSET + """Rule config that will help render the form and define the rule.""" + + dq_rule_template_metric_value_type: Union[str, None, UnsetType] = UNSET + """Type of the metric value returned by the rule(absolute, percentage, time etc.).""" + + dq_is_part_of_contract: Union[bool, None, UnsetType] = UNSET + """Whether this data quality is part of contract (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are created from the template.""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "DataQualityRuleTemplate" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _data_quality_rule_template_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> DataQualityRuleTemplate: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + DataQualityRuleTemplate instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _data_quality_rule_template_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DataQualityRuleTemplateAttributes(AssetAttributes): + """DataQualityRuleTemplate-specific attributes for nested API format.""" + + dq_rule_template_dimension: Union[str, None, UnsetType] = UNSET + """Name of the dimension the rule belongs to.""" + + dq_rule_template_config: Union[Dict[str, Any], None, UnsetType] = UNSET + """Rule config that will help render the form and define the rule.""" + + dq_rule_template_metric_value_type: Union[str, None, UnsetType] = UNSET + """Type of the metric value returned by the rule(absolute, percentage, time etc.).""" + + dq_is_part_of_contract: Union[bool, None, UnsetType] = UNSET + """Whether this data quality is part of contract (true) or not (false).""" + + +class DataQualityRuleTemplateRelationshipAttributes(AssetRelationshipAttributes): + """DataQualityRuleTemplate-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are created from the template.""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DataQualityRuleTemplateNested(AssetNested): + """DataQualityRuleTemplate in nested API format for high-performance serialization.""" + + attributes: Union[DataQualityRuleTemplateAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + DataQualityRuleTemplateRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + DataQualityRuleTemplateRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + DataQualityRuleTemplateRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DATA_QUALITY_RULE_TEMPLATE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_rules", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_data_quality_rule_template_attrs( + attrs: DataQualityRuleTemplateAttributes, obj: DataQualityRuleTemplate +) -> None: + """Populate DataQualityRuleTemplate-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.dq_rule_template_dimension = obj.dq_rule_template_dimension + attrs.dq_rule_template_config = obj.dq_rule_template_config + attrs.dq_rule_template_metric_value_type = obj.dq_rule_template_metric_value_type + attrs.dq_is_part_of_contract = obj.dq_is_part_of_contract + + +def _extract_data_quality_rule_template_attrs( + attrs: DataQualityRuleTemplateAttributes, +) -> dict: + """Extract all DataQualityRuleTemplate attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["dq_rule_template_dimension"] = attrs.dq_rule_template_dimension + result["dq_rule_template_config"] = attrs.dq_rule_template_config + result["dq_rule_template_metric_value_type"] = ( + attrs.dq_rule_template_metric_value_type + ) + result["dq_is_part_of_contract"] = attrs.dq_is_part_of_contract + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _data_quality_rule_template_to_nested( + data_quality_rule_template: DataQualityRuleTemplate, +) -> DataQualityRuleTemplateNested: + """Convert flat DataQualityRuleTemplate to nested format.""" + attrs = DataQualityRuleTemplateAttributes() + _populate_data_quality_rule_template_attrs(attrs, data_quality_rule_template) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + data_quality_rule_template, + _DATA_QUALITY_RULE_TEMPLATE_REL_FIELDS, + DataQualityRuleTemplateRelationshipAttributes, + ) + return DataQualityRuleTemplateNested( + guid=data_quality_rule_template.guid, + type_name=data_quality_rule_template.type_name, + status=data_quality_rule_template.status, + version=data_quality_rule_template.version, + create_time=data_quality_rule_template.create_time, + update_time=data_quality_rule_template.update_time, + created_by=data_quality_rule_template.created_by, + updated_by=data_quality_rule_template.updated_by, + classifications=data_quality_rule_template.classifications, + classification_names=data_quality_rule_template.classification_names, + meanings=data_quality_rule_template.meanings, + labels=data_quality_rule_template.labels, + business_attributes=data_quality_rule_template.business_attributes, + custom_attributes=data_quality_rule_template.custom_attributes, + pending_tasks=data_quality_rule_template.pending_tasks, + proxy=data_quality_rule_template.proxy, + is_incomplete=data_quality_rule_template.is_incomplete, + provenance_type=data_quality_rule_template.provenance_type, + home_id=data_quality_rule_template.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _data_quality_rule_template_from_nested( + nested: DataQualityRuleTemplateNested, +) -> DataQualityRuleTemplate: + """Convert nested format to flat DataQualityRuleTemplate.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else DataQualityRuleTemplateAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DATA_QUALITY_RULE_TEMPLATE_REL_FIELDS, + DataQualityRuleTemplateRelationshipAttributes, + ) + return DataQualityRuleTemplate( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_data_quality_rule_template_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _data_quality_rule_template_to_nested_bytes( + data_quality_rule_template: DataQualityRuleTemplate, serde: Serde +) -> bytes: + """Convert flat DataQualityRuleTemplate to nested JSON bytes.""" + return serde.encode( + _data_quality_rule_template_to_nested(data_quality_rule_template) + ) + + +def _data_quality_rule_template_from_nested_bytes( + data: bytes, serde: Serde +) -> DataQualityRuleTemplate: + """Convert nested JSON bytes to flat DataQualityRuleTemplate.""" + nested = serde.decode(data, DataQualityRuleTemplateNested) + return _data_quality_rule_template_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + RelationField, +) + +DataQualityRuleTemplate.DQ_RULE_TEMPLATE_DIMENSION = KeywordField( + "dqRuleTemplateDimension", "dqRuleTemplateDimension" +) +DataQualityRuleTemplate.DQ_RULE_TEMPLATE_CONFIG = KeywordField( + "dqRuleTemplateConfig", "dqRuleTemplateConfig" +) +DataQualityRuleTemplate.DQ_RULE_TEMPLATE_METRIC_VALUE_TYPE = KeywordField( + "dqRuleTemplateMetricValueType", "dqRuleTemplateMetricValueType" +) +DataQualityRuleTemplate.DQ_IS_PART_OF_CONTRACT = BooleanField( + "dqIsPartOfContract", "dqIsPartOfContract" +) +DataQualityRuleTemplate.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +DataQualityRuleTemplate.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +DataQualityRuleTemplate.ANOMALO_CHECKS = RelationField("anomaloChecks") +DataQualityRuleTemplate.APPLICATION = RelationField("application") +DataQualityRuleTemplate.APPLICATION_FIELD = RelationField("applicationField") +DataQualityRuleTemplate.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +DataQualityRuleTemplate.INPUT_PORT_DATA_PRODUCTS = RelationField( + "inputPortDataProducts" +) +DataQualityRuleTemplate.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +DataQualityRuleTemplate.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +DataQualityRuleTemplate.METRICS = RelationField("metrics") +DataQualityRuleTemplate.DQ_RULES = RelationField("dqRules") +DataQualityRuleTemplate.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +DataQualityRuleTemplate.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +DataQualityRuleTemplate.MEANINGS = RelationField("meanings") +DataQualityRuleTemplate.MC_MONITORS = RelationField("mcMonitors") +DataQualityRuleTemplate.MC_INCIDENTS = RelationField("mcIncidents") +DataQualityRuleTemplate.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +DataQualityRuleTemplate.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +DataQualityRuleTemplate.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +DataQualityRuleTemplate.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +DataQualityRuleTemplate.USER_DEF_RELATIONSHIP_TO = RelationField( + "userDefRelationshipTo" +) +DataQualityRuleTemplate.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +DataQualityRuleTemplate.FILES = RelationField("files") +DataQualityRuleTemplate.LINKS = RelationField("links") +DataQualityRuleTemplate.README = RelationField("readme") +DataQualityRuleTemplate.SCHEMA_REGISTRY_SUBJECTS = RelationField( + "schemaRegistrySubjects" +) +DataQualityRuleTemplate.SODA_CHECKS = RelationField("sodaChecks") +DataQualityRuleTemplate.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +DataQualityRuleTemplate.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/data_set.py b/pyatlan_v9/model/assets/data_set.py new file mode 100644 index 000000000..00b00a0fe --- /dev/null +++ b/pyatlan_v9/model/assets/data_set.py @@ -0,0 +1,2915 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DataSet asset model with flattened inheritance. + +This module provides: +- DataSet: Flat asset class (easy to use) +- DataSetAttributes: Nested attributes struct (extends AssetAttributes) +- DataSetNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Set, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .referenceable import ( + _REFERENCEABLE_REL_FIELDS, + Referenceable, + ReferenceableAttributes, + ReferenceableNested, + ReferenceableRelationshipAttributes, + _extract_referenceable_attrs, + _populate_referenceable_attrs, +) +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +class DataSet(Referenceable): + """ + deprecated + """ + + NAME: ClassVar[Any] = None + DISPLAY_NAME: ClassVar[Any] = None + DESCRIPTION: ClassVar[Any] = None + ASSET_SOURCE_README: ClassVar[Any] = None + USER_DESCRIPTION: ClassVar[Any] = None + ASSET_AI_GENERATED_DESCRIPTION: ClassVar[Any] = None + ASSET_AI_GENERATED_DESCRIPTION_CONFIDENCE: ClassVar[Any] = None + ASSET_AI_GENERATED_DESCRIPTION_REASONING: ClassVar[Any] = None + TENANT_ID: ClassVar[Any] = None + CERTIFICATE_STATUS: ClassVar[Any] = None + CERTIFICATE_STATUS_MESSAGE: ClassVar[Any] = None + CERTIFICATE_UPDATED_BY: ClassVar[Any] = None + CERTIFICATE_UPDATED_AT: ClassVar[Any] = None + ANNOUNCEMENT_TITLE: ClassVar[Any] = None + ANNOUNCEMENT_MESSAGE: ClassVar[Any] = None + ANNOUNCEMENT_TYPE: ClassVar[Any] = None + ANNOUNCEMENT_UPDATED_AT: ClassVar[Any] = None + ANNOUNCEMENT_UPDATED_BY: ClassVar[Any] = None + OWNER_USERS: ClassVar[Any] = None + OWNER_GROUPS: ClassVar[Any] = None + ADMIN_USERS: ClassVar[Any] = None + ADMIN_GROUPS: ClassVar[Any] = None + VIEWER_USERS: ClassVar[Any] = None + VIEWER_GROUPS: ClassVar[Any] = None + CONNECTOR_NAME: ClassVar[Any] = None + CONNECTION_NAME: ClassVar[Any] = None + CONNECTION_QUALIFIED_NAME: ClassVar[Any] = None + HAS_LINEAGE: ClassVar[Any] = None + IS_DISCOVERABLE: ClassVar[Any] = None + IS_EDITABLE: ClassVar[Any] = None + SUB_TYPE: ClassVar[Any] = None + VIEW_SCORE: ClassVar[Any] = None + POPULARITY_SCORE: ClassVar[Any] = None + SOURCE_OWNERS: ClassVar[Any] = None + ASSET_SOURCE_ID: ClassVar[Any] = None + SOURCE_CREATED_BY: ClassVar[Any] = None + SOURCE_CREATED_AT: ClassVar[Any] = None + SOURCE_UPDATED_AT: ClassVar[Any] = None + SOURCE_UPDATED_BY: ClassVar[Any] = None + SOURCE_URL: ClassVar[Any] = None + SOURCE_EMBED_URL: ClassVar[Any] = None + LAST_SYNC_WORKFLOW_NAME: ClassVar[Any] = None + LAST_SYNC_RUN_AT: ClassVar[Any] = None + LAST_SYNC_RUN: ClassVar[Any] = None + ADMIN_ROLES: ClassVar[Any] = None + SOURCE_READ_COUNT: ClassVar[Any] = None + SOURCE_READ_USER_COUNT: ClassVar[Any] = None + SOURCE_LAST_READ_AT: ClassVar[Any] = None + LAST_ROW_CHANGED_AT: ClassVar[Any] = None + SOURCE_TOTAL_COST: ClassVar[Any] = None + SOURCE_COST_UNIT: ClassVar[Any] = None + SOURCE_READ_QUERY_COST: ClassVar[Any] = None + SOURCE_READ_RECENT_USER_LIST: ClassVar[Any] = None + SOURCE_READ_RECENT_USER_RECORD_LIST: ClassVar[Any] = None + SOURCE_READ_TOP_USER_LIST: ClassVar[Any] = None + SOURCE_READ_TOP_USER_RECORD_LIST: ClassVar[Any] = None + SOURCE_READ_POPULAR_QUERY_RECORD_LIST: ClassVar[Any] = None + SOURCE_READ_EXPENSIVE_QUERY_RECORD_LIST: ClassVar[Any] = None + SOURCE_READ_SLOW_QUERY_RECORD_LIST: ClassVar[Any] = None + SOURCE_QUERY_COMPUTE_COST_LIST: ClassVar[Any] = None + SOURCE_QUERY_COMPUTE_COST_RECORD_LIST: ClassVar[Any] = None + DBT_QUALIFIED_NAME: ClassVar[Any] = None + ASSET_DBT_WORKFLOW_LAST_UPDATED: ClassVar[Any] = None + ASSET_DBT_ALIAS: ClassVar[Any] = None + ASSET_DBT_META: ClassVar[Any] = None + ASSET_DBT_UNIQUE_ID: ClassVar[Any] = None + ASSET_DBT_ACCOUNT_NAME: ClassVar[Any] = None + ASSET_DBT_PROJECT_NAME: ClassVar[Any] = None + ASSET_DBT_PACKAGE_NAME: ClassVar[Any] = None + ASSET_DBT_JOB_NAME: ClassVar[Any] = None + ASSET_DBT_JOB_SCHEDULE: ClassVar[Any] = None + ASSET_DBT_JOB_STATUS: ClassVar[Any] = None + ASSET_DBT_TEST_STATUS: ClassVar[Any] = None + ASSET_DBT_JOB_SCHEDULE_CRON_HUMANIZED: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_URL: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_CREATED_AT: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_UPDATED_AT: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_DEQUED_AT: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_STARTED_AT: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_TOTAL_DURATION: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_TOTAL_DURATION_HUMANIZED: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_QUEUED_DURATION: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_QUEUED_DURATION_HUMANIZED: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_RUN_DURATION: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_RUN_DURATION_HUMANIZED: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_GIT_BRANCH: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_GIT_SHA: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_STATUS_MESSAGE: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_OWNER_THREAD_ID: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_EXECUTED_BY_THREAD_ID: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_ARTIFACTS_SAVED: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_ARTIFACT_S3_PATH: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_HAS_DOCS_GENERATED: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_HAS_SOURCES_GENERATED: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_NOTIFICATIONS_SENT: ClassVar[Any] = None + ASSET_DBT_JOB_NEXT_RUN: ClassVar[Any] = None + ASSET_DBT_JOB_NEXT_RUN_HUMANIZED: ClassVar[Any] = None + ASSET_DBT_ENVIRONMENT_NAME: ClassVar[Any] = None + ASSET_DBT_ENVIRONMENT_DBT_VERSION: ClassVar[Any] = None + ASSET_DBT_TAGS: ClassVar[Any] = None + ASSET_DBT_SEMANTIC_LAYER_PROXY_URL: ClassVar[Any] = None + ASSET_DBT_SOURCE_FRESHNESS_CRITERIA: ClassVar[Any] = None + SAMPLE_DATA_URL: ClassVar[Any] = None + ASSET_TAGS: ClassVar[Any] = None + ASSET_MC_INCIDENT_NAMES: ClassVar[Any] = None + ASSET_MC_INCIDENT_QUALIFIED_NAMES: ClassVar[Any] = None + ASSET_MC_ALERT_QUALIFIED_NAMES: ClassVar[Any] = None + ASSET_MC_MONITOR_NAMES: ClassVar[Any] = None + ASSET_MC_MONITOR_QUALIFIED_NAMES: ClassVar[Any] = None + ASSET_MC_MONITOR_STATUSES: ClassVar[Any] = None + ASSET_MC_MONITOR_TYPES: ClassVar[Any] = None + ASSET_MC_MONITOR_SCHEDULE_TYPES: ClassVar[Any] = None + ASSET_MC_INCIDENT_TYPES: ClassVar[Any] = None + ASSET_MC_INCIDENT_SUB_TYPES: ClassVar[Any] = None + ASSET_MC_INCIDENT_SEVERITIES: ClassVar[Any] = None + ASSET_MC_INCIDENT_PRIORITIES: ClassVar[Any] = None + ASSET_MC_INCIDENT_STATES: ClassVar[Any] = None + ASSET_MC_IS_MONITORED: ClassVar[Any] = None + ASSET_MC_LAST_SYNC_RUN_AT: ClassVar[Any] = None + STARRED_BY: ClassVar[Any] = None + STARRED_DETAILS_LIST: ClassVar[Any] = None + STARRED_COUNT: ClassVar[Any] = None + ASSET_ANOMALO_DQ_STATUS: ClassVar[Any] = None + ASSET_ANOMALO_CHECK_COUNT: ClassVar[Any] = None + ASSET_ANOMALO_FAILED_CHECK_COUNT: ClassVar[Any] = None + ASSET_ANOMALO_CHECK_STATUSES: ClassVar[Any] = None + ASSET_ANOMALO_LAST_CHECK_RUN_AT: ClassVar[Any] = None + ASSET_ANOMALO_APPLIED_CHECK_TYPES: ClassVar[Any] = None + ASSET_ANOMALO_FAILED_CHECK_TYPES: ClassVar[Any] = None + ASSET_ANOMALO_SOURCE_URL: ClassVar[Any] = None + ASSET_SODA_DQ_STATUS: ClassVar[Any] = None + ASSET_SODA_CHECK_COUNT: ClassVar[Any] = None + ASSET_SODA_LAST_SYNC_RUN_AT: ClassVar[Any] = None + ASSET_SODA_LAST_SCAN_AT: ClassVar[Any] = None + ASSET_SODA_CHECK_STATUSES: ClassVar[Any] = None + ASSET_SODA_SOURCE_URL: ClassVar[Any] = None + ASSET_ICON: ClassVar[Any] = None + ASSET_EXTERNAL_DQ_METADATA_DETAILS: ClassVar[Any] = None + IS_PARTIAL: ClassVar[Any] = None + IS_AI_GENERATED: ClassVar[Any] = None + ASSET_COVER_IMAGE: ClassVar[Any] = None + ASSET_THEME_HEX: ClassVar[Any] = None + LEXICOGRAPHICAL_SORT_ORDER: ClassVar[Any] = None + HAS_CONTRACT: ClassVar[Any] = None + ASSET_REDIRECT_GUIDS: ClassVar[Any] = None + ASSET_POLICY_GUIDS: ClassVar[Any] = None + ASSET_POLICIES_COUNT: ClassVar[Any] = None + DOMAIN_GUIDS: ClassVar[Any] = None + NON_COMPLIANT_ASSET_POLICY_GUIDS: ClassVar[Any] = None + PRODUCT_GUIDS: ClassVar[Any] = None + OUTPUT_PRODUCT_GUIDS: ClassVar[Any] = None + APPLICATION_QUALIFIED_NAME: ClassVar[Any] = None + APPLICATION_FIELD_QUALIFIED_NAME: ClassVar[Any] = None + ASSET_USER_DEFINED_TYPE: ClassVar[Any] = None + ASSET_INTERNAL_POPULARITY_SCORE: ClassVar[Any] = None + ASSET_DQ_SCHEDULE_TYPE: ClassVar[Any] = None + ASSET_DQ_SCHEDULE_CRONTAB: ClassVar[Any] = None + ASSET_DQ_SCHEDULE_TIME_ZONE: ClassVar[Any] = None + ASSET_DQ_SCHEDULE_SOURCE_SYNC_STATUS: ClassVar[Any] = None + ASSET_DQ_SCHEDULE_SOURCE_SYNCED_AT: ClassVar[Any] = None + ASSET_DQ_SCHEDULE_SOURCE_SYNC_ERROR_MESSAGE: ClassVar[Any] = None + ASSET_DQ_SCHEDULE_SOURCE_SYNC_ERROR_CODE: ClassVar[Any] = None + ASSET_DQ_SCHEDULE_SOURCE_SYNC_RAW_ERROR: ClassVar[Any] = None + ASSET_DQ_RULE_ATTACHED_DIMENSIONS: ClassVar[Any] = None + ASSET_DQ_RULE_FAILED_DIMENSIONS: ClassVar[Any] = None + ASSET_DQ_RULE_PASSED_DIMENSIONS: ClassVar[Any] = None + ASSET_DQ_RULE_ATTACHED_RULE_TYPES: ClassVar[Any] = None + ASSET_DQ_RULE_FAILED_RULE_TYPES: ClassVar[Any] = None + ASSET_DQ_RULE_PASSED_RULE_TYPES: ClassVar[Any] = None + ASSET_DQ_RULE_RESULT_TAGS: ClassVar[Any] = None + ASSET_DQ_RULE_LAST_RUN_AT: ClassVar[Any] = None + ASSET_DQ_MANUAL_RUN_STATUS: ClassVar[Any] = None + ASSET_DQ_RULE_TOTAL_COUNT: ClassVar[Any] = None + ASSET_DQ_RULE_FAILED_COUNT: ClassVar[Any] = None + ASSET_DQ_RULE_PASSED_COUNT: ClassVar[Any] = None + ASSET_DQ_RESULT: ClassVar[Any] = None + ASSET_DQ_FRESHNESS_VALUE: ClassVar[Any] = None + ASSET_DQ_FRESHNESS_EXPECTATION: ClassVar[Any] = None + ASSET_DQ_ROW_SCOPE_FILTER_COLUMN_QUALIFIED_NAME: ClassVar[Any] = None + ASSET_SPACE_QUALIFIED_NAME: ClassVar[Any] = None + ASSET_SPACE_NAME: ClassVar[Any] = None + ASSET_GCP_DATAPLEX_METADATA_DETAILS: ClassVar[Any] = None + ASSET_GCP_DATAPLEX_ASPECT_LIST: ClassVar[Any] = None + ASSET_GCP_DATAPLEX_ASPECT_FIELD_LIST: ClassVar[Any] = None + ASSET_SMUS_METADATA_FORM_NAMES: ClassVar[Any] = None + ASSET_SMUS_METADATA_FORM_KEY_VALUE_DETAILS: ClassVar[Any] = None + ASSET_SMUS_METADATA_FORM_DETAILS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "DataSet" + + name: Union[str, None, UnsetType] = UNSET + """Name of this asset. Fallback for display purposes, if displayName is empty.""" + + display_name: Union[str, None, UnsetType] = UNSET + """Human-readable name of this asset used for display purposes (in user interface).""" + + description: Union[str, None, UnsetType] = UNSET + """Description of this asset, for example as crawled from a source. Fallback for display purposes, if userDescription is empty.""" + + asset_source_readme: Union[str, None, UnsetType] = UNSET + """Readme of this asset, as extracted from source. If present, this will be used for the readme in user interface.""" + + user_description: Union[str, None, UnsetType] = UNSET + """Description of this asset, as provided by a user. If present, this will be used for the description in user interface.""" + + asset_ai_generated_description: Union[str, None, UnsetType] = UNSET + """Description of this asset, generated by AI based on the asset's context. Displayed separately in the UI and can be used to overwrite existing descriptions.""" + + asset_ai_generated_description_confidence: Union[float, None, UnsetType] = UNSET + """Confidence score of the AI-generated description, ranging from 0.0 to 1.0.""" + + asset_ai_generated_description_reasoning: Union[str, None, UnsetType] = UNSET + """Reasoning behind the AI-generated description, explaining how the description was derived from the asset's context.""" + + tenant_id: Union[str, None, UnsetType] = UNSET + """Name of the Atlan workspace in which this asset exists.""" + + certificate_status: Union[str, None, UnsetType] = UNSET + """Status of this asset's certification.""" + + certificate_status_message: Union[str, None, UnsetType] = UNSET + """Human-readable descriptive message used to provide further detail to certificateStatus.""" + + certificate_updated_by: Union[str, None, UnsetType] = UNSET + """Name of the user who last updated the certification of this asset.""" + + certificate_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the certification was last updated, in milliseconds.""" + + announcement_title: Union[str, None, UnsetType] = UNSET + """Brief title for the announcement on this asset. Required when announcementType is specified.""" + + announcement_message: Union[str, None, UnsetType] = UNSET + """Detailed message to include in the announcement on this asset.""" + + announcement_type: Union[str, None, UnsetType] = UNSET + """Type of announcement on this asset.""" + + announcement_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the announcement was last updated, in milliseconds.""" + + announcement_updated_by: Union[str, None, UnsetType] = UNSET + """Name of the user who last updated the announcement.""" + + owner_users: Union[Set[str], None, UnsetType] = UNSET + """List of users who own this asset.""" + + owner_groups: Union[Set[str], None, UnsetType] = UNSET + """List of groups who own this asset.""" + + admin_users: Union[Set[str], None, UnsetType] = UNSET + """List of users who administer this asset. (This is only used for certain asset types.)""" + + admin_groups: Union[Set[str], None, UnsetType] = UNSET + """List of groups who administer this asset. (This is only used for certain asset types.)""" + + viewer_users: Union[Set[str], None, UnsetType] = UNSET + """List of users who can view assets contained in a collection. (This is only used for certain asset types.)""" + + viewer_groups: Union[Set[str], None, UnsetType] = UNSET + """List of groups who can view assets contained in a collection. (This is only used for certain asset types.)""" + + connector_name: Union[str, None, UnsetType] = UNSET + """Type of the connector through which this asset is accessible.""" + + connection_name: Union[str, None, UnsetType] = UNSET + """Simple name of the connection through which this asset is accessible.""" + + connection_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the connection through which this asset is accessible.""" + + has_lineage: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="__hasLineage" + ) + """Whether this asset has lineage (true) or not (false).""" + + is_discoverable: Union[bool, None, UnsetType] = UNSET + """Whether this asset is discoverable through the UI (true) or not (false).""" + + is_editable: Union[bool, None, UnsetType] = UNSET + """Whether this asset can be edited in the UI (true) or not (false).""" + + sub_type: Union[str, None, UnsetType] = UNSET + """Subtype of this asset.""" + + view_score: Union[float, None, UnsetType] = UNSET + """View score for this asset.""" + + popularity_score: Union[float, None, UnsetType] = UNSET + """Popularity score for this asset.""" + + source_owners: Union[str, None, UnsetType] = UNSET + """List of owners of this asset, in the source system.""" + + asset_source_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for this asset in the system from which it was sourced.""" + + source_created_by: Union[str, None, UnsetType] = UNSET + """Name of the user who created this asset, in the source system.""" + + source_created_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was created in the source system, in milliseconds.""" + + source_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last updated in the source system, in milliseconds.""" + + source_updated_by: Union[str, None, UnsetType] = UNSET + """Name of the user who last updated this asset, in the source system.""" + + source_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sourceURL" + ) + """URL to the resource within the source application, used to create a button to view this asset in the source application.""" + + source_embed_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sourceEmbedURL" + ) + """URL to create an embed for a resource (for example, an image of a dashboard) within Atlan.""" + + last_sync_workflow_name: Union[str, None, UnsetType] = UNSET + """Name of the crawler that last synchronized this asset.""" + + last_sync_run_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last crawled, in milliseconds.""" + + last_sync_run: Union[str, None, UnsetType] = UNSET + """Name of the last run of the crawler that last synchronized this asset.""" + + admin_roles: Union[Set[str], None, UnsetType] = UNSET + """List of roles who administer this asset. (This is only used for Connection assets.)""" + + source_read_count: Union[int, None, UnsetType] = UNSET + """Total count of all read operations at source.""" + + source_read_user_count: Union[int, None, UnsetType] = UNSET + """Total number of unique users that read data from asset.""" + + source_last_read_at: Union[int, None, UnsetType] = UNSET + """Timestamp of most recent read operation.""" + + last_row_changed_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) of the last operation that inserted, updated, or deleted rows, in milliseconds.""" + + source_total_cost: Union[float, None, UnsetType] = UNSET + """Total cost of all operations at source.""" + + source_cost_unit: Union[str, None, UnsetType] = UNSET + """The unit of measure for sourceTotalCost.""" + + source_read_query_cost: Union[float, None, UnsetType] = UNSET + """Total cost of read queries at source.""" + + source_read_recent_user_list: Union[List[str], None, UnsetType] = UNSET + """List of usernames of the most recent users who read this asset.""" + + source_read_recent_user_record_list: Union[ + List[Dict[str, Any]], None, UnsetType + ] = UNSET + """List of usernames with extra insights for the most recent users who read this asset.""" + + source_read_top_user_list: Union[List[str], None, UnsetType] = UNSET + """List of usernames of the users who read this asset the most.""" + + source_read_top_user_record_list: Union[List[Dict[str, Any]], None, UnsetType] = ( + UNSET + ) + """List of usernames with extra insights for the users who read this asset the most.""" + + source_read_popular_query_record_list: Union[ + List[Dict[str, Any]], None, UnsetType + ] = UNSET + """List of the most popular queries that accessed this asset.""" + + source_read_expensive_query_record_list: Union[ + List[Dict[str, Any]], None, UnsetType + ] = UNSET + """List of the most expensive queries that accessed this asset.""" + + source_read_slow_query_record_list: Union[List[Dict[str, Any]], None, UnsetType] = ( + UNSET + ) + """List of the slowest queries that accessed this asset.""" + + source_query_compute_cost_list: Union[List[str], None, UnsetType] = UNSET + """List of most expensive warehouse names.""" + + source_query_compute_cost_record_list: Union[ + List[Dict[str, Any]], None, UnsetType + ] = UNSET + """List of most expensive warehouses with extra insights.""" + + dbt_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of this asset in dbt.""" + + asset_dbt_workflow_last_updated: Union[str, None, UnsetType] = UNSET + """Name of the DBT workflow in Atlan that last updated the asset.""" + + asset_dbt_alias: Union[str, None, UnsetType] = UNSET + """Alias of this asset in dbt.""" + + asset_dbt_meta: Union[str, None, UnsetType] = UNSET + """Metadata for this asset in dbt, specifically everything under the 'meta' key in the dbt object.""" + + asset_dbt_unique_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of this asset in dbt.""" + + asset_dbt_account_name: Union[str, None, UnsetType] = UNSET + """Name of the account in which this asset exists in dbt.""" + + asset_dbt_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which this asset exists in dbt.""" + + asset_dbt_package_name: Union[str, None, UnsetType] = UNSET + """Name of the package in which this asset exists in dbt.""" + + asset_dbt_job_name: Union[str, None, UnsetType] = UNSET + """Name of the job that materialized this asset in dbt.""" + + asset_dbt_job_schedule: Union[str, None, UnsetType] = UNSET + """Schedule of the job that materialized this asset in dbt.""" + + asset_dbt_job_status: Union[str, None, UnsetType] = UNSET + """Status of the job that materialized this asset in dbt.""" + + asset_dbt_test_status: Union[str, None, UnsetType] = UNSET + """All associated dbt test statuses.""" + + asset_dbt_job_schedule_cron_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable cron schedule of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt last ran, in milliseconds.""" + + asset_dbt_job_last_run_url: Union[str, None, UnsetType] = UNSET + """URL of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_created_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt was last created, in milliseconds.""" + + asset_dbt_job_last_run_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt was last updated, in milliseconds.""" + + asset_dbt_job_last_run_dequed_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt was dequeued, in milliseconds.""" + + asset_dbt_job_last_run_started_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt was started running, in milliseconds.""" + + asset_dbt_job_last_run_total_duration: Union[str, None, UnsetType] = UNSET + """Total duration of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_total_duration_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable total duration of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_queued_duration: Union[str, None, UnsetType] = UNSET + """Total duration the job that materialized this asset in dbt spent being queued.""" + + asset_dbt_job_last_run_queued_duration_humanized: Union[str, None, UnsetType] = ( + UNSET + ) + """Human-readable total duration of the last run of the job that materialized this asset in dbt spend being queued.""" + + asset_dbt_job_last_run_run_duration: Union[str, None, UnsetType] = UNSET + """Run duration of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_run_duration_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable run duration of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_git_branch: Union[str, None, UnsetType] = UNSET + """Branch in git from which the last run of the job that materialized this asset in dbt ran.""" + + asset_dbt_job_last_run_git_sha: Union[str, None, UnsetType] = UNSET + """SHA hash in git for the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_status_message: Union[str, None, UnsetType] = UNSET + """Status message of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_owner_thread_id: Union[str, None, UnsetType] = UNSET + """Thread ID of the owner of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_executed_by_thread_id: Union[str, None, UnsetType] = UNSET + """Thread ID of the user who executed the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_artifacts_saved: Union[bool, None, UnsetType] = UNSET + """Whether artifacts were saved from the last run of the job that materialized this asset in dbt (true) or not (false).""" + + asset_dbt_job_last_run_artifact_s3_path: Union[str, None, UnsetType] = UNSET + """Path in S3 to the artifacts saved from the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_has_docs_generated: Union[bool, None, UnsetType] = UNSET + """Whether docs were generated from the last run of the job that materialized this asset in dbt (true) or not (false).""" + + asset_dbt_job_last_run_has_sources_generated: Union[bool, None, UnsetType] = UNSET + """Whether sources were generated from the last run of the job that materialized this asset in dbt (true) or not (false).""" + + asset_dbt_job_last_run_notifications_sent: Union[bool, None, UnsetType] = UNSET + """Whether notifications were sent from the last run of the job that materialized this asset in dbt (true) or not (false).""" + + asset_dbt_job_next_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) when the next run of the job that materializes this asset in dbt is scheduled.""" + + asset_dbt_job_next_run_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable time when the next run of the job that materializes this asset in dbt is scheduled.""" + + asset_dbt_environment_name: Union[str, None, UnsetType] = UNSET + """Name of the environment in which this asset is materialized in dbt.""" + + asset_dbt_environment_dbt_version: Union[str, None, UnsetType] = UNSET + """Version of the environment in which this asset is materialized in dbt.""" + + asset_dbt_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset in dbt.""" + + asset_dbt_semantic_layer_proxy_url: Union[str, None, UnsetType] = UNSET + """URL of the semantic layer proxy for this asset in dbt.""" + + asset_dbt_source_freshness_criteria: Union[str, None, UnsetType] = UNSET + """Freshness criteria for the source of this asset in dbt.""" + + sample_data_url: Union[str, None, UnsetType] = UNSET + """URL for sample data for this asset.""" + + asset_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset.""" + + asset_mc_incident_names: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident names attached to this asset.""" + + asset_mc_incident_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of unique Monte Carlo incident names attached to this asset.""" + + asset_mc_alert_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of unique Monte Carlo alert names attached to this asset.""" + + asset_mc_monitor_names: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo monitor names attached to this asset.""" + + asset_mc_monitor_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of unique Monte Carlo monitor names attached to this asset.""" + + asset_mc_monitor_statuses: Union[List[str], None, UnsetType] = UNSET + """Statuses of all associated Monte Carlo monitors.""" + + asset_mc_monitor_types: Union[List[str], None, UnsetType] = UNSET + """Types of all associated Monte Carlo monitors.""" + + asset_mc_monitor_schedule_types: Union[List[str], None, UnsetType] = UNSET + """Schedules of all associated Monte Carlo monitors.""" + + asset_mc_incident_types: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident types associated with this asset.""" + + asset_mc_incident_sub_types: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident sub-types associated with this asset.""" + + asset_mc_incident_severities: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident severities associated with this asset.""" + + asset_mc_incident_priorities: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident priorities associated with this asset.""" + + asset_mc_incident_states: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident states associated with this asset.""" + + asset_mc_is_monitored: Union[bool, None, UnsetType] = UNSET + """Tracks whether this asset is monitored by MC or not""" + + asset_mc_last_sync_run_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last synced from Monte Carlo.""" + + starred_by: Union[List[str], None, UnsetType] = UNSET + """Users who have starred this asset.""" + + starred_details_list: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of usernames with extra information of the users who have starred an asset.""" + + starred_count: Union[int, None, UnsetType] = UNSET + """Number of users who have starred this asset.""" + + asset_anomalo_dq_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetAnomaloDQStatus" + ) + """Status of data quality from Anomalo.""" + + asset_anomalo_check_count: Union[int, None, UnsetType] = UNSET + """Total number of checks present in Anomalo for this asset.""" + + asset_anomalo_failed_check_count: Union[int, None, UnsetType] = UNSET + """Total number of checks failed in Anomalo for this asset.""" + + asset_anomalo_check_statuses: Union[str, None, UnsetType] = UNSET + """Stringified JSON object containing status of all Anomalo checks associated to this asset.""" + + asset_anomalo_last_check_run_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the last check was run via Anomalo.""" + + asset_anomalo_applied_check_types: Union[List[str], None, UnsetType] = UNSET + """All associated Anomalo check types.""" + + asset_anomalo_failed_check_types: Union[List[str], None, UnsetType] = UNSET + """All associated Anomalo failed check types.""" + + asset_anomalo_source_url: Union[str, None, UnsetType] = UNSET + """URL of the source in Anomalo.""" + + asset_soda_dq_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetSodaDQStatus" + ) + """Status of data quality from Soda.""" + + asset_soda_check_count: Union[int, None, UnsetType] = UNSET + """Number of checks done via Soda.""" + + asset_soda_last_sync_run_at: Union[int, None, UnsetType] = UNSET + """""" + + asset_soda_last_scan_at: Union[int, None, UnsetType] = UNSET + """""" + + asset_soda_check_statuses: Union[str, None, UnsetType] = UNSET + """All associated Soda check statuses.""" + + asset_soda_source_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetSodaSourceURL" + ) + """""" + + asset_icon: Union[str, None, UnsetType] = UNSET + """Name of the icon to use for this asset. (Only applies to glossaries, currently.)""" + + asset_external_dq_metadata_details: Union[ + Dict[str, Dict[str, Any]], None, UnsetType + ] = msgspec.field(default=UNSET, name="assetExternalDQMetadataDetails") + """DQ metadata captured for asset from external DQ tool(s).""" + + is_partial: Union[bool, None, UnsetType] = UNSET + """Indicates this asset is not fully-known, if true.""" + + is_ai_generated: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="isAIGenerated" + ) + """""" + + asset_cover_image: Union[str, None, UnsetType] = UNSET + """Cover image to use for this asset in the UI (applicable to only a few asset types).""" + + asset_theme_hex: Union[str, None, UnsetType] = UNSET + """Color (in hexadecimal RGB) to use to represent this asset.""" + + lexicographical_sort_order: Union[str, None, UnsetType] = UNSET + """Custom order for sorting purpose, managed by client""" + + has_contract: Union[bool, None, UnsetType] = UNSET + """Whether this asset has contract (true) or not (false).""" + + asset_redirect_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetRedirectGUIDs" + ) + """Array of asset ids that equivalent to this asset.""" + + asset_policy_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetPolicyGUIDs" + ) + """Array of policy ids governing this asset""" + + asset_policies_count: Union[int, None, UnsetType] = UNSET + """Count of policies inside the asset""" + + domain_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="domainGUIDs" + ) + """Array of domain guids linked to this asset""" + + non_compliant_asset_policy_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="nonCompliantAssetPolicyGUIDs" + ) + """Array of policy ids non-compliant to this asset""" + + product_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="productGUIDs" + ) + """Array of product guids linked to this asset""" + + output_product_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="outputProductGUIDs" + ) + """Array of product guids which have this asset as outputPort""" + + application_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the Application that contains this asset.""" + + application_field_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the ApplicationField that contains this asset.""" + + asset_user_defined_type: Union[str, None, UnsetType] = UNSET + """Name to use for this type of asset, as a subtype of the actual typeName.""" + + asset_internal_popularity_score: Union[float, None, UnsetType] = UNSET + """Internal Popularity score for this asset.""" + + asset_dq_schedule_type: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleType" + ) + """Type of schedule of the DQ rule that will run at datasource.""" + + asset_dq_schedule_crontab: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleCrontab" + ) + """Crontab of the DQ rule that will run at datasource.""" + + asset_dq_schedule_time_zone: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleTimeZone" + ) + """Timezone of the DQ rule schedule that will run at datasource""" + + asset_dq_schedule_source_sync_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleSourceSyncStatus" + ) + """Latest sync status of the schedule to the source.""" + + asset_dq_schedule_source_synced_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleSourceSyncedAt" + ) + """Time (epoch) at which the schedule synced to the source.""" + + asset_dq_schedule_source_sync_error_message: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQScheduleSourceSyncErrorMessage") + ) + """Error message in the case of sync state being "error".""" + + asset_dq_schedule_source_sync_error_code: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQScheduleSourceSyncErrorCode") + ) + """Error code in the case of sync state being "error".""" + + asset_dq_schedule_source_sync_raw_error: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQScheduleSourceSyncRawError") + ) + """Raw error message from the source.""" + + asset_dq_rule_attached_dimensions: Union[List[str], None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQRuleAttachedDimensions") + ) + """List of all the dimensions of attached rules.""" + + asset_dq_rule_failed_dimensions: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleFailedDimensions" + ) + """List of all the dimensions of failed rules.""" + + asset_dq_rule_passed_dimensions: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRulePassedDimensions" + ) + """List of all the dimensions for which all the rules passed.""" + + asset_dq_rule_attached_rule_types: Union[List[str], None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQRuleAttachedRuleTypes") + ) + """List of all the types of attached rules.""" + + asset_dq_rule_failed_rule_types: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleFailedRuleTypes" + ) + """List of all the types of failed rules.""" + + asset_dq_rule_passed_rule_types: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRulePassedRuleTypes" + ) + """List of all the types of rules for which all the rules passed.""" + + asset_dq_rule_result_tags: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleResultTags" + ) + """Tag for the result of the DQ rules. Eg, rule_pass:completeness:null_count.""" + + asset_dq_rule_last_run_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleLastRunAt" + ) + """Time (epoch) at which the last dq rule ran.""" + + asset_dq_manual_run_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQManualRunStatus" + ) + """Status of the latest manual DQ run triggered for this asset.""" + + asset_dq_rule_total_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleTotalCount" + ) + """Count of DQ rules attached to this asset.""" + + asset_dq_rule_failed_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleFailedCount" + ) + """Count of failed DQ rules attached to this asset.""" + + asset_dq_rule_passed_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRulePassedCount" + ) + """Count of passed DQ rules attached to this asset.""" + + asset_dq_result: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQResult" + ) + """Overall result of all the dq rules. If any one rule failed, then fail else pass.""" + + asset_dq_freshness_value: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQFreshnessValue" + ) + """Value of data freshness from Source.""" + + asset_dq_freshness_expectation: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQFreshnessExpectation" + ) + """Expectation of data freshness from Source.""" + + asset_dq_row_scope_filter_column_qualified_name: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQRowScopeFilterColumnQualifiedName") + ) + """Qualified name of the column used for row scope filtering in DQ rules for this asset.""" + + asset_space_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the space that contains this asset.""" + + asset_space_name: Union[str, None, UnsetType] = UNSET + """Name of the space that contains this asset.""" + + asset_gcp_dataplex_metadata_details: Union[Dict[str, Any], None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetGCPDataplexMetadataDetails") + ) + """Metrics captured by GCP Dataplex for objects associated with GCP services.""" + + asset_gcp_dataplex_aspect_list: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetGCPDataplexAspectList" + ) + """List of names of all Aspects linked to this asset.""" + + asset_gcp_dataplex_aspect_field_list: Union[List[str], None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetGCPDataplexAspectFieldList") + ) + """List of field key-values associated with all Aspects linked to this asset.""" + + asset_smus_metadata_form_names: Union[List[str], None, UnsetType] = UNSET + """List of AWS SMUS MetadataForm Names. This is mainly used for filtering purpose.""" + + asset_smus_metadata_form_key_value_details: Union[List[str], None, UnsetType] = ( + UNSET + ) + """List of AWS SMUS MetadataForm Key:Value Details. This is mainly used for filtering purpose.""" + + asset_smus_metadata_form_details: Union[List[Dict[str, Any]], None, UnsetType] = ( + UNSET + ) + """AWS SMUS Asset MetadataForm details""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "DataSet" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _data_set_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> DataSet: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + DataSet instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _data_set_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DataSetAttributes(ReferenceableAttributes): + """DataSet-specific attributes for nested API format.""" + + name: Union[str, None, UnsetType] = UNSET + """Name of this asset. Fallback for display purposes, if displayName is empty.""" + + display_name: Union[str, None, UnsetType] = UNSET + """Human-readable name of this asset used for display purposes (in user interface).""" + + description: Union[str, None, UnsetType] = UNSET + """Description of this asset, for example as crawled from a source. Fallback for display purposes, if userDescription is empty.""" + + asset_source_readme: Union[str, None, UnsetType] = UNSET + """Readme of this asset, as extracted from source. If present, this will be used for the readme in user interface.""" + + user_description: Union[str, None, UnsetType] = UNSET + """Description of this asset, as provided by a user. If present, this will be used for the description in user interface.""" + + asset_ai_generated_description: Union[str, None, UnsetType] = UNSET + """Description of this asset, generated by AI based on the asset's context. Displayed separately in the UI and can be used to overwrite existing descriptions.""" + + asset_ai_generated_description_confidence: Union[float, None, UnsetType] = UNSET + """Confidence score of the AI-generated description, ranging from 0.0 to 1.0.""" + + asset_ai_generated_description_reasoning: Union[str, None, UnsetType] = UNSET + """Reasoning behind the AI-generated description, explaining how the description was derived from the asset's context.""" + + tenant_id: Union[str, None, UnsetType] = UNSET + """Name of the Atlan workspace in which this asset exists.""" + + certificate_status: Union[str, None, UnsetType] = UNSET + """Status of this asset's certification.""" + + certificate_status_message: Union[str, None, UnsetType] = UNSET + """Human-readable descriptive message used to provide further detail to certificateStatus.""" + + certificate_updated_by: Union[str, None, UnsetType] = UNSET + """Name of the user who last updated the certification of this asset.""" + + certificate_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the certification was last updated, in milliseconds.""" + + announcement_title: Union[str, None, UnsetType] = UNSET + """Brief title for the announcement on this asset. Required when announcementType is specified.""" + + announcement_message: Union[str, None, UnsetType] = UNSET + """Detailed message to include in the announcement on this asset.""" + + announcement_type: Union[str, None, UnsetType] = UNSET + """Type of announcement on this asset.""" + + announcement_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the announcement was last updated, in milliseconds.""" + + announcement_updated_by: Union[str, None, UnsetType] = UNSET + """Name of the user who last updated the announcement.""" + + owner_users: Union[Set[str], None, UnsetType] = UNSET + """List of users who own this asset.""" + + owner_groups: Union[Set[str], None, UnsetType] = UNSET + """List of groups who own this asset.""" + + admin_users: Union[Set[str], None, UnsetType] = UNSET + """List of users who administer this asset. (This is only used for certain asset types.)""" + + admin_groups: Union[Set[str], None, UnsetType] = UNSET + """List of groups who administer this asset. (This is only used for certain asset types.)""" + + viewer_users: Union[Set[str], None, UnsetType] = UNSET + """List of users who can view assets contained in a collection. (This is only used for certain asset types.)""" + + viewer_groups: Union[Set[str], None, UnsetType] = UNSET + """List of groups who can view assets contained in a collection. (This is only used for certain asset types.)""" + + connector_name: Union[str, None, UnsetType] = UNSET + """Type of the connector through which this asset is accessible.""" + + connection_name: Union[str, None, UnsetType] = UNSET + """Simple name of the connection through which this asset is accessible.""" + + connection_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the connection through which this asset is accessible.""" + + has_lineage: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="__hasLineage" + ) + """Whether this asset has lineage (true) or not (false).""" + + is_discoverable: Union[bool, None, UnsetType] = UNSET + """Whether this asset is discoverable through the UI (true) or not (false).""" + + is_editable: Union[bool, None, UnsetType] = UNSET + """Whether this asset can be edited in the UI (true) or not (false).""" + + sub_type: Union[str, None, UnsetType] = UNSET + """Subtype of this asset.""" + + view_score: Union[float, None, UnsetType] = UNSET + """View score for this asset.""" + + popularity_score: Union[float, None, UnsetType] = UNSET + """Popularity score for this asset.""" + + source_owners: Union[str, None, UnsetType] = UNSET + """List of owners of this asset, in the source system.""" + + asset_source_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for this asset in the system from which it was sourced.""" + + source_created_by: Union[str, None, UnsetType] = UNSET + """Name of the user who created this asset, in the source system.""" + + source_created_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was created in the source system, in milliseconds.""" + + source_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last updated in the source system, in milliseconds.""" + + source_updated_by: Union[str, None, UnsetType] = UNSET + """Name of the user who last updated this asset, in the source system.""" + + source_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sourceURL" + ) + """URL to the resource within the source application, used to create a button to view this asset in the source application.""" + + source_embed_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sourceEmbedURL" + ) + """URL to create an embed for a resource (for example, an image of a dashboard) within Atlan.""" + + last_sync_workflow_name: Union[str, None, UnsetType] = UNSET + """Name of the crawler that last synchronized this asset.""" + + last_sync_run_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last crawled, in milliseconds.""" + + last_sync_run: Union[str, None, UnsetType] = UNSET + """Name of the last run of the crawler that last synchronized this asset.""" + + admin_roles: Union[Set[str], None, UnsetType] = UNSET + """List of roles who administer this asset. (This is only used for Connection assets.)""" + + source_read_count: Union[int, None, UnsetType] = UNSET + """Total count of all read operations at source.""" + + source_read_user_count: Union[int, None, UnsetType] = UNSET + """Total number of unique users that read data from asset.""" + + source_last_read_at: Union[int, None, UnsetType] = UNSET + """Timestamp of most recent read operation.""" + + last_row_changed_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) of the last operation that inserted, updated, or deleted rows, in milliseconds.""" + + source_total_cost: Union[float, None, UnsetType] = UNSET + """Total cost of all operations at source.""" + + source_cost_unit: Union[str, None, UnsetType] = UNSET + """The unit of measure for sourceTotalCost.""" + + source_read_query_cost: Union[float, None, UnsetType] = UNSET + """Total cost of read queries at source.""" + + source_read_recent_user_list: Union[List[str], None, UnsetType] = UNSET + """List of usernames of the most recent users who read this asset.""" + + source_read_recent_user_record_list: Union[ + List[Dict[str, Any]], None, UnsetType + ] = UNSET + """List of usernames with extra insights for the most recent users who read this asset.""" + + source_read_top_user_list: Union[List[str], None, UnsetType] = UNSET + """List of usernames of the users who read this asset the most.""" + + source_read_top_user_record_list: Union[List[Dict[str, Any]], None, UnsetType] = ( + UNSET + ) + """List of usernames with extra insights for the users who read this asset the most.""" + + source_read_popular_query_record_list: Union[ + List[Dict[str, Any]], None, UnsetType + ] = UNSET + """List of the most popular queries that accessed this asset.""" + + source_read_expensive_query_record_list: Union[ + List[Dict[str, Any]], None, UnsetType + ] = UNSET + """List of the most expensive queries that accessed this asset.""" + + source_read_slow_query_record_list: Union[List[Dict[str, Any]], None, UnsetType] = ( + UNSET + ) + """List of the slowest queries that accessed this asset.""" + + source_query_compute_cost_list: Union[List[str], None, UnsetType] = UNSET + """List of most expensive warehouse names.""" + + source_query_compute_cost_record_list: Union[ + List[Dict[str, Any]], None, UnsetType + ] = UNSET + """List of most expensive warehouses with extra insights.""" + + dbt_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of this asset in dbt.""" + + asset_dbt_workflow_last_updated: Union[str, None, UnsetType] = UNSET + """Name of the DBT workflow in Atlan that last updated the asset.""" + + asset_dbt_alias: Union[str, None, UnsetType] = UNSET + """Alias of this asset in dbt.""" + + asset_dbt_meta: Union[str, None, UnsetType] = UNSET + """Metadata for this asset in dbt, specifically everything under the 'meta' key in the dbt object.""" + + asset_dbt_unique_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of this asset in dbt.""" + + asset_dbt_account_name: Union[str, None, UnsetType] = UNSET + """Name of the account in which this asset exists in dbt.""" + + asset_dbt_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which this asset exists in dbt.""" + + asset_dbt_package_name: Union[str, None, UnsetType] = UNSET + """Name of the package in which this asset exists in dbt.""" + + asset_dbt_job_name: Union[str, None, UnsetType] = UNSET + """Name of the job that materialized this asset in dbt.""" + + asset_dbt_job_schedule: Union[str, None, UnsetType] = UNSET + """Schedule of the job that materialized this asset in dbt.""" + + asset_dbt_job_status: Union[str, None, UnsetType] = UNSET + """Status of the job that materialized this asset in dbt.""" + + asset_dbt_test_status: Union[str, None, UnsetType] = UNSET + """All associated dbt test statuses.""" + + asset_dbt_job_schedule_cron_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable cron schedule of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt last ran, in milliseconds.""" + + asset_dbt_job_last_run_url: Union[str, None, UnsetType] = UNSET + """URL of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_created_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt was last created, in milliseconds.""" + + asset_dbt_job_last_run_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt was last updated, in milliseconds.""" + + asset_dbt_job_last_run_dequed_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt was dequeued, in milliseconds.""" + + asset_dbt_job_last_run_started_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt was started running, in milliseconds.""" + + asset_dbt_job_last_run_total_duration: Union[str, None, UnsetType] = UNSET + """Total duration of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_total_duration_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable total duration of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_queued_duration: Union[str, None, UnsetType] = UNSET + """Total duration the job that materialized this asset in dbt spent being queued.""" + + asset_dbt_job_last_run_queued_duration_humanized: Union[str, None, UnsetType] = ( + UNSET + ) + """Human-readable total duration of the last run of the job that materialized this asset in dbt spend being queued.""" + + asset_dbt_job_last_run_run_duration: Union[str, None, UnsetType] = UNSET + """Run duration of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_run_duration_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable run duration of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_git_branch: Union[str, None, UnsetType] = UNSET + """Branch in git from which the last run of the job that materialized this asset in dbt ran.""" + + asset_dbt_job_last_run_git_sha: Union[str, None, UnsetType] = UNSET + """SHA hash in git for the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_status_message: Union[str, None, UnsetType] = UNSET + """Status message of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_owner_thread_id: Union[str, None, UnsetType] = UNSET + """Thread ID of the owner of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_executed_by_thread_id: Union[str, None, UnsetType] = UNSET + """Thread ID of the user who executed the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_artifacts_saved: Union[bool, None, UnsetType] = UNSET + """Whether artifacts were saved from the last run of the job that materialized this asset in dbt (true) or not (false).""" + + asset_dbt_job_last_run_artifact_s3_path: Union[str, None, UnsetType] = UNSET + """Path in S3 to the artifacts saved from the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_has_docs_generated: Union[bool, None, UnsetType] = UNSET + """Whether docs were generated from the last run of the job that materialized this asset in dbt (true) or not (false).""" + + asset_dbt_job_last_run_has_sources_generated: Union[bool, None, UnsetType] = UNSET + """Whether sources were generated from the last run of the job that materialized this asset in dbt (true) or not (false).""" + + asset_dbt_job_last_run_notifications_sent: Union[bool, None, UnsetType] = UNSET + """Whether notifications were sent from the last run of the job that materialized this asset in dbt (true) or not (false).""" + + asset_dbt_job_next_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) when the next run of the job that materializes this asset in dbt is scheduled.""" + + asset_dbt_job_next_run_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable time when the next run of the job that materializes this asset in dbt is scheduled.""" + + asset_dbt_environment_name: Union[str, None, UnsetType] = UNSET + """Name of the environment in which this asset is materialized in dbt.""" + + asset_dbt_environment_dbt_version: Union[str, None, UnsetType] = UNSET + """Version of the environment in which this asset is materialized in dbt.""" + + asset_dbt_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset in dbt.""" + + asset_dbt_semantic_layer_proxy_url: Union[str, None, UnsetType] = UNSET + """URL of the semantic layer proxy for this asset in dbt.""" + + asset_dbt_source_freshness_criteria: Union[str, None, UnsetType] = UNSET + """Freshness criteria for the source of this asset in dbt.""" + + sample_data_url: Union[str, None, UnsetType] = UNSET + """URL for sample data for this asset.""" + + asset_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset.""" + + asset_mc_incident_names: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident names attached to this asset.""" + + asset_mc_incident_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of unique Monte Carlo incident names attached to this asset.""" + + asset_mc_alert_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of unique Monte Carlo alert names attached to this asset.""" + + asset_mc_monitor_names: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo monitor names attached to this asset.""" + + asset_mc_monitor_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of unique Monte Carlo monitor names attached to this asset.""" + + asset_mc_monitor_statuses: Union[List[str], None, UnsetType] = UNSET + """Statuses of all associated Monte Carlo monitors.""" + + asset_mc_monitor_types: Union[List[str], None, UnsetType] = UNSET + """Types of all associated Monte Carlo monitors.""" + + asset_mc_monitor_schedule_types: Union[List[str], None, UnsetType] = UNSET + """Schedules of all associated Monte Carlo monitors.""" + + asset_mc_incident_types: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident types associated with this asset.""" + + asset_mc_incident_sub_types: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident sub-types associated with this asset.""" + + asset_mc_incident_severities: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident severities associated with this asset.""" + + asset_mc_incident_priorities: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident priorities associated with this asset.""" + + asset_mc_incident_states: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident states associated with this asset.""" + + asset_mc_is_monitored: Union[bool, None, UnsetType] = UNSET + """Tracks whether this asset is monitored by MC or not""" + + asset_mc_last_sync_run_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last synced from Monte Carlo.""" + + starred_by: Union[List[str], None, UnsetType] = UNSET + """Users who have starred this asset.""" + + starred_details_list: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of usernames with extra information of the users who have starred an asset.""" + + starred_count: Union[int, None, UnsetType] = UNSET + """Number of users who have starred this asset.""" + + asset_anomalo_dq_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetAnomaloDQStatus" + ) + """Status of data quality from Anomalo.""" + + asset_anomalo_check_count: Union[int, None, UnsetType] = UNSET + """Total number of checks present in Anomalo for this asset.""" + + asset_anomalo_failed_check_count: Union[int, None, UnsetType] = UNSET + """Total number of checks failed in Anomalo for this asset.""" + + asset_anomalo_check_statuses: Union[str, None, UnsetType] = UNSET + """Stringified JSON object containing status of all Anomalo checks associated to this asset.""" + + asset_anomalo_last_check_run_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the last check was run via Anomalo.""" + + asset_anomalo_applied_check_types: Union[List[str], None, UnsetType] = UNSET + """All associated Anomalo check types.""" + + asset_anomalo_failed_check_types: Union[List[str], None, UnsetType] = UNSET + """All associated Anomalo failed check types.""" + + asset_anomalo_source_url: Union[str, None, UnsetType] = UNSET + """URL of the source in Anomalo.""" + + asset_soda_dq_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetSodaDQStatus" + ) + """Status of data quality from Soda.""" + + asset_soda_check_count: Union[int, None, UnsetType] = UNSET + """Number of checks done via Soda.""" + + asset_soda_last_sync_run_at: Union[int, None, UnsetType] = UNSET + """""" + + asset_soda_last_scan_at: Union[int, None, UnsetType] = UNSET + """""" + + asset_soda_check_statuses: Union[str, None, UnsetType] = UNSET + """All associated Soda check statuses.""" + + asset_soda_source_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetSodaSourceURL" + ) + """""" + + asset_icon: Union[str, None, UnsetType] = UNSET + """Name of the icon to use for this asset. (Only applies to glossaries, currently.)""" + + asset_external_dq_metadata_details: Union[ + Dict[str, Dict[str, Any]], None, UnsetType + ] = msgspec.field(default=UNSET, name="assetExternalDQMetadataDetails") + """DQ metadata captured for asset from external DQ tool(s).""" + + is_partial: Union[bool, None, UnsetType] = UNSET + """Indicates this asset is not fully-known, if true.""" + + is_ai_generated: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="isAIGenerated" + ) + """""" + + asset_cover_image: Union[str, None, UnsetType] = UNSET + """Cover image to use for this asset in the UI (applicable to only a few asset types).""" + + asset_theme_hex: Union[str, None, UnsetType] = UNSET + """Color (in hexadecimal RGB) to use to represent this asset.""" + + lexicographical_sort_order: Union[str, None, UnsetType] = UNSET + """Custom order for sorting purpose, managed by client""" + + has_contract: Union[bool, None, UnsetType] = UNSET + """Whether this asset has contract (true) or not (false).""" + + asset_redirect_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetRedirectGUIDs" + ) + """Array of asset ids that equivalent to this asset.""" + + asset_policy_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetPolicyGUIDs" + ) + """Array of policy ids governing this asset""" + + asset_policies_count: Union[int, None, UnsetType] = UNSET + """Count of policies inside the asset""" + + domain_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="domainGUIDs" + ) + """Array of domain guids linked to this asset""" + + non_compliant_asset_policy_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="nonCompliantAssetPolicyGUIDs" + ) + """Array of policy ids non-compliant to this asset""" + + product_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="productGUIDs" + ) + """Array of product guids linked to this asset""" + + output_product_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="outputProductGUIDs" + ) + """Array of product guids which have this asset as outputPort""" + + application_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the Application that contains this asset.""" + + application_field_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the ApplicationField that contains this asset.""" + + asset_user_defined_type: Union[str, None, UnsetType] = UNSET + """Name to use for this type of asset, as a subtype of the actual typeName.""" + + asset_internal_popularity_score: Union[float, None, UnsetType] = UNSET + """Internal Popularity score for this asset.""" + + asset_dq_schedule_type: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleType" + ) + """Type of schedule of the DQ rule that will run at datasource.""" + + asset_dq_schedule_crontab: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleCrontab" + ) + """Crontab of the DQ rule that will run at datasource.""" + + asset_dq_schedule_time_zone: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleTimeZone" + ) + """Timezone of the DQ rule schedule that will run at datasource""" + + asset_dq_schedule_source_sync_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleSourceSyncStatus" + ) + """Latest sync status of the schedule to the source.""" + + asset_dq_schedule_source_synced_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleSourceSyncedAt" + ) + """Time (epoch) at which the schedule synced to the source.""" + + asset_dq_schedule_source_sync_error_message: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQScheduleSourceSyncErrorMessage") + ) + """Error message in the case of sync state being "error".""" + + asset_dq_schedule_source_sync_error_code: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQScheduleSourceSyncErrorCode") + ) + """Error code in the case of sync state being "error".""" + + asset_dq_schedule_source_sync_raw_error: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQScheduleSourceSyncRawError") + ) + """Raw error message from the source.""" + + asset_dq_rule_attached_dimensions: Union[List[str], None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQRuleAttachedDimensions") + ) + """List of all the dimensions of attached rules.""" + + asset_dq_rule_failed_dimensions: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleFailedDimensions" + ) + """List of all the dimensions of failed rules.""" + + asset_dq_rule_passed_dimensions: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRulePassedDimensions" + ) + """List of all the dimensions for which all the rules passed.""" + + asset_dq_rule_attached_rule_types: Union[List[str], None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQRuleAttachedRuleTypes") + ) + """List of all the types of attached rules.""" + + asset_dq_rule_failed_rule_types: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleFailedRuleTypes" + ) + """List of all the types of failed rules.""" + + asset_dq_rule_passed_rule_types: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRulePassedRuleTypes" + ) + """List of all the types of rules for which all the rules passed.""" + + asset_dq_rule_result_tags: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleResultTags" + ) + """Tag for the result of the DQ rules. Eg, rule_pass:completeness:null_count.""" + + asset_dq_rule_last_run_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleLastRunAt" + ) + """Time (epoch) at which the last dq rule ran.""" + + asset_dq_manual_run_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQManualRunStatus" + ) + """Status of the latest manual DQ run triggered for this asset.""" + + asset_dq_rule_total_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleTotalCount" + ) + """Count of DQ rules attached to this asset.""" + + asset_dq_rule_failed_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleFailedCount" + ) + """Count of failed DQ rules attached to this asset.""" + + asset_dq_rule_passed_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRulePassedCount" + ) + """Count of passed DQ rules attached to this asset.""" + + asset_dq_result: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQResult" + ) + """Overall result of all the dq rules. If any one rule failed, then fail else pass.""" + + asset_dq_freshness_value: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQFreshnessValue" + ) + """Value of data freshness from Source.""" + + asset_dq_freshness_expectation: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQFreshnessExpectation" + ) + """Expectation of data freshness from Source.""" + + asset_dq_row_scope_filter_column_qualified_name: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQRowScopeFilterColumnQualifiedName") + ) + """Qualified name of the column used for row scope filtering in DQ rules for this asset.""" + + asset_space_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the space that contains this asset.""" + + asset_space_name: Union[str, None, UnsetType] = UNSET + """Name of the space that contains this asset.""" + + asset_gcp_dataplex_metadata_details: Union[Dict[str, Any], None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetGCPDataplexMetadataDetails") + ) + """Metrics captured by GCP Dataplex for objects associated with GCP services.""" + + asset_gcp_dataplex_aspect_list: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetGCPDataplexAspectList" + ) + """List of names of all Aspects linked to this asset.""" + + asset_gcp_dataplex_aspect_field_list: Union[List[str], None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetGCPDataplexAspectFieldList") + ) + """List of field key-values associated with all Aspects linked to this asset.""" + + asset_smus_metadata_form_names: Union[List[str], None, UnsetType] = UNSET + """List of AWS SMUS MetadataForm Names. This is mainly used for filtering purpose.""" + + asset_smus_metadata_form_key_value_details: Union[List[str], None, UnsetType] = ( + UNSET + ) + """List of AWS SMUS MetadataForm Key:Value Details. This is mainly used for filtering purpose.""" + + asset_smus_metadata_form_details: Union[List[Dict[str, Any]], None, UnsetType] = ( + UNSET + ) + """AWS SMUS Asset MetadataForm details""" + + +class DataSetRelationshipAttributes(ReferenceableRelationshipAttributes): + """DataSet-specific relationship attributes for nested API format.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + +class DataSetNested(ReferenceableNested): + """DataSet in nested API format for high-performance serialization.""" + + attributes: Union[DataSetAttributes, UnsetType] = UNSET + relationship_attributes: Union[DataSetRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[DataSetRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[DataSetRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DATA_SET_REL_FIELDS: List[str] = [ + *_REFERENCEABLE_REL_FIELDS, + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", +] + + +def _populate_data_set_attrs(attrs: DataSetAttributes, obj: DataSet) -> None: + """Populate DataSet-specific attributes on the attrs struct.""" + _populate_referenceable_attrs(attrs, obj) + attrs.name = obj.name + attrs.display_name = obj.display_name + attrs.description = obj.description + attrs.asset_source_readme = obj.asset_source_readme + attrs.user_description = obj.user_description + attrs.asset_ai_generated_description = obj.asset_ai_generated_description + attrs.asset_ai_generated_description_confidence = ( + obj.asset_ai_generated_description_confidence + ) + attrs.asset_ai_generated_description_reasoning = ( + obj.asset_ai_generated_description_reasoning + ) + attrs.tenant_id = obj.tenant_id + attrs.certificate_status = obj.certificate_status + attrs.certificate_status_message = obj.certificate_status_message + attrs.certificate_updated_by = obj.certificate_updated_by + attrs.certificate_updated_at = obj.certificate_updated_at + attrs.announcement_title = obj.announcement_title + attrs.announcement_message = obj.announcement_message + attrs.announcement_type = obj.announcement_type + attrs.announcement_updated_at = obj.announcement_updated_at + attrs.announcement_updated_by = obj.announcement_updated_by + attrs.owner_users = obj.owner_users + attrs.owner_groups = obj.owner_groups + attrs.admin_users = obj.admin_users + attrs.admin_groups = obj.admin_groups + attrs.viewer_users = obj.viewer_users + attrs.viewer_groups = obj.viewer_groups + attrs.connector_name = obj.connector_name + attrs.connection_name = obj.connection_name + attrs.connection_qualified_name = obj.connection_qualified_name + attrs.has_lineage = obj.has_lineage + attrs.is_discoverable = obj.is_discoverable + attrs.is_editable = obj.is_editable + attrs.sub_type = obj.sub_type + attrs.view_score = obj.view_score + attrs.popularity_score = obj.popularity_score + attrs.source_owners = obj.source_owners + attrs.asset_source_id = obj.asset_source_id + attrs.source_created_by = obj.source_created_by + attrs.source_created_at = obj.source_created_at + attrs.source_updated_at = obj.source_updated_at + attrs.source_updated_by = obj.source_updated_by + attrs.source_url = obj.source_url + attrs.source_embed_url = obj.source_embed_url + attrs.last_sync_workflow_name = obj.last_sync_workflow_name + attrs.last_sync_run_at = obj.last_sync_run_at + attrs.last_sync_run = obj.last_sync_run + attrs.admin_roles = obj.admin_roles + attrs.source_read_count = obj.source_read_count + attrs.source_read_user_count = obj.source_read_user_count + attrs.source_last_read_at = obj.source_last_read_at + attrs.last_row_changed_at = obj.last_row_changed_at + attrs.source_total_cost = obj.source_total_cost + attrs.source_cost_unit = obj.source_cost_unit + attrs.source_read_query_cost = obj.source_read_query_cost + attrs.source_read_recent_user_list = obj.source_read_recent_user_list + attrs.source_read_recent_user_record_list = obj.source_read_recent_user_record_list + attrs.source_read_top_user_list = obj.source_read_top_user_list + attrs.source_read_top_user_record_list = obj.source_read_top_user_record_list + attrs.source_read_popular_query_record_list = ( + obj.source_read_popular_query_record_list + ) + attrs.source_read_expensive_query_record_list = ( + obj.source_read_expensive_query_record_list + ) + attrs.source_read_slow_query_record_list = obj.source_read_slow_query_record_list + attrs.source_query_compute_cost_list = obj.source_query_compute_cost_list + attrs.source_query_compute_cost_record_list = ( + obj.source_query_compute_cost_record_list + ) + attrs.dbt_qualified_name = obj.dbt_qualified_name + attrs.asset_dbt_workflow_last_updated = obj.asset_dbt_workflow_last_updated + attrs.asset_dbt_alias = obj.asset_dbt_alias + attrs.asset_dbt_meta = obj.asset_dbt_meta + attrs.asset_dbt_unique_id = obj.asset_dbt_unique_id + attrs.asset_dbt_account_name = obj.asset_dbt_account_name + attrs.asset_dbt_project_name = obj.asset_dbt_project_name + attrs.asset_dbt_package_name = obj.asset_dbt_package_name + attrs.asset_dbt_job_name = obj.asset_dbt_job_name + attrs.asset_dbt_job_schedule = obj.asset_dbt_job_schedule + attrs.asset_dbt_job_status = obj.asset_dbt_job_status + attrs.asset_dbt_test_status = obj.asset_dbt_test_status + attrs.asset_dbt_job_schedule_cron_humanized = ( + obj.asset_dbt_job_schedule_cron_humanized + ) + attrs.asset_dbt_job_last_run = obj.asset_dbt_job_last_run + attrs.asset_dbt_job_last_run_url = obj.asset_dbt_job_last_run_url + attrs.asset_dbt_job_last_run_created_at = obj.asset_dbt_job_last_run_created_at + attrs.asset_dbt_job_last_run_updated_at = obj.asset_dbt_job_last_run_updated_at + attrs.asset_dbt_job_last_run_dequed_at = obj.asset_dbt_job_last_run_dequed_at + attrs.asset_dbt_job_last_run_started_at = obj.asset_dbt_job_last_run_started_at + attrs.asset_dbt_job_last_run_total_duration = ( + obj.asset_dbt_job_last_run_total_duration + ) + attrs.asset_dbt_job_last_run_total_duration_humanized = ( + obj.asset_dbt_job_last_run_total_duration_humanized + ) + attrs.asset_dbt_job_last_run_queued_duration = ( + obj.asset_dbt_job_last_run_queued_duration + ) + attrs.asset_dbt_job_last_run_queued_duration_humanized = ( + obj.asset_dbt_job_last_run_queued_duration_humanized + ) + attrs.asset_dbt_job_last_run_run_duration = obj.asset_dbt_job_last_run_run_duration + attrs.asset_dbt_job_last_run_run_duration_humanized = ( + obj.asset_dbt_job_last_run_run_duration_humanized + ) + attrs.asset_dbt_job_last_run_git_branch = obj.asset_dbt_job_last_run_git_branch + attrs.asset_dbt_job_last_run_git_sha = obj.asset_dbt_job_last_run_git_sha + attrs.asset_dbt_job_last_run_status_message = ( + obj.asset_dbt_job_last_run_status_message + ) + attrs.asset_dbt_job_last_run_owner_thread_id = ( + obj.asset_dbt_job_last_run_owner_thread_id + ) + attrs.asset_dbt_job_last_run_executed_by_thread_id = ( + obj.asset_dbt_job_last_run_executed_by_thread_id + ) + attrs.asset_dbt_job_last_run_artifacts_saved = ( + obj.asset_dbt_job_last_run_artifacts_saved + ) + attrs.asset_dbt_job_last_run_artifact_s3_path = ( + obj.asset_dbt_job_last_run_artifact_s3_path + ) + attrs.asset_dbt_job_last_run_has_docs_generated = ( + obj.asset_dbt_job_last_run_has_docs_generated + ) + attrs.asset_dbt_job_last_run_has_sources_generated = ( + obj.asset_dbt_job_last_run_has_sources_generated + ) + attrs.asset_dbt_job_last_run_notifications_sent = ( + obj.asset_dbt_job_last_run_notifications_sent + ) + attrs.asset_dbt_job_next_run = obj.asset_dbt_job_next_run + attrs.asset_dbt_job_next_run_humanized = obj.asset_dbt_job_next_run_humanized + attrs.asset_dbt_environment_name = obj.asset_dbt_environment_name + attrs.asset_dbt_environment_dbt_version = obj.asset_dbt_environment_dbt_version + attrs.asset_dbt_tags = obj.asset_dbt_tags + attrs.asset_dbt_semantic_layer_proxy_url = obj.asset_dbt_semantic_layer_proxy_url + attrs.asset_dbt_source_freshness_criteria = obj.asset_dbt_source_freshness_criteria + attrs.sample_data_url = obj.sample_data_url + attrs.asset_tags = obj.asset_tags + attrs.asset_mc_incident_names = obj.asset_mc_incident_names + attrs.asset_mc_incident_qualified_names = obj.asset_mc_incident_qualified_names + attrs.asset_mc_alert_qualified_names = obj.asset_mc_alert_qualified_names + attrs.asset_mc_monitor_names = obj.asset_mc_monitor_names + attrs.asset_mc_monitor_qualified_names = obj.asset_mc_monitor_qualified_names + attrs.asset_mc_monitor_statuses = obj.asset_mc_monitor_statuses + attrs.asset_mc_monitor_types = obj.asset_mc_monitor_types + attrs.asset_mc_monitor_schedule_types = obj.asset_mc_monitor_schedule_types + attrs.asset_mc_incident_types = obj.asset_mc_incident_types + attrs.asset_mc_incident_sub_types = obj.asset_mc_incident_sub_types + attrs.asset_mc_incident_severities = obj.asset_mc_incident_severities + attrs.asset_mc_incident_priorities = obj.asset_mc_incident_priorities + attrs.asset_mc_incident_states = obj.asset_mc_incident_states + attrs.asset_mc_is_monitored = obj.asset_mc_is_monitored + attrs.asset_mc_last_sync_run_at = obj.asset_mc_last_sync_run_at + attrs.starred_by = obj.starred_by + attrs.starred_details_list = obj.starred_details_list + attrs.starred_count = obj.starred_count + attrs.asset_anomalo_dq_status = obj.asset_anomalo_dq_status + attrs.asset_anomalo_check_count = obj.asset_anomalo_check_count + attrs.asset_anomalo_failed_check_count = obj.asset_anomalo_failed_check_count + attrs.asset_anomalo_check_statuses = obj.asset_anomalo_check_statuses + attrs.asset_anomalo_last_check_run_at = obj.asset_anomalo_last_check_run_at + attrs.asset_anomalo_applied_check_types = obj.asset_anomalo_applied_check_types + attrs.asset_anomalo_failed_check_types = obj.asset_anomalo_failed_check_types + attrs.asset_anomalo_source_url = obj.asset_anomalo_source_url + attrs.asset_soda_dq_status = obj.asset_soda_dq_status + attrs.asset_soda_check_count = obj.asset_soda_check_count + attrs.asset_soda_last_sync_run_at = obj.asset_soda_last_sync_run_at + attrs.asset_soda_last_scan_at = obj.asset_soda_last_scan_at + attrs.asset_soda_check_statuses = obj.asset_soda_check_statuses + attrs.asset_soda_source_url = obj.asset_soda_source_url + attrs.asset_icon = obj.asset_icon + attrs.asset_external_dq_metadata_details = obj.asset_external_dq_metadata_details + attrs.is_partial = obj.is_partial + attrs.is_ai_generated = obj.is_ai_generated + attrs.asset_cover_image = obj.asset_cover_image + attrs.asset_theme_hex = obj.asset_theme_hex + attrs.lexicographical_sort_order = obj.lexicographical_sort_order + attrs.has_contract = obj.has_contract + attrs.asset_redirect_guids = obj.asset_redirect_guids + attrs.asset_policy_guids = obj.asset_policy_guids + attrs.asset_policies_count = obj.asset_policies_count + attrs.domain_guids = obj.domain_guids + attrs.non_compliant_asset_policy_guids = obj.non_compliant_asset_policy_guids + attrs.product_guids = obj.product_guids + attrs.output_product_guids = obj.output_product_guids + attrs.application_qualified_name = obj.application_qualified_name + attrs.application_field_qualified_name = obj.application_field_qualified_name + attrs.asset_user_defined_type = obj.asset_user_defined_type + attrs.asset_internal_popularity_score = obj.asset_internal_popularity_score + attrs.asset_dq_schedule_type = obj.asset_dq_schedule_type + attrs.asset_dq_schedule_crontab = obj.asset_dq_schedule_crontab + attrs.asset_dq_schedule_time_zone = obj.asset_dq_schedule_time_zone + attrs.asset_dq_schedule_source_sync_status = ( + obj.asset_dq_schedule_source_sync_status + ) + attrs.asset_dq_schedule_source_synced_at = obj.asset_dq_schedule_source_synced_at + attrs.asset_dq_schedule_source_sync_error_message = ( + obj.asset_dq_schedule_source_sync_error_message + ) + attrs.asset_dq_schedule_source_sync_error_code = ( + obj.asset_dq_schedule_source_sync_error_code + ) + attrs.asset_dq_schedule_source_sync_raw_error = ( + obj.asset_dq_schedule_source_sync_raw_error + ) + attrs.asset_dq_rule_attached_dimensions = obj.asset_dq_rule_attached_dimensions + attrs.asset_dq_rule_failed_dimensions = obj.asset_dq_rule_failed_dimensions + attrs.asset_dq_rule_passed_dimensions = obj.asset_dq_rule_passed_dimensions + attrs.asset_dq_rule_attached_rule_types = obj.asset_dq_rule_attached_rule_types + attrs.asset_dq_rule_failed_rule_types = obj.asset_dq_rule_failed_rule_types + attrs.asset_dq_rule_passed_rule_types = obj.asset_dq_rule_passed_rule_types + attrs.asset_dq_rule_result_tags = obj.asset_dq_rule_result_tags + attrs.asset_dq_rule_last_run_at = obj.asset_dq_rule_last_run_at + attrs.asset_dq_manual_run_status = obj.asset_dq_manual_run_status + attrs.asset_dq_rule_total_count = obj.asset_dq_rule_total_count + attrs.asset_dq_rule_failed_count = obj.asset_dq_rule_failed_count + attrs.asset_dq_rule_passed_count = obj.asset_dq_rule_passed_count + attrs.asset_dq_result = obj.asset_dq_result + attrs.asset_dq_freshness_value = obj.asset_dq_freshness_value + attrs.asset_dq_freshness_expectation = obj.asset_dq_freshness_expectation + attrs.asset_dq_row_scope_filter_column_qualified_name = ( + obj.asset_dq_row_scope_filter_column_qualified_name + ) + attrs.asset_space_qualified_name = obj.asset_space_qualified_name + attrs.asset_space_name = obj.asset_space_name + attrs.asset_gcp_dataplex_metadata_details = obj.asset_gcp_dataplex_metadata_details + attrs.asset_gcp_dataplex_aspect_list = obj.asset_gcp_dataplex_aspect_list + attrs.asset_gcp_dataplex_aspect_field_list = ( + obj.asset_gcp_dataplex_aspect_field_list + ) + attrs.asset_smus_metadata_form_names = obj.asset_smus_metadata_form_names + attrs.asset_smus_metadata_form_key_value_details = ( + obj.asset_smus_metadata_form_key_value_details + ) + attrs.asset_smus_metadata_form_details = obj.asset_smus_metadata_form_details + + +def _extract_data_set_attrs(attrs: DataSetAttributes) -> dict: + """Extract all DataSet attributes from the attrs struct into a flat dict.""" + result = _extract_referenceable_attrs(attrs) + result["name"] = attrs.name + result["display_name"] = attrs.display_name + result["description"] = attrs.description + result["asset_source_readme"] = attrs.asset_source_readme + result["user_description"] = attrs.user_description + result["asset_ai_generated_description"] = attrs.asset_ai_generated_description + result["asset_ai_generated_description_confidence"] = ( + attrs.asset_ai_generated_description_confidence + ) + result["asset_ai_generated_description_reasoning"] = ( + attrs.asset_ai_generated_description_reasoning + ) + result["tenant_id"] = attrs.tenant_id + result["certificate_status"] = attrs.certificate_status + result["certificate_status_message"] = attrs.certificate_status_message + result["certificate_updated_by"] = attrs.certificate_updated_by + result["certificate_updated_at"] = attrs.certificate_updated_at + result["announcement_title"] = attrs.announcement_title + result["announcement_message"] = attrs.announcement_message + result["announcement_type"] = attrs.announcement_type + result["announcement_updated_at"] = attrs.announcement_updated_at + result["announcement_updated_by"] = attrs.announcement_updated_by + result["owner_users"] = attrs.owner_users + result["owner_groups"] = attrs.owner_groups + result["admin_users"] = attrs.admin_users + result["admin_groups"] = attrs.admin_groups + result["viewer_users"] = attrs.viewer_users + result["viewer_groups"] = attrs.viewer_groups + result["connector_name"] = attrs.connector_name + result["connection_name"] = attrs.connection_name + result["connection_qualified_name"] = attrs.connection_qualified_name + result["has_lineage"] = attrs.has_lineage + result["is_discoverable"] = attrs.is_discoverable + result["is_editable"] = attrs.is_editable + result["sub_type"] = attrs.sub_type + result["view_score"] = attrs.view_score + result["popularity_score"] = attrs.popularity_score + result["source_owners"] = attrs.source_owners + result["asset_source_id"] = attrs.asset_source_id + result["source_created_by"] = attrs.source_created_by + result["source_created_at"] = attrs.source_created_at + result["source_updated_at"] = attrs.source_updated_at + result["source_updated_by"] = attrs.source_updated_by + result["source_url"] = attrs.source_url + result["source_embed_url"] = attrs.source_embed_url + result["last_sync_workflow_name"] = attrs.last_sync_workflow_name + result["last_sync_run_at"] = attrs.last_sync_run_at + result["last_sync_run"] = attrs.last_sync_run + result["admin_roles"] = attrs.admin_roles + result["source_read_count"] = attrs.source_read_count + result["source_read_user_count"] = attrs.source_read_user_count + result["source_last_read_at"] = attrs.source_last_read_at + result["last_row_changed_at"] = attrs.last_row_changed_at + result["source_total_cost"] = attrs.source_total_cost + result["source_cost_unit"] = attrs.source_cost_unit + result["source_read_query_cost"] = attrs.source_read_query_cost + result["source_read_recent_user_list"] = attrs.source_read_recent_user_list + result["source_read_recent_user_record_list"] = ( + attrs.source_read_recent_user_record_list + ) + result["source_read_top_user_list"] = attrs.source_read_top_user_list + result["source_read_top_user_record_list"] = attrs.source_read_top_user_record_list + result["source_read_popular_query_record_list"] = ( + attrs.source_read_popular_query_record_list + ) + result["source_read_expensive_query_record_list"] = ( + attrs.source_read_expensive_query_record_list + ) + result["source_read_slow_query_record_list"] = ( + attrs.source_read_slow_query_record_list + ) + result["source_query_compute_cost_list"] = attrs.source_query_compute_cost_list + result["source_query_compute_cost_record_list"] = ( + attrs.source_query_compute_cost_record_list + ) + result["dbt_qualified_name"] = attrs.dbt_qualified_name + result["asset_dbt_workflow_last_updated"] = attrs.asset_dbt_workflow_last_updated + result["asset_dbt_alias"] = attrs.asset_dbt_alias + result["asset_dbt_meta"] = attrs.asset_dbt_meta + result["asset_dbt_unique_id"] = attrs.asset_dbt_unique_id + result["asset_dbt_account_name"] = attrs.asset_dbt_account_name + result["asset_dbt_project_name"] = attrs.asset_dbt_project_name + result["asset_dbt_package_name"] = attrs.asset_dbt_package_name + result["asset_dbt_job_name"] = attrs.asset_dbt_job_name + result["asset_dbt_job_schedule"] = attrs.asset_dbt_job_schedule + result["asset_dbt_job_status"] = attrs.asset_dbt_job_status + result["asset_dbt_test_status"] = attrs.asset_dbt_test_status + result["asset_dbt_job_schedule_cron_humanized"] = ( + attrs.asset_dbt_job_schedule_cron_humanized + ) + result["asset_dbt_job_last_run"] = attrs.asset_dbt_job_last_run + result["asset_dbt_job_last_run_url"] = attrs.asset_dbt_job_last_run_url + result["asset_dbt_job_last_run_created_at"] = ( + attrs.asset_dbt_job_last_run_created_at + ) + result["asset_dbt_job_last_run_updated_at"] = ( + attrs.asset_dbt_job_last_run_updated_at + ) + result["asset_dbt_job_last_run_dequed_at"] = attrs.asset_dbt_job_last_run_dequed_at + result["asset_dbt_job_last_run_started_at"] = ( + attrs.asset_dbt_job_last_run_started_at + ) + result["asset_dbt_job_last_run_total_duration"] = ( + attrs.asset_dbt_job_last_run_total_duration + ) + result["asset_dbt_job_last_run_total_duration_humanized"] = ( + attrs.asset_dbt_job_last_run_total_duration_humanized + ) + result["asset_dbt_job_last_run_queued_duration"] = ( + attrs.asset_dbt_job_last_run_queued_duration + ) + result["asset_dbt_job_last_run_queued_duration_humanized"] = ( + attrs.asset_dbt_job_last_run_queued_duration_humanized + ) + result["asset_dbt_job_last_run_run_duration"] = ( + attrs.asset_dbt_job_last_run_run_duration + ) + result["asset_dbt_job_last_run_run_duration_humanized"] = ( + attrs.asset_dbt_job_last_run_run_duration_humanized + ) + result["asset_dbt_job_last_run_git_branch"] = ( + attrs.asset_dbt_job_last_run_git_branch + ) + result["asset_dbt_job_last_run_git_sha"] = attrs.asset_dbt_job_last_run_git_sha + result["asset_dbt_job_last_run_status_message"] = ( + attrs.asset_dbt_job_last_run_status_message + ) + result["asset_dbt_job_last_run_owner_thread_id"] = ( + attrs.asset_dbt_job_last_run_owner_thread_id + ) + result["asset_dbt_job_last_run_executed_by_thread_id"] = ( + attrs.asset_dbt_job_last_run_executed_by_thread_id + ) + result["asset_dbt_job_last_run_artifacts_saved"] = ( + attrs.asset_dbt_job_last_run_artifacts_saved + ) + result["asset_dbt_job_last_run_artifact_s3_path"] = ( + attrs.asset_dbt_job_last_run_artifact_s3_path + ) + result["asset_dbt_job_last_run_has_docs_generated"] = ( + attrs.asset_dbt_job_last_run_has_docs_generated + ) + result["asset_dbt_job_last_run_has_sources_generated"] = ( + attrs.asset_dbt_job_last_run_has_sources_generated + ) + result["asset_dbt_job_last_run_notifications_sent"] = ( + attrs.asset_dbt_job_last_run_notifications_sent + ) + result["asset_dbt_job_next_run"] = attrs.asset_dbt_job_next_run + result["asset_dbt_job_next_run_humanized"] = attrs.asset_dbt_job_next_run_humanized + result["asset_dbt_environment_name"] = attrs.asset_dbt_environment_name + result["asset_dbt_environment_dbt_version"] = ( + attrs.asset_dbt_environment_dbt_version + ) + result["asset_dbt_tags"] = attrs.asset_dbt_tags + result["asset_dbt_semantic_layer_proxy_url"] = ( + attrs.asset_dbt_semantic_layer_proxy_url + ) + result["asset_dbt_source_freshness_criteria"] = ( + attrs.asset_dbt_source_freshness_criteria + ) + result["sample_data_url"] = attrs.sample_data_url + result["asset_tags"] = attrs.asset_tags + result["asset_mc_incident_names"] = attrs.asset_mc_incident_names + result["asset_mc_incident_qualified_names"] = ( + attrs.asset_mc_incident_qualified_names + ) + result["asset_mc_alert_qualified_names"] = attrs.asset_mc_alert_qualified_names + result["asset_mc_monitor_names"] = attrs.asset_mc_monitor_names + result["asset_mc_monitor_qualified_names"] = attrs.asset_mc_monitor_qualified_names + result["asset_mc_monitor_statuses"] = attrs.asset_mc_monitor_statuses + result["asset_mc_monitor_types"] = attrs.asset_mc_monitor_types + result["asset_mc_monitor_schedule_types"] = attrs.asset_mc_monitor_schedule_types + result["asset_mc_incident_types"] = attrs.asset_mc_incident_types + result["asset_mc_incident_sub_types"] = attrs.asset_mc_incident_sub_types + result["asset_mc_incident_severities"] = attrs.asset_mc_incident_severities + result["asset_mc_incident_priorities"] = attrs.asset_mc_incident_priorities + result["asset_mc_incident_states"] = attrs.asset_mc_incident_states + result["asset_mc_is_monitored"] = attrs.asset_mc_is_monitored + result["asset_mc_last_sync_run_at"] = attrs.asset_mc_last_sync_run_at + result["starred_by"] = attrs.starred_by + result["starred_details_list"] = attrs.starred_details_list + result["starred_count"] = attrs.starred_count + result["asset_anomalo_dq_status"] = attrs.asset_anomalo_dq_status + result["asset_anomalo_check_count"] = attrs.asset_anomalo_check_count + result["asset_anomalo_failed_check_count"] = attrs.asset_anomalo_failed_check_count + result["asset_anomalo_check_statuses"] = attrs.asset_anomalo_check_statuses + result["asset_anomalo_last_check_run_at"] = attrs.asset_anomalo_last_check_run_at + result["asset_anomalo_applied_check_types"] = ( + attrs.asset_anomalo_applied_check_types + ) + result["asset_anomalo_failed_check_types"] = attrs.asset_anomalo_failed_check_types + result["asset_anomalo_source_url"] = attrs.asset_anomalo_source_url + result["asset_soda_dq_status"] = attrs.asset_soda_dq_status + result["asset_soda_check_count"] = attrs.asset_soda_check_count + result["asset_soda_last_sync_run_at"] = attrs.asset_soda_last_sync_run_at + result["asset_soda_last_scan_at"] = attrs.asset_soda_last_scan_at + result["asset_soda_check_statuses"] = attrs.asset_soda_check_statuses + result["asset_soda_source_url"] = attrs.asset_soda_source_url + result["asset_icon"] = attrs.asset_icon + result["asset_external_dq_metadata_details"] = ( + attrs.asset_external_dq_metadata_details + ) + result["is_partial"] = attrs.is_partial + result["is_ai_generated"] = attrs.is_ai_generated + result["asset_cover_image"] = attrs.asset_cover_image + result["asset_theme_hex"] = attrs.asset_theme_hex + result["lexicographical_sort_order"] = attrs.lexicographical_sort_order + result["has_contract"] = attrs.has_contract + result["asset_redirect_guids"] = attrs.asset_redirect_guids + result["asset_policy_guids"] = attrs.asset_policy_guids + result["asset_policies_count"] = attrs.asset_policies_count + result["domain_guids"] = attrs.domain_guids + result["non_compliant_asset_policy_guids"] = attrs.non_compliant_asset_policy_guids + result["product_guids"] = attrs.product_guids + result["output_product_guids"] = attrs.output_product_guids + result["application_qualified_name"] = attrs.application_qualified_name + result["application_field_qualified_name"] = attrs.application_field_qualified_name + result["asset_user_defined_type"] = attrs.asset_user_defined_type + result["asset_internal_popularity_score"] = attrs.asset_internal_popularity_score + result["asset_dq_schedule_type"] = attrs.asset_dq_schedule_type + result["asset_dq_schedule_crontab"] = attrs.asset_dq_schedule_crontab + result["asset_dq_schedule_time_zone"] = attrs.asset_dq_schedule_time_zone + result["asset_dq_schedule_source_sync_status"] = ( + attrs.asset_dq_schedule_source_sync_status + ) + result["asset_dq_schedule_source_synced_at"] = ( + attrs.asset_dq_schedule_source_synced_at + ) + result["asset_dq_schedule_source_sync_error_message"] = ( + attrs.asset_dq_schedule_source_sync_error_message + ) + result["asset_dq_schedule_source_sync_error_code"] = ( + attrs.asset_dq_schedule_source_sync_error_code + ) + result["asset_dq_schedule_source_sync_raw_error"] = ( + attrs.asset_dq_schedule_source_sync_raw_error + ) + result["asset_dq_rule_attached_dimensions"] = ( + attrs.asset_dq_rule_attached_dimensions + ) + result["asset_dq_rule_failed_dimensions"] = attrs.asset_dq_rule_failed_dimensions + result["asset_dq_rule_passed_dimensions"] = attrs.asset_dq_rule_passed_dimensions + result["asset_dq_rule_attached_rule_types"] = ( + attrs.asset_dq_rule_attached_rule_types + ) + result["asset_dq_rule_failed_rule_types"] = attrs.asset_dq_rule_failed_rule_types + result["asset_dq_rule_passed_rule_types"] = attrs.asset_dq_rule_passed_rule_types + result["asset_dq_rule_result_tags"] = attrs.asset_dq_rule_result_tags + result["asset_dq_rule_last_run_at"] = attrs.asset_dq_rule_last_run_at + result["asset_dq_manual_run_status"] = attrs.asset_dq_manual_run_status + result["asset_dq_rule_total_count"] = attrs.asset_dq_rule_total_count + result["asset_dq_rule_failed_count"] = attrs.asset_dq_rule_failed_count + result["asset_dq_rule_passed_count"] = attrs.asset_dq_rule_passed_count + result["asset_dq_result"] = attrs.asset_dq_result + result["asset_dq_freshness_value"] = attrs.asset_dq_freshness_value + result["asset_dq_freshness_expectation"] = attrs.asset_dq_freshness_expectation + result["asset_dq_row_scope_filter_column_qualified_name"] = ( + attrs.asset_dq_row_scope_filter_column_qualified_name + ) + result["asset_space_qualified_name"] = attrs.asset_space_qualified_name + result["asset_space_name"] = attrs.asset_space_name + result["asset_gcp_dataplex_metadata_details"] = ( + attrs.asset_gcp_dataplex_metadata_details + ) + result["asset_gcp_dataplex_aspect_list"] = attrs.asset_gcp_dataplex_aspect_list + result["asset_gcp_dataplex_aspect_field_list"] = ( + attrs.asset_gcp_dataplex_aspect_field_list + ) + result["asset_smus_metadata_form_names"] = attrs.asset_smus_metadata_form_names + result["asset_smus_metadata_form_key_value_details"] = ( + attrs.asset_smus_metadata_form_key_value_details + ) + result["asset_smus_metadata_form_details"] = attrs.asset_smus_metadata_form_details + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _data_set_to_nested(data_set: DataSet) -> DataSetNested: + """Convert flat DataSet to nested format.""" + attrs = DataSetAttributes() + _populate_data_set_attrs(attrs, data_set) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + data_set, _DATA_SET_REL_FIELDS, DataSetRelationshipAttributes + ) + return DataSetNested( + guid=data_set.guid, + type_name=data_set.type_name, + status=data_set.status, + version=data_set.version, + create_time=data_set.create_time, + update_time=data_set.update_time, + created_by=data_set.created_by, + updated_by=data_set.updated_by, + classifications=data_set.classifications, + classification_names=data_set.classification_names, + meanings=data_set.meanings, + labels=data_set.labels, + business_attributes=data_set.business_attributes, + custom_attributes=data_set.custom_attributes, + pending_tasks=data_set.pending_tasks, + proxy=data_set.proxy, + is_incomplete=data_set.is_incomplete, + provenance_type=data_set.provenance_type, + home_id=data_set.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _data_set_from_nested(nested: DataSetNested) -> DataSet: + """Convert nested format to flat DataSet.""" + attrs = nested.attributes if nested.attributes is not UNSET else DataSetAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DATA_SET_REL_FIELDS, + DataSetRelationshipAttributes, + ) + return DataSet( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_data_set_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _data_set_to_nested_bytes(data_set: DataSet, serde: Serde) -> bytes: + """Convert flat DataSet to nested JSON bytes.""" + return serde.encode(_data_set_to_nested(data_set)) + + +def _data_set_from_nested_bytes(data: bytes, serde: Serde) -> DataSet: + """Convert nested JSON bytes to flat DataSet.""" + nested = serde.decode(data, DataSetNested) + return _data_set_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + NumericRankField, + RelationField, + TextField, +) + +DataSet.NAME = KeywordField("name", "name") +DataSet.DISPLAY_NAME = KeywordField("displayName", "displayName") +DataSet.DESCRIPTION = KeywordField("description", "description") +DataSet.ASSET_SOURCE_README = KeywordTextField( + "assetSourceReadme", "assetSourceReadme", "assetSourceReadme.text" +) +DataSet.USER_DESCRIPTION = KeywordField("userDescription", "userDescription") +DataSet.ASSET_AI_GENERATED_DESCRIPTION = TextField( + "assetAiGeneratedDescription", "assetAiGeneratedDescription" +) +DataSet.ASSET_AI_GENERATED_DESCRIPTION_CONFIDENCE = NumericField( + "assetAiGeneratedDescriptionConfidence", "assetAiGeneratedDescriptionConfidence" +) +DataSet.ASSET_AI_GENERATED_DESCRIPTION_REASONING = KeywordField( + "assetAiGeneratedDescriptionReasoning", "assetAiGeneratedDescriptionReasoning" +) +DataSet.TENANT_ID = KeywordField("tenantId", "tenantId") +DataSet.CERTIFICATE_STATUS = KeywordTextField( + "certificateStatus", "certificateStatus", "certificateStatus.text" +) +DataSet.CERTIFICATE_STATUS_MESSAGE = KeywordField( + "certificateStatusMessage", "certificateStatusMessage" +) +DataSet.CERTIFICATE_UPDATED_BY = KeywordField( + "certificateUpdatedBy", "certificateUpdatedBy" +) +DataSet.CERTIFICATE_UPDATED_AT = NumericField( + "certificateUpdatedAt", "certificateUpdatedAt" +) +DataSet.ANNOUNCEMENT_TITLE = KeywordField("announcementTitle", "announcementTitle") +DataSet.ANNOUNCEMENT_MESSAGE = KeywordField( + "announcementMessage", "announcementMessage" +) +DataSet.ANNOUNCEMENT_TYPE = KeywordField("announcementType", "announcementType") +DataSet.ANNOUNCEMENT_UPDATED_AT = NumericField( + "announcementUpdatedAt", "announcementUpdatedAt" +) +DataSet.ANNOUNCEMENT_UPDATED_BY = KeywordField( + "announcementUpdatedBy", "announcementUpdatedBy" +) +DataSet.OWNER_USERS = KeywordField("ownerUsers", "ownerUsers") +DataSet.OWNER_GROUPS = KeywordField("ownerGroups", "ownerGroups") +DataSet.ADMIN_USERS = KeywordField("adminUsers", "adminUsers") +DataSet.ADMIN_GROUPS = KeywordField("adminGroups", "adminGroups") +DataSet.VIEWER_USERS = KeywordField("viewerUsers", "viewerUsers") +DataSet.VIEWER_GROUPS = KeywordField("viewerGroups", "viewerGroups") +DataSet.CONNECTOR_NAME = KeywordField("connectorName", "connectorName") +DataSet.CONNECTION_NAME = KeywordTextField( + "connectionName", "connectionName", "connectionName.text" +) +DataSet.CONNECTION_QUALIFIED_NAME = KeywordTextField( + "connectionQualifiedName", "connectionQualifiedName", "connectionQualifiedName.text" +) +DataSet.HAS_LINEAGE = BooleanField("__hasLineage", "__hasLineage") +DataSet.IS_DISCOVERABLE = BooleanField("isDiscoverable", "isDiscoverable") +DataSet.IS_EDITABLE = BooleanField("isEditable", "isEditable") +DataSet.SUB_TYPE = KeywordField("subType", "subType") +DataSet.VIEW_SCORE = NumericField("viewScore", "viewScore") +DataSet.POPULARITY_SCORE = NumericField("popularityScore", "popularityScore") +DataSet.SOURCE_OWNERS = KeywordField("sourceOwners", "sourceOwners") +DataSet.ASSET_SOURCE_ID = KeywordField("assetSourceId", "assetSourceId") +DataSet.SOURCE_CREATED_BY = KeywordField("sourceCreatedBy", "sourceCreatedBy") +DataSet.SOURCE_CREATED_AT = NumericField("sourceCreatedAt", "sourceCreatedAt") +DataSet.SOURCE_UPDATED_AT = NumericField("sourceUpdatedAt", "sourceUpdatedAt") +DataSet.SOURCE_UPDATED_BY = KeywordField("sourceUpdatedBy", "sourceUpdatedBy") +DataSet.SOURCE_URL = KeywordField("sourceURL", "sourceURL") +DataSet.SOURCE_EMBED_URL = KeywordField("sourceEmbedURL", "sourceEmbedURL") +DataSet.LAST_SYNC_WORKFLOW_NAME = KeywordField( + "lastSyncWorkflowName", "lastSyncWorkflowName" +) +DataSet.LAST_SYNC_RUN_AT = NumericField("lastSyncRunAt", "lastSyncRunAt") +DataSet.LAST_SYNC_RUN = KeywordField("lastSyncRun", "lastSyncRun") +DataSet.ADMIN_ROLES = KeywordField("adminRoles", "adminRoles") +DataSet.SOURCE_READ_COUNT = NumericField("sourceReadCount", "sourceReadCount") +DataSet.SOURCE_READ_USER_COUNT = NumericField( + "sourceReadUserCount", "sourceReadUserCount" +) +DataSet.SOURCE_LAST_READ_AT = NumericField("sourceLastReadAt", "sourceLastReadAt") +DataSet.LAST_ROW_CHANGED_AT = NumericField("lastRowChangedAt", "lastRowChangedAt") +DataSet.SOURCE_TOTAL_COST = NumericField("sourceTotalCost", "sourceTotalCost") +DataSet.SOURCE_COST_UNIT = KeywordField("sourceCostUnit", "sourceCostUnit") +DataSet.SOURCE_READ_QUERY_COST = NumericField( + "sourceReadQueryCost", "sourceReadQueryCost" +) +DataSet.SOURCE_READ_RECENT_USER_LIST = KeywordField( + "sourceReadRecentUserList", "sourceReadRecentUserList" +) +DataSet.SOURCE_READ_RECENT_USER_RECORD_LIST = KeywordField( + "sourceReadRecentUserRecordList", "sourceReadRecentUserRecordList" +) +DataSet.SOURCE_READ_TOP_USER_LIST = KeywordField( + "sourceReadTopUserList", "sourceReadTopUserList" +) +DataSet.SOURCE_READ_TOP_USER_RECORD_LIST = KeywordField( + "sourceReadTopUserRecordList", "sourceReadTopUserRecordList" +) +DataSet.SOURCE_READ_POPULAR_QUERY_RECORD_LIST = KeywordField( + "sourceReadPopularQueryRecordList", "sourceReadPopularQueryRecordList" +) +DataSet.SOURCE_READ_EXPENSIVE_QUERY_RECORD_LIST = KeywordField( + "sourceReadExpensiveQueryRecordList", "sourceReadExpensiveQueryRecordList" +) +DataSet.SOURCE_READ_SLOW_QUERY_RECORD_LIST = KeywordField( + "sourceReadSlowQueryRecordList", "sourceReadSlowQueryRecordList" +) +DataSet.SOURCE_QUERY_COMPUTE_COST_LIST = KeywordField( + "sourceQueryComputeCostList", "sourceQueryComputeCostList" +) +DataSet.SOURCE_QUERY_COMPUTE_COST_RECORD_LIST = KeywordField( + "sourceQueryComputeCostRecordList", "sourceQueryComputeCostRecordList" +) +DataSet.DBT_QUALIFIED_NAME = KeywordTextField( + "dbtQualifiedName", "dbtQualifiedName", "dbtQualifiedName.text" +) +DataSet.ASSET_DBT_WORKFLOW_LAST_UPDATED = KeywordField( + "assetDbtWorkflowLastUpdated", "assetDbtWorkflowLastUpdated" +) +DataSet.ASSET_DBT_ALIAS = KeywordField("assetDbtAlias", "assetDbtAlias") +DataSet.ASSET_DBT_META = KeywordField("assetDbtMeta", "assetDbtMeta") +DataSet.ASSET_DBT_UNIQUE_ID = KeywordField("assetDbtUniqueId", "assetDbtUniqueId") +DataSet.ASSET_DBT_ACCOUNT_NAME = KeywordField( + "assetDbtAccountName", "assetDbtAccountName" +) +DataSet.ASSET_DBT_PROJECT_NAME = KeywordField( + "assetDbtProjectName", "assetDbtProjectName" +) +DataSet.ASSET_DBT_PACKAGE_NAME = KeywordField( + "assetDbtPackageName", "assetDbtPackageName" +) +DataSet.ASSET_DBT_JOB_NAME = KeywordField("assetDbtJobName", "assetDbtJobName") +DataSet.ASSET_DBT_JOB_SCHEDULE = KeywordField( + "assetDbtJobSchedule", "assetDbtJobSchedule" +) +DataSet.ASSET_DBT_JOB_STATUS = KeywordField("assetDbtJobStatus", "assetDbtJobStatus") +DataSet.ASSET_DBT_TEST_STATUS = KeywordField("assetDbtTestStatus", "assetDbtTestStatus") +DataSet.ASSET_DBT_JOB_SCHEDULE_CRON_HUMANIZED = KeywordField( + "assetDbtJobScheduleCronHumanized", "assetDbtJobScheduleCronHumanized" +) +DataSet.ASSET_DBT_JOB_LAST_RUN = NumericField( + "assetDbtJobLastRun", "assetDbtJobLastRun" +) +DataSet.ASSET_DBT_JOB_LAST_RUN_URL = KeywordField( + "assetDbtJobLastRunUrl", "assetDbtJobLastRunUrl" +) +DataSet.ASSET_DBT_JOB_LAST_RUN_CREATED_AT = NumericField( + "assetDbtJobLastRunCreatedAt", "assetDbtJobLastRunCreatedAt" +) +DataSet.ASSET_DBT_JOB_LAST_RUN_UPDATED_AT = NumericField( + "assetDbtJobLastRunUpdatedAt", "assetDbtJobLastRunUpdatedAt" +) +DataSet.ASSET_DBT_JOB_LAST_RUN_DEQUED_AT = NumericField( + "assetDbtJobLastRunDequedAt", "assetDbtJobLastRunDequedAt" +) +DataSet.ASSET_DBT_JOB_LAST_RUN_STARTED_AT = NumericField( + "assetDbtJobLastRunStartedAt", "assetDbtJobLastRunStartedAt" +) +DataSet.ASSET_DBT_JOB_LAST_RUN_TOTAL_DURATION = KeywordField( + "assetDbtJobLastRunTotalDuration", "assetDbtJobLastRunTotalDuration" +) +DataSet.ASSET_DBT_JOB_LAST_RUN_TOTAL_DURATION_HUMANIZED = KeywordField( + "assetDbtJobLastRunTotalDurationHumanized", + "assetDbtJobLastRunTotalDurationHumanized", +) +DataSet.ASSET_DBT_JOB_LAST_RUN_QUEUED_DURATION = KeywordField( + "assetDbtJobLastRunQueuedDuration", "assetDbtJobLastRunQueuedDuration" +) +DataSet.ASSET_DBT_JOB_LAST_RUN_QUEUED_DURATION_HUMANIZED = KeywordField( + "assetDbtJobLastRunQueuedDurationHumanized", + "assetDbtJobLastRunQueuedDurationHumanized", +) +DataSet.ASSET_DBT_JOB_LAST_RUN_RUN_DURATION = KeywordField( + "assetDbtJobLastRunRunDuration", "assetDbtJobLastRunRunDuration" +) +DataSet.ASSET_DBT_JOB_LAST_RUN_RUN_DURATION_HUMANIZED = KeywordField( + "assetDbtJobLastRunRunDurationHumanized", "assetDbtJobLastRunRunDurationHumanized" +) +DataSet.ASSET_DBT_JOB_LAST_RUN_GIT_BRANCH = KeywordTextField( + "assetDbtJobLastRunGitBranch", + "assetDbtJobLastRunGitBranch", + "assetDbtJobLastRunGitBranch.text", +) +DataSet.ASSET_DBT_JOB_LAST_RUN_GIT_SHA = KeywordField( + "assetDbtJobLastRunGitSha", "assetDbtJobLastRunGitSha" +) +DataSet.ASSET_DBT_JOB_LAST_RUN_STATUS_MESSAGE = KeywordField( + "assetDbtJobLastRunStatusMessage", "assetDbtJobLastRunStatusMessage" +) +DataSet.ASSET_DBT_JOB_LAST_RUN_OWNER_THREAD_ID = KeywordField( + "assetDbtJobLastRunOwnerThreadId", "assetDbtJobLastRunOwnerThreadId" +) +DataSet.ASSET_DBT_JOB_LAST_RUN_EXECUTED_BY_THREAD_ID = KeywordField( + "assetDbtJobLastRunExecutedByThreadId", "assetDbtJobLastRunExecutedByThreadId" +) +DataSet.ASSET_DBT_JOB_LAST_RUN_ARTIFACTS_SAVED = BooleanField( + "assetDbtJobLastRunArtifactsSaved", "assetDbtJobLastRunArtifactsSaved" +) +DataSet.ASSET_DBT_JOB_LAST_RUN_ARTIFACT_S3_PATH = KeywordField( + "assetDbtJobLastRunArtifactS3Path", "assetDbtJobLastRunArtifactS3Path" +) +DataSet.ASSET_DBT_JOB_LAST_RUN_HAS_DOCS_GENERATED = BooleanField( + "assetDbtJobLastRunHasDocsGenerated", "assetDbtJobLastRunHasDocsGenerated" +) +DataSet.ASSET_DBT_JOB_LAST_RUN_HAS_SOURCES_GENERATED = BooleanField( + "assetDbtJobLastRunHasSourcesGenerated", "assetDbtJobLastRunHasSourcesGenerated" +) +DataSet.ASSET_DBT_JOB_LAST_RUN_NOTIFICATIONS_SENT = BooleanField( + "assetDbtJobLastRunNotificationsSent", "assetDbtJobLastRunNotificationsSent" +) +DataSet.ASSET_DBT_JOB_NEXT_RUN = NumericField( + "assetDbtJobNextRun", "assetDbtJobNextRun" +) +DataSet.ASSET_DBT_JOB_NEXT_RUN_HUMANIZED = KeywordField( + "assetDbtJobNextRunHumanized", "assetDbtJobNextRunHumanized" +) +DataSet.ASSET_DBT_ENVIRONMENT_NAME = KeywordField( + "assetDbtEnvironmentName", "assetDbtEnvironmentName" +) +DataSet.ASSET_DBT_ENVIRONMENT_DBT_VERSION = KeywordField( + "assetDbtEnvironmentDbtVersion", "assetDbtEnvironmentDbtVersion" +) +DataSet.ASSET_DBT_TAGS = KeywordTextField( + "assetDbtTags", "assetDbtTags", "assetDbtTags.text" +) +DataSet.ASSET_DBT_SEMANTIC_LAYER_PROXY_URL = KeywordField( + "assetDbtSemanticLayerProxyUrl", "assetDbtSemanticLayerProxyUrl" +) +DataSet.ASSET_DBT_SOURCE_FRESHNESS_CRITERIA = KeywordField( + "assetDbtSourceFreshnessCriteria", "assetDbtSourceFreshnessCriteria" +) +DataSet.SAMPLE_DATA_URL = KeywordTextField( + "sampleDataUrl", "sampleDataUrl", "sampleDataUrl.text" +) +DataSet.ASSET_TAGS = KeywordTextField("assetTags", "assetTags", "assetTags.text") +DataSet.ASSET_MC_INCIDENT_NAMES = KeywordField( + "assetMcIncidentNames", "assetMcIncidentNames" +) +DataSet.ASSET_MC_INCIDENT_QUALIFIED_NAMES = KeywordTextField( + "assetMcIncidentQualifiedNames", + "assetMcIncidentQualifiedNames", + "assetMcIncidentQualifiedNames.text", +) +DataSet.ASSET_MC_ALERT_QUALIFIED_NAMES = KeywordTextField( + "assetMcAlertQualifiedNames", + "assetMcAlertQualifiedNames", + "assetMcAlertQualifiedNames.text", +) +DataSet.ASSET_MC_MONITOR_NAMES = KeywordField( + "assetMcMonitorNames", "assetMcMonitorNames" +) +DataSet.ASSET_MC_MONITOR_QUALIFIED_NAMES = KeywordTextField( + "assetMcMonitorQualifiedNames", + "assetMcMonitorQualifiedNames", + "assetMcMonitorQualifiedNames.text", +) +DataSet.ASSET_MC_MONITOR_STATUSES = KeywordField( + "assetMcMonitorStatuses", "assetMcMonitorStatuses" +) +DataSet.ASSET_MC_MONITOR_TYPES = KeywordField( + "assetMcMonitorTypes", "assetMcMonitorTypes" +) +DataSet.ASSET_MC_MONITOR_SCHEDULE_TYPES = KeywordField( + "assetMcMonitorScheduleTypes", "assetMcMonitorScheduleTypes" +) +DataSet.ASSET_MC_INCIDENT_TYPES = KeywordField( + "assetMcIncidentTypes", "assetMcIncidentTypes" +) +DataSet.ASSET_MC_INCIDENT_SUB_TYPES = KeywordField( + "assetMcIncidentSubTypes", "assetMcIncidentSubTypes" +) +DataSet.ASSET_MC_INCIDENT_SEVERITIES = KeywordField( + "assetMcIncidentSeverities", "assetMcIncidentSeverities" +) +DataSet.ASSET_MC_INCIDENT_PRIORITIES = KeywordField( + "assetMcIncidentPriorities", "assetMcIncidentPriorities" +) +DataSet.ASSET_MC_INCIDENT_STATES = KeywordField( + "assetMcIncidentStates", "assetMcIncidentStates" +) +DataSet.ASSET_MC_IS_MONITORED = BooleanField("assetMcIsMonitored", "assetMcIsMonitored") +DataSet.ASSET_MC_LAST_SYNC_RUN_AT = NumericField( + "assetMcLastSyncRunAt", "assetMcLastSyncRunAt" +) +DataSet.STARRED_BY = KeywordField("starredBy", "starredBy") +DataSet.STARRED_DETAILS_LIST = KeywordField("starredDetailsList", "starredDetailsList") +DataSet.STARRED_COUNT = NumericField("starredCount", "starredCount") +DataSet.ASSET_ANOMALO_DQ_STATUS = KeywordField( + "assetAnomaloDQStatus", "assetAnomaloDQStatus" +) +DataSet.ASSET_ANOMALO_CHECK_COUNT = NumericField( + "assetAnomaloCheckCount", "assetAnomaloCheckCount" +) +DataSet.ASSET_ANOMALO_FAILED_CHECK_COUNT = NumericField( + "assetAnomaloFailedCheckCount", "assetAnomaloFailedCheckCount" +) +DataSet.ASSET_ANOMALO_CHECK_STATUSES = KeywordField( + "assetAnomaloCheckStatuses", "assetAnomaloCheckStatuses" +) +DataSet.ASSET_ANOMALO_LAST_CHECK_RUN_AT = NumericField( + "assetAnomaloLastCheckRunAt", "assetAnomaloLastCheckRunAt" +) +DataSet.ASSET_ANOMALO_APPLIED_CHECK_TYPES = KeywordField( + "assetAnomaloAppliedCheckTypes", "assetAnomaloAppliedCheckTypes" +) +DataSet.ASSET_ANOMALO_FAILED_CHECK_TYPES = KeywordField( + "assetAnomaloFailedCheckTypes", "assetAnomaloFailedCheckTypes" +) +DataSet.ASSET_ANOMALO_SOURCE_URL = KeywordField( + "assetAnomaloSourceUrl", "assetAnomaloSourceUrl" +) +DataSet.ASSET_SODA_DQ_STATUS = KeywordField("assetSodaDQStatus", "assetSodaDQStatus") +DataSet.ASSET_SODA_CHECK_COUNT = NumericField( + "assetSodaCheckCount", "assetSodaCheckCount" +) +DataSet.ASSET_SODA_LAST_SYNC_RUN_AT = NumericField( + "assetSodaLastSyncRunAt", "assetSodaLastSyncRunAt" +) +DataSet.ASSET_SODA_LAST_SCAN_AT = NumericField( + "assetSodaLastScanAt", "assetSodaLastScanAt" +) +DataSet.ASSET_SODA_CHECK_STATUSES = KeywordField( + "assetSodaCheckStatuses", "assetSodaCheckStatuses" +) +DataSet.ASSET_SODA_SOURCE_URL = KeywordField("assetSodaSourceURL", "assetSodaSourceURL") +DataSet.ASSET_ICON = KeywordField("assetIcon", "assetIcon") +DataSet.ASSET_EXTERNAL_DQ_METADATA_DETAILS = KeywordField( + "assetExternalDQMetadataDetails", "assetExternalDQMetadataDetails" +) +DataSet.IS_PARTIAL = BooleanField("isPartial", "isPartial") +DataSet.IS_AI_GENERATED = BooleanField("isAIGenerated", "isAIGenerated") +DataSet.ASSET_COVER_IMAGE = KeywordField("assetCoverImage", "assetCoverImage") +DataSet.ASSET_THEME_HEX = KeywordField("assetThemeHex", "assetThemeHex") +DataSet.LEXICOGRAPHICAL_SORT_ORDER = KeywordField( + "lexicographicalSortOrder", "lexicographicalSortOrder" +) +DataSet.HAS_CONTRACT = BooleanField("hasContract", "hasContract") +DataSet.ASSET_REDIRECT_GUIDS = KeywordField("assetRedirectGUIDs", "assetRedirectGUIDs") +DataSet.ASSET_POLICY_GUIDS = KeywordField("assetPolicyGUIDs", "assetPolicyGUIDs") +DataSet.ASSET_POLICIES_COUNT = NumericField("assetPoliciesCount", "assetPoliciesCount") +DataSet.DOMAIN_GUIDS = KeywordField("domainGUIDs", "domainGUIDs") +DataSet.NON_COMPLIANT_ASSET_POLICY_GUIDS = KeywordField( + "nonCompliantAssetPolicyGUIDs", "nonCompliantAssetPolicyGUIDs" +) +DataSet.PRODUCT_GUIDS = KeywordField("productGUIDs", "productGUIDs") +DataSet.OUTPUT_PRODUCT_GUIDS = KeywordField("outputProductGUIDs", "outputProductGUIDs") +DataSet.APPLICATION_QUALIFIED_NAME = KeywordField( + "applicationQualifiedName", "applicationQualifiedName" +) +DataSet.APPLICATION_FIELD_QUALIFIED_NAME = KeywordField( + "applicationFieldQualifiedName", "applicationFieldQualifiedName" +) +DataSet.ASSET_USER_DEFINED_TYPE = KeywordField( + "assetUserDefinedType", "assetUserDefinedType" +) +DataSet.ASSET_INTERNAL_POPULARITY_SCORE = NumericRankField( + "assetInternalPopularityScore", + "assetInternalPopularityScore", + "assetInternalPopularityScore.rank", +) +DataSet.ASSET_DQ_SCHEDULE_TYPE = KeywordField( + "assetDQScheduleType", "assetDQScheduleType" +) +DataSet.ASSET_DQ_SCHEDULE_CRONTAB = KeywordField( + "assetDQScheduleCrontab", "assetDQScheduleCrontab" +) +DataSet.ASSET_DQ_SCHEDULE_TIME_ZONE = KeywordField( + "assetDQScheduleTimeZone", "assetDQScheduleTimeZone" +) +DataSet.ASSET_DQ_SCHEDULE_SOURCE_SYNC_STATUS = KeywordField( + "assetDQScheduleSourceSyncStatus", "assetDQScheduleSourceSyncStatus" +) +DataSet.ASSET_DQ_SCHEDULE_SOURCE_SYNCED_AT = NumericField( + "assetDQScheduleSourceSyncedAt", "assetDQScheduleSourceSyncedAt" +) +DataSet.ASSET_DQ_SCHEDULE_SOURCE_SYNC_ERROR_MESSAGE = TextField( + "assetDQScheduleSourceSyncErrorMessage", "assetDQScheduleSourceSyncErrorMessage" +) +DataSet.ASSET_DQ_SCHEDULE_SOURCE_SYNC_ERROR_CODE = KeywordField( + "assetDQScheduleSourceSyncErrorCode", "assetDQScheduleSourceSyncErrorCode" +) +DataSet.ASSET_DQ_SCHEDULE_SOURCE_SYNC_RAW_ERROR = TextField( + "assetDQScheduleSourceSyncRawError", "assetDQScheduleSourceSyncRawError" +) +DataSet.ASSET_DQ_RULE_ATTACHED_DIMENSIONS = KeywordField( + "assetDQRuleAttachedDimensions", "assetDQRuleAttachedDimensions" +) +DataSet.ASSET_DQ_RULE_FAILED_DIMENSIONS = KeywordField( + "assetDQRuleFailedDimensions", "assetDQRuleFailedDimensions" +) +DataSet.ASSET_DQ_RULE_PASSED_DIMENSIONS = KeywordField( + "assetDQRulePassedDimensions", "assetDQRulePassedDimensions" +) +DataSet.ASSET_DQ_RULE_ATTACHED_RULE_TYPES = KeywordField( + "assetDQRuleAttachedRuleTypes", "assetDQRuleAttachedRuleTypes" +) +DataSet.ASSET_DQ_RULE_FAILED_RULE_TYPES = KeywordField( + "assetDQRuleFailedRuleTypes", "assetDQRuleFailedRuleTypes" +) +DataSet.ASSET_DQ_RULE_PASSED_RULE_TYPES = KeywordField( + "assetDQRulePassedRuleTypes", "assetDQRulePassedRuleTypes" +) +DataSet.ASSET_DQ_RULE_RESULT_TAGS = KeywordField( + "assetDQRuleResultTags", "assetDQRuleResultTags" +) +DataSet.ASSET_DQ_RULE_LAST_RUN_AT = NumericField( + "assetDQRuleLastRunAt", "assetDQRuleLastRunAt" +) +DataSet.ASSET_DQ_MANUAL_RUN_STATUS = KeywordField( + "assetDQManualRunStatus", "assetDQManualRunStatus" +) +DataSet.ASSET_DQ_RULE_TOTAL_COUNT = NumericField( + "assetDQRuleTotalCount", "assetDQRuleTotalCount" +) +DataSet.ASSET_DQ_RULE_FAILED_COUNT = NumericField( + "assetDQRuleFailedCount", "assetDQRuleFailedCount" +) +DataSet.ASSET_DQ_RULE_PASSED_COUNT = NumericField( + "assetDQRulePassedCount", "assetDQRulePassedCount" +) +DataSet.ASSET_DQ_RESULT = KeywordField("assetDQResult", "assetDQResult") +DataSet.ASSET_DQ_FRESHNESS_VALUE = NumericField( + "assetDQFreshnessValue", "assetDQFreshnessValue" +) +DataSet.ASSET_DQ_FRESHNESS_EXPECTATION = NumericField( + "assetDQFreshnessExpectation", "assetDQFreshnessExpectation" +) +DataSet.ASSET_DQ_ROW_SCOPE_FILTER_COLUMN_QUALIFIED_NAME = KeywordField( + "assetDQRowScopeFilterColumnQualifiedName", + "assetDQRowScopeFilterColumnQualifiedName", +) +DataSet.ASSET_SPACE_QUALIFIED_NAME = KeywordField( + "assetSpaceQualifiedName", "assetSpaceQualifiedName" +) +DataSet.ASSET_SPACE_NAME = KeywordField("assetSpaceName", "assetSpaceName") +DataSet.ASSET_GCP_DATAPLEX_METADATA_DETAILS = KeywordField( + "assetGCPDataplexMetadataDetails", "assetGCPDataplexMetadataDetails" +) +DataSet.ASSET_GCP_DATAPLEX_ASPECT_LIST = KeywordField( + "assetGCPDataplexAspectList", "assetGCPDataplexAspectList" +) +DataSet.ASSET_GCP_DATAPLEX_ASPECT_FIELD_LIST = KeywordField( + "assetGCPDataplexAspectFieldList", "assetGCPDataplexAspectFieldList" +) +DataSet.ASSET_SMUS_METADATA_FORM_NAMES = KeywordTextField( + "assetSmusMetadataFormNames", + "assetSmusMetadataFormNames", + "assetSmusMetadataFormNames.text", +) +DataSet.ASSET_SMUS_METADATA_FORM_KEY_VALUE_DETAILS = KeywordTextField( + "assetSmusMetadataFormKeyValueDetails", + "assetSmusMetadataFormKeyValueDetails", + "assetSmusMetadataFormKeyValueDetails.text", +) +DataSet.ASSET_SMUS_METADATA_FORM_DETAILS = KeywordField( + "assetSmusMetadataFormDetails", "assetSmusMetadataFormDetails" +) +DataSet.ANOMALO_CHECKS = RelationField("anomaloChecks") +DataSet.APPLICATION = RelationField("application") +DataSet.APPLICATION_FIELD = RelationField("applicationField") +DataSet.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +DataSet.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +DataSet.METRICS = RelationField("metrics") +DataSet.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +DataSet.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +DataSet.MEANINGS = RelationField("meanings") +DataSet.MC_MONITORS = RelationField("mcMonitors") +DataSet.MC_INCIDENTS = RelationField("mcIncidents") +DataSet.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +DataSet.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +DataSet.FILES = RelationField("files") +DataSet.LINKS = RelationField("links") +DataSet.README = RelationField("readme") +DataSet.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +DataSet.SODA_CHECKS = RelationField("sodaChecks") diff --git a/pyatlan_v9/model/assets/data_studio.py b/pyatlan_v9/model/assets/data_studio.py new file mode 100644 index 000000000..a9523f9ca --- /dev/null +++ b/pyatlan_v9/model/assets/data_studio.py @@ -0,0 +1,629 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DataStudio asset model with flattened inheritance. + +This module provides: +- DataStudio: Flat asset class (easy to use) +- DataStudioAttributes: Nested attributes struct (extends AssetAttributes) +- DataStudioNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class DataStudio(Asset): + """ + Base class for Google Data Studio assets. + """ + + GOOGLE_SERVICE: ClassVar[Any] = None + GOOGLE_PROJECT_NAME: ClassVar[Any] = None + GOOGLE_PROJECT_ID: ClassVar[Any] = None + GOOGLE_PROJECT_NUMBER: ClassVar[Any] = None + GOOGLE_LOCATION: ClassVar[Any] = None + GOOGLE_LOCATION_TYPE: ClassVar[Any] = None + GOOGLE_LABELS: ClassVar[Any] = None + GOOGLE_TAGS: ClassVar[Any] = None + CLOUD_UNIFORM_RESOURCE_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "DataStudio" + + google_service: Union[str, None, UnsetType] = UNSET + """Service in Google in which the asset exists.""" + + google_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which the asset exists.""" + + google_project_id: Union[str, None, UnsetType] = UNSET + """ID of the project in which the asset exists.""" + + google_project_number: Union[int, None, UnsetType] = UNSET + """Number of the project in which the asset exists.""" + + google_location: Union[str, None, UnsetType] = UNSET + """Location of this asset in Google.""" + + google_location_type: Union[str, None, UnsetType] = UNSET + """Type of location of this asset in Google.""" + + google_labels: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of labels that have been applied to the asset in Google.""" + + google_tags: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of tags that have been applied to the asset in Google.""" + + cloud_uniform_resource_name: Union[str, None, UnsetType] = UNSET + """Uniform resource name (URN) for the asset: AWS ARN, Google Cloud URI, Azure resource ID, Oracle OCID, and so on.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "DataStudio" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _data_studio_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> DataStudio: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + DataStudio instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _data_studio_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DataStudioAttributes(AssetAttributes): + """DataStudio-specific attributes for nested API format.""" + + google_service: Union[str, None, UnsetType] = UNSET + """Service in Google in which the asset exists.""" + + google_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which the asset exists.""" + + google_project_id: Union[str, None, UnsetType] = UNSET + """ID of the project in which the asset exists.""" + + google_project_number: Union[int, None, UnsetType] = UNSET + """Number of the project in which the asset exists.""" + + google_location: Union[str, None, UnsetType] = UNSET + """Location of this asset in Google.""" + + google_location_type: Union[str, None, UnsetType] = UNSET + """Type of location of this asset in Google.""" + + google_labels: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of labels that have been applied to the asset in Google.""" + + google_tags: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of tags that have been applied to the asset in Google.""" + + cloud_uniform_resource_name: Union[str, None, UnsetType] = UNSET + """Uniform resource name (URN) for the asset: AWS ARN, Google Cloud URI, Azure resource ID, Oracle OCID, and so on.""" + + +class DataStudioRelationshipAttributes(AssetRelationshipAttributes): + """DataStudio-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DataStudioNested(AssetNested): + """DataStudio in nested API format for high-performance serialization.""" + + attributes: Union[DataStudioAttributes, UnsetType] = UNSET + relationship_attributes: Union[DataStudioRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + DataStudioRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + DataStudioRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DATA_STUDIO_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_data_studio_attrs(attrs: DataStudioAttributes, obj: DataStudio) -> None: + """Populate DataStudio-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.google_service = obj.google_service + attrs.google_project_name = obj.google_project_name + attrs.google_project_id = obj.google_project_id + attrs.google_project_number = obj.google_project_number + attrs.google_location = obj.google_location + attrs.google_location_type = obj.google_location_type + attrs.google_labels = obj.google_labels + attrs.google_tags = obj.google_tags + attrs.cloud_uniform_resource_name = obj.cloud_uniform_resource_name + + +def _extract_data_studio_attrs(attrs: DataStudioAttributes) -> dict: + """Extract all DataStudio attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["google_service"] = attrs.google_service + result["google_project_name"] = attrs.google_project_name + result["google_project_id"] = attrs.google_project_id + result["google_project_number"] = attrs.google_project_number + result["google_location"] = attrs.google_location + result["google_location_type"] = attrs.google_location_type + result["google_labels"] = attrs.google_labels + result["google_tags"] = attrs.google_tags + result["cloud_uniform_resource_name"] = attrs.cloud_uniform_resource_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _data_studio_to_nested(data_studio: DataStudio) -> DataStudioNested: + """Convert flat DataStudio to nested format.""" + attrs = DataStudioAttributes() + _populate_data_studio_attrs(attrs, data_studio) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + data_studio, _DATA_STUDIO_REL_FIELDS, DataStudioRelationshipAttributes + ) + return DataStudioNested( + guid=data_studio.guid, + type_name=data_studio.type_name, + status=data_studio.status, + version=data_studio.version, + create_time=data_studio.create_time, + update_time=data_studio.update_time, + created_by=data_studio.created_by, + updated_by=data_studio.updated_by, + classifications=data_studio.classifications, + classification_names=data_studio.classification_names, + meanings=data_studio.meanings, + labels=data_studio.labels, + business_attributes=data_studio.business_attributes, + custom_attributes=data_studio.custom_attributes, + pending_tasks=data_studio.pending_tasks, + proxy=data_studio.proxy, + is_incomplete=data_studio.is_incomplete, + provenance_type=data_studio.provenance_type, + home_id=data_studio.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _data_studio_from_nested(nested: DataStudioNested) -> DataStudio: + """Convert nested format to flat DataStudio.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else DataStudioAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DATA_STUDIO_REL_FIELDS, + DataStudioRelationshipAttributes, + ) + return DataStudio( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_data_studio_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _data_studio_to_nested_bytes(data_studio: DataStudio, serde: Serde) -> bytes: + """Convert flat DataStudio to nested JSON bytes.""" + return serde.encode(_data_studio_to_nested(data_studio)) + + +def _data_studio_from_nested_bytes(data: bytes, serde: Serde) -> DataStudio: + """Convert nested JSON bytes to flat DataStudio.""" + nested = serde.decode(data, DataStudioNested) + return _data_studio_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +DataStudio.GOOGLE_SERVICE = KeywordField("googleService", "googleService") +DataStudio.GOOGLE_PROJECT_NAME = KeywordTextField( + "googleProjectName", "googleProjectName", "googleProjectName.text" +) +DataStudio.GOOGLE_PROJECT_ID = KeywordTextField( + "googleProjectId", "googleProjectId", "googleProjectId.text" +) +DataStudio.GOOGLE_PROJECT_NUMBER = NumericField( + "googleProjectNumber", "googleProjectNumber" +) +DataStudio.GOOGLE_LOCATION = KeywordField("googleLocation", "googleLocation") +DataStudio.GOOGLE_LOCATION_TYPE = KeywordField( + "googleLocationType", "googleLocationType" +) +DataStudio.GOOGLE_LABELS = KeywordField("googleLabels", "googleLabels") +DataStudio.GOOGLE_TAGS = KeywordField("googleTags", "googleTags") +DataStudio.CLOUD_UNIFORM_RESOURCE_NAME = KeywordField( + "cloudUniformResourceName", "cloudUniformResourceName" +) +DataStudio.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +DataStudio.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +DataStudio.ANOMALO_CHECKS = RelationField("anomaloChecks") +DataStudio.APPLICATION = RelationField("application") +DataStudio.APPLICATION_FIELD = RelationField("applicationField") +DataStudio.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +DataStudio.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +DataStudio.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +DataStudio.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +DataStudio.METRICS = RelationField("metrics") +DataStudio.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +DataStudio.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +DataStudio.MEANINGS = RelationField("meanings") +DataStudio.MC_MONITORS = RelationField("mcMonitors") +DataStudio.MC_INCIDENTS = RelationField("mcIncidents") +DataStudio.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +DataStudio.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +DataStudio.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +DataStudio.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +DataStudio.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +DataStudio.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +DataStudio.FILES = RelationField("files") +DataStudio.LINKS = RelationField("links") +DataStudio.README = RelationField("readme") +DataStudio.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +DataStudio.SODA_CHECKS = RelationField("sodaChecks") +DataStudio.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +DataStudio.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/data_studio_asset.py b/pyatlan_v9/model/assets/data_studio_asset.py new file mode 100644 index 000000000..4115c4fc2 --- /dev/null +++ b/pyatlan_v9/model/assets/data_studio_asset.py @@ -0,0 +1,732 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DataStudioAsset asset model with flattened inheritance. + +This module provides: +- DataStudioAsset: Flat asset class (easy to use) +- DataStudioAssetAttributes: Nested attributes struct (extends AssetAttributes) +- DataStudioAssetNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class DataStudioAsset(Asset): + """ + Instance of a Google Data Studio asset in Atlan. + """ + + DATA_STUDIO_ASSET_TYPE: ClassVar[Any] = None + DATA_STUDIO_ASSET_TITLE: ClassVar[Any] = None + DATA_STUDIO_ASSET_OWNER: ClassVar[Any] = None + IS_TRASHED_DATA_STUDIO_ASSET: ClassVar[Any] = None + GOOGLE_SERVICE: ClassVar[Any] = None + GOOGLE_PROJECT_NAME: ClassVar[Any] = None + GOOGLE_PROJECT_ID: ClassVar[Any] = None + GOOGLE_PROJECT_NUMBER: ClassVar[Any] = None + GOOGLE_LOCATION: ClassVar[Any] = None + GOOGLE_LOCATION_TYPE: ClassVar[Any] = None + GOOGLE_LABELS: ClassVar[Any] = None + GOOGLE_TAGS: ClassVar[Any] = None + CLOUD_UNIFORM_RESOURCE_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "DataStudioAsset" + + data_studio_asset_type: Union[str, None, UnsetType] = UNSET + """Type of the Google Data Studio asset, for example: REPORT or DATA_SOURCE.""" + + data_studio_asset_title: Union[str, None, UnsetType] = UNSET + """Title of the Google Data Studio asset.""" + + data_studio_asset_owner: Union[str, None, UnsetType] = UNSET + """Owner of the asset, from Google Data Studio.""" + + is_trashed_data_studio_asset: Union[bool, None, UnsetType] = UNSET + """Whether the Google Data Studio asset has been trashed (true) or not (false).""" + + google_service: Union[str, None, UnsetType] = UNSET + """Service in Google in which the asset exists.""" + + google_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which the asset exists.""" + + google_project_id: Union[str, None, UnsetType] = UNSET + """ID of the project in which the asset exists.""" + + google_project_number: Union[int, None, UnsetType] = UNSET + """Number of the project in which the asset exists.""" + + google_location: Union[str, None, UnsetType] = UNSET + """Location of this asset in Google.""" + + google_location_type: Union[str, None, UnsetType] = UNSET + """Type of location of this asset in Google.""" + + google_labels: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of labels that have been applied to the asset in Google.""" + + google_tags: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of tags that have been applied to the asset in Google.""" + + cloud_uniform_resource_name: Union[str, None, UnsetType] = UNSET + """Uniform resource name (URN) for the asset: AWS ARN, Google Cloud URI, Azure resource ID, Oracle OCID, and so on.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "DataStudioAsset" + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + connection_qualified_name: str, + data_studio_asset_type: str, + ) -> "DataStudioAsset": + """Create a new DataStudioAsset asset.""" + validate_required_fields( + ["name", "connection_qualified_name", "data_studio_asset_type"], + [name, connection_qualified_name, data_studio_asset_type], + ) + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + return cls( + name=name, + connection_qualified_name=connection_qualified_name, + qualified_name=f"{connection_qualified_name}/{name}", + connector_name=connector_name, + data_studio_asset_type=data_studio_asset_type, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "DataStudioAsset": + """Create a DataStudioAsset instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "DataStudioAsset": + """Return only fields required for update operations.""" + return DataStudioAsset.updater( + qualified_name=self.qualified_name, + name=self.name, + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _data_studio_asset_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> DataStudioAsset: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + DataStudioAsset instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _data_studio_asset_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DataStudioAssetAttributes(AssetAttributes): + """DataStudioAsset-specific attributes for nested API format.""" + + data_studio_asset_type: Union[str, None, UnsetType] = UNSET + """Type of the Google Data Studio asset, for example: REPORT or DATA_SOURCE.""" + + data_studio_asset_title: Union[str, None, UnsetType] = UNSET + """Title of the Google Data Studio asset.""" + + data_studio_asset_owner: Union[str, None, UnsetType] = UNSET + """Owner of the asset, from Google Data Studio.""" + + is_trashed_data_studio_asset: Union[bool, None, UnsetType] = UNSET + """Whether the Google Data Studio asset has been trashed (true) or not (false).""" + + google_service: Union[str, None, UnsetType] = UNSET + """Service in Google in which the asset exists.""" + + google_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which the asset exists.""" + + google_project_id: Union[str, None, UnsetType] = UNSET + """ID of the project in which the asset exists.""" + + google_project_number: Union[int, None, UnsetType] = UNSET + """Number of the project in which the asset exists.""" + + google_location: Union[str, None, UnsetType] = UNSET + """Location of this asset in Google.""" + + google_location_type: Union[str, None, UnsetType] = UNSET + """Type of location of this asset in Google.""" + + google_labels: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of labels that have been applied to the asset in Google.""" + + google_tags: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of tags that have been applied to the asset in Google.""" + + cloud_uniform_resource_name: Union[str, None, UnsetType] = UNSET + """Uniform resource name (URN) for the asset: AWS ARN, Google Cloud URI, Azure resource ID, Oracle OCID, and so on.""" + + +class DataStudioAssetRelationshipAttributes(AssetRelationshipAttributes): + """DataStudioAsset-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DataStudioAssetNested(AssetNested): + """DataStudioAsset in nested API format for high-performance serialization.""" + + attributes: Union[DataStudioAssetAttributes, UnsetType] = UNSET + relationship_attributes: Union[DataStudioAssetRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + DataStudioAssetRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + DataStudioAssetRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DATA_STUDIO_ASSET_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_data_studio_asset_attrs( + attrs: DataStudioAssetAttributes, obj: DataStudioAsset +) -> None: + """Populate DataStudioAsset-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.data_studio_asset_type = obj.data_studio_asset_type + attrs.data_studio_asset_title = obj.data_studio_asset_title + attrs.data_studio_asset_owner = obj.data_studio_asset_owner + attrs.is_trashed_data_studio_asset = obj.is_trashed_data_studio_asset + attrs.google_service = obj.google_service + attrs.google_project_name = obj.google_project_name + attrs.google_project_id = obj.google_project_id + attrs.google_project_number = obj.google_project_number + attrs.google_location = obj.google_location + attrs.google_location_type = obj.google_location_type + attrs.google_labels = obj.google_labels + attrs.google_tags = obj.google_tags + attrs.cloud_uniform_resource_name = obj.cloud_uniform_resource_name + + +def _extract_data_studio_asset_attrs(attrs: DataStudioAssetAttributes) -> dict: + """Extract all DataStudioAsset attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["data_studio_asset_type"] = attrs.data_studio_asset_type + result["data_studio_asset_title"] = attrs.data_studio_asset_title + result["data_studio_asset_owner"] = attrs.data_studio_asset_owner + result["is_trashed_data_studio_asset"] = attrs.is_trashed_data_studio_asset + result["google_service"] = attrs.google_service + result["google_project_name"] = attrs.google_project_name + result["google_project_id"] = attrs.google_project_id + result["google_project_number"] = attrs.google_project_number + result["google_location"] = attrs.google_location + result["google_location_type"] = attrs.google_location_type + result["google_labels"] = attrs.google_labels + result["google_tags"] = attrs.google_tags + result["cloud_uniform_resource_name"] = attrs.cloud_uniform_resource_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _data_studio_asset_to_nested( + data_studio_asset: DataStudioAsset, +) -> DataStudioAssetNested: + """Convert flat DataStudioAsset to nested format.""" + attrs = DataStudioAssetAttributes() + _populate_data_studio_asset_attrs(attrs, data_studio_asset) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + data_studio_asset, + _DATA_STUDIO_ASSET_REL_FIELDS, + DataStudioAssetRelationshipAttributes, + ) + return DataStudioAssetNested( + guid=data_studio_asset.guid, + type_name=data_studio_asset.type_name, + status=data_studio_asset.status, + version=data_studio_asset.version, + create_time=data_studio_asset.create_time, + update_time=data_studio_asset.update_time, + created_by=data_studio_asset.created_by, + updated_by=data_studio_asset.updated_by, + classifications=data_studio_asset.classifications, + classification_names=data_studio_asset.classification_names, + meanings=data_studio_asset.meanings, + labels=data_studio_asset.labels, + business_attributes=data_studio_asset.business_attributes, + custom_attributes=data_studio_asset.custom_attributes, + pending_tasks=data_studio_asset.pending_tasks, + proxy=data_studio_asset.proxy, + is_incomplete=data_studio_asset.is_incomplete, + provenance_type=data_studio_asset.provenance_type, + home_id=data_studio_asset.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _data_studio_asset_from_nested(nested: DataStudioAssetNested) -> DataStudioAsset: + """Convert nested format to flat DataStudioAsset.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else DataStudioAssetAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DATA_STUDIO_ASSET_REL_FIELDS, + DataStudioAssetRelationshipAttributes, + ) + return DataStudioAsset( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_data_studio_asset_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _data_studio_asset_to_nested_bytes( + data_studio_asset: DataStudioAsset, serde: Serde +) -> bytes: + """Convert flat DataStudioAsset to nested JSON bytes.""" + return serde.encode(_data_studio_asset_to_nested(data_studio_asset)) + + +def _data_studio_asset_from_nested_bytes(data: bytes, serde: Serde) -> DataStudioAsset: + """Convert nested JSON bytes to flat DataStudioAsset.""" + nested = serde.decode(data, DataStudioAssetNested) + return _data_studio_asset_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +DataStudioAsset.DATA_STUDIO_ASSET_TYPE = KeywordField( + "dataStudioAssetType", "dataStudioAssetType" +) +DataStudioAsset.DATA_STUDIO_ASSET_TITLE = KeywordField( + "dataStudioAssetTitle", "dataStudioAssetTitle" +) +DataStudioAsset.DATA_STUDIO_ASSET_OWNER = KeywordField( + "dataStudioAssetOwner", "dataStudioAssetOwner" +) +DataStudioAsset.IS_TRASHED_DATA_STUDIO_ASSET = BooleanField( + "isTrashedDataStudioAsset", "isTrashedDataStudioAsset" +) +DataStudioAsset.GOOGLE_SERVICE = KeywordField("googleService", "googleService") +DataStudioAsset.GOOGLE_PROJECT_NAME = KeywordTextField( + "googleProjectName", "googleProjectName", "googleProjectName.text" +) +DataStudioAsset.GOOGLE_PROJECT_ID = KeywordTextField( + "googleProjectId", "googleProjectId", "googleProjectId.text" +) +DataStudioAsset.GOOGLE_PROJECT_NUMBER = NumericField( + "googleProjectNumber", "googleProjectNumber" +) +DataStudioAsset.GOOGLE_LOCATION = KeywordField("googleLocation", "googleLocation") +DataStudioAsset.GOOGLE_LOCATION_TYPE = KeywordField( + "googleLocationType", "googleLocationType" +) +DataStudioAsset.GOOGLE_LABELS = KeywordField("googleLabels", "googleLabels") +DataStudioAsset.GOOGLE_TAGS = KeywordField("googleTags", "googleTags") +DataStudioAsset.CLOUD_UNIFORM_RESOURCE_NAME = KeywordField( + "cloudUniformResourceName", "cloudUniformResourceName" +) +DataStudioAsset.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +DataStudioAsset.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +DataStudioAsset.ANOMALO_CHECKS = RelationField("anomaloChecks") +DataStudioAsset.APPLICATION = RelationField("application") +DataStudioAsset.APPLICATION_FIELD = RelationField("applicationField") +DataStudioAsset.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +DataStudioAsset.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +DataStudioAsset.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +DataStudioAsset.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +DataStudioAsset.METRICS = RelationField("metrics") +DataStudioAsset.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +DataStudioAsset.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +DataStudioAsset.MEANINGS = RelationField("meanings") +DataStudioAsset.MC_MONITORS = RelationField("mcMonitors") +DataStudioAsset.MC_INCIDENTS = RelationField("mcIncidents") +DataStudioAsset.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +DataStudioAsset.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +DataStudioAsset.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +DataStudioAsset.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +DataStudioAsset.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +DataStudioAsset.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +DataStudioAsset.FILES = RelationField("files") +DataStudioAsset.LINKS = RelationField("links") +DataStudioAsset.README = RelationField("readme") +DataStudioAsset.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +DataStudioAsset.SODA_CHECKS = RelationField("sodaChecks") +DataStudioAsset.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +DataStudioAsset.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/data_studio_related.py b/pyatlan_v9/model/assets/data_studio_related.py new file mode 100644 index 000000000..285dbb655 --- /dev/null +++ b/pyatlan_v9/model/assets/data_studio_related.py @@ -0,0 +1,66 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for DataStudio module. + +This module contains all Related{Type} classes for the DataStudio type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Union + +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedBI +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedDataStudio", + "RelatedDataStudioAsset", +] + + +class RelatedDataStudio(RelatedBI): + """ + Related entity reference for DataStudio assets. + + Extends RelatedBI with DataStudio-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DataStudio" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DataStudio" + + +class RelatedDataStudioAsset(RelatedDataStudio): + """ + Related entity reference for DataStudioAsset assets. + + Extends RelatedDataStudio with DataStudioAsset-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DataStudioAsset" so it serializes correctly + + data_studio_asset_type: Union[str, None, UnsetType] = UNSET + """Type of the Google Data Studio asset, for example: REPORT or DATA_SOURCE.""" + + data_studio_asset_title: Union[str, None, UnsetType] = UNSET + """Title of the Google Data Studio asset.""" + + data_studio_asset_owner: Union[str, None, UnsetType] = UNSET + """Owner of the asset, from Google Data Studio.""" + + is_trashed_data_studio_asset: Union[bool, None, UnsetType] = UNSET + """Whether the Google Data Studio asset has been trashed (true) or not (false).""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DataStudioAsset" diff --git a/pyatlan_v9/model/assets/database.py b/pyatlan_v9/model/assets/database.py new file mode 100644 index 000000000..18cde77ec --- /dev/null +++ b/pyatlan_v9/model/assets/database.py @@ -0,0 +1,898 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Database asset model with flattened inheritance. + +This module provides: +- Database: Flat asset class (easy to use) +- DatabaseAttributes: Nested attributes struct (extends AssetAttributes) +- DatabaseNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .fabric_related import RelatedFabricWorkspace +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .snowflake_related import RelatedSnowflakeSemanticLogicalTable +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .sql_related import RelatedSchema + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Database(Asset): + """ + Instance of a (relational) database in Atlan. + """ + + SCHEMA_COUNT: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + FABRIC_WORKSPACE: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMAS: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Database" + + schema_count: Union[int, None, UnsetType] = UNSET + """Number of schemas in this database.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + fabric_workspace: Union[RelatedFabricWorkspace, None, UnsetType] = UNSET + """Workspace containing the database.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schemas: Union[List[RelatedSchema], None, UnsetType] = UNSET + """Schemas that exist within this database.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Database" + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + connection_qualified_name: str, + ) -> "Database": + """ + Create a new Database asset. + + Args: + name: Name of the database + connection_qualified_name: Unique name of the connection in which this database exists + + Returns: + Database instance ready to be created + + Raises: + ValueError: If required parameters are missing or invalid + """ + validate_required_fields( + ["name", "connection_qualified_name"], [name, connection_qualified_name] + ) + + fields = connection_qualified_name.split("/") + if len(fields) != 3: + raise ValueError( + f"Invalid connection_qualified_name: {connection_qualified_name}. " + "Expected format: default/connector/connection_id" + ) + + connector_name = fields[1] + qualified_name = f"{connection_qualified_name}/{name}" + + return cls( + name=name, + qualified_name=qualified_name, + connection_qualified_name=connection_qualified_name, + connector_name=connector_name, + ) + + @classmethod + def create(cls, *, name: str, connection_qualified_name: str) -> "Database": + """ + Create a new Database asset (deprecated - use creator instead). + + Args: + name: Name of the database + connection_qualified_name: Unique name of the connection in which this database exists + + Returns: + Database instance ready to be created + """ + return cls.creator( + name=name, connection_qualified_name=connection_qualified_name + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _database_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Database: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Database instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _database_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DatabaseAttributes(AssetAttributes): + """Database-specific attributes for nested API format.""" + + schema_count: Union[int, None, UnsetType] = UNSET + """Number of schemas in this database.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + +class DatabaseRelationshipAttributes(AssetRelationshipAttributes): + """Database-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + fabric_workspace: Union[RelatedFabricWorkspace, None, UnsetType] = UNSET + """Workspace containing the database.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schemas: Union[List[RelatedSchema], None, UnsetType] = UNSET + """Schemas that exist within this database.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DatabaseNested(AssetNested): + """Database in nested API format for high-performance serialization.""" + + attributes: Union[DatabaseAttributes, UnsetType] = UNSET + relationship_attributes: Union[DatabaseRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[DatabaseRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[DatabaseRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DATABASE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "fabric_workspace", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schemas", + "schema_registry_subjects", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_database_attrs(attrs: DatabaseAttributes, obj: Database) -> None: + """Populate Database-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.schema_count = obj.schema_count + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + + +def _extract_database_attrs(attrs: DatabaseAttributes) -> dict: + """Extract all Database attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["schema_count"] = attrs.schema_count + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _database_to_nested(database: Database) -> DatabaseNested: + """Convert flat Database to nested format.""" + attrs = DatabaseAttributes() + _populate_database_attrs(attrs, database) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + database, _DATABASE_REL_FIELDS, DatabaseRelationshipAttributes + ) + return DatabaseNested( + guid=database.guid, + type_name=database.type_name, + status=database.status, + version=database.version, + create_time=database.create_time, + update_time=database.update_time, + created_by=database.created_by, + updated_by=database.updated_by, + classifications=database.classifications, + classification_names=database.classification_names, + meanings=database.meanings, + labels=database.labels, + business_attributes=database.business_attributes, + custom_attributes=database.custom_attributes, + pending_tasks=database.pending_tasks, + proxy=database.proxy, + is_incomplete=database.is_incomplete, + provenance_type=database.provenance_type, + home_id=database.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _database_from_nested(nested: DatabaseNested) -> Database: + """Convert nested format to flat Database.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else DatabaseAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DATABASE_REL_FIELDS, + DatabaseRelationshipAttributes, + ) + return Database( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_database_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _database_to_nested_bytes(database: Database, serde: Serde) -> bytes: + """Convert flat Database to nested JSON bytes.""" + return serde.encode(_database_to_nested(database)) + + +def _database_from_nested_bytes(data: bytes, serde: Serde) -> Database: + """Convert nested JSON bytes to flat Database.""" + nested = serde.decode(data, DatabaseNested) + return _database_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, +) + +Database.SCHEMA_COUNT = NumericField("schemaCount", "schemaCount") +Database.QUERY_COUNT = NumericField("queryCount", "queryCount") +Database.QUERY_USER_COUNT = NumericField("queryUserCount", "queryUserCount") +Database.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +Database.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +Database.DATABASE_NAME = KeywordField("databaseName", "databaseName") +Database.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +Database.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +Database.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +Database.TABLE_NAME = KeywordField("tableName", "tableName") +Database.TABLE_QUALIFIED_NAME = KeywordField("tableQualifiedName", "tableQualifiedName") +Database.VIEW_NAME = KeywordField("viewName", "viewName") +Database.VIEW_QUALIFIED_NAME = KeywordField("viewQualifiedName", "viewQualifiedName") +Database.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +Database.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +Database.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +Database.LAST_PROFILED_AT = NumericField("lastProfiledAt", "lastProfiledAt") +Database.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +Database.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +Database.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Database.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Database.ANOMALO_CHECKS = RelationField("anomaloChecks") +Database.APPLICATION = RelationField("application") +Database.APPLICATION_FIELD = RelationField("applicationField") +Database.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Database.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Database.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Database.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Database.METRICS = RelationField("metrics") +Database.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Database.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Database.DBT_MODELS = RelationField("dbtModels") +Database.SQL_DBT_MODELS = RelationField("sqlDbtModels") +Database.DBT_TESTS = RelationField("dbtTests") +Database.DBT_SOURCES = RelationField("dbtSources") +Database.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +Database.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +Database.FABRIC_WORKSPACE = RelationField("fabricWorkspace") +Database.MEANINGS = RelationField("meanings") +Database.MC_MONITORS = RelationField("mcMonitors") +Database.MC_INCIDENTS = RelationField("mcIncidents") +Database.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Database.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Database.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Database.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Database.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Database.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Database.FILES = RelationField("files") +Database.LINKS = RelationField("links") +Database.README = RelationField("readme") +Database.SCHEMAS = RelationField("schemas") +Database.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Database.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +Database.SODA_CHECKS = RelationField("sodaChecks") +Database.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Database.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/databricks.py b/pyatlan_v9/model/assets/databricks.py new file mode 100644 index 000000000..b337e1252 --- /dev/null +++ b/pyatlan_v9/model/assets/databricks.py @@ -0,0 +1,810 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Databricks asset model with flattened inheritance. + +This module provides: +- Databricks: Flat asset class (easy to use) +- DatabricksAttributes: Nested attributes struct (extends AssetAttributes) +- DatabricksNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .snowflake_related import RelatedSnowflakeSemanticLogicalTable +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Databricks(Asset): + """ + Instance of an asset in Databricks. + """ + + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Databricks" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Databricks" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _databricks_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Databricks: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Databricks instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _databricks_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DatabricksAttributes(AssetAttributes): + """Databricks-specific attributes for nested API format.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + +class DatabricksRelationshipAttributes(AssetRelationshipAttributes): + """Databricks-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DatabricksNested(AssetNested): + """Databricks in nested API format for high-performance serialization.""" + + attributes: Union[DatabricksAttributes, UnsetType] = UNSET + relationship_attributes: Union[DatabricksRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + DatabricksRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + DatabricksRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DATABRICKS_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_databricks_attrs(attrs: DatabricksAttributes, obj: Databricks) -> None: + """Populate Databricks-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + + +def _extract_databricks_attrs(attrs: DatabricksAttributes) -> dict: + """Extract all Databricks attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _databricks_to_nested(databricks: Databricks) -> DatabricksNested: + """Convert flat Databricks to nested format.""" + attrs = DatabricksAttributes() + _populate_databricks_attrs(attrs, databricks) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + databricks, _DATABRICKS_REL_FIELDS, DatabricksRelationshipAttributes + ) + return DatabricksNested( + guid=databricks.guid, + type_name=databricks.type_name, + status=databricks.status, + version=databricks.version, + create_time=databricks.create_time, + update_time=databricks.update_time, + created_by=databricks.created_by, + updated_by=databricks.updated_by, + classifications=databricks.classifications, + classification_names=databricks.classification_names, + meanings=databricks.meanings, + labels=databricks.labels, + business_attributes=databricks.business_attributes, + custom_attributes=databricks.custom_attributes, + pending_tasks=databricks.pending_tasks, + proxy=databricks.proxy, + is_incomplete=databricks.is_incomplete, + provenance_type=databricks.provenance_type, + home_id=databricks.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _databricks_from_nested(nested: DatabricksNested) -> Databricks: + """Convert nested format to flat Databricks.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else DatabricksAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DATABRICKS_REL_FIELDS, + DatabricksRelationshipAttributes, + ) + return Databricks( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_databricks_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _databricks_to_nested_bytes(databricks: Databricks, serde: Serde) -> bytes: + """Convert flat Databricks to nested JSON bytes.""" + return serde.encode(_databricks_to_nested(databricks)) + + +def _databricks_from_nested_bytes(data: bytes, serde: Serde) -> Databricks: + """Convert nested JSON bytes to flat Databricks.""" + nested = serde.decode(data, DatabricksNested) + return _databricks_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, +) + +Databricks.QUERY_COUNT = NumericField("queryCount", "queryCount") +Databricks.QUERY_USER_COUNT = NumericField("queryUserCount", "queryUserCount") +Databricks.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +Databricks.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +Databricks.DATABASE_NAME = KeywordField("databaseName", "databaseName") +Databricks.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +Databricks.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +Databricks.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +Databricks.TABLE_NAME = KeywordField("tableName", "tableName") +Databricks.TABLE_QUALIFIED_NAME = KeywordField( + "tableQualifiedName", "tableQualifiedName" +) +Databricks.VIEW_NAME = KeywordField("viewName", "viewName") +Databricks.VIEW_QUALIFIED_NAME = KeywordField("viewQualifiedName", "viewQualifiedName") +Databricks.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +Databricks.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +Databricks.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +Databricks.LAST_PROFILED_AT = NumericField("lastProfiledAt", "lastProfiledAt") +Databricks.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +Databricks.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +Databricks.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Databricks.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Databricks.ANOMALO_CHECKS = RelationField("anomaloChecks") +Databricks.APPLICATION = RelationField("application") +Databricks.APPLICATION_FIELD = RelationField("applicationField") +Databricks.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Databricks.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Databricks.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Databricks.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Databricks.METRICS = RelationField("metrics") +Databricks.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Databricks.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Databricks.DBT_MODELS = RelationField("dbtModels") +Databricks.SQL_DBT_MODELS = RelationField("sqlDbtModels") +Databricks.DBT_TESTS = RelationField("dbtTests") +Databricks.DBT_SOURCES = RelationField("dbtSources") +Databricks.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +Databricks.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +Databricks.MEANINGS = RelationField("meanings") +Databricks.MC_MONITORS = RelationField("mcMonitors") +Databricks.MC_INCIDENTS = RelationField("mcIncidents") +Databricks.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Databricks.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Databricks.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Databricks.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Databricks.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Databricks.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Databricks.FILES = RelationField("files") +Databricks.LINKS = RelationField("links") +Databricks.README = RelationField("readme") +Databricks.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Databricks.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +Databricks.SODA_CHECKS = RelationField("sodaChecks") +Databricks.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Databricks.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/databricks_ai_model_context.py b/pyatlan_v9/model/assets/databricks_ai_model_context.py new file mode 100644 index 000000000..7c3fe9409 --- /dev/null +++ b/pyatlan_v9/model/assets/databricks_ai_model_context.py @@ -0,0 +1,1092 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DatabricksAIModelContext asset model with flattened inheritance. + +This module provides: +- DatabricksAIModelContext: Flat asset class (easy to use) +- DatabricksAIModelContextAttributes: Nested attributes struct (extends AssetAttributes) +- DatabricksAIModelContextNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .ai_related import RelatedAIApplication, RelatedAIModelVersion +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .snowflake_related import RelatedSnowflakeSemanticLogicalTable +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from .sql_related import RelatedSchema +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .databricks_related import RelatedDatabricksAIModelVersion + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class DatabricksAIModelContext(Asset): + """ + Instance of an ai model in databricks. + """ + + DATABRICKS_METASTORE_ID: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + AI_MODEL_DATASETS_DSL: ClassVar[Any] = None + AI_MODEL_STATUS: ClassVar[Any] = None + AI_MODEL_VERSION: ClassVar[Any] = None + ETHICAL_AI_PRIVACY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_FAIRNESS_CONFIG: ClassVar[Any] = None + ETHICAL_AI_BIAS_MITIGATION_CONFIG: ClassVar[Any] = None + ETHICAL_AI_RELIABILITY_AND_SAFETY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_TRANSPARENCY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_ACCOUNTABILITY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_ENVIRONMENTAL_CONSCIOUSNESS_CONFIG: ClassVar[Any] = None + APPLICATIONS: ClassVar[Any] = None + AI_MODEL_VERSIONS: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DATABRICKS_AI_MODEL_SCHEMA: ClassVar[Any] = None + DATABRICKS_AI_MODEL_VERSIONS: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "DatabricksAIModelContext" + + databricks_metastore_id: Union[str, None, UnsetType] = UNSET + """The id of the model, common across versions.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + ai_model_datasets_dsl: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="aiModelDatasetsDSL" + ) + """Search DSL used to define which assets/datasets are part of the AI model.""" + + ai_model_status: Union[str, None, UnsetType] = UNSET + """Status of the AI model.""" + + ai_model_version: Union[str, None, UnsetType] = UNSET + """Version of the AI model.""" + + ethical_ai_privacy_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIPrivacyConfig" + ) + """Privacy configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_fairness_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIFairnessConfig" + ) + """Fairness configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_bias_mitigation_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIBiasMitigationConfig" + ) + """Bias mitigation configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_reliability_and_safety_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIReliabilityAndSafetyConfig") + ) + """Reliability and safety configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_transparency_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAITransparencyConfig" + ) + """Transparency configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_accountability_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIAccountabilityConfig" + ) + """Accountability configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_environmental_consciousness_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIEnvironmentalConsciousnessConfig") + ) + """Environmental consciousness configuration for ensuring the ethical use of an AI asset""" + + applications: Union[List[RelatedAIApplication], None, UnsetType] = UNSET + """AI applications that are created using this AI model.""" + + ai_model_versions: Union[List[RelatedAIModelVersion], None, UnsetType] = UNSET + """Versions contained within the model.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + databricks_ai_model_schema: Union[RelatedSchema, None, UnsetType] = msgspec.field( + default=UNSET, name="databricksAIModelSchema" + ) + """Schema containing the context.""" + + databricks_ai_model_versions: Union[ + List[RelatedDatabricksAIModelVersion], None, UnsetType + ] = msgspec.field(default=UNSET, name="databricksAIModelVersions") + """Versions contained within the context.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "DatabricksAIModelContext" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _databricks_ai_model_context_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> DatabricksAIModelContext: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + DatabricksAIModelContext instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _databricks_ai_model_context_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DatabricksAIModelContextAttributes(AssetAttributes): + """DatabricksAIModelContext-specific attributes for nested API format.""" + + databricks_metastore_id: Union[str, None, UnsetType] = UNSET + """The id of the model, common across versions.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + ai_model_datasets_dsl: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="aiModelDatasetsDSL" + ) + """Search DSL used to define which assets/datasets are part of the AI model.""" + + ai_model_status: Union[str, None, UnsetType] = UNSET + """Status of the AI model.""" + + ai_model_version: Union[str, None, UnsetType] = UNSET + """Version of the AI model.""" + + ethical_ai_privacy_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIPrivacyConfig" + ) + """Privacy configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_fairness_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIFairnessConfig" + ) + """Fairness configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_bias_mitigation_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIBiasMitigationConfig" + ) + """Bias mitigation configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_reliability_and_safety_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIReliabilityAndSafetyConfig") + ) + """Reliability and safety configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_transparency_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAITransparencyConfig" + ) + """Transparency configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_accountability_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIAccountabilityConfig" + ) + """Accountability configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_environmental_consciousness_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIEnvironmentalConsciousnessConfig") + ) + """Environmental consciousness configuration for ensuring the ethical use of an AI asset""" + + +class DatabricksAIModelContextRelationshipAttributes(AssetRelationshipAttributes): + """DatabricksAIModelContext-specific relationship attributes for nested API format.""" + + applications: Union[List[RelatedAIApplication], None, UnsetType] = UNSET + """AI applications that are created using this AI model.""" + + ai_model_versions: Union[List[RelatedAIModelVersion], None, UnsetType] = UNSET + """Versions contained within the model.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + databricks_ai_model_schema: Union[RelatedSchema, None, UnsetType] = msgspec.field( + default=UNSET, name="databricksAIModelSchema" + ) + """Schema containing the context.""" + + databricks_ai_model_versions: Union[ + List[RelatedDatabricksAIModelVersion], None, UnsetType + ] = msgspec.field(default=UNSET, name="databricksAIModelVersions") + """Versions contained within the context.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DatabricksAIModelContextNested(AssetNested): + """DatabricksAIModelContext in nested API format for high-performance serialization.""" + + attributes: Union[DatabricksAIModelContextAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + DatabricksAIModelContextRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + DatabricksAIModelContextRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + DatabricksAIModelContextRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DATABRICKS_AI_MODEL_CONTEXT_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "applications", + "ai_model_versions", + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "databricks_ai_model_schema", + "databricks_ai_model_versions", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_databricks_ai_model_context_attrs( + attrs: DatabricksAIModelContextAttributes, obj: DatabricksAIModelContext +) -> None: + """Populate DatabricksAIModelContext-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.databricks_metastore_id = obj.databricks_metastore_id + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + attrs.ai_model_datasets_dsl = obj.ai_model_datasets_dsl + attrs.ai_model_status = obj.ai_model_status + attrs.ai_model_version = obj.ai_model_version + attrs.ethical_ai_privacy_config = obj.ethical_ai_privacy_config + attrs.ethical_ai_fairness_config = obj.ethical_ai_fairness_config + attrs.ethical_ai_bias_mitigation_config = obj.ethical_ai_bias_mitigation_config + attrs.ethical_ai_reliability_and_safety_config = ( + obj.ethical_ai_reliability_and_safety_config + ) + attrs.ethical_ai_transparency_config = obj.ethical_ai_transparency_config + attrs.ethical_ai_accountability_config = obj.ethical_ai_accountability_config + attrs.ethical_ai_environmental_consciousness_config = ( + obj.ethical_ai_environmental_consciousness_config + ) + + +def _extract_databricks_ai_model_context_attrs( + attrs: DatabricksAIModelContextAttributes, +) -> dict: + """Extract all DatabricksAIModelContext attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["databricks_metastore_id"] = attrs.databricks_metastore_id + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + result["ai_model_datasets_dsl"] = attrs.ai_model_datasets_dsl + result["ai_model_status"] = attrs.ai_model_status + result["ai_model_version"] = attrs.ai_model_version + result["ethical_ai_privacy_config"] = attrs.ethical_ai_privacy_config + result["ethical_ai_fairness_config"] = attrs.ethical_ai_fairness_config + result["ethical_ai_bias_mitigation_config"] = ( + attrs.ethical_ai_bias_mitigation_config + ) + result["ethical_ai_reliability_and_safety_config"] = ( + attrs.ethical_ai_reliability_and_safety_config + ) + result["ethical_ai_transparency_config"] = attrs.ethical_ai_transparency_config + result["ethical_ai_accountability_config"] = attrs.ethical_ai_accountability_config + result["ethical_ai_environmental_consciousness_config"] = ( + attrs.ethical_ai_environmental_consciousness_config + ) + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _databricks_ai_model_context_to_nested( + databricks_ai_model_context: DatabricksAIModelContext, +) -> DatabricksAIModelContextNested: + """Convert flat DatabricksAIModelContext to nested format.""" + attrs = DatabricksAIModelContextAttributes() + _populate_databricks_ai_model_context_attrs(attrs, databricks_ai_model_context) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + databricks_ai_model_context, + _DATABRICKS_AI_MODEL_CONTEXT_REL_FIELDS, + DatabricksAIModelContextRelationshipAttributes, + ) + return DatabricksAIModelContextNested( + guid=databricks_ai_model_context.guid, + type_name=databricks_ai_model_context.type_name, + status=databricks_ai_model_context.status, + version=databricks_ai_model_context.version, + create_time=databricks_ai_model_context.create_time, + update_time=databricks_ai_model_context.update_time, + created_by=databricks_ai_model_context.created_by, + updated_by=databricks_ai_model_context.updated_by, + classifications=databricks_ai_model_context.classifications, + classification_names=databricks_ai_model_context.classification_names, + meanings=databricks_ai_model_context.meanings, + labels=databricks_ai_model_context.labels, + business_attributes=databricks_ai_model_context.business_attributes, + custom_attributes=databricks_ai_model_context.custom_attributes, + pending_tasks=databricks_ai_model_context.pending_tasks, + proxy=databricks_ai_model_context.proxy, + is_incomplete=databricks_ai_model_context.is_incomplete, + provenance_type=databricks_ai_model_context.provenance_type, + home_id=databricks_ai_model_context.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _databricks_ai_model_context_from_nested( + nested: DatabricksAIModelContextNested, +) -> DatabricksAIModelContext: + """Convert nested format to flat DatabricksAIModelContext.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else DatabricksAIModelContextAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DATABRICKS_AI_MODEL_CONTEXT_REL_FIELDS, + DatabricksAIModelContextRelationshipAttributes, + ) + return DatabricksAIModelContext( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_databricks_ai_model_context_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _databricks_ai_model_context_to_nested_bytes( + databricks_ai_model_context: DatabricksAIModelContext, serde: Serde +) -> bytes: + """Convert flat DatabricksAIModelContext to nested JSON bytes.""" + return serde.encode( + _databricks_ai_model_context_to_nested(databricks_ai_model_context) + ) + + +def _databricks_ai_model_context_from_nested_bytes( + data: bytes, serde: Serde +) -> DatabricksAIModelContext: + """Convert nested JSON bytes to flat DatabricksAIModelContext.""" + nested = serde.decode(data, DatabricksAIModelContextNested) + return _databricks_ai_model_context_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, +) + +DatabricksAIModelContext.DATABRICKS_METASTORE_ID = KeywordField( + "databricksMetastoreId", "databricksMetastoreId" +) +DatabricksAIModelContext.QUERY_COUNT = NumericField("queryCount", "queryCount") +DatabricksAIModelContext.QUERY_USER_COUNT = NumericField( + "queryUserCount", "queryUserCount" +) +DatabricksAIModelContext.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +DatabricksAIModelContext.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +DatabricksAIModelContext.DATABASE_NAME = KeywordField("databaseName", "databaseName") +DatabricksAIModelContext.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +DatabricksAIModelContext.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +DatabricksAIModelContext.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +DatabricksAIModelContext.TABLE_NAME = KeywordField("tableName", "tableName") +DatabricksAIModelContext.TABLE_QUALIFIED_NAME = KeywordField( + "tableQualifiedName", "tableQualifiedName" +) +DatabricksAIModelContext.VIEW_NAME = KeywordField("viewName", "viewName") +DatabricksAIModelContext.VIEW_QUALIFIED_NAME = KeywordField( + "viewQualifiedName", "viewQualifiedName" +) +DatabricksAIModelContext.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +DatabricksAIModelContext.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +DatabricksAIModelContext.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +DatabricksAIModelContext.LAST_PROFILED_AT = NumericField( + "lastProfiledAt", "lastProfiledAt" +) +DatabricksAIModelContext.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +DatabricksAIModelContext.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +DatabricksAIModelContext.AI_MODEL_DATASETS_DSL = KeywordField( + "aiModelDatasetsDSL", "aiModelDatasetsDSL" +) +DatabricksAIModelContext.AI_MODEL_STATUS = KeywordField( + "aiModelStatus", "aiModelStatus" +) +DatabricksAIModelContext.AI_MODEL_VERSION = KeywordField( + "aiModelVersion", "aiModelVersion" +) +DatabricksAIModelContext.ETHICAL_AI_PRIVACY_CONFIG = KeywordField( + "ethicalAIPrivacyConfig", "ethicalAIPrivacyConfig" +) +DatabricksAIModelContext.ETHICAL_AI_FAIRNESS_CONFIG = KeywordField( + "ethicalAIFairnessConfig", "ethicalAIFairnessConfig" +) +DatabricksAIModelContext.ETHICAL_AI_BIAS_MITIGATION_CONFIG = KeywordField( + "ethicalAIBiasMitigationConfig", "ethicalAIBiasMitigationConfig" +) +DatabricksAIModelContext.ETHICAL_AI_RELIABILITY_AND_SAFETY_CONFIG = KeywordField( + "ethicalAIReliabilityAndSafetyConfig", "ethicalAIReliabilityAndSafetyConfig" +) +DatabricksAIModelContext.ETHICAL_AI_TRANSPARENCY_CONFIG = KeywordField( + "ethicalAITransparencyConfig", "ethicalAITransparencyConfig" +) +DatabricksAIModelContext.ETHICAL_AI_ACCOUNTABILITY_CONFIG = KeywordField( + "ethicalAIAccountabilityConfig", "ethicalAIAccountabilityConfig" +) +DatabricksAIModelContext.ETHICAL_AI_ENVIRONMENTAL_CONSCIOUSNESS_CONFIG = KeywordField( + "ethicalAIEnvironmentalConsciousnessConfig", + "ethicalAIEnvironmentalConsciousnessConfig", +) +DatabricksAIModelContext.APPLICATIONS = RelationField("applications") +DatabricksAIModelContext.AI_MODEL_VERSIONS = RelationField("aiModelVersions") +DatabricksAIModelContext.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +DatabricksAIModelContext.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +DatabricksAIModelContext.ANOMALO_CHECKS = RelationField("anomaloChecks") +DatabricksAIModelContext.APPLICATION = RelationField("application") +DatabricksAIModelContext.APPLICATION_FIELD = RelationField("applicationField") +DatabricksAIModelContext.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +DatabricksAIModelContext.INPUT_PORT_DATA_PRODUCTS = RelationField( + "inputPortDataProducts" +) +DatabricksAIModelContext.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +DatabricksAIModelContext.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +DatabricksAIModelContext.METRICS = RelationField("metrics") +DatabricksAIModelContext.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +DatabricksAIModelContext.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +DatabricksAIModelContext.DATABRICKS_AI_MODEL_SCHEMA = RelationField( + "databricksAIModelSchema" +) +DatabricksAIModelContext.DATABRICKS_AI_MODEL_VERSIONS = RelationField( + "databricksAIModelVersions" +) +DatabricksAIModelContext.DBT_MODELS = RelationField("dbtModels") +DatabricksAIModelContext.SQL_DBT_MODELS = RelationField("sqlDbtModels") +DatabricksAIModelContext.DBT_TESTS = RelationField("dbtTests") +DatabricksAIModelContext.DBT_SOURCES = RelationField("dbtSources") +DatabricksAIModelContext.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +DatabricksAIModelContext.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +DatabricksAIModelContext.MEANINGS = RelationField("meanings") +DatabricksAIModelContext.MC_MONITORS = RelationField("mcMonitors") +DatabricksAIModelContext.MC_INCIDENTS = RelationField("mcIncidents") +DatabricksAIModelContext.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +DatabricksAIModelContext.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +DatabricksAIModelContext.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +DatabricksAIModelContext.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +DatabricksAIModelContext.USER_DEF_RELATIONSHIP_TO = RelationField( + "userDefRelationshipTo" +) +DatabricksAIModelContext.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +DatabricksAIModelContext.FILES = RelationField("files") +DatabricksAIModelContext.LINKS = RelationField("links") +DatabricksAIModelContext.README = RelationField("readme") +DatabricksAIModelContext.SCHEMA_REGISTRY_SUBJECTS = RelationField( + "schemaRegistrySubjects" +) +DatabricksAIModelContext.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +DatabricksAIModelContext.SODA_CHECKS = RelationField("sodaChecks") +DatabricksAIModelContext.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +DatabricksAIModelContext.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/databricks_ai_model_version.py b/pyatlan_v9/model/assets/databricks_ai_model_version.py new file mode 100644 index 000000000..cbf2b467c --- /dev/null +++ b/pyatlan_v9/model/assets/databricks_ai_model_version.py @@ -0,0 +1,1157 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DatabricksAIModelVersion asset model with flattened inheritance. + +This module provides: +- DatabricksAIModelVersion: Flat asset class (easy to use) +- DatabricksAIModelVersionAttributes: Nested attributes struct (extends AssetAttributes) +- DatabricksAIModelVersionNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .ai_related import RelatedAIModel +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .snowflake_related import RelatedSnowflakeSemanticLogicalTable +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .databricks_related import RelatedDatabricksAIModelContext + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class DatabricksAIModelVersion(Asset): + """ + Instance of an ai model version in databricks. + """ + + DATABRICKS_ID: ClassVar[Any] = None + DATABRICKS_RUN_ID: ClassVar[Any] = None + DATABRICKS_RUN_NAME: ClassVar[Any] = None + DATABRICKS_RUN_START_TIME: ClassVar[Any] = None + DATABRICKS_RUN_END_TIME: ClassVar[Any] = None + DATABRICKS_STATUS: ClassVar[Any] = None + DATABRICKS_ALIASES: ClassVar[Any] = None + DATABRICKS_DATASET_COUNT: ClassVar[Any] = None + DATABRICKS_SOURCE: ClassVar[Any] = None + DATABRICKS_ARTIFACT_URI: ClassVar[Any] = None + DATABRICKS_METRICS: ClassVar[Any] = None + DATABRICKS_PARAMS: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + ETHICAL_AI_PRIVACY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_FAIRNESS_CONFIG: ClassVar[Any] = None + ETHICAL_AI_BIAS_MITIGATION_CONFIG: ClassVar[Any] = None + ETHICAL_AI_RELIABILITY_AND_SAFETY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_TRANSPARENCY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_ACCOUNTABILITY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_ENVIRONMENTAL_CONSCIOUSNESS_CONFIG: ClassVar[Any] = None + AI_MODEL: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DATABRICKS_AI_MODEL_CONTEXT: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "DatabricksAIModelVersion" + + databricks_id: Union[int, None, UnsetType] = UNSET + """The id of the model, unique to every version.""" + + databricks_run_id: Union[str, None, UnsetType] = UNSET + """The run id of the model.""" + + databricks_run_name: Union[str, None, UnsetType] = UNSET + """The run name of the model.""" + + databricks_run_start_time: Union[int, None, UnsetType] = UNSET + """The run start time of the model.""" + + databricks_run_end_time: Union[int, None, UnsetType] = UNSET + """The run end time of the model.""" + + databricks_status: Union[str, None, UnsetType] = UNSET + """The status of the model.""" + + databricks_aliases: Union[List[str], None, UnsetType] = UNSET + """The aliases of the model.""" + + databricks_dataset_count: Union[int, None, UnsetType] = UNSET + """Number of datasets.""" + + databricks_source: Union[str, None, UnsetType] = UNSET + """Source artifact link for the model.""" + + databricks_artifact_uri: Union[str, None, UnsetType] = UNSET + """Artifact uri for the model.""" + + databricks_metrics: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """Metrics for an individual experiment.""" + + databricks_params: Union[Dict[str, str], None, UnsetType] = UNSET + """Params with key mapped to value for an individual experiment.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + ethical_ai_privacy_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIPrivacyConfig" + ) + """Privacy configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_fairness_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIFairnessConfig" + ) + """Fairness configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_bias_mitigation_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIBiasMitigationConfig" + ) + """Bias mitigation configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_reliability_and_safety_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIReliabilityAndSafetyConfig") + ) + """Reliability and safety configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_transparency_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAITransparencyConfig" + ) + """Transparency configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_accountability_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIAccountabilityConfig" + ) + """Accountability configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_environmental_consciousness_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIEnvironmentalConsciousnessConfig") + ) + """Environmental consciousness configuration for ensuring the ethical use of an AI asset""" + + ai_model: Union[RelatedAIModel, None, UnsetType] = UNSET + """Model containing the versions.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + databricks_ai_model_context: Union[ + RelatedDatabricksAIModelContext, None, UnsetType + ] = msgspec.field(default=UNSET, name="databricksAIModelContext") + """Context containing the version.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "DatabricksAIModelVersion" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _databricks_ai_model_version_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> DatabricksAIModelVersion: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + DatabricksAIModelVersion instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _databricks_ai_model_version_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DatabricksAIModelVersionAttributes(AssetAttributes): + """DatabricksAIModelVersion-specific attributes for nested API format.""" + + databricks_id: Union[int, None, UnsetType] = UNSET + """The id of the model, unique to every version.""" + + databricks_run_id: Union[str, None, UnsetType] = UNSET + """The run id of the model.""" + + databricks_run_name: Union[str, None, UnsetType] = UNSET + """The run name of the model.""" + + databricks_run_start_time: Union[int, None, UnsetType] = UNSET + """The run start time of the model.""" + + databricks_run_end_time: Union[int, None, UnsetType] = UNSET + """The run end time of the model.""" + + databricks_status: Union[str, None, UnsetType] = UNSET + """The status of the model.""" + + databricks_aliases: Union[List[str], None, UnsetType] = UNSET + """The aliases of the model.""" + + databricks_dataset_count: Union[int, None, UnsetType] = UNSET + """Number of datasets.""" + + databricks_source: Union[str, None, UnsetType] = UNSET + """Source artifact link for the model.""" + + databricks_artifact_uri: Union[str, None, UnsetType] = UNSET + """Artifact uri for the model.""" + + databricks_metrics: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """Metrics for an individual experiment.""" + + databricks_params: Union[Dict[str, str], None, UnsetType] = UNSET + """Params with key mapped to value for an individual experiment.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + ethical_ai_privacy_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIPrivacyConfig" + ) + """Privacy configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_fairness_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIFairnessConfig" + ) + """Fairness configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_bias_mitigation_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIBiasMitigationConfig" + ) + """Bias mitigation configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_reliability_and_safety_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIReliabilityAndSafetyConfig") + ) + """Reliability and safety configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_transparency_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAITransparencyConfig" + ) + """Transparency configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_accountability_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIAccountabilityConfig" + ) + """Accountability configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_environmental_consciousness_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIEnvironmentalConsciousnessConfig") + ) + """Environmental consciousness configuration for ensuring the ethical use of an AI asset""" + + +class DatabricksAIModelVersionRelationshipAttributes(AssetRelationshipAttributes): + """DatabricksAIModelVersion-specific relationship attributes for nested API format.""" + + ai_model: Union[RelatedAIModel, None, UnsetType] = UNSET + """Model containing the versions.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + databricks_ai_model_context: Union[ + RelatedDatabricksAIModelContext, None, UnsetType + ] = msgspec.field(default=UNSET, name="databricksAIModelContext") + """Context containing the version.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DatabricksAIModelVersionNested(AssetNested): + """DatabricksAIModelVersion in nested API format for high-performance serialization.""" + + attributes: Union[DatabricksAIModelVersionAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + DatabricksAIModelVersionRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + DatabricksAIModelVersionRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + DatabricksAIModelVersionRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DATABRICKS_AI_MODEL_VERSION_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "ai_model", + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "databricks_ai_model_context", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_databricks_ai_model_version_attrs( + attrs: DatabricksAIModelVersionAttributes, obj: DatabricksAIModelVersion +) -> None: + """Populate DatabricksAIModelVersion-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.databricks_id = obj.databricks_id + attrs.databricks_run_id = obj.databricks_run_id + attrs.databricks_run_name = obj.databricks_run_name + attrs.databricks_run_start_time = obj.databricks_run_start_time + attrs.databricks_run_end_time = obj.databricks_run_end_time + attrs.databricks_status = obj.databricks_status + attrs.databricks_aliases = obj.databricks_aliases + attrs.databricks_dataset_count = obj.databricks_dataset_count + attrs.databricks_source = obj.databricks_source + attrs.databricks_artifact_uri = obj.databricks_artifact_uri + attrs.databricks_metrics = obj.databricks_metrics + attrs.databricks_params = obj.databricks_params + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + attrs.ethical_ai_privacy_config = obj.ethical_ai_privacy_config + attrs.ethical_ai_fairness_config = obj.ethical_ai_fairness_config + attrs.ethical_ai_bias_mitigation_config = obj.ethical_ai_bias_mitigation_config + attrs.ethical_ai_reliability_and_safety_config = ( + obj.ethical_ai_reliability_and_safety_config + ) + attrs.ethical_ai_transparency_config = obj.ethical_ai_transparency_config + attrs.ethical_ai_accountability_config = obj.ethical_ai_accountability_config + attrs.ethical_ai_environmental_consciousness_config = ( + obj.ethical_ai_environmental_consciousness_config + ) + + +def _extract_databricks_ai_model_version_attrs( + attrs: DatabricksAIModelVersionAttributes, +) -> dict: + """Extract all DatabricksAIModelVersion attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["databricks_id"] = attrs.databricks_id + result["databricks_run_id"] = attrs.databricks_run_id + result["databricks_run_name"] = attrs.databricks_run_name + result["databricks_run_start_time"] = attrs.databricks_run_start_time + result["databricks_run_end_time"] = attrs.databricks_run_end_time + result["databricks_status"] = attrs.databricks_status + result["databricks_aliases"] = attrs.databricks_aliases + result["databricks_dataset_count"] = attrs.databricks_dataset_count + result["databricks_source"] = attrs.databricks_source + result["databricks_artifact_uri"] = attrs.databricks_artifact_uri + result["databricks_metrics"] = attrs.databricks_metrics + result["databricks_params"] = attrs.databricks_params + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + result["ethical_ai_privacy_config"] = attrs.ethical_ai_privacy_config + result["ethical_ai_fairness_config"] = attrs.ethical_ai_fairness_config + result["ethical_ai_bias_mitigation_config"] = ( + attrs.ethical_ai_bias_mitigation_config + ) + result["ethical_ai_reliability_and_safety_config"] = ( + attrs.ethical_ai_reliability_and_safety_config + ) + result["ethical_ai_transparency_config"] = attrs.ethical_ai_transparency_config + result["ethical_ai_accountability_config"] = attrs.ethical_ai_accountability_config + result["ethical_ai_environmental_consciousness_config"] = ( + attrs.ethical_ai_environmental_consciousness_config + ) + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _databricks_ai_model_version_to_nested( + databricks_ai_model_version: DatabricksAIModelVersion, +) -> DatabricksAIModelVersionNested: + """Convert flat DatabricksAIModelVersion to nested format.""" + attrs = DatabricksAIModelVersionAttributes() + _populate_databricks_ai_model_version_attrs(attrs, databricks_ai_model_version) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + databricks_ai_model_version, + _DATABRICKS_AI_MODEL_VERSION_REL_FIELDS, + DatabricksAIModelVersionRelationshipAttributes, + ) + return DatabricksAIModelVersionNested( + guid=databricks_ai_model_version.guid, + type_name=databricks_ai_model_version.type_name, + status=databricks_ai_model_version.status, + version=databricks_ai_model_version.version, + create_time=databricks_ai_model_version.create_time, + update_time=databricks_ai_model_version.update_time, + created_by=databricks_ai_model_version.created_by, + updated_by=databricks_ai_model_version.updated_by, + classifications=databricks_ai_model_version.classifications, + classification_names=databricks_ai_model_version.classification_names, + meanings=databricks_ai_model_version.meanings, + labels=databricks_ai_model_version.labels, + business_attributes=databricks_ai_model_version.business_attributes, + custom_attributes=databricks_ai_model_version.custom_attributes, + pending_tasks=databricks_ai_model_version.pending_tasks, + proxy=databricks_ai_model_version.proxy, + is_incomplete=databricks_ai_model_version.is_incomplete, + provenance_type=databricks_ai_model_version.provenance_type, + home_id=databricks_ai_model_version.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _databricks_ai_model_version_from_nested( + nested: DatabricksAIModelVersionNested, +) -> DatabricksAIModelVersion: + """Convert nested format to flat DatabricksAIModelVersion.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else DatabricksAIModelVersionAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DATABRICKS_AI_MODEL_VERSION_REL_FIELDS, + DatabricksAIModelVersionRelationshipAttributes, + ) + return DatabricksAIModelVersion( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_databricks_ai_model_version_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _databricks_ai_model_version_to_nested_bytes( + databricks_ai_model_version: DatabricksAIModelVersion, serde: Serde +) -> bytes: + """Convert flat DatabricksAIModelVersion to nested JSON bytes.""" + return serde.encode( + _databricks_ai_model_version_to_nested(databricks_ai_model_version) + ) + + +def _databricks_ai_model_version_from_nested_bytes( + data: bytes, serde: Serde +) -> DatabricksAIModelVersion: + """Convert nested JSON bytes to flat DatabricksAIModelVersion.""" + nested = serde.decode(data, DatabricksAIModelVersionNested) + return _databricks_ai_model_version_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, +) + +DatabricksAIModelVersion.DATABRICKS_ID = NumericField("databricksId", "databricksId") +DatabricksAIModelVersion.DATABRICKS_RUN_ID = KeywordField( + "databricksRunId", "databricksRunId" +) +DatabricksAIModelVersion.DATABRICKS_RUN_NAME = KeywordField( + "databricksRunName", "databricksRunName" +) +DatabricksAIModelVersion.DATABRICKS_RUN_START_TIME = NumericField( + "databricksRunStartTime", "databricksRunStartTime" +) +DatabricksAIModelVersion.DATABRICKS_RUN_END_TIME = NumericField( + "databricksRunEndTime", "databricksRunEndTime" +) +DatabricksAIModelVersion.DATABRICKS_STATUS = KeywordField( + "databricksStatus", "databricksStatus" +) +DatabricksAIModelVersion.DATABRICKS_ALIASES = KeywordField( + "databricksAliases", "databricksAliases" +) +DatabricksAIModelVersion.DATABRICKS_DATASET_COUNT = NumericField( + "databricksDatasetCount", "databricksDatasetCount" +) +DatabricksAIModelVersion.DATABRICKS_SOURCE = KeywordField( + "databricksSource", "databricksSource" +) +DatabricksAIModelVersion.DATABRICKS_ARTIFACT_URI = KeywordField( + "databricksArtifactUri", "databricksArtifactUri" +) +DatabricksAIModelVersion.DATABRICKS_METRICS = KeywordField( + "databricksMetrics", "databricksMetrics" +) +DatabricksAIModelVersion.DATABRICKS_PARAMS = KeywordField( + "databricksParams", "databricksParams" +) +DatabricksAIModelVersion.QUERY_COUNT = NumericField("queryCount", "queryCount") +DatabricksAIModelVersion.QUERY_USER_COUNT = NumericField( + "queryUserCount", "queryUserCount" +) +DatabricksAIModelVersion.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +DatabricksAIModelVersion.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +DatabricksAIModelVersion.DATABASE_NAME = KeywordField("databaseName", "databaseName") +DatabricksAIModelVersion.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +DatabricksAIModelVersion.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +DatabricksAIModelVersion.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +DatabricksAIModelVersion.TABLE_NAME = KeywordField("tableName", "tableName") +DatabricksAIModelVersion.TABLE_QUALIFIED_NAME = KeywordField( + "tableQualifiedName", "tableQualifiedName" +) +DatabricksAIModelVersion.VIEW_NAME = KeywordField("viewName", "viewName") +DatabricksAIModelVersion.VIEW_QUALIFIED_NAME = KeywordField( + "viewQualifiedName", "viewQualifiedName" +) +DatabricksAIModelVersion.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +DatabricksAIModelVersion.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +DatabricksAIModelVersion.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +DatabricksAIModelVersion.LAST_PROFILED_AT = NumericField( + "lastProfiledAt", "lastProfiledAt" +) +DatabricksAIModelVersion.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +DatabricksAIModelVersion.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +DatabricksAIModelVersion.ETHICAL_AI_PRIVACY_CONFIG = KeywordField( + "ethicalAIPrivacyConfig", "ethicalAIPrivacyConfig" +) +DatabricksAIModelVersion.ETHICAL_AI_FAIRNESS_CONFIG = KeywordField( + "ethicalAIFairnessConfig", "ethicalAIFairnessConfig" +) +DatabricksAIModelVersion.ETHICAL_AI_BIAS_MITIGATION_CONFIG = KeywordField( + "ethicalAIBiasMitigationConfig", "ethicalAIBiasMitigationConfig" +) +DatabricksAIModelVersion.ETHICAL_AI_RELIABILITY_AND_SAFETY_CONFIG = KeywordField( + "ethicalAIReliabilityAndSafetyConfig", "ethicalAIReliabilityAndSafetyConfig" +) +DatabricksAIModelVersion.ETHICAL_AI_TRANSPARENCY_CONFIG = KeywordField( + "ethicalAITransparencyConfig", "ethicalAITransparencyConfig" +) +DatabricksAIModelVersion.ETHICAL_AI_ACCOUNTABILITY_CONFIG = KeywordField( + "ethicalAIAccountabilityConfig", "ethicalAIAccountabilityConfig" +) +DatabricksAIModelVersion.ETHICAL_AI_ENVIRONMENTAL_CONSCIOUSNESS_CONFIG = KeywordField( + "ethicalAIEnvironmentalConsciousnessConfig", + "ethicalAIEnvironmentalConsciousnessConfig", +) +DatabricksAIModelVersion.AI_MODEL = RelationField("aiModel") +DatabricksAIModelVersion.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +DatabricksAIModelVersion.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +DatabricksAIModelVersion.ANOMALO_CHECKS = RelationField("anomaloChecks") +DatabricksAIModelVersion.APPLICATION = RelationField("application") +DatabricksAIModelVersion.APPLICATION_FIELD = RelationField("applicationField") +DatabricksAIModelVersion.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +DatabricksAIModelVersion.INPUT_PORT_DATA_PRODUCTS = RelationField( + "inputPortDataProducts" +) +DatabricksAIModelVersion.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +DatabricksAIModelVersion.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +DatabricksAIModelVersion.METRICS = RelationField("metrics") +DatabricksAIModelVersion.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +DatabricksAIModelVersion.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +DatabricksAIModelVersion.DATABRICKS_AI_MODEL_CONTEXT = RelationField( + "databricksAIModelContext" +) +DatabricksAIModelVersion.DBT_MODELS = RelationField("dbtModels") +DatabricksAIModelVersion.SQL_DBT_MODELS = RelationField("sqlDbtModels") +DatabricksAIModelVersion.DBT_TESTS = RelationField("dbtTests") +DatabricksAIModelVersion.DBT_SOURCES = RelationField("dbtSources") +DatabricksAIModelVersion.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +DatabricksAIModelVersion.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +DatabricksAIModelVersion.MEANINGS = RelationField("meanings") +DatabricksAIModelVersion.MC_MONITORS = RelationField("mcMonitors") +DatabricksAIModelVersion.MC_INCIDENTS = RelationField("mcIncidents") +DatabricksAIModelVersion.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +DatabricksAIModelVersion.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +DatabricksAIModelVersion.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +DatabricksAIModelVersion.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +DatabricksAIModelVersion.USER_DEF_RELATIONSHIP_TO = RelationField( + "userDefRelationshipTo" +) +DatabricksAIModelVersion.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +DatabricksAIModelVersion.FILES = RelationField("files") +DatabricksAIModelVersion.LINKS = RelationField("links") +DatabricksAIModelVersion.README = RelationField("readme") +DatabricksAIModelVersion.SCHEMA_REGISTRY_SUBJECTS = RelationField( + "schemaRegistrySubjects" +) +DatabricksAIModelVersion.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +DatabricksAIModelVersion.SODA_CHECKS = RelationField("sodaChecks") +DatabricksAIModelVersion.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +DatabricksAIModelVersion.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/databricks_external_location.py b/pyatlan_v9/model/assets/databricks_external_location.py new file mode 100644 index 000000000..e524a656d --- /dev/null +++ b/pyatlan_v9/model/assets/databricks_external_location.py @@ -0,0 +1,897 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DatabricksExternalLocation asset model with flattened inheritance. + +This module provides: +- DatabricksExternalLocation: Flat asset class (easy to use) +- DatabricksExternalLocationAttributes: Nested attributes struct (extends AssetAttributes) +- DatabricksExternalLocationNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .snowflake_related import RelatedSnowflakeSemanticLogicalTable +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .databricks_related import RelatedDatabricksExternalLocationPath + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class DatabricksExternalLocation(Asset): + """ + Represents a Databricks External Location, a storage object for managing and accessing data files. + """ + + DATABRICKS_URL: ClassVar[Any] = None + DATABRICKS_OWNER: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DATABRICKS_EXTERNAL_LOCATION_PATHS: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "DatabricksExternalLocation" + + databricks_url: Union[str, None, UnsetType] = UNSET + """URL of the external location.""" + + databricks_owner: Union[str, None, UnsetType] = UNSET + """User or group (principal) currently owning the external location.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + databricks_external_location_paths: Union[ + List[RelatedDatabricksExternalLocationPath], None, UnsetType + ] = UNSET + """Paths contained within the external location.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "DatabricksExternalLocation" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _databricks_external_location_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> DatabricksExternalLocation: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + DatabricksExternalLocation instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _databricks_external_location_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DatabricksExternalLocationAttributes(AssetAttributes): + """DatabricksExternalLocation-specific attributes for nested API format.""" + + databricks_url: Union[str, None, UnsetType] = UNSET + """URL of the external location.""" + + databricks_owner: Union[str, None, UnsetType] = UNSET + """User or group (principal) currently owning the external location.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + +class DatabricksExternalLocationRelationshipAttributes(AssetRelationshipAttributes): + """DatabricksExternalLocation-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + databricks_external_location_paths: Union[ + List[RelatedDatabricksExternalLocationPath], None, UnsetType + ] = UNSET + """Paths contained within the external location.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DatabricksExternalLocationNested(AssetNested): + """DatabricksExternalLocation in nested API format for high-performance serialization.""" + + attributes: Union[DatabricksExternalLocationAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + DatabricksExternalLocationRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + DatabricksExternalLocationRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + DatabricksExternalLocationRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DATABRICKS_EXTERNAL_LOCATION_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "databricks_external_location_paths", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_databricks_external_location_attrs( + attrs: DatabricksExternalLocationAttributes, obj: DatabricksExternalLocation +) -> None: + """Populate DatabricksExternalLocation-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.databricks_url = obj.databricks_url + attrs.databricks_owner = obj.databricks_owner + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + + +def _extract_databricks_external_location_attrs( + attrs: DatabricksExternalLocationAttributes, +) -> dict: + """Extract all DatabricksExternalLocation attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["databricks_url"] = attrs.databricks_url + result["databricks_owner"] = attrs.databricks_owner + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _databricks_external_location_to_nested( + databricks_external_location: DatabricksExternalLocation, +) -> DatabricksExternalLocationNested: + """Convert flat DatabricksExternalLocation to nested format.""" + attrs = DatabricksExternalLocationAttributes() + _populate_databricks_external_location_attrs(attrs, databricks_external_location) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + databricks_external_location, + _DATABRICKS_EXTERNAL_LOCATION_REL_FIELDS, + DatabricksExternalLocationRelationshipAttributes, + ) + return DatabricksExternalLocationNested( + guid=databricks_external_location.guid, + type_name=databricks_external_location.type_name, + status=databricks_external_location.status, + version=databricks_external_location.version, + create_time=databricks_external_location.create_time, + update_time=databricks_external_location.update_time, + created_by=databricks_external_location.created_by, + updated_by=databricks_external_location.updated_by, + classifications=databricks_external_location.classifications, + classification_names=databricks_external_location.classification_names, + meanings=databricks_external_location.meanings, + labels=databricks_external_location.labels, + business_attributes=databricks_external_location.business_attributes, + custom_attributes=databricks_external_location.custom_attributes, + pending_tasks=databricks_external_location.pending_tasks, + proxy=databricks_external_location.proxy, + is_incomplete=databricks_external_location.is_incomplete, + provenance_type=databricks_external_location.provenance_type, + home_id=databricks_external_location.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _databricks_external_location_from_nested( + nested: DatabricksExternalLocationNested, +) -> DatabricksExternalLocation: + """Convert nested format to flat DatabricksExternalLocation.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else DatabricksExternalLocationAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DATABRICKS_EXTERNAL_LOCATION_REL_FIELDS, + DatabricksExternalLocationRelationshipAttributes, + ) + return DatabricksExternalLocation( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_databricks_external_location_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _databricks_external_location_to_nested_bytes( + databricks_external_location: DatabricksExternalLocation, serde: Serde +) -> bytes: + """Convert flat DatabricksExternalLocation to nested JSON bytes.""" + return serde.encode( + _databricks_external_location_to_nested(databricks_external_location) + ) + + +def _databricks_external_location_from_nested_bytes( + data: bytes, serde: Serde +) -> DatabricksExternalLocation: + """Convert nested JSON bytes to flat DatabricksExternalLocation.""" + nested = serde.decode(data, DatabricksExternalLocationNested) + return _databricks_external_location_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, +) + +DatabricksExternalLocation.DATABRICKS_URL = KeywordField( + "databricksUrl", "databricksUrl" +) +DatabricksExternalLocation.DATABRICKS_OWNER = KeywordField( + "databricksOwner", "databricksOwner" +) +DatabricksExternalLocation.QUERY_COUNT = NumericField("queryCount", "queryCount") +DatabricksExternalLocation.QUERY_USER_COUNT = NumericField( + "queryUserCount", "queryUserCount" +) +DatabricksExternalLocation.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +DatabricksExternalLocation.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +DatabricksExternalLocation.DATABASE_NAME = KeywordField("databaseName", "databaseName") +DatabricksExternalLocation.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +DatabricksExternalLocation.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +DatabricksExternalLocation.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +DatabricksExternalLocation.TABLE_NAME = KeywordField("tableName", "tableName") +DatabricksExternalLocation.TABLE_QUALIFIED_NAME = KeywordField( + "tableQualifiedName", "tableQualifiedName" +) +DatabricksExternalLocation.VIEW_NAME = KeywordField("viewName", "viewName") +DatabricksExternalLocation.VIEW_QUALIFIED_NAME = KeywordField( + "viewQualifiedName", "viewQualifiedName" +) +DatabricksExternalLocation.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +DatabricksExternalLocation.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +DatabricksExternalLocation.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +DatabricksExternalLocation.LAST_PROFILED_AT = NumericField( + "lastProfiledAt", "lastProfiledAt" +) +DatabricksExternalLocation.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +DatabricksExternalLocation.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +DatabricksExternalLocation.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +DatabricksExternalLocation.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +DatabricksExternalLocation.ANOMALO_CHECKS = RelationField("anomaloChecks") +DatabricksExternalLocation.APPLICATION = RelationField("application") +DatabricksExternalLocation.APPLICATION_FIELD = RelationField("applicationField") +DatabricksExternalLocation.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +DatabricksExternalLocation.INPUT_PORT_DATA_PRODUCTS = RelationField( + "inputPortDataProducts" +) +DatabricksExternalLocation.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +DatabricksExternalLocation.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +DatabricksExternalLocation.METRICS = RelationField("metrics") +DatabricksExternalLocation.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +DatabricksExternalLocation.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +DatabricksExternalLocation.DATABRICKS_EXTERNAL_LOCATION_PATHS = RelationField( + "databricksExternalLocationPaths" +) +DatabricksExternalLocation.DBT_MODELS = RelationField("dbtModels") +DatabricksExternalLocation.SQL_DBT_MODELS = RelationField("sqlDbtModels") +DatabricksExternalLocation.DBT_TESTS = RelationField("dbtTests") +DatabricksExternalLocation.DBT_SOURCES = RelationField("dbtSources") +DatabricksExternalLocation.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +DatabricksExternalLocation.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +DatabricksExternalLocation.MEANINGS = RelationField("meanings") +DatabricksExternalLocation.MC_MONITORS = RelationField("mcMonitors") +DatabricksExternalLocation.MC_INCIDENTS = RelationField("mcIncidents") +DatabricksExternalLocation.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +DatabricksExternalLocation.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +DatabricksExternalLocation.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +DatabricksExternalLocation.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +DatabricksExternalLocation.USER_DEF_RELATIONSHIP_TO = RelationField( + "userDefRelationshipTo" +) +DatabricksExternalLocation.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +DatabricksExternalLocation.FILES = RelationField("files") +DatabricksExternalLocation.LINKS = RelationField("links") +DatabricksExternalLocation.README = RelationField("readme") +DatabricksExternalLocation.SCHEMA_REGISTRY_SUBJECTS = RelationField( + "schemaRegistrySubjects" +) +DatabricksExternalLocation.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +DatabricksExternalLocation.SODA_CHECKS = RelationField("sodaChecks") +DatabricksExternalLocation.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +DatabricksExternalLocation.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/databricks_external_location_path.py b/pyatlan_v9/model/assets/databricks_external_location_path.py new file mode 100644 index 000000000..0d5a0e736 --- /dev/null +++ b/pyatlan_v9/model/assets/databricks_external_location_path.py @@ -0,0 +1,936 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DatabricksExternalLocationPath asset model with flattened inheritance. + +This module provides: +- DatabricksExternalLocationPath: Flat asset class (easy to use) +- DatabricksExternalLocationPathAttributes: Nested attributes struct (extends AssetAttributes) +- DatabricksExternalLocationPathNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .snowflake_related import RelatedSnowflakeSemanticLogicalTable +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .databricks_related import RelatedDatabricksExternalLocation + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class DatabricksExternalLocationPath(Asset): + """ + Represents a path within a Databricks External Location, providing access to specific data files or directories in external storage. + """ + + DATABRICKS_PATH: ClassVar[Any] = None + DATABRICKS_PARENT_QUALIFIED_NAME: ClassVar[Any] = None + DATABRICKS_PARENT_NAME: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DATABRICKS_EXTERNAL_LOCATION: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "DatabricksExternalLocationPath" + + databricks_path: Union[str, None, UnsetType] = UNSET + """Path of data at the external location.""" + + databricks_parent_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the parent external location.""" + + databricks_parent_name: Union[str, None, UnsetType] = UNSET + """Name of the parent external location.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + databricks_external_location: Union[ + RelatedDatabricksExternalLocation, None, UnsetType + ] = UNSET + """External location that contains the paths.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "DatabricksExternalLocationPath" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _databricks_external_location_path_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> DatabricksExternalLocationPath: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + DatabricksExternalLocationPath instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _databricks_external_location_path_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DatabricksExternalLocationPathAttributes(AssetAttributes): + """DatabricksExternalLocationPath-specific attributes for nested API format.""" + + databricks_path: Union[str, None, UnsetType] = UNSET + """Path of data at the external location.""" + + databricks_parent_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the parent external location.""" + + databricks_parent_name: Union[str, None, UnsetType] = UNSET + """Name of the parent external location.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + +class DatabricksExternalLocationPathRelationshipAttributes(AssetRelationshipAttributes): + """DatabricksExternalLocationPath-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + databricks_external_location: Union[ + RelatedDatabricksExternalLocation, None, UnsetType + ] = UNSET + """External location that contains the paths.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DatabricksExternalLocationPathNested(AssetNested): + """DatabricksExternalLocationPath in nested API format for high-performance serialization.""" + + attributes: Union[DatabricksExternalLocationPathAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + DatabricksExternalLocationPathRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + DatabricksExternalLocationPathRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + DatabricksExternalLocationPathRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DATABRICKS_EXTERNAL_LOCATION_PATH_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "databricks_external_location", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_databricks_external_location_path_attrs( + attrs: DatabricksExternalLocationPathAttributes, obj: DatabricksExternalLocationPath +) -> None: + """Populate DatabricksExternalLocationPath-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.databricks_path = obj.databricks_path + attrs.databricks_parent_qualified_name = obj.databricks_parent_qualified_name + attrs.databricks_parent_name = obj.databricks_parent_name + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + + +def _extract_databricks_external_location_path_attrs( + attrs: DatabricksExternalLocationPathAttributes, +) -> dict: + """Extract all DatabricksExternalLocationPath attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["databricks_path"] = attrs.databricks_path + result["databricks_parent_qualified_name"] = attrs.databricks_parent_qualified_name + result["databricks_parent_name"] = attrs.databricks_parent_name + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _databricks_external_location_path_to_nested( + databricks_external_location_path: DatabricksExternalLocationPath, +) -> DatabricksExternalLocationPathNested: + """Convert flat DatabricksExternalLocationPath to nested format.""" + attrs = DatabricksExternalLocationPathAttributes() + _populate_databricks_external_location_path_attrs( + attrs, databricks_external_location_path + ) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + databricks_external_location_path, + _DATABRICKS_EXTERNAL_LOCATION_PATH_REL_FIELDS, + DatabricksExternalLocationPathRelationshipAttributes, + ) + return DatabricksExternalLocationPathNested( + guid=databricks_external_location_path.guid, + type_name=databricks_external_location_path.type_name, + status=databricks_external_location_path.status, + version=databricks_external_location_path.version, + create_time=databricks_external_location_path.create_time, + update_time=databricks_external_location_path.update_time, + created_by=databricks_external_location_path.created_by, + updated_by=databricks_external_location_path.updated_by, + classifications=databricks_external_location_path.classifications, + classification_names=databricks_external_location_path.classification_names, + meanings=databricks_external_location_path.meanings, + labels=databricks_external_location_path.labels, + business_attributes=databricks_external_location_path.business_attributes, + custom_attributes=databricks_external_location_path.custom_attributes, + pending_tasks=databricks_external_location_path.pending_tasks, + proxy=databricks_external_location_path.proxy, + is_incomplete=databricks_external_location_path.is_incomplete, + provenance_type=databricks_external_location_path.provenance_type, + home_id=databricks_external_location_path.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _databricks_external_location_path_from_nested( + nested: DatabricksExternalLocationPathNested, +) -> DatabricksExternalLocationPath: + """Convert nested format to flat DatabricksExternalLocationPath.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else DatabricksExternalLocationPathAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DATABRICKS_EXTERNAL_LOCATION_PATH_REL_FIELDS, + DatabricksExternalLocationPathRelationshipAttributes, + ) + return DatabricksExternalLocationPath( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_databricks_external_location_path_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _databricks_external_location_path_to_nested_bytes( + databricks_external_location_path: DatabricksExternalLocationPath, serde: Serde +) -> bytes: + """Convert flat DatabricksExternalLocationPath to nested JSON bytes.""" + return serde.encode( + _databricks_external_location_path_to_nested(databricks_external_location_path) + ) + + +def _databricks_external_location_path_from_nested_bytes( + data: bytes, serde: Serde +) -> DatabricksExternalLocationPath: + """Convert nested JSON bytes to flat DatabricksExternalLocationPath.""" + nested = serde.decode(data, DatabricksExternalLocationPathNested) + return _databricks_external_location_path_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, +) + +DatabricksExternalLocationPath.DATABRICKS_PATH = KeywordField( + "databricksPath", "databricksPath" +) +DatabricksExternalLocationPath.DATABRICKS_PARENT_QUALIFIED_NAME = KeywordField( + "databricksParentQualifiedName", "databricksParentQualifiedName" +) +DatabricksExternalLocationPath.DATABRICKS_PARENT_NAME = KeywordField( + "databricksParentName", "databricksParentName" +) +DatabricksExternalLocationPath.QUERY_COUNT = NumericField("queryCount", "queryCount") +DatabricksExternalLocationPath.QUERY_USER_COUNT = NumericField( + "queryUserCount", "queryUserCount" +) +DatabricksExternalLocationPath.QUERY_USER_MAP = KeywordField( + "queryUserMap", "queryUserMap" +) +DatabricksExternalLocationPath.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +DatabricksExternalLocationPath.DATABASE_NAME = KeywordField( + "databaseName", "databaseName" +) +DatabricksExternalLocationPath.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +DatabricksExternalLocationPath.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +DatabricksExternalLocationPath.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +DatabricksExternalLocationPath.TABLE_NAME = KeywordField("tableName", "tableName") +DatabricksExternalLocationPath.TABLE_QUALIFIED_NAME = KeywordField( + "tableQualifiedName", "tableQualifiedName" +) +DatabricksExternalLocationPath.VIEW_NAME = KeywordField("viewName", "viewName") +DatabricksExternalLocationPath.VIEW_QUALIFIED_NAME = KeywordField( + "viewQualifiedName", "viewQualifiedName" +) +DatabricksExternalLocationPath.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +DatabricksExternalLocationPath.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +DatabricksExternalLocationPath.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +DatabricksExternalLocationPath.LAST_PROFILED_AT = NumericField( + "lastProfiledAt", "lastProfiledAt" +) +DatabricksExternalLocationPath.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +DatabricksExternalLocationPath.SQL_IS_SECURE = BooleanField( + "sqlIsSecure", "sqlIsSecure" +) +DatabricksExternalLocationPath.INPUT_TO_AIRFLOW_TASKS = RelationField( + "inputToAirflowTasks" +) +DatabricksExternalLocationPath.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +DatabricksExternalLocationPath.ANOMALO_CHECKS = RelationField("anomaloChecks") +DatabricksExternalLocationPath.APPLICATION = RelationField("application") +DatabricksExternalLocationPath.APPLICATION_FIELD = RelationField("applicationField") +DatabricksExternalLocationPath.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +DatabricksExternalLocationPath.INPUT_PORT_DATA_PRODUCTS = RelationField( + "inputPortDataProducts" +) +DatabricksExternalLocationPath.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +DatabricksExternalLocationPath.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +DatabricksExternalLocationPath.METRICS = RelationField("metrics") +DatabricksExternalLocationPath.DQ_BASE_DATASET_RULES = RelationField( + "dqBaseDatasetRules" +) +DatabricksExternalLocationPath.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +DatabricksExternalLocationPath.DATABRICKS_EXTERNAL_LOCATION = RelationField( + "databricksExternalLocation" +) +DatabricksExternalLocationPath.DBT_MODELS = RelationField("dbtModels") +DatabricksExternalLocationPath.SQL_DBT_MODELS = RelationField("sqlDbtModels") +DatabricksExternalLocationPath.DBT_TESTS = RelationField("dbtTests") +DatabricksExternalLocationPath.DBT_SOURCES = RelationField("dbtSources") +DatabricksExternalLocationPath.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +DatabricksExternalLocationPath.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +DatabricksExternalLocationPath.MEANINGS = RelationField("meanings") +DatabricksExternalLocationPath.MC_MONITORS = RelationField("mcMonitors") +DatabricksExternalLocationPath.MC_INCIDENTS = RelationField("mcIncidents") +DatabricksExternalLocationPath.PARTIAL_CHILD_FIELDS = RelationField( + "partialChildFields" +) +DatabricksExternalLocationPath.PARTIAL_CHILD_OBJECTS = RelationField( + "partialChildObjects" +) +DatabricksExternalLocationPath.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +DatabricksExternalLocationPath.OUTPUT_FROM_PROCESSES = RelationField( + "outputFromProcesses" +) +DatabricksExternalLocationPath.USER_DEF_RELATIONSHIP_TO = RelationField( + "userDefRelationshipTo" +) +DatabricksExternalLocationPath.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +DatabricksExternalLocationPath.FILES = RelationField("files") +DatabricksExternalLocationPath.LINKS = RelationField("links") +DatabricksExternalLocationPath.README = RelationField("readme") +DatabricksExternalLocationPath.SCHEMA_REGISTRY_SUBJECTS = RelationField( + "schemaRegistrySubjects" +) +DatabricksExternalLocationPath.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +DatabricksExternalLocationPath.SODA_CHECKS = RelationField("sodaChecks") +DatabricksExternalLocationPath.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +DatabricksExternalLocationPath.OUTPUT_FROM_SPARK_JOBS = RelationField( + "outputFromSparkJobs" +) diff --git a/pyatlan_v9/model/assets/databricks_metric_view.py b/pyatlan_v9/model/assets/databricks_metric_view.py new file mode 100644 index 000000000..0a5bbfb94 --- /dev/null +++ b/pyatlan_v9/model/assets/databricks_metric_view.py @@ -0,0 +1,950 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DatabricksMetricView asset model with flattened inheritance. + +This module provides: +- DatabricksMetricView: Flat asset class (easy to use) +- DatabricksMetricViewAttributes: Nested attributes struct (extends AssetAttributes) +- DatabricksMetricViewNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .snowflake_related import RelatedSnowflakeSemanticLogicalTable +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from .sql_related import RelatedColumn, RelatedQuery, RelatedSchema +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class DatabricksMetricView(Asset): + """ + Instance of a Databricks metric view in Atlan. + """ + + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + COLUMN_COUNT: ClassVar[Any] = None + ROW_COUNT: ClassVar[Any] = None + SIZE_BYTES: ClassVar[Any] = None + IS_QUERY_PREVIEW: ClassVar[Any] = None + QUERY_PREVIEW_CONFIG: ClassVar[Any] = None + ALIAS: ClassVar[Any] = None + IS_TEMPORARY: ClassVar[Any] = None + DEFINITION: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + COLUMNS: ClassVar[Any] = None + QUERIES: ClassVar[Any] = None + ATLAN_SCHEMA: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "DatabricksMetricView" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this view.""" + + row_count: Union[int, None, UnsetType] = UNSET + """Number of rows in this view.""" + + size_bytes: Union[int, None, UnsetType] = UNSET + """Size of this view, in bytes.""" + + is_query_preview: Union[bool, None, UnsetType] = UNSET + """Whether preview queries are allowed on this view (true) or not (false).""" + + query_preview_config: Union[Dict[str, str], None, UnsetType] = UNSET + """Configuration for preview queries on this view.""" + + alias: Union[str, None, UnsetType] = UNSET + """Alias for this view.""" + + is_temporary: Union[bool, None, UnsetType] = UNSET + """Whether this view is temporary (true) or not (false).""" + + definition: Union[str, None, UnsetType] = UNSET + """SQL definition of this view.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Columns that exist within this view.""" + + queries: Union[List[RelatedQuery], None, UnsetType] = UNSET + """Queries that access this view.""" + + atlan_schema: Union[RelatedSchema, None, UnsetType] = UNSET + """Schema in which this view exists.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "DatabricksMetricView" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _databricks_metric_view_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> DatabricksMetricView: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + DatabricksMetricView instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _databricks_metric_view_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DatabricksMetricViewAttributes(AssetAttributes): + """DatabricksMetricView-specific attributes for nested API format.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this view.""" + + row_count: Union[int, None, UnsetType] = UNSET + """Number of rows in this view.""" + + size_bytes: Union[int, None, UnsetType] = UNSET + """Size of this view, in bytes.""" + + is_query_preview: Union[bool, None, UnsetType] = UNSET + """Whether preview queries are allowed on this view (true) or not (false).""" + + query_preview_config: Union[Dict[str, str], None, UnsetType] = UNSET + """Configuration for preview queries on this view.""" + + alias: Union[str, None, UnsetType] = UNSET + """Alias for this view.""" + + is_temporary: Union[bool, None, UnsetType] = UNSET + """Whether this view is temporary (true) or not (false).""" + + definition: Union[str, None, UnsetType] = UNSET + """SQL definition of this view.""" + + +class DatabricksMetricViewRelationshipAttributes(AssetRelationshipAttributes): + """DatabricksMetricView-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Columns that exist within this view.""" + + queries: Union[List[RelatedQuery], None, UnsetType] = UNSET + """Queries that access this view.""" + + atlan_schema: Union[RelatedSchema, None, UnsetType] = UNSET + """Schema in which this view exists.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DatabricksMetricViewNested(AssetNested): + """DatabricksMetricView in nested API format for high-performance serialization.""" + + attributes: Union[DatabricksMetricViewAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + DatabricksMetricViewRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + DatabricksMetricViewRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + DatabricksMetricViewRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DATABRICKS_METRIC_VIEW_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "columns", + "queries", + "atlan_schema", + "schema_registry_subjects", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_databricks_metric_view_attrs( + attrs: DatabricksMetricViewAttributes, obj: DatabricksMetricView +) -> None: + """Populate DatabricksMetricView-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + attrs.column_count = obj.column_count + attrs.row_count = obj.row_count + attrs.size_bytes = obj.size_bytes + attrs.is_query_preview = obj.is_query_preview + attrs.query_preview_config = obj.query_preview_config + attrs.alias = obj.alias + attrs.is_temporary = obj.is_temporary + attrs.definition = obj.definition + + +def _extract_databricks_metric_view_attrs( + attrs: DatabricksMetricViewAttributes, +) -> dict: + """Extract all DatabricksMetricView attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + result["column_count"] = attrs.column_count + result["row_count"] = attrs.row_count + result["size_bytes"] = attrs.size_bytes + result["is_query_preview"] = attrs.is_query_preview + result["query_preview_config"] = attrs.query_preview_config + result["alias"] = attrs.alias + result["is_temporary"] = attrs.is_temporary + result["definition"] = attrs.definition + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _databricks_metric_view_to_nested( + databricks_metric_view: DatabricksMetricView, +) -> DatabricksMetricViewNested: + """Convert flat DatabricksMetricView to nested format.""" + attrs = DatabricksMetricViewAttributes() + _populate_databricks_metric_view_attrs(attrs, databricks_metric_view) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + databricks_metric_view, + _DATABRICKS_METRIC_VIEW_REL_FIELDS, + DatabricksMetricViewRelationshipAttributes, + ) + return DatabricksMetricViewNested( + guid=databricks_metric_view.guid, + type_name=databricks_metric_view.type_name, + status=databricks_metric_view.status, + version=databricks_metric_view.version, + create_time=databricks_metric_view.create_time, + update_time=databricks_metric_view.update_time, + created_by=databricks_metric_view.created_by, + updated_by=databricks_metric_view.updated_by, + classifications=databricks_metric_view.classifications, + classification_names=databricks_metric_view.classification_names, + meanings=databricks_metric_view.meanings, + labels=databricks_metric_view.labels, + business_attributes=databricks_metric_view.business_attributes, + custom_attributes=databricks_metric_view.custom_attributes, + pending_tasks=databricks_metric_view.pending_tasks, + proxy=databricks_metric_view.proxy, + is_incomplete=databricks_metric_view.is_incomplete, + provenance_type=databricks_metric_view.provenance_type, + home_id=databricks_metric_view.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _databricks_metric_view_from_nested( + nested: DatabricksMetricViewNested, +) -> DatabricksMetricView: + """Convert nested format to flat DatabricksMetricView.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else DatabricksMetricViewAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DATABRICKS_METRIC_VIEW_REL_FIELDS, + DatabricksMetricViewRelationshipAttributes, + ) + return DatabricksMetricView( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_databricks_metric_view_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _databricks_metric_view_to_nested_bytes( + databricks_metric_view: DatabricksMetricView, serde: Serde +) -> bytes: + """Convert flat DatabricksMetricView to nested JSON bytes.""" + return serde.encode(_databricks_metric_view_to_nested(databricks_metric_view)) + + +def _databricks_metric_view_from_nested_bytes( + data: bytes, serde: Serde +) -> DatabricksMetricView: + """Convert nested JSON bytes to flat DatabricksMetricView.""" + nested = serde.decode(data, DatabricksMetricViewNested) + return _databricks_metric_view_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, +) + +DatabricksMetricView.QUERY_COUNT = NumericField("queryCount", "queryCount") +DatabricksMetricView.QUERY_USER_COUNT = NumericField("queryUserCount", "queryUserCount") +DatabricksMetricView.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +DatabricksMetricView.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +DatabricksMetricView.DATABASE_NAME = KeywordField("databaseName", "databaseName") +DatabricksMetricView.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +DatabricksMetricView.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +DatabricksMetricView.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +DatabricksMetricView.TABLE_NAME = KeywordField("tableName", "tableName") +DatabricksMetricView.TABLE_QUALIFIED_NAME = KeywordField( + "tableQualifiedName", "tableQualifiedName" +) +DatabricksMetricView.VIEW_NAME = KeywordField("viewName", "viewName") +DatabricksMetricView.VIEW_QUALIFIED_NAME = KeywordField( + "viewQualifiedName", "viewQualifiedName" +) +DatabricksMetricView.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +DatabricksMetricView.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +DatabricksMetricView.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +DatabricksMetricView.LAST_PROFILED_AT = NumericField("lastProfiledAt", "lastProfiledAt") +DatabricksMetricView.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +DatabricksMetricView.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +DatabricksMetricView.COLUMN_COUNT = NumericField("columnCount", "columnCount") +DatabricksMetricView.ROW_COUNT = NumericField("rowCount", "rowCount") +DatabricksMetricView.SIZE_BYTES = NumericField("sizeBytes", "sizeBytes") +DatabricksMetricView.IS_QUERY_PREVIEW = BooleanField("isQueryPreview", "isQueryPreview") +DatabricksMetricView.QUERY_PREVIEW_CONFIG = KeywordField( + "queryPreviewConfig", "queryPreviewConfig" +) +DatabricksMetricView.ALIAS = KeywordField("alias", "alias") +DatabricksMetricView.IS_TEMPORARY = BooleanField("isTemporary", "isTemporary") +DatabricksMetricView.DEFINITION = KeywordField("definition", "definition") +DatabricksMetricView.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +DatabricksMetricView.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +DatabricksMetricView.ANOMALO_CHECKS = RelationField("anomaloChecks") +DatabricksMetricView.APPLICATION = RelationField("application") +DatabricksMetricView.APPLICATION_FIELD = RelationField("applicationField") +DatabricksMetricView.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +DatabricksMetricView.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +DatabricksMetricView.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +DatabricksMetricView.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +DatabricksMetricView.METRICS = RelationField("metrics") +DatabricksMetricView.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +DatabricksMetricView.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +DatabricksMetricView.DBT_MODELS = RelationField("dbtModels") +DatabricksMetricView.SQL_DBT_MODELS = RelationField("sqlDbtModels") +DatabricksMetricView.DBT_TESTS = RelationField("dbtTests") +DatabricksMetricView.DBT_SOURCES = RelationField("dbtSources") +DatabricksMetricView.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +DatabricksMetricView.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +DatabricksMetricView.MEANINGS = RelationField("meanings") +DatabricksMetricView.MC_MONITORS = RelationField("mcMonitors") +DatabricksMetricView.MC_INCIDENTS = RelationField("mcIncidents") +DatabricksMetricView.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +DatabricksMetricView.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +DatabricksMetricView.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +DatabricksMetricView.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +DatabricksMetricView.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +DatabricksMetricView.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +DatabricksMetricView.FILES = RelationField("files") +DatabricksMetricView.LINKS = RelationField("links") +DatabricksMetricView.README = RelationField("readme") +DatabricksMetricView.COLUMNS = RelationField("columns") +DatabricksMetricView.QUERIES = RelationField("queries") +DatabricksMetricView.ATLAN_SCHEMA = RelationField("atlanSchema") +DatabricksMetricView.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +DatabricksMetricView.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +DatabricksMetricView.SODA_CHECKS = RelationField("sodaChecks") +DatabricksMetricView.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +DatabricksMetricView.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/databricks_notebook.py b/pyatlan_v9/model/assets/databricks_notebook.py new file mode 100644 index 000000000..0b4478fa9 --- /dev/null +++ b/pyatlan_v9/model/assets/databricks_notebook.py @@ -0,0 +1,856 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DatabricksNotebook asset model with flattened inheritance. + +This module provides: +- DatabricksNotebook: Flat asset class (easy to use) +- DatabricksNotebookAttributes: Nested attributes struct (extends AssetAttributes) +- DatabricksNotebookNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .snowflake_related import RelatedSnowflakeSemanticLogicalTable +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class DatabricksNotebook(Asset): + """ + Base class for all databricks notebook assets. + """ + + DATABRICKS_PATH: ClassVar[Any] = None + DATABRICKS_WORKSPACE_ID: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "DatabricksNotebook" + + databricks_path: Union[str, None, UnsetType] = UNSET + """Path of the notebook.""" + + databricks_workspace_id: Union[str, None, UnsetType] = UNSET + """Workspace Id of the notebook.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "DatabricksNotebook" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _databricks_notebook_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> DatabricksNotebook: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + DatabricksNotebook instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _databricks_notebook_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DatabricksNotebookAttributes(AssetAttributes): + """DatabricksNotebook-specific attributes for nested API format.""" + + databricks_path: Union[str, None, UnsetType] = UNSET + """Path of the notebook.""" + + databricks_workspace_id: Union[str, None, UnsetType] = UNSET + """Workspace Id of the notebook.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + +class DatabricksNotebookRelationshipAttributes(AssetRelationshipAttributes): + """DatabricksNotebook-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DatabricksNotebookNested(AssetNested): + """DatabricksNotebook in nested API format for high-performance serialization.""" + + attributes: Union[DatabricksNotebookAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + DatabricksNotebookRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + DatabricksNotebookRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + DatabricksNotebookRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DATABRICKS_NOTEBOOK_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_databricks_notebook_attrs( + attrs: DatabricksNotebookAttributes, obj: DatabricksNotebook +) -> None: + """Populate DatabricksNotebook-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.databricks_path = obj.databricks_path + attrs.databricks_workspace_id = obj.databricks_workspace_id + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + + +def _extract_databricks_notebook_attrs(attrs: DatabricksNotebookAttributes) -> dict: + """Extract all DatabricksNotebook attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["databricks_path"] = attrs.databricks_path + result["databricks_workspace_id"] = attrs.databricks_workspace_id + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _databricks_notebook_to_nested( + databricks_notebook: DatabricksNotebook, +) -> DatabricksNotebookNested: + """Convert flat DatabricksNotebook to nested format.""" + attrs = DatabricksNotebookAttributes() + _populate_databricks_notebook_attrs(attrs, databricks_notebook) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + databricks_notebook, + _DATABRICKS_NOTEBOOK_REL_FIELDS, + DatabricksNotebookRelationshipAttributes, + ) + return DatabricksNotebookNested( + guid=databricks_notebook.guid, + type_name=databricks_notebook.type_name, + status=databricks_notebook.status, + version=databricks_notebook.version, + create_time=databricks_notebook.create_time, + update_time=databricks_notebook.update_time, + created_by=databricks_notebook.created_by, + updated_by=databricks_notebook.updated_by, + classifications=databricks_notebook.classifications, + classification_names=databricks_notebook.classification_names, + meanings=databricks_notebook.meanings, + labels=databricks_notebook.labels, + business_attributes=databricks_notebook.business_attributes, + custom_attributes=databricks_notebook.custom_attributes, + pending_tasks=databricks_notebook.pending_tasks, + proxy=databricks_notebook.proxy, + is_incomplete=databricks_notebook.is_incomplete, + provenance_type=databricks_notebook.provenance_type, + home_id=databricks_notebook.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _databricks_notebook_from_nested( + nested: DatabricksNotebookNested, +) -> DatabricksNotebook: + """Convert nested format to flat DatabricksNotebook.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else DatabricksNotebookAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DATABRICKS_NOTEBOOK_REL_FIELDS, + DatabricksNotebookRelationshipAttributes, + ) + return DatabricksNotebook( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_databricks_notebook_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _databricks_notebook_to_nested_bytes( + databricks_notebook: DatabricksNotebook, serde: Serde +) -> bytes: + """Convert flat DatabricksNotebook to nested JSON bytes.""" + return serde.encode(_databricks_notebook_to_nested(databricks_notebook)) + + +def _databricks_notebook_from_nested_bytes( + data: bytes, serde: Serde +) -> DatabricksNotebook: + """Convert nested JSON bytes to flat DatabricksNotebook.""" + nested = serde.decode(data, DatabricksNotebookNested) + return _databricks_notebook_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, +) + +DatabricksNotebook.DATABRICKS_PATH = KeywordField("databricksPath", "databricksPath") +DatabricksNotebook.DATABRICKS_WORKSPACE_ID = KeywordField( + "databricksWorkspaceId", "databricksWorkspaceId" +) +DatabricksNotebook.QUERY_COUNT = NumericField("queryCount", "queryCount") +DatabricksNotebook.QUERY_USER_COUNT = NumericField("queryUserCount", "queryUserCount") +DatabricksNotebook.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +DatabricksNotebook.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +DatabricksNotebook.DATABASE_NAME = KeywordField("databaseName", "databaseName") +DatabricksNotebook.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +DatabricksNotebook.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +DatabricksNotebook.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +DatabricksNotebook.TABLE_NAME = KeywordField("tableName", "tableName") +DatabricksNotebook.TABLE_QUALIFIED_NAME = KeywordField( + "tableQualifiedName", "tableQualifiedName" +) +DatabricksNotebook.VIEW_NAME = KeywordField("viewName", "viewName") +DatabricksNotebook.VIEW_QUALIFIED_NAME = KeywordField( + "viewQualifiedName", "viewQualifiedName" +) +DatabricksNotebook.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +DatabricksNotebook.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +DatabricksNotebook.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +DatabricksNotebook.LAST_PROFILED_AT = NumericField("lastProfiledAt", "lastProfiledAt") +DatabricksNotebook.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +DatabricksNotebook.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +DatabricksNotebook.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +DatabricksNotebook.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +DatabricksNotebook.ANOMALO_CHECKS = RelationField("anomaloChecks") +DatabricksNotebook.APPLICATION = RelationField("application") +DatabricksNotebook.APPLICATION_FIELD = RelationField("applicationField") +DatabricksNotebook.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +DatabricksNotebook.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +DatabricksNotebook.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +DatabricksNotebook.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +DatabricksNotebook.METRICS = RelationField("metrics") +DatabricksNotebook.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +DatabricksNotebook.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +DatabricksNotebook.DBT_MODELS = RelationField("dbtModels") +DatabricksNotebook.SQL_DBT_MODELS = RelationField("sqlDbtModels") +DatabricksNotebook.DBT_TESTS = RelationField("dbtTests") +DatabricksNotebook.DBT_SOURCES = RelationField("dbtSources") +DatabricksNotebook.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +DatabricksNotebook.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +DatabricksNotebook.MEANINGS = RelationField("meanings") +DatabricksNotebook.MC_MONITORS = RelationField("mcMonitors") +DatabricksNotebook.MC_INCIDENTS = RelationField("mcIncidents") +DatabricksNotebook.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +DatabricksNotebook.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +DatabricksNotebook.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +DatabricksNotebook.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +DatabricksNotebook.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +DatabricksNotebook.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +DatabricksNotebook.FILES = RelationField("files") +DatabricksNotebook.LINKS = RelationField("links") +DatabricksNotebook.README = RelationField("readme") +DatabricksNotebook.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +DatabricksNotebook.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +DatabricksNotebook.SODA_CHECKS = RelationField("sodaChecks") +DatabricksNotebook.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +DatabricksNotebook.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/databricks_related.py b/pyatlan_v9/model/assets/databricks_related.py new file mode 100644 index 000000000..7b4c15f13 --- /dev/null +++ b/pyatlan_v9/model/assets/databricks_related.py @@ -0,0 +1,260 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Databricks module. + +This module contains all Related{Type} classes for the Databricks type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Any, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .referenceable_related import RelatedReferenceable +from .sql_related import RelatedSQL + +__all__ = [ + "RelatedDatabricks", + "RelatedDatabricksVolume", + "RelatedDatabricksVolumePath", + "RelatedDatabricksExternalLocation", + "RelatedDatabricksExternalLocationPath", + "RelatedDatabricksAIModelContext", + "RelatedDatabricksAIModelVersion", + "RelatedDatabricksUnityCatalogTag", + "RelatedDatabricksNotebook", + "RelatedDatabricksMetricView", +] + + +class RelatedDatabricks(RelatedSQL): + """ + Related entity reference for Databricks assets. + + Extends RelatedSQL with Databricks-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Databricks" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Databricks" + + +class RelatedDatabricksVolume(RelatedDatabricks): + """ + Related entity reference for DatabricksVolume assets. + + Extends RelatedDatabricks with DatabricksVolume-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DatabricksVolume" so it serializes correctly + + databricks_owner: Union[str, None, UnsetType] = UNSET + """User or group (principal) currently owning the volume.""" + + databricks_external_location: Union[str, None, UnsetType] = UNSET + """The storage location where the volume is created.""" + + databricks_type: Union[str, None, UnsetType] = UNSET + """Type of the volume.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DatabricksVolume" + + +class RelatedDatabricksVolumePath(RelatedDatabricks): + """ + Related entity reference for DatabricksVolumePath assets. + + Extends RelatedDatabricks with DatabricksVolumePath-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DatabricksVolumePath" so it serializes correctly + + databricks_path: Union[str, None, UnsetType] = UNSET + """Path of data on the volume.""" + + databricks_volume_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the parent volume.""" + + databricks_volume_name: Union[str, None, UnsetType] = UNSET + """Name of the parent volume.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DatabricksVolumePath" + + +class RelatedDatabricksExternalLocation(RelatedDatabricks): + """ + Related entity reference for DatabricksExternalLocation assets. + + Extends RelatedDatabricks with DatabricksExternalLocation-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DatabricksExternalLocation" so it serializes correctly + + databricks_url: Union[str, None, UnsetType] = UNSET + """URL of the external location.""" + + databricks_owner: Union[str, None, UnsetType] = UNSET + """User or group (principal) currently owning the external location.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DatabricksExternalLocation" + + +class RelatedDatabricksExternalLocationPath(RelatedDatabricks): + """ + Related entity reference for DatabricksExternalLocationPath assets. + + Extends RelatedDatabricks with DatabricksExternalLocationPath-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DatabricksExternalLocationPath" so it serializes correctly + + databricks_path: Union[str, None, UnsetType] = UNSET + """Path of data at the external location.""" + + databricks_parent_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the parent external location.""" + + databricks_parent_name: Union[str, None, UnsetType] = UNSET + """Name of the parent external location.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DatabricksExternalLocationPath" + + +class RelatedDatabricksAIModelContext(RelatedDatabricks): + """ + Related entity reference for DatabricksAIModelContext assets. + + Extends RelatedDatabricks with DatabricksAIModelContext-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DatabricksAIModelContext" so it serializes correctly + + databricks_metastore_id: Union[str, None, UnsetType] = UNSET + """The id of the model, common across versions.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DatabricksAIModelContext" + + +class RelatedDatabricksAIModelVersion(RelatedDatabricks): + """ + Related entity reference for DatabricksAIModelVersion assets. + + Extends RelatedDatabricks with DatabricksAIModelVersion-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DatabricksAIModelVersion" so it serializes correctly + + databricks_id: Union[int, None, UnsetType] = UNSET + """The id of the model, unique to every version.""" + + databricks_run_id: Union[str, None, UnsetType] = UNSET + """The run id of the model.""" + + databricks_run_name: Union[str, None, UnsetType] = UNSET + """The run name of the model.""" + + databricks_run_start_time: Union[int, None, UnsetType] = UNSET + """The run start time of the model.""" + + databricks_run_end_time: Union[int, None, UnsetType] = UNSET + """The run end time of the model.""" + + databricks_status: Union[str, None, UnsetType] = UNSET + """The status of the model.""" + + databricks_aliases: Union[List[str], None, UnsetType] = UNSET + """The aliases of the model.""" + + databricks_dataset_count: Union[int, None, UnsetType] = UNSET + """Number of datasets.""" + + databricks_source: Union[str, None, UnsetType] = UNSET + """Source artifact link for the model.""" + + databricks_artifact_uri: Union[str, None, UnsetType] = UNSET + """Artifact uri for the model.""" + + databricks_metrics: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """Metrics for an individual experiment.""" + + databricks_params: Union[Dict[str, str], None, UnsetType] = UNSET + """Params with key mapped to value for an individual experiment.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DatabricksAIModelVersion" + + +class RelatedDatabricksUnityCatalogTag(RelatedDatabricks): + """ + Related entity reference for DatabricksUnityCatalogTag assets. + + Extends RelatedDatabricks with DatabricksUnityCatalogTag-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DatabricksUnityCatalogTag" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DatabricksUnityCatalogTag" + + +class RelatedDatabricksNotebook(RelatedDatabricks): + """ + Related entity reference for DatabricksNotebook assets. + + Extends RelatedDatabricks with DatabricksNotebook-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DatabricksNotebook" so it serializes correctly + + databricks_path: Union[str, None, UnsetType] = UNSET + """Path of the notebook.""" + + databricks_workspace_id: Union[str, None, UnsetType] = UNSET + """Workspace Id of the notebook.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DatabricksNotebook" + + +class RelatedDatabricksMetricView(RelatedDatabricks): + """ + Related entity reference for DatabricksMetricView assets. + + Extends RelatedDatabricks with DatabricksMetricView-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DatabricksMetricView" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DatabricksMetricView" diff --git a/pyatlan_v9/model/assets/databricks_volume.py b/pyatlan_v9/model/assets/databricks_volume.py new file mode 100644 index 000000000..874a6f1e7 --- /dev/null +++ b/pyatlan_v9/model/assets/databricks_volume.py @@ -0,0 +1,894 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DatabricksVolume asset model with flattened inheritance. + +This module provides: +- DatabricksVolume: Flat asset class (easy to use) +- DatabricksVolumeAttributes: Nested attributes struct (extends AssetAttributes) +- DatabricksVolumeNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .snowflake_related import RelatedSnowflakeSemanticLogicalTable +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from .sql_related import RelatedSchema +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .databricks_related import RelatedDatabricksVolumePath + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class DatabricksVolume(Asset): + """ + Represents a Databricks Volume, a storage object for managing and accessing data files within Databricks workspaces. + """ + + DATABRICKS_OWNER: ClassVar[Any] = None + DATABRICKS_EXTERNAL_LOCATION: ClassVar[Any] = None + DATABRICKS_TYPE: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DATABRICKS_VOLUME_SCHEMA: ClassVar[Any] = None + DATABRICKS_VOLUME_PATHS: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "DatabricksVolume" + + databricks_owner: Union[str, None, UnsetType] = UNSET + """User or group (principal) currently owning the volume.""" + + databricks_external_location: Union[str, None, UnsetType] = UNSET + """The storage location where the volume is created.""" + + databricks_type: Union[str, None, UnsetType] = UNSET + """Type of the volume.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + databricks_volume_schema: Union[RelatedSchema, None, UnsetType] = UNSET + """Schema that contains the volume.""" + + databricks_volume_paths: Union[ + List[RelatedDatabricksVolumePath], None, UnsetType + ] = UNSET + """Paths contained within the volume.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "DatabricksVolume" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _databricks_volume_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> DatabricksVolume: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + DatabricksVolume instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _databricks_volume_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DatabricksVolumeAttributes(AssetAttributes): + """DatabricksVolume-specific attributes for nested API format.""" + + databricks_owner: Union[str, None, UnsetType] = UNSET + """User or group (principal) currently owning the volume.""" + + databricks_external_location: Union[str, None, UnsetType] = UNSET + """The storage location where the volume is created.""" + + databricks_type: Union[str, None, UnsetType] = UNSET + """Type of the volume.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + +class DatabricksVolumeRelationshipAttributes(AssetRelationshipAttributes): + """DatabricksVolume-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + databricks_volume_schema: Union[RelatedSchema, None, UnsetType] = UNSET + """Schema that contains the volume.""" + + databricks_volume_paths: Union[ + List[RelatedDatabricksVolumePath], None, UnsetType + ] = UNSET + """Paths contained within the volume.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DatabricksVolumeNested(AssetNested): + """DatabricksVolume in nested API format for high-performance serialization.""" + + attributes: Union[DatabricksVolumeAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + DatabricksVolumeRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + DatabricksVolumeRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + DatabricksVolumeRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DATABRICKS_VOLUME_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "databricks_volume_schema", + "databricks_volume_paths", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_databricks_volume_attrs( + attrs: DatabricksVolumeAttributes, obj: DatabricksVolume +) -> None: + """Populate DatabricksVolume-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.databricks_owner = obj.databricks_owner + attrs.databricks_external_location = obj.databricks_external_location + attrs.databricks_type = obj.databricks_type + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + + +def _extract_databricks_volume_attrs(attrs: DatabricksVolumeAttributes) -> dict: + """Extract all DatabricksVolume attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["databricks_owner"] = attrs.databricks_owner + result["databricks_external_location"] = attrs.databricks_external_location + result["databricks_type"] = attrs.databricks_type + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _databricks_volume_to_nested( + databricks_volume: DatabricksVolume, +) -> DatabricksVolumeNested: + """Convert flat DatabricksVolume to nested format.""" + attrs = DatabricksVolumeAttributes() + _populate_databricks_volume_attrs(attrs, databricks_volume) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + databricks_volume, + _DATABRICKS_VOLUME_REL_FIELDS, + DatabricksVolumeRelationshipAttributes, + ) + return DatabricksVolumeNested( + guid=databricks_volume.guid, + type_name=databricks_volume.type_name, + status=databricks_volume.status, + version=databricks_volume.version, + create_time=databricks_volume.create_time, + update_time=databricks_volume.update_time, + created_by=databricks_volume.created_by, + updated_by=databricks_volume.updated_by, + classifications=databricks_volume.classifications, + classification_names=databricks_volume.classification_names, + meanings=databricks_volume.meanings, + labels=databricks_volume.labels, + business_attributes=databricks_volume.business_attributes, + custom_attributes=databricks_volume.custom_attributes, + pending_tasks=databricks_volume.pending_tasks, + proxy=databricks_volume.proxy, + is_incomplete=databricks_volume.is_incomplete, + provenance_type=databricks_volume.provenance_type, + home_id=databricks_volume.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _databricks_volume_from_nested(nested: DatabricksVolumeNested) -> DatabricksVolume: + """Convert nested format to flat DatabricksVolume.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else DatabricksVolumeAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DATABRICKS_VOLUME_REL_FIELDS, + DatabricksVolumeRelationshipAttributes, + ) + return DatabricksVolume( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_databricks_volume_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _databricks_volume_to_nested_bytes( + databricks_volume: DatabricksVolume, serde: Serde +) -> bytes: + """Convert flat DatabricksVolume to nested JSON bytes.""" + return serde.encode(_databricks_volume_to_nested(databricks_volume)) + + +def _databricks_volume_from_nested_bytes(data: bytes, serde: Serde) -> DatabricksVolume: + """Convert nested JSON bytes to flat DatabricksVolume.""" + nested = serde.decode(data, DatabricksVolumeNested) + return _databricks_volume_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, +) + +DatabricksVolume.DATABRICKS_OWNER = KeywordField("databricksOwner", "databricksOwner") +DatabricksVolume.DATABRICKS_EXTERNAL_LOCATION = KeywordField( + "databricksExternalLocation", "databricksExternalLocation" +) +DatabricksVolume.DATABRICKS_TYPE = KeywordField("databricksType", "databricksType") +DatabricksVolume.QUERY_COUNT = NumericField("queryCount", "queryCount") +DatabricksVolume.QUERY_USER_COUNT = NumericField("queryUserCount", "queryUserCount") +DatabricksVolume.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +DatabricksVolume.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +DatabricksVolume.DATABASE_NAME = KeywordField("databaseName", "databaseName") +DatabricksVolume.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +DatabricksVolume.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +DatabricksVolume.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +DatabricksVolume.TABLE_NAME = KeywordField("tableName", "tableName") +DatabricksVolume.TABLE_QUALIFIED_NAME = KeywordField( + "tableQualifiedName", "tableQualifiedName" +) +DatabricksVolume.VIEW_NAME = KeywordField("viewName", "viewName") +DatabricksVolume.VIEW_QUALIFIED_NAME = KeywordField( + "viewQualifiedName", "viewQualifiedName" +) +DatabricksVolume.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +DatabricksVolume.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +DatabricksVolume.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +DatabricksVolume.LAST_PROFILED_AT = NumericField("lastProfiledAt", "lastProfiledAt") +DatabricksVolume.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +DatabricksVolume.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +DatabricksVolume.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +DatabricksVolume.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +DatabricksVolume.ANOMALO_CHECKS = RelationField("anomaloChecks") +DatabricksVolume.APPLICATION = RelationField("application") +DatabricksVolume.APPLICATION_FIELD = RelationField("applicationField") +DatabricksVolume.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +DatabricksVolume.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +DatabricksVolume.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +DatabricksVolume.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +DatabricksVolume.METRICS = RelationField("metrics") +DatabricksVolume.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +DatabricksVolume.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +DatabricksVolume.DATABRICKS_VOLUME_SCHEMA = RelationField("databricksVolumeSchema") +DatabricksVolume.DATABRICKS_VOLUME_PATHS = RelationField("databricksVolumePaths") +DatabricksVolume.DBT_MODELS = RelationField("dbtModels") +DatabricksVolume.SQL_DBT_MODELS = RelationField("sqlDbtModels") +DatabricksVolume.DBT_TESTS = RelationField("dbtTests") +DatabricksVolume.DBT_SOURCES = RelationField("dbtSources") +DatabricksVolume.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +DatabricksVolume.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +DatabricksVolume.MEANINGS = RelationField("meanings") +DatabricksVolume.MC_MONITORS = RelationField("mcMonitors") +DatabricksVolume.MC_INCIDENTS = RelationField("mcIncidents") +DatabricksVolume.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +DatabricksVolume.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +DatabricksVolume.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +DatabricksVolume.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +DatabricksVolume.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +DatabricksVolume.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +DatabricksVolume.FILES = RelationField("files") +DatabricksVolume.LINKS = RelationField("links") +DatabricksVolume.README = RelationField("readme") +DatabricksVolume.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +DatabricksVolume.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +DatabricksVolume.SODA_CHECKS = RelationField("sodaChecks") +DatabricksVolume.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +DatabricksVolume.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/databricks_volume_path.py b/pyatlan_v9/model/assets/databricks_volume_path.py new file mode 100644 index 000000000..8c79cbca8 --- /dev/null +++ b/pyatlan_v9/model/assets/databricks_volume_path.py @@ -0,0 +1,894 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DatabricksVolumePath asset model with flattened inheritance. + +This module provides: +- DatabricksVolumePath: Flat asset class (easy to use) +- DatabricksVolumePathAttributes: Nested attributes struct (extends AssetAttributes) +- DatabricksVolumePathNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .snowflake_related import RelatedSnowflakeSemanticLogicalTable +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .databricks_related import RelatedDatabricksVolume + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class DatabricksVolumePath(Asset): + """ + Represents a path within a Databricks Volume, providing access to specific data files or directories. + """ + + DATABRICKS_PATH: ClassVar[Any] = None + DATABRICKS_VOLUME_QUALIFIED_NAME: ClassVar[Any] = None + DATABRICKS_VOLUME_NAME: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DATABRICKS_VOLUME: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "DatabricksVolumePath" + + databricks_path: Union[str, None, UnsetType] = UNSET + """Path of data on the volume.""" + + databricks_volume_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the parent volume.""" + + databricks_volume_name: Union[str, None, UnsetType] = UNSET + """Name of the parent volume.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + databricks_volume: Union[RelatedDatabricksVolume, None, UnsetType] = UNSET + """Volume that contains the paths.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "DatabricksVolumePath" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _databricks_volume_path_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> DatabricksVolumePath: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + DatabricksVolumePath instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _databricks_volume_path_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DatabricksVolumePathAttributes(AssetAttributes): + """DatabricksVolumePath-specific attributes for nested API format.""" + + databricks_path: Union[str, None, UnsetType] = UNSET + """Path of data on the volume.""" + + databricks_volume_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the parent volume.""" + + databricks_volume_name: Union[str, None, UnsetType] = UNSET + """Name of the parent volume.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + +class DatabricksVolumePathRelationshipAttributes(AssetRelationshipAttributes): + """DatabricksVolumePath-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + databricks_volume: Union[RelatedDatabricksVolume, None, UnsetType] = UNSET + """Volume that contains the paths.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DatabricksVolumePathNested(AssetNested): + """DatabricksVolumePath in nested API format for high-performance serialization.""" + + attributes: Union[DatabricksVolumePathAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + DatabricksVolumePathRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + DatabricksVolumePathRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + DatabricksVolumePathRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DATABRICKS_VOLUME_PATH_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "databricks_volume", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_databricks_volume_path_attrs( + attrs: DatabricksVolumePathAttributes, obj: DatabricksVolumePath +) -> None: + """Populate DatabricksVolumePath-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.databricks_path = obj.databricks_path + attrs.databricks_volume_qualified_name = obj.databricks_volume_qualified_name + attrs.databricks_volume_name = obj.databricks_volume_name + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + + +def _extract_databricks_volume_path_attrs( + attrs: DatabricksVolumePathAttributes, +) -> dict: + """Extract all DatabricksVolumePath attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["databricks_path"] = attrs.databricks_path + result["databricks_volume_qualified_name"] = attrs.databricks_volume_qualified_name + result["databricks_volume_name"] = attrs.databricks_volume_name + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _databricks_volume_path_to_nested( + databricks_volume_path: DatabricksVolumePath, +) -> DatabricksVolumePathNested: + """Convert flat DatabricksVolumePath to nested format.""" + attrs = DatabricksVolumePathAttributes() + _populate_databricks_volume_path_attrs(attrs, databricks_volume_path) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + databricks_volume_path, + _DATABRICKS_VOLUME_PATH_REL_FIELDS, + DatabricksVolumePathRelationshipAttributes, + ) + return DatabricksVolumePathNested( + guid=databricks_volume_path.guid, + type_name=databricks_volume_path.type_name, + status=databricks_volume_path.status, + version=databricks_volume_path.version, + create_time=databricks_volume_path.create_time, + update_time=databricks_volume_path.update_time, + created_by=databricks_volume_path.created_by, + updated_by=databricks_volume_path.updated_by, + classifications=databricks_volume_path.classifications, + classification_names=databricks_volume_path.classification_names, + meanings=databricks_volume_path.meanings, + labels=databricks_volume_path.labels, + business_attributes=databricks_volume_path.business_attributes, + custom_attributes=databricks_volume_path.custom_attributes, + pending_tasks=databricks_volume_path.pending_tasks, + proxy=databricks_volume_path.proxy, + is_incomplete=databricks_volume_path.is_incomplete, + provenance_type=databricks_volume_path.provenance_type, + home_id=databricks_volume_path.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _databricks_volume_path_from_nested( + nested: DatabricksVolumePathNested, +) -> DatabricksVolumePath: + """Convert nested format to flat DatabricksVolumePath.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else DatabricksVolumePathAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DATABRICKS_VOLUME_PATH_REL_FIELDS, + DatabricksVolumePathRelationshipAttributes, + ) + return DatabricksVolumePath( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_databricks_volume_path_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _databricks_volume_path_to_nested_bytes( + databricks_volume_path: DatabricksVolumePath, serde: Serde +) -> bytes: + """Convert flat DatabricksVolumePath to nested JSON bytes.""" + return serde.encode(_databricks_volume_path_to_nested(databricks_volume_path)) + + +def _databricks_volume_path_from_nested_bytes( + data: bytes, serde: Serde +) -> DatabricksVolumePath: + """Convert nested JSON bytes to flat DatabricksVolumePath.""" + nested = serde.decode(data, DatabricksVolumePathNested) + return _databricks_volume_path_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, +) + +DatabricksVolumePath.DATABRICKS_PATH = KeywordField("databricksPath", "databricksPath") +DatabricksVolumePath.DATABRICKS_VOLUME_QUALIFIED_NAME = KeywordField( + "databricksVolumeQualifiedName", "databricksVolumeQualifiedName" +) +DatabricksVolumePath.DATABRICKS_VOLUME_NAME = KeywordField( + "databricksVolumeName", "databricksVolumeName" +) +DatabricksVolumePath.QUERY_COUNT = NumericField("queryCount", "queryCount") +DatabricksVolumePath.QUERY_USER_COUNT = NumericField("queryUserCount", "queryUserCount") +DatabricksVolumePath.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +DatabricksVolumePath.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +DatabricksVolumePath.DATABASE_NAME = KeywordField("databaseName", "databaseName") +DatabricksVolumePath.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +DatabricksVolumePath.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +DatabricksVolumePath.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +DatabricksVolumePath.TABLE_NAME = KeywordField("tableName", "tableName") +DatabricksVolumePath.TABLE_QUALIFIED_NAME = KeywordField( + "tableQualifiedName", "tableQualifiedName" +) +DatabricksVolumePath.VIEW_NAME = KeywordField("viewName", "viewName") +DatabricksVolumePath.VIEW_QUALIFIED_NAME = KeywordField( + "viewQualifiedName", "viewQualifiedName" +) +DatabricksVolumePath.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +DatabricksVolumePath.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +DatabricksVolumePath.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +DatabricksVolumePath.LAST_PROFILED_AT = NumericField("lastProfiledAt", "lastProfiledAt") +DatabricksVolumePath.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +DatabricksVolumePath.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +DatabricksVolumePath.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +DatabricksVolumePath.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +DatabricksVolumePath.ANOMALO_CHECKS = RelationField("anomaloChecks") +DatabricksVolumePath.APPLICATION = RelationField("application") +DatabricksVolumePath.APPLICATION_FIELD = RelationField("applicationField") +DatabricksVolumePath.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +DatabricksVolumePath.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +DatabricksVolumePath.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +DatabricksVolumePath.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +DatabricksVolumePath.METRICS = RelationField("metrics") +DatabricksVolumePath.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +DatabricksVolumePath.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +DatabricksVolumePath.DATABRICKS_VOLUME = RelationField("databricksVolume") +DatabricksVolumePath.DBT_MODELS = RelationField("dbtModels") +DatabricksVolumePath.SQL_DBT_MODELS = RelationField("sqlDbtModels") +DatabricksVolumePath.DBT_TESTS = RelationField("dbtTests") +DatabricksVolumePath.DBT_SOURCES = RelationField("dbtSources") +DatabricksVolumePath.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +DatabricksVolumePath.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +DatabricksVolumePath.MEANINGS = RelationField("meanings") +DatabricksVolumePath.MC_MONITORS = RelationField("mcMonitors") +DatabricksVolumePath.MC_INCIDENTS = RelationField("mcIncidents") +DatabricksVolumePath.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +DatabricksVolumePath.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +DatabricksVolumePath.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +DatabricksVolumePath.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +DatabricksVolumePath.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +DatabricksVolumePath.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +DatabricksVolumePath.FILES = RelationField("files") +DatabricksVolumePath.LINKS = RelationField("links") +DatabricksVolumePath.README = RelationField("readme") +DatabricksVolumePath.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +DatabricksVolumePath.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +DatabricksVolumePath.SODA_CHECKS = RelationField("sodaChecks") +DatabricksVolumePath.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +DatabricksVolumePath.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/dataverse.py b/pyatlan_v9/model/assets/dataverse.py new file mode 100644 index 000000000..f911fcfdf --- /dev/null +++ b/pyatlan_v9/model/assets/dataverse.py @@ -0,0 +1,561 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Dataverse asset model with flattened inheritance. + +This module provides: +- Dataverse: Flat asset class (easy to use) +- DataverseAttributes: Nested attributes struct (extends AssetAttributes) +- DataverseNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Dataverse(Asset): + """ + Base class for all Dataverse types. + """ + + DATAVERSE_IS_CUSTOM: ClassVar[Any] = None + DATAVERSE_IS_CUSTOMIZABLE: ClassVar[Any] = None + DATAVERSE_IS_AUDIT_ENABLED: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Dataverse" + + dataverse_is_custom: Union[bool, None, UnsetType] = UNSET + """Indicator if DataverseEntity is custom built.""" + + dataverse_is_customizable: Union[bool, None, UnsetType] = UNSET + """Indicator if DataverseEntity is customizable.""" + + dataverse_is_audit_enabled: Union[bool, None, UnsetType] = UNSET + """Indicator if DataverseEntity has auditing enabled.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Dataverse" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _dataverse_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Dataverse: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Dataverse instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _dataverse_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DataverseAttributes(AssetAttributes): + """Dataverse-specific attributes for nested API format.""" + + dataverse_is_custom: Union[bool, None, UnsetType] = UNSET + """Indicator if DataverseEntity is custom built.""" + + dataverse_is_customizable: Union[bool, None, UnsetType] = UNSET + """Indicator if DataverseEntity is customizable.""" + + dataverse_is_audit_enabled: Union[bool, None, UnsetType] = UNSET + """Indicator if DataverseEntity has auditing enabled.""" + + +class DataverseRelationshipAttributes(AssetRelationshipAttributes): + """Dataverse-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DataverseNested(AssetNested): + """Dataverse in nested API format for high-performance serialization.""" + + attributes: Union[DataverseAttributes, UnsetType] = UNSET + relationship_attributes: Union[DataverseRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + DataverseRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + DataverseRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DATAVERSE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_dataverse_attrs(attrs: DataverseAttributes, obj: Dataverse) -> None: + """Populate Dataverse-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.dataverse_is_custom = obj.dataverse_is_custom + attrs.dataverse_is_customizable = obj.dataverse_is_customizable + attrs.dataverse_is_audit_enabled = obj.dataverse_is_audit_enabled + + +def _extract_dataverse_attrs(attrs: DataverseAttributes) -> dict: + """Extract all Dataverse attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["dataverse_is_custom"] = attrs.dataverse_is_custom + result["dataverse_is_customizable"] = attrs.dataverse_is_customizable + result["dataverse_is_audit_enabled"] = attrs.dataverse_is_audit_enabled + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _dataverse_to_nested(dataverse: Dataverse) -> DataverseNested: + """Convert flat Dataverse to nested format.""" + attrs = DataverseAttributes() + _populate_dataverse_attrs(attrs, dataverse) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + dataverse, _DATAVERSE_REL_FIELDS, DataverseRelationshipAttributes + ) + return DataverseNested( + guid=dataverse.guid, + type_name=dataverse.type_name, + status=dataverse.status, + version=dataverse.version, + create_time=dataverse.create_time, + update_time=dataverse.update_time, + created_by=dataverse.created_by, + updated_by=dataverse.updated_by, + classifications=dataverse.classifications, + classification_names=dataverse.classification_names, + meanings=dataverse.meanings, + labels=dataverse.labels, + business_attributes=dataverse.business_attributes, + custom_attributes=dataverse.custom_attributes, + pending_tasks=dataverse.pending_tasks, + proxy=dataverse.proxy, + is_incomplete=dataverse.is_incomplete, + provenance_type=dataverse.provenance_type, + home_id=dataverse.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _dataverse_from_nested(nested: DataverseNested) -> Dataverse: + """Convert nested format to flat Dataverse.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else DataverseAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DATAVERSE_REL_FIELDS, + DataverseRelationshipAttributes, + ) + return Dataverse( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_dataverse_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _dataverse_to_nested_bytes(dataverse: Dataverse, serde: Serde) -> bytes: + """Convert flat Dataverse to nested JSON bytes.""" + return serde.encode(_dataverse_to_nested(dataverse)) + + +def _dataverse_from_nested_bytes(data: bytes, serde: Serde) -> Dataverse: + """Convert nested JSON bytes to flat Dataverse.""" + nested = serde.decode(data, DataverseNested) + return _dataverse_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + RelationField, +) + +Dataverse.DATAVERSE_IS_CUSTOM = BooleanField("dataverseIsCustom", "dataverseIsCustom") +Dataverse.DATAVERSE_IS_CUSTOMIZABLE = BooleanField( + "dataverseIsCustomizable", "dataverseIsCustomizable" +) +Dataverse.DATAVERSE_IS_AUDIT_ENABLED = BooleanField( + "dataverseIsAuditEnabled", "dataverseIsAuditEnabled" +) +Dataverse.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Dataverse.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Dataverse.ANOMALO_CHECKS = RelationField("anomaloChecks") +Dataverse.APPLICATION = RelationField("application") +Dataverse.APPLICATION_FIELD = RelationField("applicationField") +Dataverse.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Dataverse.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Dataverse.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Dataverse.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Dataverse.METRICS = RelationField("metrics") +Dataverse.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Dataverse.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Dataverse.MEANINGS = RelationField("meanings") +Dataverse.MC_MONITORS = RelationField("mcMonitors") +Dataverse.MC_INCIDENTS = RelationField("mcIncidents") +Dataverse.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Dataverse.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Dataverse.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Dataverse.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Dataverse.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Dataverse.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Dataverse.FILES = RelationField("files") +Dataverse.LINKS = RelationField("links") +Dataverse.README = RelationField("readme") +Dataverse.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Dataverse.SODA_CHECKS = RelationField("sodaChecks") +Dataverse.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Dataverse.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/dataverse_attribute.py b/pyatlan_v9/model/assets/dataverse_attribute.py new file mode 100644 index 000000000..96b253fd0 --- /dev/null +++ b/pyatlan_v9/model/assets/dataverse_attribute.py @@ -0,0 +1,719 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DataverseAttribute asset model with flattened inheritance. + +This module provides: +- DataverseAttribute: Flat asset class (easy to use) +- DataverseAttributeAttributes: Nested attributes struct (extends AssetAttributes) +- DataverseAttributeNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .dataverse_related import RelatedDataverseEntity + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class DataverseAttribute(Asset): + """ + Instances of DataverseAttribute in Atlan. + """ + + DATAVERSE_ENTITY_QUALIFIED_NAME: ClassVar[Any] = None + DATAVERSE_ATTRIBUTE_SCHEMA_NAME: ClassVar[Any] = None + DATAVERSE_ATTRIBUTE_TYPE: ClassVar[Any] = None + DATAVERSE_ATTRIBUTE_IS_PRIMARY_ID: ClassVar[Any] = None + DATAVERSE_ATTRIBUTE_IS_SEARCHABLE: ClassVar[Any] = None + DATAVERSE_IS_CUSTOM: ClassVar[Any] = None + DATAVERSE_IS_CUSTOMIZABLE: ClassVar[Any] = None + DATAVERSE_IS_AUDIT_ENABLED: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DATAVERSE_ENTITY: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "DataverseAttribute" + + dataverse_entity_qualified_name: Union[str, None, UnsetType] = UNSET + """Entity Qualified Name of the DataverseAttribute.""" + + dataverse_attribute_schema_name: Union[str, None, UnsetType] = UNSET + """Schema Name of the DataverseAttribute.""" + + dataverse_attribute_type: Union[str, None, UnsetType] = UNSET + """Type of the DataverseAttribute.""" + + dataverse_attribute_is_primary_id: Union[bool, None, UnsetType] = UNSET + """Indicator if DataverseAttribute is the primary key.""" + + dataverse_attribute_is_searchable: Union[bool, None, UnsetType] = UNSET + """Indicator if DataverseAttribute is searchable.""" + + dataverse_is_custom: Union[bool, None, UnsetType] = UNSET + """Indicator if DataverseEntity is custom built.""" + + dataverse_is_customizable: Union[bool, None, UnsetType] = UNSET + """Indicator if DataverseEntity is customizable.""" + + dataverse_is_audit_enabled: Union[bool, None, UnsetType] = UNSET + """Indicator if DataverseEntity has auditing enabled.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dataverse_entity: Union[RelatedDataverseEntity, None, UnsetType] = UNSET + """DataverseEntity asset containing this DataverseAttribute.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "DataverseAttribute" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + dataverse_entity_qualified_name: str, + connection_qualified_name: str | None = None, + ) -> "DataverseAttribute": + """Create a new DataverseAttribute asset.""" + validate_required_fields( + ["name", "dataverse_entity_qualified_name"], + [name, dataverse_entity_qualified_name], + ) + if connection_qualified_name: + connector_name = ( + connection_qualified_name.split("/")[1] + if len(connection_qualified_name.split("/")) > 1 + else "" + ) + else: + fields = dataverse_entity_qualified_name.split("/") + if len(fields) < 3: + raise ValueError("dataverse_entity_qualified_name is invalid") + connection_qualified_name = "/".join(fields[:3]) + connector_name = fields[1] + return cls( + name=name, + qualified_name=f"{dataverse_entity_qualified_name}/{name}", + connection_qualified_name=connection_qualified_name, + connector_name=connector_name, + dataverse_entity_qualified_name=dataverse_entity_qualified_name, + dataverse_entity=RelatedDataverseEntity( + unique_attributes={"qualifiedName": dataverse_entity_qualified_name} + ), + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "DataverseAttribute": + """Create a DataverseAttribute instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "DataverseAttribute": + """Return only fields required for update operations.""" + return DataverseAttribute.updater( + qualified_name=self.qualified_name, + name=self.name, + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _dataverse_attribute_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> DataverseAttribute: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + DataverseAttribute instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _dataverse_attribute_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DataverseAttributeAttributes(AssetAttributes): + """DataverseAttribute-specific attributes for nested API format.""" + + dataverse_entity_qualified_name: Union[str, None, UnsetType] = UNSET + """Entity Qualified Name of the DataverseAttribute.""" + + dataverse_attribute_schema_name: Union[str, None, UnsetType] = UNSET + """Schema Name of the DataverseAttribute.""" + + dataverse_attribute_type: Union[str, None, UnsetType] = UNSET + """Type of the DataverseAttribute.""" + + dataverse_attribute_is_primary_id: Union[bool, None, UnsetType] = UNSET + """Indicator if DataverseAttribute is the primary key.""" + + dataverse_attribute_is_searchable: Union[bool, None, UnsetType] = UNSET + """Indicator if DataverseAttribute is searchable.""" + + dataverse_is_custom: Union[bool, None, UnsetType] = UNSET + """Indicator if DataverseEntity is custom built.""" + + dataverse_is_customizable: Union[bool, None, UnsetType] = UNSET + """Indicator if DataverseEntity is customizable.""" + + dataverse_is_audit_enabled: Union[bool, None, UnsetType] = UNSET + """Indicator if DataverseEntity has auditing enabled.""" + + +class DataverseAttributeRelationshipAttributes(AssetRelationshipAttributes): + """DataverseAttribute-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dataverse_entity: Union[RelatedDataverseEntity, None, UnsetType] = UNSET + """DataverseEntity asset containing this DataverseAttribute.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DataverseAttributeNested(AssetNested): + """DataverseAttribute in nested API format for high-performance serialization.""" + + attributes: Union[DataverseAttributeAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + DataverseAttributeRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + DataverseAttributeRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + DataverseAttributeRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DATAVERSE_ATTRIBUTE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "dataverse_entity", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_dataverse_attribute_attrs( + attrs: DataverseAttributeAttributes, obj: DataverseAttribute +) -> None: + """Populate DataverseAttribute-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.dataverse_entity_qualified_name = obj.dataverse_entity_qualified_name + attrs.dataverse_attribute_schema_name = obj.dataverse_attribute_schema_name + attrs.dataverse_attribute_type = obj.dataverse_attribute_type + attrs.dataverse_attribute_is_primary_id = obj.dataverse_attribute_is_primary_id + attrs.dataverse_attribute_is_searchable = obj.dataverse_attribute_is_searchable + attrs.dataverse_is_custom = obj.dataverse_is_custom + attrs.dataverse_is_customizable = obj.dataverse_is_customizable + attrs.dataverse_is_audit_enabled = obj.dataverse_is_audit_enabled + + +def _extract_dataverse_attribute_attrs(attrs: DataverseAttributeAttributes) -> dict: + """Extract all DataverseAttribute attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["dataverse_entity_qualified_name"] = attrs.dataverse_entity_qualified_name + result["dataverse_attribute_schema_name"] = attrs.dataverse_attribute_schema_name + result["dataverse_attribute_type"] = attrs.dataverse_attribute_type + result["dataverse_attribute_is_primary_id"] = ( + attrs.dataverse_attribute_is_primary_id + ) + result["dataverse_attribute_is_searchable"] = ( + attrs.dataverse_attribute_is_searchable + ) + result["dataverse_is_custom"] = attrs.dataverse_is_custom + result["dataverse_is_customizable"] = attrs.dataverse_is_customizable + result["dataverse_is_audit_enabled"] = attrs.dataverse_is_audit_enabled + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _dataverse_attribute_to_nested( + dataverse_attribute: DataverseAttribute, +) -> DataverseAttributeNested: + """Convert flat DataverseAttribute to nested format.""" + attrs = DataverseAttributeAttributes() + _populate_dataverse_attribute_attrs(attrs, dataverse_attribute) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + dataverse_attribute, + _DATAVERSE_ATTRIBUTE_REL_FIELDS, + DataverseAttributeRelationshipAttributes, + ) + return DataverseAttributeNested( + guid=dataverse_attribute.guid, + type_name=dataverse_attribute.type_name, + status=dataverse_attribute.status, + version=dataverse_attribute.version, + create_time=dataverse_attribute.create_time, + update_time=dataverse_attribute.update_time, + created_by=dataverse_attribute.created_by, + updated_by=dataverse_attribute.updated_by, + classifications=dataverse_attribute.classifications, + classification_names=dataverse_attribute.classification_names, + meanings=dataverse_attribute.meanings, + labels=dataverse_attribute.labels, + business_attributes=dataverse_attribute.business_attributes, + custom_attributes=dataverse_attribute.custom_attributes, + pending_tasks=dataverse_attribute.pending_tasks, + proxy=dataverse_attribute.proxy, + is_incomplete=dataverse_attribute.is_incomplete, + provenance_type=dataverse_attribute.provenance_type, + home_id=dataverse_attribute.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _dataverse_attribute_from_nested( + nested: DataverseAttributeNested, +) -> DataverseAttribute: + """Convert nested format to flat DataverseAttribute.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else DataverseAttributeAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DATAVERSE_ATTRIBUTE_REL_FIELDS, + DataverseAttributeRelationshipAttributes, + ) + return DataverseAttribute( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_dataverse_attribute_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _dataverse_attribute_to_nested_bytes( + dataverse_attribute: DataverseAttribute, serde: Serde +) -> bytes: + """Convert flat DataverseAttribute to nested JSON bytes.""" + return serde.encode(_dataverse_attribute_to_nested(dataverse_attribute)) + + +def _dataverse_attribute_from_nested_bytes( + data: bytes, serde: Serde +) -> DataverseAttribute: + """Convert nested JSON bytes to flat DataverseAttribute.""" + nested = serde.decode(data, DataverseAttributeNested) + return _dataverse_attribute_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + RelationField, +) + +DataverseAttribute.DATAVERSE_ENTITY_QUALIFIED_NAME = KeywordField( + "dataverseEntityQualifiedName", "dataverseEntityQualifiedName" +) +DataverseAttribute.DATAVERSE_ATTRIBUTE_SCHEMA_NAME = KeywordField( + "dataverseAttributeSchemaName", "dataverseAttributeSchemaName" +) +DataverseAttribute.DATAVERSE_ATTRIBUTE_TYPE = KeywordField( + "dataverseAttributeType", "dataverseAttributeType" +) +DataverseAttribute.DATAVERSE_ATTRIBUTE_IS_PRIMARY_ID = BooleanField( + "dataverseAttributeIsPrimaryId", "dataverseAttributeIsPrimaryId" +) +DataverseAttribute.DATAVERSE_ATTRIBUTE_IS_SEARCHABLE = BooleanField( + "dataverseAttributeIsSearchable", "dataverseAttributeIsSearchable" +) +DataverseAttribute.DATAVERSE_IS_CUSTOM = BooleanField( + "dataverseIsCustom", "dataverseIsCustom" +) +DataverseAttribute.DATAVERSE_IS_CUSTOMIZABLE = BooleanField( + "dataverseIsCustomizable", "dataverseIsCustomizable" +) +DataverseAttribute.DATAVERSE_IS_AUDIT_ENABLED = BooleanField( + "dataverseIsAuditEnabled", "dataverseIsAuditEnabled" +) +DataverseAttribute.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +DataverseAttribute.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +DataverseAttribute.ANOMALO_CHECKS = RelationField("anomaloChecks") +DataverseAttribute.APPLICATION = RelationField("application") +DataverseAttribute.APPLICATION_FIELD = RelationField("applicationField") +DataverseAttribute.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +DataverseAttribute.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +DataverseAttribute.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +DataverseAttribute.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +DataverseAttribute.METRICS = RelationField("metrics") +DataverseAttribute.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +DataverseAttribute.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +DataverseAttribute.DATAVERSE_ENTITY = RelationField("dataverseEntity") +DataverseAttribute.MEANINGS = RelationField("meanings") +DataverseAttribute.MC_MONITORS = RelationField("mcMonitors") +DataverseAttribute.MC_INCIDENTS = RelationField("mcIncidents") +DataverseAttribute.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +DataverseAttribute.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +DataverseAttribute.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +DataverseAttribute.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +DataverseAttribute.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +DataverseAttribute.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +DataverseAttribute.FILES = RelationField("files") +DataverseAttribute.LINKS = RelationField("links") +DataverseAttribute.README = RelationField("readme") +DataverseAttribute.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +DataverseAttribute.SODA_CHECKS = RelationField("sodaChecks") +DataverseAttribute.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +DataverseAttribute.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/dataverse_entity.py b/pyatlan_v9/model/assets/dataverse_entity.py new file mode 100644 index 000000000..7c36f081e --- /dev/null +++ b/pyatlan_v9/model/assets/dataverse_entity.py @@ -0,0 +1,656 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DataverseEntity asset model with flattened inheritance. + +This module provides: +- DataverseEntity: Flat asset class (easy to use) +- DataverseEntityAttributes: Nested attributes struct (extends AssetAttributes) +- DataverseEntityNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .dataverse_related import RelatedDataverseAttribute + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class DataverseEntity(Asset): + """ + Instances of DataverseEntity in Atlan. + """ + + DATAVERSE_ENTITY_SCHEMA_NAME: ClassVar[Any] = None + DATAVERSE_ENTITY_TABLE_TYPE: ClassVar[Any] = None + DATAVERSE_IS_CUSTOM: ClassVar[Any] = None + DATAVERSE_IS_CUSTOMIZABLE: ClassVar[Any] = None + DATAVERSE_IS_AUDIT_ENABLED: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DATAVERSE_ATTRIBUTES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "DataverseEntity" + + dataverse_entity_schema_name: Union[str, None, UnsetType] = UNSET + """Schema Name of the DataverseEntity.""" + + dataverse_entity_table_type: Union[str, None, UnsetType] = UNSET + """Table Type of the DataverseEntity.""" + + dataverse_is_custom: Union[bool, None, UnsetType] = UNSET + """Indicator if DataverseEntity is custom built.""" + + dataverse_is_customizable: Union[bool, None, UnsetType] = UNSET + """Indicator if DataverseEntity is customizable.""" + + dataverse_is_audit_enabled: Union[bool, None, UnsetType] = UNSET + """Indicator if DataverseEntity has auditing enabled.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dataverse_attributes: Union[List[RelatedDataverseAttribute], None, UnsetType] = ( + UNSET + ) + """DataverseAttribute assets contained within this DataverseEntity.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "DataverseEntity" + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + connection_qualified_name: str, + ) -> "DataverseEntity": + """Create a new DataverseEntity asset.""" + validate_required_fields( + ["name", "connection_qualified_name"], [name, connection_qualified_name] + ) + connector_name = ( + connection_qualified_name.split("/")[1] + if len(connection_qualified_name.split("/")) > 1 + else "" + ) + return cls( + name=name, + qualified_name=f"{connection_qualified_name}/{name}", + connection_qualified_name=connection_qualified_name, + connector_name=connector_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "DataverseEntity": + """Create a DataverseEntity instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "DataverseEntity": + """Return only fields required for update operations.""" + return DataverseEntity.updater( + qualified_name=self.qualified_name, name=self.name + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _dataverse_entity_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> DataverseEntity: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + DataverseEntity instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _dataverse_entity_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DataverseEntityAttributes(AssetAttributes): + """DataverseEntity-specific attributes for nested API format.""" + + dataverse_entity_schema_name: Union[str, None, UnsetType] = UNSET + """Schema Name of the DataverseEntity.""" + + dataverse_entity_table_type: Union[str, None, UnsetType] = UNSET + """Table Type of the DataverseEntity.""" + + dataverse_is_custom: Union[bool, None, UnsetType] = UNSET + """Indicator if DataverseEntity is custom built.""" + + dataverse_is_customizable: Union[bool, None, UnsetType] = UNSET + """Indicator if DataverseEntity is customizable.""" + + dataverse_is_audit_enabled: Union[bool, None, UnsetType] = UNSET + """Indicator if DataverseEntity has auditing enabled.""" + + +class DataverseEntityRelationshipAttributes(AssetRelationshipAttributes): + """DataverseEntity-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dataverse_attributes: Union[List[RelatedDataverseAttribute], None, UnsetType] = ( + UNSET + ) + """DataverseAttribute assets contained within this DataverseEntity.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DataverseEntityNested(AssetNested): + """DataverseEntity in nested API format for high-performance serialization.""" + + attributes: Union[DataverseEntityAttributes, UnsetType] = UNSET + relationship_attributes: Union[DataverseEntityRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + DataverseEntityRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + DataverseEntityRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DATAVERSE_ENTITY_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "dataverse_attributes", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_dataverse_entity_attrs( + attrs: DataverseEntityAttributes, obj: DataverseEntity +) -> None: + """Populate DataverseEntity-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.dataverse_entity_schema_name = obj.dataverse_entity_schema_name + attrs.dataverse_entity_table_type = obj.dataverse_entity_table_type + attrs.dataverse_is_custom = obj.dataverse_is_custom + attrs.dataverse_is_customizable = obj.dataverse_is_customizable + attrs.dataverse_is_audit_enabled = obj.dataverse_is_audit_enabled + + +def _extract_dataverse_entity_attrs(attrs: DataverseEntityAttributes) -> dict: + """Extract all DataverseEntity attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["dataverse_entity_schema_name"] = attrs.dataverse_entity_schema_name + result["dataverse_entity_table_type"] = attrs.dataverse_entity_table_type + result["dataverse_is_custom"] = attrs.dataverse_is_custom + result["dataverse_is_customizable"] = attrs.dataverse_is_customizable + result["dataverse_is_audit_enabled"] = attrs.dataverse_is_audit_enabled + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _dataverse_entity_to_nested( + dataverse_entity: DataverseEntity, +) -> DataverseEntityNested: + """Convert flat DataverseEntity to nested format.""" + attrs = DataverseEntityAttributes() + _populate_dataverse_entity_attrs(attrs, dataverse_entity) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + dataverse_entity, + _DATAVERSE_ENTITY_REL_FIELDS, + DataverseEntityRelationshipAttributes, + ) + return DataverseEntityNested( + guid=dataverse_entity.guid, + type_name=dataverse_entity.type_name, + status=dataverse_entity.status, + version=dataverse_entity.version, + create_time=dataverse_entity.create_time, + update_time=dataverse_entity.update_time, + created_by=dataverse_entity.created_by, + updated_by=dataverse_entity.updated_by, + classifications=dataverse_entity.classifications, + classification_names=dataverse_entity.classification_names, + meanings=dataverse_entity.meanings, + labels=dataverse_entity.labels, + business_attributes=dataverse_entity.business_attributes, + custom_attributes=dataverse_entity.custom_attributes, + pending_tasks=dataverse_entity.pending_tasks, + proxy=dataverse_entity.proxy, + is_incomplete=dataverse_entity.is_incomplete, + provenance_type=dataverse_entity.provenance_type, + home_id=dataverse_entity.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _dataverse_entity_from_nested(nested: DataverseEntityNested) -> DataverseEntity: + """Convert nested format to flat DataverseEntity.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else DataverseEntityAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DATAVERSE_ENTITY_REL_FIELDS, + DataverseEntityRelationshipAttributes, + ) + return DataverseEntity( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_dataverse_entity_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _dataverse_entity_to_nested_bytes( + dataverse_entity: DataverseEntity, serde: Serde +) -> bytes: + """Convert flat DataverseEntity to nested JSON bytes.""" + return serde.encode(_dataverse_entity_to_nested(dataverse_entity)) + + +def _dataverse_entity_from_nested_bytes(data: bytes, serde: Serde) -> DataverseEntity: + """Convert nested JSON bytes to flat DataverseEntity.""" + nested = serde.decode(data, DataverseEntityNested) + return _dataverse_entity_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + RelationField, +) + +DataverseEntity.DATAVERSE_ENTITY_SCHEMA_NAME = KeywordField( + "dataverseEntitySchemaName", "dataverseEntitySchemaName" +) +DataverseEntity.DATAVERSE_ENTITY_TABLE_TYPE = KeywordField( + "dataverseEntityTableType", "dataverseEntityTableType" +) +DataverseEntity.DATAVERSE_IS_CUSTOM = BooleanField( + "dataverseIsCustom", "dataverseIsCustom" +) +DataverseEntity.DATAVERSE_IS_CUSTOMIZABLE = BooleanField( + "dataverseIsCustomizable", "dataverseIsCustomizable" +) +DataverseEntity.DATAVERSE_IS_AUDIT_ENABLED = BooleanField( + "dataverseIsAuditEnabled", "dataverseIsAuditEnabled" +) +DataverseEntity.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +DataverseEntity.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +DataverseEntity.ANOMALO_CHECKS = RelationField("anomaloChecks") +DataverseEntity.APPLICATION = RelationField("application") +DataverseEntity.APPLICATION_FIELD = RelationField("applicationField") +DataverseEntity.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +DataverseEntity.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +DataverseEntity.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +DataverseEntity.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +DataverseEntity.METRICS = RelationField("metrics") +DataverseEntity.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +DataverseEntity.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +DataverseEntity.DATAVERSE_ATTRIBUTES = RelationField("dataverseAttributes") +DataverseEntity.MEANINGS = RelationField("meanings") +DataverseEntity.MC_MONITORS = RelationField("mcMonitors") +DataverseEntity.MC_INCIDENTS = RelationField("mcIncidents") +DataverseEntity.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +DataverseEntity.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +DataverseEntity.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +DataverseEntity.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +DataverseEntity.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +DataverseEntity.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +DataverseEntity.FILES = RelationField("files") +DataverseEntity.LINKS = RelationField("links") +DataverseEntity.README = RelationField("readme") +DataverseEntity.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +DataverseEntity.SODA_CHECKS = RelationField("sodaChecks") +DataverseEntity.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +DataverseEntity.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/dataverse_related.py b/pyatlan_v9/model/assets/dataverse_related.py new file mode 100644 index 000000000..aec4f51bd --- /dev/null +++ b/pyatlan_v9/model/assets/dataverse_related.py @@ -0,0 +1,100 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Dataverse module. + +This module contains all Related{Type} classes for the Dataverse type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Union + +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedSaaS +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedDataverse", + "RelatedDataverseEntity", + "RelatedDataverseAttribute", +] + + +class RelatedDataverse(RelatedSaaS): + """ + Related entity reference for Dataverse assets. + + Extends RelatedSaaS with Dataverse-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Dataverse" so it serializes correctly + + dataverse_is_custom: Union[bool, None, UnsetType] = UNSET + """Indicator if DataverseEntity is custom built.""" + + dataverse_is_customizable: Union[bool, None, UnsetType] = UNSET + """Indicator if DataverseEntity is customizable.""" + + dataverse_is_audit_enabled: Union[bool, None, UnsetType] = UNSET + """Indicator if DataverseEntity has auditing enabled.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Dataverse" + + +class RelatedDataverseEntity(RelatedDataverse): + """ + Related entity reference for DataverseEntity assets. + + Extends RelatedDataverse with DataverseEntity-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DataverseEntity" so it serializes correctly + + dataverse_entity_schema_name: Union[str, None, UnsetType] = UNSET + """Schema Name of the DataverseEntity.""" + + dataverse_entity_table_type: Union[str, None, UnsetType] = UNSET + """Table Type of the DataverseEntity.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DataverseEntity" + + +class RelatedDataverseAttribute(RelatedDataverse): + """ + Related entity reference for DataverseAttribute assets. + + Extends RelatedDataverse with DataverseAttribute-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DataverseAttribute" so it serializes correctly + + dataverse_entity_qualified_name: Union[str, None, UnsetType] = UNSET + """Entity Qualified Name of the DataverseAttribute.""" + + dataverse_attribute_schema_name: Union[str, None, UnsetType] = UNSET + """Schema Name of the DataverseAttribute.""" + + dataverse_attribute_type: Union[str, None, UnsetType] = UNSET + """Type of the DataverseAttribute.""" + + dataverse_attribute_is_primary_id: Union[bool, None, UnsetType] = UNSET + """Indicator if DataverseAttribute is the primary key.""" + + dataverse_attribute_is_searchable: Union[bool, None, UnsetType] = UNSET + """Indicator if DataverseAttribute is searchable.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DataverseAttribute" diff --git a/pyatlan_v9/model/assets/dbt.py b/pyatlan_v9/model/assets/dbt.py new file mode 100644 index 000000000..890d58f47 --- /dev/null +++ b/pyatlan_v9/model/assets/dbt.py @@ -0,0 +1,722 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Dbt asset model with flattened inheritance. + +This module provides: +- Dbt: Flat asset class (easy to use) +- DbtAttributes: Nested attributes struct (extends AssetAttributes) +- DbtNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Dbt(Asset): + """ + Base class for dbt assets. + """ + + DBT_ALIAS: ClassVar[Any] = None + DBT_META: ClassVar[Any] = None + DBT_UNIQUE_ID: ClassVar[Any] = None + DBT_ACCOUNT_NAME: ClassVar[Any] = None + DBT_PROJECT_NAME: ClassVar[Any] = None + DBT_PACKAGE_NAME: ClassVar[Any] = None + DBT_JOB_NAME: ClassVar[Any] = None + DBT_JOB_SCHEDULE: ClassVar[Any] = None + DBT_JOB_STATUS: ClassVar[Any] = None + DBT_JOB_SCHEDULE_CRON_HUMANIZED: ClassVar[Any] = None + DBT_JOB_LAST_RUN: ClassVar[Any] = None + DBT_JOB_NEXT_RUN: ClassVar[Any] = None + DBT_JOB_NEXT_RUN_HUMANIZED: ClassVar[Any] = None + DBT_ENVIRONMENT_NAME: ClassVar[Any] = None + DBT_ENVIRONMENT_DBT_VERSION: ClassVar[Any] = None + DBT_TAGS: ClassVar[Any] = None + DBT_CONNECTION_CONTEXT: ClassVar[Any] = None + DBT_SEMANTIC_LAYER_PROXY_URL: ClassVar[Any] = None + DBT_JOB_RUNS: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Dbt" + + dbt_alias: Union[str, None, UnsetType] = UNSET + """Alias of this asset in dbt.""" + + dbt_meta: Union[str, None, UnsetType] = UNSET + """Metadata for this asset in dbt, specifically everything under the 'meta' key in the dbt object.""" + + dbt_unique_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of this asset in dbt.""" + + dbt_account_name: Union[str, None, UnsetType] = UNSET + """Name of the account in which this asset exists in dbt.""" + + dbt_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which this asset exists in dbt.""" + + dbt_package_name: Union[str, None, UnsetType] = UNSET + """Name of the package in which this asset exists in dbt.""" + + dbt_job_name: Union[str, None, UnsetType] = UNSET + """Name of the job that materialized this asset in dbt.""" + + dbt_job_schedule: Union[str, None, UnsetType] = UNSET + """Schedule of the job that materialized this asset in dbt.""" + + dbt_job_status: Union[str, None, UnsetType] = UNSET + """Status of the job that materialized this asset in dbt.""" + + dbt_job_schedule_cron_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable cron schedule of the job that materialized this asset in dbt.""" + + dbt_job_last_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt last ran, in milliseconds.""" + + dbt_job_next_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt will next run, in milliseconds.""" + + dbt_job_next_run_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable time at which the job that materialized this asset in dbt will next run.""" + + dbt_environment_name: Union[str, None, UnsetType] = UNSET + """Name of the environment in which this asset exists in dbt.""" + + dbt_environment_dbt_version: Union[str, None, UnsetType] = UNSET + """Version of dbt used in the environment.""" + + dbt_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset in dbt.""" + + dbt_connection_context: Union[str, None, UnsetType] = UNSET + """Connection context for this asset in dbt.""" + + dbt_semantic_layer_proxy_url: Union[str, None, UnsetType] = UNSET + """URL of the semantic layer proxy for this asset in dbt.""" + + dbt_job_runs: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of latest dbt job runs across all environments.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Dbt" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _dbt_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Dbt: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Dbt instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _dbt_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DbtAttributes(AssetAttributes): + """Dbt-specific attributes for nested API format.""" + + dbt_alias: Union[str, None, UnsetType] = UNSET + """Alias of this asset in dbt.""" + + dbt_meta: Union[str, None, UnsetType] = UNSET + """Metadata for this asset in dbt, specifically everything under the 'meta' key in the dbt object.""" + + dbt_unique_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of this asset in dbt.""" + + dbt_account_name: Union[str, None, UnsetType] = UNSET + """Name of the account in which this asset exists in dbt.""" + + dbt_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which this asset exists in dbt.""" + + dbt_package_name: Union[str, None, UnsetType] = UNSET + """Name of the package in which this asset exists in dbt.""" + + dbt_job_name: Union[str, None, UnsetType] = UNSET + """Name of the job that materialized this asset in dbt.""" + + dbt_job_schedule: Union[str, None, UnsetType] = UNSET + """Schedule of the job that materialized this asset in dbt.""" + + dbt_job_status: Union[str, None, UnsetType] = UNSET + """Status of the job that materialized this asset in dbt.""" + + dbt_job_schedule_cron_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable cron schedule of the job that materialized this asset in dbt.""" + + dbt_job_last_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt last ran, in milliseconds.""" + + dbt_job_next_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt will next run, in milliseconds.""" + + dbt_job_next_run_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable time at which the job that materialized this asset in dbt will next run.""" + + dbt_environment_name: Union[str, None, UnsetType] = UNSET + """Name of the environment in which this asset exists in dbt.""" + + dbt_environment_dbt_version: Union[str, None, UnsetType] = UNSET + """Version of dbt used in the environment.""" + + dbt_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset in dbt.""" + + dbt_connection_context: Union[str, None, UnsetType] = UNSET + """Connection context for this asset in dbt.""" + + dbt_semantic_layer_proxy_url: Union[str, None, UnsetType] = UNSET + """URL of the semantic layer proxy for this asset in dbt.""" + + dbt_job_runs: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of latest dbt job runs across all environments.""" + + +class DbtRelationshipAttributes(AssetRelationshipAttributes): + """Dbt-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DbtNested(AssetNested): + """Dbt in nested API format for high-performance serialization.""" + + attributes: Union[DbtAttributes, UnsetType] = UNSET + relationship_attributes: Union[DbtRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[DbtRelationshipAttributes, UnsetType] = UNSET + remove_relationship_attributes: Union[DbtRelationshipAttributes, UnsetType] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DBT_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_dbt_attrs(attrs: DbtAttributes, obj: Dbt) -> None: + """Populate Dbt-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.dbt_alias = obj.dbt_alias + attrs.dbt_meta = obj.dbt_meta + attrs.dbt_unique_id = obj.dbt_unique_id + attrs.dbt_account_name = obj.dbt_account_name + attrs.dbt_project_name = obj.dbt_project_name + attrs.dbt_package_name = obj.dbt_package_name + attrs.dbt_job_name = obj.dbt_job_name + attrs.dbt_job_schedule = obj.dbt_job_schedule + attrs.dbt_job_status = obj.dbt_job_status + attrs.dbt_job_schedule_cron_humanized = obj.dbt_job_schedule_cron_humanized + attrs.dbt_job_last_run = obj.dbt_job_last_run + attrs.dbt_job_next_run = obj.dbt_job_next_run + attrs.dbt_job_next_run_humanized = obj.dbt_job_next_run_humanized + attrs.dbt_environment_name = obj.dbt_environment_name + attrs.dbt_environment_dbt_version = obj.dbt_environment_dbt_version + attrs.dbt_tags = obj.dbt_tags + attrs.dbt_connection_context = obj.dbt_connection_context + attrs.dbt_semantic_layer_proxy_url = obj.dbt_semantic_layer_proxy_url + attrs.dbt_job_runs = obj.dbt_job_runs + + +def _extract_dbt_attrs(attrs: DbtAttributes) -> dict: + """Extract all Dbt attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["dbt_alias"] = attrs.dbt_alias + result["dbt_meta"] = attrs.dbt_meta + result["dbt_unique_id"] = attrs.dbt_unique_id + result["dbt_account_name"] = attrs.dbt_account_name + result["dbt_project_name"] = attrs.dbt_project_name + result["dbt_package_name"] = attrs.dbt_package_name + result["dbt_job_name"] = attrs.dbt_job_name + result["dbt_job_schedule"] = attrs.dbt_job_schedule + result["dbt_job_status"] = attrs.dbt_job_status + result["dbt_job_schedule_cron_humanized"] = attrs.dbt_job_schedule_cron_humanized + result["dbt_job_last_run"] = attrs.dbt_job_last_run + result["dbt_job_next_run"] = attrs.dbt_job_next_run + result["dbt_job_next_run_humanized"] = attrs.dbt_job_next_run_humanized + result["dbt_environment_name"] = attrs.dbt_environment_name + result["dbt_environment_dbt_version"] = attrs.dbt_environment_dbt_version + result["dbt_tags"] = attrs.dbt_tags + result["dbt_connection_context"] = attrs.dbt_connection_context + result["dbt_semantic_layer_proxy_url"] = attrs.dbt_semantic_layer_proxy_url + result["dbt_job_runs"] = attrs.dbt_job_runs + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _dbt_to_nested(dbt: Dbt) -> DbtNested: + """Convert flat Dbt to nested format.""" + attrs = DbtAttributes() + _populate_dbt_attrs(attrs, dbt) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + dbt, _DBT_REL_FIELDS, DbtRelationshipAttributes + ) + return DbtNested( + guid=dbt.guid, + type_name=dbt.type_name, + status=dbt.status, + version=dbt.version, + create_time=dbt.create_time, + update_time=dbt.update_time, + created_by=dbt.created_by, + updated_by=dbt.updated_by, + classifications=dbt.classifications, + classification_names=dbt.classification_names, + meanings=dbt.meanings, + labels=dbt.labels, + business_attributes=dbt.business_attributes, + custom_attributes=dbt.custom_attributes, + pending_tasks=dbt.pending_tasks, + proxy=dbt.proxy, + is_incomplete=dbt.is_incomplete, + provenance_type=dbt.provenance_type, + home_id=dbt.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _dbt_from_nested(nested: DbtNested) -> Dbt: + """Convert nested format to flat Dbt.""" + attrs = nested.attributes if nested.attributes is not UNSET else DbtAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DBT_REL_FIELDS, + DbtRelationshipAttributes, + ) + return Dbt( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_dbt_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _dbt_to_nested_bytes(dbt: Dbt, serde: Serde) -> bytes: + """Convert flat Dbt to nested JSON bytes.""" + return serde.encode(_dbt_to_nested(dbt)) + + +def _dbt_from_nested_bytes(data: bytes, serde: Serde) -> Dbt: + """Convert nested JSON bytes to flat Dbt.""" + nested = serde.decode(data, DbtNested) + return _dbt_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +Dbt.DBT_ALIAS = KeywordField("dbtAlias", "dbtAlias") +Dbt.DBT_META = KeywordField("dbtMeta", "dbtMeta") +Dbt.DBT_UNIQUE_ID = KeywordField("dbtUniqueId", "dbtUniqueId") +Dbt.DBT_ACCOUNT_NAME = KeywordField("dbtAccountName", "dbtAccountName") +Dbt.DBT_PROJECT_NAME = KeywordField("dbtProjectName", "dbtProjectName") +Dbt.DBT_PACKAGE_NAME = KeywordField("dbtPackageName", "dbtPackageName") +Dbt.DBT_JOB_NAME = KeywordField("dbtJobName", "dbtJobName") +Dbt.DBT_JOB_SCHEDULE = KeywordField("dbtJobSchedule", "dbtJobSchedule") +Dbt.DBT_JOB_STATUS = KeywordField("dbtJobStatus", "dbtJobStatus") +Dbt.DBT_JOB_SCHEDULE_CRON_HUMANIZED = KeywordField( + "dbtJobScheduleCronHumanized", "dbtJobScheduleCronHumanized" +) +Dbt.DBT_JOB_LAST_RUN = NumericField("dbtJobLastRun", "dbtJobLastRun") +Dbt.DBT_JOB_NEXT_RUN = NumericField("dbtJobNextRun", "dbtJobNextRun") +Dbt.DBT_JOB_NEXT_RUN_HUMANIZED = KeywordField( + "dbtJobNextRunHumanized", "dbtJobNextRunHumanized" +) +Dbt.DBT_ENVIRONMENT_NAME = KeywordField("dbtEnvironmentName", "dbtEnvironmentName") +Dbt.DBT_ENVIRONMENT_DBT_VERSION = KeywordField( + "dbtEnvironmentDbtVersion", "dbtEnvironmentDbtVersion" +) +Dbt.DBT_TAGS = KeywordField("dbtTags", "dbtTags") +Dbt.DBT_CONNECTION_CONTEXT = KeywordField( + "dbtConnectionContext", "dbtConnectionContext" +) +Dbt.DBT_SEMANTIC_LAYER_PROXY_URL = KeywordField( + "dbtSemanticLayerProxyUrl", "dbtSemanticLayerProxyUrl" +) +Dbt.DBT_JOB_RUNS = KeywordField("dbtJobRuns", "dbtJobRuns") +Dbt.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Dbt.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Dbt.ANOMALO_CHECKS = RelationField("anomaloChecks") +Dbt.APPLICATION = RelationField("application") +Dbt.APPLICATION_FIELD = RelationField("applicationField") +Dbt.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Dbt.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Dbt.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Dbt.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Dbt.METRICS = RelationField("metrics") +Dbt.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Dbt.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Dbt.MEANINGS = RelationField("meanings") +Dbt.MC_MONITORS = RelationField("mcMonitors") +Dbt.MC_INCIDENTS = RelationField("mcIncidents") +Dbt.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Dbt.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Dbt.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Dbt.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Dbt.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Dbt.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Dbt.FILES = RelationField("files") +Dbt.LINKS = RelationField("links") +Dbt.README = RelationField("readme") +Dbt.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Dbt.SODA_CHECKS = RelationField("sodaChecks") +Dbt.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Dbt.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/dbt_column_process.py b/pyatlan_v9/model/assets/dbt_column_process.py new file mode 100644 index 000000000..4c5bc5bd1 --- /dev/null +++ b/pyatlan_v9/model/assets/dbt_column_process.py @@ -0,0 +1,967 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DbtColumnProcess asset model with flattened inheritance. + +This module provides: +- DbtColumnProcess: Flat asset class (easy to use) +- DbtColumnProcessAttributes: Nested attributes struct (extends AssetAttributes) +- DbtColumnProcessNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .adf_related import RelatedAdfActivity +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .catalog_related import RelatedCatalog +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .fabric_related import RelatedFabricActivity +from .fivetran_related import RelatedFivetranConnector +from .flow_related import RelatedFlowControlOperation +from .gtc_related import RelatedAtlasGlossaryTerm +from .matillion_related import RelatedMatillionComponent +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .power_bi_related import RelatedPowerBIDataflow +from .process_related import RelatedColumnProcess, RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from .sql_related import RelatedFunction, RelatedProcedure +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class DbtColumnProcess(Asset): + """ + Instance of a column-level dbt process in Atlan. + """ + + DBT_COLUMN_PROCESS_JOB_STATUS: ClassVar[Any] = None + DBT_ALIAS: ClassVar[Any] = None + DBT_META: ClassVar[Any] = None + DBT_UNIQUE_ID: ClassVar[Any] = None + DBT_ACCOUNT_NAME: ClassVar[Any] = None + DBT_PROJECT_NAME: ClassVar[Any] = None + DBT_PACKAGE_NAME: ClassVar[Any] = None + DBT_JOB_NAME: ClassVar[Any] = None + DBT_JOB_SCHEDULE: ClassVar[Any] = None + DBT_JOB_STATUS: ClassVar[Any] = None + DBT_JOB_SCHEDULE_CRON_HUMANIZED: ClassVar[Any] = None + DBT_JOB_LAST_RUN: ClassVar[Any] = None + DBT_JOB_NEXT_RUN: ClassVar[Any] = None + DBT_JOB_NEXT_RUN_HUMANIZED: ClassVar[Any] = None + DBT_ENVIRONMENT_NAME: ClassVar[Any] = None + DBT_ENVIRONMENT_DBT_VERSION: ClassVar[Any] = None + DBT_TAGS: ClassVar[Any] = None + DBT_CONNECTION_CONTEXT: ClassVar[Any] = None + DBT_SEMANTIC_LAYER_PROXY_URL: ClassVar[Any] = None + DBT_JOB_RUNS: ClassVar[Any] = None + CODE: ClassVar[Any] = None + SQL: ClassVar[Any] = None + PARENT_CONNECTION_PROCESS_QUALIFIED_NAME: ClassVar[Any] = None + AST: ClassVar[Any] = None + ADDITIONAL_ETL_CONTEXT: ClassVar[Any] = None + AI_DATASET_TYPE: ClassVar[Any] = None + ADF_ACTIVITY: ClassVar[Any] = None + AIRFLOW_TASKS: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + FABRIC_ACTIVITIES: ClassVar[Any] = None + FIVETRAN_CONNECTOR: ClassVar[Any] = None + FLOW_ORCHESTRATED_BY: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MATILLION_COMPONENT: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + POWER_BI_DATAFLOW: ClassVar[Any] = None + INPUTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUTS: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + COLUMN_PROCESSES: ClassVar[Any] = None + PROCESS: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SQL_PROCEDURES: ClassVar[Any] = None + SQL_FUNCTIONS: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + SPARK_JOBS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "DbtColumnProcess" + + dbt_column_process_job_status: Union[str, None, UnsetType] = UNSET + """Status of the dbt column process job.""" + + dbt_alias: Union[str, None, UnsetType] = UNSET + """Alias of this asset in dbt.""" + + dbt_meta: Union[str, None, UnsetType] = UNSET + """Metadata for this asset in dbt, specifically everything under the 'meta' key in the dbt object.""" + + dbt_unique_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of this asset in dbt.""" + + dbt_account_name: Union[str, None, UnsetType] = UNSET + """Name of the account in which this asset exists in dbt.""" + + dbt_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which this asset exists in dbt.""" + + dbt_package_name: Union[str, None, UnsetType] = UNSET + """Name of the package in which this asset exists in dbt.""" + + dbt_job_name: Union[str, None, UnsetType] = UNSET + """Name of the job that materialized this asset in dbt.""" + + dbt_job_schedule: Union[str, None, UnsetType] = UNSET + """Schedule of the job that materialized this asset in dbt.""" + + dbt_job_status: Union[str, None, UnsetType] = UNSET + """Status of the job that materialized this asset in dbt.""" + + dbt_job_schedule_cron_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable cron schedule of the job that materialized this asset in dbt.""" + + dbt_job_last_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt last ran, in milliseconds.""" + + dbt_job_next_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt will next run, in milliseconds.""" + + dbt_job_next_run_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable time at which the job that materialized this asset in dbt will next run.""" + + dbt_environment_name: Union[str, None, UnsetType] = UNSET + """Name of the environment in which this asset exists in dbt.""" + + dbt_environment_dbt_version: Union[str, None, UnsetType] = UNSET + """Version of dbt used in the environment.""" + + dbt_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset in dbt.""" + + dbt_connection_context: Union[str, None, UnsetType] = UNSET + """Connection context for this asset in dbt.""" + + dbt_semantic_layer_proxy_url: Union[str, None, UnsetType] = UNSET + """URL of the semantic layer proxy for this asset in dbt.""" + + dbt_job_runs: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of latest dbt job runs across all environments.""" + + code: Union[str, None, UnsetType] = UNSET + """Code that ran within the process.""" + + sql: Union[str, None, UnsetType] = UNSET + """SQL query that ran to produce the outputs.""" + + parent_connection_process_qualified_name: Union[List[str], None, UnsetType] = UNSET + """""" + + ast: Union[str, None, UnsetType] = UNSET + """Parsed AST of the code or SQL statements that describe the logic of this process.""" + + additional_etl_context: Union[str, None, UnsetType] = UNSET + """Additional Context of the ETL pipeline/notebook which creates the process.""" + + ai_dataset_type: Union[str, None, UnsetType] = UNSET + """Dataset type for AI Model - dataset process.""" + + adf_activity: Union[RelatedAdfActivity, None, UnsetType] = UNSET + """ADF Activity that is associated with this lineage process.""" + + airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks that exist within this process.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + fabric_activities: Union[List[RelatedFabricActivity], None, UnsetType] = UNSET + """Individual Fabric activities contained in the process.""" + + fivetran_connector: Union[RelatedFivetranConnector, None, UnsetType] = UNSET + """fivetranConnector in which this process exists.""" + + flow_orchestrated_by: Union[RelatedFlowControlOperation, None, UnsetType] = UNSET + """Orchestrated control operation that ran these data flows (process).""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + matillion_component: Union[RelatedMatillionComponent, None, UnsetType] = UNSET + """Matillion component that contains the logic for this lineage process.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + power_bi_dataflow: Union[RelatedPowerBIDataflow, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIDataflow" + ) + """PowerBI Dataflow that is associated with this lineage process.""" + + inputs: Union[List[RelatedCatalog], None, UnsetType] = UNSET + """Assets that are inputs to this process.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + outputs: Union[List[RelatedCatalog], None, UnsetType] = UNSET + """Assets that are outputs from this process.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + column_processes: Union[List[RelatedColumnProcess], None, UnsetType] = UNSET + """Processes that detail column-level lineage for this process.""" + + process: Union[RelatedProcess, None, UnsetType] = UNSET + """Parent process that contains this column-level process.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + sql_procedures: Union[List[RelatedProcedure], None, UnsetType] = UNSET + """Procedures used by this process.""" + + sql_functions: Union[List[RelatedFunction], None, UnsetType] = UNSET + """Functions used by this process.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "DbtColumnProcess" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _dbt_column_process_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> DbtColumnProcess: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + DbtColumnProcess instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _dbt_column_process_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DbtColumnProcessAttributes(AssetAttributes): + """DbtColumnProcess-specific attributes for nested API format.""" + + dbt_column_process_job_status: Union[str, None, UnsetType] = UNSET + """Status of the dbt column process job.""" + + dbt_alias: Union[str, None, UnsetType] = UNSET + """Alias of this asset in dbt.""" + + dbt_meta: Union[str, None, UnsetType] = UNSET + """Metadata for this asset in dbt, specifically everything under the 'meta' key in the dbt object.""" + + dbt_unique_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of this asset in dbt.""" + + dbt_account_name: Union[str, None, UnsetType] = UNSET + """Name of the account in which this asset exists in dbt.""" + + dbt_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which this asset exists in dbt.""" + + dbt_package_name: Union[str, None, UnsetType] = UNSET + """Name of the package in which this asset exists in dbt.""" + + dbt_job_name: Union[str, None, UnsetType] = UNSET + """Name of the job that materialized this asset in dbt.""" + + dbt_job_schedule: Union[str, None, UnsetType] = UNSET + """Schedule of the job that materialized this asset in dbt.""" + + dbt_job_status: Union[str, None, UnsetType] = UNSET + """Status of the job that materialized this asset in dbt.""" + + dbt_job_schedule_cron_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable cron schedule of the job that materialized this asset in dbt.""" + + dbt_job_last_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt last ran, in milliseconds.""" + + dbt_job_next_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt will next run, in milliseconds.""" + + dbt_job_next_run_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable time at which the job that materialized this asset in dbt will next run.""" + + dbt_environment_name: Union[str, None, UnsetType] = UNSET + """Name of the environment in which this asset exists in dbt.""" + + dbt_environment_dbt_version: Union[str, None, UnsetType] = UNSET + """Version of dbt used in the environment.""" + + dbt_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset in dbt.""" + + dbt_connection_context: Union[str, None, UnsetType] = UNSET + """Connection context for this asset in dbt.""" + + dbt_semantic_layer_proxy_url: Union[str, None, UnsetType] = UNSET + """URL of the semantic layer proxy for this asset in dbt.""" + + dbt_job_runs: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of latest dbt job runs across all environments.""" + + code: Union[str, None, UnsetType] = UNSET + """Code that ran within the process.""" + + sql: Union[str, None, UnsetType] = UNSET + """SQL query that ran to produce the outputs.""" + + parent_connection_process_qualified_name: Union[List[str], None, UnsetType] = UNSET + """""" + + ast: Union[str, None, UnsetType] = UNSET + """Parsed AST of the code or SQL statements that describe the logic of this process.""" + + additional_etl_context: Union[str, None, UnsetType] = UNSET + """Additional Context of the ETL pipeline/notebook which creates the process.""" + + ai_dataset_type: Union[str, None, UnsetType] = UNSET + """Dataset type for AI Model - dataset process.""" + + +class DbtColumnProcessRelationshipAttributes(AssetRelationshipAttributes): + """DbtColumnProcess-specific relationship attributes for nested API format.""" + + adf_activity: Union[RelatedAdfActivity, None, UnsetType] = UNSET + """ADF Activity that is associated with this lineage process.""" + + airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks that exist within this process.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + fabric_activities: Union[List[RelatedFabricActivity], None, UnsetType] = UNSET + """Individual Fabric activities contained in the process.""" + + fivetran_connector: Union[RelatedFivetranConnector, None, UnsetType] = UNSET + """fivetranConnector in which this process exists.""" + + flow_orchestrated_by: Union[RelatedFlowControlOperation, None, UnsetType] = UNSET + """Orchestrated control operation that ran these data flows (process).""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + matillion_component: Union[RelatedMatillionComponent, None, UnsetType] = UNSET + """Matillion component that contains the logic for this lineage process.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + power_bi_dataflow: Union[RelatedPowerBIDataflow, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIDataflow" + ) + """PowerBI Dataflow that is associated with this lineage process.""" + + inputs: Union[List[RelatedCatalog], None, UnsetType] = UNSET + """Assets that are inputs to this process.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + outputs: Union[List[RelatedCatalog], None, UnsetType] = UNSET + """Assets that are outputs from this process.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + column_processes: Union[List[RelatedColumnProcess], None, UnsetType] = UNSET + """Processes that detail column-level lineage for this process.""" + + process: Union[RelatedProcess, None, UnsetType] = UNSET + """Parent process that contains this column-level process.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + sql_procedures: Union[List[RelatedProcedure], None, UnsetType] = UNSET + """Procedures used by this process.""" + + sql_functions: Union[List[RelatedFunction], None, UnsetType] = UNSET + """Functions used by this process.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DbtColumnProcessNested(AssetNested): + """DbtColumnProcess in nested API format for high-performance serialization.""" + + attributes: Union[DbtColumnProcessAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + DbtColumnProcessRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + DbtColumnProcessRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + DbtColumnProcessRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DBT_COLUMN_PROCESS_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "adf_activity", + "airflow_tasks", + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "fabric_activities", + "fivetran_connector", + "flow_orchestrated_by", + "meanings", + "matillion_component", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "power_bi_dataflow", + "inputs", + "input_to_processes", + "outputs", + "output_from_processes", + "column_processes", + "process", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "sql_procedures", + "sql_functions", + "schema_registry_subjects", + "soda_checks", + "spark_jobs", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_dbt_column_process_attrs( + attrs: DbtColumnProcessAttributes, obj: DbtColumnProcess +) -> None: + """Populate DbtColumnProcess-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.dbt_column_process_job_status = obj.dbt_column_process_job_status + attrs.dbt_alias = obj.dbt_alias + attrs.dbt_meta = obj.dbt_meta + attrs.dbt_unique_id = obj.dbt_unique_id + attrs.dbt_account_name = obj.dbt_account_name + attrs.dbt_project_name = obj.dbt_project_name + attrs.dbt_package_name = obj.dbt_package_name + attrs.dbt_job_name = obj.dbt_job_name + attrs.dbt_job_schedule = obj.dbt_job_schedule + attrs.dbt_job_status = obj.dbt_job_status + attrs.dbt_job_schedule_cron_humanized = obj.dbt_job_schedule_cron_humanized + attrs.dbt_job_last_run = obj.dbt_job_last_run + attrs.dbt_job_next_run = obj.dbt_job_next_run + attrs.dbt_job_next_run_humanized = obj.dbt_job_next_run_humanized + attrs.dbt_environment_name = obj.dbt_environment_name + attrs.dbt_environment_dbt_version = obj.dbt_environment_dbt_version + attrs.dbt_tags = obj.dbt_tags + attrs.dbt_connection_context = obj.dbt_connection_context + attrs.dbt_semantic_layer_proxy_url = obj.dbt_semantic_layer_proxy_url + attrs.dbt_job_runs = obj.dbt_job_runs + attrs.code = obj.code + attrs.sql = obj.sql + attrs.parent_connection_process_qualified_name = ( + obj.parent_connection_process_qualified_name + ) + attrs.ast = obj.ast + attrs.additional_etl_context = obj.additional_etl_context + attrs.ai_dataset_type = obj.ai_dataset_type + + +def _extract_dbt_column_process_attrs(attrs: DbtColumnProcessAttributes) -> dict: + """Extract all DbtColumnProcess attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["dbt_column_process_job_status"] = attrs.dbt_column_process_job_status + result["dbt_alias"] = attrs.dbt_alias + result["dbt_meta"] = attrs.dbt_meta + result["dbt_unique_id"] = attrs.dbt_unique_id + result["dbt_account_name"] = attrs.dbt_account_name + result["dbt_project_name"] = attrs.dbt_project_name + result["dbt_package_name"] = attrs.dbt_package_name + result["dbt_job_name"] = attrs.dbt_job_name + result["dbt_job_schedule"] = attrs.dbt_job_schedule + result["dbt_job_status"] = attrs.dbt_job_status + result["dbt_job_schedule_cron_humanized"] = attrs.dbt_job_schedule_cron_humanized + result["dbt_job_last_run"] = attrs.dbt_job_last_run + result["dbt_job_next_run"] = attrs.dbt_job_next_run + result["dbt_job_next_run_humanized"] = attrs.dbt_job_next_run_humanized + result["dbt_environment_name"] = attrs.dbt_environment_name + result["dbt_environment_dbt_version"] = attrs.dbt_environment_dbt_version + result["dbt_tags"] = attrs.dbt_tags + result["dbt_connection_context"] = attrs.dbt_connection_context + result["dbt_semantic_layer_proxy_url"] = attrs.dbt_semantic_layer_proxy_url + result["dbt_job_runs"] = attrs.dbt_job_runs + result["code"] = attrs.code + result["sql"] = attrs.sql + result["parent_connection_process_qualified_name"] = ( + attrs.parent_connection_process_qualified_name + ) + result["ast"] = attrs.ast + result["additional_etl_context"] = attrs.additional_etl_context + result["ai_dataset_type"] = attrs.ai_dataset_type + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _dbt_column_process_to_nested( + dbt_column_process: DbtColumnProcess, +) -> DbtColumnProcessNested: + """Convert flat DbtColumnProcess to nested format.""" + attrs = DbtColumnProcessAttributes() + _populate_dbt_column_process_attrs(attrs, dbt_column_process) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + dbt_column_process, + _DBT_COLUMN_PROCESS_REL_FIELDS, + DbtColumnProcessRelationshipAttributes, + ) + return DbtColumnProcessNested( + guid=dbt_column_process.guid, + type_name=dbt_column_process.type_name, + status=dbt_column_process.status, + version=dbt_column_process.version, + create_time=dbt_column_process.create_time, + update_time=dbt_column_process.update_time, + created_by=dbt_column_process.created_by, + updated_by=dbt_column_process.updated_by, + classifications=dbt_column_process.classifications, + classification_names=dbt_column_process.classification_names, + meanings=dbt_column_process.meanings, + labels=dbt_column_process.labels, + business_attributes=dbt_column_process.business_attributes, + custom_attributes=dbt_column_process.custom_attributes, + pending_tasks=dbt_column_process.pending_tasks, + proxy=dbt_column_process.proxy, + is_incomplete=dbt_column_process.is_incomplete, + provenance_type=dbt_column_process.provenance_type, + home_id=dbt_column_process.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _dbt_column_process_from_nested(nested: DbtColumnProcessNested) -> DbtColumnProcess: + """Convert nested format to flat DbtColumnProcess.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else DbtColumnProcessAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DBT_COLUMN_PROCESS_REL_FIELDS, + DbtColumnProcessRelationshipAttributes, + ) + return DbtColumnProcess( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_dbt_column_process_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _dbt_column_process_to_nested_bytes( + dbt_column_process: DbtColumnProcess, serde: Serde +) -> bytes: + """Convert flat DbtColumnProcess to nested JSON bytes.""" + return serde.encode(_dbt_column_process_to_nested(dbt_column_process)) + + +def _dbt_column_process_from_nested_bytes( + data: bytes, serde: Serde +) -> DbtColumnProcess: + """Convert nested JSON bytes to flat DbtColumnProcess.""" + nested = serde.decode(data, DbtColumnProcessNested) + return _dbt_column_process_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +DbtColumnProcess.DBT_COLUMN_PROCESS_JOB_STATUS = KeywordField( + "dbtColumnProcessJobStatus", "dbtColumnProcessJobStatus" +) +DbtColumnProcess.DBT_ALIAS = KeywordField("dbtAlias", "dbtAlias") +DbtColumnProcess.DBT_META = KeywordField("dbtMeta", "dbtMeta") +DbtColumnProcess.DBT_UNIQUE_ID = KeywordField("dbtUniqueId", "dbtUniqueId") +DbtColumnProcess.DBT_ACCOUNT_NAME = KeywordField("dbtAccountName", "dbtAccountName") +DbtColumnProcess.DBT_PROJECT_NAME = KeywordField("dbtProjectName", "dbtProjectName") +DbtColumnProcess.DBT_PACKAGE_NAME = KeywordField("dbtPackageName", "dbtPackageName") +DbtColumnProcess.DBT_JOB_NAME = KeywordField("dbtJobName", "dbtJobName") +DbtColumnProcess.DBT_JOB_SCHEDULE = KeywordField("dbtJobSchedule", "dbtJobSchedule") +DbtColumnProcess.DBT_JOB_STATUS = KeywordField("dbtJobStatus", "dbtJobStatus") +DbtColumnProcess.DBT_JOB_SCHEDULE_CRON_HUMANIZED = KeywordField( + "dbtJobScheduleCronHumanized", "dbtJobScheduleCronHumanized" +) +DbtColumnProcess.DBT_JOB_LAST_RUN = NumericField("dbtJobLastRun", "dbtJobLastRun") +DbtColumnProcess.DBT_JOB_NEXT_RUN = NumericField("dbtJobNextRun", "dbtJobNextRun") +DbtColumnProcess.DBT_JOB_NEXT_RUN_HUMANIZED = KeywordField( + "dbtJobNextRunHumanized", "dbtJobNextRunHumanized" +) +DbtColumnProcess.DBT_ENVIRONMENT_NAME = KeywordField( + "dbtEnvironmentName", "dbtEnvironmentName" +) +DbtColumnProcess.DBT_ENVIRONMENT_DBT_VERSION = KeywordField( + "dbtEnvironmentDbtVersion", "dbtEnvironmentDbtVersion" +) +DbtColumnProcess.DBT_TAGS = KeywordField("dbtTags", "dbtTags") +DbtColumnProcess.DBT_CONNECTION_CONTEXT = KeywordField( + "dbtConnectionContext", "dbtConnectionContext" +) +DbtColumnProcess.DBT_SEMANTIC_LAYER_PROXY_URL = KeywordField( + "dbtSemanticLayerProxyUrl", "dbtSemanticLayerProxyUrl" +) +DbtColumnProcess.DBT_JOB_RUNS = KeywordField("dbtJobRuns", "dbtJobRuns") +DbtColumnProcess.CODE = KeywordField("code", "code") +DbtColumnProcess.SQL = KeywordField("sql", "sql") +DbtColumnProcess.PARENT_CONNECTION_PROCESS_QUALIFIED_NAME = KeywordField( + "parentConnectionProcessQualifiedName", "parentConnectionProcessQualifiedName" +) +DbtColumnProcess.AST = KeywordField("ast", "ast") +DbtColumnProcess.ADDITIONAL_ETL_CONTEXT = KeywordField( + "additionalEtlContext", "additionalEtlContext" +) +DbtColumnProcess.AI_DATASET_TYPE = KeywordField("aiDatasetType", "aiDatasetType") +DbtColumnProcess.ADF_ACTIVITY = RelationField("adfActivity") +DbtColumnProcess.AIRFLOW_TASKS = RelationField("airflowTasks") +DbtColumnProcess.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +DbtColumnProcess.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +DbtColumnProcess.ANOMALO_CHECKS = RelationField("anomaloChecks") +DbtColumnProcess.APPLICATION = RelationField("application") +DbtColumnProcess.APPLICATION_FIELD = RelationField("applicationField") +DbtColumnProcess.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +DbtColumnProcess.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +DbtColumnProcess.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +DbtColumnProcess.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +DbtColumnProcess.METRICS = RelationField("metrics") +DbtColumnProcess.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +DbtColumnProcess.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +DbtColumnProcess.FABRIC_ACTIVITIES = RelationField("fabricActivities") +DbtColumnProcess.FIVETRAN_CONNECTOR = RelationField("fivetranConnector") +DbtColumnProcess.FLOW_ORCHESTRATED_BY = RelationField("flowOrchestratedBy") +DbtColumnProcess.MEANINGS = RelationField("meanings") +DbtColumnProcess.MATILLION_COMPONENT = RelationField("matillionComponent") +DbtColumnProcess.MC_MONITORS = RelationField("mcMonitors") +DbtColumnProcess.MC_INCIDENTS = RelationField("mcIncidents") +DbtColumnProcess.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +DbtColumnProcess.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +DbtColumnProcess.POWER_BI_DATAFLOW = RelationField("powerBIDataflow") +DbtColumnProcess.INPUTS = RelationField("inputs") +DbtColumnProcess.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +DbtColumnProcess.OUTPUTS = RelationField("outputs") +DbtColumnProcess.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +DbtColumnProcess.COLUMN_PROCESSES = RelationField("columnProcesses") +DbtColumnProcess.PROCESS = RelationField("process") +DbtColumnProcess.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +DbtColumnProcess.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +DbtColumnProcess.FILES = RelationField("files") +DbtColumnProcess.LINKS = RelationField("links") +DbtColumnProcess.README = RelationField("readme") +DbtColumnProcess.SQL_PROCEDURES = RelationField("sqlProcedures") +DbtColumnProcess.SQL_FUNCTIONS = RelationField("sqlFunctions") +DbtColumnProcess.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +DbtColumnProcess.SODA_CHECKS = RelationField("sodaChecks") +DbtColumnProcess.SPARK_JOBS = RelationField("sparkJobs") +DbtColumnProcess.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +DbtColumnProcess.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/dbt_dimension.py b/pyatlan_v9/model/assets/dbt_dimension.py new file mode 100644 index 000000000..ee3be1923 --- /dev/null +++ b/pyatlan_v9/model/assets/dbt_dimension.py @@ -0,0 +1,851 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DbtDimension asset model with flattened inheritance. + +This module provides: +- DbtDimension: Flat asset class (easy to use) +- DbtDimensionAttributes: Nested attributes struct (extends AssetAttributes) +- DbtDimensionNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .semantic_related import RelatedSemanticModel +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class DbtDimension(Asset): + """ + Instance of a dbt semantic dimension in Atlan. + """ + + DBT_SEMANTIC_MODEL_QUALIFIED_NAME: ClassVar[Any] = None + DBT_SEMANTIC_FIELD_TIME_GRANULARITY: ClassVar[Any] = None + DBT_ALIAS: ClassVar[Any] = None + DBT_META: ClassVar[Any] = None + DBT_UNIQUE_ID: ClassVar[Any] = None + DBT_ACCOUNT_NAME: ClassVar[Any] = None + DBT_PROJECT_NAME: ClassVar[Any] = None + DBT_PACKAGE_NAME: ClassVar[Any] = None + DBT_JOB_NAME: ClassVar[Any] = None + DBT_JOB_SCHEDULE: ClassVar[Any] = None + DBT_JOB_STATUS: ClassVar[Any] = None + DBT_JOB_SCHEDULE_CRON_HUMANIZED: ClassVar[Any] = None + DBT_JOB_LAST_RUN: ClassVar[Any] = None + DBT_JOB_NEXT_RUN: ClassVar[Any] = None + DBT_JOB_NEXT_RUN_HUMANIZED: ClassVar[Any] = None + DBT_ENVIRONMENT_NAME: ClassVar[Any] = None + DBT_ENVIRONMENT_DBT_VERSION: ClassVar[Any] = None + DBT_TAGS: ClassVar[Any] = None + DBT_CONNECTION_CONTEXT: ClassVar[Any] = None + DBT_SEMANTIC_LAYER_PROXY_URL: ClassVar[Any] = None + DBT_JOB_RUNS: ClassVar[Any] = None + SEMANTIC_EXPRESSION: ClassVar[Any] = None + SEMANTIC_TYPE: ClassVar[Any] = None + SEMANTIC_SYNONYMS: ClassVar[Any] = None + SEMANTIC_SAMPLE_VALUES: ClassVar[Any] = None + SEMANTIC_ACCESS_MODIFIER: ClassVar[Any] = None + SEMANTIC_DATA_TYPE: ClassVar[Any] = None + SEMANTIC_LABELS: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SEMANTIC_MODEL: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "DbtDimension" + + dbt_semantic_model_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the dbt semantic model this dimension belongs to.""" + + dbt_semantic_field_time_granularity: Union[str, None, UnsetType] = UNSET + """Time granularity for time dimensions only (day/week/month/quarter/year).""" + + dbt_alias: Union[str, None, UnsetType] = UNSET + """Alias of this asset in dbt.""" + + dbt_meta: Union[str, None, UnsetType] = UNSET + """Metadata for this asset in dbt, specifically everything under the 'meta' key in the dbt object.""" + + dbt_unique_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of this asset in dbt.""" + + dbt_account_name: Union[str, None, UnsetType] = UNSET + """Name of the account in which this asset exists in dbt.""" + + dbt_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which this asset exists in dbt.""" + + dbt_package_name: Union[str, None, UnsetType] = UNSET + """Name of the package in which this asset exists in dbt.""" + + dbt_job_name: Union[str, None, UnsetType] = UNSET + """Name of the job that materialized this asset in dbt.""" + + dbt_job_schedule: Union[str, None, UnsetType] = UNSET + """Schedule of the job that materialized this asset in dbt.""" + + dbt_job_status: Union[str, None, UnsetType] = UNSET + """Status of the job that materialized this asset in dbt.""" + + dbt_job_schedule_cron_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable cron schedule of the job that materialized this asset in dbt.""" + + dbt_job_last_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt last ran, in milliseconds.""" + + dbt_job_next_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt will next run, in milliseconds.""" + + dbt_job_next_run_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable time at which the job that materialized this asset in dbt will next run.""" + + dbt_environment_name: Union[str, None, UnsetType] = UNSET + """Name of the environment in which this asset exists in dbt.""" + + dbt_environment_dbt_version: Union[str, None, UnsetType] = UNSET + """Version of dbt used in the environment.""" + + dbt_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset in dbt.""" + + dbt_connection_context: Union[str, None, UnsetType] = UNSET + """Connection context for this asset in dbt.""" + + dbt_semantic_layer_proxy_url: Union[str, None, UnsetType] = UNSET + """URL of the semantic layer proxy for this asset in dbt.""" + + dbt_job_runs: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of latest dbt job runs across all environments.""" + + semantic_expression: Union[str, None, UnsetType] = UNSET + """Column name or SQL expression for the semantic field.""" + + semantic_type: Union[str, None, UnsetType] = UNSET + """Detailed type of the semantic field (e.g., type of measure, type of dimension, or type of entity).""" + + semantic_synonyms: Union[List[str], None, UnsetType] = UNSET + """Alternative names or terms for the semantic field.""" + + semantic_sample_values: Union[List[str], None, UnsetType] = UNSET + """Sample values for the semantic field.""" + + semantic_access_modifier: Union[str, None, UnsetType] = UNSET + """Access level for the semantic field (e.g., public_access/private_access).""" + + semantic_data_type: Union[str, None, UnsetType] = UNSET + """Data type of the semantic field.""" + + semantic_labels: Union[List[str], None, UnsetType] = UNSET + """Labels associated with the semantic field.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + semantic_model: Union[RelatedSemanticModel, None, UnsetType] = UNSET + """Semantic model in which this dimension exists.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "DbtDimension" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _dbt_dimension_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> DbtDimension: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + DbtDimension instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _dbt_dimension_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DbtDimensionAttributes(AssetAttributes): + """DbtDimension-specific attributes for nested API format.""" + + dbt_semantic_model_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the dbt semantic model this dimension belongs to.""" + + dbt_semantic_field_time_granularity: Union[str, None, UnsetType] = UNSET + """Time granularity for time dimensions only (day/week/month/quarter/year).""" + + dbt_alias: Union[str, None, UnsetType] = UNSET + """Alias of this asset in dbt.""" + + dbt_meta: Union[str, None, UnsetType] = UNSET + """Metadata for this asset in dbt, specifically everything under the 'meta' key in the dbt object.""" + + dbt_unique_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of this asset in dbt.""" + + dbt_account_name: Union[str, None, UnsetType] = UNSET + """Name of the account in which this asset exists in dbt.""" + + dbt_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which this asset exists in dbt.""" + + dbt_package_name: Union[str, None, UnsetType] = UNSET + """Name of the package in which this asset exists in dbt.""" + + dbt_job_name: Union[str, None, UnsetType] = UNSET + """Name of the job that materialized this asset in dbt.""" + + dbt_job_schedule: Union[str, None, UnsetType] = UNSET + """Schedule of the job that materialized this asset in dbt.""" + + dbt_job_status: Union[str, None, UnsetType] = UNSET + """Status of the job that materialized this asset in dbt.""" + + dbt_job_schedule_cron_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable cron schedule of the job that materialized this asset in dbt.""" + + dbt_job_last_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt last ran, in milliseconds.""" + + dbt_job_next_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt will next run, in milliseconds.""" + + dbt_job_next_run_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable time at which the job that materialized this asset in dbt will next run.""" + + dbt_environment_name: Union[str, None, UnsetType] = UNSET + """Name of the environment in which this asset exists in dbt.""" + + dbt_environment_dbt_version: Union[str, None, UnsetType] = UNSET + """Version of dbt used in the environment.""" + + dbt_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset in dbt.""" + + dbt_connection_context: Union[str, None, UnsetType] = UNSET + """Connection context for this asset in dbt.""" + + dbt_semantic_layer_proxy_url: Union[str, None, UnsetType] = UNSET + """URL of the semantic layer proxy for this asset in dbt.""" + + dbt_job_runs: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of latest dbt job runs across all environments.""" + + semantic_expression: Union[str, None, UnsetType] = UNSET + """Column name or SQL expression for the semantic field.""" + + semantic_type: Union[str, None, UnsetType] = UNSET + """Detailed type of the semantic field (e.g., type of measure, type of dimension, or type of entity).""" + + semantic_synonyms: Union[List[str], None, UnsetType] = UNSET + """Alternative names or terms for the semantic field.""" + + semantic_sample_values: Union[List[str], None, UnsetType] = UNSET + """Sample values for the semantic field.""" + + semantic_access_modifier: Union[str, None, UnsetType] = UNSET + """Access level for the semantic field (e.g., public_access/private_access).""" + + semantic_data_type: Union[str, None, UnsetType] = UNSET + """Data type of the semantic field.""" + + semantic_labels: Union[List[str], None, UnsetType] = UNSET + """Labels associated with the semantic field.""" + + +class DbtDimensionRelationshipAttributes(AssetRelationshipAttributes): + """DbtDimension-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + semantic_model: Union[RelatedSemanticModel, None, UnsetType] = UNSET + """Semantic model in which this dimension exists.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DbtDimensionNested(AssetNested): + """DbtDimension in nested API format for high-performance serialization.""" + + attributes: Union[DbtDimensionAttributes, UnsetType] = UNSET + relationship_attributes: Union[DbtDimensionRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + DbtDimensionRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + DbtDimensionRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DBT_DIMENSION_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "semantic_model", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_dbt_dimension_attrs( + attrs: DbtDimensionAttributes, obj: DbtDimension +) -> None: + """Populate DbtDimension-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.dbt_semantic_model_qualified_name = obj.dbt_semantic_model_qualified_name + attrs.dbt_semantic_field_time_granularity = obj.dbt_semantic_field_time_granularity + attrs.dbt_alias = obj.dbt_alias + attrs.dbt_meta = obj.dbt_meta + attrs.dbt_unique_id = obj.dbt_unique_id + attrs.dbt_account_name = obj.dbt_account_name + attrs.dbt_project_name = obj.dbt_project_name + attrs.dbt_package_name = obj.dbt_package_name + attrs.dbt_job_name = obj.dbt_job_name + attrs.dbt_job_schedule = obj.dbt_job_schedule + attrs.dbt_job_status = obj.dbt_job_status + attrs.dbt_job_schedule_cron_humanized = obj.dbt_job_schedule_cron_humanized + attrs.dbt_job_last_run = obj.dbt_job_last_run + attrs.dbt_job_next_run = obj.dbt_job_next_run + attrs.dbt_job_next_run_humanized = obj.dbt_job_next_run_humanized + attrs.dbt_environment_name = obj.dbt_environment_name + attrs.dbt_environment_dbt_version = obj.dbt_environment_dbt_version + attrs.dbt_tags = obj.dbt_tags + attrs.dbt_connection_context = obj.dbt_connection_context + attrs.dbt_semantic_layer_proxy_url = obj.dbt_semantic_layer_proxy_url + attrs.dbt_job_runs = obj.dbt_job_runs + attrs.semantic_expression = obj.semantic_expression + attrs.semantic_type = obj.semantic_type + attrs.semantic_synonyms = obj.semantic_synonyms + attrs.semantic_sample_values = obj.semantic_sample_values + attrs.semantic_access_modifier = obj.semantic_access_modifier + attrs.semantic_data_type = obj.semantic_data_type + attrs.semantic_labels = obj.semantic_labels + + +def _extract_dbt_dimension_attrs(attrs: DbtDimensionAttributes) -> dict: + """Extract all DbtDimension attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["dbt_semantic_model_qualified_name"] = ( + attrs.dbt_semantic_model_qualified_name + ) + result["dbt_semantic_field_time_granularity"] = ( + attrs.dbt_semantic_field_time_granularity + ) + result["dbt_alias"] = attrs.dbt_alias + result["dbt_meta"] = attrs.dbt_meta + result["dbt_unique_id"] = attrs.dbt_unique_id + result["dbt_account_name"] = attrs.dbt_account_name + result["dbt_project_name"] = attrs.dbt_project_name + result["dbt_package_name"] = attrs.dbt_package_name + result["dbt_job_name"] = attrs.dbt_job_name + result["dbt_job_schedule"] = attrs.dbt_job_schedule + result["dbt_job_status"] = attrs.dbt_job_status + result["dbt_job_schedule_cron_humanized"] = attrs.dbt_job_schedule_cron_humanized + result["dbt_job_last_run"] = attrs.dbt_job_last_run + result["dbt_job_next_run"] = attrs.dbt_job_next_run + result["dbt_job_next_run_humanized"] = attrs.dbt_job_next_run_humanized + result["dbt_environment_name"] = attrs.dbt_environment_name + result["dbt_environment_dbt_version"] = attrs.dbt_environment_dbt_version + result["dbt_tags"] = attrs.dbt_tags + result["dbt_connection_context"] = attrs.dbt_connection_context + result["dbt_semantic_layer_proxy_url"] = attrs.dbt_semantic_layer_proxy_url + result["dbt_job_runs"] = attrs.dbt_job_runs + result["semantic_expression"] = attrs.semantic_expression + result["semantic_type"] = attrs.semantic_type + result["semantic_synonyms"] = attrs.semantic_synonyms + result["semantic_sample_values"] = attrs.semantic_sample_values + result["semantic_access_modifier"] = attrs.semantic_access_modifier + result["semantic_data_type"] = attrs.semantic_data_type + result["semantic_labels"] = attrs.semantic_labels + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _dbt_dimension_to_nested(dbt_dimension: DbtDimension) -> DbtDimensionNested: + """Convert flat DbtDimension to nested format.""" + attrs = DbtDimensionAttributes() + _populate_dbt_dimension_attrs(attrs, dbt_dimension) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + dbt_dimension, _DBT_DIMENSION_REL_FIELDS, DbtDimensionRelationshipAttributes + ) + return DbtDimensionNested( + guid=dbt_dimension.guid, + type_name=dbt_dimension.type_name, + status=dbt_dimension.status, + version=dbt_dimension.version, + create_time=dbt_dimension.create_time, + update_time=dbt_dimension.update_time, + created_by=dbt_dimension.created_by, + updated_by=dbt_dimension.updated_by, + classifications=dbt_dimension.classifications, + classification_names=dbt_dimension.classification_names, + meanings=dbt_dimension.meanings, + labels=dbt_dimension.labels, + business_attributes=dbt_dimension.business_attributes, + custom_attributes=dbt_dimension.custom_attributes, + pending_tasks=dbt_dimension.pending_tasks, + proxy=dbt_dimension.proxy, + is_incomplete=dbt_dimension.is_incomplete, + provenance_type=dbt_dimension.provenance_type, + home_id=dbt_dimension.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _dbt_dimension_from_nested(nested: DbtDimensionNested) -> DbtDimension: + """Convert nested format to flat DbtDimension.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else DbtDimensionAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DBT_DIMENSION_REL_FIELDS, + DbtDimensionRelationshipAttributes, + ) + return DbtDimension( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_dbt_dimension_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _dbt_dimension_to_nested_bytes(dbt_dimension: DbtDimension, serde: Serde) -> bytes: + """Convert flat DbtDimension to nested JSON bytes.""" + return serde.encode(_dbt_dimension_to_nested(dbt_dimension)) + + +def _dbt_dimension_from_nested_bytes(data: bytes, serde: Serde) -> DbtDimension: + """Convert nested JSON bytes to flat DbtDimension.""" + nested = serde.decode(data, DbtDimensionNested) + return _dbt_dimension_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, + TextField, +) + +DbtDimension.DBT_SEMANTIC_MODEL_QUALIFIED_NAME = KeywordField( + "dbtSemanticModelQualifiedName", "dbtSemanticModelQualifiedName" +) +DbtDimension.DBT_SEMANTIC_FIELD_TIME_GRANULARITY = KeywordField( + "dbtSemanticFieldTimeGranularity", "dbtSemanticFieldTimeGranularity" +) +DbtDimension.DBT_ALIAS = KeywordField("dbtAlias", "dbtAlias") +DbtDimension.DBT_META = KeywordField("dbtMeta", "dbtMeta") +DbtDimension.DBT_UNIQUE_ID = KeywordField("dbtUniqueId", "dbtUniqueId") +DbtDimension.DBT_ACCOUNT_NAME = KeywordField("dbtAccountName", "dbtAccountName") +DbtDimension.DBT_PROJECT_NAME = KeywordField("dbtProjectName", "dbtProjectName") +DbtDimension.DBT_PACKAGE_NAME = KeywordField("dbtPackageName", "dbtPackageName") +DbtDimension.DBT_JOB_NAME = KeywordField("dbtJobName", "dbtJobName") +DbtDimension.DBT_JOB_SCHEDULE = KeywordField("dbtJobSchedule", "dbtJobSchedule") +DbtDimension.DBT_JOB_STATUS = KeywordField("dbtJobStatus", "dbtJobStatus") +DbtDimension.DBT_JOB_SCHEDULE_CRON_HUMANIZED = KeywordField( + "dbtJobScheduleCronHumanized", "dbtJobScheduleCronHumanized" +) +DbtDimension.DBT_JOB_LAST_RUN = NumericField("dbtJobLastRun", "dbtJobLastRun") +DbtDimension.DBT_JOB_NEXT_RUN = NumericField("dbtJobNextRun", "dbtJobNextRun") +DbtDimension.DBT_JOB_NEXT_RUN_HUMANIZED = KeywordField( + "dbtJobNextRunHumanized", "dbtJobNextRunHumanized" +) +DbtDimension.DBT_ENVIRONMENT_NAME = KeywordField( + "dbtEnvironmentName", "dbtEnvironmentName" +) +DbtDimension.DBT_ENVIRONMENT_DBT_VERSION = KeywordField( + "dbtEnvironmentDbtVersion", "dbtEnvironmentDbtVersion" +) +DbtDimension.DBT_TAGS = KeywordField("dbtTags", "dbtTags") +DbtDimension.DBT_CONNECTION_CONTEXT = KeywordField( + "dbtConnectionContext", "dbtConnectionContext" +) +DbtDimension.DBT_SEMANTIC_LAYER_PROXY_URL = KeywordField( + "dbtSemanticLayerProxyUrl", "dbtSemanticLayerProxyUrl" +) +DbtDimension.DBT_JOB_RUNS = KeywordField("dbtJobRuns", "dbtJobRuns") +DbtDimension.SEMANTIC_EXPRESSION = KeywordField( + "semanticExpression", "semanticExpression" +) +DbtDimension.SEMANTIC_TYPE = KeywordField("semanticType", "semanticType") +DbtDimension.SEMANTIC_SYNONYMS = KeywordField("semanticSynonyms", "semanticSynonyms") +DbtDimension.SEMANTIC_SAMPLE_VALUES = TextField( + "semanticSampleValues", "semanticSampleValues" +) +DbtDimension.SEMANTIC_ACCESS_MODIFIER = KeywordField( + "semanticAccessModifier", "semanticAccessModifier" +) +DbtDimension.SEMANTIC_DATA_TYPE = KeywordField("semanticDataType", "semanticDataType") +DbtDimension.SEMANTIC_LABELS = KeywordField("semanticLabels", "semanticLabels") +DbtDimension.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +DbtDimension.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +DbtDimension.ANOMALO_CHECKS = RelationField("anomaloChecks") +DbtDimension.APPLICATION = RelationField("application") +DbtDimension.APPLICATION_FIELD = RelationField("applicationField") +DbtDimension.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +DbtDimension.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +DbtDimension.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +DbtDimension.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +DbtDimension.METRICS = RelationField("metrics") +DbtDimension.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +DbtDimension.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +DbtDimension.MEANINGS = RelationField("meanings") +DbtDimension.MC_MONITORS = RelationField("mcMonitors") +DbtDimension.MC_INCIDENTS = RelationField("mcIncidents") +DbtDimension.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +DbtDimension.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +DbtDimension.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +DbtDimension.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +DbtDimension.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +DbtDimension.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +DbtDimension.FILES = RelationField("files") +DbtDimension.LINKS = RelationField("links") +DbtDimension.README = RelationField("readme") +DbtDimension.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +DbtDimension.SEMANTIC_MODEL = RelationField("semanticModel") +DbtDimension.SODA_CHECKS = RelationField("sodaChecks") +DbtDimension.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +DbtDimension.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/dbt_entity.py b/pyatlan_v9/model/assets/dbt_entity.py new file mode 100644 index 000000000..3d0a18587 --- /dev/null +++ b/pyatlan_v9/model/assets/dbt_entity.py @@ -0,0 +1,829 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DbtEntity asset model with flattened inheritance. + +This module provides: +- DbtEntity: Flat asset class (easy to use) +- DbtEntityAttributes: Nested attributes struct (extends AssetAttributes) +- DbtEntityNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .semantic_related import RelatedSemanticModel +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class DbtEntity(Asset): + """ + Instance of a dbt semantic entity in Atlan. + """ + + DBT_SEMANTIC_MODEL_QUALIFIED_NAME: ClassVar[Any] = None + DBT_ALIAS: ClassVar[Any] = None + DBT_META: ClassVar[Any] = None + DBT_UNIQUE_ID: ClassVar[Any] = None + DBT_ACCOUNT_NAME: ClassVar[Any] = None + DBT_PROJECT_NAME: ClassVar[Any] = None + DBT_PACKAGE_NAME: ClassVar[Any] = None + DBT_JOB_NAME: ClassVar[Any] = None + DBT_JOB_SCHEDULE: ClassVar[Any] = None + DBT_JOB_STATUS: ClassVar[Any] = None + DBT_JOB_SCHEDULE_CRON_HUMANIZED: ClassVar[Any] = None + DBT_JOB_LAST_RUN: ClassVar[Any] = None + DBT_JOB_NEXT_RUN: ClassVar[Any] = None + DBT_JOB_NEXT_RUN_HUMANIZED: ClassVar[Any] = None + DBT_ENVIRONMENT_NAME: ClassVar[Any] = None + DBT_ENVIRONMENT_DBT_VERSION: ClassVar[Any] = None + DBT_TAGS: ClassVar[Any] = None + DBT_CONNECTION_CONTEXT: ClassVar[Any] = None + DBT_SEMANTIC_LAYER_PROXY_URL: ClassVar[Any] = None + DBT_JOB_RUNS: ClassVar[Any] = None + SEMANTIC_EXPRESSION: ClassVar[Any] = None + SEMANTIC_TYPE: ClassVar[Any] = None + SEMANTIC_SYNONYMS: ClassVar[Any] = None + SEMANTIC_SAMPLE_VALUES: ClassVar[Any] = None + SEMANTIC_ACCESS_MODIFIER: ClassVar[Any] = None + SEMANTIC_DATA_TYPE: ClassVar[Any] = None + SEMANTIC_LABELS: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SEMANTIC_MODEL: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "DbtEntity" + + dbt_semantic_model_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the dbt semantic model this entity belongs to.""" + + dbt_alias: Union[str, None, UnsetType] = UNSET + """Alias of this asset in dbt.""" + + dbt_meta: Union[str, None, UnsetType] = UNSET + """Metadata for this asset in dbt, specifically everything under the 'meta' key in the dbt object.""" + + dbt_unique_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of this asset in dbt.""" + + dbt_account_name: Union[str, None, UnsetType] = UNSET + """Name of the account in which this asset exists in dbt.""" + + dbt_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which this asset exists in dbt.""" + + dbt_package_name: Union[str, None, UnsetType] = UNSET + """Name of the package in which this asset exists in dbt.""" + + dbt_job_name: Union[str, None, UnsetType] = UNSET + """Name of the job that materialized this asset in dbt.""" + + dbt_job_schedule: Union[str, None, UnsetType] = UNSET + """Schedule of the job that materialized this asset in dbt.""" + + dbt_job_status: Union[str, None, UnsetType] = UNSET + """Status of the job that materialized this asset in dbt.""" + + dbt_job_schedule_cron_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable cron schedule of the job that materialized this asset in dbt.""" + + dbt_job_last_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt last ran, in milliseconds.""" + + dbt_job_next_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt will next run, in milliseconds.""" + + dbt_job_next_run_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable time at which the job that materialized this asset in dbt will next run.""" + + dbt_environment_name: Union[str, None, UnsetType] = UNSET + """Name of the environment in which this asset exists in dbt.""" + + dbt_environment_dbt_version: Union[str, None, UnsetType] = UNSET + """Version of dbt used in the environment.""" + + dbt_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset in dbt.""" + + dbt_connection_context: Union[str, None, UnsetType] = UNSET + """Connection context for this asset in dbt.""" + + dbt_semantic_layer_proxy_url: Union[str, None, UnsetType] = UNSET + """URL of the semantic layer proxy for this asset in dbt.""" + + dbt_job_runs: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of latest dbt job runs across all environments.""" + + semantic_expression: Union[str, None, UnsetType] = UNSET + """Column name or SQL expression for the semantic field.""" + + semantic_type: Union[str, None, UnsetType] = UNSET + """Detailed type of the semantic field (e.g., type of measure, type of dimension, or type of entity).""" + + semantic_synonyms: Union[List[str], None, UnsetType] = UNSET + """Alternative names or terms for the semantic field.""" + + semantic_sample_values: Union[List[str], None, UnsetType] = UNSET + """Sample values for the semantic field.""" + + semantic_access_modifier: Union[str, None, UnsetType] = UNSET + """Access level for the semantic field (e.g., public_access/private_access).""" + + semantic_data_type: Union[str, None, UnsetType] = UNSET + """Data type of the semantic field.""" + + semantic_labels: Union[List[str], None, UnsetType] = UNSET + """Labels associated with the semantic field.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + semantic_model: Union[RelatedSemanticModel, None, UnsetType] = UNSET + """Semantic model in which this entity exists.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "DbtEntity" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _dbt_entity_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> DbtEntity: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + DbtEntity instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _dbt_entity_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DbtEntityAttributes(AssetAttributes): + """DbtEntity-specific attributes for nested API format.""" + + dbt_semantic_model_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the dbt semantic model this entity belongs to.""" + + dbt_alias: Union[str, None, UnsetType] = UNSET + """Alias of this asset in dbt.""" + + dbt_meta: Union[str, None, UnsetType] = UNSET + """Metadata for this asset in dbt, specifically everything under the 'meta' key in the dbt object.""" + + dbt_unique_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of this asset in dbt.""" + + dbt_account_name: Union[str, None, UnsetType] = UNSET + """Name of the account in which this asset exists in dbt.""" + + dbt_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which this asset exists in dbt.""" + + dbt_package_name: Union[str, None, UnsetType] = UNSET + """Name of the package in which this asset exists in dbt.""" + + dbt_job_name: Union[str, None, UnsetType] = UNSET + """Name of the job that materialized this asset in dbt.""" + + dbt_job_schedule: Union[str, None, UnsetType] = UNSET + """Schedule of the job that materialized this asset in dbt.""" + + dbt_job_status: Union[str, None, UnsetType] = UNSET + """Status of the job that materialized this asset in dbt.""" + + dbt_job_schedule_cron_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable cron schedule of the job that materialized this asset in dbt.""" + + dbt_job_last_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt last ran, in milliseconds.""" + + dbt_job_next_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt will next run, in milliseconds.""" + + dbt_job_next_run_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable time at which the job that materialized this asset in dbt will next run.""" + + dbt_environment_name: Union[str, None, UnsetType] = UNSET + """Name of the environment in which this asset exists in dbt.""" + + dbt_environment_dbt_version: Union[str, None, UnsetType] = UNSET + """Version of dbt used in the environment.""" + + dbt_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset in dbt.""" + + dbt_connection_context: Union[str, None, UnsetType] = UNSET + """Connection context for this asset in dbt.""" + + dbt_semantic_layer_proxy_url: Union[str, None, UnsetType] = UNSET + """URL of the semantic layer proxy for this asset in dbt.""" + + dbt_job_runs: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of latest dbt job runs across all environments.""" + + semantic_expression: Union[str, None, UnsetType] = UNSET + """Column name or SQL expression for the semantic field.""" + + semantic_type: Union[str, None, UnsetType] = UNSET + """Detailed type of the semantic field (e.g., type of measure, type of dimension, or type of entity).""" + + semantic_synonyms: Union[List[str], None, UnsetType] = UNSET + """Alternative names or terms for the semantic field.""" + + semantic_sample_values: Union[List[str], None, UnsetType] = UNSET + """Sample values for the semantic field.""" + + semantic_access_modifier: Union[str, None, UnsetType] = UNSET + """Access level for the semantic field (e.g., public_access/private_access).""" + + semantic_data_type: Union[str, None, UnsetType] = UNSET + """Data type of the semantic field.""" + + semantic_labels: Union[List[str], None, UnsetType] = UNSET + """Labels associated with the semantic field.""" + + +class DbtEntityRelationshipAttributes(AssetRelationshipAttributes): + """DbtEntity-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + semantic_model: Union[RelatedSemanticModel, None, UnsetType] = UNSET + """Semantic model in which this entity exists.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DbtEntityNested(AssetNested): + """DbtEntity in nested API format for high-performance serialization.""" + + attributes: Union[DbtEntityAttributes, UnsetType] = UNSET + relationship_attributes: Union[DbtEntityRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + DbtEntityRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + DbtEntityRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DBT_ENTITY_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "semantic_model", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_dbt_entity_attrs(attrs: DbtEntityAttributes, obj: DbtEntity) -> None: + """Populate DbtEntity-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.dbt_semantic_model_qualified_name = obj.dbt_semantic_model_qualified_name + attrs.dbt_alias = obj.dbt_alias + attrs.dbt_meta = obj.dbt_meta + attrs.dbt_unique_id = obj.dbt_unique_id + attrs.dbt_account_name = obj.dbt_account_name + attrs.dbt_project_name = obj.dbt_project_name + attrs.dbt_package_name = obj.dbt_package_name + attrs.dbt_job_name = obj.dbt_job_name + attrs.dbt_job_schedule = obj.dbt_job_schedule + attrs.dbt_job_status = obj.dbt_job_status + attrs.dbt_job_schedule_cron_humanized = obj.dbt_job_schedule_cron_humanized + attrs.dbt_job_last_run = obj.dbt_job_last_run + attrs.dbt_job_next_run = obj.dbt_job_next_run + attrs.dbt_job_next_run_humanized = obj.dbt_job_next_run_humanized + attrs.dbt_environment_name = obj.dbt_environment_name + attrs.dbt_environment_dbt_version = obj.dbt_environment_dbt_version + attrs.dbt_tags = obj.dbt_tags + attrs.dbt_connection_context = obj.dbt_connection_context + attrs.dbt_semantic_layer_proxy_url = obj.dbt_semantic_layer_proxy_url + attrs.dbt_job_runs = obj.dbt_job_runs + attrs.semantic_expression = obj.semantic_expression + attrs.semantic_type = obj.semantic_type + attrs.semantic_synonyms = obj.semantic_synonyms + attrs.semantic_sample_values = obj.semantic_sample_values + attrs.semantic_access_modifier = obj.semantic_access_modifier + attrs.semantic_data_type = obj.semantic_data_type + attrs.semantic_labels = obj.semantic_labels + + +def _extract_dbt_entity_attrs(attrs: DbtEntityAttributes) -> dict: + """Extract all DbtEntity attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["dbt_semantic_model_qualified_name"] = ( + attrs.dbt_semantic_model_qualified_name + ) + result["dbt_alias"] = attrs.dbt_alias + result["dbt_meta"] = attrs.dbt_meta + result["dbt_unique_id"] = attrs.dbt_unique_id + result["dbt_account_name"] = attrs.dbt_account_name + result["dbt_project_name"] = attrs.dbt_project_name + result["dbt_package_name"] = attrs.dbt_package_name + result["dbt_job_name"] = attrs.dbt_job_name + result["dbt_job_schedule"] = attrs.dbt_job_schedule + result["dbt_job_status"] = attrs.dbt_job_status + result["dbt_job_schedule_cron_humanized"] = attrs.dbt_job_schedule_cron_humanized + result["dbt_job_last_run"] = attrs.dbt_job_last_run + result["dbt_job_next_run"] = attrs.dbt_job_next_run + result["dbt_job_next_run_humanized"] = attrs.dbt_job_next_run_humanized + result["dbt_environment_name"] = attrs.dbt_environment_name + result["dbt_environment_dbt_version"] = attrs.dbt_environment_dbt_version + result["dbt_tags"] = attrs.dbt_tags + result["dbt_connection_context"] = attrs.dbt_connection_context + result["dbt_semantic_layer_proxy_url"] = attrs.dbt_semantic_layer_proxy_url + result["dbt_job_runs"] = attrs.dbt_job_runs + result["semantic_expression"] = attrs.semantic_expression + result["semantic_type"] = attrs.semantic_type + result["semantic_synonyms"] = attrs.semantic_synonyms + result["semantic_sample_values"] = attrs.semantic_sample_values + result["semantic_access_modifier"] = attrs.semantic_access_modifier + result["semantic_data_type"] = attrs.semantic_data_type + result["semantic_labels"] = attrs.semantic_labels + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _dbt_entity_to_nested(dbt_entity: DbtEntity) -> DbtEntityNested: + """Convert flat DbtEntity to nested format.""" + attrs = DbtEntityAttributes() + _populate_dbt_entity_attrs(attrs, dbt_entity) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + dbt_entity, _DBT_ENTITY_REL_FIELDS, DbtEntityRelationshipAttributes + ) + return DbtEntityNested( + guid=dbt_entity.guid, + type_name=dbt_entity.type_name, + status=dbt_entity.status, + version=dbt_entity.version, + create_time=dbt_entity.create_time, + update_time=dbt_entity.update_time, + created_by=dbt_entity.created_by, + updated_by=dbt_entity.updated_by, + classifications=dbt_entity.classifications, + classification_names=dbt_entity.classification_names, + meanings=dbt_entity.meanings, + labels=dbt_entity.labels, + business_attributes=dbt_entity.business_attributes, + custom_attributes=dbt_entity.custom_attributes, + pending_tasks=dbt_entity.pending_tasks, + proxy=dbt_entity.proxy, + is_incomplete=dbt_entity.is_incomplete, + provenance_type=dbt_entity.provenance_type, + home_id=dbt_entity.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _dbt_entity_from_nested(nested: DbtEntityNested) -> DbtEntity: + """Convert nested format to flat DbtEntity.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else DbtEntityAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DBT_ENTITY_REL_FIELDS, + DbtEntityRelationshipAttributes, + ) + return DbtEntity( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_dbt_entity_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _dbt_entity_to_nested_bytes(dbt_entity: DbtEntity, serde: Serde) -> bytes: + """Convert flat DbtEntity to nested JSON bytes.""" + return serde.encode(_dbt_entity_to_nested(dbt_entity)) + + +def _dbt_entity_from_nested_bytes(data: bytes, serde: Serde) -> DbtEntity: + """Convert nested JSON bytes to flat DbtEntity.""" + nested = serde.decode(data, DbtEntityNested) + return _dbt_entity_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, + TextField, +) + +DbtEntity.DBT_SEMANTIC_MODEL_QUALIFIED_NAME = KeywordField( + "dbtSemanticModelQualifiedName", "dbtSemanticModelQualifiedName" +) +DbtEntity.DBT_ALIAS = KeywordField("dbtAlias", "dbtAlias") +DbtEntity.DBT_META = KeywordField("dbtMeta", "dbtMeta") +DbtEntity.DBT_UNIQUE_ID = KeywordField("dbtUniqueId", "dbtUniqueId") +DbtEntity.DBT_ACCOUNT_NAME = KeywordField("dbtAccountName", "dbtAccountName") +DbtEntity.DBT_PROJECT_NAME = KeywordField("dbtProjectName", "dbtProjectName") +DbtEntity.DBT_PACKAGE_NAME = KeywordField("dbtPackageName", "dbtPackageName") +DbtEntity.DBT_JOB_NAME = KeywordField("dbtJobName", "dbtJobName") +DbtEntity.DBT_JOB_SCHEDULE = KeywordField("dbtJobSchedule", "dbtJobSchedule") +DbtEntity.DBT_JOB_STATUS = KeywordField("dbtJobStatus", "dbtJobStatus") +DbtEntity.DBT_JOB_SCHEDULE_CRON_HUMANIZED = KeywordField( + "dbtJobScheduleCronHumanized", "dbtJobScheduleCronHumanized" +) +DbtEntity.DBT_JOB_LAST_RUN = NumericField("dbtJobLastRun", "dbtJobLastRun") +DbtEntity.DBT_JOB_NEXT_RUN = NumericField("dbtJobNextRun", "dbtJobNextRun") +DbtEntity.DBT_JOB_NEXT_RUN_HUMANIZED = KeywordField( + "dbtJobNextRunHumanized", "dbtJobNextRunHumanized" +) +DbtEntity.DBT_ENVIRONMENT_NAME = KeywordField( + "dbtEnvironmentName", "dbtEnvironmentName" +) +DbtEntity.DBT_ENVIRONMENT_DBT_VERSION = KeywordField( + "dbtEnvironmentDbtVersion", "dbtEnvironmentDbtVersion" +) +DbtEntity.DBT_TAGS = KeywordField("dbtTags", "dbtTags") +DbtEntity.DBT_CONNECTION_CONTEXT = KeywordField( + "dbtConnectionContext", "dbtConnectionContext" +) +DbtEntity.DBT_SEMANTIC_LAYER_PROXY_URL = KeywordField( + "dbtSemanticLayerProxyUrl", "dbtSemanticLayerProxyUrl" +) +DbtEntity.DBT_JOB_RUNS = KeywordField("dbtJobRuns", "dbtJobRuns") +DbtEntity.SEMANTIC_EXPRESSION = KeywordField("semanticExpression", "semanticExpression") +DbtEntity.SEMANTIC_TYPE = KeywordField("semanticType", "semanticType") +DbtEntity.SEMANTIC_SYNONYMS = KeywordField("semanticSynonyms", "semanticSynonyms") +DbtEntity.SEMANTIC_SAMPLE_VALUES = TextField( + "semanticSampleValues", "semanticSampleValues" +) +DbtEntity.SEMANTIC_ACCESS_MODIFIER = KeywordField( + "semanticAccessModifier", "semanticAccessModifier" +) +DbtEntity.SEMANTIC_DATA_TYPE = KeywordField("semanticDataType", "semanticDataType") +DbtEntity.SEMANTIC_LABELS = KeywordField("semanticLabels", "semanticLabels") +DbtEntity.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +DbtEntity.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +DbtEntity.ANOMALO_CHECKS = RelationField("anomaloChecks") +DbtEntity.APPLICATION = RelationField("application") +DbtEntity.APPLICATION_FIELD = RelationField("applicationField") +DbtEntity.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +DbtEntity.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +DbtEntity.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +DbtEntity.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +DbtEntity.METRICS = RelationField("metrics") +DbtEntity.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +DbtEntity.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +DbtEntity.MEANINGS = RelationField("meanings") +DbtEntity.MC_MONITORS = RelationField("mcMonitors") +DbtEntity.MC_INCIDENTS = RelationField("mcIncidents") +DbtEntity.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +DbtEntity.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +DbtEntity.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +DbtEntity.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +DbtEntity.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +DbtEntity.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +DbtEntity.FILES = RelationField("files") +DbtEntity.LINKS = RelationField("links") +DbtEntity.README = RelationField("readme") +DbtEntity.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +DbtEntity.SEMANTIC_MODEL = RelationField("semanticModel") +DbtEntity.SODA_CHECKS = RelationField("sodaChecks") +DbtEntity.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +DbtEntity.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/dbt_measure.py b/pyatlan_v9/model/assets/dbt_measure.py new file mode 100644 index 000000000..f9a6b9db5 --- /dev/null +++ b/pyatlan_v9/model/assets/dbt_measure.py @@ -0,0 +1,831 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DbtMeasure asset model with flattened inheritance. + +This module provides: +- DbtMeasure: Flat asset class (easy to use) +- DbtMeasureAttributes: Nested attributes struct (extends AssetAttributes) +- DbtMeasureNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .semantic_related import RelatedSemanticModel +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class DbtMeasure(Asset): + """ + Instance of a dbt semantic measure in Atlan. + """ + + DBT_SEMANTIC_MODEL_QUALIFIED_NAME: ClassVar[Any] = None + DBT_ALIAS: ClassVar[Any] = None + DBT_META: ClassVar[Any] = None + DBT_UNIQUE_ID: ClassVar[Any] = None + DBT_ACCOUNT_NAME: ClassVar[Any] = None + DBT_PROJECT_NAME: ClassVar[Any] = None + DBT_PACKAGE_NAME: ClassVar[Any] = None + DBT_JOB_NAME: ClassVar[Any] = None + DBT_JOB_SCHEDULE: ClassVar[Any] = None + DBT_JOB_STATUS: ClassVar[Any] = None + DBT_JOB_SCHEDULE_CRON_HUMANIZED: ClassVar[Any] = None + DBT_JOB_LAST_RUN: ClassVar[Any] = None + DBT_JOB_NEXT_RUN: ClassVar[Any] = None + DBT_JOB_NEXT_RUN_HUMANIZED: ClassVar[Any] = None + DBT_ENVIRONMENT_NAME: ClassVar[Any] = None + DBT_ENVIRONMENT_DBT_VERSION: ClassVar[Any] = None + DBT_TAGS: ClassVar[Any] = None + DBT_CONNECTION_CONTEXT: ClassVar[Any] = None + DBT_SEMANTIC_LAYER_PROXY_URL: ClassVar[Any] = None + DBT_JOB_RUNS: ClassVar[Any] = None + SEMANTIC_EXPRESSION: ClassVar[Any] = None + SEMANTIC_TYPE: ClassVar[Any] = None + SEMANTIC_SYNONYMS: ClassVar[Any] = None + SEMANTIC_SAMPLE_VALUES: ClassVar[Any] = None + SEMANTIC_ACCESS_MODIFIER: ClassVar[Any] = None + SEMANTIC_DATA_TYPE: ClassVar[Any] = None + SEMANTIC_LABELS: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SEMANTIC_MODEL: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "DbtMeasure" + + dbt_semantic_model_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the dbt semantic model this measure belongs to.""" + + dbt_alias: Union[str, None, UnsetType] = UNSET + """Alias of this asset in dbt.""" + + dbt_meta: Union[str, None, UnsetType] = UNSET + """Metadata for this asset in dbt, specifically everything under the 'meta' key in the dbt object.""" + + dbt_unique_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of this asset in dbt.""" + + dbt_account_name: Union[str, None, UnsetType] = UNSET + """Name of the account in which this asset exists in dbt.""" + + dbt_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which this asset exists in dbt.""" + + dbt_package_name: Union[str, None, UnsetType] = UNSET + """Name of the package in which this asset exists in dbt.""" + + dbt_job_name: Union[str, None, UnsetType] = UNSET + """Name of the job that materialized this asset in dbt.""" + + dbt_job_schedule: Union[str, None, UnsetType] = UNSET + """Schedule of the job that materialized this asset in dbt.""" + + dbt_job_status: Union[str, None, UnsetType] = UNSET + """Status of the job that materialized this asset in dbt.""" + + dbt_job_schedule_cron_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable cron schedule of the job that materialized this asset in dbt.""" + + dbt_job_last_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt last ran, in milliseconds.""" + + dbt_job_next_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt will next run, in milliseconds.""" + + dbt_job_next_run_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable time at which the job that materialized this asset in dbt will next run.""" + + dbt_environment_name: Union[str, None, UnsetType] = UNSET + """Name of the environment in which this asset exists in dbt.""" + + dbt_environment_dbt_version: Union[str, None, UnsetType] = UNSET + """Version of dbt used in the environment.""" + + dbt_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset in dbt.""" + + dbt_connection_context: Union[str, None, UnsetType] = UNSET + """Connection context for this asset in dbt.""" + + dbt_semantic_layer_proxy_url: Union[str, None, UnsetType] = UNSET + """URL of the semantic layer proxy for this asset in dbt.""" + + dbt_job_runs: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of latest dbt job runs across all environments.""" + + semantic_expression: Union[str, None, UnsetType] = UNSET + """Column name or SQL expression for the semantic field.""" + + semantic_type: Union[str, None, UnsetType] = UNSET + """Detailed type of the semantic field (e.g., type of measure, type of dimension, or type of entity).""" + + semantic_synonyms: Union[List[str], None, UnsetType] = UNSET + """Alternative names or terms for the semantic field.""" + + semantic_sample_values: Union[List[str], None, UnsetType] = UNSET + """Sample values for the semantic field.""" + + semantic_access_modifier: Union[str, None, UnsetType] = UNSET + """Access level for the semantic field (e.g., public_access/private_access).""" + + semantic_data_type: Union[str, None, UnsetType] = UNSET + """Data type of the semantic field.""" + + semantic_labels: Union[List[str], None, UnsetType] = UNSET + """Labels associated with the semantic field.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + semantic_model: Union[RelatedSemanticModel, None, UnsetType] = UNSET + """Semantic model in which this measure exists.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "DbtMeasure" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _dbt_measure_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> DbtMeasure: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + DbtMeasure instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _dbt_measure_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DbtMeasureAttributes(AssetAttributes): + """DbtMeasure-specific attributes for nested API format.""" + + dbt_semantic_model_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the dbt semantic model this measure belongs to.""" + + dbt_alias: Union[str, None, UnsetType] = UNSET + """Alias of this asset in dbt.""" + + dbt_meta: Union[str, None, UnsetType] = UNSET + """Metadata for this asset in dbt, specifically everything under the 'meta' key in the dbt object.""" + + dbt_unique_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of this asset in dbt.""" + + dbt_account_name: Union[str, None, UnsetType] = UNSET + """Name of the account in which this asset exists in dbt.""" + + dbt_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which this asset exists in dbt.""" + + dbt_package_name: Union[str, None, UnsetType] = UNSET + """Name of the package in which this asset exists in dbt.""" + + dbt_job_name: Union[str, None, UnsetType] = UNSET + """Name of the job that materialized this asset in dbt.""" + + dbt_job_schedule: Union[str, None, UnsetType] = UNSET + """Schedule of the job that materialized this asset in dbt.""" + + dbt_job_status: Union[str, None, UnsetType] = UNSET + """Status of the job that materialized this asset in dbt.""" + + dbt_job_schedule_cron_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable cron schedule of the job that materialized this asset in dbt.""" + + dbt_job_last_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt last ran, in milliseconds.""" + + dbt_job_next_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt will next run, in milliseconds.""" + + dbt_job_next_run_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable time at which the job that materialized this asset in dbt will next run.""" + + dbt_environment_name: Union[str, None, UnsetType] = UNSET + """Name of the environment in which this asset exists in dbt.""" + + dbt_environment_dbt_version: Union[str, None, UnsetType] = UNSET + """Version of dbt used in the environment.""" + + dbt_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset in dbt.""" + + dbt_connection_context: Union[str, None, UnsetType] = UNSET + """Connection context for this asset in dbt.""" + + dbt_semantic_layer_proxy_url: Union[str, None, UnsetType] = UNSET + """URL of the semantic layer proxy for this asset in dbt.""" + + dbt_job_runs: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of latest dbt job runs across all environments.""" + + semantic_expression: Union[str, None, UnsetType] = UNSET + """Column name or SQL expression for the semantic field.""" + + semantic_type: Union[str, None, UnsetType] = UNSET + """Detailed type of the semantic field (e.g., type of measure, type of dimension, or type of entity).""" + + semantic_synonyms: Union[List[str], None, UnsetType] = UNSET + """Alternative names or terms for the semantic field.""" + + semantic_sample_values: Union[List[str], None, UnsetType] = UNSET + """Sample values for the semantic field.""" + + semantic_access_modifier: Union[str, None, UnsetType] = UNSET + """Access level for the semantic field (e.g., public_access/private_access).""" + + semantic_data_type: Union[str, None, UnsetType] = UNSET + """Data type of the semantic field.""" + + semantic_labels: Union[List[str], None, UnsetType] = UNSET + """Labels associated with the semantic field.""" + + +class DbtMeasureRelationshipAttributes(AssetRelationshipAttributes): + """DbtMeasure-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + semantic_model: Union[RelatedSemanticModel, None, UnsetType] = UNSET + """Semantic model in which this measure exists.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DbtMeasureNested(AssetNested): + """DbtMeasure in nested API format for high-performance serialization.""" + + attributes: Union[DbtMeasureAttributes, UnsetType] = UNSET + relationship_attributes: Union[DbtMeasureRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + DbtMeasureRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + DbtMeasureRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DBT_MEASURE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "semantic_model", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_dbt_measure_attrs(attrs: DbtMeasureAttributes, obj: DbtMeasure) -> None: + """Populate DbtMeasure-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.dbt_semantic_model_qualified_name = obj.dbt_semantic_model_qualified_name + attrs.dbt_alias = obj.dbt_alias + attrs.dbt_meta = obj.dbt_meta + attrs.dbt_unique_id = obj.dbt_unique_id + attrs.dbt_account_name = obj.dbt_account_name + attrs.dbt_project_name = obj.dbt_project_name + attrs.dbt_package_name = obj.dbt_package_name + attrs.dbt_job_name = obj.dbt_job_name + attrs.dbt_job_schedule = obj.dbt_job_schedule + attrs.dbt_job_status = obj.dbt_job_status + attrs.dbt_job_schedule_cron_humanized = obj.dbt_job_schedule_cron_humanized + attrs.dbt_job_last_run = obj.dbt_job_last_run + attrs.dbt_job_next_run = obj.dbt_job_next_run + attrs.dbt_job_next_run_humanized = obj.dbt_job_next_run_humanized + attrs.dbt_environment_name = obj.dbt_environment_name + attrs.dbt_environment_dbt_version = obj.dbt_environment_dbt_version + attrs.dbt_tags = obj.dbt_tags + attrs.dbt_connection_context = obj.dbt_connection_context + attrs.dbt_semantic_layer_proxy_url = obj.dbt_semantic_layer_proxy_url + attrs.dbt_job_runs = obj.dbt_job_runs + attrs.semantic_expression = obj.semantic_expression + attrs.semantic_type = obj.semantic_type + attrs.semantic_synonyms = obj.semantic_synonyms + attrs.semantic_sample_values = obj.semantic_sample_values + attrs.semantic_access_modifier = obj.semantic_access_modifier + attrs.semantic_data_type = obj.semantic_data_type + attrs.semantic_labels = obj.semantic_labels + + +def _extract_dbt_measure_attrs(attrs: DbtMeasureAttributes) -> dict: + """Extract all DbtMeasure attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["dbt_semantic_model_qualified_name"] = ( + attrs.dbt_semantic_model_qualified_name + ) + result["dbt_alias"] = attrs.dbt_alias + result["dbt_meta"] = attrs.dbt_meta + result["dbt_unique_id"] = attrs.dbt_unique_id + result["dbt_account_name"] = attrs.dbt_account_name + result["dbt_project_name"] = attrs.dbt_project_name + result["dbt_package_name"] = attrs.dbt_package_name + result["dbt_job_name"] = attrs.dbt_job_name + result["dbt_job_schedule"] = attrs.dbt_job_schedule + result["dbt_job_status"] = attrs.dbt_job_status + result["dbt_job_schedule_cron_humanized"] = attrs.dbt_job_schedule_cron_humanized + result["dbt_job_last_run"] = attrs.dbt_job_last_run + result["dbt_job_next_run"] = attrs.dbt_job_next_run + result["dbt_job_next_run_humanized"] = attrs.dbt_job_next_run_humanized + result["dbt_environment_name"] = attrs.dbt_environment_name + result["dbt_environment_dbt_version"] = attrs.dbt_environment_dbt_version + result["dbt_tags"] = attrs.dbt_tags + result["dbt_connection_context"] = attrs.dbt_connection_context + result["dbt_semantic_layer_proxy_url"] = attrs.dbt_semantic_layer_proxy_url + result["dbt_job_runs"] = attrs.dbt_job_runs + result["semantic_expression"] = attrs.semantic_expression + result["semantic_type"] = attrs.semantic_type + result["semantic_synonyms"] = attrs.semantic_synonyms + result["semantic_sample_values"] = attrs.semantic_sample_values + result["semantic_access_modifier"] = attrs.semantic_access_modifier + result["semantic_data_type"] = attrs.semantic_data_type + result["semantic_labels"] = attrs.semantic_labels + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _dbt_measure_to_nested(dbt_measure: DbtMeasure) -> DbtMeasureNested: + """Convert flat DbtMeasure to nested format.""" + attrs = DbtMeasureAttributes() + _populate_dbt_measure_attrs(attrs, dbt_measure) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + dbt_measure, _DBT_MEASURE_REL_FIELDS, DbtMeasureRelationshipAttributes + ) + return DbtMeasureNested( + guid=dbt_measure.guid, + type_name=dbt_measure.type_name, + status=dbt_measure.status, + version=dbt_measure.version, + create_time=dbt_measure.create_time, + update_time=dbt_measure.update_time, + created_by=dbt_measure.created_by, + updated_by=dbt_measure.updated_by, + classifications=dbt_measure.classifications, + classification_names=dbt_measure.classification_names, + meanings=dbt_measure.meanings, + labels=dbt_measure.labels, + business_attributes=dbt_measure.business_attributes, + custom_attributes=dbt_measure.custom_attributes, + pending_tasks=dbt_measure.pending_tasks, + proxy=dbt_measure.proxy, + is_incomplete=dbt_measure.is_incomplete, + provenance_type=dbt_measure.provenance_type, + home_id=dbt_measure.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _dbt_measure_from_nested(nested: DbtMeasureNested) -> DbtMeasure: + """Convert nested format to flat DbtMeasure.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else DbtMeasureAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DBT_MEASURE_REL_FIELDS, + DbtMeasureRelationshipAttributes, + ) + return DbtMeasure( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_dbt_measure_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _dbt_measure_to_nested_bytes(dbt_measure: DbtMeasure, serde: Serde) -> bytes: + """Convert flat DbtMeasure to nested JSON bytes.""" + return serde.encode(_dbt_measure_to_nested(dbt_measure)) + + +def _dbt_measure_from_nested_bytes(data: bytes, serde: Serde) -> DbtMeasure: + """Convert nested JSON bytes to flat DbtMeasure.""" + nested = serde.decode(data, DbtMeasureNested) + return _dbt_measure_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, + TextField, +) + +DbtMeasure.DBT_SEMANTIC_MODEL_QUALIFIED_NAME = KeywordField( + "dbtSemanticModelQualifiedName", "dbtSemanticModelQualifiedName" +) +DbtMeasure.DBT_ALIAS = KeywordField("dbtAlias", "dbtAlias") +DbtMeasure.DBT_META = KeywordField("dbtMeta", "dbtMeta") +DbtMeasure.DBT_UNIQUE_ID = KeywordField("dbtUniqueId", "dbtUniqueId") +DbtMeasure.DBT_ACCOUNT_NAME = KeywordField("dbtAccountName", "dbtAccountName") +DbtMeasure.DBT_PROJECT_NAME = KeywordField("dbtProjectName", "dbtProjectName") +DbtMeasure.DBT_PACKAGE_NAME = KeywordField("dbtPackageName", "dbtPackageName") +DbtMeasure.DBT_JOB_NAME = KeywordField("dbtJobName", "dbtJobName") +DbtMeasure.DBT_JOB_SCHEDULE = KeywordField("dbtJobSchedule", "dbtJobSchedule") +DbtMeasure.DBT_JOB_STATUS = KeywordField("dbtJobStatus", "dbtJobStatus") +DbtMeasure.DBT_JOB_SCHEDULE_CRON_HUMANIZED = KeywordField( + "dbtJobScheduleCronHumanized", "dbtJobScheduleCronHumanized" +) +DbtMeasure.DBT_JOB_LAST_RUN = NumericField("dbtJobLastRun", "dbtJobLastRun") +DbtMeasure.DBT_JOB_NEXT_RUN = NumericField("dbtJobNextRun", "dbtJobNextRun") +DbtMeasure.DBT_JOB_NEXT_RUN_HUMANIZED = KeywordField( + "dbtJobNextRunHumanized", "dbtJobNextRunHumanized" +) +DbtMeasure.DBT_ENVIRONMENT_NAME = KeywordField( + "dbtEnvironmentName", "dbtEnvironmentName" +) +DbtMeasure.DBT_ENVIRONMENT_DBT_VERSION = KeywordField( + "dbtEnvironmentDbtVersion", "dbtEnvironmentDbtVersion" +) +DbtMeasure.DBT_TAGS = KeywordField("dbtTags", "dbtTags") +DbtMeasure.DBT_CONNECTION_CONTEXT = KeywordField( + "dbtConnectionContext", "dbtConnectionContext" +) +DbtMeasure.DBT_SEMANTIC_LAYER_PROXY_URL = KeywordField( + "dbtSemanticLayerProxyUrl", "dbtSemanticLayerProxyUrl" +) +DbtMeasure.DBT_JOB_RUNS = KeywordField("dbtJobRuns", "dbtJobRuns") +DbtMeasure.SEMANTIC_EXPRESSION = KeywordField( + "semanticExpression", "semanticExpression" +) +DbtMeasure.SEMANTIC_TYPE = KeywordField("semanticType", "semanticType") +DbtMeasure.SEMANTIC_SYNONYMS = KeywordField("semanticSynonyms", "semanticSynonyms") +DbtMeasure.SEMANTIC_SAMPLE_VALUES = TextField( + "semanticSampleValues", "semanticSampleValues" +) +DbtMeasure.SEMANTIC_ACCESS_MODIFIER = KeywordField( + "semanticAccessModifier", "semanticAccessModifier" +) +DbtMeasure.SEMANTIC_DATA_TYPE = KeywordField("semanticDataType", "semanticDataType") +DbtMeasure.SEMANTIC_LABELS = KeywordField("semanticLabels", "semanticLabels") +DbtMeasure.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +DbtMeasure.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +DbtMeasure.ANOMALO_CHECKS = RelationField("anomaloChecks") +DbtMeasure.APPLICATION = RelationField("application") +DbtMeasure.APPLICATION_FIELD = RelationField("applicationField") +DbtMeasure.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +DbtMeasure.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +DbtMeasure.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +DbtMeasure.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +DbtMeasure.METRICS = RelationField("metrics") +DbtMeasure.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +DbtMeasure.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +DbtMeasure.MEANINGS = RelationField("meanings") +DbtMeasure.MC_MONITORS = RelationField("mcMonitors") +DbtMeasure.MC_INCIDENTS = RelationField("mcIncidents") +DbtMeasure.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +DbtMeasure.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +DbtMeasure.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +DbtMeasure.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +DbtMeasure.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +DbtMeasure.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +DbtMeasure.FILES = RelationField("files") +DbtMeasure.LINKS = RelationField("links") +DbtMeasure.README = RelationField("readme") +DbtMeasure.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +DbtMeasure.SEMANTIC_MODEL = RelationField("semanticModel") +DbtMeasure.SODA_CHECKS = RelationField("sodaChecks") +DbtMeasure.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +DbtMeasure.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/dbt_metric.py b/pyatlan_v9/model/assets/dbt_metric.py new file mode 100644 index 000000000..237036d83 --- /dev/null +++ b/pyatlan_v9/model/assets/dbt_metric.py @@ -0,0 +1,893 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DbtMetric asset model with flattened inheritance. + +This module provides: +- DbtMetric: Flat asset class (easy to use) +- DbtMetricAttributes: Nested attributes struct (extends AssetAttributes) +- DbtMetricNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .asset_related import RelatedAsset +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from .sql_related import RelatedColumn +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .dbt_related import RelatedDbtModel + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class DbtMetric(Asset): + """ + Instance of a dbt metric in Atlan. + """ + + DBT_METRIC_FILTERS: ClassVar[Any] = None + DBT_METRIC_FILTER: ClassVar[Any] = None + DBT_METRIC_WINDOW: ClassVar[Any] = None + DBT_METRIC_CUMULATIVE_PERIOD_AGG: ClassVar[Any] = None + DBT_METRIC_CONVERSION_CALCULATION: ClassVar[Any] = None + DBT_ALIAS: ClassVar[Any] = None + DBT_META: ClassVar[Any] = None + DBT_UNIQUE_ID: ClassVar[Any] = None + DBT_ACCOUNT_NAME: ClassVar[Any] = None + DBT_PROJECT_NAME: ClassVar[Any] = None + DBT_PACKAGE_NAME: ClassVar[Any] = None + DBT_JOB_NAME: ClassVar[Any] = None + DBT_JOB_SCHEDULE: ClassVar[Any] = None + DBT_JOB_STATUS: ClassVar[Any] = None + DBT_JOB_SCHEDULE_CRON_HUMANIZED: ClassVar[Any] = None + DBT_JOB_LAST_RUN: ClassVar[Any] = None + DBT_JOB_NEXT_RUN: ClassVar[Any] = None + DBT_JOB_NEXT_RUN_HUMANIZED: ClassVar[Any] = None + DBT_ENVIRONMENT_NAME: ClassVar[Any] = None + DBT_ENVIRONMENT_DBT_VERSION: ClassVar[Any] = None + DBT_TAGS: ClassVar[Any] = None + DBT_CONNECTION_CONTEXT: ClassVar[Any] = None + DBT_SEMANTIC_LAYER_PROXY_URL: ClassVar[Any] = None + DBT_JOB_RUNS: ClassVar[Any] = None + METRIC_TYPE: ClassVar[Any] = None + METRIC_SQL: ClassVar[Any] = None + METRIC_FILTERS: ClassVar[Any] = None + METRIC_TIME_GRAINS: ClassVar[Any] = None + DQ_IS_PART_OF_CONTRACT: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + ASSETS: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + METRIC_TIMESTAMP_COLUMN: ClassVar[Any] = None + METRIC_DIMENSION_COLUMNS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DBT_MODEL: ClassVar[Any] = None + DBT_METRIC_FILTER_COLUMNS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "DbtMetric" + + dbt_metric_filters: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """Filters applied to the dbt metric.""" + + dbt_metric_filter: Union[str, None, UnsetType] = UNSET + """Top-level filter applied to the entire metric query.""" + + dbt_metric_window: Union[str, None, UnsetType] = UNSET + """Time window for cumulative/conversion metrics.""" + + dbt_metric_cumulative_period_agg: Union[str, None, UnsetType] = UNSET + """Aggregation function for cumulative metrics within each period.""" + + dbt_metric_conversion_calculation: Union[str, None, UnsetType] = UNSET + """Calculation type for conversion metrics.""" + + dbt_alias: Union[str, None, UnsetType] = UNSET + """Alias of this asset in dbt.""" + + dbt_meta: Union[str, None, UnsetType] = UNSET + """Metadata for this asset in dbt, specifically everything under the 'meta' key in the dbt object.""" + + dbt_unique_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of this asset in dbt.""" + + dbt_account_name: Union[str, None, UnsetType] = UNSET + """Name of the account in which this asset exists in dbt.""" + + dbt_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which this asset exists in dbt.""" + + dbt_package_name: Union[str, None, UnsetType] = UNSET + """Name of the package in which this asset exists in dbt.""" + + dbt_job_name: Union[str, None, UnsetType] = UNSET + """Name of the job that materialized this asset in dbt.""" + + dbt_job_schedule: Union[str, None, UnsetType] = UNSET + """Schedule of the job that materialized this asset in dbt.""" + + dbt_job_status: Union[str, None, UnsetType] = UNSET + """Status of the job that materialized this asset in dbt.""" + + dbt_job_schedule_cron_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable cron schedule of the job that materialized this asset in dbt.""" + + dbt_job_last_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt last ran, in milliseconds.""" + + dbt_job_next_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt will next run, in milliseconds.""" + + dbt_job_next_run_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable time at which the job that materialized this asset in dbt will next run.""" + + dbt_environment_name: Union[str, None, UnsetType] = UNSET + """Name of the environment in which this asset exists in dbt.""" + + dbt_environment_dbt_version: Union[str, None, UnsetType] = UNSET + """Version of dbt used in the environment.""" + + dbt_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset in dbt.""" + + dbt_connection_context: Union[str, None, UnsetType] = UNSET + """Connection context for this asset in dbt.""" + + dbt_semantic_layer_proxy_url: Union[str, None, UnsetType] = UNSET + """URL of the semantic layer proxy for this asset in dbt.""" + + dbt_job_runs: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of latest dbt job runs across all environments.""" + + metric_type: Union[str, None, UnsetType] = UNSET + """Type of the metric.""" + + metric_sql: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="metricSQL" + ) + """SQL query used to compute the metric.""" + + metric_filters: Union[str, None, UnsetType] = UNSET + """Filters to be applied to the metric query.""" + + metric_time_grains: Union[List[str], None, UnsetType] = UNSET + """List of time grains to be applied to the metric query.""" + + dq_is_part_of_contract: Union[bool, None, UnsetType] = UNSET + """Whether this data quality is part of contract (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + assets: Union[List[RelatedAsset], None, UnsetType] = UNSET + """""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + metric_timestamp_column: Union[RelatedColumn, None, UnsetType] = UNSET + """""" + + metric_dimension_columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_model: Union[RelatedDbtModel, None, UnsetType] = UNSET + """Model in which this metric exists.""" + + dbt_metric_filter_columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Model columns related to this metric.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "DbtMetric" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _dbt_metric_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> DbtMetric: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + DbtMetric instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _dbt_metric_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DbtMetricAttributes(AssetAttributes): + """DbtMetric-specific attributes for nested API format.""" + + dbt_metric_filters: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """Filters applied to the dbt metric.""" + + dbt_metric_filter: Union[str, None, UnsetType] = UNSET + """Top-level filter applied to the entire metric query.""" + + dbt_metric_window: Union[str, None, UnsetType] = UNSET + """Time window for cumulative/conversion metrics.""" + + dbt_metric_cumulative_period_agg: Union[str, None, UnsetType] = UNSET + """Aggregation function for cumulative metrics within each period.""" + + dbt_metric_conversion_calculation: Union[str, None, UnsetType] = UNSET + """Calculation type for conversion metrics.""" + + dbt_alias: Union[str, None, UnsetType] = UNSET + """Alias of this asset in dbt.""" + + dbt_meta: Union[str, None, UnsetType] = UNSET + """Metadata for this asset in dbt, specifically everything under the 'meta' key in the dbt object.""" + + dbt_unique_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of this asset in dbt.""" + + dbt_account_name: Union[str, None, UnsetType] = UNSET + """Name of the account in which this asset exists in dbt.""" + + dbt_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which this asset exists in dbt.""" + + dbt_package_name: Union[str, None, UnsetType] = UNSET + """Name of the package in which this asset exists in dbt.""" + + dbt_job_name: Union[str, None, UnsetType] = UNSET + """Name of the job that materialized this asset in dbt.""" + + dbt_job_schedule: Union[str, None, UnsetType] = UNSET + """Schedule of the job that materialized this asset in dbt.""" + + dbt_job_status: Union[str, None, UnsetType] = UNSET + """Status of the job that materialized this asset in dbt.""" + + dbt_job_schedule_cron_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable cron schedule of the job that materialized this asset in dbt.""" + + dbt_job_last_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt last ran, in milliseconds.""" + + dbt_job_next_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt will next run, in milliseconds.""" + + dbt_job_next_run_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable time at which the job that materialized this asset in dbt will next run.""" + + dbt_environment_name: Union[str, None, UnsetType] = UNSET + """Name of the environment in which this asset exists in dbt.""" + + dbt_environment_dbt_version: Union[str, None, UnsetType] = UNSET + """Version of dbt used in the environment.""" + + dbt_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset in dbt.""" + + dbt_connection_context: Union[str, None, UnsetType] = UNSET + """Connection context for this asset in dbt.""" + + dbt_semantic_layer_proxy_url: Union[str, None, UnsetType] = UNSET + """URL of the semantic layer proxy for this asset in dbt.""" + + dbt_job_runs: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of latest dbt job runs across all environments.""" + + metric_type: Union[str, None, UnsetType] = UNSET + """Type of the metric.""" + + metric_sql: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="metricSQL" + ) + """SQL query used to compute the metric.""" + + metric_filters: Union[str, None, UnsetType] = UNSET + """Filters to be applied to the metric query.""" + + metric_time_grains: Union[List[str], None, UnsetType] = UNSET + """List of time grains to be applied to the metric query.""" + + dq_is_part_of_contract: Union[bool, None, UnsetType] = UNSET + """Whether this data quality is part of contract (true) or not (false).""" + + +class DbtMetricRelationshipAttributes(AssetRelationshipAttributes): + """DbtMetric-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + assets: Union[List[RelatedAsset], None, UnsetType] = UNSET + """""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + metric_timestamp_column: Union[RelatedColumn, None, UnsetType] = UNSET + """""" + + metric_dimension_columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_model: Union[RelatedDbtModel, None, UnsetType] = UNSET + """Model in which this metric exists.""" + + dbt_metric_filter_columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Model columns related to this metric.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DbtMetricNested(AssetNested): + """DbtMetric in nested API format for high-performance serialization.""" + + attributes: Union[DbtMetricAttributes, UnsetType] = UNSET + relationship_attributes: Union[DbtMetricRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + DbtMetricRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + DbtMetricRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DBT_METRIC_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "assets", + "metrics", + "metric_timestamp_column", + "metric_dimension_columns", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "dbt_model", + "dbt_metric_filter_columns", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_dbt_metric_attrs(attrs: DbtMetricAttributes, obj: DbtMetric) -> None: + """Populate DbtMetric-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.dbt_metric_filters = obj.dbt_metric_filters + attrs.dbt_metric_filter = obj.dbt_metric_filter + attrs.dbt_metric_window = obj.dbt_metric_window + attrs.dbt_metric_cumulative_period_agg = obj.dbt_metric_cumulative_period_agg + attrs.dbt_metric_conversion_calculation = obj.dbt_metric_conversion_calculation + attrs.dbt_alias = obj.dbt_alias + attrs.dbt_meta = obj.dbt_meta + attrs.dbt_unique_id = obj.dbt_unique_id + attrs.dbt_account_name = obj.dbt_account_name + attrs.dbt_project_name = obj.dbt_project_name + attrs.dbt_package_name = obj.dbt_package_name + attrs.dbt_job_name = obj.dbt_job_name + attrs.dbt_job_schedule = obj.dbt_job_schedule + attrs.dbt_job_status = obj.dbt_job_status + attrs.dbt_job_schedule_cron_humanized = obj.dbt_job_schedule_cron_humanized + attrs.dbt_job_last_run = obj.dbt_job_last_run + attrs.dbt_job_next_run = obj.dbt_job_next_run + attrs.dbt_job_next_run_humanized = obj.dbt_job_next_run_humanized + attrs.dbt_environment_name = obj.dbt_environment_name + attrs.dbt_environment_dbt_version = obj.dbt_environment_dbt_version + attrs.dbt_tags = obj.dbt_tags + attrs.dbt_connection_context = obj.dbt_connection_context + attrs.dbt_semantic_layer_proxy_url = obj.dbt_semantic_layer_proxy_url + attrs.dbt_job_runs = obj.dbt_job_runs + attrs.metric_type = obj.metric_type + attrs.metric_sql = obj.metric_sql + attrs.metric_filters = obj.metric_filters + attrs.metric_time_grains = obj.metric_time_grains + attrs.dq_is_part_of_contract = obj.dq_is_part_of_contract + + +def _extract_dbt_metric_attrs(attrs: DbtMetricAttributes) -> dict: + """Extract all DbtMetric attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["dbt_metric_filters"] = attrs.dbt_metric_filters + result["dbt_metric_filter"] = attrs.dbt_metric_filter + result["dbt_metric_window"] = attrs.dbt_metric_window + result["dbt_metric_cumulative_period_agg"] = attrs.dbt_metric_cumulative_period_agg + result["dbt_metric_conversion_calculation"] = ( + attrs.dbt_metric_conversion_calculation + ) + result["dbt_alias"] = attrs.dbt_alias + result["dbt_meta"] = attrs.dbt_meta + result["dbt_unique_id"] = attrs.dbt_unique_id + result["dbt_account_name"] = attrs.dbt_account_name + result["dbt_project_name"] = attrs.dbt_project_name + result["dbt_package_name"] = attrs.dbt_package_name + result["dbt_job_name"] = attrs.dbt_job_name + result["dbt_job_schedule"] = attrs.dbt_job_schedule + result["dbt_job_status"] = attrs.dbt_job_status + result["dbt_job_schedule_cron_humanized"] = attrs.dbt_job_schedule_cron_humanized + result["dbt_job_last_run"] = attrs.dbt_job_last_run + result["dbt_job_next_run"] = attrs.dbt_job_next_run + result["dbt_job_next_run_humanized"] = attrs.dbt_job_next_run_humanized + result["dbt_environment_name"] = attrs.dbt_environment_name + result["dbt_environment_dbt_version"] = attrs.dbt_environment_dbt_version + result["dbt_tags"] = attrs.dbt_tags + result["dbt_connection_context"] = attrs.dbt_connection_context + result["dbt_semantic_layer_proxy_url"] = attrs.dbt_semantic_layer_proxy_url + result["dbt_job_runs"] = attrs.dbt_job_runs + result["metric_type"] = attrs.metric_type + result["metric_sql"] = attrs.metric_sql + result["metric_filters"] = attrs.metric_filters + result["metric_time_grains"] = attrs.metric_time_grains + result["dq_is_part_of_contract"] = attrs.dq_is_part_of_contract + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _dbt_metric_to_nested(dbt_metric: DbtMetric) -> DbtMetricNested: + """Convert flat DbtMetric to nested format.""" + attrs = DbtMetricAttributes() + _populate_dbt_metric_attrs(attrs, dbt_metric) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + dbt_metric, _DBT_METRIC_REL_FIELDS, DbtMetricRelationshipAttributes + ) + return DbtMetricNested( + guid=dbt_metric.guid, + type_name=dbt_metric.type_name, + status=dbt_metric.status, + version=dbt_metric.version, + create_time=dbt_metric.create_time, + update_time=dbt_metric.update_time, + created_by=dbt_metric.created_by, + updated_by=dbt_metric.updated_by, + classifications=dbt_metric.classifications, + classification_names=dbt_metric.classification_names, + meanings=dbt_metric.meanings, + labels=dbt_metric.labels, + business_attributes=dbt_metric.business_attributes, + custom_attributes=dbt_metric.custom_attributes, + pending_tasks=dbt_metric.pending_tasks, + proxy=dbt_metric.proxy, + is_incomplete=dbt_metric.is_incomplete, + provenance_type=dbt_metric.provenance_type, + home_id=dbt_metric.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _dbt_metric_from_nested(nested: DbtMetricNested) -> DbtMetric: + """Convert nested format to flat DbtMetric.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else DbtMetricAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DBT_METRIC_REL_FIELDS, + DbtMetricRelationshipAttributes, + ) + return DbtMetric( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_dbt_metric_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _dbt_metric_to_nested_bytes(dbt_metric: DbtMetric, serde: Serde) -> bytes: + """Convert flat DbtMetric to nested JSON bytes.""" + return serde.encode(_dbt_metric_to_nested(dbt_metric)) + + +def _dbt_metric_from_nested_bytes(data: bytes, serde: Serde) -> DbtMetric: + """Convert nested JSON bytes to flat DbtMetric.""" + nested = serde.decode(data, DbtMetricNested) + return _dbt_metric_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, +) + +DbtMetric.DBT_METRIC_FILTERS = KeywordField("dbtMetricFilters", "dbtMetricFilters") +DbtMetric.DBT_METRIC_FILTER = KeywordField("dbtMetricFilter", "dbtMetricFilter") +DbtMetric.DBT_METRIC_WINDOW = KeywordField("dbtMetricWindow", "dbtMetricWindow") +DbtMetric.DBT_METRIC_CUMULATIVE_PERIOD_AGG = KeywordField( + "dbtMetricCumulativePeriodAgg", "dbtMetricCumulativePeriodAgg" +) +DbtMetric.DBT_METRIC_CONVERSION_CALCULATION = KeywordField( + "dbtMetricConversionCalculation", "dbtMetricConversionCalculation" +) +DbtMetric.DBT_ALIAS = KeywordField("dbtAlias", "dbtAlias") +DbtMetric.DBT_META = KeywordField("dbtMeta", "dbtMeta") +DbtMetric.DBT_UNIQUE_ID = KeywordField("dbtUniqueId", "dbtUniqueId") +DbtMetric.DBT_ACCOUNT_NAME = KeywordField("dbtAccountName", "dbtAccountName") +DbtMetric.DBT_PROJECT_NAME = KeywordField("dbtProjectName", "dbtProjectName") +DbtMetric.DBT_PACKAGE_NAME = KeywordField("dbtPackageName", "dbtPackageName") +DbtMetric.DBT_JOB_NAME = KeywordField("dbtJobName", "dbtJobName") +DbtMetric.DBT_JOB_SCHEDULE = KeywordField("dbtJobSchedule", "dbtJobSchedule") +DbtMetric.DBT_JOB_STATUS = KeywordField("dbtJobStatus", "dbtJobStatus") +DbtMetric.DBT_JOB_SCHEDULE_CRON_HUMANIZED = KeywordField( + "dbtJobScheduleCronHumanized", "dbtJobScheduleCronHumanized" +) +DbtMetric.DBT_JOB_LAST_RUN = NumericField("dbtJobLastRun", "dbtJobLastRun") +DbtMetric.DBT_JOB_NEXT_RUN = NumericField("dbtJobNextRun", "dbtJobNextRun") +DbtMetric.DBT_JOB_NEXT_RUN_HUMANIZED = KeywordField( + "dbtJobNextRunHumanized", "dbtJobNextRunHumanized" +) +DbtMetric.DBT_ENVIRONMENT_NAME = KeywordField( + "dbtEnvironmentName", "dbtEnvironmentName" +) +DbtMetric.DBT_ENVIRONMENT_DBT_VERSION = KeywordField( + "dbtEnvironmentDbtVersion", "dbtEnvironmentDbtVersion" +) +DbtMetric.DBT_TAGS = KeywordField("dbtTags", "dbtTags") +DbtMetric.DBT_CONNECTION_CONTEXT = KeywordField( + "dbtConnectionContext", "dbtConnectionContext" +) +DbtMetric.DBT_SEMANTIC_LAYER_PROXY_URL = KeywordField( + "dbtSemanticLayerProxyUrl", "dbtSemanticLayerProxyUrl" +) +DbtMetric.DBT_JOB_RUNS = KeywordField("dbtJobRuns", "dbtJobRuns") +DbtMetric.METRIC_TYPE = KeywordField("metricType", "metricType") +DbtMetric.METRIC_SQL = KeywordField("metricSQL", "metricSQL") +DbtMetric.METRIC_FILTERS = KeywordField("metricFilters", "metricFilters") +DbtMetric.METRIC_TIME_GRAINS = KeywordField("metricTimeGrains", "metricTimeGrains") +DbtMetric.DQ_IS_PART_OF_CONTRACT = BooleanField( + "dqIsPartOfContract", "dqIsPartOfContract" +) +DbtMetric.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +DbtMetric.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +DbtMetric.ANOMALO_CHECKS = RelationField("anomaloChecks") +DbtMetric.APPLICATION = RelationField("application") +DbtMetric.APPLICATION_FIELD = RelationField("applicationField") +DbtMetric.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +DbtMetric.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +DbtMetric.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +DbtMetric.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +DbtMetric.ASSETS = RelationField("assets") +DbtMetric.METRICS = RelationField("metrics") +DbtMetric.METRIC_TIMESTAMP_COLUMN = RelationField("metricTimestampColumn") +DbtMetric.METRIC_DIMENSION_COLUMNS = RelationField("metricDimensionColumns") +DbtMetric.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +DbtMetric.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +DbtMetric.DBT_MODEL = RelationField("dbtModel") +DbtMetric.DBT_METRIC_FILTER_COLUMNS = RelationField("dbtMetricFilterColumns") +DbtMetric.MEANINGS = RelationField("meanings") +DbtMetric.MC_MONITORS = RelationField("mcMonitors") +DbtMetric.MC_INCIDENTS = RelationField("mcIncidents") +DbtMetric.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +DbtMetric.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +DbtMetric.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +DbtMetric.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +DbtMetric.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +DbtMetric.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +DbtMetric.FILES = RelationField("files") +DbtMetric.LINKS = RelationField("links") +DbtMetric.README = RelationField("readme") +DbtMetric.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +DbtMetric.SODA_CHECKS = RelationField("sodaChecks") +DbtMetric.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +DbtMetric.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/dbt_model.py b/pyatlan_v9/model/assets/dbt_model.py new file mode 100644 index 000000000..abc57dc77 --- /dev/null +++ b/pyatlan_v9/model/assets/dbt_model.py @@ -0,0 +1,938 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DbtModel asset model with flattened inheritance. + +This module provides: +- DbtModel: Flat asset class (easy to use) +- DbtModelAttributes: Nested attributes struct (extends AssetAttributes) +- DbtModelNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from .sql_related import RelatedSQL +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .dbt_related import RelatedDbtMetric, RelatedDbtModelColumn, RelatedDbtTest + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class DbtModel(Asset): + """ + Instance of a dbt model in Atlan. + """ + + DBT_STATUS: ClassVar[Any] = None + DBT_ERROR: ClassVar[Any] = None + DBT_RAW_SQL: ClassVar[Any] = None + DBT_COMPILED_SQL: ClassVar[Any] = None + DBT_STATS: ClassVar[Any] = None + DBT_MATERIALIZATION_TYPE: ClassVar[Any] = None + DBT_MODEL_COMPILE_STARTED_AT: ClassVar[Any] = None + DBT_MODEL_COMPILE_COMPLETED_AT: ClassVar[Any] = None + DBT_MODEL_EXECUTE_STARTED_AT: ClassVar[Any] = None + DBT_MODEL_EXECUTE_COMPLETED_AT: ClassVar[Any] = None + DBT_MODEL_EXECUTION_TIME: ClassVar[Any] = None + DBT_MODEL_RUN_GENERATED_AT: ClassVar[Any] = None + DBT_MODEL_RUN_ELAPSED_TIME: ClassVar[Any] = None + DBT_ALIAS: ClassVar[Any] = None + DBT_META: ClassVar[Any] = None + DBT_UNIQUE_ID: ClassVar[Any] = None + DBT_ACCOUNT_NAME: ClassVar[Any] = None + DBT_PROJECT_NAME: ClassVar[Any] = None + DBT_PACKAGE_NAME: ClassVar[Any] = None + DBT_JOB_NAME: ClassVar[Any] = None + DBT_JOB_SCHEDULE: ClassVar[Any] = None + DBT_JOB_STATUS: ClassVar[Any] = None + DBT_JOB_SCHEDULE_CRON_HUMANIZED: ClassVar[Any] = None + DBT_JOB_LAST_RUN: ClassVar[Any] = None + DBT_JOB_NEXT_RUN: ClassVar[Any] = None + DBT_JOB_NEXT_RUN_HUMANIZED: ClassVar[Any] = None + DBT_ENVIRONMENT_NAME: ClassVar[Any] = None + DBT_ENVIRONMENT_DBT_VERSION: ClassVar[Any] = None + DBT_TAGS: ClassVar[Any] = None + DBT_CONNECTION_CONTEXT: ClassVar[Any] = None + DBT_SEMANTIC_LAYER_PROXY_URL: ClassVar[Any] = None + DBT_JOB_RUNS: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + SQL_ASSET: ClassVar[Any] = None + DBT_MODEL_SQL_ASSETS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_METRICS: ClassVar[Any] = None + DBT_MODEL_COLUMNS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "DbtModel" + + dbt_status: Union[str, None, UnsetType] = UNSET + """Status of the dbt model.""" + + dbt_error: Union[str, None, UnsetType] = UNSET + """Error message if any for the dbt model.""" + + dbt_raw_sql: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="dbtRawSQL" + ) + """Raw SQL of the dbt model.""" + + dbt_compiled_sql: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="dbtCompiledSQL" + ) + """Compiled SQL of the dbt model.""" + + dbt_stats: Union[str, None, UnsetType] = UNSET + """Statistics of the dbt model.""" + + dbt_materialization_type: Union[str, None, UnsetType] = UNSET + """Type of materialization used for the dbt model.""" + + dbt_model_compile_started_at: Union[int, None, UnsetType] = UNSET + """Timestamp when the dbt model compilation started.""" + + dbt_model_compile_completed_at: Union[int, None, UnsetType] = UNSET + """Timestamp when the dbt model compilation completed.""" + + dbt_model_execute_started_at: Union[int, None, UnsetType] = UNSET + """Timestamp when the dbt model execution started.""" + + dbt_model_execute_completed_at: Union[int, None, UnsetType] = UNSET + """Timestamp when the dbt model execution completed.""" + + dbt_model_execution_time: Union[float, None, UnsetType] = UNSET + """Execution time of the dbt model.""" + + dbt_model_run_generated_at: Union[int, None, UnsetType] = UNSET + """Timestamp when the dbt model run was generated.""" + + dbt_model_run_elapsed_time: Union[float, None, UnsetType] = UNSET + """Elapsed time of the dbt model run.""" + + dbt_alias: Union[str, None, UnsetType] = UNSET + """Alias of this asset in dbt.""" + + dbt_meta: Union[str, None, UnsetType] = UNSET + """Metadata for this asset in dbt, specifically everything under the 'meta' key in the dbt object.""" + + dbt_unique_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of this asset in dbt.""" + + dbt_account_name: Union[str, None, UnsetType] = UNSET + """Name of the account in which this asset exists in dbt.""" + + dbt_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which this asset exists in dbt.""" + + dbt_package_name: Union[str, None, UnsetType] = UNSET + """Name of the package in which this asset exists in dbt.""" + + dbt_job_name: Union[str, None, UnsetType] = UNSET + """Name of the job that materialized this asset in dbt.""" + + dbt_job_schedule: Union[str, None, UnsetType] = UNSET + """Schedule of the job that materialized this asset in dbt.""" + + dbt_job_status: Union[str, None, UnsetType] = UNSET + """Status of the job that materialized this asset in dbt.""" + + dbt_job_schedule_cron_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable cron schedule of the job that materialized this asset in dbt.""" + + dbt_job_last_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt last ran, in milliseconds.""" + + dbt_job_next_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt will next run, in milliseconds.""" + + dbt_job_next_run_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable time at which the job that materialized this asset in dbt will next run.""" + + dbt_environment_name: Union[str, None, UnsetType] = UNSET + """Name of the environment in which this asset exists in dbt.""" + + dbt_environment_dbt_version: Union[str, None, UnsetType] = UNSET + """Version of dbt used in the environment.""" + + dbt_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset in dbt.""" + + dbt_connection_context: Union[str, None, UnsetType] = UNSET + """Connection context for this asset in dbt.""" + + dbt_semantic_layer_proxy_url: Union[str, None, UnsetType] = UNSET + """URL of the semantic layer proxy for this asset in dbt.""" + + dbt_job_runs: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of latest dbt job runs across all environments.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + sql_asset: Union[RelatedSQL, None, UnsetType] = UNSET + """(Deprecated) Assets related to the model.""" + + dbt_model_sql_assets: Union[List[RelatedSQL], None, UnsetType] = UNSET + """Model containing the assets.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this model.""" + + dbt_metrics: Union[List[RelatedDbtMetric], None, UnsetType] = UNSET + """Metrics that exist within this model.""" + + dbt_model_columns: Union[List[RelatedDbtModelColumn], None, UnsetType] = UNSET + """Columns that exist within this dbt model.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "DbtModel" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _dbt_model_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> DbtModel: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + DbtModel instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _dbt_model_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DbtModelAttributes(AssetAttributes): + """DbtModel-specific attributes for nested API format.""" + + dbt_status: Union[str, None, UnsetType] = UNSET + """Status of the dbt model.""" + + dbt_error: Union[str, None, UnsetType] = UNSET + """Error message if any for the dbt model.""" + + dbt_raw_sql: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="dbtRawSQL" + ) + """Raw SQL of the dbt model.""" + + dbt_compiled_sql: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="dbtCompiledSQL" + ) + """Compiled SQL of the dbt model.""" + + dbt_stats: Union[str, None, UnsetType] = UNSET + """Statistics of the dbt model.""" + + dbt_materialization_type: Union[str, None, UnsetType] = UNSET + """Type of materialization used for the dbt model.""" + + dbt_model_compile_started_at: Union[int, None, UnsetType] = UNSET + """Timestamp when the dbt model compilation started.""" + + dbt_model_compile_completed_at: Union[int, None, UnsetType] = UNSET + """Timestamp when the dbt model compilation completed.""" + + dbt_model_execute_started_at: Union[int, None, UnsetType] = UNSET + """Timestamp when the dbt model execution started.""" + + dbt_model_execute_completed_at: Union[int, None, UnsetType] = UNSET + """Timestamp when the dbt model execution completed.""" + + dbt_model_execution_time: Union[float, None, UnsetType] = UNSET + """Execution time of the dbt model.""" + + dbt_model_run_generated_at: Union[int, None, UnsetType] = UNSET + """Timestamp when the dbt model run was generated.""" + + dbt_model_run_elapsed_time: Union[float, None, UnsetType] = UNSET + """Elapsed time of the dbt model run.""" + + dbt_alias: Union[str, None, UnsetType] = UNSET + """Alias of this asset in dbt.""" + + dbt_meta: Union[str, None, UnsetType] = UNSET + """Metadata for this asset in dbt, specifically everything under the 'meta' key in the dbt object.""" + + dbt_unique_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of this asset in dbt.""" + + dbt_account_name: Union[str, None, UnsetType] = UNSET + """Name of the account in which this asset exists in dbt.""" + + dbt_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which this asset exists in dbt.""" + + dbt_package_name: Union[str, None, UnsetType] = UNSET + """Name of the package in which this asset exists in dbt.""" + + dbt_job_name: Union[str, None, UnsetType] = UNSET + """Name of the job that materialized this asset in dbt.""" + + dbt_job_schedule: Union[str, None, UnsetType] = UNSET + """Schedule of the job that materialized this asset in dbt.""" + + dbt_job_status: Union[str, None, UnsetType] = UNSET + """Status of the job that materialized this asset in dbt.""" + + dbt_job_schedule_cron_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable cron schedule of the job that materialized this asset in dbt.""" + + dbt_job_last_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt last ran, in milliseconds.""" + + dbt_job_next_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt will next run, in milliseconds.""" + + dbt_job_next_run_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable time at which the job that materialized this asset in dbt will next run.""" + + dbt_environment_name: Union[str, None, UnsetType] = UNSET + """Name of the environment in which this asset exists in dbt.""" + + dbt_environment_dbt_version: Union[str, None, UnsetType] = UNSET + """Version of dbt used in the environment.""" + + dbt_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset in dbt.""" + + dbt_connection_context: Union[str, None, UnsetType] = UNSET + """Connection context for this asset in dbt.""" + + dbt_semantic_layer_proxy_url: Union[str, None, UnsetType] = UNSET + """URL of the semantic layer proxy for this asset in dbt.""" + + dbt_job_runs: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of latest dbt job runs across all environments.""" + + +class DbtModelRelationshipAttributes(AssetRelationshipAttributes): + """DbtModel-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + sql_asset: Union[RelatedSQL, None, UnsetType] = UNSET + """(Deprecated) Assets related to the model.""" + + dbt_model_sql_assets: Union[List[RelatedSQL], None, UnsetType] = UNSET + """Model containing the assets.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this model.""" + + dbt_metrics: Union[List[RelatedDbtMetric], None, UnsetType] = UNSET + """Metrics that exist within this model.""" + + dbt_model_columns: Union[List[RelatedDbtModelColumn], None, UnsetType] = UNSET + """Columns that exist within this dbt model.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DbtModelNested(AssetNested): + """DbtModel in nested API format for high-performance serialization.""" + + attributes: Union[DbtModelAttributes, UnsetType] = UNSET + relationship_attributes: Union[DbtModelRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[DbtModelRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[DbtModelRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DBT_MODEL_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "sql_asset", + "dbt_model_sql_assets", + "dbt_tests", + "dbt_metrics", + "dbt_model_columns", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_dbt_model_attrs(attrs: DbtModelAttributes, obj: DbtModel) -> None: + """Populate DbtModel-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.dbt_status = obj.dbt_status + attrs.dbt_error = obj.dbt_error + attrs.dbt_raw_sql = obj.dbt_raw_sql + attrs.dbt_compiled_sql = obj.dbt_compiled_sql + attrs.dbt_stats = obj.dbt_stats + attrs.dbt_materialization_type = obj.dbt_materialization_type + attrs.dbt_model_compile_started_at = obj.dbt_model_compile_started_at + attrs.dbt_model_compile_completed_at = obj.dbt_model_compile_completed_at + attrs.dbt_model_execute_started_at = obj.dbt_model_execute_started_at + attrs.dbt_model_execute_completed_at = obj.dbt_model_execute_completed_at + attrs.dbt_model_execution_time = obj.dbt_model_execution_time + attrs.dbt_model_run_generated_at = obj.dbt_model_run_generated_at + attrs.dbt_model_run_elapsed_time = obj.dbt_model_run_elapsed_time + attrs.dbt_alias = obj.dbt_alias + attrs.dbt_meta = obj.dbt_meta + attrs.dbt_unique_id = obj.dbt_unique_id + attrs.dbt_account_name = obj.dbt_account_name + attrs.dbt_project_name = obj.dbt_project_name + attrs.dbt_package_name = obj.dbt_package_name + attrs.dbt_job_name = obj.dbt_job_name + attrs.dbt_job_schedule = obj.dbt_job_schedule + attrs.dbt_job_status = obj.dbt_job_status + attrs.dbt_job_schedule_cron_humanized = obj.dbt_job_schedule_cron_humanized + attrs.dbt_job_last_run = obj.dbt_job_last_run + attrs.dbt_job_next_run = obj.dbt_job_next_run + attrs.dbt_job_next_run_humanized = obj.dbt_job_next_run_humanized + attrs.dbt_environment_name = obj.dbt_environment_name + attrs.dbt_environment_dbt_version = obj.dbt_environment_dbt_version + attrs.dbt_tags = obj.dbt_tags + attrs.dbt_connection_context = obj.dbt_connection_context + attrs.dbt_semantic_layer_proxy_url = obj.dbt_semantic_layer_proxy_url + attrs.dbt_job_runs = obj.dbt_job_runs + + +def _extract_dbt_model_attrs(attrs: DbtModelAttributes) -> dict: + """Extract all DbtModel attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["dbt_status"] = attrs.dbt_status + result["dbt_error"] = attrs.dbt_error + result["dbt_raw_sql"] = attrs.dbt_raw_sql + result["dbt_compiled_sql"] = attrs.dbt_compiled_sql + result["dbt_stats"] = attrs.dbt_stats + result["dbt_materialization_type"] = attrs.dbt_materialization_type + result["dbt_model_compile_started_at"] = attrs.dbt_model_compile_started_at + result["dbt_model_compile_completed_at"] = attrs.dbt_model_compile_completed_at + result["dbt_model_execute_started_at"] = attrs.dbt_model_execute_started_at + result["dbt_model_execute_completed_at"] = attrs.dbt_model_execute_completed_at + result["dbt_model_execution_time"] = attrs.dbt_model_execution_time + result["dbt_model_run_generated_at"] = attrs.dbt_model_run_generated_at + result["dbt_model_run_elapsed_time"] = attrs.dbt_model_run_elapsed_time + result["dbt_alias"] = attrs.dbt_alias + result["dbt_meta"] = attrs.dbt_meta + result["dbt_unique_id"] = attrs.dbt_unique_id + result["dbt_account_name"] = attrs.dbt_account_name + result["dbt_project_name"] = attrs.dbt_project_name + result["dbt_package_name"] = attrs.dbt_package_name + result["dbt_job_name"] = attrs.dbt_job_name + result["dbt_job_schedule"] = attrs.dbt_job_schedule + result["dbt_job_status"] = attrs.dbt_job_status + result["dbt_job_schedule_cron_humanized"] = attrs.dbt_job_schedule_cron_humanized + result["dbt_job_last_run"] = attrs.dbt_job_last_run + result["dbt_job_next_run"] = attrs.dbt_job_next_run + result["dbt_job_next_run_humanized"] = attrs.dbt_job_next_run_humanized + result["dbt_environment_name"] = attrs.dbt_environment_name + result["dbt_environment_dbt_version"] = attrs.dbt_environment_dbt_version + result["dbt_tags"] = attrs.dbt_tags + result["dbt_connection_context"] = attrs.dbt_connection_context + result["dbt_semantic_layer_proxy_url"] = attrs.dbt_semantic_layer_proxy_url + result["dbt_job_runs"] = attrs.dbt_job_runs + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _dbt_model_to_nested(dbt_model: DbtModel) -> DbtModelNested: + """Convert flat DbtModel to nested format.""" + attrs = DbtModelAttributes() + _populate_dbt_model_attrs(attrs, dbt_model) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + dbt_model, _DBT_MODEL_REL_FIELDS, DbtModelRelationshipAttributes + ) + return DbtModelNested( + guid=dbt_model.guid, + type_name=dbt_model.type_name, + status=dbt_model.status, + version=dbt_model.version, + create_time=dbt_model.create_time, + update_time=dbt_model.update_time, + created_by=dbt_model.created_by, + updated_by=dbt_model.updated_by, + classifications=dbt_model.classifications, + classification_names=dbt_model.classification_names, + meanings=dbt_model.meanings, + labels=dbt_model.labels, + business_attributes=dbt_model.business_attributes, + custom_attributes=dbt_model.custom_attributes, + pending_tasks=dbt_model.pending_tasks, + proxy=dbt_model.proxy, + is_incomplete=dbt_model.is_incomplete, + provenance_type=dbt_model.provenance_type, + home_id=dbt_model.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _dbt_model_from_nested(nested: DbtModelNested) -> DbtModel: + """Convert nested format to flat DbtModel.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else DbtModelAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DBT_MODEL_REL_FIELDS, + DbtModelRelationshipAttributes, + ) + return DbtModel( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_dbt_model_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _dbt_model_to_nested_bytes(dbt_model: DbtModel, serde: Serde) -> bytes: + """Convert flat DbtModel to nested JSON bytes.""" + return serde.encode(_dbt_model_to_nested(dbt_model)) + + +def _dbt_model_from_nested_bytes(data: bytes, serde: Serde) -> DbtModel: + """Convert nested JSON bytes to flat DbtModel.""" + nested = serde.decode(data, DbtModelNested) + return _dbt_model_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +DbtModel.DBT_STATUS = KeywordField("dbtStatus", "dbtStatus") +DbtModel.DBT_ERROR = KeywordField("dbtError", "dbtError") +DbtModel.DBT_RAW_SQL = KeywordField("dbtRawSQL", "dbtRawSQL") +DbtModel.DBT_COMPILED_SQL = KeywordField("dbtCompiledSQL", "dbtCompiledSQL") +DbtModel.DBT_STATS = KeywordField("dbtStats", "dbtStats") +DbtModel.DBT_MATERIALIZATION_TYPE = KeywordField( + "dbtMaterializationType", "dbtMaterializationType" +) +DbtModel.DBT_MODEL_COMPILE_STARTED_AT = NumericField( + "dbtModelCompileStartedAt", "dbtModelCompileStartedAt" +) +DbtModel.DBT_MODEL_COMPILE_COMPLETED_AT = NumericField( + "dbtModelCompileCompletedAt", "dbtModelCompileCompletedAt" +) +DbtModel.DBT_MODEL_EXECUTE_STARTED_AT = NumericField( + "dbtModelExecuteStartedAt", "dbtModelExecuteStartedAt" +) +DbtModel.DBT_MODEL_EXECUTE_COMPLETED_AT = NumericField( + "dbtModelExecuteCompletedAt", "dbtModelExecuteCompletedAt" +) +DbtModel.DBT_MODEL_EXECUTION_TIME = NumericField( + "dbtModelExecutionTime", "dbtModelExecutionTime" +) +DbtModel.DBT_MODEL_RUN_GENERATED_AT = NumericField( + "dbtModelRunGeneratedAt", "dbtModelRunGeneratedAt" +) +DbtModel.DBT_MODEL_RUN_ELAPSED_TIME = NumericField( + "dbtModelRunElapsedTime", "dbtModelRunElapsedTime" +) +DbtModel.DBT_ALIAS = KeywordField("dbtAlias", "dbtAlias") +DbtModel.DBT_META = KeywordField("dbtMeta", "dbtMeta") +DbtModel.DBT_UNIQUE_ID = KeywordField("dbtUniqueId", "dbtUniqueId") +DbtModel.DBT_ACCOUNT_NAME = KeywordField("dbtAccountName", "dbtAccountName") +DbtModel.DBT_PROJECT_NAME = KeywordField("dbtProjectName", "dbtProjectName") +DbtModel.DBT_PACKAGE_NAME = KeywordField("dbtPackageName", "dbtPackageName") +DbtModel.DBT_JOB_NAME = KeywordField("dbtJobName", "dbtJobName") +DbtModel.DBT_JOB_SCHEDULE = KeywordField("dbtJobSchedule", "dbtJobSchedule") +DbtModel.DBT_JOB_STATUS = KeywordField("dbtJobStatus", "dbtJobStatus") +DbtModel.DBT_JOB_SCHEDULE_CRON_HUMANIZED = KeywordField( + "dbtJobScheduleCronHumanized", "dbtJobScheduleCronHumanized" +) +DbtModel.DBT_JOB_LAST_RUN = NumericField("dbtJobLastRun", "dbtJobLastRun") +DbtModel.DBT_JOB_NEXT_RUN = NumericField("dbtJobNextRun", "dbtJobNextRun") +DbtModel.DBT_JOB_NEXT_RUN_HUMANIZED = KeywordField( + "dbtJobNextRunHumanized", "dbtJobNextRunHumanized" +) +DbtModel.DBT_ENVIRONMENT_NAME = KeywordField("dbtEnvironmentName", "dbtEnvironmentName") +DbtModel.DBT_ENVIRONMENT_DBT_VERSION = KeywordField( + "dbtEnvironmentDbtVersion", "dbtEnvironmentDbtVersion" +) +DbtModel.DBT_TAGS = KeywordField("dbtTags", "dbtTags") +DbtModel.DBT_CONNECTION_CONTEXT = KeywordField( + "dbtConnectionContext", "dbtConnectionContext" +) +DbtModel.DBT_SEMANTIC_LAYER_PROXY_URL = KeywordField( + "dbtSemanticLayerProxyUrl", "dbtSemanticLayerProxyUrl" +) +DbtModel.DBT_JOB_RUNS = KeywordField("dbtJobRuns", "dbtJobRuns") +DbtModel.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +DbtModel.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +DbtModel.ANOMALO_CHECKS = RelationField("anomaloChecks") +DbtModel.APPLICATION = RelationField("application") +DbtModel.APPLICATION_FIELD = RelationField("applicationField") +DbtModel.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +DbtModel.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +DbtModel.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +DbtModel.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +DbtModel.METRICS = RelationField("metrics") +DbtModel.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +DbtModel.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +DbtModel.SQL_ASSET = RelationField("sqlAsset") +DbtModel.DBT_MODEL_SQL_ASSETS = RelationField("dbtModelSqlAssets") +DbtModel.DBT_TESTS = RelationField("dbtTests") +DbtModel.DBT_METRICS = RelationField("dbtMetrics") +DbtModel.DBT_MODEL_COLUMNS = RelationField("dbtModelColumns") +DbtModel.MEANINGS = RelationField("meanings") +DbtModel.MC_MONITORS = RelationField("mcMonitors") +DbtModel.MC_INCIDENTS = RelationField("mcIncidents") +DbtModel.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +DbtModel.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +DbtModel.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +DbtModel.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +DbtModel.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +DbtModel.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +DbtModel.FILES = RelationField("files") +DbtModel.LINKS = RelationField("links") +DbtModel.README = RelationField("readme") +DbtModel.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +DbtModel.SODA_CHECKS = RelationField("sodaChecks") +DbtModel.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +DbtModel.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/dbt_model_column.py b/pyatlan_v9/model/assets/dbt_model_column.py new file mode 100644 index 000000000..1fb3c66b8 --- /dev/null +++ b/pyatlan_v9/model/assets/dbt_model_column.py @@ -0,0 +1,838 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DbtModelColumn asset model with flattened inheritance. + +This module provides: +- DbtModelColumn: Flat asset class (easy to use) +- DbtModelColumnAttributes: Nested attributes struct (extends AssetAttributes) +- DbtModelColumnNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from .sql_related import RelatedColumn +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .dbt_related import RelatedDbtModel, RelatedDbtSeed, RelatedDbtTest + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class DbtModelColumn(Asset): + """ + Instance of a column within a dbt model in Atlan. + """ + + DBT_MODEL_QUALIFIED_NAME: ClassVar[Any] = None + DBT_MODEL_COLUMN_DATA_TYPE: ClassVar[Any] = None + DBT_MODEL_COLUMN_ORDER: ClassVar[Any] = None + DBT_ALIAS: ClassVar[Any] = None + DBT_META: ClassVar[Any] = None + DBT_UNIQUE_ID: ClassVar[Any] = None + DBT_ACCOUNT_NAME: ClassVar[Any] = None + DBT_PROJECT_NAME: ClassVar[Any] = None + DBT_PACKAGE_NAME: ClassVar[Any] = None + DBT_JOB_NAME: ClassVar[Any] = None + DBT_JOB_SCHEDULE: ClassVar[Any] = None + DBT_JOB_STATUS: ClassVar[Any] = None + DBT_JOB_SCHEDULE_CRON_HUMANIZED: ClassVar[Any] = None + DBT_JOB_LAST_RUN: ClassVar[Any] = None + DBT_JOB_NEXT_RUN: ClassVar[Any] = None + DBT_JOB_NEXT_RUN_HUMANIZED: ClassVar[Any] = None + DBT_ENVIRONMENT_NAME: ClassVar[Any] = None + DBT_ENVIRONMENT_DBT_VERSION: ClassVar[Any] = None + DBT_TAGS: ClassVar[Any] = None + DBT_CONNECTION_CONTEXT: ClassVar[Any] = None + DBT_SEMANTIC_LAYER_PROXY_URL: ClassVar[Any] = None + DBT_JOB_RUNS: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_MODEL: ClassVar[Any] = None + SQL_COLUMN: ClassVar[Any] = None + DBT_MODEL_COLUMN_SQL_COLUMNS: ClassVar[Any] = None + DBT_SEED: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "DbtModelColumn" + + dbt_model_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the dbt model this column belongs to.""" + + dbt_model_column_data_type: Union[str, None, UnsetType] = UNSET + """Data type of the dbt model column.""" + + dbt_model_column_order: Union[int, None, UnsetType] = UNSET + """Order of the column in the dbt model.""" + + dbt_alias: Union[str, None, UnsetType] = UNSET + """Alias of this asset in dbt.""" + + dbt_meta: Union[str, None, UnsetType] = UNSET + """Metadata for this asset in dbt, specifically everything under the 'meta' key in the dbt object.""" + + dbt_unique_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of this asset in dbt.""" + + dbt_account_name: Union[str, None, UnsetType] = UNSET + """Name of the account in which this asset exists in dbt.""" + + dbt_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which this asset exists in dbt.""" + + dbt_package_name: Union[str, None, UnsetType] = UNSET + """Name of the package in which this asset exists in dbt.""" + + dbt_job_name: Union[str, None, UnsetType] = UNSET + """Name of the job that materialized this asset in dbt.""" + + dbt_job_schedule: Union[str, None, UnsetType] = UNSET + """Schedule of the job that materialized this asset in dbt.""" + + dbt_job_status: Union[str, None, UnsetType] = UNSET + """Status of the job that materialized this asset in dbt.""" + + dbt_job_schedule_cron_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable cron schedule of the job that materialized this asset in dbt.""" + + dbt_job_last_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt last ran, in milliseconds.""" + + dbt_job_next_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt will next run, in milliseconds.""" + + dbt_job_next_run_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable time at which the job that materialized this asset in dbt will next run.""" + + dbt_environment_name: Union[str, None, UnsetType] = UNSET + """Name of the environment in which this asset exists in dbt.""" + + dbt_environment_dbt_version: Union[str, None, UnsetType] = UNSET + """Version of dbt used in the environment.""" + + dbt_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset in dbt.""" + + dbt_connection_context: Union[str, None, UnsetType] = UNSET + """Connection context for this asset in dbt.""" + + dbt_semantic_layer_proxy_url: Union[str, None, UnsetType] = UNSET + """URL of the semantic layer proxy for this asset in dbt.""" + + dbt_job_runs: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of latest dbt job runs across all environments.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this model column.""" + + dbt_model: Union[RelatedDbtModel, None, UnsetType] = UNSET + """Model in which this dbt column exists.""" + + sql_column: Union[RelatedColumn, None, UnsetType] = UNSET + """(Deprecated) Columns related to this model column.""" + + dbt_model_column_sql_columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Columns related to this model column.""" + + dbt_seed: Union[RelatedDbtSeed, None, UnsetType] = UNSET + """Seed in which this dbt column exists.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "DbtModelColumn" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _dbt_model_column_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> DbtModelColumn: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + DbtModelColumn instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _dbt_model_column_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DbtModelColumnAttributes(AssetAttributes): + """DbtModelColumn-specific attributes for nested API format.""" + + dbt_model_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the dbt model this column belongs to.""" + + dbt_model_column_data_type: Union[str, None, UnsetType] = UNSET + """Data type of the dbt model column.""" + + dbt_model_column_order: Union[int, None, UnsetType] = UNSET + """Order of the column in the dbt model.""" + + dbt_alias: Union[str, None, UnsetType] = UNSET + """Alias of this asset in dbt.""" + + dbt_meta: Union[str, None, UnsetType] = UNSET + """Metadata for this asset in dbt, specifically everything under the 'meta' key in the dbt object.""" + + dbt_unique_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of this asset in dbt.""" + + dbt_account_name: Union[str, None, UnsetType] = UNSET + """Name of the account in which this asset exists in dbt.""" + + dbt_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which this asset exists in dbt.""" + + dbt_package_name: Union[str, None, UnsetType] = UNSET + """Name of the package in which this asset exists in dbt.""" + + dbt_job_name: Union[str, None, UnsetType] = UNSET + """Name of the job that materialized this asset in dbt.""" + + dbt_job_schedule: Union[str, None, UnsetType] = UNSET + """Schedule of the job that materialized this asset in dbt.""" + + dbt_job_status: Union[str, None, UnsetType] = UNSET + """Status of the job that materialized this asset in dbt.""" + + dbt_job_schedule_cron_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable cron schedule of the job that materialized this asset in dbt.""" + + dbt_job_last_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt last ran, in milliseconds.""" + + dbt_job_next_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt will next run, in milliseconds.""" + + dbt_job_next_run_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable time at which the job that materialized this asset in dbt will next run.""" + + dbt_environment_name: Union[str, None, UnsetType] = UNSET + """Name of the environment in which this asset exists in dbt.""" + + dbt_environment_dbt_version: Union[str, None, UnsetType] = UNSET + """Version of dbt used in the environment.""" + + dbt_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset in dbt.""" + + dbt_connection_context: Union[str, None, UnsetType] = UNSET + """Connection context for this asset in dbt.""" + + dbt_semantic_layer_proxy_url: Union[str, None, UnsetType] = UNSET + """URL of the semantic layer proxy for this asset in dbt.""" + + dbt_job_runs: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of latest dbt job runs across all environments.""" + + +class DbtModelColumnRelationshipAttributes(AssetRelationshipAttributes): + """DbtModelColumn-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this model column.""" + + dbt_model: Union[RelatedDbtModel, None, UnsetType] = UNSET + """Model in which this dbt column exists.""" + + sql_column: Union[RelatedColumn, None, UnsetType] = UNSET + """(Deprecated) Columns related to this model column.""" + + dbt_model_column_sql_columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Columns related to this model column.""" + + dbt_seed: Union[RelatedDbtSeed, None, UnsetType] = UNSET + """Seed in which this dbt column exists.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DbtModelColumnNested(AssetNested): + """DbtModelColumn in nested API format for high-performance serialization.""" + + attributes: Union[DbtModelColumnAttributes, UnsetType] = UNSET + relationship_attributes: Union[DbtModelColumnRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + DbtModelColumnRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + DbtModelColumnRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DBT_MODEL_COLUMN_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "dbt_tests", + "dbt_model", + "sql_column", + "dbt_model_column_sql_columns", + "dbt_seed", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_dbt_model_column_attrs( + attrs: DbtModelColumnAttributes, obj: DbtModelColumn +) -> None: + """Populate DbtModelColumn-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.dbt_model_qualified_name = obj.dbt_model_qualified_name + attrs.dbt_model_column_data_type = obj.dbt_model_column_data_type + attrs.dbt_model_column_order = obj.dbt_model_column_order + attrs.dbt_alias = obj.dbt_alias + attrs.dbt_meta = obj.dbt_meta + attrs.dbt_unique_id = obj.dbt_unique_id + attrs.dbt_account_name = obj.dbt_account_name + attrs.dbt_project_name = obj.dbt_project_name + attrs.dbt_package_name = obj.dbt_package_name + attrs.dbt_job_name = obj.dbt_job_name + attrs.dbt_job_schedule = obj.dbt_job_schedule + attrs.dbt_job_status = obj.dbt_job_status + attrs.dbt_job_schedule_cron_humanized = obj.dbt_job_schedule_cron_humanized + attrs.dbt_job_last_run = obj.dbt_job_last_run + attrs.dbt_job_next_run = obj.dbt_job_next_run + attrs.dbt_job_next_run_humanized = obj.dbt_job_next_run_humanized + attrs.dbt_environment_name = obj.dbt_environment_name + attrs.dbt_environment_dbt_version = obj.dbt_environment_dbt_version + attrs.dbt_tags = obj.dbt_tags + attrs.dbt_connection_context = obj.dbt_connection_context + attrs.dbt_semantic_layer_proxy_url = obj.dbt_semantic_layer_proxy_url + attrs.dbt_job_runs = obj.dbt_job_runs + + +def _extract_dbt_model_column_attrs(attrs: DbtModelColumnAttributes) -> dict: + """Extract all DbtModelColumn attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["dbt_model_qualified_name"] = attrs.dbt_model_qualified_name + result["dbt_model_column_data_type"] = attrs.dbt_model_column_data_type + result["dbt_model_column_order"] = attrs.dbt_model_column_order + result["dbt_alias"] = attrs.dbt_alias + result["dbt_meta"] = attrs.dbt_meta + result["dbt_unique_id"] = attrs.dbt_unique_id + result["dbt_account_name"] = attrs.dbt_account_name + result["dbt_project_name"] = attrs.dbt_project_name + result["dbt_package_name"] = attrs.dbt_package_name + result["dbt_job_name"] = attrs.dbt_job_name + result["dbt_job_schedule"] = attrs.dbt_job_schedule + result["dbt_job_status"] = attrs.dbt_job_status + result["dbt_job_schedule_cron_humanized"] = attrs.dbt_job_schedule_cron_humanized + result["dbt_job_last_run"] = attrs.dbt_job_last_run + result["dbt_job_next_run"] = attrs.dbt_job_next_run + result["dbt_job_next_run_humanized"] = attrs.dbt_job_next_run_humanized + result["dbt_environment_name"] = attrs.dbt_environment_name + result["dbt_environment_dbt_version"] = attrs.dbt_environment_dbt_version + result["dbt_tags"] = attrs.dbt_tags + result["dbt_connection_context"] = attrs.dbt_connection_context + result["dbt_semantic_layer_proxy_url"] = attrs.dbt_semantic_layer_proxy_url + result["dbt_job_runs"] = attrs.dbt_job_runs + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _dbt_model_column_to_nested( + dbt_model_column: DbtModelColumn, +) -> DbtModelColumnNested: + """Convert flat DbtModelColumn to nested format.""" + attrs = DbtModelColumnAttributes() + _populate_dbt_model_column_attrs(attrs, dbt_model_column) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + dbt_model_column, + _DBT_MODEL_COLUMN_REL_FIELDS, + DbtModelColumnRelationshipAttributes, + ) + return DbtModelColumnNested( + guid=dbt_model_column.guid, + type_name=dbt_model_column.type_name, + status=dbt_model_column.status, + version=dbt_model_column.version, + create_time=dbt_model_column.create_time, + update_time=dbt_model_column.update_time, + created_by=dbt_model_column.created_by, + updated_by=dbt_model_column.updated_by, + classifications=dbt_model_column.classifications, + classification_names=dbt_model_column.classification_names, + meanings=dbt_model_column.meanings, + labels=dbt_model_column.labels, + business_attributes=dbt_model_column.business_attributes, + custom_attributes=dbt_model_column.custom_attributes, + pending_tasks=dbt_model_column.pending_tasks, + proxy=dbt_model_column.proxy, + is_incomplete=dbt_model_column.is_incomplete, + provenance_type=dbt_model_column.provenance_type, + home_id=dbt_model_column.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _dbt_model_column_from_nested(nested: DbtModelColumnNested) -> DbtModelColumn: + """Convert nested format to flat DbtModelColumn.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else DbtModelColumnAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DBT_MODEL_COLUMN_REL_FIELDS, + DbtModelColumnRelationshipAttributes, + ) + return DbtModelColumn( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_dbt_model_column_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _dbt_model_column_to_nested_bytes( + dbt_model_column: DbtModelColumn, serde: Serde +) -> bytes: + """Convert flat DbtModelColumn to nested JSON bytes.""" + return serde.encode(_dbt_model_column_to_nested(dbt_model_column)) + + +def _dbt_model_column_from_nested_bytes(data: bytes, serde: Serde) -> DbtModelColumn: + """Convert nested JSON bytes to flat DbtModelColumn.""" + nested = serde.decode(data, DbtModelColumnNested) + return _dbt_model_column_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +DbtModelColumn.DBT_MODEL_QUALIFIED_NAME = KeywordTextField( + "dbtModelQualifiedName", "dbtModelQualifiedName", "dbtModelQualifiedName.text" +) +DbtModelColumn.DBT_MODEL_COLUMN_DATA_TYPE = KeywordField( + "dbtModelColumnDataType", "dbtModelColumnDataType" +) +DbtModelColumn.DBT_MODEL_COLUMN_ORDER = NumericField( + "dbtModelColumnOrder", "dbtModelColumnOrder" +) +DbtModelColumn.DBT_ALIAS = KeywordField("dbtAlias", "dbtAlias") +DbtModelColumn.DBT_META = KeywordField("dbtMeta", "dbtMeta") +DbtModelColumn.DBT_UNIQUE_ID = KeywordField("dbtUniqueId", "dbtUniqueId") +DbtModelColumn.DBT_ACCOUNT_NAME = KeywordField("dbtAccountName", "dbtAccountName") +DbtModelColumn.DBT_PROJECT_NAME = KeywordField("dbtProjectName", "dbtProjectName") +DbtModelColumn.DBT_PACKAGE_NAME = KeywordField("dbtPackageName", "dbtPackageName") +DbtModelColumn.DBT_JOB_NAME = KeywordField("dbtJobName", "dbtJobName") +DbtModelColumn.DBT_JOB_SCHEDULE = KeywordField("dbtJobSchedule", "dbtJobSchedule") +DbtModelColumn.DBT_JOB_STATUS = KeywordField("dbtJobStatus", "dbtJobStatus") +DbtModelColumn.DBT_JOB_SCHEDULE_CRON_HUMANIZED = KeywordField( + "dbtJobScheduleCronHumanized", "dbtJobScheduleCronHumanized" +) +DbtModelColumn.DBT_JOB_LAST_RUN = NumericField("dbtJobLastRun", "dbtJobLastRun") +DbtModelColumn.DBT_JOB_NEXT_RUN = NumericField("dbtJobNextRun", "dbtJobNextRun") +DbtModelColumn.DBT_JOB_NEXT_RUN_HUMANIZED = KeywordField( + "dbtJobNextRunHumanized", "dbtJobNextRunHumanized" +) +DbtModelColumn.DBT_ENVIRONMENT_NAME = KeywordField( + "dbtEnvironmentName", "dbtEnvironmentName" +) +DbtModelColumn.DBT_ENVIRONMENT_DBT_VERSION = KeywordField( + "dbtEnvironmentDbtVersion", "dbtEnvironmentDbtVersion" +) +DbtModelColumn.DBT_TAGS = KeywordField("dbtTags", "dbtTags") +DbtModelColumn.DBT_CONNECTION_CONTEXT = KeywordField( + "dbtConnectionContext", "dbtConnectionContext" +) +DbtModelColumn.DBT_SEMANTIC_LAYER_PROXY_URL = KeywordField( + "dbtSemanticLayerProxyUrl", "dbtSemanticLayerProxyUrl" +) +DbtModelColumn.DBT_JOB_RUNS = KeywordField("dbtJobRuns", "dbtJobRuns") +DbtModelColumn.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +DbtModelColumn.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +DbtModelColumn.ANOMALO_CHECKS = RelationField("anomaloChecks") +DbtModelColumn.APPLICATION = RelationField("application") +DbtModelColumn.APPLICATION_FIELD = RelationField("applicationField") +DbtModelColumn.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +DbtModelColumn.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +DbtModelColumn.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +DbtModelColumn.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +DbtModelColumn.METRICS = RelationField("metrics") +DbtModelColumn.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +DbtModelColumn.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +DbtModelColumn.DBT_TESTS = RelationField("dbtTests") +DbtModelColumn.DBT_MODEL = RelationField("dbtModel") +DbtModelColumn.SQL_COLUMN = RelationField("sqlColumn") +DbtModelColumn.DBT_MODEL_COLUMN_SQL_COLUMNS = RelationField("dbtModelColumnSqlColumns") +DbtModelColumn.DBT_SEED = RelationField("dbtSeed") +DbtModelColumn.MEANINGS = RelationField("meanings") +DbtModelColumn.MC_MONITORS = RelationField("mcMonitors") +DbtModelColumn.MC_INCIDENTS = RelationField("mcIncidents") +DbtModelColumn.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +DbtModelColumn.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +DbtModelColumn.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +DbtModelColumn.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +DbtModelColumn.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +DbtModelColumn.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +DbtModelColumn.FILES = RelationField("files") +DbtModelColumn.LINKS = RelationField("links") +DbtModelColumn.README = RelationField("readme") +DbtModelColumn.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +DbtModelColumn.SODA_CHECKS = RelationField("sodaChecks") +DbtModelColumn.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +DbtModelColumn.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/dbt_process.py b/pyatlan_v9/model/assets/dbt_process.py new file mode 100644 index 000000000..339184104 --- /dev/null +++ b/pyatlan_v9/model/assets/dbt_process.py @@ -0,0 +1,952 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DbtProcess asset model with flattened inheritance. + +This module provides: +- DbtProcess: Flat asset class (easy to use) +- DbtProcessAttributes: Nested attributes struct (extends AssetAttributes) +- DbtProcessNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .adf_related import RelatedAdfActivity +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .catalog_related import RelatedCatalog +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .fabric_related import RelatedFabricActivity +from .fivetran_related import RelatedFivetranConnector +from .flow_related import RelatedFlowControlOperation +from .gtc_related import RelatedAtlasGlossaryTerm +from .matillion_related import RelatedMatillionComponent +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .power_bi_related import RelatedPowerBIDataflow +from .process_related import RelatedColumnProcess, RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from .sql_related import RelatedFunction, RelatedProcedure +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class DbtProcess(Asset): + """ + Instance of a lineage process for dbt in Atlan. + """ + + DBT_PROCESS_JOB_STATUS: ClassVar[Any] = None + DBT_UPSTREAM_CONTEXTS: ClassVar[Any] = None + DBT_ALIAS: ClassVar[Any] = None + DBT_META: ClassVar[Any] = None + DBT_UNIQUE_ID: ClassVar[Any] = None + DBT_ACCOUNT_NAME: ClassVar[Any] = None + DBT_PROJECT_NAME: ClassVar[Any] = None + DBT_PACKAGE_NAME: ClassVar[Any] = None + DBT_JOB_NAME: ClassVar[Any] = None + DBT_JOB_SCHEDULE: ClassVar[Any] = None + DBT_JOB_STATUS: ClassVar[Any] = None + DBT_JOB_SCHEDULE_CRON_HUMANIZED: ClassVar[Any] = None + DBT_JOB_LAST_RUN: ClassVar[Any] = None + DBT_JOB_NEXT_RUN: ClassVar[Any] = None + DBT_JOB_NEXT_RUN_HUMANIZED: ClassVar[Any] = None + DBT_ENVIRONMENT_NAME: ClassVar[Any] = None + DBT_ENVIRONMENT_DBT_VERSION: ClassVar[Any] = None + DBT_TAGS: ClassVar[Any] = None + DBT_CONNECTION_CONTEXT: ClassVar[Any] = None + DBT_SEMANTIC_LAYER_PROXY_URL: ClassVar[Any] = None + DBT_JOB_RUNS: ClassVar[Any] = None + CODE: ClassVar[Any] = None + SQL: ClassVar[Any] = None + PARENT_CONNECTION_PROCESS_QUALIFIED_NAME: ClassVar[Any] = None + AST: ClassVar[Any] = None + ADDITIONAL_ETL_CONTEXT: ClassVar[Any] = None + AI_DATASET_TYPE: ClassVar[Any] = None + ADF_ACTIVITY: ClassVar[Any] = None + AIRFLOW_TASKS: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + FABRIC_ACTIVITIES: ClassVar[Any] = None + FIVETRAN_CONNECTOR: ClassVar[Any] = None + FLOW_ORCHESTRATED_BY: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MATILLION_COMPONENT: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + POWER_BI_DATAFLOW: ClassVar[Any] = None + INPUTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUTS: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + COLUMN_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SQL_PROCEDURES: ClassVar[Any] = None + SQL_FUNCTIONS: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + SPARK_JOBS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "DbtProcess" + + dbt_process_job_status: Union[str, None, UnsetType] = UNSET + """Status of the dbt process job.""" + + dbt_upstream_contexts: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """Context for inputs to this Process.""" + + dbt_alias: Union[str, None, UnsetType] = UNSET + """Alias of this asset in dbt.""" + + dbt_meta: Union[str, None, UnsetType] = UNSET + """Metadata for this asset in dbt, specifically everything under the 'meta' key in the dbt object.""" + + dbt_unique_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of this asset in dbt.""" + + dbt_account_name: Union[str, None, UnsetType] = UNSET + """Name of the account in which this asset exists in dbt.""" + + dbt_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which this asset exists in dbt.""" + + dbt_package_name: Union[str, None, UnsetType] = UNSET + """Name of the package in which this asset exists in dbt.""" + + dbt_job_name: Union[str, None, UnsetType] = UNSET + """Name of the job that materialized this asset in dbt.""" + + dbt_job_schedule: Union[str, None, UnsetType] = UNSET + """Schedule of the job that materialized this asset in dbt.""" + + dbt_job_status: Union[str, None, UnsetType] = UNSET + """Status of the job that materialized this asset in dbt.""" + + dbt_job_schedule_cron_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable cron schedule of the job that materialized this asset in dbt.""" + + dbt_job_last_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt last ran, in milliseconds.""" + + dbt_job_next_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt will next run, in milliseconds.""" + + dbt_job_next_run_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable time at which the job that materialized this asset in dbt will next run.""" + + dbt_environment_name: Union[str, None, UnsetType] = UNSET + """Name of the environment in which this asset exists in dbt.""" + + dbt_environment_dbt_version: Union[str, None, UnsetType] = UNSET + """Version of dbt used in the environment.""" + + dbt_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset in dbt.""" + + dbt_connection_context: Union[str, None, UnsetType] = UNSET + """Connection context for this asset in dbt.""" + + dbt_semantic_layer_proxy_url: Union[str, None, UnsetType] = UNSET + """URL of the semantic layer proxy for this asset in dbt.""" + + dbt_job_runs: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of latest dbt job runs across all environments.""" + + code: Union[str, None, UnsetType] = UNSET + """Code that ran within the process.""" + + sql: Union[str, None, UnsetType] = UNSET + """SQL query that ran to produce the outputs.""" + + parent_connection_process_qualified_name: Union[List[str], None, UnsetType] = UNSET + """""" + + ast: Union[str, None, UnsetType] = UNSET + """Parsed AST of the code or SQL statements that describe the logic of this process.""" + + additional_etl_context: Union[str, None, UnsetType] = UNSET + """Additional Context of the ETL pipeline/notebook which creates the process.""" + + ai_dataset_type: Union[str, None, UnsetType] = UNSET + """Dataset type for AI Model - dataset process.""" + + adf_activity: Union[RelatedAdfActivity, None, UnsetType] = UNSET + """ADF Activity that is associated with this lineage process.""" + + airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks that exist within this process.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + fabric_activities: Union[List[RelatedFabricActivity], None, UnsetType] = UNSET + """Individual Fabric activities contained in the process.""" + + fivetran_connector: Union[RelatedFivetranConnector, None, UnsetType] = UNSET + """fivetranConnector in which this process exists.""" + + flow_orchestrated_by: Union[RelatedFlowControlOperation, None, UnsetType] = UNSET + """Orchestrated control operation that ran these data flows (process).""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + matillion_component: Union[RelatedMatillionComponent, None, UnsetType] = UNSET + """Matillion component that contains the logic for this lineage process.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + power_bi_dataflow: Union[RelatedPowerBIDataflow, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIDataflow" + ) + """PowerBI Dataflow that is associated with this lineage process.""" + + inputs: Union[List[RelatedCatalog], None, UnsetType] = UNSET + """Assets that are inputs to this process.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + outputs: Union[List[RelatedCatalog], None, UnsetType] = UNSET + """Assets that are outputs from this process.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + column_processes: Union[List[RelatedColumnProcess], None, UnsetType] = UNSET + """Processes that detail column-level lineage for this process.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + sql_procedures: Union[List[RelatedProcedure], None, UnsetType] = UNSET + """Procedures used by this process.""" + + sql_functions: Union[List[RelatedFunction], None, UnsetType] = UNSET + """Functions used by this process.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "DbtProcess" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _dbt_process_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> DbtProcess: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + DbtProcess instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _dbt_process_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DbtProcessAttributes(AssetAttributes): + """DbtProcess-specific attributes for nested API format.""" + + dbt_process_job_status: Union[str, None, UnsetType] = UNSET + """Status of the dbt process job.""" + + dbt_upstream_contexts: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """Context for inputs to this Process.""" + + dbt_alias: Union[str, None, UnsetType] = UNSET + """Alias of this asset in dbt.""" + + dbt_meta: Union[str, None, UnsetType] = UNSET + """Metadata for this asset in dbt, specifically everything under the 'meta' key in the dbt object.""" + + dbt_unique_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of this asset in dbt.""" + + dbt_account_name: Union[str, None, UnsetType] = UNSET + """Name of the account in which this asset exists in dbt.""" + + dbt_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which this asset exists in dbt.""" + + dbt_package_name: Union[str, None, UnsetType] = UNSET + """Name of the package in which this asset exists in dbt.""" + + dbt_job_name: Union[str, None, UnsetType] = UNSET + """Name of the job that materialized this asset in dbt.""" + + dbt_job_schedule: Union[str, None, UnsetType] = UNSET + """Schedule of the job that materialized this asset in dbt.""" + + dbt_job_status: Union[str, None, UnsetType] = UNSET + """Status of the job that materialized this asset in dbt.""" + + dbt_job_schedule_cron_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable cron schedule of the job that materialized this asset in dbt.""" + + dbt_job_last_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt last ran, in milliseconds.""" + + dbt_job_next_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt will next run, in milliseconds.""" + + dbt_job_next_run_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable time at which the job that materialized this asset in dbt will next run.""" + + dbt_environment_name: Union[str, None, UnsetType] = UNSET + """Name of the environment in which this asset exists in dbt.""" + + dbt_environment_dbt_version: Union[str, None, UnsetType] = UNSET + """Version of dbt used in the environment.""" + + dbt_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset in dbt.""" + + dbt_connection_context: Union[str, None, UnsetType] = UNSET + """Connection context for this asset in dbt.""" + + dbt_semantic_layer_proxy_url: Union[str, None, UnsetType] = UNSET + """URL of the semantic layer proxy for this asset in dbt.""" + + dbt_job_runs: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of latest dbt job runs across all environments.""" + + code: Union[str, None, UnsetType] = UNSET + """Code that ran within the process.""" + + sql: Union[str, None, UnsetType] = UNSET + """SQL query that ran to produce the outputs.""" + + parent_connection_process_qualified_name: Union[List[str], None, UnsetType] = UNSET + """""" + + ast: Union[str, None, UnsetType] = UNSET + """Parsed AST of the code or SQL statements that describe the logic of this process.""" + + additional_etl_context: Union[str, None, UnsetType] = UNSET + """Additional Context of the ETL pipeline/notebook which creates the process.""" + + ai_dataset_type: Union[str, None, UnsetType] = UNSET + """Dataset type for AI Model - dataset process.""" + + +class DbtProcessRelationshipAttributes(AssetRelationshipAttributes): + """DbtProcess-specific relationship attributes for nested API format.""" + + adf_activity: Union[RelatedAdfActivity, None, UnsetType] = UNSET + """ADF Activity that is associated with this lineage process.""" + + airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks that exist within this process.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + fabric_activities: Union[List[RelatedFabricActivity], None, UnsetType] = UNSET + """Individual Fabric activities contained in the process.""" + + fivetran_connector: Union[RelatedFivetranConnector, None, UnsetType] = UNSET + """fivetranConnector in which this process exists.""" + + flow_orchestrated_by: Union[RelatedFlowControlOperation, None, UnsetType] = UNSET + """Orchestrated control operation that ran these data flows (process).""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + matillion_component: Union[RelatedMatillionComponent, None, UnsetType] = UNSET + """Matillion component that contains the logic for this lineage process.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + power_bi_dataflow: Union[RelatedPowerBIDataflow, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIDataflow" + ) + """PowerBI Dataflow that is associated with this lineage process.""" + + inputs: Union[List[RelatedCatalog], None, UnsetType] = UNSET + """Assets that are inputs to this process.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + outputs: Union[List[RelatedCatalog], None, UnsetType] = UNSET + """Assets that are outputs from this process.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + column_processes: Union[List[RelatedColumnProcess], None, UnsetType] = UNSET + """Processes that detail column-level lineage for this process.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + sql_procedures: Union[List[RelatedProcedure], None, UnsetType] = UNSET + """Procedures used by this process.""" + + sql_functions: Union[List[RelatedFunction], None, UnsetType] = UNSET + """Functions used by this process.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DbtProcessNested(AssetNested): + """DbtProcess in nested API format for high-performance serialization.""" + + attributes: Union[DbtProcessAttributes, UnsetType] = UNSET + relationship_attributes: Union[DbtProcessRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + DbtProcessRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + DbtProcessRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DBT_PROCESS_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "adf_activity", + "airflow_tasks", + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "fabric_activities", + "fivetran_connector", + "flow_orchestrated_by", + "meanings", + "matillion_component", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "power_bi_dataflow", + "inputs", + "input_to_processes", + "outputs", + "output_from_processes", + "column_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "sql_procedures", + "sql_functions", + "schema_registry_subjects", + "soda_checks", + "spark_jobs", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_dbt_process_attrs(attrs: DbtProcessAttributes, obj: DbtProcess) -> None: + """Populate DbtProcess-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.dbt_process_job_status = obj.dbt_process_job_status + attrs.dbt_upstream_contexts = obj.dbt_upstream_contexts + attrs.dbt_alias = obj.dbt_alias + attrs.dbt_meta = obj.dbt_meta + attrs.dbt_unique_id = obj.dbt_unique_id + attrs.dbt_account_name = obj.dbt_account_name + attrs.dbt_project_name = obj.dbt_project_name + attrs.dbt_package_name = obj.dbt_package_name + attrs.dbt_job_name = obj.dbt_job_name + attrs.dbt_job_schedule = obj.dbt_job_schedule + attrs.dbt_job_status = obj.dbt_job_status + attrs.dbt_job_schedule_cron_humanized = obj.dbt_job_schedule_cron_humanized + attrs.dbt_job_last_run = obj.dbt_job_last_run + attrs.dbt_job_next_run = obj.dbt_job_next_run + attrs.dbt_job_next_run_humanized = obj.dbt_job_next_run_humanized + attrs.dbt_environment_name = obj.dbt_environment_name + attrs.dbt_environment_dbt_version = obj.dbt_environment_dbt_version + attrs.dbt_tags = obj.dbt_tags + attrs.dbt_connection_context = obj.dbt_connection_context + attrs.dbt_semantic_layer_proxy_url = obj.dbt_semantic_layer_proxy_url + attrs.dbt_job_runs = obj.dbt_job_runs + attrs.code = obj.code + attrs.sql = obj.sql + attrs.parent_connection_process_qualified_name = ( + obj.parent_connection_process_qualified_name + ) + attrs.ast = obj.ast + attrs.additional_etl_context = obj.additional_etl_context + attrs.ai_dataset_type = obj.ai_dataset_type + + +def _extract_dbt_process_attrs(attrs: DbtProcessAttributes) -> dict: + """Extract all DbtProcess attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["dbt_process_job_status"] = attrs.dbt_process_job_status + result["dbt_upstream_contexts"] = attrs.dbt_upstream_contexts + result["dbt_alias"] = attrs.dbt_alias + result["dbt_meta"] = attrs.dbt_meta + result["dbt_unique_id"] = attrs.dbt_unique_id + result["dbt_account_name"] = attrs.dbt_account_name + result["dbt_project_name"] = attrs.dbt_project_name + result["dbt_package_name"] = attrs.dbt_package_name + result["dbt_job_name"] = attrs.dbt_job_name + result["dbt_job_schedule"] = attrs.dbt_job_schedule + result["dbt_job_status"] = attrs.dbt_job_status + result["dbt_job_schedule_cron_humanized"] = attrs.dbt_job_schedule_cron_humanized + result["dbt_job_last_run"] = attrs.dbt_job_last_run + result["dbt_job_next_run"] = attrs.dbt_job_next_run + result["dbt_job_next_run_humanized"] = attrs.dbt_job_next_run_humanized + result["dbt_environment_name"] = attrs.dbt_environment_name + result["dbt_environment_dbt_version"] = attrs.dbt_environment_dbt_version + result["dbt_tags"] = attrs.dbt_tags + result["dbt_connection_context"] = attrs.dbt_connection_context + result["dbt_semantic_layer_proxy_url"] = attrs.dbt_semantic_layer_proxy_url + result["dbt_job_runs"] = attrs.dbt_job_runs + result["code"] = attrs.code + result["sql"] = attrs.sql + result["parent_connection_process_qualified_name"] = ( + attrs.parent_connection_process_qualified_name + ) + result["ast"] = attrs.ast + result["additional_etl_context"] = attrs.additional_etl_context + result["ai_dataset_type"] = attrs.ai_dataset_type + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _dbt_process_to_nested(dbt_process: DbtProcess) -> DbtProcessNested: + """Convert flat DbtProcess to nested format.""" + attrs = DbtProcessAttributes() + _populate_dbt_process_attrs(attrs, dbt_process) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + dbt_process, _DBT_PROCESS_REL_FIELDS, DbtProcessRelationshipAttributes + ) + return DbtProcessNested( + guid=dbt_process.guid, + type_name=dbt_process.type_name, + status=dbt_process.status, + version=dbt_process.version, + create_time=dbt_process.create_time, + update_time=dbt_process.update_time, + created_by=dbt_process.created_by, + updated_by=dbt_process.updated_by, + classifications=dbt_process.classifications, + classification_names=dbt_process.classification_names, + meanings=dbt_process.meanings, + labels=dbt_process.labels, + business_attributes=dbt_process.business_attributes, + custom_attributes=dbt_process.custom_attributes, + pending_tasks=dbt_process.pending_tasks, + proxy=dbt_process.proxy, + is_incomplete=dbt_process.is_incomplete, + provenance_type=dbt_process.provenance_type, + home_id=dbt_process.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _dbt_process_from_nested(nested: DbtProcessNested) -> DbtProcess: + """Convert nested format to flat DbtProcess.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else DbtProcessAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DBT_PROCESS_REL_FIELDS, + DbtProcessRelationshipAttributes, + ) + return DbtProcess( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_dbt_process_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _dbt_process_to_nested_bytes(dbt_process: DbtProcess, serde: Serde) -> bytes: + """Convert flat DbtProcess to nested JSON bytes.""" + return serde.encode(_dbt_process_to_nested(dbt_process)) + + +def _dbt_process_from_nested_bytes(data: bytes, serde: Serde) -> DbtProcess: + """Convert nested JSON bytes to flat DbtProcess.""" + nested = serde.decode(data, DbtProcessNested) + return _dbt_process_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +DbtProcess.DBT_PROCESS_JOB_STATUS = KeywordField( + "dbtProcessJobStatus", "dbtProcessJobStatus" +) +DbtProcess.DBT_UPSTREAM_CONTEXTS = KeywordField( + "dbtUpstreamContexts", "dbtUpstreamContexts" +) +DbtProcess.DBT_ALIAS = KeywordField("dbtAlias", "dbtAlias") +DbtProcess.DBT_META = KeywordField("dbtMeta", "dbtMeta") +DbtProcess.DBT_UNIQUE_ID = KeywordField("dbtUniqueId", "dbtUniqueId") +DbtProcess.DBT_ACCOUNT_NAME = KeywordField("dbtAccountName", "dbtAccountName") +DbtProcess.DBT_PROJECT_NAME = KeywordField("dbtProjectName", "dbtProjectName") +DbtProcess.DBT_PACKAGE_NAME = KeywordField("dbtPackageName", "dbtPackageName") +DbtProcess.DBT_JOB_NAME = KeywordField("dbtJobName", "dbtJobName") +DbtProcess.DBT_JOB_SCHEDULE = KeywordField("dbtJobSchedule", "dbtJobSchedule") +DbtProcess.DBT_JOB_STATUS = KeywordField("dbtJobStatus", "dbtJobStatus") +DbtProcess.DBT_JOB_SCHEDULE_CRON_HUMANIZED = KeywordField( + "dbtJobScheduleCronHumanized", "dbtJobScheduleCronHumanized" +) +DbtProcess.DBT_JOB_LAST_RUN = NumericField("dbtJobLastRun", "dbtJobLastRun") +DbtProcess.DBT_JOB_NEXT_RUN = NumericField("dbtJobNextRun", "dbtJobNextRun") +DbtProcess.DBT_JOB_NEXT_RUN_HUMANIZED = KeywordField( + "dbtJobNextRunHumanized", "dbtJobNextRunHumanized" +) +DbtProcess.DBT_ENVIRONMENT_NAME = KeywordField( + "dbtEnvironmentName", "dbtEnvironmentName" +) +DbtProcess.DBT_ENVIRONMENT_DBT_VERSION = KeywordField( + "dbtEnvironmentDbtVersion", "dbtEnvironmentDbtVersion" +) +DbtProcess.DBT_TAGS = KeywordField("dbtTags", "dbtTags") +DbtProcess.DBT_CONNECTION_CONTEXT = KeywordField( + "dbtConnectionContext", "dbtConnectionContext" +) +DbtProcess.DBT_SEMANTIC_LAYER_PROXY_URL = KeywordField( + "dbtSemanticLayerProxyUrl", "dbtSemanticLayerProxyUrl" +) +DbtProcess.DBT_JOB_RUNS = KeywordField("dbtJobRuns", "dbtJobRuns") +DbtProcess.CODE = KeywordField("code", "code") +DbtProcess.SQL = KeywordField("sql", "sql") +DbtProcess.PARENT_CONNECTION_PROCESS_QUALIFIED_NAME = KeywordField( + "parentConnectionProcessQualifiedName", "parentConnectionProcessQualifiedName" +) +DbtProcess.AST = KeywordField("ast", "ast") +DbtProcess.ADDITIONAL_ETL_CONTEXT = KeywordField( + "additionalEtlContext", "additionalEtlContext" +) +DbtProcess.AI_DATASET_TYPE = KeywordField("aiDatasetType", "aiDatasetType") +DbtProcess.ADF_ACTIVITY = RelationField("adfActivity") +DbtProcess.AIRFLOW_TASKS = RelationField("airflowTasks") +DbtProcess.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +DbtProcess.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +DbtProcess.ANOMALO_CHECKS = RelationField("anomaloChecks") +DbtProcess.APPLICATION = RelationField("application") +DbtProcess.APPLICATION_FIELD = RelationField("applicationField") +DbtProcess.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +DbtProcess.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +DbtProcess.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +DbtProcess.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +DbtProcess.METRICS = RelationField("metrics") +DbtProcess.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +DbtProcess.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +DbtProcess.FABRIC_ACTIVITIES = RelationField("fabricActivities") +DbtProcess.FIVETRAN_CONNECTOR = RelationField("fivetranConnector") +DbtProcess.FLOW_ORCHESTRATED_BY = RelationField("flowOrchestratedBy") +DbtProcess.MEANINGS = RelationField("meanings") +DbtProcess.MATILLION_COMPONENT = RelationField("matillionComponent") +DbtProcess.MC_MONITORS = RelationField("mcMonitors") +DbtProcess.MC_INCIDENTS = RelationField("mcIncidents") +DbtProcess.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +DbtProcess.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +DbtProcess.POWER_BI_DATAFLOW = RelationField("powerBIDataflow") +DbtProcess.INPUTS = RelationField("inputs") +DbtProcess.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +DbtProcess.OUTPUTS = RelationField("outputs") +DbtProcess.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +DbtProcess.COLUMN_PROCESSES = RelationField("columnProcesses") +DbtProcess.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +DbtProcess.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +DbtProcess.FILES = RelationField("files") +DbtProcess.LINKS = RelationField("links") +DbtProcess.README = RelationField("readme") +DbtProcess.SQL_PROCEDURES = RelationField("sqlProcedures") +DbtProcess.SQL_FUNCTIONS = RelationField("sqlFunctions") +DbtProcess.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +DbtProcess.SODA_CHECKS = RelationField("sodaChecks") +DbtProcess.SPARK_JOBS = RelationField("sparkJobs") +DbtProcess.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +DbtProcess.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/dbt_related.py b/pyatlan_v9/model/assets/dbt_related.py new file mode 100644 index 000000000..0a3deea34 --- /dev/null +++ b/pyatlan_v9/model/assets/dbt_related.py @@ -0,0 +1,432 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Dbt module. + +This module contains all Related{Type} classes for the Dbt type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Any, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedCatalog +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedDbt", + "RelatedDbtSemanticModel", + "RelatedDbtDimension", + "RelatedDbtMeasure", + "RelatedDbtEntity", + "RelatedDbtModel", + "RelatedDbtTest", + "RelatedDbtSource", + "RelatedDbtMetric", + "RelatedDbtModelColumn", + "RelatedDbtProcess", + "RelatedDbtColumnProcess", + "RelatedDbtTag", + "RelatedDbtSeed", +] + + +class RelatedDbt(RelatedCatalog): + """ + Related entity reference for Dbt assets. + + Extends RelatedCatalog with Dbt-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Dbt" so it serializes correctly + + dbt_alias: Union[str, None, UnsetType] = UNSET + """Alias of this asset in dbt.""" + + dbt_meta: Union[str, None, UnsetType] = UNSET + """Metadata for this asset in dbt, specifically everything under the 'meta' key in the dbt object.""" + + dbt_unique_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of this asset in dbt.""" + + dbt_account_name: Union[str, None, UnsetType] = UNSET + """Name of the account in which this asset exists in dbt.""" + + dbt_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which this asset exists in dbt.""" + + dbt_package_name: Union[str, None, UnsetType] = UNSET + """Name of the package in which this asset exists in dbt.""" + + dbt_job_name: Union[str, None, UnsetType] = UNSET + """Name of the job that materialized this asset in dbt.""" + + dbt_job_schedule: Union[str, None, UnsetType] = UNSET + """Schedule of the job that materialized this asset in dbt.""" + + dbt_job_status: Union[str, None, UnsetType] = UNSET + """Status of the job that materialized this asset in dbt.""" + + dbt_job_schedule_cron_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable cron schedule of the job that materialized this asset in dbt.""" + + dbt_job_last_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt last ran, in milliseconds.""" + + dbt_job_next_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt will next run, in milliseconds.""" + + dbt_job_next_run_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable time at which the job that materialized this asset in dbt will next run.""" + + dbt_environment_name: Union[str, None, UnsetType] = UNSET + """Name of the environment in which this asset exists in dbt.""" + + dbt_environment_dbt_version: Union[str, None, UnsetType] = UNSET + """Version of dbt used in the environment.""" + + dbt_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset in dbt.""" + + dbt_connection_context: Union[str, None, UnsetType] = UNSET + """Connection context for this asset in dbt.""" + + dbt_semantic_layer_proxy_url: Union[str, None, UnsetType] = UNSET + """URL of the semantic layer proxy for this asset in dbt.""" + + dbt_job_runs: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of latest dbt job runs across all environments.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Dbt" + + +class RelatedDbtSemanticModel(RelatedDbt): + """ + Related entity reference for DbtSemanticModel assets. + + Extends RelatedDbt with DbtSemanticModel-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DbtSemanticModel" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DbtSemanticModel" + + +class RelatedDbtDimension(RelatedDbt): + """ + Related entity reference for DbtDimension assets. + + Extends RelatedDbt with DbtDimension-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DbtDimension" so it serializes correctly + + dbt_semantic_model_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the dbt semantic model this dimension belongs to.""" + + dbt_semantic_field_time_granularity: Union[str, None, UnsetType] = UNSET + """Time granularity for time dimensions only (day/week/month/quarter/year).""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DbtDimension" + + +class RelatedDbtMeasure(RelatedDbt): + """ + Related entity reference for DbtMeasure assets. + + Extends RelatedDbt with DbtMeasure-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DbtMeasure" so it serializes correctly + + dbt_semantic_model_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the dbt semantic model this measure belongs to.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DbtMeasure" + + +class RelatedDbtEntity(RelatedDbt): + """ + Related entity reference for DbtEntity assets. + + Extends RelatedDbt with DbtEntity-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DbtEntity" so it serializes correctly + + dbt_semantic_model_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the dbt semantic model this entity belongs to.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DbtEntity" + + +class RelatedDbtModel(RelatedDbt): + """ + Related entity reference for DbtModel assets. + + Extends RelatedDbt with DbtModel-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DbtModel" so it serializes correctly + + dbt_status: Union[str, None, UnsetType] = UNSET + """Status of the dbt model.""" + + dbt_error: Union[str, None, UnsetType] = UNSET + """Error message if any for the dbt model.""" + + dbt_raw_sql: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="dbtRawSQL" + ) + """Raw SQL of the dbt model.""" + + dbt_compiled_sql: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="dbtCompiledSQL" + ) + """Compiled SQL of the dbt model.""" + + dbt_stats: Union[str, None, UnsetType] = UNSET + """Statistics of the dbt model.""" + + dbt_materialization_type: Union[str, None, UnsetType] = UNSET + """Type of materialization used for the dbt model.""" + + dbt_model_compile_started_at: Union[int, None, UnsetType] = UNSET + """Timestamp when the dbt model compilation started.""" + + dbt_model_compile_completed_at: Union[int, None, UnsetType] = UNSET + """Timestamp when the dbt model compilation completed.""" + + dbt_model_execute_started_at: Union[int, None, UnsetType] = UNSET + """Timestamp when the dbt model execution started.""" + + dbt_model_execute_completed_at: Union[int, None, UnsetType] = UNSET + """Timestamp when the dbt model execution completed.""" + + dbt_model_execution_time: Union[float, None, UnsetType] = UNSET + """Execution time of the dbt model.""" + + dbt_model_run_generated_at: Union[int, None, UnsetType] = UNSET + """Timestamp when the dbt model run was generated.""" + + dbt_model_run_elapsed_time: Union[float, None, UnsetType] = UNSET + """Elapsed time of the dbt model run.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DbtModel" + + +class RelatedDbtTest(RelatedDbt): + """ + Related entity reference for DbtTest assets. + + Extends RelatedDbt with DbtTest-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DbtTest" so it serializes correctly + + dbt_test_status: Union[str, None, UnsetType] = UNSET + """Details of the results of the test. For errors, it reads "ERROR".""" + + dbt_test_state: Union[str, None, UnsetType] = UNSET + """Test results. Can be one of, in order of severity, "error", "fail", "warn", "pass".""" + + dbt_test_error: Union[str, None, UnsetType] = UNSET + """Error message in the case of state being "error".""" + + dbt_test_raw_sql: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="dbtTestRawSQL" + ) + """Raw SQL of the test.""" + + dbt_test_compiled_sql: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="dbtTestCompiledSQL" + ) + """Compiled SQL of the test.""" + + dbt_test_raw_code: Union[str, None, UnsetType] = UNSET + """Raw code of the test (when the test is defined using Python).""" + + dbt_test_compiled_code: Union[str, None, UnsetType] = UNSET + """Compiled code of the test (when the test is defined using Python).""" + + dbt_test_language: Union[str, None, UnsetType] = UNSET + """Language in which the test is written, for example: SQL or Python.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DbtTest" + + +class RelatedDbtSource(RelatedDbt): + """ + Related entity reference for DbtSource assets. + + Extends RelatedDbt with DbtSource-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DbtSource" so it serializes correctly + + dbt_state: Union[str, None, UnsetType] = UNSET + """State of the dbt source.""" + + dbt_freshness_criteria: Union[str, None, UnsetType] = UNSET + """Freshness criteria for the dbt source.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DbtSource" + + +class RelatedDbtMetric(RelatedDbt): + """ + Related entity reference for DbtMetric assets. + + Extends RelatedDbt with DbtMetric-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DbtMetric" so it serializes correctly + + dbt_metric_filters: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """Filters applied to the dbt metric.""" + + dbt_metric_filter: Union[str, None, UnsetType] = UNSET + """Top-level filter applied to the entire metric query.""" + + dbt_metric_window: Union[str, None, UnsetType] = UNSET + """Time window for cumulative/conversion metrics.""" + + dbt_metric_cumulative_period_agg: Union[str, None, UnsetType] = UNSET + """Aggregation function for cumulative metrics within each period.""" + + dbt_metric_conversion_calculation: Union[str, None, UnsetType] = UNSET + """Calculation type for conversion metrics.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DbtMetric" + + +class RelatedDbtModelColumn(RelatedDbt): + """ + Related entity reference for DbtModelColumn assets. + + Extends RelatedDbt with DbtModelColumn-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DbtModelColumn" so it serializes correctly + + dbt_model_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the dbt model this column belongs to.""" + + dbt_model_column_data_type: Union[str, None, UnsetType] = UNSET + """Data type of the dbt model column.""" + + dbt_model_column_order: Union[int, None, UnsetType] = UNSET + """Order of the column in the dbt model.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DbtModelColumn" + + +class RelatedDbtProcess(RelatedDbt): + """ + Related entity reference for DbtProcess assets. + + Extends RelatedDbt with DbtProcess-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DbtProcess" so it serializes correctly + + dbt_process_job_status: Union[str, None, UnsetType] = UNSET + """Status of the dbt process job.""" + + dbt_upstream_contexts: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """Context for inputs to this Process.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DbtProcess" + + +class RelatedDbtColumnProcess(RelatedDbt): + """ + Related entity reference for DbtColumnProcess assets. + + Extends RelatedDbt with DbtColumnProcess-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DbtColumnProcess" so it serializes correctly + + dbt_column_process_job_status: Union[str, None, UnsetType] = UNSET + """Status of the dbt column process job.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DbtColumnProcess" + + +class RelatedDbtTag(RelatedDbt): + """ + Related entity reference for DbtTag assets. + + Extends RelatedDbt with DbtTag-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DbtTag" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DbtTag" + + +class RelatedDbtSeed(RelatedDbt): + """ + Related entity reference for DbtSeed assets. + + Extends RelatedDbt with DbtSeed-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DbtSeed" so it serializes correctly + + dbt_seed_file_path: Union[str, None, UnsetType] = UNSET + """File path of the dbt seed.""" + + dbt_seed_stats: Union[str, None, UnsetType] = UNSET + """Statistics of the dbt seed.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DbtSeed" diff --git a/pyatlan_v9/model/assets/dbt_seed.py b/pyatlan_v9/model/assets/dbt_seed.py new file mode 100644 index 000000000..fffcf31f6 --- /dev/null +++ b/pyatlan_v9/model/assets/dbt_seed.py @@ -0,0 +1,774 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DbtSeed asset model with flattened inheritance. + +This module provides: +- DbtSeed: Flat asset class (easy to use) +- DbtSeedAttributes: Nested attributes struct (extends AssetAttributes) +- DbtSeedNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from .sql_related import RelatedSQL +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .dbt_related import RelatedDbtModelColumn + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class DbtSeed(Asset): + """ + Instance of a dbt seed in Atlan. + """ + + DBT_SEED_FILE_PATH: ClassVar[Any] = None + DBT_SEED_STATS: ClassVar[Any] = None + DBT_ALIAS: ClassVar[Any] = None + DBT_META: ClassVar[Any] = None + DBT_UNIQUE_ID: ClassVar[Any] = None + DBT_ACCOUNT_NAME: ClassVar[Any] = None + DBT_PROJECT_NAME: ClassVar[Any] = None + DBT_PACKAGE_NAME: ClassVar[Any] = None + DBT_JOB_NAME: ClassVar[Any] = None + DBT_JOB_SCHEDULE: ClassVar[Any] = None + DBT_JOB_STATUS: ClassVar[Any] = None + DBT_JOB_SCHEDULE_CRON_HUMANIZED: ClassVar[Any] = None + DBT_JOB_LAST_RUN: ClassVar[Any] = None + DBT_JOB_NEXT_RUN: ClassVar[Any] = None + DBT_JOB_NEXT_RUN_HUMANIZED: ClassVar[Any] = None + DBT_ENVIRONMENT_NAME: ClassVar[Any] = None + DBT_ENVIRONMENT_DBT_VERSION: ClassVar[Any] = None + DBT_TAGS: ClassVar[Any] = None + DBT_CONNECTION_CONTEXT: ClassVar[Any] = None + DBT_SEMANTIC_LAYER_PROXY_URL: ClassVar[Any] = None + DBT_JOB_RUNS: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DBT_SEED_SQL_ASSETS: ClassVar[Any] = None + DBT_MODEL_COLUMNS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "DbtSeed" + + dbt_seed_file_path: Union[str, None, UnsetType] = UNSET + """File path of the dbt seed.""" + + dbt_seed_stats: Union[str, None, UnsetType] = UNSET + """Statistics of the dbt seed.""" + + dbt_alias: Union[str, None, UnsetType] = UNSET + """Alias of this asset in dbt.""" + + dbt_meta: Union[str, None, UnsetType] = UNSET + """Metadata for this asset in dbt, specifically everything under the 'meta' key in the dbt object.""" + + dbt_unique_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of this asset in dbt.""" + + dbt_account_name: Union[str, None, UnsetType] = UNSET + """Name of the account in which this asset exists in dbt.""" + + dbt_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which this asset exists in dbt.""" + + dbt_package_name: Union[str, None, UnsetType] = UNSET + """Name of the package in which this asset exists in dbt.""" + + dbt_job_name: Union[str, None, UnsetType] = UNSET + """Name of the job that materialized this asset in dbt.""" + + dbt_job_schedule: Union[str, None, UnsetType] = UNSET + """Schedule of the job that materialized this asset in dbt.""" + + dbt_job_status: Union[str, None, UnsetType] = UNSET + """Status of the job that materialized this asset in dbt.""" + + dbt_job_schedule_cron_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable cron schedule of the job that materialized this asset in dbt.""" + + dbt_job_last_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt last ran, in milliseconds.""" + + dbt_job_next_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt will next run, in milliseconds.""" + + dbt_job_next_run_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable time at which the job that materialized this asset in dbt will next run.""" + + dbt_environment_name: Union[str, None, UnsetType] = UNSET + """Name of the environment in which this asset exists in dbt.""" + + dbt_environment_dbt_version: Union[str, None, UnsetType] = UNSET + """Version of dbt used in the environment.""" + + dbt_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset in dbt.""" + + dbt_connection_context: Union[str, None, UnsetType] = UNSET + """Connection context for this asset in dbt.""" + + dbt_semantic_layer_proxy_url: Union[str, None, UnsetType] = UNSET + """URL of the semantic layer proxy for this asset in dbt.""" + + dbt_job_runs: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of latest dbt job runs across all environments.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_seed_sql_assets: Union[List[RelatedSQL], None, UnsetType] = UNSET + """SQL assets from warehouse that are materializations of the dbt seed.""" + + dbt_model_columns: Union[List[RelatedDbtModelColumn], None, UnsetType] = UNSET + """Columns that exist within this dbt seed.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "DbtSeed" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _dbt_seed_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> DbtSeed: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + DbtSeed instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _dbt_seed_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DbtSeedAttributes(AssetAttributes): + """DbtSeed-specific attributes for nested API format.""" + + dbt_seed_file_path: Union[str, None, UnsetType] = UNSET + """File path of the dbt seed.""" + + dbt_seed_stats: Union[str, None, UnsetType] = UNSET + """Statistics of the dbt seed.""" + + dbt_alias: Union[str, None, UnsetType] = UNSET + """Alias of this asset in dbt.""" + + dbt_meta: Union[str, None, UnsetType] = UNSET + """Metadata for this asset in dbt, specifically everything under the 'meta' key in the dbt object.""" + + dbt_unique_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of this asset in dbt.""" + + dbt_account_name: Union[str, None, UnsetType] = UNSET + """Name of the account in which this asset exists in dbt.""" + + dbt_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which this asset exists in dbt.""" + + dbt_package_name: Union[str, None, UnsetType] = UNSET + """Name of the package in which this asset exists in dbt.""" + + dbt_job_name: Union[str, None, UnsetType] = UNSET + """Name of the job that materialized this asset in dbt.""" + + dbt_job_schedule: Union[str, None, UnsetType] = UNSET + """Schedule of the job that materialized this asset in dbt.""" + + dbt_job_status: Union[str, None, UnsetType] = UNSET + """Status of the job that materialized this asset in dbt.""" + + dbt_job_schedule_cron_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable cron schedule of the job that materialized this asset in dbt.""" + + dbt_job_last_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt last ran, in milliseconds.""" + + dbt_job_next_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt will next run, in milliseconds.""" + + dbt_job_next_run_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable time at which the job that materialized this asset in dbt will next run.""" + + dbt_environment_name: Union[str, None, UnsetType] = UNSET + """Name of the environment in which this asset exists in dbt.""" + + dbt_environment_dbt_version: Union[str, None, UnsetType] = UNSET + """Version of dbt used in the environment.""" + + dbt_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset in dbt.""" + + dbt_connection_context: Union[str, None, UnsetType] = UNSET + """Connection context for this asset in dbt.""" + + dbt_semantic_layer_proxy_url: Union[str, None, UnsetType] = UNSET + """URL of the semantic layer proxy for this asset in dbt.""" + + dbt_job_runs: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of latest dbt job runs across all environments.""" + + +class DbtSeedRelationshipAttributes(AssetRelationshipAttributes): + """DbtSeed-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_seed_sql_assets: Union[List[RelatedSQL], None, UnsetType] = UNSET + """SQL assets from warehouse that are materializations of the dbt seed.""" + + dbt_model_columns: Union[List[RelatedDbtModelColumn], None, UnsetType] = UNSET + """Columns that exist within this dbt seed.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DbtSeedNested(AssetNested): + """DbtSeed in nested API format for high-performance serialization.""" + + attributes: Union[DbtSeedAttributes, UnsetType] = UNSET + relationship_attributes: Union[DbtSeedRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[DbtSeedRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[DbtSeedRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DBT_SEED_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "dbt_seed_sql_assets", + "dbt_model_columns", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_dbt_seed_attrs(attrs: DbtSeedAttributes, obj: DbtSeed) -> None: + """Populate DbtSeed-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.dbt_seed_file_path = obj.dbt_seed_file_path + attrs.dbt_seed_stats = obj.dbt_seed_stats + attrs.dbt_alias = obj.dbt_alias + attrs.dbt_meta = obj.dbt_meta + attrs.dbt_unique_id = obj.dbt_unique_id + attrs.dbt_account_name = obj.dbt_account_name + attrs.dbt_project_name = obj.dbt_project_name + attrs.dbt_package_name = obj.dbt_package_name + attrs.dbt_job_name = obj.dbt_job_name + attrs.dbt_job_schedule = obj.dbt_job_schedule + attrs.dbt_job_status = obj.dbt_job_status + attrs.dbt_job_schedule_cron_humanized = obj.dbt_job_schedule_cron_humanized + attrs.dbt_job_last_run = obj.dbt_job_last_run + attrs.dbt_job_next_run = obj.dbt_job_next_run + attrs.dbt_job_next_run_humanized = obj.dbt_job_next_run_humanized + attrs.dbt_environment_name = obj.dbt_environment_name + attrs.dbt_environment_dbt_version = obj.dbt_environment_dbt_version + attrs.dbt_tags = obj.dbt_tags + attrs.dbt_connection_context = obj.dbt_connection_context + attrs.dbt_semantic_layer_proxy_url = obj.dbt_semantic_layer_proxy_url + attrs.dbt_job_runs = obj.dbt_job_runs + + +def _extract_dbt_seed_attrs(attrs: DbtSeedAttributes) -> dict: + """Extract all DbtSeed attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["dbt_seed_file_path"] = attrs.dbt_seed_file_path + result["dbt_seed_stats"] = attrs.dbt_seed_stats + result["dbt_alias"] = attrs.dbt_alias + result["dbt_meta"] = attrs.dbt_meta + result["dbt_unique_id"] = attrs.dbt_unique_id + result["dbt_account_name"] = attrs.dbt_account_name + result["dbt_project_name"] = attrs.dbt_project_name + result["dbt_package_name"] = attrs.dbt_package_name + result["dbt_job_name"] = attrs.dbt_job_name + result["dbt_job_schedule"] = attrs.dbt_job_schedule + result["dbt_job_status"] = attrs.dbt_job_status + result["dbt_job_schedule_cron_humanized"] = attrs.dbt_job_schedule_cron_humanized + result["dbt_job_last_run"] = attrs.dbt_job_last_run + result["dbt_job_next_run"] = attrs.dbt_job_next_run + result["dbt_job_next_run_humanized"] = attrs.dbt_job_next_run_humanized + result["dbt_environment_name"] = attrs.dbt_environment_name + result["dbt_environment_dbt_version"] = attrs.dbt_environment_dbt_version + result["dbt_tags"] = attrs.dbt_tags + result["dbt_connection_context"] = attrs.dbt_connection_context + result["dbt_semantic_layer_proxy_url"] = attrs.dbt_semantic_layer_proxy_url + result["dbt_job_runs"] = attrs.dbt_job_runs + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _dbt_seed_to_nested(dbt_seed: DbtSeed) -> DbtSeedNested: + """Convert flat DbtSeed to nested format.""" + attrs = DbtSeedAttributes() + _populate_dbt_seed_attrs(attrs, dbt_seed) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + dbt_seed, _DBT_SEED_REL_FIELDS, DbtSeedRelationshipAttributes + ) + return DbtSeedNested( + guid=dbt_seed.guid, + type_name=dbt_seed.type_name, + status=dbt_seed.status, + version=dbt_seed.version, + create_time=dbt_seed.create_time, + update_time=dbt_seed.update_time, + created_by=dbt_seed.created_by, + updated_by=dbt_seed.updated_by, + classifications=dbt_seed.classifications, + classification_names=dbt_seed.classification_names, + meanings=dbt_seed.meanings, + labels=dbt_seed.labels, + business_attributes=dbt_seed.business_attributes, + custom_attributes=dbt_seed.custom_attributes, + pending_tasks=dbt_seed.pending_tasks, + proxy=dbt_seed.proxy, + is_incomplete=dbt_seed.is_incomplete, + provenance_type=dbt_seed.provenance_type, + home_id=dbt_seed.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _dbt_seed_from_nested(nested: DbtSeedNested) -> DbtSeed: + """Convert nested format to flat DbtSeed.""" + attrs = nested.attributes if nested.attributes is not UNSET else DbtSeedAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DBT_SEED_REL_FIELDS, + DbtSeedRelationshipAttributes, + ) + return DbtSeed( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_dbt_seed_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _dbt_seed_to_nested_bytes(dbt_seed: DbtSeed, serde: Serde) -> bytes: + """Convert flat DbtSeed to nested JSON bytes.""" + return serde.encode(_dbt_seed_to_nested(dbt_seed)) + + +def _dbt_seed_from_nested_bytes(data: bytes, serde: Serde) -> DbtSeed: + """Convert nested JSON bytes to flat DbtSeed.""" + nested = serde.decode(data, DbtSeedNested) + return _dbt_seed_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +DbtSeed.DBT_SEED_FILE_PATH = KeywordField("dbtSeedFilePath", "dbtSeedFilePath") +DbtSeed.DBT_SEED_STATS = KeywordField("dbtSeedStats", "dbtSeedStats") +DbtSeed.DBT_ALIAS = KeywordField("dbtAlias", "dbtAlias") +DbtSeed.DBT_META = KeywordField("dbtMeta", "dbtMeta") +DbtSeed.DBT_UNIQUE_ID = KeywordField("dbtUniqueId", "dbtUniqueId") +DbtSeed.DBT_ACCOUNT_NAME = KeywordField("dbtAccountName", "dbtAccountName") +DbtSeed.DBT_PROJECT_NAME = KeywordField("dbtProjectName", "dbtProjectName") +DbtSeed.DBT_PACKAGE_NAME = KeywordField("dbtPackageName", "dbtPackageName") +DbtSeed.DBT_JOB_NAME = KeywordField("dbtJobName", "dbtJobName") +DbtSeed.DBT_JOB_SCHEDULE = KeywordField("dbtJobSchedule", "dbtJobSchedule") +DbtSeed.DBT_JOB_STATUS = KeywordField("dbtJobStatus", "dbtJobStatus") +DbtSeed.DBT_JOB_SCHEDULE_CRON_HUMANIZED = KeywordField( + "dbtJobScheduleCronHumanized", "dbtJobScheduleCronHumanized" +) +DbtSeed.DBT_JOB_LAST_RUN = NumericField("dbtJobLastRun", "dbtJobLastRun") +DbtSeed.DBT_JOB_NEXT_RUN = NumericField("dbtJobNextRun", "dbtJobNextRun") +DbtSeed.DBT_JOB_NEXT_RUN_HUMANIZED = KeywordField( + "dbtJobNextRunHumanized", "dbtJobNextRunHumanized" +) +DbtSeed.DBT_ENVIRONMENT_NAME = KeywordField("dbtEnvironmentName", "dbtEnvironmentName") +DbtSeed.DBT_ENVIRONMENT_DBT_VERSION = KeywordField( + "dbtEnvironmentDbtVersion", "dbtEnvironmentDbtVersion" +) +DbtSeed.DBT_TAGS = KeywordField("dbtTags", "dbtTags") +DbtSeed.DBT_CONNECTION_CONTEXT = KeywordField( + "dbtConnectionContext", "dbtConnectionContext" +) +DbtSeed.DBT_SEMANTIC_LAYER_PROXY_URL = KeywordField( + "dbtSemanticLayerProxyUrl", "dbtSemanticLayerProxyUrl" +) +DbtSeed.DBT_JOB_RUNS = KeywordField("dbtJobRuns", "dbtJobRuns") +DbtSeed.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +DbtSeed.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +DbtSeed.ANOMALO_CHECKS = RelationField("anomaloChecks") +DbtSeed.APPLICATION = RelationField("application") +DbtSeed.APPLICATION_FIELD = RelationField("applicationField") +DbtSeed.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +DbtSeed.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +DbtSeed.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +DbtSeed.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +DbtSeed.METRICS = RelationField("metrics") +DbtSeed.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +DbtSeed.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +DbtSeed.DBT_SEED_SQL_ASSETS = RelationField("dbtSeedSqlAssets") +DbtSeed.DBT_MODEL_COLUMNS = RelationField("dbtModelColumns") +DbtSeed.MEANINGS = RelationField("meanings") +DbtSeed.MC_MONITORS = RelationField("mcMonitors") +DbtSeed.MC_INCIDENTS = RelationField("mcIncidents") +DbtSeed.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +DbtSeed.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +DbtSeed.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +DbtSeed.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +DbtSeed.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +DbtSeed.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +DbtSeed.FILES = RelationField("files") +DbtSeed.LINKS = RelationField("links") +DbtSeed.README = RelationField("readme") +DbtSeed.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +DbtSeed.SODA_CHECKS = RelationField("sodaChecks") +DbtSeed.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +DbtSeed.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/dbt_semantic_model.py b/pyatlan_v9/model/assets/dbt_semantic_model.py new file mode 100644 index 000000000..c629ae94d --- /dev/null +++ b/pyatlan_v9/model/assets/dbt_semantic_model.py @@ -0,0 +1,780 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DbtSemanticModel asset model with flattened inheritance. + +This module provides: +- DbtSemanticModel: Flat asset class (easy to use) +- DbtSemanticModelAttributes: Nested attributes struct (extends AssetAttributes) +- DbtSemanticModelNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .semantic_related import ( + RelatedSemanticDimension, + RelatedSemanticEntity, + RelatedSemanticMeasure, +) +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class DbtSemanticModel(Asset): + """ + Instance of a dbt semantic model in Atlan. + """ + + DBT_ALIAS: ClassVar[Any] = None + DBT_META: ClassVar[Any] = None + DBT_UNIQUE_ID: ClassVar[Any] = None + DBT_ACCOUNT_NAME: ClassVar[Any] = None + DBT_PROJECT_NAME: ClassVar[Any] = None + DBT_PACKAGE_NAME: ClassVar[Any] = None + DBT_JOB_NAME: ClassVar[Any] = None + DBT_JOB_SCHEDULE: ClassVar[Any] = None + DBT_JOB_STATUS: ClassVar[Any] = None + DBT_JOB_SCHEDULE_CRON_HUMANIZED: ClassVar[Any] = None + DBT_JOB_LAST_RUN: ClassVar[Any] = None + DBT_JOB_NEXT_RUN: ClassVar[Any] = None + DBT_JOB_NEXT_RUN_HUMANIZED: ClassVar[Any] = None + DBT_ENVIRONMENT_NAME: ClassVar[Any] = None + DBT_ENVIRONMENT_DBT_VERSION: ClassVar[Any] = None + DBT_TAGS: ClassVar[Any] = None + DBT_CONNECTION_CONTEXT: ClassVar[Any] = None + DBT_SEMANTIC_LAYER_PROXY_URL: ClassVar[Any] = None + DBT_JOB_RUNS: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SEMANTIC_DIMENSIONS: ClassVar[Any] = None + SEMANTIC_MEASURES: ClassVar[Any] = None + SEMANTIC_ENTITIES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "DbtSemanticModel" + + dbt_alias: Union[str, None, UnsetType] = UNSET + """Alias of this asset in dbt.""" + + dbt_meta: Union[str, None, UnsetType] = UNSET + """Metadata for this asset in dbt, specifically everything under the 'meta' key in the dbt object.""" + + dbt_unique_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of this asset in dbt.""" + + dbt_account_name: Union[str, None, UnsetType] = UNSET + """Name of the account in which this asset exists in dbt.""" + + dbt_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which this asset exists in dbt.""" + + dbt_package_name: Union[str, None, UnsetType] = UNSET + """Name of the package in which this asset exists in dbt.""" + + dbt_job_name: Union[str, None, UnsetType] = UNSET + """Name of the job that materialized this asset in dbt.""" + + dbt_job_schedule: Union[str, None, UnsetType] = UNSET + """Schedule of the job that materialized this asset in dbt.""" + + dbt_job_status: Union[str, None, UnsetType] = UNSET + """Status of the job that materialized this asset in dbt.""" + + dbt_job_schedule_cron_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable cron schedule of the job that materialized this asset in dbt.""" + + dbt_job_last_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt last ran, in milliseconds.""" + + dbt_job_next_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt will next run, in milliseconds.""" + + dbt_job_next_run_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable time at which the job that materialized this asset in dbt will next run.""" + + dbt_environment_name: Union[str, None, UnsetType] = UNSET + """Name of the environment in which this asset exists in dbt.""" + + dbt_environment_dbt_version: Union[str, None, UnsetType] = UNSET + """Version of dbt used in the environment.""" + + dbt_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset in dbt.""" + + dbt_connection_context: Union[str, None, UnsetType] = UNSET + """Connection context for this asset in dbt.""" + + dbt_semantic_layer_proxy_url: Union[str, None, UnsetType] = UNSET + """URL of the semantic layer proxy for this asset in dbt.""" + + dbt_job_runs: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of latest dbt job runs across all environments.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + semantic_dimensions: Union[List[RelatedSemanticDimension], None, UnsetType] = UNSET + """Dimensions that exist within this semantic model.""" + + semantic_measures: Union[List[RelatedSemanticMeasure], None, UnsetType] = UNSET + """Measures that exist within this semantic model.""" + + semantic_entities: Union[List[RelatedSemanticEntity], None, UnsetType] = UNSET + """Entities that exist within this semantic model.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "DbtSemanticModel" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _dbt_semantic_model_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> DbtSemanticModel: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + DbtSemanticModel instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _dbt_semantic_model_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DbtSemanticModelAttributes(AssetAttributes): + """DbtSemanticModel-specific attributes for nested API format.""" + + dbt_alias: Union[str, None, UnsetType] = UNSET + """Alias of this asset in dbt.""" + + dbt_meta: Union[str, None, UnsetType] = UNSET + """Metadata for this asset in dbt, specifically everything under the 'meta' key in the dbt object.""" + + dbt_unique_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of this asset in dbt.""" + + dbt_account_name: Union[str, None, UnsetType] = UNSET + """Name of the account in which this asset exists in dbt.""" + + dbt_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which this asset exists in dbt.""" + + dbt_package_name: Union[str, None, UnsetType] = UNSET + """Name of the package in which this asset exists in dbt.""" + + dbt_job_name: Union[str, None, UnsetType] = UNSET + """Name of the job that materialized this asset in dbt.""" + + dbt_job_schedule: Union[str, None, UnsetType] = UNSET + """Schedule of the job that materialized this asset in dbt.""" + + dbt_job_status: Union[str, None, UnsetType] = UNSET + """Status of the job that materialized this asset in dbt.""" + + dbt_job_schedule_cron_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable cron schedule of the job that materialized this asset in dbt.""" + + dbt_job_last_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt last ran, in milliseconds.""" + + dbt_job_next_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt will next run, in milliseconds.""" + + dbt_job_next_run_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable time at which the job that materialized this asset in dbt will next run.""" + + dbt_environment_name: Union[str, None, UnsetType] = UNSET + """Name of the environment in which this asset exists in dbt.""" + + dbt_environment_dbt_version: Union[str, None, UnsetType] = UNSET + """Version of dbt used in the environment.""" + + dbt_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset in dbt.""" + + dbt_connection_context: Union[str, None, UnsetType] = UNSET + """Connection context for this asset in dbt.""" + + dbt_semantic_layer_proxy_url: Union[str, None, UnsetType] = UNSET + """URL of the semantic layer proxy for this asset in dbt.""" + + dbt_job_runs: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of latest dbt job runs across all environments.""" + + +class DbtSemanticModelRelationshipAttributes(AssetRelationshipAttributes): + """DbtSemanticModel-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + semantic_dimensions: Union[List[RelatedSemanticDimension], None, UnsetType] = UNSET + """Dimensions that exist within this semantic model.""" + + semantic_measures: Union[List[RelatedSemanticMeasure], None, UnsetType] = UNSET + """Measures that exist within this semantic model.""" + + semantic_entities: Union[List[RelatedSemanticEntity], None, UnsetType] = UNSET + """Entities that exist within this semantic model.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DbtSemanticModelNested(AssetNested): + """DbtSemanticModel in nested API format for high-performance serialization.""" + + attributes: Union[DbtSemanticModelAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + DbtSemanticModelRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + DbtSemanticModelRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + DbtSemanticModelRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DBT_SEMANTIC_MODEL_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "semantic_dimensions", + "semantic_measures", + "semantic_entities", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_dbt_semantic_model_attrs( + attrs: DbtSemanticModelAttributes, obj: DbtSemanticModel +) -> None: + """Populate DbtSemanticModel-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.dbt_alias = obj.dbt_alias + attrs.dbt_meta = obj.dbt_meta + attrs.dbt_unique_id = obj.dbt_unique_id + attrs.dbt_account_name = obj.dbt_account_name + attrs.dbt_project_name = obj.dbt_project_name + attrs.dbt_package_name = obj.dbt_package_name + attrs.dbt_job_name = obj.dbt_job_name + attrs.dbt_job_schedule = obj.dbt_job_schedule + attrs.dbt_job_status = obj.dbt_job_status + attrs.dbt_job_schedule_cron_humanized = obj.dbt_job_schedule_cron_humanized + attrs.dbt_job_last_run = obj.dbt_job_last_run + attrs.dbt_job_next_run = obj.dbt_job_next_run + attrs.dbt_job_next_run_humanized = obj.dbt_job_next_run_humanized + attrs.dbt_environment_name = obj.dbt_environment_name + attrs.dbt_environment_dbt_version = obj.dbt_environment_dbt_version + attrs.dbt_tags = obj.dbt_tags + attrs.dbt_connection_context = obj.dbt_connection_context + attrs.dbt_semantic_layer_proxy_url = obj.dbt_semantic_layer_proxy_url + attrs.dbt_job_runs = obj.dbt_job_runs + + +def _extract_dbt_semantic_model_attrs(attrs: DbtSemanticModelAttributes) -> dict: + """Extract all DbtSemanticModel attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["dbt_alias"] = attrs.dbt_alias + result["dbt_meta"] = attrs.dbt_meta + result["dbt_unique_id"] = attrs.dbt_unique_id + result["dbt_account_name"] = attrs.dbt_account_name + result["dbt_project_name"] = attrs.dbt_project_name + result["dbt_package_name"] = attrs.dbt_package_name + result["dbt_job_name"] = attrs.dbt_job_name + result["dbt_job_schedule"] = attrs.dbt_job_schedule + result["dbt_job_status"] = attrs.dbt_job_status + result["dbt_job_schedule_cron_humanized"] = attrs.dbt_job_schedule_cron_humanized + result["dbt_job_last_run"] = attrs.dbt_job_last_run + result["dbt_job_next_run"] = attrs.dbt_job_next_run + result["dbt_job_next_run_humanized"] = attrs.dbt_job_next_run_humanized + result["dbt_environment_name"] = attrs.dbt_environment_name + result["dbt_environment_dbt_version"] = attrs.dbt_environment_dbt_version + result["dbt_tags"] = attrs.dbt_tags + result["dbt_connection_context"] = attrs.dbt_connection_context + result["dbt_semantic_layer_proxy_url"] = attrs.dbt_semantic_layer_proxy_url + result["dbt_job_runs"] = attrs.dbt_job_runs + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _dbt_semantic_model_to_nested( + dbt_semantic_model: DbtSemanticModel, +) -> DbtSemanticModelNested: + """Convert flat DbtSemanticModel to nested format.""" + attrs = DbtSemanticModelAttributes() + _populate_dbt_semantic_model_attrs(attrs, dbt_semantic_model) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + dbt_semantic_model, + _DBT_SEMANTIC_MODEL_REL_FIELDS, + DbtSemanticModelRelationshipAttributes, + ) + return DbtSemanticModelNested( + guid=dbt_semantic_model.guid, + type_name=dbt_semantic_model.type_name, + status=dbt_semantic_model.status, + version=dbt_semantic_model.version, + create_time=dbt_semantic_model.create_time, + update_time=dbt_semantic_model.update_time, + created_by=dbt_semantic_model.created_by, + updated_by=dbt_semantic_model.updated_by, + classifications=dbt_semantic_model.classifications, + classification_names=dbt_semantic_model.classification_names, + meanings=dbt_semantic_model.meanings, + labels=dbt_semantic_model.labels, + business_attributes=dbt_semantic_model.business_attributes, + custom_attributes=dbt_semantic_model.custom_attributes, + pending_tasks=dbt_semantic_model.pending_tasks, + proxy=dbt_semantic_model.proxy, + is_incomplete=dbt_semantic_model.is_incomplete, + provenance_type=dbt_semantic_model.provenance_type, + home_id=dbt_semantic_model.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _dbt_semantic_model_from_nested(nested: DbtSemanticModelNested) -> DbtSemanticModel: + """Convert nested format to flat DbtSemanticModel.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else DbtSemanticModelAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DBT_SEMANTIC_MODEL_REL_FIELDS, + DbtSemanticModelRelationshipAttributes, + ) + return DbtSemanticModel( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_dbt_semantic_model_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _dbt_semantic_model_to_nested_bytes( + dbt_semantic_model: DbtSemanticModel, serde: Serde +) -> bytes: + """Convert flat DbtSemanticModel to nested JSON bytes.""" + return serde.encode(_dbt_semantic_model_to_nested(dbt_semantic_model)) + + +def _dbt_semantic_model_from_nested_bytes( + data: bytes, serde: Serde +) -> DbtSemanticModel: + """Convert nested JSON bytes to flat DbtSemanticModel.""" + nested = serde.decode(data, DbtSemanticModelNested) + return _dbt_semantic_model_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +DbtSemanticModel.DBT_ALIAS = KeywordField("dbtAlias", "dbtAlias") +DbtSemanticModel.DBT_META = KeywordField("dbtMeta", "dbtMeta") +DbtSemanticModel.DBT_UNIQUE_ID = KeywordField("dbtUniqueId", "dbtUniqueId") +DbtSemanticModel.DBT_ACCOUNT_NAME = KeywordField("dbtAccountName", "dbtAccountName") +DbtSemanticModel.DBT_PROJECT_NAME = KeywordField("dbtProjectName", "dbtProjectName") +DbtSemanticModel.DBT_PACKAGE_NAME = KeywordField("dbtPackageName", "dbtPackageName") +DbtSemanticModel.DBT_JOB_NAME = KeywordField("dbtJobName", "dbtJobName") +DbtSemanticModel.DBT_JOB_SCHEDULE = KeywordField("dbtJobSchedule", "dbtJobSchedule") +DbtSemanticModel.DBT_JOB_STATUS = KeywordField("dbtJobStatus", "dbtJobStatus") +DbtSemanticModel.DBT_JOB_SCHEDULE_CRON_HUMANIZED = KeywordField( + "dbtJobScheduleCronHumanized", "dbtJobScheduleCronHumanized" +) +DbtSemanticModel.DBT_JOB_LAST_RUN = NumericField("dbtJobLastRun", "dbtJobLastRun") +DbtSemanticModel.DBT_JOB_NEXT_RUN = NumericField("dbtJobNextRun", "dbtJobNextRun") +DbtSemanticModel.DBT_JOB_NEXT_RUN_HUMANIZED = KeywordField( + "dbtJobNextRunHumanized", "dbtJobNextRunHumanized" +) +DbtSemanticModel.DBT_ENVIRONMENT_NAME = KeywordField( + "dbtEnvironmentName", "dbtEnvironmentName" +) +DbtSemanticModel.DBT_ENVIRONMENT_DBT_VERSION = KeywordField( + "dbtEnvironmentDbtVersion", "dbtEnvironmentDbtVersion" +) +DbtSemanticModel.DBT_TAGS = KeywordField("dbtTags", "dbtTags") +DbtSemanticModel.DBT_CONNECTION_CONTEXT = KeywordField( + "dbtConnectionContext", "dbtConnectionContext" +) +DbtSemanticModel.DBT_SEMANTIC_LAYER_PROXY_URL = KeywordField( + "dbtSemanticLayerProxyUrl", "dbtSemanticLayerProxyUrl" +) +DbtSemanticModel.DBT_JOB_RUNS = KeywordField("dbtJobRuns", "dbtJobRuns") +DbtSemanticModel.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +DbtSemanticModel.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +DbtSemanticModel.ANOMALO_CHECKS = RelationField("anomaloChecks") +DbtSemanticModel.APPLICATION = RelationField("application") +DbtSemanticModel.APPLICATION_FIELD = RelationField("applicationField") +DbtSemanticModel.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +DbtSemanticModel.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +DbtSemanticModel.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +DbtSemanticModel.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +DbtSemanticModel.METRICS = RelationField("metrics") +DbtSemanticModel.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +DbtSemanticModel.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +DbtSemanticModel.MEANINGS = RelationField("meanings") +DbtSemanticModel.MC_MONITORS = RelationField("mcMonitors") +DbtSemanticModel.MC_INCIDENTS = RelationField("mcIncidents") +DbtSemanticModel.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +DbtSemanticModel.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +DbtSemanticModel.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +DbtSemanticModel.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +DbtSemanticModel.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +DbtSemanticModel.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +DbtSemanticModel.FILES = RelationField("files") +DbtSemanticModel.LINKS = RelationField("links") +DbtSemanticModel.README = RelationField("readme") +DbtSemanticModel.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +DbtSemanticModel.SEMANTIC_DIMENSIONS = RelationField("semanticDimensions") +DbtSemanticModel.SEMANTIC_MEASURES = RelationField("semanticMeasures") +DbtSemanticModel.SEMANTIC_ENTITIES = RelationField("semanticEntities") +DbtSemanticModel.SODA_CHECKS = RelationField("sodaChecks") +DbtSemanticModel.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +DbtSemanticModel.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/dbt_source.py b/pyatlan_v9/model/assets/dbt_source.py new file mode 100644 index 000000000..150a72935 --- /dev/null +++ b/pyatlan_v9/model/assets/dbt_source.py @@ -0,0 +1,789 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DbtSource asset model with flattened inheritance. + +This module provides: +- DbtSource: Flat asset class (easy to use) +- DbtSourceAttributes: Nested attributes struct (extends AssetAttributes) +- DbtSourceNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from .sql_related import RelatedSQL +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .dbt_related import RelatedDbtTest + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class DbtSource(Asset): + """ + Instance of a dbt source asset in Atlan. + """ + + DBT_STATE: ClassVar[Any] = None + DBT_FRESHNESS_CRITERIA: ClassVar[Any] = None + DBT_ALIAS: ClassVar[Any] = None + DBT_META: ClassVar[Any] = None + DBT_UNIQUE_ID: ClassVar[Any] = None + DBT_ACCOUNT_NAME: ClassVar[Any] = None + DBT_PROJECT_NAME: ClassVar[Any] = None + DBT_PACKAGE_NAME: ClassVar[Any] = None + DBT_JOB_NAME: ClassVar[Any] = None + DBT_JOB_SCHEDULE: ClassVar[Any] = None + DBT_JOB_STATUS: ClassVar[Any] = None + DBT_JOB_SCHEDULE_CRON_HUMANIZED: ClassVar[Any] = None + DBT_JOB_LAST_RUN: ClassVar[Any] = None + DBT_JOB_NEXT_RUN: ClassVar[Any] = None + DBT_JOB_NEXT_RUN_HUMANIZED: ClassVar[Any] = None + DBT_ENVIRONMENT_NAME: ClassVar[Any] = None + DBT_ENVIRONMENT_DBT_VERSION: ClassVar[Any] = None + DBT_TAGS: ClassVar[Any] = None + DBT_CONNECTION_CONTEXT: ClassVar[Any] = None + DBT_SEMANTIC_LAYER_PROXY_URL: ClassVar[Any] = None + DBT_JOB_RUNS: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + SQL_ASSET: ClassVar[Any] = None + SQL_ASSETS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "DbtSource" + + dbt_state: Union[str, None, UnsetType] = UNSET + """State of the dbt source.""" + + dbt_freshness_criteria: Union[str, None, UnsetType] = UNSET + """Freshness criteria for the dbt source.""" + + dbt_alias: Union[str, None, UnsetType] = UNSET + """Alias of this asset in dbt.""" + + dbt_meta: Union[str, None, UnsetType] = UNSET + """Metadata for this asset in dbt, specifically everything under the 'meta' key in the dbt object.""" + + dbt_unique_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of this asset in dbt.""" + + dbt_account_name: Union[str, None, UnsetType] = UNSET + """Name of the account in which this asset exists in dbt.""" + + dbt_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which this asset exists in dbt.""" + + dbt_package_name: Union[str, None, UnsetType] = UNSET + """Name of the package in which this asset exists in dbt.""" + + dbt_job_name: Union[str, None, UnsetType] = UNSET + """Name of the job that materialized this asset in dbt.""" + + dbt_job_schedule: Union[str, None, UnsetType] = UNSET + """Schedule of the job that materialized this asset in dbt.""" + + dbt_job_status: Union[str, None, UnsetType] = UNSET + """Status of the job that materialized this asset in dbt.""" + + dbt_job_schedule_cron_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable cron schedule of the job that materialized this asset in dbt.""" + + dbt_job_last_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt last ran, in milliseconds.""" + + dbt_job_next_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt will next run, in milliseconds.""" + + dbt_job_next_run_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable time at which the job that materialized this asset in dbt will next run.""" + + dbt_environment_name: Union[str, None, UnsetType] = UNSET + """Name of the environment in which this asset exists in dbt.""" + + dbt_environment_dbt_version: Union[str, None, UnsetType] = UNSET + """Version of dbt used in the environment.""" + + dbt_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset in dbt.""" + + dbt_connection_context: Union[str, None, UnsetType] = UNSET + """Connection context for this asset in dbt.""" + + dbt_semantic_layer_proxy_url: Union[str, None, UnsetType] = UNSET + """URL of the semantic layer proxy for this asset in dbt.""" + + dbt_job_runs: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of latest dbt job runs across all environments.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this source.""" + + sql_asset: Union[RelatedSQL, None, UnsetType] = UNSET + """Assets related to this source.""" + + sql_assets: Union[List[RelatedSQL], None, UnsetType] = UNSET + """Assets related to this source.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "DbtSource" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _dbt_source_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> DbtSource: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + DbtSource instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _dbt_source_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DbtSourceAttributes(AssetAttributes): + """DbtSource-specific attributes for nested API format.""" + + dbt_state: Union[str, None, UnsetType] = UNSET + """State of the dbt source.""" + + dbt_freshness_criteria: Union[str, None, UnsetType] = UNSET + """Freshness criteria for the dbt source.""" + + dbt_alias: Union[str, None, UnsetType] = UNSET + """Alias of this asset in dbt.""" + + dbt_meta: Union[str, None, UnsetType] = UNSET + """Metadata for this asset in dbt, specifically everything under the 'meta' key in the dbt object.""" + + dbt_unique_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of this asset in dbt.""" + + dbt_account_name: Union[str, None, UnsetType] = UNSET + """Name of the account in which this asset exists in dbt.""" + + dbt_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which this asset exists in dbt.""" + + dbt_package_name: Union[str, None, UnsetType] = UNSET + """Name of the package in which this asset exists in dbt.""" + + dbt_job_name: Union[str, None, UnsetType] = UNSET + """Name of the job that materialized this asset in dbt.""" + + dbt_job_schedule: Union[str, None, UnsetType] = UNSET + """Schedule of the job that materialized this asset in dbt.""" + + dbt_job_status: Union[str, None, UnsetType] = UNSET + """Status of the job that materialized this asset in dbt.""" + + dbt_job_schedule_cron_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable cron schedule of the job that materialized this asset in dbt.""" + + dbt_job_last_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt last ran, in milliseconds.""" + + dbt_job_next_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt will next run, in milliseconds.""" + + dbt_job_next_run_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable time at which the job that materialized this asset in dbt will next run.""" + + dbt_environment_name: Union[str, None, UnsetType] = UNSET + """Name of the environment in which this asset exists in dbt.""" + + dbt_environment_dbt_version: Union[str, None, UnsetType] = UNSET + """Version of dbt used in the environment.""" + + dbt_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset in dbt.""" + + dbt_connection_context: Union[str, None, UnsetType] = UNSET + """Connection context for this asset in dbt.""" + + dbt_semantic_layer_proxy_url: Union[str, None, UnsetType] = UNSET + """URL of the semantic layer proxy for this asset in dbt.""" + + dbt_job_runs: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of latest dbt job runs across all environments.""" + + +class DbtSourceRelationshipAttributes(AssetRelationshipAttributes): + """DbtSource-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this source.""" + + sql_asset: Union[RelatedSQL, None, UnsetType] = UNSET + """Assets related to this source.""" + + sql_assets: Union[List[RelatedSQL], None, UnsetType] = UNSET + """Assets related to this source.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DbtSourceNested(AssetNested): + """DbtSource in nested API format for high-performance serialization.""" + + attributes: Union[DbtSourceAttributes, UnsetType] = UNSET + relationship_attributes: Union[DbtSourceRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + DbtSourceRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + DbtSourceRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DBT_SOURCE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "dbt_tests", + "sql_asset", + "sql_assets", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_dbt_source_attrs(attrs: DbtSourceAttributes, obj: DbtSource) -> None: + """Populate DbtSource-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.dbt_state = obj.dbt_state + attrs.dbt_freshness_criteria = obj.dbt_freshness_criteria + attrs.dbt_alias = obj.dbt_alias + attrs.dbt_meta = obj.dbt_meta + attrs.dbt_unique_id = obj.dbt_unique_id + attrs.dbt_account_name = obj.dbt_account_name + attrs.dbt_project_name = obj.dbt_project_name + attrs.dbt_package_name = obj.dbt_package_name + attrs.dbt_job_name = obj.dbt_job_name + attrs.dbt_job_schedule = obj.dbt_job_schedule + attrs.dbt_job_status = obj.dbt_job_status + attrs.dbt_job_schedule_cron_humanized = obj.dbt_job_schedule_cron_humanized + attrs.dbt_job_last_run = obj.dbt_job_last_run + attrs.dbt_job_next_run = obj.dbt_job_next_run + attrs.dbt_job_next_run_humanized = obj.dbt_job_next_run_humanized + attrs.dbt_environment_name = obj.dbt_environment_name + attrs.dbt_environment_dbt_version = obj.dbt_environment_dbt_version + attrs.dbt_tags = obj.dbt_tags + attrs.dbt_connection_context = obj.dbt_connection_context + attrs.dbt_semantic_layer_proxy_url = obj.dbt_semantic_layer_proxy_url + attrs.dbt_job_runs = obj.dbt_job_runs + + +def _extract_dbt_source_attrs(attrs: DbtSourceAttributes) -> dict: + """Extract all DbtSource attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["dbt_state"] = attrs.dbt_state + result["dbt_freshness_criteria"] = attrs.dbt_freshness_criteria + result["dbt_alias"] = attrs.dbt_alias + result["dbt_meta"] = attrs.dbt_meta + result["dbt_unique_id"] = attrs.dbt_unique_id + result["dbt_account_name"] = attrs.dbt_account_name + result["dbt_project_name"] = attrs.dbt_project_name + result["dbt_package_name"] = attrs.dbt_package_name + result["dbt_job_name"] = attrs.dbt_job_name + result["dbt_job_schedule"] = attrs.dbt_job_schedule + result["dbt_job_status"] = attrs.dbt_job_status + result["dbt_job_schedule_cron_humanized"] = attrs.dbt_job_schedule_cron_humanized + result["dbt_job_last_run"] = attrs.dbt_job_last_run + result["dbt_job_next_run"] = attrs.dbt_job_next_run + result["dbt_job_next_run_humanized"] = attrs.dbt_job_next_run_humanized + result["dbt_environment_name"] = attrs.dbt_environment_name + result["dbt_environment_dbt_version"] = attrs.dbt_environment_dbt_version + result["dbt_tags"] = attrs.dbt_tags + result["dbt_connection_context"] = attrs.dbt_connection_context + result["dbt_semantic_layer_proxy_url"] = attrs.dbt_semantic_layer_proxy_url + result["dbt_job_runs"] = attrs.dbt_job_runs + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _dbt_source_to_nested(dbt_source: DbtSource) -> DbtSourceNested: + """Convert flat DbtSource to nested format.""" + attrs = DbtSourceAttributes() + _populate_dbt_source_attrs(attrs, dbt_source) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + dbt_source, _DBT_SOURCE_REL_FIELDS, DbtSourceRelationshipAttributes + ) + return DbtSourceNested( + guid=dbt_source.guid, + type_name=dbt_source.type_name, + status=dbt_source.status, + version=dbt_source.version, + create_time=dbt_source.create_time, + update_time=dbt_source.update_time, + created_by=dbt_source.created_by, + updated_by=dbt_source.updated_by, + classifications=dbt_source.classifications, + classification_names=dbt_source.classification_names, + meanings=dbt_source.meanings, + labels=dbt_source.labels, + business_attributes=dbt_source.business_attributes, + custom_attributes=dbt_source.custom_attributes, + pending_tasks=dbt_source.pending_tasks, + proxy=dbt_source.proxy, + is_incomplete=dbt_source.is_incomplete, + provenance_type=dbt_source.provenance_type, + home_id=dbt_source.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _dbt_source_from_nested(nested: DbtSourceNested) -> DbtSource: + """Convert nested format to flat DbtSource.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else DbtSourceAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DBT_SOURCE_REL_FIELDS, + DbtSourceRelationshipAttributes, + ) + return DbtSource( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_dbt_source_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _dbt_source_to_nested_bytes(dbt_source: DbtSource, serde: Serde) -> bytes: + """Convert flat DbtSource to nested JSON bytes.""" + return serde.encode(_dbt_source_to_nested(dbt_source)) + + +def _dbt_source_from_nested_bytes(data: bytes, serde: Serde) -> DbtSource: + """Convert nested JSON bytes to flat DbtSource.""" + nested = serde.decode(data, DbtSourceNested) + return _dbt_source_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +DbtSource.DBT_STATE = KeywordField("dbtState", "dbtState") +DbtSource.DBT_FRESHNESS_CRITERIA = KeywordField( + "dbtFreshnessCriteria", "dbtFreshnessCriteria" +) +DbtSource.DBT_ALIAS = KeywordField("dbtAlias", "dbtAlias") +DbtSource.DBT_META = KeywordField("dbtMeta", "dbtMeta") +DbtSource.DBT_UNIQUE_ID = KeywordField("dbtUniqueId", "dbtUniqueId") +DbtSource.DBT_ACCOUNT_NAME = KeywordField("dbtAccountName", "dbtAccountName") +DbtSource.DBT_PROJECT_NAME = KeywordField("dbtProjectName", "dbtProjectName") +DbtSource.DBT_PACKAGE_NAME = KeywordField("dbtPackageName", "dbtPackageName") +DbtSource.DBT_JOB_NAME = KeywordField("dbtJobName", "dbtJobName") +DbtSource.DBT_JOB_SCHEDULE = KeywordField("dbtJobSchedule", "dbtJobSchedule") +DbtSource.DBT_JOB_STATUS = KeywordField("dbtJobStatus", "dbtJobStatus") +DbtSource.DBT_JOB_SCHEDULE_CRON_HUMANIZED = KeywordField( + "dbtJobScheduleCronHumanized", "dbtJobScheduleCronHumanized" +) +DbtSource.DBT_JOB_LAST_RUN = NumericField("dbtJobLastRun", "dbtJobLastRun") +DbtSource.DBT_JOB_NEXT_RUN = NumericField("dbtJobNextRun", "dbtJobNextRun") +DbtSource.DBT_JOB_NEXT_RUN_HUMANIZED = KeywordField( + "dbtJobNextRunHumanized", "dbtJobNextRunHumanized" +) +DbtSource.DBT_ENVIRONMENT_NAME = KeywordField( + "dbtEnvironmentName", "dbtEnvironmentName" +) +DbtSource.DBT_ENVIRONMENT_DBT_VERSION = KeywordField( + "dbtEnvironmentDbtVersion", "dbtEnvironmentDbtVersion" +) +DbtSource.DBT_TAGS = KeywordField("dbtTags", "dbtTags") +DbtSource.DBT_CONNECTION_CONTEXT = KeywordField( + "dbtConnectionContext", "dbtConnectionContext" +) +DbtSource.DBT_SEMANTIC_LAYER_PROXY_URL = KeywordField( + "dbtSemanticLayerProxyUrl", "dbtSemanticLayerProxyUrl" +) +DbtSource.DBT_JOB_RUNS = KeywordField("dbtJobRuns", "dbtJobRuns") +DbtSource.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +DbtSource.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +DbtSource.ANOMALO_CHECKS = RelationField("anomaloChecks") +DbtSource.APPLICATION = RelationField("application") +DbtSource.APPLICATION_FIELD = RelationField("applicationField") +DbtSource.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +DbtSource.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +DbtSource.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +DbtSource.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +DbtSource.METRICS = RelationField("metrics") +DbtSource.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +DbtSource.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +DbtSource.DBT_TESTS = RelationField("dbtTests") +DbtSource.SQL_ASSET = RelationField("sqlAsset") +DbtSource.SQL_ASSETS = RelationField("sqlAssets") +DbtSource.MEANINGS = RelationField("meanings") +DbtSource.MC_MONITORS = RelationField("mcMonitors") +DbtSource.MC_INCIDENTS = RelationField("mcIncidents") +DbtSource.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +DbtSource.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +DbtSource.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +DbtSource.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +DbtSource.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +DbtSource.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +DbtSource.FILES = RelationField("files") +DbtSource.LINKS = RelationField("links") +DbtSource.README = RelationField("readme") +DbtSource.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +DbtSource.SODA_CHECKS = RelationField("sodaChecks") +DbtSource.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +DbtSource.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/dbt_tag.py b/pyatlan_v9/model/assets/dbt_tag.py new file mode 100644 index 000000000..94e48f4cb --- /dev/null +++ b/pyatlan_v9/model/assets/dbt_tag.py @@ -0,0 +1,780 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DbtTag asset model with flattened inheritance. + +This module provides: +- DbtTag: Flat asset class (easy to use) +- DbtTagAttributes: Nested attributes struct (extends AssetAttributes) +- DbtTagNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class DbtTag(Asset): + """ + Instance of a dbt tag in Atlan. + """ + + DBT_ALIAS: ClassVar[Any] = None + DBT_META: ClassVar[Any] = None + DBT_UNIQUE_ID: ClassVar[Any] = None + DBT_ACCOUNT_NAME: ClassVar[Any] = None + DBT_PROJECT_NAME: ClassVar[Any] = None + DBT_PACKAGE_NAME: ClassVar[Any] = None + DBT_JOB_NAME: ClassVar[Any] = None + DBT_JOB_SCHEDULE: ClassVar[Any] = None + DBT_JOB_STATUS: ClassVar[Any] = None + DBT_JOB_SCHEDULE_CRON_HUMANIZED: ClassVar[Any] = None + DBT_JOB_LAST_RUN: ClassVar[Any] = None + DBT_JOB_NEXT_RUN: ClassVar[Any] = None + DBT_JOB_NEXT_RUN_HUMANIZED: ClassVar[Any] = None + DBT_ENVIRONMENT_NAME: ClassVar[Any] = None + DBT_ENVIRONMENT_DBT_VERSION: ClassVar[Any] = None + DBT_TAGS: ClassVar[Any] = None + DBT_CONNECTION_CONTEXT: ClassVar[Any] = None + DBT_SEMANTIC_LAYER_PROXY_URL: ClassVar[Any] = None + DBT_JOB_RUNS: ClassVar[Any] = None + TAG_ID: ClassVar[Any] = None + TAG_ATTRIBUTES: ClassVar[Any] = None + TAG_ALLOWED_VALUES: ClassVar[Any] = None + MAPPED_CLASSIFICATION_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "DbtTag" + + dbt_alias: Union[str, None, UnsetType] = UNSET + """Alias of this asset in dbt.""" + + dbt_meta: Union[str, None, UnsetType] = UNSET + """Metadata for this asset in dbt, specifically everything under the 'meta' key in the dbt object.""" + + dbt_unique_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of this asset in dbt.""" + + dbt_account_name: Union[str, None, UnsetType] = UNSET + """Name of the account in which this asset exists in dbt.""" + + dbt_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which this asset exists in dbt.""" + + dbt_package_name: Union[str, None, UnsetType] = UNSET + """Name of the package in which this asset exists in dbt.""" + + dbt_job_name: Union[str, None, UnsetType] = UNSET + """Name of the job that materialized this asset in dbt.""" + + dbt_job_schedule: Union[str, None, UnsetType] = UNSET + """Schedule of the job that materialized this asset in dbt.""" + + dbt_job_status: Union[str, None, UnsetType] = UNSET + """Status of the job that materialized this asset in dbt.""" + + dbt_job_schedule_cron_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable cron schedule of the job that materialized this asset in dbt.""" + + dbt_job_last_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt last ran, in milliseconds.""" + + dbt_job_next_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt will next run, in milliseconds.""" + + dbt_job_next_run_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable time at which the job that materialized this asset in dbt will next run.""" + + dbt_environment_name: Union[str, None, UnsetType] = UNSET + """Name of the environment in which this asset exists in dbt.""" + + dbt_environment_dbt_version: Union[str, None, UnsetType] = UNSET + """Version of dbt used in the environment.""" + + dbt_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset in dbt.""" + + dbt_connection_context: Union[str, None, UnsetType] = UNSET + """Connection context for this asset in dbt.""" + + dbt_semantic_layer_proxy_url: Union[str, None, UnsetType] = UNSET + """URL of the semantic layer proxy for this asset in dbt.""" + + dbt_job_runs: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of latest dbt job runs across all environments.""" + + tag_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the tag in the source system.""" + + tag_attributes: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """Attributes associated with the tag in the source system.""" + + tag_allowed_values: Union[List[str], None, UnsetType] = UNSET + """Allowed values for the tag in the source system. These are denormalized from tagAttributes for ease of querying.""" + + mapped_classification_name: Union[str, None, UnsetType] = UNSET + """Name of the classification in Atlan that is mapped to this tag.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "DbtTag" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/account/[^/]+/project/[^/]+/tag/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _dbt_tag_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> DbtTag: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + DbtTag instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _dbt_tag_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DbtTagAttributes(AssetAttributes): + """DbtTag-specific attributes for nested API format.""" + + dbt_alias: Union[str, None, UnsetType] = UNSET + """Alias of this asset in dbt.""" + + dbt_meta: Union[str, None, UnsetType] = UNSET + """Metadata for this asset in dbt, specifically everything under the 'meta' key in the dbt object.""" + + dbt_unique_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of this asset in dbt.""" + + dbt_account_name: Union[str, None, UnsetType] = UNSET + """Name of the account in which this asset exists in dbt.""" + + dbt_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which this asset exists in dbt.""" + + dbt_package_name: Union[str, None, UnsetType] = UNSET + """Name of the package in which this asset exists in dbt.""" + + dbt_job_name: Union[str, None, UnsetType] = UNSET + """Name of the job that materialized this asset in dbt.""" + + dbt_job_schedule: Union[str, None, UnsetType] = UNSET + """Schedule of the job that materialized this asset in dbt.""" + + dbt_job_status: Union[str, None, UnsetType] = UNSET + """Status of the job that materialized this asset in dbt.""" + + dbt_job_schedule_cron_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable cron schedule of the job that materialized this asset in dbt.""" + + dbt_job_last_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt last ran, in milliseconds.""" + + dbt_job_next_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt will next run, in milliseconds.""" + + dbt_job_next_run_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable time at which the job that materialized this asset in dbt will next run.""" + + dbt_environment_name: Union[str, None, UnsetType] = UNSET + """Name of the environment in which this asset exists in dbt.""" + + dbt_environment_dbt_version: Union[str, None, UnsetType] = UNSET + """Version of dbt used in the environment.""" + + dbt_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset in dbt.""" + + dbt_connection_context: Union[str, None, UnsetType] = UNSET + """Connection context for this asset in dbt.""" + + dbt_semantic_layer_proxy_url: Union[str, None, UnsetType] = UNSET + """URL of the semantic layer proxy for this asset in dbt.""" + + dbt_job_runs: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of latest dbt job runs across all environments.""" + + tag_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the tag in the source system.""" + + tag_attributes: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """Attributes associated with the tag in the source system.""" + + tag_allowed_values: Union[List[str], None, UnsetType] = UNSET + """Allowed values for the tag in the source system. These are denormalized from tagAttributes for ease of querying.""" + + mapped_classification_name: Union[str, None, UnsetType] = UNSET + """Name of the classification in Atlan that is mapped to this tag.""" + + +class DbtTagRelationshipAttributes(AssetRelationshipAttributes): + """DbtTag-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DbtTagNested(AssetNested): + """DbtTag in nested API format for high-performance serialization.""" + + attributes: Union[DbtTagAttributes, UnsetType] = UNSET + relationship_attributes: Union[DbtTagRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[DbtTagRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[DbtTagRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DBT_TAG_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_dbt_tag_attrs(attrs: DbtTagAttributes, obj: DbtTag) -> None: + """Populate DbtTag-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.dbt_alias = obj.dbt_alias + attrs.dbt_meta = obj.dbt_meta + attrs.dbt_unique_id = obj.dbt_unique_id + attrs.dbt_account_name = obj.dbt_account_name + attrs.dbt_project_name = obj.dbt_project_name + attrs.dbt_package_name = obj.dbt_package_name + attrs.dbt_job_name = obj.dbt_job_name + attrs.dbt_job_schedule = obj.dbt_job_schedule + attrs.dbt_job_status = obj.dbt_job_status + attrs.dbt_job_schedule_cron_humanized = obj.dbt_job_schedule_cron_humanized + attrs.dbt_job_last_run = obj.dbt_job_last_run + attrs.dbt_job_next_run = obj.dbt_job_next_run + attrs.dbt_job_next_run_humanized = obj.dbt_job_next_run_humanized + attrs.dbt_environment_name = obj.dbt_environment_name + attrs.dbt_environment_dbt_version = obj.dbt_environment_dbt_version + attrs.dbt_tags = obj.dbt_tags + attrs.dbt_connection_context = obj.dbt_connection_context + attrs.dbt_semantic_layer_proxy_url = obj.dbt_semantic_layer_proxy_url + attrs.dbt_job_runs = obj.dbt_job_runs + attrs.tag_id = obj.tag_id + attrs.tag_attributes = obj.tag_attributes + attrs.tag_allowed_values = obj.tag_allowed_values + attrs.mapped_classification_name = obj.mapped_classification_name + + +def _extract_dbt_tag_attrs(attrs: DbtTagAttributes) -> dict: + """Extract all DbtTag attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["dbt_alias"] = attrs.dbt_alias + result["dbt_meta"] = attrs.dbt_meta + result["dbt_unique_id"] = attrs.dbt_unique_id + result["dbt_account_name"] = attrs.dbt_account_name + result["dbt_project_name"] = attrs.dbt_project_name + result["dbt_package_name"] = attrs.dbt_package_name + result["dbt_job_name"] = attrs.dbt_job_name + result["dbt_job_schedule"] = attrs.dbt_job_schedule + result["dbt_job_status"] = attrs.dbt_job_status + result["dbt_job_schedule_cron_humanized"] = attrs.dbt_job_schedule_cron_humanized + result["dbt_job_last_run"] = attrs.dbt_job_last_run + result["dbt_job_next_run"] = attrs.dbt_job_next_run + result["dbt_job_next_run_humanized"] = attrs.dbt_job_next_run_humanized + result["dbt_environment_name"] = attrs.dbt_environment_name + result["dbt_environment_dbt_version"] = attrs.dbt_environment_dbt_version + result["dbt_tags"] = attrs.dbt_tags + result["dbt_connection_context"] = attrs.dbt_connection_context + result["dbt_semantic_layer_proxy_url"] = attrs.dbt_semantic_layer_proxy_url + result["dbt_job_runs"] = attrs.dbt_job_runs + result["tag_id"] = attrs.tag_id + result["tag_attributes"] = attrs.tag_attributes + result["tag_allowed_values"] = attrs.tag_allowed_values + result["mapped_classification_name"] = attrs.mapped_classification_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _dbt_tag_to_nested(dbt_tag: DbtTag) -> DbtTagNested: + """Convert flat DbtTag to nested format.""" + attrs = DbtTagAttributes() + _populate_dbt_tag_attrs(attrs, dbt_tag) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + dbt_tag, _DBT_TAG_REL_FIELDS, DbtTagRelationshipAttributes + ) + return DbtTagNested( + guid=dbt_tag.guid, + type_name=dbt_tag.type_name, + status=dbt_tag.status, + version=dbt_tag.version, + create_time=dbt_tag.create_time, + update_time=dbt_tag.update_time, + created_by=dbt_tag.created_by, + updated_by=dbt_tag.updated_by, + classifications=dbt_tag.classifications, + classification_names=dbt_tag.classification_names, + meanings=dbt_tag.meanings, + labels=dbt_tag.labels, + business_attributes=dbt_tag.business_attributes, + custom_attributes=dbt_tag.custom_attributes, + pending_tasks=dbt_tag.pending_tasks, + proxy=dbt_tag.proxy, + is_incomplete=dbt_tag.is_incomplete, + provenance_type=dbt_tag.provenance_type, + home_id=dbt_tag.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _dbt_tag_from_nested(nested: DbtTagNested) -> DbtTag: + """Convert nested format to flat DbtTag.""" + attrs = nested.attributes if nested.attributes is not UNSET else DbtTagAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DBT_TAG_REL_FIELDS, + DbtTagRelationshipAttributes, + ) + return DbtTag( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_dbt_tag_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _dbt_tag_to_nested_bytes(dbt_tag: DbtTag, serde: Serde) -> bytes: + """Convert flat DbtTag to nested JSON bytes.""" + return serde.encode(_dbt_tag_to_nested(dbt_tag)) + + +def _dbt_tag_from_nested_bytes(data: bytes, serde: Serde) -> DbtTag: + """Convert nested JSON bytes to flat DbtTag.""" + nested = serde.decode(data, DbtTagNested) + return _dbt_tag_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +DbtTag.DBT_ALIAS = KeywordField("dbtAlias", "dbtAlias") +DbtTag.DBT_META = KeywordField("dbtMeta", "dbtMeta") +DbtTag.DBT_UNIQUE_ID = KeywordField("dbtUniqueId", "dbtUniqueId") +DbtTag.DBT_ACCOUNT_NAME = KeywordField("dbtAccountName", "dbtAccountName") +DbtTag.DBT_PROJECT_NAME = KeywordField("dbtProjectName", "dbtProjectName") +DbtTag.DBT_PACKAGE_NAME = KeywordField("dbtPackageName", "dbtPackageName") +DbtTag.DBT_JOB_NAME = KeywordField("dbtJobName", "dbtJobName") +DbtTag.DBT_JOB_SCHEDULE = KeywordField("dbtJobSchedule", "dbtJobSchedule") +DbtTag.DBT_JOB_STATUS = KeywordField("dbtJobStatus", "dbtJobStatus") +DbtTag.DBT_JOB_SCHEDULE_CRON_HUMANIZED = KeywordField( + "dbtJobScheduleCronHumanized", "dbtJobScheduleCronHumanized" +) +DbtTag.DBT_JOB_LAST_RUN = NumericField("dbtJobLastRun", "dbtJobLastRun") +DbtTag.DBT_JOB_NEXT_RUN = NumericField("dbtJobNextRun", "dbtJobNextRun") +DbtTag.DBT_JOB_NEXT_RUN_HUMANIZED = KeywordField( + "dbtJobNextRunHumanized", "dbtJobNextRunHumanized" +) +DbtTag.DBT_ENVIRONMENT_NAME = KeywordField("dbtEnvironmentName", "dbtEnvironmentName") +DbtTag.DBT_ENVIRONMENT_DBT_VERSION = KeywordField( + "dbtEnvironmentDbtVersion", "dbtEnvironmentDbtVersion" +) +DbtTag.DBT_TAGS = KeywordField("dbtTags", "dbtTags") +DbtTag.DBT_CONNECTION_CONTEXT = KeywordField( + "dbtConnectionContext", "dbtConnectionContext" +) +DbtTag.DBT_SEMANTIC_LAYER_PROXY_URL = KeywordField( + "dbtSemanticLayerProxyUrl", "dbtSemanticLayerProxyUrl" +) +DbtTag.DBT_JOB_RUNS = KeywordField("dbtJobRuns", "dbtJobRuns") +DbtTag.TAG_ID = KeywordField("tagId", "tagId") +DbtTag.TAG_ATTRIBUTES = KeywordField("tagAttributes", "tagAttributes") +DbtTag.TAG_ALLOWED_VALUES = KeywordTextField( + "tagAllowedValues", "tagAllowedValues", "tagAllowedValues.text" +) +DbtTag.MAPPED_CLASSIFICATION_NAME = KeywordField( + "mappedClassificationName", "mappedClassificationName" +) +DbtTag.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +DbtTag.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +DbtTag.ANOMALO_CHECKS = RelationField("anomaloChecks") +DbtTag.APPLICATION = RelationField("application") +DbtTag.APPLICATION_FIELD = RelationField("applicationField") +DbtTag.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +DbtTag.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +DbtTag.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +DbtTag.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +DbtTag.METRICS = RelationField("metrics") +DbtTag.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +DbtTag.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +DbtTag.MEANINGS = RelationField("meanings") +DbtTag.MC_MONITORS = RelationField("mcMonitors") +DbtTag.MC_INCIDENTS = RelationField("mcIncidents") +DbtTag.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +DbtTag.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +DbtTag.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +DbtTag.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +DbtTag.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +DbtTag.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +DbtTag.FILES = RelationField("files") +DbtTag.LINKS = RelationField("links") +DbtTag.README = RelationField("readme") +DbtTag.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +DbtTag.SODA_CHECKS = RelationField("sodaChecks") +DbtTag.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +DbtTag.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/dbt_test.py b/pyatlan_v9/model/assets/dbt_test.py new file mode 100644 index 000000000..1dab0e4c6 --- /dev/null +++ b/pyatlan_v9/model/assets/dbt_test.py @@ -0,0 +1,856 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DbtTest asset model with flattened inheritance. + +This module provides: +- DbtTest: Flat asset class (easy to use) +- DbtTestAttributes: Nested attributes struct (extends AssetAttributes) +- DbtTestNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from .sql_related import RelatedSQL +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .dbt_related import RelatedDbtModel, RelatedDbtModelColumn, RelatedDbtSource + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class DbtTest(Asset): + """ + Instance of a dbt test in Atlan. + """ + + DBT_TEST_STATUS: ClassVar[Any] = None + DBT_TEST_STATE: ClassVar[Any] = None + DBT_TEST_ERROR: ClassVar[Any] = None + DBT_TEST_RAW_SQL: ClassVar[Any] = None + DBT_TEST_COMPILED_SQL: ClassVar[Any] = None + DBT_TEST_RAW_CODE: ClassVar[Any] = None + DBT_TEST_COMPILED_CODE: ClassVar[Any] = None + DBT_TEST_LANGUAGE: ClassVar[Any] = None + DBT_ALIAS: ClassVar[Any] = None + DBT_META: ClassVar[Any] = None + DBT_UNIQUE_ID: ClassVar[Any] = None + DBT_ACCOUNT_NAME: ClassVar[Any] = None + DBT_PROJECT_NAME: ClassVar[Any] = None + DBT_PACKAGE_NAME: ClassVar[Any] = None + DBT_JOB_NAME: ClassVar[Any] = None + DBT_JOB_SCHEDULE: ClassVar[Any] = None + DBT_JOB_STATUS: ClassVar[Any] = None + DBT_JOB_SCHEDULE_CRON_HUMANIZED: ClassVar[Any] = None + DBT_JOB_LAST_RUN: ClassVar[Any] = None + DBT_JOB_NEXT_RUN: ClassVar[Any] = None + DBT_JOB_NEXT_RUN_HUMANIZED: ClassVar[Any] = None + DBT_ENVIRONMENT_NAME: ClassVar[Any] = None + DBT_ENVIRONMENT_DBT_VERSION: ClassVar[Any] = None + DBT_TAGS: ClassVar[Any] = None + DBT_CONNECTION_CONTEXT: ClassVar[Any] = None + DBT_SEMANTIC_LAYER_PROXY_URL: ClassVar[Any] = None + DBT_JOB_RUNS: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + DBT_MODEL_COLUMNS: ClassVar[Any] = None + SQL_ASSETS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "DbtTest" + + dbt_test_status: Union[str, None, UnsetType] = UNSET + """Details of the results of the test. For errors, it reads "ERROR".""" + + dbt_test_state: Union[str, None, UnsetType] = UNSET + """Test results. Can be one of, in order of severity, "error", "fail", "warn", "pass".""" + + dbt_test_error: Union[str, None, UnsetType] = UNSET + """Error message in the case of state being "error".""" + + dbt_test_raw_sql: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="dbtTestRawSQL" + ) + """Raw SQL of the test.""" + + dbt_test_compiled_sql: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="dbtTestCompiledSQL" + ) + """Compiled SQL of the test.""" + + dbt_test_raw_code: Union[str, None, UnsetType] = UNSET + """Raw code of the test (when the test is defined using Python).""" + + dbt_test_compiled_code: Union[str, None, UnsetType] = UNSET + """Compiled code of the test (when the test is defined using Python).""" + + dbt_test_language: Union[str, None, UnsetType] = UNSET + """Language in which the test is written, for example: SQL or Python.""" + + dbt_alias: Union[str, None, UnsetType] = UNSET + """Alias of this asset in dbt.""" + + dbt_meta: Union[str, None, UnsetType] = UNSET + """Metadata for this asset in dbt, specifically everything under the 'meta' key in the dbt object.""" + + dbt_unique_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of this asset in dbt.""" + + dbt_account_name: Union[str, None, UnsetType] = UNSET + """Name of the account in which this asset exists in dbt.""" + + dbt_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which this asset exists in dbt.""" + + dbt_package_name: Union[str, None, UnsetType] = UNSET + """Name of the package in which this asset exists in dbt.""" + + dbt_job_name: Union[str, None, UnsetType] = UNSET + """Name of the job that materialized this asset in dbt.""" + + dbt_job_schedule: Union[str, None, UnsetType] = UNSET + """Schedule of the job that materialized this asset in dbt.""" + + dbt_job_status: Union[str, None, UnsetType] = UNSET + """Status of the job that materialized this asset in dbt.""" + + dbt_job_schedule_cron_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable cron schedule of the job that materialized this asset in dbt.""" + + dbt_job_last_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt last ran, in milliseconds.""" + + dbt_job_next_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt will next run, in milliseconds.""" + + dbt_job_next_run_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable time at which the job that materialized this asset in dbt will next run.""" + + dbt_environment_name: Union[str, None, UnsetType] = UNSET + """Name of the environment in which this asset exists in dbt.""" + + dbt_environment_dbt_version: Union[str, None, UnsetType] = UNSET + """Version of dbt used in the environment.""" + + dbt_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset in dbt.""" + + dbt_connection_context: Union[str, None, UnsetType] = UNSET + """Connection context for this asset in dbt.""" + + dbt_semantic_layer_proxy_url: Union[str, None, UnsetType] = UNSET + """URL of the semantic layer proxy for this asset in dbt.""" + + dbt_job_runs: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of latest dbt job runs across all environments.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Models related to this test.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Sources related to this test.""" + + dbt_model_columns: Union[List[RelatedDbtModelColumn], None, UnsetType] = UNSET + """Model columns related to this test.""" + + sql_assets: Union[List[RelatedSQL], None, UnsetType] = UNSET + """Assets related to this test.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "DbtTest" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _dbt_test_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> DbtTest: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + DbtTest instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _dbt_test_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DbtTestAttributes(AssetAttributes): + """DbtTest-specific attributes for nested API format.""" + + dbt_test_status: Union[str, None, UnsetType] = UNSET + """Details of the results of the test. For errors, it reads "ERROR".""" + + dbt_test_state: Union[str, None, UnsetType] = UNSET + """Test results. Can be one of, in order of severity, "error", "fail", "warn", "pass".""" + + dbt_test_error: Union[str, None, UnsetType] = UNSET + """Error message in the case of state being "error".""" + + dbt_test_raw_sql: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="dbtTestRawSQL" + ) + """Raw SQL of the test.""" + + dbt_test_compiled_sql: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="dbtTestCompiledSQL" + ) + """Compiled SQL of the test.""" + + dbt_test_raw_code: Union[str, None, UnsetType] = UNSET + """Raw code of the test (when the test is defined using Python).""" + + dbt_test_compiled_code: Union[str, None, UnsetType] = UNSET + """Compiled code of the test (when the test is defined using Python).""" + + dbt_test_language: Union[str, None, UnsetType] = UNSET + """Language in which the test is written, for example: SQL or Python.""" + + dbt_alias: Union[str, None, UnsetType] = UNSET + """Alias of this asset in dbt.""" + + dbt_meta: Union[str, None, UnsetType] = UNSET + """Metadata for this asset in dbt, specifically everything under the 'meta' key in the dbt object.""" + + dbt_unique_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of this asset in dbt.""" + + dbt_account_name: Union[str, None, UnsetType] = UNSET + """Name of the account in which this asset exists in dbt.""" + + dbt_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which this asset exists in dbt.""" + + dbt_package_name: Union[str, None, UnsetType] = UNSET + """Name of the package in which this asset exists in dbt.""" + + dbt_job_name: Union[str, None, UnsetType] = UNSET + """Name of the job that materialized this asset in dbt.""" + + dbt_job_schedule: Union[str, None, UnsetType] = UNSET + """Schedule of the job that materialized this asset in dbt.""" + + dbt_job_status: Union[str, None, UnsetType] = UNSET + """Status of the job that materialized this asset in dbt.""" + + dbt_job_schedule_cron_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable cron schedule of the job that materialized this asset in dbt.""" + + dbt_job_last_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt last ran, in milliseconds.""" + + dbt_job_next_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt will next run, in milliseconds.""" + + dbt_job_next_run_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable time at which the job that materialized this asset in dbt will next run.""" + + dbt_environment_name: Union[str, None, UnsetType] = UNSET + """Name of the environment in which this asset exists in dbt.""" + + dbt_environment_dbt_version: Union[str, None, UnsetType] = UNSET + """Version of dbt used in the environment.""" + + dbt_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset in dbt.""" + + dbt_connection_context: Union[str, None, UnsetType] = UNSET + """Connection context for this asset in dbt.""" + + dbt_semantic_layer_proxy_url: Union[str, None, UnsetType] = UNSET + """URL of the semantic layer proxy for this asset in dbt.""" + + dbt_job_runs: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of latest dbt job runs across all environments.""" + + +class DbtTestRelationshipAttributes(AssetRelationshipAttributes): + """DbtTest-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Models related to this test.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Sources related to this test.""" + + dbt_model_columns: Union[List[RelatedDbtModelColumn], None, UnsetType] = UNSET + """Model columns related to this test.""" + + sql_assets: Union[List[RelatedSQL], None, UnsetType] = UNSET + """Assets related to this test.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DbtTestNested(AssetNested): + """DbtTest in nested API format for high-performance serialization.""" + + attributes: Union[DbtTestAttributes, UnsetType] = UNSET + relationship_attributes: Union[DbtTestRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[DbtTestRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[DbtTestRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DBT_TEST_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "dbt_models", + "dbt_sources", + "dbt_model_columns", + "sql_assets", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_dbt_test_attrs(attrs: DbtTestAttributes, obj: DbtTest) -> None: + """Populate DbtTest-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.dbt_test_status = obj.dbt_test_status + attrs.dbt_test_state = obj.dbt_test_state + attrs.dbt_test_error = obj.dbt_test_error + attrs.dbt_test_raw_sql = obj.dbt_test_raw_sql + attrs.dbt_test_compiled_sql = obj.dbt_test_compiled_sql + attrs.dbt_test_raw_code = obj.dbt_test_raw_code + attrs.dbt_test_compiled_code = obj.dbt_test_compiled_code + attrs.dbt_test_language = obj.dbt_test_language + attrs.dbt_alias = obj.dbt_alias + attrs.dbt_meta = obj.dbt_meta + attrs.dbt_unique_id = obj.dbt_unique_id + attrs.dbt_account_name = obj.dbt_account_name + attrs.dbt_project_name = obj.dbt_project_name + attrs.dbt_package_name = obj.dbt_package_name + attrs.dbt_job_name = obj.dbt_job_name + attrs.dbt_job_schedule = obj.dbt_job_schedule + attrs.dbt_job_status = obj.dbt_job_status + attrs.dbt_job_schedule_cron_humanized = obj.dbt_job_schedule_cron_humanized + attrs.dbt_job_last_run = obj.dbt_job_last_run + attrs.dbt_job_next_run = obj.dbt_job_next_run + attrs.dbt_job_next_run_humanized = obj.dbt_job_next_run_humanized + attrs.dbt_environment_name = obj.dbt_environment_name + attrs.dbt_environment_dbt_version = obj.dbt_environment_dbt_version + attrs.dbt_tags = obj.dbt_tags + attrs.dbt_connection_context = obj.dbt_connection_context + attrs.dbt_semantic_layer_proxy_url = obj.dbt_semantic_layer_proxy_url + attrs.dbt_job_runs = obj.dbt_job_runs + + +def _extract_dbt_test_attrs(attrs: DbtTestAttributes) -> dict: + """Extract all DbtTest attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["dbt_test_status"] = attrs.dbt_test_status + result["dbt_test_state"] = attrs.dbt_test_state + result["dbt_test_error"] = attrs.dbt_test_error + result["dbt_test_raw_sql"] = attrs.dbt_test_raw_sql + result["dbt_test_compiled_sql"] = attrs.dbt_test_compiled_sql + result["dbt_test_raw_code"] = attrs.dbt_test_raw_code + result["dbt_test_compiled_code"] = attrs.dbt_test_compiled_code + result["dbt_test_language"] = attrs.dbt_test_language + result["dbt_alias"] = attrs.dbt_alias + result["dbt_meta"] = attrs.dbt_meta + result["dbt_unique_id"] = attrs.dbt_unique_id + result["dbt_account_name"] = attrs.dbt_account_name + result["dbt_project_name"] = attrs.dbt_project_name + result["dbt_package_name"] = attrs.dbt_package_name + result["dbt_job_name"] = attrs.dbt_job_name + result["dbt_job_schedule"] = attrs.dbt_job_schedule + result["dbt_job_status"] = attrs.dbt_job_status + result["dbt_job_schedule_cron_humanized"] = attrs.dbt_job_schedule_cron_humanized + result["dbt_job_last_run"] = attrs.dbt_job_last_run + result["dbt_job_next_run"] = attrs.dbt_job_next_run + result["dbt_job_next_run_humanized"] = attrs.dbt_job_next_run_humanized + result["dbt_environment_name"] = attrs.dbt_environment_name + result["dbt_environment_dbt_version"] = attrs.dbt_environment_dbt_version + result["dbt_tags"] = attrs.dbt_tags + result["dbt_connection_context"] = attrs.dbt_connection_context + result["dbt_semantic_layer_proxy_url"] = attrs.dbt_semantic_layer_proxy_url + result["dbt_job_runs"] = attrs.dbt_job_runs + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _dbt_test_to_nested(dbt_test: DbtTest) -> DbtTestNested: + """Convert flat DbtTest to nested format.""" + attrs = DbtTestAttributes() + _populate_dbt_test_attrs(attrs, dbt_test) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + dbt_test, _DBT_TEST_REL_FIELDS, DbtTestRelationshipAttributes + ) + return DbtTestNested( + guid=dbt_test.guid, + type_name=dbt_test.type_name, + status=dbt_test.status, + version=dbt_test.version, + create_time=dbt_test.create_time, + update_time=dbt_test.update_time, + created_by=dbt_test.created_by, + updated_by=dbt_test.updated_by, + classifications=dbt_test.classifications, + classification_names=dbt_test.classification_names, + meanings=dbt_test.meanings, + labels=dbt_test.labels, + business_attributes=dbt_test.business_attributes, + custom_attributes=dbt_test.custom_attributes, + pending_tasks=dbt_test.pending_tasks, + proxy=dbt_test.proxy, + is_incomplete=dbt_test.is_incomplete, + provenance_type=dbt_test.provenance_type, + home_id=dbt_test.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _dbt_test_from_nested(nested: DbtTestNested) -> DbtTest: + """Convert nested format to flat DbtTest.""" + attrs = nested.attributes if nested.attributes is not UNSET else DbtTestAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DBT_TEST_REL_FIELDS, + DbtTestRelationshipAttributes, + ) + return DbtTest( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_dbt_test_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _dbt_test_to_nested_bytes(dbt_test: DbtTest, serde: Serde) -> bytes: + """Convert flat DbtTest to nested JSON bytes.""" + return serde.encode(_dbt_test_to_nested(dbt_test)) + + +def _dbt_test_from_nested_bytes(data: bytes, serde: Serde) -> DbtTest: + """Convert nested JSON bytes to flat DbtTest.""" + nested = serde.decode(data, DbtTestNested) + return _dbt_test_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +DbtTest.DBT_TEST_STATUS = KeywordField("dbtTestStatus", "dbtTestStatus") +DbtTest.DBT_TEST_STATE = KeywordField("dbtTestState", "dbtTestState") +DbtTest.DBT_TEST_ERROR = KeywordField("dbtTestError", "dbtTestError") +DbtTest.DBT_TEST_RAW_SQL = KeywordField("dbtTestRawSQL", "dbtTestRawSQL") +DbtTest.DBT_TEST_COMPILED_SQL = KeywordField("dbtTestCompiledSQL", "dbtTestCompiledSQL") +DbtTest.DBT_TEST_RAW_CODE = KeywordField("dbtTestRawCode", "dbtTestRawCode") +DbtTest.DBT_TEST_COMPILED_CODE = KeywordField( + "dbtTestCompiledCode", "dbtTestCompiledCode" +) +DbtTest.DBT_TEST_LANGUAGE = KeywordField("dbtTestLanguage", "dbtTestLanguage") +DbtTest.DBT_ALIAS = KeywordField("dbtAlias", "dbtAlias") +DbtTest.DBT_META = KeywordField("dbtMeta", "dbtMeta") +DbtTest.DBT_UNIQUE_ID = KeywordField("dbtUniqueId", "dbtUniqueId") +DbtTest.DBT_ACCOUNT_NAME = KeywordField("dbtAccountName", "dbtAccountName") +DbtTest.DBT_PROJECT_NAME = KeywordField("dbtProjectName", "dbtProjectName") +DbtTest.DBT_PACKAGE_NAME = KeywordField("dbtPackageName", "dbtPackageName") +DbtTest.DBT_JOB_NAME = KeywordField("dbtJobName", "dbtJobName") +DbtTest.DBT_JOB_SCHEDULE = KeywordField("dbtJobSchedule", "dbtJobSchedule") +DbtTest.DBT_JOB_STATUS = KeywordField("dbtJobStatus", "dbtJobStatus") +DbtTest.DBT_JOB_SCHEDULE_CRON_HUMANIZED = KeywordField( + "dbtJobScheduleCronHumanized", "dbtJobScheduleCronHumanized" +) +DbtTest.DBT_JOB_LAST_RUN = NumericField("dbtJobLastRun", "dbtJobLastRun") +DbtTest.DBT_JOB_NEXT_RUN = NumericField("dbtJobNextRun", "dbtJobNextRun") +DbtTest.DBT_JOB_NEXT_RUN_HUMANIZED = KeywordField( + "dbtJobNextRunHumanized", "dbtJobNextRunHumanized" +) +DbtTest.DBT_ENVIRONMENT_NAME = KeywordField("dbtEnvironmentName", "dbtEnvironmentName") +DbtTest.DBT_ENVIRONMENT_DBT_VERSION = KeywordField( + "dbtEnvironmentDbtVersion", "dbtEnvironmentDbtVersion" +) +DbtTest.DBT_TAGS = KeywordField("dbtTags", "dbtTags") +DbtTest.DBT_CONNECTION_CONTEXT = KeywordField( + "dbtConnectionContext", "dbtConnectionContext" +) +DbtTest.DBT_SEMANTIC_LAYER_PROXY_URL = KeywordField( + "dbtSemanticLayerProxyUrl", "dbtSemanticLayerProxyUrl" +) +DbtTest.DBT_JOB_RUNS = KeywordField("dbtJobRuns", "dbtJobRuns") +DbtTest.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +DbtTest.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +DbtTest.ANOMALO_CHECKS = RelationField("anomaloChecks") +DbtTest.APPLICATION = RelationField("application") +DbtTest.APPLICATION_FIELD = RelationField("applicationField") +DbtTest.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +DbtTest.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +DbtTest.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +DbtTest.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +DbtTest.METRICS = RelationField("metrics") +DbtTest.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +DbtTest.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +DbtTest.DBT_MODELS = RelationField("dbtModels") +DbtTest.DBT_SOURCES = RelationField("dbtSources") +DbtTest.DBT_MODEL_COLUMNS = RelationField("dbtModelColumns") +DbtTest.SQL_ASSETS = RelationField("sqlAssets") +DbtTest.MEANINGS = RelationField("meanings") +DbtTest.MC_MONITORS = RelationField("mcMonitors") +DbtTest.MC_INCIDENTS = RelationField("mcIncidents") +DbtTest.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +DbtTest.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +DbtTest.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +DbtTest.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +DbtTest.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +DbtTest.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +DbtTest.FILES = RelationField("files") +DbtTest.LINKS = RelationField("links") +DbtTest.README = RelationField("readme") +DbtTest.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +DbtTest.SODA_CHECKS = RelationField("sodaChecks") +DbtTest.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +DbtTest.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/document_db.py b/pyatlan_v9/model/assets/document_db.py new file mode 100644 index 000000000..436bdde56 --- /dev/null +++ b/pyatlan_v9/model/assets/document_db.py @@ -0,0 +1,544 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DocumentDB asset model with flattened inheritance. + +This module provides: +- DocumentDB: Flat asset class (easy to use) +- DocumentDBAttributes: Nested attributes struct (extends AssetAttributes) +- DocumentDBNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class DocumentDB(Asset): + """ + Base class for all DocumentDB types. + """ + + NO_SQL_SCHEMA_DEFINITION: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "DocumentDB" + + no_sql_schema_definition: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="noSQLSchemaDefinition" + ) + """Represents attributes for describing the key schema for the table and indexes.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "DocumentDB" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _document_db_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> DocumentDB: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + DocumentDB instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _document_db_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DocumentDBAttributes(AssetAttributes): + """DocumentDB-specific attributes for nested API format.""" + + no_sql_schema_definition: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="noSQLSchemaDefinition" + ) + """Represents attributes for describing the key schema for the table and indexes.""" + + +class DocumentDBRelationshipAttributes(AssetRelationshipAttributes): + """DocumentDB-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DocumentDBNested(AssetNested): + """DocumentDB in nested API format for high-performance serialization.""" + + attributes: Union[DocumentDBAttributes, UnsetType] = UNSET + relationship_attributes: Union[DocumentDBRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + DocumentDBRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + DocumentDBRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DOCUMENT_DB_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_document_db_attrs(attrs: DocumentDBAttributes, obj: DocumentDB) -> None: + """Populate DocumentDB-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.no_sql_schema_definition = obj.no_sql_schema_definition + + +def _extract_document_db_attrs(attrs: DocumentDBAttributes) -> dict: + """Extract all DocumentDB attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["no_sql_schema_definition"] = attrs.no_sql_schema_definition + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _document_db_to_nested(document_db: DocumentDB) -> DocumentDBNested: + """Convert flat DocumentDB to nested format.""" + attrs = DocumentDBAttributes() + _populate_document_db_attrs(attrs, document_db) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + document_db, _DOCUMENT_DB_REL_FIELDS, DocumentDBRelationshipAttributes + ) + return DocumentDBNested( + guid=document_db.guid, + type_name=document_db.type_name, + status=document_db.status, + version=document_db.version, + create_time=document_db.create_time, + update_time=document_db.update_time, + created_by=document_db.created_by, + updated_by=document_db.updated_by, + classifications=document_db.classifications, + classification_names=document_db.classification_names, + meanings=document_db.meanings, + labels=document_db.labels, + business_attributes=document_db.business_attributes, + custom_attributes=document_db.custom_attributes, + pending_tasks=document_db.pending_tasks, + proxy=document_db.proxy, + is_incomplete=document_db.is_incomplete, + provenance_type=document_db.provenance_type, + home_id=document_db.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _document_db_from_nested(nested: DocumentDBNested) -> DocumentDB: + """Convert nested format to flat DocumentDB.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else DocumentDBAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DOCUMENT_DB_REL_FIELDS, + DocumentDBRelationshipAttributes, + ) + return DocumentDB( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_document_db_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _document_db_to_nested_bytes(document_db: DocumentDB, serde: Serde) -> bytes: + """Convert flat DocumentDB to nested JSON bytes.""" + return serde.encode(_document_db_to_nested(document_db)) + + +def _document_db_from_nested_bytes(data: bytes, serde: Serde) -> DocumentDB: + """Convert nested JSON bytes to flat DocumentDB.""" + nested = serde.decode(data, DocumentDBNested) + return _document_db_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +DocumentDB.NO_SQL_SCHEMA_DEFINITION = KeywordField( + "noSQLSchemaDefinition", "noSQLSchemaDefinition" +) +DocumentDB.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +DocumentDB.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +DocumentDB.ANOMALO_CHECKS = RelationField("anomaloChecks") +DocumentDB.APPLICATION = RelationField("application") +DocumentDB.APPLICATION_FIELD = RelationField("applicationField") +DocumentDB.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +DocumentDB.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +DocumentDB.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +DocumentDB.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +DocumentDB.METRICS = RelationField("metrics") +DocumentDB.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +DocumentDB.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +DocumentDB.MEANINGS = RelationField("meanings") +DocumentDB.MC_MONITORS = RelationField("mcMonitors") +DocumentDB.MC_INCIDENTS = RelationField("mcIncidents") +DocumentDB.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +DocumentDB.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +DocumentDB.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +DocumentDB.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +DocumentDB.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +DocumentDB.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +DocumentDB.FILES = RelationField("files") +DocumentDB.LINKS = RelationField("links") +DocumentDB.README = RelationField("readme") +DocumentDB.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +DocumentDB.SODA_CHECKS = RelationField("sodaChecks") +DocumentDB.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +DocumentDB.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/document_db_collection.py b/pyatlan_v9/model/assets/document_db_collection.py new file mode 100644 index 000000000..965b20462 --- /dev/null +++ b/pyatlan_v9/model/assets/document_db_collection.py @@ -0,0 +1,1514 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DocumentDBCollection asset model with flattened inheritance. + +This module provides: +- DocumentDBCollection: Flat asset class (easy to use) +- DocumentDBCollectionAttributes: Nested attributes struct (extends AssetAttributes) +- DocumentDBCollectionNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .snowflake_related import RelatedSnowflakeSemanticLogicalTable +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from .sql_related import ( + RelatedColumn, + RelatedQuery, + RelatedSchema, + RelatedTable, + RelatedTablePartition, +) +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .document_db_related import RelatedDocumentDBDatabase + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class DocumentDBCollection(Asset): + """ + Instance of a DocumentDBCollection in Atlan. + """ + + DOCUMENT_DB_COLLECTION_SUBTYPE: ClassVar[Any] = None + DOCUMENT_DB_COLLECTION_IS_CAPPED: ClassVar[Any] = None + DOCUMENT_DB_COLLECTION_TIME_FIELD: ClassVar[Any] = None + DOCUMENT_DB_COLLECTION_TIME_GRANULARITY: ClassVar[Any] = None + DOCUMENT_DB_COLLECTION_EXPIRE_AFTER_SECONDS: ClassVar[Any] = None + DOCUMENT_DB_COLLECTION_MAXIMUM_DOCUMENT_COUNT: ClassVar[Any] = None + DOCUMENT_DB_COLLECTION_MAX_SIZE: ClassVar[Any] = None + DOCUMENT_DB_COLLECTION_NUM_ORPHAN_DOCS: ClassVar[Any] = None + DOCUMENT_DB_COLLECTION_NUM_INDEXES: ClassVar[Any] = None + DOCUMENT_DB_COLLECTION_TOTAL_INDEX_SIZE: ClassVar[Any] = None + DOCUMENT_DB_COLLECTION_AVERAGE_OBJECT_SIZE: ClassVar[Any] = None + DOCUMENT_DB_COLLECTION_SCHEMA_DEFINITION: ClassVar[Any] = None + NO_SQL_SCHEMA_DEFINITION: ClassVar[Any] = None + COLUMN_COUNT: ClassVar[Any] = None + ROW_COUNT: ClassVar[Any] = None + SIZE_BYTES: ClassVar[Any] = None + TABLE_OBJECT_COUNT: ClassVar[Any] = None + ALIAS: ClassVar[Any] = None + IS_TEMPORARY: ClassVar[Any] = None + IS_QUERY_PREVIEW: ClassVar[Any] = None + QUERY_PREVIEW_CONFIG: ClassVar[Any] = None + EXTERNAL_LOCATION: ClassVar[Any] = None + EXTERNAL_LOCATION_REGION: ClassVar[Any] = None + EXTERNAL_LOCATION_FORMAT: ClassVar[Any] = None + IS_PARTITIONED: ClassVar[Any] = None + PARTITION_STRATEGY: ClassVar[Any] = None + PARTITION_COUNT: ClassVar[Any] = None + TABLE_DEFINITION: ClassVar[Any] = None + PARTITION_LIST: ClassVar[Any] = None + IS_SHARDED: ClassVar[Any] = None + TABLE_TYPE: ClassVar[Any] = None + ICEBERG_CATALOG_NAME: ClassVar[Any] = None + ICEBERG_TABLE_TYPE: ClassVar[Any] = None + ICEBERG_CATALOG_SOURCE: ClassVar[Any] = None + ICEBERG_CATALOG_TABLE_NAME: ClassVar[Any] = None + TABLE_IMPALA_PARAMETERS: ClassVar[Any] = None + ICEBERG_CATALOG_TABLE_NAMESPACE: ClassVar[Any] = None + TABLE_EXTERNAL_VOLUME_NAME: ClassVar[Any] = None + ICEBERG_TABLE_BASE_LOCATION: ClassVar[Any] = None + TABLE_RETENTION_TIME: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + DOCUMENT_DB_DATABASE: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + COLUMNS: ClassVar[Any] = None + QUERIES: ClassVar[Any] = None + ATLAN_SCHEMA: ClassVar[Any] = None + DIMENSIONS: ClassVar[Any] = None + FACTS: ClassVar[Any] = None + PARTITIONS: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "DocumentDBCollection" + + document_db_collection_subtype: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="documentDBCollectionSubtype" + ) + """Subtype of a DocumentDBCollection, for example: Capped, Time Series, etc.""" + + document_db_collection_is_capped: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="documentDBCollectionIsCapped" + ) + """Whether the collection is capped (true) or not (false).""" + + document_db_collection_time_field: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="documentDBCollectionTimeField" + ) + """Name of the field containing the date in each time series document.""" + + document_db_collection_time_granularity: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="documentDBCollectionTimeGranularity") + ) + """Closest match to the time span between consecutive incoming measurements.""" + + document_db_collection_expire_after_seconds: Union[int, None, UnsetType] = ( + msgspec.field(default=UNSET, name="documentDBCollectionExpireAfterSeconds") + ) + """Seconds after which documents in a time series collection or clustered collection expire.""" + + document_db_collection_maximum_document_count: Union[int, None, UnsetType] = ( + msgspec.field(default=UNSET, name="documentDBCollectionMaximumDocumentCount") + ) + """Maximum number of documents allowed in a capped collection.""" + + document_db_collection_max_size: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="documentDBCollectionMaxSize" + ) + """Maximum size allowed in a capped collection.""" + + document_db_collection_num_orphan_docs: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="documentDBCollectionNumOrphanDocs" + ) + """Number of orphaned documents in the collection.""" + + document_db_collection_num_indexes: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="documentDBCollectionNumIndexes" + ) + """Number of indexes in the collection.""" + + document_db_collection_total_index_size: Union[int, None, UnsetType] = ( + msgspec.field(default=UNSET, name="documentDBCollectionTotalIndexSize") + ) + """Total size of all indexes.""" + + document_db_collection_average_object_size: Union[int, None, UnsetType] = ( + msgspec.field(default=UNSET, name="documentDBCollectionAverageObjectSize") + ) + """Average size of an object in the collection.""" + + document_db_collection_schema_definition: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="documentDBCollectionSchemaDefinition") + ) + """Definition of the schema applicable for the collection.""" + + no_sql_schema_definition: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="noSQLSchemaDefinition" + ) + """Represents attributes for describing the key schema for the table and indexes.""" + + column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this table.""" + + row_count: Union[int, None, UnsetType] = UNSET + """Number of rows in this table.""" + + size_bytes: Union[int, None, UnsetType] = UNSET + """Size of this table, in bytes.""" + + table_object_count: Union[int, None, UnsetType] = UNSET + """Number of objects in this table.""" + + alias: Union[str, None, UnsetType] = UNSET + """Alias for this table.""" + + is_temporary: Union[bool, None, UnsetType] = UNSET + """Whether this table is temporary (true) or not (false).""" + + is_query_preview: Union[bool, None, UnsetType] = UNSET + """Whether preview queries are allowed for this table (true) or not (false).""" + + query_preview_config: Union[Dict[str, str], None, UnsetType] = UNSET + """Configuration for preview queries.""" + + external_location: Union[str, None, UnsetType] = UNSET + """External location of this table, for example: an S3 object location.""" + + external_location_region: Union[str, None, UnsetType] = UNSET + """Region of the external location of this table, for example: S3 region.""" + + external_location_format: Union[str, None, UnsetType] = UNSET + """Format of the external location of this table, for example: JSON, CSV, PARQUET, etc.""" + + is_partitioned: Union[bool, None, UnsetType] = UNSET + """Whether this table is partitioned (true) or not (false).""" + + partition_strategy: Union[str, None, UnsetType] = UNSET + """Partition strategy for this table.""" + + partition_count: Union[int, None, UnsetType] = UNSET + """Number of partitions in this table.""" + + table_definition: Union[str, None, UnsetType] = UNSET + """Definition of the table.""" + + partition_list: Union[str, None, UnsetType] = UNSET + """List of partitions in this table.""" + + is_sharded: Union[bool, None, UnsetType] = UNSET + """Whether this table is a sharded table (true) or not (false).""" + + table_type: Union[str, None, UnsetType] = UNSET + """Type of the table.""" + + iceberg_catalog_name: Union[str, None, UnsetType] = UNSET + """Iceberg table catalog name (can be any user defined name)""" + + iceberg_table_type: Union[str, None, UnsetType] = UNSET + """Iceberg table type (managed vs unmanaged)""" + + iceberg_catalog_source: Union[str, None, UnsetType] = UNSET + """Iceberg table catalog type (glue, polaris, snowflake)""" + + iceberg_catalog_table_name: Union[str, None, UnsetType] = UNSET + """Catalog table name (actual table name on the catalog side).""" + + table_impala_parameters: Union[Dict[str, str], None, UnsetType] = UNSET + """Extra attributes for Impala""" + + iceberg_catalog_table_namespace: Union[str, None, UnsetType] = UNSET + """Catalog table namespace (actual database name on the catalog side).""" + + table_external_volume_name: Union[str, None, UnsetType] = UNSET + """External volume name for the table.""" + + iceberg_table_base_location: Union[str, None, UnsetType] = UNSET + """Iceberg table base location inside the external volume.""" + + table_retention_time: Union[int, None, UnsetType] = UNSET + """Data retention time in days.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + document_db_database: Union[RelatedDocumentDBDatabase, None, UnsetType] = ( + msgspec.field(default=UNSET, name="documentDBDatabase") + ) + """Database in which the collection exists.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Columns that exist within this table.""" + + queries: Union[List[RelatedQuery], None, UnsetType] = UNSET + """Queries that access this table.""" + + atlan_schema: Union[RelatedSchema, None, UnsetType] = UNSET + """Schema in which this table exists.""" + + dimensions: Union[List[RelatedTable], None, UnsetType] = UNSET + """""" + + facts: Union[List[RelatedTable], None, UnsetType] = UNSET + """""" + + partitions: Union[List[RelatedTablePartition], None, UnsetType] = UNSET + """Partitions that exist within this table.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "DocumentDBCollection" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + database_qualified_name: str, + connection_qualified_name: str | None = None, + ) -> "DocumentDBCollection": + """Create a new DocumentDBCollection asset.""" + validate_required_fields( + ["name", "database_qualified_name"], [name, database_qualified_name] + ) + if connection_qualified_name: + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + else: + parts = database_qualified_name.split("/") + if len(parts) < 3: + raise ValueError("database_qualified_name is invalid") + connection_qualified_name = "/".join(parts[:3]) + connector_name = parts[1] + return cls( + name=name, + database_qualified_name=database_qualified_name, + connection_qualified_name=connection_qualified_name, + qualified_name=f"{database_qualified_name}/{name}", + connector_name=connector_name, + document_db_database=RelatedDocumentDBDatabase( + unique_attributes={"qualifiedName": database_qualified_name} + ), + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "DocumentDBCollection": + """Create a DocumentDBCollection instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "DocumentDBCollection": + """Return only fields required for update operations.""" + return DocumentDBCollection.updater( + qualified_name=self.qualified_name, + name=self.name, + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _document_db_collection_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> DocumentDBCollection: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + DocumentDBCollection instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _document_db_collection_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DocumentDBCollectionAttributes(AssetAttributes): + """DocumentDBCollection-specific attributes for nested API format.""" + + document_db_collection_subtype: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="documentDBCollectionSubtype" + ) + """Subtype of a DocumentDBCollection, for example: Capped, Time Series, etc.""" + + document_db_collection_is_capped: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="documentDBCollectionIsCapped" + ) + """Whether the collection is capped (true) or not (false).""" + + document_db_collection_time_field: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="documentDBCollectionTimeField" + ) + """Name of the field containing the date in each time series document.""" + + document_db_collection_time_granularity: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="documentDBCollectionTimeGranularity") + ) + """Closest match to the time span between consecutive incoming measurements.""" + + document_db_collection_expire_after_seconds: Union[int, None, UnsetType] = ( + msgspec.field(default=UNSET, name="documentDBCollectionExpireAfterSeconds") + ) + """Seconds after which documents in a time series collection or clustered collection expire.""" + + document_db_collection_maximum_document_count: Union[int, None, UnsetType] = ( + msgspec.field(default=UNSET, name="documentDBCollectionMaximumDocumentCount") + ) + """Maximum number of documents allowed in a capped collection.""" + + document_db_collection_max_size: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="documentDBCollectionMaxSize" + ) + """Maximum size allowed in a capped collection.""" + + document_db_collection_num_orphan_docs: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="documentDBCollectionNumOrphanDocs" + ) + """Number of orphaned documents in the collection.""" + + document_db_collection_num_indexes: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="documentDBCollectionNumIndexes" + ) + """Number of indexes in the collection.""" + + document_db_collection_total_index_size: Union[int, None, UnsetType] = ( + msgspec.field(default=UNSET, name="documentDBCollectionTotalIndexSize") + ) + """Total size of all indexes.""" + + document_db_collection_average_object_size: Union[int, None, UnsetType] = ( + msgspec.field(default=UNSET, name="documentDBCollectionAverageObjectSize") + ) + """Average size of an object in the collection.""" + + document_db_collection_schema_definition: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="documentDBCollectionSchemaDefinition") + ) + """Definition of the schema applicable for the collection.""" + + no_sql_schema_definition: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="noSQLSchemaDefinition" + ) + """Represents attributes for describing the key schema for the table and indexes.""" + + column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this table.""" + + row_count: Union[int, None, UnsetType] = UNSET + """Number of rows in this table.""" + + size_bytes: Union[int, None, UnsetType] = UNSET + """Size of this table, in bytes.""" + + table_object_count: Union[int, None, UnsetType] = UNSET + """Number of objects in this table.""" + + alias: Union[str, None, UnsetType] = UNSET + """Alias for this table.""" + + is_temporary: Union[bool, None, UnsetType] = UNSET + """Whether this table is temporary (true) or not (false).""" + + is_query_preview: Union[bool, None, UnsetType] = UNSET + """Whether preview queries are allowed for this table (true) or not (false).""" + + query_preview_config: Union[Dict[str, str], None, UnsetType] = UNSET + """Configuration for preview queries.""" + + external_location: Union[str, None, UnsetType] = UNSET + """External location of this table, for example: an S3 object location.""" + + external_location_region: Union[str, None, UnsetType] = UNSET + """Region of the external location of this table, for example: S3 region.""" + + external_location_format: Union[str, None, UnsetType] = UNSET + """Format of the external location of this table, for example: JSON, CSV, PARQUET, etc.""" + + is_partitioned: Union[bool, None, UnsetType] = UNSET + """Whether this table is partitioned (true) or not (false).""" + + partition_strategy: Union[str, None, UnsetType] = UNSET + """Partition strategy for this table.""" + + partition_count: Union[int, None, UnsetType] = UNSET + """Number of partitions in this table.""" + + table_definition: Union[str, None, UnsetType] = UNSET + """Definition of the table.""" + + partition_list: Union[str, None, UnsetType] = UNSET + """List of partitions in this table.""" + + is_sharded: Union[bool, None, UnsetType] = UNSET + """Whether this table is a sharded table (true) or not (false).""" + + table_type: Union[str, None, UnsetType] = UNSET + """Type of the table.""" + + iceberg_catalog_name: Union[str, None, UnsetType] = UNSET + """Iceberg table catalog name (can be any user defined name)""" + + iceberg_table_type: Union[str, None, UnsetType] = UNSET + """Iceberg table type (managed vs unmanaged)""" + + iceberg_catalog_source: Union[str, None, UnsetType] = UNSET + """Iceberg table catalog type (glue, polaris, snowflake)""" + + iceberg_catalog_table_name: Union[str, None, UnsetType] = UNSET + """Catalog table name (actual table name on the catalog side).""" + + table_impala_parameters: Union[Dict[str, str], None, UnsetType] = UNSET + """Extra attributes for Impala""" + + iceberg_catalog_table_namespace: Union[str, None, UnsetType] = UNSET + """Catalog table namespace (actual database name on the catalog side).""" + + table_external_volume_name: Union[str, None, UnsetType] = UNSET + """External volume name for the table.""" + + iceberg_table_base_location: Union[str, None, UnsetType] = UNSET + """Iceberg table base location inside the external volume.""" + + table_retention_time: Union[int, None, UnsetType] = UNSET + """Data retention time in days.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + +class DocumentDBCollectionRelationshipAttributes(AssetRelationshipAttributes): + """DocumentDBCollection-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + document_db_database: Union[RelatedDocumentDBDatabase, None, UnsetType] = ( + msgspec.field(default=UNSET, name="documentDBDatabase") + ) + """Database in which the collection exists.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Columns that exist within this table.""" + + queries: Union[List[RelatedQuery], None, UnsetType] = UNSET + """Queries that access this table.""" + + atlan_schema: Union[RelatedSchema, None, UnsetType] = UNSET + """Schema in which this table exists.""" + + dimensions: Union[List[RelatedTable], None, UnsetType] = UNSET + """""" + + facts: Union[List[RelatedTable], None, UnsetType] = UNSET + """""" + + partitions: Union[List[RelatedTablePartition], None, UnsetType] = UNSET + """Partitions that exist within this table.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DocumentDBCollectionNested(AssetNested): + """DocumentDBCollection in nested API format for high-performance serialization.""" + + attributes: Union[DocumentDBCollectionAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + DocumentDBCollectionRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + DocumentDBCollectionRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + DocumentDBCollectionRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DOCUMENT_DB_COLLECTION_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "document_db_database", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "columns", + "queries", + "atlan_schema", + "dimensions", + "facts", + "partitions", + "schema_registry_subjects", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_document_db_collection_attrs( + attrs: DocumentDBCollectionAttributes, obj: DocumentDBCollection +) -> None: + """Populate DocumentDBCollection-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.document_db_collection_subtype = obj.document_db_collection_subtype + attrs.document_db_collection_is_capped = obj.document_db_collection_is_capped + attrs.document_db_collection_time_field = obj.document_db_collection_time_field + attrs.document_db_collection_time_granularity = ( + obj.document_db_collection_time_granularity + ) + attrs.document_db_collection_expire_after_seconds = ( + obj.document_db_collection_expire_after_seconds + ) + attrs.document_db_collection_maximum_document_count = ( + obj.document_db_collection_maximum_document_count + ) + attrs.document_db_collection_max_size = obj.document_db_collection_max_size + attrs.document_db_collection_num_orphan_docs = ( + obj.document_db_collection_num_orphan_docs + ) + attrs.document_db_collection_num_indexes = obj.document_db_collection_num_indexes + attrs.document_db_collection_total_index_size = ( + obj.document_db_collection_total_index_size + ) + attrs.document_db_collection_average_object_size = ( + obj.document_db_collection_average_object_size + ) + attrs.document_db_collection_schema_definition = ( + obj.document_db_collection_schema_definition + ) + attrs.no_sql_schema_definition = obj.no_sql_schema_definition + attrs.column_count = obj.column_count + attrs.row_count = obj.row_count + attrs.size_bytes = obj.size_bytes + attrs.table_object_count = obj.table_object_count + attrs.alias = obj.alias + attrs.is_temporary = obj.is_temporary + attrs.is_query_preview = obj.is_query_preview + attrs.query_preview_config = obj.query_preview_config + attrs.external_location = obj.external_location + attrs.external_location_region = obj.external_location_region + attrs.external_location_format = obj.external_location_format + attrs.is_partitioned = obj.is_partitioned + attrs.partition_strategy = obj.partition_strategy + attrs.partition_count = obj.partition_count + attrs.table_definition = obj.table_definition + attrs.partition_list = obj.partition_list + attrs.is_sharded = obj.is_sharded + attrs.table_type = obj.table_type + attrs.iceberg_catalog_name = obj.iceberg_catalog_name + attrs.iceberg_table_type = obj.iceberg_table_type + attrs.iceberg_catalog_source = obj.iceberg_catalog_source + attrs.iceberg_catalog_table_name = obj.iceberg_catalog_table_name + attrs.table_impala_parameters = obj.table_impala_parameters + attrs.iceberg_catalog_table_namespace = obj.iceberg_catalog_table_namespace + attrs.table_external_volume_name = obj.table_external_volume_name + attrs.iceberg_table_base_location = obj.iceberg_table_base_location + attrs.table_retention_time = obj.table_retention_time + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + + +def _extract_document_db_collection_attrs( + attrs: DocumentDBCollectionAttributes, +) -> dict: + """Extract all DocumentDBCollection attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["document_db_collection_subtype"] = attrs.document_db_collection_subtype + result["document_db_collection_is_capped"] = attrs.document_db_collection_is_capped + result["document_db_collection_time_field"] = ( + attrs.document_db_collection_time_field + ) + result["document_db_collection_time_granularity"] = ( + attrs.document_db_collection_time_granularity + ) + result["document_db_collection_expire_after_seconds"] = ( + attrs.document_db_collection_expire_after_seconds + ) + result["document_db_collection_maximum_document_count"] = ( + attrs.document_db_collection_maximum_document_count + ) + result["document_db_collection_max_size"] = attrs.document_db_collection_max_size + result["document_db_collection_num_orphan_docs"] = ( + attrs.document_db_collection_num_orphan_docs + ) + result["document_db_collection_num_indexes"] = ( + attrs.document_db_collection_num_indexes + ) + result["document_db_collection_total_index_size"] = ( + attrs.document_db_collection_total_index_size + ) + result["document_db_collection_average_object_size"] = ( + attrs.document_db_collection_average_object_size + ) + result["document_db_collection_schema_definition"] = ( + attrs.document_db_collection_schema_definition + ) + result["no_sql_schema_definition"] = attrs.no_sql_schema_definition + result["column_count"] = attrs.column_count + result["row_count"] = attrs.row_count + result["size_bytes"] = attrs.size_bytes + result["table_object_count"] = attrs.table_object_count + result["alias"] = attrs.alias + result["is_temporary"] = attrs.is_temporary + result["is_query_preview"] = attrs.is_query_preview + result["query_preview_config"] = attrs.query_preview_config + result["external_location"] = attrs.external_location + result["external_location_region"] = attrs.external_location_region + result["external_location_format"] = attrs.external_location_format + result["is_partitioned"] = attrs.is_partitioned + result["partition_strategy"] = attrs.partition_strategy + result["partition_count"] = attrs.partition_count + result["table_definition"] = attrs.table_definition + result["partition_list"] = attrs.partition_list + result["is_sharded"] = attrs.is_sharded + result["table_type"] = attrs.table_type + result["iceberg_catalog_name"] = attrs.iceberg_catalog_name + result["iceberg_table_type"] = attrs.iceberg_table_type + result["iceberg_catalog_source"] = attrs.iceberg_catalog_source + result["iceberg_catalog_table_name"] = attrs.iceberg_catalog_table_name + result["table_impala_parameters"] = attrs.table_impala_parameters + result["iceberg_catalog_table_namespace"] = attrs.iceberg_catalog_table_namespace + result["table_external_volume_name"] = attrs.table_external_volume_name + result["iceberg_table_base_location"] = attrs.iceberg_table_base_location + result["table_retention_time"] = attrs.table_retention_time + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _document_db_collection_to_nested( + document_db_collection: DocumentDBCollection, +) -> DocumentDBCollectionNested: + """Convert flat DocumentDBCollection to nested format.""" + attrs = DocumentDBCollectionAttributes() + _populate_document_db_collection_attrs(attrs, document_db_collection) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + document_db_collection, + _DOCUMENT_DB_COLLECTION_REL_FIELDS, + DocumentDBCollectionRelationshipAttributes, + ) + return DocumentDBCollectionNested( + guid=document_db_collection.guid, + type_name=document_db_collection.type_name, + status=document_db_collection.status, + version=document_db_collection.version, + create_time=document_db_collection.create_time, + update_time=document_db_collection.update_time, + created_by=document_db_collection.created_by, + updated_by=document_db_collection.updated_by, + classifications=document_db_collection.classifications, + classification_names=document_db_collection.classification_names, + meanings=document_db_collection.meanings, + labels=document_db_collection.labels, + business_attributes=document_db_collection.business_attributes, + custom_attributes=document_db_collection.custom_attributes, + pending_tasks=document_db_collection.pending_tasks, + proxy=document_db_collection.proxy, + is_incomplete=document_db_collection.is_incomplete, + provenance_type=document_db_collection.provenance_type, + home_id=document_db_collection.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _document_db_collection_from_nested( + nested: DocumentDBCollectionNested, +) -> DocumentDBCollection: + """Convert nested format to flat DocumentDBCollection.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else DocumentDBCollectionAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DOCUMENT_DB_COLLECTION_REL_FIELDS, + DocumentDBCollectionRelationshipAttributes, + ) + return DocumentDBCollection( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_document_db_collection_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _document_db_collection_to_nested_bytes( + document_db_collection: DocumentDBCollection, serde: Serde +) -> bytes: + """Convert flat DocumentDBCollection to nested JSON bytes.""" + return serde.encode(_document_db_collection_to_nested(document_db_collection)) + + +def _document_db_collection_from_nested_bytes( + data: bytes, serde: Serde +) -> DocumentDBCollection: + """Convert nested JSON bytes to flat DocumentDBCollection.""" + nested = serde.decode(data, DocumentDBCollectionNested) + return _document_db_collection_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, + TextField, +) + +DocumentDBCollection.DOCUMENT_DB_COLLECTION_SUBTYPE = KeywordField( + "documentDBCollectionSubtype", "documentDBCollectionSubtype" +) +DocumentDBCollection.DOCUMENT_DB_COLLECTION_IS_CAPPED = BooleanField( + "documentDBCollectionIsCapped", "documentDBCollectionIsCapped" +) +DocumentDBCollection.DOCUMENT_DB_COLLECTION_TIME_FIELD = KeywordField( + "documentDBCollectionTimeField", "documentDBCollectionTimeField" +) +DocumentDBCollection.DOCUMENT_DB_COLLECTION_TIME_GRANULARITY = KeywordField( + "documentDBCollectionTimeGranularity", "documentDBCollectionTimeGranularity" +) +DocumentDBCollection.DOCUMENT_DB_COLLECTION_EXPIRE_AFTER_SECONDS = NumericField( + "documentDBCollectionExpireAfterSeconds", "documentDBCollectionExpireAfterSeconds" +) +DocumentDBCollection.DOCUMENT_DB_COLLECTION_MAXIMUM_DOCUMENT_COUNT = NumericField( + "documentDBCollectionMaximumDocumentCount", + "documentDBCollectionMaximumDocumentCount", +) +DocumentDBCollection.DOCUMENT_DB_COLLECTION_MAX_SIZE = NumericField( + "documentDBCollectionMaxSize", "documentDBCollectionMaxSize" +) +DocumentDBCollection.DOCUMENT_DB_COLLECTION_NUM_ORPHAN_DOCS = NumericField( + "documentDBCollectionNumOrphanDocs", "documentDBCollectionNumOrphanDocs" +) +DocumentDBCollection.DOCUMENT_DB_COLLECTION_NUM_INDEXES = NumericField( + "documentDBCollectionNumIndexes", "documentDBCollectionNumIndexes" +) +DocumentDBCollection.DOCUMENT_DB_COLLECTION_TOTAL_INDEX_SIZE = NumericField( + "documentDBCollectionTotalIndexSize", "documentDBCollectionTotalIndexSize" +) +DocumentDBCollection.DOCUMENT_DB_COLLECTION_AVERAGE_OBJECT_SIZE = NumericField( + "documentDBCollectionAverageObjectSize", "documentDBCollectionAverageObjectSize" +) +DocumentDBCollection.DOCUMENT_DB_COLLECTION_SCHEMA_DEFINITION = TextField( + "documentDBCollectionSchemaDefinition", "documentDBCollectionSchemaDefinition" +) +DocumentDBCollection.NO_SQL_SCHEMA_DEFINITION = KeywordField( + "noSQLSchemaDefinition", "noSQLSchemaDefinition" +) +DocumentDBCollection.COLUMN_COUNT = NumericField("columnCount", "columnCount") +DocumentDBCollection.ROW_COUNT = NumericField("rowCount", "rowCount") +DocumentDBCollection.SIZE_BYTES = NumericField("sizeBytes", "sizeBytes") +DocumentDBCollection.TABLE_OBJECT_COUNT = NumericField( + "tableObjectCount", "tableObjectCount" +) +DocumentDBCollection.ALIAS = KeywordField("alias", "alias") +DocumentDBCollection.IS_TEMPORARY = BooleanField("isTemporary", "isTemporary") +DocumentDBCollection.IS_QUERY_PREVIEW = BooleanField("isQueryPreview", "isQueryPreview") +DocumentDBCollection.QUERY_PREVIEW_CONFIG = KeywordField( + "queryPreviewConfig", "queryPreviewConfig" +) +DocumentDBCollection.EXTERNAL_LOCATION = KeywordField( + "externalLocation", "externalLocation" +) +DocumentDBCollection.EXTERNAL_LOCATION_REGION = KeywordField( + "externalLocationRegion", "externalLocationRegion" +) +DocumentDBCollection.EXTERNAL_LOCATION_FORMAT = KeywordField( + "externalLocationFormat", "externalLocationFormat" +) +DocumentDBCollection.IS_PARTITIONED = BooleanField("isPartitioned", "isPartitioned") +DocumentDBCollection.PARTITION_STRATEGY = KeywordField( + "partitionStrategy", "partitionStrategy" +) +DocumentDBCollection.PARTITION_COUNT = NumericField("partitionCount", "partitionCount") +DocumentDBCollection.TABLE_DEFINITION = KeywordField( + "tableDefinition", "tableDefinition" +) +DocumentDBCollection.PARTITION_LIST = KeywordField("partitionList", "partitionList") +DocumentDBCollection.IS_SHARDED = BooleanField("isSharded", "isSharded") +DocumentDBCollection.TABLE_TYPE = KeywordField("tableType", "tableType") +DocumentDBCollection.ICEBERG_CATALOG_NAME = KeywordField( + "icebergCatalogName", "icebergCatalogName" +) +DocumentDBCollection.ICEBERG_TABLE_TYPE = KeywordField( + "icebergTableType", "icebergTableType" +) +DocumentDBCollection.ICEBERG_CATALOG_SOURCE = KeywordField( + "icebergCatalogSource", "icebergCatalogSource" +) +DocumentDBCollection.ICEBERG_CATALOG_TABLE_NAME = KeywordField( + "icebergCatalogTableName", "icebergCatalogTableName" +) +DocumentDBCollection.TABLE_IMPALA_PARAMETERS = KeywordField( + "tableImpalaParameters", "tableImpalaParameters" +) +DocumentDBCollection.ICEBERG_CATALOG_TABLE_NAMESPACE = KeywordField( + "icebergCatalogTableNamespace", "icebergCatalogTableNamespace" +) +DocumentDBCollection.TABLE_EXTERNAL_VOLUME_NAME = KeywordField( + "tableExternalVolumeName", "tableExternalVolumeName" +) +DocumentDBCollection.ICEBERG_TABLE_BASE_LOCATION = KeywordField( + "icebergTableBaseLocation", "icebergTableBaseLocation" +) +DocumentDBCollection.TABLE_RETENTION_TIME = NumericField( + "tableRetentionTime", "tableRetentionTime" +) +DocumentDBCollection.QUERY_COUNT = NumericField("queryCount", "queryCount") +DocumentDBCollection.QUERY_USER_COUNT = NumericField("queryUserCount", "queryUserCount") +DocumentDBCollection.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +DocumentDBCollection.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +DocumentDBCollection.DATABASE_NAME = KeywordField("databaseName", "databaseName") +DocumentDBCollection.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +DocumentDBCollection.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +DocumentDBCollection.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +DocumentDBCollection.TABLE_NAME = KeywordField("tableName", "tableName") +DocumentDBCollection.TABLE_QUALIFIED_NAME = KeywordField( + "tableQualifiedName", "tableQualifiedName" +) +DocumentDBCollection.VIEW_NAME = KeywordField("viewName", "viewName") +DocumentDBCollection.VIEW_QUALIFIED_NAME = KeywordField( + "viewQualifiedName", "viewQualifiedName" +) +DocumentDBCollection.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +DocumentDBCollection.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +DocumentDBCollection.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +DocumentDBCollection.LAST_PROFILED_AT = NumericField("lastProfiledAt", "lastProfiledAt") +DocumentDBCollection.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +DocumentDBCollection.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +DocumentDBCollection.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +DocumentDBCollection.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +DocumentDBCollection.ANOMALO_CHECKS = RelationField("anomaloChecks") +DocumentDBCollection.APPLICATION = RelationField("application") +DocumentDBCollection.APPLICATION_FIELD = RelationField("applicationField") +DocumentDBCollection.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +DocumentDBCollection.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +DocumentDBCollection.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +DocumentDBCollection.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +DocumentDBCollection.METRICS = RelationField("metrics") +DocumentDBCollection.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +DocumentDBCollection.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +DocumentDBCollection.DBT_MODELS = RelationField("dbtModels") +DocumentDBCollection.SQL_DBT_MODELS = RelationField("sqlDbtModels") +DocumentDBCollection.DBT_TESTS = RelationField("dbtTests") +DocumentDBCollection.DBT_SOURCES = RelationField("dbtSources") +DocumentDBCollection.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +DocumentDBCollection.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +DocumentDBCollection.DOCUMENT_DB_DATABASE = RelationField("documentDBDatabase") +DocumentDBCollection.MEANINGS = RelationField("meanings") +DocumentDBCollection.MC_MONITORS = RelationField("mcMonitors") +DocumentDBCollection.MC_INCIDENTS = RelationField("mcIncidents") +DocumentDBCollection.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +DocumentDBCollection.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +DocumentDBCollection.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +DocumentDBCollection.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +DocumentDBCollection.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +DocumentDBCollection.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +DocumentDBCollection.FILES = RelationField("files") +DocumentDBCollection.LINKS = RelationField("links") +DocumentDBCollection.README = RelationField("readme") +DocumentDBCollection.COLUMNS = RelationField("columns") +DocumentDBCollection.QUERIES = RelationField("queries") +DocumentDBCollection.ATLAN_SCHEMA = RelationField("atlanSchema") +DocumentDBCollection.DIMENSIONS = RelationField("dimensions") +DocumentDBCollection.FACTS = RelationField("facts") +DocumentDBCollection.PARTITIONS = RelationField("partitions") +DocumentDBCollection.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +DocumentDBCollection.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +DocumentDBCollection.SODA_CHECKS = RelationField("sodaChecks") +DocumentDBCollection.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +DocumentDBCollection.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/document_db_database.py b/pyatlan_v9/model/assets/document_db_database.py new file mode 100644 index 000000000..a8f157c2f --- /dev/null +++ b/pyatlan_v9/model/assets/document_db_database.py @@ -0,0 +1,950 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DocumentDBDatabase asset model with flattened inheritance. + +This module provides: +- DocumentDBDatabase: Flat asset class (easy to use) +- DocumentDBDatabaseAttributes: Nested attributes struct (extends AssetAttributes) +- DocumentDBDatabaseNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .fabric_related import RelatedFabricWorkspace +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .snowflake_related import RelatedSnowflakeSemanticLogicalTable +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from .sql_related import RelatedSchema +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .document_db_related import RelatedDocumentDBCollection + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class DocumentDBDatabase(Asset): + """ + Instance of a DocumentDBDatabase in Atlan. + """ + + DOCUMENT_DB_DATABASE_COLLECTION_COUNT: ClassVar[Any] = None + NO_SQL_SCHEMA_DEFINITION: ClassVar[Any] = None + SCHEMA_COUNT: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + DOCUMENT_DB_COLLECTIONS: ClassVar[Any] = None + FABRIC_WORKSPACE: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMAS: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "DocumentDBDatabase" + + document_db_database_collection_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="documentDBDatabaseCollectionCount" + ) + """Number of collections in the database.""" + + no_sql_schema_definition: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="noSQLSchemaDefinition" + ) + """Represents attributes for describing the key schema for the table and indexes.""" + + schema_count: Union[int, None, UnsetType] = UNSET + """Number of schemas in this database.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + document_db_collections: Union[ + List[RelatedDocumentDBCollection], None, UnsetType + ] = msgspec.field(default=UNSET, name="documentDBCollections") + """Collections that exist within this database.""" + + fabric_workspace: Union[RelatedFabricWorkspace, None, UnsetType] = UNSET + """Workspace containing the database.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schemas: Union[List[RelatedSchema], None, UnsetType] = UNSET + """Schemas that exist within this database.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "DocumentDBDatabase" + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + connection_qualified_name: str, + ) -> "DocumentDBDatabase": + """Create a new DocumentDBDatabase asset.""" + validate_required_fields( + ["name", "connection_qualified_name"], [name, connection_qualified_name] + ) + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + return cls( + name=name, + connection_qualified_name=connection_qualified_name, + qualified_name=f"{connection_qualified_name}/{name}", + connector_name=connector_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "DocumentDBDatabase": + """Create a DocumentDBDatabase instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "DocumentDBDatabase": + """Return only fields required for update operations.""" + return DocumentDBDatabase.updater( + qualified_name=self.qualified_name, + name=self.name, + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _document_db_database_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> DocumentDBDatabase: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + DocumentDBDatabase instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _document_db_database_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DocumentDBDatabaseAttributes(AssetAttributes): + """DocumentDBDatabase-specific attributes for nested API format.""" + + document_db_database_collection_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="documentDBDatabaseCollectionCount" + ) + """Number of collections in the database.""" + + no_sql_schema_definition: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="noSQLSchemaDefinition" + ) + """Represents attributes for describing the key schema for the table and indexes.""" + + schema_count: Union[int, None, UnsetType] = UNSET + """Number of schemas in this database.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + +class DocumentDBDatabaseRelationshipAttributes(AssetRelationshipAttributes): + """DocumentDBDatabase-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + document_db_collections: Union[ + List[RelatedDocumentDBCollection], None, UnsetType + ] = msgspec.field(default=UNSET, name="documentDBCollections") + """Collections that exist within this database.""" + + fabric_workspace: Union[RelatedFabricWorkspace, None, UnsetType] = UNSET + """Workspace containing the database.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schemas: Union[List[RelatedSchema], None, UnsetType] = UNSET + """Schemas that exist within this database.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DocumentDBDatabaseNested(AssetNested): + """DocumentDBDatabase in nested API format for high-performance serialization.""" + + attributes: Union[DocumentDBDatabaseAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + DocumentDBDatabaseRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + DocumentDBDatabaseRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + DocumentDBDatabaseRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DOCUMENT_DB_DATABASE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "document_db_collections", + "fabric_workspace", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schemas", + "schema_registry_subjects", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_document_db_database_attrs( + attrs: DocumentDBDatabaseAttributes, obj: DocumentDBDatabase +) -> None: + """Populate DocumentDBDatabase-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.document_db_database_collection_count = ( + obj.document_db_database_collection_count + ) + attrs.no_sql_schema_definition = obj.no_sql_schema_definition + attrs.schema_count = obj.schema_count + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + + +def _extract_document_db_database_attrs(attrs: DocumentDBDatabaseAttributes) -> dict: + """Extract all DocumentDBDatabase attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["document_db_database_collection_count"] = ( + attrs.document_db_database_collection_count + ) + result["no_sql_schema_definition"] = attrs.no_sql_schema_definition + result["schema_count"] = attrs.schema_count + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _document_db_database_to_nested( + document_db_database: DocumentDBDatabase, +) -> DocumentDBDatabaseNested: + """Convert flat DocumentDBDatabase to nested format.""" + attrs = DocumentDBDatabaseAttributes() + _populate_document_db_database_attrs(attrs, document_db_database) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + document_db_database, + _DOCUMENT_DB_DATABASE_REL_FIELDS, + DocumentDBDatabaseRelationshipAttributes, + ) + return DocumentDBDatabaseNested( + guid=document_db_database.guid, + type_name=document_db_database.type_name, + status=document_db_database.status, + version=document_db_database.version, + create_time=document_db_database.create_time, + update_time=document_db_database.update_time, + created_by=document_db_database.created_by, + updated_by=document_db_database.updated_by, + classifications=document_db_database.classifications, + classification_names=document_db_database.classification_names, + meanings=document_db_database.meanings, + labels=document_db_database.labels, + business_attributes=document_db_database.business_attributes, + custom_attributes=document_db_database.custom_attributes, + pending_tasks=document_db_database.pending_tasks, + proxy=document_db_database.proxy, + is_incomplete=document_db_database.is_incomplete, + provenance_type=document_db_database.provenance_type, + home_id=document_db_database.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _document_db_database_from_nested( + nested: DocumentDBDatabaseNested, +) -> DocumentDBDatabase: + """Convert nested format to flat DocumentDBDatabase.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else DocumentDBDatabaseAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DOCUMENT_DB_DATABASE_REL_FIELDS, + DocumentDBDatabaseRelationshipAttributes, + ) + return DocumentDBDatabase( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_document_db_database_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _document_db_database_to_nested_bytes( + document_db_database: DocumentDBDatabase, serde: Serde +) -> bytes: + """Convert flat DocumentDBDatabase to nested JSON bytes.""" + return serde.encode(_document_db_database_to_nested(document_db_database)) + + +def _document_db_database_from_nested_bytes( + data: bytes, serde: Serde +) -> DocumentDBDatabase: + """Convert nested JSON bytes to flat DocumentDBDatabase.""" + nested = serde.decode(data, DocumentDBDatabaseNested) + return _document_db_database_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, +) + +DocumentDBDatabase.DOCUMENT_DB_DATABASE_COLLECTION_COUNT = NumericField( + "documentDBDatabaseCollectionCount", "documentDBDatabaseCollectionCount" +) +DocumentDBDatabase.NO_SQL_SCHEMA_DEFINITION = KeywordField( + "noSQLSchemaDefinition", "noSQLSchemaDefinition" +) +DocumentDBDatabase.SCHEMA_COUNT = NumericField("schemaCount", "schemaCount") +DocumentDBDatabase.QUERY_COUNT = NumericField("queryCount", "queryCount") +DocumentDBDatabase.QUERY_USER_COUNT = NumericField("queryUserCount", "queryUserCount") +DocumentDBDatabase.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +DocumentDBDatabase.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +DocumentDBDatabase.DATABASE_NAME = KeywordField("databaseName", "databaseName") +DocumentDBDatabase.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +DocumentDBDatabase.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +DocumentDBDatabase.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +DocumentDBDatabase.TABLE_NAME = KeywordField("tableName", "tableName") +DocumentDBDatabase.TABLE_QUALIFIED_NAME = KeywordField( + "tableQualifiedName", "tableQualifiedName" +) +DocumentDBDatabase.VIEW_NAME = KeywordField("viewName", "viewName") +DocumentDBDatabase.VIEW_QUALIFIED_NAME = KeywordField( + "viewQualifiedName", "viewQualifiedName" +) +DocumentDBDatabase.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +DocumentDBDatabase.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +DocumentDBDatabase.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +DocumentDBDatabase.LAST_PROFILED_AT = NumericField("lastProfiledAt", "lastProfiledAt") +DocumentDBDatabase.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +DocumentDBDatabase.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +DocumentDBDatabase.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +DocumentDBDatabase.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +DocumentDBDatabase.ANOMALO_CHECKS = RelationField("anomaloChecks") +DocumentDBDatabase.APPLICATION = RelationField("application") +DocumentDBDatabase.APPLICATION_FIELD = RelationField("applicationField") +DocumentDBDatabase.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +DocumentDBDatabase.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +DocumentDBDatabase.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +DocumentDBDatabase.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +DocumentDBDatabase.METRICS = RelationField("metrics") +DocumentDBDatabase.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +DocumentDBDatabase.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +DocumentDBDatabase.DBT_MODELS = RelationField("dbtModels") +DocumentDBDatabase.SQL_DBT_MODELS = RelationField("sqlDbtModels") +DocumentDBDatabase.DBT_TESTS = RelationField("dbtTests") +DocumentDBDatabase.DBT_SOURCES = RelationField("dbtSources") +DocumentDBDatabase.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +DocumentDBDatabase.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +DocumentDBDatabase.DOCUMENT_DB_COLLECTIONS = RelationField("documentDBCollections") +DocumentDBDatabase.FABRIC_WORKSPACE = RelationField("fabricWorkspace") +DocumentDBDatabase.MEANINGS = RelationField("meanings") +DocumentDBDatabase.MC_MONITORS = RelationField("mcMonitors") +DocumentDBDatabase.MC_INCIDENTS = RelationField("mcIncidents") +DocumentDBDatabase.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +DocumentDBDatabase.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +DocumentDBDatabase.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +DocumentDBDatabase.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +DocumentDBDatabase.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +DocumentDBDatabase.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +DocumentDBDatabase.FILES = RelationField("files") +DocumentDBDatabase.LINKS = RelationField("links") +DocumentDBDatabase.README = RelationField("readme") +DocumentDBDatabase.SCHEMAS = RelationField("schemas") +DocumentDBDatabase.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +DocumentDBDatabase.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +DocumentDBDatabase.SODA_CHECKS = RelationField("sodaChecks") +DocumentDBDatabase.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +DocumentDBDatabase.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/document_db_related.py b/pyatlan_v9/model/assets/document_db_related.py new file mode 100644 index 000000000..5c9ebda6e --- /dev/null +++ b/pyatlan_v9/model/assets/document_db_related.py @@ -0,0 +1,136 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for DocumentDB module. + +This module contains all Related{Type} classes for the DocumentDB type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedNoSQL +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedDocumentDB", + "RelatedDocumentDBCollection", + "RelatedDocumentDBDatabase", +] + + +class RelatedDocumentDB(RelatedNoSQL): + """ + Related entity reference for DocumentDB assets. + + Extends RelatedNoSQL with DocumentDB-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DocumentDB" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DocumentDB" + + +class RelatedDocumentDBCollection(RelatedDocumentDB): + """ + Related entity reference for DocumentDBCollection assets. + + Extends RelatedDocumentDB with DocumentDBCollection-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DocumentDBCollection" so it serializes correctly + + document_db_collection_subtype: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="documentDBCollectionSubtype" + ) + """Subtype of a DocumentDBCollection, for example: Capped, Time Series, etc.""" + + document_db_collection_is_capped: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="documentDBCollectionIsCapped" + ) + """Whether the collection is capped (true) or not (false).""" + + document_db_collection_time_field: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="documentDBCollectionTimeField" + ) + """Name of the field containing the date in each time series document.""" + + document_db_collection_time_granularity: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="documentDBCollectionTimeGranularity") + ) + """Closest match to the time span between consecutive incoming measurements.""" + + document_db_collection_expire_after_seconds: Union[int, None, UnsetType] = ( + msgspec.field(default=UNSET, name="documentDBCollectionExpireAfterSeconds") + ) + """Seconds after which documents in a time series collection or clustered collection expire.""" + + document_db_collection_maximum_document_count: Union[int, None, UnsetType] = ( + msgspec.field(default=UNSET, name="documentDBCollectionMaximumDocumentCount") + ) + """Maximum number of documents allowed in a capped collection.""" + + document_db_collection_max_size: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="documentDBCollectionMaxSize" + ) + """Maximum size allowed in a capped collection.""" + + document_db_collection_num_orphan_docs: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="documentDBCollectionNumOrphanDocs" + ) + """Number of orphaned documents in the collection.""" + + document_db_collection_num_indexes: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="documentDBCollectionNumIndexes" + ) + """Number of indexes in the collection.""" + + document_db_collection_total_index_size: Union[int, None, UnsetType] = ( + msgspec.field(default=UNSET, name="documentDBCollectionTotalIndexSize") + ) + """Total size of all indexes.""" + + document_db_collection_average_object_size: Union[int, None, UnsetType] = ( + msgspec.field(default=UNSET, name="documentDBCollectionAverageObjectSize") + ) + """Average size of an object in the collection.""" + + document_db_collection_schema_definition: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="documentDBCollectionSchemaDefinition") + ) + """Definition of the schema applicable for the collection.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DocumentDBCollection" + + +class RelatedDocumentDBDatabase(RelatedDocumentDB): + """ + Related entity reference for DocumentDBDatabase assets. + + Extends RelatedDocumentDB with DocumentDBDatabase-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DocumentDBDatabase" so it serializes correctly + + document_db_database_collection_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="documentDBDatabaseCollectionCount" + ) + """Number of collections in the database.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DocumentDBDatabase" diff --git a/pyatlan_v9/model/assets/domo.py b/pyatlan_v9/model/assets/domo.py new file mode 100644 index 000000000..8bad2e342 --- /dev/null +++ b/pyatlan_v9/model/assets/domo.py @@ -0,0 +1,541 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Domo asset model with flattened inheritance. + +This module provides: +- Domo: Flat asset class (easy to use) +- DomoAttributes: Nested attributes struct (extends AssetAttributes) +- DomoNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Domo(Asset): + """ + Base class for Domo assets. + """ + + DOMO_ID: ClassVar[Any] = None + DOMO_OWNER_ID: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Domo" + + domo_id: Union[str, None, UnsetType] = UNSET + """Id of the Domo dataset.""" + + domo_owner_id: Union[str, None, UnsetType] = UNSET + """Id of the owner of the Domo dataset.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Domo" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _domo_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Domo: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Domo instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _domo_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DomoAttributes(AssetAttributes): + """Domo-specific attributes for nested API format.""" + + domo_id: Union[str, None, UnsetType] = UNSET + """Id of the Domo dataset.""" + + domo_owner_id: Union[str, None, UnsetType] = UNSET + """Id of the owner of the Domo dataset.""" + + +class DomoRelationshipAttributes(AssetRelationshipAttributes): + """Domo-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DomoNested(AssetNested): + """Domo in nested API format for high-performance serialization.""" + + attributes: Union[DomoAttributes, UnsetType] = UNSET + relationship_attributes: Union[DomoRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[DomoRelationshipAttributes, UnsetType] = UNSET + remove_relationship_attributes: Union[DomoRelationshipAttributes, UnsetType] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DOMO_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_domo_attrs(attrs: DomoAttributes, obj: Domo) -> None: + """Populate Domo-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.domo_id = obj.domo_id + attrs.domo_owner_id = obj.domo_owner_id + + +def _extract_domo_attrs(attrs: DomoAttributes) -> dict: + """Extract all Domo attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["domo_id"] = attrs.domo_id + result["domo_owner_id"] = attrs.domo_owner_id + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _domo_to_nested(domo: Domo) -> DomoNested: + """Convert flat Domo to nested format.""" + attrs = DomoAttributes() + _populate_domo_attrs(attrs, domo) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + domo, _DOMO_REL_FIELDS, DomoRelationshipAttributes + ) + return DomoNested( + guid=domo.guid, + type_name=domo.type_name, + status=domo.status, + version=domo.version, + create_time=domo.create_time, + update_time=domo.update_time, + created_by=domo.created_by, + updated_by=domo.updated_by, + classifications=domo.classifications, + classification_names=domo.classification_names, + meanings=domo.meanings, + labels=domo.labels, + business_attributes=domo.business_attributes, + custom_attributes=domo.custom_attributes, + pending_tasks=domo.pending_tasks, + proxy=domo.proxy, + is_incomplete=domo.is_incomplete, + provenance_type=domo.provenance_type, + home_id=domo.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _domo_from_nested(nested: DomoNested) -> Domo: + """Convert nested format to flat Domo.""" + attrs = nested.attributes if nested.attributes is not UNSET else DomoAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DOMO_REL_FIELDS, + DomoRelationshipAttributes, + ) + return Domo( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_domo_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _domo_to_nested_bytes(domo: Domo, serde: Serde) -> bytes: + """Convert flat Domo to nested JSON bytes.""" + return serde.encode(_domo_to_nested(domo)) + + +def _domo_from_nested_bytes(data: bytes, serde: Serde) -> Domo: + """Convert nested JSON bytes to flat Domo.""" + nested = serde.decode(data, DomoNested) + return _domo_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +Domo.DOMO_ID = KeywordField("domoId", "domoId") +Domo.DOMO_OWNER_ID = KeywordField("domoOwnerId", "domoOwnerId") +Domo.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Domo.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Domo.ANOMALO_CHECKS = RelationField("anomaloChecks") +Domo.APPLICATION = RelationField("application") +Domo.APPLICATION_FIELD = RelationField("applicationField") +Domo.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Domo.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Domo.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Domo.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Domo.METRICS = RelationField("metrics") +Domo.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Domo.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Domo.MEANINGS = RelationField("meanings") +Domo.MC_MONITORS = RelationField("mcMonitors") +Domo.MC_INCIDENTS = RelationField("mcIncidents") +Domo.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Domo.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Domo.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Domo.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Domo.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Domo.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Domo.FILES = RelationField("files") +Domo.LINKS = RelationField("links") +Domo.README = RelationField("readme") +Domo.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Domo.SODA_CHECKS = RelationField("sodaChecks") +Domo.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Domo.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/domo_card.py b/pyatlan_v9/model/assets/domo_card.py new file mode 100644 index 000000000..6db81b7f8 --- /dev/null +++ b/pyatlan_v9/model/assets/domo_card.py @@ -0,0 +1,607 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DomoCard asset model with flattened inheritance. + +This module provides: +- DomoCard: Flat asset class (easy to use) +- DomoCardAttributes: Nested attributes struct (extends AssetAttributes) +- DomoCardNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .domo_related import RelatedDomoDashboard, RelatedDomoDataset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class DomoCard(Asset): + """ + Instance of a Domo Card in Atlan. + """ + + DOMO_CARD_TYPE: ClassVar[Any] = None + DOMO_CARD_TYPE_VALUE: ClassVar[Any] = None + DOMO_CARD_DASHBOARD_COUNT: ClassVar[Any] = None + DOMO_ID: ClassVar[Any] = None + DOMO_OWNER_ID: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DOMO_DASHBOARDS: ClassVar[Any] = None + DOMO_DATASET: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "DomoCard" + + domo_card_type: Union[str, None, UnsetType] = UNSET + """Type of the Domo Card.""" + + domo_card_type_value: Union[str, None, UnsetType] = UNSET + """Type of the Domo Card.""" + + domo_card_dashboard_count: Union[int, None, UnsetType] = UNSET + """Number of dashboards linked to this card.""" + + domo_id: Union[str, None, UnsetType] = UNSET + """Id of the Domo dataset.""" + + domo_owner_id: Union[str, None, UnsetType] = UNSET + """Id of the owner of the Domo dataset.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + domo_dashboards: Union[List[RelatedDomoDashboard], None, UnsetType] = UNSET + """Domo Dashboards that are associated with this Domo Card.""" + + domo_dataset: Union[RelatedDomoDataset, None, UnsetType] = UNSET + """Domo Dataset that contains this Domo Card.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "DomoCard" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _domo_card_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> DomoCard: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + DomoCard instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _domo_card_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DomoCardAttributes(AssetAttributes): + """DomoCard-specific attributes for nested API format.""" + + domo_card_type: Union[str, None, UnsetType] = UNSET + """Type of the Domo Card.""" + + domo_card_type_value: Union[str, None, UnsetType] = UNSET + """Type of the Domo Card.""" + + domo_card_dashboard_count: Union[int, None, UnsetType] = UNSET + """Number of dashboards linked to this card.""" + + domo_id: Union[str, None, UnsetType] = UNSET + """Id of the Domo dataset.""" + + domo_owner_id: Union[str, None, UnsetType] = UNSET + """Id of the owner of the Domo dataset.""" + + +class DomoCardRelationshipAttributes(AssetRelationshipAttributes): + """DomoCard-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + domo_dashboards: Union[List[RelatedDomoDashboard], None, UnsetType] = UNSET + """Domo Dashboards that are associated with this Domo Card.""" + + domo_dataset: Union[RelatedDomoDataset, None, UnsetType] = UNSET + """Domo Dataset that contains this Domo Card.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DomoCardNested(AssetNested): + """DomoCard in nested API format for high-performance serialization.""" + + attributes: Union[DomoCardAttributes, UnsetType] = UNSET + relationship_attributes: Union[DomoCardRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[DomoCardRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[DomoCardRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DOMO_CARD_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "domo_dashboards", + "domo_dataset", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_domo_card_attrs(attrs: DomoCardAttributes, obj: DomoCard) -> None: + """Populate DomoCard-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.domo_card_type = obj.domo_card_type + attrs.domo_card_type_value = obj.domo_card_type_value + attrs.domo_card_dashboard_count = obj.domo_card_dashboard_count + attrs.domo_id = obj.domo_id + attrs.domo_owner_id = obj.domo_owner_id + + +def _extract_domo_card_attrs(attrs: DomoCardAttributes) -> dict: + """Extract all DomoCard attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["domo_card_type"] = attrs.domo_card_type + result["domo_card_type_value"] = attrs.domo_card_type_value + result["domo_card_dashboard_count"] = attrs.domo_card_dashboard_count + result["domo_id"] = attrs.domo_id + result["domo_owner_id"] = attrs.domo_owner_id + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _domo_card_to_nested(domo_card: DomoCard) -> DomoCardNested: + """Convert flat DomoCard to nested format.""" + attrs = DomoCardAttributes() + _populate_domo_card_attrs(attrs, domo_card) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + domo_card, _DOMO_CARD_REL_FIELDS, DomoCardRelationshipAttributes + ) + return DomoCardNested( + guid=domo_card.guid, + type_name=domo_card.type_name, + status=domo_card.status, + version=domo_card.version, + create_time=domo_card.create_time, + update_time=domo_card.update_time, + created_by=domo_card.created_by, + updated_by=domo_card.updated_by, + classifications=domo_card.classifications, + classification_names=domo_card.classification_names, + meanings=domo_card.meanings, + labels=domo_card.labels, + business_attributes=domo_card.business_attributes, + custom_attributes=domo_card.custom_attributes, + pending_tasks=domo_card.pending_tasks, + proxy=domo_card.proxy, + is_incomplete=domo_card.is_incomplete, + provenance_type=domo_card.provenance_type, + home_id=domo_card.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _domo_card_from_nested(nested: DomoCardNested) -> DomoCard: + """Convert nested format to flat DomoCard.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else DomoCardAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DOMO_CARD_REL_FIELDS, + DomoCardRelationshipAttributes, + ) + return DomoCard( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_domo_card_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _domo_card_to_nested_bytes(domo_card: DomoCard, serde: Serde) -> bytes: + """Convert flat DomoCard to nested JSON bytes.""" + return serde.encode(_domo_card_to_nested(domo_card)) + + +def _domo_card_from_nested_bytes(data: bytes, serde: Serde) -> DomoCard: + """Convert nested JSON bytes to flat DomoCard.""" + nested = serde.decode(data, DomoCardNested) + return _domo_card_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +DomoCard.DOMO_CARD_TYPE = KeywordField("domoCardType", "domoCardType") +DomoCard.DOMO_CARD_TYPE_VALUE = KeywordField("domoCardTypeValue", "domoCardTypeValue") +DomoCard.DOMO_CARD_DASHBOARD_COUNT = NumericField( + "domoCardDashboardCount", "domoCardDashboardCount" +) +DomoCard.DOMO_ID = KeywordField("domoId", "domoId") +DomoCard.DOMO_OWNER_ID = KeywordField("domoOwnerId", "domoOwnerId") +DomoCard.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +DomoCard.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +DomoCard.ANOMALO_CHECKS = RelationField("anomaloChecks") +DomoCard.APPLICATION = RelationField("application") +DomoCard.APPLICATION_FIELD = RelationField("applicationField") +DomoCard.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +DomoCard.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +DomoCard.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +DomoCard.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +DomoCard.METRICS = RelationField("metrics") +DomoCard.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +DomoCard.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +DomoCard.DOMO_DASHBOARDS = RelationField("domoDashboards") +DomoCard.DOMO_DATASET = RelationField("domoDataset") +DomoCard.MEANINGS = RelationField("meanings") +DomoCard.MC_MONITORS = RelationField("mcMonitors") +DomoCard.MC_INCIDENTS = RelationField("mcIncidents") +DomoCard.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +DomoCard.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +DomoCard.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +DomoCard.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +DomoCard.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +DomoCard.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +DomoCard.FILES = RelationField("files") +DomoCard.LINKS = RelationField("links") +DomoCard.README = RelationField("readme") +DomoCard.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +DomoCard.SODA_CHECKS = RelationField("sodaChecks") +DomoCard.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +DomoCard.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/domo_dashboard.py b/pyatlan_v9/model/assets/domo_dashboard.py new file mode 100644 index 000000000..a4d0b4c18 --- /dev/null +++ b/pyatlan_v9/model/assets/domo_dashboard.py @@ -0,0 +1,604 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DomoDashboard asset model with flattened inheritance. + +This module provides: +- DomoDashboard: Flat asset class (easy to use) +- DomoDashboardAttributes: Nested attributes struct (extends AssetAttributes) +- DomoDashboardNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .domo_related import RelatedDomoCard, RelatedDomoDashboard + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class DomoDashboard(Asset): + """ + Instance of a Domo Dashboard in Atlan. + """ + + DOMO_DASHBOARD_CARD_COUNT: ClassVar[Any] = None + DOMO_ID: ClassVar[Any] = None + DOMO_OWNER_ID: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DOMO_CARDS: ClassVar[Any] = None + DOMO_DASHBOARD_CHILDREN: ClassVar[Any] = None + DOMO_DASHBOARD_PARENT: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "DomoDashboard" + + domo_dashboard_card_count: Union[int, None, UnsetType] = UNSET + """Number of cards linked to this dashboard.""" + + domo_id: Union[str, None, UnsetType] = UNSET + """Id of the Domo dataset.""" + + domo_owner_id: Union[str, None, UnsetType] = UNSET + """Id of the owner of the Domo dataset.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + domo_cards: Union[List[RelatedDomoCard], None, UnsetType] = UNSET + """Domo Cards that associate with this Domo Dashboard.""" + + domo_dashboard_children: Union[List[RelatedDomoDashboard], None, UnsetType] = UNSET + """Child Domo Dashboards that are contained by this parent Domo Dashboard.""" + + domo_dashboard_parent: Union[RelatedDomoDashboard, None, UnsetType] = UNSET + """Parent Domo Dashboard that contains this child Domo Dashboard.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "DomoDashboard" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _domo_dashboard_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> DomoDashboard: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + DomoDashboard instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _domo_dashboard_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DomoDashboardAttributes(AssetAttributes): + """DomoDashboard-specific attributes for nested API format.""" + + domo_dashboard_card_count: Union[int, None, UnsetType] = UNSET + """Number of cards linked to this dashboard.""" + + domo_id: Union[str, None, UnsetType] = UNSET + """Id of the Domo dataset.""" + + domo_owner_id: Union[str, None, UnsetType] = UNSET + """Id of the owner of the Domo dataset.""" + + +class DomoDashboardRelationshipAttributes(AssetRelationshipAttributes): + """DomoDashboard-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + domo_cards: Union[List[RelatedDomoCard], None, UnsetType] = UNSET + """Domo Cards that associate with this Domo Dashboard.""" + + domo_dashboard_children: Union[List[RelatedDomoDashboard], None, UnsetType] = UNSET + """Child Domo Dashboards that are contained by this parent Domo Dashboard.""" + + domo_dashboard_parent: Union[RelatedDomoDashboard, None, UnsetType] = UNSET + """Parent Domo Dashboard that contains this child Domo Dashboard.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DomoDashboardNested(AssetNested): + """DomoDashboard in nested API format for high-performance serialization.""" + + attributes: Union[DomoDashboardAttributes, UnsetType] = UNSET + relationship_attributes: Union[DomoDashboardRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + DomoDashboardRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + DomoDashboardRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DOMO_DASHBOARD_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "domo_cards", + "domo_dashboard_children", + "domo_dashboard_parent", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_domo_dashboard_attrs( + attrs: DomoDashboardAttributes, obj: DomoDashboard +) -> None: + """Populate DomoDashboard-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.domo_dashboard_card_count = obj.domo_dashboard_card_count + attrs.domo_id = obj.domo_id + attrs.domo_owner_id = obj.domo_owner_id + + +def _extract_domo_dashboard_attrs(attrs: DomoDashboardAttributes) -> dict: + """Extract all DomoDashboard attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["domo_dashboard_card_count"] = attrs.domo_dashboard_card_count + result["domo_id"] = attrs.domo_id + result["domo_owner_id"] = attrs.domo_owner_id + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _domo_dashboard_to_nested(domo_dashboard: DomoDashboard) -> DomoDashboardNested: + """Convert flat DomoDashboard to nested format.""" + attrs = DomoDashboardAttributes() + _populate_domo_dashboard_attrs(attrs, domo_dashboard) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + domo_dashboard, _DOMO_DASHBOARD_REL_FIELDS, DomoDashboardRelationshipAttributes + ) + return DomoDashboardNested( + guid=domo_dashboard.guid, + type_name=domo_dashboard.type_name, + status=domo_dashboard.status, + version=domo_dashboard.version, + create_time=domo_dashboard.create_time, + update_time=domo_dashboard.update_time, + created_by=domo_dashboard.created_by, + updated_by=domo_dashboard.updated_by, + classifications=domo_dashboard.classifications, + classification_names=domo_dashboard.classification_names, + meanings=domo_dashboard.meanings, + labels=domo_dashboard.labels, + business_attributes=domo_dashboard.business_attributes, + custom_attributes=domo_dashboard.custom_attributes, + pending_tasks=domo_dashboard.pending_tasks, + proxy=domo_dashboard.proxy, + is_incomplete=domo_dashboard.is_incomplete, + provenance_type=domo_dashboard.provenance_type, + home_id=domo_dashboard.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _domo_dashboard_from_nested(nested: DomoDashboardNested) -> DomoDashboard: + """Convert nested format to flat DomoDashboard.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else DomoDashboardAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DOMO_DASHBOARD_REL_FIELDS, + DomoDashboardRelationshipAttributes, + ) + return DomoDashboard( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_domo_dashboard_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _domo_dashboard_to_nested_bytes( + domo_dashboard: DomoDashboard, serde: Serde +) -> bytes: + """Convert flat DomoDashboard to nested JSON bytes.""" + return serde.encode(_domo_dashboard_to_nested(domo_dashboard)) + + +def _domo_dashboard_from_nested_bytes(data: bytes, serde: Serde) -> DomoDashboard: + """Convert nested JSON bytes to flat DomoDashboard.""" + nested = serde.decode(data, DomoDashboardNested) + return _domo_dashboard_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +DomoDashboard.DOMO_DASHBOARD_CARD_COUNT = NumericField( + "domoDashboardCardCount", "domoDashboardCardCount" +) +DomoDashboard.DOMO_ID = KeywordField("domoId", "domoId") +DomoDashboard.DOMO_OWNER_ID = KeywordField("domoOwnerId", "domoOwnerId") +DomoDashboard.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +DomoDashboard.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +DomoDashboard.ANOMALO_CHECKS = RelationField("anomaloChecks") +DomoDashboard.APPLICATION = RelationField("application") +DomoDashboard.APPLICATION_FIELD = RelationField("applicationField") +DomoDashboard.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +DomoDashboard.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +DomoDashboard.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +DomoDashboard.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +DomoDashboard.METRICS = RelationField("metrics") +DomoDashboard.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +DomoDashboard.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +DomoDashboard.DOMO_CARDS = RelationField("domoCards") +DomoDashboard.DOMO_DASHBOARD_CHILDREN = RelationField("domoDashboardChildren") +DomoDashboard.DOMO_DASHBOARD_PARENT = RelationField("domoDashboardParent") +DomoDashboard.MEANINGS = RelationField("meanings") +DomoDashboard.MC_MONITORS = RelationField("mcMonitors") +DomoDashboard.MC_INCIDENTS = RelationField("mcIncidents") +DomoDashboard.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +DomoDashboard.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +DomoDashboard.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +DomoDashboard.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +DomoDashboard.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +DomoDashboard.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +DomoDashboard.FILES = RelationField("files") +DomoDashboard.LINKS = RelationField("links") +DomoDashboard.README = RelationField("readme") +DomoDashboard.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +DomoDashboard.SODA_CHECKS = RelationField("sodaChecks") +DomoDashboard.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +DomoDashboard.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/domo_dataset.py b/pyatlan_v9/model/assets/domo_dataset.py new file mode 100644 index 000000000..c7dbf19c3 --- /dev/null +++ b/pyatlan_v9/model/assets/domo_dataset.py @@ -0,0 +1,631 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DomoDataset asset model with flattened inheritance. + +This module provides: +- DomoDataset: Flat asset class (easy to use) +- DomoDatasetAttributes: Nested attributes struct (extends AssetAttributes) +- DomoDatasetNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .domo_related import RelatedDomoCard, RelatedDomoDatasetColumn + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class DomoDataset(Asset): + """ + Instance of a Domo Dataset in Atlan. + """ + + DOMO_DATASET_ROW_COUNT: ClassVar[Any] = None + DOMO_DATASET_COLUMN_COUNT: ClassVar[Any] = None + DOMO_DATASET_TYPE: ClassVar[Any] = None + DOMO_DATASET_CARD_COUNT: ClassVar[Any] = None + DOMO_DATASET_LAST_RUN: ClassVar[Any] = None + DOMO_ID: ClassVar[Any] = None + DOMO_OWNER_ID: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DOMO_CARDS: ClassVar[Any] = None + DOMO_DATASET_COLUMNS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "DomoDataset" + + domo_dataset_row_count: Union[int, None, UnsetType] = UNSET + """Number of rows in the Domo dataset.""" + + domo_dataset_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in the Domo dataset.""" + + domo_dataset_type: Union[str, None, UnsetType] = UNSET + """Type of Domo dataset.""" + + domo_dataset_card_count: Union[int, None, UnsetType] = UNSET + """Number of cards linked to the Domo dataset.""" + + domo_dataset_last_run: Union[str, None, UnsetType] = UNSET + """An ISO-8601 representation of the time the DataSet was last run.""" + + domo_id: Union[str, None, UnsetType] = UNSET + """Id of the Domo dataset.""" + + domo_owner_id: Union[str, None, UnsetType] = UNSET + """Id of the owner of the Domo dataset.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + domo_cards: Union[List[RelatedDomoCard], None, UnsetType] = UNSET + """Domo Cards that are contained by this Domo Dataset.""" + + domo_dataset_columns: Union[List[RelatedDomoDatasetColumn], None, UnsetType] = UNSET + """Domo Dataset Columns that are contained by this Domo Dataset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "DomoDataset" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _domo_dataset_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> DomoDataset: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + DomoDataset instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _domo_dataset_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DomoDatasetAttributes(AssetAttributes): + """DomoDataset-specific attributes for nested API format.""" + + domo_dataset_row_count: Union[int, None, UnsetType] = UNSET + """Number of rows in the Domo dataset.""" + + domo_dataset_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in the Domo dataset.""" + + domo_dataset_type: Union[str, None, UnsetType] = UNSET + """Type of Domo dataset.""" + + domo_dataset_card_count: Union[int, None, UnsetType] = UNSET + """Number of cards linked to the Domo dataset.""" + + domo_dataset_last_run: Union[str, None, UnsetType] = UNSET + """An ISO-8601 representation of the time the DataSet was last run.""" + + domo_id: Union[str, None, UnsetType] = UNSET + """Id of the Domo dataset.""" + + domo_owner_id: Union[str, None, UnsetType] = UNSET + """Id of the owner of the Domo dataset.""" + + +class DomoDatasetRelationshipAttributes(AssetRelationshipAttributes): + """DomoDataset-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + domo_cards: Union[List[RelatedDomoCard], None, UnsetType] = UNSET + """Domo Cards that are contained by this Domo Dataset.""" + + domo_dataset_columns: Union[List[RelatedDomoDatasetColumn], None, UnsetType] = UNSET + """Domo Dataset Columns that are contained by this Domo Dataset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DomoDatasetNested(AssetNested): + """DomoDataset in nested API format for high-performance serialization.""" + + attributes: Union[DomoDatasetAttributes, UnsetType] = UNSET + relationship_attributes: Union[DomoDatasetRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + DomoDatasetRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + DomoDatasetRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DOMO_DATASET_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "domo_cards", + "domo_dataset_columns", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_domo_dataset_attrs( + attrs: DomoDatasetAttributes, obj: DomoDataset +) -> None: + """Populate DomoDataset-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.domo_dataset_row_count = obj.domo_dataset_row_count + attrs.domo_dataset_column_count = obj.domo_dataset_column_count + attrs.domo_dataset_type = obj.domo_dataset_type + attrs.domo_dataset_card_count = obj.domo_dataset_card_count + attrs.domo_dataset_last_run = obj.domo_dataset_last_run + attrs.domo_id = obj.domo_id + attrs.domo_owner_id = obj.domo_owner_id + + +def _extract_domo_dataset_attrs(attrs: DomoDatasetAttributes) -> dict: + """Extract all DomoDataset attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["domo_dataset_row_count"] = attrs.domo_dataset_row_count + result["domo_dataset_column_count"] = attrs.domo_dataset_column_count + result["domo_dataset_type"] = attrs.domo_dataset_type + result["domo_dataset_card_count"] = attrs.domo_dataset_card_count + result["domo_dataset_last_run"] = attrs.domo_dataset_last_run + result["domo_id"] = attrs.domo_id + result["domo_owner_id"] = attrs.domo_owner_id + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _domo_dataset_to_nested(domo_dataset: DomoDataset) -> DomoDatasetNested: + """Convert flat DomoDataset to nested format.""" + attrs = DomoDatasetAttributes() + _populate_domo_dataset_attrs(attrs, domo_dataset) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + domo_dataset, _DOMO_DATASET_REL_FIELDS, DomoDatasetRelationshipAttributes + ) + return DomoDatasetNested( + guid=domo_dataset.guid, + type_name=domo_dataset.type_name, + status=domo_dataset.status, + version=domo_dataset.version, + create_time=domo_dataset.create_time, + update_time=domo_dataset.update_time, + created_by=domo_dataset.created_by, + updated_by=domo_dataset.updated_by, + classifications=domo_dataset.classifications, + classification_names=domo_dataset.classification_names, + meanings=domo_dataset.meanings, + labels=domo_dataset.labels, + business_attributes=domo_dataset.business_attributes, + custom_attributes=domo_dataset.custom_attributes, + pending_tasks=domo_dataset.pending_tasks, + proxy=domo_dataset.proxy, + is_incomplete=domo_dataset.is_incomplete, + provenance_type=domo_dataset.provenance_type, + home_id=domo_dataset.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _domo_dataset_from_nested(nested: DomoDatasetNested) -> DomoDataset: + """Convert nested format to flat DomoDataset.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else DomoDatasetAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DOMO_DATASET_REL_FIELDS, + DomoDatasetRelationshipAttributes, + ) + return DomoDataset( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_domo_dataset_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _domo_dataset_to_nested_bytes(domo_dataset: DomoDataset, serde: Serde) -> bytes: + """Convert flat DomoDataset to nested JSON bytes.""" + return serde.encode(_domo_dataset_to_nested(domo_dataset)) + + +def _domo_dataset_from_nested_bytes(data: bytes, serde: Serde) -> DomoDataset: + """Convert nested JSON bytes to flat DomoDataset.""" + nested = serde.decode(data, DomoDatasetNested) + return _domo_dataset_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +DomoDataset.DOMO_DATASET_ROW_COUNT = NumericField( + "domoDatasetRowCount", "domoDatasetRowCount" +) +DomoDataset.DOMO_DATASET_COLUMN_COUNT = NumericField( + "domoDatasetColumnCount", "domoDatasetColumnCount" +) +DomoDataset.DOMO_DATASET_TYPE = KeywordTextField( + "domoDatasetType", "domoDatasetType", "domoDatasetType.text" +) +DomoDataset.DOMO_DATASET_CARD_COUNT = NumericField( + "domoDatasetCardCount", "domoDatasetCardCount" +) +DomoDataset.DOMO_DATASET_LAST_RUN = KeywordField( + "domoDatasetLastRun", "domoDatasetLastRun" +) +DomoDataset.DOMO_ID = KeywordField("domoId", "domoId") +DomoDataset.DOMO_OWNER_ID = KeywordField("domoOwnerId", "domoOwnerId") +DomoDataset.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +DomoDataset.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +DomoDataset.ANOMALO_CHECKS = RelationField("anomaloChecks") +DomoDataset.APPLICATION = RelationField("application") +DomoDataset.APPLICATION_FIELD = RelationField("applicationField") +DomoDataset.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +DomoDataset.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +DomoDataset.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +DomoDataset.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +DomoDataset.METRICS = RelationField("metrics") +DomoDataset.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +DomoDataset.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +DomoDataset.DOMO_CARDS = RelationField("domoCards") +DomoDataset.DOMO_DATASET_COLUMNS = RelationField("domoDatasetColumns") +DomoDataset.MEANINGS = RelationField("meanings") +DomoDataset.MC_MONITORS = RelationField("mcMonitors") +DomoDataset.MC_INCIDENTS = RelationField("mcIncidents") +DomoDataset.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +DomoDataset.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +DomoDataset.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +DomoDataset.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +DomoDataset.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +DomoDataset.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +DomoDataset.FILES = RelationField("files") +DomoDataset.LINKS = RelationField("links") +DomoDataset.README = RelationField("readme") +DomoDataset.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +DomoDataset.SODA_CHECKS = RelationField("sodaChecks") +DomoDataset.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +DomoDataset.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/domo_dataset_column.py b/pyatlan_v9/model/assets/domo_dataset_column.py new file mode 100644 index 000000000..9021f3871 --- /dev/null +++ b/pyatlan_v9/model/assets/domo_dataset_column.py @@ -0,0 +1,636 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DomoDatasetColumn asset model with flattened inheritance. + +This module provides: +- DomoDatasetColumn: Flat asset class (easy to use) +- DomoDatasetColumnAttributes: Nested attributes struct (extends AssetAttributes) +- DomoDatasetColumnNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .domo_related import RelatedDomoDataset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class DomoDatasetColumn(Asset): + """ + Instance of a Domo Dataset Column in Atlan. + """ + + DOMO_DATASET_COLUMN_TYPE: ClassVar[Any] = None + DOMO_DATASET_QUALIFIED_NAME: ClassVar[Any] = None + DOMO_DATASET_COLUMN_EXPRESSION: ClassVar[Any] = None + DOMO_DATASET_COLUMN_IS_CALCULATED: ClassVar[Any] = None + DOMO_ID: ClassVar[Any] = None + DOMO_OWNER_ID: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DOMO_DATASET: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "DomoDatasetColumn" + + domo_dataset_column_type: Union[str, None, UnsetType] = UNSET + """Type of Domo Dataset Column.""" + + domo_dataset_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of domo dataset of this column.""" + + domo_dataset_column_expression: Union[str, None, UnsetType] = UNSET + """Expression used to create this calculated column.""" + + domo_dataset_column_is_calculated: Union[bool, None, UnsetType] = UNSET + """If the column is a calculated column.""" + + domo_id: Union[str, None, UnsetType] = UNSET + """Id of the Domo dataset.""" + + domo_owner_id: Union[str, None, UnsetType] = UNSET + """Id of the owner of the Domo dataset.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + domo_dataset: Union[RelatedDomoDataset, None, UnsetType] = UNSET + """Domo Dataset that contains this Domo Dataset Column.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "DomoDatasetColumn" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _domo_dataset_column_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> DomoDatasetColumn: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + DomoDatasetColumn instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _domo_dataset_column_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DomoDatasetColumnAttributes(AssetAttributes): + """DomoDatasetColumn-specific attributes for nested API format.""" + + domo_dataset_column_type: Union[str, None, UnsetType] = UNSET + """Type of Domo Dataset Column.""" + + domo_dataset_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of domo dataset of this column.""" + + domo_dataset_column_expression: Union[str, None, UnsetType] = UNSET + """Expression used to create this calculated column.""" + + domo_dataset_column_is_calculated: Union[bool, None, UnsetType] = UNSET + """If the column is a calculated column.""" + + domo_id: Union[str, None, UnsetType] = UNSET + """Id of the Domo dataset.""" + + domo_owner_id: Union[str, None, UnsetType] = UNSET + """Id of the owner of the Domo dataset.""" + + +class DomoDatasetColumnRelationshipAttributes(AssetRelationshipAttributes): + """DomoDatasetColumn-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + domo_dataset: Union[RelatedDomoDataset, None, UnsetType] = UNSET + """Domo Dataset that contains this Domo Dataset Column.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DomoDatasetColumnNested(AssetNested): + """DomoDatasetColumn in nested API format for high-performance serialization.""" + + attributes: Union[DomoDatasetColumnAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + DomoDatasetColumnRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + DomoDatasetColumnRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + DomoDatasetColumnRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DOMO_DATASET_COLUMN_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "domo_dataset", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_domo_dataset_column_attrs( + attrs: DomoDatasetColumnAttributes, obj: DomoDatasetColumn +) -> None: + """Populate DomoDatasetColumn-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.domo_dataset_column_type = obj.domo_dataset_column_type + attrs.domo_dataset_qualified_name = obj.domo_dataset_qualified_name + attrs.domo_dataset_column_expression = obj.domo_dataset_column_expression + attrs.domo_dataset_column_is_calculated = obj.domo_dataset_column_is_calculated + attrs.domo_id = obj.domo_id + attrs.domo_owner_id = obj.domo_owner_id + + +def _extract_domo_dataset_column_attrs(attrs: DomoDatasetColumnAttributes) -> dict: + """Extract all DomoDatasetColumn attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["domo_dataset_column_type"] = attrs.domo_dataset_column_type + result["domo_dataset_qualified_name"] = attrs.domo_dataset_qualified_name + result["domo_dataset_column_expression"] = attrs.domo_dataset_column_expression + result["domo_dataset_column_is_calculated"] = ( + attrs.domo_dataset_column_is_calculated + ) + result["domo_id"] = attrs.domo_id + result["domo_owner_id"] = attrs.domo_owner_id + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _domo_dataset_column_to_nested( + domo_dataset_column: DomoDatasetColumn, +) -> DomoDatasetColumnNested: + """Convert flat DomoDatasetColumn to nested format.""" + attrs = DomoDatasetColumnAttributes() + _populate_domo_dataset_column_attrs(attrs, domo_dataset_column) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + domo_dataset_column, + _DOMO_DATASET_COLUMN_REL_FIELDS, + DomoDatasetColumnRelationshipAttributes, + ) + return DomoDatasetColumnNested( + guid=domo_dataset_column.guid, + type_name=domo_dataset_column.type_name, + status=domo_dataset_column.status, + version=domo_dataset_column.version, + create_time=domo_dataset_column.create_time, + update_time=domo_dataset_column.update_time, + created_by=domo_dataset_column.created_by, + updated_by=domo_dataset_column.updated_by, + classifications=domo_dataset_column.classifications, + classification_names=domo_dataset_column.classification_names, + meanings=domo_dataset_column.meanings, + labels=domo_dataset_column.labels, + business_attributes=domo_dataset_column.business_attributes, + custom_attributes=domo_dataset_column.custom_attributes, + pending_tasks=domo_dataset_column.pending_tasks, + proxy=domo_dataset_column.proxy, + is_incomplete=domo_dataset_column.is_incomplete, + provenance_type=domo_dataset_column.provenance_type, + home_id=domo_dataset_column.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _domo_dataset_column_from_nested( + nested: DomoDatasetColumnNested, +) -> DomoDatasetColumn: + """Convert nested format to flat DomoDatasetColumn.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else DomoDatasetColumnAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DOMO_DATASET_COLUMN_REL_FIELDS, + DomoDatasetColumnRelationshipAttributes, + ) + return DomoDatasetColumn( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_domo_dataset_column_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _domo_dataset_column_to_nested_bytes( + domo_dataset_column: DomoDatasetColumn, serde: Serde +) -> bytes: + """Convert flat DomoDatasetColumn to nested JSON bytes.""" + return serde.encode(_domo_dataset_column_to_nested(domo_dataset_column)) + + +def _domo_dataset_column_from_nested_bytes( + data: bytes, serde: Serde +) -> DomoDatasetColumn: + """Convert nested JSON bytes to flat DomoDatasetColumn.""" + nested = serde.decode(data, DomoDatasetColumnNested) + return _domo_dataset_column_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + RelationField, +) + +DomoDatasetColumn.DOMO_DATASET_COLUMN_TYPE = KeywordField( + "domoDatasetColumnType", "domoDatasetColumnType" +) +DomoDatasetColumn.DOMO_DATASET_QUALIFIED_NAME = KeywordField( + "domoDatasetQualifiedName", "domoDatasetQualifiedName" +) +DomoDatasetColumn.DOMO_DATASET_COLUMN_EXPRESSION = KeywordField( + "domoDatasetColumnExpression", "domoDatasetColumnExpression" +) +DomoDatasetColumn.DOMO_DATASET_COLUMN_IS_CALCULATED = BooleanField( + "domoDatasetColumnIsCalculated", "domoDatasetColumnIsCalculated" +) +DomoDatasetColumn.DOMO_ID = KeywordField("domoId", "domoId") +DomoDatasetColumn.DOMO_OWNER_ID = KeywordField("domoOwnerId", "domoOwnerId") +DomoDatasetColumn.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +DomoDatasetColumn.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +DomoDatasetColumn.ANOMALO_CHECKS = RelationField("anomaloChecks") +DomoDatasetColumn.APPLICATION = RelationField("application") +DomoDatasetColumn.APPLICATION_FIELD = RelationField("applicationField") +DomoDatasetColumn.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +DomoDatasetColumn.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +DomoDatasetColumn.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +DomoDatasetColumn.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +DomoDatasetColumn.METRICS = RelationField("metrics") +DomoDatasetColumn.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +DomoDatasetColumn.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +DomoDatasetColumn.DOMO_DATASET = RelationField("domoDataset") +DomoDatasetColumn.MEANINGS = RelationField("meanings") +DomoDatasetColumn.MC_MONITORS = RelationField("mcMonitors") +DomoDatasetColumn.MC_INCIDENTS = RelationField("mcIncidents") +DomoDatasetColumn.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +DomoDatasetColumn.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +DomoDatasetColumn.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +DomoDatasetColumn.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +DomoDatasetColumn.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +DomoDatasetColumn.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +DomoDatasetColumn.FILES = RelationField("files") +DomoDatasetColumn.LINKS = RelationField("links") +DomoDatasetColumn.README = RelationField("readme") +DomoDatasetColumn.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +DomoDatasetColumn.SODA_CHECKS = RelationField("sodaChecks") +DomoDatasetColumn.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +DomoDatasetColumn.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/domo_related.py b/pyatlan_v9/model/assets/domo_related.py new file mode 100644 index 000000000..3b1c3f84a --- /dev/null +++ b/pyatlan_v9/model/assets/domo_related.py @@ -0,0 +1,147 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Domo module. + +This module contains all Related{Type} classes for the Domo type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Union + +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedBI +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedDomo", + "RelatedDomoCard", + "RelatedDomoDashboard", + "RelatedDomoDataset", + "RelatedDomoDatasetColumn", +] + + +class RelatedDomo(RelatedBI): + """ + Related entity reference for Domo assets. + + Extends RelatedBI with Domo-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Domo" so it serializes correctly + + domo_id: Union[str, None, UnsetType] = UNSET + """Id of the Domo dataset.""" + + domo_owner_id: Union[str, None, UnsetType] = UNSET + """Id of the owner of the Domo dataset.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Domo" + + +class RelatedDomoCard(RelatedDomo): + """ + Related entity reference for DomoCard assets. + + Extends RelatedDomo with DomoCard-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DomoCard" so it serializes correctly + + domo_card_type: Union[str, None, UnsetType] = UNSET + """Type of the Domo Card.""" + + domo_card_type_value: Union[str, None, UnsetType] = UNSET + """Type of the Domo Card.""" + + domo_card_dashboard_count: Union[int, None, UnsetType] = UNSET + """Number of dashboards linked to this card.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DomoCard" + + +class RelatedDomoDashboard(RelatedDomo): + """ + Related entity reference for DomoDashboard assets. + + Extends RelatedDomo with DomoDashboard-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DomoDashboard" so it serializes correctly + + domo_dashboard_card_count: Union[int, None, UnsetType] = UNSET + """Number of cards linked to this dashboard.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DomoDashboard" + + +class RelatedDomoDataset(RelatedDomo): + """ + Related entity reference for DomoDataset assets. + + Extends RelatedDomo with DomoDataset-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DomoDataset" so it serializes correctly + + domo_dataset_row_count: Union[int, None, UnsetType] = UNSET + """Number of rows in the Domo dataset.""" + + domo_dataset_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in the Domo dataset.""" + + domo_dataset_type: Union[str, None, UnsetType] = UNSET + """Type of Domo dataset.""" + + domo_dataset_card_count: Union[int, None, UnsetType] = UNSET + """Number of cards linked to the Domo dataset.""" + + domo_dataset_last_run: Union[str, None, UnsetType] = UNSET + """An ISO-8601 representation of the time the DataSet was last run.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DomoDataset" + + +class RelatedDomoDatasetColumn(RelatedDomo): + """ + Related entity reference for DomoDatasetColumn assets. + + Extends RelatedDomo with DomoDatasetColumn-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DomoDatasetColumn" so it serializes correctly + + domo_dataset_column_type: Union[str, None, UnsetType] = UNSET + """Type of Domo Dataset Column.""" + + domo_dataset_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of domo dataset of this column.""" + + domo_dataset_column_expression: Union[str, None, UnsetType] = UNSET + """Expression used to create this calculated column.""" + + domo_dataset_column_is_calculated: Union[bool, None, UnsetType] = UNSET + """If the column is a calculated column.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DomoDatasetColumn" diff --git a/pyatlan_v9/model/assets/dremio.py b/pyatlan_v9/model/assets/dremio.py new file mode 100644 index 000000000..3dcd6d638 --- /dev/null +++ b/pyatlan_v9/model/assets/dremio.py @@ -0,0 +1,896 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Dremio asset model with flattened inheritance. + +This module provides: +- Dremio: Flat asset class (easy to use) +- DremioAttributes: Nested attributes struct (extends AssetAttributes) +- DremioNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .snowflake_related import RelatedSnowflakeSemanticLogicalTable +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Dremio(Asset): + """ + Base class for Dremio assets. + """ + + DREMIO_ID: ClassVar[Any] = None + DREMIO_SPACE_QUALIFIED_NAME: ClassVar[Any] = None + DREMIO_SPACE_NAME: ClassVar[Any] = None + DREMIO_SOURCE_QUALIFIED_NAME: ClassVar[Any] = None + DREMIO_SOURCE_NAME: ClassVar[Any] = None + DREMIO_PARENT_FOLDER_QUALIFIED_NAME: ClassVar[Any] = None + DREMIO_FOLDER_HIERARCHY: ClassVar[Any] = None + DREMIO_LABELS: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Dremio" + + dremio_id: Union[str, None, UnsetType] = UNSET + """Source ID of this asset in Dremio.""" + + dremio_space_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique qualified name of the Dremio Space containing this asset.""" + + dremio_space_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Dremio Space containing this asset.""" + + dremio_source_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique qualified name of the Dremio Source containing this asset.""" + + dremio_source_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Dremio Source containing this asset.""" + + dremio_parent_folder_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique qualified name of the immediate parent folder containing this asset.""" + + dremio_folder_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Ordered array of folder assets with qualified name and name representing the complete folder hierarchy path for this asset, from immediate parent to root folder.""" + + dremio_labels: Union[List[str], None, UnsetType] = UNSET + """Dremio Labels associated with this asset.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Dremio" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _dremio_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Dremio: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Dremio instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _dremio_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DremioAttributes(AssetAttributes): + """Dremio-specific attributes for nested API format.""" + + dremio_id: Union[str, None, UnsetType] = UNSET + """Source ID of this asset in Dremio.""" + + dremio_space_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique qualified name of the Dremio Space containing this asset.""" + + dremio_space_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Dremio Space containing this asset.""" + + dremio_source_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique qualified name of the Dremio Source containing this asset.""" + + dremio_source_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Dremio Source containing this asset.""" + + dremio_parent_folder_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique qualified name of the immediate parent folder containing this asset.""" + + dremio_folder_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Ordered array of folder assets with qualified name and name representing the complete folder hierarchy path for this asset, from immediate parent to root folder.""" + + dremio_labels: Union[List[str], None, UnsetType] = UNSET + """Dremio Labels associated with this asset.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + +class DremioRelationshipAttributes(AssetRelationshipAttributes): + """Dremio-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DremioNested(AssetNested): + """Dremio in nested API format for high-performance serialization.""" + + attributes: Union[DremioAttributes, UnsetType] = UNSET + relationship_attributes: Union[DremioRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[DremioRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[DremioRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DREMIO_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_dremio_attrs(attrs: DremioAttributes, obj: Dremio) -> None: + """Populate Dremio-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.dremio_id = obj.dremio_id + attrs.dremio_space_qualified_name = obj.dremio_space_qualified_name + attrs.dremio_space_name = obj.dremio_space_name + attrs.dremio_source_qualified_name = obj.dremio_source_qualified_name + attrs.dremio_source_name = obj.dremio_source_name + attrs.dremio_parent_folder_qualified_name = obj.dremio_parent_folder_qualified_name + attrs.dremio_folder_hierarchy = obj.dremio_folder_hierarchy + attrs.dremio_labels = obj.dremio_labels + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + + +def _extract_dremio_attrs(attrs: DremioAttributes) -> dict: + """Extract all Dremio attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["dremio_id"] = attrs.dremio_id + result["dremio_space_qualified_name"] = attrs.dremio_space_qualified_name + result["dremio_space_name"] = attrs.dremio_space_name + result["dremio_source_qualified_name"] = attrs.dremio_source_qualified_name + result["dremio_source_name"] = attrs.dremio_source_name + result["dremio_parent_folder_qualified_name"] = ( + attrs.dremio_parent_folder_qualified_name + ) + result["dremio_folder_hierarchy"] = attrs.dremio_folder_hierarchy + result["dremio_labels"] = attrs.dremio_labels + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _dremio_to_nested(dremio: Dremio) -> DremioNested: + """Convert flat Dremio to nested format.""" + attrs = DremioAttributes() + _populate_dremio_attrs(attrs, dremio) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + dremio, _DREMIO_REL_FIELDS, DremioRelationshipAttributes + ) + return DremioNested( + guid=dremio.guid, + type_name=dremio.type_name, + status=dremio.status, + version=dremio.version, + create_time=dremio.create_time, + update_time=dremio.update_time, + created_by=dremio.created_by, + updated_by=dremio.updated_by, + classifications=dremio.classifications, + classification_names=dremio.classification_names, + meanings=dremio.meanings, + labels=dremio.labels, + business_attributes=dremio.business_attributes, + custom_attributes=dremio.custom_attributes, + pending_tasks=dremio.pending_tasks, + proxy=dremio.proxy, + is_incomplete=dremio.is_incomplete, + provenance_type=dremio.provenance_type, + home_id=dremio.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _dremio_from_nested(nested: DremioNested) -> Dremio: + """Convert nested format to flat Dremio.""" + attrs = nested.attributes if nested.attributes is not UNSET else DremioAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DREMIO_REL_FIELDS, + DremioRelationshipAttributes, + ) + return Dremio( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_dremio_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _dremio_to_nested_bytes(dremio: Dremio, serde: Serde) -> bytes: + """Convert flat Dremio to nested JSON bytes.""" + return serde.encode(_dremio_to_nested(dremio)) + + +def _dremio_from_nested_bytes(data: bytes, serde: Serde) -> Dremio: + """Convert nested JSON bytes to flat Dremio.""" + nested = serde.decode(data, DremioNested) + return _dremio_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, +) + +Dremio.DREMIO_ID = KeywordField("dremioId", "dremioId") +Dremio.DREMIO_SPACE_QUALIFIED_NAME = KeywordField( + "dremioSpaceQualifiedName", "dremioSpaceQualifiedName" +) +Dremio.DREMIO_SPACE_NAME = KeywordField("dremioSpaceName", "dremioSpaceName") +Dremio.DREMIO_SOURCE_QUALIFIED_NAME = KeywordField( + "dremioSourceQualifiedName", "dremioSourceQualifiedName" +) +Dremio.DREMIO_SOURCE_NAME = KeywordField("dremioSourceName", "dremioSourceName") +Dremio.DREMIO_PARENT_FOLDER_QUALIFIED_NAME = KeywordField( + "dremioParentFolderQualifiedName", "dremioParentFolderQualifiedName" +) +Dremio.DREMIO_FOLDER_HIERARCHY = KeywordField( + "dremioFolderHierarchy", "dremioFolderHierarchy" +) +Dremio.DREMIO_LABELS = KeywordField("dremioLabels", "dremioLabels") +Dremio.QUERY_COUNT = NumericField("queryCount", "queryCount") +Dremio.QUERY_USER_COUNT = NumericField("queryUserCount", "queryUserCount") +Dremio.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +Dremio.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +Dremio.DATABASE_NAME = KeywordField("databaseName", "databaseName") +Dremio.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +Dremio.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +Dremio.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +Dremio.TABLE_NAME = KeywordField("tableName", "tableName") +Dremio.TABLE_QUALIFIED_NAME = KeywordField("tableQualifiedName", "tableQualifiedName") +Dremio.VIEW_NAME = KeywordField("viewName", "viewName") +Dremio.VIEW_QUALIFIED_NAME = KeywordField("viewQualifiedName", "viewQualifiedName") +Dremio.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +Dremio.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +Dremio.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +Dremio.LAST_PROFILED_AT = NumericField("lastProfiledAt", "lastProfiledAt") +Dremio.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +Dremio.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +Dremio.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Dremio.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Dremio.ANOMALO_CHECKS = RelationField("anomaloChecks") +Dremio.APPLICATION = RelationField("application") +Dremio.APPLICATION_FIELD = RelationField("applicationField") +Dremio.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Dremio.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Dremio.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Dremio.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Dremio.METRICS = RelationField("metrics") +Dremio.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Dremio.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Dremio.DBT_MODELS = RelationField("dbtModels") +Dremio.SQL_DBT_MODELS = RelationField("sqlDbtModels") +Dremio.DBT_TESTS = RelationField("dbtTests") +Dremio.DBT_SOURCES = RelationField("dbtSources") +Dremio.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +Dremio.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +Dremio.MEANINGS = RelationField("meanings") +Dremio.MC_MONITORS = RelationField("mcMonitors") +Dremio.MC_INCIDENTS = RelationField("mcIncidents") +Dremio.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Dremio.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Dremio.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Dremio.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Dremio.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Dremio.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Dremio.FILES = RelationField("files") +Dremio.LINKS = RelationField("links") +Dremio.README = RelationField("readme") +Dremio.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Dremio.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +Dremio.SODA_CHECKS = RelationField("sodaChecks") +Dremio.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Dremio.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/dremio_column.py b/pyatlan_v9/model/assets/dremio_column.py new file mode 100644 index 000000000..57ed0da58 --- /dev/null +++ b/pyatlan_v9/model/assets/dremio_column.py @@ -0,0 +1,1867 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DremioColumn asset model with flattened inheritance. + +This module provides: +- DremioColumn: Flat asset class (easy to use) +- DremioColumnAttributes: Nested attributes struct (extends AssetAttributes) +- DremioColumnNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .cosmos_mongo_db_related import RelatedCosmosMongoDBCollection +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtMetric, + RelatedDbtModel, + RelatedDbtModelColumn, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .mongo_db_related import RelatedMongoDBCollection +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .snowflake_related import ( + RelatedSnowflakeDynamicTable, + RelatedSnowflakeSemanticLogicalTable, +) +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from .sql_related import ( + RelatedCalculationView, + RelatedColumn, + RelatedMaterialisedView, + RelatedQuery, + RelatedTable, + RelatedTablePartition, + RelatedView, +) +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class DremioColumn(Asset): + """ + Instance of a Dremio Column in Atlan. + """ + + DREMIO_ID: ClassVar[Any] = None + DREMIO_SPACE_QUALIFIED_NAME: ClassVar[Any] = None + DREMIO_SPACE_NAME: ClassVar[Any] = None + DREMIO_SOURCE_QUALIFIED_NAME: ClassVar[Any] = None + DREMIO_SOURCE_NAME: ClassVar[Any] = None + DREMIO_PARENT_FOLDER_QUALIFIED_NAME: ClassVar[Any] = None + DREMIO_FOLDER_HIERARCHY: ClassVar[Any] = None + DREMIO_LABELS: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + DATA_TYPE: ClassVar[Any] = None + SUB_DATA_TYPE: ClassVar[Any] = None + COLUMN_COMPRESSION: ClassVar[Any] = None + COLUMN_ENCODING: ClassVar[Any] = None + RAW_DATA_TYPE_DEFINITION: ClassVar[Any] = None + ORDER: ClassVar[Any] = None + NESTED_COLUMN_ORDER: ClassVar[Any] = None + NESTED_COLUMN_COUNT: ClassVar[Any] = None + COLUMN_HIERARCHY: ClassVar[Any] = None + IS_PARTITION: ClassVar[Any] = None + PARTITION_ORDER: ClassVar[Any] = None + IS_CLUSTERED: ClassVar[Any] = None + IS_PRIMARY: ClassVar[Any] = None + IS_FOREIGN: ClassVar[Any] = None + IS_INDEXED: ClassVar[Any] = None + IS_SORT: ClassVar[Any] = None + IS_DIST: ClassVar[Any] = None + IS_PINNED: ClassVar[Any] = None + PINNED_BY: ClassVar[Any] = None + PINNED_AT: ClassVar[Any] = None + PRECISION: ClassVar[Any] = None + DEFAULT_VALUE: ClassVar[Any] = None + IS_NULLABLE: ClassVar[Any] = None + NUMERIC_SCALE: ClassVar[Any] = None + MAX_LENGTH: ClassVar[Any] = None + VALIDATIONS: ClassVar[Any] = None + PARENT_COLUMN_QUALIFIED_NAME: ClassVar[Any] = None + PARENT_COLUMN_NAME: ClassVar[Any] = None + COLUMN_DISTINCT_VALUES_COUNT: ClassVar[Any] = None + COLUMN_DISTINCT_VALUES_COUNT_LONG: ClassVar[Any] = None + COLUMN_HISTOGRAM: ClassVar[Any] = None + COLUMN_MAX: ClassVar[Any] = None + COLUMN_MIN: ClassVar[Any] = None + COLUMN_MEAN: ClassVar[Any] = None + COLUMN_SUM: ClassVar[Any] = None + COLUMN_MEDIAN: ClassVar[Any] = None + COLUMN_STANDARD_DEVIATION: ClassVar[Any] = None + COLUMN_UNIQUE_VALUES_COUNT: ClassVar[Any] = None + COLUMN_UNIQUE_VALUES_COUNT_LONG: ClassVar[Any] = None + COLUMN_AVERAGE: ClassVar[Any] = None + COLUMN_AVERAGE_LENGTH: ClassVar[Any] = None + COLUMN_DUPLICATE_VALUES_COUNT: ClassVar[Any] = None + COLUMN_DUPLICATE_VALUES_COUNT_LONG: ClassVar[Any] = None + COLUMN_MAXIMUM_STRING_LENGTH: ClassVar[Any] = None + COLUMN_MAXS: ClassVar[Any] = None + COLUMN_MINIMUM_STRING_LENGTH: ClassVar[Any] = None + COLUMN_MINS: ClassVar[Any] = None + COLUMN_MISSING_VALUES_COUNT: ClassVar[Any] = None + COLUMN_MISSING_VALUES_COUNT_LONG: ClassVar[Any] = None + COLUMN_MISSING_VALUES_PERCENTAGE: ClassVar[Any] = None + COLUMN_UNIQUENESS_PERCENTAGE: ClassVar[Any] = None + COLUMN_VARIANCE: ClassVar[Any] = None + COLUMN_TOP_VALUES: ClassVar[Any] = None + COLUMN_MAX_VALUE: ClassVar[Any] = None + COLUMN_MIN_VALUE: ClassVar[Any] = None + COLUMN_MEAN_VALUE: ClassVar[Any] = None + COLUMN_SUM_VALUE: ClassVar[Any] = None + COLUMN_MEDIAN_VALUE: ClassVar[Any] = None + COLUMN_STANDARD_DEVIATION_VALUE: ClassVar[Any] = None + COLUMN_AVERAGE_VALUE: ClassVar[Any] = None + COLUMN_VARIANCE_VALUE: ClassVar[Any] = None + COLUMN_AVERAGE_LENGTH_VALUE: ClassVar[Any] = None + COLUMN_DISTRIBUTION_HISTOGRAM: ClassVar[Any] = None + COLUMN_DEPTH_LEVEL: ClassVar[Any] = None + NOSQL_COLLECTION_NAME: ClassVar[Any] = None + NOSQL_COLLECTION_QUALIFIED_NAME: ClassVar[Any] = None + COLUMN_IS_MEASURE: ClassVar[Any] = None + COLUMN_MEASURE_TYPE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + COSMOS_MONGO_DB_COLLECTION: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + METRIC_TIMESTAMPS: ClassVar[Any] = None + DATA_QUALITY_METRIC_DIMENSIONS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_BASE_COLUMN_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_COLUMN_RULES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_METRICS: ClassVar[Any] = None + DBT_MODEL_COLUMNS: ClassVar[Any] = None + COLUMN_DBT_MODEL_COLUMNS: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MONGO_DB_COLLECTION: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + TABLE: ClassVar[Any] = None + NESTED_COLUMNS: ClassVar[Any] = None + PARENT_COLUMN: ClassVar[Any] = None + TABLE_PARTITION: ClassVar[Any] = None + VIEW: ClassVar[Any] = None + CALCULATION_VIEW: ClassVar[Any] = None + MATERIALISED_VIEW: ClassVar[Any] = None + FOREIGN_KEY_TO: ClassVar[Any] = None + FOREIGN_KEY_FROM: ClassVar[Any] = None + QUERIES: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_DYNAMIC_TABLE: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "DremioColumn" + + dremio_id: Union[str, None, UnsetType] = UNSET + """Source ID of this asset in Dremio.""" + + dremio_space_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique qualified name of the Dremio Space containing this asset.""" + + dremio_space_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Dremio Space containing this asset.""" + + dremio_source_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique qualified name of the Dremio Source containing this asset.""" + + dremio_source_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Dremio Source containing this asset.""" + + dremio_parent_folder_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique qualified name of the immediate parent folder containing this asset.""" + + dremio_folder_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Ordered array of folder assets with qualified name and name representing the complete folder hierarchy path for this asset, from immediate parent to root folder.""" + + dremio_labels: Union[List[str], None, UnsetType] = UNSET + """Dremio Labels associated with this asset.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + data_type: Union[str, None, UnsetType] = UNSET + """Data type of values in this column.""" + + sub_data_type: Union[str, None, UnsetType] = UNSET + """Sub-data type of this column.""" + + column_compression: Union[str, None, UnsetType] = UNSET + """Compression type of this column.""" + + column_encoding: Union[str, None, UnsetType] = UNSET + """Encoding type of this column.""" + + raw_data_type_definition: Union[str, None, UnsetType] = UNSET + """Raw data type definition of this column.""" + + order: Union[int, None, UnsetType] = UNSET + """Order (position) in which this column appears in the table (starting at 1).""" + + nested_column_order: Union[str, None, UnsetType] = UNSET + """Order (position) in which this column appears in the nested Column (nest level starts at 1).""" + + nested_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns nested within this (STRUCT or NESTED) column.""" + + column_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of top-level upstream nested columns.""" + + is_partition: Union[bool, None, UnsetType] = UNSET + """Whether this column is a partition column (true) or not (false).""" + + partition_order: Union[int, None, UnsetType] = UNSET + """Order (position) of this partition column in the table.""" + + is_clustered: Union[bool, None, UnsetType] = UNSET + """Whether this column is a clustered column (true) or not (false).""" + + is_primary: Union[bool, None, UnsetType] = UNSET + """When true, this column is the primary key for the table.""" + + is_foreign: Union[bool, None, UnsetType] = UNSET + """When true, this column is a foreign key to another table. NOTE: this must be true when using the foreignKeyTo relationship to specify columns that refer to this column as a foreign key.""" + + is_indexed: Union[bool, None, UnsetType] = UNSET + """When true, this column is indexed in the database.""" + + is_sort: Union[bool, None, UnsetType] = UNSET + """Whether this column is a sort column (true) or not (false).""" + + is_dist: Union[bool, None, UnsetType] = UNSET + """Whether this column is a distribution column (true) or not (false).""" + + is_pinned: Union[bool, None, UnsetType] = UNSET + """Whether this column is pinned (true) or not (false).""" + + pinned_by: Union[str, None, UnsetType] = UNSET + """User who pinned this column.""" + + pinned_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this column was pinned, in milliseconds.""" + + precision: Union[int, None, UnsetType] = UNSET + """Total number of digits allowed, when the dataType is numeric.""" + + default_value: Union[str, None, UnsetType] = UNSET + """Default value for this column.""" + + is_nullable: Union[bool, None, UnsetType] = UNSET + """When true, the values in this column can be null.""" + + numeric_scale: Union[float, None, UnsetType] = UNSET + """Number of digits allowed to the right of the decimal point.""" + + max_length: Union[int, None, UnsetType] = UNSET + """Maximum length of a value in this column.""" + + validations: Union[Dict[str, str], None, UnsetType] = UNSET + """Validations for this column.""" + + parent_column_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the column this column is nested within, for STRUCT and NESTED columns.""" + + parent_column_name: Union[str, None, UnsetType] = UNSET + """Simple name of the column this column is nested within, for STRUCT and NESTED columns.""" + + column_distinct_values_count: Union[int, None, UnsetType] = UNSET + """Number of rows that contain distinct values.""" + + column_distinct_values_count_long: Union[int, None, UnsetType] = UNSET + """Number of rows that contain distinct values.""" + + column_histogram: Union[Dict[str, Any], None, UnsetType] = UNSET + """List of values in a histogram that represents the contents of this column.""" + + column_max: Union[float, None, UnsetType] = UNSET + """Greatest value in a numeric column.""" + + column_min: Union[float, None, UnsetType] = UNSET + """Least value in a numeric column.""" + + column_mean: Union[float, None, UnsetType] = UNSET + """Arithmetic mean of the values in a numeric column.""" + + column_sum: Union[float, None, UnsetType] = UNSET + """Calculated sum of the values in a numeric column.""" + + column_median: Union[float, None, UnsetType] = UNSET + """Calculated median of the values in a numeric column.""" + + column_standard_deviation: Union[float, None, UnsetType] = UNSET + """Calculated standard deviation of the values in a numeric column.""" + + column_unique_values_count: Union[int, None, UnsetType] = UNSET + """Number of rows in which a value in this column appears only once.""" + + column_unique_values_count_long: Union[int, None, UnsetType] = UNSET + """Number of rows in which a value in this column appears only once.""" + + column_average: Union[float, None, UnsetType] = UNSET + """Average value in this column.""" + + column_average_length: Union[float, None, UnsetType] = UNSET + """Average length of values in a string column.""" + + column_duplicate_values_count: Union[int, None, UnsetType] = UNSET + """Number of rows that contain duplicate values.""" + + column_duplicate_values_count_long: Union[int, None, UnsetType] = UNSET + """Number of rows that contain duplicate values.""" + + column_maximum_string_length: Union[int, None, UnsetType] = UNSET + """Length of the longest value in a string column.""" + + column_maxs: Union[List[str], None, UnsetType] = UNSET + """List of the greatest values in a column.""" + + column_minimum_string_length: Union[int, None, UnsetType] = UNSET + """Length of the shortest value in a string column.""" + + column_mins: Union[List[str], None, UnsetType] = UNSET + """List of the least values in a column.""" + + column_missing_values_count: Union[int, None, UnsetType] = UNSET + """Number of rows in a column that do not contain content.""" + + column_missing_values_count_long: Union[int, None, UnsetType] = UNSET + """Number of rows in a column that do not contain content.""" + + column_missing_values_percentage: Union[float, None, UnsetType] = UNSET + """Percentage of rows in a column that do not contain content.""" + + column_uniqueness_percentage: Union[float, None, UnsetType] = UNSET + """Ratio indicating how unique data in this column is: 0 indicates that all values are the same, 100 indicates that all values in this column are unique.""" + + column_variance: Union[float, None, UnsetType] = UNSET + """Calculated variance of the values in a numeric column.""" + + column_top_values: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of top values in this column.""" + + column_max_value: Union[float, None, UnsetType] = UNSET + """Greatest value in a numeric column.""" + + column_min_value: Union[float, None, UnsetType] = UNSET + """Least value in a numeric column.""" + + column_mean_value: Union[float, None, UnsetType] = UNSET + """Arithmetic mean of the values in a numeric column.""" + + column_sum_value: Union[float, None, UnsetType] = UNSET + """Calculated sum of the values in a numeric column.""" + + column_median_value: Union[float, None, UnsetType] = UNSET + """Calculated median of the values in a numeric column.""" + + column_standard_deviation_value: Union[float, None, UnsetType] = UNSET + """Calculated standard deviation of the values in a numeric column.""" + + column_average_value: Union[float, None, UnsetType] = UNSET + """Average value in this column.""" + + column_variance_value: Union[float, None, UnsetType] = UNSET + """Calculated variance of the values in a numeric column.""" + + column_average_length_value: Union[float, None, UnsetType] = UNSET + """Average length of values in a string column.""" + + column_distribution_histogram: Union[Dict[str, Any], None, UnsetType] = UNSET + """Detailed information representing a histogram of values for a column.""" + + column_depth_level: Union[int, None, UnsetType] = UNSET + """Level of nesting of this column, used for STRUCT and NESTED columns.""" + + nosql_collection_name: Union[str, None, UnsetType] = UNSET + """Simple name of the cosmos/mongo collection in which this SQL asset (column) exists, or empty if it does not exist within a cosmos/mongo collection.""" + + nosql_collection_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the cosmos/mongo collection in which this SQL asset (column) exists, or empty if it does not exist within a cosmos/mongo collection.""" + + column_is_measure: Union[bool, None, UnsetType] = UNSET + """When true, this column is of type measure/calculated.""" + + column_measure_type: Union[str, None, UnsetType] = UNSET + """The type of measure/calculated column this is, eg: base, calculated, derived.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cosmos_mongo_db_collection: Union[ + RelatedCosmosMongoDBCollection, None, UnsetType + ] = msgspec.field(default=UNSET, name="cosmosMongoDBCollection") + """Cosmos collection in which this column exists.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + metric_timestamps: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + data_quality_metric_dimensions: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_base_column_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this column.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dq_reference_column_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this column is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_metrics: Union[List[RelatedDbtMetric], None, UnsetType] = UNSET + """Metrics related to this model column.""" + + dbt_model_columns: Union[List[RelatedDbtModelColumn], None, UnsetType] = UNSET + """(Deprecated) Model columns related to this model column.""" + + column_dbt_model_columns: Union[List[RelatedDbtModelColumn], None, UnsetType] = ( + UNSET + ) + """Model columns related to this column.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mongo_db_collection: Union[RelatedMongoDBCollection, None, UnsetType] = ( + msgspec.field(default=UNSET, name="mongoDBCollection") + ) + """Collection in which the columns exist.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + table: Union[RelatedTable, None, UnsetType] = UNSET + """Table in which this column exists.""" + + nested_columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Nested columns that exist within this column.""" + + parent_column: Union[RelatedColumn, None, UnsetType] = UNSET + """Column in which this sub-column is nested.""" + + table_partition: Union[RelatedTablePartition, None, UnsetType] = UNSET + """Table partition that contains this column.""" + + view: Union[RelatedView, None, UnsetType] = UNSET + """View in which this column exists.""" + + calculation_view: Union[RelatedCalculationView, None, UnsetType] = UNSET + """Calculate view in which this column exists.""" + + materialised_view: Union[RelatedMaterialisedView, None, UnsetType] = UNSET + """Materialized view in which this column exists.""" + + foreign_key_to: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Columns that use this column as a foreign key.""" + + foreign_key_from: Union[RelatedColumn, None, UnsetType] = UNSET + """Column this foreign key column refers to.""" + + queries: Union[List[RelatedQuery], None, UnsetType] = UNSET + """Queries that access this column.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_dynamic_table: Union[RelatedSnowflakeDynamicTable, None, UnsetType] = ( + UNSET + ) + """Snowflake dynamic table in which this column exists.""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "DremioColumn" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _dremio_column_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> DremioColumn: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + DremioColumn instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _dremio_column_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DremioColumnAttributes(AssetAttributes): + """DremioColumn-specific attributes for nested API format.""" + + dremio_id: Union[str, None, UnsetType] = UNSET + """Source ID of this asset in Dremio.""" + + dremio_space_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique qualified name of the Dremio Space containing this asset.""" + + dremio_space_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Dremio Space containing this asset.""" + + dremio_source_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique qualified name of the Dremio Source containing this asset.""" + + dremio_source_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Dremio Source containing this asset.""" + + dremio_parent_folder_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique qualified name of the immediate parent folder containing this asset.""" + + dremio_folder_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Ordered array of folder assets with qualified name and name representing the complete folder hierarchy path for this asset, from immediate parent to root folder.""" + + dremio_labels: Union[List[str], None, UnsetType] = UNSET + """Dremio Labels associated with this asset.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + data_type: Union[str, None, UnsetType] = UNSET + """Data type of values in this column.""" + + sub_data_type: Union[str, None, UnsetType] = UNSET + """Sub-data type of this column.""" + + column_compression: Union[str, None, UnsetType] = UNSET + """Compression type of this column.""" + + column_encoding: Union[str, None, UnsetType] = UNSET + """Encoding type of this column.""" + + raw_data_type_definition: Union[str, None, UnsetType] = UNSET + """Raw data type definition of this column.""" + + order: Union[int, None, UnsetType] = UNSET + """Order (position) in which this column appears in the table (starting at 1).""" + + nested_column_order: Union[str, None, UnsetType] = UNSET + """Order (position) in which this column appears in the nested Column (nest level starts at 1).""" + + nested_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns nested within this (STRUCT or NESTED) column.""" + + column_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of top-level upstream nested columns.""" + + is_partition: Union[bool, None, UnsetType] = UNSET + """Whether this column is a partition column (true) or not (false).""" + + partition_order: Union[int, None, UnsetType] = UNSET + """Order (position) of this partition column in the table.""" + + is_clustered: Union[bool, None, UnsetType] = UNSET + """Whether this column is a clustered column (true) or not (false).""" + + is_primary: Union[bool, None, UnsetType] = UNSET + """When true, this column is the primary key for the table.""" + + is_foreign: Union[bool, None, UnsetType] = UNSET + """When true, this column is a foreign key to another table. NOTE: this must be true when using the foreignKeyTo relationship to specify columns that refer to this column as a foreign key.""" + + is_indexed: Union[bool, None, UnsetType] = UNSET + """When true, this column is indexed in the database.""" + + is_sort: Union[bool, None, UnsetType] = UNSET + """Whether this column is a sort column (true) or not (false).""" + + is_dist: Union[bool, None, UnsetType] = UNSET + """Whether this column is a distribution column (true) or not (false).""" + + is_pinned: Union[bool, None, UnsetType] = UNSET + """Whether this column is pinned (true) or not (false).""" + + pinned_by: Union[str, None, UnsetType] = UNSET + """User who pinned this column.""" + + pinned_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this column was pinned, in milliseconds.""" + + precision: Union[int, None, UnsetType] = UNSET + """Total number of digits allowed, when the dataType is numeric.""" + + default_value: Union[str, None, UnsetType] = UNSET + """Default value for this column.""" + + is_nullable: Union[bool, None, UnsetType] = UNSET + """When true, the values in this column can be null.""" + + numeric_scale: Union[float, None, UnsetType] = UNSET + """Number of digits allowed to the right of the decimal point.""" + + max_length: Union[int, None, UnsetType] = UNSET + """Maximum length of a value in this column.""" + + validations: Union[Dict[str, str], None, UnsetType] = UNSET + """Validations for this column.""" + + parent_column_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the column this column is nested within, for STRUCT and NESTED columns.""" + + parent_column_name: Union[str, None, UnsetType] = UNSET + """Simple name of the column this column is nested within, for STRUCT and NESTED columns.""" + + column_distinct_values_count: Union[int, None, UnsetType] = UNSET + """Number of rows that contain distinct values.""" + + column_distinct_values_count_long: Union[int, None, UnsetType] = UNSET + """Number of rows that contain distinct values.""" + + column_histogram: Union[Dict[str, Any], None, UnsetType] = UNSET + """List of values in a histogram that represents the contents of this column.""" + + column_max: Union[float, None, UnsetType] = UNSET + """Greatest value in a numeric column.""" + + column_min: Union[float, None, UnsetType] = UNSET + """Least value in a numeric column.""" + + column_mean: Union[float, None, UnsetType] = UNSET + """Arithmetic mean of the values in a numeric column.""" + + column_sum: Union[float, None, UnsetType] = UNSET + """Calculated sum of the values in a numeric column.""" + + column_median: Union[float, None, UnsetType] = UNSET + """Calculated median of the values in a numeric column.""" + + column_standard_deviation: Union[float, None, UnsetType] = UNSET + """Calculated standard deviation of the values in a numeric column.""" + + column_unique_values_count: Union[int, None, UnsetType] = UNSET + """Number of rows in which a value in this column appears only once.""" + + column_unique_values_count_long: Union[int, None, UnsetType] = UNSET + """Number of rows in which a value in this column appears only once.""" + + column_average: Union[float, None, UnsetType] = UNSET + """Average value in this column.""" + + column_average_length: Union[float, None, UnsetType] = UNSET + """Average length of values in a string column.""" + + column_duplicate_values_count: Union[int, None, UnsetType] = UNSET + """Number of rows that contain duplicate values.""" + + column_duplicate_values_count_long: Union[int, None, UnsetType] = UNSET + """Number of rows that contain duplicate values.""" + + column_maximum_string_length: Union[int, None, UnsetType] = UNSET + """Length of the longest value in a string column.""" + + column_maxs: Union[List[str], None, UnsetType] = UNSET + """List of the greatest values in a column.""" + + column_minimum_string_length: Union[int, None, UnsetType] = UNSET + """Length of the shortest value in a string column.""" + + column_mins: Union[List[str], None, UnsetType] = UNSET + """List of the least values in a column.""" + + column_missing_values_count: Union[int, None, UnsetType] = UNSET + """Number of rows in a column that do not contain content.""" + + column_missing_values_count_long: Union[int, None, UnsetType] = UNSET + """Number of rows in a column that do not contain content.""" + + column_missing_values_percentage: Union[float, None, UnsetType] = UNSET + """Percentage of rows in a column that do not contain content.""" + + column_uniqueness_percentage: Union[float, None, UnsetType] = UNSET + """Ratio indicating how unique data in this column is: 0 indicates that all values are the same, 100 indicates that all values in this column are unique.""" + + column_variance: Union[float, None, UnsetType] = UNSET + """Calculated variance of the values in a numeric column.""" + + column_top_values: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of top values in this column.""" + + column_max_value: Union[float, None, UnsetType] = UNSET + """Greatest value in a numeric column.""" + + column_min_value: Union[float, None, UnsetType] = UNSET + """Least value in a numeric column.""" + + column_mean_value: Union[float, None, UnsetType] = UNSET + """Arithmetic mean of the values in a numeric column.""" + + column_sum_value: Union[float, None, UnsetType] = UNSET + """Calculated sum of the values in a numeric column.""" + + column_median_value: Union[float, None, UnsetType] = UNSET + """Calculated median of the values in a numeric column.""" + + column_standard_deviation_value: Union[float, None, UnsetType] = UNSET + """Calculated standard deviation of the values in a numeric column.""" + + column_average_value: Union[float, None, UnsetType] = UNSET + """Average value in this column.""" + + column_variance_value: Union[float, None, UnsetType] = UNSET + """Calculated variance of the values in a numeric column.""" + + column_average_length_value: Union[float, None, UnsetType] = UNSET + """Average length of values in a string column.""" + + column_distribution_histogram: Union[Dict[str, Any], None, UnsetType] = UNSET + """Detailed information representing a histogram of values for a column.""" + + column_depth_level: Union[int, None, UnsetType] = UNSET + """Level of nesting of this column, used for STRUCT and NESTED columns.""" + + nosql_collection_name: Union[str, None, UnsetType] = UNSET + """Simple name of the cosmos/mongo collection in which this SQL asset (column) exists, or empty if it does not exist within a cosmos/mongo collection.""" + + nosql_collection_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the cosmos/mongo collection in which this SQL asset (column) exists, or empty if it does not exist within a cosmos/mongo collection.""" + + column_is_measure: Union[bool, None, UnsetType] = UNSET + """When true, this column is of type measure/calculated.""" + + column_measure_type: Union[str, None, UnsetType] = UNSET + """The type of measure/calculated column this is, eg: base, calculated, derived.""" + + +class DremioColumnRelationshipAttributes(AssetRelationshipAttributes): + """DremioColumn-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cosmos_mongo_db_collection: Union[ + RelatedCosmosMongoDBCollection, None, UnsetType + ] = msgspec.field(default=UNSET, name="cosmosMongoDBCollection") + """Cosmos collection in which this column exists.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + metric_timestamps: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + data_quality_metric_dimensions: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_base_column_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this column.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dq_reference_column_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this column is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_metrics: Union[List[RelatedDbtMetric], None, UnsetType] = UNSET + """Metrics related to this model column.""" + + dbt_model_columns: Union[List[RelatedDbtModelColumn], None, UnsetType] = UNSET + """(Deprecated) Model columns related to this model column.""" + + column_dbt_model_columns: Union[List[RelatedDbtModelColumn], None, UnsetType] = ( + UNSET + ) + """Model columns related to this column.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mongo_db_collection: Union[RelatedMongoDBCollection, None, UnsetType] = ( + msgspec.field(default=UNSET, name="mongoDBCollection") + ) + """Collection in which the columns exist.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + table: Union[RelatedTable, None, UnsetType] = UNSET + """Table in which this column exists.""" + + nested_columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Nested columns that exist within this column.""" + + parent_column: Union[RelatedColumn, None, UnsetType] = UNSET + """Column in which this sub-column is nested.""" + + table_partition: Union[RelatedTablePartition, None, UnsetType] = UNSET + """Table partition that contains this column.""" + + view: Union[RelatedView, None, UnsetType] = UNSET + """View in which this column exists.""" + + calculation_view: Union[RelatedCalculationView, None, UnsetType] = UNSET + """Calculate view in which this column exists.""" + + materialised_view: Union[RelatedMaterialisedView, None, UnsetType] = UNSET + """Materialized view in which this column exists.""" + + foreign_key_to: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Columns that use this column as a foreign key.""" + + foreign_key_from: Union[RelatedColumn, None, UnsetType] = UNSET + """Column this foreign key column refers to.""" + + queries: Union[List[RelatedQuery], None, UnsetType] = UNSET + """Queries that access this column.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_dynamic_table: Union[RelatedSnowflakeDynamicTable, None, UnsetType] = ( + UNSET + ) + """Snowflake dynamic table in which this column exists.""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DremioColumnNested(AssetNested): + """DremioColumn in nested API format for high-performance serialization.""" + + attributes: Union[DremioColumnAttributes, UnsetType] = UNSET + relationship_attributes: Union[DremioColumnRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + DremioColumnRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + DremioColumnRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DREMIO_COLUMN_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "cosmos_mongo_db_collection", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "metric_timestamps", + "data_quality_metric_dimensions", + "dq_base_dataset_rules", + "dq_base_column_rules", + "dq_reference_dataset_rules", + "dq_reference_column_rules", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_metrics", + "dbt_model_columns", + "column_dbt_model_columns", + "dbt_seed_assets", + "meanings", + "mongo_db_collection", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "table", + "nested_columns", + "parent_column", + "table_partition", + "view", + "calculation_view", + "materialised_view", + "foreign_key_to", + "foreign_key_from", + "queries", + "schema_registry_subjects", + "snowflake_dynamic_table", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_dremio_column_attrs( + attrs: DremioColumnAttributes, obj: DremioColumn +) -> None: + """Populate DremioColumn-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.dremio_id = obj.dremio_id + attrs.dremio_space_qualified_name = obj.dremio_space_qualified_name + attrs.dremio_space_name = obj.dremio_space_name + attrs.dremio_source_qualified_name = obj.dremio_source_qualified_name + attrs.dremio_source_name = obj.dremio_source_name + attrs.dremio_parent_folder_qualified_name = obj.dremio_parent_folder_qualified_name + attrs.dremio_folder_hierarchy = obj.dremio_folder_hierarchy + attrs.dremio_labels = obj.dremio_labels + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + attrs.data_type = obj.data_type + attrs.sub_data_type = obj.sub_data_type + attrs.column_compression = obj.column_compression + attrs.column_encoding = obj.column_encoding + attrs.raw_data_type_definition = obj.raw_data_type_definition + attrs.order = obj.order + attrs.nested_column_order = obj.nested_column_order + attrs.nested_column_count = obj.nested_column_count + attrs.column_hierarchy = obj.column_hierarchy + attrs.is_partition = obj.is_partition + attrs.partition_order = obj.partition_order + attrs.is_clustered = obj.is_clustered + attrs.is_primary = obj.is_primary + attrs.is_foreign = obj.is_foreign + attrs.is_indexed = obj.is_indexed + attrs.is_sort = obj.is_sort + attrs.is_dist = obj.is_dist + attrs.is_pinned = obj.is_pinned + attrs.pinned_by = obj.pinned_by + attrs.pinned_at = obj.pinned_at + attrs.precision = obj.precision + attrs.default_value = obj.default_value + attrs.is_nullable = obj.is_nullable + attrs.numeric_scale = obj.numeric_scale + attrs.max_length = obj.max_length + attrs.validations = obj.validations + attrs.parent_column_qualified_name = obj.parent_column_qualified_name + attrs.parent_column_name = obj.parent_column_name + attrs.column_distinct_values_count = obj.column_distinct_values_count + attrs.column_distinct_values_count_long = obj.column_distinct_values_count_long + attrs.column_histogram = obj.column_histogram + attrs.column_max = obj.column_max + attrs.column_min = obj.column_min + attrs.column_mean = obj.column_mean + attrs.column_sum = obj.column_sum + attrs.column_median = obj.column_median + attrs.column_standard_deviation = obj.column_standard_deviation + attrs.column_unique_values_count = obj.column_unique_values_count + attrs.column_unique_values_count_long = obj.column_unique_values_count_long + attrs.column_average = obj.column_average + attrs.column_average_length = obj.column_average_length + attrs.column_duplicate_values_count = obj.column_duplicate_values_count + attrs.column_duplicate_values_count_long = obj.column_duplicate_values_count_long + attrs.column_maximum_string_length = obj.column_maximum_string_length + attrs.column_maxs = obj.column_maxs + attrs.column_minimum_string_length = obj.column_minimum_string_length + attrs.column_mins = obj.column_mins + attrs.column_missing_values_count = obj.column_missing_values_count + attrs.column_missing_values_count_long = obj.column_missing_values_count_long + attrs.column_missing_values_percentage = obj.column_missing_values_percentage + attrs.column_uniqueness_percentage = obj.column_uniqueness_percentage + attrs.column_variance = obj.column_variance + attrs.column_top_values = obj.column_top_values + attrs.column_max_value = obj.column_max_value + attrs.column_min_value = obj.column_min_value + attrs.column_mean_value = obj.column_mean_value + attrs.column_sum_value = obj.column_sum_value + attrs.column_median_value = obj.column_median_value + attrs.column_standard_deviation_value = obj.column_standard_deviation_value + attrs.column_average_value = obj.column_average_value + attrs.column_variance_value = obj.column_variance_value + attrs.column_average_length_value = obj.column_average_length_value + attrs.column_distribution_histogram = obj.column_distribution_histogram + attrs.column_depth_level = obj.column_depth_level + attrs.nosql_collection_name = obj.nosql_collection_name + attrs.nosql_collection_qualified_name = obj.nosql_collection_qualified_name + attrs.column_is_measure = obj.column_is_measure + attrs.column_measure_type = obj.column_measure_type + + +def _extract_dremio_column_attrs(attrs: DremioColumnAttributes) -> dict: + """Extract all DremioColumn attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["dremio_id"] = attrs.dremio_id + result["dremio_space_qualified_name"] = attrs.dremio_space_qualified_name + result["dremio_space_name"] = attrs.dremio_space_name + result["dremio_source_qualified_name"] = attrs.dremio_source_qualified_name + result["dremio_source_name"] = attrs.dremio_source_name + result["dremio_parent_folder_qualified_name"] = ( + attrs.dremio_parent_folder_qualified_name + ) + result["dremio_folder_hierarchy"] = attrs.dremio_folder_hierarchy + result["dremio_labels"] = attrs.dremio_labels + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + result["data_type"] = attrs.data_type + result["sub_data_type"] = attrs.sub_data_type + result["column_compression"] = attrs.column_compression + result["column_encoding"] = attrs.column_encoding + result["raw_data_type_definition"] = attrs.raw_data_type_definition + result["order"] = attrs.order + result["nested_column_order"] = attrs.nested_column_order + result["nested_column_count"] = attrs.nested_column_count + result["column_hierarchy"] = attrs.column_hierarchy + result["is_partition"] = attrs.is_partition + result["partition_order"] = attrs.partition_order + result["is_clustered"] = attrs.is_clustered + result["is_primary"] = attrs.is_primary + result["is_foreign"] = attrs.is_foreign + result["is_indexed"] = attrs.is_indexed + result["is_sort"] = attrs.is_sort + result["is_dist"] = attrs.is_dist + result["is_pinned"] = attrs.is_pinned + result["pinned_by"] = attrs.pinned_by + result["pinned_at"] = attrs.pinned_at + result["precision"] = attrs.precision + result["default_value"] = attrs.default_value + result["is_nullable"] = attrs.is_nullable + result["numeric_scale"] = attrs.numeric_scale + result["max_length"] = attrs.max_length + result["validations"] = attrs.validations + result["parent_column_qualified_name"] = attrs.parent_column_qualified_name + result["parent_column_name"] = attrs.parent_column_name + result["column_distinct_values_count"] = attrs.column_distinct_values_count + result["column_distinct_values_count_long"] = ( + attrs.column_distinct_values_count_long + ) + result["column_histogram"] = attrs.column_histogram + result["column_max"] = attrs.column_max + result["column_min"] = attrs.column_min + result["column_mean"] = attrs.column_mean + result["column_sum"] = attrs.column_sum + result["column_median"] = attrs.column_median + result["column_standard_deviation"] = attrs.column_standard_deviation + result["column_unique_values_count"] = attrs.column_unique_values_count + result["column_unique_values_count_long"] = attrs.column_unique_values_count_long + result["column_average"] = attrs.column_average + result["column_average_length"] = attrs.column_average_length + result["column_duplicate_values_count"] = attrs.column_duplicate_values_count + result["column_duplicate_values_count_long"] = ( + attrs.column_duplicate_values_count_long + ) + result["column_maximum_string_length"] = attrs.column_maximum_string_length + result["column_maxs"] = attrs.column_maxs + result["column_minimum_string_length"] = attrs.column_minimum_string_length + result["column_mins"] = attrs.column_mins + result["column_missing_values_count"] = attrs.column_missing_values_count + result["column_missing_values_count_long"] = attrs.column_missing_values_count_long + result["column_missing_values_percentage"] = attrs.column_missing_values_percentage + result["column_uniqueness_percentage"] = attrs.column_uniqueness_percentage + result["column_variance"] = attrs.column_variance + result["column_top_values"] = attrs.column_top_values + result["column_max_value"] = attrs.column_max_value + result["column_min_value"] = attrs.column_min_value + result["column_mean_value"] = attrs.column_mean_value + result["column_sum_value"] = attrs.column_sum_value + result["column_median_value"] = attrs.column_median_value + result["column_standard_deviation_value"] = attrs.column_standard_deviation_value + result["column_average_value"] = attrs.column_average_value + result["column_variance_value"] = attrs.column_variance_value + result["column_average_length_value"] = attrs.column_average_length_value + result["column_distribution_histogram"] = attrs.column_distribution_histogram + result["column_depth_level"] = attrs.column_depth_level + result["nosql_collection_name"] = attrs.nosql_collection_name + result["nosql_collection_qualified_name"] = attrs.nosql_collection_qualified_name + result["column_is_measure"] = attrs.column_is_measure + result["column_measure_type"] = attrs.column_measure_type + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _dremio_column_to_nested(dremio_column: DremioColumn) -> DremioColumnNested: + """Convert flat DremioColumn to nested format.""" + attrs = DremioColumnAttributes() + _populate_dremio_column_attrs(attrs, dremio_column) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + dremio_column, _DREMIO_COLUMN_REL_FIELDS, DremioColumnRelationshipAttributes + ) + return DremioColumnNested( + guid=dremio_column.guid, + type_name=dremio_column.type_name, + status=dremio_column.status, + version=dremio_column.version, + create_time=dremio_column.create_time, + update_time=dremio_column.update_time, + created_by=dremio_column.created_by, + updated_by=dremio_column.updated_by, + classifications=dremio_column.classifications, + classification_names=dremio_column.classification_names, + meanings=dremio_column.meanings, + labels=dremio_column.labels, + business_attributes=dremio_column.business_attributes, + custom_attributes=dremio_column.custom_attributes, + pending_tasks=dremio_column.pending_tasks, + proxy=dremio_column.proxy, + is_incomplete=dremio_column.is_incomplete, + provenance_type=dremio_column.provenance_type, + home_id=dremio_column.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _dremio_column_from_nested(nested: DremioColumnNested) -> DremioColumn: + """Convert nested format to flat DremioColumn.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else DremioColumnAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DREMIO_COLUMN_REL_FIELDS, + DremioColumnRelationshipAttributes, + ) + return DremioColumn( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_dremio_column_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _dremio_column_to_nested_bytes(dremio_column: DremioColumn, serde: Serde) -> bytes: + """Convert flat DremioColumn to nested JSON bytes.""" + return serde.encode(_dremio_column_to_nested(dremio_column)) + + +def _dremio_column_from_nested_bytes(data: bytes, serde: Serde) -> DremioColumn: + """Convert nested JSON bytes to flat DremioColumn.""" + nested = serde.decode(data, DremioColumnNested) + return _dremio_column_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +DremioColumn.DREMIO_ID = KeywordField("dremioId", "dremioId") +DremioColumn.DREMIO_SPACE_QUALIFIED_NAME = KeywordField( + "dremioSpaceQualifiedName", "dremioSpaceQualifiedName" +) +DremioColumn.DREMIO_SPACE_NAME = KeywordField("dremioSpaceName", "dremioSpaceName") +DremioColumn.DREMIO_SOURCE_QUALIFIED_NAME = KeywordField( + "dremioSourceQualifiedName", "dremioSourceQualifiedName" +) +DremioColumn.DREMIO_SOURCE_NAME = KeywordField("dremioSourceName", "dremioSourceName") +DremioColumn.DREMIO_PARENT_FOLDER_QUALIFIED_NAME = KeywordField( + "dremioParentFolderQualifiedName", "dremioParentFolderQualifiedName" +) +DremioColumn.DREMIO_FOLDER_HIERARCHY = KeywordField( + "dremioFolderHierarchy", "dremioFolderHierarchy" +) +DremioColumn.DREMIO_LABELS = KeywordField("dremioLabels", "dremioLabels") +DremioColumn.QUERY_COUNT = NumericField("queryCount", "queryCount") +DremioColumn.QUERY_USER_COUNT = NumericField("queryUserCount", "queryUserCount") +DremioColumn.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +DremioColumn.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +DremioColumn.DATABASE_NAME = KeywordField("databaseName", "databaseName") +DremioColumn.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +DremioColumn.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +DremioColumn.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +DremioColumn.TABLE_NAME = KeywordField("tableName", "tableName") +DremioColumn.TABLE_QUALIFIED_NAME = KeywordField( + "tableQualifiedName", "tableQualifiedName" +) +DremioColumn.VIEW_NAME = KeywordField("viewName", "viewName") +DremioColumn.VIEW_QUALIFIED_NAME = KeywordField( + "viewQualifiedName", "viewQualifiedName" +) +DremioColumn.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +DremioColumn.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +DremioColumn.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +DremioColumn.LAST_PROFILED_AT = NumericField("lastProfiledAt", "lastProfiledAt") +DremioColumn.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +DremioColumn.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +DremioColumn.DATA_TYPE = KeywordTextField("dataType", "dataType", "dataType.text") +DremioColumn.SUB_DATA_TYPE = KeywordField("subDataType", "subDataType") +DremioColumn.COLUMN_COMPRESSION = KeywordField("columnCompression", "columnCompression") +DremioColumn.COLUMN_ENCODING = KeywordField("columnEncoding", "columnEncoding") +DremioColumn.RAW_DATA_TYPE_DEFINITION = KeywordField( + "rawDataTypeDefinition", "rawDataTypeDefinition" +) +DremioColumn.ORDER = NumericField("order", "order") +DremioColumn.NESTED_COLUMN_ORDER = KeywordTextField( + "nestedColumnOrder", "nestedColumnOrder", "nestedColumnOrder.text" +) +DremioColumn.NESTED_COLUMN_COUNT = NumericField( + "nestedColumnCount", "nestedColumnCount" +) +DremioColumn.COLUMN_HIERARCHY = KeywordField("columnHierarchy", "columnHierarchy") +DremioColumn.IS_PARTITION = BooleanField("isPartition", "isPartition") +DremioColumn.PARTITION_ORDER = NumericField("partitionOrder", "partitionOrder") +DremioColumn.IS_CLUSTERED = BooleanField("isClustered", "isClustered") +DremioColumn.IS_PRIMARY = BooleanField("isPrimary", "isPrimary") +DremioColumn.IS_FOREIGN = BooleanField("isForeign", "isForeign") +DremioColumn.IS_INDEXED = BooleanField("isIndexed", "isIndexed") +DremioColumn.IS_SORT = BooleanField("isSort", "isSort") +DremioColumn.IS_DIST = BooleanField("isDist", "isDist") +DremioColumn.IS_PINNED = BooleanField("isPinned", "isPinned") +DremioColumn.PINNED_BY = KeywordField("pinnedBy", "pinnedBy") +DremioColumn.PINNED_AT = NumericField("pinnedAt", "pinnedAt") +DremioColumn.PRECISION = NumericField("precision", "precision") +DremioColumn.DEFAULT_VALUE = KeywordField("defaultValue", "defaultValue") +DremioColumn.IS_NULLABLE = BooleanField("isNullable", "isNullable") +DremioColumn.NUMERIC_SCALE = NumericField("numericScale", "numericScale") +DremioColumn.MAX_LENGTH = NumericField("maxLength", "maxLength") +DremioColumn.VALIDATIONS = KeywordField("validations", "validations") +DremioColumn.PARENT_COLUMN_QUALIFIED_NAME = KeywordTextField( + "parentColumnQualifiedName", + "parentColumnQualifiedName", + "parentColumnQualifiedName.text", +) +DremioColumn.PARENT_COLUMN_NAME = KeywordField("parentColumnName", "parentColumnName") +DremioColumn.COLUMN_DISTINCT_VALUES_COUNT = NumericField( + "columnDistinctValuesCount", "columnDistinctValuesCount" +) +DremioColumn.COLUMN_DISTINCT_VALUES_COUNT_LONG = NumericField( + "columnDistinctValuesCountLong", "columnDistinctValuesCountLong" +) +DremioColumn.COLUMN_HISTOGRAM = KeywordField("columnHistogram", "columnHistogram") +DremioColumn.COLUMN_MAX = NumericField("columnMax", "columnMax") +DremioColumn.COLUMN_MIN = NumericField("columnMin", "columnMin") +DremioColumn.COLUMN_MEAN = NumericField("columnMean", "columnMean") +DremioColumn.COLUMN_SUM = NumericField("columnSum", "columnSum") +DremioColumn.COLUMN_MEDIAN = NumericField("columnMedian", "columnMedian") +DremioColumn.COLUMN_STANDARD_DEVIATION = NumericField( + "columnStandardDeviation", "columnStandardDeviation" +) +DremioColumn.COLUMN_UNIQUE_VALUES_COUNT = NumericField( + "columnUniqueValuesCount", "columnUniqueValuesCount" +) +DremioColumn.COLUMN_UNIQUE_VALUES_COUNT_LONG = NumericField( + "columnUniqueValuesCountLong", "columnUniqueValuesCountLong" +) +DremioColumn.COLUMN_AVERAGE = NumericField("columnAverage", "columnAverage") +DremioColumn.COLUMN_AVERAGE_LENGTH = NumericField( + "columnAverageLength", "columnAverageLength" +) +DremioColumn.COLUMN_DUPLICATE_VALUES_COUNT = NumericField( + "columnDuplicateValuesCount", "columnDuplicateValuesCount" +) +DremioColumn.COLUMN_DUPLICATE_VALUES_COUNT_LONG = NumericField( + "columnDuplicateValuesCountLong", "columnDuplicateValuesCountLong" +) +DremioColumn.COLUMN_MAXIMUM_STRING_LENGTH = NumericField( + "columnMaximumStringLength", "columnMaximumStringLength" +) +DremioColumn.COLUMN_MAXS = KeywordField("columnMaxs", "columnMaxs") +DremioColumn.COLUMN_MINIMUM_STRING_LENGTH = NumericField( + "columnMinimumStringLength", "columnMinimumStringLength" +) +DremioColumn.COLUMN_MINS = KeywordField("columnMins", "columnMins") +DremioColumn.COLUMN_MISSING_VALUES_COUNT = NumericField( + "columnMissingValuesCount", "columnMissingValuesCount" +) +DremioColumn.COLUMN_MISSING_VALUES_COUNT_LONG = NumericField( + "columnMissingValuesCountLong", "columnMissingValuesCountLong" +) +DremioColumn.COLUMN_MISSING_VALUES_PERCENTAGE = NumericField( + "columnMissingValuesPercentage", "columnMissingValuesPercentage" +) +DremioColumn.COLUMN_UNIQUENESS_PERCENTAGE = NumericField( + "columnUniquenessPercentage", "columnUniquenessPercentage" +) +DremioColumn.COLUMN_VARIANCE = NumericField("columnVariance", "columnVariance") +DremioColumn.COLUMN_TOP_VALUES = KeywordField("columnTopValues", "columnTopValues") +DremioColumn.COLUMN_MAX_VALUE = NumericField("columnMaxValue", "columnMaxValue") +DremioColumn.COLUMN_MIN_VALUE = NumericField("columnMinValue", "columnMinValue") +DremioColumn.COLUMN_MEAN_VALUE = NumericField("columnMeanValue", "columnMeanValue") +DremioColumn.COLUMN_SUM_VALUE = NumericField("columnSumValue", "columnSumValue") +DremioColumn.COLUMN_MEDIAN_VALUE = NumericField( + "columnMedianValue", "columnMedianValue" +) +DremioColumn.COLUMN_STANDARD_DEVIATION_VALUE = NumericField( + "columnStandardDeviationValue", "columnStandardDeviationValue" +) +DremioColumn.COLUMN_AVERAGE_VALUE = NumericField( + "columnAverageValue", "columnAverageValue" +) +DremioColumn.COLUMN_VARIANCE_VALUE = NumericField( + "columnVarianceValue", "columnVarianceValue" +) +DremioColumn.COLUMN_AVERAGE_LENGTH_VALUE = NumericField( + "columnAverageLengthValue", "columnAverageLengthValue" +) +DremioColumn.COLUMN_DISTRIBUTION_HISTOGRAM = KeywordField( + "columnDistributionHistogram", "columnDistributionHistogram" +) +DremioColumn.COLUMN_DEPTH_LEVEL = NumericField("columnDepthLevel", "columnDepthLevel") +DremioColumn.NOSQL_COLLECTION_NAME = KeywordField( + "nosqlCollectionName", "nosqlCollectionName" +) +DremioColumn.NOSQL_COLLECTION_QUALIFIED_NAME = KeywordField( + "nosqlCollectionQualifiedName", "nosqlCollectionQualifiedName" +) +DremioColumn.COLUMN_IS_MEASURE = BooleanField("columnIsMeasure", "columnIsMeasure") +DremioColumn.COLUMN_MEASURE_TYPE = KeywordField( + "columnMeasureType", "columnMeasureType" +) +DremioColumn.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +DremioColumn.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +DremioColumn.ANOMALO_CHECKS = RelationField("anomaloChecks") +DremioColumn.APPLICATION = RelationField("application") +DremioColumn.APPLICATION_FIELD = RelationField("applicationField") +DremioColumn.COSMOS_MONGO_DB_COLLECTION = RelationField("cosmosMongoDBCollection") +DremioColumn.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +DremioColumn.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +DremioColumn.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +DremioColumn.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +DremioColumn.METRICS = RelationField("metrics") +DremioColumn.METRIC_TIMESTAMPS = RelationField("metricTimestamps") +DremioColumn.DATA_QUALITY_METRIC_DIMENSIONS = RelationField( + "dataQualityMetricDimensions" +) +DremioColumn.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +DremioColumn.DQ_BASE_COLUMN_RULES = RelationField("dqBaseColumnRules") +DremioColumn.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +DremioColumn.DQ_REFERENCE_COLUMN_RULES = RelationField("dqReferenceColumnRules") +DremioColumn.DBT_MODELS = RelationField("dbtModels") +DremioColumn.SQL_DBT_MODELS = RelationField("sqlDbtModels") +DremioColumn.DBT_TESTS = RelationField("dbtTests") +DremioColumn.DBT_SOURCES = RelationField("dbtSources") +DremioColumn.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +DremioColumn.DBT_METRICS = RelationField("dbtMetrics") +DremioColumn.DBT_MODEL_COLUMNS = RelationField("dbtModelColumns") +DremioColumn.COLUMN_DBT_MODEL_COLUMNS = RelationField("columnDbtModelColumns") +DremioColumn.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +DremioColumn.MEANINGS = RelationField("meanings") +DremioColumn.MONGO_DB_COLLECTION = RelationField("mongoDBCollection") +DremioColumn.MC_MONITORS = RelationField("mcMonitors") +DremioColumn.MC_INCIDENTS = RelationField("mcIncidents") +DremioColumn.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +DremioColumn.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +DremioColumn.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +DremioColumn.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +DremioColumn.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +DremioColumn.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +DremioColumn.FILES = RelationField("files") +DremioColumn.LINKS = RelationField("links") +DremioColumn.README = RelationField("readme") +DremioColumn.TABLE = RelationField("table") +DremioColumn.NESTED_COLUMNS = RelationField("nestedColumns") +DremioColumn.PARENT_COLUMN = RelationField("parentColumn") +DremioColumn.TABLE_PARTITION = RelationField("tablePartition") +DremioColumn.VIEW = RelationField("view") +DremioColumn.CALCULATION_VIEW = RelationField("calculationView") +DremioColumn.MATERIALISED_VIEW = RelationField("materialisedView") +DremioColumn.FOREIGN_KEY_TO = RelationField("foreignKeyTo") +DremioColumn.FOREIGN_KEY_FROM = RelationField("foreignKeyFrom") +DremioColumn.QUERIES = RelationField("queries") +DremioColumn.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +DremioColumn.SNOWFLAKE_DYNAMIC_TABLE = RelationField("snowflakeDynamicTable") +DremioColumn.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +DremioColumn.SODA_CHECKS = RelationField("sodaChecks") +DremioColumn.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +DremioColumn.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/dremio_folder.py b/pyatlan_v9/model/assets/dremio_folder.py new file mode 100644 index 000000000..eecfbd57d --- /dev/null +++ b/pyatlan_v9/model/assets/dremio_folder.py @@ -0,0 +1,997 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DremioFolder asset model with flattened inheritance. + +This module provides: +- DremioFolder: Flat asset class (easy to use) +- DremioFolderAttributes: Nested attributes struct (extends AssetAttributes) +- DremioFolderNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .snowflake_related import RelatedSnowflakeSemanticLogicalTable +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .dremio_related import ( + RelatedDremioFolder, + RelatedDremioPhysicalDataset, + RelatedDremioSource, + RelatedDremioSpace, + RelatedDremioVirtualDataset, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class DremioFolder(Asset): + """ + Instance of a Dremio Folder in Atlan. Represents organizational containers within spaces or sources that can contain datasets and sub-folders, enabling hierarchical data organization. + """ + + DREMIO_PARENT_ASSET_TYPE: ClassVar[Any] = None + DREMIO_ID: ClassVar[Any] = None + DREMIO_SPACE_QUALIFIED_NAME: ClassVar[Any] = None + DREMIO_SPACE_NAME: ClassVar[Any] = None + DREMIO_SOURCE_QUALIFIED_NAME: ClassVar[Any] = None + DREMIO_SOURCE_NAME: ClassVar[Any] = None + DREMIO_PARENT_FOLDER_QUALIFIED_NAME: ClassVar[Any] = None + DREMIO_FOLDER_HIERARCHY: ClassVar[Any] = None + DREMIO_LABELS: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + DREMIO_SOURCE: ClassVar[Any] = None + DREMIO_SPACE: ClassVar[Any] = None + DREMIO_SUB_FOLDERS: ClassVar[Any] = None + DREMIO_PARENT_FOLDER: ClassVar[Any] = None + DREMIO_PHYSICAL_DATASETS: ClassVar[Any] = None + DREMIO_VIRTUAL_DATASETS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "DremioFolder" + + dremio_parent_asset_type: Union[str, None, UnsetType] = UNSET + """Type of top level asset that contains this folder.""" + + dremio_id: Union[str, None, UnsetType] = UNSET + """Source ID of this asset in Dremio.""" + + dremio_space_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique qualified name of the Dremio Space containing this asset.""" + + dremio_space_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Dremio Space containing this asset.""" + + dremio_source_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique qualified name of the Dremio Source containing this asset.""" + + dremio_source_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Dremio Source containing this asset.""" + + dremio_parent_folder_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique qualified name of the immediate parent folder containing this asset.""" + + dremio_folder_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Ordered array of folder assets with qualified name and name representing the complete folder hierarchy path for this asset, from immediate parent to root folder.""" + + dremio_labels: Union[List[str], None, UnsetType] = UNSET + """Dremio Labels associated with this asset.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + dremio_source: Union[RelatedDremioSource, None, UnsetType] = UNSET + """Dremio Source that contains the folders.""" + + dremio_space: Union[RelatedDremioSpace, None, UnsetType] = UNSET + """Dremio Space that contains the folders.""" + + dremio_sub_folders: Union[List[RelatedDremioFolder], None, UnsetType] = UNSET + """Child folders nested within the parent Dremio Folder.""" + + dremio_parent_folder: Union[RelatedDremioFolder, None, UnsetType] = UNSET + """Parent Dremio Folder containing the sub-folders.""" + + dremio_physical_datasets: Union[ + List[RelatedDremioPhysicalDataset], None, UnsetType + ] = UNSET + """Physical datasets (tables) contained within the Dremio Folder.""" + + dremio_virtual_datasets: Union[ + List[RelatedDremioVirtualDataset], None, UnsetType + ] = UNSET + """Virtual datasets (views) contained within the Dremio Folder.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "DremioFolder" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _dremio_folder_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> DremioFolder: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + DremioFolder instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _dremio_folder_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DremioFolderAttributes(AssetAttributes): + """DremioFolder-specific attributes for nested API format.""" + + dremio_parent_asset_type: Union[str, None, UnsetType] = UNSET + """Type of top level asset that contains this folder.""" + + dremio_id: Union[str, None, UnsetType] = UNSET + """Source ID of this asset in Dremio.""" + + dremio_space_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique qualified name of the Dremio Space containing this asset.""" + + dremio_space_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Dremio Space containing this asset.""" + + dremio_source_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique qualified name of the Dremio Source containing this asset.""" + + dremio_source_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Dremio Source containing this asset.""" + + dremio_parent_folder_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique qualified name of the immediate parent folder containing this asset.""" + + dremio_folder_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Ordered array of folder assets with qualified name and name representing the complete folder hierarchy path for this asset, from immediate parent to root folder.""" + + dremio_labels: Union[List[str], None, UnsetType] = UNSET + """Dremio Labels associated with this asset.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + +class DremioFolderRelationshipAttributes(AssetRelationshipAttributes): + """DremioFolder-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + dremio_source: Union[RelatedDremioSource, None, UnsetType] = UNSET + """Dremio Source that contains the folders.""" + + dremio_space: Union[RelatedDremioSpace, None, UnsetType] = UNSET + """Dremio Space that contains the folders.""" + + dremio_sub_folders: Union[List[RelatedDremioFolder], None, UnsetType] = UNSET + """Child folders nested within the parent Dremio Folder.""" + + dremio_parent_folder: Union[RelatedDremioFolder, None, UnsetType] = UNSET + """Parent Dremio Folder containing the sub-folders.""" + + dremio_physical_datasets: Union[ + List[RelatedDremioPhysicalDataset], None, UnsetType + ] = UNSET + """Physical datasets (tables) contained within the Dremio Folder.""" + + dremio_virtual_datasets: Union[ + List[RelatedDremioVirtualDataset], None, UnsetType + ] = UNSET + """Virtual datasets (views) contained within the Dremio Folder.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DremioFolderNested(AssetNested): + """DremioFolder in nested API format for high-performance serialization.""" + + attributes: Union[DremioFolderAttributes, UnsetType] = UNSET + relationship_attributes: Union[DremioFolderRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + DremioFolderRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + DremioFolderRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DREMIO_FOLDER_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "dremio_source", + "dremio_space", + "dremio_sub_folders", + "dremio_parent_folder", + "dremio_physical_datasets", + "dremio_virtual_datasets", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_dremio_folder_attrs( + attrs: DremioFolderAttributes, obj: DremioFolder +) -> None: + """Populate DremioFolder-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.dremio_parent_asset_type = obj.dremio_parent_asset_type + attrs.dremio_id = obj.dremio_id + attrs.dremio_space_qualified_name = obj.dremio_space_qualified_name + attrs.dremio_space_name = obj.dremio_space_name + attrs.dremio_source_qualified_name = obj.dremio_source_qualified_name + attrs.dremio_source_name = obj.dremio_source_name + attrs.dremio_parent_folder_qualified_name = obj.dremio_parent_folder_qualified_name + attrs.dremio_folder_hierarchy = obj.dremio_folder_hierarchy + attrs.dremio_labels = obj.dremio_labels + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + + +def _extract_dremio_folder_attrs(attrs: DremioFolderAttributes) -> dict: + """Extract all DremioFolder attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["dremio_parent_asset_type"] = attrs.dremio_parent_asset_type + result["dremio_id"] = attrs.dremio_id + result["dremio_space_qualified_name"] = attrs.dremio_space_qualified_name + result["dremio_space_name"] = attrs.dremio_space_name + result["dremio_source_qualified_name"] = attrs.dremio_source_qualified_name + result["dremio_source_name"] = attrs.dremio_source_name + result["dremio_parent_folder_qualified_name"] = ( + attrs.dremio_parent_folder_qualified_name + ) + result["dremio_folder_hierarchy"] = attrs.dremio_folder_hierarchy + result["dremio_labels"] = attrs.dremio_labels + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _dremio_folder_to_nested(dremio_folder: DremioFolder) -> DremioFolderNested: + """Convert flat DremioFolder to nested format.""" + attrs = DremioFolderAttributes() + _populate_dremio_folder_attrs(attrs, dremio_folder) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + dremio_folder, _DREMIO_FOLDER_REL_FIELDS, DremioFolderRelationshipAttributes + ) + return DremioFolderNested( + guid=dremio_folder.guid, + type_name=dremio_folder.type_name, + status=dremio_folder.status, + version=dremio_folder.version, + create_time=dremio_folder.create_time, + update_time=dremio_folder.update_time, + created_by=dremio_folder.created_by, + updated_by=dremio_folder.updated_by, + classifications=dremio_folder.classifications, + classification_names=dremio_folder.classification_names, + meanings=dremio_folder.meanings, + labels=dremio_folder.labels, + business_attributes=dremio_folder.business_attributes, + custom_attributes=dremio_folder.custom_attributes, + pending_tasks=dremio_folder.pending_tasks, + proxy=dremio_folder.proxy, + is_incomplete=dremio_folder.is_incomplete, + provenance_type=dremio_folder.provenance_type, + home_id=dremio_folder.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _dremio_folder_from_nested(nested: DremioFolderNested) -> DremioFolder: + """Convert nested format to flat DremioFolder.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else DremioFolderAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DREMIO_FOLDER_REL_FIELDS, + DremioFolderRelationshipAttributes, + ) + return DremioFolder( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_dremio_folder_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _dremio_folder_to_nested_bytes(dremio_folder: DremioFolder, serde: Serde) -> bytes: + """Convert flat DremioFolder to nested JSON bytes.""" + return serde.encode(_dremio_folder_to_nested(dremio_folder)) + + +def _dremio_folder_from_nested_bytes(data: bytes, serde: Serde) -> DremioFolder: + """Convert nested JSON bytes to flat DremioFolder.""" + nested = serde.decode(data, DremioFolderNested) + return _dremio_folder_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, +) + +DremioFolder.DREMIO_PARENT_ASSET_TYPE = KeywordField( + "dremioParentAssetType", "dremioParentAssetType" +) +DremioFolder.DREMIO_ID = KeywordField("dremioId", "dremioId") +DremioFolder.DREMIO_SPACE_QUALIFIED_NAME = KeywordField( + "dremioSpaceQualifiedName", "dremioSpaceQualifiedName" +) +DremioFolder.DREMIO_SPACE_NAME = KeywordField("dremioSpaceName", "dremioSpaceName") +DremioFolder.DREMIO_SOURCE_QUALIFIED_NAME = KeywordField( + "dremioSourceQualifiedName", "dremioSourceQualifiedName" +) +DremioFolder.DREMIO_SOURCE_NAME = KeywordField("dremioSourceName", "dremioSourceName") +DremioFolder.DREMIO_PARENT_FOLDER_QUALIFIED_NAME = KeywordField( + "dremioParentFolderQualifiedName", "dremioParentFolderQualifiedName" +) +DremioFolder.DREMIO_FOLDER_HIERARCHY = KeywordField( + "dremioFolderHierarchy", "dremioFolderHierarchy" +) +DremioFolder.DREMIO_LABELS = KeywordField("dremioLabels", "dremioLabels") +DremioFolder.QUERY_COUNT = NumericField("queryCount", "queryCount") +DremioFolder.QUERY_USER_COUNT = NumericField("queryUserCount", "queryUserCount") +DremioFolder.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +DremioFolder.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +DremioFolder.DATABASE_NAME = KeywordField("databaseName", "databaseName") +DremioFolder.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +DremioFolder.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +DremioFolder.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +DremioFolder.TABLE_NAME = KeywordField("tableName", "tableName") +DremioFolder.TABLE_QUALIFIED_NAME = KeywordField( + "tableQualifiedName", "tableQualifiedName" +) +DremioFolder.VIEW_NAME = KeywordField("viewName", "viewName") +DremioFolder.VIEW_QUALIFIED_NAME = KeywordField( + "viewQualifiedName", "viewQualifiedName" +) +DremioFolder.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +DremioFolder.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +DremioFolder.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +DremioFolder.LAST_PROFILED_AT = NumericField("lastProfiledAt", "lastProfiledAt") +DremioFolder.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +DremioFolder.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +DremioFolder.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +DremioFolder.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +DremioFolder.ANOMALO_CHECKS = RelationField("anomaloChecks") +DremioFolder.APPLICATION = RelationField("application") +DremioFolder.APPLICATION_FIELD = RelationField("applicationField") +DremioFolder.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +DremioFolder.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +DremioFolder.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +DremioFolder.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +DremioFolder.METRICS = RelationField("metrics") +DremioFolder.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +DremioFolder.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +DremioFolder.DBT_MODELS = RelationField("dbtModels") +DremioFolder.SQL_DBT_MODELS = RelationField("sqlDbtModels") +DremioFolder.DBT_TESTS = RelationField("dbtTests") +DremioFolder.DBT_SOURCES = RelationField("dbtSources") +DremioFolder.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +DremioFolder.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +DremioFolder.DREMIO_SOURCE = RelationField("dremioSource") +DremioFolder.DREMIO_SPACE = RelationField("dremioSpace") +DremioFolder.DREMIO_SUB_FOLDERS = RelationField("dremioSubFolders") +DremioFolder.DREMIO_PARENT_FOLDER = RelationField("dremioParentFolder") +DremioFolder.DREMIO_PHYSICAL_DATASETS = RelationField("dremioPhysicalDatasets") +DremioFolder.DREMIO_VIRTUAL_DATASETS = RelationField("dremioVirtualDatasets") +DremioFolder.MEANINGS = RelationField("meanings") +DremioFolder.MC_MONITORS = RelationField("mcMonitors") +DremioFolder.MC_INCIDENTS = RelationField("mcIncidents") +DremioFolder.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +DremioFolder.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +DremioFolder.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +DremioFolder.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +DremioFolder.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +DremioFolder.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +DremioFolder.FILES = RelationField("files") +DremioFolder.LINKS = RelationField("links") +DremioFolder.README = RelationField("readme") +DremioFolder.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +DremioFolder.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +DremioFolder.SODA_CHECKS = RelationField("sodaChecks") +DremioFolder.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +DremioFolder.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/dremio_physical_dataset.py b/pyatlan_v9/model/assets/dremio_physical_dataset.py new file mode 100644 index 000000000..265dcdcd1 --- /dev/null +++ b/pyatlan_v9/model/assets/dremio_physical_dataset.py @@ -0,0 +1,1334 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DremioPhysicalDataset asset model with flattened inheritance. + +This module provides: +- DremioPhysicalDataset: Flat asset class (easy to use) +- DremioPhysicalDatasetAttributes: Nested attributes struct (extends AssetAttributes) +- DremioPhysicalDatasetNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .snowflake_related import RelatedSnowflakeSemanticLogicalTable +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from .sql_related import ( + RelatedColumn, + RelatedQuery, + RelatedSchema, + RelatedTable, + RelatedTablePartition, +) +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .dremio_related import RelatedDremioFolder, RelatedDremioSource + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class DremioPhysicalDataset(Asset): + """ + Instance of a Dremio Physical Dataset (Table) in Atlan. Represents actual data files or database tables that can be queried directly and serve as the foundation for virtual datasets. + """ + + DREMIO_ID: ClassVar[Any] = None + DREMIO_SPACE_QUALIFIED_NAME: ClassVar[Any] = None + DREMIO_SPACE_NAME: ClassVar[Any] = None + DREMIO_SOURCE_QUALIFIED_NAME: ClassVar[Any] = None + DREMIO_SOURCE_NAME: ClassVar[Any] = None + DREMIO_PARENT_FOLDER_QUALIFIED_NAME: ClassVar[Any] = None + DREMIO_FOLDER_HIERARCHY: ClassVar[Any] = None + DREMIO_LABELS: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + COLUMN_COUNT: ClassVar[Any] = None + ROW_COUNT: ClassVar[Any] = None + SIZE_BYTES: ClassVar[Any] = None + TABLE_OBJECT_COUNT: ClassVar[Any] = None + ALIAS: ClassVar[Any] = None + IS_TEMPORARY: ClassVar[Any] = None + IS_QUERY_PREVIEW: ClassVar[Any] = None + QUERY_PREVIEW_CONFIG: ClassVar[Any] = None + EXTERNAL_LOCATION: ClassVar[Any] = None + EXTERNAL_LOCATION_REGION: ClassVar[Any] = None + EXTERNAL_LOCATION_FORMAT: ClassVar[Any] = None + IS_PARTITIONED: ClassVar[Any] = None + PARTITION_STRATEGY: ClassVar[Any] = None + PARTITION_COUNT: ClassVar[Any] = None + TABLE_DEFINITION: ClassVar[Any] = None + PARTITION_LIST: ClassVar[Any] = None + IS_SHARDED: ClassVar[Any] = None + TABLE_TYPE: ClassVar[Any] = None + ICEBERG_CATALOG_NAME: ClassVar[Any] = None + ICEBERG_TABLE_TYPE: ClassVar[Any] = None + ICEBERG_CATALOG_SOURCE: ClassVar[Any] = None + ICEBERG_CATALOG_TABLE_NAME: ClassVar[Any] = None + TABLE_IMPALA_PARAMETERS: ClassVar[Any] = None + ICEBERG_CATALOG_TABLE_NAMESPACE: ClassVar[Any] = None + TABLE_EXTERNAL_VOLUME_NAME: ClassVar[Any] = None + ICEBERG_TABLE_BASE_LOCATION: ClassVar[Any] = None + TABLE_RETENTION_TIME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + DREMIO_SOURCE: ClassVar[Any] = None + DREMIO_FOLDER: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + COLUMNS: ClassVar[Any] = None + QUERIES: ClassVar[Any] = None + ATLAN_SCHEMA: ClassVar[Any] = None + DIMENSIONS: ClassVar[Any] = None + FACTS: ClassVar[Any] = None + PARTITIONS: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "DremioPhysicalDataset" + + dremio_id: Union[str, None, UnsetType] = UNSET + """Source ID of this asset in Dremio.""" + + dremio_space_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique qualified name of the Dremio Space containing this asset.""" + + dremio_space_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Dremio Space containing this asset.""" + + dremio_source_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique qualified name of the Dremio Source containing this asset.""" + + dremio_source_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Dremio Source containing this asset.""" + + dremio_parent_folder_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique qualified name of the immediate parent folder containing this asset.""" + + dremio_folder_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Ordered array of folder assets with qualified name and name representing the complete folder hierarchy path for this asset, from immediate parent to root folder.""" + + dremio_labels: Union[List[str], None, UnsetType] = UNSET + """Dremio Labels associated with this asset.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this table.""" + + row_count: Union[int, None, UnsetType] = UNSET + """Number of rows in this table.""" + + size_bytes: Union[int, None, UnsetType] = UNSET + """Size of this table, in bytes.""" + + table_object_count: Union[int, None, UnsetType] = UNSET + """Number of objects in this table.""" + + alias: Union[str, None, UnsetType] = UNSET + """Alias for this table.""" + + is_temporary: Union[bool, None, UnsetType] = UNSET + """Whether this table is temporary (true) or not (false).""" + + is_query_preview: Union[bool, None, UnsetType] = UNSET + """Whether preview queries are allowed for this table (true) or not (false).""" + + query_preview_config: Union[Dict[str, str], None, UnsetType] = UNSET + """Configuration for preview queries.""" + + external_location: Union[str, None, UnsetType] = UNSET + """External location of this table, for example: an S3 object location.""" + + external_location_region: Union[str, None, UnsetType] = UNSET + """Region of the external location of this table, for example: S3 region.""" + + external_location_format: Union[str, None, UnsetType] = UNSET + """Format of the external location of this table, for example: JSON, CSV, PARQUET, etc.""" + + is_partitioned: Union[bool, None, UnsetType] = UNSET + """Whether this table is partitioned (true) or not (false).""" + + partition_strategy: Union[str, None, UnsetType] = UNSET + """Partition strategy for this table.""" + + partition_count: Union[int, None, UnsetType] = UNSET + """Number of partitions in this table.""" + + table_definition: Union[str, None, UnsetType] = UNSET + """Definition of the table.""" + + partition_list: Union[str, None, UnsetType] = UNSET + """List of partitions in this table.""" + + is_sharded: Union[bool, None, UnsetType] = UNSET + """Whether this table is a sharded table (true) or not (false).""" + + table_type: Union[str, None, UnsetType] = UNSET + """Type of the table.""" + + iceberg_catalog_name: Union[str, None, UnsetType] = UNSET + """Iceberg table catalog name (can be any user defined name)""" + + iceberg_table_type: Union[str, None, UnsetType] = UNSET + """Iceberg table type (managed vs unmanaged)""" + + iceberg_catalog_source: Union[str, None, UnsetType] = UNSET + """Iceberg table catalog type (glue, polaris, snowflake)""" + + iceberg_catalog_table_name: Union[str, None, UnsetType] = UNSET + """Catalog table name (actual table name on the catalog side).""" + + table_impala_parameters: Union[Dict[str, str], None, UnsetType] = UNSET + """Extra attributes for Impala""" + + iceberg_catalog_table_namespace: Union[str, None, UnsetType] = UNSET + """Catalog table namespace (actual database name on the catalog side).""" + + table_external_volume_name: Union[str, None, UnsetType] = UNSET + """External volume name for the table.""" + + iceberg_table_base_location: Union[str, None, UnsetType] = UNSET + """Iceberg table base location inside the external volume.""" + + table_retention_time: Union[int, None, UnsetType] = UNSET + """Data retention time in days.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + dremio_source: Union[RelatedDremioSource, None, UnsetType] = UNSET + """Dremio Source that contains the physical datasets (tables).""" + + dremio_folder: Union[RelatedDremioFolder, None, UnsetType] = UNSET + """Dremio Folder that contains the physical datasets (tables).""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Columns that exist within this table.""" + + queries: Union[List[RelatedQuery], None, UnsetType] = UNSET + """Queries that access this table.""" + + atlan_schema: Union[RelatedSchema, None, UnsetType] = UNSET + """Schema in which this table exists.""" + + dimensions: Union[List[RelatedTable], None, UnsetType] = UNSET + """""" + + facts: Union[List[RelatedTable], None, UnsetType] = UNSET + """""" + + partitions: Union[List[RelatedTablePartition], None, UnsetType] = UNSET + """Partitions that exist within this table.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "DremioPhysicalDataset" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _dremio_physical_dataset_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> DremioPhysicalDataset: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + DremioPhysicalDataset instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _dremio_physical_dataset_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DremioPhysicalDatasetAttributes(AssetAttributes): + """DremioPhysicalDataset-specific attributes for nested API format.""" + + dremio_id: Union[str, None, UnsetType] = UNSET + """Source ID of this asset in Dremio.""" + + dremio_space_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique qualified name of the Dremio Space containing this asset.""" + + dremio_space_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Dremio Space containing this asset.""" + + dremio_source_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique qualified name of the Dremio Source containing this asset.""" + + dremio_source_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Dremio Source containing this asset.""" + + dremio_parent_folder_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique qualified name of the immediate parent folder containing this asset.""" + + dremio_folder_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Ordered array of folder assets with qualified name and name representing the complete folder hierarchy path for this asset, from immediate parent to root folder.""" + + dremio_labels: Union[List[str], None, UnsetType] = UNSET + """Dremio Labels associated with this asset.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this table.""" + + row_count: Union[int, None, UnsetType] = UNSET + """Number of rows in this table.""" + + size_bytes: Union[int, None, UnsetType] = UNSET + """Size of this table, in bytes.""" + + table_object_count: Union[int, None, UnsetType] = UNSET + """Number of objects in this table.""" + + alias: Union[str, None, UnsetType] = UNSET + """Alias for this table.""" + + is_temporary: Union[bool, None, UnsetType] = UNSET + """Whether this table is temporary (true) or not (false).""" + + is_query_preview: Union[bool, None, UnsetType] = UNSET + """Whether preview queries are allowed for this table (true) or not (false).""" + + query_preview_config: Union[Dict[str, str], None, UnsetType] = UNSET + """Configuration for preview queries.""" + + external_location: Union[str, None, UnsetType] = UNSET + """External location of this table, for example: an S3 object location.""" + + external_location_region: Union[str, None, UnsetType] = UNSET + """Region of the external location of this table, for example: S3 region.""" + + external_location_format: Union[str, None, UnsetType] = UNSET + """Format of the external location of this table, for example: JSON, CSV, PARQUET, etc.""" + + is_partitioned: Union[bool, None, UnsetType] = UNSET + """Whether this table is partitioned (true) or not (false).""" + + partition_strategy: Union[str, None, UnsetType] = UNSET + """Partition strategy for this table.""" + + partition_count: Union[int, None, UnsetType] = UNSET + """Number of partitions in this table.""" + + table_definition: Union[str, None, UnsetType] = UNSET + """Definition of the table.""" + + partition_list: Union[str, None, UnsetType] = UNSET + """List of partitions in this table.""" + + is_sharded: Union[bool, None, UnsetType] = UNSET + """Whether this table is a sharded table (true) or not (false).""" + + table_type: Union[str, None, UnsetType] = UNSET + """Type of the table.""" + + iceberg_catalog_name: Union[str, None, UnsetType] = UNSET + """Iceberg table catalog name (can be any user defined name)""" + + iceberg_table_type: Union[str, None, UnsetType] = UNSET + """Iceberg table type (managed vs unmanaged)""" + + iceberg_catalog_source: Union[str, None, UnsetType] = UNSET + """Iceberg table catalog type (glue, polaris, snowflake)""" + + iceberg_catalog_table_name: Union[str, None, UnsetType] = UNSET + """Catalog table name (actual table name on the catalog side).""" + + table_impala_parameters: Union[Dict[str, str], None, UnsetType] = UNSET + """Extra attributes for Impala""" + + iceberg_catalog_table_namespace: Union[str, None, UnsetType] = UNSET + """Catalog table namespace (actual database name on the catalog side).""" + + table_external_volume_name: Union[str, None, UnsetType] = UNSET + """External volume name for the table.""" + + iceberg_table_base_location: Union[str, None, UnsetType] = UNSET + """Iceberg table base location inside the external volume.""" + + table_retention_time: Union[int, None, UnsetType] = UNSET + """Data retention time in days.""" + + +class DremioPhysicalDatasetRelationshipAttributes(AssetRelationshipAttributes): + """DremioPhysicalDataset-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + dremio_source: Union[RelatedDremioSource, None, UnsetType] = UNSET + """Dremio Source that contains the physical datasets (tables).""" + + dremio_folder: Union[RelatedDremioFolder, None, UnsetType] = UNSET + """Dremio Folder that contains the physical datasets (tables).""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Columns that exist within this table.""" + + queries: Union[List[RelatedQuery], None, UnsetType] = UNSET + """Queries that access this table.""" + + atlan_schema: Union[RelatedSchema, None, UnsetType] = UNSET + """Schema in which this table exists.""" + + dimensions: Union[List[RelatedTable], None, UnsetType] = UNSET + """""" + + facts: Union[List[RelatedTable], None, UnsetType] = UNSET + """""" + + partitions: Union[List[RelatedTablePartition], None, UnsetType] = UNSET + """Partitions that exist within this table.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DremioPhysicalDatasetNested(AssetNested): + """DremioPhysicalDataset in nested API format for high-performance serialization.""" + + attributes: Union[DremioPhysicalDatasetAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + DremioPhysicalDatasetRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + DremioPhysicalDatasetRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + DremioPhysicalDatasetRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DREMIO_PHYSICAL_DATASET_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "dremio_source", + "dremio_folder", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "columns", + "queries", + "atlan_schema", + "dimensions", + "facts", + "partitions", + "schema_registry_subjects", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_dremio_physical_dataset_attrs( + attrs: DremioPhysicalDatasetAttributes, obj: DremioPhysicalDataset +) -> None: + """Populate DremioPhysicalDataset-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.dremio_id = obj.dremio_id + attrs.dremio_space_qualified_name = obj.dremio_space_qualified_name + attrs.dremio_space_name = obj.dremio_space_name + attrs.dremio_source_qualified_name = obj.dremio_source_qualified_name + attrs.dremio_source_name = obj.dremio_source_name + attrs.dremio_parent_folder_qualified_name = obj.dremio_parent_folder_qualified_name + attrs.dremio_folder_hierarchy = obj.dremio_folder_hierarchy + attrs.dremio_labels = obj.dremio_labels + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + attrs.column_count = obj.column_count + attrs.row_count = obj.row_count + attrs.size_bytes = obj.size_bytes + attrs.table_object_count = obj.table_object_count + attrs.alias = obj.alias + attrs.is_temporary = obj.is_temporary + attrs.is_query_preview = obj.is_query_preview + attrs.query_preview_config = obj.query_preview_config + attrs.external_location = obj.external_location + attrs.external_location_region = obj.external_location_region + attrs.external_location_format = obj.external_location_format + attrs.is_partitioned = obj.is_partitioned + attrs.partition_strategy = obj.partition_strategy + attrs.partition_count = obj.partition_count + attrs.table_definition = obj.table_definition + attrs.partition_list = obj.partition_list + attrs.is_sharded = obj.is_sharded + attrs.table_type = obj.table_type + attrs.iceberg_catalog_name = obj.iceberg_catalog_name + attrs.iceberg_table_type = obj.iceberg_table_type + attrs.iceberg_catalog_source = obj.iceberg_catalog_source + attrs.iceberg_catalog_table_name = obj.iceberg_catalog_table_name + attrs.table_impala_parameters = obj.table_impala_parameters + attrs.iceberg_catalog_table_namespace = obj.iceberg_catalog_table_namespace + attrs.table_external_volume_name = obj.table_external_volume_name + attrs.iceberg_table_base_location = obj.iceberg_table_base_location + attrs.table_retention_time = obj.table_retention_time + + +def _extract_dremio_physical_dataset_attrs( + attrs: DremioPhysicalDatasetAttributes, +) -> dict: + """Extract all DremioPhysicalDataset attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["dremio_id"] = attrs.dremio_id + result["dremio_space_qualified_name"] = attrs.dremio_space_qualified_name + result["dremio_space_name"] = attrs.dremio_space_name + result["dremio_source_qualified_name"] = attrs.dremio_source_qualified_name + result["dremio_source_name"] = attrs.dremio_source_name + result["dremio_parent_folder_qualified_name"] = ( + attrs.dremio_parent_folder_qualified_name + ) + result["dremio_folder_hierarchy"] = attrs.dremio_folder_hierarchy + result["dremio_labels"] = attrs.dremio_labels + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + result["column_count"] = attrs.column_count + result["row_count"] = attrs.row_count + result["size_bytes"] = attrs.size_bytes + result["table_object_count"] = attrs.table_object_count + result["alias"] = attrs.alias + result["is_temporary"] = attrs.is_temporary + result["is_query_preview"] = attrs.is_query_preview + result["query_preview_config"] = attrs.query_preview_config + result["external_location"] = attrs.external_location + result["external_location_region"] = attrs.external_location_region + result["external_location_format"] = attrs.external_location_format + result["is_partitioned"] = attrs.is_partitioned + result["partition_strategy"] = attrs.partition_strategy + result["partition_count"] = attrs.partition_count + result["table_definition"] = attrs.table_definition + result["partition_list"] = attrs.partition_list + result["is_sharded"] = attrs.is_sharded + result["table_type"] = attrs.table_type + result["iceberg_catalog_name"] = attrs.iceberg_catalog_name + result["iceberg_table_type"] = attrs.iceberg_table_type + result["iceberg_catalog_source"] = attrs.iceberg_catalog_source + result["iceberg_catalog_table_name"] = attrs.iceberg_catalog_table_name + result["table_impala_parameters"] = attrs.table_impala_parameters + result["iceberg_catalog_table_namespace"] = attrs.iceberg_catalog_table_namespace + result["table_external_volume_name"] = attrs.table_external_volume_name + result["iceberg_table_base_location"] = attrs.iceberg_table_base_location + result["table_retention_time"] = attrs.table_retention_time + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _dremio_physical_dataset_to_nested( + dremio_physical_dataset: DremioPhysicalDataset, +) -> DremioPhysicalDatasetNested: + """Convert flat DremioPhysicalDataset to nested format.""" + attrs = DremioPhysicalDatasetAttributes() + _populate_dremio_physical_dataset_attrs(attrs, dremio_physical_dataset) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + dremio_physical_dataset, + _DREMIO_PHYSICAL_DATASET_REL_FIELDS, + DremioPhysicalDatasetRelationshipAttributes, + ) + return DremioPhysicalDatasetNested( + guid=dremio_physical_dataset.guid, + type_name=dremio_physical_dataset.type_name, + status=dremio_physical_dataset.status, + version=dremio_physical_dataset.version, + create_time=dremio_physical_dataset.create_time, + update_time=dremio_physical_dataset.update_time, + created_by=dremio_physical_dataset.created_by, + updated_by=dremio_physical_dataset.updated_by, + classifications=dremio_physical_dataset.classifications, + classification_names=dremio_physical_dataset.classification_names, + meanings=dremio_physical_dataset.meanings, + labels=dremio_physical_dataset.labels, + business_attributes=dremio_physical_dataset.business_attributes, + custom_attributes=dremio_physical_dataset.custom_attributes, + pending_tasks=dremio_physical_dataset.pending_tasks, + proxy=dremio_physical_dataset.proxy, + is_incomplete=dremio_physical_dataset.is_incomplete, + provenance_type=dremio_physical_dataset.provenance_type, + home_id=dremio_physical_dataset.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _dremio_physical_dataset_from_nested( + nested: DremioPhysicalDatasetNested, +) -> DremioPhysicalDataset: + """Convert nested format to flat DremioPhysicalDataset.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else DremioPhysicalDatasetAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DREMIO_PHYSICAL_DATASET_REL_FIELDS, + DremioPhysicalDatasetRelationshipAttributes, + ) + return DremioPhysicalDataset( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_dremio_physical_dataset_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _dremio_physical_dataset_to_nested_bytes( + dremio_physical_dataset: DremioPhysicalDataset, serde: Serde +) -> bytes: + """Convert flat DremioPhysicalDataset to nested JSON bytes.""" + return serde.encode(_dremio_physical_dataset_to_nested(dremio_physical_dataset)) + + +def _dremio_physical_dataset_from_nested_bytes( + data: bytes, serde: Serde +) -> DremioPhysicalDataset: + """Convert nested JSON bytes to flat DremioPhysicalDataset.""" + nested = serde.decode(data, DremioPhysicalDatasetNested) + return _dremio_physical_dataset_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, +) + +DremioPhysicalDataset.DREMIO_ID = KeywordField("dremioId", "dremioId") +DremioPhysicalDataset.DREMIO_SPACE_QUALIFIED_NAME = KeywordField( + "dremioSpaceQualifiedName", "dremioSpaceQualifiedName" +) +DremioPhysicalDataset.DREMIO_SPACE_NAME = KeywordField( + "dremioSpaceName", "dremioSpaceName" +) +DremioPhysicalDataset.DREMIO_SOURCE_QUALIFIED_NAME = KeywordField( + "dremioSourceQualifiedName", "dremioSourceQualifiedName" +) +DremioPhysicalDataset.DREMIO_SOURCE_NAME = KeywordField( + "dremioSourceName", "dremioSourceName" +) +DremioPhysicalDataset.DREMIO_PARENT_FOLDER_QUALIFIED_NAME = KeywordField( + "dremioParentFolderQualifiedName", "dremioParentFolderQualifiedName" +) +DremioPhysicalDataset.DREMIO_FOLDER_HIERARCHY = KeywordField( + "dremioFolderHierarchy", "dremioFolderHierarchy" +) +DremioPhysicalDataset.DREMIO_LABELS = KeywordField("dremioLabels", "dremioLabels") +DremioPhysicalDataset.QUERY_COUNT = NumericField("queryCount", "queryCount") +DremioPhysicalDataset.QUERY_USER_COUNT = NumericField( + "queryUserCount", "queryUserCount" +) +DremioPhysicalDataset.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +DremioPhysicalDataset.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +DremioPhysicalDataset.DATABASE_NAME = KeywordField("databaseName", "databaseName") +DremioPhysicalDataset.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +DremioPhysicalDataset.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +DremioPhysicalDataset.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +DremioPhysicalDataset.TABLE_NAME = KeywordField("tableName", "tableName") +DremioPhysicalDataset.TABLE_QUALIFIED_NAME = KeywordField( + "tableQualifiedName", "tableQualifiedName" +) +DremioPhysicalDataset.VIEW_NAME = KeywordField("viewName", "viewName") +DremioPhysicalDataset.VIEW_QUALIFIED_NAME = KeywordField( + "viewQualifiedName", "viewQualifiedName" +) +DremioPhysicalDataset.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +DremioPhysicalDataset.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +DremioPhysicalDataset.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +DremioPhysicalDataset.LAST_PROFILED_AT = NumericField( + "lastProfiledAt", "lastProfiledAt" +) +DremioPhysicalDataset.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +DremioPhysicalDataset.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +DremioPhysicalDataset.COLUMN_COUNT = NumericField("columnCount", "columnCount") +DremioPhysicalDataset.ROW_COUNT = NumericField("rowCount", "rowCount") +DremioPhysicalDataset.SIZE_BYTES = NumericField("sizeBytes", "sizeBytes") +DremioPhysicalDataset.TABLE_OBJECT_COUNT = NumericField( + "tableObjectCount", "tableObjectCount" +) +DremioPhysicalDataset.ALIAS = KeywordField("alias", "alias") +DremioPhysicalDataset.IS_TEMPORARY = BooleanField("isTemporary", "isTemporary") +DremioPhysicalDataset.IS_QUERY_PREVIEW = BooleanField( + "isQueryPreview", "isQueryPreview" +) +DremioPhysicalDataset.QUERY_PREVIEW_CONFIG = KeywordField( + "queryPreviewConfig", "queryPreviewConfig" +) +DremioPhysicalDataset.EXTERNAL_LOCATION = KeywordField( + "externalLocation", "externalLocation" +) +DremioPhysicalDataset.EXTERNAL_LOCATION_REGION = KeywordField( + "externalLocationRegion", "externalLocationRegion" +) +DremioPhysicalDataset.EXTERNAL_LOCATION_FORMAT = KeywordField( + "externalLocationFormat", "externalLocationFormat" +) +DremioPhysicalDataset.IS_PARTITIONED = BooleanField("isPartitioned", "isPartitioned") +DremioPhysicalDataset.PARTITION_STRATEGY = KeywordField( + "partitionStrategy", "partitionStrategy" +) +DremioPhysicalDataset.PARTITION_COUNT = NumericField("partitionCount", "partitionCount") +DremioPhysicalDataset.TABLE_DEFINITION = KeywordField( + "tableDefinition", "tableDefinition" +) +DremioPhysicalDataset.PARTITION_LIST = KeywordField("partitionList", "partitionList") +DremioPhysicalDataset.IS_SHARDED = BooleanField("isSharded", "isSharded") +DremioPhysicalDataset.TABLE_TYPE = KeywordField("tableType", "tableType") +DremioPhysicalDataset.ICEBERG_CATALOG_NAME = KeywordField( + "icebergCatalogName", "icebergCatalogName" +) +DremioPhysicalDataset.ICEBERG_TABLE_TYPE = KeywordField( + "icebergTableType", "icebergTableType" +) +DremioPhysicalDataset.ICEBERG_CATALOG_SOURCE = KeywordField( + "icebergCatalogSource", "icebergCatalogSource" +) +DremioPhysicalDataset.ICEBERG_CATALOG_TABLE_NAME = KeywordField( + "icebergCatalogTableName", "icebergCatalogTableName" +) +DremioPhysicalDataset.TABLE_IMPALA_PARAMETERS = KeywordField( + "tableImpalaParameters", "tableImpalaParameters" +) +DremioPhysicalDataset.ICEBERG_CATALOG_TABLE_NAMESPACE = KeywordField( + "icebergCatalogTableNamespace", "icebergCatalogTableNamespace" +) +DremioPhysicalDataset.TABLE_EXTERNAL_VOLUME_NAME = KeywordField( + "tableExternalVolumeName", "tableExternalVolumeName" +) +DremioPhysicalDataset.ICEBERG_TABLE_BASE_LOCATION = KeywordField( + "icebergTableBaseLocation", "icebergTableBaseLocation" +) +DremioPhysicalDataset.TABLE_RETENTION_TIME = NumericField( + "tableRetentionTime", "tableRetentionTime" +) +DremioPhysicalDataset.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +DremioPhysicalDataset.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +DremioPhysicalDataset.ANOMALO_CHECKS = RelationField("anomaloChecks") +DremioPhysicalDataset.APPLICATION = RelationField("application") +DremioPhysicalDataset.APPLICATION_FIELD = RelationField("applicationField") +DremioPhysicalDataset.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +DremioPhysicalDataset.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +DremioPhysicalDataset.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +DremioPhysicalDataset.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +DremioPhysicalDataset.METRICS = RelationField("metrics") +DremioPhysicalDataset.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +DremioPhysicalDataset.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +DremioPhysicalDataset.DBT_MODELS = RelationField("dbtModels") +DremioPhysicalDataset.SQL_DBT_MODELS = RelationField("sqlDbtModels") +DremioPhysicalDataset.DBT_TESTS = RelationField("dbtTests") +DremioPhysicalDataset.DBT_SOURCES = RelationField("dbtSources") +DremioPhysicalDataset.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +DremioPhysicalDataset.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +DremioPhysicalDataset.DREMIO_SOURCE = RelationField("dremioSource") +DremioPhysicalDataset.DREMIO_FOLDER = RelationField("dremioFolder") +DremioPhysicalDataset.MEANINGS = RelationField("meanings") +DremioPhysicalDataset.MC_MONITORS = RelationField("mcMonitors") +DremioPhysicalDataset.MC_INCIDENTS = RelationField("mcIncidents") +DremioPhysicalDataset.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +DremioPhysicalDataset.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +DremioPhysicalDataset.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +DremioPhysicalDataset.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +DremioPhysicalDataset.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +DremioPhysicalDataset.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +DremioPhysicalDataset.FILES = RelationField("files") +DremioPhysicalDataset.LINKS = RelationField("links") +DremioPhysicalDataset.README = RelationField("readme") +DremioPhysicalDataset.COLUMNS = RelationField("columns") +DremioPhysicalDataset.QUERIES = RelationField("queries") +DremioPhysicalDataset.ATLAN_SCHEMA = RelationField("atlanSchema") +DremioPhysicalDataset.DIMENSIONS = RelationField("dimensions") +DremioPhysicalDataset.FACTS = RelationField("facts") +DremioPhysicalDataset.PARTITIONS = RelationField("partitions") +DremioPhysicalDataset.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +DremioPhysicalDataset.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +DremioPhysicalDataset.SODA_CHECKS = RelationField("sodaChecks") +DremioPhysicalDataset.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +DremioPhysicalDataset.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/dremio_related.py b/pyatlan_v9/model/assets/dremio_related.py new file mode 100644 index 000000000..ccfff7004 --- /dev/null +++ b/pyatlan_v9/model/assets/dremio_related.py @@ -0,0 +1,179 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Dremio module. + +This module contains all Related{Type} classes for the Dremio type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .referenceable_related import RelatedReferenceable +from .sql_related import RelatedSQL + +__all__ = [ + "RelatedDremio", + "RelatedDremioSpace", + "RelatedDremioSource", + "RelatedDremioFolder", + "RelatedDremioPhysicalDataset", + "RelatedDremioVirtualDataset", + "RelatedDremioColumn", +] + + +class RelatedDremio(RelatedSQL): + """ + Related entity reference for Dremio assets. + + Extends RelatedSQL with Dremio-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Dremio" so it serializes correctly + + dremio_id: Union[str, None, UnsetType] = UNSET + """Source ID of this asset in Dremio.""" + + dremio_space_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique qualified name of the Dremio Space containing this asset.""" + + dremio_space_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Dremio Space containing this asset.""" + + dremio_source_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique qualified name of the Dremio Source containing this asset.""" + + dremio_source_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Dremio Source containing this asset.""" + + dremio_parent_folder_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique qualified name of the immediate parent folder containing this asset.""" + + dremio_folder_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Ordered array of folder assets with qualified name and name representing the complete folder hierarchy path for this asset, from immediate parent to root folder.""" + + dremio_labels: Union[List[str], None, UnsetType] = UNSET + """Dremio Labels associated with this asset.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Dremio" + + +class RelatedDremioSpace(RelatedDremio): + """ + Related entity reference for DremioSpace assets. + + Extends RelatedDremio with DremioSpace-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DremioSpace" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DremioSpace" + + +class RelatedDremioSource(RelatedDremio): + """ + Related entity reference for DremioSource assets. + + Extends RelatedDremio with DremioSource-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DremioSource" so it serializes correctly + + dremio_source_type: Union[str, None, UnsetType] = UNSET + """Type of external source.""" + + dremio_source_connection_configs: Union[Dict[str, str], None, UnsetType] = UNSET + """Configuration parameters for connecting to the external source.""" + + dremio_source_acceleration_settings: Union[Dict[str, str], None, UnsetType] = UNSET + """Default acceleration settings for datasets in this source.""" + + dremio_source_metadata_policies: Union[Dict[str, str], None, UnsetType] = UNSET + """Metadata refresh and caching policies.""" + + dremio_source_health_status: Union[str, None, UnsetType] = UNSET + """Current health status of the source connection.""" + + dremio_source_health_status_message: Union[str, None, UnsetType] = UNSET + """Current health status message of the source connection.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DremioSource" + + +class RelatedDremioFolder(RelatedDremio): + """ + Related entity reference for DremioFolder assets. + + Extends RelatedDremio with DremioFolder-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DremioFolder" so it serializes correctly + + dremio_parent_asset_type: Union[str, None, UnsetType] = UNSET + """Type of top level asset that contains this folder.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DremioFolder" + + +class RelatedDremioPhysicalDataset(RelatedDremio): + """ + Related entity reference for DremioPhysicalDataset assets. + + Extends RelatedDremio with DremioPhysicalDataset-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DremioPhysicalDataset" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DremioPhysicalDataset" + + +class RelatedDremioVirtualDataset(RelatedDremio): + """ + Related entity reference for DremioVirtualDataset assets. + + Extends RelatedDremio with DremioVirtualDataset-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DremioVirtualDataset" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DremioVirtualDataset" + + +class RelatedDremioColumn(RelatedDremio): + """ + Related entity reference for DremioColumn assets. + + Extends RelatedDremio with DremioColumn-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DremioColumn" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DremioColumn" diff --git a/pyatlan_v9/model/assets/dremio_source.py b/pyatlan_v9/model/assets/dremio_source.py new file mode 100644 index 000000000..e516d56fb --- /dev/null +++ b/pyatlan_v9/model/assets/dremio_source.py @@ -0,0 +1,1006 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DremioSource asset model with flattened inheritance. + +This module provides: +- DremioSource: Flat asset class (easy to use) +- DremioSourceAttributes: Nested attributes struct (extends AssetAttributes) +- DremioSourceNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .snowflake_related import RelatedSnowflakeSemanticLogicalTable +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .dremio_related import RelatedDremioFolder, RelatedDremioPhysicalDataset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class DremioSource(Asset): + """ + Instance of a Dremio Source in Atlan. Represents external data connections that provide access to data stored in various systems like cloud storage, databases, or file systems. + """ + + DREMIO_SOURCE_TYPE: ClassVar[Any] = None + DREMIO_SOURCE_CONNECTION_CONFIGS: ClassVar[Any] = None + DREMIO_SOURCE_ACCELERATION_SETTINGS: ClassVar[Any] = None + DREMIO_SOURCE_METADATA_POLICIES: ClassVar[Any] = None + DREMIO_SOURCE_HEALTH_STATUS: ClassVar[Any] = None + DREMIO_SOURCE_HEALTH_STATUS_MESSAGE: ClassVar[Any] = None + DREMIO_ID: ClassVar[Any] = None + DREMIO_SPACE_QUALIFIED_NAME: ClassVar[Any] = None + DREMIO_SPACE_NAME: ClassVar[Any] = None + DREMIO_SOURCE_QUALIFIED_NAME: ClassVar[Any] = None + DREMIO_SOURCE_NAME: ClassVar[Any] = None + DREMIO_PARENT_FOLDER_QUALIFIED_NAME: ClassVar[Any] = None + DREMIO_FOLDER_HIERARCHY: ClassVar[Any] = None + DREMIO_LABELS: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + DREMIO_FOLDERS: ClassVar[Any] = None + DREMIO_PHYSICAL_DATASETS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "DremioSource" + + dremio_source_type: Union[str, None, UnsetType] = UNSET + """Type of external source.""" + + dremio_source_connection_configs: Union[Dict[str, str], None, UnsetType] = UNSET + """Configuration parameters for connecting to the external source.""" + + dremio_source_acceleration_settings: Union[Dict[str, str], None, UnsetType] = UNSET + """Default acceleration settings for datasets in this source.""" + + dremio_source_metadata_policies: Union[Dict[str, str], None, UnsetType] = UNSET + """Metadata refresh and caching policies.""" + + dremio_source_health_status: Union[str, None, UnsetType] = UNSET + """Current health status of the source connection.""" + + dremio_source_health_status_message: Union[str, None, UnsetType] = UNSET + """Current health status message of the source connection.""" + + dremio_id: Union[str, None, UnsetType] = UNSET + """Source ID of this asset in Dremio.""" + + dremio_space_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique qualified name of the Dremio Space containing this asset.""" + + dremio_space_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Dremio Space containing this asset.""" + + dremio_source_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique qualified name of the Dremio Source containing this asset.""" + + dremio_source_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Dremio Source containing this asset.""" + + dremio_parent_folder_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique qualified name of the immediate parent folder containing this asset.""" + + dremio_folder_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Ordered array of folder assets with qualified name and name representing the complete folder hierarchy path for this asset, from immediate parent to root folder.""" + + dremio_labels: Union[List[str], None, UnsetType] = UNSET + """Dremio Labels associated with this asset.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + dremio_folders: Union[List[RelatedDremioFolder], None, UnsetType] = UNSET + """Folders directly contained within the Dremio Source.""" + + dremio_physical_datasets: Union[ + List[RelatedDremioPhysicalDataset], None, UnsetType + ] = UNSET + """Physical datasets (tables) directly contained within the Dremio Source.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "DremioSource" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _dremio_source_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> DremioSource: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + DremioSource instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _dremio_source_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DremioSourceAttributes(AssetAttributes): + """DremioSource-specific attributes for nested API format.""" + + dremio_source_type: Union[str, None, UnsetType] = UNSET + """Type of external source.""" + + dremio_source_connection_configs: Union[Dict[str, str], None, UnsetType] = UNSET + """Configuration parameters for connecting to the external source.""" + + dremio_source_acceleration_settings: Union[Dict[str, str], None, UnsetType] = UNSET + """Default acceleration settings for datasets in this source.""" + + dremio_source_metadata_policies: Union[Dict[str, str], None, UnsetType] = UNSET + """Metadata refresh and caching policies.""" + + dremio_source_health_status: Union[str, None, UnsetType] = UNSET + """Current health status of the source connection.""" + + dremio_source_health_status_message: Union[str, None, UnsetType] = UNSET + """Current health status message of the source connection.""" + + dremio_id: Union[str, None, UnsetType] = UNSET + """Source ID of this asset in Dremio.""" + + dremio_space_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique qualified name of the Dremio Space containing this asset.""" + + dremio_space_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Dremio Space containing this asset.""" + + dremio_source_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique qualified name of the Dremio Source containing this asset.""" + + dremio_source_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Dremio Source containing this asset.""" + + dremio_parent_folder_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique qualified name of the immediate parent folder containing this asset.""" + + dremio_folder_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Ordered array of folder assets with qualified name and name representing the complete folder hierarchy path for this asset, from immediate parent to root folder.""" + + dremio_labels: Union[List[str], None, UnsetType] = UNSET + """Dremio Labels associated with this asset.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + +class DremioSourceRelationshipAttributes(AssetRelationshipAttributes): + """DremioSource-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + dremio_folders: Union[List[RelatedDremioFolder], None, UnsetType] = UNSET + """Folders directly contained within the Dremio Source.""" + + dremio_physical_datasets: Union[ + List[RelatedDremioPhysicalDataset], None, UnsetType + ] = UNSET + """Physical datasets (tables) directly contained within the Dremio Source.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DremioSourceNested(AssetNested): + """DremioSource in nested API format for high-performance serialization.""" + + attributes: Union[DremioSourceAttributes, UnsetType] = UNSET + relationship_attributes: Union[DremioSourceRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + DremioSourceRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + DremioSourceRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DREMIO_SOURCE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "dremio_folders", + "dremio_physical_datasets", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_dremio_source_attrs( + attrs: DremioSourceAttributes, obj: DremioSource +) -> None: + """Populate DremioSource-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.dremio_source_type = obj.dremio_source_type + attrs.dremio_source_connection_configs = obj.dremio_source_connection_configs + attrs.dremio_source_acceleration_settings = obj.dremio_source_acceleration_settings + attrs.dremio_source_metadata_policies = obj.dremio_source_metadata_policies + attrs.dremio_source_health_status = obj.dremio_source_health_status + attrs.dremio_source_health_status_message = obj.dremio_source_health_status_message + attrs.dremio_id = obj.dremio_id + attrs.dremio_space_qualified_name = obj.dremio_space_qualified_name + attrs.dremio_space_name = obj.dremio_space_name + attrs.dremio_source_qualified_name = obj.dremio_source_qualified_name + attrs.dremio_source_name = obj.dremio_source_name + attrs.dremio_parent_folder_qualified_name = obj.dremio_parent_folder_qualified_name + attrs.dremio_folder_hierarchy = obj.dremio_folder_hierarchy + attrs.dremio_labels = obj.dremio_labels + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + + +def _extract_dremio_source_attrs(attrs: DremioSourceAttributes) -> dict: + """Extract all DremioSource attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["dremio_source_type"] = attrs.dremio_source_type + result["dremio_source_connection_configs"] = attrs.dremio_source_connection_configs + result["dremio_source_acceleration_settings"] = ( + attrs.dremio_source_acceleration_settings + ) + result["dremio_source_metadata_policies"] = attrs.dremio_source_metadata_policies + result["dremio_source_health_status"] = attrs.dremio_source_health_status + result["dremio_source_health_status_message"] = ( + attrs.dremio_source_health_status_message + ) + result["dremio_id"] = attrs.dremio_id + result["dremio_space_qualified_name"] = attrs.dremio_space_qualified_name + result["dremio_space_name"] = attrs.dremio_space_name + result["dremio_source_qualified_name"] = attrs.dremio_source_qualified_name + result["dremio_source_name"] = attrs.dremio_source_name + result["dremio_parent_folder_qualified_name"] = ( + attrs.dremio_parent_folder_qualified_name + ) + result["dremio_folder_hierarchy"] = attrs.dremio_folder_hierarchy + result["dremio_labels"] = attrs.dremio_labels + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _dremio_source_to_nested(dremio_source: DremioSource) -> DremioSourceNested: + """Convert flat DremioSource to nested format.""" + attrs = DremioSourceAttributes() + _populate_dremio_source_attrs(attrs, dremio_source) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + dremio_source, _DREMIO_SOURCE_REL_FIELDS, DremioSourceRelationshipAttributes + ) + return DremioSourceNested( + guid=dremio_source.guid, + type_name=dremio_source.type_name, + status=dremio_source.status, + version=dremio_source.version, + create_time=dremio_source.create_time, + update_time=dremio_source.update_time, + created_by=dremio_source.created_by, + updated_by=dremio_source.updated_by, + classifications=dremio_source.classifications, + classification_names=dremio_source.classification_names, + meanings=dremio_source.meanings, + labels=dremio_source.labels, + business_attributes=dremio_source.business_attributes, + custom_attributes=dremio_source.custom_attributes, + pending_tasks=dremio_source.pending_tasks, + proxy=dremio_source.proxy, + is_incomplete=dremio_source.is_incomplete, + provenance_type=dremio_source.provenance_type, + home_id=dremio_source.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _dremio_source_from_nested(nested: DremioSourceNested) -> DremioSource: + """Convert nested format to flat DremioSource.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else DremioSourceAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DREMIO_SOURCE_REL_FIELDS, + DremioSourceRelationshipAttributes, + ) + return DremioSource( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_dremio_source_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _dremio_source_to_nested_bytes(dremio_source: DremioSource, serde: Serde) -> bytes: + """Convert flat DremioSource to nested JSON bytes.""" + return serde.encode(_dremio_source_to_nested(dremio_source)) + + +def _dremio_source_from_nested_bytes(data: bytes, serde: Serde) -> DremioSource: + """Convert nested JSON bytes to flat DremioSource.""" + nested = serde.decode(data, DremioSourceNested) + return _dremio_source_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, +) + +DremioSource.DREMIO_SOURCE_TYPE = KeywordField("dremioSourceType", "dremioSourceType") +DremioSource.DREMIO_SOURCE_CONNECTION_CONFIGS = KeywordField( + "dremioSourceConnectionConfigs", "dremioSourceConnectionConfigs" +) +DremioSource.DREMIO_SOURCE_ACCELERATION_SETTINGS = KeywordField( + "dremioSourceAccelerationSettings", "dremioSourceAccelerationSettings" +) +DremioSource.DREMIO_SOURCE_METADATA_POLICIES = KeywordField( + "dremioSourceMetadataPolicies", "dremioSourceMetadataPolicies" +) +DremioSource.DREMIO_SOURCE_HEALTH_STATUS = KeywordField( + "dremioSourceHealthStatus", "dremioSourceHealthStatus" +) +DremioSource.DREMIO_SOURCE_HEALTH_STATUS_MESSAGE = KeywordField( + "dremioSourceHealthStatusMessage", "dremioSourceHealthStatusMessage" +) +DremioSource.DREMIO_ID = KeywordField("dremioId", "dremioId") +DremioSource.DREMIO_SPACE_QUALIFIED_NAME = KeywordField( + "dremioSpaceQualifiedName", "dremioSpaceQualifiedName" +) +DremioSource.DREMIO_SPACE_NAME = KeywordField("dremioSpaceName", "dremioSpaceName") +DremioSource.DREMIO_SOURCE_QUALIFIED_NAME = KeywordField( + "dremioSourceQualifiedName", "dremioSourceQualifiedName" +) +DremioSource.DREMIO_SOURCE_NAME = KeywordField("dremioSourceName", "dremioSourceName") +DremioSource.DREMIO_PARENT_FOLDER_QUALIFIED_NAME = KeywordField( + "dremioParentFolderQualifiedName", "dremioParentFolderQualifiedName" +) +DremioSource.DREMIO_FOLDER_HIERARCHY = KeywordField( + "dremioFolderHierarchy", "dremioFolderHierarchy" +) +DremioSource.DREMIO_LABELS = KeywordField("dremioLabels", "dremioLabels") +DremioSource.QUERY_COUNT = NumericField("queryCount", "queryCount") +DremioSource.QUERY_USER_COUNT = NumericField("queryUserCount", "queryUserCount") +DremioSource.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +DremioSource.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +DremioSource.DATABASE_NAME = KeywordField("databaseName", "databaseName") +DremioSource.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +DremioSource.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +DremioSource.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +DremioSource.TABLE_NAME = KeywordField("tableName", "tableName") +DremioSource.TABLE_QUALIFIED_NAME = KeywordField( + "tableQualifiedName", "tableQualifiedName" +) +DremioSource.VIEW_NAME = KeywordField("viewName", "viewName") +DremioSource.VIEW_QUALIFIED_NAME = KeywordField( + "viewQualifiedName", "viewQualifiedName" +) +DremioSource.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +DremioSource.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +DremioSource.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +DremioSource.LAST_PROFILED_AT = NumericField("lastProfiledAt", "lastProfiledAt") +DremioSource.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +DremioSource.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +DremioSource.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +DremioSource.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +DremioSource.ANOMALO_CHECKS = RelationField("anomaloChecks") +DremioSource.APPLICATION = RelationField("application") +DremioSource.APPLICATION_FIELD = RelationField("applicationField") +DremioSource.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +DremioSource.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +DremioSource.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +DremioSource.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +DremioSource.METRICS = RelationField("metrics") +DremioSource.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +DremioSource.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +DremioSource.DBT_MODELS = RelationField("dbtModels") +DremioSource.SQL_DBT_MODELS = RelationField("sqlDbtModels") +DremioSource.DBT_TESTS = RelationField("dbtTests") +DremioSource.DBT_SOURCES = RelationField("dbtSources") +DremioSource.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +DremioSource.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +DremioSource.DREMIO_FOLDERS = RelationField("dremioFolders") +DremioSource.DREMIO_PHYSICAL_DATASETS = RelationField("dremioPhysicalDatasets") +DremioSource.MEANINGS = RelationField("meanings") +DremioSource.MC_MONITORS = RelationField("mcMonitors") +DremioSource.MC_INCIDENTS = RelationField("mcIncidents") +DremioSource.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +DremioSource.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +DremioSource.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +DremioSource.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +DremioSource.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +DremioSource.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +DremioSource.FILES = RelationField("files") +DremioSource.LINKS = RelationField("links") +DremioSource.README = RelationField("readme") +DremioSource.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +DremioSource.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +DremioSource.SODA_CHECKS = RelationField("sodaChecks") +DremioSource.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +DremioSource.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/dremio_space.py b/pyatlan_v9/model/assets/dremio_space.py new file mode 100644 index 000000000..327265979 --- /dev/null +++ b/pyatlan_v9/model/assets/dremio_space.py @@ -0,0 +1,926 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DremioSpace asset model with flattened inheritance. + +This module provides: +- DremioSpace: Flat asset class (easy to use) +- DremioSpaceAttributes: Nested attributes struct (extends AssetAttributes) +- DremioSpaceNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .snowflake_related import RelatedSnowflakeSemanticLogicalTable +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .dremio_related import RelatedDremioFolder, RelatedDremioVirtualDataset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class DremioSpace(Asset): + """ + Instance of a Dremio Space in Atlan. Represents a logical workspace in Dremio where users can create and organize virtual datasets. + """ + + DREMIO_ID: ClassVar[Any] = None + DREMIO_SPACE_QUALIFIED_NAME: ClassVar[Any] = None + DREMIO_SPACE_NAME: ClassVar[Any] = None + DREMIO_SOURCE_QUALIFIED_NAME: ClassVar[Any] = None + DREMIO_SOURCE_NAME: ClassVar[Any] = None + DREMIO_PARENT_FOLDER_QUALIFIED_NAME: ClassVar[Any] = None + DREMIO_FOLDER_HIERARCHY: ClassVar[Any] = None + DREMIO_LABELS: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + DREMIO_FOLDERS: ClassVar[Any] = None + DREMIO_VIRTUAL_DATASETS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "DremioSpace" + + dremio_id: Union[str, None, UnsetType] = UNSET + """Source ID of this asset in Dremio.""" + + dremio_space_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique qualified name of the Dremio Space containing this asset.""" + + dremio_space_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Dremio Space containing this asset.""" + + dremio_source_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique qualified name of the Dremio Source containing this asset.""" + + dremio_source_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Dremio Source containing this asset.""" + + dremio_parent_folder_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique qualified name of the immediate parent folder containing this asset.""" + + dremio_folder_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Ordered array of folder assets with qualified name and name representing the complete folder hierarchy path for this asset, from immediate parent to root folder.""" + + dremio_labels: Union[List[str], None, UnsetType] = UNSET + """Dremio Labels associated with this asset.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + dremio_folders: Union[List[RelatedDremioFolder], None, UnsetType] = UNSET + """Folders directly contained within the Dremio Space.""" + + dremio_virtual_datasets: Union[ + List[RelatedDremioVirtualDataset], None, UnsetType + ] = UNSET + """Virtual datasets (views) directly contained within the Dremio Space.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "DremioSpace" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _dremio_space_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> DremioSpace: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + DremioSpace instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _dremio_space_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DremioSpaceAttributes(AssetAttributes): + """DremioSpace-specific attributes for nested API format.""" + + dremio_id: Union[str, None, UnsetType] = UNSET + """Source ID of this asset in Dremio.""" + + dremio_space_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique qualified name of the Dremio Space containing this asset.""" + + dremio_space_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Dremio Space containing this asset.""" + + dremio_source_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique qualified name of the Dremio Source containing this asset.""" + + dremio_source_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Dremio Source containing this asset.""" + + dremio_parent_folder_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique qualified name of the immediate parent folder containing this asset.""" + + dremio_folder_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Ordered array of folder assets with qualified name and name representing the complete folder hierarchy path for this asset, from immediate parent to root folder.""" + + dremio_labels: Union[List[str], None, UnsetType] = UNSET + """Dremio Labels associated with this asset.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + +class DremioSpaceRelationshipAttributes(AssetRelationshipAttributes): + """DremioSpace-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + dremio_folders: Union[List[RelatedDremioFolder], None, UnsetType] = UNSET + """Folders directly contained within the Dremio Space.""" + + dremio_virtual_datasets: Union[ + List[RelatedDremioVirtualDataset], None, UnsetType + ] = UNSET + """Virtual datasets (views) directly contained within the Dremio Space.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DremioSpaceNested(AssetNested): + """DremioSpace in nested API format for high-performance serialization.""" + + attributes: Union[DremioSpaceAttributes, UnsetType] = UNSET + relationship_attributes: Union[DremioSpaceRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + DremioSpaceRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + DremioSpaceRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DREMIO_SPACE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "dremio_folders", + "dremio_virtual_datasets", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_dremio_space_attrs( + attrs: DremioSpaceAttributes, obj: DremioSpace +) -> None: + """Populate DremioSpace-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.dremio_id = obj.dremio_id + attrs.dremio_space_qualified_name = obj.dremio_space_qualified_name + attrs.dremio_space_name = obj.dremio_space_name + attrs.dremio_source_qualified_name = obj.dremio_source_qualified_name + attrs.dremio_source_name = obj.dremio_source_name + attrs.dremio_parent_folder_qualified_name = obj.dremio_parent_folder_qualified_name + attrs.dremio_folder_hierarchy = obj.dremio_folder_hierarchy + attrs.dremio_labels = obj.dremio_labels + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + + +def _extract_dremio_space_attrs(attrs: DremioSpaceAttributes) -> dict: + """Extract all DremioSpace attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["dremio_id"] = attrs.dremio_id + result["dremio_space_qualified_name"] = attrs.dremio_space_qualified_name + result["dremio_space_name"] = attrs.dremio_space_name + result["dremio_source_qualified_name"] = attrs.dremio_source_qualified_name + result["dremio_source_name"] = attrs.dremio_source_name + result["dremio_parent_folder_qualified_name"] = ( + attrs.dremio_parent_folder_qualified_name + ) + result["dremio_folder_hierarchy"] = attrs.dremio_folder_hierarchy + result["dremio_labels"] = attrs.dremio_labels + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _dremio_space_to_nested(dremio_space: DremioSpace) -> DremioSpaceNested: + """Convert flat DremioSpace to nested format.""" + attrs = DremioSpaceAttributes() + _populate_dremio_space_attrs(attrs, dremio_space) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + dremio_space, _DREMIO_SPACE_REL_FIELDS, DremioSpaceRelationshipAttributes + ) + return DremioSpaceNested( + guid=dremio_space.guid, + type_name=dremio_space.type_name, + status=dremio_space.status, + version=dremio_space.version, + create_time=dremio_space.create_time, + update_time=dremio_space.update_time, + created_by=dremio_space.created_by, + updated_by=dremio_space.updated_by, + classifications=dremio_space.classifications, + classification_names=dremio_space.classification_names, + meanings=dremio_space.meanings, + labels=dremio_space.labels, + business_attributes=dremio_space.business_attributes, + custom_attributes=dremio_space.custom_attributes, + pending_tasks=dremio_space.pending_tasks, + proxy=dremio_space.proxy, + is_incomplete=dremio_space.is_incomplete, + provenance_type=dremio_space.provenance_type, + home_id=dremio_space.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _dremio_space_from_nested(nested: DremioSpaceNested) -> DremioSpace: + """Convert nested format to flat DremioSpace.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else DremioSpaceAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DREMIO_SPACE_REL_FIELDS, + DremioSpaceRelationshipAttributes, + ) + return DremioSpace( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_dremio_space_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _dremio_space_to_nested_bytes(dremio_space: DremioSpace, serde: Serde) -> bytes: + """Convert flat DremioSpace to nested JSON bytes.""" + return serde.encode(_dremio_space_to_nested(dremio_space)) + + +def _dremio_space_from_nested_bytes(data: bytes, serde: Serde) -> DremioSpace: + """Convert nested JSON bytes to flat DremioSpace.""" + nested = serde.decode(data, DremioSpaceNested) + return _dremio_space_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, +) + +DremioSpace.DREMIO_ID = KeywordField("dremioId", "dremioId") +DremioSpace.DREMIO_SPACE_QUALIFIED_NAME = KeywordField( + "dremioSpaceQualifiedName", "dremioSpaceQualifiedName" +) +DremioSpace.DREMIO_SPACE_NAME = KeywordField("dremioSpaceName", "dremioSpaceName") +DremioSpace.DREMIO_SOURCE_QUALIFIED_NAME = KeywordField( + "dremioSourceQualifiedName", "dremioSourceQualifiedName" +) +DremioSpace.DREMIO_SOURCE_NAME = KeywordField("dremioSourceName", "dremioSourceName") +DremioSpace.DREMIO_PARENT_FOLDER_QUALIFIED_NAME = KeywordField( + "dremioParentFolderQualifiedName", "dremioParentFolderQualifiedName" +) +DremioSpace.DREMIO_FOLDER_HIERARCHY = KeywordField( + "dremioFolderHierarchy", "dremioFolderHierarchy" +) +DremioSpace.DREMIO_LABELS = KeywordField("dremioLabels", "dremioLabels") +DremioSpace.QUERY_COUNT = NumericField("queryCount", "queryCount") +DremioSpace.QUERY_USER_COUNT = NumericField("queryUserCount", "queryUserCount") +DremioSpace.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +DremioSpace.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +DremioSpace.DATABASE_NAME = KeywordField("databaseName", "databaseName") +DremioSpace.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +DremioSpace.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +DremioSpace.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +DremioSpace.TABLE_NAME = KeywordField("tableName", "tableName") +DremioSpace.TABLE_QUALIFIED_NAME = KeywordField( + "tableQualifiedName", "tableQualifiedName" +) +DremioSpace.VIEW_NAME = KeywordField("viewName", "viewName") +DremioSpace.VIEW_QUALIFIED_NAME = KeywordField("viewQualifiedName", "viewQualifiedName") +DremioSpace.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +DremioSpace.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +DremioSpace.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +DremioSpace.LAST_PROFILED_AT = NumericField("lastProfiledAt", "lastProfiledAt") +DremioSpace.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +DremioSpace.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +DremioSpace.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +DremioSpace.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +DremioSpace.ANOMALO_CHECKS = RelationField("anomaloChecks") +DremioSpace.APPLICATION = RelationField("application") +DremioSpace.APPLICATION_FIELD = RelationField("applicationField") +DremioSpace.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +DremioSpace.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +DremioSpace.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +DremioSpace.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +DremioSpace.METRICS = RelationField("metrics") +DremioSpace.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +DremioSpace.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +DremioSpace.DBT_MODELS = RelationField("dbtModels") +DremioSpace.SQL_DBT_MODELS = RelationField("sqlDbtModels") +DremioSpace.DBT_TESTS = RelationField("dbtTests") +DremioSpace.DBT_SOURCES = RelationField("dbtSources") +DremioSpace.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +DremioSpace.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +DremioSpace.DREMIO_FOLDERS = RelationField("dremioFolders") +DremioSpace.DREMIO_VIRTUAL_DATASETS = RelationField("dremioVirtualDatasets") +DremioSpace.MEANINGS = RelationField("meanings") +DremioSpace.MC_MONITORS = RelationField("mcMonitors") +DremioSpace.MC_INCIDENTS = RelationField("mcIncidents") +DremioSpace.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +DremioSpace.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +DremioSpace.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +DremioSpace.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +DremioSpace.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +DremioSpace.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +DremioSpace.FILES = RelationField("files") +DremioSpace.LINKS = RelationField("links") +DremioSpace.README = RelationField("readme") +DremioSpace.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +DremioSpace.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +DremioSpace.SODA_CHECKS = RelationField("sodaChecks") +DremioSpace.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +DremioSpace.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/dremio_virtual_dataset.py b/pyatlan_v9/model/assets/dremio_virtual_dataset.py new file mode 100644 index 000000000..46f573b80 --- /dev/null +++ b/pyatlan_v9/model/assets/dremio_virtual_dataset.py @@ -0,0 +1,1071 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DremioVirtualDataset asset model with flattened inheritance. + +This module provides: +- DremioVirtualDataset: Flat asset class (easy to use) +- DremioVirtualDatasetAttributes: Nested attributes struct (extends AssetAttributes) +- DremioVirtualDatasetNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .snowflake_related import RelatedSnowflakeSemanticLogicalTable +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from .sql_related import RelatedColumn, RelatedQuery, RelatedSchema +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .dremio_related import RelatedDremioFolder, RelatedDremioSpace + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class DremioVirtualDataset(Asset): + """ + Instance of a Dremio Virtual Dataset (View) in Atlan. Represents SQL-defined views that transform and combine data from physical datasets or other virtual datasets. + """ + + DREMIO_ID: ClassVar[Any] = None + DREMIO_SPACE_QUALIFIED_NAME: ClassVar[Any] = None + DREMIO_SPACE_NAME: ClassVar[Any] = None + DREMIO_SOURCE_QUALIFIED_NAME: ClassVar[Any] = None + DREMIO_SOURCE_NAME: ClassVar[Any] = None + DREMIO_PARENT_FOLDER_QUALIFIED_NAME: ClassVar[Any] = None + DREMIO_FOLDER_HIERARCHY: ClassVar[Any] = None + DREMIO_LABELS: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + COLUMN_COUNT: ClassVar[Any] = None + ROW_COUNT: ClassVar[Any] = None + SIZE_BYTES: ClassVar[Any] = None + IS_QUERY_PREVIEW: ClassVar[Any] = None + QUERY_PREVIEW_CONFIG: ClassVar[Any] = None + ALIAS: ClassVar[Any] = None + IS_TEMPORARY: ClassVar[Any] = None + DEFINITION: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + DREMIO_SPACE: ClassVar[Any] = None + DREMIO_FOLDER: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + COLUMNS: ClassVar[Any] = None + QUERIES: ClassVar[Any] = None + ATLAN_SCHEMA: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "DremioVirtualDataset" + + dremio_id: Union[str, None, UnsetType] = UNSET + """Source ID of this asset in Dremio.""" + + dremio_space_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique qualified name of the Dremio Space containing this asset.""" + + dremio_space_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Dremio Space containing this asset.""" + + dremio_source_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique qualified name of the Dremio Source containing this asset.""" + + dremio_source_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Dremio Source containing this asset.""" + + dremio_parent_folder_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique qualified name of the immediate parent folder containing this asset.""" + + dremio_folder_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Ordered array of folder assets with qualified name and name representing the complete folder hierarchy path for this asset, from immediate parent to root folder.""" + + dremio_labels: Union[List[str], None, UnsetType] = UNSET + """Dremio Labels associated with this asset.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this view.""" + + row_count: Union[int, None, UnsetType] = UNSET + """Number of rows in this view.""" + + size_bytes: Union[int, None, UnsetType] = UNSET + """Size of this view, in bytes.""" + + is_query_preview: Union[bool, None, UnsetType] = UNSET + """Whether preview queries are allowed on this view (true) or not (false).""" + + query_preview_config: Union[Dict[str, str], None, UnsetType] = UNSET + """Configuration for preview queries on this view.""" + + alias: Union[str, None, UnsetType] = UNSET + """Alias for this view.""" + + is_temporary: Union[bool, None, UnsetType] = UNSET + """Whether this view is temporary (true) or not (false).""" + + definition: Union[str, None, UnsetType] = UNSET + """SQL definition of this view.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + dremio_space: Union[RelatedDremioSpace, None, UnsetType] = UNSET + """Dremio Space that contains the virtual datasets (views).""" + + dremio_folder: Union[RelatedDremioFolder, None, UnsetType] = UNSET + """Dremio Folder that contains the virtual datasets (views).""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Columns that exist within this view.""" + + queries: Union[List[RelatedQuery], None, UnsetType] = UNSET + """Queries that access this view.""" + + atlan_schema: Union[RelatedSchema, None, UnsetType] = UNSET + """Schema in which this view exists.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "DremioVirtualDataset" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _dremio_virtual_dataset_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> DremioVirtualDataset: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + DremioVirtualDataset instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _dremio_virtual_dataset_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DremioVirtualDatasetAttributes(AssetAttributes): + """DremioVirtualDataset-specific attributes for nested API format.""" + + dremio_id: Union[str, None, UnsetType] = UNSET + """Source ID of this asset in Dremio.""" + + dremio_space_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique qualified name of the Dremio Space containing this asset.""" + + dremio_space_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Dremio Space containing this asset.""" + + dremio_source_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique qualified name of the Dremio Source containing this asset.""" + + dremio_source_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Dremio Source containing this asset.""" + + dremio_parent_folder_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique qualified name of the immediate parent folder containing this asset.""" + + dremio_folder_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Ordered array of folder assets with qualified name and name representing the complete folder hierarchy path for this asset, from immediate parent to root folder.""" + + dremio_labels: Union[List[str], None, UnsetType] = UNSET + """Dremio Labels associated with this asset.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this view.""" + + row_count: Union[int, None, UnsetType] = UNSET + """Number of rows in this view.""" + + size_bytes: Union[int, None, UnsetType] = UNSET + """Size of this view, in bytes.""" + + is_query_preview: Union[bool, None, UnsetType] = UNSET + """Whether preview queries are allowed on this view (true) or not (false).""" + + query_preview_config: Union[Dict[str, str], None, UnsetType] = UNSET + """Configuration for preview queries on this view.""" + + alias: Union[str, None, UnsetType] = UNSET + """Alias for this view.""" + + is_temporary: Union[bool, None, UnsetType] = UNSET + """Whether this view is temporary (true) or not (false).""" + + definition: Union[str, None, UnsetType] = UNSET + """SQL definition of this view.""" + + +class DremioVirtualDatasetRelationshipAttributes(AssetRelationshipAttributes): + """DremioVirtualDataset-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + dremio_space: Union[RelatedDremioSpace, None, UnsetType] = UNSET + """Dremio Space that contains the virtual datasets (views).""" + + dremio_folder: Union[RelatedDremioFolder, None, UnsetType] = UNSET + """Dremio Folder that contains the virtual datasets (views).""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Columns that exist within this view.""" + + queries: Union[List[RelatedQuery], None, UnsetType] = UNSET + """Queries that access this view.""" + + atlan_schema: Union[RelatedSchema, None, UnsetType] = UNSET + """Schema in which this view exists.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DremioVirtualDatasetNested(AssetNested): + """DremioVirtualDataset in nested API format for high-performance serialization.""" + + attributes: Union[DremioVirtualDatasetAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + DremioVirtualDatasetRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + DremioVirtualDatasetRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + DremioVirtualDatasetRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DREMIO_VIRTUAL_DATASET_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "dremio_space", + "dremio_folder", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "columns", + "queries", + "atlan_schema", + "schema_registry_subjects", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_dremio_virtual_dataset_attrs( + attrs: DremioVirtualDatasetAttributes, obj: DremioVirtualDataset +) -> None: + """Populate DremioVirtualDataset-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.dremio_id = obj.dremio_id + attrs.dremio_space_qualified_name = obj.dremio_space_qualified_name + attrs.dremio_space_name = obj.dremio_space_name + attrs.dremio_source_qualified_name = obj.dremio_source_qualified_name + attrs.dremio_source_name = obj.dremio_source_name + attrs.dremio_parent_folder_qualified_name = obj.dremio_parent_folder_qualified_name + attrs.dremio_folder_hierarchy = obj.dremio_folder_hierarchy + attrs.dremio_labels = obj.dremio_labels + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + attrs.column_count = obj.column_count + attrs.row_count = obj.row_count + attrs.size_bytes = obj.size_bytes + attrs.is_query_preview = obj.is_query_preview + attrs.query_preview_config = obj.query_preview_config + attrs.alias = obj.alias + attrs.is_temporary = obj.is_temporary + attrs.definition = obj.definition + + +def _extract_dremio_virtual_dataset_attrs( + attrs: DremioVirtualDatasetAttributes, +) -> dict: + """Extract all DremioVirtualDataset attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["dremio_id"] = attrs.dremio_id + result["dremio_space_qualified_name"] = attrs.dremio_space_qualified_name + result["dremio_space_name"] = attrs.dremio_space_name + result["dremio_source_qualified_name"] = attrs.dremio_source_qualified_name + result["dremio_source_name"] = attrs.dremio_source_name + result["dremio_parent_folder_qualified_name"] = ( + attrs.dremio_parent_folder_qualified_name + ) + result["dremio_folder_hierarchy"] = attrs.dremio_folder_hierarchy + result["dremio_labels"] = attrs.dremio_labels + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + result["column_count"] = attrs.column_count + result["row_count"] = attrs.row_count + result["size_bytes"] = attrs.size_bytes + result["is_query_preview"] = attrs.is_query_preview + result["query_preview_config"] = attrs.query_preview_config + result["alias"] = attrs.alias + result["is_temporary"] = attrs.is_temporary + result["definition"] = attrs.definition + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _dremio_virtual_dataset_to_nested( + dremio_virtual_dataset: DremioVirtualDataset, +) -> DremioVirtualDatasetNested: + """Convert flat DremioVirtualDataset to nested format.""" + attrs = DremioVirtualDatasetAttributes() + _populate_dremio_virtual_dataset_attrs(attrs, dremio_virtual_dataset) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + dremio_virtual_dataset, + _DREMIO_VIRTUAL_DATASET_REL_FIELDS, + DremioVirtualDatasetRelationshipAttributes, + ) + return DremioVirtualDatasetNested( + guid=dremio_virtual_dataset.guid, + type_name=dremio_virtual_dataset.type_name, + status=dremio_virtual_dataset.status, + version=dremio_virtual_dataset.version, + create_time=dremio_virtual_dataset.create_time, + update_time=dremio_virtual_dataset.update_time, + created_by=dremio_virtual_dataset.created_by, + updated_by=dremio_virtual_dataset.updated_by, + classifications=dremio_virtual_dataset.classifications, + classification_names=dremio_virtual_dataset.classification_names, + meanings=dremio_virtual_dataset.meanings, + labels=dremio_virtual_dataset.labels, + business_attributes=dremio_virtual_dataset.business_attributes, + custom_attributes=dremio_virtual_dataset.custom_attributes, + pending_tasks=dremio_virtual_dataset.pending_tasks, + proxy=dremio_virtual_dataset.proxy, + is_incomplete=dremio_virtual_dataset.is_incomplete, + provenance_type=dremio_virtual_dataset.provenance_type, + home_id=dremio_virtual_dataset.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _dremio_virtual_dataset_from_nested( + nested: DremioVirtualDatasetNested, +) -> DremioVirtualDataset: + """Convert nested format to flat DremioVirtualDataset.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else DremioVirtualDatasetAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DREMIO_VIRTUAL_DATASET_REL_FIELDS, + DremioVirtualDatasetRelationshipAttributes, + ) + return DremioVirtualDataset( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_dremio_virtual_dataset_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _dremio_virtual_dataset_to_nested_bytes( + dremio_virtual_dataset: DremioVirtualDataset, serde: Serde +) -> bytes: + """Convert flat DremioVirtualDataset to nested JSON bytes.""" + return serde.encode(_dremio_virtual_dataset_to_nested(dremio_virtual_dataset)) + + +def _dremio_virtual_dataset_from_nested_bytes( + data: bytes, serde: Serde +) -> DremioVirtualDataset: + """Convert nested JSON bytes to flat DremioVirtualDataset.""" + nested = serde.decode(data, DremioVirtualDatasetNested) + return _dremio_virtual_dataset_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, +) + +DremioVirtualDataset.DREMIO_ID = KeywordField("dremioId", "dremioId") +DremioVirtualDataset.DREMIO_SPACE_QUALIFIED_NAME = KeywordField( + "dremioSpaceQualifiedName", "dremioSpaceQualifiedName" +) +DremioVirtualDataset.DREMIO_SPACE_NAME = KeywordField( + "dremioSpaceName", "dremioSpaceName" +) +DremioVirtualDataset.DREMIO_SOURCE_QUALIFIED_NAME = KeywordField( + "dremioSourceQualifiedName", "dremioSourceQualifiedName" +) +DremioVirtualDataset.DREMIO_SOURCE_NAME = KeywordField( + "dremioSourceName", "dremioSourceName" +) +DremioVirtualDataset.DREMIO_PARENT_FOLDER_QUALIFIED_NAME = KeywordField( + "dremioParentFolderQualifiedName", "dremioParentFolderQualifiedName" +) +DremioVirtualDataset.DREMIO_FOLDER_HIERARCHY = KeywordField( + "dremioFolderHierarchy", "dremioFolderHierarchy" +) +DremioVirtualDataset.DREMIO_LABELS = KeywordField("dremioLabels", "dremioLabels") +DremioVirtualDataset.QUERY_COUNT = NumericField("queryCount", "queryCount") +DremioVirtualDataset.QUERY_USER_COUNT = NumericField("queryUserCount", "queryUserCount") +DremioVirtualDataset.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +DremioVirtualDataset.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +DremioVirtualDataset.DATABASE_NAME = KeywordField("databaseName", "databaseName") +DremioVirtualDataset.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +DremioVirtualDataset.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +DremioVirtualDataset.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +DremioVirtualDataset.TABLE_NAME = KeywordField("tableName", "tableName") +DremioVirtualDataset.TABLE_QUALIFIED_NAME = KeywordField( + "tableQualifiedName", "tableQualifiedName" +) +DremioVirtualDataset.VIEW_NAME = KeywordField("viewName", "viewName") +DremioVirtualDataset.VIEW_QUALIFIED_NAME = KeywordField( + "viewQualifiedName", "viewQualifiedName" +) +DremioVirtualDataset.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +DremioVirtualDataset.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +DremioVirtualDataset.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +DremioVirtualDataset.LAST_PROFILED_AT = NumericField("lastProfiledAt", "lastProfiledAt") +DremioVirtualDataset.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +DremioVirtualDataset.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +DremioVirtualDataset.COLUMN_COUNT = NumericField("columnCount", "columnCount") +DremioVirtualDataset.ROW_COUNT = NumericField("rowCount", "rowCount") +DremioVirtualDataset.SIZE_BYTES = NumericField("sizeBytes", "sizeBytes") +DremioVirtualDataset.IS_QUERY_PREVIEW = BooleanField("isQueryPreview", "isQueryPreview") +DremioVirtualDataset.QUERY_PREVIEW_CONFIG = KeywordField( + "queryPreviewConfig", "queryPreviewConfig" +) +DremioVirtualDataset.ALIAS = KeywordField("alias", "alias") +DremioVirtualDataset.IS_TEMPORARY = BooleanField("isTemporary", "isTemporary") +DremioVirtualDataset.DEFINITION = KeywordField("definition", "definition") +DremioVirtualDataset.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +DremioVirtualDataset.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +DremioVirtualDataset.ANOMALO_CHECKS = RelationField("anomaloChecks") +DremioVirtualDataset.APPLICATION = RelationField("application") +DremioVirtualDataset.APPLICATION_FIELD = RelationField("applicationField") +DremioVirtualDataset.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +DremioVirtualDataset.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +DremioVirtualDataset.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +DremioVirtualDataset.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +DremioVirtualDataset.METRICS = RelationField("metrics") +DremioVirtualDataset.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +DremioVirtualDataset.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +DremioVirtualDataset.DBT_MODELS = RelationField("dbtModels") +DremioVirtualDataset.SQL_DBT_MODELS = RelationField("sqlDbtModels") +DremioVirtualDataset.DBT_TESTS = RelationField("dbtTests") +DremioVirtualDataset.DBT_SOURCES = RelationField("dbtSources") +DremioVirtualDataset.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +DremioVirtualDataset.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +DremioVirtualDataset.DREMIO_SPACE = RelationField("dremioSpace") +DremioVirtualDataset.DREMIO_FOLDER = RelationField("dremioFolder") +DremioVirtualDataset.MEANINGS = RelationField("meanings") +DremioVirtualDataset.MC_MONITORS = RelationField("mcMonitors") +DremioVirtualDataset.MC_INCIDENTS = RelationField("mcIncidents") +DremioVirtualDataset.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +DremioVirtualDataset.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +DremioVirtualDataset.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +DremioVirtualDataset.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +DremioVirtualDataset.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +DremioVirtualDataset.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +DremioVirtualDataset.FILES = RelationField("files") +DremioVirtualDataset.LINKS = RelationField("links") +DremioVirtualDataset.README = RelationField("readme") +DremioVirtualDataset.COLUMNS = RelationField("columns") +DremioVirtualDataset.QUERIES = RelationField("queries") +DremioVirtualDataset.ATLAN_SCHEMA = RelationField("atlanSchema") +DremioVirtualDataset.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +DremioVirtualDataset.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +DremioVirtualDataset.SODA_CHECKS = RelationField("sodaChecks") +DremioVirtualDataset.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +DremioVirtualDataset.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/dynamo_db.py b/pyatlan_v9/model/assets/dynamo_db.py new file mode 100644 index 000000000..fd67e3333 --- /dev/null +++ b/pyatlan_v9/model/assets/dynamo_db.py @@ -0,0 +1,621 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DynamoDB asset model with flattened inheritance. + +This module provides: +- DynamoDB: Flat asset class (easy to use) +- DynamoDBAttributes: Nested attributes struct (extends AssetAttributes) +- DynamoDBNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class DynamoDB(Asset): + """ + Base class for DynamoDB assets in Atlan. + """ + + DYNAMO_DB_STATUS: ClassVar[Any] = None + DYNAMO_DB_PARTITION_KEY: ClassVar[Any] = None + DYNAMO_DB_SORT_KEY: ClassVar[Any] = None + DYNAMO_DB_READ_CAPACITY_UNITS: ClassVar[Any] = None + DYNAMO_DB_WRITE_CAPACITY_UNITS: ClassVar[Any] = None + NO_SQL_SCHEMA_DEFINITION: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "DynamoDB" + + dynamo_db_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBStatus" + ) + """Status of the DynamoDB asset.""" + + dynamo_db_partition_key: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBPartitionKey" + ) + """Specifies the partition key of the DynamoDB table or index.""" + + dynamo_db_sort_key: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBSortKey" + ) + """Specifies the sort key of the DynamoDB table or index.""" + + dynamo_db_read_capacity_units: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBReadCapacityUnits" + ) + """The maximum number of strongly consistent reads consumed per second before DynamoDB returns a ThrottlingException.""" + + dynamo_db_write_capacity_units: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBWriteCapacityUnits" + ) + """The maximum number of writes consumed per second before DynamoDB returns a ThrottlingException.""" + + no_sql_schema_definition: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="noSQLSchemaDefinition" + ) + """Represents attributes for describing the key schema for the table and indexes.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "DynamoDB" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _dynamo_db_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> DynamoDB: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + DynamoDB instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _dynamo_db_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DynamoDBAttributes(AssetAttributes): + """DynamoDB-specific attributes for nested API format.""" + + dynamo_db_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBStatus" + ) + """Status of the DynamoDB asset.""" + + dynamo_db_partition_key: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBPartitionKey" + ) + """Specifies the partition key of the DynamoDB table or index.""" + + dynamo_db_sort_key: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBSortKey" + ) + """Specifies the sort key of the DynamoDB table or index.""" + + dynamo_db_read_capacity_units: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBReadCapacityUnits" + ) + """The maximum number of strongly consistent reads consumed per second before DynamoDB returns a ThrottlingException.""" + + dynamo_db_write_capacity_units: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBWriteCapacityUnits" + ) + """The maximum number of writes consumed per second before DynamoDB returns a ThrottlingException.""" + + no_sql_schema_definition: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="noSQLSchemaDefinition" + ) + """Represents attributes for describing the key schema for the table and indexes.""" + + +class DynamoDBRelationshipAttributes(AssetRelationshipAttributes): + """DynamoDB-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DynamoDBNested(AssetNested): + """DynamoDB in nested API format for high-performance serialization.""" + + attributes: Union[DynamoDBAttributes, UnsetType] = UNSET + relationship_attributes: Union[DynamoDBRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[DynamoDBRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[DynamoDBRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DYNAMO_DB_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_dynamo_db_attrs(attrs: DynamoDBAttributes, obj: DynamoDB) -> None: + """Populate DynamoDB-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.dynamo_db_status = obj.dynamo_db_status + attrs.dynamo_db_partition_key = obj.dynamo_db_partition_key + attrs.dynamo_db_sort_key = obj.dynamo_db_sort_key + attrs.dynamo_db_read_capacity_units = obj.dynamo_db_read_capacity_units + attrs.dynamo_db_write_capacity_units = obj.dynamo_db_write_capacity_units + attrs.no_sql_schema_definition = obj.no_sql_schema_definition + + +def _extract_dynamo_db_attrs(attrs: DynamoDBAttributes) -> dict: + """Extract all DynamoDB attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["dynamo_db_status"] = attrs.dynamo_db_status + result["dynamo_db_partition_key"] = attrs.dynamo_db_partition_key + result["dynamo_db_sort_key"] = attrs.dynamo_db_sort_key + result["dynamo_db_read_capacity_units"] = attrs.dynamo_db_read_capacity_units + result["dynamo_db_write_capacity_units"] = attrs.dynamo_db_write_capacity_units + result["no_sql_schema_definition"] = attrs.no_sql_schema_definition + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _dynamo_db_to_nested(dynamo_db: DynamoDB) -> DynamoDBNested: + """Convert flat DynamoDB to nested format.""" + attrs = DynamoDBAttributes() + _populate_dynamo_db_attrs(attrs, dynamo_db) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + dynamo_db, _DYNAMO_DB_REL_FIELDS, DynamoDBRelationshipAttributes + ) + return DynamoDBNested( + guid=dynamo_db.guid, + type_name=dynamo_db.type_name, + status=dynamo_db.status, + version=dynamo_db.version, + create_time=dynamo_db.create_time, + update_time=dynamo_db.update_time, + created_by=dynamo_db.created_by, + updated_by=dynamo_db.updated_by, + classifications=dynamo_db.classifications, + classification_names=dynamo_db.classification_names, + meanings=dynamo_db.meanings, + labels=dynamo_db.labels, + business_attributes=dynamo_db.business_attributes, + custom_attributes=dynamo_db.custom_attributes, + pending_tasks=dynamo_db.pending_tasks, + proxy=dynamo_db.proxy, + is_incomplete=dynamo_db.is_incomplete, + provenance_type=dynamo_db.provenance_type, + home_id=dynamo_db.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _dynamo_db_from_nested(nested: DynamoDBNested) -> DynamoDB: + """Convert nested format to flat DynamoDB.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else DynamoDBAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DYNAMO_DB_REL_FIELDS, + DynamoDBRelationshipAttributes, + ) + return DynamoDB( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_dynamo_db_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _dynamo_db_to_nested_bytes(dynamo_db: DynamoDB, serde: Serde) -> bytes: + """Convert flat DynamoDB to nested JSON bytes.""" + return serde.encode(_dynamo_db_to_nested(dynamo_db)) + + +def _dynamo_db_from_nested_bytes(data: bytes, serde: Serde) -> DynamoDB: + """Convert nested JSON bytes to flat DynamoDB.""" + nested = serde.decode(data, DynamoDBNested) + return _dynamo_db_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +DynamoDB.DYNAMO_DB_STATUS = KeywordField("dynamoDBStatus", "dynamoDBStatus") +DynamoDB.DYNAMO_DB_PARTITION_KEY = KeywordField( + "dynamoDBPartitionKey", "dynamoDBPartitionKey" +) +DynamoDB.DYNAMO_DB_SORT_KEY = KeywordField("dynamoDBSortKey", "dynamoDBSortKey") +DynamoDB.DYNAMO_DB_READ_CAPACITY_UNITS = NumericField( + "dynamoDBReadCapacityUnits", "dynamoDBReadCapacityUnits" +) +DynamoDB.DYNAMO_DB_WRITE_CAPACITY_UNITS = NumericField( + "dynamoDBWriteCapacityUnits", "dynamoDBWriteCapacityUnits" +) +DynamoDB.NO_SQL_SCHEMA_DEFINITION = KeywordField( + "noSQLSchemaDefinition", "noSQLSchemaDefinition" +) +DynamoDB.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +DynamoDB.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +DynamoDB.ANOMALO_CHECKS = RelationField("anomaloChecks") +DynamoDB.APPLICATION = RelationField("application") +DynamoDB.APPLICATION_FIELD = RelationField("applicationField") +DynamoDB.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +DynamoDB.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +DynamoDB.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +DynamoDB.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +DynamoDB.METRICS = RelationField("metrics") +DynamoDB.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +DynamoDB.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +DynamoDB.MEANINGS = RelationField("meanings") +DynamoDB.MC_MONITORS = RelationField("mcMonitors") +DynamoDB.MC_INCIDENTS = RelationField("mcIncidents") +DynamoDB.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +DynamoDB.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +DynamoDB.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +DynamoDB.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +DynamoDB.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +DynamoDB.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +DynamoDB.FILES = RelationField("files") +DynamoDB.LINKS = RelationField("links") +DynamoDB.README = RelationField("readme") +DynamoDB.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +DynamoDB.SODA_CHECKS = RelationField("sodaChecks") +DynamoDB.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +DynamoDB.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/dynamo_db_attribute.py b/pyatlan_v9/model/assets/dynamo_db_attribute.py new file mode 100644 index 000000000..829f59d89 --- /dev/null +++ b/pyatlan_v9/model/assets/dynamo_db_attribute.py @@ -0,0 +1,1913 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DynamoDBAttribute asset model with flattened inheritance. + +This module provides: +- DynamoDBAttribute: Flat asset class (easy to use) +- DynamoDBAttributeAttributes: Nested attributes struct (extends AssetAttributes) +- DynamoDBAttributeNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .cosmos_mongo_db_related import RelatedCosmosMongoDBCollection +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtMetric, + RelatedDbtModel, + RelatedDbtModelColumn, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .mongo_db_related import RelatedMongoDBCollection +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .snowflake_related import ( + RelatedSnowflakeDynamicTable, + RelatedSnowflakeSemanticLogicalTable, +) +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from .sql_related import ( + RelatedCalculationView, + RelatedColumn, + RelatedMaterialisedView, + RelatedQuery, + RelatedTable, + RelatedTablePartition, + RelatedView, +) +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .dynamo_db_related import RelatedDynamoDBTable + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class DynamoDBAttribute(Asset): + """ + Represents an attribute (column) in a DynamoDB table. + """ + + DYNAMO_DB_STATUS: ClassVar[Any] = None + DYNAMO_DB_PARTITION_KEY: ClassVar[Any] = None + DYNAMO_DB_SORT_KEY: ClassVar[Any] = None + DYNAMO_DB_READ_CAPACITY_UNITS: ClassVar[Any] = None + DYNAMO_DB_WRITE_CAPACITY_UNITS: ClassVar[Any] = None + NO_SQL_SCHEMA_DEFINITION: ClassVar[Any] = None + DATA_TYPE: ClassVar[Any] = None + SUB_DATA_TYPE: ClassVar[Any] = None + COLUMN_COMPRESSION: ClassVar[Any] = None + COLUMN_ENCODING: ClassVar[Any] = None + RAW_DATA_TYPE_DEFINITION: ClassVar[Any] = None + ORDER: ClassVar[Any] = None + NESTED_COLUMN_ORDER: ClassVar[Any] = None + NESTED_COLUMN_COUNT: ClassVar[Any] = None + COLUMN_HIERARCHY: ClassVar[Any] = None + IS_PARTITION: ClassVar[Any] = None + PARTITION_ORDER: ClassVar[Any] = None + IS_CLUSTERED: ClassVar[Any] = None + IS_PRIMARY: ClassVar[Any] = None + IS_FOREIGN: ClassVar[Any] = None + IS_INDEXED: ClassVar[Any] = None + IS_SORT: ClassVar[Any] = None + IS_DIST: ClassVar[Any] = None + IS_PINNED: ClassVar[Any] = None + PINNED_BY: ClassVar[Any] = None + PINNED_AT: ClassVar[Any] = None + PRECISION: ClassVar[Any] = None + DEFAULT_VALUE: ClassVar[Any] = None + IS_NULLABLE: ClassVar[Any] = None + NUMERIC_SCALE: ClassVar[Any] = None + MAX_LENGTH: ClassVar[Any] = None + VALIDATIONS: ClassVar[Any] = None + PARENT_COLUMN_QUALIFIED_NAME: ClassVar[Any] = None + PARENT_COLUMN_NAME: ClassVar[Any] = None + COLUMN_DISTINCT_VALUES_COUNT: ClassVar[Any] = None + COLUMN_DISTINCT_VALUES_COUNT_LONG: ClassVar[Any] = None + COLUMN_HISTOGRAM: ClassVar[Any] = None + COLUMN_MAX: ClassVar[Any] = None + COLUMN_MIN: ClassVar[Any] = None + COLUMN_MEAN: ClassVar[Any] = None + COLUMN_SUM: ClassVar[Any] = None + COLUMN_MEDIAN: ClassVar[Any] = None + COLUMN_STANDARD_DEVIATION: ClassVar[Any] = None + COLUMN_UNIQUE_VALUES_COUNT: ClassVar[Any] = None + COLUMN_UNIQUE_VALUES_COUNT_LONG: ClassVar[Any] = None + COLUMN_AVERAGE: ClassVar[Any] = None + COLUMN_AVERAGE_LENGTH: ClassVar[Any] = None + COLUMN_DUPLICATE_VALUES_COUNT: ClassVar[Any] = None + COLUMN_DUPLICATE_VALUES_COUNT_LONG: ClassVar[Any] = None + COLUMN_MAXIMUM_STRING_LENGTH: ClassVar[Any] = None + COLUMN_MAXS: ClassVar[Any] = None + COLUMN_MINIMUM_STRING_LENGTH: ClassVar[Any] = None + COLUMN_MINS: ClassVar[Any] = None + COLUMN_MISSING_VALUES_COUNT: ClassVar[Any] = None + COLUMN_MISSING_VALUES_COUNT_LONG: ClassVar[Any] = None + COLUMN_MISSING_VALUES_PERCENTAGE: ClassVar[Any] = None + COLUMN_UNIQUENESS_PERCENTAGE: ClassVar[Any] = None + COLUMN_VARIANCE: ClassVar[Any] = None + COLUMN_TOP_VALUES: ClassVar[Any] = None + COLUMN_MAX_VALUE: ClassVar[Any] = None + COLUMN_MIN_VALUE: ClassVar[Any] = None + COLUMN_MEAN_VALUE: ClassVar[Any] = None + COLUMN_SUM_VALUE: ClassVar[Any] = None + COLUMN_MEDIAN_VALUE: ClassVar[Any] = None + COLUMN_STANDARD_DEVIATION_VALUE: ClassVar[Any] = None + COLUMN_AVERAGE_VALUE: ClassVar[Any] = None + COLUMN_VARIANCE_VALUE: ClassVar[Any] = None + COLUMN_AVERAGE_LENGTH_VALUE: ClassVar[Any] = None + COLUMN_DISTRIBUTION_HISTOGRAM: ClassVar[Any] = None + COLUMN_DEPTH_LEVEL: ClassVar[Any] = None + NOSQL_COLLECTION_NAME: ClassVar[Any] = None + NOSQL_COLLECTION_QUALIFIED_NAME: ClassVar[Any] = None + COLUMN_IS_MEASURE: ClassVar[Any] = None + COLUMN_MEASURE_TYPE: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + COSMOS_MONGO_DB_COLLECTION: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + METRIC_TIMESTAMPS: ClassVar[Any] = None + DATA_QUALITY_METRIC_DIMENSIONS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_BASE_COLUMN_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_COLUMN_RULES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_METRICS: ClassVar[Any] = None + DBT_MODEL_COLUMNS: ClassVar[Any] = None + COLUMN_DBT_MODEL_COLUMNS: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + DYNAMO_DB_TABLE: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MONGO_DB_COLLECTION: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + TABLE: ClassVar[Any] = None + NESTED_COLUMNS: ClassVar[Any] = None + PARENT_COLUMN: ClassVar[Any] = None + TABLE_PARTITION: ClassVar[Any] = None + VIEW: ClassVar[Any] = None + CALCULATION_VIEW: ClassVar[Any] = None + MATERIALISED_VIEW: ClassVar[Any] = None + FOREIGN_KEY_TO: ClassVar[Any] = None + FOREIGN_KEY_FROM: ClassVar[Any] = None + QUERIES: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_DYNAMIC_TABLE: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "DynamoDBAttribute" + + dynamo_db_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBStatus" + ) + """Status of the DynamoDB asset.""" + + dynamo_db_partition_key: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBPartitionKey" + ) + """Specifies the partition key of the DynamoDB table or index.""" + + dynamo_db_sort_key: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBSortKey" + ) + """Specifies the sort key of the DynamoDB table or index.""" + + dynamo_db_read_capacity_units: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBReadCapacityUnits" + ) + """The maximum number of strongly consistent reads consumed per second before DynamoDB returns a ThrottlingException.""" + + dynamo_db_write_capacity_units: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBWriteCapacityUnits" + ) + """The maximum number of writes consumed per second before DynamoDB returns a ThrottlingException.""" + + no_sql_schema_definition: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="noSQLSchemaDefinition" + ) + """Represents attributes for describing the key schema for the table and indexes.""" + + data_type: Union[str, None, UnsetType] = UNSET + """Data type of values in this column.""" + + sub_data_type: Union[str, None, UnsetType] = UNSET + """Sub-data type of this column.""" + + column_compression: Union[str, None, UnsetType] = UNSET + """Compression type of this column.""" + + column_encoding: Union[str, None, UnsetType] = UNSET + """Encoding type of this column.""" + + raw_data_type_definition: Union[str, None, UnsetType] = UNSET + """Raw data type definition of this column.""" + + order: Union[int, None, UnsetType] = UNSET + """Order (position) in which this column appears in the table (starting at 1).""" + + nested_column_order: Union[str, None, UnsetType] = UNSET + """Order (position) in which this column appears in the nested Column (nest level starts at 1).""" + + nested_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns nested within this (STRUCT or NESTED) column.""" + + column_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of top-level upstream nested columns.""" + + is_partition: Union[bool, None, UnsetType] = UNSET + """Whether this column is a partition column (true) or not (false).""" + + partition_order: Union[int, None, UnsetType] = UNSET + """Order (position) of this partition column in the table.""" + + is_clustered: Union[bool, None, UnsetType] = UNSET + """Whether this column is a clustered column (true) or not (false).""" + + is_primary: Union[bool, None, UnsetType] = UNSET + """When true, this column is the primary key for the table.""" + + is_foreign: Union[bool, None, UnsetType] = UNSET + """When true, this column is a foreign key to another table. NOTE: this must be true when using the foreignKeyTo relationship to specify columns that refer to this column as a foreign key.""" + + is_indexed: Union[bool, None, UnsetType] = UNSET + """When true, this column is indexed in the database.""" + + is_sort: Union[bool, None, UnsetType] = UNSET + """Whether this column is a sort column (true) or not (false).""" + + is_dist: Union[bool, None, UnsetType] = UNSET + """Whether this column is a distribution column (true) or not (false).""" + + is_pinned: Union[bool, None, UnsetType] = UNSET + """Whether this column is pinned (true) or not (false).""" + + pinned_by: Union[str, None, UnsetType] = UNSET + """User who pinned this column.""" + + pinned_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this column was pinned, in milliseconds.""" + + precision: Union[int, None, UnsetType] = UNSET + """Total number of digits allowed, when the dataType is numeric.""" + + default_value: Union[str, None, UnsetType] = UNSET + """Default value for this column.""" + + is_nullable: Union[bool, None, UnsetType] = UNSET + """When true, the values in this column can be null.""" + + numeric_scale: Union[float, None, UnsetType] = UNSET + """Number of digits allowed to the right of the decimal point.""" + + max_length: Union[int, None, UnsetType] = UNSET + """Maximum length of a value in this column.""" + + validations: Union[Dict[str, str], None, UnsetType] = UNSET + """Validations for this column.""" + + parent_column_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the column this column is nested within, for STRUCT and NESTED columns.""" + + parent_column_name: Union[str, None, UnsetType] = UNSET + """Simple name of the column this column is nested within, for STRUCT and NESTED columns.""" + + column_distinct_values_count: Union[int, None, UnsetType] = UNSET + """Number of rows that contain distinct values.""" + + column_distinct_values_count_long: Union[int, None, UnsetType] = UNSET + """Number of rows that contain distinct values.""" + + column_histogram: Union[Dict[str, Any], None, UnsetType] = UNSET + """List of values in a histogram that represents the contents of this column.""" + + column_max: Union[float, None, UnsetType] = UNSET + """Greatest value in a numeric column.""" + + column_min: Union[float, None, UnsetType] = UNSET + """Least value in a numeric column.""" + + column_mean: Union[float, None, UnsetType] = UNSET + """Arithmetic mean of the values in a numeric column.""" + + column_sum: Union[float, None, UnsetType] = UNSET + """Calculated sum of the values in a numeric column.""" + + column_median: Union[float, None, UnsetType] = UNSET + """Calculated median of the values in a numeric column.""" + + column_standard_deviation: Union[float, None, UnsetType] = UNSET + """Calculated standard deviation of the values in a numeric column.""" + + column_unique_values_count: Union[int, None, UnsetType] = UNSET + """Number of rows in which a value in this column appears only once.""" + + column_unique_values_count_long: Union[int, None, UnsetType] = UNSET + """Number of rows in which a value in this column appears only once.""" + + column_average: Union[float, None, UnsetType] = UNSET + """Average value in this column.""" + + column_average_length: Union[float, None, UnsetType] = UNSET + """Average length of values in a string column.""" + + column_duplicate_values_count: Union[int, None, UnsetType] = UNSET + """Number of rows that contain duplicate values.""" + + column_duplicate_values_count_long: Union[int, None, UnsetType] = UNSET + """Number of rows that contain duplicate values.""" + + column_maximum_string_length: Union[int, None, UnsetType] = UNSET + """Length of the longest value in a string column.""" + + column_maxs: Union[List[str], None, UnsetType] = UNSET + """List of the greatest values in a column.""" + + column_minimum_string_length: Union[int, None, UnsetType] = UNSET + """Length of the shortest value in a string column.""" + + column_mins: Union[List[str], None, UnsetType] = UNSET + """List of the least values in a column.""" + + column_missing_values_count: Union[int, None, UnsetType] = UNSET + """Number of rows in a column that do not contain content.""" + + column_missing_values_count_long: Union[int, None, UnsetType] = UNSET + """Number of rows in a column that do not contain content.""" + + column_missing_values_percentage: Union[float, None, UnsetType] = UNSET + """Percentage of rows in a column that do not contain content.""" + + column_uniqueness_percentage: Union[float, None, UnsetType] = UNSET + """Ratio indicating how unique data in this column is: 0 indicates that all values are the same, 100 indicates that all values in this column are unique.""" + + column_variance: Union[float, None, UnsetType] = UNSET + """Calculated variance of the values in a numeric column.""" + + column_top_values: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of top values in this column.""" + + column_max_value: Union[float, None, UnsetType] = UNSET + """Greatest value in a numeric column.""" + + column_min_value: Union[float, None, UnsetType] = UNSET + """Least value in a numeric column.""" + + column_mean_value: Union[float, None, UnsetType] = UNSET + """Arithmetic mean of the values in a numeric column.""" + + column_sum_value: Union[float, None, UnsetType] = UNSET + """Calculated sum of the values in a numeric column.""" + + column_median_value: Union[float, None, UnsetType] = UNSET + """Calculated median of the values in a numeric column.""" + + column_standard_deviation_value: Union[float, None, UnsetType] = UNSET + """Calculated standard deviation of the values in a numeric column.""" + + column_average_value: Union[float, None, UnsetType] = UNSET + """Average value in this column.""" + + column_variance_value: Union[float, None, UnsetType] = UNSET + """Calculated variance of the values in a numeric column.""" + + column_average_length_value: Union[float, None, UnsetType] = UNSET + """Average length of values in a string column.""" + + column_distribution_histogram: Union[Dict[str, Any], None, UnsetType] = UNSET + """Detailed information representing a histogram of values for a column.""" + + column_depth_level: Union[int, None, UnsetType] = UNSET + """Level of nesting of this column, used for STRUCT and NESTED columns.""" + + nosql_collection_name: Union[str, None, UnsetType] = UNSET + """Simple name of the cosmos/mongo collection in which this SQL asset (column) exists, or empty if it does not exist within a cosmos/mongo collection.""" + + nosql_collection_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the cosmos/mongo collection in which this SQL asset (column) exists, or empty if it does not exist within a cosmos/mongo collection.""" + + column_is_measure: Union[bool, None, UnsetType] = UNSET + """When true, this column is of type measure/calculated.""" + + column_measure_type: Union[str, None, UnsetType] = UNSET + """The type of measure/calculated column this is, eg: base, calculated, derived.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cosmos_mongo_db_collection: Union[ + RelatedCosmosMongoDBCollection, None, UnsetType + ] = msgspec.field(default=UNSET, name="cosmosMongoDBCollection") + """Cosmos collection in which this column exists.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + metric_timestamps: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + data_quality_metric_dimensions: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_base_column_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this column.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dq_reference_column_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this column is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_metrics: Union[List[RelatedDbtMetric], None, UnsetType] = UNSET + """Metrics related to this model column.""" + + dbt_model_columns: Union[List[RelatedDbtModelColumn], None, UnsetType] = UNSET + """(Deprecated) Model columns related to this model column.""" + + column_dbt_model_columns: Union[List[RelatedDbtModelColumn], None, UnsetType] = ( + UNSET + ) + """Model columns related to this column.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + dynamo_db_table: Union[RelatedDynamoDBTable, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBTable" + ) + """DynamoDB table in which this attribute exists.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mongo_db_collection: Union[RelatedMongoDBCollection, None, UnsetType] = ( + msgspec.field(default=UNSET, name="mongoDBCollection") + ) + """Collection in which the columns exist.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + table: Union[RelatedTable, None, UnsetType] = UNSET + """Table in which this column exists.""" + + nested_columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Nested columns that exist within this column.""" + + parent_column: Union[RelatedColumn, None, UnsetType] = UNSET + """Column in which this sub-column is nested.""" + + table_partition: Union[RelatedTablePartition, None, UnsetType] = UNSET + """Table partition that contains this column.""" + + view: Union[RelatedView, None, UnsetType] = UNSET + """View in which this column exists.""" + + calculation_view: Union[RelatedCalculationView, None, UnsetType] = UNSET + """Calculate view in which this column exists.""" + + materialised_view: Union[RelatedMaterialisedView, None, UnsetType] = UNSET + """Materialized view in which this column exists.""" + + foreign_key_to: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Columns that use this column as a foreign key.""" + + foreign_key_from: Union[RelatedColumn, None, UnsetType] = UNSET + """Column this foreign key column refers to.""" + + queries: Union[List[RelatedQuery], None, UnsetType] = UNSET + """Queries that access this column.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_dynamic_table: Union[RelatedSnowflakeDynamicTable, None, UnsetType] = ( + UNSET + ) + """Snowflake dynamic table in which this column exists.""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "DynamoDBAttribute" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _dynamo_db_attribute_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> DynamoDBAttribute: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + DynamoDBAttribute instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _dynamo_db_attribute_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DynamoDBAttributeAttributes(AssetAttributes): + """DynamoDBAttribute-specific attributes for nested API format.""" + + dynamo_db_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBStatus" + ) + """Status of the DynamoDB asset.""" + + dynamo_db_partition_key: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBPartitionKey" + ) + """Specifies the partition key of the DynamoDB table or index.""" + + dynamo_db_sort_key: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBSortKey" + ) + """Specifies the sort key of the DynamoDB table or index.""" + + dynamo_db_read_capacity_units: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBReadCapacityUnits" + ) + """The maximum number of strongly consistent reads consumed per second before DynamoDB returns a ThrottlingException.""" + + dynamo_db_write_capacity_units: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBWriteCapacityUnits" + ) + """The maximum number of writes consumed per second before DynamoDB returns a ThrottlingException.""" + + no_sql_schema_definition: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="noSQLSchemaDefinition" + ) + """Represents attributes for describing the key schema for the table and indexes.""" + + data_type: Union[str, None, UnsetType] = UNSET + """Data type of values in this column.""" + + sub_data_type: Union[str, None, UnsetType] = UNSET + """Sub-data type of this column.""" + + column_compression: Union[str, None, UnsetType] = UNSET + """Compression type of this column.""" + + column_encoding: Union[str, None, UnsetType] = UNSET + """Encoding type of this column.""" + + raw_data_type_definition: Union[str, None, UnsetType] = UNSET + """Raw data type definition of this column.""" + + order: Union[int, None, UnsetType] = UNSET + """Order (position) in which this column appears in the table (starting at 1).""" + + nested_column_order: Union[str, None, UnsetType] = UNSET + """Order (position) in which this column appears in the nested Column (nest level starts at 1).""" + + nested_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns nested within this (STRUCT or NESTED) column.""" + + column_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of top-level upstream nested columns.""" + + is_partition: Union[bool, None, UnsetType] = UNSET + """Whether this column is a partition column (true) or not (false).""" + + partition_order: Union[int, None, UnsetType] = UNSET + """Order (position) of this partition column in the table.""" + + is_clustered: Union[bool, None, UnsetType] = UNSET + """Whether this column is a clustered column (true) or not (false).""" + + is_primary: Union[bool, None, UnsetType] = UNSET + """When true, this column is the primary key for the table.""" + + is_foreign: Union[bool, None, UnsetType] = UNSET + """When true, this column is a foreign key to another table. NOTE: this must be true when using the foreignKeyTo relationship to specify columns that refer to this column as a foreign key.""" + + is_indexed: Union[bool, None, UnsetType] = UNSET + """When true, this column is indexed in the database.""" + + is_sort: Union[bool, None, UnsetType] = UNSET + """Whether this column is a sort column (true) or not (false).""" + + is_dist: Union[bool, None, UnsetType] = UNSET + """Whether this column is a distribution column (true) or not (false).""" + + is_pinned: Union[bool, None, UnsetType] = UNSET + """Whether this column is pinned (true) or not (false).""" + + pinned_by: Union[str, None, UnsetType] = UNSET + """User who pinned this column.""" + + pinned_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this column was pinned, in milliseconds.""" + + precision: Union[int, None, UnsetType] = UNSET + """Total number of digits allowed, when the dataType is numeric.""" + + default_value: Union[str, None, UnsetType] = UNSET + """Default value for this column.""" + + is_nullable: Union[bool, None, UnsetType] = UNSET + """When true, the values in this column can be null.""" + + numeric_scale: Union[float, None, UnsetType] = UNSET + """Number of digits allowed to the right of the decimal point.""" + + max_length: Union[int, None, UnsetType] = UNSET + """Maximum length of a value in this column.""" + + validations: Union[Dict[str, str], None, UnsetType] = UNSET + """Validations for this column.""" + + parent_column_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the column this column is nested within, for STRUCT and NESTED columns.""" + + parent_column_name: Union[str, None, UnsetType] = UNSET + """Simple name of the column this column is nested within, for STRUCT and NESTED columns.""" + + column_distinct_values_count: Union[int, None, UnsetType] = UNSET + """Number of rows that contain distinct values.""" + + column_distinct_values_count_long: Union[int, None, UnsetType] = UNSET + """Number of rows that contain distinct values.""" + + column_histogram: Union[Dict[str, Any], None, UnsetType] = UNSET + """List of values in a histogram that represents the contents of this column.""" + + column_max: Union[float, None, UnsetType] = UNSET + """Greatest value in a numeric column.""" + + column_min: Union[float, None, UnsetType] = UNSET + """Least value in a numeric column.""" + + column_mean: Union[float, None, UnsetType] = UNSET + """Arithmetic mean of the values in a numeric column.""" + + column_sum: Union[float, None, UnsetType] = UNSET + """Calculated sum of the values in a numeric column.""" + + column_median: Union[float, None, UnsetType] = UNSET + """Calculated median of the values in a numeric column.""" + + column_standard_deviation: Union[float, None, UnsetType] = UNSET + """Calculated standard deviation of the values in a numeric column.""" + + column_unique_values_count: Union[int, None, UnsetType] = UNSET + """Number of rows in which a value in this column appears only once.""" + + column_unique_values_count_long: Union[int, None, UnsetType] = UNSET + """Number of rows in which a value in this column appears only once.""" + + column_average: Union[float, None, UnsetType] = UNSET + """Average value in this column.""" + + column_average_length: Union[float, None, UnsetType] = UNSET + """Average length of values in a string column.""" + + column_duplicate_values_count: Union[int, None, UnsetType] = UNSET + """Number of rows that contain duplicate values.""" + + column_duplicate_values_count_long: Union[int, None, UnsetType] = UNSET + """Number of rows that contain duplicate values.""" + + column_maximum_string_length: Union[int, None, UnsetType] = UNSET + """Length of the longest value in a string column.""" + + column_maxs: Union[List[str], None, UnsetType] = UNSET + """List of the greatest values in a column.""" + + column_minimum_string_length: Union[int, None, UnsetType] = UNSET + """Length of the shortest value in a string column.""" + + column_mins: Union[List[str], None, UnsetType] = UNSET + """List of the least values in a column.""" + + column_missing_values_count: Union[int, None, UnsetType] = UNSET + """Number of rows in a column that do not contain content.""" + + column_missing_values_count_long: Union[int, None, UnsetType] = UNSET + """Number of rows in a column that do not contain content.""" + + column_missing_values_percentage: Union[float, None, UnsetType] = UNSET + """Percentage of rows in a column that do not contain content.""" + + column_uniqueness_percentage: Union[float, None, UnsetType] = UNSET + """Ratio indicating how unique data in this column is: 0 indicates that all values are the same, 100 indicates that all values in this column are unique.""" + + column_variance: Union[float, None, UnsetType] = UNSET + """Calculated variance of the values in a numeric column.""" + + column_top_values: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of top values in this column.""" + + column_max_value: Union[float, None, UnsetType] = UNSET + """Greatest value in a numeric column.""" + + column_min_value: Union[float, None, UnsetType] = UNSET + """Least value in a numeric column.""" + + column_mean_value: Union[float, None, UnsetType] = UNSET + """Arithmetic mean of the values in a numeric column.""" + + column_sum_value: Union[float, None, UnsetType] = UNSET + """Calculated sum of the values in a numeric column.""" + + column_median_value: Union[float, None, UnsetType] = UNSET + """Calculated median of the values in a numeric column.""" + + column_standard_deviation_value: Union[float, None, UnsetType] = UNSET + """Calculated standard deviation of the values in a numeric column.""" + + column_average_value: Union[float, None, UnsetType] = UNSET + """Average value in this column.""" + + column_variance_value: Union[float, None, UnsetType] = UNSET + """Calculated variance of the values in a numeric column.""" + + column_average_length_value: Union[float, None, UnsetType] = UNSET + """Average length of values in a string column.""" + + column_distribution_histogram: Union[Dict[str, Any], None, UnsetType] = UNSET + """Detailed information representing a histogram of values for a column.""" + + column_depth_level: Union[int, None, UnsetType] = UNSET + """Level of nesting of this column, used for STRUCT and NESTED columns.""" + + nosql_collection_name: Union[str, None, UnsetType] = UNSET + """Simple name of the cosmos/mongo collection in which this SQL asset (column) exists, or empty if it does not exist within a cosmos/mongo collection.""" + + nosql_collection_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the cosmos/mongo collection in which this SQL asset (column) exists, or empty if it does not exist within a cosmos/mongo collection.""" + + column_is_measure: Union[bool, None, UnsetType] = UNSET + """When true, this column is of type measure/calculated.""" + + column_measure_type: Union[str, None, UnsetType] = UNSET + """The type of measure/calculated column this is, eg: base, calculated, derived.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + +class DynamoDBAttributeRelationshipAttributes(AssetRelationshipAttributes): + """DynamoDBAttribute-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cosmos_mongo_db_collection: Union[ + RelatedCosmosMongoDBCollection, None, UnsetType + ] = msgspec.field(default=UNSET, name="cosmosMongoDBCollection") + """Cosmos collection in which this column exists.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + metric_timestamps: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + data_quality_metric_dimensions: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_base_column_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this column.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dq_reference_column_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this column is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_metrics: Union[List[RelatedDbtMetric], None, UnsetType] = UNSET + """Metrics related to this model column.""" + + dbt_model_columns: Union[List[RelatedDbtModelColumn], None, UnsetType] = UNSET + """(Deprecated) Model columns related to this model column.""" + + column_dbt_model_columns: Union[List[RelatedDbtModelColumn], None, UnsetType] = ( + UNSET + ) + """Model columns related to this column.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + dynamo_db_table: Union[RelatedDynamoDBTable, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBTable" + ) + """DynamoDB table in which this attribute exists.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mongo_db_collection: Union[RelatedMongoDBCollection, None, UnsetType] = ( + msgspec.field(default=UNSET, name="mongoDBCollection") + ) + """Collection in which the columns exist.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + table: Union[RelatedTable, None, UnsetType] = UNSET + """Table in which this column exists.""" + + nested_columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Nested columns that exist within this column.""" + + parent_column: Union[RelatedColumn, None, UnsetType] = UNSET + """Column in which this sub-column is nested.""" + + table_partition: Union[RelatedTablePartition, None, UnsetType] = UNSET + """Table partition that contains this column.""" + + view: Union[RelatedView, None, UnsetType] = UNSET + """View in which this column exists.""" + + calculation_view: Union[RelatedCalculationView, None, UnsetType] = UNSET + """Calculate view in which this column exists.""" + + materialised_view: Union[RelatedMaterialisedView, None, UnsetType] = UNSET + """Materialized view in which this column exists.""" + + foreign_key_to: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Columns that use this column as a foreign key.""" + + foreign_key_from: Union[RelatedColumn, None, UnsetType] = UNSET + """Column this foreign key column refers to.""" + + queries: Union[List[RelatedQuery], None, UnsetType] = UNSET + """Queries that access this column.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_dynamic_table: Union[RelatedSnowflakeDynamicTable, None, UnsetType] = ( + UNSET + ) + """Snowflake dynamic table in which this column exists.""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DynamoDBAttributeNested(AssetNested): + """DynamoDBAttribute in nested API format for high-performance serialization.""" + + attributes: Union[DynamoDBAttributeAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + DynamoDBAttributeRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + DynamoDBAttributeRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + DynamoDBAttributeRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DYNAMO_DB_ATTRIBUTE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "cosmos_mongo_db_collection", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "metric_timestamps", + "data_quality_metric_dimensions", + "dq_base_dataset_rules", + "dq_base_column_rules", + "dq_reference_dataset_rules", + "dq_reference_column_rules", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_metrics", + "dbt_model_columns", + "column_dbt_model_columns", + "dbt_seed_assets", + "dynamo_db_table", + "meanings", + "mongo_db_collection", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "table", + "nested_columns", + "parent_column", + "table_partition", + "view", + "calculation_view", + "materialised_view", + "foreign_key_to", + "foreign_key_from", + "queries", + "schema_registry_subjects", + "snowflake_dynamic_table", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_dynamo_db_attribute_attrs( + attrs: DynamoDBAttributeAttributes, obj: DynamoDBAttribute +) -> None: + """Populate DynamoDBAttribute-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.dynamo_db_status = obj.dynamo_db_status + attrs.dynamo_db_partition_key = obj.dynamo_db_partition_key + attrs.dynamo_db_sort_key = obj.dynamo_db_sort_key + attrs.dynamo_db_read_capacity_units = obj.dynamo_db_read_capacity_units + attrs.dynamo_db_write_capacity_units = obj.dynamo_db_write_capacity_units + attrs.no_sql_schema_definition = obj.no_sql_schema_definition + attrs.data_type = obj.data_type + attrs.sub_data_type = obj.sub_data_type + attrs.column_compression = obj.column_compression + attrs.column_encoding = obj.column_encoding + attrs.raw_data_type_definition = obj.raw_data_type_definition + attrs.order = obj.order + attrs.nested_column_order = obj.nested_column_order + attrs.nested_column_count = obj.nested_column_count + attrs.column_hierarchy = obj.column_hierarchy + attrs.is_partition = obj.is_partition + attrs.partition_order = obj.partition_order + attrs.is_clustered = obj.is_clustered + attrs.is_primary = obj.is_primary + attrs.is_foreign = obj.is_foreign + attrs.is_indexed = obj.is_indexed + attrs.is_sort = obj.is_sort + attrs.is_dist = obj.is_dist + attrs.is_pinned = obj.is_pinned + attrs.pinned_by = obj.pinned_by + attrs.pinned_at = obj.pinned_at + attrs.precision = obj.precision + attrs.default_value = obj.default_value + attrs.is_nullable = obj.is_nullable + attrs.numeric_scale = obj.numeric_scale + attrs.max_length = obj.max_length + attrs.validations = obj.validations + attrs.parent_column_qualified_name = obj.parent_column_qualified_name + attrs.parent_column_name = obj.parent_column_name + attrs.column_distinct_values_count = obj.column_distinct_values_count + attrs.column_distinct_values_count_long = obj.column_distinct_values_count_long + attrs.column_histogram = obj.column_histogram + attrs.column_max = obj.column_max + attrs.column_min = obj.column_min + attrs.column_mean = obj.column_mean + attrs.column_sum = obj.column_sum + attrs.column_median = obj.column_median + attrs.column_standard_deviation = obj.column_standard_deviation + attrs.column_unique_values_count = obj.column_unique_values_count + attrs.column_unique_values_count_long = obj.column_unique_values_count_long + attrs.column_average = obj.column_average + attrs.column_average_length = obj.column_average_length + attrs.column_duplicate_values_count = obj.column_duplicate_values_count + attrs.column_duplicate_values_count_long = obj.column_duplicate_values_count_long + attrs.column_maximum_string_length = obj.column_maximum_string_length + attrs.column_maxs = obj.column_maxs + attrs.column_minimum_string_length = obj.column_minimum_string_length + attrs.column_mins = obj.column_mins + attrs.column_missing_values_count = obj.column_missing_values_count + attrs.column_missing_values_count_long = obj.column_missing_values_count_long + attrs.column_missing_values_percentage = obj.column_missing_values_percentage + attrs.column_uniqueness_percentage = obj.column_uniqueness_percentage + attrs.column_variance = obj.column_variance + attrs.column_top_values = obj.column_top_values + attrs.column_max_value = obj.column_max_value + attrs.column_min_value = obj.column_min_value + attrs.column_mean_value = obj.column_mean_value + attrs.column_sum_value = obj.column_sum_value + attrs.column_median_value = obj.column_median_value + attrs.column_standard_deviation_value = obj.column_standard_deviation_value + attrs.column_average_value = obj.column_average_value + attrs.column_variance_value = obj.column_variance_value + attrs.column_average_length_value = obj.column_average_length_value + attrs.column_distribution_histogram = obj.column_distribution_histogram + attrs.column_depth_level = obj.column_depth_level + attrs.nosql_collection_name = obj.nosql_collection_name + attrs.nosql_collection_qualified_name = obj.nosql_collection_qualified_name + attrs.column_is_measure = obj.column_is_measure + attrs.column_measure_type = obj.column_measure_type + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + + +def _extract_dynamo_db_attribute_attrs(attrs: DynamoDBAttributeAttributes) -> dict: + """Extract all DynamoDBAttribute attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["dynamo_db_status"] = attrs.dynamo_db_status + result["dynamo_db_partition_key"] = attrs.dynamo_db_partition_key + result["dynamo_db_sort_key"] = attrs.dynamo_db_sort_key + result["dynamo_db_read_capacity_units"] = attrs.dynamo_db_read_capacity_units + result["dynamo_db_write_capacity_units"] = attrs.dynamo_db_write_capacity_units + result["no_sql_schema_definition"] = attrs.no_sql_schema_definition + result["data_type"] = attrs.data_type + result["sub_data_type"] = attrs.sub_data_type + result["column_compression"] = attrs.column_compression + result["column_encoding"] = attrs.column_encoding + result["raw_data_type_definition"] = attrs.raw_data_type_definition + result["order"] = attrs.order + result["nested_column_order"] = attrs.nested_column_order + result["nested_column_count"] = attrs.nested_column_count + result["column_hierarchy"] = attrs.column_hierarchy + result["is_partition"] = attrs.is_partition + result["partition_order"] = attrs.partition_order + result["is_clustered"] = attrs.is_clustered + result["is_primary"] = attrs.is_primary + result["is_foreign"] = attrs.is_foreign + result["is_indexed"] = attrs.is_indexed + result["is_sort"] = attrs.is_sort + result["is_dist"] = attrs.is_dist + result["is_pinned"] = attrs.is_pinned + result["pinned_by"] = attrs.pinned_by + result["pinned_at"] = attrs.pinned_at + result["precision"] = attrs.precision + result["default_value"] = attrs.default_value + result["is_nullable"] = attrs.is_nullable + result["numeric_scale"] = attrs.numeric_scale + result["max_length"] = attrs.max_length + result["validations"] = attrs.validations + result["parent_column_qualified_name"] = attrs.parent_column_qualified_name + result["parent_column_name"] = attrs.parent_column_name + result["column_distinct_values_count"] = attrs.column_distinct_values_count + result["column_distinct_values_count_long"] = ( + attrs.column_distinct_values_count_long + ) + result["column_histogram"] = attrs.column_histogram + result["column_max"] = attrs.column_max + result["column_min"] = attrs.column_min + result["column_mean"] = attrs.column_mean + result["column_sum"] = attrs.column_sum + result["column_median"] = attrs.column_median + result["column_standard_deviation"] = attrs.column_standard_deviation + result["column_unique_values_count"] = attrs.column_unique_values_count + result["column_unique_values_count_long"] = attrs.column_unique_values_count_long + result["column_average"] = attrs.column_average + result["column_average_length"] = attrs.column_average_length + result["column_duplicate_values_count"] = attrs.column_duplicate_values_count + result["column_duplicate_values_count_long"] = ( + attrs.column_duplicate_values_count_long + ) + result["column_maximum_string_length"] = attrs.column_maximum_string_length + result["column_maxs"] = attrs.column_maxs + result["column_minimum_string_length"] = attrs.column_minimum_string_length + result["column_mins"] = attrs.column_mins + result["column_missing_values_count"] = attrs.column_missing_values_count + result["column_missing_values_count_long"] = attrs.column_missing_values_count_long + result["column_missing_values_percentage"] = attrs.column_missing_values_percentage + result["column_uniqueness_percentage"] = attrs.column_uniqueness_percentage + result["column_variance"] = attrs.column_variance + result["column_top_values"] = attrs.column_top_values + result["column_max_value"] = attrs.column_max_value + result["column_min_value"] = attrs.column_min_value + result["column_mean_value"] = attrs.column_mean_value + result["column_sum_value"] = attrs.column_sum_value + result["column_median_value"] = attrs.column_median_value + result["column_standard_deviation_value"] = attrs.column_standard_deviation_value + result["column_average_value"] = attrs.column_average_value + result["column_variance_value"] = attrs.column_variance_value + result["column_average_length_value"] = attrs.column_average_length_value + result["column_distribution_histogram"] = attrs.column_distribution_histogram + result["column_depth_level"] = attrs.column_depth_level + result["nosql_collection_name"] = attrs.nosql_collection_name + result["nosql_collection_qualified_name"] = attrs.nosql_collection_qualified_name + result["column_is_measure"] = attrs.column_is_measure + result["column_measure_type"] = attrs.column_measure_type + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _dynamo_db_attribute_to_nested( + dynamo_db_attribute: DynamoDBAttribute, +) -> DynamoDBAttributeNested: + """Convert flat DynamoDBAttribute to nested format.""" + attrs = DynamoDBAttributeAttributes() + _populate_dynamo_db_attribute_attrs(attrs, dynamo_db_attribute) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + dynamo_db_attribute, + _DYNAMO_DB_ATTRIBUTE_REL_FIELDS, + DynamoDBAttributeRelationshipAttributes, + ) + return DynamoDBAttributeNested( + guid=dynamo_db_attribute.guid, + type_name=dynamo_db_attribute.type_name, + status=dynamo_db_attribute.status, + version=dynamo_db_attribute.version, + create_time=dynamo_db_attribute.create_time, + update_time=dynamo_db_attribute.update_time, + created_by=dynamo_db_attribute.created_by, + updated_by=dynamo_db_attribute.updated_by, + classifications=dynamo_db_attribute.classifications, + classification_names=dynamo_db_attribute.classification_names, + meanings=dynamo_db_attribute.meanings, + labels=dynamo_db_attribute.labels, + business_attributes=dynamo_db_attribute.business_attributes, + custom_attributes=dynamo_db_attribute.custom_attributes, + pending_tasks=dynamo_db_attribute.pending_tasks, + proxy=dynamo_db_attribute.proxy, + is_incomplete=dynamo_db_attribute.is_incomplete, + provenance_type=dynamo_db_attribute.provenance_type, + home_id=dynamo_db_attribute.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _dynamo_db_attribute_from_nested( + nested: DynamoDBAttributeNested, +) -> DynamoDBAttribute: + """Convert nested format to flat DynamoDBAttribute.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else DynamoDBAttributeAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DYNAMO_DB_ATTRIBUTE_REL_FIELDS, + DynamoDBAttributeRelationshipAttributes, + ) + return DynamoDBAttribute( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_dynamo_db_attribute_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _dynamo_db_attribute_to_nested_bytes( + dynamo_db_attribute: DynamoDBAttribute, serde: Serde +) -> bytes: + """Convert flat DynamoDBAttribute to nested JSON bytes.""" + return serde.encode(_dynamo_db_attribute_to_nested(dynamo_db_attribute)) + + +def _dynamo_db_attribute_from_nested_bytes( + data: bytes, serde: Serde +) -> DynamoDBAttribute: + """Convert nested JSON bytes to flat DynamoDBAttribute.""" + nested = serde.decode(data, DynamoDBAttributeNested) + return _dynamo_db_attribute_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +DynamoDBAttribute.DYNAMO_DB_STATUS = KeywordField("dynamoDBStatus", "dynamoDBStatus") +DynamoDBAttribute.DYNAMO_DB_PARTITION_KEY = KeywordField( + "dynamoDBPartitionKey", "dynamoDBPartitionKey" +) +DynamoDBAttribute.DYNAMO_DB_SORT_KEY = KeywordField( + "dynamoDBSortKey", "dynamoDBSortKey" +) +DynamoDBAttribute.DYNAMO_DB_READ_CAPACITY_UNITS = NumericField( + "dynamoDBReadCapacityUnits", "dynamoDBReadCapacityUnits" +) +DynamoDBAttribute.DYNAMO_DB_WRITE_CAPACITY_UNITS = NumericField( + "dynamoDBWriteCapacityUnits", "dynamoDBWriteCapacityUnits" +) +DynamoDBAttribute.NO_SQL_SCHEMA_DEFINITION = KeywordField( + "noSQLSchemaDefinition", "noSQLSchemaDefinition" +) +DynamoDBAttribute.DATA_TYPE = KeywordTextField("dataType", "dataType", "dataType.text") +DynamoDBAttribute.SUB_DATA_TYPE = KeywordField("subDataType", "subDataType") +DynamoDBAttribute.COLUMN_COMPRESSION = KeywordField( + "columnCompression", "columnCompression" +) +DynamoDBAttribute.COLUMN_ENCODING = KeywordField("columnEncoding", "columnEncoding") +DynamoDBAttribute.RAW_DATA_TYPE_DEFINITION = KeywordField( + "rawDataTypeDefinition", "rawDataTypeDefinition" +) +DynamoDBAttribute.ORDER = NumericField("order", "order") +DynamoDBAttribute.NESTED_COLUMN_ORDER = KeywordTextField( + "nestedColumnOrder", "nestedColumnOrder", "nestedColumnOrder.text" +) +DynamoDBAttribute.NESTED_COLUMN_COUNT = NumericField( + "nestedColumnCount", "nestedColumnCount" +) +DynamoDBAttribute.COLUMN_HIERARCHY = KeywordField("columnHierarchy", "columnHierarchy") +DynamoDBAttribute.IS_PARTITION = BooleanField("isPartition", "isPartition") +DynamoDBAttribute.PARTITION_ORDER = NumericField("partitionOrder", "partitionOrder") +DynamoDBAttribute.IS_CLUSTERED = BooleanField("isClustered", "isClustered") +DynamoDBAttribute.IS_PRIMARY = BooleanField("isPrimary", "isPrimary") +DynamoDBAttribute.IS_FOREIGN = BooleanField("isForeign", "isForeign") +DynamoDBAttribute.IS_INDEXED = BooleanField("isIndexed", "isIndexed") +DynamoDBAttribute.IS_SORT = BooleanField("isSort", "isSort") +DynamoDBAttribute.IS_DIST = BooleanField("isDist", "isDist") +DynamoDBAttribute.IS_PINNED = BooleanField("isPinned", "isPinned") +DynamoDBAttribute.PINNED_BY = KeywordField("pinnedBy", "pinnedBy") +DynamoDBAttribute.PINNED_AT = NumericField("pinnedAt", "pinnedAt") +DynamoDBAttribute.PRECISION = NumericField("precision", "precision") +DynamoDBAttribute.DEFAULT_VALUE = KeywordField("defaultValue", "defaultValue") +DynamoDBAttribute.IS_NULLABLE = BooleanField("isNullable", "isNullable") +DynamoDBAttribute.NUMERIC_SCALE = NumericField("numericScale", "numericScale") +DynamoDBAttribute.MAX_LENGTH = NumericField("maxLength", "maxLength") +DynamoDBAttribute.VALIDATIONS = KeywordField("validations", "validations") +DynamoDBAttribute.PARENT_COLUMN_QUALIFIED_NAME = KeywordTextField( + "parentColumnQualifiedName", + "parentColumnQualifiedName", + "parentColumnQualifiedName.text", +) +DynamoDBAttribute.PARENT_COLUMN_NAME = KeywordField( + "parentColumnName", "parentColumnName" +) +DynamoDBAttribute.COLUMN_DISTINCT_VALUES_COUNT = NumericField( + "columnDistinctValuesCount", "columnDistinctValuesCount" +) +DynamoDBAttribute.COLUMN_DISTINCT_VALUES_COUNT_LONG = NumericField( + "columnDistinctValuesCountLong", "columnDistinctValuesCountLong" +) +DynamoDBAttribute.COLUMN_HISTOGRAM = KeywordField("columnHistogram", "columnHistogram") +DynamoDBAttribute.COLUMN_MAX = NumericField("columnMax", "columnMax") +DynamoDBAttribute.COLUMN_MIN = NumericField("columnMin", "columnMin") +DynamoDBAttribute.COLUMN_MEAN = NumericField("columnMean", "columnMean") +DynamoDBAttribute.COLUMN_SUM = NumericField("columnSum", "columnSum") +DynamoDBAttribute.COLUMN_MEDIAN = NumericField("columnMedian", "columnMedian") +DynamoDBAttribute.COLUMN_STANDARD_DEVIATION = NumericField( + "columnStandardDeviation", "columnStandardDeviation" +) +DynamoDBAttribute.COLUMN_UNIQUE_VALUES_COUNT = NumericField( + "columnUniqueValuesCount", "columnUniqueValuesCount" +) +DynamoDBAttribute.COLUMN_UNIQUE_VALUES_COUNT_LONG = NumericField( + "columnUniqueValuesCountLong", "columnUniqueValuesCountLong" +) +DynamoDBAttribute.COLUMN_AVERAGE = NumericField("columnAverage", "columnAverage") +DynamoDBAttribute.COLUMN_AVERAGE_LENGTH = NumericField( + "columnAverageLength", "columnAverageLength" +) +DynamoDBAttribute.COLUMN_DUPLICATE_VALUES_COUNT = NumericField( + "columnDuplicateValuesCount", "columnDuplicateValuesCount" +) +DynamoDBAttribute.COLUMN_DUPLICATE_VALUES_COUNT_LONG = NumericField( + "columnDuplicateValuesCountLong", "columnDuplicateValuesCountLong" +) +DynamoDBAttribute.COLUMN_MAXIMUM_STRING_LENGTH = NumericField( + "columnMaximumStringLength", "columnMaximumStringLength" +) +DynamoDBAttribute.COLUMN_MAXS = KeywordField("columnMaxs", "columnMaxs") +DynamoDBAttribute.COLUMN_MINIMUM_STRING_LENGTH = NumericField( + "columnMinimumStringLength", "columnMinimumStringLength" +) +DynamoDBAttribute.COLUMN_MINS = KeywordField("columnMins", "columnMins") +DynamoDBAttribute.COLUMN_MISSING_VALUES_COUNT = NumericField( + "columnMissingValuesCount", "columnMissingValuesCount" +) +DynamoDBAttribute.COLUMN_MISSING_VALUES_COUNT_LONG = NumericField( + "columnMissingValuesCountLong", "columnMissingValuesCountLong" +) +DynamoDBAttribute.COLUMN_MISSING_VALUES_PERCENTAGE = NumericField( + "columnMissingValuesPercentage", "columnMissingValuesPercentage" +) +DynamoDBAttribute.COLUMN_UNIQUENESS_PERCENTAGE = NumericField( + "columnUniquenessPercentage", "columnUniquenessPercentage" +) +DynamoDBAttribute.COLUMN_VARIANCE = NumericField("columnVariance", "columnVariance") +DynamoDBAttribute.COLUMN_TOP_VALUES = KeywordField("columnTopValues", "columnTopValues") +DynamoDBAttribute.COLUMN_MAX_VALUE = NumericField("columnMaxValue", "columnMaxValue") +DynamoDBAttribute.COLUMN_MIN_VALUE = NumericField("columnMinValue", "columnMinValue") +DynamoDBAttribute.COLUMN_MEAN_VALUE = NumericField("columnMeanValue", "columnMeanValue") +DynamoDBAttribute.COLUMN_SUM_VALUE = NumericField("columnSumValue", "columnSumValue") +DynamoDBAttribute.COLUMN_MEDIAN_VALUE = NumericField( + "columnMedianValue", "columnMedianValue" +) +DynamoDBAttribute.COLUMN_STANDARD_DEVIATION_VALUE = NumericField( + "columnStandardDeviationValue", "columnStandardDeviationValue" +) +DynamoDBAttribute.COLUMN_AVERAGE_VALUE = NumericField( + "columnAverageValue", "columnAverageValue" +) +DynamoDBAttribute.COLUMN_VARIANCE_VALUE = NumericField( + "columnVarianceValue", "columnVarianceValue" +) +DynamoDBAttribute.COLUMN_AVERAGE_LENGTH_VALUE = NumericField( + "columnAverageLengthValue", "columnAverageLengthValue" +) +DynamoDBAttribute.COLUMN_DISTRIBUTION_HISTOGRAM = KeywordField( + "columnDistributionHistogram", "columnDistributionHistogram" +) +DynamoDBAttribute.COLUMN_DEPTH_LEVEL = NumericField( + "columnDepthLevel", "columnDepthLevel" +) +DynamoDBAttribute.NOSQL_COLLECTION_NAME = KeywordField( + "nosqlCollectionName", "nosqlCollectionName" +) +DynamoDBAttribute.NOSQL_COLLECTION_QUALIFIED_NAME = KeywordField( + "nosqlCollectionQualifiedName", "nosqlCollectionQualifiedName" +) +DynamoDBAttribute.COLUMN_IS_MEASURE = BooleanField("columnIsMeasure", "columnIsMeasure") +DynamoDBAttribute.COLUMN_MEASURE_TYPE = KeywordField( + "columnMeasureType", "columnMeasureType" +) +DynamoDBAttribute.QUERY_COUNT = NumericField("queryCount", "queryCount") +DynamoDBAttribute.QUERY_USER_COUNT = NumericField("queryUserCount", "queryUserCount") +DynamoDBAttribute.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +DynamoDBAttribute.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +DynamoDBAttribute.DATABASE_NAME = KeywordField("databaseName", "databaseName") +DynamoDBAttribute.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +DynamoDBAttribute.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +DynamoDBAttribute.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +DynamoDBAttribute.TABLE_NAME = KeywordField("tableName", "tableName") +DynamoDBAttribute.TABLE_QUALIFIED_NAME = KeywordField( + "tableQualifiedName", "tableQualifiedName" +) +DynamoDBAttribute.VIEW_NAME = KeywordField("viewName", "viewName") +DynamoDBAttribute.VIEW_QUALIFIED_NAME = KeywordField( + "viewQualifiedName", "viewQualifiedName" +) +DynamoDBAttribute.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +DynamoDBAttribute.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +DynamoDBAttribute.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +DynamoDBAttribute.LAST_PROFILED_AT = NumericField("lastProfiledAt", "lastProfiledAt") +DynamoDBAttribute.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +DynamoDBAttribute.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +DynamoDBAttribute.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +DynamoDBAttribute.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +DynamoDBAttribute.ANOMALO_CHECKS = RelationField("anomaloChecks") +DynamoDBAttribute.APPLICATION = RelationField("application") +DynamoDBAttribute.APPLICATION_FIELD = RelationField("applicationField") +DynamoDBAttribute.COSMOS_MONGO_DB_COLLECTION = RelationField("cosmosMongoDBCollection") +DynamoDBAttribute.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +DynamoDBAttribute.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +DynamoDBAttribute.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +DynamoDBAttribute.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +DynamoDBAttribute.METRICS = RelationField("metrics") +DynamoDBAttribute.METRIC_TIMESTAMPS = RelationField("metricTimestamps") +DynamoDBAttribute.DATA_QUALITY_METRIC_DIMENSIONS = RelationField( + "dataQualityMetricDimensions" +) +DynamoDBAttribute.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +DynamoDBAttribute.DQ_BASE_COLUMN_RULES = RelationField("dqBaseColumnRules") +DynamoDBAttribute.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +DynamoDBAttribute.DQ_REFERENCE_COLUMN_RULES = RelationField("dqReferenceColumnRules") +DynamoDBAttribute.DBT_MODELS = RelationField("dbtModels") +DynamoDBAttribute.SQL_DBT_MODELS = RelationField("sqlDbtModels") +DynamoDBAttribute.DBT_TESTS = RelationField("dbtTests") +DynamoDBAttribute.DBT_SOURCES = RelationField("dbtSources") +DynamoDBAttribute.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +DynamoDBAttribute.DBT_METRICS = RelationField("dbtMetrics") +DynamoDBAttribute.DBT_MODEL_COLUMNS = RelationField("dbtModelColumns") +DynamoDBAttribute.COLUMN_DBT_MODEL_COLUMNS = RelationField("columnDbtModelColumns") +DynamoDBAttribute.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +DynamoDBAttribute.DYNAMO_DB_TABLE = RelationField("dynamoDBTable") +DynamoDBAttribute.MEANINGS = RelationField("meanings") +DynamoDBAttribute.MONGO_DB_COLLECTION = RelationField("mongoDBCollection") +DynamoDBAttribute.MC_MONITORS = RelationField("mcMonitors") +DynamoDBAttribute.MC_INCIDENTS = RelationField("mcIncidents") +DynamoDBAttribute.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +DynamoDBAttribute.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +DynamoDBAttribute.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +DynamoDBAttribute.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +DynamoDBAttribute.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +DynamoDBAttribute.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +DynamoDBAttribute.FILES = RelationField("files") +DynamoDBAttribute.LINKS = RelationField("links") +DynamoDBAttribute.README = RelationField("readme") +DynamoDBAttribute.TABLE = RelationField("table") +DynamoDBAttribute.NESTED_COLUMNS = RelationField("nestedColumns") +DynamoDBAttribute.PARENT_COLUMN = RelationField("parentColumn") +DynamoDBAttribute.TABLE_PARTITION = RelationField("tablePartition") +DynamoDBAttribute.VIEW = RelationField("view") +DynamoDBAttribute.CALCULATION_VIEW = RelationField("calculationView") +DynamoDBAttribute.MATERIALISED_VIEW = RelationField("materialisedView") +DynamoDBAttribute.FOREIGN_KEY_TO = RelationField("foreignKeyTo") +DynamoDBAttribute.FOREIGN_KEY_FROM = RelationField("foreignKeyFrom") +DynamoDBAttribute.QUERIES = RelationField("queries") +DynamoDBAttribute.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +DynamoDBAttribute.SNOWFLAKE_DYNAMIC_TABLE = RelationField("snowflakeDynamicTable") +DynamoDBAttribute.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +DynamoDBAttribute.SODA_CHECKS = RelationField("sodaChecks") +DynamoDBAttribute.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +DynamoDBAttribute.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/dynamo_db_related.py b/pyatlan_v9/model/assets/dynamo_db_related.py new file mode 100644 index 000000000..cd953bb54 --- /dev/null +++ b/pyatlan_v9/model/assets/dynamo_db_related.py @@ -0,0 +1,159 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for DynamoDB module. + +This module contains all Related{Type} classes for the DynamoDB type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedNoSQL +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedDynamoDB", + "RelatedDynamoDBAttribute", + "RelatedDynamoDBTable", + "RelatedDynamoDBSecondaryIndex", + "RelatedDynamoDBGlobalSecondaryIndex", + "RelatedDynamoDBLocalSecondaryIndex", +] + + +class RelatedDynamoDB(RelatedNoSQL): + """ + Related entity reference for DynamoDB assets. + + Extends RelatedNoSQL with DynamoDB-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DynamoDB" so it serializes correctly + + dynamo_db_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBStatus" + ) + """Status of the DynamoDB asset.""" + + dynamo_db_partition_key: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBPartitionKey" + ) + """Specifies the partition key of the DynamoDB table or index.""" + + dynamo_db_sort_key: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBSortKey" + ) + """Specifies the sort key of the DynamoDB table or index.""" + + dynamo_db_read_capacity_units: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBReadCapacityUnits" + ) + """The maximum number of strongly consistent reads consumed per second before DynamoDB returns a ThrottlingException.""" + + dynamo_db_write_capacity_units: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBWriteCapacityUnits" + ) + """The maximum number of writes consumed per second before DynamoDB returns a ThrottlingException.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DynamoDB" + + +class RelatedDynamoDBAttribute(RelatedDynamoDB): + """ + Related entity reference for DynamoDBAttribute assets. + + Extends RelatedDynamoDB with DynamoDBAttribute-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DynamoDBAttribute" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DynamoDBAttribute" + + +class RelatedDynamoDBTable(RelatedDynamoDB): + """ + Related entity reference for DynamoDBTable assets. + + Extends RelatedDynamoDB with DynamoDBTable-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DynamoDBTable" so it serializes correctly + + dynamo_dbgsi_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBGSICount" + ) + """Represents the number of global secondary indexes on the table.""" + + dynamo_dblsi_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBLSICount" + ) + """Represents the number of local secondary indexes on the table.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DynamoDBTable" + + +class RelatedDynamoDBSecondaryIndex(RelatedDynamoDB): + """ + Related entity reference for DynamoDBSecondaryIndex assets. + + Extends RelatedDynamoDB with DynamoDBSecondaryIndex-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DynamoDBSecondaryIndex" so it serializes correctly + + dynamo_db_projection_type: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBProjectionType" + ) + """Specifies attributes that are projected from the DynamoDB table into the index.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DynamoDBSecondaryIndex" + + +class RelatedDynamoDBGlobalSecondaryIndex(RelatedDynamoDB): + """ + Related entity reference for DynamoDBGlobalSecondaryIndex assets. + + Extends RelatedDynamoDB with DynamoDBGlobalSecondaryIndex-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DynamoDBGlobalSecondaryIndex" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DynamoDBGlobalSecondaryIndex" + + +class RelatedDynamoDBLocalSecondaryIndex(RelatedDynamoDB): + """ + Related entity reference for DynamoDBLocalSecondaryIndex assets. + + Extends RelatedDynamoDB with DynamoDBLocalSecondaryIndex-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "DynamoDBLocalSecondaryIndex" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "DynamoDBLocalSecondaryIndex" diff --git a/pyatlan_v9/model/assets/dynamo_db_secondary_index.py b/pyatlan_v9/model/assets/dynamo_db_secondary_index.py new file mode 100644 index 000000000..be58d3872 --- /dev/null +++ b/pyatlan_v9/model/assets/dynamo_db_secondary_index.py @@ -0,0 +1,1329 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DynamoDBSecondaryIndex asset model with flattened inheritance. + +This module provides: +- DynamoDBSecondaryIndex: Flat asset class (easy to use) +- DynamoDBSecondaryIndexAttributes: Nested attributes struct (extends AssetAttributes) +- DynamoDBSecondaryIndexNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .snowflake_related import RelatedSnowflakeSemanticLogicalTable +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from .sql_related import ( + RelatedColumn, + RelatedQuery, + RelatedSchema, + RelatedTable, + RelatedTablePartition, +) +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class DynamoDBSecondaryIndex(Asset): + """ + Represents a DynamoDB secondary index asset in Atlan. + """ + + DYNAMO_DB_PROJECTION_TYPE: ClassVar[Any] = None + DYNAMO_DB_STATUS: ClassVar[Any] = None + DYNAMO_DB_PARTITION_KEY: ClassVar[Any] = None + DYNAMO_DB_SORT_KEY: ClassVar[Any] = None + DYNAMO_DB_READ_CAPACITY_UNITS: ClassVar[Any] = None + DYNAMO_DB_WRITE_CAPACITY_UNITS: ClassVar[Any] = None + NO_SQL_SCHEMA_DEFINITION: ClassVar[Any] = None + COLUMN_COUNT: ClassVar[Any] = None + ROW_COUNT: ClassVar[Any] = None + SIZE_BYTES: ClassVar[Any] = None + TABLE_OBJECT_COUNT: ClassVar[Any] = None + ALIAS: ClassVar[Any] = None + IS_TEMPORARY: ClassVar[Any] = None + IS_QUERY_PREVIEW: ClassVar[Any] = None + QUERY_PREVIEW_CONFIG: ClassVar[Any] = None + EXTERNAL_LOCATION: ClassVar[Any] = None + EXTERNAL_LOCATION_REGION: ClassVar[Any] = None + EXTERNAL_LOCATION_FORMAT: ClassVar[Any] = None + IS_PARTITIONED: ClassVar[Any] = None + PARTITION_STRATEGY: ClassVar[Any] = None + PARTITION_COUNT: ClassVar[Any] = None + TABLE_DEFINITION: ClassVar[Any] = None + PARTITION_LIST: ClassVar[Any] = None + IS_SHARDED: ClassVar[Any] = None + TABLE_TYPE: ClassVar[Any] = None + ICEBERG_CATALOG_NAME: ClassVar[Any] = None + ICEBERG_TABLE_TYPE: ClassVar[Any] = None + ICEBERG_CATALOG_SOURCE: ClassVar[Any] = None + ICEBERG_CATALOG_TABLE_NAME: ClassVar[Any] = None + TABLE_IMPALA_PARAMETERS: ClassVar[Any] = None + ICEBERG_CATALOG_TABLE_NAMESPACE: ClassVar[Any] = None + TABLE_EXTERNAL_VOLUME_NAME: ClassVar[Any] = None + ICEBERG_TABLE_BASE_LOCATION: ClassVar[Any] = None + TABLE_RETENTION_TIME: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + COLUMNS: ClassVar[Any] = None + QUERIES: ClassVar[Any] = None + ATLAN_SCHEMA: ClassVar[Any] = None + DIMENSIONS: ClassVar[Any] = None + FACTS: ClassVar[Any] = None + PARTITIONS: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "DynamoDBSecondaryIndex" + + dynamo_db_projection_type: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBProjectionType" + ) + """Specifies attributes that are projected from the DynamoDB table into the index.""" + + dynamo_db_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBStatus" + ) + """Status of the DynamoDB asset.""" + + dynamo_db_partition_key: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBPartitionKey" + ) + """Specifies the partition key of the DynamoDB table or index.""" + + dynamo_db_sort_key: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBSortKey" + ) + """Specifies the sort key of the DynamoDB table or index.""" + + dynamo_db_read_capacity_units: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBReadCapacityUnits" + ) + """The maximum number of strongly consistent reads consumed per second before DynamoDB returns a ThrottlingException.""" + + dynamo_db_write_capacity_units: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBWriteCapacityUnits" + ) + """The maximum number of writes consumed per second before DynamoDB returns a ThrottlingException.""" + + no_sql_schema_definition: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="noSQLSchemaDefinition" + ) + """Represents attributes for describing the key schema for the table and indexes.""" + + column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this table.""" + + row_count: Union[int, None, UnsetType] = UNSET + """Number of rows in this table.""" + + size_bytes: Union[int, None, UnsetType] = UNSET + """Size of this table, in bytes.""" + + table_object_count: Union[int, None, UnsetType] = UNSET + """Number of objects in this table.""" + + alias: Union[str, None, UnsetType] = UNSET + """Alias for this table.""" + + is_temporary: Union[bool, None, UnsetType] = UNSET + """Whether this table is temporary (true) or not (false).""" + + is_query_preview: Union[bool, None, UnsetType] = UNSET + """Whether preview queries are allowed for this table (true) or not (false).""" + + query_preview_config: Union[Dict[str, str], None, UnsetType] = UNSET + """Configuration for preview queries.""" + + external_location: Union[str, None, UnsetType] = UNSET + """External location of this table, for example: an S3 object location.""" + + external_location_region: Union[str, None, UnsetType] = UNSET + """Region of the external location of this table, for example: S3 region.""" + + external_location_format: Union[str, None, UnsetType] = UNSET + """Format of the external location of this table, for example: JSON, CSV, PARQUET, etc.""" + + is_partitioned: Union[bool, None, UnsetType] = UNSET + """Whether this table is partitioned (true) or not (false).""" + + partition_strategy: Union[str, None, UnsetType] = UNSET + """Partition strategy for this table.""" + + partition_count: Union[int, None, UnsetType] = UNSET + """Number of partitions in this table.""" + + table_definition: Union[str, None, UnsetType] = UNSET + """Definition of the table.""" + + partition_list: Union[str, None, UnsetType] = UNSET + """List of partitions in this table.""" + + is_sharded: Union[bool, None, UnsetType] = UNSET + """Whether this table is a sharded table (true) or not (false).""" + + table_type: Union[str, None, UnsetType] = UNSET + """Type of the table.""" + + iceberg_catalog_name: Union[str, None, UnsetType] = UNSET + """Iceberg table catalog name (can be any user defined name)""" + + iceberg_table_type: Union[str, None, UnsetType] = UNSET + """Iceberg table type (managed vs unmanaged)""" + + iceberg_catalog_source: Union[str, None, UnsetType] = UNSET + """Iceberg table catalog type (glue, polaris, snowflake)""" + + iceberg_catalog_table_name: Union[str, None, UnsetType] = UNSET + """Catalog table name (actual table name on the catalog side).""" + + table_impala_parameters: Union[Dict[str, str], None, UnsetType] = UNSET + """Extra attributes for Impala""" + + iceberg_catalog_table_namespace: Union[str, None, UnsetType] = UNSET + """Catalog table namespace (actual database name on the catalog side).""" + + table_external_volume_name: Union[str, None, UnsetType] = UNSET + """External volume name for the table.""" + + iceberg_table_base_location: Union[str, None, UnsetType] = UNSET + """Iceberg table base location inside the external volume.""" + + table_retention_time: Union[int, None, UnsetType] = UNSET + """Data retention time in days.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Columns that exist within this table.""" + + queries: Union[List[RelatedQuery], None, UnsetType] = UNSET + """Queries that access this table.""" + + atlan_schema: Union[RelatedSchema, None, UnsetType] = UNSET + """Schema in which this table exists.""" + + dimensions: Union[List[RelatedTable], None, UnsetType] = UNSET + """""" + + facts: Union[List[RelatedTable], None, UnsetType] = UNSET + """""" + + partitions: Union[List[RelatedTablePartition], None, UnsetType] = UNSET + """Partitions that exist within this table.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "DynamoDBSecondaryIndex" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _dynamo_db_secondary_index_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> DynamoDBSecondaryIndex: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + DynamoDBSecondaryIndex instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _dynamo_db_secondary_index_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DynamoDBSecondaryIndexAttributes(AssetAttributes): + """DynamoDBSecondaryIndex-specific attributes for nested API format.""" + + dynamo_db_projection_type: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBProjectionType" + ) + """Specifies attributes that are projected from the DynamoDB table into the index.""" + + dynamo_db_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBStatus" + ) + """Status of the DynamoDB asset.""" + + dynamo_db_partition_key: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBPartitionKey" + ) + """Specifies the partition key of the DynamoDB table or index.""" + + dynamo_db_sort_key: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBSortKey" + ) + """Specifies the sort key of the DynamoDB table or index.""" + + dynamo_db_read_capacity_units: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBReadCapacityUnits" + ) + """The maximum number of strongly consistent reads consumed per second before DynamoDB returns a ThrottlingException.""" + + dynamo_db_write_capacity_units: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBWriteCapacityUnits" + ) + """The maximum number of writes consumed per second before DynamoDB returns a ThrottlingException.""" + + no_sql_schema_definition: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="noSQLSchemaDefinition" + ) + """Represents attributes for describing the key schema for the table and indexes.""" + + column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this table.""" + + row_count: Union[int, None, UnsetType] = UNSET + """Number of rows in this table.""" + + size_bytes: Union[int, None, UnsetType] = UNSET + """Size of this table, in bytes.""" + + table_object_count: Union[int, None, UnsetType] = UNSET + """Number of objects in this table.""" + + alias: Union[str, None, UnsetType] = UNSET + """Alias for this table.""" + + is_temporary: Union[bool, None, UnsetType] = UNSET + """Whether this table is temporary (true) or not (false).""" + + is_query_preview: Union[bool, None, UnsetType] = UNSET + """Whether preview queries are allowed for this table (true) or not (false).""" + + query_preview_config: Union[Dict[str, str], None, UnsetType] = UNSET + """Configuration for preview queries.""" + + external_location: Union[str, None, UnsetType] = UNSET + """External location of this table, for example: an S3 object location.""" + + external_location_region: Union[str, None, UnsetType] = UNSET + """Region of the external location of this table, for example: S3 region.""" + + external_location_format: Union[str, None, UnsetType] = UNSET + """Format of the external location of this table, for example: JSON, CSV, PARQUET, etc.""" + + is_partitioned: Union[bool, None, UnsetType] = UNSET + """Whether this table is partitioned (true) or not (false).""" + + partition_strategy: Union[str, None, UnsetType] = UNSET + """Partition strategy for this table.""" + + partition_count: Union[int, None, UnsetType] = UNSET + """Number of partitions in this table.""" + + table_definition: Union[str, None, UnsetType] = UNSET + """Definition of the table.""" + + partition_list: Union[str, None, UnsetType] = UNSET + """List of partitions in this table.""" + + is_sharded: Union[bool, None, UnsetType] = UNSET + """Whether this table is a sharded table (true) or not (false).""" + + table_type: Union[str, None, UnsetType] = UNSET + """Type of the table.""" + + iceberg_catalog_name: Union[str, None, UnsetType] = UNSET + """Iceberg table catalog name (can be any user defined name)""" + + iceberg_table_type: Union[str, None, UnsetType] = UNSET + """Iceberg table type (managed vs unmanaged)""" + + iceberg_catalog_source: Union[str, None, UnsetType] = UNSET + """Iceberg table catalog type (glue, polaris, snowflake)""" + + iceberg_catalog_table_name: Union[str, None, UnsetType] = UNSET + """Catalog table name (actual table name on the catalog side).""" + + table_impala_parameters: Union[Dict[str, str], None, UnsetType] = UNSET + """Extra attributes for Impala""" + + iceberg_catalog_table_namespace: Union[str, None, UnsetType] = UNSET + """Catalog table namespace (actual database name on the catalog side).""" + + table_external_volume_name: Union[str, None, UnsetType] = UNSET + """External volume name for the table.""" + + iceberg_table_base_location: Union[str, None, UnsetType] = UNSET + """Iceberg table base location inside the external volume.""" + + table_retention_time: Union[int, None, UnsetType] = UNSET + """Data retention time in days.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + +class DynamoDBSecondaryIndexRelationshipAttributes(AssetRelationshipAttributes): + """DynamoDBSecondaryIndex-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Columns that exist within this table.""" + + queries: Union[List[RelatedQuery], None, UnsetType] = UNSET + """Queries that access this table.""" + + atlan_schema: Union[RelatedSchema, None, UnsetType] = UNSET + """Schema in which this table exists.""" + + dimensions: Union[List[RelatedTable], None, UnsetType] = UNSET + """""" + + facts: Union[List[RelatedTable], None, UnsetType] = UNSET + """""" + + partitions: Union[List[RelatedTablePartition], None, UnsetType] = UNSET + """Partitions that exist within this table.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DynamoDBSecondaryIndexNested(AssetNested): + """DynamoDBSecondaryIndex in nested API format for high-performance serialization.""" + + attributes: Union[DynamoDBSecondaryIndexAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + DynamoDBSecondaryIndexRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + DynamoDBSecondaryIndexRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + DynamoDBSecondaryIndexRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DYNAMO_DB_SECONDARY_INDEX_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "columns", + "queries", + "atlan_schema", + "dimensions", + "facts", + "partitions", + "schema_registry_subjects", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_dynamo_db_secondary_index_attrs( + attrs: DynamoDBSecondaryIndexAttributes, obj: DynamoDBSecondaryIndex +) -> None: + """Populate DynamoDBSecondaryIndex-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.dynamo_db_projection_type = obj.dynamo_db_projection_type + attrs.dynamo_db_status = obj.dynamo_db_status + attrs.dynamo_db_partition_key = obj.dynamo_db_partition_key + attrs.dynamo_db_sort_key = obj.dynamo_db_sort_key + attrs.dynamo_db_read_capacity_units = obj.dynamo_db_read_capacity_units + attrs.dynamo_db_write_capacity_units = obj.dynamo_db_write_capacity_units + attrs.no_sql_schema_definition = obj.no_sql_schema_definition + attrs.column_count = obj.column_count + attrs.row_count = obj.row_count + attrs.size_bytes = obj.size_bytes + attrs.table_object_count = obj.table_object_count + attrs.alias = obj.alias + attrs.is_temporary = obj.is_temporary + attrs.is_query_preview = obj.is_query_preview + attrs.query_preview_config = obj.query_preview_config + attrs.external_location = obj.external_location + attrs.external_location_region = obj.external_location_region + attrs.external_location_format = obj.external_location_format + attrs.is_partitioned = obj.is_partitioned + attrs.partition_strategy = obj.partition_strategy + attrs.partition_count = obj.partition_count + attrs.table_definition = obj.table_definition + attrs.partition_list = obj.partition_list + attrs.is_sharded = obj.is_sharded + attrs.table_type = obj.table_type + attrs.iceberg_catalog_name = obj.iceberg_catalog_name + attrs.iceberg_table_type = obj.iceberg_table_type + attrs.iceberg_catalog_source = obj.iceberg_catalog_source + attrs.iceberg_catalog_table_name = obj.iceberg_catalog_table_name + attrs.table_impala_parameters = obj.table_impala_parameters + attrs.iceberg_catalog_table_namespace = obj.iceberg_catalog_table_namespace + attrs.table_external_volume_name = obj.table_external_volume_name + attrs.iceberg_table_base_location = obj.iceberg_table_base_location + attrs.table_retention_time = obj.table_retention_time + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + + +def _extract_dynamo_db_secondary_index_attrs( + attrs: DynamoDBSecondaryIndexAttributes, +) -> dict: + """Extract all DynamoDBSecondaryIndex attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["dynamo_db_projection_type"] = attrs.dynamo_db_projection_type + result["dynamo_db_status"] = attrs.dynamo_db_status + result["dynamo_db_partition_key"] = attrs.dynamo_db_partition_key + result["dynamo_db_sort_key"] = attrs.dynamo_db_sort_key + result["dynamo_db_read_capacity_units"] = attrs.dynamo_db_read_capacity_units + result["dynamo_db_write_capacity_units"] = attrs.dynamo_db_write_capacity_units + result["no_sql_schema_definition"] = attrs.no_sql_schema_definition + result["column_count"] = attrs.column_count + result["row_count"] = attrs.row_count + result["size_bytes"] = attrs.size_bytes + result["table_object_count"] = attrs.table_object_count + result["alias"] = attrs.alias + result["is_temporary"] = attrs.is_temporary + result["is_query_preview"] = attrs.is_query_preview + result["query_preview_config"] = attrs.query_preview_config + result["external_location"] = attrs.external_location + result["external_location_region"] = attrs.external_location_region + result["external_location_format"] = attrs.external_location_format + result["is_partitioned"] = attrs.is_partitioned + result["partition_strategy"] = attrs.partition_strategy + result["partition_count"] = attrs.partition_count + result["table_definition"] = attrs.table_definition + result["partition_list"] = attrs.partition_list + result["is_sharded"] = attrs.is_sharded + result["table_type"] = attrs.table_type + result["iceberg_catalog_name"] = attrs.iceberg_catalog_name + result["iceberg_table_type"] = attrs.iceberg_table_type + result["iceberg_catalog_source"] = attrs.iceberg_catalog_source + result["iceberg_catalog_table_name"] = attrs.iceberg_catalog_table_name + result["table_impala_parameters"] = attrs.table_impala_parameters + result["iceberg_catalog_table_namespace"] = attrs.iceberg_catalog_table_namespace + result["table_external_volume_name"] = attrs.table_external_volume_name + result["iceberg_table_base_location"] = attrs.iceberg_table_base_location + result["table_retention_time"] = attrs.table_retention_time + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _dynamo_db_secondary_index_to_nested( + dynamo_db_secondary_index: DynamoDBSecondaryIndex, +) -> DynamoDBSecondaryIndexNested: + """Convert flat DynamoDBSecondaryIndex to nested format.""" + attrs = DynamoDBSecondaryIndexAttributes() + _populate_dynamo_db_secondary_index_attrs(attrs, dynamo_db_secondary_index) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + dynamo_db_secondary_index, + _DYNAMO_DB_SECONDARY_INDEX_REL_FIELDS, + DynamoDBSecondaryIndexRelationshipAttributes, + ) + return DynamoDBSecondaryIndexNested( + guid=dynamo_db_secondary_index.guid, + type_name=dynamo_db_secondary_index.type_name, + status=dynamo_db_secondary_index.status, + version=dynamo_db_secondary_index.version, + create_time=dynamo_db_secondary_index.create_time, + update_time=dynamo_db_secondary_index.update_time, + created_by=dynamo_db_secondary_index.created_by, + updated_by=dynamo_db_secondary_index.updated_by, + classifications=dynamo_db_secondary_index.classifications, + classification_names=dynamo_db_secondary_index.classification_names, + meanings=dynamo_db_secondary_index.meanings, + labels=dynamo_db_secondary_index.labels, + business_attributes=dynamo_db_secondary_index.business_attributes, + custom_attributes=dynamo_db_secondary_index.custom_attributes, + pending_tasks=dynamo_db_secondary_index.pending_tasks, + proxy=dynamo_db_secondary_index.proxy, + is_incomplete=dynamo_db_secondary_index.is_incomplete, + provenance_type=dynamo_db_secondary_index.provenance_type, + home_id=dynamo_db_secondary_index.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _dynamo_db_secondary_index_from_nested( + nested: DynamoDBSecondaryIndexNested, +) -> DynamoDBSecondaryIndex: + """Convert nested format to flat DynamoDBSecondaryIndex.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else DynamoDBSecondaryIndexAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DYNAMO_DB_SECONDARY_INDEX_REL_FIELDS, + DynamoDBSecondaryIndexRelationshipAttributes, + ) + return DynamoDBSecondaryIndex( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_dynamo_db_secondary_index_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _dynamo_db_secondary_index_to_nested_bytes( + dynamo_db_secondary_index: DynamoDBSecondaryIndex, serde: Serde +) -> bytes: + """Convert flat DynamoDBSecondaryIndex to nested JSON bytes.""" + return serde.encode(_dynamo_db_secondary_index_to_nested(dynamo_db_secondary_index)) + + +def _dynamo_db_secondary_index_from_nested_bytes( + data: bytes, serde: Serde +) -> DynamoDBSecondaryIndex: + """Convert nested JSON bytes to flat DynamoDBSecondaryIndex.""" + nested = serde.decode(data, DynamoDBSecondaryIndexNested) + return _dynamo_db_secondary_index_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, +) + +DynamoDBSecondaryIndex.DYNAMO_DB_PROJECTION_TYPE = KeywordField( + "dynamoDBProjectionType", "dynamoDBProjectionType" +) +DynamoDBSecondaryIndex.DYNAMO_DB_STATUS = KeywordField( + "dynamoDBStatus", "dynamoDBStatus" +) +DynamoDBSecondaryIndex.DYNAMO_DB_PARTITION_KEY = KeywordField( + "dynamoDBPartitionKey", "dynamoDBPartitionKey" +) +DynamoDBSecondaryIndex.DYNAMO_DB_SORT_KEY = KeywordField( + "dynamoDBSortKey", "dynamoDBSortKey" +) +DynamoDBSecondaryIndex.DYNAMO_DB_READ_CAPACITY_UNITS = NumericField( + "dynamoDBReadCapacityUnits", "dynamoDBReadCapacityUnits" +) +DynamoDBSecondaryIndex.DYNAMO_DB_WRITE_CAPACITY_UNITS = NumericField( + "dynamoDBWriteCapacityUnits", "dynamoDBWriteCapacityUnits" +) +DynamoDBSecondaryIndex.NO_SQL_SCHEMA_DEFINITION = KeywordField( + "noSQLSchemaDefinition", "noSQLSchemaDefinition" +) +DynamoDBSecondaryIndex.COLUMN_COUNT = NumericField("columnCount", "columnCount") +DynamoDBSecondaryIndex.ROW_COUNT = NumericField("rowCount", "rowCount") +DynamoDBSecondaryIndex.SIZE_BYTES = NumericField("sizeBytes", "sizeBytes") +DynamoDBSecondaryIndex.TABLE_OBJECT_COUNT = NumericField( + "tableObjectCount", "tableObjectCount" +) +DynamoDBSecondaryIndex.ALIAS = KeywordField("alias", "alias") +DynamoDBSecondaryIndex.IS_TEMPORARY = BooleanField("isTemporary", "isTemporary") +DynamoDBSecondaryIndex.IS_QUERY_PREVIEW = BooleanField( + "isQueryPreview", "isQueryPreview" +) +DynamoDBSecondaryIndex.QUERY_PREVIEW_CONFIG = KeywordField( + "queryPreviewConfig", "queryPreviewConfig" +) +DynamoDBSecondaryIndex.EXTERNAL_LOCATION = KeywordField( + "externalLocation", "externalLocation" +) +DynamoDBSecondaryIndex.EXTERNAL_LOCATION_REGION = KeywordField( + "externalLocationRegion", "externalLocationRegion" +) +DynamoDBSecondaryIndex.EXTERNAL_LOCATION_FORMAT = KeywordField( + "externalLocationFormat", "externalLocationFormat" +) +DynamoDBSecondaryIndex.IS_PARTITIONED = BooleanField("isPartitioned", "isPartitioned") +DynamoDBSecondaryIndex.PARTITION_STRATEGY = KeywordField( + "partitionStrategy", "partitionStrategy" +) +DynamoDBSecondaryIndex.PARTITION_COUNT = NumericField( + "partitionCount", "partitionCount" +) +DynamoDBSecondaryIndex.TABLE_DEFINITION = KeywordField( + "tableDefinition", "tableDefinition" +) +DynamoDBSecondaryIndex.PARTITION_LIST = KeywordField("partitionList", "partitionList") +DynamoDBSecondaryIndex.IS_SHARDED = BooleanField("isSharded", "isSharded") +DynamoDBSecondaryIndex.TABLE_TYPE = KeywordField("tableType", "tableType") +DynamoDBSecondaryIndex.ICEBERG_CATALOG_NAME = KeywordField( + "icebergCatalogName", "icebergCatalogName" +) +DynamoDBSecondaryIndex.ICEBERG_TABLE_TYPE = KeywordField( + "icebergTableType", "icebergTableType" +) +DynamoDBSecondaryIndex.ICEBERG_CATALOG_SOURCE = KeywordField( + "icebergCatalogSource", "icebergCatalogSource" +) +DynamoDBSecondaryIndex.ICEBERG_CATALOG_TABLE_NAME = KeywordField( + "icebergCatalogTableName", "icebergCatalogTableName" +) +DynamoDBSecondaryIndex.TABLE_IMPALA_PARAMETERS = KeywordField( + "tableImpalaParameters", "tableImpalaParameters" +) +DynamoDBSecondaryIndex.ICEBERG_CATALOG_TABLE_NAMESPACE = KeywordField( + "icebergCatalogTableNamespace", "icebergCatalogTableNamespace" +) +DynamoDBSecondaryIndex.TABLE_EXTERNAL_VOLUME_NAME = KeywordField( + "tableExternalVolumeName", "tableExternalVolumeName" +) +DynamoDBSecondaryIndex.ICEBERG_TABLE_BASE_LOCATION = KeywordField( + "icebergTableBaseLocation", "icebergTableBaseLocation" +) +DynamoDBSecondaryIndex.TABLE_RETENTION_TIME = NumericField( + "tableRetentionTime", "tableRetentionTime" +) +DynamoDBSecondaryIndex.QUERY_COUNT = NumericField("queryCount", "queryCount") +DynamoDBSecondaryIndex.QUERY_USER_COUNT = NumericField( + "queryUserCount", "queryUserCount" +) +DynamoDBSecondaryIndex.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +DynamoDBSecondaryIndex.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +DynamoDBSecondaryIndex.DATABASE_NAME = KeywordField("databaseName", "databaseName") +DynamoDBSecondaryIndex.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +DynamoDBSecondaryIndex.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +DynamoDBSecondaryIndex.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +DynamoDBSecondaryIndex.TABLE_NAME = KeywordField("tableName", "tableName") +DynamoDBSecondaryIndex.TABLE_QUALIFIED_NAME = KeywordField( + "tableQualifiedName", "tableQualifiedName" +) +DynamoDBSecondaryIndex.VIEW_NAME = KeywordField("viewName", "viewName") +DynamoDBSecondaryIndex.VIEW_QUALIFIED_NAME = KeywordField( + "viewQualifiedName", "viewQualifiedName" +) +DynamoDBSecondaryIndex.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +DynamoDBSecondaryIndex.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +DynamoDBSecondaryIndex.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +DynamoDBSecondaryIndex.LAST_PROFILED_AT = NumericField( + "lastProfiledAt", "lastProfiledAt" +) +DynamoDBSecondaryIndex.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +DynamoDBSecondaryIndex.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +DynamoDBSecondaryIndex.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +DynamoDBSecondaryIndex.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +DynamoDBSecondaryIndex.ANOMALO_CHECKS = RelationField("anomaloChecks") +DynamoDBSecondaryIndex.APPLICATION = RelationField("application") +DynamoDBSecondaryIndex.APPLICATION_FIELD = RelationField("applicationField") +DynamoDBSecondaryIndex.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +DynamoDBSecondaryIndex.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +DynamoDBSecondaryIndex.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +DynamoDBSecondaryIndex.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +DynamoDBSecondaryIndex.METRICS = RelationField("metrics") +DynamoDBSecondaryIndex.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +DynamoDBSecondaryIndex.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +DynamoDBSecondaryIndex.DBT_MODELS = RelationField("dbtModels") +DynamoDBSecondaryIndex.SQL_DBT_MODELS = RelationField("sqlDbtModels") +DynamoDBSecondaryIndex.DBT_TESTS = RelationField("dbtTests") +DynamoDBSecondaryIndex.DBT_SOURCES = RelationField("dbtSources") +DynamoDBSecondaryIndex.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +DynamoDBSecondaryIndex.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +DynamoDBSecondaryIndex.MEANINGS = RelationField("meanings") +DynamoDBSecondaryIndex.MC_MONITORS = RelationField("mcMonitors") +DynamoDBSecondaryIndex.MC_INCIDENTS = RelationField("mcIncidents") +DynamoDBSecondaryIndex.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +DynamoDBSecondaryIndex.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +DynamoDBSecondaryIndex.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +DynamoDBSecondaryIndex.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +DynamoDBSecondaryIndex.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +DynamoDBSecondaryIndex.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +DynamoDBSecondaryIndex.FILES = RelationField("files") +DynamoDBSecondaryIndex.LINKS = RelationField("links") +DynamoDBSecondaryIndex.README = RelationField("readme") +DynamoDBSecondaryIndex.COLUMNS = RelationField("columns") +DynamoDBSecondaryIndex.QUERIES = RelationField("queries") +DynamoDBSecondaryIndex.ATLAN_SCHEMA = RelationField("atlanSchema") +DynamoDBSecondaryIndex.DIMENSIONS = RelationField("dimensions") +DynamoDBSecondaryIndex.FACTS = RelationField("facts") +DynamoDBSecondaryIndex.PARTITIONS = RelationField("partitions") +DynamoDBSecondaryIndex.SCHEMA_REGISTRY_SUBJECTS = RelationField( + "schemaRegistrySubjects" +) +DynamoDBSecondaryIndex.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +DynamoDBSecondaryIndex.SODA_CHECKS = RelationField("sodaChecks") +DynamoDBSecondaryIndex.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +DynamoDBSecondaryIndex.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/dynamo_db_table.py b/pyatlan_v9/model/assets/dynamo_db_table.py new file mode 100644 index 000000000..24e1d1696 --- /dev/null +++ b/pyatlan_v9/model/assets/dynamo_db_table.py @@ -0,0 +1,1346 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +DynamoDBTable asset model with flattened inheritance. + +This module provides: +- DynamoDBTable: Flat asset class (easy to use) +- DynamoDBTableAttributes: Nested attributes struct (extends AssetAttributes) +- DynamoDBTableNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .snowflake_related import RelatedSnowflakeSemanticLogicalTable +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from .sql_related import ( + RelatedColumn, + RelatedQuery, + RelatedSchema, + RelatedTable, + RelatedTablePartition, +) +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .dynamo_db_related import ( + RelatedDynamoDBAttribute, + RelatedDynamoDBGlobalSecondaryIndex, + RelatedDynamoDBLocalSecondaryIndex, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class DynamoDBTable(Asset): + """ + Represents a DynamoDB table asset in Atlan. + """ + + DYNAMO_DBGSI_COUNT: ClassVar[Any] = None + DYNAMO_DBLSI_COUNT: ClassVar[Any] = None + DYNAMO_DB_STATUS: ClassVar[Any] = None + DYNAMO_DB_PARTITION_KEY: ClassVar[Any] = None + DYNAMO_DB_SORT_KEY: ClassVar[Any] = None + DYNAMO_DB_READ_CAPACITY_UNITS: ClassVar[Any] = None + DYNAMO_DB_WRITE_CAPACITY_UNITS: ClassVar[Any] = None + NO_SQL_SCHEMA_DEFINITION: ClassVar[Any] = None + COLUMN_COUNT: ClassVar[Any] = None + ROW_COUNT: ClassVar[Any] = None + SIZE_BYTES: ClassVar[Any] = None + TABLE_OBJECT_COUNT: ClassVar[Any] = None + ALIAS: ClassVar[Any] = None + IS_TEMPORARY: ClassVar[Any] = None + IS_QUERY_PREVIEW: ClassVar[Any] = None + QUERY_PREVIEW_CONFIG: ClassVar[Any] = None + EXTERNAL_LOCATION: ClassVar[Any] = None + EXTERNAL_LOCATION_REGION: ClassVar[Any] = None + EXTERNAL_LOCATION_FORMAT: ClassVar[Any] = None + IS_PARTITIONED: ClassVar[Any] = None + PARTITION_STRATEGY: ClassVar[Any] = None + PARTITION_COUNT: ClassVar[Any] = None + TABLE_DEFINITION: ClassVar[Any] = None + PARTITION_LIST: ClassVar[Any] = None + IS_SHARDED: ClassVar[Any] = None + TABLE_TYPE: ClassVar[Any] = None + ICEBERG_CATALOG_NAME: ClassVar[Any] = None + ICEBERG_TABLE_TYPE: ClassVar[Any] = None + ICEBERG_CATALOG_SOURCE: ClassVar[Any] = None + ICEBERG_CATALOG_TABLE_NAME: ClassVar[Any] = None + TABLE_IMPALA_PARAMETERS: ClassVar[Any] = None + ICEBERG_CATALOG_TABLE_NAMESPACE: ClassVar[Any] = None + TABLE_EXTERNAL_VOLUME_NAME: ClassVar[Any] = None + ICEBERG_TABLE_BASE_LOCATION: ClassVar[Any] = None + TABLE_RETENTION_TIME: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + DYNAMO_DB_GLOBAL_SECONDARY_INDEXES: ClassVar[Any] = None + DYNAMO_DB_LOCAL_SECONDARY_INDEXES: ClassVar[Any] = None + DYNAMO_DB_COLUMNS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + COLUMNS: ClassVar[Any] = None + QUERIES: ClassVar[Any] = None + ATLAN_SCHEMA: ClassVar[Any] = None + DIMENSIONS: ClassVar[Any] = None + FACTS: ClassVar[Any] = None + PARTITIONS: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "DynamoDBTable" + + dynamo_dbgsi_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBGSICount" + ) + """Represents the number of global secondary indexes on the table.""" + + dynamo_dblsi_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBLSICount" + ) + """Represents the number of local secondary indexes on the table.""" + + dynamo_db_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBStatus" + ) + """Status of the DynamoDB asset.""" + + dynamo_db_partition_key: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBPartitionKey" + ) + """Specifies the partition key of the DynamoDB table or index.""" + + dynamo_db_sort_key: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBSortKey" + ) + """Specifies the sort key of the DynamoDB table or index.""" + + dynamo_db_read_capacity_units: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBReadCapacityUnits" + ) + """The maximum number of strongly consistent reads consumed per second before DynamoDB returns a ThrottlingException.""" + + dynamo_db_write_capacity_units: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBWriteCapacityUnits" + ) + """The maximum number of writes consumed per second before DynamoDB returns a ThrottlingException.""" + + no_sql_schema_definition: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="noSQLSchemaDefinition" + ) + """Represents attributes for describing the key schema for the table and indexes.""" + + column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this table.""" + + row_count: Union[int, None, UnsetType] = UNSET + """Number of rows in this table.""" + + size_bytes: Union[int, None, UnsetType] = UNSET + """Size of this table, in bytes.""" + + table_object_count: Union[int, None, UnsetType] = UNSET + """Number of objects in this table.""" + + alias: Union[str, None, UnsetType] = UNSET + """Alias for this table.""" + + is_temporary: Union[bool, None, UnsetType] = UNSET + """Whether this table is temporary (true) or not (false).""" + + is_query_preview: Union[bool, None, UnsetType] = UNSET + """Whether preview queries are allowed for this table (true) or not (false).""" + + query_preview_config: Union[Dict[str, str], None, UnsetType] = UNSET + """Configuration for preview queries.""" + + external_location: Union[str, None, UnsetType] = UNSET + """External location of this table, for example: an S3 object location.""" + + external_location_region: Union[str, None, UnsetType] = UNSET + """Region of the external location of this table, for example: S3 region.""" + + external_location_format: Union[str, None, UnsetType] = UNSET + """Format of the external location of this table, for example: JSON, CSV, PARQUET, etc.""" + + is_partitioned: Union[bool, None, UnsetType] = UNSET + """Whether this table is partitioned (true) or not (false).""" + + partition_strategy: Union[str, None, UnsetType] = UNSET + """Partition strategy for this table.""" + + partition_count: Union[int, None, UnsetType] = UNSET + """Number of partitions in this table.""" + + table_definition: Union[str, None, UnsetType] = UNSET + """Definition of the table.""" + + partition_list: Union[str, None, UnsetType] = UNSET + """List of partitions in this table.""" + + is_sharded: Union[bool, None, UnsetType] = UNSET + """Whether this table is a sharded table (true) or not (false).""" + + table_type: Union[str, None, UnsetType] = UNSET + """Type of the table.""" + + iceberg_catalog_name: Union[str, None, UnsetType] = UNSET + """Iceberg table catalog name (can be any user defined name)""" + + iceberg_table_type: Union[str, None, UnsetType] = UNSET + """Iceberg table type (managed vs unmanaged)""" + + iceberg_catalog_source: Union[str, None, UnsetType] = UNSET + """Iceberg table catalog type (glue, polaris, snowflake)""" + + iceberg_catalog_table_name: Union[str, None, UnsetType] = UNSET + """Catalog table name (actual table name on the catalog side).""" + + table_impala_parameters: Union[Dict[str, str], None, UnsetType] = UNSET + """Extra attributes for Impala""" + + iceberg_catalog_table_namespace: Union[str, None, UnsetType] = UNSET + """Catalog table namespace (actual database name on the catalog side).""" + + table_external_volume_name: Union[str, None, UnsetType] = UNSET + """External volume name for the table.""" + + iceberg_table_base_location: Union[str, None, UnsetType] = UNSET + """Iceberg table base location inside the external volume.""" + + table_retention_time: Union[int, None, UnsetType] = UNSET + """Data retention time in days.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + dynamo_db_global_secondary_indexes: Union[ + List[RelatedDynamoDBGlobalSecondaryIndex], None, UnsetType + ] = msgspec.field(default=UNSET, name="dynamoDBGlobalSecondaryIndexes") + """DynamoDB table containing global secondary indexes.""" + + dynamo_db_local_secondary_indexes: Union[ + List[RelatedDynamoDBLocalSecondaryIndex], None, UnsetType + ] = msgspec.field(default=UNSET, name="dynamoDBLocalSecondaryIndexes") + """DynamoDB table containing local secondary indexes.""" + + dynamo_db_columns: Union[List[RelatedDynamoDBAttribute], None, UnsetType] = ( + msgspec.field(default=UNSET, name="dynamoDBColumns") + ) + """Columns (attributes) that exist within this DynamoDB table.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Columns that exist within this table.""" + + queries: Union[List[RelatedQuery], None, UnsetType] = UNSET + """Queries that access this table.""" + + atlan_schema: Union[RelatedSchema, None, UnsetType] = UNSET + """Schema in which this table exists.""" + + dimensions: Union[List[RelatedTable], None, UnsetType] = UNSET + """""" + + facts: Union[List[RelatedTable], None, UnsetType] = UNSET + """""" + + partitions: Union[List[RelatedTablePartition], None, UnsetType] = UNSET + """Partitions that exist within this table.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "DynamoDBTable" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _dynamo_db_table_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> DynamoDBTable: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + DynamoDBTable instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _dynamo_db_table_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class DynamoDBTableAttributes(AssetAttributes): + """DynamoDBTable-specific attributes for nested API format.""" + + dynamo_dbgsi_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBGSICount" + ) + """Represents the number of global secondary indexes on the table.""" + + dynamo_dblsi_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBLSICount" + ) + """Represents the number of local secondary indexes on the table.""" + + dynamo_db_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBStatus" + ) + """Status of the DynamoDB asset.""" + + dynamo_db_partition_key: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBPartitionKey" + ) + """Specifies the partition key of the DynamoDB table or index.""" + + dynamo_db_sort_key: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBSortKey" + ) + """Specifies the sort key of the DynamoDB table or index.""" + + dynamo_db_read_capacity_units: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBReadCapacityUnits" + ) + """The maximum number of strongly consistent reads consumed per second before DynamoDB returns a ThrottlingException.""" + + dynamo_db_write_capacity_units: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="dynamoDBWriteCapacityUnits" + ) + """The maximum number of writes consumed per second before DynamoDB returns a ThrottlingException.""" + + no_sql_schema_definition: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="noSQLSchemaDefinition" + ) + """Represents attributes for describing the key schema for the table and indexes.""" + + column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this table.""" + + row_count: Union[int, None, UnsetType] = UNSET + """Number of rows in this table.""" + + size_bytes: Union[int, None, UnsetType] = UNSET + """Size of this table, in bytes.""" + + table_object_count: Union[int, None, UnsetType] = UNSET + """Number of objects in this table.""" + + alias: Union[str, None, UnsetType] = UNSET + """Alias for this table.""" + + is_temporary: Union[bool, None, UnsetType] = UNSET + """Whether this table is temporary (true) or not (false).""" + + is_query_preview: Union[bool, None, UnsetType] = UNSET + """Whether preview queries are allowed for this table (true) or not (false).""" + + query_preview_config: Union[Dict[str, str], None, UnsetType] = UNSET + """Configuration for preview queries.""" + + external_location: Union[str, None, UnsetType] = UNSET + """External location of this table, for example: an S3 object location.""" + + external_location_region: Union[str, None, UnsetType] = UNSET + """Region of the external location of this table, for example: S3 region.""" + + external_location_format: Union[str, None, UnsetType] = UNSET + """Format of the external location of this table, for example: JSON, CSV, PARQUET, etc.""" + + is_partitioned: Union[bool, None, UnsetType] = UNSET + """Whether this table is partitioned (true) or not (false).""" + + partition_strategy: Union[str, None, UnsetType] = UNSET + """Partition strategy for this table.""" + + partition_count: Union[int, None, UnsetType] = UNSET + """Number of partitions in this table.""" + + table_definition: Union[str, None, UnsetType] = UNSET + """Definition of the table.""" + + partition_list: Union[str, None, UnsetType] = UNSET + """List of partitions in this table.""" + + is_sharded: Union[bool, None, UnsetType] = UNSET + """Whether this table is a sharded table (true) or not (false).""" + + table_type: Union[str, None, UnsetType] = UNSET + """Type of the table.""" + + iceberg_catalog_name: Union[str, None, UnsetType] = UNSET + """Iceberg table catalog name (can be any user defined name)""" + + iceberg_table_type: Union[str, None, UnsetType] = UNSET + """Iceberg table type (managed vs unmanaged)""" + + iceberg_catalog_source: Union[str, None, UnsetType] = UNSET + """Iceberg table catalog type (glue, polaris, snowflake)""" + + iceberg_catalog_table_name: Union[str, None, UnsetType] = UNSET + """Catalog table name (actual table name on the catalog side).""" + + table_impala_parameters: Union[Dict[str, str], None, UnsetType] = UNSET + """Extra attributes for Impala""" + + iceberg_catalog_table_namespace: Union[str, None, UnsetType] = UNSET + """Catalog table namespace (actual database name on the catalog side).""" + + table_external_volume_name: Union[str, None, UnsetType] = UNSET + """External volume name for the table.""" + + iceberg_table_base_location: Union[str, None, UnsetType] = UNSET + """Iceberg table base location inside the external volume.""" + + table_retention_time: Union[int, None, UnsetType] = UNSET + """Data retention time in days.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + +class DynamoDBTableRelationshipAttributes(AssetRelationshipAttributes): + """DynamoDBTable-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + dynamo_db_global_secondary_indexes: Union[ + List[RelatedDynamoDBGlobalSecondaryIndex], None, UnsetType + ] = msgspec.field(default=UNSET, name="dynamoDBGlobalSecondaryIndexes") + """DynamoDB table containing global secondary indexes.""" + + dynamo_db_local_secondary_indexes: Union[ + List[RelatedDynamoDBLocalSecondaryIndex], None, UnsetType + ] = msgspec.field(default=UNSET, name="dynamoDBLocalSecondaryIndexes") + """DynamoDB table containing local secondary indexes.""" + + dynamo_db_columns: Union[List[RelatedDynamoDBAttribute], None, UnsetType] = ( + msgspec.field(default=UNSET, name="dynamoDBColumns") + ) + """Columns (attributes) that exist within this DynamoDB table.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Columns that exist within this table.""" + + queries: Union[List[RelatedQuery], None, UnsetType] = UNSET + """Queries that access this table.""" + + atlan_schema: Union[RelatedSchema, None, UnsetType] = UNSET + """Schema in which this table exists.""" + + dimensions: Union[List[RelatedTable], None, UnsetType] = UNSET + """""" + + facts: Union[List[RelatedTable], None, UnsetType] = UNSET + """""" + + partitions: Union[List[RelatedTablePartition], None, UnsetType] = UNSET + """Partitions that exist within this table.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class DynamoDBTableNested(AssetNested): + """DynamoDBTable in nested API format for high-performance serialization.""" + + attributes: Union[DynamoDBTableAttributes, UnsetType] = UNSET + relationship_attributes: Union[DynamoDBTableRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + DynamoDBTableRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + DynamoDBTableRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_DYNAMO_DB_TABLE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "dynamo_db_global_secondary_indexes", + "dynamo_db_local_secondary_indexes", + "dynamo_db_columns", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "columns", + "queries", + "atlan_schema", + "dimensions", + "facts", + "partitions", + "schema_registry_subjects", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_dynamo_db_table_attrs( + attrs: DynamoDBTableAttributes, obj: DynamoDBTable +) -> None: + """Populate DynamoDBTable-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.dynamo_dbgsi_count = obj.dynamo_dbgsi_count + attrs.dynamo_dblsi_count = obj.dynamo_dblsi_count + attrs.dynamo_db_status = obj.dynamo_db_status + attrs.dynamo_db_partition_key = obj.dynamo_db_partition_key + attrs.dynamo_db_sort_key = obj.dynamo_db_sort_key + attrs.dynamo_db_read_capacity_units = obj.dynamo_db_read_capacity_units + attrs.dynamo_db_write_capacity_units = obj.dynamo_db_write_capacity_units + attrs.no_sql_schema_definition = obj.no_sql_schema_definition + attrs.column_count = obj.column_count + attrs.row_count = obj.row_count + attrs.size_bytes = obj.size_bytes + attrs.table_object_count = obj.table_object_count + attrs.alias = obj.alias + attrs.is_temporary = obj.is_temporary + attrs.is_query_preview = obj.is_query_preview + attrs.query_preview_config = obj.query_preview_config + attrs.external_location = obj.external_location + attrs.external_location_region = obj.external_location_region + attrs.external_location_format = obj.external_location_format + attrs.is_partitioned = obj.is_partitioned + attrs.partition_strategy = obj.partition_strategy + attrs.partition_count = obj.partition_count + attrs.table_definition = obj.table_definition + attrs.partition_list = obj.partition_list + attrs.is_sharded = obj.is_sharded + attrs.table_type = obj.table_type + attrs.iceberg_catalog_name = obj.iceberg_catalog_name + attrs.iceberg_table_type = obj.iceberg_table_type + attrs.iceberg_catalog_source = obj.iceberg_catalog_source + attrs.iceberg_catalog_table_name = obj.iceberg_catalog_table_name + attrs.table_impala_parameters = obj.table_impala_parameters + attrs.iceberg_catalog_table_namespace = obj.iceberg_catalog_table_namespace + attrs.table_external_volume_name = obj.table_external_volume_name + attrs.iceberg_table_base_location = obj.iceberg_table_base_location + attrs.table_retention_time = obj.table_retention_time + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + + +def _extract_dynamo_db_table_attrs(attrs: DynamoDBTableAttributes) -> dict: + """Extract all DynamoDBTable attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["dynamo_dbgsi_count"] = attrs.dynamo_dbgsi_count + result["dynamo_dblsi_count"] = attrs.dynamo_dblsi_count + result["dynamo_db_status"] = attrs.dynamo_db_status + result["dynamo_db_partition_key"] = attrs.dynamo_db_partition_key + result["dynamo_db_sort_key"] = attrs.dynamo_db_sort_key + result["dynamo_db_read_capacity_units"] = attrs.dynamo_db_read_capacity_units + result["dynamo_db_write_capacity_units"] = attrs.dynamo_db_write_capacity_units + result["no_sql_schema_definition"] = attrs.no_sql_schema_definition + result["column_count"] = attrs.column_count + result["row_count"] = attrs.row_count + result["size_bytes"] = attrs.size_bytes + result["table_object_count"] = attrs.table_object_count + result["alias"] = attrs.alias + result["is_temporary"] = attrs.is_temporary + result["is_query_preview"] = attrs.is_query_preview + result["query_preview_config"] = attrs.query_preview_config + result["external_location"] = attrs.external_location + result["external_location_region"] = attrs.external_location_region + result["external_location_format"] = attrs.external_location_format + result["is_partitioned"] = attrs.is_partitioned + result["partition_strategy"] = attrs.partition_strategy + result["partition_count"] = attrs.partition_count + result["table_definition"] = attrs.table_definition + result["partition_list"] = attrs.partition_list + result["is_sharded"] = attrs.is_sharded + result["table_type"] = attrs.table_type + result["iceberg_catalog_name"] = attrs.iceberg_catalog_name + result["iceberg_table_type"] = attrs.iceberg_table_type + result["iceberg_catalog_source"] = attrs.iceberg_catalog_source + result["iceberg_catalog_table_name"] = attrs.iceberg_catalog_table_name + result["table_impala_parameters"] = attrs.table_impala_parameters + result["iceberg_catalog_table_namespace"] = attrs.iceberg_catalog_table_namespace + result["table_external_volume_name"] = attrs.table_external_volume_name + result["iceberg_table_base_location"] = attrs.iceberg_table_base_location + result["table_retention_time"] = attrs.table_retention_time + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _dynamo_db_table_to_nested(dynamo_db_table: DynamoDBTable) -> DynamoDBTableNested: + """Convert flat DynamoDBTable to nested format.""" + attrs = DynamoDBTableAttributes() + _populate_dynamo_db_table_attrs(attrs, dynamo_db_table) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + dynamo_db_table, + _DYNAMO_DB_TABLE_REL_FIELDS, + DynamoDBTableRelationshipAttributes, + ) + return DynamoDBTableNested( + guid=dynamo_db_table.guid, + type_name=dynamo_db_table.type_name, + status=dynamo_db_table.status, + version=dynamo_db_table.version, + create_time=dynamo_db_table.create_time, + update_time=dynamo_db_table.update_time, + created_by=dynamo_db_table.created_by, + updated_by=dynamo_db_table.updated_by, + classifications=dynamo_db_table.classifications, + classification_names=dynamo_db_table.classification_names, + meanings=dynamo_db_table.meanings, + labels=dynamo_db_table.labels, + business_attributes=dynamo_db_table.business_attributes, + custom_attributes=dynamo_db_table.custom_attributes, + pending_tasks=dynamo_db_table.pending_tasks, + proxy=dynamo_db_table.proxy, + is_incomplete=dynamo_db_table.is_incomplete, + provenance_type=dynamo_db_table.provenance_type, + home_id=dynamo_db_table.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _dynamo_db_table_from_nested(nested: DynamoDBTableNested) -> DynamoDBTable: + """Convert nested format to flat DynamoDBTable.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else DynamoDBTableAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _DYNAMO_DB_TABLE_REL_FIELDS, + DynamoDBTableRelationshipAttributes, + ) + return DynamoDBTable( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_dynamo_db_table_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _dynamo_db_table_to_nested_bytes( + dynamo_db_table: DynamoDBTable, serde: Serde +) -> bytes: + """Convert flat DynamoDBTable to nested JSON bytes.""" + return serde.encode(_dynamo_db_table_to_nested(dynamo_db_table)) + + +def _dynamo_db_table_from_nested_bytes(data: bytes, serde: Serde) -> DynamoDBTable: + """Convert nested JSON bytes to flat DynamoDBTable.""" + nested = serde.decode(data, DynamoDBTableNested) + return _dynamo_db_table_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, +) + +DynamoDBTable.DYNAMO_DBGSI_COUNT = NumericField("dynamoDBGSICount", "dynamoDBGSICount") +DynamoDBTable.DYNAMO_DBLSI_COUNT = NumericField("dynamoDBLSICount", "dynamoDBLSICount") +DynamoDBTable.DYNAMO_DB_STATUS = KeywordField("dynamoDBStatus", "dynamoDBStatus") +DynamoDBTable.DYNAMO_DB_PARTITION_KEY = KeywordField( + "dynamoDBPartitionKey", "dynamoDBPartitionKey" +) +DynamoDBTable.DYNAMO_DB_SORT_KEY = KeywordField("dynamoDBSortKey", "dynamoDBSortKey") +DynamoDBTable.DYNAMO_DB_READ_CAPACITY_UNITS = NumericField( + "dynamoDBReadCapacityUnits", "dynamoDBReadCapacityUnits" +) +DynamoDBTable.DYNAMO_DB_WRITE_CAPACITY_UNITS = NumericField( + "dynamoDBWriteCapacityUnits", "dynamoDBWriteCapacityUnits" +) +DynamoDBTable.NO_SQL_SCHEMA_DEFINITION = KeywordField( + "noSQLSchemaDefinition", "noSQLSchemaDefinition" +) +DynamoDBTable.COLUMN_COUNT = NumericField("columnCount", "columnCount") +DynamoDBTable.ROW_COUNT = NumericField("rowCount", "rowCount") +DynamoDBTable.SIZE_BYTES = NumericField("sizeBytes", "sizeBytes") +DynamoDBTable.TABLE_OBJECT_COUNT = NumericField("tableObjectCount", "tableObjectCount") +DynamoDBTable.ALIAS = KeywordField("alias", "alias") +DynamoDBTable.IS_TEMPORARY = BooleanField("isTemporary", "isTemporary") +DynamoDBTable.IS_QUERY_PREVIEW = BooleanField("isQueryPreview", "isQueryPreview") +DynamoDBTable.QUERY_PREVIEW_CONFIG = KeywordField( + "queryPreviewConfig", "queryPreviewConfig" +) +DynamoDBTable.EXTERNAL_LOCATION = KeywordField("externalLocation", "externalLocation") +DynamoDBTable.EXTERNAL_LOCATION_REGION = KeywordField( + "externalLocationRegion", "externalLocationRegion" +) +DynamoDBTable.EXTERNAL_LOCATION_FORMAT = KeywordField( + "externalLocationFormat", "externalLocationFormat" +) +DynamoDBTable.IS_PARTITIONED = BooleanField("isPartitioned", "isPartitioned") +DynamoDBTable.PARTITION_STRATEGY = KeywordField( + "partitionStrategy", "partitionStrategy" +) +DynamoDBTable.PARTITION_COUNT = NumericField("partitionCount", "partitionCount") +DynamoDBTable.TABLE_DEFINITION = KeywordField("tableDefinition", "tableDefinition") +DynamoDBTable.PARTITION_LIST = KeywordField("partitionList", "partitionList") +DynamoDBTable.IS_SHARDED = BooleanField("isSharded", "isSharded") +DynamoDBTable.TABLE_TYPE = KeywordField("tableType", "tableType") +DynamoDBTable.ICEBERG_CATALOG_NAME = KeywordField( + "icebergCatalogName", "icebergCatalogName" +) +DynamoDBTable.ICEBERG_TABLE_TYPE = KeywordField("icebergTableType", "icebergTableType") +DynamoDBTable.ICEBERG_CATALOG_SOURCE = KeywordField( + "icebergCatalogSource", "icebergCatalogSource" +) +DynamoDBTable.ICEBERG_CATALOG_TABLE_NAME = KeywordField( + "icebergCatalogTableName", "icebergCatalogTableName" +) +DynamoDBTable.TABLE_IMPALA_PARAMETERS = KeywordField( + "tableImpalaParameters", "tableImpalaParameters" +) +DynamoDBTable.ICEBERG_CATALOG_TABLE_NAMESPACE = KeywordField( + "icebergCatalogTableNamespace", "icebergCatalogTableNamespace" +) +DynamoDBTable.TABLE_EXTERNAL_VOLUME_NAME = KeywordField( + "tableExternalVolumeName", "tableExternalVolumeName" +) +DynamoDBTable.ICEBERG_TABLE_BASE_LOCATION = KeywordField( + "icebergTableBaseLocation", "icebergTableBaseLocation" +) +DynamoDBTable.TABLE_RETENTION_TIME = NumericField( + "tableRetentionTime", "tableRetentionTime" +) +DynamoDBTable.QUERY_COUNT = NumericField("queryCount", "queryCount") +DynamoDBTable.QUERY_USER_COUNT = NumericField("queryUserCount", "queryUserCount") +DynamoDBTable.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +DynamoDBTable.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +DynamoDBTable.DATABASE_NAME = KeywordField("databaseName", "databaseName") +DynamoDBTable.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +DynamoDBTable.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +DynamoDBTable.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +DynamoDBTable.TABLE_NAME = KeywordField("tableName", "tableName") +DynamoDBTable.TABLE_QUALIFIED_NAME = KeywordField( + "tableQualifiedName", "tableQualifiedName" +) +DynamoDBTable.VIEW_NAME = KeywordField("viewName", "viewName") +DynamoDBTable.VIEW_QUALIFIED_NAME = KeywordField( + "viewQualifiedName", "viewQualifiedName" +) +DynamoDBTable.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +DynamoDBTable.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +DynamoDBTable.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +DynamoDBTable.LAST_PROFILED_AT = NumericField("lastProfiledAt", "lastProfiledAt") +DynamoDBTable.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +DynamoDBTable.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +DynamoDBTable.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +DynamoDBTable.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +DynamoDBTable.ANOMALO_CHECKS = RelationField("anomaloChecks") +DynamoDBTable.APPLICATION = RelationField("application") +DynamoDBTable.APPLICATION_FIELD = RelationField("applicationField") +DynamoDBTable.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +DynamoDBTable.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +DynamoDBTable.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +DynamoDBTable.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +DynamoDBTable.METRICS = RelationField("metrics") +DynamoDBTable.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +DynamoDBTable.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +DynamoDBTable.DBT_MODELS = RelationField("dbtModels") +DynamoDBTable.SQL_DBT_MODELS = RelationField("sqlDbtModels") +DynamoDBTable.DBT_TESTS = RelationField("dbtTests") +DynamoDBTable.DBT_SOURCES = RelationField("dbtSources") +DynamoDBTable.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +DynamoDBTable.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +DynamoDBTable.DYNAMO_DB_GLOBAL_SECONDARY_INDEXES = RelationField( + "dynamoDBGlobalSecondaryIndexes" +) +DynamoDBTable.DYNAMO_DB_LOCAL_SECONDARY_INDEXES = RelationField( + "dynamoDBLocalSecondaryIndexes" +) +DynamoDBTable.DYNAMO_DB_COLUMNS = RelationField("dynamoDBColumns") +DynamoDBTable.MEANINGS = RelationField("meanings") +DynamoDBTable.MC_MONITORS = RelationField("mcMonitors") +DynamoDBTable.MC_INCIDENTS = RelationField("mcIncidents") +DynamoDBTable.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +DynamoDBTable.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +DynamoDBTable.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +DynamoDBTable.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +DynamoDBTable.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +DynamoDBTable.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +DynamoDBTable.FILES = RelationField("files") +DynamoDBTable.LINKS = RelationField("links") +DynamoDBTable.README = RelationField("readme") +DynamoDBTable.COLUMNS = RelationField("columns") +DynamoDBTable.QUERIES = RelationField("queries") +DynamoDBTable.ATLAN_SCHEMA = RelationField("atlanSchema") +DynamoDBTable.DIMENSIONS = RelationField("dimensions") +DynamoDBTable.FACTS = RelationField("facts") +DynamoDBTable.PARTITIONS = RelationField("partitions") +DynamoDBTable.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +DynamoDBTable.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +DynamoDBTable.SODA_CHECKS = RelationField("sodaChecks") +DynamoDBTable.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +DynamoDBTable.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/entity.py b/pyatlan_v9/model/assets/entity.py new file mode 100644 index 000000000..7ab0fa732 --- /dev/null +++ b/pyatlan_v9/model/assets/entity.py @@ -0,0 +1,667 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Entity base class with all Apache Atlas top-level entity properties. + +This module provides: +- AtlasClassification: Classification/tag assigned to an entity +- TermAssignment: Glossary term assignment (AtlasTermAssignmentHeader) +- Entity: Base class for all Atlas entities with top-level properties +""" + +from __future__ import annotations + +from typing import Any, Union + +import msgspec +import msgspec.structs +from msgspec import UNSET, UnsetType + +from pyatlan_v9.model.assets.related_entity import SaveSemantic + +_metadata_proxies: dict[Any, Any] = {} + + +class AtlasClassification( + msgspec.Struct, kw_only=True, omit_defaults=True, rename="camel" +): + """ + Atlas classification/tag assigned to an entity. + + Classifications are tags that can be assigned to entities with optional + propagation settings and validity periods. + """ + + type_name: Any = UNSET + """The name of the classification type (str or AtlanTagName).""" + + entity_guid: Union[str, UnsetType] = UNSET + """The GUID of the entity this classification is assigned to.""" + + entity_status: Union[str, UnsetType] = UNSET + """The status of the entity (ACTIVE, DELETED).""" + + propagate: Union[bool, UnsetType] = UNSET + """Whether this classification propagates to related entities.""" + + remove_propagations_on_entity_delete: Union[bool, UnsetType] = UNSET + """Whether to remove propagated classifications when the entity is deleted.""" + + restrict_propagation_through_lineage: Union[bool, UnsetType] = UNSET + """Whether to avoid propagating through lineage (True) or do propagate through lineage (False).""" + + restrict_propagation_through_hierarchy: Union[bool, UnsetType] = UNSET + """Whether to prevent this classification from propagating through hierarchy (True) or allow it (False).""" + + validity_periods: Union[list[dict[str, Any]], UnsetType] = UNSET + """Time periods during which this classification is valid.""" + + source_tag_attachments: Union[list[Any], UnsetType] = UNSET + """Source tag attachments for this classification.""" + + attributes: Union[dict[str, Any], UnsetType] = UNSET + """Custom attributes for this classification.""" + + +class TermAssignment(msgspec.Struct, kw_only=True, omit_defaults=True, rename="camel"): + """ + Glossary term assignment to an entity (AtlasTermAssignmentHeader). + + Represents the association between an entity and a glossary term, + including metadata about the assignment. + """ + + term_guid: Union[str, UnsetType] = UNSET + """The GUID of the assigned glossary term.""" + + guid_raw: Union[str, UnsetType] = msgspec.field(default=UNSET, name="guid") + """Raw GUID field used by some Atlas responses for term assignments.""" + + relation_guid: Union[str, UnsetType] = UNSET + """The GUID of the relationship between entity and term.""" + + description: Union[str, UnsetType] = UNSET + """Description of this term assignment.""" + + display_text: Union[str, UnsetType] = UNSET + """Display text for the assigned term.""" + + expression: Union[str, UnsetType] = UNSET + """Expression associated with this assignment.""" + + created_by: Union[str, UnsetType] = UNSET + """User who created this assignment.""" + + steward: Union[str, UnsetType] = UNSET + """Data steward responsible for this assignment.""" + + source: Union[str, UnsetType] = UNSET + """Source of this assignment.""" + + confidence: Union[int, UnsetType] = UNSET + """Confidence score for this assignment (0-100).""" + + status: Union[str, UnsetType] = UNSET + """Status of the assignment (DISCOVERED, PROPOSED, IMPORTED, VALIDATED, DEPRECATED, OBSOLETE, OTHER).""" + + @property + def guid(self) -> Union[str, None]: + """Backward-compatible alias for term_guid.""" + if self.term_guid is not UNSET: + return self.term_guid + if self.guid_raw is not UNSET: + return self.guid_raw + return None + + @guid.setter + def guid(self, value: Union[str, None]) -> None: + self.term_guid = UNSET if value is None else value + + +class Entity(msgspec.Struct, kw_only=True, omit_defaults=True, rename="camel"): + """ + Base class for all Atlas entities with top-level properties. + + This class contains all Apache Atlas entity properties that exist + outside the 'attributes' object in the API response, such as: + - Core identity (guid, typeName, status, version) + - Timestamps (createTime, updateTime, createdBy, updatedBy) + - Classifications and meanings + - Additional metadata (labels, businessAttributes, etc.) + + All concrete entity types (Referenceable, Asset, Table, etc.) ultimately + extend from this base class. + """ + + # Core identity + guid: Union[str, UnsetType] = UNSET + """Globally unique identifier for this entity.""" + + type_name: Union[str, UnsetType] = UNSET + """The type name of this entity.""" + + super_type_names: Union[list[str], UnsetType] = UNSET + """List of all supertype names in the type hierarchy (e.g., ["Referenceable", "Asset"]).""" + + doc_id: Union[str, UnsetType] = UNSET + """Document identifier used for search indexing.""" + + status: Union[str, UnsetType] = UNSET + """Entity status (ACTIVE, DELETED, PURGED).""" + + version: Union[int, UnsetType] = UNSET + """Version number of this entity.""" + + # Timestamps + create_time: Union[int, UnsetType] = UNSET + """Timestamp when this entity was created (epoch milliseconds).""" + + update_time: Union[int, UnsetType] = UNSET + """Timestamp when this entity was last updated (epoch milliseconds).""" + + created_by: Union[str, UnsetType] = UNSET + """Username of the user who created this entity.""" + + updated_by: Union[str, UnsetType] = UNSET + """Username of the user who last updated this entity.""" + + # Classifications - typed for better validation + classifications: Union[list[AtlasClassification], UnsetType] = UNSET + """Classifications (tags) assigned to this entity.""" + + add_or_update_classifications: Union[list[Any], UnsetType] = UNSET + """Classifications to add or update on this entity (used during save).""" + + remove_classifications: Union[list[Any], UnsetType] = UNSET + """Classifications to remove from this entity (used during save).""" + + classification_names: Union[list, None, UnsetType] = UNSET + """Simple list of classification type names assigned to this entity.""" + + # Meanings - typed for better validation + meanings: Union[list[TermAssignment], None, UnsetType] = UNSET + """Glossary term assignments for this entity.""" + + # Labels + labels: Union[list[str], None, UnsetType] = UNSET + """Simple string labels attached to this entity.""" + + # Additional metadata + business_attributes: Union[dict[str, Any], UnsetType] = UNSET + """Business metadata attributes for this entity.""" + + custom_attributes: Union[dict[str, str], UnsetType] = UNSET + """Custom key-value pairs for this entity.""" + + pending_tasks: Union[list[str], None, UnsetType] = UNSET + """Identifiers of pending tasks for this entity.""" + + proxy: Union[bool, UnsetType] = UNSET + """Whether this is a proxy entity.""" + + is_incomplete: Union[bool, UnsetType] = UNSET + """Whether this entity has incomplete data.""" + + provenance_type: Union[int, UnsetType] = UNSET + """Provenance type identifier for this entity.""" + + delete_handler: Union[str, None, UnsetType] = UNSET + """Details on the handler used for deletion of the asset.""" + + home_id: Union[str, UnsetType] = UNSET + """Home identifier for distributed Atlas systems.""" + + semantic: Union[SaveSemantic, None, UnsetType] = UNSET + """Save semantic for relationship operations (REPLACE, APPEND, REMOVE). + Not serialized to JSON — used internally to control how relationship + attributes are categorized during bulk save operations.""" + + # ========================================================================= + # Compatibility Methods (legacy API surface) + # ========================================================================= + + @property + def atlan_tags(self): + """User-friendly alias for classifications.""" + val = self.classifications + return None if val is UNSET else val + + @atlan_tags.setter + def atlan_tags(self, value): + msgspec.Struct.__setattr__(self, "classifications", value) + + @property + def attributes(self): + """ + Backward-compatible accessor that returns self. + + In legacy Pydantic models, attributes were nested inside an + `attributes` object. v9 uses flat fields, so this property + returns self to allow legacy-style access like + `asset.attributes.qualified_name`. + """ + return self + + def validate_required(self): + """ + Validate that required fields are present. + + Subclasses should override this to add specific validation. + """ + pass + + def __getitem__(self, key): + """ + Support dict-like access for backward compatibility. + + Allows `asset["qualifiedName"]` style access. + """ + if not isinstance(key, str): + raise KeyError(key) + # Try camelCase to snake_case conversion + import re + + snake_key = re.sub(r"(? str: + """ + Map legacy field names to v9 field names. + + Legacy Pydantic models used different snake_case conventions for + some fields (e.g., asset_d_q_schedule_time_zone vs asset_dq_schedule_time_zone, + data_product_assets_d_s_l vs data_product_assets_dsl). + This collapses consecutive single-letter segments into groups. + """ + parts = name.split("_") + result: list[str] = [] + i = 0 + while i < len(parts): + if len(parts[i]) == 1 and parts[i].isalpha(): + group = parts[i] + while ( + i + 1 < len(parts) + and len(parts[i + 1]) == 1 + and parts[i + 1].isalpha() + ): + i += 1 + group += parts[i] + result.append(group) + else: + result.append(parts[i]) + i += 1 + collapsed = "_".join(result) + return collapsed if collapsed != name else name + + @property + def _metadata_proxy(self): + return _metadata_proxies.get(id(self)) + + @_metadata_proxy.setter + def _metadata_proxy(self, value): + _metadata_proxies[id(self)] = value + + @property + def _async_metadata_proxy(self): + return _metadata_proxies.get(("async", id(self))) + + @_async_metadata_proxy.setter + def _async_metadata_proxy(self, value): + _metadata_proxies[("async", id(self))] = value + + def __getattr__(self, name: str): + """Fallback for legacy field name compatibility.""" + if name.startswith("_"): + raise AttributeError(name) + + collapsed = self._collapse_legacy_name(name) + if collapsed != name: + try: + return object.__getattribute__(self, collapsed) + except AttributeError: + pass + + raise AttributeError( + f"'{type(self).__name__}' object has no attribute '{name}'" + ) + + def __setattr__(self, name: str, value): + """Support legacy field name writes.""" + collapsed = self._collapse_legacy_name(name) + if collapsed != name and collapsed in self.__struct_fields__: + msgspec.Struct.__setattr__(self, collapsed, value) + else: + msgspec.Struct.__setattr__(self, name, value) + + # ========================================================================= + # Custom Metadata Methods (required by legacy save pipeline) + # ========================================================================= + + def flush_custom_metadata(self, client: Any = None) -> None: + """Flush custom metadata proxy to business_attributes.""" + if self._metadata_proxy: + msgspec.Struct.__setattr__( + self, "business_attributes", self._metadata_proxy.business_attributes + ) + + async def flush_custom_metadata_async(self, client: Any = None) -> None: + """Async version of flush_custom_metadata.""" + if self._async_metadata_proxy: + ba = await self._async_metadata_proxy.business_attributes() + msgspec.Struct.__setattr__(self, "business_attributes", ba) + + def _get_business_attributes_or_none(self) -> Any: + ba = self.business_attributes + return None if ba is UNSET else ba + + def get_custom_metadata(self, client: Any = None, name: str = "") -> Any: + """Get custom metadata by name from the proxy.""" + from pyatlan_v9.model.custom_metadata import CustomMetadataProxy + + if not self._metadata_proxy: + self._metadata_proxy = CustomMetadataProxy( + business_attributes=self._get_business_attributes_or_none(), + client=client, + ) + return self._metadata_proxy.get_custom_metadata(name) + + def set_custom_metadata( + self, client: Any = None, custom_metadata: Any = None + ) -> None: + """Set custom metadata via the proxy.""" + from pyatlan_v9.model.custom_metadata import CustomMetadataProxy + + if not self._metadata_proxy: + self._metadata_proxy = CustomMetadataProxy( + business_attributes=self._get_business_attributes_or_none(), + client=client, + ) + self._metadata_proxy.set_custom_metadata(custom_metadata) + + async def get_custom_metadata_async( + self, client: Any = None, name: str = "" + ) -> Any: + """Async version of get_custom_metadata.""" + from pyatlan_v9.model.aio.custom_metadata import AsyncCustomMetadataProxy + + if not self._async_metadata_proxy: + self._async_metadata_proxy = AsyncCustomMetadataProxy( + business_attributes=self._get_business_attributes_or_none(), + client=client, + ) + return await self._async_metadata_proxy.get_custom_metadata(name) + + async def set_custom_metadata_async( + self, client: Any = None, custom_metadata: Any = None + ) -> None: + """Async version of set_custom_metadata.""" + from pyatlan_v9.model.aio.custom_metadata import AsyncCustomMetadataProxy + + if not self._async_metadata_proxy: + self._async_metadata_proxy = AsyncCustomMetadataProxy( + business_attributes=self._get_business_attributes_or_none(), + client=client, + ) + await self._async_metadata_proxy.set_custom_metadata(custom_metadata) + + # ========================================================================= + # Generic Nested Serialization + # ========================================================================= + + def to_nested_dict(self) -> dict: + """ + Convert this entity to the Atlas API nested format using a generic + approach that works for ALL entity types. + + Splits flat fields into top-level entity fields, attributes, and + relationship attributes (bucketed by save semantic). + """ + + flat = msgspec.to_builtins(self, enc_hook=_enc_hook) + + # Dynamically discover relationship fields from the generated + # XRelationshipAttributes class in the same module. + rel_field_names = _get_relationship_fields(type(self)) + + # Build a map from camelCase field names to original Entity objects + # so we can restore typeName that omit_defaults may have dropped. + _rel_originals: dict[str, Any] = {} + for f in msgspec.structs.fields(type(self)): + camel = f.encode_name + if camel in rel_field_names: + val = getattr(self, f.name, UNSET) + if val is not UNSET and val is not None: + _rel_originals[camel] = val + + top_level: dict[str, Any] = {} + attributes: dict[str, Any] = {} + rel_replace: dict[str, Any] = {} + rel_append: dict[str, Any] = {} + rel_remove: dict[str, Any] = {} + + for key, value in flat.items(): + if key in _ENTITY_TOP_LEVEL_FIELDS: + if key == "semantic": + continue + top_level[key] = value + elif key in rel_field_names: + _ensure_type_name(key, value, _rel_originals) + _bucket_relationship(key, value, rel_replace, rel_append, rel_remove) + else: + attributes[key] = value + + # Fields that live in Attributes but represent entity references + # (e.g. parentCategory, anchor) must ALSO appear in + # relationshipAttributes when explicitly set, so the Atlas API + # processes relationship changes. + _mirror_ref_fields_to_rels(type(self), attributes, rel_replace) + + # Always include typeName — omit_defaults may have dropped it + if "typeName" not in top_level and self.type_name is not UNSET: + top_level["typeName"] = self.type_name + + result = dict(top_level) + if attributes: + result["attributes"] = attributes + if rel_replace: + result["relationshipAttributes"] = rel_replace + if rel_append: + result["appendRelationshipAttributes"] = rel_append + if rel_remove: + result["removeRelationshipAttributes"] = rel_remove + return result + + +def _enc_hook(obj: Any) -> Any: + """Fallback encoder for non-msgspec types used in v9 entities. + + Handles v9-native types that msgspec doesn't know about (AtlanTagName). + """ + import datetime + + from pyatlan_v9.model.core import AtlanTagName + + if type(obj) is AtlanTagName: + return str(obj) + if isinstance(obj, datetime.date): + # Convert date to timestamp in milliseconds (epoch time) + dt = datetime.datetime.combine(obj, datetime.time.min) + return int(dt.timestamp() * 1000) + if isinstance(obj, datetime.datetime): + return int(obj.timestamp() * 1000) + if hasattr(obj, "dict") and hasattr(obj, "__fields__"): + return obj.dict(by_alias=True, exclude_none=True) + raise TypeError(f"Encoding objects of type {type(obj).__name__} is unsupported") + + +_ENTITY_TOP_LEVEL_FIELDS = frozenset( + f.encode_name for f in msgspec.structs.fields(Entity) +) + +_ref_field_cache: dict[type, frozenset[str]] = {} + + +def _get_struct_ref_fields(cls: type) -> frozenset[str]: + """Return camelCase names of fields whose type includes a Struct subclass. + + These are attribute-level fields that hold references to other entities + (e.g. ``parent_category``, ``anchor``) and need to be mirrored into + ``relationshipAttributes`` for the Atlas API to process them. + """ + if cls in _ref_field_cache: + return _ref_field_cache[cls] + + import typing + + ref_names: set[str] = set() + for f in msgspec.structs.fields(cls): + if f.encode_name in _ENTITY_TOP_LEVEL_FIELDS: + continue + args = typing.get_args(f.type) + for arg in args: + if isinstance(arg, type) and issubclass(arg, msgspec.Struct): + ref_names.add(f.encode_name) + break + result = frozenset(ref_names) + _ref_field_cache[cls] = result + return result + + +def _mirror_ref_fields_to_rels( + cls: type, + attributes: dict[str, Any], + rel_replace: dict[str, Any], +) -> None: + """Copy entity-reference fields from attributes into rel_replace. + + The Atlas API requires relationship-like fields (parentCategory, anchor, + etc.) to appear in ``relationshipAttributes`` for mutations to take + effect. The v9 model places some of these in ``Attributes`` rather + than ``RelationshipAttributes``, so we mirror them here. + """ + ref_fields = _get_struct_ref_fields(cls) + for key in ref_fields: + if key in attributes and key not in rel_replace: + rel_replace[key] = attributes[key] + + +_rel_fields_cache: dict[type, frozenset[str]] = {} + + +def _get_relationship_fields(cls: type) -> frozenset[str]: + """ + Get the set of camelCase relationship field names for an entity type. + + Looks up the generated ``XRelationshipAttributes`` class in the same + module and caches the result. + """ + if cls in _rel_fields_cache: + return _rel_fields_cache[cls] + + import sys + + rel_names: set[str] = set() + # Walk the MRO to collect relationship fields from all parent types + for klass in cls.__mro__: + mod = sys.modules.get(klass.__module__) + if mod is None: + continue + rel_cls_name = klass.__name__ + "RelationshipAttributes" + rel_cls = getattr(mod, rel_cls_name, None) + if rel_cls is not None and hasattr(rel_cls, "__struct_fields__"): + rel_names.update(f.encode_name for f in msgspec.structs.fields(rel_cls)) + + result = frozenset(rel_names) + _rel_fields_cache[cls] = result + return result + + +def _strip_semantic(item: dict) -> dict: + """Remove the internal 'semantic' key from a relationship dict.""" + item.pop("semantic", None) + return item + + +def _fixup_ref(d: dict, original: Any) -> None: + """Fix a serialized reference dict to match Atlas API expectations. + + - Restores typeName that omit_defaults may have dropped + - Wraps qualifiedName in uniqueAttributes for ref_by_qualified_name + """ + if not isinstance(d, dict): + return + if ( + "typeName" not in d + and isinstance(original, Entity) + and original.type_name is not UNSET + ): + d["typeName"] = original.type_name + qn = d.pop("qualifiedName", None) + if qn is not None and "guid" not in d: + d["uniqueAttributes"] = {"qualifiedName": qn} + elif qn is not None: + d.setdefault("uniqueAttributes", {})["qualifiedName"] = qn + + +def _ensure_type_name(key: str, value: Any, originals: dict[str, Any]) -> None: + """Ensure serialized relationship dicts contain typeName and proper structure.""" + original = originals.get(key) + if original is None: + return + + if isinstance(value, dict): + _fixup_ref(value, original) + elif isinstance(value, list) and isinstance(original, list): + for i, item in enumerate(value): + if isinstance(item, dict) and i < len(original): + _fixup_ref(item, original[i]) + + +def _bucket_relationship( + key: str, + value: Any, + replace: dict, + append: dict, + remove: dict, +) -> None: + """Sort a relationship value into replace/append/remove buckets.""" + if value is None: + replace[key] = None + return + if isinstance(value, dict): + semantic = value.get("semantic") + cleaned = _strip_semantic(value) + if semantic == "APPEND": + append[key] = cleaned + elif semantic == "REMOVE": + remove[key] = cleaned + else: + replace[key] = cleaned + elif isinstance(value, list): + if len(value) == 0: + replace[key] = [] + return + rep, app, rem = [], [], [] + for item in value: + semantic = item.get("semantic") if isinstance(item, dict) else None + if isinstance(item, dict): + _strip_semantic(item) + if semantic == "APPEND": + app.append(item) + elif semantic == "REMOVE": + rem.append(item) + else: + rep.append(item) + if rep: + replace[key] = rep + if app: + append[key] = app + if rem: + remove[key] = rem diff --git a/pyatlan_v9/model/assets/event_store.py b/pyatlan_v9/model/assets/event_store.py new file mode 100644 index 000000000..fb47ca2b2 --- /dev/null +++ b/pyatlan_v9/model/assets/event_store.py @@ -0,0 +1,525 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +EventStore asset model with flattened inheritance. + +This module provides: +- EventStore: Flat asset class (easy to use) +- EventStoreAttributes: Nested attributes struct (extends AssetAttributes) +- EventStoreNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class EventStore(Asset): + """ + Base class for event store assets. + """ + + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "EventStore" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "EventStore" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _event_store_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> EventStore: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + EventStore instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _event_store_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class EventStoreAttributes(AssetAttributes): + """EventStore-specific attributes for nested API format.""" + + pass + + +class EventStoreRelationshipAttributes(AssetRelationshipAttributes): + """EventStore-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class EventStoreNested(AssetNested): + """EventStore in nested API format for high-performance serialization.""" + + attributes: Union[EventStoreAttributes, UnsetType] = UNSET + relationship_attributes: Union[EventStoreRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + EventStoreRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + EventStoreRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_EVENT_STORE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_event_store_attrs(attrs: EventStoreAttributes, obj: EventStore) -> None: + """Populate EventStore-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + + +def _extract_event_store_attrs(attrs: EventStoreAttributes) -> dict: + """Extract all EventStore attributes from the attrs struct into a flat dict.""" + return _extract_asset_attrs(attrs) + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _event_store_to_nested(event_store: EventStore) -> EventStoreNested: + """Convert flat EventStore to nested format.""" + attrs = EventStoreAttributes() + _populate_event_store_attrs(attrs, event_store) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + event_store, _EVENT_STORE_REL_FIELDS, EventStoreRelationshipAttributes + ) + return EventStoreNested( + guid=event_store.guid, + type_name=event_store.type_name, + status=event_store.status, + version=event_store.version, + create_time=event_store.create_time, + update_time=event_store.update_time, + created_by=event_store.created_by, + updated_by=event_store.updated_by, + classifications=event_store.classifications, + classification_names=event_store.classification_names, + meanings=event_store.meanings, + labels=event_store.labels, + business_attributes=event_store.business_attributes, + custom_attributes=event_store.custom_attributes, + pending_tasks=event_store.pending_tasks, + proxy=event_store.proxy, + is_incomplete=event_store.is_incomplete, + provenance_type=event_store.provenance_type, + home_id=event_store.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _event_store_from_nested(nested: EventStoreNested) -> EventStore: + """Convert nested format to flat EventStore.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else EventStoreAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _EVENT_STORE_REL_FIELDS, + EventStoreRelationshipAttributes, + ) + return EventStore( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_event_store_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _event_store_to_nested_bytes(event_store: EventStore, serde: Serde) -> bytes: + """Convert flat EventStore to nested JSON bytes.""" + return serde.encode(_event_store_to_nested(event_store)) + + +def _event_store_from_nested_bytes(data: bytes, serde: Serde) -> EventStore: + """Convert nested JSON bytes to flat EventStore.""" + nested = serde.decode(data, EventStoreNested) + return _event_store_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import RelationField # noqa: E402 + +EventStore.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +EventStore.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +EventStore.ANOMALO_CHECKS = RelationField("anomaloChecks") +EventStore.APPLICATION = RelationField("application") +EventStore.APPLICATION_FIELD = RelationField("applicationField") +EventStore.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +EventStore.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +EventStore.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +EventStore.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +EventStore.METRICS = RelationField("metrics") +EventStore.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +EventStore.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +EventStore.MEANINGS = RelationField("meanings") +EventStore.MC_MONITORS = RelationField("mcMonitors") +EventStore.MC_INCIDENTS = RelationField("mcIncidents") +EventStore.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +EventStore.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +EventStore.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +EventStore.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +EventStore.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +EventStore.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +EventStore.FILES = RelationField("files") +EventStore.LINKS = RelationField("links") +EventStore.README = RelationField("readme") +EventStore.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +EventStore.SODA_CHECKS = RelationField("sodaChecks") +EventStore.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +EventStore.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/fabric.py b/pyatlan_v9/model/assets/fabric.py new file mode 100644 index 000000000..6c1b2242c --- /dev/null +++ b/pyatlan_v9/model/assets/fabric.py @@ -0,0 +1,556 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Fabric asset model with flattened inheritance. + +This module provides: +- Fabric: Flat asset class (easy to use) +- FabricAttributes: Nested attributes struct (extends AssetAttributes) +- FabricNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Fabric(Asset): + """ + Base class for all Fabric types. + """ + + FABRIC_COLUMN_COUNT: ClassVar[Any] = None + FABRIC_DATA_TYPE: ClassVar[Any] = None + FABRIC_ORDINAL: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Fabric" + + fabric_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this asset.""" + + fabric_data_type: Union[str, None, UnsetType] = UNSET + """Data type of this asset.""" + + fabric_ordinal: Union[int, None, UnsetType] = UNSET + """Order/position of this asset within its parent.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Fabric" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _fabric_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Fabric: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Fabric instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _fabric_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class FabricAttributes(AssetAttributes): + """Fabric-specific attributes for nested API format.""" + + fabric_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this asset.""" + + fabric_data_type: Union[str, None, UnsetType] = UNSET + """Data type of this asset.""" + + fabric_ordinal: Union[int, None, UnsetType] = UNSET + """Order/position of this asset within its parent.""" + + +class FabricRelationshipAttributes(AssetRelationshipAttributes): + """Fabric-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class FabricNested(AssetNested): + """Fabric in nested API format for high-performance serialization.""" + + attributes: Union[FabricAttributes, UnsetType] = UNSET + relationship_attributes: Union[FabricRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[FabricRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[FabricRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_FABRIC_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_fabric_attrs(attrs: FabricAttributes, obj: Fabric) -> None: + """Populate Fabric-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.fabric_column_count = obj.fabric_column_count + attrs.fabric_data_type = obj.fabric_data_type + attrs.fabric_ordinal = obj.fabric_ordinal + + +def _extract_fabric_attrs(attrs: FabricAttributes) -> dict: + """Extract all Fabric attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["fabric_column_count"] = attrs.fabric_column_count + result["fabric_data_type"] = attrs.fabric_data_type + result["fabric_ordinal"] = attrs.fabric_ordinal + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _fabric_to_nested(fabric: Fabric) -> FabricNested: + """Convert flat Fabric to nested format.""" + attrs = FabricAttributes() + _populate_fabric_attrs(attrs, fabric) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + fabric, _FABRIC_REL_FIELDS, FabricRelationshipAttributes + ) + return FabricNested( + guid=fabric.guid, + type_name=fabric.type_name, + status=fabric.status, + version=fabric.version, + create_time=fabric.create_time, + update_time=fabric.update_time, + created_by=fabric.created_by, + updated_by=fabric.updated_by, + classifications=fabric.classifications, + classification_names=fabric.classification_names, + meanings=fabric.meanings, + labels=fabric.labels, + business_attributes=fabric.business_attributes, + custom_attributes=fabric.custom_attributes, + pending_tasks=fabric.pending_tasks, + proxy=fabric.proxy, + is_incomplete=fabric.is_incomplete, + provenance_type=fabric.provenance_type, + home_id=fabric.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _fabric_from_nested(nested: FabricNested) -> Fabric: + """Convert nested format to flat Fabric.""" + attrs = nested.attributes if nested.attributes is not UNSET else FabricAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _FABRIC_REL_FIELDS, + FabricRelationshipAttributes, + ) + return Fabric( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_fabric_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _fabric_to_nested_bytes(fabric: Fabric, serde: Serde) -> bytes: + """Convert flat Fabric to nested JSON bytes.""" + return serde.encode(_fabric_to_nested(fabric)) + + +def _fabric_from_nested_bytes(data: bytes, serde: Serde) -> Fabric: + """Convert nested JSON bytes to flat Fabric.""" + nested = serde.decode(data, FabricNested) + return _fabric_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +Fabric.FABRIC_COLUMN_COUNT = NumericField("fabricColumnCount", "fabricColumnCount") +Fabric.FABRIC_DATA_TYPE = KeywordField("fabricDataType", "fabricDataType") +Fabric.FABRIC_ORDINAL = NumericField("fabricOrdinal", "fabricOrdinal") +Fabric.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Fabric.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Fabric.ANOMALO_CHECKS = RelationField("anomaloChecks") +Fabric.APPLICATION = RelationField("application") +Fabric.APPLICATION_FIELD = RelationField("applicationField") +Fabric.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Fabric.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Fabric.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Fabric.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Fabric.METRICS = RelationField("metrics") +Fabric.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Fabric.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Fabric.MEANINGS = RelationField("meanings") +Fabric.MC_MONITORS = RelationField("mcMonitors") +Fabric.MC_INCIDENTS = RelationField("mcIncidents") +Fabric.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Fabric.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Fabric.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Fabric.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Fabric.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Fabric.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Fabric.FILES = RelationField("files") +Fabric.LINKS = RelationField("links") +Fabric.README = RelationField("readme") +Fabric.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Fabric.SODA_CHECKS = RelationField("sodaChecks") +Fabric.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Fabric.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/fabric_activity.py b/pyatlan_v9/model/assets/fabric_activity.py new file mode 100644 index 000000000..2a2c1324c --- /dev/null +++ b/pyatlan_v9/model/assets/fabric_activity.py @@ -0,0 +1,627 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +FabricActivity asset model with flattened inheritance. + +This module provides: +- FabricActivity: Flat asset class (easy to use) +- FabricActivityAttributes: Nested attributes struct (extends AssetAttributes) +- FabricActivityNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .fabric_related import RelatedFabricDataPipeline + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class FabricActivity(Asset): + """ + Instance of a Microsoft Fabric activity in Atlan. + """ + + FABRIC_DATA_PIPELINE_QUALIFIED_NAME: ClassVar[Any] = None + FABRIC_ACTIVITY_TYPE: ClassVar[Any] = None + FABRIC_COLUMN_COUNT: ClassVar[Any] = None + FABRIC_DATA_TYPE: ClassVar[Any] = None + FABRIC_ORDINAL: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + FABRIC_DATA_PIPELINE: ClassVar[Any] = None + FABRIC_PROCESS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "FabricActivity" + + fabric_data_pipeline_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the Fabric data pipeline that contains this asset.""" + + fabric_activity_type: Union[str, None, UnsetType] = UNSET + """Type of activity.""" + + fabric_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this asset.""" + + fabric_data_type: Union[str, None, UnsetType] = UNSET + """Data type of this asset.""" + + fabric_ordinal: Union[int, None, UnsetType] = UNSET + """Order/position of this asset within its parent.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + fabric_data_pipeline: Union[RelatedFabricDataPipeline, None, UnsetType] = UNSET + """Data pipeline containing the activity.""" + + fabric_process: Union[RelatedProcess, None, UnsetType] = UNSET + """Process containing the Fabric activity.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "FabricActivity" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _fabric_activity_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> FabricActivity: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + FabricActivity instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _fabric_activity_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class FabricActivityAttributes(AssetAttributes): + """FabricActivity-specific attributes for nested API format.""" + + fabric_data_pipeline_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the Fabric data pipeline that contains this asset.""" + + fabric_activity_type: Union[str, None, UnsetType] = UNSET + """Type of activity.""" + + fabric_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this asset.""" + + fabric_data_type: Union[str, None, UnsetType] = UNSET + """Data type of this asset.""" + + fabric_ordinal: Union[int, None, UnsetType] = UNSET + """Order/position of this asset within its parent.""" + + +class FabricActivityRelationshipAttributes(AssetRelationshipAttributes): + """FabricActivity-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + fabric_data_pipeline: Union[RelatedFabricDataPipeline, None, UnsetType] = UNSET + """Data pipeline containing the activity.""" + + fabric_process: Union[RelatedProcess, None, UnsetType] = UNSET + """Process containing the Fabric activity.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class FabricActivityNested(AssetNested): + """FabricActivity in nested API format for high-performance serialization.""" + + attributes: Union[FabricActivityAttributes, UnsetType] = UNSET + relationship_attributes: Union[FabricActivityRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + FabricActivityRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + FabricActivityRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_FABRIC_ACTIVITY_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "fabric_data_pipeline", + "fabric_process", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_fabric_activity_attrs( + attrs: FabricActivityAttributes, obj: FabricActivity +) -> None: + """Populate FabricActivity-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.fabric_data_pipeline_qualified_name = obj.fabric_data_pipeline_qualified_name + attrs.fabric_activity_type = obj.fabric_activity_type + attrs.fabric_column_count = obj.fabric_column_count + attrs.fabric_data_type = obj.fabric_data_type + attrs.fabric_ordinal = obj.fabric_ordinal + + +def _extract_fabric_activity_attrs(attrs: FabricActivityAttributes) -> dict: + """Extract all FabricActivity attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["fabric_data_pipeline_qualified_name"] = ( + attrs.fabric_data_pipeline_qualified_name + ) + result["fabric_activity_type"] = attrs.fabric_activity_type + result["fabric_column_count"] = attrs.fabric_column_count + result["fabric_data_type"] = attrs.fabric_data_type + result["fabric_ordinal"] = attrs.fabric_ordinal + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _fabric_activity_to_nested(fabric_activity: FabricActivity) -> FabricActivityNested: + """Convert flat FabricActivity to nested format.""" + attrs = FabricActivityAttributes() + _populate_fabric_activity_attrs(attrs, fabric_activity) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + fabric_activity, + _FABRIC_ACTIVITY_REL_FIELDS, + FabricActivityRelationshipAttributes, + ) + return FabricActivityNested( + guid=fabric_activity.guid, + type_name=fabric_activity.type_name, + status=fabric_activity.status, + version=fabric_activity.version, + create_time=fabric_activity.create_time, + update_time=fabric_activity.update_time, + created_by=fabric_activity.created_by, + updated_by=fabric_activity.updated_by, + classifications=fabric_activity.classifications, + classification_names=fabric_activity.classification_names, + meanings=fabric_activity.meanings, + labels=fabric_activity.labels, + business_attributes=fabric_activity.business_attributes, + custom_attributes=fabric_activity.custom_attributes, + pending_tasks=fabric_activity.pending_tasks, + proxy=fabric_activity.proxy, + is_incomplete=fabric_activity.is_incomplete, + provenance_type=fabric_activity.provenance_type, + home_id=fabric_activity.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _fabric_activity_from_nested(nested: FabricActivityNested) -> FabricActivity: + """Convert nested format to flat FabricActivity.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else FabricActivityAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _FABRIC_ACTIVITY_REL_FIELDS, + FabricActivityRelationshipAttributes, + ) + return FabricActivity( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_fabric_activity_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _fabric_activity_to_nested_bytes( + fabric_activity: FabricActivity, serde: Serde +) -> bytes: + """Convert flat FabricActivity to nested JSON bytes.""" + return serde.encode(_fabric_activity_to_nested(fabric_activity)) + + +def _fabric_activity_from_nested_bytes(data: bytes, serde: Serde) -> FabricActivity: + """Convert nested JSON bytes to flat FabricActivity.""" + nested = serde.decode(data, FabricActivityNested) + return _fabric_activity_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +FabricActivity.FABRIC_DATA_PIPELINE_QUALIFIED_NAME = KeywordField( + "fabricDataPipelineQualifiedName", "fabricDataPipelineQualifiedName" +) +FabricActivity.FABRIC_ACTIVITY_TYPE = KeywordField( + "fabricActivityType", "fabricActivityType" +) +FabricActivity.FABRIC_COLUMN_COUNT = NumericField( + "fabricColumnCount", "fabricColumnCount" +) +FabricActivity.FABRIC_DATA_TYPE = KeywordField("fabricDataType", "fabricDataType") +FabricActivity.FABRIC_ORDINAL = NumericField("fabricOrdinal", "fabricOrdinal") +FabricActivity.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +FabricActivity.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +FabricActivity.ANOMALO_CHECKS = RelationField("anomaloChecks") +FabricActivity.APPLICATION = RelationField("application") +FabricActivity.APPLICATION_FIELD = RelationField("applicationField") +FabricActivity.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +FabricActivity.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +FabricActivity.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +FabricActivity.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +FabricActivity.METRICS = RelationField("metrics") +FabricActivity.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +FabricActivity.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +FabricActivity.FABRIC_DATA_PIPELINE = RelationField("fabricDataPipeline") +FabricActivity.FABRIC_PROCESS = RelationField("fabricProcess") +FabricActivity.MEANINGS = RelationField("meanings") +FabricActivity.MC_MONITORS = RelationField("mcMonitors") +FabricActivity.MC_INCIDENTS = RelationField("mcIncidents") +FabricActivity.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +FabricActivity.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +FabricActivity.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +FabricActivity.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +FabricActivity.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +FabricActivity.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +FabricActivity.FILES = RelationField("files") +FabricActivity.LINKS = RelationField("links") +FabricActivity.README = RelationField("readme") +FabricActivity.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +FabricActivity.SODA_CHECKS = RelationField("sodaChecks") +FabricActivity.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +FabricActivity.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/fabric_dashboard.py b/pyatlan_v9/model/assets/fabric_dashboard.py new file mode 100644 index 000000000..95ee3e5fb --- /dev/null +++ b/pyatlan_v9/model/assets/fabric_dashboard.py @@ -0,0 +1,594 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +FabricDashboard asset model with flattened inheritance. + +This module provides: +- FabricDashboard: Flat asset class (easy to use) +- FabricDashboardAttributes: Nested attributes struct (extends AssetAttributes) +- FabricDashboardNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .fabric_related import RelatedFabricWorkspace + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class FabricDashboard(Asset): + """ + Instance of a Microsoft Fabric dashboard in Atlan. + """ + + FABRIC_COLUMN_COUNT: ClassVar[Any] = None + FABRIC_DATA_TYPE: ClassVar[Any] = None + FABRIC_ORDINAL: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + FABRIC_WORKSPACE: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "FabricDashboard" + + fabric_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this asset.""" + + fabric_data_type: Union[str, None, UnsetType] = UNSET + """Data type of this asset.""" + + fabric_ordinal: Union[int, None, UnsetType] = UNSET + """Order/position of this asset within its parent.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + fabric_workspace: Union[RelatedFabricWorkspace, None, UnsetType] = UNSET + """Workspace containing the dashboard.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "FabricDashboard" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _fabric_dashboard_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> FabricDashboard: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + FabricDashboard instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _fabric_dashboard_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class FabricDashboardAttributes(AssetAttributes): + """FabricDashboard-specific attributes for nested API format.""" + + fabric_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this asset.""" + + fabric_data_type: Union[str, None, UnsetType] = UNSET + """Data type of this asset.""" + + fabric_ordinal: Union[int, None, UnsetType] = UNSET + """Order/position of this asset within its parent.""" + + +class FabricDashboardRelationshipAttributes(AssetRelationshipAttributes): + """FabricDashboard-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + fabric_workspace: Union[RelatedFabricWorkspace, None, UnsetType] = UNSET + """Workspace containing the dashboard.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class FabricDashboardNested(AssetNested): + """FabricDashboard in nested API format for high-performance serialization.""" + + attributes: Union[FabricDashboardAttributes, UnsetType] = UNSET + relationship_attributes: Union[FabricDashboardRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + FabricDashboardRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + FabricDashboardRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_FABRIC_DASHBOARD_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "fabric_workspace", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_fabric_dashboard_attrs( + attrs: FabricDashboardAttributes, obj: FabricDashboard +) -> None: + """Populate FabricDashboard-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.fabric_column_count = obj.fabric_column_count + attrs.fabric_data_type = obj.fabric_data_type + attrs.fabric_ordinal = obj.fabric_ordinal + + +def _extract_fabric_dashboard_attrs(attrs: FabricDashboardAttributes) -> dict: + """Extract all FabricDashboard attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["fabric_column_count"] = attrs.fabric_column_count + result["fabric_data_type"] = attrs.fabric_data_type + result["fabric_ordinal"] = attrs.fabric_ordinal + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _fabric_dashboard_to_nested( + fabric_dashboard: FabricDashboard, +) -> FabricDashboardNested: + """Convert flat FabricDashboard to nested format.""" + attrs = FabricDashboardAttributes() + _populate_fabric_dashboard_attrs(attrs, fabric_dashboard) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + fabric_dashboard, + _FABRIC_DASHBOARD_REL_FIELDS, + FabricDashboardRelationshipAttributes, + ) + return FabricDashboardNested( + guid=fabric_dashboard.guid, + type_name=fabric_dashboard.type_name, + status=fabric_dashboard.status, + version=fabric_dashboard.version, + create_time=fabric_dashboard.create_time, + update_time=fabric_dashboard.update_time, + created_by=fabric_dashboard.created_by, + updated_by=fabric_dashboard.updated_by, + classifications=fabric_dashboard.classifications, + classification_names=fabric_dashboard.classification_names, + meanings=fabric_dashboard.meanings, + labels=fabric_dashboard.labels, + business_attributes=fabric_dashboard.business_attributes, + custom_attributes=fabric_dashboard.custom_attributes, + pending_tasks=fabric_dashboard.pending_tasks, + proxy=fabric_dashboard.proxy, + is_incomplete=fabric_dashboard.is_incomplete, + provenance_type=fabric_dashboard.provenance_type, + home_id=fabric_dashboard.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _fabric_dashboard_from_nested(nested: FabricDashboardNested) -> FabricDashboard: + """Convert nested format to flat FabricDashboard.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else FabricDashboardAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _FABRIC_DASHBOARD_REL_FIELDS, + FabricDashboardRelationshipAttributes, + ) + return FabricDashboard( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_fabric_dashboard_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _fabric_dashboard_to_nested_bytes( + fabric_dashboard: FabricDashboard, serde: Serde +) -> bytes: + """Convert flat FabricDashboard to nested JSON bytes.""" + return serde.encode(_fabric_dashboard_to_nested(fabric_dashboard)) + + +def _fabric_dashboard_from_nested_bytes(data: bytes, serde: Serde) -> FabricDashboard: + """Convert nested JSON bytes to flat FabricDashboard.""" + nested = serde.decode(data, FabricDashboardNested) + return _fabric_dashboard_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +FabricDashboard.FABRIC_COLUMN_COUNT = NumericField( + "fabricColumnCount", "fabricColumnCount" +) +FabricDashboard.FABRIC_DATA_TYPE = KeywordField("fabricDataType", "fabricDataType") +FabricDashboard.FABRIC_ORDINAL = NumericField("fabricOrdinal", "fabricOrdinal") +FabricDashboard.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +FabricDashboard.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +FabricDashboard.ANOMALO_CHECKS = RelationField("anomaloChecks") +FabricDashboard.APPLICATION = RelationField("application") +FabricDashboard.APPLICATION_FIELD = RelationField("applicationField") +FabricDashboard.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +FabricDashboard.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +FabricDashboard.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +FabricDashboard.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +FabricDashboard.METRICS = RelationField("metrics") +FabricDashboard.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +FabricDashboard.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +FabricDashboard.FABRIC_WORKSPACE = RelationField("fabricWorkspace") +FabricDashboard.MEANINGS = RelationField("meanings") +FabricDashboard.MC_MONITORS = RelationField("mcMonitors") +FabricDashboard.MC_INCIDENTS = RelationField("mcIncidents") +FabricDashboard.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +FabricDashboard.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +FabricDashboard.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +FabricDashboard.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +FabricDashboard.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +FabricDashboard.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +FabricDashboard.FILES = RelationField("files") +FabricDashboard.LINKS = RelationField("links") +FabricDashboard.README = RelationField("readme") +FabricDashboard.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +FabricDashboard.SODA_CHECKS = RelationField("sodaChecks") +FabricDashboard.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +FabricDashboard.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/fabric_data_pipeline.py b/pyatlan_v9/model/assets/fabric_data_pipeline.py new file mode 100644 index 000000000..ed8335767 --- /dev/null +++ b/pyatlan_v9/model/assets/fabric_data_pipeline.py @@ -0,0 +1,609 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +FabricDataPipeline asset model with flattened inheritance. + +This module provides: +- FabricDataPipeline: Flat asset class (easy to use) +- FabricDataPipelineAttributes: Nested attributes struct (extends AssetAttributes) +- FabricDataPipelineNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .fabric_related import RelatedFabricActivity, RelatedFabricWorkspace + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class FabricDataPipeline(Asset): + """ + Instance of a Microsoft Fabric data pipeline in Atlan. + """ + + FABRIC_COLUMN_COUNT: ClassVar[Any] = None + FABRIC_DATA_TYPE: ClassVar[Any] = None + FABRIC_ORDINAL: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + FABRIC_WORKSPACE: ClassVar[Any] = None + FABRIC_ACTIVITIES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "FabricDataPipeline" + + fabric_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this asset.""" + + fabric_data_type: Union[str, None, UnsetType] = UNSET + """Data type of this asset.""" + + fabric_ordinal: Union[int, None, UnsetType] = UNSET + """Order/position of this asset within its parent.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + fabric_workspace: Union[RelatedFabricWorkspace, None, UnsetType] = UNSET + """Workspace containing the data pipeline.""" + + fabric_activities: Union[List[RelatedFabricActivity], None, UnsetType] = UNSET + """Individual activities contained in the data pipeline.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "FabricDataPipeline" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _fabric_data_pipeline_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> FabricDataPipeline: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + FabricDataPipeline instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _fabric_data_pipeline_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class FabricDataPipelineAttributes(AssetAttributes): + """FabricDataPipeline-specific attributes for nested API format.""" + + fabric_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this asset.""" + + fabric_data_type: Union[str, None, UnsetType] = UNSET + """Data type of this asset.""" + + fabric_ordinal: Union[int, None, UnsetType] = UNSET + """Order/position of this asset within its parent.""" + + +class FabricDataPipelineRelationshipAttributes(AssetRelationshipAttributes): + """FabricDataPipeline-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + fabric_workspace: Union[RelatedFabricWorkspace, None, UnsetType] = UNSET + """Workspace containing the data pipeline.""" + + fabric_activities: Union[List[RelatedFabricActivity], None, UnsetType] = UNSET + """Individual activities contained in the data pipeline.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class FabricDataPipelineNested(AssetNested): + """FabricDataPipeline in nested API format for high-performance serialization.""" + + attributes: Union[FabricDataPipelineAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + FabricDataPipelineRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + FabricDataPipelineRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + FabricDataPipelineRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_FABRIC_DATA_PIPELINE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "fabric_workspace", + "fabric_activities", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_fabric_data_pipeline_attrs( + attrs: FabricDataPipelineAttributes, obj: FabricDataPipeline +) -> None: + """Populate FabricDataPipeline-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.fabric_column_count = obj.fabric_column_count + attrs.fabric_data_type = obj.fabric_data_type + attrs.fabric_ordinal = obj.fabric_ordinal + + +def _extract_fabric_data_pipeline_attrs(attrs: FabricDataPipelineAttributes) -> dict: + """Extract all FabricDataPipeline attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["fabric_column_count"] = attrs.fabric_column_count + result["fabric_data_type"] = attrs.fabric_data_type + result["fabric_ordinal"] = attrs.fabric_ordinal + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _fabric_data_pipeline_to_nested( + fabric_data_pipeline: FabricDataPipeline, +) -> FabricDataPipelineNested: + """Convert flat FabricDataPipeline to nested format.""" + attrs = FabricDataPipelineAttributes() + _populate_fabric_data_pipeline_attrs(attrs, fabric_data_pipeline) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + fabric_data_pipeline, + _FABRIC_DATA_PIPELINE_REL_FIELDS, + FabricDataPipelineRelationshipAttributes, + ) + return FabricDataPipelineNested( + guid=fabric_data_pipeline.guid, + type_name=fabric_data_pipeline.type_name, + status=fabric_data_pipeline.status, + version=fabric_data_pipeline.version, + create_time=fabric_data_pipeline.create_time, + update_time=fabric_data_pipeline.update_time, + created_by=fabric_data_pipeline.created_by, + updated_by=fabric_data_pipeline.updated_by, + classifications=fabric_data_pipeline.classifications, + classification_names=fabric_data_pipeline.classification_names, + meanings=fabric_data_pipeline.meanings, + labels=fabric_data_pipeline.labels, + business_attributes=fabric_data_pipeline.business_attributes, + custom_attributes=fabric_data_pipeline.custom_attributes, + pending_tasks=fabric_data_pipeline.pending_tasks, + proxy=fabric_data_pipeline.proxy, + is_incomplete=fabric_data_pipeline.is_incomplete, + provenance_type=fabric_data_pipeline.provenance_type, + home_id=fabric_data_pipeline.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _fabric_data_pipeline_from_nested( + nested: FabricDataPipelineNested, +) -> FabricDataPipeline: + """Convert nested format to flat FabricDataPipeline.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else FabricDataPipelineAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _FABRIC_DATA_PIPELINE_REL_FIELDS, + FabricDataPipelineRelationshipAttributes, + ) + return FabricDataPipeline( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_fabric_data_pipeline_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _fabric_data_pipeline_to_nested_bytes( + fabric_data_pipeline: FabricDataPipeline, serde: Serde +) -> bytes: + """Convert flat FabricDataPipeline to nested JSON bytes.""" + return serde.encode(_fabric_data_pipeline_to_nested(fabric_data_pipeline)) + + +def _fabric_data_pipeline_from_nested_bytes( + data: bytes, serde: Serde +) -> FabricDataPipeline: + """Convert nested JSON bytes to flat FabricDataPipeline.""" + nested = serde.decode(data, FabricDataPipelineNested) + return _fabric_data_pipeline_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +FabricDataPipeline.FABRIC_COLUMN_COUNT = NumericField( + "fabricColumnCount", "fabricColumnCount" +) +FabricDataPipeline.FABRIC_DATA_TYPE = KeywordField("fabricDataType", "fabricDataType") +FabricDataPipeline.FABRIC_ORDINAL = NumericField("fabricOrdinal", "fabricOrdinal") +FabricDataPipeline.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +FabricDataPipeline.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +FabricDataPipeline.ANOMALO_CHECKS = RelationField("anomaloChecks") +FabricDataPipeline.APPLICATION = RelationField("application") +FabricDataPipeline.APPLICATION_FIELD = RelationField("applicationField") +FabricDataPipeline.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +FabricDataPipeline.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +FabricDataPipeline.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +FabricDataPipeline.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +FabricDataPipeline.METRICS = RelationField("metrics") +FabricDataPipeline.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +FabricDataPipeline.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +FabricDataPipeline.FABRIC_WORKSPACE = RelationField("fabricWorkspace") +FabricDataPipeline.FABRIC_ACTIVITIES = RelationField("fabricActivities") +FabricDataPipeline.MEANINGS = RelationField("meanings") +FabricDataPipeline.MC_MONITORS = RelationField("mcMonitors") +FabricDataPipeline.MC_INCIDENTS = RelationField("mcIncidents") +FabricDataPipeline.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +FabricDataPipeline.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +FabricDataPipeline.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +FabricDataPipeline.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +FabricDataPipeline.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +FabricDataPipeline.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +FabricDataPipeline.FILES = RelationField("files") +FabricDataPipeline.LINKS = RelationField("links") +FabricDataPipeline.README = RelationField("readme") +FabricDataPipeline.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +FabricDataPipeline.SODA_CHECKS = RelationField("sodaChecks") +FabricDataPipeline.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +FabricDataPipeline.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/fabric_dataflow.py b/pyatlan_v9/model/assets/fabric_dataflow.py new file mode 100644 index 000000000..d53e2e074 --- /dev/null +++ b/pyatlan_v9/model/assets/fabric_dataflow.py @@ -0,0 +1,605 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +FabricDataflow asset model with flattened inheritance. + +This module provides: +- FabricDataflow: Flat asset class (easy to use) +- FabricDataflowAttributes: Nested attributes struct (extends AssetAttributes) +- FabricDataflowNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .fabric_related import RelatedFabricDataflowEntityColumn, RelatedFabricWorkspace + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class FabricDataflow(Asset): + """ + Instance of a Microsoft Fabric dataflow in Atlan. + """ + + FABRIC_COLUMN_COUNT: ClassVar[Any] = None + FABRIC_DATA_TYPE: ClassVar[Any] = None + FABRIC_ORDINAL: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + FABRIC_WORKSPACE: ClassVar[Any] = None + FABRIC_DATAFLOW_ENTITY_COLUMNS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "FabricDataflow" + + fabric_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this asset.""" + + fabric_data_type: Union[str, None, UnsetType] = UNSET + """Data type of this asset.""" + + fabric_ordinal: Union[int, None, UnsetType] = UNSET + """Order/position of this asset within its parent.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + fabric_workspace: Union[RelatedFabricWorkspace, None, UnsetType] = UNSET + """Workspace containing the dataflow.""" + + fabric_dataflow_entity_columns: Union[ + List[RelatedFabricDataflowEntityColumn], None, UnsetType + ] = UNSET + """Individual dataflow entity columns contained in the dataflow.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "FabricDataflow" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _fabric_dataflow_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> FabricDataflow: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + FabricDataflow instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _fabric_dataflow_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class FabricDataflowAttributes(AssetAttributes): + """FabricDataflow-specific attributes for nested API format.""" + + fabric_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this asset.""" + + fabric_data_type: Union[str, None, UnsetType] = UNSET + """Data type of this asset.""" + + fabric_ordinal: Union[int, None, UnsetType] = UNSET + """Order/position of this asset within its parent.""" + + +class FabricDataflowRelationshipAttributes(AssetRelationshipAttributes): + """FabricDataflow-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + fabric_workspace: Union[RelatedFabricWorkspace, None, UnsetType] = UNSET + """Workspace containing the dataflow.""" + + fabric_dataflow_entity_columns: Union[ + List[RelatedFabricDataflowEntityColumn], None, UnsetType + ] = UNSET + """Individual dataflow entity columns contained in the dataflow.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class FabricDataflowNested(AssetNested): + """FabricDataflow in nested API format for high-performance serialization.""" + + attributes: Union[FabricDataflowAttributes, UnsetType] = UNSET + relationship_attributes: Union[FabricDataflowRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + FabricDataflowRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + FabricDataflowRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_FABRIC_DATAFLOW_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "fabric_workspace", + "fabric_dataflow_entity_columns", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_fabric_dataflow_attrs( + attrs: FabricDataflowAttributes, obj: FabricDataflow +) -> None: + """Populate FabricDataflow-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.fabric_column_count = obj.fabric_column_count + attrs.fabric_data_type = obj.fabric_data_type + attrs.fabric_ordinal = obj.fabric_ordinal + + +def _extract_fabric_dataflow_attrs(attrs: FabricDataflowAttributes) -> dict: + """Extract all FabricDataflow attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["fabric_column_count"] = attrs.fabric_column_count + result["fabric_data_type"] = attrs.fabric_data_type + result["fabric_ordinal"] = attrs.fabric_ordinal + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _fabric_dataflow_to_nested(fabric_dataflow: FabricDataflow) -> FabricDataflowNested: + """Convert flat FabricDataflow to nested format.""" + attrs = FabricDataflowAttributes() + _populate_fabric_dataflow_attrs(attrs, fabric_dataflow) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + fabric_dataflow, + _FABRIC_DATAFLOW_REL_FIELDS, + FabricDataflowRelationshipAttributes, + ) + return FabricDataflowNested( + guid=fabric_dataflow.guid, + type_name=fabric_dataflow.type_name, + status=fabric_dataflow.status, + version=fabric_dataflow.version, + create_time=fabric_dataflow.create_time, + update_time=fabric_dataflow.update_time, + created_by=fabric_dataflow.created_by, + updated_by=fabric_dataflow.updated_by, + classifications=fabric_dataflow.classifications, + classification_names=fabric_dataflow.classification_names, + meanings=fabric_dataflow.meanings, + labels=fabric_dataflow.labels, + business_attributes=fabric_dataflow.business_attributes, + custom_attributes=fabric_dataflow.custom_attributes, + pending_tasks=fabric_dataflow.pending_tasks, + proxy=fabric_dataflow.proxy, + is_incomplete=fabric_dataflow.is_incomplete, + provenance_type=fabric_dataflow.provenance_type, + home_id=fabric_dataflow.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _fabric_dataflow_from_nested(nested: FabricDataflowNested) -> FabricDataflow: + """Convert nested format to flat FabricDataflow.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else FabricDataflowAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _FABRIC_DATAFLOW_REL_FIELDS, + FabricDataflowRelationshipAttributes, + ) + return FabricDataflow( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_fabric_dataflow_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _fabric_dataflow_to_nested_bytes( + fabric_dataflow: FabricDataflow, serde: Serde +) -> bytes: + """Convert flat FabricDataflow to nested JSON bytes.""" + return serde.encode(_fabric_dataflow_to_nested(fabric_dataflow)) + + +def _fabric_dataflow_from_nested_bytes(data: bytes, serde: Serde) -> FabricDataflow: + """Convert nested JSON bytes to flat FabricDataflow.""" + nested = serde.decode(data, FabricDataflowNested) + return _fabric_dataflow_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +FabricDataflow.FABRIC_COLUMN_COUNT = NumericField( + "fabricColumnCount", "fabricColumnCount" +) +FabricDataflow.FABRIC_DATA_TYPE = KeywordField("fabricDataType", "fabricDataType") +FabricDataflow.FABRIC_ORDINAL = NumericField("fabricOrdinal", "fabricOrdinal") +FabricDataflow.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +FabricDataflow.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +FabricDataflow.ANOMALO_CHECKS = RelationField("anomaloChecks") +FabricDataflow.APPLICATION = RelationField("application") +FabricDataflow.APPLICATION_FIELD = RelationField("applicationField") +FabricDataflow.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +FabricDataflow.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +FabricDataflow.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +FabricDataflow.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +FabricDataflow.METRICS = RelationField("metrics") +FabricDataflow.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +FabricDataflow.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +FabricDataflow.FABRIC_WORKSPACE = RelationField("fabricWorkspace") +FabricDataflow.FABRIC_DATAFLOW_ENTITY_COLUMNS = RelationField( + "fabricDataflowEntityColumns" +) +FabricDataflow.MEANINGS = RelationField("meanings") +FabricDataflow.MC_MONITORS = RelationField("mcMonitors") +FabricDataflow.MC_INCIDENTS = RelationField("mcIncidents") +FabricDataflow.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +FabricDataflow.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +FabricDataflow.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +FabricDataflow.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +FabricDataflow.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +FabricDataflow.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +FabricDataflow.FILES = RelationField("files") +FabricDataflow.LINKS = RelationField("links") +FabricDataflow.README = RelationField("readme") +FabricDataflow.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +FabricDataflow.SODA_CHECKS = RelationField("sodaChecks") +FabricDataflow.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +FabricDataflow.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/fabric_dataflow_entity_column.py b/pyatlan_v9/model/assets/fabric_dataflow_entity_column.py new file mode 100644 index 000000000..e3e8f26be --- /dev/null +++ b/pyatlan_v9/model/assets/fabric_dataflow_entity_column.py @@ -0,0 +1,648 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +FabricDataflowEntityColumn asset model with flattened inheritance. + +This module provides: +- FabricDataflowEntityColumn: Flat asset class (easy to use) +- FabricDataflowEntityColumnAttributes: Nested attributes struct (extends AssetAttributes) +- FabricDataflowEntityColumnNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .fabric_related import RelatedFabricDataflow + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class FabricDataflowEntityColumn(Asset): + """ + Instance of a Microsoft Fabric dataflow entity column in Atlan. + """ + + FABRIC_DATAFLOW_QUALIFIED_NAME: ClassVar[Any] = None + FABRIC_DATAFLOW_NAME: ClassVar[Any] = None + FABRIC_COLUMN_COUNT: ClassVar[Any] = None + FABRIC_DATA_TYPE: ClassVar[Any] = None + FABRIC_ORDINAL: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + FABRIC_DATAFLOW: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "FabricDataflowEntityColumn" + + fabric_dataflow_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the Fabric dataflow that contains this asset.""" + + fabric_dataflow_name: Union[str, None, UnsetType] = UNSET + """Name of the Fabric dataflow that contains this asset.""" + + fabric_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this asset.""" + + fabric_data_type: Union[str, None, UnsetType] = UNSET + """Data type of this asset.""" + + fabric_ordinal: Union[int, None, UnsetType] = UNSET + """Order/position of this asset within its parent.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + fabric_dataflow: Union[RelatedFabricDataflow, None, UnsetType] = UNSET + """Dataflow containing the columns.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "FabricDataflowEntityColumn" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _fabric_dataflow_entity_column_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> FabricDataflowEntityColumn: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + FabricDataflowEntityColumn instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _fabric_dataflow_entity_column_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class FabricDataflowEntityColumnAttributes(AssetAttributes): + """FabricDataflowEntityColumn-specific attributes for nested API format.""" + + fabric_dataflow_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the Fabric dataflow that contains this asset.""" + + fabric_dataflow_name: Union[str, None, UnsetType] = UNSET + """Name of the Fabric dataflow that contains this asset.""" + + fabric_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this asset.""" + + fabric_data_type: Union[str, None, UnsetType] = UNSET + """Data type of this asset.""" + + fabric_ordinal: Union[int, None, UnsetType] = UNSET + """Order/position of this asset within its parent.""" + + +class FabricDataflowEntityColumnRelationshipAttributes(AssetRelationshipAttributes): + """FabricDataflowEntityColumn-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + fabric_dataflow: Union[RelatedFabricDataflow, None, UnsetType] = UNSET + """Dataflow containing the columns.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class FabricDataflowEntityColumnNested(AssetNested): + """FabricDataflowEntityColumn in nested API format for high-performance serialization.""" + + attributes: Union[FabricDataflowEntityColumnAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + FabricDataflowEntityColumnRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + FabricDataflowEntityColumnRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + FabricDataflowEntityColumnRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_FABRIC_DATAFLOW_ENTITY_COLUMN_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "fabric_dataflow", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_fabric_dataflow_entity_column_attrs( + attrs: FabricDataflowEntityColumnAttributes, obj: FabricDataflowEntityColumn +) -> None: + """Populate FabricDataflowEntityColumn-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.fabric_dataflow_qualified_name = obj.fabric_dataflow_qualified_name + attrs.fabric_dataflow_name = obj.fabric_dataflow_name + attrs.fabric_column_count = obj.fabric_column_count + attrs.fabric_data_type = obj.fabric_data_type + attrs.fabric_ordinal = obj.fabric_ordinal + + +def _extract_fabric_dataflow_entity_column_attrs( + attrs: FabricDataflowEntityColumnAttributes, +) -> dict: + """Extract all FabricDataflowEntityColumn attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["fabric_dataflow_qualified_name"] = attrs.fabric_dataflow_qualified_name + result["fabric_dataflow_name"] = attrs.fabric_dataflow_name + result["fabric_column_count"] = attrs.fabric_column_count + result["fabric_data_type"] = attrs.fabric_data_type + result["fabric_ordinal"] = attrs.fabric_ordinal + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _fabric_dataflow_entity_column_to_nested( + fabric_dataflow_entity_column: FabricDataflowEntityColumn, +) -> FabricDataflowEntityColumnNested: + """Convert flat FabricDataflowEntityColumn to nested format.""" + attrs = FabricDataflowEntityColumnAttributes() + _populate_fabric_dataflow_entity_column_attrs(attrs, fabric_dataflow_entity_column) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + fabric_dataflow_entity_column, + _FABRIC_DATAFLOW_ENTITY_COLUMN_REL_FIELDS, + FabricDataflowEntityColumnRelationshipAttributes, + ) + return FabricDataflowEntityColumnNested( + guid=fabric_dataflow_entity_column.guid, + type_name=fabric_dataflow_entity_column.type_name, + status=fabric_dataflow_entity_column.status, + version=fabric_dataflow_entity_column.version, + create_time=fabric_dataflow_entity_column.create_time, + update_time=fabric_dataflow_entity_column.update_time, + created_by=fabric_dataflow_entity_column.created_by, + updated_by=fabric_dataflow_entity_column.updated_by, + classifications=fabric_dataflow_entity_column.classifications, + classification_names=fabric_dataflow_entity_column.classification_names, + meanings=fabric_dataflow_entity_column.meanings, + labels=fabric_dataflow_entity_column.labels, + business_attributes=fabric_dataflow_entity_column.business_attributes, + custom_attributes=fabric_dataflow_entity_column.custom_attributes, + pending_tasks=fabric_dataflow_entity_column.pending_tasks, + proxy=fabric_dataflow_entity_column.proxy, + is_incomplete=fabric_dataflow_entity_column.is_incomplete, + provenance_type=fabric_dataflow_entity_column.provenance_type, + home_id=fabric_dataflow_entity_column.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _fabric_dataflow_entity_column_from_nested( + nested: FabricDataflowEntityColumnNested, +) -> FabricDataflowEntityColumn: + """Convert nested format to flat FabricDataflowEntityColumn.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else FabricDataflowEntityColumnAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _FABRIC_DATAFLOW_ENTITY_COLUMN_REL_FIELDS, + FabricDataflowEntityColumnRelationshipAttributes, + ) + return FabricDataflowEntityColumn( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_fabric_dataflow_entity_column_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _fabric_dataflow_entity_column_to_nested_bytes( + fabric_dataflow_entity_column: FabricDataflowEntityColumn, serde: Serde +) -> bytes: + """Convert flat FabricDataflowEntityColumn to nested JSON bytes.""" + return serde.encode( + _fabric_dataflow_entity_column_to_nested(fabric_dataflow_entity_column) + ) + + +def _fabric_dataflow_entity_column_from_nested_bytes( + data: bytes, serde: Serde +) -> FabricDataflowEntityColumn: + """Convert nested JSON bytes to flat FabricDataflowEntityColumn.""" + nested = serde.decode(data, FabricDataflowEntityColumnNested) + return _fabric_dataflow_entity_column_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +FabricDataflowEntityColumn.FABRIC_DATAFLOW_QUALIFIED_NAME = KeywordField( + "fabricDataflowQualifiedName", "fabricDataflowQualifiedName" +) +FabricDataflowEntityColumn.FABRIC_DATAFLOW_NAME = KeywordField( + "fabricDataflowName", "fabricDataflowName" +) +FabricDataflowEntityColumn.FABRIC_COLUMN_COUNT = NumericField( + "fabricColumnCount", "fabricColumnCount" +) +FabricDataflowEntityColumn.FABRIC_DATA_TYPE = KeywordField( + "fabricDataType", "fabricDataType" +) +FabricDataflowEntityColumn.FABRIC_ORDINAL = NumericField( + "fabricOrdinal", "fabricOrdinal" +) +FabricDataflowEntityColumn.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +FabricDataflowEntityColumn.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +FabricDataflowEntityColumn.ANOMALO_CHECKS = RelationField("anomaloChecks") +FabricDataflowEntityColumn.APPLICATION = RelationField("application") +FabricDataflowEntityColumn.APPLICATION_FIELD = RelationField("applicationField") +FabricDataflowEntityColumn.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +FabricDataflowEntityColumn.INPUT_PORT_DATA_PRODUCTS = RelationField( + "inputPortDataProducts" +) +FabricDataflowEntityColumn.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +FabricDataflowEntityColumn.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +FabricDataflowEntityColumn.METRICS = RelationField("metrics") +FabricDataflowEntityColumn.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +FabricDataflowEntityColumn.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +FabricDataflowEntityColumn.FABRIC_DATAFLOW = RelationField("fabricDataflow") +FabricDataflowEntityColumn.MEANINGS = RelationField("meanings") +FabricDataflowEntityColumn.MC_MONITORS = RelationField("mcMonitors") +FabricDataflowEntityColumn.MC_INCIDENTS = RelationField("mcIncidents") +FabricDataflowEntityColumn.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +FabricDataflowEntityColumn.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +FabricDataflowEntityColumn.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +FabricDataflowEntityColumn.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +FabricDataflowEntityColumn.USER_DEF_RELATIONSHIP_TO = RelationField( + "userDefRelationshipTo" +) +FabricDataflowEntityColumn.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +FabricDataflowEntityColumn.FILES = RelationField("files") +FabricDataflowEntityColumn.LINKS = RelationField("links") +FabricDataflowEntityColumn.README = RelationField("readme") +FabricDataflowEntityColumn.SCHEMA_REGISTRY_SUBJECTS = RelationField( + "schemaRegistrySubjects" +) +FabricDataflowEntityColumn.SODA_CHECKS = RelationField("sodaChecks") +FabricDataflowEntityColumn.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +FabricDataflowEntityColumn.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/fabric_page.py b/pyatlan_v9/model/assets/fabric_page.py new file mode 100644 index 000000000..de527d44c --- /dev/null +++ b/pyatlan_v9/model/assets/fabric_page.py @@ -0,0 +1,599 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +FabricPage asset model with flattened inheritance. + +This module provides: +- FabricPage: Flat asset class (easy to use) +- FabricPageAttributes: Nested attributes struct (extends AssetAttributes) +- FabricPageNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .fabric_related import RelatedFabricReport, RelatedFabricVisual + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class FabricPage(Asset): + """ + Instance of a Microsoft Fabric page in Atlan. + """ + + FABRIC_REPORT_QUALIFIED_NAME: ClassVar[Any] = None + FABRIC_COLUMN_COUNT: ClassVar[Any] = None + FABRIC_DATA_TYPE: ClassVar[Any] = None + FABRIC_ORDINAL: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + FABRIC_REPORT: ClassVar[Any] = None + FABRIC_VISUALS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "FabricPage" + + fabric_report_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the Fabric report that contains this asset.""" + + fabric_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this asset.""" + + fabric_data_type: Union[str, None, UnsetType] = UNSET + """Data type of this asset.""" + + fabric_ordinal: Union[int, None, UnsetType] = UNSET + """Order/position of this asset within its parent.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + fabric_report: Union[RelatedFabricReport, None, UnsetType] = UNSET + """Report containing the page.""" + + fabric_visuals: Union[List[RelatedFabricVisual], None, UnsetType] = UNSET + """Individual visuals contained in the page.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "FabricPage" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _fabric_page_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> FabricPage: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + FabricPage instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _fabric_page_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class FabricPageAttributes(AssetAttributes): + """FabricPage-specific attributes for nested API format.""" + + fabric_report_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the Fabric report that contains this asset.""" + + fabric_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this asset.""" + + fabric_data_type: Union[str, None, UnsetType] = UNSET + """Data type of this asset.""" + + fabric_ordinal: Union[int, None, UnsetType] = UNSET + """Order/position of this asset within its parent.""" + + +class FabricPageRelationshipAttributes(AssetRelationshipAttributes): + """FabricPage-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + fabric_report: Union[RelatedFabricReport, None, UnsetType] = UNSET + """Report containing the page.""" + + fabric_visuals: Union[List[RelatedFabricVisual], None, UnsetType] = UNSET + """Individual visuals contained in the page.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class FabricPageNested(AssetNested): + """FabricPage in nested API format for high-performance serialization.""" + + attributes: Union[FabricPageAttributes, UnsetType] = UNSET + relationship_attributes: Union[FabricPageRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + FabricPageRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + FabricPageRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_FABRIC_PAGE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "fabric_report", + "fabric_visuals", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_fabric_page_attrs(attrs: FabricPageAttributes, obj: FabricPage) -> None: + """Populate FabricPage-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.fabric_report_qualified_name = obj.fabric_report_qualified_name + attrs.fabric_column_count = obj.fabric_column_count + attrs.fabric_data_type = obj.fabric_data_type + attrs.fabric_ordinal = obj.fabric_ordinal + + +def _extract_fabric_page_attrs(attrs: FabricPageAttributes) -> dict: + """Extract all FabricPage attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["fabric_report_qualified_name"] = attrs.fabric_report_qualified_name + result["fabric_column_count"] = attrs.fabric_column_count + result["fabric_data_type"] = attrs.fabric_data_type + result["fabric_ordinal"] = attrs.fabric_ordinal + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _fabric_page_to_nested(fabric_page: FabricPage) -> FabricPageNested: + """Convert flat FabricPage to nested format.""" + attrs = FabricPageAttributes() + _populate_fabric_page_attrs(attrs, fabric_page) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + fabric_page, _FABRIC_PAGE_REL_FIELDS, FabricPageRelationshipAttributes + ) + return FabricPageNested( + guid=fabric_page.guid, + type_name=fabric_page.type_name, + status=fabric_page.status, + version=fabric_page.version, + create_time=fabric_page.create_time, + update_time=fabric_page.update_time, + created_by=fabric_page.created_by, + updated_by=fabric_page.updated_by, + classifications=fabric_page.classifications, + classification_names=fabric_page.classification_names, + meanings=fabric_page.meanings, + labels=fabric_page.labels, + business_attributes=fabric_page.business_attributes, + custom_attributes=fabric_page.custom_attributes, + pending_tasks=fabric_page.pending_tasks, + proxy=fabric_page.proxy, + is_incomplete=fabric_page.is_incomplete, + provenance_type=fabric_page.provenance_type, + home_id=fabric_page.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _fabric_page_from_nested(nested: FabricPageNested) -> FabricPage: + """Convert nested format to flat FabricPage.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else FabricPageAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _FABRIC_PAGE_REL_FIELDS, + FabricPageRelationshipAttributes, + ) + return FabricPage( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_fabric_page_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _fabric_page_to_nested_bytes(fabric_page: FabricPage, serde: Serde) -> bytes: + """Convert flat FabricPage to nested JSON bytes.""" + return serde.encode(_fabric_page_to_nested(fabric_page)) + + +def _fabric_page_from_nested_bytes(data: bytes, serde: Serde) -> FabricPage: + """Convert nested JSON bytes to flat FabricPage.""" + nested = serde.decode(data, FabricPageNested) + return _fabric_page_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +FabricPage.FABRIC_REPORT_QUALIFIED_NAME = KeywordField( + "fabricReportQualifiedName", "fabricReportQualifiedName" +) +FabricPage.FABRIC_COLUMN_COUNT = NumericField("fabricColumnCount", "fabricColumnCount") +FabricPage.FABRIC_DATA_TYPE = KeywordField("fabricDataType", "fabricDataType") +FabricPage.FABRIC_ORDINAL = NumericField("fabricOrdinal", "fabricOrdinal") +FabricPage.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +FabricPage.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +FabricPage.ANOMALO_CHECKS = RelationField("anomaloChecks") +FabricPage.APPLICATION = RelationField("application") +FabricPage.APPLICATION_FIELD = RelationField("applicationField") +FabricPage.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +FabricPage.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +FabricPage.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +FabricPage.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +FabricPage.METRICS = RelationField("metrics") +FabricPage.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +FabricPage.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +FabricPage.FABRIC_REPORT = RelationField("fabricReport") +FabricPage.FABRIC_VISUALS = RelationField("fabricVisuals") +FabricPage.MEANINGS = RelationField("meanings") +FabricPage.MC_MONITORS = RelationField("mcMonitors") +FabricPage.MC_INCIDENTS = RelationField("mcIncidents") +FabricPage.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +FabricPage.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +FabricPage.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +FabricPage.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +FabricPage.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +FabricPage.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +FabricPage.FILES = RelationField("files") +FabricPage.LINKS = RelationField("links") +FabricPage.README = RelationField("readme") +FabricPage.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +FabricPage.SODA_CHECKS = RelationField("sodaChecks") +FabricPage.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +FabricPage.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/fabric_related.py b/pyatlan_v9/model/assets/fabric_related.py new file mode 100644 index 000000000..d26080b60 --- /dev/null +++ b/pyatlan_v9/model/assets/fabric_related.py @@ -0,0 +1,272 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Fabric module. + +This module contains all Related{Type} classes for the Fabric type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Union + +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedBI +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedFabric", + "RelatedFabricWorkspace", + "RelatedFabricDashboard", + "RelatedFabricDataflow", + "RelatedFabricDataflowEntityColumn", + "RelatedFabricDataPipeline", + "RelatedFabricReport", + "RelatedFabricSemanticModel", + "RelatedFabricSemanticModelTable", + "RelatedFabricSemanticModelTableColumn", + "RelatedFabricPage", + "RelatedFabricActivity", + "RelatedFabricVisual", +] + + +class RelatedFabric(RelatedBI): + """ + Related entity reference for Fabric assets. + + Extends RelatedBI with Fabric-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Fabric" so it serializes correctly + + fabric_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this asset.""" + + fabric_data_type: Union[str, None, UnsetType] = UNSET + """Data type of this asset.""" + + fabric_ordinal: Union[int, None, UnsetType] = UNSET + """Order/position of this asset within its parent.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Fabric" + + +class RelatedFabricWorkspace(RelatedFabric): + """ + Related entity reference for FabricWorkspace assets. + + Extends RelatedFabric with FabricWorkspace-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "FabricWorkspace" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "FabricWorkspace" + + +class RelatedFabricDashboard(RelatedFabric): + """ + Related entity reference for FabricDashboard assets. + + Extends RelatedFabric with FabricDashboard-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "FabricDashboard" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "FabricDashboard" + + +class RelatedFabricDataflow(RelatedFabric): + """ + Related entity reference for FabricDataflow assets. + + Extends RelatedFabric with FabricDataflow-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "FabricDataflow" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "FabricDataflow" + + +class RelatedFabricDataflowEntityColumn(RelatedFabric): + """ + Related entity reference for FabricDataflowEntityColumn assets. + + Extends RelatedFabric with FabricDataflowEntityColumn-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "FabricDataflowEntityColumn" so it serializes correctly + + fabric_dataflow_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the Fabric dataflow that contains this asset.""" + + fabric_dataflow_name: Union[str, None, UnsetType] = UNSET + """Name of the Fabric dataflow that contains this asset.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "FabricDataflowEntityColumn" + + +class RelatedFabricDataPipeline(RelatedFabric): + """ + Related entity reference for FabricDataPipeline assets. + + Extends RelatedFabric with FabricDataPipeline-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "FabricDataPipeline" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "FabricDataPipeline" + + +class RelatedFabricReport(RelatedFabric): + """ + Related entity reference for FabricReport assets. + + Extends RelatedFabric with FabricReport-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "FabricReport" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "FabricReport" + + +class RelatedFabricSemanticModel(RelatedFabric): + """ + Related entity reference for FabricSemanticModel assets. + + Extends RelatedFabric with FabricSemanticModel-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "FabricSemanticModel" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "FabricSemanticModel" + + +class RelatedFabricSemanticModelTable(RelatedFabric): + """ + Related entity reference for FabricSemanticModelTable assets. + + Extends RelatedFabric with FabricSemanticModelTable-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "FabricSemanticModelTable" so it serializes correctly + + fabric_semantic_model_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the Fabric semantic model that contains this asset.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "FabricSemanticModelTable" + + +class RelatedFabricSemanticModelTableColumn(RelatedFabric): + """ + Related entity reference for FabricSemanticModelTableColumn assets. + + Extends RelatedFabric with FabricSemanticModelTableColumn-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "FabricSemanticModelTableColumn" so it serializes correctly + + fabric_semantic_model_table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the Fabric semantic model table that contains this asset.""" + + fabric_semantic_model_table_name: Union[str, None, UnsetType] = UNSET + """Name of the Fabric semantic model table that contains this asset.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "FabricSemanticModelTableColumn" + + +class RelatedFabricPage(RelatedFabric): + """ + Related entity reference for FabricPage assets. + + Extends RelatedFabric with FabricPage-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "FabricPage" so it serializes correctly + + fabric_report_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the Fabric report that contains this asset.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "FabricPage" + + +class RelatedFabricActivity(RelatedFabric): + """ + Related entity reference for FabricActivity assets. + + Extends RelatedFabric with FabricActivity-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "FabricActivity" so it serializes correctly + + fabric_data_pipeline_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the Fabric data pipeline that contains this asset.""" + + fabric_activity_type: Union[str, None, UnsetType] = UNSET + """Type of activity.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "FabricActivity" + + +class RelatedFabricVisual(RelatedFabric): + """ + Related entity reference for FabricVisual assets. + + Extends RelatedFabric with FabricVisual-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "FabricVisual" so it serializes correctly + + fabric_page_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the Fabric page that contains this asset.""" + + fabric_page_name: Union[str, None, UnsetType] = UNSET + """Name of the Fabric page that contains this asset.""" + + fabric_visual_type: Union[str, None, UnsetType] = UNSET + """Type of visual.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "FabricVisual" diff --git a/pyatlan_v9/model/assets/fabric_report.py b/pyatlan_v9/model/assets/fabric_report.py new file mode 100644 index 000000000..040942714 --- /dev/null +++ b/pyatlan_v9/model/assets/fabric_report.py @@ -0,0 +1,593 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +FabricReport asset model with flattened inheritance. + +This module provides: +- FabricReport: Flat asset class (easy to use) +- FabricReportAttributes: Nested attributes struct (extends AssetAttributes) +- FabricReportNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .fabric_related import RelatedFabricPage, RelatedFabricWorkspace + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class FabricReport(Asset): + """ + Instance of a Microsoft Fabric report in Atlan. + """ + + FABRIC_COLUMN_COUNT: ClassVar[Any] = None + FABRIC_DATA_TYPE: ClassVar[Any] = None + FABRIC_ORDINAL: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + FABRIC_WORKSPACE: ClassVar[Any] = None + FABRIC_PAGES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "FabricReport" + + fabric_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this asset.""" + + fabric_data_type: Union[str, None, UnsetType] = UNSET + """Data type of this asset.""" + + fabric_ordinal: Union[int, None, UnsetType] = UNSET + """Order/position of this asset within its parent.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + fabric_workspace: Union[RelatedFabricWorkspace, None, UnsetType] = UNSET + """Workspace containing the report.""" + + fabric_pages: Union[List[RelatedFabricPage], None, UnsetType] = UNSET + """Individual pages contained in the report.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "FabricReport" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _fabric_report_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> FabricReport: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + FabricReport instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _fabric_report_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class FabricReportAttributes(AssetAttributes): + """FabricReport-specific attributes for nested API format.""" + + fabric_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this asset.""" + + fabric_data_type: Union[str, None, UnsetType] = UNSET + """Data type of this asset.""" + + fabric_ordinal: Union[int, None, UnsetType] = UNSET + """Order/position of this asset within its parent.""" + + +class FabricReportRelationshipAttributes(AssetRelationshipAttributes): + """FabricReport-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + fabric_workspace: Union[RelatedFabricWorkspace, None, UnsetType] = UNSET + """Workspace containing the report.""" + + fabric_pages: Union[List[RelatedFabricPage], None, UnsetType] = UNSET + """Individual pages contained in the report.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class FabricReportNested(AssetNested): + """FabricReport in nested API format for high-performance serialization.""" + + attributes: Union[FabricReportAttributes, UnsetType] = UNSET + relationship_attributes: Union[FabricReportRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + FabricReportRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + FabricReportRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_FABRIC_REPORT_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "fabric_workspace", + "fabric_pages", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_fabric_report_attrs( + attrs: FabricReportAttributes, obj: FabricReport +) -> None: + """Populate FabricReport-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.fabric_column_count = obj.fabric_column_count + attrs.fabric_data_type = obj.fabric_data_type + attrs.fabric_ordinal = obj.fabric_ordinal + + +def _extract_fabric_report_attrs(attrs: FabricReportAttributes) -> dict: + """Extract all FabricReport attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["fabric_column_count"] = attrs.fabric_column_count + result["fabric_data_type"] = attrs.fabric_data_type + result["fabric_ordinal"] = attrs.fabric_ordinal + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _fabric_report_to_nested(fabric_report: FabricReport) -> FabricReportNested: + """Convert flat FabricReport to nested format.""" + attrs = FabricReportAttributes() + _populate_fabric_report_attrs(attrs, fabric_report) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + fabric_report, _FABRIC_REPORT_REL_FIELDS, FabricReportRelationshipAttributes + ) + return FabricReportNested( + guid=fabric_report.guid, + type_name=fabric_report.type_name, + status=fabric_report.status, + version=fabric_report.version, + create_time=fabric_report.create_time, + update_time=fabric_report.update_time, + created_by=fabric_report.created_by, + updated_by=fabric_report.updated_by, + classifications=fabric_report.classifications, + classification_names=fabric_report.classification_names, + meanings=fabric_report.meanings, + labels=fabric_report.labels, + business_attributes=fabric_report.business_attributes, + custom_attributes=fabric_report.custom_attributes, + pending_tasks=fabric_report.pending_tasks, + proxy=fabric_report.proxy, + is_incomplete=fabric_report.is_incomplete, + provenance_type=fabric_report.provenance_type, + home_id=fabric_report.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _fabric_report_from_nested(nested: FabricReportNested) -> FabricReport: + """Convert nested format to flat FabricReport.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else FabricReportAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _FABRIC_REPORT_REL_FIELDS, + FabricReportRelationshipAttributes, + ) + return FabricReport( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_fabric_report_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _fabric_report_to_nested_bytes(fabric_report: FabricReport, serde: Serde) -> bytes: + """Convert flat FabricReport to nested JSON bytes.""" + return serde.encode(_fabric_report_to_nested(fabric_report)) + + +def _fabric_report_from_nested_bytes(data: bytes, serde: Serde) -> FabricReport: + """Convert nested JSON bytes to flat FabricReport.""" + nested = serde.decode(data, FabricReportNested) + return _fabric_report_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +FabricReport.FABRIC_COLUMN_COUNT = NumericField( + "fabricColumnCount", "fabricColumnCount" +) +FabricReport.FABRIC_DATA_TYPE = KeywordField("fabricDataType", "fabricDataType") +FabricReport.FABRIC_ORDINAL = NumericField("fabricOrdinal", "fabricOrdinal") +FabricReport.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +FabricReport.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +FabricReport.ANOMALO_CHECKS = RelationField("anomaloChecks") +FabricReport.APPLICATION = RelationField("application") +FabricReport.APPLICATION_FIELD = RelationField("applicationField") +FabricReport.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +FabricReport.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +FabricReport.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +FabricReport.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +FabricReport.METRICS = RelationField("metrics") +FabricReport.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +FabricReport.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +FabricReport.FABRIC_WORKSPACE = RelationField("fabricWorkspace") +FabricReport.FABRIC_PAGES = RelationField("fabricPages") +FabricReport.MEANINGS = RelationField("meanings") +FabricReport.MC_MONITORS = RelationField("mcMonitors") +FabricReport.MC_INCIDENTS = RelationField("mcIncidents") +FabricReport.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +FabricReport.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +FabricReport.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +FabricReport.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +FabricReport.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +FabricReport.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +FabricReport.FILES = RelationField("files") +FabricReport.LINKS = RelationField("links") +FabricReport.README = RelationField("readme") +FabricReport.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +FabricReport.SODA_CHECKS = RelationField("sodaChecks") +FabricReport.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +FabricReport.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/fabric_semantic_model.py b/pyatlan_v9/model/assets/fabric_semantic_model.py new file mode 100644 index 000000000..d81d8defa --- /dev/null +++ b/pyatlan_v9/model/assets/fabric_semantic_model.py @@ -0,0 +1,619 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +FabricSemanticModel asset model with flattened inheritance. + +This module provides: +- FabricSemanticModel: Flat asset class (easy to use) +- FabricSemanticModelAttributes: Nested attributes struct (extends AssetAttributes) +- FabricSemanticModelNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .fabric_related import RelatedFabricSemanticModelTable, RelatedFabricWorkspace + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class FabricSemanticModel(Asset): + """ + Instance of a Microsoft Fabric semantic model in Atlan. + """ + + FABRIC_COLUMN_COUNT: ClassVar[Any] = None + FABRIC_DATA_TYPE: ClassVar[Any] = None + FABRIC_ORDINAL: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + FABRIC_WORKSPACE: ClassVar[Any] = None + FABRIC_SEMANTIC_MODEL_TABLES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "FabricSemanticModel" + + fabric_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this asset.""" + + fabric_data_type: Union[str, None, UnsetType] = UNSET + """Data type of this asset.""" + + fabric_ordinal: Union[int, None, UnsetType] = UNSET + """Order/position of this asset within its parent.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + fabric_workspace: Union[RelatedFabricWorkspace, None, UnsetType] = UNSET + """Workspace containing the semantic model.""" + + fabric_semantic_model_tables: Union[ + List[RelatedFabricSemanticModelTable], None, UnsetType + ] = UNSET + """Individual semantic model tables contained in the semantic model.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "FabricSemanticModel" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _fabric_semantic_model_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> FabricSemanticModel: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + FabricSemanticModel instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _fabric_semantic_model_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class FabricSemanticModelAttributes(AssetAttributes): + """FabricSemanticModel-specific attributes for nested API format.""" + + fabric_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this asset.""" + + fabric_data_type: Union[str, None, UnsetType] = UNSET + """Data type of this asset.""" + + fabric_ordinal: Union[int, None, UnsetType] = UNSET + """Order/position of this asset within its parent.""" + + +class FabricSemanticModelRelationshipAttributes(AssetRelationshipAttributes): + """FabricSemanticModel-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + fabric_workspace: Union[RelatedFabricWorkspace, None, UnsetType] = UNSET + """Workspace containing the semantic model.""" + + fabric_semantic_model_tables: Union[ + List[RelatedFabricSemanticModelTable], None, UnsetType + ] = UNSET + """Individual semantic model tables contained in the semantic model.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class FabricSemanticModelNested(AssetNested): + """FabricSemanticModel in nested API format for high-performance serialization.""" + + attributes: Union[FabricSemanticModelAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + FabricSemanticModelRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + FabricSemanticModelRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + FabricSemanticModelRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_FABRIC_SEMANTIC_MODEL_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "fabric_workspace", + "fabric_semantic_model_tables", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_fabric_semantic_model_attrs( + attrs: FabricSemanticModelAttributes, obj: FabricSemanticModel +) -> None: + """Populate FabricSemanticModel-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.fabric_column_count = obj.fabric_column_count + attrs.fabric_data_type = obj.fabric_data_type + attrs.fabric_ordinal = obj.fabric_ordinal + + +def _extract_fabric_semantic_model_attrs(attrs: FabricSemanticModelAttributes) -> dict: + """Extract all FabricSemanticModel attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["fabric_column_count"] = attrs.fabric_column_count + result["fabric_data_type"] = attrs.fabric_data_type + result["fabric_ordinal"] = attrs.fabric_ordinal + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _fabric_semantic_model_to_nested( + fabric_semantic_model: FabricSemanticModel, +) -> FabricSemanticModelNested: + """Convert flat FabricSemanticModel to nested format.""" + attrs = FabricSemanticModelAttributes() + _populate_fabric_semantic_model_attrs(attrs, fabric_semantic_model) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + fabric_semantic_model, + _FABRIC_SEMANTIC_MODEL_REL_FIELDS, + FabricSemanticModelRelationshipAttributes, + ) + return FabricSemanticModelNested( + guid=fabric_semantic_model.guid, + type_name=fabric_semantic_model.type_name, + status=fabric_semantic_model.status, + version=fabric_semantic_model.version, + create_time=fabric_semantic_model.create_time, + update_time=fabric_semantic_model.update_time, + created_by=fabric_semantic_model.created_by, + updated_by=fabric_semantic_model.updated_by, + classifications=fabric_semantic_model.classifications, + classification_names=fabric_semantic_model.classification_names, + meanings=fabric_semantic_model.meanings, + labels=fabric_semantic_model.labels, + business_attributes=fabric_semantic_model.business_attributes, + custom_attributes=fabric_semantic_model.custom_attributes, + pending_tasks=fabric_semantic_model.pending_tasks, + proxy=fabric_semantic_model.proxy, + is_incomplete=fabric_semantic_model.is_incomplete, + provenance_type=fabric_semantic_model.provenance_type, + home_id=fabric_semantic_model.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _fabric_semantic_model_from_nested( + nested: FabricSemanticModelNested, +) -> FabricSemanticModel: + """Convert nested format to flat FabricSemanticModel.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else FabricSemanticModelAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _FABRIC_SEMANTIC_MODEL_REL_FIELDS, + FabricSemanticModelRelationshipAttributes, + ) + return FabricSemanticModel( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_fabric_semantic_model_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _fabric_semantic_model_to_nested_bytes( + fabric_semantic_model: FabricSemanticModel, serde: Serde +) -> bytes: + """Convert flat FabricSemanticModel to nested JSON bytes.""" + return serde.encode(_fabric_semantic_model_to_nested(fabric_semantic_model)) + + +def _fabric_semantic_model_from_nested_bytes( + data: bytes, serde: Serde +) -> FabricSemanticModel: + """Convert nested JSON bytes to flat FabricSemanticModel.""" + nested = serde.decode(data, FabricSemanticModelNested) + return _fabric_semantic_model_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +FabricSemanticModel.FABRIC_COLUMN_COUNT = NumericField( + "fabricColumnCount", "fabricColumnCount" +) +FabricSemanticModel.FABRIC_DATA_TYPE = KeywordField("fabricDataType", "fabricDataType") +FabricSemanticModel.FABRIC_ORDINAL = NumericField("fabricOrdinal", "fabricOrdinal") +FabricSemanticModel.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +FabricSemanticModel.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +FabricSemanticModel.ANOMALO_CHECKS = RelationField("anomaloChecks") +FabricSemanticModel.APPLICATION = RelationField("application") +FabricSemanticModel.APPLICATION_FIELD = RelationField("applicationField") +FabricSemanticModel.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +FabricSemanticModel.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +FabricSemanticModel.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +FabricSemanticModel.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +FabricSemanticModel.METRICS = RelationField("metrics") +FabricSemanticModel.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +FabricSemanticModel.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +FabricSemanticModel.FABRIC_WORKSPACE = RelationField("fabricWorkspace") +FabricSemanticModel.FABRIC_SEMANTIC_MODEL_TABLES = RelationField( + "fabricSemanticModelTables" +) +FabricSemanticModel.MEANINGS = RelationField("meanings") +FabricSemanticModel.MC_MONITORS = RelationField("mcMonitors") +FabricSemanticModel.MC_INCIDENTS = RelationField("mcIncidents") +FabricSemanticModel.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +FabricSemanticModel.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +FabricSemanticModel.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +FabricSemanticModel.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +FabricSemanticModel.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +FabricSemanticModel.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +FabricSemanticModel.FILES = RelationField("files") +FabricSemanticModel.LINKS = RelationField("links") +FabricSemanticModel.README = RelationField("readme") +FabricSemanticModel.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +FabricSemanticModel.SODA_CHECKS = RelationField("sodaChecks") +FabricSemanticModel.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +FabricSemanticModel.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/fabric_semantic_model_table.py b/pyatlan_v9/model/assets/fabric_semantic_model_table.py new file mode 100644 index 000000000..8cdb7f336 --- /dev/null +++ b/pyatlan_v9/model/assets/fabric_semantic_model_table.py @@ -0,0 +1,656 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +FabricSemanticModelTable asset model with flattened inheritance. + +This module provides: +- FabricSemanticModelTable: Flat asset class (easy to use) +- FabricSemanticModelTableAttributes: Nested attributes struct (extends AssetAttributes) +- FabricSemanticModelTableNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .fabric_related import ( + RelatedFabricSemanticModel, + RelatedFabricSemanticModelTableColumn, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class FabricSemanticModelTable(Asset): + """ + Instance of a Microsoft Fabric semantic model table in Atlan. + """ + + FABRIC_SEMANTIC_MODEL_QUALIFIED_NAME: ClassVar[Any] = None + FABRIC_COLUMN_COUNT: ClassVar[Any] = None + FABRIC_DATA_TYPE: ClassVar[Any] = None + FABRIC_ORDINAL: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + FABRIC_SEMANTIC_MODEL: ClassVar[Any] = None + FABRIC_SEMANTIC_MODEL_TABLE_COLUMNS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "FabricSemanticModelTable" + + fabric_semantic_model_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the Fabric semantic model that contains this asset.""" + + fabric_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this asset.""" + + fabric_data_type: Union[str, None, UnsetType] = UNSET + """Data type of this asset.""" + + fabric_ordinal: Union[int, None, UnsetType] = UNSET + """Order/position of this asset within its parent.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + fabric_semantic_model: Union[RelatedFabricSemanticModel, None, UnsetType] = UNSET + """Semantic model containing the table.""" + + fabric_semantic_model_table_columns: Union[ + List[RelatedFabricSemanticModelTableColumn], None, UnsetType + ] = UNSET + """Individual semantic model table columns contained in the semantic model table.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "FabricSemanticModelTable" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _fabric_semantic_model_table_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> FabricSemanticModelTable: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + FabricSemanticModelTable instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _fabric_semantic_model_table_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class FabricSemanticModelTableAttributes(AssetAttributes): + """FabricSemanticModelTable-specific attributes for nested API format.""" + + fabric_semantic_model_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the Fabric semantic model that contains this asset.""" + + fabric_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this asset.""" + + fabric_data_type: Union[str, None, UnsetType] = UNSET + """Data type of this asset.""" + + fabric_ordinal: Union[int, None, UnsetType] = UNSET + """Order/position of this asset within its parent.""" + + +class FabricSemanticModelTableRelationshipAttributes(AssetRelationshipAttributes): + """FabricSemanticModelTable-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + fabric_semantic_model: Union[RelatedFabricSemanticModel, None, UnsetType] = UNSET + """Semantic model containing the table.""" + + fabric_semantic_model_table_columns: Union[ + List[RelatedFabricSemanticModelTableColumn], None, UnsetType + ] = UNSET + """Individual semantic model table columns contained in the semantic model table.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class FabricSemanticModelTableNested(AssetNested): + """FabricSemanticModelTable in nested API format for high-performance serialization.""" + + attributes: Union[FabricSemanticModelTableAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + FabricSemanticModelTableRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + FabricSemanticModelTableRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + FabricSemanticModelTableRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_FABRIC_SEMANTIC_MODEL_TABLE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "fabric_semantic_model", + "fabric_semantic_model_table_columns", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_fabric_semantic_model_table_attrs( + attrs: FabricSemanticModelTableAttributes, obj: FabricSemanticModelTable +) -> None: + """Populate FabricSemanticModelTable-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.fabric_semantic_model_qualified_name = ( + obj.fabric_semantic_model_qualified_name + ) + attrs.fabric_column_count = obj.fabric_column_count + attrs.fabric_data_type = obj.fabric_data_type + attrs.fabric_ordinal = obj.fabric_ordinal + + +def _extract_fabric_semantic_model_table_attrs( + attrs: FabricSemanticModelTableAttributes, +) -> dict: + """Extract all FabricSemanticModelTable attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["fabric_semantic_model_qualified_name"] = ( + attrs.fabric_semantic_model_qualified_name + ) + result["fabric_column_count"] = attrs.fabric_column_count + result["fabric_data_type"] = attrs.fabric_data_type + result["fabric_ordinal"] = attrs.fabric_ordinal + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _fabric_semantic_model_table_to_nested( + fabric_semantic_model_table: FabricSemanticModelTable, +) -> FabricSemanticModelTableNested: + """Convert flat FabricSemanticModelTable to nested format.""" + attrs = FabricSemanticModelTableAttributes() + _populate_fabric_semantic_model_table_attrs(attrs, fabric_semantic_model_table) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + fabric_semantic_model_table, + _FABRIC_SEMANTIC_MODEL_TABLE_REL_FIELDS, + FabricSemanticModelTableRelationshipAttributes, + ) + return FabricSemanticModelTableNested( + guid=fabric_semantic_model_table.guid, + type_name=fabric_semantic_model_table.type_name, + status=fabric_semantic_model_table.status, + version=fabric_semantic_model_table.version, + create_time=fabric_semantic_model_table.create_time, + update_time=fabric_semantic_model_table.update_time, + created_by=fabric_semantic_model_table.created_by, + updated_by=fabric_semantic_model_table.updated_by, + classifications=fabric_semantic_model_table.classifications, + classification_names=fabric_semantic_model_table.classification_names, + meanings=fabric_semantic_model_table.meanings, + labels=fabric_semantic_model_table.labels, + business_attributes=fabric_semantic_model_table.business_attributes, + custom_attributes=fabric_semantic_model_table.custom_attributes, + pending_tasks=fabric_semantic_model_table.pending_tasks, + proxy=fabric_semantic_model_table.proxy, + is_incomplete=fabric_semantic_model_table.is_incomplete, + provenance_type=fabric_semantic_model_table.provenance_type, + home_id=fabric_semantic_model_table.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _fabric_semantic_model_table_from_nested( + nested: FabricSemanticModelTableNested, +) -> FabricSemanticModelTable: + """Convert nested format to flat FabricSemanticModelTable.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else FabricSemanticModelTableAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _FABRIC_SEMANTIC_MODEL_TABLE_REL_FIELDS, + FabricSemanticModelTableRelationshipAttributes, + ) + return FabricSemanticModelTable( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_fabric_semantic_model_table_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _fabric_semantic_model_table_to_nested_bytes( + fabric_semantic_model_table: FabricSemanticModelTable, serde: Serde +) -> bytes: + """Convert flat FabricSemanticModelTable to nested JSON bytes.""" + return serde.encode( + _fabric_semantic_model_table_to_nested(fabric_semantic_model_table) + ) + + +def _fabric_semantic_model_table_from_nested_bytes( + data: bytes, serde: Serde +) -> FabricSemanticModelTable: + """Convert nested JSON bytes to flat FabricSemanticModelTable.""" + nested = serde.decode(data, FabricSemanticModelTableNested) + return _fabric_semantic_model_table_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +FabricSemanticModelTable.FABRIC_SEMANTIC_MODEL_QUALIFIED_NAME = KeywordField( + "fabricSemanticModelQualifiedName", "fabricSemanticModelQualifiedName" +) +FabricSemanticModelTable.FABRIC_COLUMN_COUNT = NumericField( + "fabricColumnCount", "fabricColumnCount" +) +FabricSemanticModelTable.FABRIC_DATA_TYPE = KeywordField( + "fabricDataType", "fabricDataType" +) +FabricSemanticModelTable.FABRIC_ORDINAL = NumericField("fabricOrdinal", "fabricOrdinal") +FabricSemanticModelTable.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +FabricSemanticModelTable.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +FabricSemanticModelTable.ANOMALO_CHECKS = RelationField("anomaloChecks") +FabricSemanticModelTable.APPLICATION = RelationField("application") +FabricSemanticModelTable.APPLICATION_FIELD = RelationField("applicationField") +FabricSemanticModelTable.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +FabricSemanticModelTable.INPUT_PORT_DATA_PRODUCTS = RelationField( + "inputPortDataProducts" +) +FabricSemanticModelTable.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +FabricSemanticModelTable.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +FabricSemanticModelTable.METRICS = RelationField("metrics") +FabricSemanticModelTable.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +FabricSemanticModelTable.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +FabricSemanticModelTable.FABRIC_SEMANTIC_MODEL = RelationField("fabricSemanticModel") +FabricSemanticModelTable.FABRIC_SEMANTIC_MODEL_TABLE_COLUMNS = RelationField( + "fabricSemanticModelTableColumns" +) +FabricSemanticModelTable.MEANINGS = RelationField("meanings") +FabricSemanticModelTable.MC_MONITORS = RelationField("mcMonitors") +FabricSemanticModelTable.MC_INCIDENTS = RelationField("mcIncidents") +FabricSemanticModelTable.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +FabricSemanticModelTable.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +FabricSemanticModelTable.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +FabricSemanticModelTable.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +FabricSemanticModelTable.USER_DEF_RELATIONSHIP_TO = RelationField( + "userDefRelationshipTo" +) +FabricSemanticModelTable.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +FabricSemanticModelTable.FILES = RelationField("files") +FabricSemanticModelTable.LINKS = RelationField("links") +FabricSemanticModelTable.README = RelationField("readme") +FabricSemanticModelTable.SCHEMA_REGISTRY_SUBJECTS = RelationField( + "schemaRegistrySubjects" +) +FabricSemanticModelTable.SODA_CHECKS = RelationField("sodaChecks") +FabricSemanticModelTable.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +FabricSemanticModelTable.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/fabric_semantic_model_table_column.py b/pyatlan_v9/model/assets/fabric_semantic_model_table_column.py new file mode 100644 index 000000000..c4000ffec --- /dev/null +++ b/pyatlan_v9/model/assets/fabric_semantic_model_table_column.py @@ -0,0 +1,676 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +FabricSemanticModelTableColumn asset model with flattened inheritance. + +This module provides: +- FabricSemanticModelTableColumn: Flat asset class (easy to use) +- FabricSemanticModelTableColumnAttributes: Nested attributes struct (extends AssetAttributes) +- FabricSemanticModelTableColumnNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .fabric_related import RelatedFabricSemanticModelTable + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class FabricSemanticModelTableColumn(Asset): + """ + Instance of a Microsoft Fabric semantic model table column in Atlan. + """ + + FABRIC_SEMANTIC_MODEL_TABLE_QUALIFIED_NAME: ClassVar[Any] = None + FABRIC_SEMANTIC_MODEL_TABLE_NAME: ClassVar[Any] = None + FABRIC_COLUMN_COUNT: ClassVar[Any] = None + FABRIC_DATA_TYPE: ClassVar[Any] = None + FABRIC_ORDINAL: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + FABRIC_SEMANTIC_MODEL_TABLE: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "FabricSemanticModelTableColumn" + + fabric_semantic_model_table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the Fabric semantic model table that contains this asset.""" + + fabric_semantic_model_table_name: Union[str, None, UnsetType] = UNSET + """Name of the Fabric semantic model table that contains this asset.""" + + fabric_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this asset.""" + + fabric_data_type: Union[str, None, UnsetType] = UNSET + """Data type of this asset.""" + + fabric_ordinal: Union[int, None, UnsetType] = UNSET + """Order/position of this asset within its parent.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + fabric_semantic_model_table: Union[ + RelatedFabricSemanticModelTable, None, UnsetType + ] = UNSET + """Semantic model table containing the column.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "FabricSemanticModelTableColumn" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _fabric_semantic_model_table_column_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> FabricSemanticModelTableColumn: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + FabricSemanticModelTableColumn instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _fabric_semantic_model_table_column_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class FabricSemanticModelTableColumnAttributes(AssetAttributes): + """FabricSemanticModelTableColumn-specific attributes for nested API format.""" + + fabric_semantic_model_table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the Fabric semantic model table that contains this asset.""" + + fabric_semantic_model_table_name: Union[str, None, UnsetType] = UNSET + """Name of the Fabric semantic model table that contains this asset.""" + + fabric_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this asset.""" + + fabric_data_type: Union[str, None, UnsetType] = UNSET + """Data type of this asset.""" + + fabric_ordinal: Union[int, None, UnsetType] = UNSET + """Order/position of this asset within its parent.""" + + +class FabricSemanticModelTableColumnRelationshipAttributes(AssetRelationshipAttributes): + """FabricSemanticModelTableColumn-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + fabric_semantic_model_table: Union[ + RelatedFabricSemanticModelTable, None, UnsetType + ] = UNSET + """Semantic model table containing the column.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class FabricSemanticModelTableColumnNested(AssetNested): + """FabricSemanticModelTableColumn in nested API format for high-performance serialization.""" + + attributes: Union[FabricSemanticModelTableColumnAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + FabricSemanticModelTableColumnRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + FabricSemanticModelTableColumnRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + FabricSemanticModelTableColumnRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_FABRIC_SEMANTIC_MODEL_TABLE_COLUMN_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "fabric_semantic_model_table", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_fabric_semantic_model_table_column_attrs( + attrs: FabricSemanticModelTableColumnAttributes, obj: FabricSemanticModelTableColumn +) -> None: + """Populate FabricSemanticModelTableColumn-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.fabric_semantic_model_table_qualified_name = ( + obj.fabric_semantic_model_table_qualified_name + ) + attrs.fabric_semantic_model_table_name = obj.fabric_semantic_model_table_name + attrs.fabric_column_count = obj.fabric_column_count + attrs.fabric_data_type = obj.fabric_data_type + attrs.fabric_ordinal = obj.fabric_ordinal + + +def _extract_fabric_semantic_model_table_column_attrs( + attrs: FabricSemanticModelTableColumnAttributes, +) -> dict: + """Extract all FabricSemanticModelTableColumn attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["fabric_semantic_model_table_qualified_name"] = ( + attrs.fabric_semantic_model_table_qualified_name + ) + result["fabric_semantic_model_table_name"] = attrs.fabric_semantic_model_table_name + result["fabric_column_count"] = attrs.fabric_column_count + result["fabric_data_type"] = attrs.fabric_data_type + result["fabric_ordinal"] = attrs.fabric_ordinal + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _fabric_semantic_model_table_column_to_nested( + fabric_semantic_model_table_column: FabricSemanticModelTableColumn, +) -> FabricSemanticModelTableColumnNested: + """Convert flat FabricSemanticModelTableColumn to nested format.""" + attrs = FabricSemanticModelTableColumnAttributes() + _populate_fabric_semantic_model_table_column_attrs( + attrs, fabric_semantic_model_table_column + ) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + fabric_semantic_model_table_column, + _FABRIC_SEMANTIC_MODEL_TABLE_COLUMN_REL_FIELDS, + FabricSemanticModelTableColumnRelationshipAttributes, + ) + return FabricSemanticModelTableColumnNested( + guid=fabric_semantic_model_table_column.guid, + type_name=fabric_semantic_model_table_column.type_name, + status=fabric_semantic_model_table_column.status, + version=fabric_semantic_model_table_column.version, + create_time=fabric_semantic_model_table_column.create_time, + update_time=fabric_semantic_model_table_column.update_time, + created_by=fabric_semantic_model_table_column.created_by, + updated_by=fabric_semantic_model_table_column.updated_by, + classifications=fabric_semantic_model_table_column.classifications, + classification_names=fabric_semantic_model_table_column.classification_names, + meanings=fabric_semantic_model_table_column.meanings, + labels=fabric_semantic_model_table_column.labels, + business_attributes=fabric_semantic_model_table_column.business_attributes, + custom_attributes=fabric_semantic_model_table_column.custom_attributes, + pending_tasks=fabric_semantic_model_table_column.pending_tasks, + proxy=fabric_semantic_model_table_column.proxy, + is_incomplete=fabric_semantic_model_table_column.is_incomplete, + provenance_type=fabric_semantic_model_table_column.provenance_type, + home_id=fabric_semantic_model_table_column.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _fabric_semantic_model_table_column_from_nested( + nested: FabricSemanticModelTableColumnNested, +) -> FabricSemanticModelTableColumn: + """Convert nested format to flat FabricSemanticModelTableColumn.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else FabricSemanticModelTableColumnAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _FABRIC_SEMANTIC_MODEL_TABLE_COLUMN_REL_FIELDS, + FabricSemanticModelTableColumnRelationshipAttributes, + ) + return FabricSemanticModelTableColumn( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_fabric_semantic_model_table_column_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _fabric_semantic_model_table_column_to_nested_bytes( + fabric_semantic_model_table_column: FabricSemanticModelTableColumn, serde: Serde +) -> bytes: + """Convert flat FabricSemanticModelTableColumn to nested JSON bytes.""" + return serde.encode( + _fabric_semantic_model_table_column_to_nested( + fabric_semantic_model_table_column + ) + ) + + +def _fabric_semantic_model_table_column_from_nested_bytes( + data: bytes, serde: Serde +) -> FabricSemanticModelTableColumn: + """Convert nested JSON bytes to flat FabricSemanticModelTableColumn.""" + nested = serde.decode(data, FabricSemanticModelTableColumnNested) + return _fabric_semantic_model_table_column_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +FabricSemanticModelTableColumn.FABRIC_SEMANTIC_MODEL_TABLE_QUALIFIED_NAME = ( + KeywordField( + "fabricSemanticModelTableQualifiedName", "fabricSemanticModelTableQualifiedName" + ) +) +FabricSemanticModelTableColumn.FABRIC_SEMANTIC_MODEL_TABLE_NAME = KeywordField( + "fabricSemanticModelTableName", "fabricSemanticModelTableName" +) +FabricSemanticModelTableColumn.FABRIC_COLUMN_COUNT = NumericField( + "fabricColumnCount", "fabricColumnCount" +) +FabricSemanticModelTableColumn.FABRIC_DATA_TYPE = KeywordField( + "fabricDataType", "fabricDataType" +) +FabricSemanticModelTableColumn.FABRIC_ORDINAL = NumericField( + "fabricOrdinal", "fabricOrdinal" +) +FabricSemanticModelTableColumn.INPUT_TO_AIRFLOW_TASKS = RelationField( + "inputToAirflowTasks" +) +FabricSemanticModelTableColumn.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +FabricSemanticModelTableColumn.ANOMALO_CHECKS = RelationField("anomaloChecks") +FabricSemanticModelTableColumn.APPLICATION = RelationField("application") +FabricSemanticModelTableColumn.APPLICATION_FIELD = RelationField("applicationField") +FabricSemanticModelTableColumn.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +FabricSemanticModelTableColumn.INPUT_PORT_DATA_PRODUCTS = RelationField( + "inputPortDataProducts" +) +FabricSemanticModelTableColumn.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +FabricSemanticModelTableColumn.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +FabricSemanticModelTableColumn.METRICS = RelationField("metrics") +FabricSemanticModelTableColumn.DQ_BASE_DATASET_RULES = RelationField( + "dqBaseDatasetRules" +) +FabricSemanticModelTableColumn.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +FabricSemanticModelTableColumn.FABRIC_SEMANTIC_MODEL_TABLE = RelationField( + "fabricSemanticModelTable" +) +FabricSemanticModelTableColumn.MEANINGS = RelationField("meanings") +FabricSemanticModelTableColumn.MC_MONITORS = RelationField("mcMonitors") +FabricSemanticModelTableColumn.MC_INCIDENTS = RelationField("mcIncidents") +FabricSemanticModelTableColumn.PARTIAL_CHILD_FIELDS = RelationField( + "partialChildFields" +) +FabricSemanticModelTableColumn.PARTIAL_CHILD_OBJECTS = RelationField( + "partialChildObjects" +) +FabricSemanticModelTableColumn.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +FabricSemanticModelTableColumn.OUTPUT_FROM_PROCESSES = RelationField( + "outputFromProcesses" +) +FabricSemanticModelTableColumn.USER_DEF_RELATIONSHIP_TO = RelationField( + "userDefRelationshipTo" +) +FabricSemanticModelTableColumn.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +FabricSemanticModelTableColumn.FILES = RelationField("files") +FabricSemanticModelTableColumn.LINKS = RelationField("links") +FabricSemanticModelTableColumn.README = RelationField("readme") +FabricSemanticModelTableColumn.SCHEMA_REGISTRY_SUBJECTS = RelationField( + "schemaRegistrySubjects" +) +FabricSemanticModelTableColumn.SODA_CHECKS = RelationField("sodaChecks") +FabricSemanticModelTableColumn.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +FabricSemanticModelTableColumn.OUTPUT_FROM_SPARK_JOBS = RelationField( + "outputFromSparkJobs" +) diff --git a/pyatlan_v9/model/assets/fabric_visual.py b/pyatlan_v9/model/assets/fabric_visual.py new file mode 100644 index 000000000..7a5e973e1 --- /dev/null +++ b/pyatlan_v9/model/assets/fabric_visual.py @@ -0,0 +1,618 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +FabricVisual asset model with flattened inheritance. + +This module provides: +- FabricVisual: Flat asset class (easy to use) +- FabricVisualAttributes: Nested attributes struct (extends AssetAttributes) +- FabricVisualNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .fabric_related import RelatedFabricPage + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class FabricVisual(Asset): + """ + Instance of a Microsoft Fabric visual in Atlan. + """ + + FABRIC_PAGE_QUALIFIED_NAME: ClassVar[Any] = None + FABRIC_PAGE_NAME: ClassVar[Any] = None + FABRIC_VISUAL_TYPE: ClassVar[Any] = None + FABRIC_COLUMN_COUNT: ClassVar[Any] = None + FABRIC_DATA_TYPE: ClassVar[Any] = None + FABRIC_ORDINAL: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + FABRIC_PAGE: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "FabricVisual" + + fabric_page_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the Fabric page that contains this asset.""" + + fabric_page_name: Union[str, None, UnsetType] = UNSET + """Name of the Fabric page that contains this asset.""" + + fabric_visual_type: Union[str, None, UnsetType] = UNSET + """Type of visual.""" + + fabric_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this asset.""" + + fabric_data_type: Union[str, None, UnsetType] = UNSET + """Data type of this asset.""" + + fabric_ordinal: Union[int, None, UnsetType] = UNSET + """Order/position of this asset within its parent.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + fabric_page: Union[RelatedFabricPage, None, UnsetType] = UNSET + """Page containing the visual.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "FabricVisual" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _fabric_visual_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> FabricVisual: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + FabricVisual instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _fabric_visual_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class FabricVisualAttributes(AssetAttributes): + """FabricVisual-specific attributes for nested API format.""" + + fabric_page_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the Fabric page that contains this asset.""" + + fabric_page_name: Union[str, None, UnsetType] = UNSET + """Name of the Fabric page that contains this asset.""" + + fabric_visual_type: Union[str, None, UnsetType] = UNSET + """Type of visual.""" + + fabric_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this asset.""" + + fabric_data_type: Union[str, None, UnsetType] = UNSET + """Data type of this asset.""" + + fabric_ordinal: Union[int, None, UnsetType] = UNSET + """Order/position of this asset within its parent.""" + + +class FabricVisualRelationshipAttributes(AssetRelationshipAttributes): + """FabricVisual-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + fabric_page: Union[RelatedFabricPage, None, UnsetType] = UNSET + """Page containing the visual.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class FabricVisualNested(AssetNested): + """FabricVisual in nested API format for high-performance serialization.""" + + attributes: Union[FabricVisualAttributes, UnsetType] = UNSET + relationship_attributes: Union[FabricVisualRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + FabricVisualRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + FabricVisualRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_FABRIC_VISUAL_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "fabric_page", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_fabric_visual_attrs( + attrs: FabricVisualAttributes, obj: FabricVisual +) -> None: + """Populate FabricVisual-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.fabric_page_qualified_name = obj.fabric_page_qualified_name + attrs.fabric_page_name = obj.fabric_page_name + attrs.fabric_visual_type = obj.fabric_visual_type + attrs.fabric_column_count = obj.fabric_column_count + attrs.fabric_data_type = obj.fabric_data_type + attrs.fabric_ordinal = obj.fabric_ordinal + + +def _extract_fabric_visual_attrs(attrs: FabricVisualAttributes) -> dict: + """Extract all FabricVisual attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["fabric_page_qualified_name"] = attrs.fabric_page_qualified_name + result["fabric_page_name"] = attrs.fabric_page_name + result["fabric_visual_type"] = attrs.fabric_visual_type + result["fabric_column_count"] = attrs.fabric_column_count + result["fabric_data_type"] = attrs.fabric_data_type + result["fabric_ordinal"] = attrs.fabric_ordinal + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _fabric_visual_to_nested(fabric_visual: FabricVisual) -> FabricVisualNested: + """Convert flat FabricVisual to nested format.""" + attrs = FabricVisualAttributes() + _populate_fabric_visual_attrs(attrs, fabric_visual) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + fabric_visual, _FABRIC_VISUAL_REL_FIELDS, FabricVisualRelationshipAttributes + ) + return FabricVisualNested( + guid=fabric_visual.guid, + type_name=fabric_visual.type_name, + status=fabric_visual.status, + version=fabric_visual.version, + create_time=fabric_visual.create_time, + update_time=fabric_visual.update_time, + created_by=fabric_visual.created_by, + updated_by=fabric_visual.updated_by, + classifications=fabric_visual.classifications, + classification_names=fabric_visual.classification_names, + meanings=fabric_visual.meanings, + labels=fabric_visual.labels, + business_attributes=fabric_visual.business_attributes, + custom_attributes=fabric_visual.custom_attributes, + pending_tasks=fabric_visual.pending_tasks, + proxy=fabric_visual.proxy, + is_incomplete=fabric_visual.is_incomplete, + provenance_type=fabric_visual.provenance_type, + home_id=fabric_visual.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _fabric_visual_from_nested(nested: FabricVisualNested) -> FabricVisual: + """Convert nested format to flat FabricVisual.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else FabricVisualAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _FABRIC_VISUAL_REL_FIELDS, + FabricVisualRelationshipAttributes, + ) + return FabricVisual( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_fabric_visual_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _fabric_visual_to_nested_bytes(fabric_visual: FabricVisual, serde: Serde) -> bytes: + """Convert flat FabricVisual to nested JSON bytes.""" + return serde.encode(_fabric_visual_to_nested(fabric_visual)) + + +def _fabric_visual_from_nested_bytes(data: bytes, serde: Serde) -> FabricVisual: + """Convert nested JSON bytes to flat FabricVisual.""" + nested = serde.decode(data, FabricVisualNested) + return _fabric_visual_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +FabricVisual.FABRIC_PAGE_QUALIFIED_NAME = KeywordField( + "fabricPageQualifiedName", "fabricPageQualifiedName" +) +FabricVisual.FABRIC_PAGE_NAME = KeywordField("fabricPageName", "fabricPageName") +FabricVisual.FABRIC_VISUAL_TYPE = KeywordField("fabricVisualType", "fabricVisualType") +FabricVisual.FABRIC_COLUMN_COUNT = NumericField( + "fabricColumnCount", "fabricColumnCount" +) +FabricVisual.FABRIC_DATA_TYPE = KeywordField("fabricDataType", "fabricDataType") +FabricVisual.FABRIC_ORDINAL = NumericField("fabricOrdinal", "fabricOrdinal") +FabricVisual.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +FabricVisual.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +FabricVisual.ANOMALO_CHECKS = RelationField("anomaloChecks") +FabricVisual.APPLICATION = RelationField("application") +FabricVisual.APPLICATION_FIELD = RelationField("applicationField") +FabricVisual.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +FabricVisual.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +FabricVisual.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +FabricVisual.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +FabricVisual.METRICS = RelationField("metrics") +FabricVisual.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +FabricVisual.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +FabricVisual.FABRIC_PAGE = RelationField("fabricPage") +FabricVisual.MEANINGS = RelationField("meanings") +FabricVisual.MC_MONITORS = RelationField("mcMonitors") +FabricVisual.MC_INCIDENTS = RelationField("mcIncidents") +FabricVisual.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +FabricVisual.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +FabricVisual.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +FabricVisual.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +FabricVisual.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +FabricVisual.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +FabricVisual.FILES = RelationField("files") +FabricVisual.LINKS = RelationField("links") +FabricVisual.README = RelationField("readme") +FabricVisual.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +FabricVisual.SODA_CHECKS = RelationField("sodaChecks") +FabricVisual.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +FabricVisual.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/fabric_workspace.py b/pyatlan_v9/model/assets/fabric_workspace.py new file mode 100644 index 000000000..027dff2cc --- /dev/null +++ b/pyatlan_v9/model/assets/fabric_workspace.py @@ -0,0 +1,647 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +FabricWorkspace asset model with flattened inheritance. + +This module provides: +- FabricWorkspace: Flat asset class (easy to use) +- FabricWorkspaceAttributes: Nested attributes struct (extends AssetAttributes) +- FabricWorkspaceNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from .sql_related import RelatedDatabase +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .fabric_related import ( + RelatedFabricDashboard, + RelatedFabricDataPipeline, + RelatedFabricDataflow, + RelatedFabricReport, + RelatedFabricSemanticModel, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class FabricWorkspace(Asset): + """ + Instance of a Microsoft Fabric workspace in Atlan. + """ + + FABRIC_COLUMN_COUNT: ClassVar[Any] = None + FABRIC_DATA_TYPE: ClassVar[Any] = None + FABRIC_ORDINAL: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + FABRIC_DATABASES: ClassVar[Any] = None + FABRIC_DASHBOARDS: ClassVar[Any] = None + FABRIC_DATAFLOWS: ClassVar[Any] = None + FABRIC_DATA_PIPELINES: ClassVar[Any] = None + FABRIC_REPORTS: ClassVar[Any] = None + FABRIC_SEMANTIC_MODELS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "FabricWorkspace" + + fabric_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this asset.""" + + fabric_data_type: Union[str, None, UnsetType] = UNSET + """Data type of this asset.""" + + fabric_ordinal: Union[int, None, UnsetType] = UNSET + """Order/position of this asset within its parent.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + fabric_databases: Union[List[RelatedDatabase], None, UnsetType] = UNSET + """Individual databases contained in the workspace.""" + + fabric_dashboards: Union[List[RelatedFabricDashboard], None, UnsetType] = UNSET + """Individual dashboards contained in the workspace.""" + + fabric_dataflows: Union[List[RelatedFabricDataflow], None, UnsetType] = UNSET + """Individual dataflows contained in the workspace.""" + + fabric_data_pipelines: Union[List[RelatedFabricDataPipeline], None, UnsetType] = ( + UNSET + ) + """Individual data pipelines contained in the workspace.""" + + fabric_reports: Union[List[RelatedFabricReport], None, UnsetType] = UNSET + """Individual reports contained in the workspace.""" + + fabric_semantic_models: Union[List[RelatedFabricSemanticModel], None, UnsetType] = ( + UNSET + ) + """Individual semantic models contained in the workspace.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "FabricWorkspace" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _fabric_workspace_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> FabricWorkspace: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + FabricWorkspace instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _fabric_workspace_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class FabricWorkspaceAttributes(AssetAttributes): + """FabricWorkspace-specific attributes for nested API format.""" + + fabric_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this asset.""" + + fabric_data_type: Union[str, None, UnsetType] = UNSET + """Data type of this asset.""" + + fabric_ordinal: Union[int, None, UnsetType] = UNSET + """Order/position of this asset within its parent.""" + + +class FabricWorkspaceRelationshipAttributes(AssetRelationshipAttributes): + """FabricWorkspace-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + fabric_databases: Union[List[RelatedDatabase], None, UnsetType] = UNSET + """Individual databases contained in the workspace.""" + + fabric_dashboards: Union[List[RelatedFabricDashboard], None, UnsetType] = UNSET + """Individual dashboards contained in the workspace.""" + + fabric_dataflows: Union[List[RelatedFabricDataflow], None, UnsetType] = UNSET + """Individual dataflows contained in the workspace.""" + + fabric_data_pipelines: Union[List[RelatedFabricDataPipeline], None, UnsetType] = ( + UNSET + ) + """Individual data pipelines contained in the workspace.""" + + fabric_reports: Union[List[RelatedFabricReport], None, UnsetType] = UNSET + """Individual reports contained in the workspace.""" + + fabric_semantic_models: Union[List[RelatedFabricSemanticModel], None, UnsetType] = ( + UNSET + ) + """Individual semantic models contained in the workspace.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class FabricWorkspaceNested(AssetNested): + """FabricWorkspace in nested API format for high-performance serialization.""" + + attributes: Union[FabricWorkspaceAttributes, UnsetType] = UNSET + relationship_attributes: Union[FabricWorkspaceRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + FabricWorkspaceRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + FabricWorkspaceRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_FABRIC_WORKSPACE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "fabric_databases", + "fabric_dashboards", + "fabric_dataflows", + "fabric_data_pipelines", + "fabric_reports", + "fabric_semantic_models", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_fabric_workspace_attrs( + attrs: FabricWorkspaceAttributes, obj: FabricWorkspace +) -> None: + """Populate FabricWorkspace-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.fabric_column_count = obj.fabric_column_count + attrs.fabric_data_type = obj.fabric_data_type + attrs.fabric_ordinal = obj.fabric_ordinal + + +def _extract_fabric_workspace_attrs(attrs: FabricWorkspaceAttributes) -> dict: + """Extract all FabricWorkspace attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["fabric_column_count"] = attrs.fabric_column_count + result["fabric_data_type"] = attrs.fabric_data_type + result["fabric_ordinal"] = attrs.fabric_ordinal + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _fabric_workspace_to_nested( + fabric_workspace: FabricWorkspace, +) -> FabricWorkspaceNested: + """Convert flat FabricWorkspace to nested format.""" + attrs = FabricWorkspaceAttributes() + _populate_fabric_workspace_attrs(attrs, fabric_workspace) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + fabric_workspace, + _FABRIC_WORKSPACE_REL_FIELDS, + FabricWorkspaceRelationshipAttributes, + ) + return FabricWorkspaceNested( + guid=fabric_workspace.guid, + type_name=fabric_workspace.type_name, + status=fabric_workspace.status, + version=fabric_workspace.version, + create_time=fabric_workspace.create_time, + update_time=fabric_workspace.update_time, + created_by=fabric_workspace.created_by, + updated_by=fabric_workspace.updated_by, + classifications=fabric_workspace.classifications, + classification_names=fabric_workspace.classification_names, + meanings=fabric_workspace.meanings, + labels=fabric_workspace.labels, + business_attributes=fabric_workspace.business_attributes, + custom_attributes=fabric_workspace.custom_attributes, + pending_tasks=fabric_workspace.pending_tasks, + proxy=fabric_workspace.proxy, + is_incomplete=fabric_workspace.is_incomplete, + provenance_type=fabric_workspace.provenance_type, + home_id=fabric_workspace.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _fabric_workspace_from_nested(nested: FabricWorkspaceNested) -> FabricWorkspace: + """Convert nested format to flat FabricWorkspace.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else FabricWorkspaceAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _FABRIC_WORKSPACE_REL_FIELDS, + FabricWorkspaceRelationshipAttributes, + ) + return FabricWorkspace( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_fabric_workspace_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _fabric_workspace_to_nested_bytes( + fabric_workspace: FabricWorkspace, serde: Serde +) -> bytes: + """Convert flat FabricWorkspace to nested JSON bytes.""" + return serde.encode(_fabric_workspace_to_nested(fabric_workspace)) + + +def _fabric_workspace_from_nested_bytes(data: bytes, serde: Serde) -> FabricWorkspace: + """Convert nested JSON bytes to flat FabricWorkspace.""" + nested = serde.decode(data, FabricWorkspaceNested) + return _fabric_workspace_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +FabricWorkspace.FABRIC_COLUMN_COUNT = NumericField( + "fabricColumnCount", "fabricColumnCount" +) +FabricWorkspace.FABRIC_DATA_TYPE = KeywordField("fabricDataType", "fabricDataType") +FabricWorkspace.FABRIC_ORDINAL = NumericField("fabricOrdinal", "fabricOrdinal") +FabricWorkspace.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +FabricWorkspace.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +FabricWorkspace.ANOMALO_CHECKS = RelationField("anomaloChecks") +FabricWorkspace.APPLICATION = RelationField("application") +FabricWorkspace.APPLICATION_FIELD = RelationField("applicationField") +FabricWorkspace.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +FabricWorkspace.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +FabricWorkspace.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +FabricWorkspace.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +FabricWorkspace.METRICS = RelationField("metrics") +FabricWorkspace.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +FabricWorkspace.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +FabricWorkspace.FABRIC_DATABASES = RelationField("fabricDatabases") +FabricWorkspace.FABRIC_DASHBOARDS = RelationField("fabricDashboards") +FabricWorkspace.FABRIC_DATAFLOWS = RelationField("fabricDataflows") +FabricWorkspace.FABRIC_DATA_PIPELINES = RelationField("fabricDataPipelines") +FabricWorkspace.FABRIC_REPORTS = RelationField("fabricReports") +FabricWorkspace.FABRIC_SEMANTIC_MODELS = RelationField("fabricSemanticModels") +FabricWorkspace.MEANINGS = RelationField("meanings") +FabricWorkspace.MC_MONITORS = RelationField("mcMonitors") +FabricWorkspace.MC_INCIDENTS = RelationField("mcIncidents") +FabricWorkspace.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +FabricWorkspace.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +FabricWorkspace.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +FabricWorkspace.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +FabricWorkspace.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +FabricWorkspace.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +FabricWorkspace.FILES = RelationField("files") +FabricWorkspace.LINKS = RelationField("links") +FabricWorkspace.README = RelationField("readme") +FabricWorkspace.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +FabricWorkspace.SODA_CHECKS = RelationField("sodaChecks") +FabricWorkspace.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +FabricWorkspace.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/file.py b/pyatlan_v9/model/assets/file.py new file mode 100644 index 000000000..2a652a65a --- /dev/null +++ b/pyatlan_v9/model/assets/file.py @@ -0,0 +1,680 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +File asset model with flattened inheritance. + +This module provides: +- File: Flat asset class (easy to use) +- FileAttributes: Nested attributes struct (extends AssetAttributes) +- FileNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .asset_related import RelatedAsset +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .resource_related import RelatedFile, RelatedLink, RelatedReadme + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class File(Asset): + """ + Instance of a static file in Atlan. + """ + + FILE_TYPE: ClassVar[Any] = None + FILE_PATH: ClassVar[Any] = None + LINK: ClassVar[Any] = None + IS_GLOBAL: ClassVar[Any] = None + REFERENCE: ClassVar[Any] = None + RESOURCE_METADATA: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + FILE_ASSETS: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "File" + + file_type: Union[str, None, UnsetType] = UNSET + """Type (extension) of the file.""" + + file_path: Union[str, None, UnsetType] = UNSET + """URL giving the online location where the file can be accessed.""" + + link: Union[str, None, UnsetType] = UNSET + """URL to the resource.""" + + is_global: Union[bool, None, UnsetType] = UNSET + """Whether the resource is global (true) or not (false).""" + + reference: Union[str, None, UnsetType] = UNSET + """Reference to the resource.""" + + resource_metadata: Union[Dict[str, str], None, UnsetType] = UNSET + """Metadata of the resource.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + file_assets: Union[RelatedAsset, None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "File" + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + connection_qualified_name: str, + file_type: str, + ) -> "File": + """ + Create a new File asset. + + Args: + name: Simple name of the file + connection_qualified_name: Unique name of the connection in which this file exists + file_type: Type of the file (e.g., PDF, CSV) + + Returns: + New File instance with all fields populated + + Raises: + ValueError: If required parameters are missing or blank + """ + if isinstance(name, str) and name.strip() == "": + raise ValueError("name cannot be blank") + if ( + isinstance(connection_qualified_name, str) + and connection_qualified_name.strip() == "" + ): + raise ValueError("connection_qualified_name cannot be blank") + if isinstance(file_type, str) and file_type.strip() == "": + raise ValueError("file_type cannot be blank") + validate_required_fields( + ["name", "connection_qualified_name", "file_type"], + [name, connection_qualified_name, file_type], + ) + + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + qualified_name = f"{connection_qualified_name}/{name}" + + return cls( + name=name, + qualified_name=qualified_name, + file_type=file_type, + connector_name=connector_name, + connection_qualified_name=connection_qualified_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "File": + """ + Create a File instance for updating an existing asset. + + Args: + qualified_name: Unique name of the file to update + name: Simple name of the file + + Returns: + File instance configured for updates + + Raises: + ValueError: If required parameters are missing + """ + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "File": + """ + Return a File with only required fields for reference. + + Returns: + File instance with only qualified_name and name set + """ + return File(qualified_name=self.qualified_name, name=self.name) + + @classmethod + def create(cls, **kwargs) -> "File": + """Backward compatibility alias for creator().""" + return cls.creator(**kwargs) + + @classmethod + def create_for_modification(cls, **kwargs) -> "File": + """Backward compatibility alias for updater().""" + return cls.updater(**kwargs) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _file_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> File: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + File instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _file_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class FileAttributes(AssetAttributes): + """File-specific attributes for nested API format.""" + + file_type: Union[str, None, UnsetType] = UNSET + """Type (extension) of the file.""" + + file_path: Union[str, None, UnsetType] = UNSET + """URL giving the online location where the file can be accessed.""" + + link: Union[str, None, UnsetType] = UNSET + """URL to the resource.""" + + is_global: Union[bool, None, UnsetType] = UNSET + """Whether the resource is global (true) or not (false).""" + + reference: Union[str, None, UnsetType] = UNSET + """Reference to the resource.""" + + resource_metadata: Union[Dict[str, str], None, UnsetType] = UNSET + """Metadata of the resource.""" + + +class FileRelationshipAttributes(AssetRelationshipAttributes): + """File-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + file_assets: Union[RelatedAsset, None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class FileNested(AssetNested): + """File in nested API format for high-performance serialization.""" + + attributes: Union[FileAttributes, UnsetType] = UNSET + relationship_attributes: Union[FileRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[FileRelationshipAttributes, UnsetType] = UNSET + remove_relationship_attributes: Union[FileRelationshipAttributes, UnsetType] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_FILE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "file_assets", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_file_attrs(attrs: FileAttributes, obj: File) -> None: + """Populate File-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.file_type = obj.file_type + attrs.file_path = obj.file_path + attrs.link = obj.link + attrs.is_global = obj.is_global + attrs.reference = obj.reference + attrs.resource_metadata = obj.resource_metadata + + +def _extract_file_attrs(attrs: FileAttributes) -> dict: + """Extract all File attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["file_type"] = attrs.file_type + result["file_path"] = attrs.file_path + result["link"] = attrs.link + result["is_global"] = attrs.is_global + result["reference"] = attrs.reference + result["resource_metadata"] = attrs.resource_metadata + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _file_to_nested(file: File) -> FileNested: + """Convert flat File to nested format.""" + attrs = FileAttributes() + _populate_file_attrs(attrs, file) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + file, _FILE_REL_FIELDS, FileRelationshipAttributes + ) + return FileNested( + guid=file.guid, + type_name=file.type_name, + status=file.status, + version=file.version, + create_time=file.create_time, + update_time=file.update_time, + created_by=file.created_by, + updated_by=file.updated_by, + classifications=file.classifications, + classification_names=file.classification_names, + meanings=file.meanings, + labels=file.labels, + business_attributes=file.business_attributes, + custom_attributes=file.custom_attributes, + pending_tasks=file.pending_tasks, + proxy=file.proxy, + is_incomplete=file.is_incomplete, + provenance_type=file.provenance_type, + home_id=file.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _file_from_nested(nested: FileNested) -> File: + """Convert nested format to flat File.""" + attrs = nested.attributes if nested.attributes is not UNSET else FileAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _FILE_REL_FIELDS, + FileRelationshipAttributes, + ) + return File( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_file_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _file_to_nested_bytes(file: File, serde: Serde) -> bytes: + """Convert flat File to nested JSON bytes.""" + return serde.encode(_file_to_nested(file)) + + +def _file_from_nested_bytes(data: bytes, serde: Serde) -> File: + """Convert nested JSON bytes to flat File.""" + nested = serde.decode(data, FileNested) + return _file_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + RelationField, +) + +File.FILE_TYPE = KeywordField("fileType", "fileType") +File.FILE_PATH = KeywordField("filePath", "filePath") +File.LINK = KeywordField("link", "link") +File.IS_GLOBAL = BooleanField("isGlobal", "isGlobal") +File.REFERENCE = KeywordField("reference", "reference") +File.RESOURCE_METADATA = KeywordField("resourceMetadata", "resourceMetadata") +File.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +File.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +File.ANOMALO_CHECKS = RelationField("anomaloChecks") +File.APPLICATION = RelationField("application") +File.APPLICATION_FIELD = RelationField("applicationField") +File.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +File.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +File.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +File.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +File.METRICS = RelationField("metrics") +File.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +File.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +File.MEANINGS = RelationField("meanings") +File.MC_MONITORS = RelationField("mcMonitors") +File.MC_INCIDENTS = RelationField("mcIncidents") +File.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +File.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +File.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +File.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +File.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +File.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +File.FILES = RelationField("files") +File.FILE_ASSETS = RelationField("fileAssets") +File.LINKS = RelationField("links") +File.README = RelationField("readme") +File.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +File.SODA_CHECKS = RelationField("sodaChecks") +File.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +File.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/fivetran.py b/pyatlan_v9/model/assets/fivetran.py new file mode 100644 index 000000000..3aa9ed72f --- /dev/null +++ b/pyatlan_v9/model/assets/fivetran.py @@ -0,0 +1,566 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Fivetran asset model with flattened inheritance. + +This module provides: +- Fivetran: Flat asset class (easy to use) +- FivetranAttributes: Nested attributes struct (extends AssetAttributes) +- FivetranNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Fivetran(Asset): + """ + Base class for Fivetran assets. + """ + + FIVETRAN_WORKFLOW_NAME: ClassVar[Any] = None + FIVETRAN_LAST_SYNC_STATUS: ClassVar[Any] = None + FIVETRAN_LAST_SYNC_RECORDS_UPDATED: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Fivetran" + + fivetran_workflow_name: Union[str, None, UnsetType] = UNSET + """Name of the atlan fivetran workflow that updated this asset""" + + fivetran_last_sync_status: Union[str, None, UnsetType] = UNSET + """Status of the latest sync on Fivetran.""" + + fivetran_last_sync_records_updated: Union[int, None, UnsetType] = UNSET + """Number of records updated in the latest sync on Fivetran""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Fivetran" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _fivetran_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Fivetran: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Fivetran instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _fivetran_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class FivetranAttributes(AssetAttributes): + """Fivetran-specific attributes for nested API format.""" + + fivetran_workflow_name: Union[str, None, UnsetType] = UNSET + """Name of the atlan fivetran workflow that updated this asset""" + + fivetran_last_sync_status: Union[str, None, UnsetType] = UNSET + """Status of the latest sync on Fivetran.""" + + fivetran_last_sync_records_updated: Union[int, None, UnsetType] = UNSET + """Number of records updated in the latest sync on Fivetran""" + + +class FivetranRelationshipAttributes(AssetRelationshipAttributes): + """Fivetran-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class FivetranNested(AssetNested): + """Fivetran in nested API format for high-performance serialization.""" + + attributes: Union[FivetranAttributes, UnsetType] = UNSET + relationship_attributes: Union[FivetranRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[FivetranRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[FivetranRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_FIVETRAN_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_fivetran_attrs(attrs: FivetranAttributes, obj: Fivetran) -> None: + """Populate Fivetran-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.fivetran_workflow_name = obj.fivetran_workflow_name + attrs.fivetran_last_sync_status = obj.fivetran_last_sync_status + attrs.fivetran_last_sync_records_updated = obj.fivetran_last_sync_records_updated + + +def _extract_fivetran_attrs(attrs: FivetranAttributes) -> dict: + """Extract all Fivetran attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["fivetran_workflow_name"] = attrs.fivetran_workflow_name + result["fivetran_last_sync_status"] = attrs.fivetran_last_sync_status + result["fivetran_last_sync_records_updated"] = ( + attrs.fivetran_last_sync_records_updated + ) + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _fivetran_to_nested(fivetran: Fivetran) -> FivetranNested: + """Convert flat Fivetran to nested format.""" + attrs = FivetranAttributes() + _populate_fivetran_attrs(attrs, fivetran) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + fivetran, _FIVETRAN_REL_FIELDS, FivetranRelationshipAttributes + ) + return FivetranNested( + guid=fivetran.guid, + type_name=fivetran.type_name, + status=fivetran.status, + version=fivetran.version, + create_time=fivetran.create_time, + update_time=fivetran.update_time, + created_by=fivetran.created_by, + updated_by=fivetran.updated_by, + classifications=fivetran.classifications, + classification_names=fivetran.classification_names, + meanings=fivetran.meanings, + labels=fivetran.labels, + business_attributes=fivetran.business_attributes, + custom_attributes=fivetran.custom_attributes, + pending_tasks=fivetran.pending_tasks, + proxy=fivetran.proxy, + is_incomplete=fivetran.is_incomplete, + provenance_type=fivetran.provenance_type, + home_id=fivetran.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _fivetran_from_nested(nested: FivetranNested) -> Fivetran: + """Convert nested format to flat Fivetran.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else FivetranAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _FIVETRAN_REL_FIELDS, + FivetranRelationshipAttributes, + ) + return Fivetran( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_fivetran_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _fivetran_to_nested_bytes(fivetran: Fivetran, serde: Serde) -> bytes: + """Convert flat Fivetran to nested JSON bytes.""" + return serde.encode(_fivetran_to_nested(fivetran)) + + +def _fivetran_from_nested_bytes(data: bytes, serde: Serde) -> Fivetran: + """Convert nested JSON bytes to flat Fivetran.""" + nested = serde.decode(data, FivetranNested) + return _fivetran_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +Fivetran.FIVETRAN_WORKFLOW_NAME = KeywordField( + "fivetranWorkflowName", "fivetranWorkflowName" +) +Fivetran.FIVETRAN_LAST_SYNC_STATUS = KeywordField( + "fivetranLastSyncStatus", "fivetranLastSyncStatus" +) +Fivetran.FIVETRAN_LAST_SYNC_RECORDS_UPDATED = NumericField( + "fivetranLastSyncRecordsUpdated", "fivetranLastSyncRecordsUpdated" +) +Fivetran.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Fivetran.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Fivetran.ANOMALO_CHECKS = RelationField("anomaloChecks") +Fivetran.APPLICATION = RelationField("application") +Fivetran.APPLICATION_FIELD = RelationField("applicationField") +Fivetran.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Fivetran.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Fivetran.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Fivetran.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Fivetran.METRICS = RelationField("metrics") +Fivetran.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Fivetran.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Fivetran.MEANINGS = RelationField("meanings") +Fivetran.MC_MONITORS = RelationField("mcMonitors") +Fivetran.MC_INCIDENTS = RelationField("mcIncidents") +Fivetran.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Fivetran.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Fivetran.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Fivetran.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Fivetran.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Fivetran.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Fivetran.FILES = RelationField("files") +Fivetran.LINKS = RelationField("links") +Fivetran.README = RelationField("readme") +Fivetran.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Fivetran.SODA_CHECKS = RelationField("sodaChecks") +Fivetran.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Fivetran.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/fivetran_connector.py b/pyatlan_v9/model/assets/fivetran_connector.py new file mode 100644 index 000000000..a732e74a5 --- /dev/null +++ b/pyatlan_v9/model/assets/fivetran_connector.py @@ -0,0 +1,1162 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +FivetranConnector asset model with flattened inheritance. + +This module provides: +- FivetranConnector: Flat asset class (easy to use) +- FivetranConnectorAttributes: Nested attributes struct (extends AssetAttributes) +- FivetranConnectorNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class FivetranConnector(Asset): + """ + Instance of a Fivetran connector asset in Atlan. + """ + + FIVETRAN_LAST_SYNC_ID: ClassVar[Any] = None + FIVETRAN_LAST_SYNC_STARTED_AT: ClassVar[Any] = None + FIVETRAN_LAST_SYNC_FINISHED_AT: ClassVar[Any] = None + FIVETRAN_LAST_SYNC_REASON: ClassVar[Any] = None + FIVETRAN_LAST_SYNC_TASK_TYPE: ClassVar[Any] = None + FIVETRAN_LAST_SYNC_RESCHEDULED_AT: ClassVar[Any] = None + FIVETRAN_LAST_SYNC_TABLES_SYNCED: ClassVar[Any] = None + FIVETRAN_LAST_SYNC_EXTRACT_TIME_SECONDS: ClassVar[Any] = None + FIVETRAN_LAST_SYNC_EXTRACT_VOLUME_MEGABYTES: ClassVar[Any] = None + FIVETRAN_LAST_SYNC_LOAD_TIME_SECONDS: ClassVar[Any] = None + FIVETRAN_LAST_SYNC_LOAD_VOLUME_MEGABYTES: ClassVar[Any] = None + FIVETRAN_LAST_SYNC_PROCESS_TIME_SECONDS: ClassVar[Any] = None + FIVETRAN_LAST_SYNC_PROCESS_VOLUME_MEGABYTES: ClassVar[Any] = None + FIVETRAN_LAST_SYNC_TOTAL_TIME_SECONDS: ClassVar[Any] = None + FIVETRAN_NAME: ClassVar[Any] = None + FIVETRAN_TYPE: ClassVar[Any] = None + FIVETRAN_URL: ClassVar[Any] = None + FIVETRAN_DESTINATION_NAME: ClassVar[Any] = None + FIVETRAN_DESTINATION_TYPE: ClassVar[Any] = None + FIVETRAN_DESTINATION_URL: ClassVar[Any] = None + FIVETRAN_SYNC_SETUP_ON: ClassVar[Any] = None + FIVETRAN_SYNC_FREQUENCY: ClassVar[Any] = None + FIVETRAN_SYNC_PAUSED: ClassVar[Any] = None + FIVETRAN_SYNC_SETUP_USER_FULL_NAME: ClassVar[Any] = None + FIVETRAN_SYNC_SETUP_USER_EMAIL: ClassVar[Any] = None + FIVETRAN_MONTHLY_ACTIVE_ROWS_FREE: ClassVar[Any] = None + FIVETRAN_MONTHLY_ACTIVE_ROWS_PAID: ClassVar[Any] = None + FIVETRAN_MONTHLY_ACTIVE_ROWS_TOTAL: ClassVar[Any] = None + FIVETRAN_MONTHLY_ACTIVE_ROWS_CHANGE_PERCENTAGE_FREE: ClassVar[Any] = None + FIVETRAN_MONTHLY_ACTIVE_ROWS_CHANGE_PERCENTAGE_PAID: ClassVar[Any] = None + FIVETRAN_MONTHLY_ACTIVE_ROWS_CHANGE_PERCENTAGE_TOTAL: ClassVar[Any] = None + FIVETRAN_MONTHLY_ACTIVE_ROWS_FREE_PERCENTAGE_OF_ACCOUNT: ClassVar[Any] = None + FIVETRAN_MONTHLY_ACTIVE_ROWS_PAID_PERCENTAGE_OF_ACCOUNT: ClassVar[Any] = None + FIVETRAN_MONTHLY_ACTIVE_ROWS_TOTAL_PERCENTAGE_OF_ACCOUNT: ClassVar[Any] = None + FIVETRAN_TOTAL_TABLES_SYNCED: ClassVar[Any] = None + FIVETRAN_CONNECTOR_TOP_TABLES_BY_MAR: ClassVar[Any] = None + FIVETRAN_USAGE_COST: ClassVar[Any] = None + FIVETRAN_CREDITS_USED: ClassVar[Any] = None + FIVETRAN_WORKFLOW_NAME: ClassVar[Any] = None + FIVETRAN_LAST_SYNC_STATUS: ClassVar[Any] = None + FIVETRAN_LAST_SYNC_RECORDS_UPDATED: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + PROCESSES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "FivetranConnector" + + fivetran_last_sync_id: Union[str, None, UnsetType] = UNSET + """ID of the latest sync""" + + fivetran_last_sync_started_at: Union[int, None, UnsetType] = UNSET + """Timestamp (epoch) when the latest sync started on Fivetran, in milliseconds""" + + fivetran_last_sync_finished_at: Union[int, None, UnsetType] = UNSET + """Timestamp (epoch) when the latest sync finished on Fivetran, in milliseconds""" + + fivetran_last_sync_reason: Union[str, None, UnsetType] = UNSET + """Failure reason for the latest sync on Fivetran. If status is FAILURE, this is the description of the reason why the sync failed. If status is FAILURE_WITH_TASK, this is the description of the Error. If status is RESCHEDULED, this is the description of the reason why the sync is rescheduled.""" + + fivetran_last_sync_task_type: Union[str, None, UnsetType] = UNSET + """Failure task type for the latest sync on Fivetran. If status is FAILURE_WITH_TASK or RESCHEDULED, this field displays the type of the Error that caused the failure or rescheduling, respectively, e.g., reconnect, update_service_account, etc.""" + + fivetran_last_sync_rescheduled_at: Union[int, None, UnsetType] = UNSET + """Timestamp (epoch) at which the latest sync is rescheduled at on Fivetran""" + + fivetran_last_sync_tables_synced: Union[int, None, UnsetType] = UNSET + """Number of tables synced in the latest sync on Fivetran""" + + fivetran_last_sync_extract_time_seconds: Union[float, None, UnsetType] = UNSET + """Extract time in seconds in the latest sync on fivetran""" + + fivetran_last_sync_extract_volume_megabytes: Union[float, None, UnsetType] = UNSET + """Extracted data volume in metabytes in the latest sync on Fivetran""" + + fivetran_last_sync_load_time_seconds: Union[float, None, UnsetType] = UNSET + """Load time in seconds in the latest sync on Fivetran""" + + fivetran_last_sync_load_volume_megabytes: Union[float, None, UnsetType] = UNSET + """Loaded data volume in metabytes in the latest sync on Fivetran""" + + fivetran_last_sync_process_time_seconds: Union[float, None, UnsetType] = UNSET + """Process time in seconds in the latest sync on Fivetran""" + + fivetran_last_sync_process_volume_megabytes: Union[float, None, UnsetType] = UNSET + """Process volume in metabytes in the latest sync on Fivetran""" + + fivetran_last_sync_total_time_seconds: Union[float, None, UnsetType] = UNSET + """Total sync time in seconds in the latest sync on Fivetran""" + + fivetran_name: Union[str, None, UnsetType] = UNSET + """Connector name added by the user on Fivetran""" + + fivetran_type: Union[str, None, UnsetType] = UNSET + """Type of connector on Fivetran. Eg: snowflake, google_analytics, notion etc.""" + + fivetran_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="fivetranURL" + ) + """URL to open the connector details on Fivetran""" + + fivetran_destination_name: Union[str, None, UnsetType] = UNSET + """Destination name added by the user on Fivetran""" + + fivetran_destination_type: Union[str, None, UnsetType] = UNSET + """Type of destination on Fivetran. Eg: redshift, bigquery etc.""" + + fivetran_destination_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="fivetranDestinationURL" + ) + """URL to open the destination details on Fivetran""" + + fivetran_sync_setup_on: Union[int, None, UnsetType] = UNSET + """Timestamp (epoch) on which the connector was setup on Fivetran, in milliseconds""" + + fivetran_sync_frequency: Union[str, None, UnsetType] = UNSET + """Sync frequency for the connector in number of hours. Eg: Every 6 hours""" + + fivetran_sync_paused: Union[bool, None, UnsetType] = UNSET + """Boolean to indicate whether the sync for this connector is paused or not""" + + fivetran_sync_setup_user_full_name: Union[str, None, UnsetType] = UNSET + """Full name of the user who setup the connector on Fivetran""" + + fivetran_sync_setup_user_email: Union[str, None, UnsetType] = UNSET + """Email ID of the user who setpu the connector on Fivetran""" + + fivetran_monthly_active_rows_free: Union[int, None, UnsetType] = UNSET + """Free Monthly Active Rows used by the connector in the past month""" + + fivetran_monthly_active_rows_paid: Union[int, None, UnsetType] = UNSET + """Paid Monthly Active Rows used by the connector in the past month""" + + fivetran_monthly_active_rows_total: Union[int, None, UnsetType] = UNSET + """Total Monthly Active Rows used by the connector in the past month""" + + fivetran_monthly_active_rows_change_percentage_free: Union[ + float, None, UnsetType + ] = UNSET + """Increase in the percentage of free MAR compared to the previous month""" + + fivetran_monthly_active_rows_change_percentage_paid: Union[ + float, None, UnsetType + ] = UNSET + """Increase in the percentage of paid MAR compared to the previous month""" + + fivetran_monthly_active_rows_change_percentage_total: Union[ + float, None, UnsetType + ] = UNSET + """Increase in the percentage of total MAR compared to the previous month""" + + fivetran_monthly_active_rows_free_percentage_of_account: Union[ + float, None, UnsetType + ] = UNSET + """Percentage of the account's total free MAR used by this connector""" + + fivetran_monthly_active_rows_paid_percentage_of_account: Union[ + float, None, UnsetType + ] = UNSET + """Percentage of the account's total paid MAR used by this connector""" + + fivetran_monthly_active_rows_total_percentage_of_account: Union[ + float, None, UnsetType + ] = UNSET + """Percentage of the account's total MAR used by this connector""" + + fivetran_total_tables_synced: Union[int, None, UnsetType] = UNSET + """Total number of tables synced by this connector""" + + fivetran_connector_top_tables_by_mar: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="fivetranConnectorTopTablesByMAR" + ) + """Total five tables sorted by MAR synced by this connector""" + + fivetran_usage_cost: Union[float, None, UnsetType] = UNSET + """Total usage cost by this destination""" + + fivetran_credits_used: Union[float, None, UnsetType] = UNSET + """Total credits used by this destination""" + + fivetran_workflow_name: Union[str, None, UnsetType] = UNSET + """Name of the atlan fivetran workflow that updated this asset""" + + fivetran_last_sync_status: Union[str, None, UnsetType] = UNSET + """Status of the latest sync on Fivetran.""" + + fivetran_last_sync_records_updated: Union[int, None, UnsetType] = UNSET + """Number of records updated in the latest sync on Fivetran""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes related to this Fivetran connector""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "FivetranConnector" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _fivetran_connector_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> FivetranConnector: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + FivetranConnector instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _fivetran_connector_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class FivetranConnectorAttributes(AssetAttributes): + """FivetranConnector-specific attributes for nested API format.""" + + fivetran_last_sync_id: Union[str, None, UnsetType] = UNSET + """ID of the latest sync""" + + fivetran_last_sync_started_at: Union[int, None, UnsetType] = UNSET + """Timestamp (epoch) when the latest sync started on Fivetran, in milliseconds""" + + fivetran_last_sync_finished_at: Union[int, None, UnsetType] = UNSET + """Timestamp (epoch) when the latest sync finished on Fivetran, in milliseconds""" + + fivetran_last_sync_reason: Union[str, None, UnsetType] = UNSET + """Failure reason for the latest sync on Fivetran. If status is FAILURE, this is the description of the reason why the sync failed. If status is FAILURE_WITH_TASK, this is the description of the Error. If status is RESCHEDULED, this is the description of the reason why the sync is rescheduled.""" + + fivetran_last_sync_task_type: Union[str, None, UnsetType] = UNSET + """Failure task type for the latest sync on Fivetran. If status is FAILURE_WITH_TASK or RESCHEDULED, this field displays the type of the Error that caused the failure or rescheduling, respectively, e.g., reconnect, update_service_account, etc.""" + + fivetran_last_sync_rescheduled_at: Union[int, None, UnsetType] = UNSET + """Timestamp (epoch) at which the latest sync is rescheduled at on Fivetran""" + + fivetran_last_sync_tables_synced: Union[int, None, UnsetType] = UNSET + """Number of tables synced in the latest sync on Fivetran""" + + fivetran_last_sync_extract_time_seconds: Union[float, None, UnsetType] = UNSET + """Extract time in seconds in the latest sync on fivetran""" + + fivetran_last_sync_extract_volume_megabytes: Union[float, None, UnsetType] = UNSET + """Extracted data volume in metabytes in the latest sync on Fivetran""" + + fivetran_last_sync_load_time_seconds: Union[float, None, UnsetType] = UNSET + """Load time in seconds in the latest sync on Fivetran""" + + fivetran_last_sync_load_volume_megabytes: Union[float, None, UnsetType] = UNSET + """Loaded data volume in metabytes in the latest sync on Fivetran""" + + fivetran_last_sync_process_time_seconds: Union[float, None, UnsetType] = UNSET + """Process time in seconds in the latest sync on Fivetran""" + + fivetran_last_sync_process_volume_megabytes: Union[float, None, UnsetType] = UNSET + """Process volume in metabytes in the latest sync on Fivetran""" + + fivetran_last_sync_total_time_seconds: Union[float, None, UnsetType] = UNSET + """Total sync time in seconds in the latest sync on Fivetran""" + + fivetran_name: Union[str, None, UnsetType] = UNSET + """Connector name added by the user on Fivetran""" + + fivetran_type: Union[str, None, UnsetType] = UNSET + """Type of connector on Fivetran. Eg: snowflake, google_analytics, notion etc.""" + + fivetran_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="fivetranURL" + ) + """URL to open the connector details on Fivetran""" + + fivetran_destination_name: Union[str, None, UnsetType] = UNSET + """Destination name added by the user on Fivetran""" + + fivetran_destination_type: Union[str, None, UnsetType] = UNSET + """Type of destination on Fivetran. Eg: redshift, bigquery etc.""" + + fivetran_destination_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="fivetranDestinationURL" + ) + """URL to open the destination details on Fivetran""" + + fivetran_sync_setup_on: Union[int, None, UnsetType] = UNSET + """Timestamp (epoch) on which the connector was setup on Fivetran, in milliseconds""" + + fivetran_sync_frequency: Union[str, None, UnsetType] = UNSET + """Sync frequency for the connector in number of hours. Eg: Every 6 hours""" + + fivetran_sync_paused: Union[bool, None, UnsetType] = UNSET + """Boolean to indicate whether the sync for this connector is paused or not""" + + fivetran_sync_setup_user_full_name: Union[str, None, UnsetType] = UNSET + """Full name of the user who setup the connector on Fivetran""" + + fivetran_sync_setup_user_email: Union[str, None, UnsetType] = UNSET + """Email ID of the user who setpu the connector on Fivetran""" + + fivetran_monthly_active_rows_free: Union[int, None, UnsetType] = UNSET + """Free Monthly Active Rows used by the connector in the past month""" + + fivetran_monthly_active_rows_paid: Union[int, None, UnsetType] = UNSET + """Paid Monthly Active Rows used by the connector in the past month""" + + fivetran_monthly_active_rows_total: Union[int, None, UnsetType] = UNSET + """Total Monthly Active Rows used by the connector in the past month""" + + fivetran_monthly_active_rows_change_percentage_free: Union[ + float, None, UnsetType + ] = UNSET + """Increase in the percentage of free MAR compared to the previous month""" + + fivetran_monthly_active_rows_change_percentage_paid: Union[ + float, None, UnsetType + ] = UNSET + """Increase in the percentage of paid MAR compared to the previous month""" + + fivetran_monthly_active_rows_change_percentage_total: Union[ + float, None, UnsetType + ] = UNSET + """Increase in the percentage of total MAR compared to the previous month""" + + fivetran_monthly_active_rows_free_percentage_of_account: Union[ + float, None, UnsetType + ] = UNSET + """Percentage of the account's total free MAR used by this connector""" + + fivetran_monthly_active_rows_paid_percentage_of_account: Union[ + float, None, UnsetType + ] = UNSET + """Percentage of the account's total paid MAR used by this connector""" + + fivetran_monthly_active_rows_total_percentage_of_account: Union[ + float, None, UnsetType + ] = UNSET + """Percentage of the account's total MAR used by this connector""" + + fivetran_total_tables_synced: Union[int, None, UnsetType] = UNSET + """Total number of tables synced by this connector""" + + fivetran_connector_top_tables_by_mar: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="fivetranConnectorTopTablesByMAR" + ) + """Total five tables sorted by MAR synced by this connector""" + + fivetran_usage_cost: Union[float, None, UnsetType] = UNSET + """Total usage cost by this destination""" + + fivetran_credits_used: Union[float, None, UnsetType] = UNSET + """Total credits used by this destination""" + + fivetran_workflow_name: Union[str, None, UnsetType] = UNSET + """Name of the atlan fivetran workflow that updated this asset""" + + fivetran_last_sync_status: Union[str, None, UnsetType] = UNSET + """Status of the latest sync on Fivetran.""" + + fivetran_last_sync_records_updated: Union[int, None, UnsetType] = UNSET + """Number of records updated in the latest sync on Fivetran""" + + +class FivetranConnectorRelationshipAttributes(AssetRelationshipAttributes): + """FivetranConnector-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes related to this Fivetran connector""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class FivetranConnectorNested(AssetNested): + """FivetranConnector in nested API format for high-performance serialization.""" + + attributes: Union[FivetranConnectorAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + FivetranConnectorRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + FivetranConnectorRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + FivetranConnectorRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_FIVETRAN_CONNECTOR_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "processes", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_fivetran_connector_attrs( + attrs: FivetranConnectorAttributes, obj: FivetranConnector +) -> None: + """Populate FivetranConnector-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.fivetran_last_sync_id = obj.fivetran_last_sync_id + attrs.fivetran_last_sync_started_at = obj.fivetran_last_sync_started_at + attrs.fivetran_last_sync_finished_at = obj.fivetran_last_sync_finished_at + attrs.fivetran_last_sync_reason = obj.fivetran_last_sync_reason + attrs.fivetran_last_sync_task_type = obj.fivetran_last_sync_task_type + attrs.fivetran_last_sync_rescheduled_at = obj.fivetran_last_sync_rescheduled_at + attrs.fivetran_last_sync_tables_synced = obj.fivetran_last_sync_tables_synced + attrs.fivetran_last_sync_extract_time_seconds = ( + obj.fivetran_last_sync_extract_time_seconds + ) + attrs.fivetran_last_sync_extract_volume_megabytes = ( + obj.fivetran_last_sync_extract_volume_megabytes + ) + attrs.fivetran_last_sync_load_time_seconds = ( + obj.fivetran_last_sync_load_time_seconds + ) + attrs.fivetran_last_sync_load_volume_megabytes = ( + obj.fivetran_last_sync_load_volume_megabytes + ) + attrs.fivetran_last_sync_process_time_seconds = ( + obj.fivetran_last_sync_process_time_seconds + ) + attrs.fivetran_last_sync_process_volume_megabytes = ( + obj.fivetran_last_sync_process_volume_megabytes + ) + attrs.fivetran_last_sync_total_time_seconds = ( + obj.fivetran_last_sync_total_time_seconds + ) + attrs.fivetran_name = obj.fivetran_name + attrs.fivetran_type = obj.fivetran_type + attrs.fivetran_url = obj.fivetran_url + attrs.fivetran_destination_name = obj.fivetran_destination_name + attrs.fivetran_destination_type = obj.fivetran_destination_type + attrs.fivetran_destination_url = obj.fivetran_destination_url + attrs.fivetran_sync_setup_on = obj.fivetran_sync_setup_on + attrs.fivetran_sync_frequency = obj.fivetran_sync_frequency + attrs.fivetran_sync_paused = obj.fivetran_sync_paused + attrs.fivetran_sync_setup_user_full_name = obj.fivetran_sync_setup_user_full_name + attrs.fivetran_sync_setup_user_email = obj.fivetran_sync_setup_user_email + attrs.fivetran_monthly_active_rows_free = obj.fivetran_monthly_active_rows_free + attrs.fivetran_monthly_active_rows_paid = obj.fivetran_monthly_active_rows_paid + attrs.fivetran_monthly_active_rows_total = obj.fivetran_monthly_active_rows_total + attrs.fivetran_monthly_active_rows_change_percentage_free = ( + obj.fivetran_monthly_active_rows_change_percentage_free + ) + attrs.fivetran_monthly_active_rows_change_percentage_paid = ( + obj.fivetran_monthly_active_rows_change_percentage_paid + ) + attrs.fivetran_monthly_active_rows_change_percentage_total = ( + obj.fivetran_monthly_active_rows_change_percentage_total + ) + attrs.fivetran_monthly_active_rows_free_percentage_of_account = ( + obj.fivetran_monthly_active_rows_free_percentage_of_account + ) + attrs.fivetran_monthly_active_rows_paid_percentage_of_account = ( + obj.fivetran_monthly_active_rows_paid_percentage_of_account + ) + attrs.fivetran_monthly_active_rows_total_percentage_of_account = ( + obj.fivetran_monthly_active_rows_total_percentage_of_account + ) + attrs.fivetran_total_tables_synced = obj.fivetran_total_tables_synced + attrs.fivetran_connector_top_tables_by_mar = ( + obj.fivetran_connector_top_tables_by_mar + ) + attrs.fivetran_usage_cost = obj.fivetran_usage_cost + attrs.fivetran_credits_used = obj.fivetran_credits_used + attrs.fivetran_workflow_name = obj.fivetran_workflow_name + attrs.fivetran_last_sync_status = obj.fivetran_last_sync_status + attrs.fivetran_last_sync_records_updated = obj.fivetran_last_sync_records_updated + + +def _extract_fivetran_connector_attrs(attrs: FivetranConnectorAttributes) -> dict: + """Extract all FivetranConnector attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["fivetran_last_sync_id"] = attrs.fivetran_last_sync_id + result["fivetran_last_sync_started_at"] = attrs.fivetran_last_sync_started_at + result["fivetran_last_sync_finished_at"] = attrs.fivetran_last_sync_finished_at + result["fivetran_last_sync_reason"] = attrs.fivetran_last_sync_reason + result["fivetran_last_sync_task_type"] = attrs.fivetran_last_sync_task_type + result["fivetran_last_sync_rescheduled_at"] = ( + attrs.fivetran_last_sync_rescheduled_at + ) + result["fivetran_last_sync_tables_synced"] = attrs.fivetran_last_sync_tables_synced + result["fivetran_last_sync_extract_time_seconds"] = ( + attrs.fivetran_last_sync_extract_time_seconds + ) + result["fivetran_last_sync_extract_volume_megabytes"] = ( + attrs.fivetran_last_sync_extract_volume_megabytes + ) + result["fivetran_last_sync_load_time_seconds"] = ( + attrs.fivetran_last_sync_load_time_seconds + ) + result["fivetran_last_sync_load_volume_megabytes"] = ( + attrs.fivetran_last_sync_load_volume_megabytes + ) + result["fivetran_last_sync_process_time_seconds"] = ( + attrs.fivetran_last_sync_process_time_seconds + ) + result["fivetran_last_sync_process_volume_megabytes"] = ( + attrs.fivetran_last_sync_process_volume_megabytes + ) + result["fivetran_last_sync_total_time_seconds"] = ( + attrs.fivetran_last_sync_total_time_seconds + ) + result["fivetran_name"] = attrs.fivetran_name + result["fivetran_type"] = attrs.fivetran_type + result["fivetran_url"] = attrs.fivetran_url + result["fivetran_destination_name"] = attrs.fivetran_destination_name + result["fivetran_destination_type"] = attrs.fivetran_destination_type + result["fivetran_destination_url"] = attrs.fivetran_destination_url + result["fivetran_sync_setup_on"] = attrs.fivetran_sync_setup_on + result["fivetran_sync_frequency"] = attrs.fivetran_sync_frequency + result["fivetran_sync_paused"] = attrs.fivetran_sync_paused + result["fivetran_sync_setup_user_full_name"] = ( + attrs.fivetran_sync_setup_user_full_name + ) + result["fivetran_sync_setup_user_email"] = attrs.fivetran_sync_setup_user_email + result["fivetran_monthly_active_rows_free"] = ( + attrs.fivetran_monthly_active_rows_free + ) + result["fivetran_monthly_active_rows_paid"] = ( + attrs.fivetran_monthly_active_rows_paid + ) + result["fivetran_monthly_active_rows_total"] = ( + attrs.fivetran_monthly_active_rows_total + ) + result["fivetran_monthly_active_rows_change_percentage_free"] = ( + attrs.fivetran_monthly_active_rows_change_percentage_free + ) + result["fivetran_monthly_active_rows_change_percentage_paid"] = ( + attrs.fivetran_monthly_active_rows_change_percentage_paid + ) + result["fivetran_monthly_active_rows_change_percentage_total"] = ( + attrs.fivetran_monthly_active_rows_change_percentage_total + ) + result["fivetran_monthly_active_rows_free_percentage_of_account"] = ( + attrs.fivetran_monthly_active_rows_free_percentage_of_account + ) + result["fivetran_monthly_active_rows_paid_percentage_of_account"] = ( + attrs.fivetran_monthly_active_rows_paid_percentage_of_account + ) + result["fivetran_monthly_active_rows_total_percentage_of_account"] = ( + attrs.fivetran_monthly_active_rows_total_percentage_of_account + ) + result["fivetran_total_tables_synced"] = attrs.fivetran_total_tables_synced + result["fivetran_connector_top_tables_by_mar"] = ( + attrs.fivetran_connector_top_tables_by_mar + ) + result["fivetran_usage_cost"] = attrs.fivetran_usage_cost + result["fivetran_credits_used"] = attrs.fivetran_credits_used + result["fivetran_workflow_name"] = attrs.fivetran_workflow_name + result["fivetran_last_sync_status"] = attrs.fivetran_last_sync_status + result["fivetran_last_sync_records_updated"] = ( + attrs.fivetran_last_sync_records_updated + ) + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _fivetran_connector_to_nested( + fivetran_connector: FivetranConnector, +) -> FivetranConnectorNested: + """Convert flat FivetranConnector to nested format.""" + attrs = FivetranConnectorAttributes() + _populate_fivetran_connector_attrs(attrs, fivetran_connector) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + fivetran_connector, + _FIVETRAN_CONNECTOR_REL_FIELDS, + FivetranConnectorRelationshipAttributes, + ) + return FivetranConnectorNested( + guid=fivetran_connector.guid, + type_name=fivetran_connector.type_name, + status=fivetran_connector.status, + version=fivetran_connector.version, + create_time=fivetran_connector.create_time, + update_time=fivetran_connector.update_time, + created_by=fivetran_connector.created_by, + updated_by=fivetran_connector.updated_by, + classifications=fivetran_connector.classifications, + classification_names=fivetran_connector.classification_names, + meanings=fivetran_connector.meanings, + labels=fivetran_connector.labels, + business_attributes=fivetran_connector.business_attributes, + custom_attributes=fivetran_connector.custom_attributes, + pending_tasks=fivetran_connector.pending_tasks, + proxy=fivetran_connector.proxy, + is_incomplete=fivetran_connector.is_incomplete, + provenance_type=fivetran_connector.provenance_type, + home_id=fivetran_connector.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _fivetran_connector_from_nested( + nested: FivetranConnectorNested, +) -> FivetranConnector: + """Convert nested format to flat FivetranConnector.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else FivetranConnectorAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _FIVETRAN_CONNECTOR_REL_FIELDS, + FivetranConnectorRelationshipAttributes, + ) + return FivetranConnector( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_fivetran_connector_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _fivetran_connector_to_nested_bytes( + fivetran_connector: FivetranConnector, serde: Serde +) -> bytes: + """Convert flat FivetranConnector to nested JSON bytes.""" + return serde.encode(_fivetran_connector_to_nested(fivetran_connector)) + + +def _fivetran_connector_from_nested_bytes( + data: bytes, serde: Serde +) -> FivetranConnector: + """Convert nested JSON bytes to flat FivetranConnector.""" + nested = serde.decode(data, FivetranConnectorNested) + return _fivetran_connector_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +FivetranConnector.FIVETRAN_LAST_SYNC_ID = KeywordField( + "fivetranLastSyncId", "fivetranLastSyncId" +) +FivetranConnector.FIVETRAN_LAST_SYNC_STARTED_AT = NumericField( + "fivetranLastSyncStartedAt", "fivetranLastSyncStartedAt" +) +FivetranConnector.FIVETRAN_LAST_SYNC_FINISHED_AT = NumericField( + "fivetranLastSyncFinishedAt", "fivetranLastSyncFinishedAt" +) +FivetranConnector.FIVETRAN_LAST_SYNC_REASON = KeywordTextField( + "fivetranLastSyncReason", "fivetranLastSyncReason", "fivetranLastSyncReason.text" +) +FivetranConnector.FIVETRAN_LAST_SYNC_TASK_TYPE = KeywordField( + "fivetranLastSyncTaskType", "fivetranLastSyncTaskType" +) +FivetranConnector.FIVETRAN_LAST_SYNC_RESCHEDULED_AT = NumericField( + "fivetranLastSyncRescheduledAt", "fivetranLastSyncRescheduledAt" +) +FivetranConnector.FIVETRAN_LAST_SYNC_TABLES_SYNCED = NumericField( + "fivetranLastSyncTablesSynced", "fivetranLastSyncTablesSynced" +) +FivetranConnector.FIVETRAN_LAST_SYNC_EXTRACT_TIME_SECONDS = NumericField( + "fivetranLastSyncExtractTimeSeconds", "fivetranLastSyncExtractTimeSeconds" +) +FivetranConnector.FIVETRAN_LAST_SYNC_EXTRACT_VOLUME_MEGABYTES = NumericField( + "fivetranLastSyncExtractVolumeMegabytes", "fivetranLastSyncExtractVolumeMegabytes" +) +FivetranConnector.FIVETRAN_LAST_SYNC_LOAD_TIME_SECONDS = NumericField( + "fivetranLastSyncLoadTimeSeconds", "fivetranLastSyncLoadTimeSeconds" +) +FivetranConnector.FIVETRAN_LAST_SYNC_LOAD_VOLUME_MEGABYTES = NumericField( + "fivetranLastSyncLoadVolumeMegabytes", "fivetranLastSyncLoadVolumeMegabytes" +) +FivetranConnector.FIVETRAN_LAST_SYNC_PROCESS_TIME_SECONDS = NumericField( + "fivetranLastSyncProcessTimeSeconds", "fivetranLastSyncProcessTimeSeconds" +) +FivetranConnector.FIVETRAN_LAST_SYNC_PROCESS_VOLUME_MEGABYTES = NumericField( + "fivetranLastSyncProcessVolumeMegabytes", "fivetranLastSyncProcessVolumeMegabytes" +) +FivetranConnector.FIVETRAN_LAST_SYNC_TOTAL_TIME_SECONDS = NumericField( + "fivetranLastSyncTotalTimeSeconds", "fivetranLastSyncTotalTimeSeconds" +) +FivetranConnector.FIVETRAN_NAME = KeywordField("fivetranName", "fivetranName") +FivetranConnector.FIVETRAN_TYPE = KeywordField("fivetranType", "fivetranType") +FivetranConnector.FIVETRAN_URL = KeywordField("fivetranURL", "fivetranURL") +FivetranConnector.FIVETRAN_DESTINATION_NAME = KeywordField( + "fivetranDestinationName", "fivetranDestinationName" +) +FivetranConnector.FIVETRAN_DESTINATION_TYPE = KeywordField( + "fivetranDestinationType", "fivetranDestinationType" +) +FivetranConnector.FIVETRAN_DESTINATION_URL = KeywordField( + "fivetranDestinationURL", "fivetranDestinationURL" +) +FivetranConnector.FIVETRAN_SYNC_SETUP_ON = NumericField( + "fivetranSyncSetupOn", "fivetranSyncSetupOn" +) +FivetranConnector.FIVETRAN_SYNC_FREQUENCY = KeywordField( + "fivetranSyncFrequency", "fivetranSyncFrequency" +) +FivetranConnector.FIVETRAN_SYNC_PAUSED = BooleanField( + "fivetranSyncPaused", "fivetranSyncPaused" +) +FivetranConnector.FIVETRAN_SYNC_SETUP_USER_FULL_NAME = KeywordField( + "fivetranSyncSetupUserFullName", "fivetranSyncSetupUserFullName" +) +FivetranConnector.FIVETRAN_SYNC_SETUP_USER_EMAIL = KeywordField( + "fivetranSyncSetupUserEmail", "fivetranSyncSetupUserEmail" +) +FivetranConnector.FIVETRAN_MONTHLY_ACTIVE_ROWS_FREE = NumericField( + "fivetranMonthlyActiveRowsFree", "fivetranMonthlyActiveRowsFree" +) +FivetranConnector.FIVETRAN_MONTHLY_ACTIVE_ROWS_PAID = NumericField( + "fivetranMonthlyActiveRowsPaid", "fivetranMonthlyActiveRowsPaid" +) +FivetranConnector.FIVETRAN_MONTHLY_ACTIVE_ROWS_TOTAL = NumericField( + "fivetranMonthlyActiveRowsTotal", "fivetranMonthlyActiveRowsTotal" +) +FivetranConnector.FIVETRAN_MONTHLY_ACTIVE_ROWS_CHANGE_PERCENTAGE_FREE = NumericField( + "fivetranMonthlyActiveRowsChangePercentageFree", + "fivetranMonthlyActiveRowsChangePercentageFree", +) +FivetranConnector.FIVETRAN_MONTHLY_ACTIVE_ROWS_CHANGE_PERCENTAGE_PAID = NumericField( + "fivetranMonthlyActiveRowsChangePercentagePaid", + "fivetranMonthlyActiveRowsChangePercentagePaid", +) +FivetranConnector.FIVETRAN_MONTHLY_ACTIVE_ROWS_CHANGE_PERCENTAGE_TOTAL = NumericField( + "fivetranMonthlyActiveRowsChangePercentageTotal", + "fivetranMonthlyActiveRowsChangePercentageTotal", +) +FivetranConnector.FIVETRAN_MONTHLY_ACTIVE_ROWS_FREE_PERCENTAGE_OF_ACCOUNT = ( + NumericField( + "fivetranMonthlyActiveRowsFreePercentageOfAccount", + "fivetranMonthlyActiveRowsFreePercentageOfAccount", + ) +) +FivetranConnector.FIVETRAN_MONTHLY_ACTIVE_ROWS_PAID_PERCENTAGE_OF_ACCOUNT = ( + NumericField( + "fivetranMonthlyActiveRowsPaidPercentageOfAccount", + "fivetranMonthlyActiveRowsPaidPercentageOfAccount", + ) +) +FivetranConnector.FIVETRAN_MONTHLY_ACTIVE_ROWS_TOTAL_PERCENTAGE_OF_ACCOUNT = ( + NumericField( + "fivetranMonthlyActiveRowsTotalPercentageOfAccount", + "fivetranMonthlyActiveRowsTotalPercentageOfAccount", + ) +) +FivetranConnector.FIVETRAN_TOTAL_TABLES_SYNCED = NumericField( + "fivetranTotalTablesSynced", "fivetranTotalTablesSynced" +) +FivetranConnector.FIVETRAN_CONNECTOR_TOP_TABLES_BY_MAR = KeywordField( + "fivetranConnectorTopTablesByMAR", "fivetranConnectorTopTablesByMAR" +) +FivetranConnector.FIVETRAN_USAGE_COST = NumericField( + "fivetranUsageCost", "fivetranUsageCost" +) +FivetranConnector.FIVETRAN_CREDITS_USED = NumericField( + "fivetranCreditsUsed", "fivetranCreditsUsed" +) +FivetranConnector.FIVETRAN_WORKFLOW_NAME = KeywordField( + "fivetranWorkflowName", "fivetranWorkflowName" +) +FivetranConnector.FIVETRAN_LAST_SYNC_STATUS = KeywordField( + "fivetranLastSyncStatus", "fivetranLastSyncStatus" +) +FivetranConnector.FIVETRAN_LAST_SYNC_RECORDS_UPDATED = NumericField( + "fivetranLastSyncRecordsUpdated", "fivetranLastSyncRecordsUpdated" +) +FivetranConnector.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +FivetranConnector.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +FivetranConnector.ANOMALO_CHECKS = RelationField("anomaloChecks") +FivetranConnector.APPLICATION = RelationField("application") +FivetranConnector.APPLICATION_FIELD = RelationField("applicationField") +FivetranConnector.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +FivetranConnector.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +FivetranConnector.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +FivetranConnector.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +FivetranConnector.METRICS = RelationField("metrics") +FivetranConnector.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +FivetranConnector.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +FivetranConnector.PROCESSES = RelationField("processes") +FivetranConnector.MEANINGS = RelationField("meanings") +FivetranConnector.MC_MONITORS = RelationField("mcMonitors") +FivetranConnector.MC_INCIDENTS = RelationField("mcIncidents") +FivetranConnector.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +FivetranConnector.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +FivetranConnector.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +FivetranConnector.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +FivetranConnector.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +FivetranConnector.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +FivetranConnector.FILES = RelationField("files") +FivetranConnector.LINKS = RelationField("links") +FivetranConnector.README = RelationField("readme") +FivetranConnector.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +FivetranConnector.SODA_CHECKS = RelationField("sodaChecks") +FivetranConnector.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +FivetranConnector.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/fivetran_related.py b/pyatlan_v9/model/assets/fivetran_related.py new file mode 100644 index 000000000..16f104819 --- /dev/null +++ b/pyatlan_v9/model/assets/fivetran_related.py @@ -0,0 +1,196 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Fivetran module. + +This module contains all Related{Type} classes for the Fivetran type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedCatalog +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedFivetran", + "RelatedFivetranConnector", +] + + +class RelatedFivetran(RelatedCatalog): + """ + Related entity reference for Fivetran assets. + + Extends RelatedCatalog with Fivetran-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Fivetran" so it serializes correctly + + fivetran_workflow_name: Union[str, None, UnsetType] = UNSET + """Name of the atlan fivetran workflow that updated this asset""" + + fivetran_last_sync_status: Union[str, None, UnsetType] = UNSET + """Status of the latest sync on Fivetran.""" + + fivetran_last_sync_records_updated: Union[int, None, UnsetType] = UNSET + """Number of records updated in the latest sync on Fivetran""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Fivetran" + + +class RelatedFivetranConnector(RelatedFivetran): + """ + Related entity reference for FivetranConnector assets. + + Extends RelatedFivetran with FivetranConnector-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "FivetranConnector" so it serializes correctly + + fivetran_last_sync_id: Union[str, None, UnsetType] = UNSET + """ID of the latest sync""" + + fivetran_last_sync_started_at: Union[int, None, UnsetType] = UNSET + """Timestamp (epoch) when the latest sync started on Fivetran, in milliseconds""" + + fivetran_last_sync_finished_at: Union[int, None, UnsetType] = UNSET + """Timestamp (epoch) when the latest sync finished on Fivetran, in milliseconds""" + + fivetran_last_sync_reason: Union[str, None, UnsetType] = UNSET + """Failure reason for the latest sync on Fivetran. If status is FAILURE, this is the description of the reason why the sync failed. If status is FAILURE_WITH_TASK, this is the description of the Error. If status is RESCHEDULED, this is the description of the reason why the sync is rescheduled.""" + + fivetran_last_sync_task_type: Union[str, None, UnsetType] = UNSET + """Failure task type for the latest sync on Fivetran. If status is FAILURE_WITH_TASK or RESCHEDULED, this field displays the type of the Error that caused the failure or rescheduling, respectively, e.g., reconnect, update_service_account, etc.""" + + fivetran_last_sync_rescheduled_at: Union[int, None, UnsetType] = UNSET + """Timestamp (epoch) at which the latest sync is rescheduled at on Fivetran""" + + fivetran_last_sync_tables_synced: Union[int, None, UnsetType] = UNSET + """Number of tables synced in the latest sync on Fivetran""" + + fivetran_last_sync_extract_time_seconds: Union[float, None, UnsetType] = UNSET + """Extract time in seconds in the latest sync on fivetran""" + + fivetran_last_sync_extract_volume_megabytes: Union[float, None, UnsetType] = UNSET + """Extracted data volume in metabytes in the latest sync on Fivetran""" + + fivetran_last_sync_load_time_seconds: Union[float, None, UnsetType] = UNSET + """Load time in seconds in the latest sync on Fivetran""" + + fivetran_last_sync_load_volume_megabytes: Union[float, None, UnsetType] = UNSET + """Loaded data volume in metabytes in the latest sync on Fivetran""" + + fivetran_last_sync_process_time_seconds: Union[float, None, UnsetType] = UNSET + """Process time in seconds in the latest sync on Fivetran""" + + fivetran_last_sync_process_volume_megabytes: Union[float, None, UnsetType] = UNSET + """Process volume in metabytes in the latest sync on Fivetran""" + + fivetran_last_sync_total_time_seconds: Union[float, None, UnsetType] = UNSET + """Total sync time in seconds in the latest sync on Fivetran""" + + fivetran_name: Union[str, None, UnsetType] = UNSET + """Connector name added by the user on Fivetran""" + + fivetran_type: Union[str, None, UnsetType] = UNSET + """Type of connector on Fivetran. Eg: snowflake, google_analytics, notion etc.""" + + fivetran_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="fivetranURL" + ) + """URL to open the connector details on Fivetran""" + + fivetran_destination_name: Union[str, None, UnsetType] = UNSET + """Destination name added by the user on Fivetran""" + + fivetran_destination_type: Union[str, None, UnsetType] = UNSET + """Type of destination on Fivetran. Eg: redshift, bigquery etc.""" + + fivetran_destination_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="fivetranDestinationURL" + ) + """URL to open the destination details on Fivetran""" + + fivetran_sync_setup_on: Union[int, None, UnsetType] = UNSET + """Timestamp (epoch) on which the connector was setup on Fivetran, in milliseconds""" + + fivetran_sync_frequency: Union[str, None, UnsetType] = UNSET + """Sync frequency for the connector in number of hours. Eg: Every 6 hours""" + + fivetran_sync_paused: Union[bool, None, UnsetType] = UNSET + """Boolean to indicate whether the sync for this connector is paused or not""" + + fivetran_sync_setup_user_full_name: Union[str, None, UnsetType] = UNSET + """Full name of the user who setup the connector on Fivetran""" + + fivetran_sync_setup_user_email: Union[str, None, UnsetType] = UNSET + """Email ID of the user who setpu the connector on Fivetran""" + + fivetran_monthly_active_rows_free: Union[int, None, UnsetType] = UNSET + """Free Monthly Active Rows used by the connector in the past month""" + + fivetran_monthly_active_rows_paid: Union[int, None, UnsetType] = UNSET + """Paid Monthly Active Rows used by the connector in the past month""" + + fivetran_monthly_active_rows_total: Union[int, None, UnsetType] = UNSET + """Total Monthly Active Rows used by the connector in the past month""" + + fivetran_monthly_active_rows_change_percentage_free: Union[ + float, None, UnsetType + ] = UNSET + """Increase in the percentage of free MAR compared to the previous month""" + + fivetran_monthly_active_rows_change_percentage_paid: Union[ + float, None, UnsetType + ] = UNSET + """Increase in the percentage of paid MAR compared to the previous month""" + + fivetran_monthly_active_rows_change_percentage_total: Union[ + float, None, UnsetType + ] = UNSET + """Increase in the percentage of total MAR compared to the previous month""" + + fivetran_monthly_active_rows_free_percentage_of_account: Union[ + float, None, UnsetType + ] = UNSET + """Percentage of the account's total free MAR used by this connector""" + + fivetran_monthly_active_rows_paid_percentage_of_account: Union[ + float, None, UnsetType + ] = UNSET + """Percentage of the account's total paid MAR used by this connector""" + + fivetran_monthly_active_rows_total_percentage_of_account: Union[ + float, None, UnsetType + ] = UNSET + """Percentage of the account's total MAR used by this connector""" + + fivetran_total_tables_synced: Union[int, None, UnsetType] = UNSET + """Total number of tables synced by this connector""" + + fivetran_connector_top_tables_by_mar: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="fivetranConnectorTopTablesByMAR" + ) + """Total five tables sorted by MAR synced by this connector""" + + fivetran_usage_cost: Union[float, None, UnsetType] = UNSET + """Total usage cost by this destination""" + + fivetran_credits_used: Union[float, None, UnsetType] = UNSET + """Total credits used by this destination""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "FivetranConnector" diff --git a/pyatlan_v9/model/assets/flow.py b/pyatlan_v9/model/assets/flow.py new file mode 100644 index 000000000..e1231895a --- /dev/null +++ b/pyatlan_v9/model/assets/flow.py @@ -0,0 +1,578 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Flow asset model with flattened inheritance. + +This module provides: +- Flow: Flat asset class (easy to use) +- FlowAttributes: Nested attributes struct (extends AssetAttributes) +- FlowNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Flow(Asset): + """ + Base class for flow-specific (ETL, other data processing) assets. + """ + + FLOW_STARTED_AT: ClassVar[Any] = None + FLOW_FINISHED_AT: ClassVar[Any] = None + FLOW_STATUS: ClassVar[Any] = None + FLOW_SCHEDULE: ClassVar[Any] = None + FLOW_PROJECT_NAME: ClassVar[Any] = None + FLOW_PROJECT_QUALIFIED_NAME: ClassVar[Any] = None + FLOW_FOLDER_NAME: ClassVar[Any] = None + FLOW_FOLDER_QUALIFIED_NAME: ClassVar[Any] = None + FLOW_REUSABLE_UNIT_NAME: ClassVar[Any] = None + FLOW_REUSABLE_UNIT_QUALIFIED_NAME: ClassVar[Any] = None + FLOW_ID: ClassVar[Any] = None + FLOW_RUN_ID: ClassVar[Any] = None + FLOW_ERROR_MESSAGE: ClassVar[Any] = None + FLOW_INPUT_PARAMETERS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Flow" + + flow_started_at: Union[int, None, UnsetType] = UNSET + """Date and time at which this point in the data processing or orchestration started.""" + + flow_finished_at: Union[int, None, UnsetType] = UNSET + """Date and time at which this point in the data processing or orchestration finished.""" + + flow_status: Union[str, None, UnsetType] = UNSET + """Overall status of this point in the data processing or orchestration.""" + + flow_schedule: Union[str, None, UnsetType] = UNSET + """Schedule for this point in the data processing or orchestration.""" + + flow_project_name: Union[str, None, UnsetType] = UNSET + """Simple name of the project in which this asset is contained.""" + + flow_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this asset is contained.""" + + flow_folder_name: Union[str, None, UnsetType] = UNSET + """Simple name of the folder in which this asset is contained.""" + + flow_folder_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the folder in which this asset is contained.""" + + flow_reusable_unit_name: Union[str, None, UnsetType] = UNSET + """Simple name of the reusable grouping of operations in which this ephemeral data is contained.""" + + flow_reusable_unit_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the reusable grouping of operations in which this ephemeral data is contained.""" + + flow_id: Union[str, None, UnsetType] = UNSET + """Unique ID for this flow asset, which will remain constant throughout the lifecycle of the asset.""" + + flow_run_id: Union[str, None, UnsetType] = UNSET + """Unique ID of the flow run, which could change on subsequent runs of the same flow.""" + + flow_error_message: Union[str, None, UnsetType] = UNSET + """Optional error message of the flow run.""" + + flow_input_parameters: Union[Dict[str, str], None, UnsetType] = UNSET + """Input parameters for the flow run.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Flow" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _flow_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Flow: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Flow instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _flow_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class FlowAttributes(AssetAttributes): + """Flow-specific attributes for nested API format.""" + + flow_started_at: Union[int, None, UnsetType] = UNSET + """Date and time at which this point in the data processing or orchestration started.""" + + flow_finished_at: Union[int, None, UnsetType] = UNSET + """Date and time at which this point in the data processing or orchestration finished.""" + + flow_status: Union[str, None, UnsetType] = UNSET + """Overall status of this point in the data processing or orchestration.""" + + flow_schedule: Union[str, None, UnsetType] = UNSET + """Schedule for this point in the data processing or orchestration.""" + + flow_project_name: Union[str, None, UnsetType] = UNSET + """Simple name of the project in which this asset is contained.""" + + flow_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this asset is contained.""" + + flow_folder_name: Union[str, None, UnsetType] = UNSET + """Simple name of the folder in which this asset is contained.""" + + flow_folder_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the folder in which this asset is contained.""" + + flow_reusable_unit_name: Union[str, None, UnsetType] = UNSET + """Simple name of the reusable grouping of operations in which this ephemeral data is contained.""" + + flow_reusable_unit_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the reusable grouping of operations in which this ephemeral data is contained.""" + + flow_id: Union[str, None, UnsetType] = UNSET + """Unique ID for this flow asset, which will remain constant throughout the lifecycle of the asset.""" + + flow_run_id: Union[str, None, UnsetType] = UNSET + """Unique ID of the flow run, which could change on subsequent runs of the same flow.""" + + flow_error_message: Union[str, None, UnsetType] = UNSET + """Optional error message of the flow run.""" + + flow_input_parameters: Union[Dict[str, str], None, UnsetType] = UNSET + """Input parameters for the flow run.""" + + +class FlowRelationshipAttributes(AssetRelationshipAttributes): + """Flow-specific relationship attributes for nested API format.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + +class FlowNested(AssetNested): + """Flow in nested API format for high-performance serialization.""" + + attributes: Union[FlowAttributes, UnsetType] = UNSET + relationship_attributes: Union[FlowRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[FlowRelationshipAttributes, UnsetType] = UNSET + remove_relationship_attributes: Union[FlowRelationshipAttributes, UnsetType] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_FLOW_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", +] + + +def _populate_flow_attrs(attrs: FlowAttributes, obj: Flow) -> None: + """Populate Flow-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.flow_started_at = obj.flow_started_at + attrs.flow_finished_at = obj.flow_finished_at + attrs.flow_status = obj.flow_status + attrs.flow_schedule = obj.flow_schedule + attrs.flow_project_name = obj.flow_project_name + attrs.flow_project_qualified_name = obj.flow_project_qualified_name + attrs.flow_folder_name = obj.flow_folder_name + attrs.flow_folder_qualified_name = obj.flow_folder_qualified_name + attrs.flow_reusable_unit_name = obj.flow_reusable_unit_name + attrs.flow_reusable_unit_qualified_name = obj.flow_reusable_unit_qualified_name + attrs.flow_id = obj.flow_id + attrs.flow_run_id = obj.flow_run_id + attrs.flow_error_message = obj.flow_error_message + attrs.flow_input_parameters = obj.flow_input_parameters + + +def _extract_flow_attrs(attrs: FlowAttributes) -> dict: + """Extract all Flow attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["flow_started_at"] = attrs.flow_started_at + result["flow_finished_at"] = attrs.flow_finished_at + result["flow_status"] = attrs.flow_status + result["flow_schedule"] = attrs.flow_schedule + result["flow_project_name"] = attrs.flow_project_name + result["flow_project_qualified_name"] = attrs.flow_project_qualified_name + result["flow_folder_name"] = attrs.flow_folder_name + result["flow_folder_qualified_name"] = attrs.flow_folder_qualified_name + result["flow_reusable_unit_name"] = attrs.flow_reusable_unit_name + result["flow_reusable_unit_qualified_name"] = ( + attrs.flow_reusable_unit_qualified_name + ) + result["flow_id"] = attrs.flow_id + result["flow_run_id"] = attrs.flow_run_id + result["flow_error_message"] = attrs.flow_error_message + result["flow_input_parameters"] = attrs.flow_input_parameters + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _flow_to_nested(flow: Flow) -> FlowNested: + """Convert flat Flow to nested format.""" + attrs = FlowAttributes() + _populate_flow_attrs(attrs, flow) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + flow, _FLOW_REL_FIELDS, FlowRelationshipAttributes + ) + return FlowNested( + guid=flow.guid, + type_name=flow.type_name, + status=flow.status, + version=flow.version, + create_time=flow.create_time, + update_time=flow.update_time, + created_by=flow.created_by, + updated_by=flow.updated_by, + classifications=flow.classifications, + classification_names=flow.classification_names, + meanings=flow.meanings, + labels=flow.labels, + business_attributes=flow.business_attributes, + custom_attributes=flow.custom_attributes, + pending_tasks=flow.pending_tasks, + proxy=flow.proxy, + is_incomplete=flow.is_incomplete, + provenance_type=flow.provenance_type, + home_id=flow.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _flow_from_nested(nested: FlowNested) -> Flow: + """Convert nested format to flat Flow.""" + attrs = nested.attributes if nested.attributes is not UNSET else FlowAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _FLOW_REL_FIELDS, + FlowRelationshipAttributes, + ) + return Flow( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_flow_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _flow_to_nested_bytes(flow: Flow, serde: Serde) -> bytes: + """Convert flat Flow to nested JSON bytes.""" + return serde.encode(_flow_to_nested(flow)) + + +def _flow_from_nested_bytes(data: bytes, serde: Serde) -> Flow: + """Convert nested JSON bytes to flat Flow.""" + nested = serde.decode(data, FlowNested) + return _flow_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +Flow.FLOW_STARTED_AT = NumericField("flowStartedAt", "flowStartedAt") +Flow.FLOW_FINISHED_AT = NumericField("flowFinishedAt", "flowFinishedAt") +Flow.FLOW_STATUS = KeywordField("flowStatus", "flowStatus") +Flow.FLOW_SCHEDULE = KeywordField("flowSchedule", "flowSchedule") +Flow.FLOW_PROJECT_NAME = KeywordTextField( + "flowProjectName", "flowProjectName", "flowProjectName.text" +) +Flow.FLOW_PROJECT_QUALIFIED_NAME = KeywordField( + "flowProjectQualifiedName", "flowProjectQualifiedName" +) +Flow.FLOW_FOLDER_NAME = KeywordTextField( + "flowFolderName", "flowFolderName", "flowFolderName.text" +) +Flow.FLOW_FOLDER_QUALIFIED_NAME = KeywordField( + "flowFolderQualifiedName", "flowFolderQualifiedName" +) +Flow.FLOW_REUSABLE_UNIT_NAME = KeywordTextField( + "flowReusableUnitName", "flowReusableUnitName", "flowReusableUnitName.text" +) +Flow.FLOW_REUSABLE_UNIT_QUALIFIED_NAME = KeywordField( + "flowReusableUnitQualifiedName", "flowReusableUnitQualifiedName" +) +Flow.FLOW_ID = KeywordField("flowId", "flowId") +Flow.FLOW_RUN_ID = KeywordField("flowRunId", "flowRunId") +Flow.FLOW_ERROR_MESSAGE = KeywordField("flowErrorMessage", "flowErrorMessage") +Flow.FLOW_INPUT_PARAMETERS = KeywordField("flowInputParameters", "flowInputParameters") +Flow.ANOMALO_CHECKS = RelationField("anomaloChecks") +Flow.APPLICATION = RelationField("application") +Flow.APPLICATION_FIELD = RelationField("applicationField") +Flow.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Flow.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Flow.METRICS = RelationField("metrics") +Flow.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Flow.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Flow.MEANINGS = RelationField("meanings") +Flow.MC_MONITORS = RelationField("mcMonitors") +Flow.MC_INCIDENTS = RelationField("mcIncidents") +Flow.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Flow.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Flow.FILES = RelationField("files") +Flow.LINKS = RelationField("links") +Flow.README = RelationField("readme") +Flow.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Flow.SODA_CHECKS = RelationField("sodaChecks") diff --git a/pyatlan_v9/model/assets/flow_control_operation.py b/pyatlan_v9/model/assets/flow_control_operation.py new file mode 100644 index 000000000..73dd889f3 --- /dev/null +++ b/pyatlan_v9/model/assets/flow_control_operation.py @@ -0,0 +1,673 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +FlowControlOperation asset model with flattened inheritance. + +This module provides: +- FlowControlOperation: Flat asset class (easy to use) +- FlowControlOperationAttributes: Nested attributes struct (extends AssetAttributes) +- FlowControlOperationNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .flow_related import RelatedFlowControlOperation + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class FlowControlOperation(Asset): + """ + Execution of a single orchestrate-able unit of work. + """ + + FLOW_STARTED_AT: ClassVar[Any] = None + FLOW_FINISHED_AT: ClassVar[Any] = None + FLOW_STATUS: ClassVar[Any] = None + FLOW_SCHEDULE: ClassVar[Any] = None + FLOW_PROJECT_NAME: ClassVar[Any] = None + FLOW_PROJECT_QUALIFIED_NAME: ClassVar[Any] = None + FLOW_FOLDER_NAME: ClassVar[Any] = None + FLOW_FOLDER_QUALIFIED_NAME: ClassVar[Any] = None + FLOW_REUSABLE_UNIT_NAME: ClassVar[Any] = None + FLOW_REUSABLE_UNIT_QUALIFIED_NAME: ClassVar[Any] = None + FLOW_ID: ClassVar[Any] = None + FLOW_RUN_ID: ClassVar[Any] = None + FLOW_ERROR_MESSAGE: ClassVar[Any] = None + FLOW_INPUT_PARAMETERS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + FLOW_DATA_RESULTS: ClassVar[Any] = None + FLOW_PREDECESSORS: ClassVar[Any] = None + FLOW_SUCCESSORS: ClassVar[Any] = None + FLOW_CONTROLLED_OPERATIONS: ClassVar[Any] = None + FLOW_CONTROLLED_BY: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "FlowControlOperation" + + flow_started_at: Union[int, None, UnsetType] = UNSET + """Date and time at which this point in the data processing or orchestration started.""" + + flow_finished_at: Union[int, None, UnsetType] = UNSET + """Date and time at which this point in the data processing or orchestration finished.""" + + flow_status: Union[str, None, UnsetType] = UNSET + """Overall status of this point in the data processing or orchestration.""" + + flow_schedule: Union[str, None, UnsetType] = UNSET + """Schedule for this point in the data processing or orchestration.""" + + flow_project_name: Union[str, None, UnsetType] = UNSET + """Simple name of the project in which this asset is contained.""" + + flow_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this asset is contained.""" + + flow_folder_name: Union[str, None, UnsetType] = UNSET + """Simple name of the folder in which this asset is contained.""" + + flow_folder_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the folder in which this asset is contained.""" + + flow_reusable_unit_name: Union[str, None, UnsetType] = UNSET + """Simple name of the reusable grouping of operations in which this ephemeral data is contained.""" + + flow_reusable_unit_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the reusable grouping of operations in which this ephemeral data is contained.""" + + flow_id: Union[str, None, UnsetType] = UNSET + """Unique ID for this flow asset, which will remain constant throughout the lifecycle of the asset.""" + + flow_run_id: Union[str, None, UnsetType] = UNSET + """Unique ID of the flow run, which could change on subsequent runs of the same flow.""" + + flow_error_message: Union[str, None, UnsetType] = UNSET + """Optional error message of the flow run.""" + + flow_input_parameters: Union[Dict[str, str], None, UnsetType] = UNSET + """Input parameters for the flow run.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + flow_data_results: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Individual data flows (processes) orchestrated by this control operation.""" + + flow_predecessors: Union[List[RelatedFlowControlOperation], None, UnsetType] = UNSET + """Control operations that are configured to execute before this one.""" + + flow_successors: Union[List[RelatedFlowControlOperation], None, UnsetType] = UNSET + """Control operations that are configured to execute only after this one.""" + + flow_controlled_operations: Union[ + List[RelatedFlowControlOperation], None, UnsetType + ] = UNSET + """Control operations whose execution is controlled by this control operation.""" + + flow_controlled_by: Union[RelatedFlowControlOperation, None, UnsetType] = UNSET + """Control operation that controls the execution of this control operation.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "FlowControlOperation" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _flow_control_operation_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> FlowControlOperation: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + FlowControlOperation instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _flow_control_operation_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class FlowControlOperationAttributes(AssetAttributes): + """FlowControlOperation-specific attributes for nested API format.""" + + flow_started_at: Union[int, None, UnsetType] = UNSET + """Date and time at which this point in the data processing or orchestration started.""" + + flow_finished_at: Union[int, None, UnsetType] = UNSET + """Date and time at which this point in the data processing or orchestration finished.""" + + flow_status: Union[str, None, UnsetType] = UNSET + """Overall status of this point in the data processing or orchestration.""" + + flow_schedule: Union[str, None, UnsetType] = UNSET + """Schedule for this point in the data processing or orchestration.""" + + flow_project_name: Union[str, None, UnsetType] = UNSET + """Simple name of the project in which this asset is contained.""" + + flow_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this asset is contained.""" + + flow_folder_name: Union[str, None, UnsetType] = UNSET + """Simple name of the folder in which this asset is contained.""" + + flow_folder_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the folder in which this asset is contained.""" + + flow_reusable_unit_name: Union[str, None, UnsetType] = UNSET + """Simple name of the reusable grouping of operations in which this ephemeral data is contained.""" + + flow_reusable_unit_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the reusable grouping of operations in which this ephemeral data is contained.""" + + flow_id: Union[str, None, UnsetType] = UNSET + """Unique ID for this flow asset, which will remain constant throughout the lifecycle of the asset.""" + + flow_run_id: Union[str, None, UnsetType] = UNSET + """Unique ID of the flow run, which could change on subsequent runs of the same flow.""" + + flow_error_message: Union[str, None, UnsetType] = UNSET + """Optional error message of the flow run.""" + + flow_input_parameters: Union[Dict[str, str], None, UnsetType] = UNSET + """Input parameters for the flow run.""" + + +class FlowControlOperationRelationshipAttributes(AssetRelationshipAttributes): + """FlowControlOperation-specific relationship attributes for nested API format.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + flow_data_results: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Individual data flows (processes) orchestrated by this control operation.""" + + flow_predecessors: Union[List[RelatedFlowControlOperation], None, UnsetType] = UNSET + """Control operations that are configured to execute before this one.""" + + flow_successors: Union[List[RelatedFlowControlOperation], None, UnsetType] = UNSET + """Control operations that are configured to execute only after this one.""" + + flow_controlled_operations: Union[ + List[RelatedFlowControlOperation], None, UnsetType + ] = UNSET + """Control operations whose execution is controlled by this control operation.""" + + flow_controlled_by: Union[RelatedFlowControlOperation, None, UnsetType] = UNSET + """Control operation that controls the execution of this control operation.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + +class FlowControlOperationNested(AssetNested): + """FlowControlOperation in nested API format for high-performance serialization.""" + + attributes: Union[FlowControlOperationAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + FlowControlOperationRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + FlowControlOperationRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + FlowControlOperationRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_FLOW_CONTROL_OPERATION_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "flow_data_results", + "flow_predecessors", + "flow_successors", + "flow_controlled_operations", + "flow_controlled_by", + "meanings", + "mc_monitors", + "mc_incidents", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", +] + + +def _populate_flow_control_operation_attrs( + attrs: FlowControlOperationAttributes, obj: FlowControlOperation +) -> None: + """Populate FlowControlOperation-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.flow_started_at = obj.flow_started_at + attrs.flow_finished_at = obj.flow_finished_at + attrs.flow_status = obj.flow_status + attrs.flow_schedule = obj.flow_schedule + attrs.flow_project_name = obj.flow_project_name + attrs.flow_project_qualified_name = obj.flow_project_qualified_name + attrs.flow_folder_name = obj.flow_folder_name + attrs.flow_folder_qualified_name = obj.flow_folder_qualified_name + attrs.flow_reusable_unit_name = obj.flow_reusable_unit_name + attrs.flow_reusable_unit_qualified_name = obj.flow_reusable_unit_qualified_name + attrs.flow_id = obj.flow_id + attrs.flow_run_id = obj.flow_run_id + attrs.flow_error_message = obj.flow_error_message + attrs.flow_input_parameters = obj.flow_input_parameters + + +def _extract_flow_control_operation_attrs( + attrs: FlowControlOperationAttributes, +) -> dict: + """Extract all FlowControlOperation attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["flow_started_at"] = attrs.flow_started_at + result["flow_finished_at"] = attrs.flow_finished_at + result["flow_status"] = attrs.flow_status + result["flow_schedule"] = attrs.flow_schedule + result["flow_project_name"] = attrs.flow_project_name + result["flow_project_qualified_name"] = attrs.flow_project_qualified_name + result["flow_folder_name"] = attrs.flow_folder_name + result["flow_folder_qualified_name"] = attrs.flow_folder_qualified_name + result["flow_reusable_unit_name"] = attrs.flow_reusable_unit_name + result["flow_reusable_unit_qualified_name"] = ( + attrs.flow_reusable_unit_qualified_name + ) + result["flow_id"] = attrs.flow_id + result["flow_run_id"] = attrs.flow_run_id + result["flow_error_message"] = attrs.flow_error_message + result["flow_input_parameters"] = attrs.flow_input_parameters + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _flow_control_operation_to_nested( + flow_control_operation: FlowControlOperation, +) -> FlowControlOperationNested: + """Convert flat FlowControlOperation to nested format.""" + attrs = FlowControlOperationAttributes() + _populate_flow_control_operation_attrs(attrs, flow_control_operation) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + flow_control_operation, + _FLOW_CONTROL_OPERATION_REL_FIELDS, + FlowControlOperationRelationshipAttributes, + ) + return FlowControlOperationNested( + guid=flow_control_operation.guid, + type_name=flow_control_operation.type_name, + status=flow_control_operation.status, + version=flow_control_operation.version, + create_time=flow_control_operation.create_time, + update_time=flow_control_operation.update_time, + created_by=flow_control_operation.created_by, + updated_by=flow_control_operation.updated_by, + classifications=flow_control_operation.classifications, + classification_names=flow_control_operation.classification_names, + meanings=flow_control_operation.meanings, + labels=flow_control_operation.labels, + business_attributes=flow_control_operation.business_attributes, + custom_attributes=flow_control_operation.custom_attributes, + pending_tasks=flow_control_operation.pending_tasks, + proxy=flow_control_operation.proxy, + is_incomplete=flow_control_operation.is_incomplete, + provenance_type=flow_control_operation.provenance_type, + home_id=flow_control_operation.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _flow_control_operation_from_nested( + nested: FlowControlOperationNested, +) -> FlowControlOperation: + """Convert nested format to flat FlowControlOperation.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else FlowControlOperationAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _FLOW_CONTROL_OPERATION_REL_FIELDS, + FlowControlOperationRelationshipAttributes, + ) + return FlowControlOperation( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_flow_control_operation_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _flow_control_operation_to_nested_bytes( + flow_control_operation: FlowControlOperation, serde: Serde +) -> bytes: + """Convert flat FlowControlOperation to nested JSON bytes.""" + return serde.encode(_flow_control_operation_to_nested(flow_control_operation)) + + +def _flow_control_operation_from_nested_bytes( + data: bytes, serde: Serde +) -> FlowControlOperation: + """Convert nested JSON bytes to flat FlowControlOperation.""" + nested = serde.decode(data, FlowControlOperationNested) + return _flow_control_operation_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +FlowControlOperation.FLOW_STARTED_AT = NumericField("flowStartedAt", "flowStartedAt") +FlowControlOperation.FLOW_FINISHED_AT = NumericField("flowFinishedAt", "flowFinishedAt") +FlowControlOperation.FLOW_STATUS = KeywordField("flowStatus", "flowStatus") +FlowControlOperation.FLOW_SCHEDULE = KeywordField("flowSchedule", "flowSchedule") +FlowControlOperation.FLOW_PROJECT_NAME = KeywordTextField( + "flowProjectName", "flowProjectName", "flowProjectName.text" +) +FlowControlOperation.FLOW_PROJECT_QUALIFIED_NAME = KeywordField( + "flowProjectQualifiedName", "flowProjectQualifiedName" +) +FlowControlOperation.FLOW_FOLDER_NAME = KeywordTextField( + "flowFolderName", "flowFolderName", "flowFolderName.text" +) +FlowControlOperation.FLOW_FOLDER_QUALIFIED_NAME = KeywordField( + "flowFolderQualifiedName", "flowFolderQualifiedName" +) +FlowControlOperation.FLOW_REUSABLE_UNIT_NAME = KeywordTextField( + "flowReusableUnitName", "flowReusableUnitName", "flowReusableUnitName.text" +) +FlowControlOperation.FLOW_REUSABLE_UNIT_QUALIFIED_NAME = KeywordField( + "flowReusableUnitQualifiedName", "flowReusableUnitQualifiedName" +) +FlowControlOperation.FLOW_ID = KeywordField("flowId", "flowId") +FlowControlOperation.FLOW_RUN_ID = KeywordField("flowRunId", "flowRunId") +FlowControlOperation.FLOW_ERROR_MESSAGE = KeywordField( + "flowErrorMessage", "flowErrorMessage" +) +FlowControlOperation.FLOW_INPUT_PARAMETERS = KeywordField( + "flowInputParameters", "flowInputParameters" +) +FlowControlOperation.ANOMALO_CHECKS = RelationField("anomaloChecks") +FlowControlOperation.APPLICATION = RelationField("application") +FlowControlOperation.APPLICATION_FIELD = RelationField("applicationField") +FlowControlOperation.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +FlowControlOperation.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +FlowControlOperation.METRICS = RelationField("metrics") +FlowControlOperation.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +FlowControlOperation.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +FlowControlOperation.FLOW_DATA_RESULTS = RelationField("flowDataResults") +FlowControlOperation.FLOW_PREDECESSORS = RelationField("flowPredecessors") +FlowControlOperation.FLOW_SUCCESSORS = RelationField("flowSuccessors") +FlowControlOperation.FLOW_CONTROLLED_OPERATIONS = RelationField( + "flowControlledOperations" +) +FlowControlOperation.FLOW_CONTROLLED_BY = RelationField("flowControlledBy") +FlowControlOperation.MEANINGS = RelationField("meanings") +FlowControlOperation.MC_MONITORS = RelationField("mcMonitors") +FlowControlOperation.MC_INCIDENTS = RelationField("mcIncidents") +FlowControlOperation.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +FlowControlOperation.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +FlowControlOperation.FILES = RelationField("files") +FlowControlOperation.LINKS = RelationField("links") +FlowControlOperation.README = RelationField("readme") +FlowControlOperation.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +FlowControlOperation.SODA_CHECKS = RelationField("sodaChecks") diff --git a/pyatlan_v9/model/assets/flow_dataset.py b/pyatlan_v9/model/assets/flow_dataset.py new file mode 100644 index 000000000..834286479 --- /dev/null +++ b/pyatlan_v9/model/assets/flow_dataset.py @@ -0,0 +1,763 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +FlowDataset asset model with flattened inheritance. + +This module provides: +- FlowDataset: Flat asset class (easy to use) +- FlowDatasetAttributes: Nested attributes struct (extends AssetAttributes) +- FlowDatasetNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .flow_related import RelatedFlowField, RelatedFlowReusableUnit + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class FlowDataset(Asset): + """ + An ephemeral piece of data either produced by or used as input by a data operation. + """ + + FLOW_FIELD_COUNT: ClassVar[Any] = None + FLOW_TYPE: ClassVar[Any] = None + FLOW_EXPRESSION: ClassVar[Any] = None + FLOW_QUERY: ClassVar[Any] = None + FLOW_STARTED_AT: ClassVar[Any] = None + FLOW_FINISHED_AT: ClassVar[Any] = None + FLOW_STATUS: ClassVar[Any] = None + FLOW_SCHEDULE: ClassVar[Any] = None + FLOW_PROJECT_NAME: ClassVar[Any] = None + FLOW_PROJECT_QUALIFIED_NAME: ClassVar[Any] = None + FLOW_FOLDER_NAME: ClassVar[Any] = None + FLOW_FOLDER_QUALIFIED_NAME: ClassVar[Any] = None + FLOW_REUSABLE_UNIT_NAME: ClassVar[Any] = None + FLOW_REUSABLE_UNIT_QUALIFIED_NAME: ClassVar[Any] = None + FLOW_ID: ClassVar[Any] = None + FLOW_RUN_ID: ClassVar[Any] = None + FLOW_ERROR_MESSAGE: ClassVar[Any] = None + FLOW_INPUT_PARAMETERS: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + FLOW_DETAILED_BY: ClassVar[Any] = None + FLOW_PARENT_UNIT: ClassVar[Any] = None + FLOW_FIELDS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "FlowDataset" + + flow_field_count: Union[int, None, UnsetType] = UNSET + """Count of the number of individual fields that make up this ephemeral dataset.""" + + flow_type: Union[str, None, UnsetType] = UNSET + """Type of the ephemeral piece of data.""" + + flow_expression: Union[str, None, UnsetType] = UNSET + """Logic that is applied, injected or otherwise used as part of producing this ephemeral piece of data.""" + + flow_query: Union[str, None, UnsetType] = UNSET + """Query (e.g. SQL) that was run to produce this ephemeral piece of data.""" + + flow_started_at: Union[int, None, UnsetType] = UNSET + """Date and time at which this point in the data processing or orchestration started.""" + + flow_finished_at: Union[int, None, UnsetType] = UNSET + """Date and time at which this point in the data processing or orchestration finished.""" + + flow_status: Union[str, None, UnsetType] = UNSET + """Overall status of this point in the data processing or orchestration.""" + + flow_schedule: Union[str, None, UnsetType] = UNSET + """Schedule for this point in the data processing or orchestration.""" + + flow_project_name: Union[str, None, UnsetType] = UNSET + """Simple name of the project in which this asset is contained.""" + + flow_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this asset is contained.""" + + flow_folder_name: Union[str, None, UnsetType] = UNSET + """Simple name of the folder in which this asset is contained.""" + + flow_folder_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the folder in which this asset is contained.""" + + flow_reusable_unit_name: Union[str, None, UnsetType] = UNSET + """Simple name of the reusable grouping of operations in which this ephemeral data is contained.""" + + flow_reusable_unit_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the reusable grouping of operations in which this ephemeral data is contained.""" + + flow_id: Union[str, None, UnsetType] = UNSET + """Unique ID for this flow asset, which will remain constant throughout the lifecycle of the asset.""" + + flow_run_id: Union[str, None, UnsetType] = UNSET + """Unique ID of the flow run, which could change on subsequent runs of the same flow.""" + + flow_error_message: Union[str, None, UnsetType] = UNSET + """Optional error message of the flow run.""" + + flow_input_parameters: Union[Dict[str, str], None, UnsetType] = UNSET + """Input parameters for the flow run.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + flow_detailed_by: Union[RelatedFlowReusableUnit, None, UnsetType] = UNSET + """Reusable unit that details the sub-processing to produce the ephemeral dataset.""" + + flow_parent_unit: Union[RelatedFlowReusableUnit, None, UnsetType] = UNSET + """Reusable unit in which the ephemeral dataset is contained.""" + + flow_fields: Union[List[RelatedFlowField], None, UnsetType] = UNSET + """Fields contained in the ephemeral dataset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "FlowDataset" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _flow_dataset_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> FlowDataset: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + FlowDataset instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _flow_dataset_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class FlowDatasetAttributes(AssetAttributes): + """FlowDataset-specific attributes for nested API format.""" + + flow_field_count: Union[int, None, UnsetType] = UNSET + """Count of the number of individual fields that make up this ephemeral dataset.""" + + flow_type: Union[str, None, UnsetType] = UNSET + """Type of the ephemeral piece of data.""" + + flow_expression: Union[str, None, UnsetType] = UNSET + """Logic that is applied, injected or otherwise used as part of producing this ephemeral piece of data.""" + + flow_query: Union[str, None, UnsetType] = UNSET + """Query (e.g. SQL) that was run to produce this ephemeral piece of data.""" + + flow_started_at: Union[int, None, UnsetType] = UNSET + """Date and time at which this point in the data processing or orchestration started.""" + + flow_finished_at: Union[int, None, UnsetType] = UNSET + """Date and time at which this point in the data processing or orchestration finished.""" + + flow_status: Union[str, None, UnsetType] = UNSET + """Overall status of this point in the data processing or orchestration.""" + + flow_schedule: Union[str, None, UnsetType] = UNSET + """Schedule for this point in the data processing or orchestration.""" + + flow_project_name: Union[str, None, UnsetType] = UNSET + """Simple name of the project in which this asset is contained.""" + + flow_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this asset is contained.""" + + flow_folder_name: Union[str, None, UnsetType] = UNSET + """Simple name of the folder in which this asset is contained.""" + + flow_folder_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the folder in which this asset is contained.""" + + flow_reusable_unit_name: Union[str, None, UnsetType] = UNSET + """Simple name of the reusable grouping of operations in which this ephemeral data is contained.""" + + flow_reusable_unit_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the reusable grouping of operations in which this ephemeral data is contained.""" + + flow_id: Union[str, None, UnsetType] = UNSET + """Unique ID for this flow asset, which will remain constant throughout the lifecycle of the asset.""" + + flow_run_id: Union[str, None, UnsetType] = UNSET + """Unique ID of the flow run, which could change on subsequent runs of the same flow.""" + + flow_error_message: Union[str, None, UnsetType] = UNSET + """Optional error message of the flow run.""" + + flow_input_parameters: Union[Dict[str, str], None, UnsetType] = UNSET + """Input parameters for the flow run.""" + + +class FlowDatasetRelationshipAttributes(AssetRelationshipAttributes): + """FlowDataset-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + flow_detailed_by: Union[RelatedFlowReusableUnit, None, UnsetType] = UNSET + """Reusable unit that details the sub-processing to produce the ephemeral dataset.""" + + flow_parent_unit: Union[RelatedFlowReusableUnit, None, UnsetType] = UNSET + """Reusable unit in which the ephemeral dataset is contained.""" + + flow_fields: Union[List[RelatedFlowField], None, UnsetType] = UNSET + """Fields contained in the ephemeral dataset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class FlowDatasetNested(AssetNested): + """FlowDataset in nested API format for high-performance serialization.""" + + attributes: Union[FlowDatasetAttributes, UnsetType] = UNSET + relationship_attributes: Union[FlowDatasetRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + FlowDatasetRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + FlowDatasetRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_FLOW_DATASET_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "flow_detailed_by", + "flow_parent_unit", + "flow_fields", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_flow_dataset_attrs( + attrs: FlowDatasetAttributes, obj: FlowDataset +) -> None: + """Populate FlowDataset-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.flow_field_count = obj.flow_field_count + attrs.flow_type = obj.flow_type + attrs.flow_expression = obj.flow_expression + attrs.flow_query = obj.flow_query + attrs.flow_started_at = obj.flow_started_at + attrs.flow_finished_at = obj.flow_finished_at + attrs.flow_status = obj.flow_status + attrs.flow_schedule = obj.flow_schedule + attrs.flow_project_name = obj.flow_project_name + attrs.flow_project_qualified_name = obj.flow_project_qualified_name + attrs.flow_folder_name = obj.flow_folder_name + attrs.flow_folder_qualified_name = obj.flow_folder_qualified_name + attrs.flow_reusable_unit_name = obj.flow_reusable_unit_name + attrs.flow_reusable_unit_qualified_name = obj.flow_reusable_unit_qualified_name + attrs.flow_id = obj.flow_id + attrs.flow_run_id = obj.flow_run_id + attrs.flow_error_message = obj.flow_error_message + attrs.flow_input_parameters = obj.flow_input_parameters + + +def _extract_flow_dataset_attrs(attrs: FlowDatasetAttributes) -> dict: + """Extract all FlowDataset attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["flow_field_count"] = attrs.flow_field_count + result["flow_type"] = attrs.flow_type + result["flow_expression"] = attrs.flow_expression + result["flow_query"] = attrs.flow_query + result["flow_started_at"] = attrs.flow_started_at + result["flow_finished_at"] = attrs.flow_finished_at + result["flow_status"] = attrs.flow_status + result["flow_schedule"] = attrs.flow_schedule + result["flow_project_name"] = attrs.flow_project_name + result["flow_project_qualified_name"] = attrs.flow_project_qualified_name + result["flow_folder_name"] = attrs.flow_folder_name + result["flow_folder_qualified_name"] = attrs.flow_folder_qualified_name + result["flow_reusable_unit_name"] = attrs.flow_reusable_unit_name + result["flow_reusable_unit_qualified_name"] = ( + attrs.flow_reusable_unit_qualified_name + ) + result["flow_id"] = attrs.flow_id + result["flow_run_id"] = attrs.flow_run_id + result["flow_error_message"] = attrs.flow_error_message + result["flow_input_parameters"] = attrs.flow_input_parameters + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _flow_dataset_to_nested(flow_dataset: FlowDataset) -> FlowDatasetNested: + """Convert flat FlowDataset to nested format.""" + attrs = FlowDatasetAttributes() + _populate_flow_dataset_attrs(attrs, flow_dataset) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + flow_dataset, _FLOW_DATASET_REL_FIELDS, FlowDatasetRelationshipAttributes + ) + return FlowDatasetNested( + guid=flow_dataset.guid, + type_name=flow_dataset.type_name, + status=flow_dataset.status, + version=flow_dataset.version, + create_time=flow_dataset.create_time, + update_time=flow_dataset.update_time, + created_by=flow_dataset.created_by, + updated_by=flow_dataset.updated_by, + classifications=flow_dataset.classifications, + classification_names=flow_dataset.classification_names, + meanings=flow_dataset.meanings, + labels=flow_dataset.labels, + business_attributes=flow_dataset.business_attributes, + custom_attributes=flow_dataset.custom_attributes, + pending_tasks=flow_dataset.pending_tasks, + proxy=flow_dataset.proxy, + is_incomplete=flow_dataset.is_incomplete, + provenance_type=flow_dataset.provenance_type, + home_id=flow_dataset.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _flow_dataset_from_nested(nested: FlowDatasetNested) -> FlowDataset: + """Convert nested format to flat FlowDataset.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else FlowDatasetAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _FLOW_DATASET_REL_FIELDS, + FlowDatasetRelationshipAttributes, + ) + return FlowDataset( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_flow_dataset_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _flow_dataset_to_nested_bytes(flow_dataset: FlowDataset, serde: Serde) -> bytes: + """Convert flat FlowDataset to nested JSON bytes.""" + return serde.encode(_flow_dataset_to_nested(flow_dataset)) + + +def _flow_dataset_from_nested_bytes(data: bytes, serde: Serde) -> FlowDataset: + """Convert nested JSON bytes to flat FlowDataset.""" + nested = serde.decode(data, FlowDatasetNested) + return _flow_dataset_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +FlowDataset.FLOW_FIELD_COUNT = NumericField("flowFieldCount", "flowFieldCount") +FlowDataset.FLOW_TYPE = KeywordField("flowType", "flowType") +FlowDataset.FLOW_EXPRESSION = KeywordField("flowExpression", "flowExpression") +FlowDataset.FLOW_QUERY = KeywordField("flowQuery", "flowQuery") +FlowDataset.FLOW_STARTED_AT = NumericField("flowStartedAt", "flowStartedAt") +FlowDataset.FLOW_FINISHED_AT = NumericField("flowFinishedAt", "flowFinishedAt") +FlowDataset.FLOW_STATUS = KeywordField("flowStatus", "flowStatus") +FlowDataset.FLOW_SCHEDULE = KeywordField("flowSchedule", "flowSchedule") +FlowDataset.FLOW_PROJECT_NAME = KeywordTextField( + "flowProjectName", "flowProjectName", "flowProjectName.text" +) +FlowDataset.FLOW_PROJECT_QUALIFIED_NAME = KeywordField( + "flowProjectQualifiedName", "flowProjectQualifiedName" +) +FlowDataset.FLOW_FOLDER_NAME = KeywordTextField( + "flowFolderName", "flowFolderName", "flowFolderName.text" +) +FlowDataset.FLOW_FOLDER_QUALIFIED_NAME = KeywordField( + "flowFolderQualifiedName", "flowFolderQualifiedName" +) +FlowDataset.FLOW_REUSABLE_UNIT_NAME = KeywordTextField( + "flowReusableUnitName", "flowReusableUnitName", "flowReusableUnitName.text" +) +FlowDataset.FLOW_REUSABLE_UNIT_QUALIFIED_NAME = KeywordField( + "flowReusableUnitQualifiedName", "flowReusableUnitQualifiedName" +) +FlowDataset.FLOW_ID = KeywordField("flowId", "flowId") +FlowDataset.FLOW_RUN_ID = KeywordField("flowRunId", "flowRunId") +FlowDataset.FLOW_ERROR_MESSAGE = KeywordField("flowErrorMessage", "flowErrorMessage") +FlowDataset.FLOW_INPUT_PARAMETERS = KeywordField( + "flowInputParameters", "flowInputParameters" +) +FlowDataset.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +FlowDataset.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +FlowDataset.ANOMALO_CHECKS = RelationField("anomaloChecks") +FlowDataset.APPLICATION = RelationField("application") +FlowDataset.APPLICATION_FIELD = RelationField("applicationField") +FlowDataset.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +FlowDataset.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +FlowDataset.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +FlowDataset.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +FlowDataset.METRICS = RelationField("metrics") +FlowDataset.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +FlowDataset.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +FlowDataset.FLOW_DETAILED_BY = RelationField("flowDetailedBy") +FlowDataset.FLOW_PARENT_UNIT = RelationField("flowParentUnit") +FlowDataset.FLOW_FIELDS = RelationField("flowFields") +FlowDataset.MEANINGS = RelationField("meanings") +FlowDataset.MC_MONITORS = RelationField("mcMonitors") +FlowDataset.MC_INCIDENTS = RelationField("mcIncidents") +FlowDataset.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +FlowDataset.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +FlowDataset.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +FlowDataset.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +FlowDataset.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +FlowDataset.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +FlowDataset.FILES = RelationField("files") +FlowDataset.LINKS = RelationField("links") +FlowDataset.README = RelationField("readme") +FlowDataset.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +FlowDataset.SODA_CHECKS = RelationField("sodaChecks") +FlowDataset.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +FlowDataset.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/flow_dataset_operation.py b/pyatlan_v9/model/assets/flow_dataset_operation.py new file mode 100644 index 000000000..21eab6206 --- /dev/null +++ b/pyatlan_v9/model/assets/flow_dataset_operation.py @@ -0,0 +1,830 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +FlowDatasetOperation asset model with flattened inheritance. + +This module provides: +- FlowDatasetOperation: Flat asset class (easy to use) +- FlowDatasetOperationAttributes: Nested attributes struct (extends AssetAttributes) +- FlowDatasetOperationNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .adf_related import RelatedAdfActivity +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .catalog_related import RelatedCatalog +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .fabric_related import RelatedFabricActivity +from .fivetran_related import RelatedFivetranConnector +from .gtc_related import RelatedAtlasGlossaryTerm +from .matillion_related import RelatedMatillionComponent +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .power_bi_related import RelatedPowerBIDataflow +from .process_related import RelatedColumnProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from .sql_related import RelatedFunction, RelatedProcedure +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .flow_related import RelatedFlowControlOperation, RelatedFlowReusableUnit + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class FlowDatasetOperation(Asset): + """ + A nested data operation that uses at least one ephemeral dataset as either an input or output. + """ + + FLOW_STARTED_AT: ClassVar[Any] = None + FLOW_FINISHED_AT: ClassVar[Any] = None + FLOW_STATUS: ClassVar[Any] = None + FLOW_SCHEDULE: ClassVar[Any] = None + FLOW_PROJECT_NAME: ClassVar[Any] = None + FLOW_PROJECT_QUALIFIED_NAME: ClassVar[Any] = None + FLOW_FOLDER_NAME: ClassVar[Any] = None + FLOW_FOLDER_QUALIFIED_NAME: ClassVar[Any] = None + FLOW_REUSABLE_UNIT_NAME: ClassVar[Any] = None + FLOW_REUSABLE_UNIT_QUALIFIED_NAME: ClassVar[Any] = None + FLOW_ID: ClassVar[Any] = None + FLOW_RUN_ID: ClassVar[Any] = None + FLOW_ERROR_MESSAGE: ClassVar[Any] = None + FLOW_INPUT_PARAMETERS: ClassVar[Any] = None + CODE: ClassVar[Any] = None + SQL: ClassVar[Any] = None + PARENT_CONNECTION_PROCESS_QUALIFIED_NAME: ClassVar[Any] = None + AST: ClassVar[Any] = None + ADDITIONAL_ETL_CONTEXT: ClassVar[Any] = None + AI_DATASET_TYPE: ClassVar[Any] = None + ADF_ACTIVITY: ClassVar[Any] = None + AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + FABRIC_ACTIVITIES: ClassVar[Any] = None + FIVETRAN_CONNECTOR: ClassVar[Any] = None + FLOW_ORCHESTRATED_BY: ClassVar[Any] = None + FLOW_REUSABLE_UNIT: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MATILLION_COMPONENT: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + POWER_BI_DATAFLOW: ClassVar[Any] = None + INPUTS: ClassVar[Any] = None + OUTPUTS: ClassVar[Any] = None + COLUMN_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SQL_PROCEDURES: ClassVar[Any] = None + SQL_FUNCTIONS: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "FlowDatasetOperation" + + flow_started_at: Union[int, None, UnsetType] = UNSET + """Date and time at which this point in the data processing or orchestration started.""" + + flow_finished_at: Union[int, None, UnsetType] = UNSET + """Date and time at which this point in the data processing or orchestration finished.""" + + flow_status: Union[str, None, UnsetType] = UNSET + """Overall status of this point in the data processing or orchestration.""" + + flow_schedule: Union[str, None, UnsetType] = UNSET + """Schedule for this point in the data processing or orchestration.""" + + flow_project_name: Union[str, None, UnsetType] = UNSET + """Simple name of the project in which this asset is contained.""" + + flow_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this asset is contained.""" + + flow_folder_name: Union[str, None, UnsetType] = UNSET + """Simple name of the folder in which this asset is contained.""" + + flow_folder_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the folder in which this asset is contained.""" + + flow_reusable_unit_name: Union[str, None, UnsetType] = UNSET + """Simple name of the reusable grouping of operations in which this ephemeral data is contained.""" + + flow_reusable_unit_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the reusable grouping of operations in which this ephemeral data is contained.""" + + flow_id: Union[str, None, UnsetType] = UNSET + """Unique ID for this flow asset, which will remain constant throughout the lifecycle of the asset.""" + + flow_run_id: Union[str, None, UnsetType] = UNSET + """Unique ID of the flow run, which could change on subsequent runs of the same flow.""" + + flow_error_message: Union[str, None, UnsetType] = UNSET + """Optional error message of the flow run.""" + + flow_input_parameters: Union[Dict[str, str], None, UnsetType] = UNSET + """Input parameters for the flow run.""" + + code: Union[str, None, UnsetType] = UNSET + """Code that ran within the process.""" + + sql: Union[str, None, UnsetType] = UNSET + """SQL query that ran to produce the outputs.""" + + parent_connection_process_qualified_name: Union[List[str], None, UnsetType] = UNSET + """""" + + ast: Union[str, None, UnsetType] = UNSET + """Parsed AST of the code or SQL statements that describe the logic of this process.""" + + additional_etl_context: Union[str, None, UnsetType] = UNSET + """Additional Context of the ETL pipeline/notebook which creates the process.""" + + ai_dataset_type: Union[str, None, UnsetType] = UNSET + """Dataset type for AI Model - dataset process.""" + + adf_activity: Union[RelatedAdfActivity, None, UnsetType] = UNSET + """ADF Activity that is associated with this lineage process.""" + + airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks that exist within this process.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + fabric_activities: Union[List[RelatedFabricActivity], None, UnsetType] = UNSET + """Individual Fabric activities contained in the process.""" + + fivetran_connector: Union[RelatedFivetranConnector, None, UnsetType] = UNSET + """fivetranConnector in which this process exists.""" + + flow_orchestrated_by: Union[RelatedFlowControlOperation, None, UnsetType] = UNSET + """Orchestrated control operation that ran these data flows (process).""" + + flow_reusable_unit: Union[RelatedFlowReusableUnit, None, UnsetType] = UNSET + """Reusable unit of dataset operations that are all executed together.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + matillion_component: Union[RelatedMatillionComponent, None, UnsetType] = UNSET + """Matillion component that contains the logic for this lineage process.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + power_bi_dataflow: Union[RelatedPowerBIDataflow, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIDataflow" + ) + """PowerBI Dataflow that is associated with this lineage process.""" + + inputs: Union[List[RelatedCatalog], None, UnsetType] = UNSET + """Assets that are inputs to this process.""" + + outputs: Union[List[RelatedCatalog], None, UnsetType] = UNSET + """Assets that are outputs from this process.""" + + column_processes: Union[List[RelatedColumnProcess], None, UnsetType] = UNSET + """Processes that detail column-level lineage for this process.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + sql_procedures: Union[List[RelatedProcedure], None, UnsetType] = UNSET + """Procedures used by this process.""" + + sql_functions: Union[List[RelatedFunction], None, UnsetType] = UNSET + """Functions used by this process.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "FlowDatasetOperation" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _flow_dataset_operation_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> FlowDatasetOperation: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + FlowDatasetOperation instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _flow_dataset_operation_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class FlowDatasetOperationAttributes(AssetAttributes): + """FlowDatasetOperation-specific attributes for nested API format.""" + + flow_started_at: Union[int, None, UnsetType] = UNSET + """Date and time at which this point in the data processing or orchestration started.""" + + flow_finished_at: Union[int, None, UnsetType] = UNSET + """Date and time at which this point in the data processing or orchestration finished.""" + + flow_status: Union[str, None, UnsetType] = UNSET + """Overall status of this point in the data processing or orchestration.""" + + flow_schedule: Union[str, None, UnsetType] = UNSET + """Schedule for this point in the data processing or orchestration.""" + + flow_project_name: Union[str, None, UnsetType] = UNSET + """Simple name of the project in which this asset is contained.""" + + flow_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this asset is contained.""" + + flow_folder_name: Union[str, None, UnsetType] = UNSET + """Simple name of the folder in which this asset is contained.""" + + flow_folder_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the folder in which this asset is contained.""" + + flow_reusable_unit_name: Union[str, None, UnsetType] = UNSET + """Simple name of the reusable grouping of operations in which this ephemeral data is contained.""" + + flow_reusable_unit_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the reusable grouping of operations in which this ephemeral data is contained.""" + + flow_id: Union[str, None, UnsetType] = UNSET + """Unique ID for this flow asset, which will remain constant throughout the lifecycle of the asset.""" + + flow_run_id: Union[str, None, UnsetType] = UNSET + """Unique ID of the flow run, which could change on subsequent runs of the same flow.""" + + flow_error_message: Union[str, None, UnsetType] = UNSET + """Optional error message of the flow run.""" + + flow_input_parameters: Union[Dict[str, str], None, UnsetType] = UNSET + """Input parameters for the flow run.""" + + code: Union[str, None, UnsetType] = UNSET + """Code that ran within the process.""" + + sql: Union[str, None, UnsetType] = UNSET + """SQL query that ran to produce the outputs.""" + + parent_connection_process_qualified_name: Union[List[str], None, UnsetType] = UNSET + """""" + + ast: Union[str, None, UnsetType] = UNSET + """Parsed AST of the code or SQL statements that describe the logic of this process.""" + + additional_etl_context: Union[str, None, UnsetType] = UNSET + """Additional Context of the ETL pipeline/notebook which creates the process.""" + + ai_dataset_type: Union[str, None, UnsetType] = UNSET + """Dataset type for AI Model - dataset process.""" + + +class FlowDatasetOperationRelationshipAttributes(AssetRelationshipAttributes): + """FlowDatasetOperation-specific relationship attributes for nested API format.""" + + adf_activity: Union[RelatedAdfActivity, None, UnsetType] = UNSET + """ADF Activity that is associated with this lineage process.""" + + airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks that exist within this process.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + fabric_activities: Union[List[RelatedFabricActivity], None, UnsetType] = UNSET + """Individual Fabric activities contained in the process.""" + + fivetran_connector: Union[RelatedFivetranConnector, None, UnsetType] = UNSET + """fivetranConnector in which this process exists.""" + + flow_orchestrated_by: Union[RelatedFlowControlOperation, None, UnsetType] = UNSET + """Orchestrated control operation that ran these data flows (process).""" + + flow_reusable_unit: Union[RelatedFlowReusableUnit, None, UnsetType] = UNSET + """Reusable unit of dataset operations that are all executed together.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + matillion_component: Union[RelatedMatillionComponent, None, UnsetType] = UNSET + """Matillion component that contains the logic for this lineage process.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + power_bi_dataflow: Union[RelatedPowerBIDataflow, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIDataflow" + ) + """PowerBI Dataflow that is associated with this lineage process.""" + + inputs: Union[List[RelatedCatalog], None, UnsetType] = UNSET + """Assets that are inputs to this process.""" + + outputs: Union[List[RelatedCatalog], None, UnsetType] = UNSET + """Assets that are outputs from this process.""" + + column_processes: Union[List[RelatedColumnProcess], None, UnsetType] = UNSET + """Processes that detail column-level lineage for this process.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + sql_procedures: Union[List[RelatedProcedure], None, UnsetType] = UNSET + """Procedures used by this process.""" + + sql_functions: Union[List[RelatedFunction], None, UnsetType] = UNSET + """Functions used by this process.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class FlowDatasetOperationNested(AssetNested): + """FlowDatasetOperation in nested API format for high-performance serialization.""" + + attributes: Union[FlowDatasetOperationAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + FlowDatasetOperationRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + FlowDatasetOperationRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + FlowDatasetOperationRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_FLOW_DATASET_OPERATION_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "adf_activity", + "airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "fabric_activities", + "fivetran_connector", + "flow_orchestrated_by", + "flow_reusable_unit", + "meanings", + "matillion_component", + "mc_monitors", + "mc_incidents", + "power_bi_dataflow", + "inputs", + "outputs", + "column_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "sql_procedures", + "sql_functions", + "schema_registry_subjects", + "soda_checks", + "spark_jobs", +] + + +def _populate_flow_dataset_operation_attrs( + attrs: FlowDatasetOperationAttributes, obj: FlowDatasetOperation +) -> None: + """Populate FlowDatasetOperation-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.flow_started_at = obj.flow_started_at + attrs.flow_finished_at = obj.flow_finished_at + attrs.flow_status = obj.flow_status + attrs.flow_schedule = obj.flow_schedule + attrs.flow_project_name = obj.flow_project_name + attrs.flow_project_qualified_name = obj.flow_project_qualified_name + attrs.flow_folder_name = obj.flow_folder_name + attrs.flow_folder_qualified_name = obj.flow_folder_qualified_name + attrs.flow_reusable_unit_name = obj.flow_reusable_unit_name + attrs.flow_reusable_unit_qualified_name = obj.flow_reusable_unit_qualified_name + attrs.flow_id = obj.flow_id + attrs.flow_run_id = obj.flow_run_id + attrs.flow_error_message = obj.flow_error_message + attrs.flow_input_parameters = obj.flow_input_parameters + attrs.code = obj.code + attrs.sql = obj.sql + attrs.parent_connection_process_qualified_name = ( + obj.parent_connection_process_qualified_name + ) + attrs.ast = obj.ast + attrs.additional_etl_context = obj.additional_etl_context + attrs.ai_dataset_type = obj.ai_dataset_type + + +def _extract_flow_dataset_operation_attrs( + attrs: FlowDatasetOperationAttributes, +) -> dict: + """Extract all FlowDatasetOperation attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["flow_started_at"] = attrs.flow_started_at + result["flow_finished_at"] = attrs.flow_finished_at + result["flow_status"] = attrs.flow_status + result["flow_schedule"] = attrs.flow_schedule + result["flow_project_name"] = attrs.flow_project_name + result["flow_project_qualified_name"] = attrs.flow_project_qualified_name + result["flow_folder_name"] = attrs.flow_folder_name + result["flow_folder_qualified_name"] = attrs.flow_folder_qualified_name + result["flow_reusable_unit_name"] = attrs.flow_reusable_unit_name + result["flow_reusable_unit_qualified_name"] = ( + attrs.flow_reusable_unit_qualified_name + ) + result["flow_id"] = attrs.flow_id + result["flow_run_id"] = attrs.flow_run_id + result["flow_error_message"] = attrs.flow_error_message + result["flow_input_parameters"] = attrs.flow_input_parameters + result["code"] = attrs.code + result["sql"] = attrs.sql + result["parent_connection_process_qualified_name"] = ( + attrs.parent_connection_process_qualified_name + ) + result["ast"] = attrs.ast + result["additional_etl_context"] = attrs.additional_etl_context + result["ai_dataset_type"] = attrs.ai_dataset_type + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _flow_dataset_operation_to_nested( + flow_dataset_operation: FlowDatasetOperation, +) -> FlowDatasetOperationNested: + """Convert flat FlowDatasetOperation to nested format.""" + attrs = FlowDatasetOperationAttributes() + _populate_flow_dataset_operation_attrs(attrs, flow_dataset_operation) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + flow_dataset_operation, + _FLOW_DATASET_OPERATION_REL_FIELDS, + FlowDatasetOperationRelationshipAttributes, + ) + return FlowDatasetOperationNested( + guid=flow_dataset_operation.guid, + type_name=flow_dataset_operation.type_name, + status=flow_dataset_operation.status, + version=flow_dataset_operation.version, + create_time=flow_dataset_operation.create_time, + update_time=flow_dataset_operation.update_time, + created_by=flow_dataset_operation.created_by, + updated_by=flow_dataset_operation.updated_by, + classifications=flow_dataset_operation.classifications, + classification_names=flow_dataset_operation.classification_names, + meanings=flow_dataset_operation.meanings, + labels=flow_dataset_operation.labels, + business_attributes=flow_dataset_operation.business_attributes, + custom_attributes=flow_dataset_operation.custom_attributes, + pending_tasks=flow_dataset_operation.pending_tasks, + proxy=flow_dataset_operation.proxy, + is_incomplete=flow_dataset_operation.is_incomplete, + provenance_type=flow_dataset_operation.provenance_type, + home_id=flow_dataset_operation.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _flow_dataset_operation_from_nested( + nested: FlowDatasetOperationNested, +) -> FlowDatasetOperation: + """Convert nested format to flat FlowDatasetOperation.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else FlowDatasetOperationAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _FLOW_DATASET_OPERATION_REL_FIELDS, + FlowDatasetOperationRelationshipAttributes, + ) + return FlowDatasetOperation( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_flow_dataset_operation_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _flow_dataset_operation_to_nested_bytes( + flow_dataset_operation: FlowDatasetOperation, serde: Serde +) -> bytes: + """Convert flat FlowDatasetOperation to nested JSON bytes.""" + return serde.encode(_flow_dataset_operation_to_nested(flow_dataset_operation)) + + +def _flow_dataset_operation_from_nested_bytes( + data: bytes, serde: Serde +) -> FlowDatasetOperation: + """Convert nested JSON bytes to flat FlowDatasetOperation.""" + nested = serde.decode(data, FlowDatasetOperationNested) + return _flow_dataset_operation_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +FlowDatasetOperation.FLOW_STARTED_AT = NumericField("flowStartedAt", "flowStartedAt") +FlowDatasetOperation.FLOW_FINISHED_AT = NumericField("flowFinishedAt", "flowFinishedAt") +FlowDatasetOperation.FLOW_STATUS = KeywordField("flowStatus", "flowStatus") +FlowDatasetOperation.FLOW_SCHEDULE = KeywordField("flowSchedule", "flowSchedule") +FlowDatasetOperation.FLOW_PROJECT_NAME = KeywordTextField( + "flowProjectName", "flowProjectName", "flowProjectName.text" +) +FlowDatasetOperation.FLOW_PROJECT_QUALIFIED_NAME = KeywordField( + "flowProjectQualifiedName", "flowProjectQualifiedName" +) +FlowDatasetOperation.FLOW_FOLDER_NAME = KeywordTextField( + "flowFolderName", "flowFolderName", "flowFolderName.text" +) +FlowDatasetOperation.FLOW_FOLDER_QUALIFIED_NAME = KeywordField( + "flowFolderQualifiedName", "flowFolderQualifiedName" +) +FlowDatasetOperation.FLOW_REUSABLE_UNIT_NAME = KeywordTextField( + "flowReusableUnitName", "flowReusableUnitName", "flowReusableUnitName.text" +) +FlowDatasetOperation.FLOW_REUSABLE_UNIT_QUALIFIED_NAME = KeywordField( + "flowReusableUnitQualifiedName", "flowReusableUnitQualifiedName" +) +FlowDatasetOperation.FLOW_ID = KeywordField("flowId", "flowId") +FlowDatasetOperation.FLOW_RUN_ID = KeywordField("flowRunId", "flowRunId") +FlowDatasetOperation.FLOW_ERROR_MESSAGE = KeywordField( + "flowErrorMessage", "flowErrorMessage" +) +FlowDatasetOperation.FLOW_INPUT_PARAMETERS = KeywordField( + "flowInputParameters", "flowInputParameters" +) +FlowDatasetOperation.CODE = KeywordField("code", "code") +FlowDatasetOperation.SQL = KeywordField("sql", "sql") +FlowDatasetOperation.PARENT_CONNECTION_PROCESS_QUALIFIED_NAME = KeywordField( + "parentConnectionProcessQualifiedName", "parentConnectionProcessQualifiedName" +) +FlowDatasetOperation.AST = KeywordField("ast", "ast") +FlowDatasetOperation.ADDITIONAL_ETL_CONTEXT = KeywordField( + "additionalEtlContext", "additionalEtlContext" +) +FlowDatasetOperation.AI_DATASET_TYPE = KeywordField("aiDatasetType", "aiDatasetType") +FlowDatasetOperation.ADF_ACTIVITY = RelationField("adfActivity") +FlowDatasetOperation.AIRFLOW_TASKS = RelationField("airflowTasks") +FlowDatasetOperation.ANOMALO_CHECKS = RelationField("anomaloChecks") +FlowDatasetOperation.APPLICATION = RelationField("application") +FlowDatasetOperation.APPLICATION_FIELD = RelationField("applicationField") +FlowDatasetOperation.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +FlowDatasetOperation.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +FlowDatasetOperation.METRICS = RelationField("metrics") +FlowDatasetOperation.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +FlowDatasetOperation.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +FlowDatasetOperation.FABRIC_ACTIVITIES = RelationField("fabricActivities") +FlowDatasetOperation.FIVETRAN_CONNECTOR = RelationField("fivetranConnector") +FlowDatasetOperation.FLOW_ORCHESTRATED_BY = RelationField("flowOrchestratedBy") +FlowDatasetOperation.FLOW_REUSABLE_UNIT = RelationField("flowReusableUnit") +FlowDatasetOperation.MEANINGS = RelationField("meanings") +FlowDatasetOperation.MATILLION_COMPONENT = RelationField("matillionComponent") +FlowDatasetOperation.MC_MONITORS = RelationField("mcMonitors") +FlowDatasetOperation.MC_INCIDENTS = RelationField("mcIncidents") +FlowDatasetOperation.POWER_BI_DATAFLOW = RelationField("powerBIDataflow") +FlowDatasetOperation.INPUTS = RelationField("inputs") +FlowDatasetOperation.OUTPUTS = RelationField("outputs") +FlowDatasetOperation.COLUMN_PROCESSES = RelationField("columnProcesses") +FlowDatasetOperation.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +FlowDatasetOperation.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +FlowDatasetOperation.FILES = RelationField("files") +FlowDatasetOperation.LINKS = RelationField("links") +FlowDatasetOperation.README = RelationField("readme") +FlowDatasetOperation.SQL_PROCEDURES = RelationField("sqlProcedures") +FlowDatasetOperation.SQL_FUNCTIONS = RelationField("sqlFunctions") +FlowDatasetOperation.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +FlowDatasetOperation.SODA_CHECKS = RelationField("sodaChecks") +FlowDatasetOperation.SPARK_JOBS = RelationField("sparkJobs") diff --git a/pyatlan_v9/model/assets/flow_field.py b/pyatlan_v9/model/assets/flow_field.py new file mode 100644 index 000000000..84e128658 --- /dev/null +++ b/pyatlan_v9/model/assets/flow_field.py @@ -0,0 +1,749 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +FlowField asset model with flattened inheritance. + +This module provides: +- FlowField: Flat asset class (easy to use) +- FlowFieldAttributes: Nested attributes struct (extends AssetAttributes) +- FlowFieldNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .flow_related import RelatedFlowDataset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class FlowField(Asset): + """ + A single field of data within a broader ephemeral dataset. + """ + + FLOW_DATASET_NAME: ClassVar[Any] = None + FLOW_DATASET_QUALIFIED_NAME: ClassVar[Any] = None + FLOW_DATA_TYPE: ClassVar[Any] = None + FLOW_EXPRESSION: ClassVar[Any] = None + FLOW_STARTED_AT: ClassVar[Any] = None + FLOW_FINISHED_AT: ClassVar[Any] = None + FLOW_STATUS: ClassVar[Any] = None + FLOW_SCHEDULE: ClassVar[Any] = None + FLOW_PROJECT_NAME: ClassVar[Any] = None + FLOW_PROJECT_QUALIFIED_NAME: ClassVar[Any] = None + FLOW_FOLDER_NAME: ClassVar[Any] = None + FLOW_FOLDER_QUALIFIED_NAME: ClassVar[Any] = None + FLOW_REUSABLE_UNIT_NAME: ClassVar[Any] = None + FLOW_REUSABLE_UNIT_QUALIFIED_NAME: ClassVar[Any] = None + FLOW_ID: ClassVar[Any] = None + FLOW_RUN_ID: ClassVar[Any] = None + FLOW_ERROR_MESSAGE: ClassVar[Any] = None + FLOW_INPUT_PARAMETERS: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + FLOW_DATASET: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "FlowField" + + flow_dataset_name: Union[str, None, UnsetType] = UNSET + """Simple name of the ephemeral dataset in which this field is contained.""" + + flow_dataset_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the ephemeral dataset in which this field is contained.""" + + flow_data_type: Union[str, None, UnsetType] = UNSET + """Type of the data captured in this field.""" + + flow_expression: Union[str, None, UnsetType] = UNSET + """Logic that is applied, injected or otherwise used as part of producing this ephemeral field of data.""" + + flow_started_at: Union[int, None, UnsetType] = UNSET + """Date and time at which this point in the data processing or orchestration started.""" + + flow_finished_at: Union[int, None, UnsetType] = UNSET + """Date and time at which this point in the data processing or orchestration finished.""" + + flow_status: Union[str, None, UnsetType] = UNSET + """Overall status of this point in the data processing or orchestration.""" + + flow_schedule: Union[str, None, UnsetType] = UNSET + """Schedule for this point in the data processing or orchestration.""" + + flow_project_name: Union[str, None, UnsetType] = UNSET + """Simple name of the project in which this asset is contained.""" + + flow_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this asset is contained.""" + + flow_folder_name: Union[str, None, UnsetType] = UNSET + """Simple name of the folder in which this asset is contained.""" + + flow_folder_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the folder in which this asset is contained.""" + + flow_reusable_unit_name: Union[str, None, UnsetType] = UNSET + """Simple name of the reusable grouping of operations in which this ephemeral data is contained.""" + + flow_reusable_unit_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the reusable grouping of operations in which this ephemeral data is contained.""" + + flow_id: Union[str, None, UnsetType] = UNSET + """Unique ID for this flow asset, which will remain constant throughout the lifecycle of the asset.""" + + flow_run_id: Union[str, None, UnsetType] = UNSET + """Unique ID of the flow run, which could change on subsequent runs of the same flow.""" + + flow_error_message: Union[str, None, UnsetType] = UNSET + """Optional error message of the flow run.""" + + flow_input_parameters: Union[Dict[str, str], None, UnsetType] = UNSET + """Input parameters for the flow run.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + flow_dataset: Union[RelatedFlowDataset, None, UnsetType] = UNSET + """Ephemeral dataset that contains these fields.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "FlowField" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _flow_field_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> FlowField: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + FlowField instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _flow_field_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class FlowFieldAttributes(AssetAttributes): + """FlowField-specific attributes for nested API format.""" + + flow_dataset_name: Union[str, None, UnsetType] = UNSET + """Simple name of the ephemeral dataset in which this field is contained.""" + + flow_dataset_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the ephemeral dataset in which this field is contained.""" + + flow_data_type: Union[str, None, UnsetType] = UNSET + """Type of the data captured in this field.""" + + flow_expression: Union[str, None, UnsetType] = UNSET + """Logic that is applied, injected or otherwise used as part of producing this ephemeral field of data.""" + + flow_started_at: Union[int, None, UnsetType] = UNSET + """Date and time at which this point in the data processing or orchestration started.""" + + flow_finished_at: Union[int, None, UnsetType] = UNSET + """Date and time at which this point in the data processing or orchestration finished.""" + + flow_status: Union[str, None, UnsetType] = UNSET + """Overall status of this point in the data processing or orchestration.""" + + flow_schedule: Union[str, None, UnsetType] = UNSET + """Schedule for this point in the data processing or orchestration.""" + + flow_project_name: Union[str, None, UnsetType] = UNSET + """Simple name of the project in which this asset is contained.""" + + flow_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this asset is contained.""" + + flow_folder_name: Union[str, None, UnsetType] = UNSET + """Simple name of the folder in which this asset is contained.""" + + flow_folder_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the folder in which this asset is contained.""" + + flow_reusable_unit_name: Union[str, None, UnsetType] = UNSET + """Simple name of the reusable grouping of operations in which this ephemeral data is contained.""" + + flow_reusable_unit_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the reusable grouping of operations in which this ephemeral data is contained.""" + + flow_id: Union[str, None, UnsetType] = UNSET + """Unique ID for this flow asset, which will remain constant throughout the lifecycle of the asset.""" + + flow_run_id: Union[str, None, UnsetType] = UNSET + """Unique ID of the flow run, which could change on subsequent runs of the same flow.""" + + flow_error_message: Union[str, None, UnsetType] = UNSET + """Optional error message of the flow run.""" + + flow_input_parameters: Union[Dict[str, str], None, UnsetType] = UNSET + """Input parameters for the flow run.""" + + +class FlowFieldRelationshipAttributes(AssetRelationshipAttributes): + """FlowField-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + flow_dataset: Union[RelatedFlowDataset, None, UnsetType] = UNSET + """Ephemeral dataset that contains these fields.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class FlowFieldNested(AssetNested): + """FlowField in nested API format for high-performance serialization.""" + + attributes: Union[FlowFieldAttributes, UnsetType] = UNSET + relationship_attributes: Union[FlowFieldRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + FlowFieldRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + FlowFieldRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_FLOW_FIELD_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "flow_dataset", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_flow_field_attrs(attrs: FlowFieldAttributes, obj: FlowField) -> None: + """Populate FlowField-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.flow_dataset_name = obj.flow_dataset_name + attrs.flow_dataset_qualified_name = obj.flow_dataset_qualified_name + attrs.flow_data_type = obj.flow_data_type + attrs.flow_expression = obj.flow_expression + attrs.flow_started_at = obj.flow_started_at + attrs.flow_finished_at = obj.flow_finished_at + attrs.flow_status = obj.flow_status + attrs.flow_schedule = obj.flow_schedule + attrs.flow_project_name = obj.flow_project_name + attrs.flow_project_qualified_name = obj.flow_project_qualified_name + attrs.flow_folder_name = obj.flow_folder_name + attrs.flow_folder_qualified_name = obj.flow_folder_qualified_name + attrs.flow_reusable_unit_name = obj.flow_reusable_unit_name + attrs.flow_reusable_unit_qualified_name = obj.flow_reusable_unit_qualified_name + attrs.flow_id = obj.flow_id + attrs.flow_run_id = obj.flow_run_id + attrs.flow_error_message = obj.flow_error_message + attrs.flow_input_parameters = obj.flow_input_parameters + + +def _extract_flow_field_attrs(attrs: FlowFieldAttributes) -> dict: + """Extract all FlowField attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["flow_dataset_name"] = attrs.flow_dataset_name + result["flow_dataset_qualified_name"] = attrs.flow_dataset_qualified_name + result["flow_data_type"] = attrs.flow_data_type + result["flow_expression"] = attrs.flow_expression + result["flow_started_at"] = attrs.flow_started_at + result["flow_finished_at"] = attrs.flow_finished_at + result["flow_status"] = attrs.flow_status + result["flow_schedule"] = attrs.flow_schedule + result["flow_project_name"] = attrs.flow_project_name + result["flow_project_qualified_name"] = attrs.flow_project_qualified_name + result["flow_folder_name"] = attrs.flow_folder_name + result["flow_folder_qualified_name"] = attrs.flow_folder_qualified_name + result["flow_reusable_unit_name"] = attrs.flow_reusable_unit_name + result["flow_reusable_unit_qualified_name"] = ( + attrs.flow_reusable_unit_qualified_name + ) + result["flow_id"] = attrs.flow_id + result["flow_run_id"] = attrs.flow_run_id + result["flow_error_message"] = attrs.flow_error_message + result["flow_input_parameters"] = attrs.flow_input_parameters + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _flow_field_to_nested(flow_field: FlowField) -> FlowFieldNested: + """Convert flat FlowField to nested format.""" + attrs = FlowFieldAttributes() + _populate_flow_field_attrs(attrs, flow_field) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + flow_field, _FLOW_FIELD_REL_FIELDS, FlowFieldRelationshipAttributes + ) + return FlowFieldNested( + guid=flow_field.guid, + type_name=flow_field.type_name, + status=flow_field.status, + version=flow_field.version, + create_time=flow_field.create_time, + update_time=flow_field.update_time, + created_by=flow_field.created_by, + updated_by=flow_field.updated_by, + classifications=flow_field.classifications, + classification_names=flow_field.classification_names, + meanings=flow_field.meanings, + labels=flow_field.labels, + business_attributes=flow_field.business_attributes, + custom_attributes=flow_field.custom_attributes, + pending_tasks=flow_field.pending_tasks, + proxy=flow_field.proxy, + is_incomplete=flow_field.is_incomplete, + provenance_type=flow_field.provenance_type, + home_id=flow_field.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _flow_field_from_nested(nested: FlowFieldNested) -> FlowField: + """Convert nested format to flat FlowField.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else FlowFieldAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _FLOW_FIELD_REL_FIELDS, + FlowFieldRelationshipAttributes, + ) + return FlowField( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_flow_field_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _flow_field_to_nested_bytes(flow_field: FlowField, serde: Serde) -> bytes: + """Convert flat FlowField to nested JSON bytes.""" + return serde.encode(_flow_field_to_nested(flow_field)) + + +def _flow_field_from_nested_bytes(data: bytes, serde: Serde) -> FlowField: + """Convert nested JSON bytes to flat FlowField.""" + nested = serde.decode(data, FlowFieldNested) + return _flow_field_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +FlowField.FLOW_DATASET_NAME = KeywordTextField( + "flowDatasetName", "flowDatasetName", "flowDatasetName.text" +) +FlowField.FLOW_DATASET_QUALIFIED_NAME = KeywordField( + "flowDatasetQualifiedName", "flowDatasetQualifiedName" +) +FlowField.FLOW_DATA_TYPE = KeywordField("flowDataType", "flowDataType") +FlowField.FLOW_EXPRESSION = KeywordField("flowExpression", "flowExpression") +FlowField.FLOW_STARTED_AT = NumericField("flowStartedAt", "flowStartedAt") +FlowField.FLOW_FINISHED_AT = NumericField("flowFinishedAt", "flowFinishedAt") +FlowField.FLOW_STATUS = KeywordField("flowStatus", "flowStatus") +FlowField.FLOW_SCHEDULE = KeywordField("flowSchedule", "flowSchedule") +FlowField.FLOW_PROJECT_NAME = KeywordTextField( + "flowProjectName", "flowProjectName", "flowProjectName.text" +) +FlowField.FLOW_PROJECT_QUALIFIED_NAME = KeywordField( + "flowProjectQualifiedName", "flowProjectQualifiedName" +) +FlowField.FLOW_FOLDER_NAME = KeywordTextField( + "flowFolderName", "flowFolderName", "flowFolderName.text" +) +FlowField.FLOW_FOLDER_QUALIFIED_NAME = KeywordField( + "flowFolderQualifiedName", "flowFolderQualifiedName" +) +FlowField.FLOW_REUSABLE_UNIT_NAME = KeywordTextField( + "flowReusableUnitName", "flowReusableUnitName", "flowReusableUnitName.text" +) +FlowField.FLOW_REUSABLE_UNIT_QUALIFIED_NAME = KeywordField( + "flowReusableUnitQualifiedName", "flowReusableUnitQualifiedName" +) +FlowField.FLOW_ID = KeywordField("flowId", "flowId") +FlowField.FLOW_RUN_ID = KeywordField("flowRunId", "flowRunId") +FlowField.FLOW_ERROR_MESSAGE = KeywordField("flowErrorMessage", "flowErrorMessage") +FlowField.FLOW_INPUT_PARAMETERS = KeywordField( + "flowInputParameters", "flowInputParameters" +) +FlowField.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +FlowField.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +FlowField.ANOMALO_CHECKS = RelationField("anomaloChecks") +FlowField.APPLICATION = RelationField("application") +FlowField.APPLICATION_FIELD = RelationField("applicationField") +FlowField.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +FlowField.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +FlowField.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +FlowField.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +FlowField.METRICS = RelationField("metrics") +FlowField.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +FlowField.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +FlowField.FLOW_DATASET = RelationField("flowDataset") +FlowField.MEANINGS = RelationField("meanings") +FlowField.MC_MONITORS = RelationField("mcMonitors") +FlowField.MC_INCIDENTS = RelationField("mcIncidents") +FlowField.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +FlowField.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +FlowField.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +FlowField.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +FlowField.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +FlowField.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +FlowField.FILES = RelationField("files") +FlowField.LINKS = RelationField("links") +FlowField.README = RelationField("readme") +FlowField.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +FlowField.SODA_CHECKS = RelationField("sodaChecks") +FlowField.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +FlowField.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/flow_field_operation.py b/pyatlan_v9/model/assets/flow_field_operation.py new file mode 100644 index 000000000..afc7eee97 --- /dev/null +++ b/pyatlan_v9/model/assets/flow_field_operation.py @@ -0,0 +1,817 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +FlowFieldOperation asset model with flattened inheritance. + +This module provides: +- FlowFieldOperation: Flat asset class (easy to use) +- FlowFieldOperationAttributes: Nested attributes struct (extends AssetAttributes) +- FlowFieldOperationNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .adf_related import RelatedAdfActivity +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .catalog_related import RelatedCatalog +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .fabric_related import RelatedFabricActivity +from .fivetran_related import RelatedFivetranConnector +from .gtc_related import RelatedAtlasGlossaryTerm +from .matillion_related import RelatedMatillionComponent +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .power_bi_related import RelatedPowerBIDataflow +from .process_related import RelatedColumnProcess, RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from .sql_related import RelatedFunction, RelatedProcedure +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .flow_related import RelatedFlowControlOperation + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class FlowFieldOperation(Asset): + """ + A nested field-level operation that uses at least one ephemeral field as either an input or output. + """ + + FLOW_STARTED_AT: ClassVar[Any] = None + FLOW_FINISHED_AT: ClassVar[Any] = None + FLOW_STATUS: ClassVar[Any] = None + FLOW_SCHEDULE: ClassVar[Any] = None + FLOW_PROJECT_NAME: ClassVar[Any] = None + FLOW_PROJECT_QUALIFIED_NAME: ClassVar[Any] = None + FLOW_FOLDER_NAME: ClassVar[Any] = None + FLOW_FOLDER_QUALIFIED_NAME: ClassVar[Any] = None + FLOW_REUSABLE_UNIT_NAME: ClassVar[Any] = None + FLOW_REUSABLE_UNIT_QUALIFIED_NAME: ClassVar[Any] = None + FLOW_ID: ClassVar[Any] = None + FLOW_RUN_ID: ClassVar[Any] = None + FLOW_ERROR_MESSAGE: ClassVar[Any] = None + FLOW_INPUT_PARAMETERS: ClassVar[Any] = None + CODE: ClassVar[Any] = None + SQL: ClassVar[Any] = None + PARENT_CONNECTION_PROCESS_QUALIFIED_NAME: ClassVar[Any] = None + AST: ClassVar[Any] = None + ADDITIONAL_ETL_CONTEXT: ClassVar[Any] = None + AI_DATASET_TYPE: ClassVar[Any] = None + ADF_ACTIVITY: ClassVar[Any] = None + AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + FABRIC_ACTIVITIES: ClassVar[Any] = None + FIVETRAN_CONNECTOR: ClassVar[Any] = None + FLOW_ORCHESTRATED_BY: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MATILLION_COMPONENT: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + POWER_BI_DATAFLOW: ClassVar[Any] = None + INPUTS: ClassVar[Any] = None + OUTPUTS: ClassVar[Any] = None + COLUMN_PROCESSES: ClassVar[Any] = None + PROCESS: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SQL_PROCEDURES: ClassVar[Any] = None + SQL_FUNCTIONS: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "FlowFieldOperation" + + flow_started_at: Union[int, None, UnsetType] = UNSET + """Date and time at which this point in the data processing or orchestration started.""" + + flow_finished_at: Union[int, None, UnsetType] = UNSET + """Date and time at which this point in the data processing or orchestration finished.""" + + flow_status: Union[str, None, UnsetType] = UNSET + """Overall status of this point in the data processing or orchestration.""" + + flow_schedule: Union[str, None, UnsetType] = UNSET + """Schedule for this point in the data processing or orchestration.""" + + flow_project_name: Union[str, None, UnsetType] = UNSET + """Simple name of the project in which this asset is contained.""" + + flow_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this asset is contained.""" + + flow_folder_name: Union[str, None, UnsetType] = UNSET + """Simple name of the folder in which this asset is contained.""" + + flow_folder_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the folder in which this asset is contained.""" + + flow_reusable_unit_name: Union[str, None, UnsetType] = UNSET + """Simple name of the reusable grouping of operations in which this ephemeral data is contained.""" + + flow_reusable_unit_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the reusable grouping of operations in which this ephemeral data is contained.""" + + flow_id: Union[str, None, UnsetType] = UNSET + """Unique ID for this flow asset, which will remain constant throughout the lifecycle of the asset.""" + + flow_run_id: Union[str, None, UnsetType] = UNSET + """Unique ID of the flow run, which could change on subsequent runs of the same flow.""" + + flow_error_message: Union[str, None, UnsetType] = UNSET + """Optional error message of the flow run.""" + + flow_input_parameters: Union[Dict[str, str], None, UnsetType] = UNSET + """Input parameters for the flow run.""" + + code: Union[str, None, UnsetType] = UNSET + """Code that ran within the process.""" + + sql: Union[str, None, UnsetType] = UNSET + """SQL query that ran to produce the outputs.""" + + parent_connection_process_qualified_name: Union[List[str], None, UnsetType] = UNSET + """""" + + ast: Union[str, None, UnsetType] = UNSET + """Parsed AST of the code or SQL statements that describe the logic of this process.""" + + additional_etl_context: Union[str, None, UnsetType] = UNSET + """Additional Context of the ETL pipeline/notebook which creates the process.""" + + ai_dataset_type: Union[str, None, UnsetType] = UNSET + """Dataset type for AI Model - dataset process.""" + + adf_activity: Union[RelatedAdfActivity, None, UnsetType] = UNSET + """ADF Activity that is associated with this lineage process.""" + + airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks that exist within this process.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + fabric_activities: Union[List[RelatedFabricActivity], None, UnsetType] = UNSET + """Individual Fabric activities contained in the process.""" + + fivetran_connector: Union[RelatedFivetranConnector, None, UnsetType] = UNSET + """fivetranConnector in which this process exists.""" + + flow_orchestrated_by: Union[RelatedFlowControlOperation, None, UnsetType] = UNSET + """Orchestrated control operation that ran these data flows (process).""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + matillion_component: Union[RelatedMatillionComponent, None, UnsetType] = UNSET + """Matillion component that contains the logic for this lineage process.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + power_bi_dataflow: Union[RelatedPowerBIDataflow, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIDataflow" + ) + """PowerBI Dataflow that is associated with this lineage process.""" + + inputs: Union[List[RelatedCatalog], None, UnsetType] = UNSET + """Assets that are inputs to this process.""" + + outputs: Union[List[RelatedCatalog], None, UnsetType] = UNSET + """Assets that are outputs from this process.""" + + column_processes: Union[List[RelatedColumnProcess], None, UnsetType] = UNSET + """Processes that detail column-level lineage for this process.""" + + process: Union[RelatedProcess, None, UnsetType] = UNSET + """Parent process that contains this column-level process.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + sql_procedures: Union[List[RelatedProcedure], None, UnsetType] = UNSET + """Procedures used by this process.""" + + sql_functions: Union[List[RelatedFunction], None, UnsetType] = UNSET + """Functions used by this process.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "FlowFieldOperation" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _flow_field_operation_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> FlowFieldOperation: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + FlowFieldOperation instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _flow_field_operation_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class FlowFieldOperationAttributes(AssetAttributes): + """FlowFieldOperation-specific attributes for nested API format.""" + + flow_started_at: Union[int, None, UnsetType] = UNSET + """Date and time at which this point in the data processing or orchestration started.""" + + flow_finished_at: Union[int, None, UnsetType] = UNSET + """Date and time at which this point in the data processing or orchestration finished.""" + + flow_status: Union[str, None, UnsetType] = UNSET + """Overall status of this point in the data processing or orchestration.""" + + flow_schedule: Union[str, None, UnsetType] = UNSET + """Schedule for this point in the data processing or orchestration.""" + + flow_project_name: Union[str, None, UnsetType] = UNSET + """Simple name of the project in which this asset is contained.""" + + flow_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this asset is contained.""" + + flow_folder_name: Union[str, None, UnsetType] = UNSET + """Simple name of the folder in which this asset is contained.""" + + flow_folder_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the folder in which this asset is contained.""" + + flow_reusable_unit_name: Union[str, None, UnsetType] = UNSET + """Simple name of the reusable grouping of operations in which this ephemeral data is contained.""" + + flow_reusable_unit_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the reusable grouping of operations in which this ephemeral data is contained.""" + + flow_id: Union[str, None, UnsetType] = UNSET + """Unique ID for this flow asset, which will remain constant throughout the lifecycle of the asset.""" + + flow_run_id: Union[str, None, UnsetType] = UNSET + """Unique ID of the flow run, which could change on subsequent runs of the same flow.""" + + flow_error_message: Union[str, None, UnsetType] = UNSET + """Optional error message of the flow run.""" + + flow_input_parameters: Union[Dict[str, str], None, UnsetType] = UNSET + """Input parameters for the flow run.""" + + code: Union[str, None, UnsetType] = UNSET + """Code that ran within the process.""" + + sql: Union[str, None, UnsetType] = UNSET + """SQL query that ran to produce the outputs.""" + + parent_connection_process_qualified_name: Union[List[str], None, UnsetType] = UNSET + """""" + + ast: Union[str, None, UnsetType] = UNSET + """Parsed AST of the code or SQL statements that describe the logic of this process.""" + + additional_etl_context: Union[str, None, UnsetType] = UNSET + """Additional Context of the ETL pipeline/notebook which creates the process.""" + + ai_dataset_type: Union[str, None, UnsetType] = UNSET + """Dataset type for AI Model - dataset process.""" + + +class FlowFieldOperationRelationshipAttributes(AssetRelationshipAttributes): + """FlowFieldOperation-specific relationship attributes for nested API format.""" + + adf_activity: Union[RelatedAdfActivity, None, UnsetType] = UNSET + """ADF Activity that is associated with this lineage process.""" + + airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks that exist within this process.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + fabric_activities: Union[List[RelatedFabricActivity], None, UnsetType] = UNSET + """Individual Fabric activities contained in the process.""" + + fivetran_connector: Union[RelatedFivetranConnector, None, UnsetType] = UNSET + """fivetranConnector in which this process exists.""" + + flow_orchestrated_by: Union[RelatedFlowControlOperation, None, UnsetType] = UNSET + """Orchestrated control operation that ran these data flows (process).""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + matillion_component: Union[RelatedMatillionComponent, None, UnsetType] = UNSET + """Matillion component that contains the logic for this lineage process.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + power_bi_dataflow: Union[RelatedPowerBIDataflow, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIDataflow" + ) + """PowerBI Dataflow that is associated with this lineage process.""" + + inputs: Union[List[RelatedCatalog], None, UnsetType] = UNSET + """Assets that are inputs to this process.""" + + outputs: Union[List[RelatedCatalog], None, UnsetType] = UNSET + """Assets that are outputs from this process.""" + + column_processes: Union[List[RelatedColumnProcess], None, UnsetType] = UNSET + """Processes that detail column-level lineage for this process.""" + + process: Union[RelatedProcess, None, UnsetType] = UNSET + """Parent process that contains this column-level process.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + sql_procedures: Union[List[RelatedProcedure], None, UnsetType] = UNSET + """Procedures used by this process.""" + + sql_functions: Union[List[RelatedFunction], None, UnsetType] = UNSET + """Functions used by this process.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class FlowFieldOperationNested(AssetNested): + """FlowFieldOperation in nested API format for high-performance serialization.""" + + attributes: Union[FlowFieldOperationAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + FlowFieldOperationRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + FlowFieldOperationRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + FlowFieldOperationRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_FLOW_FIELD_OPERATION_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "adf_activity", + "airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "fabric_activities", + "fivetran_connector", + "flow_orchestrated_by", + "meanings", + "matillion_component", + "mc_monitors", + "mc_incidents", + "power_bi_dataflow", + "inputs", + "outputs", + "column_processes", + "process", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "sql_procedures", + "sql_functions", + "schema_registry_subjects", + "soda_checks", + "spark_jobs", +] + + +def _populate_flow_field_operation_attrs( + attrs: FlowFieldOperationAttributes, obj: FlowFieldOperation +) -> None: + """Populate FlowFieldOperation-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.flow_started_at = obj.flow_started_at + attrs.flow_finished_at = obj.flow_finished_at + attrs.flow_status = obj.flow_status + attrs.flow_schedule = obj.flow_schedule + attrs.flow_project_name = obj.flow_project_name + attrs.flow_project_qualified_name = obj.flow_project_qualified_name + attrs.flow_folder_name = obj.flow_folder_name + attrs.flow_folder_qualified_name = obj.flow_folder_qualified_name + attrs.flow_reusable_unit_name = obj.flow_reusable_unit_name + attrs.flow_reusable_unit_qualified_name = obj.flow_reusable_unit_qualified_name + attrs.flow_id = obj.flow_id + attrs.flow_run_id = obj.flow_run_id + attrs.flow_error_message = obj.flow_error_message + attrs.flow_input_parameters = obj.flow_input_parameters + attrs.code = obj.code + attrs.sql = obj.sql + attrs.parent_connection_process_qualified_name = ( + obj.parent_connection_process_qualified_name + ) + attrs.ast = obj.ast + attrs.additional_etl_context = obj.additional_etl_context + attrs.ai_dataset_type = obj.ai_dataset_type + + +def _extract_flow_field_operation_attrs(attrs: FlowFieldOperationAttributes) -> dict: + """Extract all FlowFieldOperation attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["flow_started_at"] = attrs.flow_started_at + result["flow_finished_at"] = attrs.flow_finished_at + result["flow_status"] = attrs.flow_status + result["flow_schedule"] = attrs.flow_schedule + result["flow_project_name"] = attrs.flow_project_name + result["flow_project_qualified_name"] = attrs.flow_project_qualified_name + result["flow_folder_name"] = attrs.flow_folder_name + result["flow_folder_qualified_name"] = attrs.flow_folder_qualified_name + result["flow_reusable_unit_name"] = attrs.flow_reusable_unit_name + result["flow_reusable_unit_qualified_name"] = ( + attrs.flow_reusable_unit_qualified_name + ) + result["flow_id"] = attrs.flow_id + result["flow_run_id"] = attrs.flow_run_id + result["flow_error_message"] = attrs.flow_error_message + result["flow_input_parameters"] = attrs.flow_input_parameters + result["code"] = attrs.code + result["sql"] = attrs.sql + result["parent_connection_process_qualified_name"] = ( + attrs.parent_connection_process_qualified_name + ) + result["ast"] = attrs.ast + result["additional_etl_context"] = attrs.additional_etl_context + result["ai_dataset_type"] = attrs.ai_dataset_type + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _flow_field_operation_to_nested( + flow_field_operation: FlowFieldOperation, +) -> FlowFieldOperationNested: + """Convert flat FlowFieldOperation to nested format.""" + attrs = FlowFieldOperationAttributes() + _populate_flow_field_operation_attrs(attrs, flow_field_operation) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + flow_field_operation, + _FLOW_FIELD_OPERATION_REL_FIELDS, + FlowFieldOperationRelationshipAttributes, + ) + return FlowFieldOperationNested( + guid=flow_field_operation.guid, + type_name=flow_field_operation.type_name, + status=flow_field_operation.status, + version=flow_field_operation.version, + create_time=flow_field_operation.create_time, + update_time=flow_field_operation.update_time, + created_by=flow_field_operation.created_by, + updated_by=flow_field_operation.updated_by, + classifications=flow_field_operation.classifications, + classification_names=flow_field_operation.classification_names, + meanings=flow_field_operation.meanings, + labels=flow_field_operation.labels, + business_attributes=flow_field_operation.business_attributes, + custom_attributes=flow_field_operation.custom_attributes, + pending_tasks=flow_field_operation.pending_tasks, + proxy=flow_field_operation.proxy, + is_incomplete=flow_field_operation.is_incomplete, + provenance_type=flow_field_operation.provenance_type, + home_id=flow_field_operation.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _flow_field_operation_from_nested( + nested: FlowFieldOperationNested, +) -> FlowFieldOperation: + """Convert nested format to flat FlowFieldOperation.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else FlowFieldOperationAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _FLOW_FIELD_OPERATION_REL_FIELDS, + FlowFieldOperationRelationshipAttributes, + ) + return FlowFieldOperation( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_flow_field_operation_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _flow_field_operation_to_nested_bytes( + flow_field_operation: FlowFieldOperation, serde: Serde +) -> bytes: + """Convert flat FlowFieldOperation to nested JSON bytes.""" + return serde.encode(_flow_field_operation_to_nested(flow_field_operation)) + + +def _flow_field_operation_from_nested_bytes( + data: bytes, serde: Serde +) -> FlowFieldOperation: + """Convert nested JSON bytes to flat FlowFieldOperation.""" + nested = serde.decode(data, FlowFieldOperationNested) + return _flow_field_operation_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +FlowFieldOperation.FLOW_STARTED_AT = NumericField("flowStartedAt", "flowStartedAt") +FlowFieldOperation.FLOW_FINISHED_AT = NumericField("flowFinishedAt", "flowFinishedAt") +FlowFieldOperation.FLOW_STATUS = KeywordField("flowStatus", "flowStatus") +FlowFieldOperation.FLOW_SCHEDULE = KeywordField("flowSchedule", "flowSchedule") +FlowFieldOperation.FLOW_PROJECT_NAME = KeywordTextField( + "flowProjectName", "flowProjectName", "flowProjectName.text" +) +FlowFieldOperation.FLOW_PROJECT_QUALIFIED_NAME = KeywordField( + "flowProjectQualifiedName", "flowProjectQualifiedName" +) +FlowFieldOperation.FLOW_FOLDER_NAME = KeywordTextField( + "flowFolderName", "flowFolderName", "flowFolderName.text" +) +FlowFieldOperation.FLOW_FOLDER_QUALIFIED_NAME = KeywordField( + "flowFolderQualifiedName", "flowFolderQualifiedName" +) +FlowFieldOperation.FLOW_REUSABLE_UNIT_NAME = KeywordTextField( + "flowReusableUnitName", "flowReusableUnitName", "flowReusableUnitName.text" +) +FlowFieldOperation.FLOW_REUSABLE_UNIT_QUALIFIED_NAME = KeywordField( + "flowReusableUnitQualifiedName", "flowReusableUnitQualifiedName" +) +FlowFieldOperation.FLOW_ID = KeywordField("flowId", "flowId") +FlowFieldOperation.FLOW_RUN_ID = KeywordField("flowRunId", "flowRunId") +FlowFieldOperation.FLOW_ERROR_MESSAGE = KeywordField( + "flowErrorMessage", "flowErrorMessage" +) +FlowFieldOperation.FLOW_INPUT_PARAMETERS = KeywordField( + "flowInputParameters", "flowInputParameters" +) +FlowFieldOperation.CODE = KeywordField("code", "code") +FlowFieldOperation.SQL = KeywordField("sql", "sql") +FlowFieldOperation.PARENT_CONNECTION_PROCESS_QUALIFIED_NAME = KeywordField( + "parentConnectionProcessQualifiedName", "parentConnectionProcessQualifiedName" +) +FlowFieldOperation.AST = KeywordField("ast", "ast") +FlowFieldOperation.ADDITIONAL_ETL_CONTEXT = KeywordField( + "additionalEtlContext", "additionalEtlContext" +) +FlowFieldOperation.AI_DATASET_TYPE = KeywordField("aiDatasetType", "aiDatasetType") +FlowFieldOperation.ADF_ACTIVITY = RelationField("adfActivity") +FlowFieldOperation.AIRFLOW_TASKS = RelationField("airflowTasks") +FlowFieldOperation.ANOMALO_CHECKS = RelationField("anomaloChecks") +FlowFieldOperation.APPLICATION = RelationField("application") +FlowFieldOperation.APPLICATION_FIELD = RelationField("applicationField") +FlowFieldOperation.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +FlowFieldOperation.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +FlowFieldOperation.METRICS = RelationField("metrics") +FlowFieldOperation.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +FlowFieldOperation.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +FlowFieldOperation.FABRIC_ACTIVITIES = RelationField("fabricActivities") +FlowFieldOperation.FIVETRAN_CONNECTOR = RelationField("fivetranConnector") +FlowFieldOperation.FLOW_ORCHESTRATED_BY = RelationField("flowOrchestratedBy") +FlowFieldOperation.MEANINGS = RelationField("meanings") +FlowFieldOperation.MATILLION_COMPONENT = RelationField("matillionComponent") +FlowFieldOperation.MC_MONITORS = RelationField("mcMonitors") +FlowFieldOperation.MC_INCIDENTS = RelationField("mcIncidents") +FlowFieldOperation.POWER_BI_DATAFLOW = RelationField("powerBIDataflow") +FlowFieldOperation.INPUTS = RelationField("inputs") +FlowFieldOperation.OUTPUTS = RelationField("outputs") +FlowFieldOperation.COLUMN_PROCESSES = RelationField("columnProcesses") +FlowFieldOperation.PROCESS = RelationField("process") +FlowFieldOperation.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +FlowFieldOperation.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +FlowFieldOperation.FILES = RelationField("files") +FlowFieldOperation.LINKS = RelationField("links") +FlowFieldOperation.README = RelationField("readme") +FlowFieldOperation.SQL_PROCEDURES = RelationField("sqlProcedures") +FlowFieldOperation.SQL_FUNCTIONS = RelationField("sqlFunctions") +FlowFieldOperation.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +FlowFieldOperation.SODA_CHECKS = RelationField("sodaChecks") +FlowFieldOperation.SPARK_JOBS = RelationField("sparkJobs") diff --git a/pyatlan_v9/model/assets/flow_folder.py b/pyatlan_v9/model/assets/flow_folder.py new file mode 100644 index 000000000..2e9452ff6 --- /dev/null +++ b/pyatlan_v9/model/assets/flow_folder.py @@ -0,0 +1,613 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +FlowFolder asset model with flattened inheritance. + +This module provides: +- FlowFolder: Flat asset class (easy to use) +- FlowFolderAttributes: Nested attributes struct (extends AssetAttributes) +- FlowFolderNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .flow_related import RelatedFlowFolder + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class FlowFolder(Asset): + """ + A grouping mechanism within a project to further organize data processing. + """ + + FLOW_STARTED_AT: ClassVar[Any] = None + FLOW_FINISHED_AT: ClassVar[Any] = None + FLOW_STATUS: ClassVar[Any] = None + FLOW_SCHEDULE: ClassVar[Any] = None + FLOW_PROJECT_NAME: ClassVar[Any] = None + FLOW_PROJECT_QUALIFIED_NAME: ClassVar[Any] = None + FLOW_FOLDER_NAME: ClassVar[Any] = None + FLOW_FOLDER_QUALIFIED_NAME: ClassVar[Any] = None + FLOW_REUSABLE_UNIT_NAME: ClassVar[Any] = None + FLOW_REUSABLE_UNIT_QUALIFIED_NAME: ClassVar[Any] = None + FLOW_ID: ClassVar[Any] = None + FLOW_RUN_ID: ClassVar[Any] = None + FLOW_ERROR_MESSAGE: ClassVar[Any] = None + FLOW_INPUT_PARAMETERS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + FLOW_SUB_FOLDERS: ClassVar[Any] = None + FLOW_PARENT_FOLDER: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "FlowFolder" + + flow_started_at: Union[int, None, UnsetType] = UNSET + """Date and time at which this point in the data processing or orchestration started.""" + + flow_finished_at: Union[int, None, UnsetType] = UNSET + """Date and time at which this point in the data processing or orchestration finished.""" + + flow_status: Union[str, None, UnsetType] = UNSET + """Overall status of this point in the data processing or orchestration.""" + + flow_schedule: Union[str, None, UnsetType] = UNSET + """Schedule for this point in the data processing or orchestration.""" + + flow_project_name: Union[str, None, UnsetType] = UNSET + """Simple name of the project in which this asset is contained.""" + + flow_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this asset is contained.""" + + flow_folder_name: Union[str, None, UnsetType] = UNSET + """Simple name of the folder in which this asset is contained.""" + + flow_folder_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the folder in which this asset is contained.""" + + flow_reusable_unit_name: Union[str, None, UnsetType] = UNSET + """Simple name of the reusable grouping of operations in which this ephemeral data is contained.""" + + flow_reusable_unit_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the reusable grouping of operations in which this ephemeral data is contained.""" + + flow_id: Union[str, None, UnsetType] = UNSET + """Unique ID for this flow asset, which will remain constant throughout the lifecycle of the asset.""" + + flow_run_id: Union[str, None, UnsetType] = UNSET + """Unique ID of the flow run, which could change on subsequent runs of the same flow.""" + + flow_error_message: Union[str, None, UnsetType] = UNSET + """Optional error message of the flow run.""" + + flow_input_parameters: Union[Dict[str, str], None, UnsetType] = UNSET + """Input parameters for the flow run.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + flow_sub_folders: Union[List[RelatedFlowFolder], None, UnsetType] = UNSET + """Child (sub) folders contained within the folder.""" + + flow_parent_folder: Union[RelatedFlowFolder, None, UnsetType] = UNSET + """Parent folder containing the sub-folders.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "FlowFolder" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _flow_folder_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> FlowFolder: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + FlowFolder instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _flow_folder_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class FlowFolderAttributes(AssetAttributes): + """FlowFolder-specific attributes for nested API format.""" + + flow_started_at: Union[int, None, UnsetType] = UNSET + """Date and time at which this point in the data processing or orchestration started.""" + + flow_finished_at: Union[int, None, UnsetType] = UNSET + """Date and time at which this point in the data processing or orchestration finished.""" + + flow_status: Union[str, None, UnsetType] = UNSET + """Overall status of this point in the data processing or orchestration.""" + + flow_schedule: Union[str, None, UnsetType] = UNSET + """Schedule for this point in the data processing or orchestration.""" + + flow_project_name: Union[str, None, UnsetType] = UNSET + """Simple name of the project in which this asset is contained.""" + + flow_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this asset is contained.""" + + flow_folder_name: Union[str, None, UnsetType] = UNSET + """Simple name of the folder in which this asset is contained.""" + + flow_folder_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the folder in which this asset is contained.""" + + flow_reusable_unit_name: Union[str, None, UnsetType] = UNSET + """Simple name of the reusable grouping of operations in which this ephemeral data is contained.""" + + flow_reusable_unit_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the reusable grouping of operations in which this ephemeral data is contained.""" + + flow_id: Union[str, None, UnsetType] = UNSET + """Unique ID for this flow asset, which will remain constant throughout the lifecycle of the asset.""" + + flow_run_id: Union[str, None, UnsetType] = UNSET + """Unique ID of the flow run, which could change on subsequent runs of the same flow.""" + + flow_error_message: Union[str, None, UnsetType] = UNSET + """Optional error message of the flow run.""" + + flow_input_parameters: Union[Dict[str, str], None, UnsetType] = UNSET + """Input parameters for the flow run.""" + + +class FlowFolderRelationshipAttributes(AssetRelationshipAttributes): + """FlowFolder-specific relationship attributes for nested API format.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + flow_sub_folders: Union[List[RelatedFlowFolder], None, UnsetType] = UNSET + """Child (sub) folders contained within the folder.""" + + flow_parent_folder: Union[RelatedFlowFolder, None, UnsetType] = UNSET + """Parent folder containing the sub-folders.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + +class FlowFolderNested(AssetNested): + """FlowFolder in nested API format for high-performance serialization.""" + + attributes: Union[FlowFolderAttributes, UnsetType] = UNSET + relationship_attributes: Union[FlowFolderRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + FlowFolderRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + FlowFolderRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_FLOW_FOLDER_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "flow_sub_folders", + "flow_parent_folder", + "meanings", + "mc_monitors", + "mc_incidents", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", +] + + +def _populate_flow_folder_attrs(attrs: FlowFolderAttributes, obj: FlowFolder) -> None: + """Populate FlowFolder-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.flow_started_at = obj.flow_started_at + attrs.flow_finished_at = obj.flow_finished_at + attrs.flow_status = obj.flow_status + attrs.flow_schedule = obj.flow_schedule + attrs.flow_project_name = obj.flow_project_name + attrs.flow_project_qualified_name = obj.flow_project_qualified_name + attrs.flow_folder_name = obj.flow_folder_name + attrs.flow_folder_qualified_name = obj.flow_folder_qualified_name + attrs.flow_reusable_unit_name = obj.flow_reusable_unit_name + attrs.flow_reusable_unit_qualified_name = obj.flow_reusable_unit_qualified_name + attrs.flow_id = obj.flow_id + attrs.flow_run_id = obj.flow_run_id + attrs.flow_error_message = obj.flow_error_message + attrs.flow_input_parameters = obj.flow_input_parameters + + +def _extract_flow_folder_attrs(attrs: FlowFolderAttributes) -> dict: + """Extract all FlowFolder attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["flow_started_at"] = attrs.flow_started_at + result["flow_finished_at"] = attrs.flow_finished_at + result["flow_status"] = attrs.flow_status + result["flow_schedule"] = attrs.flow_schedule + result["flow_project_name"] = attrs.flow_project_name + result["flow_project_qualified_name"] = attrs.flow_project_qualified_name + result["flow_folder_name"] = attrs.flow_folder_name + result["flow_folder_qualified_name"] = attrs.flow_folder_qualified_name + result["flow_reusable_unit_name"] = attrs.flow_reusable_unit_name + result["flow_reusable_unit_qualified_name"] = ( + attrs.flow_reusable_unit_qualified_name + ) + result["flow_id"] = attrs.flow_id + result["flow_run_id"] = attrs.flow_run_id + result["flow_error_message"] = attrs.flow_error_message + result["flow_input_parameters"] = attrs.flow_input_parameters + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _flow_folder_to_nested(flow_folder: FlowFolder) -> FlowFolderNested: + """Convert flat FlowFolder to nested format.""" + attrs = FlowFolderAttributes() + _populate_flow_folder_attrs(attrs, flow_folder) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + flow_folder, _FLOW_FOLDER_REL_FIELDS, FlowFolderRelationshipAttributes + ) + return FlowFolderNested( + guid=flow_folder.guid, + type_name=flow_folder.type_name, + status=flow_folder.status, + version=flow_folder.version, + create_time=flow_folder.create_time, + update_time=flow_folder.update_time, + created_by=flow_folder.created_by, + updated_by=flow_folder.updated_by, + classifications=flow_folder.classifications, + classification_names=flow_folder.classification_names, + meanings=flow_folder.meanings, + labels=flow_folder.labels, + business_attributes=flow_folder.business_attributes, + custom_attributes=flow_folder.custom_attributes, + pending_tasks=flow_folder.pending_tasks, + proxy=flow_folder.proxy, + is_incomplete=flow_folder.is_incomplete, + provenance_type=flow_folder.provenance_type, + home_id=flow_folder.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _flow_folder_from_nested(nested: FlowFolderNested) -> FlowFolder: + """Convert nested format to flat FlowFolder.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else FlowFolderAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _FLOW_FOLDER_REL_FIELDS, + FlowFolderRelationshipAttributes, + ) + return FlowFolder( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_flow_folder_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _flow_folder_to_nested_bytes(flow_folder: FlowFolder, serde: Serde) -> bytes: + """Convert flat FlowFolder to nested JSON bytes.""" + return serde.encode(_flow_folder_to_nested(flow_folder)) + + +def _flow_folder_from_nested_bytes(data: bytes, serde: Serde) -> FlowFolder: + """Convert nested JSON bytes to flat FlowFolder.""" + nested = serde.decode(data, FlowFolderNested) + return _flow_folder_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +FlowFolder.FLOW_STARTED_AT = NumericField("flowStartedAt", "flowStartedAt") +FlowFolder.FLOW_FINISHED_AT = NumericField("flowFinishedAt", "flowFinishedAt") +FlowFolder.FLOW_STATUS = KeywordField("flowStatus", "flowStatus") +FlowFolder.FLOW_SCHEDULE = KeywordField("flowSchedule", "flowSchedule") +FlowFolder.FLOW_PROJECT_NAME = KeywordTextField( + "flowProjectName", "flowProjectName", "flowProjectName.text" +) +FlowFolder.FLOW_PROJECT_QUALIFIED_NAME = KeywordField( + "flowProjectQualifiedName", "flowProjectQualifiedName" +) +FlowFolder.FLOW_FOLDER_NAME = KeywordTextField( + "flowFolderName", "flowFolderName", "flowFolderName.text" +) +FlowFolder.FLOW_FOLDER_QUALIFIED_NAME = KeywordField( + "flowFolderQualifiedName", "flowFolderQualifiedName" +) +FlowFolder.FLOW_REUSABLE_UNIT_NAME = KeywordTextField( + "flowReusableUnitName", "flowReusableUnitName", "flowReusableUnitName.text" +) +FlowFolder.FLOW_REUSABLE_UNIT_QUALIFIED_NAME = KeywordField( + "flowReusableUnitQualifiedName", "flowReusableUnitQualifiedName" +) +FlowFolder.FLOW_ID = KeywordField("flowId", "flowId") +FlowFolder.FLOW_RUN_ID = KeywordField("flowRunId", "flowRunId") +FlowFolder.FLOW_ERROR_MESSAGE = KeywordField("flowErrorMessage", "flowErrorMessage") +FlowFolder.FLOW_INPUT_PARAMETERS = KeywordField( + "flowInputParameters", "flowInputParameters" +) +FlowFolder.ANOMALO_CHECKS = RelationField("anomaloChecks") +FlowFolder.APPLICATION = RelationField("application") +FlowFolder.APPLICATION_FIELD = RelationField("applicationField") +FlowFolder.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +FlowFolder.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +FlowFolder.METRICS = RelationField("metrics") +FlowFolder.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +FlowFolder.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +FlowFolder.FLOW_SUB_FOLDERS = RelationField("flowSubFolders") +FlowFolder.FLOW_PARENT_FOLDER = RelationField("flowParentFolder") +FlowFolder.MEANINGS = RelationField("meanings") +FlowFolder.MC_MONITORS = RelationField("mcMonitors") +FlowFolder.MC_INCIDENTS = RelationField("mcIncidents") +FlowFolder.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +FlowFolder.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +FlowFolder.FILES = RelationField("files") +FlowFolder.LINKS = RelationField("links") +FlowFolder.README = RelationField("readme") +FlowFolder.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +FlowFolder.SODA_CHECKS = RelationField("sodaChecks") diff --git a/pyatlan_v9/model/assets/flow_project.py b/pyatlan_v9/model/assets/flow_project.py new file mode 100644 index 000000000..a964b5c56 --- /dev/null +++ b/pyatlan_v9/model/assets/flow_project.py @@ -0,0 +1,588 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +FlowProject asset model with flattened inheritance. + +This module provides: +- FlowProject: Flat asset class (easy to use) +- FlowProjectAttributes: Nested attributes struct (extends AssetAttributes) +- FlowProjectNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class FlowProject(Asset): + """ + A project, workspace or namespace that is used to organize data processing. + """ + + FLOW_STARTED_AT: ClassVar[Any] = None + FLOW_FINISHED_AT: ClassVar[Any] = None + FLOW_STATUS: ClassVar[Any] = None + FLOW_SCHEDULE: ClassVar[Any] = None + FLOW_PROJECT_NAME: ClassVar[Any] = None + FLOW_PROJECT_QUALIFIED_NAME: ClassVar[Any] = None + FLOW_FOLDER_NAME: ClassVar[Any] = None + FLOW_FOLDER_QUALIFIED_NAME: ClassVar[Any] = None + FLOW_REUSABLE_UNIT_NAME: ClassVar[Any] = None + FLOW_REUSABLE_UNIT_QUALIFIED_NAME: ClassVar[Any] = None + FLOW_ID: ClassVar[Any] = None + FLOW_RUN_ID: ClassVar[Any] = None + FLOW_ERROR_MESSAGE: ClassVar[Any] = None + FLOW_INPUT_PARAMETERS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "FlowProject" + + flow_started_at: Union[int, None, UnsetType] = UNSET + """Date and time at which this point in the data processing or orchestration started.""" + + flow_finished_at: Union[int, None, UnsetType] = UNSET + """Date and time at which this point in the data processing or orchestration finished.""" + + flow_status: Union[str, None, UnsetType] = UNSET + """Overall status of this point in the data processing or orchestration.""" + + flow_schedule: Union[str, None, UnsetType] = UNSET + """Schedule for this point in the data processing or orchestration.""" + + flow_project_name: Union[str, None, UnsetType] = UNSET + """Simple name of the project in which this asset is contained.""" + + flow_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this asset is contained.""" + + flow_folder_name: Union[str, None, UnsetType] = UNSET + """Simple name of the folder in which this asset is contained.""" + + flow_folder_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the folder in which this asset is contained.""" + + flow_reusable_unit_name: Union[str, None, UnsetType] = UNSET + """Simple name of the reusable grouping of operations in which this ephemeral data is contained.""" + + flow_reusable_unit_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the reusable grouping of operations in which this ephemeral data is contained.""" + + flow_id: Union[str, None, UnsetType] = UNSET + """Unique ID for this flow asset, which will remain constant throughout the lifecycle of the asset.""" + + flow_run_id: Union[str, None, UnsetType] = UNSET + """Unique ID of the flow run, which could change on subsequent runs of the same flow.""" + + flow_error_message: Union[str, None, UnsetType] = UNSET + """Optional error message of the flow run.""" + + flow_input_parameters: Union[Dict[str, str], None, UnsetType] = UNSET + """Input parameters for the flow run.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "FlowProject" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _flow_project_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> FlowProject: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + FlowProject instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _flow_project_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class FlowProjectAttributes(AssetAttributes): + """FlowProject-specific attributes for nested API format.""" + + flow_started_at: Union[int, None, UnsetType] = UNSET + """Date and time at which this point in the data processing or orchestration started.""" + + flow_finished_at: Union[int, None, UnsetType] = UNSET + """Date and time at which this point in the data processing or orchestration finished.""" + + flow_status: Union[str, None, UnsetType] = UNSET + """Overall status of this point in the data processing or orchestration.""" + + flow_schedule: Union[str, None, UnsetType] = UNSET + """Schedule for this point in the data processing or orchestration.""" + + flow_project_name: Union[str, None, UnsetType] = UNSET + """Simple name of the project in which this asset is contained.""" + + flow_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this asset is contained.""" + + flow_folder_name: Union[str, None, UnsetType] = UNSET + """Simple name of the folder in which this asset is contained.""" + + flow_folder_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the folder in which this asset is contained.""" + + flow_reusable_unit_name: Union[str, None, UnsetType] = UNSET + """Simple name of the reusable grouping of operations in which this ephemeral data is contained.""" + + flow_reusable_unit_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the reusable grouping of operations in which this ephemeral data is contained.""" + + flow_id: Union[str, None, UnsetType] = UNSET + """Unique ID for this flow asset, which will remain constant throughout the lifecycle of the asset.""" + + flow_run_id: Union[str, None, UnsetType] = UNSET + """Unique ID of the flow run, which could change on subsequent runs of the same flow.""" + + flow_error_message: Union[str, None, UnsetType] = UNSET + """Optional error message of the flow run.""" + + flow_input_parameters: Union[Dict[str, str], None, UnsetType] = UNSET + """Input parameters for the flow run.""" + + +class FlowProjectRelationshipAttributes(AssetRelationshipAttributes): + """FlowProject-specific relationship attributes for nested API format.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + +class FlowProjectNested(AssetNested): + """FlowProject in nested API format for high-performance serialization.""" + + attributes: Union[FlowProjectAttributes, UnsetType] = UNSET + relationship_attributes: Union[FlowProjectRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + FlowProjectRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + FlowProjectRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_FLOW_PROJECT_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", +] + + +def _populate_flow_project_attrs( + attrs: FlowProjectAttributes, obj: FlowProject +) -> None: + """Populate FlowProject-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.flow_started_at = obj.flow_started_at + attrs.flow_finished_at = obj.flow_finished_at + attrs.flow_status = obj.flow_status + attrs.flow_schedule = obj.flow_schedule + attrs.flow_project_name = obj.flow_project_name + attrs.flow_project_qualified_name = obj.flow_project_qualified_name + attrs.flow_folder_name = obj.flow_folder_name + attrs.flow_folder_qualified_name = obj.flow_folder_qualified_name + attrs.flow_reusable_unit_name = obj.flow_reusable_unit_name + attrs.flow_reusable_unit_qualified_name = obj.flow_reusable_unit_qualified_name + attrs.flow_id = obj.flow_id + attrs.flow_run_id = obj.flow_run_id + attrs.flow_error_message = obj.flow_error_message + attrs.flow_input_parameters = obj.flow_input_parameters + + +def _extract_flow_project_attrs(attrs: FlowProjectAttributes) -> dict: + """Extract all FlowProject attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["flow_started_at"] = attrs.flow_started_at + result["flow_finished_at"] = attrs.flow_finished_at + result["flow_status"] = attrs.flow_status + result["flow_schedule"] = attrs.flow_schedule + result["flow_project_name"] = attrs.flow_project_name + result["flow_project_qualified_name"] = attrs.flow_project_qualified_name + result["flow_folder_name"] = attrs.flow_folder_name + result["flow_folder_qualified_name"] = attrs.flow_folder_qualified_name + result["flow_reusable_unit_name"] = attrs.flow_reusable_unit_name + result["flow_reusable_unit_qualified_name"] = ( + attrs.flow_reusable_unit_qualified_name + ) + result["flow_id"] = attrs.flow_id + result["flow_run_id"] = attrs.flow_run_id + result["flow_error_message"] = attrs.flow_error_message + result["flow_input_parameters"] = attrs.flow_input_parameters + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _flow_project_to_nested(flow_project: FlowProject) -> FlowProjectNested: + """Convert flat FlowProject to nested format.""" + attrs = FlowProjectAttributes() + _populate_flow_project_attrs(attrs, flow_project) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + flow_project, _FLOW_PROJECT_REL_FIELDS, FlowProjectRelationshipAttributes + ) + return FlowProjectNested( + guid=flow_project.guid, + type_name=flow_project.type_name, + status=flow_project.status, + version=flow_project.version, + create_time=flow_project.create_time, + update_time=flow_project.update_time, + created_by=flow_project.created_by, + updated_by=flow_project.updated_by, + classifications=flow_project.classifications, + classification_names=flow_project.classification_names, + meanings=flow_project.meanings, + labels=flow_project.labels, + business_attributes=flow_project.business_attributes, + custom_attributes=flow_project.custom_attributes, + pending_tasks=flow_project.pending_tasks, + proxy=flow_project.proxy, + is_incomplete=flow_project.is_incomplete, + provenance_type=flow_project.provenance_type, + home_id=flow_project.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _flow_project_from_nested(nested: FlowProjectNested) -> FlowProject: + """Convert nested format to flat FlowProject.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else FlowProjectAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _FLOW_PROJECT_REL_FIELDS, + FlowProjectRelationshipAttributes, + ) + return FlowProject( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_flow_project_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _flow_project_to_nested_bytes(flow_project: FlowProject, serde: Serde) -> bytes: + """Convert flat FlowProject to nested JSON bytes.""" + return serde.encode(_flow_project_to_nested(flow_project)) + + +def _flow_project_from_nested_bytes(data: bytes, serde: Serde) -> FlowProject: + """Convert nested JSON bytes to flat FlowProject.""" + nested = serde.decode(data, FlowProjectNested) + return _flow_project_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +FlowProject.FLOW_STARTED_AT = NumericField("flowStartedAt", "flowStartedAt") +FlowProject.FLOW_FINISHED_AT = NumericField("flowFinishedAt", "flowFinishedAt") +FlowProject.FLOW_STATUS = KeywordField("flowStatus", "flowStatus") +FlowProject.FLOW_SCHEDULE = KeywordField("flowSchedule", "flowSchedule") +FlowProject.FLOW_PROJECT_NAME = KeywordTextField( + "flowProjectName", "flowProjectName", "flowProjectName.text" +) +FlowProject.FLOW_PROJECT_QUALIFIED_NAME = KeywordField( + "flowProjectQualifiedName", "flowProjectQualifiedName" +) +FlowProject.FLOW_FOLDER_NAME = KeywordTextField( + "flowFolderName", "flowFolderName", "flowFolderName.text" +) +FlowProject.FLOW_FOLDER_QUALIFIED_NAME = KeywordField( + "flowFolderQualifiedName", "flowFolderQualifiedName" +) +FlowProject.FLOW_REUSABLE_UNIT_NAME = KeywordTextField( + "flowReusableUnitName", "flowReusableUnitName", "flowReusableUnitName.text" +) +FlowProject.FLOW_REUSABLE_UNIT_QUALIFIED_NAME = KeywordField( + "flowReusableUnitQualifiedName", "flowReusableUnitQualifiedName" +) +FlowProject.FLOW_ID = KeywordField("flowId", "flowId") +FlowProject.FLOW_RUN_ID = KeywordField("flowRunId", "flowRunId") +FlowProject.FLOW_ERROR_MESSAGE = KeywordField("flowErrorMessage", "flowErrorMessage") +FlowProject.FLOW_INPUT_PARAMETERS = KeywordField( + "flowInputParameters", "flowInputParameters" +) +FlowProject.ANOMALO_CHECKS = RelationField("anomaloChecks") +FlowProject.APPLICATION = RelationField("application") +FlowProject.APPLICATION_FIELD = RelationField("applicationField") +FlowProject.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +FlowProject.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +FlowProject.METRICS = RelationField("metrics") +FlowProject.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +FlowProject.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +FlowProject.MEANINGS = RelationField("meanings") +FlowProject.MC_MONITORS = RelationField("mcMonitors") +FlowProject.MC_INCIDENTS = RelationField("mcIncidents") +FlowProject.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +FlowProject.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +FlowProject.FILES = RelationField("files") +FlowProject.LINKS = RelationField("links") +FlowProject.README = RelationField("readme") +FlowProject.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +FlowProject.SODA_CHECKS = RelationField("sodaChecks") diff --git a/pyatlan_v9/model/assets/flow_related.py b/pyatlan_v9/model/assets/flow_related.py new file mode 100644 index 000000000..bd54e6b0e --- /dev/null +++ b/pyatlan_v9/model/assets/flow_related.py @@ -0,0 +1,238 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Flow module. + +This module contains all Related{Type} classes for the Flow type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Dict, Union + +from msgspec import UNSET, UnsetType + +from .asset_related import RelatedAsset +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedFlow", + "RelatedFlowProject", + "RelatedFlowFolder", + "RelatedFlowControlOperation", + "RelatedFlowReusableUnit", + "RelatedFlowDatasetOperation", + "RelatedFlowFieldOperation", + "RelatedFlowDataset", + "RelatedFlowField", +] + + +class RelatedFlow(RelatedAsset): + """ + Related entity reference for Flow assets. + + Extends RelatedAsset with Flow-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Flow" so it serializes correctly + + flow_started_at: Union[int, None, UnsetType] = UNSET + """Date and time at which this point in the data processing or orchestration started.""" + + flow_finished_at: Union[int, None, UnsetType] = UNSET + """Date and time at which this point in the data processing or orchestration finished.""" + + flow_status: Union[str, None, UnsetType] = UNSET + """Overall status of this point in the data processing or orchestration.""" + + flow_schedule: Union[str, None, UnsetType] = UNSET + """Schedule for this point in the data processing or orchestration.""" + + flow_project_name: Union[str, None, UnsetType] = UNSET + """Simple name of the project in which this asset is contained.""" + + flow_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this asset is contained.""" + + flow_folder_name: Union[str, None, UnsetType] = UNSET + """Simple name of the folder in which this asset is contained.""" + + flow_folder_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the folder in which this asset is contained.""" + + flow_reusable_unit_name: Union[str, None, UnsetType] = UNSET + """Simple name of the reusable grouping of operations in which this ephemeral data is contained.""" + + flow_reusable_unit_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the reusable grouping of operations in which this ephemeral data is contained.""" + + flow_id: Union[str, None, UnsetType] = UNSET + """Unique ID for this flow asset, which will remain constant throughout the lifecycle of the asset.""" + + flow_run_id: Union[str, None, UnsetType] = UNSET + """Unique ID of the flow run, which could change on subsequent runs of the same flow.""" + + flow_error_message: Union[str, None, UnsetType] = UNSET + """Optional error message of the flow run.""" + + flow_input_parameters: Union[Dict[str, str], None, UnsetType] = UNSET + """Input parameters for the flow run.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Flow" + + +class RelatedFlowProject(RelatedFlow): + """ + Related entity reference for FlowProject assets. + + Extends RelatedFlow with FlowProject-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "FlowProject" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "FlowProject" + + +class RelatedFlowFolder(RelatedFlow): + """ + Related entity reference for FlowFolder assets. + + Extends RelatedFlow with FlowFolder-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "FlowFolder" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "FlowFolder" + + +class RelatedFlowControlOperation(RelatedFlow): + """ + Related entity reference for FlowControlOperation assets. + + Extends RelatedFlow with FlowControlOperation-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "FlowControlOperation" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "FlowControlOperation" + + +class RelatedFlowReusableUnit(RelatedFlow): + """ + Related entity reference for FlowReusableUnit assets. + + Extends RelatedFlow with FlowReusableUnit-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "FlowReusableUnit" so it serializes correctly + + flow_dataset_count: Union[int, None, UnsetType] = UNSET + """Count of the number of ephemeral datasets contained within this reusable unit.""" + + flow_control_operation_count: Union[int, None, UnsetType] = UNSET + """Count of the number of control flow operations that execute this reusable unit.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "FlowReusableUnit" + + +class RelatedFlowDatasetOperation(RelatedFlow): + """ + Related entity reference for FlowDatasetOperation assets. + + Extends RelatedFlow with FlowDatasetOperation-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "FlowDatasetOperation" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "FlowDatasetOperation" + + +class RelatedFlowFieldOperation(RelatedFlow): + """ + Related entity reference for FlowFieldOperation assets. + + Extends RelatedFlow with FlowFieldOperation-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "FlowFieldOperation" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "FlowFieldOperation" + + +class RelatedFlowDataset(RelatedFlow): + """ + Related entity reference for FlowDataset assets. + + Extends RelatedFlow with FlowDataset-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "FlowDataset" so it serializes correctly + + flow_field_count: Union[int, None, UnsetType] = UNSET + """Count of the number of individual fields that make up this ephemeral dataset.""" + + flow_type: Union[str, None, UnsetType] = UNSET + """Type of the ephemeral piece of data.""" + + flow_expression: Union[str, None, UnsetType] = UNSET + """Logic that is applied, injected or otherwise used as part of producing this ephemeral piece of data.""" + + flow_query: Union[str, None, UnsetType] = UNSET + """Query (e.g. SQL) that was run to produce this ephemeral piece of data.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "FlowDataset" + + +class RelatedFlowField(RelatedFlow): + """ + Related entity reference for FlowField assets. + + Extends RelatedFlow with FlowField-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "FlowField" so it serializes correctly + + flow_dataset_name: Union[str, None, UnsetType] = UNSET + """Simple name of the ephemeral dataset in which this field is contained.""" + + flow_dataset_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the ephemeral dataset in which this field is contained.""" + + flow_data_type: Union[str, None, UnsetType] = UNSET + """Type of the data captured in this field.""" + + flow_expression: Union[str, None, UnsetType] = UNSET + """Logic that is applied, injected or otherwise used as part of producing this ephemeral field of data.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "FlowField" diff --git a/pyatlan_v9/model/assets/flow_reusable_unit.py b/pyatlan_v9/model/assets/flow_reusable_unit.py new file mode 100644 index 000000000..c3eb749c2 --- /dev/null +++ b/pyatlan_v9/model/assets/flow_reusable_unit.py @@ -0,0 +1,657 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +FlowReusableUnit asset model with flattened inheritance. + +This module provides: +- FlowReusableUnit: Flat asset class (easy to use) +- FlowReusableUnitAttributes: Nested attributes struct (extends AssetAttributes) +- FlowReusableUnitNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .flow_related import RelatedFlowDataset, RelatedFlowDatasetOperation + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class FlowReusableUnit(Asset): + """ + A reusable grouping of data flows that will be orchestrated together as a single unit. + """ + + FLOW_DATASET_COUNT: ClassVar[Any] = None + FLOW_CONTROL_OPERATION_COUNT: ClassVar[Any] = None + FLOW_STARTED_AT: ClassVar[Any] = None + FLOW_FINISHED_AT: ClassVar[Any] = None + FLOW_STATUS: ClassVar[Any] = None + FLOW_SCHEDULE: ClassVar[Any] = None + FLOW_PROJECT_NAME: ClassVar[Any] = None + FLOW_PROJECT_QUALIFIED_NAME: ClassVar[Any] = None + FLOW_FOLDER_NAME: ClassVar[Any] = None + FLOW_FOLDER_QUALIFIED_NAME: ClassVar[Any] = None + FLOW_REUSABLE_UNIT_NAME: ClassVar[Any] = None + FLOW_REUSABLE_UNIT_QUALIFIED_NAME: ClassVar[Any] = None + FLOW_ID: ClassVar[Any] = None + FLOW_RUN_ID: ClassVar[Any] = None + FLOW_ERROR_MESSAGE: ClassVar[Any] = None + FLOW_INPUT_PARAMETERS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + FLOW_DATA_FLOWS: ClassVar[Any] = None + FLOW_ABSTRACTS: ClassVar[Any] = None + FLOW_DATASETS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "FlowReusableUnit" + + flow_dataset_count: Union[int, None, UnsetType] = UNSET + """Count of the number of ephemeral datasets contained within this reusable unit.""" + + flow_control_operation_count: Union[int, None, UnsetType] = UNSET + """Count of the number of control flow operations that execute this reusable unit.""" + + flow_started_at: Union[int, None, UnsetType] = UNSET + """Date and time at which this point in the data processing or orchestration started.""" + + flow_finished_at: Union[int, None, UnsetType] = UNSET + """Date and time at which this point in the data processing or orchestration finished.""" + + flow_status: Union[str, None, UnsetType] = UNSET + """Overall status of this point in the data processing or orchestration.""" + + flow_schedule: Union[str, None, UnsetType] = UNSET + """Schedule for this point in the data processing or orchestration.""" + + flow_project_name: Union[str, None, UnsetType] = UNSET + """Simple name of the project in which this asset is contained.""" + + flow_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this asset is contained.""" + + flow_folder_name: Union[str, None, UnsetType] = UNSET + """Simple name of the folder in which this asset is contained.""" + + flow_folder_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the folder in which this asset is contained.""" + + flow_reusable_unit_name: Union[str, None, UnsetType] = UNSET + """Simple name of the reusable grouping of operations in which this ephemeral data is contained.""" + + flow_reusable_unit_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the reusable grouping of operations in which this ephemeral data is contained.""" + + flow_id: Union[str, None, UnsetType] = UNSET + """Unique ID for this flow asset, which will remain constant throughout the lifecycle of the asset.""" + + flow_run_id: Union[str, None, UnsetType] = UNSET + """Unique ID of the flow run, which could change on subsequent runs of the same flow.""" + + flow_error_message: Union[str, None, UnsetType] = UNSET + """Optional error message of the flow run.""" + + flow_input_parameters: Union[Dict[str, str], None, UnsetType] = UNSET + """Input parameters for the flow run.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + flow_data_flows: Union[List[RelatedFlowDatasetOperation], None, UnsetType] = UNSET + """Individual dataset operations contained in this reusable unit.""" + + flow_abstracts: Union[List[RelatedFlowDataset], None, UnsetType] = UNSET + """Ephemeral datasets that abstract the sub-processing carried out by the reusable unit.""" + + flow_datasets: Union[List[RelatedFlowDataset], None, UnsetType] = UNSET + """Ephemeral datasets that are contained within the reusable unit.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "FlowReusableUnit" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _flow_reusable_unit_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> FlowReusableUnit: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + FlowReusableUnit instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _flow_reusable_unit_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class FlowReusableUnitAttributes(AssetAttributes): + """FlowReusableUnit-specific attributes for nested API format.""" + + flow_dataset_count: Union[int, None, UnsetType] = UNSET + """Count of the number of ephemeral datasets contained within this reusable unit.""" + + flow_control_operation_count: Union[int, None, UnsetType] = UNSET + """Count of the number of control flow operations that execute this reusable unit.""" + + flow_started_at: Union[int, None, UnsetType] = UNSET + """Date and time at which this point in the data processing or orchestration started.""" + + flow_finished_at: Union[int, None, UnsetType] = UNSET + """Date and time at which this point in the data processing or orchestration finished.""" + + flow_status: Union[str, None, UnsetType] = UNSET + """Overall status of this point in the data processing or orchestration.""" + + flow_schedule: Union[str, None, UnsetType] = UNSET + """Schedule for this point in the data processing or orchestration.""" + + flow_project_name: Union[str, None, UnsetType] = UNSET + """Simple name of the project in which this asset is contained.""" + + flow_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this asset is contained.""" + + flow_folder_name: Union[str, None, UnsetType] = UNSET + """Simple name of the folder in which this asset is contained.""" + + flow_folder_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the folder in which this asset is contained.""" + + flow_reusable_unit_name: Union[str, None, UnsetType] = UNSET + """Simple name of the reusable grouping of operations in which this ephemeral data is contained.""" + + flow_reusable_unit_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the reusable grouping of operations in which this ephemeral data is contained.""" + + flow_id: Union[str, None, UnsetType] = UNSET + """Unique ID for this flow asset, which will remain constant throughout the lifecycle of the asset.""" + + flow_run_id: Union[str, None, UnsetType] = UNSET + """Unique ID of the flow run, which could change on subsequent runs of the same flow.""" + + flow_error_message: Union[str, None, UnsetType] = UNSET + """Optional error message of the flow run.""" + + flow_input_parameters: Union[Dict[str, str], None, UnsetType] = UNSET + """Input parameters for the flow run.""" + + +class FlowReusableUnitRelationshipAttributes(AssetRelationshipAttributes): + """FlowReusableUnit-specific relationship attributes for nested API format.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + flow_data_flows: Union[List[RelatedFlowDatasetOperation], None, UnsetType] = UNSET + """Individual dataset operations contained in this reusable unit.""" + + flow_abstracts: Union[List[RelatedFlowDataset], None, UnsetType] = UNSET + """Ephemeral datasets that abstract the sub-processing carried out by the reusable unit.""" + + flow_datasets: Union[List[RelatedFlowDataset], None, UnsetType] = UNSET + """Ephemeral datasets that are contained within the reusable unit.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + +class FlowReusableUnitNested(AssetNested): + """FlowReusableUnit in nested API format for high-performance serialization.""" + + attributes: Union[FlowReusableUnitAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + FlowReusableUnitRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + FlowReusableUnitRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + FlowReusableUnitRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_FLOW_REUSABLE_UNIT_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "flow_data_flows", + "flow_abstracts", + "flow_datasets", + "meanings", + "mc_monitors", + "mc_incidents", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", +] + + +def _populate_flow_reusable_unit_attrs( + attrs: FlowReusableUnitAttributes, obj: FlowReusableUnit +) -> None: + """Populate FlowReusableUnit-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.flow_dataset_count = obj.flow_dataset_count + attrs.flow_control_operation_count = obj.flow_control_operation_count + attrs.flow_started_at = obj.flow_started_at + attrs.flow_finished_at = obj.flow_finished_at + attrs.flow_status = obj.flow_status + attrs.flow_schedule = obj.flow_schedule + attrs.flow_project_name = obj.flow_project_name + attrs.flow_project_qualified_name = obj.flow_project_qualified_name + attrs.flow_folder_name = obj.flow_folder_name + attrs.flow_folder_qualified_name = obj.flow_folder_qualified_name + attrs.flow_reusable_unit_name = obj.flow_reusable_unit_name + attrs.flow_reusable_unit_qualified_name = obj.flow_reusable_unit_qualified_name + attrs.flow_id = obj.flow_id + attrs.flow_run_id = obj.flow_run_id + attrs.flow_error_message = obj.flow_error_message + attrs.flow_input_parameters = obj.flow_input_parameters + + +def _extract_flow_reusable_unit_attrs(attrs: FlowReusableUnitAttributes) -> dict: + """Extract all FlowReusableUnit attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["flow_dataset_count"] = attrs.flow_dataset_count + result["flow_control_operation_count"] = attrs.flow_control_operation_count + result["flow_started_at"] = attrs.flow_started_at + result["flow_finished_at"] = attrs.flow_finished_at + result["flow_status"] = attrs.flow_status + result["flow_schedule"] = attrs.flow_schedule + result["flow_project_name"] = attrs.flow_project_name + result["flow_project_qualified_name"] = attrs.flow_project_qualified_name + result["flow_folder_name"] = attrs.flow_folder_name + result["flow_folder_qualified_name"] = attrs.flow_folder_qualified_name + result["flow_reusable_unit_name"] = attrs.flow_reusable_unit_name + result["flow_reusable_unit_qualified_name"] = ( + attrs.flow_reusable_unit_qualified_name + ) + result["flow_id"] = attrs.flow_id + result["flow_run_id"] = attrs.flow_run_id + result["flow_error_message"] = attrs.flow_error_message + result["flow_input_parameters"] = attrs.flow_input_parameters + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _flow_reusable_unit_to_nested( + flow_reusable_unit: FlowReusableUnit, +) -> FlowReusableUnitNested: + """Convert flat FlowReusableUnit to nested format.""" + attrs = FlowReusableUnitAttributes() + _populate_flow_reusable_unit_attrs(attrs, flow_reusable_unit) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + flow_reusable_unit, + _FLOW_REUSABLE_UNIT_REL_FIELDS, + FlowReusableUnitRelationshipAttributes, + ) + return FlowReusableUnitNested( + guid=flow_reusable_unit.guid, + type_name=flow_reusable_unit.type_name, + status=flow_reusable_unit.status, + version=flow_reusable_unit.version, + create_time=flow_reusable_unit.create_time, + update_time=flow_reusable_unit.update_time, + created_by=flow_reusable_unit.created_by, + updated_by=flow_reusable_unit.updated_by, + classifications=flow_reusable_unit.classifications, + classification_names=flow_reusable_unit.classification_names, + meanings=flow_reusable_unit.meanings, + labels=flow_reusable_unit.labels, + business_attributes=flow_reusable_unit.business_attributes, + custom_attributes=flow_reusable_unit.custom_attributes, + pending_tasks=flow_reusable_unit.pending_tasks, + proxy=flow_reusable_unit.proxy, + is_incomplete=flow_reusable_unit.is_incomplete, + provenance_type=flow_reusable_unit.provenance_type, + home_id=flow_reusable_unit.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _flow_reusable_unit_from_nested(nested: FlowReusableUnitNested) -> FlowReusableUnit: + """Convert nested format to flat FlowReusableUnit.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else FlowReusableUnitAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _FLOW_REUSABLE_UNIT_REL_FIELDS, + FlowReusableUnitRelationshipAttributes, + ) + return FlowReusableUnit( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_flow_reusable_unit_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _flow_reusable_unit_to_nested_bytes( + flow_reusable_unit: FlowReusableUnit, serde: Serde +) -> bytes: + """Convert flat FlowReusableUnit to nested JSON bytes.""" + return serde.encode(_flow_reusable_unit_to_nested(flow_reusable_unit)) + + +def _flow_reusable_unit_from_nested_bytes( + data: bytes, serde: Serde +) -> FlowReusableUnit: + """Convert nested JSON bytes to flat FlowReusableUnit.""" + nested = serde.decode(data, FlowReusableUnitNested) + return _flow_reusable_unit_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +FlowReusableUnit.FLOW_DATASET_COUNT = NumericField( + "flowDatasetCount", "flowDatasetCount" +) +FlowReusableUnit.FLOW_CONTROL_OPERATION_COUNT = NumericField( + "flowControlOperationCount", "flowControlOperationCount" +) +FlowReusableUnit.FLOW_STARTED_AT = NumericField("flowStartedAt", "flowStartedAt") +FlowReusableUnit.FLOW_FINISHED_AT = NumericField("flowFinishedAt", "flowFinishedAt") +FlowReusableUnit.FLOW_STATUS = KeywordField("flowStatus", "flowStatus") +FlowReusableUnit.FLOW_SCHEDULE = KeywordField("flowSchedule", "flowSchedule") +FlowReusableUnit.FLOW_PROJECT_NAME = KeywordTextField( + "flowProjectName", "flowProjectName", "flowProjectName.text" +) +FlowReusableUnit.FLOW_PROJECT_QUALIFIED_NAME = KeywordField( + "flowProjectQualifiedName", "flowProjectQualifiedName" +) +FlowReusableUnit.FLOW_FOLDER_NAME = KeywordTextField( + "flowFolderName", "flowFolderName", "flowFolderName.text" +) +FlowReusableUnit.FLOW_FOLDER_QUALIFIED_NAME = KeywordField( + "flowFolderQualifiedName", "flowFolderQualifiedName" +) +FlowReusableUnit.FLOW_REUSABLE_UNIT_NAME = KeywordTextField( + "flowReusableUnitName", "flowReusableUnitName", "flowReusableUnitName.text" +) +FlowReusableUnit.FLOW_REUSABLE_UNIT_QUALIFIED_NAME = KeywordField( + "flowReusableUnitQualifiedName", "flowReusableUnitQualifiedName" +) +FlowReusableUnit.FLOW_ID = KeywordField("flowId", "flowId") +FlowReusableUnit.FLOW_RUN_ID = KeywordField("flowRunId", "flowRunId") +FlowReusableUnit.FLOW_ERROR_MESSAGE = KeywordField( + "flowErrorMessage", "flowErrorMessage" +) +FlowReusableUnit.FLOW_INPUT_PARAMETERS = KeywordField( + "flowInputParameters", "flowInputParameters" +) +FlowReusableUnit.ANOMALO_CHECKS = RelationField("anomaloChecks") +FlowReusableUnit.APPLICATION = RelationField("application") +FlowReusableUnit.APPLICATION_FIELD = RelationField("applicationField") +FlowReusableUnit.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +FlowReusableUnit.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +FlowReusableUnit.METRICS = RelationField("metrics") +FlowReusableUnit.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +FlowReusableUnit.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +FlowReusableUnit.FLOW_DATA_FLOWS = RelationField("flowDataFlows") +FlowReusableUnit.FLOW_ABSTRACTS = RelationField("flowAbstracts") +FlowReusableUnit.FLOW_DATASETS = RelationField("flowDatasets") +FlowReusableUnit.MEANINGS = RelationField("meanings") +FlowReusableUnit.MC_MONITORS = RelationField("mcMonitors") +FlowReusableUnit.MC_INCIDENTS = RelationField("mcIncidents") +FlowReusableUnit.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +FlowReusableUnit.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +FlowReusableUnit.FILES = RelationField("files") +FlowReusableUnit.LINKS = RelationField("links") +FlowReusableUnit.README = RelationField("readme") +FlowReusableUnit.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +FlowReusableUnit.SODA_CHECKS = RelationField("sodaChecks") diff --git a/pyatlan_v9/model/assets/folder.py b/pyatlan_v9/model/assets/folder.py new file mode 100644 index 000000000..01d03d098 --- /dev/null +++ b/pyatlan_v9/model/assets/folder.py @@ -0,0 +1,529 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Folder asset model with flattened inheritance. + +This module provides: +- Folder: Flat asset class (easy to use) +- FolderAttributes: Nested attributes struct (extends AssetAttributes) +- FolderNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .sql_related import RelatedQuery +from pyatlan.utils import validate_required_fields +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .namespace_related import RelatedFolder, RelatedNamespace + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Folder(Asset): + """ + Instance of a folder within a query collection in Atlan. + """ + + PARENT_QUALIFIED_NAME: ClassVar[Any] = None + COLLECTION_QUALIFIED_NAME: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + CHILDREN_FOLDERS: ClassVar[Any] = None + PARENT: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + CHILDREN_QUERIES: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Folder" + + parent_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the parent folder or collection in which this folder exists.""" + + collection_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the collection in which this folder exists.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + children_folders: Union[List[RelatedFolder], None, UnsetType] = UNSET + """Folders that exist within this namespace.""" + + parent: Union[RelatedNamespace, None, UnsetType] = UNSET + """Namespace in which this folder exists.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + children_queries: Union[List[RelatedQuery], None, UnsetType] = UNSET + """Queries that exist within this namespace.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Folder" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + @classmethod + def creator( + cls, + *, + name: str, + collection_qualified_name: str | None = None, + parent_folder_qualified_name: str | None = None, + ) -> "Folder": + validate_required_fields(["name"], [name]) + if not (parent_folder_qualified_name or collection_qualified_name): + raise ValueError( + "Either 'collection_qualified_name' or 'parent_folder_qualified_name' must be specified." + ) + + if not parent_folder_qualified_name: + qualified_name = f"{collection_qualified_name}/{name}" + parent_qn = collection_qualified_name + from pyatlan_v9.model.assets import Collection + + parent_ref = Collection.ref_by_qualified_name( + collection_qualified_name or "" + ) + else: + tokens = parent_folder_qualified_name.split("/") + if len(tokens) < 4: + raise ValueError("Invalid parent_folder_qualified_name") + collection_qualified_name = ( + f"{tokens[0]}/{tokens[1]}/{tokens[2]}/{tokens[3]}" + ) + qualified_name = f"{parent_folder_qualified_name}/{name}" + parent_qn = parent_folder_qualified_name + parent_ref = Folder.ref_by_qualified_name(parent_folder_qualified_name) + + return Folder( + name=name, + qualified_name=qualified_name, + collection_qualified_name=collection_qualified_name, + parent=parent_ref, + parent_qualified_name=parent_qn, + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _folder_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Folder: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Folder instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _folder_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class FolderAttributes(AssetAttributes): + """Folder-specific attributes for nested API format.""" + + parent_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the parent folder or collection in which this folder exists.""" + + collection_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the collection in which this folder exists.""" + + +class FolderRelationshipAttributes(AssetRelationshipAttributes): + """Folder-specific relationship attributes for nested API format.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + children_folders: Union[List[RelatedFolder], None, UnsetType] = UNSET + """Folders that exist within this namespace.""" + + parent: Union[RelatedNamespace, None, UnsetType] = UNSET + """Namespace in which this folder exists.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + children_queries: Union[List[RelatedQuery], None, UnsetType] = UNSET + """Queries that exist within this namespace.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + +class FolderNested(AssetNested): + """Folder in nested API format for high-performance serialization.""" + + attributes: Union[FolderAttributes, UnsetType] = UNSET + relationship_attributes: Union[FolderRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[FolderRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[FolderRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_FOLDER_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "children_folders", + "parent", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "children_queries", + "schema_registry_subjects", + "soda_checks", +] + + +def _populate_folder_attrs(attrs: FolderAttributes, obj: Folder) -> None: + """Populate Folder-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.parent_qualified_name = obj.parent_qualified_name + attrs.collection_qualified_name = obj.collection_qualified_name + + +def _extract_folder_attrs(attrs: FolderAttributes) -> dict: + """Extract all Folder attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["parent_qualified_name"] = attrs.parent_qualified_name + result["collection_qualified_name"] = attrs.collection_qualified_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _folder_to_nested(folder: Folder) -> FolderNested: + """Convert flat Folder to nested format.""" + attrs = FolderAttributes() + _populate_folder_attrs(attrs, folder) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + folder, _FOLDER_REL_FIELDS, FolderRelationshipAttributes + ) + return FolderNested( + guid=folder.guid, + type_name=folder.type_name, + status=folder.status, + version=folder.version, + create_time=folder.create_time, + update_time=folder.update_time, + created_by=folder.created_by, + updated_by=folder.updated_by, + classifications=folder.classifications, + classification_names=folder.classification_names, + meanings=folder.meanings, + labels=folder.labels, + business_attributes=folder.business_attributes, + custom_attributes=folder.custom_attributes, + pending_tasks=folder.pending_tasks, + proxy=folder.proxy, + is_incomplete=folder.is_incomplete, + provenance_type=folder.provenance_type, + home_id=folder.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _folder_from_nested(nested: FolderNested) -> Folder: + """Convert nested format to flat Folder.""" + attrs = nested.attributes if nested.attributes is not UNSET else FolderAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _FOLDER_REL_FIELDS, + FolderRelationshipAttributes, + ) + return Folder( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_folder_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _folder_to_nested_bytes(folder: Folder, serde: Serde) -> bytes: + """Convert flat Folder to nested JSON bytes.""" + return serde.encode(_folder_to_nested(folder)) + + +def _folder_from_nested_bytes(data: bytes, serde: Serde) -> Folder: + """Convert nested JSON bytes to flat Folder.""" + nested = serde.decode(data, FolderNested) + return _folder_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordTextField, + RelationField, +) + +Folder.PARENT_QUALIFIED_NAME = KeywordTextField( + "parentQualifiedName", "parentQualifiedName", "parentQualifiedName.text" +) +Folder.COLLECTION_QUALIFIED_NAME = KeywordTextField( + "collectionQualifiedName", "collectionQualifiedName", "collectionQualifiedName.text" +) +Folder.ANOMALO_CHECKS = RelationField("anomaloChecks") +Folder.APPLICATION = RelationField("application") +Folder.APPLICATION_FIELD = RelationField("applicationField") +Folder.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Folder.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Folder.METRICS = RelationField("metrics") +Folder.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Folder.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Folder.MEANINGS = RelationField("meanings") +Folder.MC_MONITORS = RelationField("mcMonitors") +Folder.MC_INCIDENTS = RelationField("mcIncidents") +Folder.CHILDREN_FOLDERS = RelationField("childrenFolders") +Folder.PARENT = RelationField("parent") +Folder.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Folder.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Folder.FILES = RelationField("files") +Folder.LINKS = RelationField("links") +Folder.README = RelationField("readme") +Folder.CHILDREN_QUERIES = RelationField("childrenQueries") +Folder.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Folder.SODA_CHECKS = RelationField("sodaChecks") diff --git a/pyatlan_v9/model/assets/form.py b/pyatlan_v9/model/assets/form.py new file mode 100644 index 000000000..614024563 --- /dev/null +++ b/pyatlan_v9/model/assets/form.py @@ -0,0 +1,442 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Form asset model with flattened inheritance. + +This module provides: +- Form: Flat asset class (easy to use) +- FormAttributes: Nested attributes struct (extends AssetAttributes) +- FormNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Form(Asset): + """ + Instance of a form in Atlan. + """ + + FORM_FIELDS: ClassVar[Any] = None + FORM_OPTIONS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Form" + + form_fields: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """Fields in a form.""" + + form_options: Union[Dict[str, str], None, UnsetType] = UNSET + """Options of the form.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Form" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _form_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Form: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Form instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _form_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class FormAttributes(AssetAttributes): + """Form-specific attributes for nested API format.""" + + form_fields: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """Fields in a form.""" + + form_options: Union[Dict[str, str], None, UnsetType] = UNSET + """Options of the form.""" + + +class FormRelationshipAttributes(AssetRelationshipAttributes): + """Form-specific relationship attributes for nested API format.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + +class FormNested(AssetNested): + """Form in nested API format for high-performance serialization.""" + + attributes: Union[FormAttributes, UnsetType] = UNSET + relationship_attributes: Union[FormRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[FormRelationshipAttributes, UnsetType] = UNSET + remove_relationship_attributes: Union[FormRelationshipAttributes, UnsetType] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_FORM_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", +] + + +def _populate_form_attrs(attrs: FormAttributes, obj: Form) -> None: + """Populate Form-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.form_fields = obj.form_fields + attrs.form_options = obj.form_options + + +def _extract_form_attrs(attrs: FormAttributes) -> dict: + """Extract all Form attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["form_fields"] = attrs.form_fields + result["form_options"] = attrs.form_options + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _form_to_nested(form: Form) -> FormNested: + """Convert flat Form to nested format.""" + attrs = FormAttributes() + _populate_form_attrs(attrs, form) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + form, _FORM_REL_FIELDS, FormRelationshipAttributes + ) + return FormNested( + guid=form.guid, + type_name=form.type_name, + status=form.status, + version=form.version, + create_time=form.create_time, + update_time=form.update_time, + created_by=form.created_by, + updated_by=form.updated_by, + classifications=form.classifications, + classification_names=form.classification_names, + meanings=form.meanings, + labels=form.labels, + business_attributes=form.business_attributes, + custom_attributes=form.custom_attributes, + pending_tasks=form.pending_tasks, + proxy=form.proxy, + is_incomplete=form.is_incomplete, + provenance_type=form.provenance_type, + home_id=form.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _form_from_nested(nested: FormNested) -> Form: + """Convert nested format to flat Form.""" + attrs = nested.attributes if nested.attributes is not UNSET else FormAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _FORM_REL_FIELDS, + FormRelationshipAttributes, + ) + return Form( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_form_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _form_to_nested_bytes(form: Form, serde: Serde) -> bytes: + """Convert flat Form to nested JSON bytes.""" + return serde.encode(_form_to_nested(form)) + + +def _form_from_nested_bytes(data: bytes, serde: Serde) -> Form: + """Convert nested JSON bytes to flat Form.""" + nested = serde.decode(data, FormNested) + return _form_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +Form.FORM_FIELDS = KeywordField("formFields", "formFields") +Form.FORM_OPTIONS = KeywordField("formOptions", "formOptions") +Form.ANOMALO_CHECKS = RelationField("anomaloChecks") +Form.APPLICATION = RelationField("application") +Form.APPLICATION_FIELD = RelationField("applicationField") +Form.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Form.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Form.METRICS = RelationField("metrics") +Form.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Form.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Form.MEANINGS = RelationField("meanings") +Form.MC_MONITORS = RelationField("mcMonitors") +Form.MC_INCIDENTS = RelationField("mcIncidents") +Form.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Form.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Form.FILES = RelationField("files") +Form.LINKS = RelationField("links") +Form.README = RelationField("readme") +Form.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Form.SODA_CHECKS = RelationField("sodaChecks") diff --git a/pyatlan_v9/model/assets/form_related.py b/pyatlan_v9/model/assets/form_related.py new file mode 100644 index 000000000..a54e63a70 --- /dev/null +++ b/pyatlan_v9/model/assets/form_related.py @@ -0,0 +1,69 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Form module. + +This module contains all Related{Type} classes for the Form type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Any, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .asset_related import RelatedAsset +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedForm", + "RelatedResponse", +] + + +class RelatedForm(RelatedAsset): + """ + Related entity reference for Form assets. + + Extends RelatedAsset with Form-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Form" so it serializes correctly + + form_fields: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """Fields in a form.""" + + form_options: Union[Dict[str, str], None, UnsetType] = UNSET + """Options of the form.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Form" + + +class RelatedResponse(RelatedForm): + """ + Related entity reference for Response assets. + + Extends RelatedForm with Response-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Response" so it serializes correctly + + form_guid: Union[str, None, UnsetType] = UNSET + """Unique identifier of the form.""" + + response_values: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """Fields in a form.""" + + response_options: Union[Dict[str, str], None, UnsetType] = UNSET + """Options of the response to a form.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Response" diff --git a/pyatlan_v9/model/assets/function.py b/pyatlan_v9/model/assets/function.py new file mode 100644 index 000000000..c18d33e66 --- /dev/null +++ b/pyatlan_v9/model/assets/function.py @@ -0,0 +1,978 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Function asset model with flattened inheritance. + +This module provides: +- Function: Flat asset class (easy to use) +- FunctionAttributes: Nested attributes struct (extends AssetAttributes) +- FunctionNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .snowflake_related import RelatedSnowflakeSemanticLogicalTable +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .sql_related import RelatedSchema + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Function(Asset): + """ + Instance of a function in Atlan. + """ + + FUNCTION_DEFINITION: ClassVar[Any] = None + SQL_RETURN_TYPE: ClassVar[Any] = None + SQL_ARGUMENTS: ClassVar[Any] = None + SQL_LANGUAGE: ClassVar[Any] = None + SQL_TYPE: ClassVar[Any] = None + SQL_IS_EXTERNAL: ClassVar[Any] = None + SQL_IS_DMF: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + SQL_IS_MEMOIZABLE: ClassVar[Any] = None + SQL_RUNTIME_VERSION: ClassVar[Any] = None + SQL_EXTERNAL_ACCESS_INTEGRATIONS: ClassVar[Any] = None + SQL_SECRETS: ClassVar[Any] = None + SQL_PACKAGES: ClassVar[Any] = None + SQL_INSTALLED_PACKAGES: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + FUNCTION_SCHEMA: ClassVar[Any] = None + SQL_PROCESSES: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Function" + + function_definition: Union[str, None, UnsetType] = UNSET + """Code or set of statements that determine the output of the function.""" + + sql_return_type: Union[str, None, UnsetType] = UNSET + """Data type of the value returned by the function.""" + + sql_arguments: Union[List[str], None, UnsetType] = UNSET + """Arguments that are passed in to the function.""" + + sql_language: Union[str, None, UnsetType] = UNSET + """Programming language in which the function is written.""" + + sql_type: Union[str, None, UnsetType] = UNSET + """Type of function.""" + + sql_is_external: Union[bool, None, UnsetType] = UNSET + """Whether the function is stored or executed externally (true) or internally (false).""" + + sql_is_dmf: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlIsDMF" + ) + """Whether the function is a data metric function.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + sql_is_memoizable: Union[bool, None, UnsetType] = UNSET + """Whether the function must re-compute if there are no underlying changes in the values (false) or not (true).""" + + sql_runtime_version: Union[str, None, UnsetType] = UNSET + """Version of the language runtime used by the function.""" + + sql_external_access_integrations: Union[str, None, UnsetType] = UNSET + """Names of external access integrations used by the function.""" + + sql_secrets: Union[str, None, UnsetType] = UNSET + """Secret variables used by the function.""" + + sql_packages: Union[str, None, UnsetType] = UNSET + """Packages requested by the function.""" + + sql_installed_packages: Union[str, None, UnsetType] = UNSET + """Packages actually installed for the function.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + function_schema: Union[RelatedSchema, None, UnsetType] = UNSET + """Schema in which this function exists.""" + + sql_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes that utilize this function.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Function" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _function_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Function: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Function instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _function_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class FunctionAttributes(AssetAttributes): + """Function-specific attributes for nested API format.""" + + function_definition: Union[str, None, UnsetType] = UNSET + """Code or set of statements that determine the output of the function.""" + + sql_return_type: Union[str, None, UnsetType] = UNSET + """Data type of the value returned by the function.""" + + sql_arguments: Union[List[str], None, UnsetType] = UNSET + """Arguments that are passed in to the function.""" + + sql_language: Union[str, None, UnsetType] = UNSET + """Programming language in which the function is written.""" + + sql_type: Union[str, None, UnsetType] = UNSET + """Type of function.""" + + sql_is_external: Union[bool, None, UnsetType] = UNSET + """Whether the function is stored or executed externally (true) or internally (false).""" + + sql_is_dmf: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlIsDMF" + ) + """Whether the function is a data metric function.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + sql_is_memoizable: Union[bool, None, UnsetType] = UNSET + """Whether the function must re-compute if there are no underlying changes in the values (false) or not (true).""" + + sql_runtime_version: Union[str, None, UnsetType] = UNSET + """Version of the language runtime used by the function.""" + + sql_external_access_integrations: Union[str, None, UnsetType] = UNSET + """Names of external access integrations used by the function.""" + + sql_secrets: Union[str, None, UnsetType] = UNSET + """Secret variables used by the function.""" + + sql_packages: Union[str, None, UnsetType] = UNSET + """Packages requested by the function.""" + + sql_installed_packages: Union[str, None, UnsetType] = UNSET + """Packages actually installed for the function.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + +class FunctionRelationshipAttributes(AssetRelationshipAttributes): + """Function-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + function_schema: Union[RelatedSchema, None, UnsetType] = UNSET + """Schema in which this function exists.""" + + sql_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes that utilize this function.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class FunctionNested(AssetNested): + """Function in nested API format for high-performance serialization.""" + + attributes: Union[FunctionAttributes, UnsetType] = UNSET + relationship_attributes: Union[FunctionRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[FunctionRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[FunctionRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_FUNCTION_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "function_schema", + "sql_processes", + "schema_registry_subjects", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_function_attrs(attrs: FunctionAttributes, obj: Function) -> None: + """Populate Function-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.function_definition = obj.function_definition + attrs.sql_return_type = obj.sql_return_type + attrs.sql_arguments = obj.sql_arguments + attrs.sql_language = obj.sql_language + attrs.sql_type = obj.sql_type + attrs.sql_is_external = obj.sql_is_external + attrs.sql_is_dmf = obj.sql_is_dmf + attrs.sql_is_secure = obj.sql_is_secure + attrs.sql_is_memoizable = obj.sql_is_memoizable + attrs.sql_runtime_version = obj.sql_runtime_version + attrs.sql_external_access_integrations = obj.sql_external_access_integrations + attrs.sql_secrets = obj.sql_secrets + attrs.sql_packages = obj.sql_packages + attrs.sql_installed_packages = obj.sql_installed_packages + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + + +def _extract_function_attrs(attrs: FunctionAttributes) -> dict: + """Extract all Function attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["function_definition"] = attrs.function_definition + result["sql_return_type"] = attrs.sql_return_type + result["sql_arguments"] = attrs.sql_arguments + result["sql_language"] = attrs.sql_language + result["sql_type"] = attrs.sql_type + result["sql_is_external"] = attrs.sql_is_external + result["sql_is_dmf"] = attrs.sql_is_dmf + result["sql_is_secure"] = attrs.sql_is_secure + result["sql_is_memoizable"] = attrs.sql_is_memoizable + result["sql_runtime_version"] = attrs.sql_runtime_version + result["sql_external_access_integrations"] = attrs.sql_external_access_integrations + result["sql_secrets"] = attrs.sql_secrets + result["sql_packages"] = attrs.sql_packages + result["sql_installed_packages"] = attrs.sql_installed_packages + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _function_to_nested(function: Function) -> FunctionNested: + """Convert flat Function to nested format.""" + attrs = FunctionAttributes() + _populate_function_attrs(attrs, function) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + function, _FUNCTION_REL_FIELDS, FunctionRelationshipAttributes + ) + return FunctionNested( + guid=function.guid, + type_name=function.type_name, + status=function.status, + version=function.version, + create_time=function.create_time, + update_time=function.update_time, + created_by=function.created_by, + updated_by=function.updated_by, + classifications=function.classifications, + classification_names=function.classification_names, + meanings=function.meanings, + labels=function.labels, + business_attributes=function.business_attributes, + custom_attributes=function.custom_attributes, + pending_tasks=function.pending_tasks, + proxy=function.proxy, + is_incomplete=function.is_incomplete, + provenance_type=function.provenance_type, + home_id=function.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _function_from_nested(nested: FunctionNested) -> Function: + """Convert nested format to flat Function.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else FunctionAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _FUNCTION_REL_FIELDS, + FunctionRelationshipAttributes, + ) + return Function( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_function_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _function_to_nested_bytes(function: Function, serde: Serde) -> bytes: + """Convert flat Function to nested JSON bytes.""" + return serde.encode(_function_to_nested(function)) + + +def _function_from_nested_bytes(data: bytes, serde: Serde) -> Function: + """Convert nested JSON bytes to flat Function.""" + nested = serde.decode(data, FunctionNested) + return _function_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +Function.FUNCTION_DEFINITION = KeywordField("functionDefinition", "functionDefinition") +Function.SQL_RETURN_TYPE = KeywordField("sqlReturnType", "sqlReturnType") +Function.SQL_ARGUMENTS = KeywordField("sqlArguments", "sqlArguments") +Function.SQL_LANGUAGE = KeywordField("sqlLanguage", "sqlLanguage") +Function.SQL_TYPE = KeywordField("sqlType", "sqlType") +Function.SQL_IS_EXTERNAL = BooleanField("sqlIsExternal", "sqlIsExternal") +Function.SQL_IS_DMF = BooleanField("sqlIsDMF", "sqlIsDMF") +Function.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +Function.SQL_IS_MEMOIZABLE = BooleanField("sqlIsMemoizable", "sqlIsMemoizable") +Function.SQL_RUNTIME_VERSION = KeywordTextField( + "sqlRuntimeVersion", "sqlRuntimeVersion", "sqlRuntimeVersion.text" +) +Function.SQL_EXTERNAL_ACCESS_INTEGRATIONS = KeywordField( + "sqlExternalAccessIntegrations", "sqlExternalAccessIntegrations" +) +Function.SQL_SECRETS = KeywordField("sqlSecrets", "sqlSecrets") +Function.SQL_PACKAGES = KeywordField("sqlPackages", "sqlPackages") +Function.SQL_INSTALLED_PACKAGES = KeywordField( + "sqlInstalledPackages", "sqlInstalledPackages" +) +Function.QUERY_COUNT = NumericField("queryCount", "queryCount") +Function.QUERY_USER_COUNT = NumericField("queryUserCount", "queryUserCount") +Function.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +Function.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +Function.DATABASE_NAME = KeywordField("databaseName", "databaseName") +Function.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +Function.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +Function.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +Function.TABLE_NAME = KeywordField("tableName", "tableName") +Function.TABLE_QUALIFIED_NAME = KeywordField("tableQualifiedName", "tableQualifiedName") +Function.VIEW_NAME = KeywordField("viewName", "viewName") +Function.VIEW_QUALIFIED_NAME = KeywordField("viewQualifiedName", "viewQualifiedName") +Function.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +Function.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +Function.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +Function.LAST_PROFILED_AT = NumericField("lastProfiledAt", "lastProfiledAt") +Function.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +Function.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Function.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Function.ANOMALO_CHECKS = RelationField("anomaloChecks") +Function.APPLICATION = RelationField("application") +Function.APPLICATION_FIELD = RelationField("applicationField") +Function.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Function.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Function.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Function.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Function.METRICS = RelationField("metrics") +Function.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Function.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Function.DBT_MODELS = RelationField("dbtModels") +Function.SQL_DBT_MODELS = RelationField("sqlDbtModels") +Function.DBT_TESTS = RelationField("dbtTests") +Function.DBT_SOURCES = RelationField("dbtSources") +Function.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +Function.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +Function.MEANINGS = RelationField("meanings") +Function.MC_MONITORS = RelationField("mcMonitors") +Function.MC_INCIDENTS = RelationField("mcIncidents") +Function.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Function.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Function.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Function.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Function.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Function.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Function.FILES = RelationField("files") +Function.LINKS = RelationField("links") +Function.README = RelationField("readme") +Function.FUNCTION_SCHEMA = RelationField("functionSchema") +Function.SQL_PROCESSES = RelationField("sqlProcesses") +Function.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Function.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +Function.SODA_CHECKS = RelationField("sodaChecks") +Function.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Function.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/gcs.py b/pyatlan_v9/model/assets/gcs.py new file mode 100644 index 000000000..d78213818 --- /dev/null +++ b/pyatlan_v9/model/assets/gcs.py @@ -0,0 +1,681 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +GCS asset model with flattened inheritance. + +This module provides: +- GCS: Flat asset class (easy to use) +- GCSAttributes: Nested attributes struct (extends AssetAttributes) +- GCSNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class GCS(Asset): + """ + Base class for Google Cloud Storage (GCS) assets. + """ + + GCS_STORAGE_CLASS: ClassVar[Any] = None + GCS_ENCRYPTION_TYPE: ClassVar[Any] = None + GCS_ETAG: ClassVar[Any] = None + GCS_REQUESTER_PAYS: ClassVar[Any] = None + GCS_ACCESS_CONTROL: ClassVar[Any] = None + GCS_META_GENERATION_ID: ClassVar[Any] = None + GOOGLE_SERVICE: ClassVar[Any] = None + GOOGLE_PROJECT_NAME: ClassVar[Any] = None + GOOGLE_PROJECT_ID: ClassVar[Any] = None + GOOGLE_PROJECT_NUMBER: ClassVar[Any] = None + GOOGLE_LOCATION: ClassVar[Any] = None + GOOGLE_LOCATION_TYPE: ClassVar[Any] = None + GOOGLE_LABELS: ClassVar[Any] = None + GOOGLE_TAGS: ClassVar[Any] = None + CLOUD_UNIFORM_RESOURCE_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "GCS" + + gcs_storage_class: Union[str, None, UnsetType] = UNSET + """Storage class of this asset.""" + + gcs_encryption_type: Union[str, None, UnsetType] = UNSET + """Encryption algorithm used to encrypt this asset.""" + + gcs_etag: Union[str, None, UnsetType] = msgspec.field(default=UNSET, name="gcsETag") + """Entity tag for the asset. An entity tag is a hash of the object and represents changes to the contents of an object only, not its metadata.""" + + gcs_requester_pays: Union[bool, None, UnsetType] = UNSET + """Whether the requester pays header was sent when this asset was created (true) or not (false).""" + + gcs_access_control: Union[str, None, UnsetType] = UNSET + """Access control list for this asset.""" + + gcs_meta_generation_id: Union[int, None, UnsetType] = UNSET + """Version of metadata for this asset at this generation. Used for preconditions and detecting changes in metadata. A metageneration number is only meaningful in the context of a particular generation of a particular asset.""" + + google_service: Union[str, None, UnsetType] = UNSET + """Service in Google in which the asset exists.""" + + google_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which the asset exists.""" + + google_project_id: Union[str, None, UnsetType] = UNSET + """ID of the project in which the asset exists.""" + + google_project_number: Union[int, None, UnsetType] = UNSET + """Number of the project in which the asset exists.""" + + google_location: Union[str, None, UnsetType] = UNSET + """Location of this asset in Google.""" + + google_location_type: Union[str, None, UnsetType] = UNSET + """Type of location of this asset in Google.""" + + google_labels: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of labels that have been applied to the asset in Google.""" + + google_tags: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of tags that have been applied to the asset in Google.""" + + cloud_uniform_resource_name: Union[str, None, UnsetType] = UNSET + """Uniform resource name (URN) for the asset: AWS ARN, Google Cloud URI, Azure resource ID, Oracle OCID, and so on.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "GCS" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _gcs_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> GCS: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + GCS instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _gcs_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class GCSAttributes(AssetAttributes): + """GCS-specific attributes for nested API format.""" + + gcs_storage_class: Union[str, None, UnsetType] = UNSET + """Storage class of this asset.""" + + gcs_encryption_type: Union[str, None, UnsetType] = UNSET + """Encryption algorithm used to encrypt this asset.""" + + gcs_etag: Union[str, None, UnsetType] = msgspec.field(default=UNSET, name="gcsETag") + """Entity tag for the asset. An entity tag is a hash of the object and represents changes to the contents of an object only, not its metadata.""" + + gcs_requester_pays: Union[bool, None, UnsetType] = UNSET + """Whether the requester pays header was sent when this asset was created (true) or not (false).""" + + gcs_access_control: Union[str, None, UnsetType] = UNSET + """Access control list for this asset.""" + + gcs_meta_generation_id: Union[int, None, UnsetType] = UNSET + """Version of metadata for this asset at this generation. Used for preconditions and detecting changes in metadata. A metageneration number is only meaningful in the context of a particular generation of a particular asset.""" + + google_service: Union[str, None, UnsetType] = UNSET + """Service in Google in which the asset exists.""" + + google_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which the asset exists.""" + + google_project_id: Union[str, None, UnsetType] = UNSET + """ID of the project in which the asset exists.""" + + google_project_number: Union[int, None, UnsetType] = UNSET + """Number of the project in which the asset exists.""" + + google_location: Union[str, None, UnsetType] = UNSET + """Location of this asset in Google.""" + + google_location_type: Union[str, None, UnsetType] = UNSET + """Type of location of this asset in Google.""" + + google_labels: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of labels that have been applied to the asset in Google.""" + + google_tags: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of tags that have been applied to the asset in Google.""" + + cloud_uniform_resource_name: Union[str, None, UnsetType] = UNSET + """Uniform resource name (URN) for the asset: AWS ARN, Google Cloud URI, Azure resource ID, Oracle OCID, and so on.""" + + +class GCSRelationshipAttributes(AssetRelationshipAttributes): + """GCS-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class GCSNested(AssetNested): + """GCS in nested API format for high-performance serialization.""" + + attributes: Union[GCSAttributes, UnsetType] = UNSET + relationship_attributes: Union[GCSRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[GCSRelationshipAttributes, UnsetType] = UNSET + remove_relationship_attributes: Union[GCSRelationshipAttributes, UnsetType] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_GCS_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_gcs_attrs(attrs: GCSAttributes, obj: GCS) -> None: + """Populate GCS-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.gcs_storage_class = obj.gcs_storage_class + attrs.gcs_encryption_type = obj.gcs_encryption_type + attrs.gcs_etag = obj.gcs_etag + attrs.gcs_requester_pays = obj.gcs_requester_pays + attrs.gcs_access_control = obj.gcs_access_control + attrs.gcs_meta_generation_id = obj.gcs_meta_generation_id + attrs.google_service = obj.google_service + attrs.google_project_name = obj.google_project_name + attrs.google_project_id = obj.google_project_id + attrs.google_project_number = obj.google_project_number + attrs.google_location = obj.google_location + attrs.google_location_type = obj.google_location_type + attrs.google_labels = obj.google_labels + attrs.google_tags = obj.google_tags + attrs.cloud_uniform_resource_name = obj.cloud_uniform_resource_name + + +def _extract_gcs_attrs(attrs: GCSAttributes) -> dict: + """Extract all GCS attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["gcs_storage_class"] = attrs.gcs_storage_class + result["gcs_encryption_type"] = attrs.gcs_encryption_type + result["gcs_etag"] = attrs.gcs_etag + result["gcs_requester_pays"] = attrs.gcs_requester_pays + result["gcs_access_control"] = attrs.gcs_access_control + result["gcs_meta_generation_id"] = attrs.gcs_meta_generation_id + result["google_service"] = attrs.google_service + result["google_project_name"] = attrs.google_project_name + result["google_project_id"] = attrs.google_project_id + result["google_project_number"] = attrs.google_project_number + result["google_location"] = attrs.google_location + result["google_location_type"] = attrs.google_location_type + result["google_labels"] = attrs.google_labels + result["google_tags"] = attrs.google_tags + result["cloud_uniform_resource_name"] = attrs.cloud_uniform_resource_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _gcs_to_nested(gcs: GCS) -> GCSNested: + """Convert flat GCS to nested format.""" + attrs = GCSAttributes() + _populate_gcs_attrs(attrs, gcs) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + gcs, _GCS_REL_FIELDS, GCSRelationshipAttributes + ) + return GCSNested( + guid=gcs.guid, + type_name=gcs.type_name, + status=gcs.status, + version=gcs.version, + create_time=gcs.create_time, + update_time=gcs.update_time, + created_by=gcs.created_by, + updated_by=gcs.updated_by, + classifications=gcs.classifications, + classification_names=gcs.classification_names, + meanings=gcs.meanings, + labels=gcs.labels, + business_attributes=gcs.business_attributes, + custom_attributes=gcs.custom_attributes, + pending_tasks=gcs.pending_tasks, + proxy=gcs.proxy, + is_incomplete=gcs.is_incomplete, + provenance_type=gcs.provenance_type, + home_id=gcs.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _gcs_from_nested(nested: GCSNested) -> GCS: + """Convert nested format to flat GCS.""" + attrs = nested.attributes if nested.attributes is not UNSET else GCSAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _GCS_REL_FIELDS, + GCSRelationshipAttributes, + ) + return GCS( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_gcs_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _gcs_to_nested_bytes(gcs: GCS, serde: Serde) -> bytes: + """Convert flat GCS to nested JSON bytes.""" + return serde.encode(_gcs_to_nested(gcs)) + + +def _gcs_from_nested_bytes(data: bytes, serde: Serde) -> GCS: + """Convert nested JSON bytes to flat GCS.""" + nested = serde.decode(data, GCSNested) + return _gcs_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +GCS.GCS_STORAGE_CLASS = KeywordField("gcsStorageClass", "gcsStorageClass") +GCS.GCS_ENCRYPTION_TYPE = KeywordField("gcsEncryptionType", "gcsEncryptionType") +GCS.GCS_ETAG = KeywordField("gcsETag", "gcsETag") +GCS.GCS_REQUESTER_PAYS = BooleanField("gcsRequesterPays", "gcsRequesterPays") +GCS.GCS_ACCESS_CONTROL = KeywordField("gcsAccessControl", "gcsAccessControl") +GCS.GCS_META_GENERATION_ID = NumericField("gcsMetaGenerationId", "gcsMetaGenerationId") +GCS.GOOGLE_SERVICE = KeywordField("googleService", "googleService") +GCS.GOOGLE_PROJECT_NAME = KeywordTextField( + "googleProjectName", "googleProjectName", "googleProjectName.text" +) +GCS.GOOGLE_PROJECT_ID = KeywordTextField( + "googleProjectId", "googleProjectId", "googleProjectId.text" +) +GCS.GOOGLE_PROJECT_NUMBER = NumericField("googleProjectNumber", "googleProjectNumber") +GCS.GOOGLE_LOCATION = KeywordField("googleLocation", "googleLocation") +GCS.GOOGLE_LOCATION_TYPE = KeywordField("googleLocationType", "googleLocationType") +GCS.GOOGLE_LABELS = KeywordField("googleLabels", "googleLabels") +GCS.GOOGLE_TAGS = KeywordField("googleTags", "googleTags") +GCS.CLOUD_UNIFORM_RESOURCE_NAME = KeywordField( + "cloudUniformResourceName", "cloudUniformResourceName" +) +GCS.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +GCS.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +GCS.ANOMALO_CHECKS = RelationField("anomaloChecks") +GCS.APPLICATION = RelationField("application") +GCS.APPLICATION_FIELD = RelationField("applicationField") +GCS.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +GCS.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +GCS.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +GCS.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +GCS.METRICS = RelationField("metrics") +GCS.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +GCS.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +GCS.MEANINGS = RelationField("meanings") +GCS.MC_MONITORS = RelationField("mcMonitors") +GCS.MC_INCIDENTS = RelationField("mcIncidents") +GCS.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +GCS.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +GCS.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +GCS.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +GCS.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +GCS.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +GCS.FILES = RelationField("files") +GCS.LINKS = RelationField("links") +GCS.README = RelationField("readme") +GCS.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +GCS.SODA_CHECKS = RelationField("sodaChecks") +GCS.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +GCS.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/gcs_bucket.py b/pyatlan_v9/model/assets/gcs_bucket.py new file mode 100644 index 000000000..8e97e9e66 --- /dev/null +++ b/pyatlan_v9/model/assets/gcs_bucket.py @@ -0,0 +1,844 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +GCSBucket asset model with flattened inheritance. + +This module provides: +- GCSBucket: Flat asset class (easy to use) +- GCSBucketAttributes: Nested attributes struct (extends AssetAttributes) +- GCSBucketNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .gcs_related import RelatedGCSObject + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class GCSBucket(Asset): + """ + Instance of a Google Cloud Storage bucket in Atlan. + """ + + GCS_OBJECT_COUNT: ClassVar[Any] = None + GCS_BUCKET_VERSIONING_ENABLED: ClassVar[Any] = None + GCS_BUCKET_RETENTION_LOCKED: ClassVar[Any] = None + GCS_BUCKET_RETENTION_PERIOD: ClassVar[Any] = None + GCS_BUCKET_RETENTION_EFFECTIVE_TIME: ClassVar[Any] = None + GCS_BUCKET_LIFECYCLE_RULES: ClassVar[Any] = None + GCS_BUCKET_RETENTION_POLICY: ClassVar[Any] = None + GCS_STORAGE_CLASS: ClassVar[Any] = None + GCS_ENCRYPTION_TYPE: ClassVar[Any] = None + GCS_ETAG: ClassVar[Any] = None + GCS_REQUESTER_PAYS: ClassVar[Any] = None + GCS_ACCESS_CONTROL: ClassVar[Any] = None + GCS_META_GENERATION_ID: ClassVar[Any] = None + GOOGLE_SERVICE: ClassVar[Any] = None + GOOGLE_PROJECT_NAME: ClassVar[Any] = None + GOOGLE_PROJECT_ID: ClassVar[Any] = None + GOOGLE_PROJECT_NUMBER: ClassVar[Any] = None + GOOGLE_LOCATION: ClassVar[Any] = None + GOOGLE_LOCATION_TYPE: ClassVar[Any] = None + GOOGLE_LABELS: ClassVar[Any] = None + GOOGLE_TAGS: ClassVar[Any] = None + CLOUD_UNIFORM_RESOURCE_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + GCS_OBJECTS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "GCSBucket" + + gcs_object_count: Union[int, None, UnsetType] = UNSET + """Number of objects within the bucket.""" + + gcs_bucket_versioning_enabled: Union[bool, None, UnsetType] = UNSET + """Whether versioning is enabled on the bucket (true) or not (false).""" + + gcs_bucket_retention_locked: Union[bool, None, UnsetType] = UNSET + """Whether retention is locked for this bucket (true) or not (false).""" + + gcs_bucket_retention_period: Union[int, None, UnsetType] = UNSET + """Retention period for objects in this bucket.""" + + gcs_bucket_retention_effective_time: Union[int, None, UnsetType] = UNSET + """Effective time for retention of objects in this bucket.""" + + gcs_bucket_lifecycle_rules: Union[str, None, UnsetType] = UNSET + """Lifecycle rules for this bucket.""" + + gcs_bucket_retention_policy: Union[str, None, UnsetType] = UNSET + """Retention policy for this bucket.""" + + gcs_storage_class: Union[str, None, UnsetType] = UNSET + """Storage class of this asset.""" + + gcs_encryption_type: Union[str, None, UnsetType] = UNSET + """Encryption algorithm used to encrypt this asset.""" + + gcs_etag: Union[str, None, UnsetType] = msgspec.field(default=UNSET, name="gcsETag") + """Entity tag for the asset. An entity tag is a hash of the object and represents changes to the contents of an object only, not its metadata.""" + + gcs_requester_pays: Union[bool, None, UnsetType] = UNSET + """Whether the requester pays header was sent when this asset was created (true) or not (false).""" + + gcs_access_control: Union[str, None, UnsetType] = UNSET + """Access control list for this asset.""" + + gcs_meta_generation_id: Union[int, None, UnsetType] = UNSET + """Version of metadata for this asset at this generation. Used for preconditions and detecting changes in metadata. A metageneration number is only meaningful in the context of a particular generation of a particular asset.""" + + google_service: Union[str, None, UnsetType] = UNSET + """Service in Google in which the asset exists.""" + + google_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which the asset exists.""" + + google_project_id: Union[str, None, UnsetType] = UNSET + """ID of the project in which the asset exists.""" + + google_project_number: Union[int, None, UnsetType] = UNSET + """Number of the project in which the asset exists.""" + + google_location: Union[str, None, UnsetType] = UNSET + """Location of this asset in Google.""" + + google_location_type: Union[str, None, UnsetType] = UNSET + """Type of location of this asset in Google.""" + + google_labels: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of labels that have been applied to the asset in Google.""" + + google_tags: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of tags that have been applied to the asset in Google.""" + + cloud_uniform_resource_name: Union[str, None, UnsetType] = UNSET + """Uniform resource name (URN) for the asset: AWS ARN, Google Cloud URI, Azure resource ID, Oracle OCID, and so on.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + gcs_objects: Union[List[RelatedGCSObject], None, UnsetType] = UNSET + """GCS objects within this bucket.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "GCSBucket" + + @classmethod + @init_guid + def creator(cls, *, name: str, connection_qualified_name: str) -> "GCSBucket": + """ + Create a new GCSBucket asset. + + Args: + name: Name of the bucket + connection_qualified_name: Unique name of the connection + + Returns: + GCSBucket instance ready to be created + + Raises: + ValueError: If required parameters are missing + """ + validate_required_fields( + ["name", "connection_qualified_name"], [name, connection_qualified_name] + ) + # Extract connector name from the connection_qualified_name + connector_name = connection_qualified_name.split("/")[1] + return cls( + name=name, + qualified_name=f"{connection_qualified_name}/{name}", + connection_qualified_name=connection_qualified_name, + connector_name=connector_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "GCSBucket": + """ + Create a GCSBucket instance for modification. + + Args: + qualified_name: Unique name of the GCSBucket to update + name: Human-readable name of the GCSBucket + + Returns: + GCSBucket instance ready for update + + Raises: + ValueError: If required parameters are missing + """ + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "GCSBucket": + """ + Return a copy of this GCSBucket with only the minimum required fields for update. + + Returns: + GCSBucket with only qualified_name and name set + """ + return GCSBucket.updater(qualified_name=self.qualified_name, name=self.name) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _gcs_bucket_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> GCSBucket: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + GCSBucket instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _gcs_bucket_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class GCSBucketAttributes(AssetAttributes): + """GCSBucket-specific attributes for nested API format.""" + + gcs_object_count: Union[int, None, UnsetType] = UNSET + """Number of objects within the bucket.""" + + gcs_bucket_versioning_enabled: Union[bool, None, UnsetType] = UNSET + """Whether versioning is enabled on the bucket (true) or not (false).""" + + gcs_bucket_retention_locked: Union[bool, None, UnsetType] = UNSET + """Whether retention is locked for this bucket (true) or not (false).""" + + gcs_bucket_retention_period: Union[int, None, UnsetType] = UNSET + """Retention period for objects in this bucket.""" + + gcs_bucket_retention_effective_time: Union[int, None, UnsetType] = UNSET + """Effective time for retention of objects in this bucket.""" + + gcs_bucket_lifecycle_rules: Union[str, None, UnsetType] = UNSET + """Lifecycle rules for this bucket.""" + + gcs_bucket_retention_policy: Union[str, None, UnsetType] = UNSET + """Retention policy for this bucket.""" + + gcs_storage_class: Union[str, None, UnsetType] = UNSET + """Storage class of this asset.""" + + gcs_encryption_type: Union[str, None, UnsetType] = UNSET + """Encryption algorithm used to encrypt this asset.""" + + gcs_etag: Union[str, None, UnsetType] = msgspec.field(default=UNSET, name="gcsETag") + """Entity tag for the asset. An entity tag is a hash of the object and represents changes to the contents of an object only, not its metadata.""" + + gcs_requester_pays: Union[bool, None, UnsetType] = UNSET + """Whether the requester pays header was sent when this asset was created (true) or not (false).""" + + gcs_access_control: Union[str, None, UnsetType] = UNSET + """Access control list for this asset.""" + + gcs_meta_generation_id: Union[int, None, UnsetType] = UNSET + """Version of metadata for this asset at this generation. Used for preconditions and detecting changes in metadata. A metageneration number is only meaningful in the context of a particular generation of a particular asset.""" + + google_service: Union[str, None, UnsetType] = UNSET + """Service in Google in which the asset exists.""" + + google_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which the asset exists.""" + + google_project_id: Union[str, None, UnsetType] = UNSET + """ID of the project in which the asset exists.""" + + google_project_number: Union[int, None, UnsetType] = UNSET + """Number of the project in which the asset exists.""" + + google_location: Union[str, None, UnsetType] = UNSET + """Location of this asset in Google.""" + + google_location_type: Union[str, None, UnsetType] = UNSET + """Type of location of this asset in Google.""" + + google_labels: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of labels that have been applied to the asset in Google.""" + + google_tags: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of tags that have been applied to the asset in Google.""" + + cloud_uniform_resource_name: Union[str, None, UnsetType] = UNSET + """Uniform resource name (URN) for the asset: AWS ARN, Google Cloud URI, Azure resource ID, Oracle OCID, and so on.""" + + +class GCSBucketRelationshipAttributes(AssetRelationshipAttributes): + """GCSBucket-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + gcs_objects: Union[List[RelatedGCSObject], None, UnsetType] = UNSET + """GCS objects within this bucket.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class GCSBucketNested(AssetNested): + """GCSBucket in nested API format for high-performance serialization.""" + + attributes: Union[GCSBucketAttributes, UnsetType] = UNSET + relationship_attributes: Union[GCSBucketRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + GCSBucketRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + GCSBucketRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_GCS_BUCKET_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "gcs_objects", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_gcs_bucket_attrs(attrs: GCSBucketAttributes, obj: GCSBucket) -> None: + """Populate GCSBucket-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.gcs_object_count = obj.gcs_object_count + attrs.gcs_bucket_versioning_enabled = obj.gcs_bucket_versioning_enabled + attrs.gcs_bucket_retention_locked = obj.gcs_bucket_retention_locked + attrs.gcs_bucket_retention_period = obj.gcs_bucket_retention_period + attrs.gcs_bucket_retention_effective_time = obj.gcs_bucket_retention_effective_time + attrs.gcs_bucket_lifecycle_rules = obj.gcs_bucket_lifecycle_rules + attrs.gcs_bucket_retention_policy = obj.gcs_bucket_retention_policy + attrs.gcs_storage_class = obj.gcs_storage_class + attrs.gcs_encryption_type = obj.gcs_encryption_type + attrs.gcs_etag = obj.gcs_etag + attrs.gcs_requester_pays = obj.gcs_requester_pays + attrs.gcs_access_control = obj.gcs_access_control + attrs.gcs_meta_generation_id = obj.gcs_meta_generation_id + attrs.google_service = obj.google_service + attrs.google_project_name = obj.google_project_name + attrs.google_project_id = obj.google_project_id + attrs.google_project_number = obj.google_project_number + attrs.google_location = obj.google_location + attrs.google_location_type = obj.google_location_type + attrs.google_labels = obj.google_labels + attrs.google_tags = obj.google_tags + attrs.cloud_uniform_resource_name = obj.cloud_uniform_resource_name + + +def _extract_gcs_bucket_attrs(attrs: GCSBucketAttributes) -> dict: + """Extract all GCSBucket attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["gcs_object_count"] = attrs.gcs_object_count + result["gcs_bucket_versioning_enabled"] = attrs.gcs_bucket_versioning_enabled + result["gcs_bucket_retention_locked"] = attrs.gcs_bucket_retention_locked + result["gcs_bucket_retention_period"] = attrs.gcs_bucket_retention_period + result["gcs_bucket_retention_effective_time"] = ( + attrs.gcs_bucket_retention_effective_time + ) + result["gcs_bucket_lifecycle_rules"] = attrs.gcs_bucket_lifecycle_rules + result["gcs_bucket_retention_policy"] = attrs.gcs_bucket_retention_policy + result["gcs_storage_class"] = attrs.gcs_storage_class + result["gcs_encryption_type"] = attrs.gcs_encryption_type + result["gcs_etag"] = attrs.gcs_etag + result["gcs_requester_pays"] = attrs.gcs_requester_pays + result["gcs_access_control"] = attrs.gcs_access_control + result["gcs_meta_generation_id"] = attrs.gcs_meta_generation_id + result["google_service"] = attrs.google_service + result["google_project_name"] = attrs.google_project_name + result["google_project_id"] = attrs.google_project_id + result["google_project_number"] = attrs.google_project_number + result["google_location"] = attrs.google_location + result["google_location_type"] = attrs.google_location_type + result["google_labels"] = attrs.google_labels + result["google_tags"] = attrs.google_tags + result["cloud_uniform_resource_name"] = attrs.cloud_uniform_resource_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _gcs_bucket_to_nested(gcs_bucket: GCSBucket) -> GCSBucketNested: + """Convert flat GCSBucket to nested format.""" + attrs = GCSBucketAttributes() + _populate_gcs_bucket_attrs(attrs, gcs_bucket) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + gcs_bucket, _GCS_BUCKET_REL_FIELDS, GCSBucketRelationshipAttributes + ) + return GCSBucketNested( + guid=gcs_bucket.guid, + type_name=gcs_bucket.type_name, + status=gcs_bucket.status, + version=gcs_bucket.version, + create_time=gcs_bucket.create_time, + update_time=gcs_bucket.update_time, + created_by=gcs_bucket.created_by, + updated_by=gcs_bucket.updated_by, + classifications=gcs_bucket.classifications, + classification_names=gcs_bucket.classification_names, + meanings=gcs_bucket.meanings, + labels=gcs_bucket.labels, + business_attributes=gcs_bucket.business_attributes, + custom_attributes=gcs_bucket.custom_attributes, + pending_tasks=gcs_bucket.pending_tasks, + proxy=gcs_bucket.proxy, + is_incomplete=gcs_bucket.is_incomplete, + provenance_type=gcs_bucket.provenance_type, + home_id=gcs_bucket.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _gcs_bucket_from_nested(nested: GCSBucketNested) -> GCSBucket: + """Convert nested format to flat GCSBucket.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else GCSBucketAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _GCS_BUCKET_REL_FIELDS, + GCSBucketRelationshipAttributes, + ) + return GCSBucket( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_gcs_bucket_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _gcs_bucket_to_nested_bytes(gcs_bucket: GCSBucket, serde: Serde) -> bytes: + """Convert flat GCSBucket to nested JSON bytes.""" + return serde.encode(_gcs_bucket_to_nested(gcs_bucket)) + + +def _gcs_bucket_from_nested_bytes(data: bytes, serde: Serde) -> GCSBucket: + """Convert nested JSON bytes to flat GCSBucket.""" + nested = serde.decode(data, GCSBucketNested) + return _gcs_bucket_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +GCSBucket.GCS_OBJECT_COUNT = NumericField("gcsObjectCount", "gcsObjectCount") +GCSBucket.GCS_BUCKET_VERSIONING_ENABLED = BooleanField( + "gcsBucketVersioningEnabled", "gcsBucketVersioningEnabled" +) +GCSBucket.GCS_BUCKET_RETENTION_LOCKED = BooleanField( + "gcsBucketRetentionLocked", "gcsBucketRetentionLocked" +) +GCSBucket.GCS_BUCKET_RETENTION_PERIOD = NumericField( + "gcsBucketRetentionPeriod", "gcsBucketRetentionPeriod" +) +GCSBucket.GCS_BUCKET_RETENTION_EFFECTIVE_TIME = NumericField( + "gcsBucketRetentionEffectiveTime", "gcsBucketRetentionEffectiveTime" +) +GCSBucket.GCS_BUCKET_LIFECYCLE_RULES = KeywordField( + "gcsBucketLifecycleRules", "gcsBucketLifecycleRules" +) +GCSBucket.GCS_BUCKET_RETENTION_POLICY = KeywordField( + "gcsBucketRetentionPolicy", "gcsBucketRetentionPolicy" +) +GCSBucket.GCS_STORAGE_CLASS = KeywordField("gcsStorageClass", "gcsStorageClass") +GCSBucket.GCS_ENCRYPTION_TYPE = KeywordField("gcsEncryptionType", "gcsEncryptionType") +GCSBucket.GCS_ETAG = KeywordField("gcsETag", "gcsETag") +GCSBucket.GCS_REQUESTER_PAYS = BooleanField("gcsRequesterPays", "gcsRequesterPays") +GCSBucket.GCS_ACCESS_CONTROL = KeywordField("gcsAccessControl", "gcsAccessControl") +GCSBucket.GCS_META_GENERATION_ID = NumericField( + "gcsMetaGenerationId", "gcsMetaGenerationId" +) +GCSBucket.GOOGLE_SERVICE = KeywordField("googleService", "googleService") +GCSBucket.GOOGLE_PROJECT_NAME = KeywordTextField( + "googleProjectName", "googleProjectName", "googleProjectName.text" +) +GCSBucket.GOOGLE_PROJECT_ID = KeywordTextField( + "googleProjectId", "googleProjectId", "googleProjectId.text" +) +GCSBucket.GOOGLE_PROJECT_NUMBER = NumericField( + "googleProjectNumber", "googleProjectNumber" +) +GCSBucket.GOOGLE_LOCATION = KeywordField("googleLocation", "googleLocation") +GCSBucket.GOOGLE_LOCATION_TYPE = KeywordField( + "googleLocationType", "googleLocationType" +) +GCSBucket.GOOGLE_LABELS = KeywordField("googleLabels", "googleLabels") +GCSBucket.GOOGLE_TAGS = KeywordField("googleTags", "googleTags") +GCSBucket.CLOUD_UNIFORM_RESOURCE_NAME = KeywordField( + "cloudUniformResourceName", "cloudUniformResourceName" +) +GCSBucket.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +GCSBucket.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +GCSBucket.ANOMALO_CHECKS = RelationField("anomaloChecks") +GCSBucket.APPLICATION = RelationField("application") +GCSBucket.APPLICATION_FIELD = RelationField("applicationField") +GCSBucket.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +GCSBucket.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +GCSBucket.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +GCSBucket.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +GCSBucket.METRICS = RelationField("metrics") +GCSBucket.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +GCSBucket.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +GCSBucket.GCS_OBJECTS = RelationField("gcsObjects") +GCSBucket.MEANINGS = RelationField("meanings") +GCSBucket.MC_MONITORS = RelationField("mcMonitors") +GCSBucket.MC_INCIDENTS = RelationField("mcIncidents") +GCSBucket.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +GCSBucket.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +GCSBucket.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +GCSBucket.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +GCSBucket.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +GCSBucket.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +GCSBucket.FILES = RelationField("files") +GCSBucket.LINKS = RelationField("links") +GCSBucket.README = RelationField("readme") +GCSBucket.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +GCSBucket.SODA_CHECKS = RelationField("sodaChecks") +GCSBucket.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +GCSBucket.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/gcs_object.py b/pyatlan_v9/model/assets/gcs_object.py new file mode 100644 index 000000000..6999b5f8b --- /dev/null +++ b/pyatlan_v9/model/assets/gcs_object.py @@ -0,0 +1,1035 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +GCSObject asset model with flattened inheritance. + +This module provides: +- GCSObject: Flat asset class (easy to use) +- GCSObjectAttributes: Nested attributes struct (extends AssetAttributes) +- GCSObjectNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan.model.utils import construct_object_key +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .gcs_related import RelatedGCSBucket + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class GCSObject(Asset): + """ + Instance of a Google Cloud Storage object in Atlan. + """ + + GCS_BUCKET_NAME: ClassVar[Any] = None + GCS_BUCKET_QUALIFIED_NAME: ClassVar[Any] = None + GCS_OBJECT_SIZE: ClassVar[Any] = None + GCS_OBJECT_KEY: ClassVar[Any] = None + GCS_OBJECT_MEDIA_LINK: ClassVar[Any] = None + GCS_OBJECT_HOLD_TYPE: ClassVar[Any] = None + GCS_OBJECT_GENERATION_ID: ClassVar[Any] = None + GCS_OBJECT_CRC32C_HASH: ClassVar[Any] = None + GCS_OBJECT_MD5_HASH: ClassVar[Any] = None + GCS_OBJECT_DATA_LAST_MODIFIED_TIME: ClassVar[Any] = None + GCS_OBJECT_CONTENT_TYPE: ClassVar[Any] = None + GCS_OBJECT_CONTENT_ENCODING: ClassVar[Any] = None + GCS_OBJECT_CONTENT_DISPOSITION: ClassVar[Any] = None + GCS_OBJECT_CONTENT_LANGUAGE: ClassVar[Any] = None + GCS_OBJECT_RETENTION_EXPIRATION_DATE: ClassVar[Any] = None + GCS_STORAGE_CLASS: ClassVar[Any] = None + GCS_ENCRYPTION_TYPE: ClassVar[Any] = None + GCS_ETAG: ClassVar[Any] = None + GCS_REQUESTER_PAYS: ClassVar[Any] = None + GCS_ACCESS_CONTROL: ClassVar[Any] = None + GCS_META_GENERATION_ID: ClassVar[Any] = None + GOOGLE_SERVICE: ClassVar[Any] = None + GOOGLE_PROJECT_NAME: ClassVar[Any] = None + GOOGLE_PROJECT_ID: ClassVar[Any] = None + GOOGLE_PROJECT_NUMBER: ClassVar[Any] = None + GOOGLE_LOCATION: ClassVar[Any] = None + GOOGLE_LOCATION_TYPE: ClassVar[Any] = None + GOOGLE_LABELS: ClassVar[Any] = None + GOOGLE_TAGS: ClassVar[Any] = None + CLOUD_UNIFORM_RESOURCE_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + GCS_BUCKET: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "GCSObject" + + gcs_bucket_name: Union[str, None, UnsetType] = UNSET + """Simple name of the bucket in which this object exists.""" + + gcs_bucket_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the bucket in which this object exists.""" + + gcs_object_size: Union[int, None, UnsetType] = UNSET + """Object size in bytes.""" + + gcs_object_key: Union[str, None, UnsetType] = UNSET + """Key of this object, in GCS.""" + + gcs_object_media_link: Union[str, None, UnsetType] = UNSET + """Media link to this object.""" + + gcs_object_hold_type: Union[str, None, UnsetType] = UNSET + """Type of hold on this object.""" + + gcs_object_generation_id: Union[int, None, UnsetType] = UNSET + """Generation ID of this object.""" + + gcs_object_crc32c_hash: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="gcsObjectCRC32CHash" + ) + """CRC32C hash of this object.""" + + gcs_object_md5_hash: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="gcsObjectMD5Hash" + ) + """MD5 hash of this object.""" + + gcs_object_data_last_modified_time: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this object's data was last modified, in milliseconds.""" + + gcs_object_content_type: Union[str, None, UnsetType] = UNSET + """Type of content in this object.""" + + gcs_object_content_encoding: Union[str, None, UnsetType] = UNSET + """Content encoding of this object.""" + + gcs_object_content_disposition: Union[str, None, UnsetType] = UNSET + """Information about how this object's content should be presented.""" + + gcs_object_content_language: Union[str, None, UnsetType] = UNSET + """Language of this object's contents.""" + + gcs_object_retention_expiration_date: Union[int, None, UnsetType] = UNSET + """Retention expiration date of this object.""" + + gcs_storage_class: Union[str, None, UnsetType] = UNSET + """Storage class of this asset.""" + + gcs_encryption_type: Union[str, None, UnsetType] = UNSET + """Encryption algorithm used to encrypt this asset.""" + + gcs_etag: Union[str, None, UnsetType] = msgspec.field(default=UNSET, name="gcsETag") + """Entity tag for the asset. An entity tag is a hash of the object and represents changes to the contents of an object only, not its metadata.""" + + gcs_requester_pays: Union[bool, None, UnsetType] = UNSET + """Whether the requester pays header was sent when this asset was created (true) or not (false).""" + + gcs_access_control: Union[str, None, UnsetType] = UNSET + """Access control list for this asset.""" + + gcs_meta_generation_id: Union[int, None, UnsetType] = UNSET + """Version of metadata for this asset at this generation. Used for preconditions and detecting changes in metadata. A metageneration number is only meaningful in the context of a particular generation of a particular asset.""" + + google_service: Union[str, None, UnsetType] = UNSET + """Service in Google in which the asset exists.""" + + google_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which the asset exists.""" + + google_project_id: Union[str, None, UnsetType] = UNSET + """ID of the project in which the asset exists.""" + + google_project_number: Union[int, None, UnsetType] = UNSET + """Number of the project in which the asset exists.""" + + google_location: Union[str, None, UnsetType] = UNSET + """Location of this asset in Google.""" + + google_location_type: Union[str, None, UnsetType] = UNSET + """Type of location of this asset in Google.""" + + google_labels: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of labels that have been applied to the asset in Google.""" + + google_tags: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of tags that have been applied to the asset in Google.""" + + cloud_uniform_resource_name: Union[str, None, UnsetType] = UNSET + """Uniform resource name (URN) for the asset: AWS ARN, Google Cloud URI, Azure resource ID, Oracle OCID, and so on.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + gcs_bucket: Union[RelatedGCSBucket, None, UnsetType] = UNSET + """GCS bucket in which the object exists.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "GCSObject" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + gcs_bucket_name: str, + gcs_bucket_qualified_name: str, + connection_qualified_name: str | None = None, + ) -> "GCSObject": + """ + Create a new GCSObject asset. + + Args: + name: Name of the object + gcs_bucket_name: Simple name of the bucket + gcs_bucket_qualified_name: Unique name of the bucket + connection_qualified_name: Unique name of the connection (optional, + derived from gcs_bucket_qualified_name if not provided) + + Returns: + GCSObject instance ready to be created + + Raises: + ValueError: If required parameters are missing + """ + validate_required_fields( + ["name", "gcs_bucket_name", "gcs_bucket_qualified_name"], + [name, gcs_bucket_name, gcs_bucket_qualified_name], + ) + if connection_qualified_name: + connector_name = connection_qualified_name.split("/")[1] + else: + # Derive connection_qualified_name from gcs_bucket_qualified_name + # gcs_bucket_qualified_name format: "default/gcs/123456789/mybucket" + parts = gcs_bucket_qualified_name.split("/") + connection_qualified_name = "/".join(parts[:3]) + connector_name = parts[1] + + return cls( + name=name, + connection_qualified_name=connection_qualified_name, + qualified_name=f"{gcs_bucket_qualified_name}/{name}", + connector_name=connector_name, + gcs_bucket_name=gcs_bucket_name, + gcs_bucket_qualified_name=gcs_bucket_qualified_name, + ) + + @classmethod + @init_guid + def creator_with_prefix( + cls, + *, + name: str, + connection_qualified_name: str, + gcs_bucket_name: str, + gcs_bucket_qualified_name: str, + prefix: str = "", + ) -> "GCSObject": + """ + Create a new GCSObject asset using a prefix-based object key. + + Args: + name: Name of the object + connection_qualified_name: Unique name of the connection + gcs_bucket_name: Simple name of the bucket + gcs_bucket_qualified_name: Unique name of the bucket + prefix: Prefix (folder path) for the object + + Returns: + GCSObject instance ready to be created + + Raises: + ValueError: If required parameters are missing or invalid + """ + validate_required_fields( + [ + "name", + "connection_qualified_name", + "gcs_bucket_name", + "gcs_bucket_qualified_name", + ], + [ + name, + connection_qualified_name, + gcs_bucket_name, + gcs_bucket_qualified_name, + ], + ) + fields = connection_qualified_name.split("/") + if len(fields) != 3: + raise ValueError("Invalid connection_qualified_name") + if fields[0].replace(" ", "") == "" or fields[2].replace(" ", "") == "": + raise ValueError("Invalid connection_qualified_name") + if fields[1].lower() != "gcs": + raise ValueError("Invalid connection_qualified_name") + + connector_name = fields[1] + object_key = construct_object_key(prefix, name) + return cls( + name=name, + gcs_object_key=object_key, + connection_qualified_name=connection_qualified_name, + qualified_name=f"{gcs_bucket_qualified_name}/{object_key}", + connector_name=connector_name, + gcs_bucket_name=gcs_bucket_name, + gcs_bucket_qualified_name=gcs_bucket_qualified_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "GCSObject": + """ + Create a GCSObject instance for modification. + + Args: + qualified_name: Unique name of the GCSObject to update + name: Human-readable name of the GCSObject + + Returns: + GCSObject instance ready for update + + Raises: + ValueError: If required parameters are missing + """ + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "GCSObject": + """ + Return a copy of this GCSObject with only the minimum required fields for update. + + Returns: + GCSObject with only qualified_name and name set + """ + return GCSObject.updater(qualified_name=self.qualified_name, name=self.name) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _gcs_object_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> GCSObject: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + GCSObject instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _gcs_object_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class GCSObjectAttributes(AssetAttributes): + """GCSObject-specific attributes for nested API format.""" + + gcs_bucket_name: Union[str, None, UnsetType] = UNSET + """Simple name of the bucket in which this object exists.""" + + gcs_bucket_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the bucket in which this object exists.""" + + gcs_object_size: Union[int, None, UnsetType] = UNSET + """Object size in bytes.""" + + gcs_object_key: Union[str, None, UnsetType] = UNSET + """Key of this object, in GCS.""" + + gcs_object_media_link: Union[str, None, UnsetType] = UNSET + """Media link to this object.""" + + gcs_object_hold_type: Union[str, None, UnsetType] = UNSET + """Type of hold on this object.""" + + gcs_object_generation_id: Union[int, None, UnsetType] = UNSET + """Generation ID of this object.""" + + gcs_object_crc32c_hash: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="gcsObjectCRC32CHash" + ) + """CRC32C hash of this object.""" + + gcs_object_md5_hash: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="gcsObjectMD5Hash" + ) + """MD5 hash of this object.""" + + gcs_object_data_last_modified_time: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this object's data was last modified, in milliseconds.""" + + gcs_object_content_type: Union[str, None, UnsetType] = UNSET + """Type of content in this object.""" + + gcs_object_content_encoding: Union[str, None, UnsetType] = UNSET + """Content encoding of this object.""" + + gcs_object_content_disposition: Union[str, None, UnsetType] = UNSET + """Information about how this object's content should be presented.""" + + gcs_object_content_language: Union[str, None, UnsetType] = UNSET + """Language of this object's contents.""" + + gcs_object_retention_expiration_date: Union[int, None, UnsetType] = UNSET + """Retention expiration date of this object.""" + + gcs_storage_class: Union[str, None, UnsetType] = UNSET + """Storage class of this asset.""" + + gcs_encryption_type: Union[str, None, UnsetType] = UNSET + """Encryption algorithm used to encrypt this asset.""" + + gcs_etag: Union[str, None, UnsetType] = msgspec.field(default=UNSET, name="gcsETag") + """Entity tag for the asset. An entity tag is a hash of the object and represents changes to the contents of an object only, not its metadata.""" + + gcs_requester_pays: Union[bool, None, UnsetType] = UNSET + """Whether the requester pays header was sent when this asset was created (true) or not (false).""" + + gcs_access_control: Union[str, None, UnsetType] = UNSET + """Access control list for this asset.""" + + gcs_meta_generation_id: Union[int, None, UnsetType] = UNSET + """Version of metadata for this asset at this generation. Used for preconditions and detecting changes in metadata. A metageneration number is only meaningful in the context of a particular generation of a particular asset.""" + + google_service: Union[str, None, UnsetType] = UNSET + """Service in Google in which the asset exists.""" + + google_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which the asset exists.""" + + google_project_id: Union[str, None, UnsetType] = UNSET + """ID of the project in which the asset exists.""" + + google_project_number: Union[int, None, UnsetType] = UNSET + """Number of the project in which the asset exists.""" + + google_location: Union[str, None, UnsetType] = UNSET + """Location of this asset in Google.""" + + google_location_type: Union[str, None, UnsetType] = UNSET + """Type of location of this asset in Google.""" + + google_labels: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of labels that have been applied to the asset in Google.""" + + google_tags: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of tags that have been applied to the asset in Google.""" + + cloud_uniform_resource_name: Union[str, None, UnsetType] = UNSET + """Uniform resource name (URN) for the asset: AWS ARN, Google Cloud URI, Azure resource ID, Oracle OCID, and so on.""" + + +class GCSObjectRelationshipAttributes(AssetRelationshipAttributes): + """GCSObject-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + gcs_bucket: Union[RelatedGCSBucket, None, UnsetType] = UNSET + """GCS bucket in which the object exists.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class GCSObjectNested(AssetNested): + """GCSObject in nested API format for high-performance serialization.""" + + attributes: Union[GCSObjectAttributes, UnsetType] = UNSET + relationship_attributes: Union[GCSObjectRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + GCSObjectRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + GCSObjectRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_GCS_OBJECT_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "gcs_bucket", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_gcs_object_attrs(attrs: GCSObjectAttributes, obj: GCSObject) -> None: + """Populate GCSObject-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.gcs_bucket_name = obj.gcs_bucket_name + attrs.gcs_bucket_qualified_name = obj.gcs_bucket_qualified_name + attrs.gcs_object_size = obj.gcs_object_size + attrs.gcs_object_key = obj.gcs_object_key + attrs.gcs_object_media_link = obj.gcs_object_media_link + attrs.gcs_object_hold_type = obj.gcs_object_hold_type + attrs.gcs_object_generation_id = obj.gcs_object_generation_id + attrs.gcs_object_crc32c_hash = obj.gcs_object_crc32c_hash + attrs.gcs_object_md5_hash = obj.gcs_object_md5_hash + attrs.gcs_object_data_last_modified_time = obj.gcs_object_data_last_modified_time + attrs.gcs_object_content_type = obj.gcs_object_content_type + attrs.gcs_object_content_encoding = obj.gcs_object_content_encoding + attrs.gcs_object_content_disposition = obj.gcs_object_content_disposition + attrs.gcs_object_content_language = obj.gcs_object_content_language + attrs.gcs_object_retention_expiration_date = ( + obj.gcs_object_retention_expiration_date + ) + attrs.gcs_storage_class = obj.gcs_storage_class + attrs.gcs_encryption_type = obj.gcs_encryption_type + attrs.gcs_etag = obj.gcs_etag + attrs.gcs_requester_pays = obj.gcs_requester_pays + attrs.gcs_access_control = obj.gcs_access_control + attrs.gcs_meta_generation_id = obj.gcs_meta_generation_id + attrs.google_service = obj.google_service + attrs.google_project_name = obj.google_project_name + attrs.google_project_id = obj.google_project_id + attrs.google_project_number = obj.google_project_number + attrs.google_location = obj.google_location + attrs.google_location_type = obj.google_location_type + attrs.google_labels = obj.google_labels + attrs.google_tags = obj.google_tags + attrs.cloud_uniform_resource_name = obj.cloud_uniform_resource_name + + +def _extract_gcs_object_attrs(attrs: GCSObjectAttributes) -> dict: + """Extract all GCSObject attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["gcs_bucket_name"] = attrs.gcs_bucket_name + result["gcs_bucket_qualified_name"] = attrs.gcs_bucket_qualified_name + result["gcs_object_size"] = attrs.gcs_object_size + result["gcs_object_key"] = attrs.gcs_object_key + result["gcs_object_media_link"] = attrs.gcs_object_media_link + result["gcs_object_hold_type"] = attrs.gcs_object_hold_type + result["gcs_object_generation_id"] = attrs.gcs_object_generation_id + result["gcs_object_crc32c_hash"] = attrs.gcs_object_crc32c_hash + result["gcs_object_md5_hash"] = attrs.gcs_object_md5_hash + result["gcs_object_data_last_modified_time"] = ( + attrs.gcs_object_data_last_modified_time + ) + result["gcs_object_content_type"] = attrs.gcs_object_content_type + result["gcs_object_content_encoding"] = attrs.gcs_object_content_encoding + result["gcs_object_content_disposition"] = attrs.gcs_object_content_disposition + result["gcs_object_content_language"] = attrs.gcs_object_content_language + result["gcs_object_retention_expiration_date"] = ( + attrs.gcs_object_retention_expiration_date + ) + result["gcs_storage_class"] = attrs.gcs_storage_class + result["gcs_encryption_type"] = attrs.gcs_encryption_type + result["gcs_etag"] = attrs.gcs_etag + result["gcs_requester_pays"] = attrs.gcs_requester_pays + result["gcs_access_control"] = attrs.gcs_access_control + result["gcs_meta_generation_id"] = attrs.gcs_meta_generation_id + result["google_service"] = attrs.google_service + result["google_project_name"] = attrs.google_project_name + result["google_project_id"] = attrs.google_project_id + result["google_project_number"] = attrs.google_project_number + result["google_location"] = attrs.google_location + result["google_location_type"] = attrs.google_location_type + result["google_labels"] = attrs.google_labels + result["google_tags"] = attrs.google_tags + result["cloud_uniform_resource_name"] = attrs.cloud_uniform_resource_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _gcs_object_to_nested(gcs_object: GCSObject) -> GCSObjectNested: + """Convert flat GCSObject to nested format.""" + attrs = GCSObjectAttributes() + _populate_gcs_object_attrs(attrs, gcs_object) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + gcs_object, _GCS_OBJECT_REL_FIELDS, GCSObjectRelationshipAttributes + ) + return GCSObjectNested( + guid=gcs_object.guid, + type_name=gcs_object.type_name, + status=gcs_object.status, + version=gcs_object.version, + create_time=gcs_object.create_time, + update_time=gcs_object.update_time, + created_by=gcs_object.created_by, + updated_by=gcs_object.updated_by, + classifications=gcs_object.classifications, + classification_names=gcs_object.classification_names, + meanings=gcs_object.meanings, + labels=gcs_object.labels, + business_attributes=gcs_object.business_attributes, + custom_attributes=gcs_object.custom_attributes, + pending_tasks=gcs_object.pending_tasks, + proxy=gcs_object.proxy, + is_incomplete=gcs_object.is_incomplete, + provenance_type=gcs_object.provenance_type, + home_id=gcs_object.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _gcs_object_from_nested(nested: GCSObjectNested) -> GCSObject: + """Convert nested format to flat GCSObject.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else GCSObjectAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _GCS_OBJECT_REL_FIELDS, + GCSObjectRelationshipAttributes, + ) + return GCSObject( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_gcs_object_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _gcs_object_to_nested_bytes(gcs_object: GCSObject, serde: Serde) -> bytes: + """Convert flat GCSObject to nested JSON bytes.""" + return serde.encode(_gcs_object_to_nested(gcs_object)) + + +def _gcs_object_from_nested_bytes(data: bytes, serde: Serde) -> GCSObject: + """Convert nested JSON bytes to flat GCSObject.""" + nested = serde.decode(data, GCSObjectNested) + return _gcs_object_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +GCSObject.GCS_BUCKET_NAME = KeywordField("gcsBucketName", "gcsBucketName") +GCSObject.GCS_BUCKET_QUALIFIED_NAME = KeywordTextField( + "gcsBucketQualifiedName", "gcsBucketQualifiedName", "gcsBucketQualifiedName.text" +) +GCSObject.GCS_OBJECT_SIZE = NumericField("gcsObjectSize", "gcsObjectSize") +GCSObject.GCS_OBJECT_KEY = KeywordTextField( + "gcsObjectKey", "gcsObjectKey", "gcsObjectKey.text" +) +GCSObject.GCS_OBJECT_MEDIA_LINK = KeywordTextField( + "gcsObjectMediaLink", "gcsObjectMediaLink", "gcsObjectMediaLink.text" +) +GCSObject.GCS_OBJECT_HOLD_TYPE = KeywordField("gcsObjectHoldType", "gcsObjectHoldType") +GCSObject.GCS_OBJECT_GENERATION_ID = NumericField( + "gcsObjectGenerationId", "gcsObjectGenerationId" +) +GCSObject.GCS_OBJECT_CRC32C_HASH = KeywordField( + "gcsObjectCRC32CHash", "gcsObjectCRC32CHash" +) +GCSObject.GCS_OBJECT_MD5_HASH = KeywordField("gcsObjectMD5Hash", "gcsObjectMD5Hash") +GCSObject.GCS_OBJECT_DATA_LAST_MODIFIED_TIME = NumericField( + "gcsObjectDataLastModifiedTime", "gcsObjectDataLastModifiedTime" +) +GCSObject.GCS_OBJECT_CONTENT_TYPE = KeywordField( + "gcsObjectContentType", "gcsObjectContentType" +) +GCSObject.GCS_OBJECT_CONTENT_ENCODING = KeywordField( + "gcsObjectContentEncoding", "gcsObjectContentEncoding" +) +GCSObject.GCS_OBJECT_CONTENT_DISPOSITION = KeywordField( + "gcsObjectContentDisposition", "gcsObjectContentDisposition" +) +GCSObject.GCS_OBJECT_CONTENT_LANGUAGE = KeywordField( + "gcsObjectContentLanguage", "gcsObjectContentLanguage" +) +GCSObject.GCS_OBJECT_RETENTION_EXPIRATION_DATE = NumericField( + "gcsObjectRetentionExpirationDate", "gcsObjectRetentionExpirationDate" +) +GCSObject.GCS_STORAGE_CLASS = KeywordField("gcsStorageClass", "gcsStorageClass") +GCSObject.GCS_ENCRYPTION_TYPE = KeywordField("gcsEncryptionType", "gcsEncryptionType") +GCSObject.GCS_ETAG = KeywordField("gcsETag", "gcsETag") +GCSObject.GCS_REQUESTER_PAYS = BooleanField("gcsRequesterPays", "gcsRequesterPays") +GCSObject.GCS_ACCESS_CONTROL = KeywordField("gcsAccessControl", "gcsAccessControl") +GCSObject.GCS_META_GENERATION_ID = NumericField( + "gcsMetaGenerationId", "gcsMetaGenerationId" +) +GCSObject.GOOGLE_SERVICE = KeywordField("googleService", "googleService") +GCSObject.GOOGLE_PROJECT_NAME = KeywordTextField( + "googleProjectName", "googleProjectName", "googleProjectName.text" +) +GCSObject.GOOGLE_PROJECT_ID = KeywordTextField( + "googleProjectId", "googleProjectId", "googleProjectId.text" +) +GCSObject.GOOGLE_PROJECT_NUMBER = NumericField( + "googleProjectNumber", "googleProjectNumber" +) +GCSObject.GOOGLE_LOCATION = KeywordField("googleLocation", "googleLocation") +GCSObject.GOOGLE_LOCATION_TYPE = KeywordField( + "googleLocationType", "googleLocationType" +) +GCSObject.GOOGLE_LABELS = KeywordField("googleLabels", "googleLabels") +GCSObject.GOOGLE_TAGS = KeywordField("googleTags", "googleTags") +GCSObject.CLOUD_UNIFORM_RESOURCE_NAME = KeywordField( + "cloudUniformResourceName", "cloudUniformResourceName" +) +GCSObject.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +GCSObject.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +GCSObject.ANOMALO_CHECKS = RelationField("anomaloChecks") +GCSObject.APPLICATION = RelationField("application") +GCSObject.APPLICATION_FIELD = RelationField("applicationField") +GCSObject.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +GCSObject.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +GCSObject.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +GCSObject.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +GCSObject.METRICS = RelationField("metrics") +GCSObject.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +GCSObject.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +GCSObject.GCS_BUCKET = RelationField("gcsBucket") +GCSObject.MEANINGS = RelationField("meanings") +GCSObject.MC_MONITORS = RelationField("mcMonitors") +GCSObject.MC_INCIDENTS = RelationField("mcIncidents") +GCSObject.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +GCSObject.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +GCSObject.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +GCSObject.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +GCSObject.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +GCSObject.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +GCSObject.FILES = RelationField("files") +GCSObject.LINKS = RelationField("links") +GCSObject.README = RelationField("readme") +GCSObject.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +GCSObject.SODA_CHECKS = RelationField("sodaChecks") +GCSObject.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +GCSObject.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/gcs_related.py b/pyatlan_v9/model/assets/gcs_related.py new file mode 100644 index 000000000..a7756dfad --- /dev/null +++ b/pyatlan_v9/model/assets/gcs_related.py @@ -0,0 +1,159 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for GCS module. + +This module contains all Related{Type} classes for the GCS type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedObjectStore +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedGCS", + "RelatedGCSBucket", + "RelatedGCSObject", +] + + +class RelatedGCS(RelatedObjectStore): + """ + Related entity reference for GCS assets. + + Extends RelatedObjectStore with GCS-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "GCS" so it serializes correctly + + gcs_storage_class: Union[str, None, UnsetType] = UNSET + """Storage class of this asset.""" + + gcs_encryption_type: Union[str, None, UnsetType] = UNSET + """Encryption algorithm used to encrypt this asset.""" + + gcs_etag: Union[str, None, UnsetType] = msgspec.field(default=UNSET, name="gcsETag") + """Entity tag for the asset. An entity tag is a hash of the object and represents changes to the contents of an object only, not its metadata.""" + + gcs_requester_pays: Union[bool, None, UnsetType] = UNSET + """Whether the requester pays header was sent when this asset was created (true) or not (false).""" + + gcs_access_control: Union[str, None, UnsetType] = UNSET + """Access control list for this asset.""" + + gcs_meta_generation_id: Union[int, None, UnsetType] = UNSET + """Version of metadata for this asset at this generation. Used for preconditions and detecting changes in metadata. A metageneration number is only meaningful in the context of a particular generation of a particular asset.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "GCS" + + +class RelatedGCSBucket(RelatedGCS): + """ + Related entity reference for GCSBucket assets. + + Extends RelatedGCS with GCSBucket-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "GCSBucket" so it serializes correctly + + gcs_object_count: Union[int, None, UnsetType] = UNSET + """Number of objects within the bucket.""" + + gcs_bucket_versioning_enabled: Union[bool, None, UnsetType] = UNSET + """Whether versioning is enabled on the bucket (true) or not (false).""" + + gcs_bucket_retention_locked: Union[bool, None, UnsetType] = UNSET + """Whether retention is locked for this bucket (true) or not (false).""" + + gcs_bucket_retention_period: Union[int, None, UnsetType] = UNSET + """Retention period for objects in this bucket.""" + + gcs_bucket_retention_effective_time: Union[int, None, UnsetType] = UNSET + """Effective time for retention of objects in this bucket.""" + + gcs_bucket_lifecycle_rules: Union[str, None, UnsetType] = UNSET + """Lifecycle rules for this bucket.""" + + gcs_bucket_retention_policy: Union[str, None, UnsetType] = UNSET + """Retention policy for this bucket.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "GCSBucket" + + +class RelatedGCSObject(RelatedGCS): + """ + Related entity reference for GCSObject assets. + + Extends RelatedGCS with GCSObject-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "GCSObject" so it serializes correctly + + gcs_bucket_name: Union[str, None, UnsetType] = UNSET + """Simple name of the bucket in which this object exists.""" + + gcs_bucket_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the bucket in which this object exists.""" + + gcs_object_size: Union[int, None, UnsetType] = UNSET + """Object size in bytes.""" + + gcs_object_key: Union[str, None, UnsetType] = UNSET + """Key of this object, in GCS.""" + + gcs_object_media_link: Union[str, None, UnsetType] = UNSET + """Media link to this object.""" + + gcs_object_hold_type: Union[str, None, UnsetType] = UNSET + """Type of hold on this object.""" + + gcs_object_generation_id: Union[int, None, UnsetType] = UNSET + """Generation ID of this object.""" + + gcs_object_crc32c_hash: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="gcsObjectCRC32CHash" + ) + """CRC32C hash of this object.""" + + gcs_object_md5_hash: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="gcsObjectMD5Hash" + ) + """MD5 hash of this object.""" + + gcs_object_data_last_modified_time: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this object's data was last modified, in milliseconds.""" + + gcs_object_content_type: Union[str, None, UnsetType] = UNSET + """Type of content in this object.""" + + gcs_object_content_encoding: Union[str, None, UnsetType] = UNSET + """Content encoding of this object.""" + + gcs_object_content_disposition: Union[str, None, UnsetType] = UNSET + """Information about how this object's content should be presented.""" + + gcs_object_content_language: Union[str, None, UnsetType] = UNSET + """Language of this object's contents.""" + + gcs_object_retention_expiration_date: Union[int, None, UnsetType] = UNSET + """Retention expiration date of this object.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "GCSObject" diff --git a/pyatlan_v9/model/assets/google.py b/pyatlan_v9/model/assets/google.py new file mode 100644 index 000000000..0956c5307 --- /dev/null +++ b/pyatlan_v9/model/assets/google.py @@ -0,0 +1,524 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Google asset model with flattened inheritance. + +This module provides: +- Google: Flat asset class (easy to use) +- GoogleAttributes: Nested attributes struct (extends AssetAttributes) +- GoogleNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Google(Asset): + """ + Base class for Google assets. + """ + + GOOGLE_SERVICE: ClassVar[Any] = None + GOOGLE_PROJECT_NAME: ClassVar[Any] = None + GOOGLE_PROJECT_ID: ClassVar[Any] = None + CLOUD_PROJECT_NUMBER: ClassVar[Any] = None + GOOGLE_LOCATION: ClassVar[Any] = None + GOOGLE_LOCATION_TYPE: ClassVar[Any] = None + GOOGLE_LABELS: ClassVar[Any] = None + GOOGLE_TAGS: ClassVar[Any] = None + CLOUD_UNIFORM_RESOURCE_NAME: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Google" + + google_service: Union[str, None, UnsetType] = UNSET + """Service in Google in which the asset exists.""" + + google_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which the asset exists.""" + + google_project_id: Union[str, None, UnsetType] = UNSET + """ID of the project in which the asset exists.""" + + cloud_project_number: Union[int, None, UnsetType] = UNSET + """Number of the project in which the asset exists.""" + + google_location: Union[str, None, UnsetType] = UNSET + """Location of this asset in Google.""" + + google_location_type: Union[str, None, UnsetType] = UNSET + """Type of location of this asset in Google.""" + + google_labels: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of labels that have been applied to the asset in Google.""" + + google_tags: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of tags that have been applied to the asset in Google.""" + + cloud_uniform_resource_name: Union[str, None, UnsetType] = UNSET + """Uniform resource name (URN) for the asset: AWS ARN, Google Cloud URI, Azure resource ID, Oracle OCID, and so on.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Google" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _google_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Google: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Google instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _google_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class GoogleAttributes(AssetAttributes): + """Google-specific attributes for nested API format.""" + + google_service: Union[str, None, UnsetType] = UNSET + """Service in Google in which the asset exists.""" + + google_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which the asset exists.""" + + google_project_id: Union[str, None, UnsetType] = UNSET + """ID of the project in which the asset exists.""" + + cloud_project_number: Union[int, None, UnsetType] = UNSET + """Number of the project in which the asset exists.""" + + google_location: Union[str, None, UnsetType] = UNSET + """Location of this asset in Google.""" + + google_location_type: Union[str, None, UnsetType] = UNSET + """Type of location of this asset in Google.""" + + google_labels: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of labels that have been applied to the asset in Google.""" + + google_tags: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of tags that have been applied to the asset in Google.""" + + cloud_uniform_resource_name: Union[str, None, UnsetType] = UNSET + """Uniform resource name (URN) for the asset: AWS ARN, Google Cloud URI, Azure resource ID, Oracle OCID, and so on.""" + + +class GoogleRelationshipAttributes(AssetRelationshipAttributes): + """Google-specific relationship attributes for nested API format.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + +class GoogleNested(AssetNested): + """Google in nested API format for high-performance serialization.""" + + attributes: Union[GoogleAttributes, UnsetType] = UNSET + relationship_attributes: Union[GoogleRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[GoogleRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[GoogleRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_GOOGLE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", +] + + +def _populate_google_attrs(attrs: GoogleAttributes, obj: Google) -> None: + """Populate Google-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.google_service = obj.google_service + attrs.google_project_name = obj.google_project_name + attrs.google_project_id = obj.google_project_id + attrs.cloud_project_number = obj.cloud_project_number + attrs.google_location = obj.google_location + attrs.google_location_type = obj.google_location_type + attrs.google_labels = obj.google_labels + attrs.google_tags = obj.google_tags + attrs.cloud_uniform_resource_name = obj.cloud_uniform_resource_name + + +def _extract_google_attrs(attrs: GoogleAttributes) -> dict: + """Extract all Google attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["google_service"] = attrs.google_service + result["google_project_name"] = attrs.google_project_name + result["google_project_id"] = attrs.google_project_id + result["cloud_project_number"] = attrs.cloud_project_number + result["google_location"] = attrs.google_location + result["google_location_type"] = attrs.google_location_type + result["google_labels"] = attrs.google_labels + result["google_tags"] = attrs.google_tags + result["cloud_uniform_resource_name"] = attrs.cloud_uniform_resource_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _google_to_nested(google: Google) -> GoogleNested: + """Convert flat Google to nested format.""" + attrs = GoogleAttributes() + _populate_google_attrs(attrs, google) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + google, _GOOGLE_REL_FIELDS, GoogleRelationshipAttributes + ) + return GoogleNested( + guid=google.guid, + type_name=google.type_name, + status=google.status, + version=google.version, + create_time=google.create_time, + update_time=google.update_time, + created_by=google.created_by, + updated_by=google.updated_by, + classifications=google.classifications, + classification_names=google.classification_names, + meanings=google.meanings, + labels=google.labels, + business_attributes=google.business_attributes, + custom_attributes=google.custom_attributes, + pending_tasks=google.pending_tasks, + proxy=google.proxy, + is_incomplete=google.is_incomplete, + provenance_type=google.provenance_type, + home_id=google.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _google_from_nested(nested: GoogleNested) -> Google: + """Convert nested format to flat Google.""" + attrs = nested.attributes if nested.attributes is not UNSET else GoogleAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _GOOGLE_REL_FIELDS, + GoogleRelationshipAttributes, + ) + return Google( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_google_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _google_to_nested_bytes(google: Google, serde: Serde) -> bytes: + """Convert flat Google to nested JSON bytes.""" + return serde.encode(_google_to_nested(google)) + + +def _google_from_nested_bytes(data: bytes, serde: Serde) -> Google: + """Convert nested JSON bytes to flat Google.""" + nested = serde.decode(data, GoogleNested) + return _google_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +Google.GOOGLE_SERVICE = KeywordField("googleService", "googleService") +Google.GOOGLE_PROJECT_NAME = KeywordTextField( + "googleProjectName", "googleProjectName", "googleProjectName.text" +) +Google.GOOGLE_PROJECT_ID = KeywordTextField( + "googleProjectId", "googleProjectId", "googleProjectId.text" +) +Google.CLOUD_PROJECT_NUMBER = NumericField("cloudProjectNumber", "cloudProjectNumber") +Google.GOOGLE_LOCATION = KeywordField("googleLocation", "googleLocation") +Google.GOOGLE_LOCATION_TYPE = KeywordField("googleLocationType", "googleLocationType") +Google.GOOGLE_LABELS = KeywordField("googleLabels", "googleLabels") +Google.GOOGLE_TAGS = KeywordField("googleTags", "googleTags") +Google.CLOUD_UNIFORM_RESOURCE_NAME = KeywordField( + "cloudUniformResourceName", "cloudUniformResourceName" +) +Google.ANOMALO_CHECKS = RelationField("anomaloChecks") +Google.APPLICATION = RelationField("application") +Google.APPLICATION_FIELD = RelationField("applicationField") +Google.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Google.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Google.METRICS = RelationField("metrics") +Google.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Google.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Google.MEANINGS = RelationField("meanings") +Google.MC_MONITORS = RelationField("mcMonitors") +Google.MC_INCIDENTS = RelationField("mcIncidents") +Google.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Google.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Google.FILES = RelationField("files") +Google.LINKS = RelationField("links") +Google.README = RelationField("readme") +Google.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Google.SODA_CHECKS = RelationField("sodaChecks") diff --git a/pyatlan_v9/model/assets/gtc_related.py b/pyatlan_v9/model/assets/gtc_related.py new file mode 100644 index 000000000..acfa953fc --- /dev/null +++ b/pyatlan_v9/model/assets/gtc_related.py @@ -0,0 +1,121 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for GTC module. + +This module contains all Related{Type} classes for the GTC type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .asset_related import RelatedAsset +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedAtlasGlossary", + "RelatedAtlasGlossaryCategory", + "RelatedAtlasGlossaryTerm", +] + + +class RelatedAtlasGlossary(RelatedAsset): + """ + Related entity reference for AtlasGlossary assets. + + Extends RelatedAsset with AtlasGlossary-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "AtlasGlossary" so it serializes correctly + + short_description: Union[str, None, UnsetType] = UNSET + """Unused. A short definition of the glossary. See 'description' and 'userDescription' instead.""" + + long_description: Union[str, None, UnsetType] = UNSET + """Unused. A longer description of the glossary. See 'readme' instead.""" + + language: Union[str, None, UnsetType] = UNSET + """Unused. Language of the glossary's contents.""" + + usage: Union[str, None, UnsetType] = UNSET + """Unused. Inteded usage for the glossary.""" + + additional_attributes: Union[Dict[str, str], None, UnsetType] = UNSET + """Unused. Arbitrary set of additional attributes associated with this glossary.""" + + glossary_type: Union[str, None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "AtlasGlossary" + + +class RelatedAtlasGlossaryCategory(RelatedAsset): + """ + Related entity reference for AtlasGlossaryCategory assets. + + Extends RelatedAsset with AtlasGlossaryCategory-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "AtlasGlossaryCategory" so it serializes correctly + + short_description: Union[str, None, UnsetType] = UNSET + """Unused. Brief summary of the category. See 'description' and 'userDescription' instead.""" + + long_description: Union[str, None, UnsetType] = UNSET + """Unused. Detailed description of the category. See 'readme' instead.""" + + additional_attributes: Union[Dict[str, str], None, UnsetType] = UNSET + """Unused. Arbitrary set of additional attributes associated with the category.""" + + category_type: Union[str, None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "AtlasGlossaryCategory" + + +class RelatedAtlasGlossaryTerm(RelatedAsset): + """ + Related entity reference for AtlasGlossaryTerm assets. + + Extends RelatedAsset with AtlasGlossaryTerm-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "AtlasGlossaryTerm" so it serializes correctly + + short_description: Union[str, None, UnsetType] = UNSET + """Unused. Brief summary of the term. See 'description' and 'userDescription' instead.""" + + long_description: Union[str, None, UnsetType] = UNSET + """Unused. Detailed definition of the term. See 'readme' instead.""" + + examples: Union[List[str], None, UnsetType] = UNSET + """Unused. Exmaples of the term.""" + + abbreviation: Union[str, None, UnsetType] = UNSET + """Unused. Abbreviation of the term.""" + + usage: Union[str, None, UnsetType] = UNSET + """Unused. Intended usage for the term.""" + + additional_attributes: Union[Dict[str, str], None, UnsetType] = UNSET + """Unused. Arbitrary set of additional attributes for the terrm.""" + + term_type: Union[str, None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "AtlasGlossaryTerm" diff --git a/pyatlan_v9/model/assets/iceberg.py b/pyatlan_v9/model/assets/iceberg.py new file mode 100644 index 000000000..99b3f6fc4 --- /dev/null +++ b/pyatlan_v9/model/assets/iceberg.py @@ -0,0 +1,834 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Iceberg asset model with flattened inheritance. + +This module provides: +- Iceberg: Flat asset class (easy to use) +- IcebergAttributes: Nested attributes struct (extends AssetAttributes) +- IcebergNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .snowflake_related import RelatedSnowflakeSemanticLogicalTable +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Iceberg(Asset): + """ + Base class for Iceberg assets. + """ + + ICEBERG_PARENT_NAMESPACE_QUALIFIED_NAME: ClassVar[Any] = None + ICEBERG_NAMESPACE_HIERARCHY: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Iceberg" + + iceberg_parent_namespace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the immediate parent namespace in which this asset exists.""" + + iceberg_namespace_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Ordered array of namespace assets with qualified name and name representing the complete namespace hierarchy path for this asset, from immediate parent to root namespace.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Iceberg" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _iceberg_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Iceberg: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Iceberg instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _iceberg_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class IcebergAttributes(AssetAttributes): + """Iceberg-specific attributes for nested API format.""" + + iceberg_parent_namespace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the immediate parent namespace in which this asset exists.""" + + iceberg_namespace_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Ordered array of namespace assets with qualified name and name representing the complete namespace hierarchy path for this asset, from immediate parent to root namespace.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + +class IcebergRelationshipAttributes(AssetRelationshipAttributes): + """Iceberg-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class IcebergNested(AssetNested): + """Iceberg in nested API format for high-performance serialization.""" + + attributes: Union[IcebergAttributes, UnsetType] = UNSET + relationship_attributes: Union[IcebergRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[IcebergRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[IcebergRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_ICEBERG_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_iceberg_attrs(attrs: IcebergAttributes, obj: Iceberg) -> None: + """Populate Iceberg-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.iceberg_parent_namespace_qualified_name = ( + obj.iceberg_parent_namespace_qualified_name + ) + attrs.iceberg_namespace_hierarchy = obj.iceberg_namespace_hierarchy + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + + +def _extract_iceberg_attrs(attrs: IcebergAttributes) -> dict: + """Extract all Iceberg attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["iceberg_parent_namespace_qualified_name"] = ( + attrs.iceberg_parent_namespace_qualified_name + ) + result["iceberg_namespace_hierarchy"] = attrs.iceberg_namespace_hierarchy + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _iceberg_to_nested(iceberg: Iceberg) -> IcebergNested: + """Convert flat Iceberg to nested format.""" + attrs = IcebergAttributes() + _populate_iceberg_attrs(attrs, iceberg) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + iceberg, _ICEBERG_REL_FIELDS, IcebergRelationshipAttributes + ) + return IcebergNested( + guid=iceberg.guid, + type_name=iceberg.type_name, + status=iceberg.status, + version=iceberg.version, + create_time=iceberg.create_time, + update_time=iceberg.update_time, + created_by=iceberg.created_by, + updated_by=iceberg.updated_by, + classifications=iceberg.classifications, + classification_names=iceberg.classification_names, + meanings=iceberg.meanings, + labels=iceberg.labels, + business_attributes=iceberg.business_attributes, + custom_attributes=iceberg.custom_attributes, + pending_tasks=iceberg.pending_tasks, + proxy=iceberg.proxy, + is_incomplete=iceberg.is_incomplete, + provenance_type=iceberg.provenance_type, + home_id=iceberg.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _iceberg_from_nested(nested: IcebergNested) -> Iceberg: + """Convert nested format to flat Iceberg.""" + attrs = nested.attributes if nested.attributes is not UNSET else IcebergAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _ICEBERG_REL_FIELDS, + IcebergRelationshipAttributes, + ) + return Iceberg( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_iceberg_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _iceberg_to_nested_bytes(iceberg: Iceberg, serde: Serde) -> bytes: + """Convert flat Iceberg to nested JSON bytes.""" + return serde.encode(_iceberg_to_nested(iceberg)) + + +def _iceberg_from_nested_bytes(data: bytes, serde: Serde) -> Iceberg: + """Convert nested JSON bytes to flat Iceberg.""" + nested = serde.decode(data, IcebergNested) + return _iceberg_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, +) + +Iceberg.ICEBERG_PARENT_NAMESPACE_QUALIFIED_NAME = KeywordField( + "icebergParentNamespaceQualifiedName", "icebergParentNamespaceQualifiedName" +) +Iceberg.ICEBERG_NAMESPACE_HIERARCHY = KeywordField( + "icebergNamespaceHierarchy", "icebergNamespaceHierarchy" +) +Iceberg.QUERY_COUNT = NumericField("queryCount", "queryCount") +Iceberg.QUERY_USER_COUNT = NumericField("queryUserCount", "queryUserCount") +Iceberg.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +Iceberg.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +Iceberg.DATABASE_NAME = KeywordField("databaseName", "databaseName") +Iceberg.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +Iceberg.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +Iceberg.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +Iceberg.TABLE_NAME = KeywordField("tableName", "tableName") +Iceberg.TABLE_QUALIFIED_NAME = KeywordField("tableQualifiedName", "tableQualifiedName") +Iceberg.VIEW_NAME = KeywordField("viewName", "viewName") +Iceberg.VIEW_QUALIFIED_NAME = KeywordField("viewQualifiedName", "viewQualifiedName") +Iceberg.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +Iceberg.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +Iceberg.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +Iceberg.LAST_PROFILED_AT = NumericField("lastProfiledAt", "lastProfiledAt") +Iceberg.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +Iceberg.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +Iceberg.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Iceberg.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Iceberg.ANOMALO_CHECKS = RelationField("anomaloChecks") +Iceberg.APPLICATION = RelationField("application") +Iceberg.APPLICATION_FIELD = RelationField("applicationField") +Iceberg.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Iceberg.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Iceberg.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Iceberg.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Iceberg.METRICS = RelationField("metrics") +Iceberg.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Iceberg.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Iceberg.DBT_MODELS = RelationField("dbtModels") +Iceberg.SQL_DBT_MODELS = RelationField("sqlDbtModels") +Iceberg.DBT_TESTS = RelationField("dbtTests") +Iceberg.DBT_SOURCES = RelationField("dbtSources") +Iceberg.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +Iceberg.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +Iceberg.MEANINGS = RelationField("meanings") +Iceberg.MC_MONITORS = RelationField("mcMonitors") +Iceberg.MC_INCIDENTS = RelationField("mcIncidents") +Iceberg.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Iceberg.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Iceberg.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Iceberg.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Iceberg.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Iceberg.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Iceberg.FILES = RelationField("files") +Iceberg.LINKS = RelationField("links") +Iceberg.README = RelationField("readme") +Iceberg.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Iceberg.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +Iceberg.SODA_CHECKS = RelationField("sodaChecks") +Iceberg.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Iceberg.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/iceberg_catalog.py b/pyatlan_v9/model/assets/iceberg_catalog.py new file mode 100644 index 000000000..7f22c7ae7 --- /dev/null +++ b/pyatlan_v9/model/assets/iceberg_catalog.py @@ -0,0 +1,936 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +IcebergCatalog asset model with flattened inheritance. + +This module provides: +- IcebergCatalog: Flat asset class (easy to use) +- IcebergCatalogAttributes: Nested attributes struct (extends AssetAttributes) +- IcebergCatalogNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .fabric_related import RelatedFabricWorkspace +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .snowflake_related import RelatedSnowflakeSemanticLogicalTable +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from .sql_related import RelatedSchema +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class IcebergCatalog(Asset): + """ + Instance of an Iceberg catalog in Atlan. + """ + + ICEBERG_CATALOG_TYPE: ClassVar[Any] = None + ICEBERG_URI: ClassVar[Any] = None + ICEBERG_WAREHOUSE: ClassVar[Any] = None + ICEBERG_SCOPE: ClassVar[Any] = None + ICEBERG_CATALOG_PROPERTIES: ClassVar[Any] = None + ICEBERG_PARENT_NAMESPACE_QUALIFIED_NAME: ClassVar[Any] = None + ICEBERG_NAMESPACE_HIERARCHY: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + SCHEMA_COUNT: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + FABRIC_WORKSPACE: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMAS: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "IcebergCatalog" + + iceberg_catalog_type: Union[str, None, UnsetType] = UNSET + """Type of the Iceberg catalog (e.g., 'hadoop', 'hive', 'nessie', 'rest').""" + + iceberg_uri: Union[str, None, UnsetType] = UNSET + """URI of the Iceberg catalog.""" + + iceberg_warehouse: Union[str, None, UnsetType] = UNSET + """Warehouse associated with this Iceberg catalog.""" + + iceberg_scope: Union[str, None, UnsetType] = UNSET + """Scope of the Iceberg catalog.""" + + iceberg_catalog_properties: Union[Dict[str, str], None, UnsetType] = UNSET + """Properties of the Iceberg catalog.""" + + iceberg_parent_namespace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the immediate parent namespace in which this asset exists.""" + + iceberg_namespace_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Ordered array of namespace assets with qualified name and name representing the complete namespace hierarchy path for this asset, from immediate parent to root namespace.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + schema_count: Union[int, None, UnsetType] = UNSET + """Number of schemas in this database.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + fabric_workspace: Union[RelatedFabricWorkspace, None, UnsetType] = UNSET + """Workspace containing the database.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schemas: Union[List[RelatedSchema], None, UnsetType] = UNSET + """Schemas that exist within this database.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "IcebergCatalog" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _iceberg_catalog_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> IcebergCatalog: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + IcebergCatalog instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _iceberg_catalog_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class IcebergCatalogAttributes(AssetAttributes): + """IcebergCatalog-specific attributes for nested API format.""" + + iceberg_catalog_type: Union[str, None, UnsetType] = UNSET + """Type of the Iceberg catalog (e.g., 'hadoop', 'hive', 'nessie', 'rest').""" + + iceberg_uri: Union[str, None, UnsetType] = UNSET + """URI of the Iceberg catalog.""" + + iceberg_warehouse: Union[str, None, UnsetType] = UNSET + """Warehouse associated with this Iceberg catalog.""" + + iceberg_scope: Union[str, None, UnsetType] = UNSET + """Scope of the Iceberg catalog.""" + + iceberg_catalog_properties: Union[Dict[str, str], None, UnsetType] = UNSET + """Properties of the Iceberg catalog.""" + + iceberg_parent_namespace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the immediate parent namespace in which this asset exists.""" + + iceberg_namespace_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Ordered array of namespace assets with qualified name and name representing the complete namespace hierarchy path for this asset, from immediate parent to root namespace.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + schema_count: Union[int, None, UnsetType] = UNSET + """Number of schemas in this database.""" + + +class IcebergCatalogRelationshipAttributes(AssetRelationshipAttributes): + """IcebergCatalog-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + fabric_workspace: Union[RelatedFabricWorkspace, None, UnsetType] = UNSET + """Workspace containing the database.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schemas: Union[List[RelatedSchema], None, UnsetType] = UNSET + """Schemas that exist within this database.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class IcebergCatalogNested(AssetNested): + """IcebergCatalog in nested API format for high-performance serialization.""" + + attributes: Union[IcebergCatalogAttributes, UnsetType] = UNSET + relationship_attributes: Union[IcebergCatalogRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + IcebergCatalogRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + IcebergCatalogRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_ICEBERG_CATALOG_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "fabric_workspace", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schemas", + "schema_registry_subjects", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_iceberg_catalog_attrs( + attrs: IcebergCatalogAttributes, obj: IcebergCatalog +) -> None: + """Populate IcebergCatalog-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.iceberg_catalog_type = obj.iceberg_catalog_type + attrs.iceberg_uri = obj.iceberg_uri + attrs.iceberg_warehouse = obj.iceberg_warehouse + attrs.iceberg_scope = obj.iceberg_scope + attrs.iceberg_catalog_properties = obj.iceberg_catalog_properties + attrs.iceberg_parent_namespace_qualified_name = ( + obj.iceberg_parent_namespace_qualified_name + ) + attrs.iceberg_namespace_hierarchy = obj.iceberg_namespace_hierarchy + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + attrs.schema_count = obj.schema_count + + +def _extract_iceberg_catalog_attrs(attrs: IcebergCatalogAttributes) -> dict: + """Extract all IcebergCatalog attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["iceberg_catalog_type"] = attrs.iceberg_catalog_type + result["iceberg_uri"] = attrs.iceberg_uri + result["iceberg_warehouse"] = attrs.iceberg_warehouse + result["iceberg_scope"] = attrs.iceberg_scope + result["iceberg_catalog_properties"] = attrs.iceberg_catalog_properties + result["iceberg_parent_namespace_qualified_name"] = ( + attrs.iceberg_parent_namespace_qualified_name + ) + result["iceberg_namespace_hierarchy"] = attrs.iceberg_namespace_hierarchy + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + result["schema_count"] = attrs.schema_count + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _iceberg_catalog_to_nested(iceberg_catalog: IcebergCatalog) -> IcebergCatalogNested: + """Convert flat IcebergCatalog to nested format.""" + attrs = IcebergCatalogAttributes() + _populate_iceberg_catalog_attrs(attrs, iceberg_catalog) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + iceberg_catalog, + _ICEBERG_CATALOG_REL_FIELDS, + IcebergCatalogRelationshipAttributes, + ) + return IcebergCatalogNested( + guid=iceberg_catalog.guid, + type_name=iceberg_catalog.type_name, + status=iceberg_catalog.status, + version=iceberg_catalog.version, + create_time=iceberg_catalog.create_time, + update_time=iceberg_catalog.update_time, + created_by=iceberg_catalog.created_by, + updated_by=iceberg_catalog.updated_by, + classifications=iceberg_catalog.classifications, + classification_names=iceberg_catalog.classification_names, + meanings=iceberg_catalog.meanings, + labels=iceberg_catalog.labels, + business_attributes=iceberg_catalog.business_attributes, + custom_attributes=iceberg_catalog.custom_attributes, + pending_tasks=iceberg_catalog.pending_tasks, + proxy=iceberg_catalog.proxy, + is_incomplete=iceberg_catalog.is_incomplete, + provenance_type=iceberg_catalog.provenance_type, + home_id=iceberg_catalog.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _iceberg_catalog_from_nested(nested: IcebergCatalogNested) -> IcebergCatalog: + """Convert nested format to flat IcebergCatalog.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else IcebergCatalogAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _ICEBERG_CATALOG_REL_FIELDS, + IcebergCatalogRelationshipAttributes, + ) + return IcebergCatalog( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_iceberg_catalog_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _iceberg_catalog_to_nested_bytes( + iceberg_catalog: IcebergCatalog, serde: Serde +) -> bytes: + """Convert flat IcebergCatalog to nested JSON bytes.""" + return serde.encode(_iceberg_catalog_to_nested(iceberg_catalog)) + + +def _iceberg_catalog_from_nested_bytes(data: bytes, serde: Serde) -> IcebergCatalog: + """Convert nested JSON bytes to flat IcebergCatalog.""" + nested = serde.decode(data, IcebergCatalogNested) + return _iceberg_catalog_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, +) + +IcebergCatalog.ICEBERG_CATALOG_TYPE = KeywordField( + "icebergCatalogType", "icebergCatalogType" +) +IcebergCatalog.ICEBERG_URI = KeywordField("icebergUri", "icebergUri") +IcebergCatalog.ICEBERG_WAREHOUSE = KeywordField("icebergWarehouse", "icebergWarehouse") +IcebergCatalog.ICEBERG_SCOPE = KeywordField("icebergScope", "icebergScope") +IcebergCatalog.ICEBERG_CATALOG_PROPERTIES = KeywordField( + "icebergCatalogProperties", "icebergCatalogProperties" +) +IcebergCatalog.ICEBERG_PARENT_NAMESPACE_QUALIFIED_NAME = KeywordField( + "icebergParentNamespaceQualifiedName", "icebergParentNamespaceQualifiedName" +) +IcebergCatalog.ICEBERG_NAMESPACE_HIERARCHY = KeywordField( + "icebergNamespaceHierarchy", "icebergNamespaceHierarchy" +) +IcebergCatalog.QUERY_COUNT = NumericField("queryCount", "queryCount") +IcebergCatalog.QUERY_USER_COUNT = NumericField("queryUserCount", "queryUserCount") +IcebergCatalog.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +IcebergCatalog.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +IcebergCatalog.DATABASE_NAME = KeywordField("databaseName", "databaseName") +IcebergCatalog.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +IcebergCatalog.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +IcebergCatalog.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +IcebergCatalog.TABLE_NAME = KeywordField("tableName", "tableName") +IcebergCatalog.TABLE_QUALIFIED_NAME = KeywordField( + "tableQualifiedName", "tableQualifiedName" +) +IcebergCatalog.VIEW_NAME = KeywordField("viewName", "viewName") +IcebergCatalog.VIEW_QUALIFIED_NAME = KeywordField( + "viewQualifiedName", "viewQualifiedName" +) +IcebergCatalog.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +IcebergCatalog.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +IcebergCatalog.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +IcebergCatalog.LAST_PROFILED_AT = NumericField("lastProfiledAt", "lastProfiledAt") +IcebergCatalog.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +IcebergCatalog.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +IcebergCatalog.SCHEMA_COUNT = NumericField("schemaCount", "schemaCount") +IcebergCatalog.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +IcebergCatalog.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +IcebergCatalog.ANOMALO_CHECKS = RelationField("anomaloChecks") +IcebergCatalog.APPLICATION = RelationField("application") +IcebergCatalog.APPLICATION_FIELD = RelationField("applicationField") +IcebergCatalog.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +IcebergCatalog.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +IcebergCatalog.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +IcebergCatalog.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +IcebergCatalog.METRICS = RelationField("metrics") +IcebergCatalog.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +IcebergCatalog.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +IcebergCatalog.DBT_MODELS = RelationField("dbtModels") +IcebergCatalog.SQL_DBT_MODELS = RelationField("sqlDbtModels") +IcebergCatalog.DBT_TESTS = RelationField("dbtTests") +IcebergCatalog.DBT_SOURCES = RelationField("dbtSources") +IcebergCatalog.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +IcebergCatalog.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +IcebergCatalog.FABRIC_WORKSPACE = RelationField("fabricWorkspace") +IcebergCatalog.MEANINGS = RelationField("meanings") +IcebergCatalog.MC_MONITORS = RelationField("mcMonitors") +IcebergCatalog.MC_INCIDENTS = RelationField("mcIncidents") +IcebergCatalog.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +IcebergCatalog.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +IcebergCatalog.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +IcebergCatalog.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +IcebergCatalog.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +IcebergCatalog.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +IcebergCatalog.FILES = RelationField("files") +IcebergCatalog.LINKS = RelationField("links") +IcebergCatalog.README = RelationField("readme") +IcebergCatalog.SCHEMAS = RelationField("schemas") +IcebergCatalog.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +IcebergCatalog.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +IcebergCatalog.SODA_CHECKS = RelationField("sodaChecks") +IcebergCatalog.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +IcebergCatalog.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/iceberg_column.py b/pyatlan_v9/model/assets/iceberg_column.py new file mode 100644 index 000000000..494d0b2bb --- /dev/null +++ b/pyatlan_v9/model/assets/iceberg_column.py @@ -0,0 +1,1809 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +IcebergColumn asset model with flattened inheritance. + +This module provides: +- IcebergColumn: Flat asset class (easy to use) +- IcebergColumnAttributes: Nested attributes struct (extends AssetAttributes) +- IcebergColumnNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .cosmos_mongo_db_related import RelatedCosmosMongoDBCollection +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtMetric, + RelatedDbtModel, + RelatedDbtModelColumn, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .mongo_db_related import RelatedMongoDBCollection +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .snowflake_related import ( + RelatedSnowflakeDynamicTable, + RelatedSnowflakeSemanticLogicalTable, +) +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from .sql_related import ( + RelatedCalculationView, + RelatedColumn, + RelatedMaterialisedView, + RelatedQuery, + RelatedTable, + RelatedTablePartition, + RelatedView, +) +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class IcebergColumn(Asset): + """ + Instance of an Iceberg column in Atlan. + """ + + ICEBERG_PARENT_NAMESPACE_QUALIFIED_NAME: ClassVar[Any] = None + ICEBERG_NAMESPACE_HIERARCHY: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + DATA_TYPE: ClassVar[Any] = None + SUB_DATA_TYPE: ClassVar[Any] = None + COLUMN_COMPRESSION: ClassVar[Any] = None + COLUMN_ENCODING: ClassVar[Any] = None + RAW_DATA_TYPE_DEFINITION: ClassVar[Any] = None + ORDER: ClassVar[Any] = None + NESTED_COLUMN_ORDER: ClassVar[Any] = None + NESTED_COLUMN_COUNT: ClassVar[Any] = None + COLUMN_HIERARCHY: ClassVar[Any] = None + IS_PARTITION: ClassVar[Any] = None + PARTITION_ORDER: ClassVar[Any] = None + IS_CLUSTERED: ClassVar[Any] = None + IS_PRIMARY: ClassVar[Any] = None + IS_FOREIGN: ClassVar[Any] = None + IS_INDEXED: ClassVar[Any] = None + IS_SORT: ClassVar[Any] = None + IS_DIST: ClassVar[Any] = None + IS_PINNED: ClassVar[Any] = None + PINNED_BY: ClassVar[Any] = None + PINNED_AT: ClassVar[Any] = None + PRECISION: ClassVar[Any] = None + DEFAULT_VALUE: ClassVar[Any] = None + IS_NULLABLE: ClassVar[Any] = None + NUMERIC_SCALE: ClassVar[Any] = None + MAX_LENGTH: ClassVar[Any] = None + VALIDATIONS: ClassVar[Any] = None + PARENT_COLUMN_QUALIFIED_NAME: ClassVar[Any] = None + PARENT_COLUMN_NAME: ClassVar[Any] = None + COLUMN_DISTINCT_VALUES_COUNT: ClassVar[Any] = None + COLUMN_DISTINCT_VALUES_COUNT_LONG: ClassVar[Any] = None + COLUMN_HISTOGRAM: ClassVar[Any] = None + COLUMN_MAX: ClassVar[Any] = None + COLUMN_MIN: ClassVar[Any] = None + COLUMN_MEAN: ClassVar[Any] = None + COLUMN_SUM: ClassVar[Any] = None + COLUMN_MEDIAN: ClassVar[Any] = None + COLUMN_STANDARD_DEVIATION: ClassVar[Any] = None + COLUMN_UNIQUE_VALUES_COUNT: ClassVar[Any] = None + COLUMN_UNIQUE_VALUES_COUNT_LONG: ClassVar[Any] = None + COLUMN_AVERAGE: ClassVar[Any] = None + COLUMN_AVERAGE_LENGTH: ClassVar[Any] = None + COLUMN_DUPLICATE_VALUES_COUNT: ClassVar[Any] = None + COLUMN_DUPLICATE_VALUES_COUNT_LONG: ClassVar[Any] = None + COLUMN_MAXIMUM_STRING_LENGTH: ClassVar[Any] = None + COLUMN_MAXS: ClassVar[Any] = None + COLUMN_MINIMUM_STRING_LENGTH: ClassVar[Any] = None + COLUMN_MINS: ClassVar[Any] = None + COLUMN_MISSING_VALUES_COUNT: ClassVar[Any] = None + COLUMN_MISSING_VALUES_COUNT_LONG: ClassVar[Any] = None + COLUMN_MISSING_VALUES_PERCENTAGE: ClassVar[Any] = None + COLUMN_UNIQUENESS_PERCENTAGE: ClassVar[Any] = None + COLUMN_VARIANCE: ClassVar[Any] = None + COLUMN_TOP_VALUES: ClassVar[Any] = None + COLUMN_MAX_VALUE: ClassVar[Any] = None + COLUMN_MIN_VALUE: ClassVar[Any] = None + COLUMN_MEAN_VALUE: ClassVar[Any] = None + COLUMN_SUM_VALUE: ClassVar[Any] = None + COLUMN_MEDIAN_VALUE: ClassVar[Any] = None + COLUMN_STANDARD_DEVIATION_VALUE: ClassVar[Any] = None + COLUMN_AVERAGE_VALUE: ClassVar[Any] = None + COLUMN_VARIANCE_VALUE: ClassVar[Any] = None + COLUMN_AVERAGE_LENGTH_VALUE: ClassVar[Any] = None + COLUMN_DISTRIBUTION_HISTOGRAM: ClassVar[Any] = None + COLUMN_DEPTH_LEVEL: ClassVar[Any] = None + NOSQL_COLLECTION_NAME: ClassVar[Any] = None + NOSQL_COLLECTION_QUALIFIED_NAME: ClassVar[Any] = None + COLUMN_IS_MEASURE: ClassVar[Any] = None + COLUMN_MEASURE_TYPE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + COSMOS_MONGO_DB_COLLECTION: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + METRIC_TIMESTAMPS: ClassVar[Any] = None + DATA_QUALITY_METRIC_DIMENSIONS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_BASE_COLUMN_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_COLUMN_RULES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_METRICS: ClassVar[Any] = None + DBT_MODEL_COLUMNS: ClassVar[Any] = None + COLUMN_DBT_MODEL_COLUMNS: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MONGO_DB_COLLECTION: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + TABLE: ClassVar[Any] = None + NESTED_COLUMNS: ClassVar[Any] = None + PARENT_COLUMN: ClassVar[Any] = None + TABLE_PARTITION: ClassVar[Any] = None + VIEW: ClassVar[Any] = None + CALCULATION_VIEW: ClassVar[Any] = None + MATERIALISED_VIEW: ClassVar[Any] = None + FOREIGN_KEY_TO: ClassVar[Any] = None + FOREIGN_KEY_FROM: ClassVar[Any] = None + QUERIES: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_DYNAMIC_TABLE: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "IcebergColumn" + + iceberg_parent_namespace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the immediate parent namespace in which this asset exists.""" + + iceberg_namespace_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Ordered array of namespace assets with qualified name and name representing the complete namespace hierarchy path for this asset, from immediate parent to root namespace.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + data_type: Union[str, None, UnsetType] = UNSET + """Data type of values in this column.""" + + sub_data_type: Union[str, None, UnsetType] = UNSET + """Sub-data type of this column.""" + + column_compression: Union[str, None, UnsetType] = UNSET + """Compression type of this column.""" + + column_encoding: Union[str, None, UnsetType] = UNSET + """Encoding type of this column.""" + + raw_data_type_definition: Union[str, None, UnsetType] = UNSET + """Raw data type definition of this column.""" + + order: Union[int, None, UnsetType] = UNSET + """Order (position) in which this column appears in the table (starting at 1).""" + + nested_column_order: Union[str, None, UnsetType] = UNSET + """Order (position) in which this column appears in the nested Column (nest level starts at 1).""" + + nested_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns nested within this (STRUCT or NESTED) column.""" + + column_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of top-level upstream nested columns.""" + + is_partition: Union[bool, None, UnsetType] = UNSET + """Whether this column is a partition column (true) or not (false).""" + + partition_order: Union[int, None, UnsetType] = UNSET + """Order (position) of this partition column in the table.""" + + is_clustered: Union[bool, None, UnsetType] = UNSET + """Whether this column is a clustered column (true) or not (false).""" + + is_primary: Union[bool, None, UnsetType] = UNSET + """When true, this column is the primary key for the table.""" + + is_foreign: Union[bool, None, UnsetType] = UNSET + """When true, this column is a foreign key to another table. NOTE: this must be true when using the foreignKeyTo relationship to specify columns that refer to this column as a foreign key.""" + + is_indexed: Union[bool, None, UnsetType] = UNSET + """When true, this column is indexed in the database.""" + + is_sort: Union[bool, None, UnsetType] = UNSET + """Whether this column is a sort column (true) or not (false).""" + + is_dist: Union[bool, None, UnsetType] = UNSET + """Whether this column is a distribution column (true) or not (false).""" + + is_pinned: Union[bool, None, UnsetType] = UNSET + """Whether this column is pinned (true) or not (false).""" + + pinned_by: Union[str, None, UnsetType] = UNSET + """User who pinned this column.""" + + pinned_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this column was pinned, in milliseconds.""" + + precision: Union[int, None, UnsetType] = UNSET + """Total number of digits allowed, when the dataType is numeric.""" + + default_value: Union[str, None, UnsetType] = UNSET + """Default value for this column.""" + + is_nullable: Union[bool, None, UnsetType] = UNSET + """When true, the values in this column can be null.""" + + numeric_scale: Union[float, None, UnsetType] = UNSET + """Number of digits allowed to the right of the decimal point.""" + + max_length: Union[int, None, UnsetType] = UNSET + """Maximum length of a value in this column.""" + + validations: Union[Dict[str, str], None, UnsetType] = UNSET + """Validations for this column.""" + + parent_column_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the column this column is nested within, for STRUCT and NESTED columns.""" + + parent_column_name: Union[str, None, UnsetType] = UNSET + """Simple name of the column this column is nested within, for STRUCT and NESTED columns.""" + + column_distinct_values_count: Union[int, None, UnsetType] = UNSET + """Number of rows that contain distinct values.""" + + column_distinct_values_count_long: Union[int, None, UnsetType] = UNSET + """Number of rows that contain distinct values.""" + + column_histogram: Union[Dict[str, Any], None, UnsetType] = UNSET + """List of values in a histogram that represents the contents of this column.""" + + column_max: Union[float, None, UnsetType] = UNSET + """Greatest value in a numeric column.""" + + column_min: Union[float, None, UnsetType] = UNSET + """Least value in a numeric column.""" + + column_mean: Union[float, None, UnsetType] = UNSET + """Arithmetic mean of the values in a numeric column.""" + + column_sum: Union[float, None, UnsetType] = UNSET + """Calculated sum of the values in a numeric column.""" + + column_median: Union[float, None, UnsetType] = UNSET + """Calculated median of the values in a numeric column.""" + + column_standard_deviation: Union[float, None, UnsetType] = UNSET + """Calculated standard deviation of the values in a numeric column.""" + + column_unique_values_count: Union[int, None, UnsetType] = UNSET + """Number of rows in which a value in this column appears only once.""" + + column_unique_values_count_long: Union[int, None, UnsetType] = UNSET + """Number of rows in which a value in this column appears only once.""" + + column_average: Union[float, None, UnsetType] = UNSET + """Average value in this column.""" + + column_average_length: Union[float, None, UnsetType] = UNSET + """Average length of values in a string column.""" + + column_duplicate_values_count: Union[int, None, UnsetType] = UNSET + """Number of rows that contain duplicate values.""" + + column_duplicate_values_count_long: Union[int, None, UnsetType] = UNSET + """Number of rows that contain duplicate values.""" + + column_maximum_string_length: Union[int, None, UnsetType] = UNSET + """Length of the longest value in a string column.""" + + column_maxs: Union[List[str], None, UnsetType] = UNSET + """List of the greatest values in a column.""" + + column_minimum_string_length: Union[int, None, UnsetType] = UNSET + """Length of the shortest value in a string column.""" + + column_mins: Union[List[str], None, UnsetType] = UNSET + """List of the least values in a column.""" + + column_missing_values_count: Union[int, None, UnsetType] = UNSET + """Number of rows in a column that do not contain content.""" + + column_missing_values_count_long: Union[int, None, UnsetType] = UNSET + """Number of rows in a column that do not contain content.""" + + column_missing_values_percentage: Union[float, None, UnsetType] = UNSET + """Percentage of rows in a column that do not contain content.""" + + column_uniqueness_percentage: Union[float, None, UnsetType] = UNSET + """Ratio indicating how unique data in this column is: 0 indicates that all values are the same, 100 indicates that all values in this column are unique.""" + + column_variance: Union[float, None, UnsetType] = UNSET + """Calculated variance of the values in a numeric column.""" + + column_top_values: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of top values in this column.""" + + column_max_value: Union[float, None, UnsetType] = UNSET + """Greatest value in a numeric column.""" + + column_min_value: Union[float, None, UnsetType] = UNSET + """Least value in a numeric column.""" + + column_mean_value: Union[float, None, UnsetType] = UNSET + """Arithmetic mean of the values in a numeric column.""" + + column_sum_value: Union[float, None, UnsetType] = UNSET + """Calculated sum of the values in a numeric column.""" + + column_median_value: Union[float, None, UnsetType] = UNSET + """Calculated median of the values in a numeric column.""" + + column_standard_deviation_value: Union[float, None, UnsetType] = UNSET + """Calculated standard deviation of the values in a numeric column.""" + + column_average_value: Union[float, None, UnsetType] = UNSET + """Average value in this column.""" + + column_variance_value: Union[float, None, UnsetType] = UNSET + """Calculated variance of the values in a numeric column.""" + + column_average_length_value: Union[float, None, UnsetType] = UNSET + """Average length of values in a string column.""" + + column_distribution_histogram: Union[Dict[str, Any], None, UnsetType] = UNSET + """Detailed information representing a histogram of values for a column.""" + + column_depth_level: Union[int, None, UnsetType] = UNSET + """Level of nesting of this column, used for STRUCT and NESTED columns.""" + + nosql_collection_name: Union[str, None, UnsetType] = UNSET + """Simple name of the cosmos/mongo collection in which this SQL asset (column) exists, or empty if it does not exist within a cosmos/mongo collection.""" + + nosql_collection_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the cosmos/mongo collection in which this SQL asset (column) exists, or empty if it does not exist within a cosmos/mongo collection.""" + + column_is_measure: Union[bool, None, UnsetType] = UNSET + """When true, this column is of type measure/calculated.""" + + column_measure_type: Union[str, None, UnsetType] = UNSET + """The type of measure/calculated column this is, eg: base, calculated, derived.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cosmos_mongo_db_collection: Union[ + RelatedCosmosMongoDBCollection, None, UnsetType + ] = msgspec.field(default=UNSET, name="cosmosMongoDBCollection") + """Cosmos collection in which this column exists.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + metric_timestamps: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + data_quality_metric_dimensions: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_base_column_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this column.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dq_reference_column_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this column is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_metrics: Union[List[RelatedDbtMetric], None, UnsetType] = UNSET + """Metrics related to this model column.""" + + dbt_model_columns: Union[List[RelatedDbtModelColumn], None, UnsetType] = UNSET + """(Deprecated) Model columns related to this model column.""" + + column_dbt_model_columns: Union[List[RelatedDbtModelColumn], None, UnsetType] = ( + UNSET + ) + """Model columns related to this column.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mongo_db_collection: Union[RelatedMongoDBCollection, None, UnsetType] = ( + msgspec.field(default=UNSET, name="mongoDBCollection") + ) + """Collection in which the columns exist.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + table: Union[RelatedTable, None, UnsetType] = UNSET + """Table in which this column exists.""" + + nested_columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Nested columns that exist within this column.""" + + parent_column: Union[RelatedColumn, None, UnsetType] = UNSET + """Column in which this sub-column is nested.""" + + table_partition: Union[RelatedTablePartition, None, UnsetType] = UNSET + """Table partition that contains this column.""" + + view: Union[RelatedView, None, UnsetType] = UNSET + """View in which this column exists.""" + + calculation_view: Union[RelatedCalculationView, None, UnsetType] = UNSET + """Calculate view in which this column exists.""" + + materialised_view: Union[RelatedMaterialisedView, None, UnsetType] = UNSET + """Materialized view in which this column exists.""" + + foreign_key_to: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Columns that use this column as a foreign key.""" + + foreign_key_from: Union[RelatedColumn, None, UnsetType] = UNSET + """Column this foreign key column refers to.""" + + queries: Union[List[RelatedQuery], None, UnsetType] = UNSET + """Queries that access this column.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_dynamic_table: Union[RelatedSnowflakeDynamicTable, None, UnsetType] = ( + UNSET + ) + """Snowflake dynamic table in which this column exists.""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "IcebergColumn" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _iceberg_column_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> IcebergColumn: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + IcebergColumn instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _iceberg_column_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class IcebergColumnAttributes(AssetAttributes): + """IcebergColumn-specific attributes for nested API format.""" + + iceberg_parent_namespace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the immediate parent namespace in which this asset exists.""" + + iceberg_namespace_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Ordered array of namespace assets with qualified name and name representing the complete namespace hierarchy path for this asset, from immediate parent to root namespace.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + data_type: Union[str, None, UnsetType] = UNSET + """Data type of values in this column.""" + + sub_data_type: Union[str, None, UnsetType] = UNSET + """Sub-data type of this column.""" + + column_compression: Union[str, None, UnsetType] = UNSET + """Compression type of this column.""" + + column_encoding: Union[str, None, UnsetType] = UNSET + """Encoding type of this column.""" + + raw_data_type_definition: Union[str, None, UnsetType] = UNSET + """Raw data type definition of this column.""" + + order: Union[int, None, UnsetType] = UNSET + """Order (position) in which this column appears in the table (starting at 1).""" + + nested_column_order: Union[str, None, UnsetType] = UNSET + """Order (position) in which this column appears in the nested Column (nest level starts at 1).""" + + nested_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns nested within this (STRUCT or NESTED) column.""" + + column_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of top-level upstream nested columns.""" + + is_partition: Union[bool, None, UnsetType] = UNSET + """Whether this column is a partition column (true) or not (false).""" + + partition_order: Union[int, None, UnsetType] = UNSET + """Order (position) of this partition column in the table.""" + + is_clustered: Union[bool, None, UnsetType] = UNSET + """Whether this column is a clustered column (true) or not (false).""" + + is_primary: Union[bool, None, UnsetType] = UNSET + """When true, this column is the primary key for the table.""" + + is_foreign: Union[bool, None, UnsetType] = UNSET + """When true, this column is a foreign key to another table. NOTE: this must be true when using the foreignKeyTo relationship to specify columns that refer to this column as a foreign key.""" + + is_indexed: Union[bool, None, UnsetType] = UNSET + """When true, this column is indexed in the database.""" + + is_sort: Union[bool, None, UnsetType] = UNSET + """Whether this column is a sort column (true) or not (false).""" + + is_dist: Union[bool, None, UnsetType] = UNSET + """Whether this column is a distribution column (true) or not (false).""" + + is_pinned: Union[bool, None, UnsetType] = UNSET + """Whether this column is pinned (true) or not (false).""" + + pinned_by: Union[str, None, UnsetType] = UNSET + """User who pinned this column.""" + + pinned_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this column was pinned, in milliseconds.""" + + precision: Union[int, None, UnsetType] = UNSET + """Total number of digits allowed, when the dataType is numeric.""" + + default_value: Union[str, None, UnsetType] = UNSET + """Default value for this column.""" + + is_nullable: Union[bool, None, UnsetType] = UNSET + """When true, the values in this column can be null.""" + + numeric_scale: Union[float, None, UnsetType] = UNSET + """Number of digits allowed to the right of the decimal point.""" + + max_length: Union[int, None, UnsetType] = UNSET + """Maximum length of a value in this column.""" + + validations: Union[Dict[str, str], None, UnsetType] = UNSET + """Validations for this column.""" + + parent_column_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the column this column is nested within, for STRUCT and NESTED columns.""" + + parent_column_name: Union[str, None, UnsetType] = UNSET + """Simple name of the column this column is nested within, for STRUCT and NESTED columns.""" + + column_distinct_values_count: Union[int, None, UnsetType] = UNSET + """Number of rows that contain distinct values.""" + + column_distinct_values_count_long: Union[int, None, UnsetType] = UNSET + """Number of rows that contain distinct values.""" + + column_histogram: Union[Dict[str, Any], None, UnsetType] = UNSET + """List of values in a histogram that represents the contents of this column.""" + + column_max: Union[float, None, UnsetType] = UNSET + """Greatest value in a numeric column.""" + + column_min: Union[float, None, UnsetType] = UNSET + """Least value in a numeric column.""" + + column_mean: Union[float, None, UnsetType] = UNSET + """Arithmetic mean of the values in a numeric column.""" + + column_sum: Union[float, None, UnsetType] = UNSET + """Calculated sum of the values in a numeric column.""" + + column_median: Union[float, None, UnsetType] = UNSET + """Calculated median of the values in a numeric column.""" + + column_standard_deviation: Union[float, None, UnsetType] = UNSET + """Calculated standard deviation of the values in a numeric column.""" + + column_unique_values_count: Union[int, None, UnsetType] = UNSET + """Number of rows in which a value in this column appears only once.""" + + column_unique_values_count_long: Union[int, None, UnsetType] = UNSET + """Number of rows in which a value in this column appears only once.""" + + column_average: Union[float, None, UnsetType] = UNSET + """Average value in this column.""" + + column_average_length: Union[float, None, UnsetType] = UNSET + """Average length of values in a string column.""" + + column_duplicate_values_count: Union[int, None, UnsetType] = UNSET + """Number of rows that contain duplicate values.""" + + column_duplicate_values_count_long: Union[int, None, UnsetType] = UNSET + """Number of rows that contain duplicate values.""" + + column_maximum_string_length: Union[int, None, UnsetType] = UNSET + """Length of the longest value in a string column.""" + + column_maxs: Union[List[str], None, UnsetType] = UNSET + """List of the greatest values in a column.""" + + column_minimum_string_length: Union[int, None, UnsetType] = UNSET + """Length of the shortest value in a string column.""" + + column_mins: Union[List[str], None, UnsetType] = UNSET + """List of the least values in a column.""" + + column_missing_values_count: Union[int, None, UnsetType] = UNSET + """Number of rows in a column that do not contain content.""" + + column_missing_values_count_long: Union[int, None, UnsetType] = UNSET + """Number of rows in a column that do not contain content.""" + + column_missing_values_percentage: Union[float, None, UnsetType] = UNSET + """Percentage of rows in a column that do not contain content.""" + + column_uniqueness_percentage: Union[float, None, UnsetType] = UNSET + """Ratio indicating how unique data in this column is: 0 indicates that all values are the same, 100 indicates that all values in this column are unique.""" + + column_variance: Union[float, None, UnsetType] = UNSET + """Calculated variance of the values in a numeric column.""" + + column_top_values: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of top values in this column.""" + + column_max_value: Union[float, None, UnsetType] = UNSET + """Greatest value in a numeric column.""" + + column_min_value: Union[float, None, UnsetType] = UNSET + """Least value in a numeric column.""" + + column_mean_value: Union[float, None, UnsetType] = UNSET + """Arithmetic mean of the values in a numeric column.""" + + column_sum_value: Union[float, None, UnsetType] = UNSET + """Calculated sum of the values in a numeric column.""" + + column_median_value: Union[float, None, UnsetType] = UNSET + """Calculated median of the values in a numeric column.""" + + column_standard_deviation_value: Union[float, None, UnsetType] = UNSET + """Calculated standard deviation of the values in a numeric column.""" + + column_average_value: Union[float, None, UnsetType] = UNSET + """Average value in this column.""" + + column_variance_value: Union[float, None, UnsetType] = UNSET + """Calculated variance of the values in a numeric column.""" + + column_average_length_value: Union[float, None, UnsetType] = UNSET + """Average length of values in a string column.""" + + column_distribution_histogram: Union[Dict[str, Any], None, UnsetType] = UNSET + """Detailed information representing a histogram of values for a column.""" + + column_depth_level: Union[int, None, UnsetType] = UNSET + """Level of nesting of this column, used for STRUCT and NESTED columns.""" + + nosql_collection_name: Union[str, None, UnsetType] = UNSET + """Simple name of the cosmos/mongo collection in which this SQL asset (column) exists, or empty if it does not exist within a cosmos/mongo collection.""" + + nosql_collection_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the cosmos/mongo collection in which this SQL asset (column) exists, or empty if it does not exist within a cosmos/mongo collection.""" + + column_is_measure: Union[bool, None, UnsetType] = UNSET + """When true, this column is of type measure/calculated.""" + + column_measure_type: Union[str, None, UnsetType] = UNSET + """The type of measure/calculated column this is, eg: base, calculated, derived.""" + + +class IcebergColumnRelationshipAttributes(AssetRelationshipAttributes): + """IcebergColumn-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cosmos_mongo_db_collection: Union[ + RelatedCosmosMongoDBCollection, None, UnsetType + ] = msgspec.field(default=UNSET, name="cosmosMongoDBCollection") + """Cosmos collection in which this column exists.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + metric_timestamps: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + data_quality_metric_dimensions: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_base_column_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this column.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dq_reference_column_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this column is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_metrics: Union[List[RelatedDbtMetric], None, UnsetType] = UNSET + """Metrics related to this model column.""" + + dbt_model_columns: Union[List[RelatedDbtModelColumn], None, UnsetType] = UNSET + """(Deprecated) Model columns related to this model column.""" + + column_dbt_model_columns: Union[List[RelatedDbtModelColumn], None, UnsetType] = ( + UNSET + ) + """Model columns related to this column.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mongo_db_collection: Union[RelatedMongoDBCollection, None, UnsetType] = ( + msgspec.field(default=UNSET, name="mongoDBCollection") + ) + """Collection in which the columns exist.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + table: Union[RelatedTable, None, UnsetType] = UNSET + """Table in which this column exists.""" + + nested_columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Nested columns that exist within this column.""" + + parent_column: Union[RelatedColumn, None, UnsetType] = UNSET + """Column in which this sub-column is nested.""" + + table_partition: Union[RelatedTablePartition, None, UnsetType] = UNSET + """Table partition that contains this column.""" + + view: Union[RelatedView, None, UnsetType] = UNSET + """View in which this column exists.""" + + calculation_view: Union[RelatedCalculationView, None, UnsetType] = UNSET + """Calculate view in which this column exists.""" + + materialised_view: Union[RelatedMaterialisedView, None, UnsetType] = UNSET + """Materialized view in which this column exists.""" + + foreign_key_to: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Columns that use this column as a foreign key.""" + + foreign_key_from: Union[RelatedColumn, None, UnsetType] = UNSET + """Column this foreign key column refers to.""" + + queries: Union[List[RelatedQuery], None, UnsetType] = UNSET + """Queries that access this column.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_dynamic_table: Union[RelatedSnowflakeDynamicTable, None, UnsetType] = ( + UNSET + ) + """Snowflake dynamic table in which this column exists.""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class IcebergColumnNested(AssetNested): + """IcebergColumn in nested API format for high-performance serialization.""" + + attributes: Union[IcebergColumnAttributes, UnsetType] = UNSET + relationship_attributes: Union[IcebergColumnRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + IcebergColumnRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + IcebergColumnRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_ICEBERG_COLUMN_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "cosmos_mongo_db_collection", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "metric_timestamps", + "data_quality_metric_dimensions", + "dq_base_dataset_rules", + "dq_base_column_rules", + "dq_reference_dataset_rules", + "dq_reference_column_rules", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_metrics", + "dbt_model_columns", + "column_dbt_model_columns", + "dbt_seed_assets", + "meanings", + "mongo_db_collection", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "table", + "nested_columns", + "parent_column", + "table_partition", + "view", + "calculation_view", + "materialised_view", + "foreign_key_to", + "foreign_key_from", + "queries", + "schema_registry_subjects", + "snowflake_dynamic_table", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_iceberg_column_attrs( + attrs: IcebergColumnAttributes, obj: IcebergColumn +) -> None: + """Populate IcebergColumn-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.iceberg_parent_namespace_qualified_name = ( + obj.iceberg_parent_namespace_qualified_name + ) + attrs.iceberg_namespace_hierarchy = obj.iceberg_namespace_hierarchy + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + attrs.data_type = obj.data_type + attrs.sub_data_type = obj.sub_data_type + attrs.column_compression = obj.column_compression + attrs.column_encoding = obj.column_encoding + attrs.raw_data_type_definition = obj.raw_data_type_definition + attrs.order = obj.order + attrs.nested_column_order = obj.nested_column_order + attrs.nested_column_count = obj.nested_column_count + attrs.column_hierarchy = obj.column_hierarchy + attrs.is_partition = obj.is_partition + attrs.partition_order = obj.partition_order + attrs.is_clustered = obj.is_clustered + attrs.is_primary = obj.is_primary + attrs.is_foreign = obj.is_foreign + attrs.is_indexed = obj.is_indexed + attrs.is_sort = obj.is_sort + attrs.is_dist = obj.is_dist + attrs.is_pinned = obj.is_pinned + attrs.pinned_by = obj.pinned_by + attrs.pinned_at = obj.pinned_at + attrs.precision = obj.precision + attrs.default_value = obj.default_value + attrs.is_nullable = obj.is_nullable + attrs.numeric_scale = obj.numeric_scale + attrs.max_length = obj.max_length + attrs.validations = obj.validations + attrs.parent_column_qualified_name = obj.parent_column_qualified_name + attrs.parent_column_name = obj.parent_column_name + attrs.column_distinct_values_count = obj.column_distinct_values_count + attrs.column_distinct_values_count_long = obj.column_distinct_values_count_long + attrs.column_histogram = obj.column_histogram + attrs.column_max = obj.column_max + attrs.column_min = obj.column_min + attrs.column_mean = obj.column_mean + attrs.column_sum = obj.column_sum + attrs.column_median = obj.column_median + attrs.column_standard_deviation = obj.column_standard_deviation + attrs.column_unique_values_count = obj.column_unique_values_count + attrs.column_unique_values_count_long = obj.column_unique_values_count_long + attrs.column_average = obj.column_average + attrs.column_average_length = obj.column_average_length + attrs.column_duplicate_values_count = obj.column_duplicate_values_count + attrs.column_duplicate_values_count_long = obj.column_duplicate_values_count_long + attrs.column_maximum_string_length = obj.column_maximum_string_length + attrs.column_maxs = obj.column_maxs + attrs.column_minimum_string_length = obj.column_minimum_string_length + attrs.column_mins = obj.column_mins + attrs.column_missing_values_count = obj.column_missing_values_count + attrs.column_missing_values_count_long = obj.column_missing_values_count_long + attrs.column_missing_values_percentage = obj.column_missing_values_percentage + attrs.column_uniqueness_percentage = obj.column_uniqueness_percentage + attrs.column_variance = obj.column_variance + attrs.column_top_values = obj.column_top_values + attrs.column_max_value = obj.column_max_value + attrs.column_min_value = obj.column_min_value + attrs.column_mean_value = obj.column_mean_value + attrs.column_sum_value = obj.column_sum_value + attrs.column_median_value = obj.column_median_value + attrs.column_standard_deviation_value = obj.column_standard_deviation_value + attrs.column_average_value = obj.column_average_value + attrs.column_variance_value = obj.column_variance_value + attrs.column_average_length_value = obj.column_average_length_value + attrs.column_distribution_histogram = obj.column_distribution_histogram + attrs.column_depth_level = obj.column_depth_level + attrs.nosql_collection_name = obj.nosql_collection_name + attrs.nosql_collection_qualified_name = obj.nosql_collection_qualified_name + attrs.column_is_measure = obj.column_is_measure + attrs.column_measure_type = obj.column_measure_type + + +def _extract_iceberg_column_attrs(attrs: IcebergColumnAttributes) -> dict: + """Extract all IcebergColumn attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["iceberg_parent_namespace_qualified_name"] = ( + attrs.iceberg_parent_namespace_qualified_name + ) + result["iceberg_namespace_hierarchy"] = attrs.iceberg_namespace_hierarchy + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + result["data_type"] = attrs.data_type + result["sub_data_type"] = attrs.sub_data_type + result["column_compression"] = attrs.column_compression + result["column_encoding"] = attrs.column_encoding + result["raw_data_type_definition"] = attrs.raw_data_type_definition + result["order"] = attrs.order + result["nested_column_order"] = attrs.nested_column_order + result["nested_column_count"] = attrs.nested_column_count + result["column_hierarchy"] = attrs.column_hierarchy + result["is_partition"] = attrs.is_partition + result["partition_order"] = attrs.partition_order + result["is_clustered"] = attrs.is_clustered + result["is_primary"] = attrs.is_primary + result["is_foreign"] = attrs.is_foreign + result["is_indexed"] = attrs.is_indexed + result["is_sort"] = attrs.is_sort + result["is_dist"] = attrs.is_dist + result["is_pinned"] = attrs.is_pinned + result["pinned_by"] = attrs.pinned_by + result["pinned_at"] = attrs.pinned_at + result["precision"] = attrs.precision + result["default_value"] = attrs.default_value + result["is_nullable"] = attrs.is_nullable + result["numeric_scale"] = attrs.numeric_scale + result["max_length"] = attrs.max_length + result["validations"] = attrs.validations + result["parent_column_qualified_name"] = attrs.parent_column_qualified_name + result["parent_column_name"] = attrs.parent_column_name + result["column_distinct_values_count"] = attrs.column_distinct_values_count + result["column_distinct_values_count_long"] = ( + attrs.column_distinct_values_count_long + ) + result["column_histogram"] = attrs.column_histogram + result["column_max"] = attrs.column_max + result["column_min"] = attrs.column_min + result["column_mean"] = attrs.column_mean + result["column_sum"] = attrs.column_sum + result["column_median"] = attrs.column_median + result["column_standard_deviation"] = attrs.column_standard_deviation + result["column_unique_values_count"] = attrs.column_unique_values_count + result["column_unique_values_count_long"] = attrs.column_unique_values_count_long + result["column_average"] = attrs.column_average + result["column_average_length"] = attrs.column_average_length + result["column_duplicate_values_count"] = attrs.column_duplicate_values_count + result["column_duplicate_values_count_long"] = ( + attrs.column_duplicate_values_count_long + ) + result["column_maximum_string_length"] = attrs.column_maximum_string_length + result["column_maxs"] = attrs.column_maxs + result["column_minimum_string_length"] = attrs.column_minimum_string_length + result["column_mins"] = attrs.column_mins + result["column_missing_values_count"] = attrs.column_missing_values_count + result["column_missing_values_count_long"] = attrs.column_missing_values_count_long + result["column_missing_values_percentage"] = attrs.column_missing_values_percentage + result["column_uniqueness_percentage"] = attrs.column_uniqueness_percentage + result["column_variance"] = attrs.column_variance + result["column_top_values"] = attrs.column_top_values + result["column_max_value"] = attrs.column_max_value + result["column_min_value"] = attrs.column_min_value + result["column_mean_value"] = attrs.column_mean_value + result["column_sum_value"] = attrs.column_sum_value + result["column_median_value"] = attrs.column_median_value + result["column_standard_deviation_value"] = attrs.column_standard_deviation_value + result["column_average_value"] = attrs.column_average_value + result["column_variance_value"] = attrs.column_variance_value + result["column_average_length_value"] = attrs.column_average_length_value + result["column_distribution_histogram"] = attrs.column_distribution_histogram + result["column_depth_level"] = attrs.column_depth_level + result["nosql_collection_name"] = attrs.nosql_collection_name + result["nosql_collection_qualified_name"] = attrs.nosql_collection_qualified_name + result["column_is_measure"] = attrs.column_is_measure + result["column_measure_type"] = attrs.column_measure_type + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _iceberg_column_to_nested(iceberg_column: IcebergColumn) -> IcebergColumnNested: + """Convert flat IcebergColumn to nested format.""" + attrs = IcebergColumnAttributes() + _populate_iceberg_column_attrs(attrs, iceberg_column) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + iceberg_column, _ICEBERG_COLUMN_REL_FIELDS, IcebergColumnRelationshipAttributes + ) + return IcebergColumnNested( + guid=iceberg_column.guid, + type_name=iceberg_column.type_name, + status=iceberg_column.status, + version=iceberg_column.version, + create_time=iceberg_column.create_time, + update_time=iceberg_column.update_time, + created_by=iceberg_column.created_by, + updated_by=iceberg_column.updated_by, + classifications=iceberg_column.classifications, + classification_names=iceberg_column.classification_names, + meanings=iceberg_column.meanings, + labels=iceberg_column.labels, + business_attributes=iceberg_column.business_attributes, + custom_attributes=iceberg_column.custom_attributes, + pending_tasks=iceberg_column.pending_tasks, + proxy=iceberg_column.proxy, + is_incomplete=iceberg_column.is_incomplete, + provenance_type=iceberg_column.provenance_type, + home_id=iceberg_column.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _iceberg_column_from_nested(nested: IcebergColumnNested) -> IcebergColumn: + """Convert nested format to flat IcebergColumn.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else IcebergColumnAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _ICEBERG_COLUMN_REL_FIELDS, + IcebergColumnRelationshipAttributes, + ) + return IcebergColumn( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_iceberg_column_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _iceberg_column_to_nested_bytes( + iceberg_column: IcebergColumn, serde: Serde +) -> bytes: + """Convert flat IcebergColumn to nested JSON bytes.""" + return serde.encode(_iceberg_column_to_nested(iceberg_column)) + + +def _iceberg_column_from_nested_bytes(data: bytes, serde: Serde) -> IcebergColumn: + """Convert nested JSON bytes to flat IcebergColumn.""" + nested = serde.decode(data, IcebergColumnNested) + return _iceberg_column_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +IcebergColumn.ICEBERG_PARENT_NAMESPACE_QUALIFIED_NAME = KeywordField( + "icebergParentNamespaceQualifiedName", "icebergParentNamespaceQualifiedName" +) +IcebergColumn.ICEBERG_NAMESPACE_HIERARCHY = KeywordField( + "icebergNamespaceHierarchy", "icebergNamespaceHierarchy" +) +IcebergColumn.QUERY_COUNT = NumericField("queryCount", "queryCount") +IcebergColumn.QUERY_USER_COUNT = NumericField("queryUserCount", "queryUserCount") +IcebergColumn.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +IcebergColumn.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +IcebergColumn.DATABASE_NAME = KeywordField("databaseName", "databaseName") +IcebergColumn.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +IcebergColumn.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +IcebergColumn.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +IcebergColumn.TABLE_NAME = KeywordField("tableName", "tableName") +IcebergColumn.TABLE_QUALIFIED_NAME = KeywordField( + "tableQualifiedName", "tableQualifiedName" +) +IcebergColumn.VIEW_NAME = KeywordField("viewName", "viewName") +IcebergColumn.VIEW_QUALIFIED_NAME = KeywordField( + "viewQualifiedName", "viewQualifiedName" +) +IcebergColumn.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +IcebergColumn.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +IcebergColumn.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +IcebergColumn.LAST_PROFILED_AT = NumericField("lastProfiledAt", "lastProfiledAt") +IcebergColumn.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +IcebergColumn.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +IcebergColumn.DATA_TYPE = KeywordTextField("dataType", "dataType", "dataType.text") +IcebergColumn.SUB_DATA_TYPE = KeywordField("subDataType", "subDataType") +IcebergColumn.COLUMN_COMPRESSION = KeywordField( + "columnCompression", "columnCompression" +) +IcebergColumn.COLUMN_ENCODING = KeywordField("columnEncoding", "columnEncoding") +IcebergColumn.RAW_DATA_TYPE_DEFINITION = KeywordField( + "rawDataTypeDefinition", "rawDataTypeDefinition" +) +IcebergColumn.ORDER = NumericField("order", "order") +IcebergColumn.NESTED_COLUMN_ORDER = KeywordTextField( + "nestedColumnOrder", "nestedColumnOrder", "nestedColumnOrder.text" +) +IcebergColumn.NESTED_COLUMN_COUNT = NumericField( + "nestedColumnCount", "nestedColumnCount" +) +IcebergColumn.COLUMN_HIERARCHY = KeywordField("columnHierarchy", "columnHierarchy") +IcebergColumn.IS_PARTITION = BooleanField("isPartition", "isPartition") +IcebergColumn.PARTITION_ORDER = NumericField("partitionOrder", "partitionOrder") +IcebergColumn.IS_CLUSTERED = BooleanField("isClustered", "isClustered") +IcebergColumn.IS_PRIMARY = BooleanField("isPrimary", "isPrimary") +IcebergColumn.IS_FOREIGN = BooleanField("isForeign", "isForeign") +IcebergColumn.IS_INDEXED = BooleanField("isIndexed", "isIndexed") +IcebergColumn.IS_SORT = BooleanField("isSort", "isSort") +IcebergColumn.IS_DIST = BooleanField("isDist", "isDist") +IcebergColumn.IS_PINNED = BooleanField("isPinned", "isPinned") +IcebergColumn.PINNED_BY = KeywordField("pinnedBy", "pinnedBy") +IcebergColumn.PINNED_AT = NumericField("pinnedAt", "pinnedAt") +IcebergColumn.PRECISION = NumericField("precision", "precision") +IcebergColumn.DEFAULT_VALUE = KeywordField("defaultValue", "defaultValue") +IcebergColumn.IS_NULLABLE = BooleanField("isNullable", "isNullable") +IcebergColumn.NUMERIC_SCALE = NumericField("numericScale", "numericScale") +IcebergColumn.MAX_LENGTH = NumericField("maxLength", "maxLength") +IcebergColumn.VALIDATIONS = KeywordField("validations", "validations") +IcebergColumn.PARENT_COLUMN_QUALIFIED_NAME = KeywordTextField( + "parentColumnQualifiedName", + "parentColumnQualifiedName", + "parentColumnQualifiedName.text", +) +IcebergColumn.PARENT_COLUMN_NAME = KeywordField("parentColumnName", "parentColumnName") +IcebergColumn.COLUMN_DISTINCT_VALUES_COUNT = NumericField( + "columnDistinctValuesCount", "columnDistinctValuesCount" +) +IcebergColumn.COLUMN_DISTINCT_VALUES_COUNT_LONG = NumericField( + "columnDistinctValuesCountLong", "columnDistinctValuesCountLong" +) +IcebergColumn.COLUMN_HISTOGRAM = KeywordField("columnHistogram", "columnHistogram") +IcebergColumn.COLUMN_MAX = NumericField("columnMax", "columnMax") +IcebergColumn.COLUMN_MIN = NumericField("columnMin", "columnMin") +IcebergColumn.COLUMN_MEAN = NumericField("columnMean", "columnMean") +IcebergColumn.COLUMN_SUM = NumericField("columnSum", "columnSum") +IcebergColumn.COLUMN_MEDIAN = NumericField("columnMedian", "columnMedian") +IcebergColumn.COLUMN_STANDARD_DEVIATION = NumericField( + "columnStandardDeviation", "columnStandardDeviation" +) +IcebergColumn.COLUMN_UNIQUE_VALUES_COUNT = NumericField( + "columnUniqueValuesCount", "columnUniqueValuesCount" +) +IcebergColumn.COLUMN_UNIQUE_VALUES_COUNT_LONG = NumericField( + "columnUniqueValuesCountLong", "columnUniqueValuesCountLong" +) +IcebergColumn.COLUMN_AVERAGE = NumericField("columnAverage", "columnAverage") +IcebergColumn.COLUMN_AVERAGE_LENGTH = NumericField( + "columnAverageLength", "columnAverageLength" +) +IcebergColumn.COLUMN_DUPLICATE_VALUES_COUNT = NumericField( + "columnDuplicateValuesCount", "columnDuplicateValuesCount" +) +IcebergColumn.COLUMN_DUPLICATE_VALUES_COUNT_LONG = NumericField( + "columnDuplicateValuesCountLong", "columnDuplicateValuesCountLong" +) +IcebergColumn.COLUMN_MAXIMUM_STRING_LENGTH = NumericField( + "columnMaximumStringLength", "columnMaximumStringLength" +) +IcebergColumn.COLUMN_MAXS = KeywordField("columnMaxs", "columnMaxs") +IcebergColumn.COLUMN_MINIMUM_STRING_LENGTH = NumericField( + "columnMinimumStringLength", "columnMinimumStringLength" +) +IcebergColumn.COLUMN_MINS = KeywordField("columnMins", "columnMins") +IcebergColumn.COLUMN_MISSING_VALUES_COUNT = NumericField( + "columnMissingValuesCount", "columnMissingValuesCount" +) +IcebergColumn.COLUMN_MISSING_VALUES_COUNT_LONG = NumericField( + "columnMissingValuesCountLong", "columnMissingValuesCountLong" +) +IcebergColumn.COLUMN_MISSING_VALUES_PERCENTAGE = NumericField( + "columnMissingValuesPercentage", "columnMissingValuesPercentage" +) +IcebergColumn.COLUMN_UNIQUENESS_PERCENTAGE = NumericField( + "columnUniquenessPercentage", "columnUniquenessPercentage" +) +IcebergColumn.COLUMN_VARIANCE = NumericField("columnVariance", "columnVariance") +IcebergColumn.COLUMN_TOP_VALUES = KeywordField("columnTopValues", "columnTopValues") +IcebergColumn.COLUMN_MAX_VALUE = NumericField("columnMaxValue", "columnMaxValue") +IcebergColumn.COLUMN_MIN_VALUE = NumericField("columnMinValue", "columnMinValue") +IcebergColumn.COLUMN_MEAN_VALUE = NumericField("columnMeanValue", "columnMeanValue") +IcebergColumn.COLUMN_SUM_VALUE = NumericField("columnSumValue", "columnSumValue") +IcebergColumn.COLUMN_MEDIAN_VALUE = NumericField( + "columnMedianValue", "columnMedianValue" +) +IcebergColumn.COLUMN_STANDARD_DEVIATION_VALUE = NumericField( + "columnStandardDeviationValue", "columnStandardDeviationValue" +) +IcebergColumn.COLUMN_AVERAGE_VALUE = NumericField( + "columnAverageValue", "columnAverageValue" +) +IcebergColumn.COLUMN_VARIANCE_VALUE = NumericField( + "columnVarianceValue", "columnVarianceValue" +) +IcebergColumn.COLUMN_AVERAGE_LENGTH_VALUE = NumericField( + "columnAverageLengthValue", "columnAverageLengthValue" +) +IcebergColumn.COLUMN_DISTRIBUTION_HISTOGRAM = KeywordField( + "columnDistributionHistogram", "columnDistributionHistogram" +) +IcebergColumn.COLUMN_DEPTH_LEVEL = NumericField("columnDepthLevel", "columnDepthLevel") +IcebergColumn.NOSQL_COLLECTION_NAME = KeywordField( + "nosqlCollectionName", "nosqlCollectionName" +) +IcebergColumn.NOSQL_COLLECTION_QUALIFIED_NAME = KeywordField( + "nosqlCollectionQualifiedName", "nosqlCollectionQualifiedName" +) +IcebergColumn.COLUMN_IS_MEASURE = BooleanField("columnIsMeasure", "columnIsMeasure") +IcebergColumn.COLUMN_MEASURE_TYPE = KeywordField( + "columnMeasureType", "columnMeasureType" +) +IcebergColumn.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +IcebergColumn.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +IcebergColumn.ANOMALO_CHECKS = RelationField("anomaloChecks") +IcebergColumn.APPLICATION = RelationField("application") +IcebergColumn.APPLICATION_FIELD = RelationField("applicationField") +IcebergColumn.COSMOS_MONGO_DB_COLLECTION = RelationField("cosmosMongoDBCollection") +IcebergColumn.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +IcebergColumn.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +IcebergColumn.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +IcebergColumn.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +IcebergColumn.METRICS = RelationField("metrics") +IcebergColumn.METRIC_TIMESTAMPS = RelationField("metricTimestamps") +IcebergColumn.DATA_QUALITY_METRIC_DIMENSIONS = RelationField( + "dataQualityMetricDimensions" +) +IcebergColumn.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +IcebergColumn.DQ_BASE_COLUMN_RULES = RelationField("dqBaseColumnRules") +IcebergColumn.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +IcebergColumn.DQ_REFERENCE_COLUMN_RULES = RelationField("dqReferenceColumnRules") +IcebergColumn.DBT_MODELS = RelationField("dbtModels") +IcebergColumn.SQL_DBT_MODELS = RelationField("sqlDbtModels") +IcebergColumn.DBT_TESTS = RelationField("dbtTests") +IcebergColumn.DBT_SOURCES = RelationField("dbtSources") +IcebergColumn.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +IcebergColumn.DBT_METRICS = RelationField("dbtMetrics") +IcebergColumn.DBT_MODEL_COLUMNS = RelationField("dbtModelColumns") +IcebergColumn.COLUMN_DBT_MODEL_COLUMNS = RelationField("columnDbtModelColumns") +IcebergColumn.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +IcebergColumn.MEANINGS = RelationField("meanings") +IcebergColumn.MONGO_DB_COLLECTION = RelationField("mongoDBCollection") +IcebergColumn.MC_MONITORS = RelationField("mcMonitors") +IcebergColumn.MC_INCIDENTS = RelationField("mcIncidents") +IcebergColumn.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +IcebergColumn.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +IcebergColumn.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +IcebergColumn.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +IcebergColumn.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +IcebergColumn.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +IcebergColumn.FILES = RelationField("files") +IcebergColumn.LINKS = RelationField("links") +IcebergColumn.README = RelationField("readme") +IcebergColumn.TABLE = RelationField("table") +IcebergColumn.NESTED_COLUMNS = RelationField("nestedColumns") +IcebergColumn.PARENT_COLUMN = RelationField("parentColumn") +IcebergColumn.TABLE_PARTITION = RelationField("tablePartition") +IcebergColumn.VIEW = RelationField("view") +IcebergColumn.CALCULATION_VIEW = RelationField("calculationView") +IcebergColumn.MATERIALISED_VIEW = RelationField("materialisedView") +IcebergColumn.FOREIGN_KEY_TO = RelationField("foreignKeyTo") +IcebergColumn.FOREIGN_KEY_FROM = RelationField("foreignKeyFrom") +IcebergColumn.QUERIES = RelationField("queries") +IcebergColumn.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +IcebergColumn.SNOWFLAKE_DYNAMIC_TABLE = RelationField("snowflakeDynamicTable") +IcebergColumn.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +IcebergColumn.SODA_CHECKS = RelationField("sodaChecks") +IcebergColumn.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +IcebergColumn.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/iceberg_namespace.py b/pyatlan_v9/model/assets/iceberg_namespace.py new file mode 100644 index 000000000..5955fddf8 --- /dev/null +++ b/pyatlan_v9/model/assets/iceberg_namespace.py @@ -0,0 +1,1112 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +IcebergNamespace asset model with flattened inheritance. + +This module provides: +- IcebergNamespace: Flat asset class (easy to use) +- IcebergNamespaceAttributes: Nested attributes struct (extends AssetAttributes) +- IcebergNamespaceNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .databricks_related import RelatedDatabricksAIModelContext, RelatedDatabricksVolume +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .snowflake_related import ( + RelatedSnowflakeAIModelContext, + RelatedSnowflakeDynamicTable, + RelatedSnowflakePipe, + RelatedSnowflakeSemanticLogicalTable, + RelatedSnowflakeSemanticView, + RelatedSnowflakeStage, + RelatedSnowflakeStream, + RelatedSnowflakeTag, +) +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from .sql_related import ( + RelatedCalculationView, + RelatedDatabase, + RelatedFunction, + RelatedMaterialisedView, + RelatedProcedure, + RelatedTable, + RelatedView, +) +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .iceberg_related import RelatedIcebergNamespace + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class IcebergNamespace(Asset): + """ + Instance of an Iceberg namespace in Atlan. Supports nested namespaces with dot-separated paths. + """ + + ICEBERG_PARENT_NAMESPACE_QUALIFIED_NAME: ClassVar[Any] = None + ICEBERG_NAMESPACE_HIERARCHY: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + TABLE_COUNT: ClassVar[Any] = None + SCHEMA_EXTERNAL_LOCATION: ClassVar[Any] = None + VIEWS_COUNT: ClassVar[Any] = None + LINKED_SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DATABRICKS_AI_MODEL_CONTEXTS: ClassVar[Any] = None + DATABRICKS_VOLUMES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + ICEBERG_SUB_NAMESPACES: ClassVar[Any] = None + ICEBERG_PARENT_NAMESPACE: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + CALCULATION_VIEWS: ClassVar[Any] = None + FUNCTIONS: ClassVar[Any] = None + MATERIALISED_VIEWS: ClassVar[Any] = None + PROCEDURES: ClassVar[Any] = None + DATABASE: ClassVar[Any] = None + TABLES: ClassVar[Any] = None + VIEWS: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_DYNAMIC_TABLES: ClassVar[Any] = None + SNOWFLAKE_PIPES: ClassVar[Any] = None + SNOWFLAKE_STAGES: ClassVar[Any] = None + SNOWFLAKE_STREAMS: ClassVar[Any] = None + SNOWFLAKE_TAGS: ClassVar[Any] = None + SNOWFLAKE_AI_MODEL_CONTEXTS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_VIEWS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "IcebergNamespace" + + iceberg_parent_namespace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the immediate parent namespace in which this asset exists.""" + + iceberg_namespace_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Ordered array of namespace assets with qualified name and name representing the complete namespace hierarchy path for this asset, from immediate parent to root namespace.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + table_count: Union[int, None, UnsetType] = UNSET + """Number of tables in this schema.""" + + schema_external_location: Union[str, None, UnsetType] = UNSET + """External location of this schema, for example: an S3 object location.""" + + views_count: Union[int, None, UnsetType] = UNSET + """Number of views in this schema.""" + + linked_schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the Linked Schema on which this Schema is dependent. This concept is mostly applicable for linked datasets/datasource in Google BigQuery via Analytics Hub Listing""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + databricks_ai_model_contexts: Union[ + List[RelatedDatabricksAIModelContext], None, UnsetType + ] = msgspec.field(default=UNSET, name="databricksAIModelContexts") + """Contexts contained within the schema.""" + + databricks_volumes: Union[List[RelatedDatabricksVolume], None, UnsetType] = UNSET + """Volume contained within the schema.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + iceberg_sub_namespaces: Union[List[RelatedIcebergNamespace], None, UnsetType] = ( + UNSET + ) + """Child namespaces nested within the parent Iceberg Namespace.""" + + iceberg_parent_namespace: Union[RelatedIcebergNamespace, None, UnsetType] = UNSET + """Parent Iceberg Namespace containing the sub-namespaces.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + calculation_views: Union[List[RelatedCalculationView], None, UnsetType] = UNSET + """Calculation views that exist within this schema.""" + + functions: Union[List[RelatedFunction], None, UnsetType] = UNSET + """Functions that exist within this schema.""" + + materialised_views: Union[List[RelatedMaterialisedView], None, UnsetType] = UNSET + """Materialized views that exist within this schema.""" + + procedures: Union[List[RelatedProcedure], None, UnsetType] = UNSET + """Stored procedures that exist within this schema.""" + + database: Union[RelatedDatabase, None, UnsetType] = UNSET + """Database in which this schema exists.""" + + tables: Union[List[RelatedTable], None, UnsetType] = UNSET + """Tables that exist within this schema.""" + + views: Union[List[RelatedView], None, UnsetType] = UNSET + """Views that exist within this schema.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_dynamic_tables: Union[ + List[RelatedSnowflakeDynamicTable], None, UnsetType + ] = UNSET + """Snowflake dynamic tables that exist within this schema.""" + + snowflake_pipes: Union[List[RelatedSnowflakePipe], None, UnsetType] = UNSET + """Snowflake pipes that exist within this schema.""" + + snowflake_stages: Union[List[RelatedSnowflakeStage], None, UnsetType] = UNSET + """Collection of Snowflake stages that are defined and contained within this schema, representing staging areas for data loading and unloading operations.""" + + snowflake_streams: Union[List[RelatedSnowflakeStream], None, UnsetType] = UNSET + """Snowflake streams that exist within this schema.""" + + snowflake_tags: Union[List[RelatedSnowflakeTag], None, UnsetType] = UNSET + """Snowflake tags that exist within this schema.""" + + snowflake_ai_model_contexts: Union[ + List[RelatedSnowflakeAIModelContext], None, UnsetType + ] = msgspec.field(default=UNSET, name="snowflakeAIModelContexts") + """Contexts contained within the schema.""" + + snowflake_semantic_views: Union[ + List[RelatedSnowflakeSemanticView], None, UnsetType + ] = UNSET + """Snowflake semantic views contained in the schema.""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "IcebergNamespace" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _iceberg_namespace_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> IcebergNamespace: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + IcebergNamespace instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _iceberg_namespace_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class IcebergNamespaceAttributes(AssetAttributes): + """IcebergNamespace-specific attributes for nested API format.""" + + iceberg_parent_namespace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the immediate parent namespace in which this asset exists.""" + + iceberg_namespace_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Ordered array of namespace assets with qualified name and name representing the complete namespace hierarchy path for this asset, from immediate parent to root namespace.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + table_count: Union[int, None, UnsetType] = UNSET + """Number of tables in this schema.""" + + schema_external_location: Union[str, None, UnsetType] = UNSET + """External location of this schema, for example: an S3 object location.""" + + views_count: Union[int, None, UnsetType] = UNSET + """Number of views in this schema.""" + + linked_schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the Linked Schema on which this Schema is dependent. This concept is mostly applicable for linked datasets/datasource in Google BigQuery via Analytics Hub Listing""" + + +class IcebergNamespaceRelationshipAttributes(AssetRelationshipAttributes): + """IcebergNamespace-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + databricks_ai_model_contexts: Union[ + List[RelatedDatabricksAIModelContext], None, UnsetType + ] = msgspec.field(default=UNSET, name="databricksAIModelContexts") + """Contexts contained within the schema.""" + + databricks_volumes: Union[List[RelatedDatabricksVolume], None, UnsetType] = UNSET + """Volume contained within the schema.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + iceberg_sub_namespaces: Union[List[RelatedIcebergNamespace], None, UnsetType] = ( + UNSET + ) + """Child namespaces nested within the parent Iceberg Namespace.""" + + iceberg_parent_namespace: Union[RelatedIcebergNamespace, None, UnsetType] = UNSET + """Parent Iceberg Namespace containing the sub-namespaces.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + calculation_views: Union[List[RelatedCalculationView], None, UnsetType] = UNSET + """Calculation views that exist within this schema.""" + + functions: Union[List[RelatedFunction], None, UnsetType] = UNSET + """Functions that exist within this schema.""" + + materialised_views: Union[List[RelatedMaterialisedView], None, UnsetType] = UNSET + """Materialized views that exist within this schema.""" + + procedures: Union[List[RelatedProcedure], None, UnsetType] = UNSET + """Stored procedures that exist within this schema.""" + + database: Union[RelatedDatabase, None, UnsetType] = UNSET + """Database in which this schema exists.""" + + tables: Union[List[RelatedTable], None, UnsetType] = UNSET + """Tables that exist within this schema.""" + + views: Union[List[RelatedView], None, UnsetType] = UNSET + """Views that exist within this schema.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_dynamic_tables: Union[ + List[RelatedSnowflakeDynamicTable], None, UnsetType + ] = UNSET + """Snowflake dynamic tables that exist within this schema.""" + + snowflake_pipes: Union[List[RelatedSnowflakePipe], None, UnsetType] = UNSET + """Snowflake pipes that exist within this schema.""" + + snowflake_stages: Union[List[RelatedSnowflakeStage], None, UnsetType] = UNSET + """Collection of Snowflake stages that are defined and contained within this schema, representing staging areas for data loading and unloading operations.""" + + snowflake_streams: Union[List[RelatedSnowflakeStream], None, UnsetType] = UNSET + """Snowflake streams that exist within this schema.""" + + snowflake_tags: Union[List[RelatedSnowflakeTag], None, UnsetType] = UNSET + """Snowflake tags that exist within this schema.""" + + snowflake_ai_model_contexts: Union[ + List[RelatedSnowflakeAIModelContext], None, UnsetType + ] = msgspec.field(default=UNSET, name="snowflakeAIModelContexts") + """Contexts contained within the schema.""" + + snowflake_semantic_views: Union[ + List[RelatedSnowflakeSemanticView], None, UnsetType + ] = UNSET + """Snowflake semantic views contained in the schema.""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class IcebergNamespaceNested(AssetNested): + """IcebergNamespace in nested API format for high-performance serialization.""" + + attributes: Union[IcebergNamespaceAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + IcebergNamespaceRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + IcebergNamespaceRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + IcebergNamespaceRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_ICEBERG_NAMESPACE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "databricks_ai_model_contexts", + "databricks_volumes", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "meanings", + "iceberg_sub_namespaces", + "iceberg_parent_namespace", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "calculation_views", + "functions", + "materialised_views", + "procedures", + "database", + "tables", + "views", + "schema_registry_subjects", + "snowflake_dynamic_tables", + "snowflake_pipes", + "snowflake_stages", + "snowflake_streams", + "snowflake_tags", + "snowflake_ai_model_contexts", + "snowflake_semantic_views", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_iceberg_namespace_attrs( + attrs: IcebergNamespaceAttributes, obj: IcebergNamespace +) -> None: + """Populate IcebergNamespace-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.iceberg_parent_namespace_qualified_name = ( + obj.iceberg_parent_namespace_qualified_name + ) + attrs.iceberg_namespace_hierarchy = obj.iceberg_namespace_hierarchy + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + attrs.table_count = obj.table_count + attrs.schema_external_location = obj.schema_external_location + attrs.views_count = obj.views_count + attrs.linked_schema_qualified_name = obj.linked_schema_qualified_name + + +def _extract_iceberg_namespace_attrs(attrs: IcebergNamespaceAttributes) -> dict: + """Extract all IcebergNamespace attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["iceberg_parent_namespace_qualified_name"] = ( + attrs.iceberg_parent_namespace_qualified_name + ) + result["iceberg_namespace_hierarchy"] = attrs.iceberg_namespace_hierarchy + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + result["table_count"] = attrs.table_count + result["schema_external_location"] = attrs.schema_external_location + result["views_count"] = attrs.views_count + result["linked_schema_qualified_name"] = attrs.linked_schema_qualified_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _iceberg_namespace_to_nested( + iceberg_namespace: IcebergNamespace, +) -> IcebergNamespaceNested: + """Convert flat IcebergNamespace to nested format.""" + attrs = IcebergNamespaceAttributes() + _populate_iceberg_namespace_attrs(attrs, iceberg_namespace) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + iceberg_namespace, + _ICEBERG_NAMESPACE_REL_FIELDS, + IcebergNamespaceRelationshipAttributes, + ) + return IcebergNamespaceNested( + guid=iceberg_namespace.guid, + type_name=iceberg_namespace.type_name, + status=iceberg_namespace.status, + version=iceberg_namespace.version, + create_time=iceberg_namespace.create_time, + update_time=iceberg_namespace.update_time, + created_by=iceberg_namespace.created_by, + updated_by=iceberg_namespace.updated_by, + classifications=iceberg_namespace.classifications, + classification_names=iceberg_namespace.classification_names, + meanings=iceberg_namespace.meanings, + labels=iceberg_namespace.labels, + business_attributes=iceberg_namespace.business_attributes, + custom_attributes=iceberg_namespace.custom_attributes, + pending_tasks=iceberg_namespace.pending_tasks, + proxy=iceberg_namespace.proxy, + is_incomplete=iceberg_namespace.is_incomplete, + provenance_type=iceberg_namespace.provenance_type, + home_id=iceberg_namespace.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _iceberg_namespace_from_nested(nested: IcebergNamespaceNested) -> IcebergNamespace: + """Convert nested format to flat IcebergNamespace.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else IcebergNamespaceAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _ICEBERG_NAMESPACE_REL_FIELDS, + IcebergNamespaceRelationshipAttributes, + ) + return IcebergNamespace( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_iceberg_namespace_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _iceberg_namespace_to_nested_bytes( + iceberg_namespace: IcebergNamespace, serde: Serde +) -> bytes: + """Convert flat IcebergNamespace to nested JSON bytes.""" + return serde.encode(_iceberg_namespace_to_nested(iceberg_namespace)) + + +def _iceberg_namespace_from_nested_bytes(data: bytes, serde: Serde) -> IcebergNamespace: + """Convert nested JSON bytes to flat IcebergNamespace.""" + nested = serde.decode(data, IcebergNamespaceNested) + return _iceberg_namespace_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, +) + +IcebergNamespace.ICEBERG_PARENT_NAMESPACE_QUALIFIED_NAME = KeywordField( + "icebergParentNamespaceQualifiedName", "icebergParentNamespaceQualifiedName" +) +IcebergNamespace.ICEBERG_NAMESPACE_HIERARCHY = KeywordField( + "icebergNamespaceHierarchy", "icebergNamespaceHierarchy" +) +IcebergNamespace.QUERY_COUNT = NumericField("queryCount", "queryCount") +IcebergNamespace.QUERY_USER_COUNT = NumericField("queryUserCount", "queryUserCount") +IcebergNamespace.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +IcebergNamespace.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +IcebergNamespace.DATABASE_NAME = KeywordField("databaseName", "databaseName") +IcebergNamespace.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +IcebergNamespace.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +IcebergNamespace.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +IcebergNamespace.TABLE_NAME = KeywordField("tableName", "tableName") +IcebergNamespace.TABLE_QUALIFIED_NAME = KeywordField( + "tableQualifiedName", "tableQualifiedName" +) +IcebergNamespace.VIEW_NAME = KeywordField("viewName", "viewName") +IcebergNamespace.VIEW_QUALIFIED_NAME = KeywordField( + "viewQualifiedName", "viewQualifiedName" +) +IcebergNamespace.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +IcebergNamespace.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +IcebergNamespace.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +IcebergNamespace.LAST_PROFILED_AT = NumericField("lastProfiledAt", "lastProfiledAt") +IcebergNamespace.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +IcebergNamespace.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +IcebergNamespace.TABLE_COUNT = NumericField("tableCount", "tableCount") +IcebergNamespace.SCHEMA_EXTERNAL_LOCATION = KeywordField( + "schemaExternalLocation", "schemaExternalLocation" +) +IcebergNamespace.VIEWS_COUNT = NumericField("viewsCount", "viewsCount") +IcebergNamespace.LINKED_SCHEMA_QUALIFIED_NAME = KeywordField( + "linkedSchemaQualifiedName", "linkedSchemaQualifiedName" +) +IcebergNamespace.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +IcebergNamespace.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +IcebergNamespace.ANOMALO_CHECKS = RelationField("anomaloChecks") +IcebergNamespace.APPLICATION = RelationField("application") +IcebergNamespace.APPLICATION_FIELD = RelationField("applicationField") +IcebergNamespace.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +IcebergNamespace.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +IcebergNamespace.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +IcebergNamespace.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +IcebergNamespace.METRICS = RelationField("metrics") +IcebergNamespace.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +IcebergNamespace.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +IcebergNamespace.DATABRICKS_AI_MODEL_CONTEXTS = RelationField( + "databricksAIModelContexts" +) +IcebergNamespace.DATABRICKS_VOLUMES = RelationField("databricksVolumes") +IcebergNamespace.DBT_MODELS = RelationField("dbtModels") +IcebergNamespace.SQL_DBT_MODELS = RelationField("sqlDbtModels") +IcebergNamespace.DBT_TESTS = RelationField("dbtTests") +IcebergNamespace.DBT_SOURCES = RelationField("dbtSources") +IcebergNamespace.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +IcebergNamespace.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +IcebergNamespace.MEANINGS = RelationField("meanings") +IcebergNamespace.ICEBERG_SUB_NAMESPACES = RelationField("icebergSubNamespaces") +IcebergNamespace.ICEBERG_PARENT_NAMESPACE = RelationField("icebergParentNamespace") +IcebergNamespace.MC_MONITORS = RelationField("mcMonitors") +IcebergNamespace.MC_INCIDENTS = RelationField("mcIncidents") +IcebergNamespace.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +IcebergNamespace.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +IcebergNamespace.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +IcebergNamespace.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +IcebergNamespace.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +IcebergNamespace.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +IcebergNamespace.FILES = RelationField("files") +IcebergNamespace.LINKS = RelationField("links") +IcebergNamespace.README = RelationField("readme") +IcebergNamespace.CALCULATION_VIEWS = RelationField("calculationViews") +IcebergNamespace.FUNCTIONS = RelationField("functions") +IcebergNamespace.MATERIALISED_VIEWS = RelationField("materialisedViews") +IcebergNamespace.PROCEDURES = RelationField("procedures") +IcebergNamespace.DATABASE = RelationField("database") +IcebergNamespace.TABLES = RelationField("tables") +IcebergNamespace.VIEWS = RelationField("views") +IcebergNamespace.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +IcebergNamespace.SNOWFLAKE_DYNAMIC_TABLES = RelationField("snowflakeDynamicTables") +IcebergNamespace.SNOWFLAKE_PIPES = RelationField("snowflakePipes") +IcebergNamespace.SNOWFLAKE_STAGES = RelationField("snowflakeStages") +IcebergNamespace.SNOWFLAKE_STREAMS = RelationField("snowflakeStreams") +IcebergNamespace.SNOWFLAKE_TAGS = RelationField("snowflakeTags") +IcebergNamespace.SNOWFLAKE_AI_MODEL_CONTEXTS = RelationField("snowflakeAIModelContexts") +IcebergNamespace.SNOWFLAKE_SEMANTIC_VIEWS = RelationField("snowflakeSemanticViews") +IcebergNamespace.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +IcebergNamespace.SODA_CHECKS = RelationField("sodaChecks") +IcebergNamespace.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +IcebergNamespace.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/iceberg_related.py b/pyatlan_v9/model/assets/iceberg_related.py new file mode 100644 index 000000000..5e31dc37f --- /dev/null +++ b/pyatlan_v9/model/assets/iceberg_related.py @@ -0,0 +1,138 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Iceberg module. + +This module contains all Related{Type} classes for the Iceberg type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Any, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .referenceable_related import RelatedReferenceable +from .sql_related import RelatedSQL + +__all__ = [ + "RelatedIceberg", + "RelatedIcebergCatalog", + "RelatedIcebergNamespace", + "RelatedIcebergTable", + "RelatedIcebergColumn", +] + + +class RelatedIceberg(RelatedSQL): + """ + Related entity reference for Iceberg assets. + + Extends RelatedSQL with Iceberg-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Iceberg" so it serializes correctly + + iceberg_parent_namespace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the immediate parent namespace in which this asset exists.""" + + iceberg_namespace_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Ordered array of namespace assets with qualified name and name representing the complete namespace hierarchy path for this asset, from immediate parent to root namespace.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Iceberg" + + +class RelatedIcebergCatalog(RelatedIceberg): + """ + Related entity reference for IcebergCatalog assets. + + Extends RelatedIceberg with IcebergCatalog-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "IcebergCatalog" so it serializes correctly + + iceberg_catalog_type: Union[str, None, UnsetType] = UNSET + """Type of the Iceberg catalog (e.g., 'hadoop', 'hive', 'nessie', 'rest').""" + + iceberg_uri: Union[str, None, UnsetType] = UNSET + """URI of the Iceberg catalog.""" + + iceberg_warehouse: Union[str, None, UnsetType] = UNSET + """Warehouse associated with this Iceberg catalog.""" + + iceberg_scope: Union[str, None, UnsetType] = UNSET + """Scope of the Iceberg catalog.""" + + iceberg_catalog_properties: Union[Dict[str, str], None, UnsetType] = UNSET + """Properties of the Iceberg catalog.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "IcebergCatalog" + + +class RelatedIcebergNamespace(RelatedIceberg): + """ + Related entity reference for IcebergNamespace assets. + + Extends RelatedIceberg with IcebergNamespace-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "IcebergNamespace" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "IcebergNamespace" + + +class RelatedIcebergTable(RelatedIceberg): + """ + Related entity reference for IcebergTable assets. + + Extends RelatedIceberg with IcebergTable-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "IcebergTable" so it serializes correctly + + iceberg_current_snapshot_id: Union[int, None, UnsetType] = UNSET + """Current snapshot identifier for this Iceberg table.""" + + iceberg_format_version: Union[int, None, UnsetType] = UNSET + """Iceberg format version of the table.""" + + iceberg_table_properties: Union[Dict[str, str], None, UnsetType] = UNSET + """Properties of the Iceberg table.""" + + iceberg_table_partitions: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """Partition information for the Iceberg table.""" + + iceberg_snapshots: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Snapshot information for the Iceberg table.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "IcebergTable" + + +class RelatedIcebergColumn(RelatedIceberg): + """ + Related entity reference for IcebergColumn assets. + + Extends RelatedIceberg with IcebergColumn-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "IcebergColumn" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "IcebergColumn" diff --git a/pyatlan_v9/model/assets/iceberg_table.py b/pyatlan_v9/model/assets/iceberg_table.py new file mode 100644 index 000000000..a1c463967 --- /dev/null +++ b/pyatlan_v9/model/assets/iceberg_table.py @@ -0,0 +1,1257 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +IcebergTable asset model with flattened inheritance. + +This module provides: +- IcebergTable: Flat asset class (easy to use) +- IcebergTableAttributes: Nested attributes struct (extends AssetAttributes) +- IcebergTableNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .snowflake_related import RelatedSnowflakeSemanticLogicalTable +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from .sql_related import ( + RelatedColumn, + RelatedQuery, + RelatedSchema, + RelatedTable, + RelatedTablePartition, +) +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class IcebergTable(Asset): + """ + Instance of an Iceberg table in Atlan. + """ + + ICEBERG_CURRENT_SNAPSHOT_ID: ClassVar[Any] = None + ICEBERG_FORMAT_VERSION: ClassVar[Any] = None + ICEBERG_TABLE_PROPERTIES: ClassVar[Any] = None + ICEBERG_TABLE_PARTITIONS: ClassVar[Any] = None + ICEBERG_SNAPSHOTS: ClassVar[Any] = None + ICEBERG_PARENT_NAMESPACE_QUALIFIED_NAME: ClassVar[Any] = None + ICEBERG_NAMESPACE_HIERARCHY: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + COLUMN_COUNT: ClassVar[Any] = None + ROW_COUNT: ClassVar[Any] = None + SIZE_BYTES: ClassVar[Any] = None + TABLE_OBJECT_COUNT: ClassVar[Any] = None + ALIAS: ClassVar[Any] = None + IS_TEMPORARY: ClassVar[Any] = None + IS_QUERY_PREVIEW: ClassVar[Any] = None + QUERY_PREVIEW_CONFIG: ClassVar[Any] = None + EXTERNAL_LOCATION: ClassVar[Any] = None + EXTERNAL_LOCATION_REGION: ClassVar[Any] = None + EXTERNAL_LOCATION_FORMAT: ClassVar[Any] = None + IS_PARTITIONED: ClassVar[Any] = None + PARTITION_STRATEGY: ClassVar[Any] = None + PARTITION_COUNT: ClassVar[Any] = None + TABLE_DEFINITION: ClassVar[Any] = None + PARTITION_LIST: ClassVar[Any] = None + IS_SHARDED: ClassVar[Any] = None + TABLE_TYPE: ClassVar[Any] = None + ICEBERG_CATALOG_NAME: ClassVar[Any] = None + ICEBERG_TABLE_TYPE: ClassVar[Any] = None + ICEBERG_CATALOG_SOURCE: ClassVar[Any] = None + ICEBERG_CATALOG_TABLE_NAME: ClassVar[Any] = None + TABLE_IMPALA_PARAMETERS: ClassVar[Any] = None + ICEBERG_CATALOG_TABLE_NAMESPACE: ClassVar[Any] = None + TABLE_EXTERNAL_VOLUME_NAME: ClassVar[Any] = None + ICEBERG_TABLE_BASE_LOCATION: ClassVar[Any] = None + TABLE_RETENTION_TIME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + COLUMNS: ClassVar[Any] = None + QUERIES: ClassVar[Any] = None + ATLAN_SCHEMA: ClassVar[Any] = None + DIMENSIONS: ClassVar[Any] = None + FACTS: ClassVar[Any] = None + PARTITIONS: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "IcebergTable" + + iceberg_current_snapshot_id: Union[int, None, UnsetType] = UNSET + """Current snapshot identifier for this Iceberg table.""" + + iceberg_format_version: Union[int, None, UnsetType] = UNSET + """Iceberg format version of the table.""" + + iceberg_table_properties: Union[Dict[str, str], None, UnsetType] = UNSET + """Properties of the Iceberg table.""" + + iceberg_table_partitions: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """Partition information for the Iceberg table.""" + + iceberg_snapshots: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Snapshot information for the Iceberg table.""" + + iceberg_parent_namespace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the immediate parent namespace in which this asset exists.""" + + iceberg_namespace_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Ordered array of namespace assets with qualified name and name representing the complete namespace hierarchy path for this asset, from immediate parent to root namespace.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this table.""" + + row_count: Union[int, None, UnsetType] = UNSET + """Number of rows in this table.""" + + size_bytes: Union[int, None, UnsetType] = UNSET + """Size of this table, in bytes.""" + + table_object_count: Union[int, None, UnsetType] = UNSET + """Number of objects in this table.""" + + alias: Union[str, None, UnsetType] = UNSET + """Alias for this table.""" + + is_temporary: Union[bool, None, UnsetType] = UNSET + """Whether this table is temporary (true) or not (false).""" + + is_query_preview: Union[bool, None, UnsetType] = UNSET + """Whether preview queries are allowed for this table (true) or not (false).""" + + query_preview_config: Union[Dict[str, str], None, UnsetType] = UNSET + """Configuration for preview queries.""" + + external_location: Union[str, None, UnsetType] = UNSET + """External location of this table, for example: an S3 object location.""" + + external_location_region: Union[str, None, UnsetType] = UNSET + """Region of the external location of this table, for example: S3 region.""" + + external_location_format: Union[str, None, UnsetType] = UNSET + """Format of the external location of this table, for example: JSON, CSV, PARQUET, etc.""" + + is_partitioned: Union[bool, None, UnsetType] = UNSET + """Whether this table is partitioned (true) or not (false).""" + + partition_strategy: Union[str, None, UnsetType] = UNSET + """Partition strategy for this table.""" + + partition_count: Union[int, None, UnsetType] = UNSET + """Number of partitions in this table.""" + + table_definition: Union[str, None, UnsetType] = UNSET + """Definition of the table.""" + + partition_list: Union[str, None, UnsetType] = UNSET + """List of partitions in this table.""" + + is_sharded: Union[bool, None, UnsetType] = UNSET + """Whether this table is a sharded table (true) or not (false).""" + + table_type: Union[str, None, UnsetType] = UNSET + """Type of the table.""" + + iceberg_catalog_name: Union[str, None, UnsetType] = UNSET + """Iceberg table catalog name (can be any user defined name)""" + + iceberg_table_type: Union[str, None, UnsetType] = UNSET + """Iceberg table type (managed vs unmanaged)""" + + iceberg_catalog_source: Union[str, None, UnsetType] = UNSET + """Iceberg table catalog type (glue, polaris, snowflake)""" + + iceberg_catalog_table_name: Union[str, None, UnsetType] = UNSET + """Catalog table name (actual table name on the catalog side).""" + + table_impala_parameters: Union[Dict[str, str], None, UnsetType] = UNSET + """Extra attributes for Impala""" + + iceberg_catalog_table_namespace: Union[str, None, UnsetType] = UNSET + """Catalog table namespace (actual database name on the catalog side).""" + + table_external_volume_name: Union[str, None, UnsetType] = UNSET + """External volume name for the table.""" + + iceberg_table_base_location: Union[str, None, UnsetType] = UNSET + """Iceberg table base location inside the external volume.""" + + table_retention_time: Union[int, None, UnsetType] = UNSET + """Data retention time in days.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Columns that exist within this table.""" + + queries: Union[List[RelatedQuery], None, UnsetType] = UNSET + """Queries that access this table.""" + + atlan_schema: Union[RelatedSchema, None, UnsetType] = UNSET + """Schema in which this table exists.""" + + dimensions: Union[List[RelatedTable], None, UnsetType] = UNSET + """""" + + facts: Union[List[RelatedTable], None, UnsetType] = UNSET + """""" + + partitions: Union[List[RelatedTablePartition], None, UnsetType] = UNSET + """Partitions that exist within this table.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "IcebergTable" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _iceberg_table_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> IcebergTable: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + IcebergTable instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _iceberg_table_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class IcebergTableAttributes(AssetAttributes): + """IcebergTable-specific attributes for nested API format.""" + + iceberg_current_snapshot_id: Union[int, None, UnsetType] = UNSET + """Current snapshot identifier for this Iceberg table.""" + + iceberg_format_version: Union[int, None, UnsetType] = UNSET + """Iceberg format version of the table.""" + + iceberg_table_properties: Union[Dict[str, str], None, UnsetType] = UNSET + """Properties of the Iceberg table.""" + + iceberg_table_partitions: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """Partition information for the Iceberg table.""" + + iceberg_snapshots: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Snapshot information for the Iceberg table.""" + + iceberg_parent_namespace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the immediate parent namespace in which this asset exists.""" + + iceberg_namespace_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Ordered array of namespace assets with qualified name and name representing the complete namespace hierarchy path for this asset, from immediate parent to root namespace.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this table.""" + + row_count: Union[int, None, UnsetType] = UNSET + """Number of rows in this table.""" + + size_bytes: Union[int, None, UnsetType] = UNSET + """Size of this table, in bytes.""" + + table_object_count: Union[int, None, UnsetType] = UNSET + """Number of objects in this table.""" + + alias: Union[str, None, UnsetType] = UNSET + """Alias for this table.""" + + is_temporary: Union[bool, None, UnsetType] = UNSET + """Whether this table is temporary (true) or not (false).""" + + is_query_preview: Union[bool, None, UnsetType] = UNSET + """Whether preview queries are allowed for this table (true) or not (false).""" + + query_preview_config: Union[Dict[str, str], None, UnsetType] = UNSET + """Configuration for preview queries.""" + + external_location: Union[str, None, UnsetType] = UNSET + """External location of this table, for example: an S3 object location.""" + + external_location_region: Union[str, None, UnsetType] = UNSET + """Region of the external location of this table, for example: S3 region.""" + + external_location_format: Union[str, None, UnsetType] = UNSET + """Format of the external location of this table, for example: JSON, CSV, PARQUET, etc.""" + + is_partitioned: Union[bool, None, UnsetType] = UNSET + """Whether this table is partitioned (true) or not (false).""" + + partition_strategy: Union[str, None, UnsetType] = UNSET + """Partition strategy for this table.""" + + partition_count: Union[int, None, UnsetType] = UNSET + """Number of partitions in this table.""" + + table_definition: Union[str, None, UnsetType] = UNSET + """Definition of the table.""" + + partition_list: Union[str, None, UnsetType] = UNSET + """List of partitions in this table.""" + + is_sharded: Union[bool, None, UnsetType] = UNSET + """Whether this table is a sharded table (true) or not (false).""" + + table_type: Union[str, None, UnsetType] = UNSET + """Type of the table.""" + + iceberg_catalog_name: Union[str, None, UnsetType] = UNSET + """Iceberg table catalog name (can be any user defined name)""" + + iceberg_table_type: Union[str, None, UnsetType] = UNSET + """Iceberg table type (managed vs unmanaged)""" + + iceberg_catalog_source: Union[str, None, UnsetType] = UNSET + """Iceberg table catalog type (glue, polaris, snowflake)""" + + iceberg_catalog_table_name: Union[str, None, UnsetType] = UNSET + """Catalog table name (actual table name on the catalog side).""" + + table_impala_parameters: Union[Dict[str, str], None, UnsetType] = UNSET + """Extra attributes for Impala""" + + iceberg_catalog_table_namespace: Union[str, None, UnsetType] = UNSET + """Catalog table namespace (actual database name on the catalog side).""" + + table_external_volume_name: Union[str, None, UnsetType] = UNSET + """External volume name for the table.""" + + iceberg_table_base_location: Union[str, None, UnsetType] = UNSET + """Iceberg table base location inside the external volume.""" + + table_retention_time: Union[int, None, UnsetType] = UNSET + """Data retention time in days.""" + + +class IcebergTableRelationshipAttributes(AssetRelationshipAttributes): + """IcebergTable-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Columns that exist within this table.""" + + queries: Union[List[RelatedQuery], None, UnsetType] = UNSET + """Queries that access this table.""" + + atlan_schema: Union[RelatedSchema, None, UnsetType] = UNSET + """Schema in which this table exists.""" + + dimensions: Union[List[RelatedTable], None, UnsetType] = UNSET + """""" + + facts: Union[List[RelatedTable], None, UnsetType] = UNSET + """""" + + partitions: Union[List[RelatedTablePartition], None, UnsetType] = UNSET + """Partitions that exist within this table.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class IcebergTableNested(AssetNested): + """IcebergTable in nested API format for high-performance serialization.""" + + attributes: Union[IcebergTableAttributes, UnsetType] = UNSET + relationship_attributes: Union[IcebergTableRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + IcebergTableRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + IcebergTableRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_ICEBERG_TABLE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "columns", + "queries", + "atlan_schema", + "dimensions", + "facts", + "partitions", + "schema_registry_subjects", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_iceberg_table_attrs( + attrs: IcebergTableAttributes, obj: IcebergTable +) -> None: + """Populate IcebergTable-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.iceberg_current_snapshot_id = obj.iceberg_current_snapshot_id + attrs.iceberg_format_version = obj.iceberg_format_version + attrs.iceberg_table_properties = obj.iceberg_table_properties + attrs.iceberg_table_partitions = obj.iceberg_table_partitions + attrs.iceberg_snapshots = obj.iceberg_snapshots + attrs.iceberg_parent_namespace_qualified_name = ( + obj.iceberg_parent_namespace_qualified_name + ) + attrs.iceberg_namespace_hierarchy = obj.iceberg_namespace_hierarchy + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + attrs.column_count = obj.column_count + attrs.row_count = obj.row_count + attrs.size_bytes = obj.size_bytes + attrs.table_object_count = obj.table_object_count + attrs.alias = obj.alias + attrs.is_temporary = obj.is_temporary + attrs.is_query_preview = obj.is_query_preview + attrs.query_preview_config = obj.query_preview_config + attrs.external_location = obj.external_location + attrs.external_location_region = obj.external_location_region + attrs.external_location_format = obj.external_location_format + attrs.is_partitioned = obj.is_partitioned + attrs.partition_strategy = obj.partition_strategy + attrs.partition_count = obj.partition_count + attrs.table_definition = obj.table_definition + attrs.partition_list = obj.partition_list + attrs.is_sharded = obj.is_sharded + attrs.table_type = obj.table_type + attrs.iceberg_catalog_name = obj.iceberg_catalog_name + attrs.iceberg_table_type = obj.iceberg_table_type + attrs.iceberg_catalog_source = obj.iceberg_catalog_source + attrs.iceberg_catalog_table_name = obj.iceberg_catalog_table_name + attrs.table_impala_parameters = obj.table_impala_parameters + attrs.iceberg_catalog_table_namespace = obj.iceberg_catalog_table_namespace + attrs.table_external_volume_name = obj.table_external_volume_name + attrs.iceberg_table_base_location = obj.iceberg_table_base_location + attrs.table_retention_time = obj.table_retention_time + + +def _extract_iceberg_table_attrs(attrs: IcebergTableAttributes) -> dict: + """Extract all IcebergTable attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["iceberg_current_snapshot_id"] = attrs.iceberg_current_snapshot_id + result["iceberg_format_version"] = attrs.iceberg_format_version + result["iceberg_table_properties"] = attrs.iceberg_table_properties + result["iceberg_table_partitions"] = attrs.iceberg_table_partitions + result["iceberg_snapshots"] = attrs.iceberg_snapshots + result["iceberg_parent_namespace_qualified_name"] = ( + attrs.iceberg_parent_namespace_qualified_name + ) + result["iceberg_namespace_hierarchy"] = attrs.iceberg_namespace_hierarchy + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + result["column_count"] = attrs.column_count + result["row_count"] = attrs.row_count + result["size_bytes"] = attrs.size_bytes + result["table_object_count"] = attrs.table_object_count + result["alias"] = attrs.alias + result["is_temporary"] = attrs.is_temporary + result["is_query_preview"] = attrs.is_query_preview + result["query_preview_config"] = attrs.query_preview_config + result["external_location"] = attrs.external_location + result["external_location_region"] = attrs.external_location_region + result["external_location_format"] = attrs.external_location_format + result["is_partitioned"] = attrs.is_partitioned + result["partition_strategy"] = attrs.partition_strategy + result["partition_count"] = attrs.partition_count + result["table_definition"] = attrs.table_definition + result["partition_list"] = attrs.partition_list + result["is_sharded"] = attrs.is_sharded + result["table_type"] = attrs.table_type + result["iceberg_catalog_name"] = attrs.iceberg_catalog_name + result["iceberg_table_type"] = attrs.iceberg_table_type + result["iceberg_catalog_source"] = attrs.iceberg_catalog_source + result["iceberg_catalog_table_name"] = attrs.iceberg_catalog_table_name + result["table_impala_parameters"] = attrs.table_impala_parameters + result["iceberg_catalog_table_namespace"] = attrs.iceberg_catalog_table_namespace + result["table_external_volume_name"] = attrs.table_external_volume_name + result["iceberg_table_base_location"] = attrs.iceberg_table_base_location + result["table_retention_time"] = attrs.table_retention_time + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _iceberg_table_to_nested(iceberg_table: IcebergTable) -> IcebergTableNested: + """Convert flat IcebergTable to nested format.""" + attrs = IcebergTableAttributes() + _populate_iceberg_table_attrs(attrs, iceberg_table) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + iceberg_table, _ICEBERG_TABLE_REL_FIELDS, IcebergTableRelationshipAttributes + ) + return IcebergTableNested( + guid=iceberg_table.guid, + type_name=iceberg_table.type_name, + status=iceberg_table.status, + version=iceberg_table.version, + create_time=iceberg_table.create_time, + update_time=iceberg_table.update_time, + created_by=iceberg_table.created_by, + updated_by=iceberg_table.updated_by, + classifications=iceberg_table.classifications, + classification_names=iceberg_table.classification_names, + meanings=iceberg_table.meanings, + labels=iceberg_table.labels, + business_attributes=iceberg_table.business_attributes, + custom_attributes=iceberg_table.custom_attributes, + pending_tasks=iceberg_table.pending_tasks, + proxy=iceberg_table.proxy, + is_incomplete=iceberg_table.is_incomplete, + provenance_type=iceberg_table.provenance_type, + home_id=iceberg_table.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _iceberg_table_from_nested(nested: IcebergTableNested) -> IcebergTable: + """Convert nested format to flat IcebergTable.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else IcebergTableAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _ICEBERG_TABLE_REL_FIELDS, + IcebergTableRelationshipAttributes, + ) + return IcebergTable( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_iceberg_table_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _iceberg_table_to_nested_bytes(iceberg_table: IcebergTable, serde: Serde) -> bytes: + """Convert flat IcebergTable to nested JSON bytes.""" + return serde.encode(_iceberg_table_to_nested(iceberg_table)) + + +def _iceberg_table_from_nested_bytes(data: bytes, serde: Serde) -> IcebergTable: + """Convert nested JSON bytes to flat IcebergTable.""" + nested = serde.decode(data, IcebergTableNested) + return _iceberg_table_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, +) + +IcebergTable.ICEBERG_CURRENT_SNAPSHOT_ID = NumericField( + "icebergCurrentSnapshotId", "icebergCurrentSnapshotId" +) +IcebergTable.ICEBERG_FORMAT_VERSION = NumericField( + "icebergFormatVersion", "icebergFormatVersion" +) +IcebergTable.ICEBERG_TABLE_PROPERTIES = KeywordField( + "icebergTableProperties", "icebergTableProperties" +) +IcebergTable.ICEBERG_TABLE_PARTITIONS = KeywordField( + "icebergTablePartitions", "icebergTablePartitions" +) +IcebergTable.ICEBERG_SNAPSHOTS = KeywordField("icebergSnapshots", "icebergSnapshots") +IcebergTable.ICEBERG_PARENT_NAMESPACE_QUALIFIED_NAME = KeywordField( + "icebergParentNamespaceQualifiedName", "icebergParentNamespaceQualifiedName" +) +IcebergTable.ICEBERG_NAMESPACE_HIERARCHY = KeywordField( + "icebergNamespaceHierarchy", "icebergNamespaceHierarchy" +) +IcebergTable.QUERY_COUNT = NumericField("queryCount", "queryCount") +IcebergTable.QUERY_USER_COUNT = NumericField("queryUserCount", "queryUserCount") +IcebergTable.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +IcebergTable.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +IcebergTable.DATABASE_NAME = KeywordField("databaseName", "databaseName") +IcebergTable.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +IcebergTable.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +IcebergTable.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +IcebergTable.TABLE_NAME = KeywordField("tableName", "tableName") +IcebergTable.TABLE_QUALIFIED_NAME = KeywordField( + "tableQualifiedName", "tableQualifiedName" +) +IcebergTable.VIEW_NAME = KeywordField("viewName", "viewName") +IcebergTable.VIEW_QUALIFIED_NAME = KeywordField( + "viewQualifiedName", "viewQualifiedName" +) +IcebergTable.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +IcebergTable.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +IcebergTable.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +IcebergTable.LAST_PROFILED_AT = NumericField("lastProfiledAt", "lastProfiledAt") +IcebergTable.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +IcebergTable.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +IcebergTable.COLUMN_COUNT = NumericField("columnCount", "columnCount") +IcebergTable.ROW_COUNT = NumericField("rowCount", "rowCount") +IcebergTable.SIZE_BYTES = NumericField("sizeBytes", "sizeBytes") +IcebergTable.TABLE_OBJECT_COUNT = NumericField("tableObjectCount", "tableObjectCount") +IcebergTable.ALIAS = KeywordField("alias", "alias") +IcebergTable.IS_TEMPORARY = BooleanField("isTemporary", "isTemporary") +IcebergTable.IS_QUERY_PREVIEW = BooleanField("isQueryPreview", "isQueryPreview") +IcebergTable.QUERY_PREVIEW_CONFIG = KeywordField( + "queryPreviewConfig", "queryPreviewConfig" +) +IcebergTable.EXTERNAL_LOCATION = KeywordField("externalLocation", "externalLocation") +IcebergTable.EXTERNAL_LOCATION_REGION = KeywordField( + "externalLocationRegion", "externalLocationRegion" +) +IcebergTable.EXTERNAL_LOCATION_FORMAT = KeywordField( + "externalLocationFormat", "externalLocationFormat" +) +IcebergTable.IS_PARTITIONED = BooleanField("isPartitioned", "isPartitioned") +IcebergTable.PARTITION_STRATEGY = KeywordField("partitionStrategy", "partitionStrategy") +IcebergTable.PARTITION_COUNT = NumericField("partitionCount", "partitionCount") +IcebergTable.TABLE_DEFINITION = KeywordField("tableDefinition", "tableDefinition") +IcebergTable.PARTITION_LIST = KeywordField("partitionList", "partitionList") +IcebergTable.IS_SHARDED = BooleanField("isSharded", "isSharded") +IcebergTable.TABLE_TYPE = KeywordField("tableType", "tableType") +IcebergTable.ICEBERG_CATALOG_NAME = KeywordField( + "icebergCatalogName", "icebergCatalogName" +) +IcebergTable.ICEBERG_TABLE_TYPE = KeywordField("icebergTableType", "icebergTableType") +IcebergTable.ICEBERG_CATALOG_SOURCE = KeywordField( + "icebergCatalogSource", "icebergCatalogSource" +) +IcebergTable.ICEBERG_CATALOG_TABLE_NAME = KeywordField( + "icebergCatalogTableName", "icebergCatalogTableName" +) +IcebergTable.TABLE_IMPALA_PARAMETERS = KeywordField( + "tableImpalaParameters", "tableImpalaParameters" +) +IcebergTable.ICEBERG_CATALOG_TABLE_NAMESPACE = KeywordField( + "icebergCatalogTableNamespace", "icebergCatalogTableNamespace" +) +IcebergTable.TABLE_EXTERNAL_VOLUME_NAME = KeywordField( + "tableExternalVolumeName", "tableExternalVolumeName" +) +IcebergTable.ICEBERG_TABLE_BASE_LOCATION = KeywordField( + "icebergTableBaseLocation", "icebergTableBaseLocation" +) +IcebergTable.TABLE_RETENTION_TIME = NumericField( + "tableRetentionTime", "tableRetentionTime" +) +IcebergTable.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +IcebergTable.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +IcebergTable.ANOMALO_CHECKS = RelationField("anomaloChecks") +IcebergTable.APPLICATION = RelationField("application") +IcebergTable.APPLICATION_FIELD = RelationField("applicationField") +IcebergTable.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +IcebergTable.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +IcebergTable.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +IcebergTable.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +IcebergTable.METRICS = RelationField("metrics") +IcebergTable.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +IcebergTable.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +IcebergTable.DBT_MODELS = RelationField("dbtModels") +IcebergTable.SQL_DBT_MODELS = RelationField("sqlDbtModels") +IcebergTable.DBT_TESTS = RelationField("dbtTests") +IcebergTable.DBT_SOURCES = RelationField("dbtSources") +IcebergTable.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +IcebergTable.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +IcebergTable.MEANINGS = RelationField("meanings") +IcebergTable.MC_MONITORS = RelationField("mcMonitors") +IcebergTable.MC_INCIDENTS = RelationField("mcIncidents") +IcebergTable.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +IcebergTable.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +IcebergTable.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +IcebergTable.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +IcebergTable.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +IcebergTable.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +IcebergTable.FILES = RelationField("files") +IcebergTable.LINKS = RelationField("links") +IcebergTable.README = RelationField("readme") +IcebergTable.COLUMNS = RelationField("columns") +IcebergTable.QUERIES = RelationField("queries") +IcebergTable.ATLAN_SCHEMA = RelationField("atlanSchema") +IcebergTable.DIMENSIONS = RelationField("dimensions") +IcebergTable.FACTS = RelationField("facts") +IcebergTable.PARTITIONS = RelationField("partitions") +IcebergTable.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +IcebergTable.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +IcebergTable.SODA_CHECKS = RelationField("sodaChecks") +IcebergTable.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +IcebergTable.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/incident.py b/pyatlan_v9/model/assets/incident.py new file mode 100644 index 000000000..aaf2c43c4 --- /dev/null +++ b/pyatlan_v9/model/assets/incident.py @@ -0,0 +1,2933 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Incident asset model with flattened inheritance. + +This module provides: +- Incident: Flat asset class (easy to use) +- IncidentAttributes: Nested attributes struct (extends AssetAttributes) +- IncidentNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Set, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .referenceable import ( + _REFERENCEABLE_REL_FIELDS, + Referenceable, + ReferenceableAttributes, + ReferenceableNested, + ReferenceableRelationshipAttributes, + _extract_referenceable_attrs, + _populate_referenceable_attrs, +) +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +class Incident(Referenceable): + """ + Base class for Incident assets. + """ + + ASSET_SEVERITY: ClassVar[Any] = None + NAME: ClassVar[Any] = None + DISPLAY_NAME: ClassVar[Any] = None + DESCRIPTION: ClassVar[Any] = None + ASSET_SOURCE_README: ClassVar[Any] = None + USER_DESCRIPTION: ClassVar[Any] = None + ASSET_AI_GENERATED_DESCRIPTION: ClassVar[Any] = None + ASSET_AI_GENERATED_DESCRIPTION_CONFIDENCE: ClassVar[Any] = None + ASSET_AI_GENERATED_DESCRIPTION_REASONING: ClassVar[Any] = None + TENANT_ID: ClassVar[Any] = None + CERTIFICATE_STATUS: ClassVar[Any] = None + CERTIFICATE_STATUS_MESSAGE: ClassVar[Any] = None + CERTIFICATE_UPDATED_BY: ClassVar[Any] = None + CERTIFICATE_UPDATED_AT: ClassVar[Any] = None + ANNOUNCEMENT_TITLE: ClassVar[Any] = None + ANNOUNCEMENT_MESSAGE: ClassVar[Any] = None + ANNOUNCEMENT_TYPE: ClassVar[Any] = None + ANNOUNCEMENT_UPDATED_AT: ClassVar[Any] = None + ANNOUNCEMENT_UPDATED_BY: ClassVar[Any] = None + OWNER_USERS: ClassVar[Any] = None + OWNER_GROUPS: ClassVar[Any] = None + ADMIN_USERS: ClassVar[Any] = None + ADMIN_GROUPS: ClassVar[Any] = None + VIEWER_USERS: ClassVar[Any] = None + VIEWER_GROUPS: ClassVar[Any] = None + CONNECTOR_NAME: ClassVar[Any] = None + CONNECTION_NAME: ClassVar[Any] = None + CONNECTION_QUALIFIED_NAME: ClassVar[Any] = None + HAS_LINEAGE: ClassVar[Any] = None + IS_DISCOVERABLE: ClassVar[Any] = None + IS_EDITABLE: ClassVar[Any] = None + SUB_TYPE: ClassVar[Any] = None + VIEW_SCORE: ClassVar[Any] = None + POPULARITY_SCORE: ClassVar[Any] = None + SOURCE_OWNERS: ClassVar[Any] = None + ASSET_SOURCE_ID: ClassVar[Any] = None + SOURCE_CREATED_BY: ClassVar[Any] = None + SOURCE_CREATED_AT: ClassVar[Any] = None + SOURCE_UPDATED_AT: ClassVar[Any] = None + SOURCE_UPDATED_BY: ClassVar[Any] = None + SOURCE_URL: ClassVar[Any] = None + SOURCE_EMBED_URL: ClassVar[Any] = None + LAST_SYNC_WORKFLOW_NAME: ClassVar[Any] = None + LAST_SYNC_RUN_AT: ClassVar[Any] = None + LAST_SYNC_RUN: ClassVar[Any] = None + ADMIN_ROLES: ClassVar[Any] = None + SOURCE_READ_COUNT: ClassVar[Any] = None + SOURCE_READ_USER_COUNT: ClassVar[Any] = None + SOURCE_LAST_READ_AT: ClassVar[Any] = None + LAST_ROW_CHANGED_AT: ClassVar[Any] = None + SOURCE_TOTAL_COST: ClassVar[Any] = None + SOURCE_COST_UNIT: ClassVar[Any] = None + SOURCE_READ_QUERY_COST: ClassVar[Any] = None + SOURCE_READ_RECENT_USER_LIST: ClassVar[Any] = None + SOURCE_READ_RECENT_USER_RECORD_LIST: ClassVar[Any] = None + SOURCE_READ_TOP_USER_LIST: ClassVar[Any] = None + SOURCE_READ_TOP_USER_RECORD_LIST: ClassVar[Any] = None + SOURCE_READ_POPULAR_QUERY_RECORD_LIST: ClassVar[Any] = None + SOURCE_READ_EXPENSIVE_QUERY_RECORD_LIST: ClassVar[Any] = None + SOURCE_READ_SLOW_QUERY_RECORD_LIST: ClassVar[Any] = None + SOURCE_QUERY_COMPUTE_COST_LIST: ClassVar[Any] = None + SOURCE_QUERY_COMPUTE_COST_RECORD_LIST: ClassVar[Any] = None + DBT_QUALIFIED_NAME: ClassVar[Any] = None + ASSET_DBT_WORKFLOW_LAST_UPDATED: ClassVar[Any] = None + ASSET_DBT_ALIAS: ClassVar[Any] = None + ASSET_DBT_META: ClassVar[Any] = None + ASSET_DBT_UNIQUE_ID: ClassVar[Any] = None + ASSET_DBT_ACCOUNT_NAME: ClassVar[Any] = None + ASSET_DBT_PROJECT_NAME: ClassVar[Any] = None + ASSET_DBT_PACKAGE_NAME: ClassVar[Any] = None + ASSET_DBT_JOB_NAME: ClassVar[Any] = None + ASSET_DBT_JOB_SCHEDULE: ClassVar[Any] = None + ASSET_DBT_JOB_STATUS: ClassVar[Any] = None + ASSET_DBT_TEST_STATUS: ClassVar[Any] = None + ASSET_DBT_JOB_SCHEDULE_CRON_HUMANIZED: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_URL: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_CREATED_AT: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_UPDATED_AT: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_DEQUED_AT: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_STARTED_AT: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_TOTAL_DURATION: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_TOTAL_DURATION_HUMANIZED: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_QUEUED_DURATION: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_QUEUED_DURATION_HUMANIZED: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_RUN_DURATION: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_RUN_DURATION_HUMANIZED: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_GIT_BRANCH: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_GIT_SHA: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_STATUS_MESSAGE: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_OWNER_THREAD_ID: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_EXECUTED_BY_THREAD_ID: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_ARTIFACTS_SAVED: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_ARTIFACT_S3_PATH: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_HAS_DOCS_GENERATED: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_HAS_SOURCES_GENERATED: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_NOTIFICATIONS_SENT: ClassVar[Any] = None + ASSET_DBT_JOB_NEXT_RUN: ClassVar[Any] = None + ASSET_DBT_JOB_NEXT_RUN_HUMANIZED: ClassVar[Any] = None + ASSET_DBT_ENVIRONMENT_NAME: ClassVar[Any] = None + ASSET_DBT_ENVIRONMENT_DBT_VERSION: ClassVar[Any] = None + ASSET_DBT_TAGS: ClassVar[Any] = None + ASSET_DBT_SEMANTIC_LAYER_PROXY_URL: ClassVar[Any] = None + ASSET_DBT_SOURCE_FRESHNESS_CRITERIA: ClassVar[Any] = None + SAMPLE_DATA_URL: ClassVar[Any] = None + ASSET_TAGS: ClassVar[Any] = None + ASSET_MC_INCIDENT_NAMES: ClassVar[Any] = None + ASSET_MC_INCIDENT_QUALIFIED_NAMES: ClassVar[Any] = None + ASSET_MC_ALERT_QUALIFIED_NAMES: ClassVar[Any] = None + ASSET_MC_MONITOR_NAMES: ClassVar[Any] = None + ASSET_MC_MONITOR_QUALIFIED_NAMES: ClassVar[Any] = None + ASSET_MC_MONITOR_STATUSES: ClassVar[Any] = None + ASSET_MC_MONITOR_TYPES: ClassVar[Any] = None + ASSET_MC_MONITOR_SCHEDULE_TYPES: ClassVar[Any] = None + ASSET_MC_INCIDENT_TYPES: ClassVar[Any] = None + ASSET_MC_INCIDENT_SUB_TYPES: ClassVar[Any] = None + ASSET_MC_INCIDENT_SEVERITIES: ClassVar[Any] = None + ASSET_MC_INCIDENT_PRIORITIES: ClassVar[Any] = None + ASSET_MC_INCIDENT_STATES: ClassVar[Any] = None + ASSET_MC_IS_MONITORED: ClassVar[Any] = None + ASSET_MC_LAST_SYNC_RUN_AT: ClassVar[Any] = None + STARRED_BY: ClassVar[Any] = None + STARRED_DETAILS_LIST: ClassVar[Any] = None + STARRED_COUNT: ClassVar[Any] = None + ASSET_ANOMALO_DQ_STATUS: ClassVar[Any] = None + ASSET_ANOMALO_CHECK_COUNT: ClassVar[Any] = None + ASSET_ANOMALO_FAILED_CHECK_COUNT: ClassVar[Any] = None + ASSET_ANOMALO_CHECK_STATUSES: ClassVar[Any] = None + ASSET_ANOMALO_LAST_CHECK_RUN_AT: ClassVar[Any] = None + ASSET_ANOMALO_APPLIED_CHECK_TYPES: ClassVar[Any] = None + ASSET_ANOMALO_FAILED_CHECK_TYPES: ClassVar[Any] = None + ASSET_ANOMALO_SOURCE_URL: ClassVar[Any] = None + ASSET_SODA_DQ_STATUS: ClassVar[Any] = None + ASSET_SODA_CHECK_COUNT: ClassVar[Any] = None + ASSET_SODA_LAST_SYNC_RUN_AT: ClassVar[Any] = None + ASSET_SODA_LAST_SCAN_AT: ClassVar[Any] = None + ASSET_SODA_CHECK_STATUSES: ClassVar[Any] = None + ASSET_SODA_SOURCE_URL: ClassVar[Any] = None + ASSET_ICON: ClassVar[Any] = None + ASSET_EXTERNAL_DQ_METADATA_DETAILS: ClassVar[Any] = None + IS_PARTIAL: ClassVar[Any] = None + IS_AI_GENERATED: ClassVar[Any] = None + ASSET_COVER_IMAGE: ClassVar[Any] = None + ASSET_THEME_HEX: ClassVar[Any] = None + LEXICOGRAPHICAL_SORT_ORDER: ClassVar[Any] = None + HAS_CONTRACT: ClassVar[Any] = None + ASSET_REDIRECT_GUIDS: ClassVar[Any] = None + ASSET_POLICY_GUIDS: ClassVar[Any] = None + ASSET_POLICIES_COUNT: ClassVar[Any] = None + DOMAIN_GUIDS: ClassVar[Any] = None + NON_COMPLIANT_ASSET_POLICY_GUIDS: ClassVar[Any] = None + PRODUCT_GUIDS: ClassVar[Any] = None + OUTPUT_PRODUCT_GUIDS: ClassVar[Any] = None + APPLICATION_QUALIFIED_NAME: ClassVar[Any] = None + APPLICATION_FIELD_QUALIFIED_NAME: ClassVar[Any] = None + ASSET_USER_DEFINED_TYPE: ClassVar[Any] = None + ASSET_INTERNAL_POPULARITY_SCORE: ClassVar[Any] = None + ASSET_DQ_SCHEDULE_TYPE: ClassVar[Any] = None + ASSET_DQ_SCHEDULE_CRONTAB: ClassVar[Any] = None + ASSET_DQ_SCHEDULE_TIME_ZONE: ClassVar[Any] = None + ASSET_DQ_SCHEDULE_SOURCE_SYNC_STATUS: ClassVar[Any] = None + ASSET_DQ_SCHEDULE_SOURCE_SYNCED_AT: ClassVar[Any] = None + ASSET_DQ_SCHEDULE_SOURCE_SYNC_ERROR_MESSAGE: ClassVar[Any] = None + ASSET_DQ_SCHEDULE_SOURCE_SYNC_ERROR_CODE: ClassVar[Any] = None + ASSET_DQ_SCHEDULE_SOURCE_SYNC_RAW_ERROR: ClassVar[Any] = None + ASSET_DQ_RULE_ATTACHED_DIMENSIONS: ClassVar[Any] = None + ASSET_DQ_RULE_FAILED_DIMENSIONS: ClassVar[Any] = None + ASSET_DQ_RULE_PASSED_DIMENSIONS: ClassVar[Any] = None + ASSET_DQ_RULE_ATTACHED_RULE_TYPES: ClassVar[Any] = None + ASSET_DQ_RULE_FAILED_RULE_TYPES: ClassVar[Any] = None + ASSET_DQ_RULE_PASSED_RULE_TYPES: ClassVar[Any] = None + ASSET_DQ_RULE_RESULT_TAGS: ClassVar[Any] = None + ASSET_DQ_RULE_LAST_RUN_AT: ClassVar[Any] = None + ASSET_DQ_MANUAL_RUN_STATUS: ClassVar[Any] = None + ASSET_DQ_RULE_TOTAL_COUNT: ClassVar[Any] = None + ASSET_DQ_RULE_FAILED_COUNT: ClassVar[Any] = None + ASSET_DQ_RULE_PASSED_COUNT: ClassVar[Any] = None + ASSET_DQ_RESULT: ClassVar[Any] = None + ASSET_DQ_FRESHNESS_VALUE: ClassVar[Any] = None + ASSET_DQ_FRESHNESS_EXPECTATION: ClassVar[Any] = None + ASSET_DQ_ROW_SCOPE_FILTER_COLUMN_QUALIFIED_NAME: ClassVar[Any] = None + ASSET_SPACE_QUALIFIED_NAME: ClassVar[Any] = None + ASSET_SPACE_NAME: ClassVar[Any] = None + ASSET_GCP_DATAPLEX_METADATA_DETAILS: ClassVar[Any] = None + ASSET_GCP_DATAPLEX_ASPECT_LIST: ClassVar[Any] = None + ASSET_GCP_DATAPLEX_ASPECT_FIELD_LIST: ClassVar[Any] = None + ASSET_SMUS_METADATA_FORM_NAMES: ClassVar[Any] = None + ASSET_SMUS_METADATA_FORM_KEY_VALUE_DETAILS: ClassVar[Any] = None + ASSET_SMUS_METADATA_FORM_DETAILS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Incident" + + asset_severity: Union[str, None, UnsetType] = UNSET + """Status of this asset's severity.""" + + name: Union[str, None, UnsetType] = UNSET + """Name of this asset. Fallback for display purposes, if displayName is empty.""" + + display_name: Union[str, None, UnsetType] = UNSET + """Human-readable name of this asset used for display purposes (in user interface).""" + + description: Union[str, None, UnsetType] = UNSET + """Description of this asset, for example as crawled from a source. Fallback for display purposes, if userDescription is empty.""" + + asset_source_readme: Union[str, None, UnsetType] = UNSET + """Readme of this asset, as extracted from source. If present, this will be used for the readme in user interface.""" + + user_description: Union[str, None, UnsetType] = UNSET + """Description of this asset, as provided by a user. If present, this will be used for the description in user interface.""" + + asset_ai_generated_description: Union[str, None, UnsetType] = UNSET + """Description of this asset, generated by AI based on the asset's context. Displayed separately in the UI and can be used to overwrite existing descriptions.""" + + asset_ai_generated_description_confidence: Union[float, None, UnsetType] = UNSET + """Confidence score of the AI-generated description, ranging from 0.0 to 1.0.""" + + asset_ai_generated_description_reasoning: Union[str, None, UnsetType] = UNSET + """Reasoning behind the AI-generated description, explaining how the description was derived from the asset's context.""" + + tenant_id: Union[str, None, UnsetType] = UNSET + """Name of the Atlan workspace in which this asset exists.""" + + certificate_status: Union[str, None, UnsetType] = UNSET + """Status of this asset's certification.""" + + certificate_status_message: Union[str, None, UnsetType] = UNSET + """Human-readable descriptive message used to provide further detail to certificateStatus.""" + + certificate_updated_by: Union[str, None, UnsetType] = UNSET + """Name of the user who last updated the certification of this asset.""" + + certificate_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the certification was last updated, in milliseconds.""" + + announcement_title: Union[str, None, UnsetType] = UNSET + """Brief title for the announcement on this asset. Required when announcementType is specified.""" + + announcement_message: Union[str, None, UnsetType] = UNSET + """Detailed message to include in the announcement on this asset.""" + + announcement_type: Union[str, None, UnsetType] = UNSET + """Type of announcement on this asset.""" + + announcement_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the announcement was last updated, in milliseconds.""" + + announcement_updated_by: Union[str, None, UnsetType] = UNSET + """Name of the user who last updated the announcement.""" + + owner_users: Union[Set[str], None, UnsetType] = UNSET + """List of users who own this asset.""" + + owner_groups: Union[Set[str], None, UnsetType] = UNSET + """List of groups who own this asset.""" + + admin_users: Union[Set[str], None, UnsetType] = UNSET + """List of users who administer this asset. (This is only used for certain asset types.)""" + + admin_groups: Union[Set[str], None, UnsetType] = UNSET + """List of groups who administer this asset. (This is only used for certain asset types.)""" + + viewer_users: Union[Set[str], None, UnsetType] = UNSET + """List of users who can view assets contained in a collection. (This is only used for certain asset types.)""" + + viewer_groups: Union[Set[str], None, UnsetType] = UNSET + """List of groups who can view assets contained in a collection. (This is only used for certain asset types.)""" + + connector_name: Union[str, None, UnsetType] = UNSET + """Type of the connector through which this asset is accessible.""" + + connection_name: Union[str, None, UnsetType] = UNSET + """Simple name of the connection through which this asset is accessible.""" + + connection_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the connection through which this asset is accessible.""" + + has_lineage: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="__hasLineage" + ) + """Whether this asset has lineage (true) or not (false).""" + + is_discoverable: Union[bool, None, UnsetType] = UNSET + """Whether this asset is discoverable through the UI (true) or not (false).""" + + is_editable: Union[bool, None, UnsetType] = UNSET + """Whether this asset can be edited in the UI (true) or not (false).""" + + sub_type: Union[str, None, UnsetType] = UNSET + """Subtype of this asset.""" + + view_score: Union[float, None, UnsetType] = UNSET + """View score for this asset.""" + + popularity_score: Union[float, None, UnsetType] = UNSET + """Popularity score for this asset.""" + + source_owners: Union[str, None, UnsetType] = UNSET + """List of owners of this asset, in the source system.""" + + asset_source_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for this asset in the system from which it was sourced.""" + + source_created_by: Union[str, None, UnsetType] = UNSET + """Name of the user who created this asset, in the source system.""" + + source_created_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was created in the source system, in milliseconds.""" + + source_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last updated in the source system, in milliseconds.""" + + source_updated_by: Union[str, None, UnsetType] = UNSET + """Name of the user who last updated this asset, in the source system.""" + + source_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sourceURL" + ) + """URL to the resource within the source application, used to create a button to view this asset in the source application.""" + + source_embed_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sourceEmbedURL" + ) + """URL to create an embed for a resource (for example, an image of a dashboard) within Atlan.""" + + last_sync_workflow_name: Union[str, None, UnsetType] = UNSET + """Name of the crawler that last synchronized this asset.""" + + last_sync_run_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last crawled, in milliseconds.""" + + last_sync_run: Union[str, None, UnsetType] = UNSET + """Name of the last run of the crawler that last synchronized this asset.""" + + admin_roles: Union[Set[str], None, UnsetType] = UNSET + """List of roles who administer this asset. (This is only used for Connection assets.)""" + + source_read_count: Union[int, None, UnsetType] = UNSET + """Total count of all read operations at source.""" + + source_read_user_count: Union[int, None, UnsetType] = UNSET + """Total number of unique users that read data from asset.""" + + source_last_read_at: Union[int, None, UnsetType] = UNSET + """Timestamp of most recent read operation.""" + + last_row_changed_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) of the last operation that inserted, updated, or deleted rows, in milliseconds.""" + + source_total_cost: Union[float, None, UnsetType] = UNSET + """Total cost of all operations at source.""" + + source_cost_unit: Union[str, None, UnsetType] = UNSET + """The unit of measure for sourceTotalCost.""" + + source_read_query_cost: Union[float, None, UnsetType] = UNSET + """Total cost of read queries at source.""" + + source_read_recent_user_list: Union[List[str], None, UnsetType] = UNSET + """List of usernames of the most recent users who read this asset.""" + + source_read_recent_user_record_list: Union[ + List[Dict[str, Any]], None, UnsetType + ] = UNSET + """List of usernames with extra insights for the most recent users who read this asset.""" + + source_read_top_user_list: Union[List[str], None, UnsetType] = UNSET + """List of usernames of the users who read this asset the most.""" + + source_read_top_user_record_list: Union[List[Dict[str, Any]], None, UnsetType] = ( + UNSET + ) + """List of usernames with extra insights for the users who read this asset the most.""" + + source_read_popular_query_record_list: Union[ + List[Dict[str, Any]], None, UnsetType + ] = UNSET + """List of the most popular queries that accessed this asset.""" + + source_read_expensive_query_record_list: Union[ + List[Dict[str, Any]], None, UnsetType + ] = UNSET + """List of the most expensive queries that accessed this asset.""" + + source_read_slow_query_record_list: Union[List[Dict[str, Any]], None, UnsetType] = ( + UNSET + ) + """List of the slowest queries that accessed this asset.""" + + source_query_compute_cost_list: Union[List[str], None, UnsetType] = UNSET + """List of most expensive warehouse names.""" + + source_query_compute_cost_record_list: Union[ + List[Dict[str, Any]], None, UnsetType + ] = UNSET + """List of most expensive warehouses with extra insights.""" + + dbt_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of this asset in dbt.""" + + asset_dbt_workflow_last_updated: Union[str, None, UnsetType] = UNSET + """Name of the DBT workflow in Atlan that last updated the asset.""" + + asset_dbt_alias: Union[str, None, UnsetType] = UNSET + """Alias of this asset in dbt.""" + + asset_dbt_meta: Union[str, None, UnsetType] = UNSET + """Metadata for this asset in dbt, specifically everything under the 'meta' key in the dbt object.""" + + asset_dbt_unique_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of this asset in dbt.""" + + asset_dbt_account_name: Union[str, None, UnsetType] = UNSET + """Name of the account in which this asset exists in dbt.""" + + asset_dbt_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which this asset exists in dbt.""" + + asset_dbt_package_name: Union[str, None, UnsetType] = UNSET + """Name of the package in which this asset exists in dbt.""" + + asset_dbt_job_name: Union[str, None, UnsetType] = UNSET + """Name of the job that materialized this asset in dbt.""" + + asset_dbt_job_schedule: Union[str, None, UnsetType] = UNSET + """Schedule of the job that materialized this asset in dbt.""" + + asset_dbt_job_status: Union[str, None, UnsetType] = UNSET + """Status of the job that materialized this asset in dbt.""" + + asset_dbt_test_status: Union[str, None, UnsetType] = UNSET + """All associated dbt test statuses.""" + + asset_dbt_job_schedule_cron_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable cron schedule of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt last ran, in milliseconds.""" + + asset_dbt_job_last_run_url: Union[str, None, UnsetType] = UNSET + """URL of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_created_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt was last created, in milliseconds.""" + + asset_dbt_job_last_run_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt was last updated, in milliseconds.""" + + asset_dbt_job_last_run_dequed_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt was dequeued, in milliseconds.""" + + asset_dbt_job_last_run_started_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt was started running, in milliseconds.""" + + asset_dbt_job_last_run_total_duration: Union[str, None, UnsetType] = UNSET + """Total duration of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_total_duration_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable total duration of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_queued_duration: Union[str, None, UnsetType] = UNSET + """Total duration the job that materialized this asset in dbt spent being queued.""" + + asset_dbt_job_last_run_queued_duration_humanized: Union[str, None, UnsetType] = ( + UNSET + ) + """Human-readable total duration of the last run of the job that materialized this asset in dbt spend being queued.""" + + asset_dbt_job_last_run_run_duration: Union[str, None, UnsetType] = UNSET + """Run duration of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_run_duration_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable run duration of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_git_branch: Union[str, None, UnsetType] = UNSET + """Branch in git from which the last run of the job that materialized this asset in dbt ran.""" + + asset_dbt_job_last_run_git_sha: Union[str, None, UnsetType] = UNSET + """SHA hash in git for the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_status_message: Union[str, None, UnsetType] = UNSET + """Status message of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_owner_thread_id: Union[str, None, UnsetType] = UNSET + """Thread ID of the owner of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_executed_by_thread_id: Union[str, None, UnsetType] = UNSET + """Thread ID of the user who executed the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_artifacts_saved: Union[bool, None, UnsetType] = UNSET + """Whether artifacts were saved from the last run of the job that materialized this asset in dbt (true) or not (false).""" + + asset_dbt_job_last_run_artifact_s3_path: Union[str, None, UnsetType] = UNSET + """Path in S3 to the artifacts saved from the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_has_docs_generated: Union[bool, None, UnsetType] = UNSET + """Whether docs were generated from the last run of the job that materialized this asset in dbt (true) or not (false).""" + + asset_dbt_job_last_run_has_sources_generated: Union[bool, None, UnsetType] = UNSET + """Whether sources were generated from the last run of the job that materialized this asset in dbt (true) or not (false).""" + + asset_dbt_job_last_run_notifications_sent: Union[bool, None, UnsetType] = UNSET + """Whether notifications were sent from the last run of the job that materialized this asset in dbt (true) or not (false).""" + + asset_dbt_job_next_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) when the next run of the job that materializes this asset in dbt is scheduled.""" + + asset_dbt_job_next_run_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable time when the next run of the job that materializes this asset in dbt is scheduled.""" + + asset_dbt_environment_name: Union[str, None, UnsetType] = UNSET + """Name of the environment in which this asset is materialized in dbt.""" + + asset_dbt_environment_dbt_version: Union[str, None, UnsetType] = UNSET + """Version of the environment in which this asset is materialized in dbt.""" + + asset_dbt_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset in dbt.""" + + asset_dbt_semantic_layer_proxy_url: Union[str, None, UnsetType] = UNSET + """URL of the semantic layer proxy for this asset in dbt.""" + + asset_dbt_source_freshness_criteria: Union[str, None, UnsetType] = UNSET + """Freshness criteria for the source of this asset in dbt.""" + + sample_data_url: Union[str, None, UnsetType] = UNSET + """URL for sample data for this asset.""" + + asset_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset.""" + + asset_mc_incident_names: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident names attached to this asset.""" + + asset_mc_incident_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of unique Monte Carlo incident names attached to this asset.""" + + asset_mc_alert_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of unique Monte Carlo alert names attached to this asset.""" + + asset_mc_monitor_names: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo monitor names attached to this asset.""" + + asset_mc_monitor_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of unique Monte Carlo monitor names attached to this asset.""" + + asset_mc_monitor_statuses: Union[List[str], None, UnsetType] = UNSET + """Statuses of all associated Monte Carlo monitors.""" + + asset_mc_monitor_types: Union[List[str], None, UnsetType] = UNSET + """Types of all associated Monte Carlo monitors.""" + + asset_mc_monitor_schedule_types: Union[List[str], None, UnsetType] = UNSET + """Schedules of all associated Monte Carlo monitors.""" + + asset_mc_incident_types: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident types associated with this asset.""" + + asset_mc_incident_sub_types: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident sub-types associated with this asset.""" + + asset_mc_incident_severities: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident severities associated with this asset.""" + + asset_mc_incident_priorities: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident priorities associated with this asset.""" + + asset_mc_incident_states: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident states associated with this asset.""" + + asset_mc_is_monitored: Union[bool, None, UnsetType] = UNSET + """Tracks whether this asset is monitored by MC or not""" + + asset_mc_last_sync_run_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last synced from Monte Carlo.""" + + starred_by: Union[List[str], None, UnsetType] = UNSET + """Users who have starred this asset.""" + + starred_details_list: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of usernames with extra information of the users who have starred an asset.""" + + starred_count: Union[int, None, UnsetType] = UNSET + """Number of users who have starred this asset.""" + + asset_anomalo_dq_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetAnomaloDQStatus" + ) + """Status of data quality from Anomalo.""" + + asset_anomalo_check_count: Union[int, None, UnsetType] = UNSET + """Total number of checks present in Anomalo for this asset.""" + + asset_anomalo_failed_check_count: Union[int, None, UnsetType] = UNSET + """Total number of checks failed in Anomalo for this asset.""" + + asset_anomalo_check_statuses: Union[str, None, UnsetType] = UNSET + """Stringified JSON object containing status of all Anomalo checks associated to this asset.""" + + asset_anomalo_last_check_run_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the last check was run via Anomalo.""" + + asset_anomalo_applied_check_types: Union[List[str], None, UnsetType] = UNSET + """All associated Anomalo check types.""" + + asset_anomalo_failed_check_types: Union[List[str], None, UnsetType] = UNSET + """All associated Anomalo failed check types.""" + + asset_anomalo_source_url: Union[str, None, UnsetType] = UNSET + """URL of the source in Anomalo.""" + + asset_soda_dq_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetSodaDQStatus" + ) + """Status of data quality from Soda.""" + + asset_soda_check_count: Union[int, None, UnsetType] = UNSET + """Number of checks done via Soda.""" + + asset_soda_last_sync_run_at: Union[int, None, UnsetType] = UNSET + """""" + + asset_soda_last_scan_at: Union[int, None, UnsetType] = UNSET + """""" + + asset_soda_check_statuses: Union[str, None, UnsetType] = UNSET + """All associated Soda check statuses.""" + + asset_soda_source_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetSodaSourceURL" + ) + """""" + + asset_icon: Union[str, None, UnsetType] = UNSET + """Name of the icon to use for this asset. (Only applies to glossaries, currently.)""" + + asset_external_dq_metadata_details: Union[ + Dict[str, Dict[str, Any]], None, UnsetType + ] = msgspec.field(default=UNSET, name="assetExternalDQMetadataDetails") + """DQ metadata captured for asset from external DQ tool(s).""" + + is_partial: Union[bool, None, UnsetType] = UNSET + """Indicates this asset is not fully-known, if true.""" + + is_ai_generated: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="isAIGenerated" + ) + """""" + + asset_cover_image: Union[str, None, UnsetType] = UNSET + """Cover image to use for this asset in the UI (applicable to only a few asset types).""" + + asset_theme_hex: Union[str, None, UnsetType] = UNSET + """Color (in hexadecimal RGB) to use to represent this asset.""" + + lexicographical_sort_order: Union[str, None, UnsetType] = UNSET + """Custom order for sorting purpose, managed by client""" + + has_contract: Union[bool, None, UnsetType] = UNSET + """Whether this asset has contract (true) or not (false).""" + + asset_redirect_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetRedirectGUIDs" + ) + """Array of asset ids that equivalent to this asset.""" + + asset_policy_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetPolicyGUIDs" + ) + """Array of policy ids governing this asset""" + + asset_policies_count: Union[int, None, UnsetType] = UNSET + """Count of policies inside the asset""" + + domain_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="domainGUIDs" + ) + """Array of domain guids linked to this asset""" + + non_compliant_asset_policy_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="nonCompliantAssetPolicyGUIDs" + ) + """Array of policy ids non-compliant to this asset""" + + product_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="productGUIDs" + ) + """Array of product guids linked to this asset""" + + output_product_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="outputProductGUIDs" + ) + """Array of product guids which have this asset as outputPort""" + + application_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the Application that contains this asset.""" + + application_field_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the ApplicationField that contains this asset.""" + + asset_user_defined_type: Union[str, None, UnsetType] = UNSET + """Name to use for this type of asset, as a subtype of the actual typeName.""" + + asset_internal_popularity_score: Union[float, None, UnsetType] = UNSET + """Internal Popularity score for this asset.""" + + asset_dq_schedule_type: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleType" + ) + """Type of schedule of the DQ rule that will run at datasource.""" + + asset_dq_schedule_crontab: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleCrontab" + ) + """Crontab of the DQ rule that will run at datasource.""" + + asset_dq_schedule_time_zone: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleTimeZone" + ) + """Timezone of the DQ rule schedule that will run at datasource""" + + asset_dq_schedule_source_sync_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleSourceSyncStatus" + ) + """Latest sync status of the schedule to the source.""" + + asset_dq_schedule_source_synced_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleSourceSyncedAt" + ) + """Time (epoch) at which the schedule synced to the source.""" + + asset_dq_schedule_source_sync_error_message: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQScheduleSourceSyncErrorMessage") + ) + """Error message in the case of sync state being "error".""" + + asset_dq_schedule_source_sync_error_code: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQScheduleSourceSyncErrorCode") + ) + """Error code in the case of sync state being "error".""" + + asset_dq_schedule_source_sync_raw_error: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQScheduleSourceSyncRawError") + ) + """Raw error message from the source.""" + + asset_dq_rule_attached_dimensions: Union[List[str], None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQRuleAttachedDimensions") + ) + """List of all the dimensions of attached rules.""" + + asset_dq_rule_failed_dimensions: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleFailedDimensions" + ) + """List of all the dimensions of failed rules.""" + + asset_dq_rule_passed_dimensions: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRulePassedDimensions" + ) + """List of all the dimensions for which all the rules passed.""" + + asset_dq_rule_attached_rule_types: Union[List[str], None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQRuleAttachedRuleTypes") + ) + """List of all the types of attached rules.""" + + asset_dq_rule_failed_rule_types: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleFailedRuleTypes" + ) + """List of all the types of failed rules.""" + + asset_dq_rule_passed_rule_types: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRulePassedRuleTypes" + ) + """List of all the types of rules for which all the rules passed.""" + + asset_dq_rule_result_tags: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleResultTags" + ) + """Tag for the result of the DQ rules. Eg, rule_pass:completeness:null_count.""" + + asset_dq_rule_last_run_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleLastRunAt" + ) + """Time (epoch) at which the last dq rule ran.""" + + asset_dq_manual_run_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQManualRunStatus" + ) + """Status of the latest manual DQ run triggered for this asset.""" + + asset_dq_rule_total_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleTotalCount" + ) + """Count of DQ rules attached to this asset.""" + + asset_dq_rule_failed_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleFailedCount" + ) + """Count of failed DQ rules attached to this asset.""" + + asset_dq_rule_passed_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRulePassedCount" + ) + """Count of passed DQ rules attached to this asset.""" + + asset_dq_result: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQResult" + ) + """Overall result of all the dq rules. If any one rule failed, then fail else pass.""" + + asset_dq_freshness_value: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQFreshnessValue" + ) + """Value of data freshness from Source.""" + + asset_dq_freshness_expectation: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQFreshnessExpectation" + ) + """Expectation of data freshness from Source.""" + + asset_dq_row_scope_filter_column_qualified_name: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQRowScopeFilterColumnQualifiedName") + ) + """Qualified name of the column used for row scope filtering in DQ rules for this asset.""" + + asset_space_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the space that contains this asset.""" + + asset_space_name: Union[str, None, UnsetType] = UNSET + """Name of the space that contains this asset.""" + + asset_gcp_dataplex_metadata_details: Union[Dict[str, Any], None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetGCPDataplexMetadataDetails") + ) + """Metrics captured by GCP Dataplex for objects associated with GCP services.""" + + asset_gcp_dataplex_aspect_list: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetGCPDataplexAspectList" + ) + """List of names of all Aspects linked to this asset.""" + + asset_gcp_dataplex_aspect_field_list: Union[List[str], None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetGCPDataplexAspectFieldList") + ) + """List of field key-values associated with all Aspects linked to this asset.""" + + asset_smus_metadata_form_names: Union[List[str], None, UnsetType] = UNSET + """List of AWS SMUS MetadataForm Names. This is mainly used for filtering purpose.""" + + asset_smus_metadata_form_key_value_details: Union[List[str], None, UnsetType] = ( + UNSET + ) + """List of AWS SMUS MetadataForm Key:Value Details. This is mainly used for filtering purpose.""" + + asset_smus_metadata_form_details: Union[List[Dict[str, Any]], None, UnsetType] = ( + UNSET + ) + """AWS SMUS Asset MetadataForm details""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Incident" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _incident_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Incident: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Incident instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _incident_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class IncidentAttributes(ReferenceableAttributes): + """Incident-specific attributes for nested API format.""" + + asset_severity: Union[str, None, UnsetType] = UNSET + """Status of this asset's severity.""" + + name: Union[str, None, UnsetType] = UNSET + """Name of this asset. Fallback for display purposes, if displayName is empty.""" + + display_name: Union[str, None, UnsetType] = UNSET + """Human-readable name of this asset used for display purposes (in user interface).""" + + description: Union[str, None, UnsetType] = UNSET + """Description of this asset, for example as crawled from a source. Fallback for display purposes, if userDescription is empty.""" + + asset_source_readme: Union[str, None, UnsetType] = UNSET + """Readme of this asset, as extracted from source. If present, this will be used for the readme in user interface.""" + + user_description: Union[str, None, UnsetType] = UNSET + """Description of this asset, as provided by a user. If present, this will be used for the description in user interface.""" + + asset_ai_generated_description: Union[str, None, UnsetType] = UNSET + """Description of this asset, generated by AI based on the asset's context. Displayed separately in the UI and can be used to overwrite existing descriptions.""" + + asset_ai_generated_description_confidence: Union[float, None, UnsetType] = UNSET + """Confidence score of the AI-generated description, ranging from 0.0 to 1.0.""" + + asset_ai_generated_description_reasoning: Union[str, None, UnsetType] = UNSET + """Reasoning behind the AI-generated description, explaining how the description was derived from the asset's context.""" + + tenant_id: Union[str, None, UnsetType] = UNSET + """Name of the Atlan workspace in which this asset exists.""" + + certificate_status: Union[str, None, UnsetType] = UNSET + """Status of this asset's certification.""" + + certificate_status_message: Union[str, None, UnsetType] = UNSET + """Human-readable descriptive message used to provide further detail to certificateStatus.""" + + certificate_updated_by: Union[str, None, UnsetType] = UNSET + """Name of the user who last updated the certification of this asset.""" + + certificate_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the certification was last updated, in milliseconds.""" + + announcement_title: Union[str, None, UnsetType] = UNSET + """Brief title for the announcement on this asset. Required when announcementType is specified.""" + + announcement_message: Union[str, None, UnsetType] = UNSET + """Detailed message to include in the announcement on this asset.""" + + announcement_type: Union[str, None, UnsetType] = UNSET + """Type of announcement on this asset.""" + + announcement_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the announcement was last updated, in milliseconds.""" + + announcement_updated_by: Union[str, None, UnsetType] = UNSET + """Name of the user who last updated the announcement.""" + + owner_users: Union[Set[str], None, UnsetType] = UNSET + """List of users who own this asset.""" + + owner_groups: Union[Set[str], None, UnsetType] = UNSET + """List of groups who own this asset.""" + + admin_users: Union[Set[str], None, UnsetType] = UNSET + """List of users who administer this asset. (This is only used for certain asset types.)""" + + admin_groups: Union[Set[str], None, UnsetType] = UNSET + """List of groups who administer this asset. (This is only used for certain asset types.)""" + + viewer_users: Union[Set[str], None, UnsetType] = UNSET + """List of users who can view assets contained in a collection. (This is only used for certain asset types.)""" + + viewer_groups: Union[Set[str], None, UnsetType] = UNSET + """List of groups who can view assets contained in a collection. (This is only used for certain asset types.)""" + + connector_name: Union[str, None, UnsetType] = UNSET + """Type of the connector through which this asset is accessible.""" + + connection_name: Union[str, None, UnsetType] = UNSET + """Simple name of the connection through which this asset is accessible.""" + + connection_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the connection through which this asset is accessible.""" + + has_lineage: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="__hasLineage" + ) + """Whether this asset has lineage (true) or not (false).""" + + is_discoverable: Union[bool, None, UnsetType] = UNSET + """Whether this asset is discoverable through the UI (true) or not (false).""" + + is_editable: Union[bool, None, UnsetType] = UNSET + """Whether this asset can be edited in the UI (true) or not (false).""" + + sub_type: Union[str, None, UnsetType] = UNSET + """Subtype of this asset.""" + + view_score: Union[float, None, UnsetType] = UNSET + """View score for this asset.""" + + popularity_score: Union[float, None, UnsetType] = UNSET + """Popularity score for this asset.""" + + source_owners: Union[str, None, UnsetType] = UNSET + """List of owners of this asset, in the source system.""" + + asset_source_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for this asset in the system from which it was sourced.""" + + source_created_by: Union[str, None, UnsetType] = UNSET + """Name of the user who created this asset, in the source system.""" + + source_created_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was created in the source system, in milliseconds.""" + + source_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last updated in the source system, in milliseconds.""" + + source_updated_by: Union[str, None, UnsetType] = UNSET + """Name of the user who last updated this asset, in the source system.""" + + source_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sourceURL" + ) + """URL to the resource within the source application, used to create a button to view this asset in the source application.""" + + source_embed_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sourceEmbedURL" + ) + """URL to create an embed for a resource (for example, an image of a dashboard) within Atlan.""" + + last_sync_workflow_name: Union[str, None, UnsetType] = UNSET + """Name of the crawler that last synchronized this asset.""" + + last_sync_run_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last crawled, in milliseconds.""" + + last_sync_run: Union[str, None, UnsetType] = UNSET + """Name of the last run of the crawler that last synchronized this asset.""" + + admin_roles: Union[Set[str], None, UnsetType] = UNSET + """List of roles who administer this asset. (This is only used for Connection assets.)""" + + source_read_count: Union[int, None, UnsetType] = UNSET + """Total count of all read operations at source.""" + + source_read_user_count: Union[int, None, UnsetType] = UNSET + """Total number of unique users that read data from asset.""" + + source_last_read_at: Union[int, None, UnsetType] = UNSET + """Timestamp of most recent read operation.""" + + last_row_changed_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) of the last operation that inserted, updated, or deleted rows, in milliseconds.""" + + source_total_cost: Union[float, None, UnsetType] = UNSET + """Total cost of all operations at source.""" + + source_cost_unit: Union[str, None, UnsetType] = UNSET + """The unit of measure for sourceTotalCost.""" + + source_read_query_cost: Union[float, None, UnsetType] = UNSET + """Total cost of read queries at source.""" + + source_read_recent_user_list: Union[List[str], None, UnsetType] = UNSET + """List of usernames of the most recent users who read this asset.""" + + source_read_recent_user_record_list: Union[ + List[Dict[str, Any]], None, UnsetType + ] = UNSET + """List of usernames with extra insights for the most recent users who read this asset.""" + + source_read_top_user_list: Union[List[str], None, UnsetType] = UNSET + """List of usernames of the users who read this asset the most.""" + + source_read_top_user_record_list: Union[List[Dict[str, Any]], None, UnsetType] = ( + UNSET + ) + """List of usernames with extra insights for the users who read this asset the most.""" + + source_read_popular_query_record_list: Union[ + List[Dict[str, Any]], None, UnsetType + ] = UNSET + """List of the most popular queries that accessed this asset.""" + + source_read_expensive_query_record_list: Union[ + List[Dict[str, Any]], None, UnsetType + ] = UNSET + """List of the most expensive queries that accessed this asset.""" + + source_read_slow_query_record_list: Union[List[Dict[str, Any]], None, UnsetType] = ( + UNSET + ) + """List of the slowest queries that accessed this asset.""" + + source_query_compute_cost_list: Union[List[str], None, UnsetType] = UNSET + """List of most expensive warehouse names.""" + + source_query_compute_cost_record_list: Union[ + List[Dict[str, Any]], None, UnsetType + ] = UNSET + """List of most expensive warehouses with extra insights.""" + + dbt_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of this asset in dbt.""" + + asset_dbt_workflow_last_updated: Union[str, None, UnsetType] = UNSET + """Name of the DBT workflow in Atlan that last updated the asset.""" + + asset_dbt_alias: Union[str, None, UnsetType] = UNSET + """Alias of this asset in dbt.""" + + asset_dbt_meta: Union[str, None, UnsetType] = UNSET + """Metadata for this asset in dbt, specifically everything under the 'meta' key in the dbt object.""" + + asset_dbt_unique_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of this asset in dbt.""" + + asset_dbt_account_name: Union[str, None, UnsetType] = UNSET + """Name of the account in which this asset exists in dbt.""" + + asset_dbt_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which this asset exists in dbt.""" + + asset_dbt_package_name: Union[str, None, UnsetType] = UNSET + """Name of the package in which this asset exists in dbt.""" + + asset_dbt_job_name: Union[str, None, UnsetType] = UNSET + """Name of the job that materialized this asset in dbt.""" + + asset_dbt_job_schedule: Union[str, None, UnsetType] = UNSET + """Schedule of the job that materialized this asset in dbt.""" + + asset_dbt_job_status: Union[str, None, UnsetType] = UNSET + """Status of the job that materialized this asset in dbt.""" + + asset_dbt_test_status: Union[str, None, UnsetType] = UNSET + """All associated dbt test statuses.""" + + asset_dbt_job_schedule_cron_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable cron schedule of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt last ran, in milliseconds.""" + + asset_dbt_job_last_run_url: Union[str, None, UnsetType] = UNSET + """URL of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_created_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt was last created, in milliseconds.""" + + asset_dbt_job_last_run_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt was last updated, in milliseconds.""" + + asset_dbt_job_last_run_dequed_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt was dequeued, in milliseconds.""" + + asset_dbt_job_last_run_started_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt was started running, in milliseconds.""" + + asset_dbt_job_last_run_total_duration: Union[str, None, UnsetType] = UNSET + """Total duration of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_total_duration_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable total duration of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_queued_duration: Union[str, None, UnsetType] = UNSET + """Total duration the job that materialized this asset in dbt spent being queued.""" + + asset_dbt_job_last_run_queued_duration_humanized: Union[str, None, UnsetType] = ( + UNSET + ) + """Human-readable total duration of the last run of the job that materialized this asset in dbt spend being queued.""" + + asset_dbt_job_last_run_run_duration: Union[str, None, UnsetType] = UNSET + """Run duration of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_run_duration_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable run duration of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_git_branch: Union[str, None, UnsetType] = UNSET + """Branch in git from which the last run of the job that materialized this asset in dbt ran.""" + + asset_dbt_job_last_run_git_sha: Union[str, None, UnsetType] = UNSET + """SHA hash in git for the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_status_message: Union[str, None, UnsetType] = UNSET + """Status message of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_owner_thread_id: Union[str, None, UnsetType] = UNSET + """Thread ID of the owner of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_executed_by_thread_id: Union[str, None, UnsetType] = UNSET + """Thread ID of the user who executed the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_artifacts_saved: Union[bool, None, UnsetType] = UNSET + """Whether artifacts were saved from the last run of the job that materialized this asset in dbt (true) or not (false).""" + + asset_dbt_job_last_run_artifact_s3_path: Union[str, None, UnsetType] = UNSET + """Path in S3 to the artifacts saved from the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_has_docs_generated: Union[bool, None, UnsetType] = UNSET + """Whether docs were generated from the last run of the job that materialized this asset in dbt (true) or not (false).""" + + asset_dbt_job_last_run_has_sources_generated: Union[bool, None, UnsetType] = UNSET + """Whether sources were generated from the last run of the job that materialized this asset in dbt (true) or not (false).""" + + asset_dbt_job_last_run_notifications_sent: Union[bool, None, UnsetType] = UNSET + """Whether notifications were sent from the last run of the job that materialized this asset in dbt (true) or not (false).""" + + asset_dbt_job_next_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) when the next run of the job that materializes this asset in dbt is scheduled.""" + + asset_dbt_job_next_run_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable time when the next run of the job that materializes this asset in dbt is scheduled.""" + + asset_dbt_environment_name: Union[str, None, UnsetType] = UNSET + """Name of the environment in which this asset is materialized in dbt.""" + + asset_dbt_environment_dbt_version: Union[str, None, UnsetType] = UNSET + """Version of the environment in which this asset is materialized in dbt.""" + + asset_dbt_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset in dbt.""" + + asset_dbt_semantic_layer_proxy_url: Union[str, None, UnsetType] = UNSET + """URL of the semantic layer proxy for this asset in dbt.""" + + asset_dbt_source_freshness_criteria: Union[str, None, UnsetType] = UNSET + """Freshness criteria for the source of this asset in dbt.""" + + sample_data_url: Union[str, None, UnsetType] = UNSET + """URL for sample data for this asset.""" + + asset_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset.""" + + asset_mc_incident_names: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident names attached to this asset.""" + + asset_mc_incident_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of unique Monte Carlo incident names attached to this asset.""" + + asset_mc_alert_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of unique Monte Carlo alert names attached to this asset.""" + + asset_mc_monitor_names: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo monitor names attached to this asset.""" + + asset_mc_monitor_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of unique Monte Carlo monitor names attached to this asset.""" + + asset_mc_monitor_statuses: Union[List[str], None, UnsetType] = UNSET + """Statuses of all associated Monte Carlo monitors.""" + + asset_mc_monitor_types: Union[List[str], None, UnsetType] = UNSET + """Types of all associated Monte Carlo monitors.""" + + asset_mc_monitor_schedule_types: Union[List[str], None, UnsetType] = UNSET + """Schedules of all associated Monte Carlo monitors.""" + + asset_mc_incident_types: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident types associated with this asset.""" + + asset_mc_incident_sub_types: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident sub-types associated with this asset.""" + + asset_mc_incident_severities: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident severities associated with this asset.""" + + asset_mc_incident_priorities: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident priorities associated with this asset.""" + + asset_mc_incident_states: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident states associated with this asset.""" + + asset_mc_is_monitored: Union[bool, None, UnsetType] = UNSET + """Tracks whether this asset is monitored by MC or not""" + + asset_mc_last_sync_run_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last synced from Monte Carlo.""" + + starred_by: Union[List[str], None, UnsetType] = UNSET + """Users who have starred this asset.""" + + starred_details_list: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of usernames with extra information of the users who have starred an asset.""" + + starred_count: Union[int, None, UnsetType] = UNSET + """Number of users who have starred this asset.""" + + asset_anomalo_dq_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetAnomaloDQStatus" + ) + """Status of data quality from Anomalo.""" + + asset_anomalo_check_count: Union[int, None, UnsetType] = UNSET + """Total number of checks present in Anomalo for this asset.""" + + asset_anomalo_failed_check_count: Union[int, None, UnsetType] = UNSET + """Total number of checks failed in Anomalo for this asset.""" + + asset_anomalo_check_statuses: Union[str, None, UnsetType] = UNSET + """Stringified JSON object containing status of all Anomalo checks associated to this asset.""" + + asset_anomalo_last_check_run_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the last check was run via Anomalo.""" + + asset_anomalo_applied_check_types: Union[List[str], None, UnsetType] = UNSET + """All associated Anomalo check types.""" + + asset_anomalo_failed_check_types: Union[List[str], None, UnsetType] = UNSET + """All associated Anomalo failed check types.""" + + asset_anomalo_source_url: Union[str, None, UnsetType] = UNSET + """URL of the source in Anomalo.""" + + asset_soda_dq_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetSodaDQStatus" + ) + """Status of data quality from Soda.""" + + asset_soda_check_count: Union[int, None, UnsetType] = UNSET + """Number of checks done via Soda.""" + + asset_soda_last_sync_run_at: Union[int, None, UnsetType] = UNSET + """""" + + asset_soda_last_scan_at: Union[int, None, UnsetType] = UNSET + """""" + + asset_soda_check_statuses: Union[str, None, UnsetType] = UNSET + """All associated Soda check statuses.""" + + asset_soda_source_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetSodaSourceURL" + ) + """""" + + asset_icon: Union[str, None, UnsetType] = UNSET + """Name of the icon to use for this asset. (Only applies to glossaries, currently.)""" + + asset_external_dq_metadata_details: Union[ + Dict[str, Dict[str, Any]], None, UnsetType + ] = msgspec.field(default=UNSET, name="assetExternalDQMetadataDetails") + """DQ metadata captured for asset from external DQ tool(s).""" + + is_partial: Union[bool, None, UnsetType] = UNSET + """Indicates this asset is not fully-known, if true.""" + + is_ai_generated: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="isAIGenerated" + ) + """""" + + asset_cover_image: Union[str, None, UnsetType] = UNSET + """Cover image to use for this asset in the UI (applicable to only a few asset types).""" + + asset_theme_hex: Union[str, None, UnsetType] = UNSET + """Color (in hexadecimal RGB) to use to represent this asset.""" + + lexicographical_sort_order: Union[str, None, UnsetType] = UNSET + """Custom order for sorting purpose, managed by client""" + + has_contract: Union[bool, None, UnsetType] = UNSET + """Whether this asset has contract (true) or not (false).""" + + asset_redirect_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetRedirectGUIDs" + ) + """Array of asset ids that equivalent to this asset.""" + + asset_policy_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetPolicyGUIDs" + ) + """Array of policy ids governing this asset""" + + asset_policies_count: Union[int, None, UnsetType] = UNSET + """Count of policies inside the asset""" + + domain_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="domainGUIDs" + ) + """Array of domain guids linked to this asset""" + + non_compliant_asset_policy_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="nonCompliantAssetPolicyGUIDs" + ) + """Array of policy ids non-compliant to this asset""" + + product_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="productGUIDs" + ) + """Array of product guids linked to this asset""" + + output_product_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="outputProductGUIDs" + ) + """Array of product guids which have this asset as outputPort""" + + application_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the Application that contains this asset.""" + + application_field_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the ApplicationField that contains this asset.""" + + asset_user_defined_type: Union[str, None, UnsetType] = UNSET + """Name to use for this type of asset, as a subtype of the actual typeName.""" + + asset_internal_popularity_score: Union[float, None, UnsetType] = UNSET + """Internal Popularity score for this asset.""" + + asset_dq_schedule_type: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleType" + ) + """Type of schedule of the DQ rule that will run at datasource.""" + + asset_dq_schedule_crontab: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleCrontab" + ) + """Crontab of the DQ rule that will run at datasource.""" + + asset_dq_schedule_time_zone: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleTimeZone" + ) + """Timezone of the DQ rule schedule that will run at datasource""" + + asset_dq_schedule_source_sync_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleSourceSyncStatus" + ) + """Latest sync status of the schedule to the source.""" + + asset_dq_schedule_source_synced_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleSourceSyncedAt" + ) + """Time (epoch) at which the schedule synced to the source.""" + + asset_dq_schedule_source_sync_error_message: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQScheduleSourceSyncErrorMessage") + ) + """Error message in the case of sync state being "error".""" + + asset_dq_schedule_source_sync_error_code: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQScheduleSourceSyncErrorCode") + ) + """Error code in the case of sync state being "error".""" + + asset_dq_schedule_source_sync_raw_error: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQScheduleSourceSyncRawError") + ) + """Raw error message from the source.""" + + asset_dq_rule_attached_dimensions: Union[List[str], None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQRuleAttachedDimensions") + ) + """List of all the dimensions of attached rules.""" + + asset_dq_rule_failed_dimensions: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleFailedDimensions" + ) + """List of all the dimensions of failed rules.""" + + asset_dq_rule_passed_dimensions: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRulePassedDimensions" + ) + """List of all the dimensions for which all the rules passed.""" + + asset_dq_rule_attached_rule_types: Union[List[str], None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQRuleAttachedRuleTypes") + ) + """List of all the types of attached rules.""" + + asset_dq_rule_failed_rule_types: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleFailedRuleTypes" + ) + """List of all the types of failed rules.""" + + asset_dq_rule_passed_rule_types: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRulePassedRuleTypes" + ) + """List of all the types of rules for which all the rules passed.""" + + asset_dq_rule_result_tags: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleResultTags" + ) + """Tag for the result of the DQ rules. Eg, rule_pass:completeness:null_count.""" + + asset_dq_rule_last_run_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleLastRunAt" + ) + """Time (epoch) at which the last dq rule ran.""" + + asset_dq_manual_run_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQManualRunStatus" + ) + """Status of the latest manual DQ run triggered for this asset.""" + + asset_dq_rule_total_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleTotalCount" + ) + """Count of DQ rules attached to this asset.""" + + asset_dq_rule_failed_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleFailedCount" + ) + """Count of failed DQ rules attached to this asset.""" + + asset_dq_rule_passed_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRulePassedCount" + ) + """Count of passed DQ rules attached to this asset.""" + + asset_dq_result: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQResult" + ) + """Overall result of all the dq rules. If any one rule failed, then fail else pass.""" + + asset_dq_freshness_value: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQFreshnessValue" + ) + """Value of data freshness from Source.""" + + asset_dq_freshness_expectation: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQFreshnessExpectation" + ) + """Expectation of data freshness from Source.""" + + asset_dq_row_scope_filter_column_qualified_name: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQRowScopeFilterColumnQualifiedName") + ) + """Qualified name of the column used for row scope filtering in DQ rules for this asset.""" + + asset_space_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the space that contains this asset.""" + + asset_space_name: Union[str, None, UnsetType] = UNSET + """Name of the space that contains this asset.""" + + asset_gcp_dataplex_metadata_details: Union[Dict[str, Any], None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetGCPDataplexMetadataDetails") + ) + """Metrics captured by GCP Dataplex for objects associated with GCP services.""" + + asset_gcp_dataplex_aspect_list: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetGCPDataplexAspectList" + ) + """List of names of all Aspects linked to this asset.""" + + asset_gcp_dataplex_aspect_field_list: Union[List[str], None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetGCPDataplexAspectFieldList") + ) + """List of field key-values associated with all Aspects linked to this asset.""" + + asset_smus_metadata_form_names: Union[List[str], None, UnsetType] = UNSET + """List of AWS SMUS MetadataForm Names. This is mainly used for filtering purpose.""" + + asset_smus_metadata_form_key_value_details: Union[List[str], None, UnsetType] = ( + UNSET + ) + """List of AWS SMUS MetadataForm Key:Value Details. This is mainly used for filtering purpose.""" + + asset_smus_metadata_form_details: Union[List[Dict[str, Any]], None, UnsetType] = ( + UNSET + ) + """AWS SMUS Asset MetadataForm details""" + + +class IncidentRelationshipAttributes(ReferenceableRelationshipAttributes): + """Incident-specific relationship attributes for nested API format.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + +class IncidentNested(ReferenceableNested): + """Incident in nested API format for high-performance serialization.""" + + attributes: Union[IncidentAttributes, UnsetType] = UNSET + relationship_attributes: Union[IncidentRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[IncidentRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[IncidentRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_INCIDENT_REL_FIELDS: List[str] = [ + *_REFERENCEABLE_REL_FIELDS, + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", +] + + +def _populate_incident_attrs(attrs: IncidentAttributes, obj: Incident) -> None: + """Populate Incident-specific attributes on the attrs struct.""" + _populate_referenceable_attrs(attrs, obj) + attrs.asset_severity = obj.asset_severity + attrs.name = obj.name + attrs.display_name = obj.display_name + attrs.description = obj.description + attrs.asset_source_readme = obj.asset_source_readme + attrs.user_description = obj.user_description + attrs.asset_ai_generated_description = obj.asset_ai_generated_description + attrs.asset_ai_generated_description_confidence = ( + obj.asset_ai_generated_description_confidence + ) + attrs.asset_ai_generated_description_reasoning = ( + obj.asset_ai_generated_description_reasoning + ) + attrs.tenant_id = obj.tenant_id + attrs.certificate_status = obj.certificate_status + attrs.certificate_status_message = obj.certificate_status_message + attrs.certificate_updated_by = obj.certificate_updated_by + attrs.certificate_updated_at = obj.certificate_updated_at + attrs.announcement_title = obj.announcement_title + attrs.announcement_message = obj.announcement_message + attrs.announcement_type = obj.announcement_type + attrs.announcement_updated_at = obj.announcement_updated_at + attrs.announcement_updated_by = obj.announcement_updated_by + attrs.owner_users = obj.owner_users + attrs.owner_groups = obj.owner_groups + attrs.admin_users = obj.admin_users + attrs.admin_groups = obj.admin_groups + attrs.viewer_users = obj.viewer_users + attrs.viewer_groups = obj.viewer_groups + attrs.connector_name = obj.connector_name + attrs.connection_name = obj.connection_name + attrs.connection_qualified_name = obj.connection_qualified_name + attrs.has_lineage = obj.has_lineage + attrs.is_discoverable = obj.is_discoverable + attrs.is_editable = obj.is_editable + attrs.sub_type = obj.sub_type + attrs.view_score = obj.view_score + attrs.popularity_score = obj.popularity_score + attrs.source_owners = obj.source_owners + attrs.asset_source_id = obj.asset_source_id + attrs.source_created_by = obj.source_created_by + attrs.source_created_at = obj.source_created_at + attrs.source_updated_at = obj.source_updated_at + attrs.source_updated_by = obj.source_updated_by + attrs.source_url = obj.source_url + attrs.source_embed_url = obj.source_embed_url + attrs.last_sync_workflow_name = obj.last_sync_workflow_name + attrs.last_sync_run_at = obj.last_sync_run_at + attrs.last_sync_run = obj.last_sync_run + attrs.admin_roles = obj.admin_roles + attrs.source_read_count = obj.source_read_count + attrs.source_read_user_count = obj.source_read_user_count + attrs.source_last_read_at = obj.source_last_read_at + attrs.last_row_changed_at = obj.last_row_changed_at + attrs.source_total_cost = obj.source_total_cost + attrs.source_cost_unit = obj.source_cost_unit + attrs.source_read_query_cost = obj.source_read_query_cost + attrs.source_read_recent_user_list = obj.source_read_recent_user_list + attrs.source_read_recent_user_record_list = obj.source_read_recent_user_record_list + attrs.source_read_top_user_list = obj.source_read_top_user_list + attrs.source_read_top_user_record_list = obj.source_read_top_user_record_list + attrs.source_read_popular_query_record_list = ( + obj.source_read_popular_query_record_list + ) + attrs.source_read_expensive_query_record_list = ( + obj.source_read_expensive_query_record_list + ) + attrs.source_read_slow_query_record_list = obj.source_read_slow_query_record_list + attrs.source_query_compute_cost_list = obj.source_query_compute_cost_list + attrs.source_query_compute_cost_record_list = ( + obj.source_query_compute_cost_record_list + ) + attrs.dbt_qualified_name = obj.dbt_qualified_name + attrs.asset_dbt_workflow_last_updated = obj.asset_dbt_workflow_last_updated + attrs.asset_dbt_alias = obj.asset_dbt_alias + attrs.asset_dbt_meta = obj.asset_dbt_meta + attrs.asset_dbt_unique_id = obj.asset_dbt_unique_id + attrs.asset_dbt_account_name = obj.asset_dbt_account_name + attrs.asset_dbt_project_name = obj.asset_dbt_project_name + attrs.asset_dbt_package_name = obj.asset_dbt_package_name + attrs.asset_dbt_job_name = obj.asset_dbt_job_name + attrs.asset_dbt_job_schedule = obj.asset_dbt_job_schedule + attrs.asset_dbt_job_status = obj.asset_dbt_job_status + attrs.asset_dbt_test_status = obj.asset_dbt_test_status + attrs.asset_dbt_job_schedule_cron_humanized = ( + obj.asset_dbt_job_schedule_cron_humanized + ) + attrs.asset_dbt_job_last_run = obj.asset_dbt_job_last_run + attrs.asset_dbt_job_last_run_url = obj.asset_dbt_job_last_run_url + attrs.asset_dbt_job_last_run_created_at = obj.asset_dbt_job_last_run_created_at + attrs.asset_dbt_job_last_run_updated_at = obj.asset_dbt_job_last_run_updated_at + attrs.asset_dbt_job_last_run_dequed_at = obj.asset_dbt_job_last_run_dequed_at + attrs.asset_dbt_job_last_run_started_at = obj.asset_dbt_job_last_run_started_at + attrs.asset_dbt_job_last_run_total_duration = ( + obj.asset_dbt_job_last_run_total_duration + ) + attrs.asset_dbt_job_last_run_total_duration_humanized = ( + obj.asset_dbt_job_last_run_total_duration_humanized + ) + attrs.asset_dbt_job_last_run_queued_duration = ( + obj.asset_dbt_job_last_run_queued_duration + ) + attrs.asset_dbt_job_last_run_queued_duration_humanized = ( + obj.asset_dbt_job_last_run_queued_duration_humanized + ) + attrs.asset_dbt_job_last_run_run_duration = obj.asset_dbt_job_last_run_run_duration + attrs.asset_dbt_job_last_run_run_duration_humanized = ( + obj.asset_dbt_job_last_run_run_duration_humanized + ) + attrs.asset_dbt_job_last_run_git_branch = obj.asset_dbt_job_last_run_git_branch + attrs.asset_dbt_job_last_run_git_sha = obj.asset_dbt_job_last_run_git_sha + attrs.asset_dbt_job_last_run_status_message = ( + obj.asset_dbt_job_last_run_status_message + ) + attrs.asset_dbt_job_last_run_owner_thread_id = ( + obj.asset_dbt_job_last_run_owner_thread_id + ) + attrs.asset_dbt_job_last_run_executed_by_thread_id = ( + obj.asset_dbt_job_last_run_executed_by_thread_id + ) + attrs.asset_dbt_job_last_run_artifacts_saved = ( + obj.asset_dbt_job_last_run_artifacts_saved + ) + attrs.asset_dbt_job_last_run_artifact_s3_path = ( + obj.asset_dbt_job_last_run_artifact_s3_path + ) + attrs.asset_dbt_job_last_run_has_docs_generated = ( + obj.asset_dbt_job_last_run_has_docs_generated + ) + attrs.asset_dbt_job_last_run_has_sources_generated = ( + obj.asset_dbt_job_last_run_has_sources_generated + ) + attrs.asset_dbt_job_last_run_notifications_sent = ( + obj.asset_dbt_job_last_run_notifications_sent + ) + attrs.asset_dbt_job_next_run = obj.asset_dbt_job_next_run + attrs.asset_dbt_job_next_run_humanized = obj.asset_dbt_job_next_run_humanized + attrs.asset_dbt_environment_name = obj.asset_dbt_environment_name + attrs.asset_dbt_environment_dbt_version = obj.asset_dbt_environment_dbt_version + attrs.asset_dbt_tags = obj.asset_dbt_tags + attrs.asset_dbt_semantic_layer_proxy_url = obj.asset_dbt_semantic_layer_proxy_url + attrs.asset_dbt_source_freshness_criteria = obj.asset_dbt_source_freshness_criteria + attrs.sample_data_url = obj.sample_data_url + attrs.asset_tags = obj.asset_tags + attrs.asset_mc_incident_names = obj.asset_mc_incident_names + attrs.asset_mc_incident_qualified_names = obj.asset_mc_incident_qualified_names + attrs.asset_mc_alert_qualified_names = obj.asset_mc_alert_qualified_names + attrs.asset_mc_monitor_names = obj.asset_mc_monitor_names + attrs.asset_mc_monitor_qualified_names = obj.asset_mc_monitor_qualified_names + attrs.asset_mc_monitor_statuses = obj.asset_mc_monitor_statuses + attrs.asset_mc_monitor_types = obj.asset_mc_monitor_types + attrs.asset_mc_monitor_schedule_types = obj.asset_mc_monitor_schedule_types + attrs.asset_mc_incident_types = obj.asset_mc_incident_types + attrs.asset_mc_incident_sub_types = obj.asset_mc_incident_sub_types + attrs.asset_mc_incident_severities = obj.asset_mc_incident_severities + attrs.asset_mc_incident_priorities = obj.asset_mc_incident_priorities + attrs.asset_mc_incident_states = obj.asset_mc_incident_states + attrs.asset_mc_is_monitored = obj.asset_mc_is_monitored + attrs.asset_mc_last_sync_run_at = obj.asset_mc_last_sync_run_at + attrs.starred_by = obj.starred_by + attrs.starred_details_list = obj.starred_details_list + attrs.starred_count = obj.starred_count + attrs.asset_anomalo_dq_status = obj.asset_anomalo_dq_status + attrs.asset_anomalo_check_count = obj.asset_anomalo_check_count + attrs.asset_anomalo_failed_check_count = obj.asset_anomalo_failed_check_count + attrs.asset_anomalo_check_statuses = obj.asset_anomalo_check_statuses + attrs.asset_anomalo_last_check_run_at = obj.asset_anomalo_last_check_run_at + attrs.asset_anomalo_applied_check_types = obj.asset_anomalo_applied_check_types + attrs.asset_anomalo_failed_check_types = obj.asset_anomalo_failed_check_types + attrs.asset_anomalo_source_url = obj.asset_anomalo_source_url + attrs.asset_soda_dq_status = obj.asset_soda_dq_status + attrs.asset_soda_check_count = obj.asset_soda_check_count + attrs.asset_soda_last_sync_run_at = obj.asset_soda_last_sync_run_at + attrs.asset_soda_last_scan_at = obj.asset_soda_last_scan_at + attrs.asset_soda_check_statuses = obj.asset_soda_check_statuses + attrs.asset_soda_source_url = obj.asset_soda_source_url + attrs.asset_icon = obj.asset_icon + attrs.asset_external_dq_metadata_details = obj.asset_external_dq_metadata_details + attrs.is_partial = obj.is_partial + attrs.is_ai_generated = obj.is_ai_generated + attrs.asset_cover_image = obj.asset_cover_image + attrs.asset_theme_hex = obj.asset_theme_hex + attrs.lexicographical_sort_order = obj.lexicographical_sort_order + attrs.has_contract = obj.has_contract + attrs.asset_redirect_guids = obj.asset_redirect_guids + attrs.asset_policy_guids = obj.asset_policy_guids + attrs.asset_policies_count = obj.asset_policies_count + attrs.domain_guids = obj.domain_guids + attrs.non_compliant_asset_policy_guids = obj.non_compliant_asset_policy_guids + attrs.product_guids = obj.product_guids + attrs.output_product_guids = obj.output_product_guids + attrs.application_qualified_name = obj.application_qualified_name + attrs.application_field_qualified_name = obj.application_field_qualified_name + attrs.asset_user_defined_type = obj.asset_user_defined_type + attrs.asset_internal_popularity_score = obj.asset_internal_popularity_score + attrs.asset_dq_schedule_type = obj.asset_dq_schedule_type + attrs.asset_dq_schedule_crontab = obj.asset_dq_schedule_crontab + attrs.asset_dq_schedule_time_zone = obj.asset_dq_schedule_time_zone + attrs.asset_dq_schedule_source_sync_status = ( + obj.asset_dq_schedule_source_sync_status + ) + attrs.asset_dq_schedule_source_synced_at = obj.asset_dq_schedule_source_synced_at + attrs.asset_dq_schedule_source_sync_error_message = ( + obj.asset_dq_schedule_source_sync_error_message + ) + attrs.asset_dq_schedule_source_sync_error_code = ( + obj.asset_dq_schedule_source_sync_error_code + ) + attrs.asset_dq_schedule_source_sync_raw_error = ( + obj.asset_dq_schedule_source_sync_raw_error + ) + attrs.asset_dq_rule_attached_dimensions = obj.asset_dq_rule_attached_dimensions + attrs.asset_dq_rule_failed_dimensions = obj.asset_dq_rule_failed_dimensions + attrs.asset_dq_rule_passed_dimensions = obj.asset_dq_rule_passed_dimensions + attrs.asset_dq_rule_attached_rule_types = obj.asset_dq_rule_attached_rule_types + attrs.asset_dq_rule_failed_rule_types = obj.asset_dq_rule_failed_rule_types + attrs.asset_dq_rule_passed_rule_types = obj.asset_dq_rule_passed_rule_types + attrs.asset_dq_rule_result_tags = obj.asset_dq_rule_result_tags + attrs.asset_dq_rule_last_run_at = obj.asset_dq_rule_last_run_at + attrs.asset_dq_manual_run_status = obj.asset_dq_manual_run_status + attrs.asset_dq_rule_total_count = obj.asset_dq_rule_total_count + attrs.asset_dq_rule_failed_count = obj.asset_dq_rule_failed_count + attrs.asset_dq_rule_passed_count = obj.asset_dq_rule_passed_count + attrs.asset_dq_result = obj.asset_dq_result + attrs.asset_dq_freshness_value = obj.asset_dq_freshness_value + attrs.asset_dq_freshness_expectation = obj.asset_dq_freshness_expectation + attrs.asset_dq_row_scope_filter_column_qualified_name = ( + obj.asset_dq_row_scope_filter_column_qualified_name + ) + attrs.asset_space_qualified_name = obj.asset_space_qualified_name + attrs.asset_space_name = obj.asset_space_name + attrs.asset_gcp_dataplex_metadata_details = obj.asset_gcp_dataplex_metadata_details + attrs.asset_gcp_dataplex_aspect_list = obj.asset_gcp_dataplex_aspect_list + attrs.asset_gcp_dataplex_aspect_field_list = ( + obj.asset_gcp_dataplex_aspect_field_list + ) + attrs.asset_smus_metadata_form_names = obj.asset_smus_metadata_form_names + attrs.asset_smus_metadata_form_key_value_details = ( + obj.asset_smus_metadata_form_key_value_details + ) + attrs.asset_smus_metadata_form_details = obj.asset_smus_metadata_form_details + + +def _extract_incident_attrs(attrs: IncidentAttributes) -> dict: + """Extract all Incident attributes from the attrs struct into a flat dict.""" + result = _extract_referenceable_attrs(attrs) + result["asset_severity"] = attrs.asset_severity + result["name"] = attrs.name + result["display_name"] = attrs.display_name + result["description"] = attrs.description + result["asset_source_readme"] = attrs.asset_source_readme + result["user_description"] = attrs.user_description + result["asset_ai_generated_description"] = attrs.asset_ai_generated_description + result["asset_ai_generated_description_confidence"] = ( + attrs.asset_ai_generated_description_confidence + ) + result["asset_ai_generated_description_reasoning"] = ( + attrs.asset_ai_generated_description_reasoning + ) + result["tenant_id"] = attrs.tenant_id + result["certificate_status"] = attrs.certificate_status + result["certificate_status_message"] = attrs.certificate_status_message + result["certificate_updated_by"] = attrs.certificate_updated_by + result["certificate_updated_at"] = attrs.certificate_updated_at + result["announcement_title"] = attrs.announcement_title + result["announcement_message"] = attrs.announcement_message + result["announcement_type"] = attrs.announcement_type + result["announcement_updated_at"] = attrs.announcement_updated_at + result["announcement_updated_by"] = attrs.announcement_updated_by + result["owner_users"] = attrs.owner_users + result["owner_groups"] = attrs.owner_groups + result["admin_users"] = attrs.admin_users + result["admin_groups"] = attrs.admin_groups + result["viewer_users"] = attrs.viewer_users + result["viewer_groups"] = attrs.viewer_groups + result["connector_name"] = attrs.connector_name + result["connection_name"] = attrs.connection_name + result["connection_qualified_name"] = attrs.connection_qualified_name + result["has_lineage"] = attrs.has_lineage + result["is_discoverable"] = attrs.is_discoverable + result["is_editable"] = attrs.is_editable + result["sub_type"] = attrs.sub_type + result["view_score"] = attrs.view_score + result["popularity_score"] = attrs.popularity_score + result["source_owners"] = attrs.source_owners + result["asset_source_id"] = attrs.asset_source_id + result["source_created_by"] = attrs.source_created_by + result["source_created_at"] = attrs.source_created_at + result["source_updated_at"] = attrs.source_updated_at + result["source_updated_by"] = attrs.source_updated_by + result["source_url"] = attrs.source_url + result["source_embed_url"] = attrs.source_embed_url + result["last_sync_workflow_name"] = attrs.last_sync_workflow_name + result["last_sync_run_at"] = attrs.last_sync_run_at + result["last_sync_run"] = attrs.last_sync_run + result["admin_roles"] = attrs.admin_roles + result["source_read_count"] = attrs.source_read_count + result["source_read_user_count"] = attrs.source_read_user_count + result["source_last_read_at"] = attrs.source_last_read_at + result["last_row_changed_at"] = attrs.last_row_changed_at + result["source_total_cost"] = attrs.source_total_cost + result["source_cost_unit"] = attrs.source_cost_unit + result["source_read_query_cost"] = attrs.source_read_query_cost + result["source_read_recent_user_list"] = attrs.source_read_recent_user_list + result["source_read_recent_user_record_list"] = ( + attrs.source_read_recent_user_record_list + ) + result["source_read_top_user_list"] = attrs.source_read_top_user_list + result["source_read_top_user_record_list"] = attrs.source_read_top_user_record_list + result["source_read_popular_query_record_list"] = ( + attrs.source_read_popular_query_record_list + ) + result["source_read_expensive_query_record_list"] = ( + attrs.source_read_expensive_query_record_list + ) + result["source_read_slow_query_record_list"] = ( + attrs.source_read_slow_query_record_list + ) + result["source_query_compute_cost_list"] = attrs.source_query_compute_cost_list + result["source_query_compute_cost_record_list"] = ( + attrs.source_query_compute_cost_record_list + ) + result["dbt_qualified_name"] = attrs.dbt_qualified_name + result["asset_dbt_workflow_last_updated"] = attrs.asset_dbt_workflow_last_updated + result["asset_dbt_alias"] = attrs.asset_dbt_alias + result["asset_dbt_meta"] = attrs.asset_dbt_meta + result["asset_dbt_unique_id"] = attrs.asset_dbt_unique_id + result["asset_dbt_account_name"] = attrs.asset_dbt_account_name + result["asset_dbt_project_name"] = attrs.asset_dbt_project_name + result["asset_dbt_package_name"] = attrs.asset_dbt_package_name + result["asset_dbt_job_name"] = attrs.asset_dbt_job_name + result["asset_dbt_job_schedule"] = attrs.asset_dbt_job_schedule + result["asset_dbt_job_status"] = attrs.asset_dbt_job_status + result["asset_dbt_test_status"] = attrs.asset_dbt_test_status + result["asset_dbt_job_schedule_cron_humanized"] = ( + attrs.asset_dbt_job_schedule_cron_humanized + ) + result["asset_dbt_job_last_run"] = attrs.asset_dbt_job_last_run + result["asset_dbt_job_last_run_url"] = attrs.asset_dbt_job_last_run_url + result["asset_dbt_job_last_run_created_at"] = ( + attrs.asset_dbt_job_last_run_created_at + ) + result["asset_dbt_job_last_run_updated_at"] = ( + attrs.asset_dbt_job_last_run_updated_at + ) + result["asset_dbt_job_last_run_dequed_at"] = attrs.asset_dbt_job_last_run_dequed_at + result["asset_dbt_job_last_run_started_at"] = ( + attrs.asset_dbt_job_last_run_started_at + ) + result["asset_dbt_job_last_run_total_duration"] = ( + attrs.asset_dbt_job_last_run_total_duration + ) + result["asset_dbt_job_last_run_total_duration_humanized"] = ( + attrs.asset_dbt_job_last_run_total_duration_humanized + ) + result["asset_dbt_job_last_run_queued_duration"] = ( + attrs.asset_dbt_job_last_run_queued_duration + ) + result["asset_dbt_job_last_run_queued_duration_humanized"] = ( + attrs.asset_dbt_job_last_run_queued_duration_humanized + ) + result["asset_dbt_job_last_run_run_duration"] = ( + attrs.asset_dbt_job_last_run_run_duration + ) + result["asset_dbt_job_last_run_run_duration_humanized"] = ( + attrs.asset_dbt_job_last_run_run_duration_humanized + ) + result["asset_dbt_job_last_run_git_branch"] = ( + attrs.asset_dbt_job_last_run_git_branch + ) + result["asset_dbt_job_last_run_git_sha"] = attrs.asset_dbt_job_last_run_git_sha + result["asset_dbt_job_last_run_status_message"] = ( + attrs.asset_dbt_job_last_run_status_message + ) + result["asset_dbt_job_last_run_owner_thread_id"] = ( + attrs.asset_dbt_job_last_run_owner_thread_id + ) + result["asset_dbt_job_last_run_executed_by_thread_id"] = ( + attrs.asset_dbt_job_last_run_executed_by_thread_id + ) + result["asset_dbt_job_last_run_artifacts_saved"] = ( + attrs.asset_dbt_job_last_run_artifacts_saved + ) + result["asset_dbt_job_last_run_artifact_s3_path"] = ( + attrs.asset_dbt_job_last_run_artifact_s3_path + ) + result["asset_dbt_job_last_run_has_docs_generated"] = ( + attrs.asset_dbt_job_last_run_has_docs_generated + ) + result["asset_dbt_job_last_run_has_sources_generated"] = ( + attrs.asset_dbt_job_last_run_has_sources_generated + ) + result["asset_dbt_job_last_run_notifications_sent"] = ( + attrs.asset_dbt_job_last_run_notifications_sent + ) + result["asset_dbt_job_next_run"] = attrs.asset_dbt_job_next_run + result["asset_dbt_job_next_run_humanized"] = attrs.asset_dbt_job_next_run_humanized + result["asset_dbt_environment_name"] = attrs.asset_dbt_environment_name + result["asset_dbt_environment_dbt_version"] = ( + attrs.asset_dbt_environment_dbt_version + ) + result["asset_dbt_tags"] = attrs.asset_dbt_tags + result["asset_dbt_semantic_layer_proxy_url"] = ( + attrs.asset_dbt_semantic_layer_proxy_url + ) + result["asset_dbt_source_freshness_criteria"] = ( + attrs.asset_dbt_source_freshness_criteria + ) + result["sample_data_url"] = attrs.sample_data_url + result["asset_tags"] = attrs.asset_tags + result["asset_mc_incident_names"] = attrs.asset_mc_incident_names + result["asset_mc_incident_qualified_names"] = ( + attrs.asset_mc_incident_qualified_names + ) + result["asset_mc_alert_qualified_names"] = attrs.asset_mc_alert_qualified_names + result["asset_mc_monitor_names"] = attrs.asset_mc_monitor_names + result["asset_mc_monitor_qualified_names"] = attrs.asset_mc_monitor_qualified_names + result["asset_mc_monitor_statuses"] = attrs.asset_mc_monitor_statuses + result["asset_mc_monitor_types"] = attrs.asset_mc_monitor_types + result["asset_mc_monitor_schedule_types"] = attrs.asset_mc_monitor_schedule_types + result["asset_mc_incident_types"] = attrs.asset_mc_incident_types + result["asset_mc_incident_sub_types"] = attrs.asset_mc_incident_sub_types + result["asset_mc_incident_severities"] = attrs.asset_mc_incident_severities + result["asset_mc_incident_priorities"] = attrs.asset_mc_incident_priorities + result["asset_mc_incident_states"] = attrs.asset_mc_incident_states + result["asset_mc_is_monitored"] = attrs.asset_mc_is_monitored + result["asset_mc_last_sync_run_at"] = attrs.asset_mc_last_sync_run_at + result["starred_by"] = attrs.starred_by + result["starred_details_list"] = attrs.starred_details_list + result["starred_count"] = attrs.starred_count + result["asset_anomalo_dq_status"] = attrs.asset_anomalo_dq_status + result["asset_anomalo_check_count"] = attrs.asset_anomalo_check_count + result["asset_anomalo_failed_check_count"] = attrs.asset_anomalo_failed_check_count + result["asset_anomalo_check_statuses"] = attrs.asset_anomalo_check_statuses + result["asset_anomalo_last_check_run_at"] = attrs.asset_anomalo_last_check_run_at + result["asset_anomalo_applied_check_types"] = ( + attrs.asset_anomalo_applied_check_types + ) + result["asset_anomalo_failed_check_types"] = attrs.asset_anomalo_failed_check_types + result["asset_anomalo_source_url"] = attrs.asset_anomalo_source_url + result["asset_soda_dq_status"] = attrs.asset_soda_dq_status + result["asset_soda_check_count"] = attrs.asset_soda_check_count + result["asset_soda_last_sync_run_at"] = attrs.asset_soda_last_sync_run_at + result["asset_soda_last_scan_at"] = attrs.asset_soda_last_scan_at + result["asset_soda_check_statuses"] = attrs.asset_soda_check_statuses + result["asset_soda_source_url"] = attrs.asset_soda_source_url + result["asset_icon"] = attrs.asset_icon + result["asset_external_dq_metadata_details"] = ( + attrs.asset_external_dq_metadata_details + ) + result["is_partial"] = attrs.is_partial + result["is_ai_generated"] = attrs.is_ai_generated + result["asset_cover_image"] = attrs.asset_cover_image + result["asset_theme_hex"] = attrs.asset_theme_hex + result["lexicographical_sort_order"] = attrs.lexicographical_sort_order + result["has_contract"] = attrs.has_contract + result["asset_redirect_guids"] = attrs.asset_redirect_guids + result["asset_policy_guids"] = attrs.asset_policy_guids + result["asset_policies_count"] = attrs.asset_policies_count + result["domain_guids"] = attrs.domain_guids + result["non_compliant_asset_policy_guids"] = attrs.non_compliant_asset_policy_guids + result["product_guids"] = attrs.product_guids + result["output_product_guids"] = attrs.output_product_guids + result["application_qualified_name"] = attrs.application_qualified_name + result["application_field_qualified_name"] = attrs.application_field_qualified_name + result["asset_user_defined_type"] = attrs.asset_user_defined_type + result["asset_internal_popularity_score"] = attrs.asset_internal_popularity_score + result["asset_dq_schedule_type"] = attrs.asset_dq_schedule_type + result["asset_dq_schedule_crontab"] = attrs.asset_dq_schedule_crontab + result["asset_dq_schedule_time_zone"] = attrs.asset_dq_schedule_time_zone + result["asset_dq_schedule_source_sync_status"] = ( + attrs.asset_dq_schedule_source_sync_status + ) + result["asset_dq_schedule_source_synced_at"] = ( + attrs.asset_dq_schedule_source_synced_at + ) + result["asset_dq_schedule_source_sync_error_message"] = ( + attrs.asset_dq_schedule_source_sync_error_message + ) + result["asset_dq_schedule_source_sync_error_code"] = ( + attrs.asset_dq_schedule_source_sync_error_code + ) + result["asset_dq_schedule_source_sync_raw_error"] = ( + attrs.asset_dq_schedule_source_sync_raw_error + ) + result["asset_dq_rule_attached_dimensions"] = ( + attrs.asset_dq_rule_attached_dimensions + ) + result["asset_dq_rule_failed_dimensions"] = attrs.asset_dq_rule_failed_dimensions + result["asset_dq_rule_passed_dimensions"] = attrs.asset_dq_rule_passed_dimensions + result["asset_dq_rule_attached_rule_types"] = ( + attrs.asset_dq_rule_attached_rule_types + ) + result["asset_dq_rule_failed_rule_types"] = attrs.asset_dq_rule_failed_rule_types + result["asset_dq_rule_passed_rule_types"] = attrs.asset_dq_rule_passed_rule_types + result["asset_dq_rule_result_tags"] = attrs.asset_dq_rule_result_tags + result["asset_dq_rule_last_run_at"] = attrs.asset_dq_rule_last_run_at + result["asset_dq_manual_run_status"] = attrs.asset_dq_manual_run_status + result["asset_dq_rule_total_count"] = attrs.asset_dq_rule_total_count + result["asset_dq_rule_failed_count"] = attrs.asset_dq_rule_failed_count + result["asset_dq_rule_passed_count"] = attrs.asset_dq_rule_passed_count + result["asset_dq_result"] = attrs.asset_dq_result + result["asset_dq_freshness_value"] = attrs.asset_dq_freshness_value + result["asset_dq_freshness_expectation"] = attrs.asset_dq_freshness_expectation + result["asset_dq_row_scope_filter_column_qualified_name"] = ( + attrs.asset_dq_row_scope_filter_column_qualified_name + ) + result["asset_space_qualified_name"] = attrs.asset_space_qualified_name + result["asset_space_name"] = attrs.asset_space_name + result["asset_gcp_dataplex_metadata_details"] = ( + attrs.asset_gcp_dataplex_metadata_details + ) + result["asset_gcp_dataplex_aspect_list"] = attrs.asset_gcp_dataplex_aspect_list + result["asset_gcp_dataplex_aspect_field_list"] = ( + attrs.asset_gcp_dataplex_aspect_field_list + ) + result["asset_smus_metadata_form_names"] = attrs.asset_smus_metadata_form_names + result["asset_smus_metadata_form_key_value_details"] = ( + attrs.asset_smus_metadata_form_key_value_details + ) + result["asset_smus_metadata_form_details"] = attrs.asset_smus_metadata_form_details + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _incident_to_nested(incident: Incident) -> IncidentNested: + """Convert flat Incident to nested format.""" + attrs = IncidentAttributes() + _populate_incident_attrs(attrs, incident) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + incident, _INCIDENT_REL_FIELDS, IncidentRelationshipAttributes + ) + return IncidentNested( + guid=incident.guid, + type_name=incident.type_name, + status=incident.status, + version=incident.version, + create_time=incident.create_time, + update_time=incident.update_time, + created_by=incident.created_by, + updated_by=incident.updated_by, + classifications=incident.classifications, + classification_names=incident.classification_names, + meanings=incident.meanings, + labels=incident.labels, + business_attributes=incident.business_attributes, + custom_attributes=incident.custom_attributes, + pending_tasks=incident.pending_tasks, + proxy=incident.proxy, + is_incomplete=incident.is_incomplete, + provenance_type=incident.provenance_type, + home_id=incident.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _incident_from_nested(nested: IncidentNested) -> Incident: + """Convert nested format to flat Incident.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else IncidentAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _INCIDENT_REL_FIELDS, + IncidentRelationshipAttributes, + ) + return Incident( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_incident_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _incident_to_nested_bytes(incident: Incident, serde: Serde) -> bytes: + """Convert flat Incident to nested JSON bytes.""" + return serde.encode(_incident_to_nested(incident)) + + +def _incident_from_nested_bytes(data: bytes, serde: Serde) -> Incident: + """Convert nested JSON bytes to flat Incident.""" + nested = serde.decode(data, IncidentNested) + return _incident_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + NumericRankField, + RelationField, + TextField, +) + +Incident.ASSET_SEVERITY = KeywordField("assetSeverity", "assetSeverity") +Incident.NAME = KeywordField("name", "name") +Incident.DISPLAY_NAME = KeywordField("displayName", "displayName") +Incident.DESCRIPTION = KeywordField("description", "description") +Incident.ASSET_SOURCE_README = KeywordTextField( + "assetSourceReadme", "assetSourceReadme", "assetSourceReadme.text" +) +Incident.USER_DESCRIPTION = KeywordField("userDescription", "userDescription") +Incident.ASSET_AI_GENERATED_DESCRIPTION = TextField( + "assetAiGeneratedDescription", "assetAiGeneratedDescription" +) +Incident.ASSET_AI_GENERATED_DESCRIPTION_CONFIDENCE = NumericField( + "assetAiGeneratedDescriptionConfidence", "assetAiGeneratedDescriptionConfidence" +) +Incident.ASSET_AI_GENERATED_DESCRIPTION_REASONING = KeywordField( + "assetAiGeneratedDescriptionReasoning", "assetAiGeneratedDescriptionReasoning" +) +Incident.TENANT_ID = KeywordField("tenantId", "tenantId") +Incident.CERTIFICATE_STATUS = KeywordTextField( + "certificateStatus", "certificateStatus", "certificateStatus.text" +) +Incident.CERTIFICATE_STATUS_MESSAGE = KeywordField( + "certificateStatusMessage", "certificateStatusMessage" +) +Incident.CERTIFICATE_UPDATED_BY = KeywordField( + "certificateUpdatedBy", "certificateUpdatedBy" +) +Incident.CERTIFICATE_UPDATED_AT = NumericField( + "certificateUpdatedAt", "certificateUpdatedAt" +) +Incident.ANNOUNCEMENT_TITLE = KeywordField("announcementTitle", "announcementTitle") +Incident.ANNOUNCEMENT_MESSAGE = KeywordField( + "announcementMessage", "announcementMessage" +) +Incident.ANNOUNCEMENT_TYPE = KeywordField("announcementType", "announcementType") +Incident.ANNOUNCEMENT_UPDATED_AT = NumericField( + "announcementUpdatedAt", "announcementUpdatedAt" +) +Incident.ANNOUNCEMENT_UPDATED_BY = KeywordField( + "announcementUpdatedBy", "announcementUpdatedBy" +) +Incident.OWNER_USERS = KeywordField("ownerUsers", "ownerUsers") +Incident.OWNER_GROUPS = KeywordField("ownerGroups", "ownerGroups") +Incident.ADMIN_USERS = KeywordField("adminUsers", "adminUsers") +Incident.ADMIN_GROUPS = KeywordField("adminGroups", "adminGroups") +Incident.VIEWER_USERS = KeywordField("viewerUsers", "viewerUsers") +Incident.VIEWER_GROUPS = KeywordField("viewerGroups", "viewerGroups") +Incident.CONNECTOR_NAME = KeywordField("connectorName", "connectorName") +Incident.CONNECTION_NAME = KeywordTextField( + "connectionName", "connectionName", "connectionName.text" +) +Incident.CONNECTION_QUALIFIED_NAME = KeywordTextField( + "connectionQualifiedName", "connectionQualifiedName", "connectionQualifiedName.text" +) +Incident.HAS_LINEAGE = BooleanField("__hasLineage", "__hasLineage") +Incident.IS_DISCOVERABLE = BooleanField("isDiscoverable", "isDiscoverable") +Incident.IS_EDITABLE = BooleanField("isEditable", "isEditable") +Incident.SUB_TYPE = KeywordField("subType", "subType") +Incident.VIEW_SCORE = NumericField("viewScore", "viewScore") +Incident.POPULARITY_SCORE = NumericField("popularityScore", "popularityScore") +Incident.SOURCE_OWNERS = KeywordField("sourceOwners", "sourceOwners") +Incident.ASSET_SOURCE_ID = KeywordField("assetSourceId", "assetSourceId") +Incident.SOURCE_CREATED_BY = KeywordField("sourceCreatedBy", "sourceCreatedBy") +Incident.SOURCE_CREATED_AT = NumericField("sourceCreatedAt", "sourceCreatedAt") +Incident.SOURCE_UPDATED_AT = NumericField("sourceUpdatedAt", "sourceUpdatedAt") +Incident.SOURCE_UPDATED_BY = KeywordField("sourceUpdatedBy", "sourceUpdatedBy") +Incident.SOURCE_URL = KeywordField("sourceURL", "sourceURL") +Incident.SOURCE_EMBED_URL = KeywordField("sourceEmbedURL", "sourceEmbedURL") +Incident.LAST_SYNC_WORKFLOW_NAME = KeywordField( + "lastSyncWorkflowName", "lastSyncWorkflowName" +) +Incident.LAST_SYNC_RUN_AT = NumericField("lastSyncRunAt", "lastSyncRunAt") +Incident.LAST_SYNC_RUN = KeywordField("lastSyncRun", "lastSyncRun") +Incident.ADMIN_ROLES = KeywordField("adminRoles", "adminRoles") +Incident.SOURCE_READ_COUNT = NumericField("sourceReadCount", "sourceReadCount") +Incident.SOURCE_READ_USER_COUNT = NumericField( + "sourceReadUserCount", "sourceReadUserCount" +) +Incident.SOURCE_LAST_READ_AT = NumericField("sourceLastReadAt", "sourceLastReadAt") +Incident.LAST_ROW_CHANGED_AT = NumericField("lastRowChangedAt", "lastRowChangedAt") +Incident.SOURCE_TOTAL_COST = NumericField("sourceTotalCost", "sourceTotalCost") +Incident.SOURCE_COST_UNIT = KeywordField("sourceCostUnit", "sourceCostUnit") +Incident.SOURCE_READ_QUERY_COST = NumericField( + "sourceReadQueryCost", "sourceReadQueryCost" +) +Incident.SOURCE_READ_RECENT_USER_LIST = KeywordField( + "sourceReadRecentUserList", "sourceReadRecentUserList" +) +Incident.SOURCE_READ_RECENT_USER_RECORD_LIST = KeywordField( + "sourceReadRecentUserRecordList", "sourceReadRecentUserRecordList" +) +Incident.SOURCE_READ_TOP_USER_LIST = KeywordField( + "sourceReadTopUserList", "sourceReadTopUserList" +) +Incident.SOURCE_READ_TOP_USER_RECORD_LIST = KeywordField( + "sourceReadTopUserRecordList", "sourceReadTopUserRecordList" +) +Incident.SOURCE_READ_POPULAR_QUERY_RECORD_LIST = KeywordField( + "sourceReadPopularQueryRecordList", "sourceReadPopularQueryRecordList" +) +Incident.SOURCE_READ_EXPENSIVE_QUERY_RECORD_LIST = KeywordField( + "sourceReadExpensiveQueryRecordList", "sourceReadExpensiveQueryRecordList" +) +Incident.SOURCE_READ_SLOW_QUERY_RECORD_LIST = KeywordField( + "sourceReadSlowQueryRecordList", "sourceReadSlowQueryRecordList" +) +Incident.SOURCE_QUERY_COMPUTE_COST_LIST = KeywordField( + "sourceQueryComputeCostList", "sourceQueryComputeCostList" +) +Incident.SOURCE_QUERY_COMPUTE_COST_RECORD_LIST = KeywordField( + "sourceQueryComputeCostRecordList", "sourceQueryComputeCostRecordList" +) +Incident.DBT_QUALIFIED_NAME = KeywordTextField( + "dbtQualifiedName", "dbtQualifiedName", "dbtQualifiedName.text" +) +Incident.ASSET_DBT_WORKFLOW_LAST_UPDATED = KeywordField( + "assetDbtWorkflowLastUpdated", "assetDbtWorkflowLastUpdated" +) +Incident.ASSET_DBT_ALIAS = KeywordField("assetDbtAlias", "assetDbtAlias") +Incident.ASSET_DBT_META = KeywordField("assetDbtMeta", "assetDbtMeta") +Incident.ASSET_DBT_UNIQUE_ID = KeywordField("assetDbtUniqueId", "assetDbtUniqueId") +Incident.ASSET_DBT_ACCOUNT_NAME = KeywordField( + "assetDbtAccountName", "assetDbtAccountName" +) +Incident.ASSET_DBT_PROJECT_NAME = KeywordField( + "assetDbtProjectName", "assetDbtProjectName" +) +Incident.ASSET_DBT_PACKAGE_NAME = KeywordField( + "assetDbtPackageName", "assetDbtPackageName" +) +Incident.ASSET_DBT_JOB_NAME = KeywordField("assetDbtJobName", "assetDbtJobName") +Incident.ASSET_DBT_JOB_SCHEDULE = KeywordField( + "assetDbtJobSchedule", "assetDbtJobSchedule" +) +Incident.ASSET_DBT_JOB_STATUS = KeywordField("assetDbtJobStatus", "assetDbtJobStatus") +Incident.ASSET_DBT_TEST_STATUS = KeywordField( + "assetDbtTestStatus", "assetDbtTestStatus" +) +Incident.ASSET_DBT_JOB_SCHEDULE_CRON_HUMANIZED = KeywordField( + "assetDbtJobScheduleCronHumanized", "assetDbtJobScheduleCronHumanized" +) +Incident.ASSET_DBT_JOB_LAST_RUN = NumericField( + "assetDbtJobLastRun", "assetDbtJobLastRun" +) +Incident.ASSET_DBT_JOB_LAST_RUN_URL = KeywordField( + "assetDbtJobLastRunUrl", "assetDbtJobLastRunUrl" +) +Incident.ASSET_DBT_JOB_LAST_RUN_CREATED_AT = NumericField( + "assetDbtJobLastRunCreatedAt", "assetDbtJobLastRunCreatedAt" +) +Incident.ASSET_DBT_JOB_LAST_RUN_UPDATED_AT = NumericField( + "assetDbtJobLastRunUpdatedAt", "assetDbtJobLastRunUpdatedAt" +) +Incident.ASSET_DBT_JOB_LAST_RUN_DEQUED_AT = NumericField( + "assetDbtJobLastRunDequedAt", "assetDbtJobLastRunDequedAt" +) +Incident.ASSET_DBT_JOB_LAST_RUN_STARTED_AT = NumericField( + "assetDbtJobLastRunStartedAt", "assetDbtJobLastRunStartedAt" +) +Incident.ASSET_DBT_JOB_LAST_RUN_TOTAL_DURATION = KeywordField( + "assetDbtJobLastRunTotalDuration", "assetDbtJobLastRunTotalDuration" +) +Incident.ASSET_DBT_JOB_LAST_RUN_TOTAL_DURATION_HUMANIZED = KeywordField( + "assetDbtJobLastRunTotalDurationHumanized", + "assetDbtJobLastRunTotalDurationHumanized", +) +Incident.ASSET_DBT_JOB_LAST_RUN_QUEUED_DURATION = KeywordField( + "assetDbtJobLastRunQueuedDuration", "assetDbtJobLastRunQueuedDuration" +) +Incident.ASSET_DBT_JOB_LAST_RUN_QUEUED_DURATION_HUMANIZED = KeywordField( + "assetDbtJobLastRunQueuedDurationHumanized", + "assetDbtJobLastRunQueuedDurationHumanized", +) +Incident.ASSET_DBT_JOB_LAST_RUN_RUN_DURATION = KeywordField( + "assetDbtJobLastRunRunDuration", "assetDbtJobLastRunRunDuration" +) +Incident.ASSET_DBT_JOB_LAST_RUN_RUN_DURATION_HUMANIZED = KeywordField( + "assetDbtJobLastRunRunDurationHumanized", "assetDbtJobLastRunRunDurationHumanized" +) +Incident.ASSET_DBT_JOB_LAST_RUN_GIT_BRANCH = KeywordTextField( + "assetDbtJobLastRunGitBranch", + "assetDbtJobLastRunGitBranch", + "assetDbtJobLastRunGitBranch.text", +) +Incident.ASSET_DBT_JOB_LAST_RUN_GIT_SHA = KeywordField( + "assetDbtJobLastRunGitSha", "assetDbtJobLastRunGitSha" +) +Incident.ASSET_DBT_JOB_LAST_RUN_STATUS_MESSAGE = KeywordField( + "assetDbtJobLastRunStatusMessage", "assetDbtJobLastRunStatusMessage" +) +Incident.ASSET_DBT_JOB_LAST_RUN_OWNER_THREAD_ID = KeywordField( + "assetDbtJobLastRunOwnerThreadId", "assetDbtJobLastRunOwnerThreadId" +) +Incident.ASSET_DBT_JOB_LAST_RUN_EXECUTED_BY_THREAD_ID = KeywordField( + "assetDbtJobLastRunExecutedByThreadId", "assetDbtJobLastRunExecutedByThreadId" +) +Incident.ASSET_DBT_JOB_LAST_RUN_ARTIFACTS_SAVED = BooleanField( + "assetDbtJobLastRunArtifactsSaved", "assetDbtJobLastRunArtifactsSaved" +) +Incident.ASSET_DBT_JOB_LAST_RUN_ARTIFACT_S3_PATH = KeywordField( + "assetDbtJobLastRunArtifactS3Path", "assetDbtJobLastRunArtifactS3Path" +) +Incident.ASSET_DBT_JOB_LAST_RUN_HAS_DOCS_GENERATED = BooleanField( + "assetDbtJobLastRunHasDocsGenerated", "assetDbtJobLastRunHasDocsGenerated" +) +Incident.ASSET_DBT_JOB_LAST_RUN_HAS_SOURCES_GENERATED = BooleanField( + "assetDbtJobLastRunHasSourcesGenerated", "assetDbtJobLastRunHasSourcesGenerated" +) +Incident.ASSET_DBT_JOB_LAST_RUN_NOTIFICATIONS_SENT = BooleanField( + "assetDbtJobLastRunNotificationsSent", "assetDbtJobLastRunNotificationsSent" +) +Incident.ASSET_DBT_JOB_NEXT_RUN = NumericField( + "assetDbtJobNextRun", "assetDbtJobNextRun" +) +Incident.ASSET_DBT_JOB_NEXT_RUN_HUMANIZED = KeywordField( + "assetDbtJobNextRunHumanized", "assetDbtJobNextRunHumanized" +) +Incident.ASSET_DBT_ENVIRONMENT_NAME = KeywordField( + "assetDbtEnvironmentName", "assetDbtEnvironmentName" +) +Incident.ASSET_DBT_ENVIRONMENT_DBT_VERSION = KeywordField( + "assetDbtEnvironmentDbtVersion", "assetDbtEnvironmentDbtVersion" +) +Incident.ASSET_DBT_TAGS = KeywordTextField( + "assetDbtTags", "assetDbtTags", "assetDbtTags.text" +) +Incident.ASSET_DBT_SEMANTIC_LAYER_PROXY_URL = KeywordField( + "assetDbtSemanticLayerProxyUrl", "assetDbtSemanticLayerProxyUrl" +) +Incident.ASSET_DBT_SOURCE_FRESHNESS_CRITERIA = KeywordField( + "assetDbtSourceFreshnessCriteria", "assetDbtSourceFreshnessCriteria" +) +Incident.SAMPLE_DATA_URL = KeywordTextField( + "sampleDataUrl", "sampleDataUrl", "sampleDataUrl.text" +) +Incident.ASSET_TAGS = KeywordTextField("assetTags", "assetTags", "assetTags.text") +Incident.ASSET_MC_INCIDENT_NAMES = KeywordField( + "assetMcIncidentNames", "assetMcIncidentNames" +) +Incident.ASSET_MC_INCIDENT_QUALIFIED_NAMES = KeywordTextField( + "assetMcIncidentQualifiedNames", + "assetMcIncidentQualifiedNames", + "assetMcIncidentQualifiedNames.text", +) +Incident.ASSET_MC_ALERT_QUALIFIED_NAMES = KeywordTextField( + "assetMcAlertQualifiedNames", + "assetMcAlertQualifiedNames", + "assetMcAlertQualifiedNames.text", +) +Incident.ASSET_MC_MONITOR_NAMES = KeywordField( + "assetMcMonitorNames", "assetMcMonitorNames" +) +Incident.ASSET_MC_MONITOR_QUALIFIED_NAMES = KeywordTextField( + "assetMcMonitorQualifiedNames", + "assetMcMonitorQualifiedNames", + "assetMcMonitorQualifiedNames.text", +) +Incident.ASSET_MC_MONITOR_STATUSES = KeywordField( + "assetMcMonitorStatuses", "assetMcMonitorStatuses" +) +Incident.ASSET_MC_MONITOR_TYPES = KeywordField( + "assetMcMonitorTypes", "assetMcMonitorTypes" +) +Incident.ASSET_MC_MONITOR_SCHEDULE_TYPES = KeywordField( + "assetMcMonitorScheduleTypes", "assetMcMonitorScheduleTypes" +) +Incident.ASSET_MC_INCIDENT_TYPES = KeywordField( + "assetMcIncidentTypes", "assetMcIncidentTypes" +) +Incident.ASSET_MC_INCIDENT_SUB_TYPES = KeywordField( + "assetMcIncidentSubTypes", "assetMcIncidentSubTypes" +) +Incident.ASSET_MC_INCIDENT_SEVERITIES = KeywordField( + "assetMcIncidentSeverities", "assetMcIncidentSeverities" +) +Incident.ASSET_MC_INCIDENT_PRIORITIES = KeywordField( + "assetMcIncidentPriorities", "assetMcIncidentPriorities" +) +Incident.ASSET_MC_INCIDENT_STATES = KeywordField( + "assetMcIncidentStates", "assetMcIncidentStates" +) +Incident.ASSET_MC_IS_MONITORED = BooleanField( + "assetMcIsMonitored", "assetMcIsMonitored" +) +Incident.ASSET_MC_LAST_SYNC_RUN_AT = NumericField( + "assetMcLastSyncRunAt", "assetMcLastSyncRunAt" +) +Incident.STARRED_BY = KeywordField("starredBy", "starredBy") +Incident.STARRED_DETAILS_LIST = KeywordField("starredDetailsList", "starredDetailsList") +Incident.STARRED_COUNT = NumericField("starredCount", "starredCount") +Incident.ASSET_ANOMALO_DQ_STATUS = KeywordField( + "assetAnomaloDQStatus", "assetAnomaloDQStatus" +) +Incident.ASSET_ANOMALO_CHECK_COUNT = NumericField( + "assetAnomaloCheckCount", "assetAnomaloCheckCount" +) +Incident.ASSET_ANOMALO_FAILED_CHECK_COUNT = NumericField( + "assetAnomaloFailedCheckCount", "assetAnomaloFailedCheckCount" +) +Incident.ASSET_ANOMALO_CHECK_STATUSES = KeywordField( + "assetAnomaloCheckStatuses", "assetAnomaloCheckStatuses" +) +Incident.ASSET_ANOMALO_LAST_CHECK_RUN_AT = NumericField( + "assetAnomaloLastCheckRunAt", "assetAnomaloLastCheckRunAt" +) +Incident.ASSET_ANOMALO_APPLIED_CHECK_TYPES = KeywordField( + "assetAnomaloAppliedCheckTypes", "assetAnomaloAppliedCheckTypes" +) +Incident.ASSET_ANOMALO_FAILED_CHECK_TYPES = KeywordField( + "assetAnomaloFailedCheckTypes", "assetAnomaloFailedCheckTypes" +) +Incident.ASSET_ANOMALO_SOURCE_URL = KeywordField( + "assetAnomaloSourceUrl", "assetAnomaloSourceUrl" +) +Incident.ASSET_SODA_DQ_STATUS = KeywordField("assetSodaDQStatus", "assetSodaDQStatus") +Incident.ASSET_SODA_CHECK_COUNT = NumericField( + "assetSodaCheckCount", "assetSodaCheckCount" +) +Incident.ASSET_SODA_LAST_SYNC_RUN_AT = NumericField( + "assetSodaLastSyncRunAt", "assetSodaLastSyncRunAt" +) +Incident.ASSET_SODA_LAST_SCAN_AT = NumericField( + "assetSodaLastScanAt", "assetSodaLastScanAt" +) +Incident.ASSET_SODA_CHECK_STATUSES = KeywordField( + "assetSodaCheckStatuses", "assetSodaCheckStatuses" +) +Incident.ASSET_SODA_SOURCE_URL = KeywordField( + "assetSodaSourceURL", "assetSodaSourceURL" +) +Incident.ASSET_ICON = KeywordField("assetIcon", "assetIcon") +Incident.ASSET_EXTERNAL_DQ_METADATA_DETAILS = KeywordField( + "assetExternalDQMetadataDetails", "assetExternalDQMetadataDetails" +) +Incident.IS_PARTIAL = BooleanField("isPartial", "isPartial") +Incident.IS_AI_GENERATED = BooleanField("isAIGenerated", "isAIGenerated") +Incident.ASSET_COVER_IMAGE = KeywordField("assetCoverImage", "assetCoverImage") +Incident.ASSET_THEME_HEX = KeywordField("assetThemeHex", "assetThemeHex") +Incident.LEXICOGRAPHICAL_SORT_ORDER = KeywordField( + "lexicographicalSortOrder", "lexicographicalSortOrder" +) +Incident.HAS_CONTRACT = BooleanField("hasContract", "hasContract") +Incident.ASSET_REDIRECT_GUIDS = KeywordField("assetRedirectGUIDs", "assetRedirectGUIDs") +Incident.ASSET_POLICY_GUIDS = KeywordField("assetPolicyGUIDs", "assetPolicyGUIDs") +Incident.ASSET_POLICIES_COUNT = NumericField("assetPoliciesCount", "assetPoliciesCount") +Incident.DOMAIN_GUIDS = KeywordField("domainGUIDs", "domainGUIDs") +Incident.NON_COMPLIANT_ASSET_POLICY_GUIDS = KeywordField( + "nonCompliantAssetPolicyGUIDs", "nonCompliantAssetPolicyGUIDs" +) +Incident.PRODUCT_GUIDS = KeywordField("productGUIDs", "productGUIDs") +Incident.OUTPUT_PRODUCT_GUIDS = KeywordField("outputProductGUIDs", "outputProductGUIDs") +Incident.APPLICATION_QUALIFIED_NAME = KeywordField( + "applicationQualifiedName", "applicationQualifiedName" +) +Incident.APPLICATION_FIELD_QUALIFIED_NAME = KeywordField( + "applicationFieldQualifiedName", "applicationFieldQualifiedName" +) +Incident.ASSET_USER_DEFINED_TYPE = KeywordField( + "assetUserDefinedType", "assetUserDefinedType" +) +Incident.ASSET_INTERNAL_POPULARITY_SCORE = NumericRankField( + "assetInternalPopularityScore", + "assetInternalPopularityScore", + "assetInternalPopularityScore.rank", +) +Incident.ASSET_DQ_SCHEDULE_TYPE = KeywordField( + "assetDQScheduleType", "assetDQScheduleType" +) +Incident.ASSET_DQ_SCHEDULE_CRONTAB = KeywordField( + "assetDQScheduleCrontab", "assetDQScheduleCrontab" +) +Incident.ASSET_DQ_SCHEDULE_TIME_ZONE = KeywordField( + "assetDQScheduleTimeZone", "assetDQScheduleTimeZone" +) +Incident.ASSET_DQ_SCHEDULE_SOURCE_SYNC_STATUS = KeywordField( + "assetDQScheduleSourceSyncStatus", "assetDQScheduleSourceSyncStatus" +) +Incident.ASSET_DQ_SCHEDULE_SOURCE_SYNCED_AT = NumericField( + "assetDQScheduleSourceSyncedAt", "assetDQScheduleSourceSyncedAt" +) +Incident.ASSET_DQ_SCHEDULE_SOURCE_SYNC_ERROR_MESSAGE = TextField( + "assetDQScheduleSourceSyncErrorMessage", "assetDQScheduleSourceSyncErrorMessage" +) +Incident.ASSET_DQ_SCHEDULE_SOURCE_SYNC_ERROR_CODE = KeywordField( + "assetDQScheduleSourceSyncErrorCode", "assetDQScheduleSourceSyncErrorCode" +) +Incident.ASSET_DQ_SCHEDULE_SOURCE_SYNC_RAW_ERROR = TextField( + "assetDQScheduleSourceSyncRawError", "assetDQScheduleSourceSyncRawError" +) +Incident.ASSET_DQ_RULE_ATTACHED_DIMENSIONS = KeywordField( + "assetDQRuleAttachedDimensions", "assetDQRuleAttachedDimensions" +) +Incident.ASSET_DQ_RULE_FAILED_DIMENSIONS = KeywordField( + "assetDQRuleFailedDimensions", "assetDQRuleFailedDimensions" +) +Incident.ASSET_DQ_RULE_PASSED_DIMENSIONS = KeywordField( + "assetDQRulePassedDimensions", "assetDQRulePassedDimensions" +) +Incident.ASSET_DQ_RULE_ATTACHED_RULE_TYPES = KeywordField( + "assetDQRuleAttachedRuleTypes", "assetDQRuleAttachedRuleTypes" +) +Incident.ASSET_DQ_RULE_FAILED_RULE_TYPES = KeywordField( + "assetDQRuleFailedRuleTypes", "assetDQRuleFailedRuleTypes" +) +Incident.ASSET_DQ_RULE_PASSED_RULE_TYPES = KeywordField( + "assetDQRulePassedRuleTypes", "assetDQRulePassedRuleTypes" +) +Incident.ASSET_DQ_RULE_RESULT_TAGS = KeywordField( + "assetDQRuleResultTags", "assetDQRuleResultTags" +) +Incident.ASSET_DQ_RULE_LAST_RUN_AT = NumericField( + "assetDQRuleLastRunAt", "assetDQRuleLastRunAt" +) +Incident.ASSET_DQ_MANUAL_RUN_STATUS = KeywordField( + "assetDQManualRunStatus", "assetDQManualRunStatus" +) +Incident.ASSET_DQ_RULE_TOTAL_COUNT = NumericField( + "assetDQRuleTotalCount", "assetDQRuleTotalCount" +) +Incident.ASSET_DQ_RULE_FAILED_COUNT = NumericField( + "assetDQRuleFailedCount", "assetDQRuleFailedCount" +) +Incident.ASSET_DQ_RULE_PASSED_COUNT = NumericField( + "assetDQRulePassedCount", "assetDQRulePassedCount" +) +Incident.ASSET_DQ_RESULT = KeywordField("assetDQResult", "assetDQResult") +Incident.ASSET_DQ_FRESHNESS_VALUE = NumericField( + "assetDQFreshnessValue", "assetDQFreshnessValue" +) +Incident.ASSET_DQ_FRESHNESS_EXPECTATION = NumericField( + "assetDQFreshnessExpectation", "assetDQFreshnessExpectation" +) +Incident.ASSET_DQ_ROW_SCOPE_FILTER_COLUMN_QUALIFIED_NAME = KeywordField( + "assetDQRowScopeFilterColumnQualifiedName", + "assetDQRowScopeFilterColumnQualifiedName", +) +Incident.ASSET_SPACE_QUALIFIED_NAME = KeywordField( + "assetSpaceQualifiedName", "assetSpaceQualifiedName" +) +Incident.ASSET_SPACE_NAME = KeywordField("assetSpaceName", "assetSpaceName") +Incident.ASSET_GCP_DATAPLEX_METADATA_DETAILS = KeywordField( + "assetGCPDataplexMetadataDetails", "assetGCPDataplexMetadataDetails" +) +Incident.ASSET_GCP_DATAPLEX_ASPECT_LIST = KeywordField( + "assetGCPDataplexAspectList", "assetGCPDataplexAspectList" +) +Incident.ASSET_GCP_DATAPLEX_ASPECT_FIELD_LIST = KeywordField( + "assetGCPDataplexAspectFieldList", "assetGCPDataplexAspectFieldList" +) +Incident.ASSET_SMUS_METADATA_FORM_NAMES = KeywordTextField( + "assetSmusMetadataFormNames", + "assetSmusMetadataFormNames", + "assetSmusMetadataFormNames.text", +) +Incident.ASSET_SMUS_METADATA_FORM_KEY_VALUE_DETAILS = KeywordTextField( + "assetSmusMetadataFormKeyValueDetails", + "assetSmusMetadataFormKeyValueDetails", + "assetSmusMetadataFormKeyValueDetails.text", +) +Incident.ASSET_SMUS_METADATA_FORM_DETAILS = KeywordField( + "assetSmusMetadataFormDetails", "assetSmusMetadataFormDetails" +) +Incident.ANOMALO_CHECKS = RelationField("anomaloChecks") +Incident.APPLICATION = RelationField("application") +Incident.APPLICATION_FIELD = RelationField("applicationField") +Incident.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Incident.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Incident.METRICS = RelationField("metrics") +Incident.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Incident.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Incident.MEANINGS = RelationField("meanings") +Incident.MC_MONITORS = RelationField("mcMonitors") +Incident.MC_INCIDENTS = RelationField("mcIncidents") +Incident.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Incident.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Incident.FILES = RelationField("files") +Incident.LINKS = RelationField("links") +Incident.README = RelationField("readme") +Incident.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Incident.SODA_CHECKS = RelationField("sodaChecks") diff --git a/pyatlan_v9/model/assets/infrastructure.py b/pyatlan_v9/model/assets/infrastructure.py new file mode 100644 index 000000000..9d388e2ff --- /dev/null +++ b/pyatlan_v9/model/assets/infrastructure.py @@ -0,0 +1,2951 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Infrastructure asset model with flattened inheritance. + +This module provides: +- Infrastructure: Flat asset class (easy to use) +- InfrastructureAttributes: Nested attributes struct (extends AssetAttributes) +- InfrastructureNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Set, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .referenceable import ( + _REFERENCEABLE_REL_FIELDS, + Referenceable, + ReferenceableAttributes, + ReferenceableNested, + ReferenceableRelationshipAttributes, + _extract_referenceable_attrs, + _populate_referenceable_attrs, +) +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +class Infrastructure(Referenceable): + """ + deprecated + """ + + NAME: ClassVar[Any] = None + DISPLAY_NAME: ClassVar[Any] = None + DESCRIPTION: ClassVar[Any] = None + ASSET_SOURCE_README: ClassVar[Any] = None + USER_DESCRIPTION: ClassVar[Any] = None + ASSET_AI_GENERATED_DESCRIPTION: ClassVar[Any] = None + ASSET_AI_GENERATED_DESCRIPTION_CONFIDENCE: ClassVar[Any] = None + ASSET_AI_GENERATED_DESCRIPTION_REASONING: ClassVar[Any] = None + TENANT_ID: ClassVar[Any] = None + CERTIFICATE_STATUS: ClassVar[Any] = None + CERTIFICATE_STATUS_MESSAGE: ClassVar[Any] = None + CERTIFICATE_UPDATED_BY: ClassVar[Any] = None + CERTIFICATE_UPDATED_AT: ClassVar[Any] = None + ANNOUNCEMENT_TITLE: ClassVar[Any] = None + ANNOUNCEMENT_MESSAGE: ClassVar[Any] = None + ANNOUNCEMENT_TYPE: ClassVar[Any] = None + ANNOUNCEMENT_UPDATED_AT: ClassVar[Any] = None + ANNOUNCEMENT_UPDATED_BY: ClassVar[Any] = None + OWNER_USERS: ClassVar[Any] = None + OWNER_GROUPS: ClassVar[Any] = None + ADMIN_USERS: ClassVar[Any] = None + ADMIN_GROUPS: ClassVar[Any] = None + VIEWER_USERS: ClassVar[Any] = None + VIEWER_GROUPS: ClassVar[Any] = None + CONNECTOR_NAME: ClassVar[Any] = None + CONNECTION_NAME: ClassVar[Any] = None + CONNECTION_QUALIFIED_NAME: ClassVar[Any] = None + HAS_LINEAGE: ClassVar[Any] = None + IS_DISCOVERABLE: ClassVar[Any] = None + IS_EDITABLE: ClassVar[Any] = None + SUB_TYPE: ClassVar[Any] = None + VIEW_SCORE: ClassVar[Any] = None + POPULARITY_SCORE: ClassVar[Any] = None + SOURCE_OWNERS: ClassVar[Any] = None + ASSET_SOURCE_ID: ClassVar[Any] = None + SOURCE_CREATED_BY: ClassVar[Any] = None + SOURCE_CREATED_AT: ClassVar[Any] = None + SOURCE_UPDATED_AT: ClassVar[Any] = None + SOURCE_UPDATED_BY: ClassVar[Any] = None + SOURCE_URL: ClassVar[Any] = None + SOURCE_EMBED_URL: ClassVar[Any] = None + LAST_SYNC_WORKFLOW_NAME: ClassVar[Any] = None + LAST_SYNC_RUN_AT: ClassVar[Any] = None + LAST_SYNC_RUN: ClassVar[Any] = None + ADMIN_ROLES: ClassVar[Any] = None + SOURCE_READ_COUNT: ClassVar[Any] = None + SOURCE_READ_USER_COUNT: ClassVar[Any] = None + SOURCE_LAST_READ_AT: ClassVar[Any] = None + LAST_ROW_CHANGED_AT: ClassVar[Any] = None + SOURCE_TOTAL_COST: ClassVar[Any] = None + SOURCE_COST_UNIT: ClassVar[Any] = None + SOURCE_READ_QUERY_COST: ClassVar[Any] = None + SOURCE_READ_RECENT_USER_LIST: ClassVar[Any] = None + SOURCE_READ_RECENT_USER_RECORD_LIST: ClassVar[Any] = None + SOURCE_READ_TOP_USER_LIST: ClassVar[Any] = None + SOURCE_READ_TOP_USER_RECORD_LIST: ClassVar[Any] = None + SOURCE_READ_POPULAR_QUERY_RECORD_LIST: ClassVar[Any] = None + SOURCE_READ_EXPENSIVE_QUERY_RECORD_LIST: ClassVar[Any] = None + SOURCE_READ_SLOW_QUERY_RECORD_LIST: ClassVar[Any] = None + SOURCE_QUERY_COMPUTE_COST_LIST: ClassVar[Any] = None + SOURCE_QUERY_COMPUTE_COST_RECORD_LIST: ClassVar[Any] = None + DBT_QUALIFIED_NAME: ClassVar[Any] = None + ASSET_DBT_WORKFLOW_LAST_UPDATED: ClassVar[Any] = None + ASSET_DBT_ALIAS: ClassVar[Any] = None + ASSET_DBT_META: ClassVar[Any] = None + ASSET_DBT_UNIQUE_ID: ClassVar[Any] = None + ASSET_DBT_ACCOUNT_NAME: ClassVar[Any] = None + ASSET_DBT_PROJECT_NAME: ClassVar[Any] = None + ASSET_DBT_PACKAGE_NAME: ClassVar[Any] = None + ASSET_DBT_JOB_NAME: ClassVar[Any] = None + ASSET_DBT_JOB_SCHEDULE: ClassVar[Any] = None + ASSET_DBT_JOB_STATUS: ClassVar[Any] = None + ASSET_DBT_TEST_STATUS: ClassVar[Any] = None + ASSET_DBT_JOB_SCHEDULE_CRON_HUMANIZED: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_URL: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_CREATED_AT: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_UPDATED_AT: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_DEQUED_AT: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_STARTED_AT: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_TOTAL_DURATION: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_TOTAL_DURATION_HUMANIZED: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_QUEUED_DURATION: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_QUEUED_DURATION_HUMANIZED: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_RUN_DURATION: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_RUN_DURATION_HUMANIZED: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_GIT_BRANCH: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_GIT_SHA: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_STATUS_MESSAGE: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_OWNER_THREAD_ID: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_EXECUTED_BY_THREAD_ID: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_ARTIFACTS_SAVED: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_ARTIFACT_S3_PATH: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_HAS_DOCS_GENERATED: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_HAS_SOURCES_GENERATED: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_NOTIFICATIONS_SENT: ClassVar[Any] = None + ASSET_DBT_JOB_NEXT_RUN: ClassVar[Any] = None + ASSET_DBT_JOB_NEXT_RUN_HUMANIZED: ClassVar[Any] = None + ASSET_DBT_ENVIRONMENT_NAME: ClassVar[Any] = None + ASSET_DBT_ENVIRONMENT_DBT_VERSION: ClassVar[Any] = None + ASSET_DBT_TAGS: ClassVar[Any] = None + ASSET_DBT_SEMANTIC_LAYER_PROXY_URL: ClassVar[Any] = None + ASSET_DBT_SOURCE_FRESHNESS_CRITERIA: ClassVar[Any] = None + SAMPLE_DATA_URL: ClassVar[Any] = None + ASSET_TAGS: ClassVar[Any] = None + ASSET_MC_INCIDENT_NAMES: ClassVar[Any] = None + ASSET_MC_INCIDENT_QUALIFIED_NAMES: ClassVar[Any] = None + ASSET_MC_ALERT_QUALIFIED_NAMES: ClassVar[Any] = None + ASSET_MC_MONITOR_NAMES: ClassVar[Any] = None + ASSET_MC_MONITOR_QUALIFIED_NAMES: ClassVar[Any] = None + ASSET_MC_MONITOR_STATUSES: ClassVar[Any] = None + ASSET_MC_MONITOR_TYPES: ClassVar[Any] = None + ASSET_MC_MONITOR_SCHEDULE_TYPES: ClassVar[Any] = None + ASSET_MC_INCIDENT_TYPES: ClassVar[Any] = None + ASSET_MC_INCIDENT_SUB_TYPES: ClassVar[Any] = None + ASSET_MC_INCIDENT_SEVERITIES: ClassVar[Any] = None + ASSET_MC_INCIDENT_PRIORITIES: ClassVar[Any] = None + ASSET_MC_INCIDENT_STATES: ClassVar[Any] = None + ASSET_MC_IS_MONITORED: ClassVar[Any] = None + ASSET_MC_LAST_SYNC_RUN_AT: ClassVar[Any] = None + STARRED_BY: ClassVar[Any] = None + STARRED_DETAILS_LIST: ClassVar[Any] = None + STARRED_COUNT: ClassVar[Any] = None + ASSET_ANOMALO_DQ_STATUS: ClassVar[Any] = None + ASSET_ANOMALO_CHECK_COUNT: ClassVar[Any] = None + ASSET_ANOMALO_FAILED_CHECK_COUNT: ClassVar[Any] = None + ASSET_ANOMALO_CHECK_STATUSES: ClassVar[Any] = None + ASSET_ANOMALO_LAST_CHECK_RUN_AT: ClassVar[Any] = None + ASSET_ANOMALO_APPLIED_CHECK_TYPES: ClassVar[Any] = None + ASSET_ANOMALO_FAILED_CHECK_TYPES: ClassVar[Any] = None + ASSET_ANOMALO_SOURCE_URL: ClassVar[Any] = None + ASSET_SODA_DQ_STATUS: ClassVar[Any] = None + ASSET_SODA_CHECK_COUNT: ClassVar[Any] = None + ASSET_SODA_LAST_SYNC_RUN_AT: ClassVar[Any] = None + ASSET_SODA_LAST_SCAN_AT: ClassVar[Any] = None + ASSET_SODA_CHECK_STATUSES: ClassVar[Any] = None + ASSET_SODA_SOURCE_URL: ClassVar[Any] = None + ASSET_ICON: ClassVar[Any] = None + ASSET_EXTERNAL_DQ_METADATA_DETAILS: ClassVar[Any] = None + IS_PARTIAL: ClassVar[Any] = None + IS_AI_GENERATED: ClassVar[Any] = None + ASSET_COVER_IMAGE: ClassVar[Any] = None + ASSET_THEME_HEX: ClassVar[Any] = None + LEXICOGRAPHICAL_SORT_ORDER: ClassVar[Any] = None + HAS_CONTRACT: ClassVar[Any] = None + ASSET_REDIRECT_GUIDS: ClassVar[Any] = None + ASSET_POLICY_GUIDS: ClassVar[Any] = None + ASSET_POLICIES_COUNT: ClassVar[Any] = None + DOMAIN_GUIDS: ClassVar[Any] = None + NON_COMPLIANT_ASSET_POLICY_GUIDS: ClassVar[Any] = None + PRODUCT_GUIDS: ClassVar[Any] = None + OUTPUT_PRODUCT_GUIDS: ClassVar[Any] = None + APPLICATION_QUALIFIED_NAME: ClassVar[Any] = None + APPLICATION_FIELD_QUALIFIED_NAME: ClassVar[Any] = None + ASSET_USER_DEFINED_TYPE: ClassVar[Any] = None + ASSET_INTERNAL_POPULARITY_SCORE: ClassVar[Any] = None + ASSET_DQ_SCHEDULE_TYPE: ClassVar[Any] = None + ASSET_DQ_SCHEDULE_CRONTAB: ClassVar[Any] = None + ASSET_DQ_SCHEDULE_TIME_ZONE: ClassVar[Any] = None + ASSET_DQ_SCHEDULE_SOURCE_SYNC_STATUS: ClassVar[Any] = None + ASSET_DQ_SCHEDULE_SOURCE_SYNCED_AT: ClassVar[Any] = None + ASSET_DQ_SCHEDULE_SOURCE_SYNC_ERROR_MESSAGE: ClassVar[Any] = None + ASSET_DQ_SCHEDULE_SOURCE_SYNC_ERROR_CODE: ClassVar[Any] = None + ASSET_DQ_SCHEDULE_SOURCE_SYNC_RAW_ERROR: ClassVar[Any] = None + ASSET_DQ_RULE_ATTACHED_DIMENSIONS: ClassVar[Any] = None + ASSET_DQ_RULE_FAILED_DIMENSIONS: ClassVar[Any] = None + ASSET_DQ_RULE_PASSED_DIMENSIONS: ClassVar[Any] = None + ASSET_DQ_RULE_ATTACHED_RULE_TYPES: ClassVar[Any] = None + ASSET_DQ_RULE_FAILED_RULE_TYPES: ClassVar[Any] = None + ASSET_DQ_RULE_PASSED_RULE_TYPES: ClassVar[Any] = None + ASSET_DQ_RULE_RESULT_TAGS: ClassVar[Any] = None + ASSET_DQ_RULE_LAST_RUN_AT: ClassVar[Any] = None + ASSET_DQ_MANUAL_RUN_STATUS: ClassVar[Any] = None + ASSET_DQ_RULE_TOTAL_COUNT: ClassVar[Any] = None + ASSET_DQ_RULE_FAILED_COUNT: ClassVar[Any] = None + ASSET_DQ_RULE_PASSED_COUNT: ClassVar[Any] = None + ASSET_DQ_RESULT: ClassVar[Any] = None + ASSET_DQ_FRESHNESS_VALUE: ClassVar[Any] = None + ASSET_DQ_FRESHNESS_EXPECTATION: ClassVar[Any] = None + ASSET_DQ_ROW_SCOPE_FILTER_COLUMN_QUALIFIED_NAME: ClassVar[Any] = None + ASSET_SPACE_QUALIFIED_NAME: ClassVar[Any] = None + ASSET_SPACE_NAME: ClassVar[Any] = None + ASSET_GCP_DATAPLEX_METADATA_DETAILS: ClassVar[Any] = None + ASSET_GCP_DATAPLEX_ASPECT_LIST: ClassVar[Any] = None + ASSET_GCP_DATAPLEX_ASPECT_FIELD_LIST: ClassVar[Any] = None + ASSET_SMUS_METADATA_FORM_NAMES: ClassVar[Any] = None + ASSET_SMUS_METADATA_FORM_KEY_VALUE_DETAILS: ClassVar[Any] = None + ASSET_SMUS_METADATA_FORM_DETAILS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Infrastructure" + + name: Union[str, None, UnsetType] = UNSET + """Name of this asset. Fallback for display purposes, if displayName is empty.""" + + display_name: Union[str, None, UnsetType] = UNSET + """Human-readable name of this asset used for display purposes (in user interface).""" + + description: Union[str, None, UnsetType] = UNSET + """Description of this asset, for example as crawled from a source. Fallback for display purposes, if userDescription is empty.""" + + asset_source_readme: Union[str, None, UnsetType] = UNSET + """Readme of this asset, as extracted from source. If present, this will be used for the readme in user interface.""" + + user_description: Union[str, None, UnsetType] = UNSET + """Description of this asset, as provided by a user. If present, this will be used for the description in user interface.""" + + asset_ai_generated_description: Union[str, None, UnsetType] = UNSET + """Description of this asset, generated by AI based on the asset's context. Displayed separately in the UI and can be used to overwrite existing descriptions.""" + + asset_ai_generated_description_confidence: Union[float, None, UnsetType] = UNSET + """Confidence score of the AI-generated description, ranging from 0.0 to 1.0.""" + + asset_ai_generated_description_reasoning: Union[str, None, UnsetType] = UNSET + """Reasoning behind the AI-generated description, explaining how the description was derived from the asset's context.""" + + tenant_id: Union[str, None, UnsetType] = UNSET + """Name of the Atlan workspace in which this asset exists.""" + + certificate_status: Union[str, None, UnsetType] = UNSET + """Status of this asset's certification.""" + + certificate_status_message: Union[str, None, UnsetType] = UNSET + """Human-readable descriptive message used to provide further detail to certificateStatus.""" + + certificate_updated_by: Union[str, None, UnsetType] = UNSET + """Name of the user who last updated the certification of this asset.""" + + certificate_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the certification was last updated, in milliseconds.""" + + announcement_title: Union[str, None, UnsetType] = UNSET + """Brief title for the announcement on this asset. Required when announcementType is specified.""" + + announcement_message: Union[str, None, UnsetType] = UNSET + """Detailed message to include in the announcement on this asset.""" + + announcement_type: Union[str, None, UnsetType] = UNSET + """Type of announcement on this asset.""" + + announcement_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the announcement was last updated, in milliseconds.""" + + announcement_updated_by: Union[str, None, UnsetType] = UNSET + """Name of the user who last updated the announcement.""" + + owner_users: Union[Set[str], None, UnsetType] = UNSET + """List of users who own this asset.""" + + owner_groups: Union[Set[str], None, UnsetType] = UNSET + """List of groups who own this asset.""" + + admin_users: Union[Set[str], None, UnsetType] = UNSET + """List of users who administer this asset. (This is only used for certain asset types.)""" + + admin_groups: Union[Set[str], None, UnsetType] = UNSET + """List of groups who administer this asset. (This is only used for certain asset types.)""" + + viewer_users: Union[Set[str], None, UnsetType] = UNSET + """List of users who can view assets contained in a collection. (This is only used for certain asset types.)""" + + viewer_groups: Union[Set[str], None, UnsetType] = UNSET + """List of groups who can view assets contained in a collection. (This is only used for certain asset types.)""" + + connector_name: Union[str, None, UnsetType] = UNSET + """Type of the connector through which this asset is accessible.""" + + connection_name: Union[str, None, UnsetType] = UNSET + """Simple name of the connection through which this asset is accessible.""" + + connection_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the connection through which this asset is accessible.""" + + has_lineage: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="__hasLineage" + ) + """Whether this asset has lineage (true) or not (false).""" + + is_discoverable: Union[bool, None, UnsetType] = UNSET + """Whether this asset is discoverable through the UI (true) or not (false).""" + + is_editable: Union[bool, None, UnsetType] = UNSET + """Whether this asset can be edited in the UI (true) or not (false).""" + + sub_type: Union[str, None, UnsetType] = UNSET + """Subtype of this asset.""" + + view_score: Union[float, None, UnsetType] = UNSET + """View score for this asset.""" + + popularity_score: Union[float, None, UnsetType] = UNSET + """Popularity score for this asset.""" + + source_owners: Union[str, None, UnsetType] = UNSET + """List of owners of this asset, in the source system.""" + + asset_source_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for this asset in the system from which it was sourced.""" + + source_created_by: Union[str, None, UnsetType] = UNSET + """Name of the user who created this asset, in the source system.""" + + source_created_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was created in the source system, in milliseconds.""" + + source_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last updated in the source system, in milliseconds.""" + + source_updated_by: Union[str, None, UnsetType] = UNSET + """Name of the user who last updated this asset, in the source system.""" + + source_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sourceURL" + ) + """URL to the resource within the source application, used to create a button to view this asset in the source application.""" + + source_embed_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sourceEmbedURL" + ) + """URL to create an embed for a resource (for example, an image of a dashboard) within Atlan.""" + + last_sync_workflow_name: Union[str, None, UnsetType] = UNSET + """Name of the crawler that last synchronized this asset.""" + + last_sync_run_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last crawled, in milliseconds.""" + + last_sync_run: Union[str, None, UnsetType] = UNSET + """Name of the last run of the crawler that last synchronized this asset.""" + + admin_roles: Union[Set[str], None, UnsetType] = UNSET + """List of roles who administer this asset. (This is only used for Connection assets.)""" + + source_read_count: Union[int, None, UnsetType] = UNSET + """Total count of all read operations at source.""" + + source_read_user_count: Union[int, None, UnsetType] = UNSET + """Total number of unique users that read data from asset.""" + + source_last_read_at: Union[int, None, UnsetType] = UNSET + """Timestamp of most recent read operation.""" + + last_row_changed_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) of the last operation that inserted, updated, or deleted rows, in milliseconds.""" + + source_total_cost: Union[float, None, UnsetType] = UNSET + """Total cost of all operations at source.""" + + source_cost_unit: Union[str, None, UnsetType] = UNSET + """The unit of measure for sourceTotalCost.""" + + source_read_query_cost: Union[float, None, UnsetType] = UNSET + """Total cost of read queries at source.""" + + source_read_recent_user_list: Union[List[str], None, UnsetType] = UNSET + """List of usernames of the most recent users who read this asset.""" + + source_read_recent_user_record_list: Union[ + List[Dict[str, Any]], None, UnsetType + ] = UNSET + """List of usernames with extra insights for the most recent users who read this asset.""" + + source_read_top_user_list: Union[List[str], None, UnsetType] = UNSET + """List of usernames of the users who read this asset the most.""" + + source_read_top_user_record_list: Union[List[Dict[str, Any]], None, UnsetType] = ( + UNSET + ) + """List of usernames with extra insights for the users who read this asset the most.""" + + source_read_popular_query_record_list: Union[ + List[Dict[str, Any]], None, UnsetType + ] = UNSET + """List of the most popular queries that accessed this asset.""" + + source_read_expensive_query_record_list: Union[ + List[Dict[str, Any]], None, UnsetType + ] = UNSET + """List of the most expensive queries that accessed this asset.""" + + source_read_slow_query_record_list: Union[List[Dict[str, Any]], None, UnsetType] = ( + UNSET + ) + """List of the slowest queries that accessed this asset.""" + + source_query_compute_cost_list: Union[List[str], None, UnsetType] = UNSET + """List of most expensive warehouse names.""" + + source_query_compute_cost_record_list: Union[ + List[Dict[str, Any]], None, UnsetType + ] = UNSET + """List of most expensive warehouses with extra insights.""" + + dbt_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of this asset in dbt.""" + + asset_dbt_workflow_last_updated: Union[str, None, UnsetType] = UNSET + """Name of the DBT workflow in Atlan that last updated the asset.""" + + asset_dbt_alias: Union[str, None, UnsetType] = UNSET + """Alias of this asset in dbt.""" + + asset_dbt_meta: Union[str, None, UnsetType] = UNSET + """Metadata for this asset in dbt, specifically everything under the 'meta' key in the dbt object.""" + + asset_dbt_unique_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of this asset in dbt.""" + + asset_dbt_account_name: Union[str, None, UnsetType] = UNSET + """Name of the account in which this asset exists in dbt.""" + + asset_dbt_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which this asset exists in dbt.""" + + asset_dbt_package_name: Union[str, None, UnsetType] = UNSET + """Name of the package in which this asset exists in dbt.""" + + asset_dbt_job_name: Union[str, None, UnsetType] = UNSET + """Name of the job that materialized this asset in dbt.""" + + asset_dbt_job_schedule: Union[str, None, UnsetType] = UNSET + """Schedule of the job that materialized this asset in dbt.""" + + asset_dbt_job_status: Union[str, None, UnsetType] = UNSET + """Status of the job that materialized this asset in dbt.""" + + asset_dbt_test_status: Union[str, None, UnsetType] = UNSET + """All associated dbt test statuses.""" + + asset_dbt_job_schedule_cron_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable cron schedule of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt last ran, in milliseconds.""" + + asset_dbt_job_last_run_url: Union[str, None, UnsetType] = UNSET + """URL of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_created_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt was last created, in milliseconds.""" + + asset_dbt_job_last_run_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt was last updated, in milliseconds.""" + + asset_dbt_job_last_run_dequed_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt was dequeued, in milliseconds.""" + + asset_dbt_job_last_run_started_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt was started running, in milliseconds.""" + + asset_dbt_job_last_run_total_duration: Union[str, None, UnsetType] = UNSET + """Total duration of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_total_duration_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable total duration of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_queued_duration: Union[str, None, UnsetType] = UNSET + """Total duration the job that materialized this asset in dbt spent being queued.""" + + asset_dbt_job_last_run_queued_duration_humanized: Union[str, None, UnsetType] = ( + UNSET + ) + """Human-readable total duration of the last run of the job that materialized this asset in dbt spend being queued.""" + + asset_dbt_job_last_run_run_duration: Union[str, None, UnsetType] = UNSET + """Run duration of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_run_duration_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable run duration of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_git_branch: Union[str, None, UnsetType] = UNSET + """Branch in git from which the last run of the job that materialized this asset in dbt ran.""" + + asset_dbt_job_last_run_git_sha: Union[str, None, UnsetType] = UNSET + """SHA hash in git for the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_status_message: Union[str, None, UnsetType] = UNSET + """Status message of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_owner_thread_id: Union[str, None, UnsetType] = UNSET + """Thread ID of the owner of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_executed_by_thread_id: Union[str, None, UnsetType] = UNSET + """Thread ID of the user who executed the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_artifacts_saved: Union[bool, None, UnsetType] = UNSET + """Whether artifacts were saved from the last run of the job that materialized this asset in dbt (true) or not (false).""" + + asset_dbt_job_last_run_artifact_s3_path: Union[str, None, UnsetType] = UNSET + """Path in S3 to the artifacts saved from the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_has_docs_generated: Union[bool, None, UnsetType] = UNSET + """Whether docs were generated from the last run of the job that materialized this asset in dbt (true) or not (false).""" + + asset_dbt_job_last_run_has_sources_generated: Union[bool, None, UnsetType] = UNSET + """Whether sources were generated from the last run of the job that materialized this asset in dbt (true) or not (false).""" + + asset_dbt_job_last_run_notifications_sent: Union[bool, None, UnsetType] = UNSET + """Whether notifications were sent from the last run of the job that materialized this asset in dbt (true) or not (false).""" + + asset_dbt_job_next_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) when the next run of the job that materializes this asset in dbt is scheduled.""" + + asset_dbt_job_next_run_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable time when the next run of the job that materializes this asset in dbt is scheduled.""" + + asset_dbt_environment_name: Union[str, None, UnsetType] = UNSET + """Name of the environment in which this asset is materialized in dbt.""" + + asset_dbt_environment_dbt_version: Union[str, None, UnsetType] = UNSET + """Version of the environment in which this asset is materialized in dbt.""" + + asset_dbt_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset in dbt.""" + + asset_dbt_semantic_layer_proxy_url: Union[str, None, UnsetType] = UNSET + """URL of the semantic layer proxy for this asset in dbt.""" + + asset_dbt_source_freshness_criteria: Union[str, None, UnsetType] = UNSET + """Freshness criteria for the source of this asset in dbt.""" + + sample_data_url: Union[str, None, UnsetType] = UNSET + """URL for sample data for this asset.""" + + asset_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset.""" + + asset_mc_incident_names: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident names attached to this asset.""" + + asset_mc_incident_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of unique Monte Carlo incident names attached to this asset.""" + + asset_mc_alert_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of unique Monte Carlo alert names attached to this asset.""" + + asset_mc_monitor_names: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo monitor names attached to this asset.""" + + asset_mc_monitor_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of unique Monte Carlo monitor names attached to this asset.""" + + asset_mc_monitor_statuses: Union[List[str], None, UnsetType] = UNSET + """Statuses of all associated Monte Carlo monitors.""" + + asset_mc_monitor_types: Union[List[str], None, UnsetType] = UNSET + """Types of all associated Monte Carlo monitors.""" + + asset_mc_monitor_schedule_types: Union[List[str], None, UnsetType] = UNSET + """Schedules of all associated Monte Carlo monitors.""" + + asset_mc_incident_types: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident types associated with this asset.""" + + asset_mc_incident_sub_types: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident sub-types associated with this asset.""" + + asset_mc_incident_severities: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident severities associated with this asset.""" + + asset_mc_incident_priorities: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident priorities associated with this asset.""" + + asset_mc_incident_states: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident states associated with this asset.""" + + asset_mc_is_monitored: Union[bool, None, UnsetType] = UNSET + """Tracks whether this asset is monitored by MC or not""" + + asset_mc_last_sync_run_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last synced from Monte Carlo.""" + + starred_by: Union[List[str], None, UnsetType] = UNSET + """Users who have starred this asset.""" + + starred_details_list: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of usernames with extra information of the users who have starred an asset.""" + + starred_count: Union[int, None, UnsetType] = UNSET + """Number of users who have starred this asset.""" + + asset_anomalo_dq_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetAnomaloDQStatus" + ) + """Status of data quality from Anomalo.""" + + asset_anomalo_check_count: Union[int, None, UnsetType] = UNSET + """Total number of checks present in Anomalo for this asset.""" + + asset_anomalo_failed_check_count: Union[int, None, UnsetType] = UNSET + """Total number of checks failed in Anomalo for this asset.""" + + asset_anomalo_check_statuses: Union[str, None, UnsetType] = UNSET + """Stringified JSON object containing status of all Anomalo checks associated to this asset.""" + + asset_anomalo_last_check_run_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the last check was run via Anomalo.""" + + asset_anomalo_applied_check_types: Union[List[str], None, UnsetType] = UNSET + """All associated Anomalo check types.""" + + asset_anomalo_failed_check_types: Union[List[str], None, UnsetType] = UNSET + """All associated Anomalo failed check types.""" + + asset_anomalo_source_url: Union[str, None, UnsetType] = UNSET + """URL of the source in Anomalo.""" + + asset_soda_dq_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetSodaDQStatus" + ) + """Status of data quality from Soda.""" + + asset_soda_check_count: Union[int, None, UnsetType] = UNSET + """Number of checks done via Soda.""" + + asset_soda_last_sync_run_at: Union[int, None, UnsetType] = UNSET + """""" + + asset_soda_last_scan_at: Union[int, None, UnsetType] = UNSET + """""" + + asset_soda_check_statuses: Union[str, None, UnsetType] = UNSET + """All associated Soda check statuses.""" + + asset_soda_source_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetSodaSourceURL" + ) + """""" + + asset_icon: Union[str, None, UnsetType] = UNSET + """Name of the icon to use for this asset. (Only applies to glossaries, currently.)""" + + asset_external_dq_metadata_details: Union[ + Dict[str, Dict[str, Any]], None, UnsetType + ] = msgspec.field(default=UNSET, name="assetExternalDQMetadataDetails") + """DQ metadata captured for asset from external DQ tool(s).""" + + is_partial: Union[bool, None, UnsetType] = UNSET + """Indicates this asset is not fully-known, if true.""" + + is_ai_generated: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="isAIGenerated" + ) + """""" + + asset_cover_image: Union[str, None, UnsetType] = UNSET + """Cover image to use for this asset in the UI (applicable to only a few asset types).""" + + asset_theme_hex: Union[str, None, UnsetType] = UNSET + """Color (in hexadecimal RGB) to use to represent this asset.""" + + lexicographical_sort_order: Union[str, None, UnsetType] = UNSET + """Custom order for sorting purpose, managed by client""" + + has_contract: Union[bool, None, UnsetType] = UNSET + """Whether this asset has contract (true) or not (false).""" + + asset_redirect_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetRedirectGUIDs" + ) + """Array of asset ids that equivalent to this asset.""" + + asset_policy_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetPolicyGUIDs" + ) + """Array of policy ids governing this asset""" + + asset_policies_count: Union[int, None, UnsetType] = UNSET + """Count of policies inside the asset""" + + domain_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="domainGUIDs" + ) + """Array of domain guids linked to this asset""" + + non_compliant_asset_policy_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="nonCompliantAssetPolicyGUIDs" + ) + """Array of policy ids non-compliant to this asset""" + + product_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="productGUIDs" + ) + """Array of product guids linked to this asset""" + + output_product_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="outputProductGUIDs" + ) + """Array of product guids which have this asset as outputPort""" + + application_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the Application that contains this asset.""" + + application_field_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the ApplicationField that contains this asset.""" + + asset_user_defined_type: Union[str, None, UnsetType] = UNSET + """Name to use for this type of asset, as a subtype of the actual typeName.""" + + asset_internal_popularity_score: Union[float, None, UnsetType] = UNSET + """Internal Popularity score for this asset.""" + + asset_dq_schedule_type: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleType" + ) + """Type of schedule of the DQ rule that will run at datasource.""" + + asset_dq_schedule_crontab: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleCrontab" + ) + """Crontab of the DQ rule that will run at datasource.""" + + asset_dq_schedule_time_zone: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleTimeZone" + ) + """Timezone of the DQ rule schedule that will run at datasource""" + + asset_dq_schedule_source_sync_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleSourceSyncStatus" + ) + """Latest sync status of the schedule to the source.""" + + asset_dq_schedule_source_synced_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleSourceSyncedAt" + ) + """Time (epoch) at which the schedule synced to the source.""" + + asset_dq_schedule_source_sync_error_message: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQScheduleSourceSyncErrorMessage") + ) + """Error message in the case of sync state being "error".""" + + asset_dq_schedule_source_sync_error_code: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQScheduleSourceSyncErrorCode") + ) + """Error code in the case of sync state being "error".""" + + asset_dq_schedule_source_sync_raw_error: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQScheduleSourceSyncRawError") + ) + """Raw error message from the source.""" + + asset_dq_rule_attached_dimensions: Union[List[str], None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQRuleAttachedDimensions") + ) + """List of all the dimensions of attached rules.""" + + asset_dq_rule_failed_dimensions: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleFailedDimensions" + ) + """List of all the dimensions of failed rules.""" + + asset_dq_rule_passed_dimensions: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRulePassedDimensions" + ) + """List of all the dimensions for which all the rules passed.""" + + asset_dq_rule_attached_rule_types: Union[List[str], None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQRuleAttachedRuleTypes") + ) + """List of all the types of attached rules.""" + + asset_dq_rule_failed_rule_types: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleFailedRuleTypes" + ) + """List of all the types of failed rules.""" + + asset_dq_rule_passed_rule_types: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRulePassedRuleTypes" + ) + """List of all the types of rules for which all the rules passed.""" + + asset_dq_rule_result_tags: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleResultTags" + ) + """Tag for the result of the DQ rules. Eg, rule_pass:completeness:null_count.""" + + asset_dq_rule_last_run_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleLastRunAt" + ) + """Time (epoch) at which the last dq rule ran.""" + + asset_dq_manual_run_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQManualRunStatus" + ) + """Status of the latest manual DQ run triggered for this asset.""" + + asset_dq_rule_total_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleTotalCount" + ) + """Count of DQ rules attached to this asset.""" + + asset_dq_rule_failed_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleFailedCount" + ) + """Count of failed DQ rules attached to this asset.""" + + asset_dq_rule_passed_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRulePassedCount" + ) + """Count of passed DQ rules attached to this asset.""" + + asset_dq_result: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQResult" + ) + """Overall result of all the dq rules. If any one rule failed, then fail else pass.""" + + asset_dq_freshness_value: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQFreshnessValue" + ) + """Value of data freshness from Source.""" + + asset_dq_freshness_expectation: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQFreshnessExpectation" + ) + """Expectation of data freshness from Source.""" + + asset_dq_row_scope_filter_column_qualified_name: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQRowScopeFilterColumnQualifiedName") + ) + """Qualified name of the column used for row scope filtering in DQ rules for this asset.""" + + asset_space_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the space that contains this asset.""" + + asset_space_name: Union[str, None, UnsetType] = UNSET + """Name of the space that contains this asset.""" + + asset_gcp_dataplex_metadata_details: Union[Dict[str, Any], None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetGCPDataplexMetadataDetails") + ) + """Metrics captured by GCP Dataplex for objects associated with GCP services.""" + + asset_gcp_dataplex_aspect_list: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetGCPDataplexAspectList" + ) + """List of names of all Aspects linked to this asset.""" + + asset_gcp_dataplex_aspect_field_list: Union[List[str], None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetGCPDataplexAspectFieldList") + ) + """List of field key-values associated with all Aspects linked to this asset.""" + + asset_smus_metadata_form_names: Union[List[str], None, UnsetType] = UNSET + """List of AWS SMUS MetadataForm Names. This is mainly used for filtering purpose.""" + + asset_smus_metadata_form_key_value_details: Union[List[str], None, UnsetType] = ( + UNSET + ) + """List of AWS SMUS MetadataForm Key:Value Details. This is mainly used for filtering purpose.""" + + asset_smus_metadata_form_details: Union[List[Dict[str, Any]], None, UnsetType] = ( + UNSET + ) + """AWS SMUS Asset MetadataForm details""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Infrastructure" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _infrastructure_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Infrastructure: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Infrastructure instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _infrastructure_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class InfrastructureAttributes(ReferenceableAttributes): + """Infrastructure-specific attributes for nested API format.""" + + name: Union[str, None, UnsetType] = UNSET + """Name of this asset. Fallback for display purposes, if displayName is empty.""" + + display_name: Union[str, None, UnsetType] = UNSET + """Human-readable name of this asset used for display purposes (in user interface).""" + + description: Union[str, None, UnsetType] = UNSET + """Description of this asset, for example as crawled from a source. Fallback for display purposes, if userDescription is empty.""" + + asset_source_readme: Union[str, None, UnsetType] = UNSET + """Readme of this asset, as extracted from source. If present, this will be used for the readme in user interface.""" + + user_description: Union[str, None, UnsetType] = UNSET + """Description of this asset, as provided by a user. If present, this will be used for the description in user interface.""" + + asset_ai_generated_description: Union[str, None, UnsetType] = UNSET + """Description of this asset, generated by AI based on the asset's context. Displayed separately in the UI and can be used to overwrite existing descriptions.""" + + asset_ai_generated_description_confidence: Union[float, None, UnsetType] = UNSET + """Confidence score of the AI-generated description, ranging from 0.0 to 1.0.""" + + asset_ai_generated_description_reasoning: Union[str, None, UnsetType] = UNSET + """Reasoning behind the AI-generated description, explaining how the description was derived from the asset's context.""" + + tenant_id: Union[str, None, UnsetType] = UNSET + """Name of the Atlan workspace in which this asset exists.""" + + certificate_status: Union[str, None, UnsetType] = UNSET + """Status of this asset's certification.""" + + certificate_status_message: Union[str, None, UnsetType] = UNSET + """Human-readable descriptive message used to provide further detail to certificateStatus.""" + + certificate_updated_by: Union[str, None, UnsetType] = UNSET + """Name of the user who last updated the certification of this asset.""" + + certificate_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the certification was last updated, in milliseconds.""" + + announcement_title: Union[str, None, UnsetType] = UNSET + """Brief title for the announcement on this asset. Required when announcementType is specified.""" + + announcement_message: Union[str, None, UnsetType] = UNSET + """Detailed message to include in the announcement on this asset.""" + + announcement_type: Union[str, None, UnsetType] = UNSET + """Type of announcement on this asset.""" + + announcement_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the announcement was last updated, in milliseconds.""" + + announcement_updated_by: Union[str, None, UnsetType] = UNSET + """Name of the user who last updated the announcement.""" + + owner_users: Union[Set[str], None, UnsetType] = UNSET + """List of users who own this asset.""" + + owner_groups: Union[Set[str], None, UnsetType] = UNSET + """List of groups who own this asset.""" + + admin_users: Union[Set[str], None, UnsetType] = UNSET + """List of users who administer this asset. (This is only used for certain asset types.)""" + + admin_groups: Union[Set[str], None, UnsetType] = UNSET + """List of groups who administer this asset. (This is only used for certain asset types.)""" + + viewer_users: Union[Set[str], None, UnsetType] = UNSET + """List of users who can view assets contained in a collection. (This is only used for certain asset types.)""" + + viewer_groups: Union[Set[str], None, UnsetType] = UNSET + """List of groups who can view assets contained in a collection. (This is only used for certain asset types.)""" + + connector_name: Union[str, None, UnsetType] = UNSET + """Type of the connector through which this asset is accessible.""" + + connection_name: Union[str, None, UnsetType] = UNSET + """Simple name of the connection through which this asset is accessible.""" + + connection_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the connection through which this asset is accessible.""" + + has_lineage: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="__hasLineage" + ) + """Whether this asset has lineage (true) or not (false).""" + + is_discoverable: Union[bool, None, UnsetType] = UNSET + """Whether this asset is discoverable through the UI (true) or not (false).""" + + is_editable: Union[bool, None, UnsetType] = UNSET + """Whether this asset can be edited in the UI (true) or not (false).""" + + sub_type: Union[str, None, UnsetType] = UNSET + """Subtype of this asset.""" + + view_score: Union[float, None, UnsetType] = UNSET + """View score for this asset.""" + + popularity_score: Union[float, None, UnsetType] = UNSET + """Popularity score for this asset.""" + + source_owners: Union[str, None, UnsetType] = UNSET + """List of owners of this asset, in the source system.""" + + asset_source_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for this asset in the system from which it was sourced.""" + + source_created_by: Union[str, None, UnsetType] = UNSET + """Name of the user who created this asset, in the source system.""" + + source_created_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was created in the source system, in milliseconds.""" + + source_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last updated in the source system, in milliseconds.""" + + source_updated_by: Union[str, None, UnsetType] = UNSET + """Name of the user who last updated this asset, in the source system.""" + + source_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sourceURL" + ) + """URL to the resource within the source application, used to create a button to view this asset in the source application.""" + + source_embed_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sourceEmbedURL" + ) + """URL to create an embed for a resource (for example, an image of a dashboard) within Atlan.""" + + last_sync_workflow_name: Union[str, None, UnsetType] = UNSET + """Name of the crawler that last synchronized this asset.""" + + last_sync_run_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last crawled, in milliseconds.""" + + last_sync_run: Union[str, None, UnsetType] = UNSET + """Name of the last run of the crawler that last synchronized this asset.""" + + admin_roles: Union[Set[str], None, UnsetType] = UNSET + """List of roles who administer this asset. (This is only used for Connection assets.)""" + + source_read_count: Union[int, None, UnsetType] = UNSET + """Total count of all read operations at source.""" + + source_read_user_count: Union[int, None, UnsetType] = UNSET + """Total number of unique users that read data from asset.""" + + source_last_read_at: Union[int, None, UnsetType] = UNSET + """Timestamp of most recent read operation.""" + + last_row_changed_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) of the last operation that inserted, updated, or deleted rows, in milliseconds.""" + + source_total_cost: Union[float, None, UnsetType] = UNSET + """Total cost of all operations at source.""" + + source_cost_unit: Union[str, None, UnsetType] = UNSET + """The unit of measure for sourceTotalCost.""" + + source_read_query_cost: Union[float, None, UnsetType] = UNSET + """Total cost of read queries at source.""" + + source_read_recent_user_list: Union[List[str], None, UnsetType] = UNSET + """List of usernames of the most recent users who read this asset.""" + + source_read_recent_user_record_list: Union[ + List[Dict[str, Any]], None, UnsetType + ] = UNSET + """List of usernames with extra insights for the most recent users who read this asset.""" + + source_read_top_user_list: Union[List[str], None, UnsetType] = UNSET + """List of usernames of the users who read this asset the most.""" + + source_read_top_user_record_list: Union[List[Dict[str, Any]], None, UnsetType] = ( + UNSET + ) + """List of usernames with extra insights for the users who read this asset the most.""" + + source_read_popular_query_record_list: Union[ + List[Dict[str, Any]], None, UnsetType + ] = UNSET + """List of the most popular queries that accessed this asset.""" + + source_read_expensive_query_record_list: Union[ + List[Dict[str, Any]], None, UnsetType + ] = UNSET + """List of the most expensive queries that accessed this asset.""" + + source_read_slow_query_record_list: Union[List[Dict[str, Any]], None, UnsetType] = ( + UNSET + ) + """List of the slowest queries that accessed this asset.""" + + source_query_compute_cost_list: Union[List[str], None, UnsetType] = UNSET + """List of most expensive warehouse names.""" + + source_query_compute_cost_record_list: Union[ + List[Dict[str, Any]], None, UnsetType + ] = UNSET + """List of most expensive warehouses with extra insights.""" + + dbt_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of this asset in dbt.""" + + asset_dbt_workflow_last_updated: Union[str, None, UnsetType] = UNSET + """Name of the DBT workflow in Atlan that last updated the asset.""" + + asset_dbt_alias: Union[str, None, UnsetType] = UNSET + """Alias of this asset in dbt.""" + + asset_dbt_meta: Union[str, None, UnsetType] = UNSET + """Metadata for this asset in dbt, specifically everything under the 'meta' key in the dbt object.""" + + asset_dbt_unique_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of this asset in dbt.""" + + asset_dbt_account_name: Union[str, None, UnsetType] = UNSET + """Name of the account in which this asset exists in dbt.""" + + asset_dbt_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which this asset exists in dbt.""" + + asset_dbt_package_name: Union[str, None, UnsetType] = UNSET + """Name of the package in which this asset exists in dbt.""" + + asset_dbt_job_name: Union[str, None, UnsetType] = UNSET + """Name of the job that materialized this asset in dbt.""" + + asset_dbt_job_schedule: Union[str, None, UnsetType] = UNSET + """Schedule of the job that materialized this asset in dbt.""" + + asset_dbt_job_status: Union[str, None, UnsetType] = UNSET + """Status of the job that materialized this asset in dbt.""" + + asset_dbt_test_status: Union[str, None, UnsetType] = UNSET + """All associated dbt test statuses.""" + + asset_dbt_job_schedule_cron_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable cron schedule of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt last ran, in milliseconds.""" + + asset_dbt_job_last_run_url: Union[str, None, UnsetType] = UNSET + """URL of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_created_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt was last created, in milliseconds.""" + + asset_dbt_job_last_run_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt was last updated, in milliseconds.""" + + asset_dbt_job_last_run_dequed_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt was dequeued, in milliseconds.""" + + asset_dbt_job_last_run_started_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt was started running, in milliseconds.""" + + asset_dbt_job_last_run_total_duration: Union[str, None, UnsetType] = UNSET + """Total duration of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_total_duration_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable total duration of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_queued_duration: Union[str, None, UnsetType] = UNSET + """Total duration the job that materialized this asset in dbt spent being queued.""" + + asset_dbt_job_last_run_queued_duration_humanized: Union[str, None, UnsetType] = ( + UNSET + ) + """Human-readable total duration of the last run of the job that materialized this asset in dbt spend being queued.""" + + asset_dbt_job_last_run_run_duration: Union[str, None, UnsetType] = UNSET + """Run duration of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_run_duration_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable run duration of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_git_branch: Union[str, None, UnsetType] = UNSET + """Branch in git from which the last run of the job that materialized this asset in dbt ran.""" + + asset_dbt_job_last_run_git_sha: Union[str, None, UnsetType] = UNSET + """SHA hash in git for the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_status_message: Union[str, None, UnsetType] = UNSET + """Status message of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_owner_thread_id: Union[str, None, UnsetType] = UNSET + """Thread ID of the owner of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_executed_by_thread_id: Union[str, None, UnsetType] = UNSET + """Thread ID of the user who executed the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_artifacts_saved: Union[bool, None, UnsetType] = UNSET + """Whether artifacts were saved from the last run of the job that materialized this asset in dbt (true) or not (false).""" + + asset_dbt_job_last_run_artifact_s3_path: Union[str, None, UnsetType] = UNSET + """Path in S3 to the artifacts saved from the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_has_docs_generated: Union[bool, None, UnsetType] = UNSET + """Whether docs were generated from the last run of the job that materialized this asset in dbt (true) or not (false).""" + + asset_dbt_job_last_run_has_sources_generated: Union[bool, None, UnsetType] = UNSET + """Whether sources were generated from the last run of the job that materialized this asset in dbt (true) or not (false).""" + + asset_dbt_job_last_run_notifications_sent: Union[bool, None, UnsetType] = UNSET + """Whether notifications were sent from the last run of the job that materialized this asset in dbt (true) or not (false).""" + + asset_dbt_job_next_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) when the next run of the job that materializes this asset in dbt is scheduled.""" + + asset_dbt_job_next_run_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable time when the next run of the job that materializes this asset in dbt is scheduled.""" + + asset_dbt_environment_name: Union[str, None, UnsetType] = UNSET + """Name of the environment in which this asset is materialized in dbt.""" + + asset_dbt_environment_dbt_version: Union[str, None, UnsetType] = UNSET + """Version of the environment in which this asset is materialized in dbt.""" + + asset_dbt_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset in dbt.""" + + asset_dbt_semantic_layer_proxy_url: Union[str, None, UnsetType] = UNSET + """URL of the semantic layer proxy for this asset in dbt.""" + + asset_dbt_source_freshness_criteria: Union[str, None, UnsetType] = UNSET + """Freshness criteria for the source of this asset in dbt.""" + + sample_data_url: Union[str, None, UnsetType] = UNSET + """URL for sample data for this asset.""" + + asset_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset.""" + + asset_mc_incident_names: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident names attached to this asset.""" + + asset_mc_incident_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of unique Monte Carlo incident names attached to this asset.""" + + asset_mc_alert_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of unique Monte Carlo alert names attached to this asset.""" + + asset_mc_monitor_names: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo monitor names attached to this asset.""" + + asset_mc_monitor_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of unique Monte Carlo monitor names attached to this asset.""" + + asset_mc_monitor_statuses: Union[List[str], None, UnsetType] = UNSET + """Statuses of all associated Monte Carlo monitors.""" + + asset_mc_monitor_types: Union[List[str], None, UnsetType] = UNSET + """Types of all associated Monte Carlo monitors.""" + + asset_mc_monitor_schedule_types: Union[List[str], None, UnsetType] = UNSET + """Schedules of all associated Monte Carlo monitors.""" + + asset_mc_incident_types: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident types associated with this asset.""" + + asset_mc_incident_sub_types: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident sub-types associated with this asset.""" + + asset_mc_incident_severities: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident severities associated with this asset.""" + + asset_mc_incident_priorities: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident priorities associated with this asset.""" + + asset_mc_incident_states: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident states associated with this asset.""" + + asset_mc_is_monitored: Union[bool, None, UnsetType] = UNSET + """Tracks whether this asset is monitored by MC or not""" + + asset_mc_last_sync_run_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last synced from Monte Carlo.""" + + starred_by: Union[List[str], None, UnsetType] = UNSET + """Users who have starred this asset.""" + + starred_details_list: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of usernames with extra information of the users who have starred an asset.""" + + starred_count: Union[int, None, UnsetType] = UNSET + """Number of users who have starred this asset.""" + + asset_anomalo_dq_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetAnomaloDQStatus" + ) + """Status of data quality from Anomalo.""" + + asset_anomalo_check_count: Union[int, None, UnsetType] = UNSET + """Total number of checks present in Anomalo for this asset.""" + + asset_anomalo_failed_check_count: Union[int, None, UnsetType] = UNSET + """Total number of checks failed in Anomalo for this asset.""" + + asset_anomalo_check_statuses: Union[str, None, UnsetType] = UNSET + """Stringified JSON object containing status of all Anomalo checks associated to this asset.""" + + asset_anomalo_last_check_run_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the last check was run via Anomalo.""" + + asset_anomalo_applied_check_types: Union[List[str], None, UnsetType] = UNSET + """All associated Anomalo check types.""" + + asset_anomalo_failed_check_types: Union[List[str], None, UnsetType] = UNSET + """All associated Anomalo failed check types.""" + + asset_anomalo_source_url: Union[str, None, UnsetType] = UNSET + """URL of the source in Anomalo.""" + + asset_soda_dq_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetSodaDQStatus" + ) + """Status of data quality from Soda.""" + + asset_soda_check_count: Union[int, None, UnsetType] = UNSET + """Number of checks done via Soda.""" + + asset_soda_last_sync_run_at: Union[int, None, UnsetType] = UNSET + """""" + + asset_soda_last_scan_at: Union[int, None, UnsetType] = UNSET + """""" + + asset_soda_check_statuses: Union[str, None, UnsetType] = UNSET + """All associated Soda check statuses.""" + + asset_soda_source_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetSodaSourceURL" + ) + """""" + + asset_icon: Union[str, None, UnsetType] = UNSET + """Name of the icon to use for this asset. (Only applies to glossaries, currently.)""" + + asset_external_dq_metadata_details: Union[ + Dict[str, Dict[str, Any]], None, UnsetType + ] = msgspec.field(default=UNSET, name="assetExternalDQMetadataDetails") + """DQ metadata captured for asset from external DQ tool(s).""" + + is_partial: Union[bool, None, UnsetType] = UNSET + """Indicates this asset is not fully-known, if true.""" + + is_ai_generated: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="isAIGenerated" + ) + """""" + + asset_cover_image: Union[str, None, UnsetType] = UNSET + """Cover image to use for this asset in the UI (applicable to only a few asset types).""" + + asset_theme_hex: Union[str, None, UnsetType] = UNSET + """Color (in hexadecimal RGB) to use to represent this asset.""" + + lexicographical_sort_order: Union[str, None, UnsetType] = UNSET + """Custom order for sorting purpose, managed by client""" + + has_contract: Union[bool, None, UnsetType] = UNSET + """Whether this asset has contract (true) or not (false).""" + + asset_redirect_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetRedirectGUIDs" + ) + """Array of asset ids that equivalent to this asset.""" + + asset_policy_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetPolicyGUIDs" + ) + """Array of policy ids governing this asset""" + + asset_policies_count: Union[int, None, UnsetType] = UNSET + """Count of policies inside the asset""" + + domain_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="domainGUIDs" + ) + """Array of domain guids linked to this asset""" + + non_compliant_asset_policy_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="nonCompliantAssetPolicyGUIDs" + ) + """Array of policy ids non-compliant to this asset""" + + product_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="productGUIDs" + ) + """Array of product guids linked to this asset""" + + output_product_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="outputProductGUIDs" + ) + """Array of product guids which have this asset as outputPort""" + + application_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the Application that contains this asset.""" + + application_field_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the ApplicationField that contains this asset.""" + + asset_user_defined_type: Union[str, None, UnsetType] = UNSET + """Name to use for this type of asset, as a subtype of the actual typeName.""" + + asset_internal_popularity_score: Union[float, None, UnsetType] = UNSET + """Internal Popularity score for this asset.""" + + asset_dq_schedule_type: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleType" + ) + """Type of schedule of the DQ rule that will run at datasource.""" + + asset_dq_schedule_crontab: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleCrontab" + ) + """Crontab of the DQ rule that will run at datasource.""" + + asset_dq_schedule_time_zone: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleTimeZone" + ) + """Timezone of the DQ rule schedule that will run at datasource""" + + asset_dq_schedule_source_sync_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleSourceSyncStatus" + ) + """Latest sync status of the schedule to the source.""" + + asset_dq_schedule_source_synced_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleSourceSyncedAt" + ) + """Time (epoch) at which the schedule synced to the source.""" + + asset_dq_schedule_source_sync_error_message: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQScheduleSourceSyncErrorMessage") + ) + """Error message in the case of sync state being "error".""" + + asset_dq_schedule_source_sync_error_code: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQScheduleSourceSyncErrorCode") + ) + """Error code in the case of sync state being "error".""" + + asset_dq_schedule_source_sync_raw_error: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQScheduleSourceSyncRawError") + ) + """Raw error message from the source.""" + + asset_dq_rule_attached_dimensions: Union[List[str], None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQRuleAttachedDimensions") + ) + """List of all the dimensions of attached rules.""" + + asset_dq_rule_failed_dimensions: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleFailedDimensions" + ) + """List of all the dimensions of failed rules.""" + + asset_dq_rule_passed_dimensions: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRulePassedDimensions" + ) + """List of all the dimensions for which all the rules passed.""" + + asset_dq_rule_attached_rule_types: Union[List[str], None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQRuleAttachedRuleTypes") + ) + """List of all the types of attached rules.""" + + asset_dq_rule_failed_rule_types: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleFailedRuleTypes" + ) + """List of all the types of failed rules.""" + + asset_dq_rule_passed_rule_types: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRulePassedRuleTypes" + ) + """List of all the types of rules for which all the rules passed.""" + + asset_dq_rule_result_tags: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleResultTags" + ) + """Tag for the result of the DQ rules. Eg, rule_pass:completeness:null_count.""" + + asset_dq_rule_last_run_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleLastRunAt" + ) + """Time (epoch) at which the last dq rule ran.""" + + asset_dq_manual_run_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQManualRunStatus" + ) + """Status of the latest manual DQ run triggered for this asset.""" + + asset_dq_rule_total_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleTotalCount" + ) + """Count of DQ rules attached to this asset.""" + + asset_dq_rule_failed_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleFailedCount" + ) + """Count of failed DQ rules attached to this asset.""" + + asset_dq_rule_passed_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRulePassedCount" + ) + """Count of passed DQ rules attached to this asset.""" + + asset_dq_result: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQResult" + ) + """Overall result of all the dq rules. If any one rule failed, then fail else pass.""" + + asset_dq_freshness_value: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQFreshnessValue" + ) + """Value of data freshness from Source.""" + + asset_dq_freshness_expectation: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQFreshnessExpectation" + ) + """Expectation of data freshness from Source.""" + + asset_dq_row_scope_filter_column_qualified_name: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQRowScopeFilterColumnQualifiedName") + ) + """Qualified name of the column used for row scope filtering in DQ rules for this asset.""" + + asset_space_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the space that contains this asset.""" + + asset_space_name: Union[str, None, UnsetType] = UNSET + """Name of the space that contains this asset.""" + + asset_gcp_dataplex_metadata_details: Union[Dict[str, Any], None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetGCPDataplexMetadataDetails") + ) + """Metrics captured by GCP Dataplex for objects associated with GCP services.""" + + asset_gcp_dataplex_aspect_list: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetGCPDataplexAspectList" + ) + """List of names of all Aspects linked to this asset.""" + + asset_gcp_dataplex_aspect_field_list: Union[List[str], None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetGCPDataplexAspectFieldList") + ) + """List of field key-values associated with all Aspects linked to this asset.""" + + asset_smus_metadata_form_names: Union[List[str], None, UnsetType] = UNSET + """List of AWS SMUS MetadataForm Names. This is mainly used for filtering purpose.""" + + asset_smus_metadata_form_key_value_details: Union[List[str], None, UnsetType] = ( + UNSET + ) + """List of AWS SMUS MetadataForm Key:Value Details. This is mainly used for filtering purpose.""" + + asset_smus_metadata_form_details: Union[List[Dict[str, Any]], None, UnsetType] = ( + UNSET + ) + """AWS SMUS Asset MetadataForm details""" + + +class InfrastructureRelationshipAttributes(ReferenceableRelationshipAttributes): + """Infrastructure-specific relationship attributes for nested API format.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + +class InfrastructureNested(ReferenceableNested): + """Infrastructure in nested API format for high-performance serialization.""" + + attributes: Union[InfrastructureAttributes, UnsetType] = UNSET + relationship_attributes: Union[InfrastructureRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + InfrastructureRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + InfrastructureRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_INFRASTRUCTURE_REL_FIELDS: List[str] = [ + *_REFERENCEABLE_REL_FIELDS, + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", +] + + +def _populate_infrastructure_attrs( + attrs: InfrastructureAttributes, obj: Infrastructure +) -> None: + """Populate Infrastructure-specific attributes on the attrs struct.""" + _populate_referenceable_attrs(attrs, obj) + attrs.name = obj.name + attrs.display_name = obj.display_name + attrs.description = obj.description + attrs.asset_source_readme = obj.asset_source_readme + attrs.user_description = obj.user_description + attrs.asset_ai_generated_description = obj.asset_ai_generated_description + attrs.asset_ai_generated_description_confidence = ( + obj.asset_ai_generated_description_confidence + ) + attrs.asset_ai_generated_description_reasoning = ( + obj.asset_ai_generated_description_reasoning + ) + attrs.tenant_id = obj.tenant_id + attrs.certificate_status = obj.certificate_status + attrs.certificate_status_message = obj.certificate_status_message + attrs.certificate_updated_by = obj.certificate_updated_by + attrs.certificate_updated_at = obj.certificate_updated_at + attrs.announcement_title = obj.announcement_title + attrs.announcement_message = obj.announcement_message + attrs.announcement_type = obj.announcement_type + attrs.announcement_updated_at = obj.announcement_updated_at + attrs.announcement_updated_by = obj.announcement_updated_by + attrs.owner_users = obj.owner_users + attrs.owner_groups = obj.owner_groups + attrs.admin_users = obj.admin_users + attrs.admin_groups = obj.admin_groups + attrs.viewer_users = obj.viewer_users + attrs.viewer_groups = obj.viewer_groups + attrs.connector_name = obj.connector_name + attrs.connection_name = obj.connection_name + attrs.connection_qualified_name = obj.connection_qualified_name + attrs.has_lineage = obj.has_lineage + attrs.is_discoverable = obj.is_discoverable + attrs.is_editable = obj.is_editable + attrs.sub_type = obj.sub_type + attrs.view_score = obj.view_score + attrs.popularity_score = obj.popularity_score + attrs.source_owners = obj.source_owners + attrs.asset_source_id = obj.asset_source_id + attrs.source_created_by = obj.source_created_by + attrs.source_created_at = obj.source_created_at + attrs.source_updated_at = obj.source_updated_at + attrs.source_updated_by = obj.source_updated_by + attrs.source_url = obj.source_url + attrs.source_embed_url = obj.source_embed_url + attrs.last_sync_workflow_name = obj.last_sync_workflow_name + attrs.last_sync_run_at = obj.last_sync_run_at + attrs.last_sync_run = obj.last_sync_run + attrs.admin_roles = obj.admin_roles + attrs.source_read_count = obj.source_read_count + attrs.source_read_user_count = obj.source_read_user_count + attrs.source_last_read_at = obj.source_last_read_at + attrs.last_row_changed_at = obj.last_row_changed_at + attrs.source_total_cost = obj.source_total_cost + attrs.source_cost_unit = obj.source_cost_unit + attrs.source_read_query_cost = obj.source_read_query_cost + attrs.source_read_recent_user_list = obj.source_read_recent_user_list + attrs.source_read_recent_user_record_list = obj.source_read_recent_user_record_list + attrs.source_read_top_user_list = obj.source_read_top_user_list + attrs.source_read_top_user_record_list = obj.source_read_top_user_record_list + attrs.source_read_popular_query_record_list = ( + obj.source_read_popular_query_record_list + ) + attrs.source_read_expensive_query_record_list = ( + obj.source_read_expensive_query_record_list + ) + attrs.source_read_slow_query_record_list = obj.source_read_slow_query_record_list + attrs.source_query_compute_cost_list = obj.source_query_compute_cost_list + attrs.source_query_compute_cost_record_list = ( + obj.source_query_compute_cost_record_list + ) + attrs.dbt_qualified_name = obj.dbt_qualified_name + attrs.asset_dbt_workflow_last_updated = obj.asset_dbt_workflow_last_updated + attrs.asset_dbt_alias = obj.asset_dbt_alias + attrs.asset_dbt_meta = obj.asset_dbt_meta + attrs.asset_dbt_unique_id = obj.asset_dbt_unique_id + attrs.asset_dbt_account_name = obj.asset_dbt_account_name + attrs.asset_dbt_project_name = obj.asset_dbt_project_name + attrs.asset_dbt_package_name = obj.asset_dbt_package_name + attrs.asset_dbt_job_name = obj.asset_dbt_job_name + attrs.asset_dbt_job_schedule = obj.asset_dbt_job_schedule + attrs.asset_dbt_job_status = obj.asset_dbt_job_status + attrs.asset_dbt_test_status = obj.asset_dbt_test_status + attrs.asset_dbt_job_schedule_cron_humanized = ( + obj.asset_dbt_job_schedule_cron_humanized + ) + attrs.asset_dbt_job_last_run = obj.asset_dbt_job_last_run + attrs.asset_dbt_job_last_run_url = obj.asset_dbt_job_last_run_url + attrs.asset_dbt_job_last_run_created_at = obj.asset_dbt_job_last_run_created_at + attrs.asset_dbt_job_last_run_updated_at = obj.asset_dbt_job_last_run_updated_at + attrs.asset_dbt_job_last_run_dequed_at = obj.asset_dbt_job_last_run_dequed_at + attrs.asset_dbt_job_last_run_started_at = obj.asset_dbt_job_last_run_started_at + attrs.asset_dbt_job_last_run_total_duration = ( + obj.asset_dbt_job_last_run_total_duration + ) + attrs.asset_dbt_job_last_run_total_duration_humanized = ( + obj.asset_dbt_job_last_run_total_duration_humanized + ) + attrs.asset_dbt_job_last_run_queued_duration = ( + obj.asset_dbt_job_last_run_queued_duration + ) + attrs.asset_dbt_job_last_run_queued_duration_humanized = ( + obj.asset_dbt_job_last_run_queued_duration_humanized + ) + attrs.asset_dbt_job_last_run_run_duration = obj.asset_dbt_job_last_run_run_duration + attrs.asset_dbt_job_last_run_run_duration_humanized = ( + obj.asset_dbt_job_last_run_run_duration_humanized + ) + attrs.asset_dbt_job_last_run_git_branch = obj.asset_dbt_job_last_run_git_branch + attrs.asset_dbt_job_last_run_git_sha = obj.asset_dbt_job_last_run_git_sha + attrs.asset_dbt_job_last_run_status_message = ( + obj.asset_dbt_job_last_run_status_message + ) + attrs.asset_dbt_job_last_run_owner_thread_id = ( + obj.asset_dbt_job_last_run_owner_thread_id + ) + attrs.asset_dbt_job_last_run_executed_by_thread_id = ( + obj.asset_dbt_job_last_run_executed_by_thread_id + ) + attrs.asset_dbt_job_last_run_artifacts_saved = ( + obj.asset_dbt_job_last_run_artifacts_saved + ) + attrs.asset_dbt_job_last_run_artifact_s3_path = ( + obj.asset_dbt_job_last_run_artifact_s3_path + ) + attrs.asset_dbt_job_last_run_has_docs_generated = ( + obj.asset_dbt_job_last_run_has_docs_generated + ) + attrs.asset_dbt_job_last_run_has_sources_generated = ( + obj.asset_dbt_job_last_run_has_sources_generated + ) + attrs.asset_dbt_job_last_run_notifications_sent = ( + obj.asset_dbt_job_last_run_notifications_sent + ) + attrs.asset_dbt_job_next_run = obj.asset_dbt_job_next_run + attrs.asset_dbt_job_next_run_humanized = obj.asset_dbt_job_next_run_humanized + attrs.asset_dbt_environment_name = obj.asset_dbt_environment_name + attrs.asset_dbt_environment_dbt_version = obj.asset_dbt_environment_dbt_version + attrs.asset_dbt_tags = obj.asset_dbt_tags + attrs.asset_dbt_semantic_layer_proxy_url = obj.asset_dbt_semantic_layer_proxy_url + attrs.asset_dbt_source_freshness_criteria = obj.asset_dbt_source_freshness_criteria + attrs.sample_data_url = obj.sample_data_url + attrs.asset_tags = obj.asset_tags + attrs.asset_mc_incident_names = obj.asset_mc_incident_names + attrs.asset_mc_incident_qualified_names = obj.asset_mc_incident_qualified_names + attrs.asset_mc_alert_qualified_names = obj.asset_mc_alert_qualified_names + attrs.asset_mc_monitor_names = obj.asset_mc_monitor_names + attrs.asset_mc_monitor_qualified_names = obj.asset_mc_monitor_qualified_names + attrs.asset_mc_monitor_statuses = obj.asset_mc_monitor_statuses + attrs.asset_mc_monitor_types = obj.asset_mc_monitor_types + attrs.asset_mc_monitor_schedule_types = obj.asset_mc_monitor_schedule_types + attrs.asset_mc_incident_types = obj.asset_mc_incident_types + attrs.asset_mc_incident_sub_types = obj.asset_mc_incident_sub_types + attrs.asset_mc_incident_severities = obj.asset_mc_incident_severities + attrs.asset_mc_incident_priorities = obj.asset_mc_incident_priorities + attrs.asset_mc_incident_states = obj.asset_mc_incident_states + attrs.asset_mc_is_monitored = obj.asset_mc_is_monitored + attrs.asset_mc_last_sync_run_at = obj.asset_mc_last_sync_run_at + attrs.starred_by = obj.starred_by + attrs.starred_details_list = obj.starred_details_list + attrs.starred_count = obj.starred_count + attrs.asset_anomalo_dq_status = obj.asset_anomalo_dq_status + attrs.asset_anomalo_check_count = obj.asset_anomalo_check_count + attrs.asset_anomalo_failed_check_count = obj.asset_anomalo_failed_check_count + attrs.asset_anomalo_check_statuses = obj.asset_anomalo_check_statuses + attrs.asset_anomalo_last_check_run_at = obj.asset_anomalo_last_check_run_at + attrs.asset_anomalo_applied_check_types = obj.asset_anomalo_applied_check_types + attrs.asset_anomalo_failed_check_types = obj.asset_anomalo_failed_check_types + attrs.asset_anomalo_source_url = obj.asset_anomalo_source_url + attrs.asset_soda_dq_status = obj.asset_soda_dq_status + attrs.asset_soda_check_count = obj.asset_soda_check_count + attrs.asset_soda_last_sync_run_at = obj.asset_soda_last_sync_run_at + attrs.asset_soda_last_scan_at = obj.asset_soda_last_scan_at + attrs.asset_soda_check_statuses = obj.asset_soda_check_statuses + attrs.asset_soda_source_url = obj.asset_soda_source_url + attrs.asset_icon = obj.asset_icon + attrs.asset_external_dq_metadata_details = obj.asset_external_dq_metadata_details + attrs.is_partial = obj.is_partial + attrs.is_ai_generated = obj.is_ai_generated + attrs.asset_cover_image = obj.asset_cover_image + attrs.asset_theme_hex = obj.asset_theme_hex + attrs.lexicographical_sort_order = obj.lexicographical_sort_order + attrs.has_contract = obj.has_contract + attrs.asset_redirect_guids = obj.asset_redirect_guids + attrs.asset_policy_guids = obj.asset_policy_guids + attrs.asset_policies_count = obj.asset_policies_count + attrs.domain_guids = obj.domain_guids + attrs.non_compliant_asset_policy_guids = obj.non_compliant_asset_policy_guids + attrs.product_guids = obj.product_guids + attrs.output_product_guids = obj.output_product_guids + attrs.application_qualified_name = obj.application_qualified_name + attrs.application_field_qualified_name = obj.application_field_qualified_name + attrs.asset_user_defined_type = obj.asset_user_defined_type + attrs.asset_internal_popularity_score = obj.asset_internal_popularity_score + attrs.asset_dq_schedule_type = obj.asset_dq_schedule_type + attrs.asset_dq_schedule_crontab = obj.asset_dq_schedule_crontab + attrs.asset_dq_schedule_time_zone = obj.asset_dq_schedule_time_zone + attrs.asset_dq_schedule_source_sync_status = ( + obj.asset_dq_schedule_source_sync_status + ) + attrs.asset_dq_schedule_source_synced_at = obj.asset_dq_schedule_source_synced_at + attrs.asset_dq_schedule_source_sync_error_message = ( + obj.asset_dq_schedule_source_sync_error_message + ) + attrs.asset_dq_schedule_source_sync_error_code = ( + obj.asset_dq_schedule_source_sync_error_code + ) + attrs.asset_dq_schedule_source_sync_raw_error = ( + obj.asset_dq_schedule_source_sync_raw_error + ) + attrs.asset_dq_rule_attached_dimensions = obj.asset_dq_rule_attached_dimensions + attrs.asset_dq_rule_failed_dimensions = obj.asset_dq_rule_failed_dimensions + attrs.asset_dq_rule_passed_dimensions = obj.asset_dq_rule_passed_dimensions + attrs.asset_dq_rule_attached_rule_types = obj.asset_dq_rule_attached_rule_types + attrs.asset_dq_rule_failed_rule_types = obj.asset_dq_rule_failed_rule_types + attrs.asset_dq_rule_passed_rule_types = obj.asset_dq_rule_passed_rule_types + attrs.asset_dq_rule_result_tags = obj.asset_dq_rule_result_tags + attrs.asset_dq_rule_last_run_at = obj.asset_dq_rule_last_run_at + attrs.asset_dq_manual_run_status = obj.asset_dq_manual_run_status + attrs.asset_dq_rule_total_count = obj.asset_dq_rule_total_count + attrs.asset_dq_rule_failed_count = obj.asset_dq_rule_failed_count + attrs.asset_dq_rule_passed_count = obj.asset_dq_rule_passed_count + attrs.asset_dq_result = obj.asset_dq_result + attrs.asset_dq_freshness_value = obj.asset_dq_freshness_value + attrs.asset_dq_freshness_expectation = obj.asset_dq_freshness_expectation + attrs.asset_dq_row_scope_filter_column_qualified_name = ( + obj.asset_dq_row_scope_filter_column_qualified_name + ) + attrs.asset_space_qualified_name = obj.asset_space_qualified_name + attrs.asset_space_name = obj.asset_space_name + attrs.asset_gcp_dataplex_metadata_details = obj.asset_gcp_dataplex_metadata_details + attrs.asset_gcp_dataplex_aspect_list = obj.asset_gcp_dataplex_aspect_list + attrs.asset_gcp_dataplex_aspect_field_list = ( + obj.asset_gcp_dataplex_aspect_field_list + ) + attrs.asset_smus_metadata_form_names = obj.asset_smus_metadata_form_names + attrs.asset_smus_metadata_form_key_value_details = ( + obj.asset_smus_metadata_form_key_value_details + ) + attrs.asset_smus_metadata_form_details = obj.asset_smus_metadata_form_details + + +def _extract_infrastructure_attrs(attrs: InfrastructureAttributes) -> dict: + """Extract all Infrastructure attributes from the attrs struct into a flat dict.""" + result = _extract_referenceable_attrs(attrs) + result["name"] = attrs.name + result["display_name"] = attrs.display_name + result["description"] = attrs.description + result["asset_source_readme"] = attrs.asset_source_readme + result["user_description"] = attrs.user_description + result["asset_ai_generated_description"] = attrs.asset_ai_generated_description + result["asset_ai_generated_description_confidence"] = ( + attrs.asset_ai_generated_description_confidence + ) + result["asset_ai_generated_description_reasoning"] = ( + attrs.asset_ai_generated_description_reasoning + ) + result["tenant_id"] = attrs.tenant_id + result["certificate_status"] = attrs.certificate_status + result["certificate_status_message"] = attrs.certificate_status_message + result["certificate_updated_by"] = attrs.certificate_updated_by + result["certificate_updated_at"] = attrs.certificate_updated_at + result["announcement_title"] = attrs.announcement_title + result["announcement_message"] = attrs.announcement_message + result["announcement_type"] = attrs.announcement_type + result["announcement_updated_at"] = attrs.announcement_updated_at + result["announcement_updated_by"] = attrs.announcement_updated_by + result["owner_users"] = attrs.owner_users + result["owner_groups"] = attrs.owner_groups + result["admin_users"] = attrs.admin_users + result["admin_groups"] = attrs.admin_groups + result["viewer_users"] = attrs.viewer_users + result["viewer_groups"] = attrs.viewer_groups + result["connector_name"] = attrs.connector_name + result["connection_name"] = attrs.connection_name + result["connection_qualified_name"] = attrs.connection_qualified_name + result["has_lineage"] = attrs.has_lineage + result["is_discoverable"] = attrs.is_discoverable + result["is_editable"] = attrs.is_editable + result["sub_type"] = attrs.sub_type + result["view_score"] = attrs.view_score + result["popularity_score"] = attrs.popularity_score + result["source_owners"] = attrs.source_owners + result["asset_source_id"] = attrs.asset_source_id + result["source_created_by"] = attrs.source_created_by + result["source_created_at"] = attrs.source_created_at + result["source_updated_at"] = attrs.source_updated_at + result["source_updated_by"] = attrs.source_updated_by + result["source_url"] = attrs.source_url + result["source_embed_url"] = attrs.source_embed_url + result["last_sync_workflow_name"] = attrs.last_sync_workflow_name + result["last_sync_run_at"] = attrs.last_sync_run_at + result["last_sync_run"] = attrs.last_sync_run + result["admin_roles"] = attrs.admin_roles + result["source_read_count"] = attrs.source_read_count + result["source_read_user_count"] = attrs.source_read_user_count + result["source_last_read_at"] = attrs.source_last_read_at + result["last_row_changed_at"] = attrs.last_row_changed_at + result["source_total_cost"] = attrs.source_total_cost + result["source_cost_unit"] = attrs.source_cost_unit + result["source_read_query_cost"] = attrs.source_read_query_cost + result["source_read_recent_user_list"] = attrs.source_read_recent_user_list + result["source_read_recent_user_record_list"] = ( + attrs.source_read_recent_user_record_list + ) + result["source_read_top_user_list"] = attrs.source_read_top_user_list + result["source_read_top_user_record_list"] = attrs.source_read_top_user_record_list + result["source_read_popular_query_record_list"] = ( + attrs.source_read_popular_query_record_list + ) + result["source_read_expensive_query_record_list"] = ( + attrs.source_read_expensive_query_record_list + ) + result["source_read_slow_query_record_list"] = ( + attrs.source_read_slow_query_record_list + ) + result["source_query_compute_cost_list"] = attrs.source_query_compute_cost_list + result["source_query_compute_cost_record_list"] = ( + attrs.source_query_compute_cost_record_list + ) + result["dbt_qualified_name"] = attrs.dbt_qualified_name + result["asset_dbt_workflow_last_updated"] = attrs.asset_dbt_workflow_last_updated + result["asset_dbt_alias"] = attrs.asset_dbt_alias + result["asset_dbt_meta"] = attrs.asset_dbt_meta + result["asset_dbt_unique_id"] = attrs.asset_dbt_unique_id + result["asset_dbt_account_name"] = attrs.asset_dbt_account_name + result["asset_dbt_project_name"] = attrs.asset_dbt_project_name + result["asset_dbt_package_name"] = attrs.asset_dbt_package_name + result["asset_dbt_job_name"] = attrs.asset_dbt_job_name + result["asset_dbt_job_schedule"] = attrs.asset_dbt_job_schedule + result["asset_dbt_job_status"] = attrs.asset_dbt_job_status + result["asset_dbt_test_status"] = attrs.asset_dbt_test_status + result["asset_dbt_job_schedule_cron_humanized"] = ( + attrs.asset_dbt_job_schedule_cron_humanized + ) + result["asset_dbt_job_last_run"] = attrs.asset_dbt_job_last_run + result["asset_dbt_job_last_run_url"] = attrs.asset_dbt_job_last_run_url + result["asset_dbt_job_last_run_created_at"] = ( + attrs.asset_dbt_job_last_run_created_at + ) + result["asset_dbt_job_last_run_updated_at"] = ( + attrs.asset_dbt_job_last_run_updated_at + ) + result["asset_dbt_job_last_run_dequed_at"] = attrs.asset_dbt_job_last_run_dequed_at + result["asset_dbt_job_last_run_started_at"] = ( + attrs.asset_dbt_job_last_run_started_at + ) + result["asset_dbt_job_last_run_total_duration"] = ( + attrs.asset_dbt_job_last_run_total_duration + ) + result["asset_dbt_job_last_run_total_duration_humanized"] = ( + attrs.asset_dbt_job_last_run_total_duration_humanized + ) + result["asset_dbt_job_last_run_queued_duration"] = ( + attrs.asset_dbt_job_last_run_queued_duration + ) + result["asset_dbt_job_last_run_queued_duration_humanized"] = ( + attrs.asset_dbt_job_last_run_queued_duration_humanized + ) + result["asset_dbt_job_last_run_run_duration"] = ( + attrs.asset_dbt_job_last_run_run_duration + ) + result["asset_dbt_job_last_run_run_duration_humanized"] = ( + attrs.asset_dbt_job_last_run_run_duration_humanized + ) + result["asset_dbt_job_last_run_git_branch"] = ( + attrs.asset_dbt_job_last_run_git_branch + ) + result["asset_dbt_job_last_run_git_sha"] = attrs.asset_dbt_job_last_run_git_sha + result["asset_dbt_job_last_run_status_message"] = ( + attrs.asset_dbt_job_last_run_status_message + ) + result["asset_dbt_job_last_run_owner_thread_id"] = ( + attrs.asset_dbt_job_last_run_owner_thread_id + ) + result["asset_dbt_job_last_run_executed_by_thread_id"] = ( + attrs.asset_dbt_job_last_run_executed_by_thread_id + ) + result["asset_dbt_job_last_run_artifacts_saved"] = ( + attrs.asset_dbt_job_last_run_artifacts_saved + ) + result["asset_dbt_job_last_run_artifact_s3_path"] = ( + attrs.asset_dbt_job_last_run_artifact_s3_path + ) + result["asset_dbt_job_last_run_has_docs_generated"] = ( + attrs.asset_dbt_job_last_run_has_docs_generated + ) + result["asset_dbt_job_last_run_has_sources_generated"] = ( + attrs.asset_dbt_job_last_run_has_sources_generated + ) + result["asset_dbt_job_last_run_notifications_sent"] = ( + attrs.asset_dbt_job_last_run_notifications_sent + ) + result["asset_dbt_job_next_run"] = attrs.asset_dbt_job_next_run + result["asset_dbt_job_next_run_humanized"] = attrs.asset_dbt_job_next_run_humanized + result["asset_dbt_environment_name"] = attrs.asset_dbt_environment_name + result["asset_dbt_environment_dbt_version"] = ( + attrs.asset_dbt_environment_dbt_version + ) + result["asset_dbt_tags"] = attrs.asset_dbt_tags + result["asset_dbt_semantic_layer_proxy_url"] = ( + attrs.asset_dbt_semantic_layer_proxy_url + ) + result["asset_dbt_source_freshness_criteria"] = ( + attrs.asset_dbt_source_freshness_criteria + ) + result["sample_data_url"] = attrs.sample_data_url + result["asset_tags"] = attrs.asset_tags + result["asset_mc_incident_names"] = attrs.asset_mc_incident_names + result["asset_mc_incident_qualified_names"] = ( + attrs.asset_mc_incident_qualified_names + ) + result["asset_mc_alert_qualified_names"] = attrs.asset_mc_alert_qualified_names + result["asset_mc_monitor_names"] = attrs.asset_mc_monitor_names + result["asset_mc_monitor_qualified_names"] = attrs.asset_mc_monitor_qualified_names + result["asset_mc_monitor_statuses"] = attrs.asset_mc_monitor_statuses + result["asset_mc_monitor_types"] = attrs.asset_mc_monitor_types + result["asset_mc_monitor_schedule_types"] = attrs.asset_mc_monitor_schedule_types + result["asset_mc_incident_types"] = attrs.asset_mc_incident_types + result["asset_mc_incident_sub_types"] = attrs.asset_mc_incident_sub_types + result["asset_mc_incident_severities"] = attrs.asset_mc_incident_severities + result["asset_mc_incident_priorities"] = attrs.asset_mc_incident_priorities + result["asset_mc_incident_states"] = attrs.asset_mc_incident_states + result["asset_mc_is_monitored"] = attrs.asset_mc_is_monitored + result["asset_mc_last_sync_run_at"] = attrs.asset_mc_last_sync_run_at + result["starred_by"] = attrs.starred_by + result["starred_details_list"] = attrs.starred_details_list + result["starred_count"] = attrs.starred_count + result["asset_anomalo_dq_status"] = attrs.asset_anomalo_dq_status + result["asset_anomalo_check_count"] = attrs.asset_anomalo_check_count + result["asset_anomalo_failed_check_count"] = attrs.asset_anomalo_failed_check_count + result["asset_anomalo_check_statuses"] = attrs.asset_anomalo_check_statuses + result["asset_anomalo_last_check_run_at"] = attrs.asset_anomalo_last_check_run_at + result["asset_anomalo_applied_check_types"] = ( + attrs.asset_anomalo_applied_check_types + ) + result["asset_anomalo_failed_check_types"] = attrs.asset_anomalo_failed_check_types + result["asset_anomalo_source_url"] = attrs.asset_anomalo_source_url + result["asset_soda_dq_status"] = attrs.asset_soda_dq_status + result["asset_soda_check_count"] = attrs.asset_soda_check_count + result["asset_soda_last_sync_run_at"] = attrs.asset_soda_last_sync_run_at + result["asset_soda_last_scan_at"] = attrs.asset_soda_last_scan_at + result["asset_soda_check_statuses"] = attrs.asset_soda_check_statuses + result["asset_soda_source_url"] = attrs.asset_soda_source_url + result["asset_icon"] = attrs.asset_icon + result["asset_external_dq_metadata_details"] = ( + attrs.asset_external_dq_metadata_details + ) + result["is_partial"] = attrs.is_partial + result["is_ai_generated"] = attrs.is_ai_generated + result["asset_cover_image"] = attrs.asset_cover_image + result["asset_theme_hex"] = attrs.asset_theme_hex + result["lexicographical_sort_order"] = attrs.lexicographical_sort_order + result["has_contract"] = attrs.has_contract + result["asset_redirect_guids"] = attrs.asset_redirect_guids + result["asset_policy_guids"] = attrs.asset_policy_guids + result["asset_policies_count"] = attrs.asset_policies_count + result["domain_guids"] = attrs.domain_guids + result["non_compliant_asset_policy_guids"] = attrs.non_compliant_asset_policy_guids + result["product_guids"] = attrs.product_guids + result["output_product_guids"] = attrs.output_product_guids + result["application_qualified_name"] = attrs.application_qualified_name + result["application_field_qualified_name"] = attrs.application_field_qualified_name + result["asset_user_defined_type"] = attrs.asset_user_defined_type + result["asset_internal_popularity_score"] = attrs.asset_internal_popularity_score + result["asset_dq_schedule_type"] = attrs.asset_dq_schedule_type + result["asset_dq_schedule_crontab"] = attrs.asset_dq_schedule_crontab + result["asset_dq_schedule_time_zone"] = attrs.asset_dq_schedule_time_zone + result["asset_dq_schedule_source_sync_status"] = ( + attrs.asset_dq_schedule_source_sync_status + ) + result["asset_dq_schedule_source_synced_at"] = ( + attrs.asset_dq_schedule_source_synced_at + ) + result["asset_dq_schedule_source_sync_error_message"] = ( + attrs.asset_dq_schedule_source_sync_error_message + ) + result["asset_dq_schedule_source_sync_error_code"] = ( + attrs.asset_dq_schedule_source_sync_error_code + ) + result["asset_dq_schedule_source_sync_raw_error"] = ( + attrs.asset_dq_schedule_source_sync_raw_error + ) + result["asset_dq_rule_attached_dimensions"] = ( + attrs.asset_dq_rule_attached_dimensions + ) + result["asset_dq_rule_failed_dimensions"] = attrs.asset_dq_rule_failed_dimensions + result["asset_dq_rule_passed_dimensions"] = attrs.asset_dq_rule_passed_dimensions + result["asset_dq_rule_attached_rule_types"] = ( + attrs.asset_dq_rule_attached_rule_types + ) + result["asset_dq_rule_failed_rule_types"] = attrs.asset_dq_rule_failed_rule_types + result["asset_dq_rule_passed_rule_types"] = attrs.asset_dq_rule_passed_rule_types + result["asset_dq_rule_result_tags"] = attrs.asset_dq_rule_result_tags + result["asset_dq_rule_last_run_at"] = attrs.asset_dq_rule_last_run_at + result["asset_dq_manual_run_status"] = attrs.asset_dq_manual_run_status + result["asset_dq_rule_total_count"] = attrs.asset_dq_rule_total_count + result["asset_dq_rule_failed_count"] = attrs.asset_dq_rule_failed_count + result["asset_dq_rule_passed_count"] = attrs.asset_dq_rule_passed_count + result["asset_dq_result"] = attrs.asset_dq_result + result["asset_dq_freshness_value"] = attrs.asset_dq_freshness_value + result["asset_dq_freshness_expectation"] = attrs.asset_dq_freshness_expectation + result["asset_dq_row_scope_filter_column_qualified_name"] = ( + attrs.asset_dq_row_scope_filter_column_qualified_name + ) + result["asset_space_qualified_name"] = attrs.asset_space_qualified_name + result["asset_space_name"] = attrs.asset_space_name + result["asset_gcp_dataplex_metadata_details"] = ( + attrs.asset_gcp_dataplex_metadata_details + ) + result["asset_gcp_dataplex_aspect_list"] = attrs.asset_gcp_dataplex_aspect_list + result["asset_gcp_dataplex_aspect_field_list"] = ( + attrs.asset_gcp_dataplex_aspect_field_list + ) + result["asset_smus_metadata_form_names"] = attrs.asset_smus_metadata_form_names + result["asset_smus_metadata_form_key_value_details"] = ( + attrs.asset_smus_metadata_form_key_value_details + ) + result["asset_smus_metadata_form_details"] = attrs.asset_smus_metadata_form_details + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _infrastructure_to_nested(infrastructure: Infrastructure) -> InfrastructureNested: + """Convert flat Infrastructure to nested format.""" + attrs = InfrastructureAttributes() + _populate_infrastructure_attrs(attrs, infrastructure) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + infrastructure, _INFRASTRUCTURE_REL_FIELDS, InfrastructureRelationshipAttributes + ) + return InfrastructureNested( + guid=infrastructure.guid, + type_name=infrastructure.type_name, + status=infrastructure.status, + version=infrastructure.version, + create_time=infrastructure.create_time, + update_time=infrastructure.update_time, + created_by=infrastructure.created_by, + updated_by=infrastructure.updated_by, + classifications=infrastructure.classifications, + classification_names=infrastructure.classification_names, + meanings=infrastructure.meanings, + labels=infrastructure.labels, + business_attributes=infrastructure.business_attributes, + custom_attributes=infrastructure.custom_attributes, + pending_tasks=infrastructure.pending_tasks, + proxy=infrastructure.proxy, + is_incomplete=infrastructure.is_incomplete, + provenance_type=infrastructure.provenance_type, + home_id=infrastructure.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _infrastructure_from_nested(nested: InfrastructureNested) -> Infrastructure: + """Convert nested format to flat Infrastructure.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else InfrastructureAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _INFRASTRUCTURE_REL_FIELDS, + InfrastructureRelationshipAttributes, + ) + return Infrastructure( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_infrastructure_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _infrastructure_to_nested_bytes( + infrastructure: Infrastructure, serde: Serde +) -> bytes: + """Convert flat Infrastructure to nested JSON bytes.""" + return serde.encode(_infrastructure_to_nested(infrastructure)) + + +def _infrastructure_from_nested_bytes(data: bytes, serde: Serde) -> Infrastructure: + """Convert nested JSON bytes to flat Infrastructure.""" + nested = serde.decode(data, InfrastructureNested) + return _infrastructure_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + NumericRankField, + RelationField, + TextField, +) + +Infrastructure.NAME = KeywordField("name", "name") +Infrastructure.DISPLAY_NAME = KeywordField("displayName", "displayName") +Infrastructure.DESCRIPTION = KeywordField("description", "description") +Infrastructure.ASSET_SOURCE_README = KeywordTextField( + "assetSourceReadme", "assetSourceReadme", "assetSourceReadme.text" +) +Infrastructure.USER_DESCRIPTION = KeywordField("userDescription", "userDescription") +Infrastructure.ASSET_AI_GENERATED_DESCRIPTION = TextField( + "assetAiGeneratedDescription", "assetAiGeneratedDescription" +) +Infrastructure.ASSET_AI_GENERATED_DESCRIPTION_CONFIDENCE = NumericField( + "assetAiGeneratedDescriptionConfidence", "assetAiGeneratedDescriptionConfidence" +) +Infrastructure.ASSET_AI_GENERATED_DESCRIPTION_REASONING = KeywordField( + "assetAiGeneratedDescriptionReasoning", "assetAiGeneratedDescriptionReasoning" +) +Infrastructure.TENANT_ID = KeywordField("tenantId", "tenantId") +Infrastructure.CERTIFICATE_STATUS = KeywordTextField( + "certificateStatus", "certificateStatus", "certificateStatus.text" +) +Infrastructure.CERTIFICATE_STATUS_MESSAGE = KeywordField( + "certificateStatusMessage", "certificateStatusMessage" +) +Infrastructure.CERTIFICATE_UPDATED_BY = KeywordField( + "certificateUpdatedBy", "certificateUpdatedBy" +) +Infrastructure.CERTIFICATE_UPDATED_AT = NumericField( + "certificateUpdatedAt", "certificateUpdatedAt" +) +Infrastructure.ANNOUNCEMENT_TITLE = KeywordField( + "announcementTitle", "announcementTitle" +) +Infrastructure.ANNOUNCEMENT_MESSAGE = KeywordField( + "announcementMessage", "announcementMessage" +) +Infrastructure.ANNOUNCEMENT_TYPE = KeywordField("announcementType", "announcementType") +Infrastructure.ANNOUNCEMENT_UPDATED_AT = NumericField( + "announcementUpdatedAt", "announcementUpdatedAt" +) +Infrastructure.ANNOUNCEMENT_UPDATED_BY = KeywordField( + "announcementUpdatedBy", "announcementUpdatedBy" +) +Infrastructure.OWNER_USERS = KeywordField("ownerUsers", "ownerUsers") +Infrastructure.OWNER_GROUPS = KeywordField("ownerGroups", "ownerGroups") +Infrastructure.ADMIN_USERS = KeywordField("adminUsers", "adminUsers") +Infrastructure.ADMIN_GROUPS = KeywordField("adminGroups", "adminGroups") +Infrastructure.VIEWER_USERS = KeywordField("viewerUsers", "viewerUsers") +Infrastructure.VIEWER_GROUPS = KeywordField("viewerGroups", "viewerGroups") +Infrastructure.CONNECTOR_NAME = KeywordField("connectorName", "connectorName") +Infrastructure.CONNECTION_NAME = KeywordTextField( + "connectionName", "connectionName", "connectionName.text" +) +Infrastructure.CONNECTION_QUALIFIED_NAME = KeywordTextField( + "connectionQualifiedName", "connectionQualifiedName", "connectionQualifiedName.text" +) +Infrastructure.HAS_LINEAGE = BooleanField("__hasLineage", "__hasLineage") +Infrastructure.IS_DISCOVERABLE = BooleanField("isDiscoverable", "isDiscoverable") +Infrastructure.IS_EDITABLE = BooleanField("isEditable", "isEditable") +Infrastructure.SUB_TYPE = KeywordField("subType", "subType") +Infrastructure.VIEW_SCORE = NumericField("viewScore", "viewScore") +Infrastructure.POPULARITY_SCORE = NumericField("popularityScore", "popularityScore") +Infrastructure.SOURCE_OWNERS = KeywordField("sourceOwners", "sourceOwners") +Infrastructure.ASSET_SOURCE_ID = KeywordField("assetSourceId", "assetSourceId") +Infrastructure.SOURCE_CREATED_BY = KeywordField("sourceCreatedBy", "sourceCreatedBy") +Infrastructure.SOURCE_CREATED_AT = NumericField("sourceCreatedAt", "sourceCreatedAt") +Infrastructure.SOURCE_UPDATED_AT = NumericField("sourceUpdatedAt", "sourceUpdatedAt") +Infrastructure.SOURCE_UPDATED_BY = KeywordField("sourceUpdatedBy", "sourceUpdatedBy") +Infrastructure.SOURCE_URL = KeywordField("sourceURL", "sourceURL") +Infrastructure.SOURCE_EMBED_URL = KeywordField("sourceEmbedURL", "sourceEmbedURL") +Infrastructure.LAST_SYNC_WORKFLOW_NAME = KeywordField( + "lastSyncWorkflowName", "lastSyncWorkflowName" +) +Infrastructure.LAST_SYNC_RUN_AT = NumericField("lastSyncRunAt", "lastSyncRunAt") +Infrastructure.LAST_SYNC_RUN = KeywordField("lastSyncRun", "lastSyncRun") +Infrastructure.ADMIN_ROLES = KeywordField("adminRoles", "adminRoles") +Infrastructure.SOURCE_READ_COUNT = NumericField("sourceReadCount", "sourceReadCount") +Infrastructure.SOURCE_READ_USER_COUNT = NumericField( + "sourceReadUserCount", "sourceReadUserCount" +) +Infrastructure.SOURCE_LAST_READ_AT = NumericField( + "sourceLastReadAt", "sourceLastReadAt" +) +Infrastructure.LAST_ROW_CHANGED_AT = NumericField( + "lastRowChangedAt", "lastRowChangedAt" +) +Infrastructure.SOURCE_TOTAL_COST = NumericField("sourceTotalCost", "sourceTotalCost") +Infrastructure.SOURCE_COST_UNIT = KeywordField("sourceCostUnit", "sourceCostUnit") +Infrastructure.SOURCE_READ_QUERY_COST = NumericField( + "sourceReadQueryCost", "sourceReadQueryCost" +) +Infrastructure.SOURCE_READ_RECENT_USER_LIST = KeywordField( + "sourceReadRecentUserList", "sourceReadRecentUserList" +) +Infrastructure.SOURCE_READ_RECENT_USER_RECORD_LIST = KeywordField( + "sourceReadRecentUserRecordList", "sourceReadRecentUserRecordList" +) +Infrastructure.SOURCE_READ_TOP_USER_LIST = KeywordField( + "sourceReadTopUserList", "sourceReadTopUserList" +) +Infrastructure.SOURCE_READ_TOP_USER_RECORD_LIST = KeywordField( + "sourceReadTopUserRecordList", "sourceReadTopUserRecordList" +) +Infrastructure.SOURCE_READ_POPULAR_QUERY_RECORD_LIST = KeywordField( + "sourceReadPopularQueryRecordList", "sourceReadPopularQueryRecordList" +) +Infrastructure.SOURCE_READ_EXPENSIVE_QUERY_RECORD_LIST = KeywordField( + "sourceReadExpensiveQueryRecordList", "sourceReadExpensiveQueryRecordList" +) +Infrastructure.SOURCE_READ_SLOW_QUERY_RECORD_LIST = KeywordField( + "sourceReadSlowQueryRecordList", "sourceReadSlowQueryRecordList" +) +Infrastructure.SOURCE_QUERY_COMPUTE_COST_LIST = KeywordField( + "sourceQueryComputeCostList", "sourceQueryComputeCostList" +) +Infrastructure.SOURCE_QUERY_COMPUTE_COST_RECORD_LIST = KeywordField( + "sourceQueryComputeCostRecordList", "sourceQueryComputeCostRecordList" +) +Infrastructure.DBT_QUALIFIED_NAME = KeywordTextField( + "dbtQualifiedName", "dbtQualifiedName", "dbtQualifiedName.text" +) +Infrastructure.ASSET_DBT_WORKFLOW_LAST_UPDATED = KeywordField( + "assetDbtWorkflowLastUpdated", "assetDbtWorkflowLastUpdated" +) +Infrastructure.ASSET_DBT_ALIAS = KeywordField("assetDbtAlias", "assetDbtAlias") +Infrastructure.ASSET_DBT_META = KeywordField("assetDbtMeta", "assetDbtMeta") +Infrastructure.ASSET_DBT_UNIQUE_ID = KeywordField( + "assetDbtUniqueId", "assetDbtUniqueId" +) +Infrastructure.ASSET_DBT_ACCOUNT_NAME = KeywordField( + "assetDbtAccountName", "assetDbtAccountName" +) +Infrastructure.ASSET_DBT_PROJECT_NAME = KeywordField( + "assetDbtProjectName", "assetDbtProjectName" +) +Infrastructure.ASSET_DBT_PACKAGE_NAME = KeywordField( + "assetDbtPackageName", "assetDbtPackageName" +) +Infrastructure.ASSET_DBT_JOB_NAME = KeywordField("assetDbtJobName", "assetDbtJobName") +Infrastructure.ASSET_DBT_JOB_SCHEDULE = KeywordField( + "assetDbtJobSchedule", "assetDbtJobSchedule" +) +Infrastructure.ASSET_DBT_JOB_STATUS = KeywordField( + "assetDbtJobStatus", "assetDbtJobStatus" +) +Infrastructure.ASSET_DBT_TEST_STATUS = KeywordField( + "assetDbtTestStatus", "assetDbtTestStatus" +) +Infrastructure.ASSET_DBT_JOB_SCHEDULE_CRON_HUMANIZED = KeywordField( + "assetDbtJobScheduleCronHumanized", "assetDbtJobScheduleCronHumanized" +) +Infrastructure.ASSET_DBT_JOB_LAST_RUN = NumericField( + "assetDbtJobLastRun", "assetDbtJobLastRun" +) +Infrastructure.ASSET_DBT_JOB_LAST_RUN_URL = KeywordField( + "assetDbtJobLastRunUrl", "assetDbtJobLastRunUrl" +) +Infrastructure.ASSET_DBT_JOB_LAST_RUN_CREATED_AT = NumericField( + "assetDbtJobLastRunCreatedAt", "assetDbtJobLastRunCreatedAt" +) +Infrastructure.ASSET_DBT_JOB_LAST_RUN_UPDATED_AT = NumericField( + "assetDbtJobLastRunUpdatedAt", "assetDbtJobLastRunUpdatedAt" +) +Infrastructure.ASSET_DBT_JOB_LAST_RUN_DEQUED_AT = NumericField( + "assetDbtJobLastRunDequedAt", "assetDbtJobLastRunDequedAt" +) +Infrastructure.ASSET_DBT_JOB_LAST_RUN_STARTED_AT = NumericField( + "assetDbtJobLastRunStartedAt", "assetDbtJobLastRunStartedAt" +) +Infrastructure.ASSET_DBT_JOB_LAST_RUN_TOTAL_DURATION = KeywordField( + "assetDbtJobLastRunTotalDuration", "assetDbtJobLastRunTotalDuration" +) +Infrastructure.ASSET_DBT_JOB_LAST_RUN_TOTAL_DURATION_HUMANIZED = KeywordField( + "assetDbtJobLastRunTotalDurationHumanized", + "assetDbtJobLastRunTotalDurationHumanized", +) +Infrastructure.ASSET_DBT_JOB_LAST_RUN_QUEUED_DURATION = KeywordField( + "assetDbtJobLastRunQueuedDuration", "assetDbtJobLastRunQueuedDuration" +) +Infrastructure.ASSET_DBT_JOB_LAST_RUN_QUEUED_DURATION_HUMANIZED = KeywordField( + "assetDbtJobLastRunQueuedDurationHumanized", + "assetDbtJobLastRunQueuedDurationHumanized", +) +Infrastructure.ASSET_DBT_JOB_LAST_RUN_RUN_DURATION = KeywordField( + "assetDbtJobLastRunRunDuration", "assetDbtJobLastRunRunDuration" +) +Infrastructure.ASSET_DBT_JOB_LAST_RUN_RUN_DURATION_HUMANIZED = KeywordField( + "assetDbtJobLastRunRunDurationHumanized", "assetDbtJobLastRunRunDurationHumanized" +) +Infrastructure.ASSET_DBT_JOB_LAST_RUN_GIT_BRANCH = KeywordTextField( + "assetDbtJobLastRunGitBranch", + "assetDbtJobLastRunGitBranch", + "assetDbtJobLastRunGitBranch.text", +) +Infrastructure.ASSET_DBT_JOB_LAST_RUN_GIT_SHA = KeywordField( + "assetDbtJobLastRunGitSha", "assetDbtJobLastRunGitSha" +) +Infrastructure.ASSET_DBT_JOB_LAST_RUN_STATUS_MESSAGE = KeywordField( + "assetDbtJobLastRunStatusMessage", "assetDbtJobLastRunStatusMessage" +) +Infrastructure.ASSET_DBT_JOB_LAST_RUN_OWNER_THREAD_ID = KeywordField( + "assetDbtJobLastRunOwnerThreadId", "assetDbtJobLastRunOwnerThreadId" +) +Infrastructure.ASSET_DBT_JOB_LAST_RUN_EXECUTED_BY_THREAD_ID = KeywordField( + "assetDbtJobLastRunExecutedByThreadId", "assetDbtJobLastRunExecutedByThreadId" +) +Infrastructure.ASSET_DBT_JOB_LAST_RUN_ARTIFACTS_SAVED = BooleanField( + "assetDbtJobLastRunArtifactsSaved", "assetDbtJobLastRunArtifactsSaved" +) +Infrastructure.ASSET_DBT_JOB_LAST_RUN_ARTIFACT_S3_PATH = KeywordField( + "assetDbtJobLastRunArtifactS3Path", "assetDbtJobLastRunArtifactS3Path" +) +Infrastructure.ASSET_DBT_JOB_LAST_RUN_HAS_DOCS_GENERATED = BooleanField( + "assetDbtJobLastRunHasDocsGenerated", "assetDbtJobLastRunHasDocsGenerated" +) +Infrastructure.ASSET_DBT_JOB_LAST_RUN_HAS_SOURCES_GENERATED = BooleanField( + "assetDbtJobLastRunHasSourcesGenerated", "assetDbtJobLastRunHasSourcesGenerated" +) +Infrastructure.ASSET_DBT_JOB_LAST_RUN_NOTIFICATIONS_SENT = BooleanField( + "assetDbtJobLastRunNotificationsSent", "assetDbtJobLastRunNotificationsSent" +) +Infrastructure.ASSET_DBT_JOB_NEXT_RUN = NumericField( + "assetDbtJobNextRun", "assetDbtJobNextRun" +) +Infrastructure.ASSET_DBT_JOB_NEXT_RUN_HUMANIZED = KeywordField( + "assetDbtJobNextRunHumanized", "assetDbtJobNextRunHumanized" +) +Infrastructure.ASSET_DBT_ENVIRONMENT_NAME = KeywordField( + "assetDbtEnvironmentName", "assetDbtEnvironmentName" +) +Infrastructure.ASSET_DBT_ENVIRONMENT_DBT_VERSION = KeywordField( + "assetDbtEnvironmentDbtVersion", "assetDbtEnvironmentDbtVersion" +) +Infrastructure.ASSET_DBT_TAGS = KeywordTextField( + "assetDbtTags", "assetDbtTags", "assetDbtTags.text" +) +Infrastructure.ASSET_DBT_SEMANTIC_LAYER_PROXY_URL = KeywordField( + "assetDbtSemanticLayerProxyUrl", "assetDbtSemanticLayerProxyUrl" +) +Infrastructure.ASSET_DBT_SOURCE_FRESHNESS_CRITERIA = KeywordField( + "assetDbtSourceFreshnessCriteria", "assetDbtSourceFreshnessCriteria" +) +Infrastructure.SAMPLE_DATA_URL = KeywordTextField( + "sampleDataUrl", "sampleDataUrl", "sampleDataUrl.text" +) +Infrastructure.ASSET_TAGS = KeywordTextField("assetTags", "assetTags", "assetTags.text") +Infrastructure.ASSET_MC_INCIDENT_NAMES = KeywordField( + "assetMcIncidentNames", "assetMcIncidentNames" +) +Infrastructure.ASSET_MC_INCIDENT_QUALIFIED_NAMES = KeywordTextField( + "assetMcIncidentQualifiedNames", + "assetMcIncidentQualifiedNames", + "assetMcIncidentQualifiedNames.text", +) +Infrastructure.ASSET_MC_ALERT_QUALIFIED_NAMES = KeywordTextField( + "assetMcAlertQualifiedNames", + "assetMcAlertQualifiedNames", + "assetMcAlertQualifiedNames.text", +) +Infrastructure.ASSET_MC_MONITOR_NAMES = KeywordField( + "assetMcMonitorNames", "assetMcMonitorNames" +) +Infrastructure.ASSET_MC_MONITOR_QUALIFIED_NAMES = KeywordTextField( + "assetMcMonitorQualifiedNames", + "assetMcMonitorQualifiedNames", + "assetMcMonitorQualifiedNames.text", +) +Infrastructure.ASSET_MC_MONITOR_STATUSES = KeywordField( + "assetMcMonitorStatuses", "assetMcMonitorStatuses" +) +Infrastructure.ASSET_MC_MONITOR_TYPES = KeywordField( + "assetMcMonitorTypes", "assetMcMonitorTypes" +) +Infrastructure.ASSET_MC_MONITOR_SCHEDULE_TYPES = KeywordField( + "assetMcMonitorScheduleTypes", "assetMcMonitorScheduleTypes" +) +Infrastructure.ASSET_MC_INCIDENT_TYPES = KeywordField( + "assetMcIncidentTypes", "assetMcIncidentTypes" +) +Infrastructure.ASSET_MC_INCIDENT_SUB_TYPES = KeywordField( + "assetMcIncidentSubTypes", "assetMcIncidentSubTypes" +) +Infrastructure.ASSET_MC_INCIDENT_SEVERITIES = KeywordField( + "assetMcIncidentSeverities", "assetMcIncidentSeverities" +) +Infrastructure.ASSET_MC_INCIDENT_PRIORITIES = KeywordField( + "assetMcIncidentPriorities", "assetMcIncidentPriorities" +) +Infrastructure.ASSET_MC_INCIDENT_STATES = KeywordField( + "assetMcIncidentStates", "assetMcIncidentStates" +) +Infrastructure.ASSET_MC_IS_MONITORED = BooleanField( + "assetMcIsMonitored", "assetMcIsMonitored" +) +Infrastructure.ASSET_MC_LAST_SYNC_RUN_AT = NumericField( + "assetMcLastSyncRunAt", "assetMcLastSyncRunAt" +) +Infrastructure.STARRED_BY = KeywordField("starredBy", "starredBy") +Infrastructure.STARRED_DETAILS_LIST = KeywordField( + "starredDetailsList", "starredDetailsList" +) +Infrastructure.STARRED_COUNT = NumericField("starredCount", "starredCount") +Infrastructure.ASSET_ANOMALO_DQ_STATUS = KeywordField( + "assetAnomaloDQStatus", "assetAnomaloDQStatus" +) +Infrastructure.ASSET_ANOMALO_CHECK_COUNT = NumericField( + "assetAnomaloCheckCount", "assetAnomaloCheckCount" +) +Infrastructure.ASSET_ANOMALO_FAILED_CHECK_COUNT = NumericField( + "assetAnomaloFailedCheckCount", "assetAnomaloFailedCheckCount" +) +Infrastructure.ASSET_ANOMALO_CHECK_STATUSES = KeywordField( + "assetAnomaloCheckStatuses", "assetAnomaloCheckStatuses" +) +Infrastructure.ASSET_ANOMALO_LAST_CHECK_RUN_AT = NumericField( + "assetAnomaloLastCheckRunAt", "assetAnomaloLastCheckRunAt" +) +Infrastructure.ASSET_ANOMALO_APPLIED_CHECK_TYPES = KeywordField( + "assetAnomaloAppliedCheckTypes", "assetAnomaloAppliedCheckTypes" +) +Infrastructure.ASSET_ANOMALO_FAILED_CHECK_TYPES = KeywordField( + "assetAnomaloFailedCheckTypes", "assetAnomaloFailedCheckTypes" +) +Infrastructure.ASSET_ANOMALO_SOURCE_URL = KeywordField( + "assetAnomaloSourceUrl", "assetAnomaloSourceUrl" +) +Infrastructure.ASSET_SODA_DQ_STATUS = KeywordField( + "assetSodaDQStatus", "assetSodaDQStatus" +) +Infrastructure.ASSET_SODA_CHECK_COUNT = NumericField( + "assetSodaCheckCount", "assetSodaCheckCount" +) +Infrastructure.ASSET_SODA_LAST_SYNC_RUN_AT = NumericField( + "assetSodaLastSyncRunAt", "assetSodaLastSyncRunAt" +) +Infrastructure.ASSET_SODA_LAST_SCAN_AT = NumericField( + "assetSodaLastScanAt", "assetSodaLastScanAt" +) +Infrastructure.ASSET_SODA_CHECK_STATUSES = KeywordField( + "assetSodaCheckStatuses", "assetSodaCheckStatuses" +) +Infrastructure.ASSET_SODA_SOURCE_URL = KeywordField( + "assetSodaSourceURL", "assetSodaSourceURL" +) +Infrastructure.ASSET_ICON = KeywordField("assetIcon", "assetIcon") +Infrastructure.ASSET_EXTERNAL_DQ_METADATA_DETAILS = KeywordField( + "assetExternalDQMetadataDetails", "assetExternalDQMetadataDetails" +) +Infrastructure.IS_PARTIAL = BooleanField("isPartial", "isPartial") +Infrastructure.IS_AI_GENERATED = BooleanField("isAIGenerated", "isAIGenerated") +Infrastructure.ASSET_COVER_IMAGE = KeywordField("assetCoverImage", "assetCoverImage") +Infrastructure.ASSET_THEME_HEX = KeywordField("assetThemeHex", "assetThemeHex") +Infrastructure.LEXICOGRAPHICAL_SORT_ORDER = KeywordField( + "lexicographicalSortOrder", "lexicographicalSortOrder" +) +Infrastructure.HAS_CONTRACT = BooleanField("hasContract", "hasContract") +Infrastructure.ASSET_REDIRECT_GUIDS = KeywordField( + "assetRedirectGUIDs", "assetRedirectGUIDs" +) +Infrastructure.ASSET_POLICY_GUIDS = KeywordField("assetPolicyGUIDs", "assetPolicyGUIDs") +Infrastructure.ASSET_POLICIES_COUNT = NumericField( + "assetPoliciesCount", "assetPoliciesCount" +) +Infrastructure.DOMAIN_GUIDS = KeywordField("domainGUIDs", "domainGUIDs") +Infrastructure.NON_COMPLIANT_ASSET_POLICY_GUIDS = KeywordField( + "nonCompliantAssetPolicyGUIDs", "nonCompliantAssetPolicyGUIDs" +) +Infrastructure.PRODUCT_GUIDS = KeywordField("productGUIDs", "productGUIDs") +Infrastructure.OUTPUT_PRODUCT_GUIDS = KeywordField( + "outputProductGUIDs", "outputProductGUIDs" +) +Infrastructure.APPLICATION_QUALIFIED_NAME = KeywordField( + "applicationQualifiedName", "applicationQualifiedName" +) +Infrastructure.APPLICATION_FIELD_QUALIFIED_NAME = KeywordField( + "applicationFieldQualifiedName", "applicationFieldQualifiedName" +) +Infrastructure.ASSET_USER_DEFINED_TYPE = KeywordField( + "assetUserDefinedType", "assetUserDefinedType" +) +Infrastructure.ASSET_INTERNAL_POPULARITY_SCORE = NumericRankField( + "assetInternalPopularityScore", + "assetInternalPopularityScore", + "assetInternalPopularityScore.rank", +) +Infrastructure.ASSET_DQ_SCHEDULE_TYPE = KeywordField( + "assetDQScheduleType", "assetDQScheduleType" +) +Infrastructure.ASSET_DQ_SCHEDULE_CRONTAB = KeywordField( + "assetDQScheduleCrontab", "assetDQScheduleCrontab" +) +Infrastructure.ASSET_DQ_SCHEDULE_TIME_ZONE = KeywordField( + "assetDQScheduleTimeZone", "assetDQScheduleTimeZone" +) +Infrastructure.ASSET_DQ_SCHEDULE_SOURCE_SYNC_STATUS = KeywordField( + "assetDQScheduleSourceSyncStatus", "assetDQScheduleSourceSyncStatus" +) +Infrastructure.ASSET_DQ_SCHEDULE_SOURCE_SYNCED_AT = NumericField( + "assetDQScheduleSourceSyncedAt", "assetDQScheduleSourceSyncedAt" +) +Infrastructure.ASSET_DQ_SCHEDULE_SOURCE_SYNC_ERROR_MESSAGE = TextField( + "assetDQScheduleSourceSyncErrorMessage", "assetDQScheduleSourceSyncErrorMessage" +) +Infrastructure.ASSET_DQ_SCHEDULE_SOURCE_SYNC_ERROR_CODE = KeywordField( + "assetDQScheduleSourceSyncErrorCode", "assetDQScheduleSourceSyncErrorCode" +) +Infrastructure.ASSET_DQ_SCHEDULE_SOURCE_SYNC_RAW_ERROR = TextField( + "assetDQScheduleSourceSyncRawError", "assetDQScheduleSourceSyncRawError" +) +Infrastructure.ASSET_DQ_RULE_ATTACHED_DIMENSIONS = KeywordField( + "assetDQRuleAttachedDimensions", "assetDQRuleAttachedDimensions" +) +Infrastructure.ASSET_DQ_RULE_FAILED_DIMENSIONS = KeywordField( + "assetDQRuleFailedDimensions", "assetDQRuleFailedDimensions" +) +Infrastructure.ASSET_DQ_RULE_PASSED_DIMENSIONS = KeywordField( + "assetDQRulePassedDimensions", "assetDQRulePassedDimensions" +) +Infrastructure.ASSET_DQ_RULE_ATTACHED_RULE_TYPES = KeywordField( + "assetDQRuleAttachedRuleTypes", "assetDQRuleAttachedRuleTypes" +) +Infrastructure.ASSET_DQ_RULE_FAILED_RULE_TYPES = KeywordField( + "assetDQRuleFailedRuleTypes", "assetDQRuleFailedRuleTypes" +) +Infrastructure.ASSET_DQ_RULE_PASSED_RULE_TYPES = KeywordField( + "assetDQRulePassedRuleTypes", "assetDQRulePassedRuleTypes" +) +Infrastructure.ASSET_DQ_RULE_RESULT_TAGS = KeywordField( + "assetDQRuleResultTags", "assetDQRuleResultTags" +) +Infrastructure.ASSET_DQ_RULE_LAST_RUN_AT = NumericField( + "assetDQRuleLastRunAt", "assetDQRuleLastRunAt" +) +Infrastructure.ASSET_DQ_MANUAL_RUN_STATUS = KeywordField( + "assetDQManualRunStatus", "assetDQManualRunStatus" +) +Infrastructure.ASSET_DQ_RULE_TOTAL_COUNT = NumericField( + "assetDQRuleTotalCount", "assetDQRuleTotalCount" +) +Infrastructure.ASSET_DQ_RULE_FAILED_COUNT = NumericField( + "assetDQRuleFailedCount", "assetDQRuleFailedCount" +) +Infrastructure.ASSET_DQ_RULE_PASSED_COUNT = NumericField( + "assetDQRulePassedCount", "assetDQRulePassedCount" +) +Infrastructure.ASSET_DQ_RESULT = KeywordField("assetDQResult", "assetDQResult") +Infrastructure.ASSET_DQ_FRESHNESS_VALUE = NumericField( + "assetDQFreshnessValue", "assetDQFreshnessValue" +) +Infrastructure.ASSET_DQ_FRESHNESS_EXPECTATION = NumericField( + "assetDQFreshnessExpectation", "assetDQFreshnessExpectation" +) +Infrastructure.ASSET_DQ_ROW_SCOPE_FILTER_COLUMN_QUALIFIED_NAME = KeywordField( + "assetDQRowScopeFilterColumnQualifiedName", + "assetDQRowScopeFilterColumnQualifiedName", +) +Infrastructure.ASSET_SPACE_QUALIFIED_NAME = KeywordField( + "assetSpaceQualifiedName", "assetSpaceQualifiedName" +) +Infrastructure.ASSET_SPACE_NAME = KeywordField("assetSpaceName", "assetSpaceName") +Infrastructure.ASSET_GCP_DATAPLEX_METADATA_DETAILS = KeywordField( + "assetGCPDataplexMetadataDetails", "assetGCPDataplexMetadataDetails" +) +Infrastructure.ASSET_GCP_DATAPLEX_ASPECT_LIST = KeywordField( + "assetGCPDataplexAspectList", "assetGCPDataplexAspectList" +) +Infrastructure.ASSET_GCP_DATAPLEX_ASPECT_FIELD_LIST = KeywordField( + "assetGCPDataplexAspectFieldList", "assetGCPDataplexAspectFieldList" +) +Infrastructure.ASSET_SMUS_METADATA_FORM_NAMES = KeywordTextField( + "assetSmusMetadataFormNames", + "assetSmusMetadataFormNames", + "assetSmusMetadataFormNames.text", +) +Infrastructure.ASSET_SMUS_METADATA_FORM_KEY_VALUE_DETAILS = KeywordTextField( + "assetSmusMetadataFormKeyValueDetails", + "assetSmusMetadataFormKeyValueDetails", + "assetSmusMetadataFormKeyValueDetails.text", +) +Infrastructure.ASSET_SMUS_METADATA_FORM_DETAILS = KeywordField( + "assetSmusMetadataFormDetails", "assetSmusMetadataFormDetails" +) +Infrastructure.ANOMALO_CHECKS = RelationField("anomaloChecks") +Infrastructure.APPLICATION = RelationField("application") +Infrastructure.APPLICATION_FIELD = RelationField("applicationField") +Infrastructure.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Infrastructure.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Infrastructure.METRICS = RelationField("metrics") +Infrastructure.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Infrastructure.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Infrastructure.MEANINGS = RelationField("meanings") +Infrastructure.MC_MONITORS = RelationField("mcMonitors") +Infrastructure.MC_INCIDENTS = RelationField("mcIncidents") +Infrastructure.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Infrastructure.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Infrastructure.FILES = RelationField("files") +Infrastructure.LINKS = RelationField("links") +Infrastructure.README = RelationField("readme") +Infrastructure.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Infrastructure.SODA_CHECKS = RelationField("sodaChecks") diff --git a/pyatlan_v9/model/assets/insight.py b/pyatlan_v9/model/assets/insight.py new file mode 100644 index 000000000..ec23bd4ea --- /dev/null +++ b/pyatlan_v9/model/assets/insight.py @@ -0,0 +1,523 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Insight asset model with flattened inheritance. + +This module provides: +- Insight: Flat asset class (easy to use) +- InsightAttributes: Nested attributes struct (extends AssetAttributes) +- InsightNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Insight(Asset): + """ + Base class for Insights assets in Atlan. + """ + + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Insight" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Insight" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _insight_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Insight: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Insight instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _insight_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class InsightAttributes(AssetAttributes): + """Insight-specific attributes for nested API format.""" + + pass + + +class InsightRelationshipAttributes(AssetRelationshipAttributes): + """Insight-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class InsightNested(AssetNested): + """Insight in nested API format for high-performance serialization.""" + + attributes: Union[InsightAttributes, UnsetType] = UNSET + relationship_attributes: Union[InsightRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[InsightRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[InsightRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_INSIGHT_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_insight_attrs(attrs: InsightAttributes, obj: Insight) -> None: + """Populate Insight-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + + +def _extract_insight_attrs(attrs: InsightAttributes) -> dict: + """Extract all Insight attributes from the attrs struct into a flat dict.""" + return _extract_asset_attrs(attrs) + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _insight_to_nested(insight: Insight) -> InsightNested: + """Convert flat Insight to nested format.""" + attrs = InsightAttributes() + _populate_insight_attrs(attrs, insight) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + insight, _INSIGHT_REL_FIELDS, InsightRelationshipAttributes + ) + return InsightNested( + guid=insight.guid, + type_name=insight.type_name, + status=insight.status, + version=insight.version, + create_time=insight.create_time, + update_time=insight.update_time, + created_by=insight.created_by, + updated_by=insight.updated_by, + classifications=insight.classifications, + classification_names=insight.classification_names, + meanings=insight.meanings, + labels=insight.labels, + business_attributes=insight.business_attributes, + custom_attributes=insight.custom_attributes, + pending_tasks=insight.pending_tasks, + proxy=insight.proxy, + is_incomplete=insight.is_incomplete, + provenance_type=insight.provenance_type, + home_id=insight.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _insight_from_nested(nested: InsightNested) -> Insight: + """Convert nested format to flat Insight.""" + attrs = nested.attributes if nested.attributes is not UNSET else InsightAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _INSIGHT_REL_FIELDS, + InsightRelationshipAttributes, + ) + return Insight( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_insight_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _insight_to_nested_bytes(insight: Insight, serde: Serde) -> bytes: + """Convert flat Insight to nested JSON bytes.""" + return serde.encode(_insight_to_nested(insight)) + + +def _insight_from_nested_bytes(data: bytes, serde: Serde) -> Insight: + """Convert nested JSON bytes to flat Insight.""" + nested = serde.decode(data, InsightNested) + return _insight_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import RelationField # noqa: E402 + +Insight.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Insight.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Insight.ANOMALO_CHECKS = RelationField("anomaloChecks") +Insight.APPLICATION = RelationField("application") +Insight.APPLICATION_FIELD = RelationField("applicationField") +Insight.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Insight.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Insight.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Insight.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Insight.METRICS = RelationField("metrics") +Insight.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Insight.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Insight.MEANINGS = RelationField("meanings") +Insight.MC_MONITORS = RelationField("mcMonitors") +Insight.MC_INCIDENTS = RelationField("mcIncidents") +Insight.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Insight.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Insight.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Insight.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Insight.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Insight.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Insight.FILES = RelationField("files") +Insight.LINKS = RelationField("links") +Insight.README = RelationField("readme") +Insight.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Insight.SODA_CHECKS = RelationField("sodaChecks") +Insight.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Insight.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/kafka.py b/pyatlan_v9/model/assets/kafka.py new file mode 100644 index 000000000..260717fce --- /dev/null +++ b/pyatlan_v9/model/assets/kafka.py @@ -0,0 +1,523 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Kafka asset model with flattened inheritance. + +This module provides: +- Kafka: Flat asset class (easy to use) +- KafkaAttributes: Nested attributes struct (extends AssetAttributes) +- KafkaNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Kafka(Asset): + """ + Base class for Kafka assets. + """ + + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Kafka" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Kafka" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _kafka_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Kafka: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Kafka instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _kafka_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class KafkaAttributes(AssetAttributes): + """Kafka-specific attributes for nested API format.""" + + pass + + +class KafkaRelationshipAttributes(AssetRelationshipAttributes): + """Kafka-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class KafkaNested(AssetNested): + """Kafka in nested API format for high-performance serialization.""" + + attributes: Union[KafkaAttributes, UnsetType] = UNSET + relationship_attributes: Union[KafkaRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[KafkaRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[KafkaRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_KAFKA_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_kafka_attrs(attrs: KafkaAttributes, obj: Kafka) -> None: + """Populate Kafka-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + + +def _extract_kafka_attrs(attrs: KafkaAttributes) -> dict: + """Extract all Kafka attributes from the attrs struct into a flat dict.""" + return _extract_asset_attrs(attrs) + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _kafka_to_nested(kafka: Kafka) -> KafkaNested: + """Convert flat Kafka to nested format.""" + attrs = KafkaAttributes() + _populate_kafka_attrs(attrs, kafka) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + kafka, _KAFKA_REL_FIELDS, KafkaRelationshipAttributes + ) + return KafkaNested( + guid=kafka.guid, + type_name=kafka.type_name, + status=kafka.status, + version=kafka.version, + create_time=kafka.create_time, + update_time=kafka.update_time, + created_by=kafka.created_by, + updated_by=kafka.updated_by, + classifications=kafka.classifications, + classification_names=kafka.classification_names, + meanings=kafka.meanings, + labels=kafka.labels, + business_attributes=kafka.business_attributes, + custom_attributes=kafka.custom_attributes, + pending_tasks=kafka.pending_tasks, + proxy=kafka.proxy, + is_incomplete=kafka.is_incomplete, + provenance_type=kafka.provenance_type, + home_id=kafka.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _kafka_from_nested(nested: KafkaNested) -> Kafka: + """Convert nested format to flat Kafka.""" + attrs = nested.attributes if nested.attributes is not UNSET else KafkaAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _KAFKA_REL_FIELDS, + KafkaRelationshipAttributes, + ) + return Kafka( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_kafka_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _kafka_to_nested_bytes(kafka: Kafka, serde: Serde) -> bytes: + """Convert flat Kafka to nested JSON bytes.""" + return serde.encode(_kafka_to_nested(kafka)) + + +def _kafka_from_nested_bytes(data: bytes, serde: Serde) -> Kafka: + """Convert nested JSON bytes to flat Kafka.""" + nested = serde.decode(data, KafkaNested) + return _kafka_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import RelationField # noqa: E402 + +Kafka.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Kafka.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Kafka.ANOMALO_CHECKS = RelationField("anomaloChecks") +Kafka.APPLICATION = RelationField("application") +Kafka.APPLICATION_FIELD = RelationField("applicationField") +Kafka.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Kafka.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Kafka.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Kafka.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Kafka.METRICS = RelationField("metrics") +Kafka.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Kafka.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Kafka.MEANINGS = RelationField("meanings") +Kafka.MC_MONITORS = RelationField("mcMonitors") +Kafka.MC_INCIDENTS = RelationField("mcIncidents") +Kafka.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Kafka.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Kafka.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Kafka.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Kafka.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Kafka.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Kafka.FILES = RelationField("files") +Kafka.LINKS = RelationField("links") +Kafka.README = RelationField("readme") +Kafka.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Kafka.SODA_CHECKS = RelationField("sodaChecks") +Kafka.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Kafka.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/kafka_consumer_group.py b/pyatlan_v9/model/assets/kafka_consumer_group.py new file mode 100644 index 000000000..c7d431b5c --- /dev/null +++ b/pyatlan_v9/model/assets/kafka_consumer_group.py @@ -0,0 +1,666 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +KafkaConsumerGroup asset model with flattened inheritance. + +This module provides: +- KafkaConsumerGroup: Flat asset class (easy to use) +- KafkaConsumerGroupAttributes: Nested attributes struct (extends AssetAttributes) +- KafkaConsumerGroupNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .kafka_related import RelatedKafkaTopic + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class KafkaConsumerGroup(Asset): + """ + Instance of a Kafka ConsumerGroup in Atlan. These group consumers of topics in Kafka. + """ + + KAFKA_CONSUMER_GROUP_TOPIC_CONSUMPTION_PROPERTIES: ClassVar[Any] = None + KAFKA_CONSUMER_GROUP_MEMBER_COUNT: ClassVar[Any] = None + KAFKA_TOPIC_NAMES: ClassVar[Any] = None + KAFKA_TOPIC_QUALIFIED_NAMES: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + KAFKA_TOPICS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "KafkaConsumerGroup" + + kafka_consumer_group_topic_consumption_properties: Union[ + List[Dict[str, Any]], None, UnsetType + ] = UNSET + """List of consumption properties for Kafka topics, for this consumer group.""" + + kafka_consumer_group_member_count: Union[int, None, UnsetType] = UNSET + """Number of members in this consumer group.""" + + kafka_topic_names: Union[List[str], None, UnsetType] = UNSET + """Simple names of the topics consumed by this consumer group.""" + + kafka_topic_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Unique names of the topics consumed by this consumer group.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + kafka_topics: Union[List[RelatedKafkaTopic], None, UnsetType] = UNSET + """Topics consumed by this consumer group.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "KafkaConsumerGroup" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/consumer-group/[^/]+$" + ) + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + kafka_topic_qualified_names: list[str], + ) -> "KafkaConsumerGroup": + validate_required_fields( + ["name", "kafka_topic_qualified_names"], + [name, kafka_topic_qualified_names], + ) + # Extract connection from the first topic qualified name + first_topic_qn = kafka_topic_qualified_names[0] + fields = first_topic_qn.split("/") + connector_name = fields[1] if len(fields) > 1 else None + connection_qn = ( + f"{fields[0]}/{fields[1]}/{fields[2]}" if len(fields) >= 3 else None + ) + qualified_name = f"{connection_qn}/consumer-group/{name}" + return cls( + name=name, + qualified_name=qualified_name, + connector_name=connector_name, + connection_qualified_name=connection_qn, + kafka_topic_qualified_names=set(kafka_topic_qualified_names), + ) + + @classmethod + def create(cls, **kwargs) -> "KafkaConsumerGroup": + return cls.creator(**kwargs) + + @classmethod + def create_for_modification(cls, **kwargs) -> "KafkaConsumerGroup": + return cls.updater(**kwargs) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _kafka_consumer_group_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> KafkaConsumerGroup: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + KafkaConsumerGroup instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _kafka_consumer_group_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class KafkaConsumerGroupAttributes(AssetAttributes): + """KafkaConsumerGroup-specific attributes for nested API format.""" + + kafka_consumer_group_topic_consumption_properties: Union[ + List[Dict[str, Any]], None, UnsetType + ] = UNSET + """List of consumption properties for Kafka topics, for this consumer group.""" + + kafka_consumer_group_member_count: Union[int, None, UnsetType] = UNSET + """Number of members in this consumer group.""" + + kafka_topic_names: Union[List[str], None, UnsetType] = UNSET + """Simple names of the topics consumed by this consumer group.""" + + kafka_topic_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Unique names of the topics consumed by this consumer group.""" + + +class KafkaConsumerGroupRelationshipAttributes(AssetRelationshipAttributes): + """KafkaConsumerGroup-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + kafka_topics: Union[List[RelatedKafkaTopic], None, UnsetType] = UNSET + """Topics consumed by this consumer group.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class KafkaConsumerGroupNested(AssetNested): + """KafkaConsumerGroup in nested API format for high-performance serialization.""" + + attributes: Union[KafkaConsumerGroupAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + KafkaConsumerGroupRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + KafkaConsumerGroupRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + KafkaConsumerGroupRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_KAFKA_CONSUMER_GROUP_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "kafka_topics", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_kafka_consumer_group_attrs( + attrs: KafkaConsumerGroupAttributes, obj: KafkaConsumerGroup +) -> None: + """Populate KafkaConsumerGroup-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.kafka_consumer_group_topic_consumption_properties = ( + obj.kafka_consumer_group_topic_consumption_properties + ) + attrs.kafka_consumer_group_member_count = obj.kafka_consumer_group_member_count + attrs.kafka_topic_names = obj.kafka_topic_names + attrs.kafka_topic_qualified_names = obj.kafka_topic_qualified_names + + +def _extract_kafka_consumer_group_attrs(attrs: KafkaConsumerGroupAttributes) -> dict: + """Extract all KafkaConsumerGroup attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["kafka_consumer_group_topic_consumption_properties"] = ( + attrs.kafka_consumer_group_topic_consumption_properties + ) + result["kafka_consumer_group_member_count"] = ( + attrs.kafka_consumer_group_member_count + ) + result["kafka_topic_names"] = attrs.kafka_topic_names + result["kafka_topic_qualified_names"] = attrs.kafka_topic_qualified_names + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _kafka_consumer_group_to_nested( + kafka_consumer_group: KafkaConsumerGroup, +) -> KafkaConsumerGroupNested: + """Convert flat KafkaConsumerGroup to nested format.""" + attrs = KafkaConsumerGroupAttributes() + _populate_kafka_consumer_group_attrs(attrs, kafka_consumer_group) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + kafka_consumer_group, + _KAFKA_CONSUMER_GROUP_REL_FIELDS, + KafkaConsumerGroupRelationshipAttributes, + ) + return KafkaConsumerGroupNested( + guid=kafka_consumer_group.guid, + type_name=kafka_consumer_group.type_name, + status=kafka_consumer_group.status, + version=kafka_consumer_group.version, + create_time=kafka_consumer_group.create_time, + update_time=kafka_consumer_group.update_time, + created_by=kafka_consumer_group.created_by, + updated_by=kafka_consumer_group.updated_by, + classifications=kafka_consumer_group.classifications, + classification_names=kafka_consumer_group.classification_names, + meanings=kafka_consumer_group.meanings, + labels=kafka_consumer_group.labels, + business_attributes=kafka_consumer_group.business_attributes, + custom_attributes=kafka_consumer_group.custom_attributes, + pending_tasks=kafka_consumer_group.pending_tasks, + proxy=kafka_consumer_group.proxy, + is_incomplete=kafka_consumer_group.is_incomplete, + provenance_type=kafka_consumer_group.provenance_type, + home_id=kafka_consumer_group.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _kafka_consumer_group_from_nested( + nested: KafkaConsumerGroupNested, +) -> KafkaConsumerGroup: + """Convert nested format to flat KafkaConsumerGroup.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else KafkaConsumerGroupAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _KAFKA_CONSUMER_GROUP_REL_FIELDS, + KafkaConsumerGroupRelationshipAttributes, + ) + return KafkaConsumerGroup( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_kafka_consumer_group_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _kafka_consumer_group_to_nested_bytes( + kafka_consumer_group: KafkaConsumerGroup, serde: Serde +) -> bytes: + """Convert flat KafkaConsumerGroup to nested JSON bytes.""" + return serde.encode(_kafka_consumer_group_to_nested(kafka_consumer_group)) + + +def _kafka_consumer_group_from_nested_bytes( + data: bytes, serde: Serde +) -> KafkaConsumerGroup: + """Convert nested JSON bytes to flat KafkaConsumerGroup.""" + nested = serde.decode(data, KafkaConsumerGroupNested) + return _kafka_consumer_group_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +KafkaConsumerGroup.KAFKA_CONSUMER_GROUP_TOPIC_CONSUMPTION_PROPERTIES = KeywordField( + "kafkaConsumerGroupTopicConsumptionProperties", + "kafkaConsumerGroupTopicConsumptionProperties", +) +KafkaConsumerGroup.KAFKA_CONSUMER_GROUP_MEMBER_COUNT = NumericField( + "kafkaConsumerGroupMemberCount", "kafkaConsumerGroupMemberCount" +) +KafkaConsumerGroup.KAFKA_TOPIC_NAMES = KeywordField( + "kafkaTopicNames", "kafkaTopicNames" +) +KafkaConsumerGroup.KAFKA_TOPIC_QUALIFIED_NAMES = KeywordField( + "kafkaTopicQualifiedNames", "kafkaTopicQualifiedNames" +) +KafkaConsumerGroup.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +KafkaConsumerGroup.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +KafkaConsumerGroup.ANOMALO_CHECKS = RelationField("anomaloChecks") +KafkaConsumerGroup.APPLICATION = RelationField("application") +KafkaConsumerGroup.APPLICATION_FIELD = RelationField("applicationField") +KafkaConsumerGroup.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +KafkaConsumerGroup.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +KafkaConsumerGroup.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +KafkaConsumerGroup.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +KafkaConsumerGroup.METRICS = RelationField("metrics") +KafkaConsumerGroup.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +KafkaConsumerGroup.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +KafkaConsumerGroup.MEANINGS = RelationField("meanings") +KafkaConsumerGroup.KAFKA_TOPICS = RelationField("kafkaTopics") +KafkaConsumerGroup.MC_MONITORS = RelationField("mcMonitors") +KafkaConsumerGroup.MC_INCIDENTS = RelationField("mcIncidents") +KafkaConsumerGroup.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +KafkaConsumerGroup.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +KafkaConsumerGroup.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +KafkaConsumerGroup.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +KafkaConsumerGroup.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +KafkaConsumerGroup.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +KafkaConsumerGroup.FILES = RelationField("files") +KafkaConsumerGroup.LINKS = RelationField("links") +KafkaConsumerGroup.README = RelationField("readme") +KafkaConsumerGroup.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +KafkaConsumerGroup.SODA_CHECKS = RelationField("sodaChecks") +KafkaConsumerGroup.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +KafkaConsumerGroup.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/kafka_related.py b/pyatlan_v9/model/assets/kafka_related.py new file mode 100644 index 000000000..41bbc0436 --- /dev/null +++ b/pyatlan_v9/model/assets/kafka_related.py @@ -0,0 +1,149 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Kafka module. + +This module contains all Related{Type} classes for the Kafka type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Any, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedEventStore +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedKafka", + "RelatedKafkaTopic", + "RelatedKafkaConsumerGroup", + "RelatedAzureEventHub", + "RelatedAzureEventHubConsumerGroup", +] + + +class RelatedKafka(RelatedEventStore): + """ + Related entity reference for Kafka assets. + + Extends RelatedEventStore with Kafka-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Kafka" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Kafka" + + +class RelatedKafkaTopic(RelatedKafka): + """ + Related entity reference for KafkaTopic assets. + + Extends RelatedKafka with KafkaTopic-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "KafkaTopic" so it serializes correctly + + kafka_topic_is_internal: Union[bool, None, UnsetType] = UNSET + """Whether this topic is an internal topic (true) or not (false).""" + + kafka_topic_compression_type: Union[str, None, UnsetType] = UNSET + """Type of compression used for this topic.""" + + kafka_topic_replication_factor: Union[int, None, UnsetType] = UNSET + """Replication factor for this topic.""" + + kafka_topic_segment_bytes: Union[int, None, UnsetType] = UNSET + """Segment size for this topic.""" + + kafka_topic_retention_time_in_ms: Union[int, None, UnsetType] = UNSET + """Amount of time messages will be retained in this topic, in milliseconds.""" + + kafka_topic_partitions_count: Union[int, None, UnsetType] = UNSET + """Number of partitions for this topic.""" + + kafka_topic_size_in_bytes: Union[int, None, UnsetType] = UNSET + """Size of this topic, in bytes.""" + + kafka_topic_record_count: Union[int, None, UnsetType] = UNSET + """Number of (unexpired) messages in this topic.""" + + kafka_topic_cleanup_policy: Union[str, None, UnsetType] = UNSET + """Cleanup policy for this topic.""" + + kafka_topic_log_cleanup_policy: Union[str, None, UnsetType] = UNSET + """Comma seperated Cleanup policy for this topic.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "KafkaTopic" + + +class RelatedKafkaConsumerGroup(RelatedKafka): + """ + Related entity reference for KafkaConsumerGroup assets. + + Extends RelatedKafka with KafkaConsumerGroup-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "KafkaConsumerGroup" so it serializes correctly + + kafka_consumer_group_topic_consumption_properties: Union[ + List[Dict[str, Any]], None, UnsetType + ] = UNSET + """List of consumption properties for Kafka topics, for this consumer group.""" + + kafka_consumer_group_member_count: Union[int, None, UnsetType] = UNSET + """Number of members in this consumer group.""" + + kafka_topic_names: Union[List[str], None, UnsetType] = UNSET + """Simple names of the topics consumed by this consumer group.""" + + kafka_topic_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Unique names of the topics consumed by this consumer group.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "KafkaConsumerGroup" + + +class RelatedAzureEventHub(RelatedKafka): + """ + Related entity reference for AzureEventHub assets. + + Extends RelatedKafka with AzureEventHub-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "AzureEventHub" so it serializes correctly + + kafka_status: Union[str, None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "AzureEventHub" + + +class RelatedAzureEventHubConsumerGroup(RelatedKafka): + """ + Related entity reference for AzureEventHubConsumerGroup assets. + + Extends RelatedKafka with AzureEventHubConsumerGroup-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "AzureEventHubConsumerGroup" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "AzureEventHubConsumerGroup" diff --git a/pyatlan_v9/model/assets/kafka_topic.py b/pyatlan_v9/model/assets/kafka_topic.py new file mode 100644 index 000000000..70c05b543 --- /dev/null +++ b/pyatlan_v9/model/assets/kafka_topic.py @@ -0,0 +1,702 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +KafkaTopic asset model with flattened inheritance. + +This module provides: +- KafkaTopic: Flat asset class (easy to use) +- KafkaTopicAttributes: Nested attributes struct (extends AssetAttributes) +- KafkaTopicNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .kafka_related import RelatedKafkaConsumerGroup + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class KafkaTopic(Asset): + """ + Instance of a Kafka Topic in Atlan. + """ + + KAFKA_TOPIC_IS_INTERNAL: ClassVar[Any] = None + KAFKA_TOPIC_COMPRESSION_TYPE: ClassVar[Any] = None + KAFKA_TOPIC_REPLICATION_FACTOR: ClassVar[Any] = None + KAFKA_TOPIC_SEGMENT_BYTES: ClassVar[Any] = None + KAFKA_TOPIC_RETENTION_TIME_IN_MS: ClassVar[Any] = None + KAFKA_TOPIC_PARTITIONS_COUNT: ClassVar[Any] = None + KAFKA_TOPIC_SIZE_IN_BYTES: ClassVar[Any] = None + KAFKA_TOPIC_RECORD_COUNT: ClassVar[Any] = None + KAFKA_TOPIC_CLEANUP_POLICY: ClassVar[Any] = None + KAFKA_TOPIC_LOG_CLEANUP_POLICY: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + KAFKA_CONSUMER_GROUPS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "KafkaTopic" + + kafka_topic_is_internal: Union[bool, None, UnsetType] = UNSET + """Whether this topic is an internal topic (true) or not (false).""" + + kafka_topic_compression_type: Union[str, None, UnsetType] = UNSET + """Type of compression used for this topic.""" + + kafka_topic_replication_factor: Union[int, None, UnsetType] = UNSET + """Replication factor for this topic.""" + + kafka_topic_segment_bytes: Union[int, None, UnsetType] = UNSET + """Segment size for this topic.""" + + kafka_topic_retention_time_in_ms: Union[int, None, UnsetType] = UNSET + """Amount of time messages will be retained in this topic, in milliseconds.""" + + kafka_topic_partitions_count: Union[int, None, UnsetType] = UNSET + """Number of partitions for this topic.""" + + kafka_topic_size_in_bytes: Union[int, None, UnsetType] = UNSET + """Size of this topic, in bytes.""" + + kafka_topic_record_count: Union[int, None, UnsetType] = UNSET + """Number of (unexpired) messages in this topic.""" + + kafka_topic_cleanup_policy: Union[str, None, UnsetType] = UNSET + """Cleanup policy for this topic.""" + + kafka_topic_log_cleanup_policy: Union[str, None, UnsetType] = UNSET + """Comma seperated Cleanup policy for this topic.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + kafka_consumer_groups: Union[List[RelatedKafkaConsumerGroup], None, UnsetType] = ( + UNSET + ) + """Consumer groups subscribed to this topic.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "KafkaTopic" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/topic/[^/]+$") + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + connection_qualified_name: str, + ) -> "KafkaTopic": + validate_required_fields( + ["name", "connection_qualified_name"], + [name, connection_qualified_name], + ) + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + qualified_name = f"{connection_qualified_name}/topic/{name}" + return cls( + name=name, + qualified_name=qualified_name, + connector_name=connector_name, + connection_qualified_name=connection_qualified_name, + ) + + @classmethod + def create(cls, **kwargs) -> "KafkaTopic": + return cls.creator(**kwargs) + + @classmethod + def create_for_modification(cls, **kwargs) -> "KafkaTopic": + return cls.updater(**kwargs) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _kafka_topic_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> KafkaTopic: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + KafkaTopic instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _kafka_topic_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class KafkaTopicAttributes(AssetAttributes): + """KafkaTopic-specific attributes for nested API format.""" + + kafka_topic_is_internal: Union[bool, None, UnsetType] = UNSET + """Whether this topic is an internal topic (true) or not (false).""" + + kafka_topic_compression_type: Union[str, None, UnsetType] = UNSET + """Type of compression used for this topic.""" + + kafka_topic_replication_factor: Union[int, None, UnsetType] = UNSET + """Replication factor for this topic.""" + + kafka_topic_segment_bytes: Union[int, None, UnsetType] = UNSET + """Segment size for this topic.""" + + kafka_topic_retention_time_in_ms: Union[int, None, UnsetType] = UNSET + """Amount of time messages will be retained in this topic, in milliseconds.""" + + kafka_topic_partitions_count: Union[int, None, UnsetType] = UNSET + """Number of partitions for this topic.""" + + kafka_topic_size_in_bytes: Union[int, None, UnsetType] = UNSET + """Size of this topic, in bytes.""" + + kafka_topic_record_count: Union[int, None, UnsetType] = UNSET + """Number of (unexpired) messages in this topic.""" + + kafka_topic_cleanup_policy: Union[str, None, UnsetType] = UNSET + """Cleanup policy for this topic.""" + + kafka_topic_log_cleanup_policy: Union[str, None, UnsetType] = UNSET + """Comma seperated Cleanup policy for this topic.""" + + +class KafkaTopicRelationshipAttributes(AssetRelationshipAttributes): + """KafkaTopic-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + kafka_consumer_groups: Union[List[RelatedKafkaConsumerGroup], None, UnsetType] = ( + UNSET + ) + """Consumer groups subscribed to this topic.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class KafkaTopicNested(AssetNested): + """KafkaTopic in nested API format for high-performance serialization.""" + + attributes: Union[KafkaTopicAttributes, UnsetType] = UNSET + relationship_attributes: Union[KafkaTopicRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + KafkaTopicRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + KafkaTopicRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_KAFKA_TOPIC_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "kafka_consumer_groups", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_kafka_topic_attrs(attrs: KafkaTopicAttributes, obj: KafkaTopic) -> None: + """Populate KafkaTopic-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.kafka_topic_is_internal = obj.kafka_topic_is_internal + attrs.kafka_topic_compression_type = obj.kafka_topic_compression_type + attrs.kafka_topic_replication_factor = obj.kafka_topic_replication_factor + attrs.kafka_topic_segment_bytes = obj.kafka_topic_segment_bytes + attrs.kafka_topic_retention_time_in_ms = obj.kafka_topic_retention_time_in_ms + attrs.kafka_topic_partitions_count = obj.kafka_topic_partitions_count + attrs.kafka_topic_size_in_bytes = obj.kafka_topic_size_in_bytes + attrs.kafka_topic_record_count = obj.kafka_topic_record_count + attrs.kafka_topic_cleanup_policy = obj.kafka_topic_cleanup_policy + attrs.kafka_topic_log_cleanup_policy = obj.kafka_topic_log_cleanup_policy + + +def _extract_kafka_topic_attrs(attrs: KafkaTopicAttributes) -> dict: + """Extract all KafkaTopic attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["kafka_topic_is_internal"] = attrs.kafka_topic_is_internal + result["kafka_topic_compression_type"] = attrs.kafka_topic_compression_type + result["kafka_topic_replication_factor"] = attrs.kafka_topic_replication_factor + result["kafka_topic_segment_bytes"] = attrs.kafka_topic_segment_bytes + result["kafka_topic_retention_time_in_ms"] = attrs.kafka_topic_retention_time_in_ms + result["kafka_topic_partitions_count"] = attrs.kafka_topic_partitions_count + result["kafka_topic_size_in_bytes"] = attrs.kafka_topic_size_in_bytes + result["kafka_topic_record_count"] = attrs.kafka_topic_record_count + result["kafka_topic_cleanup_policy"] = attrs.kafka_topic_cleanup_policy + result["kafka_topic_log_cleanup_policy"] = attrs.kafka_topic_log_cleanup_policy + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _kafka_topic_to_nested(kafka_topic: KafkaTopic) -> KafkaTopicNested: + """Convert flat KafkaTopic to nested format.""" + attrs = KafkaTopicAttributes() + _populate_kafka_topic_attrs(attrs, kafka_topic) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + kafka_topic, _KAFKA_TOPIC_REL_FIELDS, KafkaTopicRelationshipAttributes + ) + return KafkaTopicNested( + guid=kafka_topic.guid, + type_name=kafka_topic.type_name, + status=kafka_topic.status, + version=kafka_topic.version, + create_time=kafka_topic.create_time, + update_time=kafka_topic.update_time, + created_by=kafka_topic.created_by, + updated_by=kafka_topic.updated_by, + classifications=kafka_topic.classifications, + classification_names=kafka_topic.classification_names, + meanings=kafka_topic.meanings, + labels=kafka_topic.labels, + business_attributes=kafka_topic.business_attributes, + custom_attributes=kafka_topic.custom_attributes, + pending_tasks=kafka_topic.pending_tasks, + proxy=kafka_topic.proxy, + is_incomplete=kafka_topic.is_incomplete, + provenance_type=kafka_topic.provenance_type, + home_id=kafka_topic.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _kafka_topic_from_nested(nested: KafkaTopicNested) -> KafkaTopic: + """Convert nested format to flat KafkaTopic.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else KafkaTopicAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _KAFKA_TOPIC_REL_FIELDS, + KafkaTopicRelationshipAttributes, + ) + return KafkaTopic( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_kafka_topic_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _kafka_topic_to_nested_bytes(kafka_topic: KafkaTopic, serde: Serde) -> bytes: + """Convert flat KafkaTopic to nested JSON bytes.""" + return serde.encode(_kafka_topic_to_nested(kafka_topic)) + + +def _kafka_topic_from_nested_bytes(data: bytes, serde: Serde) -> KafkaTopic: + """Convert nested JSON bytes to flat KafkaTopic.""" + nested = serde.decode(data, KafkaTopicNested) + return _kafka_topic_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, +) + +KafkaTopic.KAFKA_TOPIC_IS_INTERNAL = BooleanField( + "kafkaTopicIsInternal", "kafkaTopicIsInternal" +) +KafkaTopic.KAFKA_TOPIC_COMPRESSION_TYPE = KeywordField( + "kafkaTopicCompressionType", "kafkaTopicCompressionType" +) +KafkaTopic.KAFKA_TOPIC_REPLICATION_FACTOR = NumericField( + "kafkaTopicReplicationFactor", "kafkaTopicReplicationFactor" +) +KafkaTopic.KAFKA_TOPIC_SEGMENT_BYTES = NumericField( + "kafkaTopicSegmentBytes", "kafkaTopicSegmentBytes" +) +KafkaTopic.KAFKA_TOPIC_RETENTION_TIME_IN_MS = NumericField( + "kafkaTopicRetentionTimeInMs", "kafkaTopicRetentionTimeInMs" +) +KafkaTopic.KAFKA_TOPIC_PARTITIONS_COUNT = NumericField( + "kafkaTopicPartitionsCount", "kafkaTopicPartitionsCount" +) +KafkaTopic.KAFKA_TOPIC_SIZE_IN_BYTES = NumericField( + "kafkaTopicSizeInBytes", "kafkaTopicSizeInBytes" +) +KafkaTopic.KAFKA_TOPIC_RECORD_COUNT = NumericField( + "kafkaTopicRecordCount", "kafkaTopicRecordCount" +) +KafkaTopic.KAFKA_TOPIC_CLEANUP_POLICY = KeywordField( + "kafkaTopicCleanupPolicy", "kafkaTopicCleanupPolicy" +) +KafkaTopic.KAFKA_TOPIC_LOG_CLEANUP_POLICY = KeywordField( + "kafkaTopicLogCleanupPolicy", "kafkaTopicLogCleanupPolicy" +) +KafkaTopic.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +KafkaTopic.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +KafkaTopic.ANOMALO_CHECKS = RelationField("anomaloChecks") +KafkaTopic.APPLICATION = RelationField("application") +KafkaTopic.APPLICATION_FIELD = RelationField("applicationField") +KafkaTopic.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +KafkaTopic.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +KafkaTopic.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +KafkaTopic.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +KafkaTopic.METRICS = RelationField("metrics") +KafkaTopic.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +KafkaTopic.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +KafkaTopic.MEANINGS = RelationField("meanings") +KafkaTopic.KAFKA_CONSUMER_GROUPS = RelationField("kafkaConsumerGroups") +KafkaTopic.MC_MONITORS = RelationField("mcMonitors") +KafkaTopic.MC_INCIDENTS = RelationField("mcIncidents") +KafkaTopic.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +KafkaTopic.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +KafkaTopic.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +KafkaTopic.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +KafkaTopic.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +KafkaTopic.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +KafkaTopic.FILES = RelationField("files") +KafkaTopic.LINKS = RelationField("links") +KafkaTopic.README = RelationField("readme") +KafkaTopic.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +KafkaTopic.SODA_CHECKS = RelationField("sodaChecks") +KafkaTopic.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +KafkaTopic.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/link.py b/pyatlan_v9/model/assets/link.py new file mode 100644 index 000000000..dfaa32905 --- /dev/null +++ b/pyatlan_v9/model/assets/link.py @@ -0,0 +1,593 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Link asset model with flattened inheritance. + +This module provides: +- Link: Flat asset class (easy to use) +- LinkAttributes: Nested attributes struct (extends AssetAttributes) +- LinkNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .asset_related import RelatedAsset +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .resource_related import RelatedFile, RelatedLink, RelatedReadme + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Link(Asset): + """ + Instance of a link in Atlan. + """ + + ICON: ClassVar[Any] = None + ICON_TYPE: ClassVar[Any] = None + LINK: ClassVar[Any] = None + IS_GLOBAL: ClassVar[Any] = None + REFERENCE: ClassVar[Any] = None + RESOURCE_METADATA: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + ASSET: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Link" + + icon: Union[str, None, UnsetType] = UNSET + """Icon for the link.""" + + icon_type: Union[str, None, UnsetType] = UNSET + """Type of icon for the link, for example: image or emoji.""" + + link: Union[str, None, UnsetType] = UNSET + """URL to the resource.""" + + is_global: Union[bool, None, UnsetType] = UNSET + """Whether the resource is global (true) or not (false).""" + + reference: Union[str, None, UnsetType] = UNSET + """Reference to the resource.""" + + resource_metadata: Union[Dict[str, str], None, UnsetType] = UNSET + """Metadata of the resource.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + asset: Union[RelatedAsset, None, UnsetType] = UNSET + """Asset to which the link is attached.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Link" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _link_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Link: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Link instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _link_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class LinkAttributes(AssetAttributes): + """Link-specific attributes for nested API format.""" + + icon: Union[str, None, UnsetType] = UNSET + """Icon for the link.""" + + icon_type: Union[str, None, UnsetType] = UNSET + """Type of icon for the link, for example: image or emoji.""" + + link: Union[str, None, UnsetType] = UNSET + """URL to the resource.""" + + is_global: Union[bool, None, UnsetType] = UNSET + """Whether the resource is global (true) or not (false).""" + + reference: Union[str, None, UnsetType] = UNSET + """Reference to the resource.""" + + resource_metadata: Union[Dict[str, str], None, UnsetType] = UNSET + """Metadata of the resource.""" + + +class LinkRelationshipAttributes(AssetRelationshipAttributes): + """Link-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + asset: Union[RelatedAsset, None, UnsetType] = UNSET + """Asset to which the link is attached.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class LinkNested(AssetNested): + """Link in nested API format for high-performance serialization.""" + + attributes: Union[LinkAttributes, UnsetType] = UNSET + relationship_attributes: Union[LinkRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[LinkRelationshipAttributes, UnsetType] = UNSET + remove_relationship_attributes: Union[LinkRelationshipAttributes, UnsetType] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_LINK_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "asset", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_link_attrs(attrs: LinkAttributes, obj: Link) -> None: + """Populate Link-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.icon = obj.icon + attrs.icon_type = obj.icon_type + attrs.link = obj.link + attrs.is_global = obj.is_global + attrs.reference = obj.reference + attrs.resource_metadata = obj.resource_metadata + + +def _extract_link_attrs(attrs: LinkAttributes) -> dict: + """Extract all Link attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["icon"] = attrs.icon + result["icon_type"] = attrs.icon_type + result["link"] = attrs.link + result["is_global"] = attrs.is_global + result["reference"] = attrs.reference + result["resource_metadata"] = attrs.resource_metadata + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _link_to_nested(link: Link) -> LinkNested: + """Convert flat Link to nested format.""" + attrs = LinkAttributes() + _populate_link_attrs(attrs, link) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + link, _LINK_REL_FIELDS, LinkRelationshipAttributes + ) + return LinkNested( + guid=link.guid, + type_name=link.type_name, + status=link.status, + version=link.version, + create_time=link.create_time, + update_time=link.update_time, + created_by=link.created_by, + updated_by=link.updated_by, + classifications=link.classifications, + classification_names=link.classification_names, + meanings=link.meanings, + labels=link.labels, + business_attributes=link.business_attributes, + custom_attributes=link.custom_attributes, + pending_tasks=link.pending_tasks, + proxy=link.proxy, + is_incomplete=link.is_incomplete, + provenance_type=link.provenance_type, + home_id=link.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _link_from_nested(nested: LinkNested) -> Link: + """Convert nested format to flat Link.""" + attrs = nested.attributes if nested.attributes is not UNSET else LinkAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _LINK_REL_FIELDS, + LinkRelationshipAttributes, + ) + return Link( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_link_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _link_to_nested_bytes(link: Link, serde: Serde) -> bytes: + """Convert flat Link to nested JSON bytes.""" + return serde.encode(_link_to_nested(link)) + + +def _link_from_nested_bytes(data: bytes, serde: Serde) -> Link: + """Convert nested JSON bytes to flat Link.""" + nested = serde.decode(data, LinkNested) + return _link_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + RelationField, +) + +Link.ICON = KeywordField("icon", "icon") +Link.ICON_TYPE = KeywordField("iconType", "iconType") +Link.LINK = KeywordField("link", "link") +Link.IS_GLOBAL = BooleanField("isGlobal", "isGlobal") +Link.REFERENCE = KeywordField("reference", "reference") +Link.RESOURCE_METADATA = KeywordField("resourceMetadata", "resourceMetadata") +Link.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Link.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Link.ANOMALO_CHECKS = RelationField("anomaloChecks") +Link.APPLICATION = RelationField("application") +Link.APPLICATION_FIELD = RelationField("applicationField") +Link.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Link.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Link.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Link.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Link.METRICS = RelationField("metrics") +Link.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Link.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Link.MEANINGS = RelationField("meanings") +Link.MC_MONITORS = RelationField("mcMonitors") +Link.MC_INCIDENTS = RelationField("mcIncidents") +Link.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Link.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Link.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Link.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Link.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Link.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Link.FILES = RelationField("files") +Link.LINKS = RelationField("links") +Link.ASSET = RelationField("asset") +Link.README = RelationField("readme") +Link.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Link.SODA_CHECKS = RelationField("sodaChecks") +Link.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Link.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/looker.py b/pyatlan_v9/model/assets/looker.py new file mode 100644 index 000000000..69ac2ee78 --- /dev/null +++ b/pyatlan_v9/model/assets/looker.py @@ -0,0 +1,535 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Looker asset model with flattened inheritance. + +This module provides: +- Looker: Flat asset class (easy to use) +- LookerAttributes: Nested attributes struct (extends AssetAttributes) +- LookerNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Looker(Asset): + """ + Base class for Looker assets. + """ + + LOOKER_SLUG: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Looker" + + looker_slug: Union[str, None, UnsetType] = UNSET + """An alpha-numeric slug for the underlying Looker asset that can be used to uniquely identify it""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Looker" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _looker_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Looker: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Looker instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _looker_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class LookerAttributes(AssetAttributes): + """Looker-specific attributes for nested API format.""" + + looker_slug: Union[str, None, UnsetType] = UNSET + """An alpha-numeric slug for the underlying Looker asset that can be used to uniquely identify it""" + + +class LookerRelationshipAttributes(AssetRelationshipAttributes): + """Looker-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class LookerNested(AssetNested): + """Looker in nested API format for high-performance serialization.""" + + attributes: Union[LookerAttributes, UnsetType] = UNSET + relationship_attributes: Union[LookerRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[LookerRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[LookerRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_LOOKER_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_looker_attrs(attrs: LookerAttributes, obj: Looker) -> None: + """Populate Looker-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.looker_slug = obj.looker_slug + + +def _extract_looker_attrs(attrs: LookerAttributes) -> dict: + """Extract all Looker attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["looker_slug"] = attrs.looker_slug + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _looker_to_nested(looker: Looker) -> LookerNested: + """Convert flat Looker to nested format.""" + attrs = LookerAttributes() + _populate_looker_attrs(attrs, looker) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + looker, _LOOKER_REL_FIELDS, LookerRelationshipAttributes + ) + return LookerNested( + guid=looker.guid, + type_name=looker.type_name, + status=looker.status, + version=looker.version, + create_time=looker.create_time, + update_time=looker.update_time, + created_by=looker.created_by, + updated_by=looker.updated_by, + classifications=looker.classifications, + classification_names=looker.classification_names, + meanings=looker.meanings, + labels=looker.labels, + business_attributes=looker.business_attributes, + custom_attributes=looker.custom_attributes, + pending_tasks=looker.pending_tasks, + proxy=looker.proxy, + is_incomplete=looker.is_incomplete, + provenance_type=looker.provenance_type, + home_id=looker.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _looker_from_nested(nested: LookerNested) -> Looker: + """Convert nested format to flat Looker.""" + attrs = nested.attributes if nested.attributes is not UNSET else LookerAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _LOOKER_REL_FIELDS, + LookerRelationshipAttributes, + ) + return Looker( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_looker_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _looker_to_nested_bytes(looker: Looker, serde: Serde) -> bytes: + """Convert flat Looker to nested JSON bytes.""" + return serde.encode(_looker_to_nested(looker)) + + +def _looker_from_nested_bytes(data: bytes, serde: Serde) -> Looker: + """Convert nested JSON bytes to flat Looker.""" + nested = serde.decode(data, LookerNested) + return _looker_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +Looker.LOOKER_SLUG = KeywordField("lookerSlug", "lookerSlug") +Looker.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Looker.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Looker.ANOMALO_CHECKS = RelationField("anomaloChecks") +Looker.APPLICATION = RelationField("application") +Looker.APPLICATION_FIELD = RelationField("applicationField") +Looker.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Looker.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Looker.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Looker.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Looker.METRICS = RelationField("metrics") +Looker.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Looker.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Looker.MEANINGS = RelationField("meanings") +Looker.MC_MONITORS = RelationField("mcMonitors") +Looker.MC_INCIDENTS = RelationField("mcIncidents") +Looker.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Looker.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Looker.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Looker.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Looker.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Looker.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Looker.FILES = RelationField("files") +Looker.LINKS = RelationField("links") +Looker.README = RelationField("readme") +Looker.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Looker.SODA_CHECKS = RelationField("sodaChecks") +Looker.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Looker.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/looker_dashboard.py b/pyatlan_v9/model/assets/looker_dashboard.py new file mode 100644 index 000000000..a7df6e1f3 --- /dev/null +++ b/pyatlan_v9/model/assets/looker_dashboard.py @@ -0,0 +1,682 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +LookerDashboard asset model with flattened inheritance. + +This module provides: +- LookerDashboard: Flat asset class (easy to use) +- LookerDashboardAttributes: Nested attributes struct (extends AssetAttributes) +- LookerDashboardNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .looker_related import ( + RelatedLookerField, + RelatedLookerFolder, + RelatedLookerLook, + RelatedLookerTile, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class LookerDashboard(Asset): + """ + Instance of a Looker dashboard in Atlan. + """ + + FOLDER_NAME: ClassVar[Any] = None + SOURCE_USER_ID: ClassVar[Any] = None + SOURCE_VIEW_COUNT: ClassVar[Any] = None + SOURCE_METADATA_ID: ClassVar[Any] = None + SOURCELAST_UPDATER_ID: ClassVar[Any] = None + SOURCE_LAST_ACCESSED_AT: ClassVar[Any] = None + SOURCE_LAST_VIEWED_AT: ClassVar[Any] = None + LOOKER_SLUG: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + FOLDER: ClassVar[Any] = None + LOOKS: ClassVar[Any] = None + TILES: ClassVar[Any] = None + FIELDS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "LookerDashboard" + + folder_name: Union[str, None, UnsetType] = UNSET + """Name of the parent folder in Looker that contains this dashboard.""" + + source_user_id: Union[int, None, UnsetType] = UNSET + """Identifier of the user who created this dashboard, from Looker.""" + + source_view_count: Union[int, None, UnsetType] = UNSET + """Number of times the dashboard has been viewed through the Looker web UI.""" + + source_metadata_id: Union[int, None, UnsetType] = UNSET + """Identifier of the dashboard's content metadata, from Looker.""" + + sourcelast_updater_id: Union[int, None, UnsetType] = UNSET + """Identifier of the user who last updated the dashboard, from Looker.""" + + source_last_accessed_at: Union[int, None, UnsetType] = UNSET + """Timestamp (epoch) when the dashboard was last accessed by a user, in milliseconds.""" + + source_last_viewed_at: Union[int, None, UnsetType] = UNSET + """Timestamp (epoch) when the dashboard was last viewed by a user.""" + + looker_slug: Union[str, None, UnsetType] = UNSET + """An alpha-numeric slug for the underlying Looker asset that can be used to uniquely identify it""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + folder: Union[RelatedLookerFolder, None, UnsetType] = UNSET + """Folder in which the dashboard exists.""" + + looks: Union[List[RelatedLookerLook], None, UnsetType] = UNSET + """Looks that are used within this dashboard.""" + + tiles: Union[List[RelatedLookerTile], None, UnsetType] = UNSET + """Tiles that exist within this dashboard.""" + + fields: Union[List[RelatedLookerField], None, UnsetType] = UNSET + """Fields that are used in this dashboard.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "LookerDashboard" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _looker_dashboard_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> LookerDashboard: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + LookerDashboard instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _looker_dashboard_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class LookerDashboardAttributes(AssetAttributes): + """LookerDashboard-specific attributes for nested API format.""" + + folder_name: Union[str, None, UnsetType] = UNSET + """Name of the parent folder in Looker that contains this dashboard.""" + + source_user_id: Union[int, None, UnsetType] = UNSET + """Identifier of the user who created this dashboard, from Looker.""" + + source_view_count: Union[int, None, UnsetType] = UNSET + """Number of times the dashboard has been viewed through the Looker web UI.""" + + source_metadata_id: Union[int, None, UnsetType] = UNSET + """Identifier of the dashboard's content metadata, from Looker.""" + + sourcelast_updater_id: Union[int, None, UnsetType] = UNSET + """Identifier of the user who last updated the dashboard, from Looker.""" + + source_last_accessed_at: Union[int, None, UnsetType] = UNSET + """Timestamp (epoch) when the dashboard was last accessed by a user, in milliseconds.""" + + source_last_viewed_at: Union[int, None, UnsetType] = UNSET + """Timestamp (epoch) when the dashboard was last viewed by a user.""" + + looker_slug: Union[str, None, UnsetType] = UNSET + """An alpha-numeric slug for the underlying Looker asset that can be used to uniquely identify it""" + + +class LookerDashboardRelationshipAttributes(AssetRelationshipAttributes): + """LookerDashboard-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + folder: Union[RelatedLookerFolder, None, UnsetType] = UNSET + """Folder in which the dashboard exists.""" + + looks: Union[List[RelatedLookerLook], None, UnsetType] = UNSET + """Looks that are used within this dashboard.""" + + tiles: Union[List[RelatedLookerTile], None, UnsetType] = UNSET + """Tiles that exist within this dashboard.""" + + fields: Union[List[RelatedLookerField], None, UnsetType] = UNSET + """Fields that are used in this dashboard.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class LookerDashboardNested(AssetNested): + """LookerDashboard in nested API format for high-performance serialization.""" + + attributes: Union[LookerDashboardAttributes, UnsetType] = UNSET + relationship_attributes: Union[LookerDashboardRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + LookerDashboardRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + LookerDashboardRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_LOOKER_DASHBOARD_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "folder", + "looks", + "tiles", + "fields", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_looker_dashboard_attrs( + attrs: LookerDashboardAttributes, obj: LookerDashboard +) -> None: + """Populate LookerDashboard-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.folder_name = obj.folder_name + attrs.source_user_id = obj.source_user_id + attrs.source_view_count = obj.source_view_count + attrs.source_metadata_id = obj.source_metadata_id + attrs.sourcelast_updater_id = obj.sourcelast_updater_id + attrs.source_last_accessed_at = obj.source_last_accessed_at + attrs.source_last_viewed_at = obj.source_last_viewed_at + attrs.looker_slug = obj.looker_slug + + +def _extract_looker_dashboard_attrs(attrs: LookerDashboardAttributes) -> dict: + """Extract all LookerDashboard attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["folder_name"] = attrs.folder_name + result["source_user_id"] = attrs.source_user_id + result["source_view_count"] = attrs.source_view_count + result["source_metadata_id"] = attrs.source_metadata_id + result["sourcelast_updater_id"] = attrs.sourcelast_updater_id + result["source_last_accessed_at"] = attrs.source_last_accessed_at + result["source_last_viewed_at"] = attrs.source_last_viewed_at + result["looker_slug"] = attrs.looker_slug + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _looker_dashboard_to_nested( + looker_dashboard: LookerDashboard, +) -> LookerDashboardNested: + """Convert flat LookerDashboard to nested format.""" + attrs = LookerDashboardAttributes() + _populate_looker_dashboard_attrs(attrs, looker_dashboard) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + looker_dashboard, + _LOOKER_DASHBOARD_REL_FIELDS, + LookerDashboardRelationshipAttributes, + ) + return LookerDashboardNested( + guid=looker_dashboard.guid, + type_name=looker_dashboard.type_name, + status=looker_dashboard.status, + version=looker_dashboard.version, + create_time=looker_dashboard.create_time, + update_time=looker_dashboard.update_time, + created_by=looker_dashboard.created_by, + updated_by=looker_dashboard.updated_by, + classifications=looker_dashboard.classifications, + classification_names=looker_dashboard.classification_names, + meanings=looker_dashboard.meanings, + labels=looker_dashboard.labels, + business_attributes=looker_dashboard.business_attributes, + custom_attributes=looker_dashboard.custom_attributes, + pending_tasks=looker_dashboard.pending_tasks, + proxy=looker_dashboard.proxy, + is_incomplete=looker_dashboard.is_incomplete, + provenance_type=looker_dashboard.provenance_type, + home_id=looker_dashboard.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _looker_dashboard_from_nested(nested: LookerDashboardNested) -> LookerDashboard: + """Convert nested format to flat LookerDashboard.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else LookerDashboardAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _LOOKER_DASHBOARD_REL_FIELDS, + LookerDashboardRelationshipAttributes, + ) + return LookerDashboard( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_looker_dashboard_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _looker_dashboard_to_nested_bytes( + looker_dashboard: LookerDashboard, serde: Serde +) -> bytes: + """Convert flat LookerDashboard to nested JSON bytes.""" + return serde.encode(_looker_dashboard_to_nested(looker_dashboard)) + + +def _looker_dashboard_from_nested_bytes(data: bytes, serde: Serde) -> LookerDashboard: + """Convert nested JSON bytes to flat LookerDashboard.""" + nested = serde.decode(data, LookerDashboardNested) + return _looker_dashboard_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +LookerDashboard.FOLDER_NAME = KeywordField("folderName", "folderName") +LookerDashboard.SOURCE_USER_ID = NumericField("sourceUserId", "sourceUserId") +LookerDashboard.SOURCE_VIEW_COUNT = NumericField("sourceViewCount", "sourceViewCount") +LookerDashboard.SOURCE_METADATA_ID = NumericField( + "sourceMetadataId", "sourceMetadataId" +) +LookerDashboard.SOURCELAST_UPDATER_ID = NumericField( + "sourcelastUpdaterId", "sourcelastUpdaterId" +) +LookerDashboard.SOURCE_LAST_ACCESSED_AT = NumericField( + "sourceLastAccessedAt", "sourceLastAccessedAt" +) +LookerDashboard.SOURCE_LAST_VIEWED_AT = NumericField( + "sourceLastViewedAt", "sourceLastViewedAt" +) +LookerDashboard.LOOKER_SLUG = KeywordField("lookerSlug", "lookerSlug") +LookerDashboard.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +LookerDashboard.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +LookerDashboard.ANOMALO_CHECKS = RelationField("anomaloChecks") +LookerDashboard.APPLICATION = RelationField("application") +LookerDashboard.APPLICATION_FIELD = RelationField("applicationField") +LookerDashboard.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +LookerDashboard.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +LookerDashboard.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +LookerDashboard.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +LookerDashboard.METRICS = RelationField("metrics") +LookerDashboard.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +LookerDashboard.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +LookerDashboard.MEANINGS = RelationField("meanings") +LookerDashboard.FOLDER = RelationField("folder") +LookerDashboard.LOOKS = RelationField("looks") +LookerDashboard.TILES = RelationField("tiles") +LookerDashboard.FIELDS = RelationField("fields") +LookerDashboard.MC_MONITORS = RelationField("mcMonitors") +LookerDashboard.MC_INCIDENTS = RelationField("mcIncidents") +LookerDashboard.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +LookerDashboard.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +LookerDashboard.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +LookerDashboard.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +LookerDashboard.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +LookerDashboard.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +LookerDashboard.FILES = RelationField("files") +LookerDashboard.LINKS = RelationField("links") +LookerDashboard.README = RelationField("readme") +LookerDashboard.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +LookerDashboard.SODA_CHECKS = RelationField("sodaChecks") +LookerDashboard.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +LookerDashboard.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/looker_explore.py b/pyatlan_v9/model/assets/looker_explore.py new file mode 100644 index 000000000..72aea899f --- /dev/null +++ b/pyatlan_v9/model/assets/looker_explore.py @@ -0,0 +1,635 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +LookerExplore asset model with flattened inheritance. + +This module provides: +- LookerExplore: Flat asset class (easy to use) +- LookerExploreAttributes: Nested attributes struct (extends AssetAttributes) +- LookerExploreNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .looker_related import RelatedLookerField, RelatedLookerModel, RelatedLookerProject + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class LookerExplore(Asset): + """ + Instance of a Looker Explore in Atlan. Explores are views that users can query in Looker. + """ + + PROJECT_NAME: ClassVar[Any] = None + MODEL_NAME: ClassVar[Any] = None + SOURCE_CONNECTION_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + SQL_TABLE_NAME: ClassVar[Any] = None + LOOKER_SLUG: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MODEL: ClassVar[Any] = None + PROJECT: ClassVar[Any] = None + FIELDS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "LookerExplore" + + project_name: Union[str, None, UnsetType] = UNSET + """Name of the parent project of this Explore.""" + + model_name: Union[str, None, UnsetType] = UNSET + """Name of the parent model of this Explore.""" + + source_connection_name: Union[str, None, UnsetType] = UNSET + """Connection name for the Explore, from Looker.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Name of the view for the Explore.""" + + sql_table_name: Union[str, None, UnsetType] = UNSET + """Name of the SQL table used to declare the Explore.""" + + looker_slug: Union[str, None, UnsetType] = UNSET + """An alpha-numeric slug for the underlying Looker asset that can be used to uniquely identify it""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + model: Union[RelatedLookerModel, None, UnsetType] = UNSET + """Model in which this explore exists.""" + + project: Union[RelatedLookerProject, None, UnsetType] = UNSET + """Project in which this explore exists.""" + + fields: Union[List[RelatedLookerField], None, UnsetType] = UNSET + """Fields that exist within this Explore.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "LookerExplore" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _looker_explore_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> LookerExplore: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + LookerExplore instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _looker_explore_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class LookerExploreAttributes(AssetAttributes): + """LookerExplore-specific attributes for nested API format.""" + + project_name: Union[str, None, UnsetType] = UNSET + """Name of the parent project of this Explore.""" + + model_name: Union[str, None, UnsetType] = UNSET + """Name of the parent model of this Explore.""" + + source_connection_name: Union[str, None, UnsetType] = UNSET + """Connection name for the Explore, from Looker.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Name of the view for the Explore.""" + + sql_table_name: Union[str, None, UnsetType] = UNSET + """Name of the SQL table used to declare the Explore.""" + + looker_slug: Union[str, None, UnsetType] = UNSET + """An alpha-numeric slug for the underlying Looker asset that can be used to uniquely identify it""" + + +class LookerExploreRelationshipAttributes(AssetRelationshipAttributes): + """LookerExplore-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + model: Union[RelatedLookerModel, None, UnsetType] = UNSET + """Model in which this explore exists.""" + + project: Union[RelatedLookerProject, None, UnsetType] = UNSET + """Project in which this explore exists.""" + + fields: Union[List[RelatedLookerField], None, UnsetType] = UNSET + """Fields that exist within this Explore.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class LookerExploreNested(AssetNested): + """LookerExplore in nested API format for high-performance serialization.""" + + attributes: Union[LookerExploreAttributes, UnsetType] = UNSET + relationship_attributes: Union[LookerExploreRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + LookerExploreRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + LookerExploreRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_LOOKER_EXPLORE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "model", + "project", + "fields", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_looker_explore_attrs( + attrs: LookerExploreAttributes, obj: LookerExplore +) -> None: + """Populate LookerExplore-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.project_name = obj.project_name + attrs.model_name = obj.model_name + attrs.source_connection_name = obj.source_connection_name + attrs.view_name = obj.view_name + attrs.sql_table_name = obj.sql_table_name + attrs.looker_slug = obj.looker_slug + + +def _extract_looker_explore_attrs(attrs: LookerExploreAttributes) -> dict: + """Extract all LookerExplore attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["project_name"] = attrs.project_name + result["model_name"] = attrs.model_name + result["source_connection_name"] = attrs.source_connection_name + result["view_name"] = attrs.view_name + result["sql_table_name"] = attrs.sql_table_name + result["looker_slug"] = attrs.looker_slug + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _looker_explore_to_nested(looker_explore: LookerExplore) -> LookerExploreNested: + """Convert flat LookerExplore to nested format.""" + attrs = LookerExploreAttributes() + _populate_looker_explore_attrs(attrs, looker_explore) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + looker_explore, _LOOKER_EXPLORE_REL_FIELDS, LookerExploreRelationshipAttributes + ) + return LookerExploreNested( + guid=looker_explore.guid, + type_name=looker_explore.type_name, + status=looker_explore.status, + version=looker_explore.version, + create_time=looker_explore.create_time, + update_time=looker_explore.update_time, + created_by=looker_explore.created_by, + updated_by=looker_explore.updated_by, + classifications=looker_explore.classifications, + classification_names=looker_explore.classification_names, + meanings=looker_explore.meanings, + labels=looker_explore.labels, + business_attributes=looker_explore.business_attributes, + custom_attributes=looker_explore.custom_attributes, + pending_tasks=looker_explore.pending_tasks, + proxy=looker_explore.proxy, + is_incomplete=looker_explore.is_incomplete, + provenance_type=looker_explore.provenance_type, + home_id=looker_explore.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _looker_explore_from_nested(nested: LookerExploreNested) -> LookerExplore: + """Convert nested format to flat LookerExplore.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else LookerExploreAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _LOOKER_EXPLORE_REL_FIELDS, + LookerExploreRelationshipAttributes, + ) + return LookerExplore( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_looker_explore_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _looker_explore_to_nested_bytes( + looker_explore: LookerExplore, serde: Serde +) -> bytes: + """Convert flat LookerExplore to nested JSON bytes.""" + return serde.encode(_looker_explore_to_nested(looker_explore)) + + +def _looker_explore_from_nested_bytes(data: bytes, serde: Serde) -> LookerExplore: + """Convert nested JSON bytes to flat LookerExplore.""" + nested = serde.decode(data, LookerExploreNested) + return _looker_explore_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +LookerExplore.PROJECT_NAME = KeywordField("projectName", "projectName") +LookerExplore.MODEL_NAME = KeywordField("modelName", "modelName") +LookerExplore.SOURCE_CONNECTION_NAME = KeywordField( + "sourceConnectionName", "sourceConnectionName" +) +LookerExplore.VIEW_NAME = KeywordField("viewName", "viewName") +LookerExplore.SQL_TABLE_NAME = KeywordField("sqlTableName", "sqlTableName") +LookerExplore.LOOKER_SLUG = KeywordField("lookerSlug", "lookerSlug") +LookerExplore.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +LookerExplore.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +LookerExplore.ANOMALO_CHECKS = RelationField("anomaloChecks") +LookerExplore.APPLICATION = RelationField("application") +LookerExplore.APPLICATION_FIELD = RelationField("applicationField") +LookerExplore.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +LookerExplore.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +LookerExplore.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +LookerExplore.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +LookerExplore.METRICS = RelationField("metrics") +LookerExplore.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +LookerExplore.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +LookerExplore.MEANINGS = RelationField("meanings") +LookerExplore.MODEL = RelationField("model") +LookerExplore.PROJECT = RelationField("project") +LookerExplore.FIELDS = RelationField("fields") +LookerExplore.MC_MONITORS = RelationField("mcMonitors") +LookerExplore.MC_INCIDENTS = RelationField("mcIncidents") +LookerExplore.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +LookerExplore.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +LookerExplore.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +LookerExplore.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +LookerExplore.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +LookerExplore.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +LookerExplore.FILES = RelationField("files") +LookerExplore.LINKS = RelationField("links") +LookerExplore.README = RelationField("readme") +LookerExplore.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +LookerExplore.SODA_CHECKS = RelationField("sodaChecks") +LookerExplore.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +LookerExplore.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/looker_field.py b/pyatlan_v9/model/assets/looker_field.py new file mode 100644 index 000000000..34a74fa28 --- /dev/null +++ b/pyatlan_v9/model/assets/looker_field.py @@ -0,0 +1,780 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +LookerField asset model with flattened inheritance. + +This module provides: +- LookerField: Flat asset class (easy to use) +- LookerFieldAttributes: Nested attributes struct (extends AssetAttributes) +- LookerFieldNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .looker_related import ( + RelatedLookerDashboard, + RelatedLookerExplore, + RelatedLookerLook, + RelatedLookerModel, + RelatedLookerProject, + RelatedLookerTile, + RelatedLookerView, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class LookerField(Asset): + """ + Instance of a Looker field in Atlan. + """ + + PROJECT_NAME: ClassVar[Any] = None + LOOKER_EXPLORE_QUALIFIED_NAME: ClassVar[Any] = None + LOOKER_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + LOOKER_TILE_QUALIFIED_NAME: ClassVar[Any] = None + LOOKER_LOOK_QUALIFIED_NAME: ClassVar[Any] = None + LOOKER_DASHBOARD_QUALIFIED_NAME: ClassVar[Any] = None + MODEL_NAME: ClassVar[Any] = None + SOURCE_DEFINITION: ClassVar[Any] = None + LOOKER_FIELD_DATA_TYPE: ClassVar[Any] = None + LOOKER_TIMES_USED: ClassVar[Any] = None + LOOKER_FIELD_IS_REFINED: ClassVar[Any] = None + LOOKER_FIELD_REFINEMENT_FILE_PATH: ClassVar[Any] = None + LOOKER_FIELD_REFINEMENT_LINE_NUMBER: ClassVar[Any] = None + LOOKER_SLUG: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MODEL: ClassVar[Any] = None + EXPLORE: ClassVar[Any] = None + PROJECT: ClassVar[Any] = None + VIEW: ClassVar[Any] = None + TILE: ClassVar[Any] = None + LOOK: ClassVar[Any] = None + DASHBOARD: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "LookerField" + + project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which this field exists.""" + + looker_explore_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the Explore in which this field exists.""" + + looker_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this field exists.""" + + looker_tile_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the tile in which this field is used.""" + + looker_look_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the look in which this field is used.""" + + looker_dashboard_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dashboard in which this field is used.""" + + model_name: Union[str, None, UnsetType] = UNSET + """Name of the model in which this field exists.""" + + source_definition: Union[str, None, UnsetType] = UNSET + """Deprecated.""" + + looker_field_data_type: Union[str, None, UnsetType] = UNSET + """Deprecated.""" + + looker_times_used: Union[int, None, UnsetType] = UNSET + """Deprecated.""" + + looker_field_is_refined: Union[bool, None, UnsetType] = UNSET + """Whether the looker field asset is coming from a refinement""" + + looker_field_refinement_file_path: Union[str, None, UnsetType] = UNSET + """Absolute path of the file where the refinement of the field is declared.""" + + looker_field_refinement_line_number: Union[str, None, UnsetType] = UNSET + """Line number in the lookerFieldRefinementFilePath where this refinement of the field is declared.""" + + looker_slug: Union[str, None, UnsetType] = UNSET + """An alpha-numeric slug for the underlying Looker asset that can be used to uniquely identify it""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + model: Union[RelatedLookerModel, None, UnsetType] = UNSET + """Model in which this field exists.""" + + explore: Union[RelatedLookerExplore, None, UnsetType] = UNSET + """Explore in which this field exists.""" + + project: Union[RelatedLookerProject, None, UnsetType] = UNSET + """Project in which this field exists.""" + + view: Union[RelatedLookerView, None, UnsetType] = UNSET + """View in which this field exists.""" + + tile: Union[RelatedLookerTile, None, UnsetType] = UNSET + """Tile in which this field is used.""" + + look: Union[RelatedLookerLook, None, UnsetType] = UNSET + """Look in which this field is used.""" + + dashboard: Union[RelatedLookerDashboard, None, UnsetType] = UNSET + """Dashboard in which this field is used.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "LookerField" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _looker_field_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> LookerField: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + LookerField instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _looker_field_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class LookerFieldAttributes(AssetAttributes): + """LookerField-specific attributes for nested API format.""" + + project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which this field exists.""" + + looker_explore_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the Explore in which this field exists.""" + + looker_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this field exists.""" + + looker_tile_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the tile in which this field is used.""" + + looker_look_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the look in which this field is used.""" + + looker_dashboard_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dashboard in which this field is used.""" + + model_name: Union[str, None, UnsetType] = UNSET + """Name of the model in which this field exists.""" + + source_definition: Union[str, None, UnsetType] = UNSET + """Deprecated.""" + + looker_field_data_type: Union[str, None, UnsetType] = UNSET + """Deprecated.""" + + looker_times_used: Union[int, None, UnsetType] = UNSET + """Deprecated.""" + + looker_field_is_refined: Union[bool, None, UnsetType] = UNSET + """Whether the looker field asset is coming from a refinement""" + + looker_field_refinement_file_path: Union[str, None, UnsetType] = UNSET + """Absolute path of the file where the refinement of the field is declared.""" + + looker_field_refinement_line_number: Union[str, None, UnsetType] = UNSET + """Line number in the lookerFieldRefinementFilePath where this refinement of the field is declared.""" + + looker_slug: Union[str, None, UnsetType] = UNSET + """An alpha-numeric slug for the underlying Looker asset that can be used to uniquely identify it""" + + +class LookerFieldRelationshipAttributes(AssetRelationshipAttributes): + """LookerField-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + model: Union[RelatedLookerModel, None, UnsetType] = UNSET + """Model in which this field exists.""" + + explore: Union[RelatedLookerExplore, None, UnsetType] = UNSET + """Explore in which this field exists.""" + + project: Union[RelatedLookerProject, None, UnsetType] = UNSET + """Project in which this field exists.""" + + view: Union[RelatedLookerView, None, UnsetType] = UNSET + """View in which this field exists.""" + + tile: Union[RelatedLookerTile, None, UnsetType] = UNSET + """Tile in which this field is used.""" + + look: Union[RelatedLookerLook, None, UnsetType] = UNSET + """Look in which this field is used.""" + + dashboard: Union[RelatedLookerDashboard, None, UnsetType] = UNSET + """Dashboard in which this field is used.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class LookerFieldNested(AssetNested): + """LookerField in nested API format for high-performance serialization.""" + + attributes: Union[LookerFieldAttributes, UnsetType] = UNSET + relationship_attributes: Union[LookerFieldRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + LookerFieldRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + LookerFieldRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_LOOKER_FIELD_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "model", + "explore", + "project", + "view", + "tile", + "look", + "dashboard", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_looker_field_attrs( + attrs: LookerFieldAttributes, obj: LookerField +) -> None: + """Populate LookerField-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.project_name = obj.project_name + attrs.looker_explore_qualified_name = obj.looker_explore_qualified_name + attrs.looker_view_qualified_name = obj.looker_view_qualified_name + attrs.looker_tile_qualified_name = obj.looker_tile_qualified_name + attrs.looker_look_qualified_name = obj.looker_look_qualified_name + attrs.looker_dashboard_qualified_name = obj.looker_dashboard_qualified_name + attrs.model_name = obj.model_name + attrs.source_definition = obj.source_definition + attrs.looker_field_data_type = obj.looker_field_data_type + attrs.looker_times_used = obj.looker_times_used + attrs.looker_field_is_refined = obj.looker_field_is_refined + attrs.looker_field_refinement_file_path = obj.looker_field_refinement_file_path + attrs.looker_field_refinement_line_number = obj.looker_field_refinement_line_number + attrs.looker_slug = obj.looker_slug + + +def _extract_looker_field_attrs(attrs: LookerFieldAttributes) -> dict: + """Extract all LookerField attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["project_name"] = attrs.project_name + result["looker_explore_qualified_name"] = attrs.looker_explore_qualified_name + result["looker_view_qualified_name"] = attrs.looker_view_qualified_name + result["looker_tile_qualified_name"] = attrs.looker_tile_qualified_name + result["looker_look_qualified_name"] = attrs.looker_look_qualified_name + result["looker_dashboard_qualified_name"] = attrs.looker_dashboard_qualified_name + result["model_name"] = attrs.model_name + result["source_definition"] = attrs.source_definition + result["looker_field_data_type"] = attrs.looker_field_data_type + result["looker_times_used"] = attrs.looker_times_used + result["looker_field_is_refined"] = attrs.looker_field_is_refined + result["looker_field_refinement_file_path"] = ( + attrs.looker_field_refinement_file_path + ) + result["looker_field_refinement_line_number"] = ( + attrs.looker_field_refinement_line_number + ) + result["looker_slug"] = attrs.looker_slug + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _looker_field_to_nested(looker_field: LookerField) -> LookerFieldNested: + """Convert flat LookerField to nested format.""" + attrs = LookerFieldAttributes() + _populate_looker_field_attrs(attrs, looker_field) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + looker_field, _LOOKER_FIELD_REL_FIELDS, LookerFieldRelationshipAttributes + ) + return LookerFieldNested( + guid=looker_field.guid, + type_name=looker_field.type_name, + status=looker_field.status, + version=looker_field.version, + create_time=looker_field.create_time, + update_time=looker_field.update_time, + created_by=looker_field.created_by, + updated_by=looker_field.updated_by, + classifications=looker_field.classifications, + classification_names=looker_field.classification_names, + meanings=looker_field.meanings, + labels=looker_field.labels, + business_attributes=looker_field.business_attributes, + custom_attributes=looker_field.custom_attributes, + pending_tasks=looker_field.pending_tasks, + proxy=looker_field.proxy, + is_incomplete=looker_field.is_incomplete, + provenance_type=looker_field.provenance_type, + home_id=looker_field.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _looker_field_from_nested(nested: LookerFieldNested) -> LookerField: + """Convert nested format to flat LookerField.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else LookerFieldAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _LOOKER_FIELD_REL_FIELDS, + LookerFieldRelationshipAttributes, + ) + return LookerField( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_looker_field_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _looker_field_to_nested_bytes(looker_field: LookerField, serde: Serde) -> bytes: + """Convert flat LookerField to nested JSON bytes.""" + return serde.encode(_looker_field_to_nested(looker_field)) + + +def _looker_field_from_nested_bytes(data: bytes, serde: Serde) -> LookerField: + """Convert nested JSON bytes to flat LookerField.""" + nested = serde.decode(data, LookerFieldNested) + return _looker_field_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +LookerField.PROJECT_NAME = KeywordField("projectName", "projectName") +LookerField.LOOKER_EXPLORE_QUALIFIED_NAME = KeywordTextField( + "lookerExploreQualifiedName", + "lookerExploreQualifiedName", + "lookerExploreQualifiedName.text", +) +LookerField.LOOKER_VIEW_QUALIFIED_NAME = KeywordTextField( + "lookerViewQualifiedName", "lookerViewQualifiedName", "lookerViewQualifiedName.text" +) +LookerField.LOOKER_TILE_QUALIFIED_NAME = KeywordTextField( + "lookerTileQualifiedName", "lookerTileQualifiedName", "lookerTileQualifiedName.text" +) +LookerField.LOOKER_LOOK_QUALIFIED_NAME = KeywordTextField( + "lookerLookQualifiedName", "lookerLookQualifiedName", "lookerLookQualifiedName.text" +) +LookerField.LOOKER_DASHBOARD_QUALIFIED_NAME = KeywordTextField( + "lookerDashboardQualifiedName", + "lookerDashboardQualifiedName", + "lookerDashboardQualifiedName.text", +) +LookerField.MODEL_NAME = KeywordField("modelName", "modelName") +LookerField.SOURCE_DEFINITION = KeywordField("sourceDefinition", "sourceDefinition") +LookerField.LOOKER_FIELD_DATA_TYPE = KeywordField( + "lookerFieldDataType", "lookerFieldDataType" +) +LookerField.LOOKER_TIMES_USED = NumericField("lookerTimesUsed", "lookerTimesUsed") +LookerField.LOOKER_FIELD_IS_REFINED = BooleanField( + "lookerFieldIsRefined", "lookerFieldIsRefined" +) +LookerField.LOOKER_FIELD_REFINEMENT_FILE_PATH = KeywordField( + "lookerFieldRefinementFilePath", "lookerFieldRefinementFilePath" +) +LookerField.LOOKER_FIELD_REFINEMENT_LINE_NUMBER = KeywordField( + "lookerFieldRefinementLineNumber", "lookerFieldRefinementLineNumber" +) +LookerField.LOOKER_SLUG = KeywordField("lookerSlug", "lookerSlug") +LookerField.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +LookerField.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +LookerField.ANOMALO_CHECKS = RelationField("anomaloChecks") +LookerField.APPLICATION = RelationField("application") +LookerField.APPLICATION_FIELD = RelationField("applicationField") +LookerField.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +LookerField.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +LookerField.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +LookerField.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +LookerField.METRICS = RelationField("metrics") +LookerField.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +LookerField.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +LookerField.MEANINGS = RelationField("meanings") +LookerField.MODEL = RelationField("model") +LookerField.EXPLORE = RelationField("explore") +LookerField.PROJECT = RelationField("project") +LookerField.VIEW = RelationField("view") +LookerField.TILE = RelationField("tile") +LookerField.LOOK = RelationField("look") +LookerField.DASHBOARD = RelationField("dashboard") +LookerField.MC_MONITORS = RelationField("mcMonitors") +LookerField.MC_INCIDENTS = RelationField("mcIncidents") +LookerField.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +LookerField.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +LookerField.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +LookerField.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +LookerField.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +LookerField.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +LookerField.FILES = RelationField("files") +LookerField.LINKS = RelationField("links") +LookerField.README = RelationField("readme") +LookerField.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +LookerField.SODA_CHECKS = RelationField("sodaChecks") +LookerField.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +LookerField.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/looker_folder.py b/pyatlan_v9/model/assets/looker_folder.py new file mode 100644 index 000000000..163ed3728 --- /dev/null +++ b/pyatlan_v9/model/assets/looker_folder.py @@ -0,0 +1,633 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +LookerFolder asset model with flattened inheritance. + +This module provides: +- LookerFolder: Flat asset class (easy to use) +- LookerFolderAttributes: Nested attributes struct (extends AssetAttributes) +- LookerFolderNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .looker_related import ( + RelatedLookerDashboard, + RelatedLookerFolder, + RelatedLookerLook, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class LookerFolder(Asset): + """ + Instance of a Looker folder in Atlan. Folders in Looker are used to organize content in a hierarchical structure and granting access. + """ + + SOURCE_CONTENT_METADATA_ID: ClassVar[Any] = None + SOURCE_CREATOR_ID: ClassVar[Any] = None + SOURCE_CHILD_COUNT: ClassVar[Any] = None + SOURCE_PARENT_ID: ClassVar[Any] = None + LOOKER_SLUG: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + DASHBOARDS: ClassVar[Any] = None + LOOKS: ClassVar[Any] = None + LOOKER_SUB_FOLDERS: ClassVar[Any] = None + LOOKER_PARENT_FOLDER: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "LookerFolder" + + source_content_metadata_id: Union[int, None, UnsetType] = UNSET + """Identifier for the folder's content metadata in Looker.""" + + source_creator_id: Union[int, None, UnsetType] = UNSET + """Identifier of the user who created the folder, from Looker.""" + + source_child_count: Union[int, None, UnsetType] = UNSET + """Number of subfolders in this folder.""" + + source_parent_id: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="sourceParentID" + ) + """Identifier of the parent folder of this folder, from Looker.""" + + looker_slug: Union[str, None, UnsetType] = UNSET + """An alpha-numeric slug for the underlying Looker asset that can be used to uniquely identify it""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + dashboards: Union[List[RelatedLookerDashboard], None, UnsetType] = UNSET + """Dashboards that exist within this folder.""" + + looks: Union[List[RelatedLookerLook], None, UnsetType] = UNSET + """Looks that exist within this folder.""" + + looker_sub_folders: Union[List[RelatedLookerFolder], None, UnsetType] = UNSET + """Subfolders that exist within this folder.""" + + looker_parent_folder: Union[RelatedLookerFolder, None, UnsetType] = UNSET + """Folder in which this subfolder exists.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "LookerFolder" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _looker_folder_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> LookerFolder: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + LookerFolder instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _looker_folder_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class LookerFolderAttributes(AssetAttributes): + """LookerFolder-specific attributes for nested API format.""" + + source_content_metadata_id: Union[int, None, UnsetType] = UNSET + """Identifier for the folder's content metadata in Looker.""" + + source_creator_id: Union[int, None, UnsetType] = UNSET + """Identifier of the user who created the folder, from Looker.""" + + source_child_count: Union[int, None, UnsetType] = UNSET + """Number of subfolders in this folder.""" + + source_parent_id: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="sourceParentID" + ) + """Identifier of the parent folder of this folder, from Looker.""" + + looker_slug: Union[str, None, UnsetType] = UNSET + """An alpha-numeric slug for the underlying Looker asset that can be used to uniquely identify it""" + + +class LookerFolderRelationshipAttributes(AssetRelationshipAttributes): + """LookerFolder-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + dashboards: Union[List[RelatedLookerDashboard], None, UnsetType] = UNSET + """Dashboards that exist within this folder.""" + + looks: Union[List[RelatedLookerLook], None, UnsetType] = UNSET + """Looks that exist within this folder.""" + + looker_sub_folders: Union[List[RelatedLookerFolder], None, UnsetType] = UNSET + """Subfolders that exist within this folder.""" + + looker_parent_folder: Union[RelatedLookerFolder, None, UnsetType] = UNSET + """Folder in which this subfolder exists.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class LookerFolderNested(AssetNested): + """LookerFolder in nested API format for high-performance serialization.""" + + attributes: Union[LookerFolderAttributes, UnsetType] = UNSET + relationship_attributes: Union[LookerFolderRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + LookerFolderRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + LookerFolderRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_LOOKER_FOLDER_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "dashboards", + "looks", + "looker_sub_folders", + "looker_parent_folder", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_looker_folder_attrs( + attrs: LookerFolderAttributes, obj: LookerFolder +) -> None: + """Populate LookerFolder-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.source_content_metadata_id = obj.source_content_metadata_id + attrs.source_creator_id = obj.source_creator_id + attrs.source_child_count = obj.source_child_count + attrs.source_parent_id = obj.source_parent_id + attrs.looker_slug = obj.looker_slug + + +def _extract_looker_folder_attrs(attrs: LookerFolderAttributes) -> dict: + """Extract all LookerFolder attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["source_content_metadata_id"] = attrs.source_content_metadata_id + result["source_creator_id"] = attrs.source_creator_id + result["source_child_count"] = attrs.source_child_count + result["source_parent_id"] = attrs.source_parent_id + result["looker_slug"] = attrs.looker_slug + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _looker_folder_to_nested(looker_folder: LookerFolder) -> LookerFolderNested: + """Convert flat LookerFolder to nested format.""" + attrs = LookerFolderAttributes() + _populate_looker_folder_attrs(attrs, looker_folder) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + looker_folder, _LOOKER_FOLDER_REL_FIELDS, LookerFolderRelationshipAttributes + ) + return LookerFolderNested( + guid=looker_folder.guid, + type_name=looker_folder.type_name, + status=looker_folder.status, + version=looker_folder.version, + create_time=looker_folder.create_time, + update_time=looker_folder.update_time, + created_by=looker_folder.created_by, + updated_by=looker_folder.updated_by, + classifications=looker_folder.classifications, + classification_names=looker_folder.classification_names, + meanings=looker_folder.meanings, + labels=looker_folder.labels, + business_attributes=looker_folder.business_attributes, + custom_attributes=looker_folder.custom_attributes, + pending_tasks=looker_folder.pending_tasks, + proxy=looker_folder.proxy, + is_incomplete=looker_folder.is_incomplete, + provenance_type=looker_folder.provenance_type, + home_id=looker_folder.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _looker_folder_from_nested(nested: LookerFolderNested) -> LookerFolder: + """Convert nested format to flat LookerFolder.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else LookerFolderAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _LOOKER_FOLDER_REL_FIELDS, + LookerFolderRelationshipAttributes, + ) + return LookerFolder( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_looker_folder_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _looker_folder_to_nested_bytes(looker_folder: LookerFolder, serde: Serde) -> bytes: + """Convert flat LookerFolder to nested JSON bytes.""" + return serde.encode(_looker_folder_to_nested(looker_folder)) + + +def _looker_folder_from_nested_bytes(data: bytes, serde: Serde) -> LookerFolder: + """Convert nested JSON bytes to flat LookerFolder.""" + nested = serde.decode(data, LookerFolderNested) + return _looker_folder_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +LookerFolder.SOURCE_CONTENT_METADATA_ID = NumericField( + "sourceContentMetadataId", "sourceContentMetadataId" +) +LookerFolder.SOURCE_CREATOR_ID = NumericField("sourceCreatorId", "sourceCreatorId") +LookerFolder.SOURCE_CHILD_COUNT = NumericField("sourceChildCount", "sourceChildCount") +LookerFolder.SOURCE_PARENT_ID = NumericField("sourceParentID", "sourceParentID") +LookerFolder.LOOKER_SLUG = KeywordField("lookerSlug", "lookerSlug") +LookerFolder.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +LookerFolder.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +LookerFolder.ANOMALO_CHECKS = RelationField("anomaloChecks") +LookerFolder.APPLICATION = RelationField("application") +LookerFolder.APPLICATION_FIELD = RelationField("applicationField") +LookerFolder.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +LookerFolder.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +LookerFolder.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +LookerFolder.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +LookerFolder.METRICS = RelationField("metrics") +LookerFolder.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +LookerFolder.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +LookerFolder.MEANINGS = RelationField("meanings") +LookerFolder.DASHBOARDS = RelationField("dashboards") +LookerFolder.LOOKS = RelationField("looks") +LookerFolder.LOOKER_SUB_FOLDERS = RelationField("lookerSubFolders") +LookerFolder.LOOKER_PARENT_FOLDER = RelationField("lookerParentFolder") +LookerFolder.MC_MONITORS = RelationField("mcMonitors") +LookerFolder.MC_INCIDENTS = RelationField("mcIncidents") +LookerFolder.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +LookerFolder.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +LookerFolder.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +LookerFolder.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +LookerFolder.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +LookerFolder.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +LookerFolder.FILES = RelationField("files") +LookerFolder.LINKS = RelationField("links") +LookerFolder.README = RelationField("readme") +LookerFolder.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +LookerFolder.SODA_CHECKS = RelationField("sodaChecks") +LookerFolder.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +LookerFolder.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/looker_look.py b/pyatlan_v9/model/assets/looker_look.py new file mode 100644 index 000000000..cf11726ec --- /dev/null +++ b/pyatlan_v9/model/assets/looker_look.py @@ -0,0 +1,718 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +LookerLook asset model with flattened inheritance. + +This module provides: +- LookerLook: Flat asset class (easy to use) +- LookerLookAttributes: Nested attributes struct (extends AssetAttributes) +- LookerLookNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .looker_related import ( + RelatedLookerDashboard, + RelatedLookerField, + RelatedLookerFolder, + RelatedLookerModel, + RelatedLookerQuery, + RelatedLookerTile, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class LookerLook(Asset): + """ + Instance of a Looker Look in Atlan. Looks are saved visualizations used to understand and analyze data. They can be shared and reused in multiple dashboards. + """ + + FOLDER_NAME: ClassVar[Any] = None + SOURCE_USER_ID: ClassVar[Any] = None + SOURCE_VIEW_COUNT: ClassVar[Any] = None + SOURCELAST_UPDATER_ID: ClassVar[Any] = None + SOURCE_LAST_ACCESSED_AT: ClassVar[Any] = None + SOURCE_LAST_VIEWED_AT: ClassVar[Any] = None + SOURCE_CONTENT_METADATA_ID: ClassVar[Any] = None + SOURCE_QUERY_ID: ClassVar[Any] = None + LOOKER_SOURCE_QUERY_ID: ClassVar[Any] = None + MODEL_NAME: ClassVar[Any] = None + LOOKER_SLUG: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + FOLDER: ClassVar[Any] = None + DASHBOARD: ClassVar[Any] = None + MODEL: ClassVar[Any] = None + TILE: ClassVar[Any] = None + QUERY: ClassVar[Any] = None + FIELDS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "LookerLook" + + folder_name: Union[str, None, UnsetType] = UNSET + """Name of the folder in which the Look is organized.""" + + source_user_id: Union[int, None, UnsetType] = UNSET + """Identifier of the user who created the Look, from Looker.""" + + source_view_count: Union[int, None, UnsetType] = UNSET + """Number of times the look has been viewed in the Looker web UI.""" + + sourcelast_updater_id: Union[int, None, UnsetType] = UNSET + """Identifier of the user that last updated the Look, from Looker.""" + + source_last_accessed_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) when the Look was last accessed by a user, in milliseconds.""" + + source_last_viewed_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) when the Look was last viewed by a user, in milliseconds.""" + + source_content_metadata_id: Union[int, None, UnsetType] = UNSET + """Identifier of the Look's content metadata, from Looker.""" + + source_query_id: Union[int, None, UnsetType] = UNSET + """(Deprecated) Please use lookerSourceQueryId instead.""" + + looker_source_query_id: Union[str, None, UnsetType] = UNSET + """Identifier of the query for the Look, from Looker.""" + + model_name: Union[str, None, UnsetType] = UNSET + """Name of the model in which this Look exists.""" + + looker_slug: Union[str, None, UnsetType] = UNSET + """An alpha-numeric slug for the underlying Looker asset that can be used to uniquely identify it""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + folder: Union[RelatedLookerFolder, None, UnsetType] = UNSET + """Folder in which this Look exists.""" + + dashboard: Union[RelatedLookerDashboard, None, UnsetType] = UNSET + """Dashboard in which this Look is used.""" + + model: Union[RelatedLookerModel, None, UnsetType] = UNSET + """Model in which this Look exists.""" + + tile: Union[RelatedLookerTile, None, UnsetType] = UNSET + """Tiles that exist within this Look.""" + + query: Union[RelatedLookerQuery, None, UnsetType] = UNSET + """Deprecated.""" + + fields: Union[List[RelatedLookerField], None, UnsetType] = UNSET + """Fields that are used in this look.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "LookerLook" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _looker_look_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> LookerLook: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + LookerLook instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _looker_look_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class LookerLookAttributes(AssetAttributes): + """LookerLook-specific attributes for nested API format.""" + + folder_name: Union[str, None, UnsetType] = UNSET + """Name of the folder in which the Look is organized.""" + + source_user_id: Union[int, None, UnsetType] = UNSET + """Identifier of the user who created the Look, from Looker.""" + + source_view_count: Union[int, None, UnsetType] = UNSET + """Number of times the look has been viewed in the Looker web UI.""" + + sourcelast_updater_id: Union[int, None, UnsetType] = UNSET + """Identifier of the user that last updated the Look, from Looker.""" + + source_last_accessed_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) when the Look was last accessed by a user, in milliseconds.""" + + source_last_viewed_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) when the Look was last viewed by a user, in milliseconds.""" + + source_content_metadata_id: Union[int, None, UnsetType] = UNSET + """Identifier of the Look's content metadata, from Looker.""" + + source_query_id: Union[int, None, UnsetType] = UNSET + """(Deprecated) Please use lookerSourceQueryId instead.""" + + looker_source_query_id: Union[str, None, UnsetType] = UNSET + """Identifier of the query for the Look, from Looker.""" + + model_name: Union[str, None, UnsetType] = UNSET + """Name of the model in which this Look exists.""" + + looker_slug: Union[str, None, UnsetType] = UNSET + """An alpha-numeric slug for the underlying Looker asset that can be used to uniquely identify it""" + + +class LookerLookRelationshipAttributes(AssetRelationshipAttributes): + """LookerLook-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + folder: Union[RelatedLookerFolder, None, UnsetType] = UNSET + """Folder in which this Look exists.""" + + dashboard: Union[RelatedLookerDashboard, None, UnsetType] = UNSET + """Dashboard in which this Look is used.""" + + model: Union[RelatedLookerModel, None, UnsetType] = UNSET + """Model in which this Look exists.""" + + tile: Union[RelatedLookerTile, None, UnsetType] = UNSET + """Tiles that exist within this Look.""" + + query: Union[RelatedLookerQuery, None, UnsetType] = UNSET + """Deprecated.""" + + fields: Union[List[RelatedLookerField], None, UnsetType] = UNSET + """Fields that are used in this look.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class LookerLookNested(AssetNested): + """LookerLook in nested API format for high-performance serialization.""" + + attributes: Union[LookerLookAttributes, UnsetType] = UNSET + relationship_attributes: Union[LookerLookRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + LookerLookRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + LookerLookRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_LOOKER_LOOK_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "folder", + "dashboard", + "model", + "tile", + "query", + "fields", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_looker_look_attrs(attrs: LookerLookAttributes, obj: LookerLook) -> None: + """Populate LookerLook-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.folder_name = obj.folder_name + attrs.source_user_id = obj.source_user_id + attrs.source_view_count = obj.source_view_count + attrs.sourcelast_updater_id = obj.sourcelast_updater_id + attrs.source_last_accessed_at = obj.source_last_accessed_at + attrs.source_last_viewed_at = obj.source_last_viewed_at + attrs.source_content_metadata_id = obj.source_content_metadata_id + attrs.source_query_id = obj.source_query_id + attrs.looker_source_query_id = obj.looker_source_query_id + attrs.model_name = obj.model_name + attrs.looker_slug = obj.looker_slug + + +def _extract_looker_look_attrs(attrs: LookerLookAttributes) -> dict: + """Extract all LookerLook attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["folder_name"] = attrs.folder_name + result["source_user_id"] = attrs.source_user_id + result["source_view_count"] = attrs.source_view_count + result["sourcelast_updater_id"] = attrs.sourcelast_updater_id + result["source_last_accessed_at"] = attrs.source_last_accessed_at + result["source_last_viewed_at"] = attrs.source_last_viewed_at + result["source_content_metadata_id"] = attrs.source_content_metadata_id + result["source_query_id"] = attrs.source_query_id + result["looker_source_query_id"] = attrs.looker_source_query_id + result["model_name"] = attrs.model_name + result["looker_slug"] = attrs.looker_slug + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _looker_look_to_nested(looker_look: LookerLook) -> LookerLookNested: + """Convert flat LookerLook to nested format.""" + attrs = LookerLookAttributes() + _populate_looker_look_attrs(attrs, looker_look) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + looker_look, _LOOKER_LOOK_REL_FIELDS, LookerLookRelationshipAttributes + ) + return LookerLookNested( + guid=looker_look.guid, + type_name=looker_look.type_name, + status=looker_look.status, + version=looker_look.version, + create_time=looker_look.create_time, + update_time=looker_look.update_time, + created_by=looker_look.created_by, + updated_by=looker_look.updated_by, + classifications=looker_look.classifications, + classification_names=looker_look.classification_names, + meanings=looker_look.meanings, + labels=looker_look.labels, + business_attributes=looker_look.business_attributes, + custom_attributes=looker_look.custom_attributes, + pending_tasks=looker_look.pending_tasks, + proxy=looker_look.proxy, + is_incomplete=looker_look.is_incomplete, + provenance_type=looker_look.provenance_type, + home_id=looker_look.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _looker_look_from_nested(nested: LookerLookNested) -> LookerLook: + """Convert nested format to flat LookerLook.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else LookerLookAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _LOOKER_LOOK_REL_FIELDS, + LookerLookRelationshipAttributes, + ) + return LookerLook( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_looker_look_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _looker_look_to_nested_bytes(looker_look: LookerLook, serde: Serde) -> bytes: + """Convert flat LookerLook to nested JSON bytes.""" + return serde.encode(_looker_look_to_nested(looker_look)) + + +def _looker_look_from_nested_bytes(data: bytes, serde: Serde) -> LookerLook: + """Convert nested JSON bytes to flat LookerLook.""" + nested = serde.decode(data, LookerLookNested) + return _looker_look_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +LookerLook.FOLDER_NAME = KeywordField("folderName", "folderName") +LookerLook.SOURCE_USER_ID = NumericField("sourceUserId", "sourceUserId") +LookerLook.SOURCE_VIEW_COUNT = NumericField("sourceViewCount", "sourceViewCount") +LookerLook.SOURCELAST_UPDATER_ID = NumericField( + "sourcelastUpdaterId", "sourcelastUpdaterId" +) +LookerLook.SOURCE_LAST_ACCESSED_AT = NumericField( + "sourceLastAccessedAt", "sourceLastAccessedAt" +) +LookerLook.SOURCE_LAST_VIEWED_AT = NumericField( + "sourceLastViewedAt", "sourceLastViewedAt" +) +LookerLook.SOURCE_CONTENT_METADATA_ID = NumericField( + "sourceContentMetadataId", "sourceContentMetadataId" +) +LookerLook.SOURCE_QUERY_ID = NumericField("sourceQueryId", "sourceQueryId") +LookerLook.LOOKER_SOURCE_QUERY_ID = KeywordField( + "lookerSourceQueryId", "lookerSourceQueryId" +) +LookerLook.MODEL_NAME = KeywordField("modelName", "modelName") +LookerLook.LOOKER_SLUG = KeywordField("lookerSlug", "lookerSlug") +LookerLook.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +LookerLook.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +LookerLook.ANOMALO_CHECKS = RelationField("anomaloChecks") +LookerLook.APPLICATION = RelationField("application") +LookerLook.APPLICATION_FIELD = RelationField("applicationField") +LookerLook.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +LookerLook.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +LookerLook.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +LookerLook.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +LookerLook.METRICS = RelationField("metrics") +LookerLook.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +LookerLook.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +LookerLook.MEANINGS = RelationField("meanings") +LookerLook.FOLDER = RelationField("folder") +LookerLook.DASHBOARD = RelationField("dashboard") +LookerLook.MODEL = RelationField("model") +LookerLook.TILE = RelationField("tile") +LookerLook.QUERY = RelationField("query") +LookerLook.FIELDS = RelationField("fields") +LookerLook.MC_MONITORS = RelationField("mcMonitors") +LookerLook.MC_INCIDENTS = RelationField("mcIncidents") +LookerLook.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +LookerLook.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +LookerLook.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +LookerLook.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +LookerLook.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +LookerLook.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +LookerLook.FILES = RelationField("files") +LookerLook.LINKS = RelationField("links") +LookerLook.README = RelationField("readme") +LookerLook.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +LookerLook.SODA_CHECKS = RelationField("sodaChecks") +LookerLook.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +LookerLook.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/looker_model.py b/pyatlan_v9/model/assets/looker_model.py new file mode 100644 index 000000000..c19848333 --- /dev/null +++ b/pyatlan_v9/model/assets/looker_model.py @@ -0,0 +1,609 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +LookerModel asset model with flattened inheritance. + +This module provides: +- LookerModel: Flat asset class (easy to use) +- LookerModelAttributes: Nested attributes struct (extends AssetAttributes) +- LookerModelNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .looker_related import ( + RelatedLookerExplore, + RelatedLookerField, + RelatedLookerLook, + RelatedLookerProject, + RelatedLookerQuery, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class LookerModel(Asset): + """ + Instance of a Looker model in Atlan. Models combine Explores and dashboards. + """ + + PROJECT_NAME: ClassVar[Any] = None + LOOKER_SLUG: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + PROJECT: ClassVar[Any] = None + EXPLORES: ClassVar[Any] = None + LOOK: ClassVar[Any] = None + QUERIES: ClassVar[Any] = None + FIELDS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "LookerModel" + + project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which the model exists.""" + + looker_slug: Union[str, None, UnsetType] = UNSET + """An alpha-numeric slug for the underlying Looker asset that can be used to uniquely identify it""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + project: Union[RelatedLookerProject, None, UnsetType] = UNSET + """Project in which this model exists.""" + + explores: Union[List[RelatedLookerExplore], None, UnsetType] = UNSET + """Explores that exist within this model.""" + + look: Union[RelatedLookerLook, None, UnsetType] = UNSET + """Look that exists for this model.""" + + queries: Union[List[RelatedLookerQuery], None, UnsetType] = UNSET + """Deprecated.""" + + fields: Union[List[RelatedLookerField], None, UnsetType] = UNSET + """Fields that exist within this model.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "LookerModel" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _looker_model_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> LookerModel: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + LookerModel instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _looker_model_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class LookerModelAttributes(AssetAttributes): + """LookerModel-specific attributes for nested API format.""" + + project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which the model exists.""" + + looker_slug: Union[str, None, UnsetType] = UNSET + """An alpha-numeric slug for the underlying Looker asset that can be used to uniquely identify it""" + + +class LookerModelRelationshipAttributes(AssetRelationshipAttributes): + """LookerModel-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + project: Union[RelatedLookerProject, None, UnsetType] = UNSET + """Project in which this model exists.""" + + explores: Union[List[RelatedLookerExplore], None, UnsetType] = UNSET + """Explores that exist within this model.""" + + look: Union[RelatedLookerLook, None, UnsetType] = UNSET + """Look that exists for this model.""" + + queries: Union[List[RelatedLookerQuery], None, UnsetType] = UNSET + """Deprecated.""" + + fields: Union[List[RelatedLookerField], None, UnsetType] = UNSET + """Fields that exist within this model.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class LookerModelNested(AssetNested): + """LookerModel in nested API format for high-performance serialization.""" + + attributes: Union[LookerModelAttributes, UnsetType] = UNSET + relationship_attributes: Union[LookerModelRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + LookerModelRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + LookerModelRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_LOOKER_MODEL_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "project", + "explores", + "look", + "queries", + "fields", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_looker_model_attrs( + attrs: LookerModelAttributes, obj: LookerModel +) -> None: + """Populate LookerModel-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.project_name = obj.project_name + attrs.looker_slug = obj.looker_slug + + +def _extract_looker_model_attrs(attrs: LookerModelAttributes) -> dict: + """Extract all LookerModel attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["project_name"] = attrs.project_name + result["looker_slug"] = attrs.looker_slug + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _looker_model_to_nested(looker_model: LookerModel) -> LookerModelNested: + """Convert flat LookerModel to nested format.""" + attrs = LookerModelAttributes() + _populate_looker_model_attrs(attrs, looker_model) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + looker_model, _LOOKER_MODEL_REL_FIELDS, LookerModelRelationshipAttributes + ) + return LookerModelNested( + guid=looker_model.guid, + type_name=looker_model.type_name, + status=looker_model.status, + version=looker_model.version, + create_time=looker_model.create_time, + update_time=looker_model.update_time, + created_by=looker_model.created_by, + updated_by=looker_model.updated_by, + classifications=looker_model.classifications, + classification_names=looker_model.classification_names, + meanings=looker_model.meanings, + labels=looker_model.labels, + business_attributes=looker_model.business_attributes, + custom_attributes=looker_model.custom_attributes, + pending_tasks=looker_model.pending_tasks, + proxy=looker_model.proxy, + is_incomplete=looker_model.is_incomplete, + provenance_type=looker_model.provenance_type, + home_id=looker_model.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _looker_model_from_nested(nested: LookerModelNested) -> LookerModel: + """Convert nested format to flat LookerModel.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else LookerModelAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _LOOKER_MODEL_REL_FIELDS, + LookerModelRelationshipAttributes, + ) + return LookerModel( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_looker_model_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _looker_model_to_nested_bytes(looker_model: LookerModel, serde: Serde) -> bytes: + """Convert flat LookerModel to nested JSON bytes.""" + return serde.encode(_looker_model_to_nested(looker_model)) + + +def _looker_model_from_nested_bytes(data: bytes, serde: Serde) -> LookerModel: + """Convert nested JSON bytes to flat LookerModel.""" + nested = serde.decode(data, LookerModelNested) + return _looker_model_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +LookerModel.PROJECT_NAME = KeywordField("projectName", "projectName") +LookerModel.LOOKER_SLUG = KeywordField("lookerSlug", "lookerSlug") +LookerModel.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +LookerModel.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +LookerModel.ANOMALO_CHECKS = RelationField("anomaloChecks") +LookerModel.APPLICATION = RelationField("application") +LookerModel.APPLICATION_FIELD = RelationField("applicationField") +LookerModel.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +LookerModel.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +LookerModel.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +LookerModel.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +LookerModel.METRICS = RelationField("metrics") +LookerModel.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +LookerModel.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +LookerModel.MEANINGS = RelationField("meanings") +LookerModel.PROJECT = RelationField("project") +LookerModel.EXPLORES = RelationField("explores") +LookerModel.LOOK = RelationField("look") +LookerModel.QUERIES = RelationField("queries") +LookerModel.FIELDS = RelationField("fields") +LookerModel.MC_MONITORS = RelationField("mcMonitors") +LookerModel.MC_INCIDENTS = RelationField("mcIncidents") +LookerModel.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +LookerModel.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +LookerModel.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +LookerModel.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +LookerModel.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +LookerModel.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +LookerModel.FILES = RelationField("files") +LookerModel.LINKS = RelationField("links") +LookerModel.README = RelationField("readme") +LookerModel.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +LookerModel.SODA_CHECKS = RelationField("sodaChecks") +LookerModel.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +LookerModel.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/looker_project.py b/pyatlan_v9/model/assets/looker_project.py new file mode 100644 index 000000000..f8e023500 --- /dev/null +++ b/pyatlan_v9/model/assets/looker_project.py @@ -0,0 +1,607 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +LookerProject asset model with flattened inheritance. + +This module provides: +- LookerProject: Flat asset class (easy to use) +- LookerProjectAttributes: Nested attributes struct (extends AssetAttributes) +- LookerProjectNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .looker_related import ( + RelatedLookerExplore, + RelatedLookerField, + RelatedLookerModel, + RelatedLookerProject, + RelatedLookerView, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class LookerProject(Asset): + """ + Instance of a Looker project in Atlan. Projects are a collection of files that describe the objects, connections and user interface elements in Looker. + """ + + LOOKER_SLUG: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MODELS: ClassVar[Any] = None + LOOKER_CHILD_PROJECTS: ClassVar[Any] = None + LOOKER_PARENT_PROJECTS: ClassVar[Any] = None + EXPLORES: ClassVar[Any] = None + VIEWS: ClassVar[Any] = None + FIELDS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "LookerProject" + + looker_slug: Union[str, None, UnsetType] = UNSET + """An alpha-numeric slug for the underlying Looker asset that can be used to uniquely identify it""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + models: Union[List[RelatedLookerModel], None, UnsetType] = UNSET + """Models that exist within this project.""" + + looker_child_projects: Union[List[RelatedLookerProject], None, UnsetType] = UNSET + """Child projects that exist within this project.""" + + looker_parent_projects: Union[List[RelatedLookerProject], None, UnsetType] = UNSET + """Projects in which this project exists.""" + + explores: Union[List[RelatedLookerExplore], None, UnsetType] = UNSET + """Explores that exist within this project.""" + + views: Union[List[RelatedLookerView], None, UnsetType] = UNSET + """Views that exist within this project.""" + + fields: Union[List[RelatedLookerField], None, UnsetType] = UNSET + """Fields that exist within this project.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "LookerProject" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _looker_project_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> LookerProject: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + LookerProject instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _looker_project_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class LookerProjectAttributes(AssetAttributes): + """LookerProject-specific attributes for nested API format.""" + + looker_slug: Union[str, None, UnsetType] = UNSET + """An alpha-numeric slug for the underlying Looker asset that can be used to uniquely identify it""" + + +class LookerProjectRelationshipAttributes(AssetRelationshipAttributes): + """LookerProject-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + models: Union[List[RelatedLookerModel], None, UnsetType] = UNSET + """Models that exist within this project.""" + + looker_child_projects: Union[List[RelatedLookerProject], None, UnsetType] = UNSET + """Child projects that exist within this project.""" + + looker_parent_projects: Union[List[RelatedLookerProject], None, UnsetType] = UNSET + """Projects in which this project exists.""" + + explores: Union[List[RelatedLookerExplore], None, UnsetType] = UNSET + """Explores that exist within this project.""" + + views: Union[List[RelatedLookerView], None, UnsetType] = UNSET + """Views that exist within this project.""" + + fields: Union[List[RelatedLookerField], None, UnsetType] = UNSET + """Fields that exist within this project.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class LookerProjectNested(AssetNested): + """LookerProject in nested API format for high-performance serialization.""" + + attributes: Union[LookerProjectAttributes, UnsetType] = UNSET + relationship_attributes: Union[LookerProjectRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + LookerProjectRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + LookerProjectRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_LOOKER_PROJECT_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "models", + "looker_child_projects", + "looker_parent_projects", + "explores", + "views", + "fields", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_looker_project_attrs( + attrs: LookerProjectAttributes, obj: LookerProject +) -> None: + """Populate LookerProject-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.looker_slug = obj.looker_slug + + +def _extract_looker_project_attrs(attrs: LookerProjectAttributes) -> dict: + """Extract all LookerProject attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["looker_slug"] = attrs.looker_slug + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _looker_project_to_nested(looker_project: LookerProject) -> LookerProjectNested: + """Convert flat LookerProject to nested format.""" + attrs = LookerProjectAttributes() + _populate_looker_project_attrs(attrs, looker_project) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + looker_project, _LOOKER_PROJECT_REL_FIELDS, LookerProjectRelationshipAttributes + ) + return LookerProjectNested( + guid=looker_project.guid, + type_name=looker_project.type_name, + status=looker_project.status, + version=looker_project.version, + create_time=looker_project.create_time, + update_time=looker_project.update_time, + created_by=looker_project.created_by, + updated_by=looker_project.updated_by, + classifications=looker_project.classifications, + classification_names=looker_project.classification_names, + meanings=looker_project.meanings, + labels=looker_project.labels, + business_attributes=looker_project.business_attributes, + custom_attributes=looker_project.custom_attributes, + pending_tasks=looker_project.pending_tasks, + proxy=looker_project.proxy, + is_incomplete=looker_project.is_incomplete, + provenance_type=looker_project.provenance_type, + home_id=looker_project.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _looker_project_from_nested(nested: LookerProjectNested) -> LookerProject: + """Convert nested format to flat LookerProject.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else LookerProjectAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _LOOKER_PROJECT_REL_FIELDS, + LookerProjectRelationshipAttributes, + ) + return LookerProject( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_looker_project_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _looker_project_to_nested_bytes( + looker_project: LookerProject, serde: Serde +) -> bytes: + """Convert flat LookerProject to nested JSON bytes.""" + return serde.encode(_looker_project_to_nested(looker_project)) + + +def _looker_project_from_nested_bytes(data: bytes, serde: Serde) -> LookerProject: + """Convert nested JSON bytes to flat LookerProject.""" + nested = serde.decode(data, LookerProjectNested) + return _looker_project_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +LookerProject.LOOKER_SLUG = KeywordField("lookerSlug", "lookerSlug") +LookerProject.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +LookerProject.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +LookerProject.ANOMALO_CHECKS = RelationField("anomaloChecks") +LookerProject.APPLICATION = RelationField("application") +LookerProject.APPLICATION_FIELD = RelationField("applicationField") +LookerProject.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +LookerProject.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +LookerProject.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +LookerProject.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +LookerProject.METRICS = RelationField("metrics") +LookerProject.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +LookerProject.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +LookerProject.MEANINGS = RelationField("meanings") +LookerProject.MODELS = RelationField("models") +LookerProject.LOOKER_CHILD_PROJECTS = RelationField("lookerChildProjects") +LookerProject.LOOKER_PARENT_PROJECTS = RelationField("lookerParentProjects") +LookerProject.EXPLORES = RelationField("explores") +LookerProject.VIEWS = RelationField("views") +LookerProject.FIELDS = RelationField("fields") +LookerProject.MC_MONITORS = RelationField("mcMonitors") +LookerProject.MC_INCIDENTS = RelationField("mcIncidents") +LookerProject.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +LookerProject.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +LookerProject.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +LookerProject.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +LookerProject.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +LookerProject.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +LookerProject.FILES = RelationField("files") +LookerProject.LINKS = RelationField("links") +LookerProject.README = RelationField("readme") +LookerProject.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +LookerProject.SODA_CHECKS = RelationField("sodaChecks") +LookerProject.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +LookerProject.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/looker_query.py b/pyatlan_v9/model/assets/looker_query.py new file mode 100644 index 000000000..f3864e2f6 --- /dev/null +++ b/pyatlan_v9/model/assets/looker_query.py @@ -0,0 +1,621 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +LookerQuery asset model with flattened inheritance. + +This module provides: +- LookerQuery: Flat asset class (easy to use) +- LookerQueryAttributes: Nested attributes struct (extends AssetAttributes) +- LookerQueryNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .looker_related import RelatedLookerLook, RelatedLookerModel, RelatedLookerTile + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class LookerQuery(Asset): + """ + Deprecated. + """ + + SOURCE_DEFINITION: ClassVar[Any] = None + SOURCE_DEFINITION_DATABASE: ClassVar[Any] = None + SOURCE_DEFINITION_SCHEMA: ClassVar[Any] = None + FIELDS: ClassVar[Any] = None + LOOKER_SLUG: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + TILES: ClassVar[Any] = None + LOOKS: ClassVar[Any] = None + MODEL: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "LookerQuery" + + source_definition: Union[str, None, UnsetType] = UNSET + """Deprecated.""" + + source_definition_database: Union[str, None, UnsetType] = UNSET + """Deprecated.""" + + source_definition_schema: Union[str, None, UnsetType] = UNSET + """Deprecated.""" + + fields: Union[List[str], None, UnsetType] = UNSET + """Deprecated.""" + + looker_slug: Union[str, None, UnsetType] = UNSET + """An alpha-numeric slug for the underlying Looker asset that can be used to uniquely identify it""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + tiles: Union[List[RelatedLookerTile], None, UnsetType] = UNSET + """Deprecated.""" + + looks: Union[List[RelatedLookerLook], None, UnsetType] = UNSET + """Deprecated.""" + + model: Union[RelatedLookerModel, None, UnsetType] = UNSET + """Deprecated.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "LookerQuery" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _looker_query_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> LookerQuery: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + LookerQuery instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _looker_query_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class LookerQueryAttributes(AssetAttributes): + """LookerQuery-specific attributes for nested API format.""" + + source_definition: Union[str, None, UnsetType] = UNSET + """Deprecated.""" + + source_definition_database: Union[str, None, UnsetType] = UNSET + """Deprecated.""" + + source_definition_schema: Union[str, None, UnsetType] = UNSET + """Deprecated.""" + + fields: Union[List[str], None, UnsetType] = UNSET + """Deprecated.""" + + looker_slug: Union[str, None, UnsetType] = UNSET + """An alpha-numeric slug for the underlying Looker asset that can be used to uniquely identify it""" + + +class LookerQueryRelationshipAttributes(AssetRelationshipAttributes): + """LookerQuery-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + tiles: Union[List[RelatedLookerTile], None, UnsetType] = UNSET + """Deprecated.""" + + looks: Union[List[RelatedLookerLook], None, UnsetType] = UNSET + """Deprecated.""" + + model: Union[RelatedLookerModel, None, UnsetType] = UNSET + """Deprecated.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class LookerQueryNested(AssetNested): + """LookerQuery in nested API format for high-performance serialization.""" + + attributes: Union[LookerQueryAttributes, UnsetType] = UNSET + relationship_attributes: Union[LookerQueryRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + LookerQueryRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + LookerQueryRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_LOOKER_QUERY_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "tiles", + "looks", + "model", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_looker_query_attrs( + attrs: LookerQueryAttributes, obj: LookerQuery +) -> None: + """Populate LookerQuery-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.source_definition = obj.source_definition + attrs.source_definition_database = obj.source_definition_database + attrs.source_definition_schema = obj.source_definition_schema + attrs.fields = obj.fields + attrs.looker_slug = obj.looker_slug + + +def _extract_looker_query_attrs(attrs: LookerQueryAttributes) -> dict: + """Extract all LookerQuery attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["source_definition"] = attrs.source_definition + result["source_definition_database"] = attrs.source_definition_database + result["source_definition_schema"] = attrs.source_definition_schema + result["fields"] = attrs.fields + result["looker_slug"] = attrs.looker_slug + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _looker_query_to_nested(looker_query: LookerQuery) -> LookerQueryNested: + """Convert flat LookerQuery to nested format.""" + attrs = LookerQueryAttributes() + _populate_looker_query_attrs(attrs, looker_query) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + looker_query, _LOOKER_QUERY_REL_FIELDS, LookerQueryRelationshipAttributes + ) + return LookerQueryNested( + guid=looker_query.guid, + type_name=looker_query.type_name, + status=looker_query.status, + version=looker_query.version, + create_time=looker_query.create_time, + update_time=looker_query.update_time, + created_by=looker_query.created_by, + updated_by=looker_query.updated_by, + classifications=looker_query.classifications, + classification_names=looker_query.classification_names, + meanings=looker_query.meanings, + labels=looker_query.labels, + business_attributes=looker_query.business_attributes, + custom_attributes=looker_query.custom_attributes, + pending_tasks=looker_query.pending_tasks, + proxy=looker_query.proxy, + is_incomplete=looker_query.is_incomplete, + provenance_type=looker_query.provenance_type, + home_id=looker_query.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _looker_query_from_nested(nested: LookerQueryNested) -> LookerQuery: + """Convert nested format to flat LookerQuery.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else LookerQueryAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _LOOKER_QUERY_REL_FIELDS, + LookerQueryRelationshipAttributes, + ) + return LookerQuery( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_looker_query_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _looker_query_to_nested_bytes(looker_query: LookerQuery, serde: Serde) -> bytes: + """Convert flat LookerQuery to nested JSON bytes.""" + return serde.encode(_looker_query_to_nested(looker_query)) + + +def _looker_query_from_nested_bytes(data: bytes, serde: Serde) -> LookerQuery: + """Convert nested JSON bytes to flat LookerQuery.""" + nested = serde.decode(data, LookerQueryNested) + return _looker_query_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +LookerQuery.SOURCE_DEFINITION = KeywordField("sourceDefinition", "sourceDefinition") +LookerQuery.SOURCE_DEFINITION_DATABASE = KeywordField( + "sourceDefinitionDatabase", "sourceDefinitionDatabase" +) +LookerQuery.SOURCE_DEFINITION_SCHEMA = KeywordField( + "sourceDefinitionSchema", "sourceDefinitionSchema" +) +LookerQuery.FIELDS = KeywordField("fields", "fields") +LookerQuery.LOOKER_SLUG = KeywordField("lookerSlug", "lookerSlug") +LookerQuery.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +LookerQuery.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +LookerQuery.ANOMALO_CHECKS = RelationField("anomaloChecks") +LookerQuery.APPLICATION = RelationField("application") +LookerQuery.APPLICATION_FIELD = RelationField("applicationField") +LookerQuery.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +LookerQuery.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +LookerQuery.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +LookerQuery.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +LookerQuery.METRICS = RelationField("metrics") +LookerQuery.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +LookerQuery.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +LookerQuery.MEANINGS = RelationField("meanings") +LookerQuery.TILES = RelationField("tiles") +LookerQuery.LOOKS = RelationField("looks") +LookerQuery.MODEL = RelationField("model") +LookerQuery.MC_MONITORS = RelationField("mcMonitors") +LookerQuery.MC_INCIDENTS = RelationField("mcIncidents") +LookerQuery.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +LookerQuery.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +LookerQuery.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +LookerQuery.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +LookerQuery.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +LookerQuery.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +LookerQuery.FILES = RelationField("files") +LookerQuery.LINKS = RelationField("links") +LookerQuery.README = RelationField("readme") +LookerQuery.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +LookerQuery.SODA_CHECKS = RelationField("sodaChecks") +LookerQuery.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +LookerQuery.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/looker_related.py b/pyatlan_v9/model/assets/looker_related.py new file mode 100644 index 000000000..a0fd46b13 --- /dev/null +++ b/pyatlan_v9/model/assets/looker_related.py @@ -0,0 +1,373 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Looker module. + +This module contains all Related{Type} classes for the Looker type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedBI +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedLooker", + "RelatedLookerDashboard", + "RelatedLookerExplore", + "RelatedLookerView", + "RelatedLookerLook", + "RelatedLookerTile", + "RelatedLookerModel", + "RelatedLookerProject", + "RelatedLookerQuery", + "RelatedLookerField", + "RelatedLookerFolder", +] + + +class RelatedLooker(RelatedBI): + """ + Related entity reference for Looker assets. + + Extends RelatedBI with Looker-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Looker" so it serializes correctly + + looker_slug: Union[str, None, UnsetType] = UNSET + """An alpha-numeric slug for the underlying Looker asset that can be used to uniquely identify it""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Looker" + + +class RelatedLookerDashboard(RelatedLooker): + """ + Related entity reference for LookerDashboard assets. + + Extends RelatedLooker with LookerDashboard-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "LookerDashboard" so it serializes correctly + + folder_name: Union[str, None, UnsetType] = UNSET + """Name of the parent folder in Looker that contains this dashboard.""" + + source_user_id: Union[int, None, UnsetType] = UNSET + """Identifier of the user who created this dashboard, from Looker.""" + + source_view_count: Union[int, None, UnsetType] = UNSET + """Number of times the dashboard has been viewed through the Looker web UI.""" + + source_metadata_id: Union[int, None, UnsetType] = UNSET + """Identifier of the dashboard's content metadata, from Looker.""" + + sourcelast_updater_id: Union[int, None, UnsetType] = UNSET + """Identifier of the user who last updated the dashboard, from Looker.""" + + source_last_accessed_at: Union[int, None, UnsetType] = UNSET + """Timestamp (epoch) when the dashboard was last accessed by a user, in milliseconds.""" + + source_last_viewed_at: Union[int, None, UnsetType] = UNSET + """Timestamp (epoch) when the dashboard was last viewed by a user.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "LookerDashboard" + + +class RelatedLookerExplore(RelatedLooker): + """ + Related entity reference for LookerExplore assets. + + Extends RelatedLooker with LookerExplore-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "LookerExplore" so it serializes correctly + + project_name: Union[str, None, UnsetType] = UNSET + """Name of the parent project of this Explore.""" + + model_name: Union[str, None, UnsetType] = UNSET + """Name of the parent model of this Explore.""" + + source_connection_name: Union[str, None, UnsetType] = UNSET + """Connection name for the Explore, from Looker.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Name of the view for the Explore.""" + + sql_table_name: Union[str, None, UnsetType] = UNSET + """Name of the SQL table used to declare the Explore.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "LookerExplore" + + +class RelatedLookerView(RelatedLooker): + """ + Related entity reference for LookerView assets. + + Extends RelatedLooker with LookerView-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "LookerView" so it serializes correctly + + project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which this view exists.""" + + looker_view_file_path: Union[str, None, UnsetType] = UNSET + """File path of this view within the project.""" + + looker_file_name: Union[str, None, UnsetType] = UNSET + """File name of this view.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "LookerView" + + +class RelatedLookerLook(RelatedLooker): + """ + Related entity reference for LookerLook assets. + + Extends RelatedLooker with LookerLook-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "LookerLook" so it serializes correctly + + folder_name: Union[str, None, UnsetType] = UNSET + """Name of the folder in which the Look is organized.""" + + source_user_id: Union[int, None, UnsetType] = UNSET + """Identifier of the user who created the Look, from Looker.""" + + source_view_count: Union[int, None, UnsetType] = UNSET + """Number of times the look has been viewed in the Looker web UI.""" + + sourcelast_updater_id: Union[int, None, UnsetType] = UNSET + """Identifier of the user that last updated the Look, from Looker.""" + + source_last_accessed_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) when the Look was last accessed by a user, in milliseconds.""" + + source_last_viewed_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) when the Look was last viewed by a user, in milliseconds.""" + + source_content_metadata_id: Union[int, None, UnsetType] = UNSET + """Identifier of the Look's content metadata, from Looker.""" + + source_query_id: Union[int, None, UnsetType] = UNSET + """(Deprecated) Please use lookerSourceQueryId instead.""" + + looker_source_query_id: Union[str, None, UnsetType] = UNSET + """Identifier of the query for the Look, from Looker.""" + + model_name: Union[str, None, UnsetType] = UNSET + """Name of the model in which this Look exists.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "LookerLook" + + +class RelatedLookerTile(RelatedLooker): + """ + Related entity reference for LookerTile assets. + + Extends RelatedLooker with LookerTile-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "LookerTile" so it serializes correctly + + lookml_link_id: Union[str, None, UnsetType] = UNSET + """Identifier for the LoomML link.""" + + merge_result_id: Union[str, None, UnsetType] = UNSET + """Identifier for the merge result.""" + + note_text: Union[str, None, UnsetType] = UNSET + """Text of notes added to the tile.""" + + query_id: Union[int, None, UnsetType] = msgspec.field(default=UNSET, name="queryID") + """(Deprecated) Please use lookerQueryID instead.""" + + looker_query_id: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="lookerQueryID" + ) + """Identifier of the query for the Look, from Looker.""" + + result_maker_id: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="resultMakerID" + ) + """Identifier of the ResultMarkerLookup entry, from Looker.""" + + subtitle_text: Union[str, None, UnsetType] = UNSET + """Text for the subtitle for text tiles.""" + + look_id: Union[int, None, UnsetType] = UNSET + """Identifier of the Look used to create this tile, from Looker.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "LookerTile" + + +class RelatedLookerModel(RelatedLooker): + """ + Related entity reference for LookerModel assets. + + Extends RelatedLooker with LookerModel-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "LookerModel" so it serializes correctly + + project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which the model exists.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "LookerModel" + + +class RelatedLookerProject(RelatedLooker): + """ + Related entity reference for LookerProject assets. + + Extends RelatedLooker with LookerProject-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "LookerProject" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "LookerProject" + + +class RelatedLookerQuery(RelatedLooker): + """ + Related entity reference for LookerQuery assets. + + Extends RelatedLooker with LookerQuery-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "LookerQuery" so it serializes correctly + + source_definition: Union[str, None, UnsetType] = UNSET + """Deprecated.""" + + source_definition_database: Union[str, None, UnsetType] = UNSET + """Deprecated.""" + + source_definition_schema: Union[str, None, UnsetType] = UNSET + """Deprecated.""" + + fields: Union[List[str], None, UnsetType] = UNSET + """Deprecated.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "LookerQuery" + + +class RelatedLookerField(RelatedLooker): + """ + Related entity reference for LookerField assets. + + Extends RelatedLooker with LookerField-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "LookerField" so it serializes correctly + + project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which this field exists.""" + + looker_explore_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the Explore in which this field exists.""" + + looker_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this field exists.""" + + looker_tile_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the tile in which this field is used.""" + + looker_look_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the look in which this field is used.""" + + looker_dashboard_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dashboard in which this field is used.""" + + model_name: Union[str, None, UnsetType] = UNSET + """Name of the model in which this field exists.""" + + source_definition: Union[str, None, UnsetType] = UNSET + """Deprecated.""" + + looker_field_data_type: Union[str, None, UnsetType] = UNSET + """Deprecated.""" + + looker_times_used: Union[int, None, UnsetType] = UNSET + """Deprecated.""" + + looker_field_is_refined: Union[bool, None, UnsetType] = UNSET + """Whether the looker field asset is coming from a refinement""" + + looker_field_refinement_file_path: Union[str, None, UnsetType] = UNSET + """Absolute path of the file where the refinement of the field is declared.""" + + looker_field_refinement_line_number: Union[str, None, UnsetType] = UNSET + """Line number in the lookerFieldRefinementFilePath where this refinement of the field is declared.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "LookerField" + + +class RelatedLookerFolder(RelatedLooker): + """ + Related entity reference for LookerFolder assets. + + Extends RelatedLooker with LookerFolder-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "LookerFolder" so it serializes correctly + + source_content_metadata_id: Union[int, None, UnsetType] = UNSET + """Identifier for the folder's content metadata in Looker.""" + + source_creator_id: Union[int, None, UnsetType] = UNSET + """Identifier of the user who created the folder, from Looker.""" + + source_child_count: Union[int, None, UnsetType] = UNSET + """Number of subfolders in this folder.""" + + source_parent_id: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="sourceParentID" + ) + """Identifier of the parent folder of this folder, from Looker.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "LookerFolder" diff --git a/pyatlan_v9/model/assets/looker_tile.py b/pyatlan_v9/model/assets/looker_tile.py new file mode 100644 index 000000000..7679dcd61 --- /dev/null +++ b/pyatlan_v9/model/assets/looker_tile.py @@ -0,0 +1,679 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +LookerTile asset model with flattened inheritance. + +This module provides: +- LookerTile: Flat asset class (easy to use) +- LookerTileAttributes: Nested attributes struct (extends AssetAttributes) +- LookerTileNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .looker_related import ( + RelatedLookerDashboard, + RelatedLookerField, + RelatedLookerLook, + RelatedLookerQuery, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class LookerTile(Asset): + """ + Instance of a Looker tile in Atlan. + """ + + LOOKML_LINK_ID: ClassVar[Any] = None + MERGE_RESULT_ID: ClassVar[Any] = None + NOTE_TEXT: ClassVar[Any] = None + QUERY_ID: ClassVar[Any] = None + LOOKER_QUERY_ID: ClassVar[Any] = None + RESULT_MAKER_ID: ClassVar[Any] = None + SUBTITLE_TEXT: ClassVar[Any] = None + LOOK_ID: ClassVar[Any] = None + LOOKER_SLUG: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + DASHBOARD: ClassVar[Any] = None + LOOK: ClassVar[Any] = None + QUERY: ClassVar[Any] = None + FIELDS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "LookerTile" + + lookml_link_id: Union[str, None, UnsetType] = UNSET + """Identifier for the LoomML link.""" + + merge_result_id: Union[str, None, UnsetType] = UNSET + """Identifier for the merge result.""" + + note_text: Union[str, None, UnsetType] = UNSET + """Text of notes added to the tile.""" + + query_id: Union[int, None, UnsetType] = msgspec.field(default=UNSET, name="queryID") + """(Deprecated) Please use lookerQueryID instead.""" + + looker_query_id: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="lookerQueryID" + ) + """Identifier of the query for the Look, from Looker.""" + + result_maker_id: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="resultMakerID" + ) + """Identifier of the ResultMarkerLookup entry, from Looker.""" + + subtitle_text: Union[str, None, UnsetType] = UNSET + """Text for the subtitle for text tiles.""" + + look_id: Union[int, None, UnsetType] = UNSET + """Identifier of the Look used to create this tile, from Looker.""" + + looker_slug: Union[str, None, UnsetType] = UNSET + """An alpha-numeric slug for the underlying Looker asset that can be used to uniquely identify it""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + dashboard: Union[RelatedLookerDashboard, None, UnsetType] = UNSET + """Dashboard in which this tile exists.""" + + look: Union[RelatedLookerLook, None, UnsetType] = UNSET + """Look in which this tile exists.""" + + query: Union[RelatedLookerQuery, None, UnsetType] = UNSET + """Deprecated.""" + + fields: Union[List[RelatedLookerField], None, UnsetType] = UNSET + """Fields that are used in the tile.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "LookerTile" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _looker_tile_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> LookerTile: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + LookerTile instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _looker_tile_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class LookerTileAttributes(AssetAttributes): + """LookerTile-specific attributes for nested API format.""" + + lookml_link_id: Union[str, None, UnsetType] = UNSET + """Identifier for the LoomML link.""" + + merge_result_id: Union[str, None, UnsetType] = UNSET + """Identifier for the merge result.""" + + note_text: Union[str, None, UnsetType] = UNSET + """Text of notes added to the tile.""" + + query_id: Union[int, None, UnsetType] = msgspec.field(default=UNSET, name="queryID") + """(Deprecated) Please use lookerQueryID instead.""" + + looker_query_id: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="lookerQueryID" + ) + """Identifier of the query for the Look, from Looker.""" + + result_maker_id: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="resultMakerID" + ) + """Identifier of the ResultMarkerLookup entry, from Looker.""" + + subtitle_text: Union[str, None, UnsetType] = UNSET + """Text for the subtitle for text tiles.""" + + look_id: Union[int, None, UnsetType] = UNSET + """Identifier of the Look used to create this tile, from Looker.""" + + looker_slug: Union[str, None, UnsetType] = UNSET + """An alpha-numeric slug for the underlying Looker asset that can be used to uniquely identify it""" + + +class LookerTileRelationshipAttributes(AssetRelationshipAttributes): + """LookerTile-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + dashboard: Union[RelatedLookerDashboard, None, UnsetType] = UNSET + """Dashboard in which this tile exists.""" + + look: Union[RelatedLookerLook, None, UnsetType] = UNSET + """Look in which this tile exists.""" + + query: Union[RelatedLookerQuery, None, UnsetType] = UNSET + """Deprecated.""" + + fields: Union[List[RelatedLookerField], None, UnsetType] = UNSET + """Fields that are used in the tile.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class LookerTileNested(AssetNested): + """LookerTile in nested API format for high-performance serialization.""" + + attributes: Union[LookerTileAttributes, UnsetType] = UNSET + relationship_attributes: Union[LookerTileRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + LookerTileRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + LookerTileRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_LOOKER_TILE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "dashboard", + "look", + "query", + "fields", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_looker_tile_attrs(attrs: LookerTileAttributes, obj: LookerTile) -> None: + """Populate LookerTile-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.lookml_link_id = obj.lookml_link_id + attrs.merge_result_id = obj.merge_result_id + attrs.note_text = obj.note_text + attrs.query_id = obj.query_id + attrs.looker_query_id = obj.looker_query_id + attrs.result_maker_id = obj.result_maker_id + attrs.subtitle_text = obj.subtitle_text + attrs.look_id = obj.look_id + attrs.looker_slug = obj.looker_slug + + +def _extract_looker_tile_attrs(attrs: LookerTileAttributes) -> dict: + """Extract all LookerTile attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["lookml_link_id"] = attrs.lookml_link_id + result["merge_result_id"] = attrs.merge_result_id + result["note_text"] = attrs.note_text + result["query_id"] = attrs.query_id + result["looker_query_id"] = attrs.looker_query_id + result["result_maker_id"] = attrs.result_maker_id + result["subtitle_text"] = attrs.subtitle_text + result["look_id"] = attrs.look_id + result["looker_slug"] = attrs.looker_slug + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _looker_tile_to_nested(looker_tile: LookerTile) -> LookerTileNested: + """Convert flat LookerTile to nested format.""" + attrs = LookerTileAttributes() + _populate_looker_tile_attrs(attrs, looker_tile) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + looker_tile, _LOOKER_TILE_REL_FIELDS, LookerTileRelationshipAttributes + ) + return LookerTileNested( + guid=looker_tile.guid, + type_name=looker_tile.type_name, + status=looker_tile.status, + version=looker_tile.version, + create_time=looker_tile.create_time, + update_time=looker_tile.update_time, + created_by=looker_tile.created_by, + updated_by=looker_tile.updated_by, + classifications=looker_tile.classifications, + classification_names=looker_tile.classification_names, + meanings=looker_tile.meanings, + labels=looker_tile.labels, + business_attributes=looker_tile.business_attributes, + custom_attributes=looker_tile.custom_attributes, + pending_tasks=looker_tile.pending_tasks, + proxy=looker_tile.proxy, + is_incomplete=looker_tile.is_incomplete, + provenance_type=looker_tile.provenance_type, + home_id=looker_tile.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _looker_tile_from_nested(nested: LookerTileNested) -> LookerTile: + """Convert nested format to flat LookerTile.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else LookerTileAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _LOOKER_TILE_REL_FIELDS, + LookerTileRelationshipAttributes, + ) + return LookerTile( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_looker_tile_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _looker_tile_to_nested_bytes(looker_tile: LookerTile, serde: Serde) -> bytes: + """Convert flat LookerTile to nested JSON bytes.""" + return serde.encode(_looker_tile_to_nested(looker_tile)) + + +def _looker_tile_from_nested_bytes(data: bytes, serde: Serde) -> LookerTile: + """Convert nested JSON bytes to flat LookerTile.""" + nested = serde.decode(data, LookerTileNested) + return _looker_tile_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +LookerTile.LOOKML_LINK_ID = KeywordField("lookmlLinkId", "lookmlLinkId") +LookerTile.MERGE_RESULT_ID = KeywordField("mergeResultId", "mergeResultId") +LookerTile.NOTE_TEXT = KeywordField("noteText", "noteText") +LookerTile.QUERY_ID = NumericField("queryID", "queryID") +LookerTile.LOOKER_QUERY_ID = KeywordField("lookerQueryID", "lookerQueryID") +LookerTile.RESULT_MAKER_ID = NumericField("resultMakerID", "resultMakerID") +LookerTile.SUBTITLE_TEXT = KeywordField("subtitleText", "subtitleText") +LookerTile.LOOK_ID = NumericField("lookId", "lookId") +LookerTile.LOOKER_SLUG = KeywordField("lookerSlug", "lookerSlug") +LookerTile.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +LookerTile.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +LookerTile.ANOMALO_CHECKS = RelationField("anomaloChecks") +LookerTile.APPLICATION = RelationField("application") +LookerTile.APPLICATION_FIELD = RelationField("applicationField") +LookerTile.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +LookerTile.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +LookerTile.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +LookerTile.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +LookerTile.METRICS = RelationField("metrics") +LookerTile.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +LookerTile.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +LookerTile.MEANINGS = RelationField("meanings") +LookerTile.DASHBOARD = RelationField("dashboard") +LookerTile.LOOK = RelationField("look") +LookerTile.QUERY = RelationField("query") +LookerTile.FIELDS = RelationField("fields") +LookerTile.MC_MONITORS = RelationField("mcMonitors") +LookerTile.MC_INCIDENTS = RelationField("mcIncidents") +LookerTile.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +LookerTile.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +LookerTile.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +LookerTile.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +LookerTile.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +LookerTile.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +LookerTile.FILES = RelationField("files") +LookerTile.LINKS = RelationField("links") +LookerTile.README = RelationField("readme") +LookerTile.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +LookerTile.SODA_CHECKS = RelationField("sodaChecks") +LookerTile.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +LookerTile.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/looker_view.py b/pyatlan_v9/model/assets/looker_view.py new file mode 100644 index 000000000..d8bb6b20a --- /dev/null +++ b/pyatlan_v9/model/assets/looker_view.py @@ -0,0 +1,596 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +LookerView asset model with flattened inheritance. + +This module provides: +- LookerView: Flat asset class (easy to use) +- LookerViewAttributes: Nested attributes struct (extends AssetAttributes) +- LookerViewNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .looker_related import RelatedLookerField, RelatedLookerProject + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class LookerView(Asset): + """ + Instance of a Looker view in Atlan. Views represent tables of data in Looker, whether the table is database-native or created using Looker's derived table functionality. + """ + + PROJECT_NAME: ClassVar[Any] = None + LOOKER_VIEW_FILE_PATH: ClassVar[Any] = None + LOOKER_FILE_NAME: ClassVar[Any] = None + LOOKER_SLUG: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + PROJECT: ClassVar[Any] = None + FIELDS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "LookerView" + + project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which this view exists.""" + + looker_view_file_path: Union[str, None, UnsetType] = UNSET + """File path of this view within the project.""" + + looker_file_name: Union[str, None, UnsetType] = UNSET + """File name of this view.""" + + looker_slug: Union[str, None, UnsetType] = UNSET + """An alpha-numeric slug for the underlying Looker asset that can be used to uniquely identify it""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + project: Union[RelatedLookerProject, None, UnsetType] = UNSET + """Project in which this view exists.""" + + fields: Union[List[RelatedLookerField], None, UnsetType] = UNSET + """Fields that exist within this view.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "LookerView" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _looker_view_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> LookerView: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + LookerView instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _looker_view_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class LookerViewAttributes(AssetAttributes): + """LookerView-specific attributes for nested API format.""" + + project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which this view exists.""" + + looker_view_file_path: Union[str, None, UnsetType] = UNSET + """File path of this view within the project.""" + + looker_file_name: Union[str, None, UnsetType] = UNSET + """File name of this view.""" + + looker_slug: Union[str, None, UnsetType] = UNSET + """An alpha-numeric slug for the underlying Looker asset that can be used to uniquely identify it""" + + +class LookerViewRelationshipAttributes(AssetRelationshipAttributes): + """LookerView-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + project: Union[RelatedLookerProject, None, UnsetType] = UNSET + """Project in which this view exists.""" + + fields: Union[List[RelatedLookerField], None, UnsetType] = UNSET + """Fields that exist within this view.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class LookerViewNested(AssetNested): + """LookerView in nested API format for high-performance serialization.""" + + attributes: Union[LookerViewAttributes, UnsetType] = UNSET + relationship_attributes: Union[LookerViewRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + LookerViewRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + LookerViewRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_LOOKER_VIEW_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "project", + "fields", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_looker_view_attrs(attrs: LookerViewAttributes, obj: LookerView) -> None: + """Populate LookerView-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.project_name = obj.project_name + attrs.looker_view_file_path = obj.looker_view_file_path + attrs.looker_file_name = obj.looker_file_name + attrs.looker_slug = obj.looker_slug + + +def _extract_looker_view_attrs(attrs: LookerViewAttributes) -> dict: + """Extract all LookerView attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["project_name"] = attrs.project_name + result["looker_view_file_path"] = attrs.looker_view_file_path + result["looker_file_name"] = attrs.looker_file_name + result["looker_slug"] = attrs.looker_slug + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _looker_view_to_nested(looker_view: LookerView) -> LookerViewNested: + """Convert flat LookerView to nested format.""" + attrs = LookerViewAttributes() + _populate_looker_view_attrs(attrs, looker_view) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + looker_view, _LOOKER_VIEW_REL_FIELDS, LookerViewRelationshipAttributes + ) + return LookerViewNested( + guid=looker_view.guid, + type_name=looker_view.type_name, + status=looker_view.status, + version=looker_view.version, + create_time=looker_view.create_time, + update_time=looker_view.update_time, + created_by=looker_view.created_by, + updated_by=looker_view.updated_by, + classifications=looker_view.classifications, + classification_names=looker_view.classification_names, + meanings=looker_view.meanings, + labels=looker_view.labels, + business_attributes=looker_view.business_attributes, + custom_attributes=looker_view.custom_attributes, + pending_tasks=looker_view.pending_tasks, + proxy=looker_view.proxy, + is_incomplete=looker_view.is_incomplete, + provenance_type=looker_view.provenance_type, + home_id=looker_view.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _looker_view_from_nested(nested: LookerViewNested) -> LookerView: + """Convert nested format to flat LookerView.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else LookerViewAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _LOOKER_VIEW_REL_FIELDS, + LookerViewRelationshipAttributes, + ) + return LookerView( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_looker_view_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _looker_view_to_nested_bytes(looker_view: LookerView, serde: Serde) -> bytes: + """Convert flat LookerView to nested JSON bytes.""" + return serde.encode(_looker_view_to_nested(looker_view)) + + +def _looker_view_from_nested_bytes(data: bytes, serde: Serde) -> LookerView: + """Convert nested JSON bytes to flat LookerView.""" + nested = serde.decode(data, LookerViewNested) + return _looker_view_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +LookerView.PROJECT_NAME = KeywordField("projectName", "projectName") +LookerView.LOOKER_VIEW_FILE_PATH = KeywordField( + "lookerViewFilePath", "lookerViewFilePath" +) +LookerView.LOOKER_FILE_NAME = KeywordField("lookerFileName", "lookerFileName") +LookerView.LOOKER_SLUG = KeywordField("lookerSlug", "lookerSlug") +LookerView.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +LookerView.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +LookerView.ANOMALO_CHECKS = RelationField("anomaloChecks") +LookerView.APPLICATION = RelationField("application") +LookerView.APPLICATION_FIELD = RelationField("applicationField") +LookerView.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +LookerView.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +LookerView.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +LookerView.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +LookerView.METRICS = RelationField("metrics") +LookerView.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +LookerView.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +LookerView.MEANINGS = RelationField("meanings") +LookerView.PROJECT = RelationField("project") +LookerView.FIELDS = RelationField("fields") +LookerView.MC_MONITORS = RelationField("mcMonitors") +LookerView.MC_INCIDENTS = RelationField("mcIncidents") +LookerView.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +LookerView.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +LookerView.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +LookerView.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +LookerView.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +LookerView.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +LookerView.FILES = RelationField("files") +LookerView.LINKS = RelationField("links") +LookerView.README = RelationField("readme") +LookerView.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +LookerView.SODA_CHECKS = RelationField("sodaChecks") +LookerView.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +LookerView.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/materialised_view.py b/pyatlan_v9/model/assets/materialised_view.py new file mode 100644 index 000000000..0545bd81a --- /dev/null +++ b/pyatlan_v9/model/assets/materialised_view.py @@ -0,0 +1,1078 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +MaterialisedView asset model with flattened inheritance. + +This module provides: +- MaterialisedView: Flat asset class (easy to use) +- MaterialisedViewAttributes: Nested attributes struct (extends AssetAttributes) +- MaterialisedViewNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .snowflake_related import RelatedSnowflakeSemanticLogicalTable +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .sql_related import RelatedColumn, RelatedSchema + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class MaterialisedView(Asset): + """ + Instance of a materialized view in Atlan. + """ + + REFRESH_MODE: ClassVar[Any] = None + REFRESH_METHOD: ClassVar[Any] = None + STALENESS: ClassVar[Any] = None + STALE_SINCE_DATE: ClassVar[Any] = None + COLUMN_COUNT: ClassVar[Any] = None + ROW_COUNT: ClassVar[Any] = None + SIZE_BYTES: ClassVar[Any] = None + IS_QUERY_PREVIEW: ClassVar[Any] = None + QUERY_PREVIEW_CONFIG: ClassVar[Any] = None + ALIAS: ClassVar[Any] = None + IS_TEMPORARY: ClassVar[Any] = None + DEFINITION: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + COLUMNS: ClassVar[Any] = None + ATLAN_SCHEMA: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "MaterialisedView" + + refresh_mode: Union[str, None, UnsetType] = UNSET + """Refresh mode for this materialized view.""" + + refresh_method: Union[str, None, UnsetType] = UNSET + """Refresh method for this materialized view.""" + + staleness: Union[str, None, UnsetType] = UNSET + """Staleness of this materialized view.""" + + stale_since_date: Union[int, None, UnsetType] = UNSET + """Time (epoch) from which this materialized view is stale, in milliseconds.""" + + column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this materialized view.""" + + row_count: Union[int, None, UnsetType] = UNSET + """Number of rows in this materialized view.""" + + size_bytes: Union[int, None, UnsetType] = UNSET + """Size of this materialized view, in bytes.""" + + is_query_preview: Union[bool, None, UnsetType] = UNSET + """Whether it's possible to run a preview query on this materialized view (true) or not (false).""" + + query_preview_config: Union[Dict[str, str], None, UnsetType] = UNSET + """Configuration for the query preview of this materialized view.""" + + alias: Union[str, None, UnsetType] = UNSET + """Alias for this materialized view.""" + + is_temporary: Union[bool, None, UnsetType] = UNSET + """Whether this materialized view is temporary (true) or not (false).""" + + definition: Union[str, None, UnsetType] = UNSET + """SQL definition of this materialized view.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Columns that exist within this materialized view.""" + + atlan_schema: Union[RelatedSchema, None, UnsetType] = UNSET + """Schema in which this materialized view exists.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "MaterialisedView" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + schema_qualified_name: str, + schema_name: str | None = None, + database_name: str | None = None, + database_qualified_name: str | None = None, + connection_qualified_name: str | None = None, + ) -> "MaterialisedView": + """ + Create a new MaterialisedView asset with auto-derived fields. + + Args: + name: Simple name of the materialized view + schema_qualified_name: Unique name of the schema in which this materialized view exists + schema_name: Simple name of the schema (auto-derived if not provided) + database_name: Simple name of the database (auto-derived if not provided) + database_qualified_name: Unique name of the database (auto-derived if not provided) + connection_qualified_name: Unique name of the connection (auto-derived if not provided) + + Returns: + New MaterialisedView instance with all fields populated + + Raises: + ValueError: If required parameters are missing or invalid + """ + validate_required_fields( + ["name", "schema_qualified_name"], [name, schema_qualified_name] + ) + + fields = schema_qualified_name.split("/") + if len(fields) != 5: + raise ValueError( + f"Invalid schema_qualified_name: {schema_qualified_name}. " + "Expected format: default/connector/connection_id/database/schema" + ) + + connector_name = fields[1] + connection_qn = ( + connection_qualified_name or f"{fields[0]}/{fields[1]}/{fields[2]}" + ) + db_name = database_name or fields[3] + sch_name = schema_name or fields[4] + db_qualified_name = database_qualified_name or f"{connection_qn}/{db_name}" + qualified_name = f"{schema_qualified_name}/{name}" + + return cls( + name=name, + qualified_name=qualified_name, + database_name=db_name, + database_qualified_name=db_qualified_name, + schema_name=sch_name, + schema_qualified_name=schema_qualified_name, + connector_name=connector_name, + connection_qualified_name=connection_qn, + atlan_schema=RelatedSchema(qualified_name=schema_qualified_name), + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "MaterialisedView": + """ + Create a MaterialisedView instance for updating an existing asset. + + Args: + qualified_name: Unique name of the materialized view to update + name: Simple name of the materialized view + + Returns: + MaterialisedView instance configured for updates + + Raises: + ValueError: If required parameters are missing + """ + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "MaterialisedView": + """ + Return a MaterialisedView with only required fields for reference. + + Returns: + MaterialisedView instance with only qualified_name and name set + """ + return MaterialisedView(qualified_name=self.qualified_name, name=self.name) + + @classmethod + def create(cls, **kwargs) -> "MaterialisedView": + """Backward compatibility alias for creator().""" + return cls.creator(**kwargs) + + @classmethod + def create_for_modification(cls, **kwargs) -> "MaterialisedView": + """Backward compatibility alias for updater().""" + return cls.updater(**kwargs) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _materialised_view_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> MaterialisedView: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + MaterialisedView instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _materialised_view_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class MaterialisedViewAttributes(AssetAttributes): + """MaterialisedView-specific attributes for nested API format.""" + + refresh_mode: Union[str, None, UnsetType] = UNSET + """Refresh mode for this materialized view.""" + + refresh_method: Union[str, None, UnsetType] = UNSET + """Refresh method for this materialized view.""" + + staleness: Union[str, None, UnsetType] = UNSET + """Staleness of this materialized view.""" + + stale_since_date: Union[int, None, UnsetType] = UNSET + """Time (epoch) from which this materialized view is stale, in milliseconds.""" + + column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this materialized view.""" + + row_count: Union[int, None, UnsetType] = UNSET + """Number of rows in this materialized view.""" + + size_bytes: Union[int, None, UnsetType] = UNSET + """Size of this materialized view, in bytes.""" + + is_query_preview: Union[bool, None, UnsetType] = UNSET + """Whether it's possible to run a preview query on this materialized view (true) or not (false).""" + + query_preview_config: Union[Dict[str, str], None, UnsetType] = UNSET + """Configuration for the query preview of this materialized view.""" + + alias: Union[str, None, UnsetType] = UNSET + """Alias for this materialized view.""" + + is_temporary: Union[bool, None, UnsetType] = UNSET + """Whether this materialized view is temporary (true) or not (false).""" + + definition: Union[str, None, UnsetType] = UNSET + """SQL definition of this materialized view.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + +class MaterialisedViewRelationshipAttributes(AssetRelationshipAttributes): + """MaterialisedView-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Columns that exist within this materialized view.""" + + atlan_schema: Union[RelatedSchema, None, UnsetType] = UNSET + """Schema in which this materialized view exists.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class MaterialisedViewNested(AssetNested): + """MaterialisedView in nested API format for high-performance serialization.""" + + attributes: Union[MaterialisedViewAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + MaterialisedViewRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + MaterialisedViewRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + MaterialisedViewRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_MATERIALISED_VIEW_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "columns", + "atlan_schema", + "schema_registry_subjects", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_materialised_view_attrs( + attrs: MaterialisedViewAttributes, obj: MaterialisedView +) -> None: + """Populate MaterialisedView-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.refresh_mode = obj.refresh_mode + attrs.refresh_method = obj.refresh_method + attrs.staleness = obj.staleness + attrs.stale_since_date = obj.stale_since_date + attrs.column_count = obj.column_count + attrs.row_count = obj.row_count + attrs.size_bytes = obj.size_bytes + attrs.is_query_preview = obj.is_query_preview + attrs.query_preview_config = obj.query_preview_config + attrs.alias = obj.alias + attrs.is_temporary = obj.is_temporary + attrs.definition = obj.definition + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + + +def _extract_materialised_view_attrs(attrs: MaterialisedViewAttributes) -> dict: + """Extract all MaterialisedView attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["refresh_mode"] = attrs.refresh_mode + result["refresh_method"] = attrs.refresh_method + result["staleness"] = attrs.staleness + result["stale_since_date"] = attrs.stale_since_date + result["column_count"] = attrs.column_count + result["row_count"] = attrs.row_count + result["size_bytes"] = attrs.size_bytes + result["is_query_preview"] = attrs.is_query_preview + result["query_preview_config"] = attrs.query_preview_config + result["alias"] = attrs.alias + result["is_temporary"] = attrs.is_temporary + result["definition"] = attrs.definition + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _materialised_view_to_nested( + materialised_view: MaterialisedView, +) -> MaterialisedViewNested: + """Convert flat MaterialisedView to nested format.""" + attrs = MaterialisedViewAttributes() + _populate_materialised_view_attrs(attrs, materialised_view) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + materialised_view, + _MATERIALISED_VIEW_REL_FIELDS, + MaterialisedViewRelationshipAttributes, + ) + return MaterialisedViewNested( + guid=materialised_view.guid, + type_name=materialised_view.type_name, + status=materialised_view.status, + version=materialised_view.version, + create_time=materialised_view.create_time, + update_time=materialised_view.update_time, + created_by=materialised_view.created_by, + updated_by=materialised_view.updated_by, + classifications=materialised_view.classifications, + classification_names=materialised_view.classification_names, + meanings=materialised_view.meanings, + labels=materialised_view.labels, + business_attributes=materialised_view.business_attributes, + custom_attributes=materialised_view.custom_attributes, + pending_tasks=materialised_view.pending_tasks, + proxy=materialised_view.proxy, + is_incomplete=materialised_view.is_incomplete, + provenance_type=materialised_view.provenance_type, + home_id=materialised_view.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _materialised_view_from_nested(nested: MaterialisedViewNested) -> MaterialisedView: + """Convert nested format to flat MaterialisedView.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else MaterialisedViewAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _MATERIALISED_VIEW_REL_FIELDS, + MaterialisedViewRelationshipAttributes, + ) + return MaterialisedView( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_materialised_view_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _materialised_view_to_nested_bytes( + materialised_view: MaterialisedView, serde: Serde +) -> bytes: + """Convert flat MaterialisedView to nested JSON bytes.""" + return serde.encode(_materialised_view_to_nested(materialised_view)) + + +def _materialised_view_from_nested_bytes(data: bytes, serde: Serde) -> MaterialisedView: + """Convert nested JSON bytes to flat MaterialisedView.""" + nested = serde.decode(data, MaterialisedViewNested) + return _materialised_view_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, +) + +MaterialisedView.REFRESH_MODE = KeywordField("refreshMode", "refreshMode") +MaterialisedView.REFRESH_METHOD = KeywordField("refreshMethod", "refreshMethod") +MaterialisedView.STALENESS = KeywordField("staleness", "staleness") +MaterialisedView.STALE_SINCE_DATE = NumericField("staleSinceDate", "staleSinceDate") +MaterialisedView.COLUMN_COUNT = NumericField("columnCount", "columnCount") +MaterialisedView.ROW_COUNT = NumericField("rowCount", "rowCount") +MaterialisedView.SIZE_BYTES = NumericField("sizeBytes", "sizeBytes") +MaterialisedView.IS_QUERY_PREVIEW = BooleanField("isQueryPreview", "isQueryPreview") +MaterialisedView.QUERY_PREVIEW_CONFIG = KeywordField( + "queryPreviewConfig", "queryPreviewConfig" +) +MaterialisedView.ALIAS = KeywordField("alias", "alias") +MaterialisedView.IS_TEMPORARY = BooleanField("isTemporary", "isTemporary") +MaterialisedView.DEFINITION = KeywordField("definition", "definition") +MaterialisedView.QUERY_COUNT = NumericField("queryCount", "queryCount") +MaterialisedView.QUERY_USER_COUNT = NumericField("queryUserCount", "queryUserCount") +MaterialisedView.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +MaterialisedView.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +MaterialisedView.DATABASE_NAME = KeywordField("databaseName", "databaseName") +MaterialisedView.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +MaterialisedView.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +MaterialisedView.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +MaterialisedView.TABLE_NAME = KeywordField("tableName", "tableName") +MaterialisedView.TABLE_QUALIFIED_NAME = KeywordField( + "tableQualifiedName", "tableQualifiedName" +) +MaterialisedView.VIEW_NAME = KeywordField("viewName", "viewName") +MaterialisedView.VIEW_QUALIFIED_NAME = KeywordField( + "viewQualifiedName", "viewQualifiedName" +) +MaterialisedView.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +MaterialisedView.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +MaterialisedView.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +MaterialisedView.LAST_PROFILED_AT = NumericField("lastProfiledAt", "lastProfiledAt") +MaterialisedView.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +MaterialisedView.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +MaterialisedView.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +MaterialisedView.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +MaterialisedView.ANOMALO_CHECKS = RelationField("anomaloChecks") +MaterialisedView.APPLICATION = RelationField("application") +MaterialisedView.APPLICATION_FIELD = RelationField("applicationField") +MaterialisedView.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +MaterialisedView.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +MaterialisedView.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +MaterialisedView.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +MaterialisedView.METRICS = RelationField("metrics") +MaterialisedView.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +MaterialisedView.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +MaterialisedView.DBT_MODELS = RelationField("dbtModels") +MaterialisedView.SQL_DBT_MODELS = RelationField("sqlDbtModels") +MaterialisedView.DBT_TESTS = RelationField("dbtTests") +MaterialisedView.DBT_SOURCES = RelationField("dbtSources") +MaterialisedView.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +MaterialisedView.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +MaterialisedView.MEANINGS = RelationField("meanings") +MaterialisedView.MC_MONITORS = RelationField("mcMonitors") +MaterialisedView.MC_INCIDENTS = RelationField("mcIncidents") +MaterialisedView.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +MaterialisedView.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +MaterialisedView.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +MaterialisedView.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +MaterialisedView.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +MaterialisedView.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +MaterialisedView.FILES = RelationField("files") +MaterialisedView.LINKS = RelationField("links") +MaterialisedView.README = RelationField("readme") +MaterialisedView.COLUMNS = RelationField("columns") +MaterialisedView.ATLAN_SCHEMA = RelationField("atlanSchema") +MaterialisedView.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +MaterialisedView.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +MaterialisedView.SODA_CHECKS = RelationField("sodaChecks") +MaterialisedView.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +MaterialisedView.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/matillion.py b/pyatlan_v9/model/assets/matillion.py new file mode 100644 index 000000000..ad06d0f9a --- /dev/null +++ b/pyatlan_v9/model/assets/matillion.py @@ -0,0 +1,537 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Matillion asset model with flattened inheritance. + +This module provides: +- Matillion: Flat asset class (easy to use) +- MatillionAttributes: Nested attributes struct (extends AssetAttributes) +- MatillionNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Matillion(Asset): + """ + Base class for Matillion assets. + """ + + MATILLION_VERSION: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Matillion" + + matillion_version: Union[str, None, UnsetType] = UNSET + """Current point in time state of a project.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Matillion" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _matillion_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Matillion: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Matillion instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _matillion_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class MatillionAttributes(AssetAttributes): + """Matillion-specific attributes for nested API format.""" + + matillion_version: Union[str, None, UnsetType] = UNSET + """Current point in time state of a project.""" + + +class MatillionRelationshipAttributes(AssetRelationshipAttributes): + """Matillion-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class MatillionNested(AssetNested): + """Matillion in nested API format for high-performance serialization.""" + + attributes: Union[MatillionAttributes, UnsetType] = UNSET + relationship_attributes: Union[MatillionRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + MatillionRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + MatillionRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_MATILLION_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_matillion_attrs(attrs: MatillionAttributes, obj: Matillion) -> None: + """Populate Matillion-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.matillion_version = obj.matillion_version + + +def _extract_matillion_attrs(attrs: MatillionAttributes) -> dict: + """Extract all Matillion attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["matillion_version"] = attrs.matillion_version + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _matillion_to_nested(matillion: Matillion) -> MatillionNested: + """Convert flat Matillion to nested format.""" + attrs = MatillionAttributes() + _populate_matillion_attrs(attrs, matillion) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + matillion, _MATILLION_REL_FIELDS, MatillionRelationshipAttributes + ) + return MatillionNested( + guid=matillion.guid, + type_name=matillion.type_name, + status=matillion.status, + version=matillion.version, + create_time=matillion.create_time, + update_time=matillion.update_time, + created_by=matillion.created_by, + updated_by=matillion.updated_by, + classifications=matillion.classifications, + classification_names=matillion.classification_names, + meanings=matillion.meanings, + labels=matillion.labels, + business_attributes=matillion.business_attributes, + custom_attributes=matillion.custom_attributes, + pending_tasks=matillion.pending_tasks, + proxy=matillion.proxy, + is_incomplete=matillion.is_incomplete, + provenance_type=matillion.provenance_type, + home_id=matillion.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _matillion_from_nested(nested: MatillionNested) -> Matillion: + """Convert nested format to flat Matillion.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else MatillionAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _MATILLION_REL_FIELDS, + MatillionRelationshipAttributes, + ) + return Matillion( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_matillion_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _matillion_to_nested_bytes(matillion: Matillion, serde: Serde) -> bytes: + """Convert flat Matillion to nested JSON bytes.""" + return serde.encode(_matillion_to_nested(matillion)) + + +def _matillion_from_nested_bytes(data: bytes, serde: Serde) -> Matillion: + """Convert nested JSON bytes to flat Matillion.""" + nested = serde.decode(data, MatillionNested) + return _matillion_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +Matillion.MATILLION_VERSION = KeywordField("matillionVersion", "matillionVersion") +Matillion.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Matillion.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Matillion.ANOMALO_CHECKS = RelationField("anomaloChecks") +Matillion.APPLICATION = RelationField("application") +Matillion.APPLICATION_FIELD = RelationField("applicationField") +Matillion.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Matillion.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Matillion.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Matillion.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Matillion.METRICS = RelationField("metrics") +Matillion.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Matillion.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Matillion.MEANINGS = RelationField("meanings") +Matillion.MC_MONITORS = RelationField("mcMonitors") +Matillion.MC_INCIDENTS = RelationField("mcIncidents") +Matillion.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Matillion.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Matillion.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Matillion.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Matillion.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Matillion.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Matillion.FILES = RelationField("files") +Matillion.LINKS = RelationField("links") +Matillion.README = RelationField("readme") +Matillion.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Matillion.SODA_CHECKS = RelationField("sodaChecks") +Matillion.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Matillion.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/matillion_component.py b/pyatlan_v9/model/assets/matillion_component.py new file mode 100644 index 000000000..9e16158a2 --- /dev/null +++ b/pyatlan_v9/model/assets/matillion_component.py @@ -0,0 +1,699 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +MatillionComponent asset model with flattened inheritance. + +This module provides: +- MatillionComponent: Flat asset class (easy to use) +- MatillionComponentAttributes: Nested attributes struct (extends AssetAttributes) +- MatillionComponentNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .matillion_related import RelatedMatillionJob + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class MatillionComponent(Asset): + """ + Instance of a Matillion component in Atlan. Components in Matillion are a part of a job, where each component is responsible for accomplishing a task based on the type of component used. + """ + + MATILLION_COMPONENT_ID: ClassVar[Any] = None + MATILLION_COMPONENT_IMPLEMENTATION_ID: ClassVar[Any] = None + MATILLION_COMPONENT_LINKED_JOB: ClassVar[Any] = None + MATILLION_COMPONENT_LAST_RUN_STATUS: ClassVar[Any] = None + MATILLION_COMPONENT_LAST_FIVE_RUN_STATUS: ClassVar[Any] = None + MATILLION_COMPONENT_SQLS: ClassVar[Any] = None + MATILLION_JOB_NAME: ClassVar[Any] = None + MATILLION_JOB_QUALIFIED_NAME: ClassVar[Any] = None + MATILLION_VERSION: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MATILLION_JOB: ClassVar[Any] = None + MATILLION_PROCESS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "MatillionComponent" + + matillion_component_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the component in Matillion.""" + + matillion_component_implementation_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the type of the component in Matillion.""" + + matillion_component_linked_job: Union[Dict[str, str], None, UnsetType] = UNSET + """Job details of the job to which the component internally links.""" + + matillion_component_last_run_status: Union[str, None, UnsetType] = UNSET + """Latest run status of the component within a job.""" + + matillion_component_last_five_run_status: Union[str, None, UnsetType] = UNSET + """Last five run statuses of the component within a job.""" + + matillion_component_sqls: Union[List[str], None, UnsetType] = UNSET + """SQL queries used by the component.""" + + matillion_job_name: Union[str, None, UnsetType] = UNSET + """Simple name of the job to which the component belongs.""" + + matillion_job_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the job to which the component belongs.""" + + matillion_version: Union[str, None, UnsetType] = UNSET + """Current point in time state of a project.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + matillion_job: Union[RelatedMatillionJob, None, UnsetType] = UNSET + """Job in which this component exists.""" + + matillion_process: Union[RelatedProcess, None, UnsetType] = UNSET + """Lineage process that represents this Matillion component.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "MatillionComponent" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _matillion_component_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> MatillionComponent: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + MatillionComponent instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _matillion_component_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class MatillionComponentAttributes(AssetAttributes): + """MatillionComponent-specific attributes for nested API format.""" + + matillion_component_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the component in Matillion.""" + + matillion_component_implementation_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the type of the component in Matillion.""" + + matillion_component_linked_job: Union[Dict[str, str], None, UnsetType] = UNSET + """Job details of the job to which the component internally links.""" + + matillion_component_last_run_status: Union[str, None, UnsetType] = UNSET + """Latest run status of the component within a job.""" + + matillion_component_last_five_run_status: Union[str, None, UnsetType] = UNSET + """Last five run statuses of the component within a job.""" + + matillion_component_sqls: Union[List[str], None, UnsetType] = UNSET + """SQL queries used by the component.""" + + matillion_job_name: Union[str, None, UnsetType] = UNSET + """Simple name of the job to which the component belongs.""" + + matillion_job_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the job to which the component belongs.""" + + matillion_version: Union[str, None, UnsetType] = UNSET + """Current point in time state of a project.""" + + +class MatillionComponentRelationshipAttributes(AssetRelationshipAttributes): + """MatillionComponent-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + matillion_job: Union[RelatedMatillionJob, None, UnsetType] = UNSET + """Job in which this component exists.""" + + matillion_process: Union[RelatedProcess, None, UnsetType] = UNSET + """Lineage process that represents this Matillion component.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class MatillionComponentNested(AssetNested): + """MatillionComponent in nested API format for high-performance serialization.""" + + attributes: Union[MatillionComponentAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + MatillionComponentRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + MatillionComponentRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + MatillionComponentRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_MATILLION_COMPONENT_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "matillion_job", + "matillion_process", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_matillion_component_attrs( + attrs: MatillionComponentAttributes, obj: MatillionComponent +) -> None: + """Populate MatillionComponent-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.matillion_component_id = obj.matillion_component_id + attrs.matillion_component_implementation_id = ( + obj.matillion_component_implementation_id + ) + attrs.matillion_component_linked_job = obj.matillion_component_linked_job + attrs.matillion_component_last_run_status = obj.matillion_component_last_run_status + attrs.matillion_component_last_five_run_status = ( + obj.matillion_component_last_five_run_status + ) + attrs.matillion_component_sqls = obj.matillion_component_sqls + attrs.matillion_job_name = obj.matillion_job_name + attrs.matillion_job_qualified_name = obj.matillion_job_qualified_name + attrs.matillion_version = obj.matillion_version + + +def _extract_matillion_component_attrs(attrs: MatillionComponentAttributes) -> dict: + """Extract all MatillionComponent attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["matillion_component_id"] = attrs.matillion_component_id + result["matillion_component_implementation_id"] = ( + attrs.matillion_component_implementation_id + ) + result["matillion_component_linked_job"] = attrs.matillion_component_linked_job + result["matillion_component_last_run_status"] = ( + attrs.matillion_component_last_run_status + ) + result["matillion_component_last_five_run_status"] = ( + attrs.matillion_component_last_five_run_status + ) + result["matillion_component_sqls"] = attrs.matillion_component_sqls + result["matillion_job_name"] = attrs.matillion_job_name + result["matillion_job_qualified_name"] = attrs.matillion_job_qualified_name + result["matillion_version"] = attrs.matillion_version + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _matillion_component_to_nested( + matillion_component: MatillionComponent, +) -> MatillionComponentNested: + """Convert flat MatillionComponent to nested format.""" + attrs = MatillionComponentAttributes() + _populate_matillion_component_attrs(attrs, matillion_component) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + matillion_component, + _MATILLION_COMPONENT_REL_FIELDS, + MatillionComponentRelationshipAttributes, + ) + return MatillionComponentNested( + guid=matillion_component.guid, + type_name=matillion_component.type_name, + status=matillion_component.status, + version=matillion_component.version, + create_time=matillion_component.create_time, + update_time=matillion_component.update_time, + created_by=matillion_component.created_by, + updated_by=matillion_component.updated_by, + classifications=matillion_component.classifications, + classification_names=matillion_component.classification_names, + meanings=matillion_component.meanings, + labels=matillion_component.labels, + business_attributes=matillion_component.business_attributes, + custom_attributes=matillion_component.custom_attributes, + pending_tasks=matillion_component.pending_tasks, + proxy=matillion_component.proxy, + is_incomplete=matillion_component.is_incomplete, + provenance_type=matillion_component.provenance_type, + home_id=matillion_component.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _matillion_component_from_nested( + nested: MatillionComponentNested, +) -> MatillionComponent: + """Convert nested format to flat MatillionComponent.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else MatillionComponentAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _MATILLION_COMPONENT_REL_FIELDS, + MatillionComponentRelationshipAttributes, + ) + return MatillionComponent( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_matillion_component_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _matillion_component_to_nested_bytes( + matillion_component: MatillionComponent, serde: Serde +) -> bytes: + """Convert flat MatillionComponent to nested JSON bytes.""" + return serde.encode(_matillion_component_to_nested(matillion_component)) + + +def _matillion_component_from_nested_bytes( + data: bytes, serde: Serde +) -> MatillionComponent: + """Convert nested JSON bytes to flat MatillionComponent.""" + nested = serde.decode(data, MatillionComponentNested) + return _matillion_component_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + RelationField, +) + +MatillionComponent.MATILLION_COMPONENT_ID = KeywordField( + "matillionComponentId", "matillionComponentId" +) +MatillionComponent.MATILLION_COMPONENT_IMPLEMENTATION_ID = KeywordField( + "matillionComponentImplementationId", "matillionComponentImplementationId" +) +MatillionComponent.MATILLION_COMPONENT_LINKED_JOB = KeywordField( + "matillionComponentLinkedJob", "matillionComponentLinkedJob" +) +MatillionComponent.MATILLION_COMPONENT_LAST_RUN_STATUS = KeywordField( + "matillionComponentLastRunStatus", "matillionComponentLastRunStatus" +) +MatillionComponent.MATILLION_COMPONENT_LAST_FIVE_RUN_STATUS = KeywordField( + "matillionComponentLastFiveRunStatus", "matillionComponentLastFiveRunStatus" +) +MatillionComponent.MATILLION_COMPONENT_SQLS = KeywordField( + "matillionComponentSqls", "matillionComponentSqls" +) +MatillionComponent.MATILLION_JOB_NAME = KeywordTextField( + "matillionJobName", "matillionJobName", "matillionJobName.text" +) +MatillionComponent.MATILLION_JOB_QUALIFIED_NAME = KeywordTextField( + "matillionJobQualifiedName", + "matillionJobQualifiedName", + "matillionJobQualifiedName.text", +) +MatillionComponent.MATILLION_VERSION = KeywordField( + "matillionVersion", "matillionVersion" +) +MatillionComponent.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +MatillionComponent.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +MatillionComponent.ANOMALO_CHECKS = RelationField("anomaloChecks") +MatillionComponent.APPLICATION = RelationField("application") +MatillionComponent.APPLICATION_FIELD = RelationField("applicationField") +MatillionComponent.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +MatillionComponent.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +MatillionComponent.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +MatillionComponent.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +MatillionComponent.METRICS = RelationField("metrics") +MatillionComponent.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +MatillionComponent.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +MatillionComponent.MEANINGS = RelationField("meanings") +MatillionComponent.MATILLION_JOB = RelationField("matillionJob") +MatillionComponent.MATILLION_PROCESS = RelationField("matillionProcess") +MatillionComponent.MC_MONITORS = RelationField("mcMonitors") +MatillionComponent.MC_INCIDENTS = RelationField("mcIncidents") +MatillionComponent.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +MatillionComponent.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +MatillionComponent.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +MatillionComponent.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +MatillionComponent.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +MatillionComponent.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +MatillionComponent.FILES = RelationField("files") +MatillionComponent.LINKS = RelationField("links") +MatillionComponent.README = RelationField("readme") +MatillionComponent.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +MatillionComponent.SODA_CHECKS = RelationField("sodaChecks") +MatillionComponent.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +MatillionComponent.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/matillion_group.py b/pyatlan_v9/model/assets/matillion_group.py new file mode 100644 index 000000000..6fa72c33c --- /dev/null +++ b/pyatlan_v9/model/assets/matillion_group.py @@ -0,0 +1,573 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +MatillionGroup asset model with flattened inheritance. + +This module provides: +- MatillionGroup: Flat asset class (easy to use) +- MatillionGroupAttributes: Nested attributes struct (extends AssetAttributes) +- MatillionGroupNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .matillion_related import RelatedMatillionProject + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class MatillionGroup(Asset): + """ + Instance of a Matillion group in Atlan. A group in Matillion is the top-level hierarchy, where resources are managed and explored. + """ + + MATILLION_PROJECT_COUNT: ClassVar[Any] = None + MATILLION_VERSION: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MATILLION_PROJECTS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "MatillionGroup" + + matillion_project_count: Union[int, None, UnsetType] = UNSET + """Number of projects within the group.""" + + matillion_version: Union[str, None, UnsetType] = UNSET + """Current point in time state of a project.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + matillion_projects: Union[List[RelatedMatillionProject], None, UnsetType] = UNSET + """Matillion projects that exist within this group.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "MatillionGroup" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _matillion_group_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> MatillionGroup: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + MatillionGroup instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _matillion_group_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class MatillionGroupAttributes(AssetAttributes): + """MatillionGroup-specific attributes for nested API format.""" + + matillion_project_count: Union[int, None, UnsetType] = UNSET + """Number of projects within the group.""" + + matillion_version: Union[str, None, UnsetType] = UNSET + """Current point in time state of a project.""" + + +class MatillionGroupRelationshipAttributes(AssetRelationshipAttributes): + """MatillionGroup-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + matillion_projects: Union[List[RelatedMatillionProject], None, UnsetType] = UNSET + """Matillion projects that exist within this group.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class MatillionGroupNested(AssetNested): + """MatillionGroup in nested API format for high-performance serialization.""" + + attributes: Union[MatillionGroupAttributes, UnsetType] = UNSET + relationship_attributes: Union[MatillionGroupRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + MatillionGroupRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + MatillionGroupRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_MATILLION_GROUP_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "matillion_projects", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_matillion_group_attrs( + attrs: MatillionGroupAttributes, obj: MatillionGroup +) -> None: + """Populate MatillionGroup-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.matillion_project_count = obj.matillion_project_count + attrs.matillion_version = obj.matillion_version + + +def _extract_matillion_group_attrs(attrs: MatillionGroupAttributes) -> dict: + """Extract all MatillionGroup attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["matillion_project_count"] = attrs.matillion_project_count + result["matillion_version"] = attrs.matillion_version + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _matillion_group_to_nested(matillion_group: MatillionGroup) -> MatillionGroupNested: + """Convert flat MatillionGroup to nested format.""" + attrs = MatillionGroupAttributes() + _populate_matillion_group_attrs(attrs, matillion_group) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + matillion_group, + _MATILLION_GROUP_REL_FIELDS, + MatillionGroupRelationshipAttributes, + ) + return MatillionGroupNested( + guid=matillion_group.guid, + type_name=matillion_group.type_name, + status=matillion_group.status, + version=matillion_group.version, + create_time=matillion_group.create_time, + update_time=matillion_group.update_time, + created_by=matillion_group.created_by, + updated_by=matillion_group.updated_by, + classifications=matillion_group.classifications, + classification_names=matillion_group.classification_names, + meanings=matillion_group.meanings, + labels=matillion_group.labels, + business_attributes=matillion_group.business_attributes, + custom_attributes=matillion_group.custom_attributes, + pending_tasks=matillion_group.pending_tasks, + proxy=matillion_group.proxy, + is_incomplete=matillion_group.is_incomplete, + provenance_type=matillion_group.provenance_type, + home_id=matillion_group.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _matillion_group_from_nested(nested: MatillionGroupNested) -> MatillionGroup: + """Convert nested format to flat MatillionGroup.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else MatillionGroupAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _MATILLION_GROUP_REL_FIELDS, + MatillionGroupRelationshipAttributes, + ) + return MatillionGroup( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_matillion_group_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _matillion_group_to_nested_bytes( + matillion_group: MatillionGroup, serde: Serde +) -> bytes: + """Convert flat MatillionGroup to nested JSON bytes.""" + return serde.encode(_matillion_group_to_nested(matillion_group)) + + +def _matillion_group_from_nested_bytes(data: bytes, serde: Serde) -> MatillionGroup: + """Convert nested JSON bytes to flat MatillionGroup.""" + nested = serde.decode(data, MatillionGroupNested) + return _matillion_group_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +MatillionGroup.MATILLION_PROJECT_COUNT = NumericField( + "matillionProjectCount", "matillionProjectCount" +) +MatillionGroup.MATILLION_VERSION = KeywordField("matillionVersion", "matillionVersion") +MatillionGroup.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +MatillionGroup.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +MatillionGroup.ANOMALO_CHECKS = RelationField("anomaloChecks") +MatillionGroup.APPLICATION = RelationField("application") +MatillionGroup.APPLICATION_FIELD = RelationField("applicationField") +MatillionGroup.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +MatillionGroup.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +MatillionGroup.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +MatillionGroup.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +MatillionGroup.METRICS = RelationField("metrics") +MatillionGroup.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +MatillionGroup.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +MatillionGroup.MEANINGS = RelationField("meanings") +MatillionGroup.MATILLION_PROJECTS = RelationField("matillionProjects") +MatillionGroup.MC_MONITORS = RelationField("mcMonitors") +MatillionGroup.MC_INCIDENTS = RelationField("mcIncidents") +MatillionGroup.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +MatillionGroup.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +MatillionGroup.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +MatillionGroup.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +MatillionGroup.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +MatillionGroup.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +MatillionGroup.FILES = RelationField("files") +MatillionGroup.LINKS = RelationField("links") +MatillionGroup.README = RelationField("readme") +MatillionGroup.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +MatillionGroup.SODA_CHECKS = RelationField("sodaChecks") +MatillionGroup.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +MatillionGroup.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/matillion_job.py b/pyatlan_v9/model/assets/matillion_job.py new file mode 100644 index 000000000..d785af5bd --- /dev/null +++ b/pyatlan_v9/model/assets/matillion_job.py @@ -0,0 +1,650 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +MatillionJob asset model with flattened inheritance. + +This module provides: +- MatillionJob: Flat asset class (easy to use) +- MatillionJobAttributes: Nested attributes struct (extends AssetAttributes) +- MatillionJobNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .matillion_related import RelatedMatillionComponent, RelatedMatillionProject + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class MatillionJob(Asset): + """ + Instance of a Matillion job in Atlan. Jobs in Matillion design, organize and execute workflows which are responsible for ETL data processing. + """ + + MATILLION_JOB_TYPE: ClassVar[Any] = None + MATILLION_JOB_PATH: ClassVar[Any] = None + MATILLION_JOB_COMPONENT_COUNT: ClassVar[Any] = None + MATILLION_JOB_SCHEDULE: ClassVar[Any] = None + MATILLION_PROJECT_NAME: ClassVar[Any] = None + MATILLION_PROJECT_QUALIFIED_NAME: ClassVar[Any] = None + MATILLION_VERSION: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MATILLION_PROJECT: ClassVar[Any] = None + MATILLION_COMPONENTS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "MatillionJob" + + matillion_job_type: Union[str, None, UnsetType] = UNSET + """Type of the job, for example: orchestration or transformation.""" + + matillion_job_path: Union[str, None, UnsetType] = UNSET + """Path of the job within the project. Jobs can be managed at multiple folder levels within a project.""" + + matillion_job_component_count: Union[int, None, UnsetType] = UNSET + """Number of components within the job.""" + + matillion_job_schedule: Union[str, None, UnsetType] = UNSET + """How the job is scheduled, for example: weekly or monthly.""" + + matillion_project_name: Union[str, None, UnsetType] = UNSET + """Simple name of the project to which the job belongs.""" + + matillion_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project to which the job belongs.""" + + matillion_version: Union[str, None, UnsetType] = UNSET + """Current point in time state of a project.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + matillion_project: Union[RelatedMatillionProject, None, UnsetType] = UNSET + """Project in which the job exists.""" + + matillion_components: Union[List[RelatedMatillionComponent], None, UnsetType] = ( + UNSET + ) + """Components that exist within this job.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "MatillionJob" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _matillion_job_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> MatillionJob: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + MatillionJob instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _matillion_job_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class MatillionJobAttributes(AssetAttributes): + """MatillionJob-specific attributes for nested API format.""" + + matillion_job_type: Union[str, None, UnsetType] = UNSET + """Type of the job, for example: orchestration or transformation.""" + + matillion_job_path: Union[str, None, UnsetType] = UNSET + """Path of the job within the project. Jobs can be managed at multiple folder levels within a project.""" + + matillion_job_component_count: Union[int, None, UnsetType] = UNSET + """Number of components within the job.""" + + matillion_job_schedule: Union[str, None, UnsetType] = UNSET + """How the job is scheduled, for example: weekly or monthly.""" + + matillion_project_name: Union[str, None, UnsetType] = UNSET + """Simple name of the project to which the job belongs.""" + + matillion_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project to which the job belongs.""" + + matillion_version: Union[str, None, UnsetType] = UNSET + """Current point in time state of a project.""" + + +class MatillionJobRelationshipAttributes(AssetRelationshipAttributes): + """MatillionJob-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + matillion_project: Union[RelatedMatillionProject, None, UnsetType] = UNSET + """Project in which the job exists.""" + + matillion_components: Union[List[RelatedMatillionComponent], None, UnsetType] = ( + UNSET + ) + """Components that exist within this job.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class MatillionJobNested(AssetNested): + """MatillionJob in nested API format for high-performance serialization.""" + + attributes: Union[MatillionJobAttributes, UnsetType] = UNSET + relationship_attributes: Union[MatillionJobRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + MatillionJobRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + MatillionJobRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_MATILLION_JOB_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "matillion_project", + "matillion_components", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_matillion_job_attrs( + attrs: MatillionJobAttributes, obj: MatillionJob +) -> None: + """Populate MatillionJob-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.matillion_job_type = obj.matillion_job_type + attrs.matillion_job_path = obj.matillion_job_path + attrs.matillion_job_component_count = obj.matillion_job_component_count + attrs.matillion_job_schedule = obj.matillion_job_schedule + attrs.matillion_project_name = obj.matillion_project_name + attrs.matillion_project_qualified_name = obj.matillion_project_qualified_name + attrs.matillion_version = obj.matillion_version + + +def _extract_matillion_job_attrs(attrs: MatillionJobAttributes) -> dict: + """Extract all MatillionJob attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["matillion_job_type"] = attrs.matillion_job_type + result["matillion_job_path"] = attrs.matillion_job_path + result["matillion_job_component_count"] = attrs.matillion_job_component_count + result["matillion_job_schedule"] = attrs.matillion_job_schedule + result["matillion_project_name"] = attrs.matillion_project_name + result["matillion_project_qualified_name"] = attrs.matillion_project_qualified_name + result["matillion_version"] = attrs.matillion_version + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _matillion_job_to_nested(matillion_job: MatillionJob) -> MatillionJobNested: + """Convert flat MatillionJob to nested format.""" + attrs = MatillionJobAttributes() + _populate_matillion_job_attrs(attrs, matillion_job) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + matillion_job, _MATILLION_JOB_REL_FIELDS, MatillionJobRelationshipAttributes + ) + return MatillionJobNested( + guid=matillion_job.guid, + type_name=matillion_job.type_name, + status=matillion_job.status, + version=matillion_job.version, + create_time=matillion_job.create_time, + update_time=matillion_job.update_time, + created_by=matillion_job.created_by, + updated_by=matillion_job.updated_by, + classifications=matillion_job.classifications, + classification_names=matillion_job.classification_names, + meanings=matillion_job.meanings, + labels=matillion_job.labels, + business_attributes=matillion_job.business_attributes, + custom_attributes=matillion_job.custom_attributes, + pending_tasks=matillion_job.pending_tasks, + proxy=matillion_job.proxy, + is_incomplete=matillion_job.is_incomplete, + provenance_type=matillion_job.provenance_type, + home_id=matillion_job.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _matillion_job_from_nested(nested: MatillionJobNested) -> MatillionJob: + """Convert nested format to flat MatillionJob.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else MatillionJobAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _MATILLION_JOB_REL_FIELDS, + MatillionJobRelationshipAttributes, + ) + return MatillionJob( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_matillion_job_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _matillion_job_to_nested_bytes(matillion_job: MatillionJob, serde: Serde) -> bytes: + """Convert flat MatillionJob to nested JSON bytes.""" + return serde.encode(_matillion_job_to_nested(matillion_job)) + + +def _matillion_job_from_nested_bytes(data: bytes, serde: Serde) -> MatillionJob: + """Convert nested JSON bytes to flat MatillionJob.""" + nested = serde.decode(data, MatillionJobNested) + return _matillion_job_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +MatillionJob.MATILLION_JOB_TYPE = KeywordField("matillionJobType", "matillionJobType") +MatillionJob.MATILLION_JOB_PATH = KeywordTextField( + "matillionJobPath", "matillionJobPath", "matillionJobPath.text" +) +MatillionJob.MATILLION_JOB_COMPONENT_COUNT = NumericField( + "matillionJobComponentCount", "matillionJobComponentCount" +) +MatillionJob.MATILLION_JOB_SCHEDULE = KeywordField( + "matillionJobSchedule", "matillionJobSchedule" +) +MatillionJob.MATILLION_PROJECT_NAME = KeywordTextField( + "matillionProjectName", "matillionProjectName", "matillionProjectName.text" +) +MatillionJob.MATILLION_PROJECT_QUALIFIED_NAME = KeywordTextField( + "matillionProjectQualifiedName", + "matillionProjectQualifiedName", + "matillionProjectQualifiedName.text", +) +MatillionJob.MATILLION_VERSION = KeywordField("matillionVersion", "matillionVersion") +MatillionJob.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +MatillionJob.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +MatillionJob.ANOMALO_CHECKS = RelationField("anomaloChecks") +MatillionJob.APPLICATION = RelationField("application") +MatillionJob.APPLICATION_FIELD = RelationField("applicationField") +MatillionJob.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +MatillionJob.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +MatillionJob.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +MatillionJob.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +MatillionJob.METRICS = RelationField("metrics") +MatillionJob.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +MatillionJob.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +MatillionJob.MEANINGS = RelationField("meanings") +MatillionJob.MATILLION_PROJECT = RelationField("matillionProject") +MatillionJob.MATILLION_COMPONENTS = RelationField("matillionComponents") +MatillionJob.MC_MONITORS = RelationField("mcMonitors") +MatillionJob.MC_INCIDENTS = RelationField("mcIncidents") +MatillionJob.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +MatillionJob.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +MatillionJob.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +MatillionJob.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +MatillionJob.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +MatillionJob.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +MatillionJob.FILES = RelationField("files") +MatillionJob.LINKS = RelationField("links") +MatillionJob.README = RelationField("readme") +MatillionJob.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +MatillionJob.SODA_CHECKS = RelationField("sodaChecks") +MatillionJob.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +MatillionJob.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/matillion_project.py b/pyatlan_v9/model/assets/matillion_project.py new file mode 100644 index 000000000..4081d10c4 --- /dev/null +++ b/pyatlan_v9/model/assets/matillion_project.py @@ -0,0 +1,646 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +MatillionProject asset model with flattened inheritance. + +This module provides: +- MatillionProject: Flat asset class (easy to use) +- MatillionProjectAttributes: Nested attributes struct (extends AssetAttributes) +- MatillionProjectNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .matillion_related import RelatedMatillionGroup, RelatedMatillionJob + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class MatillionProject(Asset): + """ + Instance of a Matillion project in Atlan. A project in Matillion is a logical grouping of configuration settings and jobs which are responsible for data processing and transformation. + """ + + MATILLION_VERSIONS: ClassVar[Any] = None + MATILLION_ENVIRONMENTS: ClassVar[Any] = None + MATILLION_PROJECT_JOB_COUNT: ClassVar[Any] = None + MATILLION_GROUP_NAME: ClassVar[Any] = None + MATILLION_GROUP_QUALIFIED_NAME: ClassVar[Any] = None + MATILLION_VERSION: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MATILLION_GROUP: ClassVar[Any] = None + MATILLION_JOBS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "MatillionProject" + + matillion_versions: Union[List[str], None, UnsetType] = UNSET + """List of versions in the project.""" + + matillion_environments: Union[List[str], None, UnsetType] = UNSET + """List of environments in the project.""" + + matillion_project_job_count: Union[int, None, UnsetType] = UNSET + """Number of jobs in the project.""" + + matillion_group_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Matillion group to which the project belongs.""" + + matillion_group_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the Matillion group to which the project belongs.""" + + matillion_version: Union[str, None, UnsetType] = UNSET + """Current point in time state of a project.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + matillion_group: Union[RelatedMatillionGroup, None, UnsetType] = UNSET + """Matillion group in which the project exists.""" + + matillion_jobs: Union[List[RelatedMatillionJob], None, UnsetType] = UNSET + """Jobs that exist within this project.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "MatillionProject" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _matillion_project_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> MatillionProject: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + MatillionProject instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _matillion_project_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class MatillionProjectAttributes(AssetAttributes): + """MatillionProject-specific attributes for nested API format.""" + + matillion_versions: Union[List[str], None, UnsetType] = UNSET + """List of versions in the project.""" + + matillion_environments: Union[List[str], None, UnsetType] = UNSET + """List of environments in the project.""" + + matillion_project_job_count: Union[int, None, UnsetType] = UNSET + """Number of jobs in the project.""" + + matillion_group_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Matillion group to which the project belongs.""" + + matillion_group_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the Matillion group to which the project belongs.""" + + matillion_version: Union[str, None, UnsetType] = UNSET + """Current point in time state of a project.""" + + +class MatillionProjectRelationshipAttributes(AssetRelationshipAttributes): + """MatillionProject-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + matillion_group: Union[RelatedMatillionGroup, None, UnsetType] = UNSET + """Matillion group in which the project exists.""" + + matillion_jobs: Union[List[RelatedMatillionJob], None, UnsetType] = UNSET + """Jobs that exist within this project.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class MatillionProjectNested(AssetNested): + """MatillionProject in nested API format for high-performance serialization.""" + + attributes: Union[MatillionProjectAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + MatillionProjectRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + MatillionProjectRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + MatillionProjectRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_MATILLION_PROJECT_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "matillion_group", + "matillion_jobs", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_matillion_project_attrs( + attrs: MatillionProjectAttributes, obj: MatillionProject +) -> None: + """Populate MatillionProject-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.matillion_versions = obj.matillion_versions + attrs.matillion_environments = obj.matillion_environments + attrs.matillion_project_job_count = obj.matillion_project_job_count + attrs.matillion_group_name = obj.matillion_group_name + attrs.matillion_group_qualified_name = obj.matillion_group_qualified_name + attrs.matillion_version = obj.matillion_version + + +def _extract_matillion_project_attrs(attrs: MatillionProjectAttributes) -> dict: + """Extract all MatillionProject attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["matillion_versions"] = attrs.matillion_versions + result["matillion_environments"] = attrs.matillion_environments + result["matillion_project_job_count"] = attrs.matillion_project_job_count + result["matillion_group_name"] = attrs.matillion_group_name + result["matillion_group_qualified_name"] = attrs.matillion_group_qualified_name + result["matillion_version"] = attrs.matillion_version + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _matillion_project_to_nested( + matillion_project: MatillionProject, +) -> MatillionProjectNested: + """Convert flat MatillionProject to nested format.""" + attrs = MatillionProjectAttributes() + _populate_matillion_project_attrs(attrs, matillion_project) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + matillion_project, + _MATILLION_PROJECT_REL_FIELDS, + MatillionProjectRelationshipAttributes, + ) + return MatillionProjectNested( + guid=matillion_project.guid, + type_name=matillion_project.type_name, + status=matillion_project.status, + version=matillion_project.version, + create_time=matillion_project.create_time, + update_time=matillion_project.update_time, + created_by=matillion_project.created_by, + updated_by=matillion_project.updated_by, + classifications=matillion_project.classifications, + classification_names=matillion_project.classification_names, + meanings=matillion_project.meanings, + labels=matillion_project.labels, + business_attributes=matillion_project.business_attributes, + custom_attributes=matillion_project.custom_attributes, + pending_tasks=matillion_project.pending_tasks, + proxy=matillion_project.proxy, + is_incomplete=matillion_project.is_incomplete, + provenance_type=matillion_project.provenance_type, + home_id=matillion_project.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _matillion_project_from_nested(nested: MatillionProjectNested) -> MatillionProject: + """Convert nested format to flat MatillionProject.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else MatillionProjectAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _MATILLION_PROJECT_REL_FIELDS, + MatillionProjectRelationshipAttributes, + ) + return MatillionProject( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_matillion_project_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _matillion_project_to_nested_bytes( + matillion_project: MatillionProject, serde: Serde +) -> bytes: + """Convert flat MatillionProject to nested JSON bytes.""" + return serde.encode(_matillion_project_to_nested(matillion_project)) + + +def _matillion_project_from_nested_bytes(data: bytes, serde: Serde) -> MatillionProject: + """Convert nested JSON bytes to flat MatillionProject.""" + nested = serde.decode(data, MatillionProjectNested) + return _matillion_project_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +MatillionProject.MATILLION_VERSIONS = KeywordField( + "matillionVersions", "matillionVersions" +) +MatillionProject.MATILLION_ENVIRONMENTS = KeywordField( + "matillionEnvironments", "matillionEnvironments" +) +MatillionProject.MATILLION_PROJECT_JOB_COUNT = NumericField( + "matillionProjectJobCount", "matillionProjectJobCount" +) +MatillionProject.MATILLION_GROUP_NAME = KeywordTextField( + "matillionGroupName", "matillionGroupName", "matillionGroupName.text" +) +MatillionProject.MATILLION_GROUP_QUALIFIED_NAME = KeywordTextField( + "matillionGroupQualifiedName", + "matillionGroupQualifiedName", + "matillionGroupQualifiedName.text", +) +MatillionProject.MATILLION_VERSION = KeywordField( + "matillionVersion", "matillionVersion" +) +MatillionProject.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +MatillionProject.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +MatillionProject.ANOMALO_CHECKS = RelationField("anomaloChecks") +MatillionProject.APPLICATION = RelationField("application") +MatillionProject.APPLICATION_FIELD = RelationField("applicationField") +MatillionProject.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +MatillionProject.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +MatillionProject.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +MatillionProject.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +MatillionProject.METRICS = RelationField("metrics") +MatillionProject.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +MatillionProject.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +MatillionProject.MEANINGS = RelationField("meanings") +MatillionProject.MATILLION_GROUP = RelationField("matillionGroup") +MatillionProject.MATILLION_JOBS = RelationField("matillionJobs") +MatillionProject.MC_MONITORS = RelationField("mcMonitors") +MatillionProject.MC_INCIDENTS = RelationField("mcIncidents") +MatillionProject.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +MatillionProject.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +MatillionProject.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +MatillionProject.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +MatillionProject.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +MatillionProject.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +MatillionProject.FILES = RelationField("files") +MatillionProject.LINKS = RelationField("links") +MatillionProject.README = RelationField("readme") +MatillionProject.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +MatillionProject.SODA_CHECKS = RelationField("sodaChecks") +MatillionProject.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +MatillionProject.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/matillion_related.py b/pyatlan_v9/model/assets/matillion_related.py new file mode 100644 index 000000000..ee7a79dfc --- /dev/null +++ b/pyatlan_v9/model/assets/matillion_related.py @@ -0,0 +1,165 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Matillion module. + +This module contains all Related{Type} classes for the Matillion type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedCatalog +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedMatillion", + "RelatedMatillionGroup", + "RelatedMatillionProject", + "RelatedMatillionJob", + "RelatedMatillionComponent", +] + + +class RelatedMatillion(RelatedCatalog): + """ + Related entity reference for Matillion assets. + + Extends RelatedCatalog with Matillion-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Matillion" so it serializes correctly + + matillion_version: Union[str, None, UnsetType] = UNSET + """Current point in time state of a project.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Matillion" + + +class RelatedMatillionGroup(RelatedMatillion): + """ + Related entity reference for MatillionGroup assets. + + Extends RelatedMatillion with MatillionGroup-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "MatillionGroup" so it serializes correctly + + matillion_project_count: Union[int, None, UnsetType] = UNSET + """Number of projects within the group.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "MatillionGroup" + + +class RelatedMatillionProject(RelatedMatillion): + """ + Related entity reference for MatillionProject assets. + + Extends RelatedMatillion with MatillionProject-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "MatillionProject" so it serializes correctly + + matillion_versions: Union[List[str], None, UnsetType] = UNSET + """List of versions in the project.""" + + matillion_environments: Union[List[str], None, UnsetType] = UNSET + """List of environments in the project.""" + + matillion_project_job_count: Union[int, None, UnsetType] = UNSET + """Number of jobs in the project.""" + + matillion_group_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Matillion group to which the project belongs.""" + + matillion_group_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the Matillion group to which the project belongs.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "MatillionProject" + + +class RelatedMatillionJob(RelatedMatillion): + """ + Related entity reference for MatillionJob assets. + + Extends RelatedMatillion with MatillionJob-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "MatillionJob" so it serializes correctly + + matillion_job_type: Union[str, None, UnsetType] = UNSET + """Type of the job, for example: orchestration or transformation.""" + + matillion_job_path: Union[str, None, UnsetType] = UNSET + """Path of the job within the project. Jobs can be managed at multiple folder levels within a project.""" + + matillion_job_component_count: Union[int, None, UnsetType] = UNSET + """Number of components within the job.""" + + matillion_job_schedule: Union[str, None, UnsetType] = UNSET + """How the job is scheduled, for example: weekly or monthly.""" + + matillion_project_name: Union[str, None, UnsetType] = UNSET + """Simple name of the project to which the job belongs.""" + + matillion_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project to which the job belongs.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "MatillionJob" + + +class RelatedMatillionComponent(RelatedMatillion): + """ + Related entity reference for MatillionComponent assets. + + Extends RelatedMatillion with MatillionComponent-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "MatillionComponent" so it serializes correctly + + matillion_component_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the component in Matillion.""" + + matillion_component_implementation_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the type of the component in Matillion.""" + + matillion_component_linked_job: Union[Dict[str, str], None, UnsetType] = UNSET + """Job details of the job to which the component internally links.""" + + matillion_component_last_run_status: Union[str, None, UnsetType] = UNSET + """Latest run status of the component within a job.""" + + matillion_component_last_five_run_status: Union[str, None, UnsetType] = UNSET + """Last five run statuses of the component within a job.""" + + matillion_component_sqls: Union[List[str], None, UnsetType] = UNSET + """SQL queries used by the component.""" + + matillion_job_name: Union[str, None, UnsetType] = UNSET + """Simple name of the job to which the component belongs.""" + + matillion_job_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the job to which the component belongs.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "MatillionComponent" diff --git a/pyatlan_v9/model/assets/mc_incident.py b/pyatlan_v9/model/assets/mc_incident.py new file mode 100644 index 000000000..2848febae --- /dev/null +++ b/pyatlan_v9/model/assets/mc_incident.py @@ -0,0 +1,667 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +MCIncident asset model with flattened inheritance. + +This module provides: +- MCIncident: Flat asset class (easy to use) +- MCIncidentAttributes: Nested attributes struct (extends AssetAttributes) +- MCIncidentNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .asset_related import RelatedAsset +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class MCIncident(Asset): + """ + Instance of a Monte Carlo incident in Atlan. + """ + + MC_INCIDENT_ID: ClassVar[Any] = None + MC_INCIDENT_TYPE: ClassVar[Any] = None + MC_INCIDENT_SUB_TYPES: ClassVar[Any] = None + MC_INCIDENT_SEVERITY: ClassVar[Any] = None + MC_INCIDENT_PRIORITY: ClassVar[Any] = None + MC_INCIDENT_STATE: ClassVar[Any] = None + MC_INCIDENT_WAREHOUSE: ClassVar[Any] = None + MC_LABELS: ClassVar[Any] = None + MC_ASSET_QUALIFIED_NAMES: ClassVar[Any] = None + DQ_IS_PART_OF_CONTRACT: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITOR: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENT_ASSETS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "MCIncident" + + mc_incident_id: Union[str, None, UnsetType] = UNSET + """Identifier of this incident, from Monte Carlo.""" + + mc_incident_type: Union[str, None, UnsetType] = UNSET + """Type of this incident.""" + + mc_incident_sub_types: Union[List[str], None, UnsetType] = UNSET + """Subtypes of this incident.""" + + mc_incident_severity: Union[str, None, UnsetType] = UNSET + """Severity of this incident.""" + + mc_incident_priority: Union[str, None, UnsetType] = UNSET + """Priority of this incident inherited from monitor.""" + + mc_incident_state: Union[str, None, UnsetType] = UNSET + """State of this incident.""" + + mc_incident_warehouse: Union[str, None, UnsetType] = UNSET + """Name of this incident's warehouse.""" + + mc_labels: Union[List[str], None, UnsetType] = UNSET + """List of labels for this Monte Carlo asset.""" + + mc_asset_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of unique names of assets that are part of this Monte Carlo asset.""" + + dq_is_part_of_contract: Union[bool, None, UnsetType] = UNSET + """Whether this data quality is part of contract (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitor: Union[RelatedMCMonitor, None, UnsetType] = UNSET + """Monitor in which this incident exists.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incident_assets: Union[List[RelatedAsset], None, UnsetType] = UNSET + """""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "MCIncident" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _mc_incident_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> MCIncident: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + MCIncident instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _mc_incident_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class MCIncidentAttributes(AssetAttributes): + """MCIncident-specific attributes for nested API format.""" + + mc_incident_id: Union[str, None, UnsetType] = UNSET + """Identifier of this incident, from Monte Carlo.""" + + mc_incident_type: Union[str, None, UnsetType] = UNSET + """Type of this incident.""" + + mc_incident_sub_types: Union[List[str], None, UnsetType] = UNSET + """Subtypes of this incident.""" + + mc_incident_severity: Union[str, None, UnsetType] = UNSET + """Severity of this incident.""" + + mc_incident_priority: Union[str, None, UnsetType] = UNSET + """Priority of this incident inherited from monitor.""" + + mc_incident_state: Union[str, None, UnsetType] = UNSET + """State of this incident.""" + + mc_incident_warehouse: Union[str, None, UnsetType] = UNSET + """Name of this incident's warehouse.""" + + mc_labels: Union[List[str], None, UnsetType] = UNSET + """List of labels for this Monte Carlo asset.""" + + mc_asset_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of unique names of assets that are part of this Monte Carlo asset.""" + + dq_is_part_of_contract: Union[bool, None, UnsetType] = UNSET + """Whether this data quality is part of contract (true) or not (false).""" + + +class MCIncidentRelationshipAttributes(AssetRelationshipAttributes): + """MCIncident-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitor: Union[RelatedMCMonitor, None, UnsetType] = UNSET + """Monitor in which this incident exists.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incident_assets: Union[List[RelatedAsset], None, UnsetType] = UNSET + """""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class MCIncidentNested(AssetNested): + """MCIncident in nested API format for high-performance serialization.""" + + attributes: Union[MCIncidentAttributes, UnsetType] = UNSET + relationship_attributes: Union[MCIncidentRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + MCIncidentRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + MCIncidentRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_MC_INCIDENT_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitor", + "mc_monitors", + "mc_incident_assets", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_mc_incident_attrs(attrs: MCIncidentAttributes, obj: MCIncident) -> None: + """Populate MCIncident-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.mc_incident_id = obj.mc_incident_id + attrs.mc_incident_type = obj.mc_incident_type + attrs.mc_incident_sub_types = obj.mc_incident_sub_types + attrs.mc_incident_severity = obj.mc_incident_severity + attrs.mc_incident_priority = obj.mc_incident_priority + attrs.mc_incident_state = obj.mc_incident_state + attrs.mc_incident_warehouse = obj.mc_incident_warehouse + attrs.mc_labels = obj.mc_labels + attrs.mc_asset_qualified_names = obj.mc_asset_qualified_names + attrs.dq_is_part_of_contract = obj.dq_is_part_of_contract + + +def _extract_mc_incident_attrs(attrs: MCIncidentAttributes) -> dict: + """Extract all MCIncident attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["mc_incident_id"] = attrs.mc_incident_id + result["mc_incident_type"] = attrs.mc_incident_type + result["mc_incident_sub_types"] = attrs.mc_incident_sub_types + result["mc_incident_severity"] = attrs.mc_incident_severity + result["mc_incident_priority"] = attrs.mc_incident_priority + result["mc_incident_state"] = attrs.mc_incident_state + result["mc_incident_warehouse"] = attrs.mc_incident_warehouse + result["mc_labels"] = attrs.mc_labels + result["mc_asset_qualified_names"] = attrs.mc_asset_qualified_names + result["dq_is_part_of_contract"] = attrs.dq_is_part_of_contract + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _mc_incident_to_nested(mc_incident: MCIncident) -> MCIncidentNested: + """Convert flat MCIncident to nested format.""" + attrs = MCIncidentAttributes() + _populate_mc_incident_attrs(attrs, mc_incident) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + mc_incident, _MC_INCIDENT_REL_FIELDS, MCIncidentRelationshipAttributes + ) + return MCIncidentNested( + guid=mc_incident.guid, + type_name=mc_incident.type_name, + status=mc_incident.status, + version=mc_incident.version, + create_time=mc_incident.create_time, + update_time=mc_incident.update_time, + created_by=mc_incident.created_by, + updated_by=mc_incident.updated_by, + classifications=mc_incident.classifications, + classification_names=mc_incident.classification_names, + meanings=mc_incident.meanings, + labels=mc_incident.labels, + business_attributes=mc_incident.business_attributes, + custom_attributes=mc_incident.custom_attributes, + pending_tasks=mc_incident.pending_tasks, + proxy=mc_incident.proxy, + is_incomplete=mc_incident.is_incomplete, + provenance_type=mc_incident.provenance_type, + home_id=mc_incident.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _mc_incident_from_nested(nested: MCIncidentNested) -> MCIncident: + """Convert nested format to flat MCIncident.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else MCIncidentAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _MC_INCIDENT_REL_FIELDS, + MCIncidentRelationshipAttributes, + ) + return MCIncident( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_mc_incident_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _mc_incident_to_nested_bytes(mc_incident: MCIncident, serde: Serde) -> bytes: + """Convert flat MCIncident to nested JSON bytes.""" + return serde.encode(_mc_incident_to_nested(mc_incident)) + + +def _mc_incident_from_nested_bytes(data: bytes, serde: Serde) -> MCIncident: + """Convert nested JSON bytes to flat MCIncident.""" + nested = serde.decode(data, MCIncidentNested) + return _mc_incident_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + RelationField, +) + +MCIncident.MC_INCIDENT_ID = KeywordField("mcIncidentId", "mcIncidentId") +MCIncident.MC_INCIDENT_TYPE = KeywordField("mcIncidentType", "mcIncidentType") +MCIncident.MC_INCIDENT_SUB_TYPES = KeywordField( + "mcIncidentSubTypes", "mcIncidentSubTypes" +) +MCIncident.MC_INCIDENT_SEVERITY = KeywordField( + "mcIncidentSeverity", "mcIncidentSeverity" +) +MCIncident.MC_INCIDENT_PRIORITY = KeywordField( + "mcIncidentPriority", "mcIncidentPriority" +) +MCIncident.MC_INCIDENT_STATE = KeywordField("mcIncidentState", "mcIncidentState") +MCIncident.MC_INCIDENT_WAREHOUSE = KeywordField( + "mcIncidentWarehouse", "mcIncidentWarehouse" +) +MCIncident.MC_LABELS = KeywordField("mcLabels", "mcLabels") +MCIncident.MC_ASSET_QUALIFIED_NAMES = KeywordField( + "mcAssetQualifiedNames", "mcAssetQualifiedNames" +) +MCIncident.DQ_IS_PART_OF_CONTRACT = BooleanField( + "dqIsPartOfContract", "dqIsPartOfContract" +) +MCIncident.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +MCIncident.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +MCIncident.ANOMALO_CHECKS = RelationField("anomaloChecks") +MCIncident.APPLICATION = RelationField("application") +MCIncident.APPLICATION_FIELD = RelationField("applicationField") +MCIncident.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +MCIncident.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +MCIncident.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +MCIncident.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +MCIncident.METRICS = RelationField("metrics") +MCIncident.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +MCIncident.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +MCIncident.MEANINGS = RelationField("meanings") +MCIncident.MC_MONITOR = RelationField("mcMonitor") +MCIncident.MC_MONITORS = RelationField("mcMonitors") +MCIncident.MC_INCIDENT_ASSETS = RelationField("mcIncidentAssets") +MCIncident.MC_INCIDENTS = RelationField("mcIncidents") +MCIncident.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +MCIncident.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +MCIncident.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +MCIncident.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +MCIncident.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +MCIncident.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +MCIncident.FILES = RelationField("files") +MCIncident.LINKS = RelationField("links") +MCIncident.README = RelationField("readme") +MCIncident.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +MCIncident.SODA_CHECKS = RelationField("sodaChecks") +MCIncident.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +MCIncident.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/mc_monitor.py b/pyatlan_v9/model/assets/mc_monitor.py new file mode 100644 index 000000000..d92823788 --- /dev/null +++ b/pyatlan_v9/model/assets/mc_monitor.py @@ -0,0 +1,825 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +MCMonitor asset model with flattened inheritance. + +This module provides: +- MCMonitor: Flat asset class (easy to use) +- MCMonitorAttributes: Nested attributes struct (extends AssetAttributes) +- MCMonitorNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .asset_related import RelatedAsset +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class MCMonitor(Asset): + """ + Instance of a Monte Carlo monitor in Atlan. + """ + + MC_MONITOR_ID: ClassVar[Any] = None + MC_MONITOR_STATUS: ClassVar[Any] = None + MC_MONITOR_TYPE: ClassVar[Any] = None + MC_MONITOR_WAREHOUSE: ClassVar[Any] = None + MC_MONITOR_SCHEDULE_TYPE: ClassVar[Any] = None + MC_MONITOR_NAMESPACE: ClassVar[Any] = None + MC_MONITOR_RULE_TYPE: ClassVar[Any] = None + MC_MONITOR_RULE_CUSTOM_SQL: ClassVar[Any] = None + MC_MONITOR_RULE_SCHEDULE_CONFIG: ClassVar[Any] = None + MC_MONITOR_RULE_SCHEDULE_CONFIG_HUMANIZED: ClassVar[Any] = None + MC_MONITOR_ALERT_CONDITION: ClassVar[Any] = None + MC_MONITOR_RULE_NEXT_EXECUTION_TIME: ClassVar[Any] = None + MC_MONITOR_RULE_PREVIOUS_EXECUTION_TIME: ClassVar[Any] = None + MC_MONITOR_RULE_COMPARISONS: ClassVar[Any] = None + MC_MONITOR_RULE_IS_SNOOZED: ClassVar[Any] = None + MC_MONITOR_BREACH_RATE: ClassVar[Any] = None + MC_MONITOR_INCIDENT_COUNT: ClassVar[Any] = None + MC_MONITOR_ALERT_COUNT: ClassVar[Any] = None + MC_MONITOR_PRIORITY: ClassVar[Any] = None + MC_MONITOR_IS_OOTB: ClassVar[Any] = None + MC_MONITOR_NOTIFICATION_CHANNELS: ClassVar[Any] = None + MC_LABELS: ClassVar[Any] = None + MC_ASSET_QUALIFIED_NAMES: ClassVar[Any] = None + DQ_IS_PART_OF_CONTRACT: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + MC_MONITOR_ASSETS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "MCMonitor" + + mc_monitor_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for this monitor, from Monte Carlo.""" + + mc_monitor_status: Union[str, None, UnsetType] = UNSET + """Status of this monitor.""" + + mc_monitor_type: Union[str, None, UnsetType] = UNSET + """Type of this monitor, for example: field health (stats) or dimension tracking (categories).""" + + mc_monitor_warehouse: Union[str, None, UnsetType] = UNSET + """Name of the warehouse for this monitor.""" + + mc_monitor_schedule_type: Union[str, None, UnsetType] = UNSET + """Type of schedule for this monitor, for example: fixed or dynamic.""" + + mc_monitor_namespace: Union[str, None, UnsetType] = UNSET + """Namespace of this monitor.""" + + mc_monitor_rule_type: Union[str, None, UnsetType] = UNSET + """Type of rule for this monitor.""" + + mc_monitor_rule_custom_sql: Union[str, None, UnsetType] = UNSET + """SQL code for custom SQL rules.""" + + mc_monitor_rule_schedule_config: Union[Dict[str, Any], None, UnsetType] = UNSET + """Schedule details for the rule.""" + + mc_monitor_rule_schedule_config_humanized: Union[str, None, UnsetType] = UNSET + """Readable description of the schedule for the rule.""" + + mc_monitor_alert_condition: Union[str, None, UnsetType] = UNSET + """Condition on which the monitor produces an alert.""" + + mc_monitor_rule_next_execution_time: Union[int, None, UnsetType] = UNSET + """Time at which the next execution of the rule should occur.""" + + mc_monitor_rule_previous_execution_time: Union[int, None, UnsetType] = UNSET + """Time at which the previous execution of the rule occurred.""" + + mc_monitor_rule_comparisons: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """Comparison logic used for the rule.""" + + mc_monitor_rule_is_snoozed: Union[bool, None, UnsetType] = UNSET + """Whether the rule is currently snoozed (true) or not (false).""" + + mc_monitor_breach_rate: Union[float, None, UnsetType] = UNSET + """Rate at which this monitor is breached.""" + + mc_monitor_incident_count: Union[int, None, UnsetType] = UNSET + """Number of incidents associated with this monitor.""" + + mc_monitor_alert_count: Union[int, None, UnsetType] = UNSET + """Number of alerts associated with this monitor.""" + + mc_monitor_priority: Union[str, None, UnsetType] = UNSET + """Priority of this monitor.""" + + mc_monitor_is_ootb: Union[bool, None, UnsetType] = UNSET + """Whether the monitor is OOTB or not""" + + mc_monitor_notification_channels: Union[List[str], None, UnsetType] = UNSET + """Channels through which notifications are sent for this monitor (e.g., email, slack, webhook).""" + + mc_labels: Union[List[str], None, UnsetType] = UNSET + """List of labels for this Monte Carlo asset.""" + + mc_asset_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of unique names of assets that are part of this Monte Carlo asset.""" + + dq_is_part_of_contract: Union[bool, None, UnsetType] = UNSET + """Whether this data quality is part of contract (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """Incidents that exist within this monitor.""" + + mc_monitor_assets: Union[List[RelatedAsset], None, UnsetType] = UNSET + """Assets impacted by this monitor.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "MCMonitor" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _mc_monitor_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> MCMonitor: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + MCMonitor instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _mc_monitor_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class MCMonitorAttributes(AssetAttributes): + """MCMonitor-specific attributes for nested API format.""" + + mc_monitor_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for this monitor, from Monte Carlo.""" + + mc_monitor_status: Union[str, None, UnsetType] = UNSET + """Status of this monitor.""" + + mc_monitor_type: Union[str, None, UnsetType] = UNSET + """Type of this monitor, for example: field health (stats) or dimension tracking (categories).""" + + mc_monitor_warehouse: Union[str, None, UnsetType] = UNSET + """Name of the warehouse for this monitor.""" + + mc_monitor_schedule_type: Union[str, None, UnsetType] = UNSET + """Type of schedule for this monitor, for example: fixed or dynamic.""" + + mc_monitor_namespace: Union[str, None, UnsetType] = UNSET + """Namespace of this monitor.""" + + mc_monitor_rule_type: Union[str, None, UnsetType] = UNSET + """Type of rule for this monitor.""" + + mc_monitor_rule_custom_sql: Union[str, None, UnsetType] = UNSET + """SQL code for custom SQL rules.""" + + mc_monitor_rule_schedule_config: Union[Dict[str, Any], None, UnsetType] = UNSET + """Schedule details for the rule.""" + + mc_monitor_rule_schedule_config_humanized: Union[str, None, UnsetType] = UNSET + """Readable description of the schedule for the rule.""" + + mc_monitor_alert_condition: Union[str, None, UnsetType] = UNSET + """Condition on which the monitor produces an alert.""" + + mc_monitor_rule_next_execution_time: Union[int, None, UnsetType] = UNSET + """Time at which the next execution of the rule should occur.""" + + mc_monitor_rule_previous_execution_time: Union[int, None, UnsetType] = UNSET + """Time at which the previous execution of the rule occurred.""" + + mc_monitor_rule_comparisons: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """Comparison logic used for the rule.""" + + mc_monitor_rule_is_snoozed: Union[bool, None, UnsetType] = UNSET + """Whether the rule is currently snoozed (true) or not (false).""" + + mc_monitor_breach_rate: Union[float, None, UnsetType] = UNSET + """Rate at which this monitor is breached.""" + + mc_monitor_incident_count: Union[int, None, UnsetType] = UNSET + """Number of incidents associated with this monitor.""" + + mc_monitor_alert_count: Union[int, None, UnsetType] = UNSET + """Number of alerts associated with this monitor.""" + + mc_monitor_priority: Union[str, None, UnsetType] = UNSET + """Priority of this monitor.""" + + mc_monitor_is_ootb: Union[bool, None, UnsetType] = UNSET + """Whether the monitor is OOTB or not""" + + mc_monitor_notification_channels: Union[List[str], None, UnsetType] = UNSET + """Channels through which notifications are sent for this monitor (e.g., email, slack, webhook).""" + + mc_labels: Union[List[str], None, UnsetType] = UNSET + """List of labels for this Monte Carlo asset.""" + + mc_asset_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of unique names of assets that are part of this Monte Carlo asset.""" + + dq_is_part_of_contract: Union[bool, None, UnsetType] = UNSET + """Whether this data quality is part of contract (true) or not (false).""" + + +class MCMonitorRelationshipAttributes(AssetRelationshipAttributes): + """MCMonitor-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """Incidents that exist within this monitor.""" + + mc_monitor_assets: Union[List[RelatedAsset], None, UnsetType] = UNSET + """Assets impacted by this monitor.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class MCMonitorNested(AssetNested): + """MCMonitor in nested API format for high-performance serialization.""" + + attributes: Union[MCMonitorAttributes, UnsetType] = UNSET + relationship_attributes: Union[MCMonitorRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + MCMonitorRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + MCMonitorRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_MC_MONITOR_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_incidents", + "mc_monitor_assets", + "mc_monitors", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_mc_monitor_attrs(attrs: MCMonitorAttributes, obj: MCMonitor) -> None: + """Populate MCMonitor-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.mc_monitor_id = obj.mc_monitor_id + attrs.mc_monitor_status = obj.mc_monitor_status + attrs.mc_monitor_type = obj.mc_monitor_type + attrs.mc_monitor_warehouse = obj.mc_monitor_warehouse + attrs.mc_monitor_schedule_type = obj.mc_monitor_schedule_type + attrs.mc_monitor_namespace = obj.mc_monitor_namespace + attrs.mc_monitor_rule_type = obj.mc_monitor_rule_type + attrs.mc_monitor_rule_custom_sql = obj.mc_monitor_rule_custom_sql + attrs.mc_monitor_rule_schedule_config = obj.mc_monitor_rule_schedule_config + attrs.mc_monitor_rule_schedule_config_humanized = ( + obj.mc_monitor_rule_schedule_config_humanized + ) + attrs.mc_monitor_alert_condition = obj.mc_monitor_alert_condition + attrs.mc_monitor_rule_next_execution_time = obj.mc_monitor_rule_next_execution_time + attrs.mc_monitor_rule_previous_execution_time = ( + obj.mc_monitor_rule_previous_execution_time + ) + attrs.mc_monitor_rule_comparisons = obj.mc_monitor_rule_comparisons + attrs.mc_monitor_rule_is_snoozed = obj.mc_monitor_rule_is_snoozed + attrs.mc_monitor_breach_rate = obj.mc_monitor_breach_rate + attrs.mc_monitor_incident_count = obj.mc_monitor_incident_count + attrs.mc_monitor_alert_count = obj.mc_monitor_alert_count + attrs.mc_monitor_priority = obj.mc_monitor_priority + attrs.mc_monitor_is_ootb = obj.mc_monitor_is_ootb + attrs.mc_monitor_notification_channels = obj.mc_monitor_notification_channels + attrs.mc_labels = obj.mc_labels + attrs.mc_asset_qualified_names = obj.mc_asset_qualified_names + attrs.dq_is_part_of_contract = obj.dq_is_part_of_contract + + +def _extract_mc_monitor_attrs(attrs: MCMonitorAttributes) -> dict: + """Extract all MCMonitor attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["mc_monitor_id"] = attrs.mc_monitor_id + result["mc_monitor_status"] = attrs.mc_monitor_status + result["mc_monitor_type"] = attrs.mc_monitor_type + result["mc_monitor_warehouse"] = attrs.mc_monitor_warehouse + result["mc_monitor_schedule_type"] = attrs.mc_monitor_schedule_type + result["mc_monitor_namespace"] = attrs.mc_monitor_namespace + result["mc_monitor_rule_type"] = attrs.mc_monitor_rule_type + result["mc_monitor_rule_custom_sql"] = attrs.mc_monitor_rule_custom_sql + result["mc_monitor_rule_schedule_config"] = attrs.mc_monitor_rule_schedule_config + result["mc_monitor_rule_schedule_config_humanized"] = ( + attrs.mc_monitor_rule_schedule_config_humanized + ) + result["mc_monitor_alert_condition"] = attrs.mc_monitor_alert_condition + result["mc_monitor_rule_next_execution_time"] = ( + attrs.mc_monitor_rule_next_execution_time + ) + result["mc_monitor_rule_previous_execution_time"] = ( + attrs.mc_monitor_rule_previous_execution_time + ) + result["mc_monitor_rule_comparisons"] = attrs.mc_monitor_rule_comparisons + result["mc_monitor_rule_is_snoozed"] = attrs.mc_monitor_rule_is_snoozed + result["mc_monitor_breach_rate"] = attrs.mc_monitor_breach_rate + result["mc_monitor_incident_count"] = attrs.mc_monitor_incident_count + result["mc_monitor_alert_count"] = attrs.mc_monitor_alert_count + result["mc_monitor_priority"] = attrs.mc_monitor_priority + result["mc_monitor_is_ootb"] = attrs.mc_monitor_is_ootb + result["mc_monitor_notification_channels"] = attrs.mc_monitor_notification_channels + result["mc_labels"] = attrs.mc_labels + result["mc_asset_qualified_names"] = attrs.mc_asset_qualified_names + result["dq_is_part_of_contract"] = attrs.dq_is_part_of_contract + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _mc_monitor_to_nested(mc_monitor: MCMonitor) -> MCMonitorNested: + """Convert flat MCMonitor to nested format.""" + attrs = MCMonitorAttributes() + _populate_mc_monitor_attrs(attrs, mc_monitor) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + mc_monitor, _MC_MONITOR_REL_FIELDS, MCMonitorRelationshipAttributes + ) + return MCMonitorNested( + guid=mc_monitor.guid, + type_name=mc_monitor.type_name, + status=mc_monitor.status, + version=mc_monitor.version, + create_time=mc_monitor.create_time, + update_time=mc_monitor.update_time, + created_by=mc_monitor.created_by, + updated_by=mc_monitor.updated_by, + classifications=mc_monitor.classifications, + classification_names=mc_monitor.classification_names, + meanings=mc_monitor.meanings, + labels=mc_monitor.labels, + business_attributes=mc_monitor.business_attributes, + custom_attributes=mc_monitor.custom_attributes, + pending_tasks=mc_monitor.pending_tasks, + proxy=mc_monitor.proxy, + is_incomplete=mc_monitor.is_incomplete, + provenance_type=mc_monitor.provenance_type, + home_id=mc_monitor.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _mc_monitor_from_nested(nested: MCMonitorNested) -> MCMonitor: + """Convert nested format to flat MCMonitor.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else MCMonitorAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _MC_MONITOR_REL_FIELDS, + MCMonitorRelationshipAttributes, + ) + return MCMonitor( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_mc_monitor_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _mc_monitor_to_nested_bytes(mc_monitor: MCMonitor, serde: Serde) -> bytes: + """Convert flat MCMonitor to nested JSON bytes.""" + return serde.encode(_mc_monitor_to_nested(mc_monitor)) + + +def _mc_monitor_from_nested_bytes(data: bytes, serde: Serde) -> MCMonitor: + """Convert nested JSON bytes to flat MCMonitor.""" + nested = serde.decode(data, MCMonitorNested) + return _mc_monitor_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +MCMonitor.MC_MONITOR_ID = KeywordField("mcMonitorId", "mcMonitorId") +MCMonitor.MC_MONITOR_STATUS = KeywordField("mcMonitorStatus", "mcMonitorStatus") +MCMonitor.MC_MONITOR_TYPE = KeywordField("mcMonitorType", "mcMonitorType") +MCMonitor.MC_MONITOR_WAREHOUSE = KeywordField( + "mcMonitorWarehouse", "mcMonitorWarehouse" +) +MCMonitor.MC_MONITOR_SCHEDULE_TYPE = KeywordField( + "mcMonitorScheduleType", "mcMonitorScheduleType" +) +MCMonitor.MC_MONITOR_NAMESPACE = KeywordTextField( + "mcMonitorNamespace", "mcMonitorNamespace", "mcMonitorNamespace.text" +) +MCMonitor.MC_MONITOR_RULE_TYPE = KeywordField("mcMonitorRuleType", "mcMonitorRuleType") +MCMonitor.MC_MONITOR_RULE_CUSTOM_SQL = KeywordField( + "mcMonitorRuleCustomSql", "mcMonitorRuleCustomSql" +) +MCMonitor.MC_MONITOR_RULE_SCHEDULE_CONFIG = KeywordField( + "mcMonitorRuleScheduleConfig", "mcMonitorRuleScheduleConfig" +) +MCMonitor.MC_MONITOR_RULE_SCHEDULE_CONFIG_HUMANIZED = KeywordField( + "mcMonitorRuleScheduleConfigHumanized", "mcMonitorRuleScheduleConfigHumanized" +) +MCMonitor.MC_MONITOR_ALERT_CONDITION = KeywordField( + "mcMonitorAlertCondition", "mcMonitorAlertCondition" +) +MCMonitor.MC_MONITOR_RULE_NEXT_EXECUTION_TIME = NumericField( + "mcMonitorRuleNextExecutionTime", "mcMonitorRuleNextExecutionTime" +) +MCMonitor.MC_MONITOR_RULE_PREVIOUS_EXECUTION_TIME = NumericField( + "mcMonitorRulePreviousExecutionTime", "mcMonitorRulePreviousExecutionTime" +) +MCMonitor.MC_MONITOR_RULE_COMPARISONS = KeywordField( + "mcMonitorRuleComparisons", "mcMonitorRuleComparisons" +) +MCMonitor.MC_MONITOR_RULE_IS_SNOOZED = BooleanField( + "mcMonitorRuleIsSnoozed", "mcMonitorRuleIsSnoozed" +) +MCMonitor.MC_MONITOR_BREACH_RATE = NumericField( + "mcMonitorBreachRate", "mcMonitorBreachRate" +) +MCMonitor.MC_MONITOR_INCIDENT_COUNT = NumericField( + "mcMonitorIncidentCount", "mcMonitorIncidentCount" +) +MCMonitor.MC_MONITOR_ALERT_COUNT = NumericField( + "mcMonitorAlertCount", "mcMonitorAlertCount" +) +MCMonitor.MC_MONITOR_PRIORITY = KeywordField("mcMonitorPriority", "mcMonitorPriority") +MCMonitor.MC_MONITOR_IS_OOTB = BooleanField("mcMonitorIsOotb", "mcMonitorIsOotb") +MCMonitor.MC_MONITOR_NOTIFICATION_CHANNELS = KeywordField( + "mcMonitorNotificationChannels", "mcMonitorNotificationChannels" +) +MCMonitor.MC_LABELS = KeywordField("mcLabels", "mcLabels") +MCMonitor.MC_ASSET_QUALIFIED_NAMES = KeywordField( + "mcAssetQualifiedNames", "mcAssetQualifiedNames" +) +MCMonitor.DQ_IS_PART_OF_CONTRACT = BooleanField( + "dqIsPartOfContract", "dqIsPartOfContract" +) +MCMonitor.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +MCMonitor.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +MCMonitor.ANOMALO_CHECKS = RelationField("anomaloChecks") +MCMonitor.APPLICATION = RelationField("application") +MCMonitor.APPLICATION_FIELD = RelationField("applicationField") +MCMonitor.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +MCMonitor.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +MCMonitor.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +MCMonitor.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +MCMonitor.METRICS = RelationField("metrics") +MCMonitor.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +MCMonitor.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +MCMonitor.MEANINGS = RelationField("meanings") +MCMonitor.MC_INCIDENTS = RelationField("mcIncidents") +MCMonitor.MC_MONITOR_ASSETS = RelationField("mcMonitorAssets") +MCMonitor.MC_MONITORS = RelationField("mcMonitors") +MCMonitor.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +MCMonitor.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +MCMonitor.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +MCMonitor.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +MCMonitor.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +MCMonitor.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +MCMonitor.FILES = RelationField("files") +MCMonitor.LINKS = RelationField("links") +MCMonitor.README = RelationField("readme") +MCMonitor.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +MCMonitor.SODA_CHECKS = RelationField("sodaChecks") +MCMonitor.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +MCMonitor.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/metabase.py b/pyatlan_v9/model/assets/metabase.py new file mode 100644 index 000000000..e05b3ce12 --- /dev/null +++ b/pyatlan_v9/model/assets/metabase.py @@ -0,0 +1,556 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Metabase asset model with flattened inheritance. + +This module provides: +- Metabase: Flat asset class (easy to use) +- MetabaseAttributes: Nested attributes struct (extends AssetAttributes) +- MetabaseNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Metabase(Asset): + """ + Base class for Metabase assets. + """ + + METABASE_COLLECTION_NAME: ClassVar[Any] = None + METABASE_COLLECTION_QUALIFIED_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Metabase" + + metabase_collection_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Metabase collection in which this asset exists.""" + + metabase_collection_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the Metabase collection in which this asset exists.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Metabase" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _metabase_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Metabase: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Metabase instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _metabase_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class MetabaseAttributes(AssetAttributes): + """Metabase-specific attributes for nested API format.""" + + metabase_collection_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Metabase collection in which this asset exists.""" + + metabase_collection_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the Metabase collection in which this asset exists.""" + + +class MetabaseRelationshipAttributes(AssetRelationshipAttributes): + """Metabase-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class MetabaseNested(AssetNested): + """Metabase in nested API format for high-performance serialization.""" + + attributes: Union[MetabaseAttributes, UnsetType] = UNSET + relationship_attributes: Union[MetabaseRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[MetabaseRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[MetabaseRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_METABASE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_metabase_attrs(attrs: MetabaseAttributes, obj: Metabase) -> None: + """Populate Metabase-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.metabase_collection_name = obj.metabase_collection_name + attrs.metabase_collection_qualified_name = obj.metabase_collection_qualified_name + + +def _extract_metabase_attrs(attrs: MetabaseAttributes) -> dict: + """Extract all Metabase attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["metabase_collection_name"] = attrs.metabase_collection_name + result["metabase_collection_qualified_name"] = ( + attrs.metabase_collection_qualified_name + ) + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _metabase_to_nested(metabase: Metabase) -> MetabaseNested: + """Convert flat Metabase to nested format.""" + attrs = MetabaseAttributes() + _populate_metabase_attrs(attrs, metabase) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + metabase, _METABASE_REL_FIELDS, MetabaseRelationshipAttributes + ) + return MetabaseNested( + guid=metabase.guid, + type_name=metabase.type_name, + status=metabase.status, + version=metabase.version, + create_time=metabase.create_time, + update_time=metabase.update_time, + created_by=metabase.created_by, + updated_by=metabase.updated_by, + classifications=metabase.classifications, + classification_names=metabase.classification_names, + meanings=metabase.meanings, + labels=metabase.labels, + business_attributes=metabase.business_attributes, + custom_attributes=metabase.custom_attributes, + pending_tasks=metabase.pending_tasks, + proxy=metabase.proxy, + is_incomplete=metabase.is_incomplete, + provenance_type=metabase.provenance_type, + home_id=metabase.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _metabase_from_nested(nested: MetabaseNested) -> Metabase: + """Convert nested format to flat Metabase.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else MetabaseAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _METABASE_REL_FIELDS, + MetabaseRelationshipAttributes, + ) + return Metabase( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_metabase_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _metabase_to_nested_bytes(metabase: Metabase, serde: Serde) -> bytes: + """Convert flat Metabase to nested JSON bytes.""" + return serde.encode(_metabase_to_nested(metabase)) + + +def _metabase_from_nested_bytes(data: bytes, serde: Serde) -> Metabase: + """Convert nested JSON bytes to flat Metabase.""" + nested = serde.decode(data, MetabaseNested) + return _metabase_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + RelationField, +) + +Metabase.METABASE_COLLECTION_NAME = KeywordField( + "metabaseCollectionName", "metabaseCollectionName" +) +Metabase.METABASE_COLLECTION_QUALIFIED_NAME = KeywordTextField( + "metabaseCollectionQualifiedName", + "metabaseCollectionQualifiedName", + "metabaseCollectionQualifiedName.text", +) +Metabase.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Metabase.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Metabase.ANOMALO_CHECKS = RelationField("anomaloChecks") +Metabase.APPLICATION = RelationField("application") +Metabase.APPLICATION_FIELD = RelationField("applicationField") +Metabase.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Metabase.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Metabase.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Metabase.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Metabase.METRICS = RelationField("metrics") +Metabase.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Metabase.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Metabase.MEANINGS = RelationField("meanings") +Metabase.MC_MONITORS = RelationField("mcMonitors") +Metabase.MC_INCIDENTS = RelationField("mcIncidents") +Metabase.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Metabase.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Metabase.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Metabase.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Metabase.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Metabase.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Metabase.FILES = RelationField("files") +Metabase.LINKS = RelationField("links") +Metabase.README = RelationField("readme") +Metabase.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Metabase.SODA_CHECKS = RelationField("sodaChecks") +Metabase.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Metabase.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/metabase_collection.py b/pyatlan_v9/model/assets/metabase_collection.py new file mode 100644 index 000000000..42d837c5b --- /dev/null +++ b/pyatlan_v9/model/assets/metabase_collection.py @@ -0,0 +1,645 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +MetabaseCollection asset model with flattened inheritance. + +This module provides: +- MetabaseCollection: Flat asset class (easy to use) +- MetabaseCollectionAttributes: Nested attributes struct (extends AssetAttributes) +- MetabaseCollectionNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .metabase_related import RelatedMetabaseDashboard, RelatedMetabaseQuestion + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class MetabaseCollection(Asset): + """ + Instance of a Metabase collection in Atlan. + """ + + METABASE_SLUG: ClassVar[Any] = None + METABASE_COLOR: ClassVar[Any] = None + METABASE_NAMESPACE: ClassVar[Any] = None + METABASE_IS_PERSONAL_COLLECTION: ClassVar[Any] = None + METABASE_COLLECTION_NAME: ClassVar[Any] = None + METABASE_COLLECTION_QUALIFIED_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + METABASE_DASHBOARDS: ClassVar[Any] = None + METABASE_QUESTIONS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "MetabaseCollection" + + metabase_slug: Union[str, None, UnsetType] = UNSET + """""" + + metabase_color: Union[str, None, UnsetType] = UNSET + """""" + + metabase_namespace: Union[str, None, UnsetType] = UNSET + """""" + + metabase_is_personal_collection: Union[bool, None, UnsetType] = UNSET + """""" + + metabase_collection_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Metabase collection in which this asset exists.""" + + metabase_collection_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the Metabase collection in which this asset exists.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + metabase_dashboards: Union[List[RelatedMetabaseDashboard], None, UnsetType] = UNSET + """Dashboards that exist within this collection.""" + + metabase_questions: Union[List[RelatedMetabaseQuestion], None, UnsetType] = UNSET + """Questions that exist within this collection.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "MetabaseCollection" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _metabase_collection_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> MetabaseCollection: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + MetabaseCollection instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _metabase_collection_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class MetabaseCollectionAttributes(AssetAttributes): + """MetabaseCollection-specific attributes for nested API format.""" + + metabase_slug: Union[str, None, UnsetType] = UNSET + """""" + + metabase_color: Union[str, None, UnsetType] = UNSET + """""" + + metabase_namespace: Union[str, None, UnsetType] = UNSET + """""" + + metabase_is_personal_collection: Union[bool, None, UnsetType] = UNSET + """""" + + metabase_collection_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Metabase collection in which this asset exists.""" + + metabase_collection_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the Metabase collection in which this asset exists.""" + + +class MetabaseCollectionRelationshipAttributes(AssetRelationshipAttributes): + """MetabaseCollection-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + metabase_dashboards: Union[List[RelatedMetabaseDashboard], None, UnsetType] = UNSET + """Dashboards that exist within this collection.""" + + metabase_questions: Union[List[RelatedMetabaseQuestion], None, UnsetType] = UNSET + """Questions that exist within this collection.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class MetabaseCollectionNested(AssetNested): + """MetabaseCollection in nested API format for high-performance serialization.""" + + attributes: Union[MetabaseCollectionAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + MetabaseCollectionRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + MetabaseCollectionRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + MetabaseCollectionRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_METABASE_COLLECTION_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "metabase_dashboards", + "metabase_questions", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_metabase_collection_attrs( + attrs: MetabaseCollectionAttributes, obj: MetabaseCollection +) -> None: + """Populate MetabaseCollection-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.metabase_slug = obj.metabase_slug + attrs.metabase_color = obj.metabase_color + attrs.metabase_namespace = obj.metabase_namespace + attrs.metabase_is_personal_collection = obj.metabase_is_personal_collection + attrs.metabase_collection_name = obj.metabase_collection_name + attrs.metabase_collection_qualified_name = obj.metabase_collection_qualified_name + + +def _extract_metabase_collection_attrs(attrs: MetabaseCollectionAttributes) -> dict: + """Extract all MetabaseCollection attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["metabase_slug"] = attrs.metabase_slug + result["metabase_color"] = attrs.metabase_color + result["metabase_namespace"] = attrs.metabase_namespace + result["metabase_is_personal_collection"] = attrs.metabase_is_personal_collection + result["metabase_collection_name"] = attrs.metabase_collection_name + result["metabase_collection_qualified_name"] = ( + attrs.metabase_collection_qualified_name + ) + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _metabase_collection_to_nested( + metabase_collection: MetabaseCollection, +) -> MetabaseCollectionNested: + """Convert flat MetabaseCollection to nested format.""" + attrs = MetabaseCollectionAttributes() + _populate_metabase_collection_attrs(attrs, metabase_collection) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + metabase_collection, + _METABASE_COLLECTION_REL_FIELDS, + MetabaseCollectionRelationshipAttributes, + ) + return MetabaseCollectionNested( + guid=metabase_collection.guid, + type_name=metabase_collection.type_name, + status=metabase_collection.status, + version=metabase_collection.version, + create_time=metabase_collection.create_time, + update_time=metabase_collection.update_time, + created_by=metabase_collection.created_by, + updated_by=metabase_collection.updated_by, + classifications=metabase_collection.classifications, + classification_names=metabase_collection.classification_names, + meanings=metabase_collection.meanings, + labels=metabase_collection.labels, + business_attributes=metabase_collection.business_attributes, + custom_attributes=metabase_collection.custom_attributes, + pending_tasks=metabase_collection.pending_tasks, + proxy=metabase_collection.proxy, + is_incomplete=metabase_collection.is_incomplete, + provenance_type=metabase_collection.provenance_type, + home_id=metabase_collection.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _metabase_collection_from_nested( + nested: MetabaseCollectionNested, +) -> MetabaseCollection: + """Convert nested format to flat MetabaseCollection.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else MetabaseCollectionAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _METABASE_COLLECTION_REL_FIELDS, + MetabaseCollectionRelationshipAttributes, + ) + return MetabaseCollection( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_metabase_collection_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _metabase_collection_to_nested_bytes( + metabase_collection: MetabaseCollection, serde: Serde +) -> bytes: + """Convert flat MetabaseCollection to nested JSON bytes.""" + return serde.encode(_metabase_collection_to_nested(metabase_collection)) + + +def _metabase_collection_from_nested_bytes( + data: bytes, serde: Serde +) -> MetabaseCollection: + """Convert nested JSON bytes to flat MetabaseCollection.""" + nested = serde.decode(data, MetabaseCollectionNested) + return _metabase_collection_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + RelationField, +) + +MetabaseCollection.METABASE_SLUG = KeywordTextField( + "metabaseSlug", "metabaseSlug", "metabaseSlug.text" +) +MetabaseCollection.METABASE_COLOR = KeywordField("metabaseColor", "metabaseColor") +MetabaseCollection.METABASE_NAMESPACE = KeywordTextField( + "metabaseNamespace", "metabaseNamespace", "metabaseNamespace.text" +) +MetabaseCollection.METABASE_IS_PERSONAL_COLLECTION = BooleanField( + "metabaseIsPersonalCollection", "metabaseIsPersonalCollection" +) +MetabaseCollection.METABASE_COLLECTION_NAME = KeywordField( + "metabaseCollectionName", "metabaseCollectionName" +) +MetabaseCollection.METABASE_COLLECTION_QUALIFIED_NAME = KeywordTextField( + "metabaseCollectionQualifiedName", + "metabaseCollectionQualifiedName", + "metabaseCollectionQualifiedName.text", +) +MetabaseCollection.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +MetabaseCollection.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +MetabaseCollection.ANOMALO_CHECKS = RelationField("anomaloChecks") +MetabaseCollection.APPLICATION = RelationField("application") +MetabaseCollection.APPLICATION_FIELD = RelationField("applicationField") +MetabaseCollection.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +MetabaseCollection.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +MetabaseCollection.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +MetabaseCollection.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +MetabaseCollection.METRICS = RelationField("metrics") +MetabaseCollection.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +MetabaseCollection.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +MetabaseCollection.MEANINGS = RelationField("meanings") +MetabaseCollection.METABASE_DASHBOARDS = RelationField("metabaseDashboards") +MetabaseCollection.METABASE_QUESTIONS = RelationField("metabaseQuestions") +MetabaseCollection.MC_MONITORS = RelationField("mcMonitors") +MetabaseCollection.MC_INCIDENTS = RelationField("mcIncidents") +MetabaseCollection.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +MetabaseCollection.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +MetabaseCollection.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +MetabaseCollection.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +MetabaseCollection.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +MetabaseCollection.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +MetabaseCollection.FILES = RelationField("files") +MetabaseCollection.LINKS = RelationField("links") +MetabaseCollection.README = RelationField("readme") +MetabaseCollection.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +MetabaseCollection.SODA_CHECKS = RelationField("sodaChecks") +MetabaseCollection.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +MetabaseCollection.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/metabase_dashboard.py b/pyatlan_v9/model/assets/metabase_dashboard.py new file mode 100644 index 000000000..aabd0ee10 --- /dev/null +++ b/pyatlan_v9/model/assets/metabase_dashboard.py @@ -0,0 +1,616 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +MetabaseDashboard asset model with flattened inheritance. + +This module provides: +- MetabaseDashboard: Flat asset class (easy to use) +- MetabaseDashboardAttributes: Nested attributes struct (extends AssetAttributes) +- MetabaseDashboardNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .metabase_related import RelatedMetabaseCollection, RelatedMetabaseQuestion + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class MetabaseDashboard(Asset): + """ + Instance of a Metabase dashboard in Atlan. + """ + + METABASE_QUESTION_COUNT: ClassVar[Any] = None + METABASE_COLLECTION_NAME: ClassVar[Any] = None + METABASE_COLLECTION_QUALIFIED_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + METABASE_COLLECTION: ClassVar[Any] = None + METABASE_QUESTIONS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "MetabaseDashboard" + + metabase_question_count: Union[int, None, UnsetType] = UNSET + """""" + + metabase_collection_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Metabase collection in which this asset exists.""" + + metabase_collection_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the Metabase collection in which this asset exists.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + metabase_collection: Union[RelatedMetabaseCollection, None, UnsetType] = UNSET + """Collection in which this dashboard exists.""" + + metabase_questions: Union[List[RelatedMetabaseQuestion], None, UnsetType] = UNSET + """Questions used on this dashboard.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "MetabaseDashboard" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _metabase_dashboard_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> MetabaseDashboard: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + MetabaseDashboard instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _metabase_dashboard_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class MetabaseDashboardAttributes(AssetAttributes): + """MetabaseDashboard-specific attributes for nested API format.""" + + metabase_question_count: Union[int, None, UnsetType] = UNSET + """""" + + metabase_collection_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Metabase collection in which this asset exists.""" + + metabase_collection_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the Metabase collection in which this asset exists.""" + + +class MetabaseDashboardRelationshipAttributes(AssetRelationshipAttributes): + """MetabaseDashboard-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + metabase_collection: Union[RelatedMetabaseCollection, None, UnsetType] = UNSET + """Collection in which this dashboard exists.""" + + metabase_questions: Union[List[RelatedMetabaseQuestion], None, UnsetType] = UNSET + """Questions used on this dashboard.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class MetabaseDashboardNested(AssetNested): + """MetabaseDashboard in nested API format for high-performance serialization.""" + + attributes: Union[MetabaseDashboardAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + MetabaseDashboardRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + MetabaseDashboardRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + MetabaseDashboardRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_METABASE_DASHBOARD_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "metabase_collection", + "metabase_questions", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_metabase_dashboard_attrs( + attrs: MetabaseDashboardAttributes, obj: MetabaseDashboard +) -> None: + """Populate MetabaseDashboard-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.metabase_question_count = obj.metabase_question_count + attrs.metabase_collection_name = obj.metabase_collection_name + attrs.metabase_collection_qualified_name = obj.metabase_collection_qualified_name + + +def _extract_metabase_dashboard_attrs(attrs: MetabaseDashboardAttributes) -> dict: + """Extract all MetabaseDashboard attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["metabase_question_count"] = attrs.metabase_question_count + result["metabase_collection_name"] = attrs.metabase_collection_name + result["metabase_collection_qualified_name"] = ( + attrs.metabase_collection_qualified_name + ) + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _metabase_dashboard_to_nested( + metabase_dashboard: MetabaseDashboard, +) -> MetabaseDashboardNested: + """Convert flat MetabaseDashboard to nested format.""" + attrs = MetabaseDashboardAttributes() + _populate_metabase_dashboard_attrs(attrs, metabase_dashboard) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + metabase_dashboard, + _METABASE_DASHBOARD_REL_FIELDS, + MetabaseDashboardRelationshipAttributes, + ) + return MetabaseDashboardNested( + guid=metabase_dashboard.guid, + type_name=metabase_dashboard.type_name, + status=metabase_dashboard.status, + version=metabase_dashboard.version, + create_time=metabase_dashboard.create_time, + update_time=metabase_dashboard.update_time, + created_by=metabase_dashboard.created_by, + updated_by=metabase_dashboard.updated_by, + classifications=metabase_dashboard.classifications, + classification_names=metabase_dashboard.classification_names, + meanings=metabase_dashboard.meanings, + labels=metabase_dashboard.labels, + business_attributes=metabase_dashboard.business_attributes, + custom_attributes=metabase_dashboard.custom_attributes, + pending_tasks=metabase_dashboard.pending_tasks, + proxy=metabase_dashboard.proxy, + is_incomplete=metabase_dashboard.is_incomplete, + provenance_type=metabase_dashboard.provenance_type, + home_id=metabase_dashboard.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _metabase_dashboard_from_nested( + nested: MetabaseDashboardNested, +) -> MetabaseDashboard: + """Convert nested format to flat MetabaseDashboard.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else MetabaseDashboardAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _METABASE_DASHBOARD_REL_FIELDS, + MetabaseDashboardRelationshipAttributes, + ) + return MetabaseDashboard( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_metabase_dashboard_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _metabase_dashboard_to_nested_bytes( + metabase_dashboard: MetabaseDashboard, serde: Serde +) -> bytes: + """Convert flat MetabaseDashboard to nested JSON bytes.""" + return serde.encode(_metabase_dashboard_to_nested(metabase_dashboard)) + + +def _metabase_dashboard_from_nested_bytes( + data: bytes, serde: Serde +) -> MetabaseDashboard: + """Convert nested JSON bytes to flat MetabaseDashboard.""" + nested = serde.decode(data, MetabaseDashboardNested) + return _metabase_dashboard_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +MetabaseDashboard.METABASE_QUESTION_COUNT = NumericField( + "metabaseQuestionCount", "metabaseQuestionCount" +) +MetabaseDashboard.METABASE_COLLECTION_NAME = KeywordField( + "metabaseCollectionName", "metabaseCollectionName" +) +MetabaseDashboard.METABASE_COLLECTION_QUALIFIED_NAME = KeywordTextField( + "metabaseCollectionQualifiedName", + "metabaseCollectionQualifiedName", + "metabaseCollectionQualifiedName.text", +) +MetabaseDashboard.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +MetabaseDashboard.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +MetabaseDashboard.ANOMALO_CHECKS = RelationField("anomaloChecks") +MetabaseDashboard.APPLICATION = RelationField("application") +MetabaseDashboard.APPLICATION_FIELD = RelationField("applicationField") +MetabaseDashboard.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +MetabaseDashboard.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +MetabaseDashboard.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +MetabaseDashboard.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +MetabaseDashboard.METRICS = RelationField("metrics") +MetabaseDashboard.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +MetabaseDashboard.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +MetabaseDashboard.MEANINGS = RelationField("meanings") +MetabaseDashboard.METABASE_COLLECTION = RelationField("metabaseCollection") +MetabaseDashboard.METABASE_QUESTIONS = RelationField("metabaseQuestions") +MetabaseDashboard.MC_MONITORS = RelationField("mcMonitors") +MetabaseDashboard.MC_INCIDENTS = RelationField("mcIncidents") +MetabaseDashboard.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +MetabaseDashboard.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +MetabaseDashboard.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +MetabaseDashboard.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +MetabaseDashboard.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +MetabaseDashboard.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +MetabaseDashboard.FILES = RelationField("files") +MetabaseDashboard.LINKS = RelationField("links") +MetabaseDashboard.README = RelationField("readme") +MetabaseDashboard.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +MetabaseDashboard.SODA_CHECKS = RelationField("sodaChecks") +MetabaseDashboard.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +MetabaseDashboard.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/metabase_question.py b/pyatlan_v9/model/assets/metabase_question.py new file mode 100644 index 000000000..63b160b6e --- /dev/null +++ b/pyatlan_v9/model/assets/metabase_question.py @@ -0,0 +1,634 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +MetabaseQuestion asset model with flattened inheritance. + +This module provides: +- MetabaseQuestion: Flat asset class (easy to use) +- MetabaseQuestionAttributes: Nested attributes struct (extends AssetAttributes) +- MetabaseQuestionNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .metabase_related import RelatedMetabaseCollection, RelatedMetabaseDashboard + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class MetabaseQuestion(Asset): + """ + Instance of a Metabase question in Atlan. + """ + + METABASE_DASHBOARD_COUNT: ClassVar[Any] = None + METABASE_QUERY_TYPE: ClassVar[Any] = None + METABASE_QUERY: ClassVar[Any] = None + METABASE_COLLECTION_NAME: ClassVar[Any] = None + METABASE_COLLECTION_QUALIFIED_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + METABASE_COLLECTION: ClassVar[Any] = None + METABASE_DASHBOARDS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "MetabaseQuestion" + + metabase_dashboard_count: Union[int, None, UnsetType] = UNSET + """""" + + metabase_query_type: Union[str, None, UnsetType] = UNSET + """""" + + metabase_query: Union[str, None, UnsetType] = UNSET + """""" + + metabase_collection_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Metabase collection in which this asset exists.""" + + metabase_collection_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the Metabase collection in which this asset exists.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + metabase_collection: Union[RelatedMetabaseCollection, None, UnsetType] = UNSET + """Collection in which this question exists.""" + + metabase_dashboards: Union[List[RelatedMetabaseDashboard], None, UnsetType] = UNSET + """Dashboards in which this question is used.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "MetabaseQuestion" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _metabase_question_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> MetabaseQuestion: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + MetabaseQuestion instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _metabase_question_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class MetabaseQuestionAttributes(AssetAttributes): + """MetabaseQuestion-specific attributes for nested API format.""" + + metabase_dashboard_count: Union[int, None, UnsetType] = UNSET + """""" + + metabase_query_type: Union[str, None, UnsetType] = UNSET + """""" + + metabase_query: Union[str, None, UnsetType] = UNSET + """""" + + metabase_collection_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Metabase collection in which this asset exists.""" + + metabase_collection_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the Metabase collection in which this asset exists.""" + + +class MetabaseQuestionRelationshipAttributes(AssetRelationshipAttributes): + """MetabaseQuestion-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + metabase_collection: Union[RelatedMetabaseCollection, None, UnsetType] = UNSET + """Collection in which this question exists.""" + + metabase_dashboards: Union[List[RelatedMetabaseDashboard], None, UnsetType] = UNSET + """Dashboards in which this question is used.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class MetabaseQuestionNested(AssetNested): + """MetabaseQuestion in nested API format for high-performance serialization.""" + + attributes: Union[MetabaseQuestionAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + MetabaseQuestionRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + MetabaseQuestionRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + MetabaseQuestionRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_METABASE_QUESTION_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "metabase_collection", + "metabase_dashboards", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_metabase_question_attrs( + attrs: MetabaseQuestionAttributes, obj: MetabaseQuestion +) -> None: + """Populate MetabaseQuestion-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.metabase_dashboard_count = obj.metabase_dashboard_count + attrs.metabase_query_type = obj.metabase_query_type + attrs.metabase_query = obj.metabase_query + attrs.metabase_collection_name = obj.metabase_collection_name + attrs.metabase_collection_qualified_name = obj.metabase_collection_qualified_name + + +def _extract_metabase_question_attrs(attrs: MetabaseQuestionAttributes) -> dict: + """Extract all MetabaseQuestion attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["metabase_dashboard_count"] = attrs.metabase_dashboard_count + result["metabase_query_type"] = attrs.metabase_query_type + result["metabase_query"] = attrs.metabase_query + result["metabase_collection_name"] = attrs.metabase_collection_name + result["metabase_collection_qualified_name"] = ( + attrs.metabase_collection_qualified_name + ) + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _metabase_question_to_nested( + metabase_question: MetabaseQuestion, +) -> MetabaseQuestionNested: + """Convert flat MetabaseQuestion to nested format.""" + attrs = MetabaseQuestionAttributes() + _populate_metabase_question_attrs(attrs, metabase_question) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + metabase_question, + _METABASE_QUESTION_REL_FIELDS, + MetabaseQuestionRelationshipAttributes, + ) + return MetabaseQuestionNested( + guid=metabase_question.guid, + type_name=metabase_question.type_name, + status=metabase_question.status, + version=metabase_question.version, + create_time=metabase_question.create_time, + update_time=metabase_question.update_time, + created_by=metabase_question.created_by, + updated_by=metabase_question.updated_by, + classifications=metabase_question.classifications, + classification_names=metabase_question.classification_names, + meanings=metabase_question.meanings, + labels=metabase_question.labels, + business_attributes=metabase_question.business_attributes, + custom_attributes=metabase_question.custom_attributes, + pending_tasks=metabase_question.pending_tasks, + proxy=metabase_question.proxy, + is_incomplete=metabase_question.is_incomplete, + provenance_type=metabase_question.provenance_type, + home_id=metabase_question.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _metabase_question_from_nested(nested: MetabaseQuestionNested) -> MetabaseQuestion: + """Convert nested format to flat MetabaseQuestion.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else MetabaseQuestionAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _METABASE_QUESTION_REL_FIELDS, + MetabaseQuestionRelationshipAttributes, + ) + return MetabaseQuestion( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_metabase_question_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _metabase_question_to_nested_bytes( + metabase_question: MetabaseQuestion, serde: Serde +) -> bytes: + """Convert flat MetabaseQuestion to nested JSON bytes.""" + return serde.encode(_metabase_question_to_nested(metabase_question)) + + +def _metabase_question_from_nested_bytes(data: bytes, serde: Serde) -> MetabaseQuestion: + """Convert nested JSON bytes to flat MetabaseQuestion.""" + nested = serde.decode(data, MetabaseQuestionNested) + return _metabase_question_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +MetabaseQuestion.METABASE_DASHBOARD_COUNT = NumericField( + "metabaseDashboardCount", "metabaseDashboardCount" +) +MetabaseQuestion.METABASE_QUERY_TYPE = KeywordTextField( + "metabaseQueryType", "metabaseQueryType", "metabaseQueryType.text" +) +MetabaseQuestion.METABASE_QUERY = KeywordField("metabaseQuery", "metabaseQuery") +MetabaseQuestion.METABASE_COLLECTION_NAME = KeywordField( + "metabaseCollectionName", "metabaseCollectionName" +) +MetabaseQuestion.METABASE_COLLECTION_QUALIFIED_NAME = KeywordTextField( + "metabaseCollectionQualifiedName", + "metabaseCollectionQualifiedName", + "metabaseCollectionQualifiedName.text", +) +MetabaseQuestion.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +MetabaseQuestion.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +MetabaseQuestion.ANOMALO_CHECKS = RelationField("anomaloChecks") +MetabaseQuestion.APPLICATION = RelationField("application") +MetabaseQuestion.APPLICATION_FIELD = RelationField("applicationField") +MetabaseQuestion.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +MetabaseQuestion.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +MetabaseQuestion.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +MetabaseQuestion.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +MetabaseQuestion.METRICS = RelationField("metrics") +MetabaseQuestion.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +MetabaseQuestion.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +MetabaseQuestion.MEANINGS = RelationField("meanings") +MetabaseQuestion.METABASE_COLLECTION = RelationField("metabaseCollection") +MetabaseQuestion.METABASE_DASHBOARDS = RelationField("metabaseDashboards") +MetabaseQuestion.MC_MONITORS = RelationField("mcMonitors") +MetabaseQuestion.MC_INCIDENTS = RelationField("mcIncidents") +MetabaseQuestion.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +MetabaseQuestion.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +MetabaseQuestion.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +MetabaseQuestion.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +MetabaseQuestion.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +MetabaseQuestion.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +MetabaseQuestion.FILES = RelationField("files") +MetabaseQuestion.LINKS = RelationField("links") +MetabaseQuestion.README = RelationField("readme") +MetabaseQuestion.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +MetabaseQuestion.SODA_CHECKS = RelationField("sodaChecks") +MetabaseQuestion.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +MetabaseQuestion.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/metabase_related.py b/pyatlan_v9/model/assets/metabase_related.py new file mode 100644 index 000000000..b2af931e1 --- /dev/null +++ b/pyatlan_v9/model/assets/metabase_related.py @@ -0,0 +1,116 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Metabase module. + +This module contains all Related{Type} classes for the Metabase type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Union + +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedBI +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedMetabase", + "RelatedMetabaseCollection", + "RelatedMetabaseDashboard", + "RelatedMetabaseQuestion", +] + + +class RelatedMetabase(RelatedBI): + """ + Related entity reference for Metabase assets. + + Extends RelatedBI with Metabase-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Metabase" so it serializes correctly + + metabase_collection_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Metabase collection in which this asset exists.""" + + metabase_collection_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the Metabase collection in which this asset exists.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Metabase" + + +class RelatedMetabaseCollection(RelatedMetabase): + """ + Related entity reference for MetabaseCollection assets. + + Extends RelatedMetabase with MetabaseCollection-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "MetabaseCollection" so it serializes correctly + + metabase_slug: Union[str, None, UnsetType] = UNSET + """""" + + metabase_color: Union[str, None, UnsetType] = UNSET + """""" + + metabase_namespace: Union[str, None, UnsetType] = UNSET + """""" + + metabase_is_personal_collection: Union[bool, None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "MetabaseCollection" + + +class RelatedMetabaseDashboard(RelatedMetabase): + """ + Related entity reference for MetabaseDashboard assets. + + Extends RelatedMetabase with MetabaseDashboard-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "MetabaseDashboard" so it serializes correctly + + metabase_question_count: Union[int, None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "MetabaseDashboard" + + +class RelatedMetabaseQuestion(RelatedMetabase): + """ + Related entity reference for MetabaseQuestion assets. + + Extends RelatedMetabase with MetabaseQuestion-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "MetabaseQuestion" so it serializes correctly + + metabase_dashboard_count: Union[int, None, UnsetType] = UNSET + """""" + + metabase_query_type: Union[str, None, UnsetType] = UNSET + """""" + + metabase_query: Union[str, None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "MetabaseQuestion" diff --git a/pyatlan_v9/model/assets/metric.py b/pyatlan_v9/model/assets/metric.py new file mode 100644 index 000000000..7c95bcc59 --- /dev/null +++ b/pyatlan_v9/model/assets/metric.py @@ -0,0 +1,611 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Metric asset model with flattened inheritance. + +This module provides: +- Metric: Flat asset class (easy to use) +- MetricAttributes: Nested attributes struct (extends AssetAttributes) +- MetricNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .asset_related import RelatedAsset +from .data_mesh_related import RelatedDataProduct +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from .sql_related import RelatedColumn +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .data_quality_related import RelatedDataQualityRule, RelatedMetric + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Metric(Asset): + """ + Base class for data quality metrics assets. + """ + + METRIC_TYPE: ClassVar[Any] = None + METRIC_SQL: ClassVar[Any] = None + METRIC_FILTERS: ClassVar[Any] = None + METRIC_TIME_GRAINS: ClassVar[Any] = None + DQ_IS_PART_OF_CONTRACT: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + ASSETS: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + METRIC_TIMESTAMP_COLUMN: ClassVar[Any] = None + METRIC_DIMENSION_COLUMNS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Metric" + + metric_type: Union[str, None, UnsetType] = UNSET + """Type of the metric.""" + + metric_sql: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="metricSQL" + ) + """SQL query used to compute the metric.""" + + metric_filters: Union[str, None, UnsetType] = UNSET + """Filters to be applied to the metric query.""" + + metric_time_grains: Union[List[str], None, UnsetType] = UNSET + """List of time grains to be applied to the metric query.""" + + dq_is_part_of_contract: Union[bool, None, UnsetType] = UNSET + """Whether this data quality is part of contract (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + assets: Union[List[RelatedAsset], None, UnsetType] = UNSET + """""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + metric_timestamp_column: Union[RelatedColumn, None, UnsetType] = UNSET + """""" + + metric_dimension_columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Metric" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _metric_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Metric: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Metric instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _metric_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class MetricAttributes(AssetAttributes): + """Metric-specific attributes for nested API format.""" + + metric_type: Union[str, None, UnsetType] = UNSET + """Type of the metric.""" + + metric_sql: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="metricSQL" + ) + """SQL query used to compute the metric.""" + + metric_filters: Union[str, None, UnsetType] = UNSET + """Filters to be applied to the metric query.""" + + metric_time_grains: Union[List[str], None, UnsetType] = UNSET + """List of time grains to be applied to the metric query.""" + + dq_is_part_of_contract: Union[bool, None, UnsetType] = UNSET + """Whether this data quality is part of contract (true) or not (false).""" + + +class MetricRelationshipAttributes(AssetRelationshipAttributes): + """Metric-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + assets: Union[List[RelatedAsset], None, UnsetType] = UNSET + """""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + metric_timestamp_column: Union[RelatedColumn, None, UnsetType] = UNSET + """""" + + metric_dimension_columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class MetricNested(AssetNested): + """Metric in nested API format for high-performance serialization.""" + + attributes: Union[MetricAttributes, UnsetType] = UNSET + relationship_attributes: Union[MetricRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[MetricRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[MetricRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_METRIC_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "assets", + "metrics", + "metric_timestamp_column", + "metric_dimension_columns", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_metric_attrs(attrs: MetricAttributes, obj: Metric) -> None: + """Populate Metric-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.metric_type = obj.metric_type + attrs.metric_sql = obj.metric_sql + attrs.metric_filters = obj.metric_filters + attrs.metric_time_grains = obj.metric_time_grains + attrs.dq_is_part_of_contract = obj.dq_is_part_of_contract + + +def _extract_metric_attrs(attrs: MetricAttributes) -> dict: + """Extract all Metric attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["metric_type"] = attrs.metric_type + result["metric_sql"] = attrs.metric_sql + result["metric_filters"] = attrs.metric_filters + result["metric_time_grains"] = attrs.metric_time_grains + result["dq_is_part_of_contract"] = attrs.dq_is_part_of_contract + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _metric_to_nested(metric: Metric) -> MetricNested: + """Convert flat Metric to nested format.""" + attrs = MetricAttributes() + _populate_metric_attrs(attrs, metric) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + metric, _METRIC_REL_FIELDS, MetricRelationshipAttributes + ) + return MetricNested( + guid=metric.guid, + type_name=metric.type_name, + status=metric.status, + version=metric.version, + create_time=metric.create_time, + update_time=metric.update_time, + created_by=metric.created_by, + updated_by=metric.updated_by, + classifications=metric.classifications, + classification_names=metric.classification_names, + meanings=metric.meanings, + labels=metric.labels, + business_attributes=metric.business_attributes, + custom_attributes=metric.custom_attributes, + pending_tasks=metric.pending_tasks, + proxy=metric.proxy, + is_incomplete=metric.is_incomplete, + provenance_type=metric.provenance_type, + home_id=metric.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _metric_from_nested(nested: MetricNested) -> Metric: + """Convert nested format to flat Metric.""" + attrs = nested.attributes if nested.attributes is not UNSET else MetricAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _METRIC_REL_FIELDS, + MetricRelationshipAttributes, + ) + return Metric( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_metric_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _metric_to_nested_bytes(metric: Metric, serde: Serde) -> bytes: + """Convert flat Metric to nested JSON bytes.""" + return serde.encode(_metric_to_nested(metric)) + + +def _metric_from_nested_bytes(data: bytes, serde: Serde) -> Metric: + """Convert nested JSON bytes to flat Metric.""" + nested = serde.decode(data, MetricNested) + return _metric_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + RelationField, +) + +Metric.METRIC_TYPE = KeywordField("metricType", "metricType") +Metric.METRIC_SQL = KeywordField("metricSQL", "metricSQL") +Metric.METRIC_FILTERS = KeywordField("metricFilters", "metricFilters") +Metric.METRIC_TIME_GRAINS = KeywordField("metricTimeGrains", "metricTimeGrains") +Metric.DQ_IS_PART_OF_CONTRACT = BooleanField("dqIsPartOfContract", "dqIsPartOfContract") +Metric.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Metric.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Metric.ANOMALO_CHECKS = RelationField("anomaloChecks") +Metric.APPLICATION = RelationField("application") +Metric.APPLICATION_FIELD = RelationField("applicationField") +Metric.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Metric.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Metric.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Metric.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Metric.ASSETS = RelationField("assets") +Metric.METRICS = RelationField("metrics") +Metric.METRIC_TIMESTAMP_COLUMN = RelationField("metricTimestampColumn") +Metric.METRIC_DIMENSION_COLUMNS = RelationField("metricDimensionColumns") +Metric.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Metric.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Metric.MEANINGS = RelationField("meanings") +Metric.MC_MONITORS = RelationField("mcMonitors") +Metric.MC_INCIDENTS = RelationField("mcIncidents") +Metric.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Metric.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Metric.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Metric.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Metric.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Metric.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Metric.FILES = RelationField("files") +Metric.LINKS = RelationField("links") +Metric.README = RelationField("readme") +Metric.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Metric.SODA_CHECKS = RelationField("sodaChecks") +Metric.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Metric.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/micro_strategy.py b/pyatlan_v9/model/assets/micro_strategy.py new file mode 100644 index 000000000..3400eb860 --- /dev/null +++ b/pyatlan_v9/model/assets/micro_strategy.py @@ -0,0 +1,676 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +MicroStrategy asset model with flattened inheritance. + +This module provides: +- MicroStrategy: Flat asset class (easy to use) +- MicroStrategyAttributes: Nested attributes struct (extends AssetAttributes) +- MicroStrategyNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class MicroStrategy(Asset): + """ + Base class for MicroStrategy assets. + """ + + MICRO_STRATEGY_PROJECT_QUALIFIED_NAME: ClassVar[Any] = None + MICRO_STRATEGY_PROJECT_NAME: ClassVar[Any] = None + MICRO_STRATEGY_CUBE_QUALIFIED_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_CUBE_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_REPORT_QUALIFIED_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_REPORT_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_IS_CERTIFIED: ClassVar[Any] = None + MICRO_STRATEGY_CERTIFIED_BY: ClassVar[Any] = None + MICRO_STRATEGY_CERTIFIED_AT: ClassVar[Any] = None + MICRO_STRATEGY_LOCATION: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "MicroStrategy" + + micro_strategy_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this asset exists.""" + + micro_strategy_project_name: Union[str, None, UnsetType] = UNSET + """Simple name of the project in which this asset exists.""" + + micro_strategy_cube_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Unique names of the cubes related to this asset.""" + + micro_strategy_cube_names: Union[List[str], None, UnsetType] = UNSET + """Simple names of the cubes related to this asset.""" + + micro_strategy_report_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Unique names of the reports related to this asset.""" + + micro_strategy_report_names: Union[List[str], None, UnsetType] = UNSET + """Simple names of the reports related to this asset.""" + + micro_strategy_is_certified: Union[bool, None, UnsetType] = UNSET + """Whether the asset is certified in MicroStrategy (true) or not (false).""" + + micro_strategy_certified_by: Union[str, None, UnsetType] = UNSET + """User who certified this asset, in MicroStrategy.""" + + micro_strategy_certified_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) this asset was certified in MicroStrategy, in milliseconds.""" + + micro_strategy_location: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Location of this asset in MicroStrategy.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "MicroStrategy" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _micro_strategy_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> MicroStrategy: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + MicroStrategy instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _micro_strategy_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class MicroStrategyAttributes(AssetAttributes): + """MicroStrategy-specific attributes for nested API format.""" + + micro_strategy_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this asset exists.""" + + micro_strategy_project_name: Union[str, None, UnsetType] = UNSET + """Simple name of the project in which this asset exists.""" + + micro_strategy_cube_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Unique names of the cubes related to this asset.""" + + micro_strategy_cube_names: Union[List[str], None, UnsetType] = UNSET + """Simple names of the cubes related to this asset.""" + + micro_strategy_report_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Unique names of the reports related to this asset.""" + + micro_strategy_report_names: Union[List[str], None, UnsetType] = UNSET + """Simple names of the reports related to this asset.""" + + micro_strategy_is_certified: Union[bool, None, UnsetType] = UNSET + """Whether the asset is certified in MicroStrategy (true) or not (false).""" + + micro_strategy_certified_by: Union[str, None, UnsetType] = UNSET + """User who certified this asset, in MicroStrategy.""" + + micro_strategy_certified_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) this asset was certified in MicroStrategy, in milliseconds.""" + + micro_strategy_location: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Location of this asset in MicroStrategy.""" + + +class MicroStrategyRelationshipAttributes(AssetRelationshipAttributes): + """MicroStrategy-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class MicroStrategyNested(AssetNested): + """MicroStrategy in nested API format for high-performance serialization.""" + + attributes: Union[MicroStrategyAttributes, UnsetType] = UNSET + relationship_attributes: Union[MicroStrategyRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + MicroStrategyRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + MicroStrategyRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_MICRO_STRATEGY_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_micro_strategy_attrs( + attrs: MicroStrategyAttributes, obj: MicroStrategy +) -> None: + """Populate MicroStrategy-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.micro_strategy_project_qualified_name = ( + obj.micro_strategy_project_qualified_name + ) + attrs.micro_strategy_project_name = obj.micro_strategy_project_name + attrs.micro_strategy_cube_qualified_names = obj.micro_strategy_cube_qualified_names + attrs.micro_strategy_cube_names = obj.micro_strategy_cube_names + attrs.micro_strategy_report_qualified_names = ( + obj.micro_strategy_report_qualified_names + ) + attrs.micro_strategy_report_names = obj.micro_strategy_report_names + attrs.micro_strategy_is_certified = obj.micro_strategy_is_certified + attrs.micro_strategy_certified_by = obj.micro_strategy_certified_by + attrs.micro_strategy_certified_at = obj.micro_strategy_certified_at + attrs.micro_strategy_location = obj.micro_strategy_location + + +def _extract_micro_strategy_attrs(attrs: MicroStrategyAttributes) -> dict: + """Extract all MicroStrategy attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["micro_strategy_project_qualified_name"] = ( + attrs.micro_strategy_project_qualified_name + ) + result["micro_strategy_project_name"] = attrs.micro_strategy_project_name + result["micro_strategy_cube_qualified_names"] = ( + attrs.micro_strategy_cube_qualified_names + ) + result["micro_strategy_cube_names"] = attrs.micro_strategy_cube_names + result["micro_strategy_report_qualified_names"] = ( + attrs.micro_strategy_report_qualified_names + ) + result["micro_strategy_report_names"] = attrs.micro_strategy_report_names + result["micro_strategy_is_certified"] = attrs.micro_strategy_is_certified + result["micro_strategy_certified_by"] = attrs.micro_strategy_certified_by + result["micro_strategy_certified_at"] = attrs.micro_strategy_certified_at + result["micro_strategy_location"] = attrs.micro_strategy_location + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _micro_strategy_to_nested(micro_strategy: MicroStrategy) -> MicroStrategyNested: + """Convert flat MicroStrategy to nested format.""" + attrs = MicroStrategyAttributes() + _populate_micro_strategy_attrs(attrs, micro_strategy) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + micro_strategy, _MICRO_STRATEGY_REL_FIELDS, MicroStrategyRelationshipAttributes + ) + return MicroStrategyNested( + guid=micro_strategy.guid, + type_name=micro_strategy.type_name, + status=micro_strategy.status, + version=micro_strategy.version, + create_time=micro_strategy.create_time, + update_time=micro_strategy.update_time, + created_by=micro_strategy.created_by, + updated_by=micro_strategy.updated_by, + classifications=micro_strategy.classifications, + classification_names=micro_strategy.classification_names, + meanings=micro_strategy.meanings, + labels=micro_strategy.labels, + business_attributes=micro_strategy.business_attributes, + custom_attributes=micro_strategy.custom_attributes, + pending_tasks=micro_strategy.pending_tasks, + proxy=micro_strategy.proxy, + is_incomplete=micro_strategy.is_incomplete, + provenance_type=micro_strategy.provenance_type, + home_id=micro_strategy.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _micro_strategy_from_nested(nested: MicroStrategyNested) -> MicroStrategy: + """Convert nested format to flat MicroStrategy.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else MicroStrategyAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _MICRO_STRATEGY_REL_FIELDS, + MicroStrategyRelationshipAttributes, + ) + return MicroStrategy( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_micro_strategy_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _micro_strategy_to_nested_bytes( + micro_strategy: MicroStrategy, serde: Serde +) -> bytes: + """Convert flat MicroStrategy to nested JSON bytes.""" + return serde.encode(_micro_strategy_to_nested(micro_strategy)) + + +def _micro_strategy_from_nested_bytes(data: bytes, serde: Serde) -> MicroStrategy: + """Convert nested JSON bytes to flat MicroStrategy.""" + nested = serde.decode(data, MicroStrategyNested) + return _micro_strategy_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +MicroStrategy.MICRO_STRATEGY_PROJECT_QUALIFIED_NAME = KeywordTextField( + "microStrategyProjectQualifiedName", + "microStrategyProjectQualifiedName", + "microStrategyProjectQualifiedName.text", +) +MicroStrategy.MICRO_STRATEGY_PROJECT_NAME = KeywordTextField( + "microStrategyProjectName", + "microStrategyProjectName", + "microStrategyProjectName.text", +) +MicroStrategy.MICRO_STRATEGY_CUBE_QUALIFIED_NAMES = KeywordTextField( + "microStrategyCubeQualifiedNames", + "microStrategyCubeQualifiedNames", + "microStrategyCubeQualifiedNames.text", +) +MicroStrategy.MICRO_STRATEGY_CUBE_NAMES = KeywordField( + "microStrategyCubeNames", "microStrategyCubeNames" +) +MicroStrategy.MICRO_STRATEGY_REPORT_QUALIFIED_NAMES = KeywordTextField( + "microStrategyReportQualifiedNames", + "microStrategyReportQualifiedNames", + "microStrategyReportQualifiedNames.text", +) +MicroStrategy.MICRO_STRATEGY_REPORT_NAMES = KeywordField( + "microStrategyReportNames", "microStrategyReportNames" +) +MicroStrategy.MICRO_STRATEGY_IS_CERTIFIED = BooleanField( + "microStrategyIsCertified", "microStrategyIsCertified" +) +MicroStrategy.MICRO_STRATEGY_CERTIFIED_BY = KeywordField( + "microStrategyCertifiedBy", "microStrategyCertifiedBy" +) +MicroStrategy.MICRO_STRATEGY_CERTIFIED_AT = NumericField( + "microStrategyCertifiedAt", "microStrategyCertifiedAt" +) +MicroStrategy.MICRO_STRATEGY_LOCATION = KeywordField( + "microStrategyLocation", "microStrategyLocation" +) +MicroStrategy.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +MicroStrategy.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +MicroStrategy.ANOMALO_CHECKS = RelationField("anomaloChecks") +MicroStrategy.APPLICATION = RelationField("application") +MicroStrategy.APPLICATION_FIELD = RelationField("applicationField") +MicroStrategy.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +MicroStrategy.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +MicroStrategy.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +MicroStrategy.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +MicroStrategy.METRICS = RelationField("metrics") +MicroStrategy.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +MicroStrategy.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +MicroStrategy.MEANINGS = RelationField("meanings") +MicroStrategy.MC_MONITORS = RelationField("mcMonitors") +MicroStrategy.MC_INCIDENTS = RelationField("mcIncidents") +MicroStrategy.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +MicroStrategy.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +MicroStrategy.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +MicroStrategy.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +MicroStrategy.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +MicroStrategy.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +MicroStrategy.FILES = RelationField("files") +MicroStrategy.LINKS = RelationField("links") +MicroStrategy.README = RelationField("readme") +MicroStrategy.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +MicroStrategy.SODA_CHECKS = RelationField("sodaChecks") +MicroStrategy.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +MicroStrategy.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/micro_strategy_attribute.py b/pyatlan_v9/model/assets/micro_strategy_attribute.py new file mode 100644 index 000000000..334a2dc32 --- /dev/null +++ b/pyatlan_v9/model/assets/micro_strategy_attribute.py @@ -0,0 +1,786 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +MicroStrategyAttribute asset model with flattened inheritance. + +This module provides: +- MicroStrategyAttribute: Flat asset class (easy to use) +- MicroStrategyAttributeAttributes: Nested attributes struct (extends AssetAttributes) +- MicroStrategyAttributeNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .micro_strategy_related import ( + RelatedMicroStrategyColumn, + RelatedMicroStrategyCube, + RelatedMicroStrategyMetric, + RelatedMicroStrategyProject, + RelatedMicroStrategyReport, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class MicroStrategyAttribute(Asset): + """ + Instance of a MicroStrategy attribute in Atlan. + """ + + MICRO_STRATEGY_ATTRIBUTE_FORMS: ClassVar[Any] = None + MICRO_STRATEGY_PROJECT_QUALIFIED_NAME: ClassVar[Any] = None + MICRO_STRATEGY_PROJECT_NAME: ClassVar[Any] = None + MICRO_STRATEGY_CUBE_QUALIFIED_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_CUBE_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_REPORT_QUALIFIED_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_REPORT_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_IS_CERTIFIED: ClassVar[Any] = None + MICRO_STRATEGY_CERTIFIED_BY: ClassVar[Any] = None + MICRO_STRATEGY_CERTIFIED_AT: ClassVar[Any] = None + MICRO_STRATEGY_LOCATION: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MICRO_STRATEGY_PROJECT: ClassVar[Any] = None + MICRO_STRATEGY_METRICS: ClassVar[Any] = None + MICRO_STRATEGY_CUBES: ClassVar[Any] = None + MICRO_STRATEGY_REPORTS: ClassVar[Any] = None + MICRO_STRATEGY_COLUMNS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "MicroStrategyAttribute" + + micro_strategy_attribute_forms: Union[str, None, UnsetType] = UNSET + """JSON string specifying the attribute's name, description, displayFormat, etc.""" + + micro_strategy_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this asset exists.""" + + micro_strategy_project_name: Union[str, None, UnsetType] = UNSET + """Simple name of the project in which this asset exists.""" + + micro_strategy_cube_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Unique names of the cubes related to this asset.""" + + micro_strategy_cube_names: Union[List[str], None, UnsetType] = UNSET + """Simple names of the cubes related to this asset.""" + + micro_strategy_report_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Unique names of the reports related to this asset.""" + + micro_strategy_report_names: Union[List[str], None, UnsetType] = UNSET + """Simple names of the reports related to this asset.""" + + micro_strategy_is_certified: Union[bool, None, UnsetType] = UNSET + """Whether the asset is certified in MicroStrategy (true) or not (false).""" + + micro_strategy_certified_by: Union[str, None, UnsetType] = UNSET + """User who certified this asset, in MicroStrategy.""" + + micro_strategy_certified_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) this asset was certified in MicroStrategy, in milliseconds.""" + + micro_strategy_location: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Location of this asset in MicroStrategy.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + micro_strategy_project: Union[RelatedMicroStrategyProject, None, UnsetType] = UNSET + """Project in which this attribute exists.""" + + micro_strategy_metrics: Union[List[RelatedMicroStrategyMetric], None, UnsetType] = ( + UNSET + ) + """Metrics that use this attribute.""" + + micro_strategy_cubes: Union[List[RelatedMicroStrategyCube], None, UnsetType] = UNSET + """Cubes in which this attribute is used.""" + + micro_strategy_reports: Union[List[RelatedMicroStrategyReport], None, UnsetType] = ( + UNSET + ) + """Reports in which this attribute is used.""" + + micro_strategy_columns: Union[List[RelatedMicroStrategyColumn], None, UnsetType] = ( + UNSET + ) + """Individual columns contained in the attribute.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "MicroStrategyAttribute" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _micro_strategy_attribute_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> MicroStrategyAttribute: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + MicroStrategyAttribute instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _micro_strategy_attribute_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class MicroStrategyAttributeAttributes(AssetAttributes): + """MicroStrategyAttribute-specific attributes for nested API format.""" + + micro_strategy_attribute_forms: Union[str, None, UnsetType] = UNSET + """JSON string specifying the attribute's name, description, displayFormat, etc.""" + + micro_strategy_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this asset exists.""" + + micro_strategy_project_name: Union[str, None, UnsetType] = UNSET + """Simple name of the project in which this asset exists.""" + + micro_strategy_cube_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Unique names of the cubes related to this asset.""" + + micro_strategy_cube_names: Union[List[str], None, UnsetType] = UNSET + """Simple names of the cubes related to this asset.""" + + micro_strategy_report_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Unique names of the reports related to this asset.""" + + micro_strategy_report_names: Union[List[str], None, UnsetType] = UNSET + """Simple names of the reports related to this asset.""" + + micro_strategy_is_certified: Union[bool, None, UnsetType] = UNSET + """Whether the asset is certified in MicroStrategy (true) or not (false).""" + + micro_strategy_certified_by: Union[str, None, UnsetType] = UNSET + """User who certified this asset, in MicroStrategy.""" + + micro_strategy_certified_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) this asset was certified in MicroStrategy, in milliseconds.""" + + micro_strategy_location: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Location of this asset in MicroStrategy.""" + + +class MicroStrategyAttributeRelationshipAttributes(AssetRelationshipAttributes): + """MicroStrategyAttribute-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + micro_strategy_project: Union[RelatedMicroStrategyProject, None, UnsetType] = UNSET + """Project in which this attribute exists.""" + + micro_strategy_metrics: Union[List[RelatedMicroStrategyMetric], None, UnsetType] = ( + UNSET + ) + """Metrics that use this attribute.""" + + micro_strategy_cubes: Union[List[RelatedMicroStrategyCube], None, UnsetType] = UNSET + """Cubes in which this attribute is used.""" + + micro_strategy_reports: Union[List[RelatedMicroStrategyReport], None, UnsetType] = ( + UNSET + ) + """Reports in which this attribute is used.""" + + micro_strategy_columns: Union[List[RelatedMicroStrategyColumn], None, UnsetType] = ( + UNSET + ) + """Individual columns contained in the attribute.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class MicroStrategyAttributeNested(AssetNested): + """MicroStrategyAttribute in nested API format for high-performance serialization.""" + + attributes: Union[MicroStrategyAttributeAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + MicroStrategyAttributeRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + MicroStrategyAttributeRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + MicroStrategyAttributeRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_MICRO_STRATEGY_ATTRIBUTE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "micro_strategy_project", + "micro_strategy_metrics", + "micro_strategy_cubes", + "micro_strategy_reports", + "micro_strategy_columns", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_micro_strategy_attribute_attrs( + attrs: MicroStrategyAttributeAttributes, obj: MicroStrategyAttribute +) -> None: + """Populate MicroStrategyAttribute-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.micro_strategy_attribute_forms = obj.micro_strategy_attribute_forms + attrs.micro_strategy_project_qualified_name = ( + obj.micro_strategy_project_qualified_name + ) + attrs.micro_strategy_project_name = obj.micro_strategy_project_name + attrs.micro_strategy_cube_qualified_names = obj.micro_strategy_cube_qualified_names + attrs.micro_strategy_cube_names = obj.micro_strategy_cube_names + attrs.micro_strategy_report_qualified_names = ( + obj.micro_strategy_report_qualified_names + ) + attrs.micro_strategy_report_names = obj.micro_strategy_report_names + attrs.micro_strategy_is_certified = obj.micro_strategy_is_certified + attrs.micro_strategy_certified_by = obj.micro_strategy_certified_by + attrs.micro_strategy_certified_at = obj.micro_strategy_certified_at + attrs.micro_strategy_location = obj.micro_strategy_location + + +def _extract_micro_strategy_attribute_attrs( + attrs: MicroStrategyAttributeAttributes, +) -> dict: + """Extract all MicroStrategyAttribute attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["micro_strategy_attribute_forms"] = attrs.micro_strategy_attribute_forms + result["micro_strategy_project_qualified_name"] = ( + attrs.micro_strategy_project_qualified_name + ) + result["micro_strategy_project_name"] = attrs.micro_strategy_project_name + result["micro_strategy_cube_qualified_names"] = ( + attrs.micro_strategy_cube_qualified_names + ) + result["micro_strategy_cube_names"] = attrs.micro_strategy_cube_names + result["micro_strategy_report_qualified_names"] = ( + attrs.micro_strategy_report_qualified_names + ) + result["micro_strategy_report_names"] = attrs.micro_strategy_report_names + result["micro_strategy_is_certified"] = attrs.micro_strategy_is_certified + result["micro_strategy_certified_by"] = attrs.micro_strategy_certified_by + result["micro_strategy_certified_at"] = attrs.micro_strategy_certified_at + result["micro_strategy_location"] = attrs.micro_strategy_location + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _micro_strategy_attribute_to_nested( + micro_strategy_attribute: MicroStrategyAttribute, +) -> MicroStrategyAttributeNested: + """Convert flat MicroStrategyAttribute to nested format.""" + attrs = MicroStrategyAttributeAttributes() + _populate_micro_strategy_attribute_attrs(attrs, micro_strategy_attribute) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + micro_strategy_attribute, + _MICRO_STRATEGY_ATTRIBUTE_REL_FIELDS, + MicroStrategyAttributeRelationshipAttributes, + ) + return MicroStrategyAttributeNested( + guid=micro_strategy_attribute.guid, + type_name=micro_strategy_attribute.type_name, + status=micro_strategy_attribute.status, + version=micro_strategy_attribute.version, + create_time=micro_strategy_attribute.create_time, + update_time=micro_strategy_attribute.update_time, + created_by=micro_strategy_attribute.created_by, + updated_by=micro_strategy_attribute.updated_by, + classifications=micro_strategy_attribute.classifications, + classification_names=micro_strategy_attribute.classification_names, + meanings=micro_strategy_attribute.meanings, + labels=micro_strategy_attribute.labels, + business_attributes=micro_strategy_attribute.business_attributes, + custom_attributes=micro_strategy_attribute.custom_attributes, + pending_tasks=micro_strategy_attribute.pending_tasks, + proxy=micro_strategy_attribute.proxy, + is_incomplete=micro_strategy_attribute.is_incomplete, + provenance_type=micro_strategy_attribute.provenance_type, + home_id=micro_strategy_attribute.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _micro_strategy_attribute_from_nested( + nested: MicroStrategyAttributeNested, +) -> MicroStrategyAttribute: + """Convert nested format to flat MicroStrategyAttribute.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else MicroStrategyAttributeAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _MICRO_STRATEGY_ATTRIBUTE_REL_FIELDS, + MicroStrategyAttributeRelationshipAttributes, + ) + return MicroStrategyAttribute( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_micro_strategy_attribute_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _micro_strategy_attribute_to_nested_bytes( + micro_strategy_attribute: MicroStrategyAttribute, serde: Serde +) -> bytes: + """Convert flat MicroStrategyAttribute to nested JSON bytes.""" + return serde.encode(_micro_strategy_attribute_to_nested(micro_strategy_attribute)) + + +def _micro_strategy_attribute_from_nested_bytes( + data: bytes, serde: Serde +) -> MicroStrategyAttribute: + """Convert nested JSON bytes to flat MicroStrategyAttribute.""" + nested = serde.decode(data, MicroStrategyAttributeNested) + return _micro_strategy_attribute_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +MicroStrategyAttribute.MICRO_STRATEGY_ATTRIBUTE_FORMS = KeywordField( + "microStrategyAttributeForms", "microStrategyAttributeForms" +) +MicroStrategyAttribute.MICRO_STRATEGY_PROJECT_QUALIFIED_NAME = KeywordTextField( + "microStrategyProjectQualifiedName", + "microStrategyProjectQualifiedName", + "microStrategyProjectQualifiedName.text", +) +MicroStrategyAttribute.MICRO_STRATEGY_PROJECT_NAME = KeywordTextField( + "microStrategyProjectName", + "microStrategyProjectName", + "microStrategyProjectName.text", +) +MicroStrategyAttribute.MICRO_STRATEGY_CUBE_QUALIFIED_NAMES = KeywordTextField( + "microStrategyCubeQualifiedNames", + "microStrategyCubeQualifiedNames", + "microStrategyCubeQualifiedNames.text", +) +MicroStrategyAttribute.MICRO_STRATEGY_CUBE_NAMES = KeywordField( + "microStrategyCubeNames", "microStrategyCubeNames" +) +MicroStrategyAttribute.MICRO_STRATEGY_REPORT_QUALIFIED_NAMES = KeywordTextField( + "microStrategyReportQualifiedNames", + "microStrategyReportQualifiedNames", + "microStrategyReportQualifiedNames.text", +) +MicroStrategyAttribute.MICRO_STRATEGY_REPORT_NAMES = KeywordField( + "microStrategyReportNames", "microStrategyReportNames" +) +MicroStrategyAttribute.MICRO_STRATEGY_IS_CERTIFIED = BooleanField( + "microStrategyIsCertified", "microStrategyIsCertified" +) +MicroStrategyAttribute.MICRO_STRATEGY_CERTIFIED_BY = KeywordField( + "microStrategyCertifiedBy", "microStrategyCertifiedBy" +) +MicroStrategyAttribute.MICRO_STRATEGY_CERTIFIED_AT = NumericField( + "microStrategyCertifiedAt", "microStrategyCertifiedAt" +) +MicroStrategyAttribute.MICRO_STRATEGY_LOCATION = KeywordField( + "microStrategyLocation", "microStrategyLocation" +) +MicroStrategyAttribute.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +MicroStrategyAttribute.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +MicroStrategyAttribute.ANOMALO_CHECKS = RelationField("anomaloChecks") +MicroStrategyAttribute.APPLICATION = RelationField("application") +MicroStrategyAttribute.APPLICATION_FIELD = RelationField("applicationField") +MicroStrategyAttribute.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +MicroStrategyAttribute.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +MicroStrategyAttribute.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +MicroStrategyAttribute.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +MicroStrategyAttribute.METRICS = RelationField("metrics") +MicroStrategyAttribute.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +MicroStrategyAttribute.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +MicroStrategyAttribute.MEANINGS = RelationField("meanings") +MicroStrategyAttribute.MICRO_STRATEGY_PROJECT = RelationField("microStrategyProject") +MicroStrategyAttribute.MICRO_STRATEGY_METRICS = RelationField("microStrategyMetrics") +MicroStrategyAttribute.MICRO_STRATEGY_CUBES = RelationField("microStrategyCubes") +MicroStrategyAttribute.MICRO_STRATEGY_REPORTS = RelationField("microStrategyReports") +MicroStrategyAttribute.MICRO_STRATEGY_COLUMNS = RelationField("microStrategyColumns") +MicroStrategyAttribute.MC_MONITORS = RelationField("mcMonitors") +MicroStrategyAttribute.MC_INCIDENTS = RelationField("mcIncidents") +MicroStrategyAttribute.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +MicroStrategyAttribute.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +MicroStrategyAttribute.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +MicroStrategyAttribute.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +MicroStrategyAttribute.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +MicroStrategyAttribute.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +MicroStrategyAttribute.FILES = RelationField("files") +MicroStrategyAttribute.LINKS = RelationField("links") +MicroStrategyAttribute.README = RelationField("readme") +MicroStrategyAttribute.SCHEMA_REGISTRY_SUBJECTS = RelationField( + "schemaRegistrySubjects" +) +MicroStrategyAttribute.SODA_CHECKS = RelationField("sodaChecks") +MicroStrategyAttribute.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +MicroStrategyAttribute.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/micro_strategy_column.py b/pyatlan_v9/model/assets/micro_strategy_column.py new file mode 100644 index 000000000..70423d2ab --- /dev/null +++ b/pyatlan_v9/model/assets/micro_strategy_column.py @@ -0,0 +1,970 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +MicroStrategyColumn asset model with flattened inheritance. + +This module provides: +- MicroStrategyColumn: Flat asset class (easy to use) +- MicroStrategyColumnAttributes: Nested attributes struct (extends AssetAttributes) +- MicroStrategyColumnNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .micro_strategy_related import ( + RelatedMicroStrategyAttribute, + RelatedMicroStrategyCube, + RelatedMicroStrategyDocument, + RelatedMicroStrategyDossier, + RelatedMicroStrategyFact, + RelatedMicroStrategyMetric, + RelatedMicroStrategyReport, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class MicroStrategyColumn(Asset): + """ + Base class for MicroStrategy column assets in Atlan. + """ + + MICRO_STRATEGY_COLUMN_ID: ClassVar[Any] = None + MICRO_STRATEGY_COLUMN_TYPE: ClassVar[Any] = None + MICRO_STRATEGY_DATA_TYPE: ClassVar[Any] = None + MICRO_STRATEGY_COLUMN_ATTRIBUTE_QUALIFIED_NAME: ClassVar[Any] = None + MICRO_STRATEGY_COLUMN_FACT_QUALIFIED_NAME: ClassVar[Any] = None + MICRO_STRATEGY_COLUMN_METRIC_QUALIFIED_NAME: ClassVar[Any] = None + MICRO_STRATEGY_COLUMN_CUBE_QUALIFIED_NAME: ClassVar[Any] = None + MICRO_STRATEGY_COLUMN_REPORT_QUALIFIED_NAME: ClassVar[Any] = None + MICRO_STRATEGY_COLUMN_DOSSIER_QUALIFIED_NAME: ClassVar[Any] = None + MICRO_STRATEGY_COLUMN_DOCUMENT_QUALIFIED_NAME: ClassVar[Any] = None + MICRO_STRATEGY_PARENT_NAME: ClassVar[Any] = None + MICRO_STRATEGY_COLUMN_EXPRESSION: ClassVar[Any] = None + MICRO_STRATEGY_PROJECT_QUALIFIED_NAME: ClassVar[Any] = None + MICRO_STRATEGY_PROJECT_NAME: ClassVar[Any] = None + MICRO_STRATEGY_CUBE_QUALIFIED_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_CUBE_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_REPORT_QUALIFIED_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_REPORT_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_IS_CERTIFIED: ClassVar[Any] = None + MICRO_STRATEGY_CERTIFIED_BY: ClassVar[Any] = None + MICRO_STRATEGY_CERTIFIED_AT: ClassVar[Any] = None + MICRO_STRATEGY_LOCATION: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MICRO_STRATEGY_ATTRIBUTE: ClassVar[Any] = None + MICRO_STRATEGY_CUBE: ClassVar[Any] = None + MICRO_STRATEGY_DOCUMENT: ClassVar[Any] = None + MICRO_STRATEGY_DOSSIER: ClassVar[Any] = None + MICRO_STRATEGY_FACT: ClassVar[Any] = None + MICRO_STRATEGY_METRIC: ClassVar[Any] = None + MICRO_STRATEGY_REPORT: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "MicroStrategyColumn" + + micro_strategy_column_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the column in MicroStrategy.""" + + micro_strategy_column_type: Union[str, None, UnsetType] = UNSET + """Type of the column (Eg attribute_column, fact_column, metric_column etc).""" + + micro_strategy_data_type: Union[str, None, UnsetType] = UNSET + """Data type of the column.""" + + micro_strategy_column_attribute_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique identifier of the Attribute in which this column exists.""" + + micro_strategy_column_fact_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique identifier of the Fact in which this column exists.""" + + micro_strategy_column_metric_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique identifier of the Metric in which this column exists.""" + + micro_strategy_column_cube_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique identifier of the Cube in which this column exists.""" + + micro_strategy_column_report_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique identifier of the Report in which this column exists.""" + + micro_strategy_column_dossier_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique identifier of the Dossier in which this column exists.""" + + micro_strategy_column_document_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique identifier of the Document in which this column exists.""" + + micro_strategy_parent_name: Union[str, None, UnsetType] = UNSET + """Name of the parent asset.""" + + micro_strategy_column_expression: Union[str, None, UnsetType] = UNSET + """Expression or formula used to define this column.""" + + micro_strategy_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this asset exists.""" + + micro_strategy_project_name: Union[str, None, UnsetType] = UNSET + """Simple name of the project in which this asset exists.""" + + micro_strategy_cube_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Unique names of the cubes related to this asset.""" + + micro_strategy_cube_names: Union[List[str], None, UnsetType] = UNSET + """Simple names of the cubes related to this asset.""" + + micro_strategy_report_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Unique names of the reports related to this asset.""" + + micro_strategy_report_names: Union[List[str], None, UnsetType] = UNSET + """Simple names of the reports related to this asset.""" + + micro_strategy_is_certified: Union[bool, None, UnsetType] = UNSET + """Whether the asset is certified in MicroStrategy (true) or not (false).""" + + micro_strategy_certified_by: Union[str, None, UnsetType] = UNSET + """User who certified this asset, in MicroStrategy.""" + + micro_strategy_certified_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) this asset was certified in MicroStrategy, in milliseconds.""" + + micro_strategy_location: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Location of this asset in MicroStrategy.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + micro_strategy_attribute: Union[RelatedMicroStrategyAttribute, None, UnsetType] = ( + UNSET + ) + """Attribute containing the column.""" + + micro_strategy_cube: Union[RelatedMicroStrategyCube, None, UnsetType] = UNSET + """Cube containing the column.""" + + micro_strategy_document: Union[RelatedMicroStrategyDocument, None, UnsetType] = ( + UNSET + ) + """Document containing the column.""" + + micro_strategy_dossier: Union[RelatedMicroStrategyDossier, None, UnsetType] = UNSET + """Dossier containing the column.""" + + micro_strategy_fact: Union[RelatedMicroStrategyFact, None, UnsetType] = UNSET + """Fact containing the column.""" + + micro_strategy_metric: Union[RelatedMicroStrategyMetric, None, UnsetType] = UNSET + """Metric containing the column.""" + + micro_strategy_report: Union[RelatedMicroStrategyReport, None, UnsetType] = UNSET + """Report containing the column.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "MicroStrategyColumn" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _micro_strategy_column_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> MicroStrategyColumn: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + MicroStrategyColumn instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _micro_strategy_column_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class MicroStrategyColumnAttributes(AssetAttributes): + """MicroStrategyColumn-specific attributes for nested API format.""" + + micro_strategy_column_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the column in MicroStrategy.""" + + micro_strategy_column_type: Union[str, None, UnsetType] = UNSET + """Type of the column (Eg attribute_column, fact_column, metric_column etc).""" + + micro_strategy_data_type: Union[str, None, UnsetType] = UNSET + """Data type of the column.""" + + micro_strategy_column_attribute_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique identifier of the Attribute in which this column exists.""" + + micro_strategy_column_fact_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique identifier of the Fact in which this column exists.""" + + micro_strategy_column_metric_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique identifier of the Metric in which this column exists.""" + + micro_strategy_column_cube_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique identifier of the Cube in which this column exists.""" + + micro_strategy_column_report_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique identifier of the Report in which this column exists.""" + + micro_strategy_column_dossier_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique identifier of the Dossier in which this column exists.""" + + micro_strategy_column_document_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique identifier of the Document in which this column exists.""" + + micro_strategy_parent_name: Union[str, None, UnsetType] = UNSET + """Name of the parent asset.""" + + micro_strategy_column_expression: Union[str, None, UnsetType] = UNSET + """Expression or formula used to define this column.""" + + micro_strategy_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this asset exists.""" + + micro_strategy_project_name: Union[str, None, UnsetType] = UNSET + """Simple name of the project in which this asset exists.""" + + micro_strategy_cube_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Unique names of the cubes related to this asset.""" + + micro_strategy_cube_names: Union[List[str], None, UnsetType] = UNSET + """Simple names of the cubes related to this asset.""" + + micro_strategy_report_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Unique names of the reports related to this asset.""" + + micro_strategy_report_names: Union[List[str], None, UnsetType] = UNSET + """Simple names of the reports related to this asset.""" + + micro_strategy_is_certified: Union[bool, None, UnsetType] = UNSET + """Whether the asset is certified in MicroStrategy (true) or not (false).""" + + micro_strategy_certified_by: Union[str, None, UnsetType] = UNSET + """User who certified this asset, in MicroStrategy.""" + + micro_strategy_certified_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) this asset was certified in MicroStrategy, in milliseconds.""" + + micro_strategy_location: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Location of this asset in MicroStrategy.""" + + +class MicroStrategyColumnRelationshipAttributes(AssetRelationshipAttributes): + """MicroStrategyColumn-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + micro_strategy_attribute: Union[RelatedMicroStrategyAttribute, None, UnsetType] = ( + UNSET + ) + """Attribute containing the column.""" + + micro_strategy_cube: Union[RelatedMicroStrategyCube, None, UnsetType] = UNSET + """Cube containing the column.""" + + micro_strategy_document: Union[RelatedMicroStrategyDocument, None, UnsetType] = ( + UNSET + ) + """Document containing the column.""" + + micro_strategy_dossier: Union[RelatedMicroStrategyDossier, None, UnsetType] = UNSET + """Dossier containing the column.""" + + micro_strategy_fact: Union[RelatedMicroStrategyFact, None, UnsetType] = UNSET + """Fact containing the column.""" + + micro_strategy_metric: Union[RelatedMicroStrategyMetric, None, UnsetType] = UNSET + """Metric containing the column.""" + + micro_strategy_report: Union[RelatedMicroStrategyReport, None, UnsetType] = UNSET + """Report containing the column.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class MicroStrategyColumnNested(AssetNested): + """MicroStrategyColumn in nested API format for high-performance serialization.""" + + attributes: Union[MicroStrategyColumnAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + MicroStrategyColumnRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + MicroStrategyColumnRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + MicroStrategyColumnRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_MICRO_STRATEGY_COLUMN_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "micro_strategy_attribute", + "micro_strategy_cube", + "micro_strategy_document", + "micro_strategy_dossier", + "micro_strategy_fact", + "micro_strategy_metric", + "micro_strategy_report", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_micro_strategy_column_attrs( + attrs: MicroStrategyColumnAttributes, obj: MicroStrategyColumn +) -> None: + """Populate MicroStrategyColumn-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.micro_strategy_column_id = obj.micro_strategy_column_id + attrs.micro_strategy_column_type = obj.micro_strategy_column_type + attrs.micro_strategy_data_type = obj.micro_strategy_data_type + attrs.micro_strategy_column_attribute_qualified_name = ( + obj.micro_strategy_column_attribute_qualified_name + ) + attrs.micro_strategy_column_fact_qualified_name = ( + obj.micro_strategy_column_fact_qualified_name + ) + attrs.micro_strategy_column_metric_qualified_name = ( + obj.micro_strategy_column_metric_qualified_name + ) + attrs.micro_strategy_column_cube_qualified_name = ( + obj.micro_strategy_column_cube_qualified_name + ) + attrs.micro_strategy_column_report_qualified_name = ( + obj.micro_strategy_column_report_qualified_name + ) + attrs.micro_strategy_column_dossier_qualified_name = ( + obj.micro_strategy_column_dossier_qualified_name + ) + attrs.micro_strategy_column_document_qualified_name = ( + obj.micro_strategy_column_document_qualified_name + ) + attrs.micro_strategy_parent_name = obj.micro_strategy_parent_name + attrs.micro_strategy_column_expression = obj.micro_strategy_column_expression + attrs.micro_strategy_project_qualified_name = ( + obj.micro_strategy_project_qualified_name + ) + attrs.micro_strategy_project_name = obj.micro_strategy_project_name + attrs.micro_strategy_cube_qualified_names = obj.micro_strategy_cube_qualified_names + attrs.micro_strategy_cube_names = obj.micro_strategy_cube_names + attrs.micro_strategy_report_qualified_names = ( + obj.micro_strategy_report_qualified_names + ) + attrs.micro_strategy_report_names = obj.micro_strategy_report_names + attrs.micro_strategy_is_certified = obj.micro_strategy_is_certified + attrs.micro_strategy_certified_by = obj.micro_strategy_certified_by + attrs.micro_strategy_certified_at = obj.micro_strategy_certified_at + attrs.micro_strategy_location = obj.micro_strategy_location + + +def _extract_micro_strategy_column_attrs(attrs: MicroStrategyColumnAttributes) -> dict: + """Extract all MicroStrategyColumn attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["micro_strategy_column_id"] = attrs.micro_strategy_column_id + result["micro_strategy_column_type"] = attrs.micro_strategy_column_type + result["micro_strategy_data_type"] = attrs.micro_strategy_data_type + result["micro_strategy_column_attribute_qualified_name"] = ( + attrs.micro_strategy_column_attribute_qualified_name + ) + result["micro_strategy_column_fact_qualified_name"] = ( + attrs.micro_strategy_column_fact_qualified_name + ) + result["micro_strategy_column_metric_qualified_name"] = ( + attrs.micro_strategy_column_metric_qualified_name + ) + result["micro_strategy_column_cube_qualified_name"] = ( + attrs.micro_strategy_column_cube_qualified_name + ) + result["micro_strategy_column_report_qualified_name"] = ( + attrs.micro_strategy_column_report_qualified_name + ) + result["micro_strategy_column_dossier_qualified_name"] = ( + attrs.micro_strategy_column_dossier_qualified_name + ) + result["micro_strategy_column_document_qualified_name"] = ( + attrs.micro_strategy_column_document_qualified_name + ) + result["micro_strategy_parent_name"] = attrs.micro_strategy_parent_name + result["micro_strategy_column_expression"] = attrs.micro_strategy_column_expression + result["micro_strategy_project_qualified_name"] = ( + attrs.micro_strategy_project_qualified_name + ) + result["micro_strategy_project_name"] = attrs.micro_strategy_project_name + result["micro_strategy_cube_qualified_names"] = ( + attrs.micro_strategy_cube_qualified_names + ) + result["micro_strategy_cube_names"] = attrs.micro_strategy_cube_names + result["micro_strategy_report_qualified_names"] = ( + attrs.micro_strategy_report_qualified_names + ) + result["micro_strategy_report_names"] = attrs.micro_strategy_report_names + result["micro_strategy_is_certified"] = attrs.micro_strategy_is_certified + result["micro_strategy_certified_by"] = attrs.micro_strategy_certified_by + result["micro_strategy_certified_at"] = attrs.micro_strategy_certified_at + result["micro_strategy_location"] = attrs.micro_strategy_location + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _micro_strategy_column_to_nested( + micro_strategy_column: MicroStrategyColumn, +) -> MicroStrategyColumnNested: + """Convert flat MicroStrategyColumn to nested format.""" + attrs = MicroStrategyColumnAttributes() + _populate_micro_strategy_column_attrs(attrs, micro_strategy_column) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + micro_strategy_column, + _MICRO_STRATEGY_COLUMN_REL_FIELDS, + MicroStrategyColumnRelationshipAttributes, + ) + return MicroStrategyColumnNested( + guid=micro_strategy_column.guid, + type_name=micro_strategy_column.type_name, + status=micro_strategy_column.status, + version=micro_strategy_column.version, + create_time=micro_strategy_column.create_time, + update_time=micro_strategy_column.update_time, + created_by=micro_strategy_column.created_by, + updated_by=micro_strategy_column.updated_by, + classifications=micro_strategy_column.classifications, + classification_names=micro_strategy_column.classification_names, + meanings=micro_strategy_column.meanings, + labels=micro_strategy_column.labels, + business_attributes=micro_strategy_column.business_attributes, + custom_attributes=micro_strategy_column.custom_attributes, + pending_tasks=micro_strategy_column.pending_tasks, + proxy=micro_strategy_column.proxy, + is_incomplete=micro_strategy_column.is_incomplete, + provenance_type=micro_strategy_column.provenance_type, + home_id=micro_strategy_column.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _micro_strategy_column_from_nested( + nested: MicroStrategyColumnNested, +) -> MicroStrategyColumn: + """Convert nested format to flat MicroStrategyColumn.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else MicroStrategyColumnAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _MICRO_STRATEGY_COLUMN_REL_FIELDS, + MicroStrategyColumnRelationshipAttributes, + ) + return MicroStrategyColumn( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_micro_strategy_column_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _micro_strategy_column_to_nested_bytes( + micro_strategy_column: MicroStrategyColumn, serde: Serde +) -> bytes: + """Convert flat MicroStrategyColumn to nested JSON bytes.""" + return serde.encode(_micro_strategy_column_to_nested(micro_strategy_column)) + + +def _micro_strategy_column_from_nested_bytes( + data: bytes, serde: Serde +) -> MicroStrategyColumn: + """Convert nested JSON bytes to flat MicroStrategyColumn.""" + nested = serde.decode(data, MicroStrategyColumnNested) + return _micro_strategy_column_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +MicroStrategyColumn.MICRO_STRATEGY_COLUMN_ID = KeywordField( + "microStrategyColumnId", "microStrategyColumnId" +) +MicroStrategyColumn.MICRO_STRATEGY_COLUMN_TYPE = KeywordField( + "microStrategyColumnType", "microStrategyColumnType" +) +MicroStrategyColumn.MICRO_STRATEGY_DATA_TYPE = KeywordField( + "microStrategyDataType", "microStrategyDataType" +) +MicroStrategyColumn.MICRO_STRATEGY_COLUMN_ATTRIBUTE_QUALIFIED_NAME = KeywordTextField( + "microStrategyColumnAttributeQualifiedName", + "microStrategyColumnAttributeQualifiedName", + "microStrategyColumnAttributeQualifiedName.text", +) +MicroStrategyColumn.MICRO_STRATEGY_COLUMN_FACT_QUALIFIED_NAME = KeywordTextField( + "microStrategyColumnFactQualifiedName", + "microStrategyColumnFactQualifiedName", + "microStrategyColumnFactQualifiedName.text", +) +MicroStrategyColumn.MICRO_STRATEGY_COLUMN_METRIC_QUALIFIED_NAME = KeywordTextField( + "microStrategyColumnMetricQualifiedName", + "microStrategyColumnMetricQualifiedName", + "microStrategyColumnMetricQualifiedName.text", +) +MicroStrategyColumn.MICRO_STRATEGY_COLUMN_CUBE_QUALIFIED_NAME = KeywordTextField( + "microStrategyColumnCubeQualifiedName", + "microStrategyColumnCubeQualifiedName", + "microStrategyColumnCubeQualifiedName.text", +) +MicroStrategyColumn.MICRO_STRATEGY_COLUMN_REPORT_QUALIFIED_NAME = KeywordTextField( + "microStrategyColumnReportQualifiedName", + "microStrategyColumnReportQualifiedName", + "microStrategyColumnReportQualifiedName.text", +) +MicroStrategyColumn.MICRO_STRATEGY_COLUMN_DOSSIER_QUALIFIED_NAME = KeywordTextField( + "microStrategyColumnDossierQualifiedName", + "microStrategyColumnDossierQualifiedName", + "microStrategyColumnDossierQualifiedName.text", +) +MicroStrategyColumn.MICRO_STRATEGY_COLUMN_DOCUMENT_QUALIFIED_NAME = KeywordTextField( + "microStrategyColumnDocumentQualifiedName", + "microStrategyColumnDocumentQualifiedName", + "microStrategyColumnDocumentQualifiedName.text", +) +MicroStrategyColumn.MICRO_STRATEGY_PARENT_NAME = KeywordField( + "microStrategyParentName", "microStrategyParentName" +) +MicroStrategyColumn.MICRO_STRATEGY_COLUMN_EXPRESSION = KeywordField( + "microStrategyColumnExpression", "microStrategyColumnExpression" +) +MicroStrategyColumn.MICRO_STRATEGY_PROJECT_QUALIFIED_NAME = KeywordTextField( + "microStrategyProjectQualifiedName", + "microStrategyProjectQualifiedName", + "microStrategyProjectQualifiedName.text", +) +MicroStrategyColumn.MICRO_STRATEGY_PROJECT_NAME = KeywordTextField( + "microStrategyProjectName", + "microStrategyProjectName", + "microStrategyProjectName.text", +) +MicroStrategyColumn.MICRO_STRATEGY_CUBE_QUALIFIED_NAMES = KeywordTextField( + "microStrategyCubeQualifiedNames", + "microStrategyCubeQualifiedNames", + "microStrategyCubeQualifiedNames.text", +) +MicroStrategyColumn.MICRO_STRATEGY_CUBE_NAMES = KeywordField( + "microStrategyCubeNames", "microStrategyCubeNames" +) +MicroStrategyColumn.MICRO_STRATEGY_REPORT_QUALIFIED_NAMES = KeywordTextField( + "microStrategyReportQualifiedNames", + "microStrategyReportQualifiedNames", + "microStrategyReportQualifiedNames.text", +) +MicroStrategyColumn.MICRO_STRATEGY_REPORT_NAMES = KeywordField( + "microStrategyReportNames", "microStrategyReportNames" +) +MicroStrategyColumn.MICRO_STRATEGY_IS_CERTIFIED = BooleanField( + "microStrategyIsCertified", "microStrategyIsCertified" +) +MicroStrategyColumn.MICRO_STRATEGY_CERTIFIED_BY = KeywordField( + "microStrategyCertifiedBy", "microStrategyCertifiedBy" +) +MicroStrategyColumn.MICRO_STRATEGY_CERTIFIED_AT = NumericField( + "microStrategyCertifiedAt", "microStrategyCertifiedAt" +) +MicroStrategyColumn.MICRO_STRATEGY_LOCATION = KeywordField( + "microStrategyLocation", "microStrategyLocation" +) +MicroStrategyColumn.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +MicroStrategyColumn.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +MicroStrategyColumn.ANOMALO_CHECKS = RelationField("anomaloChecks") +MicroStrategyColumn.APPLICATION = RelationField("application") +MicroStrategyColumn.APPLICATION_FIELD = RelationField("applicationField") +MicroStrategyColumn.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +MicroStrategyColumn.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +MicroStrategyColumn.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +MicroStrategyColumn.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +MicroStrategyColumn.METRICS = RelationField("metrics") +MicroStrategyColumn.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +MicroStrategyColumn.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +MicroStrategyColumn.MEANINGS = RelationField("meanings") +MicroStrategyColumn.MICRO_STRATEGY_ATTRIBUTE = RelationField("microStrategyAttribute") +MicroStrategyColumn.MICRO_STRATEGY_CUBE = RelationField("microStrategyCube") +MicroStrategyColumn.MICRO_STRATEGY_DOCUMENT = RelationField("microStrategyDocument") +MicroStrategyColumn.MICRO_STRATEGY_DOSSIER = RelationField("microStrategyDossier") +MicroStrategyColumn.MICRO_STRATEGY_FACT = RelationField("microStrategyFact") +MicroStrategyColumn.MICRO_STRATEGY_METRIC = RelationField("microStrategyMetric") +MicroStrategyColumn.MICRO_STRATEGY_REPORT = RelationField("microStrategyReport") +MicroStrategyColumn.MC_MONITORS = RelationField("mcMonitors") +MicroStrategyColumn.MC_INCIDENTS = RelationField("mcIncidents") +MicroStrategyColumn.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +MicroStrategyColumn.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +MicroStrategyColumn.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +MicroStrategyColumn.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +MicroStrategyColumn.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +MicroStrategyColumn.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +MicroStrategyColumn.FILES = RelationField("files") +MicroStrategyColumn.LINKS = RelationField("links") +MicroStrategyColumn.README = RelationField("readme") +MicroStrategyColumn.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +MicroStrategyColumn.SODA_CHECKS = RelationField("sodaChecks") +MicroStrategyColumn.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +MicroStrategyColumn.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/micro_strategy_cube.py b/pyatlan_v9/model/assets/micro_strategy_cube.py new file mode 100644 index 000000000..f5a1342a0 --- /dev/null +++ b/pyatlan_v9/model/assets/micro_strategy_cube.py @@ -0,0 +1,774 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +MicroStrategyCube asset model with flattened inheritance. + +This module provides: +- MicroStrategyCube: Flat asset class (easy to use) +- MicroStrategyCubeAttributes: Nested attributes struct (extends AssetAttributes) +- MicroStrategyCubeNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .micro_strategy_related import ( + RelatedMicroStrategyAttribute, + RelatedMicroStrategyColumn, + RelatedMicroStrategyMetric, + RelatedMicroStrategyProject, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class MicroStrategyCube(Asset): + """ + Instance of a MicroStrategy cube in Atlan. + """ + + MICRO_STRATEGY_CUBE_TYPE: ClassVar[Any] = None + MICRO_STRATEGY_CUBE_QUERY: ClassVar[Any] = None + MICRO_STRATEGY_PROJECT_QUALIFIED_NAME: ClassVar[Any] = None + MICRO_STRATEGY_PROJECT_NAME: ClassVar[Any] = None + MICRO_STRATEGY_CUBE_QUALIFIED_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_CUBE_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_REPORT_QUALIFIED_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_REPORT_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_IS_CERTIFIED: ClassVar[Any] = None + MICRO_STRATEGY_CERTIFIED_BY: ClassVar[Any] = None + MICRO_STRATEGY_CERTIFIED_AT: ClassVar[Any] = None + MICRO_STRATEGY_LOCATION: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MICRO_STRATEGY_PROJECT: ClassVar[Any] = None + MICRO_STRATEGY_METRICS: ClassVar[Any] = None + MICRO_STRATEGY_ATTRIBUTES: ClassVar[Any] = None + MICRO_STRATEGY_COLUMNS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "MicroStrategyCube" + + micro_strategy_cube_type: Union[str, None, UnsetType] = UNSET + """Type of cube, for example: OLAP or MTDI.""" + + micro_strategy_cube_query: Union[str, None, UnsetType] = UNSET + """Query used to create the cube.""" + + micro_strategy_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this asset exists.""" + + micro_strategy_project_name: Union[str, None, UnsetType] = UNSET + """Simple name of the project in which this asset exists.""" + + micro_strategy_cube_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Unique names of the cubes related to this asset.""" + + micro_strategy_cube_names: Union[List[str], None, UnsetType] = UNSET + """Simple names of the cubes related to this asset.""" + + micro_strategy_report_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Unique names of the reports related to this asset.""" + + micro_strategy_report_names: Union[List[str], None, UnsetType] = UNSET + """Simple names of the reports related to this asset.""" + + micro_strategy_is_certified: Union[bool, None, UnsetType] = UNSET + """Whether the asset is certified in MicroStrategy (true) or not (false).""" + + micro_strategy_certified_by: Union[str, None, UnsetType] = UNSET + """User who certified this asset, in MicroStrategy.""" + + micro_strategy_certified_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) this asset was certified in MicroStrategy, in milliseconds.""" + + micro_strategy_location: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Location of this asset in MicroStrategy.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + micro_strategy_project: Union[RelatedMicroStrategyProject, None, UnsetType] = UNSET + """Project in which this cube exists.""" + + micro_strategy_metrics: Union[List[RelatedMicroStrategyMetric], None, UnsetType] = ( + UNSET + ) + """Metrics that use this cube.""" + + micro_strategy_attributes: Union[ + List[RelatedMicroStrategyAttribute], None, UnsetType + ] = UNSET + """Attributes used by this cube.""" + + micro_strategy_columns: Union[List[RelatedMicroStrategyColumn], None, UnsetType] = ( + UNSET + ) + """Individual columns contained in the cube.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "MicroStrategyCube" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _micro_strategy_cube_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> MicroStrategyCube: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + MicroStrategyCube instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _micro_strategy_cube_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class MicroStrategyCubeAttributes(AssetAttributes): + """MicroStrategyCube-specific attributes for nested API format.""" + + micro_strategy_cube_type: Union[str, None, UnsetType] = UNSET + """Type of cube, for example: OLAP or MTDI.""" + + micro_strategy_cube_query: Union[str, None, UnsetType] = UNSET + """Query used to create the cube.""" + + micro_strategy_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this asset exists.""" + + micro_strategy_project_name: Union[str, None, UnsetType] = UNSET + """Simple name of the project in which this asset exists.""" + + micro_strategy_cube_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Unique names of the cubes related to this asset.""" + + micro_strategy_cube_names: Union[List[str], None, UnsetType] = UNSET + """Simple names of the cubes related to this asset.""" + + micro_strategy_report_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Unique names of the reports related to this asset.""" + + micro_strategy_report_names: Union[List[str], None, UnsetType] = UNSET + """Simple names of the reports related to this asset.""" + + micro_strategy_is_certified: Union[bool, None, UnsetType] = UNSET + """Whether the asset is certified in MicroStrategy (true) or not (false).""" + + micro_strategy_certified_by: Union[str, None, UnsetType] = UNSET + """User who certified this asset, in MicroStrategy.""" + + micro_strategy_certified_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) this asset was certified in MicroStrategy, in milliseconds.""" + + micro_strategy_location: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Location of this asset in MicroStrategy.""" + + +class MicroStrategyCubeRelationshipAttributes(AssetRelationshipAttributes): + """MicroStrategyCube-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + micro_strategy_project: Union[RelatedMicroStrategyProject, None, UnsetType] = UNSET + """Project in which this cube exists.""" + + micro_strategy_metrics: Union[List[RelatedMicroStrategyMetric], None, UnsetType] = ( + UNSET + ) + """Metrics that use this cube.""" + + micro_strategy_attributes: Union[ + List[RelatedMicroStrategyAttribute], None, UnsetType + ] = UNSET + """Attributes used by this cube.""" + + micro_strategy_columns: Union[List[RelatedMicroStrategyColumn], None, UnsetType] = ( + UNSET + ) + """Individual columns contained in the cube.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class MicroStrategyCubeNested(AssetNested): + """MicroStrategyCube in nested API format for high-performance serialization.""" + + attributes: Union[MicroStrategyCubeAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + MicroStrategyCubeRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + MicroStrategyCubeRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + MicroStrategyCubeRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_MICRO_STRATEGY_CUBE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "micro_strategy_project", + "micro_strategy_metrics", + "micro_strategy_attributes", + "micro_strategy_columns", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_micro_strategy_cube_attrs( + attrs: MicroStrategyCubeAttributes, obj: MicroStrategyCube +) -> None: + """Populate MicroStrategyCube-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.micro_strategy_cube_type = obj.micro_strategy_cube_type + attrs.micro_strategy_cube_query = obj.micro_strategy_cube_query + attrs.micro_strategy_project_qualified_name = ( + obj.micro_strategy_project_qualified_name + ) + attrs.micro_strategy_project_name = obj.micro_strategy_project_name + attrs.micro_strategy_cube_qualified_names = obj.micro_strategy_cube_qualified_names + attrs.micro_strategy_cube_names = obj.micro_strategy_cube_names + attrs.micro_strategy_report_qualified_names = ( + obj.micro_strategy_report_qualified_names + ) + attrs.micro_strategy_report_names = obj.micro_strategy_report_names + attrs.micro_strategy_is_certified = obj.micro_strategy_is_certified + attrs.micro_strategy_certified_by = obj.micro_strategy_certified_by + attrs.micro_strategy_certified_at = obj.micro_strategy_certified_at + attrs.micro_strategy_location = obj.micro_strategy_location + + +def _extract_micro_strategy_cube_attrs(attrs: MicroStrategyCubeAttributes) -> dict: + """Extract all MicroStrategyCube attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["micro_strategy_cube_type"] = attrs.micro_strategy_cube_type + result["micro_strategy_cube_query"] = attrs.micro_strategy_cube_query + result["micro_strategy_project_qualified_name"] = ( + attrs.micro_strategy_project_qualified_name + ) + result["micro_strategy_project_name"] = attrs.micro_strategy_project_name + result["micro_strategy_cube_qualified_names"] = ( + attrs.micro_strategy_cube_qualified_names + ) + result["micro_strategy_cube_names"] = attrs.micro_strategy_cube_names + result["micro_strategy_report_qualified_names"] = ( + attrs.micro_strategy_report_qualified_names + ) + result["micro_strategy_report_names"] = attrs.micro_strategy_report_names + result["micro_strategy_is_certified"] = attrs.micro_strategy_is_certified + result["micro_strategy_certified_by"] = attrs.micro_strategy_certified_by + result["micro_strategy_certified_at"] = attrs.micro_strategy_certified_at + result["micro_strategy_location"] = attrs.micro_strategy_location + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _micro_strategy_cube_to_nested( + micro_strategy_cube: MicroStrategyCube, +) -> MicroStrategyCubeNested: + """Convert flat MicroStrategyCube to nested format.""" + attrs = MicroStrategyCubeAttributes() + _populate_micro_strategy_cube_attrs(attrs, micro_strategy_cube) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + micro_strategy_cube, + _MICRO_STRATEGY_CUBE_REL_FIELDS, + MicroStrategyCubeRelationshipAttributes, + ) + return MicroStrategyCubeNested( + guid=micro_strategy_cube.guid, + type_name=micro_strategy_cube.type_name, + status=micro_strategy_cube.status, + version=micro_strategy_cube.version, + create_time=micro_strategy_cube.create_time, + update_time=micro_strategy_cube.update_time, + created_by=micro_strategy_cube.created_by, + updated_by=micro_strategy_cube.updated_by, + classifications=micro_strategy_cube.classifications, + classification_names=micro_strategy_cube.classification_names, + meanings=micro_strategy_cube.meanings, + labels=micro_strategy_cube.labels, + business_attributes=micro_strategy_cube.business_attributes, + custom_attributes=micro_strategy_cube.custom_attributes, + pending_tasks=micro_strategy_cube.pending_tasks, + proxy=micro_strategy_cube.proxy, + is_incomplete=micro_strategy_cube.is_incomplete, + provenance_type=micro_strategy_cube.provenance_type, + home_id=micro_strategy_cube.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _micro_strategy_cube_from_nested( + nested: MicroStrategyCubeNested, +) -> MicroStrategyCube: + """Convert nested format to flat MicroStrategyCube.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else MicroStrategyCubeAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _MICRO_STRATEGY_CUBE_REL_FIELDS, + MicroStrategyCubeRelationshipAttributes, + ) + return MicroStrategyCube( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_micro_strategy_cube_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _micro_strategy_cube_to_nested_bytes( + micro_strategy_cube: MicroStrategyCube, serde: Serde +) -> bytes: + """Convert flat MicroStrategyCube to nested JSON bytes.""" + return serde.encode(_micro_strategy_cube_to_nested(micro_strategy_cube)) + + +def _micro_strategy_cube_from_nested_bytes( + data: bytes, serde: Serde +) -> MicroStrategyCube: + """Convert nested JSON bytes to flat MicroStrategyCube.""" + nested = serde.decode(data, MicroStrategyCubeNested) + return _micro_strategy_cube_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +MicroStrategyCube.MICRO_STRATEGY_CUBE_TYPE = KeywordField( + "microStrategyCubeType", "microStrategyCubeType" +) +MicroStrategyCube.MICRO_STRATEGY_CUBE_QUERY = KeywordField( + "microStrategyCubeQuery", "microStrategyCubeQuery" +) +MicroStrategyCube.MICRO_STRATEGY_PROJECT_QUALIFIED_NAME = KeywordTextField( + "microStrategyProjectQualifiedName", + "microStrategyProjectQualifiedName", + "microStrategyProjectQualifiedName.text", +) +MicroStrategyCube.MICRO_STRATEGY_PROJECT_NAME = KeywordTextField( + "microStrategyProjectName", + "microStrategyProjectName", + "microStrategyProjectName.text", +) +MicroStrategyCube.MICRO_STRATEGY_CUBE_QUALIFIED_NAMES = KeywordTextField( + "microStrategyCubeQualifiedNames", + "microStrategyCubeQualifiedNames", + "microStrategyCubeQualifiedNames.text", +) +MicroStrategyCube.MICRO_STRATEGY_CUBE_NAMES = KeywordField( + "microStrategyCubeNames", "microStrategyCubeNames" +) +MicroStrategyCube.MICRO_STRATEGY_REPORT_QUALIFIED_NAMES = KeywordTextField( + "microStrategyReportQualifiedNames", + "microStrategyReportQualifiedNames", + "microStrategyReportQualifiedNames.text", +) +MicroStrategyCube.MICRO_STRATEGY_REPORT_NAMES = KeywordField( + "microStrategyReportNames", "microStrategyReportNames" +) +MicroStrategyCube.MICRO_STRATEGY_IS_CERTIFIED = BooleanField( + "microStrategyIsCertified", "microStrategyIsCertified" +) +MicroStrategyCube.MICRO_STRATEGY_CERTIFIED_BY = KeywordField( + "microStrategyCertifiedBy", "microStrategyCertifiedBy" +) +MicroStrategyCube.MICRO_STRATEGY_CERTIFIED_AT = NumericField( + "microStrategyCertifiedAt", "microStrategyCertifiedAt" +) +MicroStrategyCube.MICRO_STRATEGY_LOCATION = KeywordField( + "microStrategyLocation", "microStrategyLocation" +) +MicroStrategyCube.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +MicroStrategyCube.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +MicroStrategyCube.ANOMALO_CHECKS = RelationField("anomaloChecks") +MicroStrategyCube.APPLICATION = RelationField("application") +MicroStrategyCube.APPLICATION_FIELD = RelationField("applicationField") +MicroStrategyCube.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +MicroStrategyCube.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +MicroStrategyCube.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +MicroStrategyCube.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +MicroStrategyCube.METRICS = RelationField("metrics") +MicroStrategyCube.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +MicroStrategyCube.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +MicroStrategyCube.MEANINGS = RelationField("meanings") +MicroStrategyCube.MICRO_STRATEGY_PROJECT = RelationField("microStrategyProject") +MicroStrategyCube.MICRO_STRATEGY_METRICS = RelationField("microStrategyMetrics") +MicroStrategyCube.MICRO_STRATEGY_ATTRIBUTES = RelationField("microStrategyAttributes") +MicroStrategyCube.MICRO_STRATEGY_COLUMNS = RelationField("microStrategyColumns") +MicroStrategyCube.MC_MONITORS = RelationField("mcMonitors") +MicroStrategyCube.MC_INCIDENTS = RelationField("mcIncidents") +MicroStrategyCube.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +MicroStrategyCube.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +MicroStrategyCube.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +MicroStrategyCube.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +MicroStrategyCube.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +MicroStrategyCube.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +MicroStrategyCube.FILES = RelationField("files") +MicroStrategyCube.LINKS = RelationField("links") +MicroStrategyCube.README = RelationField("readme") +MicroStrategyCube.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +MicroStrategyCube.SODA_CHECKS = RelationField("sodaChecks") +MicroStrategyCube.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +MicroStrategyCube.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/micro_strategy_document.py b/pyatlan_v9/model/assets/micro_strategy_document.py new file mode 100644 index 000000000..4b8420263 --- /dev/null +++ b/pyatlan_v9/model/assets/micro_strategy_document.py @@ -0,0 +1,734 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +MicroStrategyDocument asset model with flattened inheritance. + +This module provides: +- MicroStrategyDocument: Flat asset class (easy to use) +- MicroStrategyDocumentAttributes: Nested attributes struct (extends AssetAttributes) +- MicroStrategyDocumentNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .micro_strategy_related import ( + RelatedMicroStrategyColumn, + RelatedMicroStrategyProject, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class MicroStrategyDocument(Asset): + """ + Instance of a MicroStrategy document in Atlan. + """ + + MICRO_STRATEGY_PROJECT_QUALIFIED_NAME: ClassVar[Any] = None + MICRO_STRATEGY_PROJECT_NAME: ClassVar[Any] = None + MICRO_STRATEGY_CUBE_QUALIFIED_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_CUBE_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_REPORT_QUALIFIED_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_REPORT_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_IS_CERTIFIED: ClassVar[Any] = None + MICRO_STRATEGY_CERTIFIED_BY: ClassVar[Any] = None + MICRO_STRATEGY_CERTIFIED_AT: ClassVar[Any] = None + MICRO_STRATEGY_LOCATION: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MICRO_STRATEGY_PROJECT: ClassVar[Any] = None + MICRO_STRATEGY_COLUMNS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "MicroStrategyDocument" + + micro_strategy_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this asset exists.""" + + micro_strategy_project_name: Union[str, None, UnsetType] = UNSET + """Simple name of the project in which this asset exists.""" + + micro_strategy_cube_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Unique names of the cubes related to this asset.""" + + micro_strategy_cube_names: Union[List[str], None, UnsetType] = UNSET + """Simple names of the cubes related to this asset.""" + + micro_strategy_report_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Unique names of the reports related to this asset.""" + + micro_strategy_report_names: Union[List[str], None, UnsetType] = UNSET + """Simple names of the reports related to this asset.""" + + micro_strategy_is_certified: Union[bool, None, UnsetType] = UNSET + """Whether the asset is certified in MicroStrategy (true) or not (false).""" + + micro_strategy_certified_by: Union[str, None, UnsetType] = UNSET + """User who certified this asset, in MicroStrategy.""" + + micro_strategy_certified_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) this asset was certified in MicroStrategy, in milliseconds.""" + + micro_strategy_location: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Location of this asset in MicroStrategy.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + micro_strategy_project: Union[RelatedMicroStrategyProject, None, UnsetType] = UNSET + """Project in which this document exists.""" + + micro_strategy_columns: Union[List[RelatedMicroStrategyColumn], None, UnsetType] = ( + UNSET + ) + """Individual columns contained in the document.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "MicroStrategyDocument" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _micro_strategy_document_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> MicroStrategyDocument: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + MicroStrategyDocument instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _micro_strategy_document_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class MicroStrategyDocumentAttributes(AssetAttributes): + """MicroStrategyDocument-specific attributes for nested API format.""" + + micro_strategy_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this asset exists.""" + + micro_strategy_project_name: Union[str, None, UnsetType] = UNSET + """Simple name of the project in which this asset exists.""" + + micro_strategy_cube_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Unique names of the cubes related to this asset.""" + + micro_strategy_cube_names: Union[List[str], None, UnsetType] = UNSET + """Simple names of the cubes related to this asset.""" + + micro_strategy_report_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Unique names of the reports related to this asset.""" + + micro_strategy_report_names: Union[List[str], None, UnsetType] = UNSET + """Simple names of the reports related to this asset.""" + + micro_strategy_is_certified: Union[bool, None, UnsetType] = UNSET + """Whether the asset is certified in MicroStrategy (true) or not (false).""" + + micro_strategy_certified_by: Union[str, None, UnsetType] = UNSET + """User who certified this asset, in MicroStrategy.""" + + micro_strategy_certified_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) this asset was certified in MicroStrategy, in milliseconds.""" + + micro_strategy_location: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Location of this asset in MicroStrategy.""" + + +class MicroStrategyDocumentRelationshipAttributes(AssetRelationshipAttributes): + """MicroStrategyDocument-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + micro_strategy_project: Union[RelatedMicroStrategyProject, None, UnsetType] = UNSET + """Project in which this document exists.""" + + micro_strategy_columns: Union[List[RelatedMicroStrategyColumn], None, UnsetType] = ( + UNSET + ) + """Individual columns contained in the document.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class MicroStrategyDocumentNested(AssetNested): + """MicroStrategyDocument in nested API format for high-performance serialization.""" + + attributes: Union[MicroStrategyDocumentAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + MicroStrategyDocumentRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + MicroStrategyDocumentRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + MicroStrategyDocumentRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_MICRO_STRATEGY_DOCUMENT_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "micro_strategy_project", + "micro_strategy_columns", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_micro_strategy_document_attrs( + attrs: MicroStrategyDocumentAttributes, obj: MicroStrategyDocument +) -> None: + """Populate MicroStrategyDocument-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.micro_strategy_project_qualified_name = ( + obj.micro_strategy_project_qualified_name + ) + attrs.micro_strategy_project_name = obj.micro_strategy_project_name + attrs.micro_strategy_cube_qualified_names = obj.micro_strategy_cube_qualified_names + attrs.micro_strategy_cube_names = obj.micro_strategy_cube_names + attrs.micro_strategy_report_qualified_names = ( + obj.micro_strategy_report_qualified_names + ) + attrs.micro_strategy_report_names = obj.micro_strategy_report_names + attrs.micro_strategy_is_certified = obj.micro_strategy_is_certified + attrs.micro_strategy_certified_by = obj.micro_strategy_certified_by + attrs.micro_strategy_certified_at = obj.micro_strategy_certified_at + attrs.micro_strategy_location = obj.micro_strategy_location + + +def _extract_micro_strategy_document_attrs( + attrs: MicroStrategyDocumentAttributes, +) -> dict: + """Extract all MicroStrategyDocument attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["micro_strategy_project_qualified_name"] = ( + attrs.micro_strategy_project_qualified_name + ) + result["micro_strategy_project_name"] = attrs.micro_strategy_project_name + result["micro_strategy_cube_qualified_names"] = ( + attrs.micro_strategy_cube_qualified_names + ) + result["micro_strategy_cube_names"] = attrs.micro_strategy_cube_names + result["micro_strategy_report_qualified_names"] = ( + attrs.micro_strategy_report_qualified_names + ) + result["micro_strategy_report_names"] = attrs.micro_strategy_report_names + result["micro_strategy_is_certified"] = attrs.micro_strategy_is_certified + result["micro_strategy_certified_by"] = attrs.micro_strategy_certified_by + result["micro_strategy_certified_at"] = attrs.micro_strategy_certified_at + result["micro_strategy_location"] = attrs.micro_strategy_location + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _micro_strategy_document_to_nested( + micro_strategy_document: MicroStrategyDocument, +) -> MicroStrategyDocumentNested: + """Convert flat MicroStrategyDocument to nested format.""" + attrs = MicroStrategyDocumentAttributes() + _populate_micro_strategy_document_attrs(attrs, micro_strategy_document) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + micro_strategy_document, + _MICRO_STRATEGY_DOCUMENT_REL_FIELDS, + MicroStrategyDocumentRelationshipAttributes, + ) + return MicroStrategyDocumentNested( + guid=micro_strategy_document.guid, + type_name=micro_strategy_document.type_name, + status=micro_strategy_document.status, + version=micro_strategy_document.version, + create_time=micro_strategy_document.create_time, + update_time=micro_strategy_document.update_time, + created_by=micro_strategy_document.created_by, + updated_by=micro_strategy_document.updated_by, + classifications=micro_strategy_document.classifications, + classification_names=micro_strategy_document.classification_names, + meanings=micro_strategy_document.meanings, + labels=micro_strategy_document.labels, + business_attributes=micro_strategy_document.business_attributes, + custom_attributes=micro_strategy_document.custom_attributes, + pending_tasks=micro_strategy_document.pending_tasks, + proxy=micro_strategy_document.proxy, + is_incomplete=micro_strategy_document.is_incomplete, + provenance_type=micro_strategy_document.provenance_type, + home_id=micro_strategy_document.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _micro_strategy_document_from_nested( + nested: MicroStrategyDocumentNested, +) -> MicroStrategyDocument: + """Convert nested format to flat MicroStrategyDocument.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else MicroStrategyDocumentAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _MICRO_STRATEGY_DOCUMENT_REL_FIELDS, + MicroStrategyDocumentRelationshipAttributes, + ) + return MicroStrategyDocument( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_micro_strategy_document_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _micro_strategy_document_to_nested_bytes( + micro_strategy_document: MicroStrategyDocument, serde: Serde +) -> bytes: + """Convert flat MicroStrategyDocument to nested JSON bytes.""" + return serde.encode(_micro_strategy_document_to_nested(micro_strategy_document)) + + +def _micro_strategy_document_from_nested_bytes( + data: bytes, serde: Serde +) -> MicroStrategyDocument: + """Convert nested JSON bytes to flat MicroStrategyDocument.""" + nested = serde.decode(data, MicroStrategyDocumentNested) + return _micro_strategy_document_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +MicroStrategyDocument.MICRO_STRATEGY_PROJECT_QUALIFIED_NAME = KeywordTextField( + "microStrategyProjectQualifiedName", + "microStrategyProjectQualifiedName", + "microStrategyProjectQualifiedName.text", +) +MicroStrategyDocument.MICRO_STRATEGY_PROJECT_NAME = KeywordTextField( + "microStrategyProjectName", + "microStrategyProjectName", + "microStrategyProjectName.text", +) +MicroStrategyDocument.MICRO_STRATEGY_CUBE_QUALIFIED_NAMES = KeywordTextField( + "microStrategyCubeQualifiedNames", + "microStrategyCubeQualifiedNames", + "microStrategyCubeQualifiedNames.text", +) +MicroStrategyDocument.MICRO_STRATEGY_CUBE_NAMES = KeywordField( + "microStrategyCubeNames", "microStrategyCubeNames" +) +MicroStrategyDocument.MICRO_STRATEGY_REPORT_QUALIFIED_NAMES = KeywordTextField( + "microStrategyReportQualifiedNames", + "microStrategyReportQualifiedNames", + "microStrategyReportQualifiedNames.text", +) +MicroStrategyDocument.MICRO_STRATEGY_REPORT_NAMES = KeywordField( + "microStrategyReportNames", "microStrategyReportNames" +) +MicroStrategyDocument.MICRO_STRATEGY_IS_CERTIFIED = BooleanField( + "microStrategyIsCertified", "microStrategyIsCertified" +) +MicroStrategyDocument.MICRO_STRATEGY_CERTIFIED_BY = KeywordField( + "microStrategyCertifiedBy", "microStrategyCertifiedBy" +) +MicroStrategyDocument.MICRO_STRATEGY_CERTIFIED_AT = NumericField( + "microStrategyCertifiedAt", "microStrategyCertifiedAt" +) +MicroStrategyDocument.MICRO_STRATEGY_LOCATION = KeywordField( + "microStrategyLocation", "microStrategyLocation" +) +MicroStrategyDocument.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +MicroStrategyDocument.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +MicroStrategyDocument.ANOMALO_CHECKS = RelationField("anomaloChecks") +MicroStrategyDocument.APPLICATION = RelationField("application") +MicroStrategyDocument.APPLICATION_FIELD = RelationField("applicationField") +MicroStrategyDocument.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +MicroStrategyDocument.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +MicroStrategyDocument.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +MicroStrategyDocument.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +MicroStrategyDocument.METRICS = RelationField("metrics") +MicroStrategyDocument.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +MicroStrategyDocument.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +MicroStrategyDocument.MEANINGS = RelationField("meanings") +MicroStrategyDocument.MICRO_STRATEGY_PROJECT = RelationField("microStrategyProject") +MicroStrategyDocument.MICRO_STRATEGY_COLUMNS = RelationField("microStrategyColumns") +MicroStrategyDocument.MC_MONITORS = RelationField("mcMonitors") +MicroStrategyDocument.MC_INCIDENTS = RelationField("mcIncidents") +MicroStrategyDocument.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +MicroStrategyDocument.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +MicroStrategyDocument.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +MicroStrategyDocument.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +MicroStrategyDocument.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +MicroStrategyDocument.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +MicroStrategyDocument.FILES = RelationField("files") +MicroStrategyDocument.LINKS = RelationField("links") +MicroStrategyDocument.README = RelationField("readme") +MicroStrategyDocument.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +MicroStrategyDocument.SODA_CHECKS = RelationField("sodaChecks") +MicroStrategyDocument.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +MicroStrategyDocument.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/micro_strategy_dossier.py b/pyatlan_v9/model/assets/micro_strategy_dossier.py new file mode 100644 index 000000000..4ebcb72b2 --- /dev/null +++ b/pyatlan_v9/model/assets/micro_strategy_dossier.py @@ -0,0 +1,762 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +MicroStrategyDossier asset model with flattened inheritance. + +This module provides: +- MicroStrategyDossier: Flat asset class (easy to use) +- MicroStrategyDossierAttributes: Nested attributes struct (extends AssetAttributes) +- MicroStrategyDossierNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .micro_strategy_related import ( + RelatedMicroStrategyColumn, + RelatedMicroStrategyProject, + RelatedMicroStrategyVisualization, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class MicroStrategyDossier(Asset): + """ + Instance of a MicroStrategy dossier in Atlan. + """ + + MICRO_STRATEGY_DOSSIER_CHAPTER_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_PROJECT_QUALIFIED_NAME: ClassVar[Any] = None + MICRO_STRATEGY_PROJECT_NAME: ClassVar[Any] = None + MICRO_STRATEGY_CUBE_QUALIFIED_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_CUBE_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_REPORT_QUALIFIED_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_REPORT_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_IS_CERTIFIED: ClassVar[Any] = None + MICRO_STRATEGY_CERTIFIED_BY: ClassVar[Any] = None + MICRO_STRATEGY_CERTIFIED_AT: ClassVar[Any] = None + MICRO_STRATEGY_LOCATION: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MICRO_STRATEGY_PROJECT: ClassVar[Any] = None + MICRO_STRATEGY_VISUALIZATIONS: ClassVar[Any] = None + MICRO_STRATEGY_COLUMNS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "MicroStrategyDossier" + + micro_strategy_dossier_chapter_names: Union[List[str], None, UnsetType] = UNSET + """List of chapter names in this dossier.""" + + micro_strategy_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this asset exists.""" + + micro_strategy_project_name: Union[str, None, UnsetType] = UNSET + """Simple name of the project in which this asset exists.""" + + micro_strategy_cube_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Unique names of the cubes related to this asset.""" + + micro_strategy_cube_names: Union[List[str], None, UnsetType] = UNSET + """Simple names of the cubes related to this asset.""" + + micro_strategy_report_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Unique names of the reports related to this asset.""" + + micro_strategy_report_names: Union[List[str], None, UnsetType] = UNSET + """Simple names of the reports related to this asset.""" + + micro_strategy_is_certified: Union[bool, None, UnsetType] = UNSET + """Whether the asset is certified in MicroStrategy (true) or not (false).""" + + micro_strategy_certified_by: Union[str, None, UnsetType] = UNSET + """User who certified this asset, in MicroStrategy.""" + + micro_strategy_certified_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) this asset was certified in MicroStrategy, in milliseconds.""" + + micro_strategy_location: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Location of this asset in MicroStrategy.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + micro_strategy_project: Union[RelatedMicroStrategyProject, None, UnsetType] = UNSET + """Project in which this dossier exists.""" + + micro_strategy_visualizations: Union[ + List[RelatedMicroStrategyVisualization], None, UnsetType + ] = UNSET + """Visualizations that exist within this dossier.""" + + micro_strategy_columns: Union[List[RelatedMicroStrategyColumn], None, UnsetType] = ( + UNSET + ) + """Individual columns contained in the dossier.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "MicroStrategyDossier" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _micro_strategy_dossier_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> MicroStrategyDossier: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + MicroStrategyDossier instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _micro_strategy_dossier_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class MicroStrategyDossierAttributes(AssetAttributes): + """MicroStrategyDossier-specific attributes for nested API format.""" + + micro_strategy_dossier_chapter_names: Union[List[str], None, UnsetType] = UNSET + """List of chapter names in this dossier.""" + + micro_strategy_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this asset exists.""" + + micro_strategy_project_name: Union[str, None, UnsetType] = UNSET + """Simple name of the project in which this asset exists.""" + + micro_strategy_cube_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Unique names of the cubes related to this asset.""" + + micro_strategy_cube_names: Union[List[str], None, UnsetType] = UNSET + """Simple names of the cubes related to this asset.""" + + micro_strategy_report_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Unique names of the reports related to this asset.""" + + micro_strategy_report_names: Union[List[str], None, UnsetType] = UNSET + """Simple names of the reports related to this asset.""" + + micro_strategy_is_certified: Union[bool, None, UnsetType] = UNSET + """Whether the asset is certified in MicroStrategy (true) or not (false).""" + + micro_strategy_certified_by: Union[str, None, UnsetType] = UNSET + """User who certified this asset, in MicroStrategy.""" + + micro_strategy_certified_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) this asset was certified in MicroStrategy, in milliseconds.""" + + micro_strategy_location: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Location of this asset in MicroStrategy.""" + + +class MicroStrategyDossierRelationshipAttributes(AssetRelationshipAttributes): + """MicroStrategyDossier-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + micro_strategy_project: Union[RelatedMicroStrategyProject, None, UnsetType] = UNSET + """Project in which this dossier exists.""" + + micro_strategy_visualizations: Union[ + List[RelatedMicroStrategyVisualization], None, UnsetType + ] = UNSET + """Visualizations that exist within this dossier.""" + + micro_strategy_columns: Union[List[RelatedMicroStrategyColumn], None, UnsetType] = ( + UNSET + ) + """Individual columns contained in the dossier.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class MicroStrategyDossierNested(AssetNested): + """MicroStrategyDossier in nested API format for high-performance serialization.""" + + attributes: Union[MicroStrategyDossierAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + MicroStrategyDossierRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + MicroStrategyDossierRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + MicroStrategyDossierRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_MICRO_STRATEGY_DOSSIER_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "micro_strategy_project", + "micro_strategy_visualizations", + "micro_strategy_columns", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_micro_strategy_dossier_attrs( + attrs: MicroStrategyDossierAttributes, obj: MicroStrategyDossier +) -> None: + """Populate MicroStrategyDossier-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.micro_strategy_dossier_chapter_names = ( + obj.micro_strategy_dossier_chapter_names + ) + attrs.micro_strategy_project_qualified_name = ( + obj.micro_strategy_project_qualified_name + ) + attrs.micro_strategy_project_name = obj.micro_strategy_project_name + attrs.micro_strategy_cube_qualified_names = obj.micro_strategy_cube_qualified_names + attrs.micro_strategy_cube_names = obj.micro_strategy_cube_names + attrs.micro_strategy_report_qualified_names = ( + obj.micro_strategy_report_qualified_names + ) + attrs.micro_strategy_report_names = obj.micro_strategy_report_names + attrs.micro_strategy_is_certified = obj.micro_strategy_is_certified + attrs.micro_strategy_certified_by = obj.micro_strategy_certified_by + attrs.micro_strategy_certified_at = obj.micro_strategy_certified_at + attrs.micro_strategy_location = obj.micro_strategy_location + + +def _extract_micro_strategy_dossier_attrs( + attrs: MicroStrategyDossierAttributes, +) -> dict: + """Extract all MicroStrategyDossier attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["micro_strategy_dossier_chapter_names"] = ( + attrs.micro_strategy_dossier_chapter_names + ) + result["micro_strategy_project_qualified_name"] = ( + attrs.micro_strategy_project_qualified_name + ) + result["micro_strategy_project_name"] = attrs.micro_strategy_project_name + result["micro_strategy_cube_qualified_names"] = ( + attrs.micro_strategy_cube_qualified_names + ) + result["micro_strategy_cube_names"] = attrs.micro_strategy_cube_names + result["micro_strategy_report_qualified_names"] = ( + attrs.micro_strategy_report_qualified_names + ) + result["micro_strategy_report_names"] = attrs.micro_strategy_report_names + result["micro_strategy_is_certified"] = attrs.micro_strategy_is_certified + result["micro_strategy_certified_by"] = attrs.micro_strategy_certified_by + result["micro_strategy_certified_at"] = attrs.micro_strategy_certified_at + result["micro_strategy_location"] = attrs.micro_strategy_location + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _micro_strategy_dossier_to_nested( + micro_strategy_dossier: MicroStrategyDossier, +) -> MicroStrategyDossierNested: + """Convert flat MicroStrategyDossier to nested format.""" + attrs = MicroStrategyDossierAttributes() + _populate_micro_strategy_dossier_attrs(attrs, micro_strategy_dossier) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + micro_strategy_dossier, + _MICRO_STRATEGY_DOSSIER_REL_FIELDS, + MicroStrategyDossierRelationshipAttributes, + ) + return MicroStrategyDossierNested( + guid=micro_strategy_dossier.guid, + type_name=micro_strategy_dossier.type_name, + status=micro_strategy_dossier.status, + version=micro_strategy_dossier.version, + create_time=micro_strategy_dossier.create_time, + update_time=micro_strategy_dossier.update_time, + created_by=micro_strategy_dossier.created_by, + updated_by=micro_strategy_dossier.updated_by, + classifications=micro_strategy_dossier.classifications, + classification_names=micro_strategy_dossier.classification_names, + meanings=micro_strategy_dossier.meanings, + labels=micro_strategy_dossier.labels, + business_attributes=micro_strategy_dossier.business_attributes, + custom_attributes=micro_strategy_dossier.custom_attributes, + pending_tasks=micro_strategy_dossier.pending_tasks, + proxy=micro_strategy_dossier.proxy, + is_incomplete=micro_strategy_dossier.is_incomplete, + provenance_type=micro_strategy_dossier.provenance_type, + home_id=micro_strategy_dossier.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _micro_strategy_dossier_from_nested( + nested: MicroStrategyDossierNested, +) -> MicroStrategyDossier: + """Convert nested format to flat MicroStrategyDossier.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else MicroStrategyDossierAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _MICRO_STRATEGY_DOSSIER_REL_FIELDS, + MicroStrategyDossierRelationshipAttributes, + ) + return MicroStrategyDossier( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_micro_strategy_dossier_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _micro_strategy_dossier_to_nested_bytes( + micro_strategy_dossier: MicroStrategyDossier, serde: Serde +) -> bytes: + """Convert flat MicroStrategyDossier to nested JSON bytes.""" + return serde.encode(_micro_strategy_dossier_to_nested(micro_strategy_dossier)) + + +def _micro_strategy_dossier_from_nested_bytes( + data: bytes, serde: Serde +) -> MicroStrategyDossier: + """Convert nested JSON bytes to flat MicroStrategyDossier.""" + nested = serde.decode(data, MicroStrategyDossierNested) + return _micro_strategy_dossier_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +MicroStrategyDossier.MICRO_STRATEGY_DOSSIER_CHAPTER_NAMES = KeywordField( + "microStrategyDossierChapterNames", "microStrategyDossierChapterNames" +) +MicroStrategyDossier.MICRO_STRATEGY_PROJECT_QUALIFIED_NAME = KeywordTextField( + "microStrategyProjectQualifiedName", + "microStrategyProjectQualifiedName", + "microStrategyProjectQualifiedName.text", +) +MicroStrategyDossier.MICRO_STRATEGY_PROJECT_NAME = KeywordTextField( + "microStrategyProjectName", + "microStrategyProjectName", + "microStrategyProjectName.text", +) +MicroStrategyDossier.MICRO_STRATEGY_CUBE_QUALIFIED_NAMES = KeywordTextField( + "microStrategyCubeQualifiedNames", + "microStrategyCubeQualifiedNames", + "microStrategyCubeQualifiedNames.text", +) +MicroStrategyDossier.MICRO_STRATEGY_CUBE_NAMES = KeywordField( + "microStrategyCubeNames", "microStrategyCubeNames" +) +MicroStrategyDossier.MICRO_STRATEGY_REPORT_QUALIFIED_NAMES = KeywordTextField( + "microStrategyReportQualifiedNames", + "microStrategyReportQualifiedNames", + "microStrategyReportQualifiedNames.text", +) +MicroStrategyDossier.MICRO_STRATEGY_REPORT_NAMES = KeywordField( + "microStrategyReportNames", "microStrategyReportNames" +) +MicroStrategyDossier.MICRO_STRATEGY_IS_CERTIFIED = BooleanField( + "microStrategyIsCertified", "microStrategyIsCertified" +) +MicroStrategyDossier.MICRO_STRATEGY_CERTIFIED_BY = KeywordField( + "microStrategyCertifiedBy", "microStrategyCertifiedBy" +) +MicroStrategyDossier.MICRO_STRATEGY_CERTIFIED_AT = NumericField( + "microStrategyCertifiedAt", "microStrategyCertifiedAt" +) +MicroStrategyDossier.MICRO_STRATEGY_LOCATION = KeywordField( + "microStrategyLocation", "microStrategyLocation" +) +MicroStrategyDossier.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +MicroStrategyDossier.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +MicroStrategyDossier.ANOMALO_CHECKS = RelationField("anomaloChecks") +MicroStrategyDossier.APPLICATION = RelationField("application") +MicroStrategyDossier.APPLICATION_FIELD = RelationField("applicationField") +MicroStrategyDossier.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +MicroStrategyDossier.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +MicroStrategyDossier.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +MicroStrategyDossier.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +MicroStrategyDossier.METRICS = RelationField("metrics") +MicroStrategyDossier.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +MicroStrategyDossier.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +MicroStrategyDossier.MEANINGS = RelationField("meanings") +MicroStrategyDossier.MICRO_STRATEGY_PROJECT = RelationField("microStrategyProject") +MicroStrategyDossier.MICRO_STRATEGY_VISUALIZATIONS = RelationField( + "microStrategyVisualizations" +) +MicroStrategyDossier.MICRO_STRATEGY_COLUMNS = RelationField("microStrategyColumns") +MicroStrategyDossier.MC_MONITORS = RelationField("mcMonitors") +MicroStrategyDossier.MC_INCIDENTS = RelationField("mcIncidents") +MicroStrategyDossier.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +MicroStrategyDossier.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +MicroStrategyDossier.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +MicroStrategyDossier.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +MicroStrategyDossier.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +MicroStrategyDossier.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +MicroStrategyDossier.FILES = RelationField("files") +MicroStrategyDossier.LINKS = RelationField("links") +MicroStrategyDossier.README = RelationField("readme") +MicroStrategyDossier.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +MicroStrategyDossier.SODA_CHECKS = RelationField("sodaChecks") +MicroStrategyDossier.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +MicroStrategyDossier.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/micro_strategy_fact.py b/pyatlan_v9/model/assets/micro_strategy_fact.py new file mode 100644 index 000000000..6a0783ac4 --- /dev/null +++ b/pyatlan_v9/model/assets/micro_strategy_fact.py @@ -0,0 +1,748 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +MicroStrategyFact asset model with flattened inheritance. + +This module provides: +- MicroStrategyFact: Flat asset class (easy to use) +- MicroStrategyFactAttributes: Nested attributes struct (extends AssetAttributes) +- MicroStrategyFactNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .micro_strategy_related import ( + RelatedMicroStrategyColumn, + RelatedMicroStrategyMetric, + RelatedMicroStrategyProject, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class MicroStrategyFact(Asset): + """ + Instance of a MicroStrategy fact in Atlan. + """ + + MICRO_STRATEGY_FACT_EXPRESSIONS: ClassVar[Any] = None + MICRO_STRATEGY_PROJECT_QUALIFIED_NAME: ClassVar[Any] = None + MICRO_STRATEGY_PROJECT_NAME: ClassVar[Any] = None + MICRO_STRATEGY_CUBE_QUALIFIED_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_CUBE_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_REPORT_QUALIFIED_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_REPORT_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_IS_CERTIFIED: ClassVar[Any] = None + MICRO_STRATEGY_CERTIFIED_BY: ClassVar[Any] = None + MICRO_STRATEGY_CERTIFIED_AT: ClassVar[Any] = None + MICRO_STRATEGY_LOCATION: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MICRO_STRATEGY_PROJECT: ClassVar[Any] = None + MICRO_STRATEGY_METRICS: ClassVar[Any] = None + MICRO_STRATEGY_COLUMNS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "MicroStrategyFact" + + micro_strategy_fact_expressions: Union[List[str], None, UnsetType] = UNSET + """List of expressions for this fact.""" + + micro_strategy_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this asset exists.""" + + micro_strategy_project_name: Union[str, None, UnsetType] = UNSET + """Simple name of the project in which this asset exists.""" + + micro_strategy_cube_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Unique names of the cubes related to this asset.""" + + micro_strategy_cube_names: Union[List[str], None, UnsetType] = UNSET + """Simple names of the cubes related to this asset.""" + + micro_strategy_report_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Unique names of the reports related to this asset.""" + + micro_strategy_report_names: Union[List[str], None, UnsetType] = UNSET + """Simple names of the reports related to this asset.""" + + micro_strategy_is_certified: Union[bool, None, UnsetType] = UNSET + """Whether the asset is certified in MicroStrategy (true) or not (false).""" + + micro_strategy_certified_by: Union[str, None, UnsetType] = UNSET + """User who certified this asset, in MicroStrategy.""" + + micro_strategy_certified_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) this asset was certified in MicroStrategy, in milliseconds.""" + + micro_strategy_location: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Location of this asset in MicroStrategy.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + micro_strategy_project: Union[RelatedMicroStrategyProject, None, UnsetType] = UNSET + """Project in which this fact exists.""" + + micro_strategy_metrics: Union[List[RelatedMicroStrategyMetric], None, UnsetType] = ( + UNSET + ) + """Metrics that use this fact.""" + + micro_strategy_columns: Union[List[RelatedMicroStrategyColumn], None, UnsetType] = ( + UNSET + ) + """Individual columns contained in the fact.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "MicroStrategyFact" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _micro_strategy_fact_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> MicroStrategyFact: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + MicroStrategyFact instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _micro_strategy_fact_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class MicroStrategyFactAttributes(AssetAttributes): + """MicroStrategyFact-specific attributes for nested API format.""" + + micro_strategy_fact_expressions: Union[List[str], None, UnsetType] = UNSET + """List of expressions for this fact.""" + + micro_strategy_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this asset exists.""" + + micro_strategy_project_name: Union[str, None, UnsetType] = UNSET + """Simple name of the project in which this asset exists.""" + + micro_strategy_cube_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Unique names of the cubes related to this asset.""" + + micro_strategy_cube_names: Union[List[str], None, UnsetType] = UNSET + """Simple names of the cubes related to this asset.""" + + micro_strategy_report_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Unique names of the reports related to this asset.""" + + micro_strategy_report_names: Union[List[str], None, UnsetType] = UNSET + """Simple names of the reports related to this asset.""" + + micro_strategy_is_certified: Union[bool, None, UnsetType] = UNSET + """Whether the asset is certified in MicroStrategy (true) or not (false).""" + + micro_strategy_certified_by: Union[str, None, UnsetType] = UNSET + """User who certified this asset, in MicroStrategy.""" + + micro_strategy_certified_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) this asset was certified in MicroStrategy, in milliseconds.""" + + micro_strategy_location: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Location of this asset in MicroStrategy.""" + + +class MicroStrategyFactRelationshipAttributes(AssetRelationshipAttributes): + """MicroStrategyFact-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + micro_strategy_project: Union[RelatedMicroStrategyProject, None, UnsetType] = UNSET + """Project in which this fact exists.""" + + micro_strategy_metrics: Union[List[RelatedMicroStrategyMetric], None, UnsetType] = ( + UNSET + ) + """Metrics that use this fact.""" + + micro_strategy_columns: Union[List[RelatedMicroStrategyColumn], None, UnsetType] = ( + UNSET + ) + """Individual columns contained in the fact.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class MicroStrategyFactNested(AssetNested): + """MicroStrategyFact in nested API format for high-performance serialization.""" + + attributes: Union[MicroStrategyFactAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + MicroStrategyFactRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + MicroStrategyFactRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + MicroStrategyFactRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_MICRO_STRATEGY_FACT_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "micro_strategy_project", + "micro_strategy_metrics", + "micro_strategy_columns", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_micro_strategy_fact_attrs( + attrs: MicroStrategyFactAttributes, obj: MicroStrategyFact +) -> None: + """Populate MicroStrategyFact-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.micro_strategy_fact_expressions = obj.micro_strategy_fact_expressions + attrs.micro_strategy_project_qualified_name = ( + obj.micro_strategy_project_qualified_name + ) + attrs.micro_strategy_project_name = obj.micro_strategy_project_name + attrs.micro_strategy_cube_qualified_names = obj.micro_strategy_cube_qualified_names + attrs.micro_strategy_cube_names = obj.micro_strategy_cube_names + attrs.micro_strategy_report_qualified_names = ( + obj.micro_strategy_report_qualified_names + ) + attrs.micro_strategy_report_names = obj.micro_strategy_report_names + attrs.micro_strategy_is_certified = obj.micro_strategy_is_certified + attrs.micro_strategy_certified_by = obj.micro_strategy_certified_by + attrs.micro_strategy_certified_at = obj.micro_strategy_certified_at + attrs.micro_strategy_location = obj.micro_strategy_location + + +def _extract_micro_strategy_fact_attrs(attrs: MicroStrategyFactAttributes) -> dict: + """Extract all MicroStrategyFact attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["micro_strategy_fact_expressions"] = attrs.micro_strategy_fact_expressions + result["micro_strategy_project_qualified_name"] = ( + attrs.micro_strategy_project_qualified_name + ) + result["micro_strategy_project_name"] = attrs.micro_strategy_project_name + result["micro_strategy_cube_qualified_names"] = ( + attrs.micro_strategy_cube_qualified_names + ) + result["micro_strategy_cube_names"] = attrs.micro_strategy_cube_names + result["micro_strategy_report_qualified_names"] = ( + attrs.micro_strategy_report_qualified_names + ) + result["micro_strategy_report_names"] = attrs.micro_strategy_report_names + result["micro_strategy_is_certified"] = attrs.micro_strategy_is_certified + result["micro_strategy_certified_by"] = attrs.micro_strategy_certified_by + result["micro_strategy_certified_at"] = attrs.micro_strategy_certified_at + result["micro_strategy_location"] = attrs.micro_strategy_location + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _micro_strategy_fact_to_nested( + micro_strategy_fact: MicroStrategyFact, +) -> MicroStrategyFactNested: + """Convert flat MicroStrategyFact to nested format.""" + attrs = MicroStrategyFactAttributes() + _populate_micro_strategy_fact_attrs(attrs, micro_strategy_fact) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + micro_strategy_fact, + _MICRO_STRATEGY_FACT_REL_FIELDS, + MicroStrategyFactRelationshipAttributes, + ) + return MicroStrategyFactNested( + guid=micro_strategy_fact.guid, + type_name=micro_strategy_fact.type_name, + status=micro_strategy_fact.status, + version=micro_strategy_fact.version, + create_time=micro_strategy_fact.create_time, + update_time=micro_strategy_fact.update_time, + created_by=micro_strategy_fact.created_by, + updated_by=micro_strategy_fact.updated_by, + classifications=micro_strategy_fact.classifications, + classification_names=micro_strategy_fact.classification_names, + meanings=micro_strategy_fact.meanings, + labels=micro_strategy_fact.labels, + business_attributes=micro_strategy_fact.business_attributes, + custom_attributes=micro_strategy_fact.custom_attributes, + pending_tasks=micro_strategy_fact.pending_tasks, + proxy=micro_strategy_fact.proxy, + is_incomplete=micro_strategy_fact.is_incomplete, + provenance_type=micro_strategy_fact.provenance_type, + home_id=micro_strategy_fact.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _micro_strategy_fact_from_nested( + nested: MicroStrategyFactNested, +) -> MicroStrategyFact: + """Convert nested format to flat MicroStrategyFact.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else MicroStrategyFactAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _MICRO_STRATEGY_FACT_REL_FIELDS, + MicroStrategyFactRelationshipAttributes, + ) + return MicroStrategyFact( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_micro_strategy_fact_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _micro_strategy_fact_to_nested_bytes( + micro_strategy_fact: MicroStrategyFact, serde: Serde +) -> bytes: + """Convert flat MicroStrategyFact to nested JSON bytes.""" + return serde.encode(_micro_strategy_fact_to_nested(micro_strategy_fact)) + + +def _micro_strategy_fact_from_nested_bytes( + data: bytes, serde: Serde +) -> MicroStrategyFact: + """Convert nested JSON bytes to flat MicroStrategyFact.""" + nested = serde.decode(data, MicroStrategyFactNested) + return _micro_strategy_fact_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +MicroStrategyFact.MICRO_STRATEGY_FACT_EXPRESSIONS = KeywordField( + "microStrategyFactExpressions", "microStrategyFactExpressions" +) +MicroStrategyFact.MICRO_STRATEGY_PROJECT_QUALIFIED_NAME = KeywordTextField( + "microStrategyProjectQualifiedName", + "microStrategyProjectQualifiedName", + "microStrategyProjectQualifiedName.text", +) +MicroStrategyFact.MICRO_STRATEGY_PROJECT_NAME = KeywordTextField( + "microStrategyProjectName", + "microStrategyProjectName", + "microStrategyProjectName.text", +) +MicroStrategyFact.MICRO_STRATEGY_CUBE_QUALIFIED_NAMES = KeywordTextField( + "microStrategyCubeQualifiedNames", + "microStrategyCubeQualifiedNames", + "microStrategyCubeQualifiedNames.text", +) +MicroStrategyFact.MICRO_STRATEGY_CUBE_NAMES = KeywordField( + "microStrategyCubeNames", "microStrategyCubeNames" +) +MicroStrategyFact.MICRO_STRATEGY_REPORT_QUALIFIED_NAMES = KeywordTextField( + "microStrategyReportQualifiedNames", + "microStrategyReportQualifiedNames", + "microStrategyReportQualifiedNames.text", +) +MicroStrategyFact.MICRO_STRATEGY_REPORT_NAMES = KeywordField( + "microStrategyReportNames", "microStrategyReportNames" +) +MicroStrategyFact.MICRO_STRATEGY_IS_CERTIFIED = BooleanField( + "microStrategyIsCertified", "microStrategyIsCertified" +) +MicroStrategyFact.MICRO_STRATEGY_CERTIFIED_BY = KeywordField( + "microStrategyCertifiedBy", "microStrategyCertifiedBy" +) +MicroStrategyFact.MICRO_STRATEGY_CERTIFIED_AT = NumericField( + "microStrategyCertifiedAt", "microStrategyCertifiedAt" +) +MicroStrategyFact.MICRO_STRATEGY_LOCATION = KeywordField( + "microStrategyLocation", "microStrategyLocation" +) +MicroStrategyFact.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +MicroStrategyFact.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +MicroStrategyFact.ANOMALO_CHECKS = RelationField("anomaloChecks") +MicroStrategyFact.APPLICATION = RelationField("application") +MicroStrategyFact.APPLICATION_FIELD = RelationField("applicationField") +MicroStrategyFact.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +MicroStrategyFact.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +MicroStrategyFact.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +MicroStrategyFact.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +MicroStrategyFact.METRICS = RelationField("metrics") +MicroStrategyFact.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +MicroStrategyFact.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +MicroStrategyFact.MEANINGS = RelationField("meanings") +MicroStrategyFact.MICRO_STRATEGY_PROJECT = RelationField("microStrategyProject") +MicroStrategyFact.MICRO_STRATEGY_METRICS = RelationField("microStrategyMetrics") +MicroStrategyFact.MICRO_STRATEGY_COLUMNS = RelationField("microStrategyColumns") +MicroStrategyFact.MC_MONITORS = RelationField("mcMonitors") +MicroStrategyFact.MC_INCIDENTS = RelationField("mcIncidents") +MicroStrategyFact.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +MicroStrategyFact.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +MicroStrategyFact.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +MicroStrategyFact.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +MicroStrategyFact.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +MicroStrategyFact.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +MicroStrategyFact.FILES = RelationField("files") +MicroStrategyFact.LINKS = RelationField("links") +MicroStrategyFact.README = RelationField("readme") +MicroStrategyFact.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +MicroStrategyFact.SODA_CHECKS = RelationField("sodaChecks") +MicroStrategyFact.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +MicroStrategyFact.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/micro_strategy_metric.py b/pyatlan_v9/model/assets/micro_strategy_metric.py new file mode 100644 index 000000000..9d63596c6 --- /dev/null +++ b/pyatlan_v9/model/assets/micro_strategy_metric.py @@ -0,0 +1,913 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +MicroStrategyMetric asset model with flattened inheritance. + +This module provides: +- MicroStrategyMetric: Flat asset class (easy to use) +- MicroStrategyMetricAttributes: Nested attributes struct (extends AssetAttributes) +- MicroStrategyMetricNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .micro_strategy_related import ( + RelatedMicroStrategyAttribute, + RelatedMicroStrategyColumn, + RelatedMicroStrategyCube, + RelatedMicroStrategyFact, + RelatedMicroStrategyMetric, + RelatedMicroStrategyProject, + RelatedMicroStrategyReport, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class MicroStrategyMetric(Asset): + """ + Instance of a MicroStrategy metric in Atlan. + """ + + MICRO_STRATEGY_METRIC_EXPRESSION: ClassVar[Any] = None + MICRO_STRATEGY_ATTRIBUTE_QUALIFIED_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_ATTRIBUTE_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_FACT_QUALIFIED_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_FACT_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_METRIC_PARENT_QUALIFIED_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_METRIC_PARENT_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_PROJECT_QUALIFIED_NAME: ClassVar[Any] = None + MICRO_STRATEGY_PROJECT_NAME: ClassVar[Any] = None + MICRO_STRATEGY_CUBE_QUALIFIED_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_CUBE_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_REPORT_QUALIFIED_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_REPORT_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_IS_CERTIFIED: ClassVar[Any] = None + MICRO_STRATEGY_CERTIFIED_BY: ClassVar[Any] = None + MICRO_STRATEGY_CERTIFIED_AT: ClassVar[Any] = None + MICRO_STRATEGY_LOCATION: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MICRO_STRATEGY_PROJECT: ClassVar[Any] = None + MICRO_STRATEGY_ATTRIBUTES: ClassVar[Any] = None + MICRO_STRATEGY_FACTS: ClassVar[Any] = None + MICRO_STRATEGY_METRIC_CHILDREN: ClassVar[Any] = None + MICRO_STRATEGY_METRIC_PARENTS: ClassVar[Any] = None + MICRO_STRATEGY_CUBES: ClassVar[Any] = None + MICRO_STRATEGY_REPORTS: ClassVar[Any] = None + MICRO_STRATEGY_COLUMNS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "MicroStrategyMetric" + + micro_strategy_metric_expression: Union[str, None, UnsetType] = UNSET + """Text specifiying this metric's expression.""" + + micro_strategy_attribute_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of unique names of attributes related to this metric.""" + + micro_strategy_attribute_names: Union[List[str], None, UnsetType] = UNSET + """List of simple names of attributes related to this metric.""" + + micro_strategy_fact_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of unique names of facts related to this metric.""" + + micro_strategy_fact_names: Union[List[str], None, UnsetType] = UNSET + """List of simple names of facts related to this metric.""" + + micro_strategy_metric_parent_qualified_names: Union[List[str], None, UnsetType] = ( + UNSET + ) + """List of unique names of parent metrics of this metric.""" + + micro_strategy_metric_parent_names: Union[List[str], None, UnsetType] = UNSET + """List of simple names of parent metrics of this metric.""" + + micro_strategy_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this asset exists.""" + + micro_strategy_project_name: Union[str, None, UnsetType] = UNSET + """Simple name of the project in which this asset exists.""" + + micro_strategy_cube_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Unique names of the cubes related to this asset.""" + + micro_strategy_cube_names: Union[List[str], None, UnsetType] = UNSET + """Simple names of the cubes related to this asset.""" + + micro_strategy_report_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Unique names of the reports related to this asset.""" + + micro_strategy_report_names: Union[List[str], None, UnsetType] = UNSET + """Simple names of the reports related to this asset.""" + + micro_strategy_is_certified: Union[bool, None, UnsetType] = UNSET + """Whether the asset is certified in MicroStrategy (true) or not (false).""" + + micro_strategy_certified_by: Union[str, None, UnsetType] = UNSET + """User who certified this asset, in MicroStrategy.""" + + micro_strategy_certified_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) this asset was certified in MicroStrategy, in milliseconds.""" + + micro_strategy_location: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Location of this asset in MicroStrategy.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + micro_strategy_project: Union[RelatedMicroStrategyProject, None, UnsetType] = UNSET + """Project in which this metric exists.""" + + micro_strategy_attributes: Union[ + List[RelatedMicroStrategyAttribute], None, UnsetType + ] = UNSET + """Attributes this metric uses.""" + + micro_strategy_facts: Union[List[RelatedMicroStrategyFact], None, UnsetType] = UNSET + """Facts this metric uses.""" + + micro_strategy_metric_children: Union[ + List[RelatedMicroStrategyMetric], None, UnsetType + ] = UNSET + """Child metrics of this metric.""" + + micro_strategy_metric_parents: Union[ + List[RelatedMicroStrategyMetric], None, UnsetType + ] = UNSET + """Parent metrics to this metric.""" + + micro_strategy_cubes: Union[List[RelatedMicroStrategyCube], None, UnsetType] = UNSET + """Cubes this metric uses.""" + + micro_strategy_reports: Union[List[RelatedMicroStrategyReport], None, UnsetType] = ( + UNSET + ) + """Reports in which this metric is used.""" + + micro_strategy_columns: Union[List[RelatedMicroStrategyColumn], None, UnsetType] = ( + UNSET + ) + """Individual columns contained in the metric.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "MicroStrategyMetric" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _micro_strategy_metric_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> MicroStrategyMetric: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + MicroStrategyMetric instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _micro_strategy_metric_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class MicroStrategyMetricAttributes(AssetAttributes): + """MicroStrategyMetric-specific attributes for nested API format.""" + + micro_strategy_metric_expression: Union[str, None, UnsetType] = UNSET + """Text specifiying this metric's expression.""" + + micro_strategy_attribute_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of unique names of attributes related to this metric.""" + + micro_strategy_attribute_names: Union[List[str], None, UnsetType] = UNSET + """List of simple names of attributes related to this metric.""" + + micro_strategy_fact_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of unique names of facts related to this metric.""" + + micro_strategy_fact_names: Union[List[str], None, UnsetType] = UNSET + """List of simple names of facts related to this metric.""" + + micro_strategy_metric_parent_qualified_names: Union[List[str], None, UnsetType] = ( + UNSET + ) + """List of unique names of parent metrics of this metric.""" + + micro_strategy_metric_parent_names: Union[List[str], None, UnsetType] = UNSET + """List of simple names of parent metrics of this metric.""" + + micro_strategy_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this asset exists.""" + + micro_strategy_project_name: Union[str, None, UnsetType] = UNSET + """Simple name of the project in which this asset exists.""" + + micro_strategy_cube_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Unique names of the cubes related to this asset.""" + + micro_strategy_cube_names: Union[List[str], None, UnsetType] = UNSET + """Simple names of the cubes related to this asset.""" + + micro_strategy_report_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Unique names of the reports related to this asset.""" + + micro_strategy_report_names: Union[List[str], None, UnsetType] = UNSET + """Simple names of the reports related to this asset.""" + + micro_strategy_is_certified: Union[bool, None, UnsetType] = UNSET + """Whether the asset is certified in MicroStrategy (true) or not (false).""" + + micro_strategy_certified_by: Union[str, None, UnsetType] = UNSET + """User who certified this asset, in MicroStrategy.""" + + micro_strategy_certified_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) this asset was certified in MicroStrategy, in milliseconds.""" + + micro_strategy_location: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Location of this asset in MicroStrategy.""" + + +class MicroStrategyMetricRelationshipAttributes(AssetRelationshipAttributes): + """MicroStrategyMetric-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + micro_strategy_project: Union[RelatedMicroStrategyProject, None, UnsetType] = UNSET + """Project in which this metric exists.""" + + micro_strategy_attributes: Union[ + List[RelatedMicroStrategyAttribute], None, UnsetType + ] = UNSET + """Attributes this metric uses.""" + + micro_strategy_facts: Union[List[RelatedMicroStrategyFact], None, UnsetType] = UNSET + """Facts this metric uses.""" + + micro_strategy_metric_children: Union[ + List[RelatedMicroStrategyMetric], None, UnsetType + ] = UNSET + """Child metrics of this metric.""" + + micro_strategy_metric_parents: Union[ + List[RelatedMicroStrategyMetric], None, UnsetType + ] = UNSET + """Parent metrics to this metric.""" + + micro_strategy_cubes: Union[List[RelatedMicroStrategyCube], None, UnsetType] = UNSET + """Cubes this metric uses.""" + + micro_strategy_reports: Union[List[RelatedMicroStrategyReport], None, UnsetType] = ( + UNSET + ) + """Reports in which this metric is used.""" + + micro_strategy_columns: Union[List[RelatedMicroStrategyColumn], None, UnsetType] = ( + UNSET + ) + """Individual columns contained in the metric.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class MicroStrategyMetricNested(AssetNested): + """MicroStrategyMetric in nested API format for high-performance serialization.""" + + attributes: Union[MicroStrategyMetricAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + MicroStrategyMetricRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + MicroStrategyMetricRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + MicroStrategyMetricRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_MICRO_STRATEGY_METRIC_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "micro_strategy_project", + "micro_strategy_attributes", + "micro_strategy_facts", + "micro_strategy_metric_children", + "micro_strategy_metric_parents", + "micro_strategy_cubes", + "micro_strategy_reports", + "micro_strategy_columns", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_micro_strategy_metric_attrs( + attrs: MicroStrategyMetricAttributes, obj: MicroStrategyMetric +) -> None: + """Populate MicroStrategyMetric-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.micro_strategy_metric_expression = obj.micro_strategy_metric_expression + attrs.micro_strategy_attribute_qualified_names = ( + obj.micro_strategy_attribute_qualified_names + ) + attrs.micro_strategy_attribute_names = obj.micro_strategy_attribute_names + attrs.micro_strategy_fact_qualified_names = obj.micro_strategy_fact_qualified_names + attrs.micro_strategy_fact_names = obj.micro_strategy_fact_names + attrs.micro_strategy_metric_parent_qualified_names = ( + obj.micro_strategy_metric_parent_qualified_names + ) + attrs.micro_strategy_metric_parent_names = obj.micro_strategy_metric_parent_names + attrs.micro_strategy_project_qualified_name = ( + obj.micro_strategy_project_qualified_name + ) + attrs.micro_strategy_project_name = obj.micro_strategy_project_name + attrs.micro_strategy_cube_qualified_names = obj.micro_strategy_cube_qualified_names + attrs.micro_strategy_cube_names = obj.micro_strategy_cube_names + attrs.micro_strategy_report_qualified_names = ( + obj.micro_strategy_report_qualified_names + ) + attrs.micro_strategy_report_names = obj.micro_strategy_report_names + attrs.micro_strategy_is_certified = obj.micro_strategy_is_certified + attrs.micro_strategy_certified_by = obj.micro_strategy_certified_by + attrs.micro_strategy_certified_at = obj.micro_strategy_certified_at + attrs.micro_strategy_location = obj.micro_strategy_location + + +def _extract_micro_strategy_metric_attrs(attrs: MicroStrategyMetricAttributes) -> dict: + """Extract all MicroStrategyMetric attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["micro_strategy_metric_expression"] = attrs.micro_strategy_metric_expression + result["micro_strategy_attribute_qualified_names"] = ( + attrs.micro_strategy_attribute_qualified_names + ) + result["micro_strategy_attribute_names"] = attrs.micro_strategy_attribute_names + result["micro_strategy_fact_qualified_names"] = ( + attrs.micro_strategy_fact_qualified_names + ) + result["micro_strategy_fact_names"] = attrs.micro_strategy_fact_names + result["micro_strategy_metric_parent_qualified_names"] = ( + attrs.micro_strategy_metric_parent_qualified_names + ) + result["micro_strategy_metric_parent_names"] = ( + attrs.micro_strategy_metric_parent_names + ) + result["micro_strategy_project_qualified_name"] = ( + attrs.micro_strategy_project_qualified_name + ) + result["micro_strategy_project_name"] = attrs.micro_strategy_project_name + result["micro_strategy_cube_qualified_names"] = ( + attrs.micro_strategy_cube_qualified_names + ) + result["micro_strategy_cube_names"] = attrs.micro_strategy_cube_names + result["micro_strategy_report_qualified_names"] = ( + attrs.micro_strategy_report_qualified_names + ) + result["micro_strategy_report_names"] = attrs.micro_strategy_report_names + result["micro_strategy_is_certified"] = attrs.micro_strategy_is_certified + result["micro_strategy_certified_by"] = attrs.micro_strategy_certified_by + result["micro_strategy_certified_at"] = attrs.micro_strategy_certified_at + result["micro_strategy_location"] = attrs.micro_strategy_location + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _micro_strategy_metric_to_nested( + micro_strategy_metric: MicroStrategyMetric, +) -> MicroStrategyMetricNested: + """Convert flat MicroStrategyMetric to nested format.""" + attrs = MicroStrategyMetricAttributes() + _populate_micro_strategy_metric_attrs(attrs, micro_strategy_metric) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + micro_strategy_metric, + _MICRO_STRATEGY_METRIC_REL_FIELDS, + MicroStrategyMetricRelationshipAttributes, + ) + return MicroStrategyMetricNested( + guid=micro_strategy_metric.guid, + type_name=micro_strategy_metric.type_name, + status=micro_strategy_metric.status, + version=micro_strategy_metric.version, + create_time=micro_strategy_metric.create_time, + update_time=micro_strategy_metric.update_time, + created_by=micro_strategy_metric.created_by, + updated_by=micro_strategy_metric.updated_by, + classifications=micro_strategy_metric.classifications, + classification_names=micro_strategy_metric.classification_names, + meanings=micro_strategy_metric.meanings, + labels=micro_strategy_metric.labels, + business_attributes=micro_strategy_metric.business_attributes, + custom_attributes=micro_strategy_metric.custom_attributes, + pending_tasks=micro_strategy_metric.pending_tasks, + proxy=micro_strategy_metric.proxy, + is_incomplete=micro_strategy_metric.is_incomplete, + provenance_type=micro_strategy_metric.provenance_type, + home_id=micro_strategy_metric.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _micro_strategy_metric_from_nested( + nested: MicroStrategyMetricNested, +) -> MicroStrategyMetric: + """Convert nested format to flat MicroStrategyMetric.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else MicroStrategyMetricAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _MICRO_STRATEGY_METRIC_REL_FIELDS, + MicroStrategyMetricRelationshipAttributes, + ) + return MicroStrategyMetric( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_micro_strategy_metric_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _micro_strategy_metric_to_nested_bytes( + micro_strategy_metric: MicroStrategyMetric, serde: Serde +) -> bytes: + """Convert flat MicroStrategyMetric to nested JSON bytes.""" + return serde.encode(_micro_strategy_metric_to_nested(micro_strategy_metric)) + + +def _micro_strategy_metric_from_nested_bytes( + data: bytes, serde: Serde +) -> MicroStrategyMetric: + """Convert nested JSON bytes to flat MicroStrategyMetric.""" + nested = serde.decode(data, MicroStrategyMetricNested) + return _micro_strategy_metric_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +MicroStrategyMetric.MICRO_STRATEGY_METRIC_EXPRESSION = KeywordField( + "microStrategyMetricExpression", "microStrategyMetricExpression" +) +MicroStrategyMetric.MICRO_STRATEGY_ATTRIBUTE_QUALIFIED_NAMES = KeywordTextField( + "microStrategyAttributeQualifiedNames", + "microStrategyAttributeQualifiedNames", + "microStrategyAttributeQualifiedNames.text", +) +MicroStrategyMetric.MICRO_STRATEGY_ATTRIBUTE_NAMES = KeywordField( + "microStrategyAttributeNames", "microStrategyAttributeNames" +) +MicroStrategyMetric.MICRO_STRATEGY_FACT_QUALIFIED_NAMES = KeywordTextField( + "microStrategyFactQualifiedNames", + "microStrategyFactQualifiedNames", + "microStrategyFactQualifiedNames.text", +) +MicroStrategyMetric.MICRO_STRATEGY_FACT_NAMES = KeywordField( + "microStrategyFactNames", "microStrategyFactNames" +) +MicroStrategyMetric.MICRO_STRATEGY_METRIC_PARENT_QUALIFIED_NAMES = KeywordTextField( + "microStrategyMetricParentQualifiedNames", + "microStrategyMetricParentQualifiedNames", + "microStrategyMetricParentQualifiedNames.text", +) +MicroStrategyMetric.MICRO_STRATEGY_METRIC_PARENT_NAMES = KeywordField( + "microStrategyMetricParentNames", "microStrategyMetricParentNames" +) +MicroStrategyMetric.MICRO_STRATEGY_PROJECT_QUALIFIED_NAME = KeywordTextField( + "microStrategyProjectQualifiedName", + "microStrategyProjectQualifiedName", + "microStrategyProjectQualifiedName.text", +) +MicroStrategyMetric.MICRO_STRATEGY_PROJECT_NAME = KeywordTextField( + "microStrategyProjectName", + "microStrategyProjectName", + "microStrategyProjectName.text", +) +MicroStrategyMetric.MICRO_STRATEGY_CUBE_QUALIFIED_NAMES = KeywordTextField( + "microStrategyCubeQualifiedNames", + "microStrategyCubeQualifiedNames", + "microStrategyCubeQualifiedNames.text", +) +MicroStrategyMetric.MICRO_STRATEGY_CUBE_NAMES = KeywordField( + "microStrategyCubeNames", "microStrategyCubeNames" +) +MicroStrategyMetric.MICRO_STRATEGY_REPORT_QUALIFIED_NAMES = KeywordTextField( + "microStrategyReportQualifiedNames", + "microStrategyReportQualifiedNames", + "microStrategyReportQualifiedNames.text", +) +MicroStrategyMetric.MICRO_STRATEGY_REPORT_NAMES = KeywordField( + "microStrategyReportNames", "microStrategyReportNames" +) +MicroStrategyMetric.MICRO_STRATEGY_IS_CERTIFIED = BooleanField( + "microStrategyIsCertified", "microStrategyIsCertified" +) +MicroStrategyMetric.MICRO_STRATEGY_CERTIFIED_BY = KeywordField( + "microStrategyCertifiedBy", "microStrategyCertifiedBy" +) +MicroStrategyMetric.MICRO_STRATEGY_CERTIFIED_AT = NumericField( + "microStrategyCertifiedAt", "microStrategyCertifiedAt" +) +MicroStrategyMetric.MICRO_STRATEGY_LOCATION = KeywordField( + "microStrategyLocation", "microStrategyLocation" +) +MicroStrategyMetric.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +MicroStrategyMetric.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +MicroStrategyMetric.ANOMALO_CHECKS = RelationField("anomaloChecks") +MicroStrategyMetric.APPLICATION = RelationField("application") +MicroStrategyMetric.APPLICATION_FIELD = RelationField("applicationField") +MicroStrategyMetric.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +MicroStrategyMetric.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +MicroStrategyMetric.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +MicroStrategyMetric.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +MicroStrategyMetric.METRICS = RelationField("metrics") +MicroStrategyMetric.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +MicroStrategyMetric.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +MicroStrategyMetric.MEANINGS = RelationField("meanings") +MicroStrategyMetric.MICRO_STRATEGY_PROJECT = RelationField("microStrategyProject") +MicroStrategyMetric.MICRO_STRATEGY_ATTRIBUTES = RelationField("microStrategyAttributes") +MicroStrategyMetric.MICRO_STRATEGY_FACTS = RelationField("microStrategyFacts") +MicroStrategyMetric.MICRO_STRATEGY_METRIC_CHILDREN = RelationField( + "microStrategyMetricChildren" +) +MicroStrategyMetric.MICRO_STRATEGY_METRIC_PARENTS = RelationField( + "microStrategyMetricParents" +) +MicroStrategyMetric.MICRO_STRATEGY_CUBES = RelationField("microStrategyCubes") +MicroStrategyMetric.MICRO_STRATEGY_REPORTS = RelationField("microStrategyReports") +MicroStrategyMetric.MICRO_STRATEGY_COLUMNS = RelationField("microStrategyColumns") +MicroStrategyMetric.MC_MONITORS = RelationField("mcMonitors") +MicroStrategyMetric.MC_INCIDENTS = RelationField("mcIncidents") +MicroStrategyMetric.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +MicroStrategyMetric.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +MicroStrategyMetric.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +MicroStrategyMetric.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +MicroStrategyMetric.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +MicroStrategyMetric.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +MicroStrategyMetric.FILES = RelationField("files") +MicroStrategyMetric.LINKS = RelationField("links") +MicroStrategyMetric.README = RelationField("readme") +MicroStrategyMetric.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +MicroStrategyMetric.SODA_CHECKS = RelationField("sodaChecks") +MicroStrategyMetric.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +MicroStrategyMetric.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/micro_strategy_project.py b/pyatlan_v9/model/assets/micro_strategy_project.py new file mode 100644 index 000000000..d6137384a --- /dev/null +++ b/pyatlan_v9/model/assets/micro_strategy_project.py @@ -0,0 +1,807 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +MicroStrategyProject asset model with flattened inheritance. + +This module provides: +- MicroStrategyProject: Flat asset class (easy to use) +- MicroStrategyProjectAttributes: Nested attributes struct (extends AssetAttributes) +- MicroStrategyProjectNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .micro_strategy_related import ( + RelatedMicroStrategyAttribute, + RelatedMicroStrategyCube, + RelatedMicroStrategyDocument, + RelatedMicroStrategyDossier, + RelatedMicroStrategyFact, + RelatedMicroStrategyMetric, + RelatedMicroStrategyReport, + RelatedMicroStrategyVisualization, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class MicroStrategyProject(Asset): + """ + Instance of a MicroStrategy project in Atlan. + """ + + MICRO_STRATEGY_PROJECT_QUALIFIED_NAME: ClassVar[Any] = None + MICRO_STRATEGY_PROJECT_NAME: ClassVar[Any] = None + MICRO_STRATEGY_CUBE_QUALIFIED_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_CUBE_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_REPORT_QUALIFIED_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_REPORT_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_IS_CERTIFIED: ClassVar[Any] = None + MICRO_STRATEGY_CERTIFIED_BY: ClassVar[Any] = None + MICRO_STRATEGY_CERTIFIED_AT: ClassVar[Any] = None + MICRO_STRATEGY_LOCATION: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MICRO_STRATEGY_METRICS: ClassVar[Any] = None + MICRO_STRATEGY_REPORTS: ClassVar[Any] = None + MICRO_STRATEGY_VISUALIZATIONS: ClassVar[Any] = None + MICRO_STRATEGY_ATTRIBUTES: ClassVar[Any] = None + MICRO_STRATEGY_CUBES: ClassVar[Any] = None + MICRO_STRATEGY_DOCUMENTS: ClassVar[Any] = None + MICRO_STRATEGY_DOSSIERS: ClassVar[Any] = None + MICRO_STRATEGY_FACTS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "MicroStrategyProject" + + micro_strategy_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this asset exists.""" + + micro_strategy_project_name: Union[str, None, UnsetType] = UNSET + """Simple name of the project in which this asset exists.""" + + micro_strategy_cube_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Unique names of the cubes related to this asset.""" + + micro_strategy_cube_names: Union[List[str], None, UnsetType] = UNSET + """Simple names of the cubes related to this asset.""" + + micro_strategy_report_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Unique names of the reports related to this asset.""" + + micro_strategy_report_names: Union[List[str], None, UnsetType] = UNSET + """Simple names of the reports related to this asset.""" + + micro_strategy_is_certified: Union[bool, None, UnsetType] = UNSET + """Whether the asset is certified in MicroStrategy (true) or not (false).""" + + micro_strategy_certified_by: Union[str, None, UnsetType] = UNSET + """User who certified this asset, in MicroStrategy.""" + + micro_strategy_certified_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) this asset was certified in MicroStrategy, in milliseconds.""" + + micro_strategy_location: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Location of this asset in MicroStrategy.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + micro_strategy_metrics: Union[List[RelatedMicroStrategyMetric], None, UnsetType] = ( + UNSET + ) + """Metrics that exist within this project.""" + + micro_strategy_reports: Union[List[RelatedMicroStrategyReport], None, UnsetType] = ( + UNSET + ) + """Reports that exist within this project.""" + + micro_strategy_visualizations: Union[ + List[RelatedMicroStrategyVisualization], None, UnsetType + ] = UNSET + """Visualizations that exist within this project.""" + + micro_strategy_attributes: Union[ + List[RelatedMicroStrategyAttribute], None, UnsetType + ] = UNSET + """Attributes that exist within this project.""" + + micro_strategy_cubes: Union[List[RelatedMicroStrategyCube], None, UnsetType] = UNSET + """Cubes that exist within this project.""" + + micro_strategy_documents: Union[ + List[RelatedMicroStrategyDocument], None, UnsetType + ] = UNSET + """Documents that exist within this project.""" + + micro_strategy_dossiers: Union[ + List[RelatedMicroStrategyDossier], None, UnsetType + ] = UNSET + """Dossiers that exist within this project.""" + + micro_strategy_facts: Union[List[RelatedMicroStrategyFact], None, UnsetType] = UNSET + """Facts that exist within this project.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "MicroStrategyProject" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _micro_strategy_project_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> MicroStrategyProject: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + MicroStrategyProject instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _micro_strategy_project_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class MicroStrategyProjectAttributes(AssetAttributes): + """MicroStrategyProject-specific attributes for nested API format.""" + + micro_strategy_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this asset exists.""" + + micro_strategy_project_name: Union[str, None, UnsetType] = UNSET + """Simple name of the project in which this asset exists.""" + + micro_strategy_cube_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Unique names of the cubes related to this asset.""" + + micro_strategy_cube_names: Union[List[str], None, UnsetType] = UNSET + """Simple names of the cubes related to this asset.""" + + micro_strategy_report_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Unique names of the reports related to this asset.""" + + micro_strategy_report_names: Union[List[str], None, UnsetType] = UNSET + """Simple names of the reports related to this asset.""" + + micro_strategy_is_certified: Union[bool, None, UnsetType] = UNSET + """Whether the asset is certified in MicroStrategy (true) or not (false).""" + + micro_strategy_certified_by: Union[str, None, UnsetType] = UNSET + """User who certified this asset, in MicroStrategy.""" + + micro_strategy_certified_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) this asset was certified in MicroStrategy, in milliseconds.""" + + micro_strategy_location: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Location of this asset in MicroStrategy.""" + + +class MicroStrategyProjectRelationshipAttributes(AssetRelationshipAttributes): + """MicroStrategyProject-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + micro_strategy_metrics: Union[List[RelatedMicroStrategyMetric], None, UnsetType] = ( + UNSET + ) + """Metrics that exist within this project.""" + + micro_strategy_reports: Union[List[RelatedMicroStrategyReport], None, UnsetType] = ( + UNSET + ) + """Reports that exist within this project.""" + + micro_strategy_visualizations: Union[ + List[RelatedMicroStrategyVisualization], None, UnsetType + ] = UNSET + """Visualizations that exist within this project.""" + + micro_strategy_attributes: Union[ + List[RelatedMicroStrategyAttribute], None, UnsetType + ] = UNSET + """Attributes that exist within this project.""" + + micro_strategy_cubes: Union[List[RelatedMicroStrategyCube], None, UnsetType] = UNSET + """Cubes that exist within this project.""" + + micro_strategy_documents: Union[ + List[RelatedMicroStrategyDocument], None, UnsetType + ] = UNSET + """Documents that exist within this project.""" + + micro_strategy_dossiers: Union[ + List[RelatedMicroStrategyDossier], None, UnsetType + ] = UNSET + """Dossiers that exist within this project.""" + + micro_strategy_facts: Union[List[RelatedMicroStrategyFact], None, UnsetType] = UNSET + """Facts that exist within this project.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class MicroStrategyProjectNested(AssetNested): + """MicroStrategyProject in nested API format for high-performance serialization.""" + + attributes: Union[MicroStrategyProjectAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + MicroStrategyProjectRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + MicroStrategyProjectRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + MicroStrategyProjectRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_MICRO_STRATEGY_PROJECT_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "micro_strategy_metrics", + "micro_strategy_reports", + "micro_strategy_visualizations", + "micro_strategy_attributes", + "micro_strategy_cubes", + "micro_strategy_documents", + "micro_strategy_dossiers", + "micro_strategy_facts", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_micro_strategy_project_attrs( + attrs: MicroStrategyProjectAttributes, obj: MicroStrategyProject +) -> None: + """Populate MicroStrategyProject-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.micro_strategy_project_qualified_name = ( + obj.micro_strategy_project_qualified_name + ) + attrs.micro_strategy_project_name = obj.micro_strategy_project_name + attrs.micro_strategy_cube_qualified_names = obj.micro_strategy_cube_qualified_names + attrs.micro_strategy_cube_names = obj.micro_strategy_cube_names + attrs.micro_strategy_report_qualified_names = ( + obj.micro_strategy_report_qualified_names + ) + attrs.micro_strategy_report_names = obj.micro_strategy_report_names + attrs.micro_strategy_is_certified = obj.micro_strategy_is_certified + attrs.micro_strategy_certified_by = obj.micro_strategy_certified_by + attrs.micro_strategy_certified_at = obj.micro_strategy_certified_at + attrs.micro_strategy_location = obj.micro_strategy_location + + +def _extract_micro_strategy_project_attrs( + attrs: MicroStrategyProjectAttributes, +) -> dict: + """Extract all MicroStrategyProject attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["micro_strategy_project_qualified_name"] = ( + attrs.micro_strategy_project_qualified_name + ) + result["micro_strategy_project_name"] = attrs.micro_strategy_project_name + result["micro_strategy_cube_qualified_names"] = ( + attrs.micro_strategy_cube_qualified_names + ) + result["micro_strategy_cube_names"] = attrs.micro_strategy_cube_names + result["micro_strategy_report_qualified_names"] = ( + attrs.micro_strategy_report_qualified_names + ) + result["micro_strategy_report_names"] = attrs.micro_strategy_report_names + result["micro_strategy_is_certified"] = attrs.micro_strategy_is_certified + result["micro_strategy_certified_by"] = attrs.micro_strategy_certified_by + result["micro_strategy_certified_at"] = attrs.micro_strategy_certified_at + result["micro_strategy_location"] = attrs.micro_strategy_location + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _micro_strategy_project_to_nested( + micro_strategy_project: MicroStrategyProject, +) -> MicroStrategyProjectNested: + """Convert flat MicroStrategyProject to nested format.""" + attrs = MicroStrategyProjectAttributes() + _populate_micro_strategy_project_attrs(attrs, micro_strategy_project) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + micro_strategy_project, + _MICRO_STRATEGY_PROJECT_REL_FIELDS, + MicroStrategyProjectRelationshipAttributes, + ) + return MicroStrategyProjectNested( + guid=micro_strategy_project.guid, + type_name=micro_strategy_project.type_name, + status=micro_strategy_project.status, + version=micro_strategy_project.version, + create_time=micro_strategy_project.create_time, + update_time=micro_strategy_project.update_time, + created_by=micro_strategy_project.created_by, + updated_by=micro_strategy_project.updated_by, + classifications=micro_strategy_project.classifications, + classification_names=micro_strategy_project.classification_names, + meanings=micro_strategy_project.meanings, + labels=micro_strategy_project.labels, + business_attributes=micro_strategy_project.business_attributes, + custom_attributes=micro_strategy_project.custom_attributes, + pending_tasks=micro_strategy_project.pending_tasks, + proxy=micro_strategy_project.proxy, + is_incomplete=micro_strategy_project.is_incomplete, + provenance_type=micro_strategy_project.provenance_type, + home_id=micro_strategy_project.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _micro_strategy_project_from_nested( + nested: MicroStrategyProjectNested, +) -> MicroStrategyProject: + """Convert nested format to flat MicroStrategyProject.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else MicroStrategyProjectAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _MICRO_STRATEGY_PROJECT_REL_FIELDS, + MicroStrategyProjectRelationshipAttributes, + ) + return MicroStrategyProject( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_micro_strategy_project_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _micro_strategy_project_to_nested_bytes( + micro_strategy_project: MicroStrategyProject, serde: Serde +) -> bytes: + """Convert flat MicroStrategyProject to nested JSON bytes.""" + return serde.encode(_micro_strategy_project_to_nested(micro_strategy_project)) + + +def _micro_strategy_project_from_nested_bytes( + data: bytes, serde: Serde +) -> MicroStrategyProject: + """Convert nested JSON bytes to flat MicroStrategyProject.""" + nested = serde.decode(data, MicroStrategyProjectNested) + return _micro_strategy_project_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +MicroStrategyProject.MICRO_STRATEGY_PROJECT_QUALIFIED_NAME = KeywordTextField( + "microStrategyProjectQualifiedName", + "microStrategyProjectQualifiedName", + "microStrategyProjectQualifiedName.text", +) +MicroStrategyProject.MICRO_STRATEGY_PROJECT_NAME = KeywordTextField( + "microStrategyProjectName", + "microStrategyProjectName", + "microStrategyProjectName.text", +) +MicroStrategyProject.MICRO_STRATEGY_CUBE_QUALIFIED_NAMES = KeywordTextField( + "microStrategyCubeQualifiedNames", + "microStrategyCubeQualifiedNames", + "microStrategyCubeQualifiedNames.text", +) +MicroStrategyProject.MICRO_STRATEGY_CUBE_NAMES = KeywordField( + "microStrategyCubeNames", "microStrategyCubeNames" +) +MicroStrategyProject.MICRO_STRATEGY_REPORT_QUALIFIED_NAMES = KeywordTextField( + "microStrategyReportQualifiedNames", + "microStrategyReportQualifiedNames", + "microStrategyReportQualifiedNames.text", +) +MicroStrategyProject.MICRO_STRATEGY_REPORT_NAMES = KeywordField( + "microStrategyReportNames", "microStrategyReportNames" +) +MicroStrategyProject.MICRO_STRATEGY_IS_CERTIFIED = BooleanField( + "microStrategyIsCertified", "microStrategyIsCertified" +) +MicroStrategyProject.MICRO_STRATEGY_CERTIFIED_BY = KeywordField( + "microStrategyCertifiedBy", "microStrategyCertifiedBy" +) +MicroStrategyProject.MICRO_STRATEGY_CERTIFIED_AT = NumericField( + "microStrategyCertifiedAt", "microStrategyCertifiedAt" +) +MicroStrategyProject.MICRO_STRATEGY_LOCATION = KeywordField( + "microStrategyLocation", "microStrategyLocation" +) +MicroStrategyProject.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +MicroStrategyProject.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +MicroStrategyProject.ANOMALO_CHECKS = RelationField("anomaloChecks") +MicroStrategyProject.APPLICATION = RelationField("application") +MicroStrategyProject.APPLICATION_FIELD = RelationField("applicationField") +MicroStrategyProject.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +MicroStrategyProject.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +MicroStrategyProject.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +MicroStrategyProject.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +MicroStrategyProject.METRICS = RelationField("metrics") +MicroStrategyProject.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +MicroStrategyProject.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +MicroStrategyProject.MEANINGS = RelationField("meanings") +MicroStrategyProject.MICRO_STRATEGY_METRICS = RelationField("microStrategyMetrics") +MicroStrategyProject.MICRO_STRATEGY_REPORTS = RelationField("microStrategyReports") +MicroStrategyProject.MICRO_STRATEGY_VISUALIZATIONS = RelationField( + "microStrategyVisualizations" +) +MicroStrategyProject.MICRO_STRATEGY_ATTRIBUTES = RelationField( + "microStrategyAttributes" +) +MicroStrategyProject.MICRO_STRATEGY_CUBES = RelationField("microStrategyCubes") +MicroStrategyProject.MICRO_STRATEGY_DOCUMENTS = RelationField("microStrategyDocuments") +MicroStrategyProject.MICRO_STRATEGY_DOSSIERS = RelationField("microStrategyDossiers") +MicroStrategyProject.MICRO_STRATEGY_FACTS = RelationField("microStrategyFacts") +MicroStrategyProject.MC_MONITORS = RelationField("mcMonitors") +MicroStrategyProject.MC_INCIDENTS = RelationField("mcIncidents") +MicroStrategyProject.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +MicroStrategyProject.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +MicroStrategyProject.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +MicroStrategyProject.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +MicroStrategyProject.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +MicroStrategyProject.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +MicroStrategyProject.FILES = RelationField("files") +MicroStrategyProject.LINKS = RelationField("links") +MicroStrategyProject.README = RelationField("readme") +MicroStrategyProject.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +MicroStrategyProject.SODA_CHECKS = RelationField("sodaChecks") +MicroStrategyProject.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +MicroStrategyProject.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/micro_strategy_related.py b/pyatlan_v9/model/assets/micro_strategy_related.py new file mode 100644 index 000000000..c68f5bc07 --- /dev/null +++ b/pyatlan_v9/model/assets/micro_strategy_related.py @@ -0,0 +1,314 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for MicroStrategy module. + +This module contains all Related{Type} classes for the MicroStrategy type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedBI +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedMicroStrategy", + "RelatedMicroStrategyProject", + "RelatedMicroStrategyDocument", + "RelatedMicroStrategyReport", + "RelatedMicroStrategyCube", + "RelatedMicroStrategyDossier", + "RelatedMicroStrategyFact", + "RelatedMicroStrategyAttribute", + "RelatedMicroStrategyVisualization", + "RelatedMicroStrategyMetric", + "RelatedMicroStrategyColumn", +] + + +class RelatedMicroStrategy(RelatedBI): + """ + Related entity reference for MicroStrategy assets. + + Extends RelatedBI with MicroStrategy-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "MicroStrategy" so it serializes correctly + + micro_strategy_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this asset exists.""" + + micro_strategy_project_name: Union[str, None, UnsetType] = UNSET + """Simple name of the project in which this asset exists.""" + + micro_strategy_cube_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Unique names of the cubes related to this asset.""" + + micro_strategy_cube_names: Union[List[str], None, UnsetType] = UNSET + """Simple names of the cubes related to this asset.""" + + micro_strategy_report_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Unique names of the reports related to this asset.""" + + micro_strategy_report_names: Union[List[str], None, UnsetType] = UNSET + """Simple names of the reports related to this asset.""" + + micro_strategy_is_certified: Union[bool, None, UnsetType] = UNSET + """Whether the asset is certified in MicroStrategy (true) or not (false).""" + + micro_strategy_certified_by: Union[str, None, UnsetType] = UNSET + """User who certified this asset, in MicroStrategy.""" + + micro_strategy_certified_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) this asset was certified in MicroStrategy, in milliseconds.""" + + micro_strategy_location: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Location of this asset in MicroStrategy.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "MicroStrategy" + + +class RelatedMicroStrategyProject(RelatedMicroStrategy): + """ + Related entity reference for MicroStrategyProject assets. + + Extends RelatedMicroStrategy with MicroStrategyProject-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "MicroStrategyProject" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "MicroStrategyProject" + + +class RelatedMicroStrategyDocument(RelatedMicroStrategy): + """ + Related entity reference for MicroStrategyDocument assets. + + Extends RelatedMicroStrategy with MicroStrategyDocument-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "MicroStrategyDocument" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "MicroStrategyDocument" + + +class RelatedMicroStrategyReport(RelatedMicroStrategy): + """ + Related entity reference for MicroStrategyReport assets. + + Extends RelatedMicroStrategy with MicroStrategyReport-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "MicroStrategyReport" so it serializes correctly + + micro_strategy_report_type: Union[str, None, UnsetType] = UNSET + """Type of report, for example: Grid or Chart.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "MicroStrategyReport" + + +class RelatedMicroStrategyCube(RelatedMicroStrategy): + """ + Related entity reference for MicroStrategyCube assets. + + Extends RelatedMicroStrategy with MicroStrategyCube-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "MicroStrategyCube" so it serializes correctly + + micro_strategy_cube_type: Union[str, None, UnsetType] = UNSET + """Type of cube, for example: OLAP or MTDI.""" + + micro_strategy_cube_query: Union[str, None, UnsetType] = UNSET + """Query used to create the cube.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "MicroStrategyCube" + + +class RelatedMicroStrategyDossier(RelatedMicroStrategy): + """ + Related entity reference for MicroStrategyDossier assets. + + Extends RelatedMicroStrategy with MicroStrategyDossier-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "MicroStrategyDossier" so it serializes correctly + + micro_strategy_dossier_chapter_names: Union[List[str], None, UnsetType] = UNSET + """List of chapter names in this dossier.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "MicroStrategyDossier" + + +class RelatedMicroStrategyFact(RelatedMicroStrategy): + """ + Related entity reference for MicroStrategyFact assets. + + Extends RelatedMicroStrategy with MicroStrategyFact-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "MicroStrategyFact" so it serializes correctly + + micro_strategy_fact_expressions: Union[List[str], None, UnsetType] = UNSET + """List of expressions for this fact.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "MicroStrategyFact" + + +class RelatedMicroStrategyAttribute(RelatedMicroStrategy): + """ + Related entity reference for MicroStrategyAttribute assets. + + Extends RelatedMicroStrategy with MicroStrategyAttribute-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "MicroStrategyAttribute" so it serializes correctly + + micro_strategy_attribute_forms: Union[str, None, UnsetType] = UNSET + """JSON string specifying the attribute's name, description, displayFormat, etc.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "MicroStrategyAttribute" + + +class RelatedMicroStrategyVisualization(RelatedMicroStrategy): + """ + Related entity reference for MicroStrategyVisualization assets. + + Extends RelatedMicroStrategy with MicroStrategyVisualization-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "MicroStrategyVisualization" so it serializes correctly + + micro_strategy_visualization_type: Union[str, None, UnsetType] = UNSET + """Type of visualization.""" + + micro_strategy_dossier_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dossier in which this visualization exists.""" + + micro_strategy_dossier_name: Union[str, None, UnsetType] = UNSET + """Simple name of the dossier in which this visualization exists.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "MicroStrategyVisualization" + + +class RelatedMicroStrategyMetric(RelatedMicroStrategy): + """ + Related entity reference for MicroStrategyMetric assets. + + Extends RelatedMicroStrategy with MicroStrategyMetric-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "MicroStrategyMetric" so it serializes correctly + + micro_strategy_metric_expression: Union[str, None, UnsetType] = UNSET + """Text specifiying this metric's expression.""" + + micro_strategy_attribute_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of unique names of attributes related to this metric.""" + + micro_strategy_attribute_names: Union[List[str], None, UnsetType] = UNSET + """List of simple names of attributes related to this metric.""" + + micro_strategy_fact_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of unique names of facts related to this metric.""" + + micro_strategy_fact_names: Union[List[str], None, UnsetType] = UNSET + """List of simple names of facts related to this metric.""" + + micro_strategy_metric_parent_qualified_names: Union[List[str], None, UnsetType] = ( + UNSET + ) + """List of unique names of parent metrics of this metric.""" + + micro_strategy_metric_parent_names: Union[List[str], None, UnsetType] = UNSET + """List of simple names of parent metrics of this metric.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "MicroStrategyMetric" + + +class RelatedMicroStrategyColumn(RelatedMicroStrategy): + """ + Related entity reference for MicroStrategyColumn assets. + + Extends RelatedMicroStrategy with MicroStrategyColumn-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "MicroStrategyColumn" so it serializes correctly + + micro_strategy_column_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the column in MicroStrategy.""" + + micro_strategy_column_type: Union[str, None, UnsetType] = UNSET + """Type of the column (Eg attribute_column, fact_column, metric_column etc).""" + + micro_strategy_data_type: Union[str, None, UnsetType] = UNSET + """Data type of the column.""" + + micro_strategy_column_attribute_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique identifier of the Attribute in which this column exists.""" + + micro_strategy_column_fact_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique identifier of the Fact in which this column exists.""" + + micro_strategy_column_metric_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique identifier of the Metric in which this column exists.""" + + micro_strategy_column_cube_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique identifier of the Cube in which this column exists.""" + + micro_strategy_column_report_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique identifier of the Report in which this column exists.""" + + micro_strategy_column_dossier_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique identifier of the Dossier in which this column exists.""" + + micro_strategy_column_document_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique identifier of the Document in which this column exists.""" + + micro_strategy_parent_name: Union[str, None, UnsetType] = UNSET + """Name of the parent asset.""" + + micro_strategy_column_expression: Union[str, None, UnsetType] = UNSET + """Expression or formula used to define this column.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "MicroStrategyColumn" diff --git a/pyatlan_v9/model/assets/micro_strategy_report.py b/pyatlan_v9/model/assets/micro_strategy_report.py new file mode 100644 index 000000000..898e5431e --- /dev/null +++ b/pyatlan_v9/model/assets/micro_strategy_report.py @@ -0,0 +1,768 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +MicroStrategyReport asset model with flattened inheritance. + +This module provides: +- MicroStrategyReport: Flat asset class (easy to use) +- MicroStrategyReportAttributes: Nested attributes struct (extends AssetAttributes) +- MicroStrategyReportNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .micro_strategy_related import ( + RelatedMicroStrategyAttribute, + RelatedMicroStrategyColumn, + RelatedMicroStrategyMetric, + RelatedMicroStrategyProject, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class MicroStrategyReport(Asset): + """ + Instance of a MicroStrategy report in Atlan. + """ + + MICRO_STRATEGY_REPORT_TYPE: ClassVar[Any] = None + MICRO_STRATEGY_PROJECT_QUALIFIED_NAME: ClassVar[Any] = None + MICRO_STRATEGY_PROJECT_NAME: ClassVar[Any] = None + MICRO_STRATEGY_CUBE_QUALIFIED_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_CUBE_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_REPORT_QUALIFIED_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_REPORT_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_IS_CERTIFIED: ClassVar[Any] = None + MICRO_STRATEGY_CERTIFIED_BY: ClassVar[Any] = None + MICRO_STRATEGY_CERTIFIED_AT: ClassVar[Any] = None + MICRO_STRATEGY_LOCATION: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MICRO_STRATEGY_PROJECT: ClassVar[Any] = None + MICRO_STRATEGY_METRICS: ClassVar[Any] = None + MICRO_STRATEGY_ATTRIBUTES: ClassVar[Any] = None + MICRO_STRATEGY_COLUMNS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "MicroStrategyReport" + + micro_strategy_report_type: Union[str, None, UnsetType] = UNSET + """Type of report, for example: Grid or Chart.""" + + micro_strategy_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this asset exists.""" + + micro_strategy_project_name: Union[str, None, UnsetType] = UNSET + """Simple name of the project in which this asset exists.""" + + micro_strategy_cube_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Unique names of the cubes related to this asset.""" + + micro_strategy_cube_names: Union[List[str], None, UnsetType] = UNSET + """Simple names of the cubes related to this asset.""" + + micro_strategy_report_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Unique names of the reports related to this asset.""" + + micro_strategy_report_names: Union[List[str], None, UnsetType] = UNSET + """Simple names of the reports related to this asset.""" + + micro_strategy_is_certified: Union[bool, None, UnsetType] = UNSET + """Whether the asset is certified in MicroStrategy (true) or not (false).""" + + micro_strategy_certified_by: Union[str, None, UnsetType] = UNSET + """User who certified this asset, in MicroStrategy.""" + + micro_strategy_certified_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) this asset was certified in MicroStrategy, in milliseconds.""" + + micro_strategy_location: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Location of this asset in MicroStrategy.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + micro_strategy_project: Union[RelatedMicroStrategyProject, None, UnsetType] = UNSET + """Project in which this report exists.""" + + micro_strategy_metrics: Union[List[RelatedMicroStrategyMetric], None, UnsetType] = ( + UNSET + ) + """Metrics used by this report.""" + + micro_strategy_attributes: Union[ + List[RelatedMicroStrategyAttribute], None, UnsetType + ] = UNSET + """Attributes used by this report.""" + + micro_strategy_columns: Union[List[RelatedMicroStrategyColumn], None, UnsetType] = ( + UNSET + ) + """Individual columns contained in the report.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "MicroStrategyReport" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _micro_strategy_report_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> MicroStrategyReport: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + MicroStrategyReport instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _micro_strategy_report_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class MicroStrategyReportAttributes(AssetAttributes): + """MicroStrategyReport-specific attributes for nested API format.""" + + micro_strategy_report_type: Union[str, None, UnsetType] = UNSET + """Type of report, for example: Grid or Chart.""" + + micro_strategy_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this asset exists.""" + + micro_strategy_project_name: Union[str, None, UnsetType] = UNSET + """Simple name of the project in which this asset exists.""" + + micro_strategy_cube_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Unique names of the cubes related to this asset.""" + + micro_strategy_cube_names: Union[List[str], None, UnsetType] = UNSET + """Simple names of the cubes related to this asset.""" + + micro_strategy_report_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Unique names of the reports related to this asset.""" + + micro_strategy_report_names: Union[List[str], None, UnsetType] = UNSET + """Simple names of the reports related to this asset.""" + + micro_strategy_is_certified: Union[bool, None, UnsetType] = UNSET + """Whether the asset is certified in MicroStrategy (true) or not (false).""" + + micro_strategy_certified_by: Union[str, None, UnsetType] = UNSET + """User who certified this asset, in MicroStrategy.""" + + micro_strategy_certified_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) this asset was certified in MicroStrategy, in milliseconds.""" + + micro_strategy_location: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Location of this asset in MicroStrategy.""" + + +class MicroStrategyReportRelationshipAttributes(AssetRelationshipAttributes): + """MicroStrategyReport-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + micro_strategy_project: Union[RelatedMicroStrategyProject, None, UnsetType] = UNSET + """Project in which this report exists.""" + + micro_strategy_metrics: Union[List[RelatedMicroStrategyMetric], None, UnsetType] = ( + UNSET + ) + """Metrics used by this report.""" + + micro_strategy_attributes: Union[ + List[RelatedMicroStrategyAttribute], None, UnsetType + ] = UNSET + """Attributes used by this report.""" + + micro_strategy_columns: Union[List[RelatedMicroStrategyColumn], None, UnsetType] = ( + UNSET + ) + """Individual columns contained in the report.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class MicroStrategyReportNested(AssetNested): + """MicroStrategyReport in nested API format for high-performance serialization.""" + + attributes: Union[MicroStrategyReportAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + MicroStrategyReportRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + MicroStrategyReportRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + MicroStrategyReportRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_MICRO_STRATEGY_REPORT_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "micro_strategy_project", + "micro_strategy_metrics", + "micro_strategy_attributes", + "micro_strategy_columns", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_micro_strategy_report_attrs( + attrs: MicroStrategyReportAttributes, obj: MicroStrategyReport +) -> None: + """Populate MicroStrategyReport-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.micro_strategy_report_type = obj.micro_strategy_report_type + attrs.micro_strategy_project_qualified_name = ( + obj.micro_strategy_project_qualified_name + ) + attrs.micro_strategy_project_name = obj.micro_strategy_project_name + attrs.micro_strategy_cube_qualified_names = obj.micro_strategy_cube_qualified_names + attrs.micro_strategy_cube_names = obj.micro_strategy_cube_names + attrs.micro_strategy_report_qualified_names = ( + obj.micro_strategy_report_qualified_names + ) + attrs.micro_strategy_report_names = obj.micro_strategy_report_names + attrs.micro_strategy_is_certified = obj.micro_strategy_is_certified + attrs.micro_strategy_certified_by = obj.micro_strategy_certified_by + attrs.micro_strategy_certified_at = obj.micro_strategy_certified_at + attrs.micro_strategy_location = obj.micro_strategy_location + + +def _extract_micro_strategy_report_attrs(attrs: MicroStrategyReportAttributes) -> dict: + """Extract all MicroStrategyReport attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["micro_strategy_report_type"] = attrs.micro_strategy_report_type + result["micro_strategy_project_qualified_name"] = ( + attrs.micro_strategy_project_qualified_name + ) + result["micro_strategy_project_name"] = attrs.micro_strategy_project_name + result["micro_strategy_cube_qualified_names"] = ( + attrs.micro_strategy_cube_qualified_names + ) + result["micro_strategy_cube_names"] = attrs.micro_strategy_cube_names + result["micro_strategy_report_qualified_names"] = ( + attrs.micro_strategy_report_qualified_names + ) + result["micro_strategy_report_names"] = attrs.micro_strategy_report_names + result["micro_strategy_is_certified"] = attrs.micro_strategy_is_certified + result["micro_strategy_certified_by"] = attrs.micro_strategy_certified_by + result["micro_strategy_certified_at"] = attrs.micro_strategy_certified_at + result["micro_strategy_location"] = attrs.micro_strategy_location + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _micro_strategy_report_to_nested( + micro_strategy_report: MicroStrategyReport, +) -> MicroStrategyReportNested: + """Convert flat MicroStrategyReport to nested format.""" + attrs = MicroStrategyReportAttributes() + _populate_micro_strategy_report_attrs(attrs, micro_strategy_report) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + micro_strategy_report, + _MICRO_STRATEGY_REPORT_REL_FIELDS, + MicroStrategyReportRelationshipAttributes, + ) + return MicroStrategyReportNested( + guid=micro_strategy_report.guid, + type_name=micro_strategy_report.type_name, + status=micro_strategy_report.status, + version=micro_strategy_report.version, + create_time=micro_strategy_report.create_time, + update_time=micro_strategy_report.update_time, + created_by=micro_strategy_report.created_by, + updated_by=micro_strategy_report.updated_by, + classifications=micro_strategy_report.classifications, + classification_names=micro_strategy_report.classification_names, + meanings=micro_strategy_report.meanings, + labels=micro_strategy_report.labels, + business_attributes=micro_strategy_report.business_attributes, + custom_attributes=micro_strategy_report.custom_attributes, + pending_tasks=micro_strategy_report.pending_tasks, + proxy=micro_strategy_report.proxy, + is_incomplete=micro_strategy_report.is_incomplete, + provenance_type=micro_strategy_report.provenance_type, + home_id=micro_strategy_report.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _micro_strategy_report_from_nested( + nested: MicroStrategyReportNested, +) -> MicroStrategyReport: + """Convert nested format to flat MicroStrategyReport.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else MicroStrategyReportAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _MICRO_STRATEGY_REPORT_REL_FIELDS, + MicroStrategyReportRelationshipAttributes, + ) + return MicroStrategyReport( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_micro_strategy_report_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _micro_strategy_report_to_nested_bytes( + micro_strategy_report: MicroStrategyReport, serde: Serde +) -> bytes: + """Convert flat MicroStrategyReport to nested JSON bytes.""" + return serde.encode(_micro_strategy_report_to_nested(micro_strategy_report)) + + +def _micro_strategy_report_from_nested_bytes( + data: bytes, serde: Serde +) -> MicroStrategyReport: + """Convert nested JSON bytes to flat MicroStrategyReport.""" + nested = serde.decode(data, MicroStrategyReportNested) + return _micro_strategy_report_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +MicroStrategyReport.MICRO_STRATEGY_REPORT_TYPE = KeywordField( + "microStrategyReportType", "microStrategyReportType" +) +MicroStrategyReport.MICRO_STRATEGY_PROJECT_QUALIFIED_NAME = KeywordTextField( + "microStrategyProjectQualifiedName", + "microStrategyProjectQualifiedName", + "microStrategyProjectQualifiedName.text", +) +MicroStrategyReport.MICRO_STRATEGY_PROJECT_NAME = KeywordTextField( + "microStrategyProjectName", + "microStrategyProjectName", + "microStrategyProjectName.text", +) +MicroStrategyReport.MICRO_STRATEGY_CUBE_QUALIFIED_NAMES = KeywordTextField( + "microStrategyCubeQualifiedNames", + "microStrategyCubeQualifiedNames", + "microStrategyCubeQualifiedNames.text", +) +MicroStrategyReport.MICRO_STRATEGY_CUBE_NAMES = KeywordField( + "microStrategyCubeNames", "microStrategyCubeNames" +) +MicroStrategyReport.MICRO_STRATEGY_REPORT_QUALIFIED_NAMES = KeywordTextField( + "microStrategyReportQualifiedNames", + "microStrategyReportQualifiedNames", + "microStrategyReportQualifiedNames.text", +) +MicroStrategyReport.MICRO_STRATEGY_REPORT_NAMES = KeywordField( + "microStrategyReportNames", "microStrategyReportNames" +) +MicroStrategyReport.MICRO_STRATEGY_IS_CERTIFIED = BooleanField( + "microStrategyIsCertified", "microStrategyIsCertified" +) +MicroStrategyReport.MICRO_STRATEGY_CERTIFIED_BY = KeywordField( + "microStrategyCertifiedBy", "microStrategyCertifiedBy" +) +MicroStrategyReport.MICRO_STRATEGY_CERTIFIED_AT = NumericField( + "microStrategyCertifiedAt", "microStrategyCertifiedAt" +) +MicroStrategyReport.MICRO_STRATEGY_LOCATION = KeywordField( + "microStrategyLocation", "microStrategyLocation" +) +MicroStrategyReport.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +MicroStrategyReport.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +MicroStrategyReport.ANOMALO_CHECKS = RelationField("anomaloChecks") +MicroStrategyReport.APPLICATION = RelationField("application") +MicroStrategyReport.APPLICATION_FIELD = RelationField("applicationField") +MicroStrategyReport.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +MicroStrategyReport.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +MicroStrategyReport.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +MicroStrategyReport.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +MicroStrategyReport.METRICS = RelationField("metrics") +MicroStrategyReport.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +MicroStrategyReport.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +MicroStrategyReport.MEANINGS = RelationField("meanings") +MicroStrategyReport.MICRO_STRATEGY_PROJECT = RelationField("microStrategyProject") +MicroStrategyReport.MICRO_STRATEGY_METRICS = RelationField("microStrategyMetrics") +MicroStrategyReport.MICRO_STRATEGY_ATTRIBUTES = RelationField("microStrategyAttributes") +MicroStrategyReport.MICRO_STRATEGY_COLUMNS = RelationField("microStrategyColumns") +MicroStrategyReport.MC_MONITORS = RelationField("mcMonitors") +MicroStrategyReport.MC_INCIDENTS = RelationField("mcIncidents") +MicroStrategyReport.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +MicroStrategyReport.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +MicroStrategyReport.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +MicroStrategyReport.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +MicroStrategyReport.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +MicroStrategyReport.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +MicroStrategyReport.FILES = RelationField("files") +MicroStrategyReport.LINKS = RelationField("links") +MicroStrategyReport.README = RelationField("readme") +MicroStrategyReport.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +MicroStrategyReport.SODA_CHECKS = RelationField("sodaChecks") +MicroStrategyReport.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +MicroStrategyReport.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/micro_strategy_visualization.py b/pyatlan_v9/model/assets/micro_strategy_visualization.py new file mode 100644 index 000000000..68ea28882 --- /dev/null +++ b/pyatlan_v9/model/assets/micro_strategy_visualization.py @@ -0,0 +1,788 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +MicroStrategyVisualization asset model with flattened inheritance. + +This module provides: +- MicroStrategyVisualization: Flat asset class (easy to use) +- MicroStrategyVisualizationAttributes: Nested attributes struct (extends AssetAttributes) +- MicroStrategyVisualizationNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .micro_strategy_related import ( + RelatedMicroStrategyDossier, + RelatedMicroStrategyProject, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class MicroStrategyVisualization(Asset): + """ + Instance of a MicroStrategy visualization in Atlan. + """ + + MICRO_STRATEGY_VISUALIZATION_TYPE: ClassVar[Any] = None + MICRO_STRATEGY_DOSSIER_QUALIFIED_NAME: ClassVar[Any] = None + MICRO_STRATEGY_DOSSIER_NAME: ClassVar[Any] = None + MICRO_STRATEGY_PROJECT_QUALIFIED_NAME: ClassVar[Any] = None + MICRO_STRATEGY_PROJECT_NAME: ClassVar[Any] = None + MICRO_STRATEGY_CUBE_QUALIFIED_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_CUBE_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_REPORT_QUALIFIED_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_REPORT_NAMES: ClassVar[Any] = None + MICRO_STRATEGY_IS_CERTIFIED: ClassVar[Any] = None + MICRO_STRATEGY_CERTIFIED_BY: ClassVar[Any] = None + MICRO_STRATEGY_CERTIFIED_AT: ClassVar[Any] = None + MICRO_STRATEGY_LOCATION: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MICRO_STRATEGY_PROJECT: ClassVar[Any] = None + MICRO_STRATEGY_DOSSIER: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "MicroStrategyVisualization" + + micro_strategy_visualization_type: Union[str, None, UnsetType] = UNSET + """Type of visualization.""" + + micro_strategy_dossier_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dossier in which this visualization exists.""" + + micro_strategy_dossier_name: Union[str, None, UnsetType] = UNSET + """Simple name of the dossier in which this visualization exists.""" + + micro_strategy_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this asset exists.""" + + micro_strategy_project_name: Union[str, None, UnsetType] = UNSET + """Simple name of the project in which this asset exists.""" + + micro_strategy_cube_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Unique names of the cubes related to this asset.""" + + micro_strategy_cube_names: Union[List[str], None, UnsetType] = UNSET + """Simple names of the cubes related to this asset.""" + + micro_strategy_report_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Unique names of the reports related to this asset.""" + + micro_strategy_report_names: Union[List[str], None, UnsetType] = UNSET + """Simple names of the reports related to this asset.""" + + micro_strategy_is_certified: Union[bool, None, UnsetType] = UNSET + """Whether the asset is certified in MicroStrategy (true) or not (false).""" + + micro_strategy_certified_by: Union[str, None, UnsetType] = UNSET + """User who certified this asset, in MicroStrategy.""" + + micro_strategy_certified_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) this asset was certified in MicroStrategy, in milliseconds.""" + + micro_strategy_location: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Location of this asset in MicroStrategy.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + micro_strategy_project: Union[RelatedMicroStrategyProject, None, UnsetType] = UNSET + """Project in which this visualization exists.""" + + micro_strategy_dossier: Union[RelatedMicroStrategyDossier, None, UnsetType] = UNSET + """Dossier in which this visualization exists.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "MicroStrategyVisualization" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _micro_strategy_visualization_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> MicroStrategyVisualization: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + MicroStrategyVisualization instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _micro_strategy_visualization_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class MicroStrategyVisualizationAttributes(AssetAttributes): + """MicroStrategyVisualization-specific attributes for nested API format.""" + + micro_strategy_visualization_type: Union[str, None, UnsetType] = UNSET + """Type of visualization.""" + + micro_strategy_dossier_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dossier in which this visualization exists.""" + + micro_strategy_dossier_name: Union[str, None, UnsetType] = UNSET + """Simple name of the dossier in which this visualization exists.""" + + micro_strategy_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this asset exists.""" + + micro_strategy_project_name: Union[str, None, UnsetType] = UNSET + """Simple name of the project in which this asset exists.""" + + micro_strategy_cube_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Unique names of the cubes related to this asset.""" + + micro_strategy_cube_names: Union[List[str], None, UnsetType] = UNSET + """Simple names of the cubes related to this asset.""" + + micro_strategy_report_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Unique names of the reports related to this asset.""" + + micro_strategy_report_names: Union[List[str], None, UnsetType] = UNSET + """Simple names of the reports related to this asset.""" + + micro_strategy_is_certified: Union[bool, None, UnsetType] = UNSET + """Whether the asset is certified in MicroStrategy (true) or not (false).""" + + micro_strategy_certified_by: Union[str, None, UnsetType] = UNSET + """User who certified this asset, in MicroStrategy.""" + + micro_strategy_certified_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) this asset was certified in MicroStrategy, in milliseconds.""" + + micro_strategy_location: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Location of this asset in MicroStrategy.""" + + +class MicroStrategyVisualizationRelationshipAttributes(AssetRelationshipAttributes): + """MicroStrategyVisualization-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + micro_strategy_project: Union[RelatedMicroStrategyProject, None, UnsetType] = UNSET + """Project in which this visualization exists.""" + + micro_strategy_dossier: Union[RelatedMicroStrategyDossier, None, UnsetType] = UNSET + """Dossier in which this visualization exists.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class MicroStrategyVisualizationNested(AssetNested): + """MicroStrategyVisualization in nested API format for high-performance serialization.""" + + attributes: Union[MicroStrategyVisualizationAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + MicroStrategyVisualizationRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + MicroStrategyVisualizationRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + MicroStrategyVisualizationRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_MICRO_STRATEGY_VISUALIZATION_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "micro_strategy_project", + "micro_strategy_dossier", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_micro_strategy_visualization_attrs( + attrs: MicroStrategyVisualizationAttributes, obj: MicroStrategyVisualization +) -> None: + """Populate MicroStrategyVisualization-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.micro_strategy_visualization_type = obj.micro_strategy_visualization_type + attrs.micro_strategy_dossier_qualified_name = ( + obj.micro_strategy_dossier_qualified_name + ) + attrs.micro_strategy_dossier_name = obj.micro_strategy_dossier_name + attrs.micro_strategy_project_qualified_name = ( + obj.micro_strategy_project_qualified_name + ) + attrs.micro_strategy_project_name = obj.micro_strategy_project_name + attrs.micro_strategy_cube_qualified_names = obj.micro_strategy_cube_qualified_names + attrs.micro_strategy_cube_names = obj.micro_strategy_cube_names + attrs.micro_strategy_report_qualified_names = ( + obj.micro_strategy_report_qualified_names + ) + attrs.micro_strategy_report_names = obj.micro_strategy_report_names + attrs.micro_strategy_is_certified = obj.micro_strategy_is_certified + attrs.micro_strategy_certified_by = obj.micro_strategy_certified_by + attrs.micro_strategy_certified_at = obj.micro_strategy_certified_at + attrs.micro_strategy_location = obj.micro_strategy_location + + +def _extract_micro_strategy_visualization_attrs( + attrs: MicroStrategyVisualizationAttributes, +) -> dict: + """Extract all MicroStrategyVisualization attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["micro_strategy_visualization_type"] = ( + attrs.micro_strategy_visualization_type + ) + result["micro_strategy_dossier_qualified_name"] = ( + attrs.micro_strategy_dossier_qualified_name + ) + result["micro_strategy_dossier_name"] = attrs.micro_strategy_dossier_name + result["micro_strategy_project_qualified_name"] = ( + attrs.micro_strategy_project_qualified_name + ) + result["micro_strategy_project_name"] = attrs.micro_strategy_project_name + result["micro_strategy_cube_qualified_names"] = ( + attrs.micro_strategy_cube_qualified_names + ) + result["micro_strategy_cube_names"] = attrs.micro_strategy_cube_names + result["micro_strategy_report_qualified_names"] = ( + attrs.micro_strategy_report_qualified_names + ) + result["micro_strategy_report_names"] = attrs.micro_strategy_report_names + result["micro_strategy_is_certified"] = attrs.micro_strategy_is_certified + result["micro_strategy_certified_by"] = attrs.micro_strategy_certified_by + result["micro_strategy_certified_at"] = attrs.micro_strategy_certified_at + result["micro_strategy_location"] = attrs.micro_strategy_location + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _micro_strategy_visualization_to_nested( + micro_strategy_visualization: MicroStrategyVisualization, +) -> MicroStrategyVisualizationNested: + """Convert flat MicroStrategyVisualization to nested format.""" + attrs = MicroStrategyVisualizationAttributes() + _populate_micro_strategy_visualization_attrs(attrs, micro_strategy_visualization) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + micro_strategy_visualization, + _MICRO_STRATEGY_VISUALIZATION_REL_FIELDS, + MicroStrategyVisualizationRelationshipAttributes, + ) + return MicroStrategyVisualizationNested( + guid=micro_strategy_visualization.guid, + type_name=micro_strategy_visualization.type_name, + status=micro_strategy_visualization.status, + version=micro_strategy_visualization.version, + create_time=micro_strategy_visualization.create_time, + update_time=micro_strategy_visualization.update_time, + created_by=micro_strategy_visualization.created_by, + updated_by=micro_strategy_visualization.updated_by, + classifications=micro_strategy_visualization.classifications, + classification_names=micro_strategy_visualization.classification_names, + meanings=micro_strategy_visualization.meanings, + labels=micro_strategy_visualization.labels, + business_attributes=micro_strategy_visualization.business_attributes, + custom_attributes=micro_strategy_visualization.custom_attributes, + pending_tasks=micro_strategy_visualization.pending_tasks, + proxy=micro_strategy_visualization.proxy, + is_incomplete=micro_strategy_visualization.is_incomplete, + provenance_type=micro_strategy_visualization.provenance_type, + home_id=micro_strategy_visualization.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _micro_strategy_visualization_from_nested( + nested: MicroStrategyVisualizationNested, +) -> MicroStrategyVisualization: + """Convert nested format to flat MicroStrategyVisualization.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else MicroStrategyVisualizationAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _MICRO_STRATEGY_VISUALIZATION_REL_FIELDS, + MicroStrategyVisualizationRelationshipAttributes, + ) + return MicroStrategyVisualization( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_micro_strategy_visualization_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _micro_strategy_visualization_to_nested_bytes( + micro_strategy_visualization: MicroStrategyVisualization, serde: Serde +) -> bytes: + """Convert flat MicroStrategyVisualization to nested JSON bytes.""" + return serde.encode( + _micro_strategy_visualization_to_nested(micro_strategy_visualization) + ) + + +def _micro_strategy_visualization_from_nested_bytes( + data: bytes, serde: Serde +) -> MicroStrategyVisualization: + """Convert nested JSON bytes to flat MicroStrategyVisualization.""" + nested = serde.decode(data, MicroStrategyVisualizationNested) + return _micro_strategy_visualization_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +MicroStrategyVisualization.MICRO_STRATEGY_VISUALIZATION_TYPE = KeywordField( + "microStrategyVisualizationType", "microStrategyVisualizationType" +) +MicroStrategyVisualization.MICRO_STRATEGY_DOSSIER_QUALIFIED_NAME = KeywordTextField( + "microStrategyDossierQualifiedName", + "microStrategyDossierQualifiedName", + "microStrategyDossierQualifiedName.text", +) +MicroStrategyVisualization.MICRO_STRATEGY_DOSSIER_NAME = KeywordTextField( + "microStrategyDossierName", + "microStrategyDossierName", + "microStrategyDossierName.text", +) +MicroStrategyVisualization.MICRO_STRATEGY_PROJECT_QUALIFIED_NAME = KeywordTextField( + "microStrategyProjectQualifiedName", + "microStrategyProjectQualifiedName", + "microStrategyProjectQualifiedName.text", +) +MicroStrategyVisualization.MICRO_STRATEGY_PROJECT_NAME = KeywordTextField( + "microStrategyProjectName", + "microStrategyProjectName", + "microStrategyProjectName.text", +) +MicroStrategyVisualization.MICRO_STRATEGY_CUBE_QUALIFIED_NAMES = KeywordTextField( + "microStrategyCubeQualifiedNames", + "microStrategyCubeQualifiedNames", + "microStrategyCubeQualifiedNames.text", +) +MicroStrategyVisualization.MICRO_STRATEGY_CUBE_NAMES = KeywordField( + "microStrategyCubeNames", "microStrategyCubeNames" +) +MicroStrategyVisualization.MICRO_STRATEGY_REPORT_QUALIFIED_NAMES = KeywordTextField( + "microStrategyReportQualifiedNames", + "microStrategyReportQualifiedNames", + "microStrategyReportQualifiedNames.text", +) +MicroStrategyVisualization.MICRO_STRATEGY_REPORT_NAMES = KeywordField( + "microStrategyReportNames", "microStrategyReportNames" +) +MicroStrategyVisualization.MICRO_STRATEGY_IS_CERTIFIED = BooleanField( + "microStrategyIsCertified", "microStrategyIsCertified" +) +MicroStrategyVisualization.MICRO_STRATEGY_CERTIFIED_BY = KeywordField( + "microStrategyCertifiedBy", "microStrategyCertifiedBy" +) +MicroStrategyVisualization.MICRO_STRATEGY_CERTIFIED_AT = NumericField( + "microStrategyCertifiedAt", "microStrategyCertifiedAt" +) +MicroStrategyVisualization.MICRO_STRATEGY_LOCATION = KeywordField( + "microStrategyLocation", "microStrategyLocation" +) +MicroStrategyVisualization.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +MicroStrategyVisualization.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +MicroStrategyVisualization.ANOMALO_CHECKS = RelationField("anomaloChecks") +MicroStrategyVisualization.APPLICATION = RelationField("application") +MicroStrategyVisualization.APPLICATION_FIELD = RelationField("applicationField") +MicroStrategyVisualization.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +MicroStrategyVisualization.INPUT_PORT_DATA_PRODUCTS = RelationField( + "inputPortDataProducts" +) +MicroStrategyVisualization.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +MicroStrategyVisualization.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +MicroStrategyVisualization.METRICS = RelationField("metrics") +MicroStrategyVisualization.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +MicroStrategyVisualization.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +MicroStrategyVisualization.MEANINGS = RelationField("meanings") +MicroStrategyVisualization.MICRO_STRATEGY_PROJECT = RelationField( + "microStrategyProject" +) +MicroStrategyVisualization.MICRO_STRATEGY_DOSSIER = RelationField( + "microStrategyDossier" +) +MicroStrategyVisualization.MC_MONITORS = RelationField("mcMonitors") +MicroStrategyVisualization.MC_INCIDENTS = RelationField("mcIncidents") +MicroStrategyVisualization.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +MicroStrategyVisualization.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +MicroStrategyVisualization.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +MicroStrategyVisualization.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +MicroStrategyVisualization.USER_DEF_RELATIONSHIP_TO = RelationField( + "userDefRelationshipTo" +) +MicroStrategyVisualization.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +MicroStrategyVisualization.FILES = RelationField("files") +MicroStrategyVisualization.LINKS = RelationField("links") +MicroStrategyVisualization.README = RelationField("readme") +MicroStrategyVisualization.SCHEMA_REGISTRY_SUBJECTS = RelationField( + "schemaRegistrySubjects" +) +MicroStrategyVisualization.SODA_CHECKS = RelationField("sodaChecks") +MicroStrategyVisualization.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +MicroStrategyVisualization.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/mode.py b/pyatlan_v9/model/assets/mode.py new file mode 100644 index 000000000..443bb5aca --- /dev/null +++ b/pyatlan_v9/model/assets/mode.py @@ -0,0 +1,622 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Mode asset model with flattened inheritance. + +This module provides: +- Mode: Flat asset class (easy to use) +- ModeAttributes: Nested attributes struct (extends AssetAttributes) +- ModeNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Mode(Asset): + """ + Base class for Mode assets. + """ + + MODE_ID: ClassVar[Any] = None + MODE_TOKEN: ClassVar[Any] = None + MODE_WORKSPACE_NAME: ClassVar[Any] = None + MODE_WORKSPACE_USERNAME: ClassVar[Any] = None + MODE_WORKSPACE_QUALIFIED_NAME: ClassVar[Any] = None + MODE_REPORT_NAME: ClassVar[Any] = None + MODE_REPORT_QUALIFIED_NAME: ClassVar[Any] = None + MODE_QUERY_NAME: ClassVar[Any] = None + MODE_QUERY_QUALIFIED_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Mode" + + mode_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the Mode asset.""" + + mode_token: Union[str, None, UnsetType] = UNSET + """Token for the Mode asset.""" + + mode_workspace_name: Union[str, None, UnsetType] = UNSET + """Simple name of the workspace for the Mode asset.""" + + mode_workspace_username: Union[str, None, UnsetType] = UNSET + """Username of the workspace for the Mode asset.""" + + mode_workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace for the Mode asset.""" + + mode_report_name: Union[str, None, UnsetType] = UNSET + """Simple name of the report for the Mode asset.""" + + mode_report_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the report for the Mode asset.""" + + mode_query_name: Union[str, None, UnsetType] = UNSET + """Simple name of the query for the Mode asset.""" + + mode_query_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the query for the Mode asset.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Mode" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _mode_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Mode: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Mode instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _mode_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class ModeAttributes(AssetAttributes): + """Mode-specific attributes for nested API format.""" + + mode_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the Mode asset.""" + + mode_token: Union[str, None, UnsetType] = UNSET + """Token for the Mode asset.""" + + mode_workspace_name: Union[str, None, UnsetType] = UNSET + """Simple name of the workspace for the Mode asset.""" + + mode_workspace_username: Union[str, None, UnsetType] = UNSET + """Username of the workspace for the Mode asset.""" + + mode_workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace for the Mode asset.""" + + mode_report_name: Union[str, None, UnsetType] = UNSET + """Simple name of the report for the Mode asset.""" + + mode_report_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the report for the Mode asset.""" + + mode_query_name: Union[str, None, UnsetType] = UNSET + """Simple name of the query for the Mode asset.""" + + mode_query_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the query for the Mode asset.""" + + +class ModeRelationshipAttributes(AssetRelationshipAttributes): + """Mode-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class ModeNested(AssetNested): + """Mode in nested API format for high-performance serialization.""" + + attributes: Union[ModeAttributes, UnsetType] = UNSET + relationship_attributes: Union[ModeRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ModeRelationshipAttributes, UnsetType] = UNSET + remove_relationship_attributes: Union[ModeRelationshipAttributes, UnsetType] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_MODE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_mode_attrs(attrs: ModeAttributes, obj: Mode) -> None: + """Populate Mode-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.mode_id = obj.mode_id + attrs.mode_token = obj.mode_token + attrs.mode_workspace_name = obj.mode_workspace_name + attrs.mode_workspace_username = obj.mode_workspace_username + attrs.mode_workspace_qualified_name = obj.mode_workspace_qualified_name + attrs.mode_report_name = obj.mode_report_name + attrs.mode_report_qualified_name = obj.mode_report_qualified_name + attrs.mode_query_name = obj.mode_query_name + attrs.mode_query_qualified_name = obj.mode_query_qualified_name + + +def _extract_mode_attrs(attrs: ModeAttributes) -> dict: + """Extract all Mode attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["mode_id"] = attrs.mode_id + result["mode_token"] = attrs.mode_token + result["mode_workspace_name"] = attrs.mode_workspace_name + result["mode_workspace_username"] = attrs.mode_workspace_username + result["mode_workspace_qualified_name"] = attrs.mode_workspace_qualified_name + result["mode_report_name"] = attrs.mode_report_name + result["mode_report_qualified_name"] = attrs.mode_report_qualified_name + result["mode_query_name"] = attrs.mode_query_name + result["mode_query_qualified_name"] = attrs.mode_query_qualified_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _mode_to_nested(mode: Mode) -> ModeNested: + """Convert flat Mode to nested format.""" + attrs = ModeAttributes() + _populate_mode_attrs(attrs, mode) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + mode, _MODE_REL_FIELDS, ModeRelationshipAttributes + ) + return ModeNested( + guid=mode.guid, + type_name=mode.type_name, + status=mode.status, + version=mode.version, + create_time=mode.create_time, + update_time=mode.update_time, + created_by=mode.created_by, + updated_by=mode.updated_by, + classifications=mode.classifications, + classification_names=mode.classification_names, + meanings=mode.meanings, + labels=mode.labels, + business_attributes=mode.business_attributes, + custom_attributes=mode.custom_attributes, + pending_tasks=mode.pending_tasks, + proxy=mode.proxy, + is_incomplete=mode.is_incomplete, + provenance_type=mode.provenance_type, + home_id=mode.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _mode_from_nested(nested: ModeNested) -> Mode: + """Convert nested format to flat Mode.""" + attrs = nested.attributes if nested.attributes is not UNSET else ModeAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _MODE_REL_FIELDS, + ModeRelationshipAttributes, + ) + return Mode( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_mode_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _mode_to_nested_bytes(mode: Mode, serde: Serde) -> bytes: + """Convert flat Mode to nested JSON bytes.""" + return serde.encode(_mode_to_nested(mode)) + + +def _mode_from_nested_bytes(data: bytes, serde: Serde) -> Mode: + """Convert nested JSON bytes to flat Mode.""" + nested = serde.decode(data, ModeNested) + return _mode_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + RelationField, +) + +Mode.MODE_ID = KeywordField("modeId", "modeId") +Mode.MODE_TOKEN = KeywordTextField("modeToken", "modeToken", "modeToken.text") +Mode.MODE_WORKSPACE_NAME = KeywordField("modeWorkspaceName", "modeWorkspaceName") +Mode.MODE_WORKSPACE_USERNAME = KeywordTextField( + "modeWorkspaceUsername", "modeWorkspaceUsername", "modeWorkspaceUsername.text" +) +Mode.MODE_WORKSPACE_QUALIFIED_NAME = KeywordTextField( + "modeWorkspaceQualifiedName", + "modeWorkspaceQualifiedName", + "modeWorkspaceQualifiedName.text", +) +Mode.MODE_REPORT_NAME = KeywordField("modeReportName", "modeReportName") +Mode.MODE_REPORT_QUALIFIED_NAME = KeywordTextField( + "modeReportQualifiedName", "modeReportQualifiedName", "modeReportQualifiedName.text" +) +Mode.MODE_QUERY_NAME = KeywordField("modeQueryName", "modeQueryName") +Mode.MODE_QUERY_QUALIFIED_NAME = KeywordTextField( + "modeQueryQualifiedName", "modeQueryQualifiedName", "modeQueryQualifiedName.text" +) +Mode.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Mode.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Mode.ANOMALO_CHECKS = RelationField("anomaloChecks") +Mode.APPLICATION = RelationField("application") +Mode.APPLICATION_FIELD = RelationField("applicationField") +Mode.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Mode.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Mode.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Mode.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Mode.METRICS = RelationField("metrics") +Mode.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Mode.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Mode.MEANINGS = RelationField("meanings") +Mode.MC_MONITORS = RelationField("mcMonitors") +Mode.MC_INCIDENTS = RelationField("mcIncidents") +Mode.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Mode.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Mode.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Mode.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Mode.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Mode.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Mode.FILES = RelationField("files") +Mode.LINKS = RelationField("links") +Mode.README = RelationField("readme") +Mode.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Mode.SODA_CHECKS = RelationField("sodaChecks") +Mode.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Mode.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/mode_chart.py b/pyatlan_v9/model/assets/mode_chart.py new file mode 100644 index 000000000..f421e7d56 --- /dev/null +++ b/pyatlan_v9/model/assets/mode_chart.py @@ -0,0 +1,658 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +ModeChart asset model with flattened inheritance. + +This module provides: +- ModeChart: Flat asset class (easy to use) +- ModeChartAttributes: Nested attributes struct (extends AssetAttributes) +- ModeChartNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .mode_related import RelatedModeQuery + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class ModeChart(Asset): + """ + Instance of a Mode chart in Atlan. + """ + + MODE_CHART_TYPE: ClassVar[Any] = None + MODE_ID: ClassVar[Any] = None + MODE_TOKEN: ClassVar[Any] = None + MODE_WORKSPACE_NAME: ClassVar[Any] = None + MODE_WORKSPACE_USERNAME: ClassVar[Any] = None + MODE_WORKSPACE_QUALIFIED_NAME: ClassVar[Any] = None + MODE_REPORT_NAME: ClassVar[Any] = None + MODE_REPORT_QUALIFIED_NAME: ClassVar[Any] = None + MODE_QUERY_NAME: ClassVar[Any] = None + MODE_QUERY_QUALIFIED_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MODE_QUERY: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "ModeChart" + + mode_chart_type: Union[str, None, UnsetType] = UNSET + """Type of chart.""" + + mode_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the Mode asset.""" + + mode_token: Union[str, None, UnsetType] = UNSET + """Token for the Mode asset.""" + + mode_workspace_name: Union[str, None, UnsetType] = UNSET + """Simple name of the workspace for the Mode asset.""" + + mode_workspace_username: Union[str, None, UnsetType] = UNSET + """Username of the workspace for the Mode asset.""" + + mode_workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace for the Mode asset.""" + + mode_report_name: Union[str, None, UnsetType] = UNSET + """Simple name of the report for the Mode asset.""" + + mode_report_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the report for the Mode asset.""" + + mode_query_name: Union[str, None, UnsetType] = UNSET + """Simple name of the query for the Mode asset.""" + + mode_query_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the query for the Mode asset.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mode_query: Union[RelatedModeQuery, None, UnsetType] = UNSET + """Query in which this chart exists.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "ModeChart" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _mode_chart_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> ModeChart: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + ModeChart instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _mode_chart_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class ModeChartAttributes(AssetAttributes): + """ModeChart-specific attributes for nested API format.""" + + mode_chart_type: Union[str, None, UnsetType] = UNSET + """Type of chart.""" + + mode_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the Mode asset.""" + + mode_token: Union[str, None, UnsetType] = UNSET + """Token for the Mode asset.""" + + mode_workspace_name: Union[str, None, UnsetType] = UNSET + """Simple name of the workspace for the Mode asset.""" + + mode_workspace_username: Union[str, None, UnsetType] = UNSET + """Username of the workspace for the Mode asset.""" + + mode_workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace for the Mode asset.""" + + mode_report_name: Union[str, None, UnsetType] = UNSET + """Simple name of the report for the Mode asset.""" + + mode_report_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the report for the Mode asset.""" + + mode_query_name: Union[str, None, UnsetType] = UNSET + """Simple name of the query for the Mode asset.""" + + mode_query_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the query for the Mode asset.""" + + +class ModeChartRelationshipAttributes(AssetRelationshipAttributes): + """ModeChart-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mode_query: Union[RelatedModeQuery, None, UnsetType] = UNSET + """Query in which this chart exists.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class ModeChartNested(AssetNested): + """ModeChart in nested API format for high-performance serialization.""" + + attributes: Union[ModeChartAttributes, UnsetType] = UNSET + relationship_attributes: Union[ModeChartRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + ModeChartRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + ModeChartRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_MODE_CHART_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mode_query", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_mode_chart_attrs(attrs: ModeChartAttributes, obj: ModeChart) -> None: + """Populate ModeChart-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.mode_chart_type = obj.mode_chart_type + attrs.mode_id = obj.mode_id + attrs.mode_token = obj.mode_token + attrs.mode_workspace_name = obj.mode_workspace_name + attrs.mode_workspace_username = obj.mode_workspace_username + attrs.mode_workspace_qualified_name = obj.mode_workspace_qualified_name + attrs.mode_report_name = obj.mode_report_name + attrs.mode_report_qualified_name = obj.mode_report_qualified_name + attrs.mode_query_name = obj.mode_query_name + attrs.mode_query_qualified_name = obj.mode_query_qualified_name + + +def _extract_mode_chart_attrs(attrs: ModeChartAttributes) -> dict: + """Extract all ModeChart attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["mode_chart_type"] = attrs.mode_chart_type + result["mode_id"] = attrs.mode_id + result["mode_token"] = attrs.mode_token + result["mode_workspace_name"] = attrs.mode_workspace_name + result["mode_workspace_username"] = attrs.mode_workspace_username + result["mode_workspace_qualified_name"] = attrs.mode_workspace_qualified_name + result["mode_report_name"] = attrs.mode_report_name + result["mode_report_qualified_name"] = attrs.mode_report_qualified_name + result["mode_query_name"] = attrs.mode_query_name + result["mode_query_qualified_name"] = attrs.mode_query_qualified_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _mode_chart_to_nested(mode_chart: ModeChart) -> ModeChartNested: + """Convert flat ModeChart to nested format.""" + attrs = ModeChartAttributes() + _populate_mode_chart_attrs(attrs, mode_chart) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + mode_chart, _MODE_CHART_REL_FIELDS, ModeChartRelationshipAttributes + ) + return ModeChartNested( + guid=mode_chart.guid, + type_name=mode_chart.type_name, + status=mode_chart.status, + version=mode_chart.version, + create_time=mode_chart.create_time, + update_time=mode_chart.update_time, + created_by=mode_chart.created_by, + updated_by=mode_chart.updated_by, + classifications=mode_chart.classifications, + classification_names=mode_chart.classification_names, + meanings=mode_chart.meanings, + labels=mode_chart.labels, + business_attributes=mode_chart.business_attributes, + custom_attributes=mode_chart.custom_attributes, + pending_tasks=mode_chart.pending_tasks, + proxy=mode_chart.proxy, + is_incomplete=mode_chart.is_incomplete, + provenance_type=mode_chart.provenance_type, + home_id=mode_chart.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _mode_chart_from_nested(nested: ModeChartNested) -> ModeChart: + """Convert nested format to flat ModeChart.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else ModeChartAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _MODE_CHART_REL_FIELDS, + ModeChartRelationshipAttributes, + ) + return ModeChart( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_mode_chart_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _mode_chart_to_nested_bytes(mode_chart: ModeChart, serde: Serde) -> bytes: + """Convert flat ModeChart to nested JSON bytes.""" + return serde.encode(_mode_chart_to_nested(mode_chart)) + + +def _mode_chart_from_nested_bytes(data: bytes, serde: Serde) -> ModeChart: + """Convert nested JSON bytes to flat ModeChart.""" + nested = serde.decode(data, ModeChartNested) + return _mode_chart_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + RelationField, +) + +ModeChart.MODE_CHART_TYPE = KeywordField("modeChartType", "modeChartType") +ModeChart.MODE_ID = KeywordField("modeId", "modeId") +ModeChart.MODE_TOKEN = KeywordTextField("modeToken", "modeToken", "modeToken.text") +ModeChart.MODE_WORKSPACE_NAME = KeywordField("modeWorkspaceName", "modeWorkspaceName") +ModeChart.MODE_WORKSPACE_USERNAME = KeywordTextField( + "modeWorkspaceUsername", "modeWorkspaceUsername", "modeWorkspaceUsername.text" +) +ModeChart.MODE_WORKSPACE_QUALIFIED_NAME = KeywordTextField( + "modeWorkspaceQualifiedName", + "modeWorkspaceQualifiedName", + "modeWorkspaceQualifiedName.text", +) +ModeChart.MODE_REPORT_NAME = KeywordField("modeReportName", "modeReportName") +ModeChart.MODE_REPORT_QUALIFIED_NAME = KeywordTextField( + "modeReportQualifiedName", "modeReportQualifiedName", "modeReportQualifiedName.text" +) +ModeChart.MODE_QUERY_NAME = KeywordField("modeQueryName", "modeQueryName") +ModeChart.MODE_QUERY_QUALIFIED_NAME = KeywordTextField( + "modeQueryQualifiedName", "modeQueryQualifiedName", "modeQueryQualifiedName.text" +) +ModeChart.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +ModeChart.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +ModeChart.ANOMALO_CHECKS = RelationField("anomaloChecks") +ModeChart.APPLICATION = RelationField("application") +ModeChart.APPLICATION_FIELD = RelationField("applicationField") +ModeChart.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +ModeChart.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +ModeChart.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +ModeChart.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +ModeChart.METRICS = RelationField("metrics") +ModeChart.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +ModeChart.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +ModeChart.MEANINGS = RelationField("meanings") +ModeChart.MODE_QUERY = RelationField("modeQuery") +ModeChart.MC_MONITORS = RelationField("mcMonitors") +ModeChart.MC_INCIDENTS = RelationField("mcIncidents") +ModeChart.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +ModeChart.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +ModeChart.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +ModeChart.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +ModeChart.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +ModeChart.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +ModeChart.FILES = RelationField("files") +ModeChart.LINKS = RelationField("links") +ModeChart.README = RelationField("readme") +ModeChart.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +ModeChart.SODA_CHECKS = RelationField("sodaChecks") +ModeChart.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +ModeChart.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/mode_collection.py b/pyatlan_v9/model/assets/mode_collection.py new file mode 100644 index 000000000..520e6272b --- /dev/null +++ b/pyatlan_v9/model/assets/mode_collection.py @@ -0,0 +1,693 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +ModeCollection asset model with flattened inheritance. + +This module provides: +- ModeCollection: Flat asset class (easy to use) +- ModeCollectionAttributes: Nested attributes struct (extends AssetAttributes) +- ModeCollectionNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .mode_related import RelatedModeReport, RelatedModeWorkspace + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class ModeCollection(Asset): + """ + Instance of a Mode collection in Atlan. + """ + + MODE_COLLECTION_TYPE: ClassVar[Any] = None + MODE_COLLECTION_STATE: ClassVar[Any] = None + MODE_ID: ClassVar[Any] = None + MODE_TOKEN: ClassVar[Any] = None + MODE_WORKSPACE_NAME: ClassVar[Any] = None + MODE_WORKSPACE_USERNAME: ClassVar[Any] = None + MODE_WORKSPACE_QUALIFIED_NAME: ClassVar[Any] = None + MODE_REPORT_NAME: ClassVar[Any] = None + MODE_REPORT_QUALIFIED_NAME: ClassVar[Any] = None + MODE_QUERY_NAME: ClassVar[Any] = None + MODE_QUERY_QUALIFIED_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MODE_WORKSPACE: ClassVar[Any] = None + MODE_REPORTS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "ModeCollection" + + mode_collection_type: Union[str, None, UnsetType] = UNSET + """Type of this collection.""" + + mode_collection_state: Union[str, None, UnsetType] = UNSET + """State of this collection.""" + + mode_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the Mode asset.""" + + mode_token: Union[str, None, UnsetType] = UNSET + """Token for the Mode asset.""" + + mode_workspace_name: Union[str, None, UnsetType] = UNSET + """Simple name of the workspace for the Mode asset.""" + + mode_workspace_username: Union[str, None, UnsetType] = UNSET + """Username of the workspace for the Mode asset.""" + + mode_workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace for the Mode asset.""" + + mode_report_name: Union[str, None, UnsetType] = UNSET + """Simple name of the report for the Mode asset.""" + + mode_report_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the report for the Mode asset.""" + + mode_query_name: Union[str, None, UnsetType] = UNSET + """Simple name of the query for the Mode asset.""" + + mode_query_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the query for the Mode asset.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mode_workspace: Union[RelatedModeWorkspace, None, UnsetType] = UNSET + """Workspace in which this collection exists.""" + + mode_reports: Union[List[RelatedModeReport], None, UnsetType] = UNSET + """Reports related to this collection.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "ModeCollection" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _mode_collection_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> ModeCollection: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + ModeCollection instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _mode_collection_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class ModeCollectionAttributes(AssetAttributes): + """ModeCollection-specific attributes for nested API format.""" + + mode_collection_type: Union[str, None, UnsetType] = UNSET + """Type of this collection.""" + + mode_collection_state: Union[str, None, UnsetType] = UNSET + """State of this collection.""" + + mode_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the Mode asset.""" + + mode_token: Union[str, None, UnsetType] = UNSET + """Token for the Mode asset.""" + + mode_workspace_name: Union[str, None, UnsetType] = UNSET + """Simple name of the workspace for the Mode asset.""" + + mode_workspace_username: Union[str, None, UnsetType] = UNSET + """Username of the workspace for the Mode asset.""" + + mode_workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace for the Mode asset.""" + + mode_report_name: Union[str, None, UnsetType] = UNSET + """Simple name of the report for the Mode asset.""" + + mode_report_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the report for the Mode asset.""" + + mode_query_name: Union[str, None, UnsetType] = UNSET + """Simple name of the query for the Mode asset.""" + + mode_query_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the query for the Mode asset.""" + + +class ModeCollectionRelationshipAttributes(AssetRelationshipAttributes): + """ModeCollection-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mode_workspace: Union[RelatedModeWorkspace, None, UnsetType] = UNSET + """Workspace in which this collection exists.""" + + mode_reports: Union[List[RelatedModeReport], None, UnsetType] = UNSET + """Reports related to this collection.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class ModeCollectionNested(AssetNested): + """ModeCollection in nested API format for high-performance serialization.""" + + attributes: Union[ModeCollectionAttributes, UnsetType] = UNSET + relationship_attributes: Union[ModeCollectionRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + ModeCollectionRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + ModeCollectionRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_MODE_COLLECTION_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mode_workspace", + "mode_reports", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_mode_collection_attrs( + attrs: ModeCollectionAttributes, obj: ModeCollection +) -> None: + """Populate ModeCollection-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.mode_collection_type = obj.mode_collection_type + attrs.mode_collection_state = obj.mode_collection_state + attrs.mode_id = obj.mode_id + attrs.mode_token = obj.mode_token + attrs.mode_workspace_name = obj.mode_workspace_name + attrs.mode_workspace_username = obj.mode_workspace_username + attrs.mode_workspace_qualified_name = obj.mode_workspace_qualified_name + attrs.mode_report_name = obj.mode_report_name + attrs.mode_report_qualified_name = obj.mode_report_qualified_name + attrs.mode_query_name = obj.mode_query_name + attrs.mode_query_qualified_name = obj.mode_query_qualified_name + + +def _extract_mode_collection_attrs(attrs: ModeCollectionAttributes) -> dict: + """Extract all ModeCollection attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["mode_collection_type"] = attrs.mode_collection_type + result["mode_collection_state"] = attrs.mode_collection_state + result["mode_id"] = attrs.mode_id + result["mode_token"] = attrs.mode_token + result["mode_workspace_name"] = attrs.mode_workspace_name + result["mode_workspace_username"] = attrs.mode_workspace_username + result["mode_workspace_qualified_name"] = attrs.mode_workspace_qualified_name + result["mode_report_name"] = attrs.mode_report_name + result["mode_report_qualified_name"] = attrs.mode_report_qualified_name + result["mode_query_name"] = attrs.mode_query_name + result["mode_query_qualified_name"] = attrs.mode_query_qualified_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _mode_collection_to_nested(mode_collection: ModeCollection) -> ModeCollectionNested: + """Convert flat ModeCollection to nested format.""" + attrs = ModeCollectionAttributes() + _populate_mode_collection_attrs(attrs, mode_collection) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + mode_collection, + _MODE_COLLECTION_REL_FIELDS, + ModeCollectionRelationshipAttributes, + ) + return ModeCollectionNested( + guid=mode_collection.guid, + type_name=mode_collection.type_name, + status=mode_collection.status, + version=mode_collection.version, + create_time=mode_collection.create_time, + update_time=mode_collection.update_time, + created_by=mode_collection.created_by, + updated_by=mode_collection.updated_by, + classifications=mode_collection.classifications, + classification_names=mode_collection.classification_names, + meanings=mode_collection.meanings, + labels=mode_collection.labels, + business_attributes=mode_collection.business_attributes, + custom_attributes=mode_collection.custom_attributes, + pending_tasks=mode_collection.pending_tasks, + proxy=mode_collection.proxy, + is_incomplete=mode_collection.is_incomplete, + provenance_type=mode_collection.provenance_type, + home_id=mode_collection.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _mode_collection_from_nested(nested: ModeCollectionNested) -> ModeCollection: + """Convert nested format to flat ModeCollection.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else ModeCollectionAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _MODE_COLLECTION_REL_FIELDS, + ModeCollectionRelationshipAttributes, + ) + return ModeCollection( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_mode_collection_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _mode_collection_to_nested_bytes( + mode_collection: ModeCollection, serde: Serde +) -> bytes: + """Convert flat ModeCollection to nested JSON bytes.""" + return serde.encode(_mode_collection_to_nested(mode_collection)) + + +def _mode_collection_from_nested_bytes(data: bytes, serde: Serde) -> ModeCollection: + """Convert nested JSON bytes to flat ModeCollection.""" + nested = serde.decode(data, ModeCollectionNested) + return _mode_collection_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + RelationField, +) + +ModeCollection.MODE_COLLECTION_TYPE = KeywordField( + "modeCollectionType", "modeCollectionType" +) +ModeCollection.MODE_COLLECTION_STATE = KeywordField( + "modeCollectionState", "modeCollectionState" +) +ModeCollection.MODE_ID = KeywordField("modeId", "modeId") +ModeCollection.MODE_TOKEN = KeywordTextField("modeToken", "modeToken", "modeToken.text") +ModeCollection.MODE_WORKSPACE_NAME = KeywordField( + "modeWorkspaceName", "modeWorkspaceName" +) +ModeCollection.MODE_WORKSPACE_USERNAME = KeywordTextField( + "modeWorkspaceUsername", "modeWorkspaceUsername", "modeWorkspaceUsername.text" +) +ModeCollection.MODE_WORKSPACE_QUALIFIED_NAME = KeywordTextField( + "modeWorkspaceQualifiedName", + "modeWorkspaceQualifiedName", + "modeWorkspaceQualifiedName.text", +) +ModeCollection.MODE_REPORT_NAME = KeywordField("modeReportName", "modeReportName") +ModeCollection.MODE_REPORT_QUALIFIED_NAME = KeywordTextField( + "modeReportQualifiedName", "modeReportQualifiedName", "modeReportQualifiedName.text" +) +ModeCollection.MODE_QUERY_NAME = KeywordField("modeQueryName", "modeQueryName") +ModeCollection.MODE_QUERY_QUALIFIED_NAME = KeywordTextField( + "modeQueryQualifiedName", "modeQueryQualifiedName", "modeQueryQualifiedName.text" +) +ModeCollection.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +ModeCollection.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +ModeCollection.ANOMALO_CHECKS = RelationField("anomaloChecks") +ModeCollection.APPLICATION = RelationField("application") +ModeCollection.APPLICATION_FIELD = RelationField("applicationField") +ModeCollection.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +ModeCollection.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +ModeCollection.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +ModeCollection.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +ModeCollection.METRICS = RelationField("metrics") +ModeCollection.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +ModeCollection.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +ModeCollection.MEANINGS = RelationField("meanings") +ModeCollection.MODE_WORKSPACE = RelationField("modeWorkspace") +ModeCollection.MODE_REPORTS = RelationField("modeReports") +ModeCollection.MC_MONITORS = RelationField("mcMonitors") +ModeCollection.MC_INCIDENTS = RelationField("mcIncidents") +ModeCollection.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +ModeCollection.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +ModeCollection.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +ModeCollection.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +ModeCollection.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +ModeCollection.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +ModeCollection.FILES = RelationField("files") +ModeCollection.LINKS = RelationField("links") +ModeCollection.README = RelationField("readme") +ModeCollection.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +ModeCollection.SODA_CHECKS = RelationField("sodaChecks") +ModeCollection.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +ModeCollection.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/mode_query.py b/pyatlan_v9/model/assets/mode_query.py new file mode 100644 index 000000000..262d63741 --- /dev/null +++ b/pyatlan_v9/model/assets/mode_query.py @@ -0,0 +1,680 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +ModeQuery asset model with flattened inheritance. + +This module provides: +- ModeQuery: Flat asset class (easy to use) +- ModeQueryAttributes: Nested attributes struct (extends AssetAttributes) +- ModeQueryNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .mode_related import RelatedModeChart, RelatedModeReport + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class ModeQuery(Asset): + """ + Instance of a Mode query in Atlan. + """ + + MODE_RAW_QUERY: ClassVar[Any] = None + MODE_REPORT_IMPORT_COUNT: ClassVar[Any] = None + MODE_ID: ClassVar[Any] = None + MODE_TOKEN: ClassVar[Any] = None + MODE_WORKSPACE_NAME: ClassVar[Any] = None + MODE_WORKSPACE_USERNAME: ClassVar[Any] = None + MODE_WORKSPACE_QUALIFIED_NAME: ClassVar[Any] = None + MODE_REPORT_NAME: ClassVar[Any] = None + MODE_REPORT_QUALIFIED_NAME: ClassVar[Any] = None + MODE_QUERY_NAME: ClassVar[Any] = None + MODE_QUERY_QUALIFIED_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MODE_REPORT: ClassVar[Any] = None + MODE_CHARTS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "ModeQuery" + + mode_raw_query: Union[str, None, UnsetType] = UNSET + """Raw query for the Mode asset.""" + + mode_report_import_count: Union[int, None, UnsetType] = UNSET + """Number of reports imported into this query.""" + + mode_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the Mode asset.""" + + mode_token: Union[str, None, UnsetType] = UNSET + """Token for the Mode asset.""" + + mode_workspace_name: Union[str, None, UnsetType] = UNSET + """Simple name of the workspace for the Mode asset.""" + + mode_workspace_username: Union[str, None, UnsetType] = UNSET + """Username of the workspace for the Mode asset.""" + + mode_workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace for the Mode asset.""" + + mode_report_name: Union[str, None, UnsetType] = UNSET + """Simple name of the report for the Mode asset.""" + + mode_report_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the report for the Mode asset.""" + + mode_query_name: Union[str, None, UnsetType] = UNSET + """Simple name of the query for the Mode asset.""" + + mode_query_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the query for the Mode asset.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mode_report: Union[RelatedModeReport, None, UnsetType] = UNSET + """Report in which this query exists.""" + + mode_charts: Union[List[RelatedModeChart], None, UnsetType] = UNSET + """Charts that exist within this query.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "ModeQuery" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _mode_query_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> ModeQuery: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + ModeQuery instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _mode_query_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class ModeQueryAttributes(AssetAttributes): + """ModeQuery-specific attributes for nested API format.""" + + mode_raw_query: Union[str, None, UnsetType] = UNSET + """Raw query for the Mode asset.""" + + mode_report_import_count: Union[int, None, UnsetType] = UNSET + """Number of reports imported into this query.""" + + mode_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the Mode asset.""" + + mode_token: Union[str, None, UnsetType] = UNSET + """Token for the Mode asset.""" + + mode_workspace_name: Union[str, None, UnsetType] = UNSET + """Simple name of the workspace for the Mode asset.""" + + mode_workspace_username: Union[str, None, UnsetType] = UNSET + """Username of the workspace for the Mode asset.""" + + mode_workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace for the Mode asset.""" + + mode_report_name: Union[str, None, UnsetType] = UNSET + """Simple name of the report for the Mode asset.""" + + mode_report_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the report for the Mode asset.""" + + mode_query_name: Union[str, None, UnsetType] = UNSET + """Simple name of the query for the Mode asset.""" + + mode_query_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the query for the Mode asset.""" + + +class ModeQueryRelationshipAttributes(AssetRelationshipAttributes): + """ModeQuery-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mode_report: Union[RelatedModeReport, None, UnsetType] = UNSET + """Report in which this query exists.""" + + mode_charts: Union[List[RelatedModeChart], None, UnsetType] = UNSET + """Charts that exist within this query.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class ModeQueryNested(AssetNested): + """ModeQuery in nested API format for high-performance serialization.""" + + attributes: Union[ModeQueryAttributes, UnsetType] = UNSET + relationship_attributes: Union[ModeQueryRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + ModeQueryRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + ModeQueryRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_MODE_QUERY_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mode_report", + "mode_charts", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_mode_query_attrs(attrs: ModeQueryAttributes, obj: ModeQuery) -> None: + """Populate ModeQuery-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.mode_raw_query = obj.mode_raw_query + attrs.mode_report_import_count = obj.mode_report_import_count + attrs.mode_id = obj.mode_id + attrs.mode_token = obj.mode_token + attrs.mode_workspace_name = obj.mode_workspace_name + attrs.mode_workspace_username = obj.mode_workspace_username + attrs.mode_workspace_qualified_name = obj.mode_workspace_qualified_name + attrs.mode_report_name = obj.mode_report_name + attrs.mode_report_qualified_name = obj.mode_report_qualified_name + attrs.mode_query_name = obj.mode_query_name + attrs.mode_query_qualified_name = obj.mode_query_qualified_name + + +def _extract_mode_query_attrs(attrs: ModeQueryAttributes) -> dict: + """Extract all ModeQuery attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["mode_raw_query"] = attrs.mode_raw_query + result["mode_report_import_count"] = attrs.mode_report_import_count + result["mode_id"] = attrs.mode_id + result["mode_token"] = attrs.mode_token + result["mode_workspace_name"] = attrs.mode_workspace_name + result["mode_workspace_username"] = attrs.mode_workspace_username + result["mode_workspace_qualified_name"] = attrs.mode_workspace_qualified_name + result["mode_report_name"] = attrs.mode_report_name + result["mode_report_qualified_name"] = attrs.mode_report_qualified_name + result["mode_query_name"] = attrs.mode_query_name + result["mode_query_qualified_name"] = attrs.mode_query_qualified_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _mode_query_to_nested(mode_query: ModeQuery) -> ModeQueryNested: + """Convert flat ModeQuery to nested format.""" + attrs = ModeQueryAttributes() + _populate_mode_query_attrs(attrs, mode_query) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + mode_query, _MODE_QUERY_REL_FIELDS, ModeQueryRelationshipAttributes + ) + return ModeQueryNested( + guid=mode_query.guid, + type_name=mode_query.type_name, + status=mode_query.status, + version=mode_query.version, + create_time=mode_query.create_time, + update_time=mode_query.update_time, + created_by=mode_query.created_by, + updated_by=mode_query.updated_by, + classifications=mode_query.classifications, + classification_names=mode_query.classification_names, + meanings=mode_query.meanings, + labels=mode_query.labels, + business_attributes=mode_query.business_attributes, + custom_attributes=mode_query.custom_attributes, + pending_tasks=mode_query.pending_tasks, + proxy=mode_query.proxy, + is_incomplete=mode_query.is_incomplete, + provenance_type=mode_query.provenance_type, + home_id=mode_query.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _mode_query_from_nested(nested: ModeQueryNested) -> ModeQuery: + """Convert nested format to flat ModeQuery.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else ModeQueryAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _MODE_QUERY_REL_FIELDS, + ModeQueryRelationshipAttributes, + ) + return ModeQuery( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_mode_query_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _mode_query_to_nested_bytes(mode_query: ModeQuery, serde: Serde) -> bytes: + """Convert flat ModeQuery to nested JSON bytes.""" + return serde.encode(_mode_query_to_nested(mode_query)) + + +def _mode_query_from_nested_bytes(data: bytes, serde: Serde) -> ModeQuery: + """Convert nested JSON bytes to flat ModeQuery.""" + nested = serde.decode(data, ModeQueryNested) + return _mode_query_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +ModeQuery.MODE_RAW_QUERY = KeywordField("modeRawQuery", "modeRawQuery") +ModeQuery.MODE_REPORT_IMPORT_COUNT = NumericField( + "modeReportImportCount", "modeReportImportCount" +) +ModeQuery.MODE_ID = KeywordField("modeId", "modeId") +ModeQuery.MODE_TOKEN = KeywordTextField("modeToken", "modeToken", "modeToken.text") +ModeQuery.MODE_WORKSPACE_NAME = KeywordField("modeWorkspaceName", "modeWorkspaceName") +ModeQuery.MODE_WORKSPACE_USERNAME = KeywordTextField( + "modeWorkspaceUsername", "modeWorkspaceUsername", "modeWorkspaceUsername.text" +) +ModeQuery.MODE_WORKSPACE_QUALIFIED_NAME = KeywordTextField( + "modeWorkspaceQualifiedName", + "modeWorkspaceQualifiedName", + "modeWorkspaceQualifiedName.text", +) +ModeQuery.MODE_REPORT_NAME = KeywordField("modeReportName", "modeReportName") +ModeQuery.MODE_REPORT_QUALIFIED_NAME = KeywordTextField( + "modeReportQualifiedName", "modeReportQualifiedName", "modeReportQualifiedName.text" +) +ModeQuery.MODE_QUERY_NAME = KeywordField("modeQueryName", "modeQueryName") +ModeQuery.MODE_QUERY_QUALIFIED_NAME = KeywordTextField( + "modeQueryQualifiedName", "modeQueryQualifiedName", "modeQueryQualifiedName.text" +) +ModeQuery.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +ModeQuery.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +ModeQuery.ANOMALO_CHECKS = RelationField("anomaloChecks") +ModeQuery.APPLICATION = RelationField("application") +ModeQuery.APPLICATION_FIELD = RelationField("applicationField") +ModeQuery.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +ModeQuery.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +ModeQuery.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +ModeQuery.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +ModeQuery.METRICS = RelationField("metrics") +ModeQuery.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +ModeQuery.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +ModeQuery.MEANINGS = RelationField("meanings") +ModeQuery.MODE_REPORT = RelationField("modeReport") +ModeQuery.MODE_CHARTS = RelationField("modeCharts") +ModeQuery.MC_MONITORS = RelationField("mcMonitors") +ModeQuery.MC_INCIDENTS = RelationField("mcIncidents") +ModeQuery.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +ModeQuery.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +ModeQuery.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +ModeQuery.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +ModeQuery.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +ModeQuery.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +ModeQuery.FILES = RelationField("files") +ModeQuery.LINKS = RelationField("links") +ModeQuery.README = RelationField("readme") +ModeQuery.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +ModeQuery.SODA_CHECKS = RelationField("sodaChecks") +ModeQuery.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +ModeQuery.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/mode_related.py b/pyatlan_v9/model/assets/mode_related.py new file mode 100644 index 000000000..b124d8a0a --- /dev/null +++ b/pyatlan_v9/model/assets/mode_related.py @@ -0,0 +1,187 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Mode module. + +This module contains all Related{Type} classes for the Mode type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Union + +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedBI +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedMode", + "RelatedModeCollection", + "RelatedModeQuery", + "RelatedModeReport", + "RelatedModeWorkspace", + "RelatedModeChart", +] + + +class RelatedMode(RelatedBI): + """ + Related entity reference for Mode assets. + + Extends RelatedBI with Mode-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Mode" so it serializes correctly + + mode_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the Mode asset.""" + + mode_token: Union[str, None, UnsetType] = UNSET + """Token for the Mode asset.""" + + mode_workspace_name: Union[str, None, UnsetType] = UNSET + """Simple name of the workspace for the Mode asset.""" + + mode_workspace_username: Union[str, None, UnsetType] = UNSET + """Username of the workspace for the Mode asset.""" + + mode_workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace for the Mode asset.""" + + mode_report_name: Union[str, None, UnsetType] = UNSET + """Simple name of the report for the Mode asset.""" + + mode_report_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the report for the Mode asset.""" + + mode_query_name: Union[str, None, UnsetType] = UNSET + """Simple name of the query for the Mode asset.""" + + mode_query_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the query for the Mode asset.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Mode" + + +class RelatedModeCollection(RelatedMode): + """ + Related entity reference for ModeCollection assets. + + Extends RelatedMode with ModeCollection-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "ModeCollection" so it serializes correctly + + mode_collection_type: Union[str, None, UnsetType] = UNSET + """Type of this collection.""" + + mode_collection_state: Union[str, None, UnsetType] = UNSET + """State of this collection.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "ModeCollection" + + +class RelatedModeQuery(RelatedMode): + """ + Related entity reference for ModeQuery assets. + + Extends RelatedMode with ModeQuery-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "ModeQuery" so it serializes correctly + + mode_raw_query: Union[str, None, UnsetType] = UNSET + """Raw query for the Mode asset.""" + + mode_report_import_count: Union[int, None, UnsetType] = UNSET + """Number of reports imported into this query.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "ModeQuery" + + +class RelatedModeReport(RelatedMode): + """ + Related entity reference for ModeReport assets. + + Extends RelatedMode with ModeReport-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "ModeReport" so it serializes correctly + + mode_collection_token: Union[str, None, UnsetType] = UNSET + """Token for the Mode collection.""" + + mode_report_published_at: Union[int, None, UnsetType] = UNSET + """Date and time when the report was published.""" + + mode_query_count: Union[int, None, UnsetType] = UNSET + """Number of queries in this report.""" + + mode_chart_count: Union[int, None, UnsetType] = UNSET + """Number of charts in this report.""" + + mode_query_preview: Union[str, None, UnsetType] = UNSET + """Preview of the query for the Mode asset.""" + + mode_is_public: Union[bool, None, UnsetType] = UNSET + """Whether the report is public.""" + + mode_is_shared: Union[bool, None, UnsetType] = UNSET + """Whether the report is shared.""" + + mode_is_archived: Union[bool, None, UnsetType] = UNSET + """Whether the report is archived.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "ModeReport" + + +class RelatedModeWorkspace(RelatedMode): + """ + Related entity reference for ModeWorkspace assets. + + Extends RelatedMode with ModeWorkspace-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "ModeWorkspace" so it serializes correctly + + mode_collection_count: Union[int, None, UnsetType] = UNSET + """Number of collections in this workspace.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "ModeWorkspace" + + +class RelatedModeChart(RelatedMode): + """ + Related entity reference for ModeChart assets. + + Extends RelatedMode with ModeChart-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "ModeChart" so it serializes correctly + + mode_chart_type: Union[str, None, UnsetType] = UNSET + """Type of chart.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "ModeChart" diff --git a/pyatlan_v9/model/assets/mode_report.py b/pyatlan_v9/model/assets/mode_report.py new file mode 100644 index 000000000..dabddbed5 --- /dev/null +++ b/pyatlan_v9/model/assets/mode_report.py @@ -0,0 +1,743 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +ModeReport asset model with flattened inheritance. + +This module provides: +- ModeReport: Flat asset class (easy to use) +- ModeReportAttributes: Nested attributes struct (extends AssetAttributes) +- ModeReportNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .mode_related import RelatedModeCollection, RelatedModeQuery + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class ModeReport(Asset): + """ + Instance of a Mode report in Atlan. + """ + + MODE_COLLECTION_TOKEN: ClassVar[Any] = None + MODE_REPORT_PUBLISHED_AT: ClassVar[Any] = None + MODE_QUERY_COUNT: ClassVar[Any] = None + MODE_CHART_COUNT: ClassVar[Any] = None + MODE_QUERY_PREVIEW: ClassVar[Any] = None + MODE_IS_PUBLIC: ClassVar[Any] = None + MODE_IS_SHARED: ClassVar[Any] = None + MODE_IS_ARCHIVED: ClassVar[Any] = None + MODE_ID: ClassVar[Any] = None + MODE_TOKEN: ClassVar[Any] = None + MODE_WORKSPACE_NAME: ClassVar[Any] = None + MODE_WORKSPACE_USERNAME: ClassVar[Any] = None + MODE_WORKSPACE_QUALIFIED_NAME: ClassVar[Any] = None + MODE_REPORT_NAME: ClassVar[Any] = None + MODE_REPORT_QUALIFIED_NAME: ClassVar[Any] = None + MODE_QUERY_NAME: ClassVar[Any] = None + MODE_QUERY_QUALIFIED_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MODE_QUERIES: ClassVar[Any] = None + MODE_COLLECTIONS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "ModeReport" + + mode_collection_token: Union[str, None, UnsetType] = UNSET + """Token for the Mode collection.""" + + mode_report_published_at: Union[int, None, UnsetType] = UNSET + """Date and time when the report was published.""" + + mode_query_count: Union[int, None, UnsetType] = UNSET + """Number of queries in this report.""" + + mode_chart_count: Union[int, None, UnsetType] = UNSET + """Number of charts in this report.""" + + mode_query_preview: Union[str, None, UnsetType] = UNSET + """Preview of the query for the Mode asset.""" + + mode_is_public: Union[bool, None, UnsetType] = UNSET + """Whether the report is public.""" + + mode_is_shared: Union[bool, None, UnsetType] = UNSET + """Whether the report is shared.""" + + mode_is_archived: Union[bool, None, UnsetType] = UNSET + """Whether the report is archived.""" + + mode_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the Mode asset.""" + + mode_token: Union[str, None, UnsetType] = UNSET + """Token for the Mode asset.""" + + mode_workspace_name: Union[str, None, UnsetType] = UNSET + """Simple name of the workspace for the Mode asset.""" + + mode_workspace_username: Union[str, None, UnsetType] = UNSET + """Username of the workspace for the Mode asset.""" + + mode_workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace for the Mode asset.""" + + mode_report_name: Union[str, None, UnsetType] = UNSET + """Simple name of the report for the Mode asset.""" + + mode_report_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the report for the Mode asset.""" + + mode_query_name: Union[str, None, UnsetType] = UNSET + """Simple name of the query for the Mode asset.""" + + mode_query_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the query for the Mode asset.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mode_queries: Union[List[RelatedModeQuery], None, UnsetType] = UNSET + """Queries that exist within this report.""" + + mode_collections: Union[List[RelatedModeCollection], None, UnsetType] = UNSET + """Collections related to this report.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "ModeReport" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _mode_report_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> ModeReport: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + ModeReport instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _mode_report_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class ModeReportAttributes(AssetAttributes): + """ModeReport-specific attributes for nested API format.""" + + mode_collection_token: Union[str, None, UnsetType] = UNSET + """Token for the Mode collection.""" + + mode_report_published_at: Union[int, None, UnsetType] = UNSET + """Date and time when the report was published.""" + + mode_query_count: Union[int, None, UnsetType] = UNSET + """Number of queries in this report.""" + + mode_chart_count: Union[int, None, UnsetType] = UNSET + """Number of charts in this report.""" + + mode_query_preview: Union[str, None, UnsetType] = UNSET + """Preview of the query for the Mode asset.""" + + mode_is_public: Union[bool, None, UnsetType] = UNSET + """Whether the report is public.""" + + mode_is_shared: Union[bool, None, UnsetType] = UNSET + """Whether the report is shared.""" + + mode_is_archived: Union[bool, None, UnsetType] = UNSET + """Whether the report is archived.""" + + mode_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the Mode asset.""" + + mode_token: Union[str, None, UnsetType] = UNSET + """Token for the Mode asset.""" + + mode_workspace_name: Union[str, None, UnsetType] = UNSET + """Simple name of the workspace for the Mode asset.""" + + mode_workspace_username: Union[str, None, UnsetType] = UNSET + """Username of the workspace for the Mode asset.""" + + mode_workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace for the Mode asset.""" + + mode_report_name: Union[str, None, UnsetType] = UNSET + """Simple name of the report for the Mode asset.""" + + mode_report_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the report for the Mode asset.""" + + mode_query_name: Union[str, None, UnsetType] = UNSET + """Simple name of the query for the Mode asset.""" + + mode_query_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the query for the Mode asset.""" + + +class ModeReportRelationshipAttributes(AssetRelationshipAttributes): + """ModeReport-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mode_queries: Union[List[RelatedModeQuery], None, UnsetType] = UNSET + """Queries that exist within this report.""" + + mode_collections: Union[List[RelatedModeCollection], None, UnsetType] = UNSET + """Collections related to this report.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class ModeReportNested(AssetNested): + """ModeReport in nested API format for high-performance serialization.""" + + attributes: Union[ModeReportAttributes, UnsetType] = UNSET + relationship_attributes: Union[ModeReportRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + ModeReportRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + ModeReportRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_MODE_REPORT_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mode_queries", + "mode_collections", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_mode_report_attrs(attrs: ModeReportAttributes, obj: ModeReport) -> None: + """Populate ModeReport-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.mode_collection_token = obj.mode_collection_token + attrs.mode_report_published_at = obj.mode_report_published_at + attrs.mode_query_count = obj.mode_query_count + attrs.mode_chart_count = obj.mode_chart_count + attrs.mode_query_preview = obj.mode_query_preview + attrs.mode_is_public = obj.mode_is_public + attrs.mode_is_shared = obj.mode_is_shared + attrs.mode_is_archived = obj.mode_is_archived + attrs.mode_id = obj.mode_id + attrs.mode_token = obj.mode_token + attrs.mode_workspace_name = obj.mode_workspace_name + attrs.mode_workspace_username = obj.mode_workspace_username + attrs.mode_workspace_qualified_name = obj.mode_workspace_qualified_name + attrs.mode_report_name = obj.mode_report_name + attrs.mode_report_qualified_name = obj.mode_report_qualified_name + attrs.mode_query_name = obj.mode_query_name + attrs.mode_query_qualified_name = obj.mode_query_qualified_name + + +def _extract_mode_report_attrs(attrs: ModeReportAttributes) -> dict: + """Extract all ModeReport attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["mode_collection_token"] = attrs.mode_collection_token + result["mode_report_published_at"] = attrs.mode_report_published_at + result["mode_query_count"] = attrs.mode_query_count + result["mode_chart_count"] = attrs.mode_chart_count + result["mode_query_preview"] = attrs.mode_query_preview + result["mode_is_public"] = attrs.mode_is_public + result["mode_is_shared"] = attrs.mode_is_shared + result["mode_is_archived"] = attrs.mode_is_archived + result["mode_id"] = attrs.mode_id + result["mode_token"] = attrs.mode_token + result["mode_workspace_name"] = attrs.mode_workspace_name + result["mode_workspace_username"] = attrs.mode_workspace_username + result["mode_workspace_qualified_name"] = attrs.mode_workspace_qualified_name + result["mode_report_name"] = attrs.mode_report_name + result["mode_report_qualified_name"] = attrs.mode_report_qualified_name + result["mode_query_name"] = attrs.mode_query_name + result["mode_query_qualified_name"] = attrs.mode_query_qualified_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _mode_report_to_nested(mode_report: ModeReport) -> ModeReportNested: + """Convert flat ModeReport to nested format.""" + attrs = ModeReportAttributes() + _populate_mode_report_attrs(attrs, mode_report) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + mode_report, _MODE_REPORT_REL_FIELDS, ModeReportRelationshipAttributes + ) + return ModeReportNested( + guid=mode_report.guid, + type_name=mode_report.type_name, + status=mode_report.status, + version=mode_report.version, + create_time=mode_report.create_time, + update_time=mode_report.update_time, + created_by=mode_report.created_by, + updated_by=mode_report.updated_by, + classifications=mode_report.classifications, + classification_names=mode_report.classification_names, + meanings=mode_report.meanings, + labels=mode_report.labels, + business_attributes=mode_report.business_attributes, + custom_attributes=mode_report.custom_attributes, + pending_tasks=mode_report.pending_tasks, + proxy=mode_report.proxy, + is_incomplete=mode_report.is_incomplete, + provenance_type=mode_report.provenance_type, + home_id=mode_report.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _mode_report_from_nested(nested: ModeReportNested) -> ModeReport: + """Convert nested format to flat ModeReport.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else ModeReportAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _MODE_REPORT_REL_FIELDS, + ModeReportRelationshipAttributes, + ) + return ModeReport( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_mode_report_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _mode_report_to_nested_bytes(mode_report: ModeReport, serde: Serde) -> bytes: + """Convert flat ModeReport to nested JSON bytes.""" + return serde.encode(_mode_report_to_nested(mode_report)) + + +def _mode_report_from_nested_bytes(data: bytes, serde: Serde) -> ModeReport: + """Convert nested JSON bytes to flat ModeReport.""" + nested = serde.decode(data, ModeReportNested) + return _mode_report_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +ModeReport.MODE_COLLECTION_TOKEN = KeywordField( + "modeCollectionToken", "modeCollectionToken" +) +ModeReport.MODE_REPORT_PUBLISHED_AT = NumericField( + "modeReportPublishedAt", "modeReportPublishedAt" +) +ModeReport.MODE_QUERY_COUNT = NumericField("modeQueryCount", "modeQueryCount") +ModeReport.MODE_CHART_COUNT = NumericField("modeChartCount", "modeChartCount") +ModeReport.MODE_QUERY_PREVIEW = KeywordField("modeQueryPreview", "modeQueryPreview") +ModeReport.MODE_IS_PUBLIC = BooleanField("modeIsPublic", "modeIsPublic") +ModeReport.MODE_IS_SHARED = BooleanField("modeIsShared", "modeIsShared") +ModeReport.MODE_IS_ARCHIVED = BooleanField("modeIsArchived", "modeIsArchived") +ModeReport.MODE_ID = KeywordField("modeId", "modeId") +ModeReport.MODE_TOKEN = KeywordTextField("modeToken", "modeToken", "modeToken.text") +ModeReport.MODE_WORKSPACE_NAME = KeywordField("modeWorkspaceName", "modeWorkspaceName") +ModeReport.MODE_WORKSPACE_USERNAME = KeywordTextField( + "modeWorkspaceUsername", "modeWorkspaceUsername", "modeWorkspaceUsername.text" +) +ModeReport.MODE_WORKSPACE_QUALIFIED_NAME = KeywordTextField( + "modeWorkspaceQualifiedName", + "modeWorkspaceQualifiedName", + "modeWorkspaceQualifiedName.text", +) +ModeReport.MODE_REPORT_NAME = KeywordField("modeReportName", "modeReportName") +ModeReport.MODE_REPORT_QUALIFIED_NAME = KeywordTextField( + "modeReportQualifiedName", "modeReportQualifiedName", "modeReportQualifiedName.text" +) +ModeReport.MODE_QUERY_NAME = KeywordField("modeQueryName", "modeQueryName") +ModeReport.MODE_QUERY_QUALIFIED_NAME = KeywordTextField( + "modeQueryQualifiedName", "modeQueryQualifiedName", "modeQueryQualifiedName.text" +) +ModeReport.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +ModeReport.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +ModeReport.ANOMALO_CHECKS = RelationField("anomaloChecks") +ModeReport.APPLICATION = RelationField("application") +ModeReport.APPLICATION_FIELD = RelationField("applicationField") +ModeReport.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +ModeReport.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +ModeReport.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +ModeReport.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +ModeReport.METRICS = RelationField("metrics") +ModeReport.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +ModeReport.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +ModeReport.MEANINGS = RelationField("meanings") +ModeReport.MODE_QUERIES = RelationField("modeQueries") +ModeReport.MODE_COLLECTIONS = RelationField("modeCollections") +ModeReport.MC_MONITORS = RelationField("mcMonitors") +ModeReport.MC_INCIDENTS = RelationField("mcIncidents") +ModeReport.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +ModeReport.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +ModeReport.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +ModeReport.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +ModeReport.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +ModeReport.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +ModeReport.FILES = RelationField("files") +ModeReport.LINKS = RelationField("links") +ModeReport.README = RelationField("readme") +ModeReport.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +ModeReport.SODA_CHECKS = RelationField("sodaChecks") +ModeReport.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +ModeReport.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/mode_workspace.py b/pyatlan_v9/model/assets/mode_workspace.py new file mode 100644 index 000000000..42639e1b3 --- /dev/null +++ b/pyatlan_v9/model/assets/mode_workspace.py @@ -0,0 +1,662 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +ModeWorkspace asset model with flattened inheritance. + +This module provides: +- ModeWorkspace: Flat asset class (easy to use) +- ModeWorkspaceAttributes: Nested attributes struct (extends AssetAttributes) +- ModeWorkspaceNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .mode_related import RelatedModeCollection + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class ModeWorkspace(Asset): + """ + Instance of a Mode workspace in Atlan. + """ + + MODE_COLLECTION_COUNT: ClassVar[Any] = None + MODE_ID: ClassVar[Any] = None + MODE_TOKEN: ClassVar[Any] = None + MODE_WORKSPACE_NAME: ClassVar[Any] = None + MODE_WORKSPACE_USERNAME: ClassVar[Any] = None + MODE_WORKSPACE_QUALIFIED_NAME: ClassVar[Any] = None + MODE_REPORT_NAME: ClassVar[Any] = None + MODE_REPORT_QUALIFIED_NAME: ClassVar[Any] = None + MODE_QUERY_NAME: ClassVar[Any] = None + MODE_QUERY_QUALIFIED_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MODE_COLLECTIONS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "ModeWorkspace" + + mode_collection_count: Union[int, None, UnsetType] = UNSET + """Number of collections in this workspace.""" + + mode_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the Mode asset.""" + + mode_token: Union[str, None, UnsetType] = UNSET + """Token for the Mode asset.""" + + mode_workspace_name: Union[str, None, UnsetType] = UNSET + """Simple name of the workspace for the Mode asset.""" + + mode_workspace_username: Union[str, None, UnsetType] = UNSET + """Username of the workspace for the Mode asset.""" + + mode_workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace for the Mode asset.""" + + mode_report_name: Union[str, None, UnsetType] = UNSET + """Simple name of the report for the Mode asset.""" + + mode_report_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the report for the Mode asset.""" + + mode_query_name: Union[str, None, UnsetType] = UNSET + """Simple name of the query for the Mode asset.""" + + mode_query_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the query for the Mode asset.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mode_collections: Union[List[RelatedModeCollection], None, UnsetType] = UNSET + """Collections that exist within this workspace.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "ModeWorkspace" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _mode_workspace_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> ModeWorkspace: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + ModeWorkspace instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _mode_workspace_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class ModeWorkspaceAttributes(AssetAttributes): + """ModeWorkspace-specific attributes for nested API format.""" + + mode_collection_count: Union[int, None, UnsetType] = UNSET + """Number of collections in this workspace.""" + + mode_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the Mode asset.""" + + mode_token: Union[str, None, UnsetType] = UNSET + """Token for the Mode asset.""" + + mode_workspace_name: Union[str, None, UnsetType] = UNSET + """Simple name of the workspace for the Mode asset.""" + + mode_workspace_username: Union[str, None, UnsetType] = UNSET + """Username of the workspace for the Mode asset.""" + + mode_workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace for the Mode asset.""" + + mode_report_name: Union[str, None, UnsetType] = UNSET + """Simple name of the report for the Mode asset.""" + + mode_report_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the report for the Mode asset.""" + + mode_query_name: Union[str, None, UnsetType] = UNSET + """Simple name of the query for the Mode asset.""" + + mode_query_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the query for the Mode asset.""" + + +class ModeWorkspaceRelationshipAttributes(AssetRelationshipAttributes): + """ModeWorkspace-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mode_collections: Union[List[RelatedModeCollection], None, UnsetType] = UNSET + """Collections that exist within this workspace.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class ModeWorkspaceNested(AssetNested): + """ModeWorkspace in nested API format for high-performance serialization.""" + + attributes: Union[ModeWorkspaceAttributes, UnsetType] = UNSET + relationship_attributes: Union[ModeWorkspaceRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + ModeWorkspaceRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + ModeWorkspaceRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_MODE_WORKSPACE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mode_collections", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_mode_workspace_attrs( + attrs: ModeWorkspaceAttributes, obj: ModeWorkspace +) -> None: + """Populate ModeWorkspace-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.mode_collection_count = obj.mode_collection_count + attrs.mode_id = obj.mode_id + attrs.mode_token = obj.mode_token + attrs.mode_workspace_name = obj.mode_workspace_name + attrs.mode_workspace_username = obj.mode_workspace_username + attrs.mode_workspace_qualified_name = obj.mode_workspace_qualified_name + attrs.mode_report_name = obj.mode_report_name + attrs.mode_report_qualified_name = obj.mode_report_qualified_name + attrs.mode_query_name = obj.mode_query_name + attrs.mode_query_qualified_name = obj.mode_query_qualified_name + + +def _extract_mode_workspace_attrs(attrs: ModeWorkspaceAttributes) -> dict: + """Extract all ModeWorkspace attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["mode_collection_count"] = attrs.mode_collection_count + result["mode_id"] = attrs.mode_id + result["mode_token"] = attrs.mode_token + result["mode_workspace_name"] = attrs.mode_workspace_name + result["mode_workspace_username"] = attrs.mode_workspace_username + result["mode_workspace_qualified_name"] = attrs.mode_workspace_qualified_name + result["mode_report_name"] = attrs.mode_report_name + result["mode_report_qualified_name"] = attrs.mode_report_qualified_name + result["mode_query_name"] = attrs.mode_query_name + result["mode_query_qualified_name"] = attrs.mode_query_qualified_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _mode_workspace_to_nested(mode_workspace: ModeWorkspace) -> ModeWorkspaceNested: + """Convert flat ModeWorkspace to nested format.""" + attrs = ModeWorkspaceAttributes() + _populate_mode_workspace_attrs(attrs, mode_workspace) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + mode_workspace, _MODE_WORKSPACE_REL_FIELDS, ModeWorkspaceRelationshipAttributes + ) + return ModeWorkspaceNested( + guid=mode_workspace.guid, + type_name=mode_workspace.type_name, + status=mode_workspace.status, + version=mode_workspace.version, + create_time=mode_workspace.create_time, + update_time=mode_workspace.update_time, + created_by=mode_workspace.created_by, + updated_by=mode_workspace.updated_by, + classifications=mode_workspace.classifications, + classification_names=mode_workspace.classification_names, + meanings=mode_workspace.meanings, + labels=mode_workspace.labels, + business_attributes=mode_workspace.business_attributes, + custom_attributes=mode_workspace.custom_attributes, + pending_tasks=mode_workspace.pending_tasks, + proxy=mode_workspace.proxy, + is_incomplete=mode_workspace.is_incomplete, + provenance_type=mode_workspace.provenance_type, + home_id=mode_workspace.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _mode_workspace_from_nested(nested: ModeWorkspaceNested) -> ModeWorkspace: + """Convert nested format to flat ModeWorkspace.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else ModeWorkspaceAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _MODE_WORKSPACE_REL_FIELDS, + ModeWorkspaceRelationshipAttributes, + ) + return ModeWorkspace( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_mode_workspace_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _mode_workspace_to_nested_bytes( + mode_workspace: ModeWorkspace, serde: Serde +) -> bytes: + """Convert flat ModeWorkspace to nested JSON bytes.""" + return serde.encode(_mode_workspace_to_nested(mode_workspace)) + + +def _mode_workspace_from_nested_bytes(data: bytes, serde: Serde) -> ModeWorkspace: + """Convert nested JSON bytes to flat ModeWorkspace.""" + nested = serde.decode(data, ModeWorkspaceNested) + return _mode_workspace_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +ModeWorkspace.MODE_COLLECTION_COUNT = NumericField( + "modeCollectionCount", "modeCollectionCount" +) +ModeWorkspace.MODE_ID = KeywordField("modeId", "modeId") +ModeWorkspace.MODE_TOKEN = KeywordTextField("modeToken", "modeToken", "modeToken.text") +ModeWorkspace.MODE_WORKSPACE_NAME = KeywordField( + "modeWorkspaceName", "modeWorkspaceName" +) +ModeWorkspace.MODE_WORKSPACE_USERNAME = KeywordTextField( + "modeWorkspaceUsername", "modeWorkspaceUsername", "modeWorkspaceUsername.text" +) +ModeWorkspace.MODE_WORKSPACE_QUALIFIED_NAME = KeywordTextField( + "modeWorkspaceQualifiedName", + "modeWorkspaceQualifiedName", + "modeWorkspaceQualifiedName.text", +) +ModeWorkspace.MODE_REPORT_NAME = KeywordField("modeReportName", "modeReportName") +ModeWorkspace.MODE_REPORT_QUALIFIED_NAME = KeywordTextField( + "modeReportQualifiedName", "modeReportQualifiedName", "modeReportQualifiedName.text" +) +ModeWorkspace.MODE_QUERY_NAME = KeywordField("modeQueryName", "modeQueryName") +ModeWorkspace.MODE_QUERY_QUALIFIED_NAME = KeywordTextField( + "modeQueryQualifiedName", "modeQueryQualifiedName", "modeQueryQualifiedName.text" +) +ModeWorkspace.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +ModeWorkspace.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +ModeWorkspace.ANOMALO_CHECKS = RelationField("anomaloChecks") +ModeWorkspace.APPLICATION = RelationField("application") +ModeWorkspace.APPLICATION_FIELD = RelationField("applicationField") +ModeWorkspace.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +ModeWorkspace.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +ModeWorkspace.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +ModeWorkspace.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +ModeWorkspace.METRICS = RelationField("metrics") +ModeWorkspace.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +ModeWorkspace.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +ModeWorkspace.MEANINGS = RelationField("meanings") +ModeWorkspace.MODE_COLLECTIONS = RelationField("modeCollections") +ModeWorkspace.MC_MONITORS = RelationField("mcMonitors") +ModeWorkspace.MC_INCIDENTS = RelationField("mcIncidents") +ModeWorkspace.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +ModeWorkspace.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +ModeWorkspace.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +ModeWorkspace.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +ModeWorkspace.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +ModeWorkspace.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +ModeWorkspace.FILES = RelationField("files") +ModeWorkspace.LINKS = RelationField("links") +ModeWorkspace.README = RelationField("readme") +ModeWorkspace.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +ModeWorkspace.SODA_CHECKS = RelationField("sodaChecks") +ModeWorkspace.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +ModeWorkspace.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/model.py b/pyatlan_v9/model/assets/model.py new file mode 100644 index 000000000..88656d11b --- /dev/null +++ b/pyatlan_v9/model/assets/model.py @@ -0,0 +1,688 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Model asset model with flattened inheritance. + +This module provides: +- Model: Flat asset class (easy to use) +- ModelAttributes: Nested attributes struct (extends AssetAttributes) +- ModelNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .model_related import RelatedModelAttribute, RelatedModelEntity + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Model(Asset): + """ + Assets used to model data and information. + """ + + MODEL_NAME: ClassVar[Any] = None + MODEL_QUALIFIED_NAME: ClassVar[Any] = None + MODEL_DOMAIN: ClassVar[Any] = None + MODEL_NAMESPACE: ClassVar[Any] = None + MODEL_VERSION_NAME: ClassVar[Any] = None + MODEL_VERSION_AGNOSTIC_QUALIFIED_NAME: ClassVar[Any] = None + MODEL_VERSION_QUALIFIED_NAME: ClassVar[Any] = None + MODEL_ENTITY_NAME: ClassVar[Any] = None + MODEL_ENTITY_QUALIFIED_NAME: ClassVar[Any] = None + MODEL_TYPE: ClassVar[Any] = None + MODEL_SYSTEM_DATE: ClassVar[Any] = None + MODEL_BUSINESS_DATE: ClassVar[Any] = None + MODEL_EXPIRED_AT_SYSTEM_DATE: ClassVar[Any] = None + MODEL_EXPIRED_AT_BUSINESS_DATE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Model" + + model_name: Union[str, None, UnsetType] = UNSET + """Simple name of the model in which this asset exists, or empty if it is itself a data model.""" + + model_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the model in which this asset exists, or empty if it is itself a data model.""" + + model_domain: Union[str, None, UnsetType] = UNSET + """Model domain in which this asset exists.""" + + model_namespace: Union[str, None, UnsetType] = UNSET + """Model namespace in which this asset exists.""" + + model_version_name: Union[str, None, UnsetType] = UNSET + """Simple name of the version in which this asset exists, or empty if it is itself a data model version.""" + + model_version_agnostic_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the parent in which this asset exists, irrespective of the version (always implies the latest version).""" + + model_version_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the version in which this asset exists, or empty if it is itself a data model version.""" + + model_entity_name: Union[str, None, UnsetType] = UNSET + """Simple name of the entity in which this asset exists, or empty if it is itself a data model entity.""" + + model_entity_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the entity in which this asset exists, or empty if it is itself a data model entity.""" + + model_type: Union[str, None, UnsetType] = UNSET + """Type of the model asset (conceptual, logical, physical).""" + + model_system_date: Union[int, None, UnsetType] = UNSET + """System date for the asset.""" + + model_business_date: Union[int, None, UnsetType] = UNSET + """Business date for the asset.""" + + model_expired_at_system_date: Union[int, None, UnsetType] = UNSET + """System expiration date for the asset.""" + + model_expired_at_business_date: Union[int, None, UnsetType] = UNSET + """Business expiration date for the asset.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Model" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _model_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Model: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Model instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _model_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class ModelAttributes(AssetAttributes): + """Model-specific attributes for nested API format.""" + + model_name: Union[str, None, UnsetType] = UNSET + """Simple name of the model in which this asset exists, or empty if it is itself a data model.""" + + model_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the model in which this asset exists, or empty if it is itself a data model.""" + + model_domain: Union[str, None, UnsetType] = UNSET + """Model domain in which this asset exists.""" + + model_namespace: Union[str, None, UnsetType] = UNSET + """Model namespace in which this asset exists.""" + + model_version_name: Union[str, None, UnsetType] = UNSET + """Simple name of the version in which this asset exists, or empty if it is itself a data model version.""" + + model_version_agnostic_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the parent in which this asset exists, irrespective of the version (always implies the latest version).""" + + model_version_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the version in which this asset exists, or empty if it is itself a data model version.""" + + model_entity_name: Union[str, None, UnsetType] = UNSET + """Simple name of the entity in which this asset exists, or empty if it is itself a data model entity.""" + + model_entity_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the entity in which this asset exists, or empty if it is itself a data model entity.""" + + model_type: Union[str, None, UnsetType] = UNSET + """Type of the model asset (conceptual, logical, physical).""" + + model_system_date: Union[int, None, UnsetType] = UNSET + """System date for the asset.""" + + model_business_date: Union[int, None, UnsetType] = UNSET + """Business date for the asset.""" + + model_expired_at_system_date: Union[int, None, UnsetType] = UNSET + """System expiration date for the asset.""" + + model_expired_at_business_date: Union[int, None, UnsetType] = UNSET + """Business expiration date for the asset.""" + + +class ModelRelationshipAttributes(AssetRelationshipAttributes): + """Model-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class ModelNested(AssetNested): + """Model in nested API format for high-performance serialization.""" + + attributes: Union[ModelAttributes, UnsetType] = UNSET + relationship_attributes: Union[ModelRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ModelRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[ModelRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_MODEL_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_model_attrs(attrs: ModelAttributes, obj: Model) -> None: + """Populate Model-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.model_name = obj.model_name + attrs.model_qualified_name = obj.model_qualified_name + attrs.model_domain = obj.model_domain + attrs.model_namespace = obj.model_namespace + attrs.model_version_name = obj.model_version_name + attrs.model_version_agnostic_qualified_name = ( + obj.model_version_agnostic_qualified_name + ) + attrs.model_version_qualified_name = obj.model_version_qualified_name + attrs.model_entity_name = obj.model_entity_name + attrs.model_entity_qualified_name = obj.model_entity_qualified_name + attrs.model_type = obj.model_type + attrs.model_system_date = obj.model_system_date + attrs.model_business_date = obj.model_business_date + attrs.model_expired_at_system_date = obj.model_expired_at_system_date + attrs.model_expired_at_business_date = obj.model_expired_at_business_date + + +def _extract_model_attrs(attrs: ModelAttributes) -> dict: + """Extract all Model attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["model_name"] = attrs.model_name + result["model_qualified_name"] = attrs.model_qualified_name + result["model_domain"] = attrs.model_domain + result["model_namespace"] = attrs.model_namespace + result["model_version_name"] = attrs.model_version_name + result["model_version_agnostic_qualified_name"] = ( + attrs.model_version_agnostic_qualified_name + ) + result["model_version_qualified_name"] = attrs.model_version_qualified_name + result["model_entity_name"] = attrs.model_entity_name + result["model_entity_qualified_name"] = attrs.model_entity_qualified_name + result["model_type"] = attrs.model_type + result["model_system_date"] = attrs.model_system_date + result["model_business_date"] = attrs.model_business_date + result["model_expired_at_system_date"] = attrs.model_expired_at_system_date + result["model_expired_at_business_date"] = attrs.model_expired_at_business_date + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _model_to_nested(model: Model) -> ModelNested: + """Convert flat Model to nested format.""" + attrs = ModelAttributes() + _populate_model_attrs(attrs, model) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + model, _MODEL_REL_FIELDS, ModelRelationshipAttributes + ) + return ModelNested( + guid=model.guid, + type_name=model.type_name, + status=model.status, + version=model.version, + create_time=model.create_time, + update_time=model.update_time, + created_by=model.created_by, + updated_by=model.updated_by, + classifications=model.classifications, + classification_names=model.classification_names, + meanings=model.meanings, + labels=model.labels, + business_attributes=model.business_attributes, + custom_attributes=model.custom_attributes, + pending_tasks=model.pending_tasks, + proxy=model.proxy, + is_incomplete=model.is_incomplete, + provenance_type=model.provenance_type, + home_id=model.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _model_from_nested(nested: ModelNested) -> Model: + """Convert nested format to flat Model.""" + attrs = nested.attributes if nested.attributes is not UNSET else ModelAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _MODEL_REL_FIELDS, + ModelRelationshipAttributes, + ) + return Model( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_model_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _model_to_nested_bytes(model: Model, serde: Serde) -> bytes: + """Convert flat Model to nested JSON bytes.""" + return serde.encode(_model_to_nested(model)) + + +def _model_from_nested_bytes(data: bytes, serde: Serde) -> Model: + """Convert nested JSON bytes to flat Model.""" + nested = serde.decode(data, ModelNested) + return _model_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +Model.MODEL_NAME = KeywordField("modelName", "modelName") +Model.MODEL_QUALIFIED_NAME = KeywordField("modelQualifiedName", "modelQualifiedName") +Model.MODEL_DOMAIN = KeywordTextField("modelDomain", "modelDomain", "modelDomain.text") +Model.MODEL_NAMESPACE = KeywordTextField( + "modelNamespace", "modelNamespace", "modelNamespace.text" +) +Model.MODEL_VERSION_NAME = KeywordTextField( + "modelVersionName", "modelVersionName", "modelVersionName.text" +) +Model.MODEL_VERSION_AGNOSTIC_QUALIFIED_NAME = KeywordField( + "modelVersionAgnosticQualifiedName", "modelVersionAgnosticQualifiedName" +) +Model.MODEL_VERSION_QUALIFIED_NAME = KeywordField( + "modelVersionQualifiedName", "modelVersionQualifiedName" +) +Model.MODEL_ENTITY_NAME = KeywordTextField( + "modelEntityName", "modelEntityName", "modelEntityName.text" +) +Model.MODEL_ENTITY_QUALIFIED_NAME = KeywordField( + "modelEntityQualifiedName", "modelEntityQualifiedName" +) +Model.MODEL_TYPE = KeywordField("modelType", "modelType") +Model.MODEL_SYSTEM_DATE = NumericField("modelSystemDate", "modelSystemDate") +Model.MODEL_BUSINESS_DATE = NumericField("modelBusinessDate", "modelBusinessDate") +Model.MODEL_EXPIRED_AT_SYSTEM_DATE = NumericField( + "modelExpiredAtSystemDate", "modelExpiredAtSystemDate" +) +Model.MODEL_EXPIRED_AT_BUSINESS_DATE = NumericField( + "modelExpiredAtBusinessDate", "modelExpiredAtBusinessDate" +) +Model.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Model.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Model.ANOMALO_CHECKS = RelationField("anomaloChecks") +Model.APPLICATION = RelationField("application") +Model.APPLICATION_FIELD = RelationField("applicationField") +Model.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Model.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Model.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Model.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Model.METRICS = RelationField("metrics") +Model.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Model.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Model.MEANINGS = RelationField("meanings") +Model.MC_MONITORS = RelationField("mcMonitors") +Model.MC_INCIDENTS = RelationField("mcIncidents") +Model.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Model.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Model.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Model.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Model.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Model.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Model.FILES = RelationField("files") +Model.LINKS = RelationField("links") +Model.README = RelationField("readme") +Model.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Model.SODA_CHECKS = RelationField("sodaChecks") +Model.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Model.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/model_attribute.py b/pyatlan_v9/model/assets/model_attribute.py new file mode 100644 index 000000000..e7e92791a --- /dev/null +++ b/pyatlan_v9/model/assets/model_attribute.py @@ -0,0 +1,896 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +ModelAttribute asset model with flattened inheritance. + +This module provides: +- ModelAttribute: Flat asset class (easy to use) +- ModelAttributeAttributes: Nested attributes struct (extends AssetAttributes) +- ModelAttributeNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .catalog_related import RelatedCatalog +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .model_related import ( + RelatedModelAttribute, + RelatedModelAttributeAssociation, + RelatedModelEntity, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class ModelAttribute(Asset): + """ + Instance of an attribute within a data model entity in Atlan. + """ + + MODEL_ATTRIBUTE_IS_NULLABLE: ClassVar[Any] = None + MODEL_ATTRIBUTE_IS_PRIMARY: ClassVar[Any] = None + MODEL_ATTRIBUTE_IS_FOREIGN: ClassVar[Any] = None + MODEL_ATTRIBUTE_IS_DERIVED: ClassVar[Any] = None + MODEL_ATTRIBUTE_PRECISION: ClassVar[Any] = None + MODEL_ATTRIBUTE_SCALE: ClassVar[Any] = None + MODEL_ATTRIBUTE_DATA_TYPE: ClassVar[Any] = None + MODEL_ATTRIBUTE_HAS_RELATIONSHIPS: ClassVar[Any] = None + MODEL_NAME: ClassVar[Any] = None + MODEL_QUALIFIED_NAME: ClassVar[Any] = None + MODEL_DOMAIN: ClassVar[Any] = None + MODEL_NAMESPACE: ClassVar[Any] = None + MODEL_VERSION_NAME: ClassVar[Any] = None + MODEL_VERSION_AGNOSTIC_QUALIFIED_NAME: ClassVar[Any] = None + MODEL_VERSION_QUALIFIED_NAME: ClassVar[Any] = None + MODEL_ENTITY_NAME: ClassVar[Any] = None + MODEL_ENTITY_QUALIFIED_NAME: ClassVar[Any] = None + MODEL_TYPE: ClassVar[Any] = None + MODEL_SYSTEM_DATE: ClassVar[Any] = None + MODEL_BUSINESS_DATE: ClassVar[Any] = None + MODEL_EXPIRED_AT_SYSTEM_DATE: ClassVar[Any] = None + MODEL_EXPIRED_AT_BUSINESS_DATE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_ATTRIBUTE_ENTITIES: ClassVar[Any] = None + MODEL_ATTRIBUTE_MAPPED_TO_ATTRIBUTES: ClassVar[Any] = None + MODEL_ATTRIBUTE_MAPPED_FROM_ATTRIBUTES: ClassVar[Any] = None + MODEL_ATTRIBUTE_IMPLEMENTED_BY_ASSETS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + MODEL_ATTRIBUTE_RELATED_FROM_ATTRIBUTES: ClassVar[Any] = None + MODEL_ATTRIBUTE_RELATED_TO_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "ModelAttribute" + + model_attribute_is_nullable: Union[bool, None, UnsetType] = UNSET + """When true, the values in this attribute can be null.""" + + model_attribute_is_primary: Union[bool, None, UnsetType] = UNSET + """When true, this attribute forms the primary key for the entity.""" + + model_attribute_is_foreign: Union[bool, None, UnsetType] = UNSET + """When true, this attribute is a foreign key to another entity.""" + + model_attribute_is_derived: Union[bool, None, UnsetType] = UNSET + """When true, the values in this attribute are derived data.""" + + model_attribute_precision: Union[int, None, UnsetType] = UNSET + """Precision of the attribute.""" + + model_attribute_scale: Union[int, None, UnsetType] = UNSET + """Scale of the attribute.""" + + model_attribute_data_type: Union[str, None, UnsetType] = UNSET + """Type of the attribute.""" + + model_attribute_has_relationships: Union[bool, None, UnsetType] = UNSET + """When true, this attribute has relationships with other attributes.""" + + model_name: Union[str, None, UnsetType] = UNSET + """Simple name of the model in which this asset exists, or empty if it is itself a data model.""" + + model_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the model in which this asset exists, or empty if it is itself a data model.""" + + model_domain: Union[str, None, UnsetType] = UNSET + """Model domain in which this asset exists.""" + + model_namespace: Union[str, None, UnsetType] = UNSET + """Model namespace in which this asset exists.""" + + model_version_name: Union[str, None, UnsetType] = UNSET + """Simple name of the version in which this asset exists, or empty if it is itself a data model version.""" + + model_version_agnostic_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the parent in which this asset exists, irrespective of the version (always implies the latest version).""" + + model_version_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the version in which this asset exists, or empty if it is itself a data model version.""" + + model_entity_name: Union[str, None, UnsetType] = UNSET + """Simple name of the entity in which this asset exists, or empty if it is itself a data model entity.""" + + model_entity_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the entity in which this asset exists, or empty if it is itself a data model entity.""" + + model_type: Union[str, None, UnsetType] = UNSET + """Type of the model asset (conceptual, logical, physical).""" + + model_system_date: Union[int, None, UnsetType] = UNSET + """System date for the asset.""" + + model_business_date: Union[int, None, UnsetType] = UNSET + """Business date for the asset.""" + + model_expired_at_system_date: Union[int, None, UnsetType] = UNSET + """System expiration date for the asset.""" + + model_expired_at_business_date: Union[int, None, UnsetType] = UNSET + """Business expiration date for the asset.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_attribute_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entity (or versions of an entity) in which this attribute exists.""" + + model_attribute_mapped_to_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes to which this attribute is mapped.""" + + model_attribute_mapped_from_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes from which this attribute is mapped.""" + + model_attribute_implemented_by_assets: Union[ + List[RelatedCatalog], None, UnsetType + ] = UNSET + """Assets that implement this attribute.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + model_attribute_related_from_attributes: Union[ + List[RelatedModelAttributeAssociation], None, UnsetType + ] = UNSET + """Association from which this attribute is related.""" + + model_attribute_related_to_attributes: Union[ + List[RelatedModelAttributeAssociation], None, UnsetType + ] = UNSET + """Association to which this attribute is related.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "ModelAttribute" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _model_attribute_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> ModelAttribute: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + ModelAttribute instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _model_attribute_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class ModelAttributeAttributes(AssetAttributes): + """ModelAttribute-specific attributes for nested API format.""" + + model_attribute_is_nullable: Union[bool, None, UnsetType] = UNSET + """When true, the values in this attribute can be null.""" + + model_attribute_is_primary: Union[bool, None, UnsetType] = UNSET + """When true, this attribute forms the primary key for the entity.""" + + model_attribute_is_foreign: Union[bool, None, UnsetType] = UNSET + """When true, this attribute is a foreign key to another entity.""" + + model_attribute_is_derived: Union[bool, None, UnsetType] = UNSET + """When true, the values in this attribute are derived data.""" + + model_attribute_precision: Union[int, None, UnsetType] = UNSET + """Precision of the attribute.""" + + model_attribute_scale: Union[int, None, UnsetType] = UNSET + """Scale of the attribute.""" + + model_attribute_data_type: Union[str, None, UnsetType] = UNSET + """Type of the attribute.""" + + model_attribute_has_relationships: Union[bool, None, UnsetType] = UNSET + """When true, this attribute has relationships with other attributes.""" + + model_name: Union[str, None, UnsetType] = UNSET + """Simple name of the model in which this asset exists, or empty if it is itself a data model.""" + + model_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the model in which this asset exists, or empty if it is itself a data model.""" + + model_domain: Union[str, None, UnsetType] = UNSET + """Model domain in which this asset exists.""" + + model_namespace: Union[str, None, UnsetType] = UNSET + """Model namespace in which this asset exists.""" + + model_version_name: Union[str, None, UnsetType] = UNSET + """Simple name of the version in which this asset exists, or empty if it is itself a data model version.""" + + model_version_agnostic_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the parent in which this asset exists, irrespective of the version (always implies the latest version).""" + + model_version_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the version in which this asset exists, or empty if it is itself a data model version.""" + + model_entity_name: Union[str, None, UnsetType] = UNSET + """Simple name of the entity in which this asset exists, or empty if it is itself a data model entity.""" + + model_entity_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the entity in which this asset exists, or empty if it is itself a data model entity.""" + + model_type: Union[str, None, UnsetType] = UNSET + """Type of the model asset (conceptual, logical, physical).""" + + model_system_date: Union[int, None, UnsetType] = UNSET + """System date for the asset.""" + + model_business_date: Union[int, None, UnsetType] = UNSET + """Business date for the asset.""" + + model_expired_at_system_date: Union[int, None, UnsetType] = UNSET + """System expiration date for the asset.""" + + model_expired_at_business_date: Union[int, None, UnsetType] = UNSET + """Business expiration date for the asset.""" + + +class ModelAttributeRelationshipAttributes(AssetRelationshipAttributes): + """ModelAttribute-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_attribute_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entity (or versions of an entity) in which this attribute exists.""" + + model_attribute_mapped_to_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes to which this attribute is mapped.""" + + model_attribute_mapped_from_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes from which this attribute is mapped.""" + + model_attribute_implemented_by_assets: Union[ + List[RelatedCatalog], None, UnsetType + ] = UNSET + """Assets that implement this attribute.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + model_attribute_related_from_attributes: Union[ + List[RelatedModelAttributeAssociation], None, UnsetType + ] = UNSET + """Association from which this attribute is related.""" + + model_attribute_related_to_attributes: Union[ + List[RelatedModelAttributeAssociation], None, UnsetType + ] = UNSET + """Association to which this attribute is related.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class ModelAttributeNested(AssetNested): + """ModelAttribute in nested API format for high-performance serialization.""" + + attributes: Union[ModelAttributeAttributes, UnsetType] = UNSET + relationship_attributes: Union[ModelAttributeRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + ModelAttributeRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + ModelAttributeRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_MODEL_ATTRIBUTE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_attribute_entities", + "model_attribute_mapped_to_attributes", + "model_attribute_mapped_from_attributes", + "model_attribute_implemented_by_assets", + "model_implemented_attributes", + "model_attribute_related_from_attributes", + "model_attribute_related_to_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_model_attribute_attrs( + attrs: ModelAttributeAttributes, obj: ModelAttribute +) -> None: + """Populate ModelAttribute-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.model_attribute_is_nullable = obj.model_attribute_is_nullable + attrs.model_attribute_is_primary = obj.model_attribute_is_primary + attrs.model_attribute_is_foreign = obj.model_attribute_is_foreign + attrs.model_attribute_is_derived = obj.model_attribute_is_derived + attrs.model_attribute_precision = obj.model_attribute_precision + attrs.model_attribute_scale = obj.model_attribute_scale + attrs.model_attribute_data_type = obj.model_attribute_data_type + attrs.model_attribute_has_relationships = obj.model_attribute_has_relationships + attrs.model_name = obj.model_name + attrs.model_qualified_name = obj.model_qualified_name + attrs.model_domain = obj.model_domain + attrs.model_namespace = obj.model_namespace + attrs.model_version_name = obj.model_version_name + attrs.model_version_agnostic_qualified_name = ( + obj.model_version_agnostic_qualified_name + ) + attrs.model_version_qualified_name = obj.model_version_qualified_name + attrs.model_entity_name = obj.model_entity_name + attrs.model_entity_qualified_name = obj.model_entity_qualified_name + attrs.model_type = obj.model_type + attrs.model_system_date = obj.model_system_date + attrs.model_business_date = obj.model_business_date + attrs.model_expired_at_system_date = obj.model_expired_at_system_date + attrs.model_expired_at_business_date = obj.model_expired_at_business_date + + +def _extract_model_attribute_attrs(attrs: ModelAttributeAttributes) -> dict: + """Extract all ModelAttribute attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["model_attribute_is_nullable"] = attrs.model_attribute_is_nullable + result["model_attribute_is_primary"] = attrs.model_attribute_is_primary + result["model_attribute_is_foreign"] = attrs.model_attribute_is_foreign + result["model_attribute_is_derived"] = attrs.model_attribute_is_derived + result["model_attribute_precision"] = attrs.model_attribute_precision + result["model_attribute_scale"] = attrs.model_attribute_scale + result["model_attribute_data_type"] = attrs.model_attribute_data_type + result["model_attribute_has_relationships"] = ( + attrs.model_attribute_has_relationships + ) + result["model_name"] = attrs.model_name + result["model_qualified_name"] = attrs.model_qualified_name + result["model_domain"] = attrs.model_domain + result["model_namespace"] = attrs.model_namespace + result["model_version_name"] = attrs.model_version_name + result["model_version_agnostic_qualified_name"] = ( + attrs.model_version_agnostic_qualified_name + ) + result["model_version_qualified_name"] = attrs.model_version_qualified_name + result["model_entity_name"] = attrs.model_entity_name + result["model_entity_qualified_name"] = attrs.model_entity_qualified_name + result["model_type"] = attrs.model_type + result["model_system_date"] = attrs.model_system_date + result["model_business_date"] = attrs.model_business_date + result["model_expired_at_system_date"] = attrs.model_expired_at_system_date + result["model_expired_at_business_date"] = attrs.model_expired_at_business_date + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _model_attribute_to_nested(model_attribute: ModelAttribute) -> ModelAttributeNested: + """Convert flat ModelAttribute to nested format.""" + attrs = ModelAttributeAttributes() + _populate_model_attribute_attrs(attrs, model_attribute) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + model_attribute, + _MODEL_ATTRIBUTE_REL_FIELDS, + ModelAttributeRelationshipAttributes, + ) + return ModelAttributeNested( + guid=model_attribute.guid, + type_name=model_attribute.type_name, + status=model_attribute.status, + version=model_attribute.version, + create_time=model_attribute.create_time, + update_time=model_attribute.update_time, + created_by=model_attribute.created_by, + updated_by=model_attribute.updated_by, + classifications=model_attribute.classifications, + classification_names=model_attribute.classification_names, + meanings=model_attribute.meanings, + labels=model_attribute.labels, + business_attributes=model_attribute.business_attributes, + custom_attributes=model_attribute.custom_attributes, + pending_tasks=model_attribute.pending_tasks, + proxy=model_attribute.proxy, + is_incomplete=model_attribute.is_incomplete, + provenance_type=model_attribute.provenance_type, + home_id=model_attribute.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _model_attribute_from_nested(nested: ModelAttributeNested) -> ModelAttribute: + """Convert nested format to flat ModelAttribute.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else ModelAttributeAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _MODEL_ATTRIBUTE_REL_FIELDS, + ModelAttributeRelationshipAttributes, + ) + return ModelAttribute( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_model_attribute_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _model_attribute_to_nested_bytes( + model_attribute: ModelAttribute, serde: Serde +) -> bytes: + """Convert flat ModelAttribute to nested JSON bytes.""" + return serde.encode(_model_attribute_to_nested(model_attribute)) + + +def _model_attribute_from_nested_bytes(data: bytes, serde: Serde) -> ModelAttribute: + """Convert nested JSON bytes to flat ModelAttribute.""" + nested = serde.decode(data, ModelAttributeNested) + return _model_attribute_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +ModelAttribute.MODEL_ATTRIBUTE_IS_NULLABLE = BooleanField( + "modelAttributeIsNullable", "modelAttributeIsNullable" +) +ModelAttribute.MODEL_ATTRIBUTE_IS_PRIMARY = BooleanField( + "modelAttributeIsPrimary", "modelAttributeIsPrimary" +) +ModelAttribute.MODEL_ATTRIBUTE_IS_FOREIGN = BooleanField( + "modelAttributeIsForeign", "modelAttributeIsForeign" +) +ModelAttribute.MODEL_ATTRIBUTE_IS_DERIVED = BooleanField( + "modelAttributeIsDerived", "modelAttributeIsDerived" +) +ModelAttribute.MODEL_ATTRIBUTE_PRECISION = NumericField( + "modelAttributePrecision", "modelAttributePrecision" +) +ModelAttribute.MODEL_ATTRIBUTE_SCALE = NumericField( + "modelAttributeScale", "modelAttributeScale" +) +ModelAttribute.MODEL_ATTRIBUTE_DATA_TYPE = KeywordField( + "modelAttributeDataType", "modelAttributeDataType" +) +ModelAttribute.MODEL_ATTRIBUTE_HAS_RELATIONSHIPS = BooleanField( + "modelAttributeHasRelationships", "modelAttributeHasRelationships" +) +ModelAttribute.MODEL_NAME = KeywordField("modelName", "modelName") +ModelAttribute.MODEL_QUALIFIED_NAME = KeywordField( + "modelQualifiedName", "modelQualifiedName" +) +ModelAttribute.MODEL_DOMAIN = KeywordTextField( + "modelDomain", "modelDomain", "modelDomain.text" +) +ModelAttribute.MODEL_NAMESPACE = KeywordTextField( + "modelNamespace", "modelNamespace", "modelNamespace.text" +) +ModelAttribute.MODEL_VERSION_NAME = KeywordTextField( + "modelVersionName", "modelVersionName", "modelVersionName.text" +) +ModelAttribute.MODEL_VERSION_AGNOSTIC_QUALIFIED_NAME = KeywordField( + "modelVersionAgnosticQualifiedName", "modelVersionAgnosticQualifiedName" +) +ModelAttribute.MODEL_VERSION_QUALIFIED_NAME = KeywordField( + "modelVersionQualifiedName", "modelVersionQualifiedName" +) +ModelAttribute.MODEL_ENTITY_NAME = KeywordTextField( + "modelEntityName", "modelEntityName", "modelEntityName.text" +) +ModelAttribute.MODEL_ENTITY_QUALIFIED_NAME = KeywordField( + "modelEntityQualifiedName", "modelEntityQualifiedName" +) +ModelAttribute.MODEL_TYPE = KeywordField("modelType", "modelType") +ModelAttribute.MODEL_SYSTEM_DATE = NumericField("modelSystemDate", "modelSystemDate") +ModelAttribute.MODEL_BUSINESS_DATE = NumericField( + "modelBusinessDate", "modelBusinessDate" +) +ModelAttribute.MODEL_EXPIRED_AT_SYSTEM_DATE = NumericField( + "modelExpiredAtSystemDate", "modelExpiredAtSystemDate" +) +ModelAttribute.MODEL_EXPIRED_AT_BUSINESS_DATE = NumericField( + "modelExpiredAtBusinessDate", "modelExpiredAtBusinessDate" +) +ModelAttribute.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +ModelAttribute.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +ModelAttribute.ANOMALO_CHECKS = RelationField("anomaloChecks") +ModelAttribute.APPLICATION = RelationField("application") +ModelAttribute.APPLICATION_FIELD = RelationField("applicationField") +ModelAttribute.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +ModelAttribute.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +ModelAttribute.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +ModelAttribute.MODEL_ATTRIBUTE_ENTITIES = RelationField("modelAttributeEntities") +ModelAttribute.MODEL_ATTRIBUTE_MAPPED_TO_ATTRIBUTES = RelationField( + "modelAttributeMappedToAttributes" +) +ModelAttribute.MODEL_ATTRIBUTE_MAPPED_FROM_ATTRIBUTES = RelationField( + "modelAttributeMappedFromAttributes" +) +ModelAttribute.MODEL_ATTRIBUTE_IMPLEMENTED_BY_ASSETS = RelationField( + "modelAttributeImplementedByAssets" +) +ModelAttribute.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +ModelAttribute.MODEL_ATTRIBUTE_RELATED_FROM_ATTRIBUTES = RelationField( + "modelAttributeRelatedFromAttributes" +) +ModelAttribute.MODEL_ATTRIBUTE_RELATED_TO_ATTRIBUTES = RelationField( + "modelAttributeRelatedToAttributes" +) +ModelAttribute.METRICS = RelationField("metrics") +ModelAttribute.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +ModelAttribute.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +ModelAttribute.MEANINGS = RelationField("meanings") +ModelAttribute.MC_MONITORS = RelationField("mcMonitors") +ModelAttribute.MC_INCIDENTS = RelationField("mcIncidents") +ModelAttribute.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +ModelAttribute.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +ModelAttribute.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +ModelAttribute.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +ModelAttribute.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +ModelAttribute.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +ModelAttribute.FILES = RelationField("files") +ModelAttribute.LINKS = RelationField("links") +ModelAttribute.README = RelationField("readme") +ModelAttribute.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +ModelAttribute.SODA_CHECKS = RelationField("sodaChecks") +ModelAttribute.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +ModelAttribute.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/model_attribute_association.py b/pyatlan_v9/model/assets/model_attribute_association.py new file mode 100644 index 000000000..0f2abacb9 --- /dev/null +++ b/pyatlan_v9/model/assets/model_attribute_association.py @@ -0,0 +1,827 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +ModelAttributeAssociation asset model with flattened inheritance. + +This module provides: +- ModelAttributeAssociation: Flat asset class (easy to use) +- ModelAttributeAssociationAttributes: Nested attributes struct (extends AssetAttributes) +- ModelAttributeAssociationNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .model_related import RelatedModelAttribute, RelatedModelEntity + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class ModelAttributeAssociation(Asset): + """ + Instance of a data attribute association in Atlan. + """ + + MODEL_ATTRIBUTE_ASSOCIATION_TO_QUALIFIED_NAME: ClassVar[Any] = None + MODEL_ATTRIBUTE_ASSOCIATION_FROM_QUALIFIED_NAME: ClassVar[Any] = None + MODEL_ENTITY_ASSOCIATION_QUALIFIED_NAME: ClassVar[Any] = None + MODEL_NAME: ClassVar[Any] = None + MODEL_QUALIFIED_NAME: ClassVar[Any] = None + MODEL_DOMAIN: ClassVar[Any] = None + MODEL_NAMESPACE: ClassVar[Any] = None + MODEL_VERSION_NAME: ClassVar[Any] = None + MODEL_VERSION_AGNOSTIC_QUALIFIED_NAME: ClassVar[Any] = None + MODEL_VERSION_QUALIFIED_NAME: ClassVar[Any] = None + MODEL_ENTITY_NAME: ClassVar[Any] = None + MODEL_ENTITY_QUALIFIED_NAME: ClassVar[Any] = None + MODEL_TYPE: ClassVar[Any] = None + MODEL_SYSTEM_DATE: ClassVar[Any] = None + MODEL_BUSINESS_DATE: ClassVar[Any] = None + MODEL_EXPIRED_AT_SYSTEM_DATE: ClassVar[Any] = None + MODEL_EXPIRED_AT_BUSINESS_DATE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + MODEL_ATTRIBUTE_ASSOCIATION_TO: ClassVar[Any] = None + MODEL_ATTRIBUTE_ASSOCIATION_FROM: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "ModelAttributeAssociation" + + model_attribute_association_to_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the association to which this attribute is related.""" + + model_attribute_association_from_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the association from which this attribute is related.""" + + model_entity_association_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the entity association to which this attribute is related.""" + + model_name: Union[str, None, UnsetType] = UNSET + """Simple name of the model in which this asset exists, or empty if it is itself a data model.""" + + model_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the model in which this asset exists, or empty if it is itself a data model.""" + + model_domain: Union[str, None, UnsetType] = UNSET + """Model domain in which this asset exists.""" + + model_namespace: Union[str, None, UnsetType] = UNSET + """Model namespace in which this asset exists.""" + + model_version_name: Union[str, None, UnsetType] = UNSET + """Simple name of the version in which this asset exists, or empty if it is itself a data model version.""" + + model_version_agnostic_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the parent in which this asset exists, irrespective of the version (always implies the latest version).""" + + model_version_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the version in which this asset exists, or empty if it is itself a data model version.""" + + model_entity_name: Union[str, None, UnsetType] = UNSET + """Simple name of the entity in which this asset exists, or empty if it is itself a data model entity.""" + + model_entity_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the entity in which this asset exists, or empty if it is itself a data model entity.""" + + model_type: Union[str, None, UnsetType] = UNSET + """Type of the model asset (conceptual, logical, physical).""" + + model_system_date: Union[int, None, UnsetType] = UNSET + """System date for the asset.""" + + model_business_date: Union[int, None, UnsetType] = UNSET + """Business date for the asset.""" + + model_expired_at_system_date: Union[int, None, UnsetType] = UNSET + """System expiration date for the asset.""" + + model_expired_at_business_date: Union[int, None, UnsetType] = UNSET + """Business expiration date for the asset.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + model_attribute_association_to: Union[RelatedModelAttribute, None, UnsetType] = ( + UNSET + ) + """Attribute to which this association is related.""" + + model_attribute_association_from: Union[RelatedModelAttribute, None, UnsetType] = ( + UNSET + ) + """Attribute from which this association is related.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "ModelAttributeAssociation" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _model_attribute_association_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> ModelAttributeAssociation: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + ModelAttributeAssociation instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _model_attribute_association_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class ModelAttributeAssociationAttributes(AssetAttributes): + """ModelAttributeAssociation-specific attributes for nested API format.""" + + model_attribute_association_to_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the association to which this attribute is related.""" + + model_attribute_association_from_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the association from which this attribute is related.""" + + model_entity_association_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the entity association to which this attribute is related.""" + + model_name: Union[str, None, UnsetType] = UNSET + """Simple name of the model in which this asset exists, or empty if it is itself a data model.""" + + model_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the model in which this asset exists, or empty if it is itself a data model.""" + + model_domain: Union[str, None, UnsetType] = UNSET + """Model domain in which this asset exists.""" + + model_namespace: Union[str, None, UnsetType] = UNSET + """Model namespace in which this asset exists.""" + + model_version_name: Union[str, None, UnsetType] = UNSET + """Simple name of the version in which this asset exists, or empty if it is itself a data model version.""" + + model_version_agnostic_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the parent in which this asset exists, irrespective of the version (always implies the latest version).""" + + model_version_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the version in which this asset exists, or empty if it is itself a data model version.""" + + model_entity_name: Union[str, None, UnsetType] = UNSET + """Simple name of the entity in which this asset exists, or empty if it is itself a data model entity.""" + + model_entity_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the entity in which this asset exists, or empty if it is itself a data model entity.""" + + model_type: Union[str, None, UnsetType] = UNSET + """Type of the model asset (conceptual, logical, physical).""" + + model_system_date: Union[int, None, UnsetType] = UNSET + """System date for the asset.""" + + model_business_date: Union[int, None, UnsetType] = UNSET + """Business date for the asset.""" + + model_expired_at_system_date: Union[int, None, UnsetType] = UNSET + """System expiration date for the asset.""" + + model_expired_at_business_date: Union[int, None, UnsetType] = UNSET + """Business expiration date for the asset.""" + + +class ModelAttributeAssociationRelationshipAttributes(AssetRelationshipAttributes): + """ModelAttributeAssociation-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + model_attribute_association_to: Union[RelatedModelAttribute, None, UnsetType] = ( + UNSET + ) + """Attribute to which this association is related.""" + + model_attribute_association_from: Union[RelatedModelAttribute, None, UnsetType] = ( + UNSET + ) + """Attribute from which this association is related.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class ModelAttributeAssociationNested(AssetNested): + """ModelAttributeAssociation in nested API format for high-performance serialization.""" + + attributes: Union[ModelAttributeAssociationAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + ModelAttributeAssociationRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + ModelAttributeAssociationRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + ModelAttributeAssociationRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_MODEL_ATTRIBUTE_ASSOCIATION_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "model_attribute_association_to", + "model_attribute_association_from", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_model_attribute_association_attrs( + attrs: ModelAttributeAssociationAttributes, obj: ModelAttributeAssociation +) -> None: + """Populate ModelAttributeAssociation-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.model_attribute_association_to_qualified_name = ( + obj.model_attribute_association_to_qualified_name + ) + attrs.model_attribute_association_from_qualified_name = ( + obj.model_attribute_association_from_qualified_name + ) + attrs.model_entity_association_qualified_name = ( + obj.model_entity_association_qualified_name + ) + attrs.model_name = obj.model_name + attrs.model_qualified_name = obj.model_qualified_name + attrs.model_domain = obj.model_domain + attrs.model_namespace = obj.model_namespace + attrs.model_version_name = obj.model_version_name + attrs.model_version_agnostic_qualified_name = ( + obj.model_version_agnostic_qualified_name + ) + attrs.model_version_qualified_name = obj.model_version_qualified_name + attrs.model_entity_name = obj.model_entity_name + attrs.model_entity_qualified_name = obj.model_entity_qualified_name + attrs.model_type = obj.model_type + attrs.model_system_date = obj.model_system_date + attrs.model_business_date = obj.model_business_date + attrs.model_expired_at_system_date = obj.model_expired_at_system_date + attrs.model_expired_at_business_date = obj.model_expired_at_business_date + + +def _extract_model_attribute_association_attrs( + attrs: ModelAttributeAssociationAttributes, +) -> dict: + """Extract all ModelAttributeAssociation attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["model_attribute_association_to_qualified_name"] = ( + attrs.model_attribute_association_to_qualified_name + ) + result["model_attribute_association_from_qualified_name"] = ( + attrs.model_attribute_association_from_qualified_name + ) + result["model_entity_association_qualified_name"] = ( + attrs.model_entity_association_qualified_name + ) + result["model_name"] = attrs.model_name + result["model_qualified_name"] = attrs.model_qualified_name + result["model_domain"] = attrs.model_domain + result["model_namespace"] = attrs.model_namespace + result["model_version_name"] = attrs.model_version_name + result["model_version_agnostic_qualified_name"] = ( + attrs.model_version_agnostic_qualified_name + ) + result["model_version_qualified_name"] = attrs.model_version_qualified_name + result["model_entity_name"] = attrs.model_entity_name + result["model_entity_qualified_name"] = attrs.model_entity_qualified_name + result["model_type"] = attrs.model_type + result["model_system_date"] = attrs.model_system_date + result["model_business_date"] = attrs.model_business_date + result["model_expired_at_system_date"] = attrs.model_expired_at_system_date + result["model_expired_at_business_date"] = attrs.model_expired_at_business_date + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _model_attribute_association_to_nested( + model_attribute_association: ModelAttributeAssociation, +) -> ModelAttributeAssociationNested: + """Convert flat ModelAttributeAssociation to nested format.""" + attrs = ModelAttributeAssociationAttributes() + _populate_model_attribute_association_attrs(attrs, model_attribute_association) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + model_attribute_association, + _MODEL_ATTRIBUTE_ASSOCIATION_REL_FIELDS, + ModelAttributeAssociationRelationshipAttributes, + ) + return ModelAttributeAssociationNested( + guid=model_attribute_association.guid, + type_name=model_attribute_association.type_name, + status=model_attribute_association.status, + version=model_attribute_association.version, + create_time=model_attribute_association.create_time, + update_time=model_attribute_association.update_time, + created_by=model_attribute_association.created_by, + updated_by=model_attribute_association.updated_by, + classifications=model_attribute_association.classifications, + classification_names=model_attribute_association.classification_names, + meanings=model_attribute_association.meanings, + labels=model_attribute_association.labels, + business_attributes=model_attribute_association.business_attributes, + custom_attributes=model_attribute_association.custom_attributes, + pending_tasks=model_attribute_association.pending_tasks, + proxy=model_attribute_association.proxy, + is_incomplete=model_attribute_association.is_incomplete, + provenance_type=model_attribute_association.provenance_type, + home_id=model_attribute_association.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _model_attribute_association_from_nested( + nested: ModelAttributeAssociationNested, +) -> ModelAttributeAssociation: + """Convert nested format to flat ModelAttributeAssociation.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else ModelAttributeAssociationAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _MODEL_ATTRIBUTE_ASSOCIATION_REL_FIELDS, + ModelAttributeAssociationRelationshipAttributes, + ) + return ModelAttributeAssociation( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_model_attribute_association_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _model_attribute_association_to_nested_bytes( + model_attribute_association: ModelAttributeAssociation, serde: Serde +) -> bytes: + """Convert flat ModelAttributeAssociation to nested JSON bytes.""" + return serde.encode( + _model_attribute_association_to_nested(model_attribute_association) + ) + + +def _model_attribute_association_from_nested_bytes( + data: bytes, serde: Serde +) -> ModelAttributeAssociation: + """Convert nested JSON bytes to flat ModelAttributeAssociation.""" + nested = serde.decode(data, ModelAttributeAssociationNested) + return _model_attribute_association_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +ModelAttributeAssociation.MODEL_ATTRIBUTE_ASSOCIATION_TO_QUALIFIED_NAME = KeywordField( + "modelAttributeAssociationToQualifiedName", + "modelAttributeAssociationToQualifiedName", +) +ModelAttributeAssociation.MODEL_ATTRIBUTE_ASSOCIATION_FROM_QUALIFIED_NAME = ( + KeywordField( + "modelAttributeAssociationFromQualifiedName", + "modelAttributeAssociationFromQualifiedName", + ) +) +ModelAttributeAssociation.MODEL_ENTITY_ASSOCIATION_QUALIFIED_NAME = KeywordField( + "modelEntityAssociationQualifiedName", "modelEntityAssociationQualifiedName" +) +ModelAttributeAssociation.MODEL_NAME = KeywordField("modelName", "modelName") +ModelAttributeAssociation.MODEL_QUALIFIED_NAME = KeywordField( + "modelQualifiedName", "modelQualifiedName" +) +ModelAttributeAssociation.MODEL_DOMAIN = KeywordTextField( + "modelDomain", "modelDomain", "modelDomain.text" +) +ModelAttributeAssociation.MODEL_NAMESPACE = KeywordTextField( + "modelNamespace", "modelNamespace", "modelNamespace.text" +) +ModelAttributeAssociation.MODEL_VERSION_NAME = KeywordTextField( + "modelVersionName", "modelVersionName", "modelVersionName.text" +) +ModelAttributeAssociation.MODEL_VERSION_AGNOSTIC_QUALIFIED_NAME = KeywordField( + "modelVersionAgnosticQualifiedName", "modelVersionAgnosticQualifiedName" +) +ModelAttributeAssociation.MODEL_VERSION_QUALIFIED_NAME = KeywordField( + "modelVersionQualifiedName", "modelVersionQualifiedName" +) +ModelAttributeAssociation.MODEL_ENTITY_NAME = KeywordTextField( + "modelEntityName", "modelEntityName", "modelEntityName.text" +) +ModelAttributeAssociation.MODEL_ENTITY_QUALIFIED_NAME = KeywordField( + "modelEntityQualifiedName", "modelEntityQualifiedName" +) +ModelAttributeAssociation.MODEL_TYPE = KeywordField("modelType", "modelType") +ModelAttributeAssociation.MODEL_SYSTEM_DATE = NumericField( + "modelSystemDate", "modelSystemDate" +) +ModelAttributeAssociation.MODEL_BUSINESS_DATE = NumericField( + "modelBusinessDate", "modelBusinessDate" +) +ModelAttributeAssociation.MODEL_EXPIRED_AT_SYSTEM_DATE = NumericField( + "modelExpiredAtSystemDate", "modelExpiredAtSystemDate" +) +ModelAttributeAssociation.MODEL_EXPIRED_AT_BUSINESS_DATE = NumericField( + "modelExpiredAtBusinessDate", "modelExpiredAtBusinessDate" +) +ModelAttributeAssociation.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +ModelAttributeAssociation.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +ModelAttributeAssociation.ANOMALO_CHECKS = RelationField("anomaloChecks") +ModelAttributeAssociation.APPLICATION = RelationField("application") +ModelAttributeAssociation.APPLICATION_FIELD = RelationField("applicationField") +ModelAttributeAssociation.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +ModelAttributeAssociation.INPUT_PORT_DATA_PRODUCTS = RelationField( + "inputPortDataProducts" +) +ModelAttributeAssociation.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +ModelAttributeAssociation.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +ModelAttributeAssociation.MODEL_ATTRIBUTE_ASSOCIATION_TO = RelationField( + "modelAttributeAssociationTo" +) +ModelAttributeAssociation.MODEL_ATTRIBUTE_ASSOCIATION_FROM = RelationField( + "modelAttributeAssociationFrom" +) +ModelAttributeAssociation.METRICS = RelationField("metrics") +ModelAttributeAssociation.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +ModelAttributeAssociation.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +ModelAttributeAssociation.MEANINGS = RelationField("meanings") +ModelAttributeAssociation.MC_MONITORS = RelationField("mcMonitors") +ModelAttributeAssociation.MC_INCIDENTS = RelationField("mcIncidents") +ModelAttributeAssociation.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +ModelAttributeAssociation.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +ModelAttributeAssociation.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +ModelAttributeAssociation.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +ModelAttributeAssociation.USER_DEF_RELATIONSHIP_TO = RelationField( + "userDefRelationshipTo" +) +ModelAttributeAssociation.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +ModelAttributeAssociation.FILES = RelationField("files") +ModelAttributeAssociation.LINKS = RelationField("links") +ModelAttributeAssociation.README = RelationField("readme") +ModelAttributeAssociation.SCHEMA_REGISTRY_SUBJECTS = RelationField( + "schemaRegistrySubjects" +) +ModelAttributeAssociation.SODA_CHECKS = RelationField("sodaChecks") +ModelAttributeAssociation.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +ModelAttributeAssociation.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/model_data_model.py b/pyatlan_v9/model/assets/model_data_model.py new file mode 100644 index 000000000..ffa5e968c --- /dev/null +++ b/pyatlan_v9/model/assets/model_data_model.py @@ -0,0 +1,745 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +ModelDataModel asset model with flattened inheritance. + +This module provides: +- ModelDataModel: Flat asset class (easy to use) +- ModelDataModelAttributes: Nested attributes struct (extends AssetAttributes) +- ModelDataModelNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .model_related import ( + RelatedModelAttribute, + RelatedModelEntity, + RelatedModelVersion, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class ModelDataModel(Asset): + """ + Instance of a data model in Atlan. + """ + + MODEL_VERSION_COUNT: ClassVar[Any] = None + MODEL_TOOL: ClassVar[Any] = None + MODEL_NAME: ClassVar[Any] = None + MODEL_QUALIFIED_NAME: ClassVar[Any] = None + MODEL_DOMAIN: ClassVar[Any] = None + MODEL_NAMESPACE: ClassVar[Any] = None + MODEL_VERSION_NAME: ClassVar[Any] = None + MODEL_VERSION_AGNOSTIC_QUALIFIED_NAME: ClassVar[Any] = None + MODEL_VERSION_QUALIFIED_NAME: ClassVar[Any] = None + MODEL_ENTITY_NAME: ClassVar[Any] = None + MODEL_ENTITY_QUALIFIED_NAME: ClassVar[Any] = None + MODEL_TYPE: ClassVar[Any] = None + MODEL_SYSTEM_DATE: ClassVar[Any] = None + MODEL_BUSINESS_DATE: ClassVar[Any] = None + MODEL_EXPIRED_AT_SYSTEM_DATE: ClassVar[Any] = None + MODEL_EXPIRED_AT_BUSINESS_DATE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_VERSIONS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "ModelDataModel" + + model_version_count: Union[int, None, UnsetType] = UNSET + """Number of versions of the data model.""" + + model_tool: Union[str, None, UnsetType] = UNSET + """Tool used to create this data model.""" + + model_name: Union[str, None, UnsetType] = UNSET + """Simple name of the model in which this asset exists, or empty if it is itself a data model.""" + + model_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the model in which this asset exists, or empty if it is itself a data model.""" + + model_domain: Union[str, None, UnsetType] = UNSET + """Model domain in which this asset exists.""" + + model_namespace: Union[str, None, UnsetType] = UNSET + """Model namespace in which this asset exists.""" + + model_version_name: Union[str, None, UnsetType] = UNSET + """Simple name of the version in which this asset exists, or empty if it is itself a data model version.""" + + model_version_agnostic_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the parent in which this asset exists, irrespective of the version (always implies the latest version).""" + + model_version_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the version in which this asset exists, or empty if it is itself a data model version.""" + + model_entity_name: Union[str, None, UnsetType] = UNSET + """Simple name of the entity in which this asset exists, or empty if it is itself a data model entity.""" + + model_entity_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the entity in which this asset exists, or empty if it is itself a data model entity.""" + + model_type: Union[str, None, UnsetType] = UNSET + """Type of the model asset (conceptual, logical, physical).""" + + model_system_date: Union[int, None, UnsetType] = UNSET + """System date for the asset.""" + + model_business_date: Union[int, None, UnsetType] = UNSET + """Business date for the asset.""" + + model_expired_at_system_date: Union[int, None, UnsetType] = UNSET + """System expiration date for the asset.""" + + model_expired_at_business_date: Union[int, None, UnsetType] = UNSET + """Business expiration date for the asset.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_versions: Union[List[RelatedModelVersion], None, UnsetType] = UNSET + """Individual versions of the data model.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "ModelDataModel" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _model_data_model_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> ModelDataModel: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + ModelDataModel instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _model_data_model_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class ModelDataModelAttributes(AssetAttributes): + """ModelDataModel-specific attributes for nested API format.""" + + model_version_count: Union[int, None, UnsetType] = UNSET + """Number of versions of the data model.""" + + model_tool: Union[str, None, UnsetType] = UNSET + """Tool used to create this data model.""" + + model_name: Union[str, None, UnsetType] = UNSET + """Simple name of the model in which this asset exists, or empty if it is itself a data model.""" + + model_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the model in which this asset exists, or empty if it is itself a data model.""" + + model_domain: Union[str, None, UnsetType] = UNSET + """Model domain in which this asset exists.""" + + model_namespace: Union[str, None, UnsetType] = UNSET + """Model namespace in which this asset exists.""" + + model_version_name: Union[str, None, UnsetType] = UNSET + """Simple name of the version in which this asset exists, or empty if it is itself a data model version.""" + + model_version_agnostic_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the parent in which this asset exists, irrespective of the version (always implies the latest version).""" + + model_version_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the version in which this asset exists, or empty if it is itself a data model version.""" + + model_entity_name: Union[str, None, UnsetType] = UNSET + """Simple name of the entity in which this asset exists, or empty if it is itself a data model entity.""" + + model_entity_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the entity in which this asset exists, or empty if it is itself a data model entity.""" + + model_type: Union[str, None, UnsetType] = UNSET + """Type of the model asset (conceptual, logical, physical).""" + + model_system_date: Union[int, None, UnsetType] = UNSET + """System date for the asset.""" + + model_business_date: Union[int, None, UnsetType] = UNSET + """Business date for the asset.""" + + model_expired_at_system_date: Union[int, None, UnsetType] = UNSET + """System expiration date for the asset.""" + + model_expired_at_business_date: Union[int, None, UnsetType] = UNSET + """Business expiration date for the asset.""" + + +class ModelDataModelRelationshipAttributes(AssetRelationshipAttributes): + """ModelDataModel-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_versions: Union[List[RelatedModelVersion], None, UnsetType] = UNSET + """Individual versions of the data model.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class ModelDataModelNested(AssetNested): + """ModelDataModel in nested API format for high-performance serialization.""" + + attributes: Union[ModelDataModelAttributes, UnsetType] = UNSET + relationship_attributes: Union[ModelDataModelRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + ModelDataModelRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + ModelDataModelRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_MODEL_DATA_MODEL_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_versions", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_model_data_model_attrs( + attrs: ModelDataModelAttributes, obj: ModelDataModel +) -> None: + """Populate ModelDataModel-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.model_version_count = obj.model_version_count + attrs.model_tool = obj.model_tool + attrs.model_name = obj.model_name + attrs.model_qualified_name = obj.model_qualified_name + attrs.model_domain = obj.model_domain + attrs.model_namespace = obj.model_namespace + attrs.model_version_name = obj.model_version_name + attrs.model_version_agnostic_qualified_name = ( + obj.model_version_agnostic_qualified_name + ) + attrs.model_version_qualified_name = obj.model_version_qualified_name + attrs.model_entity_name = obj.model_entity_name + attrs.model_entity_qualified_name = obj.model_entity_qualified_name + attrs.model_type = obj.model_type + attrs.model_system_date = obj.model_system_date + attrs.model_business_date = obj.model_business_date + attrs.model_expired_at_system_date = obj.model_expired_at_system_date + attrs.model_expired_at_business_date = obj.model_expired_at_business_date + + +def _extract_model_data_model_attrs(attrs: ModelDataModelAttributes) -> dict: + """Extract all ModelDataModel attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["model_version_count"] = attrs.model_version_count + result["model_tool"] = attrs.model_tool + result["model_name"] = attrs.model_name + result["model_qualified_name"] = attrs.model_qualified_name + result["model_domain"] = attrs.model_domain + result["model_namespace"] = attrs.model_namespace + result["model_version_name"] = attrs.model_version_name + result["model_version_agnostic_qualified_name"] = ( + attrs.model_version_agnostic_qualified_name + ) + result["model_version_qualified_name"] = attrs.model_version_qualified_name + result["model_entity_name"] = attrs.model_entity_name + result["model_entity_qualified_name"] = attrs.model_entity_qualified_name + result["model_type"] = attrs.model_type + result["model_system_date"] = attrs.model_system_date + result["model_business_date"] = attrs.model_business_date + result["model_expired_at_system_date"] = attrs.model_expired_at_system_date + result["model_expired_at_business_date"] = attrs.model_expired_at_business_date + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _model_data_model_to_nested( + model_data_model: ModelDataModel, +) -> ModelDataModelNested: + """Convert flat ModelDataModel to nested format.""" + attrs = ModelDataModelAttributes() + _populate_model_data_model_attrs(attrs, model_data_model) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + model_data_model, + _MODEL_DATA_MODEL_REL_FIELDS, + ModelDataModelRelationshipAttributes, + ) + return ModelDataModelNested( + guid=model_data_model.guid, + type_name=model_data_model.type_name, + status=model_data_model.status, + version=model_data_model.version, + create_time=model_data_model.create_time, + update_time=model_data_model.update_time, + created_by=model_data_model.created_by, + updated_by=model_data_model.updated_by, + classifications=model_data_model.classifications, + classification_names=model_data_model.classification_names, + meanings=model_data_model.meanings, + labels=model_data_model.labels, + business_attributes=model_data_model.business_attributes, + custom_attributes=model_data_model.custom_attributes, + pending_tasks=model_data_model.pending_tasks, + proxy=model_data_model.proxy, + is_incomplete=model_data_model.is_incomplete, + provenance_type=model_data_model.provenance_type, + home_id=model_data_model.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _model_data_model_from_nested(nested: ModelDataModelNested) -> ModelDataModel: + """Convert nested format to flat ModelDataModel.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else ModelDataModelAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _MODEL_DATA_MODEL_REL_FIELDS, + ModelDataModelRelationshipAttributes, + ) + return ModelDataModel( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_model_data_model_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _model_data_model_to_nested_bytes( + model_data_model: ModelDataModel, serde: Serde +) -> bytes: + """Convert flat ModelDataModel to nested JSON bytes.""" + return serde.encode(_model_data_model_to_nested(model_data_model)) + + +def _model_data_model_from_nested_bytes(data: bytes, serde: Serde) -> ModelDataModel: + """Convert nested JSON bytes to flat ModelDataModel.""" + nested = serde.decode(data, ModelDataModelNested) + return _model_data_model_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +ModelDataModel.MODEL_VERSION_COUNT = NumericField( + "modelVersionCount", "modelVersionCount" +) +ModelDataModel.MODEL_TOOL = KeywordField("modelTool", "modelTool") +ModelDataModel.MODEL_NAME = KeywordField("modelName", "modelName") +ModelDataModel.MODEL_QUALIFIED_NAME = KeywordField( + "modelQualifiedName", "modelQualifiedName" +) +ModelDataModel.MODEL_DOMAIN = KeywordTextField( + "modelDomain", "modelDomain", "modelDomain.text" +) +ModelDataModel.MODEL_NAMESPACE = KeywordTextField( + "modelNamespace", "modelNamespace", "modelNamespace.text" +) +ModelDataModel.MODEL_VERSION_NAME = KeywordTextField( + "modelVersionName", "modelVersionName", "modelVersionName.text" +) +ModelDataModel.MODEL_VERSION_AGNOSTIC_QUALIFIED_NAME = KeywordField( + "modelVersionAgnosticQualifiedName", "modelVersionAgnosticQualifiedName" +) +ModelDataModel.MODEL_VERSION_QUALIFIED_NAME = KeywordField( + "modelVersionQualifiedName", "modelVersionQualifiedName" +) +ModelDataModel.MODEL_ENTITY_NAME = KeywordTextField( + "modelEntityName", "modelEntityName", "modelEntityName.text" +) +ModelDataModel.MODEL_ENTITY_QUALIFIED_NAME = KeywordField( + "modelEntityQualifiedName", "modelEntityQualifiedName" +) +ModelDataModel.MODEL_TYPE = KeywordField("modelType", "modelType") +ModelDataModel.MODEL_SYSTEM_DATE = NumericField("modelSystemDate", "modelSystemDate") +ModelDataModel.MODEL_BUSINESS_DATE = NumericField( + "modelBusinessDate", "modelBusinessDate" +) +ModelDataModel.MODEL_EXPIRED_AT_SYSTEM_DATE = NumericField( + "modelExpiredAtSystemDate", "modelExpiredAtSystemDate" +) +ModelDataModel.MODEL_EXPIRED_AT_BUSINESS_DATE = NumericField( + "modelExpiredAtBusinessDate", "modelExpiredAtBusinessDate" +) +ModelDataModel.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +ModelDataModel.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +ModelDataModel.ANOMALO_CHECKS = RelationField("anomaloChecks") +ModelDataModel.APPLICATION = RelationField("application") +ModelDataModel.APPLICATION_FIELD = RelationField("applicationField") +ModelDataModel.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +ModelDataModel.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +ModelDataModel.MODEL_VERSIONS = RelationField("modelVersions") +ModelDataModel.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +ModelDataModel.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +ModelDataModel.METRICS = RelationField("metrics") +ModelDataModel.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +ModelDataModel.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +ModelDataModel.MEANINGS = RelationField("meanings") +ModelDataModel.MC_MONITORS = RelationField("mcMonitors") +ModelDataModel.MC_INCIDENTS = RelationField("mcIncidents") +ModelDataModel.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +ModelDataModel.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +ModelDataModel.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +ModelDataModel.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +ModelDataModel.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +ModelDataModel.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +ModelDataModel.FILES = RelationField("files") +ModelDataModel.LINKS = RelationField("links") +ModelDataModel.README = RelationField("readme") +ModelDataModel.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +ModelDataModel.SODA_CHECKS = RelationField("sodaChecks") +ModelDataModel.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +ModelDataModel.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/model_entity.py b/pyatlan_v9/model/assets/model_entity.py new file mode 100644 index 000000000..1ac7f22d0 --- /dev/null +++ b/pyatlan_v9/model/assets/model_entity.py @@ -0,0 +1,886 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +ModelEntity asset model with flattened inheritance. + +This module provides: +- ModelEntity: Flat asset class (easy to use) +- ModelEntityAttributes: Nested attributes struct (extends AssetAttributes) +- ModelEntityNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .catalog_related import RelatedCatalog +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .model_related import ( + RelatedModelAttribute, + RelatedModelEntity, + RelatedModelEntityAssociation, + RelatedModelVersion, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class ModelEntity(Asset): + """ + Instance of an entity within a version of a data model in Atlan. + """ + + MODEL_ENTITY_ATTRIBUTE_COUNT: ClassVar[Any] = None + MODEL_ENTITY_SUBJECT_AREA: ClassVar[Any] = None + MODEL_ENTITY_GENERALIZATION_NAME: ClassVar[Any] = None + MODEL_ENTITY_GENERALIZATION_QUALIFIED_NAME: ClassVar[Any] = None + MODEL_NAME: ClassVar[Any] = None + MODEL_QUALIFIED_NAME: ClassVar[Any] = None + MODEL_DOMAIN: ClassVar[Any] = None + MODEL_NAMESPACE: ClassVar[Any] = None + MODEL_VERSION_NAME: ClassVar[Any] = None + MODEL_VERSION_AGNOSTIC_QUALIFIED_NAME: ClassVar[Any] = None + MODEL_VERSION_QUALIFIED_NAME: ClassVar[Any] = None + MODEL_ENTITY_NAME: ClassVar[Any] = None + MODEL_ENTITY_QUALIFIED_NAME: ClassVar[Any] = None + MODEL_TYPE: ClassVar[Any] = None + MODEL_SYSTEM_DATE: ClassVar[Any] = None + MODEL_BUSINESS_DATE: ClassVar[Any] = None + MODEL_EXPIRED_AT_SYSTEM_DATE: ClassVar[Any] = None + MODEL_EXPIRED_AT_BUSINESS_DATE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_VERSIONS: ClassVar[Any] = None + MODEL_ENTITY_MAPPED_TO_ENTITIES: ClassVar[Any] = None + MODEL_ENTITY_MAPPED_FROM_ENTITIES: ClassVar[Any] = None + MODEL_ENTITY_IMPLEMENTED_BY_ASSETS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_ENTITY_SPECIALIZATION_ENTITIES: ClassVar[Any] = None + MODEL_ENTITY_GENERALIZATION_ENTITY: ClassVar[Any] = None + MODEL_ENTITY_RELATED_FROM_ENTITIES: ClassVar[Any] = None + MODEL_ENTITY_RELATED_TO_ENTITIES: ClassVar[Any] = None + MODEL_ENTITY_ATTRIBUTES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "ModelEntity" + + model_entity_attribute_count: Union[int, None, UnsetType] = UNSET + """Number of attributes in the entity.""" + + model_entity_subject_area: Union[str, None, UnsetType] = UNSET + """Subject area of the entity.""" + + model_entity_generalization_name: Union[str, None, UnsetType] = UNSET + """Name of the general entity.""" + + model_entity_generalization_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique identifier for the general entity.""" + + model_name: Union[str, None, UnsetType] = UNSET + """Simple name of the model in which this asset exists, or empty if it is itself a data model.""" + + model_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the model in which this asset exists, or empty if it is itself a data model.""" + + model_domain: Union[str, None, UnsetType] = UNSET + """Model domain in which this asset exists.""" + + model_namespace: Union[str, None, UnsetType] = UNSET + """Model namespace in which this asset exists.""" + + model_version_name: Union[str, None, UnsetType] = UNSET + """Simple name of the version in which this asset exists, or empty if it is itself a data model version.""" + + model_version_agnostic_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the parent in which this asset exists, irrespective of the version (always implies the latest version).""" + + model_version_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the version in which this asset exists, or empty if it is itself a data model version.""" + + model_entity_name: Union[str, None, UnsetType] = UNSET + """Simple name of the entity in which this asset exists, or empty if it is itself a data model entity.""" + + model_entity_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the entity in which this asset exists, or empty if it is itself a data model entity.""" + + model_type: Union[str, None, UnsetType] = UNSET + """Type of the model asset (conceptual, logical, physical).""" + + model_system_date: Union[int, None, UnsetType] = UNSET + """System date for the asset.""" + + model_business_date: Union[int, None, UnsetType] = UNSET + """Business date for the asset.""" + + model_expired_at_system_date: Union[int, None, UnsetType] = UNSET + """System expiration date for the asset.""" + + model_expired_at_business_date: Union[int, None, UnsetType] = UNSET + """Business expiration date for the asset.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_versions: Union[List[RelatedModelVersion], None, UnsetType] = UNSET + """Data model version(s) in which this entity exists.""" + + model_entity_mapped_to_entities: Union[ + List[RelatedModelEntity], None, UnsetType + ] = UNSET + """Entities to which this entity is mapped.""" + + model_entity_mapped_from_entities: Union[ + List[RelatedModelEntity], None, UnsetType + ] = UNSET + """Entities from which this entity is mapped.""" + + model_entity_implemented_by_assets: Union[List[RelatedCatalog], None, UnsetType] = ( + UNSET + ) + """Assets that implement this entity.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_entity_specialization_entities: Union[ + List[RelatedModelEntity], None, UnsetType + ] = UNSET + """Specialized entities derived from the general entity.""" + + model_entity_generalization_entity: Union[RelatedModelEntity, None, UnsetType] = ( + UNSET + ) + """General entity, representing shared characteristics of specialized entities.""" + + model_entity_related_from_entities: Union[ + List[RelatedModelEntityAssociation], None, UnsetType + ] = UNSET + """Association from which this entity is related.""" + + model_entity_related_to_entities: Union[ + List[RelatedModelEntityAssociation], None, UnsetType + ] = UNSET + """Association to which this entity is related.""" + + model_entity_attributes: Union[List[RelatedModelAttribute], None, UnsetType] = UNSET + """Individual attributes that make up the entity.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "ModelEntity" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _model_entity_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> ModelEntity: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + ModelEntity instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _model_entity_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class ModelEntityAttributes(AssetAttributes): + """ModelEntity-specific attributes for nested API format.""" + + model_entity_attribute_count: Union[int, None, UnsetType] = UNSET + """Number of attributes in the entity.""" + + model_entity_subject_area: Union[str, None, UnsetType] = UNSET + """Subject area of the entity.""" + + model_entity_generalization_name: Union[str, None, UnsetType] = UNSET + """Name of the general entity.""" + + model_entity_generalization_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique identifier for the general entity.""" + + model_name: Union[str, None, UnsetType] = UNSET + """Simple name of the model in which this asset exists, or empty if it is itself a data model.""" + + model_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the model in which this asset exists, or empty if it is itself a data model.""" + + model_domain: Union[str, None, UnsetType] = UNSET + """Model domain in which this asset exists.""" + + model_namespace: Union[str, None, UnsetType] = UNSET + """Model namespace in which this asset exists.""" + + model_version_name: Union[str, None, UnsetType] = UNSET + """Simple name of the version in which this asset exists, or empty if it is itself a data model version.""" + + model_version_agnostic_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the parent in which this asset exists, irrespective of the version (always implies the latest version).""" + + model_version_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the version in which this asset exists, or empty if it is itself a data model version.""" + + model_entity_name: Union[str, None, UnsetType] = UNSET + """Simple name of the entity in which this asset exists, or empty if it is itself a data model entity.""" + + model_entity_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the entity in which this asset exists, or empty if it is itself a data model entity.""" + + model_type: Union[str, None, UnsetType] = UNSET + """Type of the model asset (conceptual, logical, physical).""" + + model_system_date: Union[int, None, UnsetType] = UNSET + """System date for the asset.""" + + model_business_date: Union[int, None, UnsetType] = UNSET + """Business date for the asset.""" + + model_expired_at_system_date: Union[int, None, UnsetType] = UNSET + """System expiration date for the asset.""" + + model_expired_at_business_date: Union[int, None, UnsetType] = UNSET + """Business expiration date for the asset.""" + + +class ModelEntityRelationshipAttributes(AssetRelationshipAttributes): + """ModelEntity-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_versions: Union[List[RelatedModelVersion], None, UnsetType] = UNSET + """Data model version(s) in which this entity exists.""" + + model_entity_mapped_to_entities: Union[ + List[RelatedModelEntity], None, UnsetType + ] = UNSET + """Entities to which this entity is mapped.""" + + model_entity_mapped_from_entities: Union[ + List[RelatedModelEntity], None, UnsetType + ] = UNSET + """Entities from which this entity is mapped.""" + + model_entity_implemented_by_assets: Union[List[RelatedCatalog], None, UnsetType] = ( + UNSET + ) + """Assets that implement this entity.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_entity_specialization_entities: Union[ + List[RelatedModelEntity], None, UnsetType + ] = UNSET + """Specialized entities derived from the general entity.""" + + model_entity_generalization_entity: Union[RelatedModelEntity, None, UnsetType] = ( + UNSET + ) + """General entity, representing shared characteristics of specialized entities.""" + + model_entity_related_from_entities: Union[ + List[RelatedModelEntityAssociation], None, UnsetType + ] = UNSET + """Association from which this entity is related.""" + + model_entity_related_to_entities: Union[ + List[RelatedModelEntityAssociation], None, UnsetType + ] = UNSET + """Association to which this entity is related.""" + + model_entity_attributes: Union[List[RelatedModelAttribute], None, UnsetType] = UNSET + """Individual attributes that make up the entity.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class ModelEntityNested(AssetNested): + """ModelEntity in nested API format for high-performance serialization.""" + + attributes: Union[ModelEntityAttributes, UnsetType] = UNSET + relationship_attributes: Union[ModelEntityRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + ModelEntityRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + ModelEntityRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_MODEL_ENTITY_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_versions", + "model_entity_mapped_to_entities", + "model_entity_mapped_from_entities", + "model_entity_implemented_by_assets", + "model_implemented_entities", + "model_entity_specialization_entities", + "model_entity_generalization_entity", + "model_entity_related_from_entities", + "model_entity_related_to_entities", + "model_entity_attributes", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_model_entity_attrs( + attrs: ModelEntityAttributes, obj: ModelEntity +) -> None: + """Populate ModelEntity-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.model_entity_attribute_count = obj.model_entity_attribute_count + attrs.model_entity_subject_area = obj.model_entity_subject_area + attrs.model_entity_generalization_name = obj.model_entity_generalization_name + attrs.model_entity_generalization_qualified_name = ( + obj.model_entity_generalization_qualified_name + ) + attrs.model_name = obj.model_name + attrs.model_qualified_name = obj.model_qualified_name + attrs.model_domain = obj.model_domain + attrs.model_namespace = obj.model_namespace + attrs.model_version_name = obj.model_version_name + attrs.model_version_agnostic_qualified_name = ( + obj.model_version_agnostic_qualified_name + ) + attrs.model_version_qualified_name = obj.model_version_qualified_name + attrs.model_entity_name = obj.model_entity_name + attrs.model_entity_qualified_name = obj.model_entity_qualified_name + attrs.model_type = obj.model_type + attrs.model_system_date = obj.model_system_date + attrs.model_business_date = obj.model_business_date + attrs.model_expired_at_system_date = obj.model_expired_at_system_date + attrs.model_expired_at_business_date = obj.model_expired_at_business_date + + +def _extract_model_entity_attrs(attrs: ModelEntityAttributes) -> dict: + """Extract all ModelEntity attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["model_entity_attribute_count"] = attrs.model_entity_attribute_count + result["model_entity_subject_area"] = attrs.model_entity_subject_area + result["model_entity_generalization_name"] = attrs.model_entity_generalization_name + result["model_entity_generalization_qualified_name"] = ( + attrs.model_entity_generalization_qualified_name + ) + result["model_name"] = attrs.model_name + result["model_qualified_name"] = attrs.model_qualified_name + result["model_domain"] = attrs.model_domain + result["model_namespace"] = attrs.model_namespace + result["model_version_name"] = attrs.model_version_name + result["model_version_agnostic_qualified_name"] = ( + attrs.model_version_agnostic_qualified_name + ) + result["model_version_qualified_name"] = attrs.model_version_qualified_name + result["model_entity_name"] = attrs.model_entity_name + result["model_entity_qualified_name"] = attrs.model_entity_qualified_name + result["model_type"] = attrs.model_type + result["model_system_date"] = attrs.model_system_date + result["model_business_date"] = attrs.model_business_date + result["model_expired_at_system_date"] = attrs.model_expired_at_system_date + result["model_expired_at_business_date"] = attrs.model_expired_at_business_date + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _model_entity_to_nested(model_entity: ModelEntity) -> ModelEntityNested: + """Convert flat ModelEntity to nested format.""" + attrs = ModelEntityAttributes() + _populate_model_entity_attrs(attrs, model_entity) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + model_entity, _MODEL_ENTITY_REL_FIELDS, ModelEntityRelationshipAttributes + ) + return ModelEntityNested( + guid=model_entity.guid, + type_name=model_entity.type_name, + status=model_entity.status, + version=model_entity.version, + create_time=model_entity.create_time, + update_time=model_entity.update_time, + created_by=model_entity.created_by, + updated_by=model_entity.updated_by, + classifications=model_entity.classifications, + classification_names=model_entity.classification_names, + meanings=model_entity.meanings, + labels=model_entity.labels, + business_attributes=model_entity.business_attributes, + custom_attributes=model_entity.custom_attributes, + pending_tasks=model_entity.pending_tasks, + proxy=model_entity.proxy, + is_incomplete=model_entity.is_incomplete, + provenance_type=model_entity.provenance_type, + home_id=model_entity.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _model_entity_from_nested(nested: ModelEntityNested) -> ModelEntity: + """Convert nested format to flat ModelEntity.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else ModelEntityAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _MODEL_ENTITY_REL_FIELDS, + ModelEntityRelationshipAttributes, + ) + return ModelEntity( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_model_entity_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _model_entity_to_nested_bytes(model_entity: ModelEntity, serde: Serde) -> bytes: + """Convert flat ModelEntity to nested JSON bytes.""" + return serde.encode(_model_entity_to_nested(model_entity)) + + +def _model_entity_from_nested_bytes(data: bytes, serde: Serde) -> ModelEntity: + """Convert nested JSON bytes to flat ModelEntity.""" + nested = serde.decode(data, ModelEntityNested) + return _model_entity_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +ModelEntity.MODEL_ENTITY_ATTRIBUTE_COUNT = NumericField( + "modelEntityAttributeCount", "modelEntityAttributeCount" +) +ModelEntity.MODEL_ENTITY_SUBJECT_AREA = KeywordField( + "modelEntitySubjectArea", "modelEntitySubjectArea" +) +ModelEntity.MODEL_ENTITY_GENERALIZATION_NAME = KeywordTextField( + "modelEntityGeneralizationName", + "modelEntityGeneralizationName", + "modelEntityGeneralizationName.text", +) +ModelEntity.MODEL_ENTITY_GENERALIZATION_QUALIFIED_NAME = KeywordField( + "modelEntityGeneralizationQualifiedName", "modelEntityGeneralizationQualifiedName" +) +ModelEntity.MODEL_NAME = KeywordField("modelName", "modelName") +ModelEntity.MODEL_QUALIFIED_NAME = KeywordField( + "modelQualifiedName", "modelQualifiedName" +) +ModelEntity.MODEL_DOMAIN = KeywordTextField( + "modelDomain", "modelDomain", "modelDomain.text" +) +ModelEntity.MODEL_NAMESPACE = KeywordTextField( + "modelNamespace", "modelNamespace", "modelNamespace.text" +) +ModelEntity.MODEL_VERSION_NAME = KeywordTextField( + "modelVersionName", "modelVersionName", "modelVersionName.text" +) +ModelEntity.MODEL_VERSION_AGNOSTIC_QUALIFIED_NAME = KeywordField( + "modelVersionAgnosticQualifiedName", "modelVersionAgnosticQualifiedName" +) +ModelEntity.MODEL_VERSION_QUALIFIED_NAME = KeywordField( + "modelVersionQualifiedName", "modelVersionQualifiedName" +) +ModelEntity.MODEL_ENTITY_NAME = KeywordTextField( + "modelEntityName", "modelEntityName", "modelEntityName.text" +) +ModelEntity.MODEL_ENTITY_QUALIFIED_NAME = KeywordField( + "modelEntityQualifiedName", "modelEntityQualifiedName" +) +ModelEntity.MODEL_TYPE = KeywordField("modelType", "modelType") +ModelEntity.MODEL_SYSTEM_DATE = NumericField("modelSystemDate", "modelSystemDate") +ModelEntity.MODEL_BUSINESS_DATE = NumericField("modelBusinessDate", "modelBusinessDate") +ModelEntity.MODEL_EXPIRED_AT_SYSTEM_DATE = NumericField( + "modelExpiredAtSystemDate", "modelExpiredAtSystemDate" +) +ModelEntity.MODEL_EXPIRED_AT_BUSINESS_DATE = NumericField( + "modelExpiredAtBusinessDate", "modelExpiredAtBusinessDate" +) +ModelEntity.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +ModelEntity.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +ModelEntity.ANOMALO_CHECKS = RelationField("anomaloChecks") +ModelEntity.APPLICATION = RelationField("application") +ModelEntity.APPLICATION_FIELD = RelationField("applicationField") +ModelEntity.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +ModelEntity.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +ModelEntity.MODEL_VERSIONS = RelationField("modelVersions") +ModelEntity.MODEL_ENTITY_MAPPED_TO_ENTITIES = RelationField( + "modelEntityMappedToEntities" +) +ModelEntity.MODEL_ENTITY_MAPPED_FROM_ENTITIES = RelationField( + "modelEntityMappedFromEntities" +) +ModelEntity.MODEL_ENTITY_IMPLEMENTED_BY_ASSETS = RelationField( + "modelEntityImplementedByAssets" +) +ModelEntity.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +ModelEntity.MODEL_ENTITY_SPECIALIZATION_ENTITIES = RelationField( + "modelEntitySpecializationEntities" +) +ModelEntity.MODEL_ENTITY_GENERALIZATION_ENTITY = RelationField( + "modelEntityGeneralizationEntity" +) +ModelEntity.MODEL_ENTITY_RELATED_FROM_ENTITIES = RelationField( + "modelEntityRelatedFromEntities" +) +ModelEntity.MODEL_ENTITY_RELATED_TO_ENTITIES = RelationField( + "modelEntityRelatedToEntities" +) +ModelEntity.MODEL_ENTITY_ATTRIBUTES = RelationField("modelEntityAttributes") +ModelEntity.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +ModelEntity.METRICS = RelationField("metrics") +ModelEntity.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +ModelEntity.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +ModelEntity.MEANINGS = RelationField("meanings") +ModelEntity.MC_MONITORS = RelationField("mcMonitors") +ModelEntity.MC_INCIDENTS = RelationField("mcIncidents") +ModelEntity.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +ModelEntity.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +ModelEntity.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +ModelEntity.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +ModelEntity.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +ModelEntity.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +ModelEntity.FILES = RelationField("files") +ModelEntity.LINKS = RelationField("links") +ModelEntity.README = RelationField("readme") +ModelEntity.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +ModelEntity.SODA_CHECKS = RelationField("sodaChecks") +ModelEntity.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +ModelEntity.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/model_entity_association.py b/pyatlan_v9/model/assets/model_entity_association.py new file mode 100644 index 000000000..919edd0ae --- /dev/null +++ b/pyatlan_v9/model/assets/model_entity_association.py @@ -0,0 +1,917 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +ModelEntityAssociation asset model with flattened inheritance. + +This module provides: +- ModelEntityAssociation: Flat asset class (easy to use) +- ModelEntityAssociationAttributes: Nested attributes struct (extends AssetAttributes) +- ModelEntityAssociationNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .model_related import RelatedModelAttribute, RelatedModelEntity + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class ModelEntityAssociation(Asset): + """ + Instance of a data entity association in Atlan. + """ + + MODEL_ENTITY_ASSOCIATION_CARDINALITY: ClassVar[Any] = None + MODEL_ENTITY_ASSOCIATION_LABEL: ClassVar[Any] = None + MODEL_ENTITY_ASSOCIATION_TO_QUALIFIED_NAME: ClassVar[Any] = None + MODEL_ENTITY_ASSOCIATION_TO_LABEL: ClassVar[Any] = None + MODEL_ENTITY_ASSOCIATION_TO_MIN_CARDINALITY: ClassVar[Any] = None + MODEL_ENTITY_ASSOCIATION_TO_MAX_CARDINALITY: ClassVar[Any] = None + MODEL_ENTITY_ASSOCIATION_FROM_QUALIFIED_NAME: ClassVar[Any] = None + MODEL_ENTITY_ASSOCIATION_FROM_LABEL: ClassVar[Any] = None + MODEL_ENTITY_ASSOCIATION_FROM_MIN_CARDINALITY: ClassVar[Any] = None + MODEL_ENTITY_ASSOCIATION_FROM_MAX_CARDINALITY: ClassVar[Any] = None + MODEL_NAME: ClassVar[Any] = None + MODEL_QUALIFIED_NAME: ClassVar[Any] = None + MODEL_DOMAIN: ClassVar[Any] = None + MODEL_NAMESPACE: ClassVar[Any] = None + MODEL_VERSION_NAME: ClassVar[Any] = None + MODEL_VERSION_AGNOSTIC_QUALIFIED_NAME: ClassVar[Any] = None + MODEL_VERSION_QUALIFIED_NAME: ClassVar[Any] = None + MODEL_ENTITY_NAME: ClassVar[Any] = None + MODEL_ENTITY_QUALIFIED_NAME: ClassVar[Any] = None + MODEL_TYPE: ClassVar[Any] = None + MODEL_SYSTEM_DATE: ClassVar[Any] = None + MODEL_BUSINESS_DATE: ClassVar[Any] = None + MODEL_EXPIRED_AT_SYSTEM_DATE: ClassVar[Any] = None + MODEL_EXPIRED_AT_BUSINESS_DATE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_ENTITY_ASSOCIATION_TO: ClassVar[Any] = None + MODEL_ENTITY_ASSOCIATION_FROM: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "ModelEntityAssociation" + + model_entity_association_cardinality: Union[str, None, UnsetType] = UNSET + """(Deprecated) Cardinality of the data entity association.""" + + model_entity_association_label: Union[str, None, UnsetType] = UNSET + """(Deprecated) Label of the data entity association.""" + + model_entity_association_to_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the association to which this entity is related.""" + + model_entity_association_to_label: Union[str, None, UnsetType] = UNSET + """Label when read from the association to which this entity is related.""" + + model_entity_association_to_min_cardinality: Union[int, None, UnsetType] = UNSET + """Minimum cardinality of the data entity to which the association exists.""" + + model_entity_association_to_max_cardinality: Union[int, None, UnsetType] = UNSET + """Maximum cardinality of the data entity to which the association exists.""" + + model_entity_association_from_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the association from which this entity is related.""" + + model_entity_association_from_label: Union[str, None, UnsetType] = UNSET + """Label when read from the association from which this entity is related.""" + + model_entity_association_from_min_cardinality: Union[int, None, UnsetType] = UNSET + """Minimum cardinality of the data entity from which the association exists.""" + + model_entity_association_from_max_cardinality: Union[int, None, UnsetType] = UNSET + """Maximum cardinality of the data entity from which the association exists.""" + + model_name: Union[str, None, UnsetType] = UNSET + """Simple name of the model in which this asset exists, or empty if it is itself a data model.""" + + model_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the model in which this asset exists, or empty if it is itself a data model.""" + + model_domain: Union[str, None, UnsetType] = UNSET + """Model domain in which this asset exists.""" + + model_namespace: Union[str, None, UnsetType] = UNSET + """Model namespace in which this asset exists.""" + + model_version_name: Union[str, None, UnsetType] = UNSET + """Simple name of the version in which this asset exists, or empty if it is itself a data model version.""" + + model_version_agnostic_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the parent in which this asset exists, irrespective of the version (always implies the latest version).""" + + model_version_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the version in which this asset exists, or empty if it is itself a data model version.""" + + model_entity_name: Union[str, None, UnsetType] = UNSET + """Simple name of the entity in which this asset exists, or empty if it is itself a data model entity.""" + + model_entity_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the entity in which this asset exists, or empty if it is itself a data model entity.""" + + model_type: Union[str, None, UnsetType] = UNSET + """Type of the model asset (conceptual, logical, physical).""" + + model_system_date: Union[int, None, UnsetType] = UNSET + """System date for the asset.""" + + model_business_date: Union[int, None, UnsetType] = UNSET + """Business date for the asset.""" + + model_expired_at_system_date: Union[int, None, UnsetType] = UNSET + """System expiration date for the asset.""" + + model_expired_at_business_date: Union[int, None, UnsetType] = UNSET + """Business expiration date for the asset.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_entity_association_to: Union[RelatedModelEntity, None, UnsetType] = UNSET + """Entity to which this association is related.""" + + model_entity_association_from: Union[RelatedModelEntity, None, UnsetType] = UNSET + """Entity from which this association is related.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "ModelEntityAssociation" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _model_entity_association_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> ModelEntityAssociation: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + ModelEntityAssociation instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _model_entity_association_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class ModelEntityAssociationAttributes(AssetAttributes): + """ModelEntityAssociation-specific attributes for nested API format.""" + + model_entity_association_cardinality: Union[str, None, UnsetType] = UNSET + """(Deprecated) Cardinality of the data entity association.""" + + model_entity_association_label: Union[str, None, UnsetType] = UNSET + """(Deprecated) Label of the data entity association.""" + + model_entity_association_to_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the association to which this entity is related.""" + + model_entity_association_to_label: Union[str, None, UnsetType] = UNSET + """Label when read from the association to which this entity is related.""" + + model_entity_association_to_min_cardinality: Union[int, None, UnsetType] = UNSET + """Minimum cardinality of the data entity to which the association exists.""" + + model_entity_association_to_max_cardinality: Union[int, None, UnsetType] = UNSET + """Maximum cardinality of the data entity to which the association exists.""" + + model_entity_association_from_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the association from which this entity is related.""" + + model_entity_association_from_label: Union[str, None, UnsetType] = UNSET + """Label when read from the association from which this entity is related.""" + + model_entity_association_from_min_cardinality: Union[int, None, UnsetType] = UNSET + """Minimum cardinality of the data entity from which the association exists.""" + + model_entity_association_from_max_cardinality: Union[int, None, UnsetType] = UNSET + """Maximum cardinality of the data entity from which the association exists.""" + + model_name: Union[str, None, UnsetType] = UNSET + """Simple name of the model in which this asset exists, or empty if it is itself a data model.""" + + model_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the model in which this asset exists, or empty if it is itself a data model.""" + + model_domain: Union[str, None, UnsetType] = UNSET + """Model domain in which this asset exists.""" + + model_namespace: Union[str, None, UnsetType] = UNSET + """Model namespace in which this asset exists.""" + + model_version_name: Union[str, None, UnsetType] = UNSET + """Simple name of the version in which this asset exists, or empty if it is itself a data model version.""" + + model_version_agnostic_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the parent in which this asset exists, irrespective of the version (always implies the latest version).""" + + model_version_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the version in which this asset exists, or empty if it is itself a data model version.""" + + model_entity_name: Union[str, None, UnsetType] = UNSET + """Simple name of the entity in which this asset exists, or empty if it is itself a data model entity.""" + + model_entity_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the entity in which this asset exists, or empty if it is itself a data model entity.""" + + model_type: Union[str, None, UnsetType] = UNSET + """Type of the model asset (conceptual, logical, physical).""" + + model_system_date: Union[int, None, UnsetType] = UNSET + """System date for the asset.""" + + model_business_date: Union[int, None, UnsetType] = UNSET + """Business date for the asset.""" + + model_expired_at_system_date: Union[int, None, UnsetType] = UNSET + """System expiration date for the asset.""" + + model_expired_at_business_date: Union[int, None, UnsetType] = UNSET + """Business expiration date for the asset.""" + + +class ModelEntityAssociationRelationshipAttributes(AssetRelationshipAttributes): + """ModelEntityAssociation-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_entity_association_to: Union[RelatedModelEntity, None, UnsetType] = UNSET + """Entity to which this association is related.""" + + model_entity_association_from: Union[RelatedModelEntity, None, UnsetType] = UNSET + """Entity from which this association is related.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class ModelEntityAssociationNested(AssetNested): + """ModelEntityAssociation in nested API format for high-performance serialization.""" + + attributes: Union[ModelEntityAssociationAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + ModelEntityAssociationRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + ModelEntityAssociationRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + ModelEntityAssociationRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_MODEL_ENTITY_ASSOCIATION_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_entity_association_to", + "model_entity_association_from", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_model_entity_association_attrs( + attrs: ModelEntityAssociationAttributes, obj: ModelEntityAssociation +) -> None: + """Populate ModelEntityAssociation-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.model_entity_association_cardinality = ( + obj.model_entity_association_cardinality + ) + attrs.model_entity_association_label = obj.model_entity_association_label + attrs.model_entity_association_to_qualified_name = ( + obj.model_entity_association_to_qualified_name + ) + attrs.model_entity_association_to_label = obj.model_entity_association_to_label + attrs.model_entity_association_to_min_cardinality = ( + obj.model_entity_association_to_min_cardinality + ) + attrs.model_entity_association_to_max_cardinality = ( + obj.model_entity_association_to_max_cardinality + ) + attrs.model_entity_association_from_qualified_name = ( + obj.model_entity_association_from_qualified_name + ) + attrs.model_entity_association_from_label = obj.model_entity_association_from_label + attrs.model_entity_association_from_min_cardinality = ( + obj.model_entity_association_from_min_cardinality + ) + attrs.model_entity_association_from_max_cardinality = ( + obj.model_entity_association_from_max_cardinality + ) + attrs.model_name = obj.model_name + attrs.model_qualified_name = obj.model_qualified_name + attrs.model_domain = obj.model_domain + attrs.model_namespace = obj.model_namespace + attrs.model_version_name = obj.model_version_name + attrs.model_version_agnostic_qualified_name = ( + obj.model_version_agnostic_qualified_name + ) + attrs.model_version_qualified_name = obj.model_version_qualified_name + attrs.model_entity_name = obj.model_entity_name + attrs.model_entity_qualified_name = obj.model_entity_qualified_name + attrs.model_type = obj.model_type + attrs.model_system_date = obj.model_system_date + attrs.model_business_date = obj.model_business_date + attrs.model_expired_at_system_date = obj.model_expired_at_system_date + attrs.model_expired_at_business_date = obj.model_expired_at_business_date + + +def _extract_model_entity_association_attrs( + attrs: ModelEntityAssociationAttributes, +) -> dict: + """Extract all ModelEntityAssociation attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["model_entity_association_cardinality"] = ( + attrs.model_entity_association_cardinality + ) + result["model_entity_association_label"] = attrs.model_entity_association_label + result["model_entity_association_to_qualified_name"] = ( + attrs.model_entity_association_to_qualified_name + ) + result["model_entity_association_to_label"] = ( + attrs.model_entity_association_to_label + ) + result["model_entity_association_to_min_cardinality"] = ( + attrs.model_entity_association_to_min_cardinality + ) + result["model_entity_association_to_max_cardinality"] = ( + attrs.model_entity_association_to_max_cardinality + ) + result["model_entity_association_from_qualified_name"] = ( + attrs.model_entity_association_from_qualified_name + ) + result["model_entity_association_from_label"] = ( + attrs.model_entity_association_from_label + ) + result["model_entity_association_from_min_cardinality"] = ( + attrs.model_entity_association_from_min_cardinality + ) + result["model_entity_association_from_max_cardinality"] = ( + attrs.model_entity_association_from_max_cardinality + ) + result["model_name"] = attrs.model_name + result["model_qualified_name"] = attrs.model_qualified_name + result["model_domain"] = attrs.model_domain + result["model_namespace"] = attrs.model_namespace + result["model_version_name"] = attrs.model_version_name + result["model_version_agnostic_qualified_name"] = ( + attrs.model_version_agnostic_qualified_name + ) + result["model_version_qualified_name"] = attrs.model_version_qualified_name + result["model_entity_name"] = attrs.model_entity_name + result["model_entity_qualified_name"] = attrs.model_entity_qualified_name + result["model_type"] = attrs.model_type + result["model_system_date"] = attrs.model_system_date + result["model_business_date"] = attrs.model_business_date + result["model_expired_at_system_date"] = attrs.model_expired_at_system_date + result["model_expired_at_business_date"] = attrs.model_expired_at_business_date + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _model_entity_association_to_nested( + model_entity_association: ModelEntityAssociation, +) -> ModelEntityAssociationNested: + """Convert flat ModelEntityAssociation to nested format.""" + attrs = ModelEntityAssociationAttributes() + _populate_model_entity_association_attrs(attrs, model_entity_association) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + model_entity_association, + _MODEL_ENTITY_ASSOCIATION_REL_FIELDS, + ModelEntityAssociationRelationshipAttributes, + ) + return ModelEntityAssociationNested( + guid=model_entity_association.guid, + type_name=model_entity_association.type_name, + status=model_entity_association.status, + version=model_entity_association.version, + create_time=model_entity_association.create_time, + update_time=model_entity_association.update_time, + created_by=model_entity_association.created_by, + updated_by=model_entity_association.updated_by, + classifications=model_entity_association.classifications, + classification_names=model_entity_association.classification_names, + meanings=model_entity_association.meanings, + labels=model_entity_association.labels, + business_attributes=model_entity_association.business_attributes, + custom_attributes=model_entity_association.custom_attributes, + pending_tasks=model_entity_association.pending_tasks, + proxy=model_entity_association.proxy, + is_incomplete=model_entity_association.is_incomplete, + provenance_type=model_entity_association.provenance_type, + home_id=model_entity_association.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _model_entity_association_from_nested( + nested: ModelEntityAssociationNested, +) -> ModelEntityAssociation: + """Convert nested format to flat ModelEntityAssociation.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else ModelEntityAssociationAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _MODEL_ENTITY_ASSOCIATION_REL_FIELDS, + ModelEntityAssociationRelationshipAttributes, + ) + return ModelEntityAssociation( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_model_entity_association_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _model_entity_association_to_nested_bytes( + model_entity_association: ModelEntityAssociation, serde: Serde +) -> bytes: + """Convert flat ModelEntityAssociation to nested JSON bytes.""" + return serde.encode(_model_entity_association_to_nested(model_entity_association)) + + +def _model_entity_association_from_nested_bytes( + data: bytes, serde: Serde +) -> ModelEntityAssociation: + """Convert nested JSON bytes to flat ModelEntityAssociation.""" + nested = serde.decode(data, ModelEntityAssociationNested) + return _model_entity_association_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +ModelEntityAssociation.MODEL_ENTITY_ASSOCIATION_CARDINALITY = KeywordField( + "modelEntityAssociationCardinality", "modelEntityAssociationCardinality" +) +ModelEntityAssociation.MODEL_ENTITY_ASSOCIATION_LABEL = KeywordField( + "modelEntityAssociationLabel", "modelEntityAssociationLabel" +) +ModelEntityAssociation.MODEL_ENTITY_ASSOCIATION_TO_QUALIFIED_NAME = KeywordField( + "modelEntityAssociationToQualifiedName", "modelEntityAssociationToQualifiedName" +) +ModelEntityAssociation.MODEL_ENTITY_ASSOCIATION_TO_LABEL = KeywordField( + "modelEntityAssociationToLabel", "modelEntityAssociationToLabel" +) +ModelEntityAssociation.MODEL_ENTITY_ASSOCIATION_TO_MIN_CARDINALITY = NumericField( + "modelEntityAssociationToMinCardinality", "modelEntityAssociationToMinCardinality" +) +ModelEntityAssociation.MODEL_ENTITY_ASSOCIATION_TO_MAX_CARDINALITY = NumericField( + "modelEntityAssociationToMaxCardinality", "modelEntityAssociationToMaxCardinality" +) +ModelEntityAssociation.MODEL_ENTITY_ASSOCIATION_FROM_QUALIFIED_NAME = KeywordField( + "modelEntityAssociationFromQualifiedName", "modelEntityAssociationFromQualifiedName" +) +ModelEntityAssociation.MODEL_ENTITY_ASSOCIATION_FROM_LABEL = KeywordField( + "modelEntityAssociationFromLabel", "modelEntityAssociationFromLabel" +) +ModelEntityAssociation.MODEL_ENTITY_ASSOCIATION_FROM_MIN_CARDINALITY = NumericField( + "modelEntityAssociationFromMinCardinality", + "modelEntityAssociationFromMinCardinality", +) +ModelEntityAssociation.MODEL_ENTITY_ASSOCIATION_FROM_MAX_CARDINALITY = NumericField( + "modelEntityAssociationFromMaxCardinality", + "modelEntityAssociationFromMaxCardinality", +) +ModelEntityAssociation.MODEL_NAME = KeywordField("modelName", "modelName") +ModelEntityAssociation.MODEL_QUALIFIED_NAME = KeywordField( + "modelQualifiedName", "modelQualifiedName" +) +ModelEntityAssociation.MODEL_DOMAIN = KeywordTextField( + "modelDomain", "modelDomain", "modelDomain.text" +) +ModelEntityAssociation.MODEL_NAMESPACE = KeywordTextField( + "modelNamespace", "modelNamespace", "modelNamespace.text" +) +ModelEntityAssociation.MODEL_VERSION_NAME = KeywordTextField( + "modelVersionName", "modelVersionName", "modelVersionName.text" +) +ModelEntityAssociation.MODEL_VERSION_AGNOSTIC_QUALIFIED_NAME = KeywordField( + "modelVersionAgnosticQualifiedName", "modelVersionAgnosticQualifiedName" +) +ModelEntityAssociation.MODEL_VERSION_QUALIFIED_NAME = KeywordField( + "modelVersionQualifiedName", "modelVersionQualifiedName" +) +ModelEntityAssociation.MODEL_ENTITY_NAME = KeywordTextField( + "modelEntityName", "modelEntityName", "modelEntityName.text" +) +ModelEntityAssociation.MODEL_ENTITY_QUALIFIED_NAME = KeywordField( + "modelEntityQualifiedName", "modelEntityQualifiedName" +) +ModelEntityAssociation.MODEL_TYPE = KeywordField("modelType", "modelType") +ModelEntityAssociation.MODEL_SYSTEM_DATE = NumericField( + "modelSystemDate", "modelSystemDate" +) +ModelEntityAssociation.MODEL_BUSINESS_DATE = NumericField( + "modelBusinessDate", "modelBusinessDate" +) +ModelEntityAssociation.MODEL_EXPIRED_AT_SYSTEM_DATE = NumericField( + "modelExpiredAtSystemDate", "modelExpiredAtSystemDate" +) +ModelEntityAssociation.MODEL_EXPIRED_AT_BUSINESS_DATE = NumericField( + "modelExpiredAtBusinessDate", "modelExpiredAtBusinessDate" +) +ModelEntityAssociation.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +ModelEntityAssociation.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +ModelEntityAssociation.ANOMALO_CHECKS = RelationField("anomaloChecks") +ModelEntityAssociation.APPLICATION = RelationField("application") +ModelEntityAssociation.APPLICATION_FIELD = RelationField("applicationField") +ModelEntityAssociation.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +ModelEntityAssociation.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +ModelEntityAssociation.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +ModelEntityAssociation.MODEL_ENTITY_ASSOCIATION_TO = RelationField( + "modelEntityAssociationTo" +) +ModelEntityAssociation.MODEL_ENTITY_ASSOCIATION_FROM = RelationField( + "modelEntityAssociationFrom" +) +ModelEntityAssociation.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +ModelEntityAssociation.METRICS = RelationField("metrics") +ModelEntityAssociation.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +ModelEntityAssociation.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +ModelEntityAssociation.MEANINGS = RelationField("meanings") +ModelEntityAssociation.MC_MONITORS = RelationField("mcMonitors") +ModelEntityAssociation.MC_INCIDENTS = RelationField("mcIncidents") +ModelEntityAssociation.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +ModelEntityAssociation.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +ModelEntityAssociation.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +ModelEntityAssociation.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +ModelEntityAssociation.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +ModelEntityAssociation.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +ModelEntityAssociation.FILES = RelationField("files") +ModelEntityAssociation.LINKS = RelationField("links") +ModelEntityAssociation.README = RelationField("readme") +ModelEntityAssociation.SCHEMA_REGISTRY_SUBJECTS = RelationField( + "schemaRegistrySubjects" +) +ModelEntityAssociation.SODA_CHECKS = RelationField("sodaChecks") +ModelEntityAssociation.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +ModelEntityAssociation.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/model_related.py b/pyatlan_v9/model/assets/model_related.py new file mode 100644 index 000000000..a90793609 --- /dev/null +++ b/pyatlan_v9/model/assets/model_related.py @@ -0,0 +1,260 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Model module. + +This module contains all Related{Type} classes for the Model type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Union + +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedCatalog +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedModel", + "RelatedModelDataModel", + "RelatedModelVersion", + "RelatedModelEntity", + "RelatedModelEntityAssociation", + "RelatedModelAttribute", + "RelatedModelAttributeAssociation", +] + + +class RelatedModel(RelatedCatalog): + """ + Related entity reference for Model assets. + + Extends RelatedCatalog with Model-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Model" so it serializes correctly + + model_name: Union[str, None, UnsetType] = UNSET + """Simple name of the model in which this asset exists, or empty if it is itself a data model.""" + + model_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the model in which this asset exists, or empty if it is itself a data model.""" + + model_domain: Union[str, None, UnsetType] = UNSET + """Model domain in which this asset exists.""" + + model_namespace: Union[str, None, UnsetType] = UNSET + """Model namespace in which this asset exists.""" + + model_version_name: Union[str, None, UnsetType] = UNSET + """Simple name of the version in which this asset exists, or empty if it is itself a data model version.""" + + model_version_agnostic_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the parent in which this asset exists, irrespective of the version (always implies the latest version).""" + + model_version_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the version in which this asset exists, or empty if it is itself a data model version.""" + + model_entity_name: Union[str, None, UnsetType] = UNSET + """Simple name of the entity in which this asset exists, or empty if it is itself a data model entity.""" + + model_entity_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the entity in which this asset exists, or empty if it is itself a data model entity.""" + + model_type: Union[str, None, UnsetType] = UNSET + """Type of the model asset (conceptual, logical, physical).""" + + model_system_date: Union[int, None, UnsetType] = UNSET + """System date for the asset.""" + + model_business_date: Union[int, None, UnsetType] = UNSET + """Business date for the asset.""" + + model_expired_at_system_date: Union[int, None, UnsetType] = UNSET + """System expiration date for the asset.""" + + model_expired_at_business_date: Union[int, None, UnsetType] = UNSET + """Business expiration date for the asset.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Model" + + +class RelatedModelDataModel(RelatedModel): + """ + Related entity reference for ModelDataModel assets. + + Extends RelatedModel with ModelDataModel-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "ModelDataModel" so it serializes correctly + + model_version_count: Union[int, None, UnsetType] = UNSET + """Number of versions of the data model.""" + + model_tool: Union[str, None, UnsetType] = UNSET + """Tool used to create this data model.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "ModelDataModel" + + +class RelatedModelVersion(RelatedModel): + """ + Related entity reference for ModelVersion assets. + + Extends RelatedModel with ModelVersion-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "ModelVersion" so it serializes correctly + + model_version_entity_count: Union[int, None, UnsetType] = UNSET + """Number of entities in the version.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "ModelVersion" + + +class RelatedModelEntity(RelatedModel): + """ + Related entity reference for ModelEntity assets. + + Extends RelatedModel with ModelEntity-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "ModelEntity" so it serializes correctly + + model_entity_attribute_count: Union[int, None, UnsetType] = UNSET + """Number of attributes in the entity.""" + + model_entity_subject_area: Union[str, None, UnsetType] = UNSET + """Subject area of the entity.""" + + model_entity_generalization_name: Union[str, None, UnsetType] = UNSET + """Name of the general entity.""" + + model_entity_generalization_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique identifier for the general entity.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "ModelEntity" + + +class RelatedModelEntityAssociation(RelatedModel): + """ + Related entity reference for ModelEntityAssociation assets. + + Extends RelatedModel with ModelEntityAssociation-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "ModelEntityAssociation" so it serializes correctly + + model_entity_association_cardinality: Union[str, None, UnsetType] = UNSET + """(Deprecated) Cardinality of the data entity association.""" + + model_entity_association_label: Union[str, None, UnsetType] = UNSET + """(Deprecated) Label of the data entity association.""" + + model_entity_association_to_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the association to which this entity is related.""" + + model_entity_association_to_label: Union[str, None, UnsetType] = UNSET + """Label when read from the association to which this entity is related.""" + + model_entity_association_to_min_cardinality: Union[int, None, UnsetType] = UNSET + """Minimum cardinality of the data entity to which the association exists.""" + + model_entity_association_to_max_cardinality: Union[int, None, UnsetType] = UNSET + """Maximum cardinality of the data entity to which the association exists.""" + + model_entity_association_from_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the association from which this entity is related.""" + + model_entity_association_from_label: Union[str, None, UnsetType] = UNSET + """Label when read from the association from which this entity is related.""" + + model_entity_association_from_min_cardinality: Union[int, None, UnsetType] = UNSET + """Minimum cardinality of the data entity from which the association exists.""" + + model_entity_association_from_max_cardinality: Union[int, None, UnsetType] = UNSET + """Maximum cardinality of the data entity from which the association exists.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "ModelEntityAssociation" + + +class RelatedModelAttribute(RelatedModel): + """ + Related entity reference for ModelAttribute assets. + + Extends RelatedModel with ModelAttribute-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "ModelAttribute" so it serializes correctly + + model_attribute_is_nullable: Union[bool, None, UnsetType] = UNSET + """When true, the values in this attribute can be null.""" + + model_attribute_is_primary: Union[bool, None, UnsetType] = UNSET + """When true, this attribute forms the primary key for the entity.""" + + model_attribute_is_foreign: Union[bool, None, UnsetType] = UNSET + """When true, this attribute is a foreign key to another entity.""" + + model_attribute_is_derived: Union[bool, None, UnsetType] = UNSET + """When true, the values in this attribute are derived data.""" + + model_attribute_precision: Union[int, None, UnsetType] = UNSET + """Precision of the attribute.""" + + model_attribute_scale: Union[int, None, UnsetType] = UNSET + """Scale of the attribute.""" + + model_attribute_data_type: Union[str, None, UnsetType] = UNSET + """Type of the attribute.""" + + model_attribute_has_relationships: Union[bool, None, UnsetType] = UNSET + """When true, this attribute has relationships with other attributes.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "ModelAttribute" + + +class RelatedModelAttributeAssociation(RelatedModel): + """ + Related entity reference for ModelAttributeAssociation assets. + + Extends RelatedModel with ModelAttributeAssociation-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "ModelAttributeAssociation" so it serializes correctly + + model_attribute_association_to_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the association to which this attribute is related.""" + + model_attribute_association_from_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the association from which this attribute is related.""" + + model_entity_association_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the entity association to which this attribute is related.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "ModelAttributeAssociation" diff --git a/pyatlan_v9/model/assets/model_version.py b/pyatlan_v9/model/assets/model_version.py new file mode 100644 index 000000000..d7d9daeea --- /dev/null +++ b/pyatlan_v9/model/assets/model_version.py @@ -0,0 +1,743 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +ModelVersion asset model with flattened inheritance. + +This module provides: +- ModelVersion: Flat asset class (easy to use) +- ModelVersionAttributes: Nested attributes struct (extends AssetAttributes) +- ModelVersionNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .model_related import ( + RelatedModelAttribute, + RelatedModelDataModel, + RelatedModelEntity, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class ModelVersion(Asset): + """ + Instance of a version of a data model in Atlan. + """ + + MODEL_VERSION_ENTITY_COUNT: ClassVar[Any] = None + MODEL_NAME: ClassVar[Any] = None + MODEL_QUALIFIED_NAME: ClassVar[Any] = None + MODEL_DOMAIN: ClassVar[Any] = None + MODEL_NAMESPACE: ClassVar[Any] = None + MODEL_VERSION_NAME: ClassVar[Any] = None + MODEL_VERSION_AGNOSTIC_QUALIFIED_NAME: ClassVar[Any] = None + MODEL_VERSION_QUALIFIED_NAME: ClassVar[Any] = None + MODEL_ENTITY_NAME: ClassVar[Any] = None + MODEL_ENTITY_QUALIFIED_NAME: ClassVar[Any] = None + MODEL_TYPE: ClassVar[Any] = None + MODEL_SYSTEM_DATE: ClassVar[Any] = None + MODEL_BUSINESS_DATE: ClassVar[Any] = None + MODEL_EXPIRED_AT_SYSTEM_DATE: ClassVar[Any] = None + MODEL_EXPIRED_AT_BUSINESS_DATE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_DATA_MODEL: ClassVar[Any] = None + MODEL_VERSION_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "ModelVersion" + + model_version_entity_count: Union[int, None, UnsetType] = UNSET + """Number of entities in the version.""" + + model_name: Union[str, None, UnsetType] = UNSET + """Simple name of the model in which this asset exists, or empty if it is itself a data model.""" + + model_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the model in which this asset exists, or empty if it is itself a data model.""" + + model_domain: Union[str, None, UnsetType] = UNSET + """Model domain in which this asset exists.""" + + model_namespace: Union[str, None, UnsetType] = UNSET + """Model namespace in which this asset exists.""" + + model_version_name: Union[str, None, UnsetType] = UNSET + """Simple name of the version in which this asset exists, or empty if it is itself a data model version.""" + + model_version_agnostic_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the parent in which this asset exists, irrespective of the version (always implies the latest version).""" + + model_version_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the version in which this asset exists, or empty if it is itself a data model version.""" + + model_entity_name: Union[str, None, UnsetType] = UNSET + """Simple name of the entity in which this asset exists, or empty if it is itself a data model entity.""" + + model_entity_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the entity in which this asset exists, or empty if it is itself a data model entity.""" + + model_type: Union[str, None, UnsetType] = UNSET + """Type of the model asset (conceptual, logical, physical).""" + + model_system_date: Union[int, None, UnsetType] = UNSET + """System date for the asset.""" + + model_business_date: Union[int, None, UnsetType] = UNSET + """Business date for the asset.""" + + model_expired_at_system_date: Union[int, None, UnsetType] = UNSET + """System expiration date for the asset.""" + + model_expired_at_business_date: Union[int, None, UnsetType] = UNSET + """Business expiration date for the asset.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_data_model: Union[RelatedModelDataModel, None, UnsetType] = UNSET + """Data model for which this version exists.""" + + model_version_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Individual entities that make up this version of the data model.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "ModelVersion" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _model_version_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> ModelVersion: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + ModelVersion instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _model_version_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class ModelVersionAttributes(AssetAttributes): + """ModelVersion-specific attributes for nested API format.""" + + model_version_entity_count: Union[int, None, UnsetType] = UNSET + """Number of entities in the version.""" + + model_name: Union[str, None, UnsetType] = UNSET + """Simple name of the model in which this asset exists, or empty if it is itself a data model.""" + + model_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the model in which this asset exists, or empty if it is itself a data model.""" + + model_domain: Union[str, None, UnsetType] = UNSET + """Model domain in which this asset exists.""" + + model_namespace: Union[str, None, UnsetType] = UNSET + """Model namespace in which this asset exists.""" + + model_version_name: Union[str, None, UnsetType] = UNSET + """Simple name of the version in which this asset exists, or empty if it is itself a data model version.""" + + model_version_agnostic_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the parent in which this asset exists, irrespective of the version (always implies the latest version).""" + + model_version_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the version in which this asset exists, or empty if it is itself a data model version.""" + + model_entity_name: Union[str, None, UnsetType] = UNSET + """Simple name of the entity in which this asset exists, or empty if it is itself a data model entity.""" + + model_entity_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the entity in which this asset exists, or empty if it is itself a data model entity.""" + + model_type: Union[str, None, UnsetType] = UNSET + """Type of the model asset (conceptual, logical, physical).""" + + model_system_date: Union[int, None, UnsetType] = UNSET + """System date for the asset.""" + + model_business_date: Union[int, None, UnsetType] = UNSET + """Business date for the asset.""" + + model_expired_at_system_date: Union[int, None, UnsetType] = UNSET + """System expiration date for the asset.""" + + model_expired_at_business_date: Union[int, None, UnsetType] = UNSET + """Business expiration date for the asset.""" + + +class ModelVersionRelationshipAttributes(AssetRelationshipAttributes): + """ModelVersion-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_data_model: Union[RelatedModelDataModel, None, UnsetType] = UNSET + """Data model for which this version exists.""" + + model_version_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Individual entities that make up this version of the data model.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class ModelVersionNested(AssetNested): + """ModelVersion in nested API format for high-performance serialization.""" + + attributes: Union[ModelVersionAttributes, UnsetType] = UNSET + relationship_attributes: Union[ModelVersionRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + ModelVersionRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + ModelVersionRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_MODEL_VERSION_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_data_model", + "model_version_entities", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_model_version_attrs( + attrs: ModelVersionAttributes, obj: ModelVersion +) -> None: + """Populate ModelVersion-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.model_version_entity_count = obj.model_version_entity_count + attrs.model_name = obj.model_name + attrs.model_qualified_name = obj.model_qualified_name + attrs.model_domain = obj.model_domain + attrs.model_namespace = obj.model_namespace + attrs.model_version_name = obj.model_version_name + attrs.model_version_agnostic_qualified_name = ( + obj.model_version_agnostic_qualified_name + ) + attrs.model_version_qualified_name = obj.model_version_qualified_name + attrs.model_entity_name = obj.model_entity_name + attrs.model_entity_qualified_name = obj.model_entity_qualified_name + attrs.model_type = obj.model_type + attrs.model_system_date = obj.model_system_date + attrs.model_business_date = obj.model_business_date + attrs.model_expired_at_system_date = obj.model_expired_at_system_date + attrs.model_expired_at_business_date = obj.model_expired_at_business_date + + +def _extract_model_version_attrs(attrs: ModelVersionAttributes) -> dict: + """Extract all ModelVersion attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["model_version_entity_count"] = attrs.model_version_entity_count + result["model_name"] = attrs.model_name + result["model_qualified_name"] = attrs.model_qualified_name + result["model_domain"] = attrs.model_domain + result["model_namespace"] = attrs.model_namespace + result["model_version_name"] = attrs.model_version_name + result["model_version_agnostic_qualified_name"] = ( + attrs.model_version_agnostic_qualified_name + ) + result["model_version_qualified_name"] = attrs.model_version_qualified_name + result["model_entity_name"] = attrs.model_entity_name + result["model_entity_qualified_name"] = attrs.model_entity_qualified_name + result["model_type"] = attrs.model_type + result["model_system_date"] = attrs.model_system_date + result["model_business_date"] = attrs.model_business_date + result["model_expired_at_system_date"] = attrs.model_expired_at_system_date + result["model_expired_at_business_date"] = attrs.model_expired_at_business_date + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _model_version_to_nested(model_version: ModelVersion) -> ModelVersionNested: + """Convert flat ModelVersion to nested format.""" + attrs = ModelVersionAttributes() + _populate_model_version_attrs(attrs, model_version) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + model_version, _MODEL_VERSION_REL_FIELDS, ModelVersionRelationshipAttributes + ) + return ModelVersionNested( + guid=model_version.guid, + type_name=model_version.type_name, + status=model_version.status, + version=model_version.version, + create_time=model_version.create_time, + update_time=model_version.update_time, + created_by=model_version.created_by, + updated_by=model_version.updated_by, + classifications=model_version.classifications, + classification_names=model_version.classification_names, + meanings=model_version.meanings, + labels=model_version.labels, + business_attributes=model_version.business_attributes, + custom_attributes=model_version.custom_attributes, + pending_tasks=model_version.pending_tasks, + proxy=model_version.proxy, + is_incomplete=model_version.is_incomplete, + provenance_type=model_version.provenance_type, + home_id=model_version.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _model_version_from_nested(nested: ModelVersionNested) -> ModelVersion: + """Convert nested format to flat ModelVersion.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else ModelVersionAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _MODEL_VERSION_REL_FIELDS, + ModelVersionRelationshipAttributes, + ) + return ModelVersion( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_model_version_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _model_version_to_nested_bytes(model_version: ModelVersion, serde: Serde) -> bytes: + """Convert flat ModelVersion to nested JSON bytes.""" + return serde.encode(_model_version_to_nested(model_version)) + + +def _model_version_from_nested_bytes(data: bytes, serde: Serde) -> ModelVersion: + """Convert nested JSON bytes to flat ModelVersion.""" + nested = serde.decode(data, ModelVersionNested) + return _model_version_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +ModelVersion.MODEL_VERSION_ENTITY_COUNT = NumericField( + "modelVersionEntityCount", "modelVersionEntityCount" +) +ModelVersion.MODEL_NAME = KeywordField("modelName", "modelName") +ModelVersion.MODEL_QUALIFIED_NAME = KeywordField( + "modelQualifiedName", "modelQualifiedName" +) +ModelVersion.MODEL_DOMAIN = KeywordTextField( + "modelDomain", "modelDomain", "modelDomain.text" +) +ModelVersion.MODEL_NAMESPACE = KeywordTextField( + "modelNamespace", "modelNamespace", "modelNamespace.text" +) +ModelVersion.MODEL_VERSION_NAME = KeywordTextField( + "modelVersionName", "modelVersionName", "modelVersionName.text" +) +ModelVersion.MODEL_VERSION_AGNOSTIC_QUALIFIED_NAME = KeywordField( + "modelVersionAgnosticQualifiedName", "modelVersionAgnosticQualifiedName" +) +ModelVersion.MODEL_VERSION_QUALIFIED_NAME = KeywordField( + "modelVersionQualifiedName", "modelVersionQualifiedName" +) +ModelVersion.MODEL_ENTITY_NAME = KeywordTextField( + "modelEntityName", "modelEntityName", "modelEntityName.text" +) +ModelVersion.MODEL_ENTITY_QUALIFIED_NAME = KeywordField( + "modelEntityQualifiedName", "modelEntityQualifiedName" +) +ModelVersion.MODEL_TYPE = KeywordField("modelType", "modelType") +ModelVersion.MODEL_SYSTEM_DATE = NumericField("modelSystemDate", "modelSystemDate") +ModelVersion.MODEL_BUSINESS_DATE = NumericField( + "modelBusinessDate", "modelBusinessDate" +) +ModelVersion.MODEL_EXPIRED_AT_SYSTEM_DATE = NumericField( + "modelExpiredAtSystemDate", "modelExpiredAtSystemDate" +) +ModelVersion.MODEL_EXPIRED_AT_BUSINESS_DATE = NumericField( + "modelExpiredAtBusinessDate", "modelExpiredAtBusinessDate" +) +ModelVersion.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +ModelVersion.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +ModelVersion.ANOMALO_CHECKS = RelationField("anomaloChecks") +ModelVersion.APPLICATION = RelationField("application") +ModelVersion.APPLICATION_FIELD = RelationField("applicationField") +ModelVersion.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +ModelVersion.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +ModelVersion.MODEL_DATA_MODEL = RelationField("modelDataModel") +ModelVersion.MODEL_VERSION_ENTITIES = RelationField("modelVersionEntities") +ModelVersion.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +ModelVersion.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +ModelVersion.METRICS = RelationField("metrics") +ModelVersion.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +ModelVersion.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +ModelVersion.MEANINGS = RelationField("meanings") +ModelVersion.MC_MONITORS = RelationField("mcMonitors") +ModelVersion.MC_INCIDENTS = RelationField("mcIncidents") +ModelVersion.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +ModelVersion.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +ModelVersion.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +ModelVersion.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +ModelVersion.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +ModelVersion.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +ModelVersion.FILES = RelationField("files") +ModelVersion.LINKS = RelationField("links") +ModelVersion.README = RelationField("readme") +ModelVersion.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +ModelVersion.SODA_CHECKS = RelationField("sodaChecks") +ModelVersion.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +ModelVersion.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/mongo_db.py b/pyatlan_v9/model/assets/mongo_db.py new file mode 100644 index 000000000..c49e01786 --- /dev/null +++ b/pyatlan_v9/model/assets/mongo_db.py @@ -0,0 +1,542 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +MongoDB asset model with flattened inheritance. + +This module provides: +- MongoDB: Flat asset class (easy to use) +- MongoDBAttributes: Nested attributes struct (extends AssetAttributes) +- MongoDBNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class MongoDB(Asset): + """ + Base class for MongoDB assets. + """ + + NO_SQL_SCHEMA_DEFINITION: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "MongoDB" + + no_sql_schema_definition: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="noSQLSchemaDefinition" + ) + """Represents attributes for describing the key schema for the table and indexes.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "MongoDB" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _mongo_db_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> MongoDB: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + MongoDB instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _mongo_db_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class MongoDBAttributes(AssetAttributes): + """MongoDB-specific attributes for nested API format.""" + + no_sql_schema_definition: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="noSQLSchemaDefinition" + ) + """Represents attributes for describing the key schema for the table and indexes.""" + + +class MongoDBRelationshipAttributes(AssetRelationshipAttributes): + """MongoDB-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class MongoDBNested(AssetNested): + """MongoDB in nested API format for high-performance serialization.""" + + attributes: Union[MongoDBAttributes, UnsetType] = UNSET + relationship_attributes: Union[MongoDBRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[MongoDBRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[MongoDBRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_MONGO_DB_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_mongo_db_attrs(attrs: MongoDBAttributes, obj: MongoDB) -> None: + """Populate MongoDB-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.no_sql_schema_definition = obj.no_sql_schema_definition + + +def _extract_mongo_db_attrs(attrs: MongoDBAttributes) -> dict: + """Extract all MongoDB attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["no_sql_schema_definition"] = attrs.no_sql_schema_definition + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _mongo_db_to_nested(mongo_db: MongoDB) -> MongoDBNested: + """Convert flat MongoDB to nested format.""" + attrs = MongoDBAttributes() + _populate_mongo_db_attrs(attrs, mongo_db) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + mongo_db, _MONGO_DB_REL_FIELDS, MongoDBRelationshipAttributes + ) + return MongoDBNested( + guid=mongo_db.guid, + type_name=mongo_db.type_name, + status=mongo_db.status, + version=mongo_db.version, + create_time=mongo_db.create_time, + update_time=mongo_db.update_time, + created_by=mongo_db.created_by, + updated_by=mongo_db.updated_by, + classifications=mongo_db.classifications, + classification_names=mongo_db.classification_names, + meanings=mongo_db.meanings, + labels=mongo_db.labels, + business_attributes=mongo_db.business_attributes, + custom_attributes=mongo_db.custom_attributes, + pending_tasks=mongo_db.pending_tasks, + proxy=mongo_db.proxy, + is_incomplete=mongo_db.is_incomplete, + provenance_type=mongo_db.provenance_type, + home_id=mongo_db.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _mongo_db_from_nested(nested: MongoDBNested) -> MongoDB: + """Convert nested format to flat MongoDB.""" + attrs = nested.attributes if nested.attributes is not UNSET else MongoDBAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _MONGO_DB_REL_FIELDS, + MongoDBRelationshipAttributes, + ) + return MongoDB( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_mongo_db_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _mongo_db_to_nested_bytes(mongo_db: MongoDB, serde: Serde) -> bytes: + """Convert flat MongoDB to nested JSON bytes.""" + return serde.encode(_mongo_db_to_nested(mongo_db)) + + +def _mongo_db_from_nested_bytes(data: bytes, serde: Serde) -> MongoDB: + """Convert nested JSON bytes to flat MongoDB.""" + nested = serde.decode(data, MongoDBNested) + return _mongo_db_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +MongoDB.NO_SQL_SCHEMA_DEFINITION = KeywordField( + "noSQLSchemaDefinition", "noSQLSchemaDefinition" +) +MongoDB.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +MongoDB.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +MongoDB.ANOMALO_CHECKS = RelationField("anomaloChecks") +MongoDB.APPLICATION = RelationField("application") +MongoDB.APPLICATION_FIELD = RelationField("applicationField") +MongoDB.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +MongoDB.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +MongoDB.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +MongoDB.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +MongoDB.METRICS = RelationField("metrics") +MongoDB.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +MongoDB.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +MongoDB.MEANINGS = RelationField("meanings") +MongoDB.MC_MONITORS = RelationField("mcMonitors") +MongoDB.MC_INCIDENTS = RelationField("mcIncidents") +MongoDB.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +MongoDB.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +MongoDB.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +MongoDB.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +MongoDB.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +MongoDB.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +MongoDB.FILES = RelationField("files") +MongoDB.LINKS = RelationField("links") +MongoDB.README = RelationField("readme") +MongoDB.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +MongoDB.SODA_CHECKS = RelationField("sodaChecks") +MongoDB.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +MongoDB.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/mongo_db_collection.py b/pyatlan_v9/model/assets/mongo_db_collection.py new file mode 100644 index 000000000..353eaa678 --- /dev/null +++ b/pyatlan_v9/model/assets/mongo_db_collection.py @@ -0,0 +1,1441 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +MongoDBCollection asset model with flattened inheritance. + +This module provides: +- MongoDBCollection: Flat asset class (easy to use) +- MongoDBCollectionAttributes: Nested attributes struct (extends AssetAttributes) +- MongoDBCollectionNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .snowflake_related import RelatedSnowflakeSemanticLogicalTable +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from .sql_related import ( + RelatedColumn, + RelatedQuery, + RelatedSchema, + RelatedTable, + RelatedTablePartition, +) +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .mongo_db_related import RelatedMongoDBDatabase + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class MongoDBCollection(Asset): + """ + Instance of a MongoDB collection in Atlan. + """ + + MONGO_DB_COLLECTION_SUBTYPE: ClassVar[Any] = None + MONGO_DB_IS_CAPPED: ClassVar[Any] = None + MONGO_DB_COLLECTION_TIME_FIELD: ClassVar[Any] = None + MONGO_DB_TIME_GRANULARITY: ClassVar[Any] = None + MONGO_DB_EXPIRE_AFTER_SECONDS: ClassVar[Any] = None + MONGO_DB_MAXIMUM_DOCUMENT_COUNT: ClassVar[Any] = None + MONGO_DB_MAX_SIZE: ClassVar[Any] = None + MONGO_DB_NUM_ORPHAN_DOCS: ClassVar[Any] = None + MONGO_DB_NUM_INDEXES: ClassVar[Any] = None + MONGO_DB_TOTAL_INDEX_SIZE: ClassVar[Any] = None + MONGO_DB_AVERAGE_OBJECT_SIZE: ClassVar[Any] = None + MONGO_DB_COLLECTION_SCHEMA_DEFINITION: ClassVar[Any] = None + NO_SQL_SCHEMA_DEFINITION: ClassVar[Any] = None + COLUMN_COUNT: ClassVar[Any] = None + ROW_COUNT: ClassVar[Any] = None + SIZE_BYTES: ClassVar[Any] = None + TABLE_OBJECT_COUNT: ClassVar[Any] = None + ALIAS: ClassVar[Any] = None + IS_TEMPORARY: ClassVar[Any] = None + IS_QUERY_PREVIEW: ClassVar[Any] = None + QUERY_PREVIEW_CONFIG: ClassVar[Any] = None + EXTERNAL_LOCATION: ClassVar[Any] = None + EXTERNAL_LOCATION_REGION: ClassVar[Any] = None + EXTERNAL_LOCATION_FORMAT: ClassVar[Any] = None + IS_PARTITIONED: ClassVar[Any] = None + PARTITION_STRATEGY: ClassVar[Any] = None + PARTITION_COUNT: ClassVar[Any] = None + TABLE_DEFINITION: ClassVar[Any] = None + PARTITION_LIST: ClassVar[Any] = None + IS_SHARDED: ClassVar[Any] = None + TABLE_TYPE: ClassVar[Any] = None + ICEBERG_CATALOG_NAME: ClassVar[Any] = None + ICEBERG_TABLE_TYPE: ClassVar[Any] = None + ICEBERG_CATALOG_SOURCE: ClassVar[Any] = None + ICEBERG_CATALOG_TABLE_NAME: ClassVar[Any] = None + TABLE_IMPALA_PARAMETERS: ClassVar[Any] = None + ICEBERG_CATALOG_TABLE_NAMESPACE: ClassVar[Any] = None + TABLE_EXTERNAL_VOLUME_NAME: ClassVar[Any] = None + ICEBERG_TABLE_BASE_LOCATION: ClassVar[Any] = None + TABLE_RETENTION_TIME: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MONGO_DB_DATABASE: ClassVar[Any] = None + MONGO_DB_COLUMNS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + COLUMNS: ClassVar[Any] = None + QUERIES: ClassVar[Any] = None + ATLAN_SCHEMA: ClassVar[Any] = None + DIMENSIONS: ClassVar[Any] = None + FACTS: ClassVar[Any] = None + PARTITIONS: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "MongoDBCollection" + + mongo_db_collection_subtype: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBCollectionSubtype" + ) + """Subtype of a MongoDB collection, for example: Capped, Time Series, etc.""" + + mongo_db_is_capped: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBIsCapped" + ) + """Whether the collection is capped (true) or not (false).""" + + mongo_db_collection_time_field: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBCollectionTimeField" + ) + """Name of the field containing the date in each time series document.""" + + mongo_db_time_granularity: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBTimeGranularity" + ) + """Closest match to the time span between consecutive incoming measurements.""" + + mongo_db_expire_after_seconds: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBExpireAfterSeconds" + ) + """Seconds after which documents in a time series collection or clustered collection expire.""" + + mongo_db_maximum_document_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBMaximumDocumentCount" + ) + """Maximum number of documents allowed in a capped collection.""" + + mongo_db_max_size: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBMaxSize" + ) + """Maximum size allowed in a capped collection.""" + + mongo_db_num_orphan_docs: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBNumOrphanDocs" + ) + """Number of orphaned documents in the collection.""" + + mongo_db_num_indexes: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBNumIndexes" + ) + """Number of indexes on the collection.""" + + mongo_db_total_index_size: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBTotalIndexSize" + ) + """Total size of all indexes.""" + + mongo_db_average_object_size: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBAverageObjectSize" + ) + """Average size of an object in the collection.""" + + mongo_db_collection_schema_definition: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBCollectionSchemaDefinition" + ) + """Definition of the schema applicable for the collection.""" + + no_sql_schema_definition: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="noSQLSchemaDefinition" + ) + """Represents attributes for describing the key schema for the table and indexes.""" + + column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this table.""" + + row_count: Union[int, None, UnsetType] = UNSET + """Number of rows in this table.""" + + size_bytes: Union[int, None, UnsetType] = UNSET + """Size of this table, in bytes.""" + + table_object_count: Union[int, None, UnsetType] = UNSET + """Number of objects in this table.""" + + alias: Union[str, None, UnsetType] = UNSET + """Alias for this table.""" + + is_temporary: Union[bool, None, UnsetType] = UNSET + """Whether this table is temporary (true) or not (false).""" + + is_query_preview: Union[bool, None, UnsetType] = UNSET + """Whether preview queries are allowed for this table (true) or not (false).""" + + query_preview_config: Union[Dict[str, str], None, UnsetType] = UNSET + """Configuration for preview queries.""" + + external_location: Union[str, None, UnsetType] = UNSET + """External location of this table, for example: an S3 object location.""" + + external_location_region: Union[str, None, UnsetType] = UNSET + """Region of the external location of this table, for example: S3 region.""" + + external_location_format: Union[str, None, UnsetType] = UNSET + """Format of the external location of this table, for example: JSON, CSV, PARQUET, etc.""" + + is_partitioned: Union[bool, None, UnsetType] = UNSET + """Whether this table is partitioned (true) or not (false).""" + + partition_strategy: Union[str, None, UnsetType] = UNSET + """Partition strategy for this table.""" + + partition_count: Union[int, None, UnsetType] = UNSET + """Number of partitions in this table.""" + + table_definition: Union[str, None, UnsetType] = UNSET + """Definition of the table.""" + + partition_list: Union[str, None, UnsetType] = UNSET + """List of partitions in this table.""" + + is_sharded: Union[bool, None, UnsetType] = UNSET + """Whether this table is a sharded table (true) or not (false).""" + + table_type: Union[str, None, UnsetType] = UNSET + """Type of the table.""" + + iceberg_catalog_name: Union[str, None, UnsetType] = UNSET + """Iceberg table catalog name (can be any user defined name)""" + + iceberg_table_type: Union[str, None, UnsetType] = UNSET + """Iceberg table type (managed vs unmanaged)""" + + iceberg_catalog_source: Union[str, None, UnsetType] = UNSET + """Iceberg table catalog type (glue, polaris, snowflake)""" + + iceberg_catalog_table_name: Union[str, None, UnsetType] = UNSET + """Catalog table name (actual table name on the catalog side).""" + + table_impala_parameters: Union[Dict[str, str], None, UnsetType] = UNSET + """Extra attributes for Impala""" + + iceberg_catalog_table_namespace: Union[str, None, UnsetType] = UNSET + """Catalog table namespace (actual database name on the catalog side).""" + + table_external_volume_name: Union[str, None, UnsetType] = UNSET + """External volume name for the table.""" + + iceberg_table_base_location: Union[str, None, UnsetType] = UNSET + """Iceberg table base location inside the external volume.""" + + table_retention_time: Union[int, None, UnsetType] = UNSET + """Data retention time in days.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mongo_db_database: Union[RelatedMongoDBDatabase, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBDatabase" + ) + """Database in which the collection exists.""" + + mongo_db_columns: Union[List[RelatedColumn], None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBColumns" + ) + """Columns that exist within this collection.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Columns that exist within this table.""" + + queries: Union[List[RelatedQuery], None, UnsetType] = UNSET + """Queries that access this table.""" + + atlan_schema: Union[RelatedSchema, None, UnsetType] = UNSET + """Schema in which this table exists.""" + + dimensions: Union[List[RelatedTable], None, UnsetType] = UNSET + """""" + + facts: Union[List[RelatedTable], None, UnsetType] = UNSET + """""" + + partitions: Union[List[RelatedTablePartition], None, UnsetType] = UNSET + """Partitions that exist within this table.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "MongoDBCollection" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _mongo_db_collection_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> MongoDBCollection: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + MongoDBCollection instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _mongo_db_collection_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class MongoDBCollectionAttributes(AssetAttributes): + """MongoDBCollection-specific attributes for nested API format.""" + + mongo_db_collection_subtype: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBCollectionSubtype" + ) + """Subtype of a MongoDB collection, for example: Capped, Time Series, etc.""" + + mongo_db_is_capped: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBIsCapped" + ) + """Whether the collection is capped (true) or not (false).""" + + mongo_db_collection_time_field: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBCollectionTimeField" + ) + """Name of the field containing the date in each time series document.""" + + mongo_db_time_granularity: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBTimeGranularity" + ) + """Closest match to the time span between consecutive incoming measurements.""" + + mongo_db_expire_after_seconds: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBExpireAfterSeconds" + ) + """Seconds after which documents in a time series collection or clustered collection expire.""" + + mongo_db_maximum_document_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBMaximumDocumentCount" + ) + """Maximum number of documents allowed in a capped collection.""" + + mongo_db_max_size: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBMaxSize" + ) + """Maximum size allowed in a capped collection.""" + + mongo_db_num_orphan_docs: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBNumOrphanDocs" + ) + """Number of orphaned documents in the collection.""" + + mongo_db_num_indexes: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBNumIndexes" + ) + """Number of indexes on the collection.""" + + mongo_db_total_index_size: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBTotalIndexSize" + ) + """Total size of all indexes.""" + + mongo_db_average_object_size: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBAverageObjectSize" + ) + """Average size of an object in the collection.""" + + mongo_db_collection_schema_definition: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBCollectionSchemaDefinition" + ) + """Definition of the schema applicable for the collection.""" + + no_sql_schema_definition: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="noSQLSchemaDefinition" + ) + """Represents attributes for describing the key schema for the table and indexes.""" + + column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this table.""" + + row_count: Union[int, None, UnsetType] = UNSET + """Number of rows in this table.""" + + size_bytes: Union[int, None, UnsetType] = UNSET + """Size of this table, in bytes.""" + + table_object_count: Union[int, None, UnsetType] = UNSET + """Number of objects in this table.""" + + alias: Union[str, None, UnsetType] = UNSET + """Alias for this table.""" + + is_temporary: Union[bool, None, UnsetType] = UNSET + """Whether this table is temporary (true) or not (false).""" + + is_query_preview: Union[bool, None, UnsetType] = UNSET + """Whether preview queries are allowed for this table (true) or not (false).""" + + query_preview_config: Union[Dict[str, str], None, UnsetType] = UNSET + """Configuration for preview queries.""" + + external_location: Union[str, None, UnsetType] = UNSET + """External location of this table, for example: an S3 object location.""" + + external_location_region: Union[str, None, UnsetType] = UNSET + """Region of the external location of this table, for example: S3 region.""" + + external_location_format: Union[str, None, UnsetType] = UNSET + """Format of the external location of this table, for example: JSON, CSV, PARQUET, etc.""" + + is_partitioned: Union[bool, None, UnsetType] = UNSET + """Whether this table is partitioned (true) or not (false).""" + + partition_strategy: Union[str, None, UnsetType] = UNSET + """Partition strategy for this table.""" + + partition_count: Union[int, None, UnsetType] = UNSET + """Number of partitions in this table.""" + + table_definition: Union[str, None, UnsetType] = UNSET + """Definition of the table.""" + + partition_list: Union[str, None, UnsetType] = UNSET + """List of partitions in this table.""" + + is_sharded: Union[bool, None, UnsetType] = UNSET + """Whether this table is a sharded table (true) or not (false).""" + + table_type: Union[str, None, UnsetType] = UNSET + """Type of the table.""" + + iceberg_catalog_name: Union[str, None, UnsetType] = UNSET + """Iceberg table catalog name (can be any user defined name)""" + + iceberg_table_type: Union[str, None, UnsetType] = UNSET + """Iceberg table type (managed vs unmanaged)""" + + iceberg_catalog_source: Union[str, None, UnsetType] = UNSET + """Iceberg table catalog type (glue, polaris, snowflake)""" + + iceberg_catalog_table_name: Union[str, None, UnsetType] = UNSET + """Catalog table name (actual table name on the catalog side).""" + + table_impala_parameters: Union[Dict[str, str], None, UnsetType] = UNSET + """Extra attributes for Impala""" + + iceberg_catalog_table_namespace: Union[str, None, UnsetType] = UNSET + """Catalog table namespace (actual database name on the catalog side).""" + + table_external_volume_name: Union[str, None, UnsetType] = UNSET + """External volume name for the table.""" + + iceberg_table_base_location: Union[str, None, UnsetType] = UNSET + """Iceberg table base location inside the external volume.""" + + table_retention_time: Union[int, None, UnsetType] = UNSET + """Data retention time in days.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + +class MongoDBCollectionRelationshipAttributes(AssetRelationshipAttributes): + """MongoDBCollection-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mongo_db_database: Union[RelatedMongoDBDatabase, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBDatabase" + ) + """Database in which the collection exists.""" + + mongo_db_columns: Union[List[RelatedColumn], None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBColumns" + ) + """Columns that exist within this collection.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Columns that exist within this table.""" + + queries: Union[List[RelatedQuery], None, UnsetType] = UNSET + """Queries that access this table.""" + + atlan_schema: Union[RelatedSchema, None, UnsetType] = UNSET + """Schema in which this table exists.""" + + dimensions: Union[List[RelatedTable], None, UnsetType] = UNSET + """""" + + facts: Union[List[RelatedTable], None, UnsetType] = UNSET + """""" + + partitions: Union[List[RelatedTablePartition], None, UnsetType] = UNSET + """Partitions that exist within this table.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class MongoDBCollectionNested(AssetNested): + """MongoDBCollection in nested API format for high-performance serialization.""" + + attributes: Union[MongoDBCollectionAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + MongoDBCollectionRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + MongoDBCollectionRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + MongoDBCollectionRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_MONGO_DB_COLLECTION_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "meanings", + "mongo_db_database", + "mongo_db_columns", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "columns", + "queries", + "atlan_schema", + "dimensions", + "facts", + "partitions", + "schema_registry_subjects", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_mongo_db_collection_attrs( + attrs: MongoDBCollectionAttributes, obj: MongoDBCollection +) -> None: + """Populate MongoDBCollection-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.mongo_db_collection_subtype = obj.mongo_db_collection_subtype + attrs.mongo_db_is_capped = obj.mongo_db_is_capped + attrs.mongo_db_collection_time_field = obj.mongo_db_collection_time_field + attrs.mongo_db_time_granularity = obj.mongo_db_time_granularity + attrs.mongo_db_expire_after_seconds = obj.mongo_db_expire_after_seconds + attrs.mongo_db_maximum_document_count = obj.mongo_db_maximum_document_count + attrs.mongo_db_max_size = obj.mongo_db_max_size + attrs.mongo_db_num_orphan_docs = obj.mongo_db_num_orphan_docs + attrs.mongo_db_num_indexes = obj.mongo_db_num_indexes + attrs.mongo_db_total_index_size = obj.mongo_db_total_index_size + attrs.mongo_db_average_object_size = obj.mongo_db_average_object_size + attrs.mongo_db_collection_schema_definition = ( + obj.mongo_db_collection_schema_definition + ) + attrs.no_sql_schema_definition = obj.no_sql_schema_definition + attrs.column_count = obj.column_count + attrs.row_count = obj.row_count + attrs.size_bytes = obj.size_bytes + attrs.table_object_count = obj.table_object_count + attrs.alias = obj.alias + attrs.is_temporary = obj.is_temporary + attrs.is_query_preview = obj.is_query_preview + attrs.query_preview_config = obj.query_preview_config + attrs.external_location = obj.external_location + attrs.external_location_region = obj.external_location_region + attrs.external_location_format = obj.external_location_format + attrs.is_partitioned = obj.is_partitioned + attrs.partition_strategy = obj.partition_strategy + attrs.partition_count = obj.partition_count + attrs.table_definition = obj.table_definition + attrs.partition_list = obj.partition_list + attrs.is_sharded = obj.is_sharded + attrs.table_type = obj.table_type + attrs.iceberg_catalog_name = obj.iceberg_catalog_name + attrs.iceberg_table_type = obj.iceberg_table_type + attrs.iceberg_catalog_source = obj.iceberg_catalog_source + attrs.iceberg_catalog_table_name = obj.iceberg_catalog_table_name + attrs.table_impala_parameters = obj.table_impala_parameters + attrs.iceberg_catalog_table_namespace = obj.iceberg_catalog_table_namespace + attrs.table_external_volume_name = obj.table_external_volume_name + attrs.iceberg_table_base_location = obj.iceberg_table_base_location + attrs.table_retention_time = obj.table_retention_time + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + + +def _extract_mongo_db_collection_attrs(attrs: MongoDBCollectionAttributes) -> dict: + """Extract all MongoDBCollection attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["mongo_db_collection_subtype"] = attrs.mongo_db_collection_subtype + result["mongo_db_is_capped"] = attrs.mongo_db_is_capped + result["mongo_db_collection_time_field"] = attrs.mongo_db_collection_time_field + result["mongo_db_time_granularity"] = attrs.mongo_db_time_granularity + result["mongo_db_expire_after_seconds"] = attrs.mongo_db_expire_after_seconds + result["mongo_db_maximum_document_count"] = attrs.mongo_db_maximum_document_count + result["mongo_db_max_size"] = attrs.mongo_db_max_size + result["mongo_db_num_orphan_docs"] = attrs.mongo_db_num_orphan_docs + result["mongo_db_num_indexes"] = attrs.mongo_db_num_indexes + result["mongo_db_total_index_size"] = attrs.mongo_db_total_index_size + result["mongo_db_average_object_size"] = attrs.mongo_db_average_object_size + result["mongo_db_collection_schema_definition"] = ( + attrs.mongo_db_collection_schema_definition + ) + result["no_sql_schema_definition"] = attrs.no_sql_schema_definition + result["column_count"] = attrs.column_count + result["row_count"] = attrs.row_count + result["size_bytes"] = attrs.size_bytes + result["table_object_count"] = attrs.table_object_count + result["alias"] = attrs.alias + result["is_temporary"] = attrs.is_temporary + result["is_query_preview"] = attrs.is_query_preview + result["query_preview_config"] = attrs.query_preview_config + result["external_location"] = attrs.external_location + result["external_location_region"] = attrs.external_location_region + result["external_location_format"] = attrs.external_location_format + result["is_partitioned"] = attrs.is_partitioned + result["partition_strategy"] = attrs.partition_strategy + result["partition_count"] = attrs.partition_count + result["table_definition"] = attrs.table_definition + result["partition_list"] = attrs.partition_list + result["is_sharded"] = attrs.is_sharded + result["table_type"] = attrs.table_type + result["iceberg_catalog_name"] = attrs.iceberg_catalog_name + result["iceberg_table_type"] = attrs.iceberg_table_type + result["iceberg_catalog_source"] = attrs.iceberg_catalog_source + result["iceberg_catalog_table_name"] = attrs.iceberg_catalog_table_name + result["table_impala_parameters"] = attrs.table_impala_parameters + result["iceberg_catalog_table_namespace"] = attrs.iceberg_catalog_table_namespace + result["table_external_volume_name"] = attrs.table_external_volume_name + result["iceberg_table_base_location"] = attrs.iceberg_table_base_location + result["table_retention_time"] = attrs.table_retention_time + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _mongo_db_collection_to_nested( + mongo_db_collection: MongoDBCollection, +) -> MongoDBCollectionNested: + """Convert flat MongoDBCollection to nested format.""" + attrs = MongoDBCollectionAttributes() + _populate_mongo_db_collection_attrs(attrs, mongo_db_collection) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + mongo_db_collection, + _MONGO_DB_COLLECTION_REL_FIELDS, + MongoDBCollectionRelationshipAttributes, + ) + return MongoDBCollectionNested( + guid=mongo_db_collection.guid, + type_name=mongo_db_collection.type_name, + status=mongo_db_collection.status, + version=mongo_db_collection.version, + create_time=mongo_db_collection.create_time, + update_time=mongo_db_collection.update_time, + created_by=mongo_db_collection.created_by, + updated_by=mongo_db_collection.updated_by, + classifications=mongo_db_collection.classifications, + classification_names=mongo_db_collection.classification_names, + meanings=mongo_db_collection.meanings, + labels=mongo_db_collection.labels, + business_attributes=mongo_db_collection.business_attributes, + custom_attributes=mongo_db_collection.custom_attributes, + pending_tasks=mongo_db_collection.pending_tasks, + proxy=mongo_db_collection.proxy, + is_incomplete=mongo_db_collection.is_incomplete, + provenance_type=mongo_db_collection.provenance_type, + home_id=mongo_db_collection.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _mongo_db_collection_from_nested( + nested: MongoDBCollectionNested, +) -> MongoDBCollection: + """Convert nested format to flat MongoDBCollection.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else MongoDBCollectionAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _MONGO_DB_COLLECTION_REL_FIELDS, + MongoDBCollectionRelationshipAttributes, + ) + return MongoDBCollection( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_mongo_db_collection_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _mongo_db_collection_to_nested_bytes( + mongo_db_collection: MongoDBCollection, serde: Serde +) -> bytes: + """Convert flat MongoDBCollection to nested JSON bytes.""" + return serde.encode(_mongo_db_collection_to_nested(mongo_db_collection)) + + +def _mongo_db_collection_from_nested_bytes( + data: bytes, serde: Serde +) -> MongoDBCollection: + """Convert nested JSON bytes to flat MongoDBCollection.""" + nested = serde.decode(data, MongoDBCollectionNested) + return _mongo_db_collection_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +MongoDBCollection.MONGO_DB_COLLECTION_SUBTYPE = KeywordTextField( + "mongoDBCollectionSubtype", + "mongoDBCollectionSubtype", + "mongoDBCollectionSubtype.text", +) +MongoDBCollection.MONGO_DB_IS_CAPPED = BooleanField( + "mongoDBIsCapped", "mongoDBIsCapped" +) +MongoDBCollection.MONGO_DB_COLLECTION_TIME_FIELD = KeywordField( + "mongoDBCollectionTimeField", "mongoDBCollectionTimeField" +) +MongoDBCollection.MONGO_DB_TIME_GRANULARITY = KeywordField( + "mongoDBTimeGranularity", "mongoDBTimeGranularity" +) +MongoDBCollection.MONGO_DB_EXPIRE_AFTER_SECONDS = NumericField( + "mongoDBExpireAfterSeconds", "mongoDBExpireAfterSeconds" +) +MongoDBCollection.MONGO_DB_MAXIMUM_DOCUMENT_COUNT = NumericField( + "mongoDBMaximumDocumentCount", "mongoDBMaximumDocumentCount" +) +MongoDBCollection.MONGO_DB_MAX_SIZE = NumericField("mongoDBMaxSize", "mongoDBMaxSize") +MongoDBCollection.MONGO_DB_NUM_ORPHAN_DOCS = NumericField( + "mongoDBNumOrphanDocs", "mongoDBNumOrphanDocs" +) +MongoDBCollection.MONGO_DB_NUM_INDEXES = NumericField( + "mongoDBNumIndexes", "mongoDBNumIndexes" +) +MongoDBCollection.MONGO_DB_TOTAL_INDEX_SIZE = NumericField( + "mongoDBTotalIndexSize", "mongoDBTotalIndexSize" +) +MongoDBCollection.MONGO_DB_AVERAGE_OBJECT_SIZE = NumericField( + "mongoDBAverageObjectSize", "mongoDBAverageObjectSize" +) +MongoDBCollection.MONGO_DB_COLLECTION_SCHEMA_DEFINITION = KeywordField( + "mongoDBCollectionSchemaDefinition", "mongoDBCollectionSchemaDefinition" +) +MongoDBCollection.NO_SQL_SCHEMA_DEFINITION = KeywordField( + "noSQLSchemaDefinition", "noSQLSchemaDefinition" +) +MongoDBCollection.COLUMN_COUNT = NumericField("columnCount", "columnCount") +MongoDBCollection.ROW_COUNT = NumericField("rowCount", "rowCount") +MongoDBCollection.SIZE_BYTES = NumericField("sizeBytes", "sizeBytes") +MongoDBCollection.TABLE_OBJECT_COUNT = NumericField( + "tableObjectCount", "tableObjectCount" +) +MongoDBCollection.ALIAS = KeywordField("alias", "alias") +MongoDBCollection.IS_TEMPORARY = BooleanField("isTemporary", "isTemporary") +MongoDBCollection.IS_QUERY_PREVIEW = BooleanField("isQueryPreview", "isQueryPreview") +MongoDBCollection.QUERY_PREVIEW_CONFIG = KeywordField( + "queryPreviewConfig", "queryPreviewConfig" +) +MongoDBCollection.EXTERNAL_LOCATION = KeywordField( + "externalLocation", "externalLocation" +) +MongoDBCollection.EXTERNAL_LOCATION_REGION = KeywordField( + "externalLocationRegion", "externalLocationRegion" +) +MongoDBCollection.EXTERNAL_LOCATION_FORMAT = KeywordField( + "externalLocationFormat", "externalLocationFormat" +) +MongoDBCollection.IS_PARTITIONED = BooleanField("isPartitioned", "isPartitioned") +MongoDBCollection.PARTITION_STRATEGY = KeywordField( + "partitionStrategy", "partitionStrategy" +) +MongoDBCollection.PARTITION_COUNT = NumericField("partitionCount", "partitionCount") +MongoDBCollection.TABLE_DEFINITION = KeywordField("tableDefinition", "tableDefinition") +MongoDBCollection.PARTITION_LIST = KeywordField("partitionList", "partitionList") +MongoDBCollection.IS_SHARDED = BooleanField("isSharded", "isSharded") +MongoDBCollection.TABLE_TYPE = KeywordField("tableType", "tableType") +MongoDBCollection.ICEBERG_CATALOG_NAME = KeywordField( + "icebergCatalogName", "icebergCatalogName" +) +MongoDBCollection.ICEBERG_TABLE_TYPE = KeywordField( + "icebergTableType", "icebergTableType" +) +MongoDBCollection.ICEBERG_CATALOG_SOURCE = KeywordField( + "icebergCatalogSource", "icebergCatalogSource" +) +MongoDBCollection.ICEBERG_CATALOG_TABLE_NAME = KeywordField( + "icebergCatalogTableName", "icebergCatalogTableName" +) +MongoDBCollection.TABLE_IMPALA_PARAMETERS = KeywordField( + "tableImpalaParameters", "tableImpalaParameters" +) +MongoDBCollection.ICEBERG_CATALOG_TABLE_NAMESPACE = KeywordField( + "icebergCatalogTableNamespace", "icebergCatalogTableNamespace" +) +MongoDBCollection.TABLE_EXTERNAL_VOLUME_NAME = KeywordField( + "tableExternalVolumeName", "tableExternalVolumeName" +) +MongoDBCollection.ICEBERG_TABLE_BASE_LOCATION = KeywordField( + "icebergTableBaseLocation", "icebergTableBaseLocation" +) +MongoDBCollection.TABLE_RETENTION_TIME = NumericField( + "tableRetentionTime", "tableRetentionTime" +) +MongoDBCollection.QUERY_COUNT = NumericField("queryCount", "queryCount") +MongoDBCollection.QUERY_USER_COUNT = NumericField("queryUserCount", "queryUserCount") +MongoDBCollection.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +MongoDBCollection.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +MongoDBCollection.DATABASE_NAME = KeywordField("databaseName", "databaseName") +MongoDBCollection.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +MongoDBCollection.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +MongoDBCollection.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +MongoDBCollection.TABLE_NAME = KeywordField("tableName", "tableName") +MongoDBCollection.TABLE_QUALIFIED_NAME = KeywordField( + "tableQualifiedName", "tableQualifiedName" +) +MongoDBCollection.VIEW_NAME = KeywordField("viewName", "viewName") +MongoDBCollection.VIEW_QUALIFIED_NAME = KeywordField( + "viewQualifiedName", "viewQualifiedName" +) +MongoDBCollection.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +MongoDBCollection.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +MongoDBCollection.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +MongoDBCollection.LAST_PROFILED_AT = NumericField("lastProfiledAt", "lastProfiledAt") +MongoDBCollection.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +MongoDBCollection.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +MongoDBCollection.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +MongoDBCollection.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +MongoDBCollection.ANOMALO_CHECKS = RelationField("anomaloChecks") +MongoDBCollection.APPLICATION = RelationField("application") +MongoDBCollection.APPLICATION_FIELD = RelationField("applicationField") +MongoDBCollection.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +MongoDBCollection.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +MongoDBCollection.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +MongoDBCollection.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +MongoDBCollection.METRICS = RelationField("metrics") +MongoDBCollection.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +MongoDBCollection.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +MongoDBCollection.DBT_MODELS = RelationField("dbtModels") +MongoDBCollection.SQL_DBT_MODELS = RelationField("sqlDbtModels") +MongoDBCollection.DBT_TESTS = RelationField("dbtTests") +MongoDBCollection.DBT_SOURCES = RelationField("dbtSources") +MongoDBCollection.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +MongoDBCollection.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +MongoDBCollection.MEANINGS = RelationField("meanings") +MongoDBCollection.MONGO_DB_DATABASE = RelationField("mongoDBDatabase") +MongoDBCollection.MONGO_DB_COLUMNS = RelationField("mongoDBColumns") +MongoDBCollection.MC_MONITORS = RelationField("mcMonitors") +MongoDBCollection.MC_INCIDENTS = RelationField("mcIncidents") +MongoDBCollection.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +MongoDBCollection.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +MongoDBCollection.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +MongoDBCollection.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +MongoDBCollection.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +MongoDBCollection.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +MongoDBCollection.FILES = RelationField("files") +MongoDBCollection.LINKS = RelationField("links") +MongoDBCollection.README = RelationField("readme") +MongoDBCollection.COLUMNS = RelationField("columns") +MongoDBCollection.QUERIES = RelationField("queries") +MongoDBCollection.ATLAN_SCHEMA = RelationField("atlanSchema") +MongoDBCollection.DIMENSIONS = RelationField("dimensions") +MongoDBCollection.FACTS = RelationField("facts") +MongoDBCollection.PARTITIONS = RelationField("partitions") +MongoDBCollection.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +MongoDBCollection.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +MongoDBCollection.SODA_CHECKS = RelationField("sodaChecks") +MongoDBCollection.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +MongoDBCollection.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/mongo_db_database.py b/pyatlan_v9/model/assets/mongo_db_database.py new file mode 100644 index 000000000..c7afe46e7 --- /dev/null +++ b/pyatlan_v9/model/assets/mongo_db_database.py @@ -0,0 +1,907 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +MongoDBDatabase asset model with flattened inheritance. + +This module provides: +- MongoDBDatabase: Flat asset class (easy to use) +- MongoDBDatabaseAttributes: Nested attributes struct (extends AssetAttributes) +- MongoDBDatabaseNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .fabric_related import RelatedFabricWorkspace +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .snowflake_related import RelatedSnowflakeSemanticLogicalTable +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from .sql_related import RelatedSchema +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .mongo_db_related import RelatedMongoDBCollection + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class MongoDBDatabase(Asset): + """ + Instance of a MongoDB database in Atlan. + """ + + MONGO_DB_DATABASE_COLLECTION_COUNT: ClassVar[Any] = None + NO_SQL_SCHEMA_DEFINITION: ClassVar[Any] = None + SCHEMA_COUNT: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + FABRIC_WORKSPACE: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MONGO_DB_COLLECTIONS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMAS: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "MongoDBDatabase" + + mongo_db_database_collection_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBDatabaseCollectionCount" + ) + """Number of collections in the database.""" + + no_sql_schema_definition: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="noSQLSchemaDefinition" + ) + """Represents attributes for describing the key schema for the table and indexes.""" + + schema_count: Union[int, None, UnsetType] = UNSET + """Number of schemas in this database.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + fabric_workspace: Union[RelatedFabricWorkspace, None, UnsetType] = UNSET + """Workspace containing the database.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mongo_db_collections: Union[List[RelatedMongoDBCollection], None, UnsetType] = ( + msgspec.field(default=UNSET, name="mongoDBCollections") + ) + """Collections that exist within this database.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schemas: Union[List[RelatedSchema], None, UnsetType] = UNSET + """Schemas that exist within this database.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "MongoDBDatabase" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _mongo_db_database_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> MongoDBDatabase: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + MongoDBDatabase instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _mongo_db_database_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class MongoDBDatabaseAttributes(AssetAttributes): + """MongoDBDatabase-specific attributes for nested API format.""" + + mongo_db_database_collection_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBDatabaseCollectionCount" + ) + """Number of collections in the database.""" + + no_sql_schema_definition: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="noSQLSchemaDefinition" + ) + """Represents attributes for describing the key schema for the table and indexes.""" + + schema_count: Union[int, None, UnsetType] = UNSET + """Number of schemas in this database.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + +class MongoDBDatabaseRelationshipAttributes(AssetRelationshipAttributes): + """MongoDBDatabase-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + fabric_workspace: Union[RelatedFabricWorkspace, None, UnsetType] = UNSET + """Workspace containing the database.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mongo_db_collections: Union[List[RelatedMongoDBCollection], None, UnsetType] = ( + msgspec.field(default=UNSET, name="mongoDBCollections") + ) + """Collections that exist within this database.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schemas: Union[List[RelatedSchema], None, UnsetType] = UNSET + """Schemas that exist within this database.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class MongoDBDatabaseNested(AssetNested): + """MongoDBDatabase in nested API format for high-performance serialization.""" + + attributes: Union[MongoDBDatabaseAttributes, UnsetType] = UNSET + relationship_attributes: Union[MongoDBDatabaseRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + MongoDBDatabaseRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + MongoDBDatabaseRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_MONGO_DB_DATABASE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "fabric_workspace", + "meanings", + "mongo_db_collections", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schemas", + "schema_registry_subjects", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_mongo_db_database_attrs( + attrs: MongoDBDatabaseAttributes, obj: MongoDBDatabase +) -> None: + """Populate MongoDBDatabase-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.mongo_db_database_collection_count = obj.mongo_db_database_collection_count + attrs.no_sql_schema_definition = obj.no_sql_schema_definition + attrs.schema_count = obj.schema_count + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + + +def _extract_mongo_db_database_attrs(attrs: MongoDBDatabaseAttributes) -> dict: + """Extract all MongoDBDatabase attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["mongo_db_database_collection_count"] = ( + attrs.mongo_db_database_collection_count + ) + result["no_sql_schema_definition"] = attrs.no_sql_schema_definition + result["schema_count"] = attrs.schema_count + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _mongo_db_database_to_nested( + mongo_db_database: MongoDBDatabase, +) -> MongoDBDatabaseNested: + """Convert flat MongoDBDatabase to nested format.""" + attrs = MongoDBDatabaseAttributes() + _populate_mongo_db_database_attrs(attrs, mongo_db_database) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + mongo_db_database, + _MONGO_DB_DATABASE_REL_FIELDS, + MongoDBDatabaseRelationshipAttributes, + ) + return MongoDBDatabaseNested( + guid=mongo_db_database.guid, + type_name=mongo_db_database.type_name, + status=mongo_db_database.status, + version=mongo_db_database.version, + create_time=mongo_db_database.create_time, + update_time=mongo_db_database.update_time, + created_by=mongo_db_database.created_by, + updated_by=mongo_db_database.updated_by, + classifications=mongo_db_database.classifications, + classification_names=mongo_db_database.classification_names, + meanings=mongo_db_database.meanings, + labels=mongo_db_database.labels, + business_attributes=mongo_db_database.business_attributes, + custom_attributes=mongo_db_database.custom_attributes, + pending_tasks=mongo_db_database.pending_tasks, + proxy=mongo_db_database.proxy, + is_incomplete=mongo_db_database.is_incomplete, + provenance_type=mongo_db_database.provenance_type, + home_id=mongo_db_database.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _mongo_db_database_from_nested(nested: MongoDBDatabaseNested) -> MongoDBDatabase: + """Convert nested format to flat MongoDBDatabase.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else MongoDBDatabaseAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _MONGO_DB_DATABASE_REL_FIELDS, + MongoDBDatabaseRelationshipAttributes, + ) + return MongoDBDatabase( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_mongo_db_database_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _mongo_db_database_to_nested_bytes( + mongo_db_database: MongoDBDatabase, serde: Serde +) -> bytes: + """Convert flat MongoDBDatabase to nested JSON bytes.""" + return serde.encode(_mongo_db_database_to_nested(mongo_db_database)) + + +def _mongo_db_database_from_nested_bytes(data: bytes, serde: Serde) -> MongoDBDatabase: + """Convert nested JSON bytes to flat MongoDBDatabase.""" + nested = serde.decode(data, MongoDBDatabaseNested) + return _mongo_db_database_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, +) + +MongoDBDatabase.MONGO_DB_DATABASE_COLLECTION_COUNT = NumericField( + "mongoDBDatabaseCollectionCount", "mongoDBDatabaseCollectionCount" +) +MongoDBDatabase.NO_SQL_SCHEMA_DEFINITION = KeywordField( + "noSQLSchemaDefinition", "noSQLSchemaDefinition" +) +MongoDBDatabase.SCHEMA_COUNT = NumericField("schemaCount", "schemaCount") +MongoDBDatabase.QUERY_COUNT = NumericField("queryCount", "queryCount") +MongoDBDatabase.QUERY_USER_COUNT = NumericField("queryUserCount", "queryUserCount") +MongoDBDatabase.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +MongoDBDatabase.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +MongoDBDatabase.DATABASE_NAME = KeywordField("databaseName", "databaseName") +MongoDBDatabase.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +MongoDBDatabase.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +MongoDBDatabase.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +MongoDBDatabase.TABLE_NAME = KeywordField("tableName", "tableName") +MongoDBDatabase.TABLE_QUALIFIED_NAME = KeywordField( + "tableQualifiedName", "tableQualifiedName" +) +MongoDBDatabase.VIEW_NAME = KeywordField("viewName", "viewName") +MongoDBDatabase.VIEW_QUALIFIED_NAME = KeywordField( + "viewQualifiedName", "viewQualifiedName" +) +MongoDBDatabase.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +MongoDBDatabase.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +MongoDBDatabase.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +MongoDBDatabase.LAST_PROFILED_AT = NumericField("lastProfiledAt", "lastProfiledAt") +MongoDBDatabase.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +MongoDBDatabase.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +MongoDBDatabase.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +MongoDBDatabase.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +MongoDBDatabase.ANOMALO_CHECKS = RelationField("anomaloChecks") +MongoDBDatabase.APPLICATION = RelationField("application") +MongoDBDatabase.APPLICATION_FIELD = RelationField("applicationField") +MongoDBDatabase.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +MongoDBDatabase.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +MongoDBDatabase.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +MongoDBDatabase.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +MongoDBDatabase.METRICS = RelationField("metrics") +MongoDBDatabase.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +MongoDBDatabase.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +MongoDBDatabase.DBT_MODELS = RelationField("dbtModels") +MongoDBDatabase.SQL_DBT_MODELS = RelationField("sqlDbtModels") +MongoDBDatabase.DBT_TESTS = RelationField("dbtTests") +MongoDBDatabase.DBT_SOURCES = RelationField("dbtSources") +MongoDBDatabase.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +MongoDBDatabase.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +MongoDBDatabase.FABRIC_WORKSPACE = RelationField("fabricWorkspace") +MongoDBDatabase.MEANINGS = RelationField("meanings") +MongoDBDatabase.MONGO_DB_COLLECTIONS = RelationField("mongoDBCollections") +MongoDBDatabase.MC_MONITORS = RelationField("mcMonitors") +MongoDBDatabase.MC_INCIDENTS = RelationField("mcIncidents") +MongoDBDatabase.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +MongoDBDatabase.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +MongoDBDatabase.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +MongoDBDatabase.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +MongoDBDatabase.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +MongoDBDatabase.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +MongoDBDatabase.FILES = RelationField("files") +MongoDBDatabase.LINKS = RelationField("links") +MongoDBDatabase.README = RelationField("readme") +MongoDBDatabase.SCHEMAS = RelationField("schemas") +MongoDBDatabase.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +MongoDBDatabase.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +MongoDBDatabase.SODA_CHECKS = RelationField("sodaChecks") +MongoDBDatabase.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +MongoDBDatabase.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/mongo_db_related.py b/pyatlan_v9/model/assets/mongo_db_related.py new file mode 100644 index 000000000..3dce83c5e --- /dev/null +++ b/pyatlan_v9/model/assets/mongo_db_related.py @@ -0,0 +1,136 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for MongoDB module. + +This module contains all Related{Type} classes for the MongoDB type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedNoSQL +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedMongoDB", + "RelatedMongoDBDatabase", + "RelatedMongoDBCollection", +] + + +class RelatedMongoDB(RelatedNoSQL): + """ + Related entity reference for MongoDB assets. + + Extends RelatedNoSQL with MongoDB-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "MongoDB" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "MongoDB" + + +class RelatedMongoDBDatabase(RelatedMongoDB): + """ + Related entity reference for MongoDBDatabase assets. + + Extends RelatedMongoDB with MongoDBDatabase-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "MongoDBDatabase" so it serializes correctly + + mongo_db_database_collection_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBDatabaseCollectionCount" + ) + """Number of collections in the database.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "MongoDBDatabase" + + +class RelatedMongoDBCollection(RelatedMongoDB): + """ + Related entity reference for MongoDBCollection assets. + + Extends RelatedMongoDB with MongoDBCollection-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "MongoDBCollection" so it serializes correctly + + mongo_db_collection_subtype: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBCollectionSubtype" + ) + """Subtype of a MongoDB collection, for example: Capped, Time Series, etc.""" + + mongo_db_is_capped: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBIsCapped" + ) + """Whether the collection is capped (true) or not (false).""" + + mongo_db_collection_time_field: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBCollectionTimeField" + ) + """Name of the field containing the date in each time series document.""" + + mongo_db_time_granularity: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBTimeGranularity" + ) + """Closest match to the time span between consecutive incoming measurements.""" + + mongo_db_expire_after_seconds: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBExpireAfterSeconds" + ) + """Seconds after which documents in a time series collection or clustered collection expire.""" + + mongo_db_maximum_document_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBMaximumDocumentCount" + ) + """Maximum number of documents allowed in a capped collection.""" + + mongo_db_max_size: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBMaxSize" + ) + """Maximum size allowed in a capped collection.""" + + mongo_db_num_orphan_docs: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBNumOrphanDocs" + ) + """Number of orphaned documents in the collection.""" + + mongo_db_num_indexes: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBNumIndexes" + ) + """Number of indexes on the collection.""" + + mongo_db_total_index_size: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBTotalIndexSize" + ) + """Total size of all indexes.""" + + mongo_db_average_object_size: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBAverageObjectSize" + ) + """Average size of an object in the collection.""" + + mongo_db_collection_schema_definition: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="mongoDBCollectionSchemaDefinition" + ) + """Definition of the schema applicable for the collection.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "MongoDBCollection" diff --git a/pyatlan_v9/model/assets/monte_carlo.py b/pyatlan_v9/model/assets/monte_carlo.py new file mode 100644 index 000000000..9ea8aaf14 --- /dev/null +++ b/pyatlan_v9/model/assets/monte_carlo.py @@ -0,0 +1,563 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +MonteCarlo asset model with flattened inheritance. + +This module provides: +- MonteCarlo: Flat asset class (easy to use) +- MonteCarloAttributes: Nested attributes struct (extends AssetAttributes) +- MonteCarloNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class MonteCarlo(Asset): + """ + Base class for Monte Carlo assets. + """ + + MC_LABELS: ClassVar[Any] = None + MC_ASSET_QUALIFIED_NAMES: ClassVar[Any] = None + DQ_IS_PART_OF_CONTRACT: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "MonteCarlo" + + mc_labels: Union[List[str], None, UnsetType] = UNSET + """List of labels for this Monte Carlo asset.""" + + mc_asset_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of unique names of assets that are part of this Monte Carlo asset.""" + + dq_is_part_of_contract: Union[bool, None, UnsetType] = UNSET + """Whether this data quality is part of contract (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "MonteCarlo" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _monte_carlo_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> MonteCarlo: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + MonteCarlo instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _monte_carlo_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class MonteCarloAttributes(AssetAttributes): + """MonteCarlo-specific attributes for nested API format.""" + + mc_labels: Union[List[str], None, UnsetType] = UNSET + """List of labels for this Monte Carlo asset.""" + + mc_asset_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of unique names of assets that are part of this Monte Carlo asset.""" + + dq_is_part_of_contract: Union[bool, None, UnsetType] = UNSET + """Whether this data quality is part of contract (true) or not (false).""" + + +class MonteCarloRelationshipAttributes(AssetRelationshipAttributes): + """MonteCarlo-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class MonteCarloNested(AssetNested): + """MonteCarlo in nested API format for high-performance serialization.""" + + attributes: Union[MonteCarloAttributes, UnsetType] = UNSET + relationship_attributes: Union[MonteCarloRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + MonteCarloRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + MonteCarloRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_MONTE_CARLO_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_monte_carlo_attrs(attrs: MonteCarloAttributes, obj: MonteCarlo) -> None: + """Populate MonteCarlo-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.mc_labels = obj.mc_labels + attrs.mc_asset_qualified_names = obj.mc_asset_qualified_names + attrs.dq_is_part_of_contract = obj.dq_is_part_of_contract + + +def _extract_monte_carlo_attrs(attrs: MonteCarloAttributes) -> dict: + """Extract all MonteCarlo attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["mc_labels"] = attrs.mc_labels + result["mc_asset_qualified_names"] = attrs.mc_asset_qualified_names + result["dq_is_part_of_contract"] = attrs.dq_is_part_of_contract + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _monte_carlo_to_nested(monte_carlo: MonteCarlo) -> MonteCarloNested: + """Convert flat MonteCarlo to nested format.""" + attrs = MonteCarloAttributes() + _populate_monte_carlo_attrs(attrs, monte_carlo) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + monte_carlo, _MONTE_CARLO_REL_FIELDS, MonteCarloRelationshipAttributes + ) + return MonteCarloNested( + guid=monte_carlo.guid, + type_name=monte_carlo.type_name, + status=monte_carlo.status, + version=monte_carlo.version, + create_time=monte_carlo.create_time, + update_time=monte_carlo.update_time, + created_by=monte_carlo.created_by, + updated_by=monte_carlo.updated_by, + classifications=monte_carlo.classifications, + classification_names=monte_carlo.classification_names, + meanings=monte_carlo.meanings, + labels=monte_carlo.labels, + business_attributes=monte_carlo.business_attributes, + custom_attributes=monte_carlo.custom_attributes, + pending_tasks=monte_carlo.pending_tasks, + proxy=monte_carlo.proxy, + is_incomplete=monte_carlo.is_incomplete, + provenance_type=monte_carlo.provenance_type, + home_id=monte_carlo.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _monte_carlo_from_nested(nested: MonteCarloNested) -> MonteCarlo: + """Convert nested format to flat MonteCarlo.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else MonteCarloAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _MONTE_CARLO_REL_FIELDS, + MonteCarloRelationshipAttributes, + ) + return MonteCarlo( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_monte_carlo_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _monte_carlo_to_nested_bytes(monte_carlo: MonteCarlo, serde: Serde) -> bytes: + """Convert flat MonteCarlo to nested JSON bytes.""" + return serde.encode(_monte_carlo_to_nested(monte_carlo)) + + +def _monte_carlo_from_nested_bytes(data: bytes, serde: Serde) -> MonteCarlo: + """Convert nested JSON bytes to flat MonteCarlo.""" + nested = serde.decode(data, MonteCarloNested) + return _monte_carlo_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + RelationField, +) + +MonteCarlo.MC_LABELS = KeywordField("mcLabels", "mcLabels") +MonteCarlo.MC_ASSET_QUALIFIED_NAMES = KeywordField( + "mcAssetQualifiedNames", "mcAssetQualifiedNames" +) +MonteCarlo.DQ_IS_PART_OF_CONTRACT = BooleanField( + "dqIsPartOfContract", "dqIsPartOfContract" +) +MonteCarlo.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +MonteCarlo.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +MonteCarlo.ANOMALO_CHECKS = RelationField("anomaloChecks") +MonteCarlo.APPLICATION = RelationField("application") +MonteCarlo.APPLICATION_FIELD = RelationField("applicationField") +MonteCarlo.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +MonteCarlo.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +MonteCarlo.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +MonteCarlo.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +MonteCarlo.METRICS = RelationField("metrics") +MonteCarlo.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +MonteCarlo.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +MonteCarlo.MEANINGS = RelationField("meanings") +MonteCarlo.MC_MONITORS = RelationField("mcMonitors") +MonteCarlo.MC_INCIDENTS = RelationField("mcIncidents") +MonteCarlo.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +MonteCarlo.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +MonteCarlo.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +MonteCarlo.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +MonteCarlo.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +MonteCarlo.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +MonteCarlo.FILES = RelationField("files") +MonteCarlo.LINKS = RelationField("links") +MonteCarlo.README = RelationField("readme") +MonteCarlo.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +MonteCarlo.SODA_CHECKS = RelationField("sodaChecks") +MonteCarlo.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +MonteCarlo.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/monte_carlo_related.py b/pyatlan_v9/model/assets/monte_carlo_related.py new file mode 100644 index 000000000..02f9151b3 --- /dev/null +++ b/pyatlan_v9/model/assets/monte_carlo_related.py @@ -0,0 +1,160 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for MonteCarlo module. + +This module contains all Related{Type} classes for the MonteCarlo type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Any, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .data_quality_related import RelatedDataQuality +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedMonteCarlo", + "RelatedMCMonitor", + "RelatedMCIncident", +] + + +class RelatedMonteCarlo(RelatedDataQuality): + """ + Related entity reference for MonteCarlo assets. + + Extends RelatedDataQuality with MonteCarlo-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "MonteCarlo" so it serializes correctly + + mc_labels: Union[List[str], None, UnsetType] = UNSET + """List of labels for this Monte Carlo asset.""" + + mc_asset_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of unique names of assets that are part of this Monte Carlo asset.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "MonteCarlo" + + +class RelatedMCMonitor(RelatedMonteCarlo): + """ + Related entity reference for MCMonitor assets. + + Extends RelatedMonteCarlo with MCMonitor-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "MCMonitor" so it serializes correctly + + mc_monitor_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for this monitor, from Monte Carlo.""" + + mc_monitor_status: Union[str, None, UnsetType] = UNSET + """Status of this monitor.""" + + mc_monitor_type: Union[str, None, UnsetType] = UNSET + """Type of this monitor, for example: field health (stats) or dimension tracking (categories).""" + + mc_monitor_warehouse: Union[str, None, UnsetType] = UNSET + """Name of the warehouse for this monitor.""" + + mc_monitor_schedule_type: Union[str, None, UnsetType] = UNSET + """Type of schedule for this monitor, for example: fixed or dynamic.""" + + mc_monitor_namespace: Union[str, None, UnsetType] = UNSET + """Namespace of this monitor.""" + + mc_monitor_rule_type: Union[str, None, UnsetType] = UNSET + """Type of rule for this monitor.""" + + mc_monitor_rule_custom_sql: Union[str, None, UnsetType] = UNSET + """SQL code for custom SQL rules.""" + + mc_monitor_rule_schedule_config: Union[Dict[str, Any], None, UnsetType] = UNSET + """Schedule details for the rule.""" + + mc_monitor_rule_schedule_config_humanized: Union[str, None, UnsetType] = UNSET + """Readable description of the schedule for the rule.""" + + mc_monitor_alert_condition: Union[str, None, UnsetType] = UNSET + """Condition on which the monitor produces an alert.""" + + mc_monitor_rule_next_execution_time: Union[int, None, UnsetType] = UNSET + """Time at which the next execution of the rule should occur.""" + + mc_monitor_rule_previous_execution_time: Union[int, None, UnsetType] = UNSET + """Time at which the previous execution of the rule occurred.""" + + mc_monitor_rule_comparisons: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """Comparison logic used for the rule.""" + + mc_monitor_rule_is_snoozed: Union[bool, None, UnsetType] = UNSET + """Whether the rule is currently snoozed (true) or not (false).""" + + mc_monitor_breach_rate: Union[float, None, UnsetType] = UNSET + """Rate at which this monitor is breached.""" + + mc_monitor_incident_count: Union[int, None, UnsetType] = UNSET + """Number of incidents associated with this monitor.""" + + mc_monitor_alert_count: Union[int, None, UnsetType] = UNSET + """Number of alerts associated with this monitor.""" + + mc_monitor_priority: Union[str, None, UnsetType] = UNSET + """Priority of this monitor.""" + + mc_monitor_is_ootb: Union[bool, None, UnsetType] = UNSET + """Whether the monitor is OOTB or not""" + + mc_monitor_notification_channels: Union[List[str], None, UnsetType] = UNSET + """Channels through which notifications are sent for this monitor (e.g., email, slack, webhook).""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "MCMonitor" + + +class RelatedMCIncident(RelatedMonteCarlo): + """ + Related entity reference for MCIncident assets. + + Extends RelatedMonteCarlo with MCIncident-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "MCIncident" so it serializes correctly + + mc_incident_id: Union[str, None, UnsetType] = UNSET + """Identifier of this incident, from Monte Carlo.""" + + mc_incident_type: Union[str, None, UnsetType] = UNSET + """Type of this incident.""" + + mc_incident_sub_types: Union[List[str], None, UnsetType] = UNSET + """Subtypes of this incident.""" + + mc_incident_severity: Union[str, None, UnsetType] = UNSET + """Severity of this incident.""" + + mc_incident_priority: Union[str, None, UnsetType] = UNSET + """Priority of this incident inherited from monitor.""" + + mc_incident_state: Union[str, None, UnsetType] = UNSET + """State of this incident.""" + + mc_incident_warehouse: Union[str, None, UnsetType] = UNSET + """Name of this incident's warehouse.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "MCIncident" diff --git a/pyatlan_v9/model/assets/multi_dimensional_dataset.py b/pyatlan_v9/model/assets/multi_dimensional_dataset.py new file mode 100644 index 000000000..bfb0d802c --- /dev/null +++ b/pyatlan_v9/model/assets/multi_dimensional_dataset.py @@ -0,0 +1,649 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +MultiDimensionalDataset asset model with flattened inheritance. + +This module provides: +- MultiDimensionalDataset: Flat asset class (easy to use) +- MultiDimensionalDatasetAttributes: Nested attributes struct (extends AssetAttributes) +- MultiDimensionalDatasetNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .cube_related import RelatedCubeDimension + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class MultiDimensionalDataset(Asset): + """ + A dataset with multiple dimensions + """ + + CUBE_NAME: ClassVar[Any] = None + CUBE_QUALIFIED_NAME: ClassVar[Any] = None + CUBE_DIMENSION_NAME: ClassVar[Any] = None + CUBE_DIMENSION_QUALIFIED_NAME: ClassVar[Any] = None + CUBE_HIERARCHY_NAME: ClassVar[Any] = None + CUBE_HIERARCHY_QUALIFIED_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + CUBE_DIMENSIONS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "MultiDimensionalDataset" + + cube_name: Union[str, None, UnsetType] = UNSET + """Simple name of the cube in which this asset exists, or empty if it is itself a cube.""" + + cube_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the cube in which this asset exists, or empty if it is itself a cube.""" + + cube_dimension_name: Union[str, None, UnsetType] = UNSET + """Simple name of the cube dimension in which this asset exists, or empty if it is itself a dimension.""" + + cube_dimension_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the cube dimension in which this asset exists, or empty if it is itself a dimension.""" + + cube_hierarchy_name: Union[str, None, UnsetType] = UNSET + """Simple name of the dimension hierarchy in which this asset exists, or empty if it is itself a hierarchy.""" + + cube_hierarchy_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dimension hierarchy in which this asset exists, or empty if it is itself a hierarchy.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + cube_dimensions: Union[List[RelatedCubeDimension], None, UnsetType] = UNSET + """Individual dimensions contained in the cube.""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "MultiDimensionalDataset" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _multi_dimensional_dataset_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> MultiDimensionalDataset: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + MultiDimensionalDataset instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _multi_dimensional_dataset_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class MultiDimensionalDatasetAttributes(AssetAttributes): + """MultiDimensionalDataset-specific attributes for nested API format.""" + + cube_name: Union[str, None, UnsetType] = UNSET + """Simple name of the cube in which this asset exists, or empty if it is itself a cube.""" + + cube_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the cube in which this asset exists, or empty if it is itself a cube.""" + + cube_dimension_name: Union[str, None, UnsetType] = UNSET + """Simple name of the cube dimension in which this asset exists, or empty if it is itself a dimension.""" + + cube_dimension_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the cube dimension in which this asset exists, or empty if it is itself a dimension.""" + + cube_hierarchy_name: Union[str, None, UnsetType] = UNSET + """Simple name of the dimension hierarchy in which this asset exists, or empty if it is itself a hierarchy.""" + + cube_hierarchy_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dimension hierarchy in which this asset exists, or empty if it is itself a hierarchy.""" + + +class MultiDimensionalDatasetRelationshipAttributes(AssetRelationshipAttributes): + """MultiDimensionalDataset-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + cube_dimensions: Union[List[RelatedCubeDimension], None, UnsetType] = UNSET + """Individual dimensions contained in the cube.""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class MultiDimensionalDatasetNested(AssetNested): + """MultiDimensionalDataset in nested API format for high-performance serialization.""" + + attributes: Union[MultiDimensionalDatasetAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + MultiDimensionalDatasetRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + MultiDimensionalDatasetRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + MultiDimensionalDatasetRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_MULTI_DIMENSIONAL_DATASET_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "cube_dimensions", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_multi_dimensional_dataset_attrs( + attrs: MultiDimensionalDatasetAttributes, obj: MultiDimensionalDataset +) -> None: + """Populate MultiDimensionalDataset-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.cube_name = obj.cube_name + attrs.cube_qualified_name = obj.cube_qualified_name + attrs.cube_dimension_name = obj.cube_dimension_name + attrs.cube_dimension_qualified_name = obj.cube_dimension_qualified_name + attrs.cube_hierarchy_name = obj.cube_hierarchy_name + attrs.cube_hierarchy_qualified_name = obj.cube_hierarchy_qualified_name + + +def _extract_multi_dimensional_dataset_attrs( + attrs: MultiDimensionalDatasetAttributes, +) -> dict: + """Extract all MultiDimensionalDataset attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["cube_name"] = attrs.cube_name + result["cube_qualified_name"] = attrs.cube_qualified_name + result["cube_dimension_name"] = attrs.cube_dimension_name + result["cube_dimension_qualified_name"] = attrs.cube_dimension_qualified_name + result["cube_hierarchy_name"] = attrs.cube_hierarchy_name + result["cube_hierarchy_qualified_name"] = attrs.cube_hierarchy_qualified_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _multi_dimensional_dataset_to_nested( + multi_dimensional_dataset: MultiDimensionalDataset, +) -> MultiDimensionalDatasetNested: + """Convert flat MultiDimensionalDataset to nested format.""" + attrs = MultiDimensionalDatasetAttributes() + _populate_multi_dimensional_dataset_attrs(attrs, multi_dimensional_dataset) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + multi_dimensional_dataset, + _MULTI_DIMENSIONAL_DATASET_REL_FIELDS, + MultiDimensionalDatasetRelationshipAttributes, + ) + return MultiDimensionalDatasetNested( + guid=multi_dimensional_dataset.guid, + type_name=multi_dimensional_dataset.type_name, + status=multi_dimensional_dataset.status, + version=multi_dimensional_dataset.version, + create_time=multi_dimensional_dataset.create_time, + update_time=multi_dimensional_dataset.update_time, + created_by=multi_dimensional_dataset.created_by, + updated_by=multi_dimensional_dataset.updated_by, + classifications=multi_dimensional_dataset.classifications, + classification_names=multi_dimensional_dataset.classification_names, + meanings=multi_dimensional_dataset.meanings, + labels=multi_dimensional_dataset.labels, + business_attributes=multi_dimensional_dataset.business_attributes, + custom_attributes=multi_dimensional_dataset.custom_attributes, + pending_tasks=multi_dimensional_dataset.pending_tasks, + proxy=multi_dimensional_dataset.proxy, + is_incomplete=multi_dimensional_dataset.is_incomplete, + provenance_type=multi_dimensional_dataset.provenance_type, + home_id=multi_dimensional_dataset.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _multi_dimensional_dataset_from_nested( + nested: MultiDimensionalDatasetNested, +) -> MultiDimensionalDataset: + """Convert nested format to flat MultiDimensionalDataset.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else MultiDimensionalDatasetAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _MULTI_DIMENSIONAL_DATASET_REL_FIELDS, + MultiDimensionalDatasetRelationshipAttributes, + ) + return MultiDimensionalDataset( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_multi_dimensional_dataset_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _multi_dimensional_dataset_to_nested_bytes( + multi_dimensional_dataset: MultiDimensionalDataset, serde: Serde +) -> bytes: + """Convert flat MultiDimensionalDataset to nested JSON bytes.""" + return serde.encode(_multi_dimensional_dataset_to_nested(multi_dimensional_dataset)) + + +def _multi_dimensional_dataset_from_nested_bytes( + data: bytes, serde: Serde +) -> MultiDimensionalDataset: + """Convert nested JSON bytes to flat MultiDimensionalDataset.""" + nested = serde.decode(data, MultiDimensionalDatasetNested) + return _multi_dimensional_dataset_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + RelationField, +) + +MultiDimensionalDataset.CUBE_NAME = KeywordTextField( + "cubeName", "cubeName", "cubeName.text" +) +MultiDimensionalDataset.CUBE_QUALIFIED_NAME = KeywordField( + "cubeQualifiedName", "cubeQualifiedName" +) +MultiDimensionalDataset.CUBE_DIMENSION_NAME = KeywordTextField( + "cubeDimensionName", "cubeDimensionName", "cubeDimensionName.text" +) +MultiDimensionalDataset.CUBE_DIMENSION_QUALIFIED_NAME = KeywordField( + "cubeDimensionQualifiedName", "cubeDimensionQualifiedName" +) +MultiDimensionalDataset.CUBE_HIERARCHY_NAME = KeywordTextField( + "cubeHierarchyName", "cubeHierarchyName", "cubeHierarchyName.text" +) +MultiDimensionalDataset.CUBE_HIERARCHY_QUALIFIED_NAME = KeywordField( + "cubeHierarchyQualifiedName", "cubeHierarchyQualifiedName" +) +MultiDimensionalDataset.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +MultiDimensionalDataset.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +MultiDimensionalDataset.ANOMALO_CHECKS = RelationField("anomaloChecks") +MultiDimensionalDataset.APPLICATION = RelationField("application") +MultiDimensionalDataset.APPLICATION_FIELD = RelationField("applicationField") +MultiDimensionalDataset.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +MultiDimensionalDataset.INPUT_PORT_DATA_PRODUCTS = RelationField( + "inputPortDataProducts" +) +MultiDimensionalDataset.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +MultiDimensionalDataset.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +MultiDimensionalDataset.METRICS = RelationField("metrics") +MultiDimensionalDataset.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +MultiDimensionalDataset.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +MultiDimensionalDataset.MEANINGS = RelationField("meanings") +MultiDimensionalDataset.MC_MONITORS = RelationField("mcMonitors") +MultiDimensionalDataset.MC_INCIDENTS = RelationField("mcIncidents") +MultiDimensionalDataset.CUBE_DIMENSIONS = RelationField("cubeDimensions") +MultiDimensionalDataset.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +MultiDimensionalDataset.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +MultiDimensionalDataset.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +MultiDimensionalDataset.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +MultiDimensionalDataset.USER_DEF_RELATIONSHIP_TO = RelationField( + "userDefRelationshipTo" +) +MultiDimensionalDataset.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +MultiDimensionalDataset.FILES = RelationField("files") +MultiDimensionalDataset.LINKS = RelationField("links") +MultiDimensionalDataset.README = RelationField("readme") +MultiDimensionalDataset.SCHEMA_REGISTRY_SUBJECTS = RelationField( + "schemaRegistrySubjects" +) +MultiDimensionalDataset.SODA_CHECKS = RelationField("sodaChecks") +MultiDimensionalDataset.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +MultiDimensionalDataset.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/namespace.py b/pyatlan_v9/model/assets/namespace.py new file mode 100644 index 000000000..9282d5a35 --- /dev/null +++ b/pyatlan_v9/model/assets/namespace.py @@ -0,0 +1,447 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Namespace asset model with flattened inheritance. + +This module provides: +- Namespace: Flat asset class (easy to use) +- NamespaceAttributes: Nested attributes struct (extends AssetAttributes) +- NamespaceNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .sql_related import RelatedQuery +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .namespace_related import RelatedFolder + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Namespace(Asset): + """ + Base class for query collections and folders. + """ + + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + CHILDREN_FOLDERS: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + CHILDREN_QUERIES: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Namespace" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + children_folders: Union[List[RelatedFolder], None, UnsetType] = UNSET + """Folders that exist within this namespace.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + children_queries: Union[List[RelatedQuery], None, UnsetType] = UNSET + """Queries that exist within this namespace.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Namespace" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _namespace_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Namespace: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Namespace instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _namespace_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class NamespaceAttributes(AssetAttributes): + """Namespace-specific attributes for nested API format.""" + + pass + + +class NamespaceRelationshipAttributes(AssetRelationshipAttributes): + """Namespace-specific relationship attributes for nested API format.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + children_folders: Union[List[RelatedFolder], None, UnsetType] = UNSET + """Folders that exist within this namespace.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + children_queries: Union[List[RelatedQuery], None, UnsetType] = UNSET + """Queries that exist within this namespace.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + +class NamespaceNested(AssetNested): + """Namespace in nested API format for high-performance serialization.""" + + attributes: Union[NamespaceAttributes, UnsetType] = UNSET + relationship_attributes: Union[NamespaceRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + NamespaceRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + NamespaceRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_NAMESPACE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "children_folders", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "children_queries", + "schema_registry_subjects", + "soda_checks", +] + + +def _populate_namespace_attrs(attrs: NamespaceAttributes, obj: Namespace) -> None: + """Populate Namespace-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + + +def _extract_namespace_attrs(attrs: NamespaceAttributes) -> dict: + """Extract all Namespace attributes from the attrs struct into a flat dict.""" + return _extract_asset_attrs(attrs) + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _namespace_to_nested(namespace: Namespace) -> NamespaceNested: + """Convert flat Namespace to nested format.""" + attrs = NamespaceAttributes() + _populate_namespace_attrs(attrs, namespace) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + namespace, _NAMESPACE_REL_FIELDS, NamespaceRelationshipAttributes + ) + return NamespaceNested( + guid=namespace.guid, + type_name=namespace.type_name, + status=namespace.status, + version=namespace.version, + create_time=namespace.create_time, + update_time=namespace.update_time, + created_by=namespace.created_by, + updated_by=namespace.updated_by, + classifications=namespace.classifications, + classification_names=namespace.classification_names, + meanings=namespace.meanings, + labels=namespace.labels, + business_attributes=namespace.business_attributes, + custom_attributes=namespace.custom_attributes, + pending_tasks=namespace.pending_tasks, + proxy=namespace.proxy, + is_incomplete=namespace.is_incomplete, + provenance_type=namespace.provenance_type, + home_id=namespace.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _namespace_from_nested(nested: NamespaceNested) -> Namespace: + """Convert nested format to flat Namespace.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else NamespaceAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _NAMESPACE_REL_FIELDS, + NamespaceRelationshipAttributes, + ) + return Namespace( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_namespace_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _namespace_to_nested_bytes(namespace: Namespace, serde: Serde) -> bytes: + """Convert flat Namespace to nested JSON bytes.""" + return serde.encode(_namespace_to_nested(namespace)) + + +def _namespace_from_nested_bytes(data: bytes, serde: Serde) -> Namespace: + """Convert nested JSON bytes to flat Namespace.""" + nested = serde.decode(data, NamespaceNested) + return _namespace_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import RelationField # noqa: E402 + +Namespace.ANOMALO_CHECKS = RelationField("anomaloChecks") +Namespace.APPLICATION = RelationField("application") +Namespace.APPLICATION_FIELD = RelationField("applicationField") +Namespace.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Namespace.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Namespace.METRICS = RelationField("metrics") +Namespace.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Namespace.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Namespace.MEANINGS = RelationField("meanings") +Namespace.MC_MONITORS = RelationField("mcMonitors") +Namespace.MC_INCIDENTS = RelationField("mcIncidents") +Namespace.CHILDREN_FOLDERS = RelationField("childrenFolders") +Namespace.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Namespace.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Namespace.FILES = RelationField("files") +Namespace.LINKS = RelationField("links") +Namespace.README = RelationField("readme") +Namespace.CHILDREN_QUERIES = RelationField("childrenQueries") +Namespace.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Namespace.SODA_CHECKS = RelationField("sodaChecks") diff --git a/pyatlan_v9/model/assets/namespace_related.py b/pyatlan_v9/model/assets/namespace_related.py new file mode 100644 index 000000000..dd3cc7354 --- /dev/null +++ b/pyatlan_v9/model/assets/namespace_related.py @@ -0,0 +1,82 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Namespace module. + +This module contains all Related{Type} classes for the Namespace type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Union + +from msgspec import UNSET, UnsetType + +from .asset_related import RelatedAsset +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedNamespace", + "RelatedCollection", + "RelatedFolder", +] + + +class RelatedNamespace(RelatedAsset): + """ + Related entity reference for Namespace assets. + + Extends RelatedAsset with Namespace-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Namespace" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Namespace" + + +class RelatedCollection(RelatedNamespace): + """ + Related entity reference for Collection assets. + + Extends RelatedNamespace with Collection-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Collection" so it serializes correctly + + icon: Union[str, None, UnsetType] = UNSET + """Image used to represent this collection.""" + + icon_type: Union[str, None, UnsetType] = UNSET + """Type of image used to represent the collection (for example, an emoji).""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Collection" + + +class RelatedFolder(RelatedNamespace): + """ + Related entity reference for Folder assets. + + Extends RelatedNamespace with Folder-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Folder" so it serializes correctly + + parent_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the parent folder or collection in which this folder exists.""" + + collection_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the collection in which this folder exists.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Folder" diff --git a/pyatlan_v9/model/assets/no_sql.py b/pyatlan_v9/model/assets/no_sql.py new file mode 100644 index 000000000..e8726d083 --- /dev/null +++ b/pyatlan_v9/model/assets/no_sql.py @@ -0,0 +1,542 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +NoSQL asset model with flattened inheritance. + +This module provides: +- NoSQL: Flat asset class (easy to use) +- NoSQLAttributes: Nested attributes struct (extends AssetAttributes) +- NoSQLNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class NoSQL(Asset): + """ + Base class for NoSQL assets. + """ + + NO_SQL_SCHEMA_DEFINITION: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "NoSQL" + + no_sql_schema_definition: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="noSQLSchemaDefinition" + ) + """Represents attributes for describing the key schema for the table and indexes.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "NoSQL" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _no_sql_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> NoSQL: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + NoSQL instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _no_sql_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class NoSQLAttributes(AssetAttributes): + """NoSQL-specific attributes for nested API format.""" + + no_sql_schema_definition: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="noSQLSchemaDefinition" + ) + """Represents attributes for describing the key schema for the table and indexes.""" + + +class NoSQLRelationshipAttributes(AssetRelationshipAttributes): + """NoSQL-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class NoSQLNested(AssetNested): + """NoSQL in nested API format for high-performance serialization.""" + + attributes: Union[NoSQLAttributes, UnsetType] = UNSET + relationship_attributes: Union[NoSQLRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[NoSQLRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[NoSQLRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_NO_SQL_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_no_sql_attrs(attrs: NoSQLAttributes, obj: NoSQL) -> None: + """Populate NoSQL-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.no_sql_schema_definition = obj.no_sql_schema_definition + + +def _extract_no_sql_attrs(attrs: NoSQLAttributes) -> dict: + """Extract all NoSQL attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["no_sql_schema_definition"] = attrs.no_sql_schema_definition + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _no_sql_to_nested(no_sql: NoSQL) -> NoSQLNested: + """Convert flat NoSQL to nested format.""" + attrs = NoSQLAttributes() + _populate_no_sql_attrs(attrs, no_sql) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + no_sql, _NO_SQL_REL_FIELDS, NoSQLRelationshipAttributes + ) + return NoSQLNested( + guid=no_sql.guid, + type_name=no_sql.type_name, + status=no_sql.status, + version=no_sql.version, + create_time=no_sql.create_time, + update_time=no_sql.update_time, + created_by=no_sql.created_by, + updated_by=no_sql.updated_by, + classifications=no_sql.classifications, + classification_names=no_sql.classification_names, + meanings=no_sql.meanings, + labels=no_sql.labels, + business_attributes=no_sql.business_attributes, + custom_attributes=no_sql.custom_attributes, + pending_tasks=no_sql.pending_tasks, + proxy=no_sql.proxy, + is_incomplete=no_sql.is_incomplete, + provenance_type=no_sql.provenance_type, + home_id=no_sql.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _no_sql_from_nested(nested: NoSQLNested) -> NoSQL: + """Convert nested format to flat NoSQL.""" + attrs = nested.attributes if nested.attributes is not UNSET else NoSQLAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _NO_SQL_REL_FIELDS, + NoSQLRelationshipAttributes, + ) + return NoSQL( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_no_sql_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _no_sql_to_nested_bytes(no_sql: NoSQL, serde: Serde) -> bytes: + """Convert flat NoSQL to nested JSON bytes.""" + return serde.encode(_no_sql_to_nested(no_sql)) + + +def _no_sql_from_nested_bytes(data: bytes, serde: Serde) -> NoSQL: + """Convert nested JSON bytes to flat NoSQL.""" + nested = serde.decode(data, NoSQLNested) + return _no_sql_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +NoSQL.NO_SQL_SCHEMA_DEFINITION = KeywordField( + "noSQLSchemaDefinition", "noSQLSchemaDefinition" +) +NoSQL.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +NoSQL.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +NoSQL.ANOMALO_CHECKS = RelationField("anomaloChecks") +NoSQL.APPLICATION = RelationField("application") +NoSQL.APPLICATION_FIELD = RelationField("applicationField") +NoSQL.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +NoSQL.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +NoSQL.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +NoSQL.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +NoSQL.METRICS = RelationField("metrics") +NoSQL.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +NoSQL.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +NoSQL.MEANINGS = RelationField("meanings") +NoSQL.MC_MONITORS = RelationField("mcMonitors") +NoSQL.MC_INCIDENTS = RelationField("mcIncidents") +NoSQL.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +NoSQL.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +NoSQL.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +NoSQL.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +NoSQL.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +NoSQL.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +NoSQL.FILES = RelationField("files") +NoSQL.LINKS = RelationField("links") +NoSQL.README = RelationField("readme") +NoSQL.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +NoSQL.SODA_CHECKS = RelationField("sodaChecks") +NoSQL.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +NoSQL.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/notebook.py b/pyatlan_v9/model/assets/notebook.py new file mode 100644 index 000000000..324b59679 --- /dev/null +++ b/pyatlan_v9/model/assets/notebook.py @@ -0,0 +1,525 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Notebook asset model with flattened inheritance. + +This module provides: +- Notebook: Flat asset class (easy to use) +- NotebookAttributes: Nested attributes struct (extends AssetAttributes) +- NotebookNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Notebook(Asset): + """ + Base class for all notebook assets. + """ + + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Notebook" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Notebook" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _notebook_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Notebook: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Notebook instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _notebook_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class NotebookAttributes(AssetAttributes): + """Notebook-specific attributes for nested API format.""" + + pass + + +class NotebookRelationshipAttributes(AssetRelationshipAttributes): + """Notebook-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class NotebookNested(AssetNested): + """Notebook in nested API format for high-performance serialization.""" + + attributes: Union[NotebookAttributes, UnsetType] = UNSET + relationship_attributes: Union[NotebookRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[NotebookRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[NotebookRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_NOTEBOOK_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_notebook_attrs(attrs: NotebookAttributes, obj: Notebook) -> None: + """Populate Notebook-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + + +def _extract_notebook_attrs(attrs: NotebookAttributes) -> dict: + """Extract all Notebook attributes from the attrs struct into a flat dict.""" + return _extract_asset_attrs(attrs) + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _notebook_to_nested(notebook: Notebook) -> NotebookNested: + """Convert flat Notebook to nested format.""" + attrs = NotebookAttributes() + _populate_notebook_attrs(attrs, notebook) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + notebook, _NOTEBOOK_REL_FIELDS, NotebookRelationshipAttributes + ) + return NotebookNested( + guid=notebook.guid, + type_name=notebook.type_name, + status=notebook.status, + version=notebook.version, + create_time=notebook.create_time, + update_time=notebook.update_time, + created_by=notebook.created_by, + updated_by=notebook.updated_by, + classifications=notebook.classifications, + classification_names=notebook.classification_names, + meanings=notebook.meanings, + labels=notebook.labels, + business_attributes=notebook.business_attributes, + custom_attributes=notebook.custom_attributes, + pending_tasks=notebook.pending_tasks, + proxy=notebook.proxy, + is_incomplete=notebook.is_incomplete, + provenance_type=notebook.provenance_type, + home_id=notebook.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _notebook_from_nested(nested: NotebookNested) -> Notebook: + """Convert nested format to flat Notebook.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else NotebookAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _NOTEBOOK_REL_FIELDS, + NotebookRelationshipAttributes, + ) + return Notebook( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_notebook_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _notebook_to_nested_bytes(notebook: Notebook, serde: Serde) -> bytes: + """Convert flat Notebook to nested JSON bytes.""" + return serde.encode(_notebook_to_nested(notebook)) + + +def _notebook_from_nested_bytes(data: bytes, serde: Serde) -> Notebook: + """Convert nested JSON bytes to flat Notebook.""" + nested = serde.decode(data, NotebookNested) + return _notebook_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import RelationField # noqa: E402 + +Notebook.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Notebook.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Notebook.ANOMALO_CHECKS = RelationField("anomaloChecks") +Notebook.APPLICATION = RelationField("application") +Notebook.APPLICATION_FIELD = RelationField("applicationField") +Notebook.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Notebook.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Notebook.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Notebook.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Notebook.METRICS = RelationField("metrics") +Notebook.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Notebook.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Notebook.MEANINGS = RelationField("meanings") +Notebook.MC_MONITORS = RelationField("mcMonitors") +Notebook.MC_INCIDENTS = RelationField("mcIncidents") +Notebook.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Notebook.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Notebook.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Notebook.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Notebook.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Notebook.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Notebook.FILES = RelationField("files") +Notebook.LINKS = RelationField("links") +Notebook.README = RelationField("readme") +Notebook.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Notebook.SODA_CHECKS = RelationField("sodaChecks") +Notebook.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Notebook.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/notebook_related.py b/pyatlan_v9/model/assets/notebook_related.py new file mode 100644 index 000000000..b6981d931 --- /dev/null +++ b/pyatlan_v9/model/assets/notebook_related.py @@ -0,0 +1,35 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Notebook module. + +This module contains all Related{Type} classes for the Notebook type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + + +from .catalog_related import RelatedCatalog +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedNotebook", +] + + +class RelatedNotebook(RelatedCatalog): + """ + Related entity reference for Notebook assets. + + Extends RelatedCatalog with Notebook-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Notebook" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Notebook" diff --git a/pyatlan_v9/model/assets/object_store.py b/pyatlan_v9/model/assets/object_store.py new file mode 100644 index 000000000..b969a1479 --- /dev/null +++ b/pyatlan_v9/model/assets/object_store.py @@ -0,0 +1,527 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +ObjectStore asset model with flattened inheritance. + +This module provides: +- ObjectStore: Flat asset class (easy to use) +- ObjectStoreAttributes: Nested attributes struct (extends AssetAttributes) +- ObjectStoreNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class ObjectStore(Asset): + """ + Base class for object store assets. + """ + + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "ObjectStore" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "ObjectStore" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _object_store_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> ObjectStore: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + ObjectStore instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _object_store_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class ObjectStoreAttributes(AssetAttributes): + """ObjectStore-specific attributes for nested API format.""" + + pass + + +class ObjectStoreRelationshipAttributes(AssetRelationshipAttributes): + """ObjectStore-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class ObjectStoreNested(AssetNested): + """ObjectStore in nested API format for high-performance serialization.""" + + attributes: Union[ObjectStoreAttributes, UnsetType] = UNSET + relationship_attributes: Union[ObjectStoreRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + ObjectStoreRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + ObjectStoreRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_OBJECT_STORE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_object_store_attrs( + attrs: ObjectStoreAttributes, obj: ObjectStore +) -> None: + """Populate ObjectStore-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + + +def _extract_object_store_attrs(attrs: ObjectStoreAttributes) -> dict: + """Extract all ObjectStore attributes from the attrs struct into a flat dict.""" + return _extract_asset_attrs(attrs) + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _object_store_to_nested(object_store: ObjectStore) -> ObjectStoreNested: + """Convert flat ObjectStore to nested format.""" + attrs = ObjectStoreAttributes() + _populate_object_store_attrs(attrs, object_store) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + object_store, _OBJECT_STORE_REL_FIELDS, ObjectStoreRelationshipAttributes + ) + return ObjectStoreNested( + guid=object_store.guid, + type_name=object_store.type_name, + status=object_store.status, + version=object_store.version, + create_time=object_store.create_time, + update_time=object_store.update_time, + created_by=object_store.created_by, + updated_by=object_store.updated_by, + classifications=object_store.classifications, + classification_names=object_store.classification_names, + meanings=object_store.meanings, + labels=object_store.labels, + business_attributes=object_store.business_attributes, + custom_attributes=object_store.custom_attributes, + pending_tasks=object_store.pending_tasks, + proxy=object_store.proxy, + is_incomplete=object_store.is_incomplete, + provenance_type=object_store.provenance_type, + home_id=object_store.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _object_store_from_nested(nested: ObjectStoreNested) -> ObjectStore: + """Convert nested format to flat ObjectStore.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else ObjectStoreAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _OBJECT_STORE_REL_FIELDS, + ObjectStoreRelationshipAttributes, + ) + return ObjectStore( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_object_store_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _object_store_to_nested_bytes(object_store: ObjectStore, serde: Serde) -> bytes: + """Convert flat ObjectStore to nested JSON bytes.""" + return serde.encode(_object_store_to_nested(object_store)) + + +def _object_store_from_nested_bytes(data: bytes, serde: Serde) -> ObjectStore: + """Convert nested JSON bytes to flat ObjectStore.""" + nested = serde.decode(data, ObjectStoreNested) + return _object_store_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import RelationField # noqa: E402 + +ObjectStore.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +ObjectStore.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +ObjectStore.ANOMALO_CHECKS = RelationField("anomaloChecks") +ObjectStore.APPLICATION = RelationField("application") +ObjectStore.APPLICATION_FIELD = RelationField("applicationField") +ObjectStore.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +ObjectStore.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +ObjectStore.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +ObjectStore.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +ObjectStore.METRICS = RelationField("metrics") +ObjectStore.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +ObjectStore.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +ObjectStore.MEANINGS = RelationField("meanings") +ObjectStore.MC_MONITORS = RelationField("mcMonitors") +ObjectStore.MC_INCIDENTS = RelationField("mcIncidents") +ObjectStore.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +ObjectStore.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +ObjectStore.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +ObjectStore.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +ObjectStore.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +ObjectStore.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +ObjectStore.FILES = RelationField("files") +ObjectStore.LINKS = RelationField("links") +ObjectStore.README = RelationField("readme") +ObjectStore.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +ObjectStore.SODA_CHECKS = RelationField("sodaChecks") +ObjectStore.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +ObjectStore.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/orchestration_related.py b/pyatlan_v9/model/assets/orchestration_related.py new file mode 100644 index 000000000..9cf6b2771 --- /dev/null +++ b/pyatlan_v9/model/assets/orchestration_related.py @@ -0,0 +1,15 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Orchestration module. + +This module contains all Related{Type} classes for the Orchestration type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + + +__all__ = [] diff --git a/pyatlan_v9/model/assets/partial.py b/pyatlan_v9/model/assets/partial.py new file mode 100644 index 000000000..cb60c35fc --- /dev/null +++ b/pyatlan_v9/model/assets/partial.py @@ -0,0 +1,591 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Partial asset model with flattened inheritance. + +This module provides: +- Partial: Flat asset class (easy to use) +- PartialAttributes: Nested attributes struct (extends AssetAttributes) +- PartialNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .partial_related import RelatedPartialField, RelatedPartialObject + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Partial(Asset): + """ + Base class representing assets that cannot be resolved to a pre-existing asset. + """ + + PARTIAL_STRUCTURE_JSON: ClassVar[Any] = None + PARTIAL_RESOLVED_TYPE_NAME: ClassVar[Any] = None + PARTIAL_UNKNOWN_ATTRIBUTES_HASH_ID: ClassVar[Any] = None + PARTIAL_PARENT_TYPE: ClassVar[Any] = None + PARTIAL_PARENT_QUALIFIED_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Partial" + + partial_structure_json: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="partialStructureJSON" + ) + """Complete JSON structure of this partial asset, as a string.""" + + partial_resolved_type_name: Union[str, None, UnsetType] = UNSET + """Atlan-mapped type name of this partial asset.""" + + partial_unknown_attributes_hash_id: Union[str, None, UnsetType] = UNSET + """Hash ID of the unknown attributes for this partial asset.""" + + partial_parent_type: Union[str, None, UnsetType] = UNSET + """Type of the field's parent asset.""" + + partial_parent_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the field's parent asset.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Partial" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _partial_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Partial: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Partial instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _partial_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class PartialAttributes(AssetAttributes): + """Partial-specific attributes for nested API format.""" + + partial_structure_json: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="partialStructureJSON" + ) + """Complete JSON structure of this partial asset, as a string.""" + + partial_resolved_type_name: Union[str, None, UnsetType] = UNSET + """Atlan-mapped type name of this partial asset.""" + + partial_unknown_attributes_hash_id: Union[str, None, UnsetType] = UNSET + """Hash ID of the unknown attributes for this partial asset.""" + + partial_parent_type: Union[str, None, UnsetType] = UNSET + """Type of the field's parent asset.""" + + partial_parent_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the field's parent asset.""" + + +class PartialRelationshipAttributes(AssetRelationshipAttributes): + """Partial-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class PartialNested(AssetNested): + """Partial in nested API format for high-performance serialization.""" + + attributes: Union[PartialAttributes, UnsetType] = UNSET + relationship_attributes: Union[PartialRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[PartialRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[PartialRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_PARTIAL_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_partial_attrs(attrs: PartialAttributes, obj: Partial) -> None: + """Populate Partial-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.partial_structure_json = obj.partial_structure_json + attrs.partial_resolved_type_name = obj.partial_resolved_type_name + attrs.partial_unknown_attributes_hash_id = obj.partial_unknown_attributes_hash_id + attrs.partial_parent_type = obj.partial_parent_type + attrs.partial_parent_qualified_name = obj.partial_parent_qualified_name + + +def _extract_partial_attrs(attrs: PartialAttributes) -> dict: + """Extract all Partial attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["partial_structure_json"] = attrs.partial_structure_json + result["partial_resolved_type_name"] = attrs.partial_resolved_type_name + result["partial_unknown_attributes_hash_id"] = ( + attrs.partial_unknown_attributes_hash_id + ) + result["partial_parent_type"] = attrs.partial_parent_type + result["partial_parent_qualified_name"] = attrs.partial_parent_qualified_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _partial_to_nested(partial: Partial) -> PartialNested: + """Convert flat Partial to nested format.""" + attrs = PartialAttributes() + _populate_partial_attrs(attrs, partial) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + partial, _PARTIAL_REL_FIELDS, PartialRelationshipAttributes + ) + return PartialNested( + guid=partial.guid, + type_name=partial.type_name, + status=partial.status, + version=partial.version, + create_time=partial.create_time, + update_time=partial.update_time, + created_by=partial.created_by, + updated_by=partial.updated_by, + classifications=partial.classifications, + classification_names=partial.classification_names, + meanings=partial.meanings, + labels=partial.labels, + business_attributes=partial.business_attributes, + custom_attributes=partial.custom_attributes, + pending_tasks=partial.pending_tasks, + proxy=partial.proxy, + is_incomplete=partial.is_incomplete, + provenance_type=partial.provenance_type, + home_id=partial.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _partial_from_nested(nested: PartialNested) -> Partial: + """Convert nested format to flat Partial.""" + attrs = nested.attributes if nested.attributes is not UNSET else PartialAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _PARTIAL_REL_FIELDS, + PartialRelationshipAttributes, + ) + return Partial( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_partial_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _partial_to_nested_bytes(partial: Partial, serde: Serde) -> bytes: + """Convert flat Partial to nested JSON bytes.""" + return serde.encode(_partial_to_nested(partial)) + + +def _partial_from_nested_bytes(data: bytes, serde: Serde) -> Partial: + """Convert nested JSON bytes to flat Partial.""" + nested = serde.decode(data, PartialNested) + return _partial_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +Partial.PARTIAL_STRUCTURE_JSON = KeywordField( + "partialStructureJSON", "partialStructureJSON" +) +Partial.PARTIAL_RESOLVED_TYPE_NAME = KeywordField( + "partialResolvedTypeName", "partialResolvedTypeName" +) +Partial.PARTIAL_UNKNOWN_ATTRIBUTES_HASH_ID = KeywordField( + "partialUnknownAttributesHashId", "partialUnknownAttributesHashId" +) +Partial.PARTIAL_PARENT_TYPE = KeywordField("partialParentType", "partialParentType") +Partial.PARTIAL_PARENT_QUALIFIED_NAME = KeywordField( + "partialParentQualifiedName", "partialParentQualifiedName" +) +Partial.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Partial.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Partial.ANOMALO_CHECKS = RelationField("anomaloChecks") +Partial.APPLICATION = RelationField("application") +Partial.APPLICATION_FIELD = RelationField("applicationField") +Partial.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Partial.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Partial.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Partial.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Partial.METRICS = RelationField("metrics") +Partial.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Partial.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Partial.MEANINGS = RelationField("meanings") +Partial.MC_MONITORS = RelationField("mcMonitors") +Partial.MC_INCIDENTS = RelationField("mcIncidents") +Partial.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Partial.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Partial.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Partial.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Partial.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Partial.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Partial.FILES = RelationField("files") +Partial.LINKS = RelationField("links") +Partial.README = RelationField("readme") +Partial.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Partial.SODA_CHECKS = RelationField("sodaChecks") +Partial.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Partial.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/partial_field.py b/pyatlan_v9/model/assets/partial_field.py new file mode 100644 index 000000000..9058ada19 --- /dev/null +++ b/pyatlan_v9/model/assets/partial_field.py @@ -0,0 +1,628 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +PartialField asset model with flattened inheritance. + +This module provides: +- PartialField: Flat asset class (easy to use) +- PartialFieldAttributes: Nested attributes struct (extends AssetAttributes) +- PartialFieldNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .catalog_related import RelatedCatalog +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .partial_related import RelatedPartialField, RelatedPartialObject + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class PartialField(Asset): + """ + Field-level assets that could not be resolved to a pre-existing asset. + """ + + PARTIAL_DATA_TYPE: ClassVar[Any] = None + PARTIAL_STRUCTURE_JSON: ClassVar[Any] = None + PARTIAL_RESOLVED_TYPE_NAME: ClassVar[Any] = None + PARTIAL_UNKNOWN_ATTRIBUTES_HASH_ID: ClassVar[Any] = None + PARTIAL_PARENT_TYPE: ClassVar[Any] = None + PARTIAL_PARENT_QUALIFIED_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_PARENT_ASSET: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "PartialField" + + partial_data_type: Union[str, None, UnsetType] = UNSET + """Type of data captured as values in the field.""" + + partial_structure_json: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="partialStructureJSON" + ) + """Complete JSON structure of this partial asset, as a string.""" + + partial_resolved_type_name: Union[str, None, UnsetType] = UNSET + """Atlan-mapped type name of this partial asset.""" + + partial_unknown_attributes_hash_id: Union[str, None, UnsetType] = UNSET + """Hash ID of the unknown attributes for this partial asset.""" + + partial_parent_type: Union[str, None, UnsetType] = UNSET + """Type of the field's parent asset.""" + + partial_parent_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the field's parent asset.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_parent_asset: Union[RelatedCatalog, None, UnsetType] = UNSET + """Parent asset containing partial field.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "PartialField" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _partial_field_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> PartialField: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + PartialField instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _partial_field_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class PartialFieldAttributes(AssetAttributes): + """PartialField-specific attributes for nested API format.""" + + partial_data_type: Union[str, None, UnsetType] = UNSET + """Type of data captured as values in the field.""" + + partial_structure_json: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="partialStructureJSON" + ) + """Complete JSON structure of this partial asset, as a string.""" + + partial_resolved_type_name: Union[str, None, UnsetType] = UNSET + """Atlan-mapped type name of this partial asset.""" + + partial_unknown_attributes_hash_id: Union[str, None, UnsetType] = UNSET + """Hash ID of the unknown attributes for this partial asset.""" + + partial_parent_type: Union[str, None, UnsetType] = UNSET + """Type of the field's parent asset.""" + + partial_parent_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the field's parent asset.""" + + +class PartialFieldRelationshipAttributes(AssetRelationshipAttributes): + """PartialField-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_parent_asset: Union[RelatedCatalog, None, UnsetType] = UNSET + """Parent asset containing partial field.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class PartialFieldNested(AssetNested): + """PartialField in nested API format for high-performance serialization.""" + + attributes: Union[PartialFieldAttributes, UnsetType] = UNSET + relationship_attributes: Union[PartialFieldRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + PartialFieldRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + PartialFieldRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_PARTIAL_FIELD_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_parent_asset", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_partial_field_attrs( + attrs: PartialFieldAttributes, obj: PartialField +) -> None: + """Populate PartialField-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.partial_data_type = obj.partial_data_type + attrs.partial_structure_json = obj.partial_structure_json + attrs.partial_resolved_type_name = obj.partial_resolved_type_name + attrs.partial_unknown_attributes_hash_id = obj.partial_unknown_attributes_hash_id + attrs.partial_parent_type = obj.partial_parent_type + attrs.partial_parent_qualified_name = obj.partial_parent_qualified_name + + +def _extract_partial_field_attrs(attrs: PartialFieldAttributes) -> dict: + """Extract all PartialField attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["partial_data_type"] = attrs.partial_data_type + result["partial_structure_json"] = attrs.partial_structure_json + result["partial_resolved_type_name"] = attrs.partial_resolved_type_name + result["partial_unknown_attributes_hash_id"] = ( + attrs.partial_unknown_attributes_hash_id + ) + result["partial_parent_type"] = attrs.partial_parent_type + result["partial_parent_qualified_name"] = attrs.partial_parent_qualified_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _partial_field_to_nested(partial_field: PartialField) -> PartialFieldNested: + """Convert flat PartialField to nested format.""" + attrs = PartialFieldAttributes() + _populate_partial_field_attrs(attrs, partial_field) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + partial_field, _PARTIAL_FIELD_REL_FIELDS, PartialFieldRelationshipAttributes + ) + return PartialFieldNested( + guid=partial_field.guid, + type_name=partial_field.type_name, + status=partial_field.status, + version=partial_field.version, + create_time=partial_field.create_time, + update_time=partial_field.update_time, + created_by=partial_field.created_by, + updated_by=partial_field.updated_by, + classifications=partial_field.classifications, + classification_names=partial_field.classification_names, + meanings=partial_field.meanings, + labels=partial_field.labels, + business_attributes=partial_field.business_attributes, + custom_attributes=partial_field.custom_attributes, + pending_tasks=partial_field.pending_tasks, + proxy=partial_field.proxy, + is_incomplete=partial_field.is_incomplete, + provenance_type=partial_field.provenance_type, + home_id=partial_field.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _partial_field_from_nested(nested: PartialFieldNested) -> PartialField: + """Convert nested format to flat PartialField.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else PartialFieldAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _PARTIAL_FIELD_REL_FIELDS, + PartialFieldRelationshipAttributes, + ) + return PartialField( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_partial_field_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _partial_field_to_nested_bytes(partial_field: PartialField, serde: Serde) -> bytes: + """Convert flat PartialField to nested JSON bytes.""" + return serde.encode(_partial_field_to_nested(partial_field)) + + +def _partial_field_from_nested_bytes(data: bytes, serde: Serde) -> PartialField: + """Convert nested JSON bytes to flat PartialField.""" + nested = serde.decode(data, PartialFieldNested) + return _partial_field_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +PartialField.PARTIAL_DATA_TYPE = KeywordField("partialDataType", "partialDataType") +PartialField.PARTIAL_STRUCTURE_JSON = KeywordField( + "partialStructureJSON", "partialStructureJSON" +) +PartialField.PARTIAL_RESOLVED_TYPE_NAME = KeywordField( + "partialResolvedTypeName", "partialResolvedTypeName" +) +PartialField.PARTIAL_UNKNOWN_ATTRIBUTES_HASH_ID = KeywordField( + "partialUnknownAttributesHashId", "partialUnknownAttributesHashId" +) +PartialField.PARTIAL_PARENT_TYPE = KeywordField( + "partialParentType", "partialParentType" +) +PartialField.PARTIAL_PARENT_QUALIFIED_NAME = KeywordField( + "partialParentQualifiedName", "partialParentQualifiedName" +) +PartialField.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +PartialField.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +PartialField.ANOMALO_CHECKS = RelationField("anomaloChecks") +PartialField.APPLICATION = RelationField("application") +PartialField.APPLICATION_FIELD = RelationField("applicationField") +PartialField.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +PartialField.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +PartialField.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +PartialField.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +PartialField.METRICS = RelationField("metrics") +PartialField.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +PartialField.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +PartialField.MEANINGS = RelationField("meanings") +PartialField.MC_MONITORS = RelationField("mcMonitors") +PartialField.MC_INCIDENTS = RelationField("mcIncidents") +PartialField.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +PartialField.PARTIAL_PARENT_ASSET = RelationField("partialParentAsset") +PartialField.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +PartialField.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +PartialField.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +PartialField.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +PartialField.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +PartialField.FILES = RelationField("files") +PartialField.LINKS = RelationField("links") +PartialField.README = RelationField("readme") +PartialField.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +PartialField.SODA_CHECKS = RelationField("sodaChecks") +PartialField.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +PartialField.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/partial_object.py b/pyatlan_v9/model/assets/partial_object.py new file mode 100644 index 000000000..bf3ae289d --- /dev/null +++ b/pyatlan_v9/model/assets/partial_object.py @@ -0,0 +1,620 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +PartialObject asset model with flattened inheritance. + +This module provides: +- PartialObject: Flat asset class (easy to use) +- PartialObjectAttributes: Nested attributes struct (extends AssetAttributes) +- PartialObjectNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .catalog_related import RelatedCatalog +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .partial_related import RelatedPartialField, RelatedPartialObject + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class PartialObject(Asset): + """ + Object-level assets that could not be resolved to a pre-existing asset. + """ + + PARTIAL_STRUCTURE_JSON: ClassVar[Any] = None + PARTIAL_RESOLVED_TYPE_NAME: ClassVar[Any] = None + PARTIAL_UNKNOWN_ATTRIBUTES_HASH_ID: ClassVar[Any] = None + PARTIAL_PARENT_TYPE: ClassVar[Any] = None + PARTIAL_PARENT_QUALIFIED_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + PARTIAL_PARENT_ASSET: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "PartialObject" + + partial_structure_json: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="partialStructureJSON" + ) + """Complete JSON structure of this partial asset, as a string.""" + + partial_resolved_type_name: Union[str, None, UnsetType] = UNSET + """Atlan-mapped type name of this partial asset.""" + + partial_unknown_attributes_hash_id: Union[str, None, UnsetType] = UNSET + """Hash ID of the unknown attributes for this partial asset.""" + + partial_parent_type: Union[str, None, UnsetType] = UNSET + """Type of the field's parent asset.""" + + partial_parent_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the field's parent asset.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + partial_parent_asset: Union[RelatedCatalog, None, UnsetType] = UNSET + """Parent asset containing partial object.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "PartialObject" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _partial_object_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> PartialObject: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + PartialObject instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _partial_object_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class PartialObjectAttributes(AssetAttributes): + """PartialObject-specific attributes for nested API format.""" + + partial_structure_json: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="partialStructureJSON" + ) + """Complete JSON structure of this partial asset, as a string.""" + + partial_resolved_type_name: Union[str, None, UnsetType] = UNSET + """Atlan-mapped type name of this partial asset.""" + + partial_unknown_attributes_hash_id: Union[str, None, UnsetType] = UNSET + """Hash ID of the unknown attributes for this partial asset.""" + + partial_parent_type: Union[str, None, UnsetType] = UNSET + """Type of the field's parent asset.""" + + partial_parent_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the field's parent asset.""" + + +class PartialObjectRelationshipAttributes(AssetRelationshipAttributes): + """PartialObject-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + partial_parent_asset: Union[RelatedCatalog, None, UnsetType] = UNSET + """Parent asset containing partial object.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class PartialObjectNested(AssetNested): + """PartialObject in nested API format for high-performance serialization.""" + + attributes: Union[PartialObjectAttributes, UnsetType] = UNSET + relationship_attributes: Union[PartialObjectRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + PartialObjectRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + PartialObjectRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_PARTIAL_OBJECT_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "partial_parent_asset", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_partial_object_attrs( + attrs: PartialObjectAttributes, obj: PartialObject +) -> None: + """Populate PartialObject-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.partial_structure_json = obj.partial_structure_json + attrs.partial_resolved_type_name = obj.partial_resolved_type_name + attrs.partial_unknown_attributes_hash_id = obj.partial_unknown_attributes_hash_id + attrs.partial_parent_type = obj.partial_parent_type + attrs.partial_parent_qualified_name = obj.partial_parent_qualified_name + + +def _extract_partial_object_attrs(attrs: PartialObjectAttributes) -> dict: + """Extract all PartialObject attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["partial_structure_json"] = attrs.partial_structure_json + result["partial_resolved_type_name"] = attrs.partial_resolved_type_name + result["partial_unknown_attributes_hash_id"] = ( + attrs.partial_unknown_attributes_hash_id + ) + result["partial_parent_type"] = attrs.partial_parent_type + result["partial_parent_qualified_name"] = attrs.partial_parent_qualified_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _partial_object_to_nested(partial_object: PartialObject) -> PartialObjectNested: + """Convert flat PartialObject to nested format.""" + attrs = PartialObjectAttributes() + _populate_partial_object_attrs(attrs, partial_object) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + partial_object, _PARTIAL_OBJECT_REL_FIELDS, PartialObjectRelationshipAttributes + ) + return PartialObjectNested( + guid=partial_object.guid, + type_name=partial_object.type_name, + status=partial_object.status, + version=partial_object.version, + create_time=partial_object.create_time, + update_time=partial_object.update_time, + created_by=partial_object.created_by, + updated_by=partial_object.updated_by, + classifications=partial_object.classifications, + classification_names=partial_object.classification_names, + meanings=partial_object.meanings, + labels=partial_object.labels, + business_attributes=partial_object.business_attributes, + custom_attributes=partial_object.custom_attributes, + pending_tasks=partial_object.pending_tasks, + proxy=partial_object.proxy, + is_incomplete=partial_object.is_incomplete, + provenance_type=partial_object.provenance_type, + home_id=partial_object.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _partial_object_from_nested(nested: PartialObjectNested) -> PartialObject: + """Convert nested format to flat PartialObject.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else PartialObjectAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _PARTIAL_OBJECT_REL_FIELDS, + PartialObjectRelationshipAttributes, + ) + return PartialObject( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_partial_object_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _partial_object_to_nested_bytes( + partial_object: PartialObject, serde: Serde +) -> bytes: + """Convert flat PartialObject to nested JSON bytes.""" + return serde.encode(_partial_object_to_nested(partial_object)) + + +def _partial_object_from_nested_bytes(data: bytes, serde: Serde) -> PartialObject: + """Convert nested JSON bytes to flat PartialObject.""" + nested = serde.decode(data, PartialObjectNested) + return _partial_object_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +PartialObject.PARTIAL_STRUCTURE_JSON = KeywordField( + "partialStructureJSON", "partialStructureJSON" +) +PartialObject.PARTIAL_RESOLVED_TYPE_NAME = KeywordField( + "partialResolvedTypeName", "partialResolvedTypeName" +) +PartialObject.PARTIAL_UNKNOWN_ATTRIBUTES_HASH_ID = KeywordField( + "partialUnknownAttributesHashId", "partialUnknownAttributesHashId" +) +PartialObject.PARTIAL_PARENT_TYPE = KeywordField( + "partialParentType", "partialParentType" +) +PartialObject.PARTIAL_PARENT_QUALIFIED_NAME = KeywordField( + "partialParentQualifiedName", "partialParentQualifiedName" +) +PartialObject.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +PartialObject.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +PartialObject.ANOMALO_CHECKS = RelationField("anomaloChecks") +PartialObject.APPLICATION = RelationField("application") +PartialObject.APPLICATION_FIELD = RelationField("applicationField") +PartialObject.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +PartialObject.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +PartialObject.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +PartialObject.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +PartialObject.METRICS = RelationField("metrics") +PartialObject.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +PartialObject.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +PartialObject.MEANINGS = RelationField("meanings") +PartialObject.MC_MONITORS = RelationField("mcMonitors") +PartialObject.MC_INCIDENTS = RelationField("mcIncidents") +PartialObject.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +PartialObject.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +PartialObject.PARTIAL_PARENT_ASSET = RelationField("partialParentAsset") +PartialObject.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +PartialObject.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +PartialObject.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +PartialObject.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +PartialObject.FILES = RelationField("files") +PartialObject.LINKS = RelationField("links") +PartialObject.README = RelationField("readme") +PartialObject.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +PartialObject.SODA_CHECKS = RelationField("sodaChecks") +PartialObject.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +PartialObject.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/partial_related.py b/pyatlan_v9/model/assets/partial_related.py new file mode 100644 index 000000000..720535f94 --- /dev/null +++ b/pyatlan_v9/model/assets/partial_related.py @@ -0,0 +1,91 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Partial module. + +This module contains all Related{Type} classes for the Partial type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedCatalog +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedPartial", + "RelatedPartialObject", + "RelatedPartialField", +] + + +class RelatedPartial(RelatedCatalog): + """ + Related entity reference for Partial assets. + + Extends RelatedCatalog with Partial-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Partial" so it serializes correctly + + partial_structure_json: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="partialStructureJSON" + ) + """Complete JSON structure of this partial asset, as a string.""" + + partial_resolved_type_name: Union[str, None, UnsetType] = UNSET + """Atlan-mapped type name of this partial asset.""" + + partial_unknown_attributes_hash_id: Union[str, None, UnsetType] = UNSET + """Hash ID of the unknown attributes for this partial asset.""" + + partial_parent_type: Union[str, None, UnsetType] = UNSET + """Type of the field's parent asset.""" + + partial_parent_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the field's parent asset.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Partial" + + +class RelatedPartialObject(RelatedPartial): + """ + Related entity reference for PartialObject assets. + + Extends RelatedPartial with PartialObject-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "PartialObject" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "PartialObject" + + +class RelatedPartialField(RelatedPartial): + """ + Related entity reference for PartialField assets. + + Extends RelatedPartial with PartialField-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "PartialField" so it serializes correctly + + partial_data_type: Union[str, None, UnsetType] = UNSET + """Type of data captured as values in the field.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "PartialField" diff --git a/pyatlan_v9/model/assets/persona.py b/pyatlan_v9/model/assets/persona.py new file mode 100644 index 000000000..31cfd4ccd --- /dev/null +++ b/pyatlan_v9/model/assets/persona.py @@ -0,0 +1,339 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2026 Atlan Pte. Ltd. + +"""Persona asset model for pyatlan_v9.""" + +from __future__ import annotations + +from typing import Any, ClassVar, Set, Union +from warnings import warn + +from msgspec import UNSET, UnsetType + +from pyatlan.model.enums import ( + AuthPolicyCategory, + AuthPolicyResourceCategory, + AuthPolicyType, + DataAction, + PersonaDomainAction, + PersonaGlossaryAction, + PersonaMetadataAction, +) +from pyatlan_v9.model.conversion_utils import ( + build_attributes_kwargs, + build_flat_kwargs, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .asset import Asset, AssetAttributes, AssetNested +from .auth_policy import AuthPolicy + + +@register_asset +class Persona(Asset): + """Persona asset in Atlan — an access-control construct scoping + visibility for users/groups across connections and glossaries.""" + + PERSONA_GROUPS: ClassVar[Any] = None + PERSONA_USERS: ClassVar[Any] = None + ROLE_ID: ClassVar[Any] = None + IS_ACCESS_CONTROL_ENABLED: ClassVar[Any] = None + DENY_CUSTOM_METADATA_GUIDS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Persona" + persona_groups: Union[Set[str], None, UnsetType] = UNSET + persona_users: Union[Set[str], None, UnsetType] = UNSET + role_id: Union[str, None, UnsetType] = UNSET + is_access_control_enabled: Union[bool, None, UnsetType] = UNSET + deny_custom_metadata_guids: Union[Set[str], None, UnsetType] = UNSET + deny_asset_tabs: Union[Set[str], None, UnsetType] = UNSET + deny_asset_filters: Union[Set[str], None, UnsetType] = UNSET + deny_asset_types: Union[Set[str], None, UnsetType] = UNSET + deny_sidebar_tabs: Union[Set[str], None, UnsetType] = UNSET + deny_navigation_pages: Union[Set[str], None, UnsetType] = UNSET + default_navigation: Union[str, None, UnsetType] = UNSET + display_preferences: Union[Set[str], None, UnsetType] = UNSET + channel_link: Union[str, None, UnsetType] = UNSET + deny_asset_metadata_types: Union[Set[str], None, UnsetType] = UNSET + policies: Union[list[AuthPolicy], None, UnsetType] = UNSET + + @classmethod + @init_guid + def creator(cls, *, name: str) -> "Persona": + validate_required_fields(["name"], [name]) + return cls( + qualified_name=name, + name=name, + display_name=name, + is_access_control_enabled=True, + description="", + ) + + @classmethod + def updater( + cls, *, qualified_name: str, name: str, is_enabled: bool = True + ) -> "Persona": + validate_required_fields( + ["name", "qualified_name", "is_enabled"], + [name, qualified_name, is_enabled], + ) + return cls( + qualified_name=qualified_name, + name=name, + is_access_control_enabled=is_enabled, + ) + + @classmethod + def create_for_modification( + cls, + qualified_name: str = "", + name: str = "", + is_enabled: bool = True, + ) -> "Persona": + warn( + ( + "This method is deprecated, please use 'updater' " + "instead, which offers identical functionality." + ), + DeprecationWarning, + stacklevel=2, + ) + return cls.updater( + qualified_name=qualified_name, name=name, is_enabled=is_enabled + ) + + @classmethod + def create_metadata_policy( + cls, + *, + name: str, + persona_id: str, + policy_type: AuthPolicyType, + actions: Set[PersonaMetadataAction], + connection_qualified_name: str, + resources: Set[str], + ) -> AuthPolicy: + validate_required_fields( + ["name", "persona_id", "policy_type", "actions", "resources"], + [name, persona_id, policy_type, actions, resources], + ) + policy = AuthPolicy._create(name=name) + policy.policy_actions = {x.value for x in actions} + policy.policy_category = AuthPolicyCategory.PERSONA.value + policy.policy_type = policy_type + policy.connection_qualified_name = connection_qualified_name + policy.policy_resources = resources + policy.policy_resource_category = AuthPolicyResourceCategory.CUSTOM.value + policy.policy_service_name = "atlas" + policy.policy_sub_category = "metadata" + persona = Persona() + persona.guid = persona_id + policy.access_control = persona + return policy + + @classmethod + def create_data_policy( + cls, + *, + name: str, + persona_id: str, + policy_type: AuthPolicyType, + connection_qualified_name: str, + resources: Set[str], + ) -> AuthPolicy: + validate_required_fields( + ["name", "persona_id", "policy_type", "resources"], + [name, persona_id, policy_type, resources], + ) + policy = AuthPolicy._create(name=name) + policy.policy_actions = {DataAction.SELECT.value} + policy.policy_category = AuthPolicyCategory.PERSONA.value + policy.policy_type = policy_type + policy.connection_qualified_name = connection_qualified_name + policy.policy_resources = resources + policy.policy_resources.add("entity-type:*") + policy.policy_resource_category = AuthPolicyResourceCategory.ENTITY.value + policy.policy_service_name = "heka" + policy.policy_sub_category = "data" + persona = Persona() + persona.guid = persona_id + policy.access_control = persona + return policy + + @classmethod + def create_glossary_policy( + cls, + *, + name: str, + persona_id: str, + policy_type: AuthPolicyType, + actions: Set[PersonaGlossaryAction], + resources: Set[str], + ) -> AuthPolicy: + validate_required_fields( + ["name", "persona_id", "policy_type", "actions", "resources"], + [name, persona_id, policy_type, actions, resources], + ) + policy = AuthPolicy._create(name=name) + policy.policy_actions = {x.value for x in actions} + policy.policy_category = AuthPolicyCategory.PERSONA.value + policy.policy_type = policy_type + policy.policy_resources = resources + policy.policy_resource_category = AuthPolicyResourceCategory.CUSTOM.value + policy.policy_service_name = "atlas" + policy.policy_sub_category = "glossary" + persona = Persona() + persona.guid = persona_id + policy.access_control = persona + return policy + + @classmethod + def create_domain_policy( + cls, + *, + name: str, + persona_id: str, + actions: Set[PersonaDomainAction], + resources: Set[str], + ) -> AuthPolicy: + validate_required_fields( + ["name", "persona_id", "actions", "resources"], + [name, persona_id, actions, resources], + ) + policy = AuthPolicy._create(name=name) + policy.policy_actions = {x.value for x in actions} + policy.policy_category = AuthPolicyCategory.PERSONA.value + policy.policy_type = AuthPolicyType.ALLOW + policy.policy_resources = resources + policy.policy_resource_category = AuthPolicyResourceCategory.CUSTOM.value + policy.policy_service_name = "atlas" + policy.policy_sub_category = "domain" + persona = Persona() + persona.guid = persona_id + policy.access_control = persona + return policy + + def trim_to_required(self) -> "Persona": + return Persona.updater(qualified_name=self.qualified_name, name=self.name) + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + if serde is None: + serde = get_serde() + if nested: + return _persona_to_nested_bytes(self, serde).decode("utf-8") + return serde.encode(self).decode("utf-8") + + @staticmethod + def from_json( + json_data: Union[str, bytes], serde: Serde | None = None + ) -> "Persona": + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _persona_from_nested_bytes(json_data, serde) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import BooleanField, KeywordField # noqa: E402 + +Persona.PERSONA_GROUPS = KeywordField("personaGroups", "personaGroups") +Persona.PERSONA_USERS = KeywordField("personaUsers", "personaUsers") +Persona.ROLE_ID = KeywordField("roleId", "roleId") +Persona.IS_ACCESS_CONTROL_ENABLED = BooleanField( + "isAccessControlEnabled", "isAccessControlEnabled" +) +Persona.DENY_CUSTOM_METADATA_GUIDS = KeywordField( + "denyCustomMetadataGuids", "denyCustomMetadataGuids" +) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class PersonaAttributes(AssetAttributes): + """Persona-specific nested attributes.""" + + persona_groups: Union[Set[str], None, UnsetType] = UNSET + persona_users: Union[Set[str], None, UnsetType] = UNSET + role_id: Union[str, None, UnsetType] = UNSET + is_access_control_enabled: Union[bool, None, UnsetType] = UNSET + deny_custom_metadata_guids: Union[Set[str], None, UnsetType] = UNSET + deny_asset_tabs: Union[Set[str], None, UnsetType] = UNSET + deny_asset_filters: Union[Set[str], None, UnsetType] = UNSET + deny_asset_types: Union[Set[str], None, UnsetType] = UNSET + deny_sidebar_tabs: Union[Set[str], None, UnsetType] = UNSET + deny_navigation_pages: Union[Set[str], None, UnsetType] = UNSET + default_navigation: Union[str, None, UnsetType] = UNSET + display_preferences: Union[Set[str], None, UnsetType] = UNSET + channel_link: Union[str, None, UnsetType] = UNSET + deny_asset_metadata_types: Union[Set[str], None, UnsetType] = UNSET + + +class PersonaNested(AssetNested): + """Persona entity in nested API format.""" + + attributes: Union[PersonaAttributes, UnsetType] = UNSET + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _persona_to_nested(persona: Persona) -> PersonaNested: + attrs_kwargs = build_attributes_kwargs(persona, PersonaAttributes) + attrs = PersonaAttributes(**attrs_kwargs) + return PersonaNested( + guid=persona.guid, + type_name=persona.type_name, + status=persona.status, + version=persona.version, + create_time=persona.create_time, + update_time=persona.update_time, + created_by=persona.created_by, + updated_by=persona.updated_by, + classifications=persona.classifications, + classification_names=persona.classification_names, + meanings=persona.meanings, + labels=persona.labels, + business_attributes=persona.business_attributes, + custom_attributes=persona.custom_attributes, + pending_tasks=persona.pending_tasks, + proxy=persona.proxy, + is_incomplete=persona.is_incomplete, + provenance_type=persona.provenance_type, + home_id=persona.home_id, + attributes=attrs, + ) + + +def _persona_from_nested(nested: PersonaNested) -> Persona: + attrs = nested.attributes if nested.attributes is not UNSET else PersonaAttributes() + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + [], + object, + ) + kwargs = build_flat_kwargs( + nested, attrs, merged_rels, AssetNested, PersonaAttributes + ) + return Persona(**kwargs) + + +def _persona_to_nested_bytes(persona: Persona, serde: Serde) -> bytes: + return serde.encode(_persona_to_nested(persona)) + + +def _persona_from_nested_bytes(data: bytes, serde: Serde) -> Persona: + nested = serde.decode(data, PersonaNested) + return _persona_from_nested(nested) diff --git a/pyatlan_v9/model/assets/power_bi.py b/pyatlan_v9/model/assets/power_bi.py new file mode 100644 index 000000000..cfee506a5 --- /dev/null +++ b/pyatlan_v9/model/assets/power_bi.py @@ -0,0 +1,619 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +PowerBI asset model with flattened inheritance. + +This module provides: +- PowerBI: Flat asset class (easy to use) +- PowerBIAttributes: Nested attributes struct (extends AssetAttributes) +- PowerBINested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class PowerBI(Asset): + """ + Base class for Power BI assets. + """ + + POWER_BI_IS_HIDDEN: ClassVar[Any] = None + POWER_BI_TABLE_QUALIFIED_NAME: ClassVar[Any] = None + POWER_BI_FORMAT_STRING: ClassVar[Any] = None + POWER_BI_ENDORSEMENT: ClassVar[Any] = None + POWER_BI_ENDORSED_BY: ClassVar[Any] = None + POWER_BI_ENDORSED_AT: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "PowerBI" + + power_bi_is_hidden: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIIsHidden" + ) + """Whether this asset is hidden in Power BI (true) or not (false).""" + + power_bi_table_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBITableQualifiedName" + ) + """Unique name of the Power BI table in which this asset exists.""" + + power_bi_format_string: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIFormatString" + ) + """Format of this asset, as specified in the FORMAT_STRING of the MDX cell property.""" + + power_bi_endorsement: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsement" + ) + """Endorsement status of this asset, in Power BI.""" + + power_bi_endorsed_by: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedBy" + ) + """User who endorsed this asset in Power BI.""" + + power_bi_endorsed_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedAt" + ) + """Time at which this asset was endorsed in Power BI.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "PowerBI" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _power_bi_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> PowerBI: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + PowerBI instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _power_bi_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class PowerBIAttributes(AssetAttributes): + """PowerBI-specific attributes for nested API format.""" + + power_bi_is_hidden: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIIsHidden" + ) + """Whether this asset is hidden in Power BI (true) or not (false).""" + + power_bi_table_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBITableQualifiedName" + ) + """Unique name of the Power BI table in which this asset exists.""" + + power_bi_format_string: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIFormatString" + ) + """Format of this asset, as specified in the FORMAT_STRING of the MDX cell property.""" + + power_bi_endorsement: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsement" + ) + """Endorsement status of this asset, in Power BI.""" + + power_bi_endorsed_by: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedBy" + ) + """User who endorsed this asset in Power BI.""" + + power_bi_endorsed_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedAt" + ) + """Time at which this asset was endorsed in Power BI.""" + + +class PowerBIRelationshipAttributes(AssetRelationshipAttributes): + """PowerBI-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class PowerBINested(AssetNested): + """PowerBI in nested API format for high-performance serialization.""" + + attributes: Union[PowerBIAttributes, UnsetType] = UNSET + relationship_attributes: Union[PowerBIRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[PowerBIRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[PowerBIRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_POWER_BI_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_power_bi_attrs(attrs: PowerBIAttributes, obj: PowerBI) -> None: + """Populate PowerBI-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.power_bi_is_hidden = obj.power_bi_is_hidden + attrs.power_bi_table_qualified_name = obj.power_bi_table_qualified_name + attrs.power_bi_format_string = obj.power_bi_format_string + attrs.power_bi_endorsement = obj.power_bi_endorsement + attrs.power_bi_endorsed_by = obj.power_bi_endorsed_by + attrs.power_bi_endorsed_at = obj.power_bi_endorsed_at + + +def _extract_power_bi_attrs(attrs: PowerBIAttributes) -> dict: + """Extract all PowerBI attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["power_bi_is_hidden"] = attrs.power_bi_is_hidden + result["power_bi_table_qualified_name"] = attrs.power_bi_table_qualified_name + result["power_bi_format_string"] = attrs.power_bi_format_string + result["power_bi_endorsement"] = attrs.power_bi_endorsement + result["power_bi_endorsed_by"] = attrs.power_bi_endorsed_by + result["power_bi_endorsed_at"] = attrs.power_bi_endorsed_at + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _power_bi_to_nested(power_bi: PowerBI) -> PowerBINested: + """Convert flat PowerBI to nested format.""" + attrs = PowerBIAttributes() + _populate_power_bi_attrs(attrs, power_bi) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + power_bi, _POWER_BI_REL_FIELDS, PowerBIRelationshipAttributes + ) + return PowerBINested( + guid=power_bi.guid, + type_name=power_bi.type_name, + status=power_bi.status, + version=power_bi.version, + create_time=power_bi.create_time, + update_time=power_bi.update_time, + created_by=power_bi.created_by, + updated_by=power_bi.updated_by, + classifications=power_bi.classifications, + classification_names=power_bi.classification_names, + meanings=power_bi.meanings, + labels=power_bi.labels, + business_attributes=power_bi.business_attributes, + custom_attributes=power_bi.custom_attributes, + pending_tasks=power_bi.pending_tasks, + proxy=power_bi.proxy, + is_incomplete=power_bi.is_incomplete, + provenance_type=power_bi.provenance_type, + home_id=power_bi.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _power_bi_from_nested(nested: PowerBINested) -> PowerBI: + """Convert nested format to flat PowerBI.""" + attrs = nested.attributes if nested.attributes is not UNSET else PowerBIAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _POWER_BI_REL_FIELDS, + PowerBIRelationshipAttributes, + ) + return PowerBI( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_power_bi_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _power_bi_to_nested_bytes(power_bi: PowerBI, serde: Serde) -> bytes: + """Convert flat PowerBI to nested JSON bytes.""" + return serde.encode(_power_bi_to_nested(power_bi)) + + +def _power_bi_from_nested_bytes(data: bytes, serde: Serde) -> PowerBI: + """Convert nested JSON bytes to flat PowerBI.""" + nested = serde.decode(data, PowerBINested) + return _power_bi_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +PowerBI.POWER_BI_IS_HIDDEN = BooleanField("powerBIIsHidden", "powerBIIsHidden") +PowerBI.POWER_BI_TABLE_QUALIFIED_NAME = KeywordTextField( + "powerBITableQualifiedName", + "powerBITableQualifiedName", + "powerBITableQualifiedName.text", +) +PowerBI.POWER_BI_FORMAT_STRING = KeywordField( + "powerBIFormatString", "powerBIFormatString" +) +PowerBI.POWER_BI_ENDORSEMENT = KeywordField("powerBIEndorsement", "powerBIEndorsement") +PowerBI.POWER_BI_ENDORSED_BY = KeywordField("powerBIEndorsedBy", "powerBIEndorsedBy") +PowerBI.POWER_BI_ENDORSED_AT = NumericField("powerBIEndorsedAt", "powerBIEndorsedAt") +PowerBI.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +PowerBI.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +PowerBI.ANOMALO_CHECKS = RelationField("anomaloChecks") +PowerBI.APPLICATION = RelationField("application") +PowerBI.APPLICATION_FIELD = RelationField("applicationField") +PowerBI.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +PowerBI.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +PowerBI.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +PowerBI.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +PowerBI.METRICS = RelationField("metrics") +PowerBI.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +PowerBI.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +PowerBI.MEANINGS = RelationField("meanings") +PowerBI.MC_MONITORS = RelationField("mcMonitors") +PowerBI.MC_INCIDENTS = RelationField("mcIncidents") +PowerBI.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +PowerBI.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +PowerBI.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +PowerBI.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +PowerBI.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +PowerBI.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +PowerBI.FILES = RelationField("files") +PowerBI.LINKS = RelationField("links") +PowerBI.README = RelationField("readme") +PowerBI.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +PowerBI.SODA_CHECKS = RelationField("sodaChecks") +PowerBI.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +PowerBI.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/power_bi_app.py b/pyatlan_v9/model/assets/power_bi_app.py new file mode 100644 index 000000000..f9fe60e64 --- /dev/null +++ b/pyatlan_v9/model/assets/power_bi_app.py @@ -0,0 +1,693 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +PowerBIApp asset model with flattened inheritance. + +This module provides: +- PowerBIApp: Flat asset class (easy to use) +- PowerBIAppAttributes: Nested attributes struct (extends AssetAttributes) +- PowerBIAppNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .power_bi_related import RelatedPowerBIDashboard, RelatedPowerBIReport + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class PowerBIApp(Asset): + """ + Instance of a Power BI App in Atlan. + """ + + POWER_BI_APP_ID: ClassVar[Any] = None + POWER_BI_APP_USERS: ClassVar[Any] = None + POWER_BI_APP_GROUPS: ClassVar[Any] = None + POWER_BI_IS_HIDDEN: ClassVar[Any] = None + POWER_BI_TABLE_QUALIFIED_NAME: ClassVar[Any] = None + POWER_BI_FORMAT_STRING: ClassVar[Any] = None + POWER_BI_ENDORSEMENT: ClassVar[Any] = None + POWER_BI_ENDORSED_BY: ClassVar[Any] = None + POWER_BI_ENDORSED_AT: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + POWER_BI_REPORTS: ClassVar[Any] = None + POWER_BI_DASHBOARDS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "PowerBIApp" + + power_bi_app_id: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIAppId" + ) + """Unique ID of the PowerBI App in the PowerBI Assets Ecosystem.""" + + power_bi_app_users: Union[List[Dict[str, str]], None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIAppUsers" + ) + """List of users and their permission access for a PowerBI App.""" + + power_bi_app_groups: Union[List[Dict[str, str]], None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIAppGroups" + ) + """List of groups and their permission access for a PowerBI App.""" + + power_bi_is_hidden: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIIsHidden" + ) + """Whether this asset is hidden in Power BI (true) or not (false).""" + + power_bi_table_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBITableQualifiedName" + ) + """Unique name of the Power BI table in which this asset exists.""" + + power_bi_format_string: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIFormatString" + ) + """Format of this asset, as specified in the FORMAT_STRING of the MDX cell property.""" + + power_bi_endorsement: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsement" + ) + """Endorsement status of this asset, in Power BI.""" + + power_bi_endorsed_by: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedBy" + ) + """User who endorsed this asset in Power BI.""" + + power_bi_endorsed_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedAt" + ) + """Time at which this asset was endorsed in Power BI.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + power_bi_reports: Union[List[RelatedPowerBIReport], None, UnsetType] = ( + msgspec.field(default=UNSET, name="powerBIReports") + ) + """PowerBI Reports that associates with this PowerBI App.""" + + power_bi_dashboards: Union[List[RelatedPowerBIDashboard], None, UnsetType] = ( + msgspec.field(default=UNSET, name="powerBIDashboards") + ) + """PowerBI Dashboards that associates with this PowerBI App.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "PowerBIApp" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _power_bi_app_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> PowerBIApp: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + PowerBIApp instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _power_bi_app_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class PowerBIAppAttributes(AssetAttributes): + """PowerBIApp-specific attributes for nested API format.""" + + power_bi_app_id: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIAppId" + ) + """Unique ID of the PowerBI App in the PowerBI Assets Ecosystem.""" + + power_bi_app_users: Union[List[Dict[str, str]], None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIAppUsers" + ) + """List of users and their permission access for a PowerBI App.""" + + power_bi_app_groups: Union[List[Dict[str, str]], None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIAppGroups" + ) + """List of groups and their permission access for a PowerBI App.""" + + power_bi_is_hidden: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIIsHidden" + ) + """Whether this asset is hidden in Power BI (true) or not (false).""" + + power_bi_table_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBITableQualifiedName" + ) + """Unique name of the Power BI table in which this asset exists.""" + + power_bi_format_string: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIFormatString" + ) + """Format of this asset, as specified in the FORMAT_STRING of the MDX cell property.""" + + power_bi_endorsement: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsement" + ) + """Endorsement status of this asset, in Power BI.""" + + power_bi_endorsed_by: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedBy" + ) + """User who endorsed this asset in Power BI.""" + + power_bi_endorsed_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedAt" + ) + """Time at which this asset was endorsed in Power BI.""" + + +class PowerBIAppRelationshipAttributes(AssetRelationshipAttributes): + """PowerBIApp-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + power_bi_reports: Union[List[RelatedPowerBIReport], None, UnsetType] = ( + msgspec.field(default=UNSET, name="powerBIReports") + ) + """PowerBI Reports that associates with this PowerBI App.""" + + power_bi_dashboards: Union[List[RelatedPowerBIDashboard], None, UnsetType] = ( + msgspec.field(default=UNSET, name="powerBIDashboards") + ) + """PowerBI Dashboards that associates with this PowerBI App.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class PowerBIAppNested(AssetNested): + """PowerBIApp in nested API format for high-performance serialization.""" + + attributes: Union[PowerBIAppAttributes, UnsetType] = UNSET + relationship_attributes: Union[PowerBIAppRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + PowerBIAppRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + PowerBIAppRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_POWER_BI_APP_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "power_bi_reports", + "power_bi_dashboards", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_power_bi_app_attrs(attrs: PowerBIAppAttributes, obj: PowerBIApp) -> None: + """Populate PowerBIApp-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.power_bi_app_id = obj.power_bi_app_id + attrs.power_bi_app_users = obj.power_bi_app_users + attrs.power_bi_app_groups = obj.power_bi_app_groups + attrs.power_bi_is_hidden = obj.power_bi_is_hidden + attrs.power_bi_table_qualified_name = obj.power_bi_table_qualified_name + attrs.power_bi_format_string = obj.power_bi_format_string + attrs.power_bi_endorsement = obj.power_bi_endorsement + attrs.power_bi_endorsed_by = obj.power_bi_endorsed_by + attrs.power_bi_endorsed_at = obj.power_bi_endorsed_at + + +def _extract_power_bi_app_attrs(attrs: PowerBIAppAttributes) -> dict: + """Extract all PowerBIApp attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["power_bi_app_id"] = attrs.power_bi_app_id + result["power_bi_app_users"] = attrs.power_bi_app_users + result["power_bi_app_groups"] = attrs.power_bi_app_groups + result["power_bi_is_hidden"] = attrs.power_bi_is_hidden + result["power_bi_table_qualified_name"] = attrs.power_bi_table_qualified_name + result["power_bi_format_string"] = attrs.power_bi_format_string + result["power_bi_endorsement"] = attrs.power_bi_endorsement + result["power_bi_endorsed_by"] = attrs.power_bi_endorsed_by + result["power_bi_endorsed_at"] = attrs.power_bi_endorsed_at + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _power_bi_app_to_nested(power_bi_app: PowerBIApp) -> PowerBIAppNested: + """Convert flat PowerBIApp to nested format.""" + attrs = PowerBIAppAttributes() + _populate_power_bi_app_attrs(attrs, power_bi_app) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + power_bi_app, _POWER_BI_APP_REL_FIELDS, PowerBIAppRelationshipAttributes + ) + return PowerBIAppNested( + guid=power_bi_app.guid, + type_name=power_bi_app.type_name, + status=power_bi_app.status, + version=power_bi_app.version, + create_time=power_bi_app.create_time, + update_time=power_bi_app.update_time, + created_by=power_bi_app.created_by, + updated_by=power_bi_app.updated_by, + classifications=power_bi_app.classifications, + classification_names=power_bi_app.classification_names, + meanings=power_bi_app.meanings, + labels=power_bi_app.labels, + business_attributes=power_bi_app.business_attributes, + custom_attributes=power_bi_app.custom_attributes, + pending_tasks=power_bi_app.pending_tasks, + proxy=power_bi_app.proxy, + is_incomplete=power_bi_app.is_incomplete, + provenance_type=power_bi_app.provenance_type, + home_id=power_bi_app.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _power_bi_app_from_nested(nested: PowerBIAppNested) -> PowerBIApp: + """Convert nested format to flat PowerBIApp.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else PowerBIAppAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _POWER_BI_APP_REL_FIELDS, + PowerBIAppRelationshipAttributes, + ) + return PowerBIApp( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_power_bi_app_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _power_bi_app_to_nested_bytes(power_bi_app: PowerBIApp, serde: Serde) -> bytes: + """Convert flat PowerBIApp to nested JSON bytes.""" + return serde.encode(_power_bi_app_to_nested(power_bi_app)) + + +def _power_bi_app_from_nested_bytes(data: bytes, serde: Serde) -> PowerBIApp: + """Convert nested JSON bytes to flat PowerBIApp.""" + nested = serde.decode(data, PowerBIAppNested) + return _power_bi_app_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +PowerBIApp.POWER_BI_APP_ID = KeywordField("powerBIAppId", "powerBIAppId") +PowerBIApp.POWER_BI_APP_USERS = KeywordField("powerBIAppUsers", "powerBIAppUsers") +PowerBIApp.POWER_BI_APP_GROUPS = KeywordField("powerBIAppGroups", "powerBIAppGroups") +PowerBIApp.POWER_BI_IS_HIDDEN = BooleanField("powerBIIsHidden", "powerBIIsHidden") +PowerBIApp.POWER_BI_TABLE_QUALIFIED_NAME = KeywordTextField( + "powerBITableQualifiedName", + "powerBITableQualifiedName", + "powerBITableQualifiedName.text", +) +PowerBIApp.POWER_BI_FORMAT_STRING = KeywordField( + "powerBIFormatString", "powerBIFormatString" +) +PowerBIApp.POWER_BI_ENDORSEMENT = KeywordField( + "powerBIEndorsement", "powerBIEndorsement" +) +PowerBIApp.POWER_BI_ENDORSED_BY = KeywordField("powerBIEndorsedBy", "powerBIEndorsedBy") +PowerBIApp.POWER_BI_ENDORSED_AT = NumericField("powerBIEndorsedAt", "powerBIEndorsedAt") +PowerBIApp.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +PowerBIApp.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +PowerBIApp.ANOMALO_CHECKS = RelationField("anomaloChecks") +PowerBIApp.APPLICATION = RelationField("application") +PowerBIApp.APPLICATION_FIELD = RelationField("applicationField") +PowerBIApp.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +PowerBIApp.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +PowerBIApp.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +PowerBIApp.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +PowerBIApp.METRICS = RelationField("metrics") +PowerBIApp.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +PowerBIApp.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +PowerBIApp.MEANINGS = RelationField("meanings") +PowerBIApp.MC_MONITORS = RelationField("mcMonitors") +PowerBIApp.MC_INCIDENTS = RelationField("mcIncidents") +PowerBIApp.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +PowerBIApp.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +PowerBIApp.POWER_BI_REPORTS = RelationField("powerBIReports") +PowerBIApp.POWER_BI_DASHBOARDS = RelationField("powerBIDashboards") +PowerBIApp.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +PowerBIApp.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +PowerBIApp.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +PowerBIApp.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +PowerBIApp.FILES = RelationField("files") +PowerBIApp.LINKS = RelationField("links") +PowerBIApp.README = RelationField("readme") +PowerBIApp.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +PowerBIApp.SODA_CHECKS = RelationField("sodaChecks") +PowerBIApp.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +PowerBIApp.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/power_bi_column.py b/pyatlan_v9/model/assets/power_bi_column.py new file mode 100644 index 000000000..db066d26d --- /dev/null +++ b/pyatlan_v9/model/assets/power_bi_column.py @@ -0,0 +1,758 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +PowerBIColumn asset model with flattened inheritance. + +This module provides: +- PowerBIColumn: Flat asset class (easy to use) +- PowerBIColumnAttributes: Nested attributes struct (extends AssetAttributes) +- PowerBIColumnNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .power_bi_related import RelatedPowerBIMeasure, RelatedPowerBITable + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class PowerBIColumn(Asset): + """ + Instance of a Power BI column in Atlan. + """ + + WORKSPACE_QUALIFIED_NAME: ClassVar[Any] = None + DATASET_QUALIFIED_NAME: ClassVar[Any] = None + POWER_BI_COLUMN_DATA_CATEGORY: ClassVar[Any] = None + POWER_BI_COLUMN_DATA_TYPE: ClassVar[Any] = None + POWER_BI_SORT_BY_COLUMN: ClassVar[Any] = None + POWER_BI_COLUMN_SUMMARIZE_BY: ClassVar[Any] = None + POWER_BI_IS_HIDDEN: ClassVar[Any] = None + POWER_BI_TABLE_QUALIFIED_NAME: ClassVar[Any] = None + POWER_BI_FORMAT_STRING: ClassVar[Any] = None + POWER_BI_ENDORSEMENT: ClassVar[Any] = None + POWER_BI_ENDORSED_BY: ClassVar[Any] = None + POWER_BI_ENDORSED_AT: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + POWER_BI_MEASURES: ClassVar[Any] = None + TABLE: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "PowerBIColumn" + + workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace in which this column exists.""" + + dataset_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dataset in which this column exists.""" + + power_bi_column_data_category: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIColumnDataCategory" + ) + """Data category that describes the data in this column.""" + + power_bi_column_data_type: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIColumnDataType" + ) + """Data type of this column.""" + + power_bi_sort_by_column: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBISortByColumn" + ) + """Name of a column in the same table to use to order this column.""" + + power_bi_column_summarize_by: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIColumnSummarizeBy" + ) + """Aggregate function to use for summarizing this column.""" + + power_bi_is_hidden: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIIsHidden" + ) + """Whether this asset is hidden in Power BI (true) or not (false).""" + + power_bi_table_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBITableQualifiedName" + ) + """Unique name of the Power BI table in which this asset exists.""" + + power_bi_format_string: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIFormatString" + ) + """Format of this asset, as specified in the FORMAT_STRING of the MDX cell property.""" + + power_bi_endorsement: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsement" + ) + """Endorsement status of this asset, in Power BI.""" + + power_bi_endorsed_by: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedBy" + ) + """User who endorsed this asset in Power BI.""" + + power_bi_endorsed_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedAt" + ) + """Time at which this asset was endorsed in Power BI.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + power_bi_measures: Union[List[RelatedPowerBIMeasure], None, UnsetType] = ( + msgspec.field(default=UNSET, name="powerBIMeasures") + ) + """PowerBI Measures that can be associated with this PowerBI Column.""" + + table: Union[RelatedPowerBITable, None, UnsetType] = UNSET + """Table in which this column exists.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "PowerBIColumn" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _power_bi_column_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> PowerBIColumn: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + PowerBIColumn instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _power_bi_column_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class PowerBIColumnAttributes(AssetAttributes): + """PowerBIColumn-specific attributes for nested API format.""" + + workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace in which this column exists.""" + + dataset_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dataset in which this column exists.""" + + power_bi_column_data_category: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIColumnDataCategory" + ) + """Data category that describes the data in this column.""" + + power_bi_column_data_type: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIColumnDataType" + ) + """Data type of this column.""" + + power_bi_sort_by_column: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBISortByColumn" + ) + """Name of a column in the same table to use to order this column.""" + + power_bi_column_summarize_by: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIColumnSummarizeBy" + ) + """Aggregate function to use for summarizing this column.""" + + power_bi_is_hidden: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIIsHidden" + ) + """Whether this asset is hidden in Power BI (true) or not (false).""" + + power_bi_table_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBITableQualifiedName" + ) + """Unique name of the Power BI table in which this asset exists.""" + + power_bi_format_string: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIFormatString" + ) + """Format of this asset, as specified in the FORMAT_STRING of the MDX cell property.""" + + power_bi_endorsement: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsement" + ) + """Endorsement status of this asset, in Power BI.""" + + power_bi_endorsed_by: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedBy" + ) + """User who endorsed this asset in Power BI.""" + + power_bi_endorsed_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedAt" + ) + """Time at which this asset was endorsed in Power BI.""" + + +class PowerBIColumnRelationshipAttributes(AssetRelationshipAttributes): + """PowerBIColumn-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + power_bi_measures: Union[List[RelatedPowerBIMeasure], None, UnsetType] = ( + msgspec.field(default=UNSET, name="powerBIMeasures") + ) + """PowerBI Measures that can be associated with this PowerBI Column.""" + + table: Union[RelatedPowerBITable, None, UnsetType] = UNSET + """Table in which this column exists.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class PowerBIColumnNested(AssetNested): + """PowerBIColumn in nested API format for high-performance serialization.""" + + attributes: Union[PowerBIColumnAttributes, UnsetType] = UNSET + relationship_attributes: Union[PowerBIColumnRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + PowerBIColumnRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + PowerBIColumnRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_POWER_BI_COLUMN_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "power_bi_measures", + "table", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_power_bi_column_attrs( + attrs: PowerBIColumnAttributes, obj: PowerBIColumn +) -> None: + """Populate PowerBIColumn-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.workspace_qualified_name = obj.workspace_qualified_name + attrs.dataset_qualified_name = obj.dataset_qualified_name + attrs.power_bi_column_data_category = obj.power_bi_column_data_category + attrs.power_bi_column_data_type = obj.power_bi_column_data_type + attrs.power_bi_sort_by_column = obj.power_bi_sort_by_column + attrs.power_bi_column_summarize_by = obj.power_bi_column_summarize_by + attrs.power_bi_is_hidden = obj.power_bi_is_hidden + attrs.power_bi_table_qualified_name = obj.power_bi_table_qualified_name + attrs.power_bi_format_string = obj.power_bi_format_string + attrs.power_bi_endorsement = obj.power_bi_endorsement + attrs.power_bi_endorsed_by = obj.power_bi_endorsed_by + attrs.power_bi_endorsed_at = obj.power_bi_endorsed_at + + +def _extract_power_bi_column_attrs(attrs: PowerBIColumnAttributes) -> dict: + """Extract all PowerBIColumn attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["workspace_qualified_name"] = attrs.workspace_qualified_name + result["dataset_qualified_name"] = attrs.dataset_qualified_name + result["power_bi_column_data_category"] = attrs.power_bi_column_data_category + result["power_bi_column_data_type"] = attrs.power_bi_column_data_type + result["power_bi_sort_by_column"] = attrs.power_bi_sort_by_column + result["power_bi_column_summarize_by"] = attrs.power_bi_column_summarize_by + result["power_bi_is_hidden"] = attrs.power_bi_is_hidden + result["power_bi_table_qualified_name"] = attrs.power_bi_table_qualified_name + result["power_bi_format_string"] = attrs.power_bi_format_string + result["power_bi_endorsement"] = attrs.power_bi_endorsement + result["power_bi_endorsed_by"] = attrs.power_bi_endorsed_by + result["power_bi_endorsed_at"] = attrs.power_bi_endorsed_at + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _power_bi_column_to_nested(power_bi_column: PowerBIColumn) -> PowerBIColumnNested: + """Convert flat PowerBIColumn to nested format.""" + attrs = PowerBIColumnAttributes() + _populate_power_bi_column_attrs(attrs, power_bi_column) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + power_bi_column, + _POWER_BI_COLUMN_REL_FIELDS, + PowerBIColumnRelationshipAttributes, + ) + return PowerBIColumnNested( + guid=power_bi_column.guid, + type_name=power_bi_column.type_name, + status=power_bi_column.status, + version=power_bi_column.version, + create_time=power_bi_column.create_time, + update_time=power_bi_column.update_time, + created_by=power_bi_column.created_by, + updated_by=power_bi_column.updated_by, + classifications=power_bi_column.classifications, + classification_names=power_bi_column.classification_names, + meanings=power_bi_column.meanings, + labels=power_bi_column.labels, + business_attributes=power_bi_column.business_attributes, + custom_attributes=power_bi_column.custom_attributes, + pending_tasks=power_bi_column.pending_tasks, + proxy=power_bi_column.proxy, + is_incomplete=power_bi_column.is_incomplete, + provenance_type=power_bi_column.provenance_type, + home_id=power_bi_column.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _power_bi_column_from_nested(nested: PowerBIColumnNested) -> PowerBIColumn: + """Convert nested format to flat PowerBIColumn.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else PowerBIColumnAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _POWER_BI_COLUMN_REL_FIELDS, + PowerBIColumnRelationshipAttributes, + ) + return PowerBIColumn( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_power_bi_column_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _power_bi_column_to_nested_bytes( + power_bi_column: PowerBIColumn, serde: Serde +) -> bytes: + """Convert flat PowerBIColumn to nested JSON bytes.""" + return serde.encode(_power_bi_column_to_nested(power_bi_column)) + + +def _power_bi_column_from_nested_bytes(data: bytes, serde: Serde) -> PowerBIColumn: + """Convert nested JSON bytes to flat PowerBIColumn.""" + nested = serde.decode(data, PowerBIColumnNested) + return _power_bi_column_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +PowerBIColumn.WORKSPACE_QUALIFIED_NAME = KeywordField( + "workspaceQualifiedName", "workspaceQualifiedName" +) +PowerBIColumn.DATASET_QUALIFIED_NAME = KeywordField( + "datasetQualifiedName", "datasetQualifiedName" +) +PowerBIColumn.POWER_BI_COLUMN_DATA_CATEGORY = KeywordField( + "powerBIColumnDataCategory", "powerBIColumnDataCategory" +) +PowerBIColumn.POWER_BI_COLUMN_DATA_TYPE = KeywordField( + "powerBIColumnDataType", "powerBIColumnDataType" +) +PowerBIColumn.POWER_BI_SORT_BY_COLUMN = KeywordField( + "powerBISortByColumn", "powerBISortByColumn" +) +PowerBIColumn.POWER_BI_COLUMN_SUMMARIZE_BY = KeywordField( + "powerBIColumnSummarizeBy", "powerBIColumnSummarizeBy" +) +PowerBIColumn.POWER_BI_IS_HIDDEN = BooleanField("powerBIIsHidden", "powerBIIsHidden") +PowerBIColumn.POWER_BI_TABLE_QUALIFIED_NAME = KeywordTextField( + "powerBITableQualifiedName", + "powerBITableQualifiedName", + "powerBITableQualifiedName.text", +) +PowerBIColumn.POWER_BI_FORMAT_STRING = KeywordField( + "powerBIFormatString", "powerBIFormatString" +) +PowerBIColumn.POWER_BI_ENDORSEMENT = KeywordField( + "powerBIEndorsement", "powerBIEndorsement" +) +PowerBIColumn.POWER_BI_ENDORSED_BY = KeywordField( + "powerBIEndorsedBy", "powerBIEndorsedBy" +) +PowerBIColumn.POWER_BI_ENDORSED_AT = NumericField( + "powerBIEndorsedAt", "powerBIEndorsedAt" +) +PowerBIColumn.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +PowerBIColumn.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +PowerBIColumn.ANOMALO_CHECKS = RelationField("anomaloChecks") +PowerBIColumn.APPLICATION = RelationField("application") +PowerBIColumn.APPLICATION_FIELD = RelationField("applicationField") +PowerBIColumn.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +PowerBIColumn.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +PowerBIColumn.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +PowerBIColumn.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +PowerBIColumn.METRICS = RelationField("metrics") +PowerBIColumn.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +PowerBIColumn.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +PowerBIColumn.MEANINGS = RelationField("meanings") +PowerBIColumn.MC_MONITORS = RelationField("mcMonitors") +PowerBIColumn.MC_INCIDENTS = RelationField("mcIncidents") +PowerBIColumn.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +PowerBIColumn.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +PowerBIColumn.POWER_BI_MEASURES = RelationField("powerBIMeasures") +PowerBIColumn.TABLE = RelationField("table") +PowerBIColumn.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +PowerBIColumn.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +PowerBIColumn.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +PowerBIColumn.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +PowerBIColumn.FILES = RelationField("files") +PowerBIColumn.LINKS = RelationField("links") +PowerBIColumn.README = RelationField("readme") +PowerBIColumn.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +PowerBIColumn.SODA_CHECKS = RelationField("sodaChecks") +PowerBIColumn.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +PowerBIColumn.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/power_bi_dashboard.py b/pyatlan_v9/model/assets/power_bi_dashboard.py new file mode 100644 index 000000000..7cd5cafe3 --- /dev/null +++ b/pyatlan_v9/model/assets/power_bi_dashboard.py @@ -0,0 +1,721 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +PowerBIDashboard asset model with flattened inheritance. + +This module provides: +- PowerBIDashboard: Flat asset class (easy to use) +- PowerBIDashboardAttributes: Nested attributes struct (extends AssetAttributes) +- PowerBIDashboardNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .power_bi_related import ( + RelatedPowerBIApp, + RelatedPowerBITile, + RelatedPowerBIWorkspace, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class PowerBIDashboard(Asset): + """ + Instance of a Power BI dashboard in Atlan. Dashboards are a single page, often called a canvas, that tell a story through visualization. + """ + + WORKSPACE_QUALIFIED_NAME: ClassVar[Any] = None + WEB_URL: ClassVar[Any] = None + TILE_COUNT: ClassVar[Any] = None + POWER_BI_IS_HIDDEN: ClassVar[Any] = None + POWER_BI_TABLE_QUALIFIED_NAME: ClassVar[Any] = None + POWER_BI_FORMAT_STRING: ClassVar[Any] = None + POWER_BI_ENDORSEMENT: ClassVar[Any] = None + POWER_BI_ENDORSED_BY: ClassVar[Any] = None + POWER_BI_ENDORSED_AT: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + POWER_BI_APPS: ClassVar[Any] = None + TILES: ClassVar[Any] = None + WORKSPACE: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "PowerBIDashboard" + + workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace in which this dashboard exists.""" + + web_url: Union[str, None, UnsetType] = UNSET + """Deprecated. See 'sourceUrl' instead.""" + + tile_count: Union[int, None, UnsetType] = UNSET + """Number of tiles in this table.""" + + power_bi_is_hidden: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIIsHidden" + ) + """Whether this asset is hidden in Power BI (true) or not (false).""" + + power_bi_table_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBITableQualifiedName" + ) + """Unique name of the Power BI table in which this asset exists.""" + + power_bi_format_string: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIFormatString" + ) + """Format of this asset, as specified in the FORMAT_STRING of the MDX cell property.""" + + power_bi_endorsement: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsement" + ) + """Endorsement status of this asset, in Power BI.""" + + power_bi_endorsed_by: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedBy" + ) + """User who endorsed this asset in Power BI.""" + + power_bi_endorsed_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedAt" + ) + """Time at which this asset was endorsed in Power BI.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + power_bi_apps: Union[List[RelatedPowerBIApp], None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIApps" + ) + """PowerBI App that is associated with this PowerBI Dashboard.""" + + tiles: Union[List[RelatedPowerBITile], None, UnsetType] = UNSET + """Tiles that exist within this dashboard.""" + + workspace: Union[RelatedPowerBIWorkspace, None, UnsetType] = UNSET + """Workspace in which this dashboard exists.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "PowerBIDashboard" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _power_bi_dashboard_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> PowerBIDashboard: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + PowerBIDashboard instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _power_bi_dashboard_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class PowerBIDashboardAttributes(AssetAttributes): + """PowerBIDashboard-specific attributes for nested API format.""" + + workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace in which this dashboard exists.""" + + web_url: Union[str, None, UnsetType] = UNSET + """Deprecated. See 'sourceUrl' instead.""" + + tile_count: Union[int, None, UnsetType] = UNSET + """Number of tiles in this table.""" + + power_bi_is_hidden: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIIsHidden" + ) + """Whether this asset is hidden in Power BI (true) or not (false).""" + + power_bi_table_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBITableQualifiedName" + ) + """Unique name of the Power BI table in which this asset exists.""" + + power_bi_format_string: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIFormatString" + ) + """Format of this asset, as specified in the FORMAT_STRING of the MDX cell property.""" + + power_bi_endorsement: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsement" + ) + """Endorsement status of this asset, in Power BI.""" + + power_bi_endorsed_by: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedBy" + ) + """User who endorsed this asset in Power BI.""" + + power_bi_endorsed_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedAt" + ) + """Time at which this asset was endorsed in Power BI.""" + + +class PowerBIDashboardRelationshipAttributes(AssetRelationshipAttributes): + """PowerBIDashboard-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + power_bi_apps: Union[List[RelatedPowerBIApp], None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIApps" + ) + """PowerBI App that is associated with this PowerBI Dashboard.""" + + tiles: Union[List[RelatedPowerBITile], None, UnsetType] = UNSET + """Tiles that exist within this dashboard.""" + + workspace: Union[RelatedPowerBIWorkspace, None, UnsetType] = UNSET + """Workspace in which this dashboard exists.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class PowerBIDashboardNested(AssetNested): + """PowerBIDashboard in nested API format for high-performance serialization.""" + + attributes: Union[PowerBIDashboardAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + PowerBIDashboardRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + PowerBIDashboardRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + PowerBIDashboardRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_POWER_BI_DASHBOARD_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "power_bi_apps", + "tiles", + "workspace", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_power_bi_dashboard_attrs( + attrs: PowerBIDashboardAttributes, obj: PowerBIDashboard +) -> None: + """Populate PowerBIDashboard-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.workspace_qualified_name = obj.workspace_qualified_name + attrs.web_url = obj.web_url + attrs.tile_count = obj.tile_count + attrs.power_bi_is_hidden = obj.power_bi_is_hidden + attrs.power_bi_table_qualified_name = obj.power_bi_table_qualified_name + attrs.power_bi_format_string = obj.power_bi_format_string + attrs.power_bi_endorsement = obj.power_bi_endorsement + attrs.power_bi_endorsed_by = obj.power_bi_endorsed_by + attrs.power_bi_endorsed_at = obj.power_bi_endorsed_at + + +def _extract_power_bi_dashboard_attrs(attrs: PowerBIDashboardAttributes) -> dict: + """Extract all PowerBIDashboard attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["workspace_qualified_name"] = attrs.workspace_qualified_name + result["web_url"] = attrs.web_url + result["tile_count"] = attrs.tile_count + result["power_bi_is_hidden"] = attrs.power_bi_is_hidden + result["power_bi_table_qualified_name"] = attrs.power_bi_table_qualified_name + result["power_bi_format_string"] = attrs.power_bi_format_string + result["power_bi_endorsement"] = attrs.power_bi_endorsement + result["power_bi_endorsed_by"] = attrs.power_bi_endorsed_by + result["power_bi_endorsed_at"] = attrs.power_bi_endorsed_at + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _power_bi_dashboard_to_nested( + power_bi_dashboard: PowerBIDashboard, +) -> PowerBIDashboardNested: + """Convert flat PowerBIDashboard to nested format.""" + attrs = PowerBIDashboardAttributes() + _populate_power_bi_dashboard_attrs(attrs, power_bi_dashboard) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + power_bi_dashboard, + _POWER_BI_DASHBOARD_REL_FIELDS, + PowerBIDashboardRelationshipAttributes, + ) + return PowerBIDashboardNested( + guid=power_bi_dashboard.guid, + type_name=power_bi_dashboard.type_name, + status=power_bi_dashboard.status, + version=power_bi_dashboard.version, + create_time=power_bi_dashboard.create_time, + update_time=power_bi_dashboard.update_time, + created_by=power_bi_dashboard.created_by, + updated_by=power_bi_dashboard.updated_by, + classifications=power_bi_dashboard.classifications, + classification_names=power_bi_dashboard.classification_names, + meanings=power_bi_dashboard.meanings, + labels=power_bi_dashboard.labels, + business_attributes=power_bi_dashboard.business_attributes, + custom_attributes=power_bi_dashboard.custom_attributes, + pending_tasks=power_bi_dashboard.pending_tasks, + proxy=power_bi_dashboard.proxy, + is_incomplete=power_bi_dashboard.is_incomplete, + provenance_type=power_bi_dashboard.provenance_type, + home_id=power_bi_dashboard.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _power_bi_dashboard_from_nested(nested: PowerBIDashboardNested) -> PowerBIDashboard: + """Convert nested format to flat PowerBIDashboard.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else PowerBIDashboardAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _POWER_BI_DASHBOARD_REL_FIELDS, + PowerBIDashboardRelationshipAttributes, + ) + return PowerBIDashboard( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_power_bi_dashboard_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _power_bi_dashboard_to_nested_bytes( + power_bi_dashboard: PowerBIDashboard, serde: Serde +) -> bytes: + """Convert flat PowerBIDashboard to nested JSON bytes.""" + return serde.encode(_power_bi_dashboard_to_nested(power_bi_dashboard)) + + +def _power_bi_dashboard_from_nested_bytes( + data: bytes, serde: Serde +) -> PowerBIDashboard: + """Convert nested JSON bytes to flat PowerBIDashboard.""" + nested = serde.decode(data, PowerBIDashboardNested) + return _power_bi_dashboard_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +PowerBIDashboard.WORKSPACE_QUALIFIED_NAME = KeywordField( + "workspaceQualifiedName", "workspaceQualifiedName" +) +PowerBIDashboard.WEB_URL = KeywordField("webUrl", "webUrl") +PowerBIDashboard.TILE_COUNT = NumericField("tileCount", "tileCount") +PowerBIDashboard.POWER_BI_IS_HIDDEN = BooleanField("powerBIIsHidden", "powerBIIsHidden") +PowerBIDashboard.POWER_BI_TABLE_QUALIFIED_NAME = KeywordTextField( + "powerBITableQualifiedName", + "powerBITableQualifiedName", + "powerBITableQualifiedName.text", +) +PowerBIDashboard.POWER_BI_FORMAT_STRING = KeywordField( + "powerBIFormatString", "powerBIFormatString" +) +PowerBIDashboard.POWER_BI_ENDORSEMENT = KeywordField( + "powerBIEndorsement", "powerBIEndorsement" +) +PowerBIDashboard.POWER_BI_ENDORSED_BY = KeywordField( + "powerBIEndorsedBy", "powerBIEndorsedBy" +) +PowerBIDashboard.POWER_BI_ENDORSED_AT = NumericField( + "powerBIEndorsedAt", "powerBIEndorsedAt" +) +PowerBIDashboard.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +PowerBIDashboard.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +PowerBIDashboard.ANOMALO_CHECKS = RelationField("anomaloChecks") +PowerBIDashboard.APPLICATION = RelationField("application") +PowerBIDashboard.APPLICATION_FIELD = RelationField("applicationField") +PowerBIDashboard.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +PowerBIDashboard.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +PowerBIDashboard.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +PowerBIDashboard.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +PowerBIDashboard.METRICS = RelationField("metrics") +PowerBIDashboard.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +PowerBIDashboard.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +PowerBIDashboard.MEANINGS = RelationField("meanings") +PowerBIDashboard.MC_MONITORS = RelationField("mcMonitors") +PowerBIDashboard.MC_INCIDENTS = RelationField("mcIncidents") +PowerBIDashboard.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +PowerBIDashboard.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +PowerBIDashboard.POWER_BI_APPS = RelationField("powerBIApps") +PowerBIDashboard.TILES = RelationField("tiles") +PowerBIDashboard.WORKSPACE = RelationField("workspace") +PowerBIDashboard.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +PowerBIDashboard.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +PowerBIDashboard.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +PowerBIDashboard.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +PowerBIDashboard.FILES = RelationField("files") +PowerBIDashboard.LINKS = RelationField("links") +PowerBIDashboard.README = RelationField("readme") +PowerBIDashboard.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +PowerBIDashboard.SODA_CHECKS = RelationField("sodaChecks") +PowerBIDashboard.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +PowerBIDashboard.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/power_bi_dataflow.py b/pyatlan_v9/model/assets/power_bi_dataflow.py new file mode 100644 index 000000000..3257dce7a --- /dev/null +++ b/pyatlan_v9/model/assets/power_bi_dataflow.py @@ -0,0 +1,835 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +PowerBIDataflow asset model with flattened inheritance. + +This module provides: +- PowerBIDataflow: Flat asset class (easy to use) +- PowerBIDataflowAttributes: Nested attributes struct (extends AssetAttributes) +- PowerBIDataflowNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .power_bi_related import ( + RelatedPowerBIDataflow, + RelatedPowerBIDataflowEntityColumn, + RelatedPowerBIDataset, + RelatedPowerBIDatasource, + RelatedPowerBITable, + RelatedPowerBIWorkspace, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class PowerBIDataflow(Asset): + """ + Instance of a Power BI dataflow in Atlan. Dataflows are reusable transformation logic that can be shared by many datasets and reports inside Power BI. + """ + + WORKSPACE_QUALIFIED_NAME: ClassVar[Any] = None + WEB_URL: ClassVar[Any] = None + POWER_BI_DATAFLOW_REFRESH_SCHEDULE_FREQUENCY: ClassVar[Any] = None + POWER_BI_DATAFLOW_REFRESH_SCHEDULE_TIMES: ClassVar[Any] = None + POWER_BI_DATAFLOW_REFRESH_SCHEDULE_TIME_ZONE: ClassVar[Any] = None + POWER_BI_IS_HIDDEN: ClassVar[Any] = None + POWER_BI_TABLE_QUALIFIED_NAME: ClassVar[Any] = None + POWER_BI_FORMAT_STRING: ClassVar[Any] = None + POWER_BI_ENDORSEMENT: ClassVar[Any] = None + POWER_BI_ENDORSED_BY: ClassVar[Any] = None + POWER_BI_ENDORSED_AT: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + DATASETS: ClassVar[Any] = None + WORKSPACE: ClassVar[Any] = None + POWER_BI_DATAFLOW_CHILDREN: ClassVar[Any] = None + POWER_BI_DATAFLOW_PARENTS: ClassVar[Any] = None + TABLES: ClassVar[Any] = None + POWER_BI_PROCESSES: ClassVar[Any] = None + POWER_BI_DATASOURCES: ClassVar[Any] = None + POWER_BI_DATAFLOW_ENTITY_COLUMNS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "PowerBIDataflow" + + workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace in which this dataflow exists.""" + + web_url: Union[str, None, UnsetType] = UNSET + """Deprecated. See 'sourceUrl' instead.""" + + power_bi_dataflow_refresh_schedule_frequency: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="powerBIDataflowRefreshScheduleFrequency") + ) + """Refresh Schedule frequency for a PowerBI Dataflow.""" + + power_bi_dataflow_refresh_schedule_times: Union[List[str], None, UnsetType] = ( + msgspec.field(default=UNSET, name="powerBIDataflowRefreshScheduleTimes") + ) + """Time for the refresh schedule set for a PowerBI Dataflow.""" + + power_bi_dataflow_refresh_schedule_time_zone: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="powerBIDataflowRefreshScheduleTimeZone") + ) + """Time zone for the refresh schedule set for a PowerBI Dataflow.""" + + power_bi_is_hidden: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIIsHidden" + ) + """Whether this asset is hidden in Power BI (true) or not (false).""" + + power_bi_table_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBITableQualifiedName" + ) + """Unique name of the Power BI table in which this asset exists.""" + + power_bi_format_string: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIFormatString" + ) + """Format of this asset, as specified in the FORMAT_STRING of the MDX cell property.""" + + power_bi_endorsement: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsement" + ) + """Endorsement status of this asset, in Power BI.""" + + power_bi_endorsed_by: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedBy" + ) + """User who endorsed this asset in Power BI.""" + + power_bi_endorsed_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedAt" + ) + """Time at which this asset was endorsed in Power BI.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + datasets: Union[List[RelatedPowerBIDataset], None, UnsetType] = UNSET + """Datasets used by this dataflow.""" + + workspace: Union[RelatedPowerBIWorkspace, None, UnsetType] = UNSET + """Workspace in which this dataflow exists.""" + + power_bi_dataflow_children: Union[List[RelatedPowerBIDataflow], None, UnsetType] = ( + msgspec.field(default=UNSET, name="powerBIDataflowChildren") + ) + """Child Dataflows to this PowerBI Dataflow.""" + + power_bi_dataflow_parents: Union[List[RelatedPowerBIDataflow], None, UnsetType] = ( + msgspec.field(default=UNSET, name="powerBIDataflowParents") + ) + """Parent Dataflows to this PowerBI Dataflow.""" + + tables: Union[List[RelatedPowerBITable], None, UnsetType] = UNSET + """PowerBI Tables that are associated with this Dataflow.""" + + power_bi_processes: Union[List[RelatedProcess], None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIProcesses" + ) + """Lineage process that associates this PowerBI Dataflow.""" + + power_bi_datasources: Union[List[RelatedPowerBIDatasource], None, UnsetType] = ( + msgspec.field(default=UNSET, name="powerBIDatasources") + ) + """PowerBI Datasources that are associated with this Dataflow.""" + + power_bi_dataflow_entity_columns: Union[ + List[RelatedPowerBIDataflowEntityColumn], None, UnsetType + ] = msgspec.field(default=UNSET, name="powerBIDataflowEntityColumns") + """PowerBI Dataflow Entity Columns that exist within this Dataflow.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "PowerBIDataflow" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _power_bi_dataflow_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> PowerBIDataflow: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + PowerBIDataflow instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _power_bi_dataflow_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class PowerBIDataflowAttributes(AssetAttributes): + """PowerBIDataflow-specific attributes for nested API format.""" + + workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace in which this dataflow exists.""" + + web_url: Union[str, None, UnsetType] = UNSET + """Deprecated. See 'sourceUrl' instead.""" + + power_bi_dataflow_refresh_schedule_frequency: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="powerBIDataflowRefreshScheduleFrequency") + ) + """Refresh Schedule frequency for a PowerBI Dataflow.""" + + power_bi_dataflow_refresh_schedule_times: Union[List[str], None, UnsetType] = ( + msgspec.field(default=UNSET, name="powerBIDataflowRefreshScheduleTimes") + ) + """Time for the refresh schedule set for a PowerBI Dataflow.""" + + power_bi_dataflow_refresh_schedule_time_zone: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="powerBIDataflowRefreshScheduleTimeZone") + ) + """Time zone for the refresh schedule set for a PowerBI Dataflow.""" + + power_bi_is_hidden: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIIsHidden" + ) + """Whether this asset is hidden in Power BI (true) or not (false).""" + + power_bi_table_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBITableQualifiedName" + ) + """Unique name of the Power BI table in which this asset exists.""" + + power_bi_format_string: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIFormatString" + ) + """Format of this asset, as specified in the FORMAT_STRING of the MDX cell property.""" + + power_bi_endorsement: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsement" + ) + """Endorsement status of this asset, in Power BI.""" + + power_bi_endorsed_by: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedBy" + ) + """User who endorsed this asset in Power BI.""" + + power_bi_endorsed_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedAt" + ) + """Time at which this asset was endorsed in Power BI.""" + + +class PowerBIDataflowRelationshipAttributes(AssetRelationshipAttributes): + """PowerBIDataflow-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + datasets: Union[List[RelatedPowerBIDataset], None, UnsetType] = UNSET + """Datasets used by this dataflow.""" + + workspace: Union[RelatedPowerBIWorkspace, None, UnsetType] = UNSET + """Workspace in which this dataflow exists.""" + + power_bi_dataflow_children: Union[List[RelatedPowerBIDataflow], None, UnsetType] = ( + msgspec.field(default=UNSET, name="powerBIDataflowChildren") + ) + """Child Dataflows to this PowerBI Dataflow.""" + + power_bi_dataflow_parents: Union[List[RelatedPowerBIDataflow], None, UnsetType] = ( + msgspec.field(default=UNSET, name="powerBIDataflowParents") + ) + """Parent Dataflows to this PowerBI Dataflow.""" + + tables: Union[List[RelatedPowerBITable], None, UnsetType] = UNSET + """PowerBI Tables that are associated with this Dataflow.""" + + power_bi_processes: Union[List[RelatedProcess], None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIProcesses" + ) + """Lineage process that associates this PowerBI Dataflow.""" + + power_bi_datasources: Union[List[RelatedPowerBIDatasource], None, UnsetType] = ( + msgspec.field(default=UNSET, name="powerBIDatasources") + ) + """PowerBI Datasources that are associated with this Dataflow.""" + + power_bi_dataflow_entity_columns: Union[ + List[RelatedPowerBIDataflowEntityColumn], None, UnsetType + ] = msgspec.field(default=UNSET, name="powerBIDataflowEntityColumns") + """PowerBI Dataflow Entity Columns that exist within this Dataflow.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class PowerBIDataflowNested(AssetNested): + """PowerBIDataflow in nested API format for high-performance serialization.""" + + attributes: Union[PowerBIDataflowAttributes, UnsetType] = UNSET + relationship_attributes: Union[PowerBIDataflowRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + PowerBIDataflowRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + PowerBIDataflowRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_POWER_BI_DATAFLOW_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "datasets", + "workspace", + "power_bi_dataflow_children", + "power_bi_dataflow_parents", + "tables", + "power_bi_processes", + "power_bi_datasources", + "power_bi_dataflow_entity_columns", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_power_bi_dataflow_attrs( + attrs: PowerBIDataflowAttributes, obj: PowerBIDataflow +) -> None: + """Populate PowerBIDataflow-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.workspace_qualified_name = obj.workspace_qualified_name + attrs.web_url = obj.web_url + attrs.power_bi_dataflow_refresh_schedule_frequency = ( + obj.power_bi_dataflow_refresh_schedule_frequency + ) + attrs.power_bi_dataflow_refresh_schedule_times = ( + obj.power_bi_dataflow_refresh_schedule_times + ) + attrs.power_bi_dataflow_refresh_schedule_time_zone = ( + obj.power_bi_dataflow_refresh_schedule_time_zone + ) + attrs.power_bi_is_hidden = obj.power_bi_is_hidden + attrs.power_bi_table_qualified_name = obj.power_bi_table_qualified_name + attrs.power_bi_format_string = obj.power_bi_format_string + attrs.power_bi_endorsement = obj.power_bi_endorsement + attrs.power_bi_endorsed_by = obj.power_bi_endorsed_by + attrs.power_bi_endorsed_at = obj.power_bi_endorsed_at + + +def _extract_power_bi_dataflow_attrs(attrs: PowerBIDataflowAttributes) -> dict: + """Extract all PowerBIDataflow attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["workspace_qualified_name"] = attrs.workspace_qualified_name + result["web_url"] = attrs.web_url + result["power_bi_dataflow_refresh_schedule_frequency"] = ( + attrs.power_bi_dataflow_refresh_schedule_frequency + ) + result["power_bi_dataflow_refresh_schedule_times"] = ( + attrs.power_bi_dataflow_refresh_schedule_times + ) + result["power_bi_dataflow_refresh_schedule_time_zone"] = ( + attrs.power_bi_dataflow_refresh_schedule_time_zone + ) + result["power_bi_is_hidden"] = attrs.power_bi_is_hidden + result["power_bi_table_qualified_name"] = attrs.power_bi_table_qualified_name + result["power_bi_format_string"] = attrs.power_bi_format_string + result["power_bi_endorsement"] = attrs.power_bi_endorsement + result["power_bi_endorsed_by"] = attrs.power_bi_endorsed_by + result["power_bi_endorsed_at"] = attrs.power_bi_endorsed_at + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _power_bi_dataflow_to_nested( + power_bi_dataflow: PowerBIDataflow, +) -> PowerBIDataflowNested: + """Convert flat PowerBIDataflow to nested format.""" + attrs = PowerBIDataflowAttributes() + _populate_power_bi_dataflow_attrs(attrs, power_bi_dataflow) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + power_bi_dataflow, + _POWER_BI_DATAFLOW_REL_FIELDS, + PowerBIDataflowRelationshipAttributes, + ) + return PowerBIDataflowNested( + guid=power_bi_dataflow.guid, + type_name=power_bi_dataflow.type_name, + status=power_bi_dataflow.status, + version=power_bi_dataflow.version, + create_time=power_bi_dataflow.create_time, + update_time=power_bi_dataflow.update_time, + created_by=power_bi_dataflow.created_by, + updated_by=power_bi_dataflow.updated_by, + classifications=power_bi_dataflow.classifications, + classification_names=power_bi_dataflow.classification_names, + meanings=power_bi_dataflow.meanings, + labels=power_bi_dataflow.labels, + business_attributes=power_bi_dataflow.business_attributes, + custom_attributes=power_bi_dataflow.custom_attributes, + pending_tasks=power_bi_dataflow.pending_tasks, + proxy=power_bi_dataflow.proxy, + is_incomplete=power_bi_dataflow.is_incomplete, + provenance_type=power_bi_dataflow.provenance_type, + home_id=power_bi_dataflow.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _power_bi_dataflow_from_nested(nested: PowerBIDataflowNested) -> PowerBIDataflow: + """Convert nested format to flat PowerBIDataflow.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else PowerBIDataflowAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _POWER_BI_DATAFLOW_REL_FIELDS, + PowerBIDataflowRelationshipAttributes, + ) + return PowerBIDataflow( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_power_bi_dataflow_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _power_bi_dataflow_to_nested_bytes( + power_bi_dataflow: PowerBIDataflow, serde: Serde +) -> bytes: + """Convert flat PowerBIDataflow to nested JSON bytes.""" + return serde.encode(_power_bi_dataflow_to_nested(power_bi_dataflow)) + + +def _power_bi_dataflow_from_nested_bytes(data: bytes, serde: Serde) -> PowerBIDataflow: + """Convert nested JSON bytes to flat PowerBIDataflow.""" + nested = serde.decode(data, PowerBIDataflowNested) + return _power_bi_dataflow_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +PowerBIDataflow.WORKSPACE_QUALIFIED_NAME = KeywordField( + "workspaceQualifiedName", "workspaceQualifiedName" +) +PowerBIDataflow.WEB_URL = KeywordField("webUrl", "webUrl") +PowerBIDataflow.POWER_BI_DATAFLOW_REFRESH_SCHEDULE_FREQUENCY = KeywordField( + "powerBIDataflowRefreshScheduleFrequency", "powerBIDataflowRefreshScheduleFrequency" +) +PowerBIDataflow.POWER_BI_DATAFLOW_REFRESH_SCHEDULE_TIMES = KeywordField( + "powerBIDataflowRefreshScheduleTimes", "powerBIDataflowRefreshScheduleTimes" +) +PowerBIDataflow.POWER_BI_DATAFLOW_REFRESH_SCHEDULE_TIME_ZONE = KeywordField( + "powerBIDataflowRefreshScheduleTimeZone", "powerBIDataflowRefreshScheduleTimeZone" +) +PowerBIDataflow.POWER_BI_IS_HIDDEN = BooleanField("powerBIIsHidden", "powerBIIsHidden") +PowerBIDataflow.POWER_BI_TABLE_QUALIFIED_NAME = KeywordTextField( + "powerBITableQualifiedName", + "powerBITableQualifiedName", + "powerBITableQualifiedName.text", +) +PowerBIDataflow.POWER_BI_FORMAT_STRING = KeywordField( + "powerBIFormatString", "powerBIFormatString" +) +PowerBIDataflow.POWER_BI_ENDORSEMENT = KeywordField( + "powerBIEndorsement", "powerBIEndorsement" +) +PowerBIDataflow.POWER_BI_ENDORSED_BY = KeywordField( + "powerBIEndorsedBy", "powerBIEndorsedBy" +) +PowerBIDataflow.POWER_BI_ENDORSED_AT = NumericField( + "powerBIEndorsedAt", "powerBIEndorsedAt" +) +PowerBIDataflow.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +PowerBIDataflow.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +PowerBIDataflow.ANOMALO_CHECKS = RelationField("anomaloChecks") +PowerBIDataflow.APPLICATION = RelationField("application") +PowerBIDataflow.APPLICATION_FIELD = RelationField("applicationField") +PowerBIDataflow.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +PowerBIDataflow.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +PowerBIDataflow.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +PowerBIDataflow.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +PowerBIDataflow.METRICS = RelationField("metrics") +PowerBIDataflow.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +PowerBIDataflow.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +PowerBIDataflow.MEANINGS = RelationField("meanings") +PowerBIDataflow.MC_MONITORS = RelationField("mcMonitors") +PowerBIDataflow.MC_INCIDENTS = RelationField("mcIncidents") +PowerBIDataflow.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +PowerBIDataflow.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +PowerBIDataflow.DATASETS = RelationField("datasets") +PowerBIDataflow.WORKSPACE = RelationField("workspace") +PowerBIDataflow.POWER_BI_DATAFLOW_CHILDREN = RelationField("powerBIDataflowChildren") +PowerBIDataflow.POWER_BI_DATAFLOW_PARENTS = RelationField("powerBIDataflowParents") +PowerBIDataflow.TABLES = RelationField("tables") +PowerBIDataflow.POWER_BI_PROCESSES = RelationField("powerBIProcesses") +PowerBIDataflow.POWER_BI_DATASOURCES = RelationField("powerBIDatasources") +PowerBIDataflow.POWER_BI_DATAFLOW_ENTITY_COLUMNS = RelationField( + "powerBIDataflowEntityColumns" +) +PowerBIDataflow.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +PowerBIDataflow.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +PowerBIDataflow.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +PowerBIDataflow.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +PowerBIDataflow.FILES = RelationField("files") +PowerBIDataflow.LINKS = RelationField("links") +PowerBIDataflow.README = RelationField("readme") +PowerBIDataflow.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +PowerBIDataflow.SODA_CHECKS = RelationField("sodaChecks") +PowerBIDataflow.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +PowerBIDataflow.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/power_bi_dataflow_entity_column.py b/pyatlan_v9/model/assets/power_bi_dataflow_entity_column.py new file mode 100644 index 000000000..2ce56cb63 --- /dev/null +++ b/pyatlan_v9/model/assets/power_bi_dataflow_entity_column.py @@ -0,0 +1,769 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +PowerBIDataflowEntityColumn asset model with flattened inheritance. + +This module provides: +- PowerBIDataflowEntityColumn: Flat asset class (easy to use) +- PowerBIDataflowEntityColumnAttributes: Nested attributes struct (extends AssetAttributes) +- PowerBIDataflowEntityColumnNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .power_bi_related import RelatedPowerBIDataflow + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class PowerBIDataflowEntityColumn(Asset): + """ + Instance of a Power BI Dataflow Entity Column in Atlan. Dataflows are reusable transformation logic that can be shared by many datasets and reports inside Power BI. Each Dataflow has an Entity which represents an instance of SQL data from source, that has columns associated with it. + """ + + POWER_BI_DATAFLOW_ENTITY_NAME: ClassVar[Any] = None + POWER_BI_WORKSPACE_QUALIFIED_NAME: ClassVar[Any] = None + POWER_BI_DATAFLOW_QUALIFIED_NAME: ClassVar[Any] = None + POWER_BI_DATAFLOW_ENTITY_COLUMN_DATA_TYPE: ClassVar[Any] = None + POWER_BI_IS_HIDDEN: ClassVar[Any] = None + POWER_BI_TABLE_QUALIFIED_NAME: ClassVar[Any] = None + POWER_BI_FORMAT_STRING: ClassVar[Any] = None + POWER_BI_ENDORSEMENT: ClassVar[Any] = None + POWER_BI_ENDORSED_BY: ClassVar[Any] = None + POWER_BI_ENDORSED_AT: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + POWER_BI_DATAFLOW: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "PowerBIDataflowEntityColumn" + + power_bi_dataflow_entity_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIDataflowEntityName" + ) + """Unique name of the dataflow entity in which this dataflow entity column exists.""" + + power_bi_workspace_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIWorkspaceQualifiedName" + ) + """Unique name of the workspace in which this dataflow entity column exists.""" + + power_bi_dataflow_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIDataflowQualifiedName" + ) + """Unique name of the dataflow in which this dataflow entity column exists.""" + + power_bi_dataflow_entity_column_data_type: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="powerBIDataflowEntityColumnDataType") + ) + """Data type of this dataflow entity column.""" + + power_bi_is_hidden: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIIsHidden" + ) + """Whether this asset is hidden in Power BI (true) or not (false).""" + + power_bi_table_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBITableQualifiedName" + ) + """Unique name of the Power BI table in which this asset exists.""" + + power_bi_format_string: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIFormatString" + ) + """Format of this asset, as specified in the FORMAT_STRING of the MDX cell property.""" + + power_bi_endorsement: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsement" + ) + """Endorsement status of this asset, in Power BI.""" + + power_bi_endorsed_by: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedBy" + ) + """User who endorsed this asset in Power BI.""" + + power_bi_endorsed_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedAt" + ) + """Time at which this asset was endorsed in Power BI.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + power_bi_dataflow: Union[RelatedPowerBIDataflow, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIDataflow" + ) + """PowerBI Dataflow in which this Dataflow Entity Column exists.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "PowerBIDataflowEntityColumn" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _power_bi_dataflow_entity_column_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> PowerBIDataflowEntityColumn: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + PowerBIDataflowEntityColumn instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _power_bi_dataflow_entity_column_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class PowerBIDataflowEntityColumnAttributes(AssetAttributes): + """PowerBIDataflowEntityColumn-specific attributes for nested API format.""" + + power_bi_dataflow_entity_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIDataflowEntityName" + ) + """Unique name of the dataflow entity in which this dataflow entity column exists.""" + + power_bi_workspace_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIWorkspaceQualifiedName" + ) + """Unique name of the workspace in which this dataflow entity column exists.""" + + power_bi_dataflow_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIDataflowQualifiedName" + ) + """Unique name of the dataflow in which this dataflow entity column exists.""" + + power_bi_dataflow_entity_column_data_type: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="powerBIDataflowEntityColumnDataType") + ) + """Data type of this dataflow entity column.""" + + power_bi_is_hidden: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIIsHidden" + ) + """Whether this asset is hidden in Power BI (true) or not (false).""" + + power_bi_table_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBITableQualifiedName" + ) + """Unique name of the Power BI table in which this asset exists.""" + + power_bi_format_string: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIFormatString" + ) + """Format of this asset, as specified in the FORMAT_STRING of the MDX cell property.""" + + power_bi_endorsement: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsement" + ) + """Endorsement status of this asset, in Power BI.""" + + power_bi_endorsed_by: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedBy" + ) + """User who endorsed this asset in Power BI.""" + + power_bi_endorsed_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedAt" + ) + """Time at which this asset was endorsed in Power BI.""" + + +class PowerBIDataflowEntityColumnRelationshipAttributes(AssetRelationshipAttributes): + """PowerBIDataflowEntityColumn-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + power_bi_dataflow: Union[RelatedPowerBIDataflow, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIDataflow" + ) + """PowerBI Dataflow in which this Dataflow Entity Column exists.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class PowerBIDataflowEntityColumnNested(AssetNested): + """PowerBIDataflowEntityColumn in nested API format for high-performance serialization.""" + + attributes: Union[PowerBIDataflowEntityColumnAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + PowerBIDataflowEntityColumnRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + PowerBIDataflowEntityColumnRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + PowerBIDataflowEntityColumnRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_POWER_BI_DATAFLOW_ENTITY_COLUMN_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "power_bi_dataflow", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_power_bi_dataflow_entity_column_attrs( + attrs: PowerBIDataflowEntityColumnAttributes, obj: PowerBIDataflowEntityColumn +) -> None: + """Populate PowerBIDataflowEntityColumn-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.power_bi_dataflow_entity_name = obj.power_bi_dataflow_entity_name + attrs.power_bi_workspace_qualified_name = obj.power_bi_workspace_qualified_name + attrs.power_bi_dataflow_qualified_name = obj.power_bi_dataflow_qualified_name + attrs.power_bi_dataflow_entity_column_data_type = ( + obj.power_bi_dataflow_entity_column_data_type + ) + attrs.power_bi_is_hidden = obj.power_bi_is_hidden + attrs.power_bi_table_qualified_name = obj.power_bi_table_qualified_name + attrs.power_bi_format_string = obj.power_bi_format_string + attrs.power_bi_endorsement = obj.power_bi_endorsement + attrs.power_bi_endorsed_by = obj.power_bi_endorsed_by + attrs.power_bi_endorsed_at = obj.power_bi_endorsed_at + + +def _extract_power_bi_dataflow_entity_column_attrs( + attrs: PowerBIDataflowEntityColumnAttributes, +) -> dict: + """Extract all PowerBIDataflowEntityColumn attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["power_bi_dataflow_entity_name"] = attrs.power_bi_dataflow_entity_name + result["power_bi_workspace_qualified_name"] = ( + attrs.power_bi_workspace_qualified_name + ) + result["power_bi_dataflow_qualified_name"] = attrs.power_bi_dataflow_qualified_name + result["power_bi_dataflow_entity_column_data_type"] = ( + attrs.power_bi_dataflow_entity_column_data_type + ) + result["power_bi_is_hidden"] = attrs.power_bi_is_hidden + result["power_bi_table_qualified_name"] = attrs.power_bi_table_qualified_name + result["power_bi_format_string"] = attrs.power_bi_format_string + result["power_bi_endorsement"] = attrs.power_bi_endorsement + result["power_bi_endorsed_by"] = attrs.power_bi_endorsed_by + result["power_bi_endorsed_at"] = attrs.power_bi_endorsed_at + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _power_bi_dataflow_entity_column_to_nested( + power_bi_dataflow_entity_column: PowerBIDataflowEntityColumn, +) -> PowerBIDataflowEntityColumnNested: + """Convert flat PowerBIDataflowEntityColumn to nested format.""" + attrs = PowerBIDataflowEntityColumnAttributes() + _populate_power_bi_dataflow_entity_column_attrs( + attrs, power_bi_dataflow_entity_column + ) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + power_bi_dataflow_entity_column, + _POWER_BI_DATAFLOW_ENTITY_COLUMN_REL_FIELDS, + PowerBIDataflowEntityColumnRelationshipAttributes, + ) + return PowerBIDataflowEntityColumnNested( + guid=power_bi_dataflow_entity_column.guid, + type_name=power_bi_dataflow_entity_column.type_name, + status=power_bi_dataflow_entity_column.status, + version=power_bi_dataflow_entity_column.version, + create_time=power_bi_dataflow_entity_column.create_time, + update_time=power_bi_dataflow_entity_column.update_time, + created_by=power_bi_dataflow_entity_column.created_by, + updated_by=power_bi_dataflow_entity_column.updated_by, + classifications=power_bi_dataflow_entity_column.classifications, + classification_names=power_bi_dataflow_entity_column.classification_names, + meanings=power_bi_dataflow_entity_column.meanings, + labels=power_bi_dataflow_entity_column.labels, + business_attributes=power_bi_dataflow_entity_column.business_attributes, + custom_attributes=power_bi_dataflow_entity_column.custom_attributes, + pending_tasks=power_bi_dataflow_entity_column.pending_tasks, + proxy=power_bi_dataflow_entity_column.proxy, + is_incomplete=power_bi_dataflow_entity_column.is_incomplete, + provenance_type=power_bi_dataflow_entity_column.provenance_type, + home_id=power_bi_dataflow_entity_column.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _power_bi_dataflow_entity_column_from_nested( + nested: PowerBIDataflowEntityColumnNested, +) -> PowerBIDataflowEntityColumn: + """Convert nested format to flat PowerBIDataflowEntityColumn.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else PowerBIDataflowEntityColumnAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _POWER_BI_DATAFLOW_ENTITY_COLUMN_REL_FIELDS, + PowerBIDataflowEntityColumnRelationshipAttributes, + ) + return PowerBIDataflowEntityColumn( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_power_bi_dataflow_entity_column_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _power_bi_dataflow_entity_column_to_nested_bytes( + power_bi_dataflow_entity_column: PowerBIDataflowEntityColumn, serde: Serde +) -> bytes: + """Convert flat PowerBIDataflowEntityColumn to nested JSON bytes.""" + return serde.encode( + _power_bi_dataflow_entity_column_to_nested(power_bi_dataflow_entity_column) + ) + + +def _power_bi_dataflow_entity_column_from_nested_bytes( + data: bytes, serde: Serde +) -> PowerBIDataflowEntityColumn: + """Convert nested JSON bytes to flat PowerBIDataflowEntityColumn.""" + nested = serde.decode(data, PowerBIDataflowEntityColumnNested) + return _power_bi_dataflow_entity_column_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +PowerBIDataflowEntityColumn.POWER_BI_DATAFLOW_ENTITY_NAME = KeywordField( + "powerBIDataflowEntityName", "powerBIDataflowEntityName" +) +PowerBIDataflowEntityColumn.POWER_BI_WORKSPACE_QUALIFIED_NAME = KeywordField( + "powerBIWorkspaceQualifiedName", "powerBIWorkspaceQualifiedName" +) +PowerBIDataflowEntityColumn.POWER_BI_DATAFLOW_QUALIFIED_NAME = KeywordField( + "powerBIDataflowQualifiedName", "powerBIDataflowQualifiedName" +) +PowerBIDataflowEntityColumn.POWER_BI_DATAFLOW_ENTITY_COLUMN_DATA_TYPE = KeywordField( + "powerBIDataflowEntityColumnDataType", "powerBIDataflowEntityColumnDataType" +) +PowerBIDataflowEntityColumn.POWER_BI_IS_HIDDEN = BooleanField( + "powerBIIsHidden", "powerBIIsHidden" +) +PowerBIDataflowEntityColumn.POWER_BI_TABLE_QUALIFIED_NAME = KeywordTextField( + "powerBITableQualifiedName", + "powerBITableQualifiedName", + "powerBITableQualifiedName.text", +) +PowerBIDataflowEntityColumn.POWER_BI_FORMAT_STRING = KeywordField( + "powerBIFormatString", "powerBIFormatString" +) +PowerBIDataflowEntityColumn.POWER_BI_ENDORSEMENT = KeywordField( + "powerBIEndorsement", "powerBIEndorsement" +) +PowerBIDataflowEntityColumn.POWER_BI_ENDORSED_BY = KeywordField( + "powerBIEndorsedBy", "powerBIEndorsedBy" +) +PowerBIDataflowEntityColumn.POWER_BI_ENDORSED_AT = NumericField( + "powerBIEndorsedAt", "powerBIEndorsedAt" +) +PowerBIDataflowEntityColumn.INPUT_TO_AIRFLOW_TASKS = RelationField( + "inputToAirflowTasks" +) +PowerBIDataflowEntityColumn.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +PowerBIDataflowEntityColumn.ANOMALO_CHECKS = RelationField("anomaloChecks") +PowerBIDataflowEntityColumn.APPLICATION = RelationField("application") +PowerBIDataflowEntityColumn.APPLICATION_FIELD = RelationField("applicationField") +PowerBIDataflowEntityColumn.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +PowerBIDataflowEntityColumn.INPUT_PORT_DATA_PRODUCTS = RelationField( + "inputPortDataProducts" +) +PowerBIDataflowEntityColumn.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +PowerBIDataflowEntityColumn.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +PowerBIDataflowEntityColumn.METRICS = RelationField("metrics") +PowerBIDataflowEntityColumn.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +PowerBIDataflowEntityColumn.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +PowerBIDataflowEntityColumn.MEANINGS = RelationField("meanings") +PowerBIDataflowEntityColumn.MC_MONITORS = RelationField("mcMonitors") +PowerBIDataflowEntityColumn.MC_INCIDENTS = RelationField("mcIncidents") +PowerBIDataflowEntityColumn.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +PowerBIDataflowEntityColumn.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +PowerBIDataflowEntityColumn.POWER_BI_DATAFLOW = RelationField("powerBIDataflow") +PowerBIDataflowEntityColumn.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +PowerBIDataflowEntityColumn.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +PowerBIDataflowEntityColumn.USER_DEF_RELATIONSHIP_TO = RelationField( + "userDefRelationshipTo" +) +PowerBIDataflowEntityColumn.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +PowerBIDataflowEntityColumn.FILES = RelationField("files") +PowerBIDataflowEntityColumn.LINKS = RelationField("links") +PowerBIDataflowEntityColumn.README = RelationField("readme") +PowerBIDataflowEntityColumn.SCHEMA_REGISTRY_SUBJECTS = RelationField( + "schemaRegistrySubjects" +) +PowerBIDataflowEntityColumn.SODA_CHECKS = RelationField("sodaChecks") +PowerBIDataflowEntityColumn.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +PowerBIDataflowEntityColumn.OUTPUT_FROM_SPARK_JOBS = RelationField( + "outputFromSparkJobs" +) diff --git a/pyatlan_v9/model/assets/power_bi_dataset.py b/pyatlan_v9/model/assets/power_bi_dataset.py new file mode 100644 index 000000000..3dd62f315 --- /dev/null +++ b/pyatlan_v9/model/assets/power_bi_dataset.py @@ -0,0 +1,733 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +PowerBIDataset asset model with flattened inheritance. + +This module provides: +- PowerBIDataset: Flat asset class (easy to use) +- PowerBIDatasetAttributes: Nested attributes struct (extends AssetAttributes) +- PowerBIDatasetNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .power_bi_related import ( + RelatedPowerBIDataflow, + RelatedPowerBIDatasource, + RelatedPowerBIReport, + RelatedPowerBITable, + RelatedPowerBITile, + RelatedPowerBIWorkspace, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class PowerBIDataset(Asset): + """ + Instance of a Power BI dataset in Atlan. + """ + + WORKSPACE_QUALIFIED_NAME: ClassVar[Any] = None + WEB_URL: ClassVar[Any] = None + POWER_BI_IS_HIDDEN: ClassVar[Any] = None + POWER_BI_TABLE_QUALIFIED_NAME: ClassVar[Any] = None + POWER_BI_FORMAT_STRING: ClassVar[Any] = None + POWER_BI_ENDORSEMENT: ClassVar[Any] = None + POWER_BI_ENDORSED_BY: ClassVar[Any] = None + POWER_BI_ENDORSED_AT: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + WORKSPACE: ClassVar[Any] = None + DATAFLOWS: ClassVar[Any] = None + DATASOURCES: ClassVar[Any] = None + REPORTS: ClassVar[Any] = None + TABLES: ClassVar[Any] = None + TILES: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "PowerBIDataset" + + workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace in which this dataset exists.""" + + web_url: Union[str, None, UnsetType] = UNSET + """Deprecated. See 'sourceUrl' instead.""" + + power_bi_is_hidden: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIIsHidden" + ) + """Whether this asset is hidden in Power BI (true) or not (false).""" + + power_bi_table_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBITableQualifiedName" + ) + """Unique name of the Power BI table in which this asset exists.""" + + power_bi_format_string: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIFormatString" + ) + """Format of this asset, as specified in the FORMAT_STRING of the MDX cell property.""" + + power_bi_endorsement: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsement" + ) + """Endorsement status of this asset, in Power BI.""" + + power_bi_endorsed_by: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedBy" + ) + """User who endorsed this asset in Power BI.""" + + power_bi_endorsed_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedAt" + ) + """Time at which this asset was endorsed in Power BI.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + workspace: Union[RelatedPowerBIWorkspace, None, UnsetType] = UNSET + """Workspace in which this dataset exists.""" + + dataflows: Union[List[RelatedPowerBIDataflow], None, UnsetType] = UNSET + """Dataflows that use this dataset.""" + + datasources: Union[List[RelatedPowerBIDatasource], None, UnsetType] = UNSET + """Datasources that use this dataset.""" + + reports: Union[List[RelatedPowerBIReport], None, UnsetType] = UNSET + """Reports that were built using this dataset.""" + + tables: Union[List[RelatedPowerBITable], None, UnsetType] = UNSET + """Tables that exist within this dataset.""" + + tiles: Union[List[RelatedPowerBITile], None, UnsetType] = UNSET + """Tiles that exist within this dataset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "PowerBIDataset" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _power_bi_dataset_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> PowerBIDataset: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + PowerBIDataset instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _power_bi_dataset_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class PowerBIDatasetAttributes(AssetAttributes): + """PowerBIDataset-specific attributes for nested API format.""" + + workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace in which this dataset exists.""" + + web_url: Union[str, None, UnsetType] = UNSET + """Deprecated. See 'sourceUrl' instead.""" + + power_bi_is_hidden: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIIsHidden" + ) + """Whether this asset is hidden in Power BI (true) or not (false).""" + + power_bi_table_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBITableQualifiedName" + ) + """Unique name of the Power BI table in which this asset exists.""" + + power_bi_format_string: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIFormatString" + ) + """Format of this asset, as specified in the FORMAT_STRING of the MDX cell property.""" + + power_bi_endorsement: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsement" + ) + """Endorsement status of this asset, in Power BI.""" + + power_bi_endorsed_by: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedBy" + ) + """User who endorsed this asset in Power BI.""" + + power_bi_endorsed_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedAt" + ) + """Time at which this asset was endorsed in Power BI.""" + + +class PowerBIDatasetRelationshipAttributes(AssetRelationshipAttributes): + """PowerBIDataset-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + workspace: Union[RelatedPowerBIWorkspace, None, UnsetType] = UNSET + """Workspace in which this dataset exists.""" + + dataflows: Union[List[RelatedPowerBIDataflow], None, UnsetType] = UNSET + """Dataflows that use this dataset.""" + + datasources: Union[List[RelatedPowerBIDatasource], None, UnsetType] = UNSET + """Datasources that use this dataset.""" + + reports: Union[List[RelatedPowerBIReport], None, UnsetType] = UNSET + """Reports that were built using this dataset.""" + + tables: Union[List[RelatedPowerBITable], None, UnsetType] = UNSET + """Tables that exist within this dataset.""" + + tiles: Union[List[RelatedPowerBITile], None, UnsetType] = UNSET + """Tiles that exist within this dataset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class PowerBIDatasetNested(AssetNested): + """PowerBIDataset in nested API format for high-performance serialization.""" + + attributes: Union[PowerBIDatasetAttributes, UnsetType] = UNSET + relationship_attributes: Union[PowerBIDatasetRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + PowerBIDatasetRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + PowerBIDatasetRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_POWER_BI_DATASET_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "workspace", + "dataflows", + "datasources", + "reports", + "tables", + "tiles", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_power_bi_dataset_attrs( + attrs: PowerBIDatasetAttributes, obj: PowerBIDataset +) -> None: + """Populate PowerBIDataset-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.workspace_qualified_name = obj.workspace_qualified_name + attrs.web_url = obj.web_url + attrs.power_bi_is_hidden = obj.power_bi_is_hidden + attrs.power_bi_table_qualified_name = obj.power_bi_table_qualified_name + attrs.power_bi_format_string = obj.power_bi_format_string + attrs.power_bi_endorsement = obj.power_bi_endorsement + attrs.power_bi_endorsed_by = obj.power_bi_endorsed_by + attrs.power_bi_endorsed_at = obj.power_bi_endorsed_at + + +def _extract_power_bi_dataset_attrs(attrs: PowerBIDatasetAttributes) -> dict: + """Extract all PowerBIDataset attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["workspace_qualified_name"] = attrs.workspace_qualified_name + result["web_url"] = attrs.web_url + result["power_bi_is_hidden"] = attrs.power_bi_is_hidden + result["power_bi_table_qualified_name"] = attrs.power_bi_table_qualified_name + result["power_bi_format_string"] = attrs.power_bi_format_string + result["power_bi_endorsement"] = attrs.power_bi_endorsement + result["power_bi_endorsed_by"] = attrs.power_bi_endorsed_by + result["power_bi_endorsed_at"] = attrs.power_bi_endorsed_at + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _power_bi_dataset_to_nested( + power_bi_dataset: PowerBIDataset, +) -> PowerBIDatasetNested: + """Convert flat PowerBIDataset to nested format.""" + attrs = PowerBIDatasetAttributes() + _populate_power_bi_dataset_attrs(attrs, power_bi_dataset) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + power_bi_dataset, + _POWER_BI_DATASET_REL_FIELDS, + PowerBIDatasetRelationshipAttributes, + ) + return PowerBIDatasetNested( + guid=power_bi_dataset.guid, + type_name=power_bi_dataset.type_name, + status=power_bi_dataset.status, + version=power_bi_dataset.version, + create_time=power_bi_dataset.create_time, + update_time=power_bi_dataset.update_time, + created_by=power_bi_dataset.created_by, + updated_by=power_bi_dataset.updated_by, + classifications=power_bi_dataset.classifications, + classification_names=power_bi_dataset.classification_names, + meanings=power_bi_dataset.meanings, + labels=power_bi_dataset.labels, + business_attributes=power_bi_dataset.business_attributes, + custom_attributes=power_bi_dataset.custom_attributes, + pending_tasks=power_bi_dataset.pending_tasks, + proxy=power_bi_dataset.proxy, + is_incomplete=power_bi_dataset.is_incomplete, + provenance_type=power_bi_dataset.provenance_type, + home_id=power_bi_dataset.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _power_bi_dataset_from_nested(nested: PowerBIDatasetNested) -> PowerBIDataset: + """Convert nested format to flat PowerBIDataset.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else PowerBIDatasetAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _POWER_BI_DATASET_REL_FIELDS, + PowerBIDatasetRelationshipAttributes, + ) + return PowerBIDataset( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_power_bi_dataset_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _power_bi_dataset_to_nested_bytes( + power_bi_dataset: PowerBIDataset, serde: Serde +) -> bytes: + """Convert flat PowerBIDataset to nested JSON bytes.""" + return serde.encode(_power_bi_dataset_to_nested(power_bi_dataset)) + + +def _power_bi_dataset_from_nested_bytes(data: bytes, serde: Serde) -> PowerBIDataset: + """Convert nested JSON bytes to flat PowerBIDataset.""" + nested = serde.decode(data, PowerBIDatasetNested) + return _power_bi_dataset_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +PowerBIDataset.WORKSPACE_QUALIFIED_NAME = KeywordField( + "workspaceQualifiedName", "workspaceQualifiedName" +) +PowerBIDataset.WEB_URL = KeywordField("webUrl", "webUrl") +PowerBIDataset.POWER_BI_IS_HIDDEN = BooleanField("powerBIIsHidden", "powerBIIsHidden") +PowerBIDataset.POWER_BI_TABLE_QUALIFIED_NAME = KeywordTextField( + "powerBITableQualifiedName", + "powerBITableQualifiedName", + "powerBITableQualifiedName.text", +) +PowerBIDataset.POWER_BI_FORMAT_STRING = KeywordField( + "powerBIFormatString", "powerBIFormatString" +) +PowerBIDataset.POWER_BI_ENDORSEMENT = KeywordField( + "powerBIEndorsement", "powerBIEndorsement" +) +PowerBIDataset.POWER_BI_ENDORSED_BY = KeywordField( + "powerBIEndorsedBy", "powerBIEndorsedBy" +) +PowerBIDataset.POWER_BI_ENDORSED_AT = NumericField( + "powerBIEndorsedAt", "powerBIEndorsedAt" +) +PowerBIDataset.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +PowerBIDataset.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +PowerBIDataset.ANOMALO_CHECKS = RelationField("anomaloChecks") +PowerBIDataset.APPLICATION = RelationField("application") +PowerBIDataset.APPLICATION_FIELD = RelationField("applicationField") +PowerBIDataset.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +PowerBIDataset.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +PowerBIDataset.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +PowerBIDataset.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +PowerBIDataset.METRICS = RelationField("metrics") +PowerBIDataset.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +PowerBIDataset.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +PowerBIDataset.MEANINGS = RelationField("meanings") +PowerBIDataset.MC_MONITORS = RelationField("mcMonitors") +PowerBIDataset.MC_INCIDENTS = RelationField("mcIncidents") +PowerBIDataset.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +PowerBIDataset.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +PowerBIDataset.WORKSPACE = RelationField("workspace") +PowerBIDataset.DATAFLOWS = RelationField("dataflows") +PowerBIDataset.DATASOURCES = RelationField("datasources") +PowerBIDataset.REPORTS = RelationField("reports") +PowerBIDataset.TABLES = RelationField("tables") +PowerBIDataset.TILES = RelationField("tiles") +PowerBIDataset.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +PowerBIDataset.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +PowerBIDataset.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +PowerBIDataset.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +PowerBIDataset.FILES = RelationField("files") +PowerBIDataset.LINKS = RelationField("links") +PowerBIDataset.README = RelationField("readme") +PowerBIDataset.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +PowerBIDataset.SODA_CHECKS = RelationField("sodaChecks") +PowerBIDataset.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +PowerBIDataset.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/power_bi_datasource.py b/pyatlan_v9/model/assets/power_bi_datasource.py new file mode 100644 index 000000000..212e7d473 --- /dev/null +++ b/pyatlan_v9/model/assets/power_bi_datasource.py @@ -0,0 +1,694 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +PowerBIDatasource asset model with flattened inheritance. + +This module provides: +- PowerBIDatasource: Flat asset class (easy to use) +- PowerBIDatasourceAttributes: Nested attributes struct (extends AssetAttributes) +- PowerBIDatasourceNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .power_bi_related import RelatedPowerBIDataflow, RelatedPowerBIDataset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class PowerBIDatasource(Asset): + """ + Instance of a Power BI datasource in Atlan. + """ + + CONNECTION_DETAILS: ClassVar[Any] = None + POWER_BI_IS_HIDDEN: ClassVar[Any] = None + POWER_BI_TABLE_QUALIFIED_NAME: ClassVar[Any] = None + POWER_BI_FORMAT_STRING: ClassVar[Any] = None + POWER_BI_ENDORSEMENT: ClassVar[Any] = None + POWER_BI_ENDORSED_BY: ClassVar[Any] = None + POWER_BI_ENDORSED_AT: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + DATASETS: ClassVar[Any] = None + POWER_BI_DATAFLOWS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "PowerBIDatasource" + + connection_details: Union[Dict[str, str], None, UnsetType] = UNSET + """Connection details of the datasource.""" + + power_bi_is_hidden: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIIsHidden" + ) + """Whether this asset is hidden in Power BI (true) or not (false).""" + + power_bi_table_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBITableQualifiedName" + ) + """Unique name of the Power BI table in which this asset exists.""" + + power_bi_format_string: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIFormatString" + ) + """Format of this asset, as specified in the FORMAT_STRING of the MDX cell property.""" + + power_bi_endorsement: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsement" + ) + """Endorsement status of this asset, in Power BI.""" + + power_bi_endorsed_by: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedBy" + ) + """User who endorsed this asset in Power BI.""" + + power_bi_endorsed_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedAt" + ) + """Time at which this asset was endorsed in Power BI.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + datasets: Union[List[RelatedPowerBIDataset], None, UnsetType] = UNSET + """Datasets created by this datasource.""" + + power_bi_dataflows: Union[List[RelatedPowerBIDataflow], None, UnsetType] = ( + msgspec.field(default=UNSET, name="powerBIDataflows") + ) + """PowerBI Dataflows that are associated with this PowerBI Datasource.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "PowerBIDatasource" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _power_bi_datasource_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> PowerBIDatasource: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + PowerBIDatasource instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _power_bi_datasource_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class PowerBIDatasourceAttributes(AssetAttributes): + """PowerBIDatasource-specific attributes for nested API format.""" + + connection_details: Union[Dict[str, str], None, UnsetType] = UNSET + """Connection details of the datasource.""" + + power_bi_is_hidden: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIIsHidden" + ) + """Whether this asset is hidden in Power BI (true) or not (false).""" + + power_bi_table_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBITableQualifiedName" + ) + """Unique name of the Power BI table in which this asset exists.""" + + power_bi_format_string: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIFormatString" + ) + """Format of this asset, as specified in the FORMAT_STRING of the MDX cell property.""" + + power_bi_endorsement: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsement" + ) + """Endorsement status of this asset, in Power BI.""" + + power_bi_endorsed_by: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedBy" + ) + """User who endorsed this asset in Power BI.""" + + power_bi_endorsed_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedAt" + ) + """Time at which this asset was endorsed in Power BI.""" + + +class PowerBIDatasourceRelationshipAttributes(AssetRelationshipAttributes): + """PowerBIDatasource-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + datasets: Union[List[RelatedPowerBIDataset], None, UnsetType] = UNSET + """Datasets created by this datasource.""" + + power_bi_dataflows: Union[List[RelatedPowerBIDataflow], None, UnsetType] = ( + msgspec.field(default=UNSET, name="powerBIDataflows") + ) + """PowerBI Dataflows that are associated with this PowerBI Datasource.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class PowerBIDatasourceNested(AssetNested): + """PowerBIDatasource in nested API format for high-performance serialization.""" + + attributes: Union[PowerBIDatasourceAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + PowerBIDatasourceRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + PowerBIDatasourceRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + PowerBIDatasourceRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_POWER_BI_DATASOURCE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "datasets", + "power_bi_dataflows", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_power_bi_datasource_attrs( + attrs: PowerBIDatasourceAttributes, obj: PowerBIDatasource +) -> None: + """Populate PowerBIDatasource-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.connection_details = obj.connection_details + attrs.power_bi_is_hidden = obj.power_bi_is_hidden + attrs.power_bi_table_qualified_name = obj.power_bi_table_qualified_name + attrs.power_bi_format_string = obj.power_bi_format_string + attrs.power_bi_endorsement = obj.power_bi_endorsement + attrs.power_bi_endorsed_by = obj.power_bi_endorsed_by + attrs.power_bi_endorsed_at = obj.power_bi_endorsed_at + + +def _extract_power_bi_datasource_attrs(attrs: PowerBIDatasourceAttributes) -> dict: + """Extract all PowerBIDatasource attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["connection_details"] = attrs.connection_details + result["power_bi_is_hidden"] = attrs.power_bi_is_hidden + result["power_bi_table_qualified_name"] = attrs.power_bi_table_qualified_name + result["power_bi_format_string"] = attrs.power_bi_format_string + result["power_bi_endorsement"] = attrs.power_bi_endorsement + result["power_bi_endorsed_by"] = attrs.power_bi_endorsed_by + result["power_bi_endorsed_at"] = attrs.power_bi_endorsed_at + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _power_bi_datasource_to_nested( + power_bi_datasource: PowerBIDatasource, +) -> PowerBIDatasourceNested: + """Convert flat PowerBIDatasource to nested format.""" + attrs = PowerBIDatasourceAttributes() + _populate_power_bi_datasource_attrs(attrs, power_bi_datasource) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + power_bi_datasource, + _POWER_BI_DATASOURCE_REL_FIELDS, + PowerBIDatasourceRelationshipAttributes, + ) + return PowerBIDatasourceNested( + guid=power_bi_datasource.guid, + type_name=power_bi_datasource.type_name, + status=power_bi_datasource.status, + version=power_bi_datasource.version, + create_time=power_bi_datasource.create_time, + update_time=power_bi_datasource.update_time, + created_by=power_bi_datasource.created_by, + updated_by=power_bi_datasource.updated_by, + classifications=power_bi_datasource.classifications, + classification_names=power_bi_datasource.classification_names, + meanings=power_bi_datasource.meanings, + labels=power_bi_datasource.labels, + business_attributes=power_bi_datasource.business_attributes, + custom_attributes=power_bi_datasource.custom_attributes, + pending_tasks=power_bi_datasource.pending_tasks, + proxy=power_bi_datasource.proxy, + is_incomplete=power_bi_datasource.is_incomplete, + provenance_type=power_bi_datasource.provenance_type, + home_id=power_bi_datasource.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _power_bi_datasource_from_nested( + nested: PowerBIDatasourceNested, +) -> PowerBIDatasource: + """Convert nested format to flat PowerBIDatasource.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else PowerBIDatasourceAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _POWER_BI_DATASOURCE_REL_FIELDS, + PowerBIDatasourceRelationshipAttributes, + ) + return PowerBIDatasource( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_power_bi_datasource_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _power_bi_datasource_to_nested_bytes( + power_bi_datasource: PowerBIDatasource, serde: Serde +) -> bytes: + """Convert flat PowerBIDatasource to nested JSON bytes.""" + return serde.encode(_power_bi_datasource_to_nested(power_bi_datasource)) + + +def _power_bi_datasource_from_nested_bytes( + data: bytes, serde: Serde +) -> PowerBIDatasource: + """Convert nested JSON bytes to flat PowerBIDatasource.""" + nested = serde.decode(data, PowerBIDatasourceNested) + return _power_bi_datasource_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +PowerBIDatasource.CONNECTION_DETAILS = KeywordField( + "connectionDetails", "connectionDetails" +) +PowerBIDatasource.POWER_BI_IS_HIDDEN = BooleanField( + "powerBIIsHidden", "powerBIIsHidden" +) +PowerBIDatasource.POWER_BI_TABLE_QUALIFIED_NAME = KeywordTextField( + "powerBITableQualifiedName", + "powerBITableQualifiedName", + "powerBITableQualifiedName.text", +) +PowerBIDatasource.POWER_BI_FORMAT_STRING = KeywordField( + "powerBIFormatString", "powerBIFormatString" +) +PowerBIDatasource.POWER_BI_ENDORSEMENT = KeywordField( + "powerBIEndorsement", "powerBIEndorsement" +) +PowerBIDatasource.POWER_BI_ENDORSED_BY = KeywordField( + "powerBIEndorsedBy", "powerBIEndorsedBy" +) +PowerBIDatasource.POWER_BI_ENDORSED_AT = NumericField( + "powerBIEndorsedAt", "powerBIEndorsedAt" +) +PowerBIDatasource.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +PowerBIDatasource.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +PowerBIDatasource.ANOMALO_CHECKS = RelationField("anomaloChecks") +PowerBIDatasource.APPLICATION = RelationField("application") +PowerBIDatasource.APPLICATION_FIELD = RelationField("applicationField") +PowerBIDatasource.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +PowerBIDatasource.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +PowerBIDatasource.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +PowerBIDatasource.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +PowerBIDatasource.METRICS = RelationField("metrics") +PowerBIDatasource.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +PowerBIDatasource.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +PowerBIDatasource.MEANINGS = RelationField("meanings") +PowerBIDatasource.MC_MONITORS = RelationField("mcMonitors") +PowerBIDatasource.MC_INCIDENTS = RelationField("mcIncidents") +PowerBIDatasource.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +PowerBIDatasource.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +PowerBIDatasource.DATASETS = RelationField("datasets") +PowerBIDatasource.POWER_BI_DATAFLOWS = RelationField("powerBIDataflows") +PowerBIDatasource.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +PowerBIDatasource.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +PowerBIDatasource.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +PowerBIDatasource.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +PowerBIDatasource.FILES = RelationField("files") +PowerBIDatasource.LINKS = RelationField("links") +PowerBIDatasource.README = RelationField("readme") +PowerBIDatasource.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +PowerBIDatasource.SODA_CHECKS = RelationField("sodaChecks") +PowerBIDatasource.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +PowerBIDatasource.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/power_bi_measure.py b/pyatlan_v9/model/assets/power_bi_measure.py new file mode 100644 index 000000000..40fcfdbc3 --- /dev/null +++ b/pyatlan_v9/model/assets/power_bi_measure.py @@ -0,0 +1,730 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +PowerBIMeasure asset model with flattened inheritance. + +This module provides: +- PowerBIMeasure: Flat asset class (easy to use) +- PowerBIMeasureAttributes: Nested attributes struct (extends AssetAttributes) +- PowerBIMeasureNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .power_bi_related import RelatedPowerBIColumn, RelatedPowerBITable + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class PowerBIMeasure(Asset): + """ + Instance of a Power BI measure in Atlan. Measures define calculations in a DAX model, which helps calculate values based on each row. + """ + + WORKSPACE_QUALIFIED_NAME: ClassVar[Any] = None + DATASET_QUALIFIED_NAME: ClassVar[Any] = None + POWER_BI_MEASURE_EXPRESSION: ClassVar[Any] = None + POWER_BI_IS_EXTERNAL_MEASURE: ClassVar[Any] = None + POWER_BI_IS_HIDDEN: ClassVar[Any] = None + POWER_BI_TABLE_QUALIFIED_NAME: ClassVar[Any] = None + POWER_BI_FORMAT_STRING: ClassVar[Any] = None + POWER_BI_ENDORSEMENT: ClassVar[Any] = None + POWER_BI_ENDORSED_BY: ClassVar[Any] = None + POWER_BI_ENDORSED_AT: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + TABLE: ClassVar[Any] = None + POWER_BI_COLUMNS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "PowerBIMeasure" + + workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace in which this measure exists.""" + + dataset_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dataset in which this measure exists.""" + + power_bi_measure_expression: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIMeasureExpression" + ) + """DAX expression for this measure.""" + + power_bi_is_external_measure: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIIsExternalMeasure" + ) + """Whether this measure is external (true) or internal (false).""" + + power_bi_is_hidden: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIIsHidden" + ) + """Whether this asset is hidden in Power BI (true) or not (false).""" + + power_bi_table_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBITableQualifiedName" + ) + """Unique name of the Power BI table in which this asset exists.""" + + power_bi_format_string: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIFormatString" + ) + """Format of this asset, as specified in the FORMAT_STRING of the MDX cell property.""" + + power_bi_endorsement: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsement" + ) + """Endorsement status of this asset, in Power BI.""" + + power_bi_endorsed_by: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedBy" + ) + """User who endorsed this asset in Power BI.""" + + power_bi_endorsed_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedAt" + ) + """Time at which this asset was endorsed in Power BI.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + table: Union[RelatedPowerBITable, None, UnsetType] = UNSET + """Table in which this measure exists.""" + + power_bi_columns: Union[List[RelatedPowerBIColumn], None, UnsetType] = ( + msgspec.field(default=UNSET, name="powerBIColumns") + ) + """PowerBI Columns that are associated with this PowerBI Measure.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "PowerBIMeasure" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _power_bi_measure_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> PowerBIMeasure: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + PowerBIMeasure instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _power_bi_measure_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class PowerBIMeasureAttributes(AssetAttributes): + """PowerBIMeasure-specific attributes for nested API format.""" + + workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace in which this measure exists.""" + + dataset_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dataset in which this measure exists.""" + + power_bi_measure_expression: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIMeasureExpression" + ) + """DAX expression for this measure.""" + + power_bi_is_external_measure: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIIsExternalMeasure" + ) + """Whether this measure is external (true) or internal (false).""" + + power_bi_is_hidden: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIIsHidden" + ) + """Whether this asset is hidden in Power BI (true) or not (false).""" + + power_bi_table_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBITableQualifiedName" + ) + """Unique name of the Power BI table in which this asset exists.""" + + power_bi_format_string: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIFormatString" + ) + """Format of this asset, as specified in the FORMAT_STRING of the MDX cell property.""" + + power_bi_endorsement: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsement" + ) + """Endorsement status of this asset, in Power BI.""" + + power_bi_endorsed_by: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedBy" + ) + """User who endorsed this asset in Power BI.""" + + power_bi_endorsed_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedAt" + ) + """Time at which this asset was endorsed in Power BI.""" + + +class PowerBIMeasureRelationshipAttributes(AssetRelationshipAttributes): + """PowerBIMeasure-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + table: Union[RelatedPowerBITable, None, UnsetType] = UNSET + """Table in which this measure exists.""" + + power_bi_columns: Union[List[RelatedPowerBIColumn], None, UnsetType] = ( + msgspec.field(default=UNSET, name="powerBIColumns") + ) + """PowerBI Columns that are associated with this PowerBI Measure.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class PowerBIMeasureNested(AssetNested): + """PowerBIMeasure in nested API format for high-performance serialization.""" + + attributes: Union[PowerBIMeasureAttributes, UnsetType] = UNSET + relationship_attributes: Union[PowerBIMeasureRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + PowerBIMeasureRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + PowerBIMeasureRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_POWER_BI_MEASURE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "table", + "power_bi_columns", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_power_bi_measure_attrs( + attrs: PowerBIMeasureAttributes, obj: PowerBIMeasure +) -> None: + """Populate PowerBIMeasure-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.workspace_qualified_name = obj.workspace_qualified_name + attrs.dataset_qualified_name = obj.dataset_qualified_name + attrs.power_bi_measure_expression = obj.power_bi_measure_expression + attrs.power_bi_is_external_measure = obj.power_bi_is_external_measure + attrs.power_bi_is_hidden = obj.power_bi_is_hidden + attrs.power_bi_table_qualified_name = obj.power_bi_table_qualified_name + attrs.power_bi_format_string = obj.power_bi_format_string + attrs.power_bi_endorsement = obj.power_bi_endorsement + attrs.power_bi_endorsed_by = obj.power_bi_endorsed_by + attrs.power_bi_endorsed_at = obj.power_bi_endorsed_at + + +def _extract_power_bi_measure_attrs(attrs: PowerBIMeasureAttributes) -> dict: + """Extract all PowerBIMeasure attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["workspace_qualified_name"] = attrs.workspace_qualified_name + result["dataset_qualified_name"] = attrs.dataset_qualified_name + result["power_bi_measure_expression"] = attrs.power_bi_measure_expression + result["power_bi_is_external_measure"] = attrs.power_bi_is_external_measure + result["power_bi_is_hidden"] = attrs.power_bi_is_hidden + result["power_bi_table_qualified_name"] = attrs.power_bi_table_qualified_name + result["power_bi_format_string"] = attrs.power_bi_format_string + result["power_bi_endorsement"] = attrs.power_bi_endorsement + result["power_bi_endorsed_by"] = attrs.power_bi_endorsed_by + result["power_bi_endorsed_at"] = attrs.power_bi_endorsed_at + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _power_bi_measure_to_nested( + power_bi_measure: PowerBIMeasure, +) -> PowerBIMeasureNested: + """Convert flat PowerBIMeasure to nested format.""" + attrs = PowerBIMeasureAttributes() + _populate_power_bi_measure_attrs(attrs, power_bi_measure) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + power_bi_measure, + _POWER_BI_MEASURE_REL_FIELDS, + PowerBIMeasureRelationshipAttributes, + ) + return PowerBIMeasureNested( + guid=power_bi_measure.guid, + type_name=power_bi_measure.type_name, + status=power_bi_measure.status, + version=power_bi_measure.version, + create_time=power_bi_measure.create_time, + update_time=power_bi_measure.update_time, + created_by=power_bi_measure.created_by, + updated_by=power_bi_measure.updated_by, + classifications=power_bi_measure.classifications, + classification_names=power_bi_measure.classification_names, + meanings=power_bi_measure.meanings, + labels=power_bi_measure.labels, + business_attributes=power_bi_measure.business_attributes, + custom_attributes=power_bi_measure.custom_attributes, + pending_tasks=power_bi_measure.pending_tasks, + proxy=power_bi_measure.proxy, + is_incomplete=power_bi_measure.is_incomplete, + provenance_type=power_bi_measure.provenance_type, + home_id=power_bi_measure.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _power_bi_measure_from_nested(nested: PowerBIMeasureNested) -> PowerBIMeasure: + """Convert nested format to flat PowerBIMeasure.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else PowerBIMeasureAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _POWER_BI_MEASURE_REL_FIELDS, + PowerBIMeasureRelationshipAttributes, + ) + return PowerBIMeasure( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_power_bi_measure_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _power_bi_measure_to_nested_bytes( + power_bi_measure: PowerBIMeasure, serde: Serde +) -> bytes: + """Convert flat PowerBIMeasure to nested JSON bytes.""" + return serde.encode(_power_bi_measure_to_nested(power_bi_measure)) + + +def _power_bi_measure_from_nested_bytes(data: bytes, serde: Serde) -> PowerBIMeasure: + """Convert nested JSON bytes to flat PowerBIMeasure.""" + nested = serde.decode(data, PowerBIMeasureNested) + return _power_bi_measure_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +PowerBIMeasure.WORKSPACE_QUALIFIED_NAME = KeywordField( + "workspaceQualifiedName", "workspaceQualifiedName" +) +PowerBIMeasure.DATASET_QUALIFIED_NAME = KeywordField( + "datasetQualifiedName", "datasetQualifiedName" +) +PowerBIMeasure.POWER_BI_MEASURE_EXPRESSION = KeywordField( + "powerBIMeasureExpression", "powerBIMeasureExpression" +) +PowerBIMeasure.POWER_BI_IS_EXTERNAL_MEASURE = BooleanField( + "powerBIIsExternalMeasure", "powerBIIsExternalMeasure" +) +PowerBIMeasure.POWER_BI_IS_HIDDEN = BooleanField("powerBIIsHidden", "powerBIIsHidden") +PowerBIMeasure.POWER_BI_TABLE_QUALIFIED_NAME = KeywordTextField( + "powerBITableQualifiedName", + "powerBITableQualifiedName", + "powerBITableQualifiedName.text", +) +PowerBIMeasure.POWER_BI_FORMAT_STRING = KeywordField( + "powerBIFormatString", "powerBIFormatString" +) +PowerBIMeasure.POWER_BI_ENDORSEMENT = KeywordField( + "powerBIEndorsement", "powerBIEndorsement" +) +PowerBIMeasure.POWER_BI_ENDORSED_BY = KeywordField( + "powerBIEndorsedBy", "powerBIEndorsedBy" +) +PowerBIMeasure.POWER_BI_ENDORSED_AT = NumericField( + "powerBIEndorsedAt", "powerBIEndorsedAt" +) +PowerBIMeasure.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +PowerBIMeasure.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +PowerBIMeasure.ANOMALO_CHECKS = RelationField("anomaloChecks") +PowerBIMeasure.APPLICATION = RelationField("application") +PowerBIMeasure.APPLICATION_FIELD = RelationField("applicationField") +PowerBIMeasure.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +PowerBIMeasure.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +PowerBIMeasure.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +PowerBIMeasure.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +PowerBIMeasure.METRICS = RelationField("metrics") +PowerBIMeasure.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +PowerBIMeasure.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +PowerBIMeasure.MEANINGS = RelationField("meanings") +PowerBIMeasure.MC_MONITORS = RelationField("mcMonitors") +PowerBIMeasure.MC_INCIDENTS = RelationField("mcIncidents") +PowerBIMeasure.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +PowerBIMeasure.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +PowerBIMeasure.TABLE = RelationField("table") +PowerBIMeasure.POWER_BI_COLUMNS = RelationField("powerBIColumns") +PowerBIMeasure.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +PowerBIMeasure.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +PowerBIMeasure.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +PowerBIMeasure.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +PowerBIMeasure.FILES = RelationField("files") +PowerBIMeasure.LINKS = RelationField("links") +PowerBIMeasure.README = RelationField("readme") +PowerBIMeasure.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +PowerBIMeasure.SODA_CHECKS = RelationField("sodaChecks") +PowerBIMeasure.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +PowerBIMeasure.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/power_bi_page.py b/pyatlan_v9/model/assets/power_bi_page.py new file mode 100644 index 000000000..ce83e6fa0 --- /dev/null +++ b/pyatlan_v9/model/assets/power_bi_page.py @@ -0,0 +1,673 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +PowerBIPage asset model with flattened inheritance. + +This module provides: +- PowerBIPage: Flat asset class (easy to use) +- PowerBIPageAttributes: Nested attributes struct (extends AssetAttributes) +- PowerBIPageNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .power_bi_related import RelatedPowerBIReport + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class PowerBIPage(Asset): + """ + Instance of a Power BI page in Atlan. Pages organize and subdivide a report in Power BI. + """ + + WORKSPACE_QUALIFIED_NAME: ClassVar[Any] = None + REPORT_QUALIFIED_NAME: ClassVar[Any] = None + POWER_BI_IS_HIDDEN: ClassVar[Any] = None + POWER_BI_TABLE_QUALIFIED_NAME: ClassVar[Any] = None + POWER_BI_FORMAT_STRING: ClassVar[Any] = None + POWER_BI_ENDORSEMENT: ClassVar[Any] = None + POWER_BI_ENDORSED_BY: ClassVar[Any] = None + POWER_BI_ENDORSED_AT: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + REPORT: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "PowerBIPage" + + workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace in which this page exists.""" + + report_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the report in which this page exists.""" + + power_bi_is_hidden: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIIsHidden" + ) + """Whether this asset is hidden in Power BI (true) or not (false).""" + + power_bi_table_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBITableQualifiedName" + ) + """Unique name of the Power BI table in which this asset exists.""" + + power_bi_format_string: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIFormatString" + ) + """Format of this asset, as specified in the FORMAT_STRING of the MDX cell property.""" + + power_bi_endorsement: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsement" + ) + """Endorsement status of this asset, in Power BI.""" + + power_bi_endorsed_by: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedBy" + ) + """User who endorsed this asset in Power BI.""" + + power_bi_endorsed_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedAt" + ) + """Time at which this asset was endorsed in Power BI.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + report: Union[RelatedPowerBIReport, None, UnsetType] = UNSET + """Report in which this page exists.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "PowerBIPage" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _power_bi_page_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> PowerBIPage: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + PowerBIPage instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _power_bi_page_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class PowerBIPageAttributes(AssetAttributes): + """PowerBIPage-specific attributes for nested API format.""" + + workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace in which this page exists.""" + + report_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the report in which this page exists.""" + + power_bi_is_hidden: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIIsHidden" + ) + """Whether this asset is hidden in Power BI (true) or not (false).""" + + power_bi_table_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBITableQualifiedName" + ) + """Unique name of the Power BI table in which this asset exists.""" + + power_bi_format_string: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIFormatString" + ) + """Format of this asset, as specified in the FORMAT_STRING of the MDX cell property.""" + + power_bi_endorsement: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsement" + ) + """Endorsement status of this asset, in Power BI.""" + + power_bi_endorsed_by: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedBy" + ) + """User who endorsed this asset in Power BI.""" + + power_bi_endorsed_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedAt" + ) + """Time at which this asset was endorsed in Power BI.""" + + +class PowerBIPageRelationshipAttributes(AssetRelationshipAttributes): + """PowerBIPage-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + report: Union[RelatedPowerBIReport, None, UnsetType] = UNSET + """Report in which this page exists.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class PowerBIPageNested(AssetNested): + """PowerBIPage in nested API format for high-performance serialization.""" + + attributes: Union[PowerBIPageAttributes, UnsetType] = UNSET + relationship_attributes: Union[PowerBIPageRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + PowerBIPageRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + PowerBIPageRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_POWER_BI_PAGE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "report", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_power_bi_page_attrs( + attrs: PowerBIPageAttributes, obj: PowerBIPage +) -> None: + """Populate PowerBIPage-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.workspace_qualified_name = obj.workspace_qualified_name + attrs.report_qualified_name = obj.report_qualified_name + attrs.power_bi_is_hidden = obj.power_bi_is_hidden + attrs.power_bi_table_qualified_name = obj.power_bi_table_qualified_name + attrs.power_bi_format_string = obj.power_bi_format_string + attrs.power_bi_endorsement = obj.power_bi_endorsement + attrs.power_bi_endorsed_by = obj.power_bi_endorsed_by + attrs.power_bi_endorsed_at = obj.power_bi_endorsed_at + + +def _extract_power_bi_page_attrs(attrs: PowerBIPageAttributes) -> dict: + """Extract all PowerBIPage attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["workspace_qualified_name"] = attrs.workspace_qualified_name + result["report_qualified_name"] = attrs.report_qualified_name + result["power_bi_is_hidden"] = attrs.power_bi_is_hidden + result["power_bi_table_qualified_name"] = attrs.power_bi_table_qualified_name + result["power_bi_format_string"] = attrs.power_bi_format_string + result["power_bi_endorsement"] = attrs.power_bi_endorsement + result["power_bi_endorsed_by"] = attrs.power_bi_endorsed_by + result["power_bi_endorsed_at"] = attrs.power_bi_endorsed_at + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _power_bi_page_to_nested(power_bi_page: PowerBIPage) -> PowerBIPageNested: + """Convert flat PowerBIPage to nested format.""" + attrs = PowerBIPageAttributes() + _populate_power_bi_page_attrs(attrs, power_bi_page) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + power_bi_page, _POWER_BI_PAGE_REL_FIELDS, PowerBIPageRelationshipAttributes + ) + return PowerBIPageNested( + guid=power_bi_page.guid, + type_name=power_bi_page.type_name, + status=power_bi_page.status, + version=power_bi_page.version, + create_time=power_bi_page.create_time, + update_time=power_bi_page.update_time, + created_by=power_bi_page.created_by, + updated_by=power_bi_page.updated_by, + classifications=power_bi_page.classifications, + classification_names=power_bi_page.classification_names, + meanings=power_bi_page.meanings, + labels=power_bi_page.labels, + business_attributes=power_bi_page.business_attributes, + custom_attributes=power_bi_page.custom_attributes, + pending_tasks=power_bi_page.pending_tasks, + proxy=power_bi_page.proxy, + is_incomplete=power_bi_page.is_incomplete, + provenance_type=power_bi_page.provenance_type, + home_id=power_bi_page.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _power_bi_page_from_nested(nested: PowerBIPageNested) -> PowerBIPage: + """Convert nested format to flat PowerBIPage.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else PowerBIPageAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _POWER_BI_PAGE_REL_FIELDS, + PowerBIPageRelationshipAttributes, + ) + return PowerBIPage( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_power_bi_page_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _power_bi_page_to_nested_bytes(power_bi_page: PowerBIPage, serde: Serde) -> bytes: + """Convert flat PowerBIPage to nested JSON bytes.""" + return serde.encode(_power_bi_page_to_nested(power_bi_page)) + + +def _power_bi_page_from_nested_bytes(data: bytes, serde: Serde) -> PowerBIPage: + """Convert nested JSON bytes to flat PowerBIPage.""" + nested = serde.decode(data, PowerBIPageNested) + return _power_bi_page_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +PowerBIPage.WORKSPACE_QUALIFIED_NAME = KeywordField( + "workspaceQualifiedName", "workspaceQualifiedName" +) +PowerBIPage.REPORT_QUALIFIED_NAME = KeywordField( + "reportQualifiedName", "reportQualifiedName" +) +PowerBIPage.POWER_BI_IS_HIDDEN = BooleanField("powerBIIsHidden", "powerBIIsHidden") +PowerBIPage.POWER_BI_TABLE_QUALIFIED_NAME = KeywordTextField( + "powerBITableQualifiedName", + "powerBITableQualifiedName", + "powerBITableQualifiedName.text", +) +PowerBIPage.POWER_BI_FORMAT_STRING = KeywordField( + "powerBIFormatString", "powerBIFormatString" +) +PowerBIPage.POWER_BI_ENDORSEMENT = KeywordField( + "powerBIEndorsement", "powerBIEndorsement" +) +PowerBIPage.POWER_BI_ENDORSED_BY = KeywordField( + "powerBIEndorsedBy", "powerBIEndorsedBy" +) +PowerBIPage.POWER_BI_ENDORSED_AT = NumericField( + "powerBIEndorsedAt", "powerBIEndorsedAt" +) +PowerBIPage.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +PowerBIPage.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +PowerBIPage.ANOMALO_CHECKS = RelationField("anomaloChecks") +PowerBIPage.APPLICATION = RelationField("application") +PowerBIPage.APPLICATION_FIELD = RelationField("applicationField") +PowerBIPage.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +PowerBIPage.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +PowerBIPage.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +PowerBIPage.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +PowerBIPage.METRICS = RelationField("metrics") +PowerBIPage.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +PowerBIPage.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +PowerBIPage.MEANINGS = RelationField("meanings") +PowerBIPage.MC_MONITORS = RelationField("mcMonitors") +PowerBIPage.MC_INCIDENTS = RelationField("mcIncidents") +PowerBIPage.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +PowerBIPage.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +PowerBIPage.REPORT = RelationField("report") +PowerBIPage.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +PowerBIPage.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +PowerBIPage.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +PowerBIPage.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +PowerBIPage.FILES = RelationField("files") +PowerBIPage.LINKS = RelationField("links") +PowerBIPage.README = RelationField("readme") +PowerBIPage.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +PowerBIPage.SODA_CHECKS = RelationField("sodaChecks") +PowerBIPage.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +PowerBIPage.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/power_bi_related.py b/pyatlan_v9/model/assets/power_bi_related.py new file mode 100644 index 000000000..050630af4 --- /dev/null +++ b/pyatlan_v9/model/assets/power_bi_related.py @@ -0,0 +1,456 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for PowerBI module. + +This module contains all Related{Type} classes for the PowerBI type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedBI +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedPowerBI", + "RelatedPowerBIApp", + "RelatedPowerBIDataset", + "RelatedPowerBIDatasource", + "RelatedPowerBIMeasure", + "RelatedPowerBIPage", + "RelatedPowerBIReport", + "RelatedPowerBITable", + "RelatedPowerBITile", + "RelatedPowerBIColumn", + "RelatedPowerBIDashboard", + "RelatedPowerBIWorkspace", + "RelatedPowerBIDataflow", + "RelatedPowerBIDataflowEntityColumn", +] + + +class RelatedPowerBI(RelatedBI): + """ + Related entity reference for PowerBI assets. + + Extends RelatedBI with PowerBI-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "PowerBI" so it serializes correctly + + power_bi_is_hidden: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIIsHidden" + ) + """Whether this asset is hidden in Power BI (true) or not (false).""" + + power_bi_table_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBITableQualifiedName" + ) + """Unique name of the Power BI table in which this asset exists.""" + + power_bi_format_string: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIFormatString" + ) + """Format of this asset, as specified in the FORMAT_STRING of the MDX cell property.""" + + power_bi_endorsement: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsement" + ) + """Endorsement status of this asset, in Power BI.""" + + power_bi_endorsed_by: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedBy" + ) + """User who endorsed this asset in Power BI.""" + + power_bi_endorsed_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedAt" + ) + """Time at which this asset was endorsed in Power BI.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "PowerBI" + + +class RelatedPowerBIApp(RelatedPowerBI): + """ + Related entity reference for PowerBIApp assets. + + Extends RelatedPowerBI with PowerBIApp-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "PowerBIApp" so it serializes correctly + + power_bi_app_id: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIAppId" + ) + """Unique ID of the PowerBI App in the PowerBI Assets Ecosystem.""" + + power_bi_app_users: Union[List[Dict[str, str]], None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIAppUsers" + ) + """List of users and their permission access for a PowerBI App.""" + + power_bi_app_groups: Union[List[Dict[str, str]], None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIAppGroups" + ) + """List of groups and their permission access for a PowerBI App.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "PowerBIApp" + + +class RelatedPowerBIDataset(RelatedPowerBI): + """ + Related entity reference for PowerBIDataset assets. + + Extends RelatedPowerBI with PowerBIDataset-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "PowerBIDataset" so it serializes correctly + + workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace in which this dataset exists.""" + + web_url: Union[str, None, UnsetType] = UNSET + """Deprecated. See 'sourceUrl' instead.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "PowerBIDataset" + + +class RelatedPowerBIDatasource(RelatedPowerBI): + """ + Related entity reference for PowerBIDatasource assets. + + Extends RelatedPowerBI with PowerBIDatasource-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "PowerBIDatasource" so it serializes correctly + + connection_details: Union[Dict[str, str], None, UnsetType] = UNSET + """Connection details of the datasource.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "PowerBIDatasource" + + +class RelatedPowerBIMeasure(RelatedPowerBI): + """ + Related entity reference for PowerBIMeasure assets. + + Extends RelatedPowerBI with PowerBIMeasure-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "PowerBIMeasure" so it serializes correctly + + workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace in which this measure exists.""" + + dataset_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dataset in which this measure exists.""" + + power_bi_measure_expression: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIMeasureExpression" + ) + """DAX expression for this measure.""" + + power_bi_is_external_measure: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIIsExternalMeasure" + ) + """Whether this measure is external (true) or internal (false).""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "PowerBIMeasure" + + +class RelatedPowerBIPage(RelatedPowerBI): + """ + Related entity reference for PowerBIPage assets. + + Extends RelatedPowerBI with PowerBIPage-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "PowerBIPage" so it serializes correctly + + workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace in which this page exists.""" + + report_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the report in which this page exists.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "PowerBIPage" + + +class RelatedPowerBIReport(RelatedPowerBI): + """ + Related entity reference for PowerBIReport assets. + + Extends RelatedPowerBI with PowerBIReport-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "PowerBIReport" so it serializes correctly + + workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace in which this report exists.""" + + dataset_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dataset used to build this report.""" + + web_url: Union[str, None, UnsetType] = UNSET + """Deprecated. See 'sourceUrl' instead.""" + + page_count: Union[int, None, UnsetType] = UNSET + """Number of pages in this report.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "PowerBIReport" + + +class RelatedPowerBITable(RelatedPowerBI): + """ + Related entity reference for PowerBITable assets. + + Extends RelatedPowerBI with PowerBITable-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "PowerBITable" so it serializes correctly + + workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace in which this table exists.""" + + dataset_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dataset in which this table exists.""" + + dataflow_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of qualified names of associated Power BI Dataflows.""" + + power_bi_table_source_expressions: Union[List[str], None, UnsetType] = ( + msgspec.field(default=UNSET, name="powerBITableSourceExpressions") + ) + """Power Query M expressions for the table.""" + + power_bi_table_column_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBITableColumnCount" + ) + """Number of columns in this table.""" + + power_bi_table_measure_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBITableMeasureCount" + ) + """Number of measures in this table.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "PowerBITable" + + +class RelatedPowerBITile(RelatedPowerBI): + """ + Related entity reference for PowerBITile assets. + + Extends RelatedPowerBI with PowerBITile-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "PowerBITile" so it serializes correctly + + workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace in which this tile exists.""" + + dashboard_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dashboard in which this tile is pinned.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "PowerBITile" + + +class RelatedPowerBIColumn(RelatedPowerBI): + """ + Related entity reference for PowerBIColumn assets. + + Extends RelatedPowerBI with PowerBIColumn-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "PowerBIColumn" so it serializes correctly + + workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace in which this column exists.""" + + dataset_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dataset in which this column exists.""" + + power_bi_column_data_category: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIColumnDataCategory" + ) + """Data category that describes the data in this column.""" + + power_bi_column_data_type: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIColumnDataType" + ) + """Data type of this column.""" + + power_bi_sort_by_column: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBISortByColumn" + ) + """Name of a column in the same table to use to order this column.""" + + power_bi_column_summarize_by: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIColumnSummarizeBy" + ) + """Aggregate function to use for summarizing this column.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "PowerBIColumn" + + +class RelatedPowerBIDashboard(RelatedPowerBI): + """ + Related entity reference for PowerBIDashboard assets. + + Extends RelatedPowerBI with PowerBIDashboard-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "PowerBIDashboard" so it serializes correctly + + workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace in which this dashboard exists.""" + + web_url: Union[str, None, UnsetType] = UNSET + """Deprecated. See 'sourceUrl' instead.""" + + tile_count: Union[int, None, UnsetType] = UNSET + """Number of tiles in this table.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "PowerBIDashboard" + + +class RelatedPowerBIWorkspace(RelatedPowerBI): + """ + Related entity reference for PowerBIWorkspace assets. + + Extends RelatedPowerBI with PowerBIWorkspace-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "PowerBIWorkspace" so it serializes correctly + + web_url: Union[str, None, UnsetType] = UNSET + """Deprecated.""" + + report_count: Union[int, None, UnsetType] = UNSET + """Number of reports in this workspace.""" + + dashboard_count: Union[int, None, UnsetType] = UNSET + """Number of dashboards in this workspace.""" + + dataset_count: Union[int, None, UnsetType] = UNSET + """Number of datasets in this workspace.""" + + dataflow_count: Union[int, None, UnsetType] = UNSET + """Number of dataflows in this workspace.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "PowerBIWorkspace" + + +class RelatedPowerBIDataflow(RelatedPowerBI): + """ + Related entity reference for PowerBIDataflow assets. + + Extends RelatedPowerBI with PowerBIDataflow-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "PowerBIDataflow" so it serializes correctly + + workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace in which this dataflow exists.""" + + web_url: Union[str, None, UnsetType] = UNSET + """Deprecated. See 'sourceUrl' instead.""" + + power_bi_dataflow_refresh_schedule_frequency: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="powerBIDataflowRefreshScheduleFrequency") + ) + """Refresh Schedule frequency for a PowerBI Dataflow.""" + + power_bi_dataflow_refresh_schedule_times: Union[List[str], None, UnsetType] = ( + msgspec.field(default=UNSET, name="powerBIDataflowRefreshScheduleTimes") + ) + """Time for the refresh schedule set for a PowerBI Dataflow.""" + + power_bi_dataflow_refresh_schedule_time_zone: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="powerBIDataflowRefreshScheduleTimeZone") + ) + """Time zone for the refresh schedule set for a PowerBI Dataflow.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "PowerBIDataflow" + + +class RelatedPowerBIDataflowEntityColumn(RelatedPowerBI): + """ + Related entity reference for PowerBIDataflowEntityColumn assets. + + Extends RelatedPowerBI with PowerBIDataflowEntityColumn-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "PowerBIDataflowEntityColumn" so it serializes correctly + + power_bi_dataflow_entity_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIDataflowEntityName" + ) + """Unique name of the dataflow entity in which this dataflow entity column exists.""" + + power_bi_workspace_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIWorkspaceQualifiedName" + ) + """Unique name of the workspace in which this dataflow entity column exists.""" + + power_bi_dataflow_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIDataflowQualifiedName" + ) + """Unique name of the dataflow in which this dataflow entity column exists.""" + + power_bi_dataflow_entity_column_data_type: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="powerBIDataflowEntityColumnDataType") + ) + """Data type of this dataflow entity column.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "PowerBIDataflowEntityColumn" diff --git a/pyatlan_v9/model/assets/power_bi_report.py b/pyatlan_v9/model/assets/power_bi_report.py new file mode 100644 index 000000000..0900cfefc --- /dev/null +++ b/pyatlan_v9/model/assets/power_bi_report.py @@ -0,0 +1,745 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +PowerBIReport asset model with flattened inheritance. + +This module provides: +- PowerBIReport: Flat asset class (easy to use) +- PowerBIReportAttributes: Nested attributes struct (extends AssetAttributes) +- PowerBIReportNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .power_bi_related import ( + RelatedPowerBIApp, + RelatedPowerBIDataset, + RelatedPowerBIPage, + RelatedPowerBITile, + RelatedPowerBIWorkspace, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class PowerBIReport(Asset): + """ + Instance of a Power BI report in Atlan. + """ + + WORKSPACE_QUALIFIED_NAME: ClassVar[Any] = None + DATASET_QUALIFIED_NAME: ClassVar[Any] = None + WEB_URL: ClassVar[Any] = None + PAGE_COUNT: ClassVar[Any] = None + POWER_BI_IS_HIDDEN: ClassVar[Any] = None + POWER_BI_TABLE_QUALIFIED_NAME: ClassVar[Any] = None + POWER_BI_FORMAT_STRING: ClassVar[Any] = None + POWER_BI_ENDORSEMENT: ClassVar[Any] = None + POWER_BI_ENDORSED_BY: ClassVar[Any] = None + POWER_BI_ENDORSED_AT: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + POWER_BI_APPS: ClassVar[Any] = None + PAGES: ClassVar[Any] = None + WORKSPACE: ClassVar[Any] = None + DATASET: ClassVar[Any] = None + TILES: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "PowerBIReport" + + workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace in which this report exists.""" + + dataset_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dataset used to build this report.""" + + web_url: Union[str, None, UnsetType] = UNSET + """Deprecated. See 'sourceUrl' instead.""" + + page_count: Union[int, None, UnsetType] = UNSET + """Number of pages in this report.""" + + power_bi_is_hidden: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIIsHidden" + ) + """Whether this asset is hidden in Power BI (true) or not (false).""" + + power_bi_table_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBITableQualifiedName" + ) + """Unique name of the Power BI table in which this asset exists.""" + + power_bi_format_string: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIFormatString" + ) + """Format of this asset, as specified in the FORMAT_STRING of the MDX cell property.""" + + power_bi_endorsement: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsement" + ) + """Endorsement status of this asset, in Power BI.""" + + power_bi_endorsed_by: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedBy" + ) + """User who endorsed this asset in Power BI.""" + + power_bi_endorsed_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedAt" + ) + """Time at which this asset was endorsed in Power BI.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + power_bi_apps: Union[List[RelatedPowerBIApp], None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIApps" + ) + """PowerBI App that is associated with this PowerBI Report.""" + + pages: Union[List[RelatedPowerBIPage], None, UnsetType] = UNSET + """Pages that exist within this report.""" + + workspace: Union[RelatedPowerBIWorkspace, None, UnsetType] = UNSET + """Workspace in which this report exists.""" + + dataset: Union[RelatedPowerBIDataset, None, UnsetType] = UNSET + """Dataset from which this report was built.""" + + tiles: Union[List[RelatedPowerBITile], None, UnsetType] = UNSET + """Tiles that exist within this report.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "PowerBIReport" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _power_bi_report_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> PowerBIReport: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + PowerBIReport instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _power_bi_report_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class PowerBIReportAttributes(AssetAttributes): + """PowerBIReport-specific attributes for nested API format.""" + + workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace in which this report exists.""" + + dataset_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dataset used to build this report.""" + + web_url: Union[str, None, UnsetType] = UNSET + """Deprecated. See 'sourceUrl' instead.""" + + page_count: Union[int, None, UnsetType] = UNSET + """Number of pages in this report.""" + + power_bi_is_hidden: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIIsHidden" + ) + """Whether this asset is hidden in Power BI (true) or not (false).""" + + power_bi_table_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBITableQualifiedName" + ) + """Unique name of the Power BI table in which this asset exists.""" + + power_bi_format_string: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIFormatString" + ) + """Format of this asset, as specified in the FORMAT_STRING of the MDX cell property.""" + + power_bi_endorsement: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsement" + ) + """Endorsement status of this asset, in Power BI.""" + + power_bi_endorsed_by: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedBy" + ) + """User who endorsed this asset in Power BI.""" + + power_bi_endorsed_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedAt" + ) + """Time at which this asset was endorsed in Power BI.""" + + +class PowerBIReportRelationshipAttributes(AssetRelationshipAttributes): + """PowerBIReport-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + power_bi_apps: Union[List[RelatedPowerBIApp], None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIApps" + ) + """PowerBI App that is associated with this PowerBI Report.""" + + pages: Union[List[RelatedPowerBIPage], None, UnsetType] = UNSET + """Pages that exist within this report.""" + + workspace: Union[RelatedPowerBIWorkspace, None, UnsetType] = UNSET + """Workspace in which this report exists.""" + + dataset: Union[RelatedPowerBIDataset, None, UnsetType] = UNSET + """Dataset from which this report was built.""" + + tiles: Union[List[RelatedPowerBITile], None, UnsetType] = UNSET + """Tiles that exist within this report.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class PowerBIReportNested(AssetNested): + """PowerBIReport in nested API format for high-performance serialization.""" + + attributes: Union[PowerBIReportAttributes, UnsetType] = UNSET + relationship_attributes: Union[PowerBIReportRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + PowerBIReportRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + PowerBIReportRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_POWER_BI_REPORT_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "power_bi_apps", + "pages", + "workspace", + "dataset", + "tiles", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_power_bi_report_attrs( + attrs: PowerBIReportAttributes, obj: PowerBIReport +) -> None: + """Populate PowerBIReport-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.workspace_qualified_name = obj.workspace_qualified_name + attrs.dataset_qualified_name = obj.dataset_qualified_name + attrs.web_url = obj.web_url + attrs.page_count = obj.page_count + attrs.power_bi_is_hidden = obj.power_bi_is_hidden + attrs.power_bi_table_qualified_name = obj.power_bi_table_qualified_name + attrs.power_bi_format_string = obj.power_bi_format_string + attrs.power_bi_endorsement = obj.power_bi_endorsement + attrs.power_bi_endorsed_by = obj.power_bi_endorsed_by + attrs.power_bi_endorsed_at = obj.power_bi_endorsed_at + + +def _extract_power_bi_report_attrs(attrs: PowerBIReportAttributes) -> dict: + """Extract all PowerBIReport attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["workspace_qualified_name"] = attrs.workspace_qualified_name + result["dataset_qualified_name"] = attrs.dataset_qualified_name + result["web_url"] = attrs.web_url + result["page_count"] = attrs.page_count + result["power_bi_is_hidden"] = attrs.power_bi_is_hidden + result["power_bi_table_qualified_name"] = attrs.power_bi_table_qualified_name + result["power_bi_format_string"] = attrs.power_bi_format_string + result["power_bi_endorsement"] = attrs.power_bi_endorsement + result["power_bi_endorsed_by"] = attrs.power_bi_endorsed_by + result["power_bi_endorsed_at"] = attrs.power_bi_endorsed_at + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _power_bi_report_to_nested(power_bi_report: PowerBIReport) -> PowerBIReportNested: + """Convert flat PowerBIReport to nested format.""" + attrs = PowerBIReportAttributes() + _populate_power_bi_report_attrs(attrs, power_bi_report) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + power_bi_report, + _POWER_BI_REPORT_REL_FIELDS, + PowerBIReportRelationshipAttributes, + ) + return PowerBIReportNested( + guid=power_bi_report.guid, + type_name=power_bi_report.type_name, + status=power_bi_report.status, + version=power_bi_report.version, + create_time=power_bi_report.create_time, + update_time=power_bi_report.update_time, + created_by=power_bi_report.created_by, + updated_by=power_bi_report.updated_by, + classifications=power_bi_report.classifications, + classification_names=power_bi_report.classification_names, + meanings=power_bi_report.meanings, + labels=power_bi_report.labels, + business_attributes=power_bi_report.business_attributes, + custom_attributes=power_bi_report.custom_attributes, + pending_tasks=power_bi_report.pending_tasks, + proxy=power_bi_report.proxy, + is_incomplete=power_bi_report.is_incomplete, + provenance_type=power_bi_report.provenance_type, + home_id=power_bi_report.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _power_bi_report_from_nested(nested: PowerBIReportNested) -> PowerBIReport: + """Convert nested format to flat PowerBIReport.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else PowerBIReportAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _POWER_BI_REPORT_REL_FIELDS, + PowerBIReportRelationshipAttributes, + ) + return PowerBIReport( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_power_bi_report_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _power_bi_report_to_nested_bytes( + power_bi_report: PowerBIReport, serde: Serde +) -> bytes: + """Convert flat PowerBIReport to nested JSON bytes.""" + return serde.encode(_power_bi_report_to_nested(power_bi_report)) + + +def _power_bi_report_from_nested_bytes(data: bytes, serde: Serde) -> PowerBIReport: + """Convert nested JSON bytes to flat PowerBIReport.""" + nested = serde.decode(data, PowerBIReportNested) + return _power_bi_report_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +PowerBIReport.WORKSPACE_QUALIFIED_NAME = KeywordField( + "workspaceQualifiedName", "workspaceQualifiedName" +) +PowerBIReport.DATASET_QUALIFIED_NAME = KeywordField( + "datasetQualifiedName", "datasetQualifiedName" +) +PowerBIReport.WEB_URL = KeywordField("webUrl", "webUrl") +PowerBIReport.PAGE_COUNT = NumericField("pageCount", "pageCount") +PowerBIReport.POWER_BI_IS_HIDDEN = BooleanField("powerBIIsHidden", "powerBIIsHidden") +PowerBIReport.POWER_BI_TABLE_QUALIFIED_NAME = KeywordTextField( + "powerBITableQualifiedName", + "powerBITableQualifiedName", + "powerBITableQualifiedName.text", +) +PowerBIReport.POWER_BI_FORMAT_STRING = KeywordField( + "powerBIFormatString", "powerBIFormatString" +) +PowerBIReport.POWER_BI_ENDORSEMENT = KeywordField( + "powerBIEndorsement", "powerBIEndorsement" +) +PowerBIReport.POWER_BI_ENDORSED_BY = KeywordField( + "powerBIEndorsedBy", "powerBIEndorsedBy" +) +PowerBIReport.POWER_BI_ENDORSED_AT = NumericField( + "powerBIEndorsedAt", "powerBIEndorsedAt" +) +PowerBIReport.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +PowerBIReport.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +PowerBIReport.ANOMALO_CHECKS = RelationField("anomaloChecks") +PowerBIReport.APPLICATION = RelationField("application") +PowerBIReport.APPLICATION_FIELD = RelationField("applicationField") +PowerBIReport.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +PowerBIReport.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +PowerBIReport.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +PowerBIReport.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +PowerBIReport.METRICS = RelationField("metrics") +PowerBIReport.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +PowerBIReport.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +PowerBIReport.MEANINGS = RelationField("meanings") +PowerBIReport.MC_MONITORS = RelationField("mcMonitors") +PowerBIReport.MC_INCIDENTS = RelationField("mcIncidents") +PowerBIReport.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +PowerBIReport.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +PowerBIReport.POWER_BI_APPS = RelationField("powerBIApps") +PowerBIReport.PAGES = RelationField("pages") +PowerBIReport.WORKSPACE = RelationField("workspace") +PowerBIReport.DATASET = RelationField("dataset") +PowerBIReport.TILES = RelationField("tiles") +PowerBIReport.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +PowerBIReport.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +PowerBIReport.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +PowerBIReport.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +PowerBIReport.FILES = RelationField("files") +PowerBIReport.LINKS = RelationField("links") +PowerBIReport.README = RelationField("readme") +PowerBIReport.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +PowerBIReport.SODA_CHECKS = RelationField("sodaChecks") +PowerBIReport.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +PowerBIReport.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/power_bi_table.py b/pyatlan_v9/model/assets/power_bi_table.py new file mode 100644 index 000000000..6d38e7610 --- /dev/null +++ b/pyatlan_v9/model/assets/power_bi_table.py @@ -0,0 +1,773 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +PowerBITable asset model with flattened inheritance. + +This module provides: +- PowerBITable: Flat asset class (easy to use) +- PowerBITableAttributes: Nested attributes struct (extends AssetAttributes) +- PowerBITableNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .power_bi_related import ( + RelatedPowerBIColumn, + RelatedPowerBIDataflow, + RelatedPowerBIDataset, + RelatedPowerBIMeasure, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class PowerBITable(Asset): + """ + Instance of a Power BI table in Atlan. + """ + + WORKSPACE_QUALIFIED_NAME: ClassVar[Any] = None + DATASET_QUALIFIED_NAME: ClassVar[Any] = None + DATAFLOW_QUALIFIED_NAMES: ClassVar[Any] = None + POWER_BI_TABLE_SOURCE_EXPRESSIONS: ClassVar[Any] = None + POWER_BI_TABLE_COLUMN_COUNT: ClassVar[Any] = None + POWER_BI_TABLE_MEASURE_COUNT: ClassVar[Any] = None + POWER_BI_IS_HIDDEN: ClassVar[Any] = None + POWER_BI_TABLE_QUALIFIED_NAME: ClassVar[Any] = None + POWER_BI_FORMAT_STRING: ClassVar[Any] = None + POWER_BI_ENDORSEMENT: ClassVar[Any] = None + POWER_BI_ENDORSED_BY: ClassVar[Any] = None + POWER_BI_ENDORSED_AT: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + MEASURES: ClassVar[Any] = None + DATASET: ClassVar[Any] = None + COLUMNS: ClassVar[Any] = None + DATAFLOWS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "PowerBITable" + + workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace in which this table exists.""" + + dataset_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dataset in which this table exists.""" + + dataflow_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of qualified names of associated Power BI Dataflows.""" + + power_bi_table_source_expressions: Union[List[str], None, UnsetType] = ( + msgspec.field(default=UNSET, name="powerBITableSourceExpressions") + ) + """Power Query M expressions for the table.""" + + power_bi_table_column_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBITableColumnCount" + ) + """Number of columns in this table.""" + + power_bi_table_measure_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBITableMeasureCount" + ) + """Number of measures in this table.""" + + power_bi_is_hidden: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIIsHidden" + ) + """Whether this asset is hidden in Power BI (true) or not (false).""" + + power_bi_table_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBITableQualifiedName" + ) + """Unique name of the Power BI table in which this asset exists.""" + + power_bi_format_string: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIFormatString" + ) + """Format of this asset, as specified in the FORMAT_STRING of the MDX cell property.""" + + power_bi_endorsement: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsement" + ) + """Endorsement status of this asset, in Power BI.""" + + power_bi_endorsed_by: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedBy" + ) + """User who endorsed this asset in Power BI.""" + + power_bi_endorsed_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedAt" + ) + """Time at which this asset was endorsed in Power BI.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + measures: Union[List[RelatedPowerBIMeasure], None, UnsetType] = UNSET + """Measures that exist within this table.""" + + dataset: Union[RelatedPowerBIDataset, None, UnsetType] = UNSET + """Dataset in which this table exists.""" + + columns: Union[List[RelatedPowerBIColumn], None, UnsetType] = UNSET + """Columns that exist within this table.""" + + dataflows: Union[List[RelatedPowerBIDataflow], None, UnsetType] = UNSET + """PowerBI Dataflow that is associated with this PowerBI Table.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "PowerBITable" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _power_bi_table_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> PowerBITable: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + PowerBITable instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _power_bi_table_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class PowerBITableAttributes(AssetAttributes): + """PowerBITable-specific attributes for nested API format.""" + + workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace in which this table exists.""" + + dataset_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dataset in which this table exists.""" + + dataflow_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of qualified names of associated Power BI Dataflows.""" + + power_bi_table_source_expressions: Union[List[str], None, UnsetType] = ( + msgspec.field(default=UNSET, name="powerBITableSourceExpressions") + ) + """Power Query M expressions for the table.""" + + power_bi_table_column_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBITableColumnCount" + ) + """Number of columns in this table.""" + + power_bi_table_measure_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBITableMeasureCount" + ) + """Number of measures in this table.""" + + power_bi_is_hidden: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIIsHidden" + ) + """Whether this asset is hidden in Power BI (true) or not (false).""" + + power_bi_table_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBITableQualifiedName" + ) + """Unique name of the Power BI table in which this asset exists.""" + + power_bi_format_string: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIFormatString" + ) + """Format of this asset, as specified in the FORMAT_STRING of the MDX cell property.""" + + power_bi_endorsement: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsement" + ) + """Endorsement status of this asset, in Power BI.""" + + power_bi_endorsed_by: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedBy" + ) + """User who endorsed this asset in Power BI.""" + + power_bi_endorsed_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedAt" + ) + """Time at which this asset was endorsed in Power BI.""" + + +class PowerBITableRelationshipAttributes(AssetRelationshipAttributes): + """PowerBITable-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + measures: Union[List[RelatedPowerBIMeasure], None, UnsetType] = UNSET + """Measures that exist within this table.""" + + dataset: Union[RelatedPowerBIDataset, None, UnsetType] = UNSET + """Dataset in which this table exists.""" + + columns: Union[List[RelatedPowerBIColumn], None, UnsetType] = UNSET + """Columns that exist within this table.""" + + dataflows: Union[List[RelatedPowerBIDataflow], None, UnsetType] = UNSET + """PowerBI Dataflow that is associated with this PowerBI Table.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class PowerBITableNested(AssetNested): + """PowerBITable in nested API format for high-performance serialization.""" + + attributes: Union[PowerBITableAttributes, UnsetType] = UNSET + relationship_attributes: Union[PowerBITableRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + PowerBITableRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + PowerBITableRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_POWER_BI_TABLE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "measures", + "dataset", + "columns", + "dataflows", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_power_bi_table_attrs( + attrs: PowerBITableAttributes, obj: PowerBITable +) -> None: + """Populate PowerBITable-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.workspace_qualified_name = obj.workspace_qualified_name + attrs.dataset_qualified_name = obj.dataset_qualified_name + attrs.dataflow_qualified_names = obj.dataflow_qualified_names + attrs.power_bi_table_source_expressions = obj.power_bi_table_source_expressions + attrs.power_bi_table_column_count = obj.power_bi_table_column_count + attrs.power_bi_table_measure_count = obj.power_bi_table_measure_count + attrs.power_bi_is_hidden = obj.power_bi_is_hidden + attrs.power_bi_table_qualified_name = obj.power_bi_table_qualified_name + attrs.power_bi_format_string = obj.power_bi_format_string + attrs.power_bi_endorsement = obj.power_bi_endorsement + attrs.power_bi_endorsed_by = obj.power_bi_endorsed_by + attrs.power_bi_endorsed_at = obj.power_bi_endorsed_at + + +def _extract_power_bi_table_attrs(attrs: PowerBITableAttributes) -> dict: + """Extract all PowerBITable attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["workspace_qualified_name"] = attrs.workspace_qualified_name + result["dataset_qualified_name"] = attrs.dataset_qualified_name + result["dataflow_qualified_names"] = attrs.dataflow_qualified_names + result["power_bi_table_source_expressions"] = ( + attrs.power_bi_table_source_expressions + ) + result["power_bi_table_column_count"] = attrs.power_bi_table_column_count + result["power_bi_table_measure_count"] = attrs.power_bi_table_measure_count + result["power_bi_is_hidden"] = attrs.power_bi_is_hidden + result["power_bi_table_qualified_name"] = attrs.power_bi_table_qualified_name + result["power_bi_format_string"] = attrs.power_bi_format_string + result["power_bi_endorsement"] = attrs.power_bi_endorsement + result["power_bi_endorsed_by"] = attrs.power_bi_endorsed_by + result["power_bi_endorsed_at"] = attrs.power_bi_endorsed_at + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _power_bi_table_to_nested(power_bi_table: PowerBITable) -> PowerBITableNested: + """Convert flat PowerBITable to nested format.""" + attrs = PowerBITableAttributes() + _populate_power_bi_table_attrs(attrs, power_bi_table) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + power_bi_table, _POWER_BI_TABLE_REL_FIELDS, PowerBITableRelationshipAttributes + ) + return PowerBITableNested( + guid=power_bi_table.guid, + type_name=power_bi_table.type_name, + status=power_bi_table.status, + version=power_bi_table.version, + create_time=power_bi_table.create_time, + update_time=power_bi_table.update_time, + created_by=power_bi_table.created_by, + updated_by=power_bi_table.updated_by, + classifications=power_bi_table.classifications, + classification_names=power_bi_table.classification_names, + meanings=power_bi_table.meanings, + labels=power_bi_table.labels, + business_attributes=power_bi_table.business_attributes, + custom_attributes=power_bi_table.custom_attributes, + pending_tasks=power_bi_table.pending_tasks, + proxy=power_bi_table.proxy, + is_incomplete=power_bi_table.is_incomplete, + provenance_type=power_bi_table.provenance_type, + home_id=power_bi_table.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _power_bi_table_from_nested(nested: PowerBITableNested) -> PowerBITable: + """Convert nested format to flat PowerBITable.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else PowerBITableAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _POWER_BI_TABLE_REL_FIELDS, + PowerBITableRelationshipAttributes, + ) + return PowerBITable( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_power_bi_table_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _power_bi_table_to_nested_bytes( + power_bi_table: PowerBITable, serde: Serde +) -> bytes: + """Convert flat PowerBITable to nested JSON bytes.""" + return serde.encode(_power_bi_table_to_nested(power_bi_table)) + + +def _power_bi_table_from_nested_bytes(data: bytes, serde: Serde) -> PowerBITable: + """Convert nested JSON bytes to flat PowerBITable.""" + nested = serde.decode(data, PowerBITableNested) + return _power_bi_table_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +PowerBITable.WORKSPACE_QUALIFIED_NAME = KeywordField( + "workspaceQualifiedName", "workspaceQualifiedName" +) +PowerBITable.DATASET_QUALIFIED_NAME = KeywordField( + "datasetQualifiedName", "datasetQualifiedName" +) +PowerBITable.DATAFLOW_QUALIFIED_NAMES = KeywordTextField( + "dataflowQualifiedNames", "dataflowQualifiedNames", "dataflowQualifiedNames.text" +) +PowerBITable.POWER_BI_TABLE_SOURCE_EXPRESSIONS = KeywordField( + "powerBITableSourceExpressions", "powerBITableSourceExpressions" +) +PowerBITable.POWER_BI_TABLE_COLUMN_COUNT = NumericField( + "powerBITableColumnCount", "powerBITableColumnCount" +) +PowerBITable.POWER_BI_TABLE_MEASURE_COUNT = NumericField( + "powerBITableMeasureCount", "powerBITableMeasureCount" +) +PowerBITable.POWER_BI_IS_HIDDEN = BooleanField("powerBIIsHidden", "powerBIIsHidden") +PowerBITable.POWER_BI_TABLE_QUALIFIED_NAME = KeywordTextField( + "powerBITableQualifiedName", + "powerBITableQualifiedName", + "powerBITableQualifiedName.text", +) +PowerBITable.POWER_BI_FORMAT_STRING = KeywordField( + "powerBIFormatString", "powerBIFormatString" +) +PowerBITable.POWER_BI_ENDORSEMENT = KeywordField( + "powerBIEndorsement", "powerBIEndorsement" +) +PowerBITable.POWER_BI_ENDORSED_BY = KeywordField( + "powerBIEndorsedBy", "powerBIEndorsedBy" +) +PowerBITable.POWER_BI_ENDORSED_AT = NumericField( + "powerBIEndorsedAt", "powerBIEndorsedAt" +) +PowerBITable.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +PowerBITable.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +PowerBITable.ANOMALO_CHECKS = RelationField("anomaloChecks") +PowerBITable.APPLICATION = RelationField("application") +PowerBITable.APPLICATION_FIELD = RelationField("applicationField") +PowerBITable.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +PowerBITable.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +PowerBITable.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +PowerBITable.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +PowerBITable.METRICS = RelationField("metrics") +PowerBITable.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +PowerBITable.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +PowerBITable.MEANINGS = RelationField("meanings") +PowerBITable.MC_MONITORS = RelationField("mcMonitors") +PowerBITable.MC_INCIDENTS = RelationField("mcIncidents") +PowerBITable.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +PowerBITable.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +PowerBITable.MEASURES = RelationField("measures") +PowerBITable.DATASET = RelationField("dataset") +PowerBITable.COLUMNS = RelationField("columns") +PowerBITable.DATAFLOWS = RelationField("dataflows") +PowerBITable.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +PowerBITable.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +PowerBITable.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +PowerBITable.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +PowerBITable.FILES = RelationField("files") +PowerBITable.LINKS = RelationField("links") +PowerBITable.README = RelationField("readme") +PowerBITable.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +PowerBITable.SODA_CHECKS = RelationField("sodaChecks") +PowerBITable.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +PowerBITable.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/power_bi_tile.py b/pyatlan_v9/model/assets/power_bi_tile.py new file mode 100644 index 000000000..52b411092 --- /dev/null +++ b/pyatlan_v9/model/assets/power_bi_tile.py @@ -0,0 +1,695 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +PowerBITile asset model with flattened inheritance. + +This module provides: +- PowerBITile: Flat asset class (easy to use) +- PowerBITileAttributes: Nested attributes struct (extends AssetAttributes) +- PowerBITileNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .power_bi_related import ( + RelatedPowerBIDashboard, + RelatedPowerBIDataset, + RelatedPowerBIReport, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class PowerBITile(Asset): + """ + Instance of a Power BI tile in Atlan. Tiles are snapshots of data, pinned to a dashboard. + """ + + WORKSPACE_QUALIFIED_NAME: ClassVar[Any] = None + DASHBOARD_QUALIFIED_NAME: ClassVar[Any] = None + POWER_BI_IS_HIDDEN: ClassVar[Any] = None + POWER_BI_TABLE_QUALIFIED_NAME: ClassVar[Any] = None + POWER_BI_FORMAT_STRING: ClassVar[Any] = None + POWER_BI_ENDORSEMENT: ClassVar[Any] = None + POWER_BI_ENDORSED_BY: ClassVar[Any] = None + POWER_BI_ENDORSED_AT: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + DASHBOARD: ClassVar[Any] = None + REPORT: ClassVar[Any] = None + DATASET: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "PowerBITile" + + workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace in which this tile exists.""" + + dashboard_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dashboard in which this tile is pinned.""" + + power_bi_is_hidden: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIIsHidden" + ) + """Whether this asset is hidden in Power BI (true) or not (false).""" + + power_bi_table_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBITableQualifiedName" + ) + """Unique name of the Power BI table in which this asset exists.""" + + power_bi_format_string: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIFormatString" + ) + """Format of this asset, as specified in the FORMAT_STRING of the MDX cell property.""" + + power_bi_endorsement: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsement" + ) + """Endorsement status of this asset, in Power BI.""" + + power_bi_endorsed_by: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedBy" + ) + """User who endorsed this asset in Power BI.""" + + power_bi_endorsed_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedAt" + ) + """Time at which this asset was endorsed in Power BI.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + dashboard: Union[RelatedPowerBIDashboard, None, UnsetType] = UNSET + """Dashboard in which this tile exists.""" + + report: Union[RelatedPowerBIReport, None, UnsetType] = UNSET + """Report in which this tile exists.""" + + dataset: Union[RelatedPowerBIDataset, None, UnsetType] = UNSET + """Dataset in which this tile exists.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "PowerBITile" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _power_bi_tile_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> PowerBITile: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + PowerBITile instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _power_bi_tile_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class PowerBITileAttributes(AssetAttributes): + """PowerBITile-specific attributes for nested API format.""" + + workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace in which this tile exists.""" + + dashboard_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dashboard in which this tile is pinned.""" + + power_bi_is_hidden: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIIsHidden" + ) + """Whether this asset is hidden in Power BI (true) or not (false).""" + + power_bi_table_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBITableQualifiedName" + ) + """Unique name of the Power BI table in which this asset exists.""" + + power_bi_format_string: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIFormatString" + ) + """Format of this asset, as specified in the FORMAT_STRING of the MDX cell property.""" + + power_bi_endorsement: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsement" + ) + """Endorsement status of this asset, in Power BI.""" + + power_bi_endorsed_by: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedBy" + ) + """User who endorsed this asset in Power BI.""" + + power_bi_endorsed_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedAt" + ) + """Time at which this asset was endorsed in Power BI.""" + + +class PowerBITileRelationshipAttributes(AssetRelationshipAttributes): + """PowerBITile-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + dashboard: Union[RelatedPowerBIDashboard, None, UnsetType] = UNSET + """Dashboard in which this tile exists.""" + + report: Union[RelatedPowerBIReport, None, UnsetType] = UNSET + """Report in which this tile exists.""" + + dataset: Union[RelatedPowerBIDataset, None, UnsetType] = UNSET + """Dataset in which this tile exists.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class PowerBITileNested(AssetNested): + """PowerBITile in nested API format for high-performance serialization.""" + + attributes: Union[PowerBITileAttributes, UnsetType] = UNSET + relationship_attributes: Union[PowerBITileRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + PowerBITileRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + PowerBITileRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_POWER_BI_TILE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "dashboard", + "report", + "dataset", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_power_bi_tile_attrs( + attrs: PowerBITileAttributes, obj: PowerBITile +) -> None: + """Populate PowerBITile-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.workspace_qualified_name = obj.workspace_qualified_name + attrs.dashboard_qualified_name = obj.dashboard_qualified_name + attrs.power_bi_is_hidden = obj.power_bi_is_hidden + attrs.power_bi_table_qualified_name = obj.power_bi_table_qualified_name + attrs.power_bi_format_string = obj.power_bi_format_string + attrs.power_bi_endorsement = obj.power_bi_endorsement + attrs.power_bi_endorsed_by = obj.power_bi_endorsed_by + attrs.power_bi_endorsed_at = obj.power_bi_endorsed_at + + +def _extract_power_bi_tile_attrs(attrs: PowerBITileAttributes) -> dict: + """Extract all PowerBITile attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["workspace_qualified_name"] = attrs.workspace_qualified_name + result["dashboard_qualified_name"] = attrs.dashboard_qualified_name + result["power_bi_is_hidden"] = attrs.power_bi_is_hidden + result["power_bi_table_qualified_name"] = attrs.power_bi_table_qualified_name + result["power_bi_format_string"] = attrs.power_bi_format_string + result["power_bi_endorsement"] = attrs.power_bi_endorsement + result["power_bi_endorsed_by"] = attrs.power_bi_endorsed_by + result["power_bi_endorsed_at"] = attrs.power_bi_endorsed_at + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _power_bi_tile_to_nested(power_bi_tile: PowerBITile) -> PowerBITileNested: + """Convert flat PowerBITile to nested format.""" + attrs = PowerBITileAttributes() + _populate_power_bi_tile_attrs(attrs, power_bi_tile) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + power_bi_tile, _POWER_BI_TILE_REL_FIELDS, PowerBITileRelationshipAttributes + ) + return PowerBITileNested( + guid=power_bi_tile.guid, + type_name=power_bi_tile.type_name, + status=power_bi_tile.status, + version=power_bi_tile.version, + create_time=power_bi_tile.create_time, + update_time=power_bi_tile.update_time, + created_by=power_bi_tile.created_by, + updated_by=power_bi_tile.updated_by, + classifications=power_bi_tile.classifications, + classification_names=power_bi_tile.classification_names, + meanings=power_bi_tile.meanings, + labels=power_bi_tile.labels, + business_attributes=power_bi_tile.business_attributes, + custom_attributes=power_bi_tile.custom_attributes, + pending_tasks=power_bi_tile.pending_tasks, + proxy=power_bi_tile.proxy, + is_incomplete=power_bi_tile.is_incomplete, + provenance_type=power_bi_tile.provenance_type, + home_id=power_bi_tile.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _power_bi_tile_from_nested(nested: PowerBITileNested) -> PowerBITile: + """Convert nested format to flat PowerBITile.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else PowerBITileAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _POWER_BI_TILE_REL_FIELDS, + PowerBITileRelationshipAttributes, + ) + return PowerBITile( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_power_bi_tile_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _power_bi_tile_to_nested_bytes(power_bi_tile: PowerBITile, serde: Serde) -> bytes: + """Convert flat PowerBITile to nested JSON bytes.""" + return serde.encode(_power_bi_tile_to_nested(power_bi_tile)) + + +def _power_bi_tile_from_nested_bytes(data: bytes, serde: Serde) -> PowerBITile: + """Convert nested JSON bytes to flat PowerBITile.""" + nested = serde.decode(data, PowerBITileNested) + return _power_bi_tile_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +PowerBITile.WORKSPACE_QUALIFIED_NAME = KeywordField( + "workspaceQualifiedName", "workspaceQualifiedName" +) +PowerBITile.DASHBOARD_QUALIFIED_NAME = KeywordField( + "dashboardQualifiedName", "dashboardQualifiedName" +) +PowerBITile.POWER_BI_IS_HIDDEN = BooleanField("powerBIIsHidden", "powerBIIsHidden") +PowerBITile.POWER_BI_TABLE_QUALIFIED_NAME = KeywordTextField( + "powerBITableQualifiedName", + "powerBITableQualifiedName", + "powerBITableQualifiedName.text", +) +PowerBITile.POWER_BI_FORMAT_STRING = KeywordField( + "powerBIFormatString", "powerBIFormatString" +) +PowerBITile.POWER_BI_ENDORSEMENT = KeywordField( + "powerBIEndorsement", "powerBIEndorsement" +) +PowerBITile.POWER_BI_ENDORSED_BY = KeywordField( + "powerBIEndorsedBy", "powerBIEndorsedBy" +) +PowerBITile.POWER_BI_ENDORSED_AT = NumericField( + "powerBIEndorsedAt", "powerBIEndorsedAt" +) +PowerBITile.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +PowerBITile.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +PowerBITile.ANOMALO_CHECKS = RelationField("anomaloChecks") +PowerBITile.APPLICATION = RelationField("application") +PowerBITile.APPLICATION_FIELD = RelationField("applicationField") +PowerBITile.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +PowerBITile.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +PowerBITile.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +PowerBITile.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +PowerBITile.METRICS = RelationField("metrics") +PowerBITile.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +PowerBITile.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +PowerBITile.MEANINGS = RelationField("meanings") +PowerBITile.MC_MONITORS = RelationField("mcMonitors") +PowerBITile.MC_INCIDENTS = RelationField("mcIncidents") +PowerBITile.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +PowerBITile.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +PowerBITile.DASHBOARD = RelationField("dashboard") +PowerBITile.REPORT = RelationField("report") +PowerBITile.DATASET = RelationField("dataset") +PowerBITile.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +PowerBITile.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +PowerBITile.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +PowerBITile.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +PowerBITile.FILES = RelationField("files") +PowerBITile.LINKS = RelationField("links") +PowerBITile.README = RelationField("readme") +PowerBITile.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +PowerBITile.SODA_CHECKS = RelationField("sodaChecks") +PowerBITile.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +PowerBITile.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/power_bi_workspace.py b/pyatlan_v9/model/assets/power_bi_workspace.py new file mode 100644 index 000000000..d4def8460 --- /dev/null +++ b/pyatlan_v9/model/assets/power_bi_workspace.py @@ -0,0 +1,738 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +PowerBIWorkspace asset model with flattened inheritance. + +This module provides: +- PowerBIWorkspace: Flat asset class (easy to use) +- PowerBIWorkspaceAttributes: Nested attributes struct (extends AssetAttributes) +- PowerBIWorkspaceNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .power_bi_related import ( + RelatedPowerBIDashboard, + RelatedPowerBIDataflow, + RelatedPowerBIDataset, + RelatedPowerBIReport, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class PowerBIWorkspace(Asset): + """ + Instance of a Power BI workspace in Atlan. Workspaces contain dashboards, reports, workbooks, datasets and dataflows. + """ + + WEB_URL: ClassVar[Any] = None + REPORT_COUNT: ClassVar[Any] = None + DASHBOARD_COUNT: ClassVar[Any] = None + DATASET_COUNT: ClassVar[Any] = None + DATAFLOW_COUNT: ClassVar[Any] = None + POWER_BI_IS_HIDDEN: ClassVar[Any] = None + POWER_BI_TABLE_QUALIFIED_NAME: ClassVar[Any] = None + POWER_BI_FORMAT_STRING: ClassVar[Any] = None + POWER_BI_ENDORSEMENT: ClassVar[Any] = None + POWER_BI_ENDORSED_BY: ClassVar[Any] = None + POWER_BI_ENDORSED_AT: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + DATASETS: ClassVar[Any] = None + REPORTS: ClassVar[Any] = None + DASHBOARDS: ClassVar[Any] = None + DATAFLOWS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "PowerBIWorkspace" + + web_url: Union[str, None, UnsetType] = UNSET + """Deprecated.""" + + report_count: Union[int, None, UnsetType] = UNSET + """Number of reports in this workspace.""" + + dashboard_count: Union[int, None, UnsetType] = UNSET + """Number of dashboards in this workspace.""" + + dataset_count: Union[int, None, UnsetType] = UNSET + """Number of datasets in this workspace.""" + + dataflow_count: Union[int, None, UnsetType] = UNSET + """Number of dataflows in this workspace.""" + + power_bi_is_hidden: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIIsHidden" + ) + """Whether this asset is hidden in Power BI (true) or not (false).""" + + power_bi_table_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBITableQualifiedName" + ) + """Unique name of the Power BI table in which this asset exists.""" + + power_bi_format_string: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIFormatString" + ) + """Format of this asset, as specified in the FORMAT_STRING of the MDX cell property.""" + + power_bi_endorsement: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsement" + ) + """Endorsement status of this asset, in Power BI.""" + + power_bi_endorsed_by: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedBy" + ) + """User who endorsed this asset in Power BI.""" + + power_bi_endorsed_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedAt" + ) + """Time at which this asset was endorsed in Power BI.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + datasets: Union[List[RelatedPowerBIDataset], None, UnsetType] = UNSET + """Datasets that exist within this workspace.""" + + reports: Union[List[RelatedPowerBIReport], None, UnsetType] = UNSET + """Reports that exist within this workspace.""" + + dashboards: Union[List[RelatedPowerBIDashboard], None, UnsetType] = UNSET + """Dashboards that exist within this workspace.""" + + dataflows: Union[List[RelatedPowerBIDataflow], None, UnsetType] = UNSET + """Dataflows that exist within this workspace.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "PowerBIWorkspace" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _power_bi_workspace_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> PowerBIWorkspace: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + PowerBIWorkspace instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _power_bi_workspace_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class PowerBIWorkspaceAttributes(AssetAttributes): + """PowerBIWorkspace-specific attributes for nested API format.""" + + web_url: Union[str, None, UnsetType] = UNSET + """Deprecated.""" + + report_count: Union[int, None, UnsetType] = UNSET + """Number of reports in this workspace.""" + + dashboard_count: Union[int, None, UnsetType] = UNSET + """Number of dashboards in this workspace.""" + + dataset_count: Union[int, None, UnsetType] = UNSET + """Number of datasets in this workspace.""" + + dataflow_count: Union[int, None, UnsetType] = UNSET + """Number of dataflows in this workspace.""" + + power_bi_is_hidden: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIIsHidden" + ) + """Whether this asset is hidden in Power BI (true) or not (false).""" + + power_bi_table_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBITableQualifiedName" + ) + """Unique name of the Power BI table in which this asset exists.""" + + power_bi_format_string: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIFormatString" + ) + """Format of this asset, as specified in the FORMAT_STRING of the MDX cell property.""" + + power_bi_endorsement: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsement" + ) + """Endorsement status of this asset, in Power BI.""" + + power_bi_endorsed_by: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedBy" + ) + """User who endorsed this asset in Power BI.""" + + power_bi_endorsed_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIEndorsedAt" + ) + """Time at which this asset was endorsed in Power BI.""" + + +class PowerBIWorkspaceRelationshipAttributes(AssetRelationshipAttributes): + """PowerBIWorkspace-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + datasets: Union[List[RelatedPowerBIDataset], None, UnsetType] = UNSET + """Datasets that exist within this workspace.""" + + reports: Union[List[RelatedPowerBIReport], None, UnsetType] = UNSET + """Reports that exist within this workspace.""" + + dashboards: Union[List[RelatedPowerBIDashboard], None, UnsetType] = UNSET + """Dashboards that exist within this workspace.""" + + dataflows: Union[List[RelatedPowerBIDataflow], None, UnsetType] = UNSET + """Dataflows that exist within this workspace.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class PowerBIWorkspaceNested(AssetNested): + """PowerBIWorkspace in nested API format for high-performance serialization.""" + + attributes: Union[PowerBIWorkspaceAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + PowerBIWorkspaceRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + PowerBIWorkspaceRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + PowerBIWorkspaceRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_POWER_BI_WORKSPACE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "datasets", + "reports", + "dashboards", + "dataflows", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_power_bi_workspace_attrs( + attrs: PowerBIWorkspaceAttributes, obj: PowerBIWorkspace +) -> None: + """Populate PowerBIWorkspace-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.web_url = obj.web_url + attrs.report_count = obj.report_count + attrs.dashboard_count = obj.dashboard_count + attrs.dataset_count = obj.dataset_count + attrs.dataflow_count = obj.dataflow_count + attrs.power_bi_is_hidden = obj.power_bi_is_hidden + attrs.power_bi_table_qualified_name = obj.power_bi_table_qualified_name + attrs.power_bi_format_string = obj.power_bi_format_string + attrs.power_bi_endorsement = obj.power_bi_endorsement + attrs.power_bi_endorsed_by = obj.power_bi_endorsed_by + attrs.power_bi_endorsed_at = obj.power_bi_endorsed_at + + +def _extract_power_bi_workspace_attrs(attrs: PowerBIWorkspaceAttributes) -> dict: + """Extract all PowerBIWorkspace attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["web_url"] = attrs.web_url + result["report_count"] = attrs.report_count + result["dashboard_count"] = attrs.dashboard_count + result["dataset_count"] = attrs.dataset_count + result["dataflow_count"] = attrs.dataflow_count + result["power_bi_is_hidden"] = attrs.power_bi_is_hidden + result["power_bi_table_qualified_name"] = attrs.power_bi_table_qualified_name + result["power_bi_format_string"] = attrs.power_bi_format_string + result["power_bi_endorsement"] = attrs.power_bi_endorsement + result["power_bi_endorsed_by"] = attrs.power_bi_endorsed_by + result["power_bi_endorsed_at"] = attrs.power_bi_endorsed_at + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _power_bi_workspace_to_nested( + power_bi_workspace: PowerBIWorkspace, +) -> PowerBIWorkspaceNested: + """Convert flat PowerBIWorkspace to nested format.""" + attrs = PowerBIWorkspaceAttributes() + _populate_power_bi_workspace_attrs(attrs, power_bi_workspace) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + power_bi_workspace, + _POWER_BI_WORKSPACE_REL_FIELDS, + PowerBIWorkspaceRelationshipAttributes, + ) + return PowerBIWorkspaceNested( + guid=power_bi_workspace.guid, + type_name=power_bi_workspace.type_name, + status=power_bi_workspace.status, + version=power_bi_workspace.version, + create_time=power_bi_workspace.create_time, + update_time=power_bi_workspace.update_time, + created_by=power_bi_workspace.created_by, + updated_by=power_bi_workspace.updated_by, + classifications=power_bi_workspace.classifications, + classification_names=power_bi_workspace.classification_names, + meanings=power_bi_workspace.meanings, + labels=power_bi_workspace.labels, + business_attributes=power_bi_workspace.business_attributes, + custom_attributes=power_bi_workspace.custom_attributes, + pending_tasks=power_bi_workspace.pending_tasks, + proxy=power_bi_workspace.proxy, + is_incomplete=power_bi_workspace.is_incomplete, + provenance_type=power_bi_workspace.provenance_type, + home_id=power_bi_workspace.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _power_bi_workspace_from_nested(nested: PowerBIWorkspaceNested) -> PowerBIWorkspace: + """Convert nested format to flat PowerBIWorkspace.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else PowerBIWorkspaceAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _POWER_BI_WORKSPACE_REL_FIELDS, + PowerBIWorkspaceRelationshipAttributes, + ) + return PowerBIWorkspace( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_power_bi_workspace_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _power_bi_workspace_to_nested_bytes( + power_bi_workspace: PowerBIWorkspace, serde: Serde +) -> bytes: + """Convert flat PowerBIWorkspace to nested JSON bytes.""" + return serde.encode(_power_bi_workspace_to_nested(power_bi_workspace)) + + +def _power_bi_workspace_from_nested_bytes( + data: bytes, serde: Serde +) -> PowerBIWorkspace: + """Convert nested JSON bytes to flat PowerBIWorkspace.""" + nested = serde.decode(data, PowerBIWorkspaceNested) + return _power_bi_workspace_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +PowerBIWorkspace.WEB_URL = KeywordField("webUrl", "webUrl") +PowerBIWorkspace.REPORT_COUNT = NumericField("reportCount", "reportCount") +PowerBIWorkspace.DASHBOARD_COUNT = NumericField("dashboardCount", "dashboardCount") +PowerBIWorkspace.DATASET_COUNT = NumericField("datasetCount", "datasetCount") +PowerBIWorkspace.DATAFLOW_COUNT = NumericField("dataflowCount", "dataflowCount") +PowerBIWorkspace.POWER_BI_IS_HIDDEN = BooleanField("powerBIIsHidden", "powerBIIsHidden") +PowerBIWorkspace.POWER_BI_TABLE_QUALIFIED_NAME = KeywordTextField( + "powerBITableQualifiedName", + "powerBITableQualifiedName", + "powerBITableQualifiedName.text", +) +PowerBIWorkspace.POWER_BI_FORMAT_STRING = KeywordField( + "powerBIFormatString", "powerBIFormatString" +) +PowerBIWorkspace.POWER_BI_ENDORSEMENT = KeywordField( + "powerBIEndorsement", "powerBIEndorsement" +) +PowerBIWorkspace.POWER_BI_ENDORSED_BY = KeywordField( + "powerBIEndorsedBy", "powerBIEndorsedBy" +) +PowerBIWorkspace.POWER_BI_ENDORSED_AT = NumericField( + "powerBIEndorsedAt", "powerBIEndorsedAt" +) +PowerBIWorkspace.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +PowerBIWorkspace.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +PowerBIWorkspace.ANOMALO_CHECKS = RelationField("anomaloChecks") +PowerBIWorkspace.APPLICATION = RelationField("application") +PowerBIWorkspace.APPLICATION_FIELD = RelationField("applicationField") +PowerBIWorkspace.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +PowerBIWorkspace.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +PowerBIWorkspace.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +PowerBIWorkspace.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +PowerBIWorkspace.METRICS = RelationField("metrics") +PowerBIWorkspace.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +PowerBIWorkspace.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +PowerBIWorkspace.MEANINGS = RelationField("meanings") +PowerBIWorkspace.MC_MONITORS = RelationField("mcMonitors") +PowerBIWorkspace.MC_INCIDENTS = RelationField("mcIncidents") +PowerBIWorkspace.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +PowerBIWorkspace.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +PowerBIWorkspace.DATASETS = RelationField("datasets") +PowerBIWorkspace.REPORTS = RelationField("reports") +PowerBIWorkspace.DASHBOARDS = RelationField("dashboards") +PowerBIWorkspace.DATAFLOWS = RelationField("dataflows") +PowerBIWorkspace.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +PowerBIWorkspace.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +PowerBIWorkspace.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +PowerBIWorkspace.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +PowerBIWorkspace.FILES = RelationField("files") +PowerBIWorkspace.LINKS = RelationField("links") +PowerBIWorkspace.README = RelationField("readme") +PowerBIWorkspace.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +PowerBIWorkspace.SODA_CHECKS = RelationField("sodaChecks") +PowerBIWorkspace.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +PowerBIWorkspace.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/preset.py b/pyatlan_v9/model/assets/preset.py new file mode 100644 index 000000000..c82d0885d --- /dev/null +++ b/pyatlan_v9/model/assets/preset.py @@ -0,0 +1,574 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Preset asset model with flattened inheritance. + +This module provides: +- Preset: Flat asset class (easy to use) +- PresetAttributes: Nested attributes struct (extends AssetAttributes) +- PresetNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Preset(Asset): + """ + Base class for Preset assets. + """ + + PRESET_WORKSPACE_ID: ClassVar[Any] = None + PRESET_WORKSPACE_QUALIFIED_NAME: ClassVar[Any] = None + PRESET_DASHBOARD_ID: ClassVar[Any] = None + PRESET_DASHBOARD_QUALIFIED_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Preset" + + preset_workspace_id: Union[int, None, UnsetType] = UNSET + """Identifier of the workspace in which this asset exists, in Preset.""" + + preset_workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace in which this asset exists.""" + + preset_dashboard_id: Union[int, None, UnsetType] = UNSET + """Identifier of the dashboard in which this asset exists, in Preset.""" + + preset_dashboard_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dashboard in which this asset exists.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Preset" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _preset_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Preset: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Preset instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _preset_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class PresetAttributes(AssetAttributes): + """Preset-specific attributes for nested API format.""" + + preset_workspace_id: Union[int, None, UnsetType] = UNSET + """Identifier of the workspace in which this asset exists, in Preset.""" + + preset_workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace in which this asset exists.""" + + preset_dashboard_id: Union[int, None, UnsetType] = UNSET + """Identifier of the dashboard in which this asset exists, in Preset.""" + + preset_dashboard_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dashboard in which this asset exists.""" + + +class PresetRelationshipAttributes(AssetRelationshipAttributes): + """Preset-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class PresetNested(AssetNested): + """Preset in nested API format for high-performance serialization.""" + + attributes: Union[PresetAttributes, UnsetType] = UNSET + relationship_attributes: Union[PresetRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[PresetRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[PresetRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_PRESET_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_preset_attrs(attrs: PresetAttributes, obj: Preset) -> None: + """Populate Preset-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.preset_workspace_id = obj.preset_workspace_id + attrs.preset_workspace_qualified_name = obj.preset_workspace_qualified_name + attrs.preset_dashboard_id = obj.preset_dashboard_id + attrs.preset_dashboard_qualified_name = obj.preset_dashboard_qualified_name + + +def _extract_preset_attrs(attrs: PresetAttributes) -> dict: + """Extract all Preset attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["preset_workspace_id"] = attrs.preset_workspace_id + result["preset_workspace_qualified_name"] = attrs.preset_workspace_qualified_name + result["preset_dashboard_id"] = attrs.preset_dashboard_id + result["preset_dashboard_qualified_name"] = attrs.preset_dashboard_qualified_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _preset_to_nested(preset: Preset) -> PresetNested: + """Convert flat Preset to nested format.""" + attrs = PresetAttributes() + _populate_preset_attrs(attrs, preset) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + preset, _PRESET_REL_FIELDS, PresetRelationshipAttributes + ) + return PresetNested( + guid=preset.guid, + type_name=preset.type_name, + status=preset.status, + version=preset.version, + create_time=preset.create_time, + update_time=preset.update_time, + created_by=preset.created_by, + updated_by=preset.updated_by, + classifications=preset.classifications, + classification_names=preset.classification_names, + meanings=preset.meanings, + labels=preset.labels, + business_attributes=preset.business_attributes, + custom_attributes=preset.custom_attributes, + pending_tasks=preset.pending_tasks, + proxy=preset.proxy, + is_incomplete=preset.is_incomplete, + provenance_type=preset.provenance_type, + home_id=preset.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _preset_from_nested(nested: PresetNested) -> Preset: + """Convert nested format to flat Preset.""" + attrs = nested.attributes if nested.attributes is not UNSET else PresetAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _PRESET_REL_FIELDS, + PresetRelationshipAttributes, + ) + return Preset( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_preset_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _preset_to_nested_bytes(preset: Preset, serde: Serde) -> bytes: + """Convert flat Preset to nested JSON bytes.""" + return serde.encode(_preset_to_nested(preset)) + + +def _preset_from_nested_bytes(data: bytes, serde: Serde) -> Preset: + """Convert nested JSON bytes to flat Preset.""" + nested = serde.decode(data, PresetNested) + return _preset_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordTextField, + NumericField, + RelationField, +) + +Preset.PRESET_WORKSPACE_ID = NumericField("presetWorkspaceId", "presetWorkspaceId") +Preset.PRESET_WORKSPACE_QUALIFIED_NAME = KeywordTextField( + "presetWorkspaceQualifiedName", + "presetWorkspaceQualifiedName", + "presetWorkspaceQualifiedName.text", +) +Preset.PRESET_DASHBOARD_ID = NumericField("presetDashboardId", "presetDashboardId") +Preset.PRESET_DASHBOARD_QUALIFIED_NAME = KeywordTextField( + "presetDashboardQualifiedName", + "presetDashboardQualifiedName", + "presetDashboardQualifiedName.text", +) +Preset.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Preset.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Preset.ANOMALO_CHECKS = RelationField("anomaloChecks") +Preset.APPLICATION = RelationField("application") +Preset.APPLICATION_FIELD = RelationField("applicationField") +Preset.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Preset.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Preset.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Preset.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Preset.METRICS = RelationField("metrics") +Preset.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Preset.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Preset.MEANINGS = RelationField("meanings") +Preset.MC_MONITORS = RelationField("mcMonitors") +Preset.MC_INCIDENTS = RelationField("mcIncidents") +Preset.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Preset.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Preset.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Preset.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Preset.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Preset.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Preset.FILES = RelationField("files") +Preset.LINKS = RelationField("links") +Preset.README = RelationField("readme") +Preset.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Preset.SODA_CHECKS = RelationField("sodaChecks") +Preset.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Preset.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/preset_chart.py b/pyatlan_v9/model/assets/preset_chart.py new file mode 100644 index 000000000..54df38ad3 --- /dev/null +++ b/pyatlan_v9/model/assets/preset_chart.py @@ -0,0 +1,655 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +PresetChart asset model with flattened inheritance. + +This module provides: +- PresetChart: Flat asset class (easy to use) +- PresetChartAttributes: Nested attributes struct (extends AssetAttributes) +- PresetChartNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .preset_related import RelatedPresetDashboard + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class PresetChart(Asset): + """ + Instance of a Preset chart in Atlan. + """ + + PRESET_CHART_DESCRIPTION_MARKDOWN: ClassVar[Any] = None + PRESET_CHART_FORM_DATA: ClassVar[Any] = None + PRESET_WORKSPACE_ID: ClassVar[Any] = None + PRESET_WORKSPACE_QUALIFIED_NAME: ClassVar[Any] = None + PRESET_DASHBOARD_ID: ClassVar[Any] = None + PRESET_DASHBOARD_QUALIFIED_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + PRESET_DASHBOARD: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "PresetChart" + + preset_chart_description_markdown: Union[str, None, UnsetType] = UNSET + """""" + + preset_chart_form_data: Union[Dict[str, str], None, UnsetType] = UNSET + """""" + + preset_workspace_id: Union[int, None, UnsetType] = UNSET + """Identifier of the workspace in which this asset exists, in Preset.""" + + preset_workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace in which this asset exists.""" + + preset_dashboard_id: Union[int, None, UnsetType] = UNSET + """Identifier of the dashboard in which this asset exists, in Preset.""" + + preset_dashboard_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dashboard in which this asset exists.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + preset_dashboard: Union[RelatedPresetDashboard, None, UnsetType] = UNSET + """Dashboard in which this chart exists.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "PresetChart" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + preset_dashboard_qualified_name: str, + connection_qualified_name: str | None = None, + ) -> "PresetChart": + validate_required_fields( + ["name", "preset_dashboard_qualified_name"], + [name, preset_dashboard_qualified_name], + ) + fields = preset_dashboard_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + connection_qn = connection_qualified_name or ( + f"{fields[0]}/{fields[1]}/{fields[2]}" if len(fields) >= 3 else None + ) + return cls( + name=name, + qualified_name=f"{preset_dashboard_qualified_name}/{name}", + preset_dashboard_qualified_name=preset_dashboard_qualified_name, + connection_qualified_name=connection_qn, + connector_name=connector_name, + preset_dashboard=RelatedPresetDashboard( + qualified_name=preset_dashboard_qualified_name + ), + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _preset_chart_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> PresetChart: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + PresetChart instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _preset_chart_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class PresetChartAttributes(AssetAttributes): + """PresetChart-specific attributes for nested API format.""" + + preset_chart_description_markdown: Union[str, None, UnsetType] = UNSET + """""" + + preset_chart_form_data: Union[Dict[str, str], None, UnsetType] = UNSET + """""" + + preset_workspace_id: Union[int, None, UnsetType] = UNSET + """Identifier of the workspace in which this asset exists, in Preset.""" + + preset_workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace in which this asset exists.""" + + preset_dashboard_id: Union[int, None, UnsetType] = UNSET + """Identifier of the dashboard in which this asset exists, in Preset.""" + + preset_dashboard_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dashboard in which this asset exists.""" + + +class PresetChartRelationshipAttributes(AssetRelationshipAttributes): + """PresetChart-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + preset_dashboard: Union[RelatedPresetDashboard, None, UnsetType] = UNSET + """Dashboard in which this chart exists.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class PresetChartNested(AssetNested): + """PresetChart in nested API format for high-performance serialization.""" + + attributes: Union[PresetChartAttributes, UnsetType] = UNSET + relationship_attributes: Union[PresetChartRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + PresetChartRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + PresetChartRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_PRESET_CHART_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "preset_dashboard", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_preset_chart_attrs( + attrs: PresetChartAttributes, obj: PresetChart +) -> None: + """Populate PresetChart-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.preset_chart_description_markdown = obj.preset_chart_description_markdown + attrs.preset_chart_form_data = obj.preset_chart_form_data + attrs.preset_workspace_id = obj.preset_workspace_id + attrs.preset_workspace_qualified_name = obj.preset_workspace_qualified_name + attrs.preset_dashboard_id = obj.preset_dashboard_id + attrs.preset_dashboard_qualified_name = obj.preset_dashboard_qualified_name + + +def _extract_preset_chart_attrs(attrs: PresetChartAttributes) -> dict: + """Extract all PresetChart attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["preset_chart_description_markdown"] = ( + attrs.preset_chart_description_markdown + ) + result["preset_chart_form_data"] = attrs.preset_chart_form_data + result["preset_workspace_id"] = attrs.preset_workspace_id + result["preset_workspace_qualified_name"] = attrs.preset_workspace_qualified_name + result["preset_dashboard_id"] = attrs.preset_dashboard_id + result["preset_dashboard_qualified_name"] = attrs.preset_dashboard_qualified_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _preset_chart_to_nested(preset_chart: PresetChart) -> PresetChartNested: + """Convert flat PresetChart to nested format.""" + attrs = PresetChartAttributes() + _populate_preset_chart_attrs(attrs, preset_chart) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + preset_chart, _PRESET_CHART_REL_FIELDS, PresetChartRelationshipAttributes + ) + return PresetChartNested( + guid=preset_chart.guid, + type_name=preset_chart.type_name, + status=preset_chart.status, + version=preset_chart.version, + create_time=preset_chart.create_time, + update_time=preset_chart.update_time, + created_by=preset_chart.created_by, + updated_by=preset_chart.updated_by, + classifications=preset_chart.classifications, + classification_names=preset_chart.classification_names, + meanings=preset_chart.meanings, + labels=preset_chart.labels, + business_attributes=preset_chart.business_attributes, + custom_attributes=preset_chart.custom_attributes, + pending_tasks=preset_chart.pending_tasks, + proxy=preset_chart.proxy, + is_incomplete=preset_chart.is_incomplete, + provenance_type=preset_chart.provenance_type, + home_id=preset_chart.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _preset_chart_from_nested(nested: PresetChartNested) -> PresetChart: + """Convert nested format to flat PresetChart.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else PresetChartAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _PRESET_CHART_REL_FIELDS, + PresetChartRelationshipAttributes, + ) + return PresetChart( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_preset_chart_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _preset_chart_to_nested_bytes(preset_chart: PresetChart, serde: Serde) -> bytes: + """Convert flat PresetChart to nested JSON bytes.""" + return serde.encode(_preset_chart_to_nested(preset_chart)) + + +def _preset_chart_from_nested_bytes(data: bytes, serde: Serde) -> PresetChart: + """Convert nested JSON bytes to flat PresetChart.""" + nested = serde.decode(data, PresetChartNested) + return _preset_chart_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +PresetChart.PRESET_CHART_DESCRIPTION_MARKDOWN = KeywordField( + "presetChartDescriptionMarkdown", "presetChartDescriptionMarkdown" +) +PresetChart.PRESET_CHART_FORM_DATA = KeywordField( + "presetChartFormData", "presetChartFormData" +) +PresetChart.PRESET_WORKSPACE_ID = NumericField("presetWorkspaceId", "presetWorkspaceId") +PresetChart.PRESET_WORKSPACE_QUALIFIED_NAME = KeywordTextField( + "presetWorkspaceQualifiedName", + "presetWorkspaceQualifiedName", + "presetWorkspaceQualifiedName.text", +) +PresetChart.PRESET_DASHBOARD_ID = NumericField("presetDashboardId", "presetDashboardId") +PresetChart.PRESET_DASHBOARD_QUALIFIED_NAME = KeywordTextField( + "presetDashboardQualifiedName", + "presetDashboardQualifiedName", + "presetDashboardQualifiedName.text", +) +PresetChart.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +PresetChart.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +PresetChart.ANOMALO_CHECKS = RelationField("anomaloChecks") +PresetChart.APPLICATION = RelationField("application") +PresetChart.APPLICATION_FIELD = RelationField("applicationField") +PresetChart.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +PresetChart.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +PresetChart.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +PresetChart.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +PresetChart.METRICS = RelationField("metrics") +PresetChart.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +PresetChart.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +PresetChart.MEANINGS = RelationField("meanings") +PresetChart.MC_MONITORS = RelationField("mcMonitors") +PresetChart.MC_INCIDENTS = RelationField("mcIncidents") +PresetChart.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +PresetChart.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +PresetChart.PRESET_DASHBOARD = RelationField("presetDashboard") +PresetChart.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +PresetChart.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +PresetChart.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +PresetChart.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +PresetChart.FILES = RelationField("files") +PresetChart.LINKS = RelationField("links") +PresetChart.README = RelationField("readme") +PresetChart.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +PresetChart.SODA_CHECKS = RelationField("sodaChecks") +PresetChart.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +PresetChart.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/preset_dashboard.py b/pyatlan_v9/model/assets/preset_dashboard.py new file mode 100644 index 000000000..b01f04a4b --- /dev/null +++ b/pyatlan_v9/model/assets/preset_dashboard.py @@ -0,0 +1,753 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +PresetDashboard asset model with flattened inheritance. + +This module provides: +- PresetDashboard: Flat asset class (easy to use) +- PresetDashboardAttributes: Nested attributes struct (extends AssetAttributes) +- PresetDashboardNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .preset_related import ( + RelatedPresetChart, + RelatedPresetDataset, + RelatedPresetWorkspace, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class PresetDashboard(Asset): + """ + Instance of a Preset dashboard in Atlan. + """ + + PRESET_DASHBOARD_CHANGED_BY_NAME: ClassVar[Any] = None + PRESET_DASHBOARD_CHANGED_BY_URL: ClassVar[Any] = None + PRESET_DASHBOARD_IS_MANAGED_EXTERNALLY: ClassVar[Any] = None + PRESET_DASHBOARD_IS_PUBLISHED: ClassVar[Any] = None + PRESET_DASHBOARD_THUMBNAIL_URL: ClassVar[Any] = None + PRESET_DASHBOARD_CHART_COUNT: ClassVar[Any] = None + PRESET_WORKSPACE_ID: ClassVar[Any] = None + PRESET_WORKSPACE_QUALIFIED_NAME: ClassVar[Any] = None + PRESET_DASHBOARD_ID: ClassVar[Any] = None + PRESET_DASHBOARD_QUALIFIED_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + PRESET_CHARTS: ClassVar[Any] = None + PRESET_WORKSPACE: ClassVar[Any] = None + PRESET_DATASETS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "PresetDashboard" + + preset_dashboard_changed_by_name: Union[str, None, UnsetType] = UNSET + """""" + + preset_dashboard_changed_by_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="presetDashboardChangedByURL" + ) + """""" + + preset_dashboard_is_managed_externally: Union[bool, None, UnsetType] = UNSET + """""" + + preset_dashboard_is_published: Union[bool, None, UnsetType] = UNSET + """""" + + preset_dashboard_thumbnail_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="presetDashboardThumbnailURL" + ) + """""" + + preset_dashboard_chart_count: Union[int, None, UnsetType] = UNSET + """""" + + preset_workspace_id: Union[int, None, UnsetType] = UNSET + """Identifier of the workspace in which this asset exists, in Preset.""" + + preset_workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace in which this asset exists.""" + + preset_dashboard_id: Union[int, None, UnsetType] = UNSET + """Identifier of the dashboard in which this asset exists, in Preset.""" + + preset_dashboard_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dashboard in which this asset exists.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + preset_charts: Union[List[RelatedPresetChart], None, UnsetType] = UNSET + """Charts that exist within this dashboard.""" + + preset_workspace: Union[RelatedPresetWorkspace, None, UnsetType] = UNSET + """Workspace in which this dashboard exists.""" + + preset_datasets: Union[List[RelatedPresetDataset], None, UnsetType] = UNSET + """Datasets that exist within this dashboard.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "PresetDashboard" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + preset_workspace_qualified_name: str, + connection_qualified_name: str | None = None, + ) -> "PresetDashboard": + validate_required_fields( + ["name", "preset_workspace_qualified_name"], + [name, preset_workspace_qualified_name], + ) + fields = preset_workspace_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + connection_qn = connection_qualified_name or ( + f"{fields[0]}/{fields[1]}/{fields[2]}" if len(fields) >= 3 else None + ) + return cls( + name=name, + qualified_name=f"{preset_workspace_qualified_name}/{name}", + preset_workspace_qualified_name=preset_workspace_qualified_name, + connection_qualified_name=connection_qn, + connector_name=connector_name, + preset_workspace=RelatedPresetWorkspace( + qualified_name=preset_workspace_qualified_name + ), + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _preset_dashboard_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> PresetDashboard: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + PresetDashboard instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _preset_dashboard_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class PresetDashboardAttributes(AssetAttributes): + """PresetDashboard-specific attributes for nested API format.""" + + preset_dashboard_changed_by_name: Union[str, None, UnsetType] = UNSET + """""" + + preset_dashboard_changed_by_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="presetDashboardChangedByURL" + ) + """""" + + preset_dashboard_is_managed_externally: Union[bool, None, UnsetType] = UNSET + """""" + + preset_dashboard_is_published: Union[bool, None, UnsetType] = UNSET + """""" + + preset_dashboard_thumbnail_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="presetDashboardThumbnailURL" + ) + """""" + + preset_dashboard_chart_count: Union[int, None, UnsetType] = UNSET + """""" + + preset_workspace_id: Union[int, None, UnsetType] = UNSET + """Identifier of the workspace in which this asset exists, in Preset.""" + + preset_workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace in which this asset exists.""" + + preset_dashboard_id: Union[int, None, UnsetType] = UNSET + """Identifier of the dashboard in which this asset exists, in Preset.""" + + preset_dashboard_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dashboard in which this asset exists.""" + + +class PresetDashboardRelationshipAttributes(AssetRelationshipAttributes): + """PresetDashboard-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + preset_charts: Union[List[RelatedPresetChart], None, UnsetType] = UNSET + """Charts that exist within this dashboard.""" + + preset_workspace: Union[RelatedPresetWorkspace, None, UnsetType] = UNSET + """Workspace in which this dashboard exists.""" + + preset_datasets: Union[List[RelatedPresetDataset], None, UnsetType] = UNSET + """Datasets that exist within this dashboard.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class PresetDashboardNested(AssetNested): + """PresetDashboard in nested API format for high-performance serialization.""" + + attributes: Union[PresetDashboardAttributes, UnsetType] = UNSET + relationship_attributes: Union[PresetDashboardRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + PresetDashboardRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + PresetDashboardRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_PRESET_DASHBOARD_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "preset_charts", + "preset_workspace", + "preset_datasets", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_preset_dashboard_attrs( + attrs: PresetDashboardAttributes, obj: PresetDashboard +) -> None: + """Populate PresetDashboard-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.preset_dashboard_changed_by_name = obj.preset_dashboard_changed_by_name + attrs.preset_dashboard_changed_by_url = obj.preset_dashboard_changed_by_url + attrs.preset_dashboard_is_managed_externally = ( + obj.preset_dashboard_is_managed_externally + ) + attrs.preset_dashboard_is_published = obj.preset_dashboard_is_published + attrs.preset_dashboard_thumbnail_url = obj.preset_dashboard_thumbnail_url + attrs.preset_dashboard_chart_count = obj.preset_dashboard_chart_count + attrs.preset_workspace_id = obj.preset_workspace_id + attrs.preset_workspace_qualified_name = obj.preset_workspace_qualified_name + attrs.preset_dashboard_id = obj.preset_dashboard_id + attrs.preset_dashboard_qualified_name = obj.preset_dashboard_qualified_name + + +def _extract_preset_dashboard_attrs(attrs: PresetDashboardAttributes) -> dict: + """Extract all PresetDashboard attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["preset_dashboard_changed_by_name"] = attrs.preset_dashboard_changed_by_name + result["preset_dashboard_changed_by_url"] = attrs.preset_dashboard_changed_by_url + result["preset_dashboard_is_managed_externally"] = ( + attrs.preset_dashboard_is_managed_externally + ) + result["preset_dashboard_is_published"] = attrs.preset_dashboard_is_published + result["preset_dashboard_thumbnail_url"] = attrs.preset_dashboard_thumbnail_url + result["preset_dashboard_chart_count"] = attrs.preset_dashboard_chart_count + result["preset_workspace_id"] = attrs.preset_workspace_id + result["preset_workspace_qualified_name"] = attrs.preset_workspace_qualified_name + result["preset_dashboard_id"] = attrs.preset_dashboard_id + result["preset_dashboard_qualified_name"] = attrs.preset_dashboard_qualified_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _preset_dashboard_to_nested( + preset_dashboard: PresetDashboard, +) -> PresetDashboardNested: + """Convert flat PresetDashboard to nested format.""" + attrs = PresetDashboardAttributes() + _populate_preset_dashboard_attrs(attrs, preset_dashboard) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + preset_dashboard, + _PRESET_DASHBOARD_REL_FIELDS, + PresetDashboardRelationshipAttributes, + ) + return PresetDashboardNested( + guid=preset_dashboard.guid, + type_name=preset_dashboard.type_name, + status=preset_dashboard.status, + version=preset_dashboard.version, + create_time=preset_dashboard.create_time, + update_time=preset_dashboard.update_time, + created_by=preset_dashboard.created_by, + updated_by=preset_dashboard.updated_by, + classifications=preset_dashboard.classifications, + classification_names=preset_dashboard.classification_names, + meanings=preset_dashboard.meanings, + labels=preset_dashboard.labels, + business_attributes=preset_dashboard.business_attributes, + custom_attributes=preset_dashboard.custom_attributes, + pending_tasks=preset_dashboard.pending_tasks, + proxy=preset_dashboard.proxy, + is_incomplete=preset_dashboard.is_incomplete, + provenance_type=preset_dashboard.provenance_type, + home_id=preset_dashboard.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _preset_dashboard_from_nested(nested: PresetDashboardNested) -> PresetDashboard: + """Convert nested format to flat PresetDashboard.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else PresetDashboardAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _PRESET_DASHBOARD_REL_FIELDS, + PresetDashboardRelationshipAttributes, + ) + return PresetDashboard( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_preset_dashboard_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _preset_dashboard_to_nested_bytes( + preset_dashboard: PresetDashboard, serde: Serde +) -> bytes: + """Convert flat PresetDashboard to nested JSON bytes.""" + return serde.encode(_preset_dashboard_to_nested(preset_dashboard)) + + +def _preset_dashboard_from_nested_bytes(data: bytes, serde: Serde) -> PresetDashboard: + """Convert nested JSON bytes to flat PresetDashboard.""" + nested = serde.decode(data, PresetDashboardNested) + return _preset_dashboard_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +PresetDashboard.PRESET_DASHBOARD_CHANGED_BY_NAME = KeywordField( + "presetDashboardChangedByName", "presetDashboardChangedByName" +) +PresetDashboard.PRESET_DASHBOARD_CHANGED_BY_URL = KeywordField( + "presetDashboardChangedByURL", "presetDashboardChangedByURL" +) +PresetDashboard.PRESET_DASHBOARD_IS_MANAGED_EXTERNALLY = BooleanField( + "presetDashboardIsManagedExternally", "presetDashboardIsManagedExternally" +) +PresetDashboard.PRESET_DASHBOARD_IS_PUBLISHED = BooleanField( + "presetDashboardIsPublished", "presetDashboardIsPublished" +) +PresetDashboard.PRESET_DASHBOARD_THUMBNAIL_URL = KeywordField( + "presetDashboardThumbnailURL", "presetDashboardThumbnailURL" +) +PresetDashboard.PRESET_DASHBOARD_CHART_COUNT = NumericField( + "presetDashboardChartCount", "presetDashboardChartCount" +) +PresetDashboard.PRESET_WORKSPACE_ID = NumericField( + "presetWorkspaceId", "presetWorkspaceId" +) +PresetDashboard.PRESET_WORKSPACE_QUALIFIED_NAME = KeywordTextField( + "presetWorkspaceQualifiedName", + "presetWorkspaceQualifiedName", + "presetWorkspaceQualifiedName.text", +) +PresetDashboard.PRESET_DASHBOARD_ID = NumericField( + "presetDashboardId", "presetDashboardId" +) +PresetDashboard.PRESET_DASHBOARD_QUALIFIED_NAME = KeywordTextField( + "presetDashboardQualifiedName", + "presetDashboardQualifiedName", + "presetDashboardQualifiedName.text", +) +PresetDashboard.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +PresetDashboard.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +PresetDashboard.ANOMALO_CHECKS = RelationField("anomaloChecks") +PresetDashboard.APPLICATION = RelationField("application") +PresetDashboard.APPLICATION_FIELD = RelationField("applicationField") +PresetDashboard.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +PresetDashboard.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +PresetDashboard.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +PresetDashboard.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +PresetDashboard.METRICS = RelationField("metrics") +PresetDashboard.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +PresetDashboard.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +PresetDashboard.MEANINGS = RelationField("meanings") +PresetDashboard.MC_MONITORS = RelationField("mcMonitors") +PresetDashboard.MC_INCIDENTS = RelationField("mcIncidents") +PresetDashboard.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +PresetDashboard.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +PresetDashboard.PRESET_CHARTS = RelationField("presetCharts") +PresetDashboard.PRESET_WORKSPACE = RelationField("presetWorkspace") +PresetDashboard.PRESET_DATASETS = RelationField("presetDatasets") +PresetDashboard.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +PresetDashboard.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +PresetDashboard.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +PresetDashboard.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +PresetDashboard.FILES = RelationField("files") +PresetDashboard.LINKS = RelationField("links") +PresetDashboard.README = RelationField("readme") +PresetDashboard.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +PresetDashboard.SODA_CHECKS = RelationField("sodaChecks") +PresetDashboard.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +PresetDashboard.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/preset_dataset.py b/pyatlan_v9/model/assets/preset_dataset.py new file mode 100644 index 000000000..395521a36 --- /dev/null +++ b/pyatlan_v9/model/assets/preset_dataset.py @@ -0,0 +1,673 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +PresetDataset asset model with flattened inheritance. + +This module provides: +- PresetDataset: Flat asset class (easy to use) +- PresetDatasetAttributes: Nested attributes struct (extends AssetAttributes) +- PresetDatasetNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .preset_related import RelatedPresetDashboard + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class PresetDataset(Asset): + """ + Instance of a Preset dataset in Atlan. + """ + + PRESET_DATASET_DATASOURCE_NAME: ClassVar[Any] = None + PRESET_DATASET_ID: ClassVar[Any] = None + PRESET_DATASET_TYPE: ClassVar[Any] = None + PRESET_WORKSPACE_ID: ClassVar[Any] = None + PRESET_WORKSPACE_QUALIFIED_NAME: ClassVar[Any] = None + PRESET_DASHBOARD_ID: ClassVar[Any] = None + PRESET_DASHBOARD_QUALIFIED_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + PRESET_DASHBOARD: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "PresetDataset" + + preset_dataset_datasource_name: Union[str, None, UnsetType] = UNSET + """""" + + preset_dataset_id: Union[int, None, UnsetType] = UNSET + """""" + + preset_dataset_type: Union[str, None, UnsetType] = UNSET + """""" + + preset_workspace_id: Union[int, None, UnsetType] = UNSET + """Identifier of the workspace in which this asset exists, in Preset.""" + + preset_workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace in which this asset exists.""" + + preset_dashboard_id: Union[int, None, UnsetType] = UNSET + """Identifier of the dashboard in which this asset exists, in Preset.""" + + preset_dashboard_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dashboard in which this asset exists.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + preset_dashboard: Union[RelatedPresetDashboard, None, UnsetType] = UNSET + """Dashboard in which this dataset exists.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "PresetDataset" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + preset_dashboard_qualified_name: str, + connection_qualified_name: str | None = None, + ) -> "PresetDataset": + validate_required_fields( + ["name", "preset_dashboard_qualified_name"], + [name, preset_dashboard_qualified_name], + ) + fields = preset_dashboard_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + connection_qn = connection_qualified_name or ( + f"{fields[0]}/{fields[1]}/{fields[2]}" if len(fields) >= 3 else None + ) + return cls( + name=name, + qualified_name=f"{preset_dashboard_qualified_name}/{name}", + preset_dashboard_qualified_name=preset_dashboard_qualified_name, + connection_qualified_name=connection_qn, + connector_name=connector_name, + preset_dashboard=RelatedPresetDashboard( + qualified_name=preset_dashboard_qualified_name + ), + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _preset_dataset_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> PresetDataset: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + PresetDataset instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _preset_dataset_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class PresetDatasetAttributes(AssetAttributes): + """PresetDataset-specific attributes for nested API format.""" + + preset_dataset_datasource_name: Union[str, None, UnsetType] = UNSET + """""" + + preset_dataset_id: Union[int, None, UnsetType] = UNSET + """""" + + preset_dataset_type: Union[str, None, UnsetType] = UNSET + """""" + + preset_workspace_id: Union[int, None, UnsetType] = UNSET + """Identifier of the workspace in which this asset exists, in Preset.""" + + preset_workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace in which this asset exists.""" + + preset_dashboard_id: Union[int, None, UnsetType] = UNSET + """Identifier of the dashboard in which this asset exists, in Preset.""" + + preset_dashboard_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dashboard in which this asset exists.""" + + +class PresetDatasetRelationshipAttributes(AssetRelationshipAttributes): + """PresetDataset-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + preset_dashboard: Union[RelatedPresetDashboard, None, UnsetType] = UNSET + """Dashboard in which this dataset exists.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class PresetDatasetNested(AssetNested): + """PresetDataset in nested API format for high-performance serialization.""" + + attributes: Union[PresetDatasetAttributes, UnsetType] = UNSET + relationship_attributes: Union[PresetDatasetRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + PresetDatasetRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + PresetDatasetRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_PRESET_DATASET_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "preset_dashboard", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_preset_dataset_attrs( + attrs: PresetDatasetAttributes, obj: PresetDataset +) -> None: + """Populate PresetDataset-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.preset_dataset_datasource_name = obj.preset_dataset_datasource_name + attrs.preset_dataset_id = obj.preset_dataset_id + attrs.preset_dataset_type = obj.preset_dataset_type + attrs.preset_workspace_id = obj.preset_workspace_id + attrs.preset_workspace_qualified_name = obj.preset_workspace_qualified_name + attrs.preset_dashboard_id = obj.preset_dashboard_id + attrs.preset_dashboard_qualified_name = obj.preset_dashboard_qualified_name + + +def _extract_preset_dataset_attrs(attrs: PresetDatasetAttributes) -> dict: + """Extract all PresetDataset attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["preset_dataset_datasource_name"] = attrs.preset_dataset_datasource_name + result["preset_dataset_id"] = attrs.preset_dataset_id + result["preset_dataset_type"] = attrs.preset_dataset_type + result["preset_workspace_id"] = attrs.preset_workspace_id + result["preset_workspace_qualified_name"] = attrs.preset_workspace_qualified_name + result["preset_dashboard_id"] = attrs.preset_dashboard_id + result["preset_dashboard_qualified_name"] = attrs.preset_dashboard_qualified_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _preset_dataset_to_nested(preset_dataset: PresetDataset) -> PresetDatasetNested: + """Convert flat PresetDataset to nested format.""" + attrs = PresetDatasetAttributes() + _populate_preset_dataset_attrs(attrs, preset_dataset) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + preset_dataset, _PRESET_DATASET_REL_FIELDS, PresetDatasetRelationshipAttributes + ) + return PresetDatasetNested( + guid=preset_dataset.guid, + type_name=preset_dataset.type_name, + status=preset_dataset.status, + version=preset_dataset.version, + create_time=preset_dataset.create_time, + update_time=preset_dataset.update_time, + created_by=preset_dataset.created_by, + updated_by=preset_dataset.updated_by, + classifications=preset_dataset.classifications, + classification_names=preset_dataset.classification_names, + meanings=preset_dataset.meanings, + labels=preset_dataset.labels, + business_attributes=preset_dataset.business_attributes, + custom_attributes=preset_dataset.custom_attributes, + pending_tasks=preset_dataset.pending_tasks, + proxy=preset_dataset.proxy, + is_incomplete=preset_dataset.is_incomplete, + provenance_type=preset_dataset.provenance_type, + home_id=preset_dataset.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _preset_dataset_from_nested(nested: PresetDatasetNested) -> PresetDataset: + """Convert nested format to flat PresetDataset.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else PresetDatasetAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _PRESET_DATASET_REL_FIELDS, + PresetDatasetRelationshipAttributes, + ) + return PresetDataset( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_preset_dataset_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _preset_dataset_to_nested_bytes( + preset_dataset: PresetDataset, serde: Serde +) -> bytes: + """Convert flat PresetDataset to nested JSON bytes.""" + return serde.encode(_preset_dataset_to_nested(preset_dataset)) + + +def _preset_dataset_from_nested_bytes(data: bytes, serde: Serde) -> PresetDataset: + """Convert nested JSON bytes to flat PresetDataset.""" + nested = serde.decode(data, PresetDatasetNested) + return _preset_dataset_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +PresetDataset.PRESET_DATASET_DATASOURCE_NAME = KeywordField( + "presetDatasetDatasourceName", "presetDatasetDatasourceName" +) +PresetDataset.PRESET_DATASET_ID = NumericField("presetDatasetId", "presetDatasetId") +PresetDataset.PRESET_DATASET_TYPE = KeywordField( + "presetDatasetType", "presetDatasetType" +) +PresetDataset.PRESET_WORKSPACE_ID = NumericField( + "presetWorkspaceId", "presetWorkspaceId" +) +PresetDataset.PRESET_WORKSPACE_QUALIFIED_NAME = KeywordTextField( + "presetWorkspaceQualifiedName", + "presetWorkspaceQualifiedName", + "presetWorkspaceQualifiedName.text", +) +PresetDataset.PRESET_DASHBOARD_ID = NumericField( + "presetDashboardId", "presetDashboardId" +) +PresetDataset.PRESET_DASHBOARD_QUALIFIED_NAME = KeywordTextField( + "presetDashboardQualifiedName", + "presetDashboardQualifiedName", + "presetDashboardQualifiedName.text", +) +PresetDataset.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +PresetDataset.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +PresetDataset.ANOMALO_CHECKS = RelationField("anomaloChecks") +PresetDataset.APPLICATION = RelationField("application") +PresetDataset.APPLICATION_FIELD = RelationField("applicationField") +PresetDataset.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +PresetDataset.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +PresetDataset.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +PresetDataset.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +PresetDataset.METRICS = RelationField("metrics") +PresetDataset.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +PresetDataset.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +PresetDataset.MEANINGS = RelationField("meanings") +PresetDataset.MC_MONITORS = RelationField("mcMonitors") +PresetDataset.MC_INCIDENTS = RelationField("mcIncidents") +PresetDataset.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +PresetDataset.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +PresetDataset.PRESET_DASHBOARD = RelationField("presetDashboard") +PresetDataset.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +PresetDataset.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +PresetDataset.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +PresetDataset.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +PresetDataset.FILES = RelationField("files") +PresetDataset.LINKS = RelationField("links") +PresetDataset.README = RelationField("readme") +PresetDataset.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +PresetDataset.SODA_CHECKS = RelationField("sodaChecks") +PresetDataset.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +PresetDataset.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/preset_related.py b/pyatlan_v9/model/assets/preset_related.py new file mode 100644 index 000000000..a32d9bd4b --- /dev/null +++ b/pyatlan_v9/model/assets/preset_related.py @@ -0,0 +1,179 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Preset module. + +This module contains all Related{Type} classes for the Preset type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Dict, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedBI +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedPreset", + "RelatedPresetChart", + "RelatedPresetDashboard", + "RelatedPresetDataset", + "RelatedPresetWorkspace", +] + + +class RelatedPreset(RelatedBI): + """ + Related entity reference for Preset assets. + + Extends RelatedBI with Preset-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Preset" so it serializes correctly + + preset_workspace_id: Union[int, None, UnsetType] = UNSET + """Identifier of the workspace in which this asset exists, in Preset.""" + + preset_workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace in which this asset exists.""" + + preset_dashboard_id: Union[int, None, UnsetType] = UNSET + """Identifier of the dashboard in which this asset exists, in Preset.""" + + preset_dashboard_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dashboard in which this asset exists.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Preset" + + +class RelatedPresetChart(RelatedPreset): + """ + Related entity reference for PresetChart assets. + + Extends RelatedPreset with PresetChart-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "PresetChart" so it serializes correctly + + preset_chart_description_markdown: Union[str, None, UnsetType] = UNSET + """""" + + preset_chart_form_data: Union[Dict[str, str], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "PresetChart" + + +class RelatedPresetDashboard(RelatedPreset): + """ + Related entity reference for PresetDashboard assets. + + Extends RelatedPreset with PresetDashboard-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "PresetDashboard" so it serializes correctly + + preset_dashboard_changed_by_name: Union[str, None, UnsetType] = UNSET + """""" + + preset_dashboard_changed_by_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="presetDashboardChangedByURL" + ) + """""" + + preset_dashboard_is_managed_externally: Union[bool, None, UnsetType] = UNSET + """""" + + preset_dashboard_is_published: Union[bool, None, UnsetType] = UNSET + """""" + + preset_dashboard_thumbnail_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="presetDashboardThumbnailURL" + ) + """""" + + preset_dashboard_chart_count: Union[int, None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "PresetDashboard" + + +class RelatedPresetDataset(RelatedPreset): + """ + Related entity reference for PresetDataset assets. + + Extends RelatedPreset with PresetDataset-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "PresetDataset" so it serializes correctly + + preset_dataset_datasource_name: Union[str, None, UnsetType] = UNSET + """""" + + preset_dataset_id: Union[int, None, UnsetType] = UNSET + """""" + + preset_dataset_type: Union[str, None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "PresetDataset" + + +class RelatedPresetWorkspace(RelatedPreset): + """ + Related entity reference for PresetWorkspace assets. + + Extends RelatedPreset with PresetWorkspace-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "PresetWorkspace" so it serializes correctly + + preset_workspace_public_dashboards_allowed: Union[bool, None, UnsetType] = UNSET + """""" + + preset_workspace_cluster_id: Union[int, None, UnsetType] = UNSET + """""" + + preset_workspace_deployment_id: Union[int, None, UnsetType] = UNSET + """""" + + preset_workspace_hostname: Union[str, None, UnsetType] = UNSET + """""" + + preset_workspace_is_in_maintenance_mode: Union[bool, None, UnsetType] = UNSET + """""" + + preset_workspace_region: Union[str, None, UnsetType] = UNSET + """""" + + preset_workspace_status: Union[str, None, UnsetType] = UNSET + """""" + + preset_workspace_dashboard_count: Union[int, None, UnsetType] = UNSET + """""" + + preset_workspace_dataset_count: Union[int, None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "PresetWorkspace" diff --git a/pyatlan_v9/model/assets/preset_workspace.py b/pyatlan_v9/model/assets/preset_workspace.py new file mode 100644 index 000000000..8b6d180f5 --- /dev/null +++ b/pyatlan_v9/model/assets/preset_workspace.py @@ -0,0 +1,755 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +PresetWorkspace asset model with flattened inheritance. + +This module provides: +- PresetWorkspace: Flat asset class (easy to use) +- PresetWorkspaceAttributes: Nested attributes struct (extends AssetAttributes) +- PresetWorkspaceNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .preset_related import RelatedPresetDashboard + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class PresetWorkspace(Asset): + """ + Instance of a Preset workspace in Atlan. + """ + + PRESET_WORKSPACE_PUBLIC_DASHBOARDS_ALLOWED: ClassVar[Any] = None + PRESET_WORKSPACE_CLUSTER_ID: ClassVar[Any] = None + PRESET_WORKSPACE_DEPLOYMENT_ID: ClassVar[Any] = None + PRESET_WORKSPACE_HOSTNAME: ClassVar[Any] = None + PRESET_WORKSPACE_IS_IN_MAINTENANCE_MODE: ClassVar[Any] = None + PRESET_WORKSPACE_REGION: ClassVar[Any] = None + PRESET_WORKSPACE_STATUS: ClassVar[Any] = None + PRESET_WORKSPACE_DASHBOARD_COUNT: ClassVar[Any] = None + PRESET_WORKSPACE_DATASET_COUNT: ClassVar[Any] = None + PRESET_WORKSPACE_ID: ClassVar[Any] = None + PRESET_WORKSPACE_QUALIFIED_NAME: ClassVar[Any] = None + PRESET_DASHBOARD_ID: ClassVar[Any] = None + PRESET_DASHBOARD_QUALIFIED_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + PRESET_DASHBOARDS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "PresetWorkspace" + + preset_workspace_public_dashboards_allowed: Union[bool, None, UnsetType] = UNSET + """""" + + preset_workspace_cluster_id: Union[int, None, UnsetType] = UNSET + """""" + + preset_workspace_deployment_id: Union[int, None, UnsetType] = UNSET + """""" + + preset_workspace_hostname: Union[str, None, UnsetType] = UNSET + """""" + + preset_workspace_is_in_maintenance_mode: Union[bool, None, UnsetType] = UNSET + """""" + + preset_workspace_region: Union[str, None, UnsetType] = UNSET + """""" + + preset_workspace_status: Union[str, None, UnsetType] = UNSET + """""" + + preset_workspace_dashboard_count: Union[int, None, UnsetType] = UNSET + """""" + + preset_workspace_dataset_count: Union[int, None, UnsetType] = UNSET + """""" + + preset_workspace_id: Union[int, None, UnsetType] = UNSET + """Identifier of the workspace in which this asset exists, in Preset.""" + + preset_workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace in which this asset exists.""" + + preset_dashboard_id: Union[int, None, UnsetType] = UNSET + """Identifier of the dashboard in which this asset exists, in Preset.""" + + preset_dashboard_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dashboard in which this asset exists.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + preset_dashboards: Union[List[RelatedPresetDashboard], None, UnsetType] = UNSET + """Dashboards that exist within this workspace.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "PresetWorkspace" + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + connection_qualified_name: str, + ) -> "PresetWorkspace": + validate_required_fields( + ["name", "connection_qualified_name"], + [name, connection_qualified_name], + ) + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + return cls( + name=name, + qualified_name=f"{connection_qualified_name}/{name}", + connection_qualified_name=connection_qualified_name, + connector_name=connector_name, + ) + + @classmethod + def create(cls, **kwargs) -> "PresetWorkspace": + return cls.creator(**kwargs) + + @classmethod + def create_for_modification(cls, **kwargs) -> "PresetWorkspace": + return cls.updater(**kwargs) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _preset_workspace_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> PresetWorkspace: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + PresetWorkspace instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _preset_workspace_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class PresetWorkspaceAttributes(AssetAttributes): + """PresetWorkspace-specific attributes for nested API format.""" + + preset_workspace_public_dashboards_allowed: Union[bool, None, UnsetType] = UNSET + """""" + + preset_workspace_cluster_id: Union[int, None, UnsetType] = UNSET + """""" + + preset_workspace_deployment_id: Union[int, None, UnsetType] = UNSET + """""" + + preset_workspace_hostname: Union[str, None, UnsetType] = UNSET + """""" + + preset_workspace_is_in_maintenance_mode: Union[bool, None, UnsetType] = UNSET + """""" + + preset_workspace_region: Union[str, None, UnsetType] = UNSET + """""" + + preset_workspace_status: Union[str, None, UnsetType] = UNSET + """""" + + preset_workspace_dashboard_count: Union[int, None, UnsetType] = UNSET + """""" + + preset_workspace_dataset_count: Union[int, None, UnsetType] = UNSET + """""" + + preset_workspace_id: Union[int, None, UnsetType] = UNSET + """Identifier of the workspace in which this asset exists, in Preset.""" + + preset_workspace_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workspace in which this asset exists.""" + + preset_dashboard_id: Union[int, None, UnsetType] = UNSET + """Identifier of the dashboard in which this asset exists, in Preset.""" + + preset_dashboard_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dashboard in which this asset exists.""" + + +class PresetWorkspaceRelationshipAttributes(AssetRelationshipAttributes): + """PresetWorkspace-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + preset_dashboards: Union[List[RelatedPresetDashboard], None, UnsetType] = UNSET + """Dashboards that exist within this workspace.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class PresetWorkspaceNested(AssetNested): + """PresetWorkspace in nested API format for high-performance serialization.""" + + attributes: Union[PresetWorkspaceAttributes, UnsetType] = UNSET + relationship_attributes: Union[PresetWorkspaceRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + PresetWorkspaceRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + PresetWorkspaceRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_PRESET_WORKSPACE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "preset_dashboards", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_preset_workspace_attrs( + attrs: PresetWorkspaceAttributes, obj: PresetWorkspace +) -> None: + """Populate PresetWorkspace-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.preset_workspace_public_dashboards_allowed = ( + obj.preset_workspace_public_dashboards_allowed + ) + attrs.preset_workspace_cluster_id = obj.preset_workspace_cluster_id + attrs.preset_workspace_deployment_id = obj.preset_workspace_deployment_id + attrs.preset_workspace_hostname = obj.preset_workspace_hostname + attrs.preset_workspace_is_in_maintenance_mode = ( + obj.preset_workspace_is_in_maintenance_mode + ) + attrs.preset_workspace_region = obj.preset_workspace_region + attrs.preset_workspace_status = obj.preset_workspace_status + attrs.preset_workspace_dashboard_count = obj.preset_workspace_dashboard_count + attrs.preset_workspace_dataset_count = obj.preset_workspace_dataset_count + attrs.preset_workspace_id = obj.preset_workspace_id + attrs.preset_workspace_qualified_name = obj.preset_workspace_qualified_name + attrs.preset_dashboard_id = obj.preset_dashboard_id + attrs.preset_dashboard_qualified_name = obj.preset_dashboard_qualified_name + + +def _extract_preset_workspace_attrs(attrs: PresetWorkspaceAttributes) -> dict: + """Extract all PresetWorkspace attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["preset_workspace_public_dashboards_allowed"] = ( + attrs.preset_workspace_public_dashboards_allowed + ) + result["preset_workspace_cluster_id"] = attrs.preset_workspace_cluster_id + result["preset_workspace_deployment_id"] = attrs.preset_workspace_deployment_id + result["preset_workspace_hostname"] = attrs.preset_workspace_hostname + result["preset_workspace_is_in_maintenance_mode"] = ( + attrs.preset_workspace_is_in_maintenance_mode + ) + result["preset_workspace_region"] = attrs.preset_workspace_region + result["preset_workspace_status"] = attrs.preset_workspace_status + result["preset_workspace_dashboard_count"] = attrs.preset_workspace_dashboard_count + result["preset_workspace_dataset_count"] = attrs.preset_workspace_dataset_count + result["preset_workspace_id"] = attrs.preset_workspace_id + result["preset_workspace_qualified_name"] = attrs.preset_workspace_qualified_name + result["preset_dashboard_id"] = attrs.preset_dashboard_id + result["preset_dashboard_qualified_name"] = attrs.preset_dashboard_qualified_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _preset_workspace_to_nested( + preset_workspace: PresetWorkspace, +) -> PresetWorkspaceNested: + """Convert flat PresetWorkspace to nested format.""" + attrs = PresetWorkspaceAttributes() + _populate_preset_workspace_attrs(attrs, preset_workspace) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + preset_workspace, + _PRESET_WORKSPACE_REL_FIELDS, + PresetWorkspaceRelationshipAttributes, + ) + return PresetWorkspaceNested( + guid=preset_workspace.guid, + type_name=preset_workspace.type_name, + status=preset_workspace.status, + version=preset_workspace.version, + create_time=preset_workspace.create_time, + update_time=preset_workspace.update_time, + created_by=preset_workspace.created_by, + updated_by=preset_workspace.updated_by, + classifications=preset_workspace.classifications, + classification_names=preset_workspace.classification_names, + meanings=preset_workspace.meanings, + labels=preset_workspace.labels, + business_attributes=preset_workspace.business_attributes, + custom_attributes=preset_workspace.custom_attributes, + pending_tasks=preset_workspace.pending_tasks, + proxy=preset_workspace.proxy, + is_incomplete=preset_workspace.is_incomplete, + provenance_type=preset_workspace.provenance_type, + home_id=preset_workspace.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _preset_workspace_from_nested(nested: PresetWorkspaceNested) -> PresetWorkspace: + """Convert nested format to flat PresetWorkspace.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else PresetWorkspaceAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _PRESET_WORKSPACE_REL_FIELDS, + PresetWorkspaceRelationshipAttributes, + ) + return PresetWorkspace( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_preset_workspace_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _preset_workspace_to_nested_bytes( + preset_workspace: PresetWorkspace, serde: Serde +) -> bytes: + """Convert flat PresetWorkspace to nested JSON bytes.""" + return serde.encode(_preset_workspace_to_nested(preset_workspace)) + + +def _preset_workspace_from_nested_bytes(data: bytes, serde: Serde) -> PresetWorkspace: + """Convert nested JSON bytes to flat PresetWorkspace.""" + nested = serde.decode(data, PresetWorkspaceNested) + return _preset_workspace_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +PresetWorkspace.PRESET_WORKSPACE_PUBLIC_DASHBOARDS_ALLOWED = BooleanField( + "presetWorkspacePublicDashboardsAllowed", "presetWorkspacePublicDashboardsAllowed" +) +PresetWorkspace.PRESET_WORKSPACE_CLUSTER_ID = NumericField( + "presetWorkspaceClusterId", "presetWorkspaceClusterId" +) +PresetWorkspace.PRESET_WORKSPACE_DEPLOYMENT_ID = NumericField( + "presetWorkspaceDeploymentId", "presetWorkspaceDeploymentId" +) +PresetWorkspace.PRESET_WORKSPACE_HOSTNAME = KeywordTextField( + "presetWorkspaceHostname", "presetWorkspaceHostname", "presetWorkspaceHostname.text" +) +PresetWorkspace.PRESET_WORKSPACE_IS_IN_MAINTENANCE_MODE = BooleanField( + "presetWorkspaceIsInMaintenanceMode", "presetWorkspaceIsInMaintenanceMode" +) +PresetWorkspace.PRESET_WORKSPACE_REGION = KeywordTextField( + "presetWorkspaceRegion", "presetWorkspaceRegion", "presetWorkspaceRegion.text" +) +PresetWorkspace.PRESET_WORKSPACE_STATUS = KeywordField( + "presetWorkspaceStatus", "presetWorkspaceStatus" +) +PresetWorkspace.PRESET_WORKSPACE_DASHBOARD_COUNT = NumericField( + "presetWorkspaceDashboardCount", "presetWorkspaceDashboardCount" +) +PresetWorkspace.PRESET_WORKSPACE_DATASET_COUNT = NumericField( + "presetWorkspaceDatasetCount", "presetWorkspaceDatasetCount" +) +PresetWorkspace.PRESET_WORKSPACE_ID = NumericField( + "presetWorkspaceId", "presetWorkspaceId" +) +PresetWorkspace.PRESET_WORKSPACE_QUALIFIED_NAME = KeywordTextField( + "presetWorkspaceQualifiedName", + "presetWorkspaceQualifiedName", + "presetWorkspaceQualifiedName.text", +) +PresetWorkspace.PRESET_DASHBOARD_ID = NumericField( + "presetDashboardId", "presetDashboardId" +) +PresetWorkspace.PRESET_DASHBOARD_QUALIFIED_NAME = KeywordTextField( + "presetDashboardQualifiedName", + "presetDashboardQualifiedName", + "presetDashboardQualifiedName.text", +) +PresetWorkspace.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +PresetWorkspace.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +PresetWorkspace.ANOMALO_CHECKS = RelationField("anomaloChecks") +PresetWorkspace.APPLICATION = RelationField("application") +PresetWorkspace.APPLICATION_FIELD = RelationField("applicationField") +PresetWorkspace.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +PresetWorkspace.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +PresetWorkspace.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +PresetWorkspace.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +PresetWorkspace.METRICS = RelationField("metrics") +PresetWorkspace.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +PresetWorkspace.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +PresetWorkspace.MEANINGS = RelationField("meanings") +PresetWorkspace.MC_MONITORS = RelationField("mcMonitors") +PresetWorkspace.MC_INCIDENTS = RelationField("mcIncidents") +PresetWorkspace.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +PresetWorkspace.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +PresetWorkspace.PRESET_DASHBOARDS = RelationField("presetDashboards") +PresetWorkspace.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +PresetWorkspace.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +PresetWorkspace.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +PresetWorkspace.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +PresetWorkspace.FILES = RelationField("files") +PresetWorkspace.LINKS = RelationField("links") +PresetWorkspace.README = RelationField("readme") +PresetWorkspace.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +PresetWorkspace.SODA_CHECKS = RelationField("sodaChecks") +PresetWorkspace.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +PresetWorkspace.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/procedure.py b/pyatlan_v9/model/assets/procedure.py new file mode 100644 index 000000000..d96ba5f30 --- /dev/null +++ b/pyatlan_v9/model/assets/procedure.py @@ -0,0 +1,1052 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Procedure asset model with flattened inheritance. + +This module provides: +- Procedure: Flat asset class (easy to use) +- ProcedureAttributes: Nested attributes struct (extends AssetAttributes) +- ProcedureNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .snowflake_related import RelatedSnowflakeSemanticLogicalTable +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .sql_related import RelatedSchema + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Procedure(Asset): + """ + Instance of a stored procedure (routine) in Atlan. + """ + + DEFINITION: ClassVar[Any] = None + SQL_LANGUAGE: ClassVar[Any] = None + SQL_RUNTIME_VERSION: ClassVar[Any] = None + SQL_OWNER_ROLE_TYPE: ClassVar[Any] = None + SQL_ARGUMENTS: ClassVar[Any] = None + SQL_PROCEDURE_RETURN: ClassVar[Any] = None + SQL_EXTERNAL_ACCESS_INTEGRATIONS: ClassVar[Any] = None + SQL_SECRETS: ClassVar[Any] = None + SQL_PACKAGES: ClassVar[Any] = None + SQL_INSTALLED_PACKAGES: ClassVar[Any] = None + SQL_SCHEMA_ID: ClassVar[Any] = None + SQL_CATALOG_ID: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + ATLAN_SCHEMA: ClassVar[Any] = None + SQL_PROCESSES: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Procedure" + + definition: Union[str, None, UnsetType] = UNSET + """SQL definition of the procedure.""" + + sql_language: Union[str, None, UnsetType] = UNSET + """Programming language used for the procedure (e.g., SQL, JavaScript, Python, Scala).""" + + sql_runtime_version: Union[str, None, UnsetType] = UNSET + """Version of the language runtime used by the procedure.""" + + sql_owner_role_type: Union[str, None, UnsetType] = UNSET + """Type of role that owns the procedure.""" + + sql_arguments: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of procedure arguments with name and type information.""" + + sql_procedure_return: Union[Dict[str, Any], None, UnsetType] = UNSET + """Detailed information about the procedure's return type.""" + + sql_external_access_integrations: Union[str, None, UnsetType] = UNSET + """Names of external access integrations used by the procedure.""" + + sql_secrets: Union[str, None, UnsetType] = UNSET + """Secret variables used by the procedure.""" + + sql_packages: Union[str, None, UnsetType] = UNSET + """Packages requested by the procedure.""" + + sql_installed_packages: Union[str, None, UnsetType] = UNSET + """Packages actually installed for the procedure.""" + + sql_schema_id: Union[str, None, UnsetType] = UNSET + """Internal ID for the schema containing the procedure.""" + + sql_catalog_id: Union[str, None, UnsetType] = UNSET + """Internal ID for the database containing the procedure.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + atlan_schema: Union[RelatedSchema, None, UnsetType] = UNSET + """Schema in which this stored procedure exists.""" + + sql_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes that utilize this procedure.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Procedure" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/_procedures_/[^/]+$" + ) + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + definition: str, + schema_qualified_name: str, + schema_name: str | None = None, + database_name: str | None = None, + database_qualified_name: str | None = None, + connection_qualified_name: str | None = None, + ) -> "Procedure": + validate_required_fields( + ["name", "definition", "schema_qualified_name"], + [name, definition, schema_qualified_name], + ) + + fields = schema_qualified_name.split("/") + if len(fields) != 5: + raise ValueError( + f"Invalid schema_qualified_name: {schema_qualified_name}. " + "Expected format: default/connector/connection_id/database/schema" + ) + + connector_name = fields[1] + connection_qn = ( + connection_qualified_name or f"{fields[0]}/{fields[1]}/{fields[2]}" + ) + db_name = database_name or fields[3] + sch_name = schema_name or fields[4] + db_qualified_name = database_qualified_name or f"{connection_qn}/{db_name}" + qualified_name = f"{schema_qualified_name}/_procedures_/{name}" + + return cls( + name=name, + definition=definition, + qualified_name=qualified_name, + database_name=db_name, + database_qualified_name=db_qualified_name, + schema_name=sch_name, + schema_qualified_name=schema_qualified_name, + connector_name=connector_name, + connection_qualified_name=connection_qn, + atlan_schema=RelatedSchema(qualified_name=schema_qualified_name), + ) + + @classmethod + def updater( + cls, + *, + qualified_name: str, + name: str, + definition: str = "", + ) -> "Procedure": + validate_required_fields( + ["qualified_name", "name"], + [qualified_name, name], + ) + proc = cls(qualified_name=qualified_name, name=name) + if definition: + proc.definition = definition + return proc + + def trim_to_required(self) -> "Procedure": + return Procedure.updater( + qualified_name=self.qualified_name or "", + name=self.name or "", + definition=self.definition or "", + ) + + @classmethod + def create(cls, **kwargs) -> "Procedure": + return cls.creator(**kwargs) + + @classmethod + def create_for_modification(cls, **kwargs) -> "Procedure": + return cls.updater(**kwargs) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _procedure_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Procedure: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Procedure instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _procedure_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class ProcedureAttributes(AssetAttributes): + """Procedure-specific attributes for nested API format.""" + + definition: Union[str, None, UnsetType] = UNSET + """SQL definition of the procedure.""" + + sql_language: Union[str, None, UnsetType] = UNSET + """Programming language used for the procedure (e.g., SQL, JavaScript, Python, Scala).""" + + sql_runtime_version: Union[str, None, UnsetType] = UNSET + """Version of the language runtime used by the procedure.""" + + sql_owner_role_type: Union[str, None, UnsetType] = UNSET + """Type of role that owns the procedure.""" + + sql_arguments: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of procedure arguments with name and type information.""" + + sql_procedure_return: Union[Dict[str, Any], None, UnsetType] = UNSET + """Detailed information about the procedure's return type.""" + + sql_external_access_integrations: Union[str, None, UnsetType] = UNSET + """Names of external access integrations used by the procedure.""" + + sql_secrets: Union[str, None, UnsetType] = UNSET + """Secret variables used by the procedure.""" + + sql_packages: Union[str, None, UnsetType] = UNSET + """Packages requested by the procedure.""" + + sql_installed_packages: Union[str, None, UnsetType] = UNSET + """Packages actually installed for the procedure.""" + + sql_schema_id: Union[str, None, UnsetType] = UNSET + """Internal ID for the schema containing the procedure.""" + + sql_catalog_id: Union[str, None, UnsetType] = UNSET + """Internal ID for the database containing the procedure.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + +class ProcedureRelationshipAttributes(AssetRelationshipAttributes): + """Procedure-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + atlan_schema: Union[RelatedSchema, None, UnsetType] = UNSET + """Schema in which this stored procedure exists.""" + + sql_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes that utilize this procedure.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class ProcedureNested(AssetNested): + """Procedure in nested API format for high-performance serialization.""" + + attributes: Union[ProcedureAttributes, UnsetType] = UNSET + relationship_attributes: Union[ProcedureRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + ProcedureRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + ProcedureRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_PROCEDURE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "atlan_schema", + "sql_processes", + "schema_registry_subjects", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_procedure_attrs(attrs: ProcedureAttributes, obj: Procedure) -> None: + """Populate Procedure-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.definition = obj.definition + attrs.sql_language = obj.sql_language + attrs.sql_runtime_version = obj.sql_runtime_version + attrs.sql_owner_role_type = obj.sql_owner_role_type + attrs.sql_arguments = obj.sql_arguments + attrs.sql_procedure_return = obj.sql_procedure_return + attrs.sql_external_access_integrations = obj.sql_external_access_integrations + attrs.sql_secrets = obj.sql_secrets + attrs.sql_packages = obj.sql_packages + attrs.sql_installed_packages = obj.sql_installed_packages + attrs.sql_schema_id = obj.sql_schema_id + attrs.sql_catalog_id = obj.sql_catalog_id + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + + +def _extract_procedure_attrs(attrs: ProcedureAttributes) -> dict: + """Extract all Procedure attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["definition"] = attrs.definition + result["sql_language"] = attrs.sql_language + result["sql_runtime_version"] = attrs.sql_runtime_version + result["sql_owner_role_type"] = attrs.sql_owner_role_type + result["sql_arguments"] = attrs.sql_arguments + result["sql_procedure_return"] = attrs.sql_procedure_return + result["sql_external_access_integrations"] = attrs.sql_external_access_integrations + result["sql_secrets"] = attrs.sql_secrets + result["sql_packages"] = attrs.sql_packages + result["sql_installed_packages"] = attrs.sql_installed_packages + result["sql_schema_id"] = attrs.sql_schema_id + result["sql_catalog_id"] = attrs.sql_catalog_id + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _procedure_to_nested(procedure: Procedure) -> ProcedureNested: + """Convert flat Procedure to nested format.""" + attrs = ProcedureAttributes() + _populate_procedure_attrs(attrs, procedure) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + procedure, _PROCEDURE_REL_FIELDS, ProcedureRelationshipAttributes + ) + return ProcedureNested( + guid=procedure.guid, + type_name=procedure.type_name, + status=procedure.status, + version=procedure.version, + create_time=procedure.create_time, + update_time=procedure.update_time, + created_by=procedure.created_by, + updated_by=procedure.updated_by, + classifications=procedure.classifications, + classification_names=procedure.classification_names, + meanings=procedure.meanings, + labels=procedure.labels, + business_attributes=procedure.business_attributes, + custom_attributes=procedure.custom_attributes, + pending_tasks=procedure.pending_tasks, + proxy=procedure.proxy, + is_incomplete=procedure.is_incomplete, + provenance_type=procedure.provenance_type, + home_id=procedure.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _procedure_from_nested(nested: ProcedureNested) -> Procedure: + """Convert nested format to flat Procedure.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else ProcedureAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _PROCEDURE_REL_FIELDS, + ProcedureRelationshipAttributes, + ) + return Procedure( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_procedure_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _procedure_to_nested_bytes(procedure: Procedure, serde: Serde) -> bytes: + """Convert flat Procedure to nested JSON bytes.""" + return serde.encode(_procedure_to_nested(procedure)) + + +def _procedure_from_nested_bytes(data: bytes, serde: Serde) -> Procedure: + """Convert nested JSON bytes to flat Procedure.""" + nested = serde.decode(data, ProcedureNested) + return _procedure_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +Procedure.DEFINITION = KeywordField("definition", "definition") +Procedure.SQL_LANGUAGE = KeywordTextField( + "sqlLanguage", "sqlLanguage", "sqlLanguage.text" +) +Procedure.SQL_RUNTIME_VERSION = KeywordTextField( + "sqlRuntimeVersion", "sqlRuntimeVersion", "sqlRuntimeVersion.text" +) +Procedure.SQL_OWNER_ROLE_TYPE = KeywordTextField( + "sqlOwnerRoleType", "sqlOwnerRoleType", "sqlOwnerRoleType.text" +) +Procedure.SQL_ARGUMENTS = KeywordField("sqlArguments", "sqlArguments") +Procedure.SQL_PROCEDURE_RETURN = KeywordField( + "sqlProcedureReturn", "sqlProcedureReturn" +) +Procedure.SQL_EXTERNAL_ACCESS_INTEGRATIONS = KeywordField( + "sqlExternalAccessIntegrations", "sqlExternalAccessIntegrations" +) +Procedure.SQL_SECRETS = KeywordField("sqlSecrets", "sqlSecrets") +Procedure.SQL_PACKAGES = KeywordField("sqlPackages", "sqlPackages") +Procedure.SQL_INSTALLED_PACKAGES = KeywordField( + "sqlInstalledPackages", "sqlInstalledPackages" +) +Procedure.SQL_SCHEMA_ID = KeywordField("sqlSchemaId", "sqlSchemaId") +Procedure.SQL_CATALOG_ID = KeywordField("sqlCatalogId", "sqlCatalogId") +Procedure.QUERY_COUNT = NumericField("queryCount", "queryCount") +Procedure.QUERY_USER_COUNT = NumericField("queryUserCount", "queryUserCount") +Procedure.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +Procedure.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +Procedure.DATABASE_NAME = KeywordField("databaseName", "databaseName") +Procedure.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +Procedure.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +Procedure.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +Procedure.TABLE_NAME = KeywordField("tableName", "tableName") +Procedure.TABLE_QUALIFIED_NAME = KeywordField( + "tableQualifiedName", "tableQualifiedName" +) +Procedure.VIEW_NAME = KeywordField("viewName", "viewName") +Procedure.VIEW_QUALIFIED_NAME = KeywordField("viewQualifiedName", "viewQualifiedName") +Procedure.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +Procedure.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +Procedure.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +Procedure.LAST_PROFILED_AT = NumericField("lastProfiledAt", "lastProfiledAt") +Procedure.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +Procedure.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +Procedure.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Procedure.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Procedure.ANOMALO_CHECKS = RelationField("anomaloChecks") +Procedure.APPLICATION = RelationField("application") +Procedure.APPLICATION_FIELD = RelationField("applicationField") +Procedure.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Procedure.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Procedure.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Procedure.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Procedure.METRICS = RelationField("metrics") +Procedure.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Procedure.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Procedure.DBT_MODELS = RelationField("dbtModels") +Procedure.SQL_DBT_MODELS = RelationField("sqlDbtModels") +Procedure.DBT_TESTS = RelationField("dbtTests") +Procedure.DBT_SOURCES = RelationField("dbtSources") +Procedure.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +Procedure.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +Procedure.MEANINGS = RelationField("meanings") +Procedure.MC_MONITORS = RelationField("mcMonitors") +Procedure.MC_INCIDENTS = RelationField("mcIncidents") +Procedure.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Procedure.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Procedure.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Procedure.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Procedure.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Procedure.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Procedure.FILES = RelationField("files") +Procedure.LINKS = RelationField("links") +Procedure.README = RelationField("readme") +Procedure.ATLAN_SCHEMA = RelationField("atlanSchema") +Procedure.SQL_PROCESSES = RelationField("sqlProcesses") +Procedure.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Procedure.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +Procedure.SODA_CHECKS = RelationField("sodaChecks") +Procedure.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Procedure.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/process.py b/pyatlan_v9/model/assets/process.py new file mode 100644 index 000000000..ef3bcfdf8 --- /dev/null +++ b/pyatlan_v9/model/assets/process.py @@ -0,0 +1,750 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Process asset model with flattened inheritance. + +This module provides: +- Process: Flat asset class (easy to use) +- ProcessAttributes: Nested attributes struct (extends AssetAttributes) +- ProcessNested: Nested API format struct +""" + +from __future__ import annotations + +import hashlib +from io import StringIO +from typing import Any, ClassVar, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .adf_related import RelatedAdfActivity +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .catalog_related import RelatedCatalog +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .fabric_related import RelatedFabricActivity +from .fivetran_related import RelatedFivetranConnector +from .flow_related import RelatedFlowControlOperation +from .gtc_related import RelatedAtlasGlossaryTerm +from .matillion_related import RelatedMatillionComponent +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .power_bi_related import RelatedPowerBIDataflow +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from .sql_related import RelatedFunction, RelatedProcedure +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .process_related import RelatedColumnProcess + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Process(Asset): + """ + Instance of a lineage process in Atlan. + """ + + CODE: ClassVar[Any] = None + SQL: ClassVar[Any] = None + PARENT_CONNECTION_PROCESS_QUALIFIED_NAME: ClassVar[Any] = None + AST: ClassVar[Any] = None + ADDITIONAL_ETL_CONTEXT: ClassVar[Any] = None + AI_DATASET_TYPE: ClassVar[Any] = None + ADF_ACTIVITY: ClassVar[Any] = None + AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + FABRIC_ACTIVITIES: ClassVar[Any] = None + FIVETRAN_CONNECTOR: ClassVar[Any] = None + FLOW_ORCHESTRATED_BY: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MATILLION_COMPONENT: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + POWER_BI_DATAFLOW: ClassVar[Any] = None + INPUTS: ClassVar[Any] = None + OUTPUTS: ClassVar[Any] = None + COLUMN_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SQL_PROCEDURES: ClassVar[Any] = None + SQL_FUNCTIONS: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Process" + + code: Union[str, None, UnsetType] = UNSET + """Code that ran within the process.""" + + sql: Union[str, None, UnsetType] = UNSET + """SQL query that ran to produce the outputs.""" + + parent_connection_process_qualified_name: Union[List[str], None, UnsetType] = UNSET + """""" + + ast: Union[str, None, UnsetType] = UNSET + """Parsed AST of the code or SQL statements that describe the logic of this process.""" + + additional_etl_context: Union[str, None, UnsetType] = UNSET + """Additional Context of the ETL pipeline/notebook which creates the process.""" + + ai_dataset_type: Union[str, None, UnsetType] = UNSET + """Dataset type for AI Model - dataset process.""" + + adf_activity: Union[RelatedAdfActivity, None, UnsetType] = UNSET + """ADF Activity that is associated with this lineage process.""" + + airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks that exist within this process.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + fabric_activities: Union[List[RelatedFabricActivity], None, UnsetType] = UNSET + """Individual Fabric activities contained in the process.""" + + fivetran_connector: Union[RelatedFivetranConnector, None, UnsetType] = UNSET + """fivetranConnector in which this process exists.""" + + flow_orchestrated_by: Union[RelatedFlowControlOperation, None, UnsetType] = UNSET + """Orchestrated control operation that ran these data flows (process).""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + matillion_component: Union[RelatedMatillionComponent, None, UnsetType] = UNSET + """Matillion component that contains the logic for this lineage process.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + power_bi_dataflow: Union[RelatedPowerBIDataflow, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIDataflow" + ) + """PowerBI Dataflow that is associated with this lineage process.""" + + inputs: Union[List[RelatedCatalog], None, UnsetType] = UNSET + """Assets that are inputs to this process.""" + + outputs: Union[List[RelatedCatalog], None, UnsetType] = UNSET + """Assets that are outputs from this process.""" + + column_processes: Union[List[RelatedColumnProcess], None, UnsetType] = UNSET + """Processes that detail column-level lineage for this process.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + sql_procedures: Union[List[RelatedProcedure], None, UnsetType] = UNSET + """Procedures used by this process.""" + + sql_functions: Union[List[RelatedFunction], None, UnsetType] = UNSET + """Functions used by this process.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Process" + + @staticmethod + def _extract_guid(relationship: Any) -> Union[str, None]: + """Extract guid from a relationship-like object.""" + if relationship is None: + return None + guid = getattr(relationship, "guid", UNSET) + if guid is UNSET or not guid: + return None + return guid + + @staticmethod + def generate_qualified_name( + *, + name: str, + connection_qualified_name: str, + inputs: list[Any], + outputs: list[Any], + parent: Union[Any, None] = None, + process_id: Union[str, None] = None, + extra_hash_params: Union[set[str], None] = None, + ) -> str: + """Generate process qualified name using explicit process_id or deterministic hash.""" + validate_required_fields( + ["name", "connection_qualified_name", "inputs", "outputs"], + [name, connection_qualified_name, inputs, outputs], + ) + if process_id and process_id.strip(): + return f"{connection_qualified_name}/{process_id}" + buffer = StringIO() + buffer.write(name) + buffer.write(connection_qualified_name) + parent_guid = Process._extract_guid(parent) + if parent_guid: + buffer.write(parent_guid) + for relationship in inputs: + guid = Process._extract_guid(relationship) + if guid: + buffer.write(guid) + for relationship in outputs: + guid = Process._extract_guid(relationship) + if guid: + buffer.write(guid) + if extra_hash_params: + for param in extra_hash_params: + buffer.write(param) + hash_seed = buffer.getvalue() + buffer.close() + # deepcode ignore InsecureHash/test: this is not used for generating security keys + return ( + f"{connection_qualified_name}/{hashlib.md5(hash_seed.encode()).hexdigest()}" # noqa: S324 + ) + + @staticmethod + def _to_related_catalog(value: Any) -> RelatedCatalog: + """Convert any relationship-like value to a RelatedCatalog reference.""" + if isinstance(value, RelatedCatalog): + return value + guid = getattr(value, "guid", UNSET) + type_name = getattr(value, "type_name", UNSET) + if guid is not UNSET and guid: + kwargs: dict[str, Any] = {"guid": guid} + if type_name is not UNSET and type_name: + kwargs["type_name"] = type_name + return RelatedCatalog(**kwargs) + qualified_name = getattr(value, "qualified_name", UNSET) + if qualified_name is not UNSET and qualified_name: + kwargs = {"unique_attributes": {"qualifiedName": qualified_name}} + if type_name is not UNSET and type_name: + kwargs["type_name"] = type_name + return RelatedCatalog(**kwargs) + return RelatedCatalog() + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + connection_qualified_name: str, + inputs: list[Any], + outputs: list[Any], + process_id: Union[str, None] = None, + parent: Union[Any, None] = None, + extra_hash_params: Union[set[str], None] = None, + ) -> "Process": + """Create a new Process asset.""" + qualified_name = cls.generate_qualified_name( + name=name, + connection_qualified_name=connection_qualified_name, + process_id=process_id, + inputs=inputs, + outputs=outputs, + parent=parent, + extra_hash_params=extra_hash_params, + ) + connector_name = ( + connection_qualified_name.split("/")[1] + if len(connection_qualified_name.split("/")) > 1 + else "" + ) + return cls( + name=name, + qualified_name=qualified_name, + connector_name=connector_name, + connection_qualified_name=connection_qualified_name, + inputs=[cls._to_related_catalog(item) for item in inputs], + outputs=[cls._to_related_catalog(item) for item in outputs], + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "Process": + """Create a Process instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "Process": + """Return only fields required for update operations.""" + return Process.updater(qualified_name=self.qualified_name, name=self.name) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _process_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Process: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Process instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _process_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class ProcessAttributes(AssetAttributes): + """Process-specific attributes for nested API format.""" + + code: Union[str, None, UnsetType] = UNSET + """Code that ran within the process.""" + + sql: Union[str, None, UnsetType] = UNSET + """SQL query that ran to produce the outputs.""" + + parent_connection_process_qualified_name: Union[List[str], None, UnsetType] = UNSET + """""" + + ast: Union[str, None, UnsetType] = UNSET + """Parsed AST of the code or SQL statements that describe the logic of this process.""" + + additional_etl_context: Union[str, None, UnsetType] = UNSET + """Additional Context of the ETL pipeline/notebook which creates the process.""" + + ai_dataset_type: Union[str, None, UnsetType] = UNSET + """Dataset type for AI Model - dataset process.""" + + +class ProcessRelationshipAttributes(AssetRelationshipAttributes): + """Process-specific relationship attributes for nested API format.""" + + adf_activity: Union[RelatedAdfActivity, None, UnsetType] = UNSET + """ADF Activity that is associated with this lineage process.""" + + airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks that exist within this process.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + fabric_activities: Union[List[RelatedFabricActivity], None, UnsetType] = UNSET + """Individual Fabric activities contained in the process.""" + + fivetran_connector: Union[RelatedFivetranConnector, None, UnsetType] = UNSET + """fivetranConnector in which this process exists.""" + + flow_orchestrated_by: Union[RelatedFlowControlOperation, None, UnsetType] = UNSET + """Orchestrated control operation that ran these data flows (process).""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + matillion_component: Union[RelatedMatillionComponent, None, UnsetType] = UNSET + """Matillion component that contains the logic for this lineage process.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + power_bi_dataflow: Union[RelatedPowerBIDataflow, None, UnsetType] = msgspec.field( + default=UNSET, name="powerBIDataflow" + ) + """PowerBI Dataflow that is associated with this lineage process.""" + + inputs: Union[List[RelatedCatalog], None, UnsetType] = UNSET + """Assets that are inputs to this process.""" + + outputs: Union[List[RelatedCatalog], None, UnsetType] = UNSET + """Assets that are outputs from this process.""" + + column_processes: Union[List[RelatedColumnProcess], None, UnsetType] = UNSET + """Processes that detail column-level lineage for this process.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + sql_procedures: Union[List[RelatedProcedure], None, UnsetType] = UNSET + """Procedures used by this process.""" + + sql_functions: Union[List[RelatedFunction], None, UnsetType] = UNSET + """Functions used by this process.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class ProcessNested(AssetNested): + """Process in nested API format for high-performance serialization.""" + + attributes: Union[ProcessAttributes, UnsetType] = UNSET + relationship_attributes: Union[ProcessRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ProcessRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[ProcessRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_PROCESS_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "adf_activity", + "airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "fabric_activities", + "fivetran_connector", + "flow_orchestrated_by", + "meanings", + "matillion_component", + "mc_monitors", + "mc_incidents", + "power_bi_dataflow", + "inputs", + "outputs", + "column_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "sql_procedures", + "sql_functions", + "schema_registry_subjects", + "soda_checks", + "spark_jobs", +] + + +def _populate_process_attrs(attrs: ProcessAttributes, obj: Process) -> None: + """Populate Process-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.code = obj.code + attrs.sql = obj.sql + attrs.parent_connection_process_qualified_name = ( + obj.parent_connection_process_qualified_name + ) + attrs.ast = obj.ast + attrs.additional_etl_context = obj.additional_etl_context + attrs.ai_dataset_type = obj.ai_dataset_type + + +def _extract_process_attrs(attrs: ProcessAttributes) -> dict: + """Extract all Process attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["code"] = attrs.code + result["sql"] = attrs.sql + result["parent_connection_process_qualified_name"] = ( + attrs.parent_connection_process_qualified_name + ) + result["ast"] = attrs.ast + result["additional_etl_context"] = attrs.additional_etl_context + result["ai_dataset_type"] = attrs.ai_dataset_type + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _process_to_nested(process: Process) -> ProcessNested: + """Convert flat Process to nested format.""" + attrs = ProcessAttributes() + _populate_process_attrs(attrs, process) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + process, _PROCESS_REL_FIELDS, ProcessRelationshipAttributes + ) + return ProcessNested( + guid=process.guid, + type_name=process.type_name, + status=process.status, + version=process.version, + create_time=process.create_time, + update_time=process.update_time, + created_by=process.created_by, + updated_by=process.updated_by, + classifications=process.classifications, + classification_names=process.classification_names, + meanings=process.meanings, + labels=process.labels, + business_attributes=process.business_attributes, + custom_attributes=process.custom_attributes, + pending_tasks=process.pending_tasks, + proxy=process.proxy, + is_incomplete=process.is_incomplete, + provenance_type=process.provenance_type, + home_id=process.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _process_from_nested(nested: ProcessNested) -> Process: + """Convert nested format to flat Process.""" + attrs = nested.attributes if nested.attributes is not UNSET else ProcessAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _PROCESS_REL_FIELDS, + ProcessRelationshipAttributes, + ) + return Process( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_process_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _process_to_nested_bytes(process: Process, serde: Serde) -> bytes: + """Convert flat Process to nested JSON bytes.""" + return serde.encode(_process_to_nested(process)) + + +def _process_from_nested_bytes(data: bytes, serde: Serde) -> Process: + """Convert nested JSON bytes to flat Process.""" + nested = serde.decode(data, ProcessNested) + return _process_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +Process.CODE = KeywordField("code", "code") +Process.SQL = KeywordField("sql", "sql") +Process.PARENT_CONNECTION_PROCESS_QUALIFIED_NAME = KeywordField( + "parentConnectionProcessQualifiedName", "parentConnectionProcessQualifiedName" +) +Process.AST = KeywordField("ast", "ast") +Process.ADDITIONAL_ETL_CONTEXT = KeywordField( + "additionalEtlContext", "additionalEtlContext" +) +Process.AI_DATASET_TYPE = KeywordField("aiDatasetType", "aiDatasetType") +Process.ADF_ACTIVITY = RelationField("adfActivity") +Process.AIRFLOW_TASKS = RelationField("airflowTasks") +Process.ANOMALO_CHECKS = RelationField("anomaloChecks") +Process.APPLICATION = RelationField("application") +Process.APPLICATION_FIELD = RelationField("applicationField") +Process.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Process.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Process.METRICS = RelationField("metrics") +Process.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Process.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Process.FABRIC_ACTIVITIES = RelationField("fabricActivities") +Process.FIVETRAN_CONNECTOR = RelationField("fivetranConnector") +Process.FLOW_ORCHESTRATED_BY = RelationField("flowOrchestratedBy") +Process.MEANINGS = RelationField("meanings") +Process.MATILLION_COMPONENT = RelationField("matillionComponent") +Process.MC_MONITORS = RelationField("mcMonitors") +Process.MC_INCIDENTS = RelationField("mcIncidents") +Process.POWER_BI_DATAFLOW = RelationField("powerBIDataflow") +Process.INPUTS = RelationField("inputs") +Process.OUTPUTS = RelationField("outputs") +Process.COLUMN_PROCESSES = RelationField("columnProcesses") +Process.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Process.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Process.FILES = RelationField("files") +Process.LINKS = RelationField("links") +Process.README = RelationField("readme") +Process.SQL_PROCEDURES = RelationField("sqlProcedures") +Process.SQL_FUNCTIONS = RelationField("sqlFunctions") +Process.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Process.SODA_CHECKS = RelationField("sodaChecks") +Process.SPARK_JOBS = RelationField("sparkJobs") diff --git a/pyatlan_v9/model/assets/process_execution.py b/pyatlan_v9/model/assets/process_execution.py new file mode 100644 index 000000000..68d9c3fc1 --- /dev/null +++ b/pyatlan_v9/model/assets/process_execution.py @@ -0,0 +1,2961 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +ProcessExecution asset model with flattened inheritance. + +This module provides: +- ProcessExecution: Flat asset class (easy to use) +- ProcessExecutionAttributes: Nested attributes struct (extends AssetAttributes) +- ProcessExecutionNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Set, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .referenceable import ( + _REFERENCEABLE_REL_FIELDS, + Referenceable, + ReferenceableAttributes, + ReferenceableNested, + ReferenceableRelationshipAttributes, + _extract_referenceable_attrs, + _populate_referenceable_attrs, +) +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +class ProcessExecution(Referenceable): + """ """ + + NAME: ClassVar[Any] = None + DISPLAY_NAME: ClassVar[Any] = None + DESCRIPTION: ClassVar[Any] = None + ASSET_SOURCE_README: ClassVar[Any] = None + USER_DESCRIPTION: ClassVar[Any] = None + ASSET_AI_GENERATED_DESCRIPTION: ClassVar[Any] = None + ASSET_AI_GENERATED_DESCRIPTION_CONFIDENCE: ClassVar[Any] = None + ASSET_AI_GENERATED_DESCRIPTION_REASONING: ClassVar[Any] = None + TENANT_ID: ClassVar[Any] = None + CERTIFICATE_STATUS: ClassVar[Any] = None + CERTIFICATE_STATUS_MESSAGE: ClassVar[Any] = None + CERTIFICATE_UPDATED_BY: ClassVar[Any] = None + CERTIFICATE_UPDATED_AT: ClassVar[Any] = None + ANNOUNCEMENT_TITLE: ClassVar[Any] = None + ANNOUNCEMENT_MESSAGE: ClassVar[Any] = None + ANNOUNCEMENT_TYPE: ClassVar[Any] = None + ANNOUNCEMENT_UPDATED_AT: ClassVar[Any] = None + ANNOUNCEMENT_UPDATED_BY: ClassVar[Any] = None + OWNER_USERS: ClassVar[Any] = None + OWNER_GROUPS: ClassVar[Any] = None + ADMIN_USERS: ClassVar[Any] = None + ADMIN_GROUPS: ClassVar[Any] = None + VIEWER_USERS: ClassVar[Any] = None + VIEWER_GROUPS: ClassVar[Any] = None + CONNECTOR_NAME: ClassVar[Any] = None + CONNECTION_NAME: ClassVar[Any] = None + CONNECTION_QUALIFIED_NAME: ClassVar[Any] = None + HAS_LINEAGE: ClassVar[Any] = None + IS_DISCOVERABLE: ClassVar[Any] = None + IS_EDITABLE: ClassVar[Any] = None + SUB_TYPE: ClassVar[Any] = None + VIEW_SCORE: ClassVar[Any] = None + POPULARITY_SCORE: ClassVar[Any] = None + SOURCE_OWNERS: ClassVar[Any] = None + ASSET_SOURCE_ID: ClassVar[Any] = None + SOURCE_CREATED_BY: ClassVar[Any] = None + SOURCE_CREATED_AT: ClassVar[Any] = None + SOURCE_UPDATED_AT: ClassVar[Any] = None + SOURCE_UPDATED_BY: ClassVar[Any] = None + SOURCE_URL: ClassVar[Any] = None + SOURCE_EMBED_URL: ClassVar[Any] = None + LAST_SYNC_WORKFLOW_NAME: ClassVar[Any] = None + LAST_SYNC_RUN_AT: ClassVar[Any] = None + LAST_SYNC_RUN: ClassVar[Any] = None + ADMIN_ROLES: ClassVar[Any] = None + SOURCE_READ_COUNT: ClassVar[Any] = None + SOURCE_READ_USER_COUNT: ClassVar[Any] = None + SOURCE_LAST_READ_AT: ClassVar[Any] = None + LAST_ROW_CHANGED_AT: ClassVar[Any] = None + SOURCE_TOTAL_COST: ClassVar[Any] = None + SOURCE_COST_UNIT: ClassVar[Any] = None + SOURCE_READ_QUERY_COST: ClassVar[Any] = None + SOURCE_READ_RECENT_USER_LIST: ClassVar[Any] = None + SOURCE_READ_RECENT_USER_RECORD_LIST: ClassVar[Any] = None + SOURCE_READ_TOP_USER_LIST: ClassVar[Any] = None + SOURCE_READ_TOP_USER_RECORD_LIST: ClassVar[Any] = None + SOURCE_READ_POPULAR_QUERY_RECORD_LIST: ClassVar[Any] = None + SOURCE_READ_EXPENSIVE_QUERY_RECORD_LIST: ClassVar[Any] = None + SOURCE_READ_SLOW_QUERY_RECORD_LIST: ClassVar[Any] = None + SOURCE_QUERY_COMPUTE_COST_LIST: ClassVar[Any] = None + SOURCE_QUERY_COMPUTE_COST_RECORD_LIST: ClassVar[Any] = None + DBT_QUALIFIED_NAME: ClassVar[Any] = None + ASSET_DBT_WORKFLOW_LAST_UPDATED: ClassVar[Any] = None + ASSET_DBT_ALIAS: ClassVar[Any] = None + ASSET_DBT_META: ClassVar[Any] = None + ASSET_DBT_UNIQUE_ID: ClassVar[Any] = None + ASSET_DBT_ACCOUNT_NAME: ClassVar[Any] = None + ASSET_DBT_PROJECT_NAME: ClassVar[Any] = None + ASSET_DBT_PACKAGE_NAME: ClassVar[Any] = None + ASSET_DBT_JOB_NAME: ClassVar[Any] = None + ASSET_DBT_JOB_SCHEDULE: ClassVar[Any] = None + ASSET_DBT_JOB_STATUS: ClassVar[Any] = None + ASSET_DBT_TEST_STATUS: ClassVar[Any] = None + ASSET_DBT_JOB_SCHEDULE_CRON_HUMANIZED: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_URL: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_CREATED_AT: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_UPDATED_AT: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_DEQUED_AT: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_STARTED_AT: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_TOTAL_DURATION: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_TOTAL_DURATION_HUMANIZED: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_QUEUED_DURATION: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_QUEUED_DURATION_HUMANIZED: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_RUN_DURATION: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_RUN_DURATION_HUMANIZED: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_GIT_BRANCH: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_GIT_SHA: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_STATUS_MESSAGE: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_OWNER_THREAD_ID: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_EXECUTED_BY_THREAD_ID: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_ARTIFACTS_SAVED: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_ARTIFACT_S3_PATH: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_HAS_DOCS_GENERATED: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_HAS_SOURCES_GENERATED: ClassVar[Any] = None + ASSET_DBT_JOB_LAST_RUN_NOTIFICATIONS_SENT: ClassVar[Any] = None + ASSET_DBT_JOB_NEXT_RUN: ClassVar[Any] = None + ASSET_DBT_JOB_NEXT_RUN_HUMANIZED: ClassVar[Any] = None + ASSET_DBT_ENVIRONMENT_NAME: ClassVar[Any] = None + ASSET_DBT_ENVIRONMENT_DBT_VERSION: ClassVar[Any] = None + ASSET_DBT_TAGS: ClassVar[Any] = None + ASSET_DBT_SEMANTIC_LAYER_PROXY_URL: ClassVar[Any] = None + ASSET_DBT_SOURCE_FRESHNESS_CRITERIA: ClassVar[Any] = None + SAMPLE_DATA_URL: ClassVar[Any] = None + ASSET_TAGS: ClassVar[Any] = None + ASSET_MC_INCIDENT_NAMES: ClassVar[Any] = None + ASSET_MC_INCIDENT_QUALIFIED_NAMES: ClassVar[Any] = None + ASSET_MC_ALERT_QUALIFIED_NAMES: ClassVar[Any] = None + ASSET_MC_MONITOR_NAMES: ClassVar[Any] = None + ASSET_MC_MONITOR_QUALIFIED_NAMES: ClassVar[Any] = None + ASSET_MC_MONITOR_STATUSES: ClassVar[Any] = None + ASSET_MC_MONITOR_TYPES: ClassVar[Any] = None + ASSET_MC_MONITOR_SCHEDULE_TYPES: ClassVar[Any] = None + ASSET_MC_INCIDENT_TYPES: ClassVar[Any] = None + ASSET_MC_INCIDENT_SUB_TYPES: ClassVar[Any] = None + ASSET_MC_INCIDENT_SEVERITIES: ClassVar[Any] = None + ASSET_MC_INCIDENT_PRIORITIES: ClassVar[Any] = None + ASSET_MC_INCIDENT_STATES: ClassVar[Any] = None + ASSET_MC_IS_MONITORED: ClassVar[Any] = None + ASSET_MC_LAST_SYNC_RUN_AT: ClassVar[Any] = None + STARRED_BY: ClassVar[Any] = None + STARRED_DETAILS_LIST: ClassVar[Any] = None + STARRED_COUNT: ClassVar[Any] = None + ASSET_ANOMALO_DQ_STATUS: ClassVar[Any] = None + ASSET_ANOMALO_CHECK_COUNT: ClassVar[Any] = None + ASSET_ANOMALO_FAILED_CHECK_COUNT: ClassVar[Any] = None + ASSET_ANOMALO_CHECK_STATUSES: ClassVar[Any] = None + ASSET_ANOMALO_LAST_CHECK_RUN_AT: ClassVar[Any] = None + ASSET_ANOMALO_APPLIED_CHECK_TYPES: ClassVar[Any] = None + ASSET_ANOMALO_FAILED_CHECK_TYPES: ClassVar[Any] = None + ASSET_ANOMALO_SOURCE_URL: ClassVar[Any] = None + ASSET_SODA_DQ_STATUS: ClassVar[Any] = None + ASSET_SODA_CHECK_COUNT: ClassVar[Any] = None + ASSET_SODA_LAST_SYNC_RUN_AT: ClassVar[Any] = None + ASSET_SODA_LAST_SCAN_AT: ClassVar[Any] = None + ASSET_SODA_CHECK_STATUSES: ClassVar[Any] = None + ASSET_SODA_SOURCE_URL: ClassVar[Any] = None + ASSET_ICON: ClassVar[Any] = None + ASSET_EXTERNAL_DQ_METADATA_DETAILS: ClassVar[Any] = None + IS_PARTIAL: ClassVar[Any] = None + IS_AI_GENERATED: ClassVar[Any] = None + ASSET_COVER_IMAGE: ClassVar[Any] = None + ASSET_THEME_HEX: ClassVar[Any] = None + LEXICOGRAPHICAL_SORT_ORDER: ClassVar[Any] = None + HAS_CONTRACT: ClassVar[Any] = None + ASSET_REDIRECT_GUIDS: ClassVar[Any] = None + ASSET_POLICY_GUIDS: ClassVar[Any] = None + ASSET_POLICIES_COUNT: ClassVar[Any] = None + DOMAIN_GUIDS: ClassVar[Any] = None + NON_COMPLIANT_ASSET_POLICY_GUIDS: ClassVar[Any] = None + PRODUCT_GUIDS: ClassVar[Any] = None + OUTPUT_PRODUCT_GUIDS: ClassVar[Any] = None + APPLICATION_QUALIFIED_NAME: ClassVar[Any] = None + APPLICATION_FIELD_QUALIFIED_NAME: ClassVar[Any] = None + ASSET_USER_DEFINED_TYPE: ClassVar[Any] = None + ASSET_INTERNAL_POPULARITY_SCORE: ClassVar[Any] = None + ASSET_DQ_SCHEDULE_TYPE: ClassVar[Any] = None + ASSET_DQ_SCHEDULE_CRONTAB: ClassVar[Any] = None + ASSET_DQ_SCHEDULE_TIME_ZONE: ClassVar[Any] = None + ASSET_DQ_SCHEDULE_SOURCE_SYNC_STATUS: ClassVar[Any] = None + ASSET_DQ_SCHEDULE_SOURCE_SYNCED_AT: ClassVar[Any] = None + ASSET_DQ_SCHEDULE_SOURCE_SYNC_ERROR_MESSAGE: ClassVar[Any] = None + ASSET_DQ_SCHEDULE_SOURCE_SYNC_ERROR_CODE: ClassVar[Any] = None + ASSET_DQ_SCHEDULE_SOURCE_SYNC_RAW_ERROR: ClassVar[Any] = None + ASSET_DQ_RULE_ATTACHED_DIMENSIONS: ClassVar[Any] = None + ASSET_DQ_RULE_FAILED_DIMENSIONS: ClassVar[Any] = None + ASSET_DQ_RULE_PASSED_DIMENSIONS: ClassVar[Any] = None + ASSET_DQ_RULE_ATTACHED_RULE_TYPES: ClassVar[Any] = None + ASSET_DQ_RULE_FAILED_RULE_TYPES: ClassVar[Any] = None + ASSET_DQ_RULE_PASSED_RULE_TYPES: ClassVar[Any] = None + ASSET_DQ_RULE_RESULT_TAGS: ClassVar[Any] = None + ASSET_DQ_RULE_LAST_RUN_AT: ClassVar[Any] = None + ASSET_DQ_MANUAL_RUN_STATUS: ClassVar[Any] = None + ASSET_DQ_RULE_TOTAL_COUNT: ClassVar[Any] = None + ASSET_DQ_RULE_FAILED_COUNT: ClassVar[Any] = None + ASSET_DQ_RULE_PASSED_COUNT: ClassVar[Any] = None + ASSET_DQ_RESULT: ClassVar[Any] = None + ASSET_DQ_FRESHNESS_VALUE: ClassVar[Any] = None + ASSET_DQ_FRESHNESS_EXPECTATION: ClassVar[Any] = None + ASSET_DQ_ROW_SCOPE_FILTER_COLUMN_QUALIFIED_NAME: ClassVar[Any] = None + ASSET_SPACE_QUALIFIED_NAME: ClassVar[Any] = None + ASSET_SPACE_NAME: ClassVar[Any] = None + ASSET_GCP_DATAPLEX_METADATA_DETAILS: ClassVar[Any] = None + ASSET_GCP_DATAPLEX_ASPECT_LIST: ClassVar[Any] = None + ASSET_GCP_DATAPLEX_ASPECT_FIELD_LIST: ClassVar[Any] = None + ASSET_SMUS_METADATA_FORM_NAMES: ClassVar[Any] = None + ASSET_SMUS_METADATA_FORM_KEY_VALUE_DETAILS: ClassVar[Any] = None + ASSET_SMUS_METADATA_FORM_DETAILS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "ProcessExecution" + + name: Union[str, None, UnsetType] = UNSET + """Name of this asset. Fallback for display purposes, if displayName is empty.""" + + display_name: Union[str, None, UnsetType] = UNSET + """Human-readable name of this asset used for display purposes (in user interface).""" + + description: Union[str, None, UnsetType] = UNSET + """Description of this asset, for example as crawled from a source. Fallback for display purposes, if userDescription is empty.""" + + asset_source_readme: Union[str, None, UnsetType] = UNSET + """Readme of this asset, as extracted from source. If present, this will be used for the readme in user interface.""" + + user_description: Union[str, None, UnsetType] = UNSET + """Description of this asset, as provided by a user. If present, this will be used for the description in user interface.""" + + asset_ai_generated_description: Union[str, None, UnsetType] = UNSET + """Description of this asset, generated by AI based on the asset's context. Displayed separately in the UI and can be used to overwrite existing descriptions.""" + + asset_ai_generated_description_confidence: Union[float, None, UnsetType] = UNSET + """Confidence score of the AI-generated description, ranging from 0.0 to 1.0.""" + + asset_ai_generated_description_reasoning: Union[str, None, UnsetType] = UNSET + """Reasoning behind the AI-generated description, explaining how the description was derived from the asset's context.""" + + tenant_id: Union[str, None, UnsetType] = UNSET + """Name of the Atlan workspace in which this asset exists.""" + + certificate_status: Union[str, None, UnsetType] = UNSET + """Status of this asset's certification.""" + + certificate_status_message: Union[str, None, UnsetType] = UNSET + """Human-readable descriptive message used to provide further detail to certificateStatus.""" + + certificate_updated_by: Union[str, None, UnsetType] = UNSET + """Name of the user who last updated the certification of this asset.""" + + certificate_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the certification was last updated, in milliseconds.""" + + announcement_title: Union[str, None, UnsetType] = UNSET + """Brief title for the announcement on this asset. Required when announcementType is specified.""" + + announcement_message: Union[str, None, UnsetType] = UNSET + """Detailed message to include in the announcement on this asset.""" + + announcement_type: Union[str, None, UnsetType] = UNSET + """Type of announcement on this asset.""" + + announcement_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the announcement was last updated, in milliseconds.""" + + announcement_updated_by: Union[str, None, UnsetType] = UNSET + """Name of the user who last updated the announcement.""" + + owner_users: Union[Set[str], None, UnsetType] = UNSET + """List of users who own this asset.""" + + owner_groups: Union[Set[str], None, UnsetType] = UNSET + """List of groups who own this asset.""" + + admin_users: Union[Set[str], None, UnsetType] = UNSET + """List of users who administer this asset. (This is only used for certain asset types.)""" + + admin_groups: Union[Set[str], None, UnsetType] = UNSET + """List of groups who administer this asset. (This is only used for certain asset types.)""" + + viewer_users: Union[Set[str], None, UnsetType] = UNSET + """List of users who can view assets contained in a collection. (This is only used for certain asset types.)""" + + viewer_groups: Union[Set[str], None, UnsetType] = UNSET + """List of groups who can view assets contained in a collection. (This is only used for certain asset types.)""" + + connector_name: Union[str, None, UnsetType] = UNSET + """Type of the connector through which this asset is accessible.""" + + connection_name: Union[str, None, UnsetType] = UNSET + """Simple name of the connection through which this asset is accessible.""" + + connection_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the connection through which this asset is accessible.""" + + has_lineage: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="__hasLineage" + ) + """Whether this asset has lineage (true) or not (false).""" + + is_discoverable: Union[bool, None, UnsetType] = UNSET + """Whether this asset is discoverable through the UI (true) or not (false).""" + + is_editable: Union[bool, None, UnsetType] = UNSET + """Whether this asset can be edited in the UI (true) or not (false).""" + + sub_type: Union[str, None, UnsetType] = UNSET + """Subtype of this asset.""" + + view_score: Union[float, None, UnsetType] = UNSET + """View score for this asset.""" + + popularity_score: Union[float, None, UnsetType] = UNSET + """Popularity score for this asset.""" + + source_owners: Union[str, None, UnsetType] = UNSET + """List of owners of this asset, in the source system.""" + + asset_source_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for this asset in the system from which it was sourced.""" + + source_created_by: Union[str, None, UnsetType] = UNSET + """Name of the user who created this asset, in the source system.""" + + source_created_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was created in the source system, in milliseconds.""" + + source_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last updated in the source system, in milliseconds.""" + + source_updated_by: Union[str, None, UnsetType] = UNSET + """Name of the user who last updated this asset, in the source system.""" + + source_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sourceURL" + ) + """URL to the resource within the source application, used to create a button to view this asset in the source application.""" + + source_embed_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sourceEmbedURL" + ) + """URL to create an embed for a resource (for example, an image of a dashboard) within Atlan.""" + + last_sync_workflow_name: Union[str, None, UnsetType] = UNSET + """Name of the crawler that last synchronized this asset.""" + + last_sync_run_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last crawled, in milliseconds.""" + + last_sync_run: Union[str, None, UnsetType] = UNSET + """Name of the last run of the crawler that last synchronized this asset.""" + + admin_roles: Union[Set[str], None, UnsetType] = UNSET + """List of roles who administer this asset. (This is only used for Connection assets.)""" + + source_read_count: Union[int, None, UnsetType] = UNSET + """Total count of all read operations at source.""" + + source_read_user_count: Union[int, None, UnsetType] = UNSET + """Total number of unique users that read data from asset.""" + + source_last_read_at: Union[int, None, UnsetType] = UNSET + """Timestamp of most recent read operation.""" + + last_row_changed_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) of the last operation that inserted, updated, or deleted rows, in milliseconds.""" + + source_total_cost: Union[float, None, UnsetType] = UNSET + """Total cost of all operations at source.""" + + source_cost_unit: Union[str, None, UnsetType] = UNSET + """The unit of measure for sourceTotalCost.""" + + source_read_query_cost: Union[float, None, UnsetType] = UNSET + """Total cost of read queries at source.""" + + source_read_recent_user_list: Union[List[str], None, UnsetType] = UNSET + """List of usernames of the most recent users who read this asset.""" + + source_read_recent_user_record_list: Union[ + List[Dict[str, Any]], None, UnsetType + ] = UNSET + """List of usernames with extra insights for the most recent users who read this asset.""" + + source_read_top_user_list: Union[List[str], None, UnsetType] = UNSET + """List of usernames of the users who read this asset the most.""" + + source_read_top_user_record_list: Union[List[Dict[str, Any]], None, UnsetType] = ( + UNSET + ) + """List of usernames with extra insights for the users who read this asset the most.""" + + source_read_popular_query_record_list: Union[ + List[Dict[str, Any]], None, UnsetType + ] = UNSET + """List of the most popular queries that accessed this asset.""" + + source_read_expensive_query_record_list: Union[ + List[Dict[str, Any]], None, UnsetType + ] = UNSET + """List of the most expensive queries that accessed this asset.""" + + source_read_slow_query_record_list: Union[List[Dict[str, Any]], None, UnsetType] = ( + UNSET + ) + """List of the slowest queries that accessed this asset.""" + + source_query_compute_cost_list: Union[List[str], None, UnsetType] = UNSET + """List of most expensive warehouse names.""" + + source_query_compute_cost_record_list: Union[ + List[Dict[str, Any]], None, UnsetType + ] = UNSET + """List of most expensive warehouses with extra insights.""" + + dbt_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of this asset in dbt.""" + + asset_dbt_workflow_last_updated: Union[str, None, UnsetType] = UNSET + """Name of the DBT workflow in Atlan that last updated the asset.""" + + asset_dbt_alias: Union[str, None, UnsetType] = UNSET + """Alias of this asset in dbt.""" + + asset_dbt_meta: Union[str, None, UnsetType] = UNSET + """Metadata for this asset in dbt, specifically everything under the 'meta' key in the dbt object.""" + + asset_dbt_unique_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of this asset in dbt.""" + + asset_dbt_account_name: Union[str, None, UnsetType] = UNSET + """Name of the account in which this asset exists in dbt.""" + + asset_dbt_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which this asset exists in dbt.""" + + asset_dbt_package_name: Union[str, None, UnsetType] = UNSET + """Name of the package in which this asset exists in dbt.""" + + asset_dbt_job_name: Union[str, None, UnsetType] = UNSET + """Name of the job that materialized this asset in dbt.""" + + asset_dbt_job_schedule: Union[str, None, UnsetType] = UNSET + """Schedule of the job that materialized this asset in dbt.""" + + asset_dbt_job_status: Union[str, None, UnsetType] = UNSET + """Status of the job that materialized this asset in dbt.""" + + asset_dbt_test_status: Union[str, None, UnsetType] = UNSET + """All associated dbt test statuses.""" + + asset_dbt_job_schedule_cron_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable cron schedule of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt last ran, in milliseconds.""" + + asset_dbt_job_last_run_url: Union[str, None, UnsetType] = UNSET + """URL of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_created_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt was last created, in milliseconds.""" + + asset_dbt_job_last_run_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt was last updated, in milliseconds.""" + + asset_dbt_job_last_run_dequed_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt was dequeued, in milliseconds.""" + + asset_dbt_job_last_run_started_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt was started running, in milliseconds.""" + + asset_dbt_job_last_run_total_duration: Union[str, None, UnsetType] = UNSET + """Total duration of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_total_duration_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable total duration of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_queued_duration: Union[str, None, UnsetType] = UNSET + """Total duration the job that materialized this asset in dbt spent being queued.""" + + asset_dbt_job_last_run_queued_duration_humanized: Union[str, None, UnsetType] = ( + UNSET + ) + """Human-readable total duration of the last run of the job that materialized this asset in dbt spend being queued.""" + + asset_dbt_job_last_run_run_duration: Union[str, None, UnsetType] = UNSET + """Run duration of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_run_duration_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable run duration of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_git_branch: Union[str, None, UnsetType] = UNSET + """Branch in git from which the last run of the job that materialized this asset in dbt ran.""" + + asset_dbt_job_last_run_git_sha: Union[str, None, UnsetType] = UNSET + """SHA hash in git for the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_status_message: Union[str, None, UnsetType] = UNSET + """Status message of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_owner_thread_id: Union[str, None, UnsetType] = UNSET + """Thread ID of the owner of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_executed_by_thread_id: Union[str, None, UnsetType] = UNSET + """Thread ID of the user who executed the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_artifacts_saved: Union[bool, None, UnsetType] = UNSET + """Whether artifacts were saved from the last run of the job that materialized this asset in dbt (true) or not (false).""" + + asset_dbt_job_last_run_artifact_s3_path: Union[str, None, UnsetType] = UNSET + """Path in S3 to the artifacts saved from the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_has_docs_generated: Union[bool, None, UnsetType] = UNSET + """Whether docs were generated from the last run of the job that materialized this asset in dbt (true) or not (false).""" + + asset_dbt_job_last_run_has_sources_generated: Union[bool, None, UnsetType] = UNSET + """Whether sources were generated from the last run of the job that materialized this asset in dbt (true) or not (false).""" + + asset_dbt_job_last_run_notifications_sent: Union[bool, None, UnsetType] = UNSET + """Whether notifications were sent from the last run of the job that materialized this asset in dbt (true) or not (false).""" + + asset_dbt_job_next_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) when the next run of the job that materializes this asset in dbt is scheduled.""" + + asset_dbt_job_next_run_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable time when the next run of the job that materializes this asset in dbt is scheduled.""" + + asset_dbt_environment_name: Union[str, None, UnsetType] = UNSET + """Name of the environment in which this asset is materialized in dbt.""" + + asset_dbt_environment_dbt_version: Union[str, None, UnsetType] = UNSET + """Version of the environment in which this asset is materialized in dbt.""" + + asset_dbt_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset in dbt.""" + + asset_dbt_semantic_layer_proxy_url: Union[str, None, UnsetType] = UNSET + """URL of the semantic layer proxy for this asset in dbt.""" + + asset_dbt_source_freshness_criteria: Union[str, None, UnsetType] = UNSET + """Freshness criteria for the source of this asset in dbt.""" + + sample_data_url: Union[str, None, UnsetType] = UNSET + """URL for sample data for this asset.""" + + asset_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset.""" + + asset_mc_incident_names: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident names attached to this asset.""" + + asset_mc_incident_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of unique Monte Carlo incident names attached to this asset.""" + + asset_mc_alert_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of unique Monte Carlo alert names attached to this asset.""" + + asset_mc_monitor_names: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo monitor names attached to this asset.""" + + asset_mc_monitor_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of unique Monte Carlo monitor names attached to this asset.""" + + asset_mc_monitor_statuses: Union[List[str], None, UnsetType] = UNSET + """Statuses of all associated Monte Carlo monitors.""" + + asset_mc_monitor_types: Union[List[str], None, UnsetType] = UNSET + """Types of all associated Monte Carlo monitors.""" + + asset_mc_monitor_schedule_types: Union[List[str], None, UnsetType] = UNSET + """Schedules of all associated Monte Carlo monitors.""" + + asset_mc_incident_types: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident types associated with this asset.""" + + asset_mc_incident_sub_types: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident sub-types associated with this asset.""" + + asset_mc_incident_severities: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident severities associated with this asset.""" + + asset_mc_incident_priorities: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident priorities associated with this asset.""" + + asset_mc_incident_states: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident states associated with this asset.""" + + asset_mc_is_monitored: Union[bool, None, UnsetType] = UNSET + """Tracks whether this asset is monitored by MC or not""" + + asset_mc_last_sync_run_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last synced from Monte Carlo.""" + + starred_by: Union[List[str], None, UnsetType] = UNSET + """Users who have starred this asset.""" + + starred_details_list: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of usernames with extra information of the users who have starred an asset.""" + + starred_count: Union[int, None, UnsetType] = UNSET + """Number of users who have starred this asset.""" + + asset_anomalo_dq_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetAnomaloDQStatus" + ) + """Status of data quality from Anomalo.""" + + asset_anomalo_check_count: Union[int, None, UnsetType] = UNSET + """Total number of checks present in Anomalo for this asset.""" + + asset_anomalo_failed_check_count: Union[int, None, UnsetType] = UNSET + """Total number of checks failed in Anomalo for this asset.""" + + asset_anomalo_check_statuses: Union[str, None, UnsetType] = UNSET + """Stringified JSON object containing status of all Anomalo checks associated to this asset.""" + + asset_anomalo_last_check_run_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the last check was run via Anomalo.""" + + asset_anomalo_applied_check_types: Union[List[str], None, UnsetType] = UNSET + """All associated Anomalo check types.""" + + asset_anomalo_failed_check_types: Union[List[str], None, UnsetType] = UNSET + """All associated Anomalo failed check types.""" + + asset_anomalo_source_url: Union[str, None, UnsetType] = UNSET + """URL of the source in Anomalo.""" + + asset_soda_dq_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetSodaDQStatus" + ) + """Status of data quality from Soda.""" + + asset_soda_check_count: Union[int, None, UnsetType] = UNSET + """Number of checks done via Soda.""" + + asset_soda_last_sync_run_at: Union[int, None, UnsetType] = UNSET + """""" + + asset_soda_last_scan_at: Union[int, None, UnsetType] = UNSET + """""" + + asset_soda_check_statuses: Union[str, None, UnsetType] = UNSET + """All associated Soda check statuses.""" + + asset_soda_source_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetSodaSourceURL" + ) + """""" + + asset_icon: Union[str, None, UnsetType] = UNSET + """Name of the icon to use for this asset. (Only applies to glossaries, currently.)""" + + asset_external_dq_metadata_details: Union[ + Dict[str, Dict[str, Any]], None, UnsetType + ] = msgspec.field(default=UNSET, name="assetExternalDQMetadataDetails") + """DQ metadata captured for asset from external DQ tool(s).""" + + is_partial: Union[bool, None, UnsetType] = UNSET + """Indicates this asset is not fully-known, if true.""" + + is_ai_generated: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="isAIGenerated" + ) + """""" + + asset_cover_image: Union[str, None, UnsetType] = UNSET + """Cover image to use for this asset in the UI (applicable to only a few asset types).""" + + asset_theme_hex: Union[str, None, UnsetType] = UNSET + """Color (in hexadecimal RGB) to use to represent this asset.""" + + lexicographical_sort_order: Union[str, None, UnsetType] = UNSET + """Custom order for sorting purpose, managed by client""" + + has_contract: Union[bool, None, UnsetType] = UNSET + """Whether this asset has contract (true) or not (false).""" + + asset_redirect_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetRedirectGUIDs" + ) + """Array of asset ids that equivalent to this asset.""" + + asset_policy_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetPolicyGUIDs" + ) + """Array of policy ids governing this asset""" + + asset_policies_count: Union[int, None, UnsetType] = UNSET + """Count of policies inside the asset""" + + domain_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="domainGUIDs" + ) + """Array of domain guids linked to this asset""" + + non_compliant_asset_policy_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="nonCompliantAssetPolicyGUIDs" + ) + """Array of policy ids non-compliant to this asset""" + + product_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="productGUIDs" + ) + """Array of product guids linked to this asset""" + + output_product_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="outputProductGUIDs" + ) + """Array of product guids which have this asset as outputPort""" + + application_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the Application that contains this asset.""" + + application_field_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the ApplicationField that contains this asset.""" + + asset_user_defined_type: Union[str, None, UnsetType] = UNSET + """Name to use for this type of asset, as a subtype of the actual typeName.""" + + asset_internal_popularity_score: Union[float, None, UnsetType] = UNSET + """Internal Popularity score for this asset.""" + + asset_dq_schedule_type: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleType" + ) + """Type of schedule of the DQ rule that will run at datasource.""" + + asset_dq_schedule_crontab: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleCrontab" + ) + """Crontab of the DQ rule that will run at datasource.""" + + asset_dq_schedule_time_zone: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleTimeZone" + ) + """Timezone of the DQ rule schedule that will run at datasource""" + + asset_dq_schedule_source_sync_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleSourceSyncStatus" + ) + """Latest sync status of the schedule to the source.""" + + asset_dq_schedule_source_synced_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleSourceSyncedAt" + ) + """Time (epoch) at which the schedule synced to the source.""" + + asset_dq_schedule_source_sync_error_message: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQScheduleSourceSyncErrorMessage") + ) + """Error message in the case of sync state being "error".""" + + asset_dq_schedule_source_sync_error_code: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQScheduleSourceSyncErrorCode") + ) + """Error code in the case of sync state being "error".""" + + asset_dq_schedule_source_sync_raw_error: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQScheduleSourceSyncRawError") + ) + """Raw error message from the source.""" + + asset_dq_rule_attached_dimensions: Union[List[str], None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQRuleAttachedDimensions") + ) + """List of all the dimensions of attached rules.""" + + asset_dq_rule_failed_dimensions: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleFailedDimensions" + ) + """List of all the dimensions of failed rules.""" + + asset_dq_rule_passed_dimensions: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRulePassedDimensions" + ) + """List of all the dimensions for which all the rules passed.""" + + asset_dq_rule_attached_rule_types: Union[List[str], None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQRuleAttachedRuleTypes") + ) + """List of all the types of attached rules.""" + + asset_dq_rule_failed_rule_types: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleFailedRuleTypes" + ) + """List of all the types of failed rules.""" + + asset_dq_rule_passed_rule_types: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRulePassedRuleTypes" + ) + """List of all the types of rules for which all the rules passed.""" + + asset_dq_rule_result_tags: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleResultTags" + ) + """Tag for the result of the DQ rules. Eg, rule_pass:completeness:null_count.""" + + asset_dq_rule_last_run_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleLastRunAt" + ) + """Time (epoch) at which the last dq rule ran.""" + + asset_dq_manual_run_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQManualRunStatus" + ) + """Status of the latest manual DQ run triggered for this asset.""" + + asset_dq_rule_total_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleTotalCount" + ) + """Count of DQ rules attached to this asset.""" + + asset_dq_rule_failed_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleFailedCount" + ) + """Count of failed DQ rules attached to this asset.""" + + asset_dq_rule_passed_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRulePassedCount" + ) + """Count of passed DQ rules attached to this asset.""" + + asset_dq_result: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQResult" + ) + """Overall result of all the dq rules. If any one rule failed, then fail else pass.""" + + asset_dq_freshness_value: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQFreshnessValue" + ) + """Value of data freshness from Source.""" + + asset_dq_freshness_expectation: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQFreshnessExpectation" + ) + """Expectation of data freshness from Source.""" + + asset_dq_row_scope_filter_column_qualified_name: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQRowScopeFilterColumnQualifiedName") + ) + """Qualified name of the column used for row scope filtering in DQ rules for this asset.""" + + asset_space_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the space that contains this asset.""" + + asset_space_name: Union[str, None, UnsetType] = UNSET + """Name of the space that contains this asset.""" + + asset_gcp_dataplex_metadata_details: Union[Dict[str, Any], None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetGCPDataplexMetadataDetails") + ) + """Metrics captured by GCP Dataplex for objects associated with GCP services.""" + + asset_gcp_dataplex_aspect_list: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetGCPDataplexAspectList" + ) + """List of names of all Aspects linked to this asset.""" + + asset_gcp_dataplex_aspect_field_list: Union[List[str], None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetGCPDataplexAspectFieldList") + ) + """List of field key-values associated with all Aspects linked to this asset.""" + + asset_smus_metadata_form_names: Union[List[str], None, UnsetType] = UNSET + """List of AWS SMUS MetadataForm Names. This is mainly used for filtering purpose.""" + + asset_smus_metadata_form_key_value_details: Union[List[str], None, UnsetType] = ( + UNSET + ) + """List of AWS SMUS MetadataForm Key:Value Details. This is mainly used for filtering purpose.""" + + asset_smus_metadata_form_details: Union[List[Dict[str, Any]], None, UnsetType] = ( + UNSET + ) + """AWS SMUS Asset MetadataForm details""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "ProcessExecution" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _process_execution_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> ProcessExecution: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + ProcessExecution instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _process_execution_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class ProcessExecutionAttributes(ReferenceableAttributes): + """ProcessExecution-specific attributes for nested API format.""" + + name: Union[str, None, UnsetType] = UNSET + """Name of this asset. Fallback for display purposes, if displayName is empty.""" + + display_name: Union[str, None, UnsetType] = UNSET + """Human-readable name of this asset used for display purposes (in user interface).""" + + description: Union[str, None, UnsetType] = UNSET + """Description of this asset, for example as crawled from a source. Fallback for display purposes, if userDescription is empty.""" + + asset_source_readme: Union[str, None, UnsetType] = UNSET + """Readme of this asset, as extracted from source. If present, this will be used for the readme in user interface.""" + + user_description: Union[str, None, UnsetType] = UNSET + """Description of this asset, as provided by a user. If present, this will be used for the description in user interface.""" + + asset_ai_generated_description: Union[str, None, UnsetType] = UNSET + """Description of this asset, generated by AI based on the asset's context. Displayed separately in the UI and can be used to overwrite existing descriptions.""" + + asset_ai_generated_description_confidence: Union[float, None, UnsetType] = UNSET + """Confidence score of the AI-generated description, ranging from 0.0 to 1.0.""" + + asset_ai_generated_description_reasoning: Union[str, None, UnsetType] = UNSET + """Reasoning behind the AI-generated description, explaining how the description was derived from the asset's context.""" + + tenant_id: Union[str, None, UnsetType] = UNSET + """Name of the Atlan workspace in which this asset exists.""" + + certificate_status: Union[str, None, UnsetType] = UNSET + """Status of this asset's certification.""" + + certificate_status_message: Union[str, None, UnsetType] = UNSET + """Human-readable descriptive message used to provide further detail to certificateStatus.""" + + certificate_updated_by: Union[str, None, UnsetType] = UNSET + """Name of the user who last updated the certification of this asset.""" + + certificate_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the certification was last updated, in milliseconds.""" + + announcement_title: Union[str, None, UnsetType] = UNSET + """Brief title for the announcement on this asset. Required when announcementType is specified.""" + + announcement_message: Union[str, None, UnsetType] = UNSET + """Detailed message to include in the announcement on this asset.""" + + announcement_type: Union[str, None, UnsetType] = UNSET + """Type of announcement on this asset.""" + + announcement_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the announcement was last updated, in milliseconds.""" + + announcement_updated_by: Union[str, None, UnsetType] = UNSET + """Name of the user who last updated the announcement.""" + + owner_users: Union[Set[str], None, UnsetType] = UNSET + """List of users who own this asset.""" + + owner_groups: Union[Set[str], None, UnsetType] = UNSET + """List of groups who own this asset.""" + + admin_users: Union[Set[str], None, UnsetType] = UNSET + """List of users who administer this asset. (This is only used for certain asset types.)""" + + admin_groups: Union[Set[str], None, UnsetType] = UNSET + """List of groups who administer this asset. (This is only used for certain asset types.)""" + + viewer_users: Union[Set[str], None, UnsetType] = UNSET + """List of users who can view assets contained in a collection. (This is only used for certain asset types.)""" + + viewer_groups: Union[Set[str], None, UnsetType] = UNSET + """List of groups who can view assets contained in a collection. (This is only used for certain asset types.)""" + + connector_name: Union[str, None, UnsetType] = UNSET + """Type of the connector through which this asset is accessible.""" + + connection_name: Union[str, None, UnsetType] = UNSET + """Simple name of the connection through which this asset is accessible.""" + + connection_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the connection through which this asset is accessible.""" + + has_lineage: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="__hasLineage" + ) + """Whether this asset has lineage (true) or not (false).""" + + is_discoverable: Union[bool, None, UnsetType] = UNSET + """Whether this asset is discoverable through the UI (true) or not (false).""" + + is_editable: Union[bool, None, UnsetType] = UNSET + """Whether this asset can be edited in the UI (true) or not (false).""" + + sub_type: Union[str, None, UnsetType] = UNSET + """Subtype of this asset.""" + + view_score: Union[float, None, UnsetType] = UNSET + """View score for this asset.""" + + popularity_score: Union[float, None, UnsetType] = UNSET + """Popularity score for this asset.""" + + source_owners: Union[str, None, UnsetType] = UNSET + """List of owners of this asset, in the source system.""" + + asset_source_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for this asset in the system from which it was sourced.""" + + source_created_by: Union[str, None, UnsetType] = UNSET + """Name of the user who created this asset, in the source system.""" + + source_created_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was created in the source system, in milliseconds.""" + + source_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last updated in the source system, in milliseconds.""" + + source_updated_by: Union[str, None, UnsetType] = UNSET + """Name of the user who last updated this asset, in the source system.""" + + source_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sourceURL" + ) + """URL to the resource within the source application, used to create a button to view this asset in the source application.""" + + source_embed_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sourceEmbedURL" + ) + """URL to create an embed for a resource (for example, an image of a dashboard) within Atlan.""" + + last_sync_workflow_name: Union[str, None, UnsetType] = UNSET + """Name of the crawler that last synchronized this asset.""" + + last_sync_run_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last crawled, in milliseconds.""" + + last_sync_run: Union[str, None, UnsetType] = UNSET + """Name of the last run of the crawler that last synchronized this asset.""" + + admin_roles: Union[Set[str], None, UnsetType] = UNSET + """List of roles who administer this asset. (This is only used for Connection assets.)""" + + source_read_count: Union[int, None, UnsetType] = UNSET + """Total count of all read operations at source.""" + + source_read_user_count: Union[int, None, UnsetType] = UNSET + """Total number of unique users that read data from asset.""" + + source_last_read_at: Union[int, None, UnsetType] = UNSET + """Timestamp of most recent read operation.""" + + last_row_changed_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) of the last operation that inserted, updated, or deleted rows, in milliseconds.""" + + source_total_cost: Union[float, None, UnsetType] = UNSET + """Total cost of all operations at source.""" + + source_cost_unit: Union[str, None, UnsetType] = UNSET + """The unit of measure for sourceTotalCost.""" + + source_read_query_cost: Union[float, None, UnsetType] = UNSET + """Total cost of read queries at source.""" + + source_read_recent_user_list: Union[List[str], None, UnsetType] = UNSET + """List of usernames of the most recent users who read this asset.""" + + source_read_recent_user_record_list: Union[ + List[Dict[str, Any]], None, UnsetType + ] = UNSET + """List of usernames with extra insights for the most recent users who read this asset.""" + + source_read_top_user_list: Union[List[str], None, UnsetType] = UNSET + """List of usernames of the users who read this asset the most.""" + + source_read_top_user_record_list: Union[List[Dict[str, Any]], None, UnsetType] = ( + UNSET + ) + """List of usernames with extra insights for the users who read this asset the most.""" + + source_read_popular_query_record_list: Union[ + List[Dict[str, Any]], None, UnsetType + ] = UNSET + """List of the most popular queries that accessed this asset.""" + + source_read_expensive_query_record_list: Union[ + List[Dict[str, Any]], None, UnsetType + ] = UNSET + """List of the most expensive queries that accessed this asset.""" + + source_read_slow_query_record_list: Union[List[Dict[str, Any]], None, UnsetType] = ( + UNSET + ) + """List of the slowest queries that accessed this asset.""" + + source_query_compute_cost_list: Union[List[str], None, UnsetType] = UNSET + """List of most expensive warehouse names.""" + + source_query_compute_cost_record_list: Union[ + List[Dict[str, Any]], None, UnsetType + ] = UNSET + """List of most expensive warehouses with extra insights.""" + + dbt_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of this asset in dbt.""" + + asset_dbt_workflow_last_updated: Union[str, None, UnsetType] = UNSET + """Name of the DBT workflow in Atlan that last updated the asset.""" + + asset_dbt_alias: Union[str, None, UnsetType] = UNSET + """Alias of this asset in dbt.""" + + asset_dbt_meta: Union[str, None, UnsetType] = UNSET + """Metadata for this asset in dbt, specifically everything under the 'meta' key in the dbt object.""" + + asset_dbt_unique_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of this asset in dbt.""" + + asset_dbt_account_name: Union[str, None, UnsetType] = UNSET + """Name of the account in which this asset exists in dbt.""" + + asset_dbt_project_name: Union[str, None, UnsetType] = UNSET + """Name of the project in which this asset exists in dbt.""" + + asset_dbt_package_name: Union[str, None, UnsetType] = UNSET + """Name of the package in which this asset exists in dbt.""" + + asset_dbt_job_name: Union[str, None, UnsetType] = UNSET + """Name of the job that materialized this asset in dbt.""" + + asset_dbt_job_schedule: Union[str, None, UnsetType] = UNSET + """Schedule of the job that materialized this asset in dbt.""" + + asset_dbt_job_status: Union[str, None, UnsetType] = UNSET + """Status of the job that materialized this asset in dbt.""" + + asset_dbt_test_status: Union[str, None, UnsetType] = UNSET + """All associated dbt test statuses.""" + + asset_dbt_job_schedule_cron_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable cron schedule of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt last ran, in milliseconds.""" + + asset_dbt_job_last_run_url: Union[str, None, UnsetType] = UNSET + """URL of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_created_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt was last created, in milliseconds.""" + + asset_dbt_job_last_run_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt was last updated, in milliseconds.""" + + asset_dbt_job_last_run_dequed_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt was dequeued, in milliseconds.""" + + asset_dbt_job_last_run_started_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the job that materialized this asset in dbt was started running, in milliseconds.""" + + asset_dbt_job_last_run_total_duration: Union[str, None, UnsetType] = UNSET + """Total duration of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_total_duration_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable total duration of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_queued_duration: Union[str, None, UnsetType] = UNSET + """Total duration the job that materialized this asset in dbt spent being queued.""" + + asset_dbt_job_last_run_queued_duration_humanized: Union[str, None, UnsetType] = ( + UNSET + ) + """Human-readable total duration of the last run of the job that materialized this asset in dbt spend being queued.""" + + asset_dbt_job_last_run_run_duration: Union[str, None, UnsetType] = UNSET + """Run duration of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_run_duration_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable run duration of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_git_branch: Union[str, None, UnsetType] = UNSET + """Branch in git from which the last run of the job that materialized this asset in dbt ran.""" + + asset_dbt_job_last_run_git_sha: Union[str, None, UnsetType] = UNSET + """SHA hash in git for the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_status_message: Union[str, None, UnsetType] = UNSET + """Status message of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_owner_thread_id: Union[str, None, UnsetType] = UNSET + """Thread ID of the owner of the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_executed_by_thread_id: Union[str, None, UnsetType] = UNSET + """Thread ID of the user who executed the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_artifacts_saved: Union[bool, None, UnsetType] = UNSET + """Whether artifacts were saved from the last run of the job that materialized this asset in dbt (true) or not (false).""" + + asset_dbt_job_last_run_artifact_s3_path: Union[str, None, UnsetType] = UNSET + """Path in S3 to the artifacts saved from the last run of the job that materialized this asset in dbt.""" + + asset_dbt_job_last_run_has_docs_generated: Union[bool, None, UnsetType] = UNSET + """Whether docs were generated from the last run of the job that materialized this asset in dbt (true) or not (false).""" + + asset_dbt_job_last_run_has_sources_generated: Union[bool, None, UnsetType] = UNSET + """Whether sources were generated from the last run of the job that materialized this asset in dbt (true) or not (false).""" + + asset_dbt_job_last_run_notifications_sent: Union[bool, None, UnsetType] = UNSET + """Whether notifications were sent from the last run of the job that materialized this asset in dbt (true) or not (false).""" + + asset_dbt_job_next_run: Union[int, None, UnsetType] = UNSET + """Time (epoch) when the next run of the job that materializes this asset in dbt is scheduled.""" + + asset_dbt_job_next_run_humanized: Union[str, None, UnsetType] = UNSET + """Human-readable time when the next run of the job that materializes this asset in dbt is scheduled.""" + + asset_dbt_environment_name: Union[str, None, UnsetType] = UNSET + """Name of the environment in which this asset is materialized in dbt.""" + + asset_dbt_environment_dbt_version: Union[str, None, UnsetType] = UNSET + """Version of the environment in which this asset is materialized in dbt.""" + + asset_dbt_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset in dbt.""" + + asset_dbt_semantic_layer_proxy_url: Union[str, None, UnsetType] = UNSET + """URL of the semantic layer proxy for this asset in dbt.""" + + asset_dbt_source_freshness_criteria: Union[str, None, UnsetType] = UNSET + """Freshness criteria for the source of this asset in dbt.""" + + sample_data_url: Union[str, None, UnsetType] = UNSET + """URL for sample data for this asset.""" + + asset_tags: Union[List[str], None, UnsetType] = UNSET + """List of tags attached to this asset.""" + + asset_mc_incident_names: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident names attached to this asset.""" + + asset_mc_incident_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of unique Monte Carlo incident names attached to this asset.""" + + asset_mc_alert_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of unique Monte Carlo alert names attached to this asset.""" + + asset_mc_monitor_names: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo monitor names attached to this asset.""" + + asset_mc_monitor_qualified_names: Union[List[str], None, UnsetType] = UNSET + """List of unique Monte Carlo monitor names attached to this asset.""" + + asset_mc_monitor_statuses: Union[List[str], None, UnsetType] = UNSET + """Statuses of all associated Monte Carlo monitors.""" + + asset_mc_monitor_types: Union[List[str], None, UnsetType] = UNSET + """Types of all associated Monte Carlo monitors.""" + + asset_mc_monitor_schedule_types: Union[List[str], None, UnsetType] = UNSET + """Schedules of all associated Monte Carlo monitors.""" + + asset_mc_incident_types: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident types associated with this asset.""" + + asset_mc_incident_sub_types: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident sub-types associated with this asset.""" + + asset_mc_incident_severities: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident severities associated with this asset.""" + + asset_mc_incident_priorities: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident priorities associated with this asset.""" + + asset_mc_incident_states: Union[List[str], None, UnsetType] = UNSET + """List of Monte Carlo incident states associated with this asset.""" + + asset_mc_is_monitored: Union[bool, None, UnsetType] = UNSET + """Tracks whether this asset is monitored by MC or not""" + + asset_mc_last_sync_run_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last synced from Monte Carlo.""" + + starred_by: Union[List[str], None, UnsetType] = UNSET + """Users who have starred this asset.""" + + starred_details_list: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of usernames with extra information of the users who have starred an asset.""" + + starred_count: Union[int, None, UnsetType] = UNSET + """Number of users who have starred this asset.""" + + asset_anomalo_dq_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetAnomaloDQStatus" + ) + """Status of data quality from Anomalo.""" + + asset_anomalo_check_count: Union[int, None, UnsetType] = UNSET + """Total number of checks present in Anomalo for this asset.""" + + asset_anomalo_failed_check_count: Union[int, None, UnsetType] = UNSET + """Total number of checks failed in Anomalo for this asset.""" + + asset_anomalo_check_statuses: Union[str, None, UnsetType] = UNSET + """Stringified JSON object containing status of all Anomalo checks associated to this asset.""" + + asset_anomalo_last_check_run_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the last check was run via Anomalo.""" + + asset_anomalo_applied_check_types: Union[List[str], None, UnsetType] = UNSET + """All associated Anomalo check types.""" + + asset_anomalo_failed_check_types: Union[List[str], None, UnsetType] = UNSET + """All associated Anomalo failed check types.""" + + asset_anomalo_source_url: Union[str, None, UnsetType] = UNSET + """URL of the source in Anomalo.""" + + asset_soda_dq_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetSodaDQStatus" + ) + """Status of data quality from Soda.""" + + asset_soda_check_count: Union[int, None, UnsetType] = UNSET + """Number of checks done via Soda.""" + + asset_soda_last_sync_run_at: Union[int, None, UnsetType] = UNSET + """""" + + asset_soda_last_scan_at: Union[int, None, UnsetType] = UNSET + """""" + + asset_soda_check_statuses: Union[str, None, UnsetType] = UNSET + """All associated Soda check statuses.""" + + asset_soda_source_url: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetSodaSourceURL" + ) + """""" + + asset_icon: Union[str, None, UnsetType] = UNSET + """Name of the icon to use for this asset. (Only applies to glossaries, currently.)""" + + asset_external_dq_metadata_details: Union[ + Dict[str, Dict[str, Any]], None, UnsetType + ] = msgspec.field(default=UNSET, name="assetExternalDQMetadataDetails") + """DQ metadata captured for asset from external DQ tool(s).""" + + is_partial: Union[bool, None, UnsetType] = UNSET + """Indicates this asset is not fully-known, if true.""" + + is_ai_generated: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="isAIGenerated" + ) + """""" + + asset_cover_image: Union[str, None, UnsetType] = UNSET + """Cover image to use for this asset in the UI (applicable to only a few asset types).""" + + asset_theme_hex: Union[str, None, UnsetType] = UNSET + """Color (in hexadecimal RGB) to use to represent this asset.""" + + lexicographical_sort_order: Union[str, None, UnsetType] = UNSET + """Custom order for sorting purpose, managed by client""" + + has_contract: Union[bool, None, UnsetType] = UNSET + """Whether this asset has contract (true) or not (false).""" + + asset_redirect_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetRedirectGUIDs" + ) + """Array of asset ids that equivalent to this asset.""" + + asset_policy_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetPolicyGUIDs" + ) + """Array of policy ids governing this asset""" + + asset_policies_count: Union[int, None, UnsetType] = UNSET + """Count of policies inside the asset""" + + domain_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="domainGUIDs" + ) + """Array of domain guids linked to this asset""" + + non_compliant_asset_policy_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="nonCompliantAssetPolicyGUIDs" + ) + """Array of policy ids non-compliant to this asset""" + + product_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="productGUIDs" + ) + """Array of product guids linked to this asset""" + + output_product_guids: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="outputProductGUIDs" + ) + """Array of product guids which have this asset as outputPort""" + + application_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the Application that contains this asset.""" + + application_field_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the ApplicationField that contains this asset.""" + + asset_user_defined_type: Union[str, None, UnsetType] = UNSET + """Name to use for this type of asset, as a subtype of the actual typeName.""" + + asset_internal_popularity_score: Union[float, None, UnsetType] = UNSET + """Internal Popularity score for this asset.""" + + asset_dq_schedule_type: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleType" + ) + """Type of schedule of the DQ rule that will run at datasource.""" + + asset_dq_schedule_crontab: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleCrontab" + ) + """Crontab of the DQ rule that will run at datasource.""" + + asset_dq_schedule_time_zone: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleTimeZone" + ) + """Timezone of the DQ rule schedule that will run at datasource""" + + asset_dq_schedule_source_sync_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleSourceSyncStatus" + ) + """Latest sync status of the schedule to the source.""" + + asset_dq_schedule_source_synced_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQScheduleSourceSyncedAt" + ) + """Time (epoch) at which the schedule synced to the source.""" + + asset_dq_schedule_source_sync_error_message: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQScheduleSourceSyncErrorMessage") + ) + """Error message in the case of sync state being "error".""" + + asset_dq_schedule_source_sync_error_code: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQScheduleSourceSyncErrorCode") + ) + """Error code in the case of sync state being "error".""" + + asset_dq_schedule_source_sync_raw_error: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQScheduleSourceSyncRawError") + ) + """Raw error message from the source.""" + + asset_dq_rule_attached_dimensions: Union[List[str], None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQRuleAttachedDimensions") + ) + """List of all the dimensions of attached rules.""" + + asset_dq_rule_failed_dimensions: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleFailedDimensions" + ) + """List of all the dimensions of failed rules.""" + + asset_dq_rule_passed_dimensions: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRulePassedDimensions" + ) + """List of all the dimensions for which all the rules passed.""" + + asset_dq_rule_attached_rule_types: Union[List[str], None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQRuleAttachedRuleTypes") + ) + """List of all the types of attached rules.""" + + asset_dq_rule_failed_rule_types: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleFailedRuleTypes" + ) + """List of all the types of failed rules.""" + + asset_dq_rule_passed_rule_types: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRulePassedRuleTypes" + ) + """List of all the types of rules for which all the rules passed.""" + + asset_dq_rule_result_tags: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleResultTags" + ) + """Tag for the result of the DQ rules. Eg, rule_pass:completeness:null_count.""" + + asset_dq_rule_last_run_at: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleLastRunAt" + ) + """Time (epoch) at which the last dq rule ran.""" + + asset_dq_manual_run_status: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQManualRunStatus" + ) + """Status of the latest manual DQ run triggered for this asset.""" + + asset_dq_rule_total_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleTotalCount" + ) + """Count of DQ rules attached to this asset.""" + + asset_dq_rule_failed_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRuleFailedCount" + ) + """Count of failed DQ rules attached to this asset.""" + + asset_dq_rule_passed_count: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQRulePassedCount" + ) + """Count of passed DQ rules attached to this asset.""" + + asset_dq_result: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQResult" + ) + """Overall result of all the dq rules. If any one rule failed, then fail else pass.""" + + asset_dq_freshness_value: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQFreshnessValue" + ) + """Value of data freshness from Source.""" + + asset_dq_freshness_expectation: Union[int, None, UnsetType] = msgspec.field( + default=UNSET, name="assetDQFreshnessExpectation" + ) + """Expectation of data freshness from Source.""" + + asset_dq_row_scope_filter_column_qualified_name: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetDQRowScopeFilterColumnQualifiedName") + ) + """Qualified name of the column used for row scope filtering in DQ rules for this asset.""" + + asset_space_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the space that contains this asset.""" + + asset_space_name: Union[str, None, UnsetType] = UNSET + """Name of the space that contains this asset.""" + + asset_gcp_dataplex_metadata_details: Union[Dict[str, Any], None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetGCPDataplexMetadataDetails") + ) + """Metrics captured by GCP Dataplex for objects associated with GCP services.""" + + asset_gcp_dataplex_aspect_list: Union[List[str], None, UnsetType] = msgspec.field( + default=UNSET, name="assetGCPDataplexAspectList" + ) + """List of names of all Aspects linked to this asset.""" + + asset_gcp_dataplex_aspect_field_list: Union[List[str], None, UnsetType] = ( + msgspec.field(default=UNSET, name="assetGCPDataplexAspectFieldList") + ) + """List of field key-values associated with all Aspects linked to this asset.""" + + asset_smus_metadata_form_names: Union[List[str], None, UnsetType] = UNSET + """List of AWS SMUS MetadataForm Names. This is mainly used for filtering purpose.""" + + asset_smus_metadata_form_key_value_details: Union[List[str], None, UnsetType] = ( + UNSET + ) + """List of AWS SMUS MetadataForm Key:Value Details. This is mainly used for filtering purpose.""" + + asset_smus_metadata_form_details: Union[List[Dict[str, Any]], None, UnsetType] = ( + UNSET + ) + """AWS SMUS Asset MetadataForm details""" + + +class ProcessExecutionRelationshipAttributes(ReferenceableRelationshipAttributes): + """ProcessExecution-specific relationship attributes for nested API format.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + +class ProcessExecutionNested(ReferenceableNested): + """ProcessExecution in nested API format for high-performance serialization.""" + + attributes: Union[ProcessExecutionAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + ProcessExecutionRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + ProcessExecutionRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + ProcessExecutionRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_PROCESS_EXECUTION_REL_FIELDS: List[str] = [ + *_REFERENCEABLE_REL_FIELDS, + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", +] + + +def _populate_process_execution_attrs( + attrs: ProcessExecutionAttributes, obj: ProcessExecution +) -> None: + """Populate ProcessExecution-specific attributes on the attrs struct.""" + _populate_referenceable_attrs(attrs, obj) + attrs.name = obj.name + attrs.display_name = obj.display_name + attrs.description = obj.description + attrs.asset_source_readme = obj.asset_source_readme + attrs.user_description = obj.user_description + attrs.asset_ai_generated_description = obj.asset_ai_generated_description + attrs.asset_ai_generated_description_confidence = ( + obj.asset_ai_generated_description_confidence + ) + attrs.asset_ai_generated_description_reasoning = ( + obj.asset_ai_generated_description_reasoning + ) + attrs.tenant_id = obj.tenant_id + attrs.certificate_status = obj.certificate_status + attrs.certificate_status_message = obj.certificate_status_message + attrs.certificate_updated_by = obj.certificate_updated_by + attrs.certificate_updated_at = obj.certificate_updated_at + attrs.announcement_title = obj.announcement_title + attrs.announcement_message = obj.announcement_message + attrs.announcement_type = obj.announcement_type + attrs.announcement_updated_at = obj.announcement_updated_at + attrs.announcement_updated_by = obj.announcement_updated_by + attrs.owner_users = obj.owner_users + attrs.owner_groups = obj.owner_groups + attrs.admin_users = obj.admin_users + attrs.admin_groups = obj.admin_groups + attrs.viewer_users = obj.viewer_users + attrs.viewer_groups = obj.viewer_groups + attrs.connector_name = obj.connector_name + attrs.connection_name = obj.connection_name + attrs.connection_qualified_name = obj.connection_qualified_name + attrs.has_lineage = obj.has_lineage + attrs.is_discoverable = obj.is_discoverable + attrs.is_editable = obj.is_editable + attrs.sub_type = obj.sub_type + attrs.view_score = obj.view_score + attrs.popularity_score = obj.popularity_score + attrs.source_owners = obj.source_owners + attrs.asset_source_id = obj.asset_source_id + attrs.source_created_by = obj.source_created_by + attrs.source_created_at = obj.source_created_at + attrs.source_updated_at = obj.source_updated_at + attrs.source_updated_by = obj.source_updated_by + attrs.source_url = obj.source_url + attrs.source_embed_url = obj.source_embed_url + attrs.last_sync_workflow_name = obj.last_sync_workflow_name + attrs.last_sync_run_at = obj.last_sync_run_at + attrs.last_sync_run = obj.last_sync_run + attrs.admin_roles = obj.admin_roles + attrs.source_read_count = obj.source_read_count + attrs.source_read_user_count = obj.source_read_user_count + attrs.source_last_read_at = obj.source_last_read_at + attrs.last_row_changed_at = obj.last_row_changed_at + attrs.source_total_cost = obj.source_total_cost + attrs.source_cost_unit = obj.source_cost_unit + attrs.source_read_query_cost = obj.source_read_query_cost + attrs.source_read_recent_user_list = obj.source_read_recent_user_list + attrs.source_read_recent_user_record_list = obj.source_read_recent_user_record_list + attrs.source_read_top_user_list = obj.source_read_top_user_list + attrs.source_read_top_user_record_list = obj.source_read_top_user_record_list + attrs.source_read_popular_query_record_list = ( + obj.source_read_popular_query_record_list + ) + attrs.source_read_expensive_query_record_list = ( + obj.source_read_expensive_query_record_list + ) + attrs.source_read_slow_query_record_list = obj.source_read_slow_query_record_list + attrs.source_query_compute_cost_list = obj.source_query_compute_cost_list + attrs.source_query_compute_cost_record_list = ( + obj.source_query_compute_cost_record_list + ) + attrs.dbt_qualified_name = obj.dbt_qualified_name + attrs.asset_dbt_workflow_last_updated = obj.asset_dbt_workflow_last_updated + attrs.asset_dbt_alias = obj.asset_dbt_alias + attrs.asset_dbt_meta = obj.asset_dbt_meta + attrs.asset_dbt_unique_id = obj.asset_dbt_unique_id + attrs.asset_dbt_account_name = obj.asset_dbt_account_name + attrs.asset_dbt_project_name = obj.asset_dbt_project_name + attrs.asset_dbt_package_name = obj.asset_dbt_package_name + attrs.asset_dbt_job_name = obj.asset_dbt_job_name + attrs.asset_dbt_job_schedule = obj.asset_dbt_job_schedule + attrs.asset_dbt_job_status = obj.asset_dbt_job_status + attrs.asset_dbt_test_status = obj.asset_dbt_test_status + attrs.asset_dbt_job_schedule_cron_humanized = ( + obj.asset_dbt_job_schedule_cron_humanized + ) + attrs.asset_dbt_job_last_run = obj.asset_dbt_job_last_run + attrs.asset_dbt_job_last_run_url = obj.asset_dbt_job_last_run_url + attrs.asset_dbt_job_last_run_created_at = obj.asset_dbt_job_last_run_created_at + attrs.asset_dbt_job_last_run_updated_at = obj.asset_dbt_job_last_run_updated_at + attrs.asset_dbt_job_last_run_dequed_at = obj.asset_dbt_job_last_run_dequed_at + attrs.asset_dbt_job_last_run_started_at = obj.asset_dbt_job_last_run_started_at + attrs.asset_dbt_job_last_run_total_duration = ( + obj.asset_dbt_job_last_run_total_duration + ) + attrs.asset_dbt_job_last_run_total_duration_humanized = ( + obj.asset_dbt_job_last_run_total_duration_humanized + ) + attrs.asset_dbt_job_last_run_queued_duration = ( + obj.asset_dbt_job_last_run_queued_duration + ) + attrs.asset_dbt_job_last_run_queued_duration_humanized = ( + obj.asset_dbt_job_last_run_queued_duration_humanized + ) + attrs.asset_dbt_job_last_run_run_duration = obj.asset_dbt_job_last_run_run_duration + attrs.asset_dbt_job_last_run_run_duration_humanized = ( + obj.asset_dbt_job_last_run_run_duration_humanized + ) + attrs.asset_dbt_job_last_run_git_branch = obj.asset_dbt_job_last_run_git_branch + attrs.asset_dbt_job_last_run_git_sha = obj.asset_dbt_job_last_run_git_sha + attrs.asset_dbt_job_last_run_status_message = ( + obj.asset_dbt_job_last_run_status_message + ) + attrs.asset_dbt_job_last_run_owner_thread_id = ( + obj.asset_dbt_job_last_run_owner_thread_id + ) + attrs.asset_dbt_job_last_run_executed_by_thread_id = ( + obj.asset_dbt_job_last_run_executed_by_thread_id + ) + attrs.asset_dbt_job_last_run_artifacts_saved = ( + obj.asset_dbt_job_last_run_artifacts_saved + ) + attrs.asset_dbt_job_last_run_artifact_s3_path = ( + obj.asset_dbt_job_last_run_artifact_s3_path + ) + attrs.asset_dbt_job_last_run_has_docs_generated = ( + obj.asset_dbt_job_last_run_has_docs_generated + ) + attrs.asset_dbt_job_last_run_has_sources_generated = ( + obj.asset_dbt_job_last_run_has_sources_generated + ) + attrs.asset_dbt_job_last_run_notifications_sent = ( + obj.asset_dbt_job_last_run_notifications_sent + ) + attrs.asset_dbt_job_next_run = obj.asset_dbt_job_next_run + attrs.asset_dbt_job_next_run_humanized = obj.asset_dbt_job_next_run_humanized + attrs.asset_dbt_environment_name = obj.asset_dbt_environment_name + attrs.asset_dbt_environment_dbt_version = obj.asset_dbt_environment_dbt_version + attrs.asset_dbt_tags = obj.asset_dbt_tags + attrs.asset_dbt_semantic_layer_proxy_url = obj.asset_dbt_semantic_layer_proxy_url + attrs.asset_dbt_source_freshness_criteria = obj.asset_dbt_source_freshness_criteria + attrs.sample_data_url = obj.sample_data_url + attrs.asset_tags = obj.asset_tags + attrs.asset_mc_incident_names = obj.asset_mc_incident_names + attrs.asset_mc_incident_qualified_names = obj.asset_mc_incident_qualified_names + attrs.asset_mc_alert_qualified_names = obj.asset_mc_alert_qualified_names + attrs.asset_mc_monitor_names = obj.asset_mc_monitor_names + attrs.asset_mc_monitor_qualified_names = obj.asset_mc_monitor_qualified_names + attrs.asset_mc_monitor_statuses = obj.asset_mc_monitor_statuses + attrs.asset_mc_monitor_types = obj.asset_mc_monitor_types + attrs.asset_mc_monitor_schedule_types = obj.asset_mc_monitor_schedule_types + attrs.asset_mc_incident_types = obj.asset_mc_incident_types + attrs.asset_mc_incident_sub_types = obj.asset_mc_incident_sub_types + attrs.asset_mc_incident_severities = obj.asset_mc_incident_severities + attrs.asset_mc_incident_priorities = obj.asset_mc_incident_priorities + attrs.asset_mc_incident_states = obj.asset_mc_incident_states + attrs.asset_mc_is_monitored = obj.asset_mc_is_monitored + attrs.asset_mc_last_sync_run_at = obj.asset_mc_last_sync_run_at + attrs.starred_by = obj.starred_by + attrs.starred_details_list = obj.starred_details_list + attrs.starred_count = obj.starred_count + attrs.asset_anomalo_dq_status = obj.asset_anomalo_dq_status + attrs.asset_anomalo_check_count = obj.asset_anomalo_check_count + attrs.asset_anomalo_failed_check_count = obj.asset_anomalo_failed_check_count + attrs.asset_anomalo_check_statuses = obj.asset_anomalo_check_statuses + attrs.asset_anomalo_last_check_run_at = obj.asset_anomalo_last_check_run_at + attrs.asset_anomalo_applied_check_types = obj.asset_anomalo_applied_check_types + attrs.asset_anomalo_failed_check_types = obj.asset_anomalo_failed_check_types + attrs.asset_anomalo_source_url = obj.asset_anomalo_source_url + attrs.asset_soda_dq_status = obj.asset_soda_dq_status + attrs.asset_soda_check_count = obj.asset_soda_check_count + attrs.asset_soda_last_sync_run_at = obj.asset_soda_last_sync_run_at + attrs.asset_soda_last_scan_at = obj.asset_soda_last_scan_at + attrs.asset_soda_check_statuses = obj.asset_soda_check_statuses + attrs.asset_soda_source_url = obj.asset_soda_source_url + attrs.asset_icon = obj.asset_icon + attrs.asset_external_dq_metadata_details = obj.asset_external_dq_metadata_details + attrs.is_partial = obj.is_partial + attrs.is_ai_generated = obj.is_ai_generated + attrs.asset_cover_image = obj.asset_cover_image + attrs.asset_theme_hex = obj.asset_theme_hex + attrs.lexicographical_sort_order = obj.lexicographical_sort_order + attrs.has_contract = obj.has_contract + attrs.asset_redirect_guids = obj.asset_redirect_guids + attrs.asset_policy_guids = obj.asset_policy_guids + attrs.asset_policies_count = obj.asset_policies_count + attrs.domain_guids = obj.domain_guids + attrs.non_compliant_asset_policy_guids = obj.non_compliant_asset_policy_guids + attrs.product_guids = obj.product_guids + attrs.output_product_guids = obj.output_product_guids + attrs.application_qualified_name = obj.application_qualified_name + attrs.application_field_qualified_name = obj.application_field_qualified_name + attrs.asset_user_defined_type = obj.asset_user_defined_type + attrs.asset_internal_popularity_score = obj.asset_internal_popularity_score + attrs.asset_dq_schedule_type = obj.asset_dq_schedule_type + attrs.asset_dq_schedule_crontab = obj.asset_dq_schedule_crontab + attrs.asset_dq_schedule_time_zone = obj.asset_dq_schedule_time_zone + attrs.asset_dq_schedule_source_sync_status = ( + obj.asset_dq_schedule_source_sync_status + ) + attrs.asset_dq_schedule_source_synced_at = obj.asset_dq_schedule_source_synced_at + attrs.asset_dq_schedule_source_sync_error_message = ( + obj.asset_dq_schedule_source_sync_error_message + ) + attrs.asset_dq_schedule_source_sync_error_code = ( + obj.asset_dq_schedule_source_sync_error_code + ) + attrs.asset_dq_schedule_source_sync_raw_error = ( + obj.asset_dq_schedule_source_sync_raw_error + ) + attrs.asset_dq_rule_attached_dimensions = obj.asset_dq_rule_attached_dimensions + attrs.asset_dq_rule_failed_dimensions = obj.asset_dq_rule_failed_dimensions + attrs.asset_dq_rule_passed_dimensions = obj.asset_dq_rule_passed_dimensions + attrs.asset_dq_rule_attached_rule_types = obj.asset_dq_rule_attached_rule_types + attrs.asset_dq_rule_failed_rule_types = obj.asset_dq_rule_failed_rule_types + attrs.asset_dq_rule_passed_rule_types = obj.asset_dq_rule_passed_rule_types + attrs.asset_dq_rule_result_tags = obj.asset_dq_rule_result_tags + attrs.asset_dq_rule_last_run_at = obj.asset_dq_rule_last_run_at + attrs.asset_dq_manual_run_status = obj.asset_dq_manual_run_status + attrs.asset_dq_rule_total_count = obj.asset_dq_rule_total_count + attrs.asset_dq_rule_failed_count = obj.asset_dq_rule_failed_count + attrs.asset_dq_rule_passed_count = obj.asset_dq_rule_passed_count + attrs.asset_dq_result = obj.asset_dq_result + attrs.asset_dq_freshness_value = obj.asset_dq_freshness_value + attrs.asset_dq_freshness_expectation = obj.asset_dq_freshness_expectation + attrs.asset_dq_row_scope_filter_column_qualified_name = ( + obj.asset_dq_row_scope_filter_column_qualified_name + ) + attrs.asset_space_qualified_name = obj.asset_space_qualified_name + attrs.asset_space_name = obj.asset_space_name + attrs.asset_gcp_dataplex_metadata_details = obj.asset_gcp_dataplex_metadata_details + attrs.asset_gcp_dataplex_aspect_list = obj.asset_gcp_dataplex_aspect_list + attrs.asset_gcp_dataplex_aspect_field_list = ( + obj.asset_gcp_dataplex_aspect_field_list + ) + attrs.asset_smus_metadata_form_names = obj.asset_smus_metadata_form_names + attrs.asset_smus_metadata_form_key_value_details = ( + obj.asset_smus_metadata_form_key_value_details + ) + attrs.asset_smus_metadata_form_details = obj.asset_smus_metadata_form_details + + +def _extract_process_execution_attrs(attrs: ProcessExecutionAttributes) -> dict: + """Extract all ProcessExecution attributes from the attrs struct into a flat dict.""" + result = _extract_referenceable_attrs(attrs) + result["name"] = attrs.name + result["display_name"] = attrs.display_name + result["description"] = attrs.description + result["asset_source_readme"] = attrs.asset_source_readme + result["user_description"] = attrs.user_description + result["asset_ai_generated_description"] = attrs.asset_ai_generated_description + result["asset_ai_generated_description_confidence"] = ( + attrs.asset_ai_generated_description_confidence + ) + result["asset_ai_generated_description_reasoning"] = ( + attrs.asset_ai_generated_description_reasoning + ) + result["tenant_id"] = attrs.tenant_id + result["certificate_status"] = attrs.certificate_status + result["certificate_status_message"] = attrs.certificate_status_message + result["certificate_updated_by"] = attrs.certificate_updated_by + result["certificate_updated_at"] = attrs.certificate_updated_at + result["announcement_title"] = attrs.announcement_title + result["announcement_message"] = attrs.announcement_message + result["announcement_type"] = attrs.announcement_type + result["announcement_updated_at"] = attrs.announcement_updated_at + result["announcement_updated_by"] = attrs.announcement_updated_by + result["owner_users"] = attrs.owner_users + result["owner_groups"] = attrs.owner_groups + result["admin_users"] = attrs.admin_users + result["admin_groups"] = attrs.admin_groups + result["viewer_users"] = attrs.viewer_users + result["viewer_groups"] = attrs.viewer_groups + result["connector_name"] = attrs.connector_name + result["connection_name"] = attrs.connection_name + result["connection_qualified_name"] = attrs.connection_qualified_name + result["has_lineage"] = attrs.has_lineage + result["is_discoverable"] = attrs.is_discoverable + result["is_editable"] = attrs.is_editable + result["sub_type"] = attrs.sub_type + result["view_score"] = attrs.view_score + result["popularity_score"] = attrs.popularity_score + result["source_owners"] = attrs.source_owners + result["asset_source_id"] = attrs.asset_source_id + result["source_created_by"] = attrs.source_created_by + result["source_created_at"] = attrs.source_created_at + result["source_updated_at"] = attrs.source_updated_at + result["source_updated_by"] = attrs.source_updated_by + result["source_url"] = attrs.source_url + result["source_embed_url"] = attrs.source_embed_url + result["last_sync_workflow_name"] = attrs.last_sync_workflow_name + result["last_sync_run_at"] = attrs.last_sync_run_at + result["last_sync_run"] = attrs.last_sync_run + result["admin_roles"] = attrs.admin_roles + result["source_read_count"] = attrs.source_read_count + result["source_read_user_count"] = attrs.source_read_user_count + result["source_last_read_at"] = attrs.source_last_read_at + result["last_row_changed_at"] = attrs.last_row_changed_at + result["source_total_cost"] = attrs.source_total_cost + result["source_cost_unit"] = attrs.source_cost_unit + result["source_read_query_cost"] = attrs.source_read_query_cost + result["source_read_recent_user_list"] = attrs.source_read_recent_user_list + result["source_read_recent_user_record_list"] = ( + attrs.source_read_recent_user_record_list + ) + result["source_read_top_user_list"] = attrs.source_read_top_user_list + result["source_read_top_user_record_list"] = attrs.source_read_top_user_record_list + result["source_read_popular_query_record_list"] = ( + attrs.source_read_popular_query_record_list + ) + result["source_read_expensive_query_record_list"] = ( + attrs.source_read_expensive_query_record_list + ) + result["source_read_slow_query_record_list"] = ( + attrs.source_read_slow_query_record_list + ) + result["source_query_compute_cost_list"] = attrs.source_query_compute_cost_list + result["source_query_compute_cost_record_list"] = ( + attrs.source_query_compute_cost_record_list + ) + result["dbt_qualified_name"] = attrs.dbt_qualified_name + result["asset_dbt_workflow_last_updated"] = attrs.asset_dbt_workflow_last_updated + result["asset_dbt_alias"] = attrs.asset_dbt_alias + result["asset_dbt_meta"] = attrs.asset_dbt_meta + result["asset_dbt_unique_id"] = attrs.asset_dbt_unique_id + result["asset_dbt_account_name"] = attrs.asset_dbt_account_name + result["asset_dbt_project_name"] = attrs.asset_dbt_project_name + result["asset_dbt_package_name"] = attrs.asset_dbt_package_name + result["asset_dbt_job_name"] = attrs.asset_dbt_job_name + result["asset_dbt_job_schedule"] = attrs.asset_dbt_job_schedule + result["asset_dbt_job_status"] = attrs.asset_dbt_job_status + result["asset_dbt_test_status"] = attrs.asset_dbt_test_status + result["asset_dbt_job_schedule_cron_humanized"] = ( + attrs.asset_dbt_job_schedule_cron_humanized + ) + result["asset_dbt_job_last_run"] = attrs.asset_dbt_job_last_run + result["asset_dbt_job_last_run_url"] = attrs.asset_dbt_job_last_run_url + result["asset_dbt_job_last_run_created_at"] = ( + attrs.asset_dbt_job_last_run_created_at + ) + result["asset_dbt_job_last_run_updated_at"] = ( + attrs.asset_dbt_job_last_run_updated_at + ) + result["asset_dbt_job_last_run_dequed_at"] = attrs.asset_dbt_job_last_run_dequed_at + result["asset_dbt_job_last_run_started_at"] = ( + attrs.asset_dbt_job_last_run_started_at + ) + result["asset_dbt_job_last_run_total_duration"] = ( + attrs.asset_dbt_job_last_run_total_duration + ) + result["asset_dbt_job_last_run_total_duration_humanized"] = ( + attrs.asset_dbt_job_last_run_total_duration_humanized + ) + result["asset_dbt_job_last_run_queued_duration"] = ( + attrs.asset_dbt_job_last_run_queued_duration + ) + result["asset_dbt_job_last_run_queued_duration_humanized"] = ( + attrs.asset_dbt_job_last_run_queued_duration_humanized + ) + result["asset_dbt_job_last_run_run_duration"] = ( + attrs.asset_dbt_job_last_run_run_duration + ) + result["asset_dbt_job_last_run_run_duration_humanized"] = ( + attrs.asset_dbt_job_last_run_run_duration_humanized + ) + result["asset_dbt_job_last_run_git_branch"] = ( + attrs.asset_dbt_job_last_run_git_branch + ) + result["asset_dbt_job_last_run_git_sha"] = attrs.asset_dbt_job_last_run_git_sha + result["asset_dbt_job_last_run_status_message"] = ( + attrs.asset_dbt_job_last_run_status_message + ) + result["asset_dbt_job_last_run_owner_thread_id"] = ( + attrs.asset_dbt_job_last_run_owner_thread_id + ) + result["asset_dbt_job_last_run_executed_by_thread_id"] = ( + attrs.asset_dbt_job_last_run_executed_by_thread_id + ) + result["asset_dbt_job_last_run_artifacts_saved"] = ( + attrs.asset_dbt_job_last_run_artifacts_saved + ) + result["asset_dbt_job_last_run_artifact_s3_path"] = ( + attrs.asset_dbt_job_last_run_artifact_s3_path + ) + result["asset_dbt_job_last_run_has_docs_generated"] = ( + attrs.asset_dbt_job_last_run_has_docs_generated + ) + result["asset_dbt_job_last_run_has_sources_generated"] = ( + attrs.asset_dbt_job_last_run_has_sources_generated + ) + result["asset_dbt_job_last_run_notifications_sent"] = ( + attrs.asset_dbt_job_last_run_notifications_sent + ) + result["asset_dbt_job_next_run"] = attrs.asset_dbt_job_next_run + result["asset_dbt_job_next_run_humanized"] = attrs.asset_dbt_job_next_run_humanized + result["asset_dbt_environment_name"] = attrs.asset_dbt_environment_name + result["asset_dbt_environment_dbt_version"] = ( + attrs.asset_dbt_environment_dbt_version + ) + result["asset_dbt_tags"] = attrs.asset_dbt_tags + result["asset_dbt_semantic_layer_proxy_url"] = ( + attrs.asset_dbt_semantic_layer_proxy_url + ) + result["asset_dbt_source_freshness_criteria"] = ( + attrs.asset_dbt_source_freshness_criteria + ) + result["sample_data_url"] = attrs.sample_data_url + result["asset_tags"] = attrs.asset_tags + result["asset_mc_incident_names"] = attrs.asset_mc_incident_names + result["asset_mc_incident_qualified_names"] = ( + attrs.asset_mc_incident_qualified_names + ) + result["asset_mc_alert_qualified_names"] = attrs.asset_mc_alert_qualified_names + result["asset_mc_monitor_names"] = attrs.asset_mc_monitor_names + result["asset_mc_monitor_qualified_names"] = attrs.asset_mc_monitor_qualified_names + result["asset_mc_monitor_statuses"] = attrs.asset_mc_monitor_statuses + result["asset_mc_monitor_types"] = attrs.asset_mc_monitor_types + result["asset_mc_monitor_schedule_types"] = attrs.asset_mc_monitor_schedule_types + result["asset_mc_incident_types"] = attrs.asset_mc_incident_types + result["asset_mc_incident_sub_types"] = attrs.asset_mc_incident_sub_types + result["asset_mc_incident_severities"] = attrs.asset_mc_incident_severities + result["asset_mc_incident_priorities"] = attrs.asset_mc_incident_priorities + result["asset_mc_incident_states"] = attrs.asset_mc_incident_states + result["asset_mc_is_monitored"] = attrs.asset_mc_is_monitored + result["asset_mc_last_sync_run_at"] = attrs.asset_mc_last_sync_run_at + result["starred_by"] = attrs.starred_by + result["starred_details_list"] = attrs.starred_details_list + result["starred_count"] = attrs.starred_count + result["asset_anomalo_dq_status"] = attrs.asset_anomalo_dq_status + result["asset_anomalo_check_count"] = attrs.asset_anomalo_check_count + result["asset_anomalo_failed_check_count"] = attrs.asset_anomalo_failed_check_count + result["asset_anomalo_check_statuses"] = attrs.asset_anomalo_check_statuses + result["asset_anomalo_last_check_run_at"] = attrs.asset_anomalo_last_check_run_at + result["asset_anomalo_applied_check_types"] = ( + attrs.asset_anomalo_applied_check_types + ) + result["asset_anomalo_failed_check_types"] = attrs.asset_anomalo_failed_check_types + result["asset_anomalo_source_url"] = attrs.asset_anomalo_source_url + result["asset_soda_dq_status"] = attrs.asset_soda_dq_status + result["asset_soda_check_count"] = attrs.asset_soda_check_count + result["asset_soda_last_sync_run_at"] = attrs.asset_soda_last_sync_run_at + result["asset_soda_last_scan_at"] = attrs.asset_soda_last_scan_at + result["asset_soda_check_statuses"] = attrs.asset_soda_check_statuses + result["asset_soda_source_url"] = attrs.asset_soda_source_url + result["asset_icon"] = attrs.asset_icon + result["asset_external_dq_metadata_details"] = ( + attrs.asset_external_dq_metadata_details + ) + result["is_partial"] = attrs.is_partial + result["is_ai_generated"] = attrs.is_ai_generated + result["asset_cover_image"] = attrs.asset_cover_image + result["asset_theme_hex"] = attrs.asset_theme_hex + result["lexicographical_sort_order"] = attrs.lexicographical_sort_order + result["has_contract"] = attrs.has_contract + result["asset_redirect_guids"] = attrs.asset_redirect_guids + result["asset_policy_guids"] = attrs.asset_policy_guids + result["asset_policies_count"] = attrs.asset_policies_count + result["domain_guids"] = attrs.domain_guids + result["non_compliant_asset_policy_guids"] = attrs.non_compliant_asset_policy_guids + result["product_guids"] = attrs.product_guids + result["output_product_guids"] = attrs.output_product_guids + result["application_qualified_name"] = attrs.application_qualified_name + result["application_field_qualified_name"] = attrs.application_field_qualified_name + result["asset_user_defined_type"] = attrs.asset_user_defined_type + result["asset_internal_popularity_score"] = attrs.asset_internal_popularity_score + result["asset_dq_schedule_type"] = attrs.asset_dq_schedule_type + result["asset_dq_schedule_crontab"] = attrs.asset_dq_schedule_crontab + result["asset_dq_schedule_time_zone"] = attrs.asset_dq_schedule_time_zone + result["asset_dq_schedule_source_sync_status"] = ( + attrs.asset_dq_schedule_source_sync_status + ) + result["asset_dq_schedule_source_synced_at"] = ( + attrs.asset_dq_schedule_source_synced_at + ) + result["asset_dq_schedule_source_sync_error_message"] = ( + attrs.asset_dq_schedule_source_sync_error_message + ) + result["asset_dq_schedule_source_sync_error_code"] = ( + attrs.asset_dq_schedule_source_sync_error_code + ) + result["asset_dq_schedule_source_sync_raw_error"] = ( + attrs.asset_dq_schedule_source_sync_raw_error + ) + result["asset_dq_rule_attached_dimensions"] = ( + attrs.asset_dq_rule_attached_dimensions + ) + result["asset_dq_rule_failed_dimensions"] = attrs.asset_dq_rule_failed_dimensions + result["asset_dq_rule_passed_dimensions"] = attrs.asset_dq_rule_passed_dimensions + result["asset_dq_rule_attached_rule_types"] = ( + attrs.asset_dq_rule_attached_rule_types + ) + result["asset_dq_rule_failed_rule_types"] = attrs.asset_dq_rule_failed_rule_types + result["asset_dq_rule_passed_rule_types"] = attrs.asset_dq_rule_passed_rule_types + result["asset_dq_rule_result_tags"] = attrs.asset_dq_rule_result_tags + result["asset_dq_rule_last_run_at"] = attrs.asset_dq_rule_last_run_at + result["asset_dq_manual_run_status"] = attrs.asset_dq_manual_run_status + result["asset_dq_rule_total_count"] = attrs.asset_dq_rule_total_count + result["asset_dq_rule_failed_count"] = attrs.asset_dq_rule_failed_count + result["asset_dq_rule_passed_count"] = attrs.asset_dq_rule_passed_count + result["asset_dq_result"] = attrs.asset_dq_result + result["asset_dq_freshness_value"] = attrs.asset_dq_freshness_value + result["asset_dq_freshness_expectation"] = attrs.asset_dq_freshness_expectation + result["asset_dq_row_scope_filter_column_qualified_name"] = ( + attrs.asset_dq_row_scope_filter_column_qualified_name + ) + result["asset_space_qualified_name"] = attrs.asset_space_qualified_name + result["asset_space_name"] = attrs.asset_space_name + result["asset_gcp_dataplex_metadata_details"] = ( + attrs.asset_gcp_dataplex_metadata_details + ) + result["asset_gcp_dataplex_aspect_list"] = attrs.asset_gcp_dataplex_aspect_list + result["asset_gcp_dataplex_aspect_field_list"] = ( + attrs.asset_gcp_dataplex_aspect_field_list + ) + result["asset_smus_metadata_form_names"] = attrs.asset_smus_metadata_form_names + result["asset_smus_metadata_form_key_value_details"] = ( + attrs.asset_smus_metadata_form_key_value_details + ) + result["asset_smus_metadata_form_details"] = attrs.asset_smus_metadata_form_details + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _process_execution_to_nested( + process_execution: ProcessExecution, +) -> ProcessExecutionNested: + """Convert flat ProcessExecution to nested format.""" + attrs = ProcessExecutionAttributes() + _populate_process_execution_attrs(attrs, process_execution) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + process_execution, + _PROCESS_EXECUTION_REL_FIELDS, + ProcessExecutionRelationshipAttributes, + ) + return ProcessExecutionNested( + guid=process_execution.guid, + type_name=process_execution.type_name, + status=process_execution.status, + version=process_execution.version, + create_time=process_execution.create_time, + update_time=process_execution.update_time, + created_by=process_execution.created_by, + updated_by=process_execution.updated_by, + classifications=process_execution.classifications, + classification_names=process_execution.classification_names, + meanings=process_execution.meanings, + labels=process_execution.labels, + business_attributes=process_execution.business_attributes, + custom_attributes=process_execution.custom_attributes, + pending_tasks=process_execution.pending_tasks, + proxy=process_execution.proxy, + is_incomplete=process_execution.is_incomplete, + provenance_type=process_execution.provenance_type, + home_id=process_execution.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _process_execution_from_nested(nested: ProcessExecutionNested) -> ProcessExecution: + """Convert nested format to flat ProcessExecution.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else ProcessExecutionAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _PROCESS_EXECUTION_REL_FIELDS, + ProcessExecutionRelationshipAttributes, + ) + return ProcessExecution( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_process_execution_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _process_execution_to_nested_bytes( + process_execution: ProcessExecution, serde: Serde +) -> bytes: + """Convert flat ProcessExecution to nested JSON bytes.""" + return serde.encode(_process_execution_to_nested(process_execution)) + + +def _process_execution_from_nested_bytes(data: bytes, serde: Serde) -> ProcessExecution: + """Convert nested JSON bytes to flat ProcessExecution.""" + nested = serde.decode(data, ProcessExecutionNested) + return _process_execution_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + NumericRankField, + RelationField, + TextField, +) + +ProcessExecution.NAME = KeywordField("name", "name") +ProcessExecution.DISPLAY_NAME = KeywordField("displayName", "displayName") +ProcessExecution.DESCRIPTION = KeywordField("description", "description") +ProcessExecution.ASSET_SOURCE_README = KeywordTextField( + "assetSourceReadme", "assetSourceReadme", "assetSourceReadme.text" +) +ProcessExecution.USER_DESCRIPTION = KeywordField("userDescription", "userDescription") +ProcessExecution.ASSET_AI_GENERATED_DESCRIPTION = TextField( + "assetAiGeneratedDescription", "assetAiGeneratedDescription" +) +ProcessExecution.ASSET_AI_GENERATED_DESCRIPTION_CONFIDENCE = NumericField( + "assetAiGeneratedDescriptionConfidence", "assetAiGeneratedDescriptionConfidence" +) +ProcessExecution.ASSET_AI_GENERATED_DESCRIPTION_REASONING = KeywordField( + "assetAiGeneratedDescriptionReasoning", "assetAiGeneratedDescriptionReasoning" +) +ProcessExecution.TENANT_ID = KeywordField("tenantId", "tenantId") +ProcessExecution.CERTIFICATE_STATUS = KeywordTextField( + "certificateStatus", "certificateStatus", "certificateStatus.text" +) +ProcessExecution.CERTIFICATE_STATUS_MESSAGE = KeywordField( + "certificateStatusMessage", "certificateStatusMessage" +) +ProcessExecution.CERTIFICATE_UPDATED_BY = KeywordField( + "certificateUpdatedBy", "certificateUpdatedBy" +) +ProcessExecution.CERTIFICATE_UPDATED_AT = NumericField( + "certificateUpdatedAt", "certificateUpdatedAt" +) +ProcessExecution.ANNOUNCEMENT_TITLE = KeywordField( + "announcementTitle", "announcementTitle" +) +ProcessExecution.ANNOUNCEMENT_MESSAGE = KeywordField( + "announcementMessage", "announcementMessage" +) +ProcessExecution.ANNOUNCEMENT_TYPE = KeywordField( + "announcementType", "announcementType" +) +ProcessExecution.ANNOUNCEMENT_UPDATED_AT = NumericField( + "announcementUpdatedAt", "announcementUpdatedAt" +) +ProcessExecution.ANNOUNCEMENT_UPDATED_BY = KeywordField( + "announcementUpdatedBy", "announcementUpdatedBy" +) +ProcessExecution.OWNER_USERS = KeywordField("ownerUsers", "ownerUsers") +ProcessExecution.OWNER_GROUPS = KeywordField("ownerGroups", "ownerGroups") +ProcessExecution.ADMIN_USERS = KeywordField("adminUsers", "adminUsers") +ProcessExecution.ADMIN_GROUPS = KeywordField("adminGroups", "adminGroups") +ProcessExecution.VIEWER_USERS = KeywordField("viewerUsers", "viewerUsers") +ProcessExecution.VIEWER_GROUPS = KeywordField("viewerGroups", "viewerGroups") +ProcessExecution.CONNECTOR_NAME = KeywordField("connectorName", "connectorName") +ProcessExecution.CONNECTION_NAME = KeywordTextField( + "connectionName", "connectionName", "connectionName.text" +) +ProcessExecution.CONNECTION_QUALIFIED_NAME = KeywordTextField( + "connectionQualifiedName", "connectionQualifiedName", "connectionQualifiedName.text" +) +ProcessExecution.HAS_LINEAGE = BooleanField("__hasLineage", "__hasLineage") +ProcessExecution.IS_DISCOVERABLE = BooleanField("isDiscoverable", "isDiscoverable") +ProcessExecution.IS_EDITABLE = BooleanField("isEditable", "isEditable") +ProcessExecution.SUB_TYPE = KeywordField("subType", "subType") +ProcessExecution.VIEW_SCORE = NumericField("viewScore", "viewScore") +ProcessExecution.POPULARITY_SCORE = NumericField("popularityScore", "popularityScore") +ProcessExecution.SOURCE_OWNERS = KeywordField("sourceOwners", "sourceOwners") +ProcessExecution.ASSET_SOURCE_ID = KeywordField("assetSourceId", "assetSourceId") +ProcessExecution.SOURCE_CREATED_BY = KeywordField("sourceCreatedBy", "sourceCreatedBy") +ProcessExecution.SOURCE_CREATED_AT = NumericField("sourceCreatedAt", "sourceCreatedAt") +ProcessExecution.SOURCE_UPDATED_AT = NumericField("sourceUpdatedAt", "sourceUpdatedAt") +ProcessExecution.SOURCE_UPDATED_BY = KeywordField("sourceUpdatedBy", "sourceUpdatedBy") +ProcessExecution.SOURCE_URL = KeywordField("sourceURL", "sourceURL") +ProcessExecution.SOURCE_EMBED_URL = KeywordField("sourceEmbedURL", "sourceEmbedURL") +ProcessExecution.LAST_SYNC_WORKFLOW_NAME = KeywordField( + "lastSyncWorkflowName", "lastSyncWorkflowName" +) +ProcessExecution.LAST_SYNC_RUN_AT = NumericField("lastSyncRunAt", "lastSyncRunAt") +ProcessExecution.LAST_SYNC_RUN = KeywordField("lastSyncRun", "lastSyncRun") +ProcessExecution.ADMIN_ROLES = KeywordField("adminRoles", "adminRoles") +ProcessExecution.SOURCE_READ_COUNT = NumericField("sourceReadCount", "sourceReadCount") +ProcessExecution.SOURCE_READ_USER_COUNT = NumericField( + "sourceReadUserCount", "sourceReadUserCount" +) +ProcessExecution.SOURCE_LAST_READ_AT = NumericField( + "sourceLastReadAt", "sourceLastReadAt" +) +ProcessExecution.LAST_ROW_CHANGED_AT = NumericField( + "lastRowChangedAt", "lastRowChangedAt" +) +ProcessExecution.SOURCE_TOTAL_COST = NumericField("sourceTotalCost", "sourceTotalCost") +ProcessExecution.SOURCE_COST_UNIT = KeywordField("sourceCostUnit", "sourceCostUnit") +ProcessExecution.SOURCE_READ_QUERY_COST = NumericField( + "sourceReadQueryCost", "sourceReadQueryCost" +) +ProcessExecution.SOURCE_READ_RECENT_USER_LIST = KeywordField( + "sourceReadRecentUserList", "sourceReadRecentUserList" +) +ProcessExecution.SOURCE_READ_RECENT_USER_RECORD_LIST = KeywordField( + "sourceReadRecentUserRecordList", "sourceReadRecentUserRecordList" +) +ProcessExecution.SOURCE_READ_TOP_USER_LIST = KeywordField( + "sourceReadTopUserList", "sourceReadTopUserList" +) +ProcessExecution.SOURCE_READ_TOP_USER_RECORD_LIST = KeywordField( + "sourceReadTopUserRecordList", "sourceReadTopUserRecordList" +) +ProcessExecution.SOURCE_READ_POPULAR_QUERY_RECORD_LIST = KeywordField( + "sourceReadPopularQueryRecordList", "sourceReadPopularQueryRecordList" +) +ProcessExecution.SOURCE_READ_EXPENSIVE_QUERY_RECORD_LIST = KeywordField( + "sourceReadExpensiveQueryRecordList", "sourceReadExpensiveQueryRecordList" +) +ProcessExecution.SOURCE_READ_SLOW_QUERY_RECORD_LIST = KeywordField( + "sourceReadSlowQueryRecordList", "sourceReadSlowQueryRecordList" +) +ProcessExecution.SOURCE_QUERY_COMPUTE_COST_LIST = KeywordField( + "sourceQueryComputeCostList", "sourceQueryComputeCostList" +) +ProcessExecution.SOURCE_QUERY_COMPUTE_COST_RECORD_LIST = KeywordField( + "sourceQueryComputeCostRecordList", "sourceQueryComputeCostRecordList" +) +ProcessExecution.DBT_QUALIFIED_NAME = KeywordTextField( + "dbtQualifiedName", "dbtQualifiedName", "dbtQualifiedName.text" +) +ProcessExecution.ASSET_DBT_WORKFLOW_LAST_UPDATED = KeywordField( + "assetDbtWorkflowLastUpdated", "assetDbtWorkflowLastUpdated" +) +ProcessExecution.ASSET_DBT_ALIAS = KeywordField("assetDbtAlias", "assetDbtAlias") +ProcessExecution.ASSET_DBT_META = KeywordField("assetDbtMeta", "assetDbtMeta") +ProcessExecution.ASSET_DBT_UNIQUE_ID = KeywordField( + "assetDbtUniqueId", "assetDbtUniqueId" +) +ProcessExecution.ASSET_DBT_ACCOUNT_NAME = KeywordField( + "assetDbtAccountName", "assetDbtAccountName" +) +ProcessExecution.ASSET_DBT_PROJECT_NAME = KeywordField( + "assetDbtProjectName", "assetDbtProjectName" +) +ProcessExecution.ASSET_DBT_PACKAGE_NAME = KeywordField( + "assetDbtPackageName", "assetDbtPackageName" +) +ProcessExecution.ASSET_DBT_JOB_NAME = KeywordField("assetDbtJobName", "assetDbtJobName") +ProcessExecution.ASSET_DBT_JOB_SCHEDULE = KeywordField( + "assetDbtJobSchedule", "assetDbtJobSchedule" +) +ProcessExecution.ASSET_DBT_JOB_STATUS = KeywordField( + "assetDbtJobStatus", "assetDbtJobStatus" +) +ProcessExecution.ASSET_DBT_TEST_STATUS = KeywordField( + "assetDbtTestStatus", "assetDbtTestStatus" +) +ProcessExecution.ASSET_DBT_JOB_SCHEDULE_CRON_HUMANIZED = KeywordField( + "assetDbtJobScheduleCronHumanized", "assetDbtJobScheduleCronHumanized" +) +ProcessExecution.ASSET_DBT_JOB_LAST_RUN = NumericField( + "assetDbtJobLastRun", "assetDbtJobLastRun" +) +ProcessExecution.ASSET_DBT_JOB_LAST_RUN_URL = KeywordField( + "assetDbtJobLastRunUrl", "assetDbtJobLastRunUrl" +) +ProcessExecution.ASSET_DBT_JOB_LAST_RUN_CREATED_AT = NumericField( + "assetDbtJobLastRunCreatedAt", "assetDbtJobLastRunCreatedAt" +) +ProcessExecution.ASSET_DBT_JOB_LAST_RUN_UPDATED_AT = NumericField( + "assetDbtJobLastRunUpdatedAt", "assetDbtJobLastRunUpdatedAt" +) +ProcessExecution.ASSET_DBT_JOB_LAST_RUN_DEQUED_AT = NumericField( + "assetDbtJobLastRunDequedAt", "assetDbtJobLastRunDequedAt" +) +ProcessExecution.ASSET_DBT_JOB_LAST_RUN_STARTED_AT = NumericField( + "assetDbtJobLastRunStartedAt", "assetDbtJobLastRunStartedAt" +) +ProcessExecution.ASSET_DBT_JOB_LAST_RUN_TOTAL_DURATION = KeywordField( + "assetDbtJobLastRunTotalDuration", "assetDbtJobLastRunTotalDuration" +) +ProcessExecution.ASSET_DBT_JOB_LAST_RUN_TOTAL_DURATION_HUMANIZED = KeywordField( + "assetDbtJobLastRunTotalDurationHumanized", + "assetDbtJobLastRunTotalDurationHumanized", +) +ProcessExecution.ASSET_DBT_JOB_LAST_RUN_QUEUED_DURATION = KeywordField( + "assetDbtJobLastRunQueuedDuration", "assetDbtJobLastRunQueuedDuration" +) +ProcessExecution.ASSET_DBT_JOB_LAST_RUN_QUEUED_DURATION_HUMANIZED = KeywordField( + "assetDbtJobLastRunQueuedDurationHumanized", + "assetDbtJobLastRunQueuedDurationHumanized", +) +ProcessExecution.ASSET_DBT_JOB_LAST_RUN_RUN_DURATION = KeywordField( + "assetDbtJobLastRunRunDuration", "assetDbtJobLastRunRunDuration" +) +ProcessExecution.ASSET_DBT_JOB_LAST_RUN_RUN_DURATION_HUMANIZED = KeywordField( + "assetDbtJobLastRunRunDurationHumanized", "assetDbtJobLastRunRunDurationHumanized" +) +ProcessExecution.ASSET_DBT_JOB_LAST_RUN_GIT_BRANCH = KeywordTextField( + "assetDbtJobLastRunGitBranch", + "assetDbtJobLastRunGitBranch", + "assetDbtJobLastRunGitBranch.text", +) +ProcessExecution.ASSET_DBT_JOB_LAST_RUN_GIT_SHA = KeywordField( + "assetDbtJobLastRunGitSha", "assetDbtJobLastRunGitSha" +) +ProcessExecution.ASSET_DBT_JOB_LAST_RUN_STATUS_MESSAGE = KeywordField( + "assetDbtJobLastRunStatusMessage", "assetDbtJobLastRunStatusMessage" +) +ProcessExecution.ASSET_DBT_JOB_LAST_RUN_OWNER_THREAD_ID = KeywordField( + "assetDbtJobLastRunOwnerThreadId", "assetDbtJobLastRunOwnerThreadId" +) +ProcessExecution.ASSET_DBT_JOB_LAST_RUN_EXECUTED_BY_THREAD_ID = KeywordField( + "assetDbtJobLastRunExecutedByThreadId", "assetDbtJobLastRunExecutedByThreadId" +) +ProcessExecution.ASSET_DBT_JOB_LAST_RUN_ARTIFACTS_SAVED = BooleanField( + "assetDbtJobLastRunArtifactsSaved", "assetDbtJobLastRunArtifactsSaved" +) +ProcessExecution.ASSET_DBT_JOB_LAST_RUN_ARTIFACT_S3_PATH = KeywordField( + "assetDbtJobLastRunArtifactS3Path", "assetDbtJobLastRunArtifactS3Path" +) +ProcessExecution.ASSET_DBT_JOB_LAST_RUN_HAS_DOCS_GENERATED = BooleanField( + "assetDbtJobLastRunHasDocsGenerated", "assetDbtJobLastRunHasDocsGenerated" +) +ProcessExecution.ASSET_DBT_JOB_LAST_RUN_HAS_SOURCES_GENERATED = BooleanField( + "assetDbtJobLastRunHasSourcesGenerated", "assetDbtJobLastRunHasSourcesGenerated" +) +ProcessExecution.ASSET_DBT_JOB_LAST_RUN_NOTIFICATIONS_SENT = BooleanField( + "assetDbtJobLastRunNotificationsSent", "assetDbtJobLastRunNotificationsSent" +) +ProcessExecution.ASSET_DBT_JOB_NEXT_RUN = NumericField( + "assetDbtJobNextRun", "assetDbtJobNextRun" +) +ProcessExecution.ASSET_DBT_JOB_NEXT_RUN_HUMANIZED = KeywordField( + "assetDbtJobNextRunHumanized", "assetDbtJobNextRunHumanized" +) +ProcessExecution.ASSET_DBT_ENVIRONMENT_NAME = KeywordField( + "assetDbtEnvironmentName", "assetDbtEnvironmentName" +) +ProcessExecution.ASSET_DBT_ENVIRONMENT_DBT_VERSION = KeywordField( + "assetDbtEnvironmentDbtVersion", "assetDbtEnvironmentDbtVersion" +) +ProcessExecution.ASSET_DBT_TAGS = KeywordTextField( + "assetDbtTags", "assetDbtTags", "assetDbtTags.text" +) +ProcessExecution.ASSET_DBT_SEMANTIC_LAYER_PROXY_URL = KeywordField( + "assetDbtSemanticLayerProxyUrl", "assetDbtSemanticLayerProxyUrl" +) +ProcessExecution.ASSET_DBT_SOURCE_FRESHNESS_CRITERIA = KeywordField( + "assetDbtSourceFreshnessCriteria", "assetDbtSourceFreshnessCriteria" +) +ProcessExecution.SAMPLE_DATA_URL = KeywordTextField( + "sampleDataUrl", "sampleDataUrl", "sampleDataUrl.text" +) +ProcessExecution.ASSET_TAGS = KeywordTextField( + "assetTags", "assetTags", "assetTags.text" +) +ProcessExecution.ASSET_MC_INCIDENT_NAMES = KeywordField( + "assetMcIncidentNames", "assetMcIncidentNames" +) +ProcessExecution.ASSET_MC_INCIDENT_QUALIFIED_NAMES = KeywordTextField( + "assetMcIncidentQualifiedNames", + "assetMcIncidentQualifiedNames", + "assetMcIncidentQualifiedNames.text", +) +ProcessExecution.ASSET_MC_ALERT_QUALIFIED_NAMES = KeywordTextField( + "assetMcAlertQualifiedNames", + "assetMcAlertQualifiedNames", + "assetMcAlertQualifiedNames.text", +) +ProcessExecution.ASSET_MC_MONITOR_NAMES = KeywordField( + "assetMcMonitorNames", "assetMcMonitorNames" +) +ProcessExecution.ASSET_MC_MONITOR_QUALIFIED_NAMES = KeywordTextField( + "assetMcMonitorQualifiedNames", + "assetMcMonitorQualifiedNames", + "assetMcMonitorQualifiedNames.text", +) +ProcessExecution.ASSET_MC_MONITOR_STATUSES = KeywordField( + "assetMcMonitorStatuses", "assetMcMonitorStatuses" +) +ProcessExecution.ASSET_MC_MONITOR_TYPES = KeywordField( + "assetMcMonitorTypes", "assetMcMonitorTypes" +) +ProcessExecution.ASSET_MC_MONITOR_SCHEDULE_TYPES = KeywordField( + "assetMcMonitorScheduleTypes", "assetMcMonitorScheduleTypes" +) +ProcessExecution.ASSET_MC_INCIDENT_TYPES = KeywordField( + "assetMcIncidentTypes", "assetMcIncidentTypes" +) +ProcessExecution.ASSET_MC_INCIDENT_SUB_TYPES = KeywordField( + "assetMcIncidentSubTypes", "assetMcIncidentSubTypes" +) +ProcessExecution.ASSET_MC_INCIDENT_SEVERITIES = KeywordField( + "assetMcIncidentSeverities", "assetMcIncidentSeverities" +) +ProcessExecution.ASSET_MC_INCIDENT_PRIORITIES = KeywordField( + "assetMcIncidentPriorities", "assetMcIncidentPriorities" +) +ProcessExecution.ASSET_MC_INCIDENT_STATES = KeywordField( + "assetMcIncidentStates", "assetMcIncidentStates" +) +ProcessExecution.ASSET_MC_IS_MONITORED = BooleanField( + "assetMcIsMonitored", "assetMcIsMonitored" +) +ProcessExecution.ASSET_MC_LAST_SYNC_RUN_AT = NumericField( + "assetMcLastSyncRunAt", "assetMcLastSyncRunAt" +) +ProcessExecution.STARRED_BY = KeywordField("starredBy", "starredBy") +ProcessExecution.STARRED_DETAILS_LIST = KeywordField( + "starredDetailsList", "starredDetailsList" +) +ProcessExecution.STARRED_COUNT = NumericField("starredCount", "starredCount") +ProcessExecution.ASSET_ANOMALO_DQ_STATUS = KeywordField( + "assetAnomaloDQStatus", "assetAnomaloDQStatus" +) +ProcessExecution.ASSET_ANOMALO_CHECK_COUNT = NumericField( + "assetAnomaloCheckCount", "assetAnomaloCheckCount" +) +ProcessExecution.ASSET_ANOMALO_FAILED_CHECK_COUNT = NumericField( + "assetAnomaloFailedCheckCount", "assetAnomaloFailedCheckCount" +) +ProcessExecution.ASSET_ANOMALO_CHECK_STATUSES = KeywordField( + "assetAnomaloCheckStatuses", "assetAnomaloCheckStatuses" +) +ProcessExecution.ASSET_ANOMALO_LAST_CHECK_RUN_AT = NumericField( + "assetAnomaloLastCheckRunAt", "assetAnomaloLastCheckRunAt" +) +ProcessExecution.ASSET_ANOMALO_APPLIED_CHECK_TYPES = KeywordField( + "assetAnomaloAppliedCheckTypes", "assetAnomaloAppliedCheckTypes" +) +ProcessExecution.ASSET_ANOMALO_FAILED_CHECK_TYPES = KeywordField( + "assetAnomaloFailedCheckTypes", "assetAnomaloFailedCheckTypes" +) +ProcessExecution.ASSET_ANOMALO_SOURCE_URL = KeywordField( + "assetAnomaloSourceUrl", "assetAnomaloSourceUrl" +) +ProcessExecution.ASSET_SODA_DQ_STATUS = KeywordField( + "assetSodaDQStatus", "assetSodaDQStatus" +) +ProcessExecution.ASSET_SODA_CHECK_COUNT = NumericField( + "assetSodaCheckCount", "assetSodaCheckCount" +) +ProcessExecution.ASSET_SODA_LAST_SYNC_RUN_AT = NumericField( + "assetSodaLastSyncRunAt", "assetSodaLastSyncRunAt" +) +ProcessExecution.ASSET_SODA_LAST_SCAN_AT = NumericField( + "assetSodaLastScanAt", "assetSodaLastScanAt" +) +ProcessExecution.ASSET_SODA_CHECK_STATUSES = KeywordField( + "assetSodaCheckStatuses", "assetSodaCheckStatuses" +) +ProcessExecution.ASSET_SODA_SOURCE_URL = KeywordField( + "assetSodaSourceURL", "assetSodaSourceURL" +) +ProcessExecution.ASSET_ICON = KeywordField("assetIcon", "assetIcon") +ProcessExecution.ASSET_EXTERNAL_DQ_METADATA_DETAILS = KeywordField( + "assetExternalDQMetadataDetails", "assetExternalDQMetadataDetails" +) +ProcessExecution.IS_PARTIAL = BooleanField("isPartial", "isPartial") +ProcessExecution.IS_AI_GENERATED = BooleanField("isAIGenerated", "isAIGenerated") +ProcessExecution.ASSET_COVER_IMAGE = KeywordField("assetCoverImage", "assetCoverImage") +ProcessExecution.ASSET_THEME_HEX = KeywordField("assetThemeHex", "assetThemeHex") +ProcessExecution.LEXICOGRAPHICAL_SORT_ORDER = KeywordField( + "lexicographicalSortOrder", "lexicographicalSortOrder" +) +ProcessExecution.HAS_CONTRACT = BooleanField("hasContract", "hasContract") +ProcessExecution.ASSET_REDIRECT_GUIDS = KeywordField( + "assetRedirectGUIDs", "assetRedirectGUIDs" +) +ProcessExecution.ASSET_POLICY_GUIDS = KeywordField( + "assetPolicyGUIDs", "assetPolicyGUIDs" +) +ProcessExecution.ASSET_POLICIES_COUNT = NumericField( + "assetPoliciesCount", "assetPoliciesCount" +) +ProcessExecution.DOMAIN_GUIDS = KeywordField("domainGUIDs", "domainGUIDs") +ProcessExecution.NON_COMPLIANT_ASSET_POLICY_GUIDS = KeywordField( + "nonCompliantAssetPolicyGUIDs", "nonCompliantAssetPolicyGUIDs" +) +ProcessExecution.PRODUCT_GUIDS = KeywordField("productGUIDs", "productGUIDs") +ProcessExecution.OUTPUT_PRODUCT_GUIDS = KeywordField( + "outputProductGUIDs", "outputProductGUIDs" +) +ProcessExecution.APPLICATION_QUALIFIED_NAME = KeywordField( + "applicationQualifiedName", "applicationQualifiedName" +) +ProcessExecution.APPLICATION_FIELD_QUALIFIED_NAME = KeywordField( + "applicationFieldQualifiedName", "applicationFieldQualifiedName" +) +ProcessExecution.ASSET_USER_DEFINED_TYPE = KeywordField( + "assetUserDefinedType", "assetUserDefinedType" +) +ProcessExecution.ASSET_INTERNAL_POPULARITY_SCORE = NumericRankField( + "assetInternalPopularityScore", + "assetInternalPopularityScore", + "assetInternalPopularityScore.rank", +) +ProcessExecution.ASSET_DQ_SCHEDULE_TYPE = KeywordField( + "assetDQScheduleType", "assetDQScheduleType" +) +ProcessExecution.ASSET_DQ_SCHEDULE_CRONTAB = KeywordField( + "assetDQScheduleCrontab", "assetDQScheduleCrontab" +) +ProcessExecution.ASSET_DQ_SCHEDULE_TIME_ZONE = KeywordField( + "assetDQScheduleTimeZone", "assetDQScheduleTimeZone" +) +ProcessExecution.ASSET_DQ_SCHEDULE_SOURCE_SYNC_STATUS = KeywordField( + "assetDQScheduleSourceSyncStatus", "assetDQScheduleSourceSyncStatus" +) +ProcessExecution.ASSET_DQ_SCHEDULE_SOURCE_SYNCED_AT = NumericField( + "assetDQScheduleSourceSyncedAt", "assetDQScheduleSourceSyncedAt" +) +ProcessExecution.ASSET_DQ_SCHEDULE_SOURCE_SYNC_ERROR_MESSAGE = TextField( + "assetDQScheduleSourceSyncErrorMessage", "assetDQScheduleSourceSyncErrorMessage" +) +ProcessExecution.ASSET_DQ_SCHEDULE_SOURCE_SYNC_ERROR_CODE = KeywordField( + "assetDQScheduleSourceSyncErrorCode", "assetDQScheduleSourceSyncErrorCode" +) +ProcessExecution.ASSET_DQ_SCHEDULE_SOURCE_SYNC_RAW_ERROR = TextField( + "assetDQScheduleSourceSyncRawError", "assetDQScheduleSourceSyncRawError" +) +ProcessExecution.ASSET_DQ_RULE_ATTACHED_DIMENSIONS = KeywordField( + "assetDQRuleAttachedDimensions", "assetDQRuleAttachedDimensions" +) +ProcessExecution.ASSET_DQ_RULE_FAILED_DIMENSIONS = KeywordField( + "assetDQRuleFailedDimensions", "assetDQRuleFailedDimensions" +) +ProcessExecution.ASSET_DQ_RULE_PASSED_DIMENSIONS = KeywordField( + "assetDQRulePassedDimensions", "assetDQRulePassedDimensions" +) +ProcessExecution.ASSET_DQ_RULE_ATTACHED_RULE_TYPES = KeywordField( + "assetDQRuleAttachedRuleTypes", "assetDQRuleAttachedRuleTypes" +) +ProcessExecution.ASSET_DQ_RULE_FAILED_RULE_TYPES = KeywordField( + "assetDQRuleFailedRuleTypes", "assetDQRuleFailedRuleTypes" +) +ProcessExecution.ASSET_DQ_RULE_PASSED_RULE_TYPES = KeywordField( + "assetDQRulePassedRuleTypes", "assetDQRulePassedRuleTypes" +) +ProcessExecution.ASSET_DQ_RULE_RESULT_TAGS = KeywordField( + "assetDQRuleResultTags", "assetDQRuleResultTags" +) +ProcessExecution.ASSET_DQ_RULE_LAST_RUN_AT = NumericField( + "assetDQRuleLastRunAt", "assetDQRuleLastRunAt" +) +ProcessExecution.ASSET_DQ_MANUAL_RUN_STATUS = KeywordField( + "assetDQManualRunStatus", "assetDQManualRunStatus" +) +ProcessExecution.ASSET_DQ_RULE_TOTAL_COUNT = NumericField( + "assetDQRuleTotalCount", "assetDQRuleTotalCount" +) +ProcessExecution.ASSET_DQ_RULE_FAILED_COUNT = NumericField( + "assetDQRuleFailedCount", "assetDQRuleFailedCount" +) +ProcessExecution.ASSET_DQ_RULE_PASSED_COUNT = NumericField( + "assetDQRulePassedCount", "assetDQRulePassedCount" +) +ProcessExecution.ASSET_DQ_RESULT = KeywordField("assetDQResult", "assetDQResult") +ProcessExecution.ASSET_DQ_FRESHNESS_VALUE = NumericField( + "assetDQFreshnessValue", "assetDQFreshnessValue" +) +ProcessExecution.ASSET_DQ_FRESHNESS_EXPECTATION = NumericField( + "assetDQFreshnessExpectation", "assetDQFreshnessExpectation" +) +ProcessExecution.ASSET_DQ_ROW_SCOPE_FILTER_COLUMN_QUALIFIED_NAME = KeywordField( + "assetDQRowScopeFilterColumnQualifiedName", + "assetDQRowScopeFilterColumnQualifiedName", +) +ProcessExecution.ASSET_SPACE_QUALIFIED_NAME = KeywordField( + "assetSpaceQualifiedName", "assetSpaceQualifiedName" +) +ProcessExecution.ASSET_SPACE_NAME = KeywordField("assetSpaceName", "assetSpaceName") +ProcessExecution.ASSET_GCP_DATAPLEX_METADATA_DETAILS = KeywordField( + "assetGCPDataplexMetadataDetails", "assetGCPDataplexMetadataDetails" +) +ProcessExecution.ASSET_GCP_DATAPLEX_ASPECT_LIST = KeywordField( + "assetGCPDataplexAspectList", "assetGCPDataplexAspectList" +) +ProcessExecution.ASSET_GCP_DATAPLEX_ASPECT_FIELD_LIST = KeywordField( + "assetGCPDataplexAspectFieldList", "assetGCPDataplexAspectFieldList" +) +ProcessExecution.ASSET_SMUS_METADATA_FORM_NAMES = KeywordTextField( + "assetSmusMetadataFormNames", + "assetSmusMetadataFormNames", + "assetSmusMetadataFormNames.text", +) +ProcessExecution.ASSET_SMUS_METADATA_FORM_KEY_VALUE_DETAILS = KeywordTextField( + "assetSmusMetadataFormKeyValueDetails", + "assetSmusMetadataFormKeyValueDetails", + "assetSmusMetadataFormKeyValueDetails.text", +) +ProcessExecution.ASSET_SMUS_METADATA_FORM_DETAILS = KeywordField( + "assetSmusMetadataFormDetails", "assetSmusMetadataFormDetails" +) +ProcessExecution.ANOMALO_CHECKS = RelationField("anomaloChecks") +ProcessExecution.APPLICATION = RelationField("application") +ProcessExecution.APPLICATION_FIELD = RelationField("applicationField") +ProcessExecution.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +ProcessExecution.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +ProcessExecution.METRICS = RelationField("metrics") +ProcessExecution.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +ProcessExecution.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +ProcessExecution.MEANINGS = RelationField("meanings") +ProcessExecution.MC_MONITORS = RelationField("mcMonitors") +ProcessExecution.MC_INCIDENTS = RelationField("mcIncidents") +ProcessExecution.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +ProcessExecution.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +ProcessExecution.FILES = RelationField("files") +ProcessExecution.LINKS = RelationField("links") +ProcessExecution.README = RelationField("readme") +ProcessExecution.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +ProcessExecution.SODA_CHECKS = RelationField("sodaChecks") diff --git a/pyatlan_v9/model/assets/process_related.py b/pyatlan_v9/model/assets/process_related.py new file mode 100644 index 000000000..e274164ae --- /dev/null +++ b/pyatlan_v9/model/assets/process_related.py @@ -0,0 +1,104 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Process module. + +This module contains all Related{Type} classes for the Process type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import List, Union + +from msgspec import UNSET, UnsetType + +from .asset_related import RelatedAsset +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedProcess", + "RelatedBIProcess", + "RelatedColumnProcess", + "RelatedConnectionProcess", +] + + +class RelatedProcess(RelatedAsset): + """ + Related entity reference for Process assets. + + Extends RelatedAsset with Process-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Process" so it serializes correctly + + code: Union[str, None, UnsetType] = UNSET + """Code that ran within the process.""" + + sql: Union[str, None, UnsetType] = UNSET + """SQL query that ran to produce the outputs.""" + + parent_connection_process_qualified_name: Union[List[str], None, UnsetType] = UNSET + """""" + + ast: Union[str, None, UnsetType] = UNSET + """Parsed AST of the code or SQL statements that describe the logic of this process.""" + + additional_etl_context: Union[str, None, UnsetType] = UNSET + """Additional Context of the ETL pipeline/notebook which creates the process.""" + + ai_dataset_type: Union[str, None, UnsetType] = UNSET + """Dataset type for AI Model - dataset process.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Process" + + +class RelatedBIProcess(RelatedProcess): + """ + Related entity reference for BIProcess assets. + + Extends RelatedProcess with BIProcess-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "BIProcess" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "BIProcess" + + +class RelatedColumnProcess(RelatedProcess): + """ + Related entity reference for ColumnProcess assets. + + Extends RelatedProcess with ColumnProcess-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "ColumnProcess" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "ColumnProcess" + + +class RelatedConnectionProcess(RelatedProcess): + """ + Related entity reference for ConnectionProcess assets. + + Extends RelatedProcess with ConnectionProcess-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "ConnectionProcess" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "ConnectionProcess" diff --git a/pyatlan_v9/model/assets/purpose.py b/pyatlan_v9/model/assets/purpose.py new file mode 100644 index 000000000..1d2ddbca5 --- /dev/null +++ b/pyatlan_v9/model/assets/purpose.py @@ -0,0 +1,353 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2026 Atlan Pte. Ltd. + +"""Purpose asset model for pyatlan_v9.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Optional, Set, Union +from warnings import warn + +import msgspec +from msgspec import UNSET, UnsetType + +from pyatlan.model.enums import ( + AuthPolicyCategory, + AuthPolicyResourceCategory, + AuthPolicyType, + DataAction, + PurposeMetadataAction, +) +from pyatlan_v9.model.conversion_utils import ( + build_attributes_kwargs, + build_flat_kwargs, + merge_relationships, +) +from pyatlan_v9.model.core import AtlanTagName +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.structs import SourceTagAttachment +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .asset import Asset, AssetAttributes, AssetNested +from .auth_policy import AuthPolicy + +if TYPE_CHECKING: + from pyatlan_v9.client.atlan import AtlanClient + + +class PurposeClassification(msgspec.Struct, kw_only=True, rename="camel"): + """Classification view used by Purpose to retain source-tag attachments.""" + + type_name: Any = None + source_tag_attachments: list[SourceTagAttachment] = msgspec.field( + default_factory=list + ) + entity_status: Union[str, None] = None + + +@register_asset +class Purpose(Asset): + """Purpose asset in Atlan.""" + + type_name: Union[str, UnsetType] = "Purpose" + + is_access_control_enabled: Union[bool, None, UnsetType] = UNSET + deny_custom_metadata_guids: Union[Set[str], None, UnsetType] = UNSET + deny_asset_tabs: Union[Set[str], None, UnsetType] = UNSET + deny_asset_filters: Union[Set[str], None, UnsetType] = UNSET + deny_asset_types: Union[Set[str], None, UnsetType] = UNSET + deny_sidebar_tabs: Union[Set[str], None, UnsetType] = UNSET + deny_navigation_pages: Union[Set[str], None, UnsetType] = UNSET + default_navigation: Union[str, None, UnsetType] = UNSET + display_preferences: Union[Set[str], None, UnsetType] = UNSET + channel_link: Union[str, None, UnsetType] = UNSET + deny_asset_metadata_types: Union[Set[str], None, UnsetType] = UNSET + policies: Union[list[AuthPolicy], None, UnsetType] = UNSET + purpose_classifications: Union[list[Any], None, UnsetType] = UNSET + classifications: Union[list[PurposeClassification], None, UnsetType] = UNSET + + @property + def purpose_atlan_tags(self) -> Union[list[AtlanTagName], None]: + """Expose purpose classifications as AtlanTagName objects for parity.""" + if self.purpose_classifications in (UNSET, None): + return None + return [ + tag if isinstance(tag, AtlanTagName) else AtlanTagName(str(tag)) + for tag in self.purpose_classifications + ] + + @purpose_atlan_tags.setter + def purpose_atlan_tags(self, value: Union[list[AtlanTagName], None]) -> None: + if value is None: + self.purpose_classifications = None + else: + self.purpose_classifications = [str(tag) for tag in value] + + @classmethod + @init_guid + def creator(cls, *, name: str, atlan_tags: list[AtlanTagName]) -> "Purpose": + """Create a new Purpose asset.""" + validate_required_fields(["name", "atlan_tags"], [name, atlan_tags]) + return cls( + name=name, + qualified_name=name, + display_name=name, + description="", + is_access_control_enabled=True, + purpose_classifications=[str(tag) for tag in atlan_tags], + ) + + @classmethod + def updater( + cls, *, qualified_name: str, name: str, is_enabled: bool = True + ) -> "Purpose": + """Create a Purpose asset for update operations.""" + validate_required_fields( + ["qualified_name", "name", "is_enabled"], + [qualified_name, name, is_enabled], + ) + return cls( + qualified_name=qualified_name, + name=name, + is_access_control_enabled=is_enabled, + ) + + @classmethod + def create_for_modification( + cls, + qualified_name: str = "", + name: str = "", + is_enabled: bool = True, + ) -> "Purpose": + warn( + ( + "This method is deprecated, please use 'updater' " + "instead, which offers identical functionality." + ), + DeprecationWarning, + stacklevel=2, + ) + return cls.updater( + qualified_name=qualified_name, name=name, is_enabled=is_enabled + ) + + @classmethod + def create_metadata_policy( + cls, + *, + client: "AtlanClient", + name: str, + purpose_id: str, + policy_type: AuthPolicyType, + actions: Set[PurposeMetadataAction], + policy_groups: Optional[Set[str]] = None, + policy_users: Optional[Set[str]] = None, + all_users: bool = False, + ) -> AuthPolicy: + validate_required_fields( + ["client", "name", "purpose_id", "policy_type", "actions"], + [client, name, purpose_id, policy_type, actions], + ) + target_found = False + policy = AuthPolicy._create(name=name) + policy.policy_actions = {x.value for x in actions} + policy.policy_category = AuthPolicyCategory.PURPOSE.value + policy.policy_type = policy_type + policy.policy_resource_category = AuthPolicyResourceCategory.TAG.value + policy.policy_service_name = "atlas_tag" + policy.policy_sub_category = "metadata" + purpose = Purpose() + purpose.guid = purpose_id + policy.access_control = purpose + if all_users: + target_found = True + policy.policy_groups = {"public"} + else: + if policy_groups: + for group_name in policy_groups: + if not client.group_cache.get_id_for_name(group_name): + raise ValueError( + f"Provided group name {group_name} was not found in Atlan." + ) + target_found = True + policy.policy_groups = policy_groups + else: + policy.policy_groups = None + if policy_users: + for username in policy_users: + if not client.user_cache.get_id_for_name(username): + raise ValueError( + f"Provided username {username} was not found in Atlan." + ) + target_found = True + policy.policy_users = policy_users + else: + policy.policy_users = None + if target_found: + return policy + else: + raise ValueError("No user or group specified for the policy.") + + @classmethod + def create_data_policy( + cls, + *, + client: "AtlanClient", + name: str, + purpose_id: str, + policy_type: AuthPolicyType, + policy_groups: Optional[Set[str]] = None, + policy_users: Optional[Set[str]] = None, + all_users: bool = False, + ) -> AuthPolicy: + validate_required_fields( + ["client", "name", "purpose_id", "policy_type"], + [client, name, purpose_id, policy_type], + ) + policy = AuthPolicy._create(name=name) + policy.policy_actions = {DataAction.SELECT.value} + policy.policy_category = AuthPolicyCategory.PURPOSE.value + policy.policy_type = policy_type + policy.policy_resource_category = AuthPolicyResourceCategory.TAG.value + policy.policy_service_name = "atlas_tag" + policy.policy_sub_category = "data" + purpose = Purpose() + purpose.guid = purpose_id + policy.access_control = purpose + if all_users: + target_found = True + policy.policy_groups = {"public"} + else: + if policy_groups: + for group_name in policy_groups: + if not client.group_cache.get_id_for_name(group_name): + raise ValueError( + f"Provided group name {group_name} was not found in Atlan." + ) + target_found = True + policy.policy_groups = policy_groups + else: + policy.policy_groups = None + if policy_users: + for username in policy_users: + if not client.user_cache.get_id_for_name(username): + raise ValueError( + f"Provided username {username} was not found in Atlan." + ) + target_found = True + policy.policy_users = policy_users + else: + policy.policy_users = None + if target_found: + return policy + else: + raise ValueError("No user or group specified for the policy.") + + def trim_to_required(self) -> "Purpose": + """Return only required fields for updates.""" + return Purpose.updater(qualified_name=self.qualified_name, name=self.name) + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """Serialize the Purpose to JSON.""" + if serde is None: + serde = get_serde() + if nested: + return _purpose_to_nested_bytes(self, serde).decode("utf-8") + return serde.encode(self).decode("utf-8") + + @staticmethod + def from_json( + json_data: Union[str, bytes], serde: Serde | None = None + ) -> "Purpose": + """Deserialize a Purpose from nested API JSON.""" + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _purpose_from_nested_bytes(json_data, serde) + + +class PurposeAttributes(AssetAttributes): + """Purpose-specific nested attributes.""" + + is_access_control_enabled: Union[bool, None, UnsetType] = UNSET + deny_custom_metadata_guids: Union[Set[str], None, UnsetType] = UNSET + deny_asset_tabs: Union[Set[str], None, UnsetType] = UNSET + deny_asset_filters: Union[Set[str], None, UnsetType] = UNSET + deny_asset_types: Union[Set[str], None, UnsetType] = UNSET + deny_sidebar_tabs: Union[Set[str], None, UnsetType] = UNSET + deny_navigation_pages: Union[Set[str], None, UnsetType] = UNSET + default_navigation: Union[str, None, UnsetType] = UNSET + display_preferences: Union[Set[str], None, UnsetType] = UNSET + channel_link: Union[str, None, UnsetType] = UNSET + deny_asset_metadata_types: Union[Set[str], None, UnsetType] = UNSET + purpose_classifications: Union[list[Any], None, UnsetType] = UNSET + + +class PurposeNested(AssetNested): + """Purpose entity in nested API format.""" + + attributes: Union[PurposeAttributes, UnsetType] = UNSET + + +def _purpose_to_nested(purpose: Purpose) -> PurposeNested: + attrs_kwargs = build_attributes_kwargs(purpose, PurposeAttributes) + attrs = PurposeAttributes(**attrs_kwargs) + return PurposeNested( + guid=purpose.guid, + type_name=purpose.type_name, + status=purpose.status, + version=purpose.version, + create_time=purpose.create_time, + update_time=purpose.update_time, + created_by=purpose.created_by, + updated_by=purpose.updated_by, + classifications=purpose.classifications, + classification_names=purpose.classification_names, + meanings=purpose.meanings, + labels=purpose.labels, + business_attributes=purpose.business_attributes, + custom_attributes=purpose.custom_attributes, + pending_tasks=purpose.pending_tasks, + proxy=purpose.proxy, + is_incomplete=purpose.is_incomplete, + provenance_type=purpose.provenance_type, + home_id=purpose.home_id, + attributes=attrs, + ) + + +def _purpose_from_nested(nested: PurposeNested) -> Purpose: + attrs = nested.attributes if nested.attributes is not UNSET else PurposeAttributes() + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + [], + object, + ) + kwargs = build_flat_kwargs( + nested, attrs, merged_rels, AssetNested, PurposeAttributes + ) + purpose = Purpose(**kwargs) + if ( + purpose.classifications is not UNSET + and purpose.classifications is not None + and purpose.classifications + and isinstance(purpose.classifications[0], dict) + ): + purpose.classifications = [ + msgspec.convert(classification, type=PurposeClassification) + for classification in purpose.classifications + ] + return purpose + + +def _purpose_to_nested_bytes(purpose: Purpose, serde: Serde) -> bytes: + return serde.encode(_purpose_to_nested(purpose)) + + +def _purpose_from_nested_bytes(data: bytes, serde: Serde) -> Purpose: + nested = serde.decode(data, PurposeNested) + return _purpose_from_nested(nested) diff --git a/pyatlan_v9/model/assets/qlik.py b/pyatlan_v9/model/assets/qlik.py new file mode 100644 index 000000000..9c04e36c4 --- /dev/null +++ b/pyatlan_v9/model/assets/qlik.py @@ -0,0 +1,608 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Qlik asset model with flattened inheritance. + +This module provides: +- Qlik: Flat asset class (easy to use) +- QlikAttributes: Nested attributes struct (extends AssetAttributes) +- QlikNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Qlik(Asset): + """ + Base class for Qlik assets. + """ + + QLIK_ID: ClassVar[Any] = None + QLIK_QRI: ClassVar[Any] = None + QLIK_SPACE_ID: ClassVar[Any] = None + QLIK_SPACE_QUALIFIED_NAME: ClassVar[Any] = None + QLIK_APP_ID: ClassVar[Any] = None + QLIK_APP_QUALIFIED_NAME: ClassVar[Any] = None + QLIK_OWNER_ID: ClassVar[Any] = None + QLIK_IS_PUBLISHED: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Qlik" + + qlik_id: Union[str, None, UnsetType] = UNSET + """Identifier of this asset, from Qlik.""" + + qlik_qri: Union[str, None, UnsetType] = msgspec.field(default=UNSET, name="qlikQRI") + """Unique QRI of this asset, from Qlik.""" + + qlik_space_id: Union[str, None, UnsetType] = UNSET + """Identifier of the space in which this asset exists, from Qlik.""" + + qlik_space_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the space in which this asset exists.""" + + qlik_app_id: Union[str, None, UnsetType] = UNSET + """Identifier of the app in which this asset belongs, from Qlik.""" + + qlik_app_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the app where this asset belongs.""" + + qlik_owner_id: Union[str, None, UnsetType] = UNSET + """Identifier of the owner of this asset, in Qlik.""" + + qlik_is_published: Union[bool, None, UnsetType] = UNSET + """Whether this asset is published in Qlik (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Qlik" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _qlik_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Qlik: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Qlik instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _qlik_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class QlikAttributes(AssetAttributes): + """Qlik-specific attributes for nested API format.""" + + qlik_id: Union[str, None, UnsetType] = UNSET + """Identifier of this asset, from Qlik.""" + + qlik_qri: Union[str, None, UnsetType] = msgspec.field(default=UNSET, name="qlikQRI") + """Unique QRI of this asset, from Qlik.""" + + qlik_space_id: Union[str, None, UnsetType] = UNSET + """Identifier of the space in which this asset exists, from Qlik.""" + + qlik_space_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the space in which this asset exists.""" + + qlik_app_id: Union[str, None, UnsetType] = UNSET + """Identifier of the app in which this asset belongs, from Qlik.""" + + qlik_app_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the app where this asset belongs.""" + + qlik_owner_id: Union[str, None, UnsetType] = UNSET + """Identifier of the owner of this asset, in Qlik.""" + + qlik_is_published: Union[bool, None, UnsetType] = UNSET + """Whether this asset is published in Qlik (true) or not (false).""" + + +class QlikRelationshipAttributes(AssetRelationshipAttributes): + """Qlik-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class QlikNested(AssetNested): + """Qlik in nested API format for high-performance serialization.""" + + attributes: Union[QlikAttributes, UnsetType] = UNSET + relationship_attributes: Union[QlikRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[QlikRelationshipAttributes, UnsetType] = UNSET + remove_relationship_attributes: Union[QlikRelationshipAttributes, UnsetType] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_QLIK_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_qlik_attrs(attrs: QlikAttributes, obj: Qlik) -> None: + """Populate Qlik-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.qlik_id = obj.qlik_id + attrs.qlik_qri = obj.qlik_qri + attrs.qlik_space_id = obj.qlik_space_id + attrs.qlik_space_qualified_name = obj.qlik_space_qualified_name + attrs.qlik_app_id = obj.qlik_app_id + attrs.qlik_app_qualified_name = obj.qlik_app_qualified_name + attrs.qlik_owner_id = obj.qlik_owner_id + attrs.qlik_is_published = obj.qlik_is_published + + +def _extract_qlik_attrs(attrs: QlikAttributes) -> dict: + """Extract all Qlik attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["qlik_id"] = attrs.qlik_id + result["qlik_qri"] = attrs.qlik_qri + result["qlik_space_id"] = attrs.qlik_space_id + result["qlik_space_qualified_name"] = attrs.qlik_space_qualified_name + result["qlik_app_id"] = attrs.qlik_app_id + result["qlik_app_qualified_name"] = attrs.qlik_app_qualified_name + result["qlik_owner_id"] = attrs.qlik_owner_id + result["qlik_is_published"] = attrs.qlik_is_published + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _qlik_to_nested(qlik: Qlik) -> QlikNested: + """Convert flat Qlik to nested format.""" + attrs = QlikAttributes() + _populate_qlik_attrs(attrs, qlik) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + qlik, _QLIK_REL_FIELDS, QlikRelationshipAttributes + ) + return QlikNested( + guid=qlik.guid, + type_name=qlik.type_name, + status=qlik.status, + version=qlik.version, + create_time=qlik.create_time, + update_time=qlik.update_time, + created_by=qlik.created_by, + updated_by=qlik.updated_by, + classifications=qlik.classifications, + classification_names=qlik.classification_names, + meanings=qlik.meanings, + labels=qlik.labels, + business_attributes=qlik.business_attributes, + custom_attributes=qlik.custom_attributes, + pending_tasks=qlik.pending_tasks, + proxy=qlik.proxy, + is_incomplete=qlik.is_incomplete, + provenance_type=qlik.provenance_type, + home_id=qlik.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _qlik_from_nested(nested: QlikNested) -> Qlik: + """Convert nested format to flat Qlik.""" + attrs = nested.attributes if nested.attributes is not UNSET else QlikAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _QLIK_REL_FIELDS, + QlikRelationshipAttributes, + ) + return Qlik( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_qlik_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _qlik_to_nested_bytes(qlik: Qlik, serde: Serde) -> bytes: + """Convert flat Qlik to nested JSON bytes.""" + return serde.encode(_qlik_to_nested(qlik)) + + +def _qlik_from_nested_bytes(data: bytes, serde: Serde) -> Qlik: + """Convert nested JSON bytes to flat Qlik.""" + nested = serde.decode(data, QlikNested) + return _qlik_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + RelationField, +) + +Qlik.QLIK_ID = KeywordField("qlikId", "qlikId") +Qlik.QLIK_QRI = KeywordTextField("qlikQRI", "qlikQRI", "qlikQRI.text") +Qlik.QLIK_SPACE_ID = KeywordField("qlikSpaceId", "qlikSpaceId") +Qlik.QLIK_SPACE_QUALIFIED_NAME = KeywordTextField( + "qlikSpaceQualifiedName", "qlikSpaceQualifiedName", "qlikSpaceQualifiedName.text" +) +Qlik.QLIK_APP_ID = KeywordField("qlikAppId", "qlikAppId") +Qlik.QLIK_APP_QUALIFIED_NAME = KeywordTextField( + "qlikAppQualifiedName", "qlikAppQualifiedName", "qlikAppQualifiedName.text" +) +Qlik.QLIK_OWNER_ID = KeywordField("qlikOwnerId", "qlikOwnerId") +Qlik.QLIK_IS_PUBLISHED = BooleanField("qlikIsPublished", "qlikIsPublished") +Qlik.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Qlik.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Qlik.ANOMALO_CHECKS = RelationField("anomaloChecks") +Qlik.APPLICATION = RelationField("application") +Qlik.APPLICATION_FIELD = RelationField("applicationField") +Qlik.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Qlik.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Qlik.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Qlik.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Qlik.METRICS = RelationField("metrics") +Qlik.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Qlik.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Qlik.MEANINGS = RelationField("meanings") +Qlik.MC_MONITORS = RelationField("mcMonitors") +Qlik.MC_INCIDENTS = RelationField("mcIncidents") +Qlik.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Qlik.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Qlik.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Qlik.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Qlik.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Qlik.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Qlik.FILES = RelationField("files") +Qlik.LINKS = RelationField("links") +Qlik.README = RelationField("readme") +Qlik.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Qlik.SODA_CHECKS = RelationField("sodaChecks") +Qlik.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Qlik.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/qlik_app.py b/pyatlan_v9/model/assets/qlik_app.py new file mode 100644 index 000000000..922b80d12 --- /dev/null +++ b/pyatlan_v9/model/assets/qlik_app.py @@ -0,0 +1,696 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +QlikApp asset model with flattened inheritance. + +This module provides: +- QlikApp: Flat asset class (easy to use) +- QlikAppAttributes: Nested attributes struct (extends AssetAttributes) +- QlikAppNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .qlik_related import RelatedQlikSheet, RelatedQlikSpace + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class QlikApp(Asset): + """ + Instance of a Qlik app in Atlan. + """ + + QLIK_HAS_SECTION_ACCESS: ClassVar[Any] = None + QLIK_ORIGIN_APP_ID: ClassVar[Any] = None + QLIK_IS_ENCRYPTED: ClassVar[Any] = None + QLIK_IS_DIRECT_QUERY_MODE: ClassVar[Any] = None + QLIK_APP_STATIC_BYTE_SIZE: ClassVar[Any] = None + QLIK_ID: ClassVar[Any] = None + QLIK_QRI: ClassVar[Any] = None + QLIK_SPACE_ID: ClassVar[Any] = None + QLIK_SPACE_QUALIFIED_NAME: ClassVar[Any] = None + QLIK_APP_ID: ClassVar[Any] = None + QLIK_APP_QUALIFIED_NAME: ClassVar[Any] = None + QLIK_OWNER_ID: ClassVar[Any] = None + QLIK_IS_PUBLISHED: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + QLIK_SHEETS: ClassVar[Any] = None + QLIK_SPACE: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "QlikApp" + + qlik_has_section_access: Union[bool, None, UnsetType] = UNSET + """Whether section access or data masking is enabled on the source (true) or not (false).""" + + qlik_origin_app_id: Union[str, None, UnsetType] = UNSET + """Value of originAppId for this app.""" + + qlik_is_encrypted: Union[bool, None, UnsetType] = UNSET + """Whether this app is encrypted (true) or not (false).""" + + qlik_is_direct_query_mode: Union[bool, None, UnsetType] = UNSET + """Whether this app is in direct query mode (true) or not (false).""" + + qlik_app_static_byte_size: Union[int, None, UnsetType] = UNSET + """Static space used by this app, in bytes.""" + + qlik_id: Union[str, None, UnsetType] = UNSET + """Identifier of this asset, from Qlik.""" + + qlik_qri: Union[str, None, UnsetType] = msgspec.field(default=UNSET, name="qlikQRI") + """Unique QRI of this asset, from Qlik.""" + + qlik_space_id: Union[str, None, UnsetType] = UNSET + """Identifier of the space in which this asset exists, from Qlik.""" + + qlik_space_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the space in which this asset exists.""" + + qlik_app_id: Union[str, None, UnsetType] = UNSET + """Identifier of the app in which this asset belongs, from Qlik.""" + + qlik_app_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the app where this asset belongs.""" + + qlik_owner_id: Union[str, None, UnsetType] = UNSET + """Identifier of the owner of this asset, in Qlik.""" + + qlik_is_published: Union[bool, None, UnsetType] = UNSET + """Whether this asset is published in Qlik (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + qlik_sheets: Union[List[RelatedQlikSheet], None, UnsetType] = UNSET + """Sheets that exist within this app.""" + + qlik_space: Union[RelatedQlikSpace, None, UnsetType] = UNSET + """Space in which this app exists.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "QlikApp" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _qlik_app_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> QlikApp: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + QlikApp instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _qlik_app_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class QlikAppAttributes(AssetAttributes): + """QlikApp-specific attributes for nested API format.""" + + qlik_has_section_access: Union[bool, None, UnsetType] = UNSET + """Whether section access or data masking is enabled on the source (true) or not (false).""" + + qlik_origin_app_id: Union[str, None, UnsetType] = UNSET + """Value of originAppId for this app.""" + + qlik_is_encrypted: Union[bool, None, UnsetType] = UNSET + """Whether this app is encrypted (true) or not (false).""" + + qlik_is_direct_query_mode: Union[bool, None, UnsetType] = UNSET + """Whether this app is in direct query mode (true) or not (false).""" + + qlik_app_static_byte_size: Union[int, None, UnsetType] = UNSET + """Static space used by this app, in bytes.""" + + qlik_id: Union[str, None, UnsetType] = UNSET + """Identifier of this asset, from Qlik.""" + + qlik_qri: Union[str, None, UnsetType] = msgspec.field(default=UNSET, name="qlikQRI") + """Unique QRI of this asset, from Qlik.""" + + qlik_space_id: Union[str, None, UnsetType] = UNSET + """Identifier of the space in which this asset exists, from Qlik.""" + + qlik_space_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the space in which this asset exists.""" + + qlik_app_id: Union[str, None, UnsetType] = UNSET + """Identifier of the app in which this asset belongs, from Qlik.""" + + qlik_app_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the app where this asset belongs.""" + + qlik_owner_id: Union[str, None, UnsetType] = UNSET + """Identifier of the owner of this asset, in Qlik.""" + + qlik_is_published: Union[bool, None, UnsetType] = UNSET + """Whether this asset is published in Qlik (true) or not (false).""" + + +class QlikAppRelationshipAttributes(AssetRelationshipAttributes): + """QlikApp-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + qlik_sheets: Union[List[RelatedQlikSheet], None, UnsetType] = UNSET + """Sheets that exist within this app.""" + + qlik_space: Union[RelatedQlikSpace, None, UnsetType] = UNSET + """Space in which this app exists.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class QlikAppNested(AssetNested): + """QlikApp in nested API format for high-performance serialization.""" + + attributes: Union[QlikAppAttributes, UnsetType] = UNSET + relationship_attributes: Union[QlikAppRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[QlikAppRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[QlikAppRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_QLIK_APP_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "qlik_sheets", + "qlik_space", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_qlik_app_attrs(attrs: QlikAppAttributes, obj: QlikApp) -> None: + """Populate QlikApp-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.qlik_has_section_access = obj.qlik_has_section_access + attrs.qlik_origin_app_id = obj.qlik_origin_app_id + attrs.qlik_is_encrypted = obj.qlik_is_encrypted + attrs.qlik_is_direct_query_mode = obj.qlik_is_direct_query_mode + attrs.qlik_app_static_byte_size = obj.qlik_app_static_byte_size + attrs.qlik_id = obj.qlik_id + attrs.qlik_qri = obj.qlik_qri + attrs.qlik_space_id = obj.qlik_space_id + attrs.qlik_space_qualified_name = obj.qlik_space_qualified_name + attrs.qlik_app_id = obj.qlik_app_id + attrs.qlik_app_qualified_name = obj.qlik_app_qualified_name + attrs.qlik_owner_id = obj.qlik_owner_id + attrs.qlik_is_published = obj.qlik_is_published + + +def _extract_qlik_app_attrs(attrs: QlikAppAttributes) -> dict: + """Extract all QlikApp attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["qlik_has_section_access"] = attrs.qlik_has_section_access + result["qlik_origin_app_id"] = attrs.qlik_origin_app_id + result["qlik_is_encrypted"] = attrs.qlik_is_encrypted + result["qlik_is_direct_query_mode"] = attrs.qlik_is_direct_query_mode + result["qlik_app_static_byte_size"] = attrs.qlik_app_static_byte_size + result["qlik_id"] = attrs.qlik_id + result["qlik_qri"] = attrs.qlik_qri + result["qlik_space_id"] = attrs.qlik_space_id + result["qlik_space_qualified_name"] = attrs.qlik_space_qualified_name + result["qlik_app_id"] = attrs.qlik_app_id + result["qlik_app_qualified_name"] = attrs.qlik_app_qualified_name + result["qlik_owner_id"] = attrs.qlik_owner_id + result["qlik_is_published"] = attrs.qlik_is_published + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _qlik_app_to_nested(qlik_app: QlikApp) -> QlikAppNested: + """Convert flat QlikApp to nested format.""" + attrs = QlikAppAttributes() + _populate_qlik_app_attrs(attrs, qlik_app) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + qlik_app, _QLIK_APP_REL_FIELDS, QlikAppRelationshipAttributes + ) + return QlikAppNested( + guid=qlik_app.guid, + type_name=qlik_app.type_name, + status=qlik_app.status, + version=qlik_app.version, + create_time=qlik_app.create_time, + update_time=qlik_app.update_time, + created_by=qlik_app.created_by, + updated_by=qlik_app.updated_by, + classifications=qlik_app.classifications, + classification_names=qlik_app.classification_names, + meanings=qlik_app.meanings, + labels=qlik_app.labels, + business_attributes=qlik_app.business_attributes, + custom_attributes=qlik_app.custom_attributes, + pending_tasks=qlik_app.pending_tasks, + proxy=qlik_app.proxy, + is_incomplete=qlik_app.is_incomplete, + provenance_type=qlik_app.provenance_type, + home_id=qlik_app.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _qlik_app_from_nested(nested: QlikAppNested) -> QlikApp: + """Convert nested format to flat QlikApp.""" + attrs = nested.attributes if nested.attributes is not UNSET else QlikAppAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _QLIK_APP_REL_FIELDS, + QlikAppRelationshipAttributes, + ) + return QlikApp( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_qlik_app_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _qlik_app_to_nested_bytes(qlik_app: QlikApp, serde: Serde) -> bytes: + """Convert flat QlikApp to nested JSON bytes.""" + return serde.encode(_qlik_app_to_nested(qlik_app)) + + +def _qlik_app_from_nested_bytes(data: bytes, serde: Serde) -> QlikApp: + """Convert nested JSON bytes to flat QlikApp.""" + nested = serde.decode(data, QlikAppNested) + return _qlik_app_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +QlikApp.QLIK_HAS_SECTION_ACCESS = BooleanField( + "qlikHasSectionAccess", "qlikHasSectionAccess" +) +QlikApp.QLIK_ORIGIN_APP_ID = KeywordField("qlikOriginAppId", "qlikOriginAppId") +QlikApp.QLIK_IS_ENCRYPTED = BooleanField("qlikIsEncrypted", "qlikIsEncrypted") +QlikApp.QLIK_IS_DIRECT_QUERY_MODE = BooleanField( + "qlikIsDirectQueryMode", "qlikIsDirectQueryMode" +) +QlikApp.QLIK_APP_STATIC_BYTE_SIZE = NumericField( + "qlikAppStaticByteSize", "qlikAppStaticByteSize" +) +QlikApp.QLIK_ID = KeywordField("qlikId", "qlikId") +QlikApp.QLIK_QRI = KeywordTextField("qlikQRI", "qlikQRI", "qlikQRI.text") +QlikApp.QLIK_SPACE_ID = KeywordField("qlikSpaceId", "qlikSpaceId") +QlikApp.QLIK_SPACE_QUALIFIED_NAME = KeywordTextField( + "qlikSpaceQualifiedName", "qlikSpaceQualifiedName", "qlikSpaceQualifiedName.text" +) +QlikApp.QLIK_APP_ID = KeywordField("qlikAppId", "qlikAppId") +QlikApp.QLIK_APP_QUALIFIED_NAME = KeywordTextField( + "qlikAppQualifiedName", "qlikAppQualifiedName", "qlikAppQualifiedName.text" +) +QlikApp.QLIK_OWNER_ID = KeywordField("qlikOwnerId", "qlikOwnerId") +QlikApp.QLIK_IS_PUBLISHED = BooleanField("qlikIsPublished", "qlikIsPublished") +QlikApp.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +QlikApp.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +QlikApp.ANOMALO_CHECKS = RelationField("anomaloChecks") +QlikApp.APPLICATION = RelationField("application") +QlikApp.APPLICATION_FIELD = RelationField("applicationField") +QlikApp.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +QlikApp.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +QlikApp.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +QlikApp.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +QlikApp.METRICS = RelationField("metrics") +QlikApp.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +QlikApp.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +QlikApp.MEANINGS = RelationField("meanings") +QlikApp.MC_MONITORS = RelationField("mcMonitors") +QlikApp.MC_INCIDENTS = RelationField("mcIncidents") +QlikApp.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +QlikApp.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +QlikApp.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +QlikApp.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +QlikApp.QLIK_SHEETS = RelationField("qlikSheets") +QlikApp.QLIK_SPACE = RelationField("qlikSpace") +QlikApp.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +QlikApp.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +QlikApp.FILES = RelationField("files") +QlikApp.LINKS = RelationField("links") +QlikApp.README = RelationField("readme") +QlikApp.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +QlikApp.SODA_CHECKS = RelationField("sodaChecks") +QlikApp.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +QlikApp.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/qlik_chart.py b/pyatlan_v9/model/assets/qlik_chart.py new file mode 100644 index 000000000..c6ee281d6 --- /dev/null +++ b/pyatlan_v9/model/assets/qlik_chart.py @@ -0,0 +1,683 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +QlikChart asset model with flattened inheritance. + +This module provides: +- QlikChart: Flat asset class (easy to use) +- QlikChartAttributes: Nested attributes struct (extends AssetAttributes) +- QlikChartNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .qlik_related import RelatedQlikColumn, RelatedQlikSheet + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class QlikChart(Asset): + """ + Instance of a Qlik chart in Atlan. + """ + + QLIK_CHART_SUBTITLE: ClassVar[Any] = None + QLIK_CHART_FOOTNOTE: ClassVar[Any] = None + QLIK_ORIENTATION: ClassVar[Any] = None + QLIK_TYPE: ClassVar[Any] = None + QLIK_ID: ClassVar[Any] = None + QLIK_QRI: ClassVar[Any] = None + QLIK_SPACE_ID: ClassVar[Any] = None + QLIK_SPACE_QUALIFIED_NAME: ClassVar[Any] = None + QLIK_APP_ID: ClassVar[Any] = None + QLIK_APP_QUALIFIED_NAME: ClassVar[Any] = None + QLIK_OWNER_ID: ClassVar[Any] = None + QLIK_IS_PUBLISHED: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + QLIK_SHEET: ClassVar[Any] = None + QLIK_COLUMNS: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "QlikChart" + + qlik_chart_subtitle: Union[str, None, UnsetType] = UNSET + """Subtitle of this chart.""" + + qlik_chart_footnote: Union[str, None, UnsetType] = UNSET + """Footnote of this chart.""" + + qlik_orientation: Union[str, None, UnsetType] = UNSET + """Orientation of this chart.""" + + qlik_type: Union[str, None, UnsetType] = UNSET + """Subtype of this chart, for example: bar, graph, pie, etc.""" + + qlik_id: Union[str, None, UnsetType] = UNSET + """Identifier of this asset, from Qlik.""" + + qlik_qri: Union[str, None, UnsetType] = msgspec.field(default=UNSET, name="qlikQRI") + """Unique QRI of this asset, from Qlik.""" + + qlik_space_id: Union[str, None, UnsetType] = UNSET + """Identifier of the space in which this asset exists, from Qlik.""" + + qlik_space_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the space in which this asset exists.""" + + qlik_app_id: Union[str, None, UnsetType] = UNSET + """Identifier of the app in which this asset belongs, from Qlik.""" + + qlik_app_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the app where this asset belongs.""" + + qlik_owner_id: Union[str, None, UnsetType] = UNSET + """Identifier of the owner of this asset, in Qlik.""" + + qlik_is_published: Union[bool, None, UnsetType] = UNSET + """Whether this asset is published in Qlik (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + qlik_sheet: Union[RelatedQlikSheet, None, UnsetType] = UNSET + """Sheet in which this chart exists.""" + + qlik_columns: Union[List[RelatedQlikColumn], None, UnsetType] = UNSET + """Columns contained in the chart.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "QlikChart" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _qlik_chart_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> QlikChart: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + QlikChart instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _qlik_chart_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class QlikChartAttributes(AssetAttributes): + """QlikChart-specific attributes for nested API format.""" + + qlik_chart_subtitle: Union[str, None, UnsetType] = UNSET + """Subtitle of this chart.""" + + qlik_chart_footnote: Union[str, None, UnsetType] = UNSET + """Footnote of this chart.""" + + qlik_orientation: Union[str, None, UnsetType] = UNSET + """Orientation of this chart.""" + + qlik_type: Union[str, None, UnsetType] = UNSET + """Subtype of this chart, for example: bar, graph, pie, etc.""" + + qlik_id: Union[str, None, UnsetType] = UNSET + """Identifier of this asset, from Qlik.""" + + qlik_qri: Union[str, None, UnsetType] = msgspec.field(default=UNSET, name="qlikQRI") + """Unique QRI of this asset, from Qlik.""" + + qlik_space_id: Union[str, None, UnsetType] = UNSET + """Identifier of the space in which this asset exists, from Qlik.""" + + qlik_space_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the space in which this asset exists.""" + + qlik_app_id: Union[str, None, UnsetType] = UNSET + """Identifier of the app in which this asset belongs, from Qlik.""" + + qlik_app_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the app where this asset belongs.""" + + qlik_owner_id: Union[str, None, UnsetType] = UNSET + """Identifier of the owner of this asset, in Qlik.""" + + qlik_is_published: Union[bool, None, UnsetType] = UNSET + """Whether this asset is published in Qlik (true) or not (false).""" + + +class QlikChartRelationshipAttributes(AssetRelationshipAttributes): + """QlikChart-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + qlik_sheet: Union[RelatedQlikSheet, None, UnsetType] = UNSET + """Sheet in which this chart exists.""" + + qlik_columns: Union[List[RelatedQlikColumn], None, UnsetType] = UNSET + """Columns contained in the chart.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class QlikChartNested(AssetNested): + """QlikChart in nested API format for high-performance serialization.""" + + attributes: Union[QlikChartAttributes, UnsetType] = UNSET + relationship_attributes: Union[QlikChartRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + QlikChartRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + QlikChartRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_QLIK_CHART_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "qlik_sheet", + "qlik_columns", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_qlik_chart_attrs(attrs: QlikChartAttributes, obj: QlikChart) -> None: + """Populate QlikChart-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.qlik_chart_subtitle = obj.qlik_chart_subtitle + attrs.qlik_chart_footnote = obj.qlik_chart_footnote + attrs.qlik_orientation = obj.qlik_orientation + attrs.qlik_type = obj.qlik_type + attrs.qlik_id = obj.qlik_id + attrs.qlik_qri = obj.qlik_qri + attrs.qlik_space_id = obj.qlik_space_id + attrs.qlik_space_qualified_name = obj.qlik_space_qualified_name + attrs.qlik_app_id = obj.qlik_app_id + attrs.qlik_app_qualified_name = obj.qlik_app_qualified_name + attrs.qlik_owner_id = obj.qlik_owner_id + attrs.qlik_is_published = obj.qlik_is_published + + +def _extract_qlik_chart_attrs(attrs: QlikChartAttributes) -> dict: + """Extract all QlikChart attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["qlik_chart_subtitle"] = attrs.qlik_chart_subtitle + result["qlik_chart_footnote"] = attrs.qlik_chart_footnote + result["qlik_orientation"] = attrs.qlik_orientation + result["qlik_type"] = attrs.qlik_type + result["qlik_id"] = attrs.qlik_id + result["qlik_qri"] = attrs.qlik_qri + result["qlik_space_id"] = attrs.qlik_space_id + result["qlik_space_qualified_name"] = attrs.qlik_space_qualified_name + result["qlik_app_id"] = attrs.qlik_app_id + result["qlik_app_qualified_name"] = attrs.qlik_app_qualified_name + result["qlik_owner_id"] = attrs.qlik_owner_id + result["qlik_is_published"] = attrs.qlik_is_published + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _qlik_chart_to_nested(qlik_chart: QlikChart) -> QlikChartNested: + """Convert flat QlikChart to nested format.""" + attrs = QlikChartAttributes() + _populate_qlik_chart_attrs(attrs, qlik_chart) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + qlik_chart, _QLIK_CHART_REL_FIELDS, QlikChartRelationshipAttributes + ) + return QlikChartNested( + guid=qlik_chart.guid, + type_name=qlik_chart.type_name, + status=qlik_chart.status, + version=qlik_chart.version, + create_time=qlik_chart.create_time, + update_time=qlik_chart.update_time, + created_by=qlik_chart.created_by, + updated_by=qlik_chart.updated_by, + classifications=qlik_chart.classifications, + classification_names=qlik_chart.classification_names, + meanings=qlik_chart.meanings, + labels=qlik_chart.labels, + business_attributes=qlik_chart.business_attributes, + custom_attributes=qlik_chart.custom_attributes, + pending_tasks=qlik_chart.pending_tasks, + proxy=qlik_chart.proxy, + is_incomplete=qlik_chart.is_incomplete, + provenance_type=qlik_chart.provenance_type, + home_id=qlik_chart.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _qlik_chart_from_nested(nested: QlikChartNested) -> QlikChart: + """Convert nested format to flat QlikChart.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else QlikChartAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _QLIK_CHART_REL_FIELDS, + QlikChartRelationshipAttributes, + ) + return QlikChart( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_qlik_chart_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _qlik_chart_to_nested_bytes(qlik_chart: QlikChart, serde: Serde) -> bytes: + """Convert flat QlikChart to nested JSON bytes.""" + return serde.encode(_qlik_chart_to_nested(qlik_chart)) + + +def _qlik_chart_from_nested_bytes(data: bytes, serde: Serde) -> QlikChart: + """Convert nested JSON bytes to flat QlikChart.""" + nested = serde.decode(data, QlikChartNested) + return _qlik_chart_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + RelationField, +) + +QlikChart.QLIK_CHART_SUBTITLE = KeywordField("qlikChartSubtitle", "qlikChartSubtitle") +QlikChart.QLIK_CHART_FOOTNOTE = KeywordField("qlikChartFootnote", "qlikChartFootnote") +QlikChart.QLIK_ORIENTATION = KeywordField("qlikOrientation", "qlikOrientation") +QlikChart.QLIK_TYPE = KeywordField("qlikType", "qlikType") +QlikChart.QLIK_ID = KeywordField("qlikId", "qlikId") +QlikChart.QLIK_QRI = KeywordTextField("qlikQRI", "qlikQRI", "qlikQRI.text") +QlikChart.QLIK_SPACE_ID = KeywordField("qlikSpaceId", "qlikSpaceId") +QlikChart.QLIK_SPACE_QUALIFIED_NAME = KeywordTextField( + "qlikSpaceQualifiedName", "qlikSpaceQualifiedName", "qlikSpaceQualifiedName.text" +) +QlikChart.QLIK_APP_ID = KeywordField("qlikAppId", "qlikAppId") +QlikChart.QLIK_APP_QUALIFIED_NAME = KeywordTextField( + "qlikAppQualifiedName", "qlikAppQualifiedName", "qlikAppQualifiedName.text" +) +QlikChart.QLIK_OWNER_ID = KeywordField("qlikOwnerId", "qlikOwnerId") +QlikChart.QLIK_IS_PUBLISHED = BooleanField("qlikIsPublished", "qlikIsPublished") +QlikChart.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +QlikChart.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +QlikChart.ANOMALO_CHECKS = RelationField("anomaloChecks") +QlikChart.APPLICATION = RelationField("application") +QlikChart.APPLICATION_FIELD = RelationField("applicationField") +QlikChart.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +QlikChart.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +QlikChart.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +QlikChart.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +QlikChart.METRICS = RelationField("metrics") +QlikChart.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +QlikChart.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +QlikChart.MEANINGS = RelationField("meanings") +QlikChart.MC_MONITORS = RelationField("mcMonitors") +QlikChart.MC_INCIDENTS = RelationField("mcIncidents") +QlikChart.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +QlikChart.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +QlikChart.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +QlikChart.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +QlikChart.QLIK_SHEET = RelationField("qlikSheet") +QlikChart.QLIK_COLUMNS = RelationField("qlikColumns") +QlikChart.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +QlikChart.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +QlikChart.FILES = RelationField("files") +QlikChart.LINKS = RelationField("links") +QlikChart.README = RelationField("readme") +QlikChart.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +QlikChart.SODA_CHECKS = RelationField("sodaChecks") +QlikChart.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +QlikChart.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/qlik_column.py b/pyatlan_v9/model/assets/qlik_column.py new file mode 100644 index 000000000..3c3735da4 --- /dev/null +++ b/pyatlan_v9/model/assets/qlik_column.py @@ -0,0 +1,694 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +QlikColumn asset model with flattened inheritance. + +This module provides: +- QlikColumn: Flat asset class (easy to use) +- QlikColumnAttributes: Nested attributes struct (extends AssetAttributes) +- QlikColumnNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .qlik_related import RelatedQlikChart, RelatedQlikDataset, RelatedQlikSheet + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class QlikColumn(Asset): + """ + Instance of a Qlik Columns in Atlan. + """ + + QLIK_COLUMN_NAME: ClassVar[Any] = None + QLIK_DATA_TYPE: ClassVar[Any] = None + QLIK_COLUMN_TYPE: ClassVar[Any] = None + QLIK_PARENT_QUALIFIED_NAME: ClassVar[Any] = None + QLIK_ID: ClassVar[Any] = None + QLIK_QRI: ClassVar[Any] = None + QLIK_SPACE_ID: ClassVar[Any] = None + QLIK_SPACE_QUALIFIED_NAME: ClassVar[Any] = None + QLIK_APP_ID: ClassVar[Any] = None + QLIK_APP_QUALIFIED_NAME: ClassVar[Any] = None + QLIK_OWNER_ID: ClassVar[Any] = None + QLIK_IS_PUBLISHED: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + QLIK_SHEET: ClassVar[Any] = None + QLIK_DATASET: ClassVar[Any] = None + QLIK_CHART: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "QlikColumn" + + qlik_column_name: Union[str, None, UnsetType] = UNSET + """Qlik Column name.""" + + qlik_data_type: Union[str, None, UnsetType] = UNSET + """Data type of the Qlik Column.""" + + qlik_column_type: Union[str, None, UnsetType] = UNSET + """Column type can be: Dimension, Measure or Normal.""" + + qlik_parent_qualified_name: Union[str, None, UnsetType] = UNSET + """Parent Qualified name of column.""" + + qlik_id: Union[str, None, UnsetType] = UNSET + """Identifier of this asset, from Qlik.""" + + qlik_qri: Union[str, None, UnsetType] = msgspec.field(default=UNSET, name="qlikQRI") + """Unique QRI of this asset, from Qlik.""" + + qlik_space_id: Union[str, None, UnsetType] = UNSET + """Identifier of the space in which this asset exists, from Qlik.""" + + qlik_space_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the space in which this asset exists.""" + + qlik_app_id: Union[str, None, UnsetType] = UNSET + """Identifier of the app in which this asset belongs, from Qlik.""" + + qlik_app_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the app where this asset belongs.""" + + qlik_owner_id: Union[str, None, UnsetType] = UNSET + """Identifier of the owner of this asset, in Qlik.""" + + qlik_is_published: Union[bool, None, UnsetType] = UNSET + """Whether this asset is published in Qlik (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + qlik_sheet: Union[RelatedQlikSheet, None, UnsetType] = UNSET + """Parent sheet containing the columns.""" + + qlik_dataset: Union[RelatedQlikDataset, None, UnsetType] = UNSET + """Parent dataset containing the columns.""" + + qlik_chart: Union[RelatedQlikChart, None, UnsetType] = UNSET + """Parent chart containing the columns.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "QlikColumn" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _qlik_column_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> QlikColumn: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + QlikColumn instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _qlik_column_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class QlikColumnAttributes(AssetAttributes): + """QlikColumn-specific attributes for nested API format.""" + + qlik_column_name: Union[str, None, UnsetType] = UNSET + """Qlik Column name.""" + + qlik_data_type: Union[str, None, UnsetType] = UNSET + """Data type of the Qlik Column.""" + + qlik_column_type: Union[str, None, UnsetType] = UNSET + """Column type can be: Dimension, Measure or Normal.""" + + qlik_parent_qualified_name: Union[str, None, UnsetType] = UNSET + """Parent Qualified name of column.""" + + qlik_id: Union[str, None, UnsetType] = UNSET + """Identifier of this asset, from Qlik.""" + + qlik_qri: Union[str, None, UnsetType] = msgspec.field(default=UNSET, name="qlikQRI") + """Unique QRI of this asset, from Qlik.""" + + qlik_space_id: Union[str, None, UnsetType] = UNSET + """Identifier of the space in which this asset exists, from Qlik.""" + + qlik_space_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the space in which this asset exists.""" + + qlik_app_id: Union[str, None, UnsetType] = UNSET + """Identifier of the app in which this asset belongs, from Qlik.""" + + qlik_app_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the app where this asset belongs.""" + + qlik_owner_id: Union[str, None, UnsetType] = UNSET + """Identifier of the owner of this asset, in Qlik.""" + + qlik_is_published: Union[bool, None, UnsetType] = UNSET + """Whether this asset is published in Qlik (true) or not (false).""" + + +class QlikColumnRelationshipAttributes(AssetRelationshipAttributes): + """QlikColumn-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + qlik_sheet: Union[RelatedQlikSheet, None, UnsetType] = UNSET + """Parent sheet containing the columns.""" + + qlik_dataset: Union[RelatedQlikDataset, None, UnsetType] = UNSET + """Parent dataset containing the columns.""" + + qlik_chart: Union[RelatedQlikChart, None, UnsetType] = UNSET + """Parent chart containing the columns.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class QlikColumnNested(AssetNested): + """QlikColumn in nested API format for high-performance serialization.""" + + attributes: Union[QlikColumnAttributes, UnsetType] = UNSET + relationship_attributes: Union[QlikColumnRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + QlikColumnRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + QlikColumnRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_QLIK_COLUMN_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "qlik_sheet", + "qlik_dataset", + "qlik_chart", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_qlik_column_attrs(attrs: QlikColumnAttributes, obj: QlikColumn) -> None: + """Populate QlikColumn-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.qlik_column_name = obj.qlik_column_name + attrs.qlik_data_type = obj.qlik_data_type + attrs.qlik_column_type = obj.qlik_column_type + attrs.qlik_parent_qualified_name = obj.qlik_parent_qualified_name + attrs.qlik_id = obj.qlik_id + attrs.qlik_qri = obj.qlik_qri + attrs.qlik_space_id = obj.qlik_space_id + attrs.qlik_space_qualified_name = obj.qlik_space_qualified_name + attrs.qlik_app_id = obj.qlik_app_id + attrs.qlik_app_qualified_name = obj.qlik_app_qualified_name + attrs.qlik_owner_id = obj.qlik_owner_id + attrs.qlik_is_published = obj.qlik_is_published + + +def _extract_qlik_column_attrs(attrs: QlikColumnAttributes) -> dict: + """Extract all QlikColumn attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["qlik_column_name"] = attrs.qlik_column_name + result["qlik_data_type"] = attrs.qlik_data_type + result["qlik_column_type"] = attrs.qlik_column_type + result["qlik_parent_qualified_name"] = attrs.qlik_parent_qualified_name + result["qlik_id"] = attrs.qlik_id + result["qlik_qri"] = attrs.qlik_qri + result["qlik_space_id"] = attrs.qlik_space_id + result["qlik_space_qualified_name"] = attrs.qlik_space_qualified_name + result["qlik_app_id"] = attrs.qlik_app_id + result["qlik_app_qualified_name"] = attrs.qlik_app_qualified_name + result["qlik_owner_id"] = attrs.qlik_owner_id + result["qlik_is_published"] = attrs.qlik_is_published + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _qlik_column_to_nested(qlik_column: QlikColumn) -> QlikColumnNested: + """Convert flat QlikColumn to nested format.""" + attrs = QlikColumnAttributes() + _populate_qlik_column_attrs(attrs, qlik_column) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + qlik_column, _QLIK_COLUMN_REL_FIELDS, QlikColumnRelationshipAttributes + ) + return QlikColumnNested( + guid=qlik_column.guid, + type_name=qlik_column.type_name, + status=qlik_column.status, + version=qlik_column.version, + create_time=qlik_column.create_time, + update_time=qlik_column.update_time, + created_by=qlik_column.created_by, + updated_by=qlik_column.updated_by, + classifications=qlik_column.classifications, + classification_names=qlik_column.classification_names, + meanings=qlik_column.meanings, + labels=qlik_column.labels, + business_attributes=qlik_column.business_attributes, + custom_attributes=qlik_column.custom_attributes, + pending_tasks=qlik_column.pending_tasks, + proxy=qlik_column.proxy, + is_incomplete=qlik_column.is_incomplete, + provenance_type=qlik_column.provenance_type, + home_id=qlik_column.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _qlik_column_from_nested(nested: QlikColumnNested) -> QlikColumn: + """Convert nested format to flat QlikColumn.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else QlikColumnAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _QLIK_COLUMN_REL_FIELDS, + QlikColumnRelationshipAttributes, + ) + return QlikColumn( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_qlik_column_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _qlik_column_to_nested_bytes(qlik_column: QlikColumn, serde: Serde) -> bytes: + """Convert flat QlikColumn to nested JSON bytes.""" + return serde.encode(_qlik_column_to_nested(qlik_column)) + + +def _qlik_column_from_nested_bytes(data: bytes, serde: Serde) -> QlikColumn: + """Convert nested JSON bytes to flat QlikColumn.""" + nested = serde.decode(data, QlikColumnNested) + return _qlik_column_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + RelationField, +) + +QlikColumn.QLIK_COLUMN_NAME = KeywordField("qlikColumnName", "qlikColumnName") +QlikColumn.QLIK_DATA_TYPE = KeywordField("qlikDataType", "qlikDataType") +QlikColumn.QLIK_COLUMN_TYPE = KeywordField("qlikColumnType", "qlikColumnType") +QlikColumn.QLIK_PARENT_QUALIFIED_NAME = KeywordField( + "qlikParentQualifiedName", "qlikParentQualifiedName" +) +QlikColumn.QLIK_ID = KeywordField("qlikId", "qlikId") +QlikColumn.QLIK_QRI = KeywordTextField("qlikQRI", "qlikQRI", "qlikQRI.text") +QlikColumn.QLIK_SPACE_ID = KeywordField("qlikSpaceId", "qlikSpaceId") +QlikColumn.QLIK_SPACE_QUALIFIED_NAME = KeywordTextField( + "qlikSpaceQualifiedName", "qlikSpaceQualifiedName", "qlikSpaceQualifiedName.text" +) +QlikColumn.QLIK_APP_ID = KeywordField("qlikAppId", "qlikAppId") +QlikColumn.QLIK_APP_QUALIFIED_NAME = KeywordTextField( + "qlikAppQualifiedName", "qlikAppQualifiedName", "qlikAppQualifiedName.text" +) +QlikColumn.QLIK_OWNER_ID = KeywordField("qlikOwnerId", "qlikOwnerId") +QlikColumn.QLIK_IS_PUBLISHED = BooleanField("qlikIsPublished", "qlikIsPublished") +QlikColumn.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +QlikColumn.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +QlikColumn.ANOMALO_CHECKS = RelationField("anomaloChecks") +QlikColumn.APPLICATION = RelationField("application") +QlikColumn.APPLICATION_FIELD = RelationField("applicationField") +QlikColumn.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +QlikColumn.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +QlikColumn.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +QlikColumn.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +QlikColumn.METRICS = RelationField("metrics") +QlikColumn.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +QlikColumn.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +QlikColumn.MEANINGS = RelationField("meanings") +QlikColumn.MC_MONITORS = RelationField("mcMonitors") +QlikColumn.MC_INCIDENTS = RelationField("mcIncidents") +QlikColumn.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +QlikColumn.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +QlikColumn.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +QlikColumn.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +QlikColumn.QLIK_SHEET = RelationField("qlikSheet") +QlikColumn.QLIK_DATASET = RelationField("qlikDataset") +QlikColumn.QLIK_CHART = RelationField("qlikChart") +QlikColumn.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +QlikColumn.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +QlikColumn.FILES = RelationField("files") +QlikColumn.LINKS = RelationField("links") +QlikColumn.README = RelationField("readme") +QlikColumn.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +QlikColumn.SODA_CHECKS = RelationField("sodaChecks") +QlikColumn.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +QlikColumn.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/qlik_dataset.py b/pyatlan_v9/model/assets/qlik_dataset.py new file mode 100644 index 000000000..7acd05ca6 --- /dev/null +++ b/pyatlan_v9/model/assets/qlik_dataset.py @@ -0,0 +1,701 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +QlikDataset asset model with flattened inheritance. + +This module provides: +- QlikDataset: Flat asset class (easy to use) +- QlikDatasetAttributes: Nested attributes struct (extends AssetAttributes) +- QlikDatasetNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .qlik_related import RelatedQlikColumn, RelatedQlikSpace + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class QlikDataset(Asset): + """ + Instance of a Qlik dataset, datafile, datastore or dataasset in Atlan. + """ + + QLIK_DATASET_TECHNICAL_NAME: ClassVar[Any] = None + QLIK_DATASET_TYPE: ClassVar[Any] = None + QLIK_DATASET_URI: ClassVar[Any] = None + QLIK_DATASET_SUBTYPE: ClassVar[Any] = None + QLIK_IS_IMPLICIT: ClassVar[Any] = None + QLIK_ID: ClassVar[Any] = None + QLIK_QRI: ClassVar[Any] = None + QLIK_SPACE_ID: ClassVar[Any] = None + QLIK_SPACE_QUALIFIED_NAME: ClassVar[Any] = None + QLIK_APP_ID: ClassVar[Any] = None + QLIK_APP_QUALIFIED_NAME: ClassVar[Any] = None + QLIK_OWNER_ID: ClassVar[Any] = None + QLIK_IS_PUBLISHED: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + QLIK_SPACE: ClassVar[Any] = None + QLIK_COLUMNS: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "QlikDataset" + + qlik_dataset_technical_name: Union[str, None, UnsetType] = UNSET + """Technical name of this asset.""" + + qlik_dataset_type: Union[str, None, UnsetType] = UNSET + """Type of this data asset, for example: qix-df, snowflake, etc.""" + + qlik_dataset_uri: Union[str, None, UnsetType] = UNSET + """URI of this dataset.""" + + qlik_dataset_subtype: Union[str, None, UnsetType] = UNSET + """Subtype this dataset asset.""" + + qlik_is_implicit: Union[bool, None, UnsetType] = UNSET + """Whether the Qlik dataset is an implicit dataset""" + + qlik_id: Union[str, None, UnsetType] = UNSET + """Identifier of this asset, from Qlik.""" + + qlik_qri: Union[str, None, UnsetType] = msgspec.field(default=UNSET, name="qlikQRI") + """Unique QRI of this asset, from Qlik.""" + + qlik_space_id: Union[str, None, UnsetType] = UNSET + """Identifier of the space in which this asset exists, from Qlik.""" + + qlik_space_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the space in which this asset exists.""" + + qlik_app_id: Union[str, None, UnsetType] = UNSET + """Identifier of the app in which this asset belongs, from Qlik.""" + + qlik_app_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the app where this asset belongs.""" + + qlik_owner_id: Union[str, None, UnsetType] = UNSET + """Identifier of the owner of this asset, in Qlik.""" + + qlik_is_published: Union[bool, None, UnsetType] = UNSET + """Whether this asset is published in Qlik (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + qlik_space: Union[RelatedQlikSpace, None, UnsetType] = UNSET + """Space in which this dataset exists.""" + + qlik_columns: Union[List[RelatedQlikColumn], None, UnsetType] = UNSET + """Columns contained in the dataset.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "QlikDataset" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _qlik_dataset_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> QlikDataset: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + QlikDataset instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _qlik_dataset_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class QlikDatasetAttributes(AssetAttributes): + """QlikDataset-specific attributes for nested API format.""" + + qlik_dataset_technical_name: Union[str, None, UnsetType] = UNSET + """Technical name of this asset.""" + + qlik_dataset_type: Union[str, None, UnsetType] = UNSET + """Type of this data asset, for example: qix-df, snowflake, etc.""" + + qlik_dataset_uri: Union[str, None, UnsetType] = UNSET + """URI of this dataset.""" + + qlik_dataset_subtype: Union[str, None, UnsetType] = UNSET + """Subtype this dataset asset.""" + + qlik_is_implicit: Union[bool, None, UnsetType] = UNSET + """Whether the Qlik dataset is an implicit dataset""" + + qlik_id: Union[str, None, UnsetType] = UNSET + """Identifier of this asset, from Qlik.""" + + qlik_qri: Union[str, None, UnsetType] = msgspec.field(default=UNSET, name="qlikQRI") + """Unique QRI of this asset, from Qlik.""" + + qlik_space_id: Union[str, None, UnsetType] = UNSET + """Identifier of the space in which this asset exists, from Qlik.""" + + qlik_space_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the space in which this asset exists.""" + + qlik_app_id: Union[str, None, UnsetType] = UNSET + """Identifier of the app in which this asset belongs, from Qlik.""" + + qlik_app_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the app where this asset belongs.""" + + qlik_owner_id: Union[str, None, UnsetType] = UNSET + """Identifier of the owner of this asset, in Qlik.""" + + qlik_is_published: Union[bool, None, UnsetType] = UNSET + """Whether this asset is published in Qlik (true) or not (false).""" + + +class QlikDatasetRelationshipAttributes(AssetRelationshipAttributes): + """QlikDataset-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + qlik_space: Union[RelatedQlikSpace, None, UnsetType] = UNSET + """Space in which this dataset exists.""" + + qlik_columns: Union[List[RelatedQlikColumn], None, UnsetType] = UNSET + """Columns contained in the dataset.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class QlikDatasetNested(AssetNested): + """QlikDataset in nested API format for high-performance serialization.""" + + attributes: Union[QlikDatasetAttributes, UnsetType] = UNSET + relationship_attributes: Union[QlikDatasetRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + QlikDatasetRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + QlikDatasetRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_QLIK_DATASET_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "qlik_space", + "qlik_columns", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_qlik_dataset_attrs( + attrs: QlikDatasetAttributes, obj: QlikDataset +) -> None: + """Populate QlikDataset-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.qlik_dataset_technical_name = obj.qlik_dataset_technical_name + attrs.qlik_dataset_type = obj.qlik_dataset_type + attrs.qlik_dataset_uri = obj.qlik_dataset_uri + attrs.qlik_dataset_subtype = obj.qlik_dataset_subtype + attrs.qlik_is_implicit = obj.qlik_is_implicit + attrs.qlik_id = obj.qlik_id + attrs.qlik_qri = obj.qlik_qri + attrs.qlik_space_id = obj.qlik_space_id + attrs.qlik_space_qualified_name = obj.qlik_space_qualified_name + attrs.qlik_app_id = obj.qlik_app_id + attrs.qlik_app_qualified_name = obj.qlik_app_qualified_name + attrs.qlik_owner_id = obj.qlik_owner_id + attrs.qlik_is_published = obj.qlik_is_published + + +def _extract_qlik_dataset_attrs(attrs: QlikDatasetAttributes) -> dict: + """Extract all QlikDataset attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["qlik_dataset_technical_name"] = attrs.qlik_dataset_technical_name + result["qlik_dataset_type"] = attrs.qlik_dataset_type + result["qlik_dataset_uri"] = attrs.qlik_dataset_uri + result["qlik_dataset_subtype"] = attrs.qlik_dataset_subtype + result["qlik_is_implicit"] = attrs.qlik_is_implicit + result["qlik_id"] = attrs.qlik_id + result["qlik_qri"] = attrs.qlik_qri + result["qlik_space_id"] = attrs.qlik_space_id + result["qlik_space_qualified_name"] = attrs.qlik_space_qualified_name + result["qlik_app_id"] = attrs.qlik_app_id + result["qlik_app_qualified_name"] = attrs.qlik_app_qualified_name + result["qlik_owner_id"] = attrs.qlik_owner_id + result["qlik_is_published"] = attrs.qlik_is_published + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _qlik_dataset_to_nested(qlik_dataset: QlikDataset) -> QlikDatasetNested: + """Convert flat QlikDataset to nested format.""" + attrs = QlikDatasetAttributes() + _populate_qlik_dataset_attrs(attrs, qlik_dataset) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + qlik_dataset, _QLIK_DATASET_REL_FIELDS, QlikDatasetRelationshipAttributes + ) + return QlikDatasetNested( + guid=qlik_dataset.guid, + type_name=qlik_dataset.type_name, + status=qlik_dataset.status, + version=qlik_dataset.version, + create_time=qlik_dataset.create_time, + update_time=qlik_dataset.update_time, + created_by=qlik_dataset.created_by, + updated_by=qlik_dataset.updated_by, + classifications=qlik_dataset.classifications, + classification_names=qlik_dataset.classification_names, + meanings=qlik_dataset.meanings, + labels=qlik_dataset.labels, + business_attributes=qlik_dataset.business_attributes, + custom_attributes=qlik_dataset.custom_attributes, + pending_tasks=qlik_dataset.pending_tasks, + proxy=qlik_dataset.proxy, + is_incomplete=qlik_dataset.is_incomplete, + provenance_type=qlik_dataset.provenance_type, + home_id=qlik_dataset.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _qlik_dataset_from_nested(nested: QlikDatasetNested) -> QlikDataset: + """Convert nested format to flat QlikDataset.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else QlikDatasetAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _QLIK_DATASET_REL_FIELDS, + QlikDatasetRelationshipAttributes, + ) + return QlikDataset( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_qlik_dataset_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _qlik_dataset_to_nested_bytes(qlik_dataset: QlikDataset, serde: Serde) -> bytes: + """Convert flat QlikDataset to nested JSON bytes.""" + return serde.encode(_qlik_dataset_to_nested(qlik_dataset)) + + +def _qlik_dataset_from_nested_bytes(data: bytes, serde: Serde) -> QlikDataset: + """Convert nested JSON bytes to flat QlikDataset.""" + nested = serde.decode(data, QlikDatasetNested) + return _qlik_dataset_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + RelationField, +) + +QlikDataset.QLIK_DATASET_TECHNICAL_NAME = KeywordTextField( + "qlikDatasetTechnicalName", + "qlikDatasetTechnicalName", + "qlikDatasetTechnicalName.text", +) +QlikDataset.QLIK_DATASET_TYPE = KeywordField("qlikDatasetType", "qlikDatasetType") +QlikDataset.QLIK_DATASET_URI = KeywordTextField( + "qlikDatasetUri", "qlikDatasetUri", "qlikDatasetUri.text" +) +QlikDataset.QLIK_DATASET_SUBTYPE = KeywordField( + "qlikDatasetSubtype", "qlikDatasetSubtype" +) +QlikDataset.QLIK_IS_IMPLICIT = BooleanField("qlikIsImplicit", "qlikIsImplicit") +QlikDataset.QLIK_ID = KeywordField("qlikId", "qlikId") +QlikDataset.QLIK_QRI = KeywordTextField("qlikQRI", "qlikQRI", "qlikQRI.text") +QlikDataset.QLIK_SPACE_ID = KeywordField("qlikSpaceId", "qlikSpaceId") +QlikDataset.QLIK_SPACE_QUALIFIED_NAME = KeywordTextField( + "qlikSpaceQualifiedName", "qlikSpaceQualifiedName", "qlikSpaceQualifiedName.text" +) +QlikDataset.QLIK_APP_ID = KeywordField("qlikAppId", "qlikAppId") +QlikDataset.QLIK_APP_QUALIFIED_NAME = KeywordTextField( + "qlikAppQualifiedName", "qlikAppQualifiedName", "qlikAppQualifiedName.text" +) +QlikDataset.QLIK_OWNER_ID = KeywordField("qlikOwnerId", "qlikOwnerId") +QlikDataset.QLIK_IS_PUBLISHED = BooleanField("qlikIsPublished", "qlikIsPublished") +QlikDataset.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +QlikDataset.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +QlikDataset.ANOMALO_CHECKS = RelationField("anomaloChecks") +QlikDataset.APPLICATION = RelationField("application") +QlikDataset.APPLICATION_FIELD = RelationField("applicationField") +QlikDataset.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +QlikDataset.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +QlikDataset.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +QlikDataset.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +QlikDataset.METRICS = RelationField("metrics") +QlikDataset.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +QlikDataset.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +QlikDataset.MEANINGS = RelationField("meanings") +QlikDataset.MC_MONITORS = RelationField("mcMonitors") +QlikDataset.MC_INCIDENTS = RelationField("mcIncidents") +QlikDataset.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +QlikDataset.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +QlikDataset.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +QlikDataset.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +QlikDataset.QLIK_SPACE = RelationField("qlikSpace") +QlikDataset.QLIK_COLUMNS = RelationField("qlikColumns") +QlikDataset.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +QlikDataset.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +QlikDataset.FILES = RelationField("files") +QlikDataset.LINKS = RelationField("links") +QlikDataset.README = RelationField("readme") +QlikDataset.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +QlikDataset.SODA_CHECKS = RelationField("sodaChecks") +QlikDataset.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +QlikDataset.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/qlik_related.py b/pyatlan_v9/model/assets/qlik_related.py new file mode 100644 index 000000000..d84c99c01 --- /dev/null +++ b/pyatlan_v9/model/assets/qlik_related.py @@ -0,0 +1,235 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Qlik module. + +This module contains all Related{Type} classes for the Qlik type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedBI +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedQlik", + "RelatedQlikChart", + "RelatedQlikSheet", + "RelatedQlikSpace", + "RelatedQlikStream", + "RelatedQlikApp", + "RelatedQlikDataset", + "RelatedQlikColumn", +] + + +class RelatedQlik(RelatedBI): + """ + Related entity reference for Qlik assets. + + Extends RelatedBI with Qlik-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Qlik" so it serializes correctly + + qlik_id: Union[str, None, UnsetType] = UNSET + """Identifier of this asset, from Qlik.""" + + qlik_qri: Union[str, None, UnsetType] = msgspec.field(default=UNSET, name="qlikQRI") + """Unique QRI of this asset, from Qlik.""" + + qlik_space_id: Union[str, None, UnsetType] = UNSET + """Identifier of the space in which this asset exists, from Qlik.""" + + qlik_space_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the space in which this asset exists.""" + + qlik_app_id: Union[str, None, UnsetType] = UNSET + """Identifier of the app in which this asset belongs, from Qlik.""" + + qlik_app_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the app where this asset belongs.""" + + qlik_owner_id: Union[str, None, UnsetType] = UNSET + """Identifier of the owner of this asset, in Qlik.""" + + qlik_is_published: Union[bool, None, UnsetType] = UNSET + """Whether this asset is published in Qlik (true) or not (false).""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Qlik" + + +class RelatedQlikChart(RelatedQlik): + """ + Related entity reference for QlikChart assets. + + Extends RelatedQlik with QlikChart-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "QlikChart" so it serializes correctly + + qlik_chart_subtitle: Union[str, None, UnsetType] = UNSET + """Subtitle of this chart.""" + + qlik_chart_footnote: Union[str, None, UnsetType] = UNSET + """Footnote of this chart.""" + + qlik_orientation: Union[str, None, UnsetType] = UNSET + """Orientation of this chart.""" + + qlik_type: Union[str, None, UnsetType] = UNSET + """Subtype of this chart, for example: bar, graph, pie, etc.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "QlikChart" + + +class RelatedQlikSheet(RelatedQlik): + """ + Related entity reference for QlikSheet assets. + + Extends RelatedQlik with QlikSheet-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "QlikSheet" so it serializes correctly + + qlik_is_approved: Union[bool, None, UnsetType] = UNSET + """Whether this is approved (true) or not (false).""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "QlikSheet" + + +class RelatedQlikSpace(RelatedQlik): + """ + Related entity reference for QlikSpace assets. + + Extends RelatedQlik with QlikSpace-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "QlikSpace" so it serializes correctly + + qlik_type: Union[str, None, UnsetType] = UNSET + """Type of this space, for exmaple: Private, Shared, etc.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "QlikSpace" + + +class RelatedQlikStream(RelatedQlik): + """ + Related entity reference for QlikStream assets. + + Extends RelatedQlik with QlikStream-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "QlikStream" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "QlikStream" + + +class RelatedQlikApp(RelatedQlik): + """ + Related entity reference for QlikApp assets. + + Extends RelatedQlik with QlikApp-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "QlikApp" so it serializes correctly + + qlik_has_section_access: Union[bool, None, UnsetType] = UNSET + """Whether section access or data masking is enabled on the source (true) or not (false).""" + + qlik_origin_app_id: Union[str, None, UnsetType] = UNSET + """Value of originAppId for this app.""" + + qlik_is_encrypted: Union[bool, None, UnsetType] = UNSET + """Whether this app is encrypted (true) or not (false).""" + + qlik_is_direct_query_mode: Union[bool, None, UnsetType] = UNSET + """Whether this app is in direct query mode (true) or not (false).""" + + qlik_app_static_byte_size: Union[int, None, UnsetType] = UNSET + """Static space used by this app, in bytes.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "QlikApp" + + +class RelatedQlikDataset(RelatedQlik): + """ + Related entity reference for QlikDataset assets. + + Extends RelatedQlik with QlikDataset-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "QlikDataset" so it serializes correctly + + qlik_dataset_technical_name: Union[str, None, UnsetType] = UNSET + """Technical name of this asset.""" + + qlik_dataset_type: Union[str, None, UnsetType] = UNSET + """Type of this data asset, for example: qix-df, snowflake, etc.""" + + qlik_dataset_uri: Union[str, None, UnsetType] = UNSET + """URI of this dataset.""" + + qlik_dataset_subtype: Union[str, None, UnsetType] = UNSET + """Subtype this dataset asset.""" + + qlik_is_implicit: Union[bool, None, UnsetType] = UNSET + """Whether the Qlik dataset is an implicit dataset""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "QlikDataset" + + +class RelatedQlikColumn(RelatedQlik): + """ + Related entity reference for QlikColumn assets. + + Extends RelatedQlik with QlikColumn-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "QlikColumn" so it serializes correctly + + qlik_column_name: Union[str, None, UnsetType] = UNSET + """Qlik Column name.""" + + qlik_data_type: Union[str, None, UnsetType] = UNSET + """Data type of the Qlik Column.""" + + qlik_column_type: Union[str, None, UnsetType] = UNSET + """Column type can be: Dimension, Measure or Normal.""" + + qlik_parent_qualified_name: Union[str, None, UnsetType] = UNSET + """Parent Qualified name of column.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "QlikColumn" diff --git a/pyatlan_v9/model/assets/qlik_sheet.py b/pyatlan_v9/model/assets/qlik_sheet.py new file mode 100644 index 000000000..07f085249 --- /dev/null +++ b/pyatlan_v9/model/assets/qlik_sheet.py @@ -0,0 +1,662 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +QlikSheet asset model with flattened inheritance. + +This module provides: +- QlikSheet: Flat asset class (easy to use) +- QlikSheetAttributes: Nested attributes struct (extends AssetAttributes) +- QlikSheetNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .qlik_related import RelatedQlikApp, RelatedQlikChart, RelatedQlikColumn + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class QlikSheet(Asset): + """ + Instance of a Qlik sheet in Atlan. + """ + + QLIK_IS_APPROVED: ClassVar[Any] = None + QLIK_ID: ClassVar[Any] = None + QLIK_QRI: ClassVar[Any] = None + QLIK_SPACE_ID: ClassVar[Any] = None + QLIK_SPACE_QUALIFIED_NAME: ClassVar[Any] = None + QLIK_APP_ID: ClassVar[Any] = None + QLIK_APP_QUALIFIED_NAME: ClassVar[Any] = None + QLIK_OWNER_ID: ClassVar[Any] = None + QLIK_IS_PUBLISHED: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + QLIK_CHARTS: ClassVar[Any] = None + QLIK_APP: ClassVar[Any] = None + QLIK_COLUMNS: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "QlikSheet" + + qlik_is_approved: Union[bool, None, UnsetType] = UNSET + """Whether this is approved (true) or not (false).""" + + qlik_id: Union[str, None, UnsetType] = UNSET + """Identifier of this asset, from Qlik.""" + + qlik_qri: Union[str, None, UnsetType] = msgspec.field(default=UNSET, name="qlikQRI") + """Unique QRI of this asset, from Qlik.""" + + qlik_space_id: Union[str, None, UnsetType] = UNSET + """Identifier of the space in which this asset exists, from Qlik.""" + + qlik_space_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the space in which this asset exists.""" + + qlik_app_id: Union[str, None, UnsetType] = UNSET + """Identifier of the app in which this asset belongs, from Qlik.""" + + qlik_app_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the app where this asset belongs.""" + + qlik_owner_id: Union[str, None, UnsetType] = UNSET + """Identifier of the owner of this asset, in Qlik.""" + + qlik_is_published: Union[bool, None, UnsetType] = UNSET + """Whether this asset is published in Qlik (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + qlik_charts: Union[List[RelatedQlikChart], None, UnsetType] = UNSET + """Charts that exist within this sheet.""" + + qlik_app: Union[RelatedQlikApp, None, UnsetType] = UNSET + """App in which this sheet exists.""" + + qlik_columns: Union[List[RelatedQlikColumn], None, UnsetType] = UNSET + """Columns contained in the sheet.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "QlikSheet" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _qlik_sheet_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> QlikSheet: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + QlikSheet instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _qlik_sheet_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class QlikSheetAttributes(AssetAttributes): + """QlikSheet-specific attributes for nested API format.""" + + qlik_is_approved: Union[bool, None, UnsetType] = UNSET + """Whether this is approved (true) or not (false).""" + + qlik_id: Union[str, None, UnsetType] = UNSET + """Identifier of this asset, from Qlik.""" + + qlik_qri: Union[str, None, UnsetType] = msgspec.field(default=UNSET, name="qlikQRI") + """Unique QRI of this asset, from Qlik.""" + + qlik_space_id: Union[str, None, UnsetType] = UNSET + """Identifier of the space in which this asset exists, from Qlik.""" + + qlik_space_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the space in which this asset exists.""" + + qlik_app_id: Union[str, None, UnsetType] = UNSET + """Identifier of the app in which this asset belongs, from Qlik.""" + + qlik_app_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the app where this asset belongs.""" + + qlik_owner_id: Union[str, None, UnsetType] = UNSET + """Identifier of the owner of this asset, in Qlik.""" + + qlik_is_published: Union[bool, None, UnsetType] = UNSET + """Whether this asset is published in Qlik (true) or not (false).""" + + +class QlikSheetRelationshipAttributes(AssetRelationshipAttributes): + """QlikSheet-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + qlik_charts: Union[List[RelatedQlikChart], None, UnsetType] = UNSET + """Charts that exist within this sheet.""" + + qlik_app: Union[RelatedQlikApp, None, UnsetType] = UNSET + """App in which this sheet exists.""" + + qlik_columns: Union[List[RelatedQlikColumn], None, UnsetType] = UNSET + """Columns contained in the sheet.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class QlikSheetNested(AssetNested): + """QlikSheet in nested API format for high-performance serialization.""" + + attributes: Union[QlikSheetAttributes, UnsetType] = UNSET + relationship_attributes: Union[QlikSheetRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + QlikSheetRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + QlikSheetRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_QLIK_SHEET_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "qlik_charts", + "qlik_app", + "qlik_columns", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_qlik_sheet_attrs(attrs: QlikSheetAttributes, obj: QlikSheet) -> None: + """Populate QlikSheet-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.qlik_is_approved = obj.qlik_is_approved + attrs.qlik_id = obj.qlik_id + attrs.qlik_qri = obj.qlik_qri + attrs.qlik_space_id = obj.qlik_space_id + attrs.qlik_space_qualified_name = obj.qlik_space_qualified_name + attrs.qlik_app_id = obj.qlik_app_id + attrs.qlik_app_qualified_name = obj.qlik_app_qualified_name + attrs.qlik_owner_id = obj.qlik_owner_id + attrs.qlik_is_published = obj.qlik_is_published + + +def _extract_qlik_sheet_attrs(attrs: QlikSheetAttributes) -> dict: + """Extract all QlikSheet attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["qlik_is_approved"] = attrs.qlik_is_approved + result["qlik_id"] = attrs.qlik_id + result["qlik_qri"] = attrs.qlik_qri + result["qlik_space_id"] = attrs.qlik_space_id + result["qlik_space_qualified_name"] = attrs.qlik_space_qualified_name + result["qlik_app_id"] = attrs.qlik_app_id + result["qlik_app_qualified_name"] = attrs.qlik_app_qualified_name + result["qlik_owner_id"] = attrs.qlik_owner_id + result["qlik_is_published"] = attrs.qlik_is_published + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _qlik_sheet_to_nested(qlik_sheet: QlikSheet) -> QlikSheetNested: + """Convert flat QlikSheet to nested format.""" + attrs = QlikSheetAttributes() + _populate_qlik_sheet_attrs(attrs, qlik_sheet) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + qlik_sheet, _QLIK_SHEET_REL_FIELDS, QlikSheetRelationshipAttributes + ) + return QlikSheetNested( + guid=qlik_sheet.guid, + type_name=qlik_sheet.type_name, + status=qlik_sheet.status, + version=qlik_sheet.version, + create_time=qlik_sheet.create_time, + update_time=qlik_sheet.update_time, + created_by=qlik_sheet.created_by, + updated_by=qlik_sheet.updated_by, + classifications=qlik_sheet.classifications, + classification_names=qlik_sheet.classification_names, + meanings=qlik_sheet.meanings, + labels=qlik_sheet.labels, + business_attributes=qlik_sheet.business_attributes, + custom_attributes=qlik_sheet.custom_attributes, + pending_tasks=qlik_sheet.pending_tasks, + proxy=qlik_sheet.proxy, + is_incomplete=qlik_sheet.is_incomplete, + provenance_type=qlik_sheet.provenance_type, + home_id=qlik_sheet.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _qlik_sheet_from_nested(nested: QlikSheetNested) -> QlikSheet: + """Convert nested format to flat QlikSheet.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else QlikSheetAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _QLIK_SHEET_REL_FIELDS, + QlikSheetRelationshipAttributes, + ) + return QlikSheet( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_qlik_sheet_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _qlik_sheet_to_nested_bytes(qlik_sheet: QlikSheet, serde: Serde) -> bytes: + """Convert flat QlikSheet to nested JSON bytes.""" + return serde.encode(_qlik_sheet_to_nested(qlik_sheet)) + + +def _qlik_sheet_from_nested_bytes(data: bytes, serde: Serde) -> QlikSheet: + """Convert nested JSON bytes to flat QlikSheet.""" + nested = serde.decode(data, QlikSheetNested) + return _qlik_sheet_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + RelationField, +) + +QlikSheet.QLIK_IS_APPROVED = BooleanField("qlikIsApproved", "qlikIsApproved") +QlikSheet.QLIK_ID = KeywordField("qlikId", "qlikId") +QlikSheet.QLIK_QRI = KeywordTextField("qlikQRI", "qlikQRI", "qlikQRI.text") +QlikSheet.QLIK_SPACE_ID = KeywordField("qlikSpaceId", "qlikSpaceId") +QlikSheet.QLIK_SPACE_QUALIFIED_NAME = KeywordTextField( + "qlikSpaceQualifiedName", "qlikSpaceQualifiedName", "qlikSpaceQualifiedName.text" +) +QlikSheet.QLIK_APP_ID = KeywordField("qlikAppId", "qlikAppId") +QlikSheet.QLIK_APP_QUALIFIED_NAME = KeywordTextField( + "qlikAppQualifiedName", "qlikAppQualifiedName", "qlikAppQualifiedName.text" +) +QlikSheet.QLIK_OWNER_ID = KeywordField("qlikOwnerId", "qlikOwnerId") +QlikSheet.QLIK_IS_PUBLISHED = BooleanField("qlikIsPublished", "qlikIsPublished") +QlikSheet.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +QlikSheet.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +QlikSheet.ANOMALO_CHECKS = RelationField("anomaloChecks") +QlikSheet.APPLICATION = RelationField("application") +QlikSheet.APPLICATION_FIELD = RelationField("applicationField") +QlikSheet.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +QlikSheet.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +QlikSheet.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +QlikSheet.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +QlikSheet.METRICS = RelationField("metrics") +QlikSheet.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +QlikSheet.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +QlikSheet.MEANINGS = RelationField("meanings") +QlikSheet.MC_MONITORS = RelationField("mcMonitors") +QlikSheet.MC_INCIDENTS = RelationField("mcIncidents") +QlikSheet.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +QlikSheet.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +QlikSheet.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +QlikSheet.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +QlikSheet.QLIK_CHARTS = RelationField("qlikCharts") +QlikSheet.QLIK_APP = RelationField("qlikApp") +QlikSheet.QLIK_COLUMNS = RelationField("qlikColumns") +QlikSheet.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +QlikSheet.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +QlikSheet.FILES = RelationField("files") +QlikSheet.LINKS = RelationField("links") +QlikSheet.README = RelationField("readme") +QlikSheet.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +QlikSheet.SODA_CHECKS = RelationField("sodaChecks") +QlikSheet.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +QlikSheet.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/qlik_space.py b/pyatlan_v9/model/assets/qlik_space.py new file mode 100644 index 000000000..b465b4327 --- /dev/null +++ b/pyatlan_v9/model/assets/qlik_space.py @@ -0,0 +1,644 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +QlikSpace asset model with flattened inheritance. + +This module provides: +- QlikSpace: Flat asset class (easy to use) +- QlikSpaceAttributes: Nested attributes struct (extends AssetAttributes) +- QlikSpaceNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .qlik_related import RelatedQlikApp, RelatedQlikDataset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class QlikSpace(Asset): + """ + Instance of a Qlik space in Atlan. + """ + + QLIK_TYPE: ClassVar[Any] = None + QLIK_ID: ClassVar[Any] = None + QLIK_QRI: ClassVar[Any] = None + QLIK_SPACE_ID: ClassVar[Any] = None + QLIK_SPACE_QUALIFIED_NAME: ClassVar[Any] = None + QLIK_APP_ID: ClassVar[Any] = None + QLIK_APP_QUALIFIED_NAME: ClassVar[Any] = None + QLIK_OWNER_ID: ClassVar[Any] = None + QLIK_IS_PUBLISHED: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + QLIK_APPS: ClassVar[Any] = None + QLIK_DATASETS: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "QlikSpace" + + qlik_type: Union[str, None, UnsetType] = UNSET + """Type of this space, for exmaple: Private, Shared, etc.""" + + qlik_id: Union[str, None, UnsetType] = UNSET + """Identifier of this asset, from Qlik.""" + + qlik_qri: Union[str, None, UnsetType] = msgspec.field(default=UNSET, name="qlikQRI") + """Unique QRI of this asset, from Qlik.""" + + qlik_space_id: Union[str, None, UnsetType] = UNSET + """Identifier of the space in which this asset exists, from Qlik.""" + + qlik_space_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the space in which this asset exists.""" + + qlik_app_id: Union[str, None, UnsetType] = UNSET + """Identifier of the app in which this asset belongs, from Qlik.""" + + qlik_app_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the app where this asset belongs.""" + + qlik_owner_id: Union[str, None, UnsetType] = UNSET + """Identifier of the owner of this asset, in Qlik.""" + + qlik_is_published: Union[bool, None, UnsetType] = UNSET + """Whether this asset is published in Qlik (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + qlik_apps: Union[List[RelatedQlikApp], None, UnsetType] = UNSET + """Apps that exist within this space.""" + + qlik_datasets: Union[List[RelatedQlikDataset], None, UnsetType] = UNSET + """Datasets that exist within this space.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "QlikSpace" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _qlik_space_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> QlikSpace: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + QlikSpace instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _qlik_space_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class QlikSpaceAttributes(AssetAttributes): + """QlikSpace-specific attributes for nested API format.""" + + qlik_type: Union[str, None, UnsetType] = UNSET + """Type of this space, for exmaple: Private, Shared, etc.""" + + qlik_id: Union[str, None, UnsetType] = UNSET + """Identifier of this asset, from Qlik.""" + + qlik_qri: Union[str, None, UnsetType] = msgspec.field(default=UNSET, name="qlikQRI") + """Unique QRI of this asset, from Qlik.""" + + qlik_space_id: Union[str, None, UnsetType] = UNSET + """Identifier of the space in which this asset exists, from Qlik.""" + + qlik_space_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the space in which this asset exists.""" + + qlik_app_id: Union[str, None, UnsetType] = UNSET + """Identifier of the app in which this asset belongs, from Qlik.""" + + qlik_app_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the app where this asset belongs.""" + + qlik_owner_id: Union[str, None, UnsetType] = UNSET + """Identifier of the owner of this asset, in Qlik.""" + + qlik_is_published: Union[bool, None, UnsetType] = UNSET + """Whether this asset is published in Qlik (true) or not (false).""" + + +class QlikSpaceRelationshipAttributes(AssetRelationshipAttributes): + """QlikSpace-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + qlik_apps: Union[List[RelatedQlikApp], None, UnsetType] = UNSET + """Apps that exist within this space.""" + + qlik_datasets: Union[List[RelatedQlikDataset], None, UnsetType] = UNSET + """Datasets that exist within this space.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class QlikSpaceNested(AssetNested): + """QlikSpace in nested API format for high-performance serialization.""" + + attributes: Union[QlikSpaceAttributes, UnsetType] = UNSET + relationship_attributes: Union[QlikSpaceRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + QlikSpaceRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + QlikSpaceRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_QLIK_SPACE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "qlik_apps", + "qlik_datasets", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_qlik_space_attrs(attrs: QlikSpaceAttributes, obj: QlikSpace) -> None: + """Populate QlikSpace-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.qlik_type = obj.qlik_type + attrs.qlik_id = obj.qlik_id + attrs.qlik_qri = obj.qlik_qri + attrs.qlik_space_id = obj.qlik_space_id + attrs.qlik_space_qualified_name = obj.qlik_space_qualified_name + attrs.qlik_app_id = obj.qlik_app_id + attrs.qlik_app_qualified_name = obj.qlik_app_qualified_name + attrs.qlik_owner_id = obj.qlik_owner_id + attrs.qlik_is_published = obj.qlik_is_published + + +def _extract_qlik_space_attrs(attrs: QlikSpaceAttributes) -> dict: + """Extract all QlikSpace attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["qlik_type"] = attrs.qlik_type + result["qlik_id"] = attrs.qlik_id + result["qlik_qri"] = attrs.qlik_qri + result["qlik_space_id"] = attrs.qlik_space_id + result["qlik_space_qualified_name"] = attrs.qlik_space_qualified_name + result["qlik_app_id"] = attrs.qlik_app_id + result["qlik_app_qualified_name"] = attrs.qlik_app_qualified_name + result["qlik_owner_id"] = attrs.qlik_owner_id + result["qlik_is_published"] = attrs.qlik_is_published + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _qlik_space_to_nested(qlik_space: QlikSpace) -> QlikSpaceNested: + """Convert flat QlikSpace to nested format.""" + attrs = QlikSpaceAttributes() + _populate_qlik_space_attrs(attrs, qlik_space) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + qlik_space, _QLIK_SPACE_REL_FIELDS, QlikSpaceRelationshipAttributes + ) + return QlikSpaceNested( + guid=qlik_space.guid, + type_name=qlik_space.type_name, + status=qlik_space.status, + version=qlik_space.version, + create_time=qlik_space.create_time, + update_time=qlik_space.update_time, + created_by=qlik_space.created_by, + updated_by=qlik_space.updated_by, + classifications=qlik_space.classifications, + classification_names=qlik_space.classification_names, + meanings=qlik_space.meanings, + labels=qlik_space.labels, + business_attributes=qlik_space.business_attributes, + custom_attributes=qlik_space.custom_attributes, + pending_tasks=qlik_space.pending_tasks, + proxy=qlik_space.proxy, + is_incomplete=qlik_space.is_incomplete, + provenance_type=qlik_space.provenance_type, + home_id=qlik_space.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _qlik_space_from_nested(nested: QlikSpaceNested) -> QlikSpace: + """Convert nested format to flat QlikSpace.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else QlikSpaceAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _QLIK_SPACE_REL_FIELDS, + QlikSpaceRelationshipAttributes, + ) + return QlikSpace( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_qlik_space_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _qlik_space_to_nested_bytes(qlik_space: QlikSpace, serde: Serde) -> bytes: + """Convert flat QlikSpace to nested JSON bytes.""" + return serde.encode(_qlik_space_to_nested(qlik_space)) + + +def _qlik_space_from_nested_bytes(data: bytes, serde: Serde) -> QlikSpace: + """Convert nested JSON bytes to flat QlikSpace.""" + nested = serde.decode(data, QlikSpaceNested) + return _qlik_space_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + RelationField, +) + +QlikSpace.QLIK_TYPE = KeywordField("qlikType", "qlikType") +QlikSpace.QLIK_ID = KeywordField("qlikId", "qlikId") +QlikSpace.QLIK_QRI = KeywordTextField("qlikQRI", "qlikQRI", "qlikQRI.text") +QlikSpace.QLIK_SPACE_ID = KeywordField("qlikSpaceId", "qlikSpaceId") +QlikSpace.QLIK_SPACE_QUALIFIED_NAME = KeywordTextField( + "qlikSpaceQualifiedName", "qlikSpaceQualifiedName", "qlikSpaceQualifiedName.text" +) +QlikSpace.QLIK_APP_ID = KeywordField("qlikAppId", "qlikAppId") +QlikSpace.QLIK_APP_QUALIFIED_NAME = KeywordTextField( + "qlikAppQualifiedName", "qlikAppQualifiedName", "qlikAppQualifiedName.text" +) +QlikSpace.QLIK_OWNER_ID = KeywordField("qlikOwnerId", "qlikOwnerId") +QlikSpace.QLIK_IS_PUBLISHED = BooleanField("qlikIsPublished", "qlikIsPublished") +QlikSpace.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +QlikSpace.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +QlikSpace.ANOMALO_CHECKS = RelationField("anomaloChecks") +QlikSpace.APPLICATION = RelationField("application") +QlikSpace.APPLICATION_FIELD = RelationField("applicationField") +QlikSpace.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +QlikSpace.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +QlikSpace.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +QlikSpace.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +QlikSpace.METRICS = RelationField("metrics") +QlikSpace.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +QlikSpace.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +QlikSpace.MEANINGS = RelationField("meanings") +QlikSpace.MC_MONITORS = RelationField("mcMonitors") +QlikSpace.MC_INCIDENTS = RelationField("mcIncidents") +QlikSpace.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +QlikSpace.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +QlikSpace.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +QlikSpace.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +QlikSpace.QLIK_APPS = RelationField("qlikApps") +QlikSpace.QLIK_DATASETS = RelationField("qlikDatasets") +QlikSpace.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +QlikSpace.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +QlikSpace.FILES = RelationField("files") +QlikSpace.LINKS = RelationField("links") +QlikSpace.README = RelationField("readme") +QlikSpace.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +QlikSpace.SODA_CHECKS = RelationField("sodaChecks") +QlikSpace.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +QlikSpace.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/query.py b/pyatlan_v9/model/assets/query.py new file mode 100644 index 000000000..a6adaaacd --- /dev/null +++ b/pyatlan_v9/model/assets/query.py @@ -0,0 +1,1088 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Query asset model with flattened inheritance. + +This module provides: +- Query: Flat asset class (easy to use) +- QueryAttributes: Nested attributes struct (extends AssetAttributes) +- QueryNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .namespace_related import RelatedNamespace +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .snowflake_related import RelatedSnowflakeSemanticLogicalTable +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan.model.enums import AtlanConnectorType +from pyatlan.utils import validate_required_fields +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .sql_related import RelatedColumn, RelatedTable, RelatedView + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Query(Asset): + """ + Instance of a query in Atlan. + """ + + RAW_QUERY: ClassVar[Any] = None + LONG_RAW_QUERY: ClassVar[Any] = None + RAW_QUERY_TEXT: ClassVar[Any] = None + DEFAULT_SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + DEFAULT_DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + VARIABLES_SCHEMA_BASE64: ClassVar[Any] = None + IS_PRIVATE: ClassVar[Any] = None + IS_SQL_SNIPPET: ClassVar[Any] = None + PARENT_QUALIFIED_NAME: ClassVar[Any] = None + COLLECTION_QUALIFIED_NAME: ClassVar[Any] = None + IS_VISUAL_QUERY: ClassVar[Any] = None + VISUAL_BUILDER_SCHEMA_BASE64: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + PARENT: ClassVar[Any] = None + TABLES: ClassVar[Any] = None + VIEWS: ClassVar[Any] = None + COLUMNS: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Query" + + raw_query: Union[str, None, UnsetType] = UNSET + """Deprecated. See 'longRawQuery' instead.""" + + long_raw_query: Union[str, None, UnsetType] = UNSET + """Raw SQL query string.""" + + raw_query_text: Union[str, None, UnsetType] = UNSET + """""" + + default_schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the default schema to use for this query.""" + + default_database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the default database to use for this query.""" + + variables_schema_base64: Union[str, None, UnsetType] = UNSET + """Base64-encoded string of the variables to use in this query.""" + + is_private: Union[bool, None, UnsetType] = UNSET + """Whether this query is private (true) or shared (false).""" + + is_sql_snippet: Union[bool, None, UnsetType] = UNSET + """Whether this query is a SQL snippet (true) or not (false).""" + + parent_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the parent collection or folder in which this query exists.""" + + collection_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the collection in which this query exists.""" + + is_visual_query: Union[bool, None, UnsetType] = UNSET + """Whether this query is a visual query (true) or not (false).""" + + visual_builder_schema_base64: Union[str, None, UnsetType] = UNSET + """Base64-encoded string for the visual query builder.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + parent: Union[RelatedNamespace, None, UnsetType] = UNSET + """Namespace in which this query exists.""" + + tables: Union[List[RelatedTable], None, UnsetType] = UNSET + """Tables this query accesses.""" + + views: Union[List[RelatedView], None, UnsetType] = UNSET + """Views this query accesses.""" + + columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Columns this query accesses.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Query" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + @classmethod + def creator( + cls, + *, + name: str, + collection_qualified_name: str | None = None, + parent_folder_qualified_name: str | None = None, + ) -> "Query": + validate_required_fields(["name"], [name]) + if not (parent_folder_qualified_name or collection_qualified_name): + raise ValueError( + "Either 'collection_qualified_name' or 'parent_folder_qualified_name' must be specified." + ) + + if not parent_folder_qualified_name: + qualified_name = f"{collection_qualified_name}/{name}" + parent_qn = collection_qualified_name + from pyatlan_v9.model.assets import Collection + + parent_ref = Collection.ref_by_qualified_name( + collection_qualified_name or "" + ) + else: + tokens = parent_folder_qualified_name.split("/") + if len(tokens) < 4: + raise ValueError("Invalid parent_folder_qualified_name") + collection_qualified_name = ( + f"{tokens[0]}/{tokens[1]}/{tokens[2]}/{tokens[3]}" + ) + qualified_name = f"{parent_folder_qualified_name}/{name}" + parent_qn = parent_folder_qualified_name + from pyatlan_v9.model.assets import Folder + + parent_ref = Folder.ref_by_qualified_name(parent_folder_qualified_name) + + return Query( + name=name, + qualified_name=qualified_name, + collection_qualified_name=collection_qualified_name, + parent=parent_ref, + parent_qualified_name=parent_qn, + ) + + @classmethod + def updater( + cls, + *, + name: str, + qualified_name: str, + collection_qualified_name: str, + parent_qualified_name: str, + ) -> "Query": + validate_required_fields( + ["name", "collection_qualified_name", "parent_qualified_name"], + [name, collection_qualified_name, parent_qualified_name], + ) + if collection_qualified_name == parent_qualified_name: + from pyatlan_v9.model.assets import Collection + + parent = Collection.ref_by_qualified_name(collection_qualified_name) + else: + from pyatlan_v9.model.assets import Folder + + parent = Folder.ref_by_qualified_name(parent_qualified_name) + + return Query( + qualified_name=qualified_name, + name=name, + parent=parent, + collection_qualified_name=collection_qualified_name, + parent_qualified_name=parent_qualified_name, + ) + + def with_raw_query(self, schema_qualified_name: str, query: str): + from base64 import b64encode + from json import dumps + + _DEFAULT_VARIABLE_SCHEMA = dumps( + { + "customvariablesDateTimeFormat": { + "defaultDateFormat": "YYYY-MM-DD", + "defaultTimeFormat": "HH:mm", + }, + "customVariables": [], + } + ) + connection_qn, connector_name = AtlanConnectorType.get_connector_name( + schema_qualified_name, "schema_qualified_name", 5 + ) + tokens = schema_qualified_name.split("/") + database_qn = f"{tokens[0]}/{tokens[1]}/{tokens[2]}/{tokens[3]}" + self.connection_name = connector_name + self.connection_qualified_name = connection_qn + self.default_database_qualified_name = database_qn + self.default_schema_qualified_name = schema_qualified_name + self.is_visual_query = False + self.raw_query_text = query + self.variables_schema_base64 = b64encode( + _DEFAULT_VARIABLE_SCHEMA.encode("utf-8") + ).decode("utf-8") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _query_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Query: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Query instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _query_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class QueryAttributes(AssetAttributes): + """Query-specific attributes for nested API format.""" + + raw_query: Union[str, None, UnsetType] = UNSET + """Deprecated. See 'longRawQuery' instead.""" + + long_raw_query: Union[str, None, UnsetType] = UNSET + """Raw SQL query string.""" + + raw_query_text: Union[str, None, UnsetType] = UNSET + """""" + + default_schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the default schema to use for this query.""" + + default_database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the default database to use for this query.""" + + variables_schema_base64: Union[str, None, UnsetType] = UNSET + """Base64-encoded string of the variables to use in this query.""" + + is_private: Union[bool, None, UnsetType] = UNSET + """Whether this query is private (true) or shared (false).""" + + is_sql_snippet: Union[bool, None, UnsetType] = UNSET + """Whether this query is a SQL snippet (true) or not (false).""" + + parent_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the parent collection or folder in which this query exists.""" + + collection_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the collection in which this query exists.""" + + is_visual_query: Union[bool, None, UnsetType] = UNSET + """Whether this query is a visual query (true) or not (false).""" + + visual_builder_schema_base64: Union[str, None, UnsetType] = UNSET + """Base64-encoded string for the visual query builder.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + +class QueryRelationshipAttributes(AssetRelationshipAttributes): + """Query-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + parent: Union[RelatedNamespace, None, UnsetType] = UNSET + """Namespace in which this query exists.""" + + tables: Union[List[RelatedTable], None, UnsetType] = UNSET + """Tables this query accesses.""" + + views: Union[List[RelatedView], None, UnsetType] = UNSET + """Views this query accesses.""" + + columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Columns this query accesses.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class QueryNested(AssetNested): + """Query in nested API format for high-performance serialization.""" + + attributes: Union[QueryAttributes, UnsetType] = UNSET + relationship_attributes: Union[QueryRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[QueryRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[QueryRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_QUERY_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "parent", + "tables", + "views", + "columns", + "schema_registry_subjects", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_query_attrs(attrs: QueryAttributes, obj: Query) -> None: + """Populate Query-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.raw_query = obj.raw_query + attrs.long_raw_query = obj.long_raw_query + attrs.raw_query_text = obj.raw_query_text + attrs.default_schema_qualified_name = obj.default_schema_qualified_name + attrs.default_database_qualified_name = obj.default_database_qualified_name + attrs.variables_schema_base64 = obj.variables_schema_base64 + attrs.is_private = obj.is_private + attrs.is_sql_snippet = obj.is_sql_snippet + attrs.parent_qualified_name = obj.parent_qualified_name + attrs.collection_qualified_name = obj.collection_qualified_name + attrs.is_visual_query = obj.is_visual_query + attrs.visual_builder_schema_base64 = obj.visual_builder_schema_base64 + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + + +def _extract_query_attrs(attrs: QueryAttributes) -> dict: + """Extract all Query attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["raw_query"] = attrs.raw_query + result["long_raw_query"] = attrs.long_raw_query + result["raw_query_text"] = attrs.raw_query_text + result["default_schema_qualified_name"] = attrs.default_schema_qualified_name + result["default_database_qualified_name"] = attrs.default_database_qualified_name + result["variables_schema_base64"] = attrs.variables_schema_base64 + result["is_private"] = attrs.is_private + result["is_sql_snippet"] = attrs.is_sql_snippet + result["parent_qualified_name"] = attrs.parent_qualified_name + result["collection_qualified_name"] = attrs.collection_qualified_name + result["is_visual_query"] = attrs.is_visual_query + result["visual_builder_schema_base64"] = attrs.visual_builder_schema_base64 + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _query_to_nested(query: Query) -> QueryNested: + """Convert flat Query to nested format.""" + attrs = QueryAttributes() + _populate_query_attrs(attrs, query) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + query, _QUERY_REL_FIELDS, QueryRelationshipAttributes + ) + return QueryNested( + guid=query.guid, + type_name=query.type_name, + status=query.status, + version=query.version, + create_time=query.create_time, + update_time=query.update_time, + created_by=query.created_by, + updated_by=query.updated_by, + classifications=query.classifications, + classification_names=query.classification_names, + meanings=query.meanings, + labels=query.labels, + business_attributes=query.business_attributes, + custom_attributes=query.custom_attributes, + pending_tasks=query.pending_tasks, + proxy=query.proxy, + is_incomplete=query.is_incomplete, + provenance_type=query.provenance_type, + home_id=query.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _query_from_nested(nested: QueryNested) -> Query: + """Convert nested format to flat Query.""" + attrs = nested.attributes if nested.attributes is not UNSET else QueryAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _QUERY_REL_FIELDS, + QueryRelationshipAttributes, + ) + return Query( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_query_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _query_to_nested_bytes(query: Query, serde: Serde) -> bytes: + """Convert flat Query to nested JSON bytes.""" + return serde.encode(_query_to_nested(query)) + + +def _query_from_nested_bytes(data: bytes, serde: Serde) -> Query: + """Convert nested JSON bytes to flat Query.""" + nested = serde.decode(data, QueryNested) + return _query_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +Query.RAW_QUERY = KeywordField("rawQuery", "rawQuery") +Query.LONG_RAW_QUERY = KeywordField("longRawQuery", "longRawQuery") +Query.RAW_QUERY_TEXT = KeywordField("rawQueryText", "rawQueryText") +Query.DEFAULT_SCHEMA_QUALIFIED_NAME = KeywordTextField( + "defaultSchemaQualifiedName", + "defaultSchemaQualifiedName", + "defaultSchemaQualifiedName.text", +) +Query.DEFAULT_DATABASE_QUALIFIED_NAME = KeywordTextField( + "defaultDatabaseQualifiedName", + "defaultDatabaseQualifiedName", + "defaultDatabaseQualifiedName.text", +) +Query.VARIABLES_SCHEMA_BASE64 = KeywordField( + "variablesSchemaBase64", "variablesSchemaBase64" +) +Query.IS_PRIVATE = BooleanField("isPrivate", "isPrivate") +Query.IS_SQL_SNIPPET = BooleanField("isSqlSnippet", "isSqlSnippet") +Query.PARENT_QUALIFIED_NAME = KeywordTextField( + "parentQualifiedName", "parentQualifiedName", "parentQualifiedName.text" +) +Query.COLLECTION_QUALIFIED_NAME = KeywordTextField( + "collectionQualifiedName", "collectionQualifiedName", "collectionQualifiedName.text" +) +Query.IS_VISUAL_QUERY = BooleanField("isVisualQuery", "isVisualQuery") +Query.VISUAL_BUILDER_SCHEMA_BASE64 = KeywordField( + "visualBuilderSchemaBase64", "visualBuilderSchemaBase64" +) +Query.QUERY_COUNT = NumericField("queryCount", "queryCount") +Query.QUERY_USER_COUNT = NumericField("queryUserCount", "queryUserCount") +Query.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +Query.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +Query.DATABASE_NAME = KeywordField("databaseName", "databaseName") +Query.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +Query.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +Query.SCHEMA_QUALIFIED_NAME = KeywordField("schemaQualifiedName", "schemaQualifiedName") +Query.TABLE_NAME = KeywordField("tableName", "tableName") +Query.TABLE_QUALIFIED_NAME = KeywordField("tableQualifiedName", "tableQualifiedName") +Query.VIEW_NAME = KeywordField("viewName", "viewName") +Query.VIEW_QUALIFIED_NAME = KeywordField("viewQualifiedName", "viewQualifiedName") +Query.CALCULATION_VIEW_NAME = KeywordField("calculationViewName", "calculationViewName") +Query.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +Query.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +Query.LAST_PROFILED_AT = NumericField("lastProfiledAt", "lastProfiledAt") +Query.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +Query.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +Query.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Query.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Query.ANOMALO_CHECKS = RelationField("anomaloChecks") +Query.APPLICATION = RelationField("application") +Query.APPLICATION_FIELD = RelationField("applicationField") +Query.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Query.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Query.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Query.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Query.METRICS = RelationField("metrics") +Query.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Query.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Query.DBT_MODELS = RelationField("dbtModels") +Query.SQL_DBT_MODELS = RelationField("sqlDbtModels") +Query.DBT_TESTS = RelationField("dbtTests") +Query.DBT_SOURCES = RelationField("dbtSources") +Query.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +Query.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +Query.MEANINGS = RelationField("meanings") +Query.MC_MONITORS = RelationField("mcMonitors") +Query.MC_INCIDENTS = RelationField("mcIncidents") +Query.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Query.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Query.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Query.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Query.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Query.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Query.FILES = RelationField("files") +Query.LINKS = RelationField("links") +Query.README = RelationField("readme") +Query.PARENT = RelationField("parent") +Query.TABLES = RelationField("tables") +Query.VIEWS = RelationField("views") +Query.COLUMNS = RelationField("columns") +Query.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Query.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +Query.SODA_CHECKS = RelationField("sodaChecks") +Query.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Query.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/quick_sight.py b/pyatlan_v9/model/assets/quick_sight.py new file mode 100644 index 000000000..cc304ebea --- /dev/null +++ b/pyatlan_v9/model/assets/quick_sight.py @@ -0,0 +1,560 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +QuickSight asset model with flattened inheritance. + +This module provides: +- QuickSight: Flat asset class (easy to use) +- QuickSightAttributes: Nested attributes struct (extends AssetAttributes) +- QuickSightNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class QuickSight(Asset): + """ + Base class for QuickSight assets. + """ + + QUICK_SIGHT_ID: ClassVar[Any] = None + QUICK_SIGHT_SHEET_ID: ClassVar[Any] = None + QUICK_SIGHT_SHEET_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "QuickSight" + + quick_sight_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the QuickSight asset.""" + + quick_sight_sheet_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the QuickSight sheet.""" + + quick_sight_sheet_name: Union[str, None, UnsetType] = UNSET + """Name of the QuickSight sheet.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "QuickSight" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _quick_sight_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> QuickSight: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + QuickSight instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _quick_sight_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class QuickSightAttributes(AssetAttributes): + """QuickSight-specific attributes for nested API format.""" + + quick_sight_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the QuickSight asset.""" + + quick_sight_sheet_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the QuickSight sheet.""" + + quick_sight_sheet_name: Union[str, None, UnsetType] = UNSET + """Name of the QuickSight sheet.""" + + +class QuickSightRelationshipAttributes(AssetRelationshipAttributes): + """QuickSight-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class QuickSightNested(AssetNested): + """QuickSight in nested API format for high-performance serialization.""" + + attributes: Union[QuickSightAttributes, UnsetType] = UNSET + relationship_attributes: Union[QuickSightRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + QuickSightRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + QuickSightRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_QUICK_SIGHT_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_quick_sight_attrs(attrs: QuickSightAttributes, obj: QuickSight) -> None: + """Populate QuickSight-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.quick_sight_id = obj.quick_sight_id + attrs.quick_sight_sheet_id = obj.quick_sight_sheet_id + attrs.quick_sight_sheet_name = obj.quick_sight_sheet_name + + +def _extract_quick_sight_attrs(attrs: QuickSightAttributes) -> dict: + """Extract all QuickSight attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["quick_sight_id"] = attrs.quick_sight_id + result["quick_sight_sheet_id"] = attrs.quick_sight_sheet_id + result["quick_sight_sheet_name"] = attrs.quick_sight_sheet_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _quick_sight_to_nested(quick_sight: QuickSight) -> QuickSightNested: + """Convert flat QuickSight to nested format.""" + attrs = QuickSightAttributes() + _populate_quick_sight_attrs(attrs, quick_sight) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + quick_sight, _QUICK_SIGHT_REL_FIELDS, QuickSightRelationshipAttributes + ) + return QuickSightNested( + guid=quick_sight.guid, + type_name=quick_sight.type_name, + status=quick_sight.status, + version=quick_sight.version, + create_time=quick_sight.create_time, + update_time=quick_sight.update_time, + created_by=quick_sight.created_by, + updated_by=quick_sight.updated_by, + classifications=quick_sight.classifications, + classification_names=quick_sight.classification_names, + meanings=quick_sight.meanings, + labels=quick_sight.labels, + business_attributes=quick_sight.business_attributes, + custom_attributes=quick_sight.custom_attributes, + pending_tasks=quick_sight.pending_tasks, + proxy=quick_sight.proxy, + is_incomplete=quick_sight.is_incomplete, + provenance_type=quick_sight.provenance_type, + home_id=quick_sight.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _quick_sight_from_nested(nested: QuickSightNested) -> QuickSight: + """Convert nested format to flat QuickSight.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else QuickSightAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _QUICK_SIGHT_REL_FIELDS, + QuickSightRelationshipAttributes, + ) + return QuickSight( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_quick_sight_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _quick_sight_to_nested_bytes(quick_sight: QuickSight, serde: Serde) -> bytes: + """Convert flat QuickSight to nested JSON bytes.""" + return serde.encode(_quick_sight_to_nested(quick_sight)) + + +def _quick_sight_from_nested_bytes(data: bytes, serde: Serde) -> QuickSight: + """Convert nested JSON bytes to flat QuickSight.""" + nested = serde.decode(data, QuickSightNested) + return _quick_sight_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + RelationField, +) + +QuickSight.QUICK_SIGHT_ID = KeywordField("quickSightId", "quickSightId") +QuickSight.QUICK_SIGHT_SHEET_ID = KeywordField("quickSightSheetId", "quickSightSheetId") +QuickSight.QUICK_SIGHT_SHEET_NAME = KeywordTextField( + "quickSightSheetName", "quickSightSheetName", "quickSightSheetName.text" +) +QuickSight.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +QuickSight.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +QuickSight.ANOMALO_CHECKS = RelationField("anomaloChecks") +QuickSight.APPLICATION = RelationField("application") +QuickSight.APPLICATION_FIELD = RelationField("applicationField") +QuickSight.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +QuickSight.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +QuickSight.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +QuickSight.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +QuickSight.METRICS = RelationField("metrics") +QuickSight.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +QuickSight.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +QuickSight.MEANINGS = RelationField("meanings") +QuickSight.MC_MONITORS = RelationField("mcMonitors") +QuickSight.MC_INCIDENTS = RelationField("mcIncidents") +QuickSight.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +QuickSight.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +QuickSight.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +QuickSight.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +QuickSight.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +QuickSight.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +QuickSight.FILES = RelationField("files") +QuickSight.LINKS = RelationField("links") +QuickSight.README = RelationField("readme") +QuickSight.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +QuickSight.SODA_CHECKS = RelationField("sodaChecks") +QuickSight.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +QuickSight.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/quick_sight_analysis.py b/pyatlan_v9/model/assets/quick_sight_analysis.py new file mode 100644 index 000000000..9d07542a4 --- /dev/null +++ b/pyatlan_v9/model/assets/quick_sight_analysis.py @@ -0,0 +1,726 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +QuickSightAnalysis asset model with flattened inheritance. + +This module provides: +- QuickSightAnalysis: Flat asset class (easy to use) +- QuickSightAnalysisAttributes: Nested attributes struct (extends AssetAttributes) +- QuickSightAnalysisNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .quick_sight_related import ( + RelatedQuickSightAnalysisVisual, + RelatedQuickSightFolder, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class QuickSightAnalysis(Asset): + """ + Instance of a QuickSight analysis in Atlan. In QuickSight, you analyze and visualize your data in analyses, which can be published as a dashboard to share with others. + """ + + QUICK_SIGHT_STATUS: ClassVar[Any] = None + QUICK_SIGHT_ANALYSIS_CALCULATED_FIELDS: ClassVar[Any] = None + QUICK_SIGHT_ANALYSIS_PARAMETER_DECLARATIONS: ClassVar[Any] = None + QUICK_SIGHT_ANALYSIS_FILTER_GROUPS: ClassVar[Any] = None + QUICK_SIGHT_ID: ClassVar[Any] = None + QUICK_SIGHT_SHEET_ID: ClassVar[Any] = None + QUICK_SIGHT_SHEET_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + QUICK_SIGHT_ANALYSIS_FOLDERS: ClassVar[Any] = None + QUICK_SIGHT_ANALYSIS_VISUALS: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "QuickSightAnalysis" + + quick_sight_status: Union[str, None, UnsetType] = UNSET + """Status of this analysis, for example: CREATION_IN_PROGRESS, UPDATE_SUCCESSFUL, etc.""" + + quick_sight_analysis_calculated_fields: Union[List[str], None, UnsetType] = UNSET + """List of field names calculated by this analysis.""" + + quick_sight_analysis_parameter_declarations: Union[List[str], None, UnsetType] = ( + UNSET + ) + """List of parameters used for this analysis.""" + + quick_sight_analysis_filter_groups: Union[List[str], None, UnsetType] = UNSET + """List of filter groups used for this analysis.""" + + quick_sight_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the QuickSight asset.""" + + quick_sight_sheet_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the QuickSight sheet.""" + + quick_sight_sheet_name: Union[str, None, UnsetType] = UNSET + """Name of the QuickSight sheet.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + quick_sight_analysis_folders: Union[ + List[RelatedQuickSightFolder], None, UnsetType + ] = UNSET + """""" + + quick_sight_analysis_visuals: Union[ + List[RelatedQuickSightAnalysisVisual], None, UnsetType + ] = UNSET + """Visuals that exist within this analysis.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "QuickSightAnalysis" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + connection_qualified_name: str, + quick_sight_id: str, + quick_sight_analysis_folders: Union[list[str], None] = None, + ) -> "QuickSightAnalysis": + validate_required_fields( + ["name", "connection_qualified_name", "quick_sight_id"], + [name, connection_qualified_name, quick_sight_id], + ) + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + qualified_name = f"{connection_qualified_name}/{quick_sight_id}" + return cls( + name=name, + quick_sight_id=quick_sight_id, + qualified_name=qualified_name, + connection_qualified_name=connection_qualified_name, + connector_name=connector_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "QuickSightAnalysis": + """Create a QuickSightAnalysis instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "QuickSightAnalysis": + """Return only fields required for update operations.""" + return QuickSightAnalysis.updater( + qualified_name=self.qualified_name, name=self.name + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _quick_sight_analysis_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> QuickSightAnalysis: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + QuickSightAnalysis instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _quick_sight_analysis_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class QuickSightAnalysisAttributes(AssetAttributes): + """QuickSightAnalysis-specific attributes for nested API format.""" + + quick_sight_status: Union[str, None, UnsetType] = UNSET + """Status of this analysis, for example: CREATION_IN_PROGRESS, UPDATE_SUCCESSFUL, etc.""" + + quick_sight_analysis_calculated_fields: Union[List[str], None, UnsetType] = UNSET + """List of field names calculated by this analysis.""" + + quick_sight_analysis_parameter_declarations: Union[List[str], None, UnsetType] = ( + UNSET + ) + """List of parameters used for this analysis.""" + + quick_sight_analysis_filter_groups: Union[List[str], None, UnsetType] = UNSET + """List of filter groups used for this analysis.""" + + quick_sight_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the QuickSight asset.""" + + quick_sight_sheet_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the QuickSight sheet.""" + + quick_sight_sheet_name: Union[str, None, UnsetType] = UNSET + """Name of the QuickSight sheet.""" + + +class QuickSightAnalysisRelationshipAttributes(AssetRelationshipAttributes): + """QuickSightAnalysis-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + quick_sight_analysis_folders: Union[ + List[RelatedQuickSightFolder], None, UnsetType + ] = UNSET + """""" + + quick_sight_analysis_visuals: Union[ + List[RelatedQuickSightAnalysisVisual], None, UnsetType + ] = UNSET + """Visuals that exist within this analysis.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class QuickSightAnalysisNested(AssetNested): + """QuickSightAnalysis in nested API format for high-performance serialization.""" + + attributes: Union[QuickSightAnalysisAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + QuickSightAnalysisRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + QuickSightAnalysisRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + QuickSightAnalysisRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_QUICK_SIGHT_ANALYSIS_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "quick_sight_analysis_folders", + "quick_sight_analysis_visuals", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_quick_sight_analysis_attrs( + attrs: QuickSightAnalysisAttributes, obj: QuickSightAnalysis +) -> None: + """Populate QuickSightAnalysis-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.quick_sight_status = obj.quick_sight_status + attrs.quick_sight_analysis_calculated_fields = ( + obj.quick_sight_analysis_calculated_fields + ) + attrs.quick_sight_analysis_parameter_declarations = ( + obj.quick_sight_analysis_parameter_declarations + ) + attrs.quick_sight_analysis_filter_groups = obj.quick_sight_analysis_filter_groups + attrs.quick_sight_id = obj.quick_sight_id + attrs.quick_sight_sheet_id = obj.quick_sight_sheet_id + attrs.quick_sight_sheet_name = obj.quick_sight_sheet_name + + +def _extract_quick_sight_analysis_attrs(attrs: QuickSightAnalysisAttributes) -> dict: + """Extract all QuickSightAnalysis attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["quick_sight_status"] = attrs.quick_sight_status + result["quick_sight_analysis_calculated_fields"] = ( + attrs.quick_sight_analysis_calculated_fields + ) + result["quick_sight_analysis_parameter_declarations"] = ( + attrs.quick_sight_analysis_parameter_declarations + ) + result["quick_sight_analysis_filter_groups"] = ( + attrs.quick_sight_analysis_filter_groups + ) + result["quick_sight_id"] = attrs.quick_sight_id + result["quick_sight_sheet_id"] = attrs.quick_sight_sheet_id + result["quick_sight_sheet_name"] = attrs.quick_sight_sheet_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _quick_sight_analysis_to_nested( + quick_sight_analysis: QuickSightAnalysis, +) -> QuickSightAnalysisNested: + """Convert flat QuickSightAnalysis to nested format.""" + attrs = QuickSightAnalysisAttributes() + _populate_quick_sight_analysis_attrs(attrs, quick_sight_analysis) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + quick_sight_analysis, + _QUICK_SIGHT_ANALYSIS_REL_FIELDS, + QuickSightAnalysisRelationshipAttributes, + ) + return QuickSightAnalysisNested( + guid=quick_sight_analysis.guid, + type_name=quick_sight_analysis.type_name, + status=quick_sight_analysis.status, + version=quick_sight_analysis.version, + create_time=quick_sight_analysis.create_time, + update_time=quick_sight_analysis.update_time, + created_by=quick_sight_analysis.created_by, + updated_by=quick_sight_analysis.updated_by, + classifications=quick_sight_analysis.classifications, + classification_names=quick_sight_analysis.classification_names, + meanings=quick_sight_analysis.meanings, + labels=quick_sight_analysis.labels, + business_attributes=quick_sight_analysis.business_attributes, + custom_attributes=quick_sight_analysis.custom_attributes, + pending_tasks=quick_sight_analysis.pending_tasks, + proxy=quick_sight_analysis.proxy, + is_incomplete=quick_sight_analysis.is_incomplete, + provenance_type=quick_sight_analysis.provenance_type, + home_id=quick_sight_analysis.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _quick_sight_analysis_from_nested( + nested: QuickSightAnalysisNested, +) -> QuickSightAnalysis: + """Convert nested format to flat QuickSightAnalysis.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else QuickSightAnalysisAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _QUICK_SIGHT_ANALYSIS_REL_FIELDS, + QuickSightAnalysisRelationshipAttributes, + ) + return QuickSightAnalysis( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_quick_sight_analysis_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _quick_sight_analysis_to_nested_bytes( + quick_sight_analysis: QuickSightAnalysis, serde: Serde +) -> bytes: + """Convert flat QuickSightAnalysis to nested JSON bytes.""" + return serde.encode(_quick_sight_analysis_to_nested(quick_sight_analysis)) + + +def _quick_sight_analysis_from_nested_bytes( + data: bytes, serde: Serde +) -> QuickSightAnalysis: + """Convert nested JSON bytes to flat QuickSightAnalysis.""" + nested = serde.decode(data, QuickSightAnalysisNested) + return _quick_sight_analysis_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + RelationField, +) + +QuickSightAnalysis.QUICK_SIGHT_STATUS = KeywordField( + "quickSightStatus", "quickSightStatus" +) +QuickSightAnalysis.QUICK_SIGHT_ANALYSIS_CALCULATED_FIELDS = KeywordField( + "quickSightAnalysisCalculatedFields", "quickSightAnalysisCalculatedFields" +) +QuickSightAnalysis.QUICK_SIGHT_ANALYSIS_PARAMETER_DECLARATIONS = KeywordField( + "quickSightAnalysisParameterDeclarations", "quickSightAnalysisParameterDeclarations" +) +QuickSightAnalysis.QUICK_SIGHT_ANALYSIS_FILTER_GROUPS = KeywordField( + "quickSightAnalysisFilterGroups", "quickSightAnalysisFilterGroups" +) +QuickSightAnalysis.QUICK_SIGHT_ID = KeywordField("quickSightId", "quickSightId") +QuickSightAnalysis.QUICK_SIGHT_SHEET_ID = KeywordField( + "quickSightSheetId", "quickSightSheetId" +) +QuickSightAnalysis.QUICK_SIGHT_SHEET_NAME = KeywordTextField( + "quickSightSheetName", "quickSightSheetName", "quickSightSheetName.text" +) +QuickSightAnalysis.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +QuickSightAnalysis.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +QuickSightAnalysis.ANOMALO_CHECKS = RelationField("anomaloChecks") +QuickSightAnalysis.APPLICATION = RelationField("application") +QuickSightAnalysis.APPLICATION_FIELD = RelationField("applicationField") +QuickSightAnalysis.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +QuickSightAnalysis.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +QuickSightAnalysis.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +QuickSightAnalysis.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +QuickSightAnalysis.METRICS = RelationField("metrics") +QuickSightAnalysis.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +QuickSightAnalysis.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +QuickSightAnalysis.MEANINGS = RelationField("meanings") +QuickSightAnalysis.MC_MONITORS = RelationField("mcMonitors") +QuickSightAnalysis.MC_INCIDENTS = RelationField("mcIncidents") +QuickSightAnalysis.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +QuickSightAnalysis.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +QuickSightAnalysis.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +QuickSightAnalysis.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +QuickSightAnalysis.QUICK_SIGHT_ANALYSIS_FOLDERS = RelationField( + "quickSightAnalysisFolders" +) +QuickSightAnalysis.QUICK_SIGHT_ANALYSIS_VISUALS = RelationField( + "quickSightAnalysisVisuals" +) +QuickSightAnalysis.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +QuickSightAnalysis.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +QuickSightAnalysis.FILES = RelationField("files") +QuickSightAnalysis.LINKS = RelationField("links") +QuickSightAnalysis.README = RelationField("readme") +QuickSightAnalysis.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +QuickSightAnalysis.SODA_CHECKS = RelationField("sodaChecks") +QuickSightAnalysis.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +QuickSightAnalysis.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/quick_sight_analysis_visual.py b/pyatlan_v9/model/assets/quick_sight_analysis_visual.py new file mode 100644 index 000000000..ad9ca755a --- /dev/null +++ b/pyatlan_v9/model/assets/quick_sight_analysis_visual.py @@ -0,0 +1,702 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +QuickSightAnalysisVisual asset model with flattened inheritance. + +This module provides: +- QuickSightAnalysisVisual: Flat asset class (easy to use) +- QuickSightAnalysisVisualAttributes: Nested attributes struct (extends AssetAttributes) +- QuickSightAnalysisVisualNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .quick_sight_related import RelatedQuickSightAnalysis + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class QuickSightAnalysisVisual(Asset): + """ + Instance of a QuickSight analysis visual in Atlan. These represent individual visuals inside an analysis. + """ + + QUICK_SIGHT_ANALYSIS_QUALIFIED_NAME: ClassVar[Any] = None + QUICK_SIGHT_ID: ClassVar[Any] = None + QUICK_SIGHT_SHEET_ID: ClassVar[Any] = None + QUICK_SIGHT_SHEET_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + QUICK_SIGHT_ANALYSIS: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "QuickSightAnalysisVisual" + + quick_sight_analysis_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the QuickSight analysis in which this visual exists.""" + + quick_sight_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the QuickSight asset.""" + + quick_sight_sheet_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the QuickSight sheet.""" + + quick_sight_sheet_name: Union[str, None, UnsetType] = UNSET + """Name of the QuickSight sheet.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + quick_sight_analysis: Union[RelatedQuickSightAnalysis, None, UnsetType] = UNSET + """Analysis in which this visual exists.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "QuickSightAnalysisVisual" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + quick_sight_id: str, + quick_sight_sheet_id: str, + quick_sight_sheet_name: str, + quick_sight_analysis_qualified_name: str, + connection_qualified_name: Union[str, None] = None, + ) -> "QuickSightAnalysisVisual": + validate_required_fields( + [ + "name", + "quick_sight_id", + "quick_sight_sheet_id", + "quick_sight_sheet_name", + "quick_sight_analysis_qualified_name", + ], + [ + name, + quick_sight_id, + quick_sight_sheet_id, + quick_sight_sheet_name, + quick_sight_analysis_qualified_name, + ], + ) + if connection_qualified_name: + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + else: + parts = quick_sight_analysis_qualified_name.split("/") + connector_name = parts[1] if len(parts) > 1 else None + connection_qualified_name = ( + "/".join(parts[:3]) + if len(parts) >= 3 + else quick_sight_analysis_qualified_name + ) + qualified_name = f"{quick_sight_analysis_qualified_name}/{quick_sight_sheet_id}/{quick_sight_id}" + return cls( + name=name, + quick_sight_id=quick_sight_id, + quick_sight_sheet_id=quick_sight_sheet_id, + quick_sight_sheet_name=quick_sight_sheet_name, + quick_sight_analysis_qualified_name=quick_sight_analysis_qualified_name, + qualified_name=qualified_name, + connection_qualified_name=connection_qualified_name, + connector_name=connector_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "QuickSightAnalysisVisual": + """Create a QuickSightAnalysisVisual instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "QuickSightAnalysisVisual": + """Return only fields required for update operations.""" + return QuickSightAnalysisVisual.updater( + qualified_name=self.qualified_name, name=self.name + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _quick_sight_analysis_visual_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> QuickSightAnalysisVisual: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + QuickSightAnalysisVisual instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _quick_sight_analysis_visual_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class QuickSightAnalysisVisualAttributes(AssetAttributes): + """QuickSightAnalysisVisual-specific attributes for nested API format.""" + + quick_sight_analysis_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the QuickSight analysis in which this visual exists.""" + + quick_sight_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the QuickSight asset.""" + + quick_sight_sheet_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the QuickSight sheet.""" + + quick_sight_sheet_name: Union[str, None, UnsetType] = UNSET + """Name of the QuickSight sheet.""" + + +class QuickSightAnalysisVisualRelationshipAttributes(AssetRelationshipAttributes): + """QuickSightAnalysisVisual-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + quick_sight_analysis: Union[RelatedQuickSightAnalysis, None, UnsetType] = UNSET + """Analysis in which this visual exists.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class QuickSightAnalysisVisualNested(AssetNested): + """QuickSightAnalysisVisual in nested API format for high-performance serialization.""" + + attributes: Union[QuickSightAnalysisVisualAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + QuickSightAnalysisVisualRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + QuickSightAnalysisVisualRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + QuickSightAnalysisVisualRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_QUICK_SIGHT_ANALYSIS_VISUAL_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "quick_sight_analysis", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_quick_sight_analysis_visual_attrs( + attrs: QuickSightAnalysisVisualAttributes, obj: QuickSightAnalysisVisual +) -> None: + """Populate QuickSightAnalysisVisual-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.quick_sight_analysis_qualified_name = obj.quick_sight_analysis_qualified_name + attrs.quick_sight_id = obj.quick_sight_id + attrs.quick_sight_sheet_id = obj.quick_sight_sheet_id + attrs.quick_sight_sheet_name = obj.quick_sight_sheet_name + + +def _extract_quick_sight_analysis_visual_attrs( + attrs: QuickSightAnalysisVisualAttributes, +) -> dict: + """Extract all QuickSightAnalysisVisual attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["quick_sight_analysis_qualified_name"] = ( + attrs.quick_sight_analysis_qualified_name + ) + result["quick_sight_id"] = attrs.quick_sight_id + result["quick_sight_sheet_id"] = attrs.quick_sight_sheet_id + result["quick_sight_sheet_name"] = attrs.quick_sight_sheet_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _quick_sight_analysis_visual_to_nested( + quick_sight_analysis_visual: QuickSightAnalysisVisual, +) -> QuickSightAnalysisVisualNested: + """Convert flat QuickSightAnalysisVisual to nested format.""" + attrs = QuickSightAnalysisVisualAttributes() + _populate_quick_sight_analysis_visual_attrs(attrs, quick_sight_analysis_visual) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + quick_sight_analysis_visual, + _QUICK_SIGHT_ANALYSIS_VISUAL_REL_FIELDS, + QuickSightAnalysisVisualRelationshipAttributes, + ) + return QuickSightAnalysisVisualNested( + guid=quick_sight_analysis_visual.guid, + type_name=quick_sight_analysis_visual.type_name, + status=quick_sight_analysis_visual.status, + version=quick_sight_analysis_visual.version, + create_time=quick_sight_analysis_visual.create_time, + update_time=quick_sight_analysis_visual.update_time, + created_by=quick_sight_analysis_visual.created_by, + updated_by=quick_sight_analysis_visual.updated_by, + classifications=quick_sight_analysis_visual.classifications, + classification_names=quick_sight_analysis_visual.classification_names, + meanings=quick_sight_analysis_visual.meanings, + labels=quick_sight_analysis_visual.labels, + business_attributes=quick_sight_analysis_visual.business_attributes, + custom_attributes=quick_sight_analysis_visual.custom_attributes, + pending_tasks=quick_sight_analysis_visual.pending_tasks, + proxy=quick_sight_analysis_visual.proxy, + is_incomplete=quick_sight_analysis_visual.is_incomplete, + provenance_type=quick_sight_analysis_visual.provenance_type, + home_id=quick_sight_analysis_visual.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _quick_sight_analysis_visual_from_nested( + nested: QuickSightAnalysisVisualNested, +) -> QuickSightAnalysisVisual: + """Convert nested format to flat QuickSightAnalysisVisual.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else QuickSightAnalysisVisualAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _QUICK_SIGHT_ANALYSIS_VISUAL_REL_FIELDS, + QuickSightAnalysisVisualRelationshipAttributes, + ) + return QuickSightAnalysisVisual( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_quick_sight_analysis_visual_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _quick_sight_analysis_visual_to_nested_bytes( + quick_sight_analysis_visual: QuickSightAnalysisVisual, serde: Serde +) -> bytes: + """Convert flat QuickSightAnalysisVisual to nested JSON bytes.""" + return serde.encode( + _quick_sight_analysis_visual_to_nested(quick_sight_analysis_visual) + ) + + +def _quick_sight_analysis_visual_from_nested_bytes( + data: bytes, serde: Serde +) -> QuickSightAnalysisVisual: + """Convert nested JSON bytes to flat QuickSightAnalysisVisual.""" + nested = serde.decode(data, QuickSightAnalysisVisualNested) + return _quick_sight_analysis_visual_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + RelationField, +) + +QuickSightAnalysisVisual.QUICK_SIGHT_ANALYSIS_QUALIFIED_NAME = KeywordTextField( + "quickSightAnalysisQualifiedName", + "quickSightAnalysisQualifiedName", + "quickSightAnalysisQualifiedName.text", +) +QuickSightAnalysisVisual.QUICK_SIGHT_ID = KeywordField("quickSightId", "quickSightId") +QuickSightAnalysisVisual.QUICK_SIGHT_SHEET_ID = KeywordField( + "quickSightSheetId", "quickSightSheetId" +) +QuickSightAnalysisVisual.QUICK_SIGHT_SHEET_NAME = KeywordTextField( + "quickSightSheetName", "quickSightSheetName", "quickSightSheetName.text" +) +QuickSightAnalysisVisual.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +QuickSightAnalysisVisual.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +QuickSightAnalysisVisual.ANOMALO_CHECKS = RelationField("anomaloChecks") +QuickSightAnalysisVisual.APPLICATION = RelationField("application") +QuickSightAnalysisVisual.APPLICATION_FIELD = RelationField("applicationField") +QuickSightAnalysisVisual.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +QuickSightAnalysisVisual.INPUT_PORT_DATA_PRODUCTS = RelationField( + "inputPortDataProducts" +) +QuickSightAnalysisVisual.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +QuickSightAnalysisVisual.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +QuickSightAnalysisVisual.METRICS = RelationField("metrics") +QuickSightAnalysisVisual.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +QuickSightAnalysisVisual.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +QuickSightAnalysisVisual.MEANINGS = RelationField("meanings") +QuickSightAnalysisVisual.MC_MONITORS = RelationField("mcMonitors") +QuickSightAnalysisVisual.MC_INCIDENTS = RelationField("mcIncidents") +QuickSightAnalysisVisual.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +QuickSightAnalysisVisual.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +QuickSightAnalysisVisual.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +QuickSightAnalysisVisual.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +QuickSightAnalysisVisual.QUICK_SIGHT_ANALYSIS = RelationField("quickSightAnalysis") +QuickSightAnalysisVisual.USER_DEF_RELATIONSHIP_TO = RelationField( + "userDefRelationshipTo" +) +QuickSightAnalysisVisual.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +QuickSightAnalysisVisual.FILES = RelationField("files") +QuickSightAnalysisVisual.LINKS = RelationField("links") +QuickSightAnalysisVisual.README = RelationField("readme") +QuickSightAnalysisVisual.SCHEMA_REGISTRY_SUBJECTS = RelationField( + "schemaRegistrySubjects" +) +QuickSightAnalysisVisual.SODA_CHECKS = RelationField("sodaChecks") +QuickSightAnalysisVisual.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +QuickSightAnalysisVisual.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/quick_sight_dashboard.py b/pyatlan_v9/model/assets/quick_sight_dashboard.py new file mode 100644 index 000000000..e2c3746fa --- /dev/null +++ b/pyatlan_v9/model/assets/quick_sight_dashboard.py @@ -0,0 +1,706 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +QuickSightDashboard asset model with flattened inheritance. + +This module provides: +- QuickSightDashboard: Flat asset class (easy to use) +- QuickSightDashboardAttributes: Nested attributes struct (extends AssetAttributes) +- QuickSightDashboardNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .quick_sight_related import ( + RelatedQuickSightDashboardVisual, + RelatedQuickSightFolder, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class QuickSightDashboard(Asset): + """ + Instance of a QuickSight dashboard in Atlan. These are reports in QuickSight, created from analyses. + """ + + QUICK_SIGHT_PUBLISHED_VERSION_NUMBER: ClassVar[Any] = None + QUICK_SIGHT_LAST_PUBLISHED_TIME: ClassVar[Any] = None + QUICK_SIGHT_ID: ClassVar[Any] = None + QUICK_SIGHT_SHEET_ID: ClassVar[Any] = None + QUICK_SIGHT_SHEET_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + QUICK_SIGHT_DASHBOARD_VISUALS: ClassVar[Any] = None + QUICK_SIGHT_DASHBOARD_FOLDERS: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "QuickSightDashboard" + + quick_sight_published_version_number: Union[int, None, UnsetType] = UNSET + """Version number of the published dashboard.""" + + quick_sight_last_published_time: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this dashboard was last published, in milliseconds.""" + + quick_sight_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the QuickSight asset.""" + + quick_sight_sheet_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the QuickSight sheet.""" + + quick_sight_sheet_name: Union[str, None, UnsetType] = UNSET + """Name of the QuickSight sheet.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + quick_sight_dashboard_visuals: Union[ + List[RelatedQuickSightDashboardVisual], None, UnsetType + ] = UNSET + """Visuals that exist within this dashboard.""" + + quick_sight_dashboard_folders: Union[ + List[RelatedQuickSightFolder], None, UnsetType + ] = UNSET + """""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "QuickSightDashboard" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + connection_qualified_name: str, + quick_sight_id: str, + quick_sight_dashboard_folders: Union[list[str], None] = None, + ) -> "QuickSightDashboard": + """Create a new QuickSightDashboard asset.""" + validate_required_fields( + ["name", "connection_qualified_name", "quick_sight_id"], + [name, connection_qualified_name, quick_sight_id], + ) + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + folder_refs = ( + [ + RelatedQuickSightFolder(unique_attributes={"qualifiedName": folder_qn}) + for folder_qn in quick_sight_dashboard_folders + ] + if quick_sight_dashboard_folders + else UNSET + ) + return cls( + name=name, + quick_sight_id=quick_sight_id, + qualified_name=f"{connection_qualified_name}/{quick_sight_id}", + connection_qualified_name=connection_qualified_name, + connector_name=connector_name, + quick_sight_dashboard_folders=folder_refs, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "QuickSightDashboard": + """Create a QuickSightDashboard instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "QuickSightDashboard": + """Return only fields required for update operations.""" + return QuickSightDashboard.updater( + qualified_name=self.qualified_name, name=self.name + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _quick_sight_dashboard_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> QuickSightDashboard: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + QuickSightDashboard instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _quick_sight_dashboard_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class QuickSightDashboardAttributes(AssetAttributes): + """QuickSightDashboard-specific attributes for nested API format.""" + + quick_sight_published_version_number: Union[int, None, UnsetType] = UNSET + """Version number of the published dashboard.""" + + quick_sight_last_published_time: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this dashboard was last published, in milliseconds.""" + + quick_sight_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the QuickSight asset.""" + + quick_sight_sheet_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the QuickSight sheet.""" + + quick_sight_sheet_name: Union[str, None, UnsetType] = UNSET + """Name of the QuickSight sheet.""" + + +class QuickSightDashboardRelationshipAttributes(AssetRelationshipAttributes): + """QuickSightDashboard-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + quick_sight_dashboard_visuals: Union[ + List[RelatedQuickSightDashboardVisual], None, UnsetType + ] = UNSET + """Visuals that exist within this dashboard.""" + + quick_sight_dashboard_folders: Union[ + List[RelatedQuickSightFolder], None, UnsetType + ] = UNSET + """""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class QuickSightDashboardNested(AssetNested): + """QuickSightDashboard in nested API format for high-performance serialization.""" + + attributes: Union[QuickSightDashboardAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + QuickSightDashboardRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + QuickSightDashboardRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + QuickSightDashboardRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_QUICK_SIGHT_DASHBOARD_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "quick_sight_dashboard_visuals", + "quick_sight_dashboard_folders", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_quick_sight_dashboard_attrs( + attrs: QuickSightDashboardAttributes, obj: QuickSightDashboard +) -> None: + """Populate QuickSightDashboard-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.quick_sight_published_version_number = ( + obj.quick_sight_published_version_number + ) + attrs.quick_sight_last_published_time = obj.quick_sight_last_published_time + attrs.quick_sight_id = obj.quick_sight_id + attrs.quick_sight_sheet_id = obj.quick_sight_sheet_id + attrs.quick_sight_sheet_name = obj.quick_sight_sheet_name + + +def _extract_quick_sight_dashboard_attrs(attrs: QuickSightDashboardAttributes) -> dict: + """Extract all QuickSightDashboard attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["quick_sight_published_version_number"] = ( + attrs.quick_sight_published_version_number + ) + result["quick_sight_last_published_time"] = attrs.quick_sight_last_published_time + result["quick_sight_id"] = attrs.quick_sight_id + result["quick_sight_sheet_id"] = attrs.quick_sight_sheet_id + result["quick_sight_sheet_name"] = attrs.quick_sight_sheet_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _quick_sight_dashboard_to_nested( + quick_sight_dashboard: QuickSightDashboard, +) -> QuickSightDashboardNested: + """Convert flat QuickSightDashboard to nested format.""" + attrs = QuickSightDashboardAttributes() + _populate_quick_sight_dashboard_attrs(attrs, quick_sight_dashboard) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + quick_sight_dashboard, + _QUICK_SIGHT_DASHBOARD_REL_FIELDS, + QuickSightDashboardRelationshipAttributes, + ) + return QuickSightDashboardNested( + guid=quick_sight_dashboard.guid, + type_name=quick_sight_dashboard.type_name, + status=quick_sight_dashboard.status, + version=quick_sight_dashboard.version, + create_time=quick_sight_dashboard.create_time, + update_time=quick_sight_dashboard.update_time, + created_by=quick_sight_dashboard.created_by, + updated_by=quick_sight_dashboard.updated_by, + classifications=quick_sight_dashboard.classifications, + classification_names=quick_sight_dashboard.classification_names, + meanings=quick_sight_dashboard.meanings, + labels=quick_sight_dashboard.labels, + business_attributes=quick_sight_dashboard.business_attributes, + custom_attributes=quick_sight_dashboard.custom_attributes, + pending_tasks=quick_sight_dashboard.pending_tasks, + proxy=quick_sight_dashboard.proxy, + is_incomplete=quick_sight_dashboard.is_incomplete, + provenance_type=quick_sight_dashboard.provenance_type, + home_id=quick_sight_dashboard.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _quick_sight_dashboard_from_nested( + nested: QuickSightDashboardNested, +) -> QuickSightDashboard: + """Convert nested format to flat QuickSightDashboard.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else QuickSightDashboardAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _QUICK_SIGHT_DASHBOARD_REL_FIELDS, + QuickSightDashboardRelationshipAttributes, + ) + return QuickSightDashboard( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_quick_sight_dashboard_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _quick_sight_dashboard_to_nested_bytes( + quick_sight_dashboard: QuickSightDashboard, serde: Serde +) -> bytes: + """Convert flat QuickSightDashboard to nested JSON bytes.""" + return serde.encode(_quick_sight_dashboard_to_nested(quick_sight_dashboard)) + + +def _quick_sight_dashboard_from_nested_bytes( + data: bytes, serde: Serde +) -> QuickSightDashboard: + """Convert nested JSON bytes to flat QuickSightDashboard.""" + nested = serde.decode(data, QuickSightDashboardNested) + return _quick_sight_dashboard_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +QuickSightDashboard.QUICK_SIGHT_PUBLISHED_VERSION_NUMBER = NumericField( + "quickSightPublishedVersionNumber", "quickSightPublishedVersionNumber" +) +QuickSightDashboard.QUICK_SIGHT_LAST_PUBLISHED_TIME = NumericField( + "quickSightLastPublishedTime", "quickSightLastPublishedTime" +) +QuickSightDashboard.QUICK_SIGHT_ID = KeywordField("quickSightId", "quickSightId") +QuickSightDashboard.QUICK_SIGHT_SHEET_ID = KeywordField( + "quickSightSheetId", "quickSightSheetId" +) +QuickSightDashboard.QUICK_SIGHT_SHEET_NAME = KeywordTextField( + "quickSightSheetName", "quickSightSheetName", "quickSightSheetName.text" +) +QuickSightDashboard.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +QuickSightDashboard.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +QuickSightDashboard.ANOMALO_CHECKS = RelationField("anomaloChecks") +QuickSightDashboard.APPLICATION = RelationField("application") +QuickSightDashboard.APPLICATION_FIELD = RelationField("applicationField") +QuickSightDashboard.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +QuickSightDashboard.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +QuickSightDashboard.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +QuickSightDashboard.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +QuickSightDashboard.METRICS = RelationField("metrics") +QuickSightDashboard.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +QuickSightDashboard.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +QuickSightDashboard.MEANINGS = RelationField("meanings") +QuickSightDashboard.MC_MONITORS = RelationField("mcMonitors") +QuickSightDashboard.MC_INCIDENTS = RelationField("mcIncidents") +QuickSightDashboard.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +QuickSightDashboard.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +QuickSightDashboard.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +QuickSightDashboard.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +QuickSightDashboard.QUICK_SIGHT_DASHBOARD_VISUALS = RelationField( + "quickSightDashboardVisuals" +) +QuickSightDashboard.QUICK_SIGHT_DASHBOARD_FOLDERS = RelationField( + "quickSightDashboardFolders" +) +QuickSightDashboard.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +QuickSightDashboard.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +QuickSightDashboard.FILES = RelationField("files") +QuickSightDashboard.LINKS = RelationField("links") +QuickSightDashboard.README = RelationField("readme") +QuickSightDashboard.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +QuickSightDashboard.SODA_CHECKS = RelationField("sodaChecks") +QuickSightDashboard.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +QuickSightDashboard.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/quick_sight_dashboard_visual.py b/pyatlan_v9/model/assets/quick_sight_dashboard_visual.py new file mode 100644 index 000000000..866dea314 --- /dev/null +++ b/pyatlan_v9/model/assets/quick_sight_dashboard_visual.py @@ -0,0 +1,704 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +QuickSightDashboardVisual asset model with flattened inheritance. + +This module provides: +- QuickSightDashboardVisual: Flat asset class (easy to use) +- QuickSightDashboardVisualAttributes: Nested attributes struct (extends AssetAttributes) +- QuickSightDashboardVisualNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .quick_sight_related import RelatedQuickSightDashboard + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class QuickSightDashboardVisual(Asset): + """ + Instance of a QuickSight dashboard visual in Atlan. These represent individual visuals inside a dashboard. + """ + + QUICK_SIGHT_DASHBOARD_QUALIFIED_NAME: ClassVar[Any] = None + QUICK_SIGHT_ID: ClassVar[Any] = None + QUICK_SIGHT_SHEET_ID: ClassVar[Any] = None + QUICK_SIGHT_SHEET_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + QUICK_SIGHT_DASHBOARD: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "QuickSightDashboardVisual" + + quick_sight_dashboard_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dashboard in which this visual exists.""" + + quick_sight_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the QuickSight asset.""" + + quick_sight_sheet_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the QuickSight sheet.""" + + quick_sight_sheet_name: Union[str, None, UnsetType] = UNSET + """Name of the QuickSight sheet.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + quick_sight_dashboard: Union[RelatedQuickSightDashboard, None, UnsetType] = UNSET + """Dashboard in which this visual exists.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "QuickSightDashboardVisual" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + quick_sight_id: str, + quick_sight_sheet_id: str, + quick_sight_sheet_name: str, + quick_sight_dashboard_qualified_name: str, + connection_qualified_name: Union[str, None] = None, + ) -> "QuickSightDashboardVisual": + validate_required_fields( + [ + "name", + "quick_sight_id", + "quick_sight_sheet_id", + "quick_sight_sheet_name", + "quick_sight_dashboard_qualified_name", + ], + [ + name, + quick_sight_id, + quick_sight_sheet_id, + quick_sight_sheet_name, + quick_sight_dashboard_qualified_name, + ], + ) + if connection_qualified_name: + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + else: + parts = quick_sight_dashboard_qualified_name.split("/") + connector_name = parts[1] if len(parts) > 1 else None + connection_qualified_name = ( + "/".join(parts[:3]) + if len(parts) >= 3 + else quick_sight_dashboard_qualified_name + ) + qualified_name = f"{quick_sight_dashboard_qualified_name}/{quick_sight_sheet_id}/{quick_sight_id}" + return cls( + name=name, + quick_sight_id=quick_sight_id, + quick_sight_sheet_id=quick_sight_sheet_id, + quick_sight_sheet_name=quick_sight_sheet_name, + quick_sight_dashboard_qualified_name=quick_sight_dashboard_qualified_name, + qualified_name=qualified_name, + connection_qualified_name=connection_qualified_name, + connector_name=connector_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "QuickSightDashboardVisual": + """Create a QuickSightDashboardVisual instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "QuickSightDashboardVisual": + """Return only fields required for update operations.""" + return QuickSightDashboardVisual.updater( + qualified_name=self.qualified_name, name=self.name + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _quick_sight_dashboard_visual_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> QuickSightDashboardVisual: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + QuickSightDashboardVisual instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _quick_sight_dashboard_visual_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class QuickSightDashboardVisualAttributes(AssetAttributes): + """QuickSightDashboardVisual-specific attributes for nested API format.""" + + quick_sight_dashboard_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dashboard in which this visual exists.""" + + quick_sight_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the QuickSight asset.""" + + quick_sight_sheet_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the QuickSight sheet.""" + + quick_sight_sheet_name: Union[str, None, UnsetType] = UNSET + """Name of the QuickSight sheet.""" + + +class QuickSightDashboardVisualRelationshipAttributes(AssetRelationshipAttributes): + """QuickSightDashboardVisual-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + quick_sight_dashboard: Union[RelatedQuickSightDashboard, None, UnsetType] = UNSET + """Dashboard in which this visual exists.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class QuickSightDashboardVisualNested(AssetNested): + """QuickSightDashboardVisual in nested API format for high-performance serialization.""" + + attributes: Union[QuickSightDashboardVisualAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + QuickSightDashboardVisualRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + QuickSightDashboardVisualRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + QuickSightDashboardVisualRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_QUICK_SIGHT_DASHBOARD_VISUAL_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "quick_sight_dashboard", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_quick_sight_dashboard_visual_attrs( + attrs: QuickSightDashboardVisualAttributes, obj: QuickSightDashboardVisual +) -> None: + """Populate QuickSightDashboardVisual-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.quick_sight_dashboard_qualified_name = ( + obj.quick_sight_dashboard_qualified_name + ) + attrs.quick_sight_id = obj.quick_sight_id + attrs.quick_sight_sheet_id = obj.quick_sight_sheet_id + attrs.quick_sight_sheet_name = obj.quick_sight_sheet_name + + +def _extract_quick_sight_dashboard_visual_attrs( + attrs: QuickSightDashboardVisualAttributes, +) -> dict: + """Extract all QuickSightDashboardVisual attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["quick_sight_dashboard_qualified_name"] = ( + attrs.quick_sight_dashboard_qualified_name + ) + result["quick_sight_id"] = attrs.quick_sight_id + result["quick_sight_sheet_id"] = attrs.quick_sight_sheet_id + result["quick_sight_sheet_name"] = attrs.quick_sight_sheet_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _quick_sight_dashboard_visual_to_nested( + quick_sight_dashboard_visual: QuickSightDashboardVisual, +) -> QuickSightDashboardVisualNested: + """Convert flat QuickSightDashboardVisual to nested format.""" + attrs = QuickSightDashboardVisualAttributes() + _populate_quick_sight_dashboard_visual_attrs(attrs, quick_sight_dashboard_visual) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + quick_sight_dashboard_visual, + _QUICK_SIGHT_DASHBOARD_VISUAL_REL_FIELDS, + QuickSightDashboardVisualRelationshipAttributes, + ) + return QuickSightDashboardVisualNested( + guid=quick_sight_dashboard_visual.guid, + type_name=quick_sight_dashboard_visual.type_name, + status=quick_sight_dashboard_visual.status, + version=quick_sight_dashboard_visual.version, + create_time=quick_sight_dashboard_visual.create_time, + update_time=quick_sight_dashboard_visual.update_time, + created_by=quick_sight_dashboard_visual.created_by, + updated_by=quick_sight_dashboard_visual.updated_by, + classifications=quick_sight_dashboard_visual.classifications, + classification_names=quick_sight_dashboard_visual.classification_names, + meanings=quick_sight_dashboard_visual.meanings, + labels=quick_sight_dashboard_visual.labels, + business_attributes=quick_sight_dashboard_visual.business_attributes, + custom_attributes=quick_sight_dashboard_visual.custom_attributes, + pending_tasks=quick_sight_dashboard_visual.pending_tasks, + proxy=quick_sight_dashboard_visual.proxy, + is_incomplete=quick_sight_dashboard_visual.is_incomplete, + provenance_type=quick_sight_dashboard_visual.provenance_type, + home_id=quick_sight_dashboard_visual.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _quick_sight_dashboard_visual_from_nested( + nested: QuickSightDashboardVisualNested, +) -> QuickSightDashboardVisual: + """Convert nested format to flat QuickSightDashboardVisual.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else QuickSightDashboardVisualAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _QUICK_SIGHT_DASHBOARD_VISUAL_REL_FIELDS, + QuickSightDashboardVisualRelationshipAttributes, + ) + return QuickSightDashboardVisual( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_quick_sight_dashboard_visual_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _quick_sight_dashboard_visual_to_nested_bytes( + quick_sight_dashboard_visual: QuickSightDashboardVisual, serde: Serde +) -> bytes: + """Convert flat QuickSightDashboardVisual to nested JSON bytes.""" + return serde.encode( + _quick_sight_dashboard_visual_to_nested(quick_sight_dashboard_visual) + ) + + +def _quick_sight_dashboard_visual_from_nested_bytes( + data: bytes, serde: Serde +) -> QuickSightDashboardVisual: + """Convert nested JSON bytes to flat QuickSightDashboardVisual.""" + nested = serde.decode(data, QuickSightDashboardVisualNested) + return _quick_sight_dashboard_visual_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + RelationField, +) + +QuickSightDashboardVisual.QUICK_SIGHT_DASHBOARD_QUALIFIED_NAME = KeywordTextField( + "quickSightDashboardQualifiedName", + "quickSightDashboardQualifiedName", + "quickSightDashboardQualifiedName.text", +) +QuickSightDashboardVisual.QUICK_SIGHT_ID = KeywordField("quickSightId", "quickSightId") +QuickSightDashboardVisual.QUICK_SIGHT_SHEET_ID = KeywordField( + "quickSightSheetId", "quickSightSheetId" +) +QuickSightDashboardVisual.QUICK_SIGHT_SHEET_NAME = KeywordTextField( + "quickSightSheetName", "quickSightSheetName", "quickSightSheetName.text" +) +QuickSightDashboardVisual.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +QuickSightDashboardVisual.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +QuickSightDashboardVisual.ANOMALO_CHECKS = RelationField("anomaloChecks") +QuickSightDashboardVisual.APPLICATION = RelationField("application") +QuickSightDashboardVisual.APPLICATION_FIELD = RelationField("applicationField") +QuickSightDashboardVisual.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +QuickSightDashboardVisual.INPUT_PORT_DATA_PRODUCTS = RelationField( + "inputPortDataProducts" +) +QuickSightDashboardVisual.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +QuickSightDashboardVisual.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +QuickSightDashboardVisual.METRICS = RelationField("metrics") +QuickSightDashboardVisual.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +QuickSightDashboardVisual.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +QuickSightDashboardVisual.MEANINGS = RelationField("meanings") +QuickSightDashboardVisual.MC_MONITORS = RelationField("mcMonitors") +QuickSightDashboardVisual.MC_INCIDENTS = RelationField("mcIncidents") +QuickSightDashboardVisual.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +QuickSightDashboardVisual.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +QuickSightDashboardVisual.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +QuickSightDashboardVisual.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +QuickSightDashboardVisual.QUICK_SIGHT_DASHBOARD = RelationField("quickSightDashboard") +QuickSightDashboardVisual.USER_DEF_RELATIONSHIP_TO = RelationField( + "userDefRelationshipTo" +) +QuickSightDashboardVisual.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +QuickSightDashboardVisual.FILES = RelationField("files") +QuickSightDashboardVisual.LINKS = RelationField("links") +QuickSightDashboardVisual.README = RelationField("readme") +QuickSightDashboardVisual.SCHEMA_REGISTRY_SUBJECTS = RelationField( + "schemaRegistrySubjects" +) +QuickSightDashboardVisual.SODA_CHECKS = RelationField("sodaChecks") +QuickSightDashboardVisual.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +QuickSightDashboardVisual.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/quick_sight_dataset.py b/pyatlan_v9/model/assets/quick_sight_dataset.py new file mode 100644 index 000000000..18d7fef0e --- /dev/null +++ b/pyatlan_v9/model/assets/quick_sight_dataset.py @@ -0,0 +1,313 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +QuickSightDataset asset model with flattened inheritance. + +This module provides: +- QuickSightDataset: Flat asset class (easy to use) +- QuickSightDatasetAttributes: Nested attributes struct (extends AssetAttributes) +- QuickSightDatasetNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Union + +import msgspec +from msgspec import UNSET, UnsetType + +from pyatlan_v9.model.conversion_utils import ( + build_attributes_kwargs, + build_flat_kwargs, + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .asset import Asset, AssetAttributes, AssetNested, AssetRelationshipAttributes +from .quick_sight_related import RelatedQuickSightDatasetField, RelatedQuickSightFolder + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class QuickSightDataset(Asset): + """ + Instance of a QuickSight dataset in Atlan. These are an internal data model built to be used by analysis. In a dataset, data can be pulled from different sources, joined, filtered, and columns translated to more business-friendly names when preparing the data for visualizing in the analysis layer. + """ + + # Override type_name with QuickSightDataset-specific default + type_name: Union[str, UnsetType] = "QuickSightDataset" + + quick_sight_dataset_import_mode: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="quickSightDatasetImportMode" + ) + """Import mode for this dataset, for example: SPICE or DIRECT_QUERY.""" + + quick_sight_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns present in this dataset.""" + + quick_sight_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the QuickSight asset.""" + + quick_sight_sheet_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the QuickSight sheet.""" + + quick_sight_sheet_name: Union[str, None, UnsetType] = UNSET + """Name of the QuickSight sheet.""" + + quick_sight_dataset_folders: Union[ + list[RelatedQuickSightFolder], None, UnsetType + ] = UNSET + """""" + + quick_sight_dataset_fields: Union[ + list[RelatedQuickSightDatasetField], None, UnsetType + ] = UNSET + """Fields that exist within this dataset.""" + + # ========================================================================= + # Convenience Methods + # ========================================================================= + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + connection_qualified_name: str, + quick_sight_id: str, + quick_sight_dataset_import_mode: Union[str, None] = None, + quick_sight_dataset_folders: Union[list[str], None] = None, + ) -> "QuickSightDataset": + """Create a new QuickSightDataset asset.""" + validate_required_fields( + ["name", "connection_qualified_name", "quick_sight_id"], + [name, connection_qualified_name, quick_sight_id], + ) + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + folder_refs = ( + [ + RelatedQuickSightFolder(unique_attributes={"qualifiedName": folder_qn}) + for folder_qn in quick_sight_dataset_folders + ] + if quick_sight_dataset_folders + else UNSET + ) + return cls( + name=name, + quick_sight_id=quick_sight_id, + qualified_name=f"{connection_qualified_name}/{quick_sight_id}", + connection_qualified_name=connection_qualified_name, + connector_name=connector_name, + quick_sight_dataset_import_mode=quick_sight_dataset_import_mode + if quick_sight_dataset_import_mode is not None + else UNSET, + quick_sight_dataset_folders=folder_refs, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "QuickSightDataset": + """Create a QuickSightDataset instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "QuickSightDataset": + """Return only fields required for update operations.""" + return QuickSightDataset.updater( + qualified_name=self.qualified_name, name=self.name + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return _quick_sight_dataset_to_nested_bytes(self, serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + @staticmethod + def from_json( + json_data: Union[str, bytes], serde: Serde | None = None + ) -> "QuickSightDataset": + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + QuickSightDataset instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _quick_sight_dataset_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class QuickSightDatasetAttributes(AssetAttributes): + """QuickSightDataset-specific attributes for nested API format.""" + + quick_sight_dataset_import_mode: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="quickSightDatasetImportMode" + ) + """Import mode for this dataset, for example: SPICE or DIRECT_QUERY.""" + + quick_sight_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns present in this dataset.""" + + quick_sight_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the QuickSight asset.""" + + quick_sight_sheet_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the QuickSight sheet.""" + + quick_sight_sheet_name: Union[str, None, UnsetType] = UNSET + """Name of the QuickSight sheet.""" + + +class QuickSightDatasetRelationshipAttributes(AssetRelationshipAttributes): + """QuickSightDataset-specific relationship attributes for nested API format.""" + + quick_sight_dataset_folders: Union[ + list[RelatedQuickSightFolder], None, UnsetType + ] = UNSET + """""" + + quick_sight_dataset_fields: Union[ + list[RelatedQuickSightDatasetField], None, UnsetType + ] = UNSET + """Fields that exist within this dataset.""" + + +class QuickSightDatasetNested(AssetNested): + """QuickSightDataset in nested API format for high-performance serialization.""" + + attributes: Union[QuickSightDatasetAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + QuickSightDatasetRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + QuickSightDatasetRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + QuickSightDatasetRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _quick_sight_dataset_to_nested( + quick_sight_dataset: QuickSightDataset, +) -> QuickSightDatasetNested: + """Convert flat QuickSightDataset to nested format.""" + attrs_kwargs = build_attributes_kwargs( + quick_sight_dataset, QuickSightDatasetAttributes + ) + attrs = QuickSightDatasetAttributes(**attrs_kwargs) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + rel_fields: list[str] = [ + "quick_sight_dataset_folders", + "quick_sight_dataset_fields", + ] + replace_rels, append_rels, remove_rels = categorize_relationships( + quick_sight_dataset, rel_fields, QuickSightDatasetRelationshipAttributes + ) + return QuickSightDatasetNested( + guid=quick_sight_dataset.guid, + type_name=quick_sight_dataset.type_name, + status=quick_sight_dataset.status, + version=quick_sight_dataset.version, + create_time=quick_sight_dataset.create_time, + update_time=quick_sight_dataset.update_time, + created_by=quick_sight_dataset.created_by, + updated_by=quick_sight_dataset.updated_by, + classifications=quick_sight_dataset.classifications, + classification_names=quick_sight_dataset.classification_names, + meanings=quick_sight_dataset.meanings, + labels=quick_sight_dataset.labels, + business_attributes=quick_sight_dataset.business_attributes, + custom_attributes=quick_sight_dataset.custom_attributes, + pending_tasks=quick_sight_dataset.pending_tasks, + proxy=quick_sight_dataset.proxy, + is_incomplete=quick_sight_dataset.is_incomplete, + provenance_type=quick_sight_dataset.provenance_type, + home_id=quick_sight_dataset.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _quick_sight_dataset_from_nested( + nested: QuickSightDatasetNested, +) -> QuickSightDataset: + """Convert nested format to flat QuickSightDataset.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else QuickSightDatasetAttributes() + ) + rel_fields: list[str] = [ + "quick_sight_dataset_folders", + "quick_sight_dataset_fields", + ] + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + rel_fields, + QuickSightDatasetRelationshipAttributes, + ) + kwargs = build_flat_kwargs( + nested, attrs, merged_rels, AssetNested, QuickSightDatasetAttributes + ) + return QuickSightDataset(**kwargs) + + +def _quick_sight_dataset_to_nested_bytes( + quick_sight_dataset: QuickSightDataset, serde: Serde +) -> bytes: + """Convert flat QuickSightDataset to nested JSON bytes.""" + return serde.encode(_quick_sight_dataset_to_nested(quick_sight_dataset)) + + +def _quick_sight_dataset_from_nested_bytes( + data: bytes, serde: Serde +) -> QuickSightDataset: + """Convert nested JSON bytes to flat QuickSightDataset.""" + nested = serde.decode(data, QuickSightDatasetNested) + return _quick_sight_dataset_from_nested(nested) diff --git a/pyatlan_v9/model/assets/quick_sight_dataset_field.py b/pyatlan_v9/model/assets/quick_sight_dataset_field.py new file mode 100644 index 000000000..e9fd08b8c --- /dev/null +++ b/pyatlan_v9/model/assets/quick_sight_dataset_field.py @@ -0,0 +1,302 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +QuickSightDatasetField asset model with flattened inheritance. + +This module provides: +- QuickSightDatasetField: Flat asset class (easy to use) +- QuickSightDatasetFieldAttributes: Nested attributes struct (extends AssetAttributes) +- QuickSightDatasetFieldNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Union + +import msgspec +from msgspec import UNSET, UnsetType + +from pyatlan_v9.model.conversion_utils import ( + build_attributes_kwargs, + build_flat_kwargs, + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .asset import Asset, AssetAttributes, AssetNested, AssetRelationshipAttributes +from .quick_sight_related import RelatedQuickSightDataset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class QuickSightDatasetField(Asset): + """ + Instance of a QuickSight dataset field in Atlan. + """ + + # Override type_name with QuickSightDatasetField-specific default + type_name: Union[str, UnsetType] = "QuickSightDatasetField" + + quick_sight_dataset_field_type: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="quickSightDatasetFieldType" + ) + """Datatype of this field, for example: STRING, INTEGER, etc.""" + + quick_sight_dataset_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dataset in which this field exists.""" + + quick_sight_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the QuickSight asset.""" + + quick_sight_sheet_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the QuickSight sheet.""" + + quick_sight_sheet_name: Union[str, None, UnsetType] = UNSET + """Name of the QuickSight sheet.""" + + quick_sight_dataset: Union[RelatedQuickSightDataset, None, UnsetType] = UNSET + """Dataset in which this field exists.""" + + # ========================================================================= + # Convenience Methods + # ========================================================================= + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + quick_sight_dataset_qualified_name: str, + quick_sight_id: str, + quick_sight_dataset_field_type: Union[str, None] = None, + connection_qualified_name: Union[str, None] = None, + ) -> "QuickSightDatasetField": + """Create a new QuickSightDatasetField asset.""" + validate_required_fields( + ["name", "quick_sight_dataset_qualified_name", "quick_sight_id"], + [name, quick_sight_dataset_qualified_name, quick_sight_id], + ) + if connection_qualified_name: + connector_name = ( + connection_qualified_name.split("/")[1] + if len(connection_qualified_name.split("/")) > 1 + else "" + ) + else: + fields = quick_sight_dataset_qualified_name.split("/") + if len(fields) < 3: + raise ValueError("quick_sight_dataset_qualified_name is invalid") + connection_qualified_name = "/".join(fields[:3]) + connector_name = fields[1] + return cls( + name=name, + quick_sight_id=quick_sight_id, + quick_sight_dataset_qualified_name=quick_sight_dataset_qualified_name, + qualified_name=f"{quick_sight_dataset_qualified_name}/{quick_sight_id}", + connection_qualified_name=connection_qualified_name, + connector_name=connector_name, + quick_sight_dataset_field_type=quick_sight_dataset_field_type + if quick_sight_dataset_field_type is not None + else UNSET, + quick_sight_dataset=RelatedQuickSightDataset( + unique_attributes={"qualifiedName": quick_sight_dataset_qualified_name} + ), + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "QuickSightDatasetField": + """Create a QuickSightDatasetField instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "QuickSightDatasetField": + """Return only fields required for update operations.""" + return QuickSightDatasetField.updater( + qualified_name=self.qualified_name, name=self.name + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return _quick_sight_dataset_field_to_nested_bytes(self, serde).decode( + "utf-8" + ) + else: + return serde.encode(self).decode("utf-8") + + @staticmethod + def from_json( + json_data: Union[str, bytes], serde: Serde | None = None + ) -> "QuickSightDatasetField": + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + QuickSightDatasetField instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _quick_sight_dataset_field_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class QuickSightDatasetFieldAttributes(AssetAttributes): + """QuickSightDatasetField-specific attributes for nested API format.""" + + quick_sight_dataset_field_type: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="quickSightDatasetFieldType" + ) + """Datatype of this field, for example: STRING, INTEGER, etc.""" + + quick_sight_dataset_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dataset in which this field exists.""" + + quick_sight_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the QuickSight asset.""" + + quick_sight_sheet_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the QuickSight sheet.""" + + quick_sight_sheet_name: Union[str, None, UnsetType] = UNSET + """Name of the QuickSight sheet.""" + + +class QuickSightDatasetFieldRelationshipAttributes(AssetRelationshipAttributes): + """QuickSightDatasetField-specific relationship attributes for nested API format.""" + + quick_sight_dataset: Union[RelatedQuickSightDataset, None, UnsetType] = UNSET + """Dataset in which this field exists.""" + + +class QuickSightDatasetFieldNested(AssetNested): + """QuickSightDatasetField in nested API format for high-performance serialization.""" + + attributes: Union[QuickSightDatasetFieldAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + QuickSightDatasetFieldRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + QuickSightDatasetFieldRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + QuickSightDatasetFieldRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _quick_sight_dataset_field_to_nested( + quick_sight_dataset_field: QuickSightDatasetField, +) -> QuickSightDatasetFieldNested: + """Convert flat QuickSightDatasetField to nested format.""" + attrs_kwargs = build_attributes_kwargs( + quick_sight_dataset_field, QuickSightDatasetFieldAttributes + ) + attrs = QuickSightDatasetFieldAttributes(**attrs_kwargs) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + rel_fields: list[str] = ["quick_sight_dataset"] + replace_rels, append_rels, remove_rels = categorize_relationships( + quick_sight_dataset_field, + rel_fields, + QuickSightDatasetFieldRelationshipAttributes, + ) + return QuickSightDatasetFieldNested( + guid=quick_sight_dataset_field.guid, + type_name=quick_sight_dataset_field.type_name, + status=quick_sight_dataset_field.status, + version=quick_sight_dataset_field.version, + create_time=quick_sight_dataset_field.create_time, + update_time=quick_sight_dataset_field.update_time, + created_by=quick_sight_dataset_field.created_by, + updated_by=quick_sight_dataset_field.updated_by, + classifications=quick_sight_dataset_field.classifications, + classification_names=quick_sight_dataset_field.classification_names, + meanings=quick_sight_dataset_field.meanings, + labels=quick_sight_dataset_field.labels, + business_attributes=quick_sight_dataset_field.business_attributes, + custom_attributes=quick_sight_dataset_field.custom_attributes, + pending_tasks=quick_sight_dataset_field.pending_tasks, + proxy=quick_sight_dataset_field.proxy, + is_incomplete=quick_sight_dataset_field.is_incomplete, + provenance_type=quick_sight_dataset_field.provenance_type, + home_id=quick_sight_dataset_field.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _quick_sight_dataset_field_from_nested( + nested: QuickSightDatasetFieldNested, +) -> QuickSightDatasetField: + """Convert nested format to flat QuickSightDatasetField.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else QuickSightDatasetFieldAttributes() + ) + rel_fields: list[str] = ["quick_sight_dataset"] + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + rel_fields, + QuickSightDatasetFieldRelationshipAttributes, + ) + kwargs = build_flat_kwargs( + nested, attrs, merged_rels, AssetNested, QuickSightDatasetFieldAttributes + ) + return QuickSightDatasetField(**kwargs) + + +def _quick_sight_dataset_field_to_nested_bytes( + quick_sight_dataset_field: QuickSightDatasetField, serde: Serde +) -> bytes: + """Convert flat QuickSightDatasetField to nested JSON bytes.""" + return serde.encode(_quick_sight_dataset_field_to_nested(quick_sight_dataset_field)) + + +def _quick_sight_dataset_field_from_nested_bytes( + data: bytes, serde: Serde +) -> QuickSightDatasetField: + """Convert nested JSON bytes to flat QuickSightDatasetField.""" + nested = serde.decode(data, QuickSightDatasetFieldNested) + return _quick_sight_dataset_field_from_nested(nested) diff --git a/pyatlan_v9/model/assets/quick_sight_folder.py b/pyatlan_v9/model/assets/quick_sight_folder.py new file mode 100644 index 000000000..11e304923 --- /dev/null +++ b/pyatlan_v9/model/assets/quick_sight_folder.py @@ -0,0 +1,313 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +QuickSightFolder asset model with flattened inheritance. + +This module provides: +- QuickSightFolder: Flat asset class (easy to use) +- QuickSightFolderAttributes: Nested attributes struct (extends AssetAttributes) +- QuickSightFolderNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Union + +import msgspec +from msgspec import UNSET, UnsetType + +from pyatlan_v9.model.conversion_utils import ( + build_attributes_kwargs, + build_flat_kwargs, + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .asset import Asset, AssetAttributes, AssetNested, AssetRelationshipAttributes +from .quick_sight_related import ( + RelatedQuickSightAnalysis, + RelatedQuickSightDashboard, + RelatedQuickSightDataset, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class QuickSightFolder(Asset): + """ + Instance of a QuickSight folder in Atlan. + """ + + # Override type_name with QuickSightFolder-specific default + type_name: Union[str, UnsetType] = "QuickSightFolder" + + quick_sight_folder_type: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="quickSightFolderType" + ) + """Type of this folder, for example: SHARED.""" + + quick_sight_folder_hierarchy: Union[list[dict[str, str]], None, UnsetType] = UNSET + """Detailed path of this folder.""" + + quick_sight_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the QuickSight asset.""" + + quick_sight_sheet_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the QuickSight sheet.""" + + quick_sight_sheet_name: Union[str, None, UnsetType] = UNSET + """Name of the QuickSight sheet.""" + + quick_sight_datasets: Union[list[RelatedQuickSightDataset], None, UnsetType] = UNSET + """""" + + quick_sight_analyses: Union[list[RelatedQuickSightAnalysis], None, UnsetType] = ( + UNSET + ) + """""" + + quick_sight_dashboards: Union[list[RelatedQuickSightDashboard], None, UnsetType] = ( + UNSET + ) + """""" + + # ========================================================================= + # Convenience Methods + # ========================================================================= + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + connection_qualified_name: str, + quick_sight_id: str, + quick_sight_folder_type: Union[str, None] = None, + ) -> "QuickSightFolder": + validate_required_fields( + ["name", "connection_qualified_name", "quick_sight_id"], + [name, connection_qualified_name, quick_sight_id], + ) + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + qualified_name = f"{connection_qualified_name}/{quick_sight_id}" + return cls( + name=name, + quick_sight_id=quick_sight_id, + qualified_name=qualified_name, + connection_qualified_name=connection_qualified_name, + connector_name=connector_name, + quick_sight_folder_type=quick_sight_folder_type + if quick_sight_folder_type is not None + else UNSET, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "QuickSightFolder": + """Create a QuickSightFolder instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "QuickSightFolder": + """Return only fields required for update operations.""" + return QuickSightFolder.updater( + qualified_name=self.qualified_name, name=self.name + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return _quick_sight_folder_to_nested_bytes(self, serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + @staticmethod + def from_json( + json_data: Union[str, bytes], serde: Serde | None = None + ) -> "QuickSightFolder": + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + QuickSightFolder instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _quick_sight_folder_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class QuickSightFolderAttributes(AssetAttributes): + """QuickSightFolder-specific attributes for nested API format.""" + + quick_sight_folder_type: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="quickSightFolderType" + ) + """Type of this folder, for example: SHARED.""" + + quick_sight_folder_hierarchy: Union[list[dict[str, str]], None, UnsetType] = UNSET + """Detailed path of this folder.""" + + quick_sight_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the QuickSight asset.""" + + quick_sight_sheet_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the QuickSight sheet.""" + + quick_sight_sheet_name: Union[str, None, UnsetType] = UNSET + """Name of the QuickSight sheet.""" + + +class QuickSightFolderRelationshipAttributes(AssetRelationshipAttributes): + """QuickSightFolder-specific relationship attributes for nested API format.""" + + quick_sight_datasets: Union[list[RelatedQuickSightDataset], None, UnsetType] = UNSET + """""" + + quick_sight_analyses: Union[list[RelatedQuickSightAnalysis], None, UnsetType] = ( + UNSET + ) + """""" + + quick_sight_dashboards: Union[list[RelatedQuickSightDashboard], None, UnsetType] = ( + UNSET + ) + """""" + + +class QuickSightFolderNested(AssetNested): + """QuickSightFolder in nested API format for high-performance serialization.""" + + attributes: Union[QuickSightFolderAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + QuickSightFolderRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + QuickSightFolderRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + QuickSightFolderRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _quick_sight_folder_to_nested( + quick_sight_folder: QuickSightFolder, +) -> QuickSightFolderNested: + """Convert flat QuickSightFolder to nested format.""" + attrs_kwargs = build_attributes_kwargs( + quick_sight_folder, QuickSightFolderAttributes + ) + attrs = QuickSightFolderAttributes(**attrs_kwargs) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + rel_fields: list[str] = [ + "quick_sight_datasets", + "quick_sight_analyses", + "quick_sight_dashboards", + ] + replace_rels, append_rels, remove_rels = categorize_relationships( + quick_sight_folder, rel_fields, QuickSightFolderRelationshipAttributes + ) + return QuickSightFolderNested( + guid=quick_sight_folder.guid, + type_name=quick_sight_folder.type_name, + status=quick_sight_folder.status, + version=quick_sight_folder.version, + create_time=quick_sight_folder.create_time, + update_time=quick_sight_folder.update_time, + created_by=quick_sight_folder.created_by, + updated_by=quick_sight_folder.updated_by, + classifications=quick_sight_folder.classifications, + classification_names=quick_sight_folder.classification_names, + meanings=quick_sight_folder.meanings, + labels=quick_sight_folder.labels, + business_attributes=quick_sight_folder.business_attributes, + custom_attributes=quick_sight_folder.custom_attributes, + pending_tasks=quick_sight_folder.pending_tasks, + proxy=quick_sight_folder.proxy, + is_incomplete=quick_sight_folder.is_incomplete, + provenance_type=quick_sight_folder.provenance_type, + home_id=quick_sight_folder.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _quick_sight_folder_from_nested(nested: QuickSightFolderNested) -> QuickSightFolder: + """Convert nested format to flat QuickSightFolder.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else QuickSightFolderAttributes() + ) + rel_fields: list[str] = [ + "quick_sight_datasets", + "quick_sight_analyses", + "quick_sight_dashboards", + ] + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + rel_fields, + QuickSightFolderRelationshipAttributes, + ) + kwargs = build_flat_kwargs( + nested, attrs, merged_rels, AssetNested, QuickSightFolderAttributes + ) + return QuickSightFolder(**kwargs) + + +def _quick_sight_folder_to_nested_bytes( + quick_sight_folder: QuickSightFolder, serde: Serde +) -> bytes: + """Convert flat QuickSightFolder to nested JSON bytes.""" + return serde.encode(_quick_sight_folder_to_nested(quick_sight_folder)) + + +def _quick_sight_folder_from_nested_bytes( + data: bytes, serde: Serde +) -> QuickSightFolder: + """Convert nested JSON bytes to flat QuickSightFolder.""" + nested = serde.decode(data, QuickSightFolderNested) + return _quick_sight_folder_from_nested(nested) diff --git a/pyatlan_v9/model/assets/quick_sight_related.py b/pyatlan_v9/model/assets/quick_sight_related.py new file mode 100644 index 000000000..eee224863 --- /dev/null +++ b/pyatlan_v9/model/assets/quick_sight_related.py @@ -0,0 +1,203 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for QuickSight module. + +This module contains all Related{Type} classes for the QuickSight type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedBI +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedQuickSight", + "RelatedQuickSightDashboardVisual", + "RelatedQuickSightDataset", + "RelatedQuickSightDatasetField", + "RelatedQuickSightFolder", + "RelatedQuickSightAnalysis", + "RelatedQuickSightAnalysisVisual", + "RelatedQuickSightDashboard", +] + + +class RelatedQuickSight(RelatedBI): + """ + Related entity reference for QuickSight assets. + + Extends RelatedBI with QuickSight-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "QuickSight" so it serializes correctly + + quick_sight_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the QuickSight asset.""" + + quick_sight_sheet_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for the QuickSight sheet.""" + + quick_sight_sheet_name: Union[str, None, UnsetType] = UNSET + """Name of the QuickSight sheet.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "QuickSight" + + +class RelatedQuickSightDashboardVisual(RelatedQuickSight): + """ + Related entity reference for QuickSightDashboardVisual assets. + + Extends RelatedQuickSight with QuickSightDashboardVisual-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "QuickSightDashboardVisual" so it serializes correctly + + quick_sight_dashboard_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dashboard in which this visual exists.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "QuickSightDashboardVisual" + + +class RelatedQuickSightDataset(RelatedQuickSight): + """ + Related entity reference for QuickSightDataset assets. + + Extends RelatedQuickSight with QuickSightDataset-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "QuickSightDataset" so it serializes correctly + + quick_sight_import_mode: Union[str, None, UnsetType] = UNSET + """Import mode for this dataset, for example: SPICE or DIRECT_QUERY.""" + + quick_sight_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns present in this dataset.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "QuickSightDataset" + + +class RelatedQuickSightDatasetField(RelatedQuickSight): + """ + Related entity reference for QuickSightDatasetField assets. + + Extends RelatedQuickSight with QuickSightDatasetField-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "QuickSightDatasetField" so it serializes correctly + + quick_sight_type: Union[str, None, UnsetType] = UNSET + """Datatype of this field, for example: STRING, INTEGER, etc.""" + + quick_sight_dataset_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dataset in which this field exists.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "QuickSightDatasetField" + + +class RelatedQuickSightFolder(RelatedQuickSight): + """ + Related entity reference for QuickSightFolder assets. + + Extends RelatedQuickSight with QuickSightFolder-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "QuickSightFolder" so it serializes correctly + + quick_sight_type: Union[str, None, UnsetType] = UNSET + """Type of this folder, for example: SHARED.""" + + quick_sight_folder_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Detailed path of this folder.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "QuickSightFolder" + + +class RelatedQuickSightAnalysis(RelatedQuickSight): + """ + Related entity reference for QuickSightAnalysis assets. + + Extends RelatedQuickSight with QuickSightAnalysis-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "QuickSightAnalysis" so it serializes correctly + + quick_sight_status: Union[str, None, UnsetType] = UNSET + """Status of this analysis, for example: CREATION_IN_PROGRESS, UPDATE_SUCCESSFUL, etc.""" + + quick_sight_analysis_calculated_fields: Union[List[str], None, UnsetType] = UNSET + """List of field names calculated by this analysis.""" + + quick_sight_analysis_parameter_declarations: Union[List[str], None, UnsetType] = ( + UNSET + ) + """List of parameters used for this analysis.""" + + quick_sight_analysis_filter_groups: Union[List[str], None, UnsetType] = UNSET + """List of filter groups used for this analysis.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "QuickSightAnalysis" + + +class RelatedQuickSightAnalysisVisual(RelatedQuickSight): + """ + Related entity reference for QuickSightAnalysisVisual assets. + + Extends RelatedQuickSight with QuickSightAnalysisVisual-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "QuickSightAnalysisVisual" so it serializes correctly + + quick_sight_analysis_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the QuickSight analysis in which this visual exists.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "QuickSightAnalysisVisual" + + +class RelatedQuickSightDashboard(RelatedQuickSight): + """ + Related entity reference for QuickSightDashboard assets. + + Extends RelatedQuickSight with QuickSightDashboard-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "QuickSightDashboard" so it serializes correctly + + quick_sight_published_version_number: Union[int, None, UnsetType] = UNSET + """Version number of the published dashboard.""" + + quick_sight_last_published_time: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this dashboard was last published, in milliseconds.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "QuickSightDashboard" diff --git a/pyatlan_v9/model/assets/readme.py b/pyatlan_v9/model/assets/readme.py new file mode 100644 index 000000000..55daba218 --- /dev/null +++ b/pyatlan_v9/model/assets/readme.py @@ -0,0 +1,663 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Readme asset model with flattened inheritance. + +This module provides: +- Readme: Flat asset class (easy to use) +- ReadmeAttributes: Nested attributes struct (extends AssetAttributes) +- ReadmeNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union +from urllib.parse import quote, unquote + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .asset_related import RelatedAsset +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .resource_related import RelatedFile, RelatedLink, RelatedReadme + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Readme(Asset): + """ + Instance of a README in Atlan. + """ + + LINK: ClassVar[Any] = None + IS_GLOBAL: ClassVar[Any] = None + REFERENCE: ClassVar[Any] = None + RESOURCE_METADATA: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + ASSET: ClassVar[Any] = None + SEE_ALSO: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Readme" + + link: Union[str, None, UnsetType] = UNSET + """URL to the resource.""" + + is_global: Union[bool, None, UnsetType] = UNSET + """Whether the resource is global (true) or not (false).""" + + reference: Union[str, None, UnsetType] = UNSET + """Reference to the resource.""" + + resource_metadata: Union[Dict[str, str], None, UnsetType] = UNSET + """Metadata of the resource.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + asset: Union[RelatedAsset, None, UnsetType] = UNSET + """Asset that this README describes.""" + + see_also: Union[List[RelatedReadme], None, UnsetType] = UNSET + """""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Readme" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + @property + def description(self) -> Union[str, None, UnsetType]: + """Decode URL-encoded description content for parity with legacy models.""" + if self.user_description is not UNSET: + return ( + unquote(self.user_description) + if self.user_description is not None + else None + ) + if self.asset_source_readme is not UNSET: + return ( + unquote(self.asset_source_readme) + if self.asset_source_readme is not None + else None + ) + return UNSET + + @description.setter + def description(self, description: Union[str, None, UnsetType]) -> None: + """Store README content in user_description with URL encoding.""" + if description is UNSET: + self.user_description = UNSET + return + self.user_description = quote(description) if description is not None else None + + @classmethod + @init_guid + def creator( + cls, + *, + asset: Asset, + content: str, + asset_name: Union[str, None] = None, + ) -> "Readme": + """Create a new Readme asset.""" + validate_required_fields(["asset", "content"], [asset, content]) + actual_asset_name = asset.name if asset.name is not UNSET else None + if actual_asset_name: + if asset_name: + raise ValueError( + "asset_name can not be given when name is available from asset" + ) + asset_name = actual_asset_name + elif not asset_name: + raise ValueError( + "asset_name is required when name is not available from asset" + ) + if asset.guid is UNSET or not asset.guid: + raise ValueError( + "asset guid must be present, use the client.asset.ref_by_guid() method to retrieve an asset by its GUID" + ) + return cls( + qualified_name=f"{asset.guid}/readme", + name=f"{asset_name} Readme", + asset=RelatedAsset(guid=asset.guid), + user_description=quote(content), + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "Readme": + """Create a Readme instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "Readme": + """Return only fields required for update operations.""" + return Readme.updater(qualified_name=self.qualified_name, name=self.name) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _readme_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Readme: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Readme instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _readme_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class ReadmeAttributes(AssetAttributes): + """Readme-specific attributes for nested API format.""" + + link: Union[str, None, UnsetType] = UNSET + """URL to the resource.""" + + is_global: Union[bool, None, UnsetType] = UNSET + """Whether the resource is global (true) or not (false).""" + + reference: Union[str, None, UnsetType] = UNSET + """Reference to the resource.""" + + resource_metadata: Union[Dict[str, str], None, UnsetType] = UNSET + """Metadata of the resource.""" + + +class ReadmeRelationshipAttributes(AssetRelationshipAttributes): + """Readme-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + asset: Union[RelatedAsset, None, UnsetType] = UNSET + """Asset that this README describes.""" + + see_also: Union[List[RelatedReadme], None, UnsetType] = UNSET + """""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class ReadmeNested(AssetNested): + """Readme in nested API format for high-performance serialization.""" + + attributes: Union[ReadmeAttributes, UnsetType] = UNSET + relationship_attributes: Union[ReadmeRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ReadmeRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[ReadmeRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_README_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "asset", + "see_also", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_readme_attrs(attrs: ReadmeAttributes, obj: Readme) -> None: + """Populate Readme-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.link = obj.link + attrs.is_global = obj.is_global + attrs.reference = obj.reference + attrs.resource_metadata = obj.resource_metadata + + +def _extract_readme_attrs(attrs: ReadmeAttributes) -> dict: + """Extract all Readme attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["link"] = attrs.link + result["is_global"] = attrs.is_global + result["reference"] = attrs.reference + result["resource_metadata"] = attrs.resource_metadata + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _readme_to_nested(readme: Readme) -> ReadmeNested: + """Convert flat Readme to nested format.""" + attrs = ReadmeAttributes() + _populate_readme_attrs(attrs, readme) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + readme, _README_REL_FIELDS, ReadmeRelationshipAttributes + ) + return ReadmeNested( + guid=readme.guid, + type_name=readme.type_name, + status=readme.status, + version=readme.version, + create_time=readme.create_time, + update_time=readme.update_time, + created_by=readme.created_by, + updated_by=readme.updated_by, + classifications=readme.classifications, + classification_names=readme.classification_names, + meanings=readme.meanings, + labels=readme.labels, + business_attributes=readme.business_attributes, + custom_attributes=readme.custom_attributes, + pending_tasks=readme.pending_tasks, + proxy=readme.proxy, + is_incomplete=readme.is_incomplete, + provenance_type=readme.provenance_type, + home_id=readme.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _readme_from_nested(nested: ReadmeNested) -> Readme: + """Convert nested format to flat Readme.""" + attrs = nested.attributes if nested.attributes is not UNSET else ReadmeAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _README_REL_FIELDS, + ReadmeRelationshipAttributes, + ) + return Readme( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_readme_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _readme_to_nested_bytes(readme: Readme, serde: Serde) -> bytes: + """Convert flat Readme to nested JSON bytes.""" + return serde.encode(_readme_to_nested(readme)) + + +def _readme_from_nested_bytes(data: bytes, serde: Serde) -> Readme: + """Convert nested JSON bytes to flat Readme.""" + nested = serde.decode(data, ReadmeNested) + return _readme_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + RelationField, +) + +Readme.LINK = KeywordField("link", "link") +Readme.IS_GLOBAL = BooleanField("isGlobal", "isGlobal") +Readme.REFERENCE = KeywordField("reference", "reference") +Readme.RESOURCE_METADATA = KeywordField("resourceMetadata", "resourceMetadata") +Readme.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Readme.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Readme.ANOMALO_CHECKS = RelationField("anomaloChecks") +Readme.APPLICATION = RelationField("application") +Readme.APPLICATION_FIELD = RelationField("applicationField") +Readme.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Readme.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Readme.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Readme.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Readme.METRICS = RelationField("metrics") +Readme.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Readme.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Readme.MEANINGS = RelationField("meanings") +Readme.MC_MONITORS = RelationField("mcMonitors") +Readme.MC_INCIDENTS = RelationField("mcIncidents") +Readme.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Readme.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Readme.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Readme.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Readme.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Readme.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Readme.FILES = RelationField("files") +Readme.LINKS = RelationField("links") +Readme.README = RelationField("readme") +Readme.ASSET = RelationField("asset") +Readme.SEE_ALSO = RelationField("seeAlso") +Readme.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Readme.SODA_CHECKS = RelationField("sodaChecks") +Readme.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Readme.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/readme_template.py b/pyatlan_v9/model/assets/readme_template.py new file mode 100644 index 000000000..5d86f0e32 --- /dev/null +++ b/pyatlan_v9/model/assets/readme_template.py @@ -0,0 +1,601 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +ReadmeTemplate asset model with flattened inheritance. + +This module provides: +- ReadmeTemplate: Flat asset class (easy to use) +- ReadmeTemplateAttributes: Nested attributes struct (extends AssetAttributes) +- ReadmeTemplateNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .resource_related import RelatedFile, RelatedLink, RelatedReadme + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class ReadmeTemplate(Asset): + """ + Instance of a README template in Atlan. + """ + + ICON: ClassVar[Any] = None + ICON_TYPE: ClassVar[Any] = None + LINK: ClassVar[Any] = None + IS_GLOBAL: ClassVar[Any] = None + REFERENCE: ClassVar[Any] = None + RESOURCE_METADATA: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "ReadmeTemplate" + + icon: Union[str, None, UnsetType] = UNSET + """Icon to use for the README template.""" + + icon_type: Union[str, None, UnsetType] = UNSET + """Type of icon, for example: image or emoji.""" + + link: Union[str, None, UnsetType] = UNSET + """URL to the resource.""" + + is_global: Union[bool, None, UnsetType] = UNSET + """Whether the resource is global (true) or not (false).""" + + reference: Union[str, None, UnsetType] = UNSET + """Reference to the resource.""" + + resource_metadata: Union[Dict[str, str], None, UnsetType] = UNSET + """Metadata of the resource.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "ReadmeTemplate" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _readme_template_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> ReadmeTemplate: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + ReadmeTemplate instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _readme_template_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class ReadmeTemplateAttributes(AssetAttributes): + """ReadmeTemplate-specific attributes for nested API format.""" + + icon: Union[str, None, UnsetType] = UNSET + """Icon to use for the README template.""" + + icon_type: Union[str, None, UnsetType] = UNSET + """Type of icon, for example: image or emoji.""" + + link: Union[str, None, UnsetType] = UNSET + """URL to the resource.""" + + is_global: Union[bool, None, UnsetType] = UNSET + """Whether the resource is global (true) or not (false).""" + + reference: Union[str, None, UnsetType] = UNSET + """Reference to the resource.""" + + resource_metadata: Union[Dict[str, str], None, UnsetType] = UNSET + """Metadata of the resource.""" + + +class ReadmeTemplateRelationshipAttributes(AssetRelationshipAttributes): + """ReadmeTemplate-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class ReadmeTemplateNested(AssetNested): + """ReadmeTemplate in nested API format for high-performance serialization.""" + + attributes: Union[ReadmeTemplateAttributes, UnsetType] = UNSET + relationship_attributes: Union[ReadmeTemplateRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + ReadmeTemplateRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + ReadmeTemplateRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_README_TEMPLATE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_readme_template_attrs( + attrs: ReadmeTemplateAttributes, obj: ReadmeTemplate +) -> None: + """Populate ReadmeTemplate-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.icon = obj.icon + attrs.icon_type = obj.icon_type + attrs.link = obj.link + attrs.is_global = obj.is_global + attrs.reference = obj.reference + attrs.resource_metadata = obj.resource_metadata + + +def _extract_readme_template_attrs(attrs: ReadmeTemplateAttributes) -> dict: + """Extract all ReadmeTemplate attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["icon"] = attrs.icon + result["icon_type"] = attrs.icon_type + result["link"] = attrs.link + result["is_global"] = attrs.is_global + result["reference"] = attrs.reference + result["resource_metadata"] = attrs.resource_metadata + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _readme_template_to_nested(readme_template: ReadmeTemplate) -> ReadmeTemplateNested: + """Convert flat ReadmeTemplate to nested format.""" + attrs = ReadmeTemplateAttributes() + _populate_readme_template_attrs(attrs, readme_template) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + readme_template, + _README_TEMPLATE_REL_FIELDS, + ReadmeTemplateRelationshipAttributes, + ) + return ReadmeTemplateNested( + guid=readme_template.guid, + type_name=readme_template.type_name, + status=readme_template.status, + version=readme_template.version, + create_time=readme_template.create_time, + update_time=readme_template.update_time, + created_by=readme_template.created_by, + updated_by=readme_template.updated_by, + classifications=readme_template.classifications, + classification_names=readme_template.classification_names, + meanings=readme_template.meanings, + labels=readme_template.labels, + business_attributes=readme_template.business_attributes, + custom_attributes=readme_template.custom_attributes, + pending_tasks=readme_template.pending_tasks, + proxy=readme_template.proxy, + is_incomplete=readme_template.is_incomplete, + provenance_type=readme_template.provenance_type, + home_id=readme_template.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _readme_template_from_nested(nested: ReadmeTemplateNested) -> ReadmeTemplate: + """Convert nested format to flat ReadmeTemplate.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else ReadmeTemplateAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _README_TEMPLATE_REL_FIELDS, + ReadmeTemplateRelationshipAttributes, + ) + return ReadmeTemplate( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_readme_template_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _readme_template_to_nested_bytes( + readme_template: ReadmeTemplate, serde: Serde +) -> bytes: + """Convert flat ReadmeTemplate to nested JSON bytes.""" + return serde.encode(_readme_template_to_nested(readme_template)) + + +def _readme_template_from_nested_bytes(data: bytes, serde: Serde) -> ReadmeTemplate: + """Convert nested JSON bytes to flat ReadmeTemplate.""" + nested = serde.decode(data, ReadmeTemplateNested) + return _readme_template_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + RelationField, +) + +ReadmeTemplate.ICON = KeywordField("icon", "icon") +ReadmeTemplate.ICON_TYPE = KeywordField("iconType", "iconType") +ReadmeTemplate.LINK = KeywordField("link", "link") +ReadmeTemplate.IS_GLOBAL = BooleanField("isGlobal", "isGlobal") +ReadmeTemplate.REFERENCE = KeywordField("reference", "reference") +ReadmeTemplate.RESOURCE_METADATA = KeywordField("resourceMetadata", "resourceMetadata") +ReadmeTemplate.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +ReadmeTemplate.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +ReadmeTemplate.ANOMALO_CHECKS = RelationField("anomaloChecks") +ReadmeTemplate.APPLICATION = RelationField("application") +ReadmeTemplate.APPLICATION_FIELD = RelationField("applicationField") +ReadmeTemplate.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +ReadmeTemplate.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +ReadmeTemplate.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +ReadmeTemplate.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +ReadmeTemplate.METRICS = RelationField("metrics") +ReadmeTemplate.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +ReadmeTemplate.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +ReadmeTemplate.MEANINGS = RelationField("meanings") +ReadmeTemplate.MC_MONITORS = RelationField("mcMonitors") +ReadmeTemplate.MC_INCIDENTS = RelationField("mcIncidents") +ReadmeTemplate.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +ReadmeTemplate.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +ReadmeTemplate.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +ReadmeTemplate.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +ReadmeTemplate.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +ReadmeTemplate.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +ReadmeTemplate.FILES = RelationField("files") +ReadmeTemplate.LINKS = RelationField("links") +ReadmeTemplate.README = RelationField("readme") +ReadmeTemplate.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +ReadmeTemplate.SODA_CHECKS = RelationField("sodaChecks") +ReadmeTemplate.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +ReadmeTemplate.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/redash.py b/pyatlan_v9/model/assets/redash.py new file mode 100644 index 000000000..64abce72d --- /dev/null +++ b/pyatlan_v9/model/assets/redash.py @@ -0,0 +1,535 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Redash asset model with flattened inheritance. + +This module provides: +- Redash: Flat asset class (easy to use) +- RedashAttributes: Nested attributes struct (extends AssetAttributes) +- RedashNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Redash(Asset): + """ + Base class for Redash assets. + """ + + REDASH_IS_PUBLISHED: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Redash" + + redash_is_published: Union[bool, None, UnsetType] = UNSET + """Whether this asset is published in Redash (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Redash" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _redash_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Redash: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Redash instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _redash_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class RedashAttributes(AssetAttributes): + """Redash-specific attributes for nested API format.""" + + redash_is_published: Union[bool, None, UnsetType] = UNSET + """Whether this asset is published in Redash (true) or not (false).""" + + +class RedashRelationshipAttributes(AssetRelationshipAttributes): + """Redash-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class RedashNested(AssetNested): + """Redash in nested API format for high-performance serialization.""" + + attributes: Union[RedashAttributes, UnsetType] = UNSET + relationship_attributes: Union[RedashRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[RedashRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[RedashRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_REDASH_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_redash_attrs(attrs: RedashAttributes, obj: Redash) -> None: + """Populate Redash-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.redash_is_published = obj.redash_is_published + + +def _extract_redash_attrs(attrs: RedashAttributes) -> dict: + """Extract all Redash attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["redash_is_published"] = attrs.redash_is_published + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _redash_to_nested(redash: Redash) -> RedashNested: + """Convert flat Redash to nested format.""" + attrs = RedashAttributes() + _populate_redash_attrs(attrs, redash) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + redash, _REDASH_REL_FIELDS, RedashRelationshipAttributes + ) + return RedashNested( + guid=redash.guid, + type_name=redash.type_name, + status=redash.status, + version=redash.version, + create_time=redash.create_time, + update_time=redash.update_time, + created_by=redash.created_by, + updated_by=redash.updated_by, + classifications=redash.classifications, + classification_names=redash.classification_names, + meanings=redash.meanings, + labels=redash.labels, + business_attributes=redash.business_attributes, + custom_attributes=redash.custom_attributes, + pending_tasks=redash.pending_tasks, + proxy=redash.proxy, + is_incomplete=redash.is_incomplete, + provenance_type=redash.provenance_type, + home_id=redash.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _redash_from_nested(nested: RedashNested) -> Redash: + """Convert nested format to flat Redash.""" + attrs = nested.attributes if nested.attributes is not UNSET else RedashAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _REDASH_REL_FIELDS, + RedashRelationshipAttributes, + ) + return Redash( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_redash_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _redash_to_nested_bytes(redash: Redash, serde: Serde) -> bytes: + """Convert flat Redash to nested JSON bytes.""" + return serde.encode(_redash_to_nested(redash)) + + +def _redash_from_nested_bytes(data: bytes, serde: Serde) -> Redash: + """Convert nested JSON bytes to flat Redash.""" + nested = serde.decode(data, RedashNested) + return _redash_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + RelationField, +) + +Redash.REDASH_IS_PUBLISHED = BooleanField("redashIsPublished", "redashIsPublished") +Redash.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Redash.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Redash.ANOMALO_CHECKS = RelationField("anomaloChecks") +Redash.APPLICATION = RelationField("application") +Redash.APPLICATION_FIELD = RelationField("applicationField") +Redash.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Redash.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Redash.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Redash.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Redash.METRICS = RelationField("metrics") +Redash.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Redash.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Redash.MEANINGS = RelationField("meanings") +Redash.MC_MONITORS = RelationField("mcMonitors") +Redash.MC_INCIDENTS = RelationField("mcIncidents") +Redash.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Redash.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Redash.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Redash.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Redash.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Redash.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Redash.FILES = RelationField("files") +Redash.LINKS = RelationField("links") +Redash.README = RelationField("readme") +Redash.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Redash.SODA_CHECKS = RelationField("sodaChecks") +Redash.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Redash.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/redash_dashboard.py b/pyatlan_v9/model/assets/redash_dashboard.py new file mode 100644 index 000000000..a5050bf3d --- /dev/null +++ b/pyatlan_v9/model/assets/redash_dashboard.py @@ -0,0 +1,568 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +RedashDashboard asset model with flattened inheritance. + +This module provides: +- RedashDashboard: Flat asset class (easy to use) +- RedashDashboardAttributes: Nested attributes struct (extends AssetAttributes) +- RedashDashboardNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class RedashDashboard(Asset): + """ + Instance of a Redash dashboard in Atlan. These are collections of widgets. + """ + + REDASH_DASHBOARD_WIDGET_COUNT: ClassVar[Any] = None + REDASH_IS_PUBLISHED: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "RedashDashboard" + + redash_dashboard_widget_count: Union[int, None, UnsetType] = UNSET + """Number of widgets in this dashboard.""" + + redash_is_published: Union[bool, None, UnsetType] = UNSET + """Whether this asset is published in Redash (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "RedashDashboard" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _redash_dashboard_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> RedashDashboard: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + RedashDashboard instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _redash_dashboard_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class RedashDashboardAttributes(AssetAttributes): + """RedashDashboard-specific attributes for nested API format.""" + + redash_dashboard_widget_count: Union[int, None, UnsetType] = UNSET + """Number of widgets in this dashboard.""" + + redash_is_published: Union[bool, None, UnsetType] = UNSET + """Whether this asset is published in Redash (true) or not (false).""" + + +class RedashDashboardRelationshipAttributes(AssetRelationshipAttributes): + """RedashDashboard-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class RedashDashboardNested(AssetNested): + """RedashDashboard in nested API format for high-performance serialization.""" + + attributes: Union[RedashDashboardAttributes, UnsetType] = UNSET + relationship_attributes: Union[RedashDashboardRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + RedashDashboardRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + RedashDashboardRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_REDASH_DASHBOARD_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_redash_dashboard_attrs( + attrs: RedashDashboardAttributes, obj: RedashDashboard +) -> None: + """Populate RedashDashboard-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.redash_dashboard_widget_count = obj.redash_dashboard_widget_count + attrs.redash_is_published = obj.redash_is_published + + +def _extract_redash_dashboard_attrs(attrs: RedashDashboardAttributes) -> dict: + """Extract all RedashDashboard attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["redash_dashboard_widget_count"] = attrs.redash_dashboard_widget_count + result["redash_is_published"] = attrs.redash_is_published + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _redash_dashboard_to_nested( + redash_dashboard: RedashDashboard, +) -> RedashDashboardNested: + """Convert flat RedashDashboard to nested format.""" + attrs = RedashDashboardAttributes() + _populate_redash_dashboard_attrs(attrs, redash_dashboard) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + redash_dashboard, + _REDASH_DASHBOARD_REL_FIELDS, + RedashDashboardRelationshipAttributes, + ) + return RedashDashboardNested( + guid=redash_dashboard.guid, + type_name=redash_dashboard.type_name, + status=redash_dashboard.status, + version=redash_dashboard.version, + create_time=redash_dashboard.create_time, + update_time=redash_dashboard.update_time, + created_by=redash_dashboard.created_by, + updated_by=redash_dashboard.updated_by, + classifications=redash_dashboard.classifications, + classification_names=redash_dashboard.classification_names, + meanings=redash_dashboard.meanings, + labels=redash_dashboard.labels, + business_attributes=redash_dashboard.business_attributes, + custom_attributes=redash_dashboard.custom_attributes, + pending_tasks=redash_dashboard.pending_tasks, + proxy=redash_dashboard.proxy, + is_incomplete=redash_dashboard.is_incomplete, + provenance_type=redash_dashboard.provenance_type, + home_id=redash_dashboard.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _redash_dashboard_from_nested(nested: RedashDashboardNested) -> RedashDashboard: + """Convert nested format to flat RedashDashboard.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else RedashDashboardAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _REDASH_DASHBOARD_REL_FIELDS, + RedashDashboardRelationshipAttributes, + ) + return RedashDashboard( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_redash_dashboard_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _redash_dashboard_to_nested_bytes( + redash_dashboard: RedashDashboard, serde: Serde +) -> bytes: + """Convert flat RedashDashboard to nested JSON bytes.""" + return serde.encode(_redash_dashboard_to_nested(redash_dashboard)) + + +def _redash_dashboard_from_nested_bytes(data: bytes, serde: Serde) -> RedashDashboard: + """Convert nested JSON bytes to flat RedashDashboard.""" + nested = serde.decode(data, RedashDashboardNested) + return _redash_dashboard_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + NumericField, + RelationField, +) + +RedashDashboard.REDASH_DASHBOARD_WIDGET_COUNT = NumericField( + "redashDashboardWidgetCount", "redashDashboardWidgetCount" +) +RedashDashboard.REDASH_IS_PUBLISHED = BooleanField( + "redashIsPublished", "redashIsPublished" +) +RedashDashboard.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +RedashDashboard.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +RedashDashboard.ANOMALO_CHECKS = RelationField("anomaloChecks") +RedashDashboard.APPLICATION = RelationField("application") +RedashDashboard.APPLICATION_FIELD = RelationField("applicationField") +RedashDashboard.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +RedashDashboard.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +RedashDashboard.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +RedashDashboard.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +RedashDashboard.METRICS = RelationField("metrics") +RedashDashboard.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +RedashDashboard.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +RedashDashboard.MEANINGS = RelationField("meanings") +RedashDashboard.MC_MONITORS = RelationField("mcMonitors") +RedashDashboard.MC_INCIDENTS = RelationField("mcIncidents") +RedashDashboard.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +RedashDashboard.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +RedashDashboard.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +RedashDashboard.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +RedashDashboard.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +RedashDashboard.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +RedashDashboard.FILES = RelationField("files") +RedashDashboard.LINKS = RelationField("links") +RedashDashboard.README = RelationField("readme") +RedashDashboard.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +RedashDashboard.SODA_CHECKS = RelationField("sodaChecks") +RedashDashboard.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +RedashDashboard.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/redash_query.py b/pyatlan_v9/model/assets/redash_query.py new file mode 100644 index 000000000..e85525d22 --- /dev/null +++ b/pyatlan_v9/model/assets/redash_query.py @@ -0,0 +1,636 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +RedashQuery asset model with flattened inheritance. + +This module provides: +- RedashQuery: Flat asset class (easy to use) +- RedashQueryAttributes: Nested attributes struct (extends AssetAttributes) +- RedashQueryNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .redash_related import RelatedRedashVisualization + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class RedashQuery(Asset): + """ + Instance of a Redash query in Atlan. + """ + + REDASH_QUERY_SQL: ClassVar[Any] = None + REDASH_QUERY_PARAMETERS: ClassVar[Any] = None + REDASH_QUERY_SCHEDULE: ClassVar[Any] = None + REDASH_QUERY_LAST_EXECUTION_RUNTIME: ClassVar[Any] = None + REDASH_QUERY_LAST_EXECUTED_AT: ClassVar[Any] = None + REDASH_QUERY_SCHEDULE_HUMANIZED: ClassVar[Any] = None + REDASH_IS_PUBLISHED: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + REDASH_VISUALIZATIONS: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "RedashQuery" + + redash_query_sql: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="redashQuerySQL" + ) + """SQL code of this query.""" + + redash_query_parameters: Union[str, None, UnsetType] = UNSET + """Parameters of this query.""" + + redash_query_schedule: Union[Dict[str, str], None, UnsetType] = UNSET + """Schedule for this query.""" + + redash_query_last_execution_runtime: Union[float, None, UnsetType] = UNSET + """Elapsed time of the last execution of this query.""" + + redash_query_last_executed_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) when this query was last executed, in milliseconds.""" + + redash_query_schedule_humanized: Union[str, None, UnsetType] = UNSET + """Schdule for this query in readable text for overview tab and filtering.""" + + redash_is_published: Union[bool, None, UnsetType] = UNSET + """Whether this asset is published in Redash (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + redash_visualizations: Union[List[RelatedRedashVisualization], None, UnsetType] = ( + UNSET + ) + """Visualizations that were created by this query.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "RedashQuery" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _redash_query_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> RedashQuery: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + RedashQuery instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _redash_query_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class RedashQueryAttributes(AssetAttributes): + """RedashQuery-specific attributes for nested API format.""" + + redash_query_sql: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="redashQuerySQL" + ) + """SQL code of this query.""" + + redash_query_parameters: Union[str, None, UnsetType] = UNSET + """Parameters of this query.""" + + redash_query_schedule: Union[Dict[str, str], None, UnsetType] = UNSET + """Schedule for this query.""" + + redash_query_last_execution_runtime: Union[float, None, UnsetType] = UNSET + """Elapsed time of the last execution of this query.""" + + redash_query_last_executed_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) when this query was last executed, in milliseconds.""" + + redash_query_schedule_humanized: Union[str, None, UnsetType] = UNSET + """Schdule for this query in readable text for overview tab and filtering.""" + + redash_is_published: Union[bool, None, UnsetType] = UNSET + """Whether this asset is published in Redash (true) or not (false).""" + + +class RedashQueryRelationshipAttributes(AssetRelationshipAttributes): + """RedashQuery-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + redash_visualizations: Union[List[RelatedRedashVisualization], None, UnsetType] = ( + UNSET + ) + """Visualizations that were created by this query.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class RedashQueryNested(AssetNested): + """RedashQuery in nested API format for high-performance serialization.""" + + attributes: Union[RedashQueryAttributes, UnsetType] = UNSET + relationship_attributes: Union[RedashQueryRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + RedashQueryRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + RedashQueryRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_REDASH_QUERY_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "redash_visualizations", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_redash_query_attrs( + attrs: RedashQueryAttributes, obj: RedashQuery +) -> None: + """Populate RedashQuery-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.redash_query_sql = obj.redash_query_sql + attrs.redash_query_parameters = obj.redash_query_parameters + attrs.redash_query_schedule = obj.redash_query_schedule + attrs.redash_query_last_execution_runtime = obj.redash_query_last_execution_runtime + attrs.redash_query_last_executed_at = obj.redash_query_last_executed_at + attrs.redash_query_schedule_humanized = obj.redash_query_schedule_humanized + attrs.redash_is_published = obj.redash_is_published + + +def _extract_redash_query_attrs(attrs: RedashQueryAttributes) -> dict: + """Extract all RedashQuery attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["redash_query_sql"] = attrs.redash_query_sql + result["redash_query_parameters"] = attrs.redash_query_parameters + result["redash_query_schedule"] = attrs.redash_query_schedule + result["redash_query_last_execution_runtime"] = ( + attrs.redash_query_last_execution_runtime + ) + result["redash_query_last_executed_at"] = attrs.redash_query_last_executed_at + result["redash_query_schedule_humanized"] = attrs.redash_query_schedule_humanized + result["redash_is_published"] = attrs.redash_is_published + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _redash_query_to_nested(redash_query: RedashQuery) -> RedashQueryNested: + """Convert flat RedashQuery to nested format.""" + attrs = RedashQueryAttributes() + _populate_redash_query_attrs(attrs, redash_query) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + redash_query, _REDASH_QUERY_REL_FIELDS, RedashQueryRelationshipAttributes + ) + return RedashQueryNested( + guid=redash_query.guid, + type_name=redash_query.type_name, + status=redash_query.status, + version=redash_query.version, + create_time=redash_query.create_time, + update_time=redash_query.update_time, + created_by=redash_query.created_by, + updated_by=redash_query.updated_by, + classifications=redash_query.classifications, + classification_names=redash_query.classification_names, + meanings=redash_query.meanings, + labels=redash_query.labels, + business_attributes=redash_query.business_attributes, + custom_attributes=redash_query.custom_attributes, + pending_tasks=redash_query.pending_tasks, + proxy=redash_query.proxy, + is_incomplete=redash_query.is_incomplete, + provenance_type=redash_query.provenance_type, + home_id=redash_query.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _redash_query_from_nested(nested: RedashQueryNested) -> RedashQuery: + """Convert nested format to flat RedashQuery.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else RedashQueryAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _REDASH_QUERY_REL_FIELDS, + RedashQueryRelationshipAttributes, + ) + return RedashQuery( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_redash_query_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _redash_query_to_nested_bytes(redash_query: RedashQuery, serde: Serde) -> bytes: + """Convert flat RedashQuery to nested JSON bytes.""" + return serde.encode(_redash_query_to_nested(redash_query)) + + +def _redash_query_from_nested_bytes(data: bytes, serde: Serde) -> RedashQuery: + """Convert nested JSON bytes to flat RedashQuery.""" + nested = serde.decode(data, RedashQueryNested) + return _redash_query_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +RedashQuery.REDASH_QUERY_SQL = KeywordField("redashQuerySQL", "redashQuerySQL") +RedashQuery.REDASH_QUERY_PARAMETERS = KeywordField( + "redashQueryParameters", "redashQueryParameters" +) +RedashQuery.REDASH_QUERY_SCHEDULE = KeywordField( + "redashQuerySchedule", "redashQuerySchedule" +) +RedashQuery.REDASH_QUERY_LAST_EXECUTION_RUNTIME = NumericField( + "redashQueryLastExecutionRuntime", "redashQueryLastExecutionRuntime" +) +RedashQuery.REDASH_QUERY_LAST_EXECUTED_AT = NumericField( + "redashQueryLastExecutedAt", "redashQueryLastExecutedAt" +) +RedashQuery.REDASH_QUERY_SCHEDULE_HUMANIZED = KeywordTextField( + "redashQueryScheduleHumanized", + "redashQueryScheduleHumanized", + "redashQueryScheduleHumanized.text", +) +RedashQuery.REDASH_IS_PUBLISHED = BooleanField("redashIsPublished", "redashIsPublished") +RedashQuery.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +RedashQuery.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +RedashQuery.ANOMALO_CHECKS = RelationField("anomaloChecks") +RedashQuery.APPLICATION = RelationField("application") +RedashQuery.APPLICATION_FIELD = RelationField("applicationField") +RedashQuery.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +RedashQuery.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +RedashQuery.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +RedashQuery.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +RedashQuery.METRICS = RelationField("metrics") +RedashQuery.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +RedashQuery.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +RedashQuery.MEANINGS = RelationField("meanings") +RedashQuery.MC_MONITORS = RelationField("mcMonitors") +RedashQuery.MC_INCIDENTS = RelationField("mcIncidents") +RedashQuery.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +RedashQuery.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +RedashQuery.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +RedashQuery.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +RedashQuery.REDASH_VISUALIZATIONS = RelationField("redashVisualizations") +RedashQuery.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +RedashQuery.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +RedashQuery.FILES = RelationField("files") +RedashQuery.LINKS = RelationField("links") +RedashQuery.README = RelationField("readme") +RedashQuery.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +RedashQuery.SODA_CHECKS = RelationField("sodaChecks") +RedashQuery.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +RedashQuery.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/redash_related.py b/pyatlan_v9/model/assets/redash_related.py new file mode 100644 index 000000000..0a3ecf7ee --- /dev/null +++ b/pyatlan_v9/model/assets/redash_related.py @@ -0,0 +1,122 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Redash module. + +This module contains all Related{Type} classes for the Redash type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Dict, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedBI +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedRedash", + "RelatedRedashVisualization", + "RelatedRedashDashboard", + "RelatedRedashQuery", +] + + +class RelatedRedash(RelatedBI): + """ + Related entity reference for Redash assets. + + Extends RelatedBI with Redash-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Redash" so it serializes correctly + + redash_is_published: Union[bool, None, UnsetType] = UNSET + """Whether this asset is published in Redash (true) or not (false).""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Redash" + + +class RelatedRedashVisualization(RelatedRedash): + """ + Related entity reference for RedashVisualization assets. + + Extends RelatedRedash with RedashVisualization-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "RedashVisualization" so it serializes correctly + + redash_visualization_type: Union[str, None, UnsetType] = UNSET + """Type of this visualization.""" + + redash_query_name: Union[str, None, UnsetType] = UNSET + """Simple name of the query from which this visualization is created.""" + + redash_query_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the query from which this visualization is created.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "RedashVisualization" + + +class RelatedRedashDashboard(RelatedRedash): + """ + Related entity reference for RedashDashboard assets. + + Extends RelatedRedash with RedashDashboard-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "RedashDashboard" so it serializes correctly + + redash_dashboard_widget_count: Union[int, None, UnsetType] = UNSET + """Number of widgets in this dashboard.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "RedashDashboard" + + +class RelatedRedashQuery(RelatedRedash): + """ + Related entity reference for RedashQuery assets. + + Extends RelatedRedash with RedashQuery-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "RedashQuery" so it serializes correctly + + redash_query_sql: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="redashQuerySQL" + ) + """SQL code of this query.""" + + redash_query_parameters: Union[str, None, UnsetType] = UNSET + """Parameters of this query.""" + + redash_query_schedule: Union[Dict[str, str], None, UnsetType] = UNSET + """Schedule for this query.""" + + redash_query_last_execution_runtime: Union[float, None, UnsetType] = UNSET + """Elapsed time of the last execution of this query.""" + + redash_query_last_executed_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) when this query was last executed, in milliseconds.""" + + redash_query_schedule_humanized: Union[str, None, UnsetType] = UNSET + """Schdule for this query in readable text for overview tab and filtering.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "RedashQuery" diff --git a/pyatlan_v9/model/assets/redash_visualization.py b/pyatlan_v9/model/assets/redash_visualization.py new file mode 100644 index 000000000..e55dad573 --- /dev/null +++ b/pyatlan_v9/model/assets/redash_visualization.py @@ -0,0 +1,623 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +RedashVisualization asset model with flattened inheritance. + +This module provides: +- RedashVisualization: Flat asset class (easy to use) +- RedashVisualizationAttributes: Nested attributes struct (extends AssetAttributes) +- RedashVisualizationNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .redash_related import RelatedRedashQuery + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class RedashVisualization(Asset): + """ + Instance of a Redash visualization in Atlan. + """ + + REDASH_VISUALIZATION_TYPE: ClassVar[Any] = None + REDASH_QUERY_NAME: ClassVar[Any] = None + REDASH_QUERY_QUALIFIED_NAME: ClassVar[Any] = None + REDASH_IS_PUBLISHED: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + REDASH_QUERY: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "RedashVisualization" + + redash_visualization_type: Union[str, None, UnsetType] = UNSET + """Type of this visualization.""" + + redash_query_name: Union[str, None, UnsetType] = UNSET + """Simple name of the query from which this visualization is created.""" + + redash_query_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the query from which this visualization is created.""" + + redash_is_published: Union[bool, None, UnsetType] = UNSET + """Whether this asset is published in Redash (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + redash_query: Union[RelatedRedashQuery, None, UnsetType] = UNSET + """Query which created this visualization.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "RedashVisualization" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _redash_visualization_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> RedashVisualization: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + RedashVisualization instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _redash_visualization_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class RedashVisualizationAttributes(AssetAttributes): + """RedashVisualization-specific attributes for nested API format.""" + + redash_visualization_type: Union[str, None, UnsetType] = UNSET + """Type of this visualization.""" + + redash_query_name: Union[str, None, UnsetType] = UNSET + """Simple name of the query from which this visualization is created.""" + + redash_query_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the query from which this visualization is created.""" + + redash_is_published: Union[bool, None, UnsetType] = UNSET + """Whether this asset is published in Redash (true) or not (false).""" + + +class RedashVisualizationRelationshipAttributes(AssetRelationshipAttributes): + """RedashVisualization-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + redash_query: Union[RelatedRedashQuery, None, UnsetType] = UNSET + """Query which created this visualization.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class RedashVisualizationNested(AssetNested): + """RedashVisualization in nested API format for high-performance serialization.""" + + attributes: Union[RedashVisualizationAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + RedashVisualizationRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + RedashVisualizationRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + RedashVisualizationRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_REDASH_VISUALIZATION_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "redash_query", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_redash_visualization_attrs( + attrs: RedashVisualizationAttributes, obj: RedashVisualization +) -> None: + """Populate RedashVisualization-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.redash_visualization_type = obj.redash_visualization_type + attrs.redash_query_name = obj.redash_query_name + attrs.redash_query_qualified_name = obj.redash_query_qualified_name + attrs.redash_is_published = obj.redash_is_published + + +def _extract_redash_visualization_attrs(attrs: RedashVisualizationAttributes) -> dict: + """Extract all RedashVisualization attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["redash_visualization_type"] = attrs.redash_visualization_type + result["redash_query_name"] = attrs.redash_query_name + result["redash_query_qualified_name"] = attrs.redash_query_qualified_name + result["redash_is_published"] = attrs.redash_is_published + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _redash_visualization_to_nested( + redash_visualization: RedashVisualization, +) -> RedashVisualizationNested: + """Convert flat RedashVisualization to nested format.""" + attrs = RedashVisualizationAttributes() + _populate_redash_visualization_attrs(attrs, redash_visualization) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + redash_visualization, + _REDASH_VISUALIZATION_REL_FIELDS, + RedashVisualizationRelationshipAttributes, + ) + return RedashVisualizationNested( + guid=redash_visualization.guid, + type_name=redash_visualization.type_name, + status=redash_visualization.status, + version=redash_visualization.version, + create_time=redash_visualization.create_time, + update_time=redash_visualization.update_time, + created_by=redash_visualization.created_by, + updated_by=redash_visualization.updated_by, + classifications=redash_visualization.classifications, + classification_names=redash_visualization.classification_names, + meanings=redash_visualization.meanings, + labels=redash_visualization.labels, + business_attributes=redash_visualization.business_attributes, + custom_attributes=redash_visualization.custom_attributes, + pending_tasks=redash_visualization.pending_tasks, + proxy=redash_visualization.proxy, + is_incomplete=redash_visualization.is_incomplete, + provenance_type=redash_visualization.provenance_type, + home_id=redash_visualization.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _redash_visualization_from_nested( + nested: RedashVisualizationNested, +) -> RedashVisualization: + """Convert nested format to flat RedashVisualization.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else RedashVisualizationAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _REDASH_VISUALIZATION_REL_FIELDS, + RedashVisualizationRelationshipAttributes, + ) + return RedashVisualization( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_redash_visualization_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _redash_visualization_to_nested_bytes( + redash_visualization: RedashVisualization, serde: Serde +) -> bytes: + """Convert flat RedashVisualization to nested JSON bytes.""" + return serde.encode(_redash_visualization_to_nested(redash_visualization)) + + +def _redash_visualization_from_nested_bytes( + data: bytes, serde: Serde +) -> RedashVisualization: + """Convert nested JSON bytes to flat RedashVisualization.""" + nested = serde.decode(data, RedashVisualizationNested) + return _redash_visualization_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + RelationField, +) + +RedashVisualization.REDASH_VISUALIZATION_TYPE = KeywordField( + "redashVisualizationType", "redashVisualizationType" +) +RedashVisualization.REDASH_QUERY_NAME = KeywordTextField( + "redashQueryName", "redashQueryName", "redashQueryName.text" +) +RedashVisualization.REDASH_QUERY_QUALIFIED_NAME = KeywordTextField( + "redashQueryQualifiedName", + "redashQueryQualifiedName", + "redashQueryQualifiedName.text", +) +RedashVisualization.REDASH_IS_PUBLISHED = BooleanField( + "redashIsPublished", "redashIsPublished" +) +RedashVisualization.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +RedashVisualization.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +RedashVisualization.ANOMALO_CHECKS = RelationField("anomaloChecks") +RedashVisualization.APPLICATION = RelationField("application") +RedashVisualization.APPLICATION_FIELD = RelationField("applicationField") +RedashVisualization.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +RedashVisualization.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +RedashVisualization.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +RedashVisualization.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +RedashVisualization.METRICS = RelationField("metrics") +RedashVisualization.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +RedashVisualization.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +RedashVisualization.MEANINGS = RelationField("meanings") +RedashVisualization.MC_MONITORS = RelationField("mcMonitors") +RedashVisualization.MC_INCIDENTS = RelationField("mcIncidents") +RedashVisualization.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +RedashVisualization.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +RedashVisualization.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +RedashVisualization.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +RedashVisualization.REDASH_QUERY = RelationField("redashQuery") +RedashVisualization.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +RedashVisualization.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +RedashVisualization.FILES = RelationField("files") +RedashVisualization.LINKS = RelationField("links") +RedashVisualization.README = RelationField("readme") +RedashVisualization.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +RedashVisualization.SODA_CHECKS = RelationField("sodaChecks") +RedashVisualization.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +RedashVisualization.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/referenceable.py b/pyatlan_v9/model/assets/referenceable.py new file mode 100644 index 000000000..231804d71 --- /dev/null +++ b/pyatlan_v9/model/assets/referenceable.py @@ -0,0 +1,427 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Referenceable asset model with flattened inheritance. + +This module provides: +- Referenceable: Flat asset class (easy to use) +- ReferenceableAttributes: Nested attributes struct (extends AssetAttributes) +- ReferenceableNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from pyatlan.model.fields.atlan_fields import ( + InternalKeywordField, + InternalKeywordTextField, + InternalNumericField, + KeywordField, + KeywordTextField, + NumericField, + TextField, +) +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.lineage_ref import LineageRef +from pyatlan_v9.model.serde import Serde, get_serde + +from .entity import Entity +from .referenceable_related import RelatedReferenceable + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +class Referenceable(Entity): + """ + Base class for everything in Atlan that can be referenced by qualifiedName. + """ + + # ========================================================================= + # Field Descriptors (class-level, for search query building) + # ========================================================================= + + TYPE_NAME: ClassVar[InternalKeywordTextField] = InternalKeywordTextField( + "typeName", "__typeName.keyword", "__typeName", "__typeName" + ) + """Type of the asset. For example Table, Column, and so on.""" + + GUID: ClassVar[KeywordField] = InternalKeywordField("guid", "__guid", "__guid") + """Globally unique identifier (GUID) of any object in Atlan.""" + + CREATED_BY: ClassVar[KeywordField] = InternalKeywordField( + "createdBy", "__createdBy", "__createdBy" + ) + """Atlan user who created this asset.""" + + UPDATED_BY: ClassVar[KeywordField] = InternalKeywordField( + "updatedBy", "__modifiedBy", "__modifiedBy" + ) + """Atlan user who last updated the asset.""" + + STATUS: ClassVar[KeywordField] = InternalKeywordField( + "status", "__state", "__state" + ) + """Asset status in Atlan (active vs deleted).""" + + ATLAN_TAGS: ClassVar[KeywordTextField] = InternalKeywordTextField( + "classificationNames", + "__traitNames", + "__classificationsText", + "__classificationNames", + ) + """All directly-assigned Atlan tags that exist on an asset.""" + + PROPAGATED_ATLAN_TAGS: ClassVar[KeywordTextField] = InternalKeywordTextField( + "classificationNames", + "__propagatedTraitNames", + "__classificationsText", + "__propagatedClassificationNames", + ) + """All propagated Atlan tags that exist on an asset.""" + + ASSIGNED_TERMS: ClassVar[KeywordTextField] = InternalKeywordTextField( + "meanings", "__meanings", "__meaningsText", "__meanings" + ) + """All terms attached to an asset, searchable by the term's qualifiedName.""" + + SUPER_TYPE_NAMES: ClassVar[KeywordTextField] = InternalKeywordTextField( + "typeName", + "__superTypeNames.keyword", + "__superTypeNames", + "__superTypeNames", + ) + """All super types of an asset.""" + + CREATE_TIME: ClassVar[NumericField] = InternalNumericField( + "createTime", "__timestamp", "__timestamp" + ) + """Time (in milliseconds) when the asset was created.""" + + UPDATE_TIME: ClassVar[NumericField] = InternalNumericField( + "updateTime", "__modificationTimestamp", "__modificationTimestamp" + ) + """Time (in milliseconds) when the asset was last updated.""" + + QUALIFIED_NAME: ClassVar[KeywordTextField] = KeywordTextField( + "qualifiedName", "qualifiedName", "qualifiedName.text" + ) + """Unique fully-qualified name of the asset in Atlan.""" + + CUSTOM_ATTRIBUTES: ClassVar[TextField] = TextField( + "customAttributes", "customAttributes" + ) + """Custom attributes for this asset.""" + + # ========================================================================= + # Instance Fields + # ========================================================================= + + # Override type_name with Referenceable-specific default + type_name: Union[str, UnsetType] = "Referenceable" + + qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name for this asset. This is typically a concatenation of the asset's name onto its parent's qualifiedName. This must be unique across all assets of the same type.""" + + replicated_from: Union[list[dict[str, Any]], None, UnsetType] = UNSET + """Unused. List of servers where this entity is replicated from.""" + + replicated_to: Union[list[dict[str, Any]], None, UnsetType] = UNSET + """Unused. List of servers where this entity is replicated to.""" + + user_def_relationship_to: Union[list[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[list[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + depth: Union[int, None, UnsetType] = UNSET + """Depth of this asset within lineage (populated by lineage API responses).""" + + immediate_upstream: Union[list[LineageRef], None] = None + """Assets immediately upstream in lineage (populated when immediateNeighbours=True).""" + + immediate_downstream: Union[list[LineageRef], None] = None + """Assets immediately downstream in lineage (populated when immediateNeighbours=True).""" + + @classmethod + def can_be_archived(cls) -> bool: + """ + Indicates if an asset can be archived via the asset.delete_by_guid method. + :returns: True if archiving is supported + """ + return True + + # ========================================================================= + # Compatibility Properties (legacy API surface) + # ========================================================================= + + @property + def assigned_terms(self): + """ + Get assigned glossary terms (maps to Entity.meanings). + + In legacy models, assigned_terms was a property that mapped to + attributes.meanings. In v9, meanings is a direct field on Entity. + """ + return self.meanings if self.meanings is not UNSET else None + + @assigned_terms.setter + def assigned_terms(self, value): + """Set assigned glossary terms.""" + self.meanings = value + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return _referenceable_to_nested_bytes(self, serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + @staticmethod + def from_json( + json_data: Union[str, bytes], serde: Serde | None = None + ) -> "Referenceable": + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Referenceable instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _referenceable_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class ReferenceableAttributes( + msgspec.Struct, kw_only=True, omit_defaults=True, rename="camel" +): + """Referenceable-specific attributes for nested API format.""" + + qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name for this asset. This is typically a concatenation of the asset's name onto its parent's qualifiedName. This must be unique across all assets of the same type.""" + + replicated_from: Union[list[dict[str, Any]], None, UnsetType] = UNSET + """Unused. List of servers where this entity is replicated from.""" + + replicated_to: Union[list[dict[str, Any]], None, UnsetType] = UNSET + """Unused. List of servers where this entity is replicated to.""" + + +class ReferenceableRelationshipAttributes( + msgspec.Struct, kw_only=True, omit_defaults=True, rename="camel" +): + """Referenceable-specific relationship attributes for nested API format.""" + + user_def_relationship_to: Union[list[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[list[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + +class ReferenceableNested( + msgspec.Struct, kw_only=True, omit_defaults=True, rename="camel" +): + """Referenceable in nested API format for high-performance serialization.""" + + guid: Union[Any, UnsetType] = UNSET + type_name: Union[Any, UnsetType] = UNSET + status: Union[Any, UnsetType] = UNSET + delete_handler: Union[Any, UnsetType] = UNSET + version: Union[Any, UnsetType] = UNSET + create_time: Union[Any, UnsetType] = UNSET + update_time: Union[Any, UnsetType] = UNSET + created_by: Union[Any, UnsetType] = UNSET + updated_by: Union[Any, UnsetType] = UNSET + classifications: Union[Any, UnsetType] = UNSET + classification_names: Union[Any, UnsetType] = UNSET + meanings: Union[Any, UnsetType] = UNSET + labels: Union[Any, UnsetType] = UNSET + business_attributes: Union[Any, UnsetType] = UNSET + custom_attributes: Union[Any, UnsetType] = UNSET + pending_tasks: Union[Any, UnsetType] = UNSET + proxy: Union[Any, UnsetType] = UNSET + is_incomplete: Union[Any, UnsetType] = UNSET + provenance_type: Union[Any, UnsetType] = UNSET + home_id: Union[Any, UnsetType] = UNSET + + attributes: Union[ReferenceableAttributes, UnsetType] = UNSET + relationship_attributes: Union[ReferenceableRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + ReferenceableRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + ReferenceableRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_REFERENCEABLE_REL_FIELDS: list[str] = [ + "meanings", + "user_def_relationship_to", + "user_def_relationship_from", +] + + +def _populate_referenceable_attrs( + attrs: ReferenceableAttributes, obj: Referenceable +) -> None: + """Populate Referenceable-specific attributes on the attrs struct.""" + attrs.qualified_name = obj.qualified_name + attrs.replicated_from = obj.replicated_from + attrs.replicated_to = obj.replicated_to + + +def _extract_referenceable_attrs(attrs: ReferenceableAttributes) -> dict: + """Extract all Referenceable attributes from the attrs struct into a flat dict.""" + result = {} + result["qualified_name"] = attrs.qualified_name + result["replicated_from"] = attrs.replicated_from + result["replicated_to"] = attrs.replicated_to + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _referenceable_to_nested(referenceable: Referenceable) -> ReferenceableNested: + """Convert flat Referenceable to nested format.""" + attrs = ReferenceableAttributes( + qualified_name=referenceable.qualified_name, + replicated_from=referenceable.replicated_from, + replicated_to=referenceable.replicated_to, + ) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + referenceable, _REFERENCEABLE_REL_FIELDS, ReferenceableRelationshipAttributes + ) + return ReferenceableNested( + guid=referenceable.guid, + type_name=referenceable.type_name, + status=referenceable.status, + delete_handler=referenceable.delete_handler, + version=referenceable.version, + create_time=referenceable.create_time, + update_time=referenceable.update_time, + created_by=referenceable.created_by, + updated_by=referenceable.updated_by, + classifications=referenceable.classifications, + classification_names=referenceable.classification_names, + meanings=referenceable.meanings, + labels=referenceable.labels, + business_attributes=referenceable.business_attributes, + custom_attributes=referenceable.custom_attributes, + pending_tasks=referenceable.pending_tasks, + proxy=referenceable.proxy, + is_incomplete=referenceable.is_incomplete, + provenance_type=referenceable.provenance_type, + home_id=referenceable.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _referenceable_from_nested(nested: ReferenceableNested) -> Referenceable: + """Convert nested format to flat Referenceable.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else ReferenceableAttributes() + ) + # Merge relationships from all three buckets + rel_fields: list[str] = ["user_def_relationship_to", "user_def_relationship_from"] + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + rel_fields, + ReferenceableRelationshipAttributes, + ) + return Referenceable( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + delete_handler=nested.delete_handler, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + qualified_name=attrs.qualified_name, + replicated_from=attrs.replicated_from, + replicated_to=attrs.replicated_to, + # Merged relationship attributes + **merged_rels, + ) + + +def _referenceable_to_nested_bytes(referenceable: Referenceable, serde: Serde) -> bytes: + """Convert flat Referenceable to nested JSON bytes.""" + return serde.encode(_referenceable_to_nested(referenceable)) + + +def _referenceable_from_nested_bytes(data: bytes, serde: Serde) -> Referenceable: + """Convert nested JSON bytes to flat Referenceable.""" + nested = serde.decode(data, ReferenceableNested) + return _referenceable_from_nested(nested) diff --git a/pyatlan_v9/model/assets/referenceable_related.py b/pyatlan_v9/model/assets/referenceable_related.py new file mode 100644 index 000000000..fda32fa13 --- /dev/null +++ b/pyatlan_v9/model/assets/referenceable_related.py @@ -0,0 +1,68 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Referenceable module. + +This module contains all Related{Type} classes for the Referenceable type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Any, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .related_entity import RelatedEntity + +__all__ = [ + "RelatedReferenceable", + "RelatedPersona", +] + + +class RelatedReferenceable(RelatedEntity): + """ + Related entity reference for Referenceable assets. + + Extends RelatedEntity with Referenceable-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Referenceable" so it serializes correctly + + qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name for this asset. This is typically a concatenation of the asset's name onto its parent's qualifiedName. This must be unique across all assets of the same type.""" + + replicated_from: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """Unused. List of servers where this entity is replicated from.""" + + replicated_to: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """Unused. List of servers where this entity is replicated to.""" + + def __post_init__(self) -> None: + """Convert qualified_name shorthand to unique_attributes if needed. + + Keeps qualified_name accessible (parity with legacy property accessor) + while also populating unique_attributes for API serialization. + """ + if self.qualified_name is not UNSET and self.unique_attributes is UNSET: + self.unique_attributes = {"qualifiedName": self.qualified_name} + + +class RelatedPersona(RelatedReferenceable): + """ + Related entity reference for Persona assets. + + Persona is a bootstrapped type that exists in all Atlan tenants but is not + defined in the typedef hierarchy. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Persona" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Persona" diff --git a/pyatlan_v9/model/assets/related_entity.py b/pyatlan_v9/model/assets/related_entity.py new file mode 100644 index 000000000..2314afe58 --- /dev/null +++ b/pyatlan_v9/model/assets/related_entity.py @@ -0,0 +1,81 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +RelatedEntity base class for relationship attribute references. + +This module provides the base class for all Related{Type} classes used to +reference related entities in relationship attributes. RelatedEntity contains +the minimal set of fields needed to identify and reference another entity. +""" + +from __future__ import annotations + +from enum import Enum +from typing import Any, Dict, Union + +import msgspec +from msgspec import UNSET, UnsetType + + +class SaveSemantic(str, Enum): + """ + Save semantic for relationship operations. + + Controls how relationship attributes are handled during save operations. + """ + + REPLACE = "REPLACE" + """Replace all existing relationships with the new values.""" + + APPEND = "APPEND" + """Add to existing relationships without removing any.""" + + REMOVE = "REMOVE" + """Remove the specified relationships.""" + + +class RelatedEntity(msgspec.Struct, kw_only=True, omit_defaults=True, rename="camel"): + """ + Base class for related entity references in relationship attributes. + + This class contains the minimal fields needed to reference another entity + in a relationship. Specific Related{Type} classes extend this with + type-specific attributes. + """ + + # Core identity + guid: Union[str, UnsetType] = UNSET + """Globally unique identifier for the related entity.""" + + type_name: Union[str, UnsetType] = UNSET + """The type name of the related entity.""" + + unique_attributes: Union[Dict[str, Any], UnsetType] = UNSET + """Unique attributes that can identify this entity (e.g., qualifiedName).""" + + # Relationship-specific attributes + relationship_attributes: Union[Dict[str, Any], None, UnsetType] = UNSET + """Attributes of the relationship itself (e.g., description, status, etc.).""" + + # Display and status + display_text: Union[str, UnsetType] = UNSET + """Display text for this related entity (e.g., "Annual Recurring Revenue").""" + + entity_status: Union[str, UnsetType] = UNSET + """Status of the related entity (ACTIVE, DELETED).""" + + # Relationship metadata + relationship_guid: Union[str, UnsetType] = UNSET + """Globally unique identifier for the relationship instance.""" + + relationship_status: Union[str, UnsetType] = UNSET + """Status of the relationship (ACTIVE, DELETED).""" + + relationship_type: Union[str, UnsetType] = UNSET + """Type name of the relationship (e.g., "AtlasGlossaryRelatedTerm").""" + + # Save semantic (not serialized to JSON, used internally) + semantic: Union[SaveSemantic, UnsetType] = UNSET + """The save semantic for this relationship (REPLACE, APPEND, REMOVE).""" diff --git a/pyatlan_v9/model/assets/relations/__init__.py b/pyatlan_v9/model/assets/relations/__init__.py new file mode 100644 index 000000000..7de5e1298 --- /dev/null +++ b/pyatlan_v9/model/assets/relations/__init__.py @@ -0,0 +1,45 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +""" +V9 relationship attribute models (msgspec.Struct). + +These classes replace the legacy Pydantic-based relationship models from +``pyatlan.model.assets.relations``. Each class: + +* holds the relationship-specific attributes (description, status, …), +* provides builder methods that return ``Related{Type}`` references with + ``relationship_type`` and ``relationship_attributes`` populated. +""" + +from pyatlan_v9.model.assets.relations.relationship_attributes import ( + AtlasGlossaryIsARelationship, + AtlasGlossaryPreferredTerm, + AtlasGlossaryRelatedTerm, + AtlasGlossaryReplacementTerm, + AtlasGlossarySemanticAssignment, + AtlasGlossarySynonym, + AtlasGlossaryTermCategorization, + AtlasGlossaryTranslation, + AtlasGlossaryValidValue, + CustomRelatedFromEntitiesCustomRelatedToEntities, + IndistinctRelationship, + RelationshipAttributes, + UserDefRelationship, +) + +__all__ = [ + "RelationshipAttributes", + "IndistinctRelationship", + "AtlasGlossaryTermCategorization", + "AtlasGlossaryIsARelationship", + "AtlasGlossaryValidValue", + "AtlasGlossaryPreferredTerm", + "AtlasGlossaryReplacementTerm", + "AtlasGlossaryTranslation", + "AtlasGlossaryRelatedTerm", + "AtlasGlossarySynonym", + "AtlasGlossarySemanticAssignment", + "UserDefRelationship", + "CustomRelatedFromEntitiesCustomRelatedToEntities", +] diff --git a/pyatlan_v9/model/assets/relations/relationship_attributes.py b/pyatlan_v9/model/assets/relations/relationship_attributes.py new file mode 100644 index 000000000..54fef778e --- /dev/null +++ b/pyatlan_v9/model/assets/relations/relationship_attributes.py @@ -0,0 +1,551 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +""" +Relationship attribute models for v9 (msgspec.Struct). + +Each class holds relationship-specific attributes and provides builder methods +that return Related{Type} instances with ``relationship_type`` and +``relationship_attributes`` populated for serialization. +""" + +from __future__ import annotations + +from typing import Any, Optional + +from msgspec import UNSET + +from pyatlan_v9.model.assets.related_entity import RelatedEntity, SaveSemantic + +# --------------------------------------------------------------------------- +# Helper: build a Related reference from a relationship + related entity +# --------------------------------------------------------------------------- + + +def _build_related( + related_cls: type, + related: Any, + relationship_type_name: str, + attrs_dict: dict[str, Any], + semantic: SaveSemantic, +) -> RelatedEntity: + """Build a ``Related{Type}`` reference with relationship attributes. + + Parameters + ---------- + related_cls: + The ``Related{Type}`` class to instantiate (e.g. ``RelatedAtlasGlossaryTerm``). + May be ``None``; in that case ``RelatedReferenceable`` is used. + related: + The entity being referenced (must have ``guid`` and/or ``qualified_name``). + relationship_type_name: + The wire-format relationship type name (e.g. ``"AtlasGlossaryTermCategorization"``). + attrs_dict: + The relationship-specific attributes as a plain dict. + semantic: + Save semantic for the relationship. + """ + kwargs: dict[str, Any] = {} + + guid = getattr(related, "guid", UNSET) + if guid is not UNSET and guid is not None: + kwargs["guid"] = guid + + qn = getattr(related, "qualified_name", UNSET) + if qn is not UNSET and qn is not None: + kwargs["unique_attributes"] = {"qualifiedName": qn} + + # Build the relationship attributes sub-object + rel_attrs: dict[str, Any] = { + "typeName": relationship_type_name, + "attributes": attrs_dict, + } + + kwargs["relationship_type"] = relationship_type_name + kwargs["relationship_attributes"] = rel_attrs + kwargs["semantic"] = semantic + + return related_cls(**kwargs) + + +def _build_generic_related( + related: Any, + relationship_type_name: str, + attrs_dict: dict[str, Any], + semantic: SaveSemantic, +) -> RelatedEntity: + """Build a ``RelatedReferenceable`` reference for generic relationships. + + Used when the related entity type is not known at class-definition time + (e.g. ``UserDefRelationship``, ``CustomRelated…``). + """ + from pyatlan_v9.model.assets.referenceable_related import RelatedReferenceable + + kwargs: dict[str, Any] = {} + + guid = getattr(related, "guid", UNSET) + if guid is not UNSET and guid is not None: + kwargs["guid"] = guid + + qn = getattr(related, "qualified_name", UNSET) + if qn is not UNSET and qn is not None: + kwargs["unique_attributes"] = {"qualifiedName": qn} + + # Preserve the related entity's type_name + tn = getattr(related, "type_name", UNSET) + if tn is not UNSET and tn is not None: + kwargs["type_name"] = tn + + rel_attrs: dict[str, Any] = { + "typeName": relationship_type_name, + "attributes": attrs_dict, + } + + kwargs["relationship_type"] = relationship_type_name + kwargs["relationship_attributes"] = rel_attrs + kwargs["semantic"] = semantic + + return RelatedReferenceable(**kwargs) + + +# --------------------------------------------------------------------------- +# Base class +# --------------------------------------------------------------------------- + + +class RelationshipAttributes: + """Base class for all v9 relationship attribute models.""" + + type_name: str = "" + + def _attrs_dict(self) -> dict[str, Any]: + """Return the relationship-specific attributes as a plain dict. + + Subclasses should override this. + """ + return {} + + +# --------------------------------------------------------------------------- +# IndistinctRelationship (fallback for unknown relationship types) +# --------------------------------------------------------------------------- + + +class IndistinctRelationship(RelationshipAttributes): + """Fallback relationship model for relationship types not modelled in the SDK.""" + + def __init__( + self, + type_name: str = "IndistinctRelationship", + attributes: Optional[dict[str, Any]] = None, + ): + self.type_name = type_name + self.attributes = attributes or {} + + def _attrs_dict(self) -> dict[str, Any]: + return dict(self.attributes) + + +# --------------------------------------------------------------------------- +# Glossary term-to-category relationship +# --------------------------------------------------------------------------- + + +class AtlasGlossaryTermCategorization(RelationshipAttributes): + """Relationship: organises terms into categories.""" + + type_name = "AtlasGlossaryTermCategorization" + + def __init__( + self, + description: Optional[str] = None, + status: Optional[str] = None, + ): + self.description = description + self.status = status + + def _attrs_dict(self) -> dict[str, Any]: + d: dict[str, Any] = {} + if self.description is not None: + d["description"] = self.description + if self.status is not None: + d["status"] = self.status + return d + + # Builder methods ------------------------------------------------------- + + def terms( + self, related: Any, semantic: SaveSemantic = SaveSemantic.REPLACE + ) -> RelatedEntity: + """Build a reference for the *terms* end of the relationship.""" + from pyatlan_v9.model.assets.gtc_related import RelatedAtlasGlossaryTerm + + return _build_related( + RelatedAtlasGlossaryTerm, + related, + self.type_name, + self._attrs_dict(), + semantic, + ) + + def categories( + self, related: Any, semantic: SaveSemantic = SaveSemantic.REPLACE + ) -> RelatedEntity: + """Build a reference for the *categories* end of the relationship.""" + from pyatlan_v9.model.assets.gtc_related import RelatedAtlasGlossaryCategory + + return _build_related( + RelatedAtlasGlossaryCategory, + related, + self.type_name, + self._attrs_dict(), + semantic, + ) + + +# --------------------------------------------------------------------------- +# Common glossary-term relationship base (shared attrs: description, expression, +# status, steward, source) +# --------------------------------------------------------------------------- + + +class _GlossaryTermRelationship(RelationshipAttributes): + """Base for glossary term-to-term relationships with the common attribute set.""" + + def __init__( + self, + description: Optional[str] = None, + expression: Optional[str] = None, + status: Optional[str] = None, + steward: Optional[str] = None, + source: Optional[str] = None, + ): + self.description = description + self.expression = expression + self.status = status + self.steward = steward + self.source = source + + def _attrs_dict(self) -> dict[str, Any]: + d: dict[str, Any] = {} + if self.description is not None: + d["description"] = self.description + if self.expression is not None: + d["expression"] = self.expression + if self.status is not None: + d["status"] = self.status + if self.steward is not None: + d["steward"] = self.steward + if self.source is not None: + d["source"] = self.source + return d + + # Convenience builder for RelatedAtlasGlossaryTerm + def _term_ref( + self, related: Any, semantic: SaveSemantic = SaveSemantic.REPLACE + ) -> RelatedEntity: + from pyatlan_v9.model.assets.gtc_related import RelatedAtlasGlossaryTerm + + return _build_related( + RelatedAtlasGlossaryTerm, + related, + self.type_name, + self._attrs_dict(), + semantic, + ) + + +# --------------------------------------------------------------------------- +# AtlasGlossaryIsARelationship +# --------------------------------------------------------------------------- + + +class AtlasGlossaryIsARelationship(_GlossaryTermRelationship): + """Relationship: ISA (hierarchy) between glossary terms.""" + + type_name = "AtlasGlossaryIsARelationship" + + def classifies( + self, related: Any, semantic: SaveSemantic = SaveSemantic.REPLACE + ) -> RelatedEntity: + return self._term_ref(related, semantic) + + def is_a( + self, related: Any, semantic: SaveSemantic = SaveSemantic.REPLACE + ) -> RelatedEntity: + return self._term_ref(related, semantic) + + +# --------------------------------------------------------------------------- +# AtlasGlossaryValidValue +# --------------------------------------------------------------------------- + + +class AtlasGlossaryValidValue(_GlossaryTermRelationship): + """Relationship: valid-value constraint between glossary terms.""" + + type_name = "AtlasGlossaryValidValue" + + def valid_values( + self, related: Any, semantic: SaveSemantic = SaveSemantic.REPLACE + ) -> RelatedEntity: + return self._term_ref(related, semantic) + + def valid_values_for( + self, related: Any, semantic: SaveSemantic = SaveSemantic.REPLACE + ) -> RelatedEntity: + return self._term_ref(related, semantic) + + +# --------------------------------------------------------------------------- +# AtlasGlossaryPreferredTerm +# --------------------------------------------------------------------------- + + +class AtlasGlossaryPreferredTerm(_GlossaryTermRelationship): + """Relationship: preferred-term link between glossary terms.""" + + type_name = "AtlasGlossaryPreferredTerm" + + def preferred_terms( + self, related: Any, semantic: SaveSemantic = SaveSemantic.REPLACE + ) -> RelatedEntity: + return self._term_ref(related, semantic) + + def preferred_to_terms( + self, related: Any, semantic: SaveSemantic = SaveSemantic.REPLACE + ) -> RelatedEntity: + return self._term_ref(related, semantic) + + +# --------------------------------------------------------------------------- +# AtlasGlossaryReplacementTerm +# --------------------------------------------------------------------------- + + +class AtlasGlossaryReplacementTerm(_GlossaryTermRelationship): + """Relationship: replacement-term link between glossary terms.""" + + type_name = "AtlasGlossaryReplacementTerm" + + def replacement_terms( + self, related: Any, semantic: SaveSemantic = SaveSemantic.REPLACE + ) -> RelatedEntity: + return self._term_ref(related, semantic) + + def replaced_by( + self, related: Any, semantic: SaveSemantic = SaveSemantic.REPLACE + ) -> RelatedEntity: + return self._term_ref(related, semantic) + + +# --------------------------------------------------------------------------- +# AtlasGlossaryTranslation +# --------------------------------------------------------------------------- + + +class AtlasGlossaryTranslation(_GlossaryTermRelationship): + """Relationship: translation link between glossary terms.""" + + type_name = "AtlasGlossaryTranslation" + + def translated_terms( + self, related: Any, semantic: SaveSemantic = SaveSemantic.REPLACE + ) -> RelatedEntity: + return self._term_ref(related, semantic) + + def translation_terms( + self, related: Any, semantic: SaveSemantic = SaveSemantic.REPLACE + ) -> RelatedEntity: + return self._term_ref(related, semantic) + + +# --------------------------------------------------------------------------- +# AtlasGlossaryRelatedTerm +# --------------------------------------------------------------------------- + + +class AtlasGlossaryRelatedTerm(_GlossaryTermRelationship): + """Relationship: see-also link between glossary terms.""" + + type_name = "AtlasGlossaryRelatedTerm" + + def see_also( + self, related: Any, semantic: SaveSemantic = SaveSemantic.REPLACE + ) -> RelatedEntity: + return self._term_ref(related, semantic) + + +# --------------------------------------------------------------------------- +# AtlasGlossarySynonym +# --------------------------------------------------------------------------- + + +class AtlasGlossarySynonym(_GlossaryTermRelationship): + """Relationship: synonym link between glossary terms.""" + + type_name = "AtlasGlossarySynonym" + + def synonyms( + self, related: Any, semantic: SaveSemantic = SaveSemantic.REPLACE + ) -> RelatedEntity: + return self._term_ref(related, semantic) + + +# --------------------------------------------------------------------------- +# AtlasGlossarySemanticAssignment +# --------------------------------------------------------------------------- + + +class AtlasGlossarySemanticAssignment(RelationshipAttributes): + """Relationship: semantic assignment (term <-> asset).""" + + type_name = "AtlasGlossarySemanticAssignment" + + def __init__( + self, + description: Optional[str] = None, + expression: Optional[str] = None, + status: Optional[str] = None, + confidence: Optional[int] = None, + created_by: Optional[str] = None, + steward: Optional[str] = None, + source: Optional[str] = None, + ): + self.description = description + self.expression = expression + self.status = status + self.confidence = confidence + self.created_by = created_by + self.steward = steward + self.source = source + + def _attrs_dict(self) -> dict[str, Any]: + d: dict[str, Any] = {} + if self.description is not None: + d["description"] = self.description + if self.expression is not None: + d["expression"] = self.expression + if self.status is not None: + d["status"] = self.status + if self.confidence is not None: + d["confidence"] = self.confidence + if self.created_by is not None: + d["createdBy"] = self.created_by + if self.steward is not None: + d["steward"] = self.steward + if self.source is not None: + d["source"] = self.source + return d + + def assigned_entities( + self, related: Any, semantic: SaveSemantic = SaveSemantic.REPLACE + ) -> RelatedEntity: + """Build a reference for the *assigned_entities* end (term → assets).""" + return _build_generic_related( + related, self.type_name, self._attrs_dict(), semantic + ) + + def meanings( + self, related: Any, semantic: SaveSemantic = SaveSemantic.REPLACE + ) -> RelatedEntity: + """Build a reference for the *meanings* end (asset → terms).""" + from pyatlan_v9.model.assets.gtc_related import RelatedAtlasGlossaryTerm + + return _build_related( + RelatedAtlasGlossaryTerm, + related, + self.type_name, + self._attrs_dict(), + semantic, + ) + + def assigned_terms( + self, related: Any, semantic: SaveSemantic = SaveSemantic.REPLACE + ) -> RelatedEntity: + """Build a reference for the *assigned_terms* end.""" + return _build_generic_related( + related, self.type_name, self._attrs_dict(), semantic + ) + + +# --------------------------------------------------------------------------- +# UserDefRelationship +# --------------------------------------------------------------------------- + + +class UserDefRelationship(RelationshipAttributes): + """Relationship: user-defined (generic) relationship between any assets.""" + + type_name = "UserDefRelationship" + + def __init__( + self, + from_type_label: Optional[str] = None, + to_type_label: Optional[str] = None, + ): + self.from_type_label = from_type_label + self.to_type_label = to_type_label + + def _attrs_dict(self) -> dict[str, Any]: + d: dict[str, Any] = {} + if self.to_type_label is not None: + d["toTypeLabel"] = self.to_type_label + if self.from_type_label is not None: + d["fromTypeLabel"] = self.from_type_label + return d + + def user_def_relationship_to( + self, related: Any, semantic: SaveSemantic = SaveSemantic.REPLACE + ) -> RelatedEntity: + return _build_generic_related( + related, self.type_name, self._attrs_dict(), semantic + ) + + def user_def_relationship_from( + self, related: Any, semantic: SaveSemantic = SaveSemantic.REPLACE + ) -> RelatedEntity: + return _build_generic_related( + related, self.type_name, self._attrs_dict(), semantic + ) + + +# --------------------------------------------------------------------------- +# CustomRelatedFromEntitiesCustomRelatedToEntities +# --------------------------------------------------------------------------- + + +class CustomRelatedFromEntitiesCustomRelatedToEntities(RelationshipAttributes): + """Relationship: custom inter-entity relationship between custom assets.""" + + type_name = "custom_related_from_entities_custom_related_to_entities" + + def __init__( + self, + custom_entity_to_label: Optional[str] = None, + custom_entity_from_label: Optional[str] = None, + ): + self.custom_entity_to_label = custom_entity_to_label + self.custom_entity_from_label = custom_entity_from_label + + def _attrs_dict(self) -> dict[str, Any]: + d: dict[str, Any] = {} + if self.custom_entity_to_label is not None: + d["customEntityToLabel"] = self.custom_entity_to_label + if self.custom_entity_from_label is not None: + d["customEntityFromLabel"] = self.custom_entity_from_label + return d + + def custom_related_to_entities( + self, related: Any, semantic: SaveSemantic = SaveSemantic.REPLACE + ) -> RelatedEntity: + return _build_generic_related( + related, self.type_name, self._attrs_dict(), semantic + ) + + def custom_related_from_entities( + self, related: Any, semantic: SaveSemantic = SaveSemantic.REPLACE + ) -> RelatedEntity: + return _build_generic_related( + related, self.type_name, self._attrs_dict(), semantic + ) diff --git a/pyatlan_v9/model/assets/resource.py b/pyatlan_v9/model/assets/resource.py new file mode 100644 index 000000000..ad0c8c847 --- /dev/null +++ b/pyatlan_v9/model/assets/resource.py @@ -0,0 +1,569 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Resource asset model with flattened inheritance. + +This module provides: +- Resource: Flat asset class (easy to use) +- ResourceAttributes: Nested attributes struct (extends AssetAttributes) +- ResourceNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .resource_related import RelatedFile, RelatedLink, RelatedReadme + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Resource(Asset): + """ + Base class for resources. + """ + + LINK: ClassVar[Any] = None + IS_GLOBAL: ClassVar[Any] = None + REFERENCE: ClassVar[Any] = None + RESOURCE_METADATA: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Resource" + + link: Union[str, None, UnsetType] = UNSET + """URL to the resource.""" + + is_global: Union[bool, None, UnsetType] = UNSET + """Whether the resource is global (true) or not (false).""" + + reference: Union[str, None, UnsetType] = UNSET + """Reference to the resource.""" + + resource_metadata: Union[Dict[str, str], None, UnsetType] = UNSET + """Metadata of the resource.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Resource" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _resource_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Resource: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Resource instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _resource_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class ResourceAttributes(AssetAttributes): + """Resource-specific attributes for nested API format.""" + + link: Union[str, None, UnsetType] = UNSET + """URL to the resource.""" + + is_global: Union[bool, None, UnsetType] = UNSET + """Whether the resource is global (true) or not (false).""" + + reference: Union[str, None, UnsetType] = UNSET + """Reference to the resource.""" + + resource_metadata: Union[Dict[str, str], None, UnsetType] = UNSET + """Metadata of the resource.""" + + +class ResourceRelationshipAttributes(AssetRelationshipAttributes): + """Resource-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class ResourceNested(AssetNested): + """Resource in nested API format for high-performance serialization.""" + + attributes: Union[ResourceAttributes, UnsetType] = UNSET + relationship_attributes: Union[ResourceRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ResourceRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[ResourceRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_RESOURCE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_resource_attrs(attrs: ResourceAttributes, obj: Resource) -> None: + """Populate Resource-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.link = obj.link + attrs.is_global = obj.is_global + attrs.reference = obj.reference + attrs.resource_metadata = obj.resource_metadata + + +def _extract_resource_attrs(attrs: ResourceAttributes) -> dict: + """Extract all Resource attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["link"] = attrs.link + result["is_global"] = attrs.is_global + result["reference"] = attrs.reference + result["resource_metadata"] = attrs.resource_metadata + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _resource_to_nested(resource: Resource) -> ResourceNested: + """Convert flat Resource to nested format.""" + attrs = ResourceAttributes() + _populate_resource_attrs(attrs, resource) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + resource, _RESOURCE_REL_FIELDS, ResourceRelationshipAttributes + ) + return ResourceNested( + guid=resource.guid, + type_name=resource.type_name, + status=resource.status, + version=resource.version, + create_time=resource.create_time, + update_time=resource.update_time, + created_by=resource.created_by, + updated_by=resource.updated_by, + classifications=resource.classifications, + classification_names=resource.classification_names, + meanings=resource.meanings, + labels=resource.labels, + business_attributes=resource.business_attributes, + custom_attributes=resource.custom_attributes, + pending_tasks=resource.pending_tasks, + proxy=resource.proxy, + is_incomplete=resource.is_incomplete, + provenance_type=resource.provenance_type, + home_id=resource.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _resource_from_nested(nested: ResourceNested) -> Resource: + """Convert nested format to flat Resource.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else ResourceAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _RESOURCE_REL_FIELDS, + ResourceRelationshipAttributes, + ) + return Resource( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_resource_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _resource_to_nested_bytes(resource: Resource, serde: Serde) -> bytes: + """Convert flat Resource to nested JSON bytes.""" + return serde.encode(_resource_to_nested(resource)) + + +def _resource_from_nested_bytes(data: bytes, serde: Serde) -> Resource: + """Convert nested JSON bytes to flat Resource.""" + nested = serde.decode(data, ResourceNested) + return _resource_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + RelationField, +) + +Resource.LINK = KeywordField("link", "link") +Resource.IS_GLOBAL = BooleanField("isGlobal", "isGlobal") +Resource.REFERENCE = KeywordField("reference", "reference") +Resource.RESOURCE_METADATA = KeywordField("resourceMetadata", "resourceMetadata") +Resource.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Resource.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Resource.ANOMALO_CHECKS = RelationField("anomaloChecks") +Resource.APPLICATION = RelationField("application") +Resource.APPLICATION_FIELD = RelationField("applicationField") +Resource.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Resource.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Resource.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Resource.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Resource.METRICS = RelationField("metrics") +Resource.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Resource.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Resource.MEANINGS = RelationField("meanings") +Resource.MC_MONITORS = RelationField("mcMonitors") +Resource.MC_INCIDENTS = RelationField("mcIncidents") +Resource.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Resource.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Resource.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Resource.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Resource.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Resource.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Resource.FILES = RelationField("files") +Resource.LINKS = RelationField("links") +Resource.README = RelationField("readme") +Resource.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Resource.SODA_CHECKS = RelationField("sodaChecks") +Resource.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Resource.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/resource_related.py b/pyatlan_v9/model/assets/resource_related.py new file mode 100644 index 000000000..ba0f5174c --- /dev/null +++ b/pyatlan_v9/model/assets/resource_related.py @@ -0,0 +1,170 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Resource module. + +This module contains all Related{Type} classes for the Resource type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Any, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedCatalog +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedResource", + "Related__internal", + "RelatedBadge", + "RelatedFile", + "RelatedLink", + "RelatedReadme", + "RelatedReadmeTemplate", +] + + +class RelatedResource(RelatedCatalog): + """ + Related entity reference for Resource assets. + + Extends RelatedCatalog with Resource-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Resource" so it serializes correctly + + link: Union[str, None, UnsetType] = UNSET + """URL to the resource.""" + + is_global: Union[bool, None, UnsetType] = UNSET + """Whether the resource is global (true) or not (false).""" + + reference: Union[str, None, UnsetType] = UNSET + """Reference to the resource.""" + + resource_metadata: Union[Dict[str, str], None, UnsetType] = UNSET + """Metadata of the resource.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Resource" + + +class Related__internal(RelatedResource): + """ + Related entity reference for __internal assets. + + Extends RelatedResource with __internal-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "__internal" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "__internal" + + +class RelatedBadge(RelatedResource): + """ + Related entity reference for Badge assets. + + Extends RelatedResource with Badge-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Badge" so it serializes correctly + + badge_conditions: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of conditions that determine the colors to diplay for various values.""" + + badge_metadata_attribute: Union[str, None, UnsetType] = UNSET + """Custom metadata attribute for which to show the badge.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Badge" + + +class RelatedFile(RelatedResource): + """ + Related entity reference for File assets. + + Extends RelatedResource with File-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "File" so it serializes correctly + + file_type: Union[str, None, UnsetType] = UNSET + """Type (extension) of the file.""" + + file_path: Union[str, None, UnsetType] = UNSET + """URL giving the online location where the file can be accessed.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "File" + + +class RelatedLink(RelatedResource): + """ + Related entity reference for Link assets. + + Extends RelatedResource with Link-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Link" so it serializes correctly + + icon: Union[str, None, UnsetType] = UNSET + """Icon for the link.""" + + icon_type: Union[str, None, UnsetType] = UNSET + """Type of icon for the link, for example: image or emoji.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Link" + + +class RelatedReadme(RelatedResource): + """ + Related entity reference for Readme assets. + + Extends RelatedResource with Readme-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Readme" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Readme" + + +class RelatedReadmeTemplate(RelatedResource): + """ + Related entity reference for ReadmeTemplate assets. + + Extends RelatedResource with ReadmeTemplate-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "ReadmeTemplate" so it serializes correctly + + icon: Union[str, None, UnsetType] = UNSET + """Icon to use for the README template.""" + + icon_type: Union[str, None, UnsetType] = UNSET + """Type of icon, for example: image or emoji.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "ReadmeTemplate" diff --git a/pyatlan_v9/model/assets/s3.py b/pyatlan_v9/model/assets/s3.py new file mode 100644 index 000000000..7abc28d6c --- /dev/null +++ b/pyatlan_v9/model/assets/s3.py @@ -0,0 +1,669 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +S3 asset model with flattened inheritance. + +This module provides: +- S3: Flat asset class (easy to use) +- S3Attributes: Nested attributes struct (extends AssetAttributes) +- S3Nested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class S3(Asset): + """ + Base class for S3 assets. + """ + + S3_ETAG: ClassVar[Any] = None + S3_ENCRYPTION: ClassVar[Any] = None + S3_PARENT_PREFIX_QUALIFIED_NAME: ClassVar[Any] = None + S3_PREFIX_HIERARCHY: ClassVar[Any] = None + AWS_ARN: ClassVar[Any] = None + AWS_PARTITION: ClassVar[Any] = None + AWS_SERVICE: ClassVar[Any] = None + AWS_REGION: ClassVar[Any] = None + AWS_ACCOUNT_ID: ClassVar[Any] = None + AWS_RESOURCE_ID: ClassVar[Any] = None + AWS_OWNER_NAME: ClassVar[Any] = None + AWS_OWNER_ID: ClassVar[Any] = None + AWS_TAGS: ClassVar[Any] = None + CLOUD_UNIFORM_RESOURCE_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "S3" + + s3_etag: Union[str, None, UnsetType] = msgspec.field(default=UNSET, name="s3ETag") + """Entity tag for the asset. An entity tag is a hash of the object and represents changes to the contents of an object only, not its metadata.""" + + s3_encryption: Union[str, None, UnsetType] = UNSET + """""" + + s3_parent_prefix_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the immediate parent prefix in which this asset exists.""" + + s3_prefix_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Ordered array of prefix assets with qualified name and name representing the complete prefix hierarchy path for this asset, from immediate parent to root prefix.""" + + aws_arn: Union[str, None, UnsetType] = UNSET + """DEPRECATED: This legacy attribute must be unique across all AWS asset instances. This can create non-obvious edge cases for creating / updating assets, and we therefore recommended NOT using it. See and use cloudResourceName instead.""" + + aws_partition: Union[str, None, UnsetType] = UNSET + """Group of AWS region and service objects.""" + + aws_service: Union[str, None, UnsetType] = UNSET + """Type of service in which the asset exists.""" + + aws_region: Union[str, None, UnsetType] = UNSET + """Physical region where the data center in which the asset exists is clustered.""" + + aws_account_id: Union[str, None, UnsetType] = UNSET + """12-digit number that uniquely identifies an AWS account.""" + + aws_resource_id: Union[str, None, UnsetType] = UNSET + """Unique resource ID assigned when a new resource is created.""" + + aws_owner_name: Union[str, None, UnsetType] = UNSET + """Root user's name.""" + + aws_owner_id: Union[str, None, UnsetType] = UNSET + """Root user's ID.""" + + aws_tags: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of tags that have been applied to the asset in AWS.""" + + cloud_uniform_resource_name: Union[str, None, UnsetType] = UNSET + """Uniform resource name (URN) for the asset: AWS ARN, Google Cloud URI, Azure resource ID, Oracle OCID, and so on.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "S3" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _s3_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> S3: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + S3 instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _s3_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class S3Attributes(AssetAttributes): + """S3-specific attributes for nested API format.""" + + s3_etag: Union[str, None, UnsetType] = msgspec.field(default=UNSET, name="s3ETag") + """Entity tag for the asset. An entity tag is a hash of the object and represents changes to the contents of an object only, not its metadata.""" + + s3_encryption: Union[str, None, UnsetType] = UNSET + """""" + + s3_parent_prefix_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the immediate parent prefix in which this asset exists.""" + + s3_prefix_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Ordered array of prefix assets with qualified name and name representing the complete prefix hierarchy path for this asset, from immediate parent to root prefix.""" + + aws_arn: Union[str, None, UnsetType] = UNSET + """DEPRECATED: This legacy attribute must be unique across all AWS asset instances. This can create non-obvious edge cases for creating / updating assets, and we therefore recommended NOT using it. See and use cloudResourceName instead.""" + + aws_partition: Union[str, None, UnsetType] = UNSET + """Group of AWS region and service objects.""" + + aws_service: Union[str, None, UnsetType] = UNSET + """Type of service in which the asset exists.""" + + aws_region: Union[str, None, UnsetType] = UNSET + """Physical region where the data center in which the asset exists is clustered.""" + + aws_account_id: Union[str, None, UnsetType] = UNSET + """12-digit number that uniquely identifies an AWS account.""" + + aws_resource_id: Union[str, None, UnsetType] = UNSET + """Unique resource ID assigned when a new resource is created.""" + + aws_owner_name: Union[str, None, UnsetType] = UNSET + """Root user's name.""" + + aws_owner_id: Union[str, None, UnsetType] = UNSET + """Root user's ID.""" + + aws_tags: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of tags that have been applied to the asset in AWS.""" + + cloud_uniform_resource_name: Union[str, None, UnsetType] = UNSET + """Uniform resource name (URN) for the asset: AWS ARN, Google Cloud URI, Azure resource ID, Oracle OCID, and so on.""" + + +class S3RelationshipAttributes(AssetRelationshipAttributes): + """S3-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class S3Nested(AssetNested): + """S3 in nested API format for high-performance serialization.""" + + attributes: Union[S3Attributes, UnsetType] = UNSET + relationship_attributes: Union[S3RelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[S3RelationshipAttributes, UnsetType] = UNSET + remove_relationship_attributes: Union[S3RelationshipAttributes, UnsetType] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_S3_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_s3_attrs(attrs: S3Attributes, obj: S3) -> None: + """Populate S3-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.s3_etag = obj.s3_etag + attrs.s3_encryption = obj.s3_encryption + attrs.s3_parent_prefix_qualified_name = obj.s3_parent_prefix_qualified_name + attrs.s3_prefix_hierarchy = obj.s3_prefix_hierarchy + attrs.aws_arn = obj.aws_arn + attrs.aws_partition = obj.aws_partition + attrs.aws_service = obj.aws_service + attrs.aws_region = obj.aws_region + attrs.aws_account_id = obj.aws_account_id + attrs.aws_resource_id = obj.aws_resource_id + attrs.aws_owner_name = obj.aws_owner_name + attrs.aws_owner_id = obj.aws_owner_id + attrs.aws_tags = obj.aws_tags + attrs.cloud_uniform_resource_name = obj.cloud_uniform_resource_name + + +def _extract_s3_attrs(attrs: S3Attributes) -> dict: + """Extract all S3 attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["s3_etag"] = attrs.s3_etag + result["s3_encryption"] = attrs.s3_encryption + result["s3_parent_prefix_qualified_name"] = attrs.s3_parent_prefix_qualified_name + result["s3_prefix_hierarchy"] = attrs.s3_prefix_hierarchy + result["aws_arn"] = attrs.aws_arn + result["aws_partition"] = attrs.aws_partition + result["aws_service"] = attrs.aws_service + result["aws_region"] = attrs.aws_region + result["aws_account_id"] = attrs.aws_account_id + result["aws_resource_id"] = attrs.aws_resource_id + result["aws_owner_name"] = attrs.aws_owner_name + result["aws_owner_id"] = attrs.aws_owner_id + result["aws_tags"] = attrs.aws_tags + result["cloud_uniform_resource_name"] = attrs.cloud_uniform_resource_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _s3_to_nested(s3: S3) -> S3Nested: + """Convert flat S3 to nested format.""" + attrs = S3Attributes() + _populate_s3_attrs(attrs, s3) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + s3, _S3_REL_FIELDS, S3RelationshipAttributes + ) + return S3Nested( + guid=s3.guid, + type_name=s3.type_name, + status=s3.status, + version=s3.version, + create_time=s3.create_time, + update_time=s3.update_time, + created_by=s3.created_by, + updated_by=s3.updated_by, + classifications=s3.classifications, + classification_names=s3.classification_names, + meanings=s3.meanings, + labels=s3.labels, + business_attributes=s3.business_attributes, + custom_attributes=s3.custom_attributes, + pending_tasks=s3.pending_tasks, + proxy=s3.proxy, + is_incomplete=s3.is_incomplete, + provenance_type=s3.provenance_type, + home_id=s3.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _s3_from_nested(nested: S3Nested) -> S3: + """Convert nested format to flat S3.""" + attrs = nested.attributes if nested.attributes is not UNSET else S3Attributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _S3_REL_FIELDS, + S3RelationshipAttributes, + ) + return S3( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_s3_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _s3_to_nested_bytes(s3: S3, serde: Serde) -> bytes: + """Convert flat S3 to nested JSON bytes.""" + return serde.encode(_s3_to_nested(s3)) + + +def _s3_from_nested_bytes(data: bytes, serde: Serde) -> S3: + """Convert nested JSON bytes to flat S3.""" + nested = serde.decode(data, S3Nested) + return _s3_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + RelationField, +) + +S3.S3_ETAG = KeywordTextField("s3ETag", "s3ETag", "s3ETag.text") +S3.S3_ENCRYPTION = KeywordField("s3Encryption", "s3Encryption") +S3.S3_PARENT_PREFIX_QUALIFIED_NAME = KeywordField( + "s3ParentPrefixQualifiedName", "s3ParentPrefixQualifiedName" +) +S3.S3_PREFIX_HIERARCHY = KeywordField("s3PrefixHierarchy", "s3PrefixHierarchy") +S3.AWS_ARN = KeywordTextField("awsArn", "awsArn", "awsArn.text") +S3.AWS_PARTITION = KeywordField("awsPartition", "awsPartition") +S3.AWS_SERVICE = KeywordField("awsService", "awsService") +S3.AWS_REGION = KeywordField("awsRegion", "awsRegion") +S3.AWS_ACCOUNT_ID = KeywordField("awsAccountId", "awsAccountId") +S3.AWS_RESOURCE_ID = KeywordField("awsResourceId", "awsResourceId") +S3.AWS_OWNER_NAME = KeywordTextField( + "awsOwnerName", "awsOwnerName", "awsOwnerName.text" +) +S3.AWS_OWNER_ID = KeywordField("awsOwnerId", "awsOwnerId") +S3.AWS_TAGS = KeywordField("awsTags", "awsTags") +S3.CLOUD_UNIFORM_RESOURCE_NAME = KeywordField( + "cloudUniformResourceName", "cloudUniformResourceName" +) +S3.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +S3.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +S3.ANOMALO_CHECKS = RelationField("anomaloChecks") +S3.APPLICATION = RelationField("application") +S3.APPLICATION_FIELD = RelationField("applicationField") +S3.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +S3.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +S3.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +S3.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +S3.METRICS = RelationField("metrics") +S3.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +S3.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +S3.MEANINGS = RelationField("meanings") +S3.MC_MONITORS = RelationField("mcMonitors") +S3.MC_INCIDENTS = RelationField("mcIncidents") +S3.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +S3.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +S3.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +S3.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +S3.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +S3.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +S3.FILES = RelationField("files") +S3.LINKS = RelationField("links") +S3.README = RelationField("readme") +S3.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +S3.SODA_CHECKS = RelationField("sodaChecks") +S3.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +S3.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/s3_bucket.py b/pyatlan_v9/model/assets/s3_bucket.py new file mode 100644 index 000000000..684e30dd1 --- /dev/null +++ b/pyatlan_v9/model/assets/s3_bucket.py @@ -0,0 +1,774 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +S3Bucket asset model with flattened inheritance. + +This module provides: +- S3Bucket: Flat asset class (easy to use) +- S3BucketAttributes: Nested attributes struct (extends AssetAttributes) +- S3BucketNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid + +from .s3_related import RelatedS3Object, RelatedS3Prefix + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class S3Bucket(Asset): + """ + Instance of an S3 bucket in Atlan. + """ + + S3_OBJECT_COUNT: ClassVar[Any] = None + S3_BUCKET_VERSIONING_ENABLED: ClassVar[Any] = None + S3_ETAG: ClassVar[Any] = None + S3_ENCRYPTION: ClassVar[Any] = None + S3_PARENT_PREFIX_QUALIFIED_NAME: ClassVar[Any] = None + S3_PREFIX_HIERARCHY: ClassVar[Any] = None + AWS_ARN: ClassVar[Any] = None + AWS_PARTITION: ClassVar[Any] = None + AWS_SERVICE: ClassVar[Any] = None + AWS_REGION: ClassVar[Any] = None + AWS_ACCOUNT_ID: ClassVar[Any] = None + AWS_RESOURCE_ID: ClassVar[Any] = None + AWS_OWNER_NAME: ClassVar[Any] = None + AWS_OWNER_ID: ClassVar[Any] = None + AWS_TAGS: ClassVar[Any] = None + CLOUD_UNIFORM_RESOURCE_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + OBJECTS: ClassVar[Any] = None + S3_PREFIXES: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "S3Bucket" + + s3_object_count: Union[int, None, UnsetType] = UNSET + """Number of objects within the bucket.""" + + s3_bucket_versioning_enabled: Union[bool, None, UnsetType] = UNSET + """Whether versioning is enabled for the bucket (true) or not (false).""" + + s3_etag: Union[str, None, UnsetType] = msgspec.field(default=UNSET, name="s3ETag") + """Entity tag for the asset. An entity tag is a hash of the object and represents changes to the contents of an object only, not its metadata.""" + + s3_encryption: Union[str, None, UnsetType] = UNSET + """""" + + s3_parent_prefix_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the immediate parent prefix in which this asset exists.""" + + s3_prefix_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Ordered array of prefix assets with qualified name and name representing the complete prefix hierarchy path for this asset, from immediate parent to root prefix.""" + + aws_arn: Union[str, None, UnsetType] = UNSET + """DEPRECATED: This legacy attribute must be unique across all AWS asset instances. This can create non-obvious edge cases for creating / updating assets, and we therefore recommended NOT using it. See and use cloudResourceName instead.""" + + aws_partition: Union[str, None, UnsetType] = UNSET + """Group of AWS region and service objects.""" + + aws_service: Union[str, None, UnsetType] = UNSET + """Type of service in which the asset exists.""" + + aws_region: Union[str, None, UnsetType] = UNSET + """Physical region where the data center in which the asset exists is clustered.""" + + aws_account_id: Union[str, None, UnsetType] = UNSET + """12-digit number that uniquely identifies an AWS account.""" + + aws_resource_id: Union[str, None, UnsetType] = UNSET + """Unique resource ID assigned when a new resource is created.""" + + aws_owner_name: Union[str, None, UnsetType] = UNSET + """Root user's name.""" + + aws_owner_id: Union[str, None, UnsetType] = UNSET + """Root user's ID.""" + + aws_tags: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of tags that have been applied to the asset in AWS.""" + + cloud_uniform_resource_name: Union[str, None, UnsetType] = UNSET + """Uniform resource name (URN) for the asset: AWS ARN, Google Cloud URI, Azure resource ID, Oracle OCID, and so on.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + objects: Union[List[RelatedS3Object], None, UnsetType] = UNSET + """S3 objects within this bucket.""" + + s3_prefixes: Union[List[RelatedS3Prefix], None, UnsetType] = UNSET + """S3 prefixes contained in this bucket.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "S3Bucket" + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + connection_qualified_name: str, + aws_arn: str | None = None, + ) -> "S3Bucket": + """ + Create a new S3Bucket asset. + + Args: + name: Name of the bucket + connection_qualified_name: Unique name of the connection in which this bucket exists + aws_arn: Amazon Resource Name (ARN) for the bucket (optional) + + Returns: + S3Bucket instance ready to be created + + Raises: + ValueError: If required parameters are missing or invalid + """ + if name is None: + raise ValueError("name is required") + if connection_qualified_name is None: + raise ValueError("connection_qualified_name is required") + + if name.strip() == "": + raise ValueError("name cannot be blank") + if connection_qualified_name.strip() == "": + raise ValueError("connection_qualified_name cannot be blank") + + fields = connection_qualified_name.split("/") + if len(fields) != 3: + raise ValueError("Invalid connection_qualified_name") + + if fields[0].replace(" ", "") == "" or fields[2].replace(" ", "") == "": + raise ValueError("Invalid connection_qualified_name") + + if fields[1].lower() != "s3": + raise ValueError("Invalid connection_qualified_name") + + connector_name = fields[1] + qualified_name = f"{connection_qualified_name}/{aws_arn if aws_arn else name}" + + return cls( + name=name, + qualified_name=qualified_name, + connection_qualified_name=connection_qualified_name, + connector_name=connector_name, + aws_arn=aws_arn, + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _s3_bucket_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> S3Bucket: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + S3Bucket instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _s3_bucket_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class S3BucketAttributes(AssetAttributes): + """S3Bucket-specific attributes for nested API format.""" + + s3_object_count: Union[int, None, UnsetType] = UNSET + """Number of objects within the bucket.""" + + s3_bucket_versioning_enabled: Union[bool, None, UnsetType] = UNSET + """Whether versioning is enabled for the bucket (true) or not (false).""" + + s3_etag: Union[str, None, UnsetType] = msgspec.field(default=UNSET, name="s3ETag") + """Entity tag for the asset. An entity tag is a hash of the object and represents changes to the contents of an object only, not its metadata.""" + + s3_encryption: Union[str, None, UnsetType] = UNSET + """""" + + s3_parent_prefix_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the immediate parent prefix in which this asset exists.""" + + s3_prefix_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Ordered array of prefix assets with qualified name and name representing the complete prefix hierarchy path for this asset, from immediate parent to root prefix.""" + + aws_arn: Union[str, None, UnsetType] = UNSET + """DEPRECATED: This legacy attribute must be unique across all AWS asset instances. This can create non-obvious edge cases for creating / updating assets, and we therefore recommended NOT using it. See and use cloudResourceName instead.""" + + aws_partition: Union[str, None, UnsetType] = UNSET + """Group of AWS region and service objects.""" + + aws_service: Union[str, None, UnsetType] = UNSET + """Type of service in which the asset exists.""" + + aws_region: Union[str, None, UnsetType] = UNSET + """Physical region where the data center in which the asset exists is clustered.""" + + aws_account_id: Union[str, None, UnsetType] = UNSET + """12-digit number that uniquely identifies an AWS account.""" + + aws_resource_id: Union[str, None, UnsetType] = UNSET + """Unique resource ID assigned when a new resource is created.""" + + aws_owner_name: Union[str, None, UnsetType] = UNSET + """Root user's name.""" + + aws_owner_id: Union[str, None, UnsetType] = UNSET + """Root user's ID.""" + + aws_tags: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of tags that have been applied to the asset in AWS.""" + + cloud_uniform_resource_name: Union[str, None, UnsetType] = UNSET + """Uniform resource name (URN) for the asset: AWS ARN, Google Cloud URI, Azure resource ID, Oracle OCID, and so on.""" + + +class S3BucketRelationshipAttributes(AssetRelationshipAttributes): + """S3Bucket-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + objects: Union[List[RelatedS3Object], None, UnsetType] = UNSET + """S3 objects within this bucket.""" + + s3_prefixes: Union[List[RelatedS3Prefix], None, UnsetType] = UNSET + """S3 prefixes contained in this bucket.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class S3BucketNested(AssetNested): + """S3Bucket in nested API format for high-performance serialization.""" + + attributes: Union[S3BucketAttributes, UnsetType] = UNSET + relationship_attributes: Union[S3BucketRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[S3BucketRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[S3BucketRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_S3_BUCKET_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "objects", + "s3_prefixes", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_s3_bucket_attrs(attrs: S3BucketAttributes, obj: S3Bucket) -> None: + """Populate S3Bucket-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.s3_object_count = obj.s3_object_count + attrs.s3_bucket_versioning_enabled = obj.s3_bucket_versioning_enabled + attrs.s3_etag = obj.s3_etag + attrs.s3_encryption = obj.s3_encryption + attrs.s3_parent_prefix_qualified_name = obj.s3_parent_prefix_qualified_name + attrs.s3_prefix_hierarchy = obj.s3_prefix_hierarchy + attrs.aws_arn = obj.aws_arn + attrs.aws_partition = obj.aws_partition + attrs.aws_service = obj.aws_service + attrs.aws_region = obj.aws_region + attrs.aws_account_id = obj.aws_account_id + attrs.aws_resource_id = obj.aws_resource_id + attrs.aws_owner_name = obj.aws_owner_name + attrs.aws_owner_id = obj.aws_owner_id + attrs.aws_tags = obj.aws_tags + attrs.cloud_uniform_resource_name = obj.cloud_uniform_resource_name + + +def _extract_s3_bucket_attrs(attrs: S3BucketAttributes) -> dict: + """Extract all S3Bucket attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["s3_object_count"] = attrs.s3_object_count + result["s3_bucket_versioning_enabled"] = attrs.s3_bucket_versioning_enabled + result["s3_etag"] = attrs.s3_etag + result["s3_encryption"] = attrs.s3_encryption + result["s3_parent_prefix_qualified_name"] = attrs.s3_parent_prefix_qualified_name + result["s3_prefix_hierarchy"] = attrs.s3_prefix_hierarchy + result["aws_arn"] = attrs.aws_arn + result["aws_partition"] = attrs.aws_partition + result["aws_service"] = attrs.aws_service + result["aws_region"] = attrs.aws_region + result["aws_account_id"] = attrs.aws_account_id + result["aws_resource_id"] = attrs.aws_resource_id + result["aws_owner_name"] = attrs.aws_owner_name + result["aws_owner_id"] = attrs.aws_owner_id + result["aws_tags"] = attrs.aws_tags + result["cloud_uniform_resource_name"] = attrs.cloud_uniform_resource_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _s3_bucket_to_nested(s3_bucket: S3Bucket) -> S3BucketNested: + """Convert flat S3Bucket to nested format.""" + attrs = S3BucketAttributes() + _populate_s3_bucket_attrs(attrs, s3_bucket) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + s3_bucket, _S3_BUCKET_REL_FIELDS, S3BucketRelationshipAttributes + ) + return S3BucketNested( + guid=s3_bucket.guid, + type_name=s3_bucket.type_name, + status=s3_bucket.status, + version=s3_bucket.version, + create_time=s3_bucket.create_time, + update_time=s3_bucket.update_time, + created_by=s3_bucket.created_by, + updated_by=s3_bucket.updated_by, + classifications=s3_bucket.classifications, + classification_names=s3_bucket.classification_names, + meanings=s3_bucket.meanings, + labels=s3_bucket.labels, + business_attributes=s3_bucket.business_attributes, + custom_attributes=s3_bucket.custom_attributes, + pending_tasks=s3_bucket.pending_tasks, + proxy=s3_bucket.proxy, + is_incomplete=s3_bucket.is_incomplete, + provenance_type=s3_bucket.provenance_type, + home_id=s3_bucket.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _s3_bucket_from_nested(nested: S3BucketNested) -> S3Bucket: + """Convert nested format to flat S3Bucket.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else S3BucketAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _S3_BUCKET_REL_FIELDS, + S3BucketRelationshipAttributes, + ) + return S3Bucket( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_s3_bucket_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _s3_bucket_to_nested_bytes(s3_bucket: S3Bucket, serde: Serde) -> bytes: + """Convert flat S3Bucket to nested JSON bytes.""" + return serde.encode(_s3_bucket_to_nested(s3_bucket)) + + +def _s3_bucket_from_nested_bytes(data: bytes, serde: Serde) -> S3Bucket: + """Convert nested JSON bytes to flat S3Bucket.""" + nested = serde.decode(data, S3BucketNested) + return _s3_bucket_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +S3Bucket.S3_OBJECT_COUNT = NumericField("s3ObjectCount", "s3ObjectCount") +S3Bucket.S3_BUCKET_VERSIONING_ENABLED = BooleanField( + "s3BucketVersioningEnabled", "s3BucketVersioningEnabled" +) +S3Bucket.S3_ETAG = KeywordTextField("s3ETag", "s3ETag", "s3ETag.text") +S3Bucket.S3_ENCRYPTION = KeywordField("s3Encryption", "s3Encryption") +S3Bucket.S3_PARENT_PREFIX_QUALIFIED_NAME = KeywordField( + "s3ParentPrefixQualifiedName", "s3ParentPrefixQualifiedName" +) +S3Bucket.S3_PREFIX_HIERARCHY = KeywordField("s3PrefixHierarchy", "s3PrefixHierarchy") +S3Bucket.AWS_ARN = KeywordTextField("awsArn", "awsArn", "awsArn.text") +S3Bucket.AWS_PARTITION = KeywordField("awsPartition", "awsPartition") +S3Bucket.AWS_SERVICE = KeywordField("awsService", "awsService") +S3Bucket.AWS_REGION = KeywordField("awsRegion", "awsRegion") +S3Bucket.AWS_ACCOUNT_ID = KeywordField("awsAccountId", "awsAccountId") +S3Bucket.AWS_RESOURCE_ID = KeywordField("awsResourceId", "awsResourceId") +S3Bucket.AWS_OWNER_NAME = KeywordTextField( + "awsOwnerName", "awsOwnerName", "awsOwnerName.text" +) +S3Bucket.AWS_OWNER_ID = KeywordField("awsOwnerId", "awsOwnerId") +S3Bucket.AWS_TAGS = KeywordField("awsTags", "awsTags") +S3Bucket.CLOUD_UNIFORM_RESOURCE_NAME = KeywordField( + "cloudUniformResourceName", "cloudUniformResourceName" +) +S3Bucket.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +S3Bucket.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +S3Bucket.ANOMALO_CHECKS = RelationField("anomaloChecks") +S3Bucket.APPLICATION = RelationField("application") +S3Bucket.APPLICATION_FIELD = RelationField("applicationField") +S3Bucket.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +S3Bucket.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +S3Bucket.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +S3Bucket.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +S3Bucket.METRICS = RelationField("metrics") +S3Bucket.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +S3Bucket.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +S3Bucket.MEANINGS = RelationField("meanings") +S3Bucket.MC_MONITORS = RelationField("mcMonitors") +S3Bucket.MC_INCIDENTS = RelationField("mcIncidents") +S3Bucket.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +S3Bucket.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +S3Bucket.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +S3Bucket.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +S3Bucket.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +S3Bucket.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +S3Bucket.FILES = RelationField("files") +S3Bucket.LINKS = RelationField("links") +S3Bucket.README = RelationField("readme") +S3Bucket.OBJECTS = RelationField("objects") +S3Bucket.S3_PREFIXES = RelationField("s3Prefixes") +S3Bucket.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +S3Bucket.SODA_CHECKS = RelationField("sodaChecks") +S3Bucket.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +S3Bucket.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/s3_object.py b/pyatlan_v9/model/assets/s3_object.py new file mode 100644 index 000000000..c81afb3cb --- /dev/null +++ b/pyatlan_v9/model/assets/s3_object.py @@ -0,0 +1,996 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +S3Object asset model with flattened inheritance. + +This module provides: +- S3Object: Flat asset class (easy to use) +- S3ObjectAttributes: Nested attributes struct (extends AssetAttributes) +- S3ObjectNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan.model.utils import construct_object_key +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .s3_related import RelatedS3Bucket, RelatedS3Prefix + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class S3Object(Asset): + """ + Instance of an S3 object in Atlan. + """ + + S3_OBJECT_LAST_MODIFIED_TIME: ClassVar[Any] = None + S3_BUCKET_NAME: ClassVar[Any] = None + S3_BUCKET_QUALIFIED_NAME: ClassVar[Any] = None + S3_OBJECT_SIZE: ClassVar[Any] = None + S3_OBJECT_STORAGE_CLASS: ClassVar[Any] = None + S3_OBJECT_KEY: ClassVar[Any] = None + S3_OBJECT_CONTENT_TYPE: ClassVar[Any] = None + S3_OBJECT_CONTENT_DISPOSITION: ClassVar[Any] = None + S3_OBJECT_VERSION_ID: ClassVar[Any] = None + S3_OBJECT_LOCK_RETAIN_UNTIL: ClassVar[Any] = None + S3_OBJECT_LOCK_MODE: ClassVar[Any] = None + S3_OBJECT_LOCK_LEGAL_HOLD_ENABLED: ClassVar[Any] = None + S3_ETAG: ClassVar[Any] = None + S3_ENCRYPTION: ClassVar[Any] = None + S3_PARENT_PREFIX_QUALIFIED_NAME: ClassVar[Any] = None + S3_PREFIX_HIERARCHY: ClassVar[Any] = None + AWS_ARN: ClassVar[Any] = None + AWS_PARTITION: ClassVar[Any] = None + AWS_SERVICE: ClassVar[Any] = None + AWS_REGION: ClassVar[Any] = None + AWS_ACCOUNT_ID: ClassVar[Any] = None + AWS_RESOURCE_ID: ClassVar[Any] = None + AWS_OWNER_NAME: ClassVar[Any] = None + AWS_OWNER_ID: ClassVar[Any] = None + AWS_TAGS: ClassVar[Any] = None + CLOUD_UNIFORM_RESOURCE_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + BUCKET: ClassVar[Any] = None + S3_PREFIX: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "S3Object" + + s3_object_last_modified_time: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this object was last updated, in milliseconds, or when it was created if it has never been modified.""" + + s3_bucket_name: Union[str, None, UnsetType] = UNSET + """Simple name of the bucket in which this object exists.""" + + s3_bucket_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the bucket in which this object exists.""" + + s3_object_size: Union[int, None, UnsetType] = UNSET + """Object size in bytes.""" + + s3_object_storage_class: Union[str, None, UnsetType] = UNSET + """Storage class used for storing this object, for example: standard, intelligent-tiering, glacier, etc.""" + + s3_object_key: Union[str, None, UnsetType] = UNSET + """Unique identity of this object in an S3 bucket. This is usually the concatenation of any prefix (folder) in the S3 bucket with the name of the object (file) itself.""" + + s3_object_content_type: Union[str, None, UnsetType] = UNSET + """Type of content in this object, for example: text/plain, application/json, etc.""" + + s3_object_content_disposition: Union[str, None, UnsetType] = UNSET + """Information about how this object's content should be presented.""" + + s3_object_version_id: Union[str, None, UnsetType] = UNSET + """Version of this object. This is only applicable when versioning is enabled on the bucket in which this object exists.""" + + s3_object_lock_retain_until: Union[int, None, UnsetType] = UNSET + """Time (epoch) when the object lock retention will expire.""" + + s3_object_lock_mode: Union[str, None, UnsetType] = UNSET + """Mode of the object lock retention.""" + + s3_object_lock_legal_hold_enabled: Union[bool, None, UnsetType] = UNSET + """Whether the object lock legal hold is enabled (true) or not (false).""" + + s3_etag: Union[str, None, UnsetType] = msgspec.field(default=UNSET, name="s3ETag") + """Entity tag for the asset. An entity tag is a hash of the object and represents changes to the contents of an object only, not its metadata.""" + + s3_encryption: Union[str, None, UnsetType] = UNSET + """""" + + s3_parent_prefix_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the immediate parent prefix in which this asset exists.""" + + s3_prefix_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Ordered array of prefix assets with qualified name and name representing the complete prefix hierarchy path for this asset, from immediate parent to root prefix.""" + + aws_arn: Union[str, None, UnsetType] = UNSET + """DEPRECATED: This legacy attribute must be unique across all AWS asset instances. This can create non-obvious edge cases for creating / updating assets, and we therefore recommended NOT using it. See and use cloudResourceName instead.""" + + aws_partition: Union[str, None, UnsetType] = UNSET + """Group of AWS region and service objects.""" + + aws_service: Union[str, None, UnsetType] = UNSET + """Type of service in which the asset exists.""" + + aws_region: Union[str, None, UnsetType] = UNSET + """Physical region where the data center in which the asset exists is clustered.""" + + aws_account_id: Union[str, None, UnsetType] = UNSET + """12-digit number that uniquely identifies an AWS account.""" + + aws_resource_id: Union[str, None, UnsetType] = UNSET + """Unique resource ID assigned when a new resource is created.""" + + aws_owner_name: Union[str, None, UnsetType] = UNSET + """Root user's name.""" + + aws_owner_id: Union[str, None, UnsetType] = UNSET + """Root user's ID.""" + + aws_tags: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of tags that have been applied to the asset in AWS.""" + + cloud_uniform_resource_name: Union[str, None, UnsetType] = UNSET + """Uniform resource name (URN) for the asset: AWS ARN, Google Cloud URI, Azure resource ID, Oracle OCID, and so on.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + bucket: Union[RelatedS3Bucket, None, UnsetType] = UNSET + """S3 bucket in which the object exists.""" + + s3_prefix: Union[RelatedS3Prefix, None, UnsetType] = UNSET + """S3 prefix that contains the object.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "S3Object" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + connection_qualified_name: str, + aws_arn: str, + s3_bucket_name: str, + s3_bucket_qualified_name: str, + ) -> "S3Object": + """ + Create a new S3Object asset with an AWS ARN. + + Args: + name: Name of the object + connection_qualified_name: Unique name of the connection + aws_arn: Amazon Resource Name (ARN) for the object + s3_bucket_name: Simple name of the bucket + s3_bucket_qualified_name: Unique name of the bucket + + Returns: + S3Object instance ready to be created + + Raises: + ValueError: If required parameters are missing or invalid + """ + validate_required_fields( + [ + "name", + "connection_qualified_name", + "aws_arn", + "s3_bucket_name", + "s3_bucket_qualified_name", + ], + [ + name, + connection_qualified_name, + aws_arn, + s3_bucket_name, + s3_bucket_qualified_name, + ], + ) + fields = connection_qualified_name.split("/") + if len(fields) != 3: + raise ValueError("Invalid connection_qualified_name") + if fields[0].replace(" ", "") == "" or fields[2].replace(" ", "") == "": + raise ValueError("Invalid connection_qualified_name") + if fields[1].lower() != "s3": + raise ValueError("Invalid connection_qualified_name") + + connector_name = fields[1] + return cls( + name=name, + connection_qualified_name=connection_qualified_name, + qualified_name=f"{connection_qualified_name}/{aws_arn}", + connector_name=connector_name, + aws_arn=aws_arn, + s3_bucket_name=s3_bucket_name, + s3_bucket_qualified_name=s3_bucket_qualified_name, + ) + + @classmethod + @init_guid + def creator_with_prefix( + cls, + *, + name: str, + connection_qualified_name: str, + s3_bucket_name: str, + s3_bucket_qualified_name: str, + prefix: str = "", + ) -> "S3Object": + """ + Create a new S3Object asset using a prefix-based object key. + + Args: + name: Name of the object + connection_qualified_name: Unique name of the connection + s3_bucket_name: Simple name of the bucket + s3_bucket_qualified_name: Unique name of the bucket + prefix: Prefix (folder path) for the object + + Returns: + S3Object instance ready to be created + + Raises: + ValueError: If required parameters are missing or invalid + """ + validate_required_fields( + [ + "name", + "connection_qualified_name", + "s3_bucket_name", + "s3_bucket_qualified_name", + ], + [ + name, + connection_qualified_name, + s3_bucket_name, + s3_bucket_qualified_name, + ], + ) + fields = connection_qualified_name.split("/") + if len(fields) != 3: + raise ValueError("Invalid connection_qualified_name") + if fields[0].replace(" ", "") == "" or fields[2].replace(" ", "") == "": + raise ValueError("Invalid connection_qualified_name") + if fields[1].lower() != "s3": + raise ValueError("Invalid connection_qualified_name") + + connector_name = fields[1] + object_key = construct_object_key(prefix, name) + return cls( + name=name, + s3_object_key=object_key, + connection_qualified_name=connection_qualified_name, + qualified_name=f"{connection_qualified_name}/{s3_bucket_name}/{object_key}", + connector_name=connector_name, + s3_bucket_name=s3_bucket_name, + s3_bucket_qualified_name=s3_bucket_qualified_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "S3Object": + """ + Create an S3Object instance for modification. + + Args: + qualified_name: Unique name of the S3Object to update + name: Human-readable name of the S3Object + + Returns: + S3Object instance ready for update + + Raises: + ValueError: If required parameters are missing + """ + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "S3Object": + """ + Return a copy of this S3Object with only the minimum required fields for update. + + Returns: + S3Object with only qualified_name and name set + """ + return S3Object.updater(qualified_name=self.qualified_name, name=self.name) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _s3_object_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> S3Object: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + S3Object instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _s3_object_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class S3ObjectAttributes(AssetAttributes): + """S3Object-specific attributes for nested API format.""" + + s3_object_last_modified_time: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this object was last updated, in milliseconds, or when it was created if it has never been modified.""" + + s3_bucket_name: Union[str, None, UnsetType] = UNSET + """Simple name of the bucket in which this object exists.""" + + s3_bucket_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the bucket in which this object exists.""" + + s3_object_size: Union[int, None, UnsetType] = UNSET + """Object size in bytes.""" + + s3_object_storage_class: Union[str, None, UnsetType] = UNSET + """Storage class used for storing this object, for example: standard, intelligent-tiering, glacier, etc.""" + + s3_object_key: Union[str, None, UnsetType] = UNSET + """Unique identity of this object in an S3 bucket. This is usually the concatenation of any prefix (folder) in the S3 bucket with the name of the object (file) itself.""" + + s3_object_content_type: Union[str, None, UnsetType] = UNSET + """Type of content in this object, for example: text/plain, application/json, etc.""" + + s3_object_content_disposition: Union[str, None, UnsetType] = UNSET + """Information about how this object's content should be presented.""" + + s3_object_version_id: Union[str, None, UnsetType] = UNSET + """Version of this object. This is only applicable when versioning is enabled on the bucket in which this object exists.""" + + s3_object_lock_retain_until: Union[int, None, UnsetType] = UNSET + """Time (epoch) when the object lock retention will expire.""" + + s3_object_lock_mode: Union[str, None, UnsetType] = UNSET + """Mode of the object lock retention.""" + + s3_object_lock_legal_hold_enabled: Union[bool, None, UnsetType] = UNSET + """Whether the object lock legal hold is enabled (true) or not (false).""" + + s3_etag: Union[str, None, UnsetType] = msgspec.field(default=UNSET, name="s3ETag") + """Entity tag for the asset. An entity tag is a hash of the object and represents changes to the contents of an object only, not its metadata.""" + + s3_encryption: Union[str, None, UnsetType] = UNSET + """""" + + s3_parent_prefix_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the immediate parent prefix in which this asset exists.""" + + s3_prefix_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Ordered array of prefix assets with qualified name and name representing the complete prefix hierarchy path for this asset, from immediate parent to root prefix.""" + + aws_arn: Union[str, None, UnsetType] = UNSET + """DEPRECATED: This legacy attribute must be unique across all AWS asset instances. This can create non-obvious edge cases for creating / updating assets, and we therefore recommended NOT using it. See and use cloudResourceName instead.""" + + aws_partition: Union[str, None, UnsetType] = UNSET + """Group of AWS region and service objects.""" + + aws_service: Union[str, None, UnsetType] = UNSET + """Type of service in which the asset exists.""" + + aws_region: Union[str, None, UnsetType] = UNSET + """Physical region where the data center in which the asset exists is clustered.""" + + aws_account_id: Union[str, None, UnsetType] = UNSET + """12-digit number that uniquely identifies an AWS account.""" + + aws_resource_id: Union[str, None, UnsetType] = UNSET + """Unique resource ID assigned when a new resource is created.""" + + aws_owner_name: Union[str, None, UnsetType] = UNSET + """Root user's name.""" + + aws_owner_id: Union[str, None, UnsetType] = UNSET + """Root user's ID.""" + + aws_tags: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of tags that have been applied to the asset in AWS.""" + + cloud_uniform_resource_name: Union[str, None, UnsetType] = UNSET + """Uniform resource name (URN) for the asset: AWS ARN, Google Cloud URI, Azure resource ID, Oracle OCID, and so on.""" + + +class S3ObjectRelationshipAttributes(AssetRelationshipAttributes): + """S3Object-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + bucket: Union[RelatedS3Bucket, None, UnsetType] = UNSET + """S3 bucket in which the object exists.""" + + s3_prefix: Union[RelatedS3Prefix, None, UnsetType] = UNSET + """S3 prefix that contains the object.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class S3ObjectNested(AssetNested): + """S3Object in nested API format for high-performance serialization.""" + + attributes: Union[S3ObjectAttributes, UnsetType] = UNSET + relationship_attributes: Union[S3ObjectRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[S3ObjectRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[S3ObjectRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_S3_OBJECT_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "bucket", + "s3_prefix", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_s3_object_attrs(attrs: S3ObjectAttributes, obj: S3Object) -> None: + """Populate S3Object-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.s3_object_last_modified_time = obj.s3_object_last_modified_time + attrs.s3_bucket_name = obj.s3_bucket_name + attrs.s3_bucket_qualified_name = obj.s3_bucket_qualified_name + attrs.s3_object_size = obj.s3_object_size + attrs.s3_object_storage_class = obj.s3_object_storage_class + attrs.s3_object_key = obj.s3_object_key + attrs.s3_object_content_type = obj.s3_object_content_type + attrs.s3_object_content_disposition = obj.s3_object_content_disposition + attrs.s3_object_version_id = obj.s3_object_version_id + attrs.s3_object_lock_retain_until = obj.s3_object_lock_retain_until + attrs.s3_object_lock_mode = obj.s3_object_lock_mode + attrs.s3_object_lock_legal_hold_enabled = obj.s3_object_lock_legal_hold_enabled + attrs.s3_etag = obj.s3_etag + attrs.s3_encryption = obj.s3_encryption + attrs.s3_parent_prefix_qualified_name = obj.s3_parent_prefix_qualified_name + attrs.s3_prefix_hierarchy = obj.s3_prefix_hierarchy + attrs.aws_arn = obj.aws_arn + attrs.aws_partition = obj.aws_partition + attrs.aws_service = obj.aws_service + attrs.aws_region = obj.aws_region + attrs.aws_account_id = obj.aws_account_id + attrs.aws_resource_id = obj.aws_resource_id + attrs.aws_owner_name = obj.aws_owner_name + attrs.aws_owner_id = obj.aws_owner_id + attrs.aws_tags = obj.aws_tags + attrs.cloud_uniform_resource_name = obj.cloud_uniform_resource_name + + +def _extract_s3_object_attrs(attrs: S3ObjectAttributes) -> dict: + """Extract all S3Object attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["s3_object_last_modified_time"] = attrs.s3_object_last_modified_time + result["s3_bucket_name"] = attrs.s3_bucket_name + result["s3_bucket_qualified_name"] = attrs.s3_bucket_qualified_name + result["s3_object_size"] = attrs.s3_object_size + result["s3_object_storage_class"] = attrs.s3_object_storage_class + result["s3_object_key"] = attrs.s3_object_key + result["s3_object_content_type"] = attrs.s3_object_content_type + result["s3_object_content_disposition"] = attrs.s3_object_content_disposition + result["s3_object_version_id"] = attrs.s3_object_version_id + result["s3_object_lock_retain_until"] = attrs.s3_object_lock_retain_until + result["s3_object_lock_mode"] = attrs.s3_object_lock_mode + result["s3_object_lock_legal_hold_enabled"] = ( + attrs.s3_object_lock_legal_hold_enabled + ) + result["s3_etag"] = attrs.s3_etag + result["s3_encryption"] = attrs.s3_encryption + result["s3_parent_prefix_qualified_name"] = attrs.s3_parent_prefix_qualified_name + result["s3_prefix_hierarchy"] = attrs.s3_prefix_hierarchy + result["aws_arn"] = attrs.aws_arn + result["aws_partition"] = attrs.aws_partition + result["aws_service"] = attrs.aws_service + result["aws_region"] = attrs.aws_region + result["aws_account_id"] = attrs.aws_account_id + result["aws_resource_id"] = attrs.aws_resource_id + result["aws_owner_name"] = attrs.aws_owner_name + result["aws_owner_id"] = attrs.aws_owner_id + result["aws_tags"] = attrs.aws_tags + result["cloud_uniform_resource_name"] = attrs.cloud_uniform_resource_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _s3_object_to_nested(s3_object: S3Object) -> S3ObjectNested: + """Convert flat S3Object to nested format.""" + attrs = S3ObjectAttributes() + _populate_s3_object_attrs(attrs, s3_object) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + s3_object, _S3_OBJECT_REL_FIELDS, S3ObjectRelationshipAttributes + ) + return S3ObjectNested( + guid=s3_object.guid, + type_name=s3_object.type_name, + status=s3_object.status, + version=s3_object.version, + create_time=s3_object.create_time, + update_time=s3_object.update_time, + created_by=s3_object.created_by, + updated_by=s3_object.updated_by, + classifications=s3_object.classifications, + classification_names=s3_object.classification_names, + meanings=s3_object.meanings, + labels=s3_object.labels, + business_attributes=s3_object.business_attributes, + custom_attributes=s3_object.custom_attributes, + pending_tasks=s3_object.pending_tasks, + proxy=s3_object.proxy, + is_incomplete=s3_object.is_incomplete, + provenance_type=s3_object.provenance_type, + home_id=s3_object.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _s3_object_from_nested(nested: S3ObjectNested) -> S3Object: + """Convert nested format to flat S3Object.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else S3ObjectAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _S3_OBJECT_REL_FIELDS, + S3ObjectRelationshipAttributes, + ) + return S3Object( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_s3_object_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _s3_object_to_nested_bytes(s3_object: S3Object, serde: Serde) -> bytes: + """Convert flat S3Object to nested JSON bytes.""" + return serde.encode(_s3_object_to_nested(s3_object)) + + +def _s3_object_from_nested_bytes(data: bytes, serde: Serde) -> S3Object: + """Convert nested JSON bytes to flat S3Object.""" + nested = serde.decode(data, S3ObjectNested) + return _s3_object_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +S3Object.S3_OBJECT_LAST_MODIFIED_TIME = NumericField( + "s3ObjectLastModifiedTime", "s3ObjectLastModifiedTime" +) +S3Object.S3_BUCKET_NAME = KeywordTextField( + "s3BucketName", "s3BucketName", "s3BucketName.text" +) +S3Object.S3_BUCKET_QUALIFIED_NAME = KeywordField( + "s3BucketQualifiedName", "s3BucketQualifiedName" +) +S3Object.S3_OBJECT_SIZE = NumericField("s3ObjectSize", "s3ObjectSize") +S3Object.S3_OBJECT_STORAGE_CLASS = KeywordField( + "s3ObjectStorageClass", "s3ObjectStorageClass" +) +S3Object.S3_OBJECT_KEY = KeywordTextField( + "s3ObjectKey", "s3ObjectKey", "s3ObjectKey.text" +) +S3Object.S3_OBJECT_CONTENT_TYPE = KeywordField( + "s3ObjectContentType", "s3ObjectContentType" +) +S3Object.S3_OBJECT_CONTENT_DISPOSITION = KeywordField( + "s3ObjectContentDisposition", "s3ObjectContentDisposition" +) +S3Object.S3_OBJECT_VERSION_ID = KeywordField("s3ObjectVersionId", "s3ObjectVersionId") +S3Object.S3_OBJECT_LOCK_RETAIN_UNTIL = NumericField( + "s3ObjectLockRetainUntil", "s3ObjectLockRetainUntil" +) +S3Object.S3_OBJECT_LOCK_MODE = KeywordField("s3ObjectLockMode", "s3ObjectLockMode") +S3Object.S3_OBJECT_LOCK_LEGAL_HOLD_ENABLED = BooleanField( + "s3ObjectLockLegalHoldEnabled", "s3ObjectLockLegalHoldEnabled" +) +S3Object.S3_ETAG = KeywordTextField("s3ETag", "s3ETag", "s3ETag.text") +S3Object.S3_ENCRYPTION = KeywordField("s3Encryption", "s3Encryption") +S3Object.S3_PARENT_PREFIX_QUALIFIED_NAME = KeywordField( + "s3ParentPrefixQualifiedName", "s3ParentPrefixQualifiedName" +) +S3Object.S3_PREFIX_HIERARCHY = KeywordField("s3PrefixHierarchy", "s3PrefixHierarchy") +S3Object.AWS_ARN = KeywordTextField("awsArn", "awsArn", "awsArn.text") +S3Object.AWS_PARTITION = KeywordField("awsPartition", "awsPartition") +S3Object.AWS_SERVICE = KeywordField("awsService", "awsService") +S3Object.AWS_REGION = KeywordField("awsRegion", "awsRegion") +S3Object.AWS_ACCOUNT_ID = KeywordField("awsAccountId", "awsAccountId") +S3Object.AWS_RESOURCE_ID = KeywordField("awsResourceId", "awsResourceId") +S3Object.AWS_OWNER_NAME = KeywordTextField( + "awsOwnerName", "awsOwnerName", "awsOwnerName.text" +) +S3Object.AWS_OWNER_ID = KeywordField("awsOwnerId", "awsOwnerId") +S3Object.AWS_TAGS = KeywordField("awsTags", "awsTags") +S3Object.CLOUD_UNIFORM_RESOURCE_NAME = KeywordField( + "cloudUniformResourceName", "cloudUniformResourceName" +) +S3Object.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +S3Object.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +S3Object.ANOMALO_CHECKS = RelationField("anomaloChecks") +S3Object.APPLICATION = RelationField("application") +S3Object.APPLICATION_FIELD = RelationField("applicationField") +S3Object.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +S3Object.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +S3Object.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +S3Object.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +S3Object.METRICS = RelationField("metrics") +S3Object.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +S3Object.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +S3Object.MEANINGS = RelationField("meanings") +S3Object.MC_MONITORS = RelationField("mcMonitors") +S3Object.MC_INCIDENTS = RelationField("mcIncidents") +S3Object.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +S3Object.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +S3Object.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +S3Object.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +S3Object.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +S3Object.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +S3Object.FILES = RelationField("files") +S3Object.LINKS = RelationField("links") +S3Object.README = RelationField("readme") +S3Object.BUCKET = RelationField("bucket") +S3Object.S3_PREFIX = RelationField("s3Prefix") +S3Object.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +S3Object.SODA_CHECKS = RelationField("sodaChecks") +S3Object.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +S3Object.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/s3_prefix.py b/pyatlan_v9/model/assets/s3_prefix.py new file mode 100644 index 000000000..3df57eaab --- /dev/null +++ b/pyatlan_v9/model/assets/s3_prefix.py @@ -0,0 +1,763 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +S3Prefix asset model with flattened inheritance. + +This module provides: +- S3Prefix: Flat asset class (easy to use) +- S3PrefixAttributes: Nested attributes struct (extends AssetAttributes) +- S3PrefixNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .s3_related import RelatedS3Bucket, RelatedS3Object, RelatedS3Prefix + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class S3Prefix(Asset): + """ + Instance of an S3 prefix in Atlan. + """ + + S3_BUCKET_NAME: ClassVar[Any] = None + S3_BUCKET_QUALIFIED_NAME: ClassVar[Any] = None + S3_PREFIX_COUNT: ClassVar[Any] = None + S3_OBJECT_COUNT: ClassVar[Any] = None + S3_ETAG: ClassVar[Any] = None + S3_ENCRYPTION: ClassVar[Any] = None + S3_PARENT_PREFIX_QUALIFIED_NAME: ClassVar[Any] = None + S3_PREFIX_HIERARCHY: ClassVar[Any] = None + AWS_ARN: ClassVar[Any] = None + AWS_PARTITION: ClassVar[Any] = None + AWS_SERVICE: ClassVar[Any] = None + AWS_REGION: ClassVar[Any] = None + AWS_ACCOUNT_ID: ClassVar[Any] = None + AWS_RESOURCE_ID: ClassVar[Any] = None + AWS_OWNER_NAME: ClassVar[Any] = None + AWS_OWNER_ID: ClassVar[Any] = None + AWS_TAGS: ClassVar[Any] = None + CLOUD_UNIFORM_RESOURCE_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + S3_BUCKET: ClassVar[Any] = None + S3_CHILD_PREFIXES: ClassVar[Any] = None + S3_PARENT_PREFIX: ClassVar[Any] = None + S3_OBJECTS: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "S3Prefix" + + s3_bucket_name: Union[str, None, UnsetType] = UNSET + """Simple name of the bucket in which this prefix exists.""" + + s3_bucket_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the bucket in which this prefix exists.""" + + s3_prefix_count: Union[int, None, UnsetType] = UNSET + """Number of prefixes immediately contained within the prefix.""" + + s3_object_count: Union[int, None, UnsetType] = UNSET + """Number of objects immediately contained within the prefix.""" + + s3_etag: Union[str, None, UnsetType] = msgspec.field(default=UNSET, name="s3ETag") + """Entity tag for the asset. An entity tag is a hash of the object and represents changes to the contents of an object only, not its metadata.""" + + s3_encryption: Union[str, None, UnsetType] = UNSET + """""" + + s3_parent_prefix_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the immediate parent prefix in which this asset exists.""" + + s3_prefix_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Ordered array of prefix assets with qualified name and name representing the complete prefix hierarchy path for this asset, from immediate parent to root prefix.""" + + aws_arn: Union[str, None, UnsetType] = UNSET + """DEPRECATED: This legacy attribute must be unique across all AWS asset instances. This can create non-obvious edge cases for creating / updating assets, and we therefore recommended NOT using it. See and use cloudResourceName instead.""" + + aws_partition: Union[str, None, UnsetType] = UNSET + """Group of AWS region and service objects.""" + + aws_service: Union[str, None, UnsetType] = UNSET + """Type of service in which the asset exists.""" + + aws_region: Union[str, None, UnsetType] = UNSET + """Physical region where the data center in which the asset exists is clustered.""" + + aws_account_id: Union[str, None, UnsetType] = UNSET + """12-digit number that uniquely identifies an AWS account.""" + + aws_resource_id: Union[str, None, UnsetType] = UNSET + """Unique resource ID assigned when a new resource is created.""" + + aws_owner_name: Union[str, None, UnsetType] = UNSET + """Root user's name.""" + + aws_owner_id: Union[str, None, UnsetType] = UNSET + """Root user's ID.""" + + aws_tags: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of tags that have been applied to the asset in AWS.""" + + cloud_uniform_resource_name: Union[str, None, UnsetType] = UNSET + """Uniform resource name (URN) for the asset: AWS ARN, Google Cloud URI, Azure resource ID, Oracle OCID, and so on.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + s3_bucket: Union[RelatedS3Bucket, None, UnsetType] = UNSET + """S3 bucket that contains the prefix.""" + + s3_child_prefixes: Union[List[RelatedS3Prefix], None, UnsetType] = UNSET + """S3 child prefixes contained in this parent prefix.""" + + s3_parent_prefix: Union[RelatedS3Prefix, None, UnsetType] = UNSET + """S3 parent prefix containing this child prefix.""" + + s3_objects: Union[List[RelatedS3Object], None, UnsetType] = UNSET + """S3 objects contained in this prefix.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "S3Prefix" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _s3_prefix_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> S3Prefix: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + S3Prefix instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _s3_prefix_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class S3PrefixAttributes(AssetAttributes): + """S3Prefix-specific attributes for nested API format.""" + + s3_bucket_name: Union[str, None, UnsetType] = UNSET + """Simple name of the bucket in which this prefix exists.""" + + s3_bucket_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the bucket in which this prefix exists.""" + + s3_prefix_count: Union[int, None, UnsetType] = UNSET + """Number of prefixes immediately contained within the prefix.""" + + s3_object_count: Union[int, None, UnsetType] = UNSET + """Number of objects immediately contained within the prefix.""" + + s3_etag: Union[str, None, UnsetType] = msgspec.field(default=UNSET, name="s3ETag") + """Entity tag for the asset. An entity tag is a hash of the object and represents changes to the contents of an object only, not its metadata.""" + + s3_encryption: Union[str, None, UnsetType] = UNSET + """""" + + s3_parent_prefix_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the immediate parent prefix in which this asset exists.""" + + s3_prefix_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Ordered array of prefix assets with qualified name and name representing the complete prefix hierarchy path for this asset, from immediate parent to root prefix.""" + + aws_arn: Union[str, None, UnsetType] = UNSET + """DEPRECATED: This legacy attribute must be unique across all AWS asset instances. This can create non-obvious edge cases for creating / updating assets, and we therefore recommended NOT using it. See and use cloudResourceName instead.""" + + aws_partition: Union[str, None, UnsetType] = UNSET + """Group of AWS region and service objects.""" + + aws_service: Union[str, None, UnsetType] = UNSET + """Type of service in which the asset exists.""" + + aws_region: Union[str, None, UnsetType] = UNSET + """Physical region where the data center in which the asset exists is clustered.""" + + aws_account_id: Union[str, None, UnsetType] = UNSET + """12-digit number that uniquely identifies an AWS account.""" + + aws_resource_id: Union[str, None, UnsetType] = UNSET + """Unique resource ID assigned when a new resource is created.""" + + aws_owner_name: Union[str, None, UnsetType] = UNSET + """Root user's name.""" + + aws_owner_id: Union[str, None, UnsetType] = UNSET + """Root user's ID.""" + + aws_tags: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of tags that have been applied to the asset in AWS.""" + + cloud_uniform_resource_name: Union[str, None, UnsetType] = UNSET + """Uniform resource name (URN) for the asset: AWS ARN, Google Cloud URI, Azure resource ID, Oracle OCID, and so on.""" + + +class S3PrefixRelationshipAttributes(AssetRelationshipAttributes): + """S3Prefix-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + s3_bucket: Union[RelatedS3Bucket, None, UnsetType] = UNSET + """S3 bucket that contains the prefix.""" + + s3_child_prefixes: Union[List[RelatedS3Prefix], None, UnsetType] = UNSET + """S3 child prefixes contained in this parent prefix.""" + + s3_parent_prefix: Union[RelatedS3Prefix, None, UnsetType] = UNSET + """S3 parent prefix containing this child prefix.""" + + s3_objects: Union[List[RelatedS3Object], None, UnsetType] = UNSET + """S3 objects contained in this prefix.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class S3PrefixNested(AssetNested): + """S3Prefix in nested API format for high-performance serialization.""" + + attributes: Union[S3PrefixAttributes, UnsetType] = UNSET + relationship_attributes: Union[S3PrefixRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[S3PrefixRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[S3PrefixRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_S3_PREFIX_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "s3_bucket", + "s3_child_prefixes", + "s3_parent_prefix", + "s3_objects", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_s3_prefix_attrs(attrs: S3PrefixAttributes, obj: S3Prefix) -> None: + """Populate S3Prefix-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.s3_bucket_name = obj.s3_bucket_name + attrs.s3_bucket_qualified_name = obj.s3_bucket_qualified_name + attrs.s3_prefix_count = obj.s3_prefix_count + attrs.s3_object_count = obj.s3_object_count + attrs.s3_etag = obj.s3_etag + attrs.s3_encryption = obj.s3_encryption + attrs.s3_parent_prefix_qualified_name = obj.s3_parent_prefix_qualified_name + attrs.s3_prefix_hierarchy = obj.s3_prefix_hierarchy + attrs.aws_arn = obj.aws_arn + attrs.aws_partition = obj.aws_partition + attrs.aws_service = obj.aws_service + attrs.aws_region = obj.aws_region + attrs.aws_account_id = obj.aws_account_id + attrs.aws_resource_id = obj.aws_resource_id + attrs.aws_owner_name = obj.aws_owner_name + attrs.aws_owner_id = obj.aws_owner_id + attrs.aws_tags = obj.aws_tags + attrs.cloud_uniform_resource_name = obj.cloud_uniform_resource_name + + +def _extract_s3_prefix_attrs(attrs: S3PrefixAttributes) -> dict: + """Extract all S3Prefix attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["s3_bucket_name"] = attrs.s3_bucket_name + result["s3_bucket_qualified_name"] = attrs.s3_bucket_qualified_name + result["s3_prefix_count"] = attrs.s3_prefix_count + result["s3_object_count"] = attrs.s3_object_count + result["s3_etag"] = attrs.s3_etag + result["s3_encryption"] = attrs.s3_encryption + result["s3_parent_prefix_qualified_name"] = attrs.s3_parent_prefix_qualified_name + result["s3_prefix_hierarchy"] = attrs.s3_prefix_hierarchy + result["aws_arn"] = attrs.aws_arn + result["aws_partition"] = attrs.aws_partition + result["aws_service"] = attrs.aws_service + result["aws_region"] = attrs.aws_region + result["aws_account_id"] = attrs.aws_account_id + result["aws_resource_id"] = attrs.aws_resource_id + result["aws_owner_name"] = attrs.aws_owner_name + result["aws_owner_id"] = attrs.aws_owner_id + result["aws_tags"] = attrs.aws_tags + result["cloud_uniform_resource_name"] = attrs.cloud_uniform_resource_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _s3_prefix_to_nested(s3_prefix: S3Prefix) -> S3PrefixNested: + """Convert flat S3Prefix to nested format.""" + attrs = S3PrefixAttributes() + _populate_s3_prefix_attrs(attrs, s3_prefix) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + s3_prefix, _S3_PREFIX_REL_FIELDS, S3PrefixRelationshipAttributes + ) + return S3PrefixNested( + guid=s3_prefix.guid, + type_name=s3_prefix.type_name, + status=s3_prefix.status, + version=s3_prefix.version, + create_time=s3_prefix.create_time, + update_time=s3_prefix.update_time, + created_by=s3_prefix.created_by, + updated_by=s3_prefix.updated_by, + classifications=s3_prefix.classifications, + classification_names=s3_prefix.classification_names, + meanings=s3_prefix.meanings, + labels=s3_prefix.labels, + business_attributes=s3_prefix.business_attributes, + custom_attributes=s3_prefix.custom_attributes, + pending_tasks=s3_prefix.pending_tasks, + proxy=s3_prefix.proxy, + is_incomplete=s3_prefix.is_incomplete, + provenance_type=s3_prefix.provenance_type, + home_id=s3_prefix.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _s3_prefix_from_nested(nested: S3PrefixNested) -> S3Prefix: + """Convert nested format to flat S3Prefix.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else S3PrefixAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _S3_PREFIX_REL_FIELDS, + S3PrefixRelationshipAttributes, + ) + return S3Prefix( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_s3_prefix_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _s3_prefix_to_nested_bytes(s3_prefix: S3Prefix, serde: Serde) -> bytes: + """Convert flat S3Prefix to nested JSON bytes.""" + return serde.encode(_s3_prefix_to_nested(s3_prefix)) + + +def _s3_prefix_from_nested_bytes(data: bytes, serde: Serde) -> S3Prefix: + """Convert nested JSON bytes to flat S3Prefix.""" + nested = serde.decode(data, S3PrefixNested) + return _s3_prefix_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +S3Prefix.S3_BUCKET_NAME = KeywordField("s3BucketName", "s3BucketName") +S3Prefix.S3_BUCKET_QUALIFIED_NAME = KeywordField( + "s3BucketQualifiedName", "s3BucketQualifiedName" +) +S3Prefix.S3_PREFIX_COUNT = NumericField("s3PrefixCount", "s3PrefixCount") +S3Prefix.S3_OBJECT_COUNT = NumericField("s3ObjectCount", "s3ObjectCount") +S3Prefix.S3_ETAG = KeywordTextField("s3ETag", "s3ETag", "s3ETag.text") +S3Prefix.S3_ENCRYPTION = KeywordField("s3Encryption", "s3Encryption") +S3Prefix.S3_PARENT_PREFIX_QUALIFIED_NAME = KeywordField( + "s3ParentPrefixQualifiedName", "s3ParentPrefixQualifiedName" +) +S3Prefix.S3_PREFIX_HIERARCHY = KeywordField("s3PrefixHierarchy", "s3PrefixHierarchy") +S3Prefix.AWS_ARN = KeywordTextField("awsArn", "awsArn", "awsArn.text") +S3Prefix.AWS_PARTITION = KeywordField("awsPartition", "awsPartition") +S3Prefix.AWS_SERVICE = KeywordField("awsService", "awsService") +S3Prefix.AWS_REGION = KeywordField("awsRegion", "awsRegion") +S3Prefix.AWS_ACCOUNT_ID = KeywordField("awsAccountId", "awsAccountId") +S3Prefix.AWS_RESOURCE_ID = KeywordField("awsResourceId", "awsResourceId") +S3Prefix.AWS_OWNER_NAME = KeywordTextField( + "awsOwnerName", "awsOwnerName", "awsOwnerName.text" +) +S3Prefix.AWS_OWNER_ID = KeywordField("awsOwnerId", "awsOwnerId") +S3Prefix.AWS_TAGS = KeywordField("awsTags", "awsTags") +S3Prefix.CLOUD_UNIFORM_RESOURCE_NAME = KeywordField( + "cloudUniformResourceName", "cloudUniformResourceName" +) +S3Prefix.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +S3Prefix.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +S3Prefix.ANOMALO_CHECKS = RelationField("anomaloChecks") +S3Prefix.APPLICATION = RelationField("application") +S3Prefix.APPLICATION_FIELD = RelationField("applicationField") +S3Prefix.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +S3Prefix.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +S3Prefix.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +S3Prefix.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +S3Prefix.METRICS = RelationField("metrics") +S3Prefix.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +S3Prefix.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +S3Prefix.MEANINGS = RelationField("meanings") +S3Prefix.MC_MONITORS = RelationField("mcMonitors") +S3Prefix.MC_INCIDENTS = RelationField("mcIncidents") +S3Prefix.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +S3Prefix.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +S3Prefix.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +S3Prefix.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +S3Prefix.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +S3Prefix.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +S3Prefix.FILES = RelationField("files") +S3Prefix.LINKS = RelationField("links") +S3Prefix.README = RelationField("readme") +S3Prefix.S3_BUCKET = RelationField("s3Bucket") +S3Prefix.S3_CHILD_PREFIXES = RelationField("s3ChildPrefixes") +S3Prefix.S3_PARENT_PREFIX = RelationField("s3ParentPrefix") +S3Prefix.S3_OBJECTS = RelationField("s3Objects") +S3Prefix.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +S3Prefix.SODA_CHECKS = RelationField("sodaChecks") +S3Prefix.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +S3Prefix.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/s3_related.py b/pyatlan_v9/model/assets/s3_related.py new file mode 100644 index 000000000..e6f6a7948 --- /dev/null +++ b/pyatlan_v9/model/assets/s3_related.py @@ -0,0 +1,153 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for S3 module. + +This module contains all Related{Type} classes for the S3 type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedObjectStore +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedS3", + "RelatedS3Bucket", + "RelatedS3Object", + "RelatedS3Prefix", +] + + +class RelatedS3(RelatedObjectStore): + """ + Related entity reference for S3 assets. + + Extends RelatedObjectStore with S3-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "S3" so it serializes correctly + + s3_etag: Union[str, None, UnsetType] = msgspec.field(default=UNSET, name="s3ETag") + """Entity tag for the asset. An entity tag is a hash of the object and represents changes to the contents of an object only, not its metadata.""" + + s3_encryption: Union[str, None, UnsetType] = UNSET + """""" + + s3_parent_prefix_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the immediate parent prefix in which this asset exists.""" + + s3_prefix_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Ordered array of prefix assets with qualified name and name representing the complete prefix hierarchy path for this asset, from immediate parent to root prefix.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "S3" + + +class RelatedS3Bucket(RelatedS3): + """ + Related entity reference for S3Bucket assets. + + Extends RelatedS3 with S3Bucket-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "S3Bucket" so it serializes correctly + + s3_object_count: Union[int, None, UnsetType] = UNSET + """Number of objects within the bucket.""" + + s3_bucket_versioning_enabled: Union[bool, None, UnsetType] = UNSET + """Whether versioning is enabled for the bucket (true) or not (false).""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "S3Bucket" + + +class RelatedS3Object(RelatedS3): + """ + Related entity reference for S3Object assets. + + Extends RelatedS3 with S3Object-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "S3Object" so it serializes correctly + + s3_object_last_modified_time: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this object was last updated, in milliseconds, or when it was created if it has never been modified.""" + + s3_bucket_name: Union[str, None, UnsetType] = UNSET + """Simple name of the bucket in which this object exists.""" + + s3_bucket_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the bucket in which this object exists.""" + + s3_object_size: Union[int, None, UnsetType] = UNSET + """Object size in bytes.""" + + s3_object_storage_class: Union[str, None, UnsetType] = UNSET + """Storage class used for storing this object, for example: standard, intelligent-tiering, glacier, etc.""" + + s3_object_key: Union[str, None, UnsetType] = UNSET + """Unique identity of this object in an S3 bucket. This is usually the concatenation of any prefix (folder) in the S3 bucket with the name of the object (file) itself.""" + + s3_object_content_type: Union[str, None, UnsetType] = UNSET + """Type of content in this object, for example: text/plain, application/json, etc.""" + + s3_object_content_disposition: Union[str, None, UnsetType] = UNSET + """Information about how this object's content should be presented.""" + + s3_object_version_id: Union[str, None, UnsetType] = UNSET + """Version of this object. This is only applicable when versioning is enabled on the bucket in which this object exists.""" + + s3_object_lock_retain_until: Union[int, None, UnsetType] = UNSET + """Time (epoch) when the object lock retention will expire.""" + + s3_object_lock_mode: Union[str, None, UnsetType] = UNSET + """Mode of the object lock retention.""" + + s3_object_lock_legal_hold_enabled: Union[bool, None, UnsetType] = UNSET + """Whether the object lock legal hold is enabled (true) or not (false).""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "S3Object" + + +class RelatedS3Prefix(RelatedS3): + """ + Related entity reference for S3Prefix assets. + + Extends RelatedS3 with S3Prefix-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "S3Prefix" so it serializes correctly + + s3_bucket_name: Union[str, None, UnsetType] = UNSET + """Simple name of the bucket in which this prefix exists.""" + + s3_bucket_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the bucket in which this prefix exists.""" + + s3_prefix_count: Union[int, None, UnsetType] = UNSET + """Number of prefixes immediately contained within the prefix.""" + + s3_object_count: Union[int, None, UnsetType] = UNSET + """Number of objects immediately contained within the prefix.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "S3Prefix" diff --git a/pyatlan_v9/model/assets/saa_s.py b/pyatlan_v9/model/assets/saa_s.py new file mode 100644 index 000000000..53ed1094b --- /dev/null +++ b/pyatlan_v9/model/assets/saa_s.py @@ -0,0 +1,519 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SaaS asset model with flattened inheritance. + +This module provides: +- SaaS: Flat asset class (easy to use) +- SaaSAttributes: Nested attributes struct (extends AssetAttributes) +- SaaSNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SaaS(Asset): + """ + Base class for SaaS application assets. + """ + + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SaaS" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SaaS" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _saa_s_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> SaaS: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SaaS instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _saa_s_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SaaSAttributes(AssetAttributes): + """SaaS-specific attributes for nested API format.""" + + pass + + +class SaaSRelationshipAttributes(AssetRelationshipAttributes): + """SaaS-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SaaSNested(AssetNested): + """SaaS in nested API format for high-performance serialization.""" + + attributes: Union[SaaSAttributes, UnsetType] = UNSET + relationship_attributes: Union[SaaSRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[SaaSRelationshipAttributes, UnsetType] = UNSET + remove_relationship_attributes: Union[SaaSRelationshipAttributes, UnsetType] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SAA_S_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_saa_s_attrs(attrs: SaaSAttributes, obj: SaaS) -> None: + """Populate SaaS-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + + +def _extract_saa_s_attrs(attrs: SaaSAttributes) -> dict: + """Extract all SaaS attributes from the attrs struct into a flat dict.""" + return _extract_asset_attrs(attrs) + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _saa_s_to_nested(saa_s: SaaS) -> SaaSNested: + """Convert flat SaaS to nested format.""" + attrs = SaaSAttributes() + _populate_saa_s_attrs(attrs, saa_s) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + saa_s, _SAA_S_REL_FIELDS, SaaSRelationshipAttributes + ) + return SaaSNested( + guid=saa_s.guid, + type_name=saa_s.type_name, + status=saa_s.status, + version=saa_s.version, + create_time=saa_s.create_time, + update_time=saa_s.update_time, + created_by=saa_s.created_by, + updated_by=saa_s.updated_by, + classifications=saa_s.classifications, + classification_names=saa_s.classification_names, + meanings=saa_s.meanings, + labels=saa_s.labels, + business_attributes=saa_s.business_attributes, + custom_attributes=saa_s.custom_attributes, + pending_tasks=saa_s.pending_tasks, + proxy=saa_s.proxy, + is_incomplete=saa_s.is_incomplete, + provenance_type=saa_s.provenance_type, + home_id=saa_s.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _saa_s_from_nested(nested: SaaSNested) -> SaaS: + """Convert nested format to flat SaaS.""" + attrs = nested.attributes if nested.attributes is not UNSET else SaaSAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SAA_S_REL_FIELDS, + SaaSRelationshipAttributes, + ) + return SaaS( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_saa_s_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _saa_s_to_nested_bytes(saa_s: SaaS, serde: Serde) -> bytes: + """Convert flat SaaS to nested JSON bytes.""" + return serde.encode(_saa_s_to_nested(saa_s)) + + +def _saa_s_from_nested_bytes(data: bytes, serde: Serde) -> SaaS: + """Convert nested JSON bytes to flat SaaS.""" + nested = serde.decode(data, SaaSNested) + return _saa_s_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import RelationField # noqa: E402 + +SaaS.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SaaS.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +SaaS.ANOMALO_CHECKS = RelationField("anomaloChecks") +SaaS.APPLICATION = RelationField("application") +SaaS.APPLICATION_FIELD = RelationField("applicationField") +SaaS.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +SaaS.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +SaaS.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +SaaS.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +SaaS.METRICS = RelationField("metrics") +SaaS.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SaaS.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +SaaS.MEANINGS = RelationField("meanings") +SaaS.MC_MONITORS = RelationField("mcMonitors") +SaaS.MC_INCIDENTS = RelationField("mcIncidents") +SaaS.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SaaS.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SaaS.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SaaS.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SaaS.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +SaaS.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +SaaS.FILES = RelationField("files") +SaaS.LINKS = RelationField("links") +SaaS.README = RelationField("readme") +SaaS.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +SaaS.SODA_CHECKS = RelationField("sodaChecks") +SaaS.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SaaS.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/sage_maker.py b/pyatlan_v9/model/assets/sage_maker.py new file mode 100644 index 000000000..e3e25d344 --- /dev/null +++ b/pyatlan_v9/model/assets/sage_maker.py @@ -0,0 +1,766 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SageMaker asset model with flattened inheritance. + +This module provides: +- SageMaker: Flat asset class (easy to use) +- SageMakerAttributes: Nested attributes struct (extends AssetAttributes) +- SageMakerNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SageMaker(Asset): + """ + Base class for AWS SageMaker assets. + """ + + SAGE_MAKER_S3_URI: ClassVar[Any] = None + ETHICAL_AI_PRIVACY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_FAIRNESS_CONFIG: ClassVar[Any] = None + ETHICAL_AI_BIAS_MITIGATION_CONFIG: ClassVar[Any] = None + ETHICAL_AI_RELIABILITY_AND_SAFETY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_TRANSPARENCY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_ACCOUNTABILITY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_ENVIRONMENTAL_CONSCIOUSNESS_CONFIG: ClassVar[Any] = None + AWS_ARN: ClassVar[Any] = None + AWS_PARTITION: ClassVar[Any] = None + AWS_SERVICE: ClassVar[Any] = None + AWS_REGION: ClassVar[Any] = None + AWS_ACCOUNT_ID: ClassVar[Any] = None + AWS_RESOURCE_ID: ClassVar[Any] = None + AWS_OWNER_NAME: ClassVar[Any] = None + AWS_OWNER_ID: ClassVar[Any] = None + AWS_TAGS: ClassVar[Any] = None + CLOUD_UNIFORM_RESOURCE_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SageMaker" + + sage_maker_s3_uri: Union[str, None, UnsetType] = UNSET + """Primary S3 URI associated with this SageMaker asset.""" + + ethical_ai_privacy_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIPrivacyConfig" + ) + """Privacy configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_fairness_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIFairnessConfig" + ) + """Fairness configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_bias_mitigation_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIBiasMitigationConfig" + ) + """Bias mitigation configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_reliability_and_safety_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIReliabilityAndSafetyConfig") + ) + """Reliability and safety configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_transparency_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAITransparencyConfig" + ) + """Transparency configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_accountability_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIAccountabilityConfig" + ) + """Accountability configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_environmental_consciousness_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIEnvironmentalConsciousnessConfig") + ) + """Environmental consciousness configuration for ensuring the ethical use of an AI asset""" + + aws_arn: Union[str, None, UnsetType] = UNSET + """DEPRECATED: This legacy attribute must be unique across all AWS asset instances. This can create non-obvious edge cases for creating / updating assets, and we therefore recommended NOT using it. See and use cloudResourceName instead.""" + + aws_partition: Union[str, None, UnsetType] = UNSET + """Group of AWS region and service objects.""" + + aws_service: Union[str, None, UnsetType] = UNSET + """Type of service in which the asset exists.""" + + aws_region: Union[str, None, UnsetType] = UNSET + """Physical region where the data center in which the asset exists is clustered.""" + + aws_account_id: Union[str, None, UnsetType] = UNSET + """12-digit number that uniquely identifies an AWS account.""" + + aws_resource_id: Union[str, None, UnsetType] = UNSET + """Unique resource ID assigned when a new resource is created.""" + + aws_owner_name: Union[str, None, UnsetType] = UNSET + """Root user's name.""" + + aws_owner_id: Union[str, None, UnsetType] = UNSET + """Root user's ID.""" + + aws_tags: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of tags that have been applied to the asset in AWS.""" + + cloud_uniform_resource_name: Union[str, None, UnsetType] = UNSET + """Uniform resource name (URN) for the asset: AWS ARN, Google Cloud URI, Azure resource ID, Oracle OCID, and so on.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SageMaker" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _sage_maker_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> SageMaker: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SageMaker instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _sage_maker_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SageMakerAttributes(AssetAttributes): + """SageMaker-specific attributes for nested API format.""" + + sage_maker_s3_uri: Union[str, None, UnsetType] = UNSET + """Primary S3 URI associated with this SageMaker asset.""" + + ethical_ai_privacy_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIPrivacyConfig" + ) + """Privacy configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_fairness_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIFairnessConfig" + ) + """Fairness configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_bias_mitigation_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIBiasMitigationConfig" + ) + """Bias mitigation configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_reliability_and_safety_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIReliabilityAndSafetyConfig") + ) + """Reliability and safety configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_transparency_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAITransparencyConfig" + ) + """Transparency configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_accountability_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIAccountabilityConfig" + ) + """Accountability configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_environmental_consciousness_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIEnvironmentalConsciousnessConfig") + ) + """Environmental consciousness configuration for ensuring the ethical use of an AI asset""" + + aws_arn: Union[str, None, UnsetType] = UNSET + """DEPRECATED: This legacy attribute must be unique across all AWS asset instances. This can create non-obvious edge cases for creating / updating assets, and we therefore recommended NOT using it. See and use cloudResourceName instead.""" + + aws_partition: Union[str, None, UnsetType] = UNSET + """Group of AWS region and service objects.""" + + aws_service: Union[str, None, UnsetType] = UNSET + """Type of service in which the asset exists.""" + + aws_region: Union[str, None, UnsetType] = UNSET + """Physical region where the data center in which the asset exists is clustered.""" + + aws_account_id: Union[str, None, UnsetType] = UNSET + """12-digit number that uniquely identifies an AWS account.""" + + aws_resource_id: Union[str, None, UnsetType] = UNSET + """Unique resource ID assigned when a new resource is created.""" + + aws_owner_name: Union[str, None, UnsetType] = UNSET + """Root user's name.""" + + aws_owner_id: Union[str, None, UnsetType] = UNSET + """Root user's ID.""" + + aws_tags: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of tags that have been applied to the asset in AWS.""" + + cloud_uniform_resource_name: Union[str, None, UnsetType] = UNSET + """Uniform resource name (URN) for the asset: AWS ARN, Google Cloud URI, Azure resource ID, Oracle OCID, and so on.""" + + +class SageMakerRelationshipAttributes(AssetRelationshipAttributes): + """SageMaker-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SageMakerNested(AssetNested): + """SageMaker in nested API format for high-performance serialization.""" + + attributes: Union[SageMakerAttributes, UnsetType] = UNSET + relationship_attributes: Union[SageMakerRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + SageMakerRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SageMakerRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SAGE_MAKER_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_sage_maker_attrs(attrs: SageMakerAttributes, obj: SageMaker) -> None: + """Populate SageMaker-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.sage_maker_s3_uri = obj.sage_maker_s3_uri + attrs.ethical_ai_privacy_config = obj.ethical_ai_privacy_config + attrs.ethical_ai_fairness_config = obj.ethical_ai_fairness_config + attrs.ethical_ai_bias_mitigation_config = obj.ethical_ai_bias_mitigation_config + attrs.ethical_ai_reliability_and_safety_config = ( + obj.ethical_ai_reliability_and_safety_config + ) + attrs.ethical_ai_transparency_config = obj.ethical_ai_transparency_config + attrs.ethical_ai_accountability_config = obj.ethical_ai_accountability_config + attrs.ethical_ai_environmental_consciousness_config = ( + obj.ethical_ai_environmental_consciousness_config + ) + attrs.aws_arn = obj.aws_arn + attrs.aws_partition = obj.aws_partition + attrs.aws_service = obj.aws_service + attrs.aws_region = obj.aws_region + attrs.aws_account_id = obj.aws_account_id + attrs.aws_resource_id = obj.aws_resource_id + attrs.aws_owner_name = obj.aws_owner_name + attrs.aws_owner_id = obj.aws_owner_id + attrs.aws_tags = obj.aws_tags + attrs.cloud_uniform_resource_name = obj.cloud_uniform_resource_name + + +def _extract_sage_maker_attrs(attrs: SageMakerAttributes) -> dict: + """Extract all SageMaker attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["sage_maker_s3_uri"] = attrs.sage_maker_s3_uri + result["ethical_ai_privacy_config"] = attrs.ethical_ai_privacy_config + result["ethical_ai_fairness_config"] = attrs.ethical_ai_fairness_config + result["ethical_ai_bias_mitigation_config"] = ( + attrs.ethical_ai_bias_mitigation_config + ) + result["ethical_ai_reliability_and_safety_config"] = ( + attrs.ethical_ai_reliability_and_safety_config + ) + result["ethical_ai_transparency_config"] = attrs.ethical_ai_transparency_config + result["ethical_ai_accountability_config"] = attrs.ethical_ai_accountability_config + result["ethical_ai_environmental_consciousness_config"] = ( + attrs.ethical_ai_environmental_consciousness_config + ) + result["aws_arn"] = attrs.aws_arn + result["aws_partition"] = attrs.aws_partition + result["aws_service"] = attrs.aws_service + result["aws_region"] = attrs.aws_region + result["aws_account_id"] = attrs.aws_account_id + result["aws_resource_id"] = attrs.aws_resource_id + result["aws_owner_name"] = attrs.aws_owner_name + result["aws_owner_id"] = attrs.aws_owner_id + result["aws_tags"] = attrs.aws_tags + result["cloud_uniform_resource_name"] = attrs.cloud_uniform_resource_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _sage_maker_to_nested(sage_maker: SageMaker) -> SageMakerNested: + """Convert flat SageMaker to nested format.""" + attrs = SageMakerAttributes() + _populate_sage_maker_attrs(attrs, sage_maker) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + sage_maker, _SAGE_MAKER_REL_FIELDS, SageMakerRelationshipAttributes + ) + return SageMakerNested( + guid=sage_maker.guid, + type_name=sage_maker.type_name, + status=sage_maker.status, + version=sage_maker.version, + create_time=sage_maker.create_time, + update_time=sage_maker.update_time, + created_by=sage_maker.created_by, + updated_by=sage_maker.updated_by, + classifications=sage_maker.classifications, + classification_names=sage_maker.classification_names, + meanings=sage_maker.meanings, + labels=sage_maker.labels, + business_attributes=sage_maker.business_attributes, + custom_attributes=sage_maker.custom_attributes, + pending_tasks=sage_maker.pending_tasks, + proxy=sage_maker.proxy, + is_incomplete=sage_maker.is_incomplete, + provenance_type=sage_maker.provenance_type, + home_id=sage_maker.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _sage_maker_from_nested(nested: SageMakerNested) -> SageMaker: + """Convert nested format to flat SageMaker.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else SageMakerAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SAGE_MAKER_REL_FIELDS, + SageMakerRelationshipAttributes, + ) + return SageMaker( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_sage_maker_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _sage_maker_to_nested_bytes(sage_maker: SageMaker, serde: Serde) -> bytes: + """Convert flat SageMaker to nested JSON bytes.""" + return serde.encode(_sage_maker_to_nested(sage_maker)) + + +def _sage_maker_from_nested_bytes(data: bytes, serde: Serde) -> SageMaker: + """Convert nested JSON bytes to flat SageMaker.""" + nested = serde.decode(data, SageMakerNested) + return _sage_maker_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + RelationField, +) + +SageMaker.SAGE_MAKER_S3_URI = KeywordField("sageMakerS3Uri", "sageMakerS3Uri") +SageMaker.ETHICAL_AI_PRIVACY_CONFIG = KeywordField( + "ethicalAIPrivacyConfig", "ethicalAIPrivacyConfig" +) +SageMaker.ETHICAL_AI_FAIRNESS_CONFIG = KeywordField( + "ethicalAIFairnessConfig", "ethicalAIFairnessConfig" +) +SageMaker.ETHICAL_AI_BIAS_MITIGATION_CONFIG = KeywordField( + "ethicalAIBiasMitigationConfig", "ethicalAIBiasMitigationConfig" +) +SageMaker.ETHICAL_AI_RELIABILITY_AND_SAFETY_CONFIG = KeywordField( + "ethicalAIReliabilityAndSafetyConfig", "ethicalAIReliabilityAndSafetyConfig" +) +SageMaker.ETHICAL_AI_TRANSPARENCY_CONFIG = KeywordField( + "ethicalAITransparencyConfig", "ethicalAITransparencyConfig" +) +SageMaker.ETHICAL_AI_ACCOUNTABILITY_CONFIG = KeywordField( + "ethicalAIAccountabilityConfig", "ethicalAIAccountabilityConfig" +) +SageMaker.ETHICAL_AI_ENVIRONMENTAL_CONSCIOUSNESS_CONFIG = KeywordField( + "ethicalAIEnvironmentalConsciousnessConfig", + "ethicalAIEnvironmentalConsciousnessConfig", +) +SageMaker.AWS_ARN = KeywordTextField("awsArn", "awsArn", "awsArn.text") +SageMaker.AWS_PARTITION = KeywordField("awsPartition", "awsPartition") +SageMaker.AWS_SERVICE = KeywordField("awsService", "awsService") +SageMaker.AWS_REGION = KeywordField("awsRegion", "awsRegion") +SageMaker.AWS_ACCOUNT_ID = KeywordField("awsAccountId", "awsAccountId") +SageMaker.AWS_RESOURCE_ID = KeywordField("awsResourceId", "awsResourceId") +SageMaker.AWS_OWNER_NAME = KeywordTextField( + "awsOwnerName", "awsOwnerName", "awsOwnerName.text" +) +SageMaker.AWS_OWNER_ID = KeywordField("awsOwnerId", "awsOwnerId") +SageMaker.AWS_TAGS = KeywordField("awsTags", "awsTags") +SageMaker.CLOUD_UNIFORM_RESOURCE_NAME = KeywordField( + "cloudUniformResourceName", "cloudUniformResourceName" +) +SageMaker.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SageMaker.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +SageMaker.ANOMALO_CHECKS = RelationField("anomaloChecks") +SageMaker.APPLICATION = RelationField("application") +SageMaker.APPLICATION_FIELD = RelationField("applicationField") +SageMaker.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +SageMaker.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +SageMaker.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +SageMaker.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +SageMaker.METRICS = RelationField("metrics") +SageMaker.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SageMaker.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +SageMaker.MEANINGS = RelationField("meanings") +SageMaker.MC_MONITORS = RelationField("mcMonitors") +SageMaker.MC_INCIDENTS = RelationField("mcIncidents") +SageMaker.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SageMaker.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SageMaker.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SageMaker.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SageMaker.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +SageMaker.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +SageMaker.FILES = RelationField("files") +SageMaker.LINKS = RelationField("links") +SageMaker.README = RelationField("readme") +SageMaker.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +SageMaker.SODA_CHECKS = RelationField("sodaChecks") +SageMaker.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SageMaker.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/sage_maker_feature.py b/pyatlan_v9/model/assets/sage_maker_feature.py new file mode 100644 index 000000000..95ec32fcb --- /dev/null +++ b/pyatlan_v9/model/assets/sage_maker_feature.py @@ -0,0 +1,855 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SageMakerFeature asset model with flattened inheritance. + +This module provides: +- SageMakerFeature: Flat asset class (easy to use) +- SageMakerFeatureAttributes: Nested attributes struct (extends AssetAttributes) +- SageMakerFeatureNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .sage_maker_related import RelatedSageMakerFeatureGroup + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SageMakerFeature(Asset): + """ + Instance of a SageMaker Feature in Atlan. Represents an individual feature within a Feature Group, including its data type and metadata. + """ + + SAGE_MAKER_GROUP_NAME: ClassVar[Any] = None + SAGE_MAKER_GROUP_QUALIFIED_NAME: ClassVar[Any] = None + SAGE_MAKER_DATA_TYPE: ClassVar[Any] = None + SAGE_MAKER_IS_RECORD_IDENTIFIER: ClassVar[Any] = None + SAGE_MAKER_S3_URI: ClassVar[Any] = None + ETHICAL_AI_PRIVACY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_FAIRNESS_CONFIG: ClassVar[Any] = None + ETHICAL_AI_BIAS_MITIGATION_CONFIG: ClassVar[Any] = None + ETHICAL_AI_RELIABILITY_AND_SAFETY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_TRANSPARENCY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_ACCOUNTABILITY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_ENVIRONMENTAL_CONSCIOUSNESS_CONFIG: ClassVar[Any] = None + AWS_ARN: ClassVar[Any] = None + AWS_PARTITION: ClassVar[Any] = None + AWS_SERVICE: ClassVar[Any] = None + AWS_REGION: ClassVar[Any] = None + AWS_ACCOUNT_ID: ClassVar[Any] = None + AWS_RESOURCE_ID: ClassVar[Any] = None + AWS_OWNER_NAME: ClassVar[Any] = None + AWS_OWNER_ID: ClassVar[Any] = None + AWS_TAGS: ClassVar[Any] = None + CLOUD_UNIFORM_RESOURCE_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SAGE_MAKER_FEATURE_GROUP: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SageMakerFeature" + + sage_maker_group_name: Union[str, None, UnsetType] = UNSET + """Name of the Feature Group that contains this feature.""" + + sage_maker_group_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the Feature Group that contains this feature.""" + + sage_maker_data_type: Union[str, None, UnsetType] = UNSET + """Data type of the feature (e.g., String, Integral, Fractional).""" + + sage_maker_is_record_identifier: Union[bool, None, UnsetType] = UNSET + """Whether this feature serves as the record identifier for the Feature Group.""" + + sage_maker_s3_uri: Union[str, None, UnsetType] = UNSET + """Primary S3 URI associated with this SageMaker asset.""" + + ethical_ai_privacy_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIPrivacyConfig" + ) + """Privacy configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_fairness_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIFairnessConfig" + ) + """Fairness configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_bias_mitigation_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIBiasMitigationConfig" + ) + """Bias mitigation configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_reliability_and_safety_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIReliabilityAndSafetyConfig") + ) + """Reliability and safety configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_transparency_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAITransparencyConfig" + ) + """Transparency configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_accountability_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIAccountabilityConfig" + ) + """Accountability configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_environmental_consciousness_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIEnvironmentalConsciousnessConfig") + ) + """Environmental consciousness configuration for ensuring the ethical use of an AI asset""" + + aws_arn: Union[str, None, UnsetType] = UNSET + """DEPRECATED: This legacy attribute must be unique across all AWS asset instances. This can create non-obvious edge cases for creating / updating assets, and we therefore recommended NOT using it. See and use cloudResourceName instead.""" + + aws_partition: Union[str, None, UnsetType] = UNSET + """Group of AWS region and service objects.""" + + aws_service: Union[str, None, UnsetType] = UNSET + """Type of service in which the asset exists.""" + + aws_region: Union[str, None, UnsetType] = UNSET + """Physical region where the data center in which the asset exists is clustered.""" + + aws_account_id: Union[str, None, UnsetType] = UNSET + """12-digit number that uniquely identifies an AWS account.""" + + aws_resource_id: Union[str, None, UnsetType] = UNSET + """Unique resource ID assigned when a new resource is created.""" + + aws_owner_name: Union[str, None, UnsetType] = UNSET + """Root user's name.""" + + aws_owner_id: Union[str, None, UnsetType] = UNSET + """Root user's ID.""" + + aws_tags: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of tags that have been applied to the asset in AWS.""" + + cloud_uniform_resource_name: Union[str, None, UnsetType] = UNSET + """Uniform resource name (URN) for the asset: AWS ARN, Google Cloud URI, Azure resource ID, Oracle OCID, and so on.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + sage_maker_feature_group: Union[RelatedSageMakerFeatureGroup, None, UnsetType] = ( + UNSET + ) + """SageMaker Feature Group that contains the features.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SageMakerFeature" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _sage_maker_feature_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> SageMakerFeature: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SageMakerFeature instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _sage_maker_feature_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SageMakerFeatureAttributes(AssetAttributes): + """SageMakerFeature-specific attributes for nested API format.""" + + sage_maker_group_name: Union[str, None, UnsetType] = UNSET + """Name of the Feature Group that contains this feature.""" + + sage_maker_group_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the Feature Group that contains this feature.""" + + sage_maker_data_type: Union[str, None, UnsetType] = UNSET + """Data type of the feature (e.g., String, Integral, Fractional).""" + + sage_maker_is_record_identifier: Union[bool, None, UnsetType] = UNSET + """Whether this feature serves as the record identifier for the Feature Group.""" + + sage_maker_s3_uri: Union[str, None, UnsetType] = UNSET + """Primary S3 URI associated with this SageMaker asset.""" + + ethical_ai_privacy_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIPrivacyConfig" + ) + """Privacy configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_fairness_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIFairnessConfig" + ) + """Fairness configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_bias_mitigation_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIBiasMitigationConfig" + ) + """Bias mitigation configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_reliability_and_safety_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIReliabilityAndSafetyConfig") + ) + """Reliability and safety configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_transparency_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAITransparencyConfig" + ) + """Transparency configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_accountability_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIAccountabilityConfig" + ) + """Accountability configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_environmental_consciousness_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIEnvironmentalConsciousnessConfig") + ) + """Environmental consciousness configuration for ensuring the ethical use of an AI asset""" + + aws_arn: Union[str, None, UnsetType] = UNSET + """DEPRECATED: This legacy attribute must be unique across all AWS asset instances. This can create non-obvious edge cases for creating / updating assets, and we therefore recommended NOT using it. See and use cloudResourceName instead.""" + + aws_partition: Union[str, None, UnsetType] = UNSET + """Group of AWS region and service objects.""" + + aws_service: Union[str, None, UnsetType] = UNSET + """Type of service in which the asset exists.""" + + aws_region: Union[str, None, UnsetType] = UNSET + """Physical region where the data center in which the asset exists is clustered.""" + + aws_account_id: Union[str, None, UnsetType] = UNSET + """12-digit number that uniquely identifies an AWS account.""" + + aws_resource_id: Union[str, None, UnsetType] = UNSET + """Unique resource ID assigned when a new resource is created.""" + + aws_owner_name: Union[str, None, UnsetType] = UNSET + """Root user's name.""" + + aws_owner_id: Union[str, None, UnsetType] = UNSET + """Root user's ID.""" + + aws_tags: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of tags that have been applied to the asset in AWS.""" + + cloud_uniform_resource_name: Union[str, None, UnsetType] = UNSET + """Uniform resource name (URN) for the asset: AWS ARN, Google Cloud URI, Azure resource ID, Oracle OCID, and so on.""" + + +class SageMakerFeatureRelationshipAttributes(AssetRelationshipAttributes): + """SageMakerFeature-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + sage_maker_feature_group: Union[RelatedSageMakerFeatureGroup, None, UnsetType] = ( + UNSET + ) + """SageMaker Feature Group that contains the features.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SageMakerFeatureNested(AssetNested): + """SageMakerFeature in nested API format for high-performance serialization.""" + + attributes: Union[SageMakerFeatureAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + SageMakerFeatureRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + SageMakerFeatureRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SageMakerFeatureRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SAGE_MAKER_FEATURE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "sage_maker_feature_group", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_sage_maker_feature_attrs( + attrs: SageMakerFeatureAttributes, obj: SageMakerFeature +) -> None: + """Populate SageMakerFeature-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.sage_maker_group_name = obj.sage_maker_group_name + attrs.sage_maker_group_qualified_name = obj.sage_maker_group_qualified_name + attrs.sage_maker_data_type = obj.sage_maker_data_type + attrs.sage_maker_is_record_identifier = obj.sage_maker_is_record_identifier + attrs.sage_maker_s3_uri = obj.sage_maker_s3_uri + attrs.ethical_ai_privacy_config = obj.ethical_ai_privacy_config + attrs.ethical_ai_fairness_config = obj.ethical_ai_fairness_config + attrs.ethical_ai_bias_mitigation_config = obj.ethical_ai_bias_mitigation_config + attrs.ethical_ai_reliability_and_safety_config = ( + obj.ethical_ai_reliability_and_safety_config + ) + attrs.ethical_ai_transparency_config = obj.ethical_ai_transparency_config + attrs.ethical_ai_accountability_config = obj.ethical_ai_accountability_config + attrs.ethical_ai_environmental_consciousness_config = ( + obj.ethical_ai_environmental_consciousness_config + ) + attrs.aws_arn = obj.aws_arn + attrs.aws_partition = obj.aws_partition + attrs.aws_service = obj.aws_service + attrs.aws_region = obj.aws_region + attrs.aws_account_id = obj.aws_account_id + attrs.aws_resource_id = obj.aws_resource_id + attrs.aws_owner_name = obj.aws_owner_name + attrs.aws_owner_id = obj.aws_owner_id + attrs.aws_tags = obj.aws_tags + attrs.cloud_uniform_resource_name = obj.cloud_uniform_resource_name + + +def _extract_sage_maker_feature_attrs(attrs: SageMakerFeatureAttributes) -> dict: + """Extract all SageMakerFeature attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["sage_maker_group_name"] = attrs.sage_maker_group_name + result["sage_maker_group_qualified_name"] = attrs.sage_maker_group_qualified_name + result["sage_maker_data_type"] = attrs.sage_maker_data_type + result["sage_maker_is_record_identifier"] = attrs.sage_maker_is_record_identifier + result["sage_maker_s3_uri"] = attrs.sage_maker_s3_uri + result["ethical_ai_privacy_config"] = attrs.ethical_ai_privacy_config + result["ethical_ai_fairness_config"] = attrs.ethical_ai_fairness_config + result["ethical_ai_bias_mitigation_config"] = ( + attrs.ethical_ai_bias_mitigation_config + ) + result["ethical_ai_reliability_and_safety_config"] = ( + attrs.ethical_ai_reliability_and_safety_config + ) + result["ethical_ai_transparency_config"] = attrs.ethical_ai_transparency_config + result["ethical_ai_accountability_config"] = attrs.ethical_ai_accountability_config + result["ethical_ai_environmental_consciousness_config"] = ( + attrs.ethical_ai_environmental_consciousness_config + ) + result["aws_arn"] = attrs.aws_arn + result["aws_partition"] = attrs.aws_partition + result["aws_service"] = attrs.aws_service + result["aws_region"] = attrs.aws_region + result["aws_account_id"] = attrs.aws_account_id + result["aws_resource_id"] = attrs.aws_resource_id + result["aws_owner_name"] = attrs.aws_owner_name + result["aws_owner_id"] = attrs.aws_owner_id + result["aws_tags"] = attrs.aws_tags + result["cloud_uniform_resource_name"] = attrs.cloud_uniform_resource_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _sage_maker_feature_to_nested( + sage_maker_feature: SageMakerFeature, +) -> SageMakerFeatureNested: + """Convert flat SageMakerFeature to nested format.""" + attrs = SageMakerFeatureAttributes() + _populate_sage_maker_feature_attrs(attrs, sage_maker_feature) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + sage_maker_feature, + _SAGE_MAKER_FEATURE_REL_FIELDS, + SageMakerFeatureRelationshipAttributes, + ) + return SageMakerFeatureNested( + guid=sage_maker_feature.guid, + type_name=sage_maker_feature.type_name, + status=sage_maker_feature.status, + version=sage_maker_feature.version, + create_time=sage_maker_feature.create_time, + update_time=sage_maker_feature.update_time, + created_by=sage_maker_feature.created_by, + updated_by=sage_maker_feature.updated_by, + classifications=sage_maker_feature.classifications, + classification_names=sage_maker_feature.classification_names, + meanings=sage_maker_feature.meanings, + labels=sage_maker_feature.labels, + business_attributes=sage_maker_feature.business_attributes, + custom_attributes=sage_maker_feature.custom_attributes, + pending_tasks=sage_maker_feature.pending_tasks, + proxy=sage_maker_feature.proxy, + is_incomplete=sage_maker_feature.is_incomplete, + provenance_type=sage_maker_feature.provenance_type, + home_id=sage_maker_feature.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _sage_maker_feature_from_nested(nested: SageMakerFeatureNested) -> SageMakerFeature: + """Convert nested format to flat SageMakerFeature.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else SageMakerFeatureAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SAGE_MAKER_FEATURE_REL_FIELDS, + SageMakerFeatureRelationshipAttributes, + ) + return SageMakerFeature( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_sage_maker_feature_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _sage_maker_feature_to_nested_bytes( + sage_maker_feature: SageMakerFeature, serde: Serde +) -> bytes: + """Convert flat SageMakerFeature to nested JSON bytes.""" + return serde.encode(_sage_maker_feature_to_nested(sage_maker_feature)) + + +def _sage_maker_feature_from_nested_bytes( + data: bytes, serde: Serde +) -> SageMakerFeature: + """Convert nested JSON bytes to flat SageMakerFeature.""" + nested = serde.decode(data, SageMakerFeatureNested) + return _sage_maker_feature_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + RelationField, +) + +SageMakerFeature.SAGE_MAKER_GROUP_NAME = KeywordField( + "sageMakerGroupName", "sageMakerGroupName" +) +SageMakerFeature.SAGE_MAKER_GROUP_QUALIFIED_NAME = KeywordField( + "sageMakerGroupQualifiedName", "sageMakerGroupQualifiedName" +) +SageMakerFeature.SAGE_MAKER_DATA_TYPE = KeywordField( + "sageMakerDataType", "sageMakerDataType" +) +SageMakerFeature.SAGE_MAKER_IS_RECORD_IDENTIFIER = BooleanField( + "sageMakerIsRecordIdentifier", "sageMakerIsRecordIdentifier" +) +SageMakerFeature.SAGE_MAKER_S3_URI = KeywordField("sageMakerS3Uri", "sageMakerS3Uri") +SageMakerFeature.ETHICAL_AI_PRIVACY_CONFIG = KeywordField( + "ethicalAIPrivacyConfig", "ethicalAIPrivacyConfig" +) +SageMakerFeature.ETHICAL_AI_FAIRNESS_CONFIG = KeywordField( + "ethicalAIFairnessConfig", "ethicalAIFairnessConfig" +) +SageMakerFeature.ETHICAL_AI_BIAS_MITIGATION_CONFIG = KeywordField( + "ethicalAIBiasMitigationConfig", "ethicalAIBiasMitigationConfig" +) +SageMakerFeature.ETHICAL_AI_RELIABILITY_AND_SAFETY_CONFIG = KeywordField( + "ethicalAIReliabilityAndSafetyConfig", "ethicalAIReliabilityAndSafetyConfig" +) +SageMakerFeature.ETHICAL_AI_TRANSPARENCY_CONFIG = KeywordField( + "ethicalAITransparencyConfig", "ethicalAITransparencyConfig" +) +SageMakerFeature.ETHICAL_AI_ACCOUNTABILITY_CONFIG = KeywordField( + "ethicalAIAccountabilityConfig", "ethicalAIAccountabilityConfig" +) +SageMakerFeature.ETHICAL_AI_ENVIRONMENTAL_CONSCIOUSNESS_CONFIG = KeywordField( + "ethicalAIEnvironmentalConsciousnessConfig", + "ethicalAIEnvironmentalConsciousnessConfig", +) +SageMakerFeature.AWS_ARN = KeywordTextField("awsArn", "awsArn", "awsArn.text") +SageMakerFeature.AWS_PARTITION = KeywordField("awsPartition", "awsPartition") +SageMakerFeature.AWS_SERVICE = KeywordField("awsService", "awsService") +SageMakerFeature.AWS_REGION = KeywordField("awsRegion", "awsRegion") +SageMakerFeature.AWS_ACCOUNT_ID = KeywordField("awsAccountId", "awsAccountId") +SageMakerFeature.AWS_RESOURCE_ID = KeywordField("awsResourceId", "awsResourceId") +SageMakerFeature.AWS_OWNER_NAME = KeywordTextField( + "awsOwnerName", "awsOwnerName", "awsOwnerName.text" +) +SageMakerFeature.AWS_OWNER_ID = KeywordField("awsOwnerId", "awsOwnerId") +SageMakerFeature.AWS_TAGS = KeywordField("awsTags", "awsTags") +SageMakerFeature.CLOUD_UNIFORM_RESOURCE_NAME = KeywordField( + "cloudUniformResourceName", "cloudUniformResourceName" +) +SageMakerFeature.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SageMakerFeature.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +SageMakerFeature.ANOMALO_CHECKS = RelationField("anomaloChecks") +SageMakerFeature.APPLICATION = RelationField("application") +SageMakerFeature.APPLICATION_FIELD = RelationField("applicationField") +SageMakerFeature.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +SageMakerFeature.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +SageMakerFeature.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +SageMakerFeature.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +SageMakerFeature.METRICS = RelationField("metrics") +SageMakerFeature.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SageMakerFeature.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +SageMakerFeature.MEANINGS = RelationField("meanings") +SageMakerFeature.MC_MONITORS = RelationField("mcMonitors") +SageMakerFeature.MC_INCIDENTS = RelationField("mcIncidents") +SageMakerFeature.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SageMakerFeature.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SageMakerFeature.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SageMakerFeature.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SageMakerFeature.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +SageMakerFeature.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +SageMakerFeature.FILES = RelationField("files") +SageMakerFeature.LINKS = RelationField("links") +SageMakerFeature.README = RelationField("readme") +SageMakerFeature.SAGE_MAKER_FEATURE_GROUP = RelationField("sageMakerFeatureGroup") +SageMakerFeature.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +SageMakerFeature.SODA_CHECKS = RelationField("sodaChecks") +SageMakerFeature.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SageMakerFeature.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/sage_maker_feature_group.py b/pyatlan_v9/model/assets/sage_maker_feature_group.py new file mode 100644 index 000000000..d198a17ec --- /dev/null +++ b/pyatlan_v9/model/assets/sage_maker_feature_group.py @@ -0,0 +1,872 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SageMakerFeatureGroup asset model with flattened inheritance. + +This module provides: +- SageMakerFeatureGroup: Flat asset class (easy to use) +- SageMakerFeatureGroupAttributes: Nested attributes struct (extends AssetAttributes) +- SageMakerFeatureGroupNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .sage_maker_related import RelatedSageMakerFeature + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SageMakerFeatureGroup(Asset): + """ + Instance of a SageMaker Feature Store Feature Group in Atlan. Represents a collection of related features that can be used for machine learning training and inference. + """ + + SAGE_MAKER_STATUS: ClassVar[Any] = None + SAGE_MAKER_RECORD_ID_NAME: ClassVar[Any] = None + SAGE_MAKER_GLUE_DATABASE_NAME: ClassVar[Any] = None + SAGE_MAKER_GLUE_TABLE_NAME: ClassVar[Any] = None + SAGE_MAKER_FEATURE_COUNT: ClassVar[Any] = None + SAGE_MAKER_S3_URI: ClassVar[Any] = None + ETHICAL_AI_PRIVACY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_FAIRNESS_CONFIG: ClassVar[Any] = None + ETHICAL_AI_BIAS_MITIGATION_CONFIG: ClassVar[Any] = None + ETHICAL_AI_RELIABILITY_AND_SAFETY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_TRANSPARENCY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_ACCOUNTABILITY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_ENVIRONMENTAL_CONSCIOUSNESS_CONFIG: ClassVar[Any] = None + AWS_ARN: ClassVar[Any] = None + AWS_PARTITION: ClassVar[Any] = None + AWS_SERVICE: ClassVar[Any] = None + AWS_REGION: ClassVar[Any] = None + AWS_ACCOUNT_ID: ClassVar[Any] = None + AWS_RESOURCE_ID: ClassVar[Any] = None + AWS_OWNER_NAME: ClassVar[Any] = None + AWS_OWNER_ID: ClassVar[Any] = None + AWS_TAGS: ClassVar[Any] = None + CLOUD_UNIFORM_RESOURCE_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SAGE_MAKER_FEATURES: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SageMakerFeatureGroup" + + sage_maker_status: Union[str, None, UnsetType] = UNSET + """Current status of the Feature Group (e.g., Created, Creating, Failed).""" + + sage_maker_record_id_name: Union[str, None, UnsetType] = UNSET + """Name of the feature that serves as the record identifier.""" + + sage_maker_glue_database_name: Union[str, None, UnsetType] = UNSET + """AWS Glue database name associated with this Feature Group.""" + + sage_maker_glue_table_name: Union[str, None, UnsetType] = UNSET + """AWS Glue table name associated with this Feature Group.""" + + sage_maker_feature_count: Union[int, None, UnsetType] = UNSET + """Number of features in this Feature Group.""" + + sage_maker_s3_uri: Union[str, None, UnsetType] = UNSET + """Primary S3 URI associated with this SageMaker asset.""" + + ethical_ai_privacy_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIPrivacyConfig" + ) + """Privacy configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_fairness_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIFairnessConfig" + ) + """Fairness configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_bias_mitigation_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIBiasMitigationConfig" + ) + """Bias mitigation configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_reliability_and_safety_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIReliabilityAndSafetyConfig") + ) + """Reliability and safety configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_transparency_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAITransparencyConfig" + ) + """Transparency configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_accountability_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIAccountabilityConfig" + ) + """Accountability configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_environmental_consciousness_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIEnvironmentalConsciousnessConfig") + ) + """Environmental consciousness configuration for ensuring the ethical use of an AI asset""" + + aws_arn: Union[str, None, UnsetType] = UNSET + """DEPRECATED: This legacy attribute must be unique across all AWS asset instances. This can create non-obvious edge cases for creating / updating assets, and we therefore recommended NOT using it. See and use cloudResourceName instead.""" + + aws_partition: Union[str, None, UnsetType] = UNSET + """Group of AWS region and service objects.""" + + aws_service: Union[str, None, UnsetType] = UNSET + """Type of service in which the asset exists.""" + + aws_region: Union[str, None, UnsetType] = UNSET + """Physical region where the data center in which the asset exists is clustered.""" + + aws_account_id: Union[str, None, UnsetType] = UNSET + """12-digit number that uniquely identifies an AWS account.""" + + aws_resource_id: Union[str, None, UnsetType] = UNSET + """Unique resource ID assigned when a new resource is created.""" + + aws_owner_name: Union[str, None, UnsetType] = UNSET + """Root user's name.""" + + aws_owner_id: Union[str, None, UnsetType] = UNSET + """Root user's ID.""" + + aws_tags: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of tags that have been applied to the asset in AWS.""" + + cloud_uniform_resource_name: Union[str, None, UnsetType] = UNSET + """Uniform resource name (URN) for the asset: AWS ARN, Google Cloud URI, Azure resource ID, Oracle OCID, and so on.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + sage_maker_features: Union[List[RelatedSageMakerFeature], None, UnsetType] = UNSET + """Features that are defined within the SageMaker Feature Group.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SageMakerFeatureGroup" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _sage_maker_feature_group_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> SageMakerFeatureGroup: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SageMakerFeatureGroup instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _sage_maker_feature_group_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SageMakerFeatureGroupAttributes(AssetAttributes): + """SageMakerFeatureGroup-specific attributes for nested API format.""" + + sage_maker_status: Union[str, None, UnsetType] = UNSET + """Current status of the Feature Group (e.g., Created, Creating, Failed).""" + + sage_maker_record_id_name: Union[str, None, UnsetType] = UNSET + """Name of the feature that serves as the record identifier.""" + + sage_maker_glue_database_name: Union[str, None, UnsetType] = UNSET + """AWS Glue database name associated with this Feature Group.""" + + sage_maker_glue_table_name: Union[str, None, UnsetType] = UNSET + """AWS Glue table name associated with this Feature Group.""" + + sage_maker_feature_count: Union[int, None, UnsetType] = UNSET + """Number of features in this Feature Group.""" + + sage_maker_s3_uri: Union[str, None, UnsetType] = UNSET + """Primary S3 URI associated with this SageMaker asset.""" + + ethical_ai_privacy_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIPrivacyConfig" + ) + """Privacy configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_fairness_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIFairnessConfig" + ) + """Fairness configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_bias_mitigation_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIBiasMitigationConfig" + ) + """Bias mitigation configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_reliability_and_safety_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIReliabilityAndSafetyConfig") + ) + """Reliability and safety configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_transparency_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAITransparencyConfig" + ) + """Transparency configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_accountability_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIAccountabilityConfig" + ) + """Accountability configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_environmental_consciousness_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIEnvironmentalConsciousnessConfig") + ) + """Environmental consciousness configuration for ensuring the ethical use of an AI asset""" + + aws_arn: Union[str, None, UnsetType] = UNSET + """DEPRECATED: This legacy attribute must be unique across all AWS asset instances. This can create non-obvious edge cases for creating / updating assets, and we therefore recommended NOT using it. See and use cloudResourceName instead.""" + + aws_partition: Union[str, None, UnsetType] = UNSET + """Group of AWS region and service objects.""" + + aws_service: Union[str, None, UnsetType] = UNSET + """Type of service in which the asset exists.""" + + aws_region: Union[str, None, UnsetType] = UNSET + """Physical region where the data center in which the asset exists is clustered.""" + + aws_account_id: Union[str, None, UnsetType] = UNSET + """12-digit number that uniquely identifies an AWS account.""" + + aws_resource_id: Union[str, None, UnsetType] = UNSET + """Unique resource ID assigned when a new resource is created.""" + + aws_owner_name: Union[str, None, UnsetType] = UNSET + """Root user's name.""" + + aws_owner_id: Union[str, None, UnsetType] = UNSET + """Root user's ID.""" + + aws_tags: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of tags that have been applied to the asset in AWS.""" + + cloud_uniform_resource_name: Union[str, None, UnsetType] = UNSET + """Uniform resource name (URN) for the asset: AWS ARN, Google Cloud URI, Azure resource ID, Oracle OCID, and so on.""" + + +class SageMakerFeatureGroupRelationshipAttributes(AssetRelationshipAttributes): + """SageMakerFeatureGroup-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + sage_maker_features: Union[List[RelatedSageMakerFeature], None, UnsetType] = UNSET + """Features that are defined within the SageMaker Feature Group.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SageMakerFeatureGroupNested(AssetNested): + """SageMakerFeatureGroup in nested API format for high-performance serialization.""" + + attributes: Union[SageMakerFeatureGroupAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + SageMakerFeatureGroupRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + SageMakerFeatureGroupRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SageMakerFeatureGroupRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SAGE_MAKER_FEATURE_GROUP_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "sage_maker_features", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_sage_maker_feature_group_attrs( + attrs: SageMakerFeatureGroupAttributes, obj: SageMakerFeatureGroup +) -> None: + """Populate SageMakerFeatureGroup-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.sage_maker_status = obj.sage_maker_status + attrs.sage_maker_record_id_name = obj.sage_maker_record_id_name + attrs.sage_maker_glue_database_name = obj.sage_maker_glue_database_name + attrs.sage_maker_glue_table_name = obj.sage_maker_glue_table_name + attrs.sage_maker_feature_count = obj.sage_maker_feature_count + attrs.sage_maker_s3_uri = obj.sage_maker_s3_uri + attrs.ethical_ai_privacy_config = obj.ethical_ai_privacy_config + attrs.ethical_ai_fairness_config = obj.ethical_ai_fairness_config + attrs.ethical_ai_bias_mitigation_config = obj.ethical_ai_bias_mitigation_config + attrs.ethical_ai_reliability_and_safety_config = ( + obj.ethical_ai_reliability_and_safety_config + ) + attrs.ethical_ai_transparency_config = obj.ethical_ai_transparency_config + attrs.ethical_ai_accountability_config = obj.ethical_ai_accountability_config + attrs.ethical_ai_environmental_consciousness_config = ( + obj.ethical_ai_environmental_consciousness_config + ) + attrs.aws_arn = obj.aws_arn + attrs.aws_partition = obj.aws_partition + attrs.aws_service = obj.aws_service + attrs.aws_region = obj.aws_region + attrs.aws_account_id = obj.aws_account_id + attrs.aws_resource_id = obj.aws_resource_id + attrs.aws_owner_name = obj.aws_owner_name + attrs.aws_owner_id = obj.aws_owner_id + attrs.aws_tags = obj.aws_tags + attrs.cloud_uniform_resource_name = obj.cloud_uniform_resource_name + + +def _extract_sage_maker_feature_group_attrs( + attrs: SageMakerFeatureGroupAttributes, +) -> dict: + """Extract all SageMakerFeatureGroup attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["sage_maker_status"] = attrs.sage_maker_status + result["sage_maker_record_id_name"] = attrs.sage_maker_record_id_name + result["sage_maker_glue_database_name"] = attrs.sage_maker_glue_database_name + result["sage_maker_glue_table_name"] = attrs.sage_maker_glue_table_name + result["sage_maker_feature_count"] = attrs.sage_maker_feature_count + result["sage_maker_s3_uri"] = attrs.sage_maker_s3_uri + result["ethical_ai_privacy_config"] = attrs.ethical_ai_privacy_config + result["ethical_ai_fairness_config"] = attrs.ethical_ai_fairness_config + result["ethical_ai_bias_mitigation_config"] = ( + attrs.ethical_ai_bias_mitigation_config + ) + result["ethical_ai_reliability_and_safety_config"] = ( + attrs.ethical_ai_reliability_and_safety_config + ) + result["ethical_ai_transparency_config"] = attrs.ethical_ai_transparency_config + result["ethical_ai_accountability_config"] = attrs.ethical_ai_accountability_config + result["ethical_ai_environmental_consciousness_config"] = ( + attrs.ethical_ai_environmental_consciousness_config + ) + result["aws_arn"] = attrs.aws_arn + result["aws_partition"] = attrs.aws_partition + result["aws_service"] = attrs.aws_service + result["aws_region"] = attrs.aws_region + result["aws_account_id"] = attrs.aws_account_id + result["aws_resource_id"] = attrs.aws_resource_id + result["aws_owner_name"] = attrs.aws_owner_name + result["aws_owner_id"] = attrs.aws_owner_id + result["aws_tags"] = attrs.aws_tags + result["cloud_uniform_resource_name"] = attrs.cloud_uniform_resource_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _sage_maker_feature_group_to_nested( + sage_maker_feature_group: SageMakerFeatureGroup, +) -> SageMakerFeatureGroupNested: + """Convert flat SageMakerFeatureGroup to nested format.""" + attrs = SageMakerFeatureGroupAttributes() + _populate_sage_maker_feature_group_attrs(attrs, sage_maker_feature_group) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + sage_maker_feature_group, + _SAGE_MAKER_FEATURE_GROUP_REL_FIELDS, + SageMakerFeatureGroupRelationshipAttributes, + ) + return SageMakerFeatureGroupNested( + guid=sage_maker_feature_group.guid, + type_name=sage_maker_feature_group.type_name, + status=sage_maker_feature_group.status, + version=sage_maker_feature_group.version, + create_time=sage_maker_feature_group.create_time, + update_time=sage_maker_feature_group.update_time, + created_by=sage_maker_feature_group.created_by, + updated_by=sage_maker_feature_group.updated_by, + classifications=sage_maker_feature_group.classifications, + classification_names=sage_maker_feature_group.classification_names, + meanings=sage_maker_feature_group.meanings, + labels=sage_maker_feature_group.labels, + business_attributes=sage_maker_feature_group.business_attributes, + custom_attributes=sage_maker_feature_group.custom_attributes, + pending_tasks=sage_maker_feature_group.pending_tasks, + proxy=sage_maker_feature_group.proxy, + is_incomplete=sage_maker_feature_group.is_incomplete, + provenance_type=sage_maker_feature_group.provenance_type, + home_id=sage_maker_feature_group.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _sage_maker_feature_group_from_nested( + nested: SageMakerFeatureGroupNested, +) -> SageMakerFeatureGroup: + """Convert nested format to flat SageMakerFeatureGroup.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else SageMakerFeatureGroupAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SAGE_MAKER_FEATURE_GROUP_REL_FIELDS, + SageMakerFeatureGroupRelationshipAttributes, + ) + return SageMakerFeatureGroup( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_sage_maker_feature_group_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _sage_maker_feature_group_to_nested_bytes( + sage_maker_feature_group: SageMakerFeatureGroup, serde: Serde +) -> bytes: + """Convert flat SageMakerFeatureGroup to nested JSON bytes.""" + return serde.encode(_sage_maker_feature_group_to_nested(sage_maker_feature_group)) + + +def _sage_maker_feature_group_from_nested_bytes( + data: bytes, serde: Serde +) -> SageMakerFeatureGroup: + """Convert nested JSON bytes to flat SageMakerFeatureGroup.""" + nested = serde.decode(data, SageMakerFeatureGroupNested) + return _sage_maker_feature_group_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +SageMakerFeatureGroup.SAGE_MAKER_STATUS = KeywordField( + "sageMakerStatus", "sageMakerStatus" +) +SageMakerFeatureGroup.SAGE_MAKER_RECORD_ID_NAME = KeywordField( + "sageMakerRecordIdName", "sageMakerRecordIdName" +) +SageMakerFeatureGroup.SAGE_MAKER_GLUE_DATABASE_NAME = KeywordField( + "sageMakerGlueDatabaseName", "sageMakerGlueDatabaseName" +) +SageMakerFeatureGroup.SAGE_MAKER_GLUE_TABLE_NAME = KeywordField( + "sageMakerGlueTableName", "sageMakerGlueTableName" +) +SageMakerFeatureGroup.SAGE_MAKER_FEATURE_COUNT = NumericField( + "sageMakerFeatureCount", "sageMakerFeatureCount" +) +SageMakerFeatureGroup.SAGE_MAKER_S3_URI = KeywordField( + "sageMakerS3Uri", "sageMakerS3Uri" +) +SageMakerFeatureGroup.ETHICAL_AI_PRIVACY_CONFIG = KeywordField( + "ethicalAIPrivacyConfig", "ethicalAIPrivacyConfig" +) +SageMakerFeatureGroup.ETHICAL_AI_FAIRNESS_CONFIG = KeywordField( + "ethicalAIFairnessConfig", "ethicalAIFairnessConfig" +) +SageMakerFeatureGroup.ETHICAL_AI_BIAS_MITIGATION_CONFIG = KeywordField( + "ethicalAIBiasMitigationConfig", "ethicalAIBiasMitigationConfig" +) +SageMakerFeatureGroup.ETHICAL_AI_RELIABILITY_AND_SAFETY_CONFIG = KeywordField( + "ethicalAIReliabilityAndSafetyConfig", "ethicalAIReliabilityAndSafetyConfig" +) +SageMakerFeatureGroup.ETHICAL_AI_TRANSPARENCY_CONFIG = KeywordField( + "ethicalAITransparencyConfig", "ethicalAITransparencyConfig" +) +SageMakerFeatureGroup.ETHICAL_AI_ACCOUNTABILITY_CONFIG = KeywordField( + "ethicalAIAccountabilityConfig", "ethicalAIAccountabilityConfig" +) +SageMakerFeatureGroup.ETHICAL_AI_ENVIRONMENTAL_CONSCIOUSNESS_CONFIG = KeywordField( + "ethicalAIEnvironmentalConsciousnessConfig", + "ethicalAIEnvironmentalConsciousnessConfig", +) +SageMakerFeatureGroup.AWS_ARN = KeywordTextField("awsArn", "awsArn", "awsArn.text") +SageMakerFeatureGroup.AWS_PARTITION = KeywordField("awsPartition", "awsPartition") +SageMakerFeatureGroup.AWS_SERVICE = KeywordField("awsService", "awsService") +SageMakerFeatureGroup.AWS_REGION = KeywordField("awsRegion", "awsRegion") +SageMakerFeatureGroup.AWS_ACCOUNT_ID = KeywordField("awsAccountId", "awsAccountId") +SageMakerFeatureGroup.AWS_RESOURCE_ID = KeywordField("awsResourceId", "awsResourceId") +SageMakerFeatureGroup.AWS_OWNER_NAME = KeywordTextField( + "awsOwnerName", "awsOwnerName", "awsOwnerName.text" +) +SageMakerFeatureGroup.AWS_OWNER_ID = KeywordField("awsOwnerId", "awsOwnerId") +SageMakerFeatureGroup.AWS_TAGS = KeywordField("awsTags", "awsTags") +SageMakerFeatureGroup.CLOUD_UNIFORM_RESOURCE_NAME = KeywordField( + "cloudUniformResourceName", "cloudUniformResourceName" +) +SageMakerFeatureGroup.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SageMakerFeatureGroup.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +SageMakerFeatureGroup.ANOMALO_CHECKS = RelationField("anomaloChecks") +SageMakerFeatureGroup.APPLICATION = RelationField("application") +SageMakerFeatureGroup.APPLICATION_FIELD = RelationField("applicationField") +SageMakerFeatureGroup.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +SageMakerFeatureGroup.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +SageMakerFeatureGroup.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +SageMakerFeatureGroup.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +SageMakerFeatureGroup.METRICS = RelationField("metrics") +SageMakerFeatureGroup.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SageMakerFeatureGroup.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +SageMakerFeatureGroup.MEANINGS = RelationField("meanings") +SageMakerFeatureGroup.MC_MONITORS = RelationField("mcMonitors") +SageMakerFeatureGroup.MC_INCIDENTS = RelationField("mcIncidents") +SageMakerFeatureGroup.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SageMakerFeatureGroup.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SageMakerFeatureGroup.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SageMakerFeatureGroup.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SageMakerFeatureGroup.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +SageMakerFeatureGroup.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +SageMakerFeatureGroup.FILES = RelationField("files") +SageMakerFeatureGroup.LINKS = RelationField("links") +SageMakerFeatureGroup.README = RelationField("readme") +SageMakerFeatureGroup.SAGE_MAKER_FEATURES = RelationField("sageMakerFeatures") +SageMakerFeatureGroup.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +SageMakerFeatureGroup.SODA_CHECKS = RelationField("sodaChecks") +SageMakerFeatureGroup.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SageMakerFeatureGroup.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/sage_maker_model.py b/pyatlan_v9/model/assets/sage_maker_model.py new file mode 100644 index 000000000..8fb530af1 --- /dev/null +++ b/pyatlan_v9/model/assets/sage_maker_model.py @@ -0,0 +1,896 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SageMakerModel asset model with flattened inheritance. + +This module provides: +- SageMakerModel: Flat asset class (easy to use) +- SageMakerModelAttributes: Nested attributes struct (extends AssetAttributes) +- SageMakerModelNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .ai_related import RelatedAIModel +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .sage_maker_related import ( + RelatedSageMakerModelDeployment, + RelatedSageMakerModelGroup, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SageMakerModel(Asset): + """ + Instance of a SageMaker ML Model in Atlan. Represents trained machine learning models that can be deployed for inference. + """ + + SAGE_MAKER_CONTAINER_IMAGE: ClassVar[Any] = None + SAGE_MAKER_EXECUTION_ROLE_ARN: ClassVar[Any] = None + SAGE_MAKER_MODEL_GROUP_NAME: ClassVar[Any] = None + SAGE_MAKER_MODEL_GROUP_QUALIFIED_NAME: ClassVar[Any] = None + SAGE_MAKER_VERSION: ClassVar[Any] = None + SAGE_MAKER_STATUS: ClassVar[Any] = None + SAGE_MAKER_S3_URI: ClassVar[Any] = None + ETHICAL_AI_PRIVACY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_FAIRNESS_CONFIG: ClassVar[Any] = None + ETHICAL_AI_BIAS_MITIGATION_CONFIG: ClassVar[Any] = None + ETHICAL_AI_RELIABILITY_AND_SAFETY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_TRANSPARENCY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_ACCOUNTABILITY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_ENVIRONMENTAL_CONSCIOUSNESS_CONFIG: ClassVar[Any] = None + AWS_ARN: ClassVar[Any] = None + AWS_PARTITION: ClassVar[Any] = None + AWS_SERVICE: ClassVar[Any] = None + AWS_REGION: ClassVar[Any] = None + AWS_ACCOUNT_ID: ClassVar[Any] = None + AWS_RESOURCE_ID: ClassVar[Any] = None + AWS_OWNER_NAME: ClassVar[Any] = None + AWS_OWNER_ID: ClassVar[Any] = None + AWS_TAGS: ClassVar[Any] = None + CLOUD_UNIFORM_RESOURCE_NAME: ClassVar[Any] = None + AI_MODEL: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SAGE_MAKER_MODEL_GROUP: ClassVar[Any] = None + SAGE_MAKER_MODEL_DEPLOYMENTS: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SageMakerModel" + + sage_maker_container_image: Union[str, None, UnsetType] = UNSET + """Docker container image used for the model.""" + + sage_maker_execution_role_arn: Union[str, None, UnsetType] = UNSET + """ARN of the IAM role used by the model for accessing AWS resources.""" + + sage_maker_model_group_name: Union[str, None, UnsetType] = UNSET + """Name of the parent Model Group.""" + + sage_maker_model_group_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the parent Model Group.""" + + sage_maker_version: Union[str, None, UnsetType] = UNSET + """Version of the SageMaker Model Package.""" + + sage_maker_status: Union[str, None, UnsetType] = UNSET + """Status of the SageMaker Model Package (ACTIVE or INACTIVE).""" + + sage_maker_s3_uri: Union[str, None, UnsetType] = UNSET + """Primary S3 URI associated with this SageMaker asset.""" + + ethical_ai_privacy_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIPrivacyConfig" + ) + """Privacy configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_fairness_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIFairnessConfig" + ) + """Fairness configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_bias_mitigation_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIBiasMitigationConfig" + ) + """Bias mitigation configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_reliability_and_safety_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIReliabilityAndSafetyConfig") + ) + """Reliability and safety configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_transparency_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAITransparencyConfig" + ) + """Transparency configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_accountability_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIAccountabilityConfig" + ) + """Accountability configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_environmental_consciousness_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIEnvironmentalConsciousnessConfig") + ) + """Environmental consciousness configuration for ensuring the ethical use of an AI asset""" + + aws_arn: Union[str, None, UnsetType] = UNSET + """DEPRECATED: This legacy attribute must be unique across all AWS asset instances. This can create non-obvious edge cases for creating / updating assets, and we therefore recommended NOT using it. See and use cloudResourceName instead.""" + + aws_partition: Union[str, None, UnsetType] = UNSET + """Group of AWS region and service objects.""" + + aws_service: Union[str, None, UnsetType] = UNSET + """Type of service in which the asset exists.""" + + aws_region: Union[str, None, UnsetType] = UNSET + """Physical region where the data center in which the asset exists is clustered.""" + + aws_account_id: Union[str, None, UnsetType] = UNSET + """12-digit number that uniquely identifies an AWS account.""" + + aws_resource_id: Union[str, None, UnsetType] = UNSET + """Unique resource ID assigned when a new resource is created.""" + + aws_owner_name: Union[str, None, UnsetType] = UNSET + """Root user's name.""" + + aws_owner_id: Union[str, None, UnsetType] = UNSET + """Root user's ID.""" + + aws_tags: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of tags that have been applied to the asset in AWS.""" + + cloud_uniform_resource_name: Union[str, None, UnsetType] = UNSET + """Uniform resource name (URN) for the asset: AWS ARN, Google Cloud URI, Azure resource ID, Oracle OCID, and so on.""" + + ai_model: Union[RelatedAIModel, None, UnsetType] = UNSET + """Model containing the versions.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + sage_maker_model_group: Union[RelatedSageMakerModelGroup, None, UnsetType] = UNSET + """SageMaker Model Group that contains the models.""" + + sage_maker_model_deployments: Union[ + List[RelatedSageMakerModelDeployment], None, UnsetType + ] = UNSET + """Deployments (endpoints) of this SageMaker Model.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SageMakerModel" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _sage_maker_model_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> SageMakerModel: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SageMakerModel instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _sage_maker_model_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SageMakerModelAttributes(AssetAttributes): + """SageMakerModel-specific attributes for nested API format.""" + + sage_maker_container_image: Union[str, None, UnsetType] = UNSET + """Docker container image used for the model.""" + + sage_maker_execution_role_arn: Union[str, None, UnsetType] = UNSET + """ARN of the IAM role used by the model for accessing AWS resources.""" + + sage_maker_model_group_name: Union[str, None, UnsetType] = UNSET + """Name of the parent Model Group.""" + + sage_maker_model_group_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the parent Model Group.""" + + sage_maker_version: Union[str, None, UnsetType] = UNSET + """Version of the SageMaker Model Package.""" + + sage_maker_status: Union[str, None, UnsetType] = UNSET + """Status of the SageMaker Model Package (ACTIVE or INACTIVE).""" + + sage_maker_s3_uri: Union[str, None, UnsetType] = UNSET + """Primary S3 URI associated with this SageMaker asset.""" + + ethical_ai_privacy_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIPrivacyConfig" + ) + """Privacy configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_fairness_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIFairnessConfig" + ) + """Fairness configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_bias_mitigation_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIBiasMitigationConfig" + ) + """Bias mitigation configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_reliability_and_safety_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIReliabilityAndSafetyConfig") + ) + """Reliability and safety configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_transparency_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAITransparencyConfig" + ) + """Transparency configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_accountability_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIAccountabilityConfig" + ) + """Accountability configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_environmental_consciousness_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIEnvironmentalConsciousnessConfig") + ) + """Environmental consciousness configuration for ensuring the ethical use of an AI asset""" + + aws_arn: Union[str, None, UnsetType] = UNSET + """DEPRECATED: This legacy attribute must be unique across all AWS asset instances. This can create non-obvious edge cases for creating / updating assets, and we therefore recommended NOT using it. See and use cloudResourceName instead.""" + + aws_partition: Union[str, None, UnsetType] = UNSET + """Group of AWS region and service objects.""" + + aws_service: Union[str, None, UnsetType] = UNSET + """Type of service in which the asset exists.""" + + aws_region: Union[str, None, UnsetType] = UNSET + """Physical region where the data center in which the asset exists is clustered.""" + + aws_account_id: Union[str, None, UnsetType] = UNSET + """12-digit number that uniquely identifies an AWS account.""" + + aws_resource_id: Union[str, None, UnsetType] = UNSET + """Unique resource ID assigned when a new resource is created.""" + + aws_owner_name: Union[str, None, UnsetType] = UNSET + """Root user's name.""" + + aws_owner_id: Union[str, None, UnsetType] = UNSET + """Root user's ID.""" + + aws_tags: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of tags that have been applied to the asset in AWS.""" + + cloud_uniform_resource_name: Union[str, None, UnsetType] = UNSET + """Uniform resource name (URN) for the asset: AWS ARN, Google Cloud URI, Azure resource ID, Oracle OCID, and so on.""" + + +class SageMakerModelRelationshipAttributes(AssetRelationshipAttributes): + """SageMakerModel-specific relationship attributes for nested API format.""" + + ai_model: Union[RelatedAIModel, None, UnsetType] = UNSET + """Model containing the versions.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + sage_maker_model_group: Union[RelatedSageMakerModelGroup, None, UnsetType] = UNSET + """SageMaker Model Group that contains the models.""" + + sage_maker_model_deployments: Union[ + List[RelatedSageMakerModelDeployment], None, UnsetType + ] = UNSET + """Deployments (endpoints) of this SageMaker Model.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SageMakerModelNested(AssetNested): + """SageMakerModel in nested API format for high-performance serialization.""" + + attributes: Union[SageMakerModelAttributes, UnsetType] = UNSET + relationship_attributes: Union[SageMakerModelRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + SageMakerModelRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SageMakerModelRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SAGE_MAKER_MODEL_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "ai_model", + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "sage_maker_model_group", + "sage_maker_model_deployments", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_sage_maker_model_attrs( + attrs: SageMakerModelAttributes, obj: SageMakerModel +) -> None: + """Populate SageMakerModel-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.sage_maker_container_image = obj.sage_maker_container_image + attrs.sage_maker_execution_role_arn = obj.sage_maker_execution_role_arn + attrs.sage_maker_model_group_name = obj.sage_maker_model_group_name + attrs.sage_maker_model_group_qualified_name = ( + obj.sage_maker_model_group_qualified_name + ) + attrs.sage_maker_version = obj.sage_maker_version + attrs.sage_maker_status = obj.sage_maker_status + attrs.sage_maker_s3_uri = obj.sage_maker_s3_uri + attrs.ethical_ai_privacy_config = obj.ethical_ai_privacy_config + attrs.ethical_ai_fairness_config = obj.ethical_ai_fairness_config + attrs.ethical_ai_bias_mitigation_config = obj.ethical_ai_bias_mitigation_config + attrs.ethical_ai_reliability_and_safety_config = ( + obj.ethical_ai_reliability_and_safety_config + ) + attrs.ethical_ai_transparency_config = obj.ethical_ai_transparency_config + attrs.ethical_ai_accountability_config = obj.ethical_ai_accountability_config + attrs.ethical_ai_environmental_consciousness_config = ( + obj.ethical_ai_environmental_consciousness_config + ) + attrs.aws_arn = obj.aws_arn + attrs.aws_partition = obj.aws_partition + attrs.aws_service = obj.aws_service + attrs.aws_region = obj.aws_region + attrs.aws_account_id = obj.aws_account_id + attrs.aws_resource_id = obj.aws_resource_id + attrs.aws_owner_name = obj.aws_owner_name + attrs.aws_owner_id = obj.aws_owner_id + attrs.aws_tags = obj.aws_tags + attrs.cloud_uniform_resource_name = obj.cloud_uniform_resource_name + + +def _extract_sage_maker_model_attrs(attrs: SageMakerModelAttributes) -> dict: + """Extract all SageMakerModel attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["sage_maker_container_image"] = attrs.sage_maker_container_image + result["sage_maker_execution_role_arn"] = attrs.sage_maker_execution_role_arn + result["sage_maker_model_group_name"] = attrs.sage_maker_model_group_name + result["sage_maker_model_group_qualified_name"] = ( + attrs.sage_maker_model_group_qualified_name + ) + result["sage_maker_version"] = attrs.sage_maker_version + result["sage_maker_status"] = attrs.sage_maker_status + result["sage_maker_s3_uri"] = attrs.sage_maker_s3_uri + result["ethical_ai_privacy_config"] = attrs.ethical_ai_privacy_config + result["ethical_ai_fairness_config"] = attrs.ethical_ai_fairness_config + result["ethical_ai_bias_mitigation_config"] = ( + attrs.ethical_ai_bias_mitigation_config + ) + result["ethical_ai_reliability_and_safety_config"] = ( + attrs.ethical_ai_reliability_and_safety_config + ) + result["ethical_ai_transparency_config"] = attrs.ethical_ai_transparency_config + result["ethical_ai_accountability_config"] = attrs.ethical_ai_accountability_config + result["ethical_ai_environmental_consciousness_config"] = ( + attrs.ethical_ai_environmental_consciousness_config + ) + result["aws_arn"] = attrs.aws_arn + result["aws_partition"] = attrs.aws_partition + result["aws_service"] = attrs.aws_service + result["aws_region"] = attrs.aws_region + result["aws_account_id"] = attrs.aws_account_id + result["aws_resource_id"] = attrs.aws_resource_id + result["aws_owner_name"] = attrs.aws_owner_name + result["aws_owner_id"] = attrs.aws_owner_id + result["aws_tags"] = attrs.aws_tags + result["cloud_uniform_resource_name"] = attrs.cloud_uniform_resource_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _sage_maker_model_to_nested( + sage_maker_model: SageMakerModel, +) -> SageMakerModelNested: + """Convert flat SageMakerModel to nested format.""" + attrs = SageMakerModelAttributes() + _populate_sage_maker_model_attrs(attrs, sage_maker_model) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + sage_maker_model, + _SAGE_MAKER_MODEL_REL_FIELDS, + SageMakerModelRelationshipAttributes, + ) + return SageMakerModelNested( + guid=sage_maker_model.guid, + type_name=sage_maker_model.type_name, + status=sage_maker_model.status, + version=sage_maker_model.version, + create_time=sage_maker_model.create_time, + update_time=sage_maker_model.update_time, + created_by=sage_maker_model.created_by, + updated_by=sage_maker_model.updated_by, + classifications=sage_maker_model.classifications, + classification_names=sage_maker_model.classification_names, + meanings=sage_maker_model.meanings, + labels=sage_maker_model.labels, + business_attributes=sage_maker_model.business_attributes, + custom_attributes=sage_maker_model.custom_attributes, + pending_tasks=sage_maker_model.pending_tasks, + proxy=sage_maker_model.proxy, + is_incomplete=sage_maker_model.is_incomplete, + provenance_type=sage_maker_model.provenance_type, + home_id=sage_maker_model.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _sage_maker_model_from_nested(nested: SageMakerModelNested) -> SageMakerModel: + """Convert nested format to flat SageMakerModel.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else SageMakerModelAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SAGE_MAKER_MODEL_REL_FIELDS, + SageMakerModelRelationshipAttributes, + ) + return SageMakerModel( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_sage_maker_model_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _sage_maker_model_to_nested_bytes( + sage_maker_model: SageMakerModel, serde: Serde +) -> bytes: + """Convert flat SageMakerModel to nested JSON bytes.""" + return serde.encode(_sage_maker_model_to_nested(sage_maker_model)) + + +def _sage_maker_model_from_nested_bytes(data: bytes, serde: Serde) -> SageMakerModel: + """Convert nested JSON bytes to flat SageMakerModel.""" + nested = serde.decode(data, SageMakerModelNested) + return _sage_maker_model_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + RelationField, +) + +SageMakerModel.SAGE_MAKER_CONTAINER_IMAGE = KeywordField( + "sageMakerContainerImage", "sageMakerContainerImage" +) +SageMakerModel.SAGE_MAKER_EXECUTION_ROLE_ARN = KeywordField( + "sageMakerExecutionRoleArn", "sageMakerExecutionRoleArn" +) +SageMakerModel.SAGE_MAKER_MODEL_GROUP_NAME = KeywordField( + "sageMakerModelGroupName", "sageMakerModelGroupName" +) +SageMakerModel.SAGE_MAKER_MODEL_GROUP_QUALIFIED_NAME = KeywordField( + "sageMakerModelGroupQualifiedName", "sageMakerModelGroupQualifiedName" +) +SageMakerModel.SAGE_MAKER_VERSION = KeywordField("sageMakerVersion", "sageMakerVersion") +SageMakerModel.SAGE_MAKER_STATUS = KeywordField("sageMakerStatus", "sageMakerStatus") +SageMakerModel.SAGE_MAKER_S3_URI = KeywordField("sageMakerS3Uri", "sageMakerS3Uri") +SageMakerModel.ETHICAL_AI_PRIVACY_CONFIG = KeywordField( + "ethicalAIPrivacyConfig", "ethicalAIPrivacyConfig" +) +SageMakerModel.ETHICAL_AI_FAIRNESS_CONFIG = KeywordField( + "ethicalAIFairnessConfig", "ethicalAIFairnessConfig" +) +SageMakerModel.ETHICAL_AI_BIAS_MITIGATION_CONFIG = KeywordField( + "ethicalAIBiasMitigationConfig", "ethicalAIBiasMitigationConfig" +) +SageMakerModel.ETHICAL_AI_RELIABILITY_AND_SAFETY_CONFIG = KeywordField( + "ethicalAIReliabilityAndSafetyConfig", "ethicalAIReliabilityAndSafetyConfig" +) +SageMakerModel.ETHICAL_AI_TRANSPARENCY_CONFIG = KeywordField( + "ethicalAITransparencyConfig", "ethicalAITransparencyConfig" +) +SageMakerModel.ETHICAL_AI_ACCOUNTABILITY_CONFIG = KeywordField( + "ethicalAIAccountabilityConfig", "ethicalAIAccountabilityConfig" +) +SageMakerModel.ETHICAL_AI_ENVIRONMENTAL_CONSCIOUSNESS_CONFIG = KeywordField( + "ethicalAIEnvironmentalConsciousnessConfig", + "ethicalAIEnvironmentalConsciousnessConfig", +) +SageMakerModel.AWS_ARN = KeywordTextField("awsArn", "awsArn", "awsArn.text") +SageMakerModel.AWS_PARTITION = KeywordField("awsPartition", "awsPartition") +SageMakerModel.AWS_SERVICE = KeywordField("awsService", "awsService") +SageMakerModel.AWS_REGION = KeywordField("awsRegion", "awsRegion") +SageMakerModel.AWS_ACCOUNT_ID = KeywordField("awsAccountId", "awsAccountId") +SageMakerModel.AWS_RESOURCE_ID = KeywordField("awsResourceId", "awsResourceId") +SageMakerModel.AWS_OWNER_NAME = KeywordTextField( + "awsOwnerName", "awsOwnerName", "awsOwnerName.text" +) +SageMakerModel.AWS_OWNER_ID = KeywordField("awsOwnerId", "awsOwnerId") +SageMakerModel.AWS_TAGS = KeywordField("awsTags", "awsTags") +SageMakerModel.CLOUD_UNIFORM_RESOURCE_NAME = KeywordField( + "cloudUniformResourceName", "cloudUniformResourceName" +) +SageMakerModel.AI_MODEL = RelationField("aiModel") +SageMakerModel.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SageMakerModel.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +SageMakerModel.ANOMALO_CHECKS = RelationField("anomaloChecks") +SageMakerModel.APPLICATION = RelationField("application") +SageMakerModel.APPLICATION_FIELD = RelationField("applicationField") +SageMakerModel.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +SageMakerModel.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +SageMakerModel.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +SageMakerModel.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +SageMakerModel.METRICS = RelationField("metrics") +SageMakerModel.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SageMakerModel.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +SageMakerModel.MEANINGS = RelationField("meanings") +SageMakerModel.MC_MONITORS = RelationField("mcMonitors") +SageMakerModel.MC_INCIDENTS = RelationField("mcIncidents") +SageMakerModel.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SageMakerModel.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SageMakerModel.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SageMakerModel.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SageMakerModel.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +SageMakerModel.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +SageMakerModel.FILES = RelationField("files") +SageMakerModel.LINKS = RelationField("links") +SageMakerModel.README = RelationField("readme") +SageMakerModel.SAGE_MAKER_MODEL_GROUP = RelationField("sageMakerModelGroup") +SageMakerModel.SAGE_MAKER_MODEL_DEPLOYMENTS = RelationField("sageMakerModelDeployments") +SageMakerModel.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +SageMakerModel.SODA_CHECKS = RelationField("sodaChecks") +SageMakerModel.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SageMakerModel.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/sage_maker_model_deployment.py b/pyatlan_v9/model/assets/sage_maker_model_deployment.py new file mode 100644 index 000000000..658a4d0f0 --- /dev/null +++ b/pyatlan_v9/model/assets/sage_maker_model_deployment.py @@ -0,0 +1,878 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SageMakerModelDeployment asset model with flattened inheritance. + +This module provides: +- SageMakerModelDeployment: Flat asset class (easy to use) +- SageMakerModelDeploymentAttributes: Nested attributes struct (extends AssetAttributes) +- SageMakerModelDeploymentNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .sage_maker_related import RelatedSageMakerModel + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SageMakerModelDeployment(Asset): + """ + Instance of a SageMaker Endpoint in Atlan. Represents deployed models that can serve real-time inference requests. + """ + + SAGE_MAKER_STATUS: ClassVar[Any] = None + SAGE_MAKER_ENDPOINT_CONFIG_NAME: ClassVar[Any] = None + SAGE_MAKER_MODEL_NAME: ClassVar[Any] = None + SAGE_MAKER_MODEL_QUALIFIED_NAME: ClassVar[Any] = None + SAGE_MAKER_S3_URI: ClassVar[Any] = None + ETHICAL_AI_PRIVACY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_FAIRNESS_CONFIG: ClassVar[Any] = None + ETHICAL_AI_BIAS_MITIGATION_CONFIG: ClassVar[Any] = None + ETHICAL_AI_RELIABILITY_AND_SAFETY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_TRANSPARENCY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_ACCOUNTABILITY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_ENVIRONMENTAL_CONSCIOUSNESS_CONFIG: ClassVar[Any] = None + AWS_ARN: ClassVar[Any] = None + AWS_PARTITION: ClassVar[Any] = None + AWS_SERVICE: ClassVar[Any] = None + AWS_REGION: ClassVar[Any] = None + AWS_ACCOUNT_ID: ClassVar[Any] = None + AWS_RESOURCE_ID: ClassVar[Any] = None + AWS_OWNER_NAME: ClassVar[Any] = None + AWS_OWNER_ID: ClassVar[Any] = None + AWS_TAGS: ClassVar[Any] = None + CLOUD_UNIFORM_RESOURCE_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SAGE_MAKER_MODEL: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SageMakerModelDeployment" + + sage_maker_status: Union[str, None, UnsetType] = UNSET + """Current status of the endpoint (e.g., InService, OutOfService, Creating, Failed).""" + + sage_maker_endpoint_config_name: Union[str, None, UnsetType] = UNSET + """Name of the endpoint configuration used by this deployment.""" + + sage_maker_model_name: Union[str, None, UnsetType] = UNSET + """Name of the parent Model.""" + + sage_maker_model_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the parent Model.""" + + sage_maker_s3_uri: Union[str, None, UnsetType] = UNSET + """Primary S3 URI associated with this SageMaker asset.""" + + ethical_ai_privacy_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIPrivacyConfig" + ) + """Privacy configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_fairness_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIFairnessConfig" + ) + """Fairness configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_bias_mitigation_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIBiasMitigationConfig" + ) + """Bias mitigation configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_reliability_and_safety_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIReliabilityAndSafetyConfig") + ) + """Reliability and safety configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_transparency_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAITransparencyConfig" + ) + """Transparency configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_accountability_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIAccountabilityConfig" + ) + """Accountability configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_environmental_consciousness_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIEnvironmentalConsciousnessConfig") + ) + """Environmental consciousness configuration for ensuring the ethical use of an AI asset""" + + aws_arn: Union[str, None, UnsetType] = UNSET + """DEPRECATED: This legacy attribute must be unique across all AWS asset instances. This can create non-obvious edge cases for creating / updating assets, and we therefore recommended NOT using it. See and use cloudResourceName instead.""" + + aws_partition: Union[str, None, UnsetType] = UNSET + """Group of AWS region and service objects.""" + + aws_service: Union[str, None, UnsetType] = UNSET + """Type of service in which the asset exists.""" + + aws_region: Union[str, None, UnsetType] = UNSET + """Physical region where the data center in which the asset exists is clustered.""" + + aws_account_id: Union[str, None, UnsetType] = UNSET + """12-digit number that uniquely identifies an AWS account.""" + + aws_resource_id: Union[str, None, UnsetType] = UNSET + """Unique resource ID assigned when a new resource is created.""" + + aws_owner_name: Union[str, None, UnsetType] = UNSET + """Root user's name.""" + + aws_owner_id: Union[str, None, UnsetType] = UNSET + """Root user's ID.""" + + aws_tags: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of tags that have been applied to the asset in AWS.""" + + cloud_uniform_resource_name: Union[str, None, UnsetType] = UNSET + """Uniform resource name (URN) for the asset: AWS ARN, Google Cloud URI, Azure resource ID, Oracle OCID, and so on.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + sage_maker_model: Union[RelatedSageMakerModel, None, UnsetType] = UNSET + """SageMaker Model that is deployed.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SageMakerModelDeployment" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _sage_maker_model_deployment_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> SageMakerModelDeployment: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SageMakerModelDeployment instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _sage_maker_model_deployment_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SageMakerModelDeploymentAttributes(AssetAttributes): + """SageMakerModelDeployment-specific attributes for nested API format.""" + + sage_maker_status: Union[str, None, UnsetType] = UNSET + """Current status of the endpoint (e.g., InService, OutOfService, Creating, Failed).""" + + sage_maker_endpoint_config_name: Union[str, None, UnsetType] = UNSET + """Name of the endpoint configuration used by this deployment.""" + + sage_maker_model_name: Union[str, None, UnsetType] = UNSET + """Name of the parent Model.""" + + sage_maker_model_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the parent Model.""" + + sage_maker_s3_uri: Union[str, None, UnsetType] = UNSET + """Primary S3 URI associated with this SageMaker asset.""" + + ethical_ai_privacy_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIPrivacyConfig" + ) + """Privacy configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_fairness_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIFairnessConfig" + ) + """Fairness configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_bias_mitigation_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIBiasMitigationConfig" + ) + """Bias mitigation configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_reliability_and_safety_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIReliabilityAndSafetyConfig") + ) + """Reliability and safety configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_transparency_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAITransparencyConfig" + ) + """Transparency configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_accountability_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIAccountabilityConfig" + ) + """Accountability configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_environmental_consciousness_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIEnvironmentalConsciousnessConfig") + ) + """Environmental consciousness configuration for ensuring the ethical use of an AI asset""" + + aws_arn: Union[str, None, UnsetType] = UNSET + """DEPRECATED: This legacy attribute must be unique across all AWS asset instances. This can create non-obvious edge cases for creating / updating assets, and we therefore recommended NOT using it. See and use cloudResourceName instead.""" + + aws_partition: Union[str, None, UnsetType] = UNSET + """Group of AWS region and service objects.""" + + aws_service: Union[str, None, UnsetType] = UNSET + """Type of service in which the asset exists.""" + + aws_region: Union[str, None, UnsetType] = UNSET + """Physical region where the data center in which the asset exists is clustered.""" + + aws_account_id: Union[str, None, UnsetType] = UNSET + """12-digit number that uniquely identifies an AWS account.""" + + aws_resource_id: Union[str, None, UnsetType] = UNSET + """Unique resource ID assigned when a new resource is created.""" + + aws_owner_name: Union[str, None, UnsetType] = UNSET + """Root user's name.""" + + aws_owner_id: Union[str, None, UnsetType] = UNSET + """Root user's ID.""" + + aws_tags: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of tags that have been applied to the asset in AWS.""" + + cloud_uniform_resource_name: Union[str, None, UnsetType] = UNSET + """Uniform resource name (URN) for the asset: AWS ARN, Google Cloud URI, Azure resource ID, Oracle OCID, and so on.""" + + +class SageMakerModelDeploymentRelationshipAttributes(AssetRelationshipAttributes): + """SageMakerModelDeployment-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + sage_maker_model: Union[RelatedSageMakerModel, None, UnsetType] = UNSET + """SageMaker Model that is deployed.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SageMakerModelDeploymentNested(AssetNested): + """SageMakerModelDeployment in nested API format for high-performance serialization.""" + + attributes: Union[SageMakerModelDeploymentAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + SageMakerModelDeploymentRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + SageMakerModelDeploymentRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SageMakerModelDeploymentRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SAGE_MAKER_MODEL_DEPLOYMENT_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "sage_maker_model", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_sage_maker_model_deployment_attrs( + attrs: SageMakerModelDeploymentAttributes, obj: SageMakerModelDeployment +) -> None: + """Populate SageMakerModelDeployment-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.sage_maker_status = obj.sage_maker_status + attrs.sage_maker_endpoint_config_name = obj.sage_maker_endpoint_config_name + attrs.sage_maker_model_name = obj.sage_maker_model_name + attrs.sage_maker_model_qualified_name = obj.sage_maker_model_qualified_name + attrs.sage_maker_s3_uri = obj.sage_maker_s3_uri + attrs.ethical_ai_privacy_config = obj.ethical_ai_privacy_config + attrs.ethical_ai_fairness_config = obj.ethical_ai_fairness_config + attrs.ethical_ai_bias_mitigation_config = obj.ethical_ai_bias_mitigation_config + attrs.ethical_ai_reliability_and_safety_config = ( + obj.ethical_ai_reliability_and_safety_config + ) + attrs.ethical_ai_transparency_config = obj.ethical_ai_transparency_config + attrs.ethical_ai_accountability_config = obj.ethical_ai_accountability_config + attrs.ethical_ai_environmental_consciousness_config = ( + obj.ethical_ai_environmental_consciousness_config + ) + attrs.aws_arn = obj.aws_arn + attrs.aws_partition = obj.aws_partition + attrs.aws_service = obj.aws_service + attrs.aws_region = obj.aws_region + attrs.aws_account_id = obj.aws_account_id + attrs.aws_resource_id = obj.aws_resource_id + attrs.aws_owner_name = obj.aws_owner_name + attrs.aws_owner_id = obj.aws_owner_id + attrs.aws_tags = obj.aws_tags + attrs.cloud_uniform_resource_name = obj.cloud_uniform_resource_name + + +def _extract_sage_maker_model_deployment_attrs( + attrs: SageMakerModelDeploymentAttributes, +) -> dict: + """Extract all SageMakerModelDeployment attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["sage_maker_status"] = attrs.sage_maker_status + result["sage_maker_endpoint_config_name"] = attrs.sage_maker_endpoint_config_name + result["sage_maker_model_name"] = attrs.sage_maker_model_name + result["sage_maker_model_qualified_name"] = attrs.sage_maker_model_qualified_name + result["sage_maker_s3_uri"] = attrs.sage_maker_s3_uri + result["ethical_ai_privacy_config"] = attrs.ethical_ai_privacy_config + result["ethical_ai_fairness_config"] = attrs.ethical_ai_fairness_config + result["ethical_ai_bias_mitigation_config"] = ( + attrs.ethical_ai_bias_mitigation_config + ) + result["ethical_ai_reliability_and_safety_config"] = ( + attrs.ethical_ai_reliability_and_safety_config + ) + result["ethical_ai_transparency_config"] = attrs.ethical_ai_transparency_config + result["ethical_ai_accountability_config"] = attrs.ethical_ai_accountability_config + result["ethical_ai_environmental_consciousness_config"] = ( + attrs.ethical_ai_environmental_consciousness_config + ) + result["aws_arn"] = attrs.aws_arn + result["aws_partition"] = attrs.aws_partition + result["aws_service"] = attrs.aws_service + result["aws_region"] = attrs.aws_region + result["aws_account_id"] = attrs.aws_account_id + result["aws_resource_id"] = attrs.aws_resource_id + result["aws_owner_name"] = attrs.aws_owner_name + result["aws_owner_id"] = attrs.aws_owner_id + result["aws_tags"] = attrs.aws_tags + result["cloud_uniform_resource_name"] = attrs.cloud_uniform_resource_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _sage_maker_model_deployment_to_nested( + sage_maker_model_deployment: SageMakerModelDeployment, +) -> SageMakerModelDeploymentNested: + """Convert flat SageMakerModelDeployment to nested format.""" + attrs = SageMakerModelDeploymentAttributes() + _populate_sage_maker_model_deployment_attrs(attrs, sage_maker_model_deployment) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + sage_maker_model_deployment, + _SAGE_MAKER_MODEL_DEPLOYMENT_REL_FIELDS, + SageMakerModelDeploymentRelationshipAttributes, + ) + return SageMakerModelDeploymentNested( + guid=sage_maker_model_deployment.guid, + type_name=sage_maker_model_deployment.type_name, + status=sage_maker_model_deployment.status, + version=sage_maker_model_deployment.version, + create_time=sage_maker_model_deployment.create_time, + update_time=sage_maker_model_deployment.update_time, + created_by=sage_maker_model_deployment.created_by, + updated_by=sage_maker_model_deployment.updated_by, + classifications=sage_maker_model_deployment.classifications, + classification_names=sage_maker_model_deployment.classification_names, + meanings=sage_maker_model_deployment.meanings, + labels=sage_maker_model_deployment.labels, + business_attributes=sage_maker_model_deployment.business_attributes, + custom_attributes=sage_maker_model_deployment.custom_attributes, + pending_tasks=sage_maker_model_deployment.pending_tasks, + proxy=sage_maker_model_deployment.proxy, + is_incomplete=sage_maker_model_deployment.is_incomplete, + provenance_type=sage_maker_model_deployment.provenance_type, + home_id=sage_maker_model_deployment.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _sage_maker_model_deployment_from_nested( + nested: SageMakerModelDeploymentNested, +) -> SageMakerModelDeployment: + """Convert nested format to flat SageMakerModelDeployment.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else SageMakerModelDeploymentAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SAGE_MAKER_MODEL_DEPLOYMENT_REL_FIELDS, + SageMakerModelDeploymentRelationshipAttributes, + ) + return SageMakerModelDeployment( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_sage_maker_model_deployment_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _sage_maker_model_deployment_to_nested_bytes( + sage_maker_model_deployment: SageMakerModelDeployment, serde: Serde +) -> bytes: + """Convert flat SageMakerModelDeployment to nested JSON bytes.""" + return serde.encode( + _sage_maker_model_deployment_to_nested(sage_maker_model_deployment) + ) + + +def _sage_maker_model_deployment_from_nested_bytes( + data: bytes, serde: Serde +) -> SageMakerModelDeployment: + """Convert nested JSON bytes to flat SageMakerModelDeployment.""" + nested = serde.decode(data, SageMakerModelDeploymentNested) + return _sage_maker_model_deployment_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + RelationField, +) + +SageMakerModelDeployment.SAGE_MAKER_STATUS = KeywordField( + "sageMakerStatus", "sageMakerStatus" +) +SageMakerModelDeployment.SAGE_MAKER_ENDPOINT_CONFIG_NAME = KeywordField( + "sageMakerEndpointConfigName", "sageMakerEndpointConfigName" +) +SageMakerModelDeployment.SAGE_MAKER_MODEL_NAME = KeywordField( + "sageMakerModelName", "sageMakerModelName" +) +SageMakerModelDeployment.SAGE_MAKER_MODEL_QUALIFIED_NAME = KeywordField( + "sageMakerModelQualifiedName", "sageMakerModelQualifiedName" +) +SageMakerModelDeployment.SAGE_MAKER_S3_URI = KeywordField( + "sageMakerS3Uri", "sageMakerS3Uri" +) +SageMakerModelDeployment.ETHICAL_AI_PRIVACY_CONFIG = KeywordField( + "ethicalAIPrivacyConfig", "ethicalAIPrivacyConfig" +) +SageMakerModelDeployment.ETHICAL_AI_FAIRNESS_CONFIG = KeywordField( + "ethicalAIFairnessConfig", "ethicalAIFairnessConfig" +) +SageMakerModelDeployment.ETHICAL_AI_BIAS_MITIGATION_CONFIG = KeywordField( + "ethicalAIBiasMitigationConfig", "ethicalAIBiasMitigationConfig" +) +SageMakerModelDeployment.ETHICAL_AI_RELIABILITY_AND_SAFETY_CONFIG = KeywordField( + "ethicalAIReliabilityAndSafetyConfig", "ethicalAIReliabilityAndSafetyConfig" +) +SageMakerModelDeployment.ETHICAL_AI_TRANSPARENCY_CONFIG = KeywordField( + "ethicalAITransparencyConfig", "ethicalAITransparencyConfig" +) +SageMakerModelDeployment.ETHICAL_AI_ACCOUNTABILITY_CONFIG = KeywordField( + "ethicalAIAccountabilityConfig", "ethicalAIAccountabilityConfig" +) +SageMakerModelDeployment.ETHICAL_AI_ENVIRONMENTAL_CONSCIOUSNESS_CONFIG = KeywordField( + "ethicalAIEnvironmentalConsciousnessConfig", + "ethicalAIEnvironmentalConsciousnessConfig", +) +SageMakerModelDeployment.AWS_ARN = KeywordTextField("awsArn", "awsArn", "awsArn.text") +SageMakerModelDeployment.AWS_PARTITION = KeywordField("awsPartition", "awsPartition") +SageMakerModelDeployment.AWS_SERVICE = KeywordField("awsService", "awsService") +SageMakerModelDeployment.AWS_REGION = KeywordField("awsRegion", "awsRegion") +SageMakerModelDeployment.AWS_ACCOUNT_ID = KeywordField("awsAccountId", "awsAccountId") +SageMakerModelDeployment.AWS_RESOURCE_ID = KeywordField( + "awsResourceId", "awsResourceId" +) +SageMakerModelDeployment.AWS_OWNER_NAME = KeywordTextField( + "awsOwnerName", "awsOwnerName", "awsOwnerName.text" +) +SageMakerModelDeployment.AWS_OWNER_ID = KeywordField("awsOwnerId", "awsOwnerId") +SageMakerModelDeployment.AWS_TAGS = KeywordField("awsTags", "awsTags") +SageMakerModelDeployment.CLOUD_UNIFORM_RESOURCE_NAME = KeywordField( + "cloudUniformResourceName", "cloudUniformResourceName" +) +SageMakerModelDeployment.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SageMakerModelDeployment.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +SageMakerModelDeployment.ANOMALO_CHECKS = RelationField("anomaloChecks") +SageMakerModelDeployment.APPLICATION = RelationField("application") +SageMakerModelDeployment.APPLICATION_FIELD = RelationField("applicationField") +SageMakerModelDeployment.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +SageMakerModelDeployment.INPUT_PORT_DATA_PRODUCTS = RelationField( + "inputPortDataProducts" +) +SageMakerModelDeployment.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +SageMakerModelDeployment.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +SageMakerModelDeployment.METRICS = RelationField("metrics") +SageMakerModelDeployment.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SageMakerModelDeployment.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +SageMakerModelDeployment.MEANINGS = RelationField("meanings") +SageMakerModelDeployment.MC_MONITORS = RelationField("mcMonitors") +SageMakerModelDeployment.MC_INCIDENTS = RelationField("mcIncidents") +SageMakerModelDeployment.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SageMakerModelDeployment.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SageMakerModelDeployment.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SageMakerModelDeployment.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SageMakerModelDeployment.USER_DEF_RELATIONSHIP_TO = RelationField( + "userDefRelationshipTo" +) +SageMakerModelDeployment.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +SageMakerModelDeployment.FILES = RelationField("files") +SageMakerModelDeployment.LINKS = RelationField("links") +SageMakerModelDeployment.README = RelationField("readme") +SageMakerModelDeployment.SAGE_MAKER_MODEL = RelationField("sageMakerModel") +SageMakerModelDeployment.SCHEMA_REGISTRY_SUBJECTS = RelationField( + "schemaRegistrySubjects" +) +SageMakerModelDeployment.SODA_CHECKS = RelationField("sodaChecks") +SageMakerModelDeployment.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SageMakerModelDeployment.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/sage_maker_model_group.py b/pyatlan_v9/model/assets/sage_maker_model_group.py new file mode 100644 index 000000000..f121bdbd8 --- /dev/null +++ b/pyatlan_v9/model/assets/sage_maker_model_group.py @@ -0,0 +1,870 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SageMakerModelGroup asset model with flattened inheritance. + +This module provides: +- SageMakerModelGroup: Flat asset class (easy to use) +- SageMakerModelGroupAttributes: Nested attributes struct (extends AssetAttributes) +- SageMakerModelGroupNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .ai_related import RelatedAIApplication, RelatedAIModelVersion +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .sage_maker_related import RelatedSageMakerModel + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SageMakerModelGroup(Asset): + """ + Instance of a SageMaker Model Package Group in Atlan. Represents a collection of versioned models that can be organized and managed together. + """ + + SAGE_MAKER_STATUS: ClassVar[Any] = None + SAGE_MAKER_S3_URI: ClassVar[Any] = None + ETHICAL_AI_PRIVACY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_FAIRNESS_CONFIG: ClassVar[Any] = None + ETHICAL_AI_BIAS_MITIGATION_CONFIG: ClassVar[Any] = None + ETHICAL_AI_RELIABILITY_AND_SAFETY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_TRANSPARENCY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_ACCOUNTABILITY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_ENVIRONMENTAL_CONSCIOUSNESS_CONFIG: ClassVar[Any] = None + AWS_ARN: ClassVar[Any] = None + AWS_PARTITION: ClassVar[Any] = None + AWS_SERVICE: ClassVar[Any] = None + AWS_REGION: ClassVar[Any] = None + AWS_ACCOUNT_ID: ClassVar[Any] = None + AWS_RESOURCE_ID: ClassVar[Any] = None + AWS_OWNER_NAME: ClassVar[Any] = None + AWS_OWNER_ID: ClassVar[Any] = None + AWS_TAGS: ClassVar[Any] = None + CLOUD_UNIFORM_RESOURCE_NAME: ClassVar[Any] = None + AI_MODEL_DATASETS_DSL: ClassVar[Any] = None + AI_MODEL_STATUS: ClassVar[Any] = None + AI_MODEL_VERSION: ClassVar[Any] = None + APPLICATIONS: ClassVar[Any] = None + AI_MODEL_VERSIONS: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SAGE_MAKER_MODELS: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SageMakerModelGroup" + + sage_maker_status: Union[str, None, UnsetType] = UNSET + """Current status of the Model Package Group.""" + + sage_maker_s3_uri: Union[str, None, UnsetType] = UNSET + """Primary S3 URI associated with this SageMaker asset.""" + + ethical_ai_privacy_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIPrivacyConfig" + ) + """Privacy configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_fairness_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIFairnessConfig" + ) + """Fairness configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_bias_mitigation_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIBiasMitigationConfig" + ) + """Bias mitigation configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_reliability_and_safety_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIReliabilityAndSafetyConfig") + ) + """Reliability and safety configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_transparency_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAITransparencyConfig" + ) + """Transparency configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_accountability_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIAccountabilityConfig" + ) + """Accountability configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_environmental_consciousness_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIEnvironmentalConsciousnessConfig") + ) + """Environmental consciousness configuration for ensuring the ethical use of an AI asset""" + + aws_arn: Union[str, None, UnsetType] = UNSET + """DEPRECATED: This legacy attribute must be unique across all AWS asset instances. This can create non-obvious edge cases for creating / updating assets, and we therefore recommended NOT using it. See and use cloudResourceName instead.""" + + aws_partition: Union[str, None, UnsetType] = UNSET + """Group of AWS region and service objects.""" + + aws_service: Union[str, None, UnsetType] = UNSET + """Type of service in which the asset exists.""" + + aws_region: Union[str, None, UnsetType] = UNSET + """Physical region where the data center in which the asset exists is clustered.""" + + aws_account_id: Union[str, None, UnsetType] = UNSET + """12-digit number that uniquely identifies an AWS account.""" + + aws_resource_id: Union[str, None, UnsetType] = UNSET + """Unique resource ID assigned when a new resource is created.""" + + aws_owner_name: Union[str, None, UnsetType] = UNSET + """Root user's name.""" + + aws_owner_id: Union[str, None, UnsetType] = UNSET + """Root user's ID.""" + + aws_tags: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of tags that have been applied to the asset in AWS.""" + + cloud_uniform_resource_name: Union[str, None, UnsetType] = UNSET + """Uniform resource name (URN) for the asset: AWS ARN, Google Cloud URI, Azure resource ID, Oracle OCID, and so on.""" + + ai_model_datasets_dsl: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="aiModelDatasetsDSL" + ) + """Search DSL used to define which assets/datasets are part of the AI model.""" + + ai_model_status: Union[str, None, UnsetType] = UNSET + """Status of the AI model.""" + + ai_model_version: Union[str, None, UnsetType] = UNSET + """Version of the AI model.""" + + applications: Union[List[RelatedAIApplication], None, UnsetType] = UNSET + """AI applications that are created using this AI model.""" + + ai_model_versions: Union[List[RelatedAIModelVersion], None, UnsetType] = UNSET + """Versions contained within the model.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + sage_maker_models: Union[List[RelatedSageMakerModel], None, UnsetType] = UNSET + """Models that are grouped within the SageMaker Model Group.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SageMakerModelGroup" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _sage_maker_model_group_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> SageMakerModelGroup: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SageMakerModelGroup instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _sage_maker_model_group_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SageMakerModelGroupAttributes(AssetAttributes): + """SageMakerModelGroup-specific attributes for nested API format.""" + + sage_maker_status: Union[str, None, UnsetType] = UNSET + """Current status of the Model Package Group.""" + + sage_maker_s3_uri: Union[str, None, UnsetType] = UNSET + """Primary S3 URI associated with this SageMaker asset.""" + + ethical_ai_privacy_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIPrivacyConfig" + ) + """Privacy configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_fairness_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIFairnessConfig" + ) + """Fairness configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_bias_mitigation_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIBiasMitigationConfig" + ) + """Bias mitigation configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_reliability_and_safety_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIReliabilityAndSafetyConfig") + ) + """Reliability and safety configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_transparency_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAITransparencyConfig" + ) + """Transparency configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_accountability_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIAccountabilityConfig" + ) + """Accountability configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_environmental_consciousness_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIEnvironmentalConsciousnessConfig") + ) + """Environmental consciousness configuration for ensuring the ethical use of an AI asset""" + + aws_arn: Union[str, None, UnsetType] = UNSET + """DEPRECATED: This legacy attribute must be unique across all AWS asset instances. This can create non-obvious edge cases for creating / updating assets, and we therefore recommended NOT using it. See and use cloudResourceName instead.""" + + aws_partition: Union[str, None, UnsetType] = UNSET + """Group of AWS region and service objects.""" + + aws_service: Union[str, None, UnsetType] = UNSET + """Type of service in which the asset exists.""" + + aws_region: Union[str, None, UnsetType] = UNSET + """Physical region where the data center in which the asset exists is clustered.""" + + aws_account_id: Union[str, None, UnsetType] = UNSET + """12-digit number that uniquely identifies an AWS account.""" + + aws_resource_id: Union[str, None, UnsetType] = UNSET + """Unique resource ID assigned when a new resource is created.""" + + aws_owner_name: Union[str, None, UnsetType] = UNSET + """Root user's name.""" + + aws_owner_id: Union[str, None, UnsetType] = UNSET + """Root user's ID.""" + + aws_tags: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of tags that have been applied to the asset in AWS.""" + + cloud_uniform_resource_name: Union[str, None, UnsetType] = UNSET + """Uniform resource name (URN) for the asset: AWS ARN, Google Cloud URI, Azure resource ID, Oracle OCID, and so on.""" + + ai_model_datasets_dsl: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="aiModelDatasetsDSL" + ) + """Search DSL used to define which assets/datasets are part of the AI model.""" + + ai_model_status: Union[str, None, UnsetType] = UNSET + """Status of the AI model.""" + + ai_model_version: Union[str, None, UnsetType] = UNSET + """Version of the AI model.""" + + +class SageMakerModelGroupRelationshipAttributes(AssetRelationshipAttributes): + """SageMakerModelGroup-specific relationship attributes for nested API format.""" + + applications: Union[List[RelatedAIApplication], None, UnsetType] = UNSET + """AI applications that are created using this AI model.""" + + ai_model_versions: Union[List[RelatedAIModelVersion], None, UnsetType] = UNSET + """Versions contained within the model.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + sage_maker_models: Union[List[RelatedSageMakerModel], None, UnsetType] = UNSET + """Models that are grouped within the SageMaker Model Group.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SageMakerModelGroupNested(AssetNested): + """SageMakerModelGroup in nested API format for high-performance serialization.""" + + attributes: Union[SageMakerModelGroupAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + SageMakerModelGroupRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + SageMakerModelGroupRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SageMakerModelGroupRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SAGE_MAKER_MODEL_GROUP_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "applications", + "ai_model_versions", + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "sage_maker_models", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_sage_maker_model_group_attrs( + attrs: SageMakerModelGroupAttributes, obj: SageMakerModelGroup +) -> None: + """Populate SageMakerModelGroup-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.sage_maker_status = obj.sage_maker_status + attrs.sage_maker_s3_uri = obj.sage_maker_s3_uri + attrs.ethical_ai_privacy_config = obj.ethical_ai_privacy_config + attrs.ethical_ai_fairness_config = obj.ethical_ai_fairness_config + attrs.ethical_ai_bias_mitigation_config = obj.ethical_ai_bias_mitigation_config + attrs.ethical_ai_reliability_and_safety_config = ( + obj.ethical_ai_reliability_and_safety_config + ) + attrs.ethical_ai_transparency_config = obj.ethical_ai_transparency_config + attrs.ethical_ai_accountability_config = obj.ethical_ai_accountability_config + attrs.ethical_ai_environmental_consciousness_config = ( + obj.ethical_ai_environmental_consciousness_config + ) + attrs.aws_arn = obj.aws_arn + attrs.aws_partition = obj.aws_partition + attrs.aws_service = obj.aws_service + attrs.aws_region = obj.aws_region + attrs.aws_account_id = obj.aws_account_id + attrs.aws_resource_id = obj.aws_resource_id + attrs.aws_owner_name = obj.aws_owner_name + attrs.aws_owner_id = obj.aws_owner_id + attrs.aws_tags = obj.aws_tags + attrs.cloud_uniform_resource_name = obj.cloud_uniform_resource_name + attrs.ai_model_datasets_dsl = obj.ai_model_datasets_dsl + attrs.ai_model_status = obj.ai_model_status + attrs.ai_model_version = obj.ai_model_version + + +def _extract_sage_maker_model_group_attrs(attrs: SageMakerModelGroupAttributes) -> dict: + """Extract all SageMakerModelGroup attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["sage_maker_status"] = attrs.sage_maker_status + result["sage_maker_s3_uri"] = attrs.sage_maker_s3_uri + result["ethical_ai_privacy_config"] = attrs.ethical_ai_privacy_config + result["ethical_ai_fairness_config"] = attrs.ethical_ai_fairness_config + result["ethical_ai_bias_mitigation_config"] = ( + attrs.ethical_ai_bias_mitigation_config + ) + result["ethical_ai_reliability_and_safety_config"] = ( + attrs.ethical_ai_reliability_and_safety_config + ) + result["ethical_ai_transparency_config"] = attrs.ethical_ai_transparency_config + result["ethical_ai_accountability_config"] = attrs.ethical_ai_accountability_config + result["ethical_ai_environmental_consciousness_config"] = ( + attrs.ethical_ai_environmental_consciousness_config + ) + result["aws_arn"] = attrs.aws_arn + result["aws_partition"] = attrs.aws_partition + result["aws_service"] = attrs.aws_service + result["aws_region"] = attrs.aws_region + result["aws_account_id"] = attrs.aws_account_id + result["aws_resource_id"] = attrs.aws_resource_id + result["aws_owner_name"] = attrs.aws_owner_name + result["aws_owner_id"] = attrs.aws_owner_id + result["aws_tags"] = attrs.aws_tags + result["cloud_uniform_resource_name"] = attrs.cloud_uniform_resource_name + result["ai_model_datasets_dsl"] = attrs.ai_model_datasets_dsl + result["ai_model_status"] = attrs.ai_model_status + result["ai_model_version"] = attrs.ai_model_version + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _sage_maker_model_group_to_nested( + sage_maker_model_group: SageMakerModelGroup, +) -> SageMakerModelGroupNested: + """Convert flat SageMakerModelGroup to nested format.""" + attrs = SageMakerModelGroupAttributes() + _populate_sage_maker_model_group_attrs(attrs, sage_maker_model_group) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + sage_maker_model_group, + _SAGE_MAKER_MODEL_GROUP_REL_FIELDS, + SageMakerModelGroupRelationshipAttributes, + ) + return SageMakerModelGroupNested( + guid=sage_maker_model_group.guid, + type_name=sage_maker_model_group.type_name, + status=sage_maker_model_group.status, + version=sage_maker_model_group.version, + create_time=sage_maker_model_group.create_time, + update_time=sage_maker_model_group.update_time, + created_by=sage_maker_model_group.created_by, + updated_by=sage_maker_model_group.updated_by, + classifications=sage_maker_model_group.classifications, + classification_names=sage_maker_model_group.classification_names, + meanings=sage_maker_model_group.meanings, + labels=sage_maker_model_group.labels, + business_attributes=sage_maker_model_group.business_attributes, + custom_attributes=sage_maker_model_group.custom_attributes, + pending_tasks=sage_maker_model_group.pending_tasks, + proxy=sage_maker_model_group.proxy, + is_incomplete=sage_maker_model_group.is_incomplete, + provenance_type=sage_maker_model_group.provenance_type, + home_id=sage_maker_model_group.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _sage_maker_model_group_from_nested( + nested: SageMakerModelGroupNested, +) -> SageMakerModelGroup: + """Convert nested format to flat SageMakerModelGroup.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else SageMakerModelGroupAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SAGE_MAKER_MODEL_GROUP_REL_FIELDS, + SageMakerModelGroupRelationshipAttributes, + ) + return SageMakerModelGroup( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_sage_maker_model_group_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _sage_maker_model_group_to_nested_bytes( + sage_maker_model_group: SageMakerModelGroup, serde: Serde +) -> bytes: + """Convert flat SageMakerModelGroup to nested JSON bytes.""" + return serde.encode(_sage_maker_model_group_to_nested(sage_maker_model_group)) + + +def _sage_maker_model_group_from_nested_bytes( + data: bytes, serde: Serde +) -> SageMakerModelGroup: + """Convert nested JSON bytes to flat SageMakerModelGroup.""" + nested = serde.decode(data, SageMakerModelGroupNested) + return _sage_maker_model_group_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + RelationField, +) + +SageMakerModelGroup.SAGE_MAKER_STATUS = KeywordField( + "sageMakerStatus", "sageMakerStatus" +) +SageMakerModelGroup.SAGE_MAKER_S3_URI = KeywordField("sageMakerS3Uri", "sageMakerS3Uri") +SageMakerModelGroup.ETHICAL_AI_PRIVACY_CONFIG = KeywordField( + "ethicalAIPrivacyConfig", "ethicalAIPrivacyConfig" +) +SageMakerModelGroup.ETHICAL_AI_FAIRNESS_CONFIG = KeywordField( + "ethicalAIFairnessConfig", "ethicalAIFairnessConfig" +) +SageMakerModelGroup.ETHICAL_AI_BIAS_MITIGATION_CONFIG = KeywordField( + "ethicalAIBiasMitigationConfig", "ethicalAIBiasMitigationConfig" +) +SageMakerModelGroup.ETHICAL_AI_RELIABILITY_AND_SAFETY_CONFIG = KeywordField( + "ethicalAIReliabilityAndSafetyConfig", "ethicalAIReliabilityAndSafetyConfig" +) +SageMakerModelGroup.ETHICAL_AI_TRANSPARENCY_CONFIG = KeywordField( + "ethicalAITransparencyConfig", "ethicalAITransparencyConfig" +) +SageMakerModelGroup.ETHICAL_AI_ACCOUNTABILITY_CONFIG = KeywordField( + "ethicalAIAccountabilityConfig", "ethicalAIAccountabilityConfig" +) +SageMakerModelGroup.ETHICAL_AI_ENVIRONMENTAL_CONSCIOUSNESS_CONFIG = KeywordField( + "ethicalAIEnvironmentalConsciousnessConfig", + "ethicalAIEnvironmentalConsciousnessConfig", +) +SageMakerModelGroup.AWS_ARN = KeywordTextField("awsArn", "awsArn", "awsArn.text") +SageMakerModelGroup.AWS_PARTITION = KeywordField("awsPartition", "awsPartition") +SageMakerModelGroup.AWS_SERVICE = KeywordField("awsService", "awsService") +SageMakerModelGroup.AWS_REGION = KeywordField("awsRegion", "awsRegion") +SageMakerModelGroup.AWS_ACCOUNT_ID = KeywordField("awsAccountId", "awsAccountId") +SageMakerModelGroup.AWS_RESOURCE_ID = KeywordField("awsResourceId", "awsResourceId") +SageMakerModelGroup.AWS_OWNER_NAME = KeywordTextField( + "awsOwnerName", "awsOwnerName", "awsOwnerName.text" +) +SageMakerModelGroup.AWS_OWNER_ID = KeywordField("awsOwnerId", "awsOwnerId") +SageMakerModelGroup.AWS_TAGS = KeywordField("awsTags", "awsTags") +SageMakerModelGroup.CLOUD_UNIFORM_RESOURCE_NAME = KeywordField( + "cloudUniformResourceName", "cloudUniformResourceName" +) +SageMakerModelGroup.AI_MODEL_DATASETS_DSL = KeywordField( + "aiModelDatasetsDSL", "aiModelDatasetsDSL" +) +SageMakerModelGroup.AI_MODEL_STATUS = KeywordField("aiModelStatus", "aiModelStatus") +SageMakerModelGroup.AI_MODEL_VERSION = KeywordField("aiModelVersion", "aiModelVersion") +SageMakerModelGroup.APPLICATIONS = RelationField("applications") +SageMakerModelGroup.AI_MODEL_VERSIONS = RelationField("aiModelVersions") +SageMakerModelGroup.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SageMakerModelGroup.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +SageMakerModelGroup.ANOMALO_CHECKS = RelationField("anomaloChecks") +SageMakerModelGroup.APPLICATION = RelationField("application") +SageMakerModelGroup.APPLICATION_FIELD = RelationField("applicationField") +SageMakerModelGroup.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +SageMakerModelGroup.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +SageMakerModelGroup.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +SageMakerModelGroup.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +SageMakerModelGroup.METRICS = RelationField("metrics") +SageMakerModelGroup.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SageMakerModelGroup.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +SageMakerModelGroup.MEANINGS = RelationField("meanings") +SageMakerModelGroup.MC_MONITORS = RelationField("mcMonitors") +SageMakerModelGroup.MC_INCIDENTS = RelationField("mcIncidents") +SageMakerModelGroup.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SageMakerModelGroup.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SageMakerModelGroup.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SageMakerModelGroup.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SageMakerModelGroup.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +SageMakerModelGroup.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +SageMakerModelGroup.FILES = RelationField("files") +SageMakerModelGroup.LINKS = RelationField("links") +SageMakerModelGroup.README = RelationField("readme") +SageMakerModelGroup.SAGE_MAKER_MODELS = RelationField("sageMakerModels") +SageMakerModelGroup.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +SageMakerModelGroup.SODA_CHECKS = RelationField("sodaChecks") +SageMakerModelGroup.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SageMakerModelGroup.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/sage_maker_related.py b/pyatlan_v9/model/assets/sage_maker_related.py new file mode 100644 index 000000000..2589e80dd --- /dev/null +++ b/pyatlan_v9/model/assets/sage_maker_related.py @@ -0,0 +1,181 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for SageMaker module. + +This module contains all Related{Type} classes for the SageMaker type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Union + +from msgspec import UNSET, UnsetType + +from .ai_related import RelatedAI +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedSageMaker", + "RelatedSageMakerFeatureGroup", + "RelatedSageMakerFeature", + "RelatedSageMakerModel", + "RelatedSageMakerModelGroup", + "RelatedSageMakerModelDeployment", +] + + +class RelatedSageMaker(RelatedAI): + """ + Related entity reference for SageMaker assets. + + Extends RelatedAI with SageMaker-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SageMaker" so it serializes correctly + + sage_maker_s3_uri: Union[str, None, UnsetType] = UNSET + """Primary S3 URI associated with this SageMaker asset.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SageMaker" + + +class RelatedSageMakerFeatureGroup(RelatedSageMaker): + """ + Related entity reference for SageMakerFeatureGroup assets. + + Extends RelatedSageMaker with SageMakerFeatureGroup-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SageMakerFeatureGroup" so it serializes correctly + + sage_maker_status: Union[str, None, UnsetType] = UNSET + """Current status of the Feature Group (e.g., Created, Creating, Failed).""" + + sage_maker_record_id_name: Union[str, None, UnsetType] = UNSET + """Name of the feature that serves as the record identifier.""" + + sage_maker_glue_database_name: Union[str, None, UnsetType] = UNSET + """AWS Glue database name associated with this Feature Group.""" + + sage_maker_glue_table_name: Union[str, None, UnsetType] = UNSET + """AWS Glue table name associated with this Feature Group.""" + + sage_maker_feature_count: Union[int, None, UnsetType] = UNSET + """Number of features in this Feature Group.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SageMakerFeatureGroup" + + +class RelatedSageMakerFeature(RelatedSageMaker): + """ + Related entity reference for SageMakerFeature assets. + + Extends RelatedSageMaker with SageMakerFeature-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SageMakerFeature" so it serializes correctly + + sage_maker_group_name: Union[str, None, UnsetType] = UNSET + """Name of the Feature Group that contains this feature.""" + + sage_maker_group_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the Feature Group that contains this feature.""" + + sage_maker_data_type: Union[str, None, UnsetType] = UNSET + """Data type of the feature (e.g., String, Integral, Fractional).""" + + sage_maker_is_record_identifier: Union[bool, None, UnsetType] = UNSET + """Whether this feature serves as the record identifier for the Feature Group.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SageMakerFeature" + + +class RelatedSageMakerModel(RelatedSageMaker): + """ + Related entity reference for SageMakerModel assets. + + Extends RelatedSageMaker with SageMakerModel-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SageMakerModel" so it serializes correctly + + sage_maker_container_image: Union[str, None, UnsetType] = UNSET + """Docker container image used for the model.""" + + sage_maker_execution_role_arn: Union[str, None, UnsetType] = UNSET + """ARN of the IAM role used by the model for accessing AWS resources.""" + + sage_maker_model_group_name: Union[str, None, UnsetType] = UNSET + """Name of the parent Model Group.""" + + sage_maker_model_group_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the parent Model Group.""" + + sage_maker_version: Union[str, None, UnsetType] = UNSET + """Version of the SageMaker Model Package.""" + + sage_maker_status: Union[str, None, UnsetType] = UNSET + """Status of the SageMaker Model Package (ACTIVE or INACTIVE).""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SageMakerModel" + + +class RelatedSageMakerModelGroup(RelatedSageMaker): + """ + Related entity reference for SageMakerModelGroup assets. + + Extends RelatedSageMaker with SageMakerModelGroup-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SageMakerModelGroup" so it serializes correctly + + sage_maker_status: Union[str, None, UnsetType] = UNSET + """Current status of the Model Package Group.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SageMakerModelGroup" + + +class RelatedSageMakerModelDeployment(RelatedSageMaker): + """ + Related entity reference for SageMakerModelDeployment assets. + + Extends RelatedSageMaker with SageMakerModelDeployment-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SageMakerModelDeployment" so it serializes correctly + + sage_maker_status: Union[str, None, UnsetType] = UNSET + """Current status of the endpoint (e.g., InService, OutOfService, Creating, Failed).""" + + sage_maker_endpoint_config_name: Union[str, None, UnsetType] = UNSET + """Name of the endpoint configuration used by this deployment.""" + + sage_maker_model_name: Union[str, None, UnsetType] = UNSET + """Name of the parent Model.""" + + sage_maker_model_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the parent Model.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SageMakerModelDeployment" diff --git a/pyatlan_v9/model/assets/sage_maker_unified_studio.py b/pyatlan_v9/model/assets/sage_maker_unified_studio.py new file mode 100644 index 000000000..dfa462774 --- /dev/null +++ b/pyatlan_v9/model/assets/sage_maker_unified_studio.py @@ -0,0 +1,629 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SageMakerUnifiedStudio asset model with flattened inheritance. + +This module provides: +- SageMakerUnifiedStudio: Flat asset class (easy to use) +- SageMakerUnifiedStudioAttributes: Nested attributes struct (extends AssetAttributes) +- SageMakerUnifiedStudioNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SageMakerUnifiedStudio(Asset): + """ + Base class for all SageMakerUnifiedStudio types. + """ + + SMUS_DOMAIN_NAME: ClassVar[Any] = None + SMUS_DOMAIN_ID: ClassVar[Any] = None + SMUS_DOMAIN_UNIT_NAME: ClassVar[Any] = None + SMUS_DOMAIN_UNIT_ID: ClassVar[Any] = None + SMUS_PROJECT_ID: ClassVar[Any] = None + SMUS_OWNING_PROJECT_ID: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SageMakerUnifiedStudio" + + smus_domain_name: Union[str, None, UnsetType] = UNSET + """Name of the SageMaker Unified Studio domain.""" + + smus_domain_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio domain.""" + + smus_domain_unit_name: Union[str, None, UnsetType] = UNSET + """Name of the SageMaker Unified Studio domain unit.""" + + smus_domain_unit_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio domain unit.""" + + smus_project_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio project.""" + + smus_owning_project_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio project which owns the asset.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SageMakerUnifiedStudio" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _sage_maker_unified_studio_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> SageMakerUnifiedStudio: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SageMakerUnifiedStudio instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _sage_maker_unified_studio_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SageMakerUnifiedStudioAttributes(AssetAttributes): + """SageMakerUnifiedStudio-specific attributes for nested API format.""" + + smus_domain_name: Union[str, None, UnsetType] = UNSET + """Name of the SageMaker Unified Studio domain.""" + + smus_domain_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio domain.""" + + smus_domain_unit_name: Union[str, None, UnsetType] = UNSET + """Name of the SageMaker Unified Studio domain unit.""" + + smus_domain_unit_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio domain unit.""" + + smus_project_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio project.""" + + smus_owning_project_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio project which owns the asset.""" + + +class SageMakerUnifiedStudioRelationshipAttributes(AssetRelationshipAttributes): + """SageMakerUnifiedStudio-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SageMakerUnifiedStudioNested(AssetNested): + """SageMakerUnifiedStudio in nested API format for high-performance serialization.""" + + attributes: Union[SageMakerUnifiedStudioAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + SageMakerUnifiedStudioRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + SageMakerUnifiedStudioRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SageMakerUnifiedStudioRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SAGE_MAKER_UNIFIED_STUDIO_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_sage_maker_unified_studio_attrs( + attrs: SageMakerUnifiedStudioAttributes, obj: SageMakerUnifiedStudio +) -> None: + """Populate SageMakerUnifiedStudio-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.smus_domain_name = obj.smus_domain_name + attrs.smus_domain_id = obj.smus_domain_id + attrs.smus_domain_unit_name = obj.smus_domain_unit_name + attrs.smus_domain_unit_id = obj.smus_domain_unit_id + attrs.smus_project_id = obj.smus_project_id + attrs.smus_owning_project_id = obj.smus_owning_project_id + + +def _extract_sage_maker_unified_studio_attrs( + attrs: SageMakerUnifiedStudioAttributes, +) -> dict: + """Extract all SageMakerUnifiedStudio attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["smus_domain_name"] = attrs.smus_domain_name + result["smus_domain_id"] = attrs.smus_domain_id + result["smus_domain_unit_name"] = attrs.smus_domain_unit_name + result["smus_domain_unit_id"] = attrs.smus_domain_unit_id + result["smus_project_id"] = attrs.smus_project_id + result["smus_owning_project_id"] = attrs.smus_owning_project_id + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _sage_maker_unified_studio_to_nested( + sage_maker_unified_studio: SageMakerUnifiedStudio, +) -> SageMakerUnifiedStudioNested: + """Convert flat SageMakerUnifiedStudio to nested format.""" + attrs = SageMakerUnifiedStudioAttributes() + _populate_sage_maker_unified_studio_attrs(attrs, sage_maker_unified_studio) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + sage_maker_unified_studio, + _SAGE_MAKER_UNIFIED_STUDIO_REL_FIELDS, + SageMakerUnifiedStudioRelationshipAttributes, + ) + return SageMakerUnifiedStudioNested( + guid=sage_maker_unified_studio.guid, + type_name=sage_maker_unified_studio.type_name, + status=sage_maker_unified_studio.status, + version=sage_maker_unified_studio.version, + create_time=sage_maker_unified_studio.create_time, + update_time=sage_maker_unified_studio.update_time, + created_by=sage_maker_unified_studio.created_by, + updated_by=sage_maker_unified_studio.updated_by, + classifications=sage_maker_unified_studio.classifications, + classification_names=sage_maker_unified_studio.classification_names, + meanings=sage_maker_unified_studio.meanings, + labels=sage_maker_unified_studio.labels, + business_attributes=sage_maker_unified_studio.business_attributes, + custom_attributes=sage_maker_unified_studio.custom_attributes, + pending_tasks=sage_maker_unified_studio.pending_tasks, + proxy=sage_maker_unified_studio.proxy, + is_incomplete=sage_maker_unified_studio.is_incomplete, + provenance_type=sage_maker_unified_studio.provenance_type, + home_id=sage_maker_unified_studio.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _sage_maker_unified_studio_from_nested( + nested: SageMakerUnifiedStudioNested, +) -> SageMakerUnifiedStudio: + """Convert nested format to flat SageMakerUnifiedStudio.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else SageMakerUnifiedStudioAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SAGE_MAKER_UNIFIED_STUDIO_REL_FIELDS, + SageMakerUnifiedStudioRelationshipAttributes, + ) + return SageMakerUnifiedStudio( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_sage_maker_unified_studio_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _sage_maker_unified_studio_to_nested_bytes( + sage_maker_unified_studio: SageMakerUnifiedStudio, serde: Serde +) -> bytes: + """Convert flat SageMakerUnifiedStudio to nested JSON bytes.""" + return serde.encode(_sage_maker_unified_studio_to_nested(sage_maker_unified_studio)) + + +def _sage_maker_unified_studio_from_nested_bytes( + data: bytes, serde: Serde +) -> SageMakerUnifiedStudio: + """Convert nested JSON bytes to flat SageMakerUnifiedStudio.""" + nested = serde.decode(data, SageMakerUnifiedStudioNested) + return _sage_maker_unified_studio_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +SageMakerUnifiedStudio.SMUS_DOMAIN_NAME = KeywordField( + "smusDomainName", "smusDomainName" +) +SageMakerUnifiedStudio.SMUS_DOMAIN_ID = KeywordField("smusDomainId", "smusDomainId") +SageMakerUnifiedStudio.SMUS_DOMAIN_UNIT_NAME = KeywordField( + "smusDomainUnitName", "smusDomainUnitName" +) +SageMakerUnifiedStudio.SMUS_DOMAIN_UNIT_ID = KeywordField( + "smusDomainUnitId", "smusDomainUnitId" +) +SageMakerUnifiedStudio.SMUS_PROJECT_ID = KeywordField("smusProjectId", "smusProjectId") +SageMakerUnifiedStudio.SMUS_OWNING_PROJECT_ID = KeywordField( + "smusOwningProjectId", "smusOwningProjectId" +) +SageMakerUnifiedStudio.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SageMakerUnifiedStudio.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +SageMakerUnifiedStudio.ANOMALO_CHECKS = RelationField("anomaloChecks") +SageMakerUnifiedStudio.APPLICATION = RelationField("application") +SageMakerUnifiedStudio.APPLICATION_FIELD = RelationField("applicationField") +SageMakerUnifiedStudio.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +SageMakerUnifiedStudio.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +SageMakerUnifiedStudio.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +SageMakerUnifiedStudio.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +SageMakerUnifiedStudio.METRICS = RelationField("metrics") +SageMakerUnifiedStudio.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SageMakerUnifiedStudio.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +SageMakerUnifiedStudio.MEANINGS = RelationField("meanings") +SageMakerUnifiedStudio.MC_MONITORS = RelationField("mcMonitors") +SageMakerUnifiedStudio.MC_INCIDENTS = RelationField("mcIncidents") +SageMakerUnifiedStudio.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SageMakerUnifiedStudio.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SageMakerUnifiedStudio.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SageMakerUnifiedStudio.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SageMakerUnifiedStudio.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +SageMakerUnifiedStudio.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +SageMakerUnifiedStudio.FILES = RelationField("files") +SageMakerUnifiedStudio.LINKS = RelationField("links") +SageMakerUnifiedStudio.README = RelationField("readme") +SageMakerUnifiedStudio.SCHEMA_REGISTRY_SUBJECTS = RelationField( + "schemaRegistrySubjects" +) +SageMakerUnifiedStudio.SODA_CHECKS = RelationField("sodaChecks") +SageMakerUnifiedStudio.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SageMakerUnifiedStudio.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/sage_maker_unified_studio_asset.py b/pyatlan_v9/model/assets/sage_maker_unified_studio_asset.py new file mode 100644 index 000000000..a0e7440d6 --- /dev/null +++ b/pyatlan_v9/model/assets/sage_maker_unified_studio_asset.py @@ -0,0 +1,720 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SageMakerUnifiedStudioAsset asset model with flattened inheritance. + +This module provides: +- SageMakerUnifiedStudioAsset: Flat asset class (easy to use) +- SageMakerUnifiedStudioAssetAttributes: Nested attributes struct (extends AssetAttributes) +- SageMakerUnifiedStudioAssetNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .sage_maker_unified_studio_related import RelatedSageMakerUnifiedStudioAssetSchema + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SageMakerUnifiedStudioAsset(Asset): + """ + Instance of SageMaker Unified Studio asset in Atlan. This holds common attributes for both Published and Subscribed assets. + """ + + SMUS_ASSET_SUMMARY: ClassVar[Any] = None + SMUS_ASSET_TECHNICAL_NAME: ClassVar[Any] = None + SMUS_ASSET_TYPE: ClassVar[Any] = None + SMUS_ASSET_REVISION: ClassVar[Any] = None + SMUS_ASSET_SOURCE_IDENTIFIER: ClassVar[Any] = None + SMUS_DOMAIN_NAME: ClassVar[Any] = None + SMUS_DOMAIN_ID: ClassVar[Any] = None + SMUS_DOMAIN_UNIT_NAME: ClassVar[Any] = None + SMUS_DOMAIN_UNIT_ID: ClassVar[Any] = None + SMUS_PROJECT_ID: ClassVar[Any] = None + SMUS_OWNING_PROJECT_ID: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SMUS_ASSET_SCHEMAS: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SageMakerUnifiedStudioAsset" + + smus_asset_summary: Union[str, None, UnsetType] = UNSET + """Summary text for the asset in SageMaker Unified Studio.""" + + smus_asset_technical_name: Union[str, None, UnsetType] = UNSET + """Technical name for the asset in SageMaker Unified Studio.""" + + smus_asset_type: Union[str, None, UnsetType] = UNSET + """Type of asset in SageMaker Unified Studio.""" + + smus_asset_revision: Union[str, None, UnsetType] = UNSET + """Latest published version of the asset in SageMaker Unified Studio.""" + + smus_asset_source_identifier: Union[str, None, UnsetType] = UNSET + """Unique source identifier for the asset in SageMaker Unified Studio.""" + + smus_domain_name: Union[str, None, UnsetType] = UNSET + """Name of the SageMaker Unified Studio domain.""" + + smus_domain_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio domain.""" + + smus_domain_unit_name: Union[str, None, UnsetType] = UNSET + """Name of the SageMaker Unified Studio domain unit.""" + + smus_domain_unit_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio domain unit.""" + + smus_project_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio project.""" + + smus_owning_project_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio project which owns the asset.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + smus_asset_schemas: Union[ + List[RelatedSageMakerUnifiedStudioAssetSchema], None, UnsetType + ] = UNSET + """Schemas that exist within this published asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SageMakerUnifiedStudioAsset" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _sage_maker_unified_studio_asset_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> SageMakerUnifiedStudioAsset: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SageMakerUnifiedStudioAsset instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _sage_maker_unified_studio_asset_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SageMakerUnifiedStudioAssetAttributes(AssetAttributes): + """SageMakerUnifiedStudioAsset-specific attributes for nested API format.""" + + smus_asset_summary: Union[str, None, UnsetType] = UNSET + """Summary text for the asset in SageMaker Unified Studio.""" + + smus_asset_technical_name: Union[str, None, UnsetType] = UNSET + """Technical name for the asset in SageMaker Unified Studio.""" + + smus_asset_type: Union[str, None, UnsetType] = UNSET + """Type of asset in SageMaker Unified Studio.""" + + smus_asset_revision: Union[str, None, UnsetType] = UNSET + """Latest published version of the asset in SageMaker Unified Studio.""" + + smus_asset_source_identifier: Union[str, None, UnsetType] = UNSET + """Unique source identifier for the asset in SageMaker Unified Studio.""" + + smus_domain_name: Union[str, None, UnsetType] = UNSET + """Name of the SageMaker Unified Studio domain.""" + + smus_domain_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio domain.""" + + smus_domain_unit_name: Union[str, None, UnsetType] = UNSET + """Name of the SageMaker Unified Studio domain unit.""" + + smus_domain_unit_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio domain unit.""" + + smus_project_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio project.""" + + smus_owning_project_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio project which owns the asset.""" + + +class SageMakerUnifiedStudioAssetRelationshipAttributes(AssetRelationshipAttributes): + """SageMakerUnifiedStudioAsset-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + smus_asset_schemas: Union[ + List[RelatedSageMakerUnifiedStudioAssetSchema], None, UnsetType + ] = UNSET + """Schemas that exist within this published asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SageMakerUnifiedStudioAssetNested(AssetNested): + """SageMakerUnifiedStudioAsset in nested API format for high-performance serialization.""" + + attributes: Union[SageMakerUnifiedStudioAssetAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + SageMakerUnifiedStudioAssetRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + SageMakerUnifiedStudioAssetRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SageMakerUnifiedStudioAssetRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SAGE_MAKER_UNIFIED_STUDIO_ASSET_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "smus_asset_schemas", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_sage_maker_unified_studio_asset_attrs( + attrs: SageMakerUnifiedStudioAssetAttributes, obj: SageMakerUnifiedStudioAsset +) -> None: + """Populate SageMakerUnifiedStudioAsset-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.smus_asset_summary = obj.smus_asset_summary + attrs.smus_asset_technical_name = obj.smus_asset_technical_name + attrs.smus_asset_type = obj.smus_asset_type + attrs.smus_asset_revision = obj.smus_asset_revision + attrs.smus_asset_source_identifier = obj.smus_asset_source_identifier + attrs.smus_domain_name = obj.smus_domain_name + attrs.smus_domain_id = obj.smus_domain_id + attrs.smus_domain_unit_name = obj.smus_domain_unit_name + attrs.smus_domain_unit_id = obj.smus_domain_unit_id + attrs.smus_project_id = obj.smus_project_id + attrs.smus_owning_project_id = obj.smus_owning_project_id + + +def _extract_sage_maker_unified_studio_asset_attrs( + attrs: SageMakerUnifiedStudioAssetAttributes, +) -> dict: + """Extract all SageMakerUnifiedStudioAsset attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["smus_asset_summary"] = attrs.smus_asset_summary + result["smus_asset_technical_name"] = attrs.smus_asset_technical_name + result["smus_asset_type"] = attrs.smus_asset_type + result["smus_asset_revision"] = attrs.smus_asset_revision + result["smus_asset_source_identifier"] = attrs.smus_asset_source_identifier + result["smus_domain_name"] = attrs.smus_domain_name + result["smus_domain_id"] = attrs.smus_domain_id + result["smus_domain_unit_name"] = attrs.smus_domain_unit_name + result["smus_domain_unit_id"] = attrs.smus_domain_unit_id + result["smus_project_id"] = attrs.smus_project_id + result["smus_owning_project_id"] = attrs.smus_owning_project_id + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _sage_maker_unified_studio_asset_to_nested( + sage_maker_unified_studio_asset: SageMakerUnifiedStudioAsset, +) -> SageMakerUnifiedStudioAssetNested: + """Convert flat SageMakerUnifiedStudioAsset to nested format.""" + attrs = SageMakerUnifiedStudioAssetAttributes() + _populate_sage_maker_unified_studio_asset_attrs( + attrs, sage_maker_unified_studio_asset + ) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + sage_maker_unified_studio_asset, + _SAGE_MAKER_UNIFIED_STUDIO_ASSET_REL_FIELDS, + SageMakerUnifiedStudioAssetRelationshipAttributes, + ) + return SageMakerUnifiedStudioAssetNested( + guid=sage_maker_unified_studio_asset.guid, + type_name=sage_maker_unified_studio_asset.type_name, + status=sage_maker_unified_studio_asset.status, + version=sage_maker_unified_studio_asset.version, + create_time=sage_maker_unified_studio_asset.create_time, + update_time=sage_maker_unified_studio_asset.update_time, + created_by=sage_maker_unified_studio_asset.created_by, + updated_by=sage_maker_unified_studio_asset.updated_by, + classifications=sage_maker_unified_studio_asset.classifications, + classification_names=sage_maker_unified_studio_asset.classification_names, + meanings=sage_maker_unified_studio_asset.meanings, + labels=sage_maker_unified_studio_asset.labels, + business_attributes=sage_maker_unified_studio_asset.business_attributes, + custom_attributes=sage_maker_unified_studio_asset.custom_attributes, + pending_tasks=sage_maker_unified_studio_asset.pending_tasks, + proxy=sage_maker_unified_studio_asset.proxy, + is_incomplete=sage_maker_unified_studio_asset.is_incomplete, + provenance_type=sage_maker_unified_studio_asset.provenance_type, + home_id=sage_maker_unified_studio_asset.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _sage_maker_unified_studio_asset_from_nested( + nested: SageMakerUnifiedStudioAssetNested, +) -> SageMakerUnifiedStudioAsset: + """Convert nested format to flat SageMakerUnifiedStudioAsset.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else SageMakerUnifiedStudioAssetAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SAGE_MAKER_UNIFIED_STUDIO_ASSET_REL_FIELDS, + SageMakerUnifiedStudioAssetRelationshipAttributes, + ) + return SageMakerUnifiedStudioAsset( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_sage_maker_unified_studio_asset_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _sage_maker_unified_studio_asset_to_nested_bytes( + sage_maker_unified_studio_asset: SageMakerUnifiedStudioAsset, serde: Serde +) -> bytes: + """Convert flat SageMakerUnifiedStudioAsset to nested JSON bytes.""" + return serde.encode( + _sage_maker_unified_studio_asset_to_nested(sage_maker_unified_studio_asset) + ) + + +def _sage_maker_unified_studio_asset_from_nested_bytes( + data: bytes, serde: Serde +) -> SageMakerUnifiedStudioAsset: + """Convert nested JSON bytes to flat SageMakerUnifiedStudioAsset.""" + nested = serde.decode(data, SageMakerUnifiedStudioAssetNested) + return _sage_maker_unified_studio_asset_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +SageMakerUnifiedStudioAsset.SMUS_ASSET_SUMMARY = KeywordField( + "smusAssetSummary", "smusAssetSummary" +) +SageMakerUnifiedStudioAsset.SMUS_ASSET_TECHNICAL_NAME = KeywordField( + "smusAssetTechnicalName", "smusAssetTechnicalName" +) +SageMakerUnifiedStudioAsset.SMUS_ASSET_TYPE = KeywordField( + "smusAssetType", "smusAssetType" +) +SageMakerUnifiedStudioAsset.SMUS_ASSET_REVISION = KeywordField( + "smusAssetRevision", "smusAssetRevision" +) +SageMakerUnifiedStudioAsset.SMUS_ASSET_SOURCE_IDENTIFIER = KeywordField( + "smusAssetSourceIdentifier", "smusAssetSourceIdentifier" +) +SageMakerUnifiedStudioAsset.SMUS_DOMAIN_NAME = KeywordField( + "smusDomainName", "smusDomainName" +) +SageMakerUnifiedStudioAsset.SMUS_DOMAIN_ID = KeywordField( + "smusDomainId", "smusDomainId" +) +SageMakerUnifiedStudioAsset.SMUS_DOMAIN_UNIT_NAME = KeywordField( + "smusDomainUnitName", "smusDomainUnitName" +) +SageMakerUnifiedStudioAsset.SMUS_DOMAIN_UNIT_ID = KeywordField( + "smusDomainUnitId", "smusDomainUnitId" +) +SageMakerUnifiedStudioAsset.SMUS_PROJECT_ID = KeywordField( + "smusProjectId", "smusProjectId" +) +SageMakerUnifiedStudioAsset.SMUS_OWNING_PROJECT_ID = KeywordField( + "smusOwningProjectId", "smusOwningProjectId" +) +SageMakerUnifiedStudioAsset.INPUT_TO_AIRFLOW_TASKS = RelationField( + "inputToAirflowTasks" +) +SageMakerUnifiedStudioAsset.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +SageMakerUnifiedStudioAsset.ANOMALO_CHECKS = RelationField("anomaloChecks") +SageMakerUnifiedStudioAsset.APPLICATION = RelationField("application") +SageMakerUnifiedStudioAsset.APPLICATION_FIELD = RelationField("applicationField") +SageMakerUnifiedStudioAsset.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +SageMakerUnifiedStudioAsset.INPUT_PORT_DATA_PRODUCTS = RelationField( + "inputPortDataProducts" +) +SageMakerUnifiedStudioAsset.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +SageMakerUnifiedStudioAsset.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +SageMakerUnifiedStudioAsset.METRICS = RelationField("metrics") +SageMakerUnifiedStudioAsset.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SageMakerUnifiedStudioAsset.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +SageMakerUnifiedStudioAsset.MEANINGS = RelationField("meanings") +SageMakerUnifiedStudioAsset.MC_MONITORS = RelationField("mcMonitors") +SageMakerUnifiedStudioAsset.MC_INCIDENTS = RelationField("mcIncidents") +SageMakerUnifiedStudioAsset.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SageMakerUnifiedStudioAsset.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SageMakerUnifiedStudioAsset.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SageMakerUnifiedStudioAsset.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SageMakerUnifiedStudioAsset.USER_DEF_RELATIONSHIP_TO = RelationField( + "userDefRelationshipTo" +) +SageMakerUnifiedStudioAsset.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +SageMakerUnifiedStudioAsset.FILES = RelationField("files") +SageMakerUnifiedStudioAsset.LINKS = RelationField("links") +SageMakerUnifiedStudioAsset.README = RelationField("readme") +SageMakerUnifiedStudioAsset.SMUS_ASSET_SCHEMAS = RelationField("smusAssetSchemas") +SageMakerUnifiedStudioAsset.SCHEMA_REGISTRY_SUBJECTS = RelationField( + "schemaRegistrySubjects" +) +SageMakerUnifiedStudioAsset.SODA_CHECKS = RelationField("sodaChecks") +SageMakerUnifiedStudioAsset.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SageMakerUnifiedStudioAsset.OUTPUT_FROM_SPARK_JOBS = RelationField( + "outputFromSparkJobs" +) diff --git a/pyatlan_v9/model/assets/sage_maker_unified_studio_asset_schema.py b/pyatlan_v9/model/assets/sage_maker_unified_studio_asset_schema.py new file mode 100644 index 000000000..0704125a2 --- /dev/null +++ b/pyatlan_v9/model/assets/sage_maker_unified_studio_asset_schema.py @@ -0,0 +1,717 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SageMakerUnifiedStudioAssetSchema asset model with flattened inheritance. + +This module provides: +- SageMakerUnifiedStudioAssetSchema: Flat asset class (easy to use) +- SageMakerUnifiedStudioAssetSchemaAttributes: Nested attributes struct (extends AssetAttributes) +- SageMakerUnifiedStudioAssetSchemaNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .sage_maker_unified_studio_related import RelatedSageMakerUnifiedStudioAsset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SageMakerUnifiedStudioAssetSchema(Asset): + """ + Schema definition for an asset in SageMaker Unified Studio, analogous to columns for a SQL table. + """ + + SMUS_DATA_TYPE: ClassVar[Any] = None + SMUS_ASSET_QUALIFIED_NAME: ClassVar[Any] = None + SMUS_ASSET_NAME: ClassVar[Any] = None + SMUS_DOMAIN_NAME: ClassVar[Any] = None + SMUS_DOMAIN_ID: ClassVar[Any] = None + SMUS_DOMAIN_UNIT_NAME: ClassVar[Any] = None + SMUS_DOMAIN_UNIT_ID: ClassVar[Any] = None + SMUS_PROJECT_ID: ClassVar[Any] = None + SMUS_OWNING_PROJECT_ID: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SMUS_ASSET: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SageMakerUnifiedStudioAssetSchema" + + smus_data_type: Union[str, None, UnsetType] = UNSET + """Data type of the schema/column.""" + + smus_asset_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the Atlan SageMaker Unified Studio published/subscribed asset that contains this schema.""" + + smus_asset_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Atlan SageMaker Unified Studio published/subscribed asset that contains this schema.""" + + smus_domain_name: Union[str, None, UnsetType] = UNSET + """Name of the SageMaker Unified Studio domain.""" + + smus_domain_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio domain.""" + + smus_domain_unit_name: Union[str, None, UnsetType] = UNSET + """Name of the SageMaker Unified Studio domain unit.""" + + smus_domain_unit_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio domain unit.""" + + smus_project_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio project.""" + + smus_owning_project_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio project which owns the asset.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + smus_asset: Union[RelatedSageMakerUnifiedStudioAsset, None, UnsetType] = UNSET + """Asset in which this schema exists. The asset can be a published or subscribed asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SageMakerUnifiedStudioAssetSchema" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _sage_maker_unified_studio_asset_schema_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> SageMakerUnifiedStudioAssetSchema: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SageMakerUnifiedStudioAssetSchema instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _sage_maker_unified_studio_asset_schema_from_nested_bytes( + json_data, serde + ) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SageMakerUnifiedStudioAssetSchemaAttributes(AssetAttributes): + """SageMakerUnifiedStudioAssetSchema-specific attributes for nested API format.""" + + smus_data_type: Union[str, None, UnsetType] = UNSET + """Data type of the schema/column.""" + + smus_asset_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the Atlan SageMaker Unified Studio published/subscribed asset that contains this schema.""" + + smus_asset_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Atlan SageMaker Unified Studio published/subscribed asset that contains this schema.""" + + smus_domain_name: Union[str, None, UnsetType] = UNSET + """Name of the SageMaker Unified Studio domain.""" + + smus_domain_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio domain.""" + + smus_domain_unit_name: Union[str, None, UnsetType] = UNSET + """Name of the SageMaker Unified Studio domain unit.""" + + smus_domain_unit_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio domain unit.""" + + smus_project_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio project.""" + + smus_owning_project_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio project which owns the asset.""" + + +class SageMakerUnifiedStudioAssetSchemaRelationshipAttributes( + AssetRelationshipAttributes +): + """SageMakerUnifiedStudioAssetSchema-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + smus_asset: Union[RelatedSageMakerUnifiedStudioAsset, None, UnsetType] = UNSET + """Asset in which this schema exists. The asset can be a published or subscribed asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SageMakerUnifiedStudioAssetSchemaNested(AssetNested): + """SageMakerUnifiedStudioAssetSchema in nested API format for high-performance serialization.""" + + attributes: Union[SageMakerUnifiedStudioAssetSchemaAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + SageMakerUnifiedStudioAssetSchemaRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + SageMakerUnifiedStudioAssetSchemaRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SageMakerUnifiedStudioAssetSchemaRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SAGE_MAKER_UNIFIED_STUDIO_ASSET_SCHEMA_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "smus_asset", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_sage_maker_unified_studio_asset_schema_attrs( + attrs: SageMakerUnifiedStudioAssetSchemaAttributes, + obj: SageMakerUnifiedStudioAssetSchema, +) -> None: + """Populate SageMakerUnifiedStudioAssetSchema-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.smus_data_type = obj.smus_data_type + attrs.smus_asset_qualified_name = obj.smus_asset_qualified_name + attrs.smus_asset_name = obj.smus_asset_name + attrs.smus_domain_name = obj.smus_domain_name + attrs.smus_domain_id = obj.smus_domain_id + attrs.smus_domain_unit_name = obj.smus_domain_unit_name + attrs.smus_domain_unit_id = obj.smus_domain_unit_id + attrs.smus_project_id = obj.smus_project_id + attrs.smus_owning_project_id = obj.smus_owning_project_id + + +def _extract_sage_maker_unified_studio_asset_schema_attrs( + attrs: SageMakerUnifiedStudioAssetSchemaAttributes, +) -> dict: + """Extract all SageMakerUnifiedStudioAssetSchema attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["smus_data_type"] = attrs.smus_data_type + result["smus_asset_qualified_name"] = attrs.smus_asset_qualified_name + result["smus_asset_name"] = attrs.smus_asset_name + result["smus_domain_name"] = attrs.smus_domain_name + result["smus_domain_id"] = attrs.smus_domain_id + result["smus_domain_unit_name"] = attrs.smus_domain_unit_name + result["smus_domain_unit_id"] = attrs.smus_domain_unit_id + result["smus_project_id"] = attrs.smus_project_id + result["smus_owning_project_id"] = attrs.smus_owning_project_id + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _sage_maker_unified_studio_asset_schema_to_nested( + sage_maker_unified_studio_asset_schema: SageMakerUnifiedStudioAssetSchema, +) -> SageMakerUnifiedStudioAssetSchemaNested: + """Convert flat SageMakerUnifiedStudioAssetSchema to nested format.""" + attrs = SageMakerUnifiedStudioAssetSchemaAttributes() + _populate_sage_maker_unified_studio_asset_schema_attrs( + attrs, sage_maker_unified_studio_asset_schema + ) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + sage_maker_unified_studio_asset_schema, + _SAGE_MAKER_UNIFIED_STUDIO_ASSET_SCHEMA_REL_FIELDS, + SageMakerUnifiedStudioAssetSchemaRelationshipAttributes, + ) + return SageMakerUnifiedStudioAssetSchemaNested( + guid=sage_maker_unified_studio_asset_schema.guid, + type_name=sage_maker_unified_studio_asset_schema.type_name, + status=sage_maker_unified_studio_asset_schema.status, + version=sage_maker_unified_studio_asset_schema.version, + create_time=sage_maker_unified_studio_asset_schema.create_time, + update_time=sage_maker_unified_studio_asset_schema.update_time, + created_by=sage_maker_unified_studio_asset_schema.created_by, + updated_by=sage_maker_unified_studio_asset_schema.updated_by, + classifications=sage_maker_unified_studio_asset_schema.classifications, + classification_names=sage_maker_unified_studio_asset_schema.classification_names, + meanings=sage_maker_unified_studio_asset_schema.meanings, + labels=sage_maker_unified_studio_asset_schema.labels, + business_attributes=sage_maker_unified_studio_asset_schema.business_attributes, + custom_attributes=sage_maker_unified_studio_asset_schema.custom_attributes, + pending_tasks=sage_maker_unified_studio_asset_schema.pending_tasks, + proxy=sage_maker_unified_studio_asset_schema.proxy, + is_incomplete=sage_maker_unified_studio_asset_schema.is_incomplete, + provenance_type=sage_maker_unified_studio_asset_schema.provenance_type, + home_id=sage_maker_unified_studio_asset_schema.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _sage_maker_unified_studio_asset_schema_from_nested( + nested: SageMakerUnifiedStudioAssetSchemaNested, +) -> SageMakerUnifiedStudioAssetSchema: + """Convert nested format to flat SageMakerUnifiedStudioAssetSchema.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else SageMakerUnifiedStudioAssetSchemaAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SAGE_MAKER_UNIFIED_STUDIO_ASSET_SCHEMA_REL_FIELDS, + SageMakerUnifiedStudioAssetSchemaRelationshipAttributes, + ) + return SageMakerUnifiedStudioAssetSchema( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_sage_maker_unified_studio_asset_schema_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _sage_maker_unified_studio_asset_schema_to_nested_bytes( + sage_maker_unified_studio_asset_schema: SageMakerUnifiedStudioAssetSchema, + serde: Serde, +) -> bytes: + """Convert flat SageMakerUnifiedStudioAssetSchema to nested JSON bytes.""" + return serde.encode( + _sage_maker_unified_studio_asset_schema_to_nested( + sage_maker_unified_studio_asset_schema + ) + ) + + +def _sage_maker_unified_studio_asset_schema_from_nested_bytes( + data: bytes, serde: Serde +) -> SageMakerUnifiedStudioAssetSchema: + """Convert nested JSON bytes to flat SageMakerUnifiedStudioAssetSchema.""" + nested = serde.decode(data, SageMakerUnifiedStudioAssetSchemaNested) + return _sage_maker_unified_studio_asset_schema_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +SageMakerUnifiedStudioAssetSchema.SMUS_DATA_TYPE = KeywordField( + "smusDataType", "smusDataType" +) +SageMakerUnifiedStudioAssetSchema.SMUS_ASSET_QUALIFIED_NAME = KeywordField( + "smusAssetQualifiedName", "smusAssetQualifiedName" +) +SageMakerUnifiedStudioAssetSchema.SMUS_ASSET_NAME = KeywordField( + "smusAssetName", "smusAssetName" +) +SageMakerUnifiedStudioAssetSchema.SMUS_DOMAIN_NAME = KeywordField( + "smusDomainName", "smusDomainName" +) +SageMakerUnifiedStudioAssetSchema.SMUS_DOMAIN_ID = KeywordField( + "smusDomainId", "smusDomainId" +) +SageMakerUnifiedStudioAssetSchema.SMUS_DOMAIN_UNIT_NAME = KeywordField( + "smusDomainUnitName", "smusDomainUnitName" +) +SageMakerUnifiedStudioAssetSchema.SMUS_DOMAIN_UNIT_ID = KeywordField( + "smusDomainUnitId", "smusDomainUnitId" +) +SageMakerUnifiedStudioAssetSchema.SMUS_PROJECT_ID = KeywordField( + "smusProjectId", "smusProjectId" +) +SageMakerUnifiedStudioAssetSchema.SMUS_OWNING_PROJECT_ID = KeywordField( + "smusOwningProjectId", "smusOwningProjectId" +) +SageMakerUnifiedStudioAssetSchema.INPUT_TO_AIRFLOW_TASKS = RelationField( + "inputToAirflowTasks" +) +SageMakerUnifiedStudioAssetSchema.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +SageMakerUnifiedStudioAssetSchema.ANOMALO_CHECKS = RelationField("anomaloChecks") +SageMakerUnifiedStudioAssetSchema.APPLICATION = RelationField("application") +SageMakerUnifiedStudioAssetSchema.APPLICATION_FIELD = RelationField("applicationField") +SageMakerUnifiedStudioAssetSchema.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +SageMakerUnifiedStudioAssetSchema.INPUT_PORT_DATA_PRODUCTS = RelationField( + "inputPortDataProducts" +) +SageMakerUnifiedStudioAssetSchema.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +SageMakerUnifiedStudioAssetSchema.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +SageMakerUnifiedStudioAssetSchema.METRICS = RelationField("metrics") +SageMakerUnifiedStudioAssetSchema.DQ_BASE_DATASET_RULES = RelationField( + "dqBaseDatasetRules" +) +SageMakerUnifiedStudioAssetSchema.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +SageMakerUnifiedStudioAssetSchema.MEANINGS = RelationField("meanings") +SageMakerUnifiedStudioAssetSchema.MC_MONITORS = RelationField("mcMonitors") +SageMakerUnifiedStudioAssetSchema.MC_INCIDENTS = RelationField("mcIncidents") +SageMakerUnifiedStudioAssetSchema.PARTIAL_CHILD_FIELDS = RelationField( + "partialChildFields" +) +SageMakerUnifiedStudioAssetSchema.PARTIAL_CHILD_OBJECTS = RelationField( + "partialChildObjects" +) +SageMakerUnifiedStudioAssetSchema.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SageMakerUnifiedStudioAssetSchema.OUTPUT_FROM_PROCESSES = RelationField( + "outputFromProcesses" +) +SageMakerUnifiedStudioAssetSchema.USER_DEF_RELATIONSHIP_TO = RelationField( + "userDefRelationshipTo" +) +SageMakerUnifiedStudioAssetSchema.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +SageMakerUnifiedStudioAssetSchema.FILES = RelationField("files") +SageMakerUnifiedStudioAssetSchema.LINKS = RelationField("links") +SageMakerUnifiedStudioAssetSchema.README = RelationField("readme") +SageMakerUnifiedStudioAssetSchema.SMUS_ASSET = RelationField("smusAsset") +SageMakerUnifiedStudioAssetSchema.SCHEMA_REGISTRY_SUBJECTS = RelationField( + "schemaRegistrySubjects" +) +SageMakerUnifiedStudioAssetSchema.SODA_CHECKS = RelationField("sodaChecks") +SageMakerUnifiedStudioAssetSchema.INPUT_TO_SPARK_JOBS = RelationField( + "inputToSparkJobs" +) +SageMakerUnifiedStudioAssetSchema.OUTPUT_FROM_SPARK_JOBS = RelationField( + "outputFromSparkJobs" +) diff --git a/pyatlan_v9/model/assets/sage_maker_unified_studio_project.py b/pyatlan_v9/model/assets/sage_maker_unified_studio_project.py new file mode 100644 index 000000000..c598facca --- /dev/null +++ b/pyatlan_v9/model/assets/sage_maker_unified_studio_project.py @@ -0,0 +1,734 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SageMakerUnifiedStudioProject asset model with flattened inheritance. + +This module provides: +- SageMakerUnifiedStudioProject: Flat asset class (easy to use) +- SageMakerUnifiedStudioProjectAttributes: Nested attributes struct (extends AssetAttributes) +- SageMakerUnifiedStudioProjectNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .sage_maker_unified_studio_related import ( + RelatedSageMakerUnifiedStudioPublishedAsset, + RelatedSageMakerUnifiedStudioSubscribedAsset, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SageMakerUnifiedStudioProject(Asset): + """ + Instance of SageMaker Unified Studio Project in Atlan. + """ + + SMUS_PROJECT_STATUS: ClassVar[Any] = None + SMUS_PROJECT_PROFILE_NAME: ClassVar[Any] = None + SMUS_PROJECT_ROLE_ARN: ClassVar[Any] = None + SMUS_PROJECT_S3_LOCATION: ClassVar[Any] = None + SMUS_DOMAIN_NAME: ClassVar[Any] = None + SMUS_DOMAIN_ID: ClassVar[Any] = None + SMUS_DOMAIN_UNIT_NAME: ClassVar[Any] = None + SMUS_DOMAIN_UNIT_ID: ClassVar[Any] = None + SMUS_PROJECT_ID: ClassVar[Any] = None + SMUS_OWNING_PROJECT_ID: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SMUS_PUBLISHED_ASSETS: ClassVar[Any] = None + SMUS_SUBSCRIBED_ASSETS: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SageMakerUnifiedStudioProject" + + smus_project_status: Union[str, None, UnsetType] = UNSET + """Status of the SageMaker Unified Studio project.""" + + smus_project_profile_name: Union[str, None, UnsetType] = UNSET + """Name of the profile of the SageMaker Unified Studio project.""" + + smus_project_role_arn: Union[str, None, UnsetType] = UNSET + """Amazon IAM role ARN of the SageMaker Unified Studio project.""" + + smus_project_s3_location: Union[str, None, UnsetType] = UNSET + """Amazon S3 location of the SageMaker Unified Studio project.""" + + smus_domain_name: Union[str, None, UnsetType] = UNSET + """Name of the SageMaker Unified Studio domain.""" + + smus_domain_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio domain.""" + + smus_domain_unit_name: Union[str, None, UnsetType] = UNSET + """Name of the SageMaker Unified Studio domain unit.""" + + smus_domain_unit_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio domain unit.""" + + smus_project_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio project.""" + + smus_owning_project_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio project which owns the asset.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + smus_published_assets: Union[ + List[RelatedSageMakerUnifiedStudioPublishedAsset], None, UnsetType + ] = UNSET + """Individual published assets contained in the project.""" + + smus_subscribed_assets: Union[ + List[RelatedSageMakerUnifiedStudioSubscribedAsset], None, UnsetType + ] = UNSET + """Individual subscribed assets contained in the project.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SageMakerUnifiedStudioProject" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _sage_maker_unified_studio_project_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> SageMakerUnifiedStudioProject: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SageMakerUnifiedStudioProject instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _sage_maker_unified_studio_project_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SageMakerUnifiedStudioProjectAttributes(AssetAttributes): + """SageMakerUnifiedStudioProject-specific attributes for nested API format.""" + + smus_project_status: Union[str, None, UnsetType] = UNSET + """Status of the SageMaker Unified Studio project.""" + + smus_project_profile_name: Union[str, None, UnsetType] = UNSET + """Name of the profile of the SageMaker Unified Studio project.""" + + smus_project_role_arn: Union[str, None, UnsetType] = UNSET + """Amazon IAM role ARN of the SageMaker Unified Studio project.""" + + smus_project_s3_location: Union[str, None, UnsetType] = UNSET + """Amazon S3 location of the SageMaker Unified Studio project.""" + + smus_domain_name: Union[str, None, UnsetType] = UNSET + """Name of the SageMaker Unified Studio domain.""" + + smus_domain_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio domain.""" + + smus_domain_unit_name: Union[str, None, UnsetType] = UNSET + """Name of the SageMaker Unified Studio domain unit.""" + + smus_domain_unit_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio domain unit.""" + + smus_project_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio project.""" + + smus_owning_project_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio project which owns the asset.""" + + +class SageMakerUnifiedStudioProjectRelationshipAttributes(AssetRelationshipAttributes): + """SageMakerUnifiedStudioProject-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + smus_published_assets: Union[ + List[RelatedSageMakerUnifiedStudioPublishedAsset], None, UnsetType + ] = UNSET + """Individual published assets contained in the project.""" + + smus_subscribed_assets: Union[ + List[RelatedSageMakerUnifiedStudioSubscribedAsset], None, UnsetType + ] = UNSET + """Individual subscribed assets contained in the project.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SageMakerUnifiedStudioProjectNested(AssetNested): + """SageMakerUnifiedStudioProject in nested API format for high-performance serialization.""" + + attributes: Union[SageMakerUnifiedStudioProjectAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + SageMakerUnifiedStudioProjectRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + SageMakerUnifiedStudioProjectRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SageMakerUnifiedStudioProjectRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SAGE_MAKER_UNIFIED_STUDIO_PROJECT_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "smus_published_assets", + "smus_subscribed_assets", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_sage_maker_unified_studio_project_attrs( + attrs: SageMakerUnifiedStudioProjectAttributes, obj: SageMakerUnifiedStudioProject +) -> None: + """Populate SageMakerUnifiedStudioProject-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.smus_project_status = obj.smus_project_status + attrs.smus_project_profile_name = obj.smus_project_profile_name + attrs.smus_project_role_arn = obj.smus_project_role_arn + attrs.smus_project_s3_location = obj.smus_project_s3_location + attrs.smus_domain_name = obj.smus_domain_name + attrs.smus_domain_id = obj.smus_domain_id + attrs.smus_domain_unit_name = obj.smus_domain_unit_name + attrs.smus_domain_unit_id = obj.smus_domain_unit_id + attrs.smus_project_id = obj.smus_project_id + attrs.smus_owning_project_id = obj.smus_owning_project_id + + +def _extract_sage_maker_unified_studio_project_attrs( + attrs: SageMakerUnifiedStudioProjectAttributes, +) -> dict: + """Extract all SageMakerUnifiedStudioProject attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["smus_project_status"] = attrs.smus_project_status + result["smus_project_profile_name"] = attrs.smus_project_profile_name + result["smus_project_role_arn"] = attrs.smus_project_role_arn + result["smus_project_s3_location"] = attrs.smus_project_s3_location + result["smus_domain_name"] = attrs.smus_domain_name + result["smus_domain_id"] = attrs.smus_domain_id + result["smus_domain_unit_name"] = attrs.smus_domain_unit_name + result["smus_domain_unit_id"] = attrs.smus_domain_unit_id + result["smus_project_id"] = attrs.smus_project_id + result["smus_owning_project_id"] = attrs.smus_owning_project_id + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _sage_maker_unified_studio_project_to_nested( + sage_maker_unified_studio_project: SageMakerUnifiedStudioProject, +) -> SageMakerUnifiedStudioProjectNested: + """Convert flat SageMakerUnifiedStudioProject to nested format.""" + attrs = SageMakerUnifiedStudioProjectAttributes() + _populate_sage_maker_unified_studio_project_attrs( + attrs, sage_maker_unified_studio_project + ) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + sage_maker_unified_studio_project, + _SAGE_MAKER_UNIFIED_STUDIO_PROJECT_REL_FIELDS, + SageMakerUnifiedStudioProjectRelationshipAttributes, + ) + return SageMakerUnifiedStudioProjectNested( + guid=sage_maker_unified_studio_project.guid, + type_name=sage_maker_unified_studio_project.type_name, + status=sage_maker_unified_studio_project.status, + version=sage_maker_unified_studio_project.version, + create_time=sage_maker_unified_studio_project.create_time, + update_time=sage_maker_unified_studio_project.update_time, + created_by=sage_maker_unified_studio_project.created_by, + updated_by=sage_maker_unified_studio_project.updated_by, + classifications=sage_maker_unified_studio_project.classifications, + classification_names=sage_maker_unified_studio_project.classification_names, + meanings=sage_maker_unified_studio_project.meanings, + labels=sage_maker_unified_studio_project.labels, + business_attributes=sage_maker_unified_studio_project.business_attributes, + custom_attributes=sage_maker_unified_studio_project.custom_attributes, + pending_tasks=sage_maker_unified_studio_project.pending_tasks, + proxy=sage_maker_unified_studio_project.proxy, + is_incomplete=sage_maker_unified_studio_project.is_incomplete, + provenance_type=sage_maker_unified_studio_project.provenance_type, + home_id=sage_maker_unified_studio_project.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _sage_maker_unified_studio_project_from_nested( + nested: SageMakerUnifiedStudioProjectNested, +) -> SageMakerUnifiedStudioProject: + """Convert nested format to flat SageMakerUnifiedStudioProject.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else SageMakerUnifiedStudioProjectAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SAGE_MAKER_UNIFIED_STUDIO_PROJECT_REL_FIELDS, + SageMakerUnifiedStudioProjectRelationshipAttributes, + ) + return SageMakerUnifiedStudioProject( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_sage_maker_unified_studio_project_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _sage_maker_unified_studio_project_to_nested_bytes( + sage_maker_unified_studio_project: SageMakerUnifiedStudioProject, serde: Serde +) -> bytes: + """Convert flat SageMakerUnifiedStudioProject to nested JSON bytes.""" + return serde.encode( + _sage_maker_unified_studio_project_to_nested(sage_maker_unified_studio_project) + ) + + +def _sage_maker_unified_studio_project_from_nested_bytes( + data: bytes, serde: Serde +) -> SageMakerUnifiedStudioProject: + """Convert nested JSON bytes to flat SageMakerUnifiedStudioProject.""" + nested = serde.decode(data, SageMakerUnifiedStudioProjectNested) + return _sage_maker_unified_studio_project_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +SageMakerUnifiedStudioProject.SMUS_PROJECT_STATUS = KeywordField( + "smusProjectStatus", "smusProjectStatus" +) +SageMakerUnifiedStudioProject.SMUS_PROJECT_PROFILE_NAME = KeywordField( + "smusProjectProfileName", "smusProjectProfileName" +) +SageMakerUnifiedStudioProject.SMUS_PROJECT_ROLE_ARN = KeywordField( + "smusProjectRoleArn", "smusProjectRoleArn" +) +SageMakerUnifiedStudioProject.SMUS_PROJECT_S3_LOCATION = KeywordField( + "smusProjectS3Location", "smusProjectS3Location" +) +SageMakerUnifiedStudioProject.SMUS_DOMAIN_NAME = KeywordField( + "smusDomainName", "smusDomainName" +) +SageMakerUnifiedStudioProject.SMUS_DOMAIN_ID = KeywordField( + "smusDomainId", "smusDomainId" +) +SageMakerUnifiedStudioProject.SMUS_DOMAIN_UNIT_NAME = KeywordField( + "smusDomainUnitName", "smusDomainUnitName" +) +SageMakerUnifiedStudioProject.SMUS_DOMAIN_UNIT_ID = KeywordField( + "smusDomainUnitId", "smusDomainUnitId" +) +SageMakerUnifiedStudioProject.SMUS_PROJECT_ID = KeywordField( + "smusProjectId", "smusProjectId" +) +SageMakerUnifiedStudioProject.SMUS_OWNING_PROJECT_ID = KeywordField( + "smusOwningProjectId", "smusOwningProjectId" +) +SageMakerUnifiedStudioProject.INPUT_TO_AIRFLOW_TASKS = RelationField( + "inputToAirflowTasks" +) +SageMakerUnifiedStudioProject.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +SageMakerUnifiedStudioProject.ANOMALO_CHECKS = RelationField("anomaloChecks") +SageMakerUnifiedStudioProject.APPLICATION = RelationField("application") +SageMakerUnifiedStudioProject.APPLICATION_FIELD = RelationField("applicationField") +SageMakerUnifiedStudioProject.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +SageMakerUnifiedStudioProject.INPUT_PORT_DATA_PRODUCTS = RelationField( + "inputPortDataProducts" +) +SageMakerUnifiedStudioProject.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +SageMakerUnifiedStudioProject.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +SageMakerUnifiedStudioProject.METRICS = RelationField("metrics") +SageMakerUnifiedStudioProject.DQ_BASE_DATASET_RULES = RelationField( + "dqBaseDatasetRules" +) +SageMakerUnifiedStudioProject.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +SageMakerUnifiedStudioProject.MEANINGS = RelationField("meanings") +SageMakerUnifiedStudioProject.MC_MONITORS = RelationField("mcMonitors") +SageMakerUnifiedStudioProject.MC_INCIDENTS = RelationField("mcIncidents") +SageMakerUnifiedStudioProject.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SageMakerUnifiedStudioProject.PARTIAL_CHILD_OBJECTS = RelationField( + "partialChildObjects" +) +SageMakerUnifiedStudioProject.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SageMakerUnifiedStudioProject.OUTPUT_FROM_PROCESSES = RelationField( + "outputFromProcesses" +) +SageMakerUnifiedStudioProject.USER_DEF_RELATIONSHIP_TO = RelationField( + "userDefRelationshipTo" +) +SageMakerUnifiedStudioProject.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +SageMakerUnifiedStudioProject.FILES = RelationField("files") +SageMakerUnifiedStudioProject.LINKS = RelationField("links") +SageMakerUnifiedStudioProject.README = RelationField("readme") +SageMakerUnifiedStudioProject.SMUS_PUBLISHED_ASSETS = RelationField( + "smusPublishedAssets" +) +SageMakerUnifiedStudioProject.SMUS_SUBSCRIBED_ASSETS = RelationField( + "smusSubscribedAssets" +) +SageMakerUnifiedStudioProject.SCHEMA_REGISTRY_SUBJECTS = RelationField( + "schemaRegistrySubjects" +) +SageMakerUnifiedStudioProject.SODA_CHECKS = RelationField("sodaChecks") +SageMakerUnifiedStudioProject.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SageMakerUnifiedStudioProject.OUTPUT_FROM_SPARK_JOBS = RelationField( + "outputFromSparkJobs" +) diff --git a/pyatlan_v9/model/assets/sage_maker_unified_studio_published_asset.py b/pyatlan_v9/model/assets/sage_maker_unified_studio_published_asset.py new file mode 100644 index 000000000..3251946fd --- /dev/null +++ b/pyatlan_v9/model/assets/sage_maker_unified_studio_published_asset.py @@ -0,0 +1,798 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SageMakerUnifiedStudioPublishedAsset asset model with flattened inheritance. + +This module provides: +- SageMakerUnifiedStudioPublishedAsset: Flat asset class (easy to use) +- SageMakerUnifiedStudioPublishedAssetAttributes: Nested attributes struct (extends AssetAttributes) +- SageMakerUnifiedStudioPublishedAssetNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .sage_maker_unified_studio_related import ( + RelatedSageMakerUnifiedStudioAssetSchema, + RelatedSageMakerUnifiedStudioProject, + RelatedSageMakerUnifiedStudioSubscribedAsset, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SageMakerUnifiedStudioPublishedAsset(Asset): + """ + Instance of SageMaker Unified Studio published asset in Atlan. + """ + + SMUS_PUBLISHED_ASSET_SUBSCRIPTIONS_COUNT: ClassVar[Any] = None + SMUS_DOMAIN_NAME: ClassVar[Any] = None + SMUS_DOMAIN_ID: ClassVar[Any] = None + SMUS_DOMAIN_UNIT_NAME: ClassVar[Any] = None + SMUS_DOMAIN_UNIT_ID: ClassVar[Any] = None + SMUS_PROJECT_ID: ClassVar[Any] = None + SMUS_OWNING_PROJECT_ID: ClassVar[Any] = None + SMUS_ASSET_SUMMARY: ClassVar[Any] = None + SMUS_ASSET_TECHNICAL_NAME: ClassVar[Any] = None + SMUS_ASSET_TYPE: ClassVar[Any] = None + SMUS_ASSET_REVISION: ClassVar[Any] = None + SMUS_ASSET_SOURCE_IDENTIFIER: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SMUS_PROJECT: ClassVar[Any] = None + SMUS_ASSET_SCHEMAS: ClassVar[Any] = None + SMUS_SUBSCRIBED_ASSETS: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SageMakerUnifiedStudioPublishedAsset" + + smus_published_asset_subscriptions_count: Union[int, None, UnsetType] = UNSET + """Number of subscriptions for the published asset.""" + + smus_domain_name: Union[str, None, UnsetType] = UNSET + """Name of the SageMaker Unified Studio domain.""" + + smus_domain_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio domain.""" + + smus_domain_unit_name: Union[str, None, UnsetType] = UNSET + """Name of the SageMaker Unified Studio domain unit.""" + + smus_domain_unit_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio domain unit.""" + + smus_project_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio project.""" + + smus_owning_project_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio project which owns the asset.""" + + smus_asset_summary: Union[str, None, UnsetType] = UNSET + """Summary text for the asset in SageMaker Unified Studio.""" + + smus_asset_technical_name: Union[str, None, UnsetType] = UNSET + """Technical name for the asset in SageMaker Unified Studio.""" + + smus_asset_type: Union[str, None, UnsetType] = UNSET + """Type of asset in SageMaker Unified Studio.""" + + smus_asset_revision: Union[str, None, UnsetType] = UNSET + """Latest published version of the asset in SageMaker Unified Studio.""" + + smus_asset_source_identifier: Union[str, None, UnsetType] = UNSET + """Unique source identifier for the asset in SageMaker Unified Studio.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + smus_project: Union[RelatedSageMakerUnifiedStudioProject, None, UnsetType] = UNSET + """Project containing the published asset.""" + + smus_asset_schemas: Union[ + List[RelatedSageMakerUnifiedStudioAssetSchema], None, UnsetType + ] = UNSET + """Schemas that exist within this published asset.""" + + smus_subscribed_assets: Union[ + List[RelatedSageMakerUnifiedStudioSubscribedAsset], None, UnsetType + ] = UNSET + """Subscribed assets associated with this published asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SageMakerUnifiedStudioPublishedAsset" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _sage_maker_unified_studio_published_asset_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> SageMakerUnifiedStudioPublishedAsset: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SageMakerUnifiedStudioPublishedAsset instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _sage_maker_unified_studio_published_asset_from_nested_bytes( + json_data, serde + ) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SageMakerUnifiedStudioPublishedAssetAttributes(AssetAttributes): + """SageMakerUnifiedStudioPublishedAsset-specific attributes for nested API format.""" + + smus_published_asset_subscriptions_count: Union[int, None, UnsetType] = UNSET + """Number of subscriptions for the published asset.""" + + smus_domain_name: Union[str, None, UnsetType] = UNSET + """Name of the SageMaker Unified Studio domain.""" + + smus_domain_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio domain.""" + + smus_domain_unit_name: Union[str, None, UnsetType] = UNSET + """Name of the SageMaker Unified Studio domain unit.""" + + smus_domain_unit_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio domain unit.""" + + smus_project_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio project.""" + + smus_owning_project_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio project which owns the asset.""" + + smus_asset_summary: Union[str, None, UnsetType] = UNSET + """Summary text for the asset in SageMaker Unified Studio.""" + + smus_asset_technical_name: Union[str, None, UnsetType] = UNSET + """Technical name for the asset in SageMaker Unified Studio.""" + + smus_asset_type: Union[str, None, UnsetType] = UNSET + """Type of asset in SageMaker Unified Studio.""" + + smus_asset_revision: Union[str, None, UnsetType] = UNSET + """Latest published version of the asset in SageMaker Unified Studio.""" + + smus_asset_source_identifier: Union[str, None, UnsetType] = UNSET + """Unique source identifier for the asset in SageMaker Unified Studio.""" + + +class SageMakerUnifiedStudioPublishedAssetRelationshipAttributes( + AssetRelationshipAttributes +): + """SageMakerUnifiedStudioPublishedAsset-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + smus_project: Union[RelatedSageMakerUnifiedStudioProject, None, UnsetType] = UNSET + """Project containing the published asset.""" + + smus_asset_schemas: Union[ + List[RelatedSageMakerUnifiedStudioAssetSchema], None, UnsetType + ] = UNSET + """Schemas that exist within this published asset.""" + + smus_subscribed_assets: Union[ + List[RelatedSageMakerUnifiedStudioSubscribedAsset], None, UnsetType + ] = UNSET + """Subscribed assets associated with this published asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SageMakerUnifiedStudioPublishedAssetNested(AssetNested): + """SageMakerUnifiedStudioPublishedAsset in nested API format for high-performance serialization.""" + + attributes: Union[SageMakerUnifiedStudioPublishedAssetAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + SageMakerUnifiedStudioPublishedAssetRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + SageMakerUnifiedStudioPublishedAssetRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SageMakerUnifiedStudioPublishedAssetRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SAGE_MAKER_UNIFIED_STUDIO_PUBLISHED_ASSET_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "smus_project", + "smus_asset_schemas", + "smus_subscribed_assets", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_sage_maker_unified_studio_published_asset_attrs( + attrs: SageMakerUnifiedStudioPublishedAssetAttributes, + obj: SageMakerUnifiedStudioPublishedAsset, +) -> None: + """Populate SageMakerUnifiedStudioPublishedAsset-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.smus_published_asset_subscriptions_count = ( + obj.smus_published_asset_subscriptions_count + ) + attrs.smus_domain_name = obj.smus_domain_name + attrs.smus_domain_id = obj.smus_domain_id + attrs.smus_domain_unit_name = obj.smus_domain_unit_name + attrs.smus_domain_unit_id = obj.smus_domain_unit_id + attrs.smus_project_id = obj.smus_project_id + attrs.smus_owning_project_id = obj.smus_owning_project_id + attrs.smus_asset_summary = obj.smus_asset_summary + attrs.smus_asset_technical_name = obj.smus_asset_technical_name + attrs.smus_asset_type = obj.smus_asset_type + attrs.smus_asset_revision = obj.smus_asset_revision + attrs.smus_asset_source_identifier = obj.smus_asset_source_identifier + + +def _extract_sage_maker_unified_studio_published_asset_attrs( + attrs: SageMakerUnifiedStudioPublishedAssetAttributes, +) -> dict: + """Extract all SageMakerUnifiedStudioPublishedAsset attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["smus_published_asset_subscriptions_count"] = ( + attrs.smus_published_asset_subscriptions_count + ) + result["smus_domain_name"] = attrs.smus_domain_name + result["smus_domain_id"] = attrs.smus_domain_id + result["smus_domain_unit_name"] = attrs.smus_domain_unit_name + result["smus_domain_unit_id"] = attrs.smus_domain_unit_id + result["smus_project_id"] = attrs.smus_project_id + result["smus_owning_project_id"] = attrs.smus_owning_project_id + result["smus_asset_summary"] = attrs.smus_asset_summary + result["smus_asset_technical_name"] = attrs.smus_asset_technical_name + result["smus_asset_type"] = attrs.smus_asset_type + result["smus_asset_revision"] = attrs.smus_asset_revision + result["smus_asset_source_identifier"] = attrs.smus_asset_source_identifier + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _sage_maker_unified_studio_published_asset_to_nested( + sage_maker_unified_studio_published_asset: SageMakerUnifiedStudioPublishedAsset, +) -> SageMakerUnifiedStudioPublishedAssetNested: + """Convert flat SageMakerUnifiedStudioPublishedAsset to nested format.""" + attrs = SageMakerUnifiedStudioPublishedAssetAttributes() + _populate_sage_maker_unified_studio_published_asset_attrs( + attrs, sage_maker_unified_studio_published_asset + ) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + sage_maker_unified_studio_published_asset, + _SAGE_MAKER_UNIFIED_STUDIO_PUBLISHED_ASSET_REL_FIELDS, + SageMakerUnifiedStudioPublishedAssetRelationshipAttributes, + ) + return SageMakerUnifiedStudioPublishedAssetNested( + guid=sage_maker_unified_studio_published_asset.guid, + type_name=sage_maker_unified_studio_published_asset.type_name, + status=sage_maker_unified_studio_published_asset.status, + version=sage_maker_unified_studio_published_asset.version, + create_time=sage_maker_unified_studio_published_asset.create_time, + update_time=sage_maker_unified_studio_published_asset.update_time, + created_by=sage_maker_unified_studio_published_asset.created_by, + updated_by=sage_maker_unified_studio_published_asset.updated_by, + classifications=sage_maker_unified_studio_published_asset.classifications, + classification_names=sage_maker_unified_studio_published_asset.classification_names, + meanings=sage_maker_unified_studio_published_asset.meanings, + labels=sage_maker_unified_studio_published_asset.labels, + business_attributes=sage_maker_unified_studio_published_asset.business_attributes, + custom_attributes=sage_maker_unified_studio_published_asset.custom_attributes, + pending_tasks=sage_maker_unified_studio_published_asset.pending_tasks, + proxy=sage_maker_unified_studio_published_asset.proxy, + is_incomplete=sage_maker_unified_studio_published_asset.is_incomplete, + provenance_type=sage_maker_unified_studio_published_asset.provenance_type, + home_id=sage_maker_unified_studio_published_asset.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _sage_maker_unified_studio_published_asset_from_nested( + nested: SageMakerUnifiedStudioPublishedAssetNested, +) -> SageMakerUnifiedStudioPublishedAsset: + """Convert nested format to flat SageMakerUnifiedStudioPublishedAsset.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else SageMakerUnifiedStudioPublishedAssetAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SAGE_MAKER_UNIFIED_STUDIO_PUBLISHED_ASSET_REL_FIELDS, + SageMakerUnifiedStudioPublishedAssetRelationshipAttributes, + ) + return SageMakerUnifiedStudioPublishedAsset( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_sage_maker_unified_studio_published_asset_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _sage_maker_unified_studio_published_asset_to_nested_bytes( + sage_maker_unified_studio_published_asset: SageMakerUnifiedStudioPublishedAsset, + serde: Serde, +) -> bytes: + """Convert flat SageMakerUnifiedStudioPublishedAsset to nested JSON bytes.""" + return serde.encode( + _sage_maker_unified_studio_published_asset_to_nested( + sage_maker_unified_studio_published_asset + ) + ) + + +def _sage_maker_unified_studio_published_asset_from_nested_bytes( + data: bytes, serde: Serde +) -> SageMakerUnifiedStudioPublishedAsset: + """Convert nested JSON bytes to flat SageMakerUnifiedStudioPublishedAsset.""" + nested = serde.decode(data, SageMakerUnifiedStudioPublishedAssetNested) + return _sage_maker_unified_studio_published_asset_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +SageMakerUnifiedStudioPublishedAsset.SMUS_PUBLISHED_ASSET_SUBSCRIPTIONS_COUNT = ( + NumericField( + "smusPublishedAssetSubscriptionsCount", "smusPublishedAssetSubscriptionsCount" + ) +) +SageMakerUnifiedStudioPublishedAsset.SMUS_DOMAIN_NAME = KeywordField( + "smusDomainName", "smusDomainName" +) +SageMakerUnifiedStudioPublishedAsset.SMUS_DOMAIN_ID = KeywordField( + "smusDomainId", "smusDomainId" +) +SageMakerUnifiedStudioPublishedAsset.SMUS_DOMAIN_UNIT_NAME = KeywordField( + "smusDomainUnitName", "smusDomainUnitName" +) +SageMakerUnifiedStudioPublishedAsset.SMUS_DOMAIN_UNIT_ID = KeywordField( + "smusDomainUnitId", "smusDomainUnitId" +) +SageMakerUnifiedStudioPublishedAsset.SMUS_PROJECT_ID = KeywordField( + "smusProjectId", "smusProjectId" +) +SageMakerUnifiedStudioPublishedAsset.SMUS_OWNING_PROJECT_ID = KeywordField( + "smusOwningProjectId", "smusOwningProjectId" +) +SageMakerUnifiedStudioPublishedAsset.SMUS_ASSET_SUMMARY = KeywordField( + "smusAssetSummary", "smusAssetSummary" +) +SageMakerUnifiedStudioPublishedAsset.SMUS_ASSET_TECHNICAL_NAME = KeywordField( + "smusAssetTechnicalName", "smusAssetTechnicalName" +) +SageMakerUnifiedStudioPublishedAsset.SMUS_ASSET_TYPE = KeywordField( + "smusAssetType", "smusAssetType" +) +SageMakerUnifiedStudioPublishedAsset.SMUS_ASSET_REVISION = KeywordField( + "smusAssetRevision", "smusAssetRevision" +) +SageMakerUnifiedStudioPublishedAsset.SMUS_ASSET_SOURCE_IDENTIFIER = KeywordField( + "smusAssetSourceIdentifier", "smusAssetSourceIdentifier" +) +SageMakerUnifiedStudioPublishedAsset.INPUT_TO_AIRFLOW_TASKS = RelationField( + "inputToAirflowTasks" +) +SageMakerUnifiedStudioPublishedAsset.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +SageMakerUnifiedStudioPublishedAsset.ANOMALO_CHECKS = RelationField("anomaloChecks") +SageMakerUnifiedStudioPublishedAsset.APPLICATION = RelationField("application") +SageMakerUnifiedStudioPublishedAsset.APPLICATION_FIELD = RelationField( + "applicationField" +) +SageMakerUnifiedStudioPublishedAsset.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +SageMakerUnifiedStudioPublishedAsset.INPUT_PORT_DATA_PRODUCTS = RelationField( + "inputPortDataProducts" +) +SageMakerUnifiedStudioPublishedAsset.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +SageMakerUnifiedStudioPublishedAsset.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +SageMakerUnifiedStudioPublishedAsset.METRICS = RelationField("metrics") +SageMakerUnifiedStudioPublishedAsset.DQ_BASE_DATASET_RULES = RelationField( + "dqBaseDatasetRules" +) +SageMakerUnifiedStudioPublishedAsset.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +SageMakerUnifiedStudioPublishedAsset.MEANINGS = RelationField("meanings") +SageMakerUnifiedStudioPublishedAsset.MC_MONITORS = RelationField("mcMonitors") +SageMakerUnifiedStudioPublishedAsset.MC_INCIDENTS = RelationField("mcIncidents") +SageMakerUnifiedStudioPublishedAsset.PARTIAL_CHILD_FIELDS = RelationField( + "partialChildFields" +) +SageMakerUnifiedStudioPublishedAsset.PARTIAL_CHILD_OBJECTS = RelationField( + "partialChildObjects" +) +SageMakerUnifiedStudioPublishedAsset.INPUT_TO_PROCESSES = RelationField( + "inputToProcesses" +) +SageMakerUnifiedStudioPublishedAsset.OUTPUT_FROM_PROCESSES = RelationField( + "outputFromProcesses" +) +SageMakerUnifiedStudioPublishedAsset.USER_DEF_RELATIONSHIP_TO = RelationField( + "userDefRelationshipTo" +) +SageMakerUnifiedStudioPublishedAsset.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +SageMakerUnifiedStudioPublishedAsset.FILES = RelationField("files") +SageMakerUnifiedStudioPublishedAsset.LINKS = RelationField("links") +SageMakerUnifiedStudioPublishedAsset.README = RelationField("readme") +SageMakerUnifiedStudioPublishedAsset.SMUS_PROJECT = RelationField("smusProject") +SageMakerUnifiedStudioPublishedAsset.SMUS_ASSET_SCHEMAS = RelationField( + "smusAssetSchemas" +) +SageMakerUnifiedStudioPublishedAsset.SMUS_SUBSCRIBED_ASSETS = RelationField( + "smusSubscribedAssets" +) +SageMakerUnifiedStudioPublishedAsset.SCHEMA_REGISTRY_SUBJECTS = RelationField( + "schemaRegistrySubjects" +) +SageMakerUnifiedStudioPublishedAsset.SODA_CHECKS = RelationField("sodaChecks") +SageMakerUnifiedStudioPublishedAsset.INPUT_TO_SPARK_JOBS = RelationField( + "inputToSparkJobs" +) +SageMakerUnifiedStudioPublishedAsset.OUTPUT_FROM_SPARK_JOBS = RelationField( + "outputFromSparkJobs" +) diff --git a/pyatlan_v9/model/assets/sage_maker_unified_studio_related.py b/pyatlan_v9/model/assets/sage_maker_unified_studio_related.py new file mode 100644 index 000000000..900b24be3 --- /dev/null +++ b/pyatlan_v9/model/assets/sage_maker_unified_studio_related.py @@ -0,0 +1,199 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for SageMakerUnifiedStudio module. + +This module contains all Related{Type} classes for the SageMakerUnifiedStudio type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Union + +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedSaaS +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedSageMakerUnifiedStudio", + "RelatedSageMakerUnifiedStudioProject", + "RelatedSageMakerUnifiedStudioAsset", + "RelatedSageMakerUnifiedStudioPublishedAsset", + "RelatedSageMakerUnifiedStudioSubscribedAsset", + "RelatedSageMakerUnifiedStudioAssetSchema", +] + + +class RelatedSageMakerUnifiedStudio(RelatedSaaS): + """ + Related entity reference for SageMakerUnifiedStudio assets. + + Extends RelatedSaaS with SageMakerUnifiedStudio-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SageMakerUnifiedStudio" so it serializes correctly + + smus_domain_name: Union[str, None, UnsetType] = UNSET + """Name of the SageMaker Unified Studio domain.""" + + smus_domain_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio domain.""" + + smus_domain_unit_name: Union[str, None, UnsetType] = UNSET + """Name of the SageMaker Unified Studio domain unit.""" + + smus_domain_unit_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio domain unit.""" + + smus_project_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio project.""" + + smus_owning_project_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio project which owns the asset.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SageMakerUnifiedStudio" + + +class RelatedSageMakerUnifiedStudioProject(RelatedSageMakerUnifiedStudio): + """ + Related entity reference for SageMakerUnifiedStudioProject assets. + + Extends RelatedSageMakerUnifiedStudio with SageMakerUnifiedStudioProject-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SageMakerUnifiedStudioProject" so it serializes correctly + + smus_project_status: Union[str, None, UnsetType] = UNSET + """Status of the SageMaker Unified Studio project.""" + + smus_project_profile_name: Union[str, None, UnsetType] = UNSET + """Name of the profile of the SageMaker Unified Studio project.""" + + smus_project_role_arn: Union[str, None, UnsetType] = UNSET + """Amazon IAM role ARN of the SageMaker Unified Studio project.""" + + smus_project_s3_location: Union[str, None, UnsetType] = UNSET + """Amazon S3 location of the SageMaker Unified Studio project.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SageMakerUnifiedStudioProject" + + +class RelatedSageMakerUnifiedStudioAsset(RelatedSageMakerUnifiedStudio): + """ + Related entity reference for SageMakerUnifiedStudioAsset assets. + + Extends RelatedSageMakerUnifiedStudio with SageMakerUnifiedStudioAsset-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SageMakerUnifiedStudioAsset" so it serializes correctly + + smus_asset_summary: Union[str, None, UnsetType] = UNSET + """Summary text for the asset in SageMaker Unified Studio.""" + + smus_asset_technical_name: Union[str, None, UnsetType] = UNSET + """Technical name for the asset in SageMaker Unified Studio.""" + + smus_asset_type: Union[str, None, UnsetType] = UNSET + """Type of asset in SageMaker Unified Studio.""" + + smus_asset_revision: Union[str, None, UnsetType] = UNSET + """Latest published version of the asset in SageMaker Unified Studio.""" + + smus_asset_source_identifier: Union[str, None, UnsetType] = UNSET + """Unique source identifier for the asset in SageMaker Unified Studio.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SageMakerUnifiedStudioAsset" + + +class RelatedSageMakerUnifiedStudioPublishedAsset(RelatedSageMakerUnifiedStudio): + """ + Related entity reference for SageMakerUnifiedStudioPublishedAsset assets. + + Extends RelatedSageMakerUnifiedStudio with SageMakerUnifiedStudioPublishedAsset-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SageMakerUnifiedStudioPublishedAsset" so it serializes correctly + + smus_published_asset_subscriptions_count: Union[int, None, UnsetType] = UNSET + """Number of subscriptions for the published asset.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SageMakerUnifiedStudioPublishedAsset" + + +class RelatedSageMakerUnifiedStudioSubscribedAsset(RelatedSageMakerUnifiedStudio): + """ + Related entity reference for SageMakerUnifiedStudioSubscribedAsset assets. + + Extends RelatedSageMakerUnifiedStudio with SageMakerUnifiedStudioSubscribedAsset-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SageMakerUnifiedStudioSubscribedAsset" so it serializes correctly + + smus_subscribed_asset_project_name: Union[str, None, UnsetType] = UNSET + """Name of the SageMaker Unified Studio project from which this asset is subscribed.""" + + smus_subscribed_asset_requestor_name: Union[str, None, UnsetType] = UNSET + """Name of the user who requested access to this subscribed asset.""" + + smus_subscribed_asset_request_reason: Union[str, None, UnsetType] = UNSET + """Reason provided by the requestor for this subscribed asset.""" + + smus_subscribed_asset_request_date: Union[int, None, UnsetType] = UNSET + """Date when the subscription request was submitted.""" + + smus_subscribed_asset_approver_name: Union[str, None, UnsetType] = UNSET + """Name of the user who approved the subscription request.""" + + smus_subscribed_asset_approved_reason: Union[str, None, UnsetType] = UNSET + """Reason provided by the approver for approving the subscription.""" + + smus_subscribed_asset_approval_date: Union[int, None, UnsetType] = UNSET + """Date when the subscription request was approved.""" + + smus_subscribed_asset_column_access_info: Union[str, None, UnsetType] = UNSET + """Number of Columns provided access grant for this subscribed asset (for example: 3 out of 23).""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SageMakerUnifiedStudioSubscribedAsset" + + +class RelatedSageMakerUnifiedStudioAssetSchema(RelatedSageMakerUnifiedStudio): + """ + Related entity reference for SageMakerUnifiedStudioAssetSchema assets. + + Extends RelatedSageMakerUnifiedStudio with SageMakerUnifiedStudioAssetSchema-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SageMakerUnifiedStudioAssetSchema" so it serializes correctly + + smus_data_type: Union[str, None, UnsetType] = UNSET + """Data type of the schema/column.""" + + smus_asset_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the Atlan SageMaker Unified Studio published/subscribed asset that contains this schema.""" + + smus_asset_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Atlan SageMaker Unified Studio published/subscribed asset that contains this schema.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SageMakerUnifiedStudioAssetSchema" diff --git a/pyatlan_v9/model/assets/sage_maker_unified_studio_subscribed_asset.py b/pyatlan_v9/model/assets/sage_maker_unified_studio_subscribed_asset.py new file mode 100644 index 000000000..0bf436fef --- /dev/null +++ b/pyatlan_v9/model/assets/sage_maker_unified_studio_subscribed_asset.py @@ -0,0 +1,906 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SageMakerUnifiedStudioSubscribedAsset asset model with flattened inheritance. + +This module provides: +- SageMakerUnifiedStudioSubscribedAsset: Flat asset class (easy to use) +- SageMakerUnifiedStudioSubscribedAssetAttributes: Nested attributes struct (extends AssetAttributes) +- SageMakerUnifiedStudioSubscribedAssetNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .sage_maker_unified_studio_related import ( + RelatedSageMakerUnifiedStudioAssetSchema, + RelatedSageMakerUnifiedStudioProject, + RelatedSageMakerUnifiedStudioPublishedAsset, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SageMakerUnifiedStudioSubscribedAsset(Asset): + """ + Instance of SageMaker Unified Studio subscribed asset in Atlan. A subscribed asset is analogous to a view, with its own lifecycle independent from the published asset itself. + """ + + SMUS_SUBSCRIBED_ASSET_PROJECT_NAME: ClassVar[Any] = None + SMUS_SUBSCRIBED_ASSET_REQUESTOR_NAME: ClassVar[Any] = None + SMUS_SUBSCRIBED_ASSET_REQUEST_REASON: ClassVar[Any] = None + SMUS_SUBSCRIBED_ASSET_REQUEST_DATE: ClassVar[Any] = None + SMUS_SUBSCRIBED_ASSET_APPROVER_NAME: ClassVar[Any] = None + SMUS_SUBSCRIBED_ASSET_APPROVED_REASON: ClassVar[Any] = None + SMUS_SUBSCRIBED_ASSET_APPROVAL_DATE: ClassVar[Any] = None + SMUS_SUBSCRIBED_ASSET_COLUMN_ACCESS_INFO: ClassVar[Any] = None + SMUS_DOMAIN_NAME: ClassVar[Any] = None + SMUS_DOMAIN_ID: ClassVar[Any] = None + SMUS_DOMAIN_UNIT_NAME: ClassVar[Any] = None + SMUS_DOMAIN_UNIT_ID: ClassVar[Any] = None + SMUS_PROJECT_ID: ClassVar[Any] = None + SMUS_OWNING_PROJECT_ID: ClassVar[Any] = None + SMUS_ASSET_SUMMARY: ClassVar[Any] = None + SMUS_ASSET_TECHNICAL_NAME: ClassVar[Any] = None + SMUS_ASSET_TYPE: ClassVar[Any] = None + SMUS_ASSET_REVISION: ClassVar[Any] = None + SMUS_ASSET_SOURCE_IDENTIFIER: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SMUS_PROJECT: ClassVar[Any] = None + SMUS_ASSET_SCHEMAS: ClassVar[Any] = None + SMUS_PUBLISHED_ASSET: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SageMakerUnifiedStudioSubscribedAsset" + + smus_subscribed_asset_project_name: Union[str, None, UnsetType] = UNSET + """Name of the SageMaker Unified Studio project from which this asset is subscribed.""" + + smus_subscribed_asset_requestor_name: Union[str, None, UnsetType] = UNSET + """Name of the user who requested access to this subscribed asset.""" + + smus_subscribed_asset_request_reason: Union[str, None, UnsetType] = UNSET + """Reason provided by the requestor for this subscribed asset.""" + + smus_subscribed_asset_request_date: Union[int, None, UnsetType] = UNSET + """Date when the subscription request was submitted.""" + + smus_subscribed_asset_approver_name: Union[str, None, UnsetType] = UNSET + """Name of the user who approved the subscription request.""" + + smus_subscribed_asset_approved_reason: Union[str, None, UnsetType] = UNSET + """Reason provided by the approver for approving the subscription.""" + + smus_subscribed_asset_approval_date: Union[int, None, UnsetType] = UNSET + """Date when the subscription request was approved.""" + + smus_subscribed_asset_column_access_info: Union[str, None, UnsetType] = UNSET + """Number of Columns provided access grant for this subscribed asset (for example: 3 out of 23).""" + + smus_domain_name: Union[str, None, UnsetType] = UNSET + """Name of the SageMaker Unified Studio domain.""" + + smus_domain_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio domain.""" + + smus_domain_unit_name: Union[str, None, UnsetType] = UNSET + """Name of the SageMaker Unified Studio domain unit.""" + + smus_domain_unit_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio domain unit.""" + + smus_project_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio project.""" + + smus_owning_project_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio project which owns the asset.""" + + smus_asset_summary: Union[str, None, UnsetType] = UNSET + """Summary text for the asset in SageMaker Unified Studio.""" + + smus_asset_technical_name: Union[str, None, UnsetType] = UNSET + """Technical name for the asset in SageMaker Unified Studio.""" + + smus_asset_type: Union[str, None, UnsetType] = UNSET + """Type of asset in SageMaker Unified Studio.""" + + smus_asset_revision: Union[str, None, UnsetType] = UNSET + """Latest published version of the asset in SageMaker Unified Studio.""" + + smus_asset_source_identifier: Union[str, None, UnsetType] = UNSET + """Unique source identifier for the asset in SageMaker Unified Studio.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + smus_project: Union[RelatedSageMakerUnifiedStudioProject, None, UnsetType] = UNSET + """Project containing the subscribed asset.""" + + smus_asset_schemas: Union[ + List[RelatedSageMakerUnifiedStudioAssetSchema], None, UnsetType + ] = UNSET + """Schemas that exist within this published asset.""" + + smus_published_asset: Union[ + RelatedSageMakerUnifiedStudioPublishedAsset, None, UnsetType + ] = UNSET + """Published asset associated with this subscribed asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SageMakerUnifiedStudioSubscribedAsset" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _sage_maker_unified_studio_subscribed_asset_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> SageMakerUnifiedStudioSubscribedAsset: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SageMakerUnifiedStudioSubscribedAsset instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _sage_maker_unified_studio_subscribed_asset_from_nested_bytes( + json_data, serde + ) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SageMakerUnifiedStudioSubscribedAssetAttributes(AssetAttributes): + """SageMakerUnifiedStudioSubscribedAsset-specific attributes for nested API format.""" + + smus_subscribed_asset_project_name: Union[str, None, UnsetType] = UNSET + """Name of the SageMaker Unified Studio project from which this asset is subscribed.""" + + smus_subscribed_asset_requestor_name: Union[str, None, UnsetType] = UNSET + """Name of the user who requested access to this subscribed asset.""" + + smus_subscribed_asset_request_reason: Union[str, None, UnsetType] = UNSET + """Reason provided by the requestor for this subscribed asset.""" + + smus_subscribed_asset_request_date: Union[int, None, UnsetType] = UNSET + """Date when the subscription request was submitted.""" + + smus_subscribed_asset_approver_name: Union[str, None, UnsetType] = UNSET + """Name of the user who approved the subscription request.""" + + smus_subscribed_asset_approved_reason: Union[str, None, UnsetType] = UNSET + """Reason provided by the approver for approving the subscription.""" + + smus_subscribed_asset_approval_date: Union[int, None, UnsetType] = UNSET + """Date when the subscription request was approved.""" + + smus_subscribed_asset_column_access_info: Union[str, None, UnsetType] = UNSET + """Number of Columns provided access grant for this subscribed asset (for example: 3 out of 23).""" + + smus_domain_name: Union[str, None, UnsetType] = UNSET + """Name of the SageMaker Unified Studio domain.""" + + smus_domain_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio domain.""" + + smus_domain_unit_name: Union[str, None, UnsetType] = UNSET + """Name of the SageMaker Unified Studio domain unit.""" + + smus_domain_unit_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio domain unit.""" + + smus_project_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio project.""" + + smus_owning_project_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the SageMaker Unified Studio project which owns the asset.""" + + smus_asset_summary: Union[str, None, UnsetType] = UNSET + """Summary text for the asset in SageMaker Unified Studio.""" + + smus_asset_technical_name: Union[str, None, UnsetType] = UNSET + """Technical name for the asset in SageMaker Unified Studio.""" + + smus_asset_type: Union[str, None, UnsetType] = UNSET + """Type of asset in SageMaker Unified Studio.""" + + smus_asset_revision: Union[str, None, UnsetType] = UNSET + """Latest published version of the asset in SageMaker Unified Studio.""" + + smus_asset_source_identifier: Union[str, None, UnsetType] = UNSET + """Unique source identifier for the asset in SageMaker Unified Studio.""" + + +class SageMakerUnifiedStudioSubscribedAssetRelationshipAttributes( + AssetRelationshipAttributes +): + """SageMakerUnifiedStudioSubscribedAsset-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + smus_project: Union[RelatedSageMakerUnifiedStudioProject, None, UnsetType] = UNSET + """Project containing the subscribed asset.""" + + smus_asset_schemas: Union[ + List[RelatedSageMakerUnifiedStudioAssetSchema], None, UnsetType + ] = UNSET + """Schemas that exist within this published asset.""" + + smus_published_asset: Union[ + RelatedSageMakerUnifiedStudioPublishedAsset, None, UnsetType + ] = UNSET + """Published asset associated with this subscribed asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SageMakerUnifiedStudioSubscribedAssetNested(AssetNested): + """SageMakerUnifiedStudioSubscribedAsset in nested API format for high-performance serialization.""" + + attributes: Union[SageMakerUnifiedStudioSubscribedAssetAttributes, UnsetType] = ( + UNSET + ) + relationship_attributes: Union[ + SageMakerUnifiedStudioSubscribedAssetRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + SageMakerUnifiedStudioSubscribedAssetRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SageMakerUnifiedStudioSubscribedAssetRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SAGE_MAKER_UNIFIED_STUDIO_SUBSCRIBED_ASSET_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "smus_project", + "smus_asset_schemas", + "smus_published_asset", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_sage_maker_unified_studio_subscribed_asset_attrs( + attrs: SageMakerUnifiedStudioSubscribedAssetAttributes, + obj: SageMakerUnifiedStudioSubscribedAsset, +) -> None: + """Populate SageMakerUnifiedStudioSubscribedAsset-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.smus_subscribed_asset_project_name = obj.smus_subscribed_asset_project_name + attrs.smus_subscribed_asset_requestor_name = ( + obj.smus_subscribed_asset_requestor_name + ) + attrs.smus_subscribed_asset_request_reason = ( + obj.smus_subscribed_asset_request_reason + ) + attrs.smus_subscribed_asset_request_date = obj.smus_subscribed_asset_request_date + attrs.smus_subscribed_asset_approver_name = obj.smus_subscribed_asset_approver_name + attrs.smus_subscribed_asset_approved_reason = ( + obj.smus_subscribed_asset_approved_reason + ) + attrs.smus_subscribed_asset_approval_date = obj.smus_subscribed_asset_approval_date + attrs.smus_subscribed_asset_column_access_info = ( + obj.smus_subscribed_asset_column_access_info + ) + attrs.smus_domain_name = obj.smus_domain_name + attrs.smus_domain_id = obj.smus_domain_id + attrs.smus_domain_unit_name = obj.smus_domain_unit_name + attrs.smus_domain_unit_id = obj.smus_domain_unit_id + attrs.smus_project_id = obj.smus_project_id + attrs.smus_owning_project_id = obj.smus_owning_project_id + attrs.smus_asset_summary = obj.smus_asset_summary + attrs.smus_asset_technical_name = obj.smus_asset_technical_name + attrs.smus_asset_type = obj.smus_asset_type + attrs.smus_asset_revision = obj.smus_asset_revision + attrs.smus_asset_source_identifier = obj.smus_asset_source_identifier + + +def _extract_sage_maker_unified_studio_subscribed_asset_attrs( + attrs: SageMakerUnifiedStudioSubscribedAssetAttributes, +) -> dict: + """Extract all SageMakerUnifiedStudioSubscribedAsset attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["smus_subscribed_asset_project_name"] = ( + attrs.smus_subscribed_asset_project_name + ) + result["smus_subscribed_asset_requestor_name"] = ( + attrs.smus_subscribed_asset_requestor_name + ) + result["smus_subscribed_asset_request_reason"] = ( + attrs.smus_subscribed_asset_request_reason + ) + result["smus_subscribed_asset_request_date"] = ( + attrs.smus_subscribed_asset_request_date + ) + result["smus_subscribed_asset_approver_name"] = ( + attrs.smus_subscribed_asset_approver_name + ) + result["smus_subscribed_asset_approved_reason"] = ( + attrs.smus_subscribed_asset_approved_reason + ) + result["smus_subscribed_asset_approval_date"] = ( + attrs.smus_subscribed_asset_approval_date + ) + result["smus_subscribed_asset_column_access_info"] = ( + attrs.smus_subscribed_asset_column_access_info + ) + result["smus_domain_name"] = attrs.smus_domain_name + result["smus_domain_id"] = attrs.smus_domain_id + result["smus_domain_unit_name"] = attrs.smus_domain_unit_name + result["smus_domain_unit_id"] = attrs.smus_domain_unit_id + result["smus_project_id"] = attrs.smus_project_id + result["smus_owning_project_id"] = attrs.smus_owning_project_id + result["smus_asset_summary"] = attrs.smus_asset_summary + result["smus_asset_technical_name"] = attrs.smus_asset_technical_name + result["smus_asset_type"] = attrs.smus_asset_type + result["smus_asset_revision"] = attrs.smus_asset_revision + result["smus_asset_source_identifier"] = attrs.smus_asset_source_identifier + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _sage_maker_unified_studio_subscribed_asset_to_nested( + sage_maker_unified_studio_subscribed_asset: SageMakerUnifiedStudioSubscribedAsset, +) -> SageMakerUnifiedStudioSubscribedAssetNested: + """Convert flat SageMakerUnifiedStudioSubscribedAsset to nested format.""" + attrs = SageMakerUnifiedStudioSubscribedAssetAttributes() + _populate_sage_maker_unified_studio_subscribed_asset_attrs( + attrs, sage_maker_unified_studio_subscribed_asset + ) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + sage_maker_unified_studio_subscribed_asset, + _SAGE_MAKER_UNIFIED_STUDIO_SUBSCRIBED_ASSET_REL_FIELDS, + SageMakerUnifiedStudioSubscribedAssetRelationshipAttributes, + ) + return SageMakerUnifiedStudioSubscribedAssetNested( + guid=sage_maker_unified_studio_subscribed_asset.guid, + type_name=sage_maker_unified_studio_subscribed_asset.type_name, + status=sage_maker_unified_studio_subscribed_asset.status, + version=sage_maker_unified_studio_subscribed_asset.version, + create_time=sage_maker_unified_studio_subscribed_asset.create_time, + update_time=sage_maker_unified_studio_subscribed_asset.update_time, + created_by=sage_maker_unified_studio_subscribed_asset.created_by, + updated_by=sage_maker_unified_studio_subscribed_asset.updated_by, + classifications=sage_maker_unified_studio_subscribed_asset.classifications, + classification_names=sage_maker_unified_studio_subscribed_asset.classification_names, + meanings=sage_maker_unified_studio_subscribed_asset.meanings, + labels=sage_maker_unified_studio_subscribed_asset.labels, + business_attributes=sage_maker_unified_studio_subscribed_asset.business_attributes, + custom_attributes=sage_maker_unified_studio_subscribed_asset.custom_attributes, + pending_tasks=sage_maker_unified_studio_subscribed_asset.pending_tasks, + proxy=sage_maker_unified_studio_subscribed_asset.proxy, + is_incomplete=sage_maker_unified_studio_subscribed_asset.is_incomplete, + provenance_type=sage_maker_unified_studio_subscribed_asset.provenance_type, + home_id=sage_maker_unified_studio_subscribed_asset.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _sage_maker_unified_studio_subscribed_asset_from_nested( + nested: SageMakerUnifiedStudioSubscribedAssetNested, +) -> SageMakerUnifiedStudioSubscribedAsset: + """Convert nested format to flat SageMakerUnifiedStudioSubscribedAsset.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else SageMakerUnifiedStudioSubscribedAssetAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SAGE_MAKER_UNIFIED_STUDIO_SUBSCRIBED_ASSET_REL_FIELDS, + SageMakerUnifiedStudioSubscribedAssetRelationshipAttributes, + ) + return SageMakerUnifiedStudioSubscribedAsset( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_sage_maker_unified_studio_subscribed_asset_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _sage_maker_unified_studio_subscribed_asset_to_nested_bytes( + sage_maker_unified_studio_subscribed_asset: SageMakerUnifiedStudioSubscribedAsset, + serde: Serde, +) -> bytes: + """Convert flat SageMakerUnifiedStudioSubscribedAsset to nested JSON bytes.""" + return serde.encode( + _sage_maker_unified_studio_subscribed_asset_to_nested( + sage_maker_unified_studio_subscribed_asset + ) + ) + + +def _sage_maker_unified_studio_subscribed_asset_from_nested_bytes( + data: bytes, serde: Serde +) -> SageMakerUnifiedStudioSubscribedAsset: + """Convert nested JSON bytes to flat SageMakerUnifiedStudioSubscribedAsset.""" + nested = serde.decode(data, SageMakerUnifiedStudioSubscribedAssetNested) + return _sage_maker_unified_studio_subscribed_asset_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +SageMakerUnifiedStudioSubscribedAsset.SMUS_SUBSCRIBED_ASSET_PROJECT_NAME = KeywordField( + "smusSubscribedAssetProjectName", "smusSubscribedAssetProjectName" +) +SageMakerUnifiedStudioSubscribedAsset.SMUS_SUBSCRIBED_ASSET_REQUESTOR_NAME = ( + KeywordField("smusSubscribedAssetRequestorName", "smusSubscribedAssetRequestorName") +) +SageMakerUnifiedStudioSubscribedAsset.SMUS_SUBSCRIBED_ASSET_REQUEST_REASON = ( + KeywordField("smusSubscribedAssetRequestReason", "smusSubscribedAssetRequestReason") +) +SageMakerUnifiedStudioSubscribedAsset.SMUS_SUBSCRIBED_ASSET_REQUEST_DATE = NumericField( + "smusSubscribedAssetRequestDate", "smusSubscribedAssetRequestDate" +) +SageMakerUnifiedStudioSubscribedAsset.SMUS_SUBSCRIBED_ASSET_APPROVER_NAME = ( + KeywordField("smusSubscribedAssetApproverName", "smusSubscribedAssetApproverName") +) +SageMakerUnifiedStudioSubscribedAsset.SMUS_SUBSCRIBED_ASSET_APPROVED_REASON = ( + KeywordField( + "smusSubscribedAssetApprovedReason", "smusSubscribedAssetApprovedReason" + ) +) +SageMakerUnifiedStudioSubscribedAsset.SMUS_SUBSCRIBED_ASSET_APPROVAL_DATE = ( + NumericField("smusSubscribedAssetApprovalDate", "smusSubscribedAssetApprovalDate") +) +SageMakerUnifiedStudioSubscribedAsset.SMUS_SUBSCRIBED_ASSET_COLUMN_ACCESS_INFO = ( + KeywordField( + "smusSubscribedAssetColumnAccessInfo", "smusSubscribedAssetColumnAccessInfo" + ) +) +SageMakerUnifiedStudioSubscribedAsset.SMUS_DOMAIN_NAME = KeywordField( + "smusDomainName", "smusDomainName" +) +SageMakerUnifiedStudioSubscribedAsset.SMUS_DOMAIN_ID = KeywordField( + "smusDomainId", "smusDomainId" +) +SageMakerUnifiedStudioSubscribedAsset.SMUS_DOMAIN_UNIT_NAME = KeywordField( + "smusDomainUnitName", "smusDomainUnitName" +) +SageMakerUnifiedStudioSubscribedAsset.SMUS_DOMAIN_UNIT_ID = KeywordField( + "smusDomainUnitId", "smusDomainUnitId" +) +SageMakerUnifiedStudioSubscribedAsset.SMUS_PROJECT_ID = KeywordField( + "smusProjectId", "smusProjectId" +) +SageMakerUnifiedStudioSubscribedAsset.SMUS_OWNING_PROJECT_ID = KeywordField( + "smusOwningProjectId", "smusOwningProjectId" +) +SageMakerUnifiedStudioSubscribedAsset.SMUS_ASSET_SUMMARY = KeywordField( + "smusAssetSummary", "smusAssetSummary" +) +SageMakerUnifiedStudioSubscribedAsset.SMUS_ASSET_TECHNICAL_NAME = KeywordField( + "smusAssetTechnicalName", "smusAssetTechnicalName" +) +SageMakerUnifiedStudioSubscribedAsset.SMUS_ASSET_TYPE = KeywordField( + "smusAssetType", "smusAssetType" +) +SageMakerUnifiedStudioSubscribedAsset.SMUS_ASSET_REVISION = KeywordField( + "smusAssetRevision", "smusAssetRevision" +) +SageMakerUnifiedStudioSubscribedAsset.SMUS_ASSET_SOURCE_IDENTIFIER = KeywordField( + "smusAssetSourceIdentifier", "smusAssetSourceIdentifier" +) +SageMakerUnifiedStudioSubscribedAsset.INPUT_TO_AIRFLOW_TASKS = RelationField( + "inputToAirflowTasks" +) +SageMakerUnifiedStudioSubscribedAsset.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +SageMakerUnifiedStudioSubscribedAsset.ANOMALO_CHECKS = RelationField("anomaloChecks") +SageMakerUnifiedStudioSubscribedAsset.APPLICATION = RelationField("application") +SageMakerUnifiedStudioSubscribedAsset.APPLICATION_FIELD = RelationField( + "applicationField" +) +SageMakerUnifiedStudioSubscribedAsset.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +SageMakerUnifiedStudioSubscribedAsset.INPUT_PORT_DATA_PRODUCTS = RelationField( + "inputPortDataProducts" +) +SageMakerUnifiedStudioSubscribedAsset.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +SageMakerUnifiedStudioSubscribedAsset.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +SageMakerUnifiedStudioSubscribedAsset.METRICS = RelationField("metrics") +SageMakerUnifiedStudioSubscribedAsset.DQ_BASE_DATASET_RULES = RelationField( + "dqBaseDatasetRules" +) +SageMakerUnifiedStudioSubscribedAsset.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +SageMakerUnifiedStudioSubscribedAsset.MEANINGS = RelationField("meanings") +SageMakerUnifiedStudioSubscribedAsset.MC_MONITORS = RelationField("mcMonitors") +SageMakerUnifiedStudioSubscribedAsset.MC_INCIDENTS = RelationField("mcIncidents") +SageMakerUnifiedStudioSubscribedAsset.PARTIAL_CHILD_FIELDS = RelationField( + "partialChildFields" +) +SageMakerUnifiedStudioSubscribedAsset.PARTIAL_CHILD_OBJECTS = RelationField( + "partialChildObjects" +) +SageMakerUnifiedStudioSubscribedAsset.INPUT_TO_PROCESSES = RelationField( + "inputToProcesses" +) +SageMakerUnifiedStudioSubscribedAsset.OUTPUT_FROM_PROCESSES = RelationField( + "outputFromProcesses" +) +SageMakerUnifiedStudioSubscribedAsset.USER_DEF_RELATIONSHIP_TO = RelationField( + "userDefRelationshipTo" +) +SageMakerUnifiedStudioSubscribedAsset.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +SageMakerUnifiedStudioSubscribedAsset.FILES = RelationField("files") +SageMakerUnifiedStudioSubscribedAsset.LINKS = RelationField("links") +SageMakerUnifiedStudioSubscribedAsset.README = RelationField("readme") +SageMakerUnifiedStudioSubscribedAsset.SMUS_PROJECT = RelationField("smusProject") +SageMakerUnifiedStudioSubscribedAsset.SMUS_ASSET_SCHEMAS = RelationField( + "smusAssetSchemas" +) +SageMakerUnifiedStudioSubscribedAsset.SMUS_PUBLISHED_ASSET = RelationField( + "smusPublishedAsset" +) +SageMakerUnifiedStudioSubscribedAsset.SCHEMA_REGISTRY_SUBJECTS = RelationField( + "schemaRegistrySubjects" +) +SageMakerUnifiedStudioSubscribedAsset.SODA_CHECKS = RelationField("sodaChecks") +SageMakerUnifiedStudioSubscribedAsset.INPUT_TO_SPARK_JOBS = RelationField( + "inputToSparkJobs" +) +SageMakerUnifiedStudioSubscribedAsset.OUTPUT_FROM_SPARK_JOBS = RelationField( + "outputFromSparkJobs" +) diff --git a/pyatlan_v9/model/assets/salesforce.py b/pyatlan_v9/model/assets/salesforce.py new file mode 100644 index 000000000..c81e0141d --- /dev/null +++ b/pyatlan_v9/model/assets/salesforce.py @@ -0,0 +1,549 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Salesforce asset model with flattened inheritance. + +This module provides: +- Salesforce: Flat asset class (easy to use) +- SalesforceAttributes: Nested attributes struct (extends AssetAttributes) +- SalesforceNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Salesforce(Asset): + """ + Base class for Salesforce assets. + """ + + ORGANIZATION_QUALIFIED_NAME: ClassVar[Any] = None + API_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Salesforce" + + organization_qualified_name: Union[str, None, UnsetType] = UNSET + """Fully-qualified name of the organization in Salesforce.""" + + api_name: Union[str, None, UnsetType] = UNSET + """Name of this asset in the Salesforce API.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Salesforce" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _salesforce_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Salesforce: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Salesforce instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _salesforce_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SalesforceAttributes(AssetAttributes): + """Salesforce-specific attributes for nested API format.""" + + organization_qualified_name: Union[str, None, UnsetType] = UNSET + """Fully-qualified name of the organization in Salesforce.""" + + api_name: Union[str, None, UnsetType] = UNSET + """Name of this asset in the Salesforce API.""" + + +class SalesforceRelationshipAttributes(AssetRelationshipAttributes): + """Salesforce-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SalesforceNested(AssetNested): + """Salesforce in nested API format for high-performance serialization.""" + + attributes: Union[SalesforceAttributes, UnsetType] = UNSET + relationship_attributes: Union[SalesforceRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + SalesforceRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SalesforceRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SALESFORCE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_salesforce_attrs(attrs: SalesforceAttributes, obj: Salesforce) -> None: + """Populate Salesforce-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.organization_qualified_name = obj.organization_qualified_name + attrs.api_name = obj.api_name + + +def _extract_salesforce_attrs(attrs: SalesforceAttributes) -> dict: + """Extract all Salesforce attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["organization_qualified_name"] = attrs.organization_qualified_name + result["api_name"] = attrs.api_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _salesforce_to_nested(salesforce: Salesforce) -> SalesforceNested: + """Convert flat Salesforce to nested format.""" + attrs = SalesforceAttributes() + _populate_salesforce_attrs(attrs, salesforce) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + salesforce, _SALESFORCE_REL_FIELDS, SalesforceRelationshipAttributes + ) + return SalesforceNested( + guid=salesforce.guid, + type_name=salesforce.type_name, + status=salesforce.status, + version=salesforce.version, + create_time=salesforce.create_time, + update_time=salesforce.update_time, + created_by=salesforce.created_by, + updated_by=salesforce.updated_by, + classifications=salesforce.classifications, + classification_names=salesforce.classification_names, + meanings=salesforce.meanings, + labels=salesforce.labels, + business_attributes=salesforce.business_attributes, + custom_attributes=salesforce.custom_attributes, + pending_tasks=salesforce.pending_tasks, + proxy=salesforce.proxy, + is_incomplete=salesforce.is_incomplete, + provenance_type=salesforce.provenance_type, + home_id=salesforce.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _salesforce_from_nested(nested: SalesforceNested) -> Salesforce: + """Convert nested format to flat Salesforce.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else SalesforceAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SALESFORCE_REL_FIELDS, + SalesforceRelationshipAttributes, + ) + return Salesforce( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_salesforce_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _salesforce_to_nested_bytes(salesforce: Salesforce, serde: Serde) -> bytes: + """Convert flat Salesforce to nested JSON bytes.""" + return serde.encode(_salesforce_to_nested(salesforce)) + + +def _salesforce_from_nested_bytes(data: bytes, serde: Serde) -> Salesforce: + """Convert nested JSON bytes to flat Salesforce.""" + nested = serde.decode(data, SalesforceNested) + return _salesforce_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +Salesforce.ORGANIZATION_QUALIFIED_NAME = KeywordField( + "organizationQualifiedName", "organizationQualifiedName" +) +Salesforce.API_NAME = KeywordField("apiName", "apiName") +Salesforce.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Salesforce.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Salesforce.ANOMALO_CHECKS = RelationField("anomaloChecks") +Salesforce.APPLICATION = RelationField("application") +Salesforce.APPLICATION_FIELD = RelationField("applicationField") +Salesforce.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Salesforce.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Salesforce.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Salesforce.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Salesforce.METRICS = RelationField("metrics") +Salesforce.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Salesforce.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Salesforce.MEANINGS = RelationField("meanings") +Salesforce.MC_MONITORS = RelationField("mcMonitors") +Salesforce.MC_INCIDENTS = RelationField("mcIncidents") +Salesforce.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Salesforce.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Salesforce.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Salesforce.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Salesforce.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Salesforce.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Salesforce.FILES = RelationField("files") +Salesforce.LINKS = RelationField("links") +Salesforce.README = RelationField("readme") +Salesforce.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Salesforce.SODA_CHECKS = RelationField("sodaChecks") +Salesforce.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Salesforce.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/salesforce_dashboard.py b/pyatlan_v9/model/assets/salesforce_dashboard.py new file mode 100644 index 000000000..87b272e37 --- /dev/null +++ b/pyatlan_v9/model/assets/salesforce_dashboard.py @@ -0,0 +1,633 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SalesforceDashboard asset model with flattened inheritance. + +This module provides: +- SalesforceDashboard: Flat asset class (easy to use) +- SalesforceDashboardAttributes: Nested attributes struct (extends AssetAttributes) +- SalesforceDashboardNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .salesforce_related import RelatedSalesforceOrganization, RelatedSalesforceReport + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SalesforceDashboard(Asset): + """ + Instance of a Salesforce dashboard in Atlan. + """ + + SOURCE_ID: ClassVar[Any] = None + DASHBOARD_TYPE: ClassVar[Any] = None + REPORT_COUNT: ClassVar[Any] = None + ORGANIZATION_QUALIFIED_NAME: ClassVar[Any] = None + API_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + ORGANIZATION: ClassVar[Any] = None + REPORTS: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SalesforceDashboard" + + source_id: Union[str, None, UnsetType] = UNSET + """Identifier of the dashboard in Salesforce.""" + + dashboard_type: Union[str, None, UnsetType] = UNSET + """Type of dashboard in Salesforce.""" + + report_count: Union[int, None, UnsetType] = UNSET + """Number of reports linked to the dashboard in Salesforce.""" + + organization_qualified_name: Union[str, None, UnsetType] = UNSET + """Fully-qualified name of the organization in Salesforce.""" + + api_name: Union[str, None, UnsetType] = UNSET + """Name of this asset in the Salesforce API.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + organization: Union[RelatedSalesforceOrganization, None, UnsetType] = UNSET + """Organization in which this dashboard exists.""" + + reports: Union[List[RelatedSalesforceReport], None, UnsetType] = UNSET + """""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SalesforceDashboard" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _salesforce_dashboard_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> SalesforceDashboard: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SalesforceDashboard instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _salesforce_dashboard_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SalesforceDashboardAttributes(AssetAttributes): + """SalesforceDashboard-specific attributes for nested API format.""" + + source_id: Union[str, None, UnsetType] = UNSET + """Identifier of the dashboard in Salesforce.""" + + dashboard_type: Union[str, None, UnsetType] = UNSET + """Type of dashboard in Salesforce.""" + + report_count: Union[int, None, UnsetType] = UNSET + """Number of reports linked to the dashboard in Salesforce.""" + + organization_qualified_name: Union[str, None, UnsetType] = UNSET + """Fully-qualified name of the organization in Salesforce.""" + + api_name: Union[str, None, UnsetType] = UNSET + """Name of this asset in the Salesforce API.""" + + +class SalesforceDashboardRelationshipAttributes(AssetRelationshipAttributes): + """SalesforceDashboard-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + organization: Union[RelatedSalesforceOrganization, None, UnsetType] = UNSET + """Organization in which this dashboard exists.""" + + reports: Union[List[RelatedSalesforceReport], None, UnsetType] = UNSET + """""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SalesforceDashboardNested(AssetNested): + """SalesforceDashboard in nested API format for high-performance serialization.""" + + attributes: Union[SalesforceDashboardAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + SalesforceDashboardRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + SalesforceDashboardRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SalesforceDashboardRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SALESFORCE_DASHBOARD_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "organization", + "reports", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_salesforce_dashboard_attrs( + attrs: SalesforceDashboardAttributes, obj: SalesforceDashboard +) -> None: + """Populate SalesforceDashboard-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.source_id = obj.source_id + attrs.dashboard_type = obj.dashboard_type + attrs.report_count = obj.report_count + attrs.organization_qualified_name = obj.organization_qualified_name + attrs.api_name = obj.api_name + + +def _extract_salesforce_dashboard_attrs(attrs: SalesforceDashboardAttributes) -> dict: + """Extract all SalesforceDashboard attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["source_id"] = attrs.source_id + result["dashboard_type"] = attrs.dashboard_type + result["report_count"] = attrs.report_count + result["organization_qualified_name"] = attrs.organization_qualified_name + result["api_name"] = attrs.api_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _salesforce_dashboard_to_nested( + salesforce_dashboard: SalesforceDashboard, +) -> SalesforceDashboardNested: + """Convert flat SalesforceDashboard to nested format.""" + attrs = SalesforceDashboardAttributes() + _populate_salesforce_dashboard_attrs(attrs, salesforce_dashboard) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + salesforce_dashboard, + _SALESFORCE_DASHBOARD_REL_FIELDS, + SalesforceDashboardRelationshipAttributes, + ) + return SalesforceDashboardNested( + guid=salesforce_dashboard.guid, + type_name=salesforce_dashboard.type_name, + status=salesforce_dashboard.status, + version=salesforce_dashboard.version, + create_time=salesforce_dashboard.create_time, + update_time=salesforce_dashboard.update_time, + created_by=salesforce_dashboard.created_by, + updated_by=salesforce_dashboard.updated_by, + classifications=salesforce_dashboard.classifications, + classification_names=salesforce_dashboard.classification_names, + meanings=salesforce_dashboard.meanings, + labels=salesforce_dashboard.labels, + business_attributes=salesforce_dashboard.business_attributes, + custom_attributes=salesforce_dashboard.custom_attributes, + pending_tasks=salesforce_dashboard.pending_tasks, + proxy=salesforce_dashboard.proxy, + is_incomplete=salesforce_dashboard.is_incomplete, + provenance_type=salesforce_dashboard.provenance_type, + home_id=salesforce_dashboard.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _salesforce_dashboard_from_nested( + nested: SalesforceDashboardNested, +) -> SalesforceDashboard: + """Convert nested format to flat SalesforceDashboard.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else SalesforceDashboardAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SALESFORCE_DASHBOARD_REL_FIELDS, + SalesforceDashboardRelationshipAttributes, + ) + return SalesforceDashboard( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_salesforce_dashboard_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _salesforce_dashboard_to_nested_bytes( + salesforce_dashboard: SalesforceDashboard, serde: Serde +) -> bytes: + """Convert flat SalesforceDashboard to nested JSON bytes.""" + return serde.encode(_salesforce_dashboard_to_nested(salesforce_dashboard)) + + +def _salesforce_dashboard_from_nested_bytes( + data: bytes, serde: Serde +) -> SalesforceDashboard: + """Convert nested JSON bytes to flat SalesforceDashboard.""" + nested = serde.decode(data, SalesforceDashboardNested) + return _salesforce_dashboard_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +SalesforceDashboard.SOURCE_ID = KeywordField("sourceId", "sourceId") +SalesforceDashboard.DASHBOARD_TYPE = KeywordField("dashboardType", "dashboardType") +SalesforceDashboard.REPORT_COUNT = NumericField("reportCount", "reportCount") +SalesforceDashboard.ORGANIZATION_QUALIFIED_NAME = KeywordField( + "organizationQualifiedName", "organizationQualifiedName" +) +SalesforceDashboard.API_NAME = KeywordField("apiName", "apiName") +SalesforceDashboard.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SalesforceDashboard.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +SalesforceDashboard.ANOMALO_CHECKS = RelationField("anomaloChecks") +SalesforceDashboard.APPLICATION = RelationField("application") +SalesforceDashboard.APPLICATION_FIELD = RelationField("applicationField") +SalesforceDashboard.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +SalesforceDashboard.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +SalesforceDashboard.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +SalesforceDashboard.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +SalesforceDashboard.METRICS = RelationField("metrics") +SalesforceDashboard.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SalesforceDashboard.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +SalesforceDashboard.MEANINGS = RelationField("meanings") +SalesforceDashboard.MC_MONITORS = RelationField("mcMonitors") +SalesforceDashboard.MC_INCIDENTS = RelationField("mcIncidents") +SalesforceDashboard.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SalesforceDashboard.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SalesforceDashboard.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SalesforceDashboard.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SalesforceDashboard.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +SalesforceDashboard.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +SalesforceDashboard.FILES = RelationField("files") +SalesforceDashboard.LINKS = RelationField("links") +SalesforceDashboard.README = RelationField("readme") +SalesforceDashboard.ORGANIZATION = RelationField("organization") +SalesforceDashboard.REPORTS = RelationField("reports") +SalesforceDashboard.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +SalesforceDashboard.SODA_CHECKS = RelationField("sodaChecks") +SalesforceDashboard.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SalesforceDashboard.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/salesforce_field.py b/pyatlan_v9/model/assets/salesforce_field.py new file mode 100644 index 000000000..69b8b6c72 --- /dev/null +++ b/pyatlan_v9/model/assets/salesforce_field.py @@ -0,0 +1,763 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SalesforceField asset model with flattened inheritance. + +This module provides: +- SalesforceField: Flat asset class (easy to use) +- SalesforceFieldAttributes: Nested attributes struct (extends AssetAttributes) +- SalesforceFieldNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .salesforce_related import RelatedSalesforceObject + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SalesforceField(Asset): + """ + Instance of a Salesforce field in Atlan. + """ + + DATA_TYPE: ClassVar[Any] = None + OBJECT_QUALIFIED_NAME: ClassVar[Any] = None + ORDER: ClassVar[Any] = None + INLINE_HELP_TEXT: ClassVar[Any] = None + IS_CALCULATED: ClassVar[Any] = None + FORMULA: ClassVar[Any] = None + IS_CASE_SENSITIVE: ClassVar[Any] = None + IS_ENCRYPTED: ClassVar[Any] = None + MAX_LENGTH: ClassVar[Any] = None + IS_NULLABLE: ClassVar[Any] = None + PRECISION: ClassVar[Any] = None + NUMERIC_SCALE: ClassVar[Any] = None + IS_UNIQUE: ClassVar[Any] = None + PICKLIST_VALUES: ClassVar[Any] = None + IS_POLYMORPHIC_FOREIGN_KEY: ClassVar[Any] = None + DEFAULT_VALUE_FORMULA: ClassVar[Any] = None + ORGANIZATION_QUALIFIED_NAME: ClassVar[Any] = None + API_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + OBJECT: ClassVar[Any] = None + LOOKUP_OBJECTS: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SalesforceField" + + data_type: Union[str, None, UnsetType] = UNSET + """Data type of values in this field.""" + + object_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the object in which this field exists.""" + + order: Union[int, None, UnsetType] = UNSET + """Order (position) of this field within the object.""" + + inline_help_text: Union[str, None, UnsetType] = UNSET + """Help text for this field.""" + + is_calculated: Union[bool, None, UnsetType] = UNSET + """Whether this field is calculated (true) or not (false).""" + + formula: Union[str, None, UnsetType] = UNSET + """Formula for this field, if it is a calculated field.""" + + is_case_sensitive: Union[bool, None, UnsetType] = UNSET + """Whether this field is case sensitive (true) or in-sensitive (false).""" + + is_encrypted: Union[bool, None, UnsetType] = UNSET + """Whether this field is encrypted (true) or not (false).""" + + max_length: Union[int, None, UnsetType] = UNSET + """Maximum length of this field.""" + + is_nullable: Union[bool, None, UnsetType] = UNSET + """Whether this field allows null values (true) or not (false).""" + + precision: Union[int, None, UnsetType] = UNSET + """Total number of digits allowed.""" + + numeric_scale: Union[float, None, UnsetType] = UNSET + """Number of digits allowed to the right of the decimal point.""" + + is_unique: Union[bool, None, UnsetType] = UNSET + """Whether this field must have unique values (true) or not (false).""" + + picklist_values: Union[List[str], None, UnsetType] = UNSET + """List of values from which a user can pick while adding a record.""" + + is_polymorphic_foreign_key: Union[bool, None, UnsetType] = UNSET + """Whether this field references a record of multiple objects (true) or not (false).""" + + default_value_formula: Union[str, None, UnsetType] = UNSET + """Formula for the default value for this field.""" + + organization_qualified_name: Union[str, None, UnsetType] = UNSET + """Fully-qualified name of the organization in Salesforce.""" + + api_name: Union[str, None, UnsetType] = UNSET + """Name of this asset in the Salesforce API.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + object: Union[RelatedSalesforceObject, None, UnsetType] = UNSET + """Object in which this field exists.""" + + lookup_objects: Union[List[RelatedSalesforceObject], None, UnsetType] = UNSET + """""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SalesforceField" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _salesforce_field_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> SalesforceField: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SalesforceField instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _salesforce_field_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SalesforceFieldAttributes(AssetAttributes): + """SalesforceField-specific attributes for nested API format.""" + + data_type: Union[str, None, UnsetType] = UNSET + """Data type of values in this field.""" + + object_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the object in which this field exists.""" + + order: Union[int, None, UnsetType] = UNSET + """Order (position) of this field within the object.""" + + inline_help_text: Union[str, None, UnsetType] = UNSET + """Help text for this field.""" + + is_calculated: Union[bool, None, UnsetType] = UNSET + """Whether this field is calculated (true) or not (false).""" + + formula: Union[str, None, UnsetType] = UNSET + """Formula for this field, if it is a calculated field.""" + + is_case_sensitive: Union[bool, None, UnsetType] = UNSET + """Whether this field is case sensitive (true) or in-sensitive (false).""" + + is_encrypted: Union[bool, None, UnsetType] = UNSET + """Whether this field is encrypted (true) or not (false).""" + + max_length: Union[int, None, UnsetType] = UNSET + """Maximum length of this field.""" + + is_nullable: Union[bool, None, UnsetType] = UNSET + """Whether this field allows null values (true) or not (false).""" + + precision: Union[int, None, UnsetType] = UNSET + """Total number of digits allowed.""" + + numeric_scale: Union[float, None, UnsetType] = UNSET + """Number of digits allowed to the right of the decimal point.""" + + is_unique: Union[bool, None, UnsetType] = UNSET + """Whether this field must have unique values (true) or not (false).""" + + picklist_values: Union[List[str], None, UnsetType] = UNSET + """List of values from which a user can pick while adding a record.""" + + is_polymorphic_foreign_key: Union[bool, None, UnsetType] = UNSET + """Whether this field references a record of multiple objects (true) or not (false).""" + + default_value_formula: Union[str, None, UnsetType] = UNSET + """Formula for the default value for this field.""" + + organization_qualified_name: Union[str, None, UnsetType] = UNSET + """Fully-qualified name of the organization in Salesforce.""" + + api_name: Union[str, None, UnsetType] = UNSET + """Name of this asset in the Salesforce API.""" + + +class SalesforceFieldRelationshipAttributes(AssetRelationshipAttributes): + """SalesforceField-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + object: Union[RelatedSalesforceObject, None, UnsetType] = UNSET + """Object in which this field exists.""" + + lookup_objects: Union[List[RelatedSalesforceObject], None, UnsetType] = UNSET + """""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SalesforceFieldNested(AssetNested): + """SalesforceField in nested API format for high-performance serialization.""" + + attributes: Union[SalesforceFieldAttributes, UnsetType] = UNSET + relationship_attributes: Union[SalesforceFieldRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + SalesforceFieldRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SalesforceFieldRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SALESFORCE_FIELD_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "object", + "lookup_objects", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_salesforce_field_attrs( + attrs: SalesforceFieldAttributes, obj: SalesforceField +) -> None: + """Populate SalesforceField-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.data_type = obj.data_type + attrs.object_qualified_name = obj.object_qualified_name + attrs.order = obj.order + attrs.inline_help_text = obj.inline_help_text + attrs.is_calculated = obj.is_calculated + attrs.formula = obj.formula + attrs.is_case_sensitive = obj.is_case_sensitive + attrs.is_encrypted = obj.is_encrypted + attrs.max_length = obj.max_length + attrs.is_nullable = obj.is_nullable + attrs.precision = obj.precision + attrs.numeric_scale = obj.numeric_scale + attrs.is_unique = obj.is_unique + attrs.picklist_values = obj.picklist_values + attrs.is_polymorphic_foreign_key = obj.is_polymorphic_foreign_key + attrs.default_value_formula = obj.default_value_formula + attrs.organization_qualified_name = obj.organization_qualified_name + attrs.api_name = obj.api_name + + +def _extract_salesforce_field_attrs(attrs: SalesforceFieldAttributes) -> dict: + """Extract all SalesforceField attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["data_type"] = attrs.data_type + result["object_qualified_name"] = attrs.object_qualified_name + result["order"] = attrs.order + result["inline_help_text"] = attrs.inline_help_text + result["is_calculated"] = attrs.is_calculated + result["formula"] = attrs.formula + result["is_case_sensitive"] = attrs.is_case_sensitive + result["is_encrypted"] = attrs.is_encrypted + result["max_length"] = attrs.max_length + result["is_nullable"] = attrs.is_nullable + result["precision"] = attrs.precision + result["numeric_scale"] = attrs.numeric_scale + result["is_unique"] = attrs.is_unique + result["picklist_values"] = attrs.picklist_values + result["is_polymorphic_foreign_key"] = attrs.is_polymorphic_foreign_key + result["default_value_formula"] = attrs.default_value_formula + result["organization_qualified_name"] = attrs.organization_qualified_name + result["api_name"] = attrs.api_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _salesforce_field_to_nested( + salesforce_field: SalesforceField, +) -> SalesforceFieldNested: + """Convert flat SalesforceField to nested format.""" + attrs = SalesforceFieldAttributes() + _populate_salesforce_field_attrs(attrs, salesforce_field) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + salesforce_field, + _SALESFORCE_FIELD_REL_FIELDS, + SalesforceFieldRelationshipAttributes, + ) + return SalesforceFieldNested( + guid=salesforce_field.guid, + type_name=salesforce_field.type_name, + status=salesforce_field.status, + version=salesforce_field.version, + create_time=salesforce_field.create_time, + update_time=salesforce_field.update_time, + created_by=salesforce_field.created_by, + updated_by=salesforce_field.updated_by, + classifications=salesforce_field.classifications, + classification_names=salesforce_field.classification_names, + meanings=salesforce_field.meanings, + labels=salesforce_field.labels, + business_attributes=salesforce_field.business_attributes, + custom_attributes=salesforce_field.custom_attributes, + pending_tasks=salesforce_field.pending_tasks, + proxy=salesforce_field.proxy, + is_incomplete=salesforce_field.is_incomplete, + provenance_type=salesforce_field.provenance_type, + home_id=salesforce_field.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _salesforce_field_from_nested(nested: SalesforceFieldNested) -> SalesforceField: + """Convert nested format to flat SalesforceField.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else SalesforceFieldAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SALESFORCE_FIELD_REL_FIELDS, + SalesforceFieldRelationshipAttributes, + ) + return SalesforceField( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_salesforce_field_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _salesforce_field_to_nested_bytes( + salesforce_field: SalesforceField, serde: Serde +) -> bytes: + """Convert flat SalesforceField to nested JSON bytes.""" + return serde.encode(_salesforce_field_to_nested(salesforce_field)) + + +def _salesforce_field_from_nested_bytes(data: bytes, serde: Serde) -> SalesforceField: + """Convert nested JSON bytes to flat SalesforceField.""" + nested = serde.decode(data, SalesforceFieldNested) + return _salesforce_field_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +SalesforceField.DATA_TYPE = KeywordTextField("dataType", "dataType", "dataType.text") +SalesforceField.OBJECT_QUALIFIED_NAME = KeywordField( + "objectQualifiedName", "objectQualifiedName" +) +SalesforceField.ORDER = NumericField("order", "order") +SalesforceField.INLINE_HELP_TEXT = KeywordField("inlineHelpText", "inlineHelpText") +SalesforceField.IS_CALCULATED = BooleanField("isCalculated", "isCalculated") +SalesforceField.FORMULA = KeywordField("formula", "formula") +SalesforceField.IS_CASE_SENSITIVE = BooleanField("isCaseSensitive", "isCaseSensitive") +SalesforceField.IS_ENCRYPTED = BooleanField("isEncrypted", "isEncrypted") +SalesforceField.MAX_LENGTH = NumericField("maxLength", "maxLength") +SalesforceField.IS_NULLABLE = BooleanField("isNullable", "isNullable") +SalesforceField.PRECISION = NumericField("precision", "precision") +SalesforceField.NUMERIC_SCALE = NumericField("numericScale", "numericScale") +SalesforceField.IS_UNIQUE = BooleanField("isUnique", "isUnique") +SalesforceField.PICKLIST_VALUES = KeywordField("picklistValues", "picklistValues") +SalesforceField.IS_POLYMORPHIC_FOREIGN_KEY = BooleanField( + "isPolymorphicForeignKey", "isPolymorphicForeignKey" +) +SalesforceField.DEFAULT_VALUE_FORMULA = KeywordField( + "defaultValueFormula", "defaultValueFormula" +) +SalesforceField.ORGANIZATION_QUALIFIED_NAME = KeywordField( + "organizationQualifiedName", "organizationQualifiedName" +) +SalesforceField.API_NAME = KeywordField("apiName", "apiName") +SalesforceField.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SalesforceField.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +SalesforceField.ANOMALO_CHECKS = RelationField("anomaloChecks") +SalesforceField.APPLICATION = RelationField("application") +SalesforceField.APPLICATION_FIELD = RelationField("applicationField") +SalesforceField.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +SalesforceField.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +SalesforceField.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +SalesforceField.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +SalesforceField.METRICS = RelationField("metrics") +SalesforceField.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SalesforceField.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +SalesforceField.MEANINGS = RelationField("meanings") +SalesforceField.MC_MONITORS = RelationField("mcMonitors") +SalesforceField.MC_INCIDENTS = RelationField("mcIncidents") +SalesforceField.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SalesforceField.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SalesforceField.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SalesforceField.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SalesforceField.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +SalesforceField.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +SalesforceField.FILES = RelationField("files") +SalesforceField.LINKS = RelationField("links") +SalesforceField.README = RelationField("readme") +SalesforceField.OBJECT = RelationField("object") +SalesforceField.LOOKUP_OBJECTS = RelationField("lookupObjects") +SalesforceField.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +SalesforceField.SODA_CHECKS = RelationField("sodaChecks") +SalesforceField.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SalesforceField.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/salesforce_object.py b/pyatlan_v9/model/assets/salesforce_object.py new file mode 100644 index 000000000..97ee3cf61 --- /dev/null +++ b/pyatlan_v9/model/assets/salesforce_object.py @@ -0,0 +1,643 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SalesforceObject asset model with flattened inheritance. + +This module provides: +- SalesforceObject: Flat asset class (easy to use) +- SalesforceObjectAttributes: Nested attributes struct (extends AssetAttributes) +- SalesforceObjectNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .salesforce_related import RelatedSalesforceField, RelatedSalesforceOrganization + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SalesforceObject(Asset): + """ + Instance of a Salesforce object in Atlan. + """ + + IS_CUSTOM: ClassVar[Any] = None + IS_MERGABLE: ClassVar[Any] = None + IS_QUERYABLE: ClassVar[Any] = None + FIELD_COUNT: ClassVar[Any] = None + ORGANIZATION_QUALIFIED_NAME: ClassVar[Any] = None + API_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + ORGANIZATION: ClassVar[Any] = None + FIELDS: ClassVar[Any] = None + LOOKUP_FIELDS: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SalesforceObject" + + is_custom: Union[bool, None, UnsetType] = UNSET + """Whether this object is a custom object (true) or not (false).""" + + is_mergable: Union[bool, None, UnsetType] = UNSET + """Whether this object is mergable (true) or not (false).""" + + is_queryable: Union[bool, None, UnsetType] = UNSET + """Whether this object is queryable (true) or not (false).""" + + field_count: Union[int, None, UnsetType] = UNSET + """Number of fields in this object.""" + + organization_qualified_name: Union[str, None, UnsetType] = UNSET + """Fully-qualified name of the organization in Salesforce.""" + + api_name: Union[str, None, UnsetType] = UNSET + """Name of this asset in the Salesforce API.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + organization: Union[RelatedSalesforceOrganization, None, UnsetType] = UNSET + """Organization in which this object exists.""" + + fields: Union[List[RelatedSalesforceField], None, UnsetType] = UNSET + """Fields that exist within this object.""" + + lookup_fields: Union[List[RelatedSalesforceField], None, UnsetType] = UNSET + """""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SalesforceObject" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _salesforce_object_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> SalesforceObject: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SalesforceObject instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _salesforce_object_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SalesforceObjectAttributes(AssetAttributes): + """SalesforceObject-specific attributes for nested API format.""" + + is_custom: Union[bool, None, UnsetType] = UNSET + """Whether this object is a custom object (true) or not (false).""" + + is_mergable: Union[bool, None, UnsetType] = UNSET + """Whether this object is mergable (true) or not (false).""" + + is_queryable: Union[bool, None, UnsetType] = UNSET + """Whether this object is queryable (true) or not (false).""" + + field_count: Union[int, None, UnsetType] = UNSET + """Number of fields in this object.""" + + organization_qualified_name: Union[str, None, UnsetType] = UNSET + """Fully-qualified name of the organization in Salesforce.""" + + api_name: Union[str, None, UnsetType] = UNSET + """Name of this asset in the Salesforce API.""" + + +class SalesforceObjectRelationshipAttributes(AssetRelationshipAttributes): + """SalesforceObject-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + organization: Union[RelatedSalesforceOrganization, None, UnsetType] = UNSET + """Organization in which this object exists.""" + + fields: Union[List[RelatedSalesforceField], None, UnsetType] = UNSET + """Fields that exist within this object.""" + + lookup_fields: Union[List[RelatedSalesforceField], None, UnsetType] = UNSET + """""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SalesforceObjectNested(AssetNested): + """SalesforceObject in nested API format for high-performance serialization.""" + + attributes: Union[SalesforceObjectAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + SalesforceObjectRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + SalesforceObjectRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SalesforceObjectRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SALESFORCE_OBJECT_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "organization", + "fields", + "lookup_fields", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_salesforce_object_attrs( + attrs: SalesforceObjectAttributes, obj: SalesforceObject +) -> None: + """Populate SalesforceObject-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.is_custom = obj.is_custom + attrs.is_mergable = obj.is_mergable + attrs.is_queryable = obj.is_queryable + attrs.field_count = obj.field_count + attrs.organization_qualified_name = obj.organization_qualified_name + attrs.api_name = obj.api_name + + +def _extract_salesforce_object_attrs(attrs: SalesforceObjectAttributes) -> dict: + """Extract all SalesforceObject attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["is_custom"] = attrs.is_custom + result["is_mergable"] = attrs.is_mergable + result["is_queryable"] = attrs.is_queryable + result["field_count"] = attrs.field_count + result["organization_qualified_name"] = attrs.organization_qualified_name + result["api_name"] = attrs.api_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _salesforce_object_to_nested( + salesforce_object: SalesforceObject, +) -> SalesforceObjectNested: + """Convert flat SalesforceObject to nested format.""" + attrs = SalesforceObjectAttributes() + _populate_salesforce_object_attrs(attrs, salesforce_object) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + salesforce_object, + _SALESFORCE_OBJECT_REL_FIELDS, + SalesforceObjectRelationshipAttributes, + ) + return SalesforceObjectNested( + guid=salesforce_object.guid, + type_name=salesforce_object.type_name, + status=salesforce_object.status, + version=salesforce_object.version, + create_time=salesforce_object.create_time, + update_time=salesforce_object.update_time, + created_by=salesforce_object.created_by, + updated_by=salesforce_object.updated_by, + classifications=salesforce_object.classifications, + classification_names=salesforce_object.classification_names, + meanings=salesforce_object.meanings, + labels=salesforce_object.labels, + business_attributes=salesforce_object.business_attributes, + custom_attributes=salesforce_object.custom_attributes, + pending_tasks=salesforce_object.pending_tasks, + proxy=salesforce_object.proxy, + is_incomplete=salesforce_object.is_incomplete, + provenance_type=salesforce_object.provenance_type, + home_id=salesforce_object.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _salesforce_object_from_nested(nested: SalesforceObjectNested) -> SalesforceObject: + """Convert nested format to flat SalesforceObject.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else SalesforceObjectAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SALESFORCE_OBJECT_REL_FIELDS, + SalesforceObjectRelationshipAttributes, + ) + return SalesforceObject( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_salesforce_object_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _salesforce_object_to_nested_bytes( + salesforce_object: SalesforceObject, serde: Serde +) -> bytes: + """Convert flat SalesforceObject to nested JSON bytes.""" + return serde.encode(_salesforce_object_to_nested(salesforce_object)) + + +def _salesforce_object_from_nested_bytes(data: bytes, serde: Serde) -> SalesforceObject: + """Convert nested JSON bytes to flat SalesforceObject.""" + nested = serde.decode(data, SalesforceObjectNested) + return _salesforce_object_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, +) + +SalesforceObject.IS_CUSTOM = BooleanField("isCustom", "isCustom") +SalesforceObject.IS_MERGABLE = BooleanField("isMergable", "isMergable") +SalesforceObject.IS_QUERYABLE = BooleanField("isQueryable", "isQueryable") +SalesforceObject.FIELD_COUNT = NumericField("fieldCount", "fieldCount") +SalesforceObject.ORGANIZATION_QUALIFIED_NAME = KeywordField( + "organizationQualifiedName", "organizationQualifiedName" +) +SalesforceObject.API_NAME = KeywordField("apiName", "apiName") +SalesforceObject.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SalesforceObject.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +SalesforceObject.ANOMALO_CHECKS = RelationField("anomaloChecks") +SalesforceObject.APPLICATION = RelationField("application") +SalesforceObject.APPLICATION_FIELD = RelationField("applicationField") +SalesforceObject.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +SalesforceObject.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +SalesforceObject.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +SalesforceObject.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +SalesforceObject.METRICS = RelationField("metrics") +SalesforceObject.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SalesforceObject.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +SalesforceObject.MEANINGS = RelationField("meanings") +SalesforceObject.MC_MONITORS = RelationField("mcMonitors") +SalesforceObject.MC_INCIDENTS = RelationField("mcIncidents") +SalesforceObject.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SalesforceObject.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SalesforceObject.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SalesforceObject.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SalesforceObject.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +SalesforceObject.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +SalesforceObject.FILES = RelationField("files") +SalesforceObject.LINKS = RelationField("links") +SalesforceObject.README = RelationField("readme") +SalesforceObject.ORGANIZATION = RelationField("organization") +SalesforceObject.FIELDS = RelationField("fields") +SalesforceObject.LOOKUP_FIELDS = RelationField("lookupFields") +SalesforceObject.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +SalesforceObject.SODA_CHECKS = RelationField("sodaChecks") +SalesforceObject.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SalesforceObject.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/salesforce_organization.py b/pyatlan_v9/model/assets/salesforce_organization.py new file mode 100644 index 000000000..dafe261e1 --- /dev/null +++ b/pyatlan_v9/model/assets/salesforce_organization.py @@ -0,0 +1,626 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SalesforceOrganization asset model with flattened inheritance. + +This module provides: +- SalesforceOrganization: Flat asset class (easy to use) +- SalesforceOrganizationAttributes: Nested attributes struct (extends AssetAttributes) +- SalesforceOrganizationNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .salesforce_related import ( + RelatedSalesforceDashboard, + RelatedSalesforceObject, + RelatedSalesforceReport, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SalesforceOrganization(Asset): + """ + Instance of a Salesforce organization in Atlan. + """ + + SOURCE_ID: ClassVar[Any] = None + ORGANIZATION_QUALIFIED_NAME: ClassVar[Any] = None + API_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + REPORTS: ClassVar[Any] = None + DASHBOARDS: ClassVar[Any] = None + OBJECTS: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SalesforceOrganization" + + source_id: Union[str, None, UnsetType] = UNSET + """Identifier of the organization in Salesforce.""" + + organization_qualified_name: Union[str, None, UnsetType] = UNSET + """Fully-qualified name of the organization in Salesforce.""" + + api_name: Union[str, None, UnsetType] = UNSET + """Name of this asset in the Salesforce API.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + reports: Union[List[RelatedSalesforceReport], None, UnsetType] = UNSET + """Reports that exist within this organization.""" + + dashboards: Union[List[RelatedSalesforceDashboard], None, UnsetType] = UNSET + """Dashboards that exist within this organization.""" + + objects: Union[List[RelatedSalesforceObject], None, UnsetType] = UNSET + """Objects that exist within this organization.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SalesforceOrganization" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _salesforce_organization_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> SalesforceOrganization: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SalesforceOrganization instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _salesforce_organization_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SalesforceOrganizationAttributes(AssetAttributes): + """SalesforceOrganization-specific attributes for nested API format.""" + + source_id: Union[str, None, UnsetType] = UNSET + """Identifier of the organization in Salesforce.""" + + organization_qualified_name: Union[str, None, UnsetType] = UNSET + """Fully-qualified name of the organization in Salesforce.""" + + api_name: Union[str, None, UnsetType] = UNSET + """Name of this asset in the Salesforce API.""" + + +class SalesforceOrganizationRelationshipAttributes(AssetRelationshipAttributes): + """SalesforceOrganization-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + reports: Union[List[RelatedSalesforceReport], None, UnsetType] = UNSET + """Reports that exist within this organization.""" + + dashboards: Union[List[RelatedSalesforceDashboard], None, UnsetType] = UNSET + """Dashboards that exist within this organization.""" + + objects: Union[List[RelatedSalesforceObject], None, UnsetType] = UNSET + """Objects that exist within this organization.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SalesforceOrganizationNested(AssetNested): + """SalesforceOrganization in nested API format for high-performance serialization.""" + + attributes: Union[SalesforceOrganizationAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + SalesforceOrganizationRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + SalesforceOrganizationRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SalesforceOrganizationRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SALESFORCE_ORGANIZATION_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "reports", + "dashboards", + "objects", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_salesforce_organization_attrs( + attrs: SalesforceOrganizationAttributes, obj: SalesforceOrganization +) -> None: + """Populate SalesforceOrganization-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.source_id = obj.source_id + attrs.organization_qualified_name = obj.organization_qualified_name + attrs.api_name = obj.api_name + + +def _extract_salesforce_organization_attrs( + attrs: SalesforceOrganizationAttributes, +) -> dict: + """Extract all SalesforceOrganization attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["source_id"] = attrs.source_id + result["organization_qualified_name"] = attrs.organization_qualified_name + result["api_name"] = attrs.api_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _salesforce_organization_to_nested( + salesforce_organization: SalesforceOrganization, +) -> SalesforceOrganizationNested: + """Convert flat SalesforceOrganization to nested format.""" + attrs = SalesforceOrganizationAttributes() + _populate_salesforce_organization_attrs(attrs, salesforce_organization) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + salesforce_organization, + _SALESFORCE_ORGANIZATION_REL_FIELDS, + SalesforceOrganizationRelationshipAttributes, + ) + return SalesforceOrganizationNested( + guid=salesforce_organization.guid, + type_name=salesforce_organization.type_name, + status=salesforce_organization.status, + version=salesforce_organization.version, + create_time=salesforce_organization.create_time, + update_time=salesforce_organization.update_time, + created_by=salesforce_organization.created_by, + updated_by=salesforce_organization.updated_by, + classifications=salesforce_organization.classifications, + classification_names=salesforce_organization.classification_names, + meanings=salesforce_organization.meanings, + labels=salesforce_organization.labels, + business_attributes=salesforce_organization.business_attributes, + custom_attributes=salesforce_organization.custom_attributes, + pending_tasks=salesforce_organization.pending_tasks, + proxy=salesforce_organization.proxy, + is_incomplete=salesforce_organization.is_incomplete, + provenance_type=salesforce_organization.provenance_type, + home_id=salesforce_organization.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _salesforce_organization_from_nested( + nested: SalesforceOrganizationNested, +) -> SalesforceOrganization: + """Convert nested format to flat SalesforceOrganization.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else SalesforceOrganizationAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SALESFORCE_ORGANIZATION_REL_FIELDS, + SalesforceOrganizationRelationshipAttributes, + ) + return SalesforceOrganization( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_salesforce_organization_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _salesforce_organization_to_nested_bytes( + salesforce_organization: SalesforceOrganization, serde: Serde +) -> bytes: + """Convert flat SalesforceOrganization to nested JSON bytes.""" + return serde.encode(_salesforce_organization_to_nested(salesforce_organization)) + + +def _salesforce_organization_from_nested_bytes( + data: bytes, serde: Serde +) -> SalesforceOrganization: + """Convert nested JSON bytes to flat SalesforceOrganization.""" + nested = serde.decode(data, SalesforceOrganizationNested) + return _salesforce_organization_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +SalesforceOrganization.SOURCE_ID = KeywordField("sourceId", "sourceId") +SalesforceOrganization.ORGANIZATION_QUALIFIED_NAME = KeywordField( + "organizationQualifiedName", "organizationQualifiedName" +) +SalesforceOrganization.API_NAME = KeywordField("apiName", "apiName") +SalesforceOrganization.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SalesforceOrganization.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +SalesforceOrganization.ANOMALO_CHECKS = RelationField("anomaloChecks") +SalesforceOrganization.APPLICATION = RelationField("application") +SalesforceOrganization.APPLICATION_FIELD = RelationField("applicationField") +SalesforceOrganization.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +SalesforceOrganization.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +SalesforceOrganization.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +SalesforceOrganization.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +SalesforceOrganization.METRICS = RelationField("metrics") +SalesforceOrganization.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SalesforceOrganization.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +SalesforceOrganization.MEANINGS = RelationField("meanings") +SalesforceOrganization.MC_MONITORS = RelationField("mcMonitors") +SalesforceOrganization.MC_INCIDENTS = RelationField("mcIncidents") +SalesforceOrganization.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SalesforceOrganization.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SalesforceOrganization.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SalesforceOrganization.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SalesforceOrganization.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +SalesforceOrganization.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +SalesforceOrganization.FILES = RelationField("files") +SalesforceOrganization.LINKS = RelationField("links") +SalesforceOrganization.README = RelationField("readme") +SalesforceOrganization.REPORTS = RelationField("reports") +SalesforceOrganization.DASHBOARDS = RelationField("dashboards") +SalesforceOrganization.OBJECTS = RelationField("objects") +SalesforceOrganization.SCHEMA_REGISTRY_SUBJECTS = RelationField( + "schemaRegistrySubjects" +) +SalesforceOrganization.SODA_CHECKS = RelationField("sodaChecks") +SalesforceOrganization.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SalesforceOrganization.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/salesforce_related.py b/pyatlan_v9/model/assets/salesforce_related.py new file mode 100644 index 000000000..0ef7c4737 --- /dev/null +++ b/pyatlan_v9/model/assets/salesforce_related.py @@ -0,0 +1,205 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Salesforce module. + +This module contains all Related{Type} classes for the Salesforce type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedSaaS +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedSalesforce", + "RelatedSalesforceOrganization", + "RelatedSalesforceObject", + "RelatedSalesforceField", + "RelatedSalesforceReport", + "RelatedSalesforceDashboard", +] + + +class RelatedSalesforce(RelatedSaaS): + """ + Related entity reference for Salesforce assets. + + Extends RelatedSaaS with Salesforce-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Salesforce" so it serializes correctly + + organization_qualified_name: Union[str, None, UnsetType] = UNSET + """Fully-qualified name of the organization in Salesforce.""" + + api_name: Union[str, None, UnsetType] = UNSET + """Name of this asset in the Salesforce API.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Salesforce" + + +class RelatedSalesforceOrganization(RelatedSalesforce): + """ + Related entity reference for SalesforceOrganization assets. + + Extends RelatedSalesforce with SalesforceOrganization-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SalesforceOrganization" so it serializes correctly + + source_id: Union[str, None, UnsetType] = UNSET + """Identifier of the organization in Salesforce.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SalesforceOrganization" + + +class RelatedSalesforceObject(RelatedSalesforce): + """ + Related entity reference for SalesforceObject assets. + + Extends RelatedSalesforce with SalesforceObject-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SalesforceObject" so it serializes correctly + + is_custom: Union[bool, None, UnsetType] = UNSET + """Whether this object is a custom object (true) or not (false).""" + + is_mergable: Union[bool, None, UnsetType] = UNSET + """Whether this object is mergable (true) or not (false).""" + + is_queryable: Union[bool, None, UnsetType] = UNSET + """Whether this object is queryable (true) or not (false).""" + + field_count: Union[int, None, UnsetType] = UNSET + """Number of fields in this object.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SalesforceObject" + + +class RelatedSalesforceField(RelatedSalesforce): + """ + Related entity reference for SalesforceField assets. + + Extends RelatedSalesforce with SalesforceField-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SalesforceField" so it serializes correctly + + data_type: Union[str, None, UnsetType] = UNSET + """Data type of values in this field.""" + + object_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the object in which this field exists.""" + + order: Union[int, None, UnsetType] = UNSET + """Order (position) of this field within the object.""" + + inline_help_text: Union[str, None, UnsetType] = UNSET + """Help text for this field.""" + + is_calculated: Union[bool, None, UnsetType] = UNSET + """Whether this field is calculated (true) or not (false).""" + + formula: Union[str, None, UnsetType] = UNSET + """Formula for this field, if it is a calculated field.""" + + is_case_sensitive: Union[bool, None, UnsetType] = UNSET + """Whether this field is case sensitive (true) or in-sensitive (false).""" + + is_encrypted: Union[bool, None, UnsetType] = UNSET + """Whether this field is encrypted (true) or not (false).""" + + max_length: Union[int, None, UnsetType] = UNSET + """Maximum length of this field.""" + + is_nullable: Union[bool, None, UnsetType] = UNSET + """Whether this field allows null values (true) or not (false).""" + + precision: Union[int, None, UnsetType] = UNSET + """Total number of digits allowed.""" + + numeric_scale: Union[float, None, UnsetType] = UNSET + """Number of digits allowed to the right of the decimal point.""" + + is_unique: Union[bool, None, UnsetType] = UNSET + """Whether this field must have unique values (true) or not (false).""" + + picklist_values: Union[List[str], None, UnsetType] = UNSET + """List of values from which a user can pick while adding a record.""" + + is_polymorphic_foreign_key: Union[bool, None, UnsetType] = UNSET + """Whether this field references a record of multiple objects (true) or not (false).""" + + default_value_formula: Union[str, None, UnsetType] = UNSET + """Formula for the default value for this field.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SalesforceField" + + +class RelatedSalesforceReport(RelatedSalesforce): + """ + Related entity reference for SalesforceReport assets. + + Extends RelatedSalesforce with SalesforceReport-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SalesforceReport" so it serializes correctly + + source_id: Union[str, None, UnsetType] = UNSET + """Identifier of the report in Salesforce.""" + + report_type: Union[Dict[str, str], None, UnsetType] = UNSET + """Type of report in Salesforce.""" + + detail_columns: Union[List[str], None, UnsetType] = UNSET + """List of column names on the report.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SalesforceReport" + + +class RelatedSalesforceDashboard(RelatedSalesforce): + """ + Related entity reference for SalesforceDashboard assets. + + Extends RelatedSalesforce with SalesforceDashboard-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SalesforceDashboard" so it serializes correctly + + source_id: Union[str, None, UnsetType] = UNSET + """Identifier of the dashboard in Salesforce.""" + + dashboard_type: Union[str, None, UnsetType] = UNSET + """Type of dashboard in Salesforce.""" + + report_count: Union[int, None, UnsetType] = UNSET + """Number of reports linked to the dashboard in Salesforce.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SalesforceDashboard" diff --git a/pyatlan_v9/model/assets/salesforce_report.py b/pyatlan_v9/model/assets/salesforce_report.py new file mode 100644 index 000000000..4942a72d9 --- /dev/null +++ b/pyatlan_v9/model/assets/salesforce_report.py @@ -0,0 +1,625 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SalesforceReport asset model with flattened inheritance. + +This module provides: +- SalesforceReport: Flat asset class (easy to use) +- SalesforceReportAttributes: Nested attributes struct (extends AssetAttributes) +- SalesforceReportNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .salesforce_related import ( + RelatedSalesforceDashboard, + RelatedSalesforceOrganization, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SalesforceReport(Asset): + """ + Instance of a Salesforce report in Atlan. + """ + + SOURCE_ID: ClassVar[Any] = None + REPORT_TYPE: ClassVar[Any] = None + DETAIL_COLUMNS: ClassVar[Any] = None + ORGANIZATION_QUALIFIED_NAME: ClassVar[Any] = None + API_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + ORGANIZATION: ClassVar[Any] = None + DASHBOARDS: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SalesforceReport" + + source_id: Union[str, None, UnsetType] = UNSET + """Identifier of the report in Salesforce.""" + + report_type: Union[Dict[str, str], None, UnsetType] = UNSET + """Type of report in Salesforce.""" + + detail_columns: Union[List[str], None, UnsetType] = UNSET + """List of column names on the report.""" + + organization_qualified_name: Union[str, None, UnsetType] = UNSET + """Fully-qualified name of the organization in Salesforce.""" + + api_name: Union[str, None, UnsetType] = UNSET + """Name of this asset in the Salesforce API.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + organization: Union[RelatedSalesforceOrganization, None, UnsetType] = UNSET + """Organization in which this report exists.""" + + dashboards: Union[List[RelatedSalesforceDashboard], None, UnsetType] = UNSET + """""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SalesforceReport" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _salesforce_report_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> SalesforceReport: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SalesforceReport instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _salesforce_report_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SalesforceReportAttributes(AssetAttributes): + """SalesforceReport-specific attributes for nested API format.""" + + source_id: Union[str, None, UnsetType] = UNSET + """Identifier of the report in Salesforce.""" + + report_type: Union[Dict[str, str], None, UnsetType] = UNSET + """Type of report in Salesforce.""" + + detail_columns: Union[List[str], None, UnsetType] = UNSET + """List of column names on the report.""" + + organization_qualified_name: Union[str, None, UnsetType] = UNSET + """Fully-qualified name of the organization in Salesforce.""" + + api_name: Union[str, None, UnsetType] = UNSET + """Name of this asset in the Salesforce API.""" + + +class SalesforceReportRelationshipAttributes(AssetRelationshipAttributes): + """SalesforceReport-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + organization: Union[RelatedSalesforceOrganization, None, UnsetType] = UNSET + """Organization in which this report exists.""" + + dashboards: Union[List[RelatedSalesforceDashboard], None, UnsetType] = UNSET + """""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SalesforceReportNested(AssetNested): + """SalesforceReport in nested API format for high-performance serialization.""" + + attributes: Union[SalesforceReportAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + SalesforceReportRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + SalesforceReportRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SalesforceReportRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SALESFORCE_REPORT_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "organization", + "dashboards", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_salesforce_report_attrs( + attrs: SalesforceReportAttributes, obj: SalesforceReport +) -> None: + """Populate SalesforceReport-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.source_id = obj.source_id + attrs.report_type = obj.report_type + attrs.detail_columns = obj.detail_columns + attrs.organization_qualified_name = obj.organization_qualified_name + attrs.api_name = obj.api_name + + +def _extract_salesforce_report_attrs(attrs: SalesforceReportAttributes) -> dict: + """Extract all SalesforceReport attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["source_id"] = attrs.source_id + result["report_type"] = attrs.report_type + result["detail_columns"] = attrs.detail_columns + result["organization_qualified_name"] = attrs.organization_qualified_name + result["api_name"] = attrs.api_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _salesforce_report_to_nested( + salesforce_report: SalesforceReport, +) -> SalesforceReportNested: + """Convert flat SalesforceReport to nested format.""" + attrs = SalesforceReportAttributes() + _populate_salesforce_report_attrs(attrs, salesforce_report) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + salesforce_report, + _SALESFORCE_REPORT_REL_FIELDS, + SalesforceReportRelationshipAttributes, + ) + return SalesforceReportNested( + guid=salesforce_report.guid, + type_name=salesforce_report.type_name, + status=salesforce_report.status, + version=salesforce_report.version, + create_time=salesforce_report.create_time, + update_time=salesforce_report.update_time, + created_by=salesforce_report.created_by, + updated_by=salesforce_report.updated_by, + classifications=salesforce_report.classifications, + classification_names=salesforce_report.classification_names, + meanings=salesforce_report.meanings, + labels=salesforce_report.labels, + business_attributes=salesforce_report.business_attributes, + custom_attributes=salesforce_report.custom_attributes, + pending_tasks=salesforce_report.pending_tasks, + proxy=salesforce_report.proxy, + is_incomplete=salesforce_report.is_incomplete, + provenance_type=salesforce_report.provenance_type, + home_id=salesforce_report.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _salesforce_report_from_nested(nested: SalesforceReportNested) -> SalesforceReport: + """Convert nested format to flat SalesforceReport.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else SalesforceReportAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SALESFORCE_REPORT_REL_FIELDS, + SalesforceReportRelationshipAttributes, + ) + return SalesforceReport( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_salesforce_report_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _salesforce_report_to_nested_bytes( + salesforce_report: SalesforceReport, serde: Serde +) -> bytes: + """Convert flat SalesforceReport to nested JSON bytes.""" + return serde.encode(_salesforce_report_to_nested(salesforce_report)) + + +def _salesforce_report_from_nested_bytes(data: bytes, serde: Serde) -> SalesforceReport: + """Convert nested JSON bytes to flat SalesforceReport.""" + nested = serde.decode(data, SalesforceReportNested) + return _salesforce_report_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +SalesforceReport.SOURCE_ID = KeywordField("sourceId", "sourceId") +SalesforceReport.REPORT_TYPE = KeywordField("reportType", "reportType") +SalesforceReport.DETAIL_COLUMNS = KeywordField("detailColumns", "detailColumns") +SalesforceReport.ORGANIZATION_QUALIFIED_NAME = KeywordField( + "organizationQualifiedName", "organizationQualifiedName" +) +SalesforceReport.API_NAME = KeywordField("apiName", "apiName") +SalesforceReport.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SalesforceReport.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +SalesforceReport.ANOMALO_CHECKS = RelationField("anomaloChecks") +SalesforceReport.APPLICATION = RelationField("application") +SalesforceReport.APPLICATION_FIELD = RelationField("applicationField") +SalesforceReport.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +SalesforceReport.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +SalesforceReport.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +SalesforceReport.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +SalesforceReport.METRICS = RelationField("metrics") +SalesforceReport.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SalesforceReport.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +SalesforceReport.MEANINGS = RelationField("meanings") +SalesforceReport.MC_MONITORS = RelationField("mcMonitors") +SalesforceReport.MC_INCIDENTS = RelationField("mcIncidents") +SalesforceReport.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SalesforceReport.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SalesforceReport.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SalesforceReport.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SalesforceReport.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +SalesforceReport.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +SalesforceReport.FILES = RelationField("files") +SalesforceReport.LINKS = RelationField("links") +SalesforceReport.README = RelationField("readme") +SalesforceReport.ORGANIZATION = RelationField("organization") +SalesforceReport.DASHBOARDS = RelationField("dashboards") +SalesforceReport.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +SalesforceReport.SODA_CHECKS = RelationField("sodaChecks") +SalesforceReport.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SalesforceReport.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/sap.py b/pyatlan_v9/model/assets/sap.py new file mode 100644 index 000000000..604c83b2b --- /dev/null +++ b/pyatlan_v9/model/assets/sap.py @@ -0,0 +1,592 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SAP asset model with flattened inheritance. + +This module provides: +- SAP: Flat asset class (easy to use) +- SAPAttributes: Nested attributes struct (extends AssetAttributes) +- SAPNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SAP(Asset): + """ + Base class for SAP assets. + """ + + SAP_TECHNICAL_NAME: ClassVar[Any] = None + SAP_LOGICAL_NAME: ClassVar[Any] = None + SAP_PACKAGE_NAME: ClassVar[Any] = None + SAP_COMPONENT_NAME: ClassVar[Any] = None + SAP_DATA_TYPE: ClassVar[Any] = None + SAP_FIELD_COUNT: ClassVar[Any] = None + SAP_FIELD_ORDER: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SAP" + + sap_technical_name: Union[str, None, UnsetType] = UNSET + """Technical identifier for SAP data objects, used for integration and internal reference.""" + + sap_logical_name: Union[str, None, UnsetType] = UNSET + """Logical, business-friendly identifier for SAP data objects, aligned with business terminology and concepts.""" + + sap_package_name: Union[str, None, UnsetType] = UNSET + """Name of the SAP package, representing a logical grouping of related SAP data objects.""" + + sap_component_name: Union[str, None, UnsetType] = UNSET + """Name of the SAP component, representing a specific functional area in SAP.""" + + sap_data_type: Union[str, None, UnsetType] = UNSET + """SAP-specific data types""" + + sap_field_count: Union[int, None, UnsetType] = UNSET + """Represents the total number of fields, columns, or child assets present in a given SAP asset.""" + + sap_field_order: Union[int, None, UnsetType] = UNSET + """Indicates the sequential position of a field, column, or child asset within its parent SAP asset, starting from 1.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SAP" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _sap_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> SAP: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SAP instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _sap_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SAPAttributes(AssetAttributes): + """SAP-specific attributes for nested API format.""" + + sap_technical_name: Union[str, None, UnsetType] = UNSET + """Technical identifier for SAP data objects, used for integration and internal reference.""" + + sap_logical_name: Union[str, None, UnsetType] = UNSET + """Logical, business-friendly identifier for SAP data objects, aligned with business terminology and concepts.""" + + sap_package_name: Union[str, None, UnsetType] = UNSET + """Name of the SAP package, representing a logical grouping of related SAP data objects.""" + + sap_component_name: Union[str, None, UnsetType] = UNSET + """Name of the SAP component, representing a specific functional area in SAP.""" + + sap_data_type: Union[str, None, UnsetType] = UNSET + """SAP-specific data types""" + + sap_field_count: Union[int, None, UnsetType] = UNSET + """Represents the total number of fields, columns, or child assets present in a given SAP asset.""" + + sap_field_order: Union[int, None, UnsetType] = UNSET + """Indicates the sequential position of a field, column, or child asset within its parent SAP asset, starting from 1.""" + + +class SAPRelationshipAttributes(AssetRelationshipAttributes): + """SAP-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SAPNested(AssetNested): + """SAP in nested API format for high-performance serialization.""" + + attributes: Union[SAPAttributes, UnsetType] = UNSET + relationship_attributes: Union[SAPRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[SAPRelationshipAttributes, UnsetType] = UNSET + remove_relationship_attributes: Union[SAPRelationshipAttributes, UnsetType] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SAP_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_sap_attrs(attrs: SAPAttributes, obj: SAP) -> None: + """Populate SAP-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.sap_technical_name = obj.sap_technical_name + attrs.sap_logical_name = obj.sap_logical_name + attrs.sap_package_name = obj.sap_package_name + attrs.sap_component_name = obj.sap_component_name + attrs.sap_data_type = obj.sap_data_type + attrs.sap_field_count = obj.sap_field_count + attrs.sap_field_order = obj.sap_field_order + + +def _extract_sap_attrs(attrs: SAPAttributes) -> dict: + """Extract all SAP attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["sap_technical_name"] = attrs.sap_technical_name + result["sap_logical_name"] = attrs.sap_logical_name + result["sap_package_name"] = attrs.sap_package_name + result["sap_component_name"] = attrs.sap_component_name + result["sap_data_type"] = attrs.sap_data_type + result["sap_field_count"] = attrs.sap_field_count + result["sap_field_order"] = attrs.sap_field_order + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _sap_to_nested(sap: SAP) -> SAPNested: + """Convert flat SAP to nested format.""" + attrs = SAPAttributes() + _populate_sap_attrs(attrs, sap) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + sap, _SAP_REL_FIELDS, SAPRelationshipAttributes + ) + return SAPNested( + guid=sap.guid, + type_name=sap.type_name, + status=sap.status, + version=sap.version, + create_time=sap.create_time, + update_time=sap.update_time, + created_by=sap.created_by, + updated_by=sap.updated_by, + classifications=sap.classifications, + classification_names=sap.classification_names, + meanings=sap.meanings, + labels=sap.labels, + business_attributes=sap.business_attributes, + custom_attributes=sap.custom_attributes, + pending_tasks=sap.pending_tasks, + proxy=sap.proxy, + is_incomplete=sap.is_incomplete, + provenance_type=sap.provenance_type, + home_id=sap.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _sap_from_nested(nested: SAPNested) -> SAP: + """Convert nested format to flat SAP.""" + attrs = nested.attributes if nested.attributes is not UNSET else SAPAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SAP_REL_FIELDS, + SAPRelationshipAttributes, + ) + return SAP( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_sap_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _sap_to_nested_bytes(sap: SAP, serde: Serde) -> bytes: + """Convert flat SAP to nested JSON bytes.""" + return serde.encode(_sap_to_nested(sap)) + + +def _sap_from_nested_bytes(data: bytes, serde: Serde) -> SAP: + """Convert nested JSON bytes to flat SAP.""" + nested = serde.decode(data, SAPNested) + return _sap_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +SAP.SAP_TECHNICAL_NAME = KeywordField("sapTechnicalName", "sapTechnicalName") +SAP.SAP_LOGICAL_NAME = KeywordField("sapLogicalName", "sapLogicalName") +SAP.SAP_PACKAGE_NAME = KeywordField("sapPackageName", "sapPackageName") +SAP.SAP_COMPONENT_NAME = KeywordField("sapComponentName", "sapComponentName") +SAP.SAP_DATA_TYPE = KeywordField("sapDataType", "sapDataType") +SAP.SAP_FIELD_COUNT = NumericField("sapFieldCount", "sapFieldCount") +SAP.SAP_FIELD_ORDER = NumericField("sapFieldOrder", "sapFieldOrder") +SAP.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SAP.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +SAP.ANOMALO_CHECKS = RelationField("anomaloChecks") +SAP.APPLICATION = RelationField("application") +SAP.APPLICATION_FIELD = RelationField("applicationField") +SAP.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +SAP.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +SAP.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +SAP.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +SAP.METRICS = RelationField("metrics") +SAP.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SAP.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +SAP.MEANINGS = RelationField("meanings") +SAP.MC_MONITORS = RelationField("mcMonitors") +SAP.MC_INCIDENTS = RelationField("mcIncidents") +SAP.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SAP.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SAP.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SAP.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SAP.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +SAP.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +SAP.FILES = RelationField("files") +SAP.LINKS = RelationField("links") +SAP.README = RelationField("readme") +SAP.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +SAP.SODA_CHECKS = RelationField("sodaChecks") +SAP.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SAP.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/sap_erp_abap_program.py b/pyatlan_v9/model/assets/sap_erp_abap_program.py new file mode 100644 index 000000000..5cffe50ba --- /dev/null +++ b/pyatlan_v9/model/assets/sap_erp_abap_program.py @@ -0,0 +1,675 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SapErpAbapProgram asset model with flattened inheritance. + +This module provides: +- SapErpAbapProgram: Flat asset class (easy to use) +- SapErpAbapProgramAttributes: Nested attributes struct (extends AssetAttributes) +- SapErpAbapProgramNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .sap_related import ( + RelatedSapErpComponent, + RelatedSapErpFunctionModule, + RelatedSapErpTransactionCode, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SapErpAbapProgram(Asset): + """ + Instance of a SAP ABAP Program in Atlan. + """ + + SAP_ERP_ABAP_PROGRAM_TYPE: ClassVar[Any] = None + SAP_TECHNICAL_NAME: ClassVar[Any] = None + SAP_LOGICAL_NAME: ClassVar[Any] = None + SAP_PACKAGE_NAME: ClassVar[Any] = None + SAP_COMPONENT_NAME: ClassVar[Any] = None + SAP_DATA_TYPE: ClassVar[Any] = None + SAP_FIELD_COUNT: ClassVar[Any] = None + SAP_FIELD_ORDER: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SAP_ERP_TRANSACTION_CODES: ClassVar[Any] = None + SAP_ERP_FUNCTION_MODULES: ClassVar[Any] = None + SAP_ERP_COMPONENT: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SapErpAbapProgram" + + sap_erp_abap_program_type: Union[str, None, UnsetType] = UNSET + """Specifies the type of ABAP program in SAP ERP (e.g., Report, Module Pool, Function Group).""" + + sap_technical_name: Union[str, None, UnsetType] = UNSET + """Technical identifier for SAP data objects, used for integration and internal reference.""" + + sap_logical_name: Union[str, None, UnsetType] = UNSET + """Logical, business-friendly identifier for SAP data objects, aligned with business terminology and concepts.""" + + sap_package_name: Union[str, None, UnsetType] = UNSET + """Name of the SAP package, representing a logical grouping of related SAP data objects.""" + + sap_component_name: Union[str, None, UnsetType] = UNSET + """Name of the SAP component, representing a specific functional area in SAP.""" + + sap_data_type: Union[str, None, UnsetType] = UNSET + """SAP-specific data types""" + + sap_field_count: Union[int, None, UnsetType] = UNSET + """Represents the total number of fields, columns, or child assets present in a given SAP asset.""" + + sap_field_order: Union[int, None, UnsetType] = UNSET + """Indicates the sequential position of a field, column, or child asset within its parent SAP asset, starting from 1.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + sap_erp_transaction_codes: Union[ + List[RelatedSapErpTransactionCode], None, UnsetType + ] = UNSET + """SAP ERP ABAP Program associated with this SAP ERP Transaction Code.""" + + sap_erp_function_modules: Union[ + List[RelatedSapErpFunctionModule], None, UnsetType + ] = UNSET + """SAP ERP ABAP Program associated with this SAP ERP Function Modules.""" + + sap_erp_component: Union[RelatedSapErpComponent, None, UnsetType] = UNSET + """SAP ERP ABAP Program that are associated with this SAP ERP Component.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SapErpAbapProgram" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _sap_erp_abap_program_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> SapErpAbapProgram: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SapErpAbapProgram instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _sap_erp_abap_program_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SapErpAbapProgramAttributes(AssetAttributes): + """SapErpAbapProgram-specific attributes for nested API format.""" + + sap_erp_abap_program_type: Union[str, None, UnsetType] = UNSET + """Specifies the type of ABAP program in SAP ERP (e.g., Report, Module Pool, Function Group).""" + + sap_technical_name: Union[str, None, UnsetType] = UNSET + """Technical identifier for SAP data objects, used for integration and internal reference.""" + + sap_logical_name: Union[str, None, UnsetType] = UNSET + """Logical, business-friendly identifier for SAP data objects, aligned with business terminology and concepts.""" + + sap_package_name: Union[str, None, UnsetType] = UNSET + """Name of the SAP package, representing a logical grouping of related SAP data objects.""" + + sap_component_name: Union[str, None, UnsetType] = UNSET + """Name of the SAP component, representing a specific functional area in SAP.""" + + sap_data_type: Union[str, None, UnsetType] = UNSET + """SAP-specific data types""" + + sap_field_count: Union[int, None, UnsetType] = UNSET + """Represents the total number of fields, columns, or child assets present in a given SAP asset.""" + + sap_field_order: Union[int, None, UnsetType] = UNSET + """Indicates the sequential position of a field, column, or child asset within its parent SAP asset, starting from 1.""" + + +class SapErpAbapProgramRelationshipAttributes(AssetRelationshipAttributes): + """SapErpAbapProgram-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + sap_erp_transaction_codes: Union[ + List[RelatedSapErpTransactionCode], None, UnsetType + ] = UNSET + """SAP ERP ABAP Program associated with this SAP ERP Transaction Code.""" + + sap_erp_function_modules: Union[ + List[RelatedSapErpFunctionModule], None, UnsetType + ] = UNSET + """SAP ERP ABAP Program associated with this SAP ERP Function Modules.""" + + sap_erp_component: Union[RelatedSapErpComponent, None, UnsetType] = UNSET + """SAP ERP ABAP Program that are associated with this SAP ERP Component.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SapErpAbapProgramNested(AssetNested): + """SapErpAbapProgram in nested API format for high-performance serialization.""" + + attributes: Union[SapErpAbapProgramAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + SapErpAbapProgramRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + SapErpAbapProgramRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SapErpAbapProgramRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SAP_ERP_ABAP_PROGRAM_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "sap_erp_transaction_codes", + "sap_erp_function_modules", + "sap_erp_component", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_sap_erp_abap_program_attrs( + attrs: SapErpAbapProgramAttributes, obj: SapErpAbapProgram +) -> None: + """Populate SapErpAbapProgram-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.sap_erp_abap_program_type = obj.sap_erp_abap_program_type + attrs.sap_technical_name = obj.sap_technical_name + attrs.sap_logical_name = obj.sap_logical_name + attrs.sap_package_name = obj.sap_package_name + attrs.sap_component_name = obj.sap_component_name + attrs.sap_data_type = obj.sap_data_type + attrs.sap_field_count = obj.sap_field_count + attrs.sap_field_order = obj.sap_field_order + + +def _extract_sap_erp_abap_program_attrs(attrs: SapErpAbapProgramAttributes) -> dict: + """Extract all SapErpAbapProgram attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["sap_erp_abap_program_type"] = attrs.sap_erp_abap_program_type + result["sap_technical_name"] = attrs.sap_technical_name + result["sap_logical_name"] = attrs.sap_logical_name + result["sap_package_name"] = attrs.sap_package_name + result["sap_component_name"] = attrs.sap_component_name + result["sap_data_type"] = attrs.sap_data_type + result["sap_field_count"] = attrs.sap_field_count + result["sap_field_order"] = attrs.sap_field_order + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _sap_erp_abap_program_to_nested( + sap_erp_abap_program: SapErpAbapProgram, +) -> SapErpAbapProgramNested: + """Convert flat SapErpAbapProgram to nested format.""" + attrs = SapErpAbapProgramAttributes() + _populate_sap_erp_abap_program_attrs(attrs, sap_erp_abap_program) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + sap_erp_abap_program, + _SAP_ERP_ABAP_PROGRAM_REL_FIELDS, + SapErpAbapProgramRelationshipAttributes, + ) + return SapErpAbapProgramNested( + guid=sap_erp_abap_program.guid, + type_name=sap_erp_abap_program.type_name, + status=sap_erp_abap_program.status, + version=sap_erp_abap_program.version, + create_time=sap_erp_abap_program.create_time, + update_time=sap_erp_abap_program.update_time, + created_by=sap_erp_abap_program.created_by, + updated_by=sap_erp_abap_program.updated_by, + classifications=sap_erp_abap_program.classifications, + classification_names=sap_erp_abap_program.classification_names, + meanings=sap_erp_abap_program.meanings, + labels=sap_erp_abap_program.labels, + business_attributes=sap_erp_abap_program.business_attributes, + custom_attributes=sap_erp_abap_program.custom_attributes, + pending_tasks=sap_erp_abap_program.pending_tasks, + proxy=sap_erp_abap_program.proxy, + is_incomplete=sap_erp_abap_program.is_incomplete, + provenance_type=sap_erp_abap_program.provenance_type, + home_id=sap_erp_abap_program.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _sap_erp_abap_program_from_nested( + nested: SapErpAbapProgramNested, +) -> SapErpAbapProgram: + """Convert nested format to flat SapErpAbapProgram.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else SapErpAbapProgramAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SAP_ERP_ABAP_PROGRAM_REL_FIELDS, + SapErpAbapProgramRelationshipAttributes, + ) + return SapErpAbapProgram( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_sap_erp_abap_program_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _sap_erp_abap_program_to_nested_bytes( + sap_erp_abap_program: SapErpAbapProgram, serde: Serde +) -> bytes: + """Convert flat SapErpAbapProgram to nested JSON bytes.""" + return serde.encode(_sap_erp_abap_program_to_nested(sap_erp_abap_program)) + + +def _sap_erp_abap_program_from_nested_bytes( + data: bytes, serde: Serde +) -> SapErpAbapProgram: + """Convert nested JSON bytes to flat SapErpAbapProgram.""" + nested = serde.decode(data, SapErpAbapProgramNested) + return _sap_erp_abap_program_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +SapErpAbapProgram.SAP_ERP_ABAP_PROGRAM_TYPE = KeywordField( + "sapErpAbapProgramType", "sapErpAbapProgramType" +) +SapErpAbapProgram.SAP_TECHNICAL_NAME = KeywordField( + "sapTechnicalName", "sapTechnicalName" +) +SapErpAbapProgram.SAP_LOGICAL_NAME = KeywordField("sapLogicalName", "sapLogicalName") +SapErpAbapProgram.SAP_PACKAGE_NAME = KeywordField("sapPackageName", "sapPackageName") +SapErpAbapProgram.SAP_COMPONENT_NAME = KeywordField( + "sapComponentName", "sapComponentName" +) +SapErpAbapProgram.SAP_DATA_TYPE = KeywordField("sapDataType", "sapDataType") +SapErpAbapProgram.SAP_FIELD_COUNT = NumericField("sapFieldCount", "sapFieldCount") +SapErpAbapProgram.SAP_FIELD_ORDER = NumericField("sapFieldOrder", "sapFieldOrder") +SapErpAbapProgram.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SapErpAbapProgram.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +SapErpAbapProgram.ANOMALO_CHECKS = RelationField("anomaloChecks") +SapErpAbapProgram.APPLICATION = RelationField("application") +SapErpAbapProgram.APPLICATION_FIELD = RelationField("applicationField") +SapErpAbapProgram.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +SapErpAbapProgram.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +SapErpAbapProgram.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +SapErpAbapProgram.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +SapErpAbapProgram.METRICS = RelationField("metrics") +SapErpAbapProgram.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SapErpAbapProgram.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +SapErpAbapProgram.MEANINGS = RelationField("meanings") +SapErpAbapProgram.MC_MONITORS = RelationField("mcMonitors") +SapErpAbapProgram.MC_INCIDENTS = RelationField("mcIncidents") +SapErpAbapProgram.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SapErpAbapProgram.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SapErpAbapProgram.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SapErpAbapProgram.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SapErpAbapProgram.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +SapErpAbapProgram.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +SapErpAbapProgram.FILES = RelationField("files") +SapErpAbapProgram.LINKS = RelationField("links") +SapErpAbapProgram.README = RelationField("readme") +SapErpAbapProgram.SAP_ERP_TRANSACTION_CODES = RelationField("sapErpTransactionCodes") +SapErpAbapProgram.SAP_ERP_FUNCTION_MODULES = RelationField("sapErpFunctionModules") +SapErpAbapProgram.SAP_ERP_COMPONENT = RelationField("sapErpComponent") +SapErpAbapProgram.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +SapErpAbapProgram.SODA_CHECKS = RelationField("sodaChecks") +SapErpAbapProgram.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SapErpAbapProgram.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/sap_erp_cds_view.py b/pyatlan_v9/model/assets/sap_erp_cds_view.py new file mode 100644 index 000000000..bf060f9c5 --- /dev/null +++ b/pyatlan_v9/model/assets/sap_erp_cds_view.py @@ -0,0 +1,648 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SapErpCdsView asset model with flattened inheritance. + +This module provides: +- SapErpCdsView: Flat asset class (easy to use) +- SapErpCdsViewAttributes: Nested attributes struct (extends AssetAttributes) +- SapErpCdsViewNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .sap_related import RelatedSapErpColumn, RelatedSapErpComponent + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SapErpCdsView(Asset): + """ + Instance of a SAP CDS View in Atlan. + """ + + SAP_TECHNICAL_NAME: ClassVar[Any] = None + SAP_SOURCE_NAME: ClassVar[Any] = None + SAP_SOURCE_TYPE: ClassVar[Any] = None + SAP_LOGICAL_NAME: ClassVar[Any] = None + SAP_PACKAGE_NAME: ClassVar[Any] = None + SAP_COMPONENT_NAME: ClassVar[Any] = None + SAP_DATA_TYPE: ClassVar[Any] = None + SAP_FIELD_COUNT: ClassVar[Any] = None + SAP_FIELD_ORDER: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SAP_ERP_COMPONENT: ClassVar[Any] = None + SAP_ERP_COLUMNS: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SapErpCdsView" + + sap_technical_name: Union[str, None, UnsetType] = UNSET + """Technical identifier for SAP data objects, used for integration and internal reference.""" + + sap_source_name: Union[str, None, UnsetType] = UNSET + """The source name of the SAP ERP CDS View Definition.""" + + sap_source_type: Union[str, None, UnsetType] = UNSET + """The source type of the SAP ERP CDS View Definition.""" + + sap_logical_name: Union[str, None, UnsetType] = UNSET + """Logical, business-friendly identifier for SAP data objects, aligned with business terminology and concepts.""" + + sap_package_name: Union[str, None, UnsetType] = UNSET + """Name of the SAP package, representing a logical grouping of related SAP data objects.""" + + sap_component_name: Union[str, None, UnsetType] = UNSET + """Name of the SAP component, representing a specific functional area in SAP.""" + + sap_data_type: Union[str, None, UnsetType] = UNSET + """SAP-specific data types""" + + sap_field_count: Union[int, None, UnsetType] = UNSET + """Represents the total number of fields, columns, or child assets present in a given SAP asset.""" + + sap_field_order: Union[int, None, UnsetType] = UNSET + """Indicates the sequential position of a field, column, or child asset within its parent SAP asset, starting from 1.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + sap_erp_component: Union[RelatedSapErpComponent, None, UnsetType] = UNSET + """SAP ERP CDS Views that are associated with this SAP ERP Component.""" + + sap_erp_columns: Union[List[RelatedSapErpColumn], None, UnsetType] = UNSET + """SAP ERP Columns that exist within this CDS view.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SapErpCdsView" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _sap_erp_cds_view_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> SapErpCdsView: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SapErpCdsView instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _sap_erp_cds_view_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SapErpCdsViewAttributes(AssetAttributes): + """SapErpCdsView-specific attributes for nested API format.""" + + sap_technical_name: Union[str, None, UnsetType] = UNSET + """Technical identifier for SAP data objects, used for integration and internal reference.""" + + sap_source_name: Union[str, None, UnsetType] = UNSET + """The source name of the SAP ERP CDS View Definition.""" + + sap_source_type: Union[str, None, UnsetType] = UNSET + """The source type of the SAP ERP CDS View Definition.""" + + sap_logical_name: Union[str, None, UnsetType] = UNSET + """Logical, business-friendly identifier for SAP data objects, aligned with business terminology and concepts.""" + + sap_package_name: Union[str, None, UnsetType] = UNSET + """Name of the SAP package, representing a logical grouping of related SAP data objects.""" + + sap_component_name: Union[str, None, UnsetType] = UNSET + """Name of the SAP component, representing a specific functional area in SAP.""" + + sap_data_type: Union[str, None, UnsetType] = UNSET + """SAP-specific data types""" + + sap_field_count: Union[int, None, UnsetType] = UNSET + """Represents the total number of fields, columns, or child assets present in a given SAP asset.""" + + sap_field_order: Union[int, None, UnsetType] = UNSET + """Indicates the sequential position of a field, column, or child asset within its parent SAP asset, starting from 1.""" + + +class SapErpCdsViewRelationshipAttributes(AssetRelationshipAttributes): + """SapErpCdsView-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + sap_erp_component: Union[RelatedSapErpComponent, None, UnsetType] = UNSET + """SAP ERP CDS Views that are associated with this SAP ERP Component.""" + + sap_erp_columns: Union[List[RelatedSapErpColumn], None, UnsetType] = UNSET + """SAP ERP Columns that exist within this CDS view.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SapErpCdsViewNested(AssetNested): + """SapErpCdsView in nested API format for high-performance serialization.""" + + attributes: Union[SapErpCdsViewAttributes, UnsetType] = UNSET + relationship_attributes: Union[SapErpCdsViewRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + SapErpCdsViewRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SapErpCdsViewRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SAP_ERP_CDS_VIEW_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "sap_erp_component", + "sap_erp_columns", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_sap_erp_cds_view_attrs( + attrs: SapErpCdsViewAttributes, obj: SapErpCdsView +) -> None: + """Populate SapErpCdsView-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.sap_technical_name = obj.sap_technical_name + attrs.sap_source_name = obj.sap_source_name + attrs.sap_source_type = obj.sap_source_type + attrs.sap_logical_name = obj.sap_logical_name + attrs.sap_package_name = obj.sap_package_name + attrs.sap_component_name = obj.sap_component_name + attrs.sap_data_type = obj.sap_data_type + attrs.sap_field_count = obj.sap_field_count + attrs.sap_field_order = obj.sap_field_order + + +def _extract_sap_erp_cds_view_attrs(attrs: SapErpCdsViewAttributes) -> dict: + """Extract all SapErpCdsView attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["sap_technical_name"] = attrs.sap_technical_name + result["sap_source_name"] = attrs.sap_source_name + result["sap_source_type"] = attrs.sap_source_type + result["sap_logical_name"] = attrs.sap_logical_name + result["sap_package_name"] = attrs.sap_package_name + result["sap_component_name"] = attrs.sap_component_name + result["sap_data_type"] = attrs.sap_data_type + result["sap_field_count"] = attrs.sap_field_count + result["sap_field_order"] = attrs.sap_field_order + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _sap_erp_cds_view_to_nested(sap_erp_cds_view: SapErpCdsView) -> SapErpCdsViewNested: + """Convert flat SapErpCdsView to nested format.""" + attrs = SapErpCdsViewAttributes() + _populate_sap_erp_cds_view_attrs(attrs, sap_erp_cds_view) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + sap_erp_cds_view, + _SAP_ERP_CDS_VIEW_REL_FIELDS, + SapErpCdsViewRelationshipAttributes, + ) + return SapErpCdsViewNested( + guid=sap_erp_cds_view.guid, + type_name=sap_erp_cds_view.type_name, + status=sap_erp_cds_view.status, + version=sap_erp_cds_view.version, + create_time=sap_erp_cds_view.create_time, + update_time=sap_erp_cds_view.update_time, + created_by=sap_erp_cds_view.created_by, + updated_by=sap_erp_cds_view.updated_by, + classifications=sap_erp_cds_view.classifications, + classification_names=sap_erp_cds_view.classification_names, + meanings=sap_erp_cds_view.meanings, + labels=sap_erp_cds_view.labels, + business_attributes=sap_erp_cds_view.business_attributes, + custom_attributes=sap_erp_cds_view.custom_attributes, + pending_tasks=sap_erp_cds_view.pending_tasks, + proxy=sap_erp_cds_view.proxy, + is_incomplete=sap_erp_cds_view.is_incomplete, + provenance_type=sap_erp_cds_view.provenance_type, + home_id=sap_erp_cds_view.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _sap_erp_cds_view_from_nested(nested: SapErpCdsViewNested) -> SapErpCdsView: + """Convert nested format to flat SapErpCdsView.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else SapErpCdsViewAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SAP_ERP_CDS_VIEW_REL_FIELDS, + SapErpCdsViewRelationshipAttributes, + ) + return SapErpCdsView( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_sap_erp_cds_view_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _sap_erp_cds_view_to_nested_bytes( + sap_erp_cds_view: SapErpCdsView, serde: Serde +) -> bytes: + """Convert flat SapErpCdsView to nested JSON bytes.""" + return serde.encode(_sap_erp_cds_view_to_nested(sap_erp_cds_view)) + + +def _sap_erp_cds_view_from_nested_bytes(data: bytes, serde: Serde) -> SapErpCdsView: + """Convert nested JSON bytes to flat SapErpCdsView.""" + nested = serde.decode(data, SapErpCdsViewNested) + return _sap_erp_cds_view_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +SapErpCdsView.SAP_TECHNICAL_NAME = KeywordField("sapTechnicalName", "sapTechnicalName") +SapErpCdsView.SAP_SOURCE_NAME = KeywordField("sapSourceName", "sapSourceName") +SapErpCdsView.SAP_SOURCE_TYPE = KeywordField("sapSourceType", "sapSourceType") +SapErpCdsView.SAP_LOGICAL_NAME = KeywordField("sapLogicalName", "sapLogicalName") +SapErpCdsView.SAP_PACKAGE_NAME = KeywordField("sapPackageName", "sapPackageName") +SapErpCdsView.SAP_COMPONENT_NAME = KeywordField("sapComponentName", "sapComponentName") +SapErpCdsView.SAP_DATA_TYPE = KeywordField("sapDataType", "sapDataType") +SapErpCdsView.SAP_FIELD_COUNT = NumericField("sapFieldCount", "sapFieldCount") +SapErpCdsView.SAP_FIELD_ORDER = NumericField("sapFieldOrder", "sapFieldOrder") +SapErpCdsView.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SapErpCdsView.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +SapErpCdsView.ANOMALO_CHECKS = RelationField("anomaloChecks") +SapErpCdsView.APPLICATION = RelationField("application") +SapErpCdsView.APPLICATION_FIELD = RelationField("applicationField") +SapErpCdsView.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +SapErpCdsView.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +SapErpCdsView.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +SapErpCdsView.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +SapErpCdsView.METRICS = RelationField("metrics") +SapErpCdsView.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SapErpCdsView.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +SapErpCdsView.MEANINGS = RelationField("meanings") +SapErpCdsView.MC_MONITORS = RelationField("mcMonitors") +SapErpCdsView.MC_INCIDENTS = RelationField("mcIncidents") +SapErpCdsView.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SapErpCdsView.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SapErpCdsView.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SapErpCdsView.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SapErpCdsView.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +SapErpCdsView.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +SapErpCdsView.FILES = RelationField("files") +SapErpCdsView.LINKS = RelationField("links") +SapErpCdsView.README = RelationField("readme") +SapErpCdsView.SAP_ERP_COMPONENT = RelationField("sapErpComponent") +SapErpCdsView.SAP_ERP_COLUMNS = RelationField("sapErpColumns") +SapErpCdsView.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +SapErpCdsView.SODA_CHECKS = RelationField("sodaChecks") +SapErpCdsView.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SapErpCdsView.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/sap_erp_column.py b/pyatlan_v9/model/assets/sap_erp_column.py new file mode 100644 index 000000000..2bd0c7041 --- /dev/null +++ b/pyatlan_v9/model/assets/sap_erp_column.py @@ -0,0 +1,1095 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SapErpColumn asset model with flattened inheritance. + +This module provides: +- SapErpColumn: Flat asset class (easy to use) +- SapErpColumnAttributes: Nested attributes struct (extends AssetAttributes) +- SapErpColumnNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .snowflake_related import RelatedSnowflakeSemanticLogicalTable +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .sap_related import RelatedSapErpCdsView, RelatedSapErpTable, RelatedSapErpView + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SapErpColumn(Asset): + """ + Instance of a SAP Column in Atlan. + """ + + SAP_DATA_ELEMENT: ClassVar[Any] = None + SAP_LOGICAL_DATA_TYPE: ClassVar[Any] = None + SAP_LENGTH: ClassVar[Any] = None + SAP_DECIMALS: ClassVar[Any] = None + SAP_IS_PRIMARY: ClassVar[Any] = None + SAP_IS_FOREIGN: ClassVar[Any] = None + SAP_IS_MANDATORY: ClassVar[Any] = None + SAP_ERP_TABLE_NAME: ClassVar[Any] = None + SAP_ERP_TABLE_QUALIFIED_NAME: ClassVar[Any] = None + SAP_ERP_VIEW_NAME: ClassVar[Any] = None + SAP_ERP_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + SAP_ERP_CDS_VIEW_NAME: ClassVar[Any] = None + SAP_ERP_CDS_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + SAP_CHECK_TABLE_NAME: ClassVar[Any] = None + SAP_CHECK_TABLE_QUALIFIED_NAME: ClassVar[Any] = None + SAP_TECHNICAL_NAME: ClassVar[Any] = None + SAP_LOGICAL_NAME: ClassVar[Any] = None + SAP_PACKAGE_NAME: ClassVar[Any] = None + SAP_COMPONENT_NAME: ClassVar[Any] = None + SAP_DATA_TYPE: ClassVar[Any] = None + SAP_FIELD_COUNT: ClassVar[Any] = None + SAP_FIELD_ORDER: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SAP_ERP_TABLE: ClassVar[Any] = None + SAP_ERP_VIEW: ClassVar[Any] = None + SAP_ERP_CDS_VIEW: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SapErpColumn" + + sap_data_element: Union[str, None, UnsetType] = UNSET + """Represents the SAP ERP data element, providing semantic information about the column.""" + + sap_logical_data_type: Union[str, None, UnsetType] = UNSET + """Specifies the logical data type of values in this SAP ERP column""" + + sap_length: Union[str, None, UnsetType] = UNSET + """Indicates the maximum length of the values that the SAP ERP column can store.""" + + sap_decimals: Union[str, None, UnsetType] = UNSET + """Defines the number of decimal places allowed for numeric values in the SAP ERP column.""" + + sap_is_primary: Union[bool, None, UnsetType] = UNSET + """When true, this column is the primary key for the SAP ERP table or view.""" + + sap_is_foreign: Union[bool, None, UnsetType] = UNSET + """When true, this column is the foreign key for the SAP ERP table or view.""" + + sap_is_mandatory: Union[bool, None, UnsetType] = UNSET + """When true, the values in this column can be null.""" + + sap_erp_table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the SAP ERP table in which this column asset exists.""" + + sap_erp_table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the SAP ERP table in which this SQL asset exists.""" + + sap_erp_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the SAP ERP view in which this column asset exists.""" + + sap_erp_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the SAP ERP view in which this column asset exists.""" + + sap_erp_cds_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the SAP ERP CDS view in which this column asset exists.""" + + sap_erp_cds_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the SAP ERP CDS view in which this column asset exists.""" + + sap_check_table_name: Union[str, None, UnsetType] = UNSET + """Defines the SAP ERP table name used as a foreign key reference to validate permissible values for this column.""" + + sap_check_table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the SAP ERP Table used as a foreign key reference to validate permissible values for this column.""" + + sap_technical_name: Union[str, None, UnsetType] = UNSET + """Technical identifier for SAP data objects, used for integration and internal reference.""" + + sap_logical_name: Union[str, None, UnsetType] = UNSET + """Logical, business-friendly identifier for SAP data objects, aligned with business terminology and concepts.""" + + sap_package_name: Union[str, None, UnsetType] = UNSET + """Name of the SAP package, representing a logical grouping of related SAP data objects.""" + + sap_component_name: Union[str, None, UnsetType] = UNSET + """Name of the SAP component, representing a specific functional area in SAP.""" + + sap_data_type: Union[str, None, UnsetType] = UNSET + """SAP-specific data types""" + + sap_field_count: Union[int, None, UnsetType] = UNSET + """Represents the total number of fields, columns, or child assets present in a given SAP asset.""" + + sap_field_order: Union[int, None, UnsetType] = UNSET + """Indicates the sequential position of a field, column, or child asset within its parent SAP asset, starting from 1.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + sap_erp_table: Union[RelatedSapErpTable, None, UnsetType] = UNSET + """SAP ERP table in which this column exists.""" + + sap_erp_view: Union[RelatedSapErpView, None, UnsetType] = UNSET + """SAP ERP View in which this column exists.""" + + sap_erp_cds_view: Union[RelatedSapErpCdsView, None, UnsetType] = UNSET + """SAP ERP CDS View in which this column exists.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SapErpColumn" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _sap_erp_column_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> SapErpColumn: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SapErpColumn instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _sap_erp_column_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SapErpColumnAttributes(AssetAttributes): + """SapErpColumn-specific attributes for nested API format.""" + + sap_data_element: Union[str, None, UnsetType] = UNSET + """Represents the SAP ERP data element, providing semantic information about the column.""" + + sap_logical_data_type: Union[str, None, UnsetType] = UNSET + """Specifies the logical data type of values in this SAP ERP column""" + + sap_length: Union[str, None, UnsetType] = UNSET + """Indicates the maximum length of the values that the SAP ERP column can store.""" + + sap_decimals: Union[str, None, UnsetType] = UNSET + """Defines the number of decimal places allowed for numeric values in the SAP ERP column.""" + + sap_is_primary: Union[bool, None, UnsetType] = UNSET + """When true, this column is the primary key for the SAP ERP table or view.""" + + sap_is_foreign: Union[bool, None, UnsetType] = UNSET + """When true, this column is the foreign key for the SAP ERP table or view.""" + + sap_is_mandatory: Union[bool, None, UnsetType] = UNSET + """When true, the values in this column can be null.""" + + sap_erp_table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the SAP ERP table in which this column asset exists.""" + + sap_erp_table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the SAP ERP table in which this SQL asset exists.""" + + sap_erp_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the SAP ERP view in which this column asset exists.""" + + sap_erp_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the SAP ERP view in which this column asset exists.""" + + sap_erp_cds_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the SAP ERP CDS view in which this column asset exists.""" + + sap_erp_cds_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the SAP ERP CDS view in which this column asset exists.""" + + sap_check_table_name: Union[str, None, UnsetType] = UNSET + """Defines the SAP ERP table name used as a foreign key reference to validate permissible values for this column.""" + + sap_check_table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the SAP ERP Table used as a foreign key reference to validate permissible values for this column.""" + + sap_technical_name: Union[str, None, UnsetType] = UNSET + """Technical identifier for SAP data objects, used for integration and internal reference.""" + + sap_logical_name: Union[str, None, UnsetType] = UNSET + """Logical, business-friendly identifier for SAP data objects, aligned with business terminology and concepts.""" + + sap_package_name: Union[str, None, UnsetType] = UNSET + """Name of the SAP package, representing a logical grouping of related SAP data objects.""" + + sap_component_name: Union[str, None, UnsetType] = UNSET + """Name of the SAP component, representing a specific functional area in SAP.""" + + sap_data_type: Union[str, None, UnsetType] = UNSET + """SAP-specific data types""" + + sap_field_count: Union[int, None, UnsetType] = UNSET + """Represents the total number of fields, columns, or child assets present in a given SAP asset.""" + + sap_field_order: Union[int, None, UnsetType] = UNSET + """Indicates the sequential position of a field, column, or child asset within its parent SAP asset, starting from 1.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + +class SapErpColumnRelationshipAttributes(AssetRelationshipAttributes): + """SapErpColumn-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + sap_erp_table: Union[RelatedSapErpTable, None, UnsetType] = UNSET + """SAP ERP table in which this column exists.""" + + sap_erp_view: Union[RelatedSapErpView, None, UnsetType] = UNSET + """SAP ERP View in which this column exists.""" + + sap_erp_cds_view: Union[RelatedSapErpCdsView, None, UnsetType] = UNSET + """SAP ERP CDS View in which this column exists.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SapErpColumnNested(AssetNested): + """SapErpColumn in nested API format for high-performance serialization.""" + + attributes: Union[SapErpColumnAttributes, UnsetType] = UNSET + relationship_attributes: Union[SapErpColumnRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + SapErpColumnRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SapErpColumnRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SAP_ERP_COLUMN_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "sap_erp_table", + "sap_erp_view", + "sap_erp_cds_view", + "schema_registry_subjects", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_sap_erp_column_attrs( + attrs: SapErpColumnAttributes, obj: SapErpColumn +) -> None: + """Populate SapErpColumn-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.sap_data_element = obj.sap_data_element + attrs.sap_logical_data_type = obj.sap_logical_data_type + attrs.sap_length = obj.sap_length + attrs.sap_decimals = obj.sap_decimals + attrs.sap_is_primary = obj.sap_is_primary + attrs.sap_is_foreign = obj.sap_is_foreign + attrs.sap_is_mandatory = obj.sap_is_mandatory + attrs.sap_erp_table_name = obj.sap_erp_table_name + attrs.sap_erp_table_qualified_name = obj.sap_erp_table_qualified_name + attrs.sap_erp_view_name = obj.sap_erp_view_name + attrs.sap_erp_view_qualified_name = obj.sap_erp_view_qualified_name + attrs.sap_erp_cds_view_name = obj.sap_erp_cds_view_name + attrs.sap_erp_cds_view_qualified_name = obj.sap_erp_cds_view_qualified_name + attrs.sap_check_table_name = obj.sap_check_table_name + attrs.sap_check_table_qualified_name = obj.sap_check_table_qualified_name + attrs.sap_technical_name = obj.sap_technical_name + attrs.sap_logical_name = obj.sap_logical_name + attrs.sap_package_name = obj.sap_package_name + attrs.sap_component_name = obj.sap_component_name + attrs.sap_data_type = obj.sap_data_type + attrs.sap_field_count = obj.sap_field_count + attrs.sap_field_order = obj.sap_field_order + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + + +def _extract_sap_erp_column_attrs(attrs: SapErpColumnAttributes) -> dict: + """Extract all SapErpColumn attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["sap_data_element"] = attrs.sap_data_element + result["sap_logical_data_type"] = attrs.sap_logical_data_type + result["sap_length"] = attrs.sap_length + result["sap_decimals"] = attrs.sap_decimals + result["sap_is_primary"] = attrs.sap_is_primary + result["sap_is_foreign"] = attrs.sap_is_foreign + result["sap_is_mandatory"] = attrs.sap_is_mandatory + result["sap_erp_table_name"] = attrs.sap_erp_table_name + result["sap_erp_table_qualified_name"] = attrs.sap_erp_table_qualified_name + result["sap_erp_view_name"] = attrs.sap_erp_view_name + result["sap_erp_view_qualified_name"] = attrs.sap_erp_view_qualified_name + result["sap_erp_cds_view_name"] = attrs.sap_erp_cds_view_name + result["sap_erp_cds_view_qualified_name"] = attrs.sap_erp_cds_view_qualified_name + result["sap_check_table_name"] = attrs.sap_check_table_name + result["sap_check_table_qualified_name"] = attrs.sap_check_table_qualified_name + result["sap_technical_name"] = attrs.sap_technical_name + result["sap_logical_name"] = attrs.sap_logical_name + result["sap_package_name"] = attrs.sap_package_name + result["sap_component_name"] = attrs.sap_component_name + result["sap_data_type"] = attrs.sap_data_type + result["sap_field_count"] = attrs.sap_field_count + result["sap_field_order"] = attrs.sap_field_order + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _sap_erp_column_to_nested(sap_erp_column: SapErpColumn) -> SapErpColumnNested: + """Convert flat SapErpColumn to nested format.""" + attrs = SapErpColumnAttributes() + _populate_sap_erp_column_attrs(attrs, sap_erp_column) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + sap_erp_column, _SAP_ERP_COLUMN_REL_FIELDS, SapErpColumnRelationshipAttributes + ) + return SapErpColumnNested( + guid=sap_erp_column.guid, + type_name=sap_erp_column.type_name, + status=sap_erp_column.status, + version=sap_erp_column.version, + create_time=sap_erp_column.create_time, + update_time=sap_erp_column.update_time, + created_by=sap_erp_column.created_by, + updated_by=sap_erp_column.updated_by, + classifications=sap_erp_column.classifications, + classification_names=sap_erp_column.classification_names, + meanings=sap_erp_column.meanings, + labels=sap_erp_column.labels, + business_attributes=sap_erp_column.business_attributes, + custom_attributes=sap_erp_column.custom_attributes, + pending_tasks=sap_erp_column.pending_tasks, + proxy=sap_erp_column.proxy, + is_incomplete=sap_erp_column.is_incomplete, + provenance_type=sap_erp_column.provenance_type, + home_id=sap_erp_column.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _sap_erp_column_from_nested(nested: SapErpColumnNested) -> SapErpColumn: + """Convert nested format to flat SapErpColumn.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else SapErpColumnAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SAP_ERP_COLUMN_REL_FIELDS, + SapErpColumnRelationshipAttributes, + ) + return SapErpColumn( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_sap_erp_column_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _sap_erp_column_to_nested_bytes( + sap_erp_column: SapErpColumn, serde: Serde +) -> bytes: + """Convert flat SapErpColumn to nested JSON bytes.""" + return serde.encode(_sap_erp_column_to_nested(sap_erp_column)) + + +def _sap_erp_column_from_nested_bytes(data: bytes, serde: Serde) -> SapErpColumn: + """Convert nested JSON bytes to flat SapErpColumn.""" + nested = serde.decode(data, SapErpColumnNested) + return _sap_erp_column_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +SapErpColumn.SAP_DATA_ELEMENT = KeywordField("sapDataElement", "sapDataElement") +SapErpColumn.SAP_LOGICAL_DATA_TYPE = KeywordField( + "sapLogicalDataType", "sapLogicalDataType" +) +SapErpColumn.SAP_LENGTH = KeywordField("sapLength", "sapLength") +SapErpColumn.SAP_DECIMALS = KeywordField("sapDecimals", "sapDecimals") +SapErpColumn.SAP_IS_PRIMARY = BooleanField("sapIsPrimary", "sapIsPrimary") +SapErpColumn.SAP_IS_FOREIGN = BooleanField("sapIsForeign", "sapIsForeign") +SapErpColumn.SAP_IS_MANDATORY = BooleanField("sapIsMandatory", "sapIsMandatory") +SapErpColumn.SAP_ERP_TABLE_NAME = KeywordField("sapErpTableName", "sapErpTableName") +SapErpColumn.SAP_ERP_TABLE_QUALIFIED_NAME = KeywordTextField( + "sapErpTableQualifiedName", + "sapErpTableQualifiedName", + "sapErpTableQualifiedName.text", +) +SapErpColumn.SAP_ERP_VIEW_NAME = KeywordField("sapErpViewName", "sapErpViewName") +SapErpColumn.SAP_ERP_VIEW_QUALIFIED_NAME = KeywordTextField( + "sapErpViewQualifiedName", "sapErpViewQualifiedName", "sapErpViewQualifiedName.text" +) +SapErpColumn.SAP_ERP_CDS_VIEW_NAME = KeywordField( + "sapErpCdsViewName", "sapErpCdsViewName" +) +SapErpColumn.SAP_ERP_CDS_VIEW_QUALIFIED_NAME = KeywordTextField( + "sapErpCdsViewQualifiedName", + "sapErpCdsViewQualifiedName", + "sapErpCdsViewQualifiedName.text", +) +SapErpColumn.SAP_CHECK_TABLE_NAME = KeywordField( + "sapCheckTableName", "sapCheckTableName" +) +SapErpColumn.SAP_CHECK_TABLE_QUALIFIED_NAME = KeywordField( + "sapCheckTableQualifiedName", "sapCheckTableQualifiedName" +) +SapErpColumn.SAP_TECHNICAL_NAME = KeywordField("sapTechnicalName", "sapTechnicalName") +SapErpColumn.SAP_LOGICAL_NAME = KeywordField("sapLogicalName", "sapLogicalName") +SapErpColumn.SAP_PACKAGE_NAME = KeywordField("sapPackageName", "sapPackageName") +SapErpColumn.SAP_COMPONENT_NAME = KeywordField("sapComponentName", "sapComponentName") +SapErpColumn.SAP_DATA_TYPE = KeywordField("sapDataType", "sapDataType") +SapErpColumn.SAP_FIELD_COUNT = NumericField("sapFieldCount", "sapFieldCount") +SapErpColumn.SAP_FIELD_ORDER = NumericField("sapFieldOrder", "sapFieldOrder") +SapErpColumn.QUERY_COUNT = NumericField("queryCount", "queryCount") +SapErpColumn.QUERY_USER_COUNT = NumericField("queryUserCount", "queryUserCount") +SapErpColumn.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +SapErpColumn.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +SapErpColumn.DATABASE_NAME = KeywordField("databaseName", "databaseName") +SapErpColumn.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +SapErpColumn.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +SapErpColumn.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +SapErpColumn.TABLE_NAME = KeywordField("tableName", "tableName") +SapErpColumn.TABLE_QUALIFIED_NAME = KeywordField( + "tableQualifiedName", "tableQualifiedName" +) +SapErpColumn.VIEW_NAME = KeywordField("viewName", "viewName") +SapErpColumn.VIEW_QUALIFIED_NAME = KeywordField( + "viewQualifiedName", "viewQualifiedName" +) +SapErpColumn.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +SapErpColumn.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +SapErpColumn.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +SapErpColumn.LAST_PROFILED_AT = NumericField("lastProfiledAt", "lastProfiledAt") +SapErpColumn.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +SapErpColumn.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +SapErpColumn.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SapErpColumn.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +SapErpColumn.ANOMALO_CHECKS = RelationField("anomaloChecks") +SapErpColumn.APPLICATION = RelationField("application") +SapErpColumn.APPLICATION_FIELD = RelationField("applicationField") +SapErpColumn.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +SapErpColumn.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +SapErpColumn.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +SapErpColumn.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +SapErpColumn.METRICS = RelationField("metrics") +SapErpColumn.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SapErpColumn.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +SapErpColumn.DBT_MODELS = RelationField("dbtModels") +SapErpColumn.SQL_DBT_MODELS = RelationField("sqlDbtModels") +SapErpColumn.DBT_TESTS = RelationField("dbtTests") +SapErpColumn.DBT_SOURCES = RelationField("dbtSources") +SapErpColumn.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +SapErpColumn.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +SapErpColumn.MEANINGS = RelationField("meanings") +SapErpColumn.MC_MONITORS = RelationField("mcMonitors") +SapErpColumn.MC_INCIDENTS = RelationField("mcIncidents") +SapErpColumn.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SapErpColumn.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SapErpColumn.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SapErpColumn.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SapErpColumn.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +SapErpColumn.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +SapErpColumn.FILES = RelationField("files") +SapErpColumn.LINKS = RelationField("links") +SapErpColumn.README = RelationField("readme") +SapErpColumn.SAP_ERP_TABLE = RelationField("sapErpTable") +SapErpColumn.SAP_ERP_VIEW = RelationField("sapErpView") +SapErpColumn.SAP_ERP_CDS_VIEW = RelationField("sapErpCdsView") +SapErpColumn.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +SapErpColumn.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +SapErpColumn.SODA_CHECKS = RelationField("sodaChecks") +SapErpColumn.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SapErpColumn.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/sap_erp_component.py b/pyatlan_v9/model/assets/sap_erp_component.py new file mode 100644 index 000000000..f903ed041 --- /dev/null +++ b/pyatlan_v9/model/assets/sap_erp_component.py @@ -0,0 +1,712 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SapErpComponent asset model with flattened inheritance. + +This module provides: +- SapErpComponent: Flat asset class (easy to use) +- SapErpComponentAttributes: Nested attributes struct (extends AssetAttributes) +- SapErpComponentNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .sap_related import ( + RelatedSapErpAbapProgram, + RelatedSapErpCdsView, + RelatedSapErpComponent, + RelatedSapErpFunctionModule, + RelatedSapErpTable, + RelatedSapErpTransactionCode, + RelatedSapErpView, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SapErpComponent(Asset): + """ + Instance of a SAP Component in Atlan. + """ + + SAP_TECHNICAL_NAME: ClassVar[Any] = None + SAP_LOGICAL_NAME: ClassVar[Any] = None + SAP_PACKAGE_NAME: ClassVar[Any] = None + SAP_COMPONENT_NAME: ClassVar[Any] = None + SAP_DATA_TYPE: ClassVar[Any] = None + SAP_FIELD_COUNT: ClassVar[Any] = None + SAP_FIELD_ORDER: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SAP_ERP_TRANSACTION_CODES: ClassVar[Any] = None + SAP_ERP_VIEWS: ClassVar[Any] = None + SAP_ERP_CDS_VIEWS: ClassVar[Any] = None + CHILD_COMPONENTS: ClassVar[Any] = None + PARENT_COMPONENT: ClassVar[Any] = None + SAP_ERP_FUNCTION_MODULES: ClassVar[Any] = None + SAP_ERP_TABLES: ClassVar[Any] = None + SAP_ERP_ABAP_PROGRAMS: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SapErpComponent" + + sap_technical_name: Union[str, None, UnsetType] = UNSET + """Technical identifier for SAP data objects, used for integration and internal reference.""" + + sap_logical_name: Union[str, None, UnsetType] = UNSET + """Logical, business-friendly identifier for SAP data objects, aligned with business terminology and concepts.""" + + sap_package_name: Union[str, None, UnsetType] = UNSET + """Name of the SAP package, representing a logical grouping of related SAP data objects.""" + + sap_component_name: Union[str, None, UnsetType] = UNSET + """Name of the SAP component, representing a specific functional area in SAP.""" + + sap_data_type: Union[str, None, UnsetType] = UNSET + """SAP-specific data types""" + + sap_field_count: Union[int, None, UnsetType] = UNSET + """Represents the total number of fields, columns, or child assets present in a given SAP asset.""" + + sap_field_order: Union[int, None, UnsetType] = UNSET + """Indicates the sequential position of a field, column, or child asset within its parent SAP asset, starting from 1.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + sap_erp_transaction_codes: Union[ + List[RelatedSapErpTransactionCode], None, UnsetType + ] = UNSET + """SAP ERP Component associated with these SAP ERP Transaction Codes.""" + + sap_erp_views: Union[List[RelatedSapErpView], None, UnsetType] = UNSET + """SAP ERP Component associated with this SAP ERP Views.""" + + sap_erp_cds_views: Union[List[RelatedSapErpCdsView], None, UnsetType] = UNSET + """SAP ERP Component associated with this SAP ERP CDS Views.""" + + child_components: Union[List[RelatedSapErpComponent], None, UnsetType] = UNSET + """Child SAP ERP Component associated with this SAP ERP Components.""" + + parent_component: Union[RelatedSapErpComponent, None, UnsetType] = UNSET + """Parent SAP ERP Component in which these child SAP ERP Component exist.""" + + sap_erp_function_modules: Union[ + List[RelatedSapErpFunctionModule], None, UnsetType + ] = UNSET + """SAP ERP Component associated with this SAP ERP Function Modules.""" + + sap_erp_tables: Union[List[RelatedSapErpTable], None, UnsetType] = UNSET + """SAP ERP Component associated with these SAP ERP Tables.""" + + sap_erp_abap_programs: Union[List[RelatedSapErpAbapProgram], None, UnsetType] = ( + UNSET + ) + """SAP ERP Component associated with this SAP ERP ABAP Programs.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SapErpComponent" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _sap_erp_component_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> SapErpComponent: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SapErpComponent instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _sap_erp_component_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SapErpComponentAttributes(AssetAttributes): + """SapErpComponent-specific attributes for nested API format.""" + + sap_technical_name: Union[str, None, UnsetType] = UNSET + """Technical identifier for SAP data objects, used for integration and internal reference.""" + + sap_logical_name: Union[str, None, UnsetType] = UNSET + """Logical, business-friendly identifier for SAP data objects, aligned with business terminology and concepts.""" + + sap_package_name: Union[str, None, UnsetType] = UNSET + """Name of the SAP package, representing a logical grouping of related SAP data objects.""" + + sap_component_name: Union[str, None, UnsetType] = UNSET + """Name of the SAP component, representing a specific functional area in SAP.""" + + sap_data_type: Union[str, None, UnsetType] = UNSET + """SAP-specific data types""" + + sap_field_count: Union[int, None, UnsetType] = UNSET + """Represents the total number of fields, columns, or child assets present in a given SAP asset.""" + + sap_field_order: Union[int, None, UnsetType] = UNSET + """Indicates the sequential position of a field, column, or child asset within its parent SAP asset, starting from 1.""" + + +class SapErpComponentRelationshipAttributes(AssetRelationshipAttributes): + """SapErpComponent-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + sap_erp_transaction_codes: Union[ + List[RelatedSapErpTransactionCode], None, UnsetType + ] = UNSET + """SAP ERP Component associated with these SAP ERP Transaction Codes.""" + + sap_erp_views: Union[List[RelatedSapErpView], None, UnsetType] = UNSET + """SAP ERP Component associated with this SAP ERP Views.""" + + sap_erp_cds_views: Union[List[RelatedSapErpCdsView], None, UnsetType] = UNSET + """SAP ERP Component associated with this SAP ERP CDS Views.""" + + child_components: Union[List[RelatedSapErpComponent], None, UnsetType] = UNSET + """Child SAP ERP Component associated with this SAP ERP Components.""" + + parent_component: Union[RelatedSapErpComponent, None, UnsetType] = UNSET + """Parent SAP ERP Component in which these child SAP ERP Component exist.""" + + sap_erp_function_modules: Union[ + List[RelatedSapErpFunctionModule], None, UnsetType + ] = UNSET + """SAP ERP Component associated with this SAP ERP Function Modules.""" + + sap_erp_tables: Union[List[RelatedSapErpTable], None, UnsetType] = UNSET + """SAP ERP Component associated with these SAP ERP Tables.""" + + sap_erp_abap_programs: Union[List[RelatedSapErpAbapProgram], None, UnsetType] = ( + UNSET + ) + """SAP ERP Component associated with this SAP ERP ABAP Programs.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SapErpComponentNested(AssetNested): + """SapErpComponent in nested API format for high-performance serialization.""" + + attributes: Union[SapErpComponentAttributes, UnsetType] = UNSET + relationship_attributes: Union[SapErpComponentRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + SapErpComponentRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SapErpComponentRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SAP_ERP_COMPONENT_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "sap_erp_transaction_codes", + "sap_erp_views", + "sap_erp_cds_views", + "child_components", + "parent_component", + "sap_erp_function_modules", + "sap_erp_tables", + "sap_erp_abap_programs", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_sap_erp_component_attrs( + attrs: SapErpComponentAttributes, obj: SapErpComponent +) -> None: + """Populate SapErpComponent-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.sap_technical_name = obj.sap_technical_name + attrs.sap_logical_name = obj.sap_logical_name + attrs.sap_package_name = obj.sap_package_name + attrs.sap_component_name = obj.sap_component_name + attrs.sap_data_type = obj.sap_data_type + attrs.sap_field_count = obj.sap_field_count + attrs.sap_field_order = obj.sap_field_order + + +def _extract_sap_erp_component_attrs(attrs: SapErpComponentAttributes) -> dict: + """Extract all SapErpComponent attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["sap_technical_name"] = attrs.sap_technical_name + result["sap_logical_name"] = attrs.sap_logical_name + result["sap_package_name"] = attrs.sap_package_name + result["sap_component_name"] = attrs.sap_component_name + result["sap_data_type"] = attrs.sap_data_type + result["sap_field_count"] = attrs.sap_field_count + result["sap_field_order"] = attrs.sap_field_order + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _sap_erp_component_to_nested( + sap_erp_component: SapErpComponent, +) -> SapErpComponentNested: + """Convert flat SapErpComponent to nested format.""" + attrs = SapErpComponentAttributes() + _populate_sap_erp_component_attrs(attrs, sap_erp_component) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + sap_erp_component, + _SAP_ERP_COMPONENT_REL_FIELDS, + SapErpComponentRelationshipAttributes, + ) + return SapErpComponentNested( + guid=sap_erp_component.guid, + type_name=sap_erp_component.type_name, + status=sap_erp_component.status, + version=sap_erp_component.version, + create_time=sap_erp_component.create_time, + update_time=sap_erp_component.update_time, + created_by=sap_erp_component.created_by, + updated_by=sap_erp_component.updated_by, + classifications=sap_erp_component.classifications, + classification_names=sap_erp_component.classification_names, + meanings=sap_erp_component.meanings, + labels=sap_erp_component.labels, + business_attributes=sap_erp_component.business_attributes, + custom_attributes=sap_erp_component.custom_attributes, + pending_tasks=sap_erp_component.pending_tasks, + proxy=sap_erp_component.proxy, + is_incomplete=sap_erp_component.is_incomplete, + provenance_type=sap_erp_component.provenance_type, + home_id=sap_erp_component.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _sap_erp_component_from_nested(nested: SapErpComponentNested) -> SapErpComponent: + """Convert nested format to flat SapErpComponent.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else SapErpComponentAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SAP_ERP_COMPONENT_REL_FIELDS, + SapErpComponentRelationshipAttributes, + ) + return SapErpComponent( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_sap_erp_component_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _sap_erp_component_to_nested_bytes( + sap_erp_component: SapErpComponent, serde: Serde +) -> bytes: + """Convert flat SapErpComponent to nested JSON bytes.""" + return serde.encode(_sap_erp_component_to_nested(sap_erp_component)) + + +def _sap_erp_component_from_nested_bytes(data: bytes, serde: Serde) -> SapErpComponent: + """Convert nested JSON bytes to flat SapErpComponent.""" + nested = serde.decode(data, SapErpComponentNested) + return _sap_erp_component_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +SapErpComponent.SAP_TECHNICAL_NAME = KeywordField( + "sapTechnicalName", "sapTechnicalName" +) +SapErpComponent.SAP_LOGICAL_NAME = KeywordField("sapLogicalName", "sapLogicalName") +SapErpComponent.SAP_PACKAGE_NAME = KeywordField("sapPackageName", "sapPackageName") +SapErpComponent.SAP_COMPONENT_NAME = KeywordField( + "sapComponentName", "sapComponentName" +) +SapErpComponent.SAP_DATA_TYPE = KeywordField("sapDataType", "sapDataType") +SapErpComponent.SAP_FIELD_COUNT = NumericField("sapFieldCount", "sapFieldCount") +SapErpComponent.SAP_FIELD_ORDER = NumericField("sapFieldOrder", "sapFieldOrder") +SapErpComponent.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SapErpComponent.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +SapErpComponent.ANOMALO_CHECKS = RelationField("anomaloChecks") +SapErpComponent.APPLICATION = RelationField("application") +SapErpComponent.APPLICATION_FIELD = RelationField("applicationField") +SapErpComponent.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +SapErpComponent.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +SapErpComponent.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +SapErpComponent.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +SapErpComponent.METRICS = RelationField("metrics") +SapErpComponent.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SapErpComponent.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +SapErpComponent.MEANINGS = RelationField("meanings") +SapErpComponent.MC_MONITORS = RelationField("mcMonitors") +SapErpComponent.MC_INCIDENTS = RelationField("mcIncidents") +SapErpComponent.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SapErpComponent.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SapErpComponent.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SapErpComponent.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SapErpComponent.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +SapErpComponent.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +SapErpComponent.FILES = RelationField("files") +SapErpComponent.LINKS = RelationField("links") +SapErpComponent.README = RelationField("readme") +SapErpComponent.SAP_ERP_TRANSACTION_CODES = RelationField("sapErpTransactionCodes") +SapErpComponent.SAP_ERP_VIEWS = RelationField("sapErpViews") +SapErpComponent.SAP_ERP_CDS_VIEWS = RelationField("sapErpCdsViews") +SapErpComponent.CHILD_COMPONENTS = RelationField("childComponents") +SapErpComponent.PARENT_COMPONENT = RelationField("parentComponent") +SapErpComponent.SAP_ERP_FUNCTION_MODULES = RelationField("sapErpFunctionModules") +SapErpComponent.SAP_ERP_TABLES = RelationField("sapErpTables") +SapErpComponent.SAP_ERP_ABAP_PROGRAMS = RelationField("sapErpAbapPrograms") +SapErpComponent.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +SapErpComponent.SODA_CHECKS = RelationField("sodaChecks") +SapErpComponent.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SapErpComponent.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/sap_erp_function_module.py b/pyatlan_v9/model/assets/sap_erp_function_module.py new file mode 100644 index 000000000..221e0f9c7 --- /dev/null +++ b/pyatlan_v9/model/assets/sap_erp_function_module.py @@ -0,0 +1,756 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SapErpFunctionModule asset model with flattened inheritance. + +This module provides: +- SapErpFunctionModule: Flat asset class (easy to use) +- SapErpFunctionModuleAttributes: Nested attributes struct (extends AssetAttributes) +- SapErpFunctionModuleNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .sap_related import RelatedSapErpAbapProgram, RelatedSapErpComponent + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SapErpFunctionModule(Asset): + """ + Instance of a SAP Function in Atlan. + """ + + SAP_GROUP: ClassVar[Any] = None + SAP_ERP_FUNCTION_MODULE_IMPORT_PARAMS: ClassVar[Any] = None + SAP_IMPORT_PARAMS_COUNT: ClassVar[Any] = None + SAP_ERP_FUNCTION_MODULE_EXPORT_PARAMS: ClassVar[Any] = None + SAP_EXPORT_PARAMS_COUNT: ClassVar[Any] = None + SAP_ERP_FUNCTION_EXCEPTION_LIST: ClassVar[Any] = None + SAP_ERP_FUNCTION_EXCEPTION_LIST_COUNT: ClassVar[Any] = None + SAP_TECHNICAL_NAME: ClassVar[Any] = None + SAP_LOGICAL_NAME: ClassVar[Any] = None + SAP_PACKAGE_NAME: ClassVar[Any] = None + SAP_COMPONENT_NAME: ClassVar[Any] = None + SAP_DATA_TYPE: ClassVar[Any] = None + SAP_FIELD_COUNT: ClassVar[Any] = None + SAP_FIELD_ORDER: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SAP_ERP_ABAP_PROGRAM: ClassVar[Any] = None + SAP_ERP_COMPONENT: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SapErpFunctionModule" + + sap_group: Union[str, None, UnsetType] = UNSET + """Represents the group to which the SAP ERP function module belongs.""" + + sap_erp_function_module_import_params: Union[ + List[Dict[str, str]], None, UnsetType + ] = UNSET + """Parameters imported by the SAP ERP function module, defined as key-value pairs.""" + + sap_import_params_count: Union[int, None, UnsetType] = UNSET + """Represents the total number of Import Parameters in a given SAP ERP Function Module.""" + + sap_erp_function_module_export_params: Union[ + List[Dict[str, str]], None, UnsetType + ] = UNSET + """Parameters exported by the SAP ERP function module, defined as key-value pairs.""" + + sap_export_params_count: Union[int, None, UnsetType] = UNSET + """Represents the total number of Export Parameters in a given SAP ERP Function Module.""" + + sap_erp_function_exception_list: Union[List[Dict[str, str]], None, UnsetType] = ( + UNSET + ) + """List of exceptions raised by the SAP ERP function module, defined as key-value pairs.""" + + sap_erp_function_exception_list_count: Union[int, None, UnsetType] = UNSET + """Represents the total number of Exceptions in a given SAP ERP Function Module.""" + + sap_technical_name: Union[str, None, UnsetType] = UNSET + """Technical identifier for SAP data objects, used for integration and internal reference.""" + + sap_logical_name: Union[str, None, UnsetType] = UNSET + """Logical, business-friendly identifier for SAP data objects, aligned with business terminology and concepts.""" + + sap_package_name: Union[str, None, UnsetType] = UNSET + """Name of the SAP package, representing a logical grouping of related SAP data objects.""" + + sap_component_name: Union[str, None, UnsetType] = UNSET + """Name of the SAP component, representing a specific functional area in SAP.""" + + sap_data_type: Union[str, None, UnsetType] = UNSET + """SAP-specific data types""" + + sap_field_count: Union[int, None, UnsetType] = UNSET + """Represents the total number of fields, columns, or child assets present in a given SAP asset.""" + + sap_field_order: Union[int, None, UnsetType] = UNSET + """Indicates the sequential position of a field, column, or child asset within its parent SAP asset, starting from 1.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + sap_erp_abap_program: Union[RelatedSapErpAbapProgram, None, UnsetType] = UNSET + """SAP ERP Function Modules that are associated with this SAP ERP ABAP Program.""" + + sap_erp_component: Union[RelatedSapErpComponent, None, UnsetType] = UNSET + """SAP ERP Function Modules that are associated with this SAP ERP Component.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SapErpFunctionModule" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _sap_erp_function_module_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> SapErpFunctionModule: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SapErpFunctionModule instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _sap_erp_function_module_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SapErpFunctionModuleAttributes(AssetAttributes): + """SapErpFunctionModule-specific attributes for nested API format.""" + + sap_group: Union[str, None, UnsetType] = UNSET + """Represents the group to which the SAP ERP function module belongs.""" + + sap_erp_function_module_import_params: Union[ + List[Dict[str, str]], None, UnsetType + ] = UNSET + """Parameters imported by the SAP ERP function module, defined as key-value pairs.""" + + sap_import_params_count: Union[int, None, UnsetType] = UNSET + """Represents the total number of Import Parameters in a given SAP ERP Function Module.""" + + sap_erp_function_module_export_params: Union[ + List[Dict[str, str]], None, UnsetType + ] = UNSET + """Parameters exported by the SAP ERP function module, defined as key-value pairs.""" + + sap_export_params_count: Union[int, None, UnsetType] = UNSET + """Represents the total number of Export Parameters in a given SAP ERP Function Module.""" + + sap_erp_function_exception_list: Union[List[Dict[str, str]], None, UnsetType] = ( + UNSET + ) + """List of exceptions raised by the SAP ERP function module, defined as key-value pairs.""" + + sap_erp_function_exception_list_count: Union[int, None, UnsetType] = UNSET + """Represents the total number of Exceptions in a given SAP ERP Function Module.""" + + sap_technical_name: Union[str, None, UnsetType] = UNSET + """Technical identifier for SAP data objects, used for integration and internal reference.""" + + sap_logical_name: Union[str, None, UnsetType] = UNSET + """Logical, business-friendly identifier for SAP data objects, aligned with business terminology and concepts.""" + + sap_package_name: Union[str, None, UnsetType] = UNSET + """Name of the SAP package, representing a logical grouping of related SAP data objects.""" + + sap_component_name: Union[str, None, UnsetType] = UNSET + """Name of the SAP component, representing a specific functional area in SAP.""" + + sap_data_type: Union[str, None, UnsetType] = UNSET + """SAP-specific data types""" + + sap_field_count: Union[int, None, UnsetType] = UNSET + """Represents the total number of fields, columns, or child assets present in a given SAP asset.""" + + sap_field_order: Union[int, None, UnsetType] = UNSET + """Indicates the sequential position of a field, column, or child asset within its parent SAP asset, starting from 1.""" + + +class SapErpFunctionModuleRelationshipAttributes(AssetRelationshipAttributes): + """SapErpFunctionModule-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + sap_erp_abap_program: Union[RelatedSapErpAbapProgram, None, UnsetType] = UNSET + """SAP ERP Function Modules that are associated with this SAP ERP ABAP Program.""" + + sap_erp_component: Union[RelatedSapErpComponent, None, UnsetType] = UNSET + """SAP ERP Function Modules that are associated with this SAP ERP Component.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SapErpFunctionModuleNested(AssetNested): + """SapErpFunctionModule in nested API format for high-performance serialization.""" + + attributes: Union[SapErpFunctionModuleAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + SapErpFunctionModuleRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + SapErpFunctionModuleRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SapErpFunctionModuleRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SAP_ERP_FUNCTION_MODULE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "sap_erp_abap_program", + "sap_erp_component", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_sap_erp_function_module_attrs( + attrs: SapErpFunctionModuleAttributes, obj: SapErpFunctionModule +) -> None: + """Populate SapErpFunctionModule-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.sap_group = obj.sap_group + attrs.sap_erp_function_module_import_params = ( + obj.sap_erp_function_module_import_params + ) + attrs.sap_import_params_count = obj.sap_import_params_count + attrs.sap_erp_function_module_export_params = ( + obj.sap_erp_function_module_export_params + ) + attrs.sap_export_params_count = obj.sap_export_params_count + attrs.sap_erp_function_exception_list = obj.sap_erp_function_exception_list + attrs.sap_erp_function_exception_list_count = ( + obj.sap_erp_function_exception_list_count + ) + attrs.sap_technical_name = obj.sap_technical_name + attrs.sap_logical_name = obj.sap_logical_name + attrs.sap_package_name = obj.sap_package_name + attrs.sap_component_name = obj.sap_component_name + attrs.sap_data_type = obj.sap_data_type + attrs.sap_field_count = obj.sap_field_count + attrs.sap_field_order = obj.sap_field_order + + +def _extract_sap_erp_function_module_attrs( + attrs: SapErpFunctionModuleAttributes, +) -> dict: + """Extract all SapErpFunctionModule attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["sap_group"] = attrs.sap_group + result["sap_erp_function_module_import_params"] = ( + attrs.sap_erp_function_module_import_params + ) + result["sap_import_params_count"] = attrs.sap_import_params_count + result["sap_erp_function_module_export_params"] = ( + attrs.sap_erp_function_module_export_params + ) + result["sap_export_params_count"] = attrs.sap_export_params_count + result["sap_erp_function_exception_list"] = attrs.sap_erp_function_exception_list + result["sap_erp_function_exception_list_count"] = ( + attrs.sap_erp_function_exception_list_count + ) + result["sap_technical_name"] = attrs.sap_technical_name + result["sap_logical_name"] = attrs.sap_logical_name + result["sap_package_name"] = attrs.sap_package_name + result["sap_component_name"] = attrs.sap_component_name + result["sap_data_type"] = attrs.sap_data_type + result["sap_field_count"] = attrs.sap_field_count + result["sap_field_order"] = attrs.sap_field_order + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _sap_erp_function_module_to_nested( + sap_erp_function_module: SapErpFunctionModule, +) -> SapErpFunctionModuleNested: + """Convert flat SapErpFunctionModule to nested format.""" + attrs = SapErpFunctionModuleAttributes() + _populate_sap_erp_function_module_attrs(attrs, sap_erp_function_module) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + sap_erp_function_module, + _SAP_ERP_FUNCTION_MODULE_REL_FIELDS, + SapErpFunctionModuleRelationshipAttributes, + ) + return SapErpFunctionModuleNested( + guid=sap_erp_function_module.guid, + type_name=sap_erp_function_module.type_name, + status=sap_erp_function_module.status, + version=sap_erp_function_module.version, + create_time=sap_erp_function_module.create_time, + update_time=sap_erp_function_module.update_time, + created_by=sap_erp_function_module.created_by, + updated_by=sap_erp_function_module.updated_by, + classifications=sap_erp_function_module.classifications, + classification_names=sap_erp_function_module.classification_names, + meanings=sap_erp_function_module.meanings, + labels=sap_erp_function_module.labels, + business_attributes=sap_erp_function_module.business_attributes, + custom_attributes=sap_erp_function_module.custom_attributes, + pending_tasks=sap_erp_function_module.pending_tasks, + proxy=sap_erp_function_module.proxy, + is_incomplete=sap_erp_function_module.is_incomplete, + provenance_type=sap_erp_function_module.provenance_type, + home_id=sap_erp_function_module.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _sap_erp_function_module_from_nested( + nested: SapErpFunctionModuleNested, +) -> SapErpFunctionModule: + """Convert nested format to flat SapErpFunctionModule.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else SapErpFunctionModuleAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SAP_ERP_FUNCTION_MODULE_REL_FIELDS, + SapErpFunctionModuleRelationshipAttributes, + ) + return SapErpFunctionModule( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_sap_erp_function_module_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _sap_erp_function_module_to_nested_bytes( + sap_erp_function_module: SapErpFunctionModule, serde: Serde +) -> bytes: + """Convert flat SapErpFunctionModule to nested JSON bytes.""" + return serde.encode(_sap_erp_function_module_to_nested(sap_erp_function_module)) + + +def _sap_erp_function_module_from_nested_bytes( + data: bytes, serde: Serde +) -> SapErpFunctionModule: + """Convert nested JSON bytes to flat SapErpFunctionModule.""" + nested = serde.decode(data, SapErpFunctionModuleNested) + return _sap_erp_function_module_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +SapErpFunctionModule.SAP_GROUP = KeywordField("sapGroup", "sapGroup") +SapErpFunctionModule.SAP_ERP_FUNCTION_MODULE_IMPORT_PARAMS = KeywordField( + "sapErpFunctionModuleImportParams", "sapErpFunctionModuleImportParams" +) +SapErpFunctionModule.SAP_IMPORT_PARAMS_COUNT = NumericField( + "sapImportParamsCount", "sapImportParamsCount" +) +SapErpFunctionModule.SAP_ERP_FUNCTION_MODULE_EXPORT_PARAMS = KeywordField( + "sapErpFunctionModuleExportParams", "sapErpFunctionModuleExportParams" +) +SapErpFunctionModule.SAP_EXPORT_PARAMS_COUNT = NumericField( + "sapExportParamsCount", "sapExportParamsCount" +) +SapErpFunctionModule.SAP_ERP_FUNCTION_EXCEPTION_LIST = KeywordField( + "sapErpFunctionExceptionList", "sapErpFunctionExceptionList" +) +SapErpFunctionModule.SAP_ERP_FUNCTION_EXCEPTION_LIST_COUNT = NumericField( + "sapErpFunctionExceptionListCount", "sapErpFunctionExceptionListCount" +) +SapErpFunctionModule.SAP_TECHNICAL_NAME = KeywordField( + "sapTechnicalName", "sapTechnicalName" +) +SapErpFunctionModule.SAP_LOGICAL_NAME = KeywordField("sapLogicalName", "sapLogicalName") +SapErpFunctionModule.SAP_PACKAGE_NAME = KeywordField("sapPackageName", "sapPackageName") +SapErpFunctionModule.SAP_COMPONENT_NAME = KeywordField( + "sapComponentName", "sapComponentName" +) +SapErpFunctionModule.SAP_DATA_TYPE = KeywordField("sapDataType", "sapDataType") +SapErpFunctionModule.SAP_FIELD_COUNT = NumericField("sapFieldCount", "sapFieldCount") +SapErpFunctionModule.SAP_FIELD_ORDER = NumericField("sapFieldOrder", "sapFieldOrder") +SapErpFunctionModule.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SapErpFunctionModule.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +SapErpFunctionModule.ANOMALO_CHECKS = RelationField("anomaloChecks") +SapErpFunctionModule.APPLICATION = RelationField("application") +SapErpFunctionModule.APPLICATION_FIELD = RelationField("applicationField") +SapErpFunctionModule.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +SapErpFunctionModule.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +SapErpFunctionModule.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +SapErpFunctionModule.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +SapErpFunctionModule.METRICS = RelationField("metrics") +SapErpFunctionModule.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SapErpFunctionModule.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +SapErpFunctionModule.MEANINGS = RelationField("meanings") +SapErpFunctionModule.MC_MONITORS = RelationField("mcMonitors") +SapErpFunctionModule.MC_INCIDENTS = RelationField("mcIncidents") +SapErpFunctionModule.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SapErpFunctionModule.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SapErpFunctionModule.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SapErpFunctionModule.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SapErpFunctionModule.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +SapErpFunctionModule.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +SapErpFunctionModule.FILES = RelationField("files") +SapErpFunctionModule.LINKS = RelationField("links") +SapErpFunctionModule.README = RelationField("readme") +SapErpFunctionModule.SAP_ERP_ABAP_PROGRAM = RelationField("sapErpAbapProgram") +SapErpFunctionModule.SAP_ERP_COMPONENT = RelationField("sapErpComponent") +SapErpFunctionModule.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +SapErpFunctionModule.SODA_CHECKS = RelationField("sodaChecks") +SapErpFunctionModule.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SapErpFunctionModule.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/sap_erp_table.py b/pyatlan_v9/model/assets/sap_erp_table.py new file mode 100644 index 000000000..d0e17761f --- /dev/null +++ b/pyatlan_v9/model/assets/sap_erp_table.py @@ -0,0 +1,642 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SapErpTable asset model with flattened inheritance. + +This module provides: +- SapErpTable: Flat asset class (easy to use) +- SapErpTableAttributes: Nested attributes struct (extends AssetAttributes) +- SapErpTableNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .sap_related import RelatedSapErpColumn, RelatedSapErpComponent + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SapErpTable(Asset): + """ + Instance of a SAP table in Atlan. + """ + + SAP_ERP_TABLE_TYPE: ClassVar[Any] = None + SAP_ERP_TABLE_DELIVERY_CLASS: ClassVar[Any] = None + SAP_TECHNICAL_NAME: ClassVar[Any] = None + SAP_LOGICAL_NAME: ClassVar[Any] = None + SAP_PACKAGE_NAME: ClassVar[Any] = None + SAP_COMPONENT_NAME: ClassVar[Any] = None + SAP_DATA_TYPE: ClassVar[Any] = None + SAP_FIELD_COUNT: ClassVar[Any] = None + SAP_FIELD_ORDER: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SAP_ERP_COLUMNS: ClassVar[Any] = None + SAP_ERP_COMPONENT: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SapErpTable" + + sap_erp_table_type: Union[str, None, UnsetType] = UNSET + """Type of the SAP ERP table.""" + + sap_erp_table_delivery_class: Union[str, None, UnsetType] = UNSET + """Defines the delivery class of the SAP ERP table, determining how the table's data is transported and managed during system updates.""" + + sap_technical_name: Union[str, None, UnsetType] = UNSET + """Technical identifier for SAP data objects, used for integration and internal reference.""" + + sap_logical_name: Union[str, None, UnsetType] = UNSET + """Logical, business-friendly identifier for SAP data objects, aligned with business terminology and concepts.""" + + sap_package_name: Union[str, None, UnsetType] = UNSET + """Name of the SAP package, representing a logical grouping of related SAP data objects.""" + + sap_component_name: Union[str, None, UnsetType] = UNSET + """Name of the SAP component, representing a specific functional area in SAP.""" + + sap_data_type: Union[str, None, UnsetType] = UNSET + """SAP-specific data types""" + + sap_field_count: Union[int, None, UnsetType] = UNSET + """Represents the total number of fields, columns, or child assets present in a given SAP asset.""" + + sap_field_order: Union[int, None, UnsetType] = UNSET + """Indicates the sequential position of a field, column, or child asset within its parent SAP asset, starting from 1.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + sap_erp_columns: Union[List[RelatedSapErpColumn], None, UnsetType] = UNSET + """SAP ERP columns that exist within this table.""" + + sap_erp_component: Union[RelatedSapErpComponent, None, UnsetType] = UNSET + """SAP ERP Tables that are associated with this SAP ERP Component.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SapErpTable" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _sap_erp_table_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> SapErpTable: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SapErpTable instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _sap_erp_table_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SapErpTableAttributes(AssetAttributes): + """SapErpTable-specific attributes for nested API format.""" + + sap_erp_table_type: Union[str, None, UnsetType] = UNSET + """Type of the SAP ERP table.""" + + sap_erp_table_delivery_class: Union[str, None, UnsetType] = UNSET + """Defines the delivery class of the SAP ERP table, determining how the table's data is transported and managed during system updates.""" + + sap_technical_name: Union[str, None, UnsetType] = UNSET + """Technical identifier for SAP data objects, used for integration and internal reference.""" + + sap_logical_name: Union[str, None, UnsetType] = UNSET + """Logical, business-friendly identifier for SAP data objects, aligned with business terminology and concepts.""" + + sap_package_name: Union[str, None, UnsetType] = UNSET + """Name of the SAP package, representing a logical grouping of related SAP data objects.""" + + sap_component_name: Union[str, None, UnsetType] = UNSET + """Name of the SAP component, representing a specific functional area in SAP.""" + + sap_data_type: Union[str, None, UnsetType] = UNSET + """SAP-specific data types""" + + sap_field_count: Union[int, None, UnsetType] = UNSET + """Represents the total number of fields, columns, or child assets present in a given SAP asset.""" + + sap_field_order: Union[int, None, UnsetType] = UNSET + """Indicates the sequential position of a field, column, or child asset within its parent SAP asset, starting from 1.""" + + +class SapErpTableRelationshipAttributes(AssetRelationshipAttributes): + """SapErpTable-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + sap_erp_columns: Union[List[RelatedSapErpColumn], None, UnsetType] = UNSET + """SAP ERP columns that exist within this table.""" + + sap_erp_component: Union[RelatedSapErpComponent, None, UnsetType] = UNSET + """SAP ERP Tables that are associated with this SAP ERP Component.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SapErpTableNested(AssetNested): + """SapErpTable in nested API format for high-performance serialization.""" + + attributes: Union[SapErpTableAttributes, UnsetType] = UNSET + relationship_attributes: Union[SapErpTableRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + SapErpTableRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SapErpTableRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SAP_ERP_TABLE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "sap_erp_columns", + "sap_erp_component", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_sap_erp_table_attrs( + attrs: SapErpTableAttributes, obj: SapErpTable +) -> None: + """Populate SapErpTable-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.sap_erp_table_type = obj.sap_erp_table_type + attrs.sap_erp_table_delivery_class = obj.sap_erp_table_delivery_class + attrs.sap_technical_name = obj.sap_technical_name + attrs.sap_logical_name = obj.sap_logical_name + attrs.sap_package_name = obj.sap_package_name + attrs.sap_component_name = obj.sap_component_name + attrs.sap_data_type = obj.sap_data_type + attrs.sap_field_count = obj.sap_field_count + attrs.sap_field_order = obj.sap_field_order + + +def _extract_sap_erp_table_attrs(attrs: SapErpTableAttributes) -> dict: + """Extract all SapErpTable attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["sap_erp_table_type"] = attrs.sap_erp_table_type + result["sap_erp_table_delivery_class"] = attrs.sap_erp_table_delivery_class + result["sap_technical_name"] = attrs.sap_technical_name + result["sap_logical_name"] = attrs.sap_logical_name + result["sap_package_name"] = attrs.sap_package_name + result["sap_component_name"] = attrs.sap_component_name + result["sap_data_type"] = attrs.sap_data_type + result["sap_field_count"] = attrs.sap_field_count + result["sap_field_order"] = attrs.sap_field_order + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _sap_erp_table_to_nested(sap_erp_table: SapErpTable) -> SapErpTableNested: + """Convert flat SapErpTable to nested format.""" + attrs = SapErpTableAttributes() + _populate_sap_erp_table_attrs(attrs, sap_erp_table) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + sap_erp_table, _SAP_ERP_TABLE_REL_FIELDS, SapErpTableRelationshipAttributes + ) + return SapErpTableNested( + guid=sap_erp_table.guid, + type_name=sap_erp_table.type_name, + status=sap_erp_table.status, + version=sap_erp_table.version, + create_time=sap_erp_table.create_time, + update_time=sap_erp_table.update_time, + created_by=sap_erp_table.created_by, + updated_by=sap_erp_table.updated_by, + classifications=sap_erp_table.classifications, + classification_names=sap_erp_table.classification_names, + meanings=sap_erp_table.meanings, + labels=sap_erp_table.labels, + business_attributes=sap_erp_table.business_attributes, + custom_attributes=sap_erp_table.custom_attributes, + pending_tasks=sap_erp_table.pending_tasks, + proxy=sap_erp_table.proxy, + is_incomplete=sap_erp_table.is_incomplete, + provenance_type=sap_erp_table.provenance_type, + home_id=sap_erp_table.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _sap_erp_table_from_nested(nested: SapErpTableNested) -> SapErpTable: + """Convert nested format to flat SapErpTable.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else SapErpTableAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SAP_ERP_TABLE_REL_FIELDS, + SapErpTableRelationshipAttributes, + ) + return SapErpTable( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_sap_erp_table_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _sap_erp_table_to_nested_bytes(sap_erp_table: SapErpTable, serde: Serde) -> bytes: + """Convert flat SapErpTable to nested JSON bytes.""" + return serde.encode(_sap_erp_table_to_nested(sap_erp_table)) + + +def _sap_erp_table_from_nested_bytes(data: bytes, serde: Serde) -> SapErpTable: + """Convert nested JSON bytes to flat SapErpTable.""" + nested = serde.decode(data, SapErpTableNested) + return _sap_erp_table_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +SapErpTable.SAP_ERP_TABLE_TYPE = KeywordField("sapErpTableType", "sapErpTableType") +SapErpTable.SAP_ERP_TABLE_DELIVERY_CLASS = KeywordField( + "sapErpTableDeliveryClass", "sapErpTableDeliveryClass" +) +SapErpTable.SAP_TECHNICAL_NAME = KeywordField("sapTechnicalName", "sapTechnicalName") +SapErpTable.SAP_LOGICAL_NAME = KeywordField("sapLogicalName", "sapLogicalName") +SapErpTable.SAP_PACKAGE_NAME = KeywordField("sapPackageName", "sapPackageName") +SapErpTable.SAP_COMPONENT_NAME = KeywordField("sapComponentName", "sapComponentName") +SapErpTable.SAP_DATA_TYPE = KeywordField("sapDataType", "sapDataType") +SapErpTable.SAP_FIELD_COUNT = NumericField("sapFieldCount", "sapFieldCount") +SapErpTable.SAP_FIELD_ORDER = NumericField("sapFieldOrder", "sapFieldOrder") +SapErpTable.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SapErpTable.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +SapErpTable.ANOMALO_CHECKS = RelationField("anomaloChecks") +SapErpTable.APPLICATION = RelationField("application") +SapErpTable.APPLICATION_FIELD = RelationField("applicationField") +SapErpTable.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +SapErpTable.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +SapErpTable.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +SapErpTable.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +SapErpTable.METRICS = RelationField("metrics") +SapErpTable.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SapErpTable.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +SapErpTable.MEANINGS = RelationField("meanings") +SapErpTable.MC_MONITORS = RelationField("mcMonitors") +SapErpTable.MC_INCIDENTS = RelationField("mcIncidents") +SapErpTable.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SapErpTable.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SapErpTable.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SapErpTable.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SapErpTable.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +SapErpTable.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +SapErpTable.FILES = RelationField("files") +SapErpTable.LINKS = RelationField("links") +SapErpTable.README = RelationField("readme") +SapErpTable.SAP_ERP_COLUMNS = RelationField("sapErpColumns") +SapErpTable.SAP_ERP_COMPONENT = RelationField("sapErpComponent") +SapErpTable.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +SapErpTable.SODA_CHECKS = RelationField("sodaChecks") +SapErpTable.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SapErpTable.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/sap_erp_transaction_code.py b/pyatlan_v9/model/assets/sap_erp_transaction_code.py new file mode 100644 index 000000000..75c4796da --- /dev/null +++ b/pyatlan_v9/model/assets/sap_erp_transaction_code.py @@ -0,0 +1,658 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SapErpTransactionCode asset model with flattened inheritance. + +This module provides: +- SapErpTransactionCode: Flat asset class (easy to use) +- SapErpTransactionCodeAttributes: Nested attributes struct (extends AssetAttributes) +- SapErpTransactionCodeNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .sap_related import RelatedSapErpAbapProgram, RelatedSapErpComponent + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SapErpTransactionCode(Asset): + """ + Instance of a SAP Transaction Code in Atlan. + """ + + SAP_TECHNICAL_NAME: ClassVar[Any] = None + SAP_LOGICAL_NAME: ClassVar[Any] = None + SAP_PACKAGE_NAME: ClassVar[Any] = None + SAP_COMPONENT_NAME: ClassVar[Any] = None + SAP_DATA_TYPE: ClassVar[Any] = None + SAP_FIELD_COUNT: ClassVar[Any] = None + SAP_FIELD_ORDER: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SAP_ERP_ABAP_PROGRAM: ClassVar[Any] = None + SAP_ERP_COMPONENT: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SapErpTransactionCode" + + sap_technical_name: Union[str, None, UnsetType] = UNSET + """Technical identifier for SAP data objects, used for integration and internal reference.""" + + sap_logical_name: Union[str, None, UnsetType] = UNSET + """Logical, business-friendly identifier for SAP data objects, aligned with business terminology and concepts.""" + + sap_package_name: Union[str, None, UnsetType] = UNSET + """Name of the SAP package, representing a logical grouping of related SAP data objects.""" + + sap_component_name: Union[str, None, UnsetType] = UNSET + """Name of the SAP component, representing a specific functional area in SAP.""" + + sap_data_type: Union[str, None, UnsetType] = UNSET + """SAP-specific data types""" + + sap_field_count: Union[int, None, UnsetType] = UNSET + """Represents the total number of fields, columns, or child assets present in a given SAP asset.""" + + sap_field_order: Union[int, None, UnsetType] = UNSET + """Indicates the sequential position of a field, column, or child asset within its parent SAP asset, starting from 1.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + sap_erp_abap_program: Union[RelatedSapErpAbapProgram, None, UnsetType] = UNSET + """SAP ERP Transaction Codes that are associated with this SAP ERP ABAP Program.""" + + sap_erp_component: Union[RelatedSapErpComponent, None, UnsetType] = UNSET + """SAP ERP Transaction Codes that are associated with this SAP ERP Component.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SapErpTransactionCode" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _sap_erp_transaction_code_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> SapErpTransactionCode: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SapErpTransactionCode instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _sap_erp_transaction_code_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SapErpTransactionCodeAttributes(AssetAttributes): + """SapErpTransactionCode-specific attributes for nested API format.""" + + sap_technical_name: Union[str, None, UnsetType] = UNSET + """Technical identifier for SAP data objects, used for integration and internal reference.""" + + sap_logical_name: Union[str, None, UnsetType] = UNSET + """Logical, business-friendly identifier for SAP data objects, aligned with business terminology and concepts.""" + + sap_package_name: Union[str, None, UnsetType] = UNSET + """Name of the SAP package, representing a logical grouping of related SAP data objects.""" + + sap_component_name: Union[str, None, UnsetType] = UNSET + """Name of the SAP component, representing a specific functional area in SAP.""" + + sap_data_type: Union[str, None, UnsetType] = UNSET + """SAP-specific data types""" + + sap_field_count: Union[int, None, UnsetType] = UNSET + """Represents the total number of fields, columns, or child assets present in a given SAP asset.""" + + sap_field_order: Union[int, None, UnsetType] = UNSET + """Indicates the sequential position of a field, column, or child asset within its parent SAP asset, starting from 1.""" + + +class SapErpTransactionCodeRelationshipAttributes(AssetRelationshipAttributes): + """SapErpTransactionCode-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + sap_erp_abap_program: Union[RelatedSapErpAbapProgram, None, UnsetType] = UNSET + """SAP ERP Transaction Codes that are associated with this SAP ERP ABAP Program.""" + + sap_erp_component: Union[RelatedSapErpComponent, None, UnsetType] = UNSET + """SAP ERP Transaction Codes that are associated with this SAP ERP Component.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SapErpTransactionCodeNested(AssetNested): + """SapErpTransactionCode in nested API format for high-performance serialization.""" + + attributes: Union[SapErpTransactionCodeAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + SapErpTransactionCodeRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + SapErpTransactionCodeRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SapErpTransactionCodeRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SAP_ERP_TRANSACTION_CODE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "sap_erp_abap_program", + "sap_erp_component", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_sap_erp_transaction_code_attrs( + attrs: SapErpTransactionCodeAttributes, obj: SapErpTransactionCode +) -> None: + """Populate SapErpTransactionCode-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.sap_technical_name = obj.sap_technical_name + attrs.sap_logical_name = obj.sap_logical_name + attrs.sap_package_name = obj.sap_package_name + attrs.sap_component_name = obj.sap_component_name + attrs.sap_data_type = obj.sap_data_type + attrs.sap_field_count = obj.sap_field_count + attrs.sap_field_order = obj.sap_field_order + + +def _extract_sap_erp_transaction_code_attrs( + attrs: SapErpTransactionCodeAttributes, +) -> dict: + """Extract all SapErpTransactionCode attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["sap_technical_name"] = attrs.sap_technical_name + result["sap_logical_name"] = attrs.sap_logical_name + result["sap_package_name"] = attrs.sap_package_name + result["sap_component_name"] = attrs.sap_component_name + result["sap_data_type"] = attrs.sap_data_type + result["sap_field_count"] = attrs.sap_field_count + result["sap_field_order"] = attrs.sap_field_order + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _sap_erp_transaction_code_to_nested( + sap_erp_transaction_code: SapErpTransactionCode, +) -> SapErpTransactionCodeNested: + """Convert flat SapErpTransactionCode to nested format.""" + attrs = SapErpTransactionCodeAttributes() + _populate_sap_erp_transaction_code_attrs(attrs, sap_erp_transaction_code) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + sap_erp_transaction_code, + _SAP_ERP_TRANSACTION_CODE_REL_FIELDS, + SapErpTransactionCodeRelationshipAttributes, + ) + return SapErpTransactionCodeNested( + guid=sap_erp_transaction_code.guid, + type_name=sap_erp_transaction_code.type_name, + status=sap_erp_transaction_code.status, + version=sap_erp_transaction_code.version, + create_time=sap_erp_transaction_code.create_time, + update_time=sap_erp_transaction_code.update_time, + created_by=sap_erp_transaction_code.created_by, + updated_by=sap_erp_transaction_code.updated_by, + classifications=sap_erp_transaction_code.classifications, + classification_names=sap_erp_transaction_code.classification_names, + meanings=sap_erp_transaction_code.meanings, + labels=sap_erp_transaction_code.labels, + business_attributes=sap_erp_transaction_code.business_attributes, + custom_attributes=sap_erp_transaction_code.custom_attributes, + pending_tasks=sap_erp_transaction_code.pending_tasks, + proxy=sap_erp_transaction_code.proxy, + is_incomplete=sap_erp_transaction_code.is_incomplete, + provenance_type=sap_erp_transaction_code.provenance_type, + home_id=sap_erp_transaction_code.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _sap_erp_transaction_code_from_nested( + nested: SapErpTransactionCodeNested, +) -> SapErpTransactionCode: + """Convert nested format to flat SapErpTransactionCode.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else SapErpTransactionCodeAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SAP_ERP_TRANSACTION_CODE_REL_FIELDS, + SapErpTransactionCodeRelationshipAttributes, + ) + return SapErpTransactionCode( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_sap_erp_transaction_code_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _sap_erp_transaction_code_to_nested_bytes( + sap_erp_transaction_code: SapErpTransactionCode, serde: Serde +) -> bytes: + """Convert flat SapErpTransactionCode to nested JSON bytes.""" + return serde.encode(_sap_erp_transaction_code_to_nested(sap_erp_transaction_code)) + + +def _sap_erp_transaction_code_from_nested_bytes( + data: bytes, serde: Serde +) -> SapErpTransactionCode: + """Convert nested JSON bytes to flat SapErpTransactionCode.""" + nested = serde.decode(data, SapErpTransactionCodeNested) + return _sap_erp_transaction_code_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +SapErpTransactionCode.SAP_TECHNICAL_NAME = KeywordField( + "sapTechnicalName", "sapTechnicalName" +) +SapErpTransactionCode.SAP_LOGICAL_NAME = KeywordField( + "sapLogicalName", "sapLogicalName" +) +SapErpTransactionCode.SAP_PACKAGE_NAME = KeywordField( + "sapPackageName", "sapPackageName" +) +SapErpTransactionCode.SAP_COMPONENT_NAME = KeywordField( + "sapComponentName", "sapComponentName" +) +SapErpTransactionCode.SAP_DATA_TYPE = KeywordField("sapDataType", "sapDataType") +SapErpTransactionCode.SAP_FIELD_COUNT = NumericField("sapFieldCount", "sapFieldCount") +SapErpTransactionCode.SAP_FIELD_ORDER = NumericField("sapFieldOrder", "sapFieldOrder") +SapErpTransactionCode.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SapErpTransactionCode.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +SapErpTransactionCode.ANOMALO_CHECKS = RelationField("anomaloChecks") +SapErpTransactionCode.APPLICATION = RelationField("application") +SapErpTransactionCode.APPLICATION_FIELD = RelationField("applicationField") +SapErpTransactionCode.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +SapErpTransactionCode.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +SapErpTransactionCode.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +SapErpTransactionCode.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +SapErpTransactionCode.METRICS = RelationField("metrics") +SapErpTransactionCode.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SapErpTransactionCode.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +SapErpTransactionCode.MEANINGS = RelationField("meanings") +SapErpTransactionCode.MC_MONITORS = RelationField("mcMonitors") +SapErpTransactionCode.MC_INCIDENTS = RelationField("mcIncidents") +SapErpTransactionCode.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SapErpTransactionCode.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SapErpTransactionCode.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SapErpTransactionCode.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SapErpTransactionCode.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +SapErpTransactionCode.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +SapErpTransactionCode.FILES = RelationField("files") +SapErpTransactionCode.LINKS = RelationField("links") +SapErpTransactionCode.README = RelationField("readme") +SapErpTransactionCode.SAP_ERP_ABAP_PROGRAM = RelationField("sapErpAbapProgram") +SapErpTransactionCode.SAP_ERP_COMPONENT = RelationField("sapErpComponent") +SapErpTransactionCode.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +SapErpTransactionCode.SODA_CHECKS = RelationField("sodaChecks") +SapErpTransactionCode.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SapErpTransactionCode.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/sap_erp_view.py b/pyatlan_v9/model/assets/sap_erp_view.py new file mode 100644 index 000000000..106bb2358 --- /dev/null +++ b/pyatlan_v9/model/assets/sap_erp_view.py @@ -0,0 +1,638 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SapErpView asset model with flattened inheritance. + +This module provides: +- SapErpView: Flat asset class (easy to use) +- SapErpViewAttributes: Nested attributes struct (extends AssetAttributes) +- SapErpViewNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .sap_related import RelatedSapErpColumn, RelatedSapErpComponent + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SapErpView(Asset): + """ + Instance of a SAP table in Atlan. + """ + + SAP_TYPE: ClassVar[Any] = None + SAP_DEFINITION: ClassVar[Any] = None + SAP_TECHNICAL_NAME: ClassVar[Any] = None + SAP_LOGICAL_NAME: ClassVar[Any] = None + SAP_PACKAGE_NAME: ClassVar[Any] = None + SAP_COMPONENT_NAME: ClassVar[Any] = None + SAP_DATA_TYPE: ClassVar[Any] = None + SAP_FIELD_COUNT: ClassVar[Any] = None + SAP_FIELD_ORDER: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SAP_ERP_COMPONENT: ClassVar[Any] = None + SAP_ERP_COLUMNS: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SapErpView" + + sap_type: Union[str, None, UnsetType] = UNSET + """Type of the SAP ERP View.""" + + sap_definition: Union[str, None, UnsetType] = UNSET + """Specifies the definition of the SAP ERP View""" + + sap_technical_name: Union[str, None, UnsetType] = UNSET + """Technical identifier for SAP data objects, used for integration and internal reference.""" + + sap_logical_name: Union[str, None, UnsetType] = UNSET + """Logical, business-friendly identifier for SAP data objects, aligned with business terminology and concepts.""" + + sap_package_name: Union[str, None, UnsetType] = UNSET + """Name of the SAP package, representing a logical grouping of related SAP data objects.""" + + sap_component_name: Union[str, None, UnsetType] = UNSET + """Name of the SAP component, representing a specific functional area in SAP.""" + + sap_data_type: Union[str, None, UnsetType] = UNSET + """SAP-specific data types""" + + sap_field_count: Union[int, None, UnsetType] = UNSET + """Represents the total number of fields, columns, or child assets present in a given SAP asset.""" + + sap_field_order: Union[int, None, UnsetType] = UNSET + """Indicates the sequential position of a field, column, or child asset within its parent SAP asset, starting from 1.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + sap_erp_component: Union[RelatedSapErpComponent, None, UnsetType] = UNSET + """SAP ERP Views that are associated with this SAP ERP Component.""" + + sap_erp_columns: Union[List[RelatedSapErpColumn], None, UnsetType] = UNSET + """SAP ERP Columns that exist within this view.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SapErpView" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _sap_erp_view_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> SapErpView: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SapErpView instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _sap_erp_view_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SapErpViewAttributes(AssetAttributes): + """SapErpView-specific attributes for nested API format.""" + + sap_type: Union[str, None, UnsetType] = UNSET + """Type of the SAP ERP View.""" + + sap_definition: Union[str, None, UnsetType] = UNSET + """Specifies the definition of the SAP ERP View""" + + sap_technical_name: Union[str, None, UnsetType] = UNSET + """Technical identifier for SAP data objects, used for integration and internal reference.""" + + sap_logical_name: Union[str, None, UnsetType] = UNSET + """Logical, business-friendly identifier for SAP data objects, aligned with business terminology and concepts.""" + + sap_package_name: Union[str, None, UnsetType] = UNSET + """Name of the SAP package, representing a logical grouping of related SAP data objects.""" + + sap_component_name: Union[str, None, UnsetType] = UNSET + """Name of the SAP component, representing a specific functional area in SAP.""" + + sap_data_type: Union[str, None, UnsetType] = UNSET + """SAP-specific data types""" + + sap_field_count: Union[int, None, UnsetType] = UNSET + """Represents the total number of fields, columns, or child assets present in a given SAP asset.""" + + sap_field_order: Union[int, None, UnsetType] = UNSET + """Indicates the sequential position of a field, column, or child asset within its parent SAP asset, starting from 1.""" + + +class SapErpViewRelationshipAttributes(AssetRelationshipAttributes): + """SapErpView-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + sap_erp_component: Union[RelatedSapErpComponent, None, UnsetType] = UNSET + """SAP ERP Views that are associated with this SAP ERP Component.""" + + sap_erp_columns: Union[List[RelatedSapErpColumn], None, UnsetType] = UNSET + """SAP ERP Columns that exist within this view.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SapErpViewNested(AssetNested): + """SapErpView in nested API format for high-performance serialization.""" + + attributes: Union[SapErpViewAttributes, UnsetType] = UNSET + relationship_attributes: Union[SapErpViewRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + SapErpViewRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SapErpViewRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SAP_ERP_VIEW_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "sap_erp_component", + "sap_erp_columns", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_sap_erp_view_attrs(attrs: SapErpViewAttributes, obj: SapErpView) -> None: + """Populate SapErpView-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.sap_type = obj.sap_type + attrs.sap_definition = obj.sap_definition + attrs.sap_technical_name = obj.sap_technical_name + attrs.sap_logical_name = obj.sap_logical_name + attrs.sap_package_name = obj.sap_package_name + attrs.sap_component_name = obj.sap_component_name + attrs.sap_data_type = obj.sap_data_type + attrs.sap_field_count = obj.sap_field_count + attrs.sap_field_order = obj.sap_field_order + + +def _extract_sap_erp_view_attrs(attrs: SapErpViewAttributes) -> dict: + """Extract all SapErpView attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["sap_type"] = attrs.sap_type + result["sap_definition"] = attrs.sap_definition + result["sap_technical_name"] = attrs.sap_technical_name + result["sap_logical_name"] = attrs.sap_logical_name + result["sap_package_name"] = attrs.sap_package_name + result["sap_component_name"] = attrs.sap_component_name + result["sap_data_type"] = attrs.sap_data_type + result["sap_field_count"] = attrs.sap_field_count + result["sap_field_order"] = attrs.sap_field_order + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _sap_erp_view_to_nested(sap_erp_view: SapErpView) -> SapErpViewNested: + """Convert flat SapErpView to nested format.""" + attrs = SapErpViewAttributes() + _populate_sap_erp_view_attrs(attrs, sap_erp_view) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + sap_erp_view, _SAP_ERP_VIEW_REL_FIELDS, SapErpViewRelationshipAttributes + ) + return SapErpViewNested( + guid=sap_erp_view.guid, + type_name=sap_erp_view.type_name, + status=sap_erp_view.status, + version=sap_erp_view.version, + create_time=sap_erp_view.create_time, + update_time=sap_erp_view.update_time, + created_by=sap_erp_view.created_by, + updated_by=sap_erp_view.updated_by, + classifications=sap_erp_view.classifications, + classification_names=sap_erp_view.classification_names, + meanings=sap_erp_view.meanings, + labels=sap_erp_view.labels, + business_attributes=sap_erp_view.business_attributes, + custom_attributes=sap_erp_view.custom_attributes, + pending_tasks=sap_erp_view.pending_tasks, + proxy=sap_erp_view.proxy, + is_incomplete=sap_erp_view.is_incomplete, + provenance_type=sap_erp_view.provenance_type, + home_id=sap_erp_view.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _sap_erp_view_from_nested(nested: SapErpViewNested) -> SapErpView: + """Convert nested format to flat SapErpView.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else SapErpViewAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SAP_ERP_VIEW_REL_FIELDS, + SapErpViewRelationshipAttributes, + ) + return SapErpView( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_sap_erp_view_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _sap_erp_view_to_nested_bytes(sap_erp_view: SapErpView, serde: Serde) -> bytes: + """Convert flat SapErpView to nested JSON bytes.""" + return serde.encode(_sap_erp_view_to_nested(sap_erp_view)) + + +def _sap_erp_view_from_nested_bytes(data: bytes, serde: Serde) -> SapErpView: + """Convert nested JSON bytes to flat SapErpView.""" + nested = serde.decode(data, SapErpViewNested) + return _sap_erp_view_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +SapErpView.SAP_TYPE = KeywordField("sapType", "sapType") +SapErpView.SAP_DEFINITION = KeywordField("sapDefinition", "sapDefinition") +SapErpView.SAP_TECHNICAL_NAME = KeywordField("sapTechnicalName", "sapTechnicalName") +SapErpView.SAP_LOGICAL_NAME = KeywordField("sapLogicalName", "sapLogicalName") +SapErpView.SAP_PACKAGE_NAME = KeywordField("sapPackageName", "sapPackageName") +SapErpView.SAP_COMPONENT_NAME = KeywordField("sapComponentName", "sapComponentName") +SapErpView.SAP_DATA_TYPE = KeywordField("sapDataType", "sapDataType") +SapErpView.SAP_FIELD_COUNT = NumericField("sapFieldCount", "sapFieldCount") +SapErpView.SAP_FIELD_ORDER = NumericField("sapFieldOrder", "sapFieldOrder") +SapErpView.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SapErpView.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +SapErpView.ANOMALO_CHECKS = RelationField("anomaloChecks") +SapErpView.APPLICATION = RelationField("application") +SapErpView.APPLICATION_FIELD = RelationField("applicationField") +SapErpView.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +SapErpView.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +SapErpView.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +SapErpView.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +SapErpView.METRICS = RelationField("metrics") +SapErpView.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SapErpView.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +SapErpView.MEANINGS = RelationField("meanings") +SapErpView.MC_MONITORS = RelationField("mcMonitors") +SapErpView.MC_INCIDENTS = RelationField("mcIncidents") +SapErpView.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SapErpView.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SapErpView.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SapErpView.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SapErpView.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +SapErpView.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +SapErpView.FILES = RelationField("files") +SapErpView.LINKS = RelationField("links") +SapErpView.README = RelationField("readme") +SapErpView.SAP_ERP_COMPONENT = RelationField("sapErpComponent") +SapErpView.SAP_ERP_COLUMNS = RelationField("sapErpColumns") +SapErpView.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +SapErpView.SODA_CHECKS = RelationField("sodaChecks") +SapErpView.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SapErpView.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/sap_related.py b/pyatlan_v9/model/assets/sap_related.py new file mode 100644 index 000000000..d7b6d070a --- /dev/null +++ b/pyatlan_v9/model/assets/sap_related.py @@ -0,0 +1,283 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for SAP module. + +This module contains all Related{Type} classes for the SAP type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedCatalog +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedSAP", + "RelatedSapErpTransactionCode", + "RelatedSapErpView", + "RelatedSapErpCdsView", + "RelatedSapErpColumn", + "RelatedSapErpComponent", + "RelatedSapErpFunctionModule", + "RelatedSapErpTable", + "RelatedSapErpAbapProgram", +] + + +class RelatedSAP(RelatedCatalog): + """ + Related entity reference for SAP assets. + + Extends RelatedCatalog with SAP-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SAP" so it serializes correctly + + sap_technical_name: Union[str, None, UnsetType] = UNSET + """Technical identifier for SAP data objects, used for integration and internal reference.""" + + sap_logical_name: Union[str, None, UnsetType] = UNSET + """Logical, business-friendly identifier for SAP data objects, aligned with business terminology and concepts.""" + + sap_package_name: Union[str, None, UnsetType] = UNSET + """Name of the SAP package, representing a logical grouping of related SAP data objects.""" + + sap_component_name: Union[str, None, UnsetType] = UNSET + """Name of the SAP component, representing a specific functional area in SAP.""" + + sap_data_type: Union[str, None, UnsetType] = UNSET + """SAP-specific data types""" + + sap_field_count: Union[int, None, UnsetType] = UNSET + """Represents the total number of fields, columns, or child assets present in a given SAP asset.""" + + sap_field_order: Union[int, None, UnsetType] = UNSET + """Indicates the sequential position of a field, column, or child asset within its parent SAP asset, starting from 1.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SAP" + + +class RelatedSapErpTransactionCode(RelatedSAP): + """ + Related entity reference for SapErpTransactionCode assets. + + Extends RelatedSAP with SapErpTransactionCode-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SapErpTransactionCode" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SapErpTransactionCode" + + +class RelatedSapErpView(RelatedSAP): + """ + Related entity reference for SapErpView assets. + + Extends RelatedSAP with SapErpView-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SapErpView" so it serializes correctly + + sap_type: Union[str, None, UnsetType] = UNSET + """Type of the SAP ERP View.""" + + sap_definition: Union[str, None, UnsetType] = UNSET + """Specifies the definition of the SAP ERP View""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SapErpView" + + +class RelatedSapErpCdsView(RelatedSAP): + """ + Related entity reference for SapErpCdsView assets. + + Extends RelatedSAP with SapErpCdsView-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SapErpCdsView" so it serializes correctly + + sap_technical_name: Union[str, None, UnsetType] = UNSET + """The technical database view name of the SAP ERP CDS View.""" + + sap_source_name: Union[str, None, UnsetType] = UNSET + """The source name of the SAP ERP CDS View Definition.""" + + sap_source_type: Union[str, None, UnsetType] = UNSET + """The source type of the SAP ERP CDS View Definition.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SapErpCdsView" + + +class RelatedSapErpColumn(RelatedSAP): + """ + Related entity reference for SapErpColumn assets. + + Extends RelatedSAP with SapErpColumn-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SapErpColumn" so it serializes correctly + + sap_data_element: Union[str, None, UnsetType] = UNSET + """Represents the SAP ERP data element, providing semantic information about the column.""" + + sap_logical_data_type: Union[str, None, UnsetType] = UNSET + """Specifies the logical data type of values in this SAP ERP column""" + + sap_length: Union[str, None, UnsetType] = UNSET + """Indicates the maximum length of the values that the SAP ERP column can store.""" + + sap_decimals: Union[str, None, UnsetType] = UNSET + """Defines the number of decimal places allowed for numeric values in the SAP ERP column.""" + + sap_is_primary: Union[bool, None, UnsetType] = UNSET + """When true, this column is the primary key for the SAP ERP table or view.""" + + sap_is_foreign: Union[bool, None, UnsetType] = UNSET + """When true, this column is the foreign key for the SAP ERP table or view.""" + + sap_is_mandatory: Union[bool, None, UnsetType] = UNSET + """When true, the values in this column can be null.""" + + sap_erp_table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the SAP ERP table in which this column asset exists.""" + + sap_erp_table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the SAP ERP table in which this SQL asset exists.""" + + sap_erp_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the SAP ERP view in which this column asset exists.""" + + sap_erp_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the SAP ERP view in which this column asset exists.""" + + sap_erp_cds_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the SAP ERP CDS view in which this column asset exists.""" + + sap_erp_cds_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the SAP ERP CDS view in which this column asset exists.""" + + sap_check_table_name: Union[str, None, UnsetType] = UNSET + """Defines the SAP ERP table name used as a foreign key reference to validate permissible values for this column.""" + + sap_check_table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the SAP ERP Table used as a foreign key reference to validate permissible values for this column.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SapErpColumn" + + +class RelatedSapErpComponent(RelatedSAP): + """ + Related entity reference for SapErpComponent assets. + + Extends RelatedSAP with SapErpComponent-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SapErpComponent" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SapErpComponent" + + +class RelatedSapErpFunctionModule(RelatedSAP): + """ + Related entity reference for SapErpFunctionModule assets. + + Extends RelatedSAP with SapErpFunctionModule-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SapErpFunctionModule" so it serializes correctly + + sap_group: Union[str, None, UnsetType] = UNSET + """Represents the group to which the SAP ERP function module belongs.""" + + sap_erp_function_module_import_params: Union[ + List[Dict[str, str]], None, UnsetType + ] = UNSET + """Parameters imported by the SAP ERP function module, defined as key-value pairs.""" + + sap_import_params_count: Union[int, None, UnsetType] = UNSET + """Represents the total number of Import Parameters in a given SAP ERP Function Module.""" + + sap_erp_function_module_export_params: Union[ + List[Dict[str, str]], None, UnsetType + ] = UNSET + """Parameters exported by the SAP ERP function module, defined as key-value pairs.""" + + sap_export_params_count: Union[int, None, UnsetType] = UNSET + """Represents the total number of Export Parameters in a given SAP ERP Function Module.""" + + sap_erp_function_exception_list: Union[List[Dict[str, str]], None, UnsetType] = ( + UNSET + ) + """List of exceptions raised by the SAP ERP function module, defined as key-value pairs.""" + + sap_erp_function_exception_list_count: Union[int, None, UnsetType] = UNSET + """Represents the total number of Exceptions in a given SAP ERP Function Module.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SapErpFunctionModule" + + +class RelatedSapErpTable(RelatedSAP): + """ + Related entity reference for SapErpTable assets. + + Extends RelatedSAP with SapErpTable-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SapErpTable" so it serializes correctly + + sap_erp_table_type: Union[str, None, UnsetType] = UNSET + """Type of the SAP ERP table.""" + + sap_erp_table_delivery_class: Union[str, None, UnsetType] = UNSET + """Defines the delivery class of the SAP ERP table, determining how the table's data is transported and managed during system updates.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SapErpTable" + + +class RelatedSapErpAbapProgram(RelatedSAP): + """ + Related entity reference for SapErpAbapProgram assets. + + Extends RelatedSAP with SapErpAbapProgram-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SapErpAbapProgram" so it serializes correctly + + sap_erp_abap_program_type: Union[str, None, UnsetType] = UNSET + """Specifies the type of ABAP program in SAP ERP (e.g., Report, Module Pool, Function Group).""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SapErpAbapProgram" diff --git a/pyatlan_v9/model/assets/schema.py b/pyatlan_v9/model/assets/schema.py new file mode 100644 index 000000000..b0e1cfe4a --- /dev/null +++ b/pyatlan_v9/model/assets/schema.py @@ -0,0 +1,1130 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Schema asset model with flattened inheritance. + +This module provides: +- Schema: Flat asset class (easy to use) +- SchemaAttributes: Nested attributes struct (extends AssetAttributes) +- SchemaNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .databricks_related import RelatedDatabricksAIModelContext, RelatedDatabricksVolume +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .snowflake_related import ( + RelatedSnowflakeAIModelContext, + RelatedSnowflakeDynamicTable, + RelatedSnowflakePipe, + RelatedSnowflakeSemanticLogicalTable, + RelatedSnowflakeSemanticView, + RelatedSnowflakeStage, + RelatedSnowflakeStream, + RelatedSnowflakeTag, +) +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .sql_related import ( + RelatedCalculationView, + RelatedDatabase, + RelatedFunction, + RelatedMaterialisedView, + RelatedProcedure, + RelatedTable, + RelatedView, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Schema(Asset): + """ + Instance of a database schema in Atlan. + """ + + TABLE_COUNT: ClassVar[Any] = None + SQL_EXTERNAL_LOCATION: ClassVar[Any] = None + VIEWS_COUNT: ClassVar[Any] = None + LINKED_SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DATABRICKS_AI_MODEL_CONTEXTS: ClassVar[Any] = None + DATABRICKS_VOLUMES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + CALCULATION_VIEWS: ClassVar[Any] = None + FUNCTIONS: ClassVar[Any] = None + MATERIALISED_VIEWS: ClassVar[Any] = None + PROCEDURES: ClassVar[Any] = None + DATABASE: ClassVar[Any] = None + TABLES: ClassVar[Any] = None + VIEWS: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_DYNAMIC_TABLES: ClassVar[Any] = None + SNOWFLAKE_PIPES: ClassVar[Any] = None + SNOWFLAKE_STAGES: ClassVar[Any] = None + SNOWFLAKE_STREAMS: ClassVar[Any] = None + SNOWFLAKE_TAGS: ClassVar[Any] = None + SNOWFLAKE_AI_MODEL_CONTEXTS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_VIEWS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Schema" + + table_count: Union[int, None, UnsetType] = UNSET + """Number of tables in this schema.""" + + sql_external_location: Union[str, None, UnsetType] = UNSET + """External location of this schema, for example: an S3 object location.""" + + views_count: Union[int, None, UnsetType] = UNSET + """Number of views in this schema.""" + + linked_schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the Linked Schema on which this Schema is dependent. This concept is mostly applicable for linked datasets/datasource in Google BigQuery via Analytics Hub Listing""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + databricks_ai_model_contexts: Union[ + List[RelatedDatabricksAIModelContext], None, UnsetType + ] = msgspec.field(default=UNSET, name="databricksAIModelContexts") + """Contexts contained within the schema.""" + + databricks_volumes: Union[List[RelatedDatabricksVolume], None, UnsetType] = UNSET + """Volume contained within the schema.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + calculation_views: Union[List[RelatedCalculationView], None, UnsetType] = UNSET + """Calculation views that exist within this schema.""" + + functions: Union[List[RelatedFunction], None, UnsetType] = UNSET + """Functions that exist within this schema.""" + + materialised_views: Union[List[RelatedMaterialisedView], None, UnsetType] = UNSET + """Materialized views that exist within this schema.""" + + procedures: Union[List[RelatedProcedure], None, UnsetType] = UNSET + """Stored procedures that exist within this schema.""" + + database: Union[RelatedDatabase, None, UnsetType] = UNSET + """Database in which this schema exists.""" + + tables: Union[List[RelatedTable], None, UnsetType] = UNSET + """Tables that exist within this schema.""" + + views: Union[List[RelatedView], None, UnsetType] = UNSET + """Views that exist within this schema.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_dynamic_tables: Union[ + List[RelatedSnowflakeDynamicTable], None, UnsetType + ] = UNSET + """Snowflake dynamic tables that exist within this schema.""" + + snowflake_pipes: Union[List[RelatedSnowflakePipe], None, UnsetType] = UNSET + """Snowflake pipes that exist within this schema.""" + + snowflake_stages: Union[List[RelatedSnowflakeStage], None, UnsetType] = UNSET + """Collection of Snowflake stages that are defined and contained within this schema, representing staging areas for data loading and unloading operations.""" + + snowflake_streams: Union[List[RelatedSnowflakeStream], None, UnsetType] = UNSET + """Snowflake streams that exist within this schema.""" + + snowflake_tags: Union[List[RelatedSnowflakeTag], None, UnsetType] = UNSET + """Snowflake tags that exist within this schema.""" + + snowflake_ai_model_contexts: Union[ + List[RelatedSnowflakeAIModelContext], None, UnsetType + ] = msgspec.field(default=UNSET, name="snowflakeAIModelContexts") + """Contexts contained within the schema.""" + + snowflake_semantic_views: Union[ + List[RelatedSnowflakeSemanticView], None, UnsetType + ] = UNSET + """Snowflake semantic views contained in the schema.""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Schema" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + database_qualified_name: str, + database_name: str | None = None, + connection_qualified_name: str | None = None, + ) -> "Schema": + """ + Create a new Schema asset with auto-derived fields. + + Args: + name: Simple name of the schema + database_qualified_name: Unique name of the database in which this schema exists + database_name: Simple name of the database (auto-derived if not provided) + connection_qualified_name: Unique name of the connection (auto-derived if not provided) + + Returns: + New Schema instance with all fields populated + + Raises: + ValueError: If required parameters are missing or invalid + """ + validate_required_fields( + ["name", "database_qualified_name"], [name, database_qualified_name] + ) + + # Validate database_qualified_name format: default/connector/connection_id/database + fields = database_qualified_name.split("/") + if len(fields) != 4: + raise ValueError( + f"Invalid database_qualified_name: {database_qualified_name}. " + "Expected format: default/connector/connection_id/database" + ) + + # Derive other fields from database_qualified_name + connector_name = fields[1] + connection_qn = ( + connection_qualified_name or f"{fields[0]}/{fields[1]}/{fields[2]}" + ) + db_name = database_name or fields[3] + qualified_name = f"{database_qualified_name}/{name}" + + return cls( + name=name, + qualified_name=qualified_name, + database_name=db_name, + database_qualified_name=database_qualified_name, + connector_name=connector_name, + connection_qualified_name=connection_qn, + database=RelatedDatabase(qualified_name=database_qualified_name), + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "Schema": + """ + Create a Schema instance for updating an existing asset. + + Args: + qualified_name: Unique name of the schema to update + name: Simple name of the schema + + Returns: + Schema instance configured for updates + + Raises: + ValueError: If required parameters are missing + """ + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "Schema": + """ + Return a Schema with only required fields for reference. + + Returns: + Schema instance with only qualified_name and name set + """ + return Schema(qualified_name=self.qualified_name, name=self.name) + + @classmethod + def create(cls, **kwargs) -> "Schema": + """Backward compatibility alias for creator().""" + return cls.creator(**kwargs) + + @classmethod + def create_for_modification(cls, **kwargs) -> "Schema": + """Backward compatibility alias for updater().""" + return cls.updater(**kwargs) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _schema__to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Schema: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Schema instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _schema__from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SchemaAttributes(AssetAttributes): + """Schema-specific attributes for nested API format.""" + + table_count: Union[int, None, UnsetType] = UNSET + """Number of tables in this schema.""" + + sql_external_location: Union[str, None, UnsetType] = UNSET + """External location of this schema, for example: an S3 object location.""" + + views_count: Union[int, None, UnsetType] = UNSET + """Number of views in this schema.""" + + linked_schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the Linked Schema on which this Schema is dependent. This concept is mostly applicable for linked datasets/datasource in Google BigQuery via Analytics Hub Listing""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + +class SchemaRelationshipAttributes(AssetRelationshipAttributes): + """Schema-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + databricks_ai_model_contexts: Union[ + List[RelatedDatabricksAIModelContext], None, UnsetType + ] = msgspec.field(default=UNSET, name="databricksAIModelContexts") + """Contexts contained within the schema.""" + + databricks_volumes: Union[List[RelatedDatabricksVolume], None, UnsetType] = UNSET + """Volume contained within the schema.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + calculation_views: Union[List[RelatedCalculationView], None, UnsetType] = UNSET + """Calculation views that exist within this schema.""" + + functions: Union[List[RelatedFunction], None, UnsetType] = UNSET + """Functions that exist within this schema.""" + + materialised_views: Union[List[RelatedMaterialisedView], None, UnsetType] = UNSET + """Materialized views that exist within this schema.""" + + procedures: Union[List[RelatedProcedure], None, UnsetType] = UNSET + """Stored procedures that exist within this schema.""" + + database: Union[RelatedDatabase, None, UnsetType] = UNSET + """Database in which this schema exists.""" + + tables: Union[List[RelatedTable], None, UnsetType] = UNSET + """Tables that exist within this schema.""" + + views: Union[List[RelatedView], None, UnsetType] = UNSET + """Views that exist within this schema.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_dynamic_tables: Union[ + List[RelatedSnowflakeDynamicTable], None, UnsetType + ] = UNSET + """Snowflake dynamic tables that exist within this schema.""" + + snowflake_pipes: Union[List[RelatedSnowflakePipe], None, UnsetType] = UNSET + """Snowflake pipes that exist within this schema.""" + + snowflake_stages: Union[List[RelatedSnowflakeStage], None, UnsetType] = UNSET + """Collection of Snowflake stages that are defined and contained within this schema, representing staging areas for data loading and unloading operations.""" + + snowflake_streams: Union[List[RelatedSnowflakeStream], None, UnsetType] = UNSET + """Snowflake streams that exist within this schema.""" + + snowflake_tags: Union[List[RelatedSnowflakeTag], None, UnsetType] = UNSET + """Snowflake tags that exist within this schema.""" + + snowflake_ai_model_contexts: Union[ + List[RelatedSnowflakeAIModelContext], None, UnsetType + ] = msgspec.field(default=UNSET, name="snowflakeAIModelContexts") + """Contexts contained within the schema.""" + + snowflake_semantic_views: Union[ + List[RelatedSnowflakeSemanticView], None, UnsetType + ] = UNSET + """Snowflake semantic views contained in the schema.""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SchemaNested(AssetNested): + """Schema in nested API format for high-performance serialization.""" + + attributes: Union[SchemaAttributes, UnsetType] = UNSET + relationship_attributes: Union[SchemaRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[SchemaRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[SchemaRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SCHEMA_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "databricks_ai_model_contexts", + "databricks_volumes", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "calculation_views", + "functions", + "materialised_views", + "procedures", + "database", + "tables", + "views", + "schema_registry_subjects", + "snowflake_dynamic_tables", + "snowflake_pipes", + "snowflake_stages", + "snowflake_streams", + "snowflake_tags", + "snowflake_ai_model_contexts", + "snowflake_semantic_views", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_schema__attrs(attrs: SchemaAttributes, obj: Schema) -> None: + """Populate Schema-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.table_count = obj.table_count + attrs.sql_external_location = obj.sql_external_location + attrs.views_count = obj.views_count + attrs.linked_schema_qualified_name = obj.linked_schema_qualified_name + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + + +def _extract_schema__attrs(attrs: SchemaAttributes) -> dict: + """Extract all Schema attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["table_count"] = attrs.table_count + result["sql_external_location"] = attrs.sql_external_location + result["views_count"] = attrs.views_count + result["linked_schema_qualified_name"] = attrs.linked_schema_qualified_name + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _schema__to_nested(schema_: Schema) -> SchemaNested: + """Convert flat Schema to nested format.""" + attrs = SchemaAttributes() + _populate_schema__attrs(attrs, schema_) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + schema_, _SCHEMA_REL_FIELDS, SchemaRelationshipAttributes + ) + return SchemaNested( + guid=schema_.guid, + type_name=schema_.type_name, + status=schema_.status, + version=schema_.version, + create_time=schema_.create_time, + update_time=schema_.update_time, + created_by=schema_.created_by, + updated_by=schema_.updated_by, + classifications=schema_.classifications, + classification_names=schema_.classification_names, + meanings=schema_.meanings, + labels=schema_.labels, + business_attributes=schema_.business_attributes, + custom_attributes=schema_.custom_attributes, + pending_tasks=schema_.pending_tasks, + proxy=schema_.proxy, + is_incomplete=schema_.is_incomplete, + provenance_type=schema_.provenance_type, + home_id=schema_.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _schema__from_nested(nested: SchemaNested) -> Schema: + """Convert nested format to flat Schema.""" + attrs = nested.attributes if nested.attributes is not UNSET else SchemaAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SCHEMA_REL_FIELDS, + SchemaRelationshipAttributes, + ) + return Schema( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_schema__attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _schema__to_nested_bytes(schema_: Schema, serde: Serde) -> bytes: + """Convert flat Schema to nested JSON bytes.""" + return serde.encode(_schema__to_nested(schema_)) + + +def _schema__from_nested_bytes(data: bytes, serde: Serde) -> Schema: + """Convert nested JSON bytes to flat Schema.""" + nested = serde.decode(data, SchemaNested) + return _schema__from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, +) + +Schema.TABLE_COUNT = NumericField("tableCount", "tableCount") +Schema.SQL_EXTERNAL_LOCATION = KeywordField( + "sqlExternalLocation", "sqlExternalLocation" +) +Schema.VIEWS_COUNT = NumericField("viewsCount", "viewsCount") +Schema.LINKED_SCHEMA_QUALIFIED_NAME = KeywordField( + "linkedSchemaQualifiedName", "linkedSchemaQualifiedName" +) +Schema.QUERY_COUNT = NumericField("queryCount", "queryCount") +Schema.QUERY_USER_COUNT = NumericField("queryUserCount", "queryUserCount") +Schema.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +Schema.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +Schema.DATABASE_NAME = KeywordField("databaseName", "databaseName") +Schema.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +Schema.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +Schema.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +Schema.TABLE_NAME = KeywordField("tableName", "tableName") +Schema.TABLE_QUALIFIED_NAME = KeywordField("tableQualifiedName", "tableQualifiedName") +Schema.VIEW_NAME = KeywordField("viewName", "viewName") +Schema.VIEW_QUALIFIED_NAME = KeywordField("viewQualifiedName", "viewQualifiedName") +Schema.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +Schema.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +Schema.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +Schema.LAST_PROFILED_AT = NumericField("lastProfiledAt", "lastProfiledAt") +Schema.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +Schema.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +Schema.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Schema.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Schema.ANOMALO_CHECKS = RelationField("anomaloChecks") +Schema.APPLICATION = RelationField("application") +Schema.APPLICATION_FIELD = RelationField("applicationField") +Schema.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Schema.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Schema.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Schema.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Schema.METRICS = RelationField("metrics") +Schema.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Schema.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Schema.DATABRICKS_AI_MODEL_CONTEXTS = RelationField("databricksAIModelContexts") +Schema.DATABRICKS_VOLUMES = RelationField("databricksVolumes") +Schema.DBT_MODELS = RelationField("dbtModels") +Schema.SQL_DBT_MODELS = RelationField("sqlDbtModels") +Schema.DBT_TESTS = RelationField("dbtTests") +Schema.DBT_SOURCES = RelationField("dbtSources") +Schema.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +Schema.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +Schema.MEANINGS = RelationField("meanings") +Schema.MC_MONITORS = RelationField("mcMonitors") +Schema.MC_INCIDENTS = RelationField("mcIncidents") +Schema.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Schema.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Schema.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Schema.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Schema.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Schema.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Schema.FILES = RelationField("files") +Schema.LINKS = RelationField("links") +Schema.README = RelationField("readme") +Schema.CALCULATION_VIEWS = RelationField("calculationViews") +Schema.FUNCTIONS = RelationField("functions") +Schema.MATERIALISED_VIEWS = RelationField("materialisedViews") +Schema.PROCEDURES = RelationField("procedures") +Schema.DATABASE = RelationField("database") +Schema.TABLES = RelationField("tables") +Schema.VIEWS = RelationField("views") +Schema.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Schema.SNOWFLAKE_DYNAMIC_TABLES = RelationField("snowflakeDynamicTables") +Schema.SNOWFLAKE_PIPES = RelationField("snowflakePipes") +Schema.SNOWFLAKE_STAGES = RelationField("snowflakeStages") +Schema.SNOWFLAKE_STREAMS = RelationField("snowflakeStreams") +Schema.SNOWFLAKE_TAGS = RelationField("snowflakeTags") +Schema.SNOWFLAKE_AI_MODEL_CONTEXTS = RelationField("snowflakeAIModelContexts") +Schema.SNOWFLAKE_SEMANTIC_VIEWS = RelationField("snowflakeSemanticViews") +Schema.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +Schema.SODA_CHECKS = RelationField("sodaChecks") +Schema.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Schema.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/schema_registry.py b/pyatlan_v9/model/assets/schema_registry.py new file mode 100644 index 000000000..f54f3476d --- /dev/null +++ b/pyatlan_v9/model/assets/schema_registry.py @@ -0,0 +1,564 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SchemaRegistry asset model with flattened inheritance. + +This module provides: +- SchemaRegistry: Flat asset class (easy to use) +- SchemaRegistryAttributes: Nested attributes struct (extends AssetAttributes) +- SchemaRegistryNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .schema_registry_related import RelatedSchemaRegistrySubject + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SchemaRegistry(Asset): + """ + Instance of a schema registry in Atlan. + """ + + SCHEMA_REGISTRY_SCHEMA_TYPE: ClassVar[Any] = None + SCHEMA_REGISTRY_SCHEMA_ID: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SchemaRegistry" + + schema_registry_schema_type: Union[str, None, UnsetType] = UNSET + """Type of language or specification used to define the schema, for example: JSON, Protobuf, etc.""" + + schema_registry_schema_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for schema definition set by the schema registry.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SchemaRegistry" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _schema_registry_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> SchemaRegistry: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SchemaRegistry instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _schema_registry_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SchemaRegistryAttributes(AssetAttributes): + """SchemaRegistry-specific attributes for nested API format.""" + + schema_registry_schema_type: Union[str, None, UnsetType] = UNSET + """Type of language or specification used to define the schema, for example: JSON, Protobuf, etc.""" + + schema_registry_schema_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for schema definition set by the schema registry.""" + + +class SchemaRegistryRelationshipAttributes(AssetRelationshipAttributes): + """SchemaRegistry-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SchemaRegistryNested(AssetNested): + """SchemaRegistry in nested API format for high-performance serialization.""" + + attributes: Union[SchemaRegistryAttributes, UnsetType] = UNSET + relationship_attributes: Union[SchemaRegistryRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + SchemaRegistryRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SchemaRegistryRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SCHEMA_REGISTRY_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_schema_registry_attrs( + attrs: SchemaRegistryAttributes, obj: SchemaRegistry +) -> None: + """Populate SchemaRegistry-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.schema_registry_schema_type = obj.schema_registry_schema_type + attrs.schema_registry_schema_id = obj.schema_registry_schema_id + + +def _extract_schema_registry_attrs(attrs: SchemaRegistryAttributes) -> dict: + """Extract all SchemaRegistry attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["schema_registry_schema_type"] = attrs.schema_registry_schema_type + result["schema_registry_schema_id"] = attrs.schema_registry_schema_id + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _schema_registry_to_nested(schema_registry: SchemaRegistry) -> SchemaRegistryNested: + """Convert flat SchemaRegistry to nested format.""" + attrs = SchemaRegistryAttributes() + _populate_schema_registry_attrs(attrs, schema_registry) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + schema_registry, + _SCHEMA_REGISTRY_REL_FIELDS, + SchemaRegistryRelationshipAttributes, + ) + return SchemaRegistryNested( + guid=schema_registry.guid, + type_name=schema_registry.type_name, + status=schema_registry.status, + version=schema_registry.version, + create_time=schema_registry.create_time, + update_time=schema_registry.update_time, + created_by=schema_registry.created_by, + updated_by=schema_registry.updated_by, + classifications=schema_registry.classifications, + classification_names=schema_registry.classification_names, + meanings=schema_registry.meanings, + labels=schema_registry.labels, + business_attributes=schema_registry.business_attributes, + custom_attributes=schema_registry.custom_attributes, + pending_tasks=schema_registry.pending_tasks, + proxy=schema_registry.proxy, + is_incomplete=schema_registry.is_incomplete, + provenance_type=schema_registry.provenance_type, + home_id=schema_registry.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _schema_registry_from_nested(nested: SchemaRegistryNested) -> SchemaRegistry: + """Convert nested format to flat SchemaRegistry.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else SchemaRegistryAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SCHEMA_REGISTRY_REL_FIELDS, + SchemaRegistryRelationshipAttributes, + ) + return SchemaRegistry( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_schema_registry_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _schema_registry_to_nested_bytes( + schema_registry: SchemaRegistry, serde: Serde +) -> bytes: + """Convert flat SchemaRegistry to nested JSON bytes.""" + return serde.encode(_schema_registry_to_nested(schema_registry)) + + +def _schema_registry_from_nested_bytes(data: bytes, serde: Serde) -> SchemaRegistry: + """Convert nested JSON bytes to flat SchemaRegistry.""" + nested = serde.decode(data, SchemaRegistryNested) + return _schema_registry_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +SchemaRegistry.SCHEMA_REGISTRY_SCHEMA_TYPE = KeywordField( + "schemaRegistrySchemaType", "schemaRegistrySchemaType" +) +SchemaRegistry.SCHEMA_REGISTRY_SCHEMA_ID = KeywordField( + "schemaRegistrySchemaId", "schemaRegistrySchemaId" +) +SchemaRegistry.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SchemaRegistry.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +SchemaRegistry.ANOMALO_CHECKS = RelationField("anomaloChecks") +SchemaRegistry.APPLICATION = RelationField("application") +SchemaRegistry.APPLICATION_FIELD = RelationField("applicationField") +SchemaRegistry.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +SchemaRegistry.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +SchemaRegistry.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +SchemaRegistry.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +SchemaRegistry.METRICS = RelationField("metrics") +SchemaRegistry.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SchemaRegistry.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +SchemaRegistry.MEANINGS = RelationField("meanings") +SchemaRegistry.MC_MONITORS = RelationField("mcMonitors") +SchemaRegistry.MC_INCIDENTS = RelationField("mcIncidents") +SchemaRegistry.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SchemaRegistry.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SchemaRegistry.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SchemaRegistry.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SchemaRegistry.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +SchemaRegistry.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +SchemaRegistry.FILES = RelationField("files") +SchemaRegistry.LINKS = RelationField("links") +SchemaRegistry.README = RelationField("readme") +SchemaRegistry.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +SchemaRegistry.SODA_CHECKS = RelationField("sodaChecks") +SchemaRegistry.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SchemaRegistry.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/schema_registry_related.py b/pyatlan_v9/model/assets/schema_registry_related.py new file mode 100644 index 000000000..39d5dcc4f --- /dev/null +++ b/pyatlan_v9/model/assets/schema_registry_related.py @@ -0,0 +1,82 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for SchemaRegistry module. + +This module contains all Related{Type} classes for the SchemaRegistry type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import List, Union + +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedCatalog +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedSchemaRegistry", + "RelatedSchemaRegistrySubject", +] + + +class RelatedSchemaRegistry(RelatedCatalog): + """ + Related entity reference for SchemaRegistry assets. + + Extends RelatedCatalog with SchemaRegistry-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SchemaRegistry" so it serializes correctly + + schema_registry_schema_type: Union[str, None, UnsetType] = UNSET + """Type of language or specification used to define the schema, for example: JSON, Protobuf, etc.""" + + schema_registry_schema_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for schema definition set by the schema registry.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SchemaRegistry" + + +class RelatedSchemaRegistrySubject(RelatedSchemaRegistry): + """ + Related entity reference for SchemaRegistrySubject assets. + + Extends RelatedSchemaRegistry with SchemaRegistrySubject-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SchemaRegistrySubject" so it serializes correctly + + schema_registry_subject_base_name: Union[str, None, UnsetType] = UNSET + """Base name of the subject, without -key, -value prefixes.""" + + schema_registry_subject_is_key_schema: Union[bool, None, UnsetType] = UNSET + """Whether the subject is a schema for the keys of the messages (true) or not (false).""" + + schema_registry_subject_schema_compatibility: Union[str, None, UnsetType] = UNSET + """Compatibility of the schema across versions.""" + + schema_registry_subject_latest_schema_version: Union[str, None, UnsetType] = UNSET + """Latest schema version of the subject.""" + + schema_registry_subject_latest_schema_definition: Union[str, None, UnsetType] = ( + UNSET + ) + """Definition of the latest schema in the subject.""" + + schema_registry_subject_governing_asset_qualified_names: Union[ + List[str], None, UnsetType + ] = UNSET + """List of asset qualified names that this subject is governing/validating.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SchemaRegistrySubject" diff --git a/pyatlan_v9/model/assets/schema_registry_subject.py b/pyatlan_v9/model/assets/schema_registry_subject.py new file mode 100644 index 000000000..8219f146a --- /dev/null +++ b/pyatlan_v9/model/assets/schema_registry_subject.py @@ -0,0 +1,703 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SchemaRegistrySubject asset model with flattened inheritance. + +This module provides: +- SchemaRegistrySubject: Flat asset class (easy to use) +- SchemaRegistrySubjectAttributes: Nested attributes struct (extends AssetAttributes) +- SchemaRegistrySubjectNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .asset_related import RelatedAsset +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .schema_registry_related import RelatedSchemaRegistrySubject + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SchemaRegistrySubject(Asset): + """ + Instance of a schema registry subject in Atlan. + """ + + SCHEMA_REGISTRY_SUBJECT_BASE_NAME: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECT_IS_KEY_SCHEMA: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECT_SCHEMA_COMPATIBILITY: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECT_LATEST_SCHEMA_VERSION: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECT_LATEST_SCHEMA_DEFINITION: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECT_GOVERNING_ASSET_QUALIFIED_NAMES: ClassVar[Any] = None + SCHEMA_REGISTRY_SCHEMA_TYPE: ClassVar[Any] = None + SCHEMA_REGISTRY_SCHEMA_ID: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + ASSETS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SchemaRegistrySubject" + + schema_registry_subject_base_name: Union[str, None, UnsetType] = UNSET + """Base name of the subject, without -key, -value prefixes.""" + + schema_registry_subject_is_key_schema: Union[bool, None, UnsetType] = UNSET + """Whether the subject is a schema for the keys of the messages (true) or not (false).""" + + schema_registry_subject_schema_compatibility: Union[str, None, UnsetType] = UNSET + """Compatibility of the schema across versions.""" + + schema_registry_subject_latest_schema_version: Union[str, None, UnsetType] = UNSET + """Latest schema version of the subject.""" + + schema_registry_subject_latest_schema_definition: Union[str, None, UnsetType] = ( + UNSET + ) + """Definition of the latest schema in the subject.""" + + schema_registry_subject_governing_asset_qualified_names: Union[ + List[str], None, UnsetType + ] = UNSET + """List of asset qualified names that this subject is governing/validating.""" + + schema_registry_schema_type: Union[str, None, UnsetType] = UNSET + """Type of language or specification used to define the schema, for example: JSON, Protobuf, etc.""" + + schema_registry_schema_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for schema definition set by the schema registry.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + assets: Union[List[RelatedAsset], None, UnsetType] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SchemaRegistrySubject" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _schema_registry_subject_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> SchemaRegistrySubject: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SchemaRegistrySubject instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _schema_registry_subject_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SchemaRegistrySubjectAttributes(AssetAttributes): + """SchemaRegistrySubject-specific attributes for nested API format.""" + + schema_registry_subject_base_name: Union[str, None, UnsetType] = UNSET + """Base name of the subject, without -key, -value prefixes.""" + + schema_registry_subject_is_key_schema: Union[bool, None, UnsetType] = UNSET + """Whether the subject is a schema for the keys of the messages (true) or not (false).""" + + schema_registry_subject_schema_compatibility: Union[str, None, UnsetType] = UNSET + """Compatibility of the schema across versions.""" + + schema_registry_subject_latest_schema_version: Union[str, None, UnsetType] = UNSET + """Latest schema version of the subject.""" + + schema_registry_subject_latest_schema_definition: Union[str, None, UnsetType] = ( + UNSET + ) + """Definition of the latest schema in the subject.""" + + schema_registry_subject_governing_asset_qualified_names: Union[ + List[str], None, UnsetType + ] = UNSET + """List of asset qualified names that this subject is governing/validating.""" + + schema_registry_schema_type: Union[str, None, UnsetType] = UNSET + """Type of language or specification used to define the schema, for example: JSON, Protobuf, etc.""" + + schema_registry_schema_id: Union[str, None, UnsetType] = UNSET + """Unique identifier for schema definition set by the schema registry.""" + + +class SchemaRegistrySubjectRelationshipAttributes(AssetRelationshipAttributes): + """SchemaRegistrySubject-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + assets: Union[List[RelatedAsset], None, UnsetType] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SchemaRegistrySubjectNested(AssetNested): + """SchemaRegistrySubject in nested API format for high-performance serialization.""" + + attributes: Union[SchemaRegistrySubjectAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + SchemaRegistrySubjectRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + SchemaRegistrySubjectRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SchemaRegistrySubjectRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SCHEMA_REGISTRY_SUBJECT_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "assets", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_schema_registry_subject_attrs( + attrs: SchemaRegistrySubjectAttributes, obj: SchemaRegistrySubject +) -> None: + """Populate SchemaRegistrySubject-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.schema_registry_subject_base_name = obj.schema_registry_subject_base_name + attrs.schema_registry_subject_is_key_schema = ( + obj.schema_registry_subject_is_key_schema + ) + attrs.schema_registry_subject_schema_compatibility = ( + obj.schema_registry_subject_schema_compatibility + ) + attrs.schema_registry_subject_latest_schema_version = ( + obj.schema_registry_subject_latest_schema_version + ) + attrs.schema_registry_subject_latest_schema_definition = ( + obj.schema_registry_subject_latest_schema_definition + ) + attrs.schema_registry_subject_governing_asset_qualified_names = ( + obj.schema_registry_subject_governing_asset_qualified_names + ) + attrs.schema_registry_schema_type = obj.schema_registry_schema_type + attrs.schema_registry_schema_id = obj.schema_registry_schema_id + + +def _extract_schema_registry_subject_attrs( + attrs: SchemaRegistrySubjectAttributes, +) -> dict: + """Extract all SchemaRegistrySubject attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["schema_registry_subject_base_name"] = ( + attrs.schema_registry_subject_base_name + ) + result["schema_registry_subject_is_key_schema"] = ( + attrs.schema_registry_subject_is_key_schema + ) + result["schema_registry_subject_schema_compatibility"] = ( + attrs.schema_registry_subject_schema_compatibility + ) + result["schema_registry_subject_latest_schema_version"] = ( + attrs.schema_registry_subject_latest_schema_version + ) + result["schema_registry_subject_latest_schema_definition"] = ( + attrs.schema_registry_subject_latest_schema_definition + ) + result["schema_registry_subject_governing_asset_qualified_names"] = ( + attrs.schema_registry_subject_governing_asset_qualified_names + ) + result["schema_registry_schema_type"] = attrs.schema_registry_schema_type + result["schema_registry_schema_id"] = attrs.schema_registry_schema_id + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _schema_registry_subject_to_nested( + schema_registry_subject: SchemaRegistrySubject, +) -> SchemaRegistrySubjectNested: + """Convert flat SchemaRegistrySubject to nested format.""" + attrs = SchemaRegistrySubjectAttributes() + _populate_schema_registry_subject_attrs(attrs, schema_registry_subject) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + schema_registry_subject, + _SCHEMA_REGISTRY_SUBJECT_REL_FIELDS, + SchemaRegistrySubjectRelationshipAttributes, + ) + return SchemaRegistrySubjectNested( + guid=schema_registry_subject.guid, + type_name=schema_registry_subject.type_name, + status=schema_registry_subject.status, + version=schema_registry_subject.version, + create_time=schema_registry_subject.create_time, + update_time=schema_registry_subject.update_time, + created_by=schema_registry_subject.created_by, + updated_by=schema_registry_subject.updated_by, + classifications=schema_registry_subject.classifications, + classification_names=schema_registry_subject.classification_names, + meanings=schema_registry_subject.meanings, + labels=schema_registry_subject.labels, + business_attributes=schema_registry_subject.business_attributes, + custom_attributes=schema_registry_subject.custom_attributes, + pending_tasks=schema_registry_subject.pending_tasks, + proxy=schema_registry_subject.proxy, + is_incomplete=schema_registry_subject.is_incomplete, + provenance_type=schema_registry_subject.provenance_type, + home_id=schema_registry_subject.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _schema_registry_subject_from_nested( + nested: SchemaRegistrySubjectNested, +) -> SchemaRegistrySubject: + """Convert nested format to flat SchemaRegistrySubject.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else SchemaRegistrySubjectAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SCHEMA_REGISTRY_SUBJECT_REL_FIELDS, + SchemaRegistrySubjectRelationshipAttributes, + ) + return SchemaRegistrySubject( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_schema_registry_subject_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _schema_registry_subject_to_nested_bytes( + schema_registry_subject: SchemaRegistrySubject, serde: Serde +) -> bytes: + """Convert flat SchemaRegistrySubject to nested JSON bytes.""" + return serde.encode(_schema_registry_subject_to_nested(schema_registry_subject)) + + +def _schema_registry_subject_from_nested_bytes( + data: bytes, serde: Serde +) -> SchemaRegistrySubject: + """Convert nested JSON bytes to flat SchemaRegistrySubject.""" + nested = serde.decode(data, SchemaRegistrySubjectNested) + return _schema_registry_subject_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + RelationField, +) + +SchemaRegistrySubject.SCHEMA_REGISTRY_SUBJECT_BASE_NAME = KeywordField( + "schemaRegistrySubjectBaseName", "schemaRegistrySubjectBaseName" +) +SchemaRegistrySubject.SCHEMA_REGISTRY_SUBJECT_IS_KEY_SCHEMA = BooleanField( + "schemaRegistrySubjectIsKeySchema", "schemaRegistrySubjectIsKeySchema" +) +SchemaRegistrySubject.SCHEMA_REGISTRY_SUBJECT_SCHEMA_COMPATIBILITY = KeywordField( + "schemaRegistrySubjectSchemaCompatibility", + "schemaRegistrySubjectSchemaCompatibility", +) +SchemaRegistrySubject.SCHEMA_REGISTRY_SUBJECT_LATEST_SCHEMA_VERSION = KeywordField( + "schemaRegistrySubjectLatestSchemaVersion", + "schemaRegistrySubjectLatestSchemaVersion", +) +SchemaRegistrySubject.SCHEMA_REGISTRY_SUBJECT_LATEST_SCHEMA_DEFINITION = KeywordField( + "schemaRegistrySubjectLatestSchemaDefinition", + "schemaRegistrySubjectLatestSchemaDefinition", +) +SchemaRegistrySubject.SCHEMA_REGISTRY_SUBJECT_GOVERNING_ASSET_QUALIFIED_NAMES = ( + KeywordField( + "schemaRegistrySubjectGoverningAssetQualifiedNames", + "schemaRegistrySubjectGoverningAssetQualifiedNames", + ) +) +SchemaRegistrySubject.SCHEMA_REGISTRY_SCHEMA_TYPE = KeywordField( + "schemaRegistrySchemaType", "schemaRegistrySchemaType" +) +SchemaRegistrySubject.SCHEMA_REGISTRY_SCHEMA_ID = KeywordField( + "schemaRegistrySchemaId", "schemaRegistrySchemaId" +) +SchemaRegistrySubject.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SchemaRegistrySubject.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +SchemaRegistrySubject.ANOMALO_CHECKS = RelationField("anomaloChecks") +SchemaRegistrySubject.APPLICATION = RelationField("application") +SchemaRegistrySubject.APPLICATION_FIELD = RelationField("applicationField") +SchemaRegistrySubject.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +SchemaRegistrySubject.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +SchemaRegistrySubject.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +SchemaRegistrySubject.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +SchemaRegistrySubject.METRICS = RelationField("metrics") +SchemaRegistrySubject.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SchemaRegistrySubject.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +SchemaRegistrySubject.MEANINGS = RelationField("meanings") +SchemaRegistrySubject.MC_MONITORS = RelationField("mcMonitors") +SchemaRegistrySubject.MC_INCIDENTS = RelationField("mcIncidents") +SchemaRegistrySubject.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SchemaRegistrySubject.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SchemaRegistrySubject.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SchemaRegistrySubject.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SchemaRegistrySubject.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +SchemaRegistrySubject.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +SchemaRegistrySubject.FILES = RelationField("files") +SchemaRegistrySubject.LINKS = RelationField("links") +SchemaRegistrySubject.README = RelationField("readme") +SchemaRegistrySubject.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +SchemaRegistrySubject.ASSETS = RelationField("assets") +SchemaRegistrySubject.SODA_CHECKS = RelationField("sodaChecks") +SchemaRegistrySubject.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SchemaRegistrySubject.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/semantic.py b/pyatlan_v9/model/assets/semantic.py new file mode 100644 index 000000000..7f5fa0592 --- /dev/null +++ b/pyatlan_v9/model/assets/semantic.py @@ -0,0 +1,525 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Semantic asset model with flattened inheritance. + +This module provides: +- Semantic: Flat asset class (easy to use) +- SemanticAttributes: Nested attributes struct (extends AssetAttributes) +- SemanticNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Semantic(Asset): + """ + Base class for semantic layer assets. + """ + + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Semantic" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Semantic" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _semantic_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Semantic: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Semantic instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _semantic_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SemanticAttributes(AssetAttributes): + """Semantic-specific attributes for nested API format.""" + + pass + + +class SemanticRelationshipAttributes(AssetRelationshipAttributes): + """Semantic-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SemanticNested(AssetNested): + """Semantic in nested API format for high-performance serialization.""" + + attributes: Union[SemanticAttributes, UnsetType] = UNSET + relationship_attributes: Union[SemanticRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[SemanticRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[SemanticRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SEMANTIC_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_semantic_attrs(attrs: SemanticAttributes, obj: Semantic) -> None: + """Populate Semantic-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + + +def _extract_semantic_attrs(attrs: SemanticAttributes) -> dict: + """Extract all Semantic attributes from the attrs struct into a flat dict.""" + return _extract_asset_attrs(attrs) + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _semantic_to_nested(semantic: Semantic) -> SemanticNested: + """Convert flat Semantic to nested format.""" + attrs = SemanticAttributes() + _populate_semantic_attrs(attrs, semantic) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + semantic, _SEMANTIC_REL_FIELDS, SemanticRelationshipAttributes + ) + return SemanticNested( + guid=semantic.guid, + type_name=semantic.type_name, + status=semantic.status, + version=semantic.version, + create_time=semantic.create_time, + update_time=semantic.update_time, + created_by=semantic.created_by, + updated_by=semantic.updated_by, + classifications=semantic.classifications, + classification_names=semantic.classification_names, + meanings=semantic.meanings, + labels=semantic.labels, + business_attributes=semantic.business_attributes, + custom_attributes=semantic.custom_attributes, + pending_tasks=semantic.pending_tasks, + proxy=semantic.proxy, + is_incomplete=semantic.is_incomplete, + provenance_type=semantic.provenance_type, + home_id=semantic.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _semantic_from_nested(nested: SemanticNested) -> Semantic: + """Convert nested format to flat Semantic.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else SemanticAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SEMANTIC_REL_FIELDS, + SemanticRelationshipAttributes, + ) + return Semantic( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_semantic_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _semantic_to_nested_bytes(semantic: Semantic, serde: Serde) -> bytes: + """Convert flat Semantic to nested JSON bytes.""" + return serde.encode(_semantic_to_nested(semantic)) + + +def _semantic_from_nested_bytes(data: bytes, serde: Serde) -> Semantic: + """Convert nested JSON bytes to flat Semantic.""" + nested = serde.decode(data, SemanticNested) + return _semantic_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import RelationField # noqa: E402 + +Semantic.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Semantic.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Semantic.ANOMALO_CHECKS = RelationField("anomaloChecks") +Semantic.APPLICATION = RelationField("application") +Semantic.APPLICATION_FIELD = RelationField("applicationField") +Semantic.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Semantic.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Semantic.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Semantic.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Semantic.METRICS = RelationField("metrics") +Semantic.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Semantic.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Semantic.MEANINGS = RelationField("meanings") +Semantic.MC_MONITORS = RelationField("mcMonitors") +Semantic.MC_INCIDENTS = RelationField("mcIncidents") +Semantic.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Semantic.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Semantic.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Semantic.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Semantic.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Semantic.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Semantic.FILES = RelationField("files") +Semantic.LINKS = RelationField("links") +Semantic.README = RelationField("readme") +Semantic.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Semantic.SODA_CHECKS = RelationField("sodaChecks") +Semantic.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Semantic.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/semantic_dimension.py b/pyatlan_v9/model/assets/semantic_dimension.py new file mode 100644 index 000000000..abc723357 --- /dev/null +++ b/pyatlan_v9/model/assets/semantic_dimension.py @@ -0,0 +1,646 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SemanticDimension asset model with flattened inheritance. + +This module provides: +- SemanticDimension: Flat asset class (easy to use) +- SemanticDimensionAttributes: Nested attributes struct (extends AssetAttributes) +- SemanticDimensionNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .semantic_related import RelatedSemanticModel + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SemanticDimension(Asset): + """ + Base class for semantic dimensions across different sources. + """ + + SEMANTIC_EXPRESSION: ClassVar[Any] = None + SEMANTIC_TYPE: ClassVar[Any] = None + SEMANTIC_SYNONYMS: ClassVar[Any] = None + SEMANTIC_SAMPLE_VALUES: ClassVar[Any] = None + SEMANTIC_ACCESS_MODIFIER: ClassVar[Any] = None + SEMANTIC_DATA_TYPE: ClassVar[Any] = None + SEMANTIC_LABELS: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SEMANTIC_MODEL: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SemanticDimension" + + semantic_expression: Union[str, None, UnsetType] = UNSET + """Column name or SQL expression for the semantic field.""" + + semantic_type: Union[str, None, UnsetType] = UNSET + """Detailed type of the semantic field (e.g., type of measure, type of dimension, or type of entity).""" + + semantic_synonyms: Union[List[str], None, UnsetType] = UNSET + """Alternative names or terms for the semantic field.""" + + semantic_sample_values: Union[List[str], None, UnsetType] = UNSET + """Sample values for the semantic field.""" + + semantic_access_modifier: Union[str, None, UnsetType] = UNSET + """Access level for the semantic field (e.g., public_access/private_access).""" + + semantic_data_type: Union[str, None, UnsetType] = UNSET + """Data type of the semantic field.""" + + semantic_labels: Union[List[str], None, UnsetType] = UNSET + """Labels associated with the semantic field.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + semantic_model: Union[RelatedSemanticModel, None, UnsetType] = UNSET + """Semantic model in which this dimension exists.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SemanticDimension" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _semantic_dimension_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> SemanticDimension: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SemanticDimension instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _semantic_dimension_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SemanticDimensionAttributes(AssetAttributes): + """SemanticDimension-specific attributes for nested API format.""" + + semantic_expression: Union[str, None, UnsetType] = UNSET + """Column name or SQL expression for the semantic field.""" + + semantic_type: Union[str, None, UnsetType] = UNSET + """Detailed type of the semantic field (e.g., type of measure, type of dimension, or type of entity).""" + + semantic_synonyms: Union[List[str], None, UnsetType] = UNSET + """Alternative names or terms for the semantic field.""" + + semantic_sample_values: Union[List[str], None, UnsetType] = UNSET + """Sample values for the semantic field.""" + + semantic_access_modifier: Union[str, None, UnsetType] = UNSET + """Access level for the semantic field (e.g., public_access/private_access).""" + + semantic_data_type: Union[str, None, UnsetType] = UNSET + """Data type of the semantic field.""" + + semantic_labels: Union[List[str], None, UnsetType] = UNSET + """Labels associated with the semantic field.""" + + +class SemanticDimensionRelationshipAttributes(AssetRelationshipAttributes): + """SemanticDimension-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + semantic_model: Union[RelatedSemanticModel, None, UnsetType] = UNSET + """Semantic model in which this dimension exists.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SemanticDimensionNested(AssetNested): + """SemanticDimension in nested API format for high-performance serialization.""" + + attributes: Union[SemanticDimensionAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + SemanticDimensionRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + SemanticDimensionRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SemanticDimensionRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SEMANTIC_DIMENSION_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "semantic_model", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_semantic_dimension_attrs( + attrs: SemanticDimensionAttributes, obj: SemanticDimension +) -> None: + """Populate SemanticDimension-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.semantic_expression = obj.semantic_expression + attrs.semantic_type = obj.semantic_type + attrs.semantic_synonyms = obj.semantic_synonyms + attrs.semantic_sample_values = obj.semantic_sample_values + attrs.semantic_access_modifier = obj.semantic_access_modifier + attrs.semantic_data_type = obj.semantic_data_type + attrs.semantic_labels = obj.semantic_labels + + +def _extract_semantic_dimension_attrs(attrs: SemanticDimensionAttributes) -> dict: + """Extract all SemanticDimension attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["semantic_expression"] = attrs.semantic_expression + result["semantic_type"] = attrs.semantic_type + result["semantic_synonyms"] = attrs.semantic_synonyms + result["semantic_sample_values"] = attrs.semantic_sample_values + result["semantic_access_modifier"] = attrs.semantic_access_modifier + result["semantic_data_type"] = attrs.semantic_data_type + result["semantic_labels"] = attrs.semantic_labels + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _semantic_dimension_to_nested( + semantic_dimension: SemanticDimension, +) -> SemanticDimensionNested: + """Convert flat SemanticDimension to nested format.""" + attrs = SemanticDimensionAttributes() + _populate_semantic_dimension_attrs(attrs, semantic_dimension) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + semantic_dimension, + _SEMANTIC_DIMENSION_REL_FIELDS, + SemanticDimensionRelationshipAttributes, + ) + return SemanticDimensionNested( + guid=semantic_dimension.guid, + type_name=semantic_dimension.type_name, + status=semantic_dimension.status, + version=semantic_dimension.version, + create_time=semantic_dimension.create_time, + update_time=semantic_dimension.update_time, + created_by=semantic_dimension.created_by, + updated_by=semantic_dimension.updated_by, + classifications=semantic_dimension.classifications, + classification_names=semantic_dimension.classification_names, + meanings=semantic_dimension.meanings, + labels=semantic_dimension.labels, + business_attributes=semantic_dimension.business_attributes, + custom_attributes=semantic_dimension.custom_attributes, + pending_tasks=semantic_dimension.pending_tasks, + proxy=semantic_dimension.proxy, + is_incomplete=semantic_dimension.is_incomplete, + provenance_type=semantic_dimension.provenance_type, + home_id=semantic_dimension.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _semantic_dimension_from_nested( + nested: SemanticDimensionNested, +) -> SemanticDimension: + """Convert nested format to flat SemanticDimension.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else SemanticDimensionAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SEMANTIC_DIMENSION_REL_FIELDS, + SemanticDimensionRelationshipAttributes, + ) + return SemanticDimension( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_semantic_dimension_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _semantic_dimension_to_nested_bytes( + semantic_dimension: SemanticDimension, serde: Serde +) -> bytes: + """Convert flat SemanticDimension to nested JSON bytes.""" + return serde.encode(_semantic_dimension_to_nested(semantic_dimension)) + + +def _semantic_dimension_from_nested_bytes( + data: bytes, serde: Serde +) -> SemanticDimension: + """Convert nested JSON bytes to flat SemanticDimension.""" + nested = serde.decode(data, SemanticDimensionNested) + return _semantic_dimension_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, + TextField, +) + +SemanticDimension.SEMANTIC_EXPRESSION = KeywordField( + "semanticExpression", "semanticExpression" +) +SemanticDimension.SEMANTIC_TYPE = KeywordField("semanticType", "semanticType") +SemanticDimension.SEMANTIC_SYNONYMS = KeywordField( + "semanticSynonyms", "semanticSynonyms" +) +SemanticDimension.SEMANTIC_SAMPLE_VALUES = TextField( + "semanticSampleValues", "semanticSampleValues" +) +SemanticDimension.SEMANTIC_ACCESS_MODIFIER = KeywordField( + "semanticAccessModifier", "semanticAccessModifier" +) +SemanticDimension.SEMANTIC_DATA_TYPE = KeywordField( + "semanticDataType", "semanticDataType" +) +SemanticDimension.SEMANTIC_LABELS = KeywordField("semanticLabels", "semanticLabels") +SemanticDimension.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SemanticDimension.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +SemanticDimension.ANOMALO_CHECKS = RelationField("anomaloChecks") +SemanticDimension.APPLICATION = RelationField("application") +SemanticDimension.APPLICATION_FIELD = RelationField("applicationField") +SemanticDimension.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +SemanticDimension.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +SemanticDimension.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +SemanticDimension.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +SemanticDimension.METRICS = RelationField("metrics") +SemanticDimension.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SemanticDimension.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +SemanticDimension.MEANINGS = RelationField("meanings") +SemanticDimension.MC_MONITORS = RelationField("mcMonitors") +SemanticDimension.MC_INCIDENTS = RelationField("mcIncidents") +SemanticDimension.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SemanticDimension.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SemanticDimension.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SemanticDimension.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SemanticDimension.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +SemanticDimension.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +SemanticDimension.FILES = RelationField("files") +SemanticDimension.LINKS = RelationField("links") +SemanticDimension.README = RelationField("readme") +SemanticDimension.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +SemanticDimension.SEMANTIC_MODEL = RelationField("semanticModel") +SemanticDimension.SODA_CHECKS = RelationField("sodaChecks") +SemanticDimension.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SemanticDimension.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/semantic_entity.py b/pyatlan_v9/model/assets/semantic_entity.py new file mode 100644 index 000000000..ad031397c --- /dev/null +++ b/pyatlan_v9/model/assets/semantic_entity.py @@ -0,0 +1,634 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SemanticEntity asset model with flattened inheritance. + +This module provides: +- SemanticEntity: Flat asset class (easy to use) +- SemanticEntityAttributes: Nested attributes struct (extends AssetAttributes) +- SemanticEntityNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .semantic_related import RelatedSemanticModel + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SemanticEntity(Asset): + """ + Base class for semantic entities/logical tables across different sources. + """ + + SEMANTIC_EXPRESSION: ClassVar[Any] = None + SEMANTIC_TYPE: ClassVar[Any] = None + SEMANTIC_SYNONYMS: ClassVar[Any] = None + SEMANTIC_SAMPLE_VALUES: ClassVar[Any] = None + SEMANTIC_ACCESS_MODIFIER: ClassVar[Any] = None + SEMANTIC_DATA_TYPE: ClassVar[Any] = None + SEMANTIC_LABELS: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SEMANTIC_MODEL: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SemanticEntity" + + semantic_expression: Union[str, None, UnsetType] = UNSET + """Column name or SQL expression for the semantic field.""" + + semantic_type: Union[str, None, UnsetType] = UNSET + """Detailed type of the semantic field (e.g., type of measure, type of dimension, or type of entity).""" + + semantic_synonyms: Union[List[str], None, UnsetType] = UNSET + """Alternative names or terms for the semantic field.""" + + semantic_sample_values: Union[List[str], None, UnsetType] = UNSET + """Sample values for the semantic field.""" + + semantic_access_modifier: Union[str, None, UnsetType] = UNSET + """Access level for the semantic field (e.g., public_access/private_access).""" + + semantic_data_type: Union[str, None, UnsetType] = UNSET + """Data type of the semantic field.""" + + semantic_labels: Union[List[str], None, UnsetType] = UNSET + """Labels associated with the semantic field.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + semantic_model: Union[RelatedSemanticModel, None, UnsetType] = UNSET + """Semantic model in which this entity exists.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SemanticEntity" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _semantic_entity_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> SemanticEntity: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SemanticEntity instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _semantic_entity_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SemanticEntityAttributes(AssetAttributes): + """SemanticEntity-specific attributes for nested API format.""" + + semantic_expression: Union[str, None, UnsetType] = UNSET + """Column name or SQL expression for the semantic field.""" + + semantic_type: Union[str, None, UnsetType] = UNSET + """Detailed type of the semantic field (e.g., type of measure, type of dimension, or type of entity).""" + + semantic_synonyms: Union[List[str], None, UnsetType] = UNSET + """Alternative names or terms for the semantic field.""" + + semantic_sample_values: Union[List[str], None, UnsetType] = UNSET + """Sample values for the semantic field.""" + + semantic_access_modifier: Union[str, None, UnsetType] = UNSET + """Access level for the semantic field (e.g., public_access/private_access).""" + + semantic_data_type: Union[str, None, UnsetType] = UNSET + """Data type of the semantic field.""" + + semantic_labels: Union[List[str], None, UnsetType] = UNSET + """Labels associated with the semantic field.""" + + +class SemanticEntityRelationshipAttributes(AssetRelationshipAttributes): + """SemanticEntity-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + semantic_model: Union[RelatedSemanticModel, None, UnsetType] = UNSET + """Semantic model in which this entity exists.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SemanticEntityNested(AssetNested): + """SemanticEntity in nested API format for high-performance serialization.""" + + attributes: Union[SemanticEntityAttributes, UnsetType] = UNSET + relationship_attributes: Union[SemanticEntityRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + SemanticEntityRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SemanticEntityRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SEMANTIC_ENTITY_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "semantic_model", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_semantic_entity_attrs( + attrs: SemanticEntityAttributes, obj: SemanticEntity +) -> None: + """Populate SemanticEntity-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.semantic_expression = obj.semantic_expression + attrs.semantic_type = obj.semantic_type + attrs.semantic_synonyms = obj.semantic_synonyms + attrs.semantic_sample_values = obj.semantic_sample_values + attrs.semantic_access_modifier = obj.semantic_access_modifier + attrs.semantic_data_type = obj.semantic_data_type + attrs.semantic_labels = obj.semantic_labels + + +def _extract_semantic_entity_attrs(attrs: SemanticEntityAttributes) -> dict: + """Extract all SemanticEntity attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["semantic_expression"] = attrs.semantic_expression + result["semantic_type"] = attrs.semantic_type + result["semantic_synonyms"] = attrs.semantic_synonyms + result["semantic_sample_values"] = attrs.semantic_sample_values + result["semantic_access_modifier"] = attrs.semantic_access_modifier + result["semantic_data_type"] = attrs.semantic_data_type + result["semantic_labels"] = attrs.semantic_labels + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _semantic_entity_to_nested(semantic_entity: SemanticEntity) -> SemanticEntityNested: + """Convert flat SemanticEntity to nested format.""" + attrs = SemanticEntityAttributes() + _populate_semantic_entity_attrs(attrs, semantic_entity) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + semantic_entity, + _SEMANTIC_ENTITY_REL_FIELDS, + SemanticEntityRelationshipAttributes, + ) + return SemanticEntityNested( + guid=semantic_entity.guid, + type_name=semantic_entity.type_name, + status=semantic_entity.status, + version=semantic_entity.version, + create_time=semantic_entity.create_time, + update_time=semantic_entity.update_time, + created_by=semantic_entity.created_by, + updated_by=semantic_entity.updated_by, + classifications=semantic_entity.classifications, + classification_names=semantic_entity.classification_names, + meanings=semantic_entity.meanings, + labels=semantic_entity.labels, + business_attributes=semantic_entity.business_attributes, + custom_attributes=semantic_entity.custom_attributes, + pending_tasks=semantic_entity.pending_tasks, + proxy=semantic_entity.proxy, + is_incomplete=semantic_entity.is_incomplete, + provenance_type=semantic_entity.provenance_type, + home_id=semantic_entity.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _semantic_entity_from_nested(nested: SemanticEntityNested) -> SemanticEntity: + """Convert nested format to flat SemanticEntity.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else SemanticEntityAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SEMANTIC_ENTITY_REL_FIELDS, + SemanticEntityRelationshipAttributes, + ) + return SemanticEntity( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_semantic_entity_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _semantic_entity_to_nested_bytes( + semantic_entity: SemanticEntity, serde: Serde +) -> bytes: + """Convert flat SemanticEntity to nested JSON bytes.""" + return serde.encode(_semantic_entity_to_nested(semantic_entity)) + + +def _semantic_entity_from_nested_bytes(data: bytes, serde: Serde) -> SemanticEntity: + """Convert nested JSON bytes to flat SemanticEntity.""" + nested = serde.decode(data, SemanticEntityNested) + return _semantic_entity_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, + TextField, +) + +SemanticEntity.SEMANTIC_EXPRESSION = KeywordField( + "semanticExpression", "semanticExpression" +) +SemanticEntity.SEMANTIC_TYPE = KeywordField("semanticType", "semanticType") +SemanticEntity.SEMANTIC_SYNONYMS = KeywordField("semanticSynonyms", "semanticSynonyms") +SemanticEntity.SEMANTIC_SAMPLE_VALUES = TextField( + "semanticSampleValues", "semanticSampleValues" +) +SemanticEntity.SEMANTIC_ACCESS_MODIFIER = KeywordField( + "semanticAccessModifier", "semanticAccessModifier" +) +SemanticEntity.SEMANTIC_DATA_TYPE = KeywordField("semanticDataType", "semanticDataType") +SemanticEntity.SEMANTIC_LABELS = KeywordField("semanticLabels", "semanticLabels") +SemanticEntity.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SemanticEntity.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +SemanticEntity.ANOMALO_CHECKS = RelationField("anomaloChecks") +SemanticEntity.APPLICATION = RelationField("application") +SemanticEntity.APPLICATION_FIELD = RelationField("applicationField") +SemanticEntity.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +SemanticEntity.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +SemanticEntity.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +SemanticEntity.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +SemanticEntity.METRICS = RelationField("metrics") +SemanticEntity.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SemanticEntity.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +SemanticEntity.MEANINGS = RelationField("meanings") +SemanticEntity.MC_MONITORS = RelationField("mcMonitors") +SemanticEntity.MC_INCIDENTS = RelationField("mcIncidents") +SemanticEntity.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SemanticEntity.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SemanticEntity.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SemanticEntity.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SemanticEntity.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +SemanticEntity.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +SemanticEntity.FILES = RelationField("files") +SemanticEntity.LINKS = RelationField("links") +SemanticEntity.README = RelationField("readme") +SemanticEntity.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +SemanticEntity.SEMANTIC_MODEL = RelationField("semanticModel") +SemanticEntity.SODA_CHECKS = RelationField("sodaChecks") +SemanticEntity.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SemanticEntity.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/semantic_field.py b/pyatlan_v9/model/assets/semantic_field.py new file mode 100644 index 000000000..23c154990 --- /dev/null +++ b/pyatlan_v9/model/assets/semantic_field.py @@ -0,0 +1,612 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SemanticField asset model with flattened inheritance. + +This module provides: +- SemanticField: Flat asset class (easy to use) +- SemanticFieldAttributes: Nested attributes struct (extends AssetAttributes) +- SemanticFieldNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SemanticField(Asset): + """ + Base class for semantic fields (measures, dimensions, entities) across different sources. + """ + + SEMANTIC_EXPRESSION: ClassVar[Any] = None + SEMANTIC_TYPE: ClassVar[Any] = None + SEMANTIC_SYNONYMS: ClassVar[Any] = None + SEMANTIC_SAMPLE_VALUES: ClassVar[Any] = None + SEMANTIC_ACCESS_MODIFIER: ClassVar[Any] = None + SEMANTIC_DATA_TYPE: ClassVar[Any] = None + SEMANTIC_LABELS: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SemanticField" + + semantic_expression: Union[str, None, UnsetType] = UNSET + """Column name or SQL expression for the semantic field.""" + + semantic_type: Union[str, None, UnsetType] = UNSET + """Detailed type of the semantic field (e.g., type of measure, type of dimension, or type of entity).""" + + semantic_synonyms: Union[List[str], None, UnsetType] = UNSET + """Alternative names or terms for the semantic field.""" + + semantic_sample_values: Union[List[str], None, UnsetType] = UNSET + """Sample values for the semantic field.""" + + semantic_access_modifier: Union[str, None, UnsetType] = UNSET + """Access level for the semantic field (e.g., public_access/private_access).""" + + semantic_data_type: Union[str, None, UnsetType] = UNSET + """Data type of the semantic field.""" + + semantic_labels: Union[List[str], None, UnsetType] = UNSET + """Labels associated with the semantic field.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SemanticField" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _semantic_field_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> SemanticField: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SemanticField instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _semantic_field_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SemanticFieldAttributes(AssetAttributes): + """SemanticField-specific attributes for nested API format.""" + + semantic_expression: Union[str, None, UnsetType] = UNSET + """Column name or SQL expression for the semantic field.""" + + semantic_type: Union[str, None, UnsetType] = UNSET + """Detailed type of the semantic field (e.g., type of measure, type of dimension, or type of entity).""" + + semantic_synonyms: Union[List[str], None, UnsetType] = UNSET + """Alternative names or terms for the semantic field.""" + + semantic_sample_values: Union[List[str], None, UnsetType] = UNSET + """Sample values for the semantic field.""" + + semantic_access_modifier: Union[str, None, UnsetType] = UNSET + """Access level for the semantic field (e.g., public_access/private_access).""" + + semantic_data_type: Union[str, None, UnsetType] = UNSET + """Data type of the semantic field.""" + + semantic_labels: Union[List[str], None, UnsetType] = UNSET + """Labels associated with the semantic field.""" + + +class SemanticFieldRelationshipAttributes(AssetRelationshipAttributes): + """SemanticField-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SemanticFieldNested(AssetNested): + """SemanticField in nested API format for high-performance serialization.""" + + attributes: Union[SemanticFieldAttributes, UnsetType] = UNSET + relationship_attributes: Union[SemanticFieldRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + SemanticFieldRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SemanticFieldRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SEMANTIC_FIELD_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_semantic_field_attrs( + attrs: SemanticFieldAttributes, obj: SemanticField +) -> None: + """Populate SemanticField-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.semantic_expression = obj.semantic_expression + attrs.semantic_type = obj.semantic_type + attrs.semantic_synonyms = obj.semantic_synonyms + attrs.semantic_sample_values = obj.semantic_sample_values + attrs.semantic_access_modifier = obj.semantic_access_modifier + attrs.semantic_data_type = obj.semantic_data_type + attrs.semantic_labels = obj.semantic_labels + + +def _extract_semantic_field_attrs(attrs: SemanticFieldAttributes) -> dict: + """Extract all SemanticField attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["semantic_expression"] = attrs.semantic_expression + result["semantic_type"] = attrs.semantic_type + result["semantic_synonyms"] = attrs.semantic_synonyms + result["semantic_sample_values"] = attrs.semantic_sample_values + result["semantic_access_modifier"] = attrs.semantic_access_modifier + result["semantic_data_type"] = attrs.semantic_data_type + result["semantic_labels"] = attrs.semantic_labels + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _semantic_field_to_nested(semantic_field: SemanticField) -> SemanticFieldNested: + """Convert flat SemanticField to nested format.""" + attrs = SemanticFieldAttributes() + _populate_semantic_field_attrs(attrs, semantic_field) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + semantic_field, _SEMANTIC_FIELD_REL_FIELDS, SemanticFieldRelationshipAttributes + ) + return SemanticFieldNested( + guid=semantic_field.guid, + type_name=semantic_field.type_name, + status=semantic_field.status, + version=semantic_field.version, + create_time=semantic_field.create_time, + update_time=semantic_field.update_time, + created_by=semantic_field.created_by, + updated_by=semantic_field.updated_by, + classifications=semantic_field.classifications, + classification_names=semantic_field.classification_names, + meanings=semantic_field.meanings, + labels=semantic_field.labels, + business_attributes=semantic_field.business_attributes, + custom_attributes=semantic_field.custom_attributes, + pending_tasks=semantic_field.pending_tasks, + proxy=semantic_field.proxy, + is_incomplete=semantic_field.is_incomplete, + provenance_type=semantic_field.provenance_type, + home_id=semantic_field.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _semantic_field_from_nested(nested: SemanticFieldNested) -> SemanticField: + """Convert nested format to flat SemanticField.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else SemanticFieldAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SEMANTIC_FIELD_REL_FIELDS, + SemanticFieldRelationshipAttributes, + ) + return SemanticField( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_semantic_field_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _semantic_field_to_nested_bytes( + semantic_field: SemanticField, serde: Serde +) -> bytes: + """Convert flat SemanticField to nested JSON bytes.""" + return serde.encode(_semantic_field_to_nested(semantic_field)) + + +def _semantic_field_from_nested_bytes(data: bytes, serde: Serde) -> SemanticField: + """Convert nested JSON bytes to flat SemanticField.""" + nested = serde.decode(data, SemanticFieldNested) + return _semantic_field_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, + TextField, +) + +SemanticField.SEMANTIC_EXPRESSION = KeywordField( + "semanticExpression", "semanticExpression" +) +SemanticField.SEMANTIC_TYPE = KeywordField("semanticType", "semanticType") +SemanticField.SEMANTIC_SYNONYMS = KeywordField("semanticSynonyms", "semanticSynonyms") +SemanticField.SEMANTIC_SAMPLE_VALUES = TextField( + "semanticSampleValues", "semanticSampleValues" +) +SemanticField.SEMANTIC_ACCESS_MODIFIER = KeywordField( + "semanticAccessModifier", "semanticAccessModifier" +) +SemanticField.SEMANTIC_DATA_TYPE = KeywordField("semanticDataType", "semanticDataType") +SemanticField.SEMANTIC_LABELS = KeywordField("semanticLabels", "semanticLabels") +SemanticField.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SemanticField.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +SemanticField.ANOMALO_CHECKS = RelationField("anomaloChecks") +SemanticField.APPLICATION = RelationField("application") +SemanticField.APPLICATION_FIELD = RelationField("applicationField") +SemanticField.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +SemanticField.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +SemanticField.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +SemanticField.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +SemanticField.METRICS = RelationField("metrics") +SemanticField.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SemanticField.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +SemanticField.MEANINGS = RelationField("meanings") +SemanticField.MC_MONITORS = RelationField("mcMonitors") +SemanticField.MC_INCIDENTS = RelationField("mcIncidents") +SemanticField.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SemanticField.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SemanticField.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SemanticField.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SemanticField.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +SemanticField.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +SemanticField.FILES = RelationField("files") +SemanticField.LINKS = RelationField("links") +SemanticField.README = RelationField("readme") +SemanticField.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +SemanticField.SODA_CHECKS = RelationField("sodaChecks") +SemanticField.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SemanticField.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/semantic_measure.py b/pyatlan_v9/model/assets/semantic_measure.py new file mode 100644 index 000000000..781b4f65a --- /dev/null +++ b/pyatlan_v9/model/assets/semantic_measure.py @@ -0,0 +1,640 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SemanticMeasure asset model with flattened inheritance. + +This module provides: +- SemanticMeasure: Flat asset class (easy to use) +- SemanticMeasureAttributes: Nested attributes struct (extends AssetAttributes) +- SemanticMeasureNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .semantic_related import RelatedSemanticModel + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SemanticMeasure(Asset): + """ + Base class for semantic measures across different sources. + """ + + SEMANTIC_EXPRESSION: ClassVar[Any] = None + SEMANTIC_TYPE: ClassVar[Any] = None + SEMANTIC_SYNONYMS: ClassVar[Any] = None + SEMANTIC_SAMPLE_VALUES: ClassVar[Any] = None + SEMANTIC_ACCESS_MODIFIER: ClassVar[Any] = None + SEMANTIC_DATA_TYPE: ClassVar[Any] = None + SEMANTIC_LABELS: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SEMANTIC_MODEL: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SemanticMeasure" + + semantic_expression: Union[str, None, UnsetType] = UNSET + """Column name or SQL expression for the semantic field.""" + + semantic_type: Union[str, None, UnsetType] = UNSET + """Detailed type of the semantic field (e.g., type of measure, type of dimension, or type of entity).""" + + semantic_synonyms: Union[List[str], None, UnsetType] = UNSET + """Alternative names or terms for the semantic field.""" + + semantic_sample_values: Union[List[str], None, UnsetType] = UNSET + """Sample values for the semantic field.""" + + semantic_access_modifier: Union[str, None, UnsetType] = UNSET + """Access level for the semantic field (e.g., public_access/private_access).""" + + semantic_data_type: Union[str, None, UnsetType] = UNSET + """Data type of the semantic field.""" + + semantic_labels: Union[List[str], None, UnsetType] = UNSET + """Labels associated with the semantic field.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + semantic_model: Union[RelatedSemanticModel, None, UnsetType] = UNSET + """Semantic model in which this measure exists.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SemanticMeasure" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _semantic_measure_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> SemanticMeasure: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SemanticMeasure instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _semantic_measure_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SemanticMeasureAttributes(AssetAttributes): + """SemanticMeasure-specific attributes for nested API format.""" + + semantic_expression: Union[str, None, UnsetType] = UNSET + """Column name or SQL expression for the semantic field.""" + + semantic_type: Union[str, None, UnsetType] = UNSET + """Detailed type of the semantic field (e.g., type of measure, type of dimension, or type of entity).""" + + semantic_synonyms: Union[List[str], None, UnsetType] = UNSET + """Alternative names or terms for the semantic field.""" + + semantic_sample_values: Union[List[str], None, UnsetType] = UNSET + """Sample values for the semantic field.""" + + semantic_access_modifier: Union[str, None, UnsetType] = UNSET + """Access level for the semantic field (e.g., public_access/private_access).""" + + semantic_data_type: Union[str, None, UnsetType] = UNSET + """Data type of the semantic field.""" + + semantic_labels: Union[List[str], None, UnsetType] = UNSET + """Labels associated with the semantic field.""" + + +class SemanticMeasureRelationshipAttributes(AssetRelationshipAttributes): + """SemanticMeasure-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + semantic_model: Union[RelatedSemanticModel, None, UnsetType] = UNSET + """Semantic model in which this measure exists.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SemanticMeasureNested(AssetNested): + """SemanticMeasure in nested API format for high-performance serialization.""" + + attributes: Union[SemanticMeasureAttributes, UnsetType] = UNSET + relationship_attributes: Union[SemanticMeasureRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + SemanticMeasureRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SemanticMeasureRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SEMANTIC_MEASURE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "semantic_model", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_semantic_measure_attrs( + attrs: SemanticMeasureAttributes, obj: SemanticMeasure +) -> None: + """Populate SemanticMeasure-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.semantic_expression = obj.semantic_expression + attrs.semantic_type = obj.semantic_type + attrs.semantic_synonyms = obj.semantic_synonyms + attrs.semantic_sample_values = obj.semantic_sample_values + attrs.semantic_access_modifier = obj.semantic_access_modifier + attrs.semantic_data_type = obj.semantic_data_type + attrs.semantic_labels = obj.semantic_labels + + +def _extract_semantic_measure_attrs(attrs: SemanticMeasureAttributes) -> dict: + """Extract all SemanticMeasure attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["semantic_expression"] = attrs.semantic_expression + result["semantic_type"] = attrs.semantic_type + result["semantic_synonyms"] = attrs.semantic_synonyms + result["semantic_sample_values"] = attrs.semantic_sample_values + result["semantic_access_modifier"] = attrs.semantic_access_modifier + result["semantic_data_type"] = attrs.semantic_data_type + result["semantic_labels"] = attrs.semantic_labels + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _semantic_measure_to_nested( + semantic_measure: SemanticMeasure, +) -> SemanticMeasureNested: + """Convert flat SemanticMeasure to nested format.""" + attrs = SemanticMeasureAttributes() + _populate_semantic_measure_attrs(attrs, semantic_measure) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + semantic_measure, + _SEMANTIC_MEASURE_REL_FIELDS, + SemanticMeasureRelationshipAttributes, + ) + return SemanticMeasureNested( + guid=semantic_measure.guid, + type_name=semantic_measure.type_name, + status=semantic_measure.status, + version=semantic_measure.version, + create_time=semantic_measure.create_time, + update_time=semantic_measure.update_time, + created_by=semantic_measure.created_by, + updated_by=semantic_measure.updated_by, + classifications=semantic_measure.classifications, + classification_names=semantic_measure.classification_names, + meanings=semantic_measure.meanings, + labels=semantic_measure.labels, + business_attributes=semantic_measure.business_attributes, + custom_attributes=semantic_measure.custom_attributes, + pending_tasks=semantic_measure.pending_tasks, + proxy=semantic_measure.proxy, + is_incomplete=semantic_measure.is_incomplete, + provenance_type=semantic_measure.provenance_type, + home_id=semantic_measure.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _semantic_measure_from_nested(nested: SemanticMeasureNested) -> SemanticMeasure: + """Convert nested format to flat SemanticMeasure.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else SemanticMeasureAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SEMANTIC_MEASURE_REL_FIELDS, + SemanticMeasureRelationshipAttributes, + ) + return SemanticMeasure( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_semantic_measure_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _semantic_measure_to_nested_bytes( + semantic_measure: SemanticMeasure, serde: Serde +) -> bytes: + """Convert flat SemanticMeasure to nested JSON bytes.""" + return serde.encode(_semantic_measure_to_nested(semantic_measure)) + + +def _semantic_measure_from_nested_bytes(data: bytes, serde: Serde) -> SemanticMeasure: + """Convert nested JSON bytes to flat SemanticMeasure.""" + nested = serde.decode(data, SemanticMeasureNested) + return _semantic_measure_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, + TextField, +) + +SemanticMeasure.SEMANTIC_EXPRESSION = KeywordField( + "semanticExpression", "semanticExpression" +) +SemanticMeasure.SEMANTIC_TYPE = KeywordField("semanticType", "semanticType") +SemanticMeasure.SEMANTIC_SYNONYMS = KeywordField("semanticSynonyms", "semanticSynonyms") +SemanticMeasure.SEMANTIC_SAMPLE_VALUES = TextField( + "semanticSampleValues", "semanticSampleValues" +) +SemanticMeasure.SEMANTIC_ACCESS_MODIFIER = KeywordField( + "semanticAccessModifier", "semanticAccessModifier" +) +SemanticMeasure.SEMANTIC_DATA_TYPE = KeywordField( + "semanticDataType", "semanticDataType" +) +SemanticMeasure.SEMANTIC_LABELS = KeywordField("semanticLabels", "semanticLabels") +SemanticMeasure.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SemanticMeasure.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +SemanticMeasure.ANOMALO_CHECKS = RelationField("anomaloChecks") +SemanticMeasure.APPLICATION = RelationField("application") +SemanticMeasure.APPLICATION_FIELD = RelationField("applicationField") +SemanticMeasure.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +SemanticMeasure.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +SemanticMeasure.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +SemanticMeasure.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +SemanticMeasure.METRICS = RelationField("metrics") +SemanticMeasure.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SemanticMeasure.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +SemanticMeasure.MEANINGS = RelationField("meanings") +SemanticMeasure.MC_MONITORS = RelationField("mcMonitors") +SemanticMeasure.MC_INCIDENTS = RelationField("mcIncidents") +SemanticMeasure.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SemanticMeasure.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SemanticMeasure.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SemanticMeasure.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SemanticMeasure.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +SemanticMeasure.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +SemanticMeasure.FILES = RelationField("files") +SemanticMeasure.LINKS = RelationField("links") +SemanticMeasure.README = RelationField("readme") +SemanticMeasure.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +SemanticMeasure.SEMANTIC_MODEL = RelationField("semanticModel") +SemanticMeasure.SODA_CHECKS = RelationField("sodaChecks") +SemanticMeasure.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SemanticMeasure.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/semantic_model.py b/pyatlan_v9/model/assets/semantic_model.py new file mode 100644 index 000000000..60bb877f2 --- /dev/null +++ b/pyatlan_v9/model/assets/semantic_model.py @@ -0,0 +1,566 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SemanticModel asset model with flattened inheritance. + +This module provides: +- SemanticModel: Flat asset class (easy to use) +- SemanticModelAttributes: Nested attributes struct (extends AssetAttributes) +- SemanticModelNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .semantic_related import ( + RelatedSemanticDimension, + RelatedSemanticEntity, + RelatedSemanticMeasure, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SemanticModel(Asset): + """ + Base class for semantic models across different sources. + """ + + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SEMANTIC_DIMENSIONS: ClassVar[Any] = None + SEMANTIC_MEASURES: ClassVar[Any] = None + SEMANTIC_ENTITIES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SemanticModel" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + semantic_dimensions: Union[List[RelatedSemanticDimension], None, UnsetType] = UNSET + """Dimensions that exist within this semantic model.""" + + semantic_measures: Union[List[RelatedSemanticMeasure], None, UnsetType] = UNSET + """Measures that exist within this semantic model.""" + + semantic_entities: Union[List[RelatedSemanticEntity], None, UnsetType] = UNSET + """Entities that exist within this semantic model.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SemanticModel" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _semantic_model_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> SemanticModel: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SemanticModel instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _semantic_model_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SemanticModelAttributes(AssetAttributes): + """SemanticModel-specific attributes for nested API format.""" + + pass + + +class SemanticModelRelationshipAttributes(AssetRelationshipAttributes): + """SemanticModel-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + semantic_dimensions: Union[List[RelatedSemanticDimension], None, UnsetType] = UNSET + """Dimensions that exist within this semantic model.""" + + semantic_measures: Union[List[RelatedSemanticMeasure], None, UnsetType] = UNSET + """Measures that exist within this semantic model.""" + + semantic_entities: Union[List[RelatedSemanticEntity], None, UnsetType] = UNSET + """Entities that exist within this semantic model.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SemanticModelNested(AssetNested): + """SemanticModel in nested API format for high-performance serialization.""" + + attributes: Union[SemanticModelAttributes, UnsetType] = UNSET + relationship_attributes: Union[SemanticModelRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + SemanticModelRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SemanticModelRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SEMANTIC_MODEL_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "semantic_dimensions", + "semantic_measures", + "semantic_entities", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_semantic_model_attrs( + attrs: SemanticModelAttributes, obj: SemanticModel +) -> None: + """Populate SemanticModel-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + + +def _extract_semantic_model_attrs(attrs: SemanticModelAttributes) -> dict: + """Extract all SemanticModel attributes from the attrs struct into a flat dict.""" + return _extract_asset_attrs(attrs) + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _semantic_model_to_nested(semantic_model: SemanticModel) -> SemanticModelNested: + """Convert flat SemanticModel to nested format.""" + attrs = SemanticModelAttributes() + _populate_semantic_model_attrs(attrs, semantic_model) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + semantic_model, _SEMANTIC_MODEL_REL_FIELDS, SemanticModelRelationshipAttributes + ) + return SemanticModelNested( + guid=semantic_model.guid, + type_name=semantic_model.type_name, + status=semantic_model.status, + version=semantic_model.version, + create_time=semantic_model.create_time, + update_time=semantic_model.update_time, + created_by=semantic_model.created_by, + updated_by=semantic_model.updated_by, + classifications=semantic_model.classifications, + classification_names=semantic_model.classification_names, + meanings=semantic_model.meanings, + labels=semantic_model.labels, + business_attributes=semantic_model.business_attributes, + custom_attributes=semantic_model.custom_attributes, + pending_tasks=semantic_model.pending_tasks, + proxy=semantic_model.proxy, + is_incomplete=semantic_model.is_incomplete, + provenance_type=semantic_model.provenance_type, + home_id=semantic_model.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _semantic_model_from_nested(nested: SemanticModelNested) -> SemanticModel: + """Convert nested format to flat SemanticModel.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else SemanticModelAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SEMANTIC_MODEL_REL_FIELDS, + SemanticModelRelationshipAttributes, + ) + return SemanticModel( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_semantic_model_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _semantic_model_to_nested_bytes( + semantic_model: SemanticModel, serde: Serde +) -> bytes: + """Convert flat SemanticModel to nested JSON bytes.""" + return serde.encode(_semantic_model_to_nested(semantic_model)) + + +def _semantic_model_from_nested_bytes(data: bytes, serde: Serde) -> SemanticModel: + """Convert nested JSON bytes to flat SemanticModel.""" + nested = serde.decode(data, SemanticModelNested) + return _semantic_model_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import RelationField # noqa: E402 + +SemanticModel.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SemanticModel.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +SemanticModel.ANOMALO_CHECKS = RelationField("anomaloChecks") +SemanticModel.APPLICATION = RelationField("application") +SemanticModel.APPLICATION_FIELD = RelationField("applicationField") +SemanticModel.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +SemanticModel.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +SemanticModel.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +SemanticModel.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +SemanticModel.METRICS = RelationField("metrics") +SemanticModel.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SemanticModel.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +SemanticModel.MEANINGS = RelationField("meanings") +SemanticModel.MC_MONITORS = RelationField("mcMonitors") +SemanticModel.MC_INCIDENTS = RelationField("mcIncidents") +SemanticModel.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SemanticModel.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SemanticModel.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SemanticModel.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SemanticModel.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +SemanticModel.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +SemanticModel.FILES = RelationField("files") +SemanticModel.LINKS = RelationField("links") +SemanticModel.README = RelationField("readme") +SemanticModel.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +SemanticModel.SEMANTIC_DIMENSIONS = RelationField("semanticDimensions") +SemanticModel.SEMANTIC_MEASURES = RelationField("semanticMeasures") +SemanticModel.SEMANTIC_ENTITIES = RelationField("semanticEntities") +SemanticModel.SODA_CHECKS = RelationField("sodaChecks") +SemanticModel.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SemanticModel.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/semantic_related.py b/pyatlan_v9/model/assets/semantic_related.py new file mode 100644 index 000000000..29d5b2343 --- /dev/null +++ b/pyatlan_v9/model/assets/semantic_related.py @@ -0,0 +1,139 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Semantic module. + +This module contains all Related{Type} classes for the Semantic type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import List, Union + +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedCatalog +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedSemantic", + "RelatedSemanticModel", + "RelatedSemanticField", + "RelatedSemanticMeasure", + "RelatedSemanticDimension", + "RelatedSemanticEntity", +] + + +class RelatedSemantic(RelatedCatalog): + """ + Related entity reference for Semantic assets. + + Extends RelatedCatalog with Semantic-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Semantic" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Semantic" + + +class RelatedSemanticModel(RelatedSemantic): + """ + Related entity reference for SemanticModel assets. + + Extends RelatedSemantic with SemanticModel-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SemanticModel" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SemanticModel" + + +class RelatedSemanticField(RelatedSemantic): + """ + Related entity reference for SemanticField assets. + + Extends RelatedSemantic with SemanticField-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SemanticField" so it serializes correctly + + semantic_expression: Union[str, None, UnsetType] = UNSET + """Column name or SQL expression for the semantic field.""" + + semantic_type: Union[str, None, UnsetType] = UNSET + """Detailed type of the semantic field (e.g., type of measure, type of dimension, or type of entity).""" + + semantic_synonyms: Union[List[str], None, UnsetType] = UNSET + """Alternative names or terms for the semantic field.""" + + semantic_sample_values: Union[List[str], None, UnsetType] = UNSET + """Sample values for the semantic field.""" + + semantic_access_modifier: Union[str, None, UnsetType] = UNSET + """Access level for the semantic field (e.g., public_access/private_access).""" + + semantic_data_type: Union[str, None, UnsetType] = UNSET + """Data type of the semantic field.""" + + semantic_labels: Union[List[str], None, UnsetType] = UNSET + """Labels associated with the semantic field.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SemanticField" + + +class RelatedSemanticMeasure(RelatedSemantic): + """ + Related entity reference for SemanticMeasure assets. + + Extends RelatedSemantic with SemanticMeasure-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SemanticMeasure" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SemanticMeasure" + + +class RelatedSemanticDimension(RelatedSemantic): + """ + Related entity reference for SemanticDimension assets. + + Extends RelatedSemantic with SemanticDimension-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SemanticDimension" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SemanticDimension" + + +class RelatedSemanticEntity(RelatedSemantic): + """ + Related entity reference for SemanticEntity assets. + + Extends RelatedSemantic with SemanticEntity-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SemanticEntity" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SemanticEntity" diff --git a/pyatlan_v9/model/assets/sigma.py b/pyatlan_v9/model/assets/sigma.py new file mode 100644 index 000000000..bd2c0f2b4 --- /dev/null +++ b/pyatlan_v9/model/assets/sigma.py @@ -0,0 +1,600 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Sigma asset model with flattened inheritance. + +This module provides: +- Sigma: Flat asset class (easy to use) +- SigmaAttributes: Nested attributes struct (extends AssetAttributes) +- SigmaNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Sigma(Asset): + """ + Base class for Sigma assets. + """ + + SIGMA_WORKBOOK_QUALIFIED_NAME: ClassVar[Any] = None + SIGMA_WORKBOOK_NAME: ClassVar[Any] = None + SIGMA_PAGE_QUALIFIED_NAME: ClassVar[Any] = None + SIGMA_PAGE_NAME: ClassVar[Any] = None + SIGMA_DATA_ELEMENT_QUALIFIED_NAME: ClassVar[Any] = None + SIGMA_DATA_ELEMENT_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Sigma" + + sigma_workbook_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workbook in which this asset exists.""" + + sigma_workbook_name: Union[str, None, UnsetType] = UNSET + """Simple name of the workbook in which this asset exists.""" + + sigma_page_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the page on which this asset exists.""" + + sigma_page_name: Union[str, None, UnsetType] = UNSET + """Simple name of the page on which this asset exists.""" + + sigma_data_element_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the data element in which this asset exists.""" + + sigma_data_element_name: Union[str, None, UnsetType] = UNSET + """Simple name of the data element in which this asset exists.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Sigma" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _sigma_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Sigma: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Sigma instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _sigma_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SigmaAttributes(AssetAttributes): + """Sigma-specific attributes for nested API format.""" + + sigma_workbook_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workbook in which this asset exists.""" + + sigma_workbook_name: Union[str, None, UnsetType] = UNSET + """Simple name of the workbook in which this asset exists.""" + + sigma_page_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the page on which this asset exists.""" + + sigma_page_name: Union[str, None, UnsetType] = UNSET + """Simple name of the page on which this asset exists.""" + + sigma_data_element_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the data element in which this asset exists.""" + + sigma_data_element_name: Union[str, None, UnsetType] = UNSET + """Simple name of the data element in which this asset exists.""" + + +class SigmaRelationshipAttributes(AssetRelationshipAttributes): + """Sigma-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SigmaNested(AssetNested): + """Sigma in nested API format for high-performance serialization.""" + + attributes: Union[SigmaAttributes, UnsetType] = UNSET + relationship_attributes: Union[SigmaRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[SigmaRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[SigmaRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SIGMA_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_sigma_attrs(attrs: SigmaAttributes, obj: Sigma) -> None: + """Populate Sigma-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.sigma_workbook_qualified_name = obj.sigma_workbook_qualified_name + attrs.sigma_workbook_name = obj.sigma_workbook_name + attrs.sigma_page_qualified_name = obj.sigma_page_qualified_name + attrs.sigma_page_name = obj.sigma_page_name + attrs.sigma_data_element_qualified_name = obj.sigma_data_element_qualified_name + attrs.sigma_data_element_name = obj.sigma_data_element_name + + +def _extract_sigma_attrs(attrs: SigmaAttributes) -> dict: + """Extract all Sigma attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["sigma_workbook_qualified_name"] = attrs.sigma_workbook_qualified_name + result["sigma_workbook_name"] = attrs.sigma_workbook_name + result["sigma_page_qualified_name"] = attrs.sigma_page_qualified_name + result["sigma_page_name"] = attrs.sigma_page_name + result["sigma_data_element_qualified_name"] = ( + attrs.sigma_data_element_qualified_name + ) + result["sigma_data_element_name"] = attrs.sigma_data_element_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _sigma_to_nested(sigma: Sigma) -> SigmaNested: + """Convert flat Sigma to nested format.""" + attrs = SigmaAttributes() + _populate_sigma_attrs(attrs, sigma) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + sigma, _SIGMA_REL_FIELDS, SigmaRelationshipAttributes + ) + return SigmaNested( + guid=sigma.guid, + type_name=sigma.type_name, + status=sigma.status, + version=sigma.version, + create_time=sigma.create_time, + update_time=sigma.update_time, + created_by=sigma.created_by, + updated_by=sigma.updated_by, + classifications=sigma.classifications, + classification_names=sigma.classification_names, + meanings=sigma.meanings, + labels=sigma.labels, + business_attributes=sigma.business_attributes, + custom_attributes=sigma.custom_attributes, + pending_tasks=sigma.pending_tasks, + proxy=sigma.proxy, + is_incomplete=sigma.is_incomplete, + provenance_type=sigma.provenance_type, + home_id=sigma.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _sigma_from_nested(nested: SigmaNested) -> Sigma: + """Convert nested format to flat Sigma.""" + attrs = nested.attributes if nested.attributes is not UNSET else SigmaAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SIGMA_REL_FIELDS, + SigmaRelationshipAttributes, + ) + return Sigma( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_sigma_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _sigma_to_nested_bytes(sigma: Sigma, serde: Serde) -> bytes: + """Convert flat Sigma to nested JSON bytes.""" + return serde.encode(_sigma_to_nested(sigma)) + + +def _sigma_from_nested_bytes(data: bytes, serde: Serde) -> Sigma: + """Convert nested JSON bytes to flat Sigma.""" + nested = serde.decode(data, SigmaNested) + return _sigma_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + RelationField, +) + +Sigma.SIGMA_WORKBOOK_QUALIFIED_NAME = KeywordTextField( + "sigmaWorkbookQualifiedName", + "sigmaWorkbookQualifiedName", + "sigmaWorkbookQualifiedName.text", +) +Sigma.SIGMA_WORKBOOK_NAME = KeywordField("sigmaWorkbookName", "sigmaWorkbookName") +Sigma.SIGMA_PAGE_QUALIFIED_NAME = KeywordTextField( + "sigmaPageQualifiedName", "sigmaPageQualifiedName", "sigmaPageQualifiedName.text" +) +Sigma.SIGMA_PAGE_NAME = KeywordField("sigmaPageName", "sigmaPageName") +Sigma.SIGMA_DATA_ELEMENT_QUALIFIED_NAME = KeywordTextField( + "sigmaDataElementQualifiedName", + "sigmaDataElementQualifiedName", + "sigmaDataElementQualifiedName.text", +) +Sigma.SIGMA_DATA_ELEMENT_NAME = KeywordField( + "sigmaDataElementName", "sigmaDataElementName" +) +Sigma.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Sigma.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Sigma.ANOMALO_CHECKS = RelationField("anomaloChecks") +Sigma.APPLICATION = RelationField("application") +Sigma.APPLICATION_FIELD = RelationField("applicationField") +Sigma.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Sigma.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Sigma.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Sigma.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Sigma.METRICS = RelationField("metrics") +Sigma.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Sigma.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Sigma.MEANINGS = RelationField("meanings") +Sigma.MC_MONITORS = RelationField("mcMonitors") +Sigma.MC_INCIDENTS = RelationField("mcIncidents") +Sigma.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Sigma.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Sigma.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Sigma.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Sigma.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Sigma.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Sigma.FILES = RelationField("files") +Sigma.LINKS = RelationField("links") +Sigma.README = RelationField("readme") +Sigma.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Sigma.SODA_CHECKS = RelationField("sodaChecks") +Sigma.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Sigma.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/sigma_data_element.py b/pyatlan_v9/model/assets/sigma_data_element.py new file mode 100644 index 000000000..a79c0b3af --- /dev/null +++ b/pyatlan_v9/model/assets/sigma_data_element.py @@ -0,0 +1,692 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SigmaDataElement asset model with flattened inheritance. + +This module provides: +- SigmaDataElement: Flat asset class (easy to use) +- SigmaDataElementAttributes: Nested attributes struct (extends AssetAttributes) +- SigmaDataElementNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .sigma_related import RelatedSigmaDataElementField, RelatedSigmaPage + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SigmaDataElement(Asset): + """ + Instance of a Sigma data element in Atlan. + """ + + SIGMA_DATA_ELEMENT_QUERY: ClassVar[Any] = None + SIGMA_DATA_ELEMENT_TYPE: ClassVar[Any] = None + SIGMA_DATA_ELEMENT_FIELD_COUNT: ClassVar[Any] = None + SIGMA_WORKBOOK_QUALIFIED_NAME: ClassVar[Any] = None + SIGMA_WORKBOOK_NAME: ClassVar[Any] = None + SIGMA_PAGE_QUALIFIED_NAME: ClassVar[Any] = None + SIGMA_PAGE_NAME: ClassVar[Any] = None + SIGMA_DATA_ELEMENT_QUALIFIED_NAME: ClassVar[Any] = None + SIGMA_DATA_ELEMENT_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SIGMA_PAGE: ClassVar[Any] = None + SIGMA_DATA_ELEMENT_FIELDS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SigmaDataElement" + + sigma_data_element_query: Union[str, None, UnsetType] = UNSET + """""" + + sigma_data_element_type: Union[str, None, UnsetType] = UNSET + """""" + + sigma_data_element_field_count: Union[int, None, UnsetType] = UNSET + """Number of fields in this data element.""" + + sigma_workbook_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workbook in which this asset exists.""" + + sigma_workbook_name: Union[str, None, UnsetType] = UNSET + """Simple name of the workbook in which this asset exists.""" + + sigma_page_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the page on which this asset exists.""" + + sigma_page_name: Union[str, None, UnsetType] = UNSET + """Simple name of the page on which this asset exists.""" + + sigma_data_element_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the data element in which this asset exists.""" + + sigma_data_element_name: Union[str, None, UnsetType] = UNSET + """Simple name of the data element in which this asset exists.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + sigma_page: Union[RelatedSigmaPage, None, UnsetType] = UNSET + """Page on which this data element exists.""" + + sigma_data_element_fields: Union[ + List[RelatedSigmaDataElementField], None, UnsetType + ] = UNSET + """Data element fields that exist in this data element.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SigmaDataElement" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _sigma_data_element_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> SigmaDataElement: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SigmaDataElement instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _sigma_data_element_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SigmaDataElementAttributes(AssetAttributes): + """SigmaDataElement-specific attributes for nested API format.""" + + sigma_data_element_query: Union[str, None, UnsetType] = UNSET + """""" + + sigma_data_element_type: Union[str, None, UnsetType] = UNSET + """""" + + sigma_data_element_field_count: Union[int, None, UnsetType] = UNSET + """Number of fields in this data element.""" + + sigma_workbook_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workbook in which this asset exists.""" + + sigma_workbook_name: Union[str, None, UnsetType] = UNSET + """Simple name of the workbook in which this asset exists.""" + + sigma_page_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the page on which this asset exists.""" + + sigma_page_name: Union[str, None, UnsetType] = UNSET + """Simple name of the page on which this asset exists.""" + + sigma_data_element_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the data element in which this asset exists.""" + + sigma_data_element_name: Union[str, None, UnsetType] = UNSET + """Simple name of the data element in which this asset exists.""" + + +class SigmaDataElementRelationshipAttributes(AssetRelationshipAttributes): + """SigmaDataElement-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + sigma_page: Union[RelatedSigmaPage, None, UnsetType] = UNSET + """Page on which this data element exists.""" + + sigma_data_element_fields: Union[ + List[RelatedSigmaDataElementField], None, UnsetType + ] = UNSET + """Data element fields that exist in this data element.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SigmaDataElementNested(AssetNested): + """SigmaDataElement in nested API format for high-performance serialization.""" + + attributes: Union[SigmaDataElementAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + SigmaDataElementRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + SigmaDataElementRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SigmaDataElementRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SIGMA_DATA_ELEMENT_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "sigma_page", + "sigma_data_element_fields", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_sigma_data_element_attrs( + attrs: SigmaDataElementAttributes, obj: SigmaDataElement +) -> None: + """Populate SigmaDataElement-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.sigma_data_element_query = obj.sigma_data_element_query + attrs.sigma_data_element_type = obj.sigma_data_element_type + attrs.sigma_data_element_field_count = obj.sigma_data_element_field_count + attrs.sigma_workbook_qualified_name = obj.sigma_workbook_qualified_name + attrs.sigma_workbook_name = obj.sigma_workbook_name + attrs.sigma_page_qualified_name = obj.sigma_page_qualified_name + attrs.sigma_page_name = obj.sigma_page_name + attrs.sigma_data_element_qualified_name = obj.sigma_data_element_qualified_name + attrs.sigma_data_element_name = obj.sigma_data_element_name + + +def _extract_sigma_data_element_attrs(attrs: SigmaDataElementAttributes) -> dict: + """Extract all SigmaDataElement attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["sigma_data_element_query"] = attrs.sigma_data_element_query + result["sigma_data_element_type"] = attrs.sigma_data_element_type + result["sigma_data_element_field_count"] = attrs.sigma_data_element_field_count + result["sigma_workbook_qualified_name"] = attrs.sigma_workbook_qualified_name + result["sigma_workbook_name"] = attrs.sigma_workbook_name + result["sigma_page_qualified_name"] = attrs.sigma_page_qualified_name + result["sigma_page_name"] = attrs.sigma_page_name + result["sigma_data_element_qualified_name"] = ( + attrs.sigma_data_element_qualified_name + ) + result["sigma_data_element_name"] = attrs.sigma_data_element_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _sigma_data_element_to_nested( + sigma_data_element: SigmaDataElement, +) -> SigmaDataElementNested: + """Convert flat SigmaDataElement to nested format.""" + attrs = SigmaDataElementAttributes() + _populate_sigma_data_element_attrs(attrs, sigma_data_element) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + sigma_data_element, + _SIGMA_DATA_ELEMENT_REL_FIELDS, + SigmaDataElementRelationshipAttributes, + ) + return SigmaDataElementNested( + guid=sigma_data_element.guid, + type_name=sigma_data_element.type_name, + status=sigma_data_element.status, + version=sigma_data_element.version, + create_time=sigma_data_element.create_time, + update_time=sigma_data_element.update_time, + created_by=sigma_data_element.created_by, + updated_by=sigma_data_element.updated_by, + classifications=sigma_data_element.classifications, + classification_names=sigma_data_element.classification_names, + meanings=sigma_data_element.meanings, + labels=sigma_data_element.labels, + business_attributes=sigma_data_element.business_attributes, + custom_attributes=sigma_data_element.custom_attributes, + pending_tasks=sigma_data_element.pending_tasks, + proxy=sigma_data_element.proxy, + is_incomplete=sigma_data_element.is_incomplete, + provenance_type=sigma_data_element.provenance_type, + home_id=sigma_data_element.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _sigma_data_element_from_nested(nested: SigmaDataElementNested) -> SigmaDataElement: + """Convert nested format to flat SigmaDataElement.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else SigmaDataElementAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SIGMA_DATA_ELEMENT_REL_FIELDS, + SigmaDataElementRelationshipAttributes, + ) + return SigmaDataElement( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_sigma_data_element_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _sigma_data_element_to_nested_bytes( + sigma_data_element: SigmaDataElement, serde: Serde +) -> bytes: + """Convert flat SigmaDataElement to nested JSON bytes.""" + return serde.encode(_sigma_data_element_to_nested(sigma_data_element)) + + +def _sigma_data_element_from_nested_bytes( + data: bytes, serde: Serde +) -> SigmaDataElement: + """Convert nested JSON bytes to flat SigmaDataElement.""" + nested = serde.decode(data, SigmaDataElementNested) + return _sigma_data_element_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +SigmaDataElement.SIGMA_DATA_ELEMENT_QUERY = KeywordField( + "sigmaDataElementQuery", "sigmaDataElementQuery" +) +SigmaDataElement.SIGMA_DATA_ELEMENT_TYPE = KeywordField( + "sigmaDataElementType", "sigmaDataElementType" +) +SigmaDataElement.SIGMA_DATA_ELEMENT_FIELD_COUNT = NumericField( + "sigmaDataElementFieldCount", "sigmaDataElementFieldCount" +) +SigmaDataElement.SIGMA_WORKBOOK_QUALIFIED_NAME = KeywordTextField( + "sigmaWorkbookQualifiedName", + "sigmaWorkbookQualifiedName", + "sigmaWorkbookQualifiedName.text", +) +SigmaDataElement.SIGMA_WORKBOOK_NAME = KeywordField( + "sigmaWorkbookName", "sigmaWorkbookName" +) +SigmaDataElement.SIGMA_PAGE_QUALIFIED_NAME = KeywordTextField( + "sigmaPageQualifiedName", "sigmaPageQualifiedName", "sigmaPageQualifiedName.text" +) +SigmaDataElement.SIGMA_PAGE_NAME = KeywordField("sigmaPageName", "sigmaPageName") +SigmaDataElement.SIGMA_DATA_ELEMENT_QUALIFIED_NAME = KeywordTextField( + "sigmaDataElementQualifiedName", + "sigmaDataElementQualifiedName", + "sigmaDataElementQualifiedName.text", +) +SigmaDataElement.SIGMA_DATA_ELEMENT_NAME = KeywordField( + "sigmaDataElementName", "sigmaDataElementName" +) +SigmaDataElement.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SigmaDataElement.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +SigmaDataElement.ANOMALO_CHECKS = RelationField("anomaloChecks") +SigmaDataElement.APPLICATION = RelationField("application") +SigmaDataElement.APPLICATION_FIELD = RelationField("applicationField") +SigmaDataElement.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +SigmaDataElement.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +SigmaDataElement.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +SigmaDataElement.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +SigmaDataElement.METRICS = RelationField("metrics") +SigmaDataElement.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SigmaDataElement.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +SigmaDataElement.MEANINGS = RelationField("meanings") +SigmaDataElement.MC_MONITORS = RelationField("mcMonitors") +SigmaDataElement.MC_INCIDENTS = RelationField("mcIncidents") +SigmaDataElement.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SigmaDataElement.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SigmaDataElement.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SigmaDataElement.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SigmaDataElement.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +SigmaDataElement.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +SigmaDataElement.FILES = RelationField("files") +SigmaDataElement.LINKS = RelationField("links") +SigmaDataElement.README = RelationField("readme") +SigmaDataElement.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +SigmaDataElement.SIGMA_PAGE = RelationField("sigmaPage") +SigmaDataElement.SIGMA_DATA_ELEMENT_FIELDS = RelationField("sigmaDataElementFields") +SigmaDataElement.SODA_CHECKS = RelationField("sodaChecks") +SigmaDataElement.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SigmaDataElement.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/sigma_data_element_field.py b/pyatlan_v9/model/assets/sigma_data_element_field.py new file mode 100644 index 000000000..e996f2617 --- /dev/null +++ b/pyatlan_v9/model/assets/sigma_data_element_field.py @@ -0,0 +1,679 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SigmaDataElementField asset model with flattened inheritance. + +This module provides: +- SigmaDataElementField: Flat asset class (easy to use) +- SigmaDataElementFieldAttributes: Nested attributes struct (extends AssetAttributes) +- SigmaDataElementFieldNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .sigma_related import RelatedSigmaDataElement + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SigmaDataElementField(Asset): + """ + Instance of a Sigma data element field in Atlan. + """ + + SIGMA_IS_HIDDEN: ClassVar[Any] = None + SIGMA_DATA_ELEMENT_FIELD_FORMULA: ClassVar[Any] = None + SIGMA_WORKBOOK_QUALIFIED_NAME: ClassVar[Any] = None + SIGMA_WORKBOOK_NAME: ClassVar[Any] = None + SIGMA_PAGE_QUALIFIED_NAME: ClassVar[Any] = None + SIGMA_PAGE_NAME: ClassVar[Any] = None + SIGMA_DATA_ELEMENT_QUALIFIED_NAME: ClassVar[Any] = None + SIGMA_DATA_ELEMENT_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SIGMA_DATA_ELEMENT: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SigmaDataElementField" + + sigma_is_hidden: Union[bool, None, UnsetType] = UNSET + """Whether this field is hidden (true) or not (false).""" + + sigma_data_element_field_formula: Union[str, None, UnsetType] = UNSET + """""" + + sigma_workbook_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workbook in which this asset exists.""" + + sigma_workbook_name: Union[str, None, UnsetType] = UNSET + """Simple name of the workbook in which this asset exists.""" + + sigma_page_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the page on which this asset exists.""" + + sigma_page_name: Union[str, None, UnsetType] = UNSET + """Simple name of the page on which this asset exists.""" + + sigma_data_element_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the data element in which this asset exists.""" + + sigma_data_element_name: Union[str, None, UnsetType] = UNSET + """Simple name of the data element in which this asset exists.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + sigma_data_element: Union[RelatedSigmaDataElement, None, UnsetType] = UNSET + """Data element in which this data element field exists.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SigmaDataElementField" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _sigma_data_element_field_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> SigmaDataElementField: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SigmaDataElementField instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _sigma_data_element_field_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SigmaDataElementFieldAttributes(AssetAttributes): + """SigmaDataElementField-specific attributes for nested API format.""" + + sigma_is_hidden: Union[bool, None, UnsetType] = UNSET + """Whether this field is hidden (true) or not (false).""" + + sigma_data_element_field_formula: Union[str, None, UnsetType] = UNSET + """""" + + sigma_workbook_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workbook in which this asset exists.""" + + sigma_workbook_name: Union[str, None, UnsetType] = UNSET + """Simple name of the workbook in which this asset exists.""" + + sigma_page_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the page on which this asset exists.""" + + sigma_page_name: Union[str, None, UnsetType] = UNSET + """Simple name of the page on which this asset exists.""" + + sigma_data_element_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the data element in which this asset exists.""" + + sigma_data_element_name: Union[str, None, UnsetType] = UNSET + """Simple name of the data element in which this asset exists.""" + + +class SigmaDataElementFieldRelationshipAttributes(AssetRelationshipAttributes): + """SigmaDataElementField-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + sigma_data_element: Union[RelatedSigmaDataElement, None, UnsetType] = UNSET + """Data element in which this data element field exists.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SigmaDataElementFieldNested(AssetNested): + """SigmaDataElementField in nested API format for high-performance serialization.""" + + attributes: Union[SigmaDataElementFieldAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + SigmaDataElementFieldRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + SigmaDataElementFieldRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SigmaDataElementFieldRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SIGMA_DATA_ELEMENT_FIELD_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "sigma_data_element", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_sigma_data_element_field_attrs( + attrs: SigmaDataElementFieldAttributes, obj: SigmaDataElementField +) -> None: + """Populate SigmaDataElementField-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.sigma_is_hidden = obj.sigma_is_hidden + attrs.sigma_data_element_field_formula = obj.sigma_data_element_field_formula + attrs.sigma_workbook_qualified_name = obj.sigma_workbook_qualified_name + attrs.sigma_workbook_name = obj.sigma_workbook_name + attrs.sigma_page_qualified_name = obj.sigma_page_qualified_name + attrs.sigma_page_name = obj.sigma_page_name + attrs.sigma_data_element_qualified_name = obj.sigma_data_element_qualified_name + attrs.sigma_data_element_name = obj.sigma_data_element_name + + +def _extract_sigma_data_element_field_attrs( + attrs: SigmaDataElementFieldAttributes, +) -> dict: + """Extract all SigmaDataElementField attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["sigma_is_hidden"] = attrs.sigma_is_hidden + result["sigma_data_element_field_formula"] = attrs.sigma_data_element_field_formula + result["sigma_workbook_qualified_name"] = attrs.sigma_workbook_qualified_name + result["sigma_workbook_name"] = attrs.sigma_workbook_name + result["sigma_page_qualified_name"] = attrs.sigma_page_qualified_name + result["sigma_page_name"] = attrs.sigma_page_name + result["sigma_data_element_qualified_name"] = ( + attrs.sigma_data_element_qualified_name + ) + result["sigma_data_element_name"] = attrs.sigma_data_element_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _sigma_data_element_field_to_nested( + sigma_data_element_field: SigmaDataElementField, +) -> SigmaDataElementFieldNested: + """Convert flat SigmaDataElementField to nested format.""" + attrs = SigmaDataElementFieldAttributes() + _populate_sigma_data_element_field_attrs(attrs, sigma_data_element_field) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + sigma_data_element_field, + _SIGMA_DATA_ELEMENT_FIELD_REL_FIELDS, + SigmaDataElementFieldRelationshipAttributes, + ) + return SigmaDataElementFieldNested( + guid=sigma_data_element_field.guid, + type_name=sigma_data_element_field.type_name, + status=sigma_data_element_field.status, + version=sigma_data_element_field.version, + create_time=sigma_data_element_field.create_time, + update_time=sigma_data_element_field.update_time, + created_by=sigma_data_element_field.created_by, + updated_by=sigma_data_element_field.updated_by, + classifications=sigma_data_element_field.classifications, + classification_names=sigma_data_element_field.classification_names, + meanings=sigma_data_element_field.meanings, + labels=sigma_data_element_field.labels, + business_attributes=sigma_data_element_field.business_attributes, + custom_attributes=sigma_data_element_field.custom_attributes, + pending_tasks=sigma_data_element_field.pending_tasks, + proxy=sigma_data_element_field.proxy, + is_incomplete=sigma_data_element_field.is_incomplete, + provenance_type=sigma_data_element_field.provenance_type, + home_id=sigma_data_element_field.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _sigma_data_element_field_from_nested( + nested: SigmaDataElementFieldNested, +) -> SigmaDataElementField: + """Convert nested format to flat SigmaDataElementField.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else SigmaDataElementFieldAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SIGMA_DATA_ELEMENT_FIELD_REL_FIELDS, + SigmaDataElementFieldRelationshipAttributes, + ) + return SigmaDataElementField( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_sigma_data_element_field_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _sigma_data_element_field_to_nested_bytes( + sigma_data_element_field: SigmaDataElementField, serde: Serde +) -> bytes: + """Convert flat SigmaDataElementField to nested JSON bytes.""" + return serde.encode(_sigma_data_element_field_to_nested(sigma_data_element_field)) + + +def _sigma_data_element_field_from_nested_bytes( + data: bytes, serde: Serde +) -> SigmaDataElementField: + """Convert nested JSON bytes to flat SigmaDataElementField.""" + nested = serde.decode(data, SigmaDataElementFieldNested) + return _sigma_data_element_field_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + RelationField, +) + +SigmaDataElementField.SIGMA_IS_HIDDEN = BooleanField("sigmaIsHidden", "sigmaIsHidden") +SigmaDataElementField.SIGMA_DATA_ELEMENT_FIELD_FORMULA = KeywordField( + "sigmaDataElementFieldFormula", "sigmaDataElementFieldFormula" +) +SigmaDataElementField.SIGMA_WORKBOOK_QUALIFIED_NAME = KeywordTextField( + "sigmaWorkbookQualifiedName", + "sigmaWorkbookQualifiedName", + "sigmaWorkbookQualifiedName.text", +) +SigmaDataElementField.SIGMA_WORKBOOK_NAME = KeywordField( + "sigmaWorkbookName", "sigmaWorkbookName" +) +SigmaDataElementField.SIGMA_PAGE_QUALIFIED_NAME = KeywordTextField( + "sigmaPageQualifiedName", "sigmaPageQualifiedName", "sigmaPageQualifiedName.text" +) +SigmaDataElementField.SIGMA_PAGE_NAME = KeywordField("sigmaPageName", "sigmaPageName") +SigmaDataElementField.SIGMA_DATA_ELEMENT_QUALIFIED_NAME = KeywordTextField( + "sigmaDataElementQualifiedName", + "sigmaDataElementQualifiedName", + "sigmaDataElementQualifiedName.text", +) +SigmaDataElementField.SIGMA_DATA_ELEMENT_NAME = KeywordField( + "sigmaDataElementName", "sigmaDataElementName" +) +SigmaDataElementField.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SigmaDataElementField.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +SigmaDataElementField.ANOMALO_CHECKS = RelationField("anomaloChecks") +SigmaDataElementField.APPLICATION = RelationField("application") +SigmaDataElementField.APPLICATION_FIELD = RelationField("applicationField") +SigmaDataElementField.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +SigmaDataElementField.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +SigmaDataElementField.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +SigmaDataElementField.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +SigmaDataElementField.METRICS = RelationField("metrics") +SigmaDataElementField.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SigmaDataElementField.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +SigmaDataElementField.MEANINGS = RelationField("meanings") +SigmaDataElementField.MC_MONITORS = RelationField("mcMonitors") +SigmaDataElementField.MC_INCIDENTS = RelationField("mcIncidents") +SigmaDataElementField.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SigmaDataElementField.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SigmaDataElementField.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SigmaDataElementField.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SigmaDataElementField.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +SigmaDataElementField.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +SigmaDataElementField.FILES = RelationField("files") +SigmaDataElementField.LINKS = RelationField("links") +SigmaDataElementField.README = RelationField("readme") +SigmaDataElementField.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +SigmaDataElementField.SIGMA_DATA_ELEMENT = RelationField("sigmaDataElement") +SigmaDataElementField.SODA_CHECKS = RelationField("sodaChecks") +SigmaDataElementField.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SigmaDataElementField.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/sigma_dataset.py b/pyatlan_v9/model/assets/sigma_dataset.py new file mode 100644 index 000000000..7f1bfc172 --- /dev/null +++ b/pyatlan_v9/model/assets/sigma_dataset.py @@ -0,0 +1,636 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SigmaDataset asset model with flattened inheritance. + +This module provides: +- SigmaDataset: Flat asset class (easy to use) +- SigmaDatasetAttributes: Nested attributes struct (extends AssetAttributes) +- SigmaDatasetNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .sigma_related import RelatedSigmaDatasetColumn + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SigmaDataset(Asset): + """ + Instance of a Sigma dataset in Atlan. + """ + + SIGMA_COLUMN_COUNT: ClassVar[Any] = None + SIGMA_WORKBOOK_QUALIFIED_NAME: ClassVar[Any] = None + SIGMA_WORKBOOK_NAME: ClassVar[Any] = None + SIGMA_PAGE_QUALIFIED_NAME: ClassVar[Any] = None + SIGMA_PAGE_NAME: ClassVar[Any] = None + SIGMA_DATA_ELEMENT_QUALIFIED_NAME: ClassVar[Any] = None + SIGMA_DATA_ELEMENT_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SIGMA_DATASET_COLUMNS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SigmaDataset" + + sigma_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this dataset.""" + + sigma_workbook_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workbook in which this asset exists.""" + + sigma_workbook_name: Union[str, None, UnsetType] = UNSET + """Simple name of the workbook in which this asset exists.""" + + sigma_page_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the page on which this asset exists.""" + + sigma_page_name: Union[str, None, UnsetType] = UNSET + """Simple name of the page on which this asset exists.""" + + sigma_data_element_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the data element in which this asset exists.""" + + sigma_data_element_name: Union[str, None, UnsetType] = UNSET + """Simple name of the data element in which this asset exists.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + sigma_dataset_columns: Union[List[RelatedSigmaDatasetColumn], None, UnsetType] = ( + UNSET + ) + """Dataset columns that exist in this dataset.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SigmaDataset" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _sigma_dataset_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> SigmaDataset: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SigmaDataset instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _sigma_dataset_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SigmaDatasetAttributes(AssetAttributes): + """SigmaDataset-specific attributes for nested API format.""" + + sigma_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this dataset.""" + + sigma_workbook_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workbook in which this asset exists.""" + + sigma_workbook_name: Union[str, None, UnsetType] = UNSET + """Simple name of the workbook in which this asset exists.""" + + sigma_page_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the page on which this asset exists.""" + + sigma_page_name: Union[str, None, UnsetType] = UNSET + """Simple name of the page on which this asset exists.""" + + sigma_data_element_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the data element in which this asset exists.""" + + sigma_data_element_name: Union[str, None, UnsetType] = UNSET + """Simple name of the data element in which this asset exists.""" + + +class SigmaDatasetRelationshipAttributes(AssetRelationshipAttributes): + """SigmaDataset-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + sigma_dataset_columns: Union[List[RelatedSigmaDatasetColumn], None, UnsetType] = ( + UNSET + ) + """Dataset columns that exist in this dataset.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SigmaDatasetNested(AssetNested): + """SigmaDataset in nested API format for high-performance serialization.""" + + attributes: Union[SigmaDatasetAttributes, UnsetType] = UNSET + relationship_attributes: Union[SigmaDatasetRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + SigmaDatasetRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SigmaDatasetRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SIGMA_DATASET_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "sigma_dataset_columns", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_sigma_dataset_attrs( + attrs: SigmaDatasetAttributes, obj: SigmaDataset +) -> None: + """Populate SigmaDataset-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.sigma_column_count = obj.sigma_column_count + attrs.sigma_workbook_qualified_name = obj.sigma_workbook_qualified_name + attrs.sigma_workbook_name = obj.sigma_workbook_name + attrs.sigma_page_qualified_name = obj.sigma_page_qualified_name + attrs.sigma_page_name = obj.sigma_page_name + attrs.sigma_data_element_qualified_name = obj.sigma_data_element_qualified_name + attrs.sigma_data_element_name = obj.sigma_data_element_name + + +def _extract_sigma_dataset_attrs(attrs: SigmaDatasetAttributes) -> dict: + """Extract all SigmaDataset attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["sigma_column_count"] = attrs.sigma_column_count + result["sigma_workbook_qualified_name"] = attrs.sigma_workbook_qualified_name + result["sigma_workbook_name"] = attrs.sigma_workbook_name + result["sigma_page_qualified_name"] = attrs.sigma_page_qualified_name + result["sigma_page_name"] = attrs.sigma_page_name + result["sigma_data_element_qualified_name"] = ( + attrs.sigma_data_element_qualified_name + ) + result["sigma_data_element_name"] = attrs.sigma_data_element_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _sigma_dataset_to_nested(sigma_dataset: SigmaDataset) -> SigmaDatasetNested: + """Convert flat SigmaDataset to nested format.""" + attrs = SigmaDatasetAttributes() + _populate_sigma_dataset_attrs(attrs, sigma_dataset) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + sigma_dataset, _SIGMA_DATASET_REL_FIELDS, SigmaDatasetRelationshipAttributes + ) + return SigmaDatasetNested( + guid=sigma_dataset.guid, + type_name=sigma_dataset.type_name, + status=sigma_dataset.status, + version=sigma_dataset.version, + create_time=sigma_dataset.create_time, + update_time=sigma_dataset.update_time, + created_by=sigma_dataset.created_by, + updated_by=sigma_dataset.updated_by, + classifications=sigma_dataset.classifications, + classification_names=sigma_dataset.classification_names, + meanings=sigma_dataset.meanings, + labels=sigma_dataset.labels, + business_attributes=sigma_dataset.business_attributes, + custom_attributes=sigma_dataset.custom_attributes, + pending_tasks=sigma_dataset.pending_tasks, + proxy=sigma_dataset.proxy, + is_incomplete=sigma_dataset.is_incomplete, + provenance_type=sigma_dataset.provenance_type, + home_id=sigma_dataset.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _sigma_dataset_from_nested(nested: SigmaDatasetNested) -> SigmaDataset: + """Convert nested format to flat SigmaDataset.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else SigmaDatasetAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SIGMA_DATASET_REL_FIELDS, + SigmaDatasetRelationshipAttributes, + ) + return SigmaDataset( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_sigma_dataset_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _sigma_dataset_to_nested_bytes(sigma_dataset: SigmaDataset, serde: Serde) -> bytes: + """Convert flat SigmaDataset to nested JSON bytes.""" + return serde.encode(_sigma_dataset_to_nested(sigma_dataset)) + + +def _sigma_dataset_from_nested_bytes(data: bytes, serde: Serde) -> SigmaDataset: + """Convert nested JSON bytes to flat SigmaDataset.""" + nested = serde.decode(data, SigmaDatasetNested) + return _sigma_dataset_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +SigmaDataset.SIGMA_COLUMN_COUNT = NumericField("sigmaColumnCount", "sigmaColumnCount") +SigmaDataset.SIGMA_WORKBOOK_QUALIFIED_NAME = KeywordTextField( + "sigmaWorkbookQualifiedName", + "sigmaWorkbookQualifiedName", + "sigmaWorkbookQualifiedName.text", +) +SigmaDataset.SIGMA_WORKBOOK_NAME = KeywordField( + "sigmaWorkbookName", "sigmaWorkbookName" +) +SigmaDataset.SIGMA_PAGE_QUALIFIED_NAME = KeywordTextField( + "sigmaPageQualifiedName", "sigmaPageQualifiedName", "sigmaPageQualifiedName.text" +) +SigmaDataset.SIGMA_PAGE_NAME = KeywordField("sigmaPageName", "sigmaPageName") +SigmaDataset.SIGMA_DATA_ELEMENT_QUALIFIED_NAME = KeywordTextField( + "sigmaDataElementQualifiedName", + "sigmaDataElementQualifiedName", + "sigmaDataElementQualifiedName.text", +) +SigmaDataset.SIGMA_DATA_ELEMENT_NAME = KeywordField( + "sigmaDataElementName", "sigmaDataElementName" +) +SigmaDataset.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SigmaDataset.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +SigmaDataset.ANOMALO_CHECKS = RelationField("anomaloChecks") +SigmaDataset.APPLICATION = RelationField("application") +SigmaDataset.APPLICATION_FIELD = RelationField("applicationField") +SigmaDataset.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +SigmaDataset.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +SigmaDataset.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +SigmaDataset.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +SigmaDataset.METRICS = RelationField("metrics") +SigmaDataset.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SigmaDataset.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +SigmaDataset.MEANINGS = RelationField("meanings") +SigmaDataset.MC_MONITORS = RelationField("mcMonitors") +SigmaDataset.MC_INCIDENTS = RelationField("mcIncidents") +SigmaDataset.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SigmaDataset.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SigmaDataset.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SigmaDataset.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SigmaDataset.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +SigmaDataset.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +SigmaDataset.FILES = RelationField("files") +SigmaDataset.LINKS = RelationField("links") +SigmaDataset.README = RelationField("readme") +SigmaDataset.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +SigmaDataset.SIGMA_DATASET_COLUMNS = RelationField("sigmaDatasetColumns") +SigmaDataset.SODA_CHECKS = RelationField("sodaChecks") +SigmaDataset.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SigmaDataset.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/sigma_dataset_column.py b/pyatlan_v9/model/assets/sigma_dataset_column.py new file mode 100644 index 000000000..73dd3ee47 --- /dev/null +++ b/pyatlan_v9/model/assets/sigma_dataset_column.py @@ -0,0 +1,670 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SigmaDatasetColumn asset model with flattened inheritance. + +This module provides: +- SigmaDatasetColumn: Flat asset class (easy to use) +- SigmaDatasetColumnAttributes: Nested attributes struct (extends AssetAttributes) +- SigmaDatasetColumnNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .sigma_related import RelatedSigmaDataset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SigmaDatasetColumn(Asset): + """ + Instance of a Sigma dataset column in Atlan. + """ + + SIGMA_DATASET_QUALIFIED_NAME: ClassVar[Any] = None + SIGMA_DATASET_NAME: ClassVar[Any] = None + SIGMA_WORKBOOK_QUALIFIED_NAME: ClassVar[Any] = None + SIGMA_WORKBOOK_NAME: ClassVar[Any] = None + SIGMA_PAGE_QUALIFIED_NAME: ClassVar[Any] = None + SIGMA_PAGE_NAME: ClassVar[Any] = None + SIGMA_DATA_ELEMENT_QUALIFIED_NAME: ClassVar[Any] = None + SIGMA_DATA_ELEMENT_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SIGMA_DATASET: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SigmaDatasetColumn" + + sigma_dataset_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dataset in which this column exists.""" + + sigma_dataset_name: Union[str, None, UnsetType] = UNSET + """Simple name of the dataset in which this column exists.""" + + sigma_workbook_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workbook in which this asset exists.""" + + sigma_workbook_name: Union[str, None, UnsetType] = UNSET + """Simple name of the workbook in which this asset exists.""" + + sigma_page_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the page on which this asset exists.""" + + sigma_page_name: Union[str, None, UnsetType] = UNSET + """Simple name of the page on which this asset exists.""" + + sigma_data_element_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the data element in which this asset exists.""" + + sigma_data_element_name: Union[str, None, UnsetType] = UNSET + """Simple name of the data element in which this asset exists.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + sigma_dataset: Union[RelatedSigmaDataset, None, UnsetType] = UNSET + """Dataset in which this dataset column exists.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SigmaDatasetColumn" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _sigma_dataset_column_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> SigmaDatasetColumn: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SigmaDatasetColumn instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _sigma_dataset_column_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SigmaDatasetColumnAttributes(AssetAttributes): + """SigmaDatasetColumn-specific attributes for nested API format.""" + + sigma_dataset_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dataset in which this column exists.""" + + sigma_dataset_name: Union[str, None, UnsetType] = UNSET + """Simple name of the dataset in which this column exists.""" + + sigma_workbook_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workbook in which this asset exists.""" + + sigma_workbook_name: Union[str, None, UnsetType] = UNSET + """Simple name of the workbook in which this asset exists.""" + + sigma_page_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the page on which this asset exists.""" + + sigma_page_name: Union[str, None, UnsetType] = UNSET + """Simple name of the page on which this asset exists.""" + + sigma_data_element_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the data element in which this asset exists.""" + + sigma_data_element_name: Union[str, None, UnsetType] = UNSET + """Simple name of the data element in which this asset exists.""" + + +class SigmaDatasetColumnRelationshipAttributes(AssetRelationshipAttributes): + """SigmaDatasetColumn-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + sigma_dataset: Union[RelatedSigmaDataset, None, UnsetType] = UNSET + """Dataset in which this dataset column exists.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SigmaDatasetColumnNested(AssetNested): + """SigmaDatasetColumn in nested API format for high-performance serialization.""" + + attributes: Union[SigmaDatasetColumnAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + SigmaDatasetColumnRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + SigmaDatasetColumnRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SigmaDatasetColumnRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SIGMA_DATASET_COLUMN_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "sigma_dataset", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_sigma_dataset_column_attrs( + attrs: SigmaDatasetColumnAttributes, obj: SigmaDatasetColumn +) -> None: + """Populate SigmaDatasetColumn-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.sigma_dataset_qualified_name = obj.sigma_dataset_qualified_name + attrs.sigma_dataset_name = obj.sigma_dataset_name + attrs.sigma_workbook_qualified_name = obj.sigma_workbook_qualified_name + attrs.sigma_workbook_name = obj.sigma_workbook_name + attrs.sigma_page_qualified_name = obj.sigma_page_qualified_name + attrs.sigma_page_name = obj.sigma_page_name + attrs.sigma_data_element_qualified_name = obj.sigma_data_element_qualified_name + attrs.sigma_data_element_name = obj.sigma_data_element_name + + +def _extract_sigma_dataset_column_attrs(attrs: SigmaDatasetColumnAttributes) -> dict: + """Extract all SigmaDatasetColumn attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["sigma_dataset_qualified_name"] = attrs.sigma_dataset_qualified_name + result["sigma_dataset_name"] = attrs.sigma_dataset_name + result["sigma_workbook_qualified_name"] = attrs.sigma_workbook_qualified_name + result["sigma_workbook_name"] = attrs.sigma_workbook_name + result["sigma_page_qualified_name"] = attrs.sigma_page_qualified_name + result["sigma_page_name"] = attrs.sigma_page_name + result["sigma_data_element_qualified_name"] = ( + attrs.sigma_data_element_qualified_name + ) + result["sigma_data_element_name"] = attrs.sigma_data_element_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _sigma_dataset_column_to_nested( + sigma_dataset_column: SigmaDatasetColumn, +) -> SigmaDatasetColumnNested: + """Convert flat SigmaDatasetColumn to nested format.""" + attrs = SigmaDatasetColumnAttributes() + _populate_sigma_dataset_column_attrs(attrs, sigma_dataset_column) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + sigma_dataset_column, + _SIGMA_DATASET_COLUMN_REL_FIELDS, + SigmaDatasetColumnRelationshipAttributes, + ) + return SigmaDatasetColumnNested( + guid=sigma_dataset_column.guid, + type_name=sigma_dataset_column.type_name, + status=sigma_dataset_column.status, + version=sigma_dataset_column.version, + create_time=sigma_dataset_column.create_time, + update_time=sigma_dataset_column.update_time, + created_by=sigma_dataset_column.created_by, + updated_by=sigma_dataset_column.updated_by, + classifications=sigma_dataset_column.classifications, + classification_names=sigma_dataset_column.classification_names, + meanings=sigma_dataset_column.meanings, + labels=sigma_dataset_column.labels, + business_attributes=sigma_dataset_column.business_attributes, + custom_attributes=sigma_dataset_column.custom_attributes, + pending_tasks=sigma_dataset_column.pending_tasks, + proxy=sigma_dataset_column.proxy, + is_incomplete=sigma_dataset_column.is_incomplete, + provenance_type=sigma_dataset_column.provenance_type, + home_id=sigma_dataset_column.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _sigma_dataset_column_from_nested( + nested: SigmaDatasetColumnNested, +) -> SigmaDatasetColumn: + """Convert nested format to flat SigmaDatasetColumn.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else SigmaDatasetColumnAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SIGMA_DATASET_COLUMN_REL_FIELDS, + SigmaDatasetColumnRelationshipAttributes, + ) + return SigmaDatasetColumn( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_sigma_dataset_column_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _sigma_dataset_column_to_nested_bytes( + sigma_dataset_column: SigmaDatasetColumn, serde: Serde +) -> bytes: + """Convert flat SigmaDatasetColumn to nested JSON bytes.""" + return serde.encode(_sigma_dataset_column_to_nested(sigma_dataset_column)) + + +def _sigma_dataset_column_from_nested_bytes( + data: bytes, serde: Serde +) -> SigmaDatasetColumn: + """Convert nested JSON bytes to flat SigmaDatasetColumn.""" + nested = serde.decode(data, SigmaDatasetColumnNested) + return _sigma_dataset_column_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + RelationField, +) + +SigmaDatasetColumn.SIGMA_DATASET_QUALIFIED_NAME = KeywordTextField( + "sigmaDatasetQualifiedName", + "sigmaDatasetQualifiedName", + "sigmaDatasetQualifiedName.text", +) +SigmaDatasetColumn.SIGMA_DATASET_NAME = KeywordField( + "sigmaDatasetName", "sigmaDatasetName" +) +SigmaDatasetColumn.SIGMA_WORKBOOK_QUALIFIED_NAME = KeywordTextField( + "sigmaWorkbookQualifiedName", + "sigmaWorkbookQualifiedName", + "sigmaWorkbookQualifiedName.text", +) +SigmaDatasetColumn.SIGMA_WORKBOOK_NAME = KeywordField( + "sigmaWorkbookName", "sigmaWorkbookName" +) +SigmaDatasetColumn.SIGMA_PAGE_QUALIFIED_NAME = KeywordTextField( + "sigmaPageQualifiedName", "sigmaPageQualifiedName", "sigmaPageQualifiedName.text" +) +SigmaDatasetColumn.SIGMA_PAGE_NAME = KeywordField("sigmaPageName", "sigmaPageName") +SigmaDatasetColumn.SIGMA_DATA_ELEMENT_QUALIFIED_NAME = KeywordTextField( + "sigmaDataElementQualifiedName", + "sigmaDataElementQualifiedName", + "sigmaDataElementQualifiedName.text", +) +SigmaDatasetColumn.SIGMA_DATA_ELEMENT_NAME = KeywordField( + "sigmaDataElementName", "sigmaDataElementName" +) +SigmaDatasetColumn.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SigmaDatasetColumn.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +SigmaDatasetColumn.ANOMALO_CHECKS = RelationField("anomaloChecks") +SigmaDatasetColumn.APPLICATION = RelationField("application") +SigmaDatasetColumn.APPLICATION_FIELD = RelationField("applicationField") +SigmaDatasetColumn.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +SigmaDatasetColumn.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +SigmaDatasetColumn.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +SigmaDatasetColumn.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +SigmaDatasetColumn.METRICS = RelationField("metrics") +SigmaDatasetColumn.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SigmaDatasetColumn.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +SigmaDatasetColumn.MEANINGS = RelationField("meanings") +SigmaDatasetColumn.MC_MONITORS = RelationField("mcMonitors") +SigmaDatasetColumn.MC_INCIDENTS = RelationField("mcIncidents") +SigmaDatasetColumn.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SigmaDatasetColumn.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SigmaDatasetColumn.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SigmaDatasetColumn.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SigmaDatasetColumn.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +SigmaDatasetColumn.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +SigmaDatasetColumn.FILES = RelationField("files") +SigmaDatasetColumn.LINKS = RelationField("links") +SigmaDatasetColumn.README = RelationField("readme") +SigmaDatasetColumn.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +SigmaDatasetColumn.SIGMA_DATASET = RelationField("sigmaDataset") +SigmaDatasetColumn.SODA_CHECKS = RelationField("sodaChecks") +SigmaDatasetColumn.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SigmaDatasetColumn.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/sigma_page.py b/pyatlan_v9/model/assets/sigma_page.py new file mode 100644 index 000000000..e422d848b --- /dev/null +++ b/pyatlan_v9/model/assets/sigma_page.py @@ -0,0 +1,642 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SigmaPage asset model with flattened inheritance. + +This module provides: +- SigmaPage: Flat asset class (easy to use) +- SigmaPageAttributes: Nested attributes struct (extends AssetAttributes) +- SigmaPageNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .sigma_related import RelatedSigmaDataElement, RelatedSigmaWorkbook + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SigmaPage(Asset): + """ + Instance of a Sigma page in Atlan. + """ + + SIGMA_DATA_ELEMENT_COUNT: ClassVar[Any] = None + SIGMA_WORKBOOK_QUALIFIED_NAME: ClassVar[Any] = None + SIGMA_WORKBOOK_NAME: ClassVar[Any] = None + SIGMA_PAGE_QUALIFIED_NAME: ClassVar[Any] = None + SIGMA_PAGE_NAME: ClassVar[Any] = None + SIGMA_DATA_ELEMENT_QUALIFIED_NAME: ClassVar[Any] = None + SIGMA_DATA_ELEMENT_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SIGMA_DATA_ELEMENTS: ClassVar[Any] = None + SIGMA_WORKBOOK: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SigmaPage" + + sigma_data_element_count: Union[int, None, UnsetType] = UNSET + """Number of data elements on this page.""" + + sigma_workbook_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workbook in which this asset exists.""" + + sigma_workbook_name: Union[str, None, UnsetType] = UNSET + """Simple name of the workbook in which this asset exists.""" + + sigma_page_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the page on which this asset exists.""" + + sigma_page_name: Union[str, None, UnsetType] = UNSET + """Simple name of the page on which this asset exists.""" + + sigma_data_element_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the data element in which this asset exists.""" + + sigma_data_element_name: Union[str, None, UnsetType] = UNSET + """Simple name of the data element in which this asset exists.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + sigma_data_elements: Union[List[RelatedSigmaDataElement], None, UnsetType] = UNSET + """Data elements that exist on this page.""" + + sigma_workbook: Union[RelatedSigmaWorkbook, None, UnsetType] = UNSET + """Workbook in which this page exists.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SigmaPage" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _sigma_page_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> SigmaPage: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SigmaPage instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _sigma_page_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SigmaPageAttributes(AssetAttributes): + """SigmaPage-specific attributes for nested API format.""" + + sigma_data_element_count: Union[int, None, UnsetType] = UNSET + """Number of data elements on this page.""" + + sigma_workbook_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workbook in which this asset exists.""" + + sigma_workbook_name: Union[str, None, UnsetType] = UNSET + """Simple name of the workbook in which this asset exists.""" + + sigma_page_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the page on which this asset exists.""" + + sigma_page_name: Union[str, None, UnsetType] = UNSET + """Simple name of the page on which this asset exists.""" + + sigma_data_element_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the data element in which this asset exists.""" + + sigma_data_element_name: Union[str, None, UnsetType] = UNSET + """Simple name of the data element in which this asset exists.""" + + +class SigmaPageRelationshipAttributes(AssetRelationshipAttributes): + """SigmaPage-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + sigma_data_elements: Union[List[RelatedSigmaDataElement], None, UnsetType] = UNSET + """Data elements that exist on this page.""" + + sigma_workbook: Union[RelatedSigmaWorkbook, None, UnsetType] = UNSET + """Workbook in which this page exists.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SigmaPageNested(AssetNested): + """SigmaPage in nested API format for high-performance serialization.""" + + attributes: Union[SigmaPageAttributes, UnsetType] = UNSET + relationship_attributes: Union[SigmaPageRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + SigmaPageRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SigmaPageRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SIGMA_PAGE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "sigma_data_elements", + "sigma_workbook", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_sigma_page_attrs(attrs: SigmaPageAttributes, obj: SigmaPage) -> None: + """Populate SigmaPage-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.sigma_data_element_count = obj.sigma_data_element_count + attrs.sigma_workbook_qualified_name = obj.sigma_workbook_qualified_name + attrs.sigma_workbook_name = obj.sigma_workbook_name + attrs.sigma_page_qualified_name = obj.sigma_page_qualified_name + attrs.sigma_page_name = obj.sigma_page_name + attrs.sigma_data_element_qualified_name = obj.sigma_data_element_qualified_name + attrs.sigma_data_element_name = obj.sigma_data_element_name + + +def _extract_sigma_page_attrs(attrs: SigmaPageAttributes) -> dict: + """Extract all SigmaPage attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["sigma_data_element_count"] = attrs.sigma_data_element_count + result["sigma_workbook_qualified_name"] = attrs.sigma_workbook_qualified_name + result["sigma_workbook_name"] = attrs.sigma_workbook_name + result["sigma_page_qualified_name"] = attrs.sigma_page_qualified_name + result["sigma_page_name"] = attrs.sigma_page_name + result["sigma_data_element_qualified_name"] = ( + attrs.sigma_data_element_qualified_name + ) + result["sigma_data_element_name"] = attrs.sigma_data_element_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _sigma_page_to_nested(sigma_page: SigmaPage) -> SigmaPageNested: + """Convert flat SigmaPage to nested format.""" + attrs = SigmaPageAttributes() + _populate_sigma_page_attrs(attrs, sigma_page) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + sigma_page, _SIGMA_PAGE_REL_FIELDS, SigmaPageRelationshipAttributes + ) + return SigmaPageNested( + guid=sigma_page.guid, + type_name=sigma_page.type_name, + status=sigma_page.status, + version=sigma_page.version, + create_time=sigma_page.create_time, + update_time=sigma_page.update_time, + created_by=sigma_page.created_by, + updated_by=sigma_page.updated_by, + classifications=sigma_page.classifications, + classification_names=sigma_page.classification_names, + meanings=sigma_page.meanings, + labels=sigma_page.labels, + business_attributes=sigma_page.business_attributes, + custom_attributes=sigma_page.custom_attributes, + pending_tasks=sigma_page.pending_tasks, + proxy=sigma_page.proxy, + is_incomplete=sigma_page.is_incomplete, + provenance_type=sigma_page.provenance_type, + home_id=sigma_page.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _sigma_page_from_nested(nested: SigmaPageNested) -> SigmaPage: + """Convert nested format to flat SigmaPage.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else SigmaPageAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SIGMA_PAGE_REL_FIELDS, + SigmaPageRelationshipAttributes, + ) + return SigmaPage( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_sigma_page_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _sigma_page_to_nested_bytes(sigma_page: SigmaPage, serde: Serde) -> bytes: + """Convert flat SigmaPage to nested JSON bytes.""" + return serde.encode(_sigma_page_to_nested(sigma_page)) + + +def _sigma_page_from_nested_bytes(data: bytes, serde: Serde) -> SigmaPage: + """Convert nested JSON bytes to flat SigmaPage.""" + nested = serde.decode(data, SigmaPageNested) + return _sigma_page_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +SigmaPage.SIGMA_DATA_ELEMENT_COUNT = NumericField( + "sigmaDataElementCount", "sigmaDataElementCount" +) +SigmaPage.SIGMA_WORKBOOK_QUALIFIED_NAME = KeywordTextField( + "sigmaWorkbookQualifiedName", + "sigmaWorkbookQualifiedName", + "sigmaWorkbookQualifiedName.text", +) +SigmaPage.SIGMA_WORKBOOK_NAME = KeywordField("sigmaWorkbookName", "sigmaWorkbookName") +SigmaPage.SIGMA_PAGE_QUALIFIED_NAME = KeywordTextField( + "sigmaPageQualifiedName", "sigmaPageQualifiedName", "sigmaPageQualifiedName.text" +) +SigmaPage.SIGMA_PAGE_NAME = KeywordField("sigmaPageName", "sigmaPageName") +SigmaPage.SIGMA_DATA_ELEMENT_QUALIFIED_NAME = KeywordTextField( + "sigmaDataElementQualifiedName", + "sigmaDataElementQualifiedName", + "sigmaDataElementQualifiedName.text", +) +SigmaPage.SIGMA_DATA_ELEMENT_NAME = KeywordField( + "sigmaDataElementName", "sigmaDataElementName" +) +SigmaPage.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SigmaPage.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +SigmaPage.ANOMALO_CHECKS = RelationField("anomaloChecks") +SigmaPage.APPLICATION = RelationField("application") +SigmaPage.APPLICATION_FIELD = RelationField("applicationField") +SigmaPage.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +SigmaPage.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +SigmaPage.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +SigmaPage.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +SigmaPage.METRICS = RelationField("metrics") +SigmaPage.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SigmaPage.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +SigmaPage.MEANINGS = RelationField("meanings") +SigmaPage.MC_MONITORS = RelationField("mcMonitors") +SigmaPage.MC_INCIDENTS = RelationField("mcIncidents") +SigmaPage.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SigmaPage.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SigmaPage.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SigmaPage.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SigmaPage.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +SigmaPage.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +SigmaPage.FILES = RelationField("files") +SigmaPage.LINKS = RelationField("links") +SigmaPage.README = RelationField("readme") +SigmaPage.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +SigmaPage.SIGMA_DATA_ELEMENTS = RelationField("sigmaDataElements") +SigmaPage.SIGMA_WORKBOOK = RelationField("sigmaWorkbook") +SigmaPage.SODA_CHECKS = RelationField("sodaChecks") +SigmaPage.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SigmaPage.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/sigma_related.py b/pyatlan_v9/model/assets/sigma_related.py new file mode 100644 index 000000000..62c3e8fca --- /dev/null +++ b/pyatlan_v9/model/assets/sigma_related.py @@ -0,0 +1,182 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Sigma module. + +This module contains all Related{Type} classes for the Sigma type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Union + +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedBI +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedSigma", + "RelatedSigmaDataElement", + "RelatedSigmaDataElementField", + "RelatedSigmaDataset", + "RelatedSigmaDatasetColumn", + "RelatedSigmaPage", + "RelatedSigmaWorkbook", +] + + +class RelatedSigma(RelatedBI): + """ + Related entity reference for Sigma assets. + + Extends RelatedBI with Sigma-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Sigma" so it serializes correctly + + sigma_workbook_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workbook in which this asset exists.""" + + sigma_workbook_name: Union[str, None, UnsetType] = UNSET + """Simple name of the workbook in which this asset exists.""" + + sigma_page_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the page on which this asset exists.""" + + sigma_page_name: Union[str, None, UnsetType] = UNSET + """Simple name of the page on which this asset exists.""" + + sigma_data_element_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the data element in which this asset exists.""" + + sigma_data_element_name: Union[str, None, UnsetType] = UNSET + """Simple name of the data element in which this asset exists.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Sigma" + + +class RelatedSigmaDataElement(RelatedSigma): + """ + Related entity reference for SigmaDataElement assets. + + Extends RelatedSigma with SigmaDataElement-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SigmaDataElement" so it serializes correctly + + sigma_data_element_query: Union[str, None, UnsetType] = UNSET + """""" + + sigma_data_element_type: Union[str, None, UnsetType] = UNSET + """""" + + sigma_data_element_field_count: Union[int, None, UnsetType] = UNSET + """Number of fields in this data element.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SigmaDataElement" + + +class RelatedSigmaDataElementField(RelatedSigma): + """ + Related entity reference for SigmaDataElementField assets. + + Extends RelatedSigma with SigmaDataElementField-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SigmaDataElementField" so it serializes correctly + + sigma_is_hidden: Union[bool, None, UnsetType] = UNSET + """Whether this field is hidden (true) or not (false).""" + + sigma_data_element_field_formula: Union[str, None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SigmaDataElementField" + + +class RelatedSigmaDataset(RelatedSigma): + """ + Related entity reference for SigmaDataset assets. + + Extends RelatedSigma with SigmaDataset-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SigmaDataset" so it serializes correctly + + sigma_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this dataset.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SigmaDataset" + + +class RelatedSigmaDatasetColumn(RelatedSigma): + """ + Related entity reference for SigmaDatasetColumn assets. + + Extends RelatedSigma with SigmaDatasetColumn-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SigmaDatasetColumn" so it serializes correctly + + sigma_dataset_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dataset in which this column exists.""" + + sigma_dataset_name: Union[str, None, UnsetType] = UNSET + """Simple name of the dataset in which this column exists.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SigmaDatasetColumn" + + +class RelatedSigmaPage(RelatedSigma): + """ + Related entity reference for SigmaPage assets. + + Extends RelatedSigma with SigmaPage-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SigmaPage" so it serializes correctly + + sigma_data_element_count: Union[int, None, UnsetType] = UNSET + """Number of data elements on this page.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SigmaPage" + + +class RelatedSigmaWorkbook(RelatedSigma): + """ + Related entity reference for SigmaWorkbook assets. + + Extends RelatedSigma with SigmaWorkbook-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SigmaWorkbook" so it serializes correctly + + sigma_page_count: Union[int, None, UnsetType] = UNSET + """Number of pages in this workbook.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SigmaWorkbook" diff --git a/pyatlan_v9/model/assets/sigma_workbook.py b/pyatlan_v9/model/assets/sigma_workbook.py new file mode 100644 index 000000000..d0c396232 --- /dev/null +++ b/pyatlan_v9/model/assets/sigma_workbook.py @@ -0,0 +1,634 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SigmaWorkbook asset model with flattened inheritance. + +This module provides: +- SigmaWorkbook: Flat asset class (easy to use) +- SigmaWorkbookAttributes: Nested attributes struct (extends AssetAttributes) +- SigmaWorkbookNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .sigma_related import RelatedSigmaPage + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SigmaWorkbook(Asset): + """ + Instance of a Sigma workbook in Atlan. + """ + + SIGMA_PAGE_COUNT: ClassVar[Any] = None + SIGMA_WORKBOOK_QUALIFIED_NAME: ClassVar[Any] = None + SIGMA_WORKBOOK_NAME: ClassVar[Any] = None + SIGMA_PAGE_QUALIFIED_NAME: ClassVar[Any] = None + SIGMA_PAGE_NAME: ClassVar[Any] = None + SIGMA_DATA_ELEMENT_QUALIFIED_NAME: ClassVar[Any] = None + SIGMA_DATA_ELEMENT_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SIGMA_PAGES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SigmaWorkbook" + + sigma_page_count: Union[int, None, UnsetType] = UNSET + """Number of pages in this workbook.""" + + sigma_workbook_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workbook in which this asset exists.""" + + sigma_workbook_name: Union[str, None, UnsetType] = UNSET + """Simple name of the workbook in which this asset exists.""" + + sigma_page_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the page on which this asset exists.""" + + sigma_page_name: Union[str, None, UnsetType] = UNSET + """Simple name of the page on which this asset exists.""" + + sigma_data_element_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the data element in which this asset exists.""" + + sigma_data_element_name: Union[str, None, UnsetType] = UNSET + """Simple name of the data element in which this asset exists.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + sigma_pages: Union[List[RelatedSigmaPage], None, UnsetType] = UNSET + """Pages that exist in this workbook.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SigmaWorkbook" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _sigma_workbook_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> SigmaWorkbook: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SigmaWorkbook instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _sigma_workbook_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SigmaWorkbookAttributes(AssetAttributes): + """SigmaWorkbook-specific attributes for nested API format.""" + + sigma_page_count: Union[int, None, UnsetType] = UNSET + """Number of pages in this workbook.""" + + sigma_workbook_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workbook in which this asset exists.""" + + sigma_workbook_name: Union[str, None, UnsetType] = UNSET + """Simple name of the workbook in which this asset exists.""" + + sigma_page_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the page on which this asset exists.""" + + sigma_page_name: Union[str, None, UnsetType] = UNSET + """Simple name of the page on which this asset exists.""" + + sigma_data_element_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the data element in which this asset exists.""" + + sigma_data_element_name: Union[str, None, UnsetType] = UNSET + """Simple name of the data element in which this asset exists.""" + + +class SigmaWorkbookRelationshipAttributes(AssetRelationshipAttributes): + """SigmaWorkbook-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + sigma_pages: Union[List[RelatedSigmaPage], None, UnsetType] = UNSET + """Pages that exist in this workbook.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SigmaWorkbookNested(AssetNested): + """SigmaWorkbook in nested API format for high-performance serialization.""" + + attributes: Union[SigmaWorkbookAttributes, UnsetType] = UNSET + relationship_attributes: Union[SigmaWorkbookRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + SigmaWorkbookRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SigmaWorkbookRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SIGMA_WORKBOOK_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "sigma_pages", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_sigma_workbook_attrs( + attrs: SigmaWorkbookAttributes, obj: SigmaWorkbook +) -> None: + """Populate SigmaWorkbook-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.sigma_page_count = obj.sigma_page_count + attrs.sigma_workbook_qualified_name = obj.sigma_workbook_qualified_name + attrs.sigma_workbook_name = obj.sigma_workbook_name + attrs.sigma_page_qualified_name = obj.sigma_page_qualified_name + attrs.sigma_page_name = obj.sigma_page_name + attrs.sigma_data_element_qualified_name = obj.sigma_data_element_qualified_name + attrs.sigma_data_element_name = obj.sigma_data_element_name + + +def _extract_sigma_workbook_attrs(attrs: SigmaWorkbookAttributes) -> dict: + """Extract all SigmaWorkbook attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["sigma_page_count"] = attrs.sigma_page_count + result["sigma_workbook_qualified_name"] = attrs.sigma_workbook_qualified_name + result["sigma_workbook_name"] = attrs.sigma_workbook_name + result["sigma_page_qualified_name"] = attrs.sigma_page_qualified_name + result["sigma_page_name"] = attrs.sigma_page_name + result["sigma_data_element_qualified_name"] = ( + attrs.sigma_data_element_qualified_name + ) + result["sigma_data_element_name"] = attrs.sigma_data_element_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _sigma_workbook_to_nested(sigma_workbook: SigmaWorkbook) -> SigmaWorkbookNested: + """Convert flat SigmaWorkbook to nested format.""" + attrs = SigmaWorkbookAttributes() + _populate_sigma_workbook_attrs(attrs, sigma_workbook) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + sigma_workbook, _SIGMA_WORKBOOK_REL_FIELDS, SigmaWorkbookRelationshipAttributes + ) + return SigmaWorkbookNested( + guid=sigma_workbook.guid, + type_name=sigma_workbook.type_name, + status=sigma_workbook.status, + version=sigma_workbook.version, + create_time=sigma_workbook.create_time, + update_time=sigma_workbook.update_time, + created_by=sigma_workbook.created_by, + updated_by=sigma_workbook.updated_by, + classifications=sigma_workbook.classifications, + classification_names=sigma_workbook.classification_names, + meanings=sigma_workbook.meanings, + labels=sigma_workbook.labels, + business_attributes=sigma_workbook.business_attributes, + custom_attributes=sigma_workbook.custom_attributes, + pending_tasks=sigma_workbook.pending_tasks, + proxy=sigma_workbook.proxy, + is_incomplete=sigma_workbook.is_incomplete, + provenance_type=sigma_workbook.provenance_type, + home_id=sigma_workbook.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _sigma_workbook_from_nested(nested: SigmaWorkbookNested) -> SigmaWorkbook: + """Convert nested format to flat SigmaWorkbook.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else SigmaWorkbookAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SIGMA_WORKBOOK_REL_FIELDS, + SigmaWorkbookRelationshipAttributes, + ) + return SigmaWorkbook( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_sigma_workbook_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _sigma_workbook_to_nested_bytes( + sigma_workbook: SigmaWorkbook, serde: Serde +) -> bytes: + """Convert flat SigmaWorkbook to nested JSON bytes.""" + return serde.encode(_sigma_workbook_to_nested(sigma_workbook)) + + +def _sigma_workbook_from_nested_bytes(data: bytes, serde: Serde) -> SigmaWorkbook: + """Convert nested JSON bytes to flat SigmaWorkbook.""" + nested = serde.decode(data, SigmaWorkbookNested) + return _sigma_workbook_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +SigmaWorkbook.SIGMA_PAGE_COUNT = NumericField("sigmaPageCount", "sigmaPageCount") +SigmaWorkbook.SIGMA_WORKBOOK_QUALIFIED_NAME = KeywordTextField( + "sigmaWorkbookQualifiedName", + "sigmaWorkbookQualifiedName", + "sigmaWorkbookQualifiedName.text", +) +SigmaWorkbook.SIGMA_WORKBOOK_NAME = KeywordField( + "sigmaWorkbookName", "sigmaWorkbookName" +) +SigmaWorkbook.SIGMA_PAGE_QUALIFIED_NAME = KeywordTextField( + "sigmaPageQualifiedName", "sigmaPageQualifiedName", "sigmaPageQualifiedName.text" +) +SigmaWorkbook.SIGMA_PAGE_NAME = KeywordField("sigmaPageName", "sigmaPageName") +SigmaWorkbook.SIGMA_DATA_ELEMENT_QUALIFIED_NAME = KeywordTextField( + "sigmaDataElementQualifiedName", + "sigmaDataElementQualifiedName", + "sigmaDataElementQualifiedName.text", +) +SigmaWorkbook.SIGMA_DATA_ELEMENT_NAME = KeywordField( + "sigmaDataElementName", "sigmaDataElementName" +) +SigmaWorkbook.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SigmaWorkbook.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +SigmaWorkbook.ANOMALO_CHECKS = RelationField("anomaloChecks") +SigmaWorkbook.APPLICATION = RelationField("application") +SigmaWorkbook.APPLICATION_FIELD = RelationField("applicationField") +SigmaWorkbook.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +SigmaWorkbook.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +SigmaWorkbook.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +SigmaWorkbook.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +SigmaWorkbook.METRICS = RelationField("metrics") +SigmaWorkbook.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SigmaWorkbook.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +SigmaWorkbook.MEANINGS = RelationField("meanings") +SigmaWorkbook.MC_MONITORS = RelationField("mcMonitors") +SigmaWorkbook.MC_INCIDENTS = RelationField("mcIncidents") +SigmaWorkbook.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SigmaWorkbook.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SigmaWorkbook.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SigmaWorkbook.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SigmaWorkbook.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +SigmaWorkbook.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +SigmaWorkbook.FILES = RelationField("files") +SigmaWorkbook.LINKS = RelationField("links") +SigmaWorkbook.README = RelationField("readme") +SigmaWorkbook.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +SigmaWorkbook.SIGMA_PAGES = RelationField("sigmaPages") +SigmaWorkbook.SODA_CHECKS = RelationField("sodaChecks") +SigmaWorkbook.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SigmaWorkbook.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/sisense.py b/pyatlan_v9/model/assets/sisense.py new file mode 100644 index 000000000..54e4bc5ae --- /dev/null +++ b/pyatlan_v9/model/assets/sisense.py @@ -0,0 +1,523 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Sisense asset model with flattened inheritance. + +This module provides: +- Sisense: Flat asset class (easy to use) +- SisenseAttributes: Nested attributes struct (extends AssetAttributes) +- SisenseNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Sisense(Asset): + """ + Base class for Sisense assets. + """ + + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Sisense" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Sisense" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _sisense_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Sisense: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Sisense instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _sisense_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SisenseAttributes(AssetAttributes): + """Sisense-specific attributes for nested API format.""" + + pass + + +class SisenseRelationshipAttributes(AssetRelationshipAttributes): + """Sisense-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SisenseNested(AssetNested): + """Sisense in nested API format for high-performance serialization.""" + + attributes: Union[SisenseAttributes, UnsetType] = UNSET + relationship_attributes: Union[SisenseRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[SisenseRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[SisenseRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SISENSE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_sisense_attrs(attrs: SisenseAttributes, obj: Sisense) -> None: + """Populate Sisense-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + + +def _extract_sisense_attrs(attrs: SisenseAttributes) -> dict: + """Extract all Sisense attributes from the attrs struct into a flat dict.""" + return _extract_asset_attrs(attrs) + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _sisense_to_nested(sisense: Sisense) -> SisenseNested: + """Convert flat Sisense to nested format.""" + attrs = SisenseAttributes() + _populate_sisense_attrs(attrs, sisense) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + sisense, _SISENSE_REL_FIELDS, SisenseRelationshipAttributes + ) + return SisenseNested( + guid=sisense.guid, + type_name=sisense.type_name, + status=sisense.status, + version=sisense.version, + create_time=sisense.create_time, + update_time=sisense.update_time, + created_by=sisense.created_by, + updated_by=sisense.updated_by, + classifications=sisense.classifications, + classification_names=sisense.classification_names, + meanings=sisense.meanings, + labels=sisense.labels, + business_attributes=sisense.business_attributes, + custom_attributes=sisense.custom_attributes, + pending_tasks=sisense.pending_tasks, + proxy=sisense.proxy, + is_incomplete=sisense.is_incomplete, + provenance_type=sisense.provenance_type, + home_id=sisense.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _sisense_from_nested(nested: SisenseNested) -> Sisense: + """Convert nested format to flat Sisense.""" + attrs = nested.attributes if nested.attributes is not UNSET else SisenseAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SISENSE_REL_FIELDS, + SisenseRelationshipAttributes, + ) + return Sisense( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_sisense_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _sisense_to_nested_bytes(sisense: Sisense, serde: Serde) -> bytes: + """Convert flat Sisense to nested JSON bytes.""" + return serde.encode(_sisense_to_nested(sisense)) + + +def _sisense_from_nested_bytes(data: bytes, serde: Serde) -> Sisense: + """Convert nested JSON bytes to flat Sisense.""" + nested = serde.decode(data, SisenseNested) + return _sisense_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import RelationField # noqa: E402 + +Sisense.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Sisense.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Sisense.ANOMALO_CHECKS = RelationField("anomaloChecks") +Sisense.APPLICATION = RelationField("application") +Sisense.APPLICATION_FIELD = RelationField("applicationField") +Sisense.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Sisense.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Sisense.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Sisense.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Sisense.METRICS = RelationField("metrics") +Sisense.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Sisense.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Sisense.MEANINGS = RelationField("meanings") +Sisense.MC_MONITORS = RelationField("mcMonitors") +Sisense.MC_INCIDENTS = RelationField("mcIncidents") +Sisense.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Sisense.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Sisense.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Sisense.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Sisense.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Sisense.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Sisense.FILES = RelationField("files") +Sisense.LINKS = RelationField("links") +Sisense.README = RelationField("readme") +Sisense.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Sisense.SODA_CHECKS = RelationField("sodaChecks") +Sisense.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Sisense.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/sisense_dashboard.py b/pyatlan_v9/model/assets/sisense_dashboard.py new file mode 100644 index 000000000..1029a1e33 --- /dev/null +++ b/pyatlan_v9/model/assets/sisense_dashboard.py @@ -0,0 +1,614 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SisenseDashboard asset model with flattened inheritance. + +This module provides: +- SisenseDashboard: Flat asset class (easy to use) +- SisenseDashboardAttributes: Nested attributes struct (extends AssetAttributes) +- SisenseDashboardNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .sisense_related import ( + RelatedSisenseDatamodel, + RelatedSisenseFolder, + RelatedSisenseWidget, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SisenseDashboard(Asset): + """ + Instance of a Sisense dashboard in Atlan. These allow you to place multiple widgets on a single page. + """ + + SISENSE_DASHBOARD_FOLDER_QUALIFIED_NAME: ClassVar[Any] = None + SISENSE_WIDGET_COUNT: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SISENSE_WIDGETS: ClassVar[Any] = None + SISENSE_DATAMODELS: ClassVar[Any] = None + SISENSE_FOLDER: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SisenseDashboard" + + sisense_dashboard_folder_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the folder in which this dashboard exists.""" + + sisense_widget_count: Union[int, None, UnsetType] = UNSET + """Number of widgets in this dashboard.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + sisense_widgets: Union[List[RelatedSisenseWidget], None, UnsetType] = UNSET + """Widgets that exist in this dashboard.""" + + sisense_datamodels: Union[List[RelatedSisenseDatamodel], None, UnsetType] = UNSET + """""" + + sisense_folder: Union[RelatedSisenseFolder, None, UnsetType] = UNSET + """Folder in which this dashboard exists.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SisenseDashboard" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _sisense_dashboard_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> SisenseDashboard: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SisenseDashboard instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _sisense_dashboard_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SisenseDashboardAttributes(AssetAttributes): + """SisenseDashboard-specific attributes for nested API format.""" + + sisense_dashboard_folder_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the folder in which this dashboard exists.""" + + sisense_widget_count: Union[int, None, UnsetType] = UNSET + """Number of widgets in this dashboard.""" + + +class SisenseDashboardRelationshipAttributes(AssetRelationshipAttributes): + """SisenseDashboard-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + sisense_widgets: Union[List[RelatedSisenseWidget], None, UnsetType] = UNSET + """Widgets that exist in this dashboard.""" + + sisense_datamodels: Union[List[RelatedSisenseDatamodel], None, UnsetType] = UNSET + """""" + + sisense_folder: Union[RelatedSisenseFolder, None, UnsetType] = UNSET + """Folder in which this dashboard exists.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SisenseDashboardNested(AssetNested): + """SisenseDashboard in nested API format for high-performance serialization.""" + + attributes: Union[SisenseDashboardAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + SisenseDashboardRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + SisenseDashboardRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SisenseDashboardRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SISENSE_DASHBOARD_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "sisense_widgets", + "sisense_datamodels", + "sisense_folder", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_sisense_dashboard_attrs( + attrs: SisenseDashboardAttributes, obj: SisenseDashboard +) -> None: + """Populate SisenseDashboard-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.sisense_dashboard_folder_qualified_name = ( + obj.sisense_dashboard_folder_qualified_name + ) + attrs.sisense_widget_count = obj.sisense_widget_count + + +def _extract_sisense_dashboard_attrs(attrs: SisenseDashboardAttributes) -> dict: + """Extract all SisenseDashboard attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["sisense_dashboard_folder_qualified_name"] = ( + attrs.sisense_dashboard_folder_qualified_name + ) + result["sisense_widget_count"] = attrs.sisense_widget_count + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _sisense_dashboard_to_nested( + sisense_dashboard: SisenseDashboard, +) -> SisenseDashboardNested: + """Convert flat SisenseDashboard to nested format.""" + attrs = SisenseDashboardAttributes() + _populate_sisense_dashboard_attrs(attrs, sisense_dashboard) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + sisense_dashboard, + _SISENSE_DASHBOARD_REL_FIELDS, + SisenseDashboardRelationshipAttributes, + ) + return SisenseDashboardNested( + guid=sisense_dashboard.guid, + type_name=sisense_dashboard.type_name, + status=sisense_dashboard.status, + version=sisense_dashboard.version, + create_time=sisense_dashboard.create_time, + update_time=sisense_dashboard.update_time, + created_by=sisense_dashboard.created_by, + updated_by=sisense_dashboard.updated_by, + classifications=sisense_dashboard.classifications, + classification_names=sisense_dashboard.classification_names, + meanings=sisense_dashboard.meanings, + labels=sisense_dashboard.labels, + business_attributes=sisense_dashboard.business_attributes, + custom_attributes=sisense_dashboard.custom_attributes, + pending_tasks=sisense_dashboard.pending_tasks, + proxy=sisense_dashboard.proxy, + is_incomplete=sisense_dashboard.is_incomplete, + provenance_type=sisense_dashboard.provenance_type, + home_id=sisense_dashboard.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _sisense_dashboard_from_nested(nested: SisenseDashboardNested) -> SisenseDashboard: + """Convert nested format to flat SisenseDashboard.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else SisenseDashboardAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SISENSE_DASHBOARD_REL_FIELDS, + SisenseDashboardRelationshipAttributes, + ) + return SisenseDashboard( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_sisense_dashboard_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _sisense_dashboard_to_nested_bytes( + sisense_dashboard: SisenseDashboard, serde: Serde +) -> bytes: + """Convert flat SisenseDashboard to nested JSON bytes.""" + return serde.encode(_sisense_dashboard_to_nested(sisense_dashboard)) + + +def _sisense_dashboard_from_nested_bytes(data: bytes, serde: Serde) -> SisenseDashboard: + """Convert nested JSON bytes to flat SisenseDashboard.""" + nested = serde.decode(data, SisenseDashboardNested) + return _sisense_dashboard_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordTextField, + NumericField, + RelationField, +) + +SisenseDashboard.SISENSE_DASHBOARD_FOLDER_QUALIFIED_NAME = KeywordTextField( + "sisenseDashboardFolderQualifiedName", + "sisenseDashboardFolderQualifiedName", + "sisenseDashboardFolderQualifiedName.text", +) +SisenseDashboard.SISENSE_WIDGET_COUNT = NumericField( + "sisenseWidgetCount", "sisenseWidgetCount" +) +SisenseDashboard.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SisenseDashboard.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +SisenseDashboard.ANOMALO_CHECKS = RelationField("anomaloChecks") +SisenseDashboard.APPLICATION = RelationField("application") +SisenseDashboard.APPLICATION_FIELD = RelationField("applicationField") +SisenseDashboard.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +SisenseDashboard.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +SisenseDashboard.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +SisenseDashboard.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +SisenseDashboard.METRICS = RelationField("metrics") +SisenseDashboard.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SisenseDashboard.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +SisenseDashboard.MEANINGS = RelationField("meanings") +SisenseDashboard.MC_MONITORS = RelationField("mcMonitors") +SisenseDashboard.MC_INCIDENTS = RelationField("mcIncidents") +SisenseDashboard.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SisenseDashboard.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SisenseDashboard.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SisenseDashboard.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SisenseDashboard.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +SisenseDashboard.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +SisenseDashboard.FILES = RelationField("files") +SisenseDashboard.LINKS = RelationField("links") +SisenseDashboard.README = RelationField("readme") +SisenseDashboard.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +SisenseDashboard.SISENSE_WIDGETS = RelationField("sisenseWidgets") +SisenseDashboard.SISENSE_DATAMODELS = RelationField("sisenseDatamodels") +SisenseDashboard.SISENSE_FOLDER = RelationField("sisenseFolder") +SisenseDashboard.SODA_CHECKS = RelationField("sodaChecks") +SisenseDashboard.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SisenseDashboard.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/sisense_datamodel.py b/pyatlan_v9/model/assets/sisense_datamodel.py new file mode 100644 index 000000000..414a7ebc3 --- /dev/null +++ b/pyatlan_v9/model/assets/sisense_datamodel.py @@ -0,0 +1,664 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SisenseDatamodel asset model with flattened inheritance. + +This module provides: +- SisenseDatamodel: Flat asset class (easy to use) +- SisenseDatamodelAttributes: Nested attributes struct (extends AssetAttributes) +- SisenseDatamodelNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .sisense_related import RelatedSisenseDashboard, RelatedSisenseDatamodelTable + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SisenseDatamodel(Asset): + """ + Instance of a Sisense datamodel in Atlan. These group tables together that you can use to build dashboards. + """ + + SISENSE_TABLE_COUNT: ClassVar[Any] = None + SISENSE_DATAMODEL_SERVER: ClassVar[Any] = None + SISENSE_REVISION: ClassVar[Any] = None + SISENSE_LAST_BUILD_TIME: ClassVar[Any] = None + SISENSE_LAST_SUCCESSFUL_BUILD_TIME: ClassVar[Any] = None + SISENSE_LAST_PUBLISH_TIME: ClassVar[Any] = None + SISENSE_DATAMODEL_TYPE: ClassVar[Any] = None + SISENSE_DATAMODEL_RELATION_TYPE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SISENSE_DATAMODEL_TABLES: ClassVar[Any] = None + SISENSE_DASHBOARDS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SisenseDatamodel" + + sisense_table_count: Union[int, None, UnsetType] = UNSET + """Number of tables in this datamodel.""" + + sisense_datamodel_server: Union[str, None, UnsetType] = UNSET + """Hostname of the server on which this datamodel was created.""" + + sisense_revision: Union[str, None, UnsetType] = UNSET + """Revision of this datamodel.""" + + sisense_last_build_time: Union[int, None, UnsetType] = UNSET + """Time (epoch) when this datamodel was last built, in milliseconds.""" + + sisense_last_successful_build_time: Union[int, None, UnsetType] = UNSET + """Time (epoch) when this datamodel was last built successfully, in milliseconds.""" + + sisense_last_publish_time: Union[int, None, UnsetType] = UNSET + """Time (epoch) when this datamodel was last published, in milliseconds.""" + + sisense_datamodel_type: Union[str, None, UnsetType] = UNSET + """Type of this datamodel, for example: 'extract' or 'custom'.""" + + sisense_datamodel_relation_type: Union[str, None, UnsetType] = UNSET + """Default relation type for this datamodel. 'extract' type Datamodels have regular relations by default. 'live' type Datamodels have direct relations by default.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + sisense_datamodel_tables: Union[ + List[RelatedSisenseDatamodelTable], None, UnsetType + ] = UNSET + """Datamodel tables that exist within this datamodel.""" + + sisense_dashboards: Union[List[RelatedSisenseDashboard], None, UnsetType] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SisenseDatamodel" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _sisense_datamodel_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> SisenseDatamodel: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SisenseDatamodel instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _sisense_datamodel_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SisenseDatamodelAttributes(AssetAttributes): + """SisenseDatamodel-specific attributes for nested API format.""" + + sisense_table_count: Union[int, None, UnsetType] = UNSET + """Number of tables in this datamodel.""" + + sisense_datamodel_server: Union[str, None, UnsetType] = UNSET + """Hostname of the server on which this datamodel was created.""" + + sisense_revision: Union[str, None, UnsetType] = UNSET + """Revision of this datamodel.""" + + sisense_last_build_time: Union[int, None, UnsetType] = UNSET + """Time (epoch) when this datamodel was last built, in milliseconds.""" + + sisense_last_successful_build_time: Union[int, None, UnsetType] = UNSET + """Time (epoch) when this datamodel was last built successfully, in milliseconds.""" + + sisense_last_publish_time: Union[int, None, UnsetType] = UNSET + """Time (epoch) when this datamodel was last published, in milliseconds.""" + + sisense_datamodel_type: Union[str, None, UnsetType] = UNSET + """Type of this datamodel, for example: 'extract' or 'custom'.""" + + sisense_datamodel_relation_type: Union[str, None, UnsetType] = UNSET + """Default relation type for this datamodel. 'extract' type Datamodels have regular relations by default. 'live' type Datamodels have direct relations by default.""" + + +class SisenseDatamodelRelationshipAttributes(AssetRelationshipAttributes): + """SisenseDatamodel-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + sisense_datamodel_tables: Union[ + List[RelatedSisenseDatamodelTable], None, UnsetType + ] = UNSET + """Datamodel tables that exist within this datamodel.""" + + sisense_dashboards: Union[List[RelatedSisenseDashboard], None, UnsetType] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SisenseDatamodelNested(AssetNested): + """SisenseDatamodel in nested API format for high-performance serialization.""" + + attributes: Union[SisenseDatamodelAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + SisenseDatamodelRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + SisenseDatamodelRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SisenseDatamodelRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SISENSE_DATAMODEL_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "sisense_datamodel_tables", + "sisense_dashboards", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_sisense_datamodel_attrs( + attrs: SisenseDatamodelAttributes, obj: SisenseDatamodel +) -> None: + """Populate SisenseDatamodel-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.sisense_table_count = obj.sisense_table_count + attrs.sisense_datamodel_server = obj.sisense_datamodel_server + attrs.sisense_revision = obj.sisense_revision + attrs.sisense_last_build_time = obj.sisense_last_build_time + attrs.sisense_last_successful_build_time = obj.sisense_last_successful_build_time + attrs.sisense_last_publish_time = obj.sisense_last_publish_time + attrs.sisense_datamodel_type = obj.sisense_datamodel_type + attrs.sisense_datamodel_relation_type = obj.sisense_datamodel_relation_type + + +def _extract_sisense_datamodel_attrs(attrs: SisenseDatamodelAttributes) -> dict: + """Extract all SisenseDatamodel attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["sisense_table_count"] = attrs.sisense_table_count + result["sisense_datamodel_server"] = attrs.sisense_datamodel_server + result["sisense_revision"] = attrs.sisense_revision + result["sisense_last_build_time"] = attrs.sisense_last_build_time + result["sisense_last_successful_build_time"] = ( + attrs.sisense_last_successful_build_time + ) + result["sisense_last_publish_time"] = attrs.sisense_last_publish_time + result["sisense_datamodel_type"] = attrs.sisense_datamodel_type + result["sisense_datamodel_relation_type"] = attrs.sisense_datamodel_relation_type + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _sisense_datamodel_to_nested( + sisense_datamodel: SisenseDatamodel, +) -> SisenseDatamodelNested: + """Convert flat SisenseDatamodel to nested format.""" + attrs = SisenseDatamodelAttributes() + _populate_sisense_datamodel_attrs(attrs, sisense_datamodel) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + sisense_datamodel, + _SISENSE_DATAMODEL_REL_FIELDS, + SisenseDatamodelRelationshipAttributes, + ) + return SisenseDatamodelNested( + guid=sisense_datamodel.guid, + type_name=sisense_datamodel.type_name, + status=sisense_datamodel.status, + version=sisense_datamodel.version, + create_time=sisense_datamodel.create_time, + update_time=sisense_datamodel.update_time, + created_by=sisense_datamodel.created_by, + updated_by=sisense_datamodel.updated_by, + classifications=sisense_datamodel.classifications, + classification_names=sisense_datamodel.classification_names, + meanings=sisense_datamodel.meanings, + labels=sisense_datamodel.labels, + business_attributes=sisense_datamodel.business_attributes, + custom_attributes=sisense_datamodel.custom_attributes, + pending_tasks=sisense_datamodel.pending_tasks, + proxy=sisense_datamodel.proxy, + is_incomplete=sisense_datamodel.is_incomplete, + provenance_type=sisense_datamodel.provenance_type, + home_id=sisense_datamodel.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _sisense_datamodel_from_nested(nested: SisenseDatamodelNested) -> SisenseDatamodel: + """Convert nested format to flat SisenseDatamodel.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else SisenseDatamodelAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SISENSE_DATAMODEL_REL_FIELDS, + SisenseDatamodelRelationshipAttributes, + ) + return SisenseDatamodel( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_sisense_datamodel_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _sisense_datamodel_to_nested_bytes( + sisense_datamodel: SisenseDatamodel, serde: Serde +) -> bytes: + """Convert flat SisenseDatamodel to nested JSON bytes.""" + return serde.encode(_sisense_datamodel_to_nested(sisense_datamodel)) + + +def _sisense_datamodel_from_nested_bytes(data: bytes, serde: Serde) -> SisenseDatamodel: + """Convert nested JSON bytes to flat SisenseDatamodel.""" + nested = serde.decode(data, SisenseDatamodelNested) + return _sisense_datamodel_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +SisenseDatamodel.SISENSE_TABLE_COUNT = NumericField( + "sisenseTableCount", "sisenseTableCount" +) +SisenseDatamodel.SISENSE_DATAMODEL_SERVER = KeywordField( + "sisenseDatamodelServer", "sisenseDatamodelServer" +) +SisenseDatamodel.SISENSE_REVISION = KeywordField("sisenseRevision", "sisenseRevision") +SisenseDatamodel.SISENSE_LAST_BUILD_TIME = NumericField( + "sisenseLastBuildTime", "sisenseLastBuildTime" +) +SisenseDatamodel.SISENSE_LAST_SUCCESSFUL_BUILD_TIME = NumericField( + "sisenseLastSuccessfulBuildTime", "sisenseLastSuccessfulBuildTime" +) +SisenseDatamodel.SISENSE_LAST_PUBLISH_TIME = NumericField( + "sisenseLastPublishTime", "sisenseLastPublishTime" +) +SisenseDatamodel.SISENSE_DATAMODEL_TYPE = KeywordField( + "sisenseDatamodelType", "sisenseDatamodelType" +) +SisenseDatamodel.SISENSE_DATAMODEL_RELATION_TYPE = KeywordField( + "sisenseDatamodelRelationType", "sisenseDatamodelRelationType" +) +SisenseDatamodel.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SisenseDatamodel.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +SisenseDatamodel.ANOMALO_CHECKS = RelationField("anomaloChecks") +SisenseDatamodel.APPLICATION = RelationField("application") +SisenseDatamodel.APPLICATION_FIELD = RelationField("applicationField") +SisenseDatamodel.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +SisenseDatamodel.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +SisenseDatamodel.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +SisenseDatamodel.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +SisenseDatamodel.METRICS = RelationField("metrics") +SisenseDatamodel.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SisenseDatamodel.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +SisenseDatamodel.MEANINGS = RelationField("meanings") +SisenseDatamodel.MC_MONITORS = RelationField("mcMonitors") +SisenseDatamodel.MC_INCIDENTS = RelationField("mcIncidents") +SisenseDatamodel.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SisenseDatamodel.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SisenseDatamodel.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SisenseDatamodel.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SisenseDatamodel.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +SisenseDatamodel.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +SisenseDatamodel.FILES = RelationField("files") +SisenseDatamodel.LINKS = RelationField("links") +SisenseDatamodel.README = RelationField("readme") +SisenseDatamodel.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +SisenseDatamodel.SISENSE_DATAMODEL_TABLES = RelationField("sisenseDatamodelTables") +SisenseDatamodel.SISENSE_DASHBOARDS = RelationField("sisenseDashboards") +SisenseDatamodel.SODA_CHECKS = RelationField("sodaChecks") +SisenseDatamodel.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SisenseDatamodel.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/sisense_datamodel_table.py b/pyatlan_v9/model/assets/sisense_datamodel_table.py new file mode 100644 index 000000000..c1e0a7d99 --- /dev/null +++ b/pyatlan_v9/model/assets/sisense_datamodel_table.py @@ -0,0 +1,687 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SisenseDatamodelTable asset model with flattened inheritance. + +This module provides: +- SisenseDatamodelTable: Flat asset class (easy to use) +- SisenseDatamodelTableAttributes: Nested attributes struct (extends AssetAttributes) +- SisenseDatamodelTableNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .sisense_related import RelatedSisenseDatamodel, RelatedSisenseWidget + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SisenseDatamodelTable(Asset): + """ + Instance of a Sisense datamodel table in Atlan. + """ + + SISENSE_DATAMODEL_QUALIFIED_NAME: ClassVar[Any] = None + SISENSE_COLUMN_COUNT: ClassVar[Any] = None + SISENSE_TYPE: ClassVar[Any] = None + SISENSE_DATAMODEL_TABLE_EXPRESSION: ClassVar[Any] = None + SISENSE_IS_MATERIALIZED: ClassVar[Any] = None + SISENSE_IS_HIDDEN: ClassVar[Any] = None + SISENSE_SCHEDULE: ClassVar[Any] = None + SISENSE_LIVE_QUERY_SETTINGS: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SISENSE_DATAMODEL: ClassVar[Any] = None + SISENSE_WIDGETS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SisenseDatamodelTable" + + sisense_datamodel_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the datamodel in which this datamodel table exists.""" + + sisense_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns present in this datamodel table.""" + + sisense_type: Union[str, None, UnsetType] = UNSET + """Type of this datamodel table, for example: 'base' for regular tables, 'custom' for SQL expression-based tables.""" + + sisense_datamodel_table_expression: Union[str, None, UnsetType] = UNSET + """SQL expression of this datamodel table.""" + + sisense_is_materialized: Union[bool, None, UnsetType] = UNSET + """Whether this datamodel table is materialised (true) or not (false).""" + + sisense_is_hidden: Union[bool, None, UnsetType] = UNSET + """Whether this datamodel table is hidden in Sisense (true) or not (false).""" + + sisense_schedule: Union[str, None, UnsetType] = UNSET + """JSON specifying the refresh schedule of this datamodel table.""" + + sisense_live_query_settings: Union[str, None, UnsetType] = UNSET + """JSON specifying the LiveQuery settings of this datamodel table.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + sisense_datamodel: Union[RelatedSisenseDatamodel, None, UnsetType] = UNSET + """Datamodel in which this datamodel table exists.""" + + sisense_widgets: Union[List[RelatedSisenseWidget], None, UnsetType] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SisenseDatamodelTable" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _sisense_datamodel_table_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> SisenseDatamodelTable: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SisenseDatamodelTable instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _sisense_datamodel_table_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SisenseDatamodelTableAttributes(AssetAttributes): + """SisenseDatamodelTable-specific attributes for nested API format.""" + + sisense_datamodel_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the datamodel in which this datamodel table exists.""" + + sisense_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns present in this datamodel table.""" + + sisense_type: Union[str, None, UnsetType] = UNSET + """Type of this datamodel table, for example: 'base' for regular tables, 'custom' for SQL expression-based tables.""" + + sisense_datamodel_table_expression: Union[str, None, UnsetType] = UNSET + """SQL expression of this datamodel table.""" + + sisense_is_materialized: Union[bool, None, UnsetType] = UNSET + """Whether this datamodel table is materialised (true) or not (false).""" + + sisense_is_hidden: Union[bool, None, UnsetType] = UNSET + """Whether this datamodel table is hidden in Sisense (true) or not (false).""" + + sisense_schedule: Union[str, None, UnsetType] = UNSET + """JSON specifying the refresh schedule of this datamodel table.""" + + sisense_live_query_settings: Union[str, None, UnsetType] = UNSET + """JSON specifying the LiveQuery settings of this datamodel table.""" + + +class SisenseDatamodelTableRelationshipAttributes(AssetRelationshipAttributes): + """SisenseDatamodelTable-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + sisense_datamodel: Union[RelatedSisenseDatamodel, None, UnsetType] = UNSET + """Datamodel in which this datamodel table exists.""" + + sisense_widgets: Union[List[RelatedSisenseWidget], None, UnsetType] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SisenseDatamodelTableNested(AssetNested): + """SisenseDatamodelTable in nested API format for high-performance serialization.""" + + attributes: Union[SisenseDatamodelTableAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + SisenseDatamodelTableRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + SisenseDatamodelTableRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SisenseDatamodelTableRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SISENSE_DATAMODEL_TABLE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "sisense_datamodel", + "sisense_widgets", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_sisense_datamodel_table_attrs( + attrs: SisenseDatamodelTableAttributes, obj: SisenseDatamodelTable +) -> None: + """Populate SisenseDatamodelTable-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.sisense_datamodel_qualified_name = obj.sisense_datamodel_qualified_name + attrs.sisense_column_count = obj.sisense_column_count + attrs.sisense_type = obj.sisense_type + attrs.sisense_datamodel_table_expression = obj.sisense_datamodel_table_expression + attrs.sisense_is_materialized = obj.sisense_is_materialized + attrs.sisense_is_hidden = obj.sisense_is_hidden + attrs.sisense_schedule = obj.sisense_schedule + attrs.sisense_live_query_settings = obj.sisense_live_query_settings + + +def _extract_sisense_datamodel_table_attrs( + attrs: SisenseDatamodelTableAttributes, +) -> dict: + """Extract all SisenseDatamodelTable attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["sisense_datamodel_qualified_name"] = attrs.sisense_datamodel_qualified_name + result["sisense_column_count"] = attrs.sisense_column_count + result["sisense_type"] = attrs.sisense_type + result["sisense_datamodel_table_expression"] = ( + attrs.sisense_datamodel_table_expression + ) + result["sisense_is_materialized"] = attrs.sisense_is_materialized + result["sisense_is_hidden"] = attrs.sisense_is_hidden + result["sisense_schedule"] = attrs.sisense_schedule + result["sisense_live_query_settings"] = attrs.sisense_live_query_settings + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _sisense_datamodel_table_to_nested( + sisense_datamodel_table: SisenseDatamodelTable, +) -> SisenseDatamodelTableNested: + """Convert flat SisenseDatamodelTable to nested format.""" + attrs = SisenseDatamodelTableAttributes() + _populate_sisense_datamodel_table_attrs(attrs, sisense_datamodel_table) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + sisense_datamodel_table, + _SISENSE_DATAMODEL_TABLE_REL_FIELDS, + SisenseDatamodelTableRelationshipAttributes, + ) + return SisenseDatamodelTableNested( + guid=sisense_datamodel_table.guid, + type_name=sisense_datamodel_table.type_name, + status=sisense_datamodel_table.status, + version=sisense_datamodel_table.version, + create_time=sisense_datamodel_table.create_time, + update_time=sisense_datamodel_table.update_time, + created_by=sisense_datamodel_table.created_by, + updated_by=sisense_datamodel_table.updated_by, + classifications=sisense_datamodel_table.classifications, + classification_names=sisense_datamodel_table.classification_names, + meanings=sisense_datamodel_table.meanings, + labels=sisense_datamodel_table.labels, + business_attributes=sisense_datamodel_table.business_attributes, + custom_attributes=sisense_datamodel_table.custom_attributes, + pending_tasks=sisense_datamodel_table.pending_tasks, + proxy=sisense_datamodel_table.proxy, + is_incomplete=sisense_datamodel_table.is_incomplete, + provenance_type=sisense_datamodel_table.provenance_type, + home_id=sisense_datamodel_table.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _sisense_datamodel_table_from_nested( + nested: SisenseDatamodelTableNested, +) -> SisenseDatamodelTable: + """Convert nested format to flat SisenseDatamodelTable.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else SisenseDatamodelTableAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SISENSE_DATAMODEL_TABLE_REL_FIELDS, + SisenseDatamodelTableRelationshipAttributes, + ) + return SisenseDatamodelTable( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_sisense_datamodel_table_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _sisense_datamodel_table_to_nested_bytes( + sisense_datamodel_table: SisenseDatamodelTable, serde: Serde +) -> bytes: + """Convert flat SisenseDatamodelTable to nested JSON bytes.""" + return serde.encode(_sisense_datamodel_table_to_nested(sisense_datamodel_table)) + + +def _sisense_datamodel_table_from_nested_bytes( + data: bytes, serde: Serde +) -> SisenseDatamodelTable: + """Convert nested JSON bytes to flat SisenseDatamodelTable.""" + nested = serde.decode(data, SisenseDatamodelTableNested) + return _sisense_datamodel_table_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +SisenseDatamodelTable.SISENSE_DATAMODEL_QUALIFIED_NAME = KeywordTextField( + "sisenseDatamodelQualifiedName", + "sisenseDatamodelQualifiedName", + "sisenseDatamodelQualifiedName.text", +) +SisenseDatamodelTable.SISENSE_COLUMN_COUNT = NumericField( + "sisenseColumnCount", "sisenseColumnCount" +) +SisenseDatamodelTable.SISENSE_TYPE = KeywordField("sisenseType", "sisenseType") +SisenseDatamodelTable.SISENSE_DATAMODEL_TABLE_EXPRESSION = KeywordField( + "sisenseDatamodelTableExpression", "sisenseDatamodelTableExpression" +) +SisenseDatamodelTable.SISENSE_IS_MATERIALIZED = BooleanField( + "sisenseIsMaterialized", "sisenseIsMaterialized" +) +SisenseDatamodelTable.SISENSE_IS_HIDDEN = BooleanField( + "sisenseIsHidden", "sisenseIsHidden" +) +SisenseDatamodelTable.SISENSE_SCHEDULE = KeywordField( + "sisenseSchedule", "sisenseSchedule" +) +SisenseDatamodelTable.SISENSE_LIVE_QUERY_SETTINGS = KeywordField( + "sisenseLiveQuerySettings", "sisenseLiveQuerySettings" +) +SisenseDatamodelTable.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SisenseDatamodelTable.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +SisenseDatamodelTable.ANOMALO_CHECKS = RelationField("anomaloChecks") +SisenseDatamodelTable.APPLICATION = RelationField("application") +SisenseDatamodelTable.APPLICATION_FIELD = RelationField("applicationField") +SisenseDatamodelTable.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +SisenseDatamodelTable.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +SisenseDatamodelTable.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +SisenseDatamodelTable.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +SisenseDatamodelTable.METRICS = RelationField("metrics") +SisenseDatamodelTable.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SisenseDatamodelTable.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +SisenseDatamodelTable.MEANINGS = RelationField("meanings") +SisenseDatamodelTable.MC_MONITORS = RelationField("mcMonitors") +SisenseDatamodelTable.MC_INCIDENTS = RelationField("mcIncidents") +SisenseDatamodelTable.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SisenseDatamodelTable.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SisenseDatamodelTable.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SisenseDatamodelTable.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SisenseDatamodelTable.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +SisenseDatamodelTable.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +SisenseDatamodelTable.FILES = RelationField("files") +SisenseDatamodelTable.LINKS = RelationField("links") +SisenseDatamodelTable.README = RelationField("readme") +SisenseDatamodelTable.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +SisenseDatamodelTable.SISENSE_DATAMODEL = RelationField("sisenseDatamodel") +SisenseDatamodelTable.SISENSE_WIDGETS = RelationField("sisenseWidgets") +SisenseDatamodelTable.SODA_CHECKS = RelationField("sodaChecks") +SisenseDatamodelTable.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SisenseDatamodelTable.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/sisense_folder.py b/pyatlan_v9/model/assets/sisense_folder.py new file mode 100644 index 000000000..276d137d5 --- /dev/null +++ b/pyatlan_v9/model/assets/sisense_folder.py @@ -0,0 +1,602 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SisenseFolder asset model with flattened inheritance. + +This module provides: +- SisenseFolder: Flat asset class (easy to use) +- SisenseFolderAttributes: Nested attributes struct (extends AssetAttributes) +- SisenseFolderNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .sisense_related import ( + RelatedSisenseDashboard, + RelatedSisenseFolder, + RelatedSisenseWidget, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SisenseFolder(Asset): + """ + Instance of a Sisense folder in Atlan. + """ + + SISENSE_FOLDER_PARENT_FOLDER_QUALIFIED_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SISENSE_CHILD_FOLDERS: ClassVar[Any] = None + SISENSE_PARENT_FOLDER: ClassVar[Any] = None + SISENSE_DASHBOARDS: ClassVar[Any] = None + SISENSE_WIDGETS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SisenseFolder" + + sisense_folder_parent_folder_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the parent folder in which this folder exists.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + sisense_child_folders: Union[List[RelatedSisenseFolder], None, UnsetType] = UNSET + """Sub-folders that exist within this folder.""" + + sisense_parent_folder: Union[RelatedSisenseFolder, None, UnsetType] = UNSET + """Folder in which this sub-folder exists.""" + + sisense_dashboards: Union[List[RelatedSisenseDashboard], None, UnsetType] = UNSET + """Dashboards that exist within this folder.""" + + sisense_widgets: Union[List[RelatedSisenseWidget], None, UnsetType] = UNSET + """Widgets that exist within this folder.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SisenseFolder" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _sisense_folder_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> SisenseFolder: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SisenseFolder instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _sisense_folder_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SisenseFolderAttributes(AssetAttributes): + """SisenseFolder-specific attributes for nested API format.""" + + sisense_folder_parent_folder_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the parent folder in which this folder exists.""" + + +class SisenseFolderRelationshipAttributes(AssetRelationshipAttributes): + """SisenseFolder-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + sisense_child_folders: Union[List[RelatedSisenseFolder], None, UnsetType] = UNSET + """Sub-folders that exist within this folder.""" + + sisense_parent_folder: Union[RelatedSisenseFolder, None, UnsetType] = UNSET + """Folder in which this sub-folder exists.""" + + sisense_dashboards: Union[List[RelatedSisenseDashboard], None, UnsetType] = UNSET + """Dashboards that exist within this folder.""" + + sisense_widgets: Union[List[RelatedSisenseWidget], None, UnsetType] = UNSET + """Widgets that exist within this folder.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SisenseFolderNested(AssetNested): + """SisenseFolder in nested API format for high-performance serialization.""" + + attributes: Union[SisenseFolderAttributes, UnsetType] = UNSET + relationship_attributes: Union[SisenseFolderRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + SisenseFolderRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SisenseFolderRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SISENSE_FOLDER_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "sisense_child_folders", + "sisense_parent_folder", + "sisense_dashboards", + "sisense_widgets", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_sisense_folder_attrs( + attrs: SisenseFolderAttributes, obj: SisenseFolder +) -> None: + """Populate SisenseFolder-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.sisense_folder_parent_folder_qualified_name = ( + obj.sisense_folder_parent_folder_qualified_name + ) + + +def _extract_sisense_folder_attrs(attrs: SisenseFolderAttributes) -> dict: + """Extract all SisenseFolder attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["sisense_folder_parent_folder_qualified_name"] = ( + attrs.sisense_folder_parent_folder_qualified_name + ) + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _sisense_folder_to_nested(sisense_folder: SisenseFolder) -> SisenseFolderNested: + """Convert flat SisenseFolder to nested format.""" + attrs = SisenseFolderAttributes() + _populate_sisense_folder_attrs(attrs, sisense_folder) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + sisense_folder, _SISENSE_FOLDER_REL_FIELDS, SisenseFolderRelationshipAttributes + ) + return SisenseFolderNested( + guid=sisense_folder.guid, + type_name=sisense_folder.type_name, + status=sisense_folder.status, + version=sisense_folder.version, + create_time=sisense_folder.create_time, + update_time=sisense_folder.update_time, + created_by=sisense_folder.created_by, + updated_by=sisense_folder.updated_by, + classifications=sisense_folder.classifications, + classification_names=sisense_folder.classification_names, + meanings=sisense_folder.meanings, + labels=sisense_folder.labels, + business_attributes=sisense_folder.business_attributes, + custom_attributes=sisense_folder.custom_attributes, + pending_tasks=sisense_folder.pending_tasks, + proxy=sisense_folder.proxy, + is_incomplete=sisense_folder.is_incomplete, + provenance_type=sisense_folder.provenance_type, + home_id=sisense_folder.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _sisense_folder_from_nested(nested: SisenseFolderNested) -> SisenseFolder: + """Convert nested format to flat SisenseFolder.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else SisenseFolderAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SISENSE_FOLDER_REL_FIELDS, + SisenseFolderRelationshipAttributes, + ) + return SisenseFolder( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_sisense_folder_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _sisense_folder_to_nested_bytes( + sisense_folder: SisenseFolder, serde: Serde +) -> bytes: + """Convert flat SisenseFolder to nested JSON bytes.""" + return serde.encode(_sisense_folder_to_nested(sisense_folder)) + + +def _sisense_folder_from_nested_bytes(data: bytes, serde: Serde) -> SisenseFolder: + """Convert nested JSON bytes to flat SisenseFolder.""" + nested = serde.decode(data, SisenseFolderNested) + return _sisense_folder_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordTextField, + RelationField, +) + +SisenseFolder.SISENSE_FOLDER_PARENT_FOLDER_QUALIFIED_NAME = KeywordTextField( + "sisenseFolderParentFolderQualifiedName", + "sisenseFolderParentFolderQualifiedName", + "sisenseFolderParentFolderQualifiedName.text", +) +SisenseFolder.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SisenseFolder.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +SisenseFolder.ANOMALO_CHECKS = RelationField("anomaloChecks") +SisenseFolder.APPLICATION = RelationField("application") +SisenseFolder.APPLICATION_FIELD = RelationField("applicationField") +SisenseFolder.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +SisenseFolder.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +SisenseFolder.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +SisenseFolder.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +SisenseFolder.METRICS = RelationField("metrics") +SisenseFolder.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SisenseFolder.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +SisenseFolder.MEANINGS = RelationField("meanings") +SisenseFolder.MC_MONITORS = RelationField("mcMonitors") +SisenseFolder.MC_INCIDENTS = RelationField("mcIncidents") +SisenseFolder.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SisenseFolder.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SisenseFolder.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SisenseFolder.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SisenseFolder.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +SisenseFolder.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +SisenseFolder.FILES = RelationField("files") +SisenseFolder.LINKS = RelationField("links") +SisenseFolder.README = RelationField("readme") +SisenseFolder.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +SisenseFolder.SISENSE_CHILD_FOLDERS = RelationField("sisenseChildFolders") +SisenseFolder.SISENSE_PARENT_FOLDER = RelationField("sisenseParentFolder") +SisenseFolder.SISENSE_DASHBOARDS = RelationField("sisenseDashboards") +SisenseFolder.SISENSE_WIDGETS = RelationField("sisenseWidgets") +SisenseFolder.SODA_CHECKS = RelationField("sodaChecks") +SisenseFolder.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SisenseFolder.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/sisense_related.py b/pyatlan_v9/model/assets/sisense_related.py new file mode 100644 index 000000000..7c64a5103 --- /dev/null +++ b/pyatlan_v9/model/assets/sisense_related.py @@ -0,0 +1,190 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Sisense module. + +This module contains all Related{Type} classes for the Sisense type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Union + +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedBI +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedSisense", + "RelatedSisenseDashboard", + "RelatedSisenseDatamodel", + "RelatedSisenseDatamodelTable", + "RelatedSisenseFolder", + "RelatedSisenseWidget", +] + + +class RelatedSisense(RelatedBI): + """ + Related entity reference for Sisense assets. + + Extends RelatedBI with Sisense-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Sisense" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Sisense" + + +class RelatedSisenseDashboard(RelatedSisense): + """ + Related entity reference for SisenseDashboard assets. + + Extends RelatedSisense with SisenseDashboard-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SisenseDashboard" so it serializes correctly + + sisense_dashboard_folder_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the folder in which this dashboard exists.""" + + sisense_widget_count: Union[int, None, UnsetType] = UNSET + """Number of widgets in this dashboard.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SisenseDashboard" + + +class RelatedSisenseDatamodel(RelatedSisense): + """ + Related entity reference for SisenseDatamodel assets. + + Extends RelatedSisense with SisenseDatamodel-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SisenseDatamodel" so it serializes correctly + + sisense_table_count: Union[int, None, UnsetType] = UNSET + """Number of tables in this datamodel.""" + + sisense_datamodel_server: Union[str, None, UnsetType] = UNSET + """Hostname of the server on which this datamodel was created.""" + + sisense_revision: Union[str, None, UnsetType] = UNSET + """Revision of this datamodel.""" + + sisense_last_build_time: Union[int, None, UnsetType] = UNSET + """Time (epoch) when this datamodel was last built, in milliseconds.""" + + sisense_last_successful_build_time: Union[int, None, UnsetType] = UNSET + """Time (epoch) when this datamodel was last built successfully, in milliseconds.""" + + sisense_last_publish_time: Union[int, None, UnsetType] = UNSET + """Time (epoch) when this datamodel was last published, in milliseconds.""" + + sisense_datamodel_type: Union[str, None, UnsetType] = UNSET + """Type of this datamodel, for example: 'extract' or 'custom'.""" + + sisense_datamodel_relation_type: Union[str, None, UnsetType] = UNSET + """Default relation type for this datamodel. 'extract' type Datamodels have regular relations by default. 'live' type Datamodels have direct relations by default.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SisenseDatamodel" + + +class RelatedSisenseDatamodelTable(RelatedSisense): + """ + Related entity reference for SisenseDatamodelTable assets. + + Extends RelatedSisense with SisenseDatamodelTable-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SisenseDatamodelTable" so it serializes correctly + + sisense_datamodel_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the datamodel in which this datamodel table exists.""" + + sisense_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns present in this datamodel table.""" + + sisense_type: Union[str, None, UnsetType] = UNSET + """Type of this datamodel table, for example: 'base' for regular tables, 'custom' for SQL expression-based tables.""" + + sisense_datamodel_table_expression: Union[str, None, UnsetType] = UNSET + """SQL expression of this datamodel table.""" + + sisense_is_materialized: Union[bool, None, UnsetType] = UNSET + """Whether this datamodel table is materialised (true) or not (false).""" + + sisense_is_hidden: Union[bool, None, UnsetType] = UNSET + """Whether this datamodel table is hidden in Sisense (true) or not (false).""" + + sisense_schedule: Union[str, None, UnsetType] = UNSET + """JSON specifying the refresh schedule of this datamodel table.""" + + sisense_live_query_settings: Union[str, None, UnsetType] = UNSET + """JSON specifying the LiveQuery settings of this datamodel table.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SisenseDatamodelTable" + + +class RelatedSisenseFolder(RelatedSisense): + """ + Related entity reference for SisenseFolder assets. + + Extends RelatedSisense with SisenseFolder-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SisenseFolder" so it serializes correctly + + sisense_folder_parent_folder_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the parent folder in which this folder exists.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SisenseFolder" + + +class RelatedSisenseWidget(RelatedSisense): + """ + Related entity reference for SisenseWidget assets. + + Extends RelatedSisense with SisenseWidget-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SisenseWidget" so it serializes correctly + + sisense_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns used in this widget.""" + + sisense_sub_type: Union[str, None, UnsetType] = UNSET + """Subtype of this widget.""" + + sisense_size: Union[str, None, UnsetType] = UNSET + """Size of this widget.""" + + sisense_widget_dashboard_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dashboard in which this widget exists.""" + + sisense_widget_folder_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the folder in which this widget exists.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SisenseWidget" diff --git a/pyatlan_v9/model/assets/sisense_widget.py b/pyatlan_v9/model/assets/sisense_widget.py new file mode 100644 index 000000000..b0ff219f7 --- /dev/null +++ b/pyatlan_v9/model/assets/sisense_widget.py @@ -0,0 +1,651 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SisenseWidget asset model with flattened inheritance. + +This module provides: +- SisenseWidget: Flat asset class (easy to use) +- SisenseWidgetAttributes: Nested attributes struct (extends AssetAttributes) +- SisenseWidgetNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .sisense_related import ( + RelatedSisenseDashboard, + RelatedSisenseDatamodelTable, + RelatedSisenseFolder, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SisenseWidget(Asset): + """ + Instance of a Sisense widget in Atlan. + """ + + SISENSE_COLUMN_COUNT: ClassVar[Any] = None + SISENSE_SUB_TYPE: ClassVar[Any] = None + SISENSE_SIZE: ClassVar[Any] = None + SISENSE_WIDGET_DASHBOARD_QUALIFIED_NAME: ClassVar[Any] = None + SISENSE_WIDGET_FOLDER_QUALIFIED_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SISENSE_DASHBOARD: ClassVar[Any] = None + SISENSE_DATAMODEL_TABLES: ClassVar[Any] = None + SISENSE_FOLDER: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SisenseWidget" + + sisense_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns used in this widget.""" + + sisense_sub_type: Union[str, None, UnsetType] = UNSET + """Subtype of this widget.""" + + sisense_size: Union[str, None, UnsetType] = UNSET + """Size of this widget.""" + + sisense_widget_dashboard_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dashboard in which this widget exists.""" + + sisense_widget_folder_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the folder in which this widget exists.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + sisense_dashboard: Union[RelatedSisenseDashboard, None, UnsetType] = UNSET + """Dashboard in which this widget exists.""" + + sisense_datamodel_tables: Union[ + List[RelatedSisenseDatamodelTable], None, UnsetType + ] = UNSET + """""" + + sisense_folder: Union[RelatedSisenseFolder, None, UnsetType] = UNSET + """Folder in which this widget exists.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SisenseWidget" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _sisense_widget_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> SisenseWidget: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SisenseWidget instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _sisense_widget_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SisenseWidgetAttributes(AssetAttributes): + """SisenseWidget-specific attributes for nested API format.""" + + sisense_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns used in this widget.""" + + sisense_sub_type: Union[str, None, UnsetType] = UNSET + """Subtype of this widget.""" + + sisense_size: Union[str, None, UnsetType] = UNSET + """Size of this widget.""" + + sisense_widget_dashboard_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dashboard in which this widget exists.""" + + sisense_widget_folder_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the folder in which this widget exists.""" + + +class SisenseWidgetRelationshipAttributes(AssetRelationshipAttributes): + """SisenseWidget-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + sisense_dashboard: Union[RelatedSisenseDashboard, None, UnsetType] = UNSET + """Dashboard in which this widget exists.""" + + sisense_datamodel_tables: Union[ + List[RelatedSisenseDatamodelTable], None, UnsetType + ] = UNSET + """""" + + sisense_folder: Union[RelatedSisenseFolder, None, UnsetType] = UNSET + """Folder in which this widget exists.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SisenseWidgetNested(AssetNested): + """SisenseWidget in nested API format for high-performance serialization.""" + + attributes: Union[SisenseWidgetAttributes, UnsetType] = UNSET + relationship_attributes: Union[SisenseWidgetRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + SisenseWidgetRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SisenseWidgetRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SISENSE_WIDGET_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "sisense_dashboard", + "sisense_datamodel_tables", + "sisense_folder", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_sisense_widget_attrs( + attrs: SisenseWidgetAttributes, obj: SisenseWidget +) -> None: + """Populate SisenseWidget-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.sisense_column_count = obj.sisense_column_count + attrs.sisense_sub_type = obj.sisense_sub_type + attrs.sisense_size = obj.sisense_size + attrs.sisense_widget_dashboard_qualified_name = ( + obj.sisense_widget_dashboard_qualified_name + ) + attrs.sisense_widget_folder_qualified_name = ( + obj.sisense_widget_folder_qualified_name + ) + + +def _extract_sisense_widget_attrs(attrs: SisenseWidgetAttributes) -> dict: + """Extract all SisenseWidget attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["sisense_column_count"] = attrs.sisense_column_count + result["sisense_sub_type"] = attrs.sisense_sub_type + result["sisense_size"] = attrs.sisense_size + result["sisense_widget_dashboard_qualified_name"] = ( + attrs.sisense_widget_dashboard_qualified_name + ) + result["sisense_widget_folder_qualified_name"] = ( + attrs.sisense_widget_folder_qualified_name + ) + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _sisense_widget_to_nested(sisense_widget: SisenseWidget) -> SisenseWidgetNested: + """Convert flat SisenseWidget to nested format.""" + attrs = SisenseWidgetAttributes() + _populate_sisense_widget_attrs(attrs, sisense_widget) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + sisense_widget, _SISENSE_WIDGET_REL_FIELDS, SisenseWidgetRelationshipAttributes + ) + return SisenseWidgetNested( + guid=sisense_widget.guid, + type_name=sisense_widget.type_name, + status=sisense_widget.status, + version=sisense_widget.version, + create_time=sisense_widget.create_time, + update_time=sisense_widget.update_time, + created_by=sisense_widget.created_by, + updated_by=sisense_widget.updated_by, + classifications=sisense_widget.classifications, + classification_names=sisense_widget.classification_names, + meanings=sisense_widget.meanings, + labels=sisense_widget.labels, + business_attributes=sisense_widget.business_attributes, + custom_attributes=sisense_widget.custom_attributes, + pending_tasks=sisense_widget.pending_tasks, + proxy=sisense_widget.proxy, + is_incomplete=sisense_widget.is_incomplete, + provenance_type=sisense_widget.provenance_type, + home_id=sisense_widget.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _sisense_widget_from_nested(nested: SisenseWidgetNested) -> SisenseWidget: + """Convert nested format to flat SisenseWidget.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else SisenseWidgetAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SISENSE_WIDGET_REL_FIELDS, + SisenseWidgetRelationshipAttributes, + ) + return SisenseWidget( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_sisense_widget_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _sisense_widget_to_nested_bytes( + sisense_widget: SisenseWidget, serde: Serde +) -> bytes: + """Convert flat SisenseWidget to nested JSON bytes.""" + return serde.encode(_sisense_widget_to_nested(sisense_widget)) + + +def _sisense_widget_from_nested_bytes(data: bytes, serde: Serde) -> SisenseWidget: + """Convert nested JSON bytes to flat SisenseWidget.""" + nested = serde.decode(data, SisenseWidgetNested) + return _sisense_widget_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +SisenseWidget.SISENSE_COLUMN_COUNT = NumericField( + "sisenseColumnCount", "sisenseColumnCount" +) +SisenseWidget.SISENSE_SUB_TYPE = KeywordField("sisenseSubType", "sisenseSubType") +SisenseWidget.SISENSE_SIZE = KeywordField("sisenseSize", "sisenseSize") +SisenseWidget.SISENSE_WIDGET_DASHBOARD_QUALIFIED_NAME = KeywordTextField( + "sisenseWidgetDashboardQualifiedName", + "sisenseWidgetDashboardQualifiedName", + "sisenseWidgetDashboardQualifiedName.text", +) +SisenseWidget.SISENSE_WIDGET_FOLDER_QUALIFIED_NAME = KeywordTextField( + "sisenseWidgetFolderQualifiedName", + "sisenseWidgetFolderQualifiedName", + "sisenseWidgetFolderQualifiedName.text", +) +SisenseWidget.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SisenseWidget.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +SisenseWidget.ANOMALO_CHECKS = RelationField("anomaloChecks") +SisenseWidget.APPLICATION = RelationField("application") +SisenseWidget.APPLICATION_FIELD = RelationField("applicationField") +SisenseWidget.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +SisenseWidget.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +SisenseWidget.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +SisenseWidget.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +SisenseWidget.METRICS = RelationField("metrics") +SisenseWidget.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SisenseWidget.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +SisenseWidget.MEANINGS = RelationField("meanings") +SisenseWidget.MC_MONITORS = RelationField("mcMonitors") +SisenseWidget.MC_INCIDENTS = RelationField("mcIncidents") +SisenseWidget.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SisenseWidget.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SisenseWidget.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SisenseWidget.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SisenseWidget.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +SisenseWidget.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +SisenseWidget.FILES = RelationField("files") +SisenseWidget.LINKS = RelationField("links") +SisenseWidget.README = RelationField("readme") +SisenseWidget.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +SisenseWidget.SISENSE_DASHBOARD = RelationField("sisenseDashboard") +SisenseWidget.SISENSE_DATAMODEL_TABLES = RelationField("sisenseDatamodelTables") +SisenseWidget.SISENSE_FOLDER = RelationField("sisenseFolder") +SisenseWidget.SODA_CHECKS = RelationField("sodaChecks") +SisenseWidget.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SisenseWidget.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/snowflake.py b/pyatlan_v9/model/assets/snowflake.py new file mode 100644 index 000000000..0c9721bf1 --- /dev/null +++ b/pyatlan_v9/model/assets/snowflake.py @@ -0,0 +1,811 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Snowflake asset model with flattened inheritance. + +This module provides: +- Snowflake: Flat asset class (easy to use) +- SnowflakeAttributes: Nested attributes struct (extends AssetAttributes) +- SnowflakeNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .snowflake_related import RelatedSnowflakeSemanticLogicalTable + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Snowflake(Asset): + """ + Base class for Snowflake-specific assets. + """ + + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Snowflake" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Snowflake" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _snowflake_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Snowflake: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Snowflake instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _snowflake_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SnowflakeAttributes(AssetAttributes): + """Snowflake-specific attributes for nested API format.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + +class SnowflakeRelationshipAttributes(AssetRelationshipAttributes): + """Snowflake-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SnowflakeNested(AssetNested): + """Snowflake in nested API format for high-performance serialization.""" + + attributes: Union[SnowflakeAttributes, UnsetType] = UNSET + relationship_attributes: Union[SnowflakeRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + SnowflakeRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SnowflakeRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SNOWFLAKE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_snowflake_attrs(attrs: SnowflakeAttributes, obj: Snowflake) -> None: + """Populate Snowflake-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + + +def _extract_snowflake_attrs(attrs: SnowflakeAttributes) -> dict: + """Extract all Snowflake attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _snowflake_to_nested(snowflake: Snowflake) -> SnowflakeNested: + """Convert flat Snowflake to nested format.""" + attrs = SnowflakeAttributes() + _populate_snowflake_attrs(attrs, snowflake) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + snowflake, _SNOWFLAKE_REL_FIELDS, SnowflakeRelationshipAttributes + ) + return SnowflakeNested( + guid=snowflake.guid, + type_name=snowflake.type_name, + status=snowflake.status, + version=snowflake.version, + create_time=snowflake.create_time, + update_time=snowflake.update_time, + created_by=snowflake.created_by, + updated_by=snowflake.updated_by, + classifications=snowflake.classifications, + classification_names=snowflake.classification_names, + meanings=snowflake.meanings, + labels=snowflake.labels, + business_attributes=snowflake.business_attributes, + custom_attributes=snowflake.custom_attributes, + pending_tasks=snowflake.pending_tasks, + proxy=snowflake.proxy, + is_incomplete=snowflake.is_incomplete, + provenance_type=snowflake.provenance_type, + home_id=snowflake.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _snowflake_from_nested(nested: SnowflakeNested) -> Snowflake: + """Convert nested format to flat Snowflake.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else SnowflakeAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SNOWFLAKE_REL_FIELDS, + SnowflakeRelationshipAttributes, + ) + return Snowflake( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_snowflake_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _snowflake_to_nested_bytes(snowflake: Snowflake, serde: Serde) -> bytes: + """Convert flat Snowflake to nested JSON bytes.""" + return serde.encode(_snowflake_to_nested(snowflake)) + + +def _snowflake_from_nested_bytes(data: bytes, serde: Serde) -> Snowflake: + """Convert nested JSON bytes to flat Snowflake.""" + nested = serde.decode(data, SnowflakeNested) + return _snowflake_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, +) + +Snowflake.QUERY_COUNT = NumericField("queryCount", "queryCount") +Snowflake.QUERY_USER_COUNT = NumericField("queryUserCount", "queryUserCount") +Snowflake.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +Snowflake.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +Snowflake.DATABASE_NAME = KeywordField("databaseName", "databaseName") +Snowflake.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +Snowflake.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +Snowflake.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +Snowflake.TABLE_NAME = KeywordField("tableName", "tableName") +Snowflake.TABLE_QUALIFIED_NAME = KeywordField( + "tableQualifiedName", "tableQualifiedName" +) +Snowflake.VIEW_NAME = KeywordField("viewName", "viewName") +Snowflake.VIEW_QUALIFIED_NAME = KeywordField("viewQualifiedName", "viewQualifiedName") +Snowflake.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +Snowflake.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +Snowflake.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +Snowflake.LAST_PROFILED_AT = NumericField("lastProfiledAt", "lastProfiledAt") +Snowflake.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +Snowflake.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +Snowflake.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Snowflake.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Snowflake.ANOMALO_CHECKS = RelationField("anomaloChecks") +Snowflake.APPLICATION = RelationField("application") +Snowflake.APPLICATION_FIELD = RelationField("applicationField") +Snowflake.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Snowflake.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Snowflake.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Snowflake.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Snowflake.METRICS = RelationField("metrics") +Snowflake.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Snowflake.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Snowflake.DBT_MODELS = RelationField("dbtModels") +Snowflake.SQL_DBT_MODELS = RelationField("sqlDbtModels") +Snowflake.DBT_TESTS = RelationField("dbtTests") +Snowflake.DBT_SOURCES = RelationField("dbtSources") +Snowflake.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +Snowflake.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +Snowflake.MEANINGS = RelationField("meanings") +Snowflake.MC_MONITORS = RelationField("mcMonitors") +Snowflake.MC_INCIDENTS = RelationField("mcIncidents") +Snowflake.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Snowflake.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Snowflake.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Snowflake.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Snowflake.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Snowflake.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Snowflake.FILES = RelationField("files") +Snowflake.LINKS = RelationField("links") +Snowflake.README = RelationField("readme") +Snowflake.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Snowflake.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +Snowflake.SODA_CHECKS = RelationField("sodaChecks") +Snowflake.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Snowflake.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/snowflake_ai_model_context.py b/pyatlan_v9/model/assets/snowflake_ai_model_context.py new file mode 100644 index 000000000..04da6ebd6 --- /dev/null +++ b/pyatlan_v9/model/assets/snowflake_ai_model_context.py @@ -0,0 +1,1080 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SnowflakeAIModelContext asset model with flattened inheritance. + +This module provides: +- SnowflakeAIModelContext: Flat asset class (easy to use) +- SnowflakeAIModelContextAttributes: Nested attributes struct (extends AssetAttributes) +- SnowflakeAIModelContextNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .ai_related import RelatedAIApplication, RelatedAIModelVersion +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from .sql_related import RelatedSchema +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .snowflake_related import ( + RelatedSnowflakeAIModelVersion, + RelatedSnowflakeSemanticLogicalTable, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SnowflakeAIModelContext(Asset): + """ + Instance of an ai model in snowflake. + """ + + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + AI_MODEL_DATASETS_DSL: ClassVar[Any] = None + AI_MODEL_STATUS: ClassVar[Any] = None + AI_MODEL_VERSION: ClassVar[Any] = None + ETHICAL_AI_PRIVACY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_FAIRNESS_CONFIG: ClassVar[Any] = None + ETHICAL_AI_BIAS_MITIGATION_CONFIG: ClassVar[Any] = None + ETHICAL_AI_RELIABILITY_AND_SAFETY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_TRANSPARENCY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_ACCOUNTABILITY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_ENVIRONMENTAL_CONSCIOUSNESS_CONFIG: ClassVar[Any] = None + APPLICATIONS: ClassVar[Any] = None + AI_MODEL_VERSIONS: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_AI_MODEL_SCHEMA: ClassVar[Any] = None + SNOWFLAKE_AI_MODEL_VERSIONS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SnowflakeAIModelContext" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + ai_model_datasets_dsl: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="aiModelDatasetsDSL" + ) + """Search DSL used to define which assets/datasets are part of the AI model.""" + + ai_model_status: Union[str, None, UnsetType] = UNSET + """Status of the AI model.""" + + ai_model_version: Union[str, None, UnsetType] = UNSET + """Version of the AI model.""" + + ethical_ai_privacy_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIPrivacyConfig" + ) + """Privacy configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_fairness_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIFairnessConfig" + ) + """Fairness configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_bias_mitigation_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIBiasMitigationConfig" + ) + """Bias mitigation configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_reliability_and_safety_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIReliabilityAndSafetyConfig") + ) + """Reliability and safety configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_transparency_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAITransparencyConfig" + ) + """Transparency configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_accountability_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIAccountabilityConfig" + ) + """Accountability configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_environmental_consciousness_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIEnvironmentalConsciousnessConfig") + ) + """Environmental consciousness configuration for ensuring the ethical use of an AI asset""" + + applications: Union[List[RelatedAIApplication], None, UnsetType] = UNSET + """AI applications that are created using this AI model.""" + + ai_model_versions: Union[List[RelatedAIModelVersion], None, UnsetType] = UNSET + """Versions contained within the model.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_ai_model_schema: Union[RelatedSchema, None, UnsetType] = msgspec.field( + default=UNSET, name="snowflakeAIModelSchema" + ) + """Schema containing the context.""" + + snowflake_ai_model_versions: Union[ + List[RelatedSnowflakeAIModelVersion], None, UnsetType + ] = msgspec.field(default=UNSET, name="snowflakeAIModelVersions") + """Versions contained within the context.""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SnowflakeAIModelContext" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _snowflake_ai_model_context_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> SnowflakeAIModelContext: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SnowflakeAIModelContext instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _snowflake_ai_model_context_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SnowflakeAIModelContextAttributes(AssetAttributes): + """SnowflakeAIModelContext-specific attributes for nested API format.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + ai_model_datasets_dsl: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="aiModelDatasetsDSL" + ) + """Search DSL used to define which assets/datasets are part of the AI model.""" + + ai_model_status: Union[str, None, UnsetType] = UNSET + """Status of the AI model.""" + + ai_model_version: Union[str, None, UnsetType] = UNSET + """Version of the AI model.""" + + ethical_ai_privacy_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIPrivacyConfig" + ) + """Privacy configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_fairness_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIFairnessConfig" + ) + """Fairness configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_bias_mitigation_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIBiasMitigationConfig" + ) + """Bias mitigation configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_reliability_and_safety_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIReliabilityAndSafetyConfig") + ) + """Reliability and safety configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_transparency_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAITransparencyConfig" + ) + """Transparency configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_accountability_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIAccountabilityConfig" + ) + """Accountability configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_environmental_consciousness_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIEnvironmentalConsciousnessConfig") + ) + """Environmental consciousness configuration for ensuring the ethical use of an AI asset""" + + +class SnowflakeAIModelContextRelationshipAttributes(AssetRelationshipAttributes): + """SnowflakeAIModelContext-specific relationship attributes for nested API format.""" + + applications: Union[List[RelatedAIApplication], None, UnsetType] = UNSET + """AI applications that are created using this AI model.""" + + ai_model_versions: Union[List[RelatedAIModelVersion], None, UnsetType] = UNSET + """Versions contained within the model.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_ai_model_schema: Union[RelatedSchema, None, UnsetType] = msgspec.field( + default=UNSET, name="snowflakeAIModelSchema" + ) + """Schema containing the context.""" + + snowflake_ai_model_versions: Union[ + List[RelatedSnowflakeAIModelVersion], None, UnsetType + ] = msgspec.field(default=UNSET, name="snowflakeAIModelVersions") + """Versions contained within the context.""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SnowflakeAIModelContextNested(AssetNested): + """SnowflakeAIModelContext in nested API format for high-performance serialization.""" + + attributes: Union[SnowflakeAIModelContextAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + SnowflakeAIModelContextRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + SnowflakeAIModelContextRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SnowflakeAIModelContextRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SNOWFLAKE_AI_MODEL_CONTEXT_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "applications", + "ai_model_versions", + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "snowflake_ai_model_schema", + "snowflake_ai_model_versions", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_snowflake_ai_model_context_attrs( + attrs: SnowflakeAIModelContextAttributes, obj: SnowflakeAIModelContext +) -> None: + """Populate SnowflakeAIModelContext-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + attrs.ai_model_datasets_dsl = obj.ai_model_datasets_dsl + attrs.ai_model_status = obj.ai_model_status + attrs.ai_model_version = obj.ai_model_version + attrs.ethical_ai_privacy_config = obj.ethical_ai_privacy_config + attrs.ethical_ai_fairness_config = obj.ethical_ai_fairness_config + attrs.ethical_ai_bias_mitigation_config = obj.ethical_ai_bias_mitigation_config + attrs.ethical_ai_reliability_and_safety_config = ( + obj.ethical_ai_reliability_and_safety_config + ) + attrs.ethical_ai_transparency_config = obj.ethical_ai_transparency_config + attrs.ethical_ai_accountability_config = obj.ethical_ai_accountability_config + attrs.ethical_ai_environmental_consciousness_config = ( + obj.ethical_ai_environmental_consciousness_config + ) + + +def _extract_snowflake_ai_model_context_attrs( + attrs: SnowflakeAIModelContextAttributes, +) -> dict: + """Extract all SnowflakeAIModelContext attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + result["ai_model_datasets_dsl"] = attrs.ai_model_datasets_dsl + result["ai_model_status"] = attrs.ai_model_status + result["ai_model_version"] = attrs.ai_model_version + result["ethical_ai_privacy_config"] = attrs.ethical_ai_privacy_config + result["ethical_ai_fairness_config"] = attrs.ethical_ai_fairness_config + result["ethical_ai_bias_mitigation_config"] = ( + attrs.ethical_ai_bias_mitigation_config + ) + result["ethical_ai_reliability_and_safety_config"] = ( + attrs.ethical_ai_reliability_and_safety_config + ) + result["ethical_ai_transparency_config"] = attrs.ethical_ai_transparency_config + result["ethical_ai_accountability_config"] = attrs.ethical_ai_accountability_config + result["ethical_ai_environmental_consciousness_config"] = ( + attrs.ethical_ai_environmental_consciousness_config + ) + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _snowflake_ai_model_context_to_nested( + snowflake_ai_model_context: SnowflakeAIModelContext, +) -> SnowflakeAIModelContextNested: + """Convert flat SnowflakeAIModelContext to nested format.""" + attrs = SnowflakeAIModelContextAttributes() + _populate_snowflake_ai_model_context_attrs(attrs, snowflake_ai_model_context) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + snowflake_ai_model_context, + _SNOWFLAKE_AI_MODEL_CONTEXT_REL_FIELDS, + SnowflakeAIModelContextRelationshipAttributes, + ) + return SnowflakeAIModelContextNested( + guid=snowflake_ai_model_context.guid, + type_name=snowflake_ai_model_context.type_name, + status=snowflake_ai_model_context.status, + version=snowflake_ai_model_context.version, + create_time=snowflake_ai_model_context.create_time, + update_time=snowflake_ai_model_context.update_time, + created_by=snowflake_ai_model_context.created_by, + updated_by=snowflake_ai_model_context.updated_by, + classifications=snowflake_ai_model_context.classifications, + classification_names=snowflake_ai_model_context.classification_names, + meanings=snowflake_ai_model_context.meanings, + labels=snowflake_ai_model_context.labels, + business_attributes=snowflake_ai_model_context.business_attributes, + custom_attributes=snowflake_ai_model_context.custom_attributes, + pending_tasks=snowflake_ai_model_context.pending_tasks, + proxy=snowflake_ai_model_context.proxy, + is_incomplete=snowflake_ai_model_context.is_incomplete, + provenance_type=snowflake_ai_model_context.provenance_type, + home_id=snowflake_ai_model_context.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _snowflake_ai_model_context_from_nested( + nested: SnowflakeAIModelContextNested, +) -> SnowflakeAIModelContext: + """Convert nested format to flat SnowflakeAIModelContext.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else SnowflakeAIModelContextAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SNOWFLAKE_AI_MODEL_CONTEXT_REL_FIELDS, + SnowflakeAIModelContextRelationshipAttributes, + ) + return SnowflakeAIModelContext( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_snowflake_ai_model_context_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _snowflake_ai_model_context_to_nested_bytes( + snowflake_ai_model_context: SnowflakeAIModelContext, serde: Serde +) -> bytes: + """Convert flat SnowflakeAIModelContext to nested JSON bytes.""" + return serde.encode( + _snowflake_ai_model_context_to_nested(snowflake_ai_model_context) + ) + + +def _snowflake_ai_model_context_from_nested_bytes( + data: bytes, serde: Serde +) -> SnowflakeAIModelContext: + """Convert nested JSON bytes to flat SnowflakeAIModelContext.""" + nested = serde.decode(data, SnowflakeAIModelContextNested) + return _snowflake_ai_model_context_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, +) + +SnowflakeAIModelContext.QUERY_COUNT = NumericField("queryCount", "queryCount") +SnowflakeAIModelContext.QUERY_USER_COUNT = NumericField( + "queryUserCount", "queryUserCount" +) +SnowflakeAIModelContext.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +SnowflakeAIModelContext.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +SnowflakeAIModelContext.DATABASE_NAME = KeywordField("databaseName", "databaseName") +SnowflakeAIModelContext.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +SnowflakeAIModelContext.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +SnowflakeAIModelContext.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +SnowflakeAIModelContext.TABLE_NAME = KeywordField("tableName", "tableName") +SnowflakeAIModelContext.TABLE_QUALIFIED_NAME = KeywordField( + "tableQualifiedName", "tableQualifiedName" +) +SnowflakeAIModelContext.VIEW_NAME = KeywordField("viewName", "viewName") +SnowflakeAIModelContext.VIEW_QUALIFIED_NAME = KeywordField( + "viewQualifiedName", "viewQualifiedName" +) +SnowflakeAIModelContext.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +SnowflakeAIModelContext.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +SnowflakeAIModelContext.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +SnowflakeAIModelContext.LAST_PROFILED_AT = NumericField( + "lastProfiledAt", "lastProfiledAt" +) +SnowflakeAIModelContext.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +SnowflakeAIModelContext.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +SnowflakeAIModelContext.AI_MODEL_DATASETS_DSL = KeywordField( + "aiModelDatasetsDSL", "aiModelDatasetsDSL" +) +SnowflakeAIModelContext.AI_MODEL_STATUS = KeywordField("aiModelStatus", "aiModelStatus") +SnowflakeAIModelContext.AI_MODEL_VERSION = KeywordField( + "aiModelVersion", "aiModelVersion" +) +SnowflakeAIModelContext.ETHICAL_AI_PRIVACY_CONFIG = KeywordField( + "ethicalAIPrivacyConfig", "ethicalAIPrivacyConfig" +) +SnowflakeAIModelContext.ETHICAL_AI_FAIRNESS_CONFIG = KeywordField( + "ethicalAIFairnessConfig", "ethicalAIFairnessConfig" +) +SnowflakeAIModelContext.ETHICAL_AI_BIAS_MITIGATION_CONFIG = KeywordField( + "ethicalAIBiasMitigationConfig", "ethicalAIBiasMitigationConfig" +) +SnowflakeAIModelContext.ETHICAL_AI_RELIABILITY_AND_SAFETY_CONFIG = KeywordField( + "ethicalAIReliabilityAndSafetyConfig", "ethicalAIReliabilityAndSafetyConfig" +) +SnowflakeAIModelContext.ETHICAL_AI_TRANSPARENCY_CONFIG = KeywordField( + "ethicalAITransparencyConfig", "ethicalAITransparencyConfig" +) +SnowflakeAIModelContext.ETHICAL_AI_ACCOUNTABILITY_CONFIG = KeywordField( + "ethicalAIAccountabilityConfig", "ethicalAIAccountabilityConfig" +) +SnowflakeAIModelContext.ETHICAL_AI_ENVIRONMENTAL_CONSCIOUSNESS_CONFIG = KeywordField( + "ethicalAIEnvironmentalConsciousnessConfig", + "ethicalAIEnvironmentalConsciousnessConfig", +) +SnowflakeAIModelContext.APPLICATIONS = RelationField("applications") +SnowflakeAIModelContext.AI_MODEL_VERSIONS = RelationField("aiModelVersions") +SnowflakeAIModelContext.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SnowflakeAIModelContext.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +SnowflakeAIModelContext.ANOMALO_CHECKS = RelationField("anomaloChecks") +SnowflakeAIModelContext.APPLICATION = RelationField("application") +SnowflakeAIModelContext.APPLICATION_FIELD = RelationField("applicationField") +SnowflakeAIModelContext.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +SnowflakeAIModelContext.INPUT_PORT_DATA_PRODUCTS = RelationField( + "inputPortDataProducts" +) +SnowflakeAIModelContext.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +SnowflakeAIModelContext.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +SnowflakeAIModelContext.METRICS = RelationField("metrics") +SnowflakeAIModelContext.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SnowflakeAIModelContext.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +SnowflakeAIModelContext.DBT_MODELS = RelationField("dbtModels") +SnowflakeAIModelContext.SQL_DBT_MODELS = RelationField("sqlDbtModels") +SnowflakeAIModelContext.DBT_TESTS = RelationField("dbtTests") +SnowflakeAIModelContext.DBT_SOURCES = RelationField("dbtSources") +SnowflakeAIModelContext.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +SnowflakeAIModelContext.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +SnowflakeAIModelContext.MEANINGS = RelationField("meanings") +SnowflakeAIModelContext.MC_MONITORS = RelationField("mcMonitors") +SnowflakeAIModelContext.MC_INCIDENTS = RelationField("mcIncidents") +SnowflakeAIModelContext.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SnowflakeAIModelContext.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SnowflakeAIModelContext.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SnowflakeAIModelContext.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SnowflakeAIModelContext.USER_DEF_RELATIONSHIP_TO = RelationField( + "userDefRelationshipTo" +) +SnowflakeAIModelContext.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +SnowflakeAIModelContext.FILES = RelationField("files") +SnowflakeAIModelContext.LINKS = RelationField("links") +SnowflakeAIModelContext.README = RelationField("readme") +SnowflakeAIModelContext.SCHEMA_REGISTRY_SUBJECTS = RelationField( + "schemaRegistrySubjects" +) +SnowflakeAIModelContext.SNOWFLAKE_AI_MODEL_SCHEMA = RelationField( + "snowflakeAIModelSchema" +) +SnowflakeAIModelContext.SNOWFLAKE_AI_MODEL_VERSIONS = RelationField( + "snowflakeAIModelVersions" +) +SnowflakeAIModelContext.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +SnowflakeAIModelContext.SODA_CHECKS = RelationField("sodaChecks") +SnowflakeAIModelContext.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SnowflakeAIModelContext.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/snowflake_ai_model_version.py b/pyatlan_v9/model/assets/snowflake_ai_model_version.py new file mode 100644 index 000000000..d83165bab --- /dev/null +++ b/pyatlan_v9/model/assets/snowflake_ai_model_version.py @@ -0,0 +1,1073 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SnowflakeAIModelVersion asset model with flattened inheritance. + +This module provides: +- SnowflakeAIModelVersion: Flat asset class (easy to use) +- SnowflakeAIModelVersionAttributes: Nested attributes struct (extends AssetAttributes) +- SnowflakeAIModelVersionNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .ai_related import RelatedAIModel +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .snowflake_related import ( + RelatedSnowflakeAIModelContext, + RelatedSnowflakeSemanticLogicalTable, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SnowflakeAIModelVersion(Asset): + """ + Instance of an ai model version in snowflake. + """ + + SNOWFLAKE_NAME: ClassVar[Any] = None + SNOWFLAKE_TYPE: ClassVar[Any] = None + SNOWFLAKE_ALIASES: ClassVar[Any] = None + SNOWFLAKE_METRICS: ClassVar[Any] = None + SNOWFLAKE_FUNCTIONS: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + ETHICAL_AI_PRIVACY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_FAIRNESS_CONFIG: ClassVar[Any] = None + ETHICAL_AI_BIAS_MITIGATION_CONFIG: ClassVar[Any] = None + ETHICAL_AI_RELIABILITY_AND_SAFETY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_TRANSPARENCY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_ACCOUNTABILITY_CONFIG: ClassVar[Any] = None + ETHICAL_AI_ENVIRONMENTAL_CONSCIOUSNESS_CONFIG: ClassVar[Any] = None + AI_MODEL: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_AI_MODEL_CONTEXT: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SnowflakeAIModelVersion" + + snowflake_name: Union[str, None, UnsetType] = UNSET + """Version part of the model name.""" + + snowflake_type: Union[str, None, UnsetType] = UNSET + """The type of the model version.""" + + snowflake_aliases: Union[List[str], None, UnsetType] = UNSET + """The aliases for the model version.""" + + snowflake_metrics: Union[Dict[str, str], None, UnsetType] = UNSET + """Metrics for an individual experiment.""" + + snowflake_functions: Union[List[str], None, UnsetType] = UNSET + """Functions used in the model version.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + ethical_ai_privacy_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIPrivacyConfig" + ) + """Privacy configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_fairness_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIFairnessConfig" + ) + """Fairness configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_bias_mitigation_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIBiasMitigationConfig" + ) + """Bias mitigation configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_reliability_and_safety_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIReliabilityAndSafetyConfig") + ) + """Reliability and safety configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_transparency_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAITransparencyConfig" + ) + """Transparency configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_accountability_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIAccountabilityConfig" + ) + """Accountability configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_environmental_consciousness_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIEnvironmentalConsciousnessConfig") + ) + """Environmental consciousness configuration for ensuring the ethical use of an AI asset""" + + ai_model: Union[RelatedAIModel, None, UnsetType] = UNSET + """Model containing the versions.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_ai_model_context: Union[ + RelatedSnowflakeAIModelContext, None, UnsetType + ] = msgspec.field(default=UNSET, name="snowflakeAIModelContext") + """Context containing the version.""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SnowflakeAIModelVersion" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _snowflake_ai_model_version_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> SnowflakeAIModelVersion: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SnowflakeAIModelVersion instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _snowflake_ai_model_version_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SnowflakeAIModelVersionAttributes(AssetAttributes): + """SnowflakeAIModelVersion-specific attributes for nested API format.""" + + snowflake_name: Union[str, None, UnsetType] = UNSET + """Version part of the model name.""" + + snowflake_type: Union[str, None, UnsetType] = UNSET + """The type of the model version.""" + + snowflake_aliases: Union[List[str], None, UnsetType] = UNSET + """The aliases for the model version.""" + + snowflake_metrics: Union[Dict[str, str], None, UnsetType] = UNSET + """Metrics for an individual experiment.""" + + snowflake_functions: Union[List[str], None, UnsetType] = UNSET + """Functions used in the model version.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + ethical_ai_privacy_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIPrivacyConfig" + ) + """Privacy configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_fairness_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIFairnessConfig" + ) + """Fairness configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_bias_mitigation_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIBiasMitigationConfig" + ) + """Bias mitigation configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_reliability_and_safety_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIReliabilityAndSafetyConfig") + ) + """Reliability and safety configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_transparency_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAITransparencyConfig" + ) + """Transparency configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_accountability_config: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="ethicalAIAccountabilityConfig" + ) + """Accountability configuration for ensuring the ethical use of an AI asset""" + + ethical_ai_environmental_consciousness_config: Union[str, None, UnsetType] = ( + msgspec.field(default=UNSET, name="ethicalAIEnvironmentalConsciousnessConfig") + ) + """Environmental consciousness configuration for ensuring the ethical use of an AI asset""" + + +class SnowflakeAIModelVersionRelationshipAttributes(AssetRelationshipAttributes): + """SnowflakeAIModelVersion-specific relationship attributes for nested API format.""" + + ai_model: Union[RelatedAIModel, None, UnsetType] = UNSET + """Model containing the versions.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_ai_model_context: Union[ + RelatedSnowflakeAIModelContext, None, UnsetType + ] = msgspec.field(default=UNSET, name="snowflakeAIModelContext") + """Context containing the version.""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SnowflakeAIModelVersionNested(AssetNested): + """SnowflakeAIModelVersion in nested API format for high-performance serialization.""" + + attributes: Union[SnowflakeAIModelVersionAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + SnowflakeAIModelVersionRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + SnowflakeAIModelVersionRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SnowflakeAIModelVersionRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SNOWFLAKE_AI_MODEL_VERSION_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "ai_model", + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "snowflake_ai_model_context", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_snowflake_ai_model_version_attrs( + attrs: SnowflakeAIModelVersionAttributes, obj: SnowflakeAIModelVersion +) -> None: + """Populate SnowflakeAIModelVersion-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.snowflake_name = obj.snowflake_name + attrs.snowflake_type = obj.snowflake_type + attrs.snowflake_aliases = obj.snowflake_aliases + attrs.snowflake_metrics = obj.snowflake_metrics + attrs.snowflake_functions = obj.snowflake_functions + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + attrs.ethical_ai_privacy_config = obj.ethical_ai_privacy_config + attrs.ethical_ai_fairness_config = obj.ethical_ai_fairness_config + attrs.ethical_ai_bias_mitigation_config = obj.ethical_ai_bias_mitigation_config + attrs.ethical_ai_reliability_and_safety_config = ( + obj.ethical_ai_reliability_and_safety_config + ) + attrs.ethical_ai_transparency_config = obj.ethical_ai_transparency_config + attrs.ethical_ai_accountability_config = obj.ethical_ai_accountability_config + attrs.ethical_ai_environmental_consciousness_config = ( + obj.ethical_ai_environmental_consciousness_config + ) + + +def _extract_snowflake_ai_model_version_attrs( + attrs: SnowflakeAIModelVersionAttributes, +) -> dict: + """Extract all SnowflakeAIModelVersion attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["snowflake_name"] = attrs.snowflake_name + result["snowflake_type"] = attrs.snowflake_type + result["snowflake_aliases"] = attrs.snowflake_aliases + result["snowflake_metrics"] = attrs.snowflake_metrics + result["snowflake_functions"] = attrs.snowflake_functions + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + result["ethical_ai_privacy_config"] = attrs.ethical_ai_privacy_config + result["ethical_ai_fairness_config"] = attrs.ethical_ai_fairness_config + result["ethical_ai_bias_mitigation_config"] = ( + attrs.ethical_ai_bias_mitigation_config + ) + result["ethical_ai_reliability_and_safety_config"] = ( + attrs.ethical_ai_reliability_and_safety_config + ) + result["ethical_ai_transparency_config"] = attrs.ethical_ai_transparency_config + result["ethical_ai_accountability_config"] = attrs.ethical_ai_accountability_config + result["ethical_ai_environmental_consciousness_config"] = ( + attrs.ethical_ai_environmental_consciousness_config + ) + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _snowflake_ai_model_version_to_nested( + snowflake_ai_model_version: SnowflakeAIModelVersion, +) -> SnowflakeAIModelVersionNested: + """Convert flat SnowflakeAIModelVersion to nested format.""" + attrs = SnowflakeAIModelVersionAttributes() + _populate_snowflake_ai_model_version_attrs(attrs, snowflake_ai_model_version) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + snowflake_ai_model_version, + _SNOWFLAKE_AI_MODEL_VERSION_REL_FIELDS, + SnowflakeAIModelVersionRelationshipAttributes, + ) + return SnowflakeAIModelVersionNested( + guid=snowflake_ai_model_version.guid, + type_name=snowflake_ai_model_version.type_name, + status=snowflake_ai_model_version.status, + version=snowflake_ai_model_version.version, + create_time=snowflake_ai_model_version.create_time, + update_time=snowflake_ai_model_version.update_time, + created_by=snowflake_ai_model_version.created_by, + updated_by=snowflake_ai_model_version.updated_by, + classifications=snowflake_ai_model_version.classifications, + classification_names=snowflake_ai_model_version.classification_names, + meanings=snowflake_ai_model_version.meanings, + labels=snowflake_ai_model_version.labels, + business_attributes=snowflake_ai_model_version.business_attributes, + custom_attributes=snowflake_ai_model_version.custom_attributes, + pending_tasks=snowflake_ai_model_version.pending_tasks, + proxy=snowflake_ai_model_version.proxy, + is_incomplete=snowflake_ai_model_version.is_incomplete, + provenance_type=snowflake_ai_model_version.provenance_type, + home_id=snowflake_ai_model_version.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _snowflake_ai_model_version_from_nested( + nested: SnowflakeAIModelVersionNested, +) -> SnowflakeAIModelVersion: + """Convert nested format to flat SnowflakeAIModelVersion.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else SnowflakeAIModelVersionAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SNOWFLAKE_AI_MODEL_VERSION_REL_FIELDS, + SnowflakeAIModelVersionRelationshipAttributes, + ) + return SnowflakeAIModelVersion( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_snowflake_ai_model_version_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _snowflake_ai_model_version_to_nested_bytes( + snowflake_ai_model_version: SnowflakeAIModelVersion, serde: Serde +) -> bytes: + """Convert flat SnowflakeAIModelVersion to nested JSON bytes.""" + return serde.encode( + _snowflake_ai_model_version_to_nested(snowflake_ai_model_version) + ) + + +def _snowflake_ai_model_version_from_nested_bytes( + data: bytes, serde: Serde +) -> SnowflakeAIModelVersion: + """Convert nested JSON bytes to flat SnowflakeAIModelVersion.""" + nested = serde.decode(data, SnowflakeAIModelVersionNested) + return _snowflake_ai_model_version_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, +) + +SnowflakeAIModelVersion.SNOWFLAKE_NAME = KeywordField("snowflakeName", "snowflakeName") +SnowflakeAIModelVersion.SNOWFLAKE_TYPE = KeywordField("snowflakeType", "snowflakeType") +SnowflakeAIModelVersion.SNOWFLAKE_ALIASES = KeywordField( + "snowflakeAliases", "snowflakeAliases" +) +SnowflakeAIModelVersion.SNOWFLAKE_METRICS = KeywordField( + "snowflakeMetrics", "snowflakeMetrics" +) +SnowflakeAIModelVersion.SNOWFLAKE_FUNCTIONS = KeywordField( + "snowflakeFunctions", "snowflakeFunctions" +) +SnowflakeAIModelVersion.QUERY_COUNT = NumericField("queryCount", "queryCount") +SnowflakeAIModelVersion.QUERY_USER_COUNT = NumericField( + "queryUserCount", "queryUserCount" +) +SnowflakeAIModelVersion.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +SnowflakeAIModelVersion.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +SnowflakeAIModelVersion.DATABASE_NAME = KeywordField("databaseName", "databaseName") +SnowflakeAIModelVersion.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +SnowflakeAIModelVersion.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +SnowflakeAIModelVersion.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +SnowflakeAIModelVersion.TABLE_NAME = KeywordField("tableName", "tableName") +SnowflakeAIModelVersion.TABLE_QUALIFIED_NAME = KeywordField( + "tableQualifiedName", "tableQualifiedName" +) +SnowflakeAIModelVersion.VIEW_NAME = KeywordField("viewName", "viewName") +SnowflakeAIModelVersion.VIEW_QUALIFIED_NAME = KeywordField( + "viewQualifiedName", "viewQualifiedName" +) +SnowflakeAIModelVersion.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +SnowflakeAIModelVersion.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +SnowflakeAIModelVersion.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +SnowflakeAIModelVersion.LAST_PROFILED_AT = NumericField( + "lastProfiledAt", "lastProfiledAt" +) +SnowflakeAIModelVersion.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +SnowflakeAIModelVersion.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +SnowflakeAIModelVersion.ETHICAL_AI_PRIVACY_CONFIG = KeywordField( + "ethicalAIPrivacyConfig", "ethicalAIPrivacyConfig" +) +SnowflakeAIModelVersion.ETHICAL_AI_FAIRNESS_CONFIG = KeywordField( + "ethicalAIFairnessConfig", "ethicalAIFairnessConfig" +) +SnowflakeAIModelVersion.ETHICAL_AI_BIAS_MITIGATION_CONFIG = KeywordField( + "ethicalAIBiasMitigationConfig", "ethicalAIBiasMitigationConfig" +) +SnowflakeAIModelVersion.ETHICAL_AI_RELIABILITY_AND_SAFETY_CONFIG = KeywordField( + "ethicalAIReliabilityAndSafetyConfig", "ethicalAIReliabilityAndSafetyConfig" +) +SnowflakeAIModelVersion.ETHICAL_AI_TRANSPARENCY_CONFIG = KeywordField( + "ethicalAITransparencyConfig", "ethicalAITransparencyConfig" +) +SnowflakeAIModelVersion.ETHICAL_AI_ACCOUNTABILITY_CONFIG = KeywordField( + "ethicalAIAccountabilityConfig", "ethicalAIAccountabilityConfig" +) +SnowflakeAIModelVersion.ETHICAL_AI_ENVIRONMENTAL_CONSCIOUSNESS_CONFIG = KeywordField( + "ethicalAIEnvironmentalConsciousnessConfig", + "ethicalAIEnvironmentalConsciousnessConfig", +) +SnowflakeAIModelVersion.AI_MODEL = RelationField("aiModel") +SnowflakeAIModelVersion.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SnowflakeAIModelVersion.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +SnowflakeAIModelVersion.ANOMALO_CHECKS = RelationField("anomaloChecks") +SnowflakeAIModelVersion.APPLICATION = RelationField("application") +SnowflakeAIModelVersion.APPLICATION_FIELD = RelationField("applicationField") +SnowflakeAIModelVersion.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +SnowflakeAIModelVersion.INPUT_PORT_DATA_PRODUCTS = RelationField( + "inputPortDataProducts" +) +SnowflakeAIModelVersion.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +SnowflakeAIModelVersion.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +SnowflakeAIModelVersion.METRICS = RelationField("metrics") +SnowflakeAIModelVersion.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SnowflakeAIModelVersion.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +SnowflakeAIModelVersion.DBT_MODELS = RelationField("dbtModels") +SnowflakeAIModelVersion.SQL_DBT_MODELS = RelationField("sqlDbtModels") +SnowflakeAIModelVersion.DBT_TESTS = RelationField("dbtTests") +SnowflakeAIModelVersion.DBT_SOURCES = RelationField("dbtSources") +SnowflakeAIModelVersion.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +SnowflakeAIModelVersion.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +SnowflakeAIModelVersion.MEANINGS = RelationField("meanings") +SnowflakeAIModelVersion.MC_MONITORS = RelationField("mcMonitors") +SnowflakeAIModelVersion.MC_INCIDENTS = RelationField("mcIncidents") +SnowflakeAIModelVersion.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SnowflakeAIModelVersion.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SnowflakeAIModelVersion.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SnowflakeAIModelVersion.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SnowflakeAIModelVersion.USER_DEF_RELATIONSHIP_TO = RelationField( + "userDefRelationshipTo" +) +SnowflakeAIModelVersion.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +SnowflakeAIModelVersion.FILES = RelationField("files") +SnowflakeAIModelVersion.LINKS = RelationField("links") +SnowflakeAIModelVersion.README = RelationField("readme") +SnowflakeAIModelVersion.SCHEMA_REGISTRY_SUBJECTS = RelationField( + "schemaRegistrySubjects" +) +SnowflakeAIModelVersion.SNOWFLAKE_AI_MODEL_CONTEXT = RelationField( + "snowflakeAIModelContext" +) +SnowflakeAIModelVersion.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +SnowflakeAIModelVersion.SODA_CHECKS = RelationField("sodaChecks") +SnowflakeAIModelVersion.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SnowflakeAIModelVersion.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/snowflake_dynamic_table.py b/pyatlan_v9/model/assets/snowflake_dynamic_table.py new file mode 100644 index 000000000..83244a8cd --- /dev/null +++ b/pyatlan_v9/model/assets/snowflake_dynamic_table.py @@ -0,0 +1,32 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SnowflakeDynamicTable asset model. + +This module provides the SnowflakeDynamicTable flat asset class, +which extends Asset with Snowflake dynamic table-specific attributes. +In the legacy codebase, SnowflakeDynamicTable extends Table. +""" + +from __future__ import annotations + +from typing import Union + +from msgspec import UNSET, UnsetType + +from pyatlan_v9.model.transform import register_asset + +from .asset import Asset + + +@register_asset +class SnowflakeDynamicTable(Asset): + """ + Instance of a Snowflake dynamic table in Atlan. + """ + + type_name: Union[str, UnsetType] = "SnowflakeDynamicTable" + + definition: Union[str, None, UnsetType] = UNSET + """SQL statements used to define the dynamic table.""" diff --git a/pyatlan_v9/model/assets/snowflake_related.py b/pyatlan_v9/model/assets/snowflake_related.py new file mode 100644 index 000000000..a7a72aa2a --- /dev/null +++ b/pyatlan_v9/model/assets/snowflake_related.py @@ -0,0 +1,347 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Snowflake module. + +This module contains all Related{Type} classes for the Snowflake type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .referenceable_related import RelatedReferenceable +from .sql_related import RelatedSQL + +__all__ = [ + "RelatedSnowflake", + "RelatedSnowflakeDynamicTable", + "RelatedSnowflakePipe", + "RelatedSnowflakeStage", + "RelatedSnowflakeStream", + "RelatedSnowflakeTag", + "RelatedSnowflakeAIModelContext", + "RelatedSnowflakeAIModelVersion", + "RelatedSnowflakeSemanticView", + "RelatedSnowflakeSemanticLogicalTable", + "RelatedSnowflakeSemanticFact", + "RelatedSnowflakeSemanticDimension", + "RelatedSnowflakeSemanticMetric", +] + + +class RelatedSnowflake(RelatedSQL): + """ + Related entity reference for Snowflake assets. + + Extends RelatedSQL with Snowflake-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Snowflake" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Snowflake" + + +class RelatedSnowflakeDynamicTable(RelatedSnowflake): + """ + Related entity reference for SnowflakeDynamicTable assets. + + Extends RelatedSnowflake with SnowflakeDynamicTable-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SnowflakeDynamicTable" so it serializes correctly + + definition: Union[str, None, UnsetType] = UNSET + """SQL statements used to define the dynamic table.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SnowflakeDynamicTable" + + +class RelatedSnowflakePipe(RelatedSnowflake): + """ + Related entity reference for SnowflakePipe assets. + + Extends RelatedSnowflake with SnowflakePipe-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SnowflakePipe" so it serializes correctly + + definition: Union[str, None, UnsetType] = UNSET + """SQL definition of this pipe.""" + + snowflake_is_auto_ingest_enabled: Union[bool, None, UnsetType] = UNSET + """Whether auto-ingest is enabled for this pipe (true) or not (false).""" + + snowflake_pipe_notification_channel_name: Union[str, None, UnsetType] = UNSET + """Name of the notification channel for this pipe.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SnowflakePipe" + + +class RelatedSnowflakeStage(RelatedSnowflake): + """ + Related entity reference for SnowflakeStage assets. + + Extends RelatedSnowflake with SnowflakeStage-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SnowflakeStage" so it serializes correctly + + snowflake_external_location: Union[str, None, UnsetType] = UNSET + """The URL or cloud storage path specifying the external location where the stage data files are stored. This is NULL for internal stages.""" + + snowflake_external_location_region: Union[str, None, UnsetType] = UNSET + """The geographic region identifier where the external stage is located in cloud storage. This is NULL for internal stages.""" + + snowflake_storage_integration: Union[str, None, UnsetType] = UNSET + """The name of the storage integration associated with the stage; NULL for internal stages or stages that do not use a storage integration.""" + + snowflake_type: Union[str, None, UnsetType] = UNSET + """Categorization of the stage type in Snowflake, which can be 'Internal Named' or 'External Named', indicating whether the stage storage is within Snowflake or in external cloud storage.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SnowflakeStage" + + +class RelatedSnowflakeStream(RelatedSnowflake): + """ + Related entity reference for SnowflakeStream assets. + + Extends RelatedSnowflake with SnowflakeStream-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SnowflakeStream" so it serializes correctly + + snowflake_type: Union[str, None, UnsetType] = UNSET + """Type of this stream, for example: standard, append-only, insert-only, etc.""" + + snowflake_source_type: Union[str, None, UnsetType] = UNSET + """Type of the source of this stream.""" + + snowflake_mode: Union[str, None, UnsetType] = UNSET + """Mode of this stream.""" + + snowflake_is_stale: Union[bool, None, UnsetType] = UNSET + """Whether this stream is stale (true) or not (false).""" + + snowflake_stale_after: Union[int, None, UnsetType] = UNSET + """Time (epoch) after which this stream will be stale, in milliseconds.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SnowflakeStream" + + +class RelatedSnowflakeTag(RelatedSnowflake): + """ + Related entity reference for SnowflakeTag assets. + + Extends RelatedSnowflake with SnowflakeTag-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SnowflakeTag" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SnowflakeTag" + + +class RelatedSnowflakeAIModelContext(RelatedSnowflake): + """ + Related entity reference for SnowflakeAIModelContext assets. + + Extends RelatedSnowflake with SnowflakeAIModelContext-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SnowflakeAIModelContext" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SnowflakeAIModelContext" + + +class RelatedSnowflakeAIModelVersion(RelatedSnowflake): + """ + Related entity reference for SnowflakeAIModelVersion assets. + + Extends RelatedSnowflake with SnowflakeAIModelVersion-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SnowflakeAIModelVersion" so it serializes correctly + + snowflake_name: Union[str, None, UnsetType] = UNSET + """Version part of the model name.""" + + snowflake_type: Union[str, None, UnsetType] = UNSET + """The type of the model version.""" + + snowflake_aliases: Union[List[str], None, UnsetType] = UNSET + """The aliases for the model version.""" + + snowflake_metrics: Union[Dict[str, str], None, UnsetType] = UNSET + """Metrics for an individual experiment.""" + + snowflake_functions: Union[List[str], None, UnsetType] = UNSET + """Functions used in the model version.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SnowflakeAIModelVersion" + + +class RelatedSnowflakeSemanticView(RelatedSnowflake): + """ + Related entity reference for SnowflakeSemanticView assets. + + Extends RelatedSnowflake with SnowflakeSemanticView-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SnowflakeSemanticView" so it serializes correctly + + snowflake_definition: Union[str, None, UnsetType] = UNSET + """DDL definition of the semantic view (via GET_DDL).""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SnowflakeSemanticView" + + +class RelatedSnowflakeSemanticLogicalTable(RelatedSnowflake): + """ + Related entity reference for SnowflakeSemanticLogicalTable assets. + + Extends RelatedSnowflake with SnowflakeSemanticLogicalTable-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SnowflakeSemanticLogicalTable" so it serializes correctly + + snowflake_semantic_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the semantic view in which this logical table exists.""" + + snowflake_semantic_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the semantic view in which this logical table exists.""" + + snowflake_semantic_table_primary_keys: Union[List[str], None, UnsetType] = UNSET + """Comma separated list of primary key columns for the logical table.""" + + snowflake_semantic_table_unique_keys: Union[List[str], None, UnsetType] = UNSET + """Unique key columns for the logical table.""" + + snowflake_semantic_table_distinct_ranges: Union[List[str], None, UnsetType] = UNSET + """Distinct ranges defined for the logical table.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SnowflakeSemanticLogicalTable" + + +class RelatedSnowflakeSemanticFact(RelatedSnowflake): + """ + Related entity reference for SnowflakeSemanticFact assets. + + Extends RelatedSnowflake with SnowflakeSemanticFact-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SnowflakeSemanticFact" so it serializes correctly + + snowflake_semantic_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the semantic view in which this fact exists.""" + + snowflake_semantic_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the semantic view in which this fact exists.""" + + snowflake_semantic_table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the logical table in which this fact exists.""" + + snowflake_semantic_table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the logical table in which this fact exists.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SnowflakeSemanticFact" + + +class RelatedSnowflakeSemanticDimension(RelatedSnowflake): + """ + Related entity reference for SnowflakeSemanticDimension assets. + + Extends RelatedSnowflake with SnowflakeSemanticDimension-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SnowflakeSemanticDimension" so it serializes correctly + + snowflake_semantic_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the semantic view in which this dimension exists.""" + + snowflake_semantic_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the semantic view in which this dimension exists.""" + + snowflake_semantic_table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the logical table in which this dimension exists.""" + + snowflake_semantic_table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the logical table in which this dimension exists.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SnowflakeSemanticDimension" + + +class RelatedSnowflakeSemanticMetric(RelatedSnowflake): + """ + Related entity reference for SnowflakeSemanticMetric assets. + + Extends RelatedSnowflake with SnowflakeSemanticMetric-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SnowflakeSemanticMetric" so it serializes correctly + + snowflake_semantic_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the semantic view in which this metric exists.""" + + snowflake_semantic_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the semantic view in which this metric exists.""" + + snowflake_semantic_table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the logical table in which this metric exists.""" + + snowflake_semantic_table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the logical table in which this metric exists.""" + + snowflake_metric_additive_dimensions: Union[List[str], None, UnsetType] = UNSET + """Dimensions over which the metric can be additively aggregated.""" + + snowflake_metric_non_additive_dimensions: Union[List[str], None, UnsetType] = UNSET + """Dimensions over which the metric cannot be additively aggregated.""" + + snowflake_metric_using_relationships: Union[List[str], None, UnsetType] = UNSET + """Relationships used by the metric for cross-table computation.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SnowflakeSemanticMetric" diff --git a/pyatlan_v9/model/assets/snowflake_semantic_dimension.py b/pyatlan_v9/model/assets/snowflake_semantic_dimension.py new file mode 100644 index 000000000..489be5b76 --- /dev/null +++ b/pyatlan_v9/model/assets/snowflake_semantic_dimension.py @@ -0,0 +1,1030 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SnowflakeSemanticDimension asset model with flattened inheritance. + +This module provides: +- SnowflakeSemanticDimension: Flat asset class (easy to use) +- SnowflakeSemanticDimensionAttributes: Nested attributes struct (extends AssetAttributes) +- SnowflakeSemanticDimensionNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .semantic_related import RelatedSemanticModel +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .snowflake_related import RelatedSnowflakeSemanticLogicalTable + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SnowflakeSemanticDimension(Asset): + """ + Instance of a Snowflake semantic dimension in Atlan. + """ + + SNOWFLAKE_SEMANTIC_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_VIEW_NAME: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_TABLE_QUALIFIED_NAME: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_TABLE_NAME: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + SEMANTIC_EXPRESSION: ClassVar[Any] = None + SEMANTIC_TYPE: ClassVar[Any] = None + SEMANTIC_SYNONYMS: ClassVar[Any] = None + SEMANTIC_SAMPLE_VALUES: ClassVar[Any] = None + SEMANTIC_ACCESS_MODIFIER: ClassVar[Any] = None + SEMANTIC_DATA_TYPE: ClassVar[Any] = None + SEMANTIC_LABELS: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SEMANTIC_MODEL: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLE: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SnowflakeSemanticDimension" + + snowflake_semantic_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the semantic view in which this dimension exists.""" + + snowflake_semantic_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the semantic view in which this dimension exists.""" + + snowflake_semantic_table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the logical table in which this dimension exists.""" + + snowflake_semantic_table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the logical table in which this dimension exists.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + semantic_expression: Union[str, None, UnsetType] = UNSET + """Column name or SQL expression for the semantic field.""" + + semantic_type: Union[str, None, UnsetType] = UNSET + """Detailed type of the semantic field (e.g., type of measure, type of dimension, or type of entity).""" + + semantic_synonyms: Union[List[str], None, UnsetType] = UNSET + """Alternative names or terms for the semantic field.""" + + semantic_sample_values: Union[List[str], None, UnsetType] = UNSET + """Sample values for the semantic field.""" + + semantic_access_modifier: Union[str, None, UnsetType] = UNSET + """Access level for the semantic field (e.g., public_access/private_access).""" + + semantic_data_type: Union[str, None, UnsetType] = UNSET + """Data type of the semantic field.""" + + semantic_labels: Union[List[str], None, UnsetType] = UNSET + """Labels associated with the semantic field.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + semantic_model: Union[RelatedSemanticModel, None, UnsetType] = UNSET + """Semantic model in which this dimension exists.""" + + snowflake_semantic_logical_table: Union[ + RelatedSnowflakeSemanticLogicalTable, None, UnsetType + ] = UNSET + """Logical table containing the dimension.""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SnowflakeSemanticDimension" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _snowflake_semantic_dimension_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> SnowflakeSemanticDimension: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SnowflakeSemanticDimension instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _snowflake_semantic_dimension_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SnowflakeSemanticDimensionAttributes(AssetAttributes): + """SnowflakeSemanticDimension-specific attributes for nested API format.""" + + snowflake_semantic_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the semantic view in which this dimension exists.""" + + snowflake_semantic_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the semantic view in which this dimension exists.""" + + snowflake_semantic_table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the logical table in which this dimension exists.""" + + snowflake_semantic_table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the logical table in which this dimension exists.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + semantic_expression: Union[str, None, UnsetType] = UNSET + """Column name or SQL expression for the semantic field.""" + + semantic_type: Union[str, None, UnsetType] = UNSET + """Detailed type of the semantic field (e.g., type of measure, type of dimension, or type of entity).""" + + semantic_synonyms: Union[List[str], None, UnsetType] = UNSET + """Alternative names or terms for the semantic field.""" + + semantic_sample_values: Union[List[str], None, UnsetType] = UNSET + """Sample values for the semantic field.""" + + semantic_access_modifier: Union[str, None, UnsetType] = UNSET + """Access level for the semantic field (e.g., public_access/private_access).""" + + semantic_data_type: Union[str, None, UnsetType] = UNSET + """Data type of the semantic field.""" + + semantic_labels: Union[List[str], None, UnsetType] = UNSET + """Labels associated with the semantic field.""" + + +class SnowflakeSemanticDimensionRelationshipAttributes(AssetRelationshipAttributes): + """SnowflakeSemanticDimension-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + semantic_model: Union[RelatedSemanticModel, None, UnsetType] = UNSET + """Semantic model in which this dimension exists.""" + + snowflake_semantic_logical_table: Union[ + RelatedSnowflakeSemanticLogicalTable, None, UnsetType + ] = UNSET + """Logical table containing the dimension.""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SnowflakeSemanticDimensionNested(AssetNested): + """SnowflakeSemanticDimension in nested API format for high-performance serialization.""" + + attributes: Union[SnowflakeSemanticDimensionAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + SnowflakeSemanticDimensionRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + SnowflakeSemanticDimensionRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SnowflakeSemanticDimensionRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SNOWFLAKE_SEMANTIC_DIMENSION_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "semantic_model", + "snowflake_semantic_logical_table", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_snowflake_semantic_dimension_attrs( + attrs: SnowflakeSemanticDimensionAttributes, obj: SnowflakeSemanticDimension +) -> None: + """Populate SnowflakeSemanticDimension-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.snowflake_semantic_view_qualified_name = ( + obj.snowflake_semantic_view_qualified_name + ) + attrs.snowflake_semantic_view_name = obj.snowflake_semantic_view_name + attrs.snowflake_semantic_table_qualified_name = ( + obj.snowflake_semantic_table_qualified_name + ) + attrs.snowflake_semantic_table_name = obj.snowflake_semantic_table_name + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + attrs.semantic_expression = obj.semantic_expression + attrs.semantic_type = obj.semantic_type + attrs.semantic_synonyms = obj.semantic_synonyms + attrs.semantic_sample_values = obj.semantic_sample_values + attrs.semantic_access_modifier = obj.semantic_access_modifier + attrs.semantic_data_type = obj.semantic_data_type + attrs.semantic_labels = obj.semantic_labels + + +def _extract_snowflake_semantic_dimension_attrs( + attrs: SnowflakeSemanticDimensionAttributes, +) -> dict: + """Extract all SnowflakeSemanticDimension attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["snowflake_semantic_view_qualified_name"] = ( + attrs.snowflake_semantic_view_qualified_name + ) + result["snowflake_semantic_view_name"] = attrs.snowflake_semantic_view_name + result["snowflake_semantic_table_qualified_name"] = ( + attrs.snowflake_semantic_table_qualified_name + ) + result["snowflake_semantic_table_name"] = attrs.snowflake_semantic_table_name + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + result["semantic_expression"] = attrs.semantic_expression + result["semantic_type"] = attrs.semantic_type + result["semantic_synonyms"] = attrs.semantic_synonyms + result["semantic_sample_values"] = attrs.semantic_sample_values + result["semantic_access_modifier"] = attrs.semantic_access_modifier + result["semantic_data_type"] = attrs.semantic_data_type + result["semantic_labels"] = attrs.semantic_labels + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _snowflake_semantic_dimension_to_nested( + snowflake_semantic_dimension: SnowflakeSemanticDimension, +) -> SnowflakeSemanticDimensionNested: + """Convert flat SnowflakeSemanticDimension to nested format.""" + attrs = SnowflakeSemanticDimensionAttributes() + _populate_snowflake_semantic_dimension_attrs(attrs, snowflake_semantic_dimension) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + snowflake_semantic_dimension, + _SNOWFLAKE_SEMANTIC_DIMENSION_REL_FIELDS, + SnowflakeSemanticDimensionRelationshipAttributes, + ) + return SnowflakeSemanticDimensionNested( + guid=snowflake_semantic_dimension.guid, + type_name=snowflake_semantic_dimension.type_name, + status=snowflake_semantic_dimension.status, + version=snowflake_semantic_dimension.version, + create_time=snowflake_semantic_dimension.create_time, + update_time=snowflake_semantic_dimension.update_time, + created_by=snowflake_semantic_dimension.created_by, + updated_by=snowflake_semantic_dimension.updated_by, + classifications=snowflake_semantic_dimension.classifications, + classification_names=snowflake_semantic_dimension.classification_names, + meanings=snowflake_semantic_dimension.meanings, + labels=snowflake_semantic_dimension.labels, + business_attributes=snowflake_semantic_dimension.business_attributes, + custom_attributes=snowflake_semantic_dimension.custom_attributes, + pending_tasks=snowflake_semantic_dimension.pending_tasks, + proxy=snowflake_semantic_dimension.proxy, + is_incomplete=snowflake_semantic_dimension.is_incomplete, + provenance_type=snowflake_semantic_dimension.provenance_type, + home_id=snowflake_semantic_dimension.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _snowflake_semantic_dimension_from_nested( + nested: SnowflakeSemanticDimensionNested, +) -> SnowflakeSemanticDimension: + """Convert nested format to flat SnowflakeSemanticDimension.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else SnowflakeSemanticDimensionAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SNOWFLAKE_SEMANTIC_DIMENSION_REL_FIELDS, + SnowflakeSemanticDimensionRelationshipAttributes, + ) + return SnowflakeSemanticDimension( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_snowflake_semantic_dimension_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _snowflake_semantic_dimension_to_nested_bytes( + snowflake_semantic_dimension: SnowflakeSemanticDimension, serde: Serde +) -> bytes: + """Convert flat SnowflakeSemanticDimension to nested JSON bytes.""" + return serde.encode( + _snowflake_semantic_dimension_to_nested(snowflake_semantic_dimension) + ) + + +def _snowflake_semantic_dimension_from_nested_bytes( + data: bytes, serde: Serde +) -> SnowflakeSemanticDimension: + """Convert nested JSON bytes to flat SnowflakeSemanticDimension.""" + nested = serde.decode(data, SnowflakeSemanticDimensionNested) + return _snowflake_semantic_dimension_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, + TextField, +) + +SnowflakeSemanticDimension.SNOWFLAKE_SEMANTIC_VIEW_QUALIFIED_NAME = KeywordField( + "snowflakeSemanticViewQualifiedName", "snowflakeSemanticViewQualifiedName" +) +SnowflakeSemanticDimension.SNOWFLAKE_SEMANTIC_VIEW_NAME = KeywordField( + "snowflakeSemanticViewName", "snowflakeSemanticViewName" +) +SnowflakeSemanticDimension.SNOWFLAKE_SEMANTIC_TABLE_QUALIFIED_NAME = KeywordField( + "snowflakeSemanticTableQualifiedName", "snowflakeSemanticTableQualifiedName" +) +SnowflakeSemanticDimension.SNOWFLAKE_SEMANTIC_TABLE_NAME = KeywordField( + "snowflakeSemanticTableName", "snowflakeSemanticTableName" +) +SnowflakeSemanticDimension.QUERY_COUNT = NumericField("queryCount", "queryCount") +SnowflakeSemanticDimension.QUERY_USER_COUNT = NumericField( + "queryUserCount", "queryUserCount" +) +SnowflakeSemanticDimension.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +SnowflakeSemanticDimension.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +SnowflakeSemanticDimension.DATABASE_NAME = KeywordField("databaseName", "databaseName") +SnowflakeSemanticDimension.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +SnowflakeSemanticDimension.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +SnowflakeSemanticDimension.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +SnowflakeSemanticDimension.TABLE_NAME = KeywordField("tableName", "tableName") +SnowflakeSemanticDimension.TABLE_QUALIFIED_NAME = KeywordField( + "tableQualifiedName", "tableQualifiedName" +) +SnowflakeSemanticDimension.VIEW_NAME = KeywordField("viewName", "viewName") +SnowflakeSemanticDimension.VIEW_QUALIFIED_NAME = KeywordField( + "viewQualifiedName", "viewQualifiedName" +) +SnowflakeSemanticDimension.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +SnowflakeSemanticDimension.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +SnowflakeSemanticDimension.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +SnowflakeSemanticDimension.LAST_PROFILED_AT = NumericField( + "lastProfiledAt", "lastProfiledAt" +) +SnowflakeSemanticDimension.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +SnowflakeSemanticDimension.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +SnowflakeSemanticDimension.SEMANTIC_EXPRESSION = KeywordField( + "semanticExpression", "semanticExpression" +) +SnowflakeSemanticDimension.SEMANTIC_TYPE = KeywordField("semanticType", "semanticType") +SnowflakeSemanticDimension.SEMANTIC_SYNONYMS = KeywordField( + "semanticSynonyms", "semanticSynonyms" +) +SnowflakeSemanticDimension.SEMANTIC_SAMPLE_VALUES = TextField( + "semanticSampleValues", "semanticSampleValues" +) +SnowflakeSemanticDimension.SEMANTIC_ACCESS_MODIFIER = KeywordField( + "semanticAccessModifier", "semanticAccessModifier" +) +SnowflakeSemanticDimension.SEMANTIC_DATA_TYPE = KeywordField( + "semanticDataType", "semanticDataType" +) +SnowflakeSemanticDimension.SEMANTIC_LABELS = KeywordField( + "semanticLabels", "semanticLabels" +) +SnowflakeSemanticDimension.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SnowflakeSemanticDimension.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +SnowflakeSemanticDimension.ANOMALO_CHECKS = RelationField("anomaloChecks") +SnowflakeSemanticDimension.APPLICATION = RelationField("application") +SnowflakeSemanticDimension.APPLICATION_FIELD = RelationField("applicationField") +SnowflakeSemanticDimension.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +SnowflakeSemanticDimension.INPUT_PORT_DATA_PRODUCTS = RelationField( + "inputPortDataProducts" +) +SnowflakeSemanticDimension.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +SnowflakeSemanticDimension.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +SnowflakeSemanticDimension.METRICS = RelationField("metrics") +SnowflakeSemanticDimension.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SnowflakeSemanticDimension.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +SnowflakeSemanticDimension.DBT_MODELS = RelationField("dbtModels") +SnowflakeSemanticDimension.SQL_DBT_MODELS = RelationField("sqlDbtModels") +SnowflakeSemanticDimension.DBT_TESTS = RelationField("dbtTests") +SnowflakeSemanticDimension.DBT_SOURCES = RelationField("dbtSources") +SnowflakeSemanticDimension.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +SnowflakeSemanticDimension.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +SnowflakeSemanticDimension.MEANINGS = RelationField("meanings") +SnowflakeSemanticDimension.MC_MONITORS = RelationField("mcMonitors") +SnowflakeSemanticDimension.MC_INCIDENTS = RelationField("mcIncidents") +SnowflakeSemanticDimension.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SnowflakeSemanticDimension.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SnowflakeSemanticDimension.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SnowflakeSemanticDimension.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SnowflakeSemanticDimension.USER_DEF_RELATIONSHIP_TO = RelationField( + "userDefRelationshipTo" +) +SnowflakeSemanticDimension.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +SnowflakeSemanticDimension.FILES = RelationField("files") +SnowflakeSemanticDimension.LINKS = RelationField("links") +SnowflakeSemanticDimension.README = RelationField("readme") +SnowflakeSemanticDimension.SCHEMA_REGISTRY_SUBJECTS = RelationField( + "schemaRegistrySubjects" +) +SnowflakeSemanticDimension.SEMANTIC_MODEL = RelationField("semanticModel") +SnowflakeSemanticDimension.SNOWFLAKE_SEMANTIC_LOGICAL_TABLE = RelationField( + "snowflakeSemanticLogicalTable" +) +SnowflakeSemanticDimension.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +SnowflakeSemanticDimension.SODA_CHECKS = RelationField("sodaChecks") +SnowflakeSemanticDimension.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SnowflakeSemanticDimension.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/snowflake_semantic_fact.py b/pyatlan_v9/model/assets/snowflake_semantic_fact.py new file mode 100644 index 000000000..e4aa35c47 --- /dev/null +++ b/pyatlan_v9/model/assets/snowflake_semantic_fact.py @@ -0,0 +1,1020 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SnowflakeSemanticFact asset model with flattened inheritance. + +This module provides: +- SnowflakeSemanticFact: Flat asset class (easy to use) +- SnowflakeSemanticFactAttributes: Nested attributes struct (extends AssetAttributes) +- SnowflakeSemanticFactNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .semantic_related import RelatedSemanticModel +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .snowflake_related import RelatedSnowflakeSemanticLogicalTable + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SnowflakeSemanticFact(Asset): + """ + Instance of a Snowflake semantic fact in Atlan. + """ + + SNOWFLAKE_SEMANTIC_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_VIEW_NAME: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_TABLE_QUALIFIED_NAME: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_TABLE_NAME: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + SEMANTIC_EXPRESSION: ClassVar[Any] = None + SEMANTIC_TYPE: ClassVar[Any] = None + SEMANTIC_SYNONYMS: ClassVar[Any] = None + SEMANTIC_SAMPLE_VALUES: ClassVar[Any] = None + SEMANTIC_ACCESS_MODIFIER: ClassVar[Any] = None + SEMANTIC_DATA_TYPE: ClassVar[Any] = None + SEMANTIC_LABELS: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SEMANTIC_MODEL: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLE: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SnowflakeSemanticFact" + + snowflake_semantic_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the semantic view in which this fact exists.""" + + snowflake_semantic_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the semantic view in which this fact exists.""" + + snowflake_semantic_table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the logical table in which this fact exists.""" + + snowflake_semantic_table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the logical table in which this fact exists.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + semantic_expression: Union[str, None, UnsetType] = UNSET + """Column name or SQL expression for the semantic field.""" + + semantic_type: Union[str, None, UnsetType] = UNSET + """Detailed type of the semantic field (e.g., type of measure, type of dimension, or type of entity).""" + + semantic_synonyms: Union[List[str], None, UnsetType] = UNSET + """Alternative names or terms for the semantic field.""" + + semantic_sample_values: Union[List[str], None, UnsetType] = UNSET + """Sample values for the semantic field.""" + + semantic_access_modifier: Union[str, None, UnsetType] = UNSET + """Access level for the semantic field (e.g., public_access/private_access).""" + + semantic_data_type: Union[str, None, UnsetType] = UNSET + """Data type of the semantic field.""" + + semantic_labels: Union[List[str], None, UnsetType] = UNSET + """Labels associated with the semantic field.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + semantic_model: Union[RelatedSemanticModel, None, UnsetType] = UNSET + """Semantic model in which this measure exists.""" + + snowflake_semantic_logical_table: Union[ + RelatedSnowflakeSemanticLogicalTable, None, UnsetType + ] = UNSET + """Logical table containing the fact.""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SnowflakeSemanticFact" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _snowflake_semantic_fact_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> SnowflakeSemanticFact: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SnowflakeSemanticFact instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _snowflake_semantic_fact_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SnowflakeSemanticFactAttributes(AssetAttributes): + """SnowflakeSemanticFact-specific attributes for nested API format.""" + + snowflake_semantic_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the semantic view in which this fact exists.""" + + snowflake_semantic_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the semantic view in which this fact exists.""" + + snowflake_semantic_table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the logical table in which this fact exists.""" + + snowflake_semantic_table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the logical table in which this fact exists.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + semantic_expression: Union[str, None, UnsetType] = UNSET + """Column name or SQL expression for the semantic field.""" + + semantic_type: Union[str, None, UnsetType] = UNSET + """Detailed type of the semantic field (e.g., type of measure, type of dimension, or type of entity).""" + + semantic_synonyms: Union[List[str], None, UnsetType] = UNSET + """Alternative names or terms for the semantic field.""" + + semantic_sample_values: Union[List[str], None, UnsetType] = UNSET + """Sample values for the semantic field.""" + + semantic_access_modifier: Union[str, None, UnsetType] = UNSET + """Access level for the semantic field (e.g., public_access/private_access).""" + + semantic_data_type: Union[str, None, UnsetType] = UNSET + """Data type of the semantic field.""" + + semantic_labels: Union[List[str], None, UnsetType] = UNSET + """Labels associated with the semantic field.""" + + +class SnowflakeSemanticFactRelationshipAttributes(AssetRelationshipAttributes): + """SnowflakeSemanticFact-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + semantic_model: Union[RelatedSemanticModel, None, UnsetType] = UNSET + """Semantic model in which this measure exists.""" + + snowflake_semantic_logical_table: Union[ + RelatedSnowflakeSemanticLogicalTable, None, UnsetType + ] = UNSET + """Logical table containing the fact.""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SnowflakeSemanticFactNested(AssetNested): + """SnowflakeSemanticFact in nested API format for high-performance serialization.""" + + attributes: Union[SnowflakeSemanticFactAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + SnowflakeSemanticFactRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + SnowflakeSemanticFactRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SnowflakeSemanticFactRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SNOWFLAKE_SEMANTIC_FACT_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "semantic_model", + "snowflake_semantic_logical_table", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_snowflake_semantic_fact_attrs( + attrs: SnowflakeSemanticFactAttributes, obj: SnowflakeSemanticFact +) -> None: + """Populate SnowflakeSemanticFact-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.snowflake_semantic_view_qualified_name = ( + obj.snowflake_semantic_view_qualified_name + ) + attrs.snowflake_semantic_view_name = obj.snowflake_semantic_view_name + attrs.snowflake_semantic_table_qualified_name = ( + obj.snowflake_semantic_table_qualified_name + ) + attrs.snowflake_semantic_table_name = obj.snowflake_semantic_table_name + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + attrs.semantic_expression = obj.semantic_expression + attrs.semantic_type = obj.semantic_type + attrs.semantic_synonyms = obj.semantic_synonyms + attrs.semantic_sample_values = obj.semantic_sample_values + attrs.semantic_access_modifier = obj.semantic_access_modifier + attrs.semantic_data_type = obj.semantic_data_type + attrs.semantic_labels = obj.semantic_labels + + +def _extract_snowflake_semantic_fact_attrs( + attrs: SnowflakeSemanticFactAttributes, +) -> dict: + """Extract all SnowflakeSemanticFact attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["snowflake_semantic_view_qualified_name"] = ( + attrs.snowflake_semantic_view_qualified_name + ) + result["snowflake_semantic_view_name"] = attrs.snowflake_semantic_view_name + result["snowflake_semantic_table_qualified_name"] = ( + attrs.snowflake_semantic_table_qualified_name + ) + result["snowflake_semantic_table_name"] = attrs.snowflake_semantic_table_name + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + result["semantic_expression"] = attrs.semantic_expression + result["semantic_type"] = attrs.semantic_type + result["semantic_synonyms"] = attrs.semantic_synonyms + result["semantic_sample_values"] = attrs.semantic_sample_values + result["semantic_access_modifier"] = attrs.semantic_access_modifier + result["semantic_data_type"] = attrs.semantic_data_type + result["semantic_labels"] = attrs.semantic_labels + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _snowflake_semantic_fact_to_nested( + snowflake_semantic_fact: SnowflakeSemanticFact, +) -> SnowflakeSemanticFactNested: + """Convert flat SnowflakeSemanticFact to nested format.""" + attrs = SnowflakeSemanticFactAttributes() + _populate_snowflake_semantic_fact_attrs(attrs, snowflake_semantic_fact) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + snowflake_semantic_fact, + _SNOWFLAKE_SEMANTIC_FACT_REL_FIELDS, + SnowflakeSemanticFactRelationshipAttributes, + ) + return SnowflakeSemanticFactNested( + guid=snowflake_semantic_fact.guid, + type_name=snowflake_semantic_fact.type_name, + status=snowflake_semantic_fact.status, + version=snowflake_semantic_fact.version, + create_time=snowflake_semantic_fact.create_time, + update_time=snowflake_semantic_fact.update_time, + created_by=snowflake_semantic_fact.created_by, + updated_by=snowflake_semantic_fact.updated_by, + classifications=snowflake_semantic_fact.classifications, + classification_names=snowflake_semantic_fact.classification_names, + meanings=snowflake_semantic_fact.meanings, + labels=snowflake_semantic_fact.labels, + business_attributes=snowflake_semantic_fact.business_attributes, + custom_attributes=snowflake_semantic_fact.custom_attributes, + pending_tasks=snowflake_semantic_fact.pending_tasks, + proxy=snowflake_semantic_fact.proxy, + is_incomplete=snowflake_semantic_fact.is_incomplete, + provenance_type=snowflake_semantic_fact.provenance_type, + home_id=snowflake_semantic_fact.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _snowflake_semantic_fact_from_nested( + nested: SnowflakeSemanticFactNested, +) -> SnowflakeSemanticFact: + """Convert nested format to flat SnowflakeSemanticFact.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else SnowflakeSemanticFactAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SNOWFLAKE_SEMANTIC_FACT_REL_FIELDS, + SnowflakeSemanticFactRelationshipAttributes, + ) + return SnowflakeSemanticFact( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_snowflake_semantic_fact_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _snowflake_semantic_fact_to_nested_bytes( + snowflake_semantic_fact: SnowflakeSemanticFact, serde: Serde +) -> bytes: + """Convert flat SnowflakeSemanticFact to nested JSON bytes.""" + return serde.encode(_snowflake_semantic_fact_to_nested(snowflake_semantic_fact)) + + +def _snowflake_semantic_fact_from_nested_bytes( + data: bytes, serde: Serde +) -> SnowflakeSemanticFact: + """Convert nested JSON bytes to flat SnowflakeSemanticFact.""" + nested = serde.decode(data, SnowflakeSemanticFactNested) + return _snowflake_semantic_fact_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, + TextField, +) + +SnowflakeSemanticFact.SNOWFLAKE_SEMANTIC_VIEW_QUALIFIED_NAME = KeywordField( + "snowflakeSemanticViewQualifiedName", "snowflakeSemanticViewQualifiedName" +) +SnowflakeSemanticFact.SNOWFLAKE_SEMANTIC_VIEW_NAME = KeywordField( + "snowflakeSemanticViewName", "snowflakeSemanticViewName" +) +SnowflakeSemanticFact.SNOWFLAKE_SEMANTIC_TABLE_QUALIFIED_NAME = KeywordField( + "snowflakeSemanticTableQualifiedName", "snowflakeSemanticTableQualifiedName" +) +SnowflakeSemanticFact.SNOWFLAKE_SEMANTIC_TABLE_NAME = KeywordField( + "snowflakeSemanticTableName", "snowflakeSemanticTableName" +) +SnowflakeSemanticFact.QUERY_COUNT = NumericField("queryCount", "queryCount") +SnowflakeSemanticFact.QUERY_USER_COUNT = NumericField( + "queryUserCount", "queryUserCount" +) +SnowflakeSemanticFact.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +SnowflakeSemanticFact.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +SnowflakeSemanticFact.DATABASE_NAME = KeywordField("databaseName", "databaseName") +SnowflakeSemanticFact.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +SnowflakeSemanticFact.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +SnowflakeSemanticFact.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +SnowflakeSemanticFact.TABLE_NAME = KeywordField("tableName", "tableName") +SnowflakeSemanticFact.TABLE_QUALIFIED_NAME = KeywordField( + "tableQualifiedName", "tableQualifiedName" +) +SnowflakeSemanticFact.VIEW_NAME = KeywordField("viewName", "viewName") +SnowflakeSemanticFact.VIEW_QUALIFIED_NAME = KeywordField( + "viewQualifiedName", "viewQualifiedName" +) +SnowflakeSemanticFact.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +SnowflakeSemanticFact.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +SnowflakeSemanticFact.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +SnowflakeSemanticFact.LAST_PROFILED_AT = NumericField( + "lastProfiledAt", "lastProfiledAt" +) +SnowflakeSemanticFact.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +SnowflakeSemanticFact.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +SnowflakeSemanticFact.SEMANTIC_EXPRESSION = KeywordField( + "semanticExpression", "semanticExpression" +) +SnowflakeSemanticFact.SEMANTIC_TYPE = KeywordField("semanticType", "semanticType") +SnowflakeSemanticFact.SEMANTIC_SYNONYMS = KeywordField( + "semanticSynonyms", "semanticSynonyms" +) +SnowflakeSemanticFact.SEMANTIC_SAMPLE_VALUES = TextField( + "semanticSampleValues", "semanticSampleValues" +) +SnowflakeSemanticFact.SEMANTIC_ACCESS_MODIFIER = KeywordField( + "semanticAccessModifier", "semanticAccessModifier" +) +SnowflakeSemanticFact.SEMANTIC_DATA_TYPE = KeywordField( + "semanticDataType", "semanticDataType" +) +SnowflakeSemanticFact.SEMANTIC_LABELS = KeywordField("semanticLabels", "semanticLabels") +SnowflakeSemanticFact.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SnowflakeSemanticFact.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +SnowflakeSemanticFact.ANOMALO_CHECKS = RelationField("anomaloChecks") +SnowflakeSemanticFact.APPLICATION = RelationField("application") +SnowflakeSemanticFact.APPLICATION_FIELD = RelationField("applicationField") +SnowflakeSemanticFact.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +SnowflakeSemanticFact.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +SnowflakeSemanticFact.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +SnowflakeSemanticFact.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +SnowflakeSemanticFact.METRICS = RelationField("metrics") +SnowflakeSemanticFact.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SnowflakeSemanticFact.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +SnowflakeSemanticFact.DBT_MODELS = RelationField("dbtModels") +SnowflakeSemanticFact.SQL_DBT_MODELS = RelationField("sqlDbtModels") +SnowflakeSemanticFact.DBT_TESTS = RelationField("dbtTests") +SnowflakeSemanticFact.DBT_SOURCES = RelationField("dbtSources") +SnowflakeSemanticFact.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +SnowflakeSemanticFact.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +SnowflakeSemanticFact.MEANINGS = RelationField("meanings") +SnowflakeSemanticFact.MC_MONITORS = RelationField("mcMonitors") +SnowflakeSemanticFact.MC_INCIDENTS = RelationField("mcIncidents") +SnowflakeSemanticFact.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SnowflakeSemanticFact.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SnowflakeSemanticFact.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SnowflakeSemanticFact.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SnowflakeSemanticFact.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +SnowflakeSemanticFact.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +SnowflakeSemanticFact.FILES = RelationField("files") +SnowflakeSemanticFact.LINKS = RelationField("links") +SnowflakeSemanticFact.README = RelationField("readme") +SnowflakeSemanticFact.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +SnowflakeSemanticFact.SEMANTIC_MODEL = RelationField("semanticModel") +SnowflakeSemanticFact.SNOWFLAKE_SEMANTIC_LOGICAL_TABLE = RelationField( + "snowflakeSemanticLogicalTable" +) +SnowflakeSemanticFact.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +SnowflakeSemanticFact.SODA_CHECKS = RelationField("sodaChecks") +SnowflakeSemanticFact.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SnowflakeSemanticFact.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/snowflake_semantic_logical_table.py b/pyatlan_v9/model/assets/snowflake_semantic_logical_table.py new file mode 100644 index 000000000..a5146aaae --- /dev/null +++ b/pyatlan_v9/model/assets/snowflake_semantic_logical_table.py @@ -0,0 +1,1144 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SnowflakeSemanticLogicalTable asset model with flattened inheritance. + +This module provides: +- SnowflakeSemanticLogicalTable: Flat asset class (easy to use) +- SnowflakeSemanticLogicalTableAttributes: Nested attributes struct (extends AssetAttributes) +- SnowflakeSemanticLogicalTableNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .semantic_related import RelatedSemanticModel +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from .sql_related import RelatedSQL +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .snowflake_related import ( + RelatedSnowflakeSemanticDimension, + RelatedSnowflakeSemanticFact, + RelatedSnowflakeSemanticLogicalTable, + RelatedSnowflakeSemanticMetric, + RelatedSnowflakeSemanticView, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SnowflakeSemanticLogicalTable(Asset): + """ + Instance of a Snowflake semantic logical table in Atlan. + """ + + SNOWFLAKE_SEMANTIC_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_VIEW_NAME: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_TABLE_PRIMARY_KEYS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_TABLE_UNIQUE_KEYS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_TABLE_DISTINCT_RANGES: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + SEMANTIC_EXPRESSION: ClassVar[Any] = None + SEMANTIC_TYPE: ClassVar[Any] = None + SEMANTIC_SYNONYMS: ClassVar[Any] = None + SEMANTIC_SAMPLE_VALUES: ClassVar[Any] = None + SEMANTIC_ACCESS_MODIFIER: ClassVar[Any] = None + SEMANTIC_DATA_TYPE: ClassVar[Any] = None + SEMANTIC_LABELS: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SEMANTIC_MODEL: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_VIEW: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_FACTS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_DIMENSIONS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_METRICS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SNOWFLAKE_BASE_TABLE: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLE_JOINS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SnowflakeSemanticLogicalTable" + + snowflake_semantic_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the semantic view in which this logical table exists.""" + + snowflake_semantic_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the semantic view in which this logical table exists.""" + + snowflake_semantic_table_primary_keys: Union[List[str], None, UnsetType] = UNSET + """Comma separated list of primary key columns for the logical table.""" + + snowflake_semantic_table_unique_keys: Union[List[str], None, UnsetType] = UNSET + """Unique key columns for the logical table.""" + + snowflake_semantic_table_distinct_ranges: Union[List[str], None, UnsetType] = UNSET + """Distinct ranges defined for the logical table.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + semantic_expression: Union[str, None, UnsetType] = UNSET + """Column name or SQL expression for the semantic field.""" + + semantic_type: Union[str, None, UnsetType] = UNSET + """Detailed type of the semantic field (e.g., type of measure, type of dimension, or type of entity).""" + + semantic_synonyms: Union[List[str], None, UnsetType] = UNSET + """Alternative names or terms for the semantic field.""" + + semantic_sample_values: Union[List[str], None, UnsetType] = UNSET + """Sample values for the semantic field.""" + + semantic_access_modifier: Union[str, None, UnsetType] = UNSET + """Access level for the semantic field (e.g., public_access/private_access).""" + + semantic_data_type: Union[str, None, UnsetType] = UNSET + """Data type of the semantic field.""" + + semantic_labels: Union[List[str], None, UnsetType] = UNSET + """Labels associated with the semantic field.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + semantic_model: Union[RelatedSemanticModel, None, UnsetType] = UNSET + """Semantic model in which this entity exists.""" + + snowflake_semantic_view: Union[RelatedSnowflakeSemanticView, None, UnsetType] = ( + UNSET + ) + """Semantic view containing the logical table.""" + + snowflake_semantic_facts: Union[ + List[RelatedSnowflakeSemanticFact], None, UnsetType + ] = UNSET + """Facts contained in the logical table.""" + + snowflake_semantic_dimensions: Union[ + List[RelatedSnowflakeSemanticDimension], None, UnsetType + ] = UNSET + """Dimensions contained in the logical table.""" + + snowflake_semantic_metrics: Union[ + List[RelatedSnowflakeSemanticMetric], None, UnsetType + ] = UNSET + """Metrics contained in the logical table.""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + snowflake_base_table: Union[RelatedSQL, None, UnsetType] = UNSET + """Base physical table or view referenced by this logical table.""" + + snowflake_semantic_logical_table_joins: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Logical tables that join to this logical table.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SnowflakeSemanticLogicalTable" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _snowflake_semantic_logical_table_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> SnowflakeSemanticLogicalTable: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SnowflakeSemanticLogicalTable instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _snowflake_semantic_logical_table_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SnowflakeSemanticLogicalTableAttributes(AssetAttributes): + """SnowflakeSemanticLogicalTable-specific attributes for nested API format.""" + + snowflake_semantic_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the semantic view in which this logical table exists.""" + + snowflake_semantic_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the semantic view in which this logical table exists.""" + + snowflake_semantic_table_primary_keys: Union[List[str], None, UnsetType] = UNSET + """Comma separated list of primary key columns for the logical table.""" + + snowflake_semantic_table_unique_keys: Union[List[str], None, UnsetType] = UNSET + """Unique key columns for the logical table.""" + + snowflake_semantic_table_distinct_ranges: Union[List[str], None, UnsetType] = UNSET + """Distinct ranges defined for the logical table.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + semantic_expression: Union[str, None, UnsetType] = UNSET + """Column name or SQL expression for the semantic field.""" + + semantic_type: Union[str, None, UnsetType] = UNSET + """Detailed type of the semantic field (e.g., type of measure, type of dimension, or type of entity).""" + + semantic_synonyms: Union[List[str], None, UnsetType] = UNSET + """Alternative names or terms for the semantic field.""" + + semantic_sample_values: Union[List[str], None, UnsetType] = UNSET + """Sample values for the semantic field.""" + + semantic_access_modifier: Union[str, None, UnsetType] = UNSET + """Access level for the semantic field (e.g., public_access/private_access).""" + + semantic_data_type: Union[str, None, UnsetType] = UNSET + """Data type of the semantic field.""" + + semantic_labels: Union[List[str], None, UnsetType] = UNSET + """Labels associated with the semantic field.""" + + +class SnowflakeSemanticLogicalTableRelationshipAttributes(AssetRelationshipAttributes): + """SnowflakeSemanticLogicalTable-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + semantic_model: Union[RelatedSemanticModel, None, UnsetType] = UNSET + """Semantic model in which this entity exists.""" + + snowflake_semantic_view: Union[RelatedSnowflakeSemanticView, None, UnsetType] = ( + UNSET + ) + """Semantic view containing the logical table.""" + + snowflake_semantic_facts: Union[ + List[RelatedSnowflakeSemanticFact], None, UnsetType + ] = UNSET + """Facts contained in the logical table.""" + + snowflake_semantic_dimensions: Union[ + List[RelatedSnowflakeSemanticDimension], None, UnsetType + ] = UNSET + """Dimensions contained in the logical table.""" + + snowflake_semantic_metrics: Union[ + List[RelatedSnowflakeSemanticMetric], None, UnsetType + ] = UNSET + """Metrics contained in the logical table.""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + snowflake_base_table: Union[RelatedSQL, None, UnsetType] = UNSET + """Base physical table or view referenced by this logical table.""" + + snowflake_semantic_logical_table_joins: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Logical tables that join to this logical table.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SnowflakeSemanticLogicalTableNested(AssetNested): + """SnowflakeSemanticLogicalTable in nested API format for high-performance serialization.""" + + attributes: Union[SnowflakeSemanticLogicalTableAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + SnowflakeSemanticLogicalTableRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + SnowflakeSemanticLogicalTableRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SnowflakeSemanticLogicalTableRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SNOWFLAKE_SEMANTIC_LOGICAL_TABLE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "semantic_model", + "snowflake_semantic_view", + "snowflake_semantic_facts", + "snowflake_semantic_dimensions", + "snowflake_semantic_metrics", + "snowflake_semantic_logical_tables", + "snowflake_base_table", + "snowflake_semantic_logical_table_joins", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_snowflake_semantic_logical_table_attrs( + attrs: SnowflakeSemanticLogicalTableAttributes, obj: SnowflakeSemanticLogicalTable +) -> None: + """Populate SnowflakeSemanticLogicalTable-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.snowflake_semantic_view_qualified_name = ( + obj.snowflake_semantic_view_qualified_name + ) + attrs.snowflake_semantic_view_name = obj.snowflake_semantic_view_name + attrs.snowflake_semantic_table_primary_keys = ( + obj.snowflake_semantic_table_primary_keys + ) + attrs.snowflake_semantic_table_unique_keys = ( + obj.snowflake_semantic_table_unique_keys + ) + attrs.snowflake_semantic_table_distinct_ranges = ( + obj.snowflake_semantic_table_distinct_ranges + ) + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + attrs.semantic_expression = obj.semantic_expression + attrs.semantic_type = obj.semantic_type + attrs.semantic_synonyms = obj.semantic_synonyms + attrs.semantic_sample_values = obj.semantic_sample_values + attrs.semantic_access_modifier = obj.semantic_access_modifier + attrs.semantic_data_type = obj.semantic_data_type + attrs.semantic_labels = obj.semantic_labels + + +def _extract_snowflake_semantic_logical_table_attrs( + attrs: SnowflakeSemanticLogicalTableAttributes, +) -> dict: + """Extract all SnowflakeSemanticLogicalTable attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["snowflake_semantic_view_qualified_name"] = ( + attrs.snowflake_semantic_view_qualified_name + ) + result["snowflake_semantic_view_name"] = attrs.snowflake_semantic_view_name + result["snowflake_semantic_table_primary_keys"] = ( + attrs.snowflake_semantic_table_primary_keys + ) + result["snowflake_semantic_table_unique_keys"] = ( + attrs.snowflake_semantic_table_unique_keys + ) + result["snowflake_semantic_table_distinct_ranges"] = ( + attrs.snowflake_semantic_table_distinct_ranges + ) + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + result["semantic_expression"] = attrs.semantic_expression + result["semantic_type"] = attrs.semantic_type + result["semantic_synonyms"] = attrs.semantic_synonyms + result["semantic_sample_values"] = attrs.semantic_sample_values + result["semantic_access_modifier"] = attrs.semantic_access_modifier + result["semantic_data_type"] = attrs.semantic_data_type + result["semantic_labels"] = attrs.semantic_labels + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _snowflake_semantic_logical_table_to_nested( + snowflake_semantic_logical_table: SnowflakeSemanticLogicalTable, +) -> SnowflakeSemanticLogicalTableNested: + """Convert flat SnowflakeSemanticLogicalTable to nested format.""" + attrs = SnowflakeSemanticLogicalTableAttributes() + _populate_snowflake_semantic_logical_table_attrs( + attrs, snowflake_semantic_logical_table + ) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + snowflake_semantic_logical_table, + _SNOWFLAKE_SEMANTIC_LOGICAL_TABLE_REL_FIELDS, + SnowflakeSemanticLogicalTableRelationshipAttributes, + ) + return SnowflakeSemanticLogicalTableNested( + guid=snowflake_semantic_logical_table.guid, + type_name=snowflake_semantic_logical_table.type_name, + status=snowflake_semantic_logical_table.status, + version=snowflake_semantic_logical_table.version, + create_time=snowflake_semantic_logical_table.create_time, + update_time=snowflake_semantic_logical_table.update_time, + created_by=snowflake_semantic_logical_table.created_by, + updated_by=snowflake_semantic_logical_table.updated_by, + classifications=snowflake_semantic_logical_table.classifications, + classification_names=snowflake_semantic_logical_table.classification_names, + meanings=snowflake_semantic_logical_table.meanings, + labels=snowflake_semantic_logical_table.labels, + business_attributes=snowflake_semantic_logical_table.business_attributes, + custom_attributes=snowflake_semantic_logical_table.custom_attributes, + pending_tasks=snowflake_semantic_logical_table.pending_tasks, + proxy=snowflake_semantic_logical_table.proxy, + is_incomplete=snowflake_semantic_logical_table.is_incomplete, + provenance_type=snowflake_semantic_logical_table.provenance_type, + home_id=snowflake_semantic_logical_table.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _snowflake_semantic_logical_table_from_nested( + nested: SnowflakeSemanticLogicalTableNested, +) -> SnowflakeSemanticLogicalTable: + """Convert nested format to flat SnowflakeSemanticLogicalTable.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else SnowflakeSemanticLogicalTableAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SNOWFLAKE_SEMANTIC_LOGICAL_TABLE_REL_FIELDS, + SnowflakeSemanticLogicalTableRelationshipAttributes, + ) + return SnowflakeSemanticLogicalTable( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_snowflake_semantic_logical_table_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _snowflake_semantic_logical_table_to_nested_bytes( + snowflake_semantic_logical_table: SnowflakeSemanticLogicalTable, serde: Serde +) -> bytes: + """Convert flat SnowflakeSemanticLogicalTable to nested JSON bytes.""" + return serde.encode( + _snowflake_semantic_logical_table_to_nested(snowflake_semantic_logical_table) + ) + + +def _snowflake_semantic_logical_table_from_nested_bytes( + data: bytes, serde: Serde +) -> SnowflakeSemanticLogicalTable: + """Convert nested JSON bytes to flat SnowflakeSemanticLogicalTable.""" + nested = serde.decode(data, SnowflakeSemanticLogicalTableNested) + return _snowflake_semantic_logical_table_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, + TextField, +) + +SnowflakeSemanticLogicalTable.SNOWFLAKE_SEMANTIC_VIEW_QUALIFIED_NAME = KeywordField( + "snowflakeSemanticViewQualifiedName", "snowflakeSemanticViewQualifiedName" +) +SnowflakeSemanticLogicalTable.SNOWFLAKE_SEMANTIC_VIEW_NAME = KeywordField( + "snowflakeSemanticViewName", "snowflakeSemanticViewName" +) +SnowflakeSemanticLogicalTable.SNOWFLAKE_SEMANTIC_TABLE_PRIMARY_KEYS = KeywordField( + "snowflakeSemanticTablePrimaryKeys", "snowflakeSemanticTablePrimaryKeys" +) +SnowflakeSemanticLogicalTable.SNOWFLAKE_SEMANTIC_TABLE_UNIQUE_KEYS = KeywordField( + "snowflakeSemanticTableUniqueKeys", "snowflakeSemanticTableUniqueKeys" +) +SnowflakeSemanticLogicalTable.SNOWFLAKE_SEMANTIC_TABLE_DISTINCT_RANGES = KeywordField( + "snowflakeSemanticTableDistinctRanges", "snowflakeSemanticTableDistinctRanges" +) +SnowflakeSemanticLogicalTable.QUERY_COUNT = NumericField("queryCount", "queryCount") +SnowflakeSemanticLogicalTable.QUERY_USER_COUNT = NumericField( + "queryUserCount", "queryUserCount" +) +SnowflakeSemanticLogicalTable.QUERY_USER_MAP = KeywordField( + "queryUserMap", "queryUserMap" +) +SnowflakeSemanticLogicalTable.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +SnowflakeSemanticLogicalTable.DATABASE_NAME = KeywordField( + "databaseName", "databaseName" +) +SnowflakeSemanticLogicalTable.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +SnowflakeSemanticLogicalTable.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +SnowflakeSemanticLogicalTable.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +SnowflakeSemanticLogicalTable.TABLE_NAME = KeywordField("tableName", "tableName") +SnowflakeSemanticLogicalTable.TABLE_QUALIFIED_NAME = KeywordField( + "tableQualifiedName", "tableQualifiedName" +) +SnowflakeSemanticLogicalTable.VIEW_NAME = KeywordField("viewName", "viewName") +SnowflakeSemanticLogicalTable.VIEW_QUALIFIED_NAME = KeywordField( + "viewQualifiedName", "viewQualifiedName" +) +SnowflakeSemanticLogicalTable.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +SnowflakeSemanticLogicalTable.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +SnowflakeSemanticLogicalTable.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +SnowflakeSemanticLogicalTable.LAST_PROFILED_AT = NumericField( + "lastProfiledAt", "lastProfiledAt" +) +SnowflakeSemanticLogicalTable.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +SnowflakeSemanticLogicalTable.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +SnowflakeSemanticLogicalTable.SEMANTIC_EXPRESSION = KeywordField( + "semanticExpression", "semanticExpression" +) +SnowflakeSemanticLogicalTable.SEMANTIC_TYPE = KeywordField( + "semanticType", "semanticType" +) +SnowflakeSemanticLogicalTable.SEMANTIC_SYNONYMS = KeywordField( + "semanticSynonyms", "semanticSynonyms" +) +SnowflakeSemanticLogicalTable.SEMANTIC_SAMPLE_VALUES = TextField( + "semanticSampleValues", "semanticSampleValues" +) +SnowflakeSemanticLogicalTable.SEMANTIC_ACCESS_MODIFIER = KeywordField( + "semanticAccessModifier", "semanticAccessModifier" +) +SnowflakeSemanticLogicalTable.SEMANTIC_DATA_TYPE = KeywordField( + "semanticDataType", "semanticDataType" +) +SnowflakeSemanticLogicalTable.SEMANTIC_LABELS = KeywordField( + "semanticLabels", "semanticLabels" +) +SnowflakeSemanticLogicalTable.INPUT_TO_AIRFLOW_TASKS = RelationField( + "inputToAirflowTasks" +) +SnowflakeSemanticLogicalTable.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +SnowflakeSemanticLogicalTable.ANOMALO_CHECKS = RelationField("anomaloChecks") +SnowflakeSemanticLogicalTable.APPLICATION = RelationField("application") +SnowflakeSemanticLogicalTable.APPLICATION_FIELD = RelationField("applicationField") +SnowflakeSemanticLogicalTable.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +SnowflakeSemanticLogicalTable.INPUT_PORT_DATA_PRODUCTS = RelationField( + "inputPortDataProducts" +) +SnowflakeSemanticLogicalTable.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +SnowflakeSemanticLogicalTable.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +SnowflakeSemanticLogicalTable.METRICS = RelationField("metrics") +SnowflakeSemanticLogicalTable.DQ_BASE_DATASET_RULES = RelationField( + "dqBaseDatasetRules" +) +SnowflakeSemanticLogicalTable.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +SnowflakeSemanticLogicalTable.DBT_MODELS = RelationField("dbtModels") +SnowflakeSemanticLogicalTable.SQL_DBT_MODELS = RelationField("sqlDbtModels") +SnowflakeSemanticLogicalTable.DBT_TESTS = RelationField("dbtTests") +SnowflakeSemanticLogicalTable.DBT_SOURCES = RelationField("dbtSources") +SnowflakeSemanticLogicalTable.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +SnowflakeSemanticLogicalTable.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +SnowflakeSemanticLogicalTable.MEANINGS = RelationField("meanings") +SnowflakeSemanticLogicalTable.MC_MONITORS = RelationField("mcMonitors") +SnowflakeSemanticLogicalTable.MC_INCIDENTS = RelationField("mcIncidents") +SnowflakeSemanticLogicalTable.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SnowflakeSemanticLogicalTable.PARTIAL_CHILD_OBJECTS = RelationField( + "partialChildObjects" +) +SnowflakeSemanticLogicalTable.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SnowflakeSemanticLogicalTable.OUTPUT_FROM_PROCESSES = RelationField( + "outputFromProcesses" +) +SnowflakeSemanticLogicalTable.USER_DEF_RELATIONSHIP_TO = RelationField( + "userDefRelationshipTo" +) +SnowflakeSemanticLogicalTable.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +SnowflakeSemanticLogicalTable.FILES = RelationField("files") +SnowflakeSemanticLogicalTable.LINKS = RelationField("links") +SnowflakeSemanticLogicalTable.README = RelationField("readme") +SnowflakeSemanticLogicalTable.SCHEMA_REGISTRY_SUBJECTS = RelationField( + "schemaRegistrySubjects" +) +SnowflakeSemanticLogicalTable.SEMANTIC_MODEL = RelationField("semanticModel") +SnowflakeSemanticLogicalTable.SNOWFLAKE_SEMANTIC_VIEW = RelationField( + "snowflakeSemanticView" +) +SnowflakeSemanticLogicalTable.SNOWFLAKE_SEMANTIC_FACTS = RelationField( + "snowflakeSemanticFacts" +) +SnowflakeSemanticLogicalTable.SNOWFLAKE_SEMANTIC_DIMENSIONS = RelationField( + "snowflakeSemanticDimensions" +) +SnowflakeSemanticLogicalTable.SNOWFLAKE_SEMANTIC_METRICS = RelationField( + "snowflakeSemanticMetrics" +) +SnowflakeSemanticLogicalTable.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +SnowflakeSemanticLogicalTable.SNOWFLAKE_BASE_TABLE = RelationField("snowflakeBaseTable") +SnowflakeSemanticLogicalTable.SNOWFLAKE_SEMANTIC_LOGICAL_TABLE_JOINS = RelationField( + "snowflakeSemanticLogicalTableJoins" +) +SnowflakeSemanticLogicalTable.SODA_CHECKS = RelationField("sodaChecks") +SnowflakeSemanticLogicalTable.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SnowflakeSemanticLogicalTable.OUTPUT_FROM_SPARK_JOBS = RelationField( + "outputFromSparkJobs" +) diff --git a/pyatlan_v9/model/assets/snowflake_semantic_metric.py b/pyatlan_v9/model/assets/snowflake_semantic_metric.py new file mode 100644 index 000000000..66f3f094a --- /dev/null +++ b/pyatlan_v9/model/assets/snowflake_semantic_metric.py @@ -0,0 +1,1072 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SnowflakeSemanticMetric asset model with flattened inheritance. + +This module provides: +- SnowflakeSemanticMetric: Flat asset class (easy to use) +- SnowflakeSemanticMetricAttributes: Nested attributes struct (extends AssetAttributes) +- SnowflakeSemanticMetricNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .asset_related import RelatedAsset +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from .sql_related import RelatedColumn +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .snowflake_related import RelatedSnowflakeSemanticLogicalTable + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SnowflakeSemanticMetric(Asset): + """ + Instance of a Snowflake semantic metric in Atlan. + """ + + SNOWFLAKE_SEMANTIC_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_VIEW_NAME: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_TABLE_QUALIFIED_NAME: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_TABLE_NAME: ClassVar[Any] = None + SNOWFLAKE_METRIC_ADDITIVE_DIMENSIONS: ClassVar[Any] = None + SNOWFLAKE_METRIC_NON_ADDITIVE_DIMENSIONS: ClassVar[Any] = None + SNOWFLAKE_METRIC_USING_RELATIONSHIPS: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + METRIC_TYPE: ClassVar[Any] = None + METRIC_SQL: ClassVar[Any] = None + METRIC_FILTERS: ClassVar[Any] = None + METRIC_TIME_GRAINS: ClassVar[Any] = None + DQ_IS_PART_OF_CONTRACT: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + ASSETS: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + METRIC_TIMESTAMP_COLUMN: ClassVar[Any] = None + METRIC_DIMENSION_COLUMNS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLE: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SnowflakeSemanticMetric" + + snowflake_semantic_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the semantic view in which this metric exists.""" + + snowflake_semantic_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the semantic view in which this metric exists.""" + + snowflake_semantic_table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the logical table in which this metric exists.""" + + snowflake_semantic_table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the logical table in which this metric exists.""" + + snowflake_metric_additive_dimensions: Union[List[str], None, UnsetType] = UNSET + """Dimensions over which the metric can be additively aggregated.""" + + snowflake_metric_non_additive_dimensions: Union[List[str], None, UnsetType] = UNSET + """Dimensions over which the metric cannot be additively aggregated.""" + + snowflake_metric_using_relationships: Union[List[str], None, UnsetType] = UNSET + """Relationships used by the metric for cross-table computation.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + metric_type: Union[str, None, UnsetType] = UNSET + """Type of the metric.""" + + metric_sql: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="metricSQL" + ) + """SQL query used to compute the metric.""" + + metric_filters: Union[str, None, UnsetType] = UNSET + """Filters to be applied to the metric query.""" + + metric_time_grains: Union[List[str], None, UnsetType] = UNSET + """List of time grains to be applied to the metric query.""" + + dq_is_part_of_contract: Union[bool, None, UnsetType] = UNSET + """Whether this data quality is part of contract (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + assets: Union[List[RelatedAsset], None, UnsetType] = UNSET + """""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + metric_timestamp_column: Union[RelatedColumn, None, UnsetType] = UNSET + """""" + + metric_dimension_columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_table: Union[ + RelatedSnowflakeSemanticLogicalTable, None, UnsetType + ] = UNSET + """Logical table containing the metric.""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SnowflakeSemanticMetric" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _snowflake_semantic_metric_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> SnowflakeSemanticMetric: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SnowflakeSemanticMetric instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _snowflake_semantic_metric_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SnowflakeSemanticMetricAttributes(AssetAttributes): + """SnowflakeSemanticMetric-specific attributes for nested API format.""" + + snowflake_semantic_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the semantic view in which this metric exists.""" + + snowflake_semantic_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the semantic view in which this metric exists.""" + + snowflake_semantic_table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the logical table in which this metric exists.""" + + snowflake_semantic_table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the logical table in which this metric exists.""" + + snowflake_metric_additive_dimensions: Union[List[str], None, UnsetType] = UNSET + """Dimensions over which the metric can be additively aggregated.""" + + snowflake_metric_non_additive_dimensions: Union[List[str], None, UnsetType] = UNSET + """Dimensions over which the metric cannot be additively aggregated.""" + + snowflake_metric_using_relationships: Union[List[str], None, UnsetType] = UNSET + """Relationships used by the metric for cross-table computation.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + metric_type: Union[str, None, UnsetType] = UNSET + """Type of the metric.""" + + metric_sql: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="metricSQL" + ) + """SQL query used to compute the metric.""" + + metric_filters: Union[str, None, UnsetType] = UNSET + """Filters to be applied to the metric query.""" + + metric_time_grains: Union[List[str], None, UnsetType] = UNSET + """List of time grains to be applied to the metric query.""" + + dq_is_part_of_contract: Union[bool, None, UnsetType] = UNSET + """Whether this data quality is part of contract (true) or not (false).""" + + +class SnowflakeSemanticMetricRelationshipAttributes(AssetRelationshipAttributes): + """SnowflakeSemanticMetric-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + assets: Union[List[RelatedAsset], None, UnsetType] = UNSET + """""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + metric_timestamp_column: Union[RelatedColumn, None, UnsetType] = UNSET + """""" + + metric_dimension_columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_table: Union[ + RelatedSnowflakeSemanticLogicalTable, None, UnsetType + ] = UNSET + """Logical table containing the metric.""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SnowflakeSemanticMetricNested(AssetNested): + """SnowflakeSemanticMetric in nested API format for high-performance serialization.""" + + attributes: Union[SnowflakeSemanticMetricAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + SnowflakeSemanticMetricRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + SnowflakeSemanticMetricRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SnowflakeSemanticMetricRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SNOWFLAKE_SEMANTIC_METRIC_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "assets", + "metrics", + "metric_timestamp_column", + "metric_dimension_columns", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "snowflake_semantic_logical_table", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_snowflake_semantic_metric_attrs( + attrs: SnowflakeSemanticMetricAttributes, obj: SnowflakeSemanticMetric +) -> None: + """Populate SnowflakeSemanticMetric-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.snowflake_semantic_view_qualified_name = ( + obj.snowflake_semantic_view_qualified_name + ) + attrs.snowflake_semantic_view_name = obj.snowflake_semantic_view_name + attrs.snowflake_semantic_table_qualified_name = ( + obj.snowflake_semantic_table_qualified_name + ) + attrs.snowflake_semantic_table_name = obj.snowflake_semantic_table_name + attrs.snowflake_metric_additive_dimensions = ( + obj.snowflake_metric_additive_dimensions + ) + attrs.snowflake_metric_non_additive_dimensions = ( + obj.snowflake_metric_non_additive_dimensions + ) + attrs.snowflake_metric_using_relationships = ( + obj.snowflake_metric_using_relationships + ) + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + attrs.metric_type = obj.metric_type + attrs.metric_sql = obj.metric_sql + attrs.metric_filters = obj.metric_filters + attrs.metric_time_grains = obj.metric_time_grains + attrs.dq_is_part_of_contract = obj.dq_is_part_of_contract + + +def _extract_snowflake_semantic_metric_attrs( + attrs: SnowflakeSemanticMetricAttributes, +) -> dict: + """Extract all SnowflakeSemanticMetric attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["snowflake_semantic_view_qualified_name"] = ( + attrs.snowflake_semantic_view_qualified_name + ) + result["snowflake_semantic_view_name"] = attrs.snowflake_semantic_view_name + result["snowflake_semantic_table_qualified_name"] = ( + attrs.snowflake_semantic_table_qualified_name + ) + result["snowflake_semantic_table_name"] = attrs.snowflake_semantic_table_name + result["snowflake_metric_additive_dimensions"] = ( + attrs.snowflake_metric_additive_dimensions + ) + result["snowflake_metric_non_additive_dimensions"] = ( + attrs.snowflake_metric_non_additive_dimensions + ) + result["snowflake_metric_using_relationships"] = ( + attrs.snowflake_metric_using_relationships + ) + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + result["metric_type"] = attrs.metric_type + result["metric_sql"] = attrs.metric_sql + result["metric_filters"] = attrs.metric_filters + result["metric_time_grains"] = attrs.metric_time_grains + result["dq_is_part_of_contract"] = attrs.dq_is_part_of_contract + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _snowflake_semantic_metric_to_nested( + snowflake_semantic_metric: SnowflakeSemanticMetric, +) -> SnowflakeSemanticMetricNested: + """Convert flat SnowflakeSemanticMetric to nested format.""" + attrs = SnowflakeSemanticMetricAttributes() + _populate_snowflake_semantic_metric_attrs(attrs, snowflake_semantic_metric) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + snowflake_semantic_metric, + _SNOWFLAKE_SEMANTIC_METRIC_REL_FIELDS, + SnowflakeSemanticMetricRelationshipAttributes, + ) + return SnowflakeSemanticMetricNested( + guid=snowflake_semantic_metric.guid, + type_name=snowflake_semantic_metric.type_name, + status=snowflake_semantic_metric.status, + version=snowflake_semantic_metric.version, + create_time=snowflake_semantic_metric.create_time, + update_time=snowflake_semantic_metric.update_time, + created_by=snowflake_semantic_metric.created_by, + updated_by=snowflake_semantic_metric.updated_by, + classifications=snowflake_semantic_metric.classifications, + classification_names=snowflake_semantic_metric.classification_names, + meanings=snowflake_semantic_metric.meanings, + labels=snowflake_semantic_metric.labels, + business_attributes=snowflake_semantic_metric.business_attributes, + custom_attributes=snowflake_semantic_metric.custom_attributes, + pending_tasks=snowflake_semantic_metric.pending_tasks, + proxy=snowflake_semantic_metric.proxy, + is_incomplete=snowflake_semantic_metric.is_incomplete, + provenance_type=snowflake_semantic_metric.provenance_type, + home_id=snowflake_semantic_metric.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _snowflake_semantic_metric_from_nested( + nested: SnowflakeSemanticMetricNested, +) -> SnowflakeSemanticMetric: + """Convert nested format to flat SnowflakeSemanticMetric.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else SnowflakeSemanticMetricAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SNOWFLAKE_SEMANTIC_METRIC_REL_FIELDS, + SnowflakeSemanticMetricRelationshipAttributes, + ) + return SnowflakeSemanticMetric( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_snowflake_semantic_metric_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _snowflake_semantic_metric_to_nested_bytes( + snowflake_semantic_metric: SnowflakeSemanticMetric, serde: Serde +) -> bytes: + """Convert flat SnowflakeSemanticMetric to nested JSON bytes.""" + return serde.encode(_snowflake_semantic_metric_to_nested(snowflake_semantic_metric)) + + +def _snowflake_semantic_metric_from_nested_bytes( + data: bytes, serde: Serde +) -> SnowflakeSemanticMetric: + """Convert nested JSON bytes to flat SnowflakeSemanticMetric.""" + nested = serde.decode(data, SnowflakeSemanticMetricNested) + return _snowflake_semantic_metric_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, +) + +SnowflakeSemanticMetric.SNOWFLAKE_SEMANTIC_VIEW_QUALIFIED_NAME = KeywordField( + "snowflakeSemanticViewQualifiedName", "snowflakeSemanticViewQualifiedName" +) +SnowflakeSemanticMetric.SNOWFLAKE_SEMANTIC_VIEW_NAME = KeywordField( + "snowflakeSemanticViewName", "snowflakeSemanticViewName" +) +SnowflakeSemanticMetric.SNOWFLAKE_SEMANTIC_TABLE_QUALIFIED_NAME = KeywordField( + "snowflakeSemanticTableQualifiedName", "snowflakeSemanticTableQualifiedName" +) +SnowflakeSemanticMetric.SNOWFLAKE_SEMANTIC_TABLE_NAME = KeywordField( + "snowflakeSemanticTableName", "snowflakeSemanticTableName" +) +SnowflakeSemanticMetric.SNOWFLAKE_METRIC_ADDITIVE_DIMENSIONS = KeywordField( + "snowflakeMetricAdditiveDimensions", "snowflakeMetricAdditiveDimensions" +) +SnowflakeSemanticMetric.SNOWFLAKE_METRIC_NON_ADDITIVE_DIMENSIONS = KeywordField( + "snowflakeMetricNonAdditiveDimensions", "snowflakeMetricNonAdditiveDimensions" +) +SnowflakeSemanticMetric.SNOWFLAKE_METRIC_USING_RELATIONSHIPS = KeywordField( + "snowflakeMetricUsingRelationships", "snowflakeMetricUsingRelationships" +) +SnowflakeSemanticMetric.QUERY_COUNT = NumericField("queryCount", "queryCount") +SnowflakeSemanticMetric.QUERY_USER_COUNT = NumericField( + "queryUserCount", "queryUserCount" +) +SnowflakeSemanticMetric.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +SnowflakeSemanticMetric.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +SnowflakeSemanticMetric.DATABASE_NAME = KeywordField("databaseName", "databaseName") +SnowflakeSemanticMetric.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +SnowflakeSemanticMetric.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +SnowflakeSemanticMetric.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +SnowflakeSemanticMetric.TABLE_NAME = KeywordField("tableName", "tableName") +SnowflakeSemanticMetric.TABLE_QUALIFIED_NAME = KeywordField( + "tableQualifiedName", "tableQualifiedName" +) +SnowflakeSemanticMetric.VIEW_NAME = KeywordField("viewName", "viewName") +SnowflakeSemanticMetric.VIEW_QUALIFIED_NAME = KeywordField( + "viewQualifiedName", "viewQualifiedName" +) +SnowflakeSemanticMetric.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +SnowflakeSemanticMetric.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +SnowflakeSemanticMetric.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +SnowflakeSemanticMetric.LAST_PROFILED_AT = NumericField( + "lastProfiledAt", "lastProfiledAt" +) +SnowflakeSemanticMetric.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +SnowflakeSemanticMetric.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +SnowflakeSemanticMetric.METRIC_TYPE = KeywordField("metricType", "metricType") +SnowflakeSemanticMetric.METRIC_SQL = KeywordField("metricSQL", "metricSQL") +SnowflakeSemanticMetric.METRIC_FILTERS = KeywordField("metricFilters", "metricFilters") +SnowflakeSemanticMetric.METRIC_TIME_GRAINS = KeywordField( + "metricTimeGrains", "metricTimeGrains" +) +SnowflakeSemanticMetric.DQ_IS_PART_OF_CONTRACT = BooleanField( + "dqIsPartOfContract", "dqIsPartOfContract" +) +SnowflakeSemanticMetric.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SnowflakeSemanticMetric.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +SnowflakeSemanticMetric.ANOMALO_CHECKS = RelationField("anomaloChecks") +SnowflakeSemanticMetric.APPLICATION = RelationField("application") +SnowflakeSemanticMetric.APPLICATION_FIELD = RelationField("applicationField") +SnowflakeSemanticMetric.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +SnowflakeSemanticMetric.INPUT_PORT_DATA_PRODUCTS = RelationField( + "inputPortDataProducts" +) +SnowflakeSemanticMetric.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +SnowflakeSemanticMetric.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +SnowflakeSemanticMetric.ASSETS = RelationField("assets") +SnowflakeSemanticMetric.METRICS = RelationField("metrics") +SnowflakeSemanticMetric.METRIC_TIMESTAMP_COLUMN = RelationField("metricTimestampColumn") +SnowflakeSemanticMetric.METRIC_DIMENSION_COLUMNS = RelationField( + "metricDimensionColumns" +) +SnowflakeSemanticMetric.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SnowflakeSemanticMetric.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +SnowflakeSemanticMetric.DBT_MODELS = RelationField("dbtModels") +SnowflakeSemanticMetric.SQL_DBT_MODELS = RelationField("sqlDbtModels") +SnowflakeSemanticMetric.DBT_TESTS = RelationField("dbtTests") +SnowflakeSemanticMetric.DBT_SOURCES = RelationField("dbtSources") +SnowflakeSemanticMetric.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +SnowflakeSemanticMetric.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +SnowflakeSemanticMetric.MEANINGS = RelationField("meanings") +SnowflakeSemanticMetric.MC_MONITORS = RelationField("mcMonitors") +SnowflakeSemanticMetric.MC_INCIDENTS = RelationField("mcIncidents") +SnowflakeSemanticMetric.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SnowflakeSemanticMetric.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SnowflakeSemanticMetric.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SnowflakeSemanticMetric.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SnowflakeSemanticMetric.USER_DEF_RELATIONSHIP_TO = RelationField( + "userDefRelationshipTo" +) +SnowflakeSemanticMetric.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +SnowflakeSemanticMetric.FILES = RelationField("files") +SnowflakeSemanticMetric.LINKS = RelationField("links") +SnowflakeSemanticMetric.README = RelationField("readme") +SnowflakeSemanticMetric.SCHEMA_REGISTRY_SUBJECTS = RelationField( + "schemaRegistrySubjects" +) +SnowflakeSemanticMetric.SNOWFLAKE_SEMANTIC_LOGICAL_TABLE = RelationField( + "snowflakeSemanticLogicalTable" +) +SnowflakeSemanticMetric.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +SnowflakeSemanticMetric.SODA_CHECKS = RelationField("sodaChecks") +SnowflakeSemanticMetric.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SnowflakeSemanticMetric.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/snowflake_semantic_view.py b/pyatlan_v9/model/assets/snowflake_semantic_view.py new file mode 100644 index 000000000..fcb4d6c99 --- /dev/null +++ b/pyatlan_v9/model/assets/snowflake_semantic_view.py @@ -0,0 +1,914 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SnowflakeSemanticView asset model with flattened inheritance. + +This module provides: +- SnowflakeSemanticView: Flat asset class (easy to use) +- SnowflakeSemanticViewAttributes: Nested attributes struct (extends AssetAttributes) +- SnowflakeSemanticViewNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .semantic_related import ( + RelatedSemanticDimension, + RelatedSemanticEntity, + RelatedSemanticMeasure, +) +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from .sql_related import RelatedSchema +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .snowflake_related import RelatedSnowflakeSemanticLogicalTable + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SnowflakeSemanticView(Asset): + """ + Instance of a Snowflake semantic view in Atlan. + """ + + SNOWFLAKE_DEFINITION: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SEMANTIC_DIMENSIONS: ClassVar[Any] = None + SEMANTIC_MEASURES: ClassVar[Any] = None + SEMANTIC_ENTITIES: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_VIEW_SCHEMA: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SnowflakeSemanticView" + + snowflake_definition: Union[str, None, UnsetType] = UNSET + """DDL definition of the semantic view (via GET_DDL).""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + semantic_dimensions: Union[List[RelatedSemanticDimension], None, UnsetType] = UNSET + """Dimensions that exist within this semantic model.""" + + semantic_measures: Union[List[RelatedSemanticMeasure], None, UnsetType] = UNSET + """Measures that exist within this semantic model.""" + + semantic_entities: Union[List[RelatedSemanticEntity], None, UnsetType] = UNSET + """Entities that exist within this semantic model.""" + + snowflake_semantic_view_schema: Union[RelatedSchema, None, UnsetType] = UNSET + """Schema containing the semantic view.""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Logical tables contained in the semantic view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SnowflakeSemanticView" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _snowflake_semantic_view_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> SnowflakeSemanticView: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SnowflakeSemanticView instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _snowflake_semantic_view_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SnowflakeSemanticViewAttributes(AssetAttributes): + """SnowflakeSemanticView-specific attributes for nested API format.""" + + snowflake_definition: Union[str, None, UnsetType] = UNSET + """DDL definition of the semantic view (via GET_DDL).""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + +class SnowflakeSemanticViewRelationshipAttributes(AssetRelationshipAttributes): + """SnowflakeSemanticView-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + semantic_dimensions: Union[List[RelatedSemanticDimension], None, UnsetType] = UNSET + """Dimensions that exist within this semantic model.""" + + semantic_measures: Union[List[RelatedSemanticMeasure], None, UnsetType] = UNSET + """Measures that exist within this semantic model.""" + + semantic_entities: Union[List[RelatedSemanticEntity], None, UnsetType] = UNSET + """Entities that exist within this semantic model.""" + + snowflake_semantic_view_schema: Union[RelatedSchema, None, UnsetType] = UNSET + """Schema containing the semantic view.""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Logical tables contained in the semantic view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SnowflakeSemanticViewNested(AssetNested): + """SnowflakeSemanticView in nested API format for high-performance serialization.""" + + attributes: Union[SnowflakeSemanticViewAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + SnowflakeSemanticViewRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + SnowflakeSemanticViewRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SnowflakeSemanticViewRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SNOWFLAKE_SEMANTIC_VIEW_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "semantic_dimensions", + "semantic_measures", + "semantic_entities", + "snowflake_semantic_view_schema", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_snowflake_semantic_view_attrs( + attrs: SnowflakeSemanticViewAttributes, obj: SnowflakeSemanticView +) -> None: + """Populate SnowflakeSemanticView-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.snowflake_definition = obj.snowflake_definition + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + + +def _extract_snowflake_semantic_view_attrs( + attrs: SnowflakeSemanticViewAttributes, +) -> dict: + """Extract all SnowflakeSemanticView attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["snowflake_definition"] = attrs.snowflake_definition + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _snowflake_semantic_view_to_nested( + snowflake_semantic_view: SnowflakeSemanticView, +) -> SnowflakeSemanticViewNested: + """Convert flat SnowflakeSemanticView to nested format.""" + attrs = SnowflakeSemanticViewAttributes() + _populate_snowflake_semantic_view_attrs(attrs, snowflake_semantic_view) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + snowflake_semantic_view, + _SNOWFLAKE_SEMANTIC_VIEW_REL_FIELDS, + SnowflakeSemanticViewRelationshipAttributes, + ) + return SnowflakeSemanticViewNested( + guid=snowflake_semantic_view.guid, + type_name=snowflake_semantic_view.type_name, + status=snowflake_semantic_view.status, + version=snowflake_semantic_view.version, + create_time=snowflake_semantic_view.create_time, + update_time=snowflake_semantic_view.update_time, + created_by=snowflake_semantic_view.created_by, + updated_by=snowflake_semantic_view.updated_by, + classifications=snowflake_semantic_view.classifications, + classification_names=snowflake_semantic_view.classification_names, + meanings=snowflake_semantic_view.meanings, + labels=snowflake_semantic_view.labels, + business_attributes=snowflake_semantic_view.business_attributes, + custom_attributes=snowflake_semantic_view.custom_attributes, + pending_tasks=snowflake_semantic_view.pending_tasks, + proxy=snowflake_semantic_view.proxy, + is_incomplete=snowflake_semantic_view.is_incomplete, + provenance_type=snowflake_semantic_view.provenance_type, + home_id=snowflake_semantic_view.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _snowflake_semantic_view_from_nested( + nested: SnowflakeSemanticViewNested, +) -> SnowflakeSemanticView: + """Convert nested format to flat SnowflakeSemanticView.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else SnowflakeSemanticViewAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SNOWFLAKE_SEMANTIC_VIEW_REL_FIELDS, + SnowflakeSemanticViewRelationshipAttributes, + ) + return SnowflakeSemanticView( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_snowflake_semantic_view_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _snowflake_semantic_view_to_nested_bytes( + snowflake_semantic_view: SnowflakeSemanticView, serde: Serde +) -> bytes: + """Convert flat SnowflakeSemanticView to nested JSON bytes.""" + return serde.encode(_snowflake_semantic_view_to_nested(snowflake_semantic_view)) + + +def _snowflake_semantic_view_from_nested_bytes( + data: bytes, serde: Serde +) -> SnowflakeSemanticView: + """Convert nested JSON bytes to flat SnowflakeSemanticView.""" + nested = serde.decode(data, SnowflakeSemanticViewNested) + return _snowflake_semantic_view_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, +) + +SnowflakeSemanticView.SNOWFLAKE_DEFINITION = KeywordField( + "snowflakeDefinition", "snowflakeDefinition" +) +SnowflakeSemanticView.QUERY_COUNT = NumericField("queryCount", "queryCount") +SnowflakeSemanticView.QUERY_USER_COUNT = NumericField( + "queryUserCount", "queryUserCount" +) +SnowflakeSemanticView.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +SnowflakeSemanticView.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +SnowflakeSemanticView.DATABASE_NAME = KeywordField("databaseName", "databaseName") +SnowflakeSemanticView.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +SnowflakeSemanticView.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +SnowflakeSemanticView.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +SnowflakeSemanticView.TABLE_NAME = KeywordField("tableName", "tableName") +SnowflakeSemanticView.TABLE_QUALIFIED_NAME = KeywordField( + "tableQualifiedName", "tableQualifiedName" +) +SnowflakeSemanticView.VIEW_NAME = KeywordField("viewName", "viewName") +SnowflakeSemanticView.VIEW_QUALIFIED_NAME = KeywordField( + "viewQualifiedName", "viewQualifiedName" +) +SnowflakeSemanticView.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +SnowflakeSemanticView.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +SnowflakeSemanticView.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +SnowflakeSemanticView.LAST_PROFILED_AT = NumericField( + "lastProfiledAt", "lastProfiledAt" +) +SnowflakeSemanticView.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +SnowflakeSemanticView.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +SnowflakeSemanticView.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SnowflakeSemanticView.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +SnowflakeSemanticView.ANOMALO_CHECKS = RelationField("anomaloChecks") +SnowflakeSemanticView.APPLICATION = RelationField("application") +SnowflakeSemanticView.APPLICATION_FIELD = RelationField("applicationField") +SnowflakeSemanticView.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +SnowflakeSemanticView.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +SnowflakeSemanticView.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +SnowflakeSemanticView.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +SnowflakeSemanticView.METRICS = RelationField("metrics") +SnowflakeSemanticView.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SnowflakeSemanticView.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +SnowflakeSemanticView.DBT_MODELS = RelationField("dbtModels") +SnowflakeSemanticView.SQL_DBT_MODELS = RelationField("sqlDbtModels") +SnowflakeSemanticView.DBT_TESTS = RelationField("dbtTests") +SnowflakeSemanticView.DBT_SOURCES = RelationField("dbtSources") +SnowflakeSemanticView.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +SnowflakeSemanticView.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +SnowflakeSemanticView.MEANINGS = RelationField("meanings") +SnowflakeSemanticView.MC_MONITORS = RelationField("mcMonitors") +SnowflakeSemanticView.MC_INCIDENTS = RelationField("mcIncidents") +SnowflakeSemanticView.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SnowflakeSemanticView.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SnowflakeSemanticView.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SnowflakeSemanticView.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SnowflakeSemanticView.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +SnowflakeSemanticView.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +SnowflakeSemanticView.FILES = RelationField("files") +SnowflakeSemanticView.LINKS = RelationField("links") +SnowflakeSemanticView.README = RelationField("readme") +SnowflakeSemanticView.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +SnowflakeSemanticView.SEMANTIC_DIMENSIONS = RelationField("semanticDimensions") +SnowflakeSemanticView.SEMANTIC_MEASURES = RelationField("semanticMeasures") +SnowflakeSemanticView.SEMANTIC_ENTITIES = RelationField("semanticEntities") +SnowflakeSemanticView.SNOWFLAKE_SEMANTIC_VIEW_SCHEMA = RelationField( + "snowflakeSemanticViewSchema" +) +SnowflakeSemanticView.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +SnowflakeSemanticView.SODA_CHECKS = RelationField("sodaChecks") +SnowflakeSemanticView.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SnowflakeSemanticView.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/soda.py b/pyatlan_v9/model/assets/soda.py new file mode 100644 index 000000000..9d2f15a78 --- /dev/null +++ b/pyatlan_v9/model/assets/soda.py @@ -0,0 +1,532 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Soda asset model with flattened inheritance. + +This module provides: +- Soda: Flat asset class (easy to use) +- SodaAttributes: Nested attributes struct (extends AssetAttributes) +- SodaNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .soda_related import RelatedSodaCheck + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Soda(Asset): + """ + Base class for Soda assets. + """ + + DQ_IS_PART_OF_CONTRACT: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Soda" + + dq_is_part_of_contract: Union[bool, None, UnsetType] = UNSET + """Whether this data quality is part of contract (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Soda" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _soda_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Soda: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Soda instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _soda_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SodaAttributes(AssetAttributes): + """Soda-specific attributes for nested API format.""" + + dq_is_part_of_contract: Union[bool, None, UnsetType] = UNSET + """Whether this data quality is part of contract (true) or not (false).""" + + +class SodaRelationshipAttributes(AssetRelationshipAttributes): + """Soda-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SodaNested(AssetNested): + """Soda in nested API format for high-performance serialization.""" + + attributes: Union[SodaAttributes, UnsetType] = UNSET + relationship_attributes: Union[SodaRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[SodaRelationshipAttributes, UnsetType] = UNSET + remove_relationship_attributes: Union[SodaRelationshipAttributes, UnsetType] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SODA_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_soda_attrs(attrs: SodaAttributes, obj: Soda) -> None: + """Populate Soda-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.dq_is_part_of_contract = obj.dq_is_part_of_contract + + +def _extract_soda_attrs(attrs: SodaAttributes) -> dict: + """Extract all Soda attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["dq_is_part_of_contract"] = attrs.dq_is_part_of_contract + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _soda_to_nested(soda: Soda) -> SodaNested: + """Convert flat Soda to nested format.""" + attrs = SodaAttributes() + _populate_soda_attrs(attrs, soda) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + soda, _SODA_REL_FIELDS, SodaRelationshipAttributes + ) + return SodaNested( + guid=soda.guid, + type_name=soda.type_name, + status=soda.status, + version=soda.version, + create_time=soda.create_time, + update_time=soda.update_time, + created_by=soda.created_by, + updated_by=soda.updated_by, + classifications=soda.classifications, + classification_names=soda.classification_names, + meanings=soda.meanings, + labels=soda.labels, + business_attributes=soda.business_attributes, + custom_attributes=soda.custom_attributes, + pending_tasks=soda.pending_tasks, + proxy=soda.proxy, + is_incomplete=soda.is_incomplete, + provenance_type=soda.provenance_type, + home_id=soda.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _soda_from_nested(nested: SodaNested) -> Soda: + """Convert nested format to flat Soda.""" + attrs = nested.attributes if nested.attributes is not UNSET else SodaAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SODA_REL_FIELDS, + SodaRelationshipAttributes, + ) + return Soda( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_soda_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _soda_to_nested_bytes(soda: Soda, serde: Serde) -> bytes: + """Convert flat Soda to nested JSON bytes.""" + return serde.encode(_soda_to_nested(soda)) + + +def _soda_from_nested_bytes(data: bytes, serde: Serde) -> Soda: + """Convert nested JSON bytes to flat Soda.""" + nested = serde.decode(data, SodaNested) + return _soda_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + RelationField, +) + +Soda.DQ_IS_PART_OF_CONTRACT = BooleanField("dqIsPartOfContract", "dqIsPartOfContract") +Soda.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Soda.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Soda.ANOMALO_CHECKS = RelationField("anomaloChecks") +Soda.APPLICATION = RelationField("application") +Soda.APPLICATION_FIELD = RelationField("applicationField") +Soda.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Soda.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Soda.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Soda.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Soda.METRICS = RelationField("metrics") +Soda.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Soda.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Soda.MEANINGS = RelationField("meanings") +Soda.MC_MONITORS = RelationField("mcMonitors") +Soda.MC_INCIDENTS = RelationField("mcIncidents") +Soda.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Soda.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Soda.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Soda.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Soda.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Soda.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Soda.FILES = RelationField("files") +Soda.LINKS = RelationField("links") +Soda.README = RelationField("readme") +Soda.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Soda.SODA_CHECKS = RelationField("sodaChecks") +Soda.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Soda.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/soda_check.py b/pyatlan_v9/model/assets/soda_check.py new file mode 100644 index 000000000..f098f6bf1 --- /dev/null +++ b/pyatlan_v9/model/assets/soda_check.py @@ -0,0 +1,628 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SodaCheck asset model with flattened inheritance. + +This module provides: +- SodaCheck: Flat asset class (easy to use) +- SodaCheckAttributes: Nested attributes struct (extends AssetAttributes) +- SodaCheckNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .asset_related import RelatedAsset +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .spark_related import RelatedSparkJob +from .sql_related import RelatedColumn +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .soda_related import RelatedSodaCheck + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SodaCheck(Asset): + """ + Instance of a Soda check in Atlan. + """ + + SODA_ID: ClassVar[Any] = None + SODA_EVALUATION_STATUS: ClassVar[Any] = None + SODA_CHECK_DEFINITION: ClassVar[Any] = None + SODA_LAST_SCAN_AT: ClassVar[Any] = None + SODA_INCIDENT_COUNT: ClassVar[Any] = None + SODA_LINKED_ASSET_QUALIFIED_NAME: ClassVar[Any] = None + DQ_IS_PART_OF_CONTRACT: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECK_ASSETS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + SODA_CHECK_COLUMNS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SodaCheck" + + soda_id: Union[str, None, UnsetType] = UNSET + """Identifier of the check in Soda.""" + + soda_evaluation_status: Union[str, None, UnsetType] = UNSET + """Status of the check in Soda.""" + + soda_check_definition: Union[str, None, UnsetType] = UNSET + """Definition of the check in Soda.""" + + soda_last_scan_at: Union[int, None, UnsetType] = UNSET + """""" + + soda_incident_count: Union[int, None, UnsetType] = UNSET + """""" + + soda_linked_asset_qualified_name: Union[str, None, UnsetType] = UNSET + """QualifiedName of the asset associated with the check.""" + + dq_is_part_of_contract: Union[bool, None, UnsetType] = UNSET + """Whether this data quality is part of contract (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_check_assets: Union[List[RelatedAsset], None, UnsetType] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + soda_check_columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SodaCheck" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _soda_check_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> SodaCheck: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SodaCheck instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _soda_check_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SodaCheckAttributes(AssetAttributes): + """SodaCheck-specific attributes for nested API format.""" + + soda_id: Union[str, None, UnsetType] = UNSET + """Identifier of the check in Soda.""" + + soda_evaluation_status: Union[str, None, UnsetType] = UNSET + """Status of the check in Soda.""" + + soda_check_definition: Union[str, None, UnsetType] = UNSET + """Definition of the check in Soda.""" + + soda_last_scan_at: Union[int, None, UnsetType] = UNSET + """""" + + soda_incident_count: Union[int, None, UnsetType] = UNSET + """""" + + soda_linked_asset_qualified_name: Union[str, None, UnsetType] = UNSET + """QualifiedName of the asset associated with the check.""" + + dq_is_part_of_contract: Union[bool, None, UnsetType] = UNSET + """Whether this data quality is part of contract (true) or not (false).""" + + +class SodaCheckRelationshipAttributes(AssetRelationshipAttributes): + """SodaCheck-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_check_assets: Union[List[RelatedAsset], None, UnsetType] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + soda_check_columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SodaCheckNested(AssetNested): + """SodaCheck in nested API format for high-performance serialization.""" + + attributes: Union[SodaCheckAttributes, UnsetType] = UNSET + relationship_attributes: Union[SodaCheckRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + SodaCheckRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SodaCheckRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SODA_CHECK_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_check_assets", + "soda_checks", + "soda_check_columns", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_soda_check_attrs(attrs: SodaCheckAttributes, obj: SodaCheck) -> None: + """Populate SodaCheck-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.soda_id = obj.soda_id + attrs.soda_evaluation_status = obj.soda_evaluation_status + attrs.soda_check_definition = obj.soda_check_definition + attrs.soda_last_scan_at = obj.soda_last_scan_at + attrs.soda_incident_count = obj.soda_incident_count + attrs.soda_linked_asset_qualified_name = obj.soda_linked_asset_qualified_name + attrs.dq_is_part_of_contract = obj.dq_is_part_of_contract + + +def _extract_soda_check_attrs(attrs: SodaCheckAttributes) -> dict: + """Extract all SodaCheck attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["soda_id"] = attrs.soda_id + result["soda_evaluation_status"] = attrs.soda_evaluation_status + result["soda_check_definition"] = attrs.soda_check_definition + result["soda_last_scan_at"] = attrs.soda_last_scan_at + result["soda_incident_count"] = attrs.soda_incident_count + result["soda_linked_asset_qualified_name"] = attrs.soda_linked_asset_qualified_name + result["dq_is_part_of_contract"] = attrs.dq_is_part_of_contract + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _soda_check_to_nested(soda_check: SodaCheck) -> SodaCheckNested: + """Convert flat SodaCheck to nested format.""" + attrs = SodaCheckAttributes() + _populate_soda_check_attrs(attrs, soda_check) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + soda_check, _SODA_CHECK_REL_FIELDS, SodaCheckRelationshipAttributes + ) + return SodaCheckNested( + guid=soda_check.guid, + type_name=soda_check.type_name, + status=soda_check.status, + version=soda_check.version, + create_time=soda_check.create_time, + update_time=soda_check.update_time, + created_by=soda_check.created_by, + updated_by=soda_check.updated_by, + classifications=soda_check.classifications, + classification_names=soda_check.classification_names, + meanings=soda_check.meanings, + labels=soda_check.labels, + business_attributes=soda_check.business_attributes, + custom_attributes=soda_check.custom_attributes, + pending_tasks=soda_check.pending_tasks, + proxy=soda_check.proxy, + is_incomplete=soda_check.is_incomplete, + provenance_type=soda_check.provenance_type, + home_id=soda_check.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _soda_check_from_nested(nested: SodaCheckNested) -> SodaCheck: + """Convert nested format to flat SodaCheck.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else SodaCheckAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SODA_CHECK_REL_FIELDS, + SodaCheckRelationshipAttributes, + ) + return SodaCheck( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_soda_check_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _soda_check_to_nested_bytes(soda_check: SodaCheck, serde: Serde) -> bytes: + """Convert flat SodaCheck to nested JSON bytes.""" + return serde.encode(_soda_check_to_nested(soda_check)) + + +def _soda_check_from_nested_bytes(data: bytes, serde: Serde) -> SodaCheck: + """Convert nested JSON bytes to flat SodaCheck.""" + nested = serde.decode(data, SodaCheckNested) + return _soda_check_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, +) + +SodaCheck.SODA_ID = KeywordField("sodaId", "sodaId") +SodaCheck.SODA_EVALUATION_STATUS = KeywordField( + "sodaEvaluationStatus", "sodaEvaluationStatus" +) +SodaCheck.SODA_CHECK_DEFINITION = KeywordField( + "sodaCheckDefinition", "sodaCheckDefinition" +) +SodaCheck.SODA_LAST_SCAN_AT = NumericField("sodaLastScanAt", "sodaLastScanAt") +SodaCheck.SODA_INCIDENT_COUNT = NumericField("sodaIncidentCount", "sodaIncidentCount") +SodaCheck.SODA_LINKED_ASSET_QUALIFIED_NAME = KeywordField( + "sodaLinkedAssetQualifiedName", "sodaLinkedAssetQualifiedName" +) +SodaCheck.DQ_IS_PART_OF_CONTRACT = BooleanField( + "dqIsPartOfContract", "dqIsPartOfContract" +) +SodaCheck.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SodaCheck.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +SodaCheck.ANOMALO_CHECKS = RelationField("anomaloChecks") +SodaCheck.APPLICATION = RelationField("application") +SodaCheck.APPLICATION_FIELD = RelationField("applicationField") +SodaCheck.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +SodaCheck.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +SodaCheck.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +SodaCheck.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +SodaCheck.METRICS = RelationField("metrics") +SodaCheck.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SodaCheck.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +SodaCheck.MEANINGS = RelationField("meanings") +SodaCheck.MC_MONITORS = RelationField("mcMonitors") +SodaCheck.MC_INCIDENTS = RelationField("mcIncidents") +SodaCheck.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SodaCheck.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SodaCheck.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SodaCheck.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SodaCheck.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +SodaCheck.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +SodaCheck.FILES = RelationField("files") +SodaCheck.LINKS = RelationField("links") +SodaCheck.README = RelationField("readme") +SodaCheck.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +SodaCheck.SODA_CHECK_ASSETS = RelationField("sodaCheckAssets") +SodaCheck.SODA_CHECKS = RelationField("sodaChecks") +SodaCheck.SODA_CHECK_COLUMNS = RelationField("sodaCheckColumns") +SodaCheck.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SodaCheck.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/soda_related.py b/pyatlan_v9/model/assets/soda_related.py new file mode 100644 index 000000000..43979f158 --- /dev/null +++ b/pyatlan_v9/model/assets/soda_related.py @@ -0,0 +1,72 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Soda module. + +This module contains all Related{Type} classes for the Soda type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Union + +from msgspec import UNSET, UnsetType + +from .data_quality_related import RelatedDataQuality +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedSoda", + "RelatedSodaCheck", +] + + +class RelatedSoda(RelatedDataQuality): + """ + Related entity reference for Soda assets. + + Extends RelatedDataQuality with Soda-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Soda" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Soda" + + +class RelatedSodaCheck(RelatedSoda): + """ + Related entity reference for SodaCheck assets. + + Extends RelatedSoda with SodaCheck-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SodaCheck" so it serializes correctly + + soda_id: Union[str, None, UnsetType] = UNSET + """Identifier of the check in Soda.""" + + soda_evaluation_status: Union[str, None, UnsetType] = UNSET + """Status of the check in Soda.""" + + soda_check_definition: Union[str, None, UnsetType] = UNSET + """Definition of the check in Soda.""" + + soda_last_scan_at: Union[int, None, UnsetType] = UNSET + """""" + + soda_incident_count: Union[int, None, UnsetType] = UNSET + """""" + + soda_linked_asset_qualified_name: Union[str, None, UnsetType] = UNSET + """QualifiedName of the asset associated with the check.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SodaCheck" diff --git a/pyatlan_v9/model/assets/source_tag.py b/pyatlan_v9/model/assets/source_tag.py new file mode 100644 index 000000000..9e8302d36 --- /dev/null +++ b/pyatlan_v9/model/assets/source_tag.py @@ -0,0 +1,584 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SourceTag asset model with flattened inheritance. + +This module provides: +- SourceTag: Flat asset class (easy to use) +- SourceTagAttributes: Nested attributes struct (extends AssetAttributes) +- SourceTagNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SourceTag(Asset): + """ + Instance of a source system-imported tag in Atlan. + """ + + TAG_CUSTOM_CONFIGURATION: ClassVar[Any] = None + TAG_ID: ClassVar[Any] = None + TAG_ATTRIBUTES: ClassVar[Any] = None + TAG_ALLOWED_VALUES: ClassVar[Any] = None + MAPPED_CLASSIFICATION_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SourceTag" + + tag_custom_configuration: Union[str, None, UnsetType] = UNSET + """Specifies custom configuration elements based on the system the tag is being imported from.""" + + tag_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the tag in the source system.""" + + tag_attributes: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """Attributes associated with the tag in the source system.""" + + tag_allowed_values: Union[List[str], None, UnsetType] = UNSET + """Allowed values for the tag in the source system. These are denormalized from tagAttributes for ease of querying.""" + + mapped_classification_name: Union[str, None, UnsetType] = UNSET + """Name of the classification in Atlan that is mapped to this tag.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SourceTag" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _source_tag_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> SourceTag: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SourceTag instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _source_tag_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SourceTagAttributes(AssetAttributes): + """SourceTag-specific attributes for nested API format.""" + + tag_custom_configuration: Union[str, None, UnsetType] = UNSET + """Specifies custom configuration elements based on the system the tag is being imported from.""" + + tag_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the tag in the source system.""" + + tag_attributes: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """Attributes associated with the tag in the source system.""" + + tag_allowed_values: Union[List[str], None, UnsetType] = UNSET + """Allowed values for the tag in the source system. These are denormalized from tagAttributes for ease of querying.""" + + mapped_classification_name: Union[str, None, UnsetType] = UNSET + """Name of the classification in Atlan that is mapped to this tag.""" + + +class SourceTagRelationshipAttributes(AssetRelationshipAttributes): + """SourceTag-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SourceTagNested(AssetNested): + """SourceTag in nested API format for high-performance serialization.""" + + attributes: Union[SourceTagAttributes, UnsetType] = UNSET + relationship_attributes: Union[SourceTagRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + SourceTagRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SourceTagRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SOURCE_TAG_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_source_tag_attrs(attrs: SourceTagAttributes, obj: SourceTag) -> None: + """Populate SourceTag-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.tag_custom_configuration = obj.tag_custom_configuration + attrs.tag_id = obj.tag_id + attrs.tag_attributes = obj.tag_attributes + attrs.tag_allowed_values = obj.tag_allowed_values + attrs.mapped_classification_name = obj.mapped_classification_name + + +def _extract_source_tag_attrs(attrs: SourceTagAttributes) -> dict: + """Extract all SourceTag attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["tag_custom_configuration"] = attrs.tag_custom_configuration + result["tag_id"] = attrs.tag_id + result["tag_attributes"] = attrs.tag_attributes + result["tag_allowed_values"] = attrs.tag_allowed_values + result["mapped_classification_name"] = attrs.mapped_classification_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _source_tag_to_nested(source_tag: SourceTag) -> SourceTagNested: + """Convert flat SourceTag to nested format.""" + attrs = SourceTagAttributes() + _populate_source_tag_attrs(attrs, source_tag) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + source_tag, _SOURCE_TAG_REL_FIELDS, SourceTagRelationshipAttributes + ) + return SourceTagNested( + guid=source_tag.guid, + type_name=source_tag.type_name, + status=source_tag.status, + version=source_tag.version, + create_time=source_tag.create_time, + update_time=source_tag.update_time, + created_by=source_tag.created_by, + updated_by=source_tag.updated_by, + classifications=source_tag.classifications, + classification_names=source_tag.classification_names, + meanings=source_tag.meanings, + labels=source_tag.labels, + business_attributes=source_tag.business_attributes, + custom_attributes=source_tag.custom_attributes, + pending_tasks=source_tag.pending_tasks, + proxy=source_tag.proxy, + is_incomplete=source_tag.is_incomplete, + provenance_type=source_tag.provenance_type, + home_id=source_tag.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _source_tag_from_nested(nested: SourceTagNested) -> SourceTag: + """Convert nested format to flat SourceTag.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else SourceTagAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SOURCE_TAG_REL_FIELDS, + SourceTagRelationshipAttributes, + ) + return SourceTag( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_source_tag_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _source_tag_to_nested_bytes(source_tag: SourceTag, serde: Serde) -> bytes: + """Convert flat SourceTag to nested JSON bytes.""" + return serde.encode(_source_tag_to_nested(source_tag)) + + +def _source_tag_from_nested_bytes(data: bytes, serde: Serde) -> SourceTag: + """Convert nested JSON bytes to flat SourceTag.""" + nested = serde.decode(data, SourceTagNested) + return _source_tag_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + RelationField, +) + +SourceTag.TAG_CUSTOM_CONFIGURATION = KeywordField( + "tagCustomConfiguration", "tagCustomConfiguration" +) +SourceTag.TAG_ID = KeywordField("tagId", "tagId") +SourceTag.TAG_ATTRIBUTES = KeywordField("tagAttributes", "tagAttributes") +SourceTag.TAG_ALLOWED_VALUES = KeywordTextField( + "tagAllowedValues", "tagAllowedValues", "tagAllowedValues.text" +) +SourceTag.MAPPED_CLASSIFICATION_NAME = KeywordField( + "mappedClassificationName", "mappedClassificationName" +) +SourceTag.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SourceTag.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +SourceTag.ANOMALO_CHECKS = RelationField("anomaloChecks") +SourceTag.APPLICATION = RelationField("application") +SourceTag.APPLICATION_FIELD = RelationField("applicationField") +SourceTag.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +SourceTag.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +SourceTag.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +SourceTag.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +SourceTag.METRICS = RelationField("metrics") +SourceTag.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SourceTag.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +SourceTag.MEANINGS = RelationField("meanings") +SourceTag.MC_MONITORS = RelationField("mcMonitors") +SourceTag.MC_INCIDENTS = RelationField("mcIncidents") +SourceTag.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SourceTag.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SourceTag.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SourceTag.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SourceTag.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +SourceTag.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +SourceTag.FILES = RelationField("files") +SourceTag.LINKS = RelationField("links") +SourceTag.README = RelationField("readme") +SourceTag.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +SourceTag.SODA_CHECKS = RelationField("sodaChecks") +SourceTag.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SourceTag.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/spark.py b/pyatlan_v9/model/assets/spark.py new file mode 100644 index 000000000..8aa45525a --- /dev/null +++ b/pyatlan_v9/model/assets/spark.py @@ -0,0 +1,596 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Spark asset model with flattened inheritance. + +This module provides: +- Spark: Flat asset class (easy to use) +- SparkAttributes: Nested attributes struct (extends AssetAttributes) +- SparkNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflow, RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .spark_related import RelatedSparkJob + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Spark(Asset): + """ + Base class for Spark assets + """ + + SPARK_RUN_VERSION: ClassVar[Any] = None + SPARK_RUN_OPEN_LINEAGE_VERSION: ClassVar[Any] = None + SPARK_RUN_START_TIME: ClassVar[Any] = None + SPARK_RUN_END_TIME: ClassVar[Any] = None + SPARK_RUN_OPEN_LINEAGE_STATE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + SPARK_ORCHESTRATED_BY_AIRFLOW_ASSETS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Spark" + + spark_run_version: Union[str, None, UnsetType] = UNSET + """Spark Version for the Spark Job run eg. 3.4.1""" + + spark_run_open_lineage_version: Union[str, None, UnsetType] = UNSET + """OpenLineage Version of the Spark Job run eg. 1.1.0""" + + spark_run_start_time: Union[int, None, UnsetType] = UNSET + """Start time of the Spark Job eg. 1695673598218""" + + spark_run_end_time: Union[int, None, UnsetType] = UNSET + """End time of the Spark Job eg. 1695673598218""" + + spark_run_open_lineage_state: Union[str, None, UnsetType] = UNSET + """OpenLineage state of the Spark Job run eg. COMPLETE""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + spark_orchestrated_by_airflow_assets: Union[ + List[RelatedAirflow], None, UnsetType + ] = UNSET + """Airflow assets that execute this spark asset.""" + + def __post_init__(self) -> None: + self.type_name = "Spark" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _spark_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Spark: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Spark instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _spark_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SparkAttributes(AssetAttributes): + """Spark-specific attributes for nested API format.""" + + spark_run_version: Union[str, None, UnsetType] = UNSET + """Spark Version for the Spark Job run eg. 3.4.1""" + + spark_run_open_lineage_version: Union[str, None, UnsetType] = UNSET + """OpenLineage Version of the Spark Job run eg. 1.1.0""" + + spark_run_start_time: Union[int, None, UnsetType] = UNSET + """Start time of the Spark Job eg. 1695673598218""" + + spark_run_end_time: Union[int, None, UnsetType] = UNSET + """End time of the Spark Job eg. 1695673598218""" + + spark_run_open_lineage_state: Union[str, None, UnsetType] = UNSET + """OpenLineage state of the Spark Job run eg. COMPLETE""" + + +class SparkRelationshipAttributes(AssetRelationshipAttributes): + """Spark-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + spark_orchestrated_by_airflow_assets: Union[ + List[RelatedAirflow], None, UnsetType + ] = UNSET + """Airflow assets that execute this spark asset.""" + + +class SparkNested(AssetNested): + """Spark in nested API format for high-performance serialization.""" + + attributes: Union[SparkAttributes, UnsetType] = UNSET + relationship_attributes: Union[SparkRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[SparkRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[SparkRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SPARK_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", + "spark_orchestrated_by_airflow_assets", +] + + +def _populate_spark_attrs(attrs: SparkAttributes, obj: Spark) -> None: + """Populate Spark-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.spark_run_version = obj.spark_run_version + attrs.spark_run_open_lineage_version = obj.spark_run_open_lineage_version + attrs.spark_run_start_time = obj.spark_run_start_time + attrs.spark_run_end_time = obj.spark_run_end_time + attrs.spark_run_open_lineage_state = obj.spark_run_open_lineage_state + + +def _extract_spark_attrs(attrs: SparkAttributes) -> dict: + """Extract all Spark attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["spark_run_version"] = attrs.spark_run_version + result["spark_run_open_lineage_version"] = attrs.spark_run_open_lineage_version + result["spark_run_start_time"] = attrs.spark_run_start_time + result["spark_run_end_time"] = attrs.spark_run_end_time + result["spark_run_open_lineage_state"] = attrs.spark_run_open_lineage_state + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _spark_to_nested(spark: Spark) -> SparkNested: + """Convert flat Spark to nested format.""" + attrs = SparkAttributes() + _populate_spark_attrs(attrs, spark) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + spark, _SPARK_REL_FIELDS, SparkRelationshipAttributes + ) + return SparkNested( + guid=spark.guid, + type_name=spark.type_name, + status=spark.status, + version=spark.version, + create_time=spark.create_time, + update_time=spark.update_time, + created_by=spark.created_by, + updated_by=spark.updated_by, + classifications=spark.classifications, + classification_names=spark.classification_names, + meanings=spark.meanings, + labels=spark.labels, + business_attributes=spark.business_attributes, + custom_attributes=spark.custom_attributes, + pending_tasks=spark.pending_tasks, + proxy=spark.proxy, + is_incomplete=spark.is_incomplete, + provenance_type=spark.provenance_type, + home_id=spark.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _spark_from_nested(nested: SparkNested) -> Spark: + """Convert nested format to flat Spark.""" + attrs = nested.attributes if nested.attributes is not UNSET else SparkAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SPARK_REL_FIELDS, + SparkRelationshipAttributes, + ) + return Spark( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_spark_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _spark_to_nested_bytes(spark: Spark, serde: Serde) -> bytes: + """Convert flat Spark to nested JSON bytes.""" + return serde.encode(_spark_to_nested(spark)) + + +def _spark_from_nested_bytes(data: bytes, serde: Serde) -> Spark: + """Convert nested JSON bytes to flat Spark.""" + nested = serde.decode(data, SparkNested) + return _spark_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +Spark.SPARK_RUN_VERSION = KeywordField("sparkRunVersion", "sparkRunVersion") +Spark.SPARK_RUN_OPEN_LINEAGE_VERSION = KeywordField( + "sparkRunOpenLineageVersion", "sparkRunOpenLineageVersion" +) +Spark.SPARK_RUN_START_TIME = NumericField("sparkRunStartTime", "sparkRunStartTime") +Spark.SPARK_RUN_END_TIME = NumericField("sparkRunEndTime", "sparkRunEndTime") +Spark.SPARK_RUN_OPEN_LINEAGE_STATE = KeywordField( + "sparkRunOpenLineageState", "sparkRunOpenLineageState" +) +Spark.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Spark.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Spark.ANOMALO_CHECKS = RelationField("anomaloChecks") +Spark.APPLICATION = RelationField("application") +Spark.APPLICATION_FIELD = RelationField("applicationField") +Spark.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Spark.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Spark.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Spark.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Spark.METRICS = RelationField("metrics") +Spark.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Spark.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Spark.MEANINGS = RelationField("meanings") +Spark.MC_MONITORS = RelationField("mcMonitors") +Spark.MC_INCIDENTS = RelationField("mcIncidents") +Spark.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Spark.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Spark.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Spark.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Spark.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Spark.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Spark.FILES = RelationField("files") +Spark.LINKS = RelationField("links") +Spark.README = RelationField("readme") +Spark.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Spark.SODA_CHECKS = RelationField("sodaChecks") +Spark.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Spark.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") +Spark.SPARK_ORCHESTRATED_BY_AIRFLOW_ASSETS = RelationField( + "sparkOrchestratedByAirflowAssets" +) diff --git a/pyatlan_v9/model/assets/spark_job.py b/pyatlan_v9/model/assets/spark_job.py new file mode 100644 index 000000000..d42f4e2be --- /dev/null +++ b/pyatlan_v9/model/assets/spark_job.py @@ -0,0 +1,656 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SparkJob asset model with flattened inheritance. + +This module provides: +- SparkJob: Flat asset class (easy to use) +- SparkJobAttributes: Nested attributes struct (extends AssetAttributes) +- SparkJobNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflow, RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .catalog_related import RelatedCatalog +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .spark_related import RelatedSparkJob + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SparkJob(Asset): + """ + Instance of a Spark Job run in Atlan. + """ + + SPARK_APP_NAME: ClassVar[Any] = None + SPARK_MASTER: ClassVar[Any] = None + SPARK_RUN_VERSION: ClassVar[Any] = None + SPARK_RUN_OPEN_LINEAGE_VERSION: ClassVar[Any] = None + SPARK_RUN_START_TIME: ClassVar[Any] = None + SPARK_RUN_END_TIME: ClassVar[Any] = None + SPARK_RUN_OPEN_LINEAGE_STATE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + PROCESS: ClassVar[Any] = None + INPUTS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUTS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + SPARK_ORCHESTRATED_BY_AIRFLOW_ASSETS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SparkJob" + + spark_app_name: Union[str, None, UnsetType] = UNSET + """Name of the Spark app containing this Spark Job For eg. extract_raw_data""" + + spark_master: Union[str, None, UnsetType] = UNSET + """The Spark master URL eg. local, local[4], or spark://master:7077""" + + spark_run_version: Union[str, None, UnsetType] = UNSET + """Spark Version for the Spark Job run eg. 3.4.1""" + + spark_run_open_lineage_version: Union[str, None, UnsetType] = UNSET + """OpenLineage Version of the Spark Job run eg. 1.1.0""" + + spark_run_start_time: Union[int, None, UnsetType] = UNSET + """Start time of the Spark Job eg. 1695673598218""" + + spark_run_end_time: Union[int, None, UnsetType] = UNSET + """End time of the Spark Job eg. 1695673598218""" + + spark_run_open_lineage_state: Union[str, None, UnsetType] = UNSET + """OpenLineage state of the Spark Job run eg. COMPLETE""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + process: Union[RelatedProcess, None, UnsetType] = UNSET + """""" + + inputs: Union[List[RelatedCatalog], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + outputs: Union[List[RelatedCatalog], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + spark_orchestrated_by_airflow_assets: Union[ + List[RelatedAirflow], None, UnsetType + ] = UNSET + """Airflow assets that execute this spark asset.""" + + def __post_init__(self) -> None: + self.type_name = "SparkJob" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _spark_job_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> SparkJob: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SparkJob instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _spark_job_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SparkJobAttributes(AssetAttributes): + """SparkJob-specific attributes for nested API format.""" + + spark_app_name: Union[str, None, UnsetType] = UNSET + """Name of the Spark app containing this Spark Job For eg. extract_raw_data""" + + spark_master: Union[str, None, UnsetType] = UNSET + """The Spark master URL eg. local, local[4], or spark://master:7077""" + + spark_run_version: Union[str, None, UnsetType] = UNSET + """Spark Version for the Spark Job run eg. 3.4.1""" + + spark_run_open_lineage_version: Union[str, None, UnsetType] = UNSET + """OpenLineage Version of the Spark Job run eg. 1.1.0""" + + spark_run_start_time: Union[int, None, UnsetType] = UNSET + """Start time of the Spark Job eg. 1695673598218""" + + spark_run_end_time: Union[int, None, UnsetType] = UNSET + """End time of the Spark Job eg. 1695673598218""" + + spark_run_open_lineage_state: Union[str, None, UnsetType] = UNSET + """OpenLineage state of the Spark Job run eg. COMPLETE""" + + +class SparkJobRelationshipAttributes(AssetRelationshipAttributes): + """SparkJob-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + process: Union[RelatedProcess, None, UnsetType] = UNSET + """""" + + inputs: Union[List[RelatedCatalog], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + outputs: Union[List[RelatedCatalog], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + spark_orchestrated_by_airflow_assets: Union[ + List[RelatedAirflow], None, UnsetType + ] = UNSET + """Airflow assets that execute this spark asset.""" + + +class SparkJobNested(AssetNested): + """SparkJob in nested API format for high-performance serialization.""" + + attributes: Union[SparkJobAttributes, UnsetType] = UNSET + relationship_attributes: Union[SparkJobRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[SparkJobRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[SparkJobRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SPARK_JOB_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "process", + "inputs", + "input_to_spark_jobs", + "outputs", + "output_from_spark_jobs", + "spark_orchestrated_by_airflow_assets", +] + + +def _populate_spark_job_attrs(attrs: SparkJobAttributes, obj: SparkJob) -> None: + """Populate SparkJob-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.spark_app_name = obj.spark_app_name + attrs.spark_master = obj.spark_master + attrs.spark_run_version = obj.spark_run_version + attrs.spark_run_open_lineage_version = obj.spark_run_open_lineage_version + attrs.spark_run_start_time = obj.spark_run_start_time + attrs.spark_run_end_time = obj.spark_run_end_time + attrs.spark_run_open_lineage_state = obj.spark_run_open_lineage_state + + +def _extract_spark_job_attrs(attrs: SparkJobAttributes) -> dict: + """Extract all SparkJob attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["spark_app_name"] = attrs.spark_app_name + result["spark_master"] = attrs.spark_master + result["spark_run_version"] = attrs.spark_run_version + result["spark_run_open_lineage_version"] = attrs.spark_run_open_lineage_version + result["spark_run_start_time"] = attrs.spark_run_start_time + result["spark_run_end_time"] = attrs.spark_run_end_time + result["spark_run_open_lineage_state"] = attrs.spark_run_open_lineage_state + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _spark_job_to_nested(spark_job: SparkJob) -> SparkJobNested: + """Convert flat SparkJob to nested format.""" + attrs = SparkJobAttributes() + _populate_spark_job_attrs(attrs, spark_job) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + spark_job, _SPARK_JOB_REL_FIELDS, SparkJobRelationshipAttributes + ) + return SparkJobNested( + guid=spark_job.guid, + type_name=spark_job.type_name, + status=spark_job.status, + version=spark_job.version, + create_time=spark_job.create_time, + update_time=spark_job.update_time, + created_by=spark_job.created_by, + updated_by=spark_job.updated_by, + classifications=spark_job.classifications, + classification_names=spark_job.classification_names, + meanings=spark_job.meanings, + labels=spark_job.labels, + business_attributes=spark_job.business_attributes, + custom_attributes=spark_job.custom_attributes, + pending_tasks=spark_job.pending_tasks, + proxy=spark_job.proxy, + is_incomplete=spark_job.is_incomplete, + provenance_type=spark_job.provenance_type, + home_id=spark_job.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _spark_job_from_nested(nested: SparkJobNested) -> SparkJob: + """Convert nested format to flat SparkJob.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else SparkJobAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SPARK_JOB_REL_FIELDS, + SparkJobRelationshipAttributes, + ) + return SparkJob( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_spark_job_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _spark_job_to_nested_bytes(spark_job: SparkJob, serde: Serde) -> bytes: + """Convert flat SparkJob to nested JSON bytes.""" + return serde.encode(_spark_job_to_nested(spark_job)) + + +def _spark_job_from_nested_bytes(data: bytes, serde: Serde) -> SparkJob: + """Convert nested JSON bytes to flat SparkJob.""" + nested = serde.decode(data, SparkJobNested) + return _spark_job_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +SparkJob.SPARK_APP_NAME = KeywordTextField( + "sparkAppName", "sparkAppName", "sparkAppName.text" +) +SparkJob.SPARK_MASTER = KeywordField("sparkMaster", "sparkMaster") +SparkJob.SPARK_RUN_VERSION = KeywordField("sparkRunVersion", "sparkRunVersion") +SparkJob.SPARK_RUN_OPEN_LINEAGE_VERSION = KeywordField( + "sparkRunOpenLineageVersion", "sparkRunOpenLineageVersion" +) +SparkJob.SPARK_RUN_START_TIME = NumericField("sparkRunStartTime", "sparkRunStartTime") +SparkJob.SPARK_RUN_END_TIME = NumericField("sparkRunEndTime", "sparkRunEndTime") +SparkJob.SPARK_RUN_OPEN_LINEAGE_STATE = KeywordField( + "sparkRunOpenLineageState", "sparkRunOpenLineageState" +) +SparkJob.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SparkJob.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +SparkJob.ANOMALO_CHECKS = RelationField("anomaloChecks") +SparkJob.APPLICATION = RelationField("application") +SparkJob.APPLICATION_FIELD = RelationField("applicationField") +SparkJob.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +SparkJob.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +SparkJob.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +SparkJob.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +SparkJob.METRICS = RelationField("metrics") +SparkJob.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SparkJob.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +SparkJob.MEANINGS = RelationField("meanings") +SparkJob.MC_MONITORS = RelationField("mcMonitors") +SparkJob.MC_INCIDENTS = RelationField("mcIncidents") +SparkJob.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SparkJob.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SparkJob.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SparkJob.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SparkJob.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +SparkJob.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +SparkJob.FILES = RelationField("files") +SparkJob.LINKS = RelationField("links") +SparkJob.README = RelationField("readme") +SparkJob.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +SparkJob.SODA_CHECKS = RelationField("sodaChecks") +SparkJob.PROCESS = RelationField("process") +SparkJob.INPUTS = RelationField("inputs") +SparkJob.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SparkJob.OUTPUTS = RelationField("outputs") +SparkJob.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") +SparkJob.SPARK_ORCHESTRATED_BY_AIRFLOW_ASSETS = RelationField( + "sparkOrchestratedByAirflowAssets" +) diff --git a/pyatlan_v9/model/assets/spark_related.py b/pyatlan_v9/model/assets/spark_related.py new file mode 100644 index 000000000..f00408cd7 --- /dev/null +++ b/pyatlan_v9/model/assets/spark_related.py @@ -0,0 +1,75 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Spark module. + +This module contains all Related{Type} classes for the Spark type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Union + +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedCatalog +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedSpark", + "RelatedSparkJob", +] + + +class RelatedSpark(RelatedCatalog): + """ + Related entity reference for Spark assets. + + Extends RelatedCatalog with Spark-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Spark" so it serializes correctly + + spark_run_version: Union[str, None, UnsetType] = UNSET + """Spark Version for the Spark Job run eg. 3.4.1""" + + spark_run_open_lineage_version: Union[str, None, UnsetType] = UNSET + """OpenLineage Version of the Spark Job run eg. 1.1.0""" + + spark_run_start_time: Union[int, None, UnsetType] = UNSET + """Start time of the Spark Job eg. 1695673598218""" + + spark_run_end_time: Union[int, None, UnsetType] = UNSET + """End time of the Spark Job eg. 1695673598218""" + + spark_run_open_lineage_state: Union[str, None, UnsetType] = UNSET + """OpenLineage state of the Spark Job run eg. COMPLETE""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Spark" + + +class RelatedSparkJob(RelatedSpark): + """ + Related entity reference for SparkJob assets. + + Extends RelatedSpark with SparkJob-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SparkJob" so it serializes correctly + + spark_app_name: Union[str, None, UnsetType] = UNSET + """Name of the Spark app containing this Spark Job For eg. extract_raw_data""" + + spark_master: Union[str, None, UnsetType] = UNSET + """The Spark master URL eg. local, local[4], or spark://master:7077""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SparkJob" diff --git a/pyatlan_v9/model/assets/sql.py b/pyatlan_v9/model/assets/sql.py new file mode 100644 index 000000000..6f8929478 --- /dev/null +++ b/pyatlan_v9/model/assets/sql.py @@ -0,0 +1,794 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +SQL asset model with flattened inheritance. + +This module provides: +- SQL: Flat asset class (easy to use) +- SQLAttributes: Nested attributes struct (extends AssetAttributes) +- SQLNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .snowflake_related import RelatedSnowflakeSemanticLogicalTable +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class SQL(Asset): + """ + Base class for SQL assets. + """ + + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "SQL" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "SQL" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _sql_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> SQL: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + SQL instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _sql_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class SQLAttributes(AssetAttributes): + """SQL-specific attributes for nested API format.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + +class SQLRelationshipAttributes(AssetRelationshipAttributes): + """SQL-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class SQLNested(AssetNested): + """SQL in nested API format for high-performance serialization.""" + + attributes: Union[SQLAttributes, UnsetType] = UNSET + relationship_attributes: Union[SQLRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[SQLRelationshipAttributes, UnsetType] = UNSET + remove_relationship_attributes: Union[SQLRelationshipAttributes, UnsetType] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_SQL_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_sql_attrs(attrs: SQLAttributes, obj: SQL) -> None: + """Populate SQL-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + + +def _extract_sql_attrs(attrs: SQLAttributes) -> dict: + """Extract all SQL attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _sql_to_nested(sql: SQL) -> SQLNested: + """Convert flat SQL to nested format.""" + attrs = SQLAttributes() + _populate_sql_attrs(attrs, sql) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + sql, _SQL_REL_FIELDS, SQLRelationshipAttributes + ) + return SQLNested( + guid=sql.guid, + type_name=sql.type_name, + status=sql.status, + version=sql.version, + create_time=sql.create_time, + update_time=sql.update_time, + created_by=sql.created_by, + updated_by=sql.updated_by, + classifications=sql.classifications, + classification_names=sql.classification_names, + meanings=sql.meanings, + labels=sql.labels, + business_attributes=sql.business_attributes, + custom_attributes=sql.custom_attributes, + pending_tasks=sql.pending_tasks, + proxy=sql.proxy, + is_incomplete=sql.is_incomplete, + provenance_type=sql.provenance_type, + home_id=sql.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _sql_from_nested(nested: SQLNested) -> SQL: + """Convert nested format to flat SQL.""" + attrs = nested.attributes if nested.attributes is not UNSET else SQLAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _SQL_REL_FIELDS, + SQLRelationshipAttributes, + ) + return SQL( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_sql_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _sql_to_nested_bytes(sql: SQL, serde: Serde) -> bytes: + """Convert flat SQL to nested JSON bytes.""" + return serde.encode(_sql_to_nested(sql)) + + +def _sql_from_nested_bytes(data: bytes, serde: Serde) -> SQL: + """Convert nested JSON bytes to flat SQL.""" + nested = serde.decode(data, SQLNested) + return _sql_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, +) + +SQL.QUERY_COUNT = NumericField("queryCount", "queryCount") +SQL.QUERY_USER_COUNT = NumericField("queryUserCount", "queryUserCount") +SQL.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +SQL.QUERY_COUNT_UPDATED_AT = NumericField("queryCountUpdatedAt", "queryCountUpdatedAt") +SQL.DATABASE_NAME = KeywordField("databaseName", "databaseName") +SQL.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +SQL.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +SQL.SCHEMA_QUALIFIED_NAME = KeywordField("schemaQualifiedName", "schemaQualifiedName") +SQL.TABLE_NAME = KeywordField("tableName", "tableName") +SQL.TABLE_QUALIFIED_NAME = KeywordField("tableQualifiedName", "tableQualifiedName") +SQL.VIEW_NAME = KeywordField("viewName", "viewName") +SQL.VIEW_QUALIFIED_NAME = KeywordField("viewQualifiedName", "viewQualifiedName") +SQL.CALCULATION_VIEW_NAME = KeywordField("calculationViewName", "calculationViewName") +SQL.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +SQL.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +SQL.LAST_PROFILED_AT = NumericField("lastProfiledAt", "lastProfiledAt") +SQL.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +SQL.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +SQL.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +SQL.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +SQL.ANOMALO_CHECKS = RelationField("anomaloChecks") +SQL.APPLICATION = RelationField("application") +SQL.APPLICATION_FIELD = RelationField("applicationField") +SQL.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +SQL.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +SQL.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +SQL.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +SQL.METRICS = RelationField("metrics") +SQL.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +SQL.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +SQL.DBT_MODELS = RelationField("dbtModels") +SQL.SQL_DBT_MODELS = RelationField("sqlDbtModels") +SQL.DBT_TESTS = RelationField("dbtTests") +SQL.DBT_SOURCES = RelationField("dbtSources") +SQL.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +SQL.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +SQL.MEANINGS = RelationField("meanings") +SQL.MC_MONITORS = RelationField("mcMonitors") +SQL.MC_INCIDENTS = RelationField("mcIncidents") +SQL.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +SQL.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +SQL.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +SQL.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +SQL.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +SQL.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +SQL.FILES = RelationField("files") +SQL.LINKS = RelationField("links") +SQL.README = RelationField("readme") +SQL.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +SQL.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField("snowflakeSemanticLogicalTables") +SQL.SODA_CHECKS = RelationField("sodaChecks") +SQL.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +SQL.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/sql_related.py b/pyatlan_v9/model/assets/sql_related.py new file mode 100644 index 000000000..99084ed53 --- /dev/null +++ b/pyatlan_v9/model/assets/sql_related.py @@ -0,0 +1,807 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for SQL module. + +This module contains all Related{Type} classes for the SQL type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Any, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedCatalog +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedSQL", + "RelatedCalculationView", + "RelatedColumn", + "RelatedDatabase", + "RelatedFunction", + "RelatedMaterialisedView", + "RelatedProcedure", + "RelatedQuery", + "RelatedSchema", + "RelatedTable", + "RelatedTablePartition", + "RelatedView", +] + + +class RelatedSQL(RelatedCatalog): + """ + Related entity reference for SQL assets. + + Extends RelatedCatalog with SQL-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SQL" so it serializes correctly + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SQL" + + +class RelatedCalculationView(RelatedSQL): + """ + Related entity reference for CalculationView assets. + + Extends RelatedSQL with CalculationView-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "CalculationView" so it serializes correctly + + column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this calculation view.""" + + sql_version_id: Union[int, None, UnsetType] = UNSET + """The version ID of this calculation view.""" + + sql_activated_by: Union[str, None, UnsetType] = UNSET + """The owner who activated the calculation view""" + + sql_activated_at: Union[int, None, UnsetType] = UNSET + """Time at which this calculation view was activated at""" + + sql_package_id: Union[str, None, UnsetType] = UNSET + """The full package id path to which a calculation view belongs/resides in the repository.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "CalculationView" + + +class RelatedColumn(RelatedSQL): + """ + Related entity reference for Column assets. + + Extends RelatedSQL with Column-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Column" so it serializes correctly + + data_type: Union[str, None, UnsetType] = UNSET + """Data type of values in this column.""" + + sub_data_type: Union[str, None, UnsetType] = UNSET + """Sub-data type of this column.""" + + sql_compression: Union[str, None, UnsetType] = UNSET + """Compression type of this column.""" + + sql_encoding: Union[str, None, UnsetType] = UNSET + """Encoding type of this column.""" + + raw_data_type_definition: Union[str, None, UnsetType] = UNSET + """Raw data type definition of this column.""" + + order: Union[int, None, UnsetType] = UNSET + """Order (position) in which this column appears in the table (starting at 1).""" + + nested_column_order: Union[str, None, UnsetType] = UNSET + """Order (position) in which this column appears in the nested Column (nest level starts at 1).""" + + nested_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns nested within this (STRUCT or NESTED) column.""" + + column_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of top-level upstream nested columns.""" + + is_partition: Union[bool, None, UnsetType] = UNSET + """Whether this column is a partition column (true) or not (false).""" + + partition_order: Union[int, None, UnsetType] = UNSET + """Order (position) of this partition column in the table.""" + + is_clustered: Union[bool, None, UnsetType] = UNSET + """Whether this column is a clustered column (true) or not (false).""" + + is_primary: Union[bool, None, UnsetType] = UNSET + """When true, this column is the primary key for the table.""" + + is_foreign: Union[bool, None, UnsetType] = UNSET + """When true, this column is a foreign key to another table. NOTE: this must be true when using the foreignKeyTo relationship to specify columns that refer to this column as a foreign key.""" + + is_indexed: Union[bool, None, UnsetType] = UNSET + """When true, this column is indexed in the database.""" + + is_sort: Union[bool, None, UnsetType] = UNSET + """Whether this column is a sort column (true) or not (false).""" + + is_dist: Union[bool, None, UnsetType] = UNSET + """Whether this column is a distribution column (true) or not (false).""" + + is_pinned: Union[bool, None, UnsetType] = UNSET + """Whether this column is pinned (true) or not (false).""" + + pinned_by: Union[str, None, UnsetType] = UNSET + """User who pinned this column.""" + + pinned_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this column was pinned, in milliseconds.""" + + precision: Union[int, None, UnsetType] = UNSET + """Total number of digits allowed, when the dataType is numeric.""" + + default_value: Union[str, None, UnsetType] = UNSET + """Default value for this column.""" + + is_nullable: Union[bool, None, UnsetType] = UNSET + """When true, the values in this column can be null.""" + + numeric_scale: Union[float, None, UnsetType] = UNSET + """Number of digits allowed to the right of the decimal point.""" + + max_length: Union[int, None, UnsetType] = UNSET + """Maximum length of a value in this column.""" + + validations: Union[Dict[str, str], None, UnsetType] = UNSET + """Validations for this column.""" + + parent_column_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the column this column is nested within, for STRUCT and NESTED columns.""" + + parent_column_name: Union[str, None, UnsetType] = UNSET + """Simple name of the column this column is nested within, for STRUCT and NESTED columns.""" + + sql_distinct_values_count: Union[int, None, UnsetType] = UNSET + """Number of rows that contain distinct values.""" + + sql_distinct_values_count_long: Union[int, None, UnsetType] = UNSET + """Number of rows that contain distinct values.""" + + sql_histogram: Union[Dict[str, Any], None, UnsetType] = UNSET + """List of values in a histogram that represents the contents of this column.""" + + sql_max: Union[float, None, UnsetType] = UNSET + """Greatest value in a numeric column.""" + + sql_min: Union[float, None, UnsetType] = UNSET + """Least value in a numeric column.""" + + sql_mean: Union[float, None, UnsetType] = UNSET + """Arithmetic mean of the values in a numeric column.""" + + sql_sum: Union[float, None, UnsetType] = UNSET + """Calculated sum of the values in a numeric column.""" + + sql_median: Union[float, None, UnsetType] = UNSET + """Calculated median of the values in a numeric column.""" + + sql_standard_deviation: Union[float, None, UnsetType] = UNSET + """Calculated standard deviation of the values in a numeric column.""" + + sql_unique_values_count: Union[int, None, UnsetType] = UNSET + """Number of rows in which a value in this column appears only once.""" + + sql_unique_values_count_long: Union[int, None, UnsetType] = UNSET + """Number of rows in which a value in this column appears only once.""" + + sql_average: Union[float, None, UnsetType] = UNSET + """Average value in this column.""" + + sql_average_length: Union[float, None, UnsetType] = UNSET + """Average length of values in a string column.""" + + sql_duplicate_values_count: Union[int, None, UnsetType] = UNSET + """Number of rows that contain duplicate values.""" + + sql_duplicate_values_count_long: Union[int, None, UnsetType] = UNSET + """Number of rows that contain duplicate values.""" + + sql_maximum_string_length: Union[int, None, UnsetType] = UNSET + """Length of the longest value in a string column.""" + + column_maxs: Union[List[str], None, UnsetType] = UNSET + """List of the greatest values in a column.""" + + sql_minimum_string_length: Union[int, None, UnsetType] = UNSET + """Length of the shortest value in a string column.""" + + column_mins: Union[List[str], None, UnsetType] = UNSET + """List of the least values in a column.""" + + sql_missing_values_count: Union[int, None, UnsetType] = UNSET + """Number of rows in a column that do not contain content.""" + + sql_missing_values_count_long: Union[int, None, UnsetType] = UNSET + """Number of rows in a column that do not contain content.""" + + sql_missing_values_percentage: Union[float, None, UnsetType] = UNSET + """Percentage of rows in a column that do not contain content.""" + + sql_uniqueness_percentage: Union[float, None, UnsetType] = UNSET + """Ratio indicating how unique data in this column is: 0 indicates that all values are the same, 100 indicates that all values in this column are unique.""" + + sql_variance: Union[float, None, UnsetType] = UNSET + """Calculated variance of the values in a numeric column.""" + + column_top_values: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of top values in this column.""" + + sql_max_value: Union[float, None, UnsetType] = UNSET + """Greatest value in a numeric column.""" + + sql_min_value: Union[float, None, UnsetType] = UNSET + """Least value in a numeric column.""" + + sql_mean_value: Union[float, None, UnsetType] = UNSET + """Arithmetic mean of the values in a numeric column.""" + + sql_sum_value: Union[float, None, UnsetType] = UNSET + """Calculated sum of the values in a numeric column.""" + + sql_median_value: Union[float, None, UnsetType] = UNSET + """Calculated median of the values in a numeric column.""" + + sql_standard_deviation_value: Union[float, None, UnsetType] = UNSET + """Calculated standard deviation of the values in a numeric column.""" + + sql_average_value: Union[float, None, UnsetType] = UNSET + """Average value in this column.""" + + sql_variance_value: Union[float, None, UnsetType] = UNSET + """Calculated variance of the values in a numeric column.""" + + sql_average_length_value: Union[float, None, UnsetType] = UNSET + """Average length of values in a string column.""" + + sql_distribution_histogram: Union[Dict[str, Any], None, UnsetType] = UNSET + """Detailed information representing a histogram of values for a column.""" + + sql_depth_level: Union[int, None, UnsetType] = UNSET + """Level of nesting of this column, used for STRUCT and NESTED columns.""" + + nosql_collection_name: Union[str, None, UnsetType] = UNSET + """Simple name of the cosmos/mongo collection in which this SQL asset (column) exists, or empty if it does not exist within a cosmos/mongo collection.""" + + nosql_collection_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the cosmos/mongo collection in which this SQL asset (column) exists, or empty if it does not exist within a cosmos/mongo collection.""" + + sql_is_measure: Union[bool, None, UnsetType] = UNSET + """When true, this column is of type measure/calculated.""" + + sql_measure_type: Union[str, None, UnsetType] = UNSET + """The type of measure/calculated column this is, eg: base, calculated, derived.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Column" + + +class RelatedDatabase(RelatedSQL): + """ + Related entity reference for Database assets. + + Extends RelatedSQL with Database-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Database" so it serializes correctly + + schema_count: Union[int, None, UnsetType] = UNSET + """Number of schemas in this database.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Database" + + +class RelatedFunction(RelatedSQL): + """ + Related entity reference for Function assets. + + Extends RelatedSQL with Function-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Function" so it serializes correctly + + function_definition: Union[str, None, UnsetType] = UNSET + """Code or set of statements that determine the output of the function.""" + + sql_return_type: Union[str, None, UnsetType] = UNSET + """Data type of the value returned by the function.""" + + sql_arguments: Union[List[str], None, UnsetType] = UNSET + """Arguments that are passed in to the function.""" + + sql_language: Union[str, None, UnsetType] = UNSET + """Programming language in which the function is written.""" + + sql_type: Union[str, None, UnsetType] = UNSET + """Type of function.""" + + sql_is_external: Union[bool, None, UnsetType] = UNSET + """Whether the function is stored or executed externally (true) or internally (false).""" + + sql_is_dmf: Union[bool, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlIsDMF" + ) + """Whether the function is a data metric function.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether sensitive information of the function is omitted for unauthorized users (true) or not (false).""" + + sql_is_memoizable: Union[bool, None, UnsetType] = UNSET + """Whether the function must re-compute if there are no underlying changes in the values (false) or not (true).""" + + sql_runtime_version: Union[str, None, UnsetType] = UNSET + """Version of the language runtime used by the function.""" + + sql_external_access_integrations: Union[str, None, UnsetType] = UNSET + """Names of external access integrations used by the function.""" + + sql_secrets: Union[str, None, UnsetType] = UNSET + """Secret variables used by the function.""" + + sql_packages: Union[str, None, UnsetType] = UNSET + """Packages requested by the function.""" + + sql_installed_packages: Union[str, None, UnsetType] = UNSET + """Packages actually installed for the function.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Function" + + +class RelatedMaterialisedView(RelatedSQL): + """ + Related entity reference for MaterialisedView assets. + + Extends RelatedSQL with MaterialisedView-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "MaterialisedView" so it serializes correctly + + refresh_mode: Union[str, None, UnsetType] = UNSET + """Refresh mode for this materialized view.""" + + refresh_method: Union[str, None, UnsetType] = UNSET + """Refresh method for this materialized view.""" + + staleness: Union[str, None, UnsetType] = UNSET + """Staleness of this materialized view.""" + + stale_since_date: Union[int, None, UnsetType] = UNSET + """Time (epoch) from which this materialized view is stale, in milliseconds.""" + + column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this materialized view.""" + + row_count: Union[int, None, UnsetType] = UNSET + """Number of rows in this materialized view.""" + + size_bytes: Union[int, None, UnsetType] = UNSET + """Size of this materialized view, in bytes.""" + + is_query_preview: Union[bool, None, UnsetType] = UNSET + """Whether it's possible to run a preview query on this materialized view (true) or not (false).""" + + query_preview_config: Union[Dict[str, str], None, UnsetType] = UNSET + """Configuration for the query preview of this materialized view.""" + + alias: Union[str, None, UnsetType] = UNSET + """Alias for this materialized view.""" + + is_temporary: Union[bool, None, UnsetType] = UNSET + """Whether this materialized view is temporary (true) or not (false).""" + + definition: Union[str, None, UnsetType] = UNSET + """SQL definition of this materialized view.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "MaterialisedView" + + +class RelatedProcedure(RelatedSQL): + """ + Related entity reference for Procedure assets. + + Extends RelatedSQL with Procedure-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Procedure" so it serializes correctly + + definition: Union[str, None, UnsetType] = UNSET + """SQL definition of the procedure.""" + + sql_language: Union[str, None, UnsetType] = UNSET + """Programming language used for the procedure (e.g., SQL, JavaScript, Python, Scala).""" + + sql_runtime_version: Union[str, None, UnsetType] = UNSET + """Version of the language runtime used by the procedure.""" + + sql_owner_role_type: Union[str, None, UnsetType] = UNSET + """Type of role that owns the procedure.""" + + sql_arguments: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of procedure arguments with name and type information.""" + + sql_procedure_return: Union[Dict[str, Any], None, UnsetType] = UNSET + """Detailed information about the procedure's return type.""" + + sql_external_access_integrations: Union[str, None, UnsetType] = UNSET + """Names of external access integrations used by the procedure.""" + + sql_secrets: Union[str, None, UnsetType] = UNSET + """Secret variables used by the procedure.""" + + sql_packages: Union[str, None, UnsetType] = UNSET + """Packages requested by the procedure.""" + + sql_installed_packages: Union[str, None, UnsetType] = UNSET + """Packages actually installed for the procedure.""" + + sql_schema_id: Union[str, None, UnsetType] = UNSET + """Internal ID for the schema containing the procedure.""" + + sql_catalog_id: Union[str, None, UnsetType] = UNSET + """Internal ID for the database containing the procedure.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Procedure" + + +class RelatedQuery(RelatedSQL): + """ + Related entity reference for Query assets. + + Extends RelatedSQL with Query-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Query" so it serializes correctly + + raw_query: Union[str, None, UnsetType] = UNSET + """Deprecated. See 'longRawQuery' instead.""" + + long_raw_query: Union[str, None, UnsetType] = UNSET + """Raw SQL query string.""" + + raw_query_text: Union[str, None, UnsetType] = UNSET + """""" + + default_schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the default schema to use for this query.""" + + default_database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the default database to use for this query.""" + + variables_schema_base64: Union[str, None, UnsetType] = UNSET + """Base64-encoded string of the variables to use in this query.""" + + is_private: Union[bool, None, UnsetType] = UNSET + """Whether this query is private (true) or shared (false).""" + + is_sql_snippet: Union[bool, None, UnsetType] = UNSET + """Whether this query is a SQL snippet (true) or not (false).""" + + parent_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the parent collection or folder in which this query exists.""" + + collection_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the collection in which this query exists.""" + + is_visual_query: Union[bool, None, UnsetType] = UNSET + """Whether this query is a visual query (true) or not (false).""" + + visual_builder_schema_base64: Union[str, None, UnsetType] = UNSET + """Base64-encoded string for the visual query builder.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Query" + + +class RelatedSchema(RelatedSQL): + """ + Related entity reference for Schema assets. + + Extends RelatedSQL with Schema-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Schema" so it serializes correctly + + table_count: Union[int, None, UnsetType] = UNSET + """Number of tables in this schema.""" + + sql_external_location: Union[str, None, UnsetType] = UNSET + """External location of this schema, for example: an S3 object location.""" + + views_count: Union[int, None, UnsetType] = UNSET + """Number of views in this schema.""" + + linked_schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the Linked Schema on which this Schema is dependent. This concept is mostly applicable for linked datasets/datasource in Google BigQuery via Analytics Hub Listing""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Schema" + + +class RelatedTable(RelatedSQL): + """ + Related entity reference for Table assets. + + Extends RelatedSQL with Table-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Table" so it serializes correctly + + column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this table.""" + + row_count: Union[int, None, UnsetType] = UNSET + """Number of rows in this table.""" + + size_bytes: Union[int, None, UnsetType] = UNSET + """Size of this table, in bytes.""" + + sql_object_count: Union[int, None, UnsetType] = UNSET + """Number of objects in this table.""" + + alias: Union[str, None, UnsetType] = UNSET + """Alias for this table.""" + + is_temporary: Union[bool, None, UnsetType] = UNSET + """Whether this table is temporary (true) or not (false).""" + + is_query_preview: Union[bool, None, UnsetType] = UNSET + """Whether preview queries are allowed for this table (true) or not (false).""" + + query_preview_config: Union[Dict[str, str], None, UnsetType] = UNSET + """Configuration for preview queries.""" + + external_location: Union[str, None, UnsetType] = UNSET + """External location of this table, for example: an S3 object location.""" + + external_location_region: Union[str, None, UnsetType] = UNSET + """Region of the external location of this table, for example: S3 region.""" + + external_location_format: Union[str, None, UnsetType] = UNSET + """Format of the external location of this table, for example: JSON, CSV, PARQUET, etc.""" + + is_partitioned: Union[bool, None, UnsetType] = UNSET + """Whether this table is partitioned (true) or not (false).""" + + partition_strategy: Union[str, None, UnsetType] = UNSET + """Partition strategy for this table.""" + + partition_count: Union[int, None, UnsetType] = UNSET + """Number of partitions in this table.""" + + table_definition: Union[str, None, UnsetType] = UNSET + """Definition of the table.""" + + partition_list: Union[str, None, UnsetType] = UNSET + """List of partitions in this table.""" + + is_sharded: Union[bool, None, UnsetType] = UNSET + """Whether this table is a sharded table (true) or not (false).""" + + sql_type: Union[str, None, UnsetType] = UNSET + """Type of the table.""" + + iceberg_catalog_name: Union[str, None, UnsetType] = UNSET + """Iceberg table catalog name (can be any user defined name)""" + + iceberg_table_type: Union[str, None, UnsetType] = UNSET + """Iceberg table type (managed vs unmanaged)""" + + iceberg_catalog_source: Union[str, None, UnsetType] = UNSET + """Iceberg table catalog type (glue, polaris, snowflake)""" + + iceberg_catalog_table_name: Union[str, None, UnsetType] = UNSET + """Catalog table name (actual table name on the catalog side).""" + + sql_impala_parameters: Union[Dict[str, str], None, UnsetType] = UNSET + """Extra attributes for Impala""" + + iceberg_catalog_table_namespace: Union[str, None, UnsetType] = UNSET + """Catalog table namespace (actual database name on the catalog side).""" + + sql_external_volume_name: Union[str, None, UnsetType] = UNSET + """External volume name for the table.""" + + iceberg_table_base_location: Union[str, None, UnsetType] = UNSET + """Iceberg table base location inside the external volume.""" + + sql_retention_time: Union[int, None, UnsetType] = UNSET + """Data retention time in days.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Table" + + +class RelatedTablePartition(RelatedSQL): + """ + Related entity reference for TablePartition assets. + + Extends RelatedSQL with TablePartition-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "TablePartition" so it serializes correctly + + constraint: Union[str, None, UnsetType] = UNSET + """Constraint that defines this table partition.""" + + column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this partition.""" + + row_count: Union[int, None, UnsetType] = UNSET + """Number of rows in this partition.""" + + size_bytes: Union[int, None, UnsetType] = UNSET + """Size of this partition, in bytes.""" + + alias: Union[str, None, UnsetType] = UNSET + """Alias for this partition.""" + + is_temporary: Union[bool, None, UnsetType] = UNSET + """Whether this partition is temporary (true) or not (false).""" + + is_query_preview: Union[bool, None, UnsetType] = UNSET + """Whether preview queries for this partition are allowed (true) or not (false).""" + + query_preview_config: Union[Dict[str, str], None, UnsetType] = UNSET + """Configuration for the preview queries.""" + + external_location: Union[str, None, UnsetType] = UNSET + """External location of this partition, for example: an S3 object location.""" + + external_location_region: Union[str, None, UnsetType] = UNSET + """Region of the external location of this partition, for example: S3 region.""" + + external_location_format: Union[str, None, UnsetType] = UNSET + """Format of the external location of this partition, for example: JSON, CSV, PARQUET, etc.""" + + is_partitioned: Union[bool, None, UnsetType] = UNSET + """Whether this partition is further partitioned (true) or not (false).""" + + partition_strategy: Union[str, None, UnsetType] = UNSET + """Partition strategy of this partition.""" + + partition_count: Union[int, None, UnsetType] = UNSET + """Number of sub-partitions of this partition.""" + + partition_list: Union[str, None, UnsetType] = UNSET + """List of sub-partitions in this partition.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "TablePartition" + + +class RelatedView(RelatedSQL): + """ + Related entity reference for View assets. + + Extends RelatedSQL with View-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "View" so it serializes correctly + + column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this view.""" + + row_count: Union[int, None, UnsetType] = UNSET + """Number of rows in this view.""" + + size_bytes: Union[int, None, UnsetType] = UNSET + """Size of this view, in bytes.""" + + is_query_preview: Union[bool, None, UnsetType] = UNSET + """Whether preview queries are allowed on this view (true) or not (false).""" + + query_preview_config: Union[Dict[str, str], None, UnsetType] = UNSET + """Configuration for preview queries on this view.""" + + alias: Union[str, None, UnsetType] = UNSET + """Alias for this view.""" + + is_temporary: Union[bool, None, UnsetType] = UNSET + """Whether this view is temporary (true) or not (false).""" + + definition: Union[str, None, UnsetType] = UNSET + """SQL definition of this view.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "View" diff --git a/pyatlan_v9/model/assets/starburst.py b/pyatlan_v9/model/assets/starburst.py new file mode 100644 index 000000000..84c768134 --- /dev/null +++ b/pyatlan_v9/model/assets/starburst.py @@ -0,0 +1,846 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Starburst asset model with flattened inheritance. + +This module provides: +- Starburst: Flat asset class (easy to use) +- StarburstAttributes: Nested attributes struct (extends AssetAttributes) +- StarburstNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .snowflake_related import RelatedSnowflakeSemanticLogicalTable +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Starburst(Asset): + """ + Base class for Starburst assets. + """ + + STARBURST_DATA_PRODUCT_NAME: ClassVar[Any] = None + STARBURST_DATASET_QUALIFIED_NAME: ClassVar[Any] = None + STARBURST_DATASET_NAME: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Starburst" + + starburst_data_product_name: Union[str, None, UnsetType] = UNSET + """Name of the Starburst Data Product that contains this asset.""" + + starburst_dataset_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the Starburst Dataset that contains this asset, or this asset's own qualified name if it is a Dataset.""" + + starburst_dataset_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Starburst Dataset that contains this asset, or this asset's own name if it is a Dataset.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Starburst" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _starburst_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Starburst: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Starburst instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _starburst_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class StarburstAttributes(AssetAttributes): + """Starburst-specific attributes for nested API format.""" + + starburst_data_product_name: Union[str, None, UnsetType] = UNSET + """Name of the Starburst Data Product that contains this asset.""" + + starburst_dataset_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the Starburst Dataset that contains this asset, or this asset's own qualified name if it is a Dataset.""" + + starburst_dataset_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Starburst Dataset that contains this asset, or this asset's own name if it is a Dataset.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + +class StarburstRelationshipAttributes(AssetRelationshipAttributes): + """Starburst-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class StarburstNested(AssetNested): + """Starburst in nested API format for high-performance serialization.""" + + attributes: Union[StarburstAttributes, UnsetType] = UNSET + relationship_attributes: Union[StarburstRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + StarburstRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + StarburstRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_STARBURST_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_starburst_attrs(attrs: StarburstAttributes, obj: Starburst) -> None: + """Populate Starburst-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.starburst_data_product_name = obj.starburst_data_product_name + attrs.starburst_dataset_qualified_name = obj.starburst_dataset_qualified_name + attrs.starburst_dataset_name = obj.starburst_dataset_name + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + + +def _extract_starburst_attrs(attrs: StarburstAttributes) -> dict: + """Extract all Starburst attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["starburst_data_product_name"] = attrs.starburst_data_product_name + result["starburst_dataset_qualified_name"] = attrs.starburst_dataset_qualified_name + result["starburst_dataset_name"] = attrs.starburst_dataset_name + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _starburst_to_nested(starburst: Starburst) -> StarburstNested: + """Convert flat Starburst to nested format.""" + attrs = StarburstAttributes() + _populate_starburst_attrs(attrs, starburst) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + starburst, _STARBURST_REL_FIELDS, StarburstRelationshipAttributes + ) + return StarburstNested( + guid=starburst.guid, + type_name=starburst.type_name, + status=starburst.status, + version=starburst.version, + create_time=starburst.create_time, + update_time=starburst.update_time, + created_by=starburst.created_by, + updated_by=starburst.updated_by, + classifications=starburst.classifications, + classification_names=starburst.classification_names, + meanings=starburst.meanings, + labels=starburst.labels, + business_attributes=starburst.business_attributes, + custom_attributes=starburst.custom_attributes, + pending_tasks=starburst.pending_tasks, + proxy=starburst.proxy, + is_incomplete=starburst.is_incomplete, + provenance_type=starburst.provenance_type, + home_id=starburst.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _starburst_from_nested(nested: StarburstNested) -> Starburst: + """Convert nested format to flat Starburst.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else StarburstAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _STARBURST_REL_FIELDS, + StarburstRelationshipAttributes, + ) + return Starburst( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_starburst_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _starburst_to_nested_bytes(starburst: Starburst, serde: Serde) -> bytes: + """Convert flat Starburst to nested JSON bytes.""" + return serde.encode(_starburst_to_nested(starburst)) + + +def _starburst_from_nested_bytes(data: bytes, serde: Serde) -> Starburst: + """Convert nested JSON bytes to flat Starburst.""" + nested = serde.decode(data, StarburstNested) + return _starburst_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, +) + +Starburst.STARBURST_DATA_PRODUCT_NAME = KeywordField( + "starburstDataProductName", "starburstDataProductName" +) +Starburst.STARBURST_DATASET_QUALIFIED_NAME = KeywordField( + "starburstDatasetQualifiedName", "starburstDatasetQualifiedName" +) +Starburst.STARBURST_DATASET_NAME = KeywordField( + "starburstDatasetName", "starburstDatasetName" +) +Starburst.QUERY_COUNT = NumericField("queryCount", "queryCount") +Starburst.QUERY_USER_COUNT = NumericField("queryUserCount", "queryUserCount") +Starburst.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +Starburst.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +Starburst.DATABASE_NAME = KeywordField("databaseName", "databaseName") +Starburst.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +Starburst.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +Starburst.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +Starburst.TABLE_NAME = KeywordField("tableName", "tableName") +Starburst.TABLE_QUALIFIED_NAME = KeywordField( + "tableQualifiedName", "tableQualifiedName" +) +Starburst.VIEW_NAME = KeywordField("viewName", "viewName") +Starburst.VIEW_QUALIFIED_NAME = KeywordField("viewQualifiedName", "viewQualifiedName") +Starburst.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +Starburst.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +Starburst.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +Starburst.LAST_PROFILED_AT = NumericField("lastProfiledAt", "lastProfiledAt") +Starburst.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +Starburst.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +Starburst.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Starburst.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Starburst.ANOMALO_CHECKS = RelationField("anomaloChecks") +Starburst.APPLICATION = RelationField("application") +Starburst.APPLICATION_FIELD = RelationField("applicationField") +Starburst.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Starburst.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Starburst.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Starburst.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Starburst.METRICS = RelationField("metrics") +Starburst.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Starburst.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Starburst.DBT_MODELS = RelationField("dbtModels") +Starburst.SQL_DBT_MODELS = RelationField("sqlDbtModels") +Starburst.DBT_TESTS = RelationField("dbtTests") +Starburst.DBT_SOURCES = RelationField("dbtSources") +Starburst.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +Starburst.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +Starburst.MEANINGS = RelationField("meanings") +Starburst.MC_MONITORS = RelationField("mcMonitors") +Starburst.MC_INCIDENTS = RelationField("mcIncidents") +Starburst.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Starburst.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Starburst.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Starburst.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Starburst.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Starburst.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Starburst.FILES = RelationField("files") +Starburst.LINKS = RelationField("links") +Starburst.README = RelationField("readme") +Starburst.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Starburst.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +Starburst.SODA_CHECKS = RelationField("sodaChecks") +Starburst.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Starburst.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/starburst_dataset.py b/pyatlan_v9/model/assets/starburst_dataset.py new file mode 100644 index 000000000..130db93cb --- /dev/null +++ b/pyatlan_v9/model/assets/starburst_dataset.py @@ -0,0 +1,1292 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +StarburstDataset asset model with flattened inheritance. + +This module provides: +- StarburstDataset: Flat asset class (easy to use) +- StarburstDatasetAttributes: Nested attributes struct (extends AssetAttributes) +- StarburstDatasetNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .snowflake_related import RelatedSnowflakeSemanticLogicalTable +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from .sql_related import ( + RelatedColumn, + RelatedQuery, + RelatedSchema, + RelatedTable, + RelatedTablePartition, +) +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .starburst_related import RelatedStarburstDatasetColumn + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class StarburstDataset(Asset): + """ + Instance of a Starburst dataset in Atlan. A dataset is a view or materialized view published through a Starburst Data Product. + """ + + STARBURST_IS_MATERIALIZED: ClassVar[Any] = None + STARBURST_SQL_QUALIFIED_NAME: ClassVar[Any] = None + STARBURST_VIEW_DEFINITION: ClassVar[Any] = None + STARBURST_DATA_PRODUCT_NAME: ClassVar[Any] = None + STARBURST_DATASET_QUALIFIED_NAME: ClassVar[Any] = None + STARBURST_DATASET_NAME: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + COLUMN_COUNT: ClassVar[Any] = None + ROW_COUNT: ClassVar[Any] = None + SIZE_BYTES: ClassVar[Any] = None + TABLE_OBJECT_COUNT: ClassVar[Any] = None + ALIAS: ClassVar[Any] = None + IS_TEMPORARY: ClassVar[Any] = None + IS_QUERY_PREVIEW: ClassVar[Any] = None + QUERY_PREVIEW_CONFIG: ClassVar[Any] = None + EXTERNAL_LOCATION: ClassVar[Any] = None + EXTERNAL_LOCATION_REGION: ClassVar[Any] = None + EXTERNAL_LOCATION_FORMAT: ClassVar[Any] = None + IS_PARTITIONED: ClassVar[Any] = None + PARTITION_STRATEGY: ClassVar[Any] = None + PARTITION_COUNT: ClassVar[Any] = None + TABLE_DEFINITION: ClassVar[Any] = None + PARTITION_LIST: ClassVar[Any] = None + IS_SHARDED: ClassVar[Any] = None + TABLE_TYPE: ClassVar[Any] = None + ICEBERG_CATALOG_NAME: ClassVar[Any] = None + ICEBERG_TABLE_TYPE: ClassVar[Any] = None + ICEBERG_CATALOG_SOURCE: ClassVar[Any] = None + ICEBERG_CATALOG_TABLE_NAME: ClassVar[Any] = None + TABLE_IMPALA_PARAMETERS: ClassVar[Any] = None + ICEBERG_CATALOG_TABLE_NAMESPACE: ClassVar[Any] = None + TABLE_EXTERNAL_VOLUME_NAME: ClassVar[Any] = None + ICEBERG_TABLE_BASE_LOCATION: ClassVar[Any] = None + TABLE_RETENTION_TIME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + COLUMNS: ClassVar[Any] = None + QUERIES: ClassVar[Any] = None + ATLAN_SCHEMA: ClassVar[Any] = None + DIMENSIONS: ClassVar[Any] = None + FACTS: ClassVar[Any] = None + PARTITIONS: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + STARBURST_DATA_PRODUCT: ClassVar[Any] = None + STARBURST_DATASET_COLUMNS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "StarburstDataset" + + starburst_is_materialized: Union[bool, None, UnsetType] = UNSET + """Whether this dataset is a materialized view.""" + + starburst_sql_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the corresponding SQL View or MaterialisedView. Enables cross-stream lookup between the Data Product perspective and the SQL perspective of the same underlying view.""" + + starburst_view_definition: Union[str, None, UnsetType] = UNSET + """SQL definition of the underlying view or materialized view.""" + + starburst_data_product_name: Union[str, None, UnsetType] = UNSET + """Name of the Starburst Data Product that contains this asset.""" + + starburst_dataset_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the Starburst Dataset that contains this asset, or this asset's own qualified name if it is a Dataset.""" + + starburst_dataset_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Starburst Dataset that contains this asset, or this asset's own name if it is a Dataset.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this table.""" + + row_count: Union[int, None, UnsetType] = UNSET + """Number of rows in this table.""" + + size_bytes: Union[int, None, UnsetType] = UNSET + """Size of this table, in bytes.""" + + table_object_count: Union[int, None, UnsetType] = UNSET + """Number of objects in this table.""" + + alias: Union[str, None, UnsetType] = UNSET + """Alias for this table.""" + + is_temporary: Union[bool, None, UnsetType] = UNSET + """Whether this table is temporary (true) or not (false).""" + + is_query_preview: Union[bool, None, UnsetType] = UNSET + """Whether preview queries are allowed for this table (true) or not (false).""" + + query_preview_config: Union[Dict[str, str], None, UnsetType] = UNSET + """Configuration for preview queries.""" + + external_location: Union[str, None, UnsetType] = UNSET + """External location of this table, for example: an S3 object location.""" + + external_location_region: Union[str, None, UnsetType] = UNSET + """Region of the external location of this table, for example: S3 region.""" + + external_location_format: Union[str, None, UnsetType] = UNSET + """Format of the external location of this table, for example: JSON, CSV, PARQUET, etc.""" + + is_partitioned: Union[bool, None, UnsetType] = UNSET + """Whether this table is partitioned (true) or not (false).""" + + partition_strategy: Union[str, None, UnsetType] = UNSET + """Partition strategy for this table.""" + + partition_count: Union[int, None, UnsetType] = UNSET + """Number of partitions in this table.""" + + table_definition: Union[str, None, UnsetType] = UNSET + """Definition of the table.""" + + partition_list: Union[str, None, UnsetType] = UNSET + """List of partitions in this table.""" + + is_sharded: Union[bool, None, UnsetType] = UNSET + """Whether this table is a sharded table (true) or not (false).""" + + table_type: Union[str, None, UnsetType] = UNSET + """Type of the table.""" + + iceberg_catalog_name: Union[str, None, UnsetType] = UNSET + """Iceberg table catalog name (can be any user defined name)""" + + iceberg_table_type: Union[str, None, UnsetType] = UNSET + """Iceberg table type (managed vs unmanaged)""" + + iceberg_catalog_source: Union[str, None, UnsetType] = UNSET + """Iceberg table catalog type (glue, polaris, snowflake)""" + + iceberg_catalog_table_name: Union[str, None, UnsetType] = UNSET + """Catalog table name (actual table name on the catalog side).""" + + table_impala_parameters: Union[Dict[str, str], None, UnsetType] = UNSET + """Extra attributes for Impala""" + + iceberg_catalog_table_namespace: Union[str, None, UnsetType] = UNSET + """Catalog table namespace (actual database name on the catalog side).""" + + table_external_volume_name: Union[str, None, UnsetType] = UNSET + """External volume name for the table.""" + + iceberg_table_base_location: Union[str, None, UnsetType] = UNSET + """Iceberg table base location inside the external volume.""" + + table_retention_time: Union[int, None, UnsetType] = UNSET + """Data retention time in days.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Columns that exist within this table.""" + + queries: Union[List[RelatedQuery], None, UnsetType] = UNSET + """Queries that access this table.""" + + atlan_schema: Union[RelatedSchema, None, UnsetType] = UNSET + """Schema in which this table exists.""" + + dimensions: Union[List[RelatedTable], None, UnsetType] = UNSET + """""" + + facts: Union[List[RelatedTable], None, UnsetType] = UNSET + """""" + + partitions: Union[List[RelatedTablePartition], None, UnsetType] = UNSET + """Partitions that exist within this table.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + starburst_data_product: Union[RelatedDataProduct, None, UnsetType] = UNSET + """Data product that publishes this dataset.""" + + starburst_dataset_columns: Union[ + List[RelatedStarburstDatasetColumn], None, UnsetType + ] = UNSET + """Columns that exist within this dataset.""" + + def __post_init__(self) -> None: + self.type_name = "StarburstDataset" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _starburst_dataset_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> StarburstDataset: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + StarburstDataset instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _starburst_dataset_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class StarburstDatasetAttributes(AssetAttributes): + """StarburstDataset-specific attributes for nested API format.""" + + starburst_is_materialized: Union[bool, None, UnsetType] = UNSET + """Whether this dataset is a materialized view.""" + + starburst_sql_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the corresponding SQL View or MaterialisedView. Enables cross-stream lookup between the Data Product perspective and the SQL perspective of the same underlying view.""" + + starburst_view_definition: Union[str, None, UnsetType] = UNSET + """SQL definition of the underlying view or materialized view.""" + + starburst_data_product_name: Union[str, None, UnsetType] = UNSET + """Name of the Starburst Data Product that contains this asset.""" + + starburst_dataset_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the Starburst Dataset that contains this asset, or this asset's own qualified name if it is a Dataset.""" + + starburst_dataset_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Starburst Dataset that contains this asset, or this asset's own name if it is a Dataset.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this table.""" + + row_count: Union[int, None, UnsetType] = UNSET + """Number of rows in this table.""" + + size_bytes: Union[int, None, UnsetType] = UNSET + """Size of this table, in bytes.""" + + table_object_count: Union[int, None, UnsetType] = UNSET + """Number of objects in this table.""" + + alias: Union[str, None, UnsetType] = UNSET + """Alias for this table.""" + + is_temporary: Union[bool, None, UnsetType] = UNSET + """Whether this table is temporary (true) or not (false).""" + + is_query_preview: Union[bool, None, UnsetType] = UNSET + """Whether preview queries are allowed for this table (true) or not (false).""" + + query_preview_config: Union[Dict[str, str], None, UnsetType] = UNSET + """Configuration for preview queries.""" + + external_location: Union[str, None, UnsetType] = UNSET + """External location of this table, for example: an S3 object location.""" + + external_location_region: Union[str, None, UnsetType] = UNSET + """Region of the external location of this table, for example: S3 region.""" + + external_location_format: Union[str, None, UnsetType] = UNSET + """Format of the external location of this table, for example: JSON, CSV, PARQUET, etc.""" + + is_partitioned: Union[bool, None, UnsetType] = UNSET + """Whether this table is partitioned (true) or not (false).""" + + partition_strategy: Union[str, None, UnsetType] = UNSET + """Partition strategy for this table.""" + + partition_count: Union[int, None, UnsetType] = UNSET + """Number of partitions in this table.""" + + table_definition: Union[str, None, UnsetType] = UNSET + """Definition of the table.""" + + partition_list: Union[str, None, UnsetType] = UNSET + """List of partitions in this table.""" + + is_sharded: Union[bool, None, UnsetType] = UNSET + """Whether this table is a sharded table (true) or not (false).""" + + table_type: Union[str, None, UnsetType] = UNSET + """Type of the table.""" + + iceberg_catalog_name: Union[str, None, UnsetType] = UNSET + """Iceberg table catalog name (can be any user defined name)""" + + iceberg_table_type: Union[str, None, UnsetType] = UNSET + """Iceberg table type (managed vs unmanaged)""" + + iceberg_catalog_source: Union[str, None, UnsetType] = UNSET + """Iceberg table catalog type (glue, polaris, snowflake)""" + + iceberg_catalog_table_name: Union[str, None, UnsetType] = UNSET + """Catalog table name (actual table name on the catalog side).""" + + table_impala_parameters: Union[Dict[str, str], None, UnsetType] = UNSET + """Extra attributes for Impala""" + + iceberg_catalog_table_namespace: Union[str, None, UnsetType] = UNSET + """Catalog table namespace (actual database name on the catalog side).""" + + table_external_volume_name: Union[str, None, UnsetType] = UNSET + """External volume name for the table.""" + + iceberg_table_base_location: Union[str, None, UnsetType] = UNSET + """Iceberg table base location inside the external volume.""" + + table_retention_time: Union[int, None, UnsetType] = UNSET + """Data retention time in days.""" + + +class StarburstDatasetRelationshipAttributes(AssetRelationshipAttributes): + """StarburstDataset-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Columns that exist within this table.""" + + queries: Union[List[RelatedQuery], None, UnsetType] = UNSET + """Queries that access this table.""" + + atlan_schema: Union[RelatedSchema, None, UnsetType] = UNSET + """Schema in which this table exists.""" + + dimensions: Union[List[RelatedTable], None, UnsetType] = UNSET + """""" + + facts: Union[List[RelatedTable], None, UnsetType] = UNSET + """""" + + partitions: Union[List[RelatedTablePartition], None, UnsetType] = UNSET + """Partitions that exist within this table.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + starburst_data_product: Union[RelatedDataProduct, None, UnsetType] = UNSET + """Data product that publishes this dataset.""" + + starburst_dataset_columns: Union[ + List[RelatedStarburstDatasetColumn], None, UnsetType + ] = UNSET + """Columns that exist within this dataset.""" + + +class StarburstDatasetNested(AssetNested): + """StarburstDataset in nested API format for high-performance serialization.""" + + attributes: Union[StarburstDatasetAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + StarburstDatasetRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + StarburstDatasetRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + StarburstDatasetRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_STARBURST_DATASET_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "columns", + "queries", + "atlan_schema", + "dimensions", + "facts", + "partitions", + "schema_registry_subjects", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", + "starburst_data_product", + "starburst_dataset_columns", +] + + +def _populate_starburst_dataset_attrs( + attrs: StarburstDatasetAttributes, obj: StarburstDataset +) -> None: + """Populate StarburstDataset-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.starburst_is_materialized = obj.starburst_is_materialized + attrs.starburst_sql_qualified_name = obj.starburst_sql_qualified_name + attrs.starburst_view_definition = obj.starburst_view_definition + attrs.starburst_data_product_name = obj.starburst_data_product_name + attrs.starburst_dataset_qualified_name = obj.starburst_dataset_qualified_name + attrs.starburst_dataset_name = obj.starburst_dataset_name + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + attrs.column_count = obj.column_count + attrs.row_count = obj.row_count + attrs.size_bytes = obj.size_bytes + attrs.table_object_count = obj.table_object_count + attrs.alias = obj.alias + attrs.is_temporary = obj.is_temporary + attrs.is_query_preview = obj.is_query_preview + attrs.query_preview_config = obj.query_preview_config + attrs.external_location = obj.external_location + attrs.external_location_region = obj.external_location_region + attrs.external_location_format = obj.external_location_format + attrs.is_partitioned = obj.is_partitioned + attrs.partition_strategy = obj.partition_strategy + attrs.partition_count = obj.partition_count + attrs.table_definition = obj.table_definition + attrs.partition_list = obj.partition_list + attrs.is_sharded = obj.is_sharded + attrs.table_type = obj.table_type + attrs.iceberg_catalog_name = obj.iceberg_catalog_name + attrs.iceberg_table_type = obj.iceberg_table_type + attrs.iceberg_catalog_source = obj.iceberg_catalog_source + attrs.iceberg_catalog_table_name = obj.iceberg_catalog_table_name + attrs.table_impala_parameters = obj.table_impala_parameters + attrs.iceberg_catalog_table_namespace = obj.iceberg_catalog_table_namespace + attrs.table_external_volume_name = obj.table_external_volume_name + attrs.iceberg_table_base_location = obj.iceberg_table_base_location + attrs.table_retention_time = obj.table_retention_time + + +def _extract_starburst_dataset_attrs(attrs: StarburstDatasetAttributes) -> dict: + """Extract all StarburstDataset attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["starburst_is_materialized"] = attrs.starburst_is_materialized + result["starburst_sql_qualified_name"] = attrs.starburst_sql_qualified_name + result["starburst_view_definition"] = attrs.starburst_view_definition + result["starburst_data_product_name"] = attrs.starburst_data_product_name + result["starburst_dataset_qualified_name"] = attrs.starburst_dataset_qualified_name + result["starburst_dataset_name"] = attrs.starburst_dataset_name + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + result["column_count"] = attrs.column_count + result["row_count"] = attrs.row_count + result["size_bytes"] = attrs.size_bytes + result["table_object_count"] = attrs.table_object_count + result["alias"] = attrs.alias + result["is_temporary"] = attrs.is_temporary + result["is_query_preview"] = attrs.is_query_preview + result["query_preview_config"] = attrs.query_preview_config + result["external_location"] = attrs.external_location + result["external_location_region"] = attrs.external_location_region + result["external_location_format"] = attrs.external_location_format + result["is_partitioned"] = attrs.is_partitioned + result["partition_strategy"] = attrs.partition_strategy + result["partition_count"] = attrs.partition_count + result["table_definition"] = attrs.table_definition + result["partition_list"] = attrs.partition_list + result["is_sharded"] = attrs.is_sharded + result["table_type"] = attrs.table_type + result["iceberg_catalog_name"] = attrs.iceberg_catalog_name + result["iceberg_table_type"] = attrs.iceberg_table_type + result["iceberg_catalog_source"] = attrs.iceberg_catalog_source + result["iceberg_catalog_table_name"] = attrs.iceberg_catalog_table_name + result["table_impala_parameters"] = attrs.table_impala_parameters + result["iceberg_catalog_table_namespace"] = attrs.iceberg_catalog_table_namespace + result["table_external_volume_name"] = attrs.table_external_volume_name + result["iceberg_table_base_location"] = attrs.iceberg_table_base_location + result["table_retention_time"] = attrs.table_retention_time + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _starburst_dataset_to_nested( + starburst_dataset: StarburstDataset, +) -> StarburstDatasetNested: + """Convert flat StarburstDataset to nested format.""" + attrs = StarburstDatasetAttributes() + _populate_starburst_dataset_attrs(attrs, starburst_dataset) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + starburst_dataset, + _STARBURST_DATASET_REL_FIELDS, + StarburstDatasetRelationshipAttributes, + ) + return StarburstDatasetNested( + guid=starburst_dataset.guid, + type_name=starburst_dataset.type_name, + status=starburst_dataset.status, + version=starburst_dataset.version, + create_time=starburst_dataset.create_time, + update_time=starburst_dataset.update_time, + created_by=starburst_dataset.created_by, + updated_by=starburst_dataset.updated_by, + classifications=starburst_dataset.classifications, + classification_names=starburst_dataset.classification_names, + meanings=starburst_dataset.meanings, + labels=starburst_dataset.labels, + business_attributes=starburst_dataset.business_attributes, + custom_attributes=starburst_dataset.custom_attributes, + pending_tasks=starburst_dataset.pending_tasks, + proxy=starburst_dataset.proxy, + is_incomplete=starburst_dataset.is_incomplete, + provenance_type=starburst_dataset.provenance_type, + home_id=starburst_dataset.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _starburst_dataset_from_nested(nested: StarburstDatasetNested) -> StarburstDataset: + """Convert nested format to flat StarburstDataset.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else StarburstDatasetAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _STARBURST_DATASET_REL_FIELDS, + StarburstDatasetRelationshipAttributes, + ) + return StarburstDataset( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_starburst_dataset_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _starburst_dataset_to_nested_bytes( + starburst_dataset: StarburstDataset, serde: Serde +) -> bytes: + """Convert flat StarburstDataset to nested JSON bytes.""" + return serde.encode(_starburst_dataset_to_nested(starburst_dataset)) + + +def _starburst_dataset_from_nested_bytes(data: bytes, serde: Serde) -> StarburstDataset: + """Convert nested JSON bytes to flat StarburstDataset.""" + nested = serde.decode(data, StarburstDatasetNested) + return _starburst_dataset_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, +) + +StarburstDataset.STARBURST_IS_MATERIALIZED = BooleanField( + "starburstIsMaterialized", "starburstIsMaterialized" +) +StarburstDataset.STARBURST_SQL_QUALIFIED_NAME = KeywordField( + "starburstSqlQualifiedName", "starburstSqlQualifiedName" +) +StarburstDataset.STARBURST_VIEW_DEFINITION = KeywordField( + "starburstViewDefinition", "starburstViewDefinition" +) +StarburstDataset.STARBURST_DATA_PRODUCT_NAME = KeywordField( + "starburstDataProductName", "starburstDataProductName" +) +StarburstDataset.STARBURST_DATASET_QUALIFIED_NAME = KeywordField( + "starburstDatasetQualifiedName", "starburstDatasetQualifiedName" +) +StarburstDataset.STARBURST_DATASET_NAME = KeywordField( + "starburstDatasetName", "starburstDatasetName" +) +StarburstDataset.QUERY_COUNT = NumericField("queryCount", "queryCount") +StarburstDataset.QUERY_USER_COUNT = NumericField("queryUserCount", "queryUserCount") +StarburstDataset.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +StarburstDataset.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +StarburstDataset.DATABASE_NAME = KeywordField("databaseName", "databaseName") +StarburstDataset.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +StarburstDataset.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +StarburstDataset.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +StarburstDataset.TABLE_NAME = KeywordField("tableName", "tableName") +StarburstDataset.TABLE_QUALIFIED_NAME = KeywordField( + "tableQualifiedName", "tableQualifiedName" +) +StarburstDataset.VIEW_NAME = KeywordField("viewName", "viewName") +StarburstDataset.VIEW_QUALIFIED_NAME = KeywordField( + "viewQualifiedName", "viewQualifiedName" +) +StarburstDataset.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +StarburstDataset.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +StarburstDataset.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +StarburstDataset.LAST_PROFILED_AT = NumericField("lastProfiledAt", "lastProfiledAt") +StarburstDataset.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +StarburstDataset.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +StarburstDataset.COLUMN_COUNT = NumericField("columnCount", "columnCount") +StarburstDataset.ROW_COUNT = NumericField("rowCount", "rowCount") +StarburstDataset.SIZE_BYTES = NumericField("sizeBytes", "sizeBytes") +StarburstDataset.TABLE_OBJECT_COUNT = NumericField( + "tableObjectCount", "tableObjectCount" +) +StarburstDataset.ALIAS = KeywordField("alias", "alias") +StarburstDataset.IS_TEMPORARY = BooleanField("isTemporary", "isTemporary") +StarburstDataset.IS_QUERY_PREVIEW = BooleanField("isQueryPreview", "isQueryPreview") +StarburstDataset.QUERY_PREVIEW_CONFIG = KeywordField( + "queryPreviewConfig", "queryPreviewConfig" +) +StarburstDataset.EXTERNAL_LOCATION = KeywordField( + "externalLocation", "externalLocation" +) +StarburstDataset.EXTERNAL_LOCATION_REGION = KeywordField( + "externalLocationRegion", "externalLocationRegion" +) +StarburstDataset.EXTERNAL_LOCATION_FORMAT = KeywordField( + "externalLocationFormat", "externalLocationFormat" +) +StarburstDataset.IS_PARTITIONED = BooleanField("isPartitioned", "isPartitioned") +StarburstDataset.PARTITION_STRATEGY = KeywordField( + "partitionStrategy", "partitionStrategy" +) +StarburstDataset.PARTITION_COUNT = NumericField("partitionCount", "partitionCount") +StarburstDataset.TABLE_DEFINITION = KeywordField("tableDefinition", "tableDefinition") +StarburstDataset.PARTITION_LIST = KeywordField("partitionList", "partitionList") +StarburstDataset.IS_SHARDED = BooleanField("isSharded", "isSharded") +StarburstDataset.TABLE_TYPE = KeywordField("tableType", "tableType") +StarburstDataset.ICEBERG_CATALOG_NAME = KeywordField( + "icebergCatalogName", "icebergCatalogName" +) +StarburstDataset.ICEBERG_TABLE_TYPE = KeywordField( + "icebergTableType", "icebergTableType" +) +StarburstDataset.ICEBERG_CATALOG_SOURCE = KeywordField( + "icebergCatalogSource", "icebergCatalogSource" +) +StarburstDataset.ICEBERG_CATALOG_TABLE_NAME = KeywordField( + "icebergCatalogTableName", "icebergCatalogTableName" +) +StarburstDataset.TABLE_IMPALA_PARAMETERS = KeywordField( + "tableImpalaParameters", "tableImpalaParameters" +) +StarburstDataset.ICEBERG_CATALOG_TABLE_NAMESPACE = KeywordField( + "icebergCatalogTableNamespace", "icebergCatalogTableNamespace" +) +StarburstDataset.TABLE_EXTERNAL_VOLUME_NAME = KeywordField( + "tableExternalVolumeName", "tableExternalVolumeName" +) +StarburstDataset.ICEBERG_TABLE_BASE_LOCATION = KeywordField( + "icebergTableBaseLocation", "icebergTableBaseLocation" +) +StarburstDataset.TABLE_RETENTION_TIME = NumericField( + "tableRetentionTime", "tableRetentionTime" +) +StarburstDataset.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +StarburstDataset.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +StarburstDataset.ANOMALO_CHECKS = RelationField("anomaloChecks") +StarburstDataset.APPLICATION = RelationField("application") +StarburstDataset.APPLICATION_FIELD = RelationField("applicationField") +StarburstDataset.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +StarburstDataset.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +StarburstDataset.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +StarburstDataset.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +StarburstDataset.METRICS = RelationField("metrics") +StarburstDataset.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +StarburstDataset.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +StarburstDataset.DBT_MODELS = RelationField("dbtModels") +StarburstDataset.SQL_DBT_MODELS = RelationField("sqlDbtModels") +StarburstDataset.DBT_TESTS = RelationField("dbtTests") +StarburstDataset.DBT_SOURCES = RelationField("dbtSources") +StarburstDataset.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +StarburstDataset.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +StarburstDataset.MEANINGS = RelationField("meanings") +StarburstDataset.MC_MONITORS = RelationField("mcMonitors") +StarburstDataset.MC_INCIDENTS = RelationField("mcIncidents") +StarburstDataset.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +StarburstDataset.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +StarburstDataset.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +StarburstDataset.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +StarburstDataset.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +StarburstDataset.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +StarburstDataset.FILES = RelationField("files") +StarburstDataset.LINKS = RelationField("links") +StarburstDataset.README = RelationField("readme") +StarburstDataset.COLUMNS = RelationField("columns") +StarburstDataset.QUERIES = RelationField("queries") +StarburstDataset.ATLAN_SCHEMA = RelationField("atlanSchema") +StarburstDataset.DIMENSIONS = RelationField("dimensions") +StarburstDataset.FACTS = RelationField("facts") +StarburstDataset.PARTITIONS = RelationField("partitions") +StarburstDataset.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +StarburstDataset.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +StarburstDataset.SODA_CHECKS = RelationField("sodaChecks") +StarburstDataset.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +StarburstDataset.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") +StarburstDataset.STARBURST_DATA_PRODUCT = RelationField("starburstDataProduct") +StarburstDataset.STARBURST_DATASET_COLUMNS = RelationField("starburstDatasetColumns") diff --git a/pyatlan_v9/model/assets/starburst_dataset_column.py b/pyatlan_v9/model/assets/starburst_dataset_column.py new file mode 100644 index 000000000..41961a35d --- /dev/null +++ b/pyatlan_v9/model/assets/starburst_dataset_column.py @@ -0,0 +1,1913 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +StarburstDatasetColumn asset model with flattened inheritance. + +This module provides: +- StarburstDatasetColumn: Flat asset class (easy to use) +- StarburstDatasetColumnAttributes: Nested attributes struct (extends AssetAttributes) +- StarburstDatasetColumnNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .cosmos_mongo_db_related import RelatedCosmosMongoDBCollection +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtMetric, + RelatedDbtModel, + RelatedDbtModelColumn, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .mongo_db_related import RelatedMongoDBCollection +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .snowflake_related import ( + RelatedSnowflakeDynamicTable, + RelatedSnowflakeSemanticLogicalTable, +) +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from .sql_related import ( + RelatedCalculationView, + RelatedColumn, + RelatedMaterialisedView, + RelatedQuery, + RelatedTable, + RelatedTablePartition, + RelatedView, +) +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .starburst_related import RelatedStarburstDataset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class StarburstDatasetColumn(Asset): + """ + Instance of a column within a Starburst dataset in Atlan. + """ + + STARBURST_SQL_COLUMN_QUALIFIED_NAME: ClassVar[Any] = None + STARBURST_DATA_PRODUCT_NAME: ClassVar[Any] = None + STARBURST_DATASET_QUALIFIED_NAME: ClassVar[Any] = None + STARBURST_DATASET_NAME: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + DATA_TYPE: ClassVar[Any] = None + SUB_DATA_TYPE: ClassVar[Any] = None + COLUMN_COMPRESSION: ClassVar[Any] = None + COLUMN_ENCODING: ClassVar[Any] = None + RAW_DATA_TYPE_DEFINITION: ClassVar[Any] = None + ORDER: ClassVar[Any] = None + NESTED_COLUMN_ORDER: ClassVar[Any] = None + NESTED_COLUMN_COUNT: ClassVar[Any] = None + COLUMN_HIERARCHY: ClassVar[Any] = None + IS_PARTITION: ClassVar[Any] = None + PARTITION_ORDER: ClassVar[Any] = None + IS_CLUSTERED: ClassVar[Any] = None + IS_PRIMARY: ClassVar[Any] = None + IS_FOREIGN: ClassVar[Any] = None + IS_INDEXED: ClassVar[Any] = None + IS_SORT: ClassVar[Any] = None + IS_DIST: ClassVar[Any] = None + IS_PINNED: ClassVar[Any] = None + PINNED_BY: ClassVar[Any] = None + PINNED_AT: ClassVar[Any] = None + PRECISION: ClassVar[Any] = None + DEFAULT_VALUE: ClassVar[Any] = None + IS_NULLABLE: ClassVar[Any] = None + NUMERIC_SCALE: ClassVar[Any] = None + MAX_LENGTH: ClassVar[Any] = None + VALIDATIONS: ClassVar[Any] = None + PARENT_COLUMN_QUALIFIED_NAME: ClassVar[Any] = None + PARENT_COLUMN_NAME: ClassVar[Any] = None + COLUMN_DISTINCT_VALUES_COUNT: ClassVar[Any] = None + COLUMN_DISTINCT_VALUES_COUNT_LONG: ClassVar[Any] = None + COLUMN_HISTOGRAM: ClassVar[Any] = None + COLUMN_MAX: ClassVar[Any] = None + COLUMN_MIN: ClassVar[Any] = None + COLUMN_MEAN: ClassVar[Any] = None + COLUMN_SUM: ClassVar[Any] = None + COLUMN_MEDIAN: ClassVar[Any] = None + COLUMN_STANDARD_DEVIATION: ClassVar[Any] = None + COLUMN_UNIQUE_VALUES_COUNT: ClassVar[Any] = None + COLUMN_UNIQUE_VALUES_COUNT_LONG: ClassVar[Any] = None + COLUMN_AVERAGE: ClassVar[Any] = None + COLUMN_AVERAGE_LENGTH: ClassVar[Any] = None + COLUMN_DUPLICATE_VALUES_COUNT: ClassVar[Any] = None + COLUMN_DUPLICATE_VALUES_COUNT_LONG: ClassVar[Any] = None + COLUMN_MAXIMUM_STRING_LENGTH: ClassVar[Any] = None + COLUMN_MAXS: ClassVar[Any] = None + COLUMN_MINIMUM_STRING_LENGTH: ClassVar[Any] = None + COLUMN_MINS: ClassVar[Any] = None + COLUMN_MISSING_VALUES_COUNT: ClassVar[Any] = None + COLUMN_MISSING_VALUES_COUNT_LONG: ClassVar[Any] = None + COLUMN_MISSING_VALUES_PERCENTAGE: ClassVar[Any] = None + COLUMN_UNIQUENESS_PERCENTAGE: ClassVar[Any] = None + COLUMN_VARIANCE: ClassVar[Any] = None + COLUMN_TOP_VALUES: ClassVar[Any] = None + COLUMN_MAX_VALUE: ClassVar[Any] = None + COLUMN_MIN_VALUE: ClassVar[Any] = None + COLUMN_MEAN_VALUE: ClassVar[Any] = None + COLUMN_SUM_VALUE: ClassVar[Any] = None + COLUMN_MEDIAN_VALUE: ClassVar[Any] = None + COLUMN_STANDARD_DEVIATION_VALUE: ClassVar[Any] = None + COLUMN_AVERAGE_VALUE: ClassVar[Any] = None + COLUMN_VARIANCE_VALUE: ClassVar[Any] = None + COLUMN_AVERAGE_LENGTH_VALUE: ClassVar[Any] = None + COLUMN_DISTRIBUTION_HISTOGRAM: ClassVar[Any] = None + COLUMN_DEPTH_LEVEL: ClassVar[Any] = None + NOSQL_COLLECTION_NAME: ClassVar[Any] = None + NOSQL_COLLECTION_QUALIFIED_NAME: ClassVar[Any] = None + COLUMN_IS_MEASURE: ClassVar[Any] = None + COLUMN_MEASURE_TYPE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + COSMOS_MONGO_DB_COLLECTION: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + METRIC_TIMESTAMPS: ClassVar[Any] = None + DATA_QUALITY_METRIC_DIMENSIONS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_BASE_COLUMN_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_COLUMN_RULES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_METRICS: ClassVar[Any] = None + DBT_MODEL_COLUMNS: ClassVar[Any] = None + COLUMN_DBT_MODEL_COLUMNS: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MONGO_DB_COLLECTION: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + TABLE: ClassVar[Any] = None + NESTED_COLUMNS: ClassVar[Any] = None + PARENT_COLUMN: ClassVar[Any] = None + TABLE_PARTITION: ClassVar[Any] = None + VIEW: ClassVar[Any] = None + CALCULATION_VIEW: ClassVar[Any] = None + MATERIALISED_VIEW: ClassVar[Any] = None + FOREIGN_KEY_TO: ClassVar[Any] = None + FOREIGN_KEY_FROM: ClassVar[Any] = None + QUERIES: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_DYNAMIC_TABLE: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + STARBURST_DATASET: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "StarburstDatasetColumn" + + starburst_sql_column_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the corresponding SQL Column. Enables cross-stream lookup between the Data Product perspective and the SQL perspective of the same underlying column.""" + + starburst_data_product_name: Union[str, None, UnsetType] = UNSET + """Name of the Starburst Data Product that contains this asset.""" + + starburst_dataset_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the Starburst Dataset that contains this asset, or this asset's own qualified name if it is a Dataset.""" + + starburst_dataset_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Starburst Dataset that contains this asset, or this asset's own name if it is a Dataset.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + data_type: Union[str, None, UnsetType] = UNSET + """Data type of values in this column.""" + + sub_data_type: Union[str, None, UnsetType] = UNSET + """Sub-data type of this column.""" + + column_compression: Union[str, None, UnsetType] = UNSET + """Compression type of this column.""" + + column_encoding: Union[str, None, UnsetType] = UNSET + """Encoding type of this column.""" + + raw_data_type_definition: Union[str, None, UnsetType] = UNSET + """Raw data type definition of this column.""" + + order: Union[int, None, UnsetType] = UNSET + """Order (position) in which this column appears in the table (starting at 1).""" + + nested_column_order: Union[str, None, UnsetType] = UNSET + """Order (position) in which this column appears in the nested Column (nest level starts at 1).""" + + nested_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns nested within this (STRUCT or NESTED) column.""" + + column_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of top-level upstream nested columns.""" + + is_partition: Union[bool, None, UnsetType] = UNSET + """Whether this column is a partition column (true) or not (false).""" + + partition_order: Union[int, None, UnsetType] = UNSET + """Order (position) of this partition column in the table.""" + + is_clustered: Union[bool, None, UnsetType] = UNSET + """Whether this column is a clustered column (true) or not (false).""" + + is_primary: Union[bool, None, UnsetType] = UNSET + """When true, this column is the primary key for the table.""" + + is_foreign: Union[bool, None, UnsetType] = UNSET + """When true, this column is a foreign key to another table. NOTE: this must be true when using the foreignKeyTo relationship to specify columns that refer to this column as a foreign key.""" + + is_indexed: Union[bool, None, UnsetType] = UNSET + """When true, this column is indexed in the database.""" + + is_sort: Union[bool, None, UnsetType] = UNSET + """Whether this column is a sort column (true) or not (false).""" + + is_dist: Union[bool, None, UnsetType] = UNSET + """Whether this column is a distribution column (true) or not (false).""" + + is_pinned: Union[bool, None, UnsetType] = UNSET + """Whether this column is pinned (true) or not (false).""" + + pinned_by: Union[str, None, UnsetType] = UNSET + """User who pinned this column.""" + + pinned_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this column was pinned, in milliseconds.""" + + precision: Union[int, None, UnsetType] = UNSET + """Total number of digits allowed, when the dataType is numeric.""" + + default_value: Union[str, None, UnsetType] = UNSET + """Default value for this column.""" + + is_nullable: Union[bool, None, UnsetType] = UNSET + """When true, the values in this column can be null.""" + + numeric_scale: Union[float, None, UnsetType] = UNSET + """Number of digits allowed to the right of the decimal point.""" + + max_length: Union[int, None, UnsetType] = UNSET + """Maximum length of a value in this column.""" + + validations: Union[Dict[str, str], None, UnsetType] = UNSET + """Validations for this column.""" + + parent_column_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the column this column is nested within, for STRUCT and NESTED columns.""" + + parent_column_name: Union[str, None, UnsetType] = UNSET + """Simple name of the column this column is nested within, for STRUCT and NESTED columns.""" + + column_distinct_values_count: Union[int, None, UnsetType] = UNSET + """Number of rows that contain distinct values.""" + + column_distinct_values_count_long: Union[int, None, UnsetType] = UNSET + """Number of rows that contain distinct values.""" + + column_histogram: Union[Dict[str, Any], None, UnsetType] = UNSET + """List of values in a histogram that represents the contents of this column.""" + + column_max: Union[float, None, UnsetType] = UNSET + """Greatest value in a numeric column.""" + + column_min: Union[float, None, UnsetType] = UNSET + """Least value in a numeric column.""" + + column_mean: Union[float, None, UnsetType] = UNSET + """Arithmetic mean of the values in a numeric column.""" + + column_sum: Union[float, None, UnsetType] = UNSET + """Calculated sum of the values in a numeric column.""" + + column_median: Union[float, None, UnsetType] = UNSET + """Calculated median of the values in a numeric column.""" + + column_standard_deviation: Union[float, None, UnsetType] = UNSET + """Calculated standard deviation of the values in a numeric column.""" + + column_unique_values_count: Union[int, None, UnsetType] = UNSET + """Number of rows in which a value in this column appears only once.""" + + column_unique_values_count_long: Union[int, None, UnsetType] = UNSET + """Number of rows in which a value in this column appears only once.""" + + column_average: Union[float, None, UnsetType] = UNSET + """Average value in this column.""" + + column_average_length: Union[float, None, UnsetType] = UNSET + """Average length of values in a string column.""" + + column_duplicate_values_count: Union[int, None, UnsetType] = UNSET + """Number of rows that contain duplicate values.""" + + column_duplicate_values_count_long: Union[int, None, UnsetType] = UNSET + """Number of rows that contain duplicate values.""" + + column_maximum_string_length: Union[int, None, UnsetType] = UNSET + """Length of the longest value in a string column.""" + + column_maxs: Union[List[str], None, UnsetType] = UNSET + """List of the greatest values in a column.""" + + column_minimum_string_length: Union[int, None, UnsetType] = UNSET + """Length of the shortest value in a string column.""" + + column_mins: Union[List[str], None, UnsetType] = UNSET + """List of the least values in a column.""" + + column_missing_values_count: Union[int, None, UnsetType] = UNSET + """Number of rows in a column that do not contain content.""" + + column_missing_values_count_long: Union[int, None, UnsetType] = UNSET + """Number of rows in a column that do not contain content.""" + + column_missing_values_percentage: Union[float, None, UnsetType] = UNSET + """Percentage of rows in a column that do not contain content.""" + + column_uniqueness_percentage: Union[float, None, UnsetType] = UNSET + """Ratio indicating how unique data in this column is: 0 indicates that all values are the same, 100 indicates that all values in this column are unique.""" + + column_variance: Union[float, None, UnsetType] = UNSET + """Calculated variance of the values in a numeric column.""" + + column_top_values: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of top values in this column.""" + + column_max_value: Union[float, None, UnsetType] = UNSET + """Greatest value in a numeric column.""" + + column_min_value: Union[float, None, UnsetType] = UNSET + """Least value in a numeric column.""" + + column_mean_value: Union[float, None, UnsetType] = UNSET + """Arithmetic mean of the values in a numeric column.""" + + column_sum_value: Union[float, None, UnsetType] = UNSET + """Calculated sum of the values in a numeric column.""" + + column_median_value: Union[float, None, UnsetType] = UNSET + """Calculated median of the values in a numeric column.""" + + column_standard_deviation_value: Union[float, None, UnsetType] = UNSET + """Calculated standard deviation of the values in a numeric column.""" + + column_average_value: Union[float, None, UnsetType] = UNSET + """Average value in this column.""" + + column_variance_value: Union[float, None, UnsetType] = UNSET + """Calculated variance of the values in a numeric column.""" + + column_average_length_value: Union[float, None, UnsetType] = UNSET + """Average length of values in a string column.""" + + column_distribution_histogram: Union[Dict[str, Any], None, UnsetType] = UNSET + """Detailed information representing a histogram of values for a column.""" + + column_depth_level: Union[int, None, UnsetType] = UNSET + """Level of nesting of this column, used for STRUCT and NESTED columns.""" + + nosql_collection_name: Union[str, None, UnsetType] = UNSET + """Simple name of the cosmos/mongo collection in which this SQL asset (column) exists, or empty if it does not exist within a cosmos/mongo collection.""" + + nosql_collection_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the cosmos/mongo collection in which this SQL asset (column) exists, or empty if it does not exist within a cosmos/mongo collection.""" + + column_is_measure: Union[bool, None, UnsetType] = UNSET + """When true, this column is of type measure/calculated.""" + + column_measure_type: Union[str, None, UnsetType] = UNSET + """The type of measure/calculated column this is, eg: base, calculated, derived.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cosmos_mongo_db_collection: Union[ + RelatedCosmosMongoDBCollection, None, UnsetType + ] = msgspec.field(default=UNSET, name="cosmosMongoDBCollection") + """Cosmos collection in which this column exists.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + metric_timestamps: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + data_quality_metric_dimensions: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_base_column_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this column.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dq_reference_column_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this column is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_metrics: Union[List[RelatedDbtMetric], None, UnsetType] = UNSET + """Metrics related to this model column.""" + + dbt_model_columns: Union[List[RelatedDbtModelColumn], None, UnsetType] = UNSET + """(Deprecated) Model columns related to this model column.""" + + column_dbt_model_columns: Union[List[RelatedDbtModelColumn], None, UnsetType] = ( + UNSET + ) + """Model columns related to this column.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mongo_db_collection: Union[RelatedMongoDBCollection, None, UnsetType] = ( + msgspec.field(default=UNSET, name="mongoDBCollection") + ) + """Collection in which the columns exist.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + table: Union[RelatedTable, None, UnsetType] = UNSET + """Table in which this column exists.""" + + nested_columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Nested columns that exist within this column.""" + + parent_column: Union[RelatedColumn, None, UnsetType] = UNSET + """Column in which this sub-column is nested.""" + + table_partition: Union[RelatedTablePartition, None, UnsetType] = UNSET + """Table partition that contains this column.""" + + view: Union[RelatedView, None, UnsetType] = UNSET + """View in which this column exists.""" + + calculation_view: Union[RelatedCalculationView, None, UnsetType] = UNSET + """Calculate view in which this column exists.""" + + materialised_view: Union[RelatedMaterialisedView, None, UnsetType] = UNSET + """Materialized view in which this column exists.""" + + foreign_key_to: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Columns that use this column as a foreign key.""" + + foreign_key_from: Union[RelatedColumn, None, UnsetType] = UNSET + """Column this foreign key column refers to.""" + + queries: Union[List[RelatedQuery], None, UnsetType] = UNSET + """Queries that access this column.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_dynamic_table: Union[RelatedSnowflakeDynamicTable, None, UnsetType] = ( + UNSET + ) + """Snowflake dynamic table in which this column exists.""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + starburst_dataset: Union[RelatedStarburstDataset, None, UnsetType] = UNSET + """Dataset in which this column exists.""" + + def __post_init__(self) -> None: + self.type_name = "StarburstDatasetColumn" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _starburst_dataset_column_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> StarburstDatasetColumn: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + StarburstDatasetColumn instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _starburst_dataset_column_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class StarburstDatasetColumnAttributes(AssetAttributes): + """StarburstDatasetColumn-specific attributes for nested API format.""" + + starburst_sql_column_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the corresponding SQL Column. Enables cross-stream lookup between the Data Product perspective and the SQL perspective of the same underlying column.""" + + starburst_data_product_name: Union[str, None, UnsetType] = UNSET + """Name of the Starburst Data Product that contains this asset.""" + + starburst_dataset_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the Starburst Dataset that contains this asset, or this asset's own qualified name if it is a Dataset.""" + + starburst_dataset_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Starburst Dataset that contains this asset, or this asset's own name if it is a Dataset.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + data_type: Union[str, None, UnsetType] = UNSET + """Data type of values in this column.""" + + sub_data_type: Union[str, None, UnsetType] = UNSET + """Sub-data type of this column.""" + + column_compression: Union[str, None, UnsetType] = UNSET + """Compression type of this column.""" + + column_encoding: Union[str, None, UnsetType] = UNSET + """Encoding type of this column.""" + + raw_data_type_definition: Union[str, None, UnsetType] = UNSET + """Raw data type definition of this column.""" + + order: Union[int, None, UnsetType] = UNSET + """Order (position) in which this column appears in the table (starting at 1).""" + + nested_column_order: Union[str, None, UnsetType] = UNSET + """Order (position) in which this column appears in the nested Column (nest level starts at 1).""" + + nested_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns nested within this (STRUCT or NESTED) column.""" + + column_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of top-level upstream nested columns.""" + + is_partition: Union[bool, None, UnsetType] = UNSET + """Whether this column is a partition column (true) or not (false).""" + + partition_order: Union[int, None, UnsetType] = UNSET + """Order (position) of this partition column in the table.""" + + is_clustered: Union[bool, None, UnsetType] = UNSET + """Whether this column is a clustered column (true) or not (false).""" + + is_primary: Union[bool, None, UnsetType] = UNSET + """When true, this column is the primary key for the table.""" + + is_foreign: Union[bool, None, UnsetType] = UNSET + """When true, this column is a foreign key to another table. NOTE: this must be true when using the foreignKeyTo relationship to specify columns that refer to this column as a foreign key.""" + + is_indexed: Union[bool, None, UnsetType] = UNSET + """When true, this column is indexed in the database.""" + + is_sort: Union[bool, None, UnsetType] = UNSET + """Whether this column is a sort column (true) or not (false).""" + + is_dist: Union[bool, None, UnsetType] = UNSET + """Whether this column is a distribution column (true) or not (false).""" + + is_pinned: Union[bool, None, UnsetType] = UNSET + """Whether this column is pinned (true) or not (false).""" + + pinned_by: Union[str, None, UnsetType] = UNSET + """User who pinned this column.""" + + pinned_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this column was pinned, in milliseconds.""" + + precision: Union[int, None, UnsetType] = UNSET + """Total number of digits allowed, when the dataType is numeric.""" + + default_value: Union[str, None, UnsetType] = UNSET + """Default value for this column.""" + + is_nullable: Union[bool, None, UnsetType] = UNSET + """When true, the values in this column can be null.""" + + numeric_scale: Union[float, None, UnsetType] = UNSET + """Number of digits allowed to the right of the decimal point.""" + + max_length: Union[int, None, UnsetType] = UNSET + """Maximum length of a value in this column.""" + + validations: Union[Dict[str, str], None, UnsetType] = UNSET + """Validations for this column.""" + + parent_column_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the column this column is nested within, for STRUCT and NESTED columns.""" + + parent_column_name: Union[str, None, UnsetType] = UNSET + """Simple name of the column this column is nested within, for STRUCT and NESTED columns.""" + + column_distinct_values_count: Union[int, None, UnsetType] = UNSET + """Number of rows that contain distinct values.""" + + column_distinct_values_count_long: Union[int, None, UnsetType] = UNSET + """Number of rows that contain distinct values.""" + + column_histogram: Union[Dict[str, Any], None, UnsetType] = UNSET + """List of values in a histogram that represents the contents of this column.""" + + column_max: Union[float, None, UnsetType] = UNSET + """Greatest value in a numeric column.""" + + column_min: Union[float, None, UnsetType] = UNSET + """Least value in a numeric column.""" + + column_mean: Union[float, None, UnsetType] = UNSET + """Arithmetic mean of the values in a numeric column.""" + + column_sum: Union[float, None, UnsetType] = UNSET + """Calculated sum of the values in a numeric column.""" + + column_median: Union[float, None, UnsetType] = UNSET + """Calculated median of the values in a numeric column.""" + + column_standard_deviation: Union[float, None, UnsetType] = UNSET + """Calculated standard deviation of the values in a numeric column.""" + + column_unique_values_count: Union[int, None, UnsetType] = UNSET + """Number of rows in which a value in this column appears only once.""" + + column_unique_values_count_long: Union[int, None, UnsetType] = UNSET + """Number of rows in which a value in this column appears only once.""" + + column_average: Union[float, None, UnsetType] = UNSET + """Average value in this column.""" + + column_average_length: Union[float, None, UnsetType] = UNSET + """Average length of values in a string column.""" + + column_duplicate_values_count: Union[int, None, UnsetType] = UNSET + """Number of rows that contain duplicate values.""" + + column_duplicate_values_count_long: Union[int, None, UnsetType] = UNSET + """Number of rows that contain duplicate values.""" + + column_maximum_string_length: Union[int, None, UnsetType] = UNSET + """Length of the longest value in a string column.""" + + column_maxs: Union[List[str], None, UnsetType] = UNSET + """List of the greatest values in a column.""" + + column_minimum_string_length: Union[int, None, UnsetType] = UNSET + """Length of the shortest value in a string column.""" + + column_mins: Union[List[str], None, UnsetType] = UNSET + """List of the least values in a column.""" + + column_missing_values_count: Union[int, None, UnsetType] = UNSET + """Number of rows in a column that do not contain content.""" + + column_missing_values_count_long: Union[int, None, UnsetType] = UNSET + """Number of rows in a column that do not contain content.""" + + column_missing_values_percentage: Union[float, None, UnsetType] = UNSET + """Percentage of rows in a column that do not contain content.""" + + column_uniqueness_percentage: Union[float, None, UnsetType] = UNSET + """Ratio indicating how unique data in this column is: 0 indicates that all values are the same, 100 indicates that all values in this column are unique.""" + + column_variance: Union[float, None, UnsetType] = UNSET + """Calculated variance of the values in a numeric column.""" + + column_top_values: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of top values in this column.""" + + column_max_value: Union[float, None, UnsetType] = UNSET + """Greatest value in a numeric column.""" + + column_min_value: Union[float, None, UnsetType] = UNSET + """Least value in a numeric column.""" + + column_mean_value: Union[float, None, UnsetType] = UNSET + """Arithmetic mean of the values in a numeric column.""" + + column_sum_value: Union[float, None, UnsetType] = UNSET + """Calculated sum of the values in a numeric column.""" + + column_median_value: Union[float, None, UnsetType] = UNSET + """Calculated median of the values in a numeric column.""" + + column_standard_deviation_value: Union[float, None, UnsetType] = UNSET + """Calculated standard deviation of the values in a numeric column.""" + + column_average_value: Union[float, None, UnsetType] = UNSET + """Average value in this column.""" + + column_variance_value: Union[float, None, UnsetType] = UNSET + """Calculated variance of the values in a numeric column.""" + + column_average_length_value: Union[float, None, UnsetType] = UNSET + """Average length of values in a string column.""" + + column_distribution_histogram: Union[Dict[str, Any], None, UnsetType] = UNSET + """Detailed information representing a histogram of values for a column.""" + + column_depth_level: Union[int, None, UnsetType] = UNSET + """Level of nesting of this column, used for STRUCT and NESTED columns.""" + + nosql_collection_name: Union[str, None, UnsetType] = UNSET + """Simple name of the cosmos/mongo collection in which this SQL asset (column) exists, or empty if it does not exist within a cosmos/mongo collection.""" + + nosql_collection_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the cosmos/mongo collection in which this SQL asset (column) exists, or empty if it does not exist within a cosmos/mongo collection.""" + + column_is_measure: Union[bool, None, UnsetType] = UNSET + """When true, this column is of type measure/calculated.""" + + column_measure_type: Union[str, None, UnsetType] = UNSET + """The type of measure/calculated column this is, eg: base, calculated, derived.""" + + +class StarburstDatasetColumnRelationshipAttributes(AssetRelationshipAttributes): + """StarburstDatasetColumn-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + cosmos_mongo_db_collection: Union[ + RelatedCosmosMongoDBCollection, None, UnsetType + ] = msgspec.field(default=UNSET, name="cosmosMongoDBCollection") + """Cosmos collection in which this column exists.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + metric_timestamps: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + data_quality_metric_dimensions: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_base_column_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this column.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dq_reference_column_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this column is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_metrics: Union[List[RelatedDbtMetric], None, UnsetType] = UNSET + """Metrics related to this model column.""" + + dbt_model_columns: Union[List[RelatedDbtModelColumn], None, UnsetType] = UNSET + """(Deprecated) Model columns related to this model column.""" + + column_dbt_model_columns: Union[List[RelatedDbtModelColumn], None, UnsetType] = ( + UNSET + ) + """Model columns related to this column.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mongo_db_collection: Union[RelatedMongoDBCollection, None, UnsetType] = ( + msgspec.field(default=UNSET, name="mongoDBCollection") + ) + """Collection in which the columns exist.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + table: Union[RelatedTable, None, UnsetType] = UNSET + """Table in which this column exists.""" + + nested_columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Nested columns that exist within this column.""" + + parent_column: Union[RelatedColumn, None, UnsetType] = UNSET + """Column in which this sub-column is nested.""" + + table_partition: Union[RelatedTablePartition, None, UnsetType] = UNSET + """Table partition that contains this column.""" + + view: Union[RelatedView, None, UnsetType] = UNSET + """View in which this column exists.""" + + calculation_view: Union[RelatedCalculationView, None, UnsetType] = UNSET + """Calculate view in which this column exists.""" + + materialised_view: Union[RelatedMaterialisedView, None, UnsetType] = UNSET + """Materialized view in which this column exists.""" + + foreign_key_to: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Columns that use this column as a foreign key.""" + + foreign_key_from: Union[RelatedColumn, None, UnsetType] = UNSET + """Column this foreign key column refers to.""" + + queries: Union[List[RelatedQuery], None, UnsetType] = UNSET + """Queries that access this column.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_dynamic_table: Union[RelatedSnowflakeDynamicTable, None, UnsetType] = ( + UNSET + ) + """Snowflake dynamic table in which this column exists.""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + starburst_dataset: Union[RelatedStarburstDataset, None, UnsetType] = UNSET + """Dataset in which this column exists.""" + + +class StarburstDatasetColumnNested(AssetNested): + """StarburstDatasetColumn in nested API format for high-performance serialization.""" + + attributes: Union[StarburstDatasetColumnAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + StarburstDatasetColumnRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + StarburstDatasetColumnRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + StarburstDatasetColumnRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_STARBURST_DATASET_COLUMN_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "cosmos_mongo_db_collection", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "metric_timestamps", + "data_quality_metric_dimensions", + "dq_base_dataset_rules", + "dq_base_column_rules", + "dq_reference_dataset_rules", + "dq_reference_column_rules", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_metrics", + "dbt_model_columns", + "column_dbt_model_columns", + "dbt_seed_assets", + "meanings", + "mongo_db_collection", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "table", + "nested_columns", + "parent_column", + "table_partition", + "view", + "calculation_view", + "materialised_view", + "foreign_key_to", + "foreign_key_from", + "queries", + "schema_registry_subjects", + "snowflake_dynamic_table", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", + "starburst_dataset", +] + + +def _populate_starburst_dataset_column_attrs( + attrs: StarburstDatasetColumnAttributes, obj: StarburstDatasetColumn +) -> None: + """Populate StarburstDatasetColumn-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.starburst_sql_column_qualified_name = obj.starburst_sql_column_qualified_name + attrs.starburst_data_product_name = obj.starburst_data_product_name + attrs.starburst_dataset_qualified_name = obj.starburst_dataset_qualified_name + attrs.starburst_dataset_name = obj.starburst_dataset_name + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + attrs.data_type = obj.data_type + attrs.sub_data_type = obj.sub_data_type + attrs.column_compression = obj.column_compression + attrs.column_encoding = obj.column_encoding + attrs.raw_data_type_definition = obj.raw_data_type_definition + attrs.order = obj.order + attrs.nested_column_order = obj.nested_column_order + attrs.nested_column_count = obj.nested_column_count + attrs.column_hierarchy = obj.column_hierarchy + attrs.is_partition = obj.is_partition + attrs.partition_order = obj.partition_order + attrs.is_clustered = obj.is_clustered + attrs.is_primary = obj.is_primary + attrs.is_foreign = obj.is_foreign + attrs.is_indexed = obj.is_indexed + attrs.is_sort = obj.is_sort + attrs.is_dist = obj.is_dist + attrs.is_pinned = obj.is_pinned + attrs.pinned_by = obj.pinned_by + attrs.pinned_at = obj.pinned_at + attrs.precision = obj.precision + attrs.default_value = obj.default_value + attrs.is_nullable = obj.is_nullable + attrs.numeric_scale = obj.numeric_scale + attrs.max_length = obj.max_length + attrs.validations = obj.validations + attrs.parent_column_qualified_name = obj.parent_column_qualified_name + attrs.parent_column_name = obj.parent_column_name + attrs.column_distinct_values_count = obj.column_distinct_values_count + attrs.column_distinct_values_count_long = obj.column_distinct_values_count_long + attrs.column_histogram = obj.column_histogram + attrs.column_max = obj.column_max + attrs.column_min = obj.column_min + attrs.column_mean = obj.column_mean + attrs.column_sum = obj.column_sum + attrs.column_median = obj.column_median + attrs.column_standard_deviation = obj.column_standard_deviation + attrs.column_unique_values_count = obj.column_unique_values_count + attrs.column_unique_values_count_long = obj.column_unique_values_count_long + attrs.column_average = obj.column_average + attrs.column_average_length = obj.column_average_length + attrs.column_duplicate_values_count = obj.column_duplicate_values_count + attrs.column_duplicate_values_count_long = obj.column_duplicate_values_count_long + attrs.column_maximum_string_length = obj.column_maximum_string_length + attrs.column_maxs = obj.column_maxs + attrs.column_minimum_string_length = obj.column_minimum_string_length + attrs.column_mins = obj.column_mins + attrs.column_missing_values_count = obj.column_missing_values_count + attrs.column_missing_values_count_long = obj.column_missing_values_count_long + attrs.column_missing_values_percentage = obj.column_missing_values_percentage + attrs.column_uniqueness_percentage = obj.column_uniqueness_percentage + attrs.column_variance = obj.column_variance + attrs.column_top_values = obj.column_top_values + attrs.column_max_value = obj.column_max_value + attrs.column_min_value = obj.column_min_value + attrs.column_mean_value = obj.column_mean_value + attrs.column_sum_value = obj.column_sum_value + attrs.column_median_value = obj.column_median_value + attrs.column_standard_deviation_value = obj.column_standard_deviation_value + attrs.column_average_value = obj.column_average_value + attrs.column_variance_value = obj.column_variance_value + attrs.column_average_length_value = obj.column_average_length_value + attrs.column_distribution_histogram = obj.column_distribution_histogram + attrs.column_depth_level = obj.column_depth_level + attrs.nosql_collection_name = obj.nosql_collection_name + attrs.nosql_collection_qualified_name = obj.nosql_collection_qualified_name + attrs.column_is_measure = obj.column_is_measure + attrs.column_measure_type = obj.column_measure_type + + +def _extract_starburst_dataset_column_attrs( + attrs: StarburstDatasetColumnAttributes, +) -> dict: + """Extract all StarburstDatasetColumn attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["starburst_sql_column_qualified_name"] = ( + attrs.starburst_sql_column_qualified_name + ) + result["starburst_data_product_name"] = attrs.starburst_data_product_name + result["starburst_dataset_qualified_name"] = attrs.starburst_dataset_qualified_name + result["starburst_dataset_name"] = attrs.starburst_dataset_name + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + result["data_type"] = attrs.data_type + result["sub_data_type"] = attrs.sub_data_type + result["column_compression"] = attrs.column_compression + result["column_encoding"] = attrs.column_encoding + result["raw_data_type_definition"] = attrs.raw_data_type_definition + result["order"] = attrs.order + result["nested_column_order"] = attrs.nested_column_order + result["nested_column_count"] = attrs.nested_column_count + result["column_hierarchy"] = attrs.column_hierarchy + result["is_partition"] = attrs.is_partition + result["partition_order"] = attrs.partition_order + result["is_clustered"] = attrs.is_clustered + result["is_primary"] = attrs.is_primary + result["is_foreign"] = attrs.is_foreign + result["is_indexed"] = attrs.is_indexed + result["is_sort"] = attrs.is_sort + result["is_dist"] = attrs.is_dist + result["is_pinned"] = attrs.is_pinned + result["pinned_by"] = attrs.pinned_by + result["pinned_at"] = attrs.pinned_at + result["precision"] = attrs.precision + result["default_value"] = attrs.default_value + result["is_nullable"] = attrs.is_nullable + result["numeric_scale"] = attrs.numeric_scale + result["max_length"] = attrs.max_length + result["validations"] = attrs.validations + result["parent_column_qualified_name"] = attrs.parent_column_qualified_name + result["parent_column_name"] = attrs.parent_column_name + result["column_distinct_values_count"] = attrs.column_distinct_values_count + result["column_distinct_values_count_long"] = ( + attrs.column_distinct_values_count_long + ) + result["column_histogram"] = attrs.column_histogram + result["column_max"] = attrs.column_max + result["column_min"] = attrs.column_min + result["column_mean"] = attrs.column_mean + result["column_sum"] = attrs.column_sum + result["column_median"] = attrs.column_median + result["column_standard_deviation"] = attrs.column_standard_deviation + result["column_unique_values_count"] = attrs.column_unique_values_count + result["column_unique_values_count_long"] = attrs.column_unique_values_count_long + result["column_average"] = attrs.column_average + result["column_average_length"] = attrs.column_average_length + result["column_duplicate_values_count"] = attrs.column_duplicate_values_count + result["column_duplicate_values_count_long"] = ( + attrs.column_duplicate_values_count_long + ) + result["column_maximum_string_length"] = attrs.column_maximum_string_length + result["column_maxs"] = attrs.column_maxs + result["column_minimum_string_length"] = attrs.column_minimum_string_length + result["column_mins"] = attrs.column_mins + result["column_missing_values_count"] = attrs.column_missing_values_count + result["column_missing_values_count_long"] = attrs.column_missing_values_count_long + result["column_missing_values_percentage"] = attrs.column_missing_values_percentage + result["column_uniqueness_percentage"] = attrs.column_uniqueness_percentage + result["column_variance"] = attrs.column_variance + result["column_top_values"] = attrs.column_top_values + result["column_max_value"] = attrs.column_max_value + result["column_min_value"] = attrs.column_min_value + result["column_mean_value"] = attrs.column_mean_value + result["column_sum_value"] = attrs.column_sum_value + result["column_median_value"] = attrs.column_median_value + result["column_standard_deviation_value"] = attrs.column_standard_deviation_value + result["column_average_value"] = attrs.column_average_value + result["column_variance_value"] = attrs.column_variance_value + result["column_average_length_value"] = attrs.column_average_length_value + result["column_distribution_histogram"] = attrs.column_distribution_histogram + result["column_depth_level"] = attrs.column_depth_level + result["nosql_collection_name"] = attrs.nosql_collection_name + result["nosql_collection_qualified_name"] = attrs.nosql_collection_qualified_name + result["column_is_measure"] = attrs.column_is_measure + result["column_measure_type"] = attrs.column_measure_type + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _starburst_dataset_column_to_nested( + starburst_dataset_column: StarburstDatasetColumn, +) -> StarburstDatasetColumnNested: + """Convert flat StarburstDatasetColumn to nested format.""" + attrs = StarburstDatasetColumnAttributes() + _populate_starburst_dataset_column_attrs(attrs, starburst_dataset_column) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + starburst_dataset_column, + _STARBURST_DATASET_COLUMN_REL_FIELDS, + StarburstDatasetColumnRelationshipAttributes, + ) + return StarburstDatasetColumnNested( + guid=starburst_dataset_column.guid, + type_name=starburst_dataset_column.type_name, + status=starburst_dataset_column.status, + version=starburst_dataset_column.version, + create_time=starburst_dataset_column.create_time, + update_time=starburst_dataset_column.update_time, + created_by=starburst_dataset_column.created_by, + updated_by=starburst_dataset_column.updated_by, + classifications=starburst_dataset_column.classifications, + classification_names=starburst_dataset_column.classification_names, + meanings=starburst_dataset_column.meanings, + labels=starburst_dataset_column.labels, + business_attributes=starburst_dataset_column.business_attributes, + custom_attributes=starburst_dataset_column.custom_attributes, + pending_tasks=starburst_dataset_column.pending_tasks, + proxy=starburst_dataset_column.proxy, + is_incomplete=starburst_dataset_column.is_incomplete, + provenance_type=starburst_dataset_column.provenance_type, + home_id=starburst_dataset_column.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _starburst_dataset_column_from_nested( + nested: StarburstDatasetColumnNested, +) -> StarburstDatasetColumn: + """Convert nested format to flat StarburstDatasetColumn.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else StarburstDatasetColumnAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _STARBURST_DATASET_COLUMN_REL_FIELDS, + StarburstDatasetColumnRelationshipAttributes, + ) + return StarburstDatasetColumn( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_starburst_dataset_column_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _starburst_dataset_column_to_nested_bytes( + starburst_dataset_column: StarburstDatasetColumn, serde: Serde +) -> bytes: + """Convert flat StarburstDatasetColumn to nested JSON bytes.""" + return serde.encode(_starburst_dataset_column_to_nested(starburst_dataset_column)) + + +def _starburst_dataset_column_from_nested_bytes( + data: bytes, serde: Serde +) -> StarburstDatasetColumn: + """Convert nested JSON bytes to flat StarburstDatasetColumn.""" + nested = serde.decode(data, StarburstDatasetColumnNested) + return _starburst_dataset_column_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +StarburstDatasetColumn.STARBURST_SQL_COLUMN_QUALIFIED_NAME = KeywordField( + "starburstSqlColumnQualifiedName", "starburstSqlColumnQualifiedName" +) +StarburstDatasetColumn.STARBURST_DATA_PRODUCT_NAME = KeywordField( + "starburstDataProductName", "starburstDataProductName" +) +StarburstDatasetColumn.STARBURST_DATASET_QUALIFIED_NAME = KeywordField( + "starburstDatasetQualifiedName", "starburstDatasetQualifiedName" +) +StarburstDatasetColumn.STARBURST_DATASET_NAME = KeywordField( + "starburstDatasetName", "starburstDatasetName" +) +StarburstDatasetColumn.QUERY_COUNT = NumericField("queryCount", "queryCount") +StarburstDatasetColumn.QUERY_USER_COUNT = NumericField( + "queryUserCount", "queryUserCount" +) +StarburstDatasetColumn.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +StarburstDatasetColumn.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +StarburstDatasetColumn.DATABASE_NAME = KeywordField("databaseName", "databaseName") +StarburstDatasetColumn.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +StarburstDatasetColumn.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +StarburstDatasetColumn.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +StarburstDatasetColumn.TABLE_NAME = KeywordField("tableName", "tableName") +StarburstDatasetColumn.TABLE_QUALIFIED_NAME = KeywordField( + "tableQualifiedName", "tableQualifiedName" +) +StarburstDatasetColumn.VIEW_NAME = KeywordField("viewName", "viewName") +StarburstDatasetColumn.VIEW_QUALIFIED_NAME = KeywordField( + "viewQualifiedName", "viewQualifiedName" +) +StarburstDatasetColumn.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +StarburstDatasetColumn.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +StarburstDatasetColumn.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +StarburstDatasetColumn.LAST_PROFILED_AT = NumericField( + "lastProfiledAt", "lastProfiledAt" +) +StarburstDatasetColumn.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +StarburstDatasetColumn.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +StarburstDatasetColumn.DATA_TYPE = KeywordTextField( + "dataType", "dataType", "dataType.text" +) +StarburstDatasetColumn.SUB_DATA_TYPE = KeywordField("subDataType", "subDataType") +StarburstDatasetColumn.COLUMN_COMPRESSION = KeywordField( + "columnCompression", "columnCompression" +) +StarburstDatasetColumn.COLUMN_ENCODING = KeywordField( + "columnEncoding", "columnEncoding" +) +StarburstDatasetColumn.RAW_DATA_TYPE_DEFINITION = KeywordField( + "rawDataTypeDefinition", "rawDataTypeDefinition" +) +StarburstDatasetColumn.ORDER = NumericField("order", "order") +StarburstDatasetColumn.NESTED_COLUMN_ORDER = KeywordTextField( + "nestedColumnOrder", "nestedColumnOrder", "nestedColumnOrder.text" +) +StarburstDatasetColumn.NESTED_COLUMN_COUNT = NumericField( + "nestedColumnCount", "nestedColumnCount" +) +StarburstDatasetColumn.COLUMN_HIERARCHY = KeywordField( + "columnHierarchy", "columnHierarchy" +) +StarburstDatasetColumn.IS_PARTITION = BooleanField("isPartition", "isPartition") +StarburstDatasetColumn.PARTITION_ORDER = NumericField( + "partitionOrder", "partitionOrder" +) +StarburstDatasetColumn.IS_CLUSTERED = BooleanField("isClustered", "isClustered") +StarburstDatasetColumn.IS_PRIMARY = BooleanField("isPrimary", "isPrimary") +StarburstDatasetColumn.IS_FOREIGN = BooleanField("isForeign", "isForeign") +StarburstDatasetColumn.IS_INDEXED = BooleanField("isIndexed", "isIndexed") +StarburstDatasetColumn.IS_SORT = BooleanField("isSort", "isSort") +StarburstDatasetColumn.IS_DIST = BooleanField("isDist", "isDist") +StarburstDatasetColumn.IS_PINNED = BooleanField("isPinned", "isPinned") +StarburstDatasetColumn.PINNED_BY = KeywordField("pinnedBy", "pinnedBy") +StarburstDatasetColumn.PINNED_AT = NumericField("pinnedAt", "pinnedAt") +StarburstDatasetColumn.PRECISION = NumericField("precision", "precision") +StarburstDatasetColumn.DEFAULT_VALUE = KeywordField("defaultValue", "defaultValue") +StarburstDatasetColumn.IS_NULLABLE = BooleanField("isNullable", "isNullable") +StarburstDatasetColumn.NUMERIC_SCALE = NumericField("numericScale", "numericScale") +StarburstDatasetColumn.MAX_LENGTH = NumericField("maxLength", "maxLength") +StarburstDatasetColumn.VALIDATIONS = KeywordField("validations", "validations") +StarburstDatasetColumn.PARENT_COLUMN_QUALIFIED_NAME = KeywordTextField( + "parentColumnQualifiedName", + "parentColumnQualifiedName", + "parentColumnQualifiedName.text", +) +StarburstDatasetColumn.PARENT_COLUMN_NAME = KeywordField( + "parentColumnName", "parentColumnName" +) +StarburstDatasetColumn.COLUMN_DISTINCT_VALUES_COUNT = NumericField( + "columnDistinctValuesCount", "columnDistinctValuesCount" +) +StarburstDatasetColumn.COLUMN_DISTINCT_VALUES_COUNT_LONG = NumericField( + "columnDistinctValuesCountLong", "columnDistinctValuesCountLong" +) +StarburstDatasetColumn.COLUMN_HISTOGRAM = KeywordField( + "columnHistogram", "columnHistogram" +) +StarburstDatasetColumn.COLUMN_MAX = NumericField("columnMax", "columnMax") +StarburstDatasetColumn.COLUMN_MIN = NumericField("columnMin", "columnMin") +StarburstDatasetColumn.COLUMN_MEAN = NumericField("columnMean", "columnMean") +StarburstDatasetColumn.COLUMN_SUM = NumericField("columnSum", "columnSum") +StarburstDatasetColumn.COLUMN_MEDIAN = NumericField("columnMedian", "columnMedian") +StarburstDatasetColumn.COLUMN_STANDARD_DEVIATION = NumericField( + "columnStandardDeviation", "columnStandardDeviation" +) +StarburstDatasetColumn.COLUMN_UNIQUE_VALUES_COUNT = NumericField( + "columnUniqueValuesCount", "columnUniqueValuesCount" +) +StarburstDatasetColumn.COLUMN_UNIQUE_VALUES_COUNT_LONG = NumericField( + "columnUniqueValuesCountLong", "columnUniqueValuesCountLong" +) +StarburstDatasetColumn.COLUMN_AVERAGE = NumericField("columnAverage", "columnAverage") +StarburstDatasetColumn.COLUMN_AVERAGE_LENGTH = NumericField( + "columnAverageLength", "columnAverageLength" +) +StarburstDatasetColumn.COLUMN_DUPLICATE_VALUES_COUNT = NumericField( + "columnDuplicateValuesCount", "columnDuplicateValuesCount" +) +StarburstDatasetColumn.COLUMN_DUPLICATE_VALUES_COUNT_LONG = NumericField( + "columnDuplicateValuesCountLong", "columnDuplicateValuesCountLong" +) +StarburstDatasetColumn.COLUMN_MAXIMUM_STRING_LENGTH = NumericField( + "columnMaximumStringLength", "columnMaximumStringLength" +) +StarburstDatasetColumn.COLUMN_MAXS = KeywordField("columnMaxs", "columnMaxs") +StarburstDatasetColumn.COLUMN_MINIMUM_STRING_LENGTH = NumericField( + "columnMinimumStringLength", "columnMinimumStringLength" +) +StarburstDatasetColumn.COLUMN_MINS = KeywordField("columnMins", "columnMins") +StarburstDatasetColumn.COLUMN_MISSING_VALUES_COUNT = NumericField( + "columnMissingValuesCount", "columnMissingValuesCount" +) +StarburstDatasetColumn.COLUMN_MISSING_VALUES_COUNT_LONG = NumericField( + "columnMissingValuesCountLong", "columnMissingValuesCountLong" +) +StarburstDatasetColumn.COLUMN_MISSING_VALUES_PERCENTAGE = NumericField( + "columnMissingValuesPercentage", "columnMissingValuesPercentage" +) +StarburstDatasetColumn.COLUMN_UNIQUENESS_PERCENTAGE = NumericField( + "columnUniquenessPercentage", "columnUniquenessPercentage" +) +StarburstDatasetColumn.COLUMN_VARIANCE = NumericField( + "columnVariance", "columnVariance" +) +StarburstDatasetColumn.COLUMN_TOP_VALUES = KeywordField( + "columnTopValues", "columnTopValues" +) +StarburstDatasetColumn.COLUMN_MAX_VALUE = NumericField( + "columnMaxValue", "columnMaxValue" +) +StarburstDatasetColumn.COLUMN_MIN_VALUE = NumericField( + "columnMinValue", "columnMinValue" +) +StarburstDatasetColumn.COLUMN_MEAN_VALUE = NumericField( + "columnMeanValue", "columnMeanValue" +) +StarburstDatasetColumn.COLUMN_SUM_VALUE = NumericField( + "columnSumValue", "columnSumValue" +) +StarburstDatasetColumn.COLUMN_MEDIAN_VALUE = NumericField( + "columnMedianValue", "columnMedianValue" +) +StarburstDatasetColumn.COLUMN_STANDARD_DEVIATION_VALUE = NumericField( + "columnStandardDeviationValue", "columnStandardDeviationValue" +) +StarburstDatasetColumn.COLUMN_AVERAGE_VALUE = NumericField( + "columnAverageValue", "columnAverageValue" +) +StarburstDatasetColumn.COLUMN_VARIANCE_VALUE = NumericField( + "columnVarianceValue", "columnVarianceValue" +) +StarburstDatasetColumn.COLUMN_AVERAGE_LENGTH_VALUE = NumericField( + "columnAverageLengthValue", "columnAverageLengthValue" +) +StarburstDatasetColumn.COLUMN_DISTRIBUTION_HISTOGRAM = KeywordField( + "columnDistributionHistogram", "columnDistributionHistogram" +) +StarburstDatasetColumn.COLUMN_DEPTH_LEVEL = NumericField( + "columnDepthLevel", "columnDepthLevel" +) +StarburstDatasetColumn.NOSQL_COLLECTION_NAME = KeywordField( + "nosqlCollectionName", "nosqlCollectionName" +) +StarburstDatasetColumn.NOSQL_COLLECTION_QUALIFIED_NAME = KeywordField( + "nosqlCollectionQualifiedName", "nosqlCollectionQualifiedName" +) +StarburstDatasetColumn.COLUMN_IS_MEASURE = BooleanField( + "columnIsMeasure", "columnIsMeasure" +) +StarburstDatasetColumn.COLUMN_MEASURE_TYPE = KeywordField( + "columnMeasureType", "columnMeasureType" +) +StarburstDatasetColumn.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +StarburstDatasetColumn.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +StarburstDatasetColumn.ANOMALO_CHECKS = RelationField("anomaloChecks") +StarburstDatasetColumn.APPLICATION = RelationField("application") +StarburstDatasetColumn.APPLICATION_FIELD = RelationField("applicationField") +StarburstDatasetColumn.COSMOS_MONGO_DB_COLLECTION = RelationField( + "cosmosMongoDBCollection" +) +StarburstDatasetColumn.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +StarburstDatasetColumn.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +StarburstDatasetColumn.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +StarburstDatasetColumn.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +StarburstDatasetColumn.METRICS = RelationField("metrics") +StarburstDatasetColumn.METRIC_TIMESTAMPS = RelationField("metricTimestamps") +StarburstDatasetColumn.DATA_QUALITY_METRIC_DIMENSIONS = RelationField( + "dataQualityMetricDimensions" +) +StarburstDatasetColumn.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +StarburstDatasetColumn.DQ_BASE_COLUMN_RULES = RelationField("dqBaseColumnRules") +StarburstDatasetColumn.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +StarburstDatasetColumn.DQ_REFERENCE_COLUMN_RULES = RelationField( + "dqReferenceColumnRules" +) +StarburstDatasetColumn.DBT_MODELS = RelationField("dbtModels") +StarburstDatasetColumn.SQL_DBT_MODELS = RelationField("sqlDbtModels") +StarburstDatasetColumn.DBT_TESTS = RelationField("dbtTests") +StarburstDatasetColumn.DBT_SOURCES = RelationField("dbtSources") +StarburstDatasetColumn.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +StarburstDatasetColumn.DBT_METRICS = RelationField("dbtMetrics") +StarburstDatasetColumn.DBT_MODEL_COLUMNS = RelationField("dbtModelColumns") +StarburstDatasetColumn.COLUMN_DBT_MODEL_COLUMNS = RelationField("columnDbtModelColumns") +StarburstDatasetColumn.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +StarburstDatasetColumn.MEANINGS = RelationField("meanings") +StarburstDatasetColumn.MONGO_DB_COLLECTION = RelationField("mongoDBCollection") +StarburstDatasetColumn.MC_MONITORS = RelationField("mcMonitors") +StarburstDatasetColumn.MC_INCIDENTS = RelationField("mcIncidents") +StarburstDatasetColumn.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +StarburstDatasetColumn.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +StarburstDatasetColumn.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +StarburstDatasetColumn.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +StarburstDatasetColumn.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +StarburstDatasetColumn.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +StarburstDatasetColumn.FILES = RelationField("files") +StarburstDatasetColumn.LINKS = RelationField("links") +StarburstDatasetColumn.README = RelationField("readme") +StarburstDatasetColumn.TABLE = RelationField("table") +StarburstDatasetColumn.NESTED_COLUMNS = RelationField("nestedColumns") +StarburstDatasetColumn.PARENT_COLUMN = RelationField("parentColumn") +StarburstDatasetColumn.TABLE_PARTITION = RelationField("tablePartition") +StarburstDatasetColumn.VIEW = RelationField("view") +StarburstDatasetColumn.CALCULATION_VIEW = RelationField("calculationView") +StarburstDatasetColumn.MATERIALISED_VIEW = RelationField("materialisedView") +StarburstDatasetColumn.FOREIGN_KEY_TO = RelationField("foreignKeyTo") +StarburstDatasetColumn.FOREIGN_KEY_FROM = RelationField("foreignKeyFrom") +StarburstDatasetColumn.QUERIES = RelationField("queries") +StarburstDatasetColumn.SCHEMA_REGISTRY_SUBJECTS = RelationField( + "schemaRegistrySubjects" +) +StarburstDatasetColumn.SNOWFLAKE_DYNAMIC_TABLE = RelationField("snowflakeDynamicTable") +StarburstDatasetColumn.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +StarburstDatasetColumn.SODA_CHECKS = RelationField("sodaChecks") +StarburstDatasetColumn.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +StarburstDatasetColumn.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") +StarburstDatasetColumn.STARBURST_DATASET = RelationField("starburstDataset") diff --git a/pyatlan_v9/model/assets/starburst_related.py b/pyatlan_v9/model/assets/starburst_related.py new file mode 100644 index 000000000..1a902d7dd --- /dev/null +++ b/pyatlan_v9/model/assets/starburst_related.py @@ -0,0 +1,91 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Starburst module. + +This module contains all Related{Type} classes for the Starburst type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Union + +from msgspec import UNSET, UnsetType + +from .referenceable_related import RelatedReferenceable +from .sql_related import RelatedSQL + +__all__ = [ + "RelatedStarburst", + "RelatedStarburstDataset", + "RelatedStarburstDatasetColumn", +] + + +class RelatedStarburst(RelatedSQL): + """ + Related entity reference for Starburst assets. + + Extends RelatedSQL with Starburst-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Starburst" so it serializes correctly + + starburst_data_product_name: Union[str, None, UnsetType] = UNSET + """Name of the Starburst Data Product that contains this asset.""" + + starburst_dataset_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the Starburst Dataset that contains this asset, or this asset's own qualified name if it is a Dataset.""" + + starburst_dataset_name: Union[str, None, UnsetType] = UNSET + """Simple name of the Starburst Dataset that contains this asset, or this asset's own name if it is a Dataset.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Starburst" + + +class RelatedStarburstDataset(RelatedStarburst): + """ + Related entity reference for StarburstDataset assets. + + Extends RelatedStarburst with StarburstDataset-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "StarburstDataset" so it serializes correctly + + starburst_is_materialized: Union[bool, None, UnsetType] = UNSET + """Whether this dataset is a materialized view.""" + + starburst_sql_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the corresponding SQL View or MaterialisedView. Enables cross-stream lookup between the Data Product perspective and the SQL perspective of the same underlying view.""" + + starburst_view_definition: Union[str, None, UnsetType] = UNSET + """SQL definition of the underlying view or materialized view.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "StarburstDataset" + + +class RelatedStarburstDatasetColumn(RelatedStarburst): + """ + Related entity reference for StarburstDatasetColumn assets. + + Extends RelatedStarburst with StarburstDatasetColumn-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "StarburstDatasetColumn" so it serializes correctly + + starburst_sql_column_qualified_name: Union[str, None, UnsetType] = UNSET + """Qualified name of the corresponding SQL Column. Enables cross-stream lookup between the Data Product perspective and the SQL perspective of the same underlying column.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "StarburstDatasetColumn" diff --git a/pyatlan_v9/model/assets/superset.py b/pyatlan_v9/model/assets/superset.py new file mode 100644 index 000000000..d00e97f36 --- /dev/null +++ b/pyatlan_v9/model/assets/superset.py @@ -0,0 +1,142 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Superset asset model with flattened inheritance.""" + +from __future__ import annotations + +from typing import Union + +from msgspec import UNSET, UnsetType + +from pyatlan_v9.model.conversion_utils import ( + build_attributes_kwargs, + build_flat_kwargs, + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .bi import BI, BIAttributes, BINested, BIRelationshipAttributes + + +@register_asset +class Superset(BI): + """Base class for Superset assets.""" + + type_name: Union[str, UnsetType] = "Superset" + + superset_dashboard_id: Union[int, None, UnsetType] = UNSET + """Identifier of the dashboard in Superset.""" + + superset_dashboard_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dashboard in which this asset exists.""" + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """Convert to JSON string.""" + if serde is None: + serde = get_serde() + if nested: + return _superset_to_nested_bytes(self, serde).decode("utf-8") + return serde.encode(self).decode("utf-8") + + @staticmethod + def from_json( + json_data: Union[str, bytes], serde: Serde | None = None + ) -> "Superset": + """Create from JSON string or bytes.""" + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _superset_from_nested_bytes(json_data, serde) + + +class SupersetAttributes(BIAttributes): + """Superset-specific attributes for nested API format.""" + + superset_dashboard_id: Union[int, None, UnsetType] = UNSET + superset_dashboard_qualified_name: Union[str, None, UnsetType] = UNSET + + +class SupersetRelationshipAttributes(BIRelationshipAttributes): + """Superset-specific relationship attributes for nested API format.""" + + pass + + +class SupersetNested(BINested): + """Superset in nested API format for high-performance serialization.""" + + attributes: Union[SupersetAttributes, UnsetType] = UNSET + relationship_attributes: Union[SupersetRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[SupersetRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[SupersetRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +def _superset_to_nested(superset: Superset) -> SupersetNested: + """Convert flat Superset to nested format.""" + attrs_kwargs = build_attributes_kwargs(superset, SupersetAttributes) + attrs = SupersetAttributes(**attrs_kwargs) + rel_fields: list[str] = [] + replace_rels, append_rels, remove_rels = categorize_relationships( + superset, rel_fields, SupersetRelationshipAttributes + ) + return SupersetNested( + guid=superset.guid, + type_name=superset.type_name, + status=superset.status, + version=superset.version, + create_time=superset.create_time, + update_time=superset.update_time, + created_by=superset.created_by, + updated_by=superset.updated_by, + classifications=superset.classifications, + classification_names=superset.classification_names, + meanings=superset.meanings, + labels=superset.labels, + business_attributes=superset.business_attributes, + custom_attributes=superset.custom_attributes, + pending_tasks=superset.pending_tasks, + proxy=superset.proxy, + is_incomplete=superset.is_incomplete, + provenance_type=superset.provenance_type, + home_id=superset.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _superset_from_nested(nested: SupersetNested) -> Superset: + """Convert nested format to flat Superset.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else SupersetAttributes() + ) + rel_fields: list[str] = [] + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + rel_fields, + SupersetRelationshipAttributes, + ) + kwargs = build_flat_kwargs(nested, attrs, merged_rels, BINested, SupersetAttributes) + return Superset(**kwargs) + + +def _superset_to_nested_bytes(superset: Superset, serde: Serde) -> bytes: + """Convert flat Superset to nested JSON bytes.""" + return serde.encode(_superset_to_nested(superset)) + + +def _superset_from_nested_bytes(data: bytes, serde: Serde) -> Superset: + """Convert nested JSON bytes to flat Superset.""" + nested = serde.decode(data, SupersetNested) + return _superset_from_nested(nested) diff --git a/pyatlan_v9/model/assets/superset_chart.py b/pyatlan_v9/model/assets/superset_chart.py new file mode 100644 index 000000000..d40e14a1d --- /dev/null +++ b/pyatlan_v9/model/assets/superset_chart.py @@ -0,0 +1,201 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""SupersetChart asset model with flattened inheritance.""" + +from __future__ import annotations + +from typing import Union + +from msgspec import UNSET, UnsetType + +from pyatlan_v9.model.conversion_utils import ( + build_attributes_kwargs, + build_flat_kwargs, + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .bi import BIAttributes, BINested, BIRelationshipAttributes +from .superset import Superset +from .superset_related import RelatedSupersetDashboard + + +@register_asset +class SupersetChart(Superset): + """Instance of a Superset chart in Atlan.""" + + type_name: Union[str, UnsetType] = "SupersetChart" + + superset_chart_description_markdown: Union[str, None, UnsetType] = UNSET + superset_chart_form_data: Union[dict[str, str], None, UnsetType] = UNSET + + superset_dashboard: Union[RelatedSupersetDashboard, None, UnsetType] = UNSET + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + superset_dashboard_qualified_name: str, + connection_qualified_name: Union[str, None] = None, + ) -> "SupersetChart": + """Create a new SupersetChart asset.""" + validate_required_fields( + ["name", "superset_dashboard_qualified_name"], + [name, superset_dashboard_qualified_name], + ) + if connection_qualified_name: + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + else: + fields = superset_dashboard_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + connection_qualified_name = ( + "/".join(fields[:3]) + if len(fields) >= 3 + else superset_dashboard_qualified_name + ) + return cls( + name=name, + superset_dashboard_qualified_name=superset_dashboard_qualified_name, + connection_qualified_name=connection_qualified_name, + qualified_name=f"{superset_dashboard_qualified_name}/{name}", + connector_name=connector_name, + superset_dashboard=RelatedSupersetDashboard( + unique_attributes={"qualifiedName": superset_dashboard_qualified_name} + ), + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "SupersetChart": + """Create a SupersetChart instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "SupersetChart": + """Return only fields required for update operations.""" + return SupersetChart.updater(qualified_name=self.qualified_name, name=self.name) + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """Convert to JSON string.""" + if serde is None: + serde = get_serde() + if nested: + return _superset_chart_to_nested_bytes(self, serde).decode("utf-8") + return serde.encode(self).decode("utf-8") + + @staticmethod + def from_json( + json_data: Union[str, bytes], serde: Serde | None = None + ) -> "SupersetChart": + """Create from JSON string or bytes.""" + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _superset_chart_from_nested_bytes(json_data, serde) + + +class SupersetChartAttributes(BIAttributes): + """SupersetChart-specific attributes for nested API format.""" + + superset_dashboard_id: Union[int, None, UnsetType] = UNSET + superset_dashboard_qualified_name: Union[str, None, UnsetType] = UNSET + + superset_chart_description_markdown: Union[str, None, UnsetType] = UNSET + superset_chart_form_data: Union[dict[str, str], None, UnsetType] = UNSET + + +class SupersetChartRelationshipAttributes(BIRelationshipAttributes): + """SupersetChart-specific relationship attributes for nested API format.""" + + superset_dashboard: Union[RelatedSupersetDashboard, None, UnsetType] = UNSET + + +class SupersetChartNested(BINested): + """SupersetChart in nested API format for high-performance serialization.""" + + attributes: Union[SupersetChartAttributes, UnsetType] = UNSET + relationship_attributes: Union[SupersetChartRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + SupersetChartRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SupersetChartRelationshipAttributes, UnsetType + ] = UNSET + + +def _superset_chart_to_nested(superset_chart: SupersetChart) -> SupersetChartNested: + """Convert flat SupersetChart to nested format.""" + attrs_kwargs = build_attributes_kwargs(superset_chart, SupersetChartAttributes) + attrs = SupersetChartAttributes(**attrs_kwargs) + rel_fields: list[str] = ["superset_dashboard"] + replace_rels, append_rels, remove_rels = categorize_relationships( + superset_chart, rel_fields, SupersetChartRelationshipAttributes + ) + return SupersetChartNested( + guid=superset_chart.guid, + type_name=superset_chart.type_name, + status=superset_chart.status, + version=superset_chart.version, + create_time=superset_chart.create_time, + update_time=superset_chart.update_time, + created_by=superset_chart.created_by, + updated_by=superset_chart.updated_by, + classifications=superset_chart.classifications, + classification_names=superset_chart.classification_names, + meanings=superset_chart.meanings, + labels=superset_chart.labels, + business_attributes=superset_chart.business_attributes, + custom_attributes=superset_chart.custom_attributes, + pending_tasks=superset_chart.pending_tasks, + proxy=superset_chart.proxy, + is_incomplete=superset_chart.is_incomplete, + provenance_type=superset_chart.provenance_type, + home_id=superset_chart.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _superset_chart_from_nested(nested: SupersetChartNested) -> SupersetChart: + """Convert nested format to flat SupersetChart.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else SupersetChartAttributes() + ) + rel_fields: list[str] = ["superset_dashboard"] + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + rel_fields, + SupersetChartRelationshipAttributes, + ) + kwargs = build_flat_kwargs( + nested, attrs, merged_rels, BINested, SupersetChartAttributes + ) + return SupersetChart(**kwargs) + + +def _superset_chart_to_nested_bytes( + superset_chart: SupersetChart, serde: Serde +) -> bytes: + """Convert flat SupersetChart to nested JSON bytes.""" + return serde.encode(_superset_chart_to_nested(superset_chart)) + + +def _superset_chart_from_nested_bytes(data: bytes, serde: Serde) -> SupersetChart: + """Convert nested JSON bytes to flat SupersetChart.""" + nested = serde.decode(data, SupersetChartNested) + return _superset_chart_from_nested(nested) diff --git a/pyatlan_v9/model/assets/superset_dashboard.py b/pyatlan_v9/model/assets/superset_dashboard.py new file mode 100644 index 000000000..146f28cf9 --- /dev/null +++ b/pyatlan_v9/model/assets/superset_dashboard.py @@ -0,0 +1,203 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""SupersetDashboard asset model with flattened inheritance.""" + +from __future__ import annotations + +from typing import Union + +from msgspec import UNSET, UnsetType + +from pyatlan_v9.model.conversion_utils import ( + build_attributes_kwargs, + build_flat_kwargs, + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .bi import BIAttributes, BINested, BIRelationshipAttributes +from .superset import Superset +from .superset_related import RelatedSupersetChart, RelatedSupersetDataset + + +@register_asset +class SupersetDashboard(Superset): + """Instance of a Superset dashboard in Atlan.""" + + type_name: Union[str, UnsetType] = "SupersetDashboard" + + superset_dashboard_changed_by_name: Union[str, None, UnsetType] = UNSET + superset_dashboard_changed_by_url: Union[str, None, UnsetType] = UNSET + superset_dashboard_is_managed_externally: Union[bool, None, UnsetType] = UNSET + superset_dashboard_is_published: Union[bool, None, UnsetType] = UNSET + superset_dashboard_thumbnail_url: Union[str, None, UnsetType] = UNSET + superset_dashboard_chart_count: Union[int, None, UnsetType] = UNSET + + superset_datasets: Union[list[RelatedSupersetDataset], None, UnsetType] = UNSET + superset_charts: Union[list[RelatedSupersetChart], None, UnsetType] = UNSET + + @classmethod + @init_guid + def creator( + cls, *, name: str, connection_qualified_name: str + ) -> "SupersetDashboard": + """Create a new SupersetDashboard asset.""" + validate_required_fields( + ["name", "connection_qualified_name"], [name, connection_qualified_name] + ) + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + return cls( + name=name, + qualified_name=f"{connection_qualified_name}/{name}", + connection_qualified_name=connection_qualified_name, + connector_name=connector_name, + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "SupersetDashboard": + """Create a SupersetDashboard instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "SupersetDashboard": + """Return only fields required for update operations.""" + return SupersetDashboard.updater( + qualified_name=self.qualified_name, name=self.name + ) + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """Convert to JSON string.""" + if serde is None: + serde = get_serde() + if nested: + return _superset_dashboard_to_nested_bytes(self, serde).decode("utf-8") + return serde.encode(self).decode("utf-8") + + @staticmethod + def from_json( + json_data: Union[str, bytes], serde: Serde | None = None + ) -> "SupersetDashboard": + """Create from JSON string or bytes.""" + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _superset_dashboard_from_nested_bytes(json_data, serde) + + +class SupersetDashboardAttributes(BIAttributes): + """SupersetDashboard-specific attributes for nested API format.""" + + superset_dashboard_id: Union[int, None, UnsetType] = UNSET + superset_dashboard_qualified_name: Union[str, None, UnsetType] = UNSET + + superset_dashboard_changed_by_name: Union[str, None, UnsetType] = UNSET + superset_dashboard_changed_by_url: Union[str, None, UnsetType] = UNSET + superset_dashboard_is_managed_externally: Union[bool, None, UnsetType] = UNSET + superset_dashboard_is_published: Union[bool, None, UnsetType] = UNSET + superset_dashboard_thumbnail_url: Union[str, None, UnsetType] = UNSET + superset_dashboard_chart_count: Union[int, None, UnsetType] = UNSET + + +class SupersetDashboardRelationshipAttributes(BIRelationshipAttributes): + """SupersetDashboard-specific relationship attributes for nested API format.""" + + superset_datasets: Union[list[RelatedSupersetDataset], None, UnsetType] = UNSET + superset_charts: Union[list[RelatedSupersetChart], None, UnsetType] = UNSET + + +class SupersetDashboardNested(BINested): + """SupersetDashboard in nested API format for high-performance serialization.""" + + attributes: Union[SupersetDashboardAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + SupersetDashboardRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + SupersetDashboardRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SupersetDashboardRelationshipAttributes, UnsetType + ] = UNSET + + +def _superset_dashboard_to_nested( + superset_dashboard: SupersetDashboard, +) -> SupersetDashboardNested: + """Convert flat SupersetDashboard to nested format.""" + attrs_kwargs = build_attributes_kwargs( + superset_dashboard, SupersetDashboardAttributes + ) + attrs = SupersetDashboardAttributes(**attrs_kwargs) + rel_fields: list[str] = ["superset_datasets", "superset_charts"] + replace_rels, append_rels, remove_rels = categorize_relationships( + superset_dashboard, rel_fields, SupersetDashboardRelationshipAttributes + ) + return SupersetDashboardNested( + guid=superset_dashboard.guid, + type_name=superset_dashboard.type_name, + status=superset_dashboard.status, + version=superset_dashboard.version, + create_time=superset_dashboard.create_time, + update_time=superset_dashboard.update_time, + created_by=superset_dashboard.created_by, + updated_by=superset_dashboard.updated_by, + classifications=superset_dashboard.classifications, + classification_names=superset_dashboard.classification_names, + meanings=superset_dashboard.meanings, + labels=superset_dashboard.labels, + business_attributes=superset_dashboard.business_attributes, + custom_attributes=superset_dashboard.custom_attributes, + pending_tasks=superset_dashboard.pending_tasks, + proxy=superset_dashboard.proxy, + is_incomplete=superset_dashboard.is_incomplete, + provenance_type=superset_dashboard.provenance_type, + home_id=superset_dashboard.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _superset_dashboard_from_nested( + nested: SupersetDashboardNested, +) -> SupersetDashboard: + """Convert nested format to flat SupersetDashboard.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else SupersetDashboardAttributes() + ) + rel_fields: list[str] = ["superset_datasets", "superset_charts"] + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + rel_fields, + SupersetDashboardRelationshipAttributes, + ) + kwargs = build_flat_kwargs( + nested, attrs, merged_rels, BINested, SupersetDashboardAttributes + ) + return SupersetDashboard(**kwargs) + + +def _superset_dashboard_to_nested_bytes( + superset_dashboard: SupersetDashboard, serde: Serde +) -> bytes: + """Convert flat SupersetDashboard to nested JSON bytes.""" + return serde.encode(_superset_dashboard_to_nested(superset_dashboard)) + + +def _superset_dashboard_from_nested_bytes( + data: bytes, serde: Serde +) -> SupersetDashboard: + """Convert nested JSON bytes to flat SupersetDashboard.""" + nested = serde.decode(data, SupersetDashboardNested) + return _superset_dashboard_from_nested(nested) diff --git a/pyatlan_v9/model/assets/superset_dataset.py b/pyatlan_v9/model/assets/superset_dataset.py new file mode 100644 index 000000000..d38fc181b --- /dev/null +++ b/pyatlan_v9/model/assets/superset_dataset.py @@ -0,0 +1,207 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""SupersetDataset asset model with flattened inheritance.""" + +from __future__ import annotations + +from typing import Union + +from msgspec import UNSET, UnsetType + +from pyatlan_v9.model.conversion_utils import ( + build_attributes_kwargs, + build_flat_kwargs, + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .bi import BIAttributes, BINested, BIRelationshipAttributes +from .superset import Superset +from .superset_related import RelatedSupersetDashboard + + +@register_asset +class SupersetDataset(Superset): + """Instance of a Superset dataset in Atlan.""" + + type_name: Union[str, UnsetType] = "SupersetDataset" + + superset_dataset_datasource_name: Union[str, None, UnsetType] = UNSET + superset_dataset_id: Union[int, None, UnsetType] = UNSET + superset_dataset_type: Union[str, None, UnsetType] = UNSET + + superset_dashboard: Union[RelatedSupersetDashboard, None, UnsetType] = UNSET + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + superset_dashboard_qualified_name: str, + connection_qualified_name: Union[str, None] = None, + ) -> "SupersetDataset": + """Create a new SupersetDataset asset.""" + validate_required_fields( + ["name", "superset_dashboard_qualified_name"], + [name, superset_dashboard_qualified_name], + ) + if connection_qualified_name: + fields = connection_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + else: + fields = superset_dashboard_qualified_name.split("/") + connector_name = fields[1] if len(fields) > 1 else None + connection_qualified_name = ( + "/".join(fields[:3]) + if len(fields) >= 3 + else superset_dashboard_qualified_name + ) + return cls( + name=name, + superset_dashboard_qualified_name=superset_dashboard_qualified_name, + connection_qualified_name=connection_qualified_name, + qualified_name=f"{superset_dashboard_qualified_name}/{name}", + connector_name=connector_name, + superset_dashboard=RelatedSupersetDashboard( + unique_attributes={"qualifiedName": superset_dashboard_qualified_name} + ), + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "SupersetDataset": + """Create a SupersetDataset instance for update operations.""" + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "SupersetDataset": + """Return only fields required for update operations.""" + return SupersetDataset.updater( + qualified_name=self.qualified_name, name=self.name + ) + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """Convert to JSON string.""" + if serde is None: + serde = get_serde() + if nested: + return _superset_dataset_to_nested_bytes(self, serde).decode("utf-8") + return serde.encode(self).decode("utf-8") + + @staticmethod + def from_json( + json_data: Union[str, bytes], serde: Serde | None = None + ) -> "SupersetDataset": + """Create from JSON string or bytes.""" + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _superset_dataset_from_nested_bytes(json_data, serde) + + +class SupersetDatasetAttributes(BIAttributes): + """SupersetDataset-specific attributes for nested API format.""" + + superset_dashboard_id: Union[int, None, UnsetType] = UNSET + superset_dashboard_qualified_name: Union[str, None, UnsetType] = UNSET + + superset_dataset_datasource_name: Union[str, None, UnsetType] = UNSET + superset_dataset_id: Union[int, None, UnsetType] = UNSET + superset_dataset_type: Union[str, None, UnsetType] = UNSET + + +class SupersetDatasetRelationshipAttributes(BIRelationshipAttributes): + """SupersetDataset-specific relationship attributes for nested API format.""" + + superset_dashboard: Union[RelatedSupersetDashboard, None, UnsetType] = UNSET + + +class SupersetDatasetNested(BINested): + """SupersetDataset in nested API format for high-performance serialization.""" + + attributes: Union[SupersetDatasetAttributes, UnsetType] = UNSET + relationship_attributes: Union[SupersetDatasetRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + SupersetDatasetRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + SupersetDatasetRelationshipAttributes, UnsetType + ] = UNSET + + +def _superset_dataset_to_nested( + superset_dataset: SupersetDataset, +) -> SupersetDatasetNested: + """Convert flat SupersetDataset to nested format.""" + attrs_kwargs = build_attributes_kwargs(superset_dataset, SupersetDatasetAttributes) + attrs = SupersetDatasetAttributes(**attrs_kwargs) + rel_fields: list[str] = ["superset_dashboard"] + replace_rels, append_rels, remove_rels = categorize_relationships( + superset_dataset, rel_fields, SupersetDatasetRelationshipAttributes + ) + return SupersetDatasetNested( + guid=superset_dataset.guid, + type_name=superset_dataset.type_name, + status=superset_dataset.status, + version=superset_dataset.version, + create_time=superset_dataset.create_time, + update_time=superset_dataset.update_time, + created_by=superset_dataset.created_by, + updated_by=superset_dataset.updated_by, + classifications=superset_dataset.classifications, + classification_names=superset_dataset.classification_names, + meanings=superset_dataset.meanings, + labels=superset_dataset.labels, + business_attributes=superset_dataset.business_attributes, + custom_attributes=superset_dataset.custom_attributes, + pending_tasks=superset_dataset.pending_tasks, + proxy=superset_dataset.proxy, + is_incomplete=superset_dataset.is_incomplete, + provenance_type=superset_dataset.provenance_type, + home_id=superset_dataset.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _superset_dataset_from_nested(nested: SupersetDatasetNested) -> SupersetDataset: + """Convert nested format to flat SupersetDataset.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else SupersetDatasetAttributes() + ) + rel_fields: list[str] = ["superset_dashboard"] + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + rel_fields, + SupersetDatasetRelationshipAttributes, + ) + kwargs = build_flat_kwargs( + nested, attrs, merged_rels, BINested, SupersetDatasetAttributes + ) + return SupersetDataset(**kwargs) + + +def _superset_dataset_to_nested_bytes( + superset_dataset: SupersetDataset, serde: Serde +) -> bytes: + """Convert flat SupersetDataset to nested JSON bytes.""" + return serde.encode(_superset_dataset_to_nested(superset_dataset)) + + +def _superset_dataset_from_nested_bytes(data: bytes, serde: Serde) -> SupersetDataset: + """Convert nested JSON bytes to flat SupersetDataset.""" + nested = serde.decode(data, SupersetDatasetNested) + return _superset_dataset_from_nested(nested) diff --git a/pyatlan_v9/model/assets/superset_related.py b/pyatlan_v9/model/assets/superset_related.py new file mode 100644 index 000000000..067dac67f --- /dev/null +++ b/pyatlan_v9/model/assets/superset_related.py @@ -0,0 +1,128 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Superset module. + +This module contains all Related{Type} classes for the Superset type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Union + +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedBI +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedSuperset", + "RelatedSupersetChart", + "RelatedSupersetDashboard", + "RelatedSupersetDataset", +] + + +class RelatedSuperset(RelatedBI): + """ + Related entity reference for Superset assets. + + Extends RelatedBI with Superset-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Superset" so it serializes correctly + + superset_dashboard_id: Union[int, None, UnsetType] = UNSET + """Identifier of the dashboard in which this asset exists, in Superset.""" + + superset_dashboard_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the dashboard in which this asset exists.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + if self.type_name is UNSET or self.type_name is None: + self.type_name = "Superset" + + +class RelatedSupersetChart(RelatedSuperset): + """ + Related entity reference for SupersetChart assets. + + Extends RelatedSuperset with SupersetChart-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SupersetChart" so it serializes correctly + + superset_chart_description_markdown: Union[str, None, UnsetType] = UNSET + """Description markdown of the chart.""" + + superset_chart_form_data: Union[dict[str, str], None, UnsetType] = UNSET + """Data stored for the chart in key value pairs.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + if self.type_name is UNSET or self.type_name is None: + self.type_name = "SupersetChart" + + +class RelatedSupersetDashboard(RelatedSuperset): + """ + Related entity reference for SupersetDashboard assets. + + Extends RelatedSuperset with SupersetDashboard-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SupersetDashboard" so it serializes correctly + + superset_dashboard_changed_by_name: Union[str, None, UnsetType] = UNSET + """Name of the user who changed the dashboard.""" + + superset_dashboard_changed_by_url: Union[str, None, UnsetType] = UNSET + """URL of the user profile that changed the dashboard.""" + + superset_dashboard_is_managed_externally: Union[bool, None, UnsetType] = UNSET + """Whether the dashboard is managed externally (true) or not (false).""" + + superset_dashboard_is_published: Union[bool, None, UnsetType] = UNSET + """Whether the dashboard is published (true) or not (false).""" + + superset_dashboard_thumbnail_url: Union[str, None, UnsetType] = UNSET + """URL for the dashboard thumbnail image in superset.""" + + superset_dashboard_chart_count: Union[int, None, UnsetType] = UNSET + """Count of charts present in the dashboard.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + if self.type_name is UNSET or self.type_name is None: + self.type_name = "SupersetDashboard" + + +class RelatedSupersetDataset(RelatedSuperset): + """ + Related entity reference for SupersetDataset assets. + + Extends RelatedSuperset with SupersetDataset-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SupersetDataset" so it serializes correctly + + superset_dataset_datasource_name: Union[str, None, UnsetType] = UNSET + """Name of the datasource for the dataset.""" + + superset_dataset_id: Union[int, None, UnsetType] = UNSET + """Id of the dataset in superset.""" + + superset_dataset_type: Union[str, None, UnsetType] = UNSET + """Type of the dataset in superset.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + if self.type_name is UNSET or self.type_name is None: + self.type_name = "SupersetDataset" diff --git a/pyatlan_v9/model/assets/table.py b/pyatlan_v9/model/assets/table.py new file mode 100644 index 000000000..51d672e0a --- /dev/null +++ b/pyatlan_v9/model/assets/table.py @@ -0,0 +1,1233 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Table asset model with flattened inheritance. + +This module provides: +- Table: Flat asset class (easy to use) +- TableAttributes: Nested attributes struct (extends AssetAttributes) +- TableNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .snowflake_related import RelatedSnowflakeSemanticLogicalTable +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .sql_related import ( + RelatedColumn, + RelatedQuery, + RelatedSchema, + RelatedTable, + RelatedTablePartition, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Table(Asset): + """ + Instance of a database table in Atlan. + """ + + COLUMN_COUNT: ClassVar[Any] = None + ROW_COUNT: ClassVar[Any] = None + SIZE_BYTES: ClassVar[Any] = None + SQL_OBJECT_COUNT: ClassVar[Any] = None + ALIAS: ClassVar[Any] = None + IS_TEMPORARY: ClassVar[Any] = None + IS_QUERY_PREVIEW: ClassVar[Any] = None + QUERY_PREVIEW_CONFIG: ClassVar[Any] = None + EXTERNAL_LOCATION: ClassVar[Any] = None + EXTERNAL_LOCATION_REGION: ClassVar[Any] = None + EXTERNAL_LOCATION_FORMAT: ClassVar[Any] = None + IS_PARTITIONED: ClassVar[Any] = None + PARTITION_STRATEGY: ClassVar[Any] = None + PARTITION_COUNT: ClassVar[Any] = None + TABLE_DEFINITION: ClassVar[Any] = None + PARTITION_LIST: ClassVar[Any] = None + IS_SHARDED: ClassVar[Any] = None + SQL_TYPE: ClassVar[Any] = None + ICEBERG_CATALOG_NAME: ClassVar[Any] = None + ICEBERG_TABLE_TYPE: ClassVar[Any] = None + ICEBERG_CATALOG_SOURCE: ClassVar[Any] = None + ICEBERG_CATALOG_TABLE_NAME: ClassVar[Any] = None + SQL_IMPALA_PARAMETERS: ClassVar[Any] = None + ICEBERG_CATALOG_TABLE_NAMESPACE: ClassVar[Any] = None + SQL_EXTERNAL_VOLUME_NAME: ClassVar[Any] = None + ICEBERG_TABLE_BASE_LOCATION: ClassVar[Any] = None + SQL_RETENTION_TIME: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + COLUMNS: ClassVar[Any] = None + QUERIES: ClassVar[Any] = None + ATLAN_SCHEMA: ClassVar[Any] = None + DIMENSIONS: ClassVar[Any] = None + FACTS: ClassVar[Any] = None + PARTITIONS: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Table" + + column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this table.""" + + row_count: Union[int, None, UnsetType] = UNSET + """Number of rows in this table.""" + + size_bytes: Union[int, None, UnsetType] = UNSET + """Size of this table, in bytes.""" + + sql_object_count: Union[int, None, UnsetType] = UNSET + """Number of objects in this table.""" + + alias: Union[str, None, UnsetType] = UNSET + """Alias for this table.""" + + is_temporary: Union[bool, None, UnsetType] = UNSET + """Whether this table is temporary (true) or not (false).""" + + is_query_preview: Union[bool, None, UnsetType] = UNSET + """Whether preview queries are allowed for this table (true) or not (false).""" + + query_preview_config: Union[Dict[str, str], None, UnsetType] = UNSET + """Configuration for preview queries.""" + + external_location: Union[str, None, UnsetType] = UNSET + """External location of this table, for example: an S3 object location.""" + + external_location_region: Union[str, None, UnsetType] = UNSET + """Region of the external location of this table, for example: S3 region.""" + + external_location_format: Union[str, None, UnsetType] = UNSET + """Format of the external location of this table, for example: JSON, CSV, PARQUET, etc.""" + + is_partitioned: Union[bool, None, UnsetType] = UNSET + """Whether this table is partitioned (true) or not (false).""" + + partition_strategy: Union[str, None, UnsetType] = UNSET + """Partition strategy for this table.""" + + partition_count: Union[int, None, UnsetType] = UNSET + """Number of partitions in this table.""" + + table_definition: Union[str, None, UnsetType] = UNSET + """Definition of the table.""" + + partition_list: Union[str, None, UnsetType] = UNSET + """List of partitions in this table.""" + + is_sharded: Union[bool, None, UnsetType] = UNSET + """Whether this table is a sharded table (true) or not (false).""" + + sql_type: Union[str, None, UnsetType] = UNSET + """Type of the table.""" + + iceberg_catalog_name: Union[str, None, UnsetType] = UNSET + """Iceberg table catalog name (can be any user defined name)""" + + iceberg_table_type: Union[str, None, UnsetType] = UNSET + """Iceberg table type (managed vs unmanaged)""" + + iceberg_catalog_source: Union[str, None, UnsetType] = UNSET + """Iceberg table catalog type (glue, polaris, snowflake)""" + + iceberg_catalog_table_name: Union[str, None, UnsetType] = UNSET + """Catalog table name (actual table name on the catalog side).""" + + sql_impala_parameters: Union[Dict[str, str], None, UnsetType] = UNSET + """Extra attributes for Impala""" + + iceberg_catalog_table_namespace: Union[str, None, UnsetType] = UNSET + """Catalog table namespace (actual database name on the catalog side).""" + + sql_external_volume_name: Union[str, None, UnsetType] = UNSET + """External volume name for the table.""" + + iceberg_table_base_location: Union[str, None, UnsetType] = UNSET + """Iceberg table base location inside the external volume.""" + + sql_retention_time: Union[int, None, UnsetType] = UNSET + """Data retention time in days.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Columns that exist within this table.""" + + queries: Union[List[RelatedQuery], None, UnsetType] = UNSET + """Queries that access this table.""" + + atlan_schema: Union[RelatedSchema, None, UnsetType] = UNSET + """Schema in which this table exists.""" + + dimensions: Union[List[RelatedTable], None, UnsetType] = UNSET + """""" + + facts: Union[List[RelatedTable], None, UnsetType] = UNSET + """""" + + partitions: Union[List[RelatedTablePartition], None, UnsetType] = UNSET + """Partitions that exist within this table.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Table" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + schema_qualified_name: str, + schema_name: str | None = None, + database_name: str | None = None, + database_qualified_name: str | None = None, + connection_qualified_name: str | None = None, + ) -> "Table": + """ + Create a new Table asset. + + Args: + name: Name of the table + schema_qualified_name: Unique name of the schema in which this table exists + schema_name: Simple name of the schema (optional, will be derived if not provided) + database_name: Simple name of the database (optional, will be derived if not provided) + database_qualified_name: Unique name of the database (optional, will be derived if not provided) + connection_qualified_name: Unique name of the connection (optional, will be derived if not provided) + + Returns: + Table instance ready to be created + + Raises: + ValueError: If required parameters are missing or invalid + """ + validate_required_fields( + ["name", "schema_qualified_name"], [name, schema_qualified_name] + ) + + fields = schema_qualified_name.split("/") + if len(fields) != 5: + raise ValueError( + f"Invalid schema_qualified_name: {schema_qualified_name}. " + "Expected format: default/connector/connection_id/database/schema" + ) + + connector_name = fields[1] + connection_qn = ( + connection_qualified_name or f"{fields[0]}/{fields[1]}/{fields[2]}" + ) + db_name = database_name or fields[3] + sch_name = schema_name or fields[4] + db_qualified_name = database_qualified_name or f"{connection_qn}/{db_name}" + qualified_name = f"{schema_qualified_name}/{name}" + + return cls( + name=name, + qualified_name=qualified_name, + database_name=db_name, + database_qualified_name=db_qualified_name, + schema_name=sch_name, + schema_qualified_name=schema_qualified_name, + connector_name=connector_name, + connection_qualified_name=connection_qn, + atlan_schema=RelatedSchema(qualified_name=schema_qualified_name), + ) + + @classmethod + def create(cls, *, name: str, schema_qualified_name: str) -> "Table": + """ + Create a new Table asset (deprecated - use creator instead). + + Args: + name: Name of the table + schema_qualified_name: Unique name of the schema in which this table exists + + Returns: + Table instance ready to be created + """ + return cls.creator(name=name, schema_qualified_name=schema_qualified_name) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _table_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Table: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Table instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _table_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class TableAttributes(AssetAttributes): + """Table-specific attributes for nested API format.""" + + column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this table.""" + + row_count: Union[int, None, UnsetType] = UNSET + """Number of rows in this table.""" + + size_bytes: Union[int, None, UnsetType] = UNSET + """Size of this table, in bytes.""" + + sql_object_count: Union[int, None, UnsetType] = UNSET + """Number of objects in this table.""" + + alias: Union[str, None, UnsetType] = UNSET + """Alias for this table.""" + + is_temporary: Union[bool, None, UnsetType] = UNSET + """Whether this table is temporary (true) or not (false).""" + + is_query_preview: Union[bool, None, UnsetType] = UNSET + """Whether preview queries are allowed for this table (true) or not (false).""" + + query_preview_config: Union[Dict[str, str], None, UnsetType] = UNSET + """Configuration for preview queries.""" + + external_location: Union[str, None, UnsetType] = UNSET + """External location of this table, for example: an S3 object location.""" + + external_location_region: Union[str, None, UnsetType] = UNSET + """Region of the external location of this table, for example: S3 region.""" + + external_location_format: Union[str, None, UnsetType] = UNSET + """Format of the external location of this table, for example: JSON, CSV, PARQUET, etc.""" + + is_partitioned: Union[bool, None, UnsetType] = UNSET + """Whether this table is partitioned (true) or not (false).""" + + partition_strategy: Union[str, None, UnsetType] = UNSET + """Partition strategy for this table.""" + + partition_count: Union[int, None, UnsetType] = UNSET + """Number of partitions in this table.""" + + table_definition: Union[str, None, UnsetType] = UNSET + """Definition of the table.""" + + partition_list: Union[str, None, UnsetType] = UNSET + """List of partitions in this table.""" + + is_sharded: Union[bool, None, UnsetType] = UNSET + """Whether this table is a sharded table (true) or not (false).""" + + sql_type: Union[str, None, UnsetType] = UNSET + """Type of the table.""" + + iceberg_catalog_name: Union[str, None, UnsetType] = UNSET + """Iceberg table catalog name (can be any user defined name)""" + + iceberg_table_type: Union[str, None, UnsetType] = UNSET + """Iceberg table type (managed vs unmanaged)""" + + iceberg_catalog_source: Union[str, None, UnsetType] = UNSET + """Iceberg table catalog type (glue, polaris, snowflake)""" + + iceberg_catalog_table_name: Union[str, None, UnsetType] = UNSET + """Catalog table name (actual table name on the catalog side).""" + + sql_impala_parameters: Union[Dict[str, str], None, UnsetType] = UNSET + """Extra attributes for Impala""" + + iceberg_catalog_table_namespace: Union[str, None, UnsetType] = UNSET + """Catalog table namespace (actual database name on the catalog side).""" + + sql_external_volume_name: Union[str, None, UnsetType] = UNSET + """External volume name for the table.""" + + iceberg_table_base_location: Union[str, None, UnsetType] = UNSET + """Iceberg table base location inside the external volume.""" + + sql_retention_time: Union[int, None, UnsetType] = UNSET + """Data retention time in days.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + +class TableRelationshipAttributes(AssetRelationshipAttributes): + """Table-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Columns that exist within this table.""" + + queries: Union[List[RelatedQuery], None, UnsetType] = UNSET + """Queries that access this table.""" + + atlan_schema: Union[RelatedSchema, None, UnsetType] = UNSET + """Schema in which this table exists.""" + + dimensions: Union[List[RelatedTable], None, UnsetType] = UNSET + """""" + + facts: Union[List[RelatedTable], None, UnsetType] = UNSET + """""" + + partitions: Union[List[RelatedTablePartition], None, UnsetType] = UNSET + """Partitions that exist within this table.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class TableNested(AssetNested): + """Table in nested API format for high-performance serialization.""" + + attributes: Union[TableAttributes, UnsetType] = UNSET + relationship_attributes: Union[TableRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[TableRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[TableRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_TABLE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "columns", + "queries", + "atlan_schema", + "dimensions", + "facts", + "partitions", + "schema_registry_subjects", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_table_attrs(attrs: TableAttributes, obj: Table) -> None: + """Populate Table-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.column_count = obj.column_count + attrs.row_count = obj.row_count + attrs.size_bytes = obj.size_bytes + attrs.sql_object_count = obj.sql_object_count + attrs.alias = obj.alias + attrs.is_temporary = obj.is_temporary + attrs.is_query_preview = obj.is_query_preview + attrs.query_preview_config = obj.query_preview_config + attrs.external_location = obj.external_location + attrs.external_location_region = obj.external_location_region + attrs.external_location_format = obj.external_location_format + attrs.is_partitioned = obj.is_partitioned + attrs.partition_strategy = obj.partition_strategy + attrs.partition_count = obj.partition_count + attrs.table_definition = obj.table_definition + attrs.partition_list = obj.partition_list + attrs.is_sharded = obj.is_sharded + attrs.sql_type = obj.sql_type + attrs.iceberg_catalog_name = obj.iceberg_catalog_name + attrs.iceberg_table_type = obj.iceberg_table_type + attrs.iceberg_catalog_source = obj.iceberg_catalog_source + attrs.iceberg_catalog_table_name = obj.iceberg_catalog_table_name + attrs.sql_impala_parameters = obj.sql_impala_parameters + attrs.iceberg_catalog_table_namespace = obj.iceberg_catalog_table_namespace + attrs.sql_external_volume_name = obj.sql_external_volume_name + attrs.iceberg_table_base_location = obj.iceberg_table_base_location + attrs.sql_retention_time = obj.sql_retention_time + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + + +def _extract_table_attrs(attrs: TableAttributes) -> dict: + """Extract all Table attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["column_count"] = attrs.column_count + result["row_count"] = attrs.row_count + result["size_bytes"] = attrs.size_bytes + result["sql_object_count"] = attrs.sql_object_count + result["alias"] = attrs.alias + result["is_temporary"] = attrs.is_temporary + result["is_query_preview"] = attrs.is_query_preview + result["query_preview_config"] = attrs.query_preview_config + result["external_location"] = attrs.external_location + result["external_location_region"] = attrs.external_location_region + result["external_location_format"] = attrs.external_location_format + result["is_partitioned"] = attrs.is_partitioned + result["partition_strategy"] = attrs.partition_strategy + result["partition_count"] = attrs.partition_count + result["table_definition"] = attrs.table_definition + result["partition_list"] = attrs.partition_list + result["is_sharded"] = attrs.is_sharded + result["sql_type"] = attrs.sql_type + result["iceberg_catalog_name"] = attrs.iceberg_catalog_name + result["iceberg_table_type"] = attrs.iceberg_table_type + result["iceberg_catalog_source"] = attrs.iceberg_catalog_source + result["iceberg_catalog_table_name"] = attrs.iceberg_catalog_table_name + result["sql_impala_parameters"] = attrs.sql_impala_parameters + result["iceberg_catalog_table_namespace"] = attrs.iceberg_catalog_table_namespace + result["sql_external_volume_name"] = attrs.sql_external_volume_name + result["iceberg_table_base_location"] = attrs.iceberg_table_base_location + result["sql_retention_time"] = attrs.sql_retention_time + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _table_to_nested(table: Table) -> TableNested: + """Convert flat Table to nested format.""" + attrs = TableAttributes() + _populate_table_attrs(attrs, table) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + table, _TABLE_REL_FIELDS, TableRelationshipAttributes + ) + return TableNested( + guid=table.guid, + type_name=table.type_name, + status=table.status, + version=table.version, + create_time=table.create_time, + update_time=table.update_time, + created_by=table.created_by, + updated_by=table.updated_by, + classifications=table.classifications, + classification_names=table.classification_names, + meanings=table.meanings, + labels=table.labels, + business_attributes=table.business_attributes, + custom_attributes=table.custom_attributes, + pending_tasks=table.pending_tasks, + proxy=table.proxy, + is_incomplete=table.is_incomplete, + provenance_type=table.provenance_type, + home_id=table.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _table_from_nested(nested: TableNested) -> Table: + """Convert nested format to flat Table.""" + attrs = nested.attributes if nested.attributes is not UNSET else TableAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _TABLE_REL_FIELDS, + TableRelationshipAttributes, + ) + return Table( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_table_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _table_to_nested_bytes(table: Table, serde: Serde) -> bytes: + """Convert flat Table to nested JSON bytes.""" + return serde.encode(_table_to_nested(table)) + + +def _table_from_nested_bytes(data: bytes, serde: Serde) -> Table: + """Convert nested JSON bytes to flat Table.""" + nested = serde.decode(data, TableNested) + return _table_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, +) + +Table.COLUMN_COUNT = NumericField("columnCount", "columnCount") +Table.ROW_COUNT = NumericField("rowCount", "rowCount") +Table.SIZE_BYTES = NumericField("sizeBytes", "sizeBytes") +Table.SQL_OBJECT_COUNT = NumericField("sqlObjectCount", "sqlObjectCount") +Table.ALIAS = KeywordField("alias", "alias") +Table.IS_TEMPORARY = BooleanField("isTemporary", "isTemporary") +Table.IS_QUERY_PREVIEW = BooleanField("isQueryPreview", "isQueryPreview") +Table.QUERY_PREVIEW_CONFIG = KeywordField("queryPreviewConfig", "queryPreviewConfig") +Table.EXTERNAL_LOCATION = KeywordField("externalLocation", "externalLocation") +Table.EXTERNAL_LOCATION_REGION = KeywordField( + "externalLocationRegion", "externalLocationRegion" +) +Table.EXTERNAL_LOCATION_FORMAT = KeywordField( + "externalLocationFormat", "externalLocationFormat" +) +Table.IS_PARTITIONED = BooleanField("isPartitioned", "isPartitioned") +Table.PARTITION_STRATEGY = KeywordField("partitionStrategy", "partitionStrategy") +Table.PARTITION_COUNT = NumericField("partitionCount", "partitionCount") +Table.TABLE_DEFINITION = KeywordField("tableDefinition", "tableDefinition") +Table.PARTITION_LIST = KeywordField("partitionList", "partitionList") +Table.IS_SHARDED = BooleanField("isSharded", "isSharded") +Table.SQL_TYPE = KeywordField("sqlType", "sqlType") +Table.ICEBERG_CATALOG_NAME = KeywordField("icebergCatalogName", "icebergCatalogName") +Table.ICEBERG_TABLE_TYPE = KeywordField("icebergTableType", "icebergTableType") +Table.ICEBERG_CATALOG_SOURCE = KeywordField( + "icebergCatalogSource", "icebergCatalogSource" +) +Table.ICEBERG_CATALOG_TABLE_NAME = KeywordField( + "icebergCatalogTableName", "icebergCatalogTableName" +) +Table.SQL_IMPALA_PARAMETERS = KeywordField("sqlImpalaParameters", "sqlImpalaParameters") +Table.ICEBERG_CATALOG_TABLE_NAMESPACE = KeywordField( + "icebergCatalogTableNamespace", "icebergCatalogTableNamespace" +) +Table.SQL_EXTERNAL_VOLUME_NAME = KeywordField( + "sqlExternalVolumeName", "sqlExternalVolumeName" +) +Table.ICEBERG_TABLE_BASE_LOCATION = KeywordField( + "icebergTableBaseLocation", "icebergTableBaseLocation" +) +Table.SQL_RETENTION_TIME = NumericField("sqlRetentionTime", "sqlRetentionTime") +Table.QUERY_COUNT = NumericField("queryCount", "queryCount") +Table.QUERY_USER_COUNT = NumericField("queryUserCount", "queryUserCount") +Table.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +Table.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +Table.DATABASE_NAME = KeywordField("databaseName", "databaseName") +Table.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +Table.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +Table.SCHEMA_QUALIFIED_NAME = KeywordField("schemaQualifiedName", "schemaQualifiedName") +Table.TABLE_NAME = KeywordField("tableName", "tableName") +Table.TABLE_QUALIFIED_NAME = KeywordField("tableQualifiedName", "tableQualifiedName") +Table.VIEW_NAME = KeywordField("viewName", "viewName") +Table.VIEW_QUALIFIED_NAME = KeywordField("viewQualifiedName", "viewQualifiedName") +Table.CALCULATION_VIEW_NAME = KeywordField("calculationViewName", "calculationViewName") +Table.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +Table.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +Table.LAST_PROFILED_AT = NumericField("lastProfiledAt", "lastProfiledAt") +Table.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +Table.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +Table.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Table.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Table.ANOMALO_CHECKS = RelationField("anomaloChecks") +Table.APPLICATION = RelationField("application") +Table.APPLICATION_FIELD = RelationField("applicationField") +Table.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Table.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Table.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Table.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Table.METRICS = RelationField("metrics") +Table.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Table.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Table.DBT_MODELS = RelationField("dbtModels") +Table.SQL_DBT_MODELS = RelationField("sqlDbtModels") +Table.DBT_TESTS = RelationField("dbtTests") +Table.DBT_SOURCES = RelationField("dbtSources") +Table.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +Table.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +Table.MEANINGS = RelationField("meanings") +Table.MC_MONITORS = RelationField("mcMonitors") +Table.MC_INCIDENTS = RelationField("mcIncidents") +Table.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Table.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Table.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Table.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Table.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Table.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Table.FILES = RelationField("files") +Table.LINKS = RelationField("links") +Table.README = RelationField("readme") +Table.COLUMNS = RelationField("columns") +Table.QUERIES = RelationField("queries") +Table.ATLAN_SCHEMA = RelationField("atlanSchema") +Table.DIMENSIONS = RelationField("dimensions") +Table.FACTS = RelationField("facts") +Table.PARTITIONS = RelationField("partitions") +Table.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Table.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +Table.SODA_CHECKS = RelationField("sodaChecks") +Table.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Table.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/table_partition.py b/pyatlan_v9/model/assets/table_partition.py new file mode 100644 index 000000000..3a1e07289 --- /dev/null +++ b/pyatlan_v9/model/assets/table_partition.py @@ -0,0 +1,1136 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +TablePartition asset model with flattened inheritance. + +This module provides: +- TablePartition: Flat asset class (easy to use) +- TablePartitionAttributes: Nested attributes struct (extends AssetAttributes) +- TablePartitionNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .snowflake_related import RelatedSnowflakeSemanticLogicalTable +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .sql_related import RelatedColumn, RelatedTable, RelatedTablePartition + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class TablePartition(Asset): + """ + Instance of a database table partition in Atlan. + """ + + CONSTRAINT: ClassVar[Any] = None + COLUMN_COUNT: ClassVar[Any] = None + ROW_COUNT: ClassVar[Any] = None + SIZE_BYTES: ClassVar[Any] = None + ALIAS: ClassVar[Any] = None + IS_TEMPORARY: ClassVar[Any] = None + IS_QUERY_PREVIEW: ClassVar[Any] = None + QUERY_PREVIEW_CONFIG: ClassVar[Any] = None + EXTERNAL_LOCATION: ClassVar[Any] = None + EXTERNAL_LOCATION_REGION: ClassVar[Any] = None + EXTERNAL_LOCATION_FORMAT: ClassVar[Any] = None + IS_PARTITIONED: ClassVar[Any] = None + PARTITION_STRATEGY: ClassVar[Any] = None + PARTITION_COUNT: ClassVar[Any] = None + PARTITION_LIST: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + COLUMNS: ClassVar[Any] = None + PARENT_TABLE: ClassVar[Any] = None + CHILD_TABLE_PARTITIONS: ClassVar[Any] = None + PARENT_TABLE_PARTITION: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "TablePartition" + + constraint: Union[str, None, UnsetType] = UNSET + """Constraint that defines this table partition.""" + + column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this partition.""" + + row_count: Union[int, None, UnsetType] = UNSET + """Number of rows in this partition.""" + + size_bytes: Union[int, None, UnsetType] = UNSET + """Size of this partition, in bytes.""" + + alias: Union[str, None, UnsetType] = UNSET + """Alias for this partition.""" + + is_temporary: Union[bool, None, UnsetType] = UNSET + """Whether this partition is temporary (true) or not (false).""" + + is_query_preview: Union[bool, None, UnsetType] = UNSET + """Whether preview queries for this partition are allowed (true) or not (false).""" + + query_preview_config: Union[Dict[str, str], None, UnsetType] = UNSET + """Configuration for the preview queries.""" + + external_location: Union[str, None, UnsetType] = UNSET + """External location of this partition, for example: an S3 object location.""" + + external_location_region: Union[str, None, UnsetType] = UNSET + """Region of the external location of this partition, for example: S3 region.""" + + external_location_format: Union[str, None, UnsetType] = UNSET + """Format of the external location of this partition, for example: JSON, CSV, PARQUET, etc.""" + + is_partitioned: Union[bool, None, UnsetType] = UNSET + """Whether this partition is further partitioned (true) or not (false).""" + + partition_strategy: Union[str, None, UnsetType] = UNSET + """Partition strategy of this partition.""" + + partition_count: Union[int, None, UnsetType] = UNSET + """Number of sub-partitions of this partition.""" + + partition_list: Union[str, None, UnsetType] = UNSET + """List of sub-partitions in this partition.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Columns that exist within this table partition.""" + + parent_table: Union[RelatedTable, None, UnsetType] = UNSET + """Table in which this partition exists.""" + + child_table_partitions: Union[List[RelatedTablePartition], None, UnsetType] = UNSET + """Partitions that exist within this partition.""" + + parent_table_partition: Union[RelatedTablePartition, None, UnsetType] = UNSET + """Partition in which this partition exists.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "TablePartition" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+/[^/]+$" + ) + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + table_qualified_name: str, + table_name: str | None = None, + schema_name: str | None = None, + schema_qualified_name: str | None = None, + database_name: str | None = None, + database_qualified_name: str | None = None, + connection_qualified_name: str | None = None, + ) -> "TablePartition": + """ + Create a new TablePartition asset with auto-derived fields. + + Args: + name: Simple name of the table partition + table_qualified_name: Unique name of the table in which this partition exists + table_name: Simple name of the table (auto-derived if not provided) + schema_name: Simple name of the schema (auto-derived if not provided) + schema_qualified_name: Unique name of the schema (auto-derived if not provided) + database_name: Simple name of the database (auto-derived if not provided) + database_qualified_name: Unique name of the database (auto-derived if not provided) + connection_qualified_name: Unique name of the connection (auto-derived if not provided) + + Returns: + New TablePartition instance with all fields populated + + Raises: + ValueError: If required parameters are missing or invalid + """ + validate_required_fields( + ["name", "table_qualified_name"], [name, table_qualified_name] + ) + + fields = table_qualified_name.split("/") + if len(fields) != 6: + raise ValueError( + f"Invalid table_qualified_name: {table_qualified_name}. " + "Expected format: default/connector/connection_id/database/schema/table" + ) + + connector_name = fields[1] + connection_qn = ( + connection_qualified_name or f"{fields[0]}/{fields[1]}/{fields[2]}" + ) + db_name = database_name or fields[3] + sch_name = schema_name or fields[4] + tbl_name = table_name or fields[5] + db_qualified_name = database_qualified_name or f"{connection_qn}/{db_name}" + sch_qualified_name = schema_qualified_name or f"{db_qualified_name}/{sch_name}" + qualified_name = f"{sch_qualified_name}/{name}" + + return cls( + name=name, + qualified_name=qualified_name, + table_name=tbl_name, + table_qualified_name=table_qualified_name, + schema_name=sch_name, + schema_qualified_name=sch_qualified_name, + database_name=db_name, + database_qualified_name=db_qualified_name, + connector_name=connector_name, + connection_qualified_name=connection_qn, + parent_table=RelatedTable(qualified_name=table_qualified_name), + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "TablePartition": + """ + Create a TablePartition instance for updating an existing asset. + + Args: + qualified_name: Unique name of the table partition to update + name: Simple name of the table partition + + Returns: + TablePartition instance configured for updates + + Raises: + ValueError: If required parameters are missing + """ + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "TablePartition": + """ + Return a TablePartition with only required fields for reference. + + Returns: + TablePartition instance with only qualified_name and name set + """ + return TablePartition(qualified_name=self.qualified_name, name=self.name) + + @classmethod + def create(cls, **kwargs) -> "TablePartition": + """Backward compatibility alias for creator().""" + return cls.creator(**kwargs) + + @classmethod + def create_for_modification(cls, **kwargs) -> "TablePartition": + """Backward compatibility alias for updater().""" + return cls.updater(**kwargs) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _table_partition_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> TablePartition: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + TablePartition instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _table_partition_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class TablePartitionAttributes(AssetAttributes): + """TablePartition-specific attributes for nested API format.""" + + constraint: Union[str, None, UnsetType] = UNSET + """Constraint that defines this table partition.""" + + column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this partition.""" + + row_count: Union[int, None, UnsetType] = UNSET + """Number of rows in this partition.""" + + size_bytes: Union[int, None, UnsetType] = UNSET + """Size of this partition, in bytes.""" + + alias: Union[str, None, UnsetType] = UNSET + """Alias for this partition.""" + + is_temporary: Union[bool, None, UnsetType] = UNSET + """Whether this partition is temporary (true) or not (false).""" + + is_query_preview: Union[bool, None, UnsetType] = UNSET + """Whether preview queries for this partition are allowed (true) or not (false).""" + + query_preview_config: Union[Dict[str, str], None, UnsetType] = UNSET + """Configuration for the preview queries.""" + + external_location: Union[str, None, UnsetType] = UNSET + """External location of this partition, for example: an S3 object location.""" + + external_location_region: Union[str, None, UnsetType] = UNSET + """Region of the external location of this partition, for example: S3 region.""" + + external_location_format: Union[str, None, UnsetType] = UNSET + """Format of the external location of this partition, for example: JSON, CSV, PARQUET, etc.""" + + is_partitioned: Union[bool, None, UnsetType] = UNSET + """Whether this partition is further partitioned (true) or not (false).""" + + partition_strategy: Union[str, None, UnsetType] = UNSET + """Partition strategy of this partition.""" + + partition_count: Union[int, None, UnsetType] = UNSET + """Number of sub-partitions of this partition.""" + + partition_list: Union[str, None, UnsetType] = UNSET + """List of sub-partitions in this partition.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + +class TablePartitionRelationshipAttributes(AssetRelationshipAttributes): + """TablePartition-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Columns that exist within this table partition.""" + + parent_table: Union[RelatedTable, None, UnsetType] = UNSET + """Table in which this partition exists.""" + + child_table_partitions: Union[List[RelatedTablePartition], None, UnsetType] = UNSET + """Partitions that exist within this partition.""" + + parent_table_partition: Union[RelatedTablePartition, None, UnsetType] = UNSET + """Partition in which this partition exists.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class TablePartitionNested(AssetNested): + """TablePartition in nested API format for high-performance serialization.""" + + attributes: Union[TablePartitionAttributes, UnsetType] = UNSET + relationship_attributes: Union[TablePartitionRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + TablePartitionRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + TablePartitionRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_TABLE_PARTITION_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "columns", + "parent_table", + "child_table_partitions", + "parent_table_partition", + "schema_registry_subjects", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_table_partition_attrs( + attrs: TablePartitionAttributes, obj: TablePartition +) -> None: + """Populate TablePartition-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.constraint = obj.constraint + attrs.column_count = obj.column_count + attrs.row_count = obj.row_count + attrs.size_bytes = obj.size_bytes + attrs.alias = obj.alias + attrs.is_temporary = obj.is_temporary + attrs.is_query_preview = obj.is_query_preview + attrs.query_preview_config = obj.query_preview_config + attrs.external_location = obj.external_location + attrs.external_location_region = obj.external_location_region + attrs.external_location_format = obj.external_location_format + attrs.is_partitioned = obj.is_partitioned + attrs.partition_strategy = obj.partition_strategy + attrs.partition_count = obj.partition_count + attrs.partition_list = obj.partition_list + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + + +def _extract_table_partition_attrs(attrs: TablePartitionAttributes) -> dict: + """Extract all TablePartition attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["constraint"] = attrs.constraint + result["column_count"] = attrs.column_count + result["row_count"] = attrs.row_count + result["size_bytes"] = attrs.size_bytes + result["alias"] = attrs.alias + result["is_temporary"] = attrs.is_temporary + result["is_query_preview"] = attrs.is_query_preview + result["query_preview_config"] = attrs.query_preview_config + result["external_location"] = attrs.external_location + result["external_location_region"] = attrs.external_location_region + result["external_location_format"] = attrs.external_location_format + result["is_partitioned"] = attrs.is_partitioned + result["partition_strategy"] = attrs.partition_strategy + result["partition_count"] = attrs.partition_count + result["partition_list"] = attrs.partition_list + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _table_partition_to_nested(table_partition: TablePartition) -> TablePartitionNested: + """Convert flat TablePartition to nested format.""" + attrs = TablePartitionAttributes() + _populate_table_partition_attrs(attrs, table_partition) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + table_partition, + _TABLE_PARTITION_REL_FIELDS, + TablePartitionRelationshipAttributes, + ) + return TablePartitionNested( + guid=table_partition.guid, + type_name=table_partition.type_name, + status=table_partition.status, + version=table_partition.version, + create_time=table_partition.create_time, + update_time=table_partition.update_time, + created_by=table_partition.created_by, + updated_by=table_partition.updated_by, + classifications=table_partition.classifications, + classification_names=table_partition.classification_names, + meanings=table_partition.meanings, + labels=table_partition.labels, + business_attributes=table_partition.business_attributes, + custom_attributes=table_partition.custom_attributes, + pending_tasks=table_partition.pending_tasks, + proxy=table_partition.proxy, + is_incomplete=table_partition.is_incomplete, + provenance_type=table_partition.provenance_type, + home_id=table_partition.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _table_partition_from_nested(nested: TablePartitionNested) -> TablePartition: + """Convert nested format to flat TablePartition.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else TablePartitionAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _TABLE_PARTITION_REL_FIELDS, + TablePartitionRelationshipAttributes, + ) + return TablePartition( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_table_partition_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _table_partition_to_nested_bytes( + table_partition: TablePartition, serde: Serde +) -> bytes: + """Convert flat TablePartition to nested JSON bytes.""" + return serde.encode(_table_partition_to_nested(table_partition)) + + +def _table_partition_from_nested_bytes(data: bytes, serde: Serde) -> TablePartition: + """Convert nested JSON bytes to flat TablePartition.""" + nested = serde.decode(data, TablePartitionNested) + return _table_partition_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, +) + +TablePartition.CONSTRAINT = KeywordField("constraint", "constraint") +TablePartition.COLUMN_COUNT = NumericField("columnCount", "columnCount") +TablePartition.ROW_COUNT = NumericField("rowCount", "rowCount") +TablePartition.SIZE_BYTES = NumericField("sizeBytes", "sizeBytes") +TablePartition.ALIAS = KeywordField("alias", "alias") +TablePartition.IS_TEMPORARY = BooleanField("isTemporary", "isTemporary") +TablePartition.IS_QUERY_PREVIEW = BooleanField("isQueryPreview", "isQueryPreview") +TablePartition.QUERY_PREVIEW_CONFIG = KeywordField( + "queryPreviewConfig", "queryPreviewConfig" +) +TablePartition.EXTERNAL_LOCATION = KeywordField("externalLocation", "externalLocation") +TablePartition.EXTERNAL_LOCATION_REGION = KeywordField( + "externalLocationRegion", "externalLocationRegion" +) +TablePartition.EXTERNAL_LOCATION_FORMAT = KeywordField( + "externalLocationFormat", "externalLocationFormat" +) +TablePartition.IS_PARTITIONED = BooleanField("isPartitioned", "isPartitioned") +TablePartition.PARTITION_STRATEGY = KeywordField( + "partitionStrategy", "partitionStrategy" +) +TablePartition.PARTITION_COUNT = NumericField("partitionCount", "partitionCount") +TablePartition.PARTITION_LIST = KeywordField("partitionList", "partitionList") +TablePartition.QUERY_COUNT = NumericField("queryCount", "queryCount") +TablePartition.QUERY_USER_COUNT = NumericField("queryUserCount", "queryUserCount") +TablePartition.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +TablePartition.QUERY_COUNT_UPDATED_AT = NumericField( + "queryCountUpdatedAt", "queryCountUpdatedAt" +) +TablePartition.DATABASE_NAME = KeywordField("databaseName", "databaseName") +TablePartition.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +TablePartition.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +TablePartition.SCHEMA_QUALIFIED_NAME = KeywordField( + "schemaQualifiedName", "schemaQualifiedName" +) +TablePartition.TABLE_NAME = KeywordField("tableName", "tableName") +TablePartition.TABLE_QUALIFIED_NAME = KeywordField( + "tableQualifiedName", "tableQualifiedName" +) +TablePartition.VIEW_NAME = KeywordField("viewName", "viewName") +TablePartition.VIEW_QUALIFIED_NAME = KeywordField( + "viewQualifiedName", "viewQualifiedName" +) +TablePartition.CALCULATION_VIEW_NAME = KeywordField( + "calculationViewName", "calculationViewName" +) +TablePartition.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +TablePartition.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +TablePartition.LAST_PROFILED_AT = NumericField("lastProfiledAt", "lastProfiledAt") +TablePartition.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +TablePartition.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +TablePartition.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +TablePartition.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +TablePartition.ANOMALO_CHECKS = RelationField("anomaloChecks") +TablePartition.APPLICATION = RelationField("application") +TablePartition.APPLICATION_FIELD = RelationField("applicationField") +TablePartition.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +TablePartition.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +TablePartition.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +TablePartition.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +TablePartition.METRICS = RelationField("metrics") +TablePartition.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +TablePartition.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +TablePartition.DBT_MODELS = RelationField("dbtModels") +TablePartition.SQL_DBT_MODELS = RelationField("sqlDbtModels") +TablePartition.DBT_TESTS = RelationField("dbtTests") +TablePartition.DBT_SOURCES = RelationField("dbtSources") +TablePartition.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +TablePartition.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +TablePartition.MEANINGS = RelationField("meanings") +TablePartition.MC_MONITORS = RelationField("mcMonitors") +TablePartition.MC_INCIDENTS = RelationField("mcIncidents") +TablePartition.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +TablePartition.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +TablePartition.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +TablePartition.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +TablePartition.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +TablePartition.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +TablePartition.FILES = RelationField("files") +TablePartition.LINKS = RelationField("links") +TablePartition.README = RelationField("readme") +TablePartition.COLUMNS = RelationField("columns") +TablePartition.PARENT_TABLE = RelationField("parentTable") +TablePartition.CHILD_TABLE_PARTITIONS = RelationField("childTablePartitions") +TablePartition.PARENT_TABLE_PARTITION = RelationField("parentTablePartition") +TablePartition.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +TablePartition.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField( + "snowflakeSemanticLogicalTables" +) +TablePartition.SODA_CHECKS = RelationField("sodaChecks") +TablePartition.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +TablePartition.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/tableau.py b/pyatlan_v9/model/assets/tableau.py new file mode 100644 index 000000000..07e2e545e --- /dev/null +++ b/pyatlan_v9/model/assets/tableau.py @@ -0,0 +1,541 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Tableau asset model with flattened inheritance. + +This module provides: +- Tableau: Flat asset class (easy to use) +- TableauAttributes: Nested attributes struct (extends AssetAttributes) +- TableauNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Tableau(Asset): + """ + Base class for Tableau assets. + """ + + TABLEAU_PROJECT_HIERARCHY_QUALIFIED_NAMES: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Tableau" + + tableau_project_hierarchy_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Array of qualified names representing the project hierarchy for this Tableau asset.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Tableau" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _tableau_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Tableau: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Tableau instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _tableau_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class TableauAttributes(AssetAttributes): + """Tableau-specific attributes for nested API format.""" + + tableau_project_hierarchy_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Array of qualified names representing the project hierarchy for this Tableau asset.""" + + +class TableauRelationshipAttributes(AssetRelationshipAttributes): + """Tableau-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class TableauNested(AssetNested): + """Tableau in nested API format for high-performance serialization.""" + + attributes: Union[TableauAttributes, UnsetType] = UNSET + relationship_attributes: Union[TableauRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[TableauRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[TableauRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_TABLEAU_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_tableau_attrs(attrs: TableauAttributes, obj: Tableau) -> None: + """Populate Tableau-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.tableau_project_hierarchy_qualified_names = ( + obj.tableau_project_hierarchy_qualified_names + ) + + +def _extract_tableau_attrs(attrs: TableauAttributes) -> dict: + """Extract all Tableau attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["tableau_project_hierarchy_qualified_names"] = ( + attrs.tableau_project_hierarchy_qualified_names + ) + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _tableau_to_nested(tableau: Tableau) -> TableauNested: + """Convert flat Tableau to nested format.""" + attrs = TableauAttributes() + _populate_tableau_attrs(attrs, tableau) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + tableau, _TABLEAU_REL_FIELDS, TableauRelationshipAttributes + ) + return TableauNested( + guid=tableau.guid, + type_name=tableau.type_name, + status=tableau.status, + version=tableau.version, + create_time=tableau.create_time, + update_time=tableau.update_time, + created_by=tableau.created_by, + updated_by=tableau.updated_by, + classifications=tableau.classifications, + classification_names=tableau.classification_names, + meanings=tableau.meanings, + labels=tableau.labels, + business_attributes=tableau.business_attributes, + custom_attributes=tableau.custom_attributes, + pending_tasks=tableau.pending_tasks, + proxy=tableau.proxy, + is_incomplete=tableau.is_incomplete, + provenance_type=tableau.provenance_type, + home_id=tableau.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _tableau_from_nested(nested: TableauNested) -> Tableau: + """Convert nested format to flat Tableau.""" + attrs = nested.attributes if nested.attributes is not UNSET else TableauAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _TABLEAU_REL_FIELDS, + TableauRelationshipAttributes, + ) + return Tableau( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_tableau_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _tableau_to_nested_bytes(tableau: Tableau, serde: Serde) -> bytes: + """Convert flat Tableau to nested JSON bytes.""" + return serde.encode(_tableau_to_nested(tableau)) + + +def _tableau_from_nested_bytes(data: bytes, serde: Serde) -> Tableau: + """Convert nested JSON bytes to flat Tableau.""" + nested = serde.decode(data, TableauNested) + return _tableau_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +Tableau.TABLEAU_PROJECT_HIERARCHY_QUALIFIED_NAMES = KeywordField( + "tableauProjectHierarchyQualifiedNames", "tableauProjectHierarchyQualifiedNames" +) +Tableau.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Tableau.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Tableau.ANOMALO_CHECKS = RelationField("anomaloChecks") +Tableau.APPLICATION = RelationField("application") +Tableau.APPLICATION_FIELD = RelationField("applicationField") +Tableau.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Tableau.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Tableau.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Tableau.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Tableau.METRICS = RelationField("metrics") +Tableau.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Tableau.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Tableau.MEANINGS = RelationField("meanings") +Tableau.MC_MONITORS = RelationField("mcMonitors") +Tableau.MC_INCIDENTS = RelationField("mcIncidents") +Tableau.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Tableau.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Tableau.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Tableau.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Tableau.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Tableau.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Tableau.FILES = RelationField("files") +Tableau.LINKS = RelationField("links") +Tableau.README = RelationField("readme") +Tableau.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Tableau.SODA_CHECKS = RelationField("sodaChecks") +Tableau.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Tableau.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/tableau_calculated_field.py b/pyatlan_v9/model/assets/tableau_calculated_field.py new file mode 100644 index 000000000..3cc5e8a6a --- /dev/null +++ b/pyatlan_v9/model/assets/tableau_calculated_field.py @@ -0,0 +1,752 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +TableauCalculatedField asset model with flattened inheritance. + +This module provides: +- TableauCalculatedField: Flat asset class (easy to use) +- TableauCalculatedFieldAttributes: Nested attributes struct (extends AssetAttributes) +- TableauCalculatedFieldNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .tableau_related import ( + RelatedTableauDatasource, + RelatedTableauWorksheet, + RelatedTableauWorksheetField, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class TableauCalculatedField(Asset): + """ + Instance of a Tableau calculated field in Atlan. These are created by combining two or more fields using a formula, and can be created at datasource or worksheet level. + """ + + SITE_QUALIFIED_NAME: ClassVar[Any] = None + PROJECT_QUALIFIED_NAME: ClassVar[Any] = None + TOP_LEVEL_PROJECT_QUALIFIED_NAME: ClassVar[Any] = None + WORKBOOK_QUALIFIED_NAME: ClassVar[Any] = None + DATASOURCE_QUALIFIED_NAME: ClassVar[Any] = None + PROJECT_HIERARCHY: ClassVar[Any] = None + DATA_CATEGORY: ClassVar[Any] = None + ROLE: ClassVar[Any] = None + TABLEAU_DATA_TYPE: ClassVar[Any] = None + FORMULA: ClassVar[Any] = None + UPSTREAM_FIELDS: ClassVar[Any] = None + TABLEAU_PROJECT_HIERARCHY_QUALIFIED_NAMES: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + TABLEAU_WORKSHEET_FIELDS: ClassVar[Any] = None + DATASOURCE: ClassVar[Any] = None + WORKSHEETS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "TableauCalculatedField" + + site_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the site in which this calculated field exists.""" + + project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this calculated field exists.""" + + top_level_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the top-level project in which this calculated field exists.""" + + workbook_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workbook in which this calculated field exists.""" + + datasource_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the datasource in which this calculated field exists.""" + + project_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of top-level projects and their nested projects.""" + + data_category: Union[str, None, UnsetType] = UNSET + """Data category of this field.""" + + role: Union[str, None, UnsetType] = UNSET + """Role of this field, for example: 'dimension', 'measure', or 'unknown'.""" + + tableau_data_type: Union[str, None, UnsetType] = UNSET + """Data type of the field, from Tableau.""" + + formula: Union[str, None, UnsetType] = UNSET + """Formula for this calculated field.""" + + upstream_fields: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of fields that are upstream to this calculated field.""" + + tableau_project_hierarchy_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Array of qualified names representing the project hierarchy for this Tableau asset.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + tableau_worksheet_fields: Union[RelatedTableauWorksheetField, None, UnsetType] = ( + UNSET + ) + """Worksheet fields that use this calculated field.""" + + datasource: Union[RelatedTableauDatasource, None, UnsetType] = UNSET + """Datasource in which this calculated field exists.""" + + worksheets: Union[List[RelatedTableauWorksheet], None, UnsetType] = UNSET + """Worksheets that use this calculated field.""" + + def __post_init__(self) -> None: + self.type_name = "TableauCalculatedField" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _tableau_calculated_field_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> TableauCalculatedField: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + TableauCalculatedField instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _tableau_calculated_field_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class TableauCalculatedFieldAttributes(AssetAttributes): + """TableauCalculatedField-specific attributes for nested API format.""" + + site_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the site in which this calculated field exists.""" + + project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this calculated field exists.""" + + top_level_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the top-level project in which this calculated field exists.""" + + workbook_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workbook in which this calculated field exists.""" + + datasource_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the datasource in which this calculated field exists.""" + + project_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of top-level projects and their nested projects.""" + + data_category: Union[str, None, UnsetType] = UNSET + """Data category of this field.""" + + role: Union[str, None, UnsetType] = UNSET + """Role of this field, for example: 'dimension', 'measure', or 'unknown'.""" + + tableau_data_type: Union[str, None, UnsetType] = UNSET + """Data type of the field, from Tableau.""" + + formula: Union[str, None, UnsetType] = UNSET + """Formula for this calculated field.""" + + upstream_fields: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of fields that are upstream to this calculated field.""" + + tableau_project_hierarchy_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Array of qualified names representing the project hierarchy for this Tableau asset.""" + + +class TableauCalculatedFieldRelationshipAttributes(AssetRelationshipAttributes): + """TableauCalculatedField-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + tableau_worksheet_fields: Union[RelatedTableauWorksheetField, None, UnsetType] = ( + UNSET + ) + """Worksheet fields that use this calculated field.""" + + datasource: Union[RelatedTableauDatasource, None, UnsetType] = UNSET + """Datasource in which this calculated field exists.""" + + worksheets: Union[List[RelatedTableauWorksheet], None, UnsetType] = UNSET + """Worksheets that use this calculated field.""" + + +class TableauCalculatedFieldNested(AssetNested): + """TableauCalculatedField in nested API format for high-performance serialization.""" + + attributes: Union[TableauCalculatedFieldAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + TableauCalculatedFieldRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + TableauCalculatedFieldRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + TableauCalculatedFieldRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_TABLEAU_CALCULATED_FIELD_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", + "tableau_worksheet_fields", + "datasource", + "worksheets", +] + + +def _populate_tableau_calculated_field_attrs( + attrs: TableauCalculatedFieldAttributes, obj: TableauCalculatedField +) -> None: + """Populate TableauCalculatedField-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.site_qualified_name = obj.site_qualified_name + attrs.project_qualified_name = obj.project_qualified_name + attrs.top_level_project_qualified_name = obj.top_level_project_qualified_name + attrs.workbook_qualified_name = obj.workbook_qualified_name + attrs.datasource_qualified_name = obj.datasource_qualified_name + attrs.project_hierarchy = obj.project_hierarchy + attrs.data_category = obj.data_category + attrs.role = obj.role + attrs.tableau_data_type = obj.tableau_data_type + attrs.formula = obj.formula + attrs.upstream_fields = obj.upstream_fields + attrs.tableau_project_hierarchy_qualified_names = ( + obj.tableau_project_hierarchy_qualified_names + ) + + +def _extract_tableau_calculated_field_attrs( + attrs: TableauCalculatedFieldAttributes, +) -> dict: + """Extract all TableauCalculatedField attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["site_qualified_name"] = attrs.site_qualified_name + result["project_qualified_name"] = attrs.project_qualified_name + result["top_level_project_qualified_name"] = attrs.top_level_project_qualified_name + result["workbook_qualified_name"] = attrs.workbook_qualified_name + result["datasource_qualified_name"] = attrs.datasource_qualified_name + result["project_hierarchy"] = attrs.project_hierarchy + result["data_category"] = attrs.data_category + result["role"] = attrs.role + result["tableau_data_type"] = attrs.tableau_data_type + result["formula"] = attrs.formula + result["upstream_fields"] = attrs.upstream_fields + result["tableau_project_hierarchy_qualified_names"] = ( + attrs.tableau_project_hierarchy_qualified_names + ) + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _tableau_calculated_field_to_nested( + tableau_calculated_field: TableauCalculatedField, +) -> TableauCalculatedFieldNested: + """Convert flat TableauCalculatedField to nested format.""" + attrs = TableauCalculatedFieldAttributes() + _populate_tableau_calculated_field_attrs(attrs, tableau_calculated_field) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + tableau_calculated_field, + _TABLEAU_CALCULATED_FIELD_REL_FIELDS, + TableauCalculatedFieldRelationshipAttributes, + ) + return TableauCalculatedFieldNested( + guid=tableau_calculated_field.guid, + type_name=tableau_calculated_field.type_name, + status=tableau_calculated_field.status, + version=tableau_calculated_field.version, + create_time=tableau_calculated_field.create_time, + update_time=tableau_calculated_field.update_time, + created_by=tableau_calculated_field.created_by, + updated_by=tableau_calculated_field.updated_by, + classifications=tableau_calculated_field.classifications, + classification_names=tableau_calculated_field.classification_names, + meanings=tableau_calculated_field.meanings, + labels=tableau_calculated_field.labels, + business_attributes=tableau_calculated_field.business_attributes, + custom_attributes=tableau_calculated_field.custom_attributes, + pending_tasks=tableau_calculated_field.pending_tasks, + proxy=tableau_calculated_field.proxy, + is_incomplete=tableau_calculated_field.is_incomplete, + provenance_type=tableau_calculated_field.provenance_type, + home_id=tableau_calculated_field.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _tableau_calculated_field_from_nested( + nested: TableauCalculatedFieldNested, +) -> TableauCalculatedField: + """Convert nested format to flat TableauCalculatedField.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else TableauCalculatedFieldAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _TABLEAU_CALCULATED_FIELD_REL_FIELDS, + TableauCalculatedFieldRelationshipAttributes, + ) + return TableauCalculatedField( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_tableau_calculated_field_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _tableau_calculated_field_to_nested_bytes( + tableau_calculated_field: TableauCalculatedField, serde: Serde +) -> bytes: + """Convert flat TableauCalculatedField to nested JSON bytes.""" + return serde.encode(_tableau_calculated_field_to_nested(tableau_calculated_field)) + + +def _tableau_calculated_field_from_nested_bytes( + data: bytes, serde: Serde +) -> TableauCalculatedField: + """Convert nested JSON bytes to flat TableauCalculatedField.""" + nested = serde.decode(data, TableauCalculatedFieldNested) + return _tableau_calculated_field_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + RelationField, +) + +TableauCalculatedField.SITE_QUALIFIED_NAME = KeywordField( + "siteQualifiedName", "siteQualifiedName" +) +TableauCalculatedField.PROJECT_QUALIFIED_NAME = KeywordField( + "projectQualifiedName", "projectQualifiedName" +) +TableauCalculatedField.TOP_LEVEL_PROJECT_QUALIFIED_NAME = KeywordField( + "topLevelProjectQualifiedName", "topLevelProjectQualifiedName" +) +TableauCalculatedField.WORKBOOK_QUALIFIED_NAME = KeywordField( + "workbookQualifiedName", "workbookQualifiedName" +) +TableauCalculatedField.DATASOURCE_QUALIFIED_NAME = KeywordField( + "datasourceQualifiedName", "datasourceQualifiedName" +) +TableauCalculatedField.PROJECT_HIERARCHY = KeywordField( + "projectHierarchy", "projectHierarchy" +) +TableauCalculatedField.DATA_CATEGORY = KeywordField("dataCategory", "dataCategory") +TableauCalculatedField.ROLE = KeywordField("role", "role") +TableauCalculatedField.TABLEAU_DATA_TYPE = KeywordTextField( + "tableauDataType", "tableauDataType", "tableauDataType.text" +) +TableauCalculatedField.FORMULA = KeywordField("formula", "formula") +TableauCalculatedField.UPSTREAM_FIELDS = KeywordField( + "upstreamFields", "upstreamFields" +) +TableauCalculatedField.TABLEAU_PROJECT_HIERARCHY_QUALIFIED_NAMES = KeywordField( + "tableauProjectHierarchyQualifiedNames", "tableauProjectHierarchyQualifiedNames" +) +TableauCalculatedField.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +TableauCalculatedField.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +TableauCalculatedField.ANOMALO_CHECKS = RelationField("anomaloChecks") +TableauCalculatedField.APPLICATION = RelationField("application") +TableauCalculatedField.APPLICATION_FIELD = RelationField("applicationField") +TableauCalculatedField.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +TableauCalculatedField.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +TableauCalculatedField.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +TableauCalculatedField.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +TableauCalculatedField.METRICS = RelationField("metrics") +TableauCalculatedField.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +TableauCalculatedField.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +TableauCalculatedField.MEANINGS = RelationField("meanings") +TableauCalculatedField.MC_MONITORS = RelationField("mcMonitors") +TableauCalculatedField.MC_INCIDENTS = RelationField("mcIncidents") +TableauCalculatedField.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +TableauCalculatedField.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +TableauCalculatedField.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +TableauCalculatedField.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +TableauCalculatedField.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +TableauCalculatedField.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +TableauCalculatedField.FILES = RelationField("files") +TableauCalculatedField.LINKS = RelationField("links") +TableauCalculatedField.README = RelationField("readme") +TableauCalculatedField.SCHEMA_REGISTRY_SUBJECTS = RelationField( + "schemaRegistrySubjects" +) +TableauCalculatedField.SODA_CHECKS = RelationField("sodaChecks") +TableauCalculatedField.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +TableauCalculatedField.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") +TableauCalculatedField.TABLEAU_WORKSHEET_FIELDS = RelationField( + "tableauWorksheetFields" +) +TableauCalculatedField.DATASOURCE = RelationField("datasource") +TableauCalculatedField.WORKSHEETS = RelationField("worksheets") diff --git a/pyatlan_v9/model/assets/tableau_dashboard.py b/pyatlan_v9/model/assets/tableau_dashboard.py new file mode 100644 index 000000000..44f6bc3a6 --- /dev/null +++ b/pyatlan_v9/model/assets/tableau_dashboard.py @@ -0,0 +1,694 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +TableauDashboard asset model with flattened inheritance. + +This module provides: +- TableauDashboard: Flat asset class (easy to use) +- TableauDashboardAttributes: Nested attributes struct (extends AssetAttributes) +- TableauDashboardNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .tableau_related import ( + RelatedTableauDashboard, + RelatedTableauDashboardField, + RelatedTableauWorkbook, + RelatedTableauWorksheet, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class TableauDashboard(Asset): + """ + Instance of a Tableau dashboard in Atlan. These are collections of several views, letting you compare a variety of data simultaneously. + """ + + SITE_QUALIFIED_NAME: ClassVar[Any] = None + PROJECT_QUALIFIED_NAME: ClassVar[Any] = None + WORKBOOK_QUALIFIED_NAME: ClassVar[Any] = None + TOP_LEVEL_PROJECT_QUALIFIED_NAME: ClassVar[Any] = None + PROJECT_HIERARCHY: ClassVar[Any] = None + TABLEAU_PROJECT_HIERARCHY_QUALIFIED_NAMES: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + WORKBOOK: ClassVar[Any] = None + WORKSHEETS: ClassVar[Any] = None + TABLEAU_EMBEDDED_DASHBOARDS: ClassVar[Any] = None + TABLEAU_PARENT_DASHBOARDS: ClassVar[Any] = None + TABLEAU_DASHBOARD_FIELDS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "TableauDashboard" + + site_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the site in which this dashboard exists.""" + + project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this dashboard exists.""" + + workbook_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workbook in which this dashboard exists.""" + + top_level_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the top-level project in which this dashboard exists.""" + + project_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of top-level projects and their nested child projects.""" + + tableau_project_hierarchy_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Array of qualified names representing the project hierarchy for this Tableau asset.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + workbook: Union[RelatedTableauWorkbook, None, UnsetType] = UNSET + """Workbook in which this dashboard exists.""" + + worksheets: Union[List[RelatedTableauWorksheet], None, UnsetType] = UNSET + """Worksheets that use this dashboard.""" + + tableau_embedded_dashboards: Union[ + List[RelatedTableauDashboard], None, UnsetType + ] = UNSET + """Dashboards that are embedded in this dashboard.""" + + tableau_parent_dashboards: Union[List[RelatedTableauDashboard], None, UnsetType] = ( + UNSET + ) + """Dashboards in which this dashboard is embedded in (list of parent dashboards of this dashboard).""" + + tableau_dashboard_fields: Union[ + List[RelatedTableauDashboardField], None, UnsetType + ] = UNSET + """Fields that exist within this dashboard.""" + + def __post_init__(self) -> None: + self.type_name = "TableauDashboard" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _tableau_dashboard_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> TableauDashboard: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + TableauDashboard instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _tableau_dashboard_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class TableauDashboardAttributes(AssetAttributes): + """TableauDashboard-specific attributes for nested API format.""" + + site_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the site in which this dashboard exists.""" + + project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this dashboard exists.""" + + workbook_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workbook in which this dashboard exists.""" + + top_level_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the top-level project in which this dashboard exists.""" + + project_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of top-level projects and their nested child projects.""" + + tableau_project_hierarchy_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Array of qualified names representing the project hierarchy for this Tableau asset.""" + + +class TableauDashboardRelationshipAttributes(AssetRelationshipAttributes): + """TableauDashboard-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + workbook: Union[RelatedTableauWorkbook, None, UnsetType] = UNSET + """Workbook in which this dashboard exists.""" + + worksheets: Union[List[RelatedTableauWorksheet], None, UnsetType] = UNSET + """Worksheets that use this dashboard.""" + + tableau_embedded_dashboards: Union[ + List[RelatedTableauDashboard], None, UnsetType + ] = UNSET + """Dashboards that are embedded in this dashboard.""" + + tableau_parent_dashboards: Union[List[RelatedTableauDashboard], None, UnsetType] = ( + UNSET + ) + """Dashboards in which this dashboard is embedded in (list of parent dashboards of this dashboard).""" + + tableau_dashboard_fields: Union[ + List[RelatedTableauDashboardField], None, UnsetType + ] = UNSET + """Fields that exist within this dashboard.""" + + +class TableauDashboardNested(AssetNested): + """TableauDashboard in nested API format for high-performance serialization.""" + + attributes: Union[TableauDashboardAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + TableauDashboardRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + TableauDashboardRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + TableauDashboardRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_TABLEAU_DASHBOARD_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", + "workbook", + "worksheets", + "tableau_embedded_dashboards", + "tableau_parent_dashboards", + "tableau_dashboard_fields", +] + + +def _populate_tableau_dashboard_attrs( + attrs: TableauDashboardAttributes, obj: TableauDashboard +) -> None: + """Populate TableauDashboard-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.site_qualified_name = obj.site_qualified_name + attrs.project_qualified_name = obj.project_qualified_name + attrs.workbook_qualified_name = obj.workbook_qualified_name + attrs.top_level_project_qualified_name = obj.top_level_project_qualified_name + attrs.project_hierarchy = obj.project_hierarchy + attrs.tableau_project_hierarchy_qualified_names = ( + obj.tableau_project_hierarchy_qualified_names + ) + + +def _extract_tableau_dashboard_attrs(attrs: TableauDashboardAttributes) -> dict: + """Extract all TableauDashboard attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["site_qualified_name"] = attrs.site_qualified_name + result["project_qualified_name"] = attrs.project_qualified_name + result["workbook_qualified_name"] = attrs.workbook_qualified_name + result["top_level_project_qualified_name"] = attrs.top_level_project_qualified_name + result["project_hierarchy"] = attrs.project_hierarchy + result["tableau_project_hierarchy_qualified_names"] = ( + attrs.tableau_project_hierarchy_qualified_names + ) + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _tableau_dashboard_to_nested( + tableau_dashboard: TableauDashboard, +) -> TableauDashboardNested: + """Convert flat TableauDashboard to nested format.""" + attrs = TableauDashboardAttributes() + _populate_tableau_dashboard_attrs(attrs, tableau_dashboard) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + tableau_dashboard, + _TABLEAU_DASHBOARD_REL_FIELDS, + TableauDashboardRelationshipAttributes, + ) + return TableauDashboardNested( + guid=tableau_dashboard.guid, + type_name=tableau_dashboard.type_name, + status=tableau_dashboard.status, + version=tableau_dashboard.version, + create_time=tableau_dashboard.create_time, + update_time=tableau_dashboard.update_time, + created_by=tableau_dashboard.created_by, + updated_by=tableau_dashboard.updated_by, + classifications=tableau_dashboard.classifications, + classification_names=tableau_dashboard.classification_names, + meanings=tableau_dashboard.meanings, + labels=tableau_dashboard.labels, + business_attributes=tableau_dashboard.business_attributes, + custom_attributes=tableau_dashboard.custom_attributes, + pending_tasks=tableau_dashboard.pending_tasks, + proxy=tableau_dashboard.proxy, + is_incomplete=tableau_dashboard.is_incomplete, + provenance_type=tableau_dashboard.provenance_type, + home_id=tableau_dashboard.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _tableau_dashboard_from_nested(nested: TableauDashboardNested) -> TableauDashboard: + """Convert nested format to flat TableauDashboard.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else TableauDashboardAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _TABLEAU_DASHBOARD_REL_FIELDS, + TableauDashboardRelationshipAttributes, + ) + return TableauDashboard( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_tableau_dashboard_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _tableau_dashboard_to_nested_bytes( + tableau_dashboard: TableauDashboard, serde: Serde +) -> bytes: + """Convert flat TableauDashboard to nested JSON bytes.""" + return serde.encode(_tableau_dashboard_to_nested(tableau_dashboard)) + + +def _tableau_dashboard_from_nested_bytes(data: bytes, serde: Serde) -> TableauDashboard: + """Convert nested JSON bytes to flat TableauDashboard.""" + nested = serde.decode(data, TableauDashboardNested) + return _tableau_dashboard_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +TableauDashboard.SITE_QUALIFIED_NAME = KeywordField( + "siteQualifiedName", "siteQualifiedName" +) +TableauDashboard.PROJECT_QUALIFIED_NAME = KeywordField( + "projectQualifiedName", "projectQualifiedName" +) +TableauDashboard.WORKBOOK_QUALIFIED_NAME = KeywordField( + "workbookQualifiedName", "workbookQualifiedName" +) +TableauDashboard.TOP_LEVEL_PROJECT_QUALIFIED_NAME = KeywordField( + "topLevelProjectQualifiedName", "topLevelProjectQualifiedName" +) +TableauDashboard.PROJECT_HIERARCHY = KeywordField( + "projectHierarchy", "projectHierarchy" +) +TableauDashboard.TABLEAU_PROJECT_HIERARCHY_QUALIFIED_NAMES = KeywordField( + "tableauProjectHierarchyQualifiedNames", "tableauProjectHierarchyQualifiedNames" +) +TableauDashboard.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +TableauDashboard.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +TableauDashboard.ANOMALO_CHECKS = RelationField("anomaloChecks") +TableauDashboard.APPLICATION = RelationField("application") +TableauDashboard.APPLICATION_FIELD = RelationField("applicationField") +TableauDashboard.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +TableauDashboard.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +TableauDashboard.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +TableauDashboard.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +TableauDashboard.METRICS = RelationField("metrics") +TableauDashboard.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +TableauDashboard.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +TableauDashboard.MEANINGS = RelationField("meanings") +TableauDashboard.MC_MONITORS = RelationField("mcMonitors") +TableauDashboard.MC_INCIDENTS = RelationField("mcIncidents") +TableauDashboard.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +TableauDashboard.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +TableauDashboard.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +TableauDashboard.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +TableauDashboard.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +TableauDashboard.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +TableauDashboard.FILES = RelationField("files") +TableauDashboard.LINKS = RelationField("links") +TableauDashboard.README = RelationField("readme") +TableauDashboard.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +TableauDashboard.SODA_CHECKS = RelationField("sodaChecks") +TableauDashboard.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +TableauDashboard.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") +TableauDashboard.WORKBOOK = RelationField("workbook") +TableauDashboard.WORKSHEETS = RelationField("worksheets") +TableauDashboard.TABLEAU_EMBEDDED_DASHBOARDS = RelationField( + "tableauEmbeddedDashboards" +) +TableauDashboard.TABLEAU_PARENT_DASHBOARDS = RelationField("tableauParentDashboards") +TableauDashboard.TABLEAU_DASHBOARD_FIELDS = RelationField("tableauDashboardFields") diff --git a/pyatlan_v9/model/assets/tableau_dashboard_field.py b/pyatlan_v9/model/assets/tableau_dashboard_field.py new file mode 100644 index 000000000..73581e267 --- /dev/null +++ b/pyatlan_v9/model/assets/tableau_dashboard_field.py @@ -0,0 +1,817 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +TableauDashboardField asset model with flattened inheritance. + +This module provides: +- TableauDashboardField: Flat asset class (easy to use) +- TableauDashboardFieldAttributes: Nested attributes struct (extends AssetAttributes) +- TableauDashboardFieldNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .tableau_related import RelatedTableauDashboard, RelatedTableauWorksheetField + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class TableauDashboardField(Asset): + """ + Instance of a Tableau dashboard field in Atlan. + """ + + TABLEAU_SITE_QUALIFIED_NAME: ClassVar[Any] = None + TABLEAU_PROJECT_QUALIFIED_NAME: ClassVar[Any] = None + TABLEAU_TOP_LEVEL_PROJECT_QUALIFIED_NAME: ClassVar[Any] = None + TABLEAU_DASHBOARD_QUALIFIED_NAME: ClassVar[Any] = None + TABLEAU_PROJECT_HIERARCHY: ClassVar[Any] = None + TABLEAU_FULLY_QUALIFIED_NAME: ClassVar[Any] = None + TABLEAU_DASHBOARD_FIELD_DATA_CATEGORY: ClassVar[Any] = None + TABLEAU_DASHBOARD_FIELD_ROLE: ClassVar[Any] = None + TABLEAU_DASHBOARD_FIELD_DATA_TYPE: ClassVar[Any] = None + TABLEAU_UPSTREAM_TABLES: ClassVar[Any] = None + TABLEAU_DASHBOARD_FIELD_FORMULA: ClassVar[Any] = None + TABLEAU_DASHBOARD_FIELD_BIN_SIZE: ClassVar[Any] = None + TABLEAU_DASHBOARD_FIELD_UPSTREAM_COLUMNS: ClassVar[Any] = None + TABLEAU_DASHBOARD_FIELD_UPSTREAM_FIELDS: ClassVar[Any] = None + TABLEAU_DASHBOARD_FIELD_TYPE: ClassVar[Any] = None + TABLEAU_PROJECT_HIERARCHY_QUALIFIED_NAMES: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + TABLEAU_WORKSHEET_FIELD: ClassVar[Any] = None + TABLEAU_DASHBOARD: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "TableauDashboardField" + + tableau_site_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the site in which this dashboard field exists.""" + + tableau_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this dashboard field exists.""" + + tableau_top_level_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the top-level project in which this dashboard field exists.""" + + tableau_dashboard_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the datasource in which this dashboard field exists.""" + + tableau_project_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of top-level projects and their nested child projects.""" + + tableau_fully_qualified_name: Union[str, None, UnsetType] = UNSET + """Name used internally in Tableau to uniquely identify this field.""" + + tableau_dashboard_field_data_category: Union[str, None, UnsetType] = UNSET + """Data category of this field.""" + + tableau_dashboard_field_role: Union[str, None, UnsetType] = UNSET + """Role of this field, for example: 'dimension', 'measure', or 'unknown'.""" + + tableau_dashboard_field_data_type: Union[str, None, UnsetType] = UNSET + """Data type of this field.""" + + tableau_upstream_tables: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Tables upstream to this worksheet field.""" + + tableau_dashboard_field_formula: Union[str, None, UnsetType] = UNSET + """Formula for this field.""" + + tableau_dashboard_field_bin_size: Union[str, None, UnsetType] = UNSET + """Bin size of this field.""" + + tableau_dashboard_field_upstream_columns: Union[ + List[Dict[str, str]], None, UnsetType + ] = UNSET + """Columns upstream to this field.""" + + tableau_dashboard_field_upstream_fields: Union[ + List[Dict[str, str]], None, UnsetType + ] = UNSET + """Fields upstream to this field.""" + + tableau_dashboard_field_type: Union[str, None, UnsetType] = UNSET + """Type of this dashboard field.""" + + tableau_project_hierarchy_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Array of qualified names representing the project hierarchy for this Tableau asset.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + tableau_worksheet_field: Union[RelatedTableauWorksheetField, None, UnsetType] = ( + UNSET + ) + """Dashboard fields that use this worksheet field.""" + + tableau_dashboard: Union[RelatedTableauDashboard, None, UnsetType] = UNSET + """Dashboard in which this field exists.""" + + def __post_init__(self) -> None: + self.type_name = "TableauDashboardField" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _tableau_dashboard_field_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> TableauDashboardField: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + TableauDashboardField instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _tableau_dashboard_field_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class TableauDashboardFieldAttributes(AssetAttributes): + """TableauDashboardField-specific attributes for nested API format.""" + + tableau_site_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the site in which this dashboard field exists.""" + + tableau_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this dashboard field exists.""" + + tableau_top_level_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the top-level project in which this dashboard field exists.""" + + tableau_dashboard_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the datasource in which this dashboard field exists.""" + + tableau_project_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of top-level projects and their nested child projects.""" + + tableau_fully_qualified_name: Union[str, None, UnsetType] = UNSET + """Name used internally in Tableau to uniquely identify this field.""" + + tableau_dashboard_field_data_category: Union[str, None, UnsetType] = UNSET + """Data category of this field.""" + + tableau_dashboard_field_role: Union[str, None, UnsetType] = UNSET + """Role of this field, for example: 'dimension', 'measure', or 'unknown'.""" + + tableau_dashboard_field_data_type: Union[str, None, UnsetType] = UNSET + """Data type of this field.""" + + tableau_upstream_tables: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Tables upstream to this worksheet field.""" + + tableau_dashboard_field_formula: Union[str, None, UnsetType] = UNSET + """Formula for this field.""" + + tableau_dashboard_field_bin_size: Union[str, None, UnsetType] = UNSET + """Bin size of this field.""" + + tableau_dashboard_field_upstream_columns: Union[ + List[Dict[str, str]], None, UnsetType + ] = UNSET + """Columns upstream to this field.""" + + tableau_dashboard_field_upstream_fields: Union[ + List[Dict[str, str]], None, UnsetType + ] = UNSET + """Fields upstream to this field.""" + + tableau_dashboard_field_type: Union[str, None, UnsetType] = UNSET + """Type of this dashboard field.""" + + tableau_project_hierarchy_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Array of qualified names representing the project hierarchy for this Tableau asset.""" + + +class TableauDashboardFieldRelationshipAttributes(AssetRelationshipAttributes): + """TableauDashboardField-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + tableau_worksheet_field: Union[RelatedTableauWorksheetField, None, UnsetType] = ( + UNSET + ) + """Dashboard fields that use this worksheet field.""" + + tableau_dashboard: Union[RelatedTableauDashboard, None, UnsetType] = UNSET + """Dashboard in which this field exists.""" + + +class TableauDashboardFieldNested(AssetNested): + """TableauDashboardField in nested API format for high-performance serialization.""" + + attributes: Union[TableauDashboardFieldAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + TableauDashboardFieldRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + TableauDashboardFieldRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + TableauDashboardFieldRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_TABLEAU_DASHBOARD_FIELD_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", + "tableau_worksheet_field", + "tableau_dashboard", +] + + +def _populate_tableau_dashboard_field_attrs( + attrs: TableauDashboardFieldAttributes, obj: TableauDashboardField +) -> None: + """Populate TableauDashboardField-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.tableau_site_qualified_name = obj.tableau_site_qualified_name + attrs.tableau_project_qualified_name = obj.tableau_project_qualified_name + attrs.tableau_top_level_project_qualified_name = ( + obj.tableau_top_level_project_qualified_name + ) + attrs.tableau_dashboard_qualified_name = obj.tableau_dashboard_qualified_name + attrs.tableau_project_hierarchy = obj.tableau_project_hierarchy + attrs.tableau_fully_qualified_name = obj.tableau_fully_qualified_name + attrs.tableau_dashboard_field_data_category = ( + obj.tableau_dashboard_field_data_category + ) + attrs.tableau_dashboard_field_role = obj.tableau_dashboard_field_role + attrs.tableau_dashboard_field_data_type = obj.tableau_dashboard_field_data_type + attrs.tableau_upstream_tables = obj.tableau_upstream_tables + attrs.tableau_dashboard_field_formula = obj.tableau_dashboard_field_formula + attrs.tableau_dashboard_field_bin_size = obj.tableau_dashboard_field_bin_size + attrs.tableau_dashboard_field_upstream_columns = ( + obj.tableau_dashboard_field_upstream_columns + ) + attrs.tableau_dashboard_field_upstream_fields = ( + obj.tableau_dashboard_field_upstream_fields + ) + attrs.tableau_dashboard_field_type = obj.tableau_dashboard_field_type + attrs.tableau_project_hierarchy_qualified_names = ( + obj.tableau_project_hierarchy_qualified_names + ) + + +def _extract_tableau_dashboard_field_attrs( + attrs: TableauDashboardFieldAttributes, +) -> dict: + """Extract all TableauDashboardField attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["tableau_site_qualified_name"] = attrs.tableau_site_qualified_name + result["tableau_project_qualified_name"] = attrs.tableau_project_qualified_name + result["tableau_top_level_project_qualified_name"] = ( + attrs.tableau_top_level_project_qualified_name + ) + result["tableau_dashboard_qualified_name"] = attrs.tableau_dashboard_qualified_name + result["tableau_project_hierarchy"] = attrs.tableau_project_hierarchy + result["tableau_fully_qualified_name"] = attrs.tableau_fully_qualified_name + result["tableau_dashboard_field_data_category"] = ( + attrs.tableau_dashboard_field_data_category + ) + result["tableau_dashboard_field_role"] = attrs.tableau_dashboard_field_role + result["tableau_dashboard_field_data_type"] = ( + attrs.tableau_dashboard_field_data_type + ) + result["tableau_upstream_tables"] = attrs.tableau_upstream_tables + result["tableau_dashboard_field_formula"] = attrs.tableau_dashboard_field_formula + result["tableau_dashboard_field_bin_size"] = attrs.tableau_dashboard_field_bin_size + result["tableau_dashboard_field_upstream_columns"] = ( + attrs.tableau_dashboard_field_upstream_columns + ) + result["tableau_dashboard_field_upstream_fields"] = ( + attrs.tableau_dashboard_field_upstream_fields + ) + result["tableau_dashboard_field_type"] = attrs.tableau_dashboard_field_type + result["tableau_project_hierarchy_qualified_names"] = ( + attrs.tableau_project_hierarchy_qualified_names + ) + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _tableau_dashboard_field_to_nested( + tableau_dashboard_field: TableauDashboardField, +) -> TableauDashboardFieldNested: + """Convert flat TableauDashboardField to nested format.""" + attrs = TableauDashboardFieldAttributes() + _populate_tableau_dashboard_field_attrs(attrs, tableau_dashboard_field) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + tableau_dashboard_field, + _TABLEAU_DASHBOARD_FIELD_REL_FIELDS, + TableauDashboardFieldRelationshipAttributes, + ) + return TableauDashboardFieldNested( + guid=tableau_dashboard_field.guid, + type_name=tableau_dashboard_field.type_name, + status=tableau_dashboard_field.status, + version=tableau_dashboard_field.version, + create_time=tableau_dashboard_field.create_time, + update_time=tableau_dashboard_field.update_time, + created_by=tableau_dashboard_field.created_by, + updated_by=tableau_dashboard_field.updated_by, + classifications=tableau_dashboard_field.classifications, + classification_names=tableau_dashboard_field.classification_names, + meanings=tableau_dashboard_field.meanings, + labels=tableau_dashboard_field.labels, + business_attributes=tableau_dashboard_field.business_attributes, + custom_attributes=tableau_dashboard_field.custom_attributes, + pending_tasks=tableau_dashboard_field.pending_tasks, + proxy=tableau_dashboard_field.proxy, + is_incomplete=tableau_dashboard_field.is_incomplete, + provenance_type=tableau_dashboard_field.provenance_type, + home_id=tableau_dashboard_field.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _tableau_dashboard_field_from_nested( + nested: TableauDashboardFieldNested, +) -> TableauDashboardField: + """Convert nested format to flat TableauDashboardField.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else TableauDashboardFieldAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _TABLEAU_DASHBOARD_FIELD_REL_FIELDS, + TableauDashboardFieldRelationshipAttributes, + ) + return TableauDashboardField( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_tableau_dashboard_field_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _tableau_dashboard_field_to_nested_bytes( + tableau_dashboard_field: TableauDashboardField, serde: Serde +) -> bytes: + """Convert flat TableauDashboardField to nested JSON bytes.""" + return serde.encode(_tableau_dashboard_field_to_nested(tableau_dashboard_field)) + + +def _tableau_dashboard_field_from_nested_bytes( + data: bytes, serde: Serde +) -> TableauDashboardField: + """Convert nested JSON bytes to flat TableauDashboardField.""" + nested = serde.decode(data, TableauDashboardFieldNested) + return _tableau_dashboard_field_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + RelationField, +) + +TableauDashboardField.TABLEAU_SITE_QUALIFIED_NAME = KeywordField( + "tableauSiteQualifiedName", "tableauSiteQualifiedName" +) +TableauDashboardField.TABLEAU_PROJECT_QUALIFIED_NAME = KeywordField( + "tableauProjectQualifiedName", "tableauProjectQualifiedName" +) +TableauDashboardField.TABLEAU_TOP_LEVEL_PROJECT_QUALIFIED_NAME = KeywordField( + "tableauTopLevelProjectQualifiedName", "tableauTopLevelProjectQualifiedName" +) +TableauDashboardField.TABLEAU_DASHBOARD_QUALIFIED_NAME = KeywordField( + "tableauDashboardQualifiedName", "tableauDashboardQualifiedName" +) +TableauDashboardField.TABLEAU_PROJECT_HIERARCHY = KeywordField( + "tableauProjectHierarchy", "tableauProjectHierarchy" +) +TableauDashboardField.TABLEAU_FULLY_QUALIFIED_NAME = KeywordField( + "tableauFullyQualifiedName", "tableauFullyQualifiedName" +) +TableauDashboardField.TABLEAU_DASHBOARD_FIELD_DATA_CATEGORY = KeywordField( + "tableauDashboardFieldDataCategory", "tableauDashboardFieldDataCategory" +) +TableauDashboardField.TABLEAU_DASHBOARD_FIELD_ROLE = KeywordField( + "tableauDashboardFieldRole", "tableauDashboardFieldRole" +) +TableauDashboardField.TABLEAU_DASHBOARD_FIELD_DATA_TYPE = KeywordTextField( + "tableauDashboardFieldDataType", + "tableauDashboardFieldDataType", + "tableauDashboardFieldDataType.text", +) +TableauDashboardField.TABLEAU_UPSTREAM_TABLES = KeywordField( + "tableauUpstreamTables", "tableauUpstreamTables" +) +TableauDashboardField.TABLEAU_DASHBOARD_FIELD_FORMULA = KeywordField( + "tableauDashboardFieldFormula", "tableauDashboardFieldFormula" +) +TableauDashboardField.TABLEAU_DASHBOARD_FIELD_BIN_SIZE = KeywordField( + "tableauDashboardFieldBinSize", "tableauDashboardFieldBinSize" +) +TableauDashboardField.TABLEAU_DASHBOARD_FIELD_UPSTREAM_COLUMNS = KeywordField( + "tableauDashboardFieldUpstreamColumns", "tableauDashboardFieldUpstreamColumns" +) +TableauDashboardField.TABLEAU_DASHBOARD_FIELD_UPSTREAM_FIELDS = KeywordField( + "tableauDashboardFieldUpstreamFields", "tableauDashboardFieldUpstreamFields" +) +TableauDashboardField.TABLEAU_DASHBOARD_FIELD_TYPE = KeywordField( + "tableauDashboardFieldType", "tableauDashboardFieldType" +) +TableauDashboardField.TABLEAU_PROJECT_HIERARCHY_QUALIFIED_NAMES = KeywordField( + "tableauProjectHierarchyQualifiedNames", "tableauProjectHierarchyQualifiedNames" +) +TableauDashboardField.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +TableauDashboardField.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +TableauDashboardField.ANOMALO_CHECKS = RelationField("anomaloChecks") +TableauDashboardField.APPLICATION = RelationField("application") +TableauDashboardField.APPLICATION_FIELD = RelationField("applicationField") +TableauDashboardField.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +TableauDashboardField.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +TableauDashboardField.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +TableauDashboardField.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +TableauDashboardField.METRICS = RelationField("metrics") +TableauDashboardField.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +TableauDashboardField.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +TableauDashboardField.MEANINGS = RelationField("meanings") +TableauDashboardField.MC_MONITORS = RelationField("mcMonitors") +TableauDashboardField.MC_INCIDENTS = RelationField("mcIncidents") +TableauDashboardField.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +TableauDashboardField.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +TableauDashboardField.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +TableauDashboardField.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +TableauDashboardField.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +TableauDashboardField.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +TableauDashboardField.FILES = RelationField("files") +TableauDashboardField.LINKS = RelationField("links") +TableauDashboardField.README = RelationField("readme") +TableauDashboardField.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +TableauDashboardField.SODA_CHECKS = RelationField("sodaChecks") +TableauDashboardField.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +TableauDashboardField.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") +TableauDashboardField.TABLEAU_WORKSHEET_FIELD = RelationField("tableauWorksheetField") +TableauDashboardField.TABLEAU_DASHBOARD = RelationField("tableauDashboard") diff --git a/pyatlan_v9/model/assets/tableau_datasource.py b/pyatlan_v9/model/assets/tableau_datasource.py new file mode 100644 index 000000000..2f7646258 --- /dev/null +++ b/pyatlan_v9/model/assets/tableau_datasource.py @@ -0,0 +1,752 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +TableauDatasource asset model with flattened inheritance. + +This module provides: +- TableauDatasource: Flat asset class (easy to use) +- TableauDatasourceAttributes: Nested attributes struct (extends AssetAttributes) +- TableauDatasourceNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .tableau_related import ( + RelatedTableauDatasourceField, + RelatedTableauProject, + RelatedTableauWorkbook, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class TableauDatasource(Asset): + """ + Instance of a Tableau datasource in Atlan. These include both published and embedded datasources, and are the link between source data and Tableau. + """ + + SITE_QUALIFIED_NAME: ClassVar[Any] = None + PROJECT_QUALIFIED_NAME: ClassVar[Any] = None + TOP_LEVEL_PROJECT_QUALIFIED_NAME: ClassVar[Any] = None + WORKBOOK_QUALIFIED_NAME: ClassVar[Any] = None + PROJECT_HIERARCHY: ClassVar[Any] = None + IS_PUBLISHED: ClassVar[Any] = None + HAS_EXTRACTS: ClassVar[Any] = None + IS_CERTIFIED: ClassVar[Any] = None + CERTIFIER: ClassVar[Any] = None + CERTIFICATION_NOTE: ClassVar[Any] = None + CERTIFIER_DISPLAY_NAME: ClassVar[Any] = None + UPSTREAM_TABLES: ClassVar[Any] = None + UPSTREAM_DATASOURCES: ClassVar[Any] = None + TABLEAU_PROJECT_HIERARCHY_QUALIFIED_NAMES: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + PROJECT: ClassVar[Any] = None + WORKBOOK: ClassVar[Any] = None + FIELDS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "TableauDatasource" + + site_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the site in which this datasource exists.""" + + project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this datasource exists.""" + + top_level_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the top-level project in which this datasource exists.""" + + workbook_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workbook in which this datasource exists.""" + + project_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of top-level projects with their nested child projects.""" + + is_published: Union[bool, None, UnsetType] = UNSET + """Whether this datasource is published (true) or embedded (false).""" + + has_extracts: Union[bool, None, UnsetType] = UNSET + """Whether this datasource has extracts (true) or not (false).""" + + is_certified: Union[bool, None, UnsetType] = UNSET + """Whether this datasource is certified in Tableau (true) or not (false).""" + + certifier: Union[Dict[str, str], None, UnsetType] = UNSET + """Users that have marked this datasource as cerified, in Tableau.""" + + certification_note: Union[str, None, UnsetType] = UNSET + """Notes related to this datasource being cerfified, in Tableau.""" + + certifier_display_name: Union[str, None, UnsetType] = UNSET + """Name of the user who cerified this datasource, in Tableau.""" + + upstream_tables: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of tables that are upstream of this datasource.""" + + upstream_datasources: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of datasources that are upstream of this datasource.""" + + tableau_project_hierarchy_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Array of qualified names representing the project hierarchy for this Tableau asset.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + project: Union[RelatedTableauProject, None, UnsetType] = UNSET + """Project in which this datasource exists.""" + + workbook: Union[RelatedTableauWorkbook, None, UnsetType] = UNSET + """Workbook in which this datasource exists.""" + + fields: Union[List[RelatedTableauDatasourceField], None, UnsetType] = UNSET + """Fields that exist within this datasource.""" + + def __post_init__(self) -> None: + self.type_name = "TableauDatasource" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _tableau_datasource_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> TableauDatasource: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + TableauDatasource instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _tableau_datasource_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class TableauDatasourceAttributes(AssetAttributes): + """TableauDatasource-specific attributes for nested API format.""" + + site_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the site in which this datasource exists.""" + + project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this datasource exists.""" + + top_level_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the top-level project in which this datasource exists.""" + + workbook_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workbook in which this datasource exists.""" + + project_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of top-level projects with their nested child projects.""" + + is_published: Union[bool, None, UnsetType] = UNSET + """Whether this datasource is published (true) or embedded (false).""" + + has_extracts: Union[bool, None, UnsetType] = UNSET + """Whether this datasource has extracts (true) or not (false).""" + + is_certified: Union[bool, None, UnsetType] = UNSET + """Whether this datasource is certified in Tableau (true) or not (false).""" + + certifier: Union[Dict[str, str], None, UnsetType] = UNSET + """Users that have marked this datasource as cerified, in Tableau.""" + + certification_note: Union[str, None, UnsetType] = UNSET + """Notes related to this datasource being cerfified, in Tableau.""" + + certifier_display_name: Union[str, None, UnsetType] = UNSET + """Name of the user who cerified this datasource, in Tableau.""" + + upstream_tables: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of tables that are upstream of this datasource.""" + + upstream_datasources: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of datasources that are upstream of this datasource.""" + + tableau_project_hierarchy_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Array of qualified names representing the project hierarchy for this Tableau asset.""" + + +class TableauDatasourceRelationshipAttributes(AssetRelationshipAttributes): + """TableauDatasource-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + project: Union[RelatedTableauProject, None, UnsetType] = UNSET + """Project in which this datasource exists.""" + + workbook: Union[RelatedTableauWorkbook, None, UnsetType] = UNSET + """Workbook in which this datasource exists.""" + + fields: Union[List[RelatedTableauDatasourceField], None, UnsetType] = UNSET + """Fields that exist within this datasource.""" + + +class TableauDatasourceNested(AssetNested): + """TableauDatasource in nested API format for high-performance serialization.""" + + attributes: Union[TableauDatasourceAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + TableauDatasourceRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + TableauDatasourceRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + TableauDatasourceRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_TABLEAU_DATASOURCE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", + "project", + "workbook", + "fields", +] + + +def _populate_tableau_datasource_attrs( + attrs: TableauDatasourceAttributes, obj: TableauDatasource +) -> None: + """Populate TableauDatasource-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.site_qualified_name = obj.site_qualified_name + attrs.project_qualified_name = obj.project_qualified_name + attrs.top_level_project_qualified_name = obj.top_level_project_qualified_name + attrs.workbook_qualified_name = obj.workbook_qualified_name + attrs.project_hierarchy = obj.project_hierarchy + attrs.is_published = obj.is_published + attrs.has_extracts = obj.has_extracts + attrs.is_certified = obj.is_certified + attrs.certifier = obj.certifier + attrs.certification_note = obj.certification_note + attrs.certifier_display_name = obj.certifier_display_name + attrs.upstream_tables = obj.upstream_tables + attrs.upstream_datasources = obj.upstream_datasources + attrs.tableau_project_hierarchy_qualified_names = ( + obj.tableau_project_hierarchy_qualified_names + ) + + +def _extract_tableau_datasource_attrs(attrs: TableauDatasourceAttributes) -> dict: + """Extract all TableauDatasource attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["site_qualified_name"] = attrs.site_qualified_name + result["project_qualified_name"] = attrs.project_qualified_name + result["top_level_project_qualified_name"] = attrs.top_level_project_qualified_name + result["workbook_qualified_name"] = attrs.workbook_qualified_name + result["project_hierarchy"] = attrs.project_hierarchy + result["is_published"] = attrs.is_published + result["has_extracts"] = attrs.has_extracts + result["is_certified"] = attrs.is_certified + result["certifier"] = attrs.certifier + result["certification_note"] = attrs.certification_note + result["certifier_display_name"] = attrs.certifier_display_name + result["upstream_tables"] = attrs.upstream_tables + result["upstream_datasources"] = attrs.upstream_datasources + result["tableau_project_hierarchy_qualified_names"] = ( + attrs.tableau_project_hierarchy_qualified_names + ) + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _tableau_datasource_to_nested( + tableau_datasource: TableauDatasource, +) -> TableauDatasourceNested: + """Convert flat TableauDatasource to nested format.""" + attrs = TableauDatasourceAttributes() + _populate_tableau_datasource_attrs(attrs, tableau_datasource) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + tableau_datasource, + _TABLEAU_DATASOURCE_REL_FIELDS, + TableauDatasourceRelationshipAttributes, + ) + return TableauDatasourceNested( + guid=tableau_datasource.guid, + type_name=tableau_datasource.type_name, + status=tableau_datasource.status, + version=tableau_datasource.version, + create_time=tableau_datasource.create_time, + update_time=tableau_datasource.update_time, + created_by=tableau_datasource.created_by, + updated_by=tableau_datasource.updated_by, + classifications=tableau_datasource.classifications, + classification_names=tableau_datasource.classification_names, + meanings=tableau_datasource.meanings, + labels=tableau_datasource.labels, + business_attributes=tableau_datasource.business_attributes, + custom_attributes=tableau_datasource.custom_attributes, + pending_tasks=tableau_datasource.pending_tasks, + proxy=tableau_datasource.proxy, + is_incomplete=tableau_datasource.is_incomplete, + provenance_type=tableau_datasource.provenance_type, + home_id=tableau_datasource.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _tableau_datasource_from_nested( + nested: TableauDatasourceNested, +) -> TableauDatasource: + """Convert nested format to flat TableauDatasource.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else TableauDatasourceAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _TABLEAU_DATASOURCE_REL_FIELDS, + TableauDatasourceRelationshipAttributes, + ) + return TableauDatasource( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_tableau_datasource_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _tableau_datasource_to_nested_bytes( + tableau_datasource: TableauDatasource, serde: Serde +) -> bytes: + """Convert flat TableauDatasource to nested JSON bytes.""" + return serde.encode(_tableau_datasource_to_nested(tableau_datasource)) + + +def _tableau_datasource_from_nested_bytes( + data: bytes, serde: Serde +) -> TableauDatasource: + """Convert nested JSON bytes to flat TableauDatasource.""" + nested = serde.decode(data, TableauDatasourceNested) + return _tableau_datasource_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + RelationField, +) + +TableauDatasource.SITE_QUALIFIED_NAME = KeywordField( + "siteQualifiedName", "siteQualifiedName" +) +TableauDatasource.PROJECT_QUALIFIED_NAME = KeywordField( + "projectQualifiedName", "projectQualifiedName" +) +TableauDatasource.TOP_LEVEL_PROJECT_QUALIFIED_NAME = KeywordField( + "topLevelProjectQualifiedName", "topLevelProjectQualifiedName" +) +TableauDatasource.WORKBOOK_QUALIFIED_NAME = KeywordField( + "workbookQualifiedName", "workbookQualifiedName" +) +TableauDatasource.PROJECT_HIERARCHY = KeywordField( + "projectHierarchy", "projectHierarchy" +) +TableauDatasource.IS_PUBLISHED = BooleanField("isPublished", "isPublished") +TableauDatasource.HAS_EXTRACTS = BooleanField("hasExtracts", "hasExtracts") +TableauDatasource.IS_CERTIFIED = BooleanField("isCertified", "isCertified") +TableauDatasource.CERTIFIER = KeywordField("certifier", "certifier") +TableauDatasource.CERTIFICATION_NOTE = KeywordField( + "certificationNote", "certificationNote" +) +TableauDatasource.CERTIFIER_DISPLAY_NAME = KeywordField( + "certifierDisplayName", "certifierDisplayName" +) +TableauDatasource.UPSTREAM_TABLES = KeywordField("upstreamTables", "upstreamTables") +TableauDatasource.UPSTREAM_DATASOURCES = KeywordField( + "upstreamDatasources", "upstreamDatasources" +) +TableauDatasource.TABLEAU_PROJECT_HIERARCHY_QUALIFIED_NAMES = KeywordField( + "tableauProjectHierarchyQualifiedNames", "tableauProjectHierarchyQualifiedNames" +) +TableauDatasource.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +TableauDatasource.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +TableauDatasource.ANOMALO_CHECKS = RelationField("anomaloChecks") +TableauDatasource.APPLICATION = RelationField("application") +TableauDatasource.APPLICATION_FIELD = RelationField("applicationField") +TableauDatasource.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +TableauDatasource.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +TableauDatasource.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +TableauDatasource.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +TableauDatasource.METRICS = RelationField("metrics") +TableauDatasource.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +TableauDatasource.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +TableauDatasource.MEANINGS = RelationField("meanings") +TableauDatasource.MC_MONITORS = RelationField("mcMonitors") +TableauDatasource.MC_INCIDENTS = RelationField("mcIncidents") +TableauDatasource.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +TableauDatasource.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +TableauDatasource.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +TableauDatasource.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +TableauDatasource.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +TableauDatasource.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +TableauDatasource.FILES = RelationField("files") +TableauDatasource.LINKS = RelationField("links") +TableauDatasource.README = RelationField("readme") +TableauDatasource.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +TableauDatasource.SODA_CHECKS = RelationField("sodaChecks") +TableauDatasource.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +TableauDatasource.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") +TableauDatasource.PROJECT = RelationField("project") +TableauDatasource.WORKBOOK = RelationField("workbook") +TableauDatasource.FIELDS = RelationField("fields") diff --git a/pyatlan_v9/model/assets/tableau_datasource_field.py b/pyatlan_v9/model/assets/tableau_datasource_field.py new file mode 100644 index 000000000..97365607e --- /dev/null +++ b/pyatlan_v9/model/assets/tableau_datasource_field.py @@ -0,0 +1,826 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +TableauDatasourceField asset model with flattened inheritance. + +This module provides: +- TableauDatasourceField: Flat asset class (easy to use) +- TableauDatasourceFieldAttributes: Nested attributes struct (extends AssetAttributes) +- TableauDatasourceFieldNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .tableau_related import ( + RelatedTableauDatasource, + RelatedTableauWorksheet, + RelatedTableauWorksheetField, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class TableauDatasourceField(Asset): + """ + Instance of a Tableau datasource field in Atlan. + """ + + SITE_QUALIFIED_NAME: ClassVar[Any] = None + PROJECT_QUALIFIED_NAME: ClassVar[Any] = None + TOP_LEVEL_PROJECT_QUALIFIED_NAME: ClassVar[Any] = None + WORKBOOK_QUALIFIED_NAME: ClassVar[Any] = None + DATASOURCE_QUALIFIED_NAME: ClassVar[Any] = None + PROJECT_HIERARCHY: ClassVar[Any] = None + FULLY_QUALIFIED_NAME: ClassVar[Any] = None + TABLEAU_DATASOURCE_FIELD_DATA_CATEGORY: ClassVar[Any] = None + TABLEAU_DATASOURCE_FIELD_ROLE: ClassVar[Any] = None + TABLEAU_DATASOURCE_FIELD_DATA_TYPE: ClassVar[Any] = None + UPSTREAM_TABLES: ClassVar[Any] = None + TABLEAU_DATASOURCE_FIELD_FORMULA: ClassVar[Any] = None + TABLEAU_DATASOURCE_FIELD_BIN_SIZE: ClassVar[Any] = None + UPSTREAM_COLUMNS: ClassVar[Any] = None + UPSTREAM_FIELDS: ClassVar[Any] = None + DATASOURCE_FIELD_TYPE: ClassVar[Any] = None + TABLEAU_PROJECT_HIERARCHY_QUALIFIED_NAMES: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + DATASOURCE: ClassVar[Any] = None + WORKSHEETS: ClassVar[Any] = None + TABLEAU_WORKSHEET_FIELD: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "TableauDatasourceField" + + site_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the site in which this datasource field exists.""" + + project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this datasource field exists.""" + + top_level_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the top-level project in which this datasource field exists.""" + + workbook_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workbook in which this datasource field exists.""" + + datasource_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the datasource in which this datasource field exists.""" + + project_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of top-level projects and their nested child projects.""" + + fully_qualified_name: Union[str, None, UnsetType] = UNSET + """Name used internally in Tableau to uniquely identify this field.""" + + tableau_datasource_field_data_category: Union[str, None, UnsetType] = UNSET + """Data category of this field.""" + + tableau_datasource_field_role: Union[str, None, UnsetType] = UNSET + """Role of this field, for example: 'dimension', 'measure', or 'unknown'.""" + + tableau_datasource_field_data_type: Union[str, None, UnsetType] = UNSET + """Data type of this field.""" + + upstream_tables: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Tables upstream to this datasource field.""" + + tableau_datasource_field_formula: Union[str, None, UnsetType] = UNSET + """Formula for this field.""" + + tableau_datasource_field_bin_size: Union[str, None, UnsetType] = UNSET + """Bin size of this field.""" + + upstream_columns: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Columns upstream to this field.""" + + upstream_fields: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Fields upstream to this field.""" + + datasource_field_type: Union[str, None, UnsetType] = UNSET + """Type of this datasource field.""" + + tableau_project_hierarchy_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Array of qualified names representing the project hierarchy for this Tableau asset.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + datasource: Union[RelatedTableauDatasource, None, UnsetType] = UNSET + """Datasource in which this field exists.""" + + worksheets: Union[List[RelatedTableauWorksheet], None, UnsetType] = UNSET + """Worksheets that use this datasource field.""" + + tableau_worksheet_field: Union[RelatedTableauWorksheetField, None, UnsetType] = ( + UNSET + ) + """Worksheet fields that use this datasource field.""" + + def __post_init__(self) -> None: + self.type_name = "TableauDatasourceField" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _tableau_datasource_field_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> TableauDatasourceField: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + TableauDatasourceField instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _tableau_datasource_field_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class TableauDatasourceFieldAttributes(AssetAttributes): + """TableauDatasourceField-specific attributes for nested API format.""" + + site_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the site in which this datasource field exists.""" + + project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this datasource field exists.""" + + top_level_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the top-level project in which this datasource field exists.""" + + workbook_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workbook in which this datasource field exists.""" + + datasource_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the datasource in which this datasource field exists.""" + + project_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of top-level projects and their nested child projects.""" + + fully_qualified_name: Union[str, None, UnsetType] = UNSET + """Name used internally in Tableau to uniquely identify this field.""" + + tableau_datasource_field_data_category: Union[str, None, UnsetType] = UNSET + """Data category of this field.""" + + tableau_datasource_field_role: Union[str, None, UnsetType] = UNSET + """Role of this field, for example: 'dimension', 'measure', or 'unknown'.""" + + tableau_datasource_field_data_type: Union[str, None, UnsetType] = UNSET + """Data type of this field.""" + + upstream_tables: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Tables upstream to this datasource field.""" + + tableau_datasource_field_formula: Union[str, None, UnsetType] = UNSET + """Formula for this field.""" + + tableau_datasource_field_bin_size: Union[str, None, UnsetType] = UNSET + """Bin size of this field.""" + + upstream_columns: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Columns upstream to this field.""" + + upstream_fields: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Fields upstream to this field.""" + + datasource_field_type: Union[str, None, UnsetType] = UNSET + """Type of this datasource field.""" + + tableau_project_hierarchy_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Array of qualified names representing the project hierarchy for this Tableau asset.""" + + +class TableauDatasourceFieldRelationshipAttributes(AssetRelationshipAttributes): + """TableauDatasourceField-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + datasource: Union[RelatedTableauDatasource, None, UnsetType] = UNSET + """Datasource in which this field exists.""" + + worksheets: Union[List[RelatedTableauWorksheet], None, UnsetType] = UNSET + """Worksheets that use this datasource field.""" + + tableau_worksheet_field: Union[RelatedTableauWorksheetField, None, UnsetType] = ( + UNSET + ) + """Worksheet fields that use this datasource field.""" + + +class TableauDatasourceFieldNested(AssetNested): + """TableauDatasourceField in nested API format for high-performance serialization.""" + + attributes: Union[TableauDatasourceFieldAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + TableauDatasourceFieldRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + TableauDatasourceFieldRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + TableauDatasourceFieldRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_TABLEAU_DATASOURCE_FIELD_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", + "datasource", + "worksheets", + "tableau_worksheet_field", +] + + +def _populate_tableau_datasource_field_attrs( + attrs: TableauDatasourceFieldAttributes, obj: TableauDatasourceField +) -> None: + """Populate TableauDatasourceField-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.site_qualified_name = obj.site_qualified_name + attrs.project_qualified_name = obj.project_qualified_name + attrs.top_level_project_qualified_name = obj.top_level_project_qualified_name + attrs.workbook_qualified_name = obj.workbook_qualified_name + attrs.datasource_qualified_name = obj.datasource_qualified_name + attrs.project_hierarchy = obj.project_hierarchy + attrs.fully_qualified_name = obj.fully_qualified_name + attrs.tableau_datasource_field_data_category = ( + obj.tableau_datasource_field_data_category + ) + attrs.tableau_datasource_field_role = obj.tableau_datasource_field_role + attrs.tableau_datasource_field_data_type = obj.tableau_datasource_field_data_type + attrs.upstream_tables = obj.upstream_tables + attrs.tableau_datasource_field_formula = obj.tableau_datasource_field_formula + attrs.tableau_datasource_field_bin_size = obj.tableau_datasource_field_bin_size + attrs.upstream_columns = obj.upstream_columns + attrs.upstream_fields = obj.upstream_fields + attrs.datasource_field_type = obj.datasource_field_type + attrs.tableau_project_hierarchy_qualified_names = ( + obj.tableau_project_hierarchy_qualified_names + ) + + +def _extract_tableau_datasource_field_attrs( + attrs: TableauDatasourceFieldAttributes, +) -> dict: + """Extract all TableauDatasourceField attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["site_qualified_name"] = attrs.site_qualified_name + result["project_qualified_name"] = attrs.project_qualified_name + result["top_level_project_qualified_name"] = attrs.top_level_project_qualified_name + result["workbook_qualified_name"] = attrs.workbook_qualified_name + result["datasource_qualified_name"] = attrs.datasource_qualified_name + result["project_hierarchy"] = attrs.project_hierarchy + result["fully_qualified_name"] = attrs.fully_qualified_name + result["tableau_datasource_field_data_category"] = ( + attrs.tableau_datasource_field_data_category + ) + result["tableau_datasource_field_role"] = attrs.tableau_datasource_field_role + result["tableau_datasource_field_data_type"] = ( + attrs.tableau_datasource_field_data_type + ) + result["upstream_tables"] = attrs.upstream_tables + result["tableau_datasource_field_formula"] = attrs.tableau_datasource_field_formula + result["tableau_datasource_field_bin_size"] = ( + attrs.tableau_datasource_field_bin_size + ) + result["upstream_columns"] = attrs.upstream_columns + result["upstream_fields"] = attrs.upstream_fields + result["datasource_field_type"] = attrs.datasource_field_type + result["tableau_project_hierarchy_qualified_names"] = ( + attrs.tableau_project_hierarchy_qualified_names + ) + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _tableau_datasource_field_to_nested( + tableau_datasource_field: TableauDatasourceField, +) -> TableauDatasourceFieldNested: + """Convert flat TableauDatasourceField to nested format.""" + attrs = TableauDatasourceFieldAttributes() + _populate_tableau_datasource_field_attrs(attrs, tableau_datasource_field) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + tableau_datasource_field, + _TABLEAU_DATASOURCE_FIELD_REL_FIELDS, + TableauDatasourceFieldRelationshipAttributes, + ) + return TableauDatasourceFieldNested( + guid=tableau_datasource_field.guid, + type_name=tableau_datasource_field.type_name, + status=tableau_datasource_field.status, + version=tableau_datasource_field.version, + create_time=tableau_datasource_field.create_time, + update_time=tableau_datasource_field.update_time, + created_by=tableau_datasource_field.created_by, + updated_by=tableau_datasource_field.updated_by, + classifications=tableau_datasource_field.classifications, + classification_names=tableau_datasource_field.classification_names, + meanings=tableau_datasource_field.meanings, + labels=tableau_datasource_field.labels, + business_attributes=tableau_datasource_field.business_attributes, + custom_attributes=tableau_datasource_field.custom_attributes, + pending_tasks=tableau_datasource_field.pending_tasks, + proxy=tableau_datasource_field.proxy, + is_incomplete=tableau_datasource_field.is_incomplete, + provenance_type=tableau_datasource_field.provenance_type, + home_id=tableau_datasource_field.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _tableau_datasource_field_from_nested( + nested: TableauDatasourceFieldNested, +) -> TableauDatasourceField: + """Convert nested format to flat TableauDatasourceField.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else TableauDatasourceFieldAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _TABLEAU_DATASOURCE_FIELD_REL_FIELDS, + TableauDatasourceFieldRelationshipAttributes, + ) + return TableauDatasourceField( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_tableau_datasource_field_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _tableau_datasource_field_to_nested_bytes( + tableau_datasource_field: TableauDatasourceField, serde: Serde +) -> bytes: + """Convert flat TableauDatasourceField to nested JSON bytes.""" + return serde.encode(_tableau_datasource_field_to_nested(tableau_datasource_field)) + + +def _tableau_datasource_field_from_nested_bytes( + data: bytes, serde: Serde +) -> TableauDatasourceField: + """Convert nested JSON bytes to flat TableauDatasourceField.""" + nested = serde.decode(data, TableauDatasourceFieldNested) + return _tableau_datasource_field_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + RelationField, +) + +TableauDatasourceField.SITE_QUALIFIED_NAME = KeywordField( + "siteQualifiedName", "siteQualifiedName" +) +TableauDatasourceField.PROJECT_QUALIFIED_NAME = KeywordField( + "projectQualifiedName", "projectQualifiedName" +) +TableauDatasourceField.TOP_LEVEL_PROJECT_QUALIFIED_NAME = KeywordField( + "topLevelProjectQualifiedName", "topLevelProjectQualifiedName" +) +TableauDatasourceField.WORKBOOK_QUALIFIED_NAME = KeywordField( + "workbookQualifiedName", "workbookQualifiedName" +) +TableauDatasourceField.DATASOURCE_QUALIFIED_NAME = KeywordField( + "datasourceQualifiedName", "datasourceQualifiedName" +) +TableauDatasourceField.PROJECT_HIERARCHY = KeywordField( + "projectHierarchy", "projectHierarchy" +) +TableauDatasourceField.FULLY_QUALIFIED_NAME = KeywordField( + "fullyQualifiedName", "fullyQualifiedName" +) +TableauDatasourceField.TABLEAU_DATASOURCE_FIELD_DATA_CATEGORY = KeywordField( + "tableauDatasourceFieldDataCategory", "tableauDatasourceFieldDataCategory" +) +TableauDatasourceField.TABLEAU_DATASOURCE_FIELD_ROLE = KeywordField( + "tableauDatasourceFieldRole", "tableauDatasourceFieldRole" +) +TableauDatasourceField.TABLEAU_DATASOURCE_FIELD_DATA_TYPE = KeywordTextField( + "tableauDatasourceFieldDataType", + "tableauDatasourceFieldDataType", + "tableauDatasourceFieldDataType.text", +) +TableauDatasourceField.UPSTREAM_TABLES = KeywordField( + "upstreamTables", "upstreamTables" +) +TableauDatasourceField.TABLEAU_DATASOURCE_FIELD_FORMULA = KeywordField( + "tableauDatasourceFieldFormula", "tableauDatasourceFieldFormula" +) +TableauDatasourceField.TABLEAU_DATASOURCE_FIELD_BIN_SIZE = KeywordField( + "tableauDatasourceFieldBinSize", "tableauDatasourceFieldBinSize" +) +TableauDatasourceField.UPSTREAM_COLUMNS = KeywordField( + "upstreamColumns", "upstreamColumns" +) +TableauDatasourceField.UPSTREAM_FIELDS = KeywordField( + "upstreamFields", "upstreamFields" +) +TableauDatasourceField.DATASOURCE_FIELD_TYPE = KeywordField( + "datasourceFieldType", "datasourceFieldType" +) +TableauDatasourceField.TABLEAU_PROJECT_HIERARCHY_QUALIFIED_NAMES = KeywordField( + "tableauProjectHierarchyQualifiedNames", "tableauProjectHierarchyQualifiedNames" +) +TableauDatasourceField.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +TableauDatasourceField.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +TableauDatasourceField.ANOMALO_CHECKS = RelationField("anomaloChecks") +TableauDatasourceField.APPLICATION = RelationField("application") +TableauDatasourceField.APPLICATION_FIELD = RelationField("applicationField") +TableauDatasourceField.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +TableauDatasourceField.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +TableauDatasourceField.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +TableauDatasourceField.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +TableauDatasourceField.METRICS = RelationField("metrics") +TableauDatasourceField.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +TableauDatasourceField.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +TableauDatasourceField.MEANINGS = RelationField("meanings") +TableauDatasourceField.MC_MONITORS = RelationField("mcMonitors") +TableauDatasourceField.MC_INCIDENTS = RelationField("mcIncidents") +TableauDatasourceField.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +TableauDatasourceField.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +TableauDatasourceField.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +TableauDatasourceField.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +TableauDatasourceField.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +TableauDatasourceField.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +TableauDatasourceField.FILES = RelationField("files") +TableauDatasourceField.LINKS = RelationField("links") +TableauDatasourceField.README = RelationField("readme") +TableauDatasourceField.SCHEMA_REGISTRY_SUBJECTS = RelationField( + "schemaRegistrySubjects" +) +TableauDatasourceField.SODA_CHECKS = RelationField("sodaChecks") +TableauDatasourceField.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +TableauDatasourceField.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") +TableauDatasourceField.DATASOURCE = RelationField("datasource") +TableauDatasourceField.WORKSHEETS = RelationField("worksheets") +TableauDatasourceField.TABLEAU_WORKSHEET_FIELD = RelationField("tableauWorksheetField") diff --git a/pyatlan_v9/model/assets/tableau_flow.py b/pyatlan_v9/model/assets/tableau_flow.py new file mode 100644 index 000000000..b7ddaff12 --- /dev/null +++ b/pyatlan_v9/model/assets/tableau_flow.py @@ -0,0 +1,639 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +TableauFlow asset model with flattened inheritance. + +This module provides: +- TableauFlow: Flat asset class (easy to use) +- TableauFlowAttributes: Nested attributes struct (extends AssetAttributes) +- TableauFlowNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .tableau_related import RelatedTableauProject + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class TableauFlow(Asset): + """ + Instance of a Tableau flow in Atlan. + """ + + SITE_QUALIFIED_NAME: ClassVar[Any] = None + PROJECT_QUALIFIED_NAME: ClassVar[Any] = None + TOP_LEVEL_PROJECT_QUALIFIED_NAME: ClassVar[Any] = None + PROJECT_HIERARCHY: ClassVar[Any] = None + INPUT_FIELDS: ClassVar[Any] = None + OUTPUT_FIELDS: ClassVar[Any] = None + OUTPUT_STEPS: ClassVar[Any] = None + TABLEAU_PROJECT_HIERARCHY_QUALIFIED_NAMES: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + PROJECT: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "TableauFlow" + + site_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the site in which this flow exists.""" + + project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this flow exists.""" + + top_level_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the top-level project in which this flow exists.""" + + project_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of top-level projects with their nested child projects.""" + + input_fields: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of fields that are inputs to this flow.""" + + output_fields: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of fields that are outputs from this flow.""" + + output_steps: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of steps that are outputs from this flow.""" + + tableau_project_hierarchy_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Array of qualified names representing the project hierarchy for this Tableau asset.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + project: Union[RelatedTableauProject, None, UnsetType] = UNSET + """Project in which this flow exists.""" + + def __post_init__(self) -> None: + self.type_name = "TableauFlow" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _tableau_flow_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> TableauFlow: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + TableauFlow instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _tableau_flow_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class TableauFlowAttributes(AssetAttributes): + """TableauFlow-specific attributes for nested API format.""" + + site_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the site in which this flow exists.""" + + project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this flow exists.""" + + top_level_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the top-level project in which this flow exists.""" + + project_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of top-level projects with their nested child projects.""" + + input_fields: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of fields that are inputs to this flow.""" + + output_fields: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of fields that are outputs from this flow.""" + + output_steps: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of steps that are outputs from this flow.""" + + tableau_project_hierarchy_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Array of qualified names representing the project hierarchy for this Tableau asset.""" + + +class TableauFlowRelationshipAttributes(AssetRelationshipAttributes): + """TableauFlow-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + project: Union[RelatedTableauProject, None, UnsetType] = UNSET + """Project in which this flow exists.""" + + +class TableauFlowNested(AssetNested): + """TableauFlow in nested API format for high-performance serialization.""" + + attributes: Union[TableauFlowAttributes, UnsetType] = UNSET + relationship_attributes: Union[TableauFlowRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + TableauFlowRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + TableauFlowRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_TABLEAU_FLOW_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", + "project", +] + + +def _populate_tableau_flow_attrs( + attrs: TableauFlowAttributes, obj: TableauFlow +) -> None: + """Populate TableauFlow-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.site_qualified_name = obj.site_qualified_name + attrs.project_qualified_name = obj.project_qualified_name + attrs.top_level_project_qualified_name = obj.top_level_project_qualified_name + attrs.project_hierarchy = obj.project_hierarchy + attrs.input_fields = obj.input_fields + attrs.output_fields = obj.output_fields + attrs.output_steps = obj.output_steps + attrs.tableau_project_hierarchy_qualified_names = ( + obj.tableau_project_hierarchy_qualified_names + ) + + +def _extract_tableau_flow_attrs(attrs: TableauFlowAttributes) -> dict: + """Extract all TableauFlow attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["site_qualified_name"] = attrs.site_qualified_name + result["project_qualified_name"] = attrs.project_qualified_name + result["top_level_project_qualified_name"] = attrs.top_level_project_qualified_name + result["project_hierarchy"] = attrs.project_hierarchy + result["input_fields"] = attrs.input_fields + result["output_fields"] = attrs.output_fields + result["output_steps"] = attrs.output_steps + result["tableau_project_hierarchy_qualified_names"] = ( + attrs.tableau_project_hierarchy_qualified_names + ) + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _tableau_flow_to_nested(tableau_flow: TableauFlow) -> TableauFlowNested: + """Convert flat TableauFlow to nested format.""" + attrs = TableauFlowAttributes() + _populate_tableau_flow_attrs(attrs, tableau_flow) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + tableau_flow, _TABLEAU_FLOW_REL_FIELDS, TableauFlowRelationshipAttributes + ) + return TableauFlowNested( + guid=tableau_flow.guid, + type_name=tableau_flow.type_name, + status=tableau_flow.status, + version=tableau_flow.version, + create_time=tableau_flow.create_time, + update_time=tableau_flow.update_time, + created_by=tableau_flow.created_by, + updated_by=tableau_flow.updated_by, + classifications=tableau_flow.classifications, + classification_names=tableau_flow.classification_names, + meanings=tableau_flow.meanings, + labels=tableau_flow.labels, + business_attributes=tableau_flow.business_attributes, + custom_attributes=tableau_flow.custom_attributes, + pending_tasks=tableau_flow.pending_tasks, + proxy=tableau_flow.proxy, + is_incomplete=tableau_flow.is_incomplete, + provenance_type=tableau_flow.provenance_type, + home_id=tableau_flow.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _tableau_flow_from_nested(nested: TableauFlowNested) -> TableauFlow: + """Convert nested format to flat TableauFlow.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else TableauFlowAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _TABLEAU_FLOW_REL_FIELDS, + TableauFlowRelationshipAttributes, + ) + return TableauFlow( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_tableau_flow_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _tableau_flow_to_nested_bytes(tableau_flow: TableauFlow, serde: Serde) -> bytes: + """Convert flat TableauFlow to nested JSON bytes.""" + return serde.encode(_tableau_flow_to_nested(tableau_flow)) + + +def _tableau_flow_from_nested_bytes(data: bytes, serde: Serde) -> TableauFlow: + """Convert nested JSON bytes to flat TableauFlow.""" + nested = serde.decode(data, TableauFlowNested) + return _tableau_flow_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +TableauFlow.SITE_QUALIFIED_NAME = KeywordField("siteQualifiedName", "siteQualifiedName") +TableauFlow.PROJECT_QUALIFIED_NAME = KeywordField( + "projectQualifiedName", "projectQualifiedName" +) +TableauFlow.TOP_LEVEL_PROJECT_QUALIFIED_NAME = KeywordField( + "topLevelProjectQualifiedName", "topLevelProjectQualifiedName" +) +TableauFlow.PROJECT_HIERARCHY = KeywordField("projectHierarchy", "projectHierarchy") +TableauFlow.INPUT_FIELDS = KeywordField("inputFields", "inputFields") +TableauFlow.OUTPUT_FIELDS = KeywordField("outputFields", "outputFields") +TableauFlow.OUTPUT_STEPS = KeywordField("outputSteps", "outputSteps") +TableauFlow.TABLEAU_PROJECT_HIERARCHY_QUALIFIED_NAMES = KeywordField( + "tableauProjectHierarchyQualifiedNames", "tableauProjectHierarchyQualifiedNames" +) +TableauFlow.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +TableauFlow.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +TableauFlow.ANOMALO_CHECKS = RelationField("anomaloChecks") +TableauFlow.APPLICATION = RelationField("application") +TableauFlow.APPLICATION_FIELD = RelationField("applicationField") +TableauFlow.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +TableauFlow.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +TableauFlow.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +TableauFlow.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +TableauFlow.METRICS = RelationField("metrics") +TableauFlow.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +TableauFlow.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +TableauFlow.MEANINGS = RelationField("meanings") +TableauFlow.MC_MONITORS = RelationField("mcMonitors") +TableauFlow.MC_INCIDENTS = RelationField("mcIncidents") +TableauFlow.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +TableauFlow.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +TableauFlow.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +TableauFlow.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +TableauFlow.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +TableauFlow.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +TableauFlow.FILES = RelationField("files") +TableauFlow.LINKS = RelationField("links") +TableauFlow.README = RelationField("readme") +TableauFlow.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +TableauFlow.SODA_CHECKS = RelationField("sodaChecks") +TableauFlow.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +TableauFlow.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") +TableauFlow.PROJECT = RelationField("project") diff --git a/pyatlan_v9/model/assets/tableau_metric.py b/pyatlan_v9/model/assets/tableau_metric.py new file mode 100644 index 000000000..16be1183c --- /dev/null +++ b/pyatlan_v9/model/assets/tableau_metric.py @@ -0,0 +1,617 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +TableauMetric asset model with flattened inheritance. + +This module provides: +- TableauMetric: Flat asset class (easy to use) +- TableauMetricAttributes: Nested attributes struct (extends AssetAttributes) +- TableauMetricNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .tableau_related import RelatedTableauProject + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class TableauMetric(Asset): + """ + Instance of a Tableau metric in Atlan. + """ + + SITE_QUALIFIED_NAME: ClassVar[Any] = None + PROJECT_QUALIFIED_NAME: ClassVar[Any] = None + TOP_LEVEL_PROJECT_QUALIFIED_NAME: ClassVar[Any] = None + PROJECT_HIERARCHY: ClassVar[Any] = None + TABLEAU_PROJECT_HIERARCHY_QUALIFIED_NAMES: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + PROJECT: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "TableauMetric" + + site_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the site in which this metric exists.""" + + project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this metric exists.""" + + top_level_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the top-level project in which this metric exists.""" + + project_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of top-level projects with their nested child projects.""" + + tableau_project_hierarchy_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Array of qualified names representing the project hierarchy for this Tableau asset.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + project: Union[RelatedTableauProject, None, UnsetType] = UNSET + """Project in which this metric exists.""" + + def __post_init__(self) -> None: + self.type_name = "TableauMetric" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _tableau_metric_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> TableauMetric: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + TableauMetric instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _tableau_metric_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class TableauMetricAttributes(AssetAttributes): + """TableauMetric-specific attributes for nested API format.""" + + site_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the site in which this metric exists.""" + + project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this metric exists.""" + + top_level_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the top-level project in which this metric exists.""" + + project_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of top-level projects with their nested child projects.""" + + tableau_project_hierarchy_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Array of qualified names representing the project hierarchy for this Tableau asset.""" + + +class TableauMetricRelationshipAttributes(AssetRelationshipAttributes): + """TableauMetric-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + project: Union[RelatedTableauProject, None, UnsetType] = UNSET + """Project in which this metric exists.""" + + +class TableauMetricNested(AssetNested): + """TableauMetric in nested API format for high-performance serialization.""" + + attributes: Union[TableauMetricAttributes, UnsetType] = UNSET + relationship_attributes: Union[TableauMetricRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + TableauMetricRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + TableauMetricRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_TABLEAU_METRIC_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", + "project", +] + + +def _populate_tableau_metric_attrs( + attrs: TableauMetricAttributes, obj: TableauMetric +) -> None: + """Populate TableauMetric-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.site_qualified_name = obj.site_qualified_name + attrs.project_qualified_name = obj.project_qualified_name + attrs.top_level_project_qualified_name = obj.top_level_project_qualified_name + attrs.project_hierarchy = obj.project_hierarchy + attrs.tableau_project_hierarchy_qualified_names = ( + obj.tableau_project_hierarchy_qualified_names + ) + + +def _extract_tableau_metric_attrs(attrs: TableauMetricAttributes) -> dict: + """Extract all TableauMetric attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["site_qualified_name"] = attrs.site_qualified_name + result["project_qualified_name"] = attrs.project_qualified_name + result["top_level_project_qualified_name"] = attrs.top_level_project_qualified_name + result["project_hierarchy"] = attrs.project_hierarchy + result["tableau_project_hierarchy_qualified_names"] = ( + attrs.tableau_project_hierarchy_qualified_names + ) + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _tableau_metric_to_nested(tableau_metric: TableauMetric) -> TableauMetricNested: + """Convert flat TableauMetric to nested format.""" + attrs = TableauMetricAttributes() + _populate_tableau_metric_attrs(attrs, tableau_metric) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + tableau_metric, _TABLEAU_METRIC_REL_FIELDS, TableauMetricRelationshipAttributes + ) + return TableauMetricNested( + guid=tableau_metric.guid, + type_name=tableau_metric.type_name, + status=tableau_metric.status, + version=tableau_metric.version, + create_time=tableau_metric.create_time, + update_time=tableau_metric.update_time, + created_by=tableau_metric.created_by, + updated_by=tableau_metric.updated_by, + classifications=tableau_metric.classifications, + classification_names=tableau_metric.classification_names, + meanings=tableau_metric.meanings, + labels=tableau_metric.labels, + business_attributes=tableau_metric.business_attributes, + custom_attributes=tableau_metric.custom_attributes, + pending_tasks=tableau_metric.pending_tasks, + proxy=tableau_metric.proxy, + is_incomplete=tableau_metric.is_incomplete, + provenance_type=tableau_metric.provenance_type, + home_id=tableau_metric.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _tableau_metric_from_nested(nested: TableauMetricNested) -> TableauMetric: + """Convert nested format to flat TableauMetric.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else TableauMetricAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _TABLEAU_METRIC_REL_FIELDS, + TableauMetricRelationshipAttributes, + ) + return TableauMetric( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_tableau_metric_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _tableau_metric_to_nested_bytes( + tableau_metric: TableauMetric, serde: Serde +) -> bytes: + """Convert flat TableauMetric to nested JSON bytes.""" + return serde.encode(_tableau_metric_to_nested(tableau_metric)) + + +def _tableau_metric_from_nested_bytes(data: bytes, serde: Serde) -> TableauMetric: + """Convert nested JSON bytes to flat TableauMetric.""" + nested = serde.decode(data, TableauMetricNested) + return _tableau_metric_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +TableauMetric.SITE_QUALIFIED_NAME = KeywordField( + "siteQualifiedName", "siteQualifiedName" +) +TableauMetric.PROJECT_QUALIFIED_NAME = KeywordField( + "projectQualifiedName", "projectQualifiedName" +) +TableauMetric.TOP_LEVEL_PROJECT_QUALIFIED_NAME = KeywordField( + "topLevelProjectQualifiedName", "topLevelProjectQualifiedName" +) +TableauMetric.PROJECT_HIERARCHY = KeywordField("projectHierarchy", "projectHierarchy") +TableauMetric.TABLEAU_PROJECT_HIERARCHY_QUALIFIED_NAMES = KeywordField( + "tableauProjectHierarchyQualifiedNames", "tableauProjectHierarchyQualifiedNames" +) +TableauMetric.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +TableauMetric.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +TableauMetric.ANOMALO_CHECKS = RelationField("anomaloChecks") +TableauMetric.APPLICATION = RelationField("application") +TableauMetric.APPLICATION_FIELD = RelationField("applicationField") +TableauMetric.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +TableauMetric.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +TableauMetric.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +TableauMetric.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +TableauMetric.METRICS = RelationField("metrics") +TableauMetric.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +TableauMetric.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +TableauMetric.MEANINGS = RelationField("meanings") +TableauMetric.MC_MONITORS = RelationField("mcMonitors") +TableauMetric.MC_INCIDENTS = RelationField("mcIncidents") +TableauMetric.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +TableauMetric.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +TableauMetric.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +TableauMetric.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +TableauMetric.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +TableauMetric.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +TableauMetric.FILES = RelationField("files") +TableauMetric.LINKS = RelationField("links") +TableauMetric.README = RelationField("readme") +TableauMetric.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +TableauMetric.SODA_CHECKS = RelationField("sodaChecks") +TableauMetric.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +TableauMetric.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") +TableauMetric.PROJECT = RelationField("project") diff --git a/pyatlan_v9/model/assets/tableau_project.py b/pyatlan_v9/model/assets/tableau_project.py new file mode 100644 index 000000000..e303e1c5e --- /dev/null +++ b/pyatlan_v9/model/assets/tableau_project.py @@ -0,0 +1,671 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +TableauProject asset model with flattened inheritance. + +This module provides: +- TableauProject: Flat asset class (easy to use) +- TableauProjectAttributes: Nested attributes struct (extends AssetAttributes) +- TableauProjectNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .tableau_related import ( + RelatedTableauDatasource, + RelatedTableauFlow, + RelatedTableauProject, + RelatedTableauSite, + RelatedTableauWorkbook, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class TableauProject(Asset): + """ + Instance of a Tableau project in Atlan. These are used to organize other assets and for access control, and can be nested. + """ + + SITE_QUALIFIED_NAME: ClassVar[Any] = None + TOP_LEVEL_PROJECT_QUALIFIED_NAME: ClassVar[Any] = None + IS_TOP_LEVEL_PROJECT: ClassVar[Any] = None + PROJECT_HIERARCHY: ClassVar[Any] = None + TABLEAU_PROJECT_HIERARCHY_QUALIFIED_NAMES: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + DATASOURCES: ClassVar[Any] = None + SITE: ClassVar[Any] = None + CHILD_PROJECTS: ClassVar[Any] = None + PARENT_PROJECT: ClassVar[Any] = None + FLOWS: ClassVar[Any] = None + WORKBOOKS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "TableauProject" + + site_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the site in which this project exists.""" + + top_level_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the top-level project in which this project exists, if this is a nested project.""" + + is_top_level_project: Union[bool, None, UnsetType] = UNSET + """Whether this project is a top-level project (true) or not (false).""" + + project_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of top-level projects with their nested child projects.""" + + tableau_project_hierarchy_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Array of qualified names representing the project hierarchy for this Tableau asset.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + datasources: Union[List[RelatedTableauDatasource], None, UnsetType] = UNSET + """Datasources that exist within this project.""" + + site: Union[RelatedTableauSite, None, UnsetType] = UNSET + """Site in which this project exists.""" + + child_projects: Union[List[RelatedTableauProject], None, UnsetType] = UNSET + """Sub-projects that exist within this project.""" + + parent_project: Union[RelatedTableauProject, None, UnsetType] = UNSET + """Project in which this sub-project exists.""" + + flows: Union[List[RelatedTableauFlow], None, UnsetType] = UNSET + """Flows that exist within this project.""" + + workbooks: Union[List[RelatedTableauWorkbook], None, UnsetType] = UNSET + """Workbooks that exist within this project.""" + + def __post_init__(self) -> None: + self.type_name = "TableauProject" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _tableau_project_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> TableauProject: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + TableauProject instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _tableau_project_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class TableauProjectAttributes(AssetAttributes): + """TableauProject-specific attributes for nested API format.""" + + site_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the site in which this project exists.""" + + top_level_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the top-level project in which this project exists, if this is a nested project.""" + + is_top_level_project: Union[bool, None, UnsetType] = UNSET + """Whether this project is a top-level project (true) or not (false).""" + + project_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of top-level projects with their nested child projects.""" + + tableau_project_hierarchy_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Array of qualified names representing the project hierarchy for this Tableau asset.""" + + +class TableauProjectRelationshipAttributes(AssetRelationshipAttributes): + """TableauProject-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + datasources: Union[List[RelatedTableauDatasource], None, UnsetType] = UNSET + """Datasources that exist within this project.""" + + site: Union[RelatedTableauSite, None, UnsetType] = UNSET + """Site in which this project exists.""" + + child_projects: Union[List[RelatedTableauProject], None, UnsetType] = UNSET + """Sub-projects that exist within this project.""" + + parent_project: Union[RelatedTableauProject, None, UnsetType] = UNSET + """Project in which this sub-project exists.""" + + flows: Union[List[RelatedTableauFlow], None, UnsetType] = UNSET + """Flows that exist within this project.""" + + workbooks: Union[List[RelatedTableauWorkbook], None, UnsetType] = UNSET + """Workbooks that exist within this project.""" + + +class TableauProjectNested(AssetNested): + """TableauProject in nested API format for high-performance serialization.""" + + attributes: Union[TableauProjectAttributes, UnsetType] = UNSET + relationship_attributes: Union[TableauProjectRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + TableauProjectRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + TableauProjectRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_TABLEAU_PROJECT_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", + "datasources", + "site", + "child_projects", + "parent_project", + "flows", + "workbooks", +] + + +def _populate_tableau_project_attrs( + attrs: TableauProjectAttributes, obj: TableauProject +) -> None: + """Populate TableauProject-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.site_qualified_name = obj.site_qualified_name + attrs.top_level_project_qualified_name = obj.top_level_project_qualified_name + attrs.is_top_level_project = obj.is_top_level_project + attrs.project_hierarchy = obj.project_hierarchy + attrs.tableau_project_hierarchy_qualified_names = ( + obj.tableau_project_hierarchy_qualified_names + ) + + +def _extract_tableau_project_attrs(attrs: TableauProjectAttributes) -> dict: + """Extract all TableauProject attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["site_qualified_name"] = attrs.site_qualified_name + result["top_level_project_qualified_name"] = attrs.top_level_project_qualified_name + result["is_top_level_project"] = attrs.is_top_level_project + result["project_hierarchy"] = attrs.project_hierarchy + result["tableau_project_hierarchy_qualified_names"] = ( + attrs.tableau_project_hierarchy_qualified_names + ) + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _tableau_project_to_nested(tableau_project: TableauProject) -> TableauProjectNested: + """Convert flat TableauProject to nested format.""" + attrs = TableauProjectAttributes() + _populate_tableau_project_attrs(attrs, tableau_project) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + tableau_project, + _TABLEAU_PROJECT_REL_FIELDS, + TableauProjectRelationshipAttributes, + ) + return TableauProjectNested( + guid=tableau_project.guid, + type_name=tableau_project.type_name, + status=tableau_project.status, + version=tableau_project.version, + create_time=tableau_project.create_time, + update_time=tableau_project.update_time, + created_by=tableau_project.created_by, + updated_by=tableau_project.updated_by, + classifications=tableau_project.classifications, + classification_names=tableau_project.classification_names, + meanings=tableau_project.meanings, + labels=tableau_project.labels, + business_attributes=tableau_project.business_attributes, + custom_attributes=tableau_project.custom_attributes, + pending_tasks=tableau_project.pending_tasks, + proxy=tableau_project.proxy, + is_incomplete=tableau_project.is_incomplete, + provenance_type=tableau_project.provenance_type, + home_id=tableau_project.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _tableau_project_from_nested(nested: TableauProjectNested) -> TableauProject: + """Convert nested format to flat TableauProject.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else TableauProjectAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _TABLEAU_PROJECT_REL_FIELDS, + TableauProjectRelationshipAttributes, + ) + return TableauProject( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_tableau_project_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _tableau_project_to_nested_bytes( + tableau_project: TableauProject, serde: Serde +) -> bytes: + """Convert flat TableauProject to nested JSON bytes.""" + return serde.encode(_tableau_project_to_nested(tableau_project)) + + +def _tableau_project_from_nested_bytes(data: bytes, serde: Serde) -> TableauProject: + """Convert nested JSON bytes to flat TableauProject.""" + nested = serde.decode(data, TableauProjectNested) + return _tableau_project_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + RelationField, +) + +TableauProject.SITE_QUALIFIED_NAME = KeywordField( + "siteQualifiedName", "siteQualifiedName" +) +TableauProject.TOP_LEVEL_PROJECT_QUALIFIED_NAME = KeywordField( + "topLevelProjectQualifiedName", "topLevelProjectQualifiedName" +) +TableauProject.IS_TOP_LEVEL_PROJECT = BooleanField( + "isTopLevelProject", "isTopLevelProject" +) +TableauProject.PROJECT_HIERARCHY = KeywordField("projectHierarchy", "projectHierarchy") +TableauProject.TABLEAU_PROJECT_HIERARCHY_QUALIFIED_NAMES = KeywordField( + "tableauProjectHierarchyQualifiedNames", "tableauProjectHierarchyQualifiedNames" +) +TableauProject.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +TableauProject.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +TableauProject.ANOMALO_CHECKS = RelationField("anomaloChecks") +TableauProject.APPLICATION = RelationField("application") +TableauProject.APPLICATION_FIELD = RelationField("applicationField") +TableauProject.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +TableauProject.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +TableauProject.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +TableauProject.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +TableauProject.METRICS = RelationField("metrics") +TableauProject.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +TableauProject.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +TableauProject.MEANINGS = RelationField("meanings") +TableauProject.MC_MONITORS = RelationField("mcMonitors") +TableauProject.MC_INCIDENTS = RelationField("mcIncidents") +TableauProject.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +TableauProject.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +TableauProject.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +TableauProject.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +TableauProject.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +TableauProject.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +TableauProject.FILES = RelationField("files") +TableauProject.LINKS = RelationField("links") +TableauProject.README = RelationField("readme") +TableauProject.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +TableauProject.SODA_CHECKS = RelationField("sodaChecks") +TableauProject.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +TableauProject.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") +TableauProject.DATASOURCES = RelationField("datasources") +TableauProject.SITE = RelationField("site") +TableauProject.CHILD_PROJECTS = RelationField("childProjects") +TableauProject.PARENT_PROJECT = RelationField("parentProject") +TableauProject.FLOWS = RelationField("flows") +TableauProject.WORKBOOKS = RelationField("workbooks") diff --git a/pyatlan_v9/model/assets/tableau_related.py b/pyatlan_v9/model/assets/tableau_related.py new file mode 100644 index 000000000..a36289f33 --- /dev/null +++ b/pyatlan_v9/model/assets/tableau_related.py @@ -0,0 +1,546 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Tableau module. + +This module contains all Related{Type} classes for the Tableau type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedBI +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedTableau", + "RelatedTableauCalculatedField", + "RelatedTableauDashboard", + "RelatedTableauDashboardField", + "RelatedTableauDatasource", + "RelatedTableauDatasourceField", + "RelatedTableauFlow", + "RelatedTableauMetric", + "RelatedTableauProject", + "RelatedTableauSite", + "RelatedTableauWorkbook", + "RelatedTableauWorksheet", + "RelatedTableauWorksheetField", +] + + +class RelatedTableau(RelatedBI): + """ + Related entity reference for Tableau assets. + + Extends RelatedBI with Tableau-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Tableau" so it serializes correctly + + tableau_project_hierarchy_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Array of qualified names representing the project hierarchy for this Tableau asset.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Tableau" + + +class RelatedTableauCalculatedField(RelatedTableau): + """ + Related entity reference for TableauCalculatedField assets. + + Extends RelatedTableau with TableauCalculatedField-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "TableauCalculatedField" so it serializes correctly + + site_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the site in which this calculated field exists.""" + + project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this calculated field exists.""" + + top_level_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the top-level project in which this calculated field exists.""" + + workbook_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workbook in which this calculated field exists.""" + + datasource_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the datasource in which this calculated field exists.""" + + project_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of top-level projects and their nested projects.""" + + data_category: Union[str, None, UnsetType] = UNSET + """Data category of this field.""" + + role: Union[str, None, UnsetType] = UNSET + """Role of this field, for example: 'dimension', 'measure', or 'unknown'.""" + + tableau_data_type: Union[str, None, UnsetType] = UNSET + """Data type of the field, from Tableau.""" + + formula: Union[str, None, UnsetType] = UNSET + """Formula for this calculated field.""" + + upstream_fields: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of fields that are upstream to this calculated field.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "TableauCalculatedField" + + +class RelatedTableauDashboard(RelatedTableau): + """ + Related entity reference for TableauDashboard assets. + + Extends RelatedTableau with TableauDashboard-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "TableauDashboard" so it serializes correctly + + site_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the site in which this dashboard exists.""" + + project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this dashboard exists.""" + + workbook_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workbook in which this dashboard exists.""" + + top_level_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the top-level project in which this dashboard exists.""" + + project_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of top-level projects and their nested child projects.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "TableauDashboard" + + +class RelatedTableauDashboardField(RelatedTableau): + """ + Related entity reference for TableauDashboardField assets. + + Extends RelatedTableau with TableauDashboardField-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "TableauDashboardField" so it serializes correctly + + tableau_site_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the site in which this dashboard field exists.""" + + tableau_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this dashboard field exists.""" + + tableau_top_level_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the top-level project in which this dashboard field exists.""" + + tableau_dashboard_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the datasource in which this dashboard field exists.""" + + tableau_project_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of top-level projects and their nested child projects.""" + + tableau_fully_qualified_name: Union[str, None, UnsetType] = UNSET + """Name used internally in Tableau to uniquely identify this field.""" + + tableau_dashboard_field_data_category: Union[str, None, UnsetType] = UNSET + """Data category of this field.""" + + tableau_dashboard_field_role: Union[str, None, UnsetType] = UNSET + """Role of this field, for example: 'dimension', 'measure', or 'unknown'.""" + + tableau_dashboard_field_data_type: Union[str, None, UnsetType] = UNSET + """Data type of this field.""" + + tableau_upstream_tables: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Tables upstream to this worksheet field.""" + + tableau_dashboard_field_formula: Union[str, None, UnsetType] = UNSET + """Formula for this field.""" + + tableau_dashboard_field_bin_size: Union[str, None, UnsetType] = UNSET + """Bin size of this field.""" + + tableau_dashboard_field_upstream_columns: Union[ + List[Dict[str, str]], None, UnsetType + ] = UNSET + """Columns upstream to this field.""" + + tableau_dashboard_field_upstream_fields: Union[ + List[Dict[str, str]], None, UnsetType + ] = UNSET + """Fields upstream to this field.""" + + tableau_dashboard_field_type: Union[str, None, UnsetType] = UNSET + """Type of this dashboard field.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "TableauDashboardField" + + +class RelatedTableauDatasource(RelatedTableau): + """ + Related entity reference for TableauDatasource assets. + + Extends RelatedTableau with TableauDatasource-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "TableauDatasource" so it serializes correctly + + site_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the site in which this datasource exists.""" + + project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this datasource exists.""" + + top_level_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the top-level project in which this datasource exists.""" + + workbook_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workbook in which this datasource exists.""" + + project_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of top-level projects with their nested child projects.""" + + is_published: Union[bool, None, UnsetType] = UNSET + """Whether this datasource is published (true) or embedded (false).""" + + has_extracts: Union[bool, None, UnsetType] = UNSET + """Whether this datasource has extracts (true) or not (false).""" + + is_certified: Union[bool, None, UnsetType] = UNSET + """Whether this datasource is certified in Tableau (true) or not (false).""" + + certifier: Union[Dict[str, str], None, UnsetType] = UNSET + """Users that have marked this datasource as cerified, in Tableau.""" + + certification_note: Union[str, None, UnsetType] = UNSET + """Notes related to this datasource being cerfified, in Tableau.""" + + certifier_display_name: Union[str, None, UnsetType] = UNSET + """Name of the user who cerified this datasource, in Tableau.""" + + upstream_tables: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of tables that are upstream of this datasource.""" + + upstream_datasources: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of datasources that are upstream of this datasource.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "TableauDatasource" + + +class RelatedTableauDatasourceField(RelatedTableau): + """ + Related entity reference for TableauDatasourceField assets. + + Extends RelatedTableau with TableauDatasourceField-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "TableauDatasourceField" so it serializes correctly + + site_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the site in which this datasource field exists.""" + + project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this datasource field exists.""" + + top_level_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the top-level project in which this datasource field exists.""" + + workbook_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workbook in which this datasource field exists.""" + + datasource_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the datasource in which this datasource field exists.""" + + project_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of top-level projects and their nested child projects.""" + + fully_qualified_name: Union[str, None, UnsetType] = UNSET + """Name used internally in Tableau to uniquely identify this field.""" + + tableau_datasource_field_data_category: Union[str, None, UnsetType] = UNSET + """Data category of this field.""" + + tableau_datasource_field_role: Union[str, None, UnsetType] = UNSET + """Role of this field, for example: 'dimension', 'measure', or 'unknown'.""" + + tableau_datasource_field_data_type: Union[str, None, UnsetType] = UNSET + """Data type of this field.""" + + upstream_tables: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Tables upstream to this datasource field.""" + + tableau_datasource_field_formula: Union[str, None, UnsetType] = UNSET + """Formula for this field.""" + + tableau_datasource_field_bin_size: Union[str, None, UnsetType] = UNSET + """Bin size of this field.""" + + upstream_columns: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Columns upstream to this field.""" + + upstream_fields: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """Fields upstream to this field.""" + + datasource_field_type: Union[str, None, UnsetType] = UNSET + """Type of this datasource field.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "TableauDatasourceField" + + +class RelatedTableauFlow(RelatedTableau): + """ + Related entity reference for TableauFlow assets. + + Extends RelatedTableau with TableauFlow-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "TableauFlow" so it serializes correctly + + site_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the site in which this flow exists.""" + + project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this flow exists.""" + + top_level_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the top-level project in which this flow exists.""" + + project_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of top-level projects with their nested child projects.""" + + input_fields: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of fields that are inputs to this flow.""" + + output_fields: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of fields that are outputs from this flow.""" + + output_steps: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of steps that are outputs from this flow.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "TableauFlow" + + +class RelatedTableauMetric(RelatedTableau): + """ + Related entity reference for TableauMetric assets. + + Extends RelatedTableau with TableauMetric-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "TableauMetric" so it serializes correctly + + site_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the site in which this metric exists.""" + + project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this metric exists.""" + + top_level_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the top-level project in which this metric exists.""" + + project_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of top-level projects with their nested child projects.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "TableauMetric" + + +class RelatedTableauProject(RelatedTableau): + """ + Related entity reference for TableauProject assets. + + Extends RelatedTableau with TableauProject-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "TableauProject" so it serializes correctly + + site_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the site in which this project exists.""" + + top_level_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the top-level project in which this project exists, if this is a nested project.""" + + is_top_level_project: Union[bool, None, UnsetType] = UNSET + """Whether this project is a top-level project (true) or not (false).""" + + project_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of top-level projects with their nested child projects.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "TableauProject" + + +class RelatedTableauSite(RelatedTableau): + """ + Related entity reference for TableauSite assets. + + Extends RelatedTableau with TableauSite-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "TableauSite" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "TableauSite" + + +class RelatedTableauWorkbook(RelatedTableau): + """ + Related entity reference for TableauWorkbook assets. + + Extends RelatedTableau with TableauWorkbook-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "TableauWorkbook" so it serializes correctly + + site_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the site in which this workbook exists.""" + + project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this workbook exists.""" + + top_level_project_name: Union[str, None, UnsetType] = UNSET + """Simple name of the top-level project in which this workbook exists.""" + + top_level_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the top-level project in which this workbook exists.""" + + project_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of top-level projects with their nested child projects.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "TableauWorkbook" + + +class RelatedTableauWorksheet(RelatedTableau): + """ + Related entity reference for TableauWorksheet assets. + + Extends RelatedTableau with TableauWorksheet-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "TableauWorksheet" so it serializes correctly + + site_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the site in which this worksheet exists.""" + + project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this worksheet exists.""" + + top_level_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the top-level project in which this worksheet exists.""" + + project_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of top-level projects with their nested child projects.""" + + workbook_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workbook in which this worksheet exists.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "TableauWorksheet" + + +class RelatedTableauWorksheetField(RelatedTableau): + """ + Related entity reference for TableauWorksheetField assets. + + Extends RelatedTableau with TableauWorksheetField-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "TableauWorksheetField" so it serializes correctly + + tableau_site_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the site in which this worksheet field exists.""" + + tableau_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this worksheet field exists.""" + + tableau_top_level_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the top-level project in which this worksheet field exists.""" + + tableau_workbook_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workbook in which this worksheet field exists.""" + + tableau_worksheet_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the datasource in which this worksheet field exists.""" + + tableau_project_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of top-level projects and their nested child projects.""" + + tableau_fully_qualified_name: Union[str, None, UnsetType] = UNSET + """Name used internally in Tableau to uniquely identify this field.""" + + tableau_worksheet_field_data_category: Union[str, None, UnsetType] = UNSET + """Data category of this field.""" + + tableau_worksheet_field_role: Union[str, None, UnsetType] = UNSET + """Role of this field, for example: 'dimension', 'measure', or 'unknown'.""" + + tableau_worksheet_field_data_type: Union[str, None, UnsetType] = UNSET + """Data type of this field.""" + + tableau_worksheet_field_upstream_tables: Union[ + List[Dict[str, str]], None, UnsetType + ] = UNSET + """Tables upstream to this worksheet field.""" + + tableau_worksheet_field_formula: Union[str, None, UnsetType] = UNSET + """Formula for this field.""" + + tableau_worksheet_field_bin_size: Union[str, None, UnsetType] = UNSET + """Bin size of this field.""" + + tableau_worksheet_field_upstream_columns: Union[ + List[Dict[str, str]], None, UnsetType + ] = UNSET + """Columns upstream to this field.""" + + tableau_worksheet_field_upstream_fields: Union[ + List[Dict[str, str]], None, UnsetType + ] = UNSET + """Fields upstream to this field.""" + + tableau_worksheet_field_type: Union[str, None, UnsetType] = UNSET + """Type of this worksheet field.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "TableauWorksheetField" diff --git a/pyatlan_v9/model/assets/tableau_site.py b/pyatlan_v9/model/assets/tableau_site.py new file mode 100644 index 000000000..6b9754a3f --- /dev/null +++ b/pyatlan_v9/model/assets/tableau_site.py @@ -0,0 +1,556 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +TableauSite asset model with flattened inheritance. + +This module provides: +- TableauSite: Flat asset class (easy to use) +- TableauSiteAttributes: Nested attributes struct (extends AssetAttributes) +- TableauSiteNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .tableau_related import RelatedTableauProject + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class TableauSite(Asset): + """ + Instance of a Tableau site in Atlan. + """ + + TABLEAU_PROJECT_HIERARCHY_QUALIFIED_NAMES: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + PROJECTS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "TableauSite" + + tableau_project_hierarchy_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Array of qualified names representing the project hierarchy for this Tableau asset.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + projects: Union[List[RelatedTableauProject], None, UnsetType] = UNSET + """Projects that exist within this site.""" + + def __post_init__(self) -> None: + self.type_name = "TableauSite" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _tableau_site_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> TableauSite: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + TableauSite instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _tableau_site_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class TableauSiteAttributes(AssetAttributes): + """TableauSite-specific attributes for nested API format.""" + + tableau_project_hierarchy_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Array of qualified names representing the project hierarchy for this Tableau asset.""" + + +class TableauSiteRelationshipAttributes(AssetRelationshipAttributes): + """TableauSite-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + projects: Union[List[RelatedTableauProject], None, UnsetType] = UNSET + """Projects that exist within this site.""" + + +class TableauSiteNested(AssetNested): + """TableauSite in nested API format for high-performance serialization.""" + + attributes: Union[TableauSiteAttributes, UnsetType] = UNSET + relationship_attributes: Union[TableauSiteRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + TableauSiteRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + TableauSiteRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_TABLEAU_SITE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", + "projects", +] + + +def _populate_tableau_site_attrs( + attrs: TableauSiteAttributes, obj: TableauSite +) -> None: + """Populate TableauSite-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.tableau_project_hierarchy_qualified_names = ( + obj.tableau_project_hierarchy_qualified_names + ) + + +def _extract_tableau_site_attrs(attrs: TableauSiteAttributes) -> dict: + """Extract all TableauSite attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["tableau_project_hierarchy_qualified_names"] = ( + attrs.tableau_project_hierarchy_qualified_names + ) + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _tableau_site_to_nested(tableau_site: TableauSite) -> TableauSiteNested: + """Convert flat TableauSite to nested format.""" + attrs = TableauSiteAttributes() + _populate_tableau_site_attrs(attrs, tableau_site) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + tableau_site, _TABLEAU_SITE_REL_FIELDS, TableauSiteRelationshipAttributes + ) + return TableauSiteNested( + guid=tableau_site.guid, + type_name=tableau_site.type_name, + status=tableau_site.status, + version=tableau_site.version, + create_time=tableau_site.create_time, + update_time=tableau_site.update_time, + created_by=tableau_site.created_by, + updated_by=tableau_site.updated_by, + classifications=tableau_site.classifications, + classification_names=tableau_site.classification_names, + meanings=tableau_site.meanings, + labels=tableau_site.labels, + business_attributes=tableau_site.business_attributes, + custom_attributes=tableau_site.custom_attributes, + pending_tasks=tableau_site.pending_tasks, + proxy=tableau_site.proxy, + is_incomplete=tableau_site.is_incomplete, + provenance_type=tableau_site.provenance_type, + home_id=tableau_site.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _tableau_site_from_nested(nested: TableauSiteNested) -> TableauSite: + """Convert nested format to flat TableauSite.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else TableauSiteAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _TABLEAU_SITE_REL_FIELDS, + TableauSiteRelationshipAttributes, + ) + return TableauSite( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_tableau_site_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _tableau_site_to_nested_bytes(tableau_site: TableauSite, serde: Serde) -> bytes: + """Convert flat TableauSite to nested JSON bytes.""" + return serde.encode(_tableau_site_to_nested(tableau_site)) + + +def _tableau_site_from_nested_bytes(data: bytes, serde: Serde) -> TableauSite: + """Convert nested JSON bytes to flat TableauSite.""" + nested = serde.decode(data, TableauSiteNested) + return _tableau_site_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +TableauSite.TABLEAU_PROJECT_HIERARCHY_QUALIFIED_NAMES = KeywordField( + "tableauProjectHierarchyQualifiedNames", "tableauProjectHierarchyQualifiedNames" +) +TableauSite.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +TableauSite.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +TableauSite.ANOMALO_CHECKS = RelationField("anomaloChecks") +TableauSite.APPLICATION = RelationField("application") +TableauSite.APPLICATION_FIELD = RelationField("applicationField") +TableauSite.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +TableauSite.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +TableauSite.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +TableauSite.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +TableauSite.METRICS = RelationField("metrics") +TableauSite.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +TableauSite.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +TableauSite.MEANINGS = RelationField("meanings") +TableauSite.MC_MONITORS = RelationField("mcMonitors") +TableauSite.MC_INCIDENTS = RelationField("mcIncidents") +TableauSite.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +TableauSite.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +TableauSite.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +TableauSite.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +TableauSite.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +TableauSite.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +TableauSite.FILES = RelationField("files") +TableauSite.LINKS = RelationField("links") +TableauSite.README = RelationField("readme") +TableauSite.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +TableauSite.SODA_CHECKS = RelationField("sodaChecks") +TableauSite.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +TableauSite.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") +TableauSite.PROJECTS = RelationField("projects") diff --git a/pyatlan_v9/model/assets/tableau_workbook.py b/pyatlan_v9/model/assets/tableau_workbook.py new file mode 100644 index 000000000..8a57a730e --- /dev/null +++ b/pyatlan_v9/model/assets/tableau_workbook.py @@ -0,0 +1,669 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +TableauWorkbook asset model with flattened inheritance. + +This module provides: +- TableauWorkbook: Flat asset class (easy to use) +- TableauWorkbookAttributes: Nested attributes struct (extends AssetAttributes) +- TableauWorkbookNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .tableau_related import ( + RelatedTableauDashboard, + RelatedTableauDatasource, + RelatedTableauProject, + RelatedTableauWorksheet, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class TableauWorkbook(Asset): + """ + Instance of a Tableau workbook in Atlan. These contain one or more worksheets, datasources or dashboards. + """ + + SITE_QUALIFIED_NAME: ClassVar[Any] = None + PROJECT_QUALIFIED_NAME: ClassVar[Any] = None + TOP_LEVEL_PROJECT_NAME: ClassVar[Any] = None + TOP_LEVEL_PROJECT_QUALIFIED_NAME: ClassVar[Any] = None + PROJECT_HIERARCHY: ClassVar[Any] = None + TABLEAU_PROJECT_HIERARCHY_QUALIFIED_NAMES: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + DASHBOARDS: ClassVar[Any] = None + DATASOURCES: ClassVar[Any] = None + WORKSHEETS: ClassVar[Any] = None + PROJECT: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "TableauWorkbook" + + site_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the site in which this workbook exists.""" + + project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this workbook exists.""" + + top_level_project_name: Union[str, None, UnsetType] = UNSET + """Simple name of the top-level project in which this workbook exists.""" + + top_level_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the top-level project in which this workbook exists.""" + + project_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of top-level projects with their nested child projects.""" + + tableau_project_hierarchy_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Array of qualified names representing the project hierarchy for this Tableau asset.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + dashboards: Union[List[RelatedTableauDashboard], None, UnsetType] = UNSET + """Dashboards that exist within this workbook.""" + + datasources: Union[List[RelatedTableauDatasource], None, UnsetType] = UNSET + """Datasources that exist within this workbook.""" + + worksheets: Union[List[RelatedTableauWorksheet], None, UnsetType] = UNSET + """Worksheets that exist within this workbook.""" + + project: Union[RelatedTableauProject, None, UnsetType] = UNSET + """Project in which this workbook exists.""" + + def __post_init__(self) -> None: + self.type_name = "TableauWorkbook" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _tableau_workbook_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> TableauWorkbook: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + TableauWorkbook instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _tableau_workbook_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class TableauWorkbookAttributes(AssetAttributes): + """TableauWorkbook-specific attributes for nested API format.""" + + site_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the site in which this workbook exists.""" + + project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this workbook exists.""" + + top_level_project_name: Union[str, None, UnsetType] = UNSET + """Simple name of the top-level project in which this workbook exists.""" + + top_level_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the top-level project in which this workbook exists.""" + + project_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of top-level projects with their nested child projects.""" + + tableau_project_hierarchy_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Array of qualified names representing the project hierarchy for this Tableau asset.""" + + +class TableauWorkbookRelationshipAttributes(AssetRelationshipAttributes): + """TableauWorkbook-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + dashboards: Union[List[RelatedTableauDashboard], None, UnsetType] = UNSET + """Dashboards that exist within this workbook.""" + + datasources: Union[List[RelatedTableauDatasource], None, UnsetType] = UNSET + """Datasources that exist within this workbook.""" + + worksheets: Union[List[RelatedTableauWorksheet], None, UnsetType] = UNSET + """Worksheets that exist within this workbook.""" + + project: Union[RelatedTableauProject, None, UnsetType] = UNSET + """Project in which this workbook exists.""" + + +class TableauWorkbookNested(AssetNested): + """TableauWorkbook in nested API format for high-performance serialization.""" + + attributes: Union[TableauWorkbookAttributes, UnsetType] = UNSET + relationship_attributes: Union[TableauWorkbookRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + TableauWorkbookRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + TableauWorkbookRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_TABLEAU_WORKBOOK_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", + "dashboards", + "datasources", + "worksheets", + "project", +] + + +def _populate_tableau_workbook_attrs( + attrs: TableauWorkbookAttributes, obj: TableauWorkbook +) -> None: + """Populate TableauWorkbook-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.site_qualified_name = obj.site_qualified_name + attrs.project_qualified_name = obj.project_qualified_name + attrs.top_level_project_name = obj.top_level_project_name + attrs.top_level_project_qualified_name = obj.top_level_project_qualified_name + attrs.project_hierarchy = obj.project_hierarchy + attrs.tableau_project_hierarchy_qualified_names = ( + obj.tableau_project_hierarchy_qualified_names + ) + + +def _extract_tableau_workbook_attrs(attrs: TableauWorkbookAttributes) -> dict: + """Extract all TableauWorkbook attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["site_qualified_name"] = attrs.site_qualified_name + result["project_qualified_name"] = attrs.project_qualified_name + result["top_level_project_name"] = attrs.top_level_project_name + result["top_level_project_qualified_name"] = attrs.top_level_project_qualified_name + result["project_hierarchy"] = attrs.project_hierarchy + result["tableau_project_hierarchy_qualified_names"] = ( + attrs.tableau_project_hierarchy_qualified_names + ) + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _tableau_workbook_to_nested( + tableau_workbook: TableauWorkbook, +) -> TableauWorkbookNested: + """Convert flat TableauWorkbook to nested format.""" + attrs = TableauWorkbookAttributes() + _populate_tableau_workbook_attrs(attrs, tableau_workbook) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + tableau_workbook, + _TABLEAU_WORKBOOK_REL_FIELDS, + TableauWorkbookRelationshipAttributes, + ) + return TableauWorkbookNested( + guid=tableau_workbook.guid, + type_name=tableau_workbook.type_name, + status=tableau_workbook.status, + version=tableau_workbook.version, + create_time=tableau_workbook.create_time, + update_time=tableau_workbook.update_time, + created_by=tableau_workbook.created_by, + updated_by=tableau_workbook.updated_by, + classifications=tableau_workbook.classifications, + classification_names=tableau_workbook.classification_names, + meanings=tableau_workbook.meanings, + labels=tableau_workbook.labels, + business_attributes=tableau_workbook.business_attributes, + custom_attributes=tableau_workbook.custom_attributes, + pending_tasks=tableau_workbook.pending_tasks, + proxy=tableau_workbook.proxy, + is_incomplete=tableau_workbook.is_incomplete, + provenance_type=tableau_workbook.provenance_type, + home_id=tableau_workbook.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _tableau_workbook_from_nested(nested: TableauWorkbookNested) -> TableauWorkbook: + """Convert nested format to flat TableauWorkbook.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else TableauWorkbookAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _TABLEAU_WORKBOOK_REL_FIELDS, + TableauWorkbookRelationshipAttributes, + ) + return TableauWorkbook( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_tableau_workbook_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _tableau_workbook_to_nested_bytes( + tableau_workbook: TableauWorkbook, serde: Serde +) -> bytes: + """Convert flat TableauWorkbook to nested JSON bytes.""" + return serde.encode(_tableau_workbook_to_nested(tableau_workbook)) + + +def _tableau_workbook_from_nested_bytes(data: bytes, serde: Serde) -> TableauWorkbook: + """Convert nested JSON bytes to flat TableauWorkbook.""" + nested = serde.decode(data, TableauWorkbookNested) + return _tableau_workbook_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +TableauWorkbook.SITE_QUALIFIED_NAME = KeywordField( + "siteQualifiedName", "siteQualifiedName" +) +TableauWorkbook.PROJECT_QUALIFIED_NAME = KeywordField( + "projectQualifiedName", "projectQualifiedName" +) +TableauWorkbook.TOP_LEVEL_PROJECT_NAME = KeywordField( + "topLevelProjectName", "topLevelProjectName" +) +TableauWorkbook.TOP_LEVEL_PROJECT_QUALIFIED_NAME = KeywordField( + "topLevelProjectQualifiedName", "topLevelProjectQualifiedName" +) +TableauWorkbook.PROJECT_HIERARCHY = KeywordField("projectHierarchy", "projectHierarchy") +TableauWorkbook.TABLEAU_PROJECT_HIERARCHY_QUALIFIED_NAMES = KeywordField( + "tableauProjectHierarchyQualifiedNames", "tableauProjectHierarchyQualifiedNames" +) +TableauWorkbook.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +TableauWorkbook.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +TableauWorkbook.ANOMALO_CHECKS = RelationField("anomaloChecks") +TableauWorkbook.APPLICATION = RelationField("application") +TableauWorkbook.APPLICATION_FIELD = RelationField("applicationField") +TableauWorkbook.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +TableauWorkbook.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +TableauWorkbook.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +TableauWorkbook.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +TableauWorkbook.METRICS = RelationField("metrics") +TableauWorkbook.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +TableauWorkbook.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +TableauWorkbook.MEANINGS = RelationField("meanings") +TableauWorkbook.MC_MONITORS = RelationField("mcMonitors") +TableauWorkbook.MC_INCIDENTS = RelationField("mcIncidents") +TableauWorkbook.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +TableauWorkbook.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +TableauWorkbook.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +TableauWorkbook.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +TableauWorkbook.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +TableauWorkbook.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +TableauWorkbook.FILES = RelationField("files") +TableauWorkbook.LINKS = RelationField("links") +TableauWorkbook.README = RelationField("readme") +TableauWorkbook.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +TableauWorkbook.SODA_CHECKS = RelationField("sodaChecks") +TableauWorkbook.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +TableauWorkbook.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") +TableauWorkbook.DASHBOARDS = RelationField("dashboards") +TableauWorkbook.DATASOURCES = RelationField("datasources") +TableauWorkbook.WORKSHEETS = RelationField("worksheets") +TableauWorkbook.PROJECT = RelationField("project") diff --git a/pyatlan_v9/model/assets/tableau_worksheet.py b/pyatlan_v9/model/assets/tableau_worksheet.py new file mode 100644 index 000000000..e020ec22a --- /dev/null +++ b/pyatlan_v9/model/assets/tableau_worksheet.py @@ -0,0 +1,693 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +TableauWorksheet asset model with flattened inheritance. + +This module provides: +- TableauWorksheet: Flat asset class (easy to use) +- TableauWorksheetAttributes: Nested attributes struct (extends AssetAttributes) +- TableauWorksheetNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .tableau_related import ( + RelatedTableauCalculatedField, + RelatedTableauDashboard, + RelatedTableauDatasourceField, + RelatedTableauWorkbook, + RelatedTableauWorksheetField, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class TableauWorksheet(Asset): + """ + Instance of a Tableau worksheet in Atlan. + """ + + SITE_QUALIFIED_NAME: ClassVar[Any] = None + PROJECT_QUALIFIED_NAME: ClassVar[Any] = None + TOP_LEVEL_PROJECT_QUALIFIED_NAME: ClassVar[Any] = None + PROJECT_HIERARCHY: ClassVar[Any] = None + WORKBOOK_QUALIFIED_NAME: ClassVar[Any] = None + TABLEAU_PROJECT_HIERARCHY_QUALIFIED_NAMES: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + DASHBOARDS: ClassVar[Any] = None + DATASOURCE_FIELDS: ClassVar[Any] = None + TABLEAU_WORKSHEET_FIELDS: ClassVar[Any] = None + CALCULATED_FIELDS: ClassVar[Any] = None + WORKBOOK: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "TableauWorksheet" + + site_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the site in which this worksheet exists.""" + + project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this worksheet exists.""" + + top_level_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the top-level project in which this worksheet exists.""" + + project_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of top-level projects with their nested child projects.""" + + workbook_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workbook in which this worksheet exists.""" + + tableau_project_hierarchy_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Array of qualified names representing the project hierarchy for this Tableau asset.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + dashboards: Union[List[RelatedTableauDashboard], None, UnsetType] = UNSET + """Dashboards that use this worksheet.""" + + datasource_fields: Union[List[RelatedTableauDatasourceField], None, UnsetType] = ( + UNSET + ) + """Datasource fields this worksheet uses.""" + + tableau_worksheet_fields: Union[ + List[RelatedTableauWorksheetField], None, UnsetType + ] = UNSET + """Fields that exist within this worksheet.""" + + calculated_fields: Union[List[RelatedTableauCalculatedField], None, UnsetType] = ( + UNSET + ) + """Calculated fields that are used in this worksheet.""" + + workbook: Union[RelatedTableauWorkbook, None, UnsetType] = UNSET + """Workbook in which this worksheet exists.""" + + def __post_init__(self) -> None: + self.type_name = "TableauWorksheet" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _tableau_worksheet_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> TableauWorksheet: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + TableauWorksheet instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _tableau_worksheet_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class TableauWorksheetAttributes(AssetAttributes): + """TableauWorksheet-specific attributes for nested API format.""" + + site_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the site in which this worksheet exists.""" + + project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this worksheet exists.""" + + top_level_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the top-level project in which this worksheet exists.""" + + project_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of top-level projects with their nested child projects.""" + + workbook_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workbook in which this worksheet exists.""" + + tableau_project_hierarchy_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Array of qualified names representing the project hierarchy for this Tableau asset.""" + + +class TableauWorksheetRelationshipAttributes(AssetRelationshipAttributes): + """TableauWorksheet-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + dashboards: Union[List[RelatedTableauDashboard], None, UnsetType] = UNSET + """Dashboards that use this worksheet.""" + + datasource_fields: Union[List[RelatedTableauDatasourceField], None, UnsetType] = ( + UNSET + ) + """Datasource fields this worksheet uses.""" + + tableau_worksheet_fields: Union[ + List[RelatedTableauWorksheetField], None, UnsetType + ] = UNSET + """Fields that exist within this worksheet.""" + + calculated_fields: Union[List[RelatedTableauCalculatedField], None, UnsetType] = ( + UNSET + ) + """Calculated fields that are used in this worksheet.""" + + workbook: Union[RelatedTableauWorkbook, None, UnsetType] = UNSET + """Workbook in which this worksheet exists.""" + + +class TableauWorksheetNested(AssetNested): + """TableauWorksheet in nested API format for high-performance serialization.""" + + attributes: Union[TableauWorksheetAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + TableauWorksheetRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + TableauWorksheetRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + TableauWorksheetRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_TABLEAU_WORKSHEET_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", + "dashboards", + "datasource_fields", + "tableau_worksheet_fields", + "calculated_fields", + "workbook", +] + + +def _populate_tableau_worksheet_attrs( + attrs: TableauWorksheetAttributes, obj: TableauWorksheet +) -> None: + """Populate TableauWorksheet-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.site_qualified_name = obj.site_qualified_name + attrs.project_qualified_name = obj.project_qualified_name + attrs.top_level_project_qualified_name = obj.top_level_project_qualified_name + attrs.project_hierarchy = obj.project_hierarchy + attrs.workbook_qualified_name = obj.workbook_qualified_name + attrs.tableau_project_hierarchy_qualified_names = ( + obj.tableau_project_hierarchy_qualified_names + ) + + +def _extract_tableau_worksheet_attrs(attrs: TableauWorksheetAttributes) -> dict: + """Extract all TableauWorksheet attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["site_qualified_name"] = attrs.site_qualified_name + result["project_qualified_name"] = attrs.project_qualified_name + result["top_level_project_qualified_name"] = attrs.top_level_project_qualified_name + result["project_hierarchy"] = attrs.project_hierarchy + result["workbook_qualified_name"] = attrs.workbook_qualified_name + result["tableau_project_hierarchy_qualified_names"] = ( + attrs.tableau_project_hierarchy_qualified_names + ) + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _tableau_worksheet_to_nested( + tableau_worksheet: TableauWorksheet, +) -> TableauWorksheetNested: + """Convert flat TableauWorksheet to nested format.""" + attrs = TableauWorksheetAttributes() + _populate_tableau_worksheet_attrs(attrs, tableau_worksheet) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + tableau_worksheet, + _TABLEAU_WORKSHEET_REL_FIELDS, + TableauWorksheetRelationshipAttributes, + ) + return TableauWorksheetNested( + guid=tableau_worksheet.guid, + type_name=tableau_worksheet.type_name, + status=tableau_worksheet.status, + version=tableau_worksheet.version, + create_time=tableau_worksheet.create_time, + update_time=tableau_worksheet.update_time, + created_by=tableau_worksheet.created_by, + updated_by=tableau_worksheet.updated_by, + classifications=tableau_worksheet.classifications, + classification_names=tableau_worksheet.classification_names, + meanings=tableau_worksheet.meanings, + labels=tableau_worksheet.labels, + business_attributes=tableau_worksheet.business_attributes, + custom_attributes=tableau_worksheet.custom_attributes, + pending_tasks=tableau_worksheet.pending_tasks, + proxy=tableau_worksheet.proxy, + is_incomplete=tableau_worksheet.is_incomplete, + provenance_type=tableau_worksheet.provenance_type, + home_id=tableau_worksheet.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _tableau_worksheet_from_nested(nested: TableauWorksheetNested) -> TableauWorksheet: + """Convert nested format to flat TableauWorksheet.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else TableauWorksheetAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _TABLEAU_WORKSHEET_REL_FIELDS, + TableauWorksheetRelationshipAttributes, + ) + return TableauWorksheet( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_tableau_worksheet_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _tableau_worksheet_to_nested_bytes( + tableau_worksheet: TableauWorksheet, serde: Serde +) -> bytes: + """Convert flat TableauWorksheet to nested JSON bytes.""" + return serde.encode(_tableau_worksheet_to_nested(tableau_worksheet)) + + +def _tableau_worksheet_from_nested_bytes(data: bytes, serde: Serde) -> TableauWorksheet: + """Convert nested JSON bytes to flat TableauWorksheet.""" + nested = serde.decode(data, TableauWorksheetNested) + return _tableau_worksheet_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + RelationField, +) + +TableauWorksheet.SITE_QUALIFIED_NAME = KeywordField( + "siteQualifiedName", "siteQualifiedName" +) +TableauWorksheet.PROJECT_QUALIFIED_NAME = KeywordField( + "projectQualifiedName", "projectQualifiedName" +) +TableauWorksheet.TOP_LEVEL_PROJECT_QUALIFIED_NAME = KeywordField( + "topLevelProjectQualifiedName", "topLevelProjectQualifiedName" +) +TableauWorksheet.PROJECT_HIERARCHY = KeywordField( + "projectHierarchy", "projectHierarchy" +) +TableauWorksheet.WORKBOOK_QUALIFIED_NAME = KeywordField( + "workbookQualifiedName", "workbookQualifiedName" +) +TableauWorksheet.TABLEAU_PROJECT_HIERARCHY_QUALIFIED_NAMES = KeywordField( + "tableauProjectHierarchyQualifiedNames", "tableauProjectHierarchyQualifiedNames" +) +TableauWorksheet.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +TableauWorksheet.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +TableauWorksheet.ANOMALO_CHECKS = RelationField("anomaloChecks") +TableauWorksheet.APPLICATION = RelationField("application") +TableauWorksheet.APPLICATION_FIELD = RelationField("applicationField") +TableauWorksheet.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +TableauWorksheet.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +TableauWorksheet.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +TableauWorksheet.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +TableauWorksheet.METRICS = RelationField("metrics") +TableauWorksheet.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +TableauWorksheet.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +TableauWorksheet.MEANINGS = RelationField("meanings") +TableauWorksheet.MC_MONITORS = RelationField("mcMonitors") +TableauWorksheet.MC_INCIDENTS = RelationField("mcIncidents") +TableauWorksheet.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +TableauWorksheet.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +TableauWorksheet.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +TableauWorksheet.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +TableauWorksheet.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +TableauWorksheet.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +TableauWorksheet.FILES = RelationField("files") +TableauWorksheet.LINKS = RelationField("links") +TableauWorksheet.README = RelationField("readme") +TableauWorksheet.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +TableauWorksheet.SODA_CHECKS = RelationField("sodaChecks") +TableauWorksheet.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +TableauWorksheet.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") +TableauWorksheet.DASHBOARDS = RelationField("dashboards") +TableauWorksheet.DATASOURCE_FIELDS = RelationField("datasourceFields") +TableauWorksheet.TABLEAU_WORKSHEET_FIELDS = RelationField("tableauWorksheetFields") +TableauWorksheet.CALCULATED_FIELDS = RelationField("calculatedFields") +TableauWorksheet.WORKBOOK = RelationField("workbook") diff --git a/pyatlan_v9/model/assets/tableau_worksheet_field.py b/pyatlan_v9/model/assets/tableau_worksheet_field.py new file mode 100644 index 000000000..cfa5fe9f7 --- /dev/null +++ b/pyatlan_v9/model/assets/tableau_worksheet_field.py @@ -0,0 +1,868 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +TableauWorksheetField asset model with flattened inheritance. + +This module provides: +- TableauWorksheetField: Flat asset class (easy to use) +- TableauWorksheetFieldAttributes: Nested attributes struct (extends AssetAttributes) +- TableauWorksheetFieldNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .tableau_related import ( + RelatedTableauCalculatedField, + RelatedTableauDashboardField, + RelatedTableauDatasourceField, + RelatedTableauWorksheet, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class TableauWorksheetField(Asset): + """ + Instance of a Tableau worksheet field in Atlan. + """ + + TABLEAU_SITE_QUALIFIED_NAME: ClassVar[Any] = None + TABLEAU_PROJECT_QUALIFIED_NAME: ClassVar[Any] = None + TABLEAU_TOP_LEVEL_PROJECT_QUALIFIED_NAME: ClassVar[Any] = None + TABLEAU_WORKBOOK_QUALIFIED_NAME: ClassVar[Any] = None + TABLEAU_WORKSHEET_QUALIFIED_NAME: ClassVar[Any] = None + TABLEAU_PROJECT_HIERARCHY: ClassVar[Any] = None + TABLEAU_FULLY_QUALIFIED_NAME: ClassVar[Any] = None + TABLEAU_WORKSHEET_FIELD_DATA_CATEGORY: ClassVar[Any] = None + TABLEAU_WORKSHEET_FIELD_ROLE: ClassVar[Any] = None + TABLEAU_WORKSHEET_FIELD_DATA_TYPE: ClassVar[Any] = None + TABLEAU_WORKSHEET_FIELD_UPSTREAM_TABLES: ClassVar[Any] = None + TABLEAU_WORKSHEET_FIELD_FORMULA: ClassVar[Any] = None + TABLEAU_WORKSHEET_FIELD_BIN_SIZE: ClassVar[Any] = None + TABLEAU_WORKSHEET_FIELD_UPSTREAM_COLUMNS: ClassVar[Any] = None + TABLEAU_WORKSHEET_FIELD_UPSTREAM_FIELDS: ClassVar[Any] = None + TABLEAU_WORKSHEET_FIELD_TYPE: ClassVar[Any] = None + TABLEAU_PROJECT_HIERARCHY_QUALIFIED_NAMES: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + TABLEAU_DASHBOARD_FIELD: ClassVar[Any] = None + TABLEAU_DATASOURCE_FIELD: ClassVar[Any] = None + TABLEAU_CALCULATED_FIELD: ClassVar[Any] = None + TABLEAU_WORKSHEET: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "TableauWorksheetField" + + tableau_site_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the site in which this worksheet field exists.""" + + tableau_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this worksheet field exists.""" + + tableau_top_level_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the top-level project in which this worksheet field exists.""" + + tableau_workbook_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workbook in which this worksheet field exists.""" + + tableau_worksheet_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the datasource in which this worksheet field exists.""" + + tableau_project_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of top-level projects and their nested child projects.""" + + tableau_fully_qualified_name: Union[str, None, UnsetType] = UNSET + """Name used internally in Tableau to uniquely identify this field.""" + + tableau_worksheet_field_data_category: Union[str, None, UnsetType] = UNSET + """Data category of this field.""" + + tableau_worksheet_field_role: Union[str, None, UnsetType] = UNSET + """Role of this field, for example: 'dimension', 'measure', or 'unknown'.""" + + tableau_worksheet_field_data_type: Union[str, None, UnsetType] = UNSET + """Data type of this field.""" + + tableau_worksheet_field_upstream_tables: Union[ + List[Dict[str, str]], None, UnsetType + ] = UNSET + """Tables upstream to this worksheet field.""" + + tableau_worksheet_field_formula: Union[str, None, UnsetType] = UNSET + """Formula for this field.""" + + tableau_worksheet_field_bin_size: Union[str, None, UnsetType] = UNSET + """Bin size of this field.""" + + tableau_worksheet_field_upstream_columns: Union[ + List[Dict[str, str]], None, UnsetType + ] = UNSET + """Columns upstream to this field.""" + + tableau_worksheet_field_upstream_fields: Union[ + List[Dict[str, str]], None, UnsetType + ] = UNSET + """Fields upstream to this field.""" + + tableau_worksheet_field_type: Union[str, None, UnsetType] = UNSET + """Type of this worksheet field.""" + + tableau_project_hierarchy_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Array of qualified names representing the project hierarchy for this Tableau asset.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + tableau_dashboard_field: Union[RelatedTableauDashboardField, None, UnsetType] = ( + UNSET + ) + """Worksheet field used by this dashboard field.""" + + tableau_datasource_field: Union[RelatedTableauDatasourceField, None, UnsetType] = ( + UNSET + ) + """Datasource field this worksheet field uses.""" + + tableau_calculated_field: Union[RelatedTableauCalculatedField, None, UnsetType] = ( + UNSET + ) + """Calculated field this worksheet field uses.""" + + tableau_worksheet: Union[RelatedTableauWorksheet, None, UnsetType] = UNSET + """Worksheet in which this field exists.""" + + def __post_init__(self) -> None: + self.type_name = "TableauWorksheetField" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+/[^/]+/[^/]+$" + ) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _tableau_worksheet_field_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> TableauWorksheetField: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + TableauWorksheetField instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _tableau_worksheet_field_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class TableauWorksheetFieldAttributes(AssetAttributes): + """TableauWorksheetField-specific attributes for nested API format.""" + + tableau_site_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the site in which this worksheet field exists.""" + + tableau_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the project in which this worksheet field exists.""" + + tableau_top_level_project_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the top-level project in which this worksheet field exists.""" + + tableau_workbook_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the workbook in which this worksheet field exists.""" + + tableau_worksheet_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the datasource in which this worksheet field exists.""" + + tableau_project_hierarchy: Union[List[Dict[str, str]], None, UnsetType] = UNSET + """List of top-level projects and their nested child projects.""" + + tableau_fully_qualified_name: Union[str, None, UnsetType] = UNSET + """Name used internally in Tableau to uniquely identify this field.""" + + tableau_worksheet_field_data_category: Union[str, None, UnsetType] = UNSET + """Data category of this field.""" + + tableau_worksheet_field_role: Union[str, None, UnsetType] = UNSET + """Role of this field, for example: 'dimension', 'measure', or 'unknown'.""" + + tableau_worksheet_field_data_type: Union[str, None, UnsetType] = UNSET + """Data type of this field.""" + + tableau_worksheet_field_upstream_tables: Union[ + List[Dict[str, str]], None, UnsetType + ] = UNSET + """Tables upstream to this worksheet field.""" + + tableau_worksheet_field_formula: Union[str, None, UnsetType] = UNSET + """Formula for this field.""" + + tableau_worksheet_field_bin_size: Union[str, None, UnsetType] = UNSET + """Bin size of this field.""" + + tableau_worksheet_field_upstream_columns: Union[ + List[Dict[str, str]], None, UnsetType + ] = UNSET + """Columns upstream to this field.""" + + tableau_worksheet_field_upstream_fields: Union[ + List[Dict[str, str]], None, UnsetType + ] = UNSET + """Fields upstream to this field.""" + + tableau_worksheet_field_type: Union[str, None, UnsetType] = UNSET + """Type of this worksheet field.""" + + tableau_project_hierarchy_qualified_names: Union[List[str], None, UnsetType] = UNSET + """Array of qualified names representing the project hierarchy for this Tableau asset.""" + + +class TableauWorksheetFieldRelationshipAttributes(AssetRelationshipAttributes): + """TableauWorksheetField-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + tableau_dashboard_field: Union[RelatedTableauDashboardField, None, UnsetType] = ( + UNSET + ) + """Worksheet field used by this dashboard field.""" + + tableau_datasource_field: Union[RelatedTableauDatasourceField, None, UnsetType] = ( + UNSET + ) + """Datasource field this worksheet field uses.""" + + tableau_calculated_field: Union[RelatedTableauCalculatedField, None, UnsetType] = ( + UNSET + ) + """Calculated field this worksheet field uses.""" + + tableau_worksheet: Union[RelatedTableauWorksheet, None, UnsetType] = UNSET + """Worksheet in which this field exists.""" + + +class TableauWorksheetFieldNested(AssetNested): + """TableauWorksheetField in nested API format for high-performance serialization.""" + + attributes: Union[TableauWorksheetFieldAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + TableauWorksheetFieldRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + TableauWorksheetFieldRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + TableauWorksheetFieldRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_TABLEAU_WORKSHEET_FIELD_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", + "tableau_dashboard_field", + "tableau_datasource_field", + "tableau_calculated_field", + "tableau_worksheet", +] + + +def _populate_tableau_worksheet_field_attrs( + attrs: TableauWorksheetFieldAttributes, obj: TableauWorksheetField +) -> None: + """Populate TableauWorksheetField-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.tableau_site_qualified_name = obj.tableau_site_qualified_name + attrs.tableau_project_qualified_name = obj.tableau_project_qualified_name + attrs.tableau_top_level_project_qualified_name = ( + obj.tableau_top_level_project_qualified_name + ) + attrs.tableau_workbook_qualified_name = obj.tableau_workbook_qualified_name + attrs.tableau_worksheet_qualified_name = obj.tableau_worksheet_qualified_name + attrs.tableau_project_hierarchy = obj.tableau_project_hierarchy + attrs.tableau_fully_qualified_name = obj.tableau_fully_qualified_name + attrs.tableau_worksheet_field_data_category = ( + obj.tableau_worksheet_field_data_category + ) + attrs.tableau_worksheet_field_role = obj.tableau_worksheet_field_role + attrs.tableau_worksheet_field_data_type = obj.tableau_worksheet_field_data_type + attrs.tableau_worksheet_field_upstream_tables = ( + obj.tableau_worksheet_field_upstream_tables + ) + attrs.tableau_worksheet_field_formula = obj.tableau_worksheet_field_formula + attrs.tableau_worksheet_field_bin_size = obj.tableau_worksheet_field_bin_size + attrs.tableau_worksheet_field_upstream_columns = ( + obj.tableau_worksheet_field_upstream_columns + ) + attrs.tableau_worksheet_field_upstream_fields = ( + obj.tableau_worksheet_field_upstream_fields + ) + attrs.tableau_worksheet_field_type = obj.tableau_worksheet_field_type + attrs.tableau_project_hierarchy_qualified_names = ( + obj.tableau_project_hierarchy_qualified_names + ) + + +def _extract_tableau_worksheet_field_attrs( + attrs: TableauWorksheetFieldAttributes, +) -> dict: + """Extract all TableauWorksheetField attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["tableau_site_qualified_name"] = attrs.tableau_site_qualified_name + result["tableau_project_qualified_name"] = attrs.tableau_project_qualified_name + result["tableau_top_level_project_qualified_name"] = ( + attrs.tableau_top_level_project_qualified_name + ) + result["tableau_workbook_qualified_name"] = attrs.tableau_workbook_qualified_name + result["tableau_worksheet_qualified_name"] = attrs.tableau_worksheet_qualified_name + result["tableau_project_hierarchy"] = attrs.tableau_project_hierarchy + result["tableau_fully_qualified_name"] = attrs.tableau_fully_qualified_name + result["tableau_worksheet_field_data_category"] = ( + attrs.tableau_worksheet_field_data_category + ) + result["tableau_worksheet_field_role"] = attrs.tableau_worksheet_field_role + result["tableau_worksheet_field_data_type"] = ( + attrs.tableau_worksheet_field_data_type + ) + result["tableau_worksheet_field_upstream_tables"] = ( + attrs.tableau_worksheet_field_upstream_tables + ) + result["tableau_worksheet_field_formula"] = attrs.tableau_worksheet_field_formula + result["tableau_worksheet_field_bin_size"] = attrs.tableau_worksheet_field_bin_size + result["tableau_worksheet_field_upstream_columns"] = ( + attrs.tableau_worksheet_field_upstream_columns + ) + result["tableau_worksheet_field_upstream_fields"] = ( + attrs.tableau_worksheet_field_upstream_fields + ) + result["tableau_worksheet_field_type"] = attrs.tableau_worksheet_field_type + result["tableau_project_hierarchy_qualified_names"] = ( + attrs.tableau_project_hierarchy_qualified_names + ) + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _tableau_worksheet_field_to_nested( + tableau_worksheet_field: TableauWorksheetField, +) -> TableauWorksheetFieldNested: + """Convert flat TableauWorksheetField to nested format.""" + attrs = TableauWorksheetFieldAttributes() + _populate_tableau_worksheet_field_attrs(attrs, tableau_worksheet_field) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + tableau_worksheet_field, + _TABLEAU_WORKSHEET_FIELD_REL_FIELDS, + TableauWorksheetFieldRelationshipAttributes, + ) + return TableauWorksheetFieldNested( + guid=tableau_worksheet_field.guid, + type_name=tableau_worksheet_field.type_name, + status=tableau_worksheet_field.status, + version=tableau_worksheet_field.version, + create_time=tableau_worksheet_field.create_time, + update_time=tableau_worksheet_field.update_time, + created_by=tableau_worksheet_field.created_by, + updated_by=tableau_worksheet_field.updated_by, + classifications=tableau_worksheet_field.classifications, + classification_names=tableau_worksheet_field.classification_names, + meanings=tableau_worksheet_field.meanings, + labels=tableau_worksheet_field.labels, + business_attributes=tableau_worksheet_field.business_attributes, + custom_attributes=tableau_worksheet_field.custom_attributes, + pending_tasks=tableau_worksheet_field.pending_tasks, + proxy=tableau_worksheet_field.proxy, + is_incomplete=tableau_worksheet_field.is_incomplete, + provenance_type=tableau_worksheet_field.provenance_type, + home_id=tableau_worksheet_field.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _tableau_worksheet_field_from_nested( + nested: TableauWorksheetFieldNested, +) -> TableauWorksheetField: + """Convert nested format to flat TableauWorksheetField.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else TableauWorksheetFieldAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _TABLEAU_WORKSHEET_FIELD_REL_FIELDS, + TableauWorksheetFieldRelationshipAttributes, + ) + return TableauWorksheetField( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_tableau_worksheet_field_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _tableau_worksheet_field_to_nested_bytes( + tableau_worksheet_field: TableauWorksheetField, serde: Serde +) -> bytes: + """Convert flat TableauWorksheetField to nested JSON bytes.""" + return serde.encode(_tableau_worksheet_field_to_nested(tableau_worksheet_field)) + + +def _tableau_worksheet_field_from_nested_bytes( + data: bytes, serde: Serde +) -> TableauWorksheetField: + """Convert nested JSON bytes to flat TableauWorksheetField.""" + nested = serde.decode(data, TableauWorksheetFieldNested) + return _tableau_worksheet_field_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + RelationField, +) + +TableauWorksheetField.TABLEAU_SITE_QUALIFIED_NAME = KeywordField( + "tableauSiteQualifiedName", "tableauSiteQualifiedName" +) +TableauWorksheetField.TABLEAU_PROJECT_QUALIFIED_NAME = KeywordField( + "tableauProjectQualifiedName", "tableauProjectQualifiedName" +) +TableauWorksheetField.TABLEAU_TOP_LEVEL_PROJECT_QUALIFIED_NAME = KeywordField( + "tableauTopLevelProjectQualifiedName", "tableauTopLevelProjectQualifiedName" +) +TableauWorksheetField.TABLEAU_WORKBOOK_QUALIFIED_NAME = KeywordField( + "tableauWorkbookQualifiedName", "tableauWorkbookQualifiedName" +) +TableauWorksheetField.TABLEAU_WORKSHEET_QUALIFIED_NAME = KeywordField( + "tableauWorksheetQualifiedName", "tableauWorksheetQualifiedName" +) +TableauWorksheetField.TABLEAU_PROJECT_HIERARCHY = KeywordField( + "tableauProjectHierarchy", "tableauProjectHierarchy" +) +TableauWorksheetField.TABLEAU_FULLY_QUALIFIED_NAME = KeywordField( + "tableauFullyQualifiedName", "tableauFullyQualifiedName" +) +TableauWorksheetField.TABLEAU_WORKSHEET_FIELD_DATA_CATEGORY = KeywordField( + "tableauWorksheetFieldDataCategory", "tableauWorksheetFieldDataCategory" +) +TableauWorksheetField.TABLEAU_WORKSHEET_FIELD_ROLE = KeywordField( + "tableauWorksheetFieldRole", "tableauWorksheetFieldRole" +) +TableauWorksheetField.TABLEAU_WORKSHEET_FIELD_DATA_TYPE = KeywordTextField( + "tableauWorksheetFieldDataType", + "tableauWorksheetFieldDataType", + "tableauWorksheetFieldDataType.text", +) +TableauWorksheetField.TABLEAU_WORKSHEET_FIELD_UPSTREAM_TABLES = KeywordField( + "tableauWorksheetFieldUpstreamTables", "tableauWorksheetFieldUpstreamTables" +) +TableauWorksheetField.TABLEAU_WORKSHEET_FIELD_FORMULA = KeywordField( + "tableauWorksheetFieldFormula", "tableauWorksheetFieldFormula" +) +TableauWorksheetField.TABLEAU_WORKSHEET_FIELD_BIN_SIZE = KeywordField( + "tableauWorksheetFieldBinSize", "tableauWorksheetFieldBinSize" +) +TableauWorksheetField.TABLEAU_WORKSHEET_FIELD_UPSTREAM_COLUMNS = KeywordField( + "tableauWorksheetFieldUpstreamColumns", "tableauWorksheetFieldUpstreamColumns" +) +TableauWorksheetField.TABLEAU_WORKSHEET_FIELD_UPSTREAM_FIELDS = KeywordField( + "tableauWorksheetFieldUpstreamFields", "tableauWorksheetFieldUpstreamFields" +) +TableauWorksheetField.TABLEAU_WORKSHEET_FIELD_TYPE = KeywordField( + "tableauWorksheetFieldType", "tableauWorksheetFieldType" +) +TableauWorksheetField.TABLEAU_PROJECT_HIERARCHY_QUALIFIED_NAMES = KeywordField( + "tableauProjectHierarchyQualifiedNames", "tableauProjectHierarchyQualifiedNames" +) +TableauWorksheetField.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +TableauWorksheetField.OUTPUT_FROM_AIRFLOW_TASKS = RelationField( + "outputFromAirflowTasks" +) +TableauWorksheetField.ANOMALO_CHECKS = RelationField("anomaloChecks") +TableauWorksheetField.APPLICATION = RelationField("application") +TableauWorksheetField.APPLICATION_FIELD = RelationField("applicationField") +TableauWorksheetField.OUTPUT_PORT_DATA_PRODUCTS = RelationField( + "outputPortDataProducts" +) +TableauWorksheetField.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +TableauWorksheetField.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +TableauWorksheetField.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +TableauWorksheetField.METRICS = RelationField("metrics") +TableauWorksheetField.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +TableauWorksheetField.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +TableauWorksheetField.MEANINGS = RelationField("meanings") +TableauWorksheetField.MC_MONITORS = RelationField("mcMonitors") +TableauWorksheetField.MC_INCIDENTS = RelationField("mcIncidents") +TableauWorksheetField.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +TableauWorksheetField.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +TableauWorksheetField.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +TableauWorksheetField.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +TableauWorksheetField.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +TableauWorksheetField.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +TableauWorksheetField.FILES = RelationField("files") +TableauWorksheetField.LINKS = RelationField("links") +TableauWorksheetField.README = RelationField("readme") +TableauWorksheetField.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +TableauWorksheetField.SODA_CHECKS = RelationField("sodaChecks") +TableauWorksheetField.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +TableauWorksheetField.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") +TableauWorksheetField.TABLEAU_DASHBOARD_FIELD = RelationField("tableauDashboardField") +TableauWorksheetField.TABLEAU_DATASOURCE_FIELD = RelationField("tableauDatasourceField") +TableauWorksheetField.TABLEAU_CALCULATED_FIELD = RelationField("tableauCalculatedField") +TableauWorksheetField.TABLEAU_WORKSHEET = RelationField("tableauWorksheet") diff --git a/pyatlan_v9/model/assets/tag.py b/pyatlan_v9/model/assets/tag.py new file mode 100644 index 000000000..1b2ceed72 --- /dev/null +++ b/pyatlan_v9/model/assets/tag.py @@ -0,0 +1,566 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Tag asset model with flattened inheritance. + +This module provides: +- Tag: Flat asset class (easy to use) +- TagAttributes: Nested attributes struct (extends AssetAttributes) +- TagNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Tag(Asset): + """ + Base class for all (source-specific) tag assets. + """ + + TAG_ID: ClassVar[Any] = None + TAG_ATTRIBUTES: ClassVar[Any] = None + TAG_ALLOWED_VALUES: ClassVar[Any] = None + MAPPED_CLASSIFICATION_NAME: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Tag" + + tag_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the tag in the source system.""" + + tag_attributes: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """Attributes associated with the tag in the source system.""" + + tag_allowed_values: Union[List[str], None, UnsetType] = UNSET + """Allowed values for the tag in the source system. These are denormalized from tagAttributes for ease of querying.""" + + mapped_classification_name: Union[str, None, UnsetType] = UNSET + """Name of the classification in Atlan that is mapped to this tag.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Tag" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _tag_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Tag: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Tag instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _tag_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class TagAttributes(AssetAttributes): + """Tag-specific attributes for nested API format.""" + + tag_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the tag in the source system.""" + + tag_attributes: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """Attributes associated with the tag in the source system.""" + + tag_allowed_values: Union[List[str], None, UnsetType] = UNSET + """Allowed values for the tag in the source system. These are denormalized from tagAttributes for ease of querying.""" + + mapped_classification_name: Union[str, None, UnsetType] = UNSET + """Name of the classification in Atlan that is mapped to this tag.""" + + +class TagRelationshipAttributes(AssetRelationshipAttributes): + """Tag-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class TagNested(AssetNested): + """Tag in nested API format for high-performance serialization.""" + + attributes: Union[TagAttributes, UnsetType] = UNSET + relationship_attributes: Union[TagRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[TagRelationshipAttributes, UnsetType] = UNSET + remove_relationship_attributes: Union[TagRelationshipAttributes, UnsetType] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_TAG_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_tag_attrs(attrs: TagAttributes, obj: Tag) -> None: + """Populate Tag-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.tag_id = obj.tag_id + attrs.tag_attributes = obj.tag_attributes + attrs.tag_allowed_values = obj.tag_allowed_values + attrs.mapped_classification_name = obj.mapped_classification_name + + +def _extract_tag_attrs(attrs: TagAttributes) -> dict: + """Extract all Tag attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["tag_id"] = attrs.tag_id + result["tag_attributes"] = attrs.tag_attributes + result["tag_allowed_values"] = attrs.tag_allowed_values + result["mapped_classification_name"] = attrs.mapped_classification_name + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _tag_to_nested(tag: Tag) -> TagNested: + """Convert flat Tag to nested format.""" + attrs = TagAttributes() + _populate_tag_attrs(attrs, tag) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + tag, _TAG_REL_FIELDS, TagRelationshipAttributes + ) + return TagNested( + guid=tag.guid, + type_name=tag.type_name, + status=tag.status, + version=tag.version, + create_time=tag.create_time, + update_time=tag.update_time, + created_by=tag.created_by, + updated_by=tag.updated_by, + classifications=tag.classifications, + classification_names=tag.classification_names, + meanings=tag.meanings, + labels=tag.labels, + business_attributes=tag.business_attributes, + custom_attributes=tag.custom_attributes, + pending_tasks=tag.pending_tasks, + proxy=tag.proxy, + is_incomplete=tag.is_incomplete, + provenance_type=tag.provenance_type, + home_id=tag.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _tag_from_nested(nested: TagNested) -> Tag: + """Convert nested format to flat Tag.""" + attrs = nested.attributes if nested.attributes is not UNSET else TagAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _TAG_REL_FIELDS, + TagRelationshipAttributes, + ) + return Tag( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_tag_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _tag_to_nested_bytes(tag: Tag, serde: Serde) -> bytes: + """Convert flat Tag to nested JSON bytes.""" + return serde.encode(_tag_to_nested(tag)) + + +def _tag_from_nested_bytes(data: bytes, serde: Serde) -> Tag: + """Convert nested JSON bytes to flat Tag.""" + nested = serde.decode(data, TagNested) + return _tag_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + RelationField, +) + +Tag.TAG_ID = KeywordField("tagId", "tagId") +Tag.TAG_ATTRIBUTES = KeywordField("tagAttributes", "tagAttributes") +Tag.TAG_ALLOWED_VALUES = KeywordTextField( + "tagAllowedValues", "tagAllowedValues", "tagAllowedValues.text" +) +Tag.MAPPED_CLASSIFICATION_NAME = KeywordField( + "mappedClassificationName", "mappedClassificationName" +) +Tag.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Tag.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Tag.ANOMALO_CHECKS = RelationField("anomaloChecks") +Tag.APPLICATION = RelationField("application") +Tag.APPLICATION_FIELD = RelationField("applicationField") +Tag.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Tag.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Tag.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Tag.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Tag.METRICS = RelationField("metrics") +Tag.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Tag.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Tag.MEANINGS = RelationField("meanings") +Tag.MC_MONITORS = RelationField("mcMonitors") +Tag.MC_INCIDENTS = RelationField("mcIncidents") +Tag.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Tag.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Tag.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Tag.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Tag.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Tag.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Tag.FILES = RelationField("files") +Tag.LINKS = RelationField("links") +Tag.README = RelationField("readme") +Tag.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Tag.SODA_CHECKS = RelationField("sodaChecks") +Tag.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Tag.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/tag_related.py b/pyatlan_v9/model/assets/tag_related.py new file mode 100644 index 000000000..5e7716afe --- /dev/null +++ b/pyatlan_v9/model/assets/tag_related.py @@ -0,0 +1,91 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Tag module. + +This module contains all Related{Type} classes for the Tag type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Any, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedCatalog +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedTag", + "RelatedTagAttachment", + "RelatedSourceTag", +] + + +class RelatedTag(RelatedCatalog): + """ + Related entity reference for Tag assets. + + Extends RelatedCatalog with Tag-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Tag" so it serializes correctly + + tag_id: Union[str, None, UnsetType] = UNSET + """Unique identifier of the tag in the source system.""" + + tag_attributes: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """Attributes associated with the tag in the source system.""" + + tag_allowed_values: Union[List[str], None, UnsetType] = UNSET + """Allowed values for the tag in the source system. These are denormalized from tagAttributes for ease of querying.""" + + mapped_classification_name: Union[str, None, UnsetType] = UNSET + """Name of the classification in Atlan that is mapped to this tag.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Tag" + + +class RelatedTagAttachment(RelatedTag): + """ + Related entity reference for TagAttachment assets. + + Extends RelatedTag with TagAttachment-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "TagAttachment" so it serializes correctly + + tag_qualified_name: Union[str, None, UnsetType] = UNSET + """Represents associated source tag's qualified name.""" + + tag_attachment_string_value: Union[str, None, UnsetType] = UNSET + """Represents associated tag value.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "TagAttachment" + + +class RelatedSourceTag(RelatedTag): + """ + Related entity reference for SourceTag assets. + + Extends RelatedTag with SourceTag-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "SourceTag" so it serializes correctly + + tag_custom_configuration: Union[str, None, UnsetType] = UNSET + """Specifies custom configuration elements based on the system the tag is being imported from.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "SourceTag" diff --git a/pyatlan_v9/model/assets/task.py b/pyatlan_v9/model/assets/task.py new file mode 100644 index 000000000..3d77016f4 --- /dev/null +++ b/pyatlan_v9/model/assets/task.py @@ -0,0 +1,572 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Task asset model with flattened inheritance. + +This module provides: +- Task: Flat asset class (easy to use) +- TaskAttributes: Nested attributes struct (extends AssetAttributes) +- TaskNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Task(Asset): + """ + Instance of a Task for user in Atlan. + """ + + TASK_RECIPIENT: ClassVar[Any] = None + TASK_TYPE: ClassVar[Any] = None + TASK_REQUESTOR: ClassVar[Any] = None + TASK_IS_READ: ClassVar[Any] = None + TASK_REQUESTOR_COMMENT: ClassVar[Any] = None + TASK_RELATED_ASSET_GUID: ClassVar[Any] = None + TASK_PROPOSALS: ClassVar[Any] = None + TASK_EXPIRES_AT: ClassVar[Any] = None + TASK_ACTIONS: ClassVar[Any] = None + TASK_EXECUTION_COMMENT: ClassVar[Any] = None + TASK_EXECUTION_ACTION: ClassVar[Any] = None + TASK_INTEGRATION_CONFIG: ClassVar[Any] = None + TASK_CREATED_BY: ClassVar[Any] = None + TASK_UPDATED_BY: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Task" + + task_recipient: Union[str, None, UnsetType] = UNSET + """Recipient of the task.""" + + task_type: Union[str, None, UnsetType] = UNSET + """Type of task.""" + + task_requestor: Union[str, None, UnsetType] = UNSET + """Requestor of the task.""" + + task_is_read: Union[bool, None, UnsetType] = UNSET + """Flag to make task read/unread.""" + + task_requestor_comment: Union[str, None, UnsetType] = UNSET + """Comment of requestor for the task.""" + + task_related_asset_guid: Union[str, None, UnsetType] = UNSET + """Unique identifier of the asset to preview.""" + + task_proposals: Union[str, None, UnsetType] = UNSET + """Contains the payload that is proposed to the task.""" + + task_expires_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the task expires.""" + + task_actions: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of actions associated with this task.""" + + task_execution_comment: Union[str, None, UnsetType] = UNSET + """Comment for the action executed by user.""" + + task_execution_action: Union[str, None, UnsetType] = UNSET + """Action executed by the recipient.""" + + task_integration_config: Union[str, None, UnsetType] = UNSET + """Contains external integration config for the task.""" + + task_created_by: Union[str, None, UnsetType] = UNSET + """Username of the user who created this task.""" + + task_updated_by: Union[str, None, UnsetType] = UNSET + """Username of the user who updated this task.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Task" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _task_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Task: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Task instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _task_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class TaskAttributes(AssetAttributes): + """Task-specific attributes for nested API format.""" + + task_recipient: Union[str, None, UnsetType] = UNSET + """Recipient of the task.""" + + task_type: Union[str, None, UnsetType] = UNSET + """Type of task.""" + + task_requestor: Union[str, None, UnsetType] = UNSET + """Requestor of the task.""" + + task_is_read: Union[bool, None, UnsetType] = UNSET + """Flag to make task read/unread.""" + + task_requestor_comment: Union[str, None, UnsetType] = UNSET + """Comment of requestor for the task.""" + + task_related_asset_guid: Union[str, None, UnsetType] = UNSET + """Unique identifier of the asset to preview.""" + + task_proposals: Union[str, None, UnsetType] = UNSET + """Contains the payload that is proposed to the task.""" + + task_expires_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the task expires.""" + + task_actions: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of actions associated with this task.""" + + task_execution_comment: Union[str, None, UnsetType] = UNSET + """Comment for the action executed by user.""" + + task_execution_action: Union[str, None, UnsetType] = UNSET + """Action executed by the recipient.""" + + task_integration_config: Union[str, None, UnsetType] = UNSET + """Contains external integration config for the task.""" + + task_created_by: Union[str, None, UnsetType] = UNSET + """Username of the user who created this task.""" + + task_updated_by: Union[str, None, UnsetType] = UNSET + """Username of the user who updated this task.""" + + +class TaskRelationshipAttributes(AssetRelationshipAttributes): + """Task-specific relationship attributes for nested API format.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + +class TaskNested(AssetNested): + """Task in nested API format for high-performance serialization.""" + + attributes: Union[TaskAttributes, UnsetType] = UNSET + relationship_attributes: Union[TaskRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[TaskRelationshipAttributes, UnsetType] = UNSET + remove_relationship_attributes: Union[TaskRelationshipAttributes, UnsetType] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_TASK_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", +] + + +def _populate_task_attrs(attrs: TaskAttributes, obj: Task) -> None: + """Populate Task-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.task_recipient = obj.task_recipient + attrs.task_type = obj.task_type + attrs.task_requestor = obj.task_requestor + attrs.task_is_read = obj.task_is_read + attrs.task_requestor_comment = obj.task_requestor_comment + attrs.task_related_asset_guid = obj.task_related_asset_guid + attrs.task_proposals = obj.task_proposals + attrs.task_expires_at = obj.task_expires_at + attrs.task_actions = obj.task_actions + attrs.task_execution_comment = obj.task_execution_comment + attrs.task_execution_action = obj.task_execution_action + attrs.task_integration_config = obj.task_integration_config + attrs.task_created_by = obj.task_created_by + attrs.task_updated_by = obj.task_updated_by + + +def _extract_task_attrs(attrs: TaskAttributes) -> dict: + """Extract all Task attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["task_recipient"] = attrs.task_recipient + result["task_type"] = attrs.task_type + result["task_requestor"] = attrs.task_requestor + result["task_is_read"] = attrs.task_is_read + result["task_requestor_comment"] = attrs.task_requestor_comment + result["task_related_asset_guid"] = attrs.task_related_asset_guid + result["task_proposals"] = attrs.task_proposals + result["task_expires_at"] = attrs.task_expires_at + result["task_actions"] = attrs.task_actions + result["task_execution_comment"] = attrs.task_execution_comment + result["task_execution_action"] = attrs.task_execution_action + result["task_integration_config"] = attrs.task_integration_config + result["task_created_by"] = attrs.task_created_by + result["task_updated_by"] = attrs.task_updated_by + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _task_to_nested(task: Task) -> TaskNested: + """Convert flat Task to nested format.""" + attrs = TaskAttributes() + _populate_task_attrs(attrs, task) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + task, _TASK_REL_FIELDS, TaskRelationshipAttributes + ) + return TaskNested( + guid=task.guid, + type_name=task.type_name, + status=task.status, + version=task.version, + create_time=task.create_time, + update_time=task.update_time, + created_by=task.created_by, + updated_by=task.updated_by, + classifications=task.classifications, + classification_names=task.classification_names, + meanings=task.meanings, + labels=task.labels, + business_attributes=task.business_attributes, + custom_attributes=task.custom_attributes, + pending_tasks=task.pending_tasks, + proxy=task.proxy, + is_incomplete=task.is_incomplete, + provenance_type=task.provenance_type, + home_id=task.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _task_from_nested(nested: TaskNested) -> Task: + """Convert nested format to flat Task.""" + attrs = nested.attributes if nested.attributes is not UNSET else TaskAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _TASK_REL_FIELDS, + TaskRelationshipAttributes, + ) + return Task( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_task_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _task_to_nested_bytes(task: Task, serde: Serde) -> bytes: + """Convert flat Task to nested JSON bytes.""" + return serde.encode(_task_to_nested(task)) + + +def _task_from_nested_bytes(data: bytes, serde: Serde) -> Task: + """Convert nested JSON bytes to flat Task.""" + nested = serde.decode(data, TaskNested) + return _task_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, +) + +Task.TASK_RECIPIENT = KeywordField("taskRecipient", "taskRecipient") +Task.TASK_TYPE = KeywordField("taskType", "taskType") +Task.TASK_REQUESTOR = KeywordField("taskRequestor", "taskRequestor") +Task.TASK_IS_READ = BooleanField("taskIsRead", "taskIsRead") +Task.TASK_REQUESTOR_COMMENT = KeywordField( + "taskRequestorComment", "taskRequestorComment" +) +Task.TASK_RELATED_ASSET_GUID = KeywordField( + "taskRelatedAssetGuid", "taskRelatedAssetGuid" +) +Task.TASK_PROPOSALS = KeywordField("taskProposals", "taskProposals") +Task.TASK_EXPIRES_AT = NumericField("taskExpiresAt", "taskExpiresAt") +Task.TASK_ACTIONS = KeywordField("taskActions", "taskActions") +Task.TASK_EXECUTION_COMMENT = KeywordField( + "taskExecutionComment", "taskExecutionComment" +) +Task.TASK_EXECUTION_ACTION = KeywordField("taskExecutionAction", "taskExecutionAction") +Task.TASK_INTEGRATION_CONFIG = KeywordField( + "taskIntegrationConfig", "taskIntegrationConfig" +) +Task.TASK_CREATED_BY = KeywordField("taskCreatedBy", "taskCreatedBy") +Task.TASK_UPDATED_BY = KeywordField("taskUpdatedBy", "taskUpdatedBy") +Task.ANOMALO_CHECKS = RelationField("anomaloChecks") +Task.APPLICATION = RelationField("application") +Task.APPLICATION_FIELD = RelationField("applicationField") +Task.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Task.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Task.METRICS = RelationField("metrics") +Task.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Task.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Task.MEANINGS = RelationField("meanings") +Task.MC_MONITORS = RelationField("mcMonitors") +Task.MC_INCIDENTS = RelationField("mcIncidents") +Task.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Task.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Task.FILES = RelationField("files") +Task.LINKS = RelationField("links") +Task.README = RelationField("readme") +Task.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Task.SODA_CHECKS = RelationField("sodaChecks") diff --git a/pyatlan_v9/model/assets/task_related.py b/pyatlan_v9/model/assets/task_related.py new file mode 100644 index 000000000..2e20935df --- /dev/null +++ b/pyatlan_v9/model/assets/task_related.py @@ -0,0 +1,80 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Task module. + +This module contains all Related{Type} classes for the Task type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Any, Dict, List, Union + +from msgspec import UNSET, UnsetType + +from .asset_related import RelatedAsset +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedTask", +] + + +class RelatedTask(RelatedAsset): + """ + Related entity reference for Task assets. + + Extends RelatedAsset with Task-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Task" so it serializes correctly + + task_recipient: Union[str, None, UnsetType] = UNSET + """Recipient of the task.""" + + task_type: Union[str, None, UnsetType] = UNSET + """Type of task.""" + + task_requestor: Union[str, None, UnsetType] = UNSET + """Requestor of the task.""" + + task_is_read: Union[bool, None, UnsetType] = UNSET + """Flag to make task read/unread.""" + + task_requestor_comment: Union[str, None, UnsetType] = UNSET + """Comment of requestor for the task.""" + + task_related_asset_guid: Union[str, None, UnsetType] = UNSET + """Unique identifier of the asset to preview.""" + + task_proposals: Union[str, None, UnsetType] = UNSET + """Contains the payload that is proposed to the task.""" + + task_expires_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the task expires.""" + + task_actions: Union[List[Dict[str, Any]], None, UnsetType] = UNSET + """List of actions associated with this task.""" + + task_execution_comment: Union[str, None, UnsetType] = UNSET + """Comment for the action executed by user.""" + + task_execution_action: Union[str, None, UnsetType] = UNSET + """Action executed by the recipient.""" + + task_integration_config: Union[str, None, UnsetType] = UNSET + """Contains external integration config for the task.""" + + task_created_by: Union[str, None, UnsetType] = UNSET + """Username of the user who created this task.""" + + task_updated_by: Union[str, None, UnsetType] = UNSET + """Username of the user who updated this task.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Task" diff --git a/pyatlan_v9/model/assets/thoughtspot.py b/pyatlan_v9/model/assets/thoughtspot.py new file mode 100644 index 000000000..340acfdd4 --- /dev/null +++ b/pyatlan_v9/model/assets/thoughtspot.py @@ -0,0 +1,576 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Thoughtspot asset model with flattened inheritance. + +This module provides: +- Thoughtspot: Flat asset class (easy to use) +- ThoughtspotAttributes: Nested attributes struct (extends AssetAttributes) +- ThoughtspotNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Thoughtspot(Asset): + """ + Base class for Thoughtspot assets. + """ + + THOUGHTSPOT_CHART_TYPE: ClassVar[Any] = None + THOUGHTSPOT_QUESTION_TEXT: ClassVar[Any] = None + THOUGHTSPOT_JOIN_COUNT: ClassVar[Any] = None + THOUGHTSPOT_COLUMN_COUNT: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Thoughtspot" + + thoughtspot_chart_type: Union[str, None, UnsetType] = UNSET + """""" + + thoughtspot_question_text: Union[str, None, UnsetType] = UNSET + """""" + + thoughtspot_join_count: Union[int, None, UnsetType] = UNSET + """Total number of data table joins executed for analysis.""" + + thoughtspot_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Thoughtspot" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _thoughtspot_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Thoughtspot: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Thoughtspot instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _thoughtspot_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class ThoughtspotAttributes(AssetAttributes): + """Thoughtspot-specific attributes for nested API format.""" + + thoughtspot_chart_type: Union[str, None, UnsetType] = UNSET + """""" + + thoughtspot_question_text: Union[str, None, UnsetType] = UNSET + """""" + + thoughtspot_join_count: Union[int, None, UnsetType] = UNSET + """Total number of data table joins executed for analysis.""" + + thoughtspot_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns.""" + + +class ThoughtspotRelationshipAttributes(AssetRelationshipAttributes): + """Thoughtspot-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class ThoughtspotNested(AssetNested): + """Thoughtspot in nested API format for high-performance serialization.""" + + attributes: Union[ThoughtspotAttributes, UnsetType] = UNSET + relationship_attributes: Union[ThoughtspotRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ + ThoughtspotRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + ThoughtspotRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_THOUGHTSPOT_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_thoughtspot_attrs(attrs: ThoughtspotAttributes, obj: Thoughtspot) -> None: + """Populate Thoughtspot-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.thoughtspot_chart_type = obj.thoughtspot_chart_type + attrs.thoughtspot_question_text = obj.thoughtspot_question_text + attrs.thoughtspot_join_count = obj.thoughtspot_join_count + attrs.thoughtspot_column_count = obj.thoughtspot_column_count + + +def _extract_thoughtspot_attrs(attrs: ThoughtspotAttributes) -> dict: + """Extract all Thoughtspot attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["thoughtspot_chart_type"] = attrs.thoughtspot_chart_type + result["thoughtspot_question_text"] = attrs.thoughtspot_question_text + result["thoughtspot_join_count"] = attrs.thoughtspot_join_count + result["thoughtspot_column_count"] = attrs.thoughtspot_column_count + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _thoughtspot_to_nested(thoughtspot: Thoughtspot) -> ThoughtspotNested: + """Convert flat Thoughtspot to nested format.""" + attrs = ThoughtspotAttributes() + _populate_thoughtspot_attrs(attrs, thoughtspot) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + thoughtspot, _THOUGHTSPOT_REL_FIELDS, ThoughtspotRelationshipAttributes + ) + return ThoughtspotNested( + guid=thoughtspot.guid, + type_name=thoughtspot.type_name, + status=thoughtspot.status, + version=thoughtspot.version, + create_time=thoughtspot.create_time, + update_time=thoughtspot.update_time, + created_by=thoughtspot.created_by, + updated_by=thoughtspot.updated_by, + classifications=thoughtspot.classifications, + classification_names=thoughtspot.classification_names, + meanings=thoughtspot.meanings, + labels=thoughtspot.labels, + business_attributes=thoughtspot.business_attributes, + custom_attributes=thoughtspot.custom_attributes, + pending_tasks=thoughtspot.pending_tasks, + proxy=thoughtspot.proxy, + is_incomplete=thoughtspot.is_incomplete, + provenance_type=thoughtspot.provenance_type, + home_id=thoughtspot.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _thoughtspot_from_nested(nested: ThoughtspotNested) -> Thoughtspot: + """Convert nested format to flat Thoughtspot.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else ThoughtspotAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _THOUGHTSPOT_REL_FIELDS, + ThoughtspotRelationshipAttributes, + ) + return Thoughtspot( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_thoughtspot_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _thoughtspot_to_nested_bytes(thoughtspot: Thoughtspot, serde: Serde) -> bytes: + """Convert flat Thoughtspot to nested JSON bytes.""" + return serde.encode(_thoughtspot_to_nested(thoughtspot)) + + +def _thoughtspot_from_nested_bytes(data: bytes, serde: Serde) -> Thoughtspot: + """Convert nested JSON bytes to flat Thoughtspot.""" + nested = serde.decode(data, ThoughtspotNested) + return _thoughtspot_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +Thoughtspot.THOUGHTSPOT_CHART_TYPE = KeywordField( + "thoughtspotChartType", "thoughtspotChartType" +) +Thoughtspot.THOUGHTSPOT_QUESTION_TEXT = KeywordField( + "thoughtspotQuestionText", "thoughtspotQuestionText" +) +Thoughtspot.THOUGHTSPOT_JOIN_COUNT = NumericField( + "thoughtspotJoinCount", "thoughtspotJoinCount" +) +Thoughtspot.THOUGHTSPOT_COLUMN_COUNT = NumericField( + "thoughtspotColumnCount", "thoughtspotColumnCount" +) +Thoughtspot.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +Thoughtspot.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +Thoughtspot.ANOMALO_CHECKS = RelationField("anomaloChecks") +Thoughtspot.APPLICATION = RelationField("application") +Thoughtspot.APPLICATION_FIELD = RelationField("applicationField") +Thoughtspot.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Thoughtspot.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Thoughtspot.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +Thoughtspot.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +Thoughtspot.METRICS = RelationField("metrics") +Thoughtspot.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Thoughtspot.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Thoughtspot.MEANINGS = RelationField("meanings") +Thoughtspot.MC_MONITORS = RelationField("mcMonitors") +Thoughtspot.MC_INCIDENTS = RelationField("mcIncidents") +Thoughtspot.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +Thoughtspot.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +Thoughtspot.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +Thoughtspot.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +Thoughtspot.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Thoughtspot.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Thoughtspot.FILES = RelationField("files") +Thoughtspot.LINKS = RelationField("links") +Thoughtspot.README = RelationField("readme") +Thoughtspot.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Thoughtspot.SODA_CHECKS = RelationField("sodaChecks") +Thoughtspot.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +Thoughtspot.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/thoughtspot_answer.py b/pyatlan_v9/model/assets/thoughtspot_answer.py new file mode 100644 index 000000000..696b5650d --- /dev/null +++ b/pyatlan_v9/model/assets/thoughtspot_answer.py @@ -0,0 +1,596 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +ThoughtspotAnswer asset model with flattened inheritance. + +This module provides: +- ThoughtspotAnswer: Flat asset class (easy to use) +- ThoughtspotAnswerAttributes: Nested attributes struct (extends AssetAttributes) +- ThoughtspotAnswerNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class ThoughtspotAnswer(Asset): + """ + Instance of a Thoughtspot answer in Atlan. + """ + + THOUGHTSPOT_CHART_TYPE: ClassVar[Any] = None + THOUGHTSPOT_QUESTION_TEXT: ClassVar[Any] = None + THOUGHTSPOT_JOIN_COUNT: ClassVar[Any] = None + THOUGHTSPOT_COLUMN_COUNT: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "ThoughtspotAnswer" + + thoughtspot_chart_type: Union[str, None, UnsetType] = UNSET + """""" + + thoughtspot_question_text: Union[str, None, UnsetType] = UNSET + """""" + + thoughtspot_join_count: Union[int, None, UnsetType] = UNSET + """Total number of data table joins executed for analysis.""" + + thoughtspot_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "ThoughtspotAnswer" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _thoughtspot_answer_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> ThoughtspotAnswer: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + ThoughtspotAnswer instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _thoughtspot_answer_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class ThoughtspotAnswerAttributes(AssetAttributes): + """ThoughtspotAnswer-specific attributes for nested API format.""" + + thoughtspot_chart_type: Union[str, None, UnsetType] = UNSET + """""" + + thoughtspot_question_text: Union[str, None, UnsetType] = UNSET + """""" + + thoughtspot_join_count: Union[int, None, UnsetType] = UNSET + """Total number of data table joins executed for analysis.""" + + thoughtspot_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns.""" + + +class ThoughtspotAnswerRelationshipAttributes(AssetRelationshipAttributes): + """ThoughtspotAnswer-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class ThoughtspotAnswerNested(AssetNested): + """ThoughtspotAnswer in nested API format for high-performance serialization.""" + + attributes: Union[ThoughtspotAnswerAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + ThoughtspotAnswerRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + ThoughtspotAnswerRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + ThoughtspotAnswerRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_THOUGHTSPOT_ANSWER_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_thoughtspot_answer_attrs( + attrs: ThoughtspotAnswerAttributes, obj: ThoughtspotAnswer +) -> None: + """Populate ThoughtspotAnswer-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.thoughtspot_chart_type = obj.thoughtspot_chart_type + attrs.thoughtspot_question_text = obj.thoughtspot_question_text + attrs.thoughtspot_join_count = obj.thoughtspot_join_count + attrs.thoughtspot_column_count = obj.thoughtspot_column_count + + +def _extract_thoughtspot_answer_attrs(attrs: ThoughtspotAnswerAttributes) -> dict: + """Extract all ThoughtspotAnswer attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["thoughtspot_chart_type"] = attrs.thoughtspot_chart_type + result["thoughtspot_question_text"] = attrs.thoughtspot_question_text + result["thoughtspot_join_count"] = attrs.thoughtspot_join_count + result["thoughtspot_column_count"] = attrs.thoughtspot_column_count + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _thoughtspot_answer_to_nested( + thoughtspot_answer: ThoughtspotAnswer, +) -> ThoughtspotAnswerNested: + """Convert flat ThoughtspotAnswer to nested format.""" + attrs = ThoughtspotAnswerAttributes() + _populate_thoughtspot_answer_attrs(attrs, thoughtspot_answer) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + thoughtspot_answer, + _THOUGHTSPOT_ANSWER_REL_FIELDS, + ThoughtspotAnswerRelationshipAttributes, + ) + return ThoughtspotAnswerNested( + guid=thoughtspot_answer.guid, + type_name=thoughtspot_answer.type_name, + status=thoughtspot_answer.status, + version=thoughtspot_answer.version, + create_time=thoughtspot_answer.create_time, + update_time=thoughtspot_answer.update_time, + created_by=thoughtspot_answer.created_by, + updated_by=thoughtspot_answer.updated_by, + classifications=thoughtspot_answer.classifications, + classification_names=thoughtspot_answer.classification_names, + meanings=thoughtspot_answer.meanings, + labels=thoughtspot_answer.labels, + business_attributes=thoughtspot_answer.business_attributes, + custom_attributes=thoughtspot_answer.custom_attributes, + pending_tasks=thoughtspot_answer.pending_tasks, + proxy=thoughtspot_answer.proxy, + is_incomplete=thoughtspot_answer.is_incomplete, + provenance_type=thoughtspot_answer.provenance_type, + home_id=thoughtspot_answer.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _thoughtspot_answer_from_nested( + nested: ThoughtspotAnswerNested, +) -> ThoughtspotAnswer: + """Convert nested format to flat ThoughtspotAnswer.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else ThoughtspotAnswerAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _THOUGHTSPOT_ANSWER_REL_FIELDS, + ThoughtspotAnswerRelationshipAttributes, + ) + return ThoughtspotAnswer( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_thoughtspot_answer_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _thoughtspot_answer_to_nested_bytes( + thoughtspot_answer: ThoughtspotAnswer, serde: Serde +) -> bytes: + """Convert flat ThoughtspotAnswer to nested JSON bytes.""" + return serde.encode(_thoughtspot_answer_to_nested(thoughtspot_answer)) + + +def _thoughtspot_answer_from_nested_bytes( + data: bytes, serde: Serde +) -> ThoughtspotAnswer: + """Convert nested JSON bytes to flat ThoughtspotAnswer.""" + nested = serde.decode(data, ThoughtspotAnswerNested) + return _thoughtspot_answer_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +ThoughtspotAnswer.THOUGHTSPOT_CHART_TYPE = KeywordField( + "thoughtspotChartType", "thoughtspotChartType" +) +ThoughtspotAnswer.THOUGHTSPOT_QUESTION_TEXT = KeywordField( + "thoughtspotQuestionText", "thoughtspotQuestionText" +) +ThoughtspotAnswer.THOUGHTSPOT_JOIN_COUNT = NumericField( + "thoughtspotJoinCount", "thoughtspotJoinCount" +) +ThoughtspotAnswer.THOUGHTSPOT_COLUMN_COUNT = NumericField( + "thoughtspotColumnCount", "thoughtspotColumnCount" +) +ThoughtspotAnswer.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +ThoughtspotAnswer.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +ThoughtspotAnswer.ANOMALO_CHECKS = RelationField("anomaloChecks") +ThoughtspotAnswer.APPLICATION = RelationField("application") +ThoughtspotAnswer.APPLICATION_FIELD = RelationField("applicationField") +ThoughtspotAnswer.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +ThoughtspotAnswer.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +ThoughtspotAnswer.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +ThoughtspotAnswer.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +ThoughtspotAnswer.METRICS = RelationField("metrics") +ThoughtspotAnswer.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +ThoughtspotAnswer.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +ThoughtspotAnswer.MEANINGS = RelationField("meanings") +ThoughtspotAnswer.MC_MONITORS = RelationField("mcMonitors") +ThoughtspotAnswer.MC_INCIDENTS = RelationField("mcIncidents") +ThoughtspotAnswer.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +ThoughtspotAnswer.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +ThoughtspotAnswer.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +ThoughtspotAnswer.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +ThoughtspotAnswer.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +ThoughtspotAnswer.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +ThoughtspotAnswer.FILES = RelationField("files") +ThoughtspotAnswer.LINKS = RelationField("links") +ThoughtspotAnswer.README = RelationField("readme") +ThoughtspotAnswer.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +ThoughtspotAnswer.SODA_CHECKS = RelationField("sodaChecks") +ThoughtspotAnswer.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +ThoughtspotAnswer.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/thoughtspot_column.py b/pyatlan_v9/model/assets/thoughtspot_column.py new file mode 100644 index 000000000..3b413a6ef --- /dev/null +++ b/pyatlan_v9/model/assets/thoughtspot_column.py @@ -0,0 +1,705 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +ThoughtspotColumn asset model with flattened inheritance. + +This module provides: +- ThoughtspotColumn: Flat asset class (easy to use) +- ThoughtspotColumnAttributes: Nested attributes struct (extends AssetAttributes) +- ThoughtspotColumnNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .thoughtspot_related import ( + RelatedThoughtspotTable, + RelatedThoughtspotView, + RelatedThoughtspotWorksheet, +) + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class ThoughtspotColumn(Asset): + """ + Instance of a Thoughtspot column in Atlan. + """ + + THOUGHTSPOT_TABLE_QUALIFIED_NAME: ClassVar[Any] = None + THOUGHTSPOT_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + THOUGHTSPOT_WORKSHEET_QUALIFIED_NAME: ClassVar[Any] = None + THOUGHTSPOT_DATA_TYPE: ClassVar[Any] = None + THOUGHTSPOT_TYPE: ClassVar[Any] = None + THOUGHTSPOT_CHART_TYPE: ClassVar[Any] = None + THOUGHTSPOT_QUESTION_TEXT: ClassVar[Any] = None + THOUGHTSPOT_JOIN_COUNT: ClassVar[Any] = None + THOUGHTSPOT_COLUMN_COUNT: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + THOUGHTSPOT_TABLE: ClassVar[Any] = None + THOUGHTSPOT_VIEW: ClassVar[Any] = None + THOUGHTSPOT_WORKSHEET: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "ThoughtspotColumn" + + thoughtspot_table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this column exists.""" + + thoughtspot_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this column exists.""" + + thoughtspot_worksheet_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the worksheet in which this column exists.""" + + thoughtspot_data_type: Union[str, None, UnsetType] = UNSET + """Specifies the technical format of data stored in a column such as integer, float, string, date, boolean etc.""" + + thoughtspot_type: Union[str, None, UnsetType] = UNSET + """Defines the analytical role of a column in data analysis categorizing it as a dimension, measure, or attribute.""" + + thoughtspot_chart_type: Union[str, None, UnsetType] = UNSET + """""" + + thoughtspot_question_text: Union[str, None, UnsetType] = UNSET + """""" + + thoughtspot_join_count: Union[int, None, UnsetType] = UNSET + """Total number of data table joins executed for analysis.""" + + thoughtspot_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + thoughtspot_table: Union[RelatedThoughtspotTable, None, UnsetType] = UNSET + """Table in which this column exists.""" + + thoughtspot_view: Union[RelatedThoughtspotView, None, UnsetType] = UNSET + """View in which this column exists.""" + + thoughtspot_worksheet: Union[RelatedThoughtspotWorksheet, None, UnsetType] = UNSET + """Worksheet in which this column exists.""" + + def __post_init__(self) -> None: + self.type_name = "ThoughtspotColumn" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _thoughtspot_column_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> ThoughtspotColumn: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + ThoughtspotColumn instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _thoughtspot_column_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class ThoughtspotColumnAttributes(AssetAttributes): + """ThoughtspotColumn-specific attributes for nested API format.""" + + thoughtspot_table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this column exists.""" + + thoughtspot_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this column exists.""" + + thoughtspot_worksheet_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the worksheet in which this column exists.""" + + thoughtspot_data_type: Union[str, None, UnsetType] = UNSET + """Specifies the technical format of data stored in a column such as integer, float, string, date, boolean etc.""" + + thoughtspot_type: Union[str, None, UnsetType] = UNSET + """Defines the analytical role of a column in data analysis categorizing it as a dimension, measure, or attribute.""" + + thoughtspot_chart_type: Union[str, None, UnsetType] = UNSET + """""" + + thoughtspot_question_text: Union[str, None, UnsetType] = UNSET + """""" + + thoughtspot_join_count: Union[int, None, UnsetType] = UNSET + """Total number of data table joins executed for analysis.""" + + thoughtspot_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns.""" + + +class ThoughtspotColumnRelationshipAttributes(AssetRelationshipAttributes): + """ThoughtspotColumn-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + thoughtspot_table: Union[RelatedThoughtspotTable, None, UnsetType] = UNSET + """Table in which this column exists.""" + + thoughtspot_view: Union[RelatedThoughtspotView, None, UnsetType] = UNSET + """View in which this column exists.""" + + thoughtspot_worksheet: Union[RelatedThoughtspotWorksheet, None, UnsetType] = UNSET + """Worksheet in which this column exists.""" + + +class ThoughtspotColumnNested(AssetNested): + """ThoughtspotColumn in nested API format for high-performance serialization.""" + + attributes: Union[ThoughtspotColumnAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + ThoughtspotColumnRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + ThoughtspotColumnRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + ThoughtspotColumnRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_THOUGHTSPOT_COLUMN_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", + "thoughtspot_table", + "thoughtspot_view", + "thoughtspot_worksheet", +] + + +def _populate_thoughtspot_column_attrs( + attrs: ThoughtspotColumnAttributes, obj: ThoughtspotColumn +) -> None: + """Populate ThoughtspotColumn-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.thoughtspot_table_qualified_name = obj.thoughtspot_table_qualified_name + attrs.thoughtspot_view_qualified_name = obj.thoughtspot_view_qualified_name + attrs.thoughtspot_worksheet_qualified_name = ( + obj.thoughtspot_worksheet_qualified_name + ) + attrs.thoughtspot_data_type = obj.thoughtspot_data_type + attrs.thoughtspot_type = obj.thoughtspot_type + attrs.thoughtspot_chart_type = obj.thoughtspot_chart_type + attrs.thoughtspot_question_text = obj.thoughtspot_question_text + attrs.thoughtspot_join_count = obj.thoughtspot_join_count + attrs.thoughtspot_column_count = obj.thoughtspot_column_count + + +def _extract_thoughtspot_column_attrs(attrs: ThoughtspotColumnAttributes) -> dict: + """Extract all ThoughtspotColumn attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["thoughtspot_table_qualified_name"] = attrs.thoughtspot_table_qualified_name + result["thoughtspot_view_qualified_name"] = attrs.thoughtspot_view_qualified_name + result["thoughtspot_worksheet_qualified_name"] = ( + attrs.thoughtspot_worksheet_qualified_name + ) + result["thoughtspot_data_type"] = attrs.thoughtspot_data_type + result["thoughtspot_type"] = attrs.thoughtspot_type + result["thoughtspot_chart_type"] = attrs.thoughtspot_chart_type + result["thoughtspot_question_text"] = attrs.thoughtspot_question_text + result["thoughtspot_join_count"] = attrs.thoughtspot_join_count + result["thoughtspot_column_count"] = attrs.thoughtspot_column_count + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _thoughtspot_column_to_nested( + thoughtspot_column: ThoughtspotColumn, +) -> ThoughtspotColumnNested: + """Convert flat ThoughtspotColumn to nested format.""" + attrs = ThoughtspotColumnAttributes() + _populate_thoughtspot_column_attrs(attrs, thoughtspot_column) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + thoughtspot_column, + _THOUGHTSPOT_COLUMN_REL_FIELDS, + ThoughtspotColumnRelationshipAttributes, + ) + return ThoughtspotColumnNested( + guid=thoughtspot_column.guid, + type_name=thoughtspot_column.type_name, + status=thoughtspot_column.status, + version=thoughtspot_column.version, + create_time=thoughtspot_column.create_time, + update_time=thoughtspot_column.update_time, + created_by=thoughtspot_column.created_by, + updated_by=thoughtspot_column.updated_by, + classifications=thoughtspot_column.classifications, + classification_names=thoughtspot_column.classification_names, + meanings=thoughtspot_column.meanings, + labels=thoughtspot_column.labels, + business_attributes=thoughtspot_column.business_attributes, + custom_attributes=thoughtspot_column.custom_attributes, + pending_tasks=thoughtspot_column.pending_tasks, + proxy=thoughtspot_column.proxy, + is_incomplete=thoughtspot_column.is_incomplete, + provenance_type=thoughtspot_column.provenance_type, + home_id=thoughtspot_column.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _thoughtspot_column_from_nested( + nested: ThoughtspotColumnNested, +) -> ThoughtspotColumn: + """Convert nested format to flat ThoughtspotColumn.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else ThoughtspotColumnAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _THOUGHTSPOT_COLUMN_REL_FIELDS, + ThoughtspotColumnRelationshipAttributes, + ) + return ThoughtspotColumn( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_thoughtspot_column_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _thoughtspot_column_to_nested_bytes( + thoughtspot_column: ThoughtspotColumn, serde: Serde +) -> bytes: + """Convert flat ThoughtspotColumn to nested JSON bytes.""" + return serde.encode(_thoughtspot_column_to_nested(thoughtspot_column)) + + +def _thoughtspot_column_from_nested_bytes( + data: bytes, serde: Serde +) -> ThoughtspotColumn: + """Convert nested JSON bytes to flat ThoughtspotColumn.""" + nested = serde.decode(data, ThoughtspotColumnNested) + return _thoughtspot_column_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +ThoughtspotColumn.THOUGHTSPOT_TABLE_QUALIFIED_NAME = KeywordTextField( + "thoughtspotTableQualifiedName", + "thoughtspotTableQualifiedName", + "thoughtspotTableQualifiedName.text", +) +ThoughtspotColumn.THOUGHTSPOT_VIEW_QUALIFIED_NAME = KeywordTextField( + "thoughtspotViewQualifiedName", + "thoughtspotViewQualifiedName", + "thoughtspotViewQualifiedName.text", +) +ThoughtspotColumn.THOUGHTSPOT_WORKSHEET_QUALIFIED_NAME = KeywordTextField( + "thoughtspotWorksheetQualifiedName", + "thoughtspotWorksheetQualifiedName", + "thoughtspotWorksheetQualifiedName.text", +) +ThoughtspotColumn.THOUGHTSPOT_DATA_TYPE = KeywordField( + "thoughtspotDataType", "thoughtspotDataType" +) +ThoughtspotColumn.THOUGHTSPOT_TYPE = KeywordField("thoughtspotType", "thoughtspotType") +ThoughtspotColumn.THOUGHTSPOT_CHART_TYPE = KeywordField( + "thoughtspotChartType", "thoughtspotChartType" +) +ThoughtspotColumn.THOUGHTSPOT_QUESTION_TEXT = KeywordField( + "thoughtspotQuestionText", "thoughtspotQuestionText" +) +ThoughtspotColumn.THOUGHTSPOT_JOIN_COUNT = NumericField( + "thoughtspotJoinCount", "thoughtspotJoinCount" +) +ThoughtspotColumn.THOUGHTSPOT_COLUMN_COUNT = NumericField( + "thoughtspotColumnCount", "thoughtspotColumnCount" +) +ThoughtspotColumn.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +ThoughtspotColumn.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +ThoughtspotColumn.ANOMALO_CHECKS = RelationField("anomaloChecks") +ThoughtspotColumn.APPLICATION = RelationField("application") +ThoughtspotColumn.APPLICATION_FIELD = RelationField("applicationField") +ThoughtspotColumn.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +ThoughtspotColumn.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +ThoughtspotColumn.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +ThoughtspotColumn.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +ThoughtspotColumn.METRICS = RelationField("metrics") +ThoughtspotColumn.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +ThoughtspotColumn.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +ThoughtspotColumn.MEANINGS = RelationField("meanings") +ThoughtspotColumn.MC_MONITORS = RelationField("mcMonitors") +ThoughtspotColumn.MC_INCIDENTS = RelationField("mcIncidents") +ThoughtspotColumn.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +ThoughtspotColumn.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +ThoughtspotColumn.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +ThoughtspotColumn.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +ThoughtspotColumn.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +ThoughtspotColumn.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +ThoughtspotColumn.FILES = RelationField("files") +ThoughtspotColumn.LINKS = RelationField("links") +ThoughtspotColumn.README = RelationField("readme") +ThoughtspotColumn.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +ThoughtspotColumn.SODA_CHECKS = RelationField("sodaChecks") +ThoughtspotColumn.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +ThoughtspotColumn.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") +ThoughtspotColumn.THOUGHTSPOT_TABLE = RelationField("thoughtspotTable") +ThoughtspotColumn.THOUGHTSPOT_VIEW = RelationField("thoughtspotView") +ThoughtspotColumn.THOUGHTSPOT_WORKSHEET = RelationField("thoughtspotWorksheet") diff --git a/pyatlan_v9/model/assets/thoughtspot_dashlet.py b/pyatlan_v9/model/assets/thoughtspot_dashlet.py new file mode 100644 index 000000000..083f928ea --- /dev/null +++ b/pyatlan_v9/model/assets/thoughtspot_dashlet.py @@ -0,0 +1,649 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +ThoughtspotDashlet asset model with flattened inheritance. + +This module provides: +- ThoughtspotDashlet: Flat asset class (easy to use) +- ThoughtspotDashletAttributes: Nested attributes struct (extends AssetAttributes) +- ThoughtspotDashletNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .thoughtspot_related import RelatedThoughtspotLiveboard + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class ThoughtspotDashlet(Asset): + """ + Instance of a Thoughtspot dashlet in Atlan. + """ + + THOUGHTSPOT_LIVEBOARD_NAME: ClassVar[Any] = None + THOUGHTSPOT_LIVEBOARD_QUALIFIED_NAME: ClassVar[Any] = None + THOUGHTSPOT_CHART_TYPE: ClassVar[Any] = None + THOUGHTSPOT_QUESTION_TEXT: ClassVar[Any] = None + THOUGHTSPOT_JOIN_COUNT: ClassVar[Any] = None + THOUGHTSPOT_COLUMN_COUNT: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + THOUGHTSPOT_LIVEBOARD: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "ThoughtspotDashlet" + + thoughtspot_liveboard_name: Union[str, None, UnsetType] = UNSET + """Simple name of the liveboard in which this dashlet exists.""" + + thoughtspot_liveboard_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the liveboard in which this dashlet exists.""" + + thoughtspot_chart_type: Union[str, None, UnsetType] = UNSET + """""" + + thoughtspot_question_text: Union[str, None, UnsetType] = UNSET + """""" + + thoughtspot_join_count: Union[int, None, UnsetType] = UNSET + """Total number of data table joins executed for analysis.""" + + thoughtspot_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + thoughtspot_liveboard: Union[RelatedThoughtspotLiveboard, None, UnsetType] = UNSET + """Liveboard in which this dashlet exists.""" + + def __post_init__(self) -> None: + self.type_name = "ThoughtspotDashlet" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile(r"^.+/[^/]+/[^/]+$") + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _thoughtspot_dashlet_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> ThoughtspotDashlet: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + ThoughtspotDashlet instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _thoughtspot_dashlet_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class ThoughtspotDashletAttributes(AssetAttributes): + """ThoughtspotDashlet-specific attributes for nested API format.""" + + thoughtspot_liveboard_name: Union[str, None, UnsetType] = UNSET + """Simple name of the liveboard in which this dashlet exists.""" + + thoughtspot_liveboard_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the liveboard in which this dashlet exists.""" + + thoughtspot_chart_type: Union[str, None, UnsetType] = UNSET + """""" + + thoughtspot_question_text: Union[str, None, UnsetType] = UNSET + """""" + + thoughtspot_join_count: Union[int, None, UnsetType] = UNSET + """Total number of data table joins executed for analysis.""" + + thoughtspot_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns.""" + + +class ThoughtspotDashletRelationshipAttributes(AssetRelationshipAttributes): + """ThoughtspotDashlet-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + thoughtspot_liveboard: Union[RelatedThoughtspotLiveboard, None, UnsetType] = UNSET + """Liveboard in which this dashlet exists.""" + + +class ThoughtspotDashletNested(AssetNested): + """ThoughtspotDashlet in nested API format for high-performance serialization.""" + + attributes: Union[ThoughtspotDashletAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + ThoughtspotDashletRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + ThoughtspotDashletRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + ThoughtspotDashletRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_THOUGHTSPOT_DASHLET_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", + "thoughtspot_liveboard", +] + + +def _populate_thoughtspot_dashlet_attrs( + attrs: ThoughtspotDashletAttributes, obj: ThoughtspotDashlet +) -> None: + """Populate ThoughtspotDashlet-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.thoughtspot_liveboard_name = obj.thoughtspot_liveboard_name + attrs.thoughtspot_liveboard_qualified_name = ( + obj.thoughtspot_liveboard_qualified_name + ) + attrs.thoughtspot_chart_type = obj.thoughtspot_chart_type + attrs.thoughtspot_question_text = obj.thoughtspot_question_text + attrs.thoughtspot_join_count = obj.thoughtspot_join_count + attrs.thoughtspot_column_count = obj.thoughtspot_column_count + + +def _extract_thoughtspot_dashlet_attrs(attrs: ThoughtspotDashletAttributes) -> dict: + """Extract all ThoughtspotDashlet attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["thoughtspot_liveboard_name"] = attrs.thoughtspot_liveboard_name + result["thoughtspot_liveboard_qualified_name"] = ( + attrs.thoughtspot_liveboard_qualified_name + ) + result["thoughtspot_chart_type"] = attrs.thoughtspot_chart_type + result["thoughtspot_question_text"] = attrs.thoughtspot_question_text + result["thoughtspot_join_count"] = attrs.thoughtspot_join_count + result["thoughtspot_column_count"] = attrs.thoughtspot_column_count + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _thoughtspot_dashlet_to_nested( + thoughtspot_dashlet: ThoughtspotDashlet, +) -> ThoughtspotDashletNested: + """Convert flat ThoughtspotDashlet to nested format.""" + attrs = ThoughtspotDashletAttributes() + _populate_thoughtspot_dashlet_attrs(attrs, thoughtspot_dashlet) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + thoughtspot_dashlet, + _THOUGHTSPOT_DASHLET_REL_FIELDS, + ThoughtspotDashletRelationshipAttributes, + ) + return ThoughtspotDashletNested( + guid=thoughtspot_dashlet.guid, + type_name=thoughtspot_dashlet.type_name, + status=thoughtspot_dashlet.status, + version=thoughtspot_dashlet.version, + create_time=thoughtspot_dashlet.create_time, + update_time=thoughtspot_dashlet.update_time, + created_by=thoughtspot_dashlet.created_by, + updated_by=thoughtspot_dashlet.updated_by, + classifications=thoughtspot_dashlet.classifications, + classification_names=thoughtspot_dashlet.classification_names, + meanings=thoughtspot_dashlet.meanings, + labels=thoughtspot_dashlet.labels, + business_attributes=thoughtspot_dashlet.business_attributes, + custom_attributes=thoughtspot_dashlet.custom_attributes, + pending_tasks=thoughtspot_dashlet.pending_tasks, + proxy=thoughtspot_dashlet.proxy, + is_incomplete=thoughtspot_dashlet.is_incomplete, + provenance_type=thoughtspot_dashlet.provenance_type, + home_id=thoughtspot_dashlet.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _thoughtspot_dashlet_from_nested( + nested: ThoughtspotDashletNested, +) -> ThoughtspotDashlet: + """Convert nested format to flat ThoughtspotDashlet.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else ThoughtspotDashletAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _THOUGHTSPOT_DASHLET_REL_FIELDS, + ThoughtspotDashletRelationshipAttributes, + ) + return ThoughtspotDashlet( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_thoughtspot_dashlet_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _thoughtspot_dashlet_to_nested_bytes( + thoughtspot_dashlet: ThoughtspotDashlet, serde: Serde +) -> bytes: + """Convert flat ThoughtspotDashlet to nested JSON bytes.""" + return serde.encode(_thoughtspot_dashlet_to_nested(thoughtspot_dashlet)) + + +def _thoughtspot_dashlet_from_nested_bytes( + data: bytes, serde: Serde +) -> ThoughtspotDashlet: + """Convert nested JSON bytes to flat ThoughtspotDashlet.""" + nested = serde.decode(data, ThoughtspotDashletNested) + return _thoughtspot_dashlet_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + KeywordTextField, + NumericField, + RelationField, +) + +ThoughtspotDashlet.THOUGHTSPOT_LIVEBOARD_NAME = KeywordTextField( + "thoughtspotLiveboardName", + "thoughtspotLiveboardName", + "thoughtspotLiveboardName.text", +) +ThoughtspotDashlet.THOUGHTSPOT_LIVEBOARD_QUALIFIED_NAME = KeywordTextField( + "thoughtspotLiveboardQualifiedName", + "thoughtspotLiveboardQualifiedName", + "thoughtspotLiveboardQualifiedName.text", +) +ThoughtspotDashlet.THOUGHTSPOT_CHART_TYPE = KeywordField( + "thoughtspotChartType", "thoughtspotChartType" +) +ThoughtspotDashlet.THOUGHTSPOT_QUESTION_TEXT = KeywordField( + "thoughtspotQuestionText", "thoughtspotQuestionText" +) +ThoughtspotDashlet.THOUGHTSPOT_JOIN_COUNT = NumericField( + "thoughtspotJoinCount", "thoughtspotJoinCount" +) +ThoughtspotDashlet.THOUGHTSPOT_COLUMN_COUNT = NumericField( + "thoughtspotColumnCount", "thoughtspotColumnCount" +) +ThoughtspotDashlet.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +ThoughtspotDashlet.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +ThoughtspotDashlet.ANOMALO_CHECKS = RelationField("anomaloChecks") +ThoughtspotDashlet.APPLICATION = RelationField("application") +ThoughtspotDashlet.APPLICATION_FIELD = RelationField("applicationField") +ThoughtspotDashlet.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +ThoughtspotDashlet.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +ThoughtspotDashlet.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +ThoughtspotDashlet.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +ThoughtspotDashlet.METRICS = RelationField("metrics") +ThoughtspotDashlet.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +ThoughtspotDashlet.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +ThoughtspotDashlet.MEANINGS = RelationField("meanings") +ThoughtspotDashlet.MC_MONITORS = RelationField("mcMonitors") +ThoughtspotDashlet.MC_INCIDENTS = RelationField("mcIncidents") +ThoughtspotDashlet.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +ThoughtspotDashlet.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +ThoughtspotDashlet.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +ThoughtspotDashlet.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +ThoughtspotDashlet.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +ThoughtspotDashlet.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +ThoughtspotDashlet.FILES = RelationField("files") +ThoughtspotDashlet.LINKS = RelationField("links") +ThoughtspotDashlet.README = RelationField("readme") +ThoughtspotDashlet.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +ThoughtspotDashlet.SODA_CHECKS = RelationField("sodaChecks") +ThoughtspotDashlet.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +ThoughtspotDashlet.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") +ThoughtspotDashlet.THOUGHTSPOT_LIVEBOARD = RelationField("thoughtspotLiveboard") diff --git a/pyatlan_v9/model/assets/thoughtspot_liveboard.py b/pyatlan_v9/model/assets/thoughtspot_liveboard.py new file mode 100644 index 000000000..ea4721e5c --- /dev/null +++ b/pyatlan_v9/model/assets/thoughtspot_liveboard.py @@ -0,0 +1,617 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +ThoughtspotLiveboard asset model with flattened inheritance. + +This module provides: +- ThoughtspotLiveboard: Flat asset class (easy to use) +- ThoughtspotLiveboardAttributes: Nested attributes struct (extends AssetAttributes) +- ThoughtspotLiveboardNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .thoughtspot_related import RelatedThoughtspotDashlet + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class ThoughtspotLiveboard(Asset): + """ + Instance of a Thoughtspot liveboard in Atlan. + """ + + THOUGHTSPOT_CHART_TYPE: ClassVar[Any] = None + THOUGHTSPOT_QUESTION_TEXT: ClassVar[Any] = None + THOUGHTSPOT_JOIN_COUNT: ClassVar[Any] = None + THOUGHTSPOT_COLUMN_COUNT: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + THOUGHTSPOT_DASHLETS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "ThoughtspotLiveboard" + + thoughtspot_chart_type: Union[str, None, UnsetType] = UNSET + """""" + + thoughtspot_question_text: Union[str, None, UnsetType] = UNSET + """""" + + thoughtspot_join_count: Union[int, None, UnsetType] = UNSET + """Total number of data table joins executed for analysis.""" + + thoughtspot_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + thoughtspot_dashlets: Union[List[RelatedThoughtspotDashlet], None, UnsetType] = ( + UNSET + ) + """Dashlets that exist within this liveboard.""" + + def __post_init__(self) -> None: + self.type_name = "ThoughtspotLiveboard" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _thoughtspot_liveboard_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> ThoughtspotLiveboard: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + ThoughtspotLiveboard instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _thoughtspot_liveboard_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class ThoughtspotLiveboardAttributes(AssetAttributes): + """ThoughtspotLiveboard-specific attributes for nested API format.""" + + thoughtspot_chart_type: Union[str, None, UnsetType] = UNSET + """""" + + thoughtspot_question_text: Union[str, None, UnsetType] = UNSET + """""" + + thoughtspot_join_count: Union[int, None, UnsetType] = UNSET + """Total number of data table joins executed for analysis.""" + + thoughtspot_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns.""" + + +class ThoughtspotLiveboardRelationshipAttributes(AssetRelationshipAttributes): + """ThoughtspotLiveboard-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + thoughtspot_dashlets: Union[List[RelatedThoughtspotDashlet], None, UnsetType] = ( + UNSET + ) + """Dashlets that exist within this liveboard.""" + + +class ThoughtspotLiveboardNested(AssetNested): + """ThoughtspotLiveboard in nested API format for high-performance serialization.""" + + attributes: Union[ThoughtspotLiveboardAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + ThoughtspotLiveboardRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + ThoughtspotLiveboardRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + ThoughtspotLiveboardRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_THOUGHTSPOT_LIVEBOARD_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", + "thoughtspot_dashlets", +] + + +def _populate_thoughtspot_liveboard_attrs( + attrs: ThoughtspotLiveboardAttributes, obj: ThoughtspotLiveboard +) -> None: + """Populate ThoughtspotLiveboard-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.thoughtspot_chart_type = obj.thoughtspot_chart_type + attrs.thoughtspot_question_text = obj.thoughtspot_question_text + attrs.thoughtspot_join_count = obj.thoughtspot_join_count + attrs.thoughtspot_column_count = obj.thoughtspot_column_count + + +def _extract_thoughtspot_liveboard_attrs(attrs: ThoughtspotLiveboardAttributes) -> dict: + """Extract all ThoughtspotLiveboard attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["thoughtspot_chart_type"] = attrs.thoughtspot_chart_type + result["thoughtspot_question_text"] = attrs.thoughtspot_question_text + result["thoughtspot_join_count"] = attrs.thoughtspot_join_count + result["thoughtspot_column_count"] = attrs.thoughtspot_column_count + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _thoughtspot_liveboard_to_nested( + thoughtspot_liveboard: ThoughtspotLiveboard, +) -> ThoughtspotLiveboardNested: + """Convert flat ThoughtspotLiveboard to nested format.""" + attrs = ThoughtspotLiveboardAttributes() + _populate_thoughtspot_liveboard_attrs(attrs, thoughtspot_liveboard) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + thoughtspot_liveboard, + _THOUGHTSPOT_LIVEBOARD_REL_FIELDS, + ThoughtspotLiveboardRelationshipAttributes, + ) + return ThoughtspotLiveboardNested( + guid=thoughtspot_liveboard.guid, + type_name=thoughtspot_liveboard.type_name, + status=thoughtspot_liveboard.status, + version=thoughtspot_liveboard.version, + create_time=thoughtspot_liveboard.create_time, + update_time=thoughtspot_liveboard.update_time, + created_by=thoughtspot_liveboard.created_by, + updated_by=thoughtspot_liveboard.updated_by, + classifications=thoughtspot_liveboard.classifications, + classification_names=thoughtspot_liveboard.classification_names, + meanings=thoughtspot_liveboard.meanings, + labels=thoughtspot_liveboard.labels, + business_attributes=thoughtspot_liveboard.business_attributes, + custom_attributes=thoughtspot_liveboard.custom_attributes, + pending_tasks=thoughtspot_liveboard.pending_tasks, + proxy=thoughtspot_liveboard.proxy, + is_incomplete=thoughtspot_liveboard.is_incomplete, + provenance_type=thoughtspot_liveboard.provenance_type, + home_id=thoughtspot_liveboard.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _thoughtspot_liveboard_from_nested( + nested: ThoughtspotLiveboardNested, +) -> ThoughtspotLiveboard: + """Convert nested format to flat ThoughtspotLiveboard.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else ThoughtspotLiveboardAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _THOUGHTSPOT_LIVEBOARD_REL_FIELDS, + ThoughtspotLiveboardRelationshipAttributes, + ) + return ThoughtspotLiveboard( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_thoughtspot_liveboard_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _thoughtspot_liveboard_to_nested_bytes( + thoughtspot_liveboard: ThoughtspotLiveboard, serde: Serde +) -> bytes: + """Convert flat ThoughtspotLiveboard to nested JSON bytes.""" + return serde.encode(_thoughtspot_liveboard_to_nested(thoughtspot_liveboard)) + + +def _thoughtspot_liveboard_from_nested_bytes( + data: bytes, serde: Serde +) -> ThoughtspotLiveboard: + """Convert nested JSON bytes to flat ThoughtspotLiveboard.""" + nested = serde.decode(data, ThoughtspotLiveboardNested) + return _thoughtspot_liveboard_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +ThoughtspotLiveboard.THOUGHTSPOT_CHART_TYPE = KeywordField( + "thoughtspotChartType", "thoughtspotChartType" +) +ThoughtspotLiveboard.THOUGHTSPOT_QUESTION_TEXT = KeywordField( + "thoughtspotQuestionText", "thoughtspotQuestionText" +) +ThoughtspotLiveboard.THOUGHTSPOT_JOIN_COUNT = NumericField( + "thoughtspotJoinCount", "thoughtspotJoinCount" +) +ThoughtspotLiveboard.THOUGHTSPOT_COLUMN_COUNT = NumericField( + "thoughtspotColumnCount", "thoughtspotColumnCount" +) +ThoughtspotLiveboard.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +ThoughtspotLiveboard.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +ThoughtspotLiveboard.ANOMALO_CHECKS = RelationField("anomaloChecks") +ThoughtspotLiveboard.APPLICATION = RelationField("application") +ThoughtspotLiveboard.APPLICATION_FIELD = RelationField("applicationField") +ThoughtspotLiveboard.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +ThoughtspotLiveboard.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +ThoughtspotLiveboard.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +ThoughtspotLiveboard.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +ThoughtspotLiveboard.METRICS = RelationField("metrics") +ThoughtspotLiveboard.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +ThoughtspotLiveboard.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +ThoughtspotLiveboard.MEANINGS = RelationField("meanings") +ThoughtspotLiveboard.MC_MONITORS = RelationField("mcMonitors") +ThoughtspotLiveboard.MC_INCIDENTS = RelationField("mcIncidents") +ThoughtspotLiveboard.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +ThoughtspotLiveboard.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +ThoughtspotLiveboard.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +ThoughtspotLiveboard.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +ThoughtspotLiveboard.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +ThoughtspotLiveboard.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +ThoughtspotLiveboard.FILES = RelationField("files") +ThoughtspotLiveboard.LINKS = RelationField("links") +ThoughtspotLiveboard.README = RelationField("readme") +ThoughtspotLiveboard.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +ThoughtspotLiveboard.SODA_CHECKS = RelationField("sodaChecks") +ThoughtspotLiveboard.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +ThoughtspotLiveboard.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") +ThoughtspotLiveboard.THOUGHTSPOT_DASHLETS = RelationField("thoughtspotDashlets") diff --git a/pyatlan_v9/model/assets/thoughtspot_related.py b/pyatlan_v9/model/assets/thoughtspot_related.py new file mode 100644 index 000000000..c48b6705e --- /dev/null +++ b/pyatlan_v9/model/assets/thoughtspot_related.py @@ -0,0 +1,183 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Thoughtspot module. + +This module contains all Related{Type} classes for the Thoughtspot type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import Union + +from msgspec import UNSET, UnsetType + +from .catalog_related import RelatedBI +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedThoughtspot", + "RelatedThoughtspotAnswer", + "RelatedThoughtspotDashlet", + "RelatedThoughtspotLiveboard", + "RelatedThoughtspotTable", + "RelatedThoughtspotView", + "RelatedThoughtspotWorksheet", + "RelatedThoughtspotColumn", +] + + +class RelatedThoughtspot(RelatedBI): + """ + Related entity reference for Thoughtspot assets. + + Extends RelatedBI with Thoughtspot-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Thoughtspot" so it serializes correctly + + thoughtspot_chart_type: Union[str, None, UnsetType] = UNSET + """""" + + thoughtspot_question_text: Union[str, None, UnsetType] = UNSET + """""" + + thoughtspot_join_count: Union[int, None, UnsetType] = UNSET + """Total number of data table joins executed for analysis.""" + + thoughtspot_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Thoughtspot" + + +class RelatedThoughtspotAnswer(RelatedThoughtspot): + """ + Related entity reference for ThoughtspotAnswer assets. + + Extends RelatedThoughtspot with ThoughtspotAnswer-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "ThoughtspotAnswer" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "ThoughtspotAnswer" + + +class RelatedThoughtspotDashlet(RelatedThoughtspot): + """ + Related entity reference for ThoughtspotDashlet assets. + + Extends RelatedThoughtspot with ThoughtspotDashlet-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "ThoughtspotDashlet" so it serializes correctly + + thoughtspot_liveboard_name: Union[str, None, UnsetType] = UNSET + """Simple name of the liveboard in which this dashlet exists.""" + + thoughtspot_liveboard_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the liveboard in which this dashlet exists.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "ThoughtspotDashlet" + + +class RelatedThoughtspotLiveboard(RelatedThoughtspot): + """ + Related entity reference for ThoughtspotLiveboard assets. + + Extends RelatedThoughtspot with ThoughtspotLiveboard-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "ThoughtspotLiveboard" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "ThoughtspotLiveboard" + + +class RelatedThoughtspotTable(RelatedThoughtspot): + """ + Related entity reference for ThoughtspotTable assets. + + Extends RelatedThoughtspot with ThoughtspotTable-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "ThoughtspotTable" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "ThoughtspotTable" + + +class RelatedThoughtspotView(RelatedThoughtspot): + """ + Related entity reference for ThoughtspotView assets. + + Extends RelatedThoughtspot with ThoughtspotView-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "ThoughtspotView" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "ThoughtspotView" + + +class RelatedThoughtspotWorksheet(RelatedThoughtspot): + """ + Related entity reference for ThoughtspotWorksheet assets. + + Extends RelatedThoughtspot with ThoughtspotWorksheet-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "ThoughtspotWorksheet" so it serializes correctly + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "ThoughtspotWorksheet" + + +class RelatedThoughtspotColumn(RelatedThoughtspot): + """ + Related entity reference for ThoughtspotColumn assets. + + Extends RelatedThoughtspot with ThoughtspotColumn-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "ThoughtspotColumn" so it serializes correctly + + thoughtspot_table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this column exists.""" + + thoughtspot_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this column exists.""" + + thoughtspot_worksheet_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the worksheet in which this column exists.""" + + thoughtspot_data_type: Union[str, None, UnsetType] = UNSET + """Specifies the technical format of data stored in a column such as integer, float, string, date, boolean etc.""" + + thoughtspot_type: Union[str, None, UnsetType] = UNSET + """Defines the analytical role of a column in data analysis categorizing it as a dimension, measure, or attribute.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "ThoughtspotColumn" diff --git a/pyatlan_v9/model/assets/thoughtspot_table.py b/pyatlan_v9/model/assets/thoughtspot_table.py new file mode 100644 index 000000000..0f998fb1e --- /dev/null +++ b/pyatlan_v9/model/assets/thoughtspot_table.py @@ -0,0 +1,603 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +ThoughtspotTable asset model with flattened inheritance. + +This module provides: +- ThoughtspotTable: Flat asset class (easy to use) +- ThoughtspotTableAttributes: Nested attributes struct (extends AssetAttributes) +- ThoughtspotTableNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .thoughtspot_related import RelatedThoughtspotColumn + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class ThoughtspotTable(Asset): + """ + Instance of a Thoughtspot table in Atlan. + """ + + THOUGHTSPOT_CHART_TYPE: ClassVar[Any] = None + THOUGHTSPOT_QUESTION_TEXT: ClassVar[Any] = None + THOUGHTSPOT_JOIN_COUNT: ClassVar[Any] = None + THOUGHTSPOT_COLUMN_COUNT: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + THOUGHTSPOT_COLUMNS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "ThoughtspotTable" + + thoughtspot_chart_type: Union[str, None, UnsetType] = UNSET + """""" + + thoughtspot_question_text: Union[str, None, UnsetType] = UNSET + """""" + + thoughtspot_join_count: Union[int, None, UnsetType] = UNSET + """Total number of data table joins executed for analysis.""" + + thoughtspot_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + thoughtspot_columns: Union[List[RelatedThoughtspotColumn], None, UnsetType] = UNSET + """Columns that exist within this table.""" + + def __post_init__(self) -> None: + self.type_name = "ThoughtspotTable" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _thoughtspot_table_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> ThoughtspotTable: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + ThoughtspotTable instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _thoughtspot_table_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class ThoughtspotTableAttributes(AssetAttributes): + """ThoughtspotTable-specific attributes for nested API format.""" + + thoughtspot_chart_type: Union[str, None, UnsetType] = UNSET + """""" + + thoughtspot_question_text: Union[str, None, UnsetType] = UNSET + """""" + + thoughtspot_join_count: Union[int, None, UnsetType] = UNSET + """Total number of data table joins executed for analysis.""" + + thoughtspot_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns.""" + + +class ThoughtspotTableRelationshipAttributes(AssetRelationshipAttributes): + """ThoughtspotTable-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + thoughtspot_columns: Union[List[RelatedThoughtspotColumn], None, UnsetType] = UNSET + """Columns that exist within this table.""" + + +class ThoughtspotTableNested(AssetNested): + """ThoughtspotTable in nested API format for high-performance serialization.""" + + attributes: Union[ThoughtspotTableAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + ThoughtspotTableRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + ThoughtspotTableRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + ThoughtspotTableRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_THOUGHTSPOT_TABLE_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", + "thoughtspot_columns", +] + + +def _populate_thoughtspot_table_attrs( + attrs: ThoughtspotTableAttributes, obj: ThoughtspotTable +) -> None: + """Populate ThoughtspotTable-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.thoughtspot_chart_type = obj.thoughtspot_chart_type + attrs.thoughtspot_question_text = obj.thoughtspot_question_text + attrs.thoughtspot_join_count = obj.thoughtspot_join_count + attrs.thoughtspot_column_count = obj.thoughtspot_column_count + + +def _extract_thoughtspot_table_attrs(attrs: ThoughtspotTableAttributes) -> dict: + """Extract all ThoughtspotTable attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["thoughtspot_chart_type"] = attrs.thoughtspot_chart_type + result["thoughtspot_question_text"] = attrs.thoughtspot_question_text + result["thoughtspot_join_count"] = attrs.thoughtspot_join_count + result["thoughtspot_column_count"] = attrs.thoughtspot_column_count + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _thoughtspot_table_to_nested( + thoughtspot_table: ThoughtspotTable, +) -> ThoughtspotTableNested: + """Convert flat ThoughtspotTable to nested format.""" + attrs = ThoughtspotTableAttributes() + _populate_thoughtspot_table_attrs(attrs, thoughtspot_table) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + thoughtspot_table, + _THOUGHTSPOT_TABLE_REL_FIELDS, + ThoughtspotTableRelationshipAttributes, + ) + return ThoughtspotTableNested( + guid=thoughtspot_table.guid, + type_name=thoughtspot_table.type_name, + status=thoughtspot_table.status, + version=thoughtspot_table.version, + create_time=thoughtspot_table.create_time, + update_time=thoughtspot_table.update_time, + created_by=thoughtspot_table.created_by, + updated_by=thoughtspot_table.updated_by, + classifications=thoughtspot_table.classifications, + classification_names=thoughtspot_table.classification_names, + meanings=thoughtspot_table.meanings, + labels=thoughtspot_table.labels, + business_attributes=thoughtspot_table.business_attributes, + custom_attributes=thoughtspot_table.custom_attributes, + pending_tasks=thoughtspot_table.pending_tasks, + proxy=thoughtspot_table.proxy, + is_incomplete=thoughtspot_table.is_incomplete, + provenance_type=thoughtspot_table.provenance_type, + home_id=thoughtspot_table.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _thoughtspot_table_from_nested(nested: ThoughtspotTableNested) -> ThoughtspotTable: + """Convert nested format to flat ThoughtspotTable.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else ThoughtspotTableAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _THOUGHTSPOT_TABLE_REL_FIELDS, + ThoughtspotTableRelationshipAttributes, + ) + return ThoughtspotTable( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_thoughtspot_table_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _thoughtspot_table_to_nested_bytes( + thoughtspot_table: ThoughtspotTable, serde: Serde +) -> bytes: + """Convert flat ThoughtspotTable to nested JSON bytes.""" + return serde.encode(_thoughtspot_table_to_nested(thoughtspot_table)) + + +def _thoughtspot_table_from_nested_bytes(data: bytes, serde: Serde) -> ThoughtspotTable: + """Convert nested JSON bytes to flat ThoughtspotTable.""" + nested = serde.decode(data, ThoughtspotTableNested) + return _thoughtspot_table_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +ThoughtspotTable.THOUGHTSPOT_CHART_TYPE = KeywordField( + "thoughtspotChartType", "thoughtspotChartType" +) +ThoughtspotTable.THOUGHTSPOT_QUESTION_TEXT = KeywordField( + "thoughtspotQuestionText", "thoughtspotQuestionText" +) +ThoughtspotTable.THOUGHTSPOT_JOIN_COUNT = NumericField( + "thoughtspotJoinCount", "thoughtspotJoinCount" +) +ThoughtspotTable.THOUGHTSPOT_COLUMN_COUNT = NumericField( + "thoughtspotColumnCount", "thoughtspotColumnCount" +) +ThoughtspotTable.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +ThoughtspotTable.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +ThoughtspotTable.ANOMALO_CHECKS = RelationField("anomaloChecks") +ThoughtspotTable.APPLICATION = RelationField("application") +ThoughtspotTable.APPLICATION_FIELD = RelationField("applicationField") +ThoughtspotTable.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +ThoughtspotTable.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +ThoughtspotTable.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +ThoughtspotTable.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +ThoughtspotTable.METRICS = RelationField("metrics") +ThoughtspotTable.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +ThoughtspotTable.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +ThoughtspotTable.MEANINGS = RelationField("meanings") +ThoughtspotTable.MC_MONITORS = RelationField("mcMonitors") +ThoughtspotTable.MC_INCIDENTS = RelationField("mcIncidents") +ThoughtspotTable.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +ThoughtspotTable.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +ThoughtspotTable.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +ThoughtspotTable.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +ThoughtspotTable.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +ThoughtspotTable.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +ThoughtspotTable.FILES = RelationField("files") +ThoughtspotTable.LINKS = RelationField("links") +ThoughtspotTable.README = RelationField("readme") +ThoughtspotTable.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +ThoughtspotTable.SODA_CHECKS = RelationField("sodaChecks") +ThoughtspotTable.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +ThoughtspotTable.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") +ThoughtspotTable.THOUGHTSPOT_COLUMNS = RelationField("thoughtspotColumns") diff --git a/pyatlan_v9/model/assets/thoughtspot_view.py b/pyatlan_v9/model/assets/thoughtspot_view.py new file mode 100644 index 000000000..4de52b393 --- /dev/null +++ b/pyatlan_v9/model/assets/thoughtspot_view.py @@ -0,0 +1,603 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +ThoughtspotView asset model with flattened inheritance. + +This module provides: +- ThoughtspotView: Flat asset class (easy to use) +- ThoughtspotViewAttributes: Nested attributes struct (extends AssetAttributes) +- ThoughtspotViewNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .thoughtspot_related import RelatedThoughtspotColumn + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class ThoughtspotView(Asset): + """ + Instance of a Thoughtspot view in Atlan. + """ + + THOUGHTSPOT_CHART_TYPE: ClassVar[Any] = None + THOUGHTSPOT_QUESTION_TEXT: ClassVar[Any] = None + THOUGHTSPOT_JOIN_COUNT: ClassVar[Any] = None + THOUGHTSPOT_COLUMN_COUNT: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + THOUGHTSPOT_COLUMNS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "ThoughtspotView" + + thoughtspot_chart_type: Union[str, None, UnsetType] = UNSET + """""" + + thoughtspot_question_text: Union[str, None, UnsetType] = UNSET + """""" + + thoughtspot_join_count: Union[int, None, UnsetType] = UNSET + """Total number of data table joins executed for analysis.""" + + thoughtspot_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + thoughtspot_columns: Union[List[RelatedThoughtspotColumn], None, UnsetType] = UNSET + """Columns that exist within this view.""" + + def __post_init__(self) -> None: + self.type_name = "ThoughtspotView" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _thoughtspot_view_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> ThoughtspotView: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + ThoughtspotView instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _thoughtspot_view_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class ThoughtspotViewAttributes(AssetAttributes): + """ThoughtspotView-specific attributes for nested API format.""" + + thoughtspot_chart_type: Union[str, None, UnsetType] = UNSET + """""" + + thoughtspot_question_text: Union[str, None, UnsetType] = UNSET + """""" + + thoughtspot_join_count: Union[int, None, UnsetType] = UNSET + """Total number of data table joins executed for analysis.""" + + thoughtspot_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns.""" + + +class ThoughtspotViewRelationshipAttributes(AssetRelationshipAttributes): + """ThoughtspotView-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + thoughtspot_columns: Union[List[RelatedThoughtspotColumn], None, UnsetType] = UNSET + """Columns that exist within this view.""" + + +class ThoughtspotViewNested(AssetNested): + """ThoughtspotView in nested API format for high-performance serialization.""" + + attributes: Union[ThoughtspotViewAttributes, UnsetType] = UNSET + relationship_attributes: Union[ThoughtspotViewRelationshipAttributes, UnsetType] = ( + UNSET + ) + append_relationship_attributes: Union[ + ThoughtspotViewRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + ThoughtspotViewRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_THOUGHTSPOT_VIEW_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", + "thoughtspot_columns", +] + + +def _populate_thoughtspot_view_attrs( + attrs: ThoughtspotViewAttributes, obj: ThoughtspotView +) -> None: + """Populate ThoughtspotView-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.thoughtspot_chart_type = obj.thoughtspot_chart_type + attrs.thoughtspot_question_text = obj.thoughtspot_question_text + attrs.thoughtspot_join_count = obj.thoughtspot_join_count + attrs.thoughtspot_column_count = obj.thoughtspot_column_count + + +def _extract_thoughtspot_view_attrs(attrs: ThoughtspotViewAttributes) -> dict: + """Extract all ThoughtspotView attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["thoughtspot_chart_type"] = attrs.thoughtspot_chart_type + result["thoughtspot_question_text"] = attrs.thoughtspot_question_text + result["thoughtspot_join_count"] = attrs.thoughtspot_join_count + result["thoughtspot_column_count"] = attrs.thoughtspot_column_count + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _thoughtspot_view_to_nested( + thoughtspot_view: ThoughtspotView, +) -> ThoughtspotViewNested: + """Convert flat ThoughtspotView to nested format.""" + attrs = ThoughtspotViewAttributes() + _populate_thoughtspot_view_attrs(attrs, thoughtspot_view) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + thoughtspot_view, + _THOUGHTSPOT_VIEW_REL_FIELDS, + ThoughtspotViewRelationshipAttributes, + ) + return ThoughtspotViewNested( + guid=thoughtspot_view.guid, + type_name=thoughtspot_view.type_name, + status=thoughtspot_view.status, + version=thoughtspot_view.version, + create_time=thoughtspot_view.create_time, + update_time=thoughtspot_view.update_time, + created_by=thoughtspot_view.created_by, + updated_by=thoughtspot_view.updated_by, + classifications=thoughtspot_view.classifications, + classification_names=thoughtspot_view.classification_names, + meanings=thoughtspot_view.meanings, + labels=thoughtspot_view.labels, + business_attributes=thoughtspot_view.business_attributes, + custom_attributes=thoughtspot_view.custom_attributes, + pending_tasks=thoughtspot_view.pending_tasks, + proxy=thoughtspot_view.proxy, + is_incomplete=thoughtspot_view.is_incomplete, + provenance_type=thoughtspot_view.provenance_type, + home_id=thoughtspot_view.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _thoughtspot_view_from_nested(nested: ThoughtspotViewNested) -> ThoughtspotView: + """Convert nested format to flat ThoughtspotView.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else ThoughtspotViewAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _THOUGHTSPOT_VIEW_REL_FIELDS, + ThoughtspotViewRelationshipAttributes, + ) + return ThoughtspotView( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_thoughtspot_view_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _thoughtspot_view_to_nested_bytes( + thoughtspot_view: ThoughtspotView, serde: Serde +) -> bytes: + """Convert flat ThoughtspotView to nested JSON bytes.""" + return serde.encode(_thoughtspot_view_to_nested(thoughtspot_view)) + + +def _thoughtspot_view_from_nested_bytes(data: bytes, serde: Serde) -> ThoughtspotView: + """Convert nested JSON bytes to flat ThoughtspotView.""" + nested = serde.decode(data, ThoughtspotViewNested) + return _thoughtspot_view_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +ThoughtspotView.THOUGHTSPOT_CHART_TYPE = KeywordField( + "thoughtspotChartType", "thoughtspotChartType" +) +ThoughtspotView.THOUGHTSPOT_QUESTION_TEXT = KeywordField( + "thoughtspotQuestionText", "thoughtspotQuestionText" +) +ThoughtspotView.THOUGHTSPOT_JOIN_COUNT = NumericField( + "thoughtspotJoinCount", "thoughtspotJoinCount" +) +ThoughtspotView.THOUGHTSPOT_COLUMN_COUNT = NumericField( + "thoughtspotColumnCount", "thoughtspotColumnCount" +) +ThoughtspotView.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +ThoughtspotView.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +ThoughtspotView.ANOMALO_CHECKS = RelationField("anomaloChecks") +ThoughtspotView.APPLICATION = RelationField("application") +ThoughtspotView.APPLICATION_FIELD = RelationField("applicationField") +ThoughtspotView.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +ThoughtspotView.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +ThoughtspotView.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +ThoughtspotView.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +ThoughtspotView.METRICS = RelationField("metrics") +ThoughtspotView.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +ThoughtspotView.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +ThoughtspotView.MEANINGS = RelationField("meanings") +ThoughtspotView.MC_MONITORS = RelationField("mcMonitors") +ThoughtspotView.MC_INCIDENTS = RelationField("mcIncidents") +ThoughtspotView.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +ThoughtspotView.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +ThoughtspotView.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +ThoughtspotView.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +ThoughtspotView.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +ThoughtspotView.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +ThoughtspotView.FILES = RelationField("files") +ThoughtspotView.LINKS = RelationField("links") +ThoughtspotView.README = RelationField("readme") +ThoughtspotView.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +ThoughtspotView.SODA_CHECKS = RelationField("sodaChecks") +ThoughtspotView.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +ThoughtspotView.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") +ThoughtspotView.THOUGHTSPOT_COLUMNS = RelationField("thoughtspotColumns") diff --git a/pyatlan_v9/model/assets/thoughtspot_worksheet.py b/pyatlan_v9/model/assets/thoughtspot_worksheet.py new file mode 100644 index 000000000..dc439a671 --- /dev/null +++ b/pyatlan_v9/model/assets/thoughtspot_worksheet.py @@ -0,0 +1,613 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +ThoughtspotWorksheet asset model with flattened inheritance. + +This module provides: +- ThoughtspotWorksheet: Flat asset class (easy to use) +- ThoughtspotWorksheetAttributes: Nested attributes struct (extends AssetAttributes) +- ThoughtspotWorksheetNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +from .thoughtspot_related import RelatedThoughtspotColumn + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class ThoughtspotWorksheet(Asset): + """ + Instance of a Thoughtspot worksheet in Atlan. + """ + + THOUGHTSPOT_CHART_TYPE: ClassVar[Any] = None + THOUGHTSPOT_QUESTION_TEXT: ClassVar[Any] = None + THOUGHTSPOT_JOIN_COUNT: ClassVar[Any] = None + THOUGHTSPOT_COLUMN_COUNT: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + THOUGHTSPOT_COLUMNS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "ThoughtspotWorksheet" + + thoughtspot_chart_type: Union[str, None, UnsetType] = UNSET + """""" + + thoughtspot_question_text: Union[str, None, UnsetType] = UNSET + """""" + + thoughtspot_join_count: Union[int, None, UnsetType] = UNSET + """Total number of data table joins executed for analysis.""" + + thoughtspot_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + thoughtspot_columns: Union[List[RelatedThoughtspotColumn], None, UnsetType] = UNSET + """Columns that exist within this worksheet.""" + + def __post_init__(self) -> None: + self.type_name = "ThoughtspotWorksheet" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _thoughtspot_worksheet_to_nested_bytes(self, serde) + + @staticmethod + def from_json( + json_data: str | bytes, serde: Serde | None = None + ) -> ThoughtspotWorksheet: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + ThoughtspotWorksheet instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _thoughtspot_worksheet_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class ThoughtspotWorksheetAttributes(AssetAttributes): + """ThoughtspotWorksheet-specific attributes for nested API format.""" + + thoughtspot_chart_type: Union[str, None, UnsetType] = UNSET + """""" + + thoughtspot_question_text: Union[str, None, UnsetType] = UNSET + """""" + + thoughtspot_join_count: Union[int, None, UnsetType] = UNSET + """Total number of data table joins executed for analysis.""" + + thoughtspot_column_count: Union[int, None, UnsetType] = UNSET + """Number of columns.""" + + +class ThoughtspotWorksheetRelationshipAttributes(AssetRelationshipAttributes): + """ThoughtspotWorksheet-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + thoughtspot_columns: Union[List[RelatedThoughtspotColumn], None, UnsetType] = UNSET + """Columns that exist within this worksheet.""" + + +class ThoughtspotWorksheetNested(AssetNested): + """ThoughtspotWorksheet in nested API format for high-performance serialization.""" + + attributes: Union[ThoughtspotWorksheetAttributes, UnsetType] = UNSET + relationship_attributes: Union[ + ThoughtspotWorksheetRelationshipAttributes, UnsetType + ] = UNSET + append_relationship_attributes: Union[ + ThoughtspotWorksheetRelationshipAttributes, UnsetType + ] = UNSET + remove_relationship_attributes: Union[ + ThoughtspotWorksheetRelationshipAttributes, UnsetType + ] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_THOUGHTSPOT_WORKSHEET_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", + "thoughtspot_columns", +] + + +def _populate_thoughtspot_worksheet_attrs( + attrs: ThoughtspotWorksheetAttributes, obj: ThoughtspotWorksheet +) -> None: + """Populate ThoughtspotWorksheet-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.thoughtspot_chart_type = obj.thoughtspot_chart_type + attrs.thoughtspot_question_text = obj.thoughtspot_question_text + attrs.thoughtspot_join_count = obj.thoughtspot_join_count + attrs.thoughtspot_column_count = obj.thoughtspot_column_count + + +def _extract_thoughtspot_worksheet_attrs(attrs: ThoughtspotWorksheetAttributes) -> dict: + """Extract all ThoughtspotWorksheet attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["thoughtspot_chart_type"] = attrs.thoughtspot_chart_type + result["thoughtspot_question_text"] = attrs.thoughtspot_question_text + result["thoughtspot_join_count"] = attrs.thoughtspot_join_count + result["thoughtspot_column_count"] = attrs.thoughtspot_column_count + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _thoughtspot_worksheet_to_nested( + thoughtspot_worksheet: ThoughtspotWorksheet, +) -> ThoughtspotWorksheetNested: + """Convert flat ThoughtspotWorksheet to nested format.""" + attrs = ThoughtspotWorksheetAttributes() + _populate_thoughtspot_worksheet_attrs(attrs, thoughtspot_worksheet) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + thoughtspot_worksheet, + _THOUGHTSPOT_WORKSHEET_REL_FIELDS, + ThoughtspotWorksheetRelationshipAttributes, + ) + return ThoughtspotWorksheetNested( + guid=thoughtspot_worksheet.guid, + type_name=thoughtspot_worksheet.type_name, + status=thoughtspot_worksheet.status, + version=thoughtspot_worksheet.version, + create_time=thoughtspot_worksheet.create_time, + update_time=thoughtspot_worksheet.update_time, + created_by=thoughtspot_worksheet.created_by, + updated_by=thoughtspot_worksheet.updated_by, + classifications=thoughtspot_worksheet.classifications, + classification_names=thoughtspot_worksheet.classification_names, + meanings=thoughtspot_worksheet.meanings, + labels=thoughtspot_worksheet.labels, + business_attributes=thoughtspot_worksheet.business_attributes, + custom_attributes=thoughtspot_worksheet.custom_attributes, + pending_tasks=thoughtspot_worksheet.pending_tasks, + proxy=thoughtspot_worksheet.proxy, + is_incomplete=thoughtspot_worksheet.is_incomplete, + provenance_type=thoughtspot_worksheet.provenance_type, + home_id=thoughtspot_worksheet.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _thoughtspot_worksheet_from_nested( + nested: ThoughtspotWorksheetNested, +) -> ThoughtspotWorksheet: + """Convert nested format to flat ThoughtspotWorksheet.""" + attrs = ( + nested.attributes + if nested.attributes is not UNSET + else ThoughtspotWorksheetAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _THOUGHTSPOT_WORKSHEET_REL_FIELDS, + ThoughtspotWorksheetRelationshipAttributes, + ) + return ThoughtspotWorksheet( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_thoughtspot_worksheet_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _thoughtspot_worksheet_to_nested_bytes( + thoughtspot_worksheet: ThoughtspotWorksheet, serde: Serde +) -> bytes: + """Convert flat ThoughtspotWorksheet to nested JSON bytes.""" + return serde.encode(_thoughtspot_worksheet_to_nested(thoughtspot_worksheet)) + + +def _thoughtspot_worksheet_from_nested_bytes( + data: bytes, serde: Serde +) -> ThoughtspotWorksheet: + """Convert nested JSON bytes to flat ThoughtspotWorksheet.""" + nested = serde.decode(data, ThoughtspotWorksheetNested) + return _thoughtspot_worksheet_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +ThoughtspotWorksheet.THOUGHTSPOT_CHART_TYPE = KeywordField( + "thoughtspotChartType", "thoughtspotChartType" +) +ThoughtspotWorksheet.THOUGHTSPOT_QUESTION_TEXT = KeywordField( + "thoughtspotQuestionText", "thoughtspotQuestionText" +) +ThoughtspotWorksheet.THOUGHTSPOT_JOIN_COUNT = NumericField( + "thoughtspotJoinCount", "thoughtspotJoinCount" +) +ThoughtspotWorksheet.THOUGHTSPOT_COLUMN_COUNT = NumericField( + "thoughtspotColumnCount", "thoughtspotColumnCount" +) +ThoughtspotWorksheet.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +ThoughtspotWorksheet.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +ThoughtspotWorksheet.ANOMALO_CHECKS = RelationField("anomaloChecks") +ThoughtspotWorksheet.APPLICATION = RelationField("application") +ThoughtspotWorksheet.APPLICATION_FIELD = RelationField("applicationField") +ThoughtspotWorksheet.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +ThoughtspotWorksheet.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +ThoughtspotWorksheet.MODEL_IMPLEMENTED_ENTITIES = RelationField( + "modelImplementedEntities" +) +ThoughtspotWorksheet.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField( + "modelImplementedAttributes" +) +ThoughtspotWorksheet.METRICS = RelationField("metrics") +ThoughtspotWorksheet.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +ThoughtspotWorksheet.DQ_REFERENCE_DATASET_RULES = RelationField( + "dqReferenceDatasetRules" +) +ThoughtspotWorksheet.MEANINGS = RelationField("meanings") +ThoughtspotWorksheet.MC_MONITORS = RelationField("mcMonitors") +ThoughtspotWorksheet.MC_INCIDENTS = RelationField("mcIncidents") +ThoughtspotWorksheet.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +ThoughtspotWorksheet.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +ThoughtspotWorksheet.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +ThoughtspotWorksheet.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +ThoughtspotWorksheet.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +ThoughtspotWorksheet.USER_DEF_RELATIONSHIP_FROM = RelationField( + "userDefRelationshipFrom" +) +ThoughtspotWorksheet.FILES = RelationField("files") +ThoughtspotWorksheet.LINKS = RelationField("links") +ThoughtspotWorksheet.README = RelationField("readme") +ThoughtspotWorksheet.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +ThoughtspotWorksheet.SODA_CHECKS = RelationField("sodaChecks") +ThoughtspotWorksheet.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +ThoughtspotWorksheet.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") +ThoughtspotWorksheet.THOUGHTSPOT_COLUMNS = RelationField("thoughtspotColumns") diff --git a/pyatlan_v9/model/assets/view.py b/pyatlan_v9/model/assets/view.py new file mode 100644 index 000000000..fbfbba4d4 --- /dev/null +++ b/pyatlan_v9/model/assets/view.py @@ -0,0 +1,1013 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +View asset model with flattened inheritance. + +This module provides: +- View: Flat asset class (easy to use) +- ViewAttributes: Nested attributes struct (extends AssetAttributes) +- ViewNested: Nested API format struct +""" + +from __future__ import annotations + +import re +from typing import Any, ClassVar, Dict, List, Union + +import msgspec +from msgspec import UNSET, UnsetType + +from .airflow_related import RelatedAirflowTask +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .dbt_related import ( + RelatedDbtModel, + RelatedDbtSeed, + RelatedDbtSource, + RelatedDbtTest, +) +from .gtc_related import RelatedAtlasGlossaryTerm +from .model_related import RelatedModelAttribute, RelatedModelEntity +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .partial_related import RelatedPartialField, RelatedPartialObject +from .process_related import RelatedProcess +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .snowflake_related import RelatedSnowflakeSemanticLogicalTable +from .soda_related import RelatedSodaCheck +from .spark_related import RelatedSparkJob +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset +from pyatlan_v9.utils import init_guid, validate_required_fields + +from .sql_related import RelatedColumn, RelatedQuery, RelatedSchema + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class View(Asset): + """ + Instance of a database view in Atlan. + """ + + COLUMN_COUNT: ClassVar[Any] = None + ROW_COUNT: ClassVar[Any] = None + SIZE_BYTES: ClassVar[Any] = None + IS_QUERY_PREVIEW: ClassVar[Any] = None + QUERY_PREVIEW_CONFIG: ClassVar[Any] = None + ALIAS: ClassVar[Any] = None + IS_TEMPORARY: ClassVar[Any] = None + DEFINITION: ClassVar[Any] = None + QUERY_COUNT: ClassVar[Any] = None + QUERY_USER_COUNT: ClassVar[Any] = None + QUERY_USER_MAP: ClassVar[Any] = None + QUERY_COUNT_UPDATED_AT: ClassVar[Any] = None + DATABASE_NAME: ClassVar[Any] = None + DATABASE_QUALIFIED_NAME: ClassVar[Any] = None + SCHEMA_NAME: ClassVar[Any] = None + SCHEMA_QUALIFIED_NAME: ClassVar[Any] = None + TABLE_NAME: ClassVar[Any] = None + TABLE_QUALIFIED_NAME: ClassVar[Any] = None + VIEW_NAME: ClassVar[Any] = None + VIEW_QUALIFIED_NAME: ClassVar[Any] = None + CALCULATION_VIEW_NAME: ClassVar[Any] = None + CALCULATION_VIEW_QUALIFIED_NAME: ClassVar[Any] = None + IS_PROFILED: ClassVar[Any] = None + LAST_PROFILED_AT: ClassVar[Any] = None + SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME: ClassVar[Any] = None + SQL_IS_SECURE: ClassVar[Any] = None + INPUT_TO_AIRFLOW_TASKS: ClassVar[Any] = None + OUTPUT_FROM_AIRFLOW_TASKS: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + MODEL_IMPLEMENTED_ENTITIES: ClassVar[Any] = None + MODEL_IMPLEMENTED_ATTRIBUTES: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + DBT_MODELS: ClassVar[Any] = None + SQL_DBT_MODELS: ClassVar[Any] = None + DBT_TESTS: ClassVar[Any] = None + DBT_SOURCES: ClassVar[Any] = None + SQL_DBT_SOURCES: ClassVar[Any] = None + DBT_SEED_ASSETS: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + PARTIAL_CHILD_FIELDS: ClassVar[Any] = None + PARTIAL_CHILD_OBJECTS: ClassVar[Any] = None + INPUT_TO_PROCESSES: ClassVar[Any] = None + OUTPUT_FROM_PROCESSES: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + COLUMNS: ClassVar[Any] = None + QUERIES: ClassVar[Any] = None + ATLAN_SCHEMA: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SNOWFLAKE_SEMANTIC_LOGICAL_TABLES: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + INPUT_TO_SPARK_JOBS: ClassVar[Any] = None + OUTPUT_FROM_SPARK_JOBS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "View" + + column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this view.""" + + row_count: Union[int, None, UnsetType] = UNSET + """Number of rows in this view.""" + + size_bytes: Union[int, None, UnsetType] = UNSET + """Size of this view, in bytes.""" + + is_query_preview: Union[bool, None, UnsetType] = UNSET + """Whether preview queries are allowed on this view (true) or not (false).""" + + query_preview_config: Union[Dict[str, str], None, UnsetType] = UNSET + """Configuration for preview queries on this view.""" + + alias: Union[str, None, UnsetType] = UNSET + """Alias for this view.""" + + is_temporary: Union[bool, None, UnsetType] = UNSET + """Whether this view is temporary (true) or not (false).""" + + definition: Union[str, None, UnsetType] = UNSET + """SQL definition of this view.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Columns that exist within this view.""" + + queries: Union[List[RelatedQuery], None, UnsetType] = UNSET + """Queries that access this view.""" + + atlan_schema: Union[RelatedSchema, None, UnsetType] = UNSET + """Schema in which this view exists.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "View" + + # ========================================================================= + # SDK Methods + # ========================================================================= + + _QUALIFIED_NAME_PATTERN: ClassVar[re.Pattern] = re.compile( + r"^.+/[^/]+/[^/]+/[^/]+$" + ) + + @classmethod + @init_guid + def creator( + cls, + *, + name: str, + schema_qualified_name: str, + schema_name: str | None = None, + database_name: str | None = None, + database_qualified_name: str | None = None, + connection_qualified_name: str | None = None, + ) -> "View": + """ + Create a new View asset with auto-derived fields. + + Args: + name: Simple name of the view + schema_qualified_name: Unique name of the schema in which this view exists + schema_name: Simple name of the schema (auto-derived if not provided) + database_name: Simple name of the database (auto-derived if not provided) + database_qualified_name: Unique name of the database (auto-derived if not provided) + connection_qualified_name: Unique name of the connection (auto-derived if not provided) + + Returns: + New View instance with all fields populated + + Raises: + ValueError: If required parameters are missing or invalid + """ + validate_required_fields( + ["name", "schema_qualified_name"], [name, schema_qualified_name] + ) + + # Validate schema_qualified_name format: default/connector/connection_id/database/schema + fields = schema_qualified_name.split("/") + if len(fields) != 5: + raise ValueError( + f"Invalid schema_qualified_name: {schema_qualified_name}. " + "Expected format: default/connector/connection_id/database/schema" + ) + + # Derive other fields from schema_qualified_name + connector_name = fields[1] + connection_qn = ( + connection_qualified_name or f"{fields[0]}/{fields[1]}/{fields[2]}" + ) + db_name = database_name or fields[3] + sch_name = schema_name or fields[4] + db_qualified_name = database_qualified_name or f"{connection_qn}/{db_name}" + qualified_name = f"{schema_qualified_name}/{name}" + + return cls( + name=name, + qualified_name=qualified_name, + database_name=db_name, + database_qualified_name=db_qualified_name, + schema_name=sch_name, + schema_qualified_name=schema_qualified_name, + connector_name=connector_name, + connection_qualified_name=connection_qn, + atlan_schema=RelatedSchema(qualified_name=schema_qualified_name), + ) + + @classmethod + def updater(cls, *, qualified_name: str, name: str) -> "View": + """ + Create a View instance for updating an existing asset. + + Args: + qualified_name: Unique name of the view to update + name: Simple name of the view + + Returns: + View instance configured for updates + + Raises: + ValueError: If required parameters are missing + """ + validate_required_fields(["qualified_name", "name"], [qualified_name, name]) + return cls(qualified_name=qualified_name, name=name) + + def trim_to_required(self) -> "View": + """ + Return a View with only required fields for reference. + + Returns: + View instance with only qualified_name and name set + """ + return View(qualified_name=self.qualified_name, name=self.name) + + @classmethod + def create(cls, **kwargs) -> "View": + """Backward compatibility alias for creator().""" + return cls.creator(**kwargs) + + @classmethod + def create_for_modification(cls, **kwargs) -> "View": + """Backward compatibility alias for updater().""" + return cls.updater(**kwargs) + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _view_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> View: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + View instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _view_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class ViewAttributes(AssetAttributes): + """View-specific attributes for nested API format.""" + + column_count: Union[int, None, UnsetType] = UNSET + """Number of columns in this view.""" + + row_count: Union[int, None, UnsetType] = UNSET + """Number of rows in this view.""" + + size_bytes: Union[int, None, UnsetType] = UNSET + """Size of this view, in bytes.""" + + is_query_preview: Union[bool, None, UnsetType] = UNSET + """Whether preview queries are allowed on this view (true) or not (false).""" + + query_preview_config: Union[Dict[str, str], None, UnsetType] = UNSET + """Configuration for preview queries on this view.""" + + alias: Union[str, None, UnsetType] = UNSET + """Alias for this view.""" + + is_temporary: Union[bool, None, UnsetType] = UNSET + """Whether this view is temporary (true) or not (false).""" + + definition: Union[str, None, UnsetType] = UNSET + """SQL definition of this view.""" + + query_count: Union[int, None, UnsetType] = UNSET + """Number of times this asset has been queried.""" + + query_user_count: Union[int, None, UnsetType] = UNSET + """Number of unique users who have queried this asset.""" + + query_user_map: Union[Dict[str, int], None, UnsetType] = UNSET + """Map of unique users who have queried this asset to the number of times they have queried it.""" + + query_count_updated_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which the query count was last updated, in milliseconds.""" + + database_name: Union[str, None, UnsetType] = UNSET + """Simple name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + database_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the database in which this SQL asset exists, or empty if it does not exist within a database.""" + + schema_name: Union[str, None, UnsetType] = UNSET + """Simple name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + schema_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the schema in which this SQL asset exists, or empty if it does not exist within a schema.""" + + table_name: Union[str, None, UnsetType] = UNSET + """Simple name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + table_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the table in which this SQL asset exists, or empty if it does not exist within a table.""" + + view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the view in which this SQL asset exists, or empty if it does not exist within a view.""" + + calculation_view_name: Union[str, None, UnsetType] = UNSET + """Simple name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + calculation_view_qualified_name: Union[str, None, UnsetType] = UNSET + """Unique name of the calculation view in which this SQL asset exists, or empty if it does not exist within a calculation view.""" + + is_profiled: Union[bool, None, UnsetType] = UNSET + """Whether this asset has been profiled (true) or not (false).""" + + last_profiled_at: Union[int, None, UnsetType] = UNSET + """Time (epoch) at which this asset was last profiled, in milliseconds.""" + + sql_ai_model_context_qualified_name: Union[str, None, UnsetType] = msgspec.field( + default=UNSET, name="sqlAIModelContextQualifiedName" + ) + """Unique name of the context in which the model versions exist, or empty if it does not exist within an AI model context.""" + + sql_is_secure: Union[bool, None, UnsetType] = UNSET + """Whether this asset is secure (true) or not (false).""" + + +class ViewRelationshipAttributes(AssetRelationshipAttributes): + """View-specific relationship attributes for nested API format.""" + + input_to_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks to which this asset provides input.""" + + output_from_airflow_tasks: Union[List[RelatedAirflowTask], None, UnsetType] = UNSET + """Tasks from which this asset is output.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + model_implemented_entities: Union[List[RelatedModelEntity], None, UnsetType] = UNSET + """Entities implemented by this asset.""" + + model_implemented_attributes: Union[ + List[RelatedModelAttribute], None, UnsetType + ] = UNSET + """Attributes implemented by this asset.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """(Deprecated) Model containing the assets.""" + + sql_dbt_models: Union[List[RelatedDbtModel], None, UnsetType] = UNSET + """Assets related to the model.""" + + dbt_tests: Union[List[RelatedDbtTest], None, UnsetType] = UNSET + """Tests related to this asset.""" + + dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = UNSET + """Source containing the assets.""" + + sql_dbt_sources: Union[List[RelatedDbtSource], None, UnsetType] = msgspec.field( + default=UNSET, name="sqlDBTSources" + ) + """Sources related to this asset.""" + + dbt_seed_assets: Union[List[RelatedDbtSeed], None, UnsetType] = UNSET + """DBT seeds that materialize the SQL asset.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + partial_child_fields: Union[List[RelatedPartialField], None, UnsetType] = UNSET + """Partial fields contained in the asset.""" + + partial_child_objects: Union[List[RelatedPartialObject], None, UnsetType] = UNSET + """Partial objects contained in the asset.""" + + input_to_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes to which this asset provides input.""" + + output_from_processes: Union[List[RelatedProcess], None, UnsetType] = UNSET + """Processes from which this asset is produced as output.""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + columns: Union[List[RelatedColumn], None, UnsetType] = UNSET + """Columns that exist within this view.""" + + queries: Union[List[RelatedQuery], None, UnsetType] = UNSET + """Queries that access this view.""" + + atlan_schema: Union[RelatedSchema, None, UnsetType] = UNSET + """Schema in which this view exists.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + snowflake_semantic_logical_tables: Union[ + List[RelatedSnowflakeSemanticLogicalTable], None, UnsetType + ] = UNSET + """Semantic logical tables that reference this physical table or view.""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + input_to_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + output_from_spark_jobs: Union[List[RelatedSparkJob], None, UnsetType] = UNSET + """""" + + +class ViewNested(AssetNested): + """View in nested API format for high-performance serialization.""" + + attributes: Union[ViewAttributes, UnsetType] = UNSET + relationship_attributes: Union[ViewRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[ViewRelationshipAttributes, UnsetType] = UNSET + remove_relationship_attributes: Union[ViewRelationshipAttributes, UnsetType] = UNSET + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_VIEW_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "input_to_airflow_tasks", + "output_from_airflow_tasks", + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "model_implemented_entities", + "model_implemented_attributes", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "dbt_models", + "sql_dbt_models", + "dbt_tests", + "dbt_sources", + "sql_dbt_sources", + "dbt_seed_assets", + "meanings", + "mc_monitors", + "mc_incidents", + "partial_child_fields", + "partial_child_objects", + "input_to_processes", + "output_from_processes", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "columns", + "queries", + "atlan_schema", + "schema_registry_subjects", + "snowflake_semantic_logical_tables", + "soda_checks", + "input_to_spark_jobs", + "output_from_spark_jobs", +] + + +def _populate_view_attrs(attrs: ViewAttributes, obj: View) -> None: + """Populate View-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.column_count = obj.column_count + attrs.row_count = obj.row_count + attrs.size_bytes = obj.size_bytes + attrs.is_query_preview = obj.is_query_preview + attrs.query_preview_config = obj.query_preview_config + attrs.alias = obj.alias + attrs.is_temporary = obj.is_temporary + attrs.definition = obj.definition + attrs.query_count = obj.query_count + attrs.query_user_count = obj.query_user_count + attrs.query_user_map = obj.query_user_map + attrs.query_count_updated_at = obj.query_count_updated_at + attrs.database_name = obj.database_name + attrs.database_qualified_name = obj.database_qualified_name + attrs.schema_name = obj.schema_name + attrs.schema_qualified_name = obj.schema_qualified_name + attrs.table_name = obj.table_name + attrs.table_qualified_name = obj.table_qualified_name + attrs.view_name = obj.view_name + attrs.view_qualified_name = obj.view_qualified_name + attrs.calculation_view_name = obj.calculation_view_name + attrs.calculation_view_qualified_name = obj.calculation_view_qualified_name + attrs.is_profiled = obj.is_profiled + attrs.last_profiled_at = obj.last_profiled_at + attrs.sql_ai_model_context_qualified_name = obj.sql_ai_model_context_qualified_name + attrs.sql_is_secure = obj.sql_is_secure + + +def _extract_view_attrs(attrs: ViewAttributes) -> dict: + """Extract all View attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["column_count"] = attrs.column_count + result["row_count"] = attrs.row_count + result["size_bytes"] = attrs.size_bytes + result["is_query_preview"] = attrs.is_query_preview + result["query_preview_config"] = attrs.query_preview_config + result["alias"] = attrs.alias + result["is_temporary"] = attrs.is_temporary + result["definition"] = attrs.definition + result["query_count"] = attrs.query_count + result["query_user_count"] = attrs.query_user_count + result["query_user_map"] = attrs.query_user_map + result["query_count_updated_at"] = attrs.query_count_updated_at + result["database_name"] = attrs.database_name + result["database_qualified_name"] = attrs.database_qualified_name + result["schema_name"] = attrs.schema_name + result["schema_qualified_name"] = attrs.schema_qualified_name + result["table_name"] = attrs.table_name + result["table_qualified_name"] = attrs.table_qualified_name + result["view_name"] = attrs.view_name + result["view_qualified_name"] = attrs.view_qualified_name + result["calculation_view_name"] = attrs.calculation_view_name + result["calculation_view_qualified_name"] = attrs.calculation_view_qualified_name + result["is_profiled"] = attrs.is_profiled + result["last_profiled_at"] = attrs.last_profiled_at + result["sql_ai_model_context_qualified_name"] = ( + attrs.sql_ai_model_context_qualified_name + ) + result["sql_is_secure"] = attrs.sql_is_secure + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _view_to_nested(view: View) -> ViewNested: + """Convert flat View to nested format.""" + attrs = ViewAttributes() + _populate_view_attrs(attrs, view) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + view, _VIEW_REL_FIELDS, ViewRelationshipAttributes + ) + return ViewNested( + guid=view.guid, + type_name=view.type_name, + status=view.status, + version=view.version, + create_time=view.create_time, + update_time=view.update_time, + created_by=view.created_by, + updated_by=view.updated_by, + classifications=view.classifications, + classification_names=view.classification_names, + meanings=view.meanings, + labels=view.labels, + business_attributes=view.business_attributes, + custom_attributes=view.custom_attributes, + pending_tasks=view.pending_tasks, + proxy=view.proxy, + is_incomplete=view.is_incomplete, + provenance_type=view.provenance_type, + home_id=view.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _view_from_nested(nested: ViewNested) -> View: + """Convert nested format to flat View.""" + attrs = nested.attributes if nested.attributes is not UNSET else ViewAttributes() + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _VIEW_REL_FIELDS, + ViewRelationshipAttributes, + ) + return View( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_view_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _view_to_nested_bytes(view: View, serde: Serde) -> bytes: + """Convert flat View to nested JSON bytes.""" + return serde.encode(_view_to_nested(view)) + + +def _view_from_nested_bytes(data: bytes, serde: Serde) -> View: + """Convert nested JSON bytes to flat View.""" + nested = serde.decode(data, ViewNested) + return _view_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + BooleanField, + KeywordField, + NumericField, + RelationField, +) + +View.COLUMN_COUNT = NumericField("columnCount", "columnCount") +View.ROW_COUNT = NumericField("rowCount", "rowCount") +View.SIZE_BYTES = NumericField("sizeBytes", "sizeBytes") +View.IS_QUERY_PREVIEW = BooleanField("isQueryPreview", "isQueryPreview") +View.QUERY_PREVIEW_CONFIG = KeywordField("queryPreviewConfig", "queryPreviewConfig") +View.ALIAS = KeywordField("alias", "alias") +View.IS_TEMPORARY = BooleanField("isTemporary", "isTemporary") +View.DEFINITION = KeywordField("definition", "definition") +View.QUERY_COUNT = NumericField("queryCount", "queryCount") +View.QUERY_USER_COUNT = NumericField("queryUserCount", "queryUserCount") +View.QUERY_USER_MAP = KeywordField("queryUserMap", "queryUserMap") +View.QUERY_COUNT_UPDATED_AT = NumericField("queryCountUpdatedAt", "queryCountUpdatedAt") +View.DATABASE_NAME = KeywordField("databaseName", "databaseName") +View.DATABASE_QUALIFIED_NAME = KeywordField( + "databaseQualifiedName", "databaseQualifiedName" +) +View.SCHEMA_NAME = KeywordField("schemaName", "schemaName") +View.SCHEMA_QUALIFIED_NAME = KeywordField("schemaQualifiedName", "schemaQualifiedName") +View.TABLE_NAME = KeywordField("tableName", "tableName") +View.TABLE_QUALIFIED_NAME = KeywordField("tableQualifiedName", "tableQualifiedName") +View.VIEW_NAME = KeywordField("viewName", "viewName") +View.VIEW_QUALIFIED_NAME = KeywordField("viewQualifiedName", "viewQualifiedName") +View.CALCULATION_VIEW_NAME = KeywordField("calculationViewName", "calculationViewName") +View.CALCULATION_VIEW_QUALIFIED_NAME = KeywordField( + "calculationViewQualifiedName", "calculationViewQualifiedName" +) +View.IS_PROFILED = BooleanField("isProfiled", "isProfiled") +View.LAST_PROFILED_AT = NumericField("lastProfiledAt", "lastProfiledAt") +View.SQL_AI_MODEL_CONTEXT_QUALIFIED_NAME = KeywordField( + "sqlAIModelContextQualifiedName", "sqlAIModelContextQualifiedName" +) +View.SQL_IS_SECURE = BooleanField("sqlIsSecure", "sqlIsSecure") +View.INPUT_TO_AIRFLOW_TASKS = RelationField("inputToAirflowTasks") +View.OUTPUT_FROM_AIRFLOW_TASKS = RelationField("outputFromAirflowTasks") +View.ANOMALO_CHECKS = RelationField("anomaloChecks") +View.APPLICATION = RelationField("application") +View.APPLICATION_FIELD = RelationField("applicationField") +View.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +View.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +View.MODEL_IMPLEMENTED_ENTITIES = RelationField("modelImplementedEntities") +View.MODEL_IMPLEMENTED_ATTRIBUTES = RelationField("modelImplementedAttributes") +View.METRICS = RelationField("metrics") +View.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +View.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +View.DBT_MODELS = RelationField("dbtModels") +View.SQL_DBT_MODELS = RelationField("sqlDbtModels") +View.DBT_TESTS = RelationField("dbtTests") +View.DBT_SOURCES = RelationField("dbtSources") +View.SQL_DBT_SOURCES = RelationField("sqlDBTSources") +View.DBT_SEED_ASSETS = RelationField("dbtSeedAssets") +View.MEANINGS = RelationField("meanings") +View.MC_MONITORS = RelationField("mcMonitors") +View.MC_INCIDENTS = RelationField("mcIncidents") +View.PARTIAL_CHILD_FIELDS = RelationField("partialChildFields") +View.PARTIAL_CHILD_OBJECTS = RelationField("partialChildObjects") +View.INPUT_TO_PROCESSES = RelationField("inputToProcesses") +View.OUTPUT_FROM_PROCESSES = RelationField("outputFromProcesses") +View.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +View.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +View.FILES = RelationField("files") +View.LINKS = RelationField("links") +View.README = RelationField("readme") +View.COLUMNS = RelationField("columns") +View.QUERIES = RelationField("queries") +View.ATLAN_SCHEMA = RelationField("atlanSchema") +View.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +View.SNOWFLAKE_SEMANTIC_LOGICAL_TABLES = RelationField("snowflakeSemanticLogicalTables") +View.SODA_CHECKS = RelationField("sodaChecks") +View.INPUT_TO_SPARK_JOBS = RelationField("inputToSparkJobs") +View.OUTPUT_FROM_SPARK_JOBS = RelationField("outputFromSparkJobs") diff --git a/pyatlan_v9/model/assets/workflow.py b/pyatlan_v9/model/assets/workflow.py new file mode 100644 index 000000000..be763ef73 --- /dev/null +++ b/pyatlan_v9/model/assets/workflow.py @@ -0,0 +1,525 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# ruff: noqa: ARG002 +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Workflow asset model with flattened inheritance. + +This module provides: +- Workflow: Flat asset class (easy to use) +- WorkflowAttributes: Nested attributes struct (extends AssetAttributes) +- WorkflowNested: Nested API format struct +""" + +from __future__ import annotations + +from typing import Any, ClassVar, List, Union + +from msgspec import UNSET, UnsetType + +from .anomalo_related import RelatedAnomaloCheck +from .app_related import RelatedApplication, RelatedApplicationField +from .asset import ( + _ASSET_REL_FIELDS, + Asset, + AssetAttributes, + AssetNested, + AssetRelationshipAttributes, + _extract_asset_attrs, + _populate_asset_attrs, +) +from .data_mesh_related import RelatedDataProduct +from .data_quality_related import RelatedDataQualityRule, RelatedMetric +from .gtc_related import RelatedAtlasGlossaryTerm +from .monte_carlo_related import RelatedMCIncident, RelatedMCMonitor +from .referenceable_related import RelatedReferenceable +from .resource_related import RelatedFile, RelatedLink, RelatedReadme +from .schema_registry_related import RelatedSchemaRegistrySubject +from .soda_related import RelatedSodaCheck +from pyatlan_v9.model.conversion_utils import ( + categorize_relationships, + merge_relationships, +) +from pyatlan_v9.model.serde import Serde, get_serde +from pyatlan_v9.model.transform import register_asset + +# ============================================================================= +# FLAT ASSET CLASS +# ============================================================================= + + +@register_asset +class Workflow(Asset): + """ + Instance of a governance workflow. + """ + + WORKFLOW_TEMPLATE_GUID: ClassVar[Any] = None + WORKFLOW_TYPE: ClassVar[Any] = None + WORKFLOW_ACTION_CHOICES: ClassVar[Any] = None + WORKFLOW_CONFIG: ClassVar[Any] = None + WORKFLOW_STATUS: ClassVar[Any] = None + WORKFLOW_RUN_EXPIRES_IN: ClassVar[Any] = None + WORKFLOW_CREATED_BY: ClassVar[Any] = None + WORKFLOW_UPDATED_BY: ClassVar[Any] = None + WORKFLOW_DELETED_AT: ClassVar[Any] = None + ANOMALO_CHECKS: ClassVar[Any] = None + APPLICATION: ClassVar[Any] = None + APPLICATION_FIELD: ClassVar[Any] = None + OUTPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + INPUT_PORT_DATA_PRODUCTS: ClassVar[Any] = None + METRICS: ClassVar[Any] = None + DQ_BASE_DATASET_RULES: ClassVar[Any] = None + DQ_REFERENCE_DATASET_RULES: ClassVar[Any] = None + MEANINGS: ClassVar[Any] = None + MC_MONITORS: ClassVar[Any] = None + MC_INCIDENTS: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_TO: ClassVar[Any] = None + USER_DEF_RELATIONSHIP_FROM: ClassVar[Any] = None + FILES: ClassVar[Any] = None + LINKS: ClassVar[Any] = None + README: ClassVar[Any] = None + SCHEMA_REGISTRY_SUBJECTS: ClassVar[Any] = None + SODA_CHECKS: ClassVar[Any] = None + + type_name: Union[str, UnsetType] = "Workflow" + + workflow_template_guid: Union[str, None, UnsetType] = UNSET + """GUID of the workflow template from which this workflow was created.""" + + workflow_type: Union[str, None, UnsetType] = UNSET + """Type of the workflow.""" + + workflow_action_choices: Union[List[str], None, UnsetType] = UNSET + """List of workflow action choices.""" + + workflow_config: Union[str, None, UnsetType] = UNSET + """Details of the workflow.""" + + workflow_status: Union[str, None, UnsetType] = UNSET + """Status of the workflow.""" + + workflow_run_expires_in: Union[str, None, UnsetType] = UNSET + """Time duration after which a run of this workflow will expire.""" + + workflow_created_by: Union[str, None, UnsetType] = UNSET + """Username of the user who created this workflow.""" + + workflow_updated_by: Union[str, None, UnsetType] = UNSET + """Username of the user who updated this workflow.""" + + workflow_deleted_at: Union[int, None, UnsetType] = UNSET + """Deletion time of this workflow.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + def __post_init__(self) -> None: + self.type_name = "Workflow" + + # ========================================================================= + # Optimized Serialization Methods (override Asset base class) + # ========================================================================= + + def to_json(self, nested: bool = True, serde: Serde | None = None) -> str: + """ + Convert to JSON string using optimized nested struct serialization. + + Args: + nested: If True (default), use nested API format. If False, use flat format. + serde: Optional Serde instance for encoder reuse. Uses shared singleton if None. + + Returns: + JSON string representation + """ + if serde is None: + serde = get_serde() + if nested: + return self.to_nested_bytes(serde).decode("utf-8") + else: + return serde.encode(self).decode("utf-8") + + def to_nested_bytes(self, serde: Serde | None = None) -> bytes: + """Serialize to Atlas nested-format JSON bytes (pure msgspec, no dict intermediate).""" + if serde is None: + serde = get_serde() + return _workflow_to_nested_bytes(self, serde) + + @staticmethod + def from_json(json_data: str | bytes, serde: Serde | None = None) -> Workflow: + """ + Create from JSON string or bytes using optimized nested struct deserialization. + + Args: + json_data: JSON string or bytes to deserialize + serde: Optional Serde instance for decoder reuse. Uses shared singleton if None. + + Returns: + Workflow instance + """ + if isinstance(json_data, str): + json_data = json_data.encode("utf-8") + if serde is None: + serde = get_serde() + return _workflow_from_nested_bytes(json_data, serde) + + +# ============================================================================= +# NESTED FORMAT CLASSES +# ============================================================================= + + +class WorkflowAttributes(AssetAttributes): + """Workflow-specific attributes for nested API format.""" + + workflow_template_guid: Union[str, None, UnsetType] = UNSET + """GUID of the workflow template from which this workflow was created.""" + + workflow_type: Union[str, None, UnsetType] = UNSET + """Type of the workflow.""" + + workflow_action_choices: Union[List[str], None, UnsetType] = UNSET + """List of workflow action choices.""" + + workflow_config: Union[str, None, UnsetType] = UNSET + """Details of the workflow.""" + + workflow_status: Union[str, None, UnsetType] = UNSET + """Status of the workflow.""" + + workflow_run_expires_in: Union[str, None, UnsetType] = UNSET + """Time duration after which a run of this workflow will expire.""" + + workflow_created_by: Union[str, None, UnsetType] = UNSET + """Username of the user who created this workflow.""" + + workflow_updated_by: Union[str, None, UnsetType] = UNSET + """Username of the user who updated this workflow.""" + + workflow_deleted_at: Union[int, None, UnsetType] = UNSET + """Deletion time of this workflow.""" + + +class WorkflowRelationshipAttributes(AssetRelationshipAttributes): + """Workflow-specific relationship attributes for nested API format.""" + + anomalo_checks: Union[List[RelatedAnomaloCheck], None, UnsetType] = UNSET + """Checks that run on this asset.""" + + application: Union[RelatedApplication, None, UnsetType] = UNSET + """Application owning the Asset.""" + + application_field: Union[RelatedApplicationField, None, UnsetType] = UNSET + """ApplicationField owning the Asset.""" + + output_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an output port.""" + + input_port_data_products: Union[List[RelatedDataProduct], None, UnsetType] = UNSET + """Data products for which this asset is an input port.""" + + metrics: Union[List[RelatedMetric], None, UnsetType] = UNSET + """""" + + dq_base_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = UNSET + """Rules that are applied on this dataset.""" + + dq_reference_dataset_rules: Union[List[RelatedDataQualityRule], None, UnsetType] = ( + UNSET + ) + """Rules where this dataset is referenced.""" + + meanings: Union[List[RelatedAtlasGlossaryTerm], None, UnsetType] = UNSET + """Glossary terms that are linked to this asset.""" + + mc_monitors: Union[List[RelatedMCMonitor], None, UnsetType] = UNSET + """Monitors that observe this asset.""" + + mc_incidents: Union[List[RelatedMCIncident], None, UnsetType] = UNSET + """""" + + user_def_relationship_to: Union[List[RelatedReferenceable], None, UnsetType] = UNSET + """""" + + user_def_relationship_from: Union[List[RelatedReferenceable], None, UnsetType] = ( + UNSET + ) + """""" + + files: Union[List[RelatedFile], None, UnsetType] = UNSET + """""" + + links: Union[List[RelatedLink], None, UnsetType] = UNSET + """Links that are attached to this asset.""" + + readme: Union[RelatedReadme, None, UnsetType] = UNSET + """README that is linked to this asset.""" + + schema_registry_subjects: Union[ + List[RelatedSchemaRegistrySubject], None, UnsetType + ] = UNSET + """""" + + soda_checks: Union[List[RelatedSodaCheck], None, UnsetType] = UNSET + """""" + + +class WorkflowNested(AssetNested): + """Workflow in nested API format for high-performance serialization.""" + + attributes: Union[WorkflowAttributes, UnsetType] = UNSET + relationship_attributes: Union[WorkflowRelationshipAttributes, UnsetType] = UNSET + append_relationship_attributes: Union[WorkflowRelationshipAttributes, UnsetType] = ( + UNSET + ) + remove_relationship_attributes: Union[WorkflowRelationshipAttributes, UnsetType] = ( + UNSET + ) + + +# ============================================================================= +# CONVERSION HELPERS & CONSTANTS +# ============================================================================= + +_WORKFLOW_REL_FIELDS: List[str] = [ + *_ASSET_REL_FIELDS, + "anomalo_checks", + "application", + "application_field", + "output_port_data_products", + "input_port_data_products", + "metrics", + "dq_base_dataset_rules", + "dq_reference_dataset_rules", + "meanings", + "mc_monitors", + "mc_incidents", + "user_def_relationship_to", + "user_def_relationship_from", + "files", + "links", + "readme", + "schema_registry_subjects", + "soda_checks", +] + + +def _populate_workflow_attrs(attrs: WorkflowAttributes, obj: Workflow) -> None: + """Populate Workflow-specific attributes on the attrs struct.""" + _populate_asset_attrs(attrs, obj) + attrs.workflow_template_guid = obj.workflow_template_guid + attrs.workflow_type = obj.workflow_type + attrs.workflow_action_choices = obj.workflow_action_choices + attrs.workflow_config = obj.workflow_config + attrs.workflow_status = obj.workflow_status + attrs.workflow_run_expires_in = obj.workflow_run_expires_in + attrs.workflow_created_by = obj.workflow_created_by + attrs.workflow_updated_by = obj.workflow_updated_by + attrs.workflow_deleted_at = obj.workflow_deleted_at + + +def _extract_workflow_attrs(attrs: WorkflowAttributes) -> dict: + """Extract all Workflow attributes from the attrs struct into a flat dict.""" + result = _extract_asset_attrs(attrs) + result["workflow_template_guid"] = attrs.workflow_template_guid + result["workflow_type"] = attrs.workflow_type + result["workflow_action_choices"] = attrs.workflow_action_choices + result["workflow_config"] = attrs.workflow_config + result["workflow_status"] = attrs.workflow_status + result["workflow_run_expires_in"] = attrs.workflow_run_expires_in + result["workflow_created_by"] = attrs.workflow_created_by + result["workflow_updated_by"] = attrs.workflow_updated_by + result["workflow_deleted_at"] = attrs.workflow_deleted_at + return result + + +# ============================================================================= +# CONVERSION FUNCTIONS +# ============================================================================= + + +def _workflow_to_nested(workflow: Workflow) -> WorkflowNested: + """Convert flat Workflow to nested format.""" + attrs = WorkflowAttributes() + _populate_workflow_attrs(attrs, workflow) + # Categorize relationships by save semantic (REPLACE, APPEND, REMOVE) + replace_rels, append_rels, remove_rels = categorize_relationships( + workflow, _WORKFLOW_REL_FIELDS, WorkflowRelationshipAttributes + ) + return WorkflowNested( + guid=workflow.guid, + type_name=workflow.type_name, + status=workflow.status, + version=workflow.version, + create_time=workflow.create_time, + update_time=workflow.update_time, + created_by=workflow.created_by, + updated_by=workflow.updated_by, + classifications=workflow.classifications, + classification_names=workflow.classification_names, + meanings=workflow.meanings, + labels=workflow.labels, + business_attributes=workflow.business_attributes, + custom_attributes=workflow.custom_attributes, + pending_tasks=workflow.pending_tasks, + proxy=workflow.proxy, + is_incomplete=workflow.is_incomplete, + provenance_type=workflow.provenance_type, + home_id=workflow.home_id, + attributes=attrs, + relationship_attributes=replace_rels, + append_relationship_attributes=append_rels, + remove_relationship_attributes=remove_rels, + ) + + +def _workflow_from_nested(nested: WorkflowNested) -> Workflow: + """Convert nested format to flat Workflow.""" + attrs = ( + nested.attributes if nested.attributes is not UNSET else WorkflowAttributes() + ) + # Merge relationships from all three buckets + merged_rels = merge_relationships( + nested.relationship_attributes, + nested.append_relationship_attributes, + nested.remove_relationship_attributes, + _WORKFLOW_REL_FIELDS, + WorkflowRelationshipAttributes, + ) + return Workflow( + guid=nested.guid, + type_name=nested.type_name, + status=nested.status, + version=nested.version, + create_time=nested.create_time, + update_time=nested.update_time, + created_by=nested.created_by, + updated_by=nested.updated_by, + classifications=nested.classifications, + classification_names=nested.classification_names, + meanings=nested.meanings, + labels=nested.labels, + business_attributes=nested.business_attributes, + custom_attributes=nested.custom_attributes, + pending_tasks=nested.pending_tasks, + proxy=nested.proxy, + is_incomplete=nested.is_incomplete, + provenance_type=nested.provenance_type, + home_id=nested.home_id, + **_extract_workflow_attrs(attrs), + # Merged relationship attributes + **merged_rels, + ) + + +def _workflow_to_nested_bytes(workflow: Workflow, serde: Serde) -> bytes: + """Convert flat Workflow to nested JSON bytes.""" + return serde.encode(_workflow_to_nested(workflow)) + + +def _workflow_from_nested_bytes(data: bytes, serde: Serde) -> Workflow: + """Convert nested JSON bytes to flat Workflow.""" + nested = serde.decode(data, WorkflowNested) + return _workflow_from_nested(nested) + + +# --------------------------------------------------------------------------- +# Deferred field descriptor initialization +# --------------------------------------------------------------------------- +from pyatlan.model.fields.atlan_fields import ( # noqa: E402 + KeywordField, + NumericField, + RelationField, +) + +Workflow.WORKFLOW_TEMPLATE_GUID = KeywordField( + "workflowTemplateGuid", "workflowTemplateGuid" +) +Workflow.WORKFLOW_TYPE = KeywordField("workflowType", "workflowType") +Workflow.WORKFLOW_ACTION_CHOICES = KeywordField( + "workflowActionChoices", "workflowActionChoices" +) +Workflow.WORKFLOW_CONFIG = KeywordField("workflowConfig", "workflowConfig") +Workflow.WORKFLOW_STATUS = KeywordField("workflowStatus", "workflowStatus") +Workflow.WORKFLOW_RUN_EXPIRES_IN = KeywordField( + "workflowRunExpiresIn", "workflowRunExpiresIn" +) +Workflow.WORKFLOW_CREATED_BY = KeywordField("workflowCreatedBy", "workflowCreatedBy") +Workflow.WORKFLOW_UPDATED_BY = KeywordField("workflowUpdatedBy", "workflowUpdatedBy") +Workflow.WORKFLOW_DELETED_AT = NumericField("workflowDeletedAt", "workflowDeletedAt") +Workflow.ANOMALO_CHECKS = RelationField("anomaloChecks") +Workflow.APPLICATION = RelationField("application") +Workflow.APPLICATION_FIELD = RelationField("applicationField") +Workflow.OUTPUT_PORT_DATA_PRODUCTS = RelationField("outputPortDataProducts") +Workflow.INPUT_PORT_DATA_PRODUCTS = RelationField("inputPortDataProducts") +Workflow.METRICS = RelationField("metrics") +Workflow.DQ_BASE_DATASET_RULES = RelationField("dqBaseDatasetRules") +Workflow.DQ_REFERENCE_DATASET_RULES = RelationField("dqReferenceDatasetRules") +Workflow.MEANINGS = RelationField("meanings") +Workflow.MC_MONITORS = RelationField("mcMonitors") +Workflow.MC_INCIDENTS = RelationField("mcIncidents") +Workflow.USER_DEF_RELATIONSHIP_TO = RelationField("userDefRelationshipTo") +Workflow.USER_DEF_RELATIONSHIP_FROM = RelationField("userDefRelationshipFrom") +Workflow.FILES = RelationField("files") +Workflow.LINKS = RelationField("links") +Workflow.README = RelationField("readme") +Workflow.SCHEMA_REGISTRY_SUBJECTS = RelationField("schemaRegistrySubjects") +Workflow.SODA_CHECKS = RelationField("sodaChecks") diff --git a/pyatlan_v9/model/assets/workflow_related.py b/pyatlan_v9/model/assets/workflow_related.py new file mode 100644 index 000000000..8f83b7970 --- /dev/null +++ b/pyatlan_v9/model/assets/workflow_related.py @@ -0,0 +1,114 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Related type classes for Workflow module. + +This module contains all Related{Type} classes for the Workflow type hierarchy. +These classes are used for relationship attributes to reference related entities. +""" + +from __future__ import annotations + +from typing import List, Union + +from msgspec import UNSET, UnsetType + +from .asset_related import RelatedAsset +from .referenceable_related import RelatedReferenceable + +__all__ = [ + "RelatedWorkflow", + "RelatedWorkflowRun", +] + + +class RelatedWorkflow(RelatedAsset): + """ + Related entity reference for Workflow assets. + + Extends RelatedAsset with Workflow-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "Workflow" so it serializes correctly + + workflow_template_guid: Union[str, None, UnsetType] = UNSET + """GUID of the workflow template from which this workflow was created.""" + + workflow_type: Union[str, None, UnsetType] = UNSET + """Type of the workflow.""" + + workflow_action_choices: Union[List[str], None, UnsetType] = UNSET + """List of workflow action choices.""" + + workflow_config: Union[str, None, UnsetType] = UNSET + """Details of the workflow.""" + + workflow_status: Union[str, None, UnsetType] = UNSET + """Status of the workflow.""" + + workflow_run_expires_in: Union[str, None, UnsetType] = UNSET + """Time duration after which a run of this workflow will expire.""" + + workflow_created_by: Union[str, None, UnsetType] = UNSET + """Username of the user who created this workflow.""" + + workflow_updated_by: Union[str, None, UnsetType] = UNSET + """Username of the user who updated this workflow.""" + + workflow_deleted_at: Union[int, None, UnsetType] = UNSET + """Deletion time of this workflow.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "Workflow" + + +class RelatedWorkflowRun(RelatedWorkflow): + """ + Related entity reference for WorkflowRun assets. + + Extends RelatedWorkflow with WorkflowRun-specific attributes. + """ + + # type_name inherited from parent with default=UNSET + # __post_init__ sets it to "WorkflowRun" so it serializes correctly + + workflow_workflow_guid: Union[str, None, UnsetType] = UNSET + """GUID of the workflow from which this run was created.""" + + workflow_type: Union[str, None, UnsetType] = UNSET + """Type of the workflow from which this run was created.""" + + workflow_action_choices: Union[List[str], None, UnsetType] = UNSET + """List of workflow run action choices.""" + + workflow_on_asset_guid: Union[str, None, UnsetType] = UNSET + """The asset for which this run was created.""" + + workflow_run_comment: Union[str, None, UnsetType] = UNSET + """The comment added by the requester""" + + workflow_run_config: Union[str, None, UnsetType] = UNSET + """Details of the approval workflow run.""" + + workflow_status: Union[str, None, UnsetType] = UNSET + """Status of the run.""" + + workflow_expires_at: Union[int, None, UnsetType] = UNSET + """Time at which this run will expire.""" + + workflow_created_by: Union[str, None, UnsetType] = UNSET + """Username of the user who created this workflow run.""" + + workflow_updated_by: Union[str, None, UnsetType] = UNSET + """Username of the user who updated this workflow run.""" + + workflow_deleted_at: Union[int, None, UnsetType] = UNSET + """Deletion time of this workflow run.""" + + def __post_init__(self) -> None: + RelatedReferenceable.__post_init__(self) + self.type_name = "WorkflowRun" diff --git a/pyatlan_v9/model/atlan_image.py b/pyatlan_v9/model/atlan_image.py new file mode 100644 index 000000000..9e0c0df55 --- /dev/null +++ b/pyatlan_v9/model/atlan_image.py @@ -0,0 +1,53 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2023 Atlan Pte. Ltd. + +from __future__ import annotations + +from typing import Union + +import msgspec + + +class AtlanImage(msgspec.Struct, kw_only=True): + """Details of an uploaded image in Atlan.""" + + id: Union[str, None] = None + """Unique identifier (GUID) of the image.""" + + version: Union[str, None] = None + + created_at: Union[int, None] = None + """Time at which the image was uploaded (epoch), in milliseconds.""" + + updated_at: Union[int, None] = None + """Time at which the image was last modified (epoch), in milliseconds.""" + + file_name: Union[str, None] = None + """Generated name of the image that was uploaded.""" + + raw_name: Union[str, None] = None + """Generated name of the image that was uploaded.""" + + key: Union[str, None] = None + """Generated name of the image that was uploaded.""" + + extension: Union[str, None] = None + """Filename extension for the image that was uploaded.""" + + content_type: Union[str, None] = None + """MIME type for the image that was uploaded.""" + + file_size: Union[str, None] = None + """Size of the image that was uploaded, in bytes.""" + + is_encrypted: Union[bool, None] = None + """Whether the image is encrypted (true) or not (false).""" + + redirect_url: Union[str, None] = None + + is_uploaded: Union[bool, None] = None + + uploaded_at: Union[str, None] = None + + is_archived: Union[bool, None] = None + """Whether the image has been archived (true) or is still actively available (false).""" diff --git a/pyatlan_v9/model/audit.py b/pyatlan_v9/model/audit.py new file mode 100644 index 000000000..1786691d0 --- /dev/null +++ b/pyatlan_v9/model/audit.py @@ -0,0 +1,473 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2022 Atlan Pte. Ltd. +from __future__ import annotations + +import json as json_lib +from datetime import datetime +from typing import Any, Dict, Generator, Iterable, List, Optional, Set, Union + +import msgspec + +from pyatlan.cache.custom_metadata_cache import CustomMetadataCache +from pyatlan.client.constants import AUDIT_SEARCH +from pyatlan.client.protocol import ApiCaller +from pyatlan.errors import ErrorCode, NotFoundError + +# --------------------------------------------------------------------------- +# Re-export plain Enum from legacy — no migration needed. +# --------------------------------------------------------------------------- +from pyatlan.model.audit import AuditActionType # noqa: F401 +from pyatlan.model.constants import DELETED_ +from pyatlan.model.enums import SortOrder +from pyatlan_v9.model.aggregation import Aggregation +from pyatlan_v9.model.search import DSL, Bool, Query, Range, SortItem, Term + +TOTAL_COUNT = "totalCount" + +ENTITY_AUDITS = "entityAudits" + +ATTRIBUTES = "attributes" + +TYPE_NAME = "type_name" + +LATEST_FIRST = [SortItem("created", order=SortOrder.DESCENDING)] + + +class AuditSearchRequest(msgspec.Struct, kw_only=True): + """Class from which to configure a search against Atlan's activity log.""" + + dsl: DSL + attributes: List[str] = msgspec.field(default_factory=list) + + def __post_init__(self): + class_name = self.__class__.__name__ + if self.dsl and isinstance(self.dsl, DSL) and not self.dsl.req_class_name: + self.dsl = DSL( + req_class_name=class_name, + from_=self.dsl.from_, + size=self.dsl.size, + aggregations=self.dsl.aggregations, + track_total_hits=self.dsl.track_total_hits, + post_filter=self.dsl.post_filter, + query=self.dsl.query, + sort=self.dsl.sort, + ) + + def to_dict( + self, + by_alias: bool = True, + exclude_none: bool = True, + ) -> Dict[str, Any]: + """Serialize AuditSearchRequest to a dict suitable for JSON serialization.""" + d: Dict[str, Any] = { + "attributes": self.attributes, + "dsl": json_lib.loads( + self.dsl.json(by_alias=by_alias, exclude_none=exclude_none) + ), + } + if exclude_none: + d = {k: v for k, v in d.items() if v is not None} + return d + + def json( + self, + by_alias: bool = False, + exclude_none: bool = False, + exclude_unset: bool = False, + ) -> str: + """Serialize AuditSearchRequest to JSON string.""" + return json_lib.dumps( + self.to_dict(by_alias=by_alias, exclude_none=exclude_none) + ) + + @classmethod + def by_guid( + cls, + guid: str, + *, + size: int = 10, + _from: int = 0, + sort: Union[SortItem, List[SortItem]] = LATEST_FIRST, + ) -> "AuditSearchRequest": + """ + Create an audit search request for the last changes to an asset, by its GUID. + :param guid: unique identifier of the asset for which to retrieve the audit history + :param size: number of changes to retrieve + :param _from: starting point for paging. Defaults to 0 (very first result) if not overridden + :param sort: sorting criteria for the results. Defaults to LATEST_FIRST(sorting by "created" in desc order). + :returns: an AuditSearchRequest that can be used to perform the search + """ + dsl = DSL( + query=Bool(filter=[Term(field="entityId", value=guid)]), + sort=sort if LATEST_FIRST else [], + size=size, + from_=_from, + ) + return AuditSearchRequest(dsl=dsl) + + @classmethod + def by_user( + cls, + user: str, + *, + size: int = 10, + _from: int = 0, + sort: Union[SortItem, List[SortItem]] = LATEST_FIRST, + ) -> "AuditSearchRequest": + """ + Create an audit search request for the last changes to an asset, by a given user. + :param user: the name of the user for which to look for any changes + :param size: number of changes to retrieve + :param _from: starting point for paging. Defaults to 0 (very first result) if not overridden + :param sort: sorting criteria for the results. Defaults to LATEST_FIRST(sorting by "created" in desc order). + :returns: an AuditSearchRequest that can be used to perform the search + """ + dsl = DSL( + query=Bool(filter=[Term(field="user", value=user)]), + sort=sort if LATEST_FIRST else [], + size=size, + from_=_from, + ) + return AuditSearchRequest(dsl=dsl) + + @classmethod + def by_qualified_name( + cls, + type_name: str, + qualified_name: str, + *, + size: int = 10, + _from: int = 0, + sort: Union[SortItem, List[SortItem]] = LATEST_FIRST, + ) -> "AuditSearchRequest": + """ + Create an audit search request for the last changes to an asset, by its qualifiedName. + :param type_name: the type of asset for which to retrieve the audit history + :param qualified_name: unique name of the asset for which to retrieve the audit history + :param size: number of changes to retrieve + :param _from: starting point for paging. Defaults to 0 (very first result) if not overridden + :param sort: sorting criteria for the results. Defaults to LATEST_FIRST(sorting by "created" in desc order). + :returns: an AuditSearchRequest that can be used to perform the search + """ + dsl = DSL( + query=Bool( + must=[ + Term(field="entityQualifiedName", value=qualified_name), + Term(field="typeName", value=type_name), + ] + ), + sort=sort if LATEST_FIRST else [], + size=size, + from_=_from, + ) + return AuditSearchRequest(dsl=dsl) + + +class CustomMetadataAttributesAuditDetail(msgspec.Struct, kw_only=True): + """Capture the attributes and values for custom metadata as tracked through the audit log.""" + + type_name: str + attributes: Dict[str, Any] = msgspec.field(default_factory=dict) + archived_attributes: Optional[Dict[str, Any]] = None + + def __post_init__(self): + cm_id = self.type_name + try: + self.type_name = CustomMetadataCache.get_name_for_id(self.type_name) + attributes = { + CustomMetadataCache.get_attr_name_for_id(cm_id, attr_id): properties + for attr_id, properties in self.attributes.items() + } + archived_attributes = { + key: value for key, value in attributes.items() if "-archived-" in key + } + for key in archived_attributes: + del attributes[key] + self.attributes = attributes + self.archived_attributes = archived_attributes + except NotFoundError: + self.type_name = DELETED_ + self.attributes = {} + + @property + def empty(self) -> bool: + return not self.attributes or len(self.attributes) == 0 + + +class EntityAudit(msgspec.Struct, kw_only=True, rename="camel"): + """ + Detailed entry in the audit log. These objects should be treated as immutable. + """ + + entity_qualified_name: str + type_name: str + entity_id: str + timestamp: datetime + created: datetime + user: str + action: AuditActionType + details: Optional[Any] = None + event_key: str = "" + entity: Optional[Any] = None + type: Optional[Any] = None + detail: Optional[Any] = None + entity_detail: Optional[Any] = None + headers: Optional[Dict[str, str]] = None + + def __post_init__(self): + from pyatlan_v9.model.transform import from_atlas_format + + if isinstance(self.detail, dict) and "typeName" in self.detail: + try: + self.detail = from_atlas_format(self.detail) + except Exception: + pass + if isinstance(self.entity_detail, dict) and "typeName" in self.entity_detail: + try: + self.entity_detail = from_atlas_format(self.entity_detail) + except Exception: + pass + + +class AuditSearchResults(Iterable): + """ + Captures the response from a search against Atlan's activity log. + """ + + _DEFAULT_SIZE = 300 + _MASS_EXTRACT_THRESHOLD = 10000 - _DEFAULT_SIZE + + def __init__( + self, + client: ApiCaller, + criteria: AuditSearchRequest, + start: int, + size: int, + entity_audits: List[EntityAudit], + count: int, + bulk: bool = False, + aggregations: Optional[Aggregation] = None, + ): + self._client = client + self._endpoint = AUDIT_SEARCH + self._criteria = criteria + self._start = start + self._size = size + self._entity_audits = entity_audits + self._count = count + self._approximate_count = count + self._bulk = bulk + self._aggregations = aggregations + self._first_record_creation_time = -2 + self._last_record_creation_time = -2 + self._processed_entity_keys: Set[str] = set() + + @property + def aggregations(self) -> Optional[Aggregation]: + return self._aggregations + + @property + def total_count(self) -> int: + return self._count + + def current_page(self) -> List[EntityAudit]: + """ + Retrieve the current page of results. + + :returns: list of assets on the current page of results + """ + return self._entity_audits + + def next_page(self, start=None, size=None) -> bool: + """ + Indicates whether there is a next page of results. + + :returns: True if there is a next page of results, otherwise False + """ + self._start = start or self._start + self._size + is_bulk_search = ( + self._bulk or self._approximate_count > self._MASS_EXTRACT_THRESHOLD + ) + if size: + self._size = size + + if is_bulk_search: + self._processed_entity_keys.update( + entity.event_key for entity in self._entity_audits + ) + return self._get_next_page() if self._entity_audits else False + + def _get_next_page(self): + """ + Fetches the next page of results. + + :returns: True if the next page of results was fetched, False if there was no next page + """ + query = self._criteria.dsl.query + self._criteria.dsl.size = self._size + self._criteria.dsl.from_ = self._start + is_bulk_search = ( + self._bulk or self._approximate_count > self._MASS_EXTRACT_THRESHOLD + ) + + if is_bulk_search: + self._prepare_query_for_timestamp_paging(query) + + if raw_json := self._get_next_page_json(is_bulk_search): + self._count = raw_json.get(TOTAL_COUNT, 0) + return True + return False + + def _get_next_page_json(self, is_bulk_search: bool = False): + """ + Fetches the next page of results and returns the raw JSON of the retrieval. + + :returns: JSON for the next page of results, as-is + """ + raw_json = self._client._call_api( + self._endpoint, + request_obj=self._criteria, + ) + if ENTITY_AUDITS not in raw_json or not raw_json[ENTITY_AUDITS]: + self._entity_audits = [] + return None + + try: + from pyatlan_v9.client.audit import ( + _AUDIT_TS_FIELDS, + _normalize_ms_timestamps, + ) + + self._entity_audits = [ + msgspec.convert( + _normalize_ms_timestamps(audit, _AUDIT_TS_FIELDS), + EntityAudit, + strict=False, + ) + for audit in raw_json[ENTITY_AUDITS] + ] + if is_bulk_search: + self._filter_processed_entities() + self._update_first_last_record_creation_times() + return raw_json + except Exception as err: + raise ErrorCode.JSON_ERROR.exception_with_parameters( + raw_json, 200, str(err) + ) from err + + def _prepare_query_for_timestamp_paging(self, query: Query): + """ + Adjusts the query to include timestamp filters for audit bulk extraction. + """ + rewritten_filters = [] + if isinstance(query, Bool): + for filter_ in query.filter: + if self._is_paging_timestamp_query(filter_): + continue + rewritten_filters.append(filter_) + + if self._first_record_creation_time != self._last_record_creation_time: + rewritten_filters.append( + self._get_paging_timestamp_query(self._last_record_creation_time) + ) + if isinstance(query, Bool): + rewritten_query = Bool( + filter=rewritten_filters, + must=query.must, + must_not=query.must_not, + should=query.should, + boost=query.boost, + minimum_should_match=query.minimum_should_match, + ) + else: + rewritten_filters.append(query) + rewritten_query = Bool(filter=rewritten_filters) + self._criteria.dsl.from_ = 0 + self._criteria.dsl.query = rewritten_query + else: + if isinstance(query, Bool): + for filter_ in query.filter: + if self._is_paging_timestamp_query(filter_): + query.filter.remove(filter_) + self._criteria.dsl.from_ = len(self._processed_entity_keys) + + @staticmethod + def _get_paging_timestamp_query(last_timestamp: int) -> Query: + return Range(field="created", gte=last_timestamp) + + @staticmethod + def _is_paging_timestamp_query(filter_: Query) -> bool: + return ( + isinstance(filter_, Range) + and filter_.field == "created" + and filter_.gte is not None + ) + + def _update_first_last_record_creation_times(self): + self._first_record_creation_time = self._last_record_creation_time = -2 + + if not isinstance(self._entity_audits, list) or len(self._entity_audits) <= 1: + return + + first_audit, last_audit = self._entity_audits[0], self._entity_audits[-1] + + if first_audit: + self._first_record_creation_time = first_audit.created + + if last_audit: + self._last_record_creation_time = last_audit.created + + def _filter_processed_entities(self): + """ + Remove entities that have already been processed to avoid duplicates. + """ + self._entity_audits = [ + entity + for entity in self._entity_audits + if entity is not None + and entity.event_key not in self._processed_entity_keys + ] + + @staticmethod + def presorted_by_timestamp(sorts: Optional[List[SortItem]]) -> bool: + """ + Checks if the sorting options prioritize creation time in ascending order. + :param sorts: list of sorting options or None. + :returns: True if sorting is already prioritized by creation time, False otherwise. + """ + if sorts and isinstance(sorts[0], SortItem): + return sorts[0].field == "created" and sorts[0].order == SortOrder.ASCENDING + return False + + @staticmethod + def sort_by_timestamp_first(sorts: List[SortItem]) -> List[SortItem]: + """ + Rewrites the sorting options to ensure that + sorting by creation time, ascending, is the top + priority. + + :param sorts: list of sorting options + :returns: sorting options, making sorting by + creation time in ascending order the top priority + """ + creation_asc_sort = [SortItem("created", order=SortOrder.ASCENDING)] + + if not sorts: + return creation_asc_sort + + rewritten_sorts = [ + sort for sort in sorts if (not sort.field) or (sort.field != "__timestamp") + ] + return creation_asc_sort + rewritten_sorts + + def __iter__(self) -> Generator[EntityAudit, None, None]: + """ + Iterates through the results, lazily-fetching each next page until there + are no more results. + + returns: an iterable form of each result, across all pages + """ + while True: + yield from self.current_page() + if not self.next_page(): + break diff --git a/pyatlan_v9/model/constants.py b/pyatlan_v9/model/constants.py new file mode 100644 index 000000000..9566dbee2 --- /dev/null +++ b/pyatlan_v9/model/constants.py @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. +"""Re-export constants from legacy module for v9 compatibility.""" + +from pyatlan.model.constants import * # noqa: F401,F403 diff --git a/pyatlan_v9/model/contract.py b/pyatlan_v9/model/contract.py new file mode 100644 index 000000000..22d66bba9 --- /dev/null +++ b/pyatlan_v9/model/contract.py @@ -0,0 +1,233 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +from __future__ import annotations + +from typing import Any, ClassVar, Union + +import msgspec +import yaml + +from pyatlan.model.enums import CertificateStatus, DataContractStatus + + +class InitRequest(msgspec.Struct, kw_only=True, rename="camel"): + """Request to initialize a data contract.""" + + asset_type: Union[str, None] = None + asset_qualified_name: Union[str, None] = None + + +class DataContractOwners(msgspec.Struct, kw_only=True): + """Owners of the dataset.""" + + users: list[str] = msgspec.field(default_factory=list) + """Individual users who own the dataset.""" + groups: list[str] = msgspec.field(default_factory=list) + """Groups that own the dataset.""" + + +class DataContractCertification(msgspec.Struct, kw_only=True): + """Certification information for the dataset.""" + + status: Union[CertificateStatus, str, None] = None + """State of the certification.""" + message: Union[str, None] = None + """Message to accompany the certification.""" + + +class DataContractAnnouncement(msgspec.Struct, kw_only=True): + """Announcement details for the dataset.""" + + type: Union[CertificateStatus, str, None] = None + """Type of announcement.""" + title: Union[str, None] = None + """Title to use for the announcement.""" + description: Union[str, None] = None + """Message to accompany the announcement.""" + + +class DCTag(msgspec.Struct, kw_only=True): + """Tagging details for the dataset.""" + + name: Union[str, None] = None + """Human-readable name of the Atlan tag.""" + propagate: Union[bool, str, None] = None + """Whether to propagate the tag or not.""" + propagate_through_lineage: Union[bool, None] = msgspec.field( + default=None, name="restrict_propagation_through_lineage" + ) + """Whether to propagate the tag through lineage.""" + propagate_through_hierarchy: Union[bool, None] = msgspec.field( + default=None, name="restrict_propagation_through_hierarchy" + ) + """Whether to propagate the tag through asset's containment hierarchy.""" + + +class DCColumn(msgspec.Struct, kw_only=True): + """Details of individual columns in the dataset.""" + + name: Union[str, None] = None + """Name of the column as defined in the source system.""" + display_name: Union[str, None] = msgspec.field(default=None, name="business_name") + """Alias for the column, to make its name more readable.""" + description: Union[str, None] = None + """Description of this column.""" + is_primary: Union[bool, None] = None + """When true, this column is the primary key for the table.""" + required: Union[bool, None] = None + """When true, this column is required for the table.""" + data_type: Union[str, None] = None + """Physical data type of values in this column.""" + local_type: Union[str, None] = None + """Logical data type of values in this column.""" + invalid_type: Union[str, None] = None + """Format of data to consider invalid.""" + invalid_format: Union[str, None] = None + """Format of data to consider valid.""" + valid_regex: Union[str, None] = None + """Regular expression to match valid values.""" + missing_regex: Union[str, None] = None + """Regular expression to match missing values.""" + invalid_values: list[str] = msgspec.field(default_factory=list) + """Enumeration of values that should be considered invalid.""" + valid_values: list[str] = msgspec.field(default_factory=list) + """Enumeration of values that should be considered valid.""" + missing_values: list[str] = msgspec.field(default_factory=list) + """Enumeration of values that should be considered missing.""" + not_null: Union[Any, None] = None + """When true, this column cannot be empty.""" + valid_length: Union[int, None] = None + """Fixed length for a string to be considered valid.""" + valid_min: Union[int, None] = None + """Minimum numeric value considered valid.""" + valid_max: Union[int, None] = None + """Maximum numeric value considered valid.""" + valid_min_length: Union[int, None] = None + """Minimum length for a string to be considered valid.""" + unique: Union[Any, None] = None + """When true, this column must have unique values.""" + tags: list = msgspec.field(default_factory=list) + """Atlan tags for this column.""" + terms: list[str] = msgspec.field(default_factory=list) + """Glossary terms assigned to this column.""" + + +class DataContractSpec(msgspec.Struct, kw_only=True): + """Capture the detailed specification of a data contract for an asset.""" + + kind: str = "DataContract" + """Controls the specification as one for a data contract.""" + status: Union[DataContractStatus, str] = "" + """State of the contract.""" + template_version: str = "0.0.2" + """Version of the template for the data contract.""" + type: str = "" + """Type of the dataset in Atlan.""" + dataset: str = "" + """Name of the asset as it exists inside Atlan.""" + data_source: Union[str, None] = None + """Name that must match a data source defined in your config file.""" + description: Union[str, None] = None + """Description of this dataset.""" + owners: Union[DataContractOwners, None] = None + """Owners of the dataset.""" + certification: Union[DataContractCertification, None] = None + """Certification to apply to the dataset.""" + announcement: Union[DataContractAnnouncement, None] = None + """Announcement to apply to the dataset.""" + terms: list[str] = msgspec.field(default_factory=list) + """Glossary terms to assign to the dataset.""" + tags: list[DCTag] = msgspec.field(default_factory=list) + """Atlan tags for the dataset.""" + custom_metadata_sets: Union[dict[str, Any], None] = msgspec.field( + default_factory=dict, name="custom_metadata" + ) + """Custom metadata for the dataset.""" + columns: list[DCColumn] = msgspec.field(default_factory=list) + """Details of each column in the dataset to be governed.""" + checks: list[str] = msgspec.field(default_factory=list) + """List of checks to run to verify data quality of the dataset.""" + extra_properties: dict[str, Any] = msgspec.field(default_factory=dict) + """Extra properties provided in the specification.""" + + # -- Alias mappings: YAML key → Python field name ---------------------- + _ALIAS_TO_FIELD: ClassVar[dict[str, str]] = { + "custom_metadata": "custom_metadata_sets", + } + _COLUMN_ALIAS_TO_FIELD: ClassVar[dict[str, str]] = { + "business_name": "display_name", + } + _TAG_ALIAS_TO_FIELD: ClassVar[dict[str, str]] = { + "restrict_propagation_through_lineage": "propagate_through_lineage", + "restrict_propagation_through_hierarchy": "propagate_through_hierarchy", + } + + @classmethod + def _remap_keys(cls, data: dict, mapping: dict[str, str]) -> dict: + """Remap aliased YAML keys to Python field names.""" + return {mapping.get(k, k): v for k, v in data.items()} + + @classmethod + def from_yaml(cls, yaml_str: str) -> DataContractSpec: + """ + Create an instance of DataContractSpec from a YAML string. + + :param yaml_str: YAML string to parse. + :returns: a DataContractSpec with attributes populated from the YAML data. + """ + data: dict = yaml.safe_load(yaml_str) + data = cls._remap_keys(data, cls._ALIAS_TO_FIELD) + + # Convert nested dicts to struct types + if "owners" in data and isinstance(data["owners"], dict): + data["owners"] = DataContractOwners(**data["owners"]) + if "certification" in data and isinstance(data["certification"], dict): + data["certification"] = DataContractCertification(**data["certification"]) + if "announcement" in data and isinstance(data["announcement"], dict): + data["announcement"] = DataContractAnnouncement(**data["announcement"]) + if "tags" in data and isinstance(data["tags"], list): + data["tags"] = [ + DCTag(**cls._remap_keys(t, cls._TAG_ALIAS_TO_FIELD)) + if isinstance(t, dict) + else t + for t in data["tags"] + ] + if "columns" in data and isinstance(data["columns"], list): + # Filter out invalid fields from column dicts + column_known_fields = {fi.name for fi in msgspec.structs.fields(DCColumn)} + # Also include the encoded names (business_name maps to display_name) + column_known_fields.update({"business_name"}) + + data["columns"] = [ + DCColumn( + **{ + k: v + for k, v in cls._remap_keys( + c, cls._COLUMN_ALIAS_TO_FIELD + ).items() + if k in column_known_fields + } + ) + if isinstance(c, dict) + else c + for c in data["columns"] + ] + + # Collect any extra keys not defined on the struct + known_fields = {fi.name for fi in msgspec.structs.fields(cls)} + extra = {k: data.pop(k) for k in list(data) if k not in known_fields} + spec = cls(**data) + if extra: + spec.extra_properties = extra + return spec + + def to_yaml(self, sort_keys: bool = False) -> str: + """ + Serialize the DataContractSpec to a YAML string. + + :param sort_keys: whether to sort keys in the YAML output. + :returns: a YAML string representation of this DataContractSpec. + """ + raw = msgspec.to_builtins(self) + return yaml.dump(raw, sort_keys=sort_keys) diff --git a/pyatlan_v9/model/conversion_utils.py b/pyatlan_v9/model/conversion_utils.py new file mode 100644 index 000000000..e99ed3d88 --- /dev/null +++ b/pyatlan_v9/model/conversion_utils.py @@ -0,0 +1,206 @@ +# Auto-generated support module for PythonMsgspecRenderer.pkl +"""Conversion utilities for relationship attribute handling.""" + +from __future__ import annotations + +from typing import Any, Type, TypeVar + +import msgspec +from msgspec import UNSET, UnsetType + +from pyatlan_v9.model.assets.related_entity import SaveSemantic + +T = TypeVar("T") + + +def categorize_relationships( + entity: Any, rel_fields: list[str], rel_attrs_class: Type[T] +) -> tuple[T | UnsetType, T | UnsetType, T | UnsetType]: + """ + Categorize relationship attributes by their SaveSemantic. + + Examines each relationship field on the entity and routes values to the + appropriate bucket (replace, append, remove) based on the SaveSemantic + marker on each related entity. + + Args: + entity: The entity containing relationship attributes + rel_fields: List of relationship field names + rel_attrs_class: The RelationshipAttributes class to instantiate + + Returns: + Tuple of (replace_rels, append_rels, remove_rels) as class instances, + or UNSET for empty buckets + """ + replace_kwargs: dict[str, Any] = {} + append_kwargs: dict[str, Any] = {} + remove_kwargs: dict[str, Any] = {} + + for field_name in rel_fields: + value = getattr(entity, field_name, UNSET) + if value is UNSET or value is None: + continue + + # Handle list of related entities + if isinstance(value, list): + if len(value) == 0: + # Empty list means "replace with nothing" (clear the relationship) + replace_kwargs[field_name] = [] + for item in value: + semantic = getattr(item, "semantic", UNSET) + if semantic is UNSET or semantic == SaveSemantic.REPLACE: + if field_name not in replace_kwargs: + replace_kwargs[field_name] = [] + replace_kwargs[field_name].append(item) + elif semantic == SaveSemantic.APPEND: + if field_name not in append_kwargs: + append_kwargs[field_name] = [] + append_kwargs[field_name].append(item) + elif semantic == SaveSemantic.REMOVE: + if field_name not in remove_kwargs: + remove_kwargs[field_name] = [] + remove_kwargs[field_name].append(item) + else: + # Single related entity + semantic = getattr(value, "semantic", UNSET) + if semantic is UNSET or semantic == SaveSemantic.REPLACE: + replace_kwargs[field_name] = value + elif semantic == SaveSemantic.APPEND: + append_kwargs[field_name] = value + elif semantic == SaveSemantic.REMOVE: + remove_kwargs[field_name] = value + + replace_rels = rel_attrs_class(**replace_kwargs) if replace_kwargs else UNSET + append_rels = rel_attrs_class(**append_kwargs) if append_kwargs else UNSET + remove_rels = rel_attrs_class(**remove_kwargs) if remove_kwargs else UNSET + + return replace_rels, append_rels, remove_rels + + +def merge_relationships( + replace_rels: Any, + append_rels: Any, + remove_rels: Any, + rel_fields: list[str], + rel_attrs_class: Type[T], +) -> dict[str, Any]: + """ + Merge relationship attributes from all three buckets back into a flat dict. + + Used when converting from nested API format back to flat entity format. + Values are merged with replace taking priority, then append, then remove. + + Args: + replace_rels: RelationshipAttributes for replace semantic + append_rels: RelationshipAttributes for append semantic + remove_rels: RelationshipAttributes for remove semantic + rel_fields: List of relationship field names + rel_attrs_class: The RelationshipAttributes class (unused, for type info) + + Returns: + Dict of merged relationship attributes + """ + result: dict[str, Any] = {} + + # Merge in order of priority: replace, then append, then remove + for source in [replace_rels, append_rels, remove_rels]: + if source is UNSET or source is None: + continue + for field_name in rel_fields: + value = getattr(source, field_name, UNSET) + if value is not UNSET and field_name not in result: + result[field_name] = value + + return result + + +def build_attributes_kwargs(entity: Any, attributes_class: Type) -> dict[str, Any]: + """ + Build kwargs dictionary for attributes from an entity using dynamic field extraction. + + Extracts all fields from attributes_class and gets their values from entity. + This avoids manual enumeration of all fields. + + Args: + entity: The entity to extract attribute values from + attributes_class: The Attributes class defining which fields to extract + + Returns: + Dict of attribute name -> value pairs for all fields in attributes_class + + Example: + >>> attrs_kwargs = build_attributes_kwargs(table, TableAttributes) + >>> attrs = TableAttributes(**attrs_kwargs) + """ + attr_field_names = {f.name for f in msgspec.structs.fields(attributes_class)} + + return { + name: getattr(entity, name) + for name in attr_field_names + if hasattr(entity, name) + } + + +def build_flat_kwargs( + nested: Any, + attrs: Any, + merged_rels: dict[str, Any], + nested_class: Type, + attributes_class: Type, +) -> dict[str, Any]: + """ + Build kwargs dictionary for flat entity from nested format using dynamic field extraction. + + Extracts fields from nested entity, attributes, and merged relationships. + This avoids manual enumeration of all fields. + + Args: + nested: The nested entity containing top-level fields + attrs: The attributes object containing attribute fields + merged_rels: Dict of merged relationship attributes + nested_class: The Nested class defining top-level fields + attributes_class: The Attributes class defining attribute fields + + Returns: + Dict of field name -> value pairs for creating flat entity + + Example: + >>> kwargs = build_flat_kwargs( + ... nested, attrs, merged_rels, + ... TableNested, TableAttributes + ... ) + >>> table = Table(**kwargs) + """ + # Get top-level field names (exclude attributes and relationship fields) + top_level_fields = { + f.name + for f in msgspec.structs.fields(nested_class) + if f.name + not in ( + "attributes", + "relationship_attributes", + "append_relationship_attributes", + "remove_relationship_attributes", + ) + } + + # Get attribute field names + attr_field_names = {f.name for f in msgspec.structs.fields(attributes_class)} + + # Build kwargs: top-level fields + attribute fields + relationships + kwargs = {} + + # Add top-level fields from nested + for name in top_level_fields: + if hasattr(nested, name): + kwargs[name] = getattr(nested, name) + + # Add attribute fields from attrs + for name in attr_field_names: + if hasattr(attrs, name): + kwargs[name] = getattr(attrs, name) + + # Add merged relationships + kwargs.update(merged_rels) + + return kwargs diff --git a/pyatlan_v9/model/core.py b/pyatlan_v9/model/core.py new file mode 100644 index 000000000..6ea64878e --- /dev/null +++ b/pyatlan_v9/model/core.py @@ -0,0 +1,391 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Core model classes for pyatlan_v9, migrated from pyatlan/model/core.py. + +This module provides: +- AtlanTagName: Custom string-like class with sentinel pattern (plain Python class) +- Announcement: Announcement data (msgspec.Struct) +- AtlanTag: Classification/tag assignment with propagation settings (msgspec.Struct) +- Meaning: Glossary term reference (msgspec.Struct) +- AssetResponse: API response wrapper for single assets (msgspec.Struct) +- AssetRequest: API request wrapper for single assets (msgspec.Struct) +- BulkRequest: API request wrapper for bulk asset operations (msgspec.Struct) +""" + +from __future__ import annotations + +import json +from typing import TYPE_CHECKING, Any, Union + +import msgspec + +from pyatlan.model.constants import DELETED_, DELETED_SENTINEL +from pyatlan.model.enums import AnnouncementType, EntityStatus +from pyatlan_v9.model.retranslators import AtlanTagRetranslator +from pyatlan_v9.model.structs import SourceTagAttachment +from pyatlan_v9.model.translators import AtlanTagTranslator + +if TYPE_CHECKING: + from pyatlan.client.aio.client import AsyncAtlanClient + from pyatlan.client.atlan import AtlanClient + + +# ============================================================================= +# ATLAN TAG NAME (plain Python class -- NOT a msgspec.Struct) +# ============================================================================= + + +class AtlanTagName: + """ + A custom string-like class representing an Atlan tag name. + + Uses a sentinel pattern so that deleted tags are represented by a single + shared instance, allowing identity comparison. + """ + + _sentinel: Union[AtlanTagName, None] = None + + def __new__(cls, *args: Any, **kwargs: Any) -> AtlanTagName: + if args and args[0] == DELETED_SENTINEL and cls._sentinel: + return cls._sentinel + obj = super().__new__(cls) + if args and args[0] == DELETED_SENTINEL: + obj._display_text = DELETED_ + cls._sentinel = obj + return obj + + def __init__(self, display_text: str) -> None: + self._display_text = display_text + + @classmethod + def get_deleted_sentinel(cls) -> AtlanTagName: + """Return an AtlanTagName that is a sentinel object to represent deleted tags.""" + return cls._sentinel or cls.__new__( + cls, DELETED_SENTINEL + ) # Because __new__ is being invoked directly, __init__ won't be called + + @classmethod + def __get_validators__(cls): + yield cls._convert_to_tag_name + + def __str__(self) -> str: + return self._display_text + + def __repr__(self) -> str: + return f"AtlanTagName({self._display_text.__repr__()})" + + def __hash__(self) -> int: + return self._display_text.__hash__() + + def __eq__(self, other: object) -> bool: + return ( + isinstance(other, AtlanTagName) + and self._display_text == other._display_text + ) + + @classmethod + def _convert_to_tag_name(cls, data: Any) -> AtlanTagName: + if isinstance(data, AtlanTagName): + return data + return AtlanTagName(data) if data else cls.get_deleted_sentinel() + + +# ============================================================================= +# RESPONSE / REQUEST TRANSLATION WRAPPERS +# ============================================================================= + + +class AtlanResponse: + """ + Wrapper that translates backend-oriented payloads into user-friendly values. + """ + + def __init__(self, raw_json: dict[str, Any], client: Any): + self.raw_json = raw_json + self.client = client + self.translators = [ + AtlanTagTranslator(client), + ] + self.translated = self._deep_translate(self.raw_json) + + def _deep_translate(self, data: Any) -> Any: + if isinstance(data, dict): + translated = data + for translator in self.translators: + if translator.applies_to(translated): + translated = translator.translate(translated) + return { + key: self._deep_translate(value) for key, value in translated.items() + } + if isinstance(data, list): + return [self._deep_translate(item) for item in data] + return data + + def to_dict(self) -> Any: + """Return translated payload as Python builtins.""" + return self.translated + + +class AtlanRequest: + """ + Wrapper that retranslates user-friendly payloads into backend format. + """ + + def __init__(self, instance: Any, client: Any): + self.instance = instance + self.client = client + self.retranslators = [ + AtlanTagRetranslator(client), + ] + + if isinstance(instance, (dict, list)): + raw_json = instance + elif hasattr(instance, "to_json") and callable(instance.to_json): + raw_json = json.loads(instance.to_json(nested=True)) + elif hasattr(instance, "to_dict") and callable(instance.to_dict): + raw_json = instance.to_dict() + else: + raw_json = msgspec.to_builtins(instance) + self.translated = self._deep_retranslate(raw_json) + + def _deep_retranslate(self, data: Any) -> Any: + if isinstance(data, dict): + translated = data + for retranslator in self.retranslators: + if retranslator.applies_to(translated): + translated = retranslator.retranslate(translated) + return { + key: self._deep_retranslate(value) for key, value in translated.items() + } + if isinstance(data, list): + return [self._deep_retranslate(item) for item in data] + return data + + def json(self, **kwargs: Any) -> str: + """Return retranslated payload as JSON text.""" + return json.dumps(self.translated, **kwargs) + + +# ============================================================================= +# ANNOUNCEMENT +# ============================================================================= + + +class Announcement(msgspec.Struct, kw_only=True): + """ + Data class representing an announcement that can be attached to an asset. + """ + + announcement_title: str + """Title of the announcement.""" + + announcement_type: AnnouncementType + """Type of the announcement (INFORMATION, WARNING, or ISSUE).""" + + announcement_message: Union[str, None] = None + """Optional detailed message for the announcement.""" + + +# ============================================================================= +# ATLAN TAG +# ============================================================================= + + +class AtlanTag(msgspec.Struct, kw_only=True, rename="camel"): + """ + Represents an Atlan classification/tag assignment on an entity. + + Includes propagation settings that control how the tag spreads + through lineage and hierarchy relationships. + """ + + type_name: Union[str, AtlanTagName, None] = None + """Name of the type definition that defines this instance.""" + + entity_guid: Union[str, None] = None + """Unique identifier of the entity instance.""" + + entity_status: Union[EntityStatus, None] = None + """Status of the entity (ACTIVE or DELETED).""" + + propagate: Union[bool, None] = False + """Whether to propagate the Atlan tag (True) or not (False).""" + + remove_propagations_on_entity_delete: Union[bool, None] = True + """Whether to remove propagated Atlan tags when the tag is removed from this asset.""" + + restrict_propagation_through_lineage: Union[bool, None] = False + """Whether to avoid propagating through lineage (True) or propagate through lineage (False).""" + + restrict_propagation_through_hierarchy: Union[bool, None] = False + """Whether to prevent this Atlan tag from propagating through hierarchy (True) or allow it (False).""" + + validity_periods: Union[list[str], None] = None + """Time periods during which this tag assignment is valid.""" + + attributes: Union[dict[str, Any], None] = None + """Custom attributes for this tag assignment (e.g., source tag attachments).""" + + source_tag_attachments: list[SourceTagAttachment] = msgspec.field( + default_factory=list + ) + """Source tag attachments extracted from classification attributes.""" + + @classmethod + def of( + cls, + atlan_tag_name: AtlanTagName, + entity_guid: Union[str, None] = None, + source_tag_attachment: Union[SourceTagAttachment, None] = None, + client: Union[AtlanClient, None] = None, + ) -> AtlanTag: + """ + Construct an Atlan tag assignment for a specific entity. + + :param atlan_tag_name: human-readable name of the Atlan tag + :param entity_guid: unique identifier (GUID) of the entity to tag + :param source_tag_attachment: (optional) source-specific details for the tag + :param client: (optional) client instance used for translating source-specific details + :returns: an Atlan tag assignment with default settings for propagation + :raises InvalidRequestError: if client is not provided and source_tag_attachment is specified + """ + from pyatlan.errors import ErrorCode + + tag = AtlanTag(type_name=atlan_tag_name) + if entity_guid: + tag.entity_guid = entity_guid + tag.entity_status = EntityStatus.ACTIVE + if source_tag_attachment: + if not client: + raise ErrorCode.NO_ATLAN_CLIENT.exception_with_parameters() + tag_id = client.atlan_tag_cache.get_id_for_name(str(atlan_tag_name)) + source_tag_attr_id = client.atlan_tag_cache.get_source_tags_attr_id( + tag_id or "" + ) + tag.attributes = {source_tag_attr_id: [source_tag_attachment]} # type: ignore[dict-item] + tag.source_tag_attachments.append(source_tag_attachment) + return tag + + @classmethod + async def of_async( + cls, + atlan_tag_name: AtlanTagName, + entity_guid: Union[str, None] = None, + source_tag_attachment: Union[SourceTagAttachment, None] = None, + client: Union[AsyncAtlanClient, None] = None, + ) -> AtlanTag: + """ + Async version of AtlanTag.of() for use with AsyncAtlanClient. + + Construct an Atlan tag assignment for a specific entity. + + :param atlan_tag_name: human-readable name of the Atlan tag + :param entity_guid: unique identifier (GUID) of the entity to tag + :param source_tag_attachment: (optional) source-specific details for the tag + :param client: (optional) async client instance used for translating source-specific details + :returns: an Atlan tag assignment with default settings for propagation + :raises InvalidRequestError: if client is not provided and source_tag_attachment is specified + """ + from pyatlan.errors import ErrorCode + + tag = AtlanTag(type_name=atlan_tag_name) + if entity_guid: + tag.entity_guid = entity_guid + tag.entity_status = EntityStatus.ACTIVE + if source_tag_attachment: + if not client: + raise ErrorCode.NO_ATLAN_CLIENT.exception_with_parameters() + tag_id = await client.atlan_tag_cache.get_id_for_name(str(atlan_tag_name)) + source_tag_attr_id = await client.atlan_tag_cache.get_source_tags_attr_id( + tag_id or "" + ) + tag.attributes = {source_tag_attr_id: [source_tag_attachment]} # type: ignore[dict-item] + tag.source_tag_attachments.append(source_tag_attachment) + return tag + + +# ============================================================================= +# MEANING +# ============================================================================= + + +class Meaning(msgspec.Struct, kw_only=True, rename="camel"): + """ + Represents a reference to a glossary term assigned to an entity. + """ + + term_guid: Union[str, None] = None + """Unique identifier (GUID) of the related term.""" + + relation_guid: Union[str, None] = None + """Unique identifier (GUID) of the relationship itself.""" + + display_text: Union[str, None] = None + """Human-readable display name of the related term.""" + + confidence: Union[int, None] = None + """Confidence score for the term assignment.""" + + +# ============================================================================= +# API REQUEST / RESPONSE WRAPPERS +# ============================================================================= + + +class AssetResponse(msgspec.Struct, kw_only=True, rename="camel"): + """ + Wrapper for single-asset API responses. + + Wraps the entity returned by the Atlan API along with any referred entities. + """ + + entity: Any + """The primary asset entity returned by the API.""" + + referred_entities: Union[dict[str, Any], None] = None + """Map of related entities keyed by GUID.""" + + +class AssetRequest(msgspec.Struct, kw_only=True, rename="camel"): + """ + Wrapper for single-asset API requests. + """ + + entity: Any + """The asset entity to send to the API.""" + + +class BulkRequest(msgspec.Struct, kw_only=True, rename="camel"): + """ + Wrapper for bulk asset API requests. + + In v9, relationship categorization (replace/append/remove semantics) is + handled by each entity's ``to_json(nested=True)`` conversion, which calls + ``categorize_relationships()`` under the hood. + """ + + entities: list[Any] + """List of asset entities to send to the API in bulk.""" + + def to_dict(self) -> dict: + """ + Convert to a dict in the API nested format. + + Each entity is converted to its nested representation (with + ``attributes``, ``relationshipAttributes``, + ``appendRelationshipAttributes``, ``removeRelationshipAttributes``). + + Returns: + Dict suitable for JSON serialization and API submission. + """ + entity_dicts = [] + for entity in self.entities: + if hasattr(entity, "to_nested_dict"): + entity_dicts.append(entity.to_nested_dict()) + elif hasattr(entity, "to_json"): + entity_dicts.append(json.loads(entity.to_json(nested=True))) + else: + entity_dicts.append(msgspec.to_builtins(entity)) + return {"entities": entity_dicts} diff --git a/pyatlan_v9/model/credential.py b/pyatlan_v9/model/credential.py new file mode 100644 index 000000000..9f649bf20 --- /dev/null +++ b/pyatlan_v9/model/credential.py @@ -0,0 +1,118 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +from __future__ import annotations + +from typing import Any, Union + +import msgspec + + +class Credential(msgspec.Struct, kw_only=True, omit_defaults=True, rename="camel"): + """Credential used for connectivity to external systems.""" + + id: Union[str, None] = None + """Unique identifier (GUID) of the credential.""" + + name: Union[str, None] = None + """Name of the credential.""" + + description: Union[str, None] = None + """Description of the credential.""" + + host: Union[str, None] = None + """Hostname for which connectivity is defined by the credential.""" + + port: Union[int, None] = None + """Port number on which connectivity should be done.""" + + auth_type: Union[str, None] = None + """Authentication mechanism represented by the credential.""" + + connector_type: Union[str, None] = None + """Type of connector used by the credential.""" + + username: Union[str, None] = None + """Less sensitive portion of the credential (e.g. username or client ID).""" + + password: Union[str, None] = None + """More sensitive portion of the credential (e.g. password or client secret).""" + + extras: Union[dict[str, Any], None] = msgspec.field(default=None, name="extra") + """Additional details about the credential (e.g. database, role, warehouse).""" + + connector_config_name: Union[str, None] = None + """Name of the connector configuration responsible for managing the credential.""" + + metadata: Union[dict[str, Any], None] = None + + level: Union[dict[str, Any], str, None] = None + + connector: Union[str, None] = None + """Name of the connector used by the credential.""" + + +class CredentialResponse(msgspec.Struct, kw_only=True, rename="camel"): + """Response from a credential lookup.""" + + id: Union[str, None] = None + version: Union[str, None] = None + is_active: Union[bool, None] = None + created_at: Union[int, None] = None + updated_at: Union[int, None] = None + created_by: Union[str, None] = None + tenant_id: Union[str, None] = None + name: Union[str, None] = None + description: Union[str, None] = None + connector_config_name: Union[str, None] = None + connector: Union[str, None] = None + connector_type: Union[str, None] = None + auth_type: Union[str, None] = None + host: Union[str, None] = None + port: Union[int, None] = None + metadata: Union[dict[str, Any], None] = None + level: Union[dict[str, Any], str, None] = None + connection: Union[dict[str, Any], str, None] = None + username: Union[str, None] = None + extras: Union[dict[str, Any], None] = msgspec.field(default=None, name="extra") + + def to_credential(self) -> Credential: + """ + Convert this response into a credential instance. + + Note: The password field must still be populated manually, + as it will never be returned by a credential lookup for security reasons. + """ + return Credential( + id=self.id, + name=self.name, + host=self.host, + port=self.port, + auth_type=self.auth_type, + connector_type=self.connector_type, + connector_config_name=self.connector_config_name, + username=self.username, + extras=self.extras, + ) + + +class CredentialListResponse(msgspec.Struct, kw_only=True, rename="camel"): + """Response containing a list of CredentialResponse objects.""" + + records: list[CredentialResponse] = msgspec.field(default_factory=list) + """List of credential records returned.""" + + +class CredentialTestResponse(msgspec.Struct, kw_only=True, rename="camel"): + """Response from testing a credential's connectivity.""" + + code: Union[int, None] = None + error: Union[str, None] = None + info: Union[object, None] = None + message: str + request_id: Union[str, None] = None + + @property + def is_successful(self) -> bool: + """Whether the test was successful (True) or failed (False).""" + return self.message == "successful" diff --git a/pyatlan_v9/model/custom_metadata.py b/pyatlan_v9/model/custom_metadata.py new file mode 100644 index 000000000..22fb80ac1 --- /dev/null +++ b/pyatlan_v9/model/custom_metadata.py @@ -0,0 +1,207 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +from __future__ import annotations + +from collections import UserDict +from typing import TYPE_CHECKING, Any, Union + +from pyatlan.errors import NotFoundError +from pyatlan.model.constants import DELETED_, DELETED_SENTINEL + +if TYPE_CHECKING: + from pyatlan.client.atlan import AtlanClient + + +class CustomMetadataDict(UserDict): + """ + Allows the manipulation of a set of custom metadata attributes + using the human-readable names. + """ + + _sentinel: Union[CustomMetadataDict, None] = None + + def __new__(cls, *args, **kwargs): + if args and args[0] == DELETED_SENTINEL and cls._sentinel: + return cls._sentinel + obj = super().__new__(cls) + super().__init__(obj) + if args and args[0] == DELETED_SENTINEL: + obj._name = DELETED_ + obj._modified = False + obj._names: set[str] = set() + cls._sentinel = obj + return obj + + @property + def attribute_names(self) -> set[str]: + """Names of all attributes in this custom metadata set.""" + return self._names + + def __init__(self, client: AtlanClient, name: str): + """Init CustomMetadataDict with the human-readable name of a custom metadata set.""" + super().__init__() + self._name = name + self._modified = False + self._client = client + _id = self._client.custom_metadata_cache.get_id_for_name(name) + self._names = { + value + for key, value in self._client.custom_metadata_cache.map_attr_id_to_name[ + _id + ].items() + if not self._client.custom_metadata_cache.is_attr_archived(attr_id=key) + } + + @classmethod + def get_deleted_sentinel(cls) -> CustomMetadataDict: + """Return a sentinel CustomMetadataDict representing deleted custom metadata.""" + if cls._sentinel is not None: + return cls._sentinel + return cls.__new__(cls, DELETED_SENTINEL) + + @property + def modified(self) -> bool: + """Whether the set has been modified from its initial values.""" + return self._modified + + def __setitem__(self, key: str, value): + """Set a property value using the human-readable name as the key.""" + if key not in self._names: + raise KeyError(f"'{key}' is not a valid property name for {self._name}") + self._modified = True + self.data[key] = value + + def __getitem__(self, key: str): + """Retrieve a property value using the human-readable name as the key.""" + if key not in self._names: + raise KeyError(f"'{key}' is not a valid property name for {self._name}") + return None if key not in self.data else self.data[key] + + def clear_all(self): + """Set all properties to None.""" + for attribute_name in self._names: + self.data[attribute_name] = None + self._modified = True + + def clear_unset(self): + """Set all properties that haven't been set to None.""" + for name in self.attribute_names: + if name not in self.data: + self.data[name] = None + + def is_set(self, key: str) -> bool: + """Whether the given property has been set in the metadata set.""" + if key not in self._names: + raise KeyError(f"'{key}' is not a valid property name for {self._name}") + return key in self.data + + @property + def business_attributes(self) -> dict[str, Any]: + """Return the metadata set with names resolved to their internal values.""" + return { + self._client.custom_metadata_cache.get_attr_id_for_name( + self._name, key + ): value + for (key, value) in self.data.items() + } + + +class CustomMetadataProxy: + """Proxy for accessing and managing custom metadata on an asset.""" + + def __init__( + self, + client: AtlanClient, + business_attributes: Union[dict[str, Any], None], + ): + self._client = client + self._metadata: Union[dict[str, CustomMetadataDict], None] = None + self._business_attributes = business_attributes + self._modified = False + if self._business_attributes is None: + return + self._metadata = {} + for cm_id, cm_attributes in self._business_attributes.items(): + try: + cm_name = self._client.custom_metadata_cache.get_name_for_id(cm_id) + attribs = CustomMetadataDict(name=cm_name, client=self._client) + for attr_id, properties in cm_attributes.items(): + attr_name = self._client.custom_metadata_cache.get_attr_name_for_id( + cm_id, attr_id + ) + if not self._client.custom_metadata_cache.is_attr_archived( + attr_id=attr_id + ): + attribs[attr_name] = properties + attribs._modified = False + except NotFoundError: + cm_name = DELETED_ + attribs = CustomMetadataDict.get_deleted_sentinel() + self._metadata[cm_name] = attribs + + def get_custom_metadata(self, name: str) -> CustomMetadataDict: + """Get or create a custom metadata set by name.""" + if self._metadata is None: + self._metadata = {} + if name not in self._metadata: + attribs = CustomMetadataDict(name=name, client=self._client) + self._metadata[name] = attribs + return self._metadata[name] + + def set_custom_metadata(self, custom_metadata: CustomMetadataDict): + """Set a custom metadata set.""" + if self._metadata is None: + self._metadata = {} + self._metadata[custom_metadata._name] = custom_metadata + self._modified = True + + @property + def modified(self) -> bool: + """Whether any custom metadata has been modified.""" + if self._modified: + return True + if self._metadata is None: + return False + return any(metadata_dict.modified for metadata_dict in self._metadata.values()) + + @property + def business_attributes(self) -> Union[dict[str, Any], None]: + """Return the business attributes in internal format.""" + if self.modified and self._metadata is not None: + return { + self._client.custom_metadata_cache.get_id_for_name( + key + ): value.business_attributes + for key, value in self._metadata.items() + } + return self._business_attributes + + +class CustomMetadataRequest: + """ + Request to update custom metadata on an asset. + + Replaces the Pydantic __root__ pattern with a simple dict wrapper. + """ + + def __init__(self, data: dict[str, Any], set_id: str): + self._data = data + self._set_id = set_id + + @classmethod + def create(cls, custom_metadata_dict: CustomMetadataDict) -> CustomMetadataRequest: + """Create a request from a CustomMetadataDict.""" + set_id = custom_metadata_dict._client.custom_metadata_cache.get_id_for_name( + custom_metadata_dict._name + ) + return cls(data=custom_metadata_dict.business_attributes, set_id=set_id) + + @property + def custom_metadata_set_id(self) -> str: + """Unique identifier of the custom metadata set.""" + return self._set_id + + def to_dict(self) -> dict[str, Any]: + """Return the underlying data dict.""" + return self._data diff --git a/pyatlan_v9/model/data_mesh.py b/pyatlan_v9/model/data_mesh.py new file mode 100644 index 000000000..81faff3c7 --- /dev/null +++ b/pyatlan_v9/model/data_mesh.py @@ -0,0 +1,157 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Data mesh models for pyatlan_v9, migrated from pyatlan/model/data_mesh.py. + +This module provides: +- DataProductsAssetsDSL: Data products assets DSL for defining asset selection. +""" + +from __future__ import annotations + +from json import dumps, loads +from typing import Any, Dict, List + +import msgspec + +from pyatlan.errors import ErrorCode +from pyatlan_v9.model.search import DSL, IndexSearchRequest + +_ATTR_LIST: List[str] = [ + "__traitNames", + "connectorName", + "__customAttributes", + "certificateStatus", + "tenantId", + "anchor", + "parentQualifiedName", + "Query.parentQualifiedName", + "AtlasGlossaryTerm.anchor", + "databaseName", + "schemaName", + "parent", + "connectionQualifiedName", + "collectionQualifiedName", + "announcementMessage", + "announcementTitle", + "announcementType", + "announcementUpdatedAt", + "announcementUpdatedBy", + "allowQuery", + "allowQueryPreview", + "adminGroups", + "adminRoles", + "adminUsers", + "category", + "credentialStrategy", + "connectionSSOCredentialGuid", + "certificateStatus", + "certificateUpdatedAt", + "certificateUpdatedBy", + "classifications", + "connectionId", + "connectionQualifiedName", + "connectorName", + "dataType", + "defaultDatabaseQualifiedName", + "defaultSchemaQualifiedName", + "description", + "displayName", + "links", + "link", + "meanings", + "name", + "ownerGroups", + "ownerUsers", + "qualifiedName", + "typeName", + "userDescription", + "displayDescription", + "subDataType", + "rowLimit", + "queryTimeout", + "previewCredentialStrategy", + "policyStrategy", + "policyStrategyForSamplePreview", + "useObjectStorage", + "objectStorageUploadThreshold", + "outputPortDataProducts", +] + + +class DataProductsAssetsDSL(msgspec.Struct, kw_only=True): + """Data products assets DSL for defining asset selection in data products.""" + + query: IndexSearchRequest + """Parameters for the search itself.""" + + filter_scrubbed: bool = True + """Whether or not to filter scrubbed records.""" + + def _exclude_nulls(self, dict_: Dict[str, Any]) -> Dict[str, Any]: + """Remove null/empty values from a dictionary.""" + return { + key: value for key, value in dict_.items() if value not in (None, [], {}) + } + + def _contruct_dsl_str(self, asset_selection_dsl: Dict[str, Any]) -> str: + """Restructure DSL for data products format.""" + try: + filter_condition = asset_selection_dsl["query"]["dsl"]["query"]["bool"].pop( + "filter" + ) + asset_selection_dsl["query"]["dsl"]["query"]["bool"]["filter"] = { + "bool": {"filter": filter_condition} + } + except KeyError: + raise ErrorCode.UNABLE_TO_TRANSLATE_ASSETS_DSL.exception_with_parameters() from None + return dumps(asset_selection_dsl) + + def to_string(self) -> str: + """ + Convert to selected assets DSL JSON string for the data product. + + :returns: selected assets DSL JSON string for the data product. + :raises: InvalidRequestError if the query provided is invalid. + """ + search_request = IndexSearchRequest( + dsl=DSL( + track_total_hits=None, + query=self.query.dsl.query, + ), + suppress_logs=True, + request_metadata=None, + exclude_meanings=None, + show_search_score=None, + exclude_atlan_tags=None, + allow_deleted_relations=None, + attributes=_ATTR_LIST, + ) + inner = DataProductsAssetsDSL(query=search_request) + # Serialize, excluding sort/size from DSL + query_dict = search_request.to_dict() + query_dict.pop("requestMetadata", None) + inner_dict = { + "query": query_dict, + "filterScrubbed": inner.filter_scrubbed, + } + # Remove sort and size from the inner DSL + if "dsl" in inner_dict.get("query", {}): + inner_dict["query"]["dsl"].pop("sort", None) + inner_dict["query"]["dsl"].pop("size", None) + dsl_json_str = dumps(inner_dict) + asset_selection_dsl = dict(loads(dsl_json_str, object_hook=self._exclude_nulls)) + return self._contruct_dsl_str(asset_selection_dsl) + + @staticmethod + def get_asset_selection(search_request: IndexSearchRequest) -> str: + """ + Returns the selection of assets for the data product. + + :param search_request: index search request that + defines the assets to include in the data product + :returns: search DSL used to define + which assets are part of this data product. + """ + return DataProductsAssetsDSL(query=search_request).to_string() diff --git a/pyatlan_v9/model/dq_rule_conditions.py b/pyatlan_v9/model/dq_rule_conditions.py new file mode 100644 index 000000000..398e6a03c --- /dev/null +++ b/pyatlan_v9/model/dq_rule_conditions.py @@ -0,0 +1,181 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +from __future__ import annotations + +import json +from typing import Any, Union + +import msgspec + +from pyatlan.errors import ErrorCode +from pyatlan.model.enums import DataQualityRuleTemplateConfigRuleConditions +from pyatlan.utils import validate_required_fields, validate_type + + +class DQCondition(msgspec.Struct, kw_only=True): + """Data quality rule condition.""" + + type: DataQualityRuleTemplateConfigRuleConditions + """Condition type.""" + + value: Union[str, int, list[str], dict[str, Any], None] = None + """Condition value.""" + + min_value: Union[int, None] = None + """Minimum value for range-based conditions.""" + + max_value: Union[int, None] = None + """Maximum value for range-based conditions.""" + + reference_table: Union[str, None] = None + """Qualified name of the reference table for IN_LIST_REFERENCE condition.""" + + reference_column: Union[str, None] = None + """Qualified name of the reference column for IN_LIST_REFERENCE condition.""" + + target_table: Union[str, None] = None + """Qualified name of the target table for reconciliation conditions.""" + + target_column: Union[str, None] = None + """Qualified name of the target column for reconciliation conditions.""" + + def __post_init__(self) -> None: + """Validate condition fields based on the condition type.""" + DQRTCRC = DataQualityRuleTemplateConfigRuleConditions + + if self.type == DQRTCRC.STRING_LENGTH_BETWEEN: + validate_required_fields( + ["min_value", "max_value"], [self.min_value, self.max_value] + ) + if (self.min_value is not None and self.min_value < 0) or ( + self.max_value is not None and self.max_value < 0 + ): + raise ErrorCode.INVALID_PARAMETER_VALUE.exception_with_parameters( + f"min_value={self.min_value}, max_value={self.max_value}", + "min_value, max_value", + "non-negative integers", + ) + if ( + self.min_value is not None + and self.max_value is not None + and self.min_value > self.max_value + ): + raise ErrorCode.INVALID_PARAMETER_VALUE.exception_with_parameters( + f"min_value={self.min_value}, max_value={self.max_value}", + "min_value, max_value", + "min_value <= max_value", + ) + elif self.type == DQRTCRC.IN_LIST_REFERENCE: + validate_required_fields( + ["reference_table", "reference_column"], + [self.reference_table, self.reference_column], + ) + elif self.type == DQRTCRC.ROW_COUNT_RECON: + validate_required_fields(["target_table"], [self.target_table]) + elif self.type in [ + DQRTCRC.AVERAGE_RECON, + DQRTCRC.SUM_RECON, + DQRTCRC.DUPLICATE_COUNT_RECON, + DQRTCRC.UNIQUE_COUNT_RECON, + ]: + validate_required_fields( + ["target_table", "target_column"], + [self.target_table, self.target_column], + ) + else: + validate_required_fields(["value"], [self.value]) + if self.type in [DQRTCRC.IN_LIST, DQRTCRC.NOT_IN_LIST]: + validate_type("value", list, self.value) + elif self.type in [DQRTCRC.REGEX_MATCH, DQRTCRC.REGEX_NOT_MATCH]: + validate_type("value", str, self.value) + + def to_dict(self) -> dict[str, Any]: + """Convert to dict suitable for API submission.""" + DQRTCRC = DataQualityRuleTemplateConfigRuleConditions + result: dict[str, Any] = {"type": self.type.value} + + if self.type == DQRTCRC.STRING_LENGTH_BETWEEN: + result["value"] = {"minValue": self.min_value, "maxValue": self.max_value} + elif self.type == DQRTCRC.IN_LIST_REFERENCE: + result["value"] = { + "reference_table": self.reference_table, + "reference_column": self.reference_column, + } + elif self.type == DQRTCRC.ROW_COUNT_RECON: + result["value"] = {"target_table": self.target_table} + elif self.type in [ + DQRTCRC.AVERAGE_RECON, + DQRTCRC.SUM_RECON, + DQRTCRC.DUPLICATE_COUNT_RECON, + DQRTCRC.UNIQUE_COUNT_RECON, + ]: + result["value"] = { + "target_table": self.target_table, + "target_column": self.target_column, + } + else: + result["value"] = {"value": self.value} + + return result + + +class DQRuleConditionsBuilder: + """Builder for data quality rule conditions.""" + + def __init__(self) -> None: + self._conditions: list[DQCondition] = [] + + def add_condition( + self, + type: DataQualityRuleTemplateConfigRuleConditions, + value: Union[str, int, list[str], None] = None, + min_value: Union[int, None] = None, + max_value: Union[int, None] = None, + reference_table: Union[str, None] = None, + reference_column: Union[str, None] = None, + target_table: Union[str, None] = None, + target_column: Union[str, None] = None, + ) -> DQRuleConditionsBuilder: + """ + Add a condition to the builder. + + :param type: the condition type enum value + :param value: value of type str, int, or list depending on condition type + :param min_value: minimum value for range-based conditions + :param max_value: maximum value for range-based conditions + :param reference_table: qualified name of the reference table + :param reference_column: qualified name of the reference column + :param target_table: qualified name of the target table + :param target_column: qualified name of the target column + :returns: the builder for method chaining + """ + self._conditions.append( + DQCondition( + type=type, + value=value, + min_value=min_value, + max_value=max_value, + reference_table=reference_table, + reference_column=reference_column, + target_table=target_table, + target_column=target_column, + ) + ) + return self + + def build(self) -> str: + """ + Build the conditions JSON string. + + :returns: JSON string of the conditions + :raises: InvalidRequestError if conditions list is empty + """ + if not self._conditions: + raise ErrorCode.INVALID_PARAMETER_VALUE.exception_with_parameters( + "empty conditions list", "conditions", "at least one condition" + ) + + return json.dumps( + {"conditions": [condition.to_dict() for condition in self._conditions]} + ) diff --git a/pyatlan_v9/model/enums.py b/pyatlan_v9/model/enums.py new file mode 100644 index 000000000..9387af873 --- /dev/null +++ b/pyatlan_v9/model/enums.py @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. +"""Re-export all enums from legacy module for v9 compatibility.""" + +from pyatlan.model.enums import * # noqa: F401,F403 diff --git a/pyatlan_v9/model/events.py b/pyatlan_v9/model/events.py new file mode 100644 index 000000000..69f0570f8 --- /dev/null +++ b/pyatlan_v9/model/events.py @@ -0,0 +1,278 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2023 Atlan Pte. Ltd. + +from __future__ import annotations + +from typing import Any, Literal, Union + +import msgspec + +from pyatlan_v9.model.assets.asset import Asset +from pyatlan_v9.model.core import AtlanTag + +# ============================================================================= +# EVENT PAYLOAD TYPES +# ============================================================================= + + +class AtlanEventPayload(msgspec.Struct, kw_only=True, rename="camel"): + """Base payload for Atlan events.""" + + event_type: Union[str, None] = msgspec.field(default=None, name="type") + """Type of the event payload.""" + + operation_type: str = "" + """Type of the operation the event contains a payload for.""" + + event_time: Union[int, None] = None + """Time (epoch) the event was triggered in the source system, in milliseconds.""" + + asset: Union[Asset, None] = msgspec.field(default=None, name="entity") + """Details of the asset that was impacted by the event.""" + + +class AssetCreatePayload(msgspec.Struct, kw_only=True, rename="camel"): + """Payload for asset creation events.""" + + event_type: Union[str, None] = msgspec.field(default=None, name="type") + event_time: Union[int, None] = None + asset: Union[Asset, None] = msgspec.field(default=None, name="entity") + operation_type: Literal["ENTITY_CREATE"] = "ENTITY_CREATE" + + +class AssetUpdatePayload(msgspec.Struct, kw_only=True, rename="camel"): + """Payload for asset update events.""" + + event_type: Union[str, None] = msgspec.field(default=None, name="type") + event_time: Union[int, None] = None + asset: Union[Asset, None] = msgspec.field(default=None, name="entity") + operation_type: Literal["ENTITY_UPDATE"] = "ENTITY_UPDATE" + mutated_details: Union[Asset, None] = None + """Details of what was updated on the asset.""" + + +class AssetDeletePayload(msgspec.Struct, kw_only=True, rename="camel"): + """Payload for asset deletion events.""" + + event_type: Union[str, None] = msgspec.field(default=None, name="type") + event_time: Union[int, None] = None + asset: Union[Asset, None] = msgspec.field(default=None, name="entity") + operation_type: Literal["ENTITY_DELETE"] = "ENTITY_DELETE" + + +class CustomMetadataUpdatePayload(msgspec.Struct, kw_only=True, rename="camel"): + """Payload for custom metadata update events.""" + + event_type: Union[str, None] = msgspec.field(default=None, name="type") + event_time: Union[int, None] = None + asset: Union[Asset, None] = msgspec.field(default=None, name="entity") + operation_type: Literal["BUSINESS_ATTRIBUTE_UPDATE"] = "BUSINESS_ATTRIBUTE_UPDATE" + mutated_details: Union[dict[str, Any], None] = None + """Map of custom metadata attributes and values defined on the asset.""" + + +class AtlanTagAddPayload(msgspec.Struct, kw_only=True, rename="camel"): + """Payload for Atlan tag addition events.""" + + event_type: Union[str, None] = msgspec.field(default=None, name="type") + event_time: Union[int, None] = None + asset: Union[Asset, None] = msgspec.field(default=None, name="entity") + operation_type: Literal["CLASSIFICATION_ADD"] = "CLASSIFICATION_ADD" + mutated_details: Union[list[AtlanTag], None] = None + """Atlan tags that were added to the asset by this event.""" + + +class AtlanTagDeletePayload(msgspec.Struct, kw_only=True, rename="camel"): + """Payload for Atlan tag deletion events.""" + + event_type: Union[str, None] = msgspec.field(default=None, name="type") + event_time: Union[int, None] = None + asset: Union[Asset, None] = msgspec.field(default=None, name="entity") + operation_type: Literal["CLASSIFICATION_DELETE"] = "CLASSIFICATION_DELETE" + mutated_details: Union[list[AtlanTag], None] = None + """Atlan tags that were removed from the asset by this event.""" + + +# Union of all event payload types +EventPayload = Union[ + AssetCreatePayload, + AssetUpdatePayload, + AssetDeletePayload, + AtlanTagAddPayload, + AtlanTagDeletePayload, + CustomMetadataUpdatePayload, +] + + +def _atlan_tag_from_dict(data: dict[str, Any]) -> AtlanTag: + """Construct an AtlanTag from a camelCase dict. + + Uses manual construction to avoid ``msgspec.convert`` schema + validation issues with ``Union[str, AtlanTagName, None]``. + """ + return AtlanTag( + type_name=data.get("typeName"), + entity_guid=data.get("entityGuid"), + entity_status=data.get("entityStatus"), + propagate=data.get("propagate"), + remove_propagations_on_entity_delete=data.get( + "removePropagationsOnEntityDelete" + ), + restrict_propagation_through_lineage=data.get( + "restrictPropagationThroughLineage" + ), + restrict_propagation_through_hierarchy=data.get( + "restrictPropagationThroughHierarchy" + ), + validity_periods=data.get("validityPeriods"), + attributes=data.get("attributes"), + ) + + +# Map of operation type string to payload class +_PAYLOAD_MAP: dict[str, type] = { + "ENTITY_CREATE": AssetCreatePayload, + "ENTITY_UPDATE": AssetUpdatePayload, + "ENTITY_DELETE": AssetDeletePayload, + "BUSINESS_ATTRIBUTE_UPDATE": CustomMetadataUpdatePayload, + "CLASSIFICATION_ADD": AtlanTagAddPayload, + "CLASSIFICATION_DELETE": AtlanTagDeletePayload, +} + + +# ============================================================================= +# ATLAN EVENT +# ============================================================================= + + +class AtlanEvent(msgspec.Struct, kw_only=True, rename="camel"): + """Wrapper for an Atlan event.""" + + source: Union[Any, None] = None + version: Union[Any, None] = None + msg_compression_kind: Union[str, None] = None + msg_split_idx: Union[int, None] = None + msg_split_count: Union[int, None] = None + msg_source_ip: Union[str, None] = None + """Originating IP address for the event.""" + msg_created_by: Union[str, None] = None + msg_creation_time: Union[int, None] = None + """Timestamp (epoch) for when the event was created, in milliseconds.""" + spooled: Union[bool, None] = None + payload: Union[EventPayload, None] = msgspec.field(default=None, name="message") + """Detailed contents (payload) of the event.""" + + @classmethod + def from_dict(cls, data: dict[str, Any]) -> AtlanEvent: + """Deserialize from a dict, handling polymorphic asset dispatch + and payload type discrimination. + + This is the primary deserialization entry point for v9 events. + It uses :func:`~pyatlan_v9.model.transform.from_atlas_format` to + resolve the correct Asset subclass based on ``typeName``, and a + simple lookup on ``operationType`` to pick the right payload type. + + :param data: Raw event dict (camelCase keys as received from the API). + :returns: A fully-constructed AtlanEvent with typed payload and asset. + """ + # Ensure v9 assets are registered in the type registry + import pyatlan_v9.model.assets # noqa: F401 + from pyatlan_v9.model.transform import from_atlas_format + + message = data.get("message") + payload = None + + if message: + op_type = message.get("operationType", "") + payload_cls = _PAYLOAD_MAP.get(op_type) + + if payload_cls: + # Convert entity using the v9 asset registry + entity_data = message.get("entity") + asset = from_atlas_format(entity_data) if entity_data else None + + # Build common payload kwargs + kwargs: dict[str, Any] = { + "event_type": message.get("type"), + "event_time": message.get("eventTime"), + "asset": asset, + "operation_type": op_type, + } + + # Handle mutated_details based on payload type + mutated_details_raw = message.get("mutatedDetails") + if mutated_details_raw is not None: + if op_type == "ENTITY_UPDATE": + # mutated_details is another asset in Atlas API format + kwargs["mutated_details"] = from_atlas_format( + mutated_details_raw + ) + elif op_type == "BUSINESS_ATTRIBUTE_UPDATE": + # mutated_details is a dict of custom metadata + kwargs["mutated_details"] = mutated_details_raw + elif op_type in ( + "CLASSIFICATION_ADD", + "CLASSIFICATION_DELETE", + ): + # mutated_details is a list of classification dicts; + # construct AtlanTag manually to avoid msgspec.convert + # schema issues with Union[str, AtlanTagName, None] + kwargs["mutated_details"] = [ + _atlan_tag_from_dict(item) for item in mutated_details_raw + ] + + payload = payload_cls(**kwargs) + + # Build the event directly (avoids msgspec.convert schema validation + # issues with complex Union types like AtlanTagName in nested models) + return cls( + source=data.get("source"), + version=data.get("version"), + msg_compression_kind=data.get("msgCompressionKind"), + msg_split_idx=data.get("msgSplitIdx"), + msg_split_count=data.get("msgSplitCount"), + msg_source_ip=data.get("msgSourceIP"), + msg_created_by=data.get("msgCreatedBy"), + msg_creation_time=data.get("msgCreationTime"), + spooled=data.get("spooled"), + payload=payload, + ) + + +# ============================================================================= +# AWS WRAPPER TYPES +# ============================================================================= + + +class AwsRequestContext(msgspec.Struct, kw_only=True, rename="camel"): + """AWS API Gateway request context.""" + + account_id: Union[str, None] = None + """Account from which the request originated.""" + api_id: Union[str, None] = None + domain_name: Union[str, None] = None + domain_prefix: Union[str, None] = None + http: Union[dict[str, str], None] = None + request_id: Union[str, None] = None + route_key: Union[str, None] = None + stage: Union[str, None] = None + time: Union[str, None] = None + """Time at which the event was received, as a formatted string.""" + time_epoch: Union[int, None] = None + """Time at which the event was received, epoch-based, in milliseconds.""" + + +class AwsEventWrapper(msgspec.Struct, kw_only=True, rename="camel"): + """AWS Lambda event wrapper.""" + + version: Union[str, None] = None + route_key: Union[str, None] = None + raw_path: Union[str, None] = None + raw_query_string: Union[str, None] = None + headers: Union[dict[str, str], None] = None + """Headers used when sending the event through to the Lambda URL.""" + request_context: Union[AwsRequestContext, None] = None + body: Union[str, None] = None + """Actual contents of the event that was sent by Atlan.""" + is_base_64_encoded: Union[bool, None] = None + """Whether the contents are base64-encoded (True) or plain text (False).""" diff --git a/pyatlan_v9/model/fields/__init__.py b/pyatlan_v9/model/fields/__init__.py new file mode 100644 index 000000000..578a3ce52 --- /dev/null +++ b/pyatlan_v9/model/fields/__init__.py @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. diff --git a/pyatlan_v9/model/fields/atlan_fields.py b/pyatlan_v9/model/fields/atlan_fields.py new file mode 100644 index 000000000..11e2e11a3 --- /dev/null +++ b/pyatlan_v9/model/fields/atlan_fields.py @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. +"""Re-export atlan_fields from legacy module for v9 compatibility.""" + +from pyatlan.model.fields.atlan_fields import * # noqa: F401,F403 diff --git a/pyatlan_v9/model/file.py b/pyatlan_v9/model/file.py new file mode 100644 index 000000000..6b2072ec9 --- /dev/null +++ b/pyatlan_v9/model/file.py @@ -0,0 +1,33 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +from __future__ import annotations + +from enum import Enum + +import msgspec + + +class PresignedURLRequest(msgspec.Struct, kw_only=True): + """Request to generate a pre-signed URL for file upload/download.""" + + class Method(str, Enum): + GET = "GET" + PUT = "PUT" + + key: str + """Key (path) of the file.""" + + expiry: str + """Expiry duration for the URL.""" + + method: PresignedURLRequest.Method + """HTTP method for the pre-signed URL.""" + + +class CloudStorageIdentifier(str, Enum): + """Identifiers for cloud storage providers.""" + + S3 = "amazonaws.com" + GCS = "storage.googleapis.com" + AZURE_BLOB = "blob.core.windows.net" diff --git a/pyatlan_v9/model/fluent_search.py b/pyatlan_v9/model/fluent_search.py new file mode 100644 index 000000000..b07d4ffd3 --- /dev/null +++ b/pyatlan_v9/model/fluent_search.py @@ -0,0 +1,470 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. +"""V9-native FluentSearch that produces msgspec IndexSearchRequest.""" + +from __future__ import annotations + +import copy +import dataclasses +import logging +from typing import TYPE_CHECKING, Dict, List, Optional, TypeVar, Union + +from pyatlan.errors import ErrorCode +from pyatlan.model.aggregation import Aggregation +from pyatlan.model.assets import Referenceable, Tag +from pyatlan.model.enums import EntityStatus +from pyatlan.model.fields.atlan_fields import AtlanField +from pyatlan.model.search import ( + Bool, + Query, + SortItem, + SpanNear, + SpanOr, + SpanTerm, + SpanWithin, + Term, +) +from pyatlan_v9.model.search import DSL, IndexSearchRequest + +if TYPE_CHECKING: + from pyatlan_v9.client.aio.atlan import AsyncAtlanClient + from pyatlan_v9.client.atlan import AtlanClient + +LOGGER = logging.getLogger(__name__) + +SelfQuery = TypeVar("SelfQuery", bound="CompoundQuery") + + +@dataclasses.dataclass +class CompoundQuery: + """ + Class to compose compound queries combining various conditions. + """ + + wheres: Optional[List[Query]] = None + where_nots: Optional[List[Query]] = None + where_somes: Optional[List[Query]] = None + _min_somes: int = 1 + + @staticmethod + def active_assets() -> Query: + return Referenceable.STATUS.eq(EntityStatus.ACTIVE.value) + + @staticmethod + def archived_assets() -> Query: + return Referenceable.STATUS.eq(EntityStatus.DELETED.value) + + @staticmethod + def asset_type(of: type) -> Query: + return Referenceable.TYPE_NAME.eq(of.__name__) + + @staticmethod + def asset_types(one_of: List[type]) -> Query: + return Referenceable.TYPE_NAME.within(list(map(lambda x: x.__name__, one_of))) + + @staticmethod + def super_types(one_of: Union[type, List[type]]) -> Query: + if isinstance(one_of, list): + return Referenceable.SUPER_TYPE_NAMES.within( + list(map(lambda x: x.__name__, one_of)) + ) + return Referenceable.SUPER_TYPE_NAMES.eq(one_of.__name__) + + @staticmethod + def tagged( + client: AtlanClient, + with_one_of: Optional[List[str]] = None, + directly: bool = False, + ) -> Query: + values: List[str] = [] + if with_one_of: + for name in with_one_of: + if tag_id := client.atlan_tag_cache.get_id_for_name(name): + values.append(tag_id) + else: + raise ErrorCode.ATLAN_TAG_NOT_FOUND_BY_NAME.exception_with_parameters( + name + ) + if directly: + if values: + return FluentSearch( + wheres=[Referenceable.ATLAN_TAGS.within(values)] + ).to_query() + return FluentSearch( + wheres=[Referenceable.ATLAN_TAGS.has_any_value()] + ).to_query() + if values: + return FluentSearch( + where_somes=[ + Referenceable.ATLAN_TAGS.within(values), + Referenceable.PROPAGATED_ATLAN_TAGS.within(values), + ], + _min_somes=1, + ).to_query() + return FluentSearch( + where_somes=[ + Referenceable.ATLAN_TAGS.has_any_value(), + Referenceable.PROPAGATED_ATLAN_TAGS.has_any_value(), + ], + _min_somes=1, + ).to_query() + + @staticmethod + def tagged_with_value( + client: "AtlanClient", + atlan_tag_name: str, + value: str, + directly: bool = False, + source_tag_qualified_name: Optional[str] = None, + ) -> Query: + big_spans: List = [] + little_spans: List = [] + tag_id = client.atlan_tag_cache.get_id_for_name(atlan_tag_name) or "" + synced_tags = [ + tag + for tag in ( + FluentSearch() + .select() + .where(Tag.MAPPED_CLASSIFICATION_NAME.eq(tag_id)) + .execute(client=client) + ) + ] + if len(synced_tags) > 1 and source_tag_qualified_name is None: + synced_tag_qn = synced_tags[0].qualified_name or "" + LOGGER.warning( + "Multiple mapped source-synced tags found for tag %s -- using only the first: %s. " + "You can specify the `source_tag_qualified_name` so we can match to the specific one.", + atlan_tag_name, + synced_tag_qn, + ) + elif synced_tags: + synced_tag_qn = ( + source_tag_qualified_name or synced_tags[0].qualified_name or "" + ) + else: + synced_tag_qn = "NON_EXISTENT" + + little_spans.append( + SpanTerm(field="__classificationsText.text", value="tagAttachmentValue") + ) + for token in value.split(" "): + little_spans.append( + SpanTerm(field="__classificationsText.text", value=token) + ) + span_or_clauses = [ + SpanTerm(field="__classificationsText.text", value="tagAttachmentKey"), + SpanTerm(field="__classificationsText.text", value="sourceTagName"), + SpanTerm( + field="__classificationsText.text", value="sourceTagQualifiedName" + ), + SpanTerm(field="__classificationsText.text", value="sourceTagGuid"), + SpanTerm( + field="__classificationsText.text", value="sourceTagConnectorName" + ), + SpanTerm(field="__classificationsText.text", value="isSourceTagSynced"), + SpanTerm( + field="__classificationsText.text", value="sourceTagSyncTimestamp" + ), + SpanTerm(field="__classificationsText.text", value="sourceTagValue"), + ] + little_spans.append(SpanOr(clauses=span_or_clauses)) # type: ignore + + big_spans.append(SpanTerm(field="__classificationsText.text", value=tag_id)) + big_spans.append( + SpanTerm(field="__classificationsText.text", value=synced_tag_qn) + ) + + span = SpanWithin( + little=SpanNear(clauses=little_spans, slop=0, in_order=True), + big=SpanNear(clauses=big_spans, slop=10000000, in_order=True), + ) + + if directly: + return ( + FluentSearch() + .where(Referenceable.ATLAN_TAGS.eq(tag_id)) + .where(span) + .to_query() + ) + return ( + FluentSearch() + .where_some(Referenceable.ATLAN_TAGS.eq(tag_id)) + .where_some(Referenceable.PROPAGATED_ATLAN_TAGS.eq(tag_id)) + .min_somes(1) + .where(span) + .to_query() + ) + + @staticmethod + async def tagged_with_value_async( + client: "AsyncAtlanClient", + atlan_tag_name: str, + value: str, + directly: bool = False, + source_tag_qualified_name: Optional[str] = None, + ) -> Query: + big_spans: List = [] + little_spans: List = [] + tag_id = await client.atlan_tag_cache.get_id_for_name(atlan_tag_name) or "" + synced_tags = [ + tag + async for tag in ( + await FluentSearch() + .select() + .where(Tag.MAPPED_CLASSIFICATION_NAME.eq(tag_id)) + .execute_async(client=client) + ) + ] + if len(synced_tags) > 1 and source_tag_qualified_name is None: + synced_tag_qn = synced_tags[0].qualified_name or "" + LOGGER.warning( + "Multiple mapped source-synced tags found for tag %s -- using only the first: %s. " + "You can specify the `source_tag_qualified_name` so we can match to the specific one.", + atlan_tag_name, + synced_tag_qn, + ) + elif synced_tags: + synced_tag_qn = ( + source_tag_qualified_name or synced_tags[0].qualified_name or "" + ) + else: + synced_tag_qn = "NON_EXISTENT" + + little_spans.append( + SpanTerm(field="__classificationsText.text", value="tagAttachmentValue") + ) + for token in value.split(" "): + little_spans.append( + SpanTerm(field="__classificationsText.text", value=token) + ) + span_or_clauses = [ + SpanTerm(field="__classificationsText.text", value="tagAttachmentKey"), + SpanTerm(field="__classificationsText.text", value="sourceTagName"), + SpanTerm( + field="__classificationsText.text", value="sourceTagQualifiedName" + ), + SpanTerm(field="__classificationsText.text", value="sourceTagGuid"), + SpanTerm( + field="__classificationsText.text", value="sourceTagConnectorName" + ), + SpanTerm(field="__classificationsText.text", value="isSourceTagSynced"), + SpanTerm( + field="__classificationsText.text", value="sourceTagSyncTimestamp" + ), + SpanTerm(field="__classificationsText.text", value="sourceTagValue"), + ] + little_spans.append(SpanOr(clauses=span_or_clauses)) # type: ignore + + big_spans.append(SpanTerm(field="__classificationsText.text", value=tag_id)) + big_spans.append( + SpanTerm(field="__classificationsText.text", value=synced_tag_qn) + ) + + span = SpanWithin( + little=SpanNear(clauses=little_spans, slop=0, in_order=True), + big=SpanNear(clauses=big_spans, slop=10000000, in_order=True), + ) + + if directly: + return ( + FluentSearch() + .where(Referenceable.ATLAN_TAGS.eq(tag_id)) + .where(span) + .to_query() + ) + return ( + FluentSearch() + .where_some(Referenceable.ATLAN_TAGS.eq(tag_id)) + .where_some(Referenceable.PROPAGATED_ATLAN_TAGS.eq(tag_id)) + .min_somes(1) + .where(span) + .to_query() + ) + + @staticmethod + def assigned_term(qualified_names: Optional[List[str]] = None) -> Query: + if qualified_names: + return Referenceable.ASSIGNED_TERMS.within(qualified_names) + return Referenceable.ASSIGNED_TERMS.has_any_value() + + def __init__( + self, + wheres: Optional[List[Query]] = None, + where_nots: Optional[List[Query]] = None, + where_somes: Optional[List[Query]] = None, + _min_somes: int = 1, + ): + self.wheres = wheres + self.where_nots = where_nots + self.where_somes = where_somes + self._min_somes = _min_somes + + def _clone(self: SelfQuery) -> SelfQuery: + return copy.deepcopy(self) + + def where(self: SelfQuery, query: Query) -> SelfQuery: + clone = self._clone() + if clone.wheres is None: + clone.wheres = [] + clone.wheres.append(query) + return clone + + def where_not(self: SelfQuery, query: Query) -> SelfQuery: + clone = self._clone() + if clone.where_nots is None: + clone.where_nots = [] + clone.where_nots.append(query) + return clone + + def where_some(self: SelfQuery, query: Query) -> SelfQuery: + clone = self._clone() + if clone.where_somes is None: + clone.where_somes = [] + clone.where_somes.append(query) + return clone + + def min_somes(self: SelfQuery, minimum: int) -> SelfQuery: + clone = self._clone() + clone._min_somes = minimum + return clone + + def to_query(self) -> Query: + q = Bool() + q.filter = self.wheres or [] + q.must_not = self.where_nots or [] + if self.where_somes: + q.should = self.where_somes + q.minimum_should_match = self._min_somes + return q + + +@dataclasses.dataclass +class FluentSearch(CompoundQuery): + """ + V9-native FluentSearch that produces msgspec IndexSearchRequest. + """ + + sorts: Optional[List[SortItem]] = None + aggregations: Optional[Dict[str, Aggregation]] = None + _page_size: Optional[int] = None + _includes_on_results: Optional[List[str]] = None + _includes_on_relations: Optional[List[str]] = None + + @classmethod + def select(cls, include_archived=False) -> "FluentSearch": + wheres = [Term.with_super_type_names("Asset")] + if not include_archived: + wheres.append(Term.with_state("ACTIVE")) + return cls(wheres=wheres) + + def __init__( + self, + wheres: Optional[List[Query]] = None, + where_nots: Optional[List[Query]] = None, + where_somes: Optional[List[Query]] = None, + _min_somes: int = 1, + sorts: Optional[List[SortItem]] = None, + aggregations: Optional[Dict[str, Aggregation]] = None, + _page_size: Optional[int] = None, + _includes_on_results: Optional[List[str]] = None, + _includes_on_relations: Optional[List[str]] = None, + _include_relationship_attributes: Optional[bool] = False, + _enable_full_restriction: Optional[bool] = False, + ): + super().__init__(wheres, where_nots, where_somes, _min_somes) + self.sorts = sorts + self.aggregations = aggregations + self._page_size = _page_size + self._includes_on_results = _includes_on_results + self._includes_on_relations = _includes_on_relations + self._include_relationship_attributes = _include_relationship_attributes + self._enable_full_restriction = _enable_full_restriction + + def _clone(self) -> "FluentSearch": + return copy.deepcopy(self) + + def sort(self, by: SortItem) -> "FluentSearch": + clone = self._clone() + if clone.sorts is None: + clone.sorts = [] + clone.sorts.append(by) + return clone + + def aggregate(self, key: str, aggregation: Aggregation) -> "FluentSearch": + clone = self._clone() + if clone.aggregations is None: + clone.aggregations = {} + clone.aggregations[key] = aggregation + return clone + + def page_size(self, size: int) -> "FluentSearch": + clone = self._clone() + clone._page_size = size + return clone + + def include_on_results(self, field: Union[str, AtlanField]) -> "FluentSearch": + clone = self._clone() + if clone._includes_on_results is None: + clone._includes_on_results = [] + if isinstance(field, AtlanField): + clone._includes_on_results.append(field.atlan_field_name) + else: + clone._includes_on_results.append(field) + return clone + + def include_on_relations(self, field: Union[str, AtlanField]) -> "FluentSearch": + clone = self._clone() + if clone._includes_on_relations is None: + clone._includes_on_relations = [] + if isinstance(field, AtlanField): + clone._includes_on_relations.append(field.atlan_field_name) + else: + clone._includes_on_relations.append(field) + return clone + + def include_relationship_attributes(self, include: bool) -> "FluentSearch": + clone = self._clone() + clone = self.include_on_relations("name") + clone._include_relationship_attributes = include + return clone + + def enable_full_restriction(self, enable: bool) -> "FluentSearch": + clone = self._clone() + clone._enable_full_restriction = enable + return clone + + def _dsl(self) -> DSL: + return DSL(query=self.to_query()) + + def to_request(self) -> IndexSearchRequest: + dsl = self._dsl() + if self._page_size is not None: + dsl.size = self._page_size + if self.sorts: + dsl.sort = self.sorts + if self.aggregations: + dsl.aggregations.update(self.aggregations) + request = IndexSearchRequest(dsl=dsl) + if self._includes_on_results: + request.attributes = self._includes_on_results + if self._includes_on_relations: + request.relation_attributes = self._includes_on_relations + if self._include_relationship_attributes: + request.include_relationship_attributes = ( + self._include_relationship_attributes + ) + if self._enable_full_restriction: + request.enable_full_restriction = self._enable_full_restriction + return request + + def count(self, client: "AtlanClient") -> int: + dsl = self._dsl() + dsl.size = 1 + request = IndexSearchRequest(dsl=dsl) + return client.asset.search(request).count + + def execute(self, client: "AtlanClient", bulk: bool = False): + return client.asset.search(criteria=self.to_request(), bulk=bulk) + + async def execute_async(self, client: "AsyncAtlanClient", bulk: bool = False): + return await client.asset.search(criteria=self.to_request(), bulk=bulk) diff --git a/pyatlan_v9/model/fluent_tasks.py b/pyatlan_v9/model/fluent_tasks.py new file mode 100644 index 000000000..fa323e934 --- /dev/null +++ b/pyatlan_v9/model/fluent_tasks.py @@ -0,0 +1,148 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +""" +v9 FluentTasks — search abstraction for Atlan's task queue. + +Uses v9 DSL (msgspec.Struct) and v9 TaskSearchRequest instead of legacy +Pydantic models. Query-building logic (Bool, SortItem, etc.) uses the +same re-exported dataclass types. +""" + +from __future__ import annotations + +import dataclasses +from copy import deepcopy +from typing import Dict, List, Optional + +from pyatlan.errors import ErrorCode +from pyatlan.model.aggregation import Aggregation +from pyatlan.model.search import Bool, Query, SortItem +from pyatlan.utils import validate_type +from pyatlan_v9.model.search import DSL +from pyatlan_v9.model.task import TaskSearchRequest, TaskSearchResponse + + +@dataclasses.dataclass +class FluentTasks: + """ + Search abstraction mechanism, to simplify the most common searches + against Atlan task queue (removing the need to understand the guts of Elastic). + """ + + sorts: Optional[List[SortItem]] = None + aggregations: Optional[Dict[str, Aggregation]] = None + _page_size: Optional[int] = None + + def __init__( + self, + wheres: Optional[List[Query]] = None, + where_nots: Optional[List[Query]] = None, + where_somes: Optional[List[Query]] = None, + _min_somes: int = 1, + sorts: Optional[List[SortItem]] = None, + aggregations: Optional[Dict[str, Aggregation]] = None, + _page_size: Optional[int] = None, + ): + self.wheres = wheres + self.where_nots = where_nots + self.where_somes = where_somes + self._min_somes = _min_somes + self.sorts = sorts + self.aggregations = aggregations + self._page_size = _page_size + + def _clone(self) -> FluentTasks: + return deepcopy(self) + + def sort(self, by: SortItem) -> FluentTasks: + validate_type(name="by", _type=SortItem, value=by) + clone = self._clone() + if clone.sorts is None: + clone.sorts = [] + clone.sorts.append(by) + return clone + + def aggregate(self, key: str, aggregation: Aggregation) -> FluentTasks: + validate_type(name="key", _type=str, value=key) + validate_type(name="aggregation", _type=Aggregation, value=aggregation) + clone = self._clone() + if clone.aggregations is None: + clone.aggregations = {} + clone.aggregations[key] = aggregation + return clone + + def page_size(self, size: int) -> FluentTasks: + validate_type(name="size", _type=int, value=size) + clone = self._clone() + clone._page_size = size + return clone + + def where(self, query: Query) -> FluentTasks: + validate_type(name="query", _type=Query, value=query) + clone = self._clone() + if clone.wheres is None: + clone.wheres = [] + clone.wheres.append(query) + return clone + + def where_not(self, query: Query) -> FluentTasks: + validate_type(name="query", _type=Query, value=query) + clone = self._clone() + if clone.where_nots is None: + clone.where_nots = [] + clone.where_nots.append(query) + return clone + + def where_some(self, query: Query) -> FluentTasks: + validate_type(name="query", _type=Query, value=query) + clone = self._clone() + if clone.where_somes is None: + clone.where_somes = [] + clone.where_somes.append(query) + return clone + + def min_somes(self, minimum: int) -> FluentTasks: + validate_type(name="minimum", _type=int, value=minimum) + clone = self._clone() + clone._min_somes = minimum + return clone + + def to_query(self) -> Query: + q = Bool() + q.filter = self.wheres or [] + q.must_not = self.where_nots or [] + if self.where_somes: + q.should = self.where_somes + q.minimum_should_match = self._min_somes + return q + + def _dsl(self) -> DSL: + return DSL(query=self.to_query()) + + def to_request(self) -> TaskSearchRequest: + dsl = self._dsl() + if self._page_size: + dsl.size = self._page_size + if self.sorts: + dsl.sort = self.sorts + if self.aggregations: + dsl.aggregations.update(self.aggregations) + request = TaskSearchRequest(dsl=dsl) + return request + + def count(self, client: AtlanClient) -> int: + if not isinstance(client, AtlanClient): + raise ErrorCode.NO_ATLAN_CLIENT.exception_with_parameters() + dsl = self._dsl() + dsl.size = 1 + request = TaskSearchRequest(dsl=dsl) + return client.tasks.search(request).count + + def execute(self, client: AtlanClient) -> TaskSearchResponse: + if not isinstance(client, AtlanClient): + raise ErrorCode.NO_ATLAN_CLIENT.exception_with_parameters() + return client.tasks.search(self.to_request()) + + +from pyatlan.client.atlan import AtlanClient # noqa: E402 diff --git a/pyatlan_v9/model/group.py b/pyatlan_v9/model/group.py new file mode 100644 index 000000000..a769c3de1 --- /dev/null +++ b/pyatlan_v9/model/group.py @@ -0,0 +1,255 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2022 Atlan Pte. Ltd. + +from __future__ import annotations + +from typing import Any, Generator, Union + +import msgspec + +from pyatlan.errors import ErrorCode +from pyatlan.utils import validate_required_fields + + +class GroupAttributes(msgspec.Struct, kw_only=True, rename="camel", omit_defaults=True): + """Detailed attributes of an Atlan group.""" + + alias: Union[list[str], None] = None + """Name of the group as it appears in the UI.""" + + created_at: Union[list[str], None] = None + """Time (epoch) at which the group was created, in milliseconds.""" + + created_by: Union[list[str], None] = None + """User who created the group.""" + + updated_at: Union[list[str], None] = None + """Time (epoch) at which the group was last updated, in milliseconds.""" + + updated_by: Union[list[str], None] = None + """User who last updated the group.""" + + description: Union[list[str], None] = None + """Description of the group.""" + + is_default: Union[list[str], None] = None + """Whether this group should be auto-assigned to all new users or not.""" + + channels: Union[list[str], None] = None + """Slack channels for this group.""" + + +class AtlanGroup(msgspec.Struct, kw_only=True, omit_defaults=True, rename="camel"): + """Representation of a group in Atlan.""" + + alias: Union[str, None] = None + """Name of the group as it appears in the UI.""" + + attributes: Union[GroupAttributes, None] = None + """Detailed attributes of the group.""" + + roles: Union[list[str], None] = None + + decentralized_roles: Union[list[Any], None] = None + + id: Union[str, None] = None + """Unique identifier for the group (GUID).""" + + name: Union[str, None] = None + """Unique (internal) name for the group.""" + + path: Union[str, None] = None + + personas: Union[list[Any], None] = None + """Personas the group is associated with.""" + + purposes: Union[list[Any], None] = None + """Purposes the group is associated with.""" + + user_count: Union[int, None] = None + """Number of users in the group.""" + + def is_default(self) -> bool: + """Whether this group is auto-assigned to all new users.""" + return ( + self.attributes is not None + and self.attributes.is_default is not None + and self.attributes.is_default == ["true"] + ) + + @staticmethod + def creator(alias: str) -> AtlanGroup: + """ + Create a new group with the given alias. + + :param alias: human-readable name for the group + :returns: a new AtlanGroup configured for creation + """ + validate_required_fields(["alias"], [alias]) + return AtlanGroup( + name=AtlanGroup.generate_name(alias), + attributes=GroupAttributes(alias=[alias]), + ) + + @staticmethod + def updater(guid: str, path: str) -> AtlanGroup: + """ + Create a group reference for modification. + + :param guid: unique identifier of the group + :param path: path of the group + :returns: an AtlanGroup configured for update + """ + validate_required_fields(["guid", "path"], [guid, path]) + return AtlanGroup(id=guid, path=path) + + @staticmethod + def generate_name(alias: str) -> str: + """ + Generate internal name from alias. + + :param alias: human-readable name for the group + :returns: internal name for the group + """ + validate_required_fields(["alias"], [alias]) + internal = alias.lower() + return internal.replace(" ", "_") + + +class GroupRequest(msgspec.Struct, kw_only=True): + """Request parameters for listing groups.""" + + post_filter: Union[str, None] = None + """Criteria by which to filter the list of groups to retrieve.""" + + sort: Union[str, None] = "name" + """Property by which to sort the resulting list of groups.""" + + count: bool = True + """Whether to include an overall count of groups (True) or not (False).""" + + offset: int = 0 + """Starting point for the list of groups when paging.""" + + limit: Union[int, None] = 20 + """Maximum number of groups to return per page.""" + + columns: Union[list[str], None] = None + """List of specific fields to include in the response.""" + + @property + def query_params(self) -> dict: + """Convert to query parameters dict.""" + qp: dict[str, object] = {} + if self.post_filter: + qp["filter"] = self.post_filter + if self.sort: + qp["sort"] = self.sort + qp["count"] = self.count + qp["offset"] = self.offset + qp["limit"] = self.limit + if self.columns: + qp["columns"] = self.columns + return qp + + +class GroupResponse(msgspec.Struct, kw_only=True, rename="camel"): + """Response containing group information with pagination support.""" + + total_record: Union[int, None] = None + """Total number of groups.""" + + filter_record: Union[int, None] = None + """Number of groups in the filtered response.""" + + records: Union[list[AtlanGroup], None] = None + """Details of each group included in the response.""" + + # Pagination state (not from JSON — set after construction) + _size: int = 20 + _start: int = 0 + _endpoint: Any = None + _client: Any = None + _criteria: Any = None + + def current_page(self) -> list[AtlanGroup]: + """Return the current page of group results.""" + return self.records or [] + + def next_page(self, start=None, size=None) -> bool: + """Advance to the next page of results.""" + self._start = start or self._start + self._size + if size: + self._size = size + return self._get_next_page() if self.records else False + + def _get_next_page(self) -> bool: + """Fetch the next page of results.""" + self._criteria.offset = self._start + self._criteria.limit = self._size + raw_json = self._client._call_api( + api=self._endpoint.format_path_with_params(), + query_params=self._criteria.query_params, + ) + if not raw_json.get("records"): + self.records = [] + return False + try: + self.records = msgspec.convert( + raw_json.get("records"), list[AtlanGroup], strict=False + ) + except Exception as err: + raise ErrorCode.JSON_ERROR.exception_with_parameters( + raw_json, 200, str(err) + ) from err + return True + + def __iter__(self) -> Generator[AtlanGroup, None, None]: # type: ignore[override] + """Iterate through all pages of results.""" + while True: + yield from self.current_page() + if not self.next_page(): + break + + +class CreateGroupRequest(msgspec.Struct, kw_only=True, omit_defaults=True): + """Request to create a group.""" + + group: AtlanGroup + """Group to be created.""" + + users: Union[list[str], None] = None + """List of users (their GUIDs) to be included in the group.""" + + +class RemoveFromGroupRequest(msgspec.Struct, kw_only=True): + """Request to remove users from a group.""" + + users: Union[list[str], None] = None + """List of users (their GUIDs) to remove from the group.""" + + +class UserStatus(msgspec.Struct, kw_only=True, rename="camel"): + """Status of a user association with a group.""" + + status: Union[int, None] = None + """Response code for the association (200 is success).""" + + status_message: Union[str, None] = None + """Status message for the association.""" + + def was_successful(self) -> bool: + """Whether the association was successful.""" + return (self.status is not None and self.status == 200) or ( + self.status_message is not None and self.status_message == "success" + ) + + +class CreateGroupResponse(msgspec.Struct, kw_only=True): + """Response from creating a group.""" + + group: str + """Unique identifier (GUID) of the group that was created.""" + + users: Union[dict[str, UserStatus], None] = None + """Map of user association statuses, keyed by GUID of the user.""" diff --git a/pyatlan_v9/model/internal.py b/pyatlan_v9/model/internal.py new file mode 100644 index 000000000..48740380f --- /dev/null +++ b/pyatlan_v9/model/internal.py @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2022 Atlan Pte. Ltd. + +""" +Internal model classes for pyatlan_v9, migrated from pyatlan/model/internal.py. +""" + +from __future__ import annotations + +import msgspec + + +class Internal(msgspec.Struct, kw_only=True): + """For internal usage.""" + + +class AtlasServer(msgspec.Struct, kw_only=True): + """For internal usage.""" diff --git a/pyatlan_v9/model/keycloak_events.py b/pyatlan_v9/model/keycloak_events.py new file mode 100644 index 000000000..7ffe35ffd --- /dev/null +++ b/pyatlan_v9/model/keycloak_events.py @@ -0,0 +1,256 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2023 Atlan Pte. Ltd. + +from __future__ import annotations + +from typing import Any, Generator, Union + +import msgspec + +from pyatlan.client.constants import ADMIN_EVENTS, KEYCLOAK_EVENTS +from pyatlan.model.enums import AdminOperationType, AdminResourceType, KeycloakEventType + + +class AuthDetails(msgspec.Struct, kw_only=True): + """Authentication details for an admin event.""" + + client_id: Union[str, None] = None + """Unique identifier (GUID) of the client that carried out the operation.""" + ip_address: Union[str, None] = None + """IP address from which the operation was carried out.""" + realm_id: Union[str, None] = None + """Unique name of the realm from which the operation was carried out.""" + user_id: Union[str, None] = None + """Unique identifier (GUID) of the user who carried out the operation.""" + + +class KeycloakEvent(msgspec.Struct, kw_only=True): + """Keycloak login event.""" + + client_id: Union[str, None] = None + """Where the login occurred (usually 'atlan-frontend').""" + details: Union[Any, None] = None + ip_address: Union[str, None] = None + """IP address from which the user logged in.""" + realm_id: Union[str, None] = None + session_id: Union[str, None] = None + """Unique identifier (GUID) of the session for the login.""" + time: Union[int, None] = None + """Time (epoch) when the login occurred, in milliseconds.""" + type: Union[KeycloakEventType, None] = None + """Type of login event that occurred (usually 'LOGIN').""" + user_id: Union[str, None] = None + """Unique identifier (GUID) of the user that logged in.""" + + +class AdminEvent(msgspec.Struct, kw_only=True): + """Admin operation event.""" + + operation_type: Union[AdminOperationType, None] = None + """Type of admin operation that occurred.""" + realm_id: Union[str, None] = None + """Unique identifier of the realm in which the event occurred.""" + representation: Union[str, None] = None + """Detailed resource that was created or changed.""" + resource_path: Union[str, None] = None + """Location of the resource that was created or changed.""" + resource_type: Union[AdminResourceType, None] = None + """Type of resource for the admin operation.""" + time: Union[int, None] = None + """Time (epoch) when the admin operation occurred, in milliseconds.""" + auth_details: Union[AuthDetails, None] = None + """Details of who carried out the operation.""" + + +class KeycloakEventRequest(msgspec.Struct, kw_only=True): + """Request parameters for listing Keycloak events.""" + + client: Union[str, None] = None + """Application or OAuth client name.""" + ip_address: Union[str, None] = None + """IP address from which the event was triggered.""" + date_from: Union[str, None] = None + """Earliest date from which to include events (format: yyyy-MM-dd).""" + date_to: Union[str, None] = None + """Latest date up to which to include events (format: yyyy-MM-dd).""" + offset: Union[int, None] = None + """Starting point for the events (for paging).""" + size: Union[int, None] = None + """Maximum number of events to retrieve (per page).""" + types: Union[list[KeycloakEventType], None] = None + """Include events only of the supplied types.""" + user_id: Union[str, None] = None + """Unique identifier (GUID) of the user who triggered the event.""" + + @property + def query_params(self) -> dict: + """Convert to query parameters dict.""" + d: dict[str, object] = {} + if self.client: + d["client"] = self.client + if self.ip_address: + d["ipAddress"] = self.ip_address + if self.date_from: + d["dateFrom"] = self.date_from + if self.date_to: + d["dateTo"] = self.date_to + d["first"] = self.offset or 0 + d["max"] = self.size or 100 + if self.types: + d["type"] = self.types + if self.user_id: + d["user"] = self.user_id + return d + + +class KeycloakEventResponse: + """Response with pagination for Keycloak events.""" + + def __init__( + self, + client: Any, + criteria: KeycloakEventRequest, + start: int, + size: int, + events: list[KeycloakEvent], + ): + self._client = client + self._criteria = criteria + self._start = start + self._size = size + self._events = events + + def current_page(self) -> list[KeycloakEvent]: + """Return the current page of events.""" + return self._events + + def next_page(self, start=None, size=None) -> bool: + """Advance to the next page of results.""" + self._start = start or self._start + self._size + if size: + self._size = size + return self._get_next_page() if self._events else False + + def _get_next_page(self) -> bool: + """Fetch the next page of results.""" + self._criteria.offset = self._start + self._criteria.size = self._size + raw_json = self._client._call_api( + KEYCLOAK_EVENTS, + query_params=self._criteria.query_params, + ) + if not raw_json: + self._events = [] + return False + self._events = msgspec.convert(raw_json, list[KeycloakEvent], strict=False) + return True + + def __iter__(self) -> Generator[KeycloakEvent, None, None]: + """Iterate through all pages of results.""" + while True: + yield from self.current_page() + if not self.next_page(): + break + + +class AdminEventRequest(msgspec.Struct, kw_only=True): + """Request parameters for listing admin events.""" + + client_id: Union[str, None] = None + """Unique identifier (GUID) of the client.""" + ip_address: Union[str, None] = None + """IP address from which the operation was carried out.""" + realm_id: Union[str, None] = None + """Unique name of the realm.""" + user_id: Union[str, None] = None + """Unique identifier (GUID) of the user.""" + date_from: Union[str, None] = None + """Earliest date from which to include events (format: yyyy-MM-dd).""" + date_to: Union[str, None] = None + """Latest date up to which to include events (format: yyyy-MM-dd).""" + offset: Union[int, None] = None + """Starting point for the events (for paging).""" + size: Union[int, None] = None + """Maximum number of events to retrieve (per page).""" + operation_types: Union[list[AdminOperationType], None] = None + """Include events only with the supplied types of operations.""" + resource_path: Union[str, None] = None + """Include events only against the supplied resource.""" + resource_types: Union[list[AdminResourceType], None] = None + """Include events only against the supplied types of resources.""" + + @property + def query_params(self) -> dict: + """Convert to query parameters dict.""" + d: dict[str, object] = {} + if self.client_id: + d["authClient"] = self.client_id + if self.ip_address: + d["authIpAddress"] = self.ip_address + if self.realm_id: + d["authRealm"] = self.realm_id + if self.user_id: + d["authUser"] = self.user_id + if self.date_from: + d["dateFrom"] = self.date_from + if self.date_to: + d["dateTo"] = self.date_to + d["first"] = self.offset or 0 + d["max"] = self.size or 100 + if self.operation_types: + d["operationTypes"] = self.operation_types + if self.resource_path: + d["resourcePath"] = self.resource_path + if self.resource_types: + d["resourceTypes"] = self.resource_types + return d + + +class AdminEventResponse: + """Response with pagination for admin events.""" + + def __init__( + self, + client: Any, + criteria: AdminEventRequest, + start: int, + size: int, + events: list[AdminEvent], + ): + self._client = client + self._criteria = criteria + self._start = start + self._size = size + self._events = events + + def current_page(self) -> list[AdminEvent]: + """Return the current page of events.""" + return self._events + + def next_page(self, start=None, size=None) -> bool: + """Advance to the next page of results.""" + self._start = start or self._start + self._size + if size: + self._size = size + return self._get_next_page() if self._events else False + + def _get_next_page(self) -> bool: + """Fetch the next page of results.""" + self._criteria.offset = self._start + self._criteria.size = self._size + raw_json = self._client._call_api( + ADMIN_EVENTS, + query_params=self._criteria.query_params, + ) + if not raw_json: + self._events = [] + return False + self._events = msgspec.convert(raw_json, list[AdminEvent], strict=False) + return True + + def __iter__(self) -> Generator[AdminEvent, None, None]: + """Iterate through all pages of results.""" + while True: + yield from self.current_page() + if not self.next_page(): + break diff --git a/pyatlan_v9/model/lineage.py b/pyatlan_v9/model/lineage.py new file mode 100644 index 000000000..f7cce10b1 --- /dev/null +++ b/pyatlan_v9/model/lineage.py @@ -0,0 +1,421 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2022 Atlan Pte. Ltd. +from __future__ import annotations + +import copy +from enum import Enum +from typing import Any, Dict, List, Optional, Union + +import msgspec + +from pyatlan.model.enums import AtlanComparisonOperator, LineageDirection +from pyatlan.model.fields.atlan_fields import AtlanField, LineageFilter + +# --------------------------------------------------------------------------- +# Re-export plain dataclass / frozen-dataclass classes from legacy. +# These are NOT Pydantic models — no migration needed. +# --------------------------------------------------------------------------- +from pyatlan.model.lineage import DirectedPair, LineageGraph # noqa: F401 +from pyatlan.utils import validate_type +from pyatlan_v9.validate import validate_arguments + +# --------------------------------------------------------------------------- +# msgspec.Struct models — genuine Pydantic → msgspec migrations +# --------------------------------------------------------------------------- + + +class LineageRelation(msgspec.Struct, kw_only=True, rename="camel"): + from_entity_id: Optional[str] = None + to_entity_id: Optional[str] = None + process_id: Optional[str] = None + relationship_id: Optional[str] = None + + @property + def is_full_link(self): + return self.process_id is not None + + +class LineageResponse(msgspec.Struct, kw_only=True, rename="camel"): + base_entity_guid: str + lineage_direction: LineageDirection + lineage_depth: int + limit: int + offset: int + has_more_upstream_vertices: bool + has_more_downstream_vertices: bool + guid_entity_map: Dict[str, Any] # Dict[str, Asset] + relations: List[LineageRelation] + vertex_children_info: Optional[Dict[str, Any]] = None + graph: Optional[LineageGraph] = None + + def get_graph(self): + if self.graph is None: + self.graph = LineageGraph.create(self.relations) + return self.graph + + def get_all_downstream_asset_guids_dfs( + self, guid: Optional[str] = None + ) -> List[str]: + return self.get_graph().get_all_downstream_asset_guids_dfs( + guid or self.base_entity_guid + ) + + def get_all_downstream_assets_dfs(self, guid: Optional[str] = None) -> List[Any]: + return [ + self.guid_entity_map[g] + for g in self.get_graph().get_all_downstream_asset_guids_dfs( + guid or self.base_entity_guid + ) + ] + + def get_all_upstream_asset_guids_dfs(self, guid: Optional[str] = None) -> List[str]: + return self.get_graph().get_all_upstream_asset_guids_dfs( + guid or self.base_entity_guid + ) + + def get_all_upstream_assets_dfs(self, guid: Optional[str] = None) -> List[Any]: + return [ + self.guid_entity_map[g] + for g in self.get_graph().get_all_upstream_asset_guids_dfs( + guid or self.base_entity_guid + ) + ] + + def get_downstream_asset_guids(self, guid: Optional[str] = None) -> List[str]: + return self.get_graph().get_downstream_asset_guids( + guid or self.base_entity_guid + ) + + def get_downstream_assets(self, guid: Optional[str] = None) -> List[Any]: + return [ + self.guid_entity_map[g] + for g in self.get_graph().get_downstream_asset_guids( + guid or self.base_entity_guid + ) + ] + + def get_downstream_process_guids(self, guid: Optional[str] = None) -> List[str]: + return self.get_graph().get_downstream_process_guids( + guid or self.base_entity_guid + ) + + def get_upstream_asset_guids(self, guid: Optional[str] = None) -> List[str]: + return self.get_graph().get_upstream_asset_guids(guid or self.base_entity_guid) + + def get_upstream_assets(self, guid: Optional[str] = None) -> List[Any]: + return [ + self.guid_entity_map[g] + for g in self.get_graph().get_upstream_asset_guids( + guid or self.base_entity_guid + ) + ] + + def get_upstream_process_guids(self, guid: Optional[str] = None) -> List[str]: + return self.get_graph().get_upstream_process_guids( + guid or self.base_entity_guid + ) + + +class LineageRequest(msgspec.Struct, kw_only=True): + guid: str + depth: int = 0 + direction: LineageDirection = LineageDirection.BOTH + hide_process: bool = True + allow_deleted_process: bool = False + + +class EntityFilter(msgspec.Struct, kw_only=True): + attribute_name: str + operator: AtlanComparisonOperator + attribute_value: str + + +class FilterList(msgspec.Struct, kw_only=True): + class Condition(str, Enum): + AND = "AND" + OR = "OR" + + condition: Condition = Condition.AND + criteria: List[EntityFilter] = msgspec.field(default_factory=list, name="criterion") + + +class LineageListRequest(msgspec.Struct, kw_only=True): + guid: str + depth: int = 0 + direction: LineageDirection = LineageDirection.DOWNSTREAM + entity_filters: Optional[FilterList] = msgspec.field( + default=None, name="entityFilters" + ) + entity_traversal_filters: Optional[FilterList] = msgspec.field( + default=None, name="entityTraversalFilters" + ) + relation_attributes: Optional[List[str]] = msgspec.field( + default=None, name="relationAttributes" + ) + relationship_traversal_filters: Optional[FilterList] = msgspec.field( + default=None, name="relationshipTraversalFilters" + ) + attributes: Optional[List[str]] = msgspec.field(default_factory=list) + offset: Optional[int] = msgspec.field(default=None, name="from") + size: Optional[int] = None + exclude_meanings: Optional[bool] = msgspec.field( + default=None, name="excludeMeanings" + ) + exclude_classifications: Optional[bool] = msgspec.field( + default=None, name="excludeClassifications" + ) + immediate_neighbors: Optional[bool] = msgspec.field( + default=None, name="immediateNeighbours" + ) + + @staticmethod + def create(guid: str) -> "LineageListRequest": + from pyatlan.utils import validate_required_fields + + validate_required_fields(["guid"], [guid]) + return LineageListRequest( + guid=guid, + depth=1000000, + direction=LineageDirection.DOWNSTREAM, + offset=0, + size=10, + exclude_meanings=True, + exclude_classifications=True, + ) + + +# --------------------------------------------------------------------------- +# FluentLineage — plain class that constructs v9 msgspec model objects. +# Kept in v9 (not re-exported from legacy) because it builds v9 +# EntityFilter / FilterList / LineageListRequest instances. +# --------------------------------------------------------------------------- + + +class FluentLineage: + """Lineage abstraction mechanism, to simplify the most common lineage requests against Atlan + (removing the need to understand the guts of Elastic).""" + + ACTIVE: LineageFilter = None # type: ignore[assignment] + + def __init_subclass__(cls, **kwargs): + super().__init_subclass__(**kwargs) + + @validate_arguments(config=dict(arbitrary_types_allowed=True)) + def __init__( + self, + *, + starting_guid: str, + depth: int = 1000000, + direction: LineageDirection = LineageDirection.DOWNSTREAM, + size: int = 10, + exclude_meanings: bool = True, + exclude_atlan_tags: bool = True, + immediate_neighbors: bool = False, + includes_on_results: Optional[ + Union[List[str], str, List[AtlanField], AtlanField] + ] = None, + includes_in_results: Optional[Union[List[LineageFilter], LineageFilter]] = None, + includes_on_relations: Optional[ + Union[List[str], str, List[AtlanField], AtlanField] + ] = None, + includes_condition: FilterList.Condition = FilterList.Condition.AND, + where_assets: Optional[Union[List[LineageFilter], LineageFilter]] = None, + assets_condition: FilterList.Condition = FilterList.Condition.AND, + where_relationships: Optional[Union[List[LineageFilter], LineageFilter]] = None, + relationships_condition: FilterList.Condition = FilterList.Condition.AND, + ): + self._depth: int = depth + self._direction: LineageDirection = direction + self._exclude_atlan_tags: bool = exclude_atlan_tags + self._exclude_meanings: bool = exclude_meanings + self._immediate_neighbors: bool = immediate_neighbors + self._includes_on_results: List[Union[str, AtlanField]] = self._to_list( + includes_on_results + ) + self._includes_in_results: List[LineageFilter] = self._to_list( + includes_in_results + ) + self._includes_on_relations: List[Union[str, AtlanField]] = self._to_list( + includes_on_relations + ) + self._includes_condition: FilterList.Condition = includes_condition + self._size: int = size + self._starting_guid = starting_guid + self._where_assets: List[LineageFilter] = self._to_list(where_assets) + self._assets_condition: FilterList.Condition = assets_condition + self._where_relationships: List[LineageFilter] = self._to_list( + where_relationships + ) + self._relationships_condition: FilterList.Condition = relationships_condition + + @staticmethod + def _to_list(value): + return [] if value is None else value if isinstance(value, list) else [value] + + def _clone(self) -> "FluentLineage": + return copy.deepcopy(self) + + def depth(self, depth: int) -> "FluentLineage": + validate_type(name="depth", _type=int, value=depth) + clone = self._clone() + clone._depth = depth + return clone + + def direction(self, direction: LineageDirection) -> "FluentLineage": + validate_type(name="direction", _type=LineageDirection, value=direction) + clone = self._clone() + clone._direction = direction + return clone + + def size(self, size: int) -> "FluentLineage": + validate_type(name="size", _type=int, value=size) + clone = self._clone() + clone._size = size + return clone + + def exclude_atlan_tags(self, exclude_atlan_tags: bool) -> "FluentLineage": + validate_type(name="exclude_atlan_tags", _type=bool, value=exclude_atlan_tags) + clone = self._clone() + clone._exclude_atlan_tags = exclude_atlan_tags + return clone + + def exclude_meanings(self, exclude_meanings: bool) -> "FluentLineage": + validate_type(name="exclude_meanings", _type=bool, value=exclude_meanings) + clone = self._clone() + clone._exclude_meanings = exclude_meanings + return clone + + def immediate_neighbors(self, immediate_neighbors: bool) -> "FluentLineage": + validate_type(name="immediate_neighbors", _type=bool, value=immediate_neighbors) + clone = self._clone() + clone._immediate_neighbors = immediate_neighbors + return clone + + def include_on_results(self, field: Union[str, AtlanField]) -> "FluentLineage": + validate_type(name="field", _type=(str, AtlanField), value=field) + clone = self._clone() + clone._includes_on_results.append(field) + return clone + + def include_in_results(self, lineage_filter: LineageFilter) -> "FluentLineage": + validate_type(name="lineage_filter", _type=LineageFilter, value=lineage_filter) + clone = self._clone() + clone._includes_in_results.append(lineage_filter) + return clone + + def include_on_relations(self, field: Union[str, AtlanField]) -> "FluentLineage": + validate_type(name="field", _type=(str, AtlanField), value=field) + clone = self._clone() + clone._includes_on_relations.append(field) + return clone + + def includes_condition( + self, includes_condition: FilterList.Condition + ) -> "FluentLineage": + validate_type( + name="includes_condition", + _type=FilterList.Condition, + value=includes_condition, + ) + clone = self._clone() + clone._includes_condition = includes_condition + return clone + + def where_assets(self, lineage_filter: LineageFilter) -> "FluentLineage": + validate_type(name="lineage_filter", _type=LineageFilter, value=lineage_filter) + clone = self._clone() + clone._where_assets.append(lineage_filter) + return clone + + def assets_condition( + self, assets_condition: FilterList.Condition + ) -> "FluentLineage": + validate_type( + name="assets_condition", + _type=FilterList.Condition, + value=assets_condition, + ) + clone = self._clone() + clone._assets_condition = assets_condition + return clone + + def where_relationships(self, lineage_filter: LineageFilter) -> "FluentLineage": + validate_type(name="lineage_filter", _type=LineageFilter, value=lineage_filter) + clone = self._clone() + clone._where_relationships.append(lineage_filter) + return clone + + def relationships_condition( + self, relationships_condition: FilterList.Condition + ) -> "FluentLineage": + validate_type( + name="relationships_condition", + _type=FilterList.Condition, + value=relationships_condition, + ) + clone = self._clone() + clone._relationships_condition = relationships_condition + return clone + + @property + def request(self) -> LineageListRequest: + request = LineageListRequest.create(guid=self._starting_guid) + if self._depth: + request.depth = self._depth + if self._direction: + request.direction = self._direction + if self._exclude_atlan_tags is not None: + request.exclude_classifications = self._exclude_atlan_tags + if self._exclude_meanings is not None: + request.exclude_meanings = self._exclude_meanings + if self._immediate_neighbors is not None: + request.immediate_neighbors = self._immediate_neighbors + if self._includes_in_results: + criteria = [ + EntityFilter( + attribute_name=_filter.field.internal_field_name, + operator=_filter.operator, + attribute_value=_filter.value, + ) + for _filter in self._includes_in_results + ] + request.entity_filters = FilterList( + condition=self._includes_condition, criteria=criteria + ) + if self._includes_on_results: + request.attributes = [ + field.atlan_field_name if isinstance(field, AtlanField) else field + for field in self._includes_on_results + ] + if self._includes_on_relations: + request.relation_attributes = [ + field.atlan_field_name if isinstance(field, AtlanField) else field + for field in self._includes_on_relations + ] + if self._size: + request.size = self._size + if self._where_assets: + criteria = [ + EntityFilter( + attribute_name=_filter.field.internal_field_name, + operator=_filter.operator, + attribute_value=_filter.value, + ) + for _filter in self._where_assets + ] + request.entity_traversal_filters = FilterList( + condition=self._assets_condition, criteria=criteria + ) + if self._where_relationships: + criteria = [ + EntityFilter( + attribute_name=_filter.field.internal_field_name, + operator=_filter.operator, + attribute_value=_filter.value, + ) + for _filter in self._where_relationships + ] + request.relationship_traversal_filters = FilterList( + condition=self._relationships_condition, criteria=criteria + ) + return request diff --git a/pyatlan_v9/model/lineage_ref.py b/pyatlan_v9/model/lineage_ref.py new file mode 100644 index 000000000..70a6573b7 --- /dev/null +++ b/pyatlan_v9/model/lineage_ref.py @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +from __future__ import annotations + +from typing import Union + +import msgspec + + +class LineageRef(msgspec.Struct, kw_only=True, rename="camel"): + """Reference to an asset within a lineage result.""" + + qualified_name: Union[str, None] = None + """Unique name of the asset being referenced.""" + + name: Union[str, None] = None + """Simple name of the asset being referenced.""" + + guid: Union[str, None] = None + """UUID of the asset being referenced.""" diff --git a/pyatlan_v9/model/oauth_client.py b/pyatlan_v9/model/oauth_client.py new file mode 100644 index 000000000..488c1dbbe --- /dev/null +++ b/pyatlan_v9/model/oauth_client.py @@ -0,0 +1,145 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2026 Atlan Pte. Ltd. + +from __future__ import annotations + +from typing import Any, Generator, Union + +import msgspec + +from pyatlan.errors import ErrorCode + + +class OAuthClientRequest(msgspec.Struct, kw_only=True, rename="camel"): + """Request object for creating an OAuth client.""" + + display_name: str + """Human-readable name for the OAuth client.""" + description: Union[str, None] = None + """Explanation of the OAuth client.""" + role: str + """Role assigned to the OAuth client (e.g., '$admin', '$member').""" + persona_qualified_names: Union[list[str], None] = msgspec.field( + default=None, name="personaQNs" + ) + """Qualified names of personas to associate with the OAuth client.""" + + +class OAuthClientCreateResponse(msgspec.Struct, kw_only=True, rename="camel"): + """Response object returned when creating an OAuth client (includes client secret).""" + + id: Union[str, None] = None + """Unique identifier (GUID) of the OAuth client.""" + client_id: Union[str, None] = None + """Unique client identifier of the OAuth client.""" + client_secret: Union[str, None] = None + """Client secret for the OAuth client (only returned on creation).""" + display_name: Union[str, None] = None + """Human-readable name provided when creating the OAuth client.""" + description: Union[str, None] = None + """Explanation of the OAuth client.""" + token_expiry_seconds: Union[int, None] = None + """Time in seconds after which the token will expire.""" + created_at: Union[str, None] = None + """Epoch time, in milliseconds, at which the OAuth client was created.""" + created_by: Union[str, None] = None + """User who created the OAuth client.""" + + +class OAuthClientResponse(msgspec.Struct, kw_only=True, rename="camel"): + """Represents an OAuth client credential in Atlan.""" + + id: Union[str, None] = None + """Unique identifier (GUID) of the OAuth client.""" + client_id: Union[str, None] = None + """Unique client identifier of the OAuth client.""" + display_name: Union[str, None] = None + """Human-readable name provided when creating the OAuth client.""" + description: Union[str, None] = None + """Explanation of the OAuth client.""" + role: Union[str, None] = None + """Role assigned to the OAuth client (e.g., '$admin').""" + persona_qualified_names: Union[list[str], None] = msgspec.field( + default=None, name="personaQNs" + ) + """Qualified names of personas associated with the OAuth client.""" + token_expiry_seconds: Union[int, None] = None + """Time in seconds after which the token will expire.""" + created_at: Union[str, None] = None + """Epoch time, in milliseconds, at which the OAuth client was created.""" + created_by: Union[str, None] = None + """User who created the OAuth client.""" + updated_at: Union[str, None] = None + """Epoch time, in milliseconds, at which the OAuth client was last updated.""" + updated_by: Union[str, None] = None + """User who last updated the OAuth client.""" + + +class OAuthClientListResponse(msgspec.Struct, kw_only=True, rename="camel"): + """Response object containing a list of OAuth clients with pagination info.""" + + total_record: Union[int, None] = None + """Total number of OAuth clients.""" + filter_record: Union[int, None] = None + """Number of OAuth clients that matched the specified filters.""" + records: Union[list[OAuthClientResponse], None] = None + """List of OAuth clients.""" + + # Pagination state (not from JSON — set after construction) + _size: int = 20 + _start: int = 0 + _endpoint: Any = None + _client: Any = None + _sort: Any = None + + def current_page(self) -> Union[list[OAuthClientResponse], None]: + """Get the current page of OAuth clients.""" + return self.records + + def next_page( + self, start: Union[int, None] = None, size: Union[int, None] = None + ) -> bool: + """ + Retrieve the next page of results. + + :param start: starting point for the next page + :param size: page size for the next page + :returns: True if there was a next page, False otherwise + """ + self._start = start or self._start + self._size + if size: + self._size = size + return self._get_next_page() if self.records else False + + def _get_next_page(self) -> bool: + """Fetch the next page of results.""" + query_params: dict[str, str] = { + "count": "true", + "offset": str(self._start), + "limit": str(self._size), + } + if self._sort is not None: + query_params["sort"] = self._sort + raw_json = self._client._call_api( + api=self._endpoint, + query_params=query_params, + ) + if not raw_json.get("records"): + self.records = [] + return False + try: + self.records = msgspec.convert( + raw_json.get("records"), list[OAuthClientResponse], strict=False + ) + except Exception as err: + raise ErrorCode.JSON_ERROR.exception_with_parameters( + raw_json, 200, str(err) + ) from err + return True + + def __iter__(self) -> Generator[OAuthClientResponse, None, None]: # type: ignore[override] + """Iterate over all OAuth clients across all pages.""" + while True: + yield from self.current_page() or [] + if not self.next_page(): + break diff --git a/pyatlan_v9/model/open_lineage/__init__.py b/pyatlan_v9/model/open_lineage/__init__.py new file mode 100644 index 000000000..a058a0f33 --- /dev/null +++ b/pyatlan_v9/model/open_lineage/__init__.py @@ -0,0 +1,28 @@ +from pyatlan_v9.model.open_lineage.event import OpenLineageEvent, OpenLineageRawEvent +from pyatlan_v9.model.open_lineage.facet import ( + OpenLineageColumnLineageDatasetFacet, + OpenLineageColumnLineageDatasetFacetFieldsAdditional, + OpenLineageColumnLineageDatasetFacetFieldsAdditionalInputFields, + OpenLineageDatasetFacet, + OpenLineageDatasetFacets, + OpenLineageJobFacet, +) +from pyatlan_v9.model.open_lineage.input_dataset import OpenLineageInputDataset +from pyatlan_v9.model.open_lineage.job import OpenLineageJob +from pyatlan_v9.model.open_lineage.output_dataset import OpenLineageOutputDataset +from pyatlan_v9.model.open_lineage.run import OpenLineageRun + +__all__ = [ + "OpenLineageEvent", + "OpenLineageRawEvent", + "OpenLineageJob", + "OpenLineageRun", + "OpenLineageInputDataset", + "OpenLineageOutputDataset", + "OpenLineageColumnLineageDatasetFacet", + "OpenLineageColumnLineageDatasetFacetFieldsAdditional", + "OpenLineageColumnLineageDatasetFacetFieldsAdditionalInputFields", + "OpenLineageDatasetFacet", + "OpenLineageDatasetFacets", + "OpenLineageJobFacet", +] diff --git a/pyatlan_v9/model/open_lineage/base.py b/pyatlan_v9/model/open_lineage/base.py new file mode 100644 index 000000000..3b39c354f --- /dev/null +++ b/pyatlan_v9/model/open_lineage/base.py @@ -0,0 +1,64 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +from __future__ import annotations + +from typing import Union +from urllib.parse import urlparse + +import msgspec +from dateutil import parser # type:ignore[import-untyped] + + +class OpenLineageBaseEvent( + msgspec.Struct, kw_only=True, omit_defaults=True, rename="camel" +): + """ + Base model for OpenLineage events. + """ + + event_time: Union[str, None] = None + """Time the event occurred at.""" + + producer: Union[str, None] = None + """Producer of the event.""" + + schema_url: Union[str, None] = msgspec.field(default=None, name="schemaURL") + """Schema URL for the event.""" + + def __post_init__(self) -> None: + if self.schema_url is None: + self.schema_url = self._get_schema() + if self.event_time is not None: + self._validate_event_time(self.event_time) + if self.producer is not None: + urlparse(self.producer) + + @staticmethod + def _get_schema() -> str: + return "https://openlineage.io/spec/2-0-2/OpenLineage.json#/$defs/BaseEvent" + + @staticmethod + def _validate_event_time(value: str) -> str: + # Parse and validate the ISO format + parser.isoparse(value) + if "t" not in value.lower(): + raise ValueError(f"Parsed date-time has to contain time: {value}") + return value + + +class OpenLineageBaseFacet(msgspec.Struct, kw_only=True, omit_defaults=True): + """ + Base model for OpenLineage facets. + """ + + producer: Union[str, None] = msgspec.field(default=None, name="_producer") + schema_url: Union[str, None] = msgspec.field(default=None, name="_schemaURL") + + def __post_init__(self) -> None: + if self.schema_url is None: + self.schema_url = self._get_schema() + + @staticmethod + def _get_schema() -> str: + return "https://openlineage.io/spec/2-0-2/OpenLineage.json#/$defs/BaseFacet" diff --git a/pyatlan_v9/model/open_lineage/dataset.py b/pyatlan_v9/model/open_lineage/dataset.py new file mode 100644 index 000000000..4cb87e6dd --- /dev/null +++ b/pyatlan_v9/model/open_lineage/dataset.py @@ -0,0 +1,27 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +from __future__ import annotations + +from typing import Any, Dict, Union + +import msgspec + + +class OpenLineageDataset(msgspec.Struct, kw_only=True, omit_defaults=True): + """ + Model for handling OpenLineage datasets. + """ + + name: Union[str, None] = None + """Unique name for that dataset within that namespace.""" + + namespace: Union[str, None] = None + """Namespace containing that dataset.""" + + facets: Union[Dict[str, Any], None] = msgspec.field(default_factory=dict) + """Facets for this dataset.""" + + @staticmethod + def _get_schema() -> str: + return "https://openlineage.io/spec/2-0-2/OpenLineage.json#/$defs/Job" diff --git a/pyatlan_v9/model/open_lineage/event.py b/pyatlan_v9/model/open_lineage/event.py new file mode 100644 index 000000000..3f25c8f5d --- /dev/null +++ b/pyatlan_v9/model/open_lineage/event.py @@ -0,0 +1,226 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +from __future__ import annotations + +import json +from datetime import datetime +from typing import TYPE_CHECKING, Any, Dict, List, Union + +import msgspec +from pytz import utc # type:ignore[import-untyped] + +from pyatlan.model.enums import AtlanConnectorType, OpenLineageEventType +from pyatlan_v9.model.open_lineage.base import OpenLineageBaseEvent +from pyatlan_v9.model.open_lineage.input_dataset import OpenLineageInputDataset +from pyatlan_v9.model.open_lineage.job import OpenLineageJob +from pyatlan_v9.model.open_lineage.output_dataset import OpenLineageOutputDataset +from pyatlan_v9.model.open_lineage.run import OpenLineageRun + +if TYPE_CHECKING: + from pyatlan.client.atlan import AtlanClient + from pyatlan_v9.client.aio.atlan import AsyncAtlanClient + + +class OpenLineageRawEvent: + """ + Root model for handling raw OpenLineage events. + + This model accepts any arbitrary data structure (dict, list of dicts, string, etc.) + and allows it to be sent as raw OpenLineage event data to Atlan's API. + """ + + def __init__(self, data: Any = None) -> None: + self.data = data + + @classmethod + def from_json(cls, json_str: str) -> OpenLineageRawEvent: + """ + Create an OpenLineageRawEvent from a JSON string. + + :param json_str: JSON string containing raw OpenLineage event data + :returns: New OpenLineageRawEvent instance + """ + return cls(data=json.loads(json_str)) + + @classmethod + def from_dict(cls, data: Dict[str, Any]) -> OpenLineageRawEvent: + """ + Create an OpenLineageRawEvent from a dictionary. + + :param data: Dictionary containing raw OpenLineage event data + :returns: New OpenLineageRawEvent instance + """ + return cls(data=data) + + @classmethod + def parse_raw(cls, json_str: str) -> OpenLineageRawEvent: + """Compatibility method: parse from raw JSON string.""" + return cls.from_json(json_str) + + @classmethod + def parse_obj(cls, data: Any) -> OpenLineageRawEvent: + """Compatibility method: parse from a Python object.""" + return cls(data=data) + + +class OpenLineageEvent(OpenLineageBaseEvent): + """ + Atlan wrapper for abstracting OpenLineage events. + + An event represents a point-in-time state of a run. + To process lineage in Atlan, you **must** have at least two states for any run: + - `START`: Indicates that a run has started. + - One of the following to mark that the run has finished: + - `COMPLETE`: Run execution has successfully concluded. + - `ABORT`: Run has been stopped abnormally. + - `FAIL`: Run has failed. + """ + + run: Union[OpenLineageRun, None] = None + job: Union[OpenLineageJob, None] = None + event_type: Union[OpenLineageEventType, None] = msgspec.field( + default=None, name="eventType" + ) + inputs: Union[List[OpenLineageInputDataset], None] = msgspec.field( + default_factory=list + ) + outputs: Union[List[OpenLineageOutputDataset], None] = msgspec.field( + default_factory=list + ) + + def __post_init__(self) -> None: + if self.schema_url is None: + self.schema_url = self._get_schema() + if self.event_time is not None: + self._validate_event_time(self.event_time) + + @staticmethod + def _get_schema() -> str: + return "https://openlineage.io/spec/2-0-2/OpenLineage.json#/$defs/RunEvent" + + @classmethod + def creator( + cls, run: OpenLineageRun, event_type: OpenLineageEventType + ) -> OpenLineageEvent: + """ + Builds the minimal object necessary to create an OpenLineage event. + + :param run: OpenLineage run for which to create a new event + :param event_type: type of event to create + :returns: the minimal request necessary to create the event + """ + return OpenLineageEvent( + run=run, + job=run.job, + producer=run.job and run.job.producer or "", + event_type=event_type, + event_time=datetime.now(tz=utc).isoformat(), + ) + + def emit(self, client: AtlanClient) -> None: + """ + Send the OpenLineage event to Atlan to be processed. + + :param client: connectivity to an Atlan tenant + :raises AtlanError: on any API communication issues + """ + return client.open_lineage.send( + request=self, connector_type=AtlanConnectorType.SPARK + ) + + async def emit_async(self, client: "AsyncAtlanClient") -> None: + """ + Asynchronously send the OpenLineage event to Atlan to be processed. + + :param client: async connectivity to an Atlan tenant + :raises AtlanError: on any API communication issues + """ + return await client.open_lineage.send( + request=self, connector_type=AtlanConnectorType.SPARK + ) + + @classmethod + def emit_raw( + cls, + client: AtlanClient, + event: Union[OpenLineageRawEvent, List[Dict[str, Any]], Dict[str, Any], str], + connector_type: AtlanConnectorType = AtlanConnectorType.SPARK, + ) -> None: + """ + Send raw OpenLineage event data to Atlan to be processed. + + :param client: connectivity to an Atlan tenant + :param event: Raw event(s) as JSON string, dict, list of dicts, or OpenLineageRawEvent + :param connector_type: connector type for the OpenLineage event + :raises AtlanError: on any API communication issues + """ + return client.open_lineage.send(request=event, connector_type=connector_type) + + def to_dict(self) -> Dict[str, Any]: + """ + Serialize this event to a dict, properly handling excluded fields + (producer on Job, job on Run) and camelCase aliases. + + Mirrors Pydantic's ``model.json(by_alias=True, exclude_unset=True)`` + behaviour: fields that were *explicitly* passed at construction time + appear even when empty; fields left at their factory default are omitted. + """ + result: Dict[str, Any] = {} + + if self.event_time is not None: + result["eventTime"] = self.event_time + if self.producer is not None: + result["producer"] = self.producer + if self.schema_url is not None: + result["schemaURL"] = self.schema_url + + if self.run is not None: + # Exclude the 'job' field from run serialization + run_dict: Dict[str, Any] = {} + if self.run.run_id is not None: + run_dict["runId"] = self.run.run_id + # Always include facets (even when empty) — matches legacy behaviour + if self.run.facets is not None: + run_dict["facets"] = self.run.facets + result["run"] = run_dict + + if self.job is not None: + # Exclude the 'producer' field from job serialization + job_dict: Dict[str, Any] = {} + if self.job.namespace is not None: + job_dict["namespace"] = self.job.namespace + if self.job.name is not None: + job_dict["name"] = self.job.name + if self.job.facets is not None: + job_dict["facets"] = ( + msgspec.to_builtins(self.job.facets) if self.job.facets else {} + ) + result["job"] = job_dict + + if self.event_type is not None: + result["eventType"] = self.event_type.value + + if self.inputs: + inputs_list = [] + for inp in self.inputs: + inp_dict: Dict[str, Any] = {} + if inp.namespace is not None: + inp_dict["namespace"] = inp.namespace + if inp.name is not None: + inp_dict["name"] = inp.name + # Include facets (even when empty) — matches legacy behaviour + if inp.facets is not None: + inp_dict["facets"] = inp.facets + # NOTE: inputFacets is intentionally omitted when it was not + # explicitly set by the caller (mirrors exclude_unset=True). + inputs_list.append(inp_dict) + result["inputs"] = inputs_list + + if self.outputs: + outputs_list = [] + for out in self.outputs: + outputs_list.append(out.to_dict()) + result["outputs"] = outputs_list + + return result diff --git a/pyatlan_v9/model/open_lineage/facet.py b/pyatlan_v9/model/open_lineage/facet.py new file mode 100644 index 000000000..e256278e4 --- /dev/null +++ b/pyatlan_v9/model/open_lineage/facet.py @@ -0,0 +1,70 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +from __future__ import annotations + +from typing import Dict, List, Union + +import msgspec + +from pyatlan_v9.model.open_lineage.base import OpenLineageBaseFacet + + +class OpenLineageColumnLineageDatasetFacetFieldsAdditionalInputFields( + msgspec.Struct, kw_only=True, omit_defaults=True +): + namespace: Union[str, None] = None + name: Union[str, None] = None + field: Union[str, None] = None + + +class OpenLineageColumnLineageDatasetFacetFieldsAdditional( + msgspec.Struct, kw_only=True, omit_defaults=True +): + input_fields: Union[ + List[OpenLineageColumnLineageDatasetFacetFieldsAdditionalInputFields], None + ] = msgspec.field(default=None, name="inputFields") + transformation_description: Union[str, None] = msgspec.field( + default=None, name="transformationDescription" + ) + transformation_type: Union[str, None] = msgspec.field( + default=None, name="transformationType" + ) + + +class OpenLineageDatasetFacet(OpenLineageBaseFacet): + """A Dataset Facet""" + + @staticmethod + def _get_schema() -> str: + return "https://openlineage.io/spec/2-0-2/OpenLineage.json#/$defs/DatasetFacet" + + +class OpenLineageJobFacet(OpenLineageBaseFacet): + """A Job Facet""" + + @staticmethod + def _get_schema() -> str: + return "https://openlineage.io/spec/2-0-2/OpenLineage.json#/$defs/JobFacet" + + +class OpenLineageColumnLineageDatasetFacet(OpenLineageBaseFacet): + """ + This facet contains column lineage of a dataset. + """ + + fields: Dict[str, OpenLineageColumnLineageDatasetFacetFieldsAdditional] = ( + msgspec.field(default_factory=dict) + ) + + @staticmethod + def _get_schema() -> str: + return "https://openlineage.io/spec/facets/1-1-0/ColumnLineageDatasetFacet.json#/$defs/ColumnLineageDatasetFacet" + + +class OpenLineageDatasetFacets(msgspec.Struct, kw_only=True, omit_defaults=True): + """A Dataset Facets""" + + column_lineage: Union[OpenLineageColumnLineageDatasetFacet, None] = msgspec.field( + default=None, name="columnLineage" + ) diff --git a/pyatlan_v9/model/open_lineage/input_dataset.py b/pyatlan_v9/model/open_lineage/input_dataset.py new file mode 100644 index 000000000..db5dcd8d5 --- /dev/null +++ b/pyatlan_v9/model/open_lineage/input_dataset.py @@ -0,0 +1,53 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +from __future__ import annotations + +from typing import Any, Dict, Union + +import msgspec + +from pyatlan_v9.model.open_lineage.dataset import OpenLineageDataset +from pyatlan_v9.model.open_lineage.facet import ( + OpenLineageColumnLineageDatasetFacetFieldsAdditionalInputFields, +) + + +class OpenLineageInputDataset(OpenLineageDataset): + """ + Model for handling OpenLineage datasets + to be used as lineage sources (inputs). + """ + + input_facets: Union[Dict[str, Any], None] = msgspec.field( + default_factory=dict, name="inputFacets" + ) + + @staticmethod + def _get_schema() -> str: + return "https://openlineage.io/spec/2-0-2/OpenLineage.json#/$defs/InputDataset" + + @classmethod + def creator(cls, namespace: str, asset_name: str) -> OpenLineageInputDataset: + """ + Builds the minimal object necessary to create an OpenLineage dataset + use-able as a lineage source. + + :param namespace: name of the source of the asset + :param asset_name: name of the asset, by OpenLineage standard + :returns: the minimal request necessary to create the input dataset + """ + return OpenLineageInputDataset(namespace=namespace, name=asset_name, facets={}) + + def from_field( + self, field_name: str + ) -> OpenLineageColumnLineageDatasetFacetFieldsAdditionalInputFields: + """ + Create a new reference to a field within this input dataset. + + :param field_name: name of the field within the input dataset to reference + :returns: a reference to the field within this input dataset + """ + return OpenLineageColumnLineageDatasetFacetFieldsAdditionalInputFields( + namespace=self.namespace, name=self.name, field=field_name + ) diff --git a/pyatlan_v9/model/open_lineage/job.py b/pyatlan_v9/model/open_lineage/job.py new file mode 100644 index 000000000..2ef926685 --- /dev/null +++ b/pyatlan_v9/model/open_lineage/job.py @@ -0,0 +1,95 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +from __future__ import annotations + +from typing import Dict, Union + +import msgspec + +from pyatlan_v9.model.open_lineage.facet import OpenLineageJobFacet +from pyatlan_v9.model.open_lineage.input_dataset import OpenLineageInputDataset +from pyatlan_v9.model.open_lineage.output_dataset import OpenLineageOutputDataset + + +class OpenLineageJob(msgspec.Struct, kw_only=True, omit_defaults=True): + """ + Atlan wrapper for abstracting OpenLineage jobs. + + A job is a process that consumes or produces datasets. + This is abstract, and can map to different things in different operational contexts. + For example, a job could be a task in a workflow orchestration system. + It could also be a model, a query, or a checkpoint. Depending on the + system under observation, a Job can represent a small or large amount of work. + + For more details + https://openlineage.io/docs/spec/object-model#job + """ + + name: Union[str, None] = None + """Unique name for that job within that namespace.""" + + namespace: Union[str, None] = None + """Namespace containing that job.""" + + facets: Union[Dict[str, OpenLineageJobFacet], None] = msgspec.field( + default_factory=dict + ) + """Job facets.""" + + # NOTE: Added to follow a similar pattern used in the Atlan Java SDK + # This field is excluded from serialization + producer: Union[str, None] = None + + @staticmethod + def _get_schema() -> str: + return "https://openlineage.io/spec/2-0-2/OpenLineage.json#/$defs/Job" + + @classmethod + def creator( + cls, connection_name: str, job_name: str, producer: str + ) -> OpenLineageJob: + """ + Builds the minimal object necessary to create an OpenLineage job. + + :param connection_name: name of the Spark connection + :param job_name: unique name of the job + :param producer: URI indicating the code or software that implements this job + :returns: the minimal request necessary to create the job + """ + return OpenLineageJob( + namespace=connection_name, name=job_name, producer=producer, facets={} + ) + + def create_input(self, namespace: str, asset_name: str) -> OpenLineageInputDataset: + """ + Builds the minimal object necessary to create an OpenLineage dataset, + wired to use as an input (source) for lineage. + + :param namespace: name of the source of the asset + :param asset_name: name of the asset, by OpenLineage standard + :returns: the minimal request necessary to create the input dataset + """ + return OpenLineageInputDataset.creator( + namespace=namespace, + asset_name=asset_name, + ) + + def create_output( + self, + namespace: str, + asset_name: str, + ) -> OpenLineageOutputDataset: + """ + Builds the minimal object necessary to create an OpenLineage dataset, + wired to use as an output (target) for lineage. + + :param namespace: name of the source of the asset + :param asset_name: name of the asset, by OpenLineage standard + :returns: the minimal request necessary to create the output dataset + """ + return OpenLineageOutputDataset.creator( + namespace=namespace, + asset_name=asset_name, + producer=self.producer or "", + ) diff --git a/pyatlan_v9/model/open_lineage/output_dataset.py b/pyatlan_v9/model/open_lineage/output_dataset.py new file mode 100644 index 000000000..0762767e4 --- /dev/null +++ b/pyatlan_v9/model/open_lineage/output_dataset.py @@ -0,0 +1,111 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +from __future__ import annotations + +from typing import Any, Dict, List, Union + +import msgspec + +from pyatlan_v9.model.open_lineage.dataset import OpenLineageDataset +from pyatlan_v9.model.open_lineage.facet import ( + OpenLineageColumnLineageDatasetFacet, + OpenLineageColumnLineageDatasetFacetFieldsAdditional, + OpenLineageColumnLineageDatasetFacetFieldsAdditionalInputFields, + OpenLineageDatasetFacets, +) + + +class OpenLineageOutputDataset(OpenLineageDataset): + """ + Model for handling OpenLineage datasets + to be used as lineage targets (outputs). + """ + + output_facets: Union[Dict[str, Any], None] = msgspec.field( + default_factory=dict, name="outputFacets" + ) + + # NOTE: to_fields is excluded from serialization; it's used + # to build column lineage facets before serialization. + to_fields: Union[ + List[ + Dict[ + str, + List[OpenLineageColumnLineageDatasetFacetFieldsAdditionalInputFields], + ] + ], + None, + ] = msgspec.field(default_factory=list) + + # NOTE: producer is excluded from serialization + producer: Union[str, None] = None + + @staticmethod + def _get_schema() -> str: + return "https://openlineage.io/spec/2-0-2/OpenLineage.json#/$defs/OutputDataset" + + @classmethod + def creator( + cls, namespace: str, asset_name: str, producer: str + ) -> OpenLineageOutputDataset: + """ + Builds the minimal object necessary to create + an OpenLineage dataset use-able as a lineage target. + + :param namespace: name of the source of the asset + :param asset_name: name of the asset, by OpenLineage standard + :param producer: a pre-configured OpenLineage producer + :returns: the minimal request necessary to create the output dataset + """ + return OpenLineageOutputDataset( + namespace=namespace, name=asset_name, producer=producer + ) + + def _build_facets(self) -> Dict[str, Any]: + """ + Transform to_fields into facets dict for serialization. + Returns a facets dict with column lineage if to_fields is populated. + """ + column_lineage_data: Dict[ + str, OpenLineageColumnLineageDatasetFacetFieldsAdditional + ] = {} + producer = self.producer or "" + + if self.to_fields: + for entry in self.to_fields: + for key, value in entry.items(): + fields_additional = ( + OpenLineageColumnLineageDatasetFacetFieldsAdditional( + input_fields=value + ) + ) + column_lineage_data[key] = fields_additional + + if column_lineage_data: + dataset_facets = OpenLineageDatasetFacets( + column_lineage=OpenLineageColumnLineageDatasetFacet( + fields=column_lineage_data, + producer=producer, + ) + ) + return msgspec.to_builtins(dataset_facets) + return self.facets or {} + + def to_dict(self) -> Dict[str, Any]: + """ + Serialize this output dataset to a dictionary, applying + the to_fields → facets transformation. + """ + facets = self._build_facets() + result: Dict[str, Any] = {} + if self.namespace is not None: + result["namespace"] = self.namespace + if self.name is not None: + result["name"] = self.name + # Always include facets (even when empty) — matches legacy behaviour + if facets is not None: + result["facets"] = facets + if self.output_facets: + result["outputFacets"] = self.output_facets + return result diff --git a/pyatlan_v9/model/open_lineage/run.py b/pyatlan_v9/model/open_lineage/run.py new file mode 100644 index 000000000..b9e3eb375 --- /dev/null +++ b/pyatlan_v9/model/open_lineage/run.py @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +from __future__ import annotations + +from typing import Any, Dict, Union +from uuid import UUID + +import msgspec + +from pyatlan_v9.model.open_lineage.job import OpenLineageJob +from pyatlan_v9.model.open_lineage.utils import generate_new_uuid + + +class OpenLineageRun(msgspec.Struct, kw_only=True, omit_defaults=True): + """ + Atlan wrapper for abstracting OpenLineage runs. + + A run is an instance of a job execution. + + For more details + https://openlineage.io/docs/spec/object-model#run + """ + + # job is excluded from serialization (it's a reference field) + job: Union[OpenLineageJob, None] = None + + run_id: Union[str, None] = msgspec.field(default=None, name="runId") + """Globally unique ID of the run associated with the job.""" + + facets: Union[Dict[str, Any], None] = msgspec.field(default_factory=dict) + """Run facets.""" + + def __post_init__(self) -> None: + if self.run_id is not None: + # Validate it's a valid UUID + UUID(self.run_id) + + @staticmethod + def _get_schema() -> str: + return "https://openlineage.io/spec/2-0-2/OpenLineage.json#/$defs/Run" + + @classmethod + def creator(cls, job: OpenLineageJob) -> OpenLineageRun: + """ + Builds the minimal object necessary to create an OpenLineage run. + + :param job: OpenLineage job for which to create a new run + :returns: the minimal request necessary to create the run + """ + return OpenLineageRun(job=job, run_id=str(generate_new_uuid()), facets={}) diff --git a/pyatlan_v9/model/open_lineage/utils.py b/pyatlan_v9/model/open_lineage/utils.py new file mode 100644 index 000000000..d1563c26d --- /dev/null +++ b/pyatlan_v9/model/open_lineage/utils.py @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +# Re-export from legacy module for convenience +from pyatlan.model.open_lineage.utils import generate_new_uuid + +__all__ = ["generate_new_uuid"] diff --git a/pyatlan_v9/model/packages/__init__.py b/pyatlan_v9/model/packages/__init__.py new file mode 100644 index 000000000..8ce32637d --- /dev/null +++ b/pyatlan_v9/model/packages/__init__.py @@ -0,0 +1,69 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +""" +PyAtlan V9 Package models — msgspec-based equivalents of pyatlan.model.packages. + +These package builders use v9 workflow (msgspec.Struct) and credential models +instead of legacy Pydantic models. +""" + +from pyatlan_v9.model.packages.api_token_connection_admin import ( # noqa: F401 + APITokenConnectionAdmin, +) +from pyatlan_v9.model.packages.asset_export_basic import AssetExportBasic # noqa: F401 +from pyatlan_v9.model.packages.asset_import import AssetImport # noqa: F401 +from pyatlan_v9.model.packages.big_query_crawler import BigQueryCrawler # noqa: F401 +from pyatlan_v9.model.packages.confluent_kafka_crawler import ( # noqa: F401 + ConfluentKafkaCrawler, +) +from pyatlan_v9.model.packages.connection_delete import ConnectionDelete # noqa: F401 +from pyatlan_v9.model.packages.databricks_crawler import DatabricksCrawler # noqa: F401 +from pyatlan_v9.model.packages.databricks_miner import DatabricksMiner # noqa: F401 +from pyatlan_v9.model.packages.dbt_crawler import DbtCrawler # noqa: F401 +from pyatlan_v9.model.packages.dynamo_d_b_crawler import DynamoDBCrawler # noqa: F401 +from pyatlan_v9.model.packages.glue_crawler import GlueCrawler # noqa: F401 +from pyatlan_v9.model.packages.lineage_builder import LineageBuilder # noqa: F401 +from pyatlan_v9.model.packages.lineage_generator_nt import ( # noqa: F401 + LineageGenerator, +) +from pyatlan_v9.model.packages.mongodb_crawler import MongoDBCrawler # noqa: F401 +from pyatlan_v9.model.packages.oracle_crawler import OracleCrawler # noqa: F401 +from pyatlan_v9.model.packages.postgres_crawler import PostgresCrawler # noqa: F401 +from pyatlan_v9.model.packages.powerbi_crawler import PowerBICrawler # noqa: F401 +from pyatlan_v9.model.packages.relational_assets_builder import ( # noqa: F401 + RelationalAssetsBuilder, +) +from pyatlan_v9.model.packages.s_q_l_server_crawler import ( # noqa: F401 + SQLServerCrawler, +) +from pyatlan_v9.model.packages.sigma_crawler import SigmaCrawler # noqa: F401 +from pyatlan_v9.model.packages.snowflake_crawler import SnowflakeCrawler # noqa: F401 +from pyatlan_v9.model.packages.snowflake_miner import SnowflakeMiner # noqa: F401 +from pyatlan_v9.model.packages.tableau_crawler import TableauCrawler # noqa: F401 + +__all__ = [ + "APITokenConnectionAdmin", + "AssetExportBasic", + "AssetImport", + "BigQueryCrawler", + "ConfluentKafkaCrawler", + "ConnectionDelete", + "DatabricksCrawler", + "DatabricksMiner", + "DbtCrawler", + "DynamoDBCrawler", + "GlueCrawler", + "LineageBuilder", + "LineageGenerator", + "MongoDBCrawler", + "OracleCrawler", + "PostgresCrawler", + "PowerBICrawler", + "RelationalAssetsBuilder", + "SQLServerCrawler", + "SigmaCrawler", + "SnowflakeCrawler", + "SnowflakeMiner", + "TableauCrawler", +] diff --git a/pyatlan_v9/model/packages/api_token_connection_admin.py b/pyatlan_v9/model/packages/api_token_connection_admin.py new file mode 100644 index 000000000..5fcb58ded --- /dev/null +++ b/pyatlan_v9/model/packages/api_token_connection_admin.py @@ -0,0 +1,73 @@ +from __future__ import annotations + +from pyatlan.model.enums import WorkflowPackage +from pyatlan_v9.model.packages.base.custom_package import AbstractCustomPackage +from pyatlan_v9.model.workflow import WorkflowMetadata + + +class APITokenConnectionAdmin(AbstractCustomPackage): + """ + Base configuration for a new API token connection admin package. + """ + + _NAME = "api-token-connection-admin" + _PACKAGE_NAME = f"@csa/{_NAME}" + _PACKAGE_PREFIX = WorkflowPackage.API_TOKEN_CONNECTION_ADMIN.value + _PACKAGE_ICON = "http://assets.atlan.com/assets/ph-key-light.svg" + _PACKAGE_LOGO = "http://assets.atlan.com/assets/ph-key-light.svg" + + def config( + self, connection_qualified_name: str, api_token_guid: str + ) -> APITokenConnectionAdmin: + """ + Set up the API token connection admin with the specified configuration. + + :param connection_qualified_name: connection qualified name + to which you want to add the API token as a connection admin. + :param api_token_guid: guid of the API token + + :returns: package, with the specified configuration. + """ + self._parameters.append( + {"name": "connection_qualified_name", "value": connection_qualified_name} + ) + self._parameters.append({"name": "api_token_guid", "value": api_token_guid}) + return self + + def _get_metadata(self) -> WorkflowMetadata: + return WorkflowMetadata( + labels={ + "orchestration.atlan.com/certified": "true", + "orchestration.atlan.com/source": "atlan", + "orchestration.atlan.com/sourceCategory": "utility", + "orchestration.atlan.com/type": "custom", + "orchestration.atlan.com/preview": "true", + "orchestration.atlan.com/verified": "true", + "package.argoproj.io/installer": "argopm", + "package.argoproj.io/name": f"a-t-rcsas-l-a-s-h{self._NAME}", + "package.argoproj.io/registry": "httpsc-o-l-o-ns-l-a-s-hs-l-a-s-hpackages.atlan.com", + "orchestration.atlan.com/atlan-ui": "true", + }, + annotations={ + "orchestration.atlan.com/allowSchedule": "true", + "orchestration.atlan.com/categories": "kotlin,utility", + "orchestration.atlan.com/dependentPackage": "", + "orchestration.atlan.com/docsUrl": f"https://solutions.atlan.com/{self._NAME}/", + "orchestration.atlan.com/emoji": "\U0001f680", + "orchestration.atlan.com/icon": self._PACKAGE_ICON, + "orchestration.atlan.com/logo": self._PACKAGE_LOGO, + "orchestration.atlan.com/name": "API Token Connection Admin", + "package.argoproj.io/author": "Atlan CSA", + "package.argoproj.io/description": "Assigns an API token as a connection admin for an existing connection.", # noqa + "package.argoproj.io/homepage": f"https://packages.atlan.com/-/web/detail/{self._PACKAGE_NAME}", + "package.argoproj.io/keywords": '["kotlin","utility"]', # fmt: skip + "package.argoproj.io/name": self._PACKAGE_NAME, + "package.argoproj.io/parent": ".", + "package.argoproj.io/registry": "https://packages.atlan.com", + "package.argoproj.io/repository": "git+https://github.com/atlanhq/marketplace-packages.git", + "package.argoproj.io/support": "support@atlan.com", + "orchestration.atlan.com/atlanName": f"csa-{self._NAME}-{self._epoch}", + }, + name=f"csa-{self._NAME}-{self._epoch}", + namespace="default", + ) diff --git a/pyatlan_v9/model/packages/asset_export_basic.py b/pyatlan_v9/model/packages/asset_export_basic.py new file mode 100644 index 000000000..8ac6ac6e1 --- /dev/null +++ b/pyatlan_v9/model/packages/asset_export_basic.py @@ -0,0 +1,329 @@ +from __future__ import annotations + +from typing import List, Optional + +from pyatlan.model.enums import WorkflowPackage +from pyatlan_v9.model.packages.base.custom_package import AbstractCustomPackage +from pyatlan_v9.model.workflow import WorkflowMetadata + + +class AssetExportBasic(AbstractCustomPackage): + """ + Base configuration for the Asset Export package. + """ + + _NAME = "asset-export-basic" + _PACKAGE_NAME = f"@csa/{_NAME}" + _PACKAGE_PREFIX = WorkflowPackage.ASSET_EXPORT_BASIC.value + _PACKAGE_ICON = "http://assets.atlan.com/assets/ph-cloud-arrow-down-light.svg" + _PACKAGE_LOGO = "http://assets.atlan.com/assets/ph-cloud-arrow-down-light.svg" + + def __init__( + self, + ): + super().__init__() + self._email_addresses = None + self._delivery_type = None + self._export_scope = None + self._parameters = [] + + def glossaries_only( + self, include_archived: Optional[bool] = None + ) -> AssetExportBasic: + """ + Set up the package to export only glossaries. + + :param include_archived: Whether to include archived assets in the export (true) or only active assets (false). + + :returns: package, set up to export only glossaries + """ + self._export_scope = "GLOSSARIES_ONLY" + self._parameters.append({"name": "export_scope", "value": self._export_scope}) + self._parameters.append( + { + "name": "include_archived", + "value": include_archived, + } + ) + return self + + def enriched_only( + self, + prefix: str, + include_description: Optional[bool] = None, + include_glossaries: Optional[bool] = None, + include_data_products: Optional[bool] = None, + include_archived: Optional[bool] = None, + ) -> AssetExportBasic: + """ + Set up the package to export only enriched assets. + + :param prefix: Starting value for a qualifiedName that will determine which assets to export. + :param include_description: Whether to extract only user-entered description (false), or to also include + system-level description (true). + :param include_glossaries: Whether glossaries (and their terms and + categories) should be exported, too. + :param include_data_products: Whether data products (and their domains) + should be exported, too. + :param include_archived: Whether to include archived assets in the export (true) or + only active assets (false). + + :returns: package, set up to export only enriched assets + """ + self._export_scope = "ENRICHED_ONLY" + self._parameters.append({"name": "export_scope", "value": self._export_scope}) + params = { + "qn_prefix": prefix, + "include_description": include_description, + "include_glossaries": include_glossaries, + "include_products": include_data_products, + "include_archived": include_archived, + } + self._add_optional_params(params) + return self + + def products_only( + self, include_archived: Optional[bool] = None + ) -> AssetExportBasic: + """ + Set up the package to export only data products. + + :param include_archived: Whether to include archived assets in the export (true) or only active assets (false). + + :returns: package, set up to export only data products + """ + self._export_scope = "PRODUCTS_ONLY" + self._parameters.append({"name": "export_scope", "value": self._export_scope}) + self._parameters.append( + { + "name": "include_archived", + "value": include_archived, + } + ) + return self + + def all_assets( + self, + prefix: str, + include_description: Optional[bool] = None, + include_glossaries: Optional[bool] = None, + include_data_products: Optional[bool] = None, + include_archived: Optional[bool] = None, + ) -> AssetExportBasic: + """ + Set up the package to export all assets. + + :param prefix: Starting value for a qualifiedName that will determine which assets to export. + :param include_description: Whether to extract only user-entered description (false), or to also include + system-level description (true). + :param include_glossaries: Whether glossaries (and their terms and + categories) should be exported, too. + :param include_data_products: Whether data products (and their domains) + should be exported, too. + :param include_archived: Whether to include archived assets in the export (true) or + only active assets (false). + + :returns: package, set up to export all assets + """ + self._export_scope = "ALL" + self._parameters.append({"name": "export_scope", "value": self._export_scope}) + params = { + "qn_prefix": prefix, + "include_description": include_description, + "include_glossaries": include_glossaries, + "include_products": include_data_products, + "include_archived": include_archived, + } + self._add_optional_params(params) + + return self + + def direct(self) -> AssetExportBasic: + """ + Set up the package to deliver the export via direct download. + + :returns: package, set up to deliver the export via direct download + """ + self._delivery_type = "DIRECT" + self._add_delivery_parameters() + return self + + def email(self, email_addresses: List[str]) -> AssetExportBasic: + """ + Set up the package to deliver the export via email. + + :param email_addresses: List of email addresses to send the export to. + + :returns: package, set up to deliver the export via email + """ + self._delivery_type = "EMAIL" + self._email_addresses = email_addresses + self._add_delivery_parameters() + + return self + + def object_store(self, prefix: Optional[str] = None) -> AssetExportBasic: + """ + Set up the package to export to an object storage location. + + :param prefix: The directory (path) within the object store to upload the exported file. + + :returns: package, set up to export metadata to an object store + """ + self._delivery_type = "CLOUD" + self._add_delivery_parameters() + self._parameters.append({"name": "target_prefix", "value": prefix}) + self._parameters.append({"name": "cloud_target", "value": "{{credentialGuid}}"}) + return self + + def s3( + self, + access_key: str, + secret_key: str, + bucket: str, + region: str, + ) -> AssetExportBasic: + """ + Set up package to export to S3. + + :param access_key: AWS access key + :param secret_key: AWS secret key + :param bucket: S3 bucket to upload the export file to + :param region: AWS region + + :returns: package, set up to export metadata to S3 + """ + self._credentials_body.update( + { + "name": f"csa-{self._NAME}-{self._epoch}-0", + "auth_type": "s3", + "username": access_key, + "password": secret_key, + "extra": { + "region": region, + "s3_bucket": bucket, + }, + "connector_config_name": "csa-connectors-objectstore", + } + ) + return self + + def gcs( + self, project_id: str, service_account_json: str, bucket: str + ) -> AssetExportBasic: + """ + Set up package to export to Google Cloud Storage. + + :param project_id: ID of GCP project + :param service_account_json: service account credentials in JSON format + :param bucket: bucket to upload the export file to + + :returns: package, set up to export metadata to GCS + """ + self._credentials_body.update( + { + "name": f"csa-{self._NAME}-{self._epoch}-0", + "auth_type": "gcs", + "username": project_id, + "password": service_account_json, + "extra": { + "gcs_bucket": bucket, + }, + "connector_config_name": "csa-connectors-objectstore", + } + ) + return self + + def adls( + self, + client_id: str, + client_secret: str, + tenant_id: str, + account_name: str, + container: str, + ) -> AssetExportBasic: + """ + Set up package to export to Azure Data Lake Storage. + + :param client_id: unique application (client) ID assigned by Azure AD when the app was registered + :param client_secret: client secret for authentication + :param tenant_id: unique ID of the Azure Active Directory instance + :param account_name: name of the storage account + :param container: container to upload the export file to + + :returns: package, set up to export metadata to ADLS + """ + self._credentials_body.update( + { + "name": f"csa-{self._NAME}-{self._epoch}-0", + "auth_type": "adls", + "username": client_id, + "password": client_secret, + "extra": { + "azure_tenant_id": tenant_id, + "storage_account_name": account_name, + "adls_container": container, + }, + "connector_config_name": "csa-connectors-objectstore", + } + ) + return self + + def _add_delivery_parameters(self): + """ + Add delivery parameters to the parameters list. + """ + self._parameters.append( + { + "name": "delivery_type", + "value": self._delivery_type, + } + ) + if self._delivery_type == "EMAIL" and self._email_addresses: + self._parameters.append( + { + "name": "email_addresses", + "value": ",".join( + self._email_addresses + ), # Join the email addresses if they are in a list + } + ) + + def _get_metadata(self) -> WorkflowMetadata: + return WorkflowMetadata( + labels={ + "orchestration.atlan.com/certified": "true", + "orchestration.atlan.com/preview": "true", + "orchestration.atlan.com/source": self._NAME, + "orchestration.atlan.com/sourceCategory": "utility", + "orchestration.atlan.com/type": "custom", + "orchestration.atlan.com/verified": "true", + "package.argoproj.io/installer": "argopm", + "package.argoproj.io/name": f"a-t-rcsas-l-a-s-h{self._NAME}", + "package.argoproj.io/registry": "httpsc-o-l-o-ns-l-a-s-hs-l-a-s-hpackages.atlan.com", + "orchestration.atlan.com/atlan-ui": "true", + }, + annotations={ + "orchestration.atlan.com/allowSchedule": "true", + "orchestration.atlan.com/categories": "kotlin,utility", + "orchestration.atlan.com/dependentPackage": "", + "orchestration.atlan.com/docsUrl": f"https://solutions.atlan.com/{self._NAME}/", + "orchestration.atlan.com/emoji": "🚀", + "orchestration.atlan.com/icon": self._PACKAGE_ICON, + "orchestration.atlan.com/logo": self._PACKAGE_LOGO, # noqa + "orchestration.atlan.com/name": "Asset Export (Basic)", + "package.argoproj.io/author": "Atlan CSA", + "package.argoproj.io/description": "Export assets with all enrichment that could be made against them " + "via the Atlan UI.", + "package.argoproj.io/homepage": f"https://packages.atlan.com/-/web/detail/{self._PACKAGE_NAME}", + "package.argoproj.io/keywords": '["kotlin","utility"]', # fmt: skip + "package.argoproj.io/name": self._PACKAGE_NAME, + "package.argoproj.io/parent": ".", + "package.argoproj.io/registry": "https://packages.atlan.com", + "package.argoproj.io/repository": "git+https://github.com/atlanhq/marketplace-packages.git", + "package.argoproj.io/support": "support@atlan.com", + "orchestration.atlan.com/atlanName": f"csa-{self._NAME}-{self._epoch}", + }, + name=f"csa-{self._NAME}-{self._epoch}", + namespace="default", + ) diff --git a/pyatlan_v9/model/packages/asset_import.py b/pyatlan_v9/model/packages/asset_import.py new file mode 100644 index 000000000..839d8ec8d --- /dev/null +++ b/pyatlan_v9/model/packages/asset_import.py @@ -0,0 +1,390 @@ +from __future__ import annotations + +from json import dumps +from typing import List, Optional, Union + +from pyatlan.model.enums import AssetInputHandling, WorkflowPackage +from pyatlan.model.fields.atlan_fields import AtlanField +from pyatlan_v9.model.packages.base.custom_package import AbstractCustomPackage +from pyatlan_v9.model.workflow import WorkflowMetadata + + +class AssetImport(AbstractCustomPackage): + """ + Base configuration for a new Asset Import package. + """ + + _NAME = "asset-import" + _PACKAGE_NAME = f"@csa/{_NAME}" + _PACKAGE_PREFIX = WorkflowPackage.ASSET_IMPORT.value + _PACKAGE_ICON = "http://assets.atlan.com/assets/ph-cloud-arrow-up-light.svg" + _PACKAGE_LOGO = "http://assets.atlan.com/assets/ph-cloud-arrow-up-light.svg" + + def __init__( + self, + ): + self._assets_advanced = False + self._glossaries_advanced = False + self._data_product_advanced = False + super().__init__() + + def object_store(self) -> AssetImport: + """ + Set up the package to import + metadata directly from the object store. + """ + self._parameters.append({"name": "import_type", "value": "CLOUD"}) + self._parameters.append({"name": "cloud_source", "value": "{{credentialGuid}}"}) + return self + + def s3( + self, + access_key: str, + secret_key: str, + region: str, + bucket: str, + ) -> AssetImport: + """ + Set up package to import metadata from S3. + + :param access_key: AWS access key + :param secret_key: AWS secret key + :param region: AWS region + :param bucket: bucket to retrieve object store object from + + :returns: package, set up to import metadata from S3 + """ + local_creds = { + "name": f"csa-{self._NAME}-{self._epoch}-0", + "auth_type": "s3", + "username": access_key, + "password": secret_key, + "extra": { + "region": region, + "s3_bucket": bucket, + }, + "connector_config_name": "csa-connectors-objectstore", + } + self._credentials_body.update(local_creds) + return self + + def gcs( + self, project_id: str, service_account_json: str, bucket: str + ) -> AssetImport: + """ + Set up package to import metadata from GCS. + + :param project_id: ID of GCP project + :param service_account_json: service account credentials in JSON format + :param bucket: bucket to retrieve object store object from + + :returns: Package set up to import metadata from GCS + """ + local_creds = { + "name": f"csa-{self._NAME}-{self._epoch}-0", + "auth_type": "gcs", + "username": project_id, + "password": service_account_json, + "extra": { + "gcs_bucket": bucket, + }, + "connector_config_name": "csa-connectors-objectstore", + } + self._credentials_body.update(local_creds) + return self + + def adls( + self, + client_id: str, + client_secret: str, + tenant_id: str, + account_name: str, + container: str, + ) -> AssetImport: + """ + Set up package to import metadata from ADLS. + + :param client_id: unique application (client) ID assigned by Azure AD when the app was registered + :param client_secret: client secret for authentication + :param tenant_id: unique ID of the Azure Active Directory instance + :param account_name: name of the storage account + :param container: container to retrieve object store objects from + + :returns: package, set up to import metadata from ADLS + """ + local_creds = { + "name": f"csa-{self._NAME}-{self._epoch}-0", + "auth_type": "adls", + "username": client_id, + "password": client_secret, + "extra": { + "azure_tenant_id": tenant_id, + "storage_account_name": account_name, + "adls_container": container, + }, + "connector_config_name": "csa-connectors-objectstore", + } + self._credentials_body.update(local_creds) + return self + + def assets( + self, + prefix: str, + object_key: str, + input_handling: AssetInputHandling = AssetInputHandling.UPDATE, + ) -> AssetImport: + """ + Set up package to import assets. + + :param prefix: directory (path) within the object store from + which to retrieve the file containing asset metadata + :param object_key: object key (filename), + including its extension, within the object store and prefix + :param input_handling: specifies whether to allow the creation + of new assets from the input CSV (full or partial assets) + or only update existing assets in Atlan + + :returns: package, configured to import assets + """ + self._parameters.append({"name": "assets_prefix", "value": prefix}) + self._parameters.append({"name": "assets_key", "value": object_key}) + self._parameters.append( + {"name": "assets_upsert_semantic", "value": input_handling} + ) + return self + + def assets_advanced( + self, + remove_attributes: Optional[Union[List[str], List[AtlanField]]] = None, + fail_on_errors: Optional[bool] = None, + case_sensitive_match: Optional[bool] = None, + is_table_view_agnostic: Optional[bool] = None, + field_separator: Optional[str] = None, + batch_size: Optional[int] = None, + ) -> AssetImport: + """ + Set up package to import assets with advanced configuration. + + :param remove_attributes: list of attributes to clear (remove) + from assets if their value is blank in the provided file. + :param fail_on_errors: specifies whether an invalid value + in a field should cause the import to fail (`True`) or + log a warning, skip that value, and proceed (`False`). + :param case_sensitive_match: indicates whether to use + case-sensitive matching when running in update-only mode (`True`) + or to try case-insensitive matching (`False`). + :param is_table_view_agnostic: specifies whether to treat + tables, views, and materialized views as interchangeable (`True`) + or to strictly adhere to specified types in the input (`False`). + :param field_separator: character used to separate + fields in the input file (e.g., ',' or ';'). + :param batch_size: maximum number of rows + to process at a time (per API request). + + :returns: package, configured to import + assets with advanced configuration. + """ + if isinstance(remove_attributes, list) and all( + isinstance(field, AtlanField) for field in remove_attributes + ): + remove_attributes = [field.atlan_field_name for field in remove_attributes] # type: ignore + params = { + "assets_attr_to_overwrite": dumps(remove_attributes, separators=(",", ":")), + "assets_fail_on_errors": fail_on_errors, + "assets_case_sensitive": case_sensitive_match, + "assets_table_view_agnostic": is_table_view_agnostic, + "assets_field_separator": field_separator, + "assets_batch_size": batch_size, + } + self._add_optional_params(params) + self._assets_advanced = True + return self + + def glossaries( + self, + prefix: str, + object_key: str, + input_handling: AssetInputHandling = AssetInputHandling.UPDATE, + ) -> AssetImport: + """ + Set up package to import glossaries. + + :param prefix: directory (path) within the object store from + which to retrieve the file containing glossaries, categories and terms + :param object_key: object key (filename), + including its extension, within the object store and prefix + :param input_handling: specifies whether to allow the creation of new glossaries, + categories and terms from the input CSV, or ensure these are only updated + if they already exist in Atlan. + + :returns: package, configured to import glossaries, categories and terms. + """ + self._parameters.append({"name": "glossaries_prefix", "value": prefix}) + self._parameters.append({"name": "glossaries_key", "value": object_key}) + self._parameters.append( + {"name": "glossaries_upsert_semantic", "value": input_handling} + ) + return self + + def glossaries_advanced( + self, + remove_attributes: Optional[Union[List[str], List[AtlanField]]] = None, + fail_on_errors: Optional[bool] = None, + field_separator: Optional[str] = None, + batch_size: Optional[int] = None, + ) -> AssetImport: + """ + Set up package to import glossaries with advanced configuration. + + :param remove_attributes: list of attributes to clear (remove) + from assets if their value is blank in the provided file. + :param fail_on_errors: specifies whether an invalid value + in a field should cause the import to fail (`True`) or + log a warning, skip that value, and proceed (`False`). + :param field_separator: character used to separate + fields in the input file (e.g., ',' or ';'). + :param batch_size: maximum number of rows + to process at a time (per API request). + + :returns: package, configured to import + glossaries with advanced configuration. + """ + if isinstance(remove_attributes, list) and all( + isinstance(field, AtlanField) for field in remove_attributes + ): + remove_attributes = [field.atlan_field_name for field in remove_attributes] # type: ignore + params = { + "glossaries_attr_to_overwrite": dumps( + remove_attributes, separators=(",", ":") + ), + "glossaries_fail_on_errors": fail_on_errors, + "glossaries_field_separator": field_separator, + "glossaries_batch_size": batch_size, + } + self._add_optional_params(params) + self._glossaries_advanced = True + return self + + def data_products( + self, + prefix: str, + object_key: str, + input_handling: AssetInputHandling = AssetInputHandling.UPDATE, + ) -> AssetImport: + """ + Set up package to import data products. + + :param prefix: directory (path) within the object store from + which to retrieve the file containing data domains, and data products + :param object_key: object key (filename), + including its extension, within the object store and prefix + :param input_handling: specifies whether to allow the creation of new data domains, and data products + from the input CSV, or ensure these are only updated if they already exist in Atlan. + + :returns: package, configured to import data domain and data products + """ + self._parameters.append({"name": "data_products_prefix", "value": prefix}) + self._parameters.append({"name": "data_products_key", "value": object_key}) + self._parameters.append( + {"name": "data_products_upsert_semantic", "value": input_handling} + ) + return self + + def data_product_advanced( + self, + remove_attributes: Optional[Union[List[str], List[AtlanField]]] = None, + fail_on_errors: Optional[bool] = None, + field_separator: Optional[str] = None, + batch_size: Optional[int] = None, + ) -> AssetImport: + """ + Set up package to import data domain + and data products with advanced configuration. + + :param remove_attributes: list of attributes to clear (remove) + from assets if their value is blank in the provided file. + :param fail_on_errors: specifies whether an invalid value + in a field should cause the import to fail (`True`) or + log a warning, skip that value, and proceed (`False`). + :param field_separator: character used to separate + fields in the input file (e.g., ',' or ';'). + :param batch_size: maximum number of rows + to process at a time (per API request). + + :returns: package, configured to import + data domain and data products with advanced configuration. + """ + if isinstance(remove_attributes, list) and all( + isinstance(field, AtlanField) for field in remove_attributes + ): + remove_attributes = [field.atlan_field_name for field in remove_attributes] # type: ignore + params = { + "data_products_attr_to_overwrite": dumps( + remove_attributes, separators=(",", ":") + ), + "data_products_fail_on_errors": fail_on_errors, + "data_products_field_separator": field_separator, + "data_products_batch_size": batch_size, + } + self._add_optional_params(params) + self._data_product_advanced = True + return self + + def _set_required_metadata_params(self): + self._parameters.append( + dict( + name="assets_config", + value="advanced" if self._assets_advanced else "default", + ) + ) + self._parameters.append( + dict( + name="glossaries_config", + value="advanced" if self._glossaries_advanced else "default", + ) + ) + self._parameters.append( + dict( + name="data_products_config", + value="advanced" if self._data_product_advanced else "default", + ) + ) + + def _get_metadata(self) -> WorkflowMetadata: + self._set_required_metadata_params() + return WorkflowMetadata( + labels={ + "orchestration.atlan.com/certified": "true", + "orchestration.atlan.com/source": self._NAME, + "orchestration.atlan.com/sourceCategory": "utility", + "orchestration.atlan.com/type": "custom", + "orchestration.atlan.com/preview": "true", + "orchestration.atlan.com/verified": "true", + "package.argoproj.io/installer": "argopm", + "package.argoproj.io/name": f"a-t-rcsas-l-a-s-h{self._NAME}", + "package.argoproj.io/registry": "httpsc-o-l-o-ns-l-a-s-hs-l-a-s-hpackages.atlan.com", + "orchestration.atlan.com/atlan-ui": "true", + }, + annotations={ + "orchestration.atlan.com/allowSchedule": "true", + "orchestration.atlan.com/categories": "kotlin,utility", + "orchestration.atlan.com/dependentPackage": "", + "orchestration.atlan.com/docsUrl": f"https://solutions.atlan.com/{self._NAME}/", + "orchestration.atlan.com/emoji": "\U0001f680", + "orchestration.atlan.com/icon": self._PACKAGE_ICON, + "orchestration.atlan.com/logo": self._PACKAGE_LOGO, # noqa + "orchestration.atlan.com/name": "Asset Import", + "package.argoproj.io/author": "Atlan CSA", + "package.argoproj.io/description": "Import assets from a CSV file.", + "package.argoproj.io/homepage": f"https://packages.atlan.com/-/web/detail/{self._PACKAGE_NAME}", + "package.argoproj.io/keywords": '["kotlin","utility"]', # fmt: skip + "package.argoproj.io/name": self._PACKAGE_NAME, + "package.argoproj.io/parent": ".", + "package.argoproj.io/registry": "https://packages.atlan.com", + "package.argoproj.io/repository": "git+https://github.com/atlanhq/marketplace-packages.git", + "package.argoproj.io/support": "support@atlan.com", + "orchestration.atlan.com/atlanName": f"csa-{self._NAME}-{self._epoch}", + }, + name=f"csa-{self._NAME}-{self._epoch}", + namespace="default", + ) diff --git a/pyatlan_v9/model/packages/base/__init__.py b/pyatlan_v9/model/packages/base/__init__.py new file mode 100644 index 000000000..578a3ce52 --- /dev/null +++ b/pyatlan_v9/model/packages/base/__init__.py @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. diff --git a/pyatlan_v9/model/packages/base/crawler.py b/pyatlan_v9/model/packages/base/crawler.py new file mode 100644 index 000000000..67cbe6bf0 --- /dev/null +++ b/pyatlan_v9/model/packages/base/crawler.py @@ -0,0 +1,165 @@ +from json import dumps +from typing import Any, Dict, List, Optional + +from pyatlan import utils +from pyatlan.client.atlan import AtlanClient +from pyatlan.errors import ErrorCode +from pyatlan_v9.model.assets import Connection +from pyatlan_v9.model.packages.base.package import AbstractPackage + + +class AbstractCrawler(AbstractPackage): + """ + Abstract class for crawlers (v9 — uses msgspec models). + + :param client: connectivity to an Atlan tenant + :param connection_name: name for the connection + :param connection_type: type of connector for the connection + :param admin_roles: admin roles for the connection + :param admin_groups: admin groups for the connection + :param admin_users: admin users for the connection + :param allow_query: allow data to be queried in the connection (True) or not (False) + :param allow_query_preview: allow sample data viewing for assets in the connection (True) or not (False) + :param row_limit: maximum number of rows that can be returned by a query + :param source_logo: logo to use for the source + + :raises AtlanError: if there is not at least one role, + group, or user defined as an admin (or any of them are invalid) + """ + + def __init__( + self, + client: AtlanClient, + connection_name: str, + connection_type: str, + admin_roles: Optional[List[str]] = None, + admin_groups: Optional[List[str]] = None, + admin_users: Optional[List[str]] = None, + allow_query: bool = False, + allow_query_preview: bool = False, + row_limit: int = 0, + source_logo: str = "", + ): + super().__init__() + self._client = client + self._connection_name = connection_name + self._connection_type = connection_type + self._admin_roles = admin_roles + self._admin_groups = admin_groups + self._admin_users = admin_users + self._allow_query = allow_query + self._allow_query_preview = allow_query_preview + self._row_limit = row_limit + self._source_logo = source_logo + self._epoch = int(utils.get_epoch_timestamp()) + + def _get_connection(self) -> Connection: + """ + Builds a connection using the provided parameters, + which will be the target for the package to crawl assets. + """ + connection = Connection.create( + client=self._client, + name=self._connection_name, + connector_type=self._connection_type, + admin_roles=self._admin_roles, + admin_groups=self._admin_groups, + admin_users=self._admin_users, + ) + connection.allow_query = self._allow_query + connection.allow_query_preview = self._allow_query_preview + connection.row_limit = self._row_limit + connection.default_credential_guid = "{{credentialGuid}}" + connection.source_logo = self._source_logo + connection.is_discoverable = True + connection.is_editable = False + return connection + + @staticmethod + def build_hierarchical_filter(raw_filter: Optional[dict]) -> str: + """ + Build an exact match filter from the provided map of databases and schemas. + + :param raw_filter: map keyed by database name with each value being a list of schemas + :returns: an exact-match filter map string, usable in crawlers include / exclude filters + :raises InvalidRequestException: In the unlikely event the provided filter cannot be translated + """ + to_include: Dict[str, Any] = {} + if not raw_filter: + return "" + try: + for db_name, schemas in raw_filter.items(): + exact_schemas = [f"^{schema}$" for schema in schemas] + to_include[f"^{db_name}$"] = exact_schemas + return dumps(to_include) + except (AttributeError, TypeError): + raise ErrorCode.UNABLE_TO_TRANSLATE_FILTERS.exception_with_parameters() + + @staticmethod + def build_flat_hierarchical_filter(raw_filter: Optional[list]) -> str: + """ + Build an exact match flat filter from the provided list of database names. + + :param raw_filter: list of databases names to exclude when crawling + :returns: an exact-match filter map string, usable in crawlers include / exclude filters + :raises InvalidRequestException: In the unlikely event the provided filter cannot be translated + """ + to_include: Dict[str, Any] = {} + if not raw_filter: + return "" + try: + for db_name in raw_filter: + to_include[f"{db_name}"] = {} + return dumps(to_include) + except (AttributeError, TypeError): + raise ErrorCode.UNABLE_TO_TRANSLATE_FILTERS.exception_with_parameters() + + @staticmethod + def build_selective_hierarchical_filter(raw_filter: Optional[dict]) -> str: + """ + Build a selective hierarchical filter from the provided map of databases and schemas. + """ + if not raw_filter: + return "" + + try: + to_include: Dict[str, Dict[str, Dict]] = {} + + for db_name, schemas in raw_filter.items(): + schema_dict: Dict[str, Any] = {} + for schema in schemas: + schema_dict[schema] = {} + to_include[db_name] = schema_dict + + return dumps(to_include) + except (AttributeError, TypeError): + raise ErrorCode.UNABLE_TO_TRANSLATE_FILTERS.exception_with_parameters() + + @staticmethod + def build_flat_filter(raw_filter: Optional[list]) -> str: + """ + Build a filter from the provided list of object names / IDs. + + :param raw_filter: list of objects for the filter + :returns: a filter map string, usable in crawlers include / exclude filters + :raises InvalidRequestException: In the unlikely event the provided filter cannot be translated + """ + to_include: Dict[str, Any] = {} + if not raw_filter: + return "" + try: + for entry in raw_filter: + to_include[entry] = {} + return dumps(to_include) + except (AttributeError, TypeError): + raise ErrorCode.UNABLE_TO_TRANSLATE_FILTERS.exception_with_parameters() + + def _add_optional_params(self, params: Dict[str, Optional[Any]]) -> None: + """ + Helper method to add non-None params to `self._parameters`. + + :param params: dict of param names and values. + """ + for name, value in params.items(): + if value is not None: + self._parameters.append({"name": name, "value": value}) diff --git a/pyatlan_v9/model/packages/base/custom_package.py b/pyatlan_v9/model/packages/base/custom_package.py new file mode 100644 index 000000000..1c537b603 --- /dev/null +++ b/pyatlan_v9/model/packages/base/custom_package.py @@ -0,0 +1,26 @@ +from typing import Any, Dict, Optional + +from pyatlan import utils +from pyatlan_v9.model.packages.base.package import AbstractPackage + + +class AbstractCustomPackage(AbstractPackage): + """ + Abstract class for custom packages (v9 — uses msgspec models). + """ + + def __init__( + self, + ): + super().__init__() + self._epoch = int(utils.get_epoch_timestamp()) + + def _add_optional_params(self, params: Dict[str, Optional[Any]]) -> None: + """ + Helper method to add non-None params to `self._parameters`. + + :param params: dict of param names and values. + """ + for name, value in params.items(): + if value is not None: + self._parameters.append({"name": name, "value": value}) diff --git a/pyatlan_v9/model/packages/base/miner.py b/pyatlan_v9/model/packages/base/miner.py new file mode 100644 index 000000000..66afa6bb8 --- /dev/null +++ b/pyatlan_v9/model/packages/base/miner.py @@ -0,0 +1,33 @@ +from typing import Any, Dict, Optional + +from pyatlan import utils +from pyatlan_v9.model.packages.base.package import AbstractPackage + + +class AbstractMiner(AbstractPackage): + """ + Abstract class for miners (v9 — uses msgspec models). + + :param connection_qualified_name: unique name of + the connection whose assets should be mined + """ + + def __init__( + self, + connection_qualified_name: str, + ): + super().__init__() + self._epoch = int(utils.get_epoch_timestamp()) + self._parameters.append( + dict(name="connection-qualified-name", value=connection_qualified_name) + ) + + def _add_optional_params(self, params: Dict[str, Optional[Any]]) -> None: + """ + Helper method to add non-None params to `self._parameters`. + + :param params: dict of param names and values. + """ + for name, value in params.items(): + if value is not None: + self._parameters.append({"name": name, "value": value}) diff --git a/pyatlan_v9/model/packages/base/package.py b/pyatlan_v9/model/packages/base/package.py new file mode 100644 index 000000000..4f68a0c2a --- /dev/null +++ b/pyatlan_v9/model/packages/base/package.py @@ -0,0 +1,97 @@ +from typing import Any + +import msgspec + +from pyatlan_v9.model.credential import Credential +from pyatlan_v9.model.workflow import ( + NameValuePair, + PackageParameter, + Workflow, + WorkflowDAG, + WorkflowMetadata, + WorkflowParameters, + WorkflowSpec, + WorkflowTask, + WorkflowTemplate, + WorkflowTemplateRef, + _remove_nones, +) + +# Map of camelCase / legacy aliases → v9 Credential Python field names. +# Crawlers were written against the legacy Pydantic model which accepted +# either camelCase aliases or snake_case names. +_CRED_KEY_MAP: dict[str, str] = { + "authType": "auth_type", + "connectorConfigName": "connector_config_name", + "connectorType": "connector_type", + "extra": "extras", +} + + +def _normalize_cred_keys(raw: dict[str, Any]) -> dict[str, Any]: + """Normalize credential dict keys to v9 Credential Python field names.""" + return {_CRED_KEY_MAP.get(k, k): v for k, v in raw.items()} + + +class AbstractPackage: + """ + Abstract class for packages (v9 — uses msgspec workflow models). + """ + + _PACKAGE_NAME: str = "" + _PACKAGE_PREFIX: str = "" + + def __init__(self): + self._parameters = [] + self._credentials_body: dict[str, Any] = {} + + def _get_metadata(self) -> WorkflowMetadata: + raise NotImplementedError + + def to_workflow(self) -> Workflow: + metadata = self._get_metadata() + spec = WorkflowSpec( + entrypoint="main", + templates=[ + WorkflowTemplate( + name="main", + dag=WorkflowDAG( + tasks=[ + WorkflowTask( + name="run", + arguments=WorkflowParameters( + parameters=msgspec.convert( + self._parameters, list[NameValuePair] + ) + ), + template_ref=WorkflowTemplateRef( + name=self._PACKAGE_PREFIX, + template="main", + cluster_scope=True, + ), + ) + ] + ), + ) + ], + workflow_metadata=WorkflowMetadata( + annotations={"package.argoproj.io/name": self._PACKAGE_NAME} + ), + ) + payload: list[PackageParameter] = [] + if self._credentials_body: + cred = Credential(**_normalize_cred_keys(self._credentials_body)) + # Convert to dict with camelCase keys, excluding None values + cred_dict: dict[str, Any] = _remove_nones(msgspec.to_builtins(cred)) + payload = [ + PackageParameter( + parameter="credentialGuid", + type="credential", + body=cred_dict, + ) + ] + return Workflow( + metadata=metadata, + spec=spec, + payload=payload, + ) diff --git a/pyatlan_v9/model/packages/big_query_crawler.py b/pyatlan_v9/model/packages/big_query_crawler.py new file mode 100644 index 000000000..e8610ac98 --- /dev/null +++ b/pyatlan_v9/model/packages/big_query_crawler.py @@ -0,0 +1,204 @@ +from __future__ import annotations + +from typing import Dict, List, Optional + +from pyatlan.model.enums import AtlanConnectorType, WorkflowPackage +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.model.packages.base.crawler import AbstractCrawler +from pyatlan_v9.model.workflow import WorkflowMetadata + + +class BigQueryCrawler(AbstractCrawler): + """ + Base configuration for a new BigQuery crawler. + + :param client: connectivity to an Atlan tenant + :param connection_name: name for the connection + :param admin_roles: admin roles for the connection + :param admin_groups: admin groups for the connection + :param admin_users: admin users for the connection + :param allow_query: allow data to be queried in the + connection (True) or not (False), default: True + :param allow_query_preview: allow sample data viewing for + assets in the connection (True) or not (False), default: True + :param row_limit: maximum number of rows + that can be returned by a query, default: 10000 + """ + + _NAME = "bigquery" + _PACKAGE_NAME = "@atlan/bigquery" + _PACKAGE_PREFIX = WorkflowPackage.BIGQUERY.value + _CONNECTOR_TYPE = AtlanConnectorType.BIGQUERY + _PACKAGE_ICON = "https://cdn.worldvectorlogo.com/logos/google-bigquery-logo-1.svg" + _PACKAGE_LOGO = "https://cdn.worldvectorlogo.com/logos/google-bigquery-logo-1.svg" + + def __init__( + self, + client: AtlanClient, + connection_name: str, + admin_roles: Optional[List[str]] = None, + admin_groups: Optional[List[str]] = None, + admin_users: Optional[List[str]] = None, + allow_query: bool = True, + allow_query_preview: bool = True, + row_limit: int = 10000, + ): + self._advanced_config = False + super().__init__( + client=client, + connection_name=connection_name, + connection_type=self._CONNECTOR_TYPE, + admin_roles=admin_roles, + admin_groups=admin_groups, + admin_users=admin_users, + allow_query=allow_query, + allow_query_preview=allow_query_preview, + row_limit=row_limit, + source_logo=self._PACKAGE_LOGO, + ) + + def service_account_auth( + self, + project_id: str, + service_account_json: str, + service_account_email: str, + ) -> BigQueryCrawler: + """ + Set up the crawler to use service account authentication. + + :param project_id: project ID of your Google Cloud project + :param service_account_json: entire service account json + :param service_account_email: service account email + :returns: crawler, set up to use service account authentication + """ + creds = { + "name": f"default-bigquery-{self._epoch}-0", + "host": "https://www.googleapis.com/bigquery/v2", + "port": 443, + "auth_type": "basic", + "username": service_account_email, + "password": service_account_json, + "extras": {"project_id": project_id}, + "connector_config_name": f"atlan-connectors-{self._NAME}", + } + self._credentials_body.update(creds) + return self + + def include(self, assets: dict) -> BigQueryCrawler: + """ + Defines the filter for assets to include when crawling. + + :param assets: Map keyed by project name + with each value being a list of tables + :returns: crawler, set to include only those assets specified + :raises InvalidRequestException: In the unlikely + event the provided filter cannot be translated + """ + include_assets = assets or {} + to_include = self.build_hierarchical_filter(include_assets) + self._parameters.append(dict(name="include-filter", value=to_include or "{}")) + return self + + def exclude(self, assets: dict) -> BigQueryCrawler: + """ + Defines the filter for assets to exclude when crawling. + + :param assets: Map keyed by project name + with each value being a list of tables + :returns: crawler, set to exclude only those assets specified + :raises InvalidRequestException: In the unlikely + event the provided filter cannot be translated + """ + exclude_assets = assets or {} + to_exclude = self.build_hierarchical_filter(exclude_assets) + self._parameters.append(dict(name="exclude-filter", value=to_exclude or "{}")) + return self + + def exclude_regex(self, regex: str) -> BigQueryCrawler: + """ + Defines the exclude regex for crawler ignore + tables and views based on a naming convention. + + :param regex: exclude regex for the crawler + :returns: crawler, set to exclude + only those assets specified in the regex + """ + self._parameters.append(dict(name="temp-table-regex", value=regex)) + return self + + def custom_config(self, config: Dict) -> BigQueryCrawler: + """ + Defines custom JSON configuration controlling + experimental feature flags for the crawler. + + :param config: custom configuration dict eg: + `{"ignore-all-case": True}` to enable crawling + assets with case-sensitive identifiers. + :returns: miner, set to include custom configuration + """ + config and self._parameters.append( + dict(name="control-config", value=str(config)) + ) + self._advanced_config = True + return self + + def _set_required_metadata_params(self): + self._parameters.append( + {"name": "credentials-fetch-strategy", "value": "credential_guid"} + ) + self._parameters.append( + {"name": "credential-guid", "value": "{{credentialGuid}}"} + ) + self._parameters.append( + dict( + name="control-config-strategy", + value="custom" if self._advanced_config else "default", + ) + ) + self._parameters.append( + { + "name": "connection", + "value": self._get_connection().to_json(), + } + ) + self._parameters.append(dict(name="publish-mode", value="production")) + self._parameters.append(dict(name="atlas-auth-type", value="internal")) + + def _get_metadata(self) -> WorkflowMetadata: + self._set_required_metadata_params() + return WorkflowMetadata( + labels={ + "orchestration.atlan.com/certified": "true", + "orchestration.atlan.com/source": self._NAME, + "orchestration.atlan.com/sourceCategory": "warehouse", + "orchestration.atlan.com/type": "connector", + "orchestration.atlan.com/verified": "true", + "package.argoproj.io/installer": "argopm", + "package.argoproj.io/name": f"a-t-ratlans-l-a-s-h{self._NAME}", + "package.argoproj.io/registry": "httpsc-o-l-o-ns-l-a-s-hs-l-a-s-hpackages.atlan.com", + f"orchestration.atlan.com/default-{self._NAME}-{self._epoch}": "true", + "orchestration.atlan.com/atlan-ui": "true", + }, + annotations={ + "orchestration.atlan.com/allowSchedule": "true", + "orchestration.atlan.com/categories": "warehouse,crawler", + "orchestration.atlan.com/dependentPackage": "", + "orchestration.atlan.com/docsUrl": "https://ask.atlan.com/hc/en-us/articles/6326782856081", + "orchestration.atlan.com/emoji": "\U0001f680", + "orchestration.atlan.com/icon": self._PACKAGE_ICON, + "orchestration.atlan.com/logo": self._PACKAGE_LOGO, + "orchestration.atlan.com/marketplaceLink": f"https://packages.atlan.com/-/web/detail/{self._PACKAGE_NAME}", # noqa + "orchestration.atlan.com/name": "BigQuery Assets", + "package.argoproj.io/author": "Atlan", + "package.argoproj.io/description": "Package to crawl BigQuery assets and publish to Atlan for discovery", # noqa + "package.argoproj.io/homepage": f"https://packages.atlan.com/-/web/detail/{self._PACKAGE_NAME}", + "package.argoproj.io/keywords": '["bigquery","connector","crawler","google"]', # fmt: skip + "package.argoproj.io/name": self._PACKAGE_NAME, + "package.argoproj.io/registry": "https://packages.atlan.com", + "package.argoproj.io/repository": "https://github.com/atlanhq/marketplace-packages.git", + "package.argoproj.io/support": "support@atlan.com", + "orchestration.atlan.com/atlanName": f"{self._PACKAGE_PREFIX}-default-{self._NAME}-{self._epoch}", + }, + name=f"{self._PACKAGE_PREFIX}-{self._epoch}", + namespace="default", + ) diff --git a/pyatlan_v9/model/packages/confluent_kafka_crawler.py b/pyatlan_v9/model/packages/confluent_kafka_crawler.py new file mode 100644 index 000000000..6c38e5b27 --- /dev/null +++ b/pyatlan_v9/model/packages/confluent_kafka_crawler.py @@ -0,0 +1,194 @@ +from __future__ import annotations + +from typing import List, Optional + +from pyatlan.model.enums import AtlanConnectorType, WorkflowPackage +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.model.packages.base.crawler import AbstractCrawler +from pyatlan_v9.model.workflow import WorkflowMetadata + + +class ConfluentKafkaCrawler(AbstractCrawler): + """ + Base configuration for a new Confluent Kafka crawler. + + :param client: connectivity to an Atlan tenant + :param connection_name: name for the connection + :param admin_roles: admin roles for the connection + :param admin_groups: admin groups for the connection + :param admin_users: admin users for the connection + :param allow_query: allow data to be queried in the + connection (True) or not (False), default: False + :param allow_query_preview: allow sample data viewing for + assets in the connection (True) or not (False), default: False + :param row_limit: maximum number of rows + that can be returned by a query, default: 0 + """ + + _NAME = "confluent-kafka" + _PACKAGE_NAME = "@atlan/kafka-confluent-cloud" + _PACKAGE_PREFIX = WorkflowPackage.KAFKA_CONFLUENT_CLOUD.value + _CONNECTOR_TYPE = AtlanConnectorType.CONFLUENT_KAFKA + _PACKAGE_ICON = "https://cdn.confluent.io/wp-content/uploads/apache-kafka-icon-2021-e1638496305992.jpg" + _PACKAGE_LOGO = "https://cdn.confluent.io/wp-content/uploads/apache-kafka-icon-2021-e1638496305992.jpg" + + def __init__( + self, + client: AtlanClient, + connection_name: str, + admin_roles: Optional[List[str]] = None, + admin_groups: Optional[List[str]] = None, + admin_users: Optional[List[str]] = None, + allow_query: bool = False, + allow_query_preview: bool = False, + row_limit: int = 0, + ): + super().__init__( + client=client, + connection_name=connection_name, + connection_type=self._CONNECTOR_TYPE, + admin_roles=admin_roles, + admin_groups=admin_groups, + admin_users=admin_users, + allow_query=allow_query, + allow_query_preview=allow_query_preview, + row_limit=row_limit, + source_logo=self._PACKAGE_LOGO, + ) + + def direct(self, bootstrap: str, encrypted: bool = True) -> ConfluentKafkaCrawler: + """ + Set up the crawler to extract directly from Kafka. + + :param bootstrap: hostname and port number (host.example.com:9092) for the Kafka bootstrap server + :param encrypted: whether to use an encrypted SSL connection (True), or plaintext (False), default: True + :returns: crawler, set up to extract directly from Kafka + """ + local_creds = { + "name": f"default-{self._NAME}-{self._epoch}-0", + "host": bootstrap, + "port": 9092, + "extra": { + "security_protocol": "SASL_SSL" if encrypted else "SASL_PLAINTEXT" + }, + "connector_config_name": "atlan-connectors-kafka-confluent-cloud", + } + self._credentials_body.update(local_creds) + self._parameters.append(dict(name="extraction-method", value="direct")) + return self + + def api_token( + self, + api_key: str, + api_secret: str, + ) -> ConfluentKafkaCrawler: + """ + Set up the crawler to use API token-based authentication. + + :param api_key: through which to access Kafka + :param api_secret: through which to access Kafka + :returns: crawler, set up to use API token-based authentication + """ + local_creds = { + "auth_type": "basic", + "username": api_key, + "password": api_secret, + } + self._credentials_body.update(local_creds) + return self + + def include(self, regex: str = "") -> ConfluentKafkaCrawler: + """ + Defines the filter for topics to include when crawling. + + :param regex: any topic names that match this + regular expression will be included in crawling + :returns: crawler, set to include only those topics specified + """ + if not regex: + return self + self._parameters.append(dict(name="include-filter", value=regex)) + return self + + def exclude(self, regex: str = "") -> ConfluentKafkaCrawler: + """ + Defines a regular expression to use for excluding topics when crawling. + + :param regex: any topic names that match this + regular expression will be excluded from crawling + :returns: crawler, set to exclude any topics + that match the provided regular expression + """ + if not regex: + return self + self._parameters.append(dict(name="exclude-filter", value=regex)) + return self + + def skip_internal(self, enabled: bool = True) -> ConfluentKafkaCrawler: + """ + Whether to skip internal topics when crawling (True) or include them. + + :param enabled: if True, internal topics + will be skipped when crawling, default: True + :returns: crawler, set to include or exclude internal topics + """ + self._parameters.append( + { + "name": "skip-internal-topics", + "value": "true" if enabled else "false", + } + ) + return self + + def _set_required_metadata_params(self): + self._parameters.append( + {"name": "credential-guid", "value": "{{credentialGuid}}"} + ) + self._parameters.append( + { + "name": "connection", + "value": self._get_connection().to_json(), + } + ) + self._parameters.append(dict(name="publish-mode", value="production")) + self._parameters.append(dict(name="atlas-auth-type", value="internal")) + + def _get_metadata(self) -> WorkflowMetadata: + self._set_required_metadata_params() + return WorkflowMetadata( + labels={ + "orchestration.atlan.com/certified": "true", + "orchestration.atlan.com/source": self._NAME, + "orchestration.atlan.com/sourceCategory": "eventbus", + "orchestration.atlan.com/type": "connector", + "orchestration.atlan.com/verified": "true", + "package.argoproj.io/installer": "argopm", + "package.argoproj.io/name": "a-t-ratlans-l-a-s-hkafka-confluent-cloud", + "package.argoproj.io/registry": "httpsc-o-l-o-ns-l-a-s-hs-l-a-s-hpackages.atlan.com", + f"orchestration.atlan.com/default-{self._NAME}-{self._epoch}": "true", + "orchestration.atlan.com/atlan-ui": "true", + }, + annotations={ + "orchestration.atlan.com/allowSchedule": "true", + "orchestration.atlan.com/dependentPackage": "", + "orchestration.atlan.com/docsUrl": "https://ask.atlan.com/hc/en-us/articles/6778924963599", + "orchestration.atlan.com/emoji": "\U0001f680", + "orchestration.atlan.com/icon": self._PACKAGE_ICON, + "orchestration.atlan.com/logo": self._PACKAGE_LOGO, # noqa + "orchestration.atlan.com/marketplaceLink": f"https://packages.atlan.com/-/web/detail/{self._PACKAGE_NAME}", # noqa + "orchestration.atlan.com/name": "Confluent Kafka Assets", + "orchestration.atlan.com/usecase": "crawling,discovery", + "package.argoproj.io/author": "Atlan", + "package.argoproj.io/description": "Package to crawl Confluent Kafka assets and publish to Atlan for discovery.", # noqa + "package.argoproj.io/homepage": f"https://packages.atlan.com/-/web/detail/{self._PACKAGE_NAME}", + "package.argoproj.io/keywords": '["kafka-confluent-cloud","confluent-kafka","eventbus","connector","kafka"]', # fmt: skip # noqa + "package.argoproj.io/name": self._PACKAGE_NAME, + "package.argoproj.io/parent": ".", + "package.argoproj.io/registry": "https://packages.atlan.com", + "package.argoproj.io/repository": "git+https://github.com/atlanhq/marketplace-packages.git", + "package.argoproj.io/support": "support@atlan.com", + "orchestration.atlan.com/atlanName": f"{self._PACKAGE_PREFIX}-default-{self._NAME}-{self._epoch}", + }, + name=f"{self._PACKAGE_PREFIX}-{self._epoch}", + namespace="default", + ) diff --git a/pyatlan_v9/model/packages/connection_delete.py b/pyatlan_v9/model/packages/connection_delete.py new file mode 100644 index 000000000..af79b8266 --- /dev/null +++ b/pyatlan_v9/model/packages/connection_delete.py @@ -0,0 +1,68 @@ +from __future__ import annotations + +from pyatlan.model.enums import WorkflowPackage +from pyatlan_v9.model.packages.base.miner import AbstractMiner +from pyatlan_v9.model.workflow import WorkflowMetadata + + +class ConnectionDelete(AbstractMiner): + """ + Base configuration for a new connection delete workflow. + + :param qualified_name: unique name of the + connection whose assets should be deleted + :param purge: if `True`, permanently delete the connection + and its assets, otherwise only archive (soft-delete) them `False` + """ + + _NAME = "connection-delete" + _PACKAGE_NAME = "@atlan/connection-delete" + _PACKAGE_PREFIX = WorkflowPackage.CONNECTION_DELETE.value + _PACKAGE_ICON = "https://assets.atlan.com/assets/connection-delete.svg" + _PACKAGE_LOGO = "https://assets.atlan.com/assets/connection-delete.svg" + + def __init__( + self, + qualified_name: str, + purge: bool, + ): + super().__init__(connection_qualified_name=qualified_name) + self._parameters.append(dict(name="delete-assets", value="true")) + self._parameters.append( + dict(name="delete-type", value="PURGE" if purge else "SOFT") + ) + + def _get_metadata(self) -> WorkflowMetadata: + return WorkflowMetadata( + labels={ + "orchestration.atlan.com/certified": "true", + "orchestration.atlan.com/type": "utility", + "orchestration.atlan.com/verified": "true", + "package.argoproj.io/installer": "argopm", + "package.argoproj.io/name": f"a-t-ratlans-l-a-s-h{self._NAME}", + "package.argoproj.io/registry": "httpsc-o-l-o-ns-l-a-s-hs-l-a-s-hpackages.atlan.com", + "orchestration.atlan.com/atlan-ui": "true", + }, + annotations={ + "orchestration.atlan.com/allowSchedule": "false", + "orchestration.atlan.com/categories": "utility,admin,connection,delete", + "orchestration.atlan.com/dependentPackage": "", + "orchestration.atlan.com/docsUrl": "https://ask.atlan.com/hc/en-us/articles/6755306791697", + "orchestration.atlan.com/emoji": "🗑️", + "orchestration.atlan.com/icon": self._PACKAGE_ICON, + "orchestration.atlan.com/logo": self._PACKAGE_LOGO, + "orchestration.atlan.com/marketplaceLink": f"https://packages.atlan.com/-/web/detail/{self._PACKAGE_NAME}", # noqa + "orchestration.atlan.com/name": "Connection Delete", + "package.argoproj.io/author": "Atlan", + "package.argoproj.io/description": "Deletes a connection and all its related assets", + "package.argoproj.io/homepage": f"https://packages.atlan.com/-/web/detail/{self._PACKAGE_NAME}", + "package.argoproj.io/keywords": '["delete","admin","utility"]', + "package.argoproj.io/name": self._PACKAGE_NAME, + "package.argoproj.io/registry": "https://packages.atlan.com", + "package.argoproj.io/repository": "git+https://github.com/atlanhq/marketplace-packages.git", + "package.argoproj.io/support": "support@atlan.com", + "orchestration.atlan.com/atlanName": f"{self._PACKAGE_PREFIX}-{self._epoch}", + }, + name=f"{self._PACKAGE_PREFIX}-{self._epoch}", + namespace="default", + ) diff --git a/pyatlan_v9/model/packages/databricks_crawler.py b/pyatlan_v9/model/packages/databricks_crawler.py new file mode 100644 index 000000000..56dff16e8 --- /dev/null +++ b/pyatlan_v9/model/packages/databricks_crawler.py @@ -0,0 +1,530 @@ +from __future__ import annotations + +from enum import Enum +from typing import Any, List, Optional +from warnings import warn + +import msgspec + +from pyatlan.model.enums import AtlanConnectorType, WorkflowPackage +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.model.packages.base.crawler import AbstractCrawler +from pyatlan_v9.model.workflow import WorkflowMetadata + + +class DatabricksCrawler(AbstractCrawler): + """ + Base configuration for a new Databricks crawler. + + :param client: connectivity to an Atlan tenant + :param connection_name: name for the connection + :param admin_roles: admin roles for the connection + :param admin_groups: admin groups for the connection + :param admin_users: admin users for the connection + :param allow_query: allow data to be queried in the + connection (True) or not (False), default: True + :param allow_query_preview: allow sample data viewing for + assets in the connection (True) or not (False), default: True + :param row_limit: maximum number of rows + that can be returned by a query, default: 10000 + """ + + _NAME = "databricks" + _PACKAGE_NAME = "@atlan/databricks" + _PACKAGE_PREFIX = WorkflowPackage.DATABRICKS.value + _CONNECTOR_TYPE = AtlanConnectorType.DATABRICKS + _PACKAGE_ICON = "https://assets.atlan.com/assets/databricks.svg" + _PACKAGE_LOGO = "https://assets.atlan.com/assets/databricks.svg" + + class ExtractionMethod(str, Enum): + JDBC = "jdbc" + REST = "rest" + SYSTEM_TABLES = "system-tables" + + class RegexAssetTypes(str, Enum): + DATABASES = "database" + SCHEMAS = "schema" + TABLE_VIEWS = "table" + + class AssetsSelectionCriteria(str, Enum): + INCLUDE_BY_HIERARCHY = "include-filter-system-tables" + EXCLUDE_BY_HIERARCHY = "exclude-filter-system-tables" + INCLUDE_BY_REGEX = "include-regex-system-tables" + EXCLUDE_BY_REGEX = "exclude-regex-system-tables" + + class AssetsSelection(msgspec.Struct, kw_only=True): + type: DatabricksCrawler.AssetsSelectionCriteria + values: Any + + def __init__( + self, + client: AtlanClient, + connection_name: str, + admin_roles: Optional[List[str]] = None, + admin_groups: Optional[List[str]] = None, + admin_users: Optional[List[str]] = None, + allow_query: bool = True, + allow_query_preview: bool = True, + row_limit: int = 10000, + ): + self._advanced_config = False + super().__init__( + client=client, + connection_name=connection_name, + connection_type=self._CONNECTOR_TYPE, + admin_roles=admin_roles, + admin_groups=admin_groups, + admin_users=admin_users, + allow_query=allow_query, + allow_query_preview=allow_query_preview, + row_limit=row_limit, + source_logo=self._PACKAGE_LOGO, + ) + + def direct(self, hostname: str, port: int = 443) -> DatabricksCrawler: + """ + Set up the crawler to extract directly from the Databricks. + + :param hostname: hostname of the Databricks instance + :param port: port number of the Databricks instance. default: `443` + :returns: crawler, set up to extract directly from the Databricks + """ + local_creds = { + "name": f"default-{self._NAME}-{self._epoch}-0", + "host": hostname, + "port": port, + "connector_config_name": f"atlan-connectors-{self._NAME}", + } + self._credentials_body.update(local_creds) + self._parameters.append(dict(name="extraction-method", value="direct")) + return self + + def s3( + self, + bucket_name: str, + bucket_prefix: str, + bucket_region: Optional[str] = None, + ) -> DatabricksCrawler: + """ + Set up the crawler to extract from S3 bucket. + + :param bucket_name: name of the bucket/storage + that contains the extracted metadata files + :param bucket_prefix: prefix is everything after the + bucket/storage name, including the `path` + :param bucket_region: (Optional) name of the region if applicable + :returns: crawler, set up to extract from S3 bucket + """ + self._parameters.append(dict(name="extraction-method", value="s3")) + self._parameters.append( + dict(name="offline-extraction-bucket", value=bucket_name) + ) + self._parameters.append( + dict(name="offline-extraction-prefix", value=bucket_prefix) + ) + self._parameters.append( + dict(name="offline-extraction-region", value=bucket_region) + ) + return self + + def basic_auth( + self, personal_access_token: str, http_path: str + ) -> DatabricksCrawler: + """ + (DEPRECATED) Set up the crawler to use basic authentication. + + :param personal_access_token: through which to access Databricks instance + :param http_path: HTTP path of your Databricks instance + :returns: crawler, set up to use basic authentication + """ + warn( + "This method is deprecated, please use 'pat()' instead, which offers identical functionality.", + DeprecationWarning, + stacklevel=2, + ) + local_creds = { + "authType": "basic", + "username": "", + "password": personal_access_token, + "connector_type": "dual", + "extra": { + "__http_path": http_path, + }, + } + self._credentials_body.update(local_creds) + return self + + def pat(self, access_token: str, sql_warehouse_id: str) -> DatabricksCrawler: + """ + Set up the crawler to use PAT authentication. + + :param access_token: through which to access Databricks instance + :param sql_warehouse_id: ID of the associated SQL warehouse + if this data source is backed by a SQL warehouse. eg: `3d939b0cc668be06` + ref: https://docs.databricks.com/api/workspace/datasources/list#warehouse_id + :returns: crawler, set up to use PAT + """ + local_creds = { + "authType": "basic", + "username": "", + "password": access_token, + "connector_type": "dual", + "extra": { + "__http_path": f"/sql/1.0/warehouses/{sql_warehouse_id}", + }, + } + self._credentials_body.update(local_creds) + return self + + def aws_service(self, client_id: str, client_secret: str) -> DatabricksCrawler: + """ + Set up the crawler to use AWS service principal. + + :param client_id: client ID for your AWS service principal + :param client_secret: client secret for your AWS service principal + :returns: crawler, set up to use AWS service principal + """ + local_creds = { + "authType": "aws_service", + "username": "", + "connector_type": "rest", + "extra": {"client_id": client_id, "client_secret": client_secret}, + } + self._credentials_body.update(local_creds) + return self + + def azure_service( + self, client_id: str, client_secret: str, tenant_id: str + ) -> DatabricksCrawler: + """ + Set up the crawler to use Azure service principal. + + :param client_id: client ID for Azure service principal + :param client_secret: client secret for your Azure service principal + :param tenant_id: tenant ID (directory ID) for Azure service principal + :returns: crawler, set up to use Azure service principal + """ + local_creds = { + "authType": "azure_service", + "username": "", + "connector_type": "rest", + "extra": { + "client_id": client_id, + "client_secret": client_secret, + "tenant_id": tenant_id, + }, + } + self._credentials_body.update(local_creds) + return self + + def metadata_extraction_method( + self, + type: DatabricksCrawler.ExtractionMethod = ExtractionMethod.JDBC, + ) -> DatabricksCrawler: + """ + Determines the interface that the package + will use to extract metadata from Databricks. + JDBC is the recommended method (`default`). + REST API method is supported only + by Unity Catalog enabled instances. + + :param type: extraction method to use. + Defaults to `DatabricksCrawler.ExtractionMethod.JDBC` + """ + self._parameters.append({"name": "extract-strategy", "value": type.value}) + return self + + def enable_cross_workspace_discovery( + self, include: bool = False + ) -> DatabricksCrawler: + """ + Whether to enable cross-workspace discovery to discover assets from other workspaces. + + :param include: if True, cross-workspace discovery will be included while crawling Databricks, default: False + :returns: crawler, set to include or exclude cross-workspace discovery + """ + self._parameters.append( + { + "name": "enable-cross-workspace-discovery", + "value": "true" if include else "false", + } + ) + return self + + def enable_incremental_extraction(self, include: bool = False) -> DatabricksCrawler: + """ + Whether to enable or disable schema incremental extraction on source. + + :param include: if True, incremental extraction will be included while crawling Databricks, default: False + :returns: crawler, set to include or exclude incremental extraction + """ + self._parameters.append( + {"name": "incremental-extraction", "value": "true" if include else "false"} + ) + return self + + def enable_view_lineage(self, include: bool = True) -> DatabricksCrawler: + """ + Whether to enable view lineage as part of crawling Databricks. + + :param include: if True, view lineage will be included while crawling Databricks, default: True + :returns: crawler, set to include or exclude view lineage + """ + self._parameters.append({"name": "enable-view-lineage", "value": include}) + return self + + def enable_source_level_filtering(self, include: bool = False) -> DatabricksCrawler: + """ + Whether to enable or disable schema level filtering on source. + schemas selected in the include filter will be fetched. + + :param include: if True, schemas selected in the include + filter will be fetched while crawling Databricks, default: False + :returns: crawler, set to include or exclude source level filtering + """ + self._parameters.append( + { + "name": "use-source-schema-filtering", + "value": "true" if include else "false", + } + ) + self._advanced_config = True + return self + + def include(self, assets: dict) -> DatabricksCrawler: + """ + Defines the filter for assets to include when crawling. + + :param assets: Map keyed by database name with each value being a list of schemas + :returns: crawler, set to include only those assets specified + :raises InvalidRequestException: In the unlikely + event the provided filter cannot be translated + """ + include_assets = assets or {} + to_include = self.build_hierarchical_filter(include_assets) + self._parameters.append( + dict(dict(name="include-filter", value=to_include or "{}")) + ) + return self + + def exclude(self, assets: dict) -> DatabricksCrawler: + """ + Defines the filter for assets to exclude when crawling. + + :param assets: Map keyed by database name with each value being a list of schemas + :returns: crawler, set to exclude only those assets specified + :raises InvalidRequestException: In the unlikely + event the provided filter cannot be translated + """ + exclude_assets = assets or {} + to_exclude = self.build_hierarchical_filter(exclude_assets) + self._parameters.append(dict(name="exclude-filter", value=to_exclude or "{}")) + return self + + def include_for_rest_api(self, assets: List[str]) -> DatabricksCrawler: + """ + Defines the filter for assets to include when crawling + (When using REST API extraction method). + + :param assets: list of databases names to include when crawling + :returns: crawler, set to include only those assets specified + :raises InvalidRequestException: In the unlikely + event the provided filter cannot be translated + """ + include_assets = assets or [] + to_include = self.build_flat_hierarchical_filter(include_assets) + self._parameters.append( + dict(dict(name="include-filter-rest", value=to_include or "{}")) + ) + return self + + def exclude_for_rest_api(self, assets: List[str]) -> DatabricksCrawler: + """ + Defines the filter for assets to exclude when crawling. + (When using REST API extraction method). + + :param assets: list of databases names to exclude when crawling + :returns: crawler, set to exclude only those assets specified + :raises InvalidRequestException: In the unlikely + event the provided filter cannot be translated + """ + exclude_assets = assets or [] + to_exclude = self.build_flat_hierarchical_filter(exclude_assets) + self._parameters.append( + dict(name="exclude-filter-rest", value=to_exclude or "{}") + ) + return self + + def asset_selection_for_system_tables( + self, selection_criteria: List[DatabricksCrawler.AssetsSelection] + ) -> DatabricksCrawler: + """ + Defines the filter for system table assets to include or exclude when crawling. + + This method allows you to configure asset selection specifically for Databricks + system tables using various selection criteria including hierarchical filtering + and regex-based filtering. + + :param selection_criteria: List of selection criteria objects containing + the type of selection (include/exclude) and the corresponding values + for filtering system table assets + :returns: crawler, configured with system table asset selection filters + """ + for criteria in selection_criteria: + if ( + criteria.type + == DatabricksCrawler.AssetsSelectionCriteria.INCLUDE_BY_HIERARCHY + ): + include_assets = criteria.values or {} + to_include = self.build_selective_hierarchical_filter(include_assets) + self._parameters.append( + dict(name=criteria.type.value, value=to_include or "{}") + ) + + elif ( + criteria.type + == DatabricksCrawler.AssetsSelectionCriteria.EXCLUDE_BY_HIERARCHY + ): + exclude_assets = criteria.values or {} + to_exclude = self.build_selective_hierarchical_filter(exclude_assets) + self._parameters.append( + dict(name=criteria.type.value, value=to_exclude or "{}") + ) + + elif ( + criteria.type + == DatabricksCrawler.AssetsSelectionCriteria.INCLUDE_BY_REGEX + ): + include_regex = criteria.values + asset_type = include_regex.get("asset_type") + if asset_type not in DatabricksCrawler.RegexAssetTypes: + raise ValueError( + f"Invalid asset_type: {asset_type}. Must be one of {[e.value for e in DatabricksCrawler.RegexAssetTypes]}" + ) + self._parameters.append( + dict( + name=f"include-{asset_type.value}-regex", + value=include_regex.get("regex", ""), + ) + ) + + elif ( + criteria.type + == DatabricksCrawler.AssetsSelectionCriteria.EXCLUDE_BY_REGEX + ): + exclude_regex = criteria.values + asset_type = exclude_regex.get("asset_type") + if asset_type not in DatabricksCrawler.RegexAssetTypes: + raise ValueError( + f"Invalid asset_type: {asset_type}. Must be one of {[e.value for e in DatabricksCrawler.RegexAssetTypes]}" + ) + self._parameters.append( + dict( + # NOTE: temp-table-regex-system-tables is the name + # of the parameter for exclude regex for system tables (TABLE_VIEWS) + name="temp-table-regex-system-tables" + if asset_type == DatabricksCrawler.RegexAssetTypes.TABLE_VIEWS + else f"exclude-{asset_type.value}-regex", + value=exclude_regex.get("regex", ""), + ) + ) + return self + + def sql_warehouse(self, warehouse_ids: List[str]) -> DatabricksCrawler: + """ + Defines the filter for SQL warehouses to include when crawling. + (When using REST API extraction method). + + :param assets: list of `warehose_id` to include when crawling eg: [`3d939b0cc668be06`, `9a289b0cc838ce62`] + ref: https://docs.databricks.com/api/workspace/datasources/list#warehouse_id + :returns: crawler, set to include only those assets specified + :raises InvalidRequestException: In the unlikely + event the provided filter cannot be translated + """ + warehouse_ids = warehouse_ids or [] + to_include = self.build_flat_hierarchical_filter(warehouse_ids) + self._parameters.append(dict(name="sql-warehouse", value=to_include or "{}")) + return self + + def import_tags(self, include: bool = False) -> DatabricksCrawler: + """ + Whether to import tags from Databricks Unity Catalog to Atlan. + Tags attached in Databricks will be automatically attached to your Databricks assets in Atlan. + (When using REST API extraction method). + + :param include: if True, tags will be imported from Databricks Unity Catalog to Atlan, default: False + :returns: crawler, set to whether to import tags from Databricks Unity Catalog to Atlan + """ + self._parameters.append({"name": "enable-tag-sync", "value": include}) + return self + + def exclude_regex(self, regex: str) -> DatabricksCrawler: + """ + Defines the exclude regex for crawler + ignore tables & views based on a naming convention. + + :param regex: exclude regex for the crawler + :returns: crawler, set to exclude + only those assets specified in the regex + """ + self._parameters.append(dict(name="temp-table-regex", value=regex)) + return self + + def _set_required_metadata_params(self): + self._parameters.append( + {"name": "credentials-fetch-strategy", "value": "credential_guid"} + ) + self._parameters.append( + {"name": "credential-guid", "value": "{{credentialGuid}}"} + ) + self._parameters.append( + dict( + name="advanced-config-strategy", + value="custom" if self._advanced_config else "default", + ) + ) + self._parameters.append( + { + "name": "connection", + "value": self._get_connection().to_json(), + } + ) + + def _get_metadata(self) -> WorkflowMetadata: + self._set_required_metadata_params() + return WorkflowMetadata( + labels={ + "orchestration.atlan.com/certified": "true", + "orchestration.atlan.com/source": self._NAME, + "orchestration.atlan.com/sourceCategory": "lake", + "orchestration.atlan.com/type": "connector", + "orchestration.atlan.com/verified": "true", + "package.argoproj.io/installer": "argopm", + "package.argoproj.io/name": f"a-t-ratlans-l-a-s-h{self._NAME}", + "package.argoproj.io/registry": "httpsc-o-l-o-ns-l-a-s-hs-l-a-s-hpackages.atlan.com", + f"orchestration.atlan.com/default-{self._NAME}-{self._epoch}": "true", + "orchestration.atlan.com/atlan-ui": "true", + }, + annotations={ + "orchestration.atlan.com/allowSchedule": "true", + "orchestration.atlan.com/categories": "lake,crawler", + "orchestration.atlan.com/dependentPackage": "", + "orchestration.atlan.com/docsUrl": "https://ask.atlan.com/hc/en-us/articles/6328311007377", + "orchestration.atlan.com/emoji": "\U0001f680", + "orchestration.atlan.com/icon": self._PACKAGE_ICON, + "orchestration.atlan.com/logo": self._PACKAGE_LOGO, + "orchestration.atlan.com/marketplaceLink": f"https://packages.atlan.com/-/web/detail/{self._PACKAGE_NAME}", # noqa + "orchestration.atlan.com/name": "Databricks Assets", + "package.argoproj.io/author": "Atlan", + "package.argoproj.io/description": f"Package to crawl databricks assets and publish to Atlan for discovery", # noqa + "package.argoproj.io/homepage": f"https://packages.atlan.com/-/web/detail/{self._PACKAGE_NAME}", + "package.argoproj.io/keywords": '["databricks","lake","connector","crawler"]', # fmt: skip # noqa + "package.argoproj.io/name": self._PACKAGE_NAME, + "package.argoproj.io/registry": "https://packages.atlan.com", + "package.argoproj.io/repository": "git+https://github.com/atlanhq/marketplace-packages.git", + "package.argoproj.io/support": "support@atlan.com", + "orchestration.atlan.com/atlanName": f"{self._PACKAGE_PREFIX}-default-{self._NAME}-{self._epoch}", + }, + name=f"{self._PACKAGE_PREFIX}-{self._epoch}", + namespace="default", + ) diff --git a/pyatlan_v9/model/packages/databricks_miner.py b/pyatlan_v9/model/packages/databricks_miner.py new file mode 100644 index 000000000..34f351b8d --- /dev/null +++ b/pyatlan_v9/model/packages/databricks_miner.py @@ -0,0 +1,171 @@ +from __future__ import annotations + +from enum import Enum +from json import dumps +from typing import List, Optional + +from pyatlan.model.enums import WorkflowPackage +from pyatlan_v9.model.packages.base.miner import AbstractMiner +from pyatlan_v9.model.workflow import WorkflowMetadata + + +class DatabricksMiner(AbstractMiner): + """ + Base configuration for a new Databricks miner. + + :param connection_qualified_name: unique name of the + Databricks connection whose assets should be mined + """ + + _NAME = "databricks" + _PACKAGE_NAME = "@atlan/databricks-lineage" + _PACKAGE_PREFIX = WorkflowPackage.DATABRICKS_LINEAGE.value + _PACKAGE_ICON = "https://assets.atlan.com/assets/databricks.svg" + _PACKAGE_LOGO = "https://assets.atlan.com/assets/databricks.svg" + + class ExtractionMethod(str, Enum): + REST_API = "rest-api" + SYSTEM_TABLE = "system-table" + + def __init__( + self, + connection_qualified_name: str, + ): + self._advanced_config = False + super().__init__(connection_qualified_name=connection_qualified_name) + self._parameters.append(dict(name="calculate-popularity", value=False)) + self._parameters.append(dict(name="popularity-window-days", value=30)) + self._parameters.append(dict(name="miner-start-time-epoch", value=0)) + self._parameters.append( + dict( + name="extraction-method-popularity", + value=self.ExtractionMethod.REST_API.value, + ) + ) + + def rest_api(self): + """ + Sets up the Databricks miner to use the REST API method for fetching lineage. + + :returns: miner, configured to use the REST API extraction method from Databricks. + """ + self._parameters.append( + dict(name="extraction-method", value=self.ExtractionMethod.REST_API.value) + ) + return self + + def offline(self, bucket_name: str, bucket_prefix: str): + """ + Sets up the Databricks miner to use the offline extraction method. + + This method sets up the miner to extract data from an S3 bucket by specifying + the bucket name and prefix. + + :param bucket_name: name of the S3 bucket to extract data from. + :param bucket_prefix: prefix within the S3 bucket to narrow the extraction scope. + :returns: miner, configured for offline extraction. + """ + self._parameters.append(dict(name="extraction-method", value="offline")) + self._parameters.append( + dict(name="offline-extraction-bucket", value=bucket_name) + ) + self._parameters.append( + dict(name="offline-extraction-prefix", value=bucket_prefix) + ) + return self + + def system_table(self, warehouse_id: str): + """ + Sets up the Databricks miner to use the system table extraction method. + + This method sets up the miner to extract data + using a specific SQL warehouse by providing its unique ID. + + :param warehouse_id: unique identifier of the SQL + warehouse to be used for system table extraction. + :returns: miner, configured for system table extraction. + """ + self._parameters.append( + dict(name="extraction-method", value=self.ExtractionMethod.SYSTEM_TABLE) + ) + self._parameters.append(dict(name="sql-warehouse", value=warehouse_id)) + return self + + def popularity_configuration( + self, + start_date: str, + extraction_method: DatabricksMiner.ExtractionMethod = ExtractionMethod.REST_API, + window_days: Optional[int] = None, + excluded_users: Optional[List[str]] = None, + warehouse_id: Optional[str] = None, + ) -> DatabricksMiner: + """ + Configures the Databricks miner to calculate asset popularity metrics. + + This method sets up the miner to fetch query history and calculate + popularity metrics based on the specified configuration. + + :param start_date: epoch timestamp from which queries will be fetched + for calculating popularity. This does not affect lineage generation. + :param extraction_method: method used to fetch popularity data. + Defaults to `ExtractionMethod.REST_API`. + :param window_days: (Optional) number of days to consider for calculating popularity metrics. + :param excluded_users: (Optional) list of usernames to exclude from usage metrics calculations. + :param warehouse_id: (Optional) unique identifier of the SQL warehouse to use for + popularity calculations. Required if `extraction_method` is `ExtractionMethod.SYSTEM_TABLE`. + :returns: miner, configured with popularity settings. + """ + excluded_users = excluded_users or [] + config_map = { + "calculate-popularity": True, + "extraction-method-popularity": extraction_method.value, + "miner-start-time-epoch": start_date, + "popularity-window-days": window_days, + } + for param in self._parameters: + if param["name"] in config_map: + param["value"] = config_map[param["name"]] + self._parameters.append( + dict(name="popularity-exclude-user-config", value=dumps(excluded_users)) + ) + if extraction_method == self.ExtractionMethod.SYSTEM_TABLE: + self._parameters.append( + dict(name="sql-warehouse-popularity", value=warehouse_id) + ) + return self + + def _get_metadata(self) -> WorkflowMetadata: + return WorkflowMetadata( + labels={ + "orchestration.atlan.com/certified": "true", + "orchestration.atlan.com/source": self._NAME, + "orchestration.atlan.com/sourceCategory": "lake", + "orchestration.atlan.com/type": "miner", + "orchestration.atlan.com/verified": "true", + "package.argoproj.io/installer": "argopm", + "package.argoproj.io/name": f"a-t-ratlans-l-a-s-h{self._NAME}-miner", + "package.argoproj.io/registry": "httpsc-o-l-o-ns-l-a-s-hs-l-a-s-hpackages.atlan.com", + "orchestration.atlan.com/atlan-ui": "true", + }, + annotations={ + "orchestration.atlan.com/allowSchedule": "true", + "orchestration.atlan.com/categories": "lake,miner", + "orchestration.atlan.com/docsUrl": "https://ask.atlan.com/hc/en-us/articles/7034583224081", + "orchestration.atlan.com/emoji": "\ud83d\ude80", + "orchestration.atlan.com/icon": self._PACKAGE_ICON, + "orchestration.atlan.com/logo": self._PACKAGE_LOGO, + "orchestration.atlan.com/marketplaceLink": f"https://packages.atlan.com/-/web/detail/{self._PACKAGE_NAME}", # noqa + "orchestration.atlan.com/name": "Databricks Miner", + "package.argoproj.io/author": "Atlan", + "package.argoproj.io/description": "Package to extract lineage information and usage metrics from Databricks.", # noqa + "package.argoproj.io/homepage": f"https://packages.atlan.com/-/web/detail/{self._PACKAGE_NAME}", + "package.argoproj.io/keywords": '["databricks","lake","connector","miner"]', # fmt: skip + "package.argoproj.io/name": self._PACKAGE_NAME, + "package.argoproj.io/registry": "https://packages.atlan.com", + "package.argoproj.io/repository": "git+https://github.com/atlanhq/marketplace-packages.git", + "package.argoproj.io/support": "support@atlan.com", + "orchestration.atlan.com/atlanName": f"{self._PACKAGE_PREFIX}-{self._epoch}", + }, + name=f"{self._PACKAGE_PREFIX}-{self._epoch}", + namespace="default", + ) diff --git a/pyatlan_v9/model/packages/dbt_crawler.py b/pyatlan_v9/model/packages/dbt_crawler.py new file mode 100644 index 000000000..8e2c97eaf --- /dev/null +++ b/pyatlan_v9/model/packages/dbt_crawler.py @@ -0,0 +1,240 @@ +from __future__ import annotations + +from typing import List, Optional + +from pyatlan.model.enums import AtlanConnectorType, WorkflowPackage +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.model.packages.base.crawler import AbstractCrawler +from pyatlan_v9.model.workflow import WorkflowMetadata + + +class DbtCrawler(AbstractCrawler): + """ + Base configuration for a new Dbt crawler. + + :param client: connectivity to an Atlan tenant + :param connection_name: name for the connection + :param admin_roles: admin roles for the connection + :param admin_groups: admin groups for the connection + :param admin_users: admin users for the connection + :param allow_query: allow data to be queried in the + connection (True) or not (False), default: False + :param allow_query_preview: allow sample data viewing for + assets in the connection (True) or not (False), default: False + :param row_limit: maximum number of rows + that can be returned by a query, default: 0 + """ + + _NAME = "dbt" + _PACKAGE_NAME = "@atlan/dbt" + _PACKAGE_PREFIX = WorkflowPackage.DBT.value + _CONNECTOR_TYPE = AtlanConnectorType.DBT + _PACKAGE_ICON = "https://assets.atlan.com/assets/dbt-new.svg" + _PACKAGE_LOGO = "https://assets.atlan.com/assets/dbt-new.svg" + + def __init__( + self, + client: AtlanClient, + connection_name: str, + admin_roles: Optional[List[str]] = None, + admin_groups: Optional[List[str]] = None, + admin_users: Optional[List[str]] = None, + allow_query: bool = False, + allow_query_preview: bool = False, + row_limit: int = 0, + ): + super().__init__( + client=client, + connection_name=connection_name, + connection_type=self._CONNECTOR_TYPE, + admin_roles=admin_roles, + admin_groups=admin_groups, + admin_users=admin_users, + allow_query=allow_query, + allow_query_preview=allow_query_preview, + row_limit=row_limit, + source_logo=self._PACKAGE_LOGO, + ) + + def cloud( + self, + service_token: str, + hostname: str = "https://cloud.getdbt.com", + multi_tenant: bool = True, + ) -> DbtCrawler: + """ + Set up the crawler to extract using dbt Cloud. + + :param service_token: token to use to authenticate against dbt + :param hostname: of dbt, default: https://cloud.getdbt.com + :param multi_tenant: if True, use a multi-tenant + cloud config, otherwise a single-tenant cloud config + :returns: crawler, set up to extract using dbt Cloud + """ + local_creds = { + "name": f"default-{self._NAME}-{self._epoch}-1", + "host": hostname, + "port": 443, + "auth_type": "token", + "username": "", + "password": service_token, + "connector_config_name": f"atlan-connectors-{self._NAME}", + } + self._credentials_body.update(local_creds) + self._parameters.append(dict(name="extraction-method", value="api")) + self._parameters.append( + dict(name="deployment-type", value="multi" if multi_tenant else "single") + ) + self._parameters.append( + {"name": "api-credential-guid", "value": "{{credentialGuid}}"} + ) + self._parameters.append(dict(name="control-config-strategy", value="default")) + return self + + def core(self, s3_bucket: str, s3_prefix: str, s3_region: str) -> DbtCrawler: + """ + Set up the crawler to extract using dbt Core files in S3. + + :param s3_bucket: S3 bucket containing the dbt Core files + :param s3_prefix: prefix within the S3 bucket where the dbt Core files are located + :param s3_region: S3 region where the bucket is located + :returns: crawler, set up to extract using dbt Core files in S3 + """ + self._parameters.append(dict(name="extraction-method", value="core")) + self._parameters.append(dict(name="deployment-type", value="single")) + self._parameters.append(dict(name="core-extraction-s3-bucket", value=s3_bucket)) + self._parameters.append(dict(name="core-extraction-s3-prefix", value=s3_prefix)) + self._parameters.append(dict(name="core-extraction-s3-region", value=s3_region)) + return self + + def enrich_materialized_assets(self, enabled: bool = False) -> DbtCrawler: + """ + Whether to enable the enrichment of + materialized SQL assets as part of crawling dbt. + + :param enabled: if True, any assets that dbt materializes + will also be enriched with details from dbt, default: False + :returns: crawler, set up to include + or exclude enrichment of materialized assets + """ + self._parameters.append( + { + "name": "enrich-materialised-sql-assets", + "value": "true" if enabled else "false", + } + ) + return self + + def tags(self, include: bool = False) -> DbtCrawler: + """ + Whether to enable dbt tag syncing as part of crawling dbt. + + :param include: if True, tags in dbt will + be included while crawling dbt, default: False + :returns: crawler, set to include or exclude dbt tags + """ + self._parameters.append( + { + "name": "enable-dbt-tagsync", + "value": "true" if include else "false", + } + ) + return self + + def limit_to_connection(self, connection_qualified_name: str) -> DbtCrawler: + """ + Limit the crawling to a single connection's assets. + If not specified, crawling will be + attempted across all connection's assets. + + :param connection_qualified_name: unique name + of the connection for whose assets to limit crawling + :returns: crawler, set to limit crawling + to only those assets in the specified connection + """ + self._parameters.append( + { + "name": "connection-qualified-name", + "value": connection_qualified_name, + } + ) + return self + + def include(self, filter: str = "") -> DbtCrawler: + """ + Defines the filter for assets to include when crawling. + + :param filter: for dbt Core provide a wildcard + expression and for dbt Cloud provide a string-encoded map + :returns: crawler, set to include only those assets specified + """ + self._parameters.append( + dict(name="include-filter", value=filter if filter else "{}") + ) + self._parameters.append( + dict(name="include-filter-core", value=filter if filter else "*") + ) + return self + + def exclude(self, filter: str = "") -> DbtCrawler: + """ + Defines the filter for assets to exclude when crawling. + + :param filter: for dbt Core provide a wildcard + expression and for dbt Cloud provide a string-encoded map + :return: the builder, set to exclude only those assets specified + """ + self._parameters.append( + dict(name="exclude-filter", value=filter if filter else "{}") + ) + self._parameters.append( + dict(name="exclude-filter-core", value=filter if filter else "*") + ) + return self + + def _set_required_metadata_params(self): + self._parameters.append( + { + "name": "connection", + "value": self._get_connection().to_json(), + } + ) + + def _get_metadata(self) -> WorkflowMetadata: + self._set_required_metadata_params() + return WorkflowMetadata( + labels={ + "orchestration.atlan.com/certified": "true", + "orchestration.atlan.com/source": self._NAME, + "orchestration.atlan.com/sourceCategory": "elt", + "orchestration.atlan.com/type": "connector", + "orchestration.atlan.com/verified": "true", + "package.argoproj.io/installer": "argopm", + "package.argoproj.io/name": f"a-t-ratlans-l-a-s-h{self._NAME}", + "package.argoproj.io/registry": "httpsc-o-l-o-ns-l-a-s-hs-l-a-s-hpackages.atlan.com", + f"orchestration.atlan.com/default-{self._NAME}-{self._epoch}": "true", + "orchestration.atlan.com/atlan-ui": "true", + }, + annotations={ + "orchestration.atlan.com/allowSchedule": "true", + "orchestration.atlan.com/dependentPackage": "", + "orchestration.atlan.com/docsUrl": "https://ask.atlan.com/hc/en-us/articles/6335824578705", + "orchestration.atlan.com/emoji": "\U0001f680", + "orchestration.atlan.com/icon": self._PACKAGE_ICON, + "orchestration.atlan.com/logo": self._PACKAGE_LOGO, # noqa + "orchestration.atlan.com/marketplaceLink": f"https://packages.atlan.com/-/web/detail/{self._PACKAGE_NAME}", # noqa + "orchestration.atlan.com/name": f"{self._NAME} Assets", + "orchestration.atlan.com/usecase": "crawling", + "package.argoproj.io/author": "Atlan", + "package.argoproj.io/description": f"Package to crawl {self._NAME} assets and publish to Atlan for discovery.", # noqa + "package.argoproj.io/homepage": f"https://packages.atlan.com/-/web/detail/{self._PACKAGE_NAME}", + "package.argoproj.io/keywords": '["connector","crawler","dbt"]', # fmt: skip + "package.argoproj.io/name": self._PACKAGE_NAME, + "package.argoproj.io/registry": "https://packages.atlan.com", + "package.argoproj.io/repository": "git+https://github.com/atlanhq/marketplace-packages.git", + "package.argoproj.io/support": "support@atlan.com", + "orchestration.atlan.com/atlanName": f"{self._PACKAGE_PREFIX}-default-{self._NAME}-{self._epoch}", + }, + name=f"{self._PACKAGE_PREFIX}-{self._epoch}", + namespace="default", + ) diff --git a/pyatlan_v9/model/packages/dynamo_d_b_crawler.py b/pyatlan_v9/model/packages/dynamo_d_b_crawler.py new file mode 100644 index 000000000..d51d7ae45 --- /dev/null +++ b/pyatlan_v9/model/packages/dynamo_d_b_crawler.py @@ -0,0 +1,190 @@ +from __future__ import annotations + +from typing import List, Optional + +from pyatlan.model.enums import AtlanConnectorType, WorkflowPackage +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.model.packages.base.crawler import AbstractCrawler +from pyatlan_v9.model.workflow import WorkflowMetadata + + +class DynamoDBCrawler(AbstractCrawler): + """ + Base configuration for a new Amazon DynamoDB crawler. + + :param client: connectivity to an Atlan tenant + :param connection_name: name for the connection + :param admin_roles: admin roles for the connection + :param admin_groups: admin groups for the connection + :param admin_users: admin users for the connection + :param allow_query: allow data to be queried in the + connection (True) or not (False), default: True + :param allow_query_preview: allow sample data viewing for + assets in the connection (True) or not (False), default: True + :param row_limit: maximum number of rows + that can be returned by a query, default: 10000 + """ + + _NAME = "dynamodb" + _PACKAGE_NAME = "@atlan/dynamodb" + _PACKAGE_PREFIX = WorkflowPackage.DYNAMODB.value + _CONNECTOR_TYPE = AtlanConnectorType.DYNAMODB + _PACKAGE_ICON = "http://assets.atlan.com/assets/aws-dynamodb.svg" + _PACKAGE_LOGO = "http://assets.atlan.com/assets/aws-dynamodb.svg" + + def __init__( + self, + client: AtlanClient, + connection_name: str, + admin_roles: Optional[List[str]] = None, + admin_groups: Optional[List[str]] = None, + admin_users: Optional[List[str]] = None, + allow_query: bool = True, + allow_query_preview: bool = True, + row_limit: int = 10000, + ): + super().__init__( + client=client, + connection_name=connection_name, + connection_type=self._CONNECTOR_TYPE, + admin_roles=admin_roles, + admin_groups=admin_groups, + admin_users=admin_users, + allow_query=allow_query, + allow_query_preview=allow_query_preview, + row_limit=row_limit, + source_logo=self._PACKAGE_LOGO, + ) + + def direct( + self, + region: str, + ) -> DynamoDBCrawler: + """ + Set up the crawler to extract directly from the DynamoDB. + + :param region: AWS region where database is set up + :returns: crawler, set up to extract directly from DynamoDB + """ + local_creds = { + "name": f"default-{self._NAME}-{self._epoch}-0", + "extra": {"region": region}, + "connector_config_name": f"atlan-connectors-{self._NAME}", + } + self._credentials_body.update(local_creds) + self._parameters.append(dict(name="extraction-method", value="direct")) + return self + + def iam_user_auth(self, access_key: str, secret_key: str) -> DynamoDBCrawler: + """ + Set up the crawler to use IAM user-based authentication. + + :param access_key: through which to access DynamoDB + :param secret_key: through which to access DynamoDB + :returns: crawler, set up to use IAM user-based authentication + """ + local_creds = { + "auth_type": "iam", + "username": access_key, + "password": secret_key, + } + self._credentials_body.update(local_creds) + return self + + def iam_role_auth(self, arn: str, external_id: str) -> DynamoDBCrawler: + """ + Set up the crawler to use IAM role-based authentication. + + :param arn: ARN of the AWS role + :param external_id: AWS external ID + :returns: crawler, set up to use IAM user role-based authentication + """ + local_creds = { + "auth_type": "role", + "connector_type": "sdk", + } + self._credentials_body["extra"].update( + {"aws_role_arn": arn, "aws_external_id": external_id} + ) + self._credentials_body.update(local_creds) + return self + + def include_regex(self, regex: str) -> DynamoDBCrawler: + """ + Defines the regex of tables to include. + By default, everything will be included. + + :param regex: exclude regex for the crawler + :returns: crawler, set to include + only those assets specified in the regex + """ + self._parameters.append(dict(name="include-filter", value=regex)) + return self + + def exclude_regex(self, regex: str) -> DynamoDBCrawler: + """ + Defines the regex of tables to ignore. + By default, nothing will be excluded. + This takes priority over include regex. + + :param regex: exclude regex for the crawler + :returns: crawler, set to exclude + only those assets specified in the regex + """ + self._parameters.append(dict(name="exclude-filter", value=regex)) + return self + + def _set_required_metadata_params(self): + self._parameters.append( + {"name": "credentials-fetch-strategy", "value": "credential_guid"} + ) + self._parameters.append( + {"name": "credential-guid", "value": "{{credentialGuid}}"} + ) + self._parameters.append( + { + "name": "connection", + "value": self._get_connection().to_json(), + } + ) + self._parameters.append(dict(name="publish-mode", value="production")) + self._parameters.append(dict(name="atlas-auth-type", value="internal")) + + def _get_metadata(self) -> WorkflowMetadata: + self._set_required_metadata_params() + return WorkflowMetadata( + labels={ + "orchestration.atlan.com/certified": "true", + "orchestration.atlan.com/source": self._NAME, + "orchestration.atlan.com/sourceCategory": "nosql", + "orchestration.atlan.com/type": "connector", + "orchestration.atlan.com/verified": "true", + "package.argoproj.io/installer": "argopm", + "package.argoproj.io/name": f"a-t-ratlans-l-a-s-h{self._NAME}", + "package.argoproj.io/registry": "httpsc-o-l-o-ns-l-a-s-hs-l-a-s-hpackages.atlan.com", + f"orchestration.atlan.com/default-{self._NAME}-{self._epoch}": "true", + "orchestration.atlan.com/atlan-ui": "true", + }, + annotations={ + "orchestration.atlan.com/allowSchedule": "true", + "orchestration.atlan.com/categories": "nosql,crawler", + "orchestration.atlan.com/dependentPackage": "", + "orchestration.atlan.com/docsUrl": "https://ask.atlan.com/hc/en-us/articles/8362826839823", + "orchestration.atlan.com/emoji": "\U0001f680", + "orchestration.atlan.com/icon": self._PACKAGE_ICON, + "orchestration.atlan.com/logo": self._PACKAGE_LOGO, + "orchestration.atlan.com/marketplaceLink": f"https://packages.atlan.com/-/web/detail/{self._PACKAGE_NAME}", # noqa + "orchestration.atlan.com/name": "Amazon DynamoDB Assets", + "package.argoproj.io/author": "Atlan", + "package.argoproj.io/description": "Package to crawl Amazon DynamoDB assets and publish to Atlan for discovery", # noqa + "package.argoproj.io/homepage": f"https://packages.atlan.com/-/web/detail/{self._PACKAGE_NAME}", + "package.argoproj.io/keywords": '["dynamodb","nosql","document-database","connector","crawler"]', # fmt: skip # noqa + "package.argoproj.io/name": self._PACKAGE_NAME, + "package.argoproj.io/registry": "https://packages.atlan.com", + "package.argoproj.io/repository": "https://github.com/atlanhq/marketplace-packages.git", + "package.argoproj.io/support": "support@atlan.com", + "orchestration.atlan.com/atlanName": f"{self._PACKAGE_PREFIX}-default-{self._NAME}-{self._epoch}", + }, + name=f"{self._PACKAGE_PREFIX}-{self._epoch}", + namespace="default", + ) diff --git a/pyatlan_v9/model/packages/glue_crawler.py b/pyatlan_v9/model/packages/glue_crawler.py new file mode 100644 index 000000000..89c160ae6 --- /dev/null +++ b/pyatlan_v9/model/packages/glue_crawler.py @@ -0,0 +1,192 @@ +from __future__ import annotations + +from json import dumps +from typing import List, Optional + +from pyatlan.errors import ErrorCode +from pyatlan.model.enums import AtlanConnectorType, WorkflowPackage +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.model.packages.base.crawler import AbstractCrawler +from pyatlan_v9.model.workflow import WorkflowMetadata + + +class GlueCrawler(AbstractCrawler): + """ + Base configuration for a new Glue crawler. + + :param client: connectivity to an Atlan tenant + :param connection_name: name for the connection + :param admin_roles: admin roles for the connection + :param admin_groups: admin groups for the connection + :param admin_users: admin users for the connection + :param allow_query: allow data to be queried in the + connection (True) or not (False), default: False + :param allow_query_preview: allow sample data viewing for + assets in the connection (True) or not (False), default: False + :param row_limit: maximum number of rows + that can be returned by a query, default: 0 + """ + + _NAME = "glue" + _PACKAGE_NAME = "@atlan/glue" + _PACKAGE_PREFIX = WorkflowPackage.GLUE.value + _CONNECTOR_TYPE = AtlanConnectorType.GLUE + _AWS_DATA_CATALOG = "AwsDataCatalog" + _PACKAGE_ICON = ( + "https://atlan-public.s3.eu-west-1.amazonaws.com/atlan/logos/aws-glue.png" + ) + _PACKAGE_LOGO = ( + "https://atlan-public.s3.eu-west-1.amazonaws.com/atlan/logos/aws-glue.png" + ) + + def __init__( + self, + client: AtlanClient, + connection_name: str, + admin_roles: Optional[List[str]] = None, + admin_groups: Optional[List[str]] = None, + admin_users: Optional[List[str]] = None, + allow_query: bool = False, + allow_query_preview: bool = False, + row_limit: int = 0, + ): + super().__init__( + client=client, + connection_name=connection_name, + connection_type=self._CONNECTOR_TYPE, + admin_roles=admin_roles, + admin_groups=admin_groups, + admin_users=admin_users, + allow_query=allow_query, + allow_query_preview=allow_query_preview, + row_limit=row_limit, + source_logo=self._PACKAGE_LOGO, + ) + + def direct( + self, + region: str, + ) -> GlueCrawler: + """ + Set up the crawler to extract directly from Glue. + + :param region: AWS region where Glue is set up + :returns: crawler, set up to extract directly from Glue + """ + local_creds = { + "name": f"default-{self._NAME}-{self._epoch}-0", + "extra": {"region": region}, + "connector_config_name": f"atlan-connectors-{self._NAME}", + } + self._credentials_body.update(local_creds) + return self + + def iam_user_auth(self, access_key: str, secret_key: str) -> GlueCrawler: + """ + Set up the crawler to use IAM user-based authentication. + + :param access_key: through which to access Glue + :param secret_key: through which to access Glue + :returns: crawler, set up to use IAM user-based authentication + """ + local_creds = { + "auth_type": "iam", + "username": access_key, + "password": secret_key, + } + self._credentials_body.update(local_creds) + return self + + def _build_asset_filter(self, filter_type: str, filter_assets: List[str]) -> None: + if not filter_assets: + self._parameters.append({"name": f"{filter_type}-filter", "value": "{}"}) + return + filter_dict: dict = {self._AWS_DATA_CATALOG: {}} + try: + for asset in filter_assets: + filter_dict[self._AWS_DATA_CATALOG][asset] = {} + filter_values = dumps(filter_dict) + self._parameters.append( + {"name": f"{filter_type}-filter", "value": filter_values} + ) + except TypeError: + raise ErrorCode.UNABLE_TO_TRANSLATE_FILTERS.exception_with_parameters() + + def include(self, assets: List[str]) -> GlueCrawler: + """ + Defines the filter for assets to include when crawling. + + :param assets: list of schema names to include when crawling + :returns: crawler, set to include only those assets specified + :raises InvalidRequestException: In the unlikely + event the provided filter cannot be translated + """ + self._build_asset_filter("include", assets) + return self + + def exclude(self, assets: List[str]) -> GlueCrawler: + """ + Defines the filter for assets to exclude when crawling. + + :param assets: list of schema names to exclude when crawling + :returns: crawler, set to exclude only those assets specified + :raises InvalidRequestException: In the unlikely + event the provided filter cannot be translated + """ + self._build_asset_filter("exclude", assets) + return self + + def _set_required_metadata_params(self): + self._parameters.append( + dict(name="credentials-fetch-strategy", value="credential_guid") + ) + self._parameters.append( + {"name": "credential-guid", "value": "{{credentialGuid}}"} + ) + self._parameters.append( + { + "name": "connection", + "value": self._get_connection().to_json(), + } + ) + self._parameters.append(dict(name="publish-mode", value="production")) + self._parameters.append(dict(name="atlas-auth-type", value="internal")) + + def _get_metadata(self) -> WorkflowMetadata: + self._set_required_metadata_params() + return WorkflowMetadata( + labels={ + "orchestration.atlan.com/certified": "true", + "orchestration.atlan.com/source": self._NAME, + "orchestration.atlan.com/sourceCategory": "lake", + "orchestration.atlan.com/type": "connector", + "orchestration.atlan.com/verified": "true", + "package.argoproj.io/installer": "argopm", + "package.argoproj.io/name": f"a-t-ratlans-l-a-s-h{self._NAME}", + "package.argoproj.io/registry": "httpsc-o-l-o-ns-l-a-s-hs-l-a-s-hpackages.atlan.com", + f"orchestration.atlan.com/default-{self._NAME}-{self._epoch}": "true", + "orchestration.atlan.com/atlan-ui": "true", + }, + annotations={ + "orchestration.atlan.com/allowSchedule": "true", + "orchestration.atlan.com/dependentPackage": "", + "orchestration.atlan.com/docsUrl": "https://ask.atlan.com/hc/en-us/articles/6335637665681", + "orchestration.atlan.com/emoji": "\U0001f680", + "orchestration.atlan.com/icon": self._PACKAGE_ICON, + "orchestration.atlan.com/logo": self._PACKAGE_LOGO, + "orchestration.atlan.com/marketplaceLink": f"https://packages.atlan.com/-/web/detail/{self._PACKAGE_NAME}", # noqa + "orchestration.atlan.com/name": f"{self._NAME.capitalize()} Assets", + "orchestration.atlan.com/usecase": "crawling,auto-classifications", + "package.argoproj.io/author": "Atlan", + "package.argoproj.io/description": f"Package to crawl AWS {self._NAME.capitalize()} assets and publish to Atlan for discovery.", # noqa + "package.argoproj.io/homepage": f"https://packages.atlan.com/-/web/detail/{self._PACKAGE_NAME}", + "package.argoproj.io/keywords": '["lake","connector","crawler","glue","aws","s3"]', # fmt: skip # noqa + "package.argoproj.io/name": self._PACKAGE_NAME, + "package.argoproj.io/registry": "https://packages.atlan.com", + "package.argoproj.io/repository": "git+https://github.com/atlanhq/marketplace-packages.git", + "package.argoproj.io/support": "support@atlan.com", + "orchestration.atlan.com/atlanName": f"{self._PACKAGE_PREFIX}-default-{self._NAME}-{self._epoch}", + }, + name=f"{self._PACKAGE_PREFIX}-{self._epoch}", + namespace="default", + ) diff --git a/pyatlan_v9/model/packages/lineage_builder.py b/pyatlan_v9/model/packages/lineage_builder.py new file mode 100644 index 000000000..d16266a1f --- /dev/null +++ b/pyatlan_v9/model/packages/lineage_builder.py @@ -0,0 +1,206 @@ +from __future__ import annotations + +from typing import Optional + +from pyatlan.model.enums import AssetInputHandling, WorkflowPackage +from pyatlan_v9.model.packages.base.custom_package import AbstractCustomPackage +from pyatlan_v9.model.workflow import WorkflowMetadata + + +class LineageBuilder(AbstractCustomPackage): + """ + Base configuration for a new lineage builder package. + """ + + _NAME = "lineage-builder" + _PACKAGE_NAME = f"@csa/{_NAME}" + _PACKAGE_PREFIX = WorkflowPackage.LINEAGE_BUILDER.value + _PACKAGE_ICON = "http://assets.atlan.com/assets/ph-tree-structure-light.svg" + _PACKAGE_LOGO = "http://assets.atlan.com/assets/ph-tree-structure-light.svg" + + def object_store( + self, + prefix: str, + object_key: str, + ) -> LineageBuilder: + """ + Set up the package to retrieve the lineage file from cloud object storage. + + :param prefix: directory (path) within the object store from + which to retrieve the file containing asset metadata + :param object_key: object key (filename), + including its extension, within the object store and prefix + + :returns: package, set up to import lineage details from the object store + """ + self._parameters.append({"name": "lineage_prefix", "value": prefix}) + self._parameters.append({"name": "lineage_key", "value": object_key}) + self._parameters.append({"name": "lineage_import_type", "value": "CLOUD"}) + self._parameters.append({"name": "cloud_source", "value": "{{credentialGuid}}"}) + return self + + def s3( + self, + access_key: str, + secret_key: str, + region: str, + bucket: str, + ) -> LineageBuilder: + """ + Set up package to import lineage details from S3. + + :param access_key: AWS access key + :param secret_key: AWS secret key + :param region: AWS region + :param bucket: bucket to retrieve object store object from + + :returns: package, set up to import lineage details from S3 + """ + local_creds = { + "name": f"csa-{self._NAME}-{self._epoch}-0", + "auth_type": "s3", + "username": access_key, + "password": secret_key, + "extra": { + "region": region, + "s3_bucket": bucket, + }, + "connector_config_name": "csa-connectors-objectstore", + } + self._credentials_body.update(local_creds) + return self + + def gcs( + self, project_id: str, service_account_json: str, bucket: str + ) -> LineageBuilder: + """ + Set up package to import lineage details from GCS. + + :param project_id: ID of GCP project + :param service_account_json: service account credentials in JSON format + :param bucket: bucket to retrieve object store object from + + :returns: Package set up to import lineage details from GCS + """ + local_creds = { + "name": f"csa-{self._NAME}-{self._epoch}-0", + "auth_type": "gcs", + "username": project_id, + "password": service_account_json, + "extra": { + "gcs_bucket": bucket, + }, + "connector_config_name": "csa-connectors-objectstore", + } + self._credentials_body.update(local_creds) + return self + + def adls( + self, + client_id: str, + client_secret: str, + tenant_id: str, + account_name: str, + container: str, + ) -> LineageBuilder: + """ + Set up package to import lineage details from ADLS. + + :param client_id: unique application (client) ID assigned by Azure AD when the app was registered + :param client_secret: client secret for authentication + :param tenant_id: unique ID of the Azure Active Directory instance + :param account_name: name of the storage account + :param container: container to retrieve object store objects from + + :returns: package, set up to import lineage details from ADLS + """ + local_creds = { + "name": f"csa-{self._NAME}-{self._epoch}-0", + "auth_type": "adls", + "username": client_id, + "password": client_secret, + "extra": { + "azure_tenant_id": tenant_id, + "storage_account_name": account_name, + "adls_container": container, + }, + "connector_config_name": "csa-connectors-objectstore", + } + self._credentials_body.update(local_creds) + return self + + def options( + self, + input_handling: AssetInputHandling = AssetInputHandling.PARTIAL, + fail_on_errors: Optional[bool] = None, + case_sensitive_match: Optional[bool] = None, + field_separator: Optional[str] = None, + batch_size: Optional[int] = None, + ) -> LineageBuilder: + """ + Set up the lineage builder with the specified options. + + :param input_handling: specifies whether to allow the creation + of new assets from the input CSV (full or partial assets) + or only update existing (skip) assets in Atlan. + :param fail_on_errors: specifies whether an invalid value + in a field should cause the import to fail (`True`) or + log a warning, skip that value, and proceed (`False`). + :param case_sensitive_match: indicates whether to use + case-sensitive matching when running in update-only mode (`True`) + or to try case-insensitive matching (`False`). + :param field_separator: character used to separate + fields in the input file (e.g., ',' or ';'). + :param batch_size: maximum number of rows + to process at a time (per API request). + + :returns: package, configured to import + assets with advanced configuration. + """ + params = { + "lineage_upsert_semantic": input_handling, + "lineage_fail_on_errors": fail_on_errors, + "lineage_case_sensitive": case_sensitive_match, + "field_separator": field_separator, + "batch_size": batch_size, + } + self._add_optional_params(params) + return self + + def _get_metadata(self) -> WorkflowMetadata: + return WorkflowMetadata( + labels={ + "orchestration.atlan.com/certified": "true", + "orchestration.atlan.com/source": self._NAME, + "orchestration.atlan.com/sourceCategory": "utility", + "orchestration.atlan.com/type": "custom", + "orchestration.atlan.com/preview": "true", + "orchestration.atlan.com/verified": "true", + "package.argoproj.io/installer": "argopm", + "package.argoproj.io/name": f"a-t-rcsas-l-a-s-h{self._NAME}", + "package.argoproj.io/registry": "httpsc-o-l-o-ns-l-a-s-hs-l-a-s-hpackages.atlan.com", + "orchestration.atlan.com/atlan-ui": "true", + }, + annotations={ + "orchestration.atlan.com/allowSchedule": "true", + "orchestration.atlan.com/categories": "kotlin,utility", + "orchestration.atlan.com/dependentPackage": "", + "orchestration.atlan.com/docsUrl": f"https://solutions.atlan.com/{self._NAME}/", + "orchestration.atlan.com/emoji": "\U0001f680", + "orchestration.atlan.com/icon": self._PACKAGE_ICON, + "orchestration.atlan.com/logo": self._PACKAGE_LOGO, # noqa + "orchestration.atlan.com/name": "Lineage Builder", + "package.argoproj.io/author": "Atlan CSA", + "package.argoproj.io/description": "Build lineage from a CSV file.", + "package.argoproj.io/homepage": f"https://packages.atlan.com/-/web/detail/{self._PACKAGE_NAME}", + "package.argoproj.io/keywords": '["kotlin","utility"]', # fmt: skip + "package.argoproj.io/name": self._PACKAGE_NAME, + "package.argoproj.io/parent": ".", + "package.argoproj.io/registry": "https://packages.atlan.com", + "package.argoproj.io/repository": "git+https://github.com/atlanhq/marketplace-packages.git", + "package.argoproj.io/support": "support@atlan.com", + "orchestration.atlan.com/atlanName": f"csa-{self._NAME}-{self._epoch}", + }, + name=f"csa-{self._NAME}-{self._epoch}", + namespace="default", + ) diff --git a/pyatlan_v9/model/packages/lineage_generator_nt.py b/pyatlan_v9/model/packages/lineage_generator_nt.py new file mode 100644 index 000000000..d9c14886a --- /dev/null +++ b/pyatlan_v9/model/packages/lineage_generator_nt.py @@ -0,0 +1,197 @@ +from __future__ import annotations + +from enum import Enum +from typing import Optional + +from pyatlan.model.enums import WorkflowPackage +from pyatlan_v9.model.packages.base.custom_package import AbstractCustomPackage +from pyatlan_v9.model.workflow import WorkflowMetadata + + +class LineageGenerator(AbstractCustomPackage): + """ + Base configuration for a new lineage generator package. + """ + + _NAME = "lineage-generator" + _PACKAGE_NAME = f"@csa/{_NAME}" + _PACKAGE_PREFIX = WorkflowPackage.LINEAGE_GENERATOR.value + _PACKAGE_ICON = "https://assets.atlan.com/assets/add-lineage.svg" + _PACKAGE_LOGO = "https://assets.atlan.com/assets/add-lineage.svg" + + class OutputType(str, Enum): + PREVIEW = "preview" + GENERATE = "generate" + DELETE = "delete" + + class SourceAssetType(str, Enum): + Table = "Table" + View = "View" + MaterializedView = "Materialized View" + Column = "Column" + SalesforceObject = "Salesforce Object" + SalesforceField = "Salesforce Field" + MongoDBCollection = "MongoDB Collection" + S3Object = "S3 Object" + ADLSObject = "ADLS Object" + PowerBITable = "Power BI Table" + PowerBIColumn = "Power BI Column" + GCSObject = "GCS Object" + KafkaTopic = "Kafka Topic" + CalculationView = "Calculation View" + LookerField = "Looker Field" + LookerView = "Looker View" + + class TargetAssetType(str, Enum): + Table = "Table" + View = "View" + MaterializedView = "Materialized View" + Column = "Column" + SalesforceObject = "Salesforce Object" + SalesforceField = "Salesforce Field" + MongoDBCollection = "MongoDB Collection" + S3Object = "S3 Object" + ADLSObject = "ADLS Object" + PowerBITable = "Power BI Table" + PowerBIColumn = "Power BI Column" + GCSObject = "GCS Object" + KafkaTopic = "Kafka Topic" + CalculationView = "Calculation View" + LookerField = "Looker Field" + LookerView = "Looker View" + + def config( + self, + source_asset_type: SourceAssetType, + source_qualified_name: str, + target_asset_type: TargetAssetType, + target_qualified_name: str, + case_sensitive_match: bool = False, + match_on_schema: bool = False, + output_type: OutputType = OutputType.PREVIEW, + generate_on_child_assets: bool = False, + regex_match: Optional[str] = None, + regex_replace: Optional[str] = None, + regex_match_schema: Optional[str] = None, + regex_replace_schema: Optional[str] = None, + regex_match_schema_name: Optional[str] = None, + regex_replace_schema_name: Optional[str] = None, + match_prefix: Optional[str] = None, + match_suffix: Optional[str] = None, + file_advanced_seperator: Optional[str] = None, + file_advanced_position: Optional[str] = None, + process_connection_qn: Optional[str] = None, + ) -> LineageGenerator: + """ + Set up the lineage generator with the specified configuration. + + :param source_asset_type: type name of the lineage input assets (sources). + :param source_qualified_name: qualified name prefix of the lineage input assets (sources). + :param target_asset_type: type name of the lineage output assets (targets). + :param target_qualified_name: qualified name prefix of the lineage output assets (targets). + :param case_sensitive_match: whether to match asset names using a case sensitive logic, default: `False` + :param match_on_schema: whether to include the schema name to match source and target assets, default: `False`. + If one of `"Source asset type"` or `"Target asset type"` + is not a relational type (`Table`, `View`, `Materialized View`, + `Calculation View` or `Column`) or a `MongoDB Collection` the option is ignored, default: `False` + :param output_type: default to `Preview` lineage + - `Preview` lineage: to generate a csv with the lineage preview. + - `Generate` lineage: to generate the lineage on Atlan. + - `Delete` lineage: to delete the lineage on Atlan. + + :param generate_on_child_assets: whether to generate the lineage on the + child assets specified on `Source` asset type and `Target` asset type, default: `False`. + :param regex_match (optional): if there is a re-naming happening between + the source and the target that can be identified by a regex pattern, + use this field to identify the characters to be replaced. + :param regex_replace (optional): if there is a re-naming happening between the source + and the target that can be identified by a regex pattern, use this field to specify the replacements characters. + :param regex_match_schema (optional): if there is a re-naming happening between + the source and the target schema that can be identified by a regex pattern, + use this field to identify the characters to be replaced. Applicable only if `match_on_schema` is `False`. + :param regex_replace_schema (optional): if there is a re-naming happening between + the source and the target schema that can be identified by a regex pattern, + use this field to specify the replacements characters. Applicable only if `match_on_schema` is `True`. + :param regex_match_schema_name (optional): if there is a re-naming happening between + the source and the target name + schema that can be identified by a regex pattern, + use this field to identify the characters to be replaced. Applicable only if `match_on_schema` + is `True`. It overrides any other regex defined. + :param regex_replace_schema_name (optional): if there is a re-naming happening between + the source and the target name + schema that can be identified by a regex pattern, use this + field to specify the replacements characters. Applicable only if `match_on_schema` is `True`. + It overrides any other regex defined. + :param match_prefix (optional): prefix to add to source assets to match with target ones. + :param match_suffix (optional): suffix to add to source assets to match with target ones. + :param file_advanced_seperator (optional): sepator used to split the qualified name. + It's applicable to file based assets only. eg: if the separator is equal to + `/`: `default/s3/1707397085/arn:aws:s3:::mybucket/prefix/myobject.csv` + -> [`default,s3,1707397085,arn:aws:s3:::mybucket,prefix,myobject.csv`] + :param file_advanced_position (optional): number of substrings (created using "File advanced separator") + to use for the asset match. The count is from right to left. It's applicable to file based assets only. + In the above example if the value is equal to `3` -> [`arn:aws:s3:::mybucket,prefix,myobject.csv`] + :param process_connection_qn (optional): connection for the process assets. + If blank the process assets will be assigned to the source assets connection. + + :returns: package, set up lineage generator with the specified configuration. + """ + params = { + "source-asset-type": source_asset_type.value, + "source-qualified-name-prefix": source_qualified_name, + "target-asset-type": target_asset_type.value, + "target-qualified-name-prefix": target_qualified_name, + "case-sensitive": "yes" if case_sensitive_match else "no", + "match-on-schema": "yes" if match_on_schema else "no", + "output-option": output_type.value, + "child-lineage": "yes" if generate_on_child_assets else "no", + "regex-match": regex_match, + "regex-replace": regex_replace, + "regex-match-schema": regex_match_schema, + "regex-replace-schema": regex_replace_schema, + "regex-match-schema-name": regex_match_schema_name, + "regex-replace-schema-name": regex_replace_schema_name, + "name-prefix": match_prefix, + "name-suffix": match_suffix, + "file-advanced-separator": file_advanced_seperator, + "file-advanced-positions": file_advanced_position, + "connection-qualified-name": process_connection_qn, + } + self._add_optional_params(params) + return self + + def _get_metadata(self) -> WorkflowMetadata: + return WorkflowMetadata( + labels={ + "orchestration.atlan.com/certified": "true", + "orchestration.atlan.com/source": self._NAME, + "orchestration.atlan.com/sourceCategory": "utility", + "orchestration.atlan.com/type": "custom", + "orchestration.atlan.com/preview": "true", + "orchestration.atlan.com/verified": "true", + "package.argoproj.io/installer": "argopm", + "package.argoproj.io/name": f"a-t-rcsas-l-a-s-h{self._NAME}", + "package.argoproj.io/registry": "httpsc-o-l-o-ns-l-a-s-hs-l-a-s-hpackages.atlan.com", + "orchestration.atlan.com/atlan-ui": "true", + }, + annotations={ + "orchestration.atlan.com/allowSchedule": "true", + "orchestration.atlan.com/categories": "python,utility", + "orchestration.atlan.com/dependentPackage": "", + "orchestration.atlan.com/docsUrl": f"https://solutions.atlan.com/{self._NAME}/", + "orchestration.atlan.com/emoji": "\U0001f680", + "orchestration.atlan.com/icon": self._PACKAGE_ICON, + "orchestration.atlan.com/logo": self._PACKAGE_LOGO, # noqa + "orchestration.atlan.com/name": "Lineage Generator (no transformations)", + "package.argoproj.io/author": "Atlan CSA", + "package.argoproj.io/description": "Package to generate lineage between two systems - no transformations involved.", # noqa + "package.argoproj.io/homepage": f"https://packages.atlan.com/-/web/detail/{self._PACKAGE_NAME}", + "package.argoproj.io/keywords": '["python","utility", "custom-package"]', # fmt: skip + "package.argoproj.io/name": self._PACKAGE_NAME, + "package.argoproj.io/parent": ".", + "package.argoproj.io/registry": "https://packages.atlan.com", + "package.argoproj.io/repository": "git+https://github.com/atlanhq/marketplace-packages.git", + "package.argoproj.io/support": "support@atlan.com", + "orchestration.atlan.com/atlanName": f"csa-{self._NAME}-{self._epoch}", + }, + name=f"csa-{self._NAME}-{self._epoch}", + namespace="default", + ) diff --git a/pyatlan_v9/model/packages/mongodb_crawler.py b/pyatlan_v9/model/packages/mongodb_crawler.py new file mode 100644 index 000000000..38b606331 --- /dev/null +++ b/pyatlan_v9/model/packages/mongodb_crawler.py @@ -0,0 +1,208 @@ +from __future__ import annotations + +from typing import Dict, List, Optional + +from pyatlan.model.enums import AtlanConnectorType, WorkflowPackage +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.model.packages.base.crawler import AbstractCrawler +from pyatlan_v9.model.workflow import WorkflowMetadata + + +class MongoDBCrawler(AbstractCrawler): + """ + Base configuration for a new MongoDB crawler. + + :param client: connectivity to an Atlan tenant + :param connection_name: name for the connection + :param admin_roles: admin roles for the connection + :param admin_groups: admin groups for the connection + :param admin_users: admin users for the connection + :param allow_query: allow data to be queried in the + connection (True) or not (False), default: True + :param allow_query_preview: allow sample data viewing for + assets in the connection (True) or not (False), default: True + :param row_limit: maximum number of rows + that can be returned by a query, default: 10000 + """ + + _NAME = "mongodb" + _PACKAGE_NAME = "@atlan/mongodb" + _PACKAGE_PREFIX = WorkflowPackage.MONGODB.value + _CONNECTOR_TYPE = AtlanConnectorType.MONGODB + _PACKAGE_ICON = "https://assets.atlan.com/assets/mongoDB.svg" + _PACKAGE_LOGO = "https://assets.atlan.com/assets/mongoDB.svg" + + def __init__( + self, + client: AtlanClient, + connection_name: str, + admin_roles: Optional[List[str]] = None, + admin_groups: Optional[List[str]] = None, + admin_users: Optional[List[str]] = None, + allow_query: bool = True, + allow_query_preview: bool = True, + row_limit: int = 10000, + ): + super().__init__( + client=client, + connection_name=connection_name, + connection_type=self._CONNECTOR_TYPE, + admin_roles=admin_roles, + admin_groups=admin_groups, + admin_users=admin_users, + allow_query=allow_query, + allow_query_preview=allow_query_preview, + row_limit=row_limit, + source_logo=self._PACKAGE_LOGO, + ) + + def direct(self, hostname: str, port: int = 27017) -> MongoDBCrawler: + """ + Set up the crawler to extract directly from the MongoDB Atlas. + + :param hostname: hostname of the Atlas SQL connection + :param port: port number of the Atlas SQL connection. default: `27017` + :returns: crawler, set up to extract directly from the Atlas SQL connection + """ + local_creds = { + "name": f"default-{self._NAME}-{self._epoch}-0", + "host": hostname, + "port": port, + "connector_config_name": f"atlan-connectors-{self._NAME}", + } + self._credentials_body.update(local_creds) + self._parameters.append(dict(name="extraction-method", value="direct")) + return self + + def basic_auth( + self, + username: str, + password: str, + native_host: str, + default_db: str, + auth_db: str = "admin", + is_ssl: bool = True, + ) -> MongoDBCrawler: + """ + Set up the crawler to use basic authentication. + + :param username: through which to access Atlas SQL connection. + :param password: through which to access Atlas SQL connection. + :param native_host: native host address for the MongoDB connection. + :param default_db: default database to connect to. + :param auth_db: authentication database to use (default is `"admin"`). + :param is_ssl: whether to use SSL for the connection (default is `True`). + :returns: crawler, set up to use basic authentication + """ + local_creds = { + "authType": "basic", + "username": username, + "password": password, + "extra": { + "native-host": native_host, + "default-database": default_db, + "authsource": auth_db, + "ssl": is_ssl, + }, + } + self._credentials_body.update(local_creds) + return self + + def include(self, assets: List[str]) -> MongoDBCrawler: + """ + Defines the filter for assets to include when crawling. + + :param assets: list of databases names to include when crawling + :returns: crawler, set to include only those assets specified + :raises InvalidRequestException: In the unlikely + event the provided filter cannot be translated + """ + assets = assets or [] + include_assets: Dict[str, List[str]] = {asset: [] for asset in assets} + to_include = self.build_hierarchical_filter(include_assets) + self._parameters.append( + dict(dict(name="include-filter", value=to_include or "{}")) + ) + return self + + def exclude(self, assets: List[str]) -> MongoDBCrawler: + """ + Defines the filter for assets to exclude when crawling. + + :param assets: list of databases names to exclude when crawling + :returns: crawler, set to exclude only those assets specified + :raises InvalidRequestException: In the unlikely + event the provided filter cannot be translated + """ + assets = assets or [] + exclude_assets: Dict[str, List[str]] = {asset: [] for asset in assets} + to_exclude = self.build_hierarchical_filter(exclude_assets) + self._parameters.append(dict(name="exclude-filter", value=to_exclude or "{}")) + return self + + def exclude_regex(self, regex: str) -> MongoDBCrawler: + """ + Defines the exclude regex for crawler + ignore collections based on a naming convention. + + :param regex: exclude regex for the crawler + :returns: crawler, set to exclude + only those assets specified in the regex + """ + self._parameters.append(dict(name="temp-table-regex", value=regex)) + return self + + def _set_required_metadata_params(self): + self._parameters.append( + {"name": "credentials-fetch-strategy", "value": "credential_guid"} + ) + self._parameters.append( + {"name": "credential-guid", "value": "{{credentialGuid}}"} + ) + self._parameters.append( + { + "name": "connection", + "value": self._get_connection().to_json(), + } + ) + self._parameters.append(dict(name="publish-mode", value="production")) + self._parameters.append(dict(name="atlas-auth-type", value="internal")) + + def _get_metadata(self) -> WorkflowMetadata: + self._set_required_metadata_params() + return WorkflowMetadata( + labels={ + "orchestration.atlan.com/certified": "true", + "orchestration.atlan.com/source": self._NAME, + "orchestration.atlan.com/sourceCategory": "nosql", + "orchestration.atlan.com/type": "connector", + "orchestration.atlan.com/verified": "true", + "package.argoproj.io/installer": "argopm", + "package.argoproj.io/name": f"a-t-ratlans-l-a-s-h{self._NAME}", + "package.argoproj.io/registry": "httpsc-o-l-o-ns-l-a-s-hs-l-a-s-hpackages.atlan.com", + f"orchestration.atlan.com/default-{self._NAME}-{self._epoch}": "true", + "orchestration.atlan.com/atlan-ui": "true", + }, + annotations={ + "orchestration.atlan.com/allowSchedule": "true", + "orchestration.atlan.com/categories": "nosql,crawler", + "orchestration.atlan.com/dependentPackage": "", + "orchestration.atlan.com/docsUrl": "https://ask.atlan.com/hc/en-us/articles/7841931946639", # noqa + "orchestration.atlan.com/emoji": "\U0001f680", + "orchestration.atlan.com/icon": self._PACKAGE_ICON, + "orchestration.atlan.com/logo": self._PACKAGE_LOGO, + "orchestration.atlan.com/marketplaceLink": f"https://packages.atlan.com/-/web/detail/{self._PACKAGE_NAME}", # noqa + "orchestration.atlan.com/name": "MongoDB Assets", + "package.argoproj.io/author": "Atlan", + "package.argoproj.io/description": f"Package to crawl MongoDB assets and publish to Atlan for discovery", # noqa + "package.argoproj.io/homepage": f"https://packages.atlan.com/-/web/detail/{self._PACKAGE_NAME}", + "package.argoproj.io/keywords": '["mongodb","nosql","document-database","connector","crawler"]', # fmt: skip # noqa + "package.argoproj.io/name": self._PACKAGE_NAME, + "package.argoproj.io/registry": "https://packages.atlan.com", + "package.argoproj.io/repository": "git+https://github.com/atlanhq/marketplace-packages.git", + "package.argoproj.io/support": "support@atlan.com", + "orchestration.atlan.com/atlanName": f"{self._PACKAGE_PREFIX}-default-{self._NAME}-{self._epoch}", + }, + name=f"{self._PACKAGE_PREFIX}-{self._epoch}", + namespace="default", + ) diff --git a/pyatlan_v9/model/packages/oracle_crawler.py b/pyatlan_v9/model/packages/oracle_crawler.py new file mode 100644 index 000000000..269b73e12 --- /dev/null +++ b/pyatlan_v9/model/packages/oracle_crawler.py @@ -0,0 +1,368 @@ +from __future__ import annotations + +from enum import Enum +from json import dumps +from typing import Any, Dict, List, Optional + +from pyatlan.model.enums import AtlanConnectorType, WorkflowPackage +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.model.packages.base.crawler import AbstractCrawler +from pyatlan_v9.model.workflow import WorkflowMetadata + + +class OracleCrawler(AbstractCrawler): + """ + Base configuration for a new Oracle crawler. + + :param client: connectivity to an Atlan tenant + :param connection_name: name for the connection + :param admin_roles: admin roles for the connection + :param admin_groups: admin groups for the connection + :param admin_users: admin users for the connection + :param allow_query: allow data to be queried in the + connection (True) or not (False), default: True + :param allow_query_preview: allow sample data viewing for + assets in the connection (True) or not (False), default: True + :param row_limit: maximum number of rows + that can be returned by a query, default: 10000 + """ + + _NAME = "oracle" + _PACKAGE_NAME = "@atlan/oracle" + _PACKAGE_PREFIX = WorkflowPackage.ORACLE.value + _CONNECTOR_TYPE = AtlanConnectorType.ORACLE + _PACKAGE_ICON = ( + "https://docs.oracle.com/sp_common/book-template/ohc-common/img/favicon.ico" + ) + _PACKAGE_LOGO = ( + "https://docs.oracle.com/sp_common/book-template/ohc-common/img/favicon.ico" + ) + + class AuthType(str, Enum): + BASIC = "basic" + KERBEROS = "kerberos" + + class AwsAuthMethod(str, Enum): + IAM = "iam" + IAM_ASSUME_ROLE = "iam-assume-role" + ACCESS_KEY = "access-key" + + class AzureAuthMethod(str, Enum): + MANAGED_IDENTITY = "managed_identity" + SERVICE_PRINCIPAL = "service_principal" + + class SecretStore(str, Enum): + SECRET_INJECTION_ENV = "secretinjectionenvironment" + AWS_SECRET_MANAGER = "awssecretmanager" + AZURE_KEY_VAULT = "azurekeyvault" + GCP_SECRET_MANAGER = "gcpsecretmanager" + CUSTOM = "custom" + + def __init__( + self, + client: AtlanClient, + connection_name: str, + admin_roles: Optional[List[str]] = None, + admin_groups: Optional[List[str]] = None, + admin_users: Optional[List[str]] = None, + allow_query: bool = True, + allow_query_preview: bool = True, + row_limit: int = 10000, + ): + self._advanced_config = False + self._agent_config = False + super().__init__( + client=client, + connection_name=connection_name, + connection_type=self._CONNECTOR_TYPE, + admin_roles=admin_roles, + admin_groups=admin_groups, + admin_users=admin_users, + allow_query=allow_query, + allow_query_preview=allow_query_preview, + row_limit=row_limit, + source_logo=self._PACKAGE_LOGO, + ) + + def s3(self, bucket_name: str, bucket_prefix: str) -> OracleCrawler: + """ + Set up the crawler to fetch metadata directly from the S3 bucket. + + :param bucket_name: name of the S3 bucket containing the extracted metadata files + :param bucket_prefix: prefix within the S3 bucket where the extracted metadata files are located + :returns: crawler, configured to fetch metadata directly from the S3 bucket + """ + self._parameters.append(dict(name="extraction-method", value="s3")) + self._parameters.append(dict(name="metadata-s3-bucket", value=bucket_name)) + self._parameters.append(dict(name="metadata-s3-prefix", value=bucket_prefix)) + # Advanced configuration defaults + self.jdbc_internal_methods(enable=True) + self.source_level_filtering(enable=False) + return self + + def direct( + self, + hostname: str, + port: int = 1521, + ) -> OracleCrawler: + """ + Set up the crawler to extract directly from Oracle. + + :param hostname: hostname of the Oracle instance + :param port: port number of Oracle instance, defaults to `1521` + :returns: crawler, set up to extract directly from Oracle. + """ + local_creds = { + "name": f"default-{self._NAME}-{self._epoch}-0", + "host": hostname, + "port": port, + "connector_config_name": f"atlan-connectors-{self._NAME}", + } + self._credentials_body.update(local_creds) + self._parameters.append(dict(name="extraction-method", value="direct")) + return self + + def agent_config( + self, + hostname: str, + default_db_name: str, + sid: str, + agent_name: str, + port: int = 1521, + user_env_var: Optional[str] = None, + password_env_var: Optional[str] = None, + secret_store: SecretStore = SecretStore.CUSTOM, + auth_type: AuthType = AuthType.BASIC, + aws_region: str = "us-east-1", + aws_auth_method: AwsAuthMethod = AwsAuthMethod.IAM, + azure_auth_method: AzureAuthMethod = AzureAuthMethod.MANAGED_IDENTITY, + secret_path: Optional[str] = None, + principal: Optional[str] = None, + azure_vault_name: Optional[str] = None, + agent_custom_config: Optional[Dict[str, Any]] = None, + ) -> OracleCrawler: + """ + Configure the agent for Oracle extraction. + + :param hostname: host address of the Oracle instance. + :param default_db_name: default database name. + :param sid: SID (system identifier) of the Oracle instance. + :param agent_name: name of the agent. + :param secret_store: secret store to use (e.g AWS, Azure, GCP, etc) + :param port: port number for the Oracle instance. Defaults to `1521`. + :param user_env_var: (optional) environment variable storing the username. + :param password_env_var: (optional) environment variable storing the password. + :param auth_type: authentication type (`basic` or `kerberos`). Defaults to `basic`. + :param aws_region: AWS region where secrets are stored. Defaults to `us-east-1`. + :param aws_auth_method: AWS authentication method (`iam`, `iam-assume-role`, `access-key`). Defaults to `iam`. + :param azure_auth_method: Azure authentication method (`managed_identity` or `service_principal`). Defaults to `managed_identity`. + :param secret_path: (optional) path to the secret in the secret manager. + :param principal: (optional) Kerberos principal (required if using Kerberos authentication). + :param azure_vault_name: (optional) Azure Key Vault name (required if using Azure secret store). + :param agent_custom_config: (optional Custom JSON configuration for the agent. + + :returns: crawler, set up to extraction from offline agent. + """ + self._agent_config = True + _agent_dict = { + "host": hostname, + "port": port, + "auth-type": auth_type, + "database": default_db_name, + "extra-service": sid, + "agent-name": agent_name, + "secret-manager": secret_store, + "user-env": user_env_var, + "password-env": password_env_var, + "agent-config": agent_custom_config, + "aws-auth-method": aws_auth_method, + "aws-region": aws_region, + "azure-auth-method": azure_auth_method, + } + if secret_path: + _agent_dict["secret-path"] = secret_path + if principal: + _agent_dict["extra-principal"] = principal + if agent_custom_config: + _agent_dict["agent-config"] = agent_custom_config + if azure_vault_name: + _agent_dict["azure-vault-name"] = azure_vault_name + self._parameters.append(dict(name="extraction-method", value="agent")) + self._parameters.append(dict(name="agent-json", value=dumps(_agent_dict))) + return self + + def basic_auth( + self, + username: str, + password: str, + sid: str, + database_name: str, + ) -> OracleCrawler: + """ + Set up the crawler to use basic authentication. + + :param username: through which to access Oracle + :param password: through which to access Oracle + :param sid: SID (system identifier) of the Oracle instance + :param database_name: database name to crawl + :returns: crawler, set up to use basic authentication + """ + local_creds = { + "name": f"default-{self._NAME}-{self._epoch}-0", + "auth_type": "basic", + "username": username, + "password": password, + "extra": {"sid": sid, "databaseName": database_name}, + } + self._credentials_body.update(local_creds) + return self + + def include(self, assets: dict) -> OracleCrawler: + """ + Defines the filter for assets to include when crawling. + + :param assets: Map keyed by database name with each value being a list of schemas + :returns: crawler, set to include only those assets specified + :raises InvalidRequestException: In the unlikely + event the provided filter cannot be translated + """ + include_assets = assets or {} + to_include = self.build_hierarchical_filter(include_assets) + self._parameters.append( + dict( + dict( + name="include-filter" + if not self._agent_config + else "include-filter-agent", + value=to_include or "{}", + ) + ) + ) + return self + + def exclude(self, assets: dict) -> OracleCrawler: + """ + Defines the filter for assets to exclude when crawling. + + :param assets: Map keyed by database name with each value being a list of schemas + :returns: crawler, set to exclude only those assets specified + :raises InvalidRequestException: In the unlikely + event the provided filter cannot be translated + """ + exclude_assets = assets or {} + to_exclude = self.build_hierarchical_filter(exclude_assets) + self._parameters.append( + dict( + name="exclude-filter" + if not self._agent_config + else "exclude-filter-agent", + value=to_exclude or "{}", + ) + ) + return self + + def exclude_regex(self, regex: str) -> OracleCrawler: + """ + Defines the exclude regex for crawler ignore + tables and views based on a naming convention. + + :param regex: exclude regex for the crawler + :returns: crawler, set to exclude + only those assets specified in the regex + """ + self._parameters.append(dict(name="temp-table-regex", value=regex)) + return self + + def jdbc_internal_methods(self, enable: bool) -> OracleCrawler: + """ + Defines whether to enable or disable JDBC + internal methods for data extraction. + + :param enable: whether to whether to enable (`True`) or + disable (`False`) JDBC internal methods for data extraction + :returns: crawler, with jdbc internal methods for data extraction + """ + self._advanced_config = True + self._parameters.append( + dict(name="use-jdbc-internal-methods", value="true" if enable else "false") + ) + return self + + def source_level_filtering(self, enable: bool) -> OracleCrawler: + """ + Defines whether to enable or disable schema level filtering on source. + schemas selected in the include filter will be fetched. + + :param enable: whether to enable (`True`) or + disable (`False`) schema level filtering on source + :returns: crawler, with schema level filtering on source + """ + self._advanced_config = True + self._parameters.append( + dict( + name="use-source-schema-filtering", value="true" if enable else "false" + ) + ) + return self + + def _set_required_metadata_params(self): + self._parameters.append( + {"name": "credentials-fetch-strategy", "value": "credential_guid"} + ) + self._parameters.append(dict(name="publish-mode", value="production")) + self._parameters.append(dict(name="atlas-auth-type", value="internal")) + self._parameters.append( + dict( + name="advanced-config-strategy", + value="custom" if self._advanced_config else "default", + ) + ) + self._parameters.append( + { + "name": "connection", + "value": self._get_connection().to_json(), + } + ) + if not self._agent_config: + self._parameters.append( + {"name": "credential-guid", "value": "{{credentialGuid}}"} + ) + + def _get_metadata(self) -> WorkflowMetadata: + self._set_required_metadata_params() + return WorkflowMetadata( + labels={ + "orchestration.atlan.com/certified": "true", + "orchestration.atlan.com/source": self._NAME, + "orchestration.atlan.com/sourceCategory": "warehouse", + "orchestration.atlan.com/type": "connector", + "orchestration.atlan.com/verified": "true", + "package.argoproj.io/installer": "argopm", + "package.argoproj.io/name": f"a-t-ratlans-l-a-s-h{self._NAME}", + "package.argoproj.io/registry": "httpsc-o-l-o-ns-l-a-s-hs-l-a-s-hpackages.atlan.com", + f"orchestration.atlan.com/default-{self._NAME}-{self._epoch}": "true", + "orchestration.atlan.com/atlan-ui": "true", + }, + annotations={ + "orchestration.atlan.com/allowSchedule": "true", + "orchestration.atlan.com/categories": "warehouse,crawler", + "orchestration.atlan.com/dependentPackage": "", + "orchestration.atlan.com/docsUrl": "https://ask.atlan.com/hc/en-us/articles/6849958872861", + "orchestration.atlan.com/emoji": "\U0001f680", + "orchestration.atlan.com/icon": self._PACKAGE_ICON, + "orchestration.atlan.com/logo": self._PACKAGE_LOGO, + "orchestration.atlan.com/marketplaceLink": f"https://packages.atlan.com/-/web/detail/{self._PACKAGE_NAME}", # noqa + "orchestration.atlan.com/name": "Oracle Assets", + "package.argoproj.io/author": "Atlan", + "package.argoproj.io/description": "Package to crawl Oracle assets and publish to Atlan for discovery", + "package.argoproj.io/homepage": f"https://packages.atlan.com/-/web/detail/{self._PACKAGE_NAME}", + "package.argoproj.io/keywords": '["oracle","warehouse","connector","crawler"]', # fmt: skip + "package.argoproj.io/name": self._PACKAGE_NAME, + "package.argoproj.io/registry": "https://packages.atlan.com", + "package.argoproj.io/repository": "git+https://github.com/atlanhq/marketplace-packages.git", + "package.argoproj.io/support": "support@atlan.com", + "orchestration.atlan.com/atlanName": f"{self._PACKAGE_PREFIX}-default-{self._NAME}-{self._epoch}", + }, + name=f"{self._PACKAGE_PREFIX}-{self._epoch}", + namespace="default", + ) diff --git a/pyatlan_v9/model/packages/postgres_crawler.py b/pyatlan_v9/model/packages/postgres_crawler.py new file mode 100644 index 000000000..2c1694dca --- /dev/null +++ b/pyatlan_v9/model/packages/postgres_crawler.py @@ -0,0 +1,278 @@ +from __future__ import annotations + +from typing import List, Optional + +from pyatlan.model.enums import AtlanConnectorType, WorkflowPackage +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.model.packages.base.crawler import AbstractCrawler +from pyatlan_v9.model.workflow import WorkflowMetadata + + +class PostgresCrawler(AbstractCrawler): + """ + Base configuration for a new PostgreSQL crawler. + + :param client: connectivity to an Atlan tenant + :param connection_name: name for the connection + :param admin_roles: admin roles for the connection + :param admin_groups: admin groups for the connection + :param admin_users: admin users for the connection + :param allow_query: allow data to be queried in the + connection (True) or not (False), default: True + :param allow_query_preview: allow sample data viewing for + assets in the connection (True) or not (False), default: True + :param row_limit: maximum number of rows + that can be returned by a query, default: 10000 + """ + + _NAME = "postgres" + _PACKAGE_NAME = "@atlan/postgres" + _PACKAGE_PREFIX = WorkflowPackage.POSTGRES.value + _CONNECTOR_TYPE = AtlanConnectorType.POSTGRES + _PACKAGE_ICON = "https://www.postgresql.org/media/img/about/press/elephant.png" + _PACKAGE_LOGO = "https://www.postgresql.org/media/img/about/press/elephant.png" + + def __init__( + self, + client: AtlanClient, + connection_name: str, + admin_roles: Optional[List[str]] = None, + admin_groups: Optional[List[str]] = None, + admin_users: Optional[List[str]] = None, + allow_query: bool = True, + allow_query_preview: bool = True, + row_limit: int = 10000, + ): + super().__init__( + client=client, + connection_name=connection_name, + connection_type=self._CONNECTOR_TYPE, + admin_roles=admin_roles, + admin_groups=admin_groups, + admin_users=admin_users, + allow_query=allow_query, + allow_query_preview=allow_query_preview, + row_limit=row_limit, + source_logo=self._PACKAGE_LOGO, + ) + + def s3( + self, + bucket_name: str, + bucket_prefix: str, + bucket_region: Optional[str] = None, + ) -> PostgresCrawler: + """ + Set up the crawler to extract from S3 bucket + + :param bucket_name: name of the bucket/storage + that contains the extracted metadata files + :param bucket_prefix: prefix is everything after the + bucket/storage name, including the `path` + :param bucket_region: (Optional) name of the region if applicable + :returns: crawler, set up to extract from S3 bucket + """ + self._parameters.append(dict(name="extraction-method", value="s3")) + self._parameters.append(dict(name="metadata-s3-bucket", value=bucket_name)) + self._parameters.append(dict(name="metadata-s3-prefix", value=bucket_prefix)) + self._parameters.append(dict(name="metadata-s3-region", value=bucket_region)) + return self + + def direct( + self, + hostname: str, + database: str, + port: int = 5432, + ) -> PostgresCrawler: + """ + Set up the crawler to extract directly from PostgreSQL. + + :param hostname: hostname of the PostgreSQL instance + :param database: database name to crawl + :param port: port number of PostgreSQL instance, defaults to `5432` + :returns: crawler, set up to extract directly from PostgreSQL + """ + local_creds = { + "name": f"default-{self._NAME}-{self._epoch}-0", + "host": hostname, + "port": port, + "extra": {"database": database}, + "connector_config_name": f"atlan-connectors-{self._NAME}", + } + self._credentials_body.update(local_creds) + self._parameters.append(dict(name="extraction-method", value="direct")) + return self + + def basic_auth(self, username: str, password: str) -> PostgresCrawler: + """ + Set up the crawler to use basic authentication. + + :param username: through which to access PostgreSQL + :param password: through which to access PostgreSQL + :returns: crawler, set up to use basic authentication + """ + local_creds = { + "auth_type": "basic", + "username": username, + "password": password, + } + self._credentials_body.update(local_creds) + return self + + def iam_user_auth( + self, username: str, access_key: str, secret_key: str + ) -> PostgresCrawler: + """ + Set up the crawler to use IAM user-based authentication. + + :param username: database username to extract from + :param access_key: through which to access PostgreSQL + :param secret_key: through which to access PostgreSQL + :returns: crawler, set up to use IAM user-based authentication + """ + local_creds = { + "auth_type": "iam_user", + "connector_type": "jdbc", + "username": access_key, + "password": secret_key, + } + self._credentials_body["extra"].update({"username": username}) + self._credentials_body.update(local_creds) + return self + + def iam_role_auth( + self, username: str, arn: str, external_id: str + ) -> PostgresCrawler: + """ + Set up the crawler to use IAM role-based authentication. + + :param username: database username to extract from + :param arn: ARN of the AWS role + :param external_id: AWS external ID + :returns: crawler, set up to use IAM user role-based authentication + """ + local_creds = { + "auth_type": "iam_role", + "connector_type": "jdbc", + } + self._credentials_body["extra"].update( + {"username": username, "aws_role_arn": arn, "aws_external_id": external_id} + ) + self._credentials_body.update(local_creds) + return self + + def include(self, assets: dict) -> PostgresCrawler: + """ + Defines the filter for assets to include when crawling. + + :param assets: Map keyed by database name with each value being a list of schemas + :returns: crawler, set to include only those assets specified + :raises InvalidRequestException: In the unlikely + event the provided filter cannot be translated + """ + include_assets = assets or {} + to_include = self.build_hierarchical_filter(include_assets) + self._parameters.append(dict(name="include-filter", value=to_include or "{}")) + return self + + def exclude(self, assets: dict) -> PostgresCrawler: + """ + Defines the filter for assets to exclude when crawling. + + :param assets: Map keyed by database name with each value being a list of schemas + :returns: crawler, set to exclude only those assets specified + :raises InvalidRequestException: In the unlikely + event the provided filter cannot be translated + """ + exclude_assets = assets or {} + to_exclude = self.build_hierarchical_filter(exclude_assets) + self._parameters.append(dict(name="exclude-filter", value=to_exclude or "{}")) + return self + + def exclude_regex(self, regex: str) -> PostgresCrawler: + """ + Defines the exclude regex for crawler ignore + tables and views based on a naming convention. + + :param regex: exclude regex for the crawler + :returns: crawler, set to exclude + only those assets specified in the regex + """ + self._parameters.append(dict(name="temp-table-regex", value=regex)) + return self + + def source_level_filtering(self, enable: bool) -> PostgresCrawler: + """ + Defines whether to enable or disable schema level filtering on source. + schemas selected in the include filter will be fetched. + + :param enable: whether to enable (`True`) or + disable (`False`) schema level filtering on source + :returns: crawler, with schema level filtering on source + """ + self._parameters.append(dict(name="use-source-schema-filtering", value=enable)) + return self + + def jdbc_internal_methods(self, enable: bool) -> PostgresCrawler: + """ + Defines whether to enable or disable JDBC + internal methods for data extraction. + + :param enable: whether to whether to enable (`True`) or + disable (`False`) JDBC internal methods for data extraction + :returns: crawler, with jdbc internal methods for data extraction + """ + self._parameters.append(dict(name="use-jdbc-internal-methods", value=enable)) + return self + + def _set_required_metadata_params(self): + self._parameters.append( + {"name": "credential-guid", "value": "{{credentialGuid}}"} + ) + self._parameters.append( + { + "name": "connection", + "value": self._get_connection().to_json(), + } + ) + self._parameters.append(dict(name="publish-mode", value="production")) + + def _get_metadata(self) -> WorkflowMetadata: + self._set_required_metadata_params() + return WorkflowMetadata( + labels={ + "orchestration.atlan.com/certified": "true", + "orchestration.atlan.com/source": self._NAME, + "orchestration.atlan.com/sourceCategory": "database", + "orchestration.atlan.com/type": "connector", + "orchestration.atlan.com/verified": "true", + "package.argoproj.io/installer": "argopm", + "package.argoproj.io/name": f"a-t-ratlans-l-a-s-h{self._NAME}", + "package.argoproj.io/registry": "httpsc-o-l-o-ns-l-a-s-hs-l-a-s-hpackages.atlan.com", + f"orchestration.atlan.com/default-{self._NAME}-{self._epoch}": "true", + "orchestration.atlan.com/atlan-ui": "true", + "orchestration.atlan.com/dependentPackage": "", + }, + annotations={ + "orchestration.atlan.com/allowSchedule": "true", + "orchestration.atlan.com/categories": "postgres,crawler", + "orchestration.atlan.com/dependentPackage": "", + "orchestration.atlan.com/docsUrl": "https://ask.atlan.com/hc/en-us/articles/6329557275793", + "orchestration.atlan.com/emoji": "\U0001f680", + "orchestration.atlan.com/icon": self._PACKAGE_ICON, + "orchestration.atlan.com/logo": self._PACKAGE_LOGO, + "orchestration.atlan.com/marketplaceLink": f"https://packages.atlan.com/-/web/detail/{self._PACKAGE_NAME}", # noqa + "orchestration.atlan.com/name": "Postgres Assets", + "package.argoproj.io/author": "Atlan", + "package.argoproj.io/description": "Package to crawl PostgreSQL assets and publish to Atlan for discovery", # noqa + "package.argoproj.io/homepage": f"https://packages.atlan.com/-/web/detail/{self._PACKAGE_NAME}", + "package.argoproj.io/keywords": '["postgres","database","sql","connector","crawler"]', # fmt: skip # noqa + "package.argoproj.io/name": self._PACKAGE_NAME, + "package.argoproj.io/registry": "https://packages.atlan.com", + "package.argoproj.io/repository": "https://github.com/atlanhq/marketplace-packages.git", + "package.argoproj.io/support": "support@atlan.com", + "orchestration.atlan.com/atlanName": f"{self._PACKAGE_PREFIX}-default-{self._NAME}-{self._epoch}", + }, + name=f"{self._PACKAGE_PREFIX}-{self._epoch}", + namespace="default", + ) diff --git a/pyatlan_v9/model/packages/powerbi_crawler.py b/pyatlan_v9/model/packages/powerbi_crawler.py new file mode 100644 index 000000000..224a16846 --- /dev/null +++ b/pyatlan_v9/model/packages/powerbi_crawler.py @@ -0,0 +1,230 @@ +from __future__ import annotations + +from typing import List, Optional + +from pyatlan.model.enums import AtlanConnectorType, WorkflowPackage +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.model.packages.base.crawler import AbstractCrawler +from pyatlan_v9.model.workflow import WorkflowMetadata + + +class PowerBICrawler(AbstractCrawler): + """ + Base configuration for a new PowerBI crawler. + + :param client: connectivity to an Atlan tenant + :param connection_name: name for the connection + :param admin_roles: admin roles for the connection + :param admin_groups: admin groups for the connection + :param admin_users: admin users for the connection + :param allow_query: allow data to be queried in the + connection (True) or not (False), default: False + :param allow_query_preview: allow sample data viewing for + assets in the connection (True) or not (False), default: False + :param row_limit: maximum number of rows + that can be returned by a query, default: 0 + """ + + _NAME = "powerbi" + _PACKAGE_NAME = "@atlan/powerbi" + _PACKAGE_PREFIX = WorkflowPackage.POWERBI.value + _CONNECTOR_TYPE = AtlanConnectorType.POWERBI + _PACKAGE_ICON = ( + "https://powerbi.microsoft.com/pictures/application-logos/svg/powerbi.svg" + ) + _PACKAGE_LOGO = ( + "https://powerbi.microsoft.com/pictures/application-logos/svg/powerbi.svg" + ) + + def __init__( + self, + client: AtlanClient, + connection_name: str, + admin_roles: Optional[List[str]] = None, + admin_groups: Optional[List[str]] = None, + admin_users: Optional[List[str]] = None, + allow_query: bool = False, + allow_query_preview: bool = False, + row_limit: int = 0, + ): + super().__init__( + client=client, + connection_name=connection_name, + connection_type=self._CONNECTOR_TYPE, + admin_roles=admin_roles, + admin_groups=admin_groups, + admin_users=admin_users, + allow_query=allow_query, + allow_query_preview=allow_query_preview, + row_limit=row_limit, + source_logo=self._PACKAGE_LOGO, + ) + + def direct(self) -> PowerBICrawler: + """ + Set up the crawler to extract directly from Power BI. + + :returns: rawler, set up to extract directly from Power BI + """ + local_creds = { + "name": f"default-{self._NAME}-{self._epoch}-0", + "host": "api.powerbi.com", + "port": 443, + "connector_config_name": f"atlan-connectors-{self._NAME}", + } + self._credentials_body.update(local_creds) + return self + + def delegated_user( + self, + username: str, + password: str, + tenant_id: str, + client_id: str, + client_secret: str, + ) -> PowerBICrawler: + """ + Set up the crawler to use delegated user authentication. + + :param username: through which to access Power BI + :param password: through which to access Power BI + :param tenant_id: unique ID (GUID) of the tenant for Power BI + :param client_id: unique ID (GUID) of the client for Power BI + :param client_secret: through which to access Power BI + :returns: crawler, set up to use delegated user authentication + """ + local_creds = { + "authType": "basic", + "username": username, + "password": password, + "extra": { + "tenantId": tenant_id, + "clientId": client_id, + "clientSecret": client_secret, + }, + } + self._credentials_body.update(local_creds) + return self + + def service_principal( + self, tenant_id: str, client_id: str, client_secret: str + ) -> PowerBICrawler: + """ + Set up the crawler to use service principal authentication. + + :param tenant_id: unique ID (GUID) of the tenant for Power BI + :param client_id: unique ID (GUID) of the client for Power BI + :param client_secret: through which to access Power BI + :returns: crawler, set up to use service principal authentication + """ + local_creds = { + "authType": "service_principal", + "connectorType": "rest", + "extra": { + "tenantId": tenant_id, + "clientId": client_id, + "clientSecret": client_secret, + }, + } + self._credentials_body.update(local_creds) + return self + + def include(self, workspaces: List[str]) -> PowerBICrawler: + """ + Defines the filter for workspaces to include when crawling. + + :param workspaces: the GUIDs of workspaces to include when crawling + :return: crawler, set to include only those workspaces specified + :raises InvalidRequestException: In the unlikely + event the provided filter cannot be translated + """ + include_workspaces = workspaces or [] + to_include = self.build_flat_filter(include_workspaces) + self._parameters.append( + dict(dict(name="include-filter", value=to_include or "{}")) + ) + return self + + def exclude(self, workspaces: List[str]) -> PowerBICrawler: + """ + Defines the filter for workspaces to exclude when crawling. + + :param workspaces: the GUIDs of workspaces to exclude when crawling + :return: crawler, set to exclude only those workspaces specified + :raises InvalidRequestException: In the unlikely + event the provided filter cannot be translated + """ + exclude_workspaces = workspaces or [] + to_exclude = self.build_flat_filter(exclude_workspaces) + self._parameters.append(dict(name="exclude-filter", value=to_exclude or "{}")) + return self + + def direct_endorsements(self, enabled: bool = True) -> PowerBICrawler: + """ + Whether to directly attach endorsements as + certificates (True), or instead raise these as requests + + :param enabled: if True, endorsements will be directly set as + certificates on assets, otherwise requests will be raised, default: True + :returns: crawler, set to directly (or not) set certificates on assets for endorsements + """ + self._parameters.append( + { + "name": "endorsement-attach-mode", + "value": "metastore" if enabled else "requests", + } + ) + return self + + def _set_required_metadata_params(self): + self._parameters.append( + {"name": "credential-guid", "value": "{{credentialGuid}}"} + ) + self._parameters.append( + { + "name": "connection", + "value": self._get_connection().to_json(), + } + ) + self._parameters.append(dict(name="atlas-auth-type", value="internal")) + self._parameters.append(dict(name="publish-mode", value="production")) + + def _get_metadata(self) -> WorkflowMetadata: + self._set_required_metadata_params() + return WorkflowMetadata( + labels={ + "orchestration.atlan.com/certified": "true", + "orchestration.atlan.com/source": self._NAME, + "orchestration.atlan.com/sourceCategory": "bi", + "orchestration.atlan.com/type": "connector", + "orchestration.atlan.com/verified": "true", + "package.argoproj.io/installer": "argopm", + "package.argoproj.io/name": f"a-t-ratlans-l-a-s-h{self._NAME}", + "package.argoproj.io/registry": "httpsc-o-l-o-ns-l-a-s-hs-l-a-s-hpackages.atlan.com", + f"orchestration.atlan.com/default-{self._NAME}-{self._epoch}": "true", + "orchestration.atlan.com/atlan-ui": "true", + }, + annotations={ + "orchestration.atlan.com/allowSchedule": "true", + "orchestration.atlan.com/categories": "powerbi,crawler", + "orchestration.atlan.com/dependentPackage": "", + "orchestration.atlan.com/docsUrl": "https://ask.atlan.com/hc/en-us/articles/6332245668881", + "orchestration.atlan.com/emoji": "\U0001f680", + "orchestration.atlan.com/icon": self._PACKAGE_ICON, + "orchestration.atlan.com/logo": self._PACKAGE_LOGO, # noqa + "orchestration.atlan.com/marketplaceLink": f"https://packages.atlan.com/-/web/detail/{self._PACKAGE_NAME}", # noqa + "orchestration.atlan.com/name": f"{self._NAME} Assets", + "package.argoproj.io/author": "Atlan", + "package.argoproj.io/description": "Package to crawl PowerBI assets and publish to Atlan for discovery.", # noqa + "package.argoproj.io/homepage": f"https://packages.atlan.com/-/web/detail/{self._PACKAGE_NAME}", + "package.argoproj.io/keywords": '["powerbi","bi","connector","crawler"]', # fmt: skip + "package.argoproj.io/name": self._PACKAGE_NAME, + "package.argoproj.io/parent": ".", + "package.argoproj.io/registry": "https://packages.atlan.com", + "package.argoproj.io/repository": "git+https://github.com/atlanhq/marketplace-packages.git", + "package.argoproj.io/support": "support@atlan.com", + "orchestration.atlan.com/atlanName": f"{self._PACKAGE_PREFIX}-default-{self._NAME}-{self._epoch}", + }, + name=f"{self._PACKAGE_PREFIX}-{self._epoch}", + namespace="default", + ) diff --git a/pyatlan_v9/model/packages/relational_assets_builder.py b/pyatlan_v9/model/packages/relational_assets_builder.py new file mode 100644 index 000000000..defcde908 --- /dev/null +++ b/pyatlan_v9/model/packages/relational_assets_builder.py @@ -0,0 +1,254 @@ +from __future__ import annotations + +from json import dumps +from typing import List, Optional, Union + +from pyatlan.model.enums import ( + AssetDeltaHandling, + AssetInputHandling, + AssetRemovalType, + WorkflowPackage, +) +from pyatlan.model.fields.atlan_fields import AtlanField +from pyatlan_v9.model.packages.base.custom_package import AbstractCustomPackage +from pyatlan_v9.model.workflow import WorkflowMetadata + + +class RelationalAssetsBuilder(AbstractCustomPackage): + """ + Base configuration for the Relational Assets Builder package. + """ + + _NAME = "relational-assets-builder" + _PACKAGE_NAME = f"@csa/{_NAME}" + _PACKAGE_PREFIX = WorkflowPackage.RELATIONAL_ASSETS_BUILDER.value + _PACKAGE_ICON = "http://assets.atlan.com/assets/ph-database-light.svg" + _PACKAGE_LOGO = "http://assets.atlan.com/assets/ph-database-light.svg" + + def __init__( + self, + ): + super().__init__() + + def direct(self) -> RelationalAssetsBuilder: + """ + Set up package to directly upload the file. + """ + self._parameters.append({"name": "import_type", "value": "DIRECT"}) + return self + + def object_store( + self, prefix: Optional[str] = None, object_key: Optional[str] = None + ) -> RelationalAssetsBuilder: + """ + Set up the package to import + metadata directly from the object store. + + :param prefix: directory (path) within the bucket/container from which to retrieve the object(s). + :param object_key: object key (filename), including its extension, within the bucket/container and + prefix. + + :returns: package, set up to import metadata from object store + """ + self._parameters.append({"name": "import_type", "value": "CLOUD"}) + self._parameters.append({"name": "assets_prefix", "value": prefix}) + self._parameters.append({"name": "assets_key", "value": object_key}) + self._parameters.append({"name": "cloud_source", "value": "{{credentialGuid}}"}) + return self + + def s3( + self, + access_key: str, + secret_key: str, + region: str, + bucket: str, + ) -> RelationalAssetsBuilder: + """ + Set up package to import metadata from S3. + + :param access_key: AWS access key + :param secret_key: AWS secret key + :param region: AWS region + :param bucket: Enter the bucket from which to retrieve the object store object(s). + + :returns: package, set up to import metadata from S3 + """ + local_creds = { + "name": f"csa-{self._NAME}-{self._epoch}-0", + "auth_type": "s3", + "username": access_key, + "password": secret_key, + "extra": { + "region": region, + "s3_bucket": bucket, + }, + "connector_config_name": "csa-connectors-objectstore", + } + self._credentials_body.update(local_creds) + return self + + def gcs( + self, project_id: str, service_account_json: str, bucket: str + ) -> RelationalAssetsBuilder: + """ + Set up package to import metadata from GCS. + + :param project_id: ID of GCP project + :param service_account_json: service account credentials in JSON format + :param bucket: the bucket from which to retrieve the object store object(s) + + :returns: Package set up to import metadata from GCS + """ + local_creds = { + "name": f"csa-{self._NAME}-{self._epoch}-0", + "auth_type": "gcs", + "username": project_id, + "password": service_account_json, + "extra": { + "gcs_bucket": bucket, + }, + "connector_config_name": "csa-connectors-objectstore", + } + self._credentials_body.update(local_creds) + return self + + def adls( + self, + client_id: str, + client_secret: str, + tenant_id: str, + account_name: str, + container: str, + ) -> RelationalAssetsBuilder: + """ + Set up package to import metadata from ADLS. + + :param client_id: unique application (client) ID assigned by Azure AD when the app was registered + :param client_secret: client secret for authentication + :param tenant_id: unique ID of the Azure Active Directory instance + :param account_name: name of the storage account + :param container: container to retrieve object store objects from + + :returns: package, set up to import metadata from ADLS + """ + local_creds = { + "name": f"csa-{self._NAME}-{self._epoch}-0", + "auth_type": "adls", + "username": client_id, + "password": client_secret, + "extra": { + "azure_tenant_id": tenant_id, + "storage_account_name": account_name, + "adls_container": container, + }, + "connector_config_name": "csa-connectors-objectstore", + } + self._credentials_body.update(local_creds) + return self + + def assets_semantics( + self, + input_handling: AssetInputHandling = AssetInputHandling.UPSERT, + delta_handling: AssetDeltaHandling = AssetDeltaHandling.INCREMENTAL, + removal_type: AssetRemovalType = AssetRemovalType.ARCHIVE, + ) -> RelationalAssetsBuilder: + """ + Set up the package to import metadata with semantics. + + :param input_handling: Whether to allow the creation of new (full or partial) assets from the input CSV, + or ensure assets are only updated if they already exist in Atlan. + :param delta_handling: Whether to treat the input file as an initial load, full replacement (deleting any + existing assets not in the file) or only incremental (no deletion of existing assets). + :param removal_type: If `delta_handling` is set to `FULL_REPLACEMENT`, this parameter specifies whether to + delete any assets not found in the latest file by archive (recoverable) or purge (non-recoverable). + If `delta_handling` is set to `INCREMENTAL`, this parameter is ignored and assets are archived. + + :returns: package, set up to import metadata with semantics + """ + self._parameters.append( + {"name": "assets_upsert_semantic", "value": input_handling} + ) + self._parameters.append({"name": "delta_semantic", "value": delta_handling}) + if delta_handling == AssetDeltaHandling.FULL_REPLACEMENT: + self._parameters.append( + {"name": "delta_removal_type", "value": removal_type} + ) + else: + self._parameters.append( + {"name": "delta_removal_type", "value": AssetRemovalType.ARCHIVE} + ) + return self + + def options( + self, + remove_attributes: Optional[Union[List[str], List[AtlanField]]] = None, + fail_on_errors: Optional[bool] = None, + field_separator: Optional[str] = None, + batch_size: Optional[int] = None, + ) -> RelationalAssetsBuilder: + """ + Set up package to import assets with advanced configuration. + + :param remove_attributes: list of attributes to clear (remove) + from assets if their value is blank in the provided file. + :param fail_on_errors: specifies whether an invalid value + in a field should cause the import to fail (`True`) or + log a warning, skip that value, and proceed (`False`). + :param field_separator: character used to separate + fields in the input file (e.g., ',' or ';'). + :param batch_size: maximum number of rows + to process at a time (per API request). + + :returns: package, set up to import assets with advanced configuration + """ + + if isinstance(remove_attributes, list) and all( + isinstance(field, AtlanField) for field in remove_attributes + ): + remove_attributes = [field.atlan_field_name for field in remove_attributes] # type: ignore + params = { + "assets_attr_to_overwrite": dumps(remove_attributes, separators=(",", ":")), + "assets_fail_on_errors": fail_on_errors, + "assets_field_separator": field_separator, + "assets_batch_size": batch_size, + } + self._add_optional_params(params) + return self + + def _get_metadata(self) -> WorkflowMetadata: + return WorkflowMetadata( + labels={ + "orchestration.atlan.com/certified": "true", + "orchestration.atlan.com/source": self._NAME, + "orchestration.atlan.com/sourceCategory": "utility", + "orchestration.atlan.com/type": "custom", + "orchestration.atlan.com/preview": "true", + "orchestration.atlan.com/verified": "true", + "package.argoproj.io/installer": "argopm", + "package.argoproj.io/name": f"a-t-rcsas-l-a-s-h{self._NAME}", + "package.argoproj.io/registry": "httpsc-o-l-o-ns-l-a-s-hs-l-a-s-hpackages.atlan.com", + "orchestration.atlan.com/atlan-ui": "true", + }, + annotations={ + "orchestration.atlan.com/allowSchedule": "true", + "orchestration.atlan.com/categories": "kotlin,utility", + "orchestration.atlan.com/dependentPackage": "", + "orchestration.atlan.com/docsUrl": f"https://solutions.atlan.com/{self._NAME}/", + "orchestration.atlan.com/emoji": "\U0001f680", + "orchestration.atlan.com/icon": self._PACKAGE_ICON, + "orchestration.atlan.com/logo": self._PACKAGE_LOGO, # noqa + "orchestration.atlan.com/name": "Relational Assets Builder", + "package.argoproj.io/author": "Atlan CSA", + "package.argoproj.io/description": "Build (and update) relational assets managed through a CSV file.", + "package.argoproj.io/homepage": f"https://packages.atlan.com/-/web/detail/{self._PACKAGE_NAME}", + "package.argoproj.io/keywords": '["kotlin","utility"]', # fmt: skip + "package.argoproj.io/name": self._PACKAGE_NAME, + "package.argoproj.io/parent": ".", + "package.argoproj.io/registry": "https://packages.atlan.com", + "package.argoproj.io/repository": "git+https://github.com/atlanhq/marketplace-packages.git", + "package.argoproj.io/support": "support@atlan.com", + "orchestration.atlan.com/atlanName": f"csa-{self._NAME}-{self._epoch}", + }, + name=f"csa-{self._NAME}-{self._epoch}", + namespace="default", + ) diff --git a/pyatlan_v9/model/packages/s_q_l_server_crawler.py b/pyatlan_v9/model/packages/s_q_l_server_crawler.py new file mode 100644 index 000000000..6057eb09f --- /dev/null +++ b/pyatlan_v9/model/packages/s_q_l_server_crawler.py @@ -0,0 +1,181 @@ +from __future__ import annotations + +from typing import List, Optional + +from pyatlan.model.enums import AtlanConnectorType, WorkflowPackage +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.model.packages.base.crawler import AbstractCrawler +from pyatlan_v9.model.workflow import WorkflowMetadata + + +class SQLServerCrawler(AbstractCrawler): + """ + Base configuration for a new Microsoft SQL Server crawler. + + :param connection_name: name for the connection + :param admin_roles: admin roles for the connection + :param admin_groups: admin groups for the connection + :param admin_users: admin users for the connection + :param allow_query: allow data to be queried in the + connection (True) or not (False), default: True + :param allow_query_preview: allow sample data viewing for + assets in the connection (True) or not (False), default: True + :param row_limit: maximum number of rows + that can be returned by a query, default: 10000 + """ + + _NAME = "mssql" + _PACKAGE_NAME = "@atlan/mssql" + _PACKAGE_PREFIX = WorkflowPackage.MSSQL.value + _CONNECTOR_TYPE = AtlanConnectorType.MSSQL + _PACKAGE_ICON = "https://user-images.githubusercontent.com/4249331/52232852-e2c4f780-28bd-11e9-835d-1e3cf3e43888.png" # noqa + _PACKAGE_LOGO = "https://user-images.githubusercontent.com/4249331/52232852-e2c4f780-28bd-11e9-835d-1e3cf3e43888.png" # noqa + + def __init__( + self, + client: AtlanClient, + connection_name: str, + admin_roles: Optional[List[str]] = None, + admin_groups: Optional[List[str]] = None, + admin_users: Optional[List[str]] = None, + allow_query: bool = True, + allow_query_preview: bool = True, + row_limit: int = 10000, + ): + super().__init__( + client=client, + connection_name=connection_name, + connection_type=self._CONNECTOR_TYPE, + admin_roles=admin_roles, + admin_groups=admin_groups, + admin_users=admin_users, + allow_query=allow_query, + allow_query_preview=allow_query_preview, + row_limit=row_limit, + source_logo=self._PACKAGE_LOGO, + ) + + def direct( + self, hostname: str, database: str, port: int = 1433 + ) -> SQLServerCrawler: + """ + Set up the crawler to extract directly from the database. + + :param hostname: hostname of the SQL Server host + :param database: name of the database to extract + :param port: port number of the SQL Server host, default: `1433` + :returns: crawler, set up to extract directly from the database + """ + local_creds = { + "name": f"default-{self._NAME}-{self._epoch}-0", + "host": hostname, + "port": port, + "extra": {"database": database}, + "connector_config_name": f"atlan-connectors-{self._NAME}", + } + self._credentials_body.update(local_creds) + return self + + def basic_auth(self, username: str, password: str) -> SQLServerCrawler: + """ + Set up the crawler to use basic authentication. + + :param username: through which to access SQL Server + :param password: through which to access SQL Server + :returns: crawler, set up to use basic authentication + """ + local_creds = { + "authType": "basic", + "username": username, + "password": password, + } + self._credentials_body.update(local_creds) + return self + + def include(self, assets: dict) -> SQLServerCrawler: + """ + Defines the filter for assets to include when crawling. + + :param assets: map keyed by database name + with each value being a list of schemas + :returns: crawler, set to include only those assets specified + :raises InvalidRequestException: In the unlikely + event the provided filter cannot be translated + """ + include_assets = assets or {} + to_include = self.build_hierarchical_filter(include_assets) + self._parameters.append(dict(name="include-filter", value=to_include or "{}")) + return self + + def exclude(self, assets: dict) -> SQLServerCrawler: + """ + Defines the filter for assets to exclude when crawling. + + :param assets: map keyed by database name + with each value being a list of schemas + :returns: crawler, set to exclude only those assets specified + :raises InvalidRequestException: In the unlikely + event the provided filter cannot be translated + """ + exclude_assets = assets or {} + to_exclude = self.build_hierarchical_filter(exclude_assets) + self._parameters.append(dict(name="exclude-filter", value=to_exclude or "{}")) + return self + + def _set_required_metadata_params(self): + self._parameters.append( + {"name": "credential-guid", "value": "{{credentialGuid}}"} + ) + self._parameters.append(dict(name="publish-mode", value="production")) + self._parameters.append(dict(name="extraction-method", value="direct")) + self._parameters.append(dict(name="atlas-auth-type", value="internal")) + self._parameters.append(dict(name="use-jdbc-internal-methods", value="true")) + self._parameters.append(dict(name="use-source-schema-filtering", value="false")) + self._parameters.append( + dict(name="credentials-fetch-strategy", value="credential_guid") + ) + self._parameters.append( + { + "name": "connection", + "value": self._get_connection().to_json(), + } + ) + + def _get_metadata(self) -> WorkflowMetadata: + self._set_required_metadata_params() + return WorkflowMetadata( + labels={ + "orchestration.atlan.com/certified": "true", + "orchestration.atlan.com/source": self._NAME, + "orchestration.atlan.com/sourceCategory": "warehouse", + "orchestration.atlan.com/type": "connector", + "orchestration.atlan.com/verified": "true", + "package.argoproj.io/installer": "argopm", + "package.argoproj.io/name": f"a-t-ratlans-l-a-s-h{self._NAME}", + "package.argoproj.io/registry": "httpsc-o-l-o-ns-l-a-s-hs-l-a-s-hpackages.atlan.com", + f"orchestration.atlan.com/default-{self._NAME}-{self._epoch}": "true", + "orchestration.atlan.com/atlan-ui": "true", + }, + annotations={ + "orchestration.atlan.com/allowSchedule": "true", + "orchestration.atlan.com/categories": "mssql,crawler", + "orchestration.atlan.com/dependentPackage": "", + "orchestration.atlan.com/docsUrl": "https://ask.atlan.com/hc/en-us/articles/6167939436945-How-to-crawl-Microsoft-SQL-Server", # noqa + "orchestration.atlan.com/emoji": "\U0001f680", + "orchestration.atlan.com/icon": self._PACKAGE_ICON, + "orchestration.atlan.com/logo": self._PACKAGE_LOGO, + "orchestration.atlan.com/marketplaceLink": f"https://packages.atlan.com/-/web/detail/{self._PACKAGE_NAME}", # noqa + "orchestration.atlan.com/name": "SQL Server Assets", + "package.argoproj.io/author": "Atlan", + "package.argoproj.io/description": f"Package to crawl Microsoft SQL Server assets and publish to Atlan for discovery", # noqa + "package.argoproj.io/homepage": f"https://packages.atlan.com/-/web/detail/{self._PACKAGE_NAME}", + "package.argoproj.io/keywords": '["mssql","database","sql","connector","crawler"]', # fmt: skip # noqa + "package.argoproj.io/name": self._PACKAGE_NAME, + "package.argoproj.io/registry": "https://packages.atlan.com", + "package.argoproj.io/repository": "git+https://github.com/atlanhq/marketplace-packages.git", + "package.argoproj.io/support": "support@atlan.com", + "orchestration.atlan.com/atlanName": f"{self._PACKAGE_PREFIX}-default-{self._NAME}-{self._epoch}", + }, + name=f"{self._PACKAGE_PREFIX}-{self._epoch}", + namespace="default", + ) diff --git a/pyatlan_v9/model/packages/sigma_crawler.py b/pyatlan_v9/model/packages/sigma_crawler.py new file mode 100644 index 000000000..06cd5469c --- /dev/null +++ b/pyatlan_v9/model/packages/sigma_crawler.py @@ -0,0 +1,188 @@ +from __future__ import annotations + +from enum import Enum +from typing import List, Optional + +from pyatlan.model.enums import AtlanConnectorType, WorkflowPackage +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.model.packages.base.crawler import AbstractCrawler +from pyatlan_v9.model.workflow import WorkflowMetadata + + +class SigmaCrawler(AbstractCrawler): + """ + Base configuration for a new Sigma crawler. + + :param client: connectivity to an Atlan tenant + :param connection_name: name for the connection + :param admin_roles: admin roles for the connection + :param admin_groups: admin groups for the connection + :param admin_users: admin users for the connection + :param allow_query: allow data to be queried in the + connection (True) or not (False), default: False + :param allow_query_preview: allow sample data viewing for + assets in the connection (True) or not (False), default: False + :param row_limit: maximum number of rows + that can be returned by a query, default: 0 + """ + + _NAME = "sigma" + _PACKAGE_NAME = "@atlan/sigma" + _PACKAGE_PREFIX = WorkflowPackage.SIGMA.value + _CONNECTOR_TYPE = AtlanConnectorType.SIGMA + _PACKAGE_ICON = "http://assets.atlan.com/assets/sigma.svg" + _PACKAGE_LOGO = "http://assets.atlan.com/assets/sigma.svg" + + class Hostname(str, Enum): + GCP = "api.sigmacomputing.com" + AZURE = "api.us.azure.sigmacomputing.com" + AWS = "aws-api.sigmacomputing.com" + AWS_CANADA = "api.ca.aws.sigmacomputing.com" + AWS_EUROPE = "api.eu.aws.sigmacomputing.com" + + def __init__( + self, + client: AtlanClient, + connection_name: str, + admin_roles: Optional[List[str]] = None, + admin_groups: Optional[List[str]] = None, + admin_users: Optional[List[str]] = None, + allow_query: bool = False, + allow_query_preview: bool = False, + row_limit: int = 0, + ): + super().__init__( + client=client, + connection_name=connection_name, + connection_type=self._CONNECTOR_TYPE, + admin_roles=admin_roles, + admin_groups=admin_groups, + admin_users=admin_users, + allow_query=allow_query, + allow_query_preview=allow_query_preview, + row_limit=row_limit, + source_logo=self._PACKAGE_LOGO, + ) + + def direct(self, hostname: SigmaCrawler.Hostname, port: int = 443) -> SigmaCrawler: + """ + Set up the crawler to extract directly from Sigma. + + :param hostname: of the Sigma host, for example `SigmaCrawler.Hostname.AWS` + :param port: of the Sigma host, default: `443` + :returns: crawler, set up to extract directly from Sigma + """ + local_creds = { + "name": f"default-{self._NAME}-{self._epoch}-0", + "host": hostname, + "port": port, + "extra": {}, + "connector_config_name": f"atlan-connectors-{self._NAME}", + } + self._credentials_body.update(local_creds) + return self + + def api_token( + self, + client_id: str, + api_token: str, + ) -> SigmaCrawler: + """ + Set up the crawler to use API token-based authentication. + + :param client_id: through which to access Sigma + :param api_token: through which to access Sigma + :returns: crawler, set up to use API token-based authentication + """ + local_creds = { + "username": client_id, + "password": api_token, + "auth_type": "api_token", + } + self._credentials_body.update(local_creds) + return self + + def include(self, workbooks: List[str]) -> SigmaCrawler: + """ + Defines the filter for Sigma workbooks to include when crawling. + + :param workbooks: the GUIDs of workbooks to include when crawling, + default to no workbooks if `None` are specified + :returns: crawler, set to include only those workbooks specified + :raises InvalidRequestException: In the unlikely + event the provided filter cannot be translated + """ + include_workbooks = workbooks or [] + to_include = self.build_flat_filter(include_workbooks) + self._parameters.append( + dict(dict(name="include-filter", value=to_include or "{}")) + ) + return self + + def exclude(self, workbooks: List[str]) -> SigmaCrawler: + """ + Defines the filter for Sigma workbooks to exclude when crawling. + + :param workbooks: the GUIDs of workbooks to exclude when crawling, + default to no workbooks if `None` are specified + :returns: crawler, set to exclude only those workbooks specified + :raises InvalidRequestException: In the unlikely + event the provided filter cannot be translated + """ + exclude_workbooks = workbooks or [] + to_exclude = self.build_flat_filter(exclude_workbooks) + self._parameters.append(dict(name="exclude-filter", value=to_exclude or "{}")) + return self + + def _set_required_metadata_params(self): + self._parameters.append( + {"name": "credential-guid", "value": "{{credentialGuid}}"} + ) + self._parameters.append( + { + "name": "connection", + "value": self._get_connection().to_json(), + } + ) + self._parameters.append(dict(name="publish-mode", value="production")) + self._parameters.append(dict(name="atlas-auth-type", value="internal")) + + def _get_metadata(self) -> WorkflowMetadata: + self._set_required_metadata_params() + return WorkflowMetadata( + labels={ + "orchestration.atlan.com/certified": "true", + "orchestration.atlan.com/source": self._NAME, + "orchestration.atlan.com/sourceCategory": "bi", + "orchestration.atlan.com/type": "connector", + "orchestration.atlan.com/verified": "true", + "package.argoproj.io/installer": "argopm", + "package.argoproj.io/name": f"a-t-ratlans-l-a-s-h{self._NAME}", + "package.argoproj.io/registry": "httpsc-o-l-o-ns-l-a-s-hs-l-a-s-hpackages.atlan.com", + f"orchestration.atlan.com/default-{self._NAME}-{self._epoch}": "true", + "orchestration.atlan.com/atlan-ui": "true", + }, + annotations={ + "orchestration.atlan.com/allowSchedule": "true", + "orchestration.atlan.com/dependentPackage": "", + "orchestration.atlan.com/docsUrl": "https://ask.atlan.com/hc/en-us/articles/8731744918813", + "orchestration.atlan.com/emoji": "\U0001f680", + "orchestration.atlan.com/icon": self._PACKAGE_ICON, + "orchestration.atlan.com/logo": self._PACKAGE_LOGO, + "orchestration.atlan.com/marketplaceLink": f"https://packages.atlan.com/-/web/detail/{self._PACKAGE_NAME}", # noqa + "orchestration.atlan.com/name": "Sigma Assets", + "orchestration.atlan.com/categories": "sigma,crawler", + "package.argoproj.io/author": "Atlan", + "package.argoproj.io/description": "Package to crawl Sigma assets and publish to Atlan for discovery", + "package.argoproj.io/homepage": "", + "package.argoproj.io/keywords": '["sigma","bi","connector","crawler"]', # fmt: skip + "package.argoproj.io/name": self._PACKAGE_NAME, + "package.argoproj.io/parent": ".", + "package.argoproj.io/registry": "https://packages.atlan.com", + "package.argoproj.io/repository": "git+https://github.com/atlanhq/marketplace-packages.git", + "package.argoproj.io/support": "support@atlan.com", + "orchestration.atlan.com/atlanName": f"{self._PACKAGE_PREFIX}-default-{self._NAME}-{self._epoch}", + }, + name=f"{self._PACKAGE_PREFIX}-{self._epoch}", + namespace="default", + ) diff --git a/pyatlan_v9/model/packages/snowflake_crawler.py b/pyatlan_v9/model/packages/snowflake_crawler.py new file mode 100644 index 000000000..a9d7c5c35 --- /dev/null +++ b/pyatlan_v9/model/packages/snowflake_crawler.py @@ -0,0 +1,260 @@ +from __future__ import annotations + +from typing import List, Optional + +from pyatlan.model.enums import AtlanConnectorType, WorkflowPackage +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.model.packages.base.crawler import AbstractCrawler +from pyatlan_v9.model.workflow import WorkflowMetadata + + +class SnowflakeCrawler(AbstractCrawler): + """ + Base configuration for a new Snowflake crawler. + + :param client: connectivity to an Atlan tenant + :param connection_name: name for the connection + :param admin_roles: admin roles for the connection + :param admin_groups: admin groups for the connection + :param admin_users: admin users for the connection + :param allow_query: allow data to be queried in the + connection (True) or not (False), default: True + :param allow_query_preview: allow sample data viewing for + assets in the connection (True) or not (False), default: True + :param row_limit: maximum number of rows + that can be returned by a query, default: 10000 + """ + + _NAME = "snowflake" + _PACKAGE_NAME = "@atlan/snowflake" + _PACKAGE_PREFIX = WorkflowPackage.SNOWFLAKE.value + _CONNECTOR_TYPE = AtlanConnectorType.SNOWFLAKE + _PACKAGE_ICON = "https://docs.snowflake.com/en/_images/logo-snowflake-sans-text.png" + _PACKAGE_LOGO = "https://1amiydhcmj36tz3733v94f15-wpengine.netdna-ssl.com/wp-content/themes/snowflake/assets/img/logo-blue.svg" # noqa + + def __init__( + self, + client: AtlanClient, + connection_name: str, + admin_roles: Optional[List[str]] = None, + admin_groups: Optional[List[str]] = None, + admin_users: Optional[List[str]] = None, + allow_query: bool = True, + allow_query_preview: bool = True, + row_limit: int = 10000, + ): + super().__init__( + client=client, + connection_name=connection_name, + connection_type=self._CONNECTOR_TYPE, + admin_roles=admin_roles, + admin_groups=admin_groups, + admin_users=admin_users, + allow_query=allow_query, + allow_query_preview=allow_query_preview, + row_limit=row_limit, + source_logo=self._PACKAGE_LOGO, + ) + + def basic_auth( + self, username: str, password: str, role: str, warehouse: str + ) -> SnowflakeCrawler: + """ + Set up the crawler to use basic authentication. + + :param username: through which to access Snowflake + :param password: through which to access Snowflake + :param role: name of the role within Snowflake to crawl through + :param warehouse: name of the warehouse within Snowflake to crawl through + :returns: crawler, set up to use basic authentication + """ + local_creds = { + "name": f"default-snowflake-{self._epoch}-0", + "port": 443, + "auth_type": "basic", + "username": username, + "password": password, + "extras": {"role": role, "warehouse": warehouse}, + } + self._credentials_body.update(local_creds) + return self + + def keypair_auth( + self, + username: str, + private_key: str, + private_key_password: str, + role: str, + warehouse: str, + ) -> SnowflakeCrawler: + """ + Set up the crawler to use keypair-based authentication. + + :param username: through which to access Snowflake + :param private_key: encrypted private key for authenticating with Snowflake + :param private_key_password: password for the encrypted private key + :param role: name of the role within Snowflake to crawl through + :param warehouse: name of the warehouse within Snowflake to crawl through + :returns: crawler, set up to use keypair-based authentication + """ + local_creds = { + "name": f"default-snowflake-{self._epoch}-0", + "port": 443, + "auth_type": "keypair", + "username": username, + "password": private_key, + "extras": { + "role": role, + "warehouse": warehouse, + "private_key_password": private_key_password, + }, + } + self._credentials_body.update(local_creds) + return self + + def information_schema(self, hostname: str) -> SnowflakeCrawler: + """ + Set the crawler to extract using Snowflake's information schema. + + :param hostname: hostname of the Snowflake instance + :returns: crawler, set to extract using information schema + """ + local_creds = { + "host": hostname, + "name": f"default-snowflake-{self._epoch}-0", + "connector_config_name": "atlan-connectors-snowflake", + } + parameters = {"name": "extract-strategy", "value": "information-schema"} + self._credentials_body.update(local_creds) + self._parameters.append(parameters) + return self + + def account_usage( + self, hostname: str, database_name: str, schema_name: str + ) -> SnowflakeCrawler: + """ + Set the crawler to extract using Snowflake's account usage database and schema. + + :param hostname: hostname of the Snowflake instance + :param database_name: name of the database to use + :param schema_name: name of the schema to use + :returns: crawler, set to extract using account usage + """ + local_creds = { + "host": hostname, + "name": f"default-snowflake-{self._epoch}-0", + "connector_config_name": "atlan-connectors-snowflake", + } + self._credentials_body.update(local_creds) + self._parameters.append( + {"name": "account-usage-database-name", "value": database_name} + ) + self._parameters.append( + {"name": "account-usage-schema-name", "value": schema_name} + ) + return self + + def lineage(self, include: bool = True) -> SnowflakeCrawler: + """ + Whether to enable lineage as part of crawling Snowflake. + + :param include: if True, lineage will be included while crawling Snowflake, default: True + :returns: crawler, set to include or exclude lineage + """ + self._parameters.append( + {"name": "enable-lineage", "value": "true" if include else "false"} + ) + return self + + def tags(self, include: bool = False) -> SnowflakeCrawler: + """ + Whether to enable Snowflake tag syncing as part of crawling Snowflake. + + :param include: Whether true, tags in Snowflake will be included while crawling Snowflake + :returns: crawler, set to include or exclude Snowflake tags + """ + self._parameters.append( + {"name": "enable-snowflake-tag", "value": "true" if include else "false"} + ) + return self + + def include(self, assets: dict) -> SnowflakeCrawler: + """ + Defines the filter for assets to include when crawling. + + :param assets: Map keyed by database name with each value being a list of schemas + :returns: crawler, set to include only those assets specified + :raises InvalidRequestException: In the unlikely + event the provided filter cannot be translated + """ + include_assets = assets or {} + to_include = self.build_hierarchical_filter(include_assets) + self._parameters.append( + dict(dict(name="include-filter", value=to_include or "{}")) + ) + return self + + def exclude(self, assets: dict) -> SnowflakeCrawler: + """ + Defines the filter for assets to exclude when crawling. + + :param assets: Map keyed by database name with each value being a list of schemas + :returns: crawler, set to exclude only those assets specified + :raises InvalidRequestException: In the unlikely + event the provided filter cannot be translated + """ + exclude_assets = assets or {} + to_exclude = self.build_hierarchical_filter(exclude_assets) + self._parameters.append(dict(name="exclude-filter", value=to_exclude or "{}")) + return self + + def _set_required_metadata_params(self): + self._parameters.append( + {"name": "credential-guid", "value": "{{credentialGuid}}"} + ) + self._parameters.append(dict(name="control-config-strategy", value="default")) + self._parameters.append( + { + "name": "connection", + "value": self._get_connection().to_json(), + } + ) + + def _get_metadata(self) -> WorkflowMetadata: + self._set_required_metadata_params() + return WorkflowMetadata( + labels={ + "orchestration.atlan.com/certified": "true", + "orchestration.atlan.com/source": self._NAME, + "orchestration.atlan.com/sourceCategory": "warehouse", + "orchestration.atlan.com/type": "connector", + "orchestration.atlan.com/verified": "true", + "package.argoproj.io/installer": "argopm", + "package.argoproj.io/name": f"a-t-ratlans-l-a-s-h{self._NAME}", + "package.argoproj.io/registry": "httpsc-o-l-o-ns-l-a-s-hs-l-a-s-hpackages.atlan.com", + f"orchestration.atlan.com/default-{self._NAME}-{self._epoch}": "true", + "orchestration.atlan.com/atlan-ui": "true", + }, + annotations={ + "orchestration.atlan.com/allowSchedule": "true", + "orchestration.atlan.com/categories": "warehouse,crawler", + "orchestration.atlan.com/dependentPackage": "", + "orchestration.atlan.com/docsUrl": "https://ask.atlan.com/hc/en-us/articles/6037440864145", + "orchestration.atlan.com/emoji": "\U0001f680", + "orchestration.atlan.com/icon": self._PACKAGE_ICON, + "orchestration.atlan.com/logo": self._PACKAGE_LOGO, + "orchestration.atlan.com/marketplaceLink": f"https://packages.atlan.com/-/web/detail/{self._PACKAGE_NAME}", # noqa + "orchestration.atlan.com/name": f"{self._NAME.capitalize()} Assets", + "package.argoproj.io/author": "Atlan", + "package.argoproj.io/description": f"Package to crawl {self._NAME.capitalize()} assets and publish to Atlan for discovery", # noqa + "package.argoproj.io/homepage": f"https://packages.atlan.com/-/web/detail/{self._PACKAGE_NAME}", + "package.argoproj.io/keywords": '["snowflake","warehouse","connector","crawler"]', # fmt: skip + "package.argoproj.io/name": self._PACKAGE_NAME, + "package.argoproj.io/registry": "https://packages.atlan.com", + "package.argoproj.io/repository": "git+https://github.com/atlanhq/marketplace-packages.git", + "package.argoproj.io/support": "support@atlan.com", + "orchestration.atlan.com/atlanName": f"{self._PACKAGE_PREFIX}-default-{self._NAME}-{self._epoch}", + }, + name=f"{self._PACKAGE_PREFIX}-{self._epoch}", + namespace="default", + ) diff --git a/pyatlan_v9/model/packages/snowflake_miner.py b/pyatlan_v9/model/packages/snowflake_miner.py new file mode 100644 index 000000000..aa8105201 --- /dev/null +++ b/pyatlan_v9/model/packages/snowflake_miner.py @@ -0,0 +1,201 @@ +from __future__ import annotations + +from json import dumps +from typing import Dict, List, Optional + +from pyatlan.model.enums import WorkflowPackage +from pyatlan_v9.model.packages.base.miner import AbstractMiner +from pyatlan_v9.model.workflow import WorkflowMetadata + + +class SnowflakeMiner(AbstractMiner): + """ + Base configuration for a new Snowflake miner. + + :param connection_qualified_name: unique name of the + Snowflake connection whose assets should be mined + """ + + _NAME = "snowflake" + _PACKAGE_NAME = "@atlan/snowflake-miner" + _PACKAGE_PREFIX = WorkflowPackage.SNOWFLAKE_MINER.value + _PACKAGE_ICON = "https://docs.snowflake.com/en/_images/logo-snowflake-sans-text.png" + _PACKAGE_LOGO = "https://1amiydhcmj36tz3733v94f15-wpengine.netdna-ssl.com/wp-content/themes/snowflake/assets/img/logo-blue.svg" # noqa + + def __init__( + self, + connection_qualified_name: str, + ): + self._advanced_config = False + super().__init__(connection_qualified_name=connection_qualified_name) + + def direct( + self, + start_epoch: int, + database: Optional[str] = None, + schema: Optional[str] = None, + ) -> SnowflakeMiner: + """ + Set up the miner to extract directly from Snowflake. + + :param start_epoch: date and time from which to start mining, as an epoch + :param database: name of the database to extract from (cloned database) + :param schema: name of the schema to extract from (cloned database) + :returns: miner, set up to extract directly from Snowflake + """ + # In case of default database + if not (database or schema): + self._parameters.append(dict(name="snowflake-database", value="default")) + # In case of cloned database + else: + self._parameters.append(dict(name="database-name", value=database)) + self._parameters.append(dict(name="schema-name", value=schema)) + self._parameters.append(dict(name="extraction-method", value="query_history")) + self._parameters.append( + dict(name="miner-start-time-epoch", value=str(start_epoch)) + ) + return self + + def s3( + self, + s3_bucket: str, + s3_prefix: str, + sql_query_key: str, + default_database_key: str, + default_schema_key: str, + session_id_key: str, + s3_bucket_region: Optional[str] = None, + ) -> SnowflakeMiner: + """ + Set up the miner to extract from S3 (using JSON line-separated files). + + :param s3_bucket: S3 bucket where the JSON line-separated files are located + :param s3_prefix: prefix within the S3 bucket in + which the JSON line-separated files are located + :param sql_query_key: JSON key containing the query definition + :param default_database_key: JSON key containing the default + database name to use if a query is not qualified with database name + :param default_schema_key: JSON key containing the default schema name + to use if a query is not qualified with schema name + :param session_id_key: JSON key containing the session ID of the SQL query + :param s3_bucket_region: (Optional) region of the S3 bucket if applicable + :returns: miner, set up to extract from a set of JSON line-separated files in S3 + """ + self._parameters.append(dict(name="extraction-method", value="s3")) + self._parameters.append(dict(name="extraction-s3-bucket", value=s3_bucket)) + self._parameters.append(dict(name="extraction-s3-prefix", value=s3_prefix)) + self._parameters.append(dict(name="sql-json-key", value=sql_query_key)) + self._parameters.append( + dict(name="catalog-json-key", value=default_database_key) + ) + self._parameters.append(dict(name="schema-json-key", value=default_schema_key)) + self._parameters.append(dict(name="session-json-key", value=session_id_key)) + s3_bucket_region and self._parameters.append( + dict(name="extraction-s3-region", value=s3_bucket_region) + ) + return self + + def exclude_users(self, users: List[str]) -> SnowflakeMiner: + """ + Defines users who should be excluded when calculating + usage metrics for assets (for example, system accounts). + + :param users: list of users to exclude when calculating usage metrics + :returns: miner, set to exclude the specified users from usage metrics + :raises InvalidRequestException: in the unlikely event the provided + list cannot be translated + """ + exclude_users = users or [] + self._parameters.append( + dict( + name="popularity-exclude-user-config", + value=dumps(exclude_users) if exclude_users else "[]", + ) + ) + return self + + def popularity_window(self, days: int = 30) -> SnowflakeMiner: + """ + Defines number of days to consider for calculating popularity. + + :param days: number of days to consider, defaults to 30 + :returns: miner, set to include popularity window + """ + self._advanced_config = True + self._parameters.append(dict(name="popularity-window-days", value=str(days))) + return self + + def native_lineage(self, enabled: bool) -> SnowflakeMiner: + """ + Whether to enable native lineage from Snowflake, using + Snowflake's ACCESS_HISTORY.OBJECTS_MODIFIED Column. + Note: this is only available only for Snowflake Enterprise customers. + + :param enabled: if True, native lineage from Snowflake will be used for crawling + :returns: miner, set to include / exclude native lineage from Snowflake + """ + self._advanced_config = True + self._parameters.append( + dict(name="native-lineage-active", value="true" if enabled else "false") + ) + return self + + def custom_config(self, config: Dict) -> SnowflakeMiner: + """ + Defines custom JSON configuration controlling + experimental feature flags for the miner. + + :param config: custom configuration dict + :returns: miner, set to include custom configuration + """ + config and self._parameters.append( + dict(name="control-config", value=dumps(config)) + ) + self._advanced_config = True + return self + + def _set_required_metadata_params(self): + self._parameters.append( + dict( + name="control-config-strategy", + value="custom" if self._advanced_config else "default", + ) + ) + self._parameters.append(dict(name="sigle-session", value="false")) + + def _get_metadata(self) -> WorkflowMetadata: + self._set_required_metadata_params() + return WorkflowMetadata( + labels={ + "orchestration.atlan.com/certified": "true", + "orchestration.atlan.com/source": self._NAME, + "orchestration.atlan.com/sourceCategory": "warehouse", + "orchestration.atlan.com/type": "miner", + "orchestration.atlan.com/verified": "true", + "package.argoproj.io/installer": "argopm", + "package.argoproj.io/name": f"a-t-ratlans-l-a-s-h{self._NAME}-miner", + "package.argoproj.io/registry": "httpsc-o-l-o-ns-l-a-s-hs-l-a-s-hpackages.atlan.com", + "orchestration.atlan.com/atlan-ui": "true", + }, + annotations={ + "orchestration.atlan.com/allowSchedule": "true", + "orchestration.atlan.com/categories": "warehouse,miner", + "orchestration.atlan.com/docsUrl": "https://ask.atlan.com/hc/en-us/articles/6482067592337", + "orchestration.atlan.com/emoji": "\ud83d\ude80", + "orchestration.atlan.com/icon": self._PACKAGE_ICON, + "orchestration.atlan.com/logo": self._PACKAGE_LOGO, + "orchestration.atlan.com/marketplaceLink": f"https://packages.atlan.com/-/web/detail/{self._PACKAGE_NAME}", # noqa + "orchestration.atlan.com/name": "Snowflake Miner", + "package.argoproj.io/author": "Atlan", + "package.argoproj.io/description": "Package to mine query history data from Snowflake and store it for further processing. The data mined will be used for generating lineage and usage metrics.", # noqa + "package.argoproj.io/homepage": f"https://packages.atlan.com/-/web/detail/{self._PACKAGE_NAME}", + "package.argoproj.io/keywords": '["snowflake","warehouse","connector","miner"]', # fmt: skip + "package.argoproj.io/name": self._PACKAGE_NAME, + "package.argoproj.io/registry": "https://packages.atlan.com", + "package.argoproj.io/repository": "git+https://github.com/atlanhq/marketplace-packages.git", + "package.argoproj.io/support": "support@atlan.com", + "orchestration.atlan.com/atlanName": f"{self._PACKAGE_PREFIX}-{self._epoch}", + }, + name=f"{self._PACKAGE_PREFIX}-{self._epoch}", + namespace="default", + ) diff --git a/pyatlan_v9/model/packages/tableau_crawler.py b/pyatlan_v9/model/packages/tableau_crawler.py new file mode 100644 index 000000000..c669ccae2 --- /dev/null +++ b/pyatlan_v9/model/packages/tableau_crawler.py @@ -0,0 +1,266 @@ +from __future__ import annotations + +from typing import List, Optional + +from pyatlan.model.enums import AtlanConnectorType, WorkflowPackage +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.model.packages.base.crawler import AbstractCrawler +from pyatlan_v9.model.workflow import WorkflowMetadata + + +class TableauCrawler(AbstractCrawler): + """ + Base configuration for a new Tableau crawler. + + :param client: connectivity to an Atlan tenant + :param connection_name: name for the connection + :param admin_roles: admin roles for the connection + :param admin_groups: admin groups for the connection + :param admin_users: admin users for the connection + :param allow_query: allow data to be queried in the + connection (True) or not (False), default: False + :param allow_query_preview: allow sample data viewing for + assets in the connection (True) or not (False), default: False + :param row_limit: maximum number of rows + that can be returned by a query, default: 0 + """ + + _NAME = "tableau" + _PACKAGE_NAME = "@atlan/tableau" + _PACKAGE_PREFIX = WorkflowPackage.TABLEAU.value + _CONNECTOR_TYPE = AtlanConnectorType.TABLEAU + _PACKAGE_ICON = "https://img.icons8.com/color/480/000000/tableau-software.png" + _PACKAGE_LOGO = "https://img.icons8.com/color/480/000000/tableau-software.png" + + def __init__( + self, + client: AtlanClient, + connection_name: str, + admin_roles: Optional[List[str]] = None, + admin_groups: Optional[List[str]] = None, + admin_users: Optional[List[str]] = None, + allow_query: bool = False, + allow_query_preview: bool = False, + row_limit: int = 0, + ): + super().__init__( + client=client, + connection_name=connection_name, + connection_type=self._CONNECTOR_TYPE, + admin_roles=admin_roles, + admin_groups=admin_groups, + admin_users=admin_users, + allow_query=allow_query, + allow_query_preview=allow_query_preview, + row_limit=row_limit, + source_logo=self._PACKAGE_LOGO, + ) + + def s3( + self, bucket_name: str, bucket_prefix: str, bucket_region: Optional[str] = None + ) -> TableauCrawler: + """ + Set up the crawler to fetch metadata directly from the S3 bucket. + + :param bucket_name: name of the S3 bucket containing the extracted metadata files + :param bucket_prefix: prefix within the S3 bucket where the extracted metadata files are located + :param bucket_region: (Optional) region where the S3 bucket is located + :returns: crawler, configured to fetch metadata directly from the S3 bucket + """ + self._parameters.append(dict(name="extraction-method", value="s3")) + self._parameters.append(dict(name="metadata-s3-bucket", value=bucket_name)) + self._parameters.append(dict(name="metadata-s3-prefix", value=bucket_prefix)) + self._parameters.append(dict(name="metadata-s3-region", value=bucket_region)) + # Advanced configuration + self.exclude(projects=[]) + self.include(projects=[]) + self.crawl_unpublished(enabled=True) + self.crawl_hidden_fields(enabled=True) + return self + + def direct( + self, + hostname: str, + site: str, + port: int = 443, + ssl_enabled: bool = True, + ) -> TableauCrawler: + """ + Set up the crawler to extract directly from Tableau. + + :param hostname: hostname of Tableau + :param site: site in Tableau from which to extract + :param port: port for the connection to Tableau + :param ssl_enabled: if True, use SSL for the connection, otherwise do not use SSL + :returns: crawler, set up to extract directly from Tableau + """ + local_creds = { + "name": f"default-{self._NAME}-{self._epoch}-0", + "host": hostname, + "port": port, + "extra": { + "protocol": "https" if ssl_enabled else "http", + "defaultSite": site, + }, + "connector_config_name": f"atlan-connectors-{self._NAME}", + } + self._credentials_body.update(local_creds) + self._parameters.append({"name": "extraction-method", "value": "direct"}) + self._parameters.append( + {"name": "credential-guid", "value": "{{credentialGuid}}"} + ) + return self + + def basic_auth(self, username: str, password: str) -> TableauCrawler: + """ + Set up the crawler to use basic authentication. + + :param username: through which to access Tableau + :param password: through which to access Tableau + :returns: crawler, set up to use basic authentication + """ + local_creds = { + "authType": "basic", + "username": username, + "password": password, + } + self._credentials_body.update(local_creds) + return self + + def personal_access_token(self, username: str, access_token: str) -> TableauCrawler: + """ + Set up the crawler to use PAT-based authentication. + + :param username: through which to access Tableau + :param access_token: personal access token for the user, through which to access Tableau + :returns: crawler, set up to use PAT-based authentication + """ + local_creds = { + "authType": "personal_access_token", + "username": username, + "password": access_token, + } + self._credentials_body.update(local_creds) + return self + + def include(self, projects: List[str]) -> TableauCrawler: + """ + Defines the filter for projects to include when crawling. + + :param projects: the GUIDs of projects to include when crawling + :returns: crawler, set to include only those projects specified + :raises InvalidRequestException: In the unlikely + event the provided filter cannot be translated + """ + include_projects = projects or [] + to_include = self.build_flat_filter(include_projects) + self._parameters.append( + dict(dict(name="include-filter", value=to_include or "{}")) + ) + return self + + def exclude(self, projects: List[str]) -> TableauCrawler: + """ + Defines the filter for projects to exclude when crawling. + + :param projects: the GUIDs of projects to exclude when crawling + :returns: crawler, set to exclude only those projects specified + :raises InvalidRequestException: In the unlikely + event the provided filter cannot be translated + """ + exclude_projects = projects or [] + to_exclude = self.build_flat_filter(exclude_projects) + self._parameters.append(dict(name="exclude-filter", value=to_exclude or "{}")) + return self + + def crawl_hidden_fields(self, enabled: bool = True) -> TableauCrawler: + """ + Whether to crawl hidden datasource fields (True) or not. + + :param enabled: If True, hidden datasource fields + will be crawled, otherwise they will not, default: True + :returns: crawler, set to include or exclude hidden datasource fields + """ + self._parameters.append( + { + "name": "crawl-hidden-datasource-fields", + "value": "true" if enabled else "false", + } + ) + return self + + def crawl_unpublished(self, enabled: bool = True) -> TableauCrawler: + """ + Whether to crawl unpublished worksheets and dashboards (True) or not. + + :param enabled: If True, unpublished worksheets and dashboards + will be crawled, otherwise they will not, default: True + :returns: crawler, set to include or exclude unpublished worksheets and dashboards + """ + self._parameters.append( + { + "name": "crawl-unpublished-worksheets-dashboard", + "value": "true" if enabled else "false", + } + ) + return self + + def alternate_host(self, hostname: str) -> TableauCrawler: + """ + Set an alternate host to use for the "View in Tableau" button for assets in the UI. + + :param hostname: alternate hostname to use + :returns: crawler, set to use an alternate host for viewing assets in Tableau + """ + self._parameters.append({"name": "tableau-alternate-host", "value": hostname}) + return self + + def _set_required_metadata_params(self): + self._parameters.append( + { + "name": "connection", + "value": self._get_connection().to_json(), + } + ) + self._parameters.append(dict(name="atlas-auth-type", value="internal")) + self._parameters.append(dict(name="publish-mode", value="production")) + + def _get_metadata(self) -> WorkflowMetadata: + self._set_required_metadata_params() + return WorkflowMetadata( + labels={ + "orchestration.atlan.com/certified": "true", + "orchestration.atlan.com/source": self._NAME, + "orchestration.atlan.com/sourceCategory": "bi", + "orchestration.atlan.com/type": "connector", + "orchestration.atlan.com/verified": "true", + "package.argoproj.io/installer": "argopm", + "package.argoproj.io/name": f"a-t-ratlans-l-a-s-h{self._NAME}", + "package.argoproj.io/registry": "httpsc-o-l-o-ns-l-a-s-hs-l-a-s-hpackages.atlan.com", + f"orchestration.atlan.com/default-{self._NAME}-{self._epoch}": "true", + "orchestration.atlan.com/atlan-ui": "true", + }, + annotations={ + "orchestration.atlan.com/allowSchedule": "true", + "orchestration.atlan.com/categories": "tableau,crawler", + "orchestration.atlan.com/dependentPackage": "", + "orchestration.atlan.com/docsUrl": "https://ask.atlan.com/hc/en-us/articles/6332449996689", + "orchestration.atlan.com/emoji": "\U0001f680", + "orchestration.atlan.com/icon": self._PACKAGE_ICON, + "orchestration.atlan.com/logo": self._PACKAGE_LOGO, # noqa + "orchestration.atlan.com/marketplaceLink": f"https://packages.atlan.com/-/web/detail/{self._PACKAGE_NAME}", # noqa + "orchestration.atlan.com/name": f"{self._NAME} Assets", + "package.argoproj.io/author": "Atlan", + "package.argoproj.io/description": f"Package to crawl {self._NAME.capitalize()} assets and publish to Atlan for discovery.", # noqa + "package.argoproj.io/homepage": f"https://packages.atlan.com/-/web/detail/{self._PACKAGE_NAME}", + "package.argoproj.io/keywords": '["tableau","bi","connector","crawler"]', # fmt: skip + "package.argoproj.io/name": self._PACKAGE_NAME, + "package.argoproj.io/parent": ".", + "package.argoproj.io/registry": "https://packages.atlan.com", + "package.argoproj.io/repository": "git+https://github.com/atlanhq/marketplace-packages.git", + "package.argoproj.io/support": "support@atlan.com", + "orchestration.atlan.com/atlanName": f"{self._PACKAGE_PREFIX}-default-{self._NAME}-{self._epoch}", + }, + name=f"{self._PACKAGE_PREFIX}-{self._epoch}", + namespace="default", + ) diff --git a/pyatlan_v9/model/query.py b/pyatlan_v9/model/query.py new file mode 100644 index 000000000..eeebc95c6 --- /dev/null +++ b/pyatlan_v9/model/query.py @@ -0,0 +1,312 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2022 Atlan Pte. Ltd. + +from __future__ import annotations + +from typing import Any, Union + +import msgspec + +from pyatlan.model.enums import ( + HekaFlow, + ParsingFlow, + QueryParserSourceType, + QueryStatus, +) +from pyatlan.utils import validate_required_fields + + +class DatabaseColumn(msgspec.Struct, kw_only=True): + """Column referenced in a parsed query.""" + + id: Union[str, None] = None + """Numeric identifier for the column.""" + name: Union[str, None] = None + """Name of the column (unqualified).""" + source: Union[str, None] = None + + +class RelationshipEndpoint(msgspec.Struct, kw_only=True, rename="camel"): + """Endpoint of a lineage relationship in a parsed query.""" + + id: Union[str, None] = None + """Numeric identifier for the column.""" + column: Union[str, None] = None + """Name of the column.""" + parent_id: Union[str, None] = None + """Numeric identifier of the parent object.""" + parent_name: Union[str, None] = None + """Name of the parent object.""" + + +class ParserError(msgspec.Struct, kw_only=True, rename="camel"): + """Error encountered during query parsing.""" + + error_message: Union[str, None] = None + """Description of the error.""" + error_type: Union[str, None] = None + """Type of the error.""" + coordinates: Union[list[Any], None] = None + + +class QueryRelationship(msgspec.Struct, kw_only=True): + """Relationship detected in a parsed query.""" + + id: Union[str, None] = None + """Numeric identifier for the relationship.""" + type: Union[str, None] = None + """Type of the relationship.""" + effect_type: Union[str, None] = None + """Type of effect made by the query (e.g. select vs insert).""" + target: Union[RelationshipEndpoint, None] = None + sources: Union[list[RelationshipEndpoint], None] = None + process_id: Union[str, None] = None + """Numeric identifier for the procedure.""" + process_type: Union[str, None] = None + """Type of procedure.""" + + +class DatabaseObject(msgspec.Struct, kw_only=True, rename="camel"): + """Database object detected in a parsed query.""" + + display_name: Union[str, None] = None + """Fully-qualified name of the SQL object.""" + id: Union[str, None] = None + """Numeric identifier for the object.""" + name: Union[str, None] = None + """Name of the object (unqualified).""" + type: Union[str, None] = None + """Type of the object.""" + database: Union[str, None] = None + """Name of the database.""" + db_schema: Union[str, None] = msgspec.field(default=None, name="schema") + """Name of the schema.""" + columns: Union[list[DatabaseColumn], None] = None + """List of columns queried within the object.""" + procedure_name: Union[str, None] = None + """Name of the procedure (only for process objects).""" + query_hash_id: Union[str, None] = None + """Unique hash representing the query (only for process objects).""" + + +class ParsedQuery(msgspec.Struct, kw_only=True): + """Result of parsing a SQL query.""" + + dbobjs: Union[list[DatabaseObject], None] = None + """All the database objects detected in the query.""" + relationships: Union[list[QueryRelationship], None] = None + """All the relationship objects detected in the query.""" + errors: Union[list[ParserError], None] = None + """Any errors during parsing.""" + + +class QueryParserRequest(msgspec.Struct, kw_only=True): + """Request to parse a SQL query.""" + + sql: str + """SQL query to be parsed.""" + source: QueryParserSourceType + """Dialect to use when parsing the SQL.""" + default_database: Union[str, None] = None + """Default database name for unqualified objects.""" + default_schema: Union[str, None] = None + """Default schema name for unqualified objects.""" + link_orphan_column_to_first_table: Union[bool, None] = None + show_join: Union[bool, None] = None + ignore_record_set: Union[bool, None] = None + ignore_coordinate: Union[bool, None] = None + + @staticmethod + def creator( + sql: str, + source: QueryParserSourceType, + ) -> QueryParserRequest: + """ + Create a query parser request. + + :param sql: SQL query to be parsed + :param source: dialect to use when parsing + :returns: a configured QueryParserRequest + """ + validate_required_fields(["sql", "source"], [sql, source]) + return QueryParserRequest( + sql=sql, + source=source, + link_orphan_column_to_first_table=False, + show_join=True, + ignore_record_set=True, + ignore_coordinate=True, + ) + + +class QueryRequest(msgspec.Struct, kw_only=True, rename="camel"): + """Request to run a SQL query.""" + + sql: str + """SQL query to run.""" + data_source_name: str + """Unique name of the connection to use for the query.""" + default_schema: str + """Default schema name in the form 'DB.SCHEMA'.""" + + +class ColumnType(msgspec.Struct, kw_only=True): + """SQL column type details.""" + + id: Union[int, None] = None + name: Union[str, None] = None + """SQL name of the data type.""" + rep: Union[str, None] = None + + +class ColumnDetails(msgspec.Struct, kw_only=True, rename="camel"): + """Details about a column returned from a query.""" + + ordinal: Union[int, None] = None + """Position of the column (1-based).""" + auto_increment: Union[bool, None] = None + case_sensitive: Union[bool, None] = None + searchable: Union[bool, None] = None + currency: Union[bool, None] = None + nullable: Union[int, None] = None + signed: Union[bool, None] = None + display_size: Union[int, None] = None + label: Union[str, None] = None + """Display value for the column's name.""" + column_name: Union[str, None] = None + """Name of the column (technical).""" + schema_name: Union[str, None] = None + """Name of the schema.""" + precision: Union[int, None] = None + scale: Union[int, None] = None + table_name: Union[str, None] = None + """Name of the table.""" + catalog_name: Union[str, None] = None + """Name of the database.""" + read_only: Union[bool, None] = None + writable: Union[bool, None] = None + definitely_writable: Union[bool, None] = None + column_class_name: Union[str, None] = None + """Canonical name of the Java class.""" + type: Union[ColumnType, None] = None + """Details about the (SQL) data type.""" + + +class AssetDetails(msgspec.Struct, kw_only=True): + """Asset details in a query response.""" + + connection_name: Union[str, None] = None + """Simple name of the connection.""" + connection_qn: Union[str, None] = None + """Unique name of the connection.""" + database: Union[str, None] = None + """Simple name of the database.""" + schema_: Union[str, None] = msgspec.field(default=None, name="schema") + """Simple name of the schema.""" + table: Union[str, None] = None + """Simple name of the table.""" + + +class QueryDetails(msgspec.Struct, kw_only=True, rename="camel"): + """Details about a query that was run.""" + + total_rows_streamed: Union[int, None] = None + """Total number of results returned.""" + status: Union[QueryStatus, None] = None + """Status of the query.""" + parsed_query: Union[str, None] = None + pushdown_query: Union[str, None] = None + """Query sent to the data store.""" + execution_time: Union[int, None] = None + """How long the query took, in milliseconds.""" + source_query_id: Union[str, None] = None + result_output_location: Union[str, None] = None + warnings: Union[list[str], None] = None + """Warnings produced when running the query.""" + parsing_flow: Union[ParsingFlow, None] = None + """How the query was parsed.""" + heka_flow: Union[HekaFlow, None] = None + """How the query was run.""" + s3_upload_path: Union[str, None] = None + source_first_connection_time: Union[int, None] = None + source_first_connection_time_perc: Union[float, None] = None + explain_call_time_perc: Union[float, None] = None + init_data_source_time: Union[int, None] = None + init_data_source_time_perc: Union[float, None] = None + authorization_time: Union[int, None] = None + authorization_time_perc: Union[float, None] = None + rewrite_validation_time: Union[int, None] = None + rewrite_validation_time_perc: Union[float, None] = None + extract_table_metadata_time: Union[int, None] = None + """Elapsed time to extract table metadata, in milliseconds.""" + extract_table_metadata_time_perc: Union[float, None] = None + execution_time_internal: Union[int, None] = None + """Elapsed time to run the query (from internal engine), in milliseconds.""" + execution_time_perc: Union[float, None] = None + bypass_query_time: Union[int, None] = None + bypass_parsing_percentage: Union[float, None] = None + check_insights_enabled_time: Union[int, None] = None + check_insights_enabled_percentage: Union[float, None] = None + initialization_time: Union[int, None] = None + initialization_percentage: Union[float, None] = None + extract_credentials_time: Union[int, None] = None + extract_credentials_percentage: Union[float, None] = None + overall_time: Union[int, None] = None + overall_time_percentage: Union[float, None] = None + heka_atlan_time: Union[int, None] = None + calcite_parsing_percentage: Union[float, None] = None + calcite_validation_percentage: Union[float, None] = None + asset: Union[AssetDetails, None] = None + """Metadata about the asset used in the query.""" + developer_message: Union[str, None] = None + """Detailed back-end error message.""" + line: Union[int, None] = None + """Line number of the validation error.""" + column: Union[int, None] = None + """Column position of the validation error.""" + obj: Union[str, None] = None + """Name of the object that caused the validation error.""" + + +class QueryResponse: + """ + Consolidated response from multiple events related to the same query. + + Replaces the Pydantic model with a plain class since it has custom + __init__ logic for consolidating event data. + """ + + def __init__(self, events: Union[list[dict[str, Any]], None] = None): + self.request_id: Union[str, None] = None + self.error_name: Union[str, None] = None + self.error_message: Union[str, None] = None + self.error_code: Union[str, None] = None + self.query_id: Union[str, None] = None + self.rows: Union[list[list[str]], None] = None + self.columns: Union[list[ColumnDetails], None] = None + self.details: Union[QueryDetails, None] = None + + if not events: + return + + self.rows = [] + self.columns = [] + for event in events: + event_rows = event.get("rows") + event_columns = event.get("columns") + if event_rows: + self.rows.extend(event_rows) + if not self.columns and event_columns: + self.columns = msgspec.convert( + event_columns, list[ColumnDetails], strict=False + ) + + last_event = events[-1] + self.request_id = last_event.get("requestId") + self.error_name = last_event.get("errorName") + self.error_message = last_event.get("errorMessage") + self.error_code = last_event.get("errorCode") + self.query_id = last_event.get("queryId") + details_raw = last_event.get("details") + if details_raw: + self.details = msgspec.convert(details_raw, QueryDetails, strict=False) diff --git a/pyatlan_v9/model/response.py b/pyatlan_v9/model/response.py new file mode 100644 index 000000000..80e5db33a --- /dev/null +++ b/pyatlan_v9/model/response.py @@ -0,0 +1,110 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2022 Atlan Pte. Ltd. +# Based on original code from https://github.com/apache/atlas (under Apache-2.0 license) + +from __future__ import annotations + +from typing import Type, TypeVar, Union + +import msgspec + +from pyatlan_v9.model.assets import Asset + +A = TypeVar("A", bound=Asset) + + +class MutatedEntities(msgspec.Struct, kw_only=True): + """Entities that were mutated during an API operation.""" + + CREATE: Union[list[Asset], None] = msgspec.field(default=None, name="CREATE") + """Assets that were created.""" + + UPDATE: Union[list[Asset], None] = msgspec.field(default=None, name="UPDATE") + """Assets that were updated.""" + + DELETE: Union[list[Asset], None] = msgspec.field(default=None, name="DELETE") + """Assets that were deleted.""" + + PARTIAL_UPDATE: Union[list[Asset], None] = msgspec.field( + default=None, name="PARTIAL_UPDATE" + ) + """Assets that were partially updated.""" + + +class AssetMutationResponse(msgspec.Struct, kw_only=True): + """Response from an asset mutation operation.""" + + guid_assignments: Union[dict[str, str], None] = None + """Map of assigned unique identifiers for the changed assets.""" + + mutated_entities: Union[MutatedEntities, None] = None + """Assets that were changed.""" + + partial_updated_entities: Union[list[Asset], None] = None + """Assets that were partially updated.""" + + def assets_created(self, asset_type: Type[A]) -> list[A]: + """Return created assets matching the given type.""" + if self.mutated_entities and self.mutated_entities.CREATE: + return [ + asset + for asset in self.mutated_entities.CREATE + if isinstance(asset, asset_type) + ] + return [] + + def assets_updated(self, asset_type: Type[A]) -> list[A]: + """Return updated assets matching the given type.""" + if self.mutated_entities and self.mutated_entities.UPDATE: + return [ + asset + for asset in self.mutated_entities.UPDATE + if isinstance(asset, asset_type) + ] + return [] + + def assets_deleted(self, asset_type: Type[A]) -> list[A]: + """Return deleted assets matching the given type.""" + if self.mutated_entities and self.mutated_entities.DELETE: + return [ + asset + for asset in self.mutated_entities.DELETE + if isinstance(asset, asset_type) + ] + return [] + + def assets_partially_updated(self, asset_type: Type[A]) -> list[A]: + """Return partially updated assets matching the given type.""" + if self.mutated_entities and self.mutated_entities.PARTIAL_UPDATE: + return [ + asset + for asset in self.mutated_entities.PARTIAL_UPDATE + if isinstance(asset, asset_type) + ] + return [] + + +class AccessTokenResponse(msgspec.Struct, kw_only=True): + """Response from an OAuth token request.""" + + access_token: str + """The access token.""" + + expires_in: Union[int, None] = None + """Token expiry time in seconds.""" + + refresh_expires_in: Union[int, None] = None + """Refresh token expiry time in seconds.""" + + refresh_token: Union[str, None] = None + """The refresh token.""" + + token_type: Union[str, None] = None + """Type of the token (e.g. 'Bearer').""" + + not_before_policy: Union[int, None] = None + + session_state: Union[str, None] = None + + scope: Union[str, None] = None + """Scope of the token.""" diff --git a/pyatlan_v9/model/retranslators.py b/pyatlan_v9/model/retranslators.py new file mode 100644 index 000000000..68de989e7 --- /dev/null +++ b/pyatlan_v9/model/retranslators.py @@ -0,0 +1,96 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2026 Atlan Pte. Ltd. + +"""Request retranslators for pyatlan_v9.""" + +from __future__ import annotations + +from abc import ABC, abstractmethod +from typing import Any + +import msgspec + +from pyatlan.model.constants import DELETED_ +from pyatlan_v9.model.structs import SourceTagAttachment + + +class BaseRetranslator(ABC): + """Abstract request retranslator.""" + + @abstractmethod + def applies_to(self, data: dict[str, Any]) -> bool: + """Return whether this retranslator should process this dictionary.""" + + @abstractmethod + def retranslate(self, data: dict[str, Any]) -> dict[str, Any]: + """Retranslate dictionary values back into backend-compatible format.""" + + +class AtlanTagRetranslator(BaseRetranslator): + """ + Retranslator that converts human-readable tag names back to tag IDs. + + Also rebuilds source-tag attachment blocks under classification attributes + using the attribute ID expected by the backend. + """ + + _TYPE_NAME = "typeName" + _SOURCE_ATTACHMENTS = "sourceTagAttachments" + _CLASSIFICATION_NAMES = {"classificationNames", "purposeClassifications"} + _CLASSIFICATION_KEYS = { + "classifications", + "addOrUpdateClassifications", + "removeClassifications", + } + + def __init__(self, client: Any): + self.client = client + + def applies_to(self, data: dict[str, Any]) -> bool: + """Check whether classification-related keys are present.""" + return any(key in data for key in self._CLASSIFICATION_NAMES) or any( + key in data for key in self._CLASSIFICATION_KEYS + ) + + def _attachment_to_dict(self, attachment: Any) -> dict[str, Any]: + if isinstance(attachment, SourceTagAttachment): + attrs = msgspec.to_builtins(attachment) + else: + attrs = msgspec.convert(attachment, type=dict[str, Any]) + return { + "typeName": "SourceTagAttachment", + "attributes": attrs, + } + + def retranslate(self, data: dict[str, Any]) -> dict[str, Any]: + """Retranslate tag names into IDs on a copy of the provided dictionary.""" + translated = data.copy() + + for key in self._CLASSIFICATION_NAMES: + if key in translated and translated[key] is not None: + translated[key] = [ + self.client.atlan_tag_cache.get_id_for_name(str(name)) or DELETED_ + for name in translated[key] + ] + + for key in self._CLASSIFICATION_KEYS: + if key not in translated: + continue + for classification in translated[key]: + tag_name = str(classification.get(self._TYPE_NAME)) + if not tag_name: + continue + tag_id = self.client.atlan_tag_cache.get_id_for_name(tag_name) + classification[self._TYPE_NAME] = tag_id if tag_id else DELETED_ + + attachments = classification.pop(self._SOURCE_ATTACHMENTS, None) + if not attachments or not tag_id: + continue + attr_id = self.client.atlan_tag_cache.get_source_tags_attr_id(tag_id) + if not attr_id: + continue + classification.setdefault("attributes", {})[attr_id] = [ + self._attachment_to_dict(attachment) for attachment in attachments + ] + + return translated diff --git a/pyatlan_v9/model/role.py b/pyatlan_v9/model/role.py new file mode 100644 index 000000000..b51c260db --- /dev/null +++ b/pyatlan_v9/model/role.py @@ -0,0 +1,46 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2022 Atlan Pte. Ltd. + +from __future__ import annotations + +from typing import Union + +import msgspec + + +class AtlanRole(msgspec.Struct, kw_only=True): + """Representation of a role in Atlan.""" + + id: str + """Unique identifier for the role (GUID).""" + + name: str + """Unique name for the role.""" + + description: Union[str, None] = None + """Description of the role.""" + + client_role: Union[bool, None] = None + """Whether this is a client-level role.""" + + level: Union[str, None] = None + """Level of the role.""" + + member_count: Union[str, None] = None + """Number of users with this role.""" + + user_count: Union[str, None] = None + """Count of users assigned to this role.""" + + +class RoleResponse(msgspec.Struct, kw_only=True): + """Response containing role information.""" + + total_record: Union[int, None] = None + """Total number of roles.""" + + filter_record: Union[int, None] = None + """Number of roles in the filtered response.""" + + records: list[AtlanRole] = msgspec.field(default_factory=list) + """Details of each role included in the response.""" diff --git a/pyatlan_v9/model/search.py b/pyatlan_v9/model/search.py new file mode 100644 index 000000000..e6eee39ab --- /dev/null +++ b/pyatlan_v9/model/search.py @@ -0,0 +1,345 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2022 Atlan Pte. Ltd. +# Based on original code from https://github.com/elastic/elasticsearch-dsl-py.git (under Apache-2.0 license) +from __future__ import annotations + +import json as json_lib +from enum import Enum +from typing import Any, Dict, List, Optional + +import msgspec + +from pyatlan.model.enums import UTMTags + +# --------------------------------------------------------------------------- +# Re-export all dataclass / ABC / Enum search classes from legacy. +# These are NOT Pydantic models — they're plain Python dataclasses and ABCs +# that don't need migration. Importing them here keeps the +# `from pyatlan_v9.model.search import Term, Bool, ...` API stable. +# --------------------------------------------------------------------------- +from pyatlan.model.search import ( # noqa: F401 + Attributes, + Bool, + Exists, + Fuzzy, + Match, + MatchAll, + MatchNone, + MatchPhrase, + NestedQuery, + Prefix, + Query, + Range, + Regexp, + SearchFieldType, + SortItem, + Span, + SpanNear, + SpanOr, + SpanTerm, + SpanWithin, + Term, + TermAttributes, + Terms, + TextAttributes, + Wildcard, + get_with_string, +) +from pyatlan_v9.model.aggregation import Aggregation + +# --------------------------------------------------------------------------- +# v9-only helpers +# --------------------------------------------------------------------------- + + +def _serialize_value(v: Any) -> Any: + """Recursively serialize a value to JSON-compatible types.""" + if v is None: + return None + if hasattr(v, "to_dict") and callable(v.to_dict): + # Recursively serialize the result of to_dict() in case it contains nested objects + result = v.to_dict() + if isinstance(result, dict): + return {k: _serialize_value(val) for k, val in result.items()} + elif isinstance(result, list): + return [_serialize_value(item) for item in result] + return result + # Handle Pydantic __root__ models (unwrap before dict serialization) + if hasattr(v, "__root__"): + return _serialize_value(v.__root__) + # Handle Pydantic v1 models (legacy pyatlan models like Bool, Term, etc.) + if hasattr(v, "dict") and callable(v.dict): + result = v.dict(by_alias=True, exclude_none=True) + if isinstance(result, dict): + return {k: _serialize_value(val) for k, val in result.items()} + elif isinstance(result, list): + return [_serialize_value(item) for item in result] + return result + # Handle Pydantic v2 models + if hasattr(v, "model_dump") and callable(v.model_dump): + result = v.model_dump(by_alias=True, exclude_none=True) + if isinstance(result, dict): + return {k: _serialize_value(val) for k, val in result.items()} + elif isinstance(result, list): + return [_serialize_value(item) for item in result] + return result + if isinstance(v, dict): + return {k: _serialize_value(val) for k, val in v.items()} + if isinstance(v, list): + return [_serialize_value(item) for item in v] + if isinstance(v, Enum): + return v.value + return v + + +# --------------------------------------------------------------------------- +# msgspec.Struct models — these are the genuine Pydantic→msgspec migrations +# --------------------------------------------------------------------------- + + +class DSL(msgspec.Struct, kw_only=True): + from_: int = msgspec.field(default=0, name="from") + size: int = 300 + aggregations: Dict[str, Aggregation] = msgspec.field(default_factory=dict) + track_total_hits: Optional[bool] = True + # Any: from JSON we get dict; from FluentSearch we get Query. Both serialize via _serialize_value. + post_filter: Optional[Any] = None + query: Optional[Any] = None + req_class_name: Optional[str] = None + sort: List[SortItem] = msgspec.field(default_factory=list) + + def __post_init__(self): + # Validate that either query or post_filter is provided + if not self.query and not self.post_filter: + raise ValueError("Either query or post_filter is required") + + # Convert dict entries to SortItem instances + if self.sort and all(isinstance(item, dict) for item in self.sort): + self.sort = [SortItem.from_dict(item) for item in self.sort] # type: ignore[arg-type] + + # Ensure sort includes GUID sort + missing_guid_sort = True + sort_by_guid = "__guid" + auditsearch_sort_by_guid = "entityId" + searchlog_sort_by_guid = "entityGuidsAll" + for option in self.sort: + if option.field and option.field in ( + sort_by_guid, + auditsearch_sort_by_guid, + searchlog_sort_by_guid, + ): + missing_guid_sort = False + break + if missing_guid_sort: + if self.req_class_name == "SearchLogRequest": + self.sort.append(SortItem(searchlog_sort_by_guid)) + elif self.req_class_name == "AuditSearchRequest": + self.sort.append(SortItem(auditsearch_sort_by_guid)) + elif self.req_class_name == "IndexSearchRequest": + self.sort.append(SortItem(sort_by_guid)) + + def to_dict( + self, + by_alias: bool = True, + exclude_none: bool = True, + ) -> Dict[str, Any]: + """Serialize DSL to a dict, matching legacy Pydantic output format.""" + d: Dict[str, Any] = {} + d["from" if by_alias else "from_"] = self.from_ + d["size"] = self.size + d["aggregations"] = _serialize_value(self.aggregations) + d["track_total_hits"] = self.track_total_hits + d["post_filter"] = _serialize_value(self.post_filter) + d["query"] = _serialize_value(self.query) + d["sort"] = _serialize_value(self.sort) + if exclude_none: + d = {k: v for k, v in d.items() if v is not None} + return d + + def json( + self, + by_alias: bool = False, + exclude_none: bool = False, + exclude_unset: bool = False, + ) -> str: + """Serialize DSL to JSON string, matching legacy Pydantic output format.""" + return json_lib.dumps( + self.to_dict(by_alias=by_alias, exclude_none=exclude_none) + ) + + +class IndexSearchRequestMetadata(msgspec.Struct, kw_only=True): + save_search_log: bool = False + utm_tags: List[str] = msgspec.field(default_factory=list) + + def to_dict(self, by_alias: bool = True) -> Dict[str, Any]: + """Convert to dict with camelCase keys when by_alias=True.""" + if by_alias: + return { + "saveSearchLog": self.save_search_log, + "utmTags": self.utm_tags, + } + return { + "save_search_log": self.save_search_log, + "utm_tags": self.utm_tags, + } + + +class IndexSearchRequest(msgspec.Struct, kw_only=True): + dsl: DSL + attributes: Optional[List[str]] = msgspec.field(default_factory=list) + relation_attributes: Optional[List[str]] = msgspec.field( + default_factory=list, name="relationAttributes" + ) + suppress_logs: Optional[bool] = msgspec.field(default=None, name="suppressLogs") + show_search_score: Optional[bool] = msgspec.field( + default=None, name="showSearchScore" + ) + exclude_meanings: Optional[bool] = msgspec.field( + default=None, name="excludeMeanings" + ) + exclude_atlan_tags: Optional[bool] = msgspec.field( + default=None, name="excludeClassifications" + ) + allow_deleted_relations: Optional[bool] = msgspec.field( + default=None, name="allowDeletedRelations" + ) + include_atlan_tag_names: Optional[bool] = msgspec.field( + default=None, name="includeClassificationNames" + ) + persona: Optional[str] = None + purpose: Optional[str] = None + include_relationship_attributes: Optional[bool] = False + enable_full_restriction: Optional[bool] = msgspec.field( + default=None, name="enableFullRestriction" + ) + request_metadata: Optional[IndexSearchRequestMetadata] = None + + def __post_init__(self): + # Ensure DSL has the correct req_class_name + class_name = self.__class__.__name__ + if self.dsl and isinstance(self.dsl, DSL) and not self.dsl.req_class_name: + self.dsl = DSL( + req_class_name=class_name, + from_=self.dsl.from_, + size=self.dsl.size, + aggregations=self.dsl.aggregations, + track_total_hits=self.dsl.track_total_hits, + post_filter=self.dsl.post_filter, + query=self.dsl.query, + sort=self.dsl.sort, + ) + + # Set default request_metadata + if self.request_metadata is None: + self.request_metadata = IndexSearchRequestMetadata( + save_search_log=False, + utm_tags=[UTMTags.PROJECT_SDK_PYTHON], + ) + + def to_dict( + self, + by_alias: bool = True, + exclude_none: bool = True, + ) -> Dict[str, Any]: + """Serialize IndexSearchRequest to dict, matching legacy Pydantic output format.""" + _ALIAS_MAP = { + "attributes": "attributes", + "dsl": "dsl", + "relation_attributes": "relationAttributes", + "suppress_logs": "suppressLogs", + "show_search_score": "showSearchScore", + "exclude_meanings": "excludeMeanings", + "exclude_atlan_tags": "excludeClassifications", + "allow_deleted_relations": "allowDeletedRelations", + "include_atlan_tag_names": "includeClassificationNames", + "persona": "persona", + "purpose": "purpose", + "include_relationship_attributes": "includeRelationshipAttributes", + "enable_full_restriction": "enableFullRestriction", + "request_metadata": "requestMetadata", + } + d: Dict[str, Any] = {} + for field_name, alias in _ALIAS_MAP.items(): + val = getattr(self, field_name, None) + key = alias if by_alias else field_name + if field_name == "dsl" and val is not None: + val = json_lib.loads( + val.json(by_alias=by_alias, exclude_none=exclude_none) + ) + elif field_name == "request_metadata" and val is not None: + val = val.to_dict(by_alias=by_alias) + else: + val = _serialize_value(val) + d[key] = val + if exclude_none: + d = {k: v for k, v in d.items() if v is not None} + return d + + def json( + self, + by_alias: bool = False, + exclude_none: bool = False, + exclude_unset: bool = False, + ) -> str: + """Serialize IndexSearchRequest to JSON string, matching legacy Pydantic output format.""" + return json_lib.dumps( + self.to_dict(by_alias=by_alias, exclude_none=exclude_none) + ) + + +# --------------------------------------------------------------------------- +# v9-specific with_active_* helpers (use our own validation, not Pydantic's) +# --------------------------------------------------------------------------- + + +def _validate_name(name: str) -> None: + """Validate that name is a non-None, non-blank string.""" + if name is None: + raise ValueError("name must not be None") + if not name.strip(): + raise ValueError("name must have at least 1 non-whitespace character") + + +def _validate_glossary_qualified_name(qualified_name: str) -> None: + """Validate that glossary qualified_name is a non-None, non-blank string.""" + if qualified_name is None: + raise ValueError("glossary_qualified_name must not be None") + if not qualified_name.strip(): + raise ValueError( + "glossary_qualified_name must have at least 1 non-whitespace character" + ) + + +def with_active_glossary(name: str) -> Bool: + """Return a Bool query matching an active glossary by name.""" + _validate_name(name) + return ( + Term.with_state("ACTIVE") + + Term.with_type_name("AtlasGlossary") + + Term.with_name(name) + ) + + +def with_active_category(name: str, glossary_qualified_name: str) -> Bool: + """Return a Bool query matching an active glossary category by name and glossary.""" + _validate_name(name) + _validate_glossary_qualified_name(glossary_qualified_name) + return ( + Term.with_state("ACTIVE") + + Term.with_type_name("AtlasGlossaryCategory") + + Term.with_name(name) + + Term.with_glossary(glossary_qualified_name) + ) + + +def with_active_term(name: str, glossary_qualified_name: str) -> Bool: + """Return a Bool query matching an active glossary term by name and glossary.""" + _validate_name(name) + _validate_glossary_qualified_name(glossary_qualified_name) + return ( + Term.with_state("ACTIVE") + + Term.with_type_name("AtlasGlossaryTerm") + + Term.with_name(name) + + Term.with_glossary(glossary_qualified_name) + ) diff --git a/pyatlan_v9/model/search_log.py b/pyatlan_v9/model/search_log.py new file mode 100644 index 000000000..02ee1e54b --- /dev/null +++ b/pyatlan_v9/model/search_log.py @@ -0,0 +1,561 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2022 Atlan Pte. Ltd. +from __future__ import annotations + +import json as json_lib +from datetime import datetime +from typing import Any, Dict, Generator, Iterable, List, Optional + +import msgspec + +from pyatlan.client.constants import SEARCH_LOG +from pyatlan.client.protocol import ApiCaller +from pyatlan.errors import ErrorCode +from pyatlan.model.enums import SortOrder, UTMTags +from pyatlan_v9.model.aggregation import Aggregation +from pyatlan_v9.model.search import DSL, Bool, Query, Range, SortItem, Term, Terms + +BY_TIMESTAMP = [SortItem("timestamp", order=SortOrder.ASCENDING)] + + +_EXCLUDE_USERS: List[str] = [ + "support", + "atlansupport", +] +_BASE_QUERY_FILTER: List[Query] = [ + Term( + field="utmTags", + value=UTMTags.ACTION_ASSET_VIEWED.value, + ), + Bool( + should=[ + Term(field="utmTags", value=UTMTags.UI_PROFILE.value), + Term(field="utmTags", value=UTMTags.UI_SIDEBAR.value), + ], + minimum_should_match=1, + ), +] + + +class SearchLogRequest(msgspec.Struct, kw_only=True): + """Class from which to configure a search against Atlan's search log.""" + + dsl: DSL + attributes: List[str] = msgspec.field(default_factory=list) + + def __post_init__(self): + class_name = self.__class__.__name__ + if self.dsl and isinstance(self.dsl, DSL) and not self.dsl.req_class_name: + self.dsl = DSL( + req_class_name=class_name, + from_=self.dsl.from_, + size=self.dsl.size, + aggregations=self.dsl.aggregations, + track_total_hits=self.dsl.track_total_hits, + post_filter=self.dsl.post_filter, + query=self.dsl.query, + sort=self.dsl.sort, + ) + + def to_dict( + self, + by_alias: bool = True, + exclude_none: bool = True, + ) -> Dict[str, Any]: + """Serialize SearchLogRequest to a dict suitable for JSON serialization.""" + d: Dict[str, Any] = { + "attributes": self.attributes, + "dsl": json_lib.loads( + self.dsl.json(by_alias=by_alias, exclude_none=exclude_none) + ), + } + if exclude_none: + d = {k: v for k, v in d.items() if v is not None} + return d + + def json( + self, + by_alias: bool = False, + exclude_none: bool = False, + exclude_unset: bool = False, + ) -> str: + """Serialize SearchLogRequest to JSON string.""" + return json_lib.dumps( + self.to_dict(by_alias=by_alias, exclude_none=exclude_none) + ) + + @classmethod + def _get_view_dsl_kwargs( + cls, + size: int, + from_: int, + query_filter: Optional[list] = None, + sort: Optional[list] = None, + exclude_users: Optional[List[str]] = None, + ) -> dict: + sort = sort or [] + query_filter = query_filter or [] + exclude_users = exclude_users or [] + return dict( + size=size, + from_=from_, + sort=sort + BY_TIMESTAMP, + query=Bool( + filter=query_filter + _BASE_QUERY_FILTER, + must_not=[ + Terms( + field="userName", + values=exclude_users + _EXCLUDE_USERS, + ) + ], + ), + ) + + @staticmethod + def _get_recent_viewers_aggs(max_users: int) -> Dict[str, object]: + return { + "uniqueUsers": { + "terms": { + "field": "userName", + "size": max_users, + "order": [{"latest_timestamp": "desc"}], + }, + "aggregations": {"latest_timestamp": {"max": {"field": "timestamp"}}}, + }, + "totalDistinctUsers": { + "cardinality": {"field": "userName", "precision_threshold": 1000} + }, + } + + @staticmethod + def _get_most_viewed_assets_aggs( + max_assets: int, by_diff_user: bool + ) -> Dict[str, object]: + aggs_terms: Dict[str, Any] = { + "field": "entityGuidsAll", + "size": max_assets, + } + if by_diff_user: + aggs_terms.update({"order": [{"uniqueUsers": "desc"}]}) + return { + "uniqueAssets": { + "aggregations": { + "uniqueUsers": { + "cardinality": { + "field": "userName", + "precision_threshold": 1000, + } + } + }, + "terms": aggs_terms, + }, + "totalDistinctUsers": { + "cardinality": {"field": "userName", "precision_threshold": 1000} + }, + } + + @classmethod + def most_recent_viewers( + cls, + guid: str, + max_users: int = 20, + exclude_users: Optional[List[str]] = None, + ) -> SearchLogRequest: + """ + Create a search log request to retrieve views of an asset by its GUID. + + :param guid: unique identifier of the asset. + :param max_users: maximum number of recent users to consider. Defaults to 20. + :param exclude_users: a list containing usernames to be excluded from the search log results (optional). + + :returns: A SearchLogRequest that can be used to perform the search. + """ + query_filter = [ + Term(field="entityGuidsAll", value=guid, case_insensitive=False) + ] + dsl = DSL( + **cls._get_view_dsl_kwargs( + size=0, from_=0, query_filter=query_filter, exclude_users=exclude_users + ), + aggregations=cls._get_recent_viewers_aggs(max_users), + ) + return SearchLogRequest(dsl=dsl) + + @classmethod + def most_viewed_assets( + cls, + max_assets: int = 10, + by_different_user: bool = False, + exclude_users: Optional[List[str]] = None, + ) -> SearchLogRequest: + """ + Create a search log request to retrieve most viewed assets. + + :param max_assets: maximum number of assets to consider. Defaults to 10. + :param by_different_user: when True, will consider assets viewed by more users as more + important than total view count, otherwise will consider total view count most important. + :param exclude_users: a list containing usernames to be excluded from the search log results (optional). + + :returns: A SearchLogRequest that can be used to perform the search. + """ + dsl = DSL( + **cls._get_view_dsl_kwargs(size=0, from_=0, exclude_users=exclude_users), + aggregations=cls._get_most_viewed_assets_aggs( + max_assets, by_different_user + ), + ) + return SearchLogRequest(dsl=dsl) + + @classmethod + def views_by_guid( + cls, + guid: str, + size: int = 20, + from_: int = 0, + sort: Optional[List[SortItem]] = None, + exclude_users: Optional[List[str]] = None, + ) -> SearchLogRequest: + """ + Create a search log request to retrieve recent search logs of an assets. + + :param guid: unique identifier of the asset. + :param size: number of results to retrieve per page. Defaults to 20. + :param from_: starting point for paging. Defaults to 0 (very first result) if not overridden. + :param sort: properties by which to sort the results (optional). + :param exclude_users: a list containing usernames to be excluded from the search log results (optional). + + :returns: A SearchLogRequest that can be used to perform the search. + """ + query_filter = [ + Term(field="entityGuidsAll", value=guid, case_insensitive=False) + ] + dsl = DSL( + **cls._get_view_dsl_kwargs( + size=size, + from_=from_, + query_filter=query_filter, + sort=sort, + exclude_users=exclude_users, + ), + ) + return SearchLogRequest(dsl=dsl) + + +class AssetViews(msgspec.Struct, kw_only=True): + """ + Captures a specific aggregate result of assets and the views on that asset. + Instances of this class should be treated as immutable. + """ + + guid: str + total_views: int + distinct_users: int + + +class UserViews(msgspec.Struct, kw_only=True): + """ + Represents unique user views entry in the search log. + Instances of this class should be treated as immutable. + """ + + username: str + view_count: int + most_recent_view: datetime + + +class SearchLogEntry(msgspec.Struct, kw_only=True): + """ + Represents a log entry for asset search in the search log. + Instances of this class should be treated as immutable. + """ + + user_agent: str = msgspec.field(name="userAgent") + host: str + ip_address: str = msgspec.field(name="ipAddress") + user_name: str = msgspec.field(name="userName") + entity_guids_all: List[str] = msgspec.field( + default_factory=list, name="entityGuidsAll" + ) + entity_qf_names_all: List[str] = msgspec.field( + default_factory=list, name="entityQFNamesAll" + ) + entity_guids_allowed: List[str] = msgspec.field( + default_factory=list, name="entityGuidsAllowed" + ) + entity_qf_names_allowed: List[str] = msgspec.field( + default_factory=list, name="entityQFNamesAllowed" + ) + entity_type_names_all: List[str] = msgspec.field( + default_factory=list, name="entityTypeNamesAll" + ) + entity_type_names_allowed: List[str] = msgspec.field( + default_factory=list, name="entityTypeNamesAllowed" + ) + utm_tags: List[str] = msgspec.field(default_factory=list, name="utmTags") + has_result: bool = msgspec.field(default=False, name="hasResult") + results_count: int = msgspec.field(default=0, name="resultsCount") + response_time: int = msgspec.field(default=0, name="responseTime") + created_at: datetime = msgspec.field(default_factory=datetime.now, name="createdAt") + timestamp: datetime = msgspec.field(default_factory=datetime.now) + failed: bool = False + request_dsl: Optional[dict] = msgspec.field(default=None, name="request.dsl") + request_dsl_text: Optional[str] = msgspec.field( + default=None, name="request.dslText" + ) + request_attributes: Optional[List[str]] = msgspec.field( + default=None, name="request.attributes" + ) + request_relation_attributes: Optional[List[str]] = msgspec.field( + default=None, name="request.relationAttributes" + ) + + +class SearchLogViewResults: + """Captures the response from a search against Atlan's search log views.""" + + def __init__( + self, + count: int, + user_views: Optional[List[UserViews]] = None, + asset_views: Optional[List[AssetViews]] = None, + ): + self._count = count + self._user_views = user_views + self._asset_views = asset_views + + @property + def count(self) -> int: + return self._count + + @property + def user_views(self) -> Optional[List[UserViews]]: + return self._user_views + + @property + def asset_views(self) -> Optional[List[AssetViews]]: + return self._asset_views + + +class SearchLogResults(Iterable): + """Captures the response from a search against Atlan's recent search logs.""" + + _DEFAULT_SIZE = 300 + _MASS_EXTRACT_THRESHOLD = 10000 - _DEFAULT_SIZE + + def __init__( + self, + client: ApiCaller, + criteria: SearchLogRequest, + start: int, + size: int, + count: int, + log_entries: List[SearchLogEntry], + aggregations: Dict[str, Aggregation], + bulk: bool = False, + processed_log_entries_count: int = 0, + ): + self._client = client + self._endpoint = SEARCH_LOG + self._criteria = criteria + self._start = start + self._size = size + self._log_entries = log_entries + self._count = count + self._approximate_count = count + self._aggregations = aggregations + self._bulk = bulk + self._first_record_creation_time = -2 + self._last_record_creation_time = -2 + self._duplicate_timestamp_page_count: int = 0 + self._processed_log_entries_count: int = processed_log_entries_count + + @property + def count(self) -> int: + return self._count + + def current_page(self) -> List[SearchLogEntry]: + """ + Retrieve the current page of results. + + :returns: list of assets on the current page of results + """ + return self._log_entries + + def next_page(self, start=None, size=None) -> bool: + """ + Indicates whether there is a next page of results. + + :returns: True if there is a next page of results, otherwise False + """ + self._start = start or self._start + self._size + if size: + self._size = size + return self._get_next_page() if self._log_entries else False + + def _get_next_page(self): + """ + Fetches the next page of results. + + :returns: True if the next page of results was fetched, False if there was no next page + """ + query = self._criteria.dsl.query + self._criteria.dsl.from_ = self._start + self._criteria.dsl.size = self._size + is_bulk_search = ( + self._bulk or self._approximate_count > self._MASS_EXTRACT_THRESHOLD + ) + if is_bulk_search: + self._prepare_query_for_timestamp_paging(query) + + if raw_json := self._get_next_page_json(is_bulk_search): + self._count = raw_json.get("approximateCount", 0) + return True + return False + + def _get_next_page_json(self, is_bulk_search: bool = False): + """ + Fetches the next page of results and returns the raw JSON of the retrieval. + + :returns: JSON for the next page of results, as-is + """ + raw_json = self._client._call_api( + self._endpoint, + request_obj=self._criteria, + ) + + if "logs" not in raw_json or not raw_json["logs"]: + self._log_entries = [] + return None + try: + from pyatlan_v9.client.search_log import ( + _LOG_TS_FIELDS, + _normalize_ms_timestamps_copy, + ) + + self._log_entries = [ + msgspec.convert( + _normalize_ms_timestamps_copy(entry, _LOG_TS_FIELDS), + SearchLogEntry, + strict=False, + ) + for entry in raw_json["logs"] + ] + self._processed_log_entries_count += len(self._log_entries) + if is_bulk_search: + self._update_first_last_record_creation_times() + return raw_json + except Exception as err: + raise ErrorCode.JSON_ERROR.exception_with_parameters( + raw_json, 200, str(err) + ) from err + + def _prepare_query_for_timestamp_paging(self, query: Query): + """ + Adjusts the query to include timestamp filters for search log bulk extraction. + """ + self._criteria.dsl.from_ = 0 + rewritten_filters = [] + if isinstance(query, Bool): + for filter_ in query.filter: + if self._is_paging_timestamp_query(filter_): + continue + rewritten_filters.append(filter_) + + if self._first_record_creation_time != self._last_record_creation_time: + self._duplicate_timestamp_page_count = 0 + rewritten_filters.append( + self._get_paging_timestamp_query(self._last_record_creation_time) + ) + if isinstance(query, Bool): + rewritten_query = Bool( + filter=rewritten_filters, + must=query.must, + must_not=query.must_not, + should=query.should, + boost=query.boost, + minimum_should_match=query.minimum_should_match, + ) + else: + rewritten_filters.append(query) + rewritten_query = Bool(filter=rewritten_filters) + self._criteria.dsl.query = rewritten_query + else: + self._criteria.dsl.from_ = self._size * ( + self._duplicate_timestamp_page_count + 1 + ) + self._criteria.dsl.size = self._size + self._duplicate_timestamp_page_count += 1 + + @staticmethod + def _get_paging_timestamp_query(last_timestamp: int) -> Query: + return Range(field="createdAt", gt=last_timestamp) + + @staticmethod + def _is_paging_timestamp_query(filter_: Query) -> bool: + return ( + isinstance(filter_, Range) + and filter_.field == "createdAt" + and filter_.gt is not None + ) + + def _update_first_last_record_creation_times(self): + self._first_record_creation_time = self._last_record_creation_time = -2 + + if not isinstance(self._log_entries, list) or len(self._log_entries) <= 1: + return + + first_entry, last_entry = self._log_entries[0], self._log_entries[-1] + + if first_entry: + self._first_record_creation_time = first_entry.created_at + + if last_entry: + self._last_record_creation_time = last_entry.created_at + + @staticmethod + def presorted_by_timestamp(sorts: Optional[List[SortItem]]) -> bool: + """ + Checks if the sorting options prioritize creation time in ascending order. + :param sorts: list of sorting options or None. + :returns: True if sorting is already prioritized by creation time, False otherwise. + """ + if sorts and isinstance(sorts[0], SortItem): + return ( + sorts[0].field == "createdAt" and sorts[0].order == SortOrder.ASCENDING + ) + return False + + @staticmethod + def sort_by_timestamp_first(sorts: List[SortItem]) -> List[SortItem]: + """ + Rewrites the sorting options to ensure that + sorting by creation time, ascending, is the top + priority. + + :param sorts: list of sorting options + :returns: sorting options, making sorting by + creation time in ascending order the top priority + """ + creation_asc_sort = [SortItem("createdAt", order=SortOrder.ASCENDING)] + if not sorts: + return creation_asc_sort + + rewritten_sorts = [ + sort + for sort in sorts + if ((not sort.field) or (sort.field != "__timestamp")) + and (sort not in BY_TIMESTAMP) + ] + return creation_asc_sort + rewritten_sorts + + def __iter__(self) -> Generator[SearchLogEntry, None, None]: + """ + Iterates through the results, lazily-fetching each next page until there + are no more results. + + :returns: an iterable form of each result, across all pages + """ + while True: + yield from self.current_page() + if not self.next_page(): + break diff --git a/pyatlan_v9/model/serde.py b/pyatlan_v9/model/serde.py new file mode 100644 index 000000000..0472508c0 --- /dev/null +++ b/pyatlan_v9/model/serde.py @@ -0,0 +1,62 @@ +# Auto-generated support module for PythonMsgspecRenderer.pkl +"""Serialization/deserialization utilities using msgspec.""" + +from __future__ import annotations + +import datetime +from enum import Enum +from typing import Any, TypeVar + +import msgspec + +T = TypeVar("T") + + +def _enc_hook(obj: Any) -> Any: + """Handle custom types that msgspec cannot natively encode.""" + if obj.__class__.__name__ == "AtlanTagName": + return str(obj) + if isinstance(obj, Enum): + return obj.value + if isinstance(obj, datetime.date): + dt = datetime.datetime.combine(obj, datetime.time.min) + return int(dt.timestamp() * 1000) + if isinstance(obj, datetime.datetime): + return int(obj.timestamp() * 1000) + if hasattr(obj, "dict") and hasattr(obj, "__fields__"): + return obj.dict(by_alias=True, exclude_none=True) + raise NotImplementedError(f"Cannot serialize {type(obj)}") + + +class Serde: + """ + Serialization/deserialization helper using msgspec encoders/decoders. + + Reuses encoder/decoder instances for better performance. + """ + + def __init__(self) -> None: + self._encoder = msgspec.json.Encoder(enc_hook=_enc_hook) + self._decoders: dict[type[Any], msgspec.json.Decoder[Any]] = {} + + def encode(self, obj: Any) -> bytes: + """Encode an object to JSON bytes.""" + return self._encoder.encode(obj) + + def decode(self, data: bytes, type_: type[T]) -> T: + """Decode JSON bytes to the specified type.""" + if type_ not in self._decoders: + self._decoders[type_] = msgspec.json.Decoder(type_) + return self._decoders[type_].decode(data) # type: ignore[no-any-return] + + +# Singleton instance +_serde: Serde | None = None + + +def get_serde() -> Serde: + """Get the shared Serde singleton instance.""" + global _serde + if _serde is None: + _serde = Serde() + return _serde diff --git a/pyatlan_v9/model/sso.py b/pyatlan_v9/model/sso.py new file mode 100644 index 000000000..1724d2e90 --- /dev/null +++ b/pyatlan_v9/model/sso.py @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +from __future__ import annotations + +import json +from typing import Union + +import msgspec + + +class SSOMapperConfig(msgspec.Struct, kw_only=True, rename="camel", omit_defaults=True): + """Configuration for an SSO mapper.""" + + sync_mode: Union[str, None] = None + attributes: Union[str, None] = None + group_name: Union[str, None] = msgspec.field(default=None, name="group") + """Group name for the mapper.""" + attribute_name: Union[str, None] = msgspec.field( + default=None, name="attribute.name" + ) + attribute_value: Union[str, None] = msgspec.field( + default=None, name="attribute.value" + ) + attribute_friendly_name: Union[str, None] = msgspec.field( + default=None, name="attribute.friendly.name" + ) + attribute_values_regex: Union[str, None] = msgspec.field( + default=None, name="are.attribute.values.regex" + ) + + +class SSOMapper(msgspec.Struct, kw_only=True, rename="camel", omit_defaults=True): + """SSO identity provider mapper.""" + + id: Union[str, None] = None + name: Union[str, None] = None + identity_provider_mapper: str + identity_provider_alias: str + config: SSOMapperConfig + + def to_dict(self) -> dict: + """Serialize to dict, excluding fields with None/default values.""" + return json.loads(msgspec.json.encode(self)) diff --git a/pyatlan_v9/model/structs.py b/pyatlan_v9/model/structs.py new file mode 100644 index 000000000..7732c7f11 --- /dev/null +++ b/pyatlan_v9/model/structs.py @@ -0,0 +1,631 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +"""Struct classes for pyatlan_v9, migrated from Pydantic to msgspec.""" + +from __future__ import annotations + +from datetime import date, datetime +from typing import TYPE_CHECKING, Union + +import msgspec + +from pyatlan.model.enums import ( + AppWorkflowRunStatus, + AssetSmusMetadataFormStatus, + AtlanConnectorType, + DataQualityRuleThresholdUnit, + FormFieldDimension, + FormFieldType, + SourceCostUnitType, +) +from pyatlan.utils import select_optional_set_fields +from pyatlan_v9.model.assets.badge_condition import BadgeCondition + +if TYPE_CHECKING: + from pyatlan.cache.aio.source_tag_cache import AsyncSourceTagName + from pyatlan.cache.source_tag_cache import SourceTagName + from pyatlan.client.aio import AsyncAtlanClient + from pyatlan.client.atlan import AtlanClient + + +class AssetExternalDQMetadata(msgspec.Struct, kw_only=True, rename="camel"): + """Description""" + + asset_external_d_q_system_name: Union[str, None] = None + asset_external_d_q_source_logo: Union[str, None] = None + asset_external_d_q_source_url: Union[str, None] = None + asset_external_d_q_last_sync_run_at: Union[datetime, None] = None + asset_external_d_q_test_entity_name: Union[str, None] = None + asset_external_d_q_test_total_count: Union[int, None] = None + asset_external_d_q_test_last_run_success_count: Union[int, None] = None + asset_external_d_q_test_last_run_failure_count: Union[int, None] = None + asset_external_d_q_overall_score_value: Union[str, None] = None + asset_external_d_q_overall_score_type: Union[str, None] = None + asset_external_d_q_score_dimensions: Union[ + list[AssetExternalDQScoreBreakdownByDimension], None + ] = None + asset_external_d_q_tests: Union[list[AssetExternalDQTestDetails], None] = None + + +class MCRuleSchedule(msgspec.Struct, kw_only=True, rename="camel"): + """Description""" + + mc_rule_schedule_type: Union[str, None] = None + mc_rule_schedule_interval_in_minutes: Union[int, None] = None + mc_rule_schedule_start_time: Union[datetime, None] = None + mc_rule_schedule_crontab: Union[str, None] = None + + +class DbtJobRun(msgspec.Struct, kw_only=True, rename="camel"): + """Description""" + + dbt_job_id: Union[str, None] = None + dbt_job_name: Union[str, None] = None + dbt_environment_id: Union[str, None] = None + dbt_environment_name: Union[str, None] = None + dbt_job_run_id: Union[str, None] = None + dbt_job_run_completed_at: Union[datetime, None] = None + dbt_job_run_status: Union[str, None] = None + dbt_test_run_status: Union[str, None] = None + dbt_model_run_status: Union[str, None] = None + dbt_compiled_s_q_l: Union[str, None] = None + dbt_compiled_code: Union[str, None] = None + + +class AwsCloudWatchMetric(msgspec.Struct, kw_only=True, rename="camel"): + """Description""" + + aws_cloud_watch_metric_name: str + aws_cloud_watch_metric_scope: str + + +class Action(msgspec.Struct, kw_only=True, rename="camel"): + """Description""" + + task_action_fulfillment_url: Union[str, None] = None + task_action_fulfillment_method: Union[str, None] = None + task_action_fulfillment_payload: Union[str, None] = None + task_action_display_text: Union[str, None] = None + + +class Histogram(msgspec.Struct, kw_only=True, rename="camel"): + """Description""" + + boundaries: set[float] + frequencies: set[float] + + +class AssetExternalDQTestRunHistory(msgspec.Struct, kw_only=True, rename="camel"): + """Description""" + + asset_external_d_q_test_run_started_at: Union[datetime, None] = None + asset_external_d_q_test_run_ended_at: Union[datetime, None] = None + asset_external_d_q_test_run_status: Union[str, None] = None + asset_external_d_q_test_metric_info: Union[AssetExternalDQTestMetric, None] = None + + +class ColumnValueFrequencyMap(msgspec.Struct, kw_only=True, rename="camel"): + """Description""" + + column_value: Union[str, None] = None + column_value_frequency: Union[int, None] = None + + +class AssetExternalDQTestMetric(msgspec.Struct, kw_only=True, rename="camel"): + """Description""" + + asset_external_d_q_test_metric_observed_value: Union[str, None] = None + asset_external_d_q_test_metric_upper_bound: Union[str, None] = None + asset_external_d_q_test_metric_lower_bound: Union[str, None] = None + + +class SourceTagAttachmentValue(msgspec.Struct, kw_only=True, rename="camel"): + """Description""" + + tag_attachment_key: Union[str, None] = None + tag_attachment_value: Union[str, None] = None + + +class StarredDetails(msgspec.Struct, kw_only=True, rename="camel"): + """Description""" + + asset_starred_by: Union[str, None] = None + asset_starred_at: Union[datetime, None] = None + + +class AwsTag(msgspec.Struct, kw_only=True, rename="camel"): + """Description""" + + aws_tag_key: str + aws_tag_value: str + + +class GoogleTag(msgspec.Struct, kw_only=True, rename="camel"): + """Description""" + + google_tag_key: str + google_tag_value: str + + +class AssetExternalDQTestDetails(msgspec.Struct, kw_only=True, rename="camel"): + """Description""" + + asset_external_d_q_test_name: Union[str, None] = None + asset_external_d_q_test_id: Union[str, None] = None + asset_external_d_q_test_description: Union[str, None] = None + asset_external_d_q_test_schedule_type: Union[str, None] = None + asset_external_d_q_test_last_run_status: Union[str, None] = None + asset_external_d_q_test_runs: Union[list[AssetExternalDQTestRunHistory], None] = ( + None + ) + + +class BusinessPolicyRule(msgspec.Struct, kw_only=True, rename="camel"): + """Description""" + + bpr_id: Union[str, None] = None + bpr_name: Union[str, None] = None + bpr_sequence: Union[str, None] = None + bpr_operand: Union[str, None] = None + bpr_operator: Union[str, None] = None + bpr_value: Union[set[str], None] = None + bpr_query: Union[str, None] = None + + +class ResponseValue(msgspec.Struct, kw_only=True, rename="camel"): + """Description""" + + response_field_id: Union[str, None] = None + response_value_string: Union[str, None] = None + response_value_int: Union[int, None] = None + response_value_boolean: Union[bool, None] = None + response_value_json: Union[str, None] = None + response_value_long: Union[int, None] = None + response_value_date: Union[datetime, None] = None + response_value_arr_string: Union[set[str], None] = None + response_value_arr_int: Union[set[int], None] = None + response_value_arr_boolean: Union[set[bool], None] = None + response_value_arr_json: Union[set[str], None] = None + response_value_arr_long: Union[set[int], None] = None + response_value_arr_date: Union[set[datetime], None] = None + response_value_options: Union[dict[str, str], None] = None + + +class FormField(msgspec.Struct, kw_only=True, rename="camel"): + """Description""" + + form_field_id: Union[str, None] = None + form_field_name: Union[str, None] = None + form_field_type: Union[FormFieldType, None] = None + form_field_dimension: Union[FormFieldDimension, None] = None + form_field_options: Union[dict[str, str], None] = None + + +class DbtInputContext(msgspec.Struct, kw_only=True, rename="camel"): + """Description""" + + dbt_input_context_name: Union[str, None] = None + dbt_input_context_qualified_name: Union[str, None] = None + dbt_input_context_type: Union[str, None] = None + dbt_input_context_alias: Union[str, None] = None + dbt_input_context_filter: Union[str, None] = None + dbt_input_context_offset_window: Union[str, None] = None + dbt_input_context_offset_to_grain: Union[str, None] = None + + +class AssetSmusMetadataFormDetails(msgspec.Struct, kw_only=True, rename="camel"): + """Description""" + + asset_metadata_form_name: Union[str, None] = None + asset_metadata_form_description: Union[str, None] = None + asset_metadata_form_domain_id: Union[str, None] = None + asset_metadata_form_project_id: Union[str, None] = None + asset_metadata_form_status: Union[AssetSmusMetadataFormStatus, None] = None + asset_metadata_form_revision: Union[str, None] = None + asset_metadata_form_fields: Union[list[dict[str, str]], None] = None + + +class DatabricksAIModelVersionMetric(msgspec.Struct, kw_only=True, rename="camel"): + """Description""" + + databricks_a_i_model_version_metric_key: Union[str, None] = None + databricks_a_i_model_version_metric_value: Union[float, None] = None + databricks_a_i_model_version_metric_timestamp: Union[datetime, None] = None + databricks_a_i_model_version_metric_step: Union[int, None] = None + + +class KafkaTopicConsumption(msgspec.Struct, kw_only=True, rename="camel"): + """Description""" + + topic_name: Union[str, None] = None + topic_partition: Union[str, None] = None + topic_lag: Union[int, None] = None + topic_current_offset: Union[int, None] = None + + +class SQLProcedureReturn(msgspec.Struct, kw_only=True, rename="camel"): + """Description""" + + sql_return_type: Union[str, None] = None + sql_return_character_maximum_length: Union[int, None] = None + sql_return_character_octet_length: Union[int, None] = None + sql_return_numeric_precision: Union[int, None] = None + sql_return_numeric_precision_radix: Union[int, None] = None + + +class SourceTagAttachment(msgspec.Struct, kw_only=True, rename="camel"): + """Description""" + + source_tag_name: Union[str, None] = None + source_tag_qualified_name: Union[str, None] = None + source_tag_guid: Union[str, None] = None + source_tag_connector_name: Union[str, None] = None + source_tag_value: Union[list[SourceTagAttachmentValue], None] = None + is_source_tag_synced: Union[bool, None] = None + source_tag_sync_timestamp: Union[datetime, None] = None + source_tag_sync_error: Union[str, None] = None + source_tag_type: Union[str, None] = None + + @classmethod + def by_name( + cls, + client: AtlanClient, + name: SourceTagName, + source_tag_values: list[SourceTagAttachmentValue], + source_tag_sync_timestamp: Union[datetime, None] = None, + is_source_tag_synced: Union[bool, None] = None, + source_tag_sync_error: Union[str, None] = None, + ): + """ + Create a source-synced tag attachment with + a particular value when the attachment is synced to the source. + + :param client: connectivity to an Atlan tenant + :param name: unique name of the source tag in Atlan + :param source_tag_values: value of the tag attachment from the source + :param is_source_tag_synced: whether the tag attachment has been synced at the source (True) or not (False) + :param source_tag_sync_timestamp: time (epoch) when the tag attachment was synced at the source, in milliseconds + :param source_tag_sync_error: error message if the tag attachment sync at the source failed + :returns: a SourceTagAttachment with the provided information + :raises AtlanError: on any error communicating via the underlying APIs + :raises NotFoundError: if the source-synced tag cannot be resolved + """ + tag = client.source_tag_cache.get_by_name(name) + tag_connector_name = AtlanConnectorType._get_connector_type_from_qualified_name( + tag.qualified_name or "" + ) + return cls.of( + source_tag_name=tag.name, + source_tag_qualified_name=tag.qualified_name, + source_tag_guid=tag.guid, + source_tag_connector_name=tag_connector_name, + source_tag_values=source_tag_values, + **select_optional_set_fields( + dict( + is_source_tag_synced=is_source_tag_synced, + source_tag_sync_timestamp=source_tag_sync_timestamp, + source_tag_sync_error=source_tag_sync_error, + ) + ), + ) + + @classmethod + async def by_name_async( + cls, + client: AsyncAtlanClient, + name: AsyncSourceTagName, + source_tag_values: list[SourceTagAttachmentValue], + source_tag_sync_timestamp: Union[datetime, None] = None, + is_source_tag_synced: Union[bool, None] = None, + source_tag_sync_error: Union[str, None] = None, + ): + """ + Async version of by_name that creates a source-synced tag attachment with + a particular value when the attachment is synced to the source. + + :param client: async connectivity to an Atlan tenant + :param name: unique name of the source tag in Atlan + :param source_tag_values: value of the tag attachment from the source + :param is_source_tag_synced: whether the tag attachment has been synced at the source (True) or not (False) + :param source_tag_sync_timestamp: time (epoch) when the tag attachment was synced at the source, in milliseconds + :param source_tag_sync_error: error message if the tag attachment sync at the source failed + :returns: a SourceTagAttachment with the provided information + :raises AtlanError: on any error communicating via the underlying APIs + :raises NotFoundError: if the source-synced tag cannot be resolved + """ + tag = await client.source_tag_cache.get_by_name(name) + tag_connector_name = AtlanConnectorType._get_connector_type_from_qualified_name( + tag.qualified_name or "" + ) + return cls.of( + source_tag_name=tag.name, + source_tag_qualified_name=tag.qualified_name, + source_tag_guid=tag.guid, + source_tag_connector_name=tag_connector_name, + source_tag_values=source_tag_values, + **select_optional_set_fields( + dict( + is_source_tag_synced=is_source_tag_synced, + source_tag_sync_timestamp=source_tag_sync_timestamp, + source_tag_sync_error=source_tag_sync_error, + ) + ), + ) + + @classmethod + def by_qualified_name( + cls, + client: AtlanClient, + source_tag_qualified_name: str, + source_tag_values: list[SourceTagAttachmentValue], + source_tag_sync_timestamp: Union[datetime, None] = None, + is_source_tag_synced: Union[bool, None] = None, + source_tag_sync_error: Union[str, None] = None, + ): + """ + Create a source-synced tag attachment with + a particular value when the attachment is synced to the source. + + :param client: connectivity to an Atlan tenant + :param source_tag_qualified_name: unique name of the source tag in Atlan + :param source_tag_values: value of the tag attachment from the source + :param is_source_tag_synced: whether the tag attachment has been synced at the source (True) or not (False) + :param source_tag_sync_timestamp: time (epoch) when the tag attachment was synced at the source, in milliseconds + :param source_tag_sync_error: error message if the tag attachment sync at the source failed + :returns: a SourceTagAttachment with the provided information + :raises AtlanError: on any error communicating via the underlying APIs + :raises NotFoundError: if the source-synced tag cannot be resolved + """ + tag = client.source_tag_cache.get_by_qualified_name(source_tag_qualified_name) + tag_connector_name = AtlanConnectorType._get_connector_type_from_qualified_name( + source_tag_qualified_name or "" + ) + return cls.of( + source_tag_name=tag.name, + source_tag_qualified_name=source_tag_qualified_name, + source_tag_guid=tag.guid, + source_tag_connector_name=tag_connector_name, + source_tag_values=source_tag_values, + **select_optional_set_fields( + dict( + is_source_tag_synced=is_source_tag_synced, + source_tag_sync_timestamp=source_tag_sync_timestamp, + source_tag_sync_error=source_tag_sync_error, + ) + ), + ) + + @classmethod + def of( + cls, + source_tag_name: Union[str, None] = None, + source_tag_qualified_name: Union[str, None] = None, + source_tag_guid: Union[str, None] = None, + source_tag_connector_name: Union[str, None] = None, + source_tag_values: Union[list[SourceTagAttachmentValue], None] = None, + is_source_tag_synced: Union[bool, None] = None, + source_tag_sync_timestamp: Union[datetime, None] = None, + source_tag_sync_error: Union[str, None] = None, + ): + """ + Quickly create a new SourceTagAttachment. + + :param source_tag_name: simple name of the source tag + :param source_tag_qualified_name: unique name of the source tag in Atlan + :param source_tag_guid: unique identifier (GUID) of the source tag in Atlan + :param source_tag_connector_name: connector that is the source of the tag + :param source_tag_values: value of the tag attachment from the source + :param is_source_tag_synced: whether the tag attachment has been synced at the source (True) or not (False) + :param source_tag_sync_timestamp: time (epoch) when the tag attachment was synced at the source, in milliseconds + :param source_tag_sync_error: error message if the tag attachment sync at the source failed + :returns: a SourceTagAttachment with the provided information + """ + return SourceTagAttachment( + **select_optional_set_fields( + dict( + source_tag_name=source_tag_name, + source_tag_qualified_name=source_tag_qualified_name, + source_tag_guid=source_tag_guid, + source_tag_connector_name=source_tag_connector_name, + source_tag_value=source_tag_values, + is_source_tag_synced=is_source_tag_synced, + source_tag_sync_timestamp=source_tag_sync_timestamp, + source_tag_sync_error=source_tag_sync_error, + ) + ), + ) + + +class AzureTag(msgspec.Struct, kw_only=True, rename="camel"): + """Description""" + + azure_tag_key: str + azure_tag_value: str + + +class AssetExternalDQScoreBreakdownByDimension( + msgspec.Struct, kw_only=True, rename="camel" +): + """Description""" + + asset_external_d_q_score_dimension_name: Union[str, None] = None + asset_external_d_q_score_dimension_description: Union[str, None] = None + asset_external_d_q_score_dimension_score_value: Union[str, None] = None + asset_external_d_q_score_dimension_score_type: Union[str, None] = None + + +class AuthPolicyCondition(msgspec.Struct, kw_only=True, rename="camel"): + """Description""" + + policy_condition_type: str + policy_condition_values: set[str] + + +class DataQualityRuleConfigArguments(msgspec.Struct, kw_only=True, rename="camel"): + """Description""" + + dq_rule_threshold_object: Union[DataQualityRuleThresholdObject, None] = None + dq_rule_config_arguments_raw: Union[str, None] = None + dq_rule_config_rule_conditions: Union[str, None] = None + + +class SQLProcedureArgument(msgspec.Struct, kw_only=True, rename="camel"): + """Description""" + + sql_argument_name: Union[str, None] = None + sql_argument_type: Union[str, None] = None + + +class DbtMetricFilter(msgspec.Struct, kw_only=True, rename="camel"): + """Description""" + + dbt_metric_filter_column_qualified_name: Union[str, None] = None + dbt_metric_filter_field: Union[str, None] = None + dbt_metric_filter_operator: Union[str, None] = None + dbt_metric_filter_value: Union[str, None] = None + + +class AssetHistogram(msgspec.Struct, kw_only=True, rename="camel"): + """Description""" + + asset_histogram_boundaries: Union[set[float], None] = None + asset_histogram_frequencies: Union[set[float], None] = None + + +class DataQualityRuleTemplateConfig(msgspec.Struct, kw_only=True, rename="camel"): + """Description""" + + dq_rule_template_config_base_dataset_qualified_name: Union[str, None] = None + dq_rule_template_config_base_column_qualified_name: Union[str, None] = None + dq_rule_template_config_reference_dataset_qualified_names: Union[str, None] = None + dq_rule_template_config_reference_column_qualified_names: Union[str, None] = None + dq_rule_template_config_threshold_object: Union[str, None] = None + dq_rule_template_config_display_name: Union[str, None] = None + dq_rule_template_config_custom_s_q_l: Union[str, None] = None + dq_rule_template_config_dimension: Union[str, None] = None + dq_rule_template_config_user_description: Union[str, None] = None + dq_rule_template_config_advanced_settings: Union[str, None] = None + dq_rule_template_config_rule_conditions: Union[str, None] = None + dq_rule_template_config_preflight_check: Union[str, None] = None + + +class AppWorkflowRunStep(msgspec.Struct, kw_only=True, rename="camel"): + """Description""" + + app_workflow_run_label: Union[str, None] = None + app_workflow_run_status: Union[AppWorkflowRunStatus, None] = None + app_workflow_run_started_at: Union[datetime, None] = None + app_workflow_run_completed_at: Union[datetime, None] = None + app_workflow_run_outputs: Union[dict[str, str], None] = None + + +class AuthPolicyValiditySchedule(msgspec.Struct, kw_only=True, rename="camel"): + """Description""" + + policy_validity_schedule_start_time: str + policy_validity_schedule_end_time: str + policy_validity_schedule_timezone: str + + +class MCRuleComparison(msgspec.Struct, kw_only=True, rename="camel"): + """Description""" + + mc_rule_comparison_type: Union[str, None] = None + mc_rule_comparison_field: Union[str, None] = None + mc_rule_comparison_metric: Union[str, None] = None + mc_rule_comparison_operator: Union[str, None] = None + mc_rule_comparison_threshold: Union[float, None] = None + mc_rule_comparison_is_threshold_relative: Union[bool, None] = None + + +class DataQualityRuleThresholdObject(msgspec.Struct, kw_only=True, rename="camel"): + """Description""" + + dq_rule_threshold_compare_operator: Union[str, None] = None + dq_rule_threshold_value: Union[float, None] = None + dq_rule_threshold_unit: Union[DataQualityRuleThresholdUnit, None] = None + + +class GoogleLabel(msgspec.Struct, kw_only=True, rename="camel"): + """Description""" + + google_label_key: str + google_label_value: str + + +class PopularityInsights(msgspec.Struct, kw_only=True, rename="camel", frozen=False): + """Description""" + + record_user: Union[str, None] = None + record_query: Union[str, None] = None + record_query_duration: Union[int, None] = None + record_query_count: Union[int, None] = None + record_total_user_count: Union[int, None] = None + record_compute_cost: Union[float, None] = None + record_max_compute_cost: Union[float, None] = None + record_compute_cost_unit: Union[SourceCostUnitType, None] = None + record_last_timestamp: Union[int, None] = None + """Timestamp in epoch milliseconds.""" + record_warehouse: Union[str, None] = None + + def __post_init__(self): + """Convert date/datetime to epoch milliseconds if needed.""" + if isinstance(self.record_last_timestamp, datetime): + self.record_last_timestamp = int( + self.record_last_timestamp.timestamp() * 1000 + ) + elif isinstance(self.record_last_timestamp, date): + dt = datetime.combine(self.record_last_timestamp, datetime.min.time()) + self.record_last_timestamp = int(dt.timestamp() * 1000) + + +class SourceTagAttribute(msgspec.Struct, kw_only=True, rename="camel"): + """Description""" + + tag_attribute_key: Union[str, None] = None + tag_attribute_value: Union[str, None] = None + tag_attribute_properties: Union[dict[str, str], None] = None + + +__all__ = [ + "AssetExternalDQMetadata", + "MCRuleSchedule", + "DbtJobRun", + "AwsCloudWatchMetric", + "Action", + "Histogram", + "AssetExternalDQTestRunHistory", + "ColumnValueFrequencyMap", + "AssetExternalDQTestMetric", + "BadgeCondition", + "SourceTagAttachmentValue", + "StarredDetails", + "AwsTag", + "GoogleTag", + "AssetExternalDQTestDetails", + "BusinessPolicyRule", + "ResponseValue", + "FormField", + "DbtInputContext", + "AssetSmusMetadataFormDetails", + "DatabricksAIModelVersionMetric", + "KafkaTopicConsumption", + "SQLProcedureReturn", + "SourceTagAttachment", + "AzureTag", + "AssetExternalDQScoreBreakdownByDimension", + "AuthPolicyCondition", + "DataQualityRuleConfigArguments", + "SQLProcedureArgument", + "DbtMetricFilter", + "AssetHistogram", + "DataQualityRuleTemplateConfig", + "AppWorkflowRunStep", + "AuthPolicyValiditySchedule", + "MCRuleComparison", + "DataQualityRuleThresholdObject", + "GoogleLabel", + "PopularityInsights", + "SourceTagAttribute", +] diff --git a/pyatlan_v9/model/suggestions.py b/pyatlan_v9/model/suggestions.py new file mode 100644 index 000000000..f8cadce5d --- /dev/null +++ b/pyatlan_v9/model/suggestions.py @@ -0,0 +1,513 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Suggestions models for pyatlan_v9, migrated from pyatlan/model/suggestions.py. + +This module provides: +- SuggestionResponse: Response containing suggested metadata values. +- Suggestions: Builder for finding and applying metadata suggestions. +""" + +from __future__ import annotations + +from copy import deepcopy +from enum import Enum +from typing import TYPE_CHECKING, List, Optional + +import msgspec + +from pyatlan.model.aggregation import AggregationBucketResult, Aggregations +from pyatlan.model.fields.atlan_fields import AtlanField +from pyatlan.utils import validate_type +from pyatlan_v9.model.search import Query + +if TYPE_CHECKING: + from pyatlan.client.asset import Batch + from pyatlan.client.atlan import AtlanClient + from pyatlan_v9.model.assets import Asset + from pyatlan_v9.model.response import AssetMutationResponse + + +class SuggestionResponse(msgspec.Struct, kw_only=True): + """Response containing suggested metadata values for an asset.""" + + system_descriptions: List[SuggestionResponse.SuggestedItem] = msgspec.field( + default_factory=list + ) + """Suggested system descriptions.""" + + user_descriptions: List[SuggestionResponse.SuggestedItem] = msgspec.field( + default_factory=list + ) + """Suggested user descriptions.""" + + owner_users: List[SuggestionResponse.SuggestedItem] = msgspec.field( + default_factory=list + ) + """Suggested owner users.""" + + owner_groups: List[SuggestionResponse.SuggestedItem] = msgspec.field( + default_factory=list + ) + """Suggested owner groups.""" + + atlan_tags: List[SuggestionResponse.SuggestedItem] = msgspec.field( + default_factory=list + ) + """Suggested Atlan tags.""" + + assigned_terms: List[SuggestionResponse.SuggestedTerm] = msgspec.field( + default_factory=list + ) + """Suggested glossary terms.""" + + class SuggestedItem(msgspec.Struct, kw_only=True): + """A suggested metadata value with its occurrence count.""" + + count: int + """Number of occurrences of this suggestion.""" + + value: str + """The suggested value.""" + + class SuggestedTerm: + """A suggested glossary term with its occurrence count.""" + + count: int + """Number of occurrences of this suggestion.""" + + value: object # AtlasGlossaryTerm + """The suggested glossary term asset.""" + + def __init__(self, count: int, qualified_name: str): + from pyatlan_v9.model.assets import AtlasGlossaryTerm + + self.count = count + self.value = AtlasGlossaryTerm.ref_by_qualified_name(qualified_name) + + +class Suggestions: + """ + Builder for finding and applying metadata suggestions for an asset. + + Uses fluent interface pattern for building suggestion queries. + """ + + AGG_DESCRIPTION = "group_by_description" + AGG_USER_DESCRIPTION = "group_by_userDescription" + AGG_OWNER_USERS = "group_by_ownerUsers" + AGG_OWNER_GROUPS = "group_by_ownerGroups" + AGG_ATLAN_TAGS = "group_by_tags" + AGG_TERMS = "group_by_terms" + + class TYPE(str, Enum): + """Enum representing suggestion types.""" + + SYSTEM_DESCRIPTION = "SystemDescription" + USER_DESCRIPTION = "UserDescription" + INDIVIDUAL_OWNERS = "IndividualOwners" + GROUP_OWNERS = "GroupOwners" + TAGS = "Tags" + TERMS = "Terms" + + @classmethod + def all(cls): + """Return all suggestion types.""" + return list(map(lambda c: c.value, cls)) + + def __init__( + self, + asset: Optional[Asset] = None, + include_archived: bool = False, + includes: Optional[List[Suggestions.TYPE]] = None, + max_suggestions: int = 5, + with_other_types: Optional[List[str]] = None, + wheres: Optional[List[Query]] = None, + where_nots: Optional[List[Query]] = None, + ): + self.asset = asset + self.include_archived = include_archived + self.includes: List[Suggestions.TYPE] = includes or [] + self.max_suggestions = max_suggestions + self.with_other_types: List[str] = with_other_types or [] + self.wheres: List[Query] = wheres or [] + self.where_nots: List[Query] = where_nots or [] + + def _clone(self) -> Suggestions: + """Return a deep copy of the current Suggestions.""" + return deepcopy(self) + + def include_archive(self, include: bool) -> Suggestions: + """ + Add a criterion to specify whether to include archived + assets as part of the suggestions. + + :param include: whether to include archived assets + :returns: the Suggestions with this criterion added + """ + validate_type(name="include", _type=bool, value=include) + clone = self._clone() + clone.include_archived = include + return clone + + def include(self, type: Suggestions.TYPE) -> Suggestions: + """ + Add a criterion for which type(s) of suggestions to include. + + :param type: suggestion type to include + :returns: the Suggestions with this criterion added + """ + validate_type(name="types", _type=Suggestions.TYPE, value=type) + clone = self._clone() + clone.includes.append(type) + return clone + + def max_suggestion(self, value: int) -> Suggestions: + """ + Set the maximum number of suggestions to return. + + :param value: maximum number of suggestions + :returns: the Suggestions with this criterion added + """ + validate_type(name="value", _type=int, value=value) + clone = self._clone() + clone.max_suggestions = value + return clone + + def with_other_type(self, type: str) -> Suggestions: + """ + Add a single criterion to include another asset type in the suggestions. + + :param type: the asset type to include + :returns: the Suggestions with this criterion added + """ + validate_type(name="type", _type=str, value=type) + clone = self._clone() + clone.with_other_types.append(type) + return clone + + def where(self, query: Query) -> Suggestions: + """ + Add a single criterion that must be present on every search result. + + :param query: the query criterion + :returns: the Suggestions with this criterion added + """ + validate_type(name="query", _type=Query, value=query) + clone = self._clone() + clone.wheres.append(query) + return clone + + def where_not(self, query: Query) -> Suggestions: + """ + Add a single criterion that must not be present on any search result. + + :param query: the query criterion + :returns: the Suggestions with this criterion added + """ + validate_type(name="query", _type=Query, value=query) + clone = self._clone() + clone.where_nots.append(query) + return clone + + def finder(self, asset: Asset) -> Suggestions: + """ + Build a suggestion finder for the provided asset. + + :param asset: asset for which to find suggestions + :returns: the suggestion finder for the provided asset + """ + self.asset = asset + return self + + def get(self, client: AtlanClient) -> SuggestionResponse: + """ + Execute the suggestion search and return results. + + :param client: connectivity to an Atlan tenant + :returns: suggestion response with found suggestions + """ + from pyatlan_v9.model.assets import Asset + from pyatlan_v9.model.fluent_search import FluentSearch + + asset_name = "" + all_types: List[str] = [] + + if self.asset and self.asset.name: + asset_name = self.asset.name + all_types.append(self.asset.type_name) + + if self.with_other_types: + all_types.extend(self.with_other_types) + + search = ( + FluentSearch.select(include_archived=self.include_archived) + .where(Asset.TYPE_NAME.within(all_types)) + .where(Asset.NAME.eq(asset_name)) + .page_size(0) + .min_somes(1) + ) + + if self.wheres: + for condition in self.wheres: + search = search.where(condition) + + if self.where_nots: + for condition in self.where_nots: + search = search.where_not(condition) + + if not self.includes: + return SuggestionResponse() + + for incl in self.includes: + if incl == Suggestions.TYPE.SYSTEM_DESCRIPTION: + search = search.where_some(Asset.DESCRIPTION.has_any_value()).aggregate( + Suggestions.AGG_DESCRIPTION, + Asset.DESCRIPTION.bucket_by( + size=self.max_suggestions, include_source_value=True + ), + ) + elif incl == Suggestions.TYPE.USER_DESCRIPTION: + search = search.where_some( + Asset.USER_DESCRIPTION.has_any_value() + ).aggregate( + Suggestions.AGG_USER_DESCRIPTION, + Asset.USER_DESCRIPTION.bucket_by( + size=self.max_suggestions, include_source_value=True + ), + ) + elif incl == Suggestions.TYPE.INDIVIDUAL_OWNERS: + search = search.where_some(Asset.OWNER_USERS.has_any_value()).aggregate( + Suggestions.AGG_OWNER_USERS, + Asset.OWNER_USERS.bucket_by(self.max_suggestions), + ) + elif incl == Suggestions.TYPE.GROUP_OWNERS: + search = search.where_some( + Asset.OWNER_GROUPS.has_any_value() + ).aggregate( + Suggestions.AGG_OWNER_GROUPS, + Asset.OWNER_GROUPS.bucket_by(self.max_suggestions), + ) + elif incl == Suggestions.TYPE.TAGS: + search = search.where_some(Asset.ATLAN_TAGS.has_any_value()).aggregate( + Suggestions.AGG_ATLAN_TAGS, + Asset.ATLAN_TAGS.bucket_by(self.max_suggestions), + ) + elif incl == Suggestions.TYPE.TERMS: + search = search.where_some( + Asset.ASSIGNED_TERMS.has_any_value() + ).aggregate( + Suggestions.AGG_TERMS, + Asset.ASSIGNED_TERMS.bucket_by(self.max_suggestions), + ) + + search_request = search.to_request() + search_response = client.search(criteria=search_request) + aggregations = search_response.aggregations + suggestion_response = SuggestionResponse() + + for incl in self.includes: + self._build_response( + client, + incl, + suggestion_response, + aggregations, + ) + return suggestion_response + + def _get_descriptions(self, result: Aggregations, field: AtlanField): + """Extract description suggestions from aggregation results.""" + results = [] + if isinstance(result, AggregationBucketResult): + for bucket in result.buckets: + count = bucket.doc_count + value = bucket.get_source_value(field) + if count and value: + results.append( + SuggestionResponse.SuggestedItem(count=count, value=value) + ) + return results + + def _get_terms(self, result: Aggregations): + """Extract term suggestions from aggregation results.""" + results = [] + if isinstance(result, AggregationBucketResult): + for bucket in result.buckets: + count = bucket.doc_count + value = bucket.key + if count and value: + results.append( + SuggestionResponse.SuggestedTerm( + count=count, qualified_name=value + ) + ) + return results + + def _get_tags(self, client: AtlanClient, result: Aggregations): + """Extract tag suggestions from aggregation results.""" + results = [] + if isinstance(result, AggregationBucketResult): + for bucket in result.buckets: + count = bucket.doc_count + value = bucket.key + name = client.atlan_tag_cache.get_name_for_id(value) + if count and name: + results.append( + SuggestionResponse.SuggestedItem(count=count, value=name) + ) + return results + + def _get_others(self, result: Aggregations): + """Extract other suggestions from aggregation results.""" + results = [] + if isinstance(result, AggregationBucketResult): + for bucket in result.buckets: + count = bucket.doc_count + value = bucket.key + if count and value: + results.append( + SuggestionResponse.SuggestedItem(count=count, value=value) + ) + return results + + def _build_response(self, client, include, suggestion_response, aggregations): + """Build the suggestion response from aggregation results.""" + if include == Suggestions.TYPE.SYSTEM_DESCRIPTION: + suggestion_response.system_descriptions.extend( + self._get_descriptions( + aggregations.get(Suggestions.AGG_DESCRIPTION), + self._get_asset_description_field(), + ) + ) + elif include == Suggestions.TYPE.USER_DESCRIPTION: + suggestion_response.user_descriptions.extend( + self._get_descriptions( + aggregations.get(Suggestions.AGG_USER_DESCRIPTION), + self._get_asset_user_description_field(), + ) + ) + elif include == Suggestions.TYPE.INDIVIDUAL_OWNERS: + suggestion_response.owner_users.extend( + self._get_others( + aggregations.get(Suggestions.AGG_OWNER_USERS), + ) + ) + elif include == Suggestions.TYPE.GROUP_OWNERS: + suggestion_response.owner_groups.extend( + self._get_others( + aggregations.get(Suggestions.AGG_OWNER_GROUPS), + ) + ) + elif include == Suggestions.TYPE.TAGS: + suggestion_response.atlan_tags.extend( + self._get_tags(client, aggregations.get(Suggestions.AGG_ATLAN_TAGS)) + ) + elif include == Suggestions.TYPE.TERMS: + suggestion_response.assigned_terms.extend( + self._get_terms( + aggregations.get(Suggestions.AGG_TERMS), + ) + ) + + @staticmethod + def _get_asset_description_field() -> AtlanField: + """Get the Asset.DESCRIPTION field.""" + from pyatlan_v9.model.assets import Asset + + return Asset.DESCRIPTION + + @staticmethod + def _get_asset_user_description_field() -> AtlanField: + """Get the Asset.USER_DESCRIPTION field.""" + from pyatlan_v9.model.assets import Asset + + return Asset.USER_DESCRIPTION + + def apply( + self, + client: AtlanClient, + allow_multiple: bool = False, + batch: Optional[Batch] = None, + ) -> Optional[AssetMutationResponse]: + """ + Find the requested suggestions and apply the top suggestions as changes to the asset. + + :param client: client connectivity to an Atlan tenant + :param allow_multiple: if True, allow multiple suggestions to be applied + :param batch: optional batch in which to apply the suggestions + :returns: mutation response if not using batch + """ + if batch: + return batch.add(self._apply(client, allow_multiple).asset) + result = self._apply(client, allow_multiple) + return client.save(result.asset, result.include_tags) + + def _apply(self, client: AtlanClient, allow_multiple: bool) -> _Apply: + """Apply suggestions to the asset.""" + from pyatlan_v9.model.core import AtlanTag, AtlanTagName + + response = self.get(client) + asset = self.asset.trim_to_required() # type: ignore[union-attr] + + description_to_apply = self._get_description_to_apply(response) + asset.user_description = description_to_apply + + if response.owner_groups: + if allow_multiple: + asset.owner_groups = {group.value for group in response.owner_groups} + else: + asset.owner_groups = {response.owner_groups[0].value} + + if response.owner_users: + if allow_multiple: + asset.owner_users = {user.value for user in response.owner_users} + else: + asset.owner_users = {response.owner_users[0].value} + + includes_tags = False + if response.atlan_tags: + includes_tags = True + if allow_multiple: + asset.atlan_tags = [ + AtlanTag(type_name=AtlanTagName(tag.value), propagate=False) + for tag in response.atlan_tags + ] + else: + asset.atlan_tags = [ + AtlanTag( + type_name=AtlanTagName(response.atlan_tags[0].value), + propagate=False, + ) + ] + + if response.assigned_terms: + if allow_multiple: + asset.assigned_terms = [term.value for term in response.assigned_terms] + else: + asset.assigned_terms = [response.assigned_terms[0].value] + + return _Apply(asset, includes_tags) + + def _get_description_to_apply(self, response: SuggestionResponse) -> Optional[str]: + """Determine the best description to apply from suggestions.""" + max_description_count = 0 + description_to_apply = None + + if response.user_descriptions: + max_description_count = response.user_descriptions[0].count + description_to_apply = response.user_descriptions[0].value + + if response.system_descriptions: + if response.system_descriptions[0].count > max_description_count: + description_to_apply = response.system_descriptions[0].value + + return description_to_apply + + +class _Apply: + """Internal helper to hold the asset and tag inclusion flag.""" + + def __init__(self, asset: Asset, include_tags: bool): + self.asset = asset + self.include_tags = include_tags diff --git a/pyatlan_v9/model/task.py b/pyatlan_v9/model/task.py new file mode 100644 index 000000000..4bcf5eb43 --- /dev/null +++ b/pyatlan_v9/model/task.py @@ -0,0 +1,189 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +from __future__ import annotations + +import json as json_lib +from typing import Any, ClassVar, Dict, Generator, List, Union + +import msgspec + +from pyatlan.errors import ErrorCode +from pyatlan.model.enums import AtlanTaskStatus, AtlanTaskType +from pyatlan.model.fields.atlan_fields import KeywordField, NumericField, TextField +from pyatlan_v9.model.search import DSL + + +class AtlanTask(msgspec.Struct, kw_only=True, rename="camel"): + """Representation of a task in Atlan's task queue.""" + + TYPE: ClassVar[KeywordField] = KeywordField("type", "__task_type") + """Type of the task.""" + GUID: ClassVar[KeywordField] = KeywordField("guid", "__task_guid") + """Unique identifier of the task.""" + CREATED_BY: ClassVar[KeywordField] = KeywordField("createdBy", "__task_createdBy") + """User who created the task.""" + CREATED_TIME: ClassVar[NumericField] = NumericField( + "createdTime", "__task_timestamp" + ) + """Time (epoch) at which the task was created, in milliseconds.""" + UPDATED_TIME: ClassVar[NumericField] = NumericField( + "updatedTime", "__task_modificationTimestamp" + ) + """Time (epoch) at which the task was last updated, in milliseconds.""" + START_TIME: ClassVar[NumericField] = NumericField("startTime", "__task_startTime") + """Time (epoch) at which the task was started, in milliseconds.""" + END_TIME: ClassVar[NumericField] = NumericField("endTime", "__task_endTime") + """Time (epoch) at which the task was ended, in milliseconds.""" + TIME_TAKEN_IN_SECONDS: ClassVar[NumericField] = NumericField( + "timeTakenInSeconds", "__task_timeTakenInSeconds" + ) + """Total time taken to complete the task, in seconds.""" + ATTEMPT_COUNT: ClassVar[NumericField] = NumericField( + "attemptCount", "__task_attemptCount" + ) + """Number of times the task has been attempted.""" + STATUS: ClassVar[TextField] = TextField("status", "__task_status") + """Status of the task.""" + CLASSIFICATION_ID: ClassVar[KeywordField] = KeywordField( + "classificationId", "__task_classificationId" + ) + ENTITY_GUID: ClassVar[KeywordField] = KeywordField( + "entityGuid", "__task_entityGuid" + ) + """Unique identifier of the asset the task originated from.""" + + type: Union[AtlanTaskType, None] = None + """Type of the task.""" + guid: Union[str, None] = None + """Unique identifier of the task.""" + created_by: Union[str, None] = None + """User who created the task.""" + created_time: Union[int, None] = None + """Time (epoch) at which the task was created, in milliseconds.""" + updated_time: Union[int, None] = None + """Time (epoch) at which the task was last updated, in milliseconds.""" + start_time: Union[int, None] = None + """Time (epoch) at which the task was started, in milliseconds.""" + end_time: Union[int, None] = None + """Time (epoch) at which the task was ended, in milliseconds.""" + time_taken_in_seconds: Union[int, None] = None + """Total time taken to complete the task, in seconds.""" + parameters: Union[dict[str, Any], None] = None + """Parameters used for running the task.""" + attempt_count: Union[int, None] = None + """Number of times the task has been attempted.""" + status: Union[AtlanTaskStatus, None] = None + """Status of the task.""" + classification_id: Union[str, None] = msgspec.field( + default=None, name="tagTypeName" + ) + entity_guid: Union[str, None] = None + """Unique identifier of the asset the task originated from.""" + + +class TaskSearchRequest(msgspec.Struct, kw_only=True): + """Class from which to configure and run a search against Atlan's task queue.""" + + dsl: DSL + attributes: List[str] = msgspec.field(default_factory=list) + + def to_dict( + self, + by_alias: bool = True, + exclude_none: bool = True, + ) -> Dict[str, Any]: + """Serialize TaskSearchRequest to dict.""" + d: Dict[str, Any] = { + "attributes": self.attributes, + "dsl": self.dsl.to_dict(by_alias=by_alias, exclude_none=exclude_none), + } + if exclude_none: + d = {k: v for k, v in d.items() if v is not None} + return d + + def json( + self, + by_alias: bool = True, + exclude_none: bool = True, + exclude_unset: bool = False, + ) -> str: + """Serialize TaskSearchRequest to JSON string.""" + return json_lib.dumps( + self.to_dict(by_alias=by_alias, exclude_none=exclude_none) + ) + + +class TaskSearchResponse: + """Captures the response from a search against Atlan's task queue.""" + + def __init__( + self, + client: Any, + endpoint: Any, + criteria: Any, + start: int, + size: int, + count: int, + tasks: list[AtlanTask], + aggregations: Any, + ): + self._client = client + self._endpoint = endpoint + self._criteria = criteria + self._start = start + self._size = size + self._count = count + self._tasks = tasks + self._aggregations = aggregations + + @property + def count(self) -> int: + """Total count of matching tasks.""" + return self._count + + def current_page(self) -> list[AtlanTask]: + """Retrieve the current page of results.""" + return self._tasks + + def next_page(self, start=None, size=None) -> bool: + """Advance to the next page of results.""" + self._start = start or self._start + self._size + if size: + self._size = size + return self._get_next_page() if self._tasks else False + + def _get_next_page(self) -> bool: + """Fetch the next page of results.""" + self._criteria.dsl.from_ = self._start + self._criteria.dsl.size = self._size + if raw_json := self._get_next_page_json(): + self._count = raw_json.get("approximateCount", 0) + return True + return False + + def _get_next_page_json(self) -> Union[dict, None]: + """Fetch the next page of results and return raw JSON.""" + raw_json = self._client._call_api( + self._endpoint, + request_obj=self._criteria, + ) + if "tasks" not in raw_json or not raw_json["tasks"]: + self._tasks = [] + return None + try: + self._tasks = msgspec.convert( + raw_json["tasks"], list[AtlanTask], strict=False + ) + return raw_json + except Exception as err: + raise ErrorCode.JSON_ERROR.exception_with_parameters( + raw_json, 200, str(err) + ) from err + + def __iter__(self) -> Generator[AtlanTask, None, None]: + """Iterate through all pages of results.""" + while True: + yield from self.current_page() + if not self.next_page(): + break diff --git a/pyatlan_v9/model/transform.py b/pyatlan_v9/model/transform.py new file mode 100644 index 000000000..4598b9ec2 --- /dev/null +++ b/pyatlan_v9/model/transform.py @@ -0,0 +1,348 @@ +# Auto-generated by PythonMsgspecRenderer.pkl - DO NOT EDIT +""" +Transform utilities for converting between flattened SDK format and Atlas API format. + +The SDK uses flattened attributes (e.g., `entity.name`) while the Atlas API uses +nested attributes (e.g., `entity.attributes.name`). This module handles the +bidirectional conversion. + +Field name conversion (snake_case <-> camelCase) is handled automatically by +msgspec's rename="camel" configuration on the Asset base class. +""" + +from __future__ import annotations + +import importlib +import re +from typing import Any, TypeVar + +import msgspec + +from pyatlan_v9.model.assets.asset import Asset + +_T = TypeVar("_T") + +# Type registry - maps type names to classes +# Populated via @register_asset decorator or lazy loading in get_type() +_type_registry: dict[str, type] = {} + +# Track failed lazy imports to avoid repeated attempts +_failed_imports: set[str] = set() + + +def _get_type_name_default(cls: type) -> str | None: + """Extract the type_name default value from a msgspec Struct class. + + For msgspec Structs, class attributes become field descriptors, so we need + to use msgspec.structs.fields() to get the actual default value. + """ + try: + for field in msgspec.structs.fields(cls): + if field.name == "type_name": + default = field.default + if isinstance(default, str) and default != "UNSET": + return default + return None + except TypeError: + # Not a msgspec Struct - fall back to getattr + pass + + # Fallback for non-Struct classes + type_name = getattr(cls, "type_name", None) + if isinstance(type_name, str) and type_name != "UNSET": + return type_name + return None + + +def register_asset(cls: _T) -> _T: + """Decorator that registers an Asset subclass in the type registry. + + Use this decorator on Asset subclasses to enable automatic deserialization + to the correct type when fetching from the API. + + Example: + from pyatlan_v9.model.transform import register_asset + + @register_asset + class AtlasGlossaryTerm(Asset): + type_name: Union[str, UnsetType] = "AtlasGlossaryTerm" + ... + + The class is registered under its `type_name` field's default value. + Works with both msgspec Structs and regular classes. + """ + type_name = _get_type_name_default(cls) + if type_name: + _type_registry[type_name] = cls + return cls + + +def _type_name_to_module(type_name: str) -> str: + """Convert a type name to its module path. + + Converts CamelCase type names to snake_case module names. + Examples: + AtlasGlossaryTerm -> pyatlan.models.atlas_glossary_term + S3Bucket -> pyatlan.models.s3_bucket + APIField -> pyatlan.models.api_field + """ + # Handle consecutive capitals (e.g., API -> api, S3 -> s3) + # Insert underscore before a capital that's followed by lowercase + # or before a capital that follows a lowercase + s1 = re.sub(r"(.)([A-Z][a-z]+)", r"\1_\2", type_name) + snake_case = re.sub(r"([a-z0-9])([A-Z])", r"\1_\2", s1).lower() + return f"pyatlan_v9.model.assets.{snake_case}" + + +def get_type(type_name: str) -> type: + """Get the Python class for an Atlas type name. + + This uses a hybrid approach: + 1. Fast path: Return from registry if already registered (via decorator or previous load) + 2. Lazy load: If not registered, derive module name and import on-demand + + Returns the registered class if found, otherwise falls back to Asset. + """ + # Fast path: already registered + if type_name in _type_registry: + return _type_registry[type_name] + + # Skip if we already tried and failed to import this type + if type_name in _failed_imports: + return Asset + + # Lazy load: derive module from type name and import + module_name = _type_name_to_module(type_name) + try: + importlib.import_module(module_name) + # The @register_asset decorator should have registered the class + return _type_registry.get(type_name, Asset) + except ImportError: + # Module doesn't exist - remember this to avoid repeated attempts + _failed_imports.add(type_name) + return Asset + + +# Mapping of snake_case SDK field names to camelCase Atlas field names for top-level fields +TOP_LEVEL_MAPPING = { + "guid": "guid", + "type_name": "typeName", + "status": "status", + "created_by": "createdBy", + "create_time": "createTime", + "updated_by": "updatedBy", + "update_time": "updateTime", + "version": "version", +} + +# Reverse mapping for decoding (camelCase -> snake_case) +TOP_LEVEL_MAPPING_REVERSE = {v: k for k, v in TOP_LEVEL_MAPPING.items()} + + +def to_atlas_format(asset: Asset) -> dict[str, Any]: + """Convert a flattened msgspec Struct to Atlas API format. + + Takes an SDK entity with flattened attributes and converts it to the + nested format expected by the Atlas API. + + Args: + asset: The Asset instance to convert. + + Returns: + A dictionary in Atlas API format with nested attributes. + """ + # Use msgspec to encode to JSON bytes, then decode to dict + # The Struct's rename="camel" handles snake_case -> camelCase conversion + json_bytes = msgspec.json.encode(asset) + data = msgspec.json.decode(json_bytes, type=dict) + + # Restructure: move non-top-level fields into attributes + # Use type_name instance attribute (or class default) to get the type name + type_name = getattr(asset, "type_name", None) or type(asset).__name__ + result: dict[str, Any] = {"typeName": type_name} + attributes: dict[str, Any] = {} + + # These fields should remain at the top level in Atlas API format + # (outside of the 'attributes' object) + top_level_keys = { + "guid", + "typeName", + "status", + "createdBy", + "createTime", + "updatedBy", + "updateTime", + "version", + # Metadata fields that should be top-level + "meanings", + "classifications", + "classificationNames", + "labels", + "businessAttributes", + "customAttributes", + "pendingTasks", + # Special add/remove/update fields + "addOrUpdateClassifications", + "removeClassifications", + } + + # Special handling for meanings with semantic + # Need to check if meanings have semantic field to determine placement + if "meanings" in data and data["meanings"]: + meanings_list = data["meanings"] + # Group meanings by semantic + append_meanings = [] + remove_meanings = [] + replace_meanings = [] + + for meaning in meanings_list: + if isinstance(meaning, dict): + semantic = meaning.get("semantic") + # Remove semantic from the meaning object before sending to API + meaning_copy = {k: v for k, v in meaning.items() if k != "semantic"} + + if semantic == "APPEND": + append_meanings.append(meaning_copy) + elif semantic == "REMOVE": + remove_meanings.append(meaning_copy) + else: # REPLACE or no semantic + replace_meanings.append(meaning_copy) + else: + # Not a dict, just use as-is for REPLACE + replace_meanings.append(meaning) + + # Set the appropriate field based on semantic + if append_meanings: + if "appendRelationshipAttributes" not in result: + result["appendRelationshipAttributes"] = {} + result["appendRelationshipAttributes"]["meanings"] = append_meanings + if remove_meanings: + if "removeRelationshipAttributes" not in result: + result["removeRelationshipAttributes"] = {} + result["removeRelationshipAttributes"]["meanings"] = remove_meanings + if replace_meanings: + result["meanings"] = replace_meanings + + # Remove meanings from data so it doesn't get added again below + data.pop("meanings") + + for key, value in data.items(): + if value is None: + continue + if key in top_level_keys: + result[key] = value + else: + attributes[key] = value + + if attributes: + result["attributes"] = attributes + + return result + + +_NESTED_BUCKETS = frozenset( + ("attributes", "uniqueAttributes", "relationshipAttributes") +) + +_CAMEL_ABBREV_RE = re.compile(r"([A-Z]{2,})(?=[A-Z][a-z]|$)") + +# Keys that use explicit msgspec.field(name="...") with trailing uppercase +# (e.g. dataProductAssetsDSL). Normalization would turn them into Dsl; +# preserve so struct field name matches. +_PRESERVE_CAMEL_KEYS = frozenset({"dataProductAssetsDSL", "apiPathRawURI"}) + + +def _normalize_camel_key(key: str) -> str: + """Normalize uppercase abbreviations in camelCase keys for msgspec. + + msgspec's rename="camel" expects apiPathRawUri, not apiPathRawURI. + This converts trailing/mid uppercase runs like URI→Uri, DSL→Dsl, DQ→Dq. + Keys in _PRESERVE_CAMEL_KEYS are left unchanged so they match struct fields. + """ + if key in _PRESERVE_CAMEL_KEYS: + return key + return _CAMEL_ABBREV_RE.sub(lambda m: m.group(1).capitalize(), key) + + +def _flatten_entity_dict(data: dict[str, Any]) -> dict[str, Any]: + """Flatten one Atlas entity dict, merging ``attributes``, + ``uniqueAttributes``, and ``relationshipAttributes`` into the top + level. Nested entity-shaped values (dicts with ``typeName``) are + recursively flattened as well. + """ + flattened: dict[str, Any] = {} + + for key, value in data.items(): + if key in _NESTED_BUCKETS: + if isinstance(value, dict): + for k, v in value.items(): + flattened[_normalize_camel_key(k)] = v + else: + flattened[_normalize_camel_key(key)] = value + + for key, value in list(flattened.items()): + if isinstance(value, dict) and "typeName" in value: + flattened[key] = _flatten_entity_dict(value) + elif isinstance(value, list): + flattened[key] = [ + _flatten_entity_dict(item) + if isinstance(item, dict) and "typeName" in item + else item + for item in value + ] + + return flattened + + +def from_atlas_format(data: dict[str, Any]) -> Asset: + """Convert Atlas API format to a flattened msgspec Struct. + + Takes an entity from the Atlas API and converts it to the SDK's + flattened attribute format. + + Args: + data: A dictionary in Atlas API format. + + Returns: + The appropriate Asset subclass instance with flattened attributes. + """ + type_name = data.get("typeName", "Asset") + cls = get_type(type_name) + + flattened = _flatten_entity_dict(data) + + return msgspec.convert(flattened, cls, strict=False) + + +def from_atlas_json(json_bytes: bytes, type_name: str | None = None) -> Asset: # noqa: ARG001 + """Convert Atlas API JSON bytes directly to a flattened msgspec Struct. + + This is the fastest path - parses JSON and converts in minimal steps. + + Args: + json_bytes: Raw JSON bytes from the API response. + type_name: Optional type name hint (if known ahead of time). + + Returns: + The appropriate Asset subclass instance with flattened attributes. + """ + # First, decode to dict to extract structure + data = msgspec.json.decode(json_bytes, type=dict) + + # Handle wrapped response format {"entity": {...}} + if "entity" in data: + data = data["entity"] + + return from_atlas_format(data) + + +def to_bulk_payload(entities: list[Asset]) -> dict[str, Any]: + """Convert a list of entities to the bulk API payload format. + + Args: + entities: List of Asset instances to include in the payload. + + Returns: + A dictionary formatted for the /entity/bulk endpoint. + """ + return {"entities": [to_atlas_format(e) for e in entities]} diff --git a/pyatlan_v9/model/translators.py b/pyatlan_v9/model/translators.py new file mode 100644 index 000000000..98b748347 --- /dev/null +++ b/pyatlan_v9/model/translators.py @@ -0,0 +1,96 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2026 Atlan Pte. Ltd. + +"""Response translators for pyatlan_v9.""" + +from __future__ import annotations + +from abc import ABC, abstractmethod +from typing import Any + +import msgspec + +from pyatlan.model.constants import DELETED_ +from pyatlan_v9.model.structs import SourceTagAttachment + + +class BaseTranslator(ABC): + """Abstract response translator.""" + + @abstractmethod + def applies_to(self, data: dict[str, Any]) -> bool: + """Return whether this translator should process this dictionary.""" + + @abstractmethod + def translate(self, data: dict[str, Any]) -> dict[str, Any]: + """Translate the dictionary to a more user-friendly representation.""" + + +class AtlanTagTranslator(BaseTranslator): + """ + Translator that converts tag IDs into human-readable Atlan tag names. + + Also extracts source-tag attachments from classification attributes and + places them under `sourceTagAttachments` for easier consumption. + """ + + _TAG_ID = "tagId" + _TYPE_NAME = "typeName" + _SOURCE_ATTACHMENTS = "sourceTagAttachments" + _CLASSIFICATION_NAMES = {"classificationNames", "purposeClassifications"} + _CLASSIFICATION_KEYS = { + "classifications", + "addOrUpdateClassifications", + "removeClassifications", + } + + def __init__(self, client: Any): + self.client = client + + def applies_to(self, data: dict[str, Any]) -> bool: + """Check whether classification-related keys are present.""" + return any(key in data for key in self._CLASSIFICATION_NAMES) or any( + key in data for key in self._CLASSIFICATION_KEYS + ) + + def translate(self, data: dict[str, Any]) -> dict[str, Any]: + """Translate tag identifiers in-place on a copy of the provided dictionary.""" + from pyatlan_v9.model.core import AtlanTagName + + raw_json = data.copy() + + for key in self._CLASSIFICATION_NAMES: + if key in raw_json: + raw_json[key] = [ + AtlanTagName( + self.client.atlan_tag_cache.get_name_for_id(tag_id) or DELETED_ + ) + for tag_id in raw_json[key] + ] + + for key in self._CLASSIFICATION_KEYS: + if key not in raw_json: + continue + for classification in raw_json[key]: + tag_id = classification.get(self._TYPE_NAME) + if not tag_id: + continue + tag_name = self.client.atlan_tag_cache.get_name_for_id(tag_id) + classification[self._TYPE_NAME] = AtlanTagName( + tag_name if tag_name else DELETED_ + ) + classification[self._TAG_ID] = tag_id + + attr_id = self.client.atlan_tag_cache.get_source_tags_attr_id(tag_id) + if not attr_id: + continue + attributes = classification.get("attributes") + if not attributes or not attributes.get(attr_id): + continue + classification[self._SOURCE_ATTACHMENTS] = [ + msgspec.convert(source_tag["attributes"], type=SourceTagAttachment) + for source_tag in attributes.get(attr_id) + if isinstance(source_tag, dict) and source_tag.get("attributes") + ] + + return raw_json diff --git a/pyatlan_v9/model/typedef.py b/pyatlan_v9/model/typedef.py new file mode 100644 index 000000000..406bc2a54 --- /dev/null +++ b/pyatlan_v9/model/typedef.py @@ -0,0 +1,1565 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2022 Atlan Pte. Ltd. + +""" +Type definition models for pyatlan_v9, migrated from pyatlan/model/typedef.py. + +This module provides: +- TypeDef: Base type definition +- EnumDef: Enumeration type definitions with ElementDef +- AttributeDef: Custom metadata attribute definitions with Options +- RelationshipAttributeDef: Relationship attribute definitions +- StructDef: Struct type definitions +- AtlanTagDef: Classification/tag type definitions +- EntityDef: Entity type definitions +- RelationshipDef: Relationship type definitions +- CustomMetadataDef: Custom metadata (business metadata) type definitions +- TypeDefResponse: Response containing all type definitions +""" + +from __future__ import annotations + +import importlib +import json +import time +from typing import TYPE_CHECKING, Any, Dict, List, Optional, Set, Union + +import msgspec + +from pyatlan.errors import ErrorCode +from pyatlan.model.constants import ( + AIAssetTypes, + AssetTypes, + DomainTypes, + EntityTypes, + GlossaryTypes, + OtherAssetTypes, +) +from pyatlan.model.enums import ( + AtlanCustomAttributePrimitiveType, + AtlanIcon, + AtlanTagColor, + AtlanTypeCategory, + Cardinality, + IndexType, + TagIconType, +) +from pyatlan_v9.model.atlan_image import AtlanImage + +if TYPE_CHECKING: + from pyatlan.client.atlan import AtlanClient + +# ============================================================================= +# MODULE-LEVEL CONSTANTS +# ============================================================================= + +_complete_type_list: AssetTypes = { + "ADLSAccount", + "ADLSContainer", + "ADLSObject", + "AnaplanPage", + "AnaplanList", + "AnaplanLineItem", + "AnaplanWorkspace", + "AnaplanModule", + "AnaplanModel", + "AnaplanApp", + "AnaplanDimension", + "AnaplanView", + "APIObject", + "APIQuery", + "APIField", + "APIPath", + "APISpec", + "Application", + "ApplicationField", + "Collection", + "Query", + "BIProcess", + "Badge", + "Column", + "ColumnProcess", + "Connection", + "CustomEntity", + "DataStudioAsset", + "DataverseAttribute", + "DataverseEntity", + "Database", + "DbtColumnProcess", + "DbtMetric", + "DbtModel", + "DbtModelColumn", + "DbtProcess", + "DbtSource", + "Folder", + "GCSBucket", + "GCSObject", + "Insight", + "KafkaConsumerGroup", + "KafkaTopic", + "Process", + "Link", + "LookerDashboard", + "LookerExplore", + "LookerField", + "LookerFolder", + "LookerLook", + "LookerModel", + "LookerProject", + "LookerQuery", + "LookerTile", + "LookerView", + "MCIncident", + "MCMonitor", + "MaterialisedView", + "MetabaseCollection", + "MetabaseDashboard", + "MetabaseQuestion", + "ModeChart", + "ModeCollection", + "ModeQuery", + "ModeReport", + "ModeWorkspace", + "PowerBIColumn", + "PowerBIDashboard", + "PowerBIDataflow", + "PowerBIDataset", + "PowerBIDatasource", + "PowerBIMeasure", + "PowerBIPage", + "PowerBIReport", + "PowerBITable", + "PowerBITile", + "PowerBIWorkspace", + "PresetChart", + "PresetDashboard", + "PresetDataset", + "PresetWorkspace", + "Procedure", + "QlikApp", + "QlikChart", + "QlikDataset", + "QlikSheet", + "QlikSpace", + "QlikStream", + "QuickSightAnalysis", + "QuickSightAnalysisVisual", + "QuickSightDashboard", + "QuickSightDashboardVisual", + "QuickSightDataset", + "QuickSightDatasetField", + "QuickSightFolder", + "Readme", + "ReadmeTemplate", + "RedashDashboard", + "RedashQuery", + "RedashVisualization", + "S3Bucket", + "S3Object", + "SalesforceDashboard", + "SalesforceField", + "SalesforceObject", + "SalesforceOrganization", + "SalesforceReport", + "Schema", + "SigmaDataElement", + "SigmaDataElementField", + "SigmaDataset", + "SigmaDatasetColumn", + "SigmaPage", + "SigmaWorkbook", + "SnowflakePipe", + "SnowflakeStream", + "SnowflakeTag", + "SupersetChart", + "SupersetDashboard", + "SupersetDataset", + "Table", + "TablePartition", + "TableauCalculatedField", + "TableauDashboard", + "TableauDatasource", + "TableauDatasourceField", + "TableauFlow", + "TableauMetric", + "TableauProject", + "TableauSite", + "TableauWorkbook", + "TableauWorksheet", + "ThoughtspotAnswer", + "ThoughtspotDashlet", + "ThoughtspotLiveboard", + "View", +} + +_all_glossary_types: GlossaryTypes = { + "AtlasGlossary", + "AtlasGlossaryCategory", + "AtlasGlossaryTerm", +} + +_all_domains: Set[str] = {"*/super"} + +_all_domain_types: DomainTypes = { + "DataDomain", + "DataProduct", +} + +_all_ai_asset_types: AIAssetTypes = {"AIApplication", "AIModel"} +_all_other_types: OtherAssetTypes = {"File"} + + +def _get_all_qualified_names(client: AtlanClient, asset_type: str) -> Set[str]: + """Retrieve all qualified names for assets of the given type.""" + from pyatlan_v9.model.assets import Asset + from pyatlan_v9.model.fluent_search import FluentSearch + + request = ( + FluentSearch.select() + .where(Asset.TYPE_NAME.eq(asset_type)) + .include_on_results(Asset.QUALIFIED_NAME) + .to_request() + ) + results = client.asset.search(request) + names = [result.qualified_name or "" for result in results] + return set(names) + + +# ============================================================================= +# TYPE DEFINITION BASE +# ============================================================================= + + +class TypeDef(msgspec.Struct, kw_only=True, rename="camel"): + """Base type definition.""" + + category: AtlanTypeCategory + """Type of the type definition.""" + + create_time: Union[int, None] = None + """Time (epoch) at which this object was created, in milliseconds.""" + + created_by: Union[str, None] = None + """Username of the user who created the object.""" + + description: Union[str, None] = None + """Description of the type definition.""" + + guid: Union[str, None] = None + """Unique identifier that represents the type definition.""" + + name: Union[str, None] = None + """Unique name of this type definition.""" + + type_version: Union[str, None] = None + """Internal use only.""" + + update_time: Union[int, None] = None + """Time (epoch) at which this object was last updated, in milliseconds.""" + + updated_by: Union[str, None] = None + """Username of the user who last updated the object.""" + + version: Union[int, None] = None + """Version of this type definition.""" + + +# ============================================================================= +# ENUM DEFINITION +# ============================================================================= + + +class EnumDef(TypeDef, kw_only=True): + """Enumeration type definition.""" + + class ElementDef(msgspec.Struct, kw_only=True, rename="camel"): + """One element (valid value) within an enumeration.""" + + value: str + """One unique value within the enumeration.""" + + description: Union[str, None] = None + """Unused.""" + + ordinal: Union[int, None] = None + """Unique numeric identifier for the value.""" + + @staticmethod + def of(ordinal: int, value: str) -> EnumDef.ElementDef: + """Create an element definition with the given ordinal and value.""" + from pyatlan_v9.utils import validate_required_fields + + validate_required_fields( + ["ordinal", "value"], + [ordinal, value], + ) + return EnumDef.ElementDef(ordinal=ordinal, value=value) + + @staticmethod + def list_from(values: List[str]) -> List[EnumDef.ElementDef]: + """Create a list of element definitions from a list of strings.""" + from pyatlan_v9.utils import validate_required_fields + + validate_required_fields( + ["values"], + [values], + ) + return [ + EnumDef.ElementDef.of(ordinal=i, value=values[i]) + for i in range(len(values)) + ] + + @staticmethod + def extend_elements(current: List[str], new: List[str]) -> List[str]: + """ + Extends the element definitions without duplications + and also retains the order of the current enum values. + + :param current: current list of element definitions. + :param new: list of new element definitions to be added. + :return: list of unique element definitions without duplications. + """ + unique_elements = set(current) + extended_list = current[:] + for element in new: + if element not in unique_elements: + extended_list.append(element) + unique_elements.add(element) + return extended_list + + category: AtlanTypeCategory = AtlanTypeCategory.ENUM + """Type category for enumeration definitions.""" + + element_defs: List[EnumDef.ElementDef] = msgspec.field(default_factory=list) + """Valid values for the enumeration.""" + + options: Union[Dict[str, Any], None] = None + """Optional properties of the type definition.""" + + service_type: Union[str, None] = None + """Internal use only.""" + + @staticmethod + def creator(name: str, values: List[str]) -> EnumDef: + """ + Builds the minimal object necessary to create an enumeration definition. + + :param name: display name the human-readable name for the enumeration + :param values: the list of additional valid values + (as strings) to add to the existing enumeration + :returns: the minimal object necessary to create the enumeration typedef + """ + from pyatlan_v9.utils import validate_required_fields + + validate_required_fields( + ["name", "values"], + [name, values], + ) + return EnumDef( + category=AtlanTypeCategory.ENUM, + name=name, + element_defs=EnumDef.ElementDef.list_from(values), + ) + + @staticmethod + def updater( + client: AtlanClient, name: str, values: List[str], replace_existing: bool + ) -> EnumDef: + """ + Builds the minimal object necessary to update an enumeration definition. + + :param client: connectivity to an Atlan tenant + :param name: display name the human-readable name for the enumeration + :param values: the list of additional valid values + (as strings) to add to the existing enumeration + :param replace_existing: if True, will replace all + existing values in the enumeration with the new ones; + or if False the new ones will be appended to the existing set + :returns: the minimal object necessary to update the enumeration typedef + """ + from pyatlan_v9.utils import validate_required_fields + + validate_required_fields( + ["name", "values", "replace_existing"], + [name, values, replace_existing], + ) + update_values = ( + values + if replace_existing + else EnumDef.ElementDef.extend_elements( + new=values, + current=client.enum_cache.get_by_name(str(name)).get_valid_values(), + ) + ) + return EnumDef( + name=name, + category=AtlanTypeCategory.ENUM, + element_defs=EnumDef.ElementDef.list_from(update_values), + ) + + @staticmethod + async def update_async( + client, name: str, values: List[str], replace_existing: bool + ) -> EnumDef: + """ + Builds the minimal object necessary to update an enumeration definition (async). + + :param client: connectivity to an Atlan tenant + :param name: display name the human-readable name for the enumeration + :param values: the list of additional valid values + (as strings) to add to the existing enumeration + :param replace_existing: if True, will replace all + existing values in the enumeration with the new ones; + or if False the new ones will be appended to the existing set + :returns: the minimal object necessary to update the enumeration typedef + """ + from pyatlan_v9.utils import validate_required_fields + + validate_required_fields( + ["name", "values", "replace_existing"], + [name, values, replace_existing], + ) + update_values = ( + values + if replace_existing + else EnumDef.ElementDef.extend_elements( + new=values, + current=( + await client.enum_cache.get_by_name(str(name)) + ).get_valid_values(), + ) + ) + return EnumDef( + name=name, + category=AtlanTypeCategory.ENUM, + element_defs=EnumDef.ElementDef.list_from(update_values), + ) + + def get_valid_values(self) -> List[str]: + """Translate the element definitions into a simple list of strings.""" + return [one.value for one in self.element_defs] if self.element_defs else [] + + +# ============================================================================= +# ATTRIBUTE DEFINITION +# ============================================================================= + + +_OPTIONS_PARENT_MAP: dict[int, Any] = {} + + +class AttributeDef(msgspec.Struct, kw_only=True, rename="camel"): + """Custom metadata attribute definition.""" + + class Options(msgspec.Struct, kw_only=True, rename="camel", omit_defaults=True): + """Extensible options for a custom metadata attribute.""" + + custom_metadata_version: str = "v2" + """Indicates the version of the custom metadata structure.""" + + description: Union[str, None] = None + """Optional description of the attribute.""" + + applicable_entity_types: Union[str, None] = None + """Set of entities on which this attribute can be applied (JSON-encoded).""" + + custom_applicable_entity_types: Union[str, None] = None + """Deprecated: see applicable_asset_types, applicable_glossary_types.""" + + allow_search: Union[bool, None] = None + """Whether the attribute should be searchable (true) or not (false).""" + + max_str_length: Union[str, None] = None + """Maximum length allowed for a string value.""" + + allow_filtering: Union[bool, None] = None + """Whether this attribute should appear in the filterable facets.""" + + multi_value_select: Union[bool, None] = None + """Whether this attribute can have multiple values.""" + + show_in_overview: Union[bool, None] = None + """Whether users will see this attribute in the overview tab.""" + + is_deprecated: Union[str, None] = None + """Whether the attribute is deprecated ('true') or not.""" + + is_enum: Union[bool, None] = None + """Whether the attribute is an enumeration.""" + + enum_type: Union[str, None] = None + """Name of the enumeration (options), when the attribute is an enumeration.""" + + custom_type: Union[str, None] = None + """Used for Atlan-specific types like users, groups, url, and SQL.""" + + has_time_precision: Union[bool, None] = None + """If true for a date attribute, time-level precision is also available.""" + + is_archived: Union[bool, None] = None + """Whether the attribute has been deleted.""" + + archived_at: Union[int, None] = None + """When the attribute was deleted.""" + + archived_by: Union[str, None] = None + """User who deleted the attribute.""" + + is_soft_reference: Union[str, None] = None + """TBC""" + + is_append_on_partial_update: Union[str, None] = None + """TBC""" + + primitive_type: Union[str, None] = None + """Type of the attribute.""" + + applicable_connections: Union[str, None] = None + """Qualified names of connections to restrict the attribute (JSON-encoded).""" + + applicable_glossaries: Union[str, None] = None + """Qualified names of glossaries to restrict the attribute (JSON-encoded).""" + + applicable_domains: Union[str, None] = None + """Qualified names of domains to restrict the attribute (JSON-encoded).""" + + applicable_asset_types: Union[str, None] = msgspec.field( + default=None, name="assetTypesList" + ) + """Asset type names to restrict the attribute (JSON-encoded).""" + + applicable_glossary_types: Union[str, None] = msgspec.field( + default=None, name="glossaryTypeList" + ) + """Glossary type names to restrict the attribute (JSON-encoded).""" + + applicable_domain_types: Union[str, None] = msgspec.field( + default=None, name="domainTypesList" + ) + """Data product type names to restrict the attribute (JSON-encoded).""" + + applicable_ai_asset_types: Union[str, None] = msgspec.field( + default=None, name="aiAssetsTypeList" + ) + """AI asset type names to restrict the attribute (JSON-encoded).""" + + applicable_other_asset_types: Union[str, None] = msgspec.field( + default=None, name="otherAssetTypeList" + ) + """Other asset type names to restrict the attribute (JSON-encoded).""" + + is_rich_text: Union[bool, None] = False + """Whether this attribute supports rich text formatting.""" + + def __setattr__(self, name: str, value: object) -> None: + super().__setattr__(name, value) + if name == "multi_value_select" and value is True: + parent = _OPTIONS_PARENT_MAP.get(id(self)) + if parent is not None: + parent.cardinality = Cardinality.SET + if parent.type_name and "array<" not in str(parent.type_name): + parent.type_name = f"array<{parent.type_name}>" + + @staticmethod + def creator( + attribute_type: AtlanCustomAttributePrimitiveType, + options_name: Optional[str] = None, + ) -> AttributeDef.Options: + """Create options for a custom metadata attribute.""" + from pyatlan_v9.utils import validate_required_fields + + validate_required_fields( + ["type"], + [type], + ) + options = AttributeDef.Options( + custom_metadata_version="v2", + primitive_type=attribute_type.value, + applicable_entity_types='["Asset"]', + allow_search=False, + max_str_length="100000000", + allow_filtering=True, + multi_value_select=False, + show_in_overview=False, + is_enum=False, + is_rich_text=False, + ) + if attribute_type in ( + AtlanCustomAttributePrimitiveType.USERS, + AtlanCustomAttributePrimitiveType.GROUPS, + AtlanCustomAttributePrimitiveType.URL, + AtlanCustomAttributePrimitiveType.SQL, + ): + options.custom_type = attribute_type.value + elif attribute_type == AtlanCustomAttributePrimitiveType.OPTIONS: + options.is_enum = True + options.enum_type = options_name + elif attribute_type == AtlanCustomAttributePrimitiveType.RICH_TEXT: + options.is_rich_text = True + options.multi_value_select = False + return options + + is_new: Union[bool, None] = None + """Whether the attribute is being newly created.""" + + cardinality: Union[Cardinality, None] = None + """Whether the attribute allows a single or multiple values.""" + + constraints: Union[List[Dict[str, Any]], None] = None + """Internal use only.""" + + enum_values: Union[List[str], None] = None + """List of values for an enumeration.""" + + description: Union[str, None] = None + """Description of the attribute definition.""" + + default_value: Union[str, None] = None + """Default value for this attribute (if any).""" + + display_name: Union[str, None] = None + """Name to use within all user interactions through the UI.""" + + name: Union[str, None] = None + """Unique name of this attribute definition.""" + + include_in_notification: Union[bool, None] = None + """TBC""" + + index_type: Union[IndexType, None] = None + """Index type for the attribute.""" + + is_indexable: Union[bool, None] = None + """When true, values for this attribute will be indexed for searching.""" + + is_optional: Union[bool, None] = None + """When true, a value will not be required for this attribute.""" + + is_unique: Union[bool, None] = None + """When true, this attribute must be unique across all assets.""" + + options: Union[AttributeDef.Options, None] = msgspec.field(default_factory=Options) + """Extensible options for the attribute.""" + + search_weight: Union[float, None] = None + """TBC""" + + skip_scrubbing: Union[bool, None] = None + """When true, scrubbing of data will be skipped.""" + + type_name: Union[str, None] = None + """Type of this attribute.""" + + values_min_count: Union[float, None] = None + """Minimum number of values for this attribute.""" + + values_max_count: Union[float, None] = None + """Maximum number of values for this attribute.""" + + index_type_es_config: Union[Dict[str, Any], None] = msgspec.field( + default=None, name="indexTypeESConfig" + ) + """Internal use only.""" + + index_type_es_fields: Union[Dict[str, Dict[str, str]], None] = msgspec.field( + default=None, name="indexTypeESFields" + ) + """Internal use only.""" + + is_default_value_null: Union[bool, None] = None + """TBC""" + + def __post_init__(self): + """Register back-reference from Options to this AttributeDef.""" + if self.options is not None: + _OPTIONS_PARENT_MAP[id(self.options)] = self + + # --- Convenience property accessors --- + # These properties read/write from self.options and handle JSON encoding. + # In the legacy code, these were implemented via __setattr__ + _convenience_properties. + # In msgspec, we use @property descriptors that delegate to get_*/set_* methods. + + def _get_option_set(self, attr: str) -> Set[str]: + """Helper to parse a JSON-encoded set from options.""" + val = getattr(self.options, attr, None) if self.options else None + if val: + return set(json.loads(val)) + return set() + + def _set_option_json(self, attr: str, value: Set[str]) -> None: + """Helper to set a JSON-encoded set on options.""" + if self.options is None: + raise ErrorCode.MISSING_OPTIONS.exception_with_parameters() + setattr(self.options, attr, json.dumps(list(value))) + + def get_applicable_entity_types(self) -> EntityTypes: + """Set of entities on which this attribute can be applied.""" + return self._get_option_set("applicable_entity_types") + + def set_applicable_entity_types(self, entity_types: EntityTypes) -> None: + """Set the entities on which this attribute can be applied.""" + if not isinstance(entity_types, set): + raise ErrorCode.INVALID_PARAMETER_TYPE.exception_with_parameters( + "applicable_entity_types", EntityTypes + ) + self._set_option_json("applicable_entity_types", entity_types) + + def get_applicable_asset_types(self) -> Union[Set[str], AssetTypes]: + """Asset type names to which to restrict the attribute.""" + return self._get_option_set("applicable_asset_types") + + def set_applicable_asset_types( + self, asset_types: Union[Set[str], AssetTypes] + ) -> None: + """Set asset types to which to restrict the attribute.""" + if self.options is None: + raise ErrorCode.MISSING_OPTIONS.exception_with_parameters() + if not isinstance(asset_types, set): + raise ErrorCode.INVALID_PARAMETER_TYPE.exception_with_parameters( + "applicable_asset_types", AssetTypes + ) + # Validate asset type names against SDK asset classes + invalid_types = { + asset_type + for asset_type in asset_types + if not getattr( + importlib.import_module("pyatlan.model.assets"), asset_type, None + ) + } + if invalid_types: + raise ErrorCode.INVALID_PARAMETER_VALUE.exception_with_parameters( + invalid_types, "applicable_asset_types", "SDK asset types" + ) + self.options.applicable_asset_types = json.dumps(list(asset_types)) + + def get_applicable_glossary_types(self) -> GlossaryTypes: + """Glossary type names to which to restrict the attribute.""" + return self._get_option_set("applicable_glossary_types") + + def set_applicable_glossary_types(self, glossary_types: GlossaryTypes) -> None: + """Set glossary types to which to restrict the attribute.""" + if self.options is None: + raise ErrorCode.MISSING_OPTIONS.exception_with_parameters() + if not isinstance(glossary_types, set): + raise ErrorCode.INVALID_PARAMETER_TYPE.exception_with_parameters( + "applicable_glossary_types", GlossaryTypes + ) + if not glossary_types.issubset(_all_glossary_types): + raise ErrorCode.INVALID_PARAMETER_VALUE.exception_with_parameters( + glossary_types, "applicable_glossary_types", _all_glossary_types + ) + self.options.applicable_glossary_types = json.dumps(list(glossary_types)) + + def get_applicable_domain_types(self) -> DomainTypes: + """Data product type names to which to restrict the attribute.""" + return self._get_option_set("applicable_domain_types") + + def set_applicable_domain_types(self, domain_types: DomainTypes) -> None: + """Set domain types to which to restrict the attribute.""" + if self.options is None: + raise ErrorCode.MISSING_OPTIONS.exception_with_parameters() + if not isinstance(domain_types, set): + raise ErrorCode.INVALID_PARAMETER_TYPE.exception_with_parameters( + "applicable_domain_types", DomainTypes + ) + if not domain_types.issubset(_all_domain_types): + raise ErrorCode.INVALID_PARAMETER_VALUE.exception_with_parameters( + domain_types, "applicable_domain_types", _all_domain_types + ) + self.options.applicable_domain_types = json.dumps(list(domain_types)) + + def get_applicable_ai_asset_types(self) -> AIAssetTypes: + """AI asset type names to which this attribute is restricted.""" + return self._get_option_set("applicable_ai_asset_types") + + def set_applicable_ai_asset_types(self, ai_asset_types: AIAssetTypes) -> None: + """Set AI asset types to which to restrict the attribute.""" + if self.options is None: + raise ErrorCode.MISSING_OPTIONS.exception_with_parameters() + if not isinstance(ai_asset_types, set): + raise ErrorCode.INVALID_PARAMETER_TYPE.exception_with_parameters( + "applicable_ai_asset_types", AIAssetTypes + ) + if not ai_asset_types.issubset(_all_ai_asset_types): + raise ErrorCode.INVALID_PARAMETER_VALUE.exception_with_parameters( + ai_asset_types, "applicable_ai_asset_types", _all_ai_asset_types + ) + self.options.applicable_ai_asset_types = json.dumps(list(ai_asset_types)) + + def get_applicable_other_asset_types(self) -> OtherAssetTypes: + """Other asset type names to which to restrict the attribute.""" + return self._get_option_set("applicable_other_asset_types") + + def set_applicable_other_asset_types( + self, other_asset_types: OtherAssetTypes + ) -> None: + """Set other asset types to which to restrict the attribute.""" + if self.options is None: + raise ErrorCode.MISSING_OPTIONS.exception_with_parameters() + if not isinstance(other_asset_types, set): + raise ErrorCode.INVALID_PARAMETER_TYPE.exception_with_parameters( + "applicable_other_asset_types", OtherAssetTypes + ) + if not other_asset_types.issubset(_all_other_types): + raise ErrorCode.INVALID_PARAMETER_VALUE.exception_with_parameters( + other_asset_types, + "applicable_other_asset_types", + OtherAssetTypes, + ) + self.options.applicable_other_asset_types = json.dumps(list(other_asset_types)) + + def get_applicable_connections(self) -> Set[str]: + """Qualified names of connections to which to restrict the attribute.""" + return self._get_option_set("applicable_connections") + + def set_applicable_connections(self, connections: Set[str]) -> None: + """Set connections to which to restrict the attribute.""" + if self.options is None: + raise ErrorCode.MISSING_OPTIONS.exception_with_parameters() + if not isinstance(connections, set): + raise ErrorCode.INVALID_PARAMETER_TYPE.exception_with_parameters( + "applicable_connections", "Set[str]" + ) + self.options.applicable_connections = json.dumps(list(connections)) + + def get_applicable_glossaries(self) -> Set[str]: + """Qualified names of glossaries to which to restrict the attribute.""" + return self._get_option_set("applicable_glossaries") + + def set_applicable_glossaries(self, glossaries: Set[str]) -> None: + """Set glossaries to which to restrict the attribute.""" + if self.options is None: + raise ErrorCode.MISSING_OPTIONS.exception_with_parameters() + if not isinstance(glossaries, set): + raise ErrorCode.INVALID_PARAMETER_TYPE.exception_with_parameters( + "applicable_glossaries", "Set[str]" + ) + self.options.applicable_glossaries = json.dumps(list(glossaries)) + + def get_applicable_domains(self) -> Set[str]: + """Qualified names of domains to which to restrict the attribute.""" + return self._get_option_set("applicable_domains") + + def set_applicable_domains(self, domains: Set[str]) -> None: + """Set domains to which to restrict the attribute.""" + if self.options is None: + raise ErrorCode.MISSING_OPTIONS.exception_with_parameters() + if not isinstance(domains, set): + raise ErrorCode.INVALID_PARAMETER_TYPE.exception_with_parameters( + "applicable_domains", "Set[str]" + ) + self.options.applicable_domains = json.dumps(list(domains)) + + # --- Property descriptors for parity with legacy AttributeDef --- + + @property + def applicable_entity_types(self) -> EntityTypes: + """Set of entities on which this attribute can be applied.""" + return self.get_applicable_entity_types() + + @applicable_entity_types.setter + def applicable_entity_types(self, entity_types: EntityTypes) -> None: + self.set_applicable_entity_types(entity_types) + + @property + def applicable_asset_types(self) -> Union[Set[str], AssetTypes]: + """Asset type names to which to restrict the attribute.""" + return self.get_applicable_asset_types() + + @applicable_asset_types.setter + def applicable_asset_types(self, asset_types: Union[Set[str], AssetTypes]) -> None: + self.set_applicable_asset_types(asset_types) + + @property + def applicable_glossary_types(self) -> GlossaryTypes: + """Glossary type names to which to restrict the attribute.""" + return self.get_applicable_glossary_types() + + @applicable_glossary_types.setter + def applicable_glossary_types(self, glossary_types: GlossaryTypes) -> None: + self.set_applicable_glossary_types(glossary_types) + + @property + def applicable_domain_types(self) -> DomainTypes: + """Data product type names to which to restrict the attribute.""" + return self.get_applicable_domain_types() + + @applicable_domain_types.setter + def applicable_domain_types(self, domain_types: DomainTypes) -> None: + self.set_applicable_domain_types(domain_types) + + @property + def applicable_ai_asset_types(self) -> AIAssetTypes: + """AI asset type names to which this attribute is restricted.""" + return self.get_applicable_ai_asset_types() + + @applicable_ai_asset_types.setter + def applicable_ai_asset_types(self, ai_asset_types: AIAssetTypes) -> None: + self.set_applicable_ai_asset_types(ai_asset_types) + + @property + def applicable_other_asset_types(self) -> OtherAssetTypes: + """Other asset type names to which to restrict the attribute.""" + return self.get_applicable_other_asset_types() + + @applicable_other_asset_types.setter + def applicable_other_asset_types(self, other_asset_types: OtherAssetTypes) -> None: + self.set_applicable_other_asset_types(other_asset_types) + + @property + def applicable_connections(self) -> Set[str]: + """Qualified names of connections to which to restrict the attribute.""" + return self.get_applicable_connections() + + @applicable_connections.setter + def applicable_connections(self, connections: Set[str]) -> None: + self.set_applicable_connections(connections) + + @property + def applicable_glossaries(self) -> Set[str]: + """Qualified names of glossaries to which to restrict the attribute.""" + return self.get_applicable_glossaries() + + @applicable_glossaries.setter + def applicable_glossaries(self, glossaries: Set[str]) -> None: + self.set_applicable_glossaries(glossaries) + + @property + def applicable_domains(self) -> Set[str]: + """Qualified names of domains to which to restrict the attribute.""" + return self.get_applicable_domains() + + @applicable_domains.setter + def applicable_domains(self, domains: Set[str]) -> None: + self.set_applicable_domains(domains) + + @staticmethod + def creator( + client: AtlanClient, + display_name: str, + attribute_type: AtlanCustomAttributePrimitiveType, + multi_valued: bool = False, + options_name: Optional[str] = None, + applicable_connections: Optional[Set[str]] = None, + applicable_asset_types: Optional[Union[Set[str], AssetTypes]] = None, + applicable_glossaries: Optional[Set[str]] = None, + applicable_glossary_types: Optional[GlossaryTypes] = None, + applicable_other_asset_types: Optional[OtherAssetTypes] = None, + applicable_domains: Optional[Set[str]] = None, + applicable_domain_types: Optional[DomainTypes] = None, + applicable_ai_asset_types: Optional[AIAssetTypes] = None, + description: Optional[str] = None, + ) -> AttributeDef: + """ + Builds the minimal object necessary to create a custom metadata attribute. + + :param client: connectivity to an Atlan tenant + :param display_name: human-readable name for the attribute + :param attribute_type: type of the attribute + :param multi_valued: whether the attribute can have multiple values + :param options_name: name of the enumeration (if type is OPTIONS) + :param applicable_connections: connections where this attribute applies + :param applicable_asset_types: asset types where this attribute applies + :param applicable_glossaries: glossaries where this attribute applies + :param applicable_glossary_types: glossary types where this attribute applies + :param applicable_other_asset_types: other asset types where this attribute applies + :param applicable_domains: domains where this attribute applies + :param applicable_domain_types: domain types where this attribute applies + :param applicable_ai_asset_types: AI asset types where this attribute applies + :param description: description of the attribute + :returns: AttributeDef configured for the specified parameters + """ + from pyatlan_v9.utils import validate_required_fields + + validate_required_fields( + ["display_name", "attribute_type"], + [display_name, attribute_type], + ) + # RichText attributes cannot be multi-valued + if ( + attribute_type == AtlanCustomAttributePrimitiveType.RICH_TEXT + and multi_valued + ): + raise ErrorCode.INVALID_RICH_TEXT_CREATION.exception_with_parameters( + display_name + ) + attr_def = AttributeDef( + display_name=display_name, + options=AttributeDef.Options.creator( + attribute_type=attribute_type, options_name=options_name + ), + is_new=True, + cardinality=Cardinality.SINGLE, + description=description, + name="", + include_in_notification=False, + is_indexable=True, + is_optional=True, + is_unique=False, + values_min_count=0, + values_max_count=1, + ) + add_enum_values = attribute_type == AtlanCustomAttributePrimitiveType.OPTIONS + if attribute_type == AtlanCustomAttributePrimitiveType.OPTIONS: + base_type = options_name + elif attribute_type in ( + AtlanCustomAttributePrimitiveType.USERS, + AtlanCustomAttributePrimitiveType.GROUPS, + AtlanCustomAttributePrimitiveType.URL, + AtlanCustomAttributePrimitiveType.SQL, + ): + base_type = AtlanCustomAttributePrimitiveType.STRING.value + else: + base_type = attribute_type.value + if multi_valued: + attr_def.type_name = f"array<{str(base_type)}>" + attr_def.options.multi_value_select = True # type: ignore[union-attr] + attr_def.cardinality = Cardinality.SET + else: + attr_def.type_name = base_type + if add_enum_values: + if enum_def := client.enum_cache.get_by_name(str(options_name)): + attr_def.enum_values = enum_def.get_valid_values() + else: + attr_def.enum_values = [] + + attr_def.set_applicable_asset_types( + applicable_asset_types or _complete_type_list + ) + attr_def.set_applicable_glossary_types( + applicable_glossary_types or _all_glossary_types + ) + attr_def.set_applicable_domain_types( + applicable_domain_types or _all_domain_types + ) + attr_def.set_applicable_other_asset_types( + applicable_other_asset_types or _all_other_types + ) + attr_def.set_applicable_connections( + applicable_connections or _get_all_qualified_names(client, "Connection") + ) + attr_def.set_applicable_glossaries( + applicable_glossaries or _get_all_qualified_names(client, "AtlasGlossary") + ) + attr_def.set_applicable_domains(applicable_domains or _all_domains) + attr_def.set_applicable_ai_asset_types(applicable_ai_asset_types or set()) + return attr_def + + @staticmethod + async def create_async( + client, # AsyncAtlanClient + display_name: str, + attribute_type: AtlanCustomAttributePrimitiveType, + multi_valued: bool = False, + options_name: Optional[str] = None, + applicable_connections: Optional[Set[str]] = None, + applicable_asset_types: Optional[Union[Set[str], AssetTypes]] = None, + applicable_glossaries: Optional[Set[str]] = None, + applicable_glossary_types: Optional[GlossaryTypes] = None, + applicable_other_asset_types: Optional[OtherAssetTypes] = None, + applicable_domains: Optional[Set[str]] = None, + applicable_domain_types: Optional[DomainTypes] = None, + applicable_ai_asset_types: Optional[AIAssetTypes] = None, + description: Optional[str] = None, + ) -> AttributeDef: + """ + Create an AttributeDef with async client support. + + :param client: AsyncAtlanClient instance + :param display_name: human-readable name for the attribute + :param attribute_type: type of the attribute + :param multi_valued: whether the attribute can have multiple values + :param options_name: name of the enumeration (if type is OPTIONS) + :param applicable_connections: connections where this attribute applies + :param applicable_asset_types: asset types where this attribute applies + :param applicable_glossaries: glossaries where this attribute applies + :param applicable_glossary_types: glossary types where this attribute applies + :param applicable_other_asset_types: other asset types where this attribute applies + :param applicable_domains: domains where this attribute applies + :param applicable_domain_types: domain types where this attribute applies + :param applicable_ai_asset_types: AI asset types where this attribute applies + :param description: description of the attribute + :returns: AttributeDef configured for the specified parameters + """ + from pyatlan_v9.utils import validate_required_fields + + validate_required_fields( + ["display_name", "attribute_type"], + [display_name, attribute_type], + ) + if ( + attribute_type == AtlanCustomAttributePrimitiveType.RICH_TEXT + and multi_valued + ): + raise ErrorCode.INVALID_RICH_TEXT_CREATION.exception_with_parameters( + display_name + ) + + async def _get_all_qualified_names_async(asset_type: str): + from pyatlan_v9.model.assets import Asset + from pyatlan_v9.model.fluent_search import FluentSearch + + request = ( + FluentSearch.select() + .where(Asset.TYPE_NAME.eq(asset_type)) + .include_on_results(Asset.QUALIFIED_NAME) + .to_request() + ) + results = await client.asset.search(request) + names = [] + async for result in results: + names.append(result.qualified_name or "") + return set(names) + + attr_def = AttributeDef( + display_name=display_name, + options=AttributeDef.Options.creator( + attribute_type=attribute_type, options_name=options_name + ), + is_new=True, + cardinality=Cardinality.SINGLE, + description=description, + name="", + include_in_notification=False, + is_indexable=True, + is_optional=True, + is_unique=False, + values_min_count=0, + values_max_count=1, + ) + add_enum_values = attribute_type == AtlanCustomAttributePrimitiveType.OPTIONS + if attribute_type == AtlanCustomAttributePrimitiveType.OPTIONS: + base_type = options_name + elif attribute_type in ( + AtlanCustomAttributePrimitiveType.USERS, + AtlanCustomAttributePrimitiveType.GROUPS, + AtlanCustomAttributePrimitiveType.URL, + AtlanCustomAttributePrimitiveType.SQL, + ): + base_type = AtlanCustomAttributePrimitiveType.STRING.value + else: + base_type = attribute_type.value + if multi_valued: + attr_def.type_name = f"array<{str(base_type)}>" + attr_def.options.multi_value_select = True # type: ignore[union-attr] + attr_def.cardinality = Cardinality.SET + else: + attr_def.type_name = base_type + if add_enum_values: + if enum_def := await client.enum_cache.get_by_name(str(options_name)): + attr_def.enum_values = enum_def.get_valid_values() + else: + attr_def.enum_values = [] + + attr_def.set_applicable_asset_types( + applicable_asset_types or _complete_type_list + ) + attr_def.set_applicable_glossary_types( + applicable_glossary_types or _all_glossary_types + ) + attr_def.set_applicable_domain_types( + applicable_domain_types or _all_domain_types + ) + attr_def.set_applicable_other_asset_types( + applicable_other_asset_types or _all_other_types + ) + attr_def.set_applicable_connections( + applicable_connections or await _get_all_qualified_names_async("Connection") + ) + attr_def.set_applicable_glossaries( + applicable_glossaries + or await _get_all_qualified_names_async("AtlasGlossary") + ) + attr_def.set_applicable_domains(applicable_domains or _all_domains) + attr_def.set_applicable_ai_asset_types(applicable_ai_asset_types or set()) + return attr_def + + def is_archived(self) -> bool: + """Check if this attribute has been archived.""" + return bool(opt.is_archived) if (opt := self.options) else False + + def archive(self, by: str) -> AttributeDef: + """Mark this attribute as archived.""" + if self.options: + removal_epoch = int(time.time() * 1000) + self.options.is_archived = True + self.options.archived_by = by + self.options.archived_at = removal_epoch + self.display_name = f"{self.display_name}-archived-{removal_epoch}" + return self + + +# ============================================================================= +# RELATIONSHIP ATTRIBUTE DEFINITION +# ============================================================================= + + +class RelationshipAttributeDef(AttributeDef, kw_only=True): + """Relationship attribute definition.""" + + is_legacy_attribute: Union[bool, None] = None + """Unused.""" + + relationship_type_name: Union[str, None] = None + """Name of the relationship type.""" + + +# ============================================================================= +# STRUCT DEFINITION +# ============================================================================= + + +class StructDef(TypeDef, kw_only=True): + """Struct type definition.""" + + category: AtlanTypeCategory = AtlanTypeCategory.STRUCT + """Type category for struct definitions.""" + + attribute_defs: Union[List[AttributeDef], None] = None + """List of attributes that should be available in the type definition.""" + + service_type: Union[str, None] = None + """Internal use only.""" + + +# ============================================================================= +# ATLAN TAG DEFINITION (CLASSIFICATION) +# ============================================================================= + + +class AtlanTagDef(TypeDef, kw_only=True): + """Classification (Atlan tag) type definition.""" + + attribute_defs: Union[List[AttributeDef], None] = None + """Unused.""" + + category: AtlanTypeCategory = AtlanTypeCategory.CLASSIFICATION + """Type category for classification definitions.""" + + display_name: Union[str, None] = None + """Name used for display purposes (in user interfaces).""" + + entity_types: Union[List[str], None] = None + """A list of entity types that this classification can be used against.""" + + options: Union[Dict[str, Any], None] = None + """Optional properties of the type definition.""" + + sub_types: Union[List[str], None] = None + """List of sub-types that extend from this type definition.""" + + super_types: Union[List[str], None] = None + """List of super-types that this type definition extends.""" + + service_type: Union[str, None] = None + """Name used for display purposes.""" + + skip_display_name_uniqueness_check: Union[bool, None] = None + """TBC""" + + @staticmethod + def creator( + name: str, + color: AtlanTagColor = AtlanTagColor.GRAY, + icon: AtlanIcon = AtlanIcon.ATLAN_TAG, + image: Optional[AtlanImage] = None, + emoji: Optional[str] = None, + ) -> AtlanTagDef: + """ + Builds the minimal object necessary to create an Atlan tag definition. + + :param name: human-readable name for the Atlan tag + :param color: color for the tag + :param icon: icon for the tag + :param image: optional image for the tag + :param emoji: optional emoji for the tag + :returns: the minimal object necessary to create the tag typedef + """ + from pyatlan_v9.utils import validate_required_fields + + validate_required_fields( + ["name", "color"], + [name, color], + ) + cls_options: Dict[str, str] = { + "color": color.value, + "iconName": icon.value, + } + if image: + cls_options["imageID"] = str(image.id) + cls_options["iconType"] = TagIconType.IMAGE.value + elif emoji: + cls_options["emoji"] = emoji + cls_options["iconType"] = TagIconType.EMOJI.value + else: + cls_options["imageID"] = "" + cls_options["iconType"] = TagIconType.ICON.value + + return AtlanTagDef( + category=AtlanTypeCategory.CLASSIFICATION, + name=name, + display_name=name, + options=cls_options, + skip_display_name_uniqueness_check=False, + ) + + +# ============================================================================= +# ENTITY DEFINITION +# ============================================================================= + +RESERVED_SERVICE_TYPES = {"atlas_core", "atlan", "aws", "azure", "gcp", "google"} + + +class EntityDef(TypeDef, kw_only=True): + """Entity type definition.""" + + attribute_defs: List[Dict[str, Any]] = msgspec.field(default_factory=list) + """Unused.""" + + business_attribute_defs: Union[Dict[str, List[Dict[str, Any]]], None] = ( + msgspec.field(default_factory=dict) + ) + """Unused.""" + + category: AtlanTypeCategory = AtlanTypeCategory.ENTITY + """Type category for entity definitions.""" + + relationship_attribute_defs: List[Dict[str, Any]] = msgspec.field( + default_factory=list + ) + """Unused.""" + + service_type: Union[str, None] = None + """Internal use only.""" + + sub_types: List[str] = msgspec.field(default_factory=list) + """List of sub-types that extend from this type definition.""" + + super_types: List[str] = msgspec.field(default_factory=list) + """List of super-types that this type definition extends.""" + + @property + def reserved_type(self) -> bool: + """Whether this entity definition is a reserved (built-in) type.""" + return self.service_type in RESERVED_SERVICE_TYPES + + +# ============================================================================= +# RELATIONSHIP DEFINITION +# ============================================================================= + + +class RelationshipDef(TypeDef, kw_only=True): + """Relationship type definition.""" + + attribute_defs: List[Dict[str, Any]] = msgspec.field(default_factory=list) + """Unused.""" + + category: AtlanTypeCategory = AtlanTypeCategory.RELATIONSHIP + """Type category for relationship definitions.""" + + end_def1: Union[Dict[str, Any], None] = msgspec.field(default_factory=dict) + """Unused.""" + + end_def2: Union[Dict[str, Any], None] = msgspec.field(default_factory=dict) + """Unused.""" + + propagate_tags: str = "ONE_TO_TWO" + """Unused.""" + + relationship_category: str = "AGGREGATION" + """Unused.""" + + relationship_label: str = "__SalesforceOrganization.reports" + """Unused.""" + + service_type: Union[str, None] = None + """Internal use only.""" + + +# ============================================================================= +# CUSTOM METADATA DEFINITION +# ============================================================================= + + +class CustomMetadataDef(TypeDef, kw_only=True): + """Custom metadata (business metadata) type definition.""" + + class Options(msgspec.Struct, kw_only=True, rename="camel"): + """Options for a custom metadata definition.""" + + emoji: Union[str, None] = None + """If the logoType is emoji, this holds the emoji character.""" + + image_id: Union[str, None] = None + """The id of the image used for the logo.""" + + is_locked: Union[bool, None] = None + """Whether the custom metadata can be managed in the UI (false) or not (true).""" + + logo_type: Union[str, None] = None + """Type of logo used for the custom metadata.""" + + logo_url: Union[str, None] = None + """If the logoType is image, this holds a URL to the image.""" + + icon_color: Union[AtlanTagColor, None] = None + """Color to use for the icon.""" + + icon_name: Union[AtlanIcon, None] = None + """Icon to use to represent the custom metadata.""" + + @staticmethod + def with_logo_as_emoji( + emoji: str, locked: bool = False + ) -> CustomMetadataDef.Options: + """Create options with an emoji logo.""" + from pyatlan_v9.utils import validate_required_fields + + validate_required_fields( + ["emoji"], + [emoji], + ) + return CustomMetadataDef.Options( + emoji=emoji, logo_type="emoji", is_locked=locked + ) + + @staticmethod + def with_logo_from_url( + url: str, locked: bool = False + ) -> CustomMetadataDef.Options: + """Create options with a URL-based image logo.""" + from pyatlan_v9.utils import validate_required_fields + + validate_required_fields( + ["url"], + [url], + ) + return CustomMetadataDef.Options( + logo_url=url, logo_type="image", is_locked=locked + ) + + @staticmethod + def with_logo_from_icon( + icon: AtlanIcon, color: AtlanTagColor, locked: bool = False + ) -> CustomMetadataDef.Options: + """Create options with a built-in icon.""" + from pyatlan_v9.utils import validate_required_fields + + validate_required_fields( + ["icon", "color"], + [icon, color], + ) + return CustomMetadataDef.Options( + logo_type="icon", + icon_color=color, + icon_name=icon, + is_locked=locked, + ) + + attribute_defs: List[AttributeDef] = msgspec.field(default_factory=list) + """List of custom attributes defined within the custom metadata.""" + + category: AtlanTypeCategory = AtlanTypeCategory.CUSTOM_METADATA + """Type category for custom metadata definitions.""" + + display_name: Union[str, None] = None + """Name used for display purposes (in user interfaces).""" + + options: Union[CustomMetadataDef.Options, None] = None + """Optional properties of the type definition.""" + + @staticmethod + def creator( + display_name: str, description: Optional[str] = None + ) -> CustomMetadataDef: + """ + Builds the minimal object necessary to create a custom metadata definition. + + :param display_name: human-readable name for the custom metadata + :param description: optional description + :returns: the minimal object necessary to create the custom metadata typedef + """ + from pyatlan_v9.utils import validate_required_fields + + validate_required_fields( + ["display_name"], + [display_name], + ) + return CustomMetadataDef( + category=AtlanTypeCategory.CUSTOM_METADATA, + display_name=display_name, + name=display_name, + description=description, + ) + + +# ============================================================================= +# TYPE DEFINITION RESPONSE +# ============================================================================= + + +class TypeDefResponse(msgspec.Struct, kw_only=True, rename="camel", omit_defaults=True): + """Response containing all type definitions.""" + + enum_defs: List[EnumDef] = msgspec.field(default_factory=list) + """List of enumeration type definitions.""" + + struct_defs: List[StructDef] = msgspec.field(default_factory=list) + """List of struct type definitions.""" + + atlan_tag_defs: List[AtlanTagDef] = msgspec.field( + default_factory=list, name="classificationDefs" + ) + """List of classification type definitions.""" + + entity_defs: List[EntityDef] = msgspec.field(default_factory=list) + """List of entity type definitions.""" + + relationship_defs: List[RelationshipDef] = msgspec.field(default_factory=list) + """List of relationship type definitions.""" + + custom_metadata_defs: List[CustomMetadataDef] = msgspec.field( + default_factory=list, name="businessMetadataDefs" + ) + """List of custom metadata type definitions.""" + + # Internal computed lists (populated in __post_init__) + _reserved_entity_defs: list = [] # noqa: RUF012 + _custom_entity_defs: list = [] # noqa: RUF012 + _custom_entity_def_names: set = set() # noqa: RUF012 + + def __post_init__(self): + """Categorize entity defs into reserved and custom after initialization.""" + self._reserved_entity_defs = [] + self._custom_entity_defs = [] + self._custom_entity_def_names = set() + for entity_def in self.entity_defs or []: + if entity_def.reserved_type: + self._reserved_entity_defs.append(entity_def) + else: + self._custom_entity_defs.append(entity_def) + self._custom_entity_def_names.add(entity_def.name) + + @property + def reserved_entity_defs(self) -> List[EntityDef]: + """Entity definitions for reserved (built-in) types.""" + return self._reserved_entity_defs + + @property + def custom_entity_defs(self) -> List[EntityDef]: + """Entity definitions for custom types.""" + return self._custom_entity_defs + + @property + def custom_entity_def_names(self) -> Set[str]: + """Names of custom entity definitions.""" + return self._custom_entity_def_names + + def is_custom_entity_def_name(self, name: str) -> bool: + """Check if the given name matches any custom entity definition.""" + for custom_name in self.custom_entity_def_names: + if custom_name in name: + return True + return False diff --git a/pyatlan_v9/model/user.py b/pyatlan_v9/model/user.py new file mode 100644 index 000000000..99542a868 --- /dev/null +++ b/pyatlan_v9/model/user.py @@ -0,0 +1,303 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2022 Atlan Pte. Ltd. + +from __future__ import annotations + +from typing import Any, Generator, Protocol, Union + +import msgspec + +from pyatlan.errors import ErrorCode +from pyatlan.utils import validate_required_fields +from pyatlan_v9.model.api_tokens import ApiToken + + +class UserAttributes(msgspec.Struct, kw_only=True): + """Detailed attributes of an Atlan user.""" + + designation: Union[list[str], None] = None + """Designation for the user, such as an honorific or title.""" + skills: Union[list[str], None] = None + """Skills the user possesses.""" + slack: Union[list[str], None] = None + """Unique Slack member identifier.""" + jira: Union[list[str], None] = None + """Unique JIRA user identifier.""" + invited_at: Union[list[str], None] = None + """Time at which the user was invited (as a formatted string).""" + invited_by: Union[list[str], None] = None + """User who invited this user.""" + invited_by_name: Union[list[str], None] = None + + +class UserPersona(msgspec.Struct, kw_only=True, rename="camel"): + """Persona associated with a user.""" + + id: Union[str, None] = None + """Unique identifier (GUID) of the persona.""" + name: Union[str, None] = None + """Internal name of the persona.""" + display_name: Union[str, None] = None + """Human-readable name of the persona.""" + + +class UserLoginEvent(msgspec.Struct, kw_only=True): + """Login event for a user.""" + + client_id: Union[str, None] = None + """Where the login occurred (usually 'atlan-frontend').""" + details: Union[Any, None] = None + ip_address: Union[str, None] = None + """IP address from which the user logged in.""" + realm_id: Union[str, None] = None + session_id: Union[str, None] = None + """Unique identifier (GUID) of the session for the login.""" + time: Union[int, None] = None + """Time (epoch) when the login occurred, in milliseconds.""" + type: Union[str, None] = None + """Type of login event that occurred (usually 'LOGIN').""" + user_id: Union[str, None] = None + """Unique identifier (GUID) of the user that logged in.""" + + +class UserAuthDetails(msgspec.Struct, kw_only=True): + """Authentication details for a user.""" + + client_id: Union[str, None] = None + ip_address: Union[str, None] = None + realm_id: Union[str, None] = None + user_id: Union[str, None] = None + + +class UserAdminEvent(msgspec.Struct, kw_only=True): + """Admin event for a user.""" + + operation_type: Union[str, None] = None + """Type of admin operation that occurred.""" + realm_id: Union[str, None] = None + representation: Union[str, None] = None + resource_path: Union[str, None] = None + resource_type: Union[str, None] = None + """Type of resource for the admin operation that occurred.""" + time: Union[int, None] = None + """Time (epoch) when the admin operation occurred, in milliseconds.""" + auth_details: Union[UserAuthDetails, None] = None + + +class AtlanUser(msgspec.Struct, kw_only=True, rename="camel"): + """Representation of a user in Atlan.""" + + username: Union[str, None] = None + """Username of the user within Atlan.""" + id: Union[str, None] = None + """Unique identifier (GUID) of the user within Atlan.""" + workspace_role: Union[str, None] = None + """Name of the role of the user within Atlan.""" + email: Union[str, None] = None + """Email address of the user.""" + email_verified: Union[bool, None] = None + """When true, the email address of the user has been verified.""" + enabled: Union[bool, None] = None + """When true, the user is enabled.""" + first_name: Union[str, None] = None + """First name of the user.""" + last_name: Union[str, None] = None + """Last name (surname) of the user.""" + attributes: Union[UserAttributes, None] = None + """Detailed attributes of the user.""" + created_timestamp: Union[int, None] = None + """Time (epoch) at which the user was created, in milliseconds.""" + last_login_time: Union[int, None] = None + """Time (epoch) at which the user last logged into Atlan.""" + group_count: Union[int, None] = None + """Number of groups to which the user belongs.""" + default_roles: Union[list[str], None] = None + roles: Union[list[str], None] = None + decentralized_roles: Union[Any, None] = None + personas: Union[list[UserPersona], None] = None + """Personas the user is associated with.""" + purposes: Union[list[Any], None] = None + """Purposes the user is associated with.""" + admin_events: Union[list[UserAdminEvent], None] = None + """List of administration-related events for this user.""" + login_events: Union[list[UserLoginEvent], None] = None + """List of login-related events for this user.""" + + @staticmethod + def creator(email: str, role_name: str) -> AtlanUser: + """ + Create a new user with the given email and role. + + :param email: email address of the user + :param role_name: name of the workspace role for the user + :returns: an AtlanUser configured for creation + """ + validate_required_fields(["email", "role_name"], [email, role_name]) + return AtlanUser(email=email, workspace_role=role_name) + + @staticmethod + def updater(guid: str) -> AtlanUser: + """ + Create a user reference for modification. + + :param guid: unique identifier of the user + :returns: an AtlanUser configured for update + """ + validate_required_fields(["guid"], [guid]) + return AtlanUser(id=guid) + + +class UserMinimalResponse(msgspec.Struct, kw_only=True): + """Minimal user response with basic fields.""" + + username: Union[str, None] = None + """Username of the user within Atlan.""" + id: Union[str, None] = None + """Unique identifier (GUID) of the user within Atlan.""" + email: Union[str, None] = None + """Email address of the user.""" + email_verified: Union[bool, None] = None + enabled: Union[bool, None] = None + first_name: Union[str, None] = None + last_name: Union[str, None] = None + attributes: Union[UserAttributes, None] = None + created_timestamp: Union[int, None] = None + totp: Union[bool, None] = None + disableable_credential_types: Union[Any, None] = None + required_actions: Union[Any, None] = None + access: Union[Any, None] = None + + +class UserRequest(msgspec.Struct, kw_only=True): + """Request parameters for listing users.""" + + max_login_events: int = 1 + post_filter: Union[str, None] = None + """Criteria by which to filter the list of users to retrieve.""" + sort: Union[str, None] = "username" + """Property by which to sort the resulting list of users.""" + count: bool = True + """Whether to include an overall count of users.""" + offset: Union[int, None] = 0 + """Starting point for the list of users when paging.""" + limit: Union[int, None] = 20 + """Maximum number of users to return per page.""" + columns: Union[list[str], None] = None + """List of columns to be returned about each user in the response.""" + + @property + def query_params(self) -> dict: + """Convert to query parameters dict.""" + qp: dict[str, object] = {} + if self.post_filter: + qp["filter"] = self.post_filter + if self.sort: + qp["sort"] = self.sort + if self.columns: + qp["columns"] = self.columns + qp["count"] = self.count + qp["offset"] = self.offset + qp["limit"] = self.limit + qp["maxLoginEvents"] = self.max_login_events + return qp + + +class UserResponse(msgspec.Struct, kw_only=True, rename="camel"): + """Response containing user information with pagination support.""" + + total_record: Union[int, None] = None + """Total number of users.""" + filter_record: Union[int, None] = None + """Number of users in the filtered response.""" + records: Union[list[AtlanUser], None] = msgspec.field(default_factory=list) + """Details of each user included in the response.""" + + # Pagination state (not from JSON — set after construction) + _size: int = 20 + _start: int = 0 + _endpoint: Any = None + _client: Any = None + _criteria: Any = None + + def current_page(self) -> list[AtlanUser]: + """Return the current page of user results.""" + return self.records or [] + + def next_page(self, start=None, size=None) -> bool: + """Advance to the next page of results.""" + self._start = start or self._start + self._size + if size: + self._size = size + return self._get_next_page() if self.records else False + + def _get_next_page(self) -> bool: + """Fetch the next page of results.""" + self._criteria.offset = self._start + self._criteria.limit = self._size + raw_json = self._client._call_api( + api=self._endpoint.format_path_with_params(), + query_params=self._criteria.query_params, + ) + if not raw_json.get("records"): + self.records = [] + return False + try: + self.records = msgspec.convert( + raw_json.get("records"), list[AtlanUser], strict=False + ) + except Exception as err: + raise ErrorCode.JSON_ERROR.exception_with_parameters( + raw_json, 200, str(err) + ) from err + return True + + def __iter__(self) -> Generator[AtlanUser, None, None]: # type: ignore[override] + """Iterate through all pages of results.""" + while True: + yield from self.current_page() + if not self.next_page(): + break + + +class CreateUser(msgspec.Struct, kw_only=True): + """Specification for a user to create.""" + + email: str + """Email address of the user.""" + role_name: str + """Name of the workspace role for the user.""" + role_id: str + """Unique identifier (GUID) of the workspace role for the user.""" + + +class CreateUserRequest(msgspec.Struct, kw_only=True): + """Request to create users.""" + + users: list[CreateUser] + """List of users to create.""" + + +class AddToGroupsRequest(msgspec.Struct, kw_only=True): + """Request to add a user to groups.""" + + groups: Union[list[str], None] = None + """List of groups (their GUIDs) to add the user to.""" + + +class ChangeRoleRequest(msgspec.Struct, kw_only=True): + """Request to change a user's workspace role.""" + + role_id: str + """Unique identifier (GUID) of the new workspace role for the user.""" + + +class UserProvider(Protocol): + """Protocol that is implemented by classes that can provide a list of all the users in Atlan.""" + + def get_all_users(self, limit: int = 20) -> list[AtlanUser]: + """Retrieve all users defined in Atlan.""" + ... + + def get_api_token_by_id(self, client_id: str) -> Union[ApiToken, None]: + """Retrieve an API token by its client ID.""" + ... diff --git a/pyatlan_v9/model/workflow.py b/pyatlan_v9/model/workflow.py new file mode 100644 index 000000000..fc65646c8 --- /dev/null +++ b/pyatlan_v9/model/workflow.py @@ -0,0 +1,366 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2022 Atlan Pte. Ltd. + +from __future__ import annotations + +import json +from typing import Any, Generator, Union + +import msgspec + +from pyatlan.errors import ErrorCode +from pyatlan.model.enums import AtlanWorkflowPhase + +# ============================================================================= +# HELPERS +# ============================================================================= + + +def _remove_nones(obj: Any) -> Any: + """Recursively remove None values from dicts (for Pydantic exclude_none parity).""" + if isinstance(obj, dict): + return {k: _remove_nones(v) for k, v in obj.items() if v is not None} + if isinstance(obj, list): + return [_remove_nones(item) for item in obj] + return obj + + +# ============================================================================= +# PACKAGE-RELATED WORKFLOW MODELS (used for building crawler/miner workflows) +# ============================================================================= + + +class PackageParameter(msgspec.Struct, kw_only=True, rename="camel"): + """Parameter for a workflow package.""" + + parameter: Union[str, None] = None + type: Union[str, None] = None + body: Union[dict[str, Any], None] = None + + +class WorkflowMetadata(msgspec.Struct, kw_only=True, rename="camel"): + """Metadata for a workflow.""" + + annotations: Union[dict[str, str], None] = None + creation_timestamp: Union[str, None] = None + generate_name: Union[str, None] = None + generation: Union[int, None] = None + labels: Union[dict[str, str], None] = None + managed_fields: Union[list[Any], None] = None + name: Union[str, None] = None + namespace: Union[str, None] = None + resource_version: Union[str, None] = None + uid: Union[str, None] = None + + +class WorkflowTemplateRef(msgspec.Struct, kw_only=True, rename="camel"): + """Reference to a workflow template.""" + + name: Union[str, None] = None + template: Union[str, None] = None + cluster_scope: Union[bool, None] = None + + +class NameValuePair(msgspec.Struct, kw_only=True, rename="camel"): + """Simple name-value pair.""" + + name: Union[str, None] = None + value: Union[Any, None] = None + + +class WorkflowParameters(msgspec.Struct, kw_only=True, rename="camel"): + """Parameters for a workflow.""" + + parameters: Union[list[NameValuePair], None] = None + + +class WorkflowTask(msgspec.Struct, kw_only=True, rename="camel"): + """Task within a workflow DAG.""" + + name: Union[str, None] = None + arguments: Union[WorkflowParameters, None] = None + template_ref: Union[WorkflowTemplateRef, None] = None + + +class WorkflowDAG(msgspec.Struct, kw_only=True, rename="camel"): + """Directed acyclic graph of workflow tasks.""" + + tasks: Union[list[WorkflowTask], None] = None + + +class WorkflowTemplate(msgspec.Struct, kw_only=True, rename="camel"): + """Template for a workflow.""" + + name: Union[str, None] = None + inputs: Union[Any, None] = None + outputs: Union[Any, None] = None + metadata: Union[Any, None] = None + dag: Union[WorkflowDAG, None] = None + + +class WorkflowSpec(msgspec.Struct, kw_only=True, rename="camel"): + """Specification for a workflow.""" + + entrypoint: Union[str, None] = None + arguments: Union[Any, None] = None + templates: Union[list[WorkflowTemplate], None] = None + workflow_template_ref: Union[WorkflowTemplateRef, None] = None + workflow_metadata: Union[WorkflowMetadata, None] = None + + +class Workflow(msgspec.Struct, kw_only=True, rename="camel"): + """A workflow definition.""" + + metadata: Union[WorkflowMetadata, None] = None + spec: Union[WorkflowSpec, None] = None + payload: list[PackageParameter] = msgspec.field(default_factory=list) + + def to_json(self, nested: bool = True) -> str: + """ + Serialize to JSON with camelCase keys, excluding None values. + + This matches the legacy Pydantic ``Workflow.json(by_alias=True, exclude_none=True)``. + + Args: + nested: Accepted for API compatibility with the Pydantic encoder + hook but has no effect on the output format. + """ + data = msgspec.to_builtins(self) + cleaned = _remove_nones(data) + return json.dumps(cleaned) + + +# ============================================================================= +# SEARCH / API RESPONSE MODELS +# ============================================================================= + + +class WorkflowSearchResultStatus(msgspec.Struct, kw_only=True, rename="camel"): + """Status of a workflow search result.""" + + artifact_gc_status: Union[dict[str, Any], None] = msgspec.field( + default=None, name="artifactGCStatus" + ) + artifact_repository_ref: Union[Any, None] = None + compressed_nodes: Union[str, None] = None + estimated_duration: Union[int, None] = None + conditions: Union[list[Any], None] = None + message: Union[str, None] = None + finished_at: Union[str, None] = None + nodes: Union[Any, None] = None + outputs: Union[WorkflowParameters, None] = None + phase: Union[AtlanWorkflowPhase, None] = None + progress: Union[str, None] = None + resources_duration: Union[dict[str, int], None] = None + started_at: Union[str, None] = msgspec.field(default=None, name="startedAt") + stored_templates: Union[Any, None] = None + stored_workflow_template_spec: Union[Any, None] = None + synchronization: Union[dict[str, Any], None] = None + + +class WorkflowSearchResultDetail(msgspec.Struct, kw_only=True, rename="camel"): + """Details of a workflow search result.""" + + api_version: Union[str, None] = None + kind: Union[str, None] = None + metadata: Union[WorkflowMetadata, None] = None + spec: Union[WorkflowSpec, None] = None + status: Union[WorkflowSearchResultStatus, None] = None + + +class WorkflowSearchResult(msgspec.Struct, kw_only=True, rename="camel"): + """Individual result from a workflow search.""" + + index: Union[str, None] = msgspec.field(default=None, name="_index") + type: Union[str, None] = msgspec.field(default=None, name="_type") + id: Union[str, None] = msgspec.field(default=None, name="_id") + seq_no: Union[Any, None] = msgspec.field(default=None, name="_seq_no") + primary_term: Union[Any, None] = msgspec.field(default=None, name="_primary_term") + sort: Union[list[Any], None] = None + source: Union[WorkflowSearchResultDetail, None] = msgspec.field( + default=None, name="_source" + ) + + @property + def status(self) -> Union[AtlanWorkflowPhase, None]: + """Phase/status of the workflow.""" + if source := self.source: + if status := source.status: + return status.phase + return None + + def to_workflow(self) -> Workflow: + """Convert search result to a Workflow.""" + return Workflow( + spec=self.source.spec if self.source else None, + metadata=self.source.metadata if self.source else None, + ) + + +class WorkflowSearchHits(msgspec.Struct, kw_only=True, rename="camel"): + """Hits from a workflow search.""" + + total: Union[dict[str, Any], None] = None + hits: Union[list[WorkflowSearchResult], None] = None + + +class ReRunRequest(msgspec.Struct, kw_only=True, rename="camel"): + """Request to re-run a workflow.""" + + namespace: Union[str, None] = "default" + resource_kind: Union[str, None] = "WorkflowTemplate" + resource_name: Union[str, None] = None + + +class WorkflowResponse(msgspec.Struct, kw_only=True, rename="camel"): + """Response from a workflow operation.""" + + metadata: Union[WorkflowMetadata, None] = None + spec: Union[WorkflowSpec, None] = None + payload: list[Any] = msgspec.field(default_factory=list) + + +class WorkflowRunResponse(msgspec.Struct, kw_only=True, rename="camel"): + """Response from a workflow run operation.""" + + metadata: Union[WorkflowMetadata, None] = None + spec: Union[WorkflowSpec, None] = None + payload: list[Any] = msgspec.field(default_factory=list) + status: Union[WorkflowSearchResultStatus, None] = None + + +class ScheduleQueriesSearchRequest(msgspec.Struct, kw_only=True, rename="camel"): + """Request for searching schedule queries.""" + + start_date: str + """Start date in ISO 8601 format.""" + end_date: str + """End date in ISO 8601 format.""" + + +class WorkflowSchedule(msgspec.Struct, kw_only=True, rename="camel"): + """Schedule for a workflow.""" + + timezone: str + cron_schedule: str + + +class WorkflowScheduleSpec(msgspec.Struct, kw_only=True, rename="camel"): + """Specification for a workflow schedule.""" + + schedule: Union[str, None] = None + timezone: Union[str, None] = None + workflow_spec: Union[WorkflowSpec, None] = None + concurrency_policy: Union[str, None] = None + starting_deadline_seconds: Union[int, None] = None + successful_jobs_history_limit: Union[int, None] = None + failed_jobs_history_limit: Union[int, None] = None + + +class WorkflowScheduleStatus(msgspec.Struct, kw_only=True, rename="camel"): + """Status of a workflow schedule.""" + + active: Union[Any, None] = None + conditions: Union[Any, None] = None + last_scheduled_time: Union[str, None] = None + + +class WorkflowScheduleResponse(msgspec.Struct, kw_only=True, rename="camel"): + """Response from a workflow schedule operation.""" + + metadata: Union[WorkflowMetadata, None] = None + spec: Union[WorkflowScheduleSpec, None] = None + status: Union[WorkflowScheduleStatus, None] = None + workflow_metadata: Union[WorkflowMetadata, None] = None + + +class WorkflowSearchResponse(msgspec.Struct, kw_only=True, rename="camel"): + """Response from a workflow search with pagination support.""" + + took: Union[int, None] = None + hits: Union[WorkflowSearchHits, None] = None + shards: Union[dict[str, Any], None] = msgspec.field(default=None, name="_shards") + + # Pagination state (not from JSON — set after construction) + _size: int = 10 + _start: int = 0 + _endpoint: Any = None + _client: Any = None + _criteria: Any = None + + @property + def count(self) -> int: + """Total count of workflow search results.""" + return self.hits.total.get("value", 0) if self.hits and self.hits.total else 0 + + def current_page(self) -> Union[list[WorkflowSearchResult], None]: + """Return the current page of results.""" + return self.hits.hits if self.hits else None + + def next_page(self, start=None, size=None) -> bool: + """Advance to the next page of results.""" + self._start = start or self._start + self._size + if size: + self._size = size + if self.hits and self.hits.hits: + return self._get_next_page() + return False + + def _get_next_page(self) -> bool: + """Fetch the next page of results.""" + from pyatlan_v9.model.workflow import WorkflowSearchRequest + + request = WorkflowSearchRequest( + query=self._criteria, from_=self._start, size=self._size + ) + raw_json = self._client._call_api( + api=self._endpoint, + request_obj=request, + ) + if not raw_json.get("hits", {}).get("hits"): + if self.hits: + self.hits.hits = [] + return False + try: + if self.hits: + self.hits.hits = msgspec.convert( + raw_json["hits"]["hits"], list[WorkflowSearchResult], strict=False + ) + except Exception as err: + raise ErrorCode.JSON_ERROR.exception_with_parameters( + raw_json, 200, str(err) + ) from err + return True + + def __iter__(self) -> Generator[WorkflowSearchResult, None, None]: # type: ignore[override] + """Iterate through all pages of results.""" + while True: + yield from self.current_page() or [] + if not self.next_page(): + break + + +class WorkflowSearchRequest(msgspec.Struct, kw_only=True, rename="camel"): + """Request to search for workflows.""" + + from_: int = msgspec.field(default=0, name="from") + """Starting offset for results.""" + size: int = 10 + """Page size for results.""" + # Elasticsearch DSL uses snake_case for these fields + track_total_hits: bool = msgspec.field(default=True, name="track_total_hits") + """Whether to track total hit count.""" + post_filter: Union[Any, None] = msgspec.field(default=None, name="post_filter") + """Post-search filter.""" + query: Union[Any, None] = None + """Search query.""" + sort: Union[list[Any], None] = None + """Sort criteria.""" + source: Union[WorkflowSearchResultDetail, None] = msgspec.field( + default=None, name="_source" + ) + + def to_dict(self) -> dict: + """Serialize to dict, excluding None values to avoid sending + ``"_source": null`` which Elasticsearch rejects.""" + return _remove_nones(msgspec.to_builtins(self)) diff --git a/pyatlan_v9/pkg/__init__.py b/pyatlan_v9/pkg/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/pyatlan_v9/pkg/utils.py b/pyatlan_v9/pkg/utils.py new file mode 100644 index 000000000..9f262f757 --- /dev/null +++ b/pyatlan_v9/pkg/utils.py @@ -0,0 +1,92 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +import logging +import os +from typing import Optional + +from pyatlan_v9.client.atlan import AtlanClient + +LOGGER = logging.getLogger(__name__) + + +def get_client( + impersonate_user_id: Optional[str] = None, set_pkg_headers: Optional[bool] = False +) -> AtlanClient: + """ + Set up the default v9 Atlan client, based on environment variables. + This will use an API token if found in ATLAN_API_KEY, and will fallback + to attempting to impersonate a user if ATLAN_API_KEY is empty. + + :param impersonate_user_id: unique identifier (GUID) of a user or API token + to impersonate (default is None) + :param set_pkg_headers: whether to set package headers on the client + (default is False) + :returns: an initialized v9 client + """ + base_url = os.environ.get("ATLAN_BASE_URL", "INTERNAL") + api_token = os.environ.get("ATLAN_API_KEY", "") + user_id = os.environ.get("ATLAN_USER_ID", impersonate_user_id) + oauth_client_id = os.environ.get("ATLAN_OAUTH_CLIENT_ID", "") + oauth_client_secret = os.environ.get("ATLAN_OAUTH_CLIENT_SECRET", "") + + if oauth_client_id and oauth_client_secret: + LOGGER.info("Using OAuth client credentials for authentication.") + client = AtlanClient( + base_url=base_url, + oauth_client_id=str(oauth_client_id), + oauth_client_secret=str(oauth_client_secret), + ) + if set_pkg_headers: + client = set_package_headers(client) + return client + else: + LOGGER.info( + "No OAuth client credentials found. " + "Attempting to use API token or user impersonation." + ) + + if api_token: + LOGGER.info("Using provided API token for authentication.") + api_key = api_token + elif user_id: + LOGGER.info("No API token found, attempting to impersonate user: %s", user_id) + client = AtlanClient(base_url=base_url, api_key="") + api_key = client.impersonate.user(user_id=user_id) + else: + LOGGER.info( + "No API token or impersonation user, attempting short-lived escalation." + ) + client = AtlanClient(base_url=base_url, api_key="") + api_key = client.impersonate.escalate() + + client = AtlanClient(base_url=base_url, api_key=api_key) + if user_id: + client._user_id = user_id + if set_pkg_headers: + client = set_package_headers(client) + return client + + +def set_package_headers(client: AtlanClient) -> AtlanClient: + """ + Configure the AtlanClient with package headers from environment variables. + + :param client: AtlanClient instance to configure + :returns: updated client instance + """ + if (agent := os.environ.get("X_ATLAN_AGENT")) and ( + agent_id := os.environ.get("X_ATLAN_AGENT_ID") + ): + headers = { + "x-atlan-agent": agent, + "x-atlan-agent-id": agent_id, + "x-atlan-agent-package-name": os.environ.get( + "X_ATLAN_AGENT_PACKAGE_NAME", "" + ), + "x-atlan-agent-workflow-id": os.environ.get( + "X_ATLAN_AGENT_WORKFLOW_ID", "" + ), + } + client.update_headers(headers) + return client diff --git a/pyatlan_v9/test_utils/__init__.py b/pyatlan_v9/test_utils/__init__.py new file mode 100644 index 000000000..0a86c7cd0 --- /dev/null +++ b/pyatlan_v9/test_utils/__init__.py @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. +"""Re-export test utilities from the legacy pyatlan.test_utils module.""" + +from pyatlan.test_utils import * # noqa: F401,F403 diff --git a/pyatlan_v9/test_utils/base_vcr.py b/pyatlan_v9/test_utils/base_vcr.py new file mode 100644 index 000000000..6370005b6 --- /dev/null +++ b/pyatlan_v9/test_utils/base_vcr.py @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. +"""Re-export BaseVCR from the legacy pyatlan.test_utils.base_vcr module.""" + +from pyatlan.test_utils.base_vcr import * # noqa: F401,F403 diff --git a/pyatlan_v9/utils.py b/pyatlan_v9/utils.py new file mode 100644 index 000000000..f1e0ea4da --- /dev/null +++ b/pyatlan_v9/utils.py @@ -0,0 +1,70 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Utility functions for pyatlan_v9.""" + +import random +from functools import wraps + +from pyatlan.utils import ( + ComparisonCategory, + get_base_type, + is_comparable_type, + list_attributes_to_params, + unflatten_custom_metadata, + unflatten_custom_metadata_for_entity, + validate_type, +) + + +def init_guid(func): + """ + Decorator function that can be used on the creator method of an asset to initialize the guid. + + The guid is initialized with a negative random integer to indicate it's a temporary GUID + for objects being created (not yet persisted to Atlan). + """ + + @wraps(func) + def call(*args, **kwargs): + ret_value = func(*args, **kwargs) + if hasattr(ret_value, "guid"): + ret_value.guid = str( + -int(random.random() * 10000000000000000) # noqa: S311 + ) + return ret_value + + return call + + +def validate_required_fields(field_names: list[str], field_values: list) -> None: + """ + Validate that required fields are provided and not empty. + + Args: + field_names: List of field names + field_values: List of field values corresponding to field_names + + Raises: + ValueError: If any required field is missing or empty + """ + for name, value in zip(field_names, field_values): + if value is None: + raise ValueError(f"{name} is required") + if isinstance(value, str) and not value.strip(): + raise ValueError(f"{name} cannot be blank") + if isinstance(value, list) and len(value) == 0: + raise ValueError(f"{name} cannot be an empty list") + + +__all__ = [ + "ComparisonCategory", + "get_base_type", + "init_guid", + "is_comparable_type", + "list_attributes_to_params", + "unflatten_custom_metadata", + "unflatten_custom_metadata_for_entity", + "validate_required_fields", + "validate_type", +] diff --git a/pyatlan_v9/validate.py b/pyatlan_v9/validate.py new file mode 100644 index 000000000..731ff5c13 --- /dev/null +++ b/pyatlan_v9/validate.py @@ -0,0 +1,378 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Custom ``validate_arguments`` decorator for pyatlan_v9. + +This decorator validates function arguments against their type annotations +and supports **both** Pydantic ``BaseModel`` and ``msgspec.Struct`` model +instances. Used only by pyatlan_v9; legacy pyatlan uses pydantic.v1.validate_arguments. +""" + +from __future__ import annotations + +import functools +import inspect +import typing +from enum import Enum +from typing import Any, Callable, Dict, Optional, Tuple, Union + +import msgspec + +# --------------------------------------------------------------------------- +# Pydantic constrained-type detection (for backward compat with constr, etc.) +# --------------------------------------------------------------------------- +try: + from pydantic.v1 import ConstrainedStr + from pydantic.v1.types import ( # noqa: F401 + ConstrainedInt, + StrictBool, + StrictInt, + StrictStr, + ) + + _HAS_PYDANTIC_TYPES = True +except ImportError: # pragma: no cover + _HAS_PYDANTIC_TYPES = False + ConstrainedStr = None # type: ignore[assignment, misc] + ConstrainedInt = None # type: ignore[assignment, misc] + StrictBool = None # type: ignore[assignment, misc] + StrictInt = None # type: ignore[assignment, misc] + +try: + from pydantic.v1 import BaseModel as PydanticBaseModel +except ImportError: # pragma: no cover + PydanticBaseModel = None # type: ignore[assignment, misc] + + +# --------------------------------------------------------------------------- +# Helper: model-compatible isinstance check +# --------------------------------------------------------------------------- +def _is_model_instance(value: Any, expected_type: type) -> bool: + """ + Check if *value* is an instance of *expected_type*, accepting both + Pydantic BaseModel and msgspec.Struct instances whose MRO includes a + class with the same name as *expected_type*. + """ + if isinstance(value, expected_type): + return True + if isinstance(value, msgspec.Struct): + v9_mro_names = {cls.__name__ for cls in type(value).__mro__} + if expected_type.__name__ in v9_mro_names: + return True + if PydanticBaseModel is not None and isinstance(value, PydanticBaseModel): + pydantic_mro_names = {cls.__name__ for cls in type(value).__mro__} + if expected_type.__name__ in pydantic_mro_names: + return True + spec_class = getattr(value, "_spec_class", None) + if spec_class is not None: + try: + if issubclass(spec_class, expected_type): + return True + except TypeError: + pass + spec_mro_names = {cls.__name__ for cls in spec_class.__mro__} + if expected_type.__name__ in spec_mro_names: + return True + value_mro_names = {cls.__name__ for cls in type(value).__mro__} + if expected_type.__name__ in value_mro_names: + return True + return False + + +def _is_model_subclass(value: type, expected_type: type) -> bool: + try: + if issubclass(value, expected_type): + return True + except TypeError: + pass + value_mro_names = {cls.__name__ for cls in value.__mro__} + if expected_type.__name__ in value_mro_names: + return True + return False + + +def _validate_constrained_str(value: Any, hint: type) -> Tuple[Any, Optional[str]]: + if not _HAS_PYDANTIC_TYPES or ConstrainedStr is None: + if value is None: + return value, "none is not an allowed value" + if not isinstance(value, str): + return value, "str type expected" + return value, None + if not isinstance(hint, type) or not issubclass(hint, ConstrainedStr): + return value, None + if value is None: + return value, "none is not an allowed value" + strict = getattr(hint, "strict", False) + if strict and not isinstance(value, str): + return value, "str type expected" + if not isinstance(value, str): + try: + value = str(value) + except (ValueError, TypeError): + return value, "str type expected" + if getattr(hint, "strip_whitespace", False): + value = value.strip() + min_length = getattr(hint, "min_length", None) + if min_length is not None and len(value) < min_length: + return value, f"ensure this value has at least {min_length} characters" + max_length = getattr(hint, "max_length", None) + if max_length is not None and len(value) > max_length: + return value, f"ensure this value has at most {max_length} characters" + regex = getattr(hint, "regex", None) + if regex is not None: + import re + + if not re.match(regex, value): + return value, f"string does not match regex '{regex}'" + return value, None + + +def _check_type(value: Any, hint: Any) -> Tuple[Any, Optional[str]]: + if isinstance(hint, typing.TypeVar): + bound = hint.__bound__ + if bound is not None: + return _check_type(value, bound) + constraints = hint.__constraints__ + if constraints: + for c in constraints: + result, err = _check_type(value, c) + if err is None: + return result, None + return value, f"value does not match any constraint of TypeVar {hint}" + return value, None + if hint is type(None): + if value is None: + return value, None + return value, "none is not an allowed value" + if hint is typing.Any: + return value, None + origin = typing.get_origin(hint) + args = typing.get_args(hint) + if origin is Union: + has_none_type = type(None) in args + if value is None and not has_none_type: + return value, "none is not an allowed value" + last_errors: list = [] + for arg in args: + result, err = _check_type(value, arg) + if err is None: + return result, None + last_errors.append((arg, err)) + non_none_args = [a for a in args if a is not type(None)] + non_none_errors = [(a, e) for a, e in last_errors if a is not type(None)] + if len(non_none_args) == 1: + return value, non_none_errors[0][1] + for _, err in non_none_errors: + if err.startswith("-> "): + return value, err + type_names = [] + for arg in args: + if arg is type(None): + type_names.append("None") + elif isinstance(arg, type): + type_names.append(arg.__name__) + else: + inner_args = typing.get_args(arg) + inner_origin = typing.get_origin(arg) + if inner_origin is list and inner_args: + inner_name = ( + inner_args[0].__name__ + if isinstance(inner_args[0], type) + else str(inner_args[0]) + ) + type_names.append(f"List[{inner_name}]") + else: + type_names.append(str(arg).replace("typing.", "")) + return value, f"value is not a valid {' or '.join(type_names)}" + if origin is list: + if value is None: + return value, "none is not an allowed value" + if not isinstance(value, list): + return value, "value is not a valid list" + if args: + transformed = [] + for i, item in enumerate(value): + item_result, err = _check_type(item, args[0]) + if err is not None: + return value, f"-> {i}\n {err}" + transformed.append(item_result) + return transformed, None + return value, None + if origin is set: + if not isinstance(value, (set, frozenset)): + return value, "value is not a valid set" + if args: + for item in value: + _, err = _check_type(item, args[0]) + if err is not None: + return value, f"set item: {err}" + return value, None + if origin is dict: + if value is None: + return value, "none is not an allowed value" + if not isinstance(value, dict): + return value, "value is not a valid dict" + if args and len(args) >= 2: + key_hint, value_hint = args + for k, v in value.items(): + _, err_k = _check_type(k, key_hint) + if err_k is not None: + return value, f"-> __key__\n {err_k}" + _, err_v = _check_type(v, value_hint) + if err_v is not None: + return value, f"-> {k}\n {err_v}" + return value, None + if origin is tuple: + if not isinstance(value, tuple): + return value, "value is not a valid tuple" + return value, None + if origin is type: + if value is None: + return value, "none is not an allowed value" + if not isinstance(value, type): + return value, "a class is expected" + if args: + expected = args[0] + if isinstance(expected, typing.TypeVar): + expected = expected.__bound__ if expected.__bound__ else object + if not _is_model_subclass(value, expected): + return value, f"value is not a subclass of {expected.__name__}" + return value, None + if origin is not None and ( + str(origin).startswith("typing.Callable") + or str(hint).startswith("typing.Callable") + ): + if callable(value): + return value, None + return value, "value is not callable" + if isinstance(hint, type): + if value is None and hint is not type(None): + return value, "none is not an allowed value" + if _HAS_PYDANTIC_TYPES and ConstrainedStr and issubclass(hint, ConstrainedStr): + return _validate_constrained_str(value, hint) + if _HAS_PYDANTIC_TYPES and hint is StrictStr: + if isinstance(value, str): + return value, None + return value, "str type expected" + if _HAS_PYDANTIC_TYPES and hint is StrictBool: + if isinstance(value, bool): + return value, None + return value, "value is not a valid boolean" + if _HAS_PYDANTIC_TYPES and hint is StrictInt: + if isinstance(value, bool): + return value, "value is not a valid integer" + if isinstance(value, int): + return value, None + return value, "value is not a valid integer" + if _HAS_PYDANTIC_TYPES and ConstrainedInt and issubclass(hint, ConstrainedInt): + if isinstance(value, bool): + return value, "value is not a valid integer" + if isinstance(value, int): + return value, None + return value, "value is not a valid integer" + if hint is str: + if isinstance(value, str): + return value, None + return value, "str type expected" + if hint is bool: + if isinstance(value, bool): + return value, None + if isinstance(value, int): + return bool(value), None + return value, "value is not a valid boolean" + if isinstance(hint, type) and issubclass(hint, Enum): + if isinstance(value, hint): + return value, None + try: + return hint(value), None + except (ValueError, KeyError): + return ( + value, + f"value is not a valid enumeration member; permitted: {[e.value for e in hint]}", + ) + if _is_model_instance(value, hint): + return value, None + if isinstance(value, hint): + return value, None + _builtin_types = (str, int, float, bytes, bytearray, memoryview) + if hint not in _builtin_types: + return value, f"instance of {hint.__name__} expected" + return value, f"value is not a valid {hint.__name__}" + return value, None + + +def validate_arguments( + func: Optional[Callable] = None, + *, + config: Optional[Dict[str, Any]] = None, +) -> Callable: + """Decorator that validates function arguments against their type annotations (v9 only).""" + + def decorator(fn: Callable) -> Callable: + try: + hints = typing.get_type_hints(fn) + except Exception: + hints = getattr(fn, "__annotations__", {}).copy() + sig = inspect.signature(fn) + param_hints = {k: v for k, v in hints.items() if k != "return"} + pascal_name = "".join(part.capitalize() for part in fn.__name__.split("_")) + + @functools.wraps(fn) + def wrapper(*args: Any, **kwargs: Any) -> Any: + try: + bound = sig.bind_partial(*args, **kwargs) + except TypeError as e: + raise ValueError(f"1 validation error for {pascal_name}\n{e}") from None + bound.apply_defaults() + errors = [] + transformed = dict(bound.arguments) + has_transforms = False + for name, value in bound.arguments.items(): + if name in ("self", "cls"): + continue + if name not in param_hints: + continue + hint = param_hints[name] + result, err = _check_type(value, hint) + if err is not None: + errors.append((name, err)) + elif result is not value: + transformed[name] = result + has_transforms = True + if errors: + count = len(errors) + suffix = "s" if count > 1 else "" + lines = [f"{count} validation error{suffix} for {pascal_name}"] + for field_name, err_msg in errors: + if err_msg.startswith("-> "): + lines.append(f"{field_name} {err_msg}") + else: + lines.append(field_name) + lines.append(f" {err_msg}") + raise ValueError("\n".join(lines)) + try: + sig.bind(*args, **kwargs) + except TypeError as e: + raise ValueError(f"1 validation error for {pascal_name}\n{e}") from None + if has_transforms: + new_args = [] + new_kwargs = {} + for param_name, param in sig.parameters.items(): + if param_name in transformed: + if param.kind in ( + inspect.Parameter.POSITIONAL_ONLY, + inspect.Parameter.POSITIONAL_OR_KEYWORD, + ): + new_args.append(transformed[param_name]) + else: + new_kwargs[param_name] = transformed[param_name] + return fn(*new_args, **new_kwargs) + return fn(*args, **kwargs) + + wrapper.__wrapped__ = fn # type: ignore[attr-defined] + return wrapper + + if func is not None: + return decorator(func) + return decorator diff --git a/pyproject.toml b/pyproject.toml index 432da54b7..ba751efee 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,7 +37,8 @@ dependencies = [ "PyYAML~=6.0.3", "httpx~=0.28.1", "httpx-retries~=0.4.5", - "authlib~=1.6.6", + "authlib~=1.6.9", + "msgspec~=0.20.0", ] [project.urls] @@ -99,7 +100,7 @@ plugins = ["pydantic.mypy"] [tool.ruff] fix = true line-length = 88 -exclude = ["env", "venv", "__pycache__"] +exclude = ["env", "venv", "__pycache__", "pyatlan_v9/model/assets/_overlays"] [tool.ruff.lint.isort] split-on-trailing-comma = false @@ -109,6 +110,8 @@ split-on-trailing-comma = false "pyatlan/model/assets.py" = ["S307"] "pyatlan/model/assets/**.py" = ["E402", "F811"] "pyatlan/model/assets/core/**.py" = ["E402", "F811"] +"pyatlan_v9/model/assets/**.py" = ["E402", "F821"] +"pyatlan_v9/model/transform.py" = ["E402"] [tool.pytest.ini_options] addopts = "-p no:name_of_plugin" diff --git a/requirements.txt b/requirements.txt index d823c66a8..8997a9ed3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,7 @@ annotated-types==0.7.0 ; python_full_version < '3.14' and platform_python_implem # via pydantic anyio==4.10.0 ; python_full_version < '3.14' and platform_python_implementation == 'CPython' # via httpx -authlib==1.6.6 ; python_full_version < '3.14' and platform_python_implementation == 'CPython' +authlib==1.6.9 ; python_full_version < '3.14' and platform_python_implementation == 'CPython' # via pyatlan backports-asyncio-runner==1.2.0 ; python_full_version < '3.11' and platform_python_implementation == 'CPython' # via pytest-asyncio @@ -100,6 +100,8 @@ more-itertools==10.7.0 ; python_full_version < '3.14' and platform_machine != 'p # via # jaraco-classes # jaraco-functools +msgspec==0.20.0 ; python_full_version < '3.14' and platform_python_implementation == 'CPython' + # via pyatlan multidict==6.6.4 ; python_full_version < '3.14' and platform_python_implementation == 'CPython' # via yarl mypy==1.18.2 ; python_full_version < '3.14' and platform_python_implementation == 'CPython' diff --git a/tests/unit/aio/test_audit_search.py b/tests/unit/aio/test_audit_search.py index 7e090695a..9a092a1c5 100644 --- a/tests/unit/aio/test_audit_search.py +++ b/tests/unit/aio/test_audit_search.py @@ -47,23 +47,21 @@ def load_json(filename): async def _assert_audit_search_results( results: AsyncAuditSearchResults, response_json, sorts, bulk=False ): + first = response_json["entityAudits"][0] async for audit in results: - assert audit.entity_id == response_json["entityAudits"][0]["entity_id"] - assert ( - audit.entity_qualified_name - == response_json["entityAudits"][0]["entity_qualified_name"] - ) - assert audit.type_name == response_json["entityAudits"][0]["type_name"] + assert audit.entity_id == first["entityId"] + assert audit.entity_qualified_name == first["entityQualifiedName"] + assert audit.type_name == first["typeName"] expected_timestamp = datetime.fromtimestamp( - response_json["entityAudits"][0]["timestamp"] / 1000, tz=timezone.utc + first["timestamp"] / 1000, tz=timezone.utc ) assert audit.timestamp == expected_timestamp expected_created = datetime.fromtimestamp( - response_json["entityAudits"][0]["created"] / 1000, tz=timezone.utc + first["created"] / 1000, tz=timezone.utc ) assert audit.created == expected_created - assert audit.user == response_json["entityAudits"][0]["user"] - assert audit.action == response_json["entityAudits"][0]["action"] + assert audit.user == first["user"] + assert audit.action == first["action"] assert results.total_count == response_json["totalCount"] assert results._bulk == bulk diff --git a/tests/unit/data/search_responses/audit_search_paging.json b/tests/unit/data/search_responses/audit_search_paging.json index 7ebd26f88..f2eae6d92 100644 --- a/tests/unit/data/search_responses/audit_search_paging.json +++ b/tests/unit/data/search_responses/audit_search_paging.json @@ -2,9 +2,9 @@ "totalCount": 1, "entityAudits": [ { - "entity_qualified_name": "sample_entity_1", - "type_name": "AtlasGlossaryTerm", - "entity_id": "guid_1", + "entityQualifiedName": "sample_entity_1", + "typeName": "AtlasGlossaryTerm", + "entityId": "guid_1", "timestamp": 1733491479782, "created": 1733491480576, "user": "user1", diff --git a/tests/unit/model/test_alloydb_postgres.py b/tests/unit/model/test_alloydb_postgres.py deleted file mode 100644 index 318746848..000000000 --- a/tests/unit/model/test_alloydb_postgres.py +++ /dev/null @@ -1,107 +0,0 @@ -# SPDX-License-Identifier: Apache-2.0 -# Copyright 2026 Atlan Pte. Ltd. - -""" -Unit tests for ALLOYDB_POSTGRES connector type support (REQ-589). - -Verifies that AtlanConnectorType.ALLOYDB_POSTGRES and WorkflowPackage.ALLOYDB_POSTGRES -are properly defined, enabling programmatic metadata policy creation for AlloyDB assets. -""" - -import pytest - -from pyatlan.model.assets import Persona -from pyatlan.model.enums import ( - AtlanConnectionCategory, - AtlanConnectorType, - AuthPolicyType, - PersonaMetadataAction, - WorkflowPackage, -) - -ALLOYDB_POSTGRES_CONNECTION_QN = "default/alloydb-postgres/1686532494" -PERSONA_GUID = "test-persona-guid-1234" - - -class TestAtlanConnectorTypeAlloydbPostgres: - def test_enum_member_exists(self): - assert hasattr(AtlanConnectorType, "ALLOYDB_POSTGRES") - - def test_enum_value(self): - assert AtlanConnectorType.ALLOYDB_POSTGRES.value == "alloydb-postgres" - - def test_enum_category(self): - assert ( - AtlanConnectorType.ALLOYDB_POSTGRES.category - == AtlanConnectionCategory.DATABASE - ) - - def test_lookup_by_value(self): - connector = AtlanConnectorType("alloydb-postgres") - assert connector == AtlanConnectorType.ALLOYDB_POSTGRES - - def test_resolve_from_qualified_name(self): - connector = AtlanConnectorType._get_connector_type_from_qualified_name( - ALLOYDB_POSTGRES_CONNECTION_QN - ) - assert connector == AtlanConnectorType.ALLOYDB_POSTGRES - assert connector.value == "alloydb-postgres" - - def test_to_qualified_name_format(self): - qn = AtlanConnectorType.ALLOYDB_POSTGRES.to_qualified_name() - parts = qn.split("/") - assert parts[0] == "default" - assert parts[1] == "alloydb-postgres" - - -class TestWorkflowPackageAlloydbPostgres: - def test_enum_member_exists(self): - assert hasattr(WorkflowPackage, "ALLOYDB_POSTGRES") - - def test_enum_value(self): - assert WorkflowPackage.ALLOYDB_POSTGRES.value == "atlan-alloydb-postgres" - - def test_lookup_by_value(self): - pkg = WorkflowPackage("atlan-alloydb-postgres") - assert pkg == WorkflowPackage.ALLOYDB_POSTGRES - - -class TestPersonaMetadataPolicyAlloydbPostgres: - """ - Reproduces the CME use case: programmatically creating metadata policies - for AlloyDB Postgres connections (REQ-589). - """ - - def test_create_metadata_policy_for_alloydb_postgres(self): - policy = Persona.create_metadata_policy( - name="AlloyDB read access", - persona_id=PERSONA_GUID, - policy_type=AuthPolicyType.ALLOW, - actions={PersonaMetadataAction.READ}, - connection_qualified_name=ALLOYDB_POSTGRES_CONNECTION_QN, - resources={f"entity:{ALLOYDB_POSTGRES_CONNECTION_QN}"}, - ) - - assert policy is not None - assert policy.policy_sub_category == "metadata" - assert policy.connection_qualified_name == ALLOYDB_POSTGRES_CONNECTION_QN - assert f"entity:{ALLOYDB_POSTGRES_CONNECTION_QN}" in policy.policy_resources - assert PersonaMetadataAction.READ.value in policy.policy_actions - assert policy.policy_type == AuthPolicyType.ALLOW - - def test_create_metadata_policy_with_table_resource(self): - table_resource = ( - f"entity:{ALLOYDB_POSTGRES_CONNECTION_QN}/mydb/myschema/mytable" - ) - policy = Persona.create_metadata_policy( - name="AlloyDB table access", - persona_id=PERSONA_GUID, - policy_type=AuthPolicyType.ALLOW, - actions={PersonaMetadataAction.READ, PersonaMetadataAction.UPDATE}, - connection_qualified_name=ALLOYDB_POSTGRES_CONNECTION_QN, - resources={table_resource}, - ) - - assert policy is not None - assert table_resource in policy.policy_resources - assert len(policy.policy_actions) == 2 diff --git a/tests/unit/test_audit_search.py b/tests/unit/test_audit_search.py index 0ef29fd69..d14e2785f 100644 --- a/tests/unit/test_audit_search.py +++ b/tests/unit/test_audit_search.py @@ -41,23 +41,21 @@ def load_json(filename): def _assert_audit_search_results( results: AuditSearchResults, response_json, sorts, bulk=False ): + first = response_json["entityAudits"][0] for audit in results: - assert audit.entity_id == response_json["entityAudits"][0]["entity_id"] - assert ( - audit.entity_qualified_name - == response_json["entityAudits"][0]["entity_qualified_name"] - ) - assert audit.type_name == response_json["entityAudits"][0]["type_name"] + assert audit.entity_id == first["entityId"] + assert audit.entity_qualified_name == first["entityQualifiedName"] + assert audit.type_name == first["typeName"] expected_timestamp = datetime.fromtimestamp( - response_json["entityAudits"][0]["timestamp"] / 1000, tz=timezone.utc + first["timestamp"] / 1000, tz=timezone.utc ) assert audit.timestamp == expected_timestamp expected_created = datetime.fromtimestamp( - response_json["entityAudits"][0]["created"] / 1000, tz=timezone.utc + first["created"] / 1000, tz=timezone.utc ) assert audit.created == expected_created - assert audit.user == response_json["entityAudits"][0]["user"] - assert audit.action == response_json["entityAudits"][0]["action"] + assert audit.user == first["user"] + assert audit.action == first["action"] assert results.total_count == response_json["totalCount"] assert results._bulk == bulk diff --git a/tests_v9/__init__.py b/tests_v9/__init__.py new file mode 100644 index 000000000..de398f096 --- /dev/null +++ b/tests_v9/__init__.py @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. diff --git a/tests_v9/integration/__init__.py b/tests_v9/integration/__init__.py new file mode 100644 index 000000000..578a3ce52 --- /dev/null +++ b/tests_v9/integration/__init__.py @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. diff --git a/tests_v9/integration/adls_asset_test.py b/tests_v9/integration/adls_asset_test.py new file mode 100644 index 000000000..5248093dc --- /dev/null +++ b/tests_v9/integration/adls_asset_test.py @@ -0,0 +1,400 @@ +from typing import Generator + +import pytest + +from pyatlan.model.utils import construct_object_key +from pyatlan.utils import get_parent_qualified_name +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.model.assets import ADLSAccount, ADLSContainer, ADLSObject, Connection +from pyatlan_v9.model.core import Announcement +from pyatlan_v9.model.enums import ( + AnnouncementType, + AtlanConnectorType, + CertificateStatus, + EntityStatus, +) +from tests_v9.integration.client import TestId, delete_asset +from tests_v9.integration.connection_test import create_connection + +MODULE_NAME = TestId.make_unique("ADLS") + +CONNECTOR_TYPE = AtlanConnectorType.ADLS +ADLS_ACCOUNT_NAME = MODULE_NAME +ADLS_CONNECTION_QUALIFIED_NAME = f"{MODULE_NAME}" +CONTAINER_NAME = f"mycontainer_{MODULE_NAME}" +CONTAINER_NAME_OVERLOAD = f"mycontainer_overload_{MODULE_NAME}" +OBJECT_NAME = f"myobject_{MODULE_NAME}.csv" +OBJECT_NAME_PREFIX = f"myobject_{MODULE_NAME}Prefix.csv" +OBJECT_PREFIX = "/some/folder/structure" +OBJECT_NAME_OVERLOAD = f"myobject_overload_{MODULE_NAME}.csv" +CERTIFICATE_STATUS = CertificateStatus.VERIFIED +CERTIFICATE_MESSAGE = "Automated testing of the Python SDK." +ANNOUNCEMENT_TYPE = AnnouncementType.INFORMATION +ANNOUNCEMENT_TITLE = "Python SDK testing." +ANNOUNCEMENT_MESSAGE = "Automated testing of the Python SDK." + + +@pytest.fixture(scope="module") +def connection(client: AtlanClient) -> Generator[Connection, None, None]: + result = create_connection( + client=client, name=MODULE_NAME, connector_type=CONNECTOR_TYPE + ) + yield result + delete_asset(client, guid=result.guid, asset_type=Connection) + + +@pytest.fixture(scope="module") +def adls_account( + client: AtlanClient, connection: Connection +) -> Generator[ADLSAccount, None, None]: + assert connection.qualified_name + to_create = ADLSAccount.creator( + name=ADLS_ACCOUNT_NAME, connection_qualified_name=connection.qualified_name + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=ADLSAccount)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=ADLSAccount) + + +def test_adls_account( + client: AtlanClient, + connection: Connection, + adls_account: ADLSAccount, +): + assert adls_account + assert adls_account.guid + assert adls_account.qualified_name + assert adls_account.connection_qualified_name == connection.qualified_name + assert adls_account.name == ADLS_ACCOUNT_NAME + assert adls_account.connector_name == AtlanConnectorType.ADLS.value + + +@pytest.fixture(scope="module") +def adls_container( + client: AtlanClient, adls_account: ADLSAccount +) -> Generator[ADLSContainer, None, None]: + assert adls_account.qualified_name + to_create = ADLSContainer.creator( + name=CONTAINER_NAME, adls_account_qualified_name=adls_account.qualified_name + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=ADLSContainer)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=ADLSContainer) + + +def test_adls_container( + client: AtlanClient, + adls_account: ADLSAccount, + adls_container: ADLSContainer, +): + assert adls_container + assert adls_container.guid + assert adls_container.qualified_name + assert adls_container.adls_account_qualified_name == adls_account.qualified_name + assert adls_container.adls_account_name == adls_account.name + assert adls_container.name == CONTAINER_NAME + assert adls_container.connector_name == AtlanConnectorType.ADLS.value + + +@pytest.fixture(scope="module") +def adls_container_overload( + client: AtlanClient, adls_account: ADLSAccount, connection: Connection +) -> Generator[ADLSContainer, None, None]: + assert adls_account.qualified_name + assert connection.qualified_name + to_create = ADLSContainer.creator( + name=CONTAINER_NAME_OVERLOAD, + adls_account_qualified_name=adls_account.qualified_name, + connection_qualified_name=connection.qualified_name, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=ADLSContainer)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=ADLSContainer) + + +def test_overload_adls_container( + client: AtlanClient, + adls_account: ADLSAccount, + adls_container_overload: ADLSContainer, +): + assert adls_container_overload + assert adls_container_overload.guid + assert adls_container_overload.qualified_name + assert ( + adls_container_overload.adls_account_qualified_name + == adls_account.qualified_name + ) + assert adls_container_overload.adls_account_name == adls_account.name + assert adls_container_overload.name == CONTAINER_NAME_OVERLOAD + assert adls_container_overload.connector_name == AtlanConnectorType.ADLS.value + + +@pytest.fixture(scope="module") +def adls_object( + client: AtlanClient, adls_container: ADLSContainer +) -> Generator[ADLSObject, None, None]: + assert adls_container.qualified_name + to_create = ADLSObject.creator( + name=OBJECT_NAME, + adls_container_name=adls_container.name, + adls_container_qualified_name=adls_container.qualified_name, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=ADLSObject)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=ADLSObject) + + +@pytest.fixture(scope="module") +def adls_object_prefix( + client: AtlanClient, connection: Connection, adls_container: ADLSContainer +) -> Generator[ADLSObject, None, None]: + assert adls_container.qualified_name + to_create = ADLSObject.creator_with_prefix( + name=OBJECT_NAME_PREFIX, + connection_qualified_name=connection.qualified_name, + adls_container_name=adls_container.name, + adls_container_qualified_name=adls_container.qualified_name, + prefix=OBJECT_PREFIX, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=ADLSObject)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=ADLSObject) + + +def test_adls_object_with_prefix( + client: AtlanClient, + connection: Connection, + adls_container: ADLSContainer, + adls_object_prefix: ADLSObject, +): + assert adls_object_prefix + assert adls_object_prefix.guid + assert adls_object_prefix.qualified_name + assert adls_container.name + assert adls_object_prefix.name == OBJECT_NAME_PREFIX + assert adls_object_prefix.connector_name == AtlanConnectorType.ADLS.value + assert adls_object_prefix.adls_container_name == adls_container.name + assert ( + adls_object_prefix.adls_container_qualified_name + == adls_container.qualified_name + ) + assert adls_object_prefix.adls_object_key == construct_object_key( + OBJECT_PREFIX, adls_object_prefix.name + ) + + +@pytest.fixture(scope="module") +def adls_object_overload( + client: AtlanClient, + adls_container_overload: ADLSContainer, + adls_account: ADLSAccount, + connection: Connection, +) -> Generator[ADLSObject, None, None]: + assert adls_container_overload.qualified_name + assert adls_container_overload.name + assert adls_account.qualified_name + assert adls_account.name + assert connection.qualified_name + to_create = ADLSObject.creator( + name=OBJECT_NAME_OVERLOAD, + adls_container_name=adls_container_overload.name, + adls_container_qualified_name=adls_container_overload.qualified_name, + adls_account_qualified_name=adls_account.qualified_name, + connection_qualified_name=connection.qualified_name, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=ADLSObject)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=ADLSObject) + + +def test_overload_adls_object( + client: AtlanClient, + adls_container_overload: ADLSContainer, + adls_object_overload: ADLSObject, + adls_account: ADLSAccount, +): + assert adls_object_overload + assert adls_object_overload.guid + assert adls_object_overload.qualified_name + assert ( + adls_object_overload.adls_container_qualified_name + == adls_container_overload.qualified_name + ) + assert adls_object_overload.adls_container_name == adls_container_overload.name + assert adls_object_overload.name == OBJECT_NAME_OVERLOAD + assert adls_object_overload.connector_name == AtlanConnectorType.ADLS.value + assert adls_container_overload.qualified_name + assert ( + adls_object_overload.adls_account_qualified_name + == get_parent_qualified_name(adls_container_overload.qualified_name) + ) + assert adls_object_overload.adls_account_name == adls_account.name + + +def test_adls_object( + client: AtlanClient, + adls_container: ADLSContainer, + adls_object: ADLSObject, + adls_account: ADLSAccount, +): + assert adls_object + assert adls_object.guid + assert adls_object.qualified_name + assert adls_object.adls_container_qualified_name == adls_container.qualified_name + assert adls_object.adls_container_name == adls_container.name + assert adls_object.name == OBJECT_NAME + assert adls_object.connector_name == AtlanConnectorType.ADLS.value + assert adls_container.qualified_name + assert adls_object.adls_account_qualified_name == get_parent_qualified_name( + adls_container.qualified_name + ) + assert adls_object.adls_account_name == adls_account.name + + +def test_update_adls_object( + client: AtlanClient, + connection: Connection, + adls_container: ADLSContainer, + adls_object: ADLSObject, +): + assert adls_object.qualified_name + assert adls_object.name + updated = client.asset.update_certificate( + asset_type=ADLSObject, + qualified_name=adls_object.qualified_name, + name=OBJECT_NAME, + certificate_status=CERTIFICATE_STATUS, + message=CERTIFICATE_MESSAGE, + ) + assert updated + assert updated.certificate_status_message == CERTIFICATE_MESSAGE + assert adls_object.qualified_name + assert adls_object.name + updated = client.asset.update_announcement( + asset_type=ADLSObject, + qualified_name=adls_object.qualified_name, + name=OBJECT_NAME, + announcement=Announcement( + announcement_type=ANNOUNCEMENT_TYPE, + announcement_title=ANNOUNCEMENT_TITLE, + announcement_message=ANNOUNCEMENT_MESSAGE, + ), + ) + assert updated + assert updated.announcement_type == ANNOUNCEMENT_TYPE.value + assert updated.announcement_title == ANNOUNCEMENT_TITLE + assert updated.announcement_message == ANNOUNCEMENT_MESSAGE + + +@pytest.mark.order(after="test_update_adls_object") +def test_retrieve_adls_object( + client: AtlanClient, + connection: Connection, + adls_container: ADLSContainer, + adls_object: ADLSObject, +): + b = client.asset.get_by_guid( + adls_object.guid, asset_type=ADLSObject, ignore_relationships=False + ) + assert b + assert not b.is_incomplete + assert b.guid == adls_object.guid + assert b.qualified_name == adls_object.qualified_name + assert b.name == OBJECT_NAME + assert b.connector_name == AtlanConnectorType.ADLS.value + assert b.certificate_status == CERTIFICATE_STATUS + assert b.certificate_status_message == CERTIFICATE_MESSAGE + + +@pytest.mark.order(after="test_retrieve_adls_object") +def test_update_adls_object_again( + client: AtlanClient, + connection: Connection, + adls_container: ADLSContainer, + adls_object: ADLSObject, +): + assert adls_object.qualified_name + assert adls_object.name + updated = client.asset.remove_certificate( + asset_type=ADLSObject, + qualified_name=adls_object.qualified_name, + name=adls_object.name, + ) + assert updated + assert not updated.certificate_status + assert not updated.certificate_status_message + assert adls_object.qualified_name + updated = client.asset.remove_announcement( + qualified_name=adls_object.qualified_name, + asset_type=ADLSObject, + name=adls_object.name, + ) + assert updated + assert not updated.announcement_type + assert not updated.announcement_title + assert not updated.announcement_message + + +@pytest.mark.order(after="test_update_adls_object_again") +def test_delete_adls_object( + client: AtlanClient, + connection: Connection, + adls_container: ADLSContainer, + adls_object: ADLSObject, +): + response = client.asset.delete_by_guid(adls_object.guid) + assert response + assert not response.assets_created(asset_type=ADLSObject) + assert not response.assets_updated(asset_type=ADLSObject) + deleted = response.assets_deleted(asset_type=ADLSObject) + assert deleted + assert len(deleted) == 1 + assert deleted[0].guid == adls_object.guid + assert deleted[0].qualified_name == adls_object.qualified_name + assert deleted[0].delete_handler == "SOFT" + assert deleted[0].status == EntityStatus.DELETED + + +@pytest.mark.order(after="test_delete_adls_object") +def test_read_deleted_adls_object( + client: AtlanClient, + connection: Connection, + adls_container: ADLSContainer, + adls_object: ADLSObject, +): + deleted = client.asset.get_by_guid( + adls_object.guid, asset_type=ADLSObject, ignore_relationships=False + ) + assert deleted + assert deleted.guid == adls_object.guid + assert deleted.qualified_name == adls_object.qualified_name + assert deleted.status == EntityStatus.DELETED + + +@pytest.mark.order(after="test_read_deleted_adls_object") +def test_restore_object( + client: AtlanClient, + connection: Connection, + adls_container: ADLSContainer, + adls_object: ADLSObject, +): + assert adls_object.qualified_name + assert client.asset.restore( + asset_type=ADLSObject, qualified_name=adls_object.qualified_name + ) + assert adls_object.qualified_name + restored = client.asset.get_by_qualified_name( + asset_type=ADLSObject, + qualified_name=adls_object.qualified_name, + ignore_relationships=False, + ) + assert restored + assert restored.guid == adls_object.guid + assert restored.qualified_name == adls_object.qualified_name + assert restored.status == EntityStatus.ACTIVE diff --git a/tests_v9/integration/admin_test.py b/tests_v9/integration/admin_test.py new file mode 100644 index 000000000..f07ccc509 --- /dev/null +++ b/tests_v9/integration/admin_test.py @@ -0,0 +1,434 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2022 Atlan Pte. Ltd. +import math +from datetime import datetime, timedelta +from typing import Generator + +import pytest + +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.model.group import ( + AtlanGroup, + CreateGroupResponse, + GroupAttributes, + GroupRequest, +) +from pyatlan_v9.model.keycloak_events import AdminEventRequest, KeycloakEventRequest +from pyatlan_v9.model.user import UserRequest +from tests_v9.integration.client import TestId + +FIXED_USER = "aryaman" +TODAY = datetime.now().strftime("%Y-%m-%d") +YESTERDAY = (datetime.now() - timedelta(days=1)).strftime("%Y-%m-%d") +MODULE_NAME = TestId.make_unique("Admin") +GROUP_NAME = f"{MODULE_NAME}" + +EMAIL_DOMAIN = "@atlan.com" + +_default_group_count: int = 0 + + +def create_group(client: AtlanClient, name: str) -> CreateGroupResponse: + g = AtlanGroup.creator(alias=name) + r = client.group.creator(g) + return r + + +def delete_group(client: AtlanClient, guid: str) -> None: + client.group.purge(guid) + + +def test_retrieve_roles(client: AtlanClient): + admin_role_guid = client.role_cache.get_id_for_name("$admin") + assert admin_role_guid + + +@pytest.fixture(scope="module") +def group(client: AtlanClient) -> Generator[CreateGroupResponse, None, None]: + to_create = AtlanGroup.creator(GROUP_NAME) + fixed_user = client.user.get_by_username(FIXED_USER) + assert fixed_user + g = client.group.creator(group=to_create, user_ids=[str(fixed_user.id)]) + yield g + delete_group(client, g.group) + + +def _assert_search_results(results, size, TOTAL_ASSETS): + assert results.total_record > size + assert len(results.records) == size + counter = 0 + for result in results: + assert result + counter += 1 + assert counter == TOTAL_ASSETS + assert results + + +def test_create_group(client: AtlanClient, group: CreateGroupResponse): + assert group + r = client.group.get_by_name(GROUP_NAME) + assert r + assert r.records is not None + assert len(r.records) == 1 + group1_full = r.records[0] + assert group1_full + assert group1_full.path + assert group1_full.name + assert group1_full.id == group.group + assert group1_full.attributes + assert not group1_full.attributes.description + mapped_users = group.users + assert mapped_users + fixed_user = client.user.get_by_username(FIXED_USER) + assert fixed_user + assert fixed_user.id in mapped_users.keys() + user_status = mapped_users.get(str(fixed_user.id)) + assert user_status + assert user_status.was_successful() + + +def test_retrieve_all_groups(client: AtlanClient, group: CreateGroupResponse): + global _default_group_count + groups = client.group.get_all() + assert groups.records + assert len(groups.records) >= 1 + for group1 in groups.records: + if group1.is_default(): + _default_group_count += 1 + + +def test_group_get_all_pagination(client: AtlanClient): + results = client.group.get_all(limit=0) + assert results is not None + assert results.filter_record is not None + TOTAL_ASSETS = results.filter_record + limit = max(1, math.ceil(TOTAL_ASSETS / 5)) + groups = client.group.get_all(limit=limit) + _assert_search_results(groups, limit, TOTAL_ASSETS) + + +def test_group_get_pagination(client: AtlanClient, group: CreateGroupResponse): + response = client.group.get(limit=1, count=True) + + assert response + assert response.total_record is not None + assert response.total_record >= 1 + current_page = response.current_page() + assert current_page is not None + assert len(current_page) == 1 + for test_group in response: + assert test_group.id + assert test_group.name + assert test_group.path + assert test_group.attributes + current_page = response.current_page() + assert current_page is not None + assert len(current_page) == 0 + + +def test_group_get_by_name_pagination(client: AtlanClient): + results = client.group.get_by_name(alias=GROUP_NAME, limit=0) + assert results is not None + assert results.filter_record is not None + TOTAL_ASSETS = results.filter_record + limit = max(1, math.ceil(TOTAL_ASSETS / 5)) + groups = client.group.get_by_name(alias=GROUP_NAME, limit=limit) + _assert_search_results(groups, limit, TOTAL_ASSETS) + + +def test_group_get_members_pagination(client: AtlanClient, group: CreateGroupResponse): + groups = client.group.get_by_name(alias=GROUP_NAME) + assert groups + assert groups.records + assert len(groups.records) == 1 + group1 = groups.records[0] + assert group1.id + response = client.group.get_members(guid=group1.id, request=UserRequest(limit=1)) + + assert response + assert response.total_record is not None + assert response.total_record >= 1 + current_page = response.current_page() + assert current_page is not None + assert len(current_page) == 1 + for test_user in response: + assert test_user.username + assert test_user.enabled + current_page = response.current_page() + assert current_page is not None + assert len(current_page) == 0 + + +def test_user_list_pagination(client: AtlanClient, group: CreateGroupResponse): + response = client.user.get(limit=1) + + assert response + assert response.total_record is not None + assert response.total_record > 1 + current_page = response.current_page() + assert current_page is not None + assert len(current_page) == 1 + for test_user in response: + assert test_user.username + assert test_user.enabled + assert test_user.login_events is not None + assert len(test_user.login_events) >= 0 + current_page = response.current_page() + assert current_page is not None + assert len(current_page) == 0 + + +def test_user_groups_pagination(client: AtlanClient, group: CreateGroupResponse): + fixed_user = client.user.get_by_username(FIXED_USER) + assert fixed_user + assert fixed_user.id + response = client.user.get_groups(guid=fixed_user.id, request=GroupRequest(limit=1)) + + assert response + assert response.total_record is not None + assert response.total_record >= 1 + current_page = response.current_page() + assert current_page is not None + assert len(current_page) == 1 + for test_group in response: + assert test_group.id + assert test_group.name + assert test_group.path + assert test_group.attributes + current_page = response.current_page() + assert current_page is not None + assert len(current_page) == 0 + + +def test_user_get_all_pagination(client: AtlanClient): + results = client.user.get_all(limit=0) + assert results is not None + assert results.filter_record is not None + TOTAL_ASSETS = results.filter_record + limit = max(1, math.ceil(TOTAL_ASSETS / 5)) + users = client.user.get_all(limit=limit) + _assert_search_results(users, limit, TOTAL_ASSETS) + + +def test_user_get_by_usernames_pagination(client: AtlanClient): + results = client.user.get_by_usernames(usernames=[FIXED_USER], limit=0) + assert results is not None + assert results.filter_record is not None + TOTAL_ASSETS = results.filter_record + limit = max(1, math.ceil(TOTAL_ASSETS / 5)) + users = client.user.get_by_usernames(usernames=[FIXED_USER], limit=limit) + _assert_search_results(users, limit, TOTAL_ASSETS) + + +def test_user_get_by_email_and_emails_pagination(client: AtlanClient): + results = client.user.get_by_email(email=EMAIL_DOMAIN, limit=0) + assert results is not None + assert results.filter_record is not None + TOTAL_ASSETS = results.filter_record + assert results.records is not None + email = results.records[0].email + limit = max(1, math.ceil(TOTAL_ASSETS / 5)) + emails = client.user.get_by_email(email=EMAIL_DOMAIN, limit=limit) + _assert_search_results(emails, limit, TOTAL_ASSETS) + assert email is not None + results = client.user.get_by_emails(emails=[email], limit=0) + assert results is not None + assert results.filter_record is not None + TOTAL_ASSETS = results.filter_record + limit = max(1, math.ceil(TOTAL_ASSETS / 5)) + emails = client.user.get_by_emails(emails=[email], limit=limit) + _assert_search_results(emails, limit, TOTAL_ASSETS) + + +@pytest.mark.order(after="test_retrieve_all_groups") +def test_retrieve_existing_user(client: AtlanClient, group: CreateGroupResponse): + global _default_group_count + all_users = client.user.get_all() + assert all_users.records + assert len(all_users.records) >= 1 # type: ignore + user1 = client.user.get_by_username(FIXED_USER) + assert user1 + assert user1.id + assert user1.group_count == 1 + _default_group_count + response = client.user.get_by_usernames(usernames=[FIXED_USER]) + assert response + assert response.records is not None + assert len(response.records) == 1 + fixed_user = response.records[0] + assert fixed_user + assert fixed_user.id + users_list = client.user.get_by_usernames(usernames=[]) + assert users_list.records == [] # type: ignore + users_list = client.user.get_by_email(EMAIL_DOMAIN) + assert users_list + assert users_list.records is not None + assert len(users_list.records) >= 1 + email = user1.email + assert email + users_list = client.user.get_by_email(email) + assert users_list + assert users_list.records is not None + assert len(users_list.records) == 1 + assert user1.email == users_list.records[0].email + assert user1.username == users_list.records[0].username + assert user1.attributes == users_list.records[0].attributes + users_list = client.user.get_by_emails(emails=[email]) + assert users_list + assert users_list.records is not None + assert len(users_list.records) == 1 + assert user1.email == users_list.records[0].email + assert user1.username == users_list.records[0].username + assert user1.attributes == users_list.records[0].attributes + users_list = client.user.get_by_emails(emails=[]) + assert users_list.records == [] # type: ignore + + +@pytest.mark.order(after="test_create_group") +def test_update_groups(client: AtlanClient, group: CreateGroupResponse): + groups = client.group.get_by_name(alias=GROUP_NAME) + assert groups + assert groups.records is not None + assert len(groups.records) == 1 + group1 = groups.records[0] + group1.attributes = GroupAttributes(description=["Now with a description!"]) + client.group.updater(group1) + + +@pytest.mark.order(after=["test_update_groups", "test_update_users"]) +def test_updated_groups( + client: AtlanClient, + group: CreateGroupResponse, +): + groups = client.group.get_by_name(alias=GROUP_NAME) + assert groups + assert groups.records is not None + assert len(groups.records) == 1 + group1 = groups.records[0] + assert group1 + assert group1.id == group.group + assert group1.attributes + assert group1.attributes.description == ["Now with a description!"] + assert group1.user_count == 1 + + +@pytest.mark.order(after="test_updated_groups") +def test_remove_user_from_group( + client: AtlanClient, + group: CreateGroupResponse, +): + groups = client.group.get_by_name(alias=GROUP_NAME) + assert groups + assert groups.records is not None + assert len(groups.records) == 1 + group1 = groups.records[0] + assert group1.id + fixed_user = client.user.get_by_username(FIXED_USER) + assert fixed_user + assert fixed_user.id + client.group.remove_users(guid=group1.id, user_ids=[fixed_user.id]) + response = client.group.get_members(guid=group1.id) + assert response + assert not response.records + + +@pytest.mark.order(after="test_remove_user_from_group") +def test_final_user_state( + client: AtlanClient, + group: CreateGroupResponse, +): + global _default_group_count + fixed_user = client.user.get_by_username(FIXED_USER) + assert fixed_user + assert fixed_user.id + response = client.user.get_groups(fixed_user.id) + assert ( + response.records is None + or len(response.records) == 0 + or len(response.records) == _default_group_count + ) + + +@pytest.mark.order(after="test_final_user_state") +def test_retrieve_logs( + client: AtlanClient, +): + request = KeycloakEventRequest(date_from=YESTERDAY, date_to=TODAY) + events = client.admin.get_keycloak_events(request) + assert events + count = 0 + for _ in events: + count += 1 + if count >= 1000: + break + assert count > 0 + + +@pytest.mark.order(after="test_final_user_state") +def test_retrieve_admin_logs( + client: AtlanClient, +): + request = AdminEventRequest(date_from=YESTERDAY, date_to=TODAY) + events = client.admin.get_admin_events(request) + assert events + count = 0 + for _ in events: + count += 1 + if count >= 1000: + break + assert count > 0 + + +def test_get_all_with_limit(client: AtlanClient, group: CreateGroupResponse): + limit = 2 + groups = client.group.get_all(limit=limit) + assert groups.records + assert len(groups.records) == limit + + for group1 in groups.records: + assert group1.id + assert group1.name + assert group1.path is not None + + +def test_get_all_with_columns(client: AtlanClient, group: CreateGroupResponse): + columns = ["path"] + groups = client.group.get_all(columns=columns) + + assert groups + assert groups.records + assert len(groups.records) >= 1 + + for group1 in groups.records: + assert group1.name + assert group1.path is not None + assert group1.attributes is None + assert group1.roles is None + + +def test_get_all_with_sorting(client: AtlanClient, group: CreateGroupResponse): + groups = client.group.get_all(sort="name") + + assert groups + assert len(groups.records) >= 1 # type: ignore + + sorted_names = [group.name for group in groups.records if group.name is not None] # type: ignore + assert sorted_names == sorted(sorted_names) + + +def test_get_all_with_everything(client: AtlanClient, group: CreateGroupResponse): + limit = 2 + columns = ["path", "attributes"] + sort = "name" + + groups = client.group.get_all(limit=limit, columns=columns, sort=sort) + + assert groups + assert len(groups.records) == limit # type: ignore + sorted_names = [group.name for group in groups.records if group.name is not None] # type: ignore + assert sorted_names == sorted(sorted_names) + + for group1 in groups.records: # type: ignore + assert group1.name + assert group1.path is not None + assert group1.roles is None + assert group1.attributes is not None diff --git a/tests_v9/integration/ai_asset_test.py b/tests_v9/integration/ai_asset_test.py new file mode 100644 index 000000000..a5dd078ed --- /dev/null +++ b/tests_v9/integration/ai_asset_test.py @@ -0,0 +1,266 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. +from typing import Generator + +import pytest + +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.model.assets import AIApplication, AIModel, Asset, Connection +from pyatlan_v9.model.enums import ( + AIApplicationDevelopmentStage, + AIDatasetType, + AIModelStatus, +) +from pyatlan_v9.model.fluent_search import FluentSearch +from tests_v9.integration.client import TestId, delete_asset + +MODULE_NAME = TestId.make_unique("AI") + +AI_MODEL_NAME = f"test_ai_model_{MODULE_NAME}" +AI_APPLICATION_NAME = f"test_ai_application_{MODULE_NAME}" +AI_APPLICATION_VERSION = "2.0" + + +@pytest.fixture(scope="module") +def ai_model(client: AtlanClient) -> Generator[AIModel, None, None]: + to_create = AIModel.creator( + name=AI_MODEL_NAME, + ai_model_status=AIModelStatus.ACTIVE, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=AIModel)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=AIModel) + + +def test_ai_model( + ai_model: AIModel, +): + assert ai_model + assert ai_model.guid + assert ai_model.qualified_name + assert ai_model.name == AI_MODEL_NAME + assert ai_model.connector_name == "ai" + assert ai_model.ai_model_status == AIModelStatus.ACTIVE + + +@pytest.fixture(scope="module") +def ai_application(client: AtlanClient) -> Generator[AIApplication, None, None]: + to_create = AIApplication.creator( + name=AI_APPLICATION_NAME, + ai_application_version=AI_APPLICATION_VERSION, + ai_application_development_stage=AIApplicationDevelopmentStage.PRODUCTION, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=AIApplication)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=AIApplication) + + +def test_ai_application( + ai_application: AIApplication, +): + assert ai_application + assert ai_application.guid + assert ai_application.qualified_name + assert ai_application.name == AI_APPLICATION_NAME + assert ai_application.connector_name == "ai" + assert ai_application.ai_application_version == AI_APPLICATION_VERSION + assert ( + ai_application.ai_application_development_stage + == AIApplicationDevelopmentStage.PRODUCTION + ) + + +def _update_ai_application(client, ai_application: AIApplication): + updated = AIApplication.updater( + qualified_name=ai_application.qualified_name, name=ai_application.name + ) + updated.ai_application_development_stage = AIApplicationDevelopmentStage.DEVELOPMENT + updated_response = client.asset.save(updated) + assert updated_response + assert updated_response.mutated_entities.UPDATE[0] + updated_response = updated_response.mutated_entities.UPDATE[0] + assert updated_response.qualified_name + refreshed = client.asset.get_by_qualified_name( + qualified_name=updated_response.qualified_name, + asset_type=AIApplication, + ) + assert refreshed + assert refreshed.name == AI_APPLICATION_NAME + assert ( + refreshed.ai_application_development_stage + == AIApplicationDevelopmentStage.DEVELOPMENT + ) + + +def _update_ai_model(client, ai_model: AIModel): + updated = AIModel.updater( + qualified_name=ai_model.qualified_name, name=ai_model.name + ) + updated.ai_model_version = "2.1" + updated_response = client.asset.save(updated) + assert updated_response + assert updated_response.mutated_entities.UPDATE[0] + updated_response = updated_response.mutated_entities.UPDATE[0] + assert updated_response.qualified_name + refreshed = client.asset.get_by_qualified_name( + qualified_name=updated_response.qualified_name, + asset_type=AIModel, + ) + assert refreshed + assert refreshed.name == AI_MODEL_NAME + assert refreshed.ai_model_version == "2.1" + + +def test_update_ai_assets( + client: AtlanClient, + ai_model: AIModel, + ai_application: AIApplication, +): + _update_ai_application(client, ai_application) + _update_ai_model(client, ai_model) + + +def _assert_response_processes_creator( + mutation_response, asset_list, ai_dataset_type, process_sum, ai_model +): + for i in range(len(asset_list)): + assert mutation_response.mutated_entities.CREATE[i + process_sum] + assert ( + mutation_response.mutated_entities.CREATE[i + process_sum].ai_dataset_type # type: ignore + == ai_dataset_type + ) + if ai_dataset_type == AIDatasetType.OUTPUT: + assert ( + mutation_response.mutated_entities.CREATE[i + process_sum].inputs # type: ignore + and mutation_response.mutated_entities.CREATE[i + process_sum] + .inputs[0] + .guid + == ai_model.guid # type: ignore + ) + assert ( + mutation_response.mutated_entities.CREATE[i + process_sum].outputs # type: ignore + and mutation_response.mutated_entities.CREATE[i + process_sum] + .outputs[0] + .guid # type: ignore + == asset_list[i].guid + ) + else: + assert ( + mutation_response.mutated_entities.CREATE[i + process_sum].inputs # type: ignore + and mutation_response.mutated_entities.CREATE[i + process_sum] + .inputs[0] + .guid + == asset_list[i].guid # type: ignore + ) + assert ( + mutation_response.mutated_entities.CREATE[i + process_sum].outputs # type: ignore + and mutation_response.mutated_entities.CREATE[i + process_sum] + .outputs[0] + .guid # type: ignore + == ai_model.guid + ) + + +def test_ai_model_processes_creator( + client: AtlanClient, + ai_model: AIModel, +): + query = ( + FluentSearch() + .where(Connection.NAME.eq("development")) + .where(Connection.CONNECTOR_NAME.eq("snowflake")) + .include_on_results("qualified_name") + ).to_request() + connection_response = client.asset.search(query).current_page()[0] + assert connection_response.qualified_name + query = ( + FluentSearch() + .where(Asset.CONNECTION_QUALIFIED_NAME.eq(connection_response.qualified_name)) + .where(Asset.TYPE_NAME.eq("View")) + .include_on_results(Asset.NAME) + .include_on_results(Asset.GUID) + .include_on_results(Asset.TYPE_NAME) + ).to_request() + + list_training = [] + list_testing = [] + list_inference = [] + for results in client.asset.search(query): + list_training.append(results) + list_testing.append(results) + list_inference.append(results) + + query = ( + FluentSearch() + .where(Asset.CONNECTION_QUALIFIED_NAME.eq(connection_response.qualified_name)) + .where(Asset.TYPE_NAME.eq("Database")) + .include_on_results(Asset.NAME) + .include_on_results(Asset.GUID) + .include_on_results(Asset.TYPE_NAME) + ).to_request() + + list_validation = [] + list_output = [] + for results in client.asset.search(query): + list_validation.append(results) + list_output.append(results) + + dataset_dict = { + AIDatasetType.TRAINING: list_training, + AIDatasetType.TESTING: list_testing, + AIDatasetType.INFERENCE: list_inference, + AIDatasetType.VALIDATION: list_validation, + AIDatasetType.OUTPUT: list_output, + } + created_processes = AIModel.processes_creator( + ai_model=ai_model, + dataset_dict=dataset_dict, + ) + response = AIModel.processes_batch_save(client, created_processes) + + assert len(response) == 1 + mutation_response = response[0] + assert ( + mutation_response.mutated_entities and mutation_response.mutated_entities.CREATE + ) + currnt_processes_sum = 0 + _assert_response_processes_creator( + mutation_response, list_training, AIDatasetType.TRAINING, 0, ai_model + ) + currnt_processes_sum += len(list_training) + _assert_response_processes_creator( + mutation_response, + list_testing, + AIDatasetType.TESTING, + currnt_processes_sum, + ai_model, + ) + currnt_processes_sum += len(list_testing) + _assert_response_processes_creator( + mutation_response, + list_inference, + AIDatasetType.INFERENCE, + currnt_processes_sum, + ai_model, + ) + currnt_processes_sum += len(list_inference) + _assert_response_processes_creator( + mutation_response, + list_validation, + AIDatasetType.VALIDATION, + currnt_processes_sum, + ai_model, + ) + currnt_processes_sum += len(list_validation) + _assert_response_processes_creator( + mutation_response, + list_output, + AIDatasetType.OUTPUT, + currnt_processes_sum, + ai_model, + ) + currnt_processes_sum += len(list_output) + + assert currnt_processes_sum == len(created_processes) diff --git a/tests_v9/integration/aio/__init__.py b/tests_v9/integration/aio/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests_v9/integration/aio/conftest.py b/tests_v9/integration/aio/conftest.py new file mode 100644 index 000000000..173f68acb --- /dev/null +++ b/tests_v9/integration/aio/conftest.py @@ -0,0 +1,138 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +"""Conftest for async integration tests.""" + +import logging +from typing import Any, AsyncGenerator, Callable, Coroutine, Optional + +import pytest_asyncio + +from pyatlan_v9.client.aio.atlan import DEFAULT_RETRY, AsyncAtlanClient +from pyatlan_v9.errors import AtlanError +from pyatlan_v9.model.assets import Connection, Database, Schema, Table +from pyatlan_v9.model.atlan_image import AtlanImage +from pyatlan_v9.model.enums import AtlanConnectorType, AtlanTagColor, CertificateStatus +from pyatlan_v9.model.typedef import AtlanTagDef +from tests_v9.integration.aio.test_connection import create_connection_async +from tests_v9.integration.aio.utils import delete_asset_async +from tests_v9.integration.client import TestId + +LOGGER = logging.getLogger(__name__) + +# Constants for lineage test fixtures +MODULE_NAME = TestId.make_unique("aio-lineage") +DATABASE_NAME = f"{MODULE_NAME}_db" +SCHEMA_NAME = f"{MODULE_NAME}_schema" +TABLE_NAME = f"{MODULE_NAME}_tbl" +CONNECTOR_TYPE = AtlanConnectorType.SNOWFLAKE +CERTIFICATE_STATUS = CertificateStatus.VERIFIED +CERTIFICATE_MESSAGE = "Automated testing of the Python SDK." + + +@pytest_asyncio.fixture(scope="module") +async def client(): + """Async Atlan client fixture for integration tests.""" + client = AsyncAtlanClient() + yield client + + +@pytest_asyncio.fixture(scope="module") +async def token_client(): + """Async Atlan client fixture for api token integration tests.""" + DEFAULT_RETRY.total = 0 + client = AsyncAtlanClient(retry=DEFAULT_RETRY) + yield client + + +@pytest_asyncio.fixture(scope="module") +async def connection(client: AsyncAtlanClient) -> AsyncGenerator[Connection, None]: + """Async connection fixture.""" + result = await create_connection_async( + client=client, name=MODULE_NAME, connector_type=CONNECTOR_TYPE + ) + yield result + await delete_asset_async(client, guid=result.guid, asset_type=Connection) + + +@pytest_asyncio.fixture(scope="module") +async def database( + client: AsyncAtlanClient, connection: Connection +) -> AsyncGenerator[Database, None]: + """Async database fixture.""" + to_create = Database.creator( + name=DATABASE_NAME, connection_qualified_name=connection.qualified_name + ) + to_create.certificate_status = CERTIFICATE_STATUS + to_create.certificate_status_message = CERTIFICATE_MESSAGE + result = await client.asset.save(to_create) + db = result.assets_created(asset_type=Database)[0] + yield db + await delete_asset_async(client, guid=db.guid, asset_type=Database) + + +@pytest_asyncio.fixture(scope="module") +async def schema( + client: AsyncAtlanClient, + connection: Connection, + database: Database, +) -> AsyncGenerator[Schema, None]: + """Async schema fixture.""" + assert database.qualified_name + to_create = Schema.creator( + name=SCHEMA_NAME, database_qualified_name=database.qualified_name + ) + result = await client.asset.save(to_create) + sch = result.assets_created(asset_type=Schema)[0] + yield sch + await delete_asset_async(client, guid=sch.guid, asset_type=Schema) + + +@pytest_asyncio.fixture(scope="module") +async def table( + client: AsyncAtlanClient, + connection: Connection, + database: Database, + schema: Schema, +) -> AsyncGenerator[Table, None]: + """Async table fixture.""" + assert schema.qualified_name + to_create = Table.creator( + name=TABLE_NAME, schema_qualified_name=schema.qualified_name + ) + result = await client.asset.save(to_create) + tbl = result.assets_created(asset_type=Table)[0] + yield tbl + await delete_asset_async(client, guid=tbl.guid, asset_type=Table) + + +@pytest_asyncio.fixture(scope="module") +async def make_atlan_tag_async( + client: AsyncAtlanClient, +) -> AsyncGenerator[ + Callable[ + [str, AtlanTagColor, Optional[AtlanImage]], Coroutine[Any, Any, AtlanTagDef] + ], + None, +]: + """Async make_atlan_tag fixture for creating and cleaning up Atlan tags.""" + created_names = [] + + async def _make_atlan_tag_async( + name: str, + color: AtlanTagColor = AtlanTagColor.GREEN, + image: Optional[AtlanImage] = None, + ) -> AtlanTagDef: + atlan_tag_def = AtlanTagDef.creator(name=name, color=color, image=image) + r = await client.typedef.creator(atlan_tag_def) + c = r.atlan_tag_defs[0] + created_names.append(c.display_name) + return c + + yield _make_atlan_tag_async + + for n in created_names: + try: + await client.typedef.purge(name=n, typedef_type=AtlanTagDef) + except AtlanError as err: + LOGGER.error(err) diff --git a/tests_v9/integration/aio/test_admin.py b/tests_v9/integration/aio/test_admin.py new file mode 100644 index 000000000..7b7892471 --- /dev/null +++ b/tests_v9/integration/aio/test_admin.py @@ -0,0 +1,454 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. +import math +from datetime import datetime, timedelta +from typing import AsyncGenerator + +import pytest +import pytest_asyncio + +from pyatlan_v9.client.aio.atlan import AsyncAtlanClient +from pyatlan_v9.model.group import AtlanGroup, CreateGroupResponse, GroupRequest +from pyatlan_v9.model.keycloak_events import AdminEventRequest, KeycloakEventRequest +from pyatlan_v9.model.user import UserRequest +from tests_v9.integration.client import TestId + +FIXED_USER = "chris" +TODAY = datetime.now().strftime("%Y-%m-%d") +YESTERDAY = (datetime.now() - timedelta(days=1)).strftime("%Y-%m-%d") +MODULE_NAME = TestId.make_unique("AsyncAdmin") +GROUP_NAME = f"{MODULE_NAME}" + +EMAIL_DOMAIN = "@atlan.com" + +_default_group_count: int = 0 + + +async def create_group_async( + client: AsyncAtlanClient, name: str +) -> CreateGroupResponse: + g = AtlanGroup.creator(alias=name) + r = await client.group.creator(g) + return r + + +async def delete_group_async(client: AsyncAtlanClient, guid: str) -> None: + await client.group.purge(guid) + + +async def test_retrieve_roles(client: AsyncAtlanClient): + admin_role_guid = await client.role_cache.get_id_for_name("$admin") + assert admin_role_guid + + +@pytest_asyncio.fixture(scope="module") +async def group(client: AsyncAtlanClient) -> AsyncGenerator[CreateGroupResponse, None]: + to_create = AtlanGroup.creator(GROUP_NAME) + fixed_user = await client.user.get_by_username(FIXED_USER) + assert fixed_user + g = await client.group.creator(group=to_create, user_ids=[str(fixed_user.id)]) + yield g + await delete_group_async(client, g.group) + + +async def _assert_search_results(results, size, TOTAL_ASSETS): + assert results.total_record > size + assert len(results.records) == size + counter = 0 + async for result in results: + assert result + counter += 1 + assert counter == TOTAL_ASSETS + assert results + + +async def test_create_group(client: AsyncAtlanClient, group: CreateGroupResponse): + assert group + r = await client.group.get_by_name(GROUP_NAME) + assert r + assert r.records is not None + assert len(r.records) == 1 + group1_full = r.records[0] + assert group1_full + assert group1_full.path + assert group1_full.name + assert group1_full.id == group.group + assert group1_full.attributes + assert not group1_full.attributes.description + mapped_users = group.users + assert mapped_users + fixed_user = await client.user.get_by_username(FIXED_USER) + assert fixed_user + assert fixed_user.id in mapped_users.keys() + user_status = mapped_users.get(str(fixed_user.id)) + assert user_status + assert user_status.was_successful() + + +async def test_retrieve_all_groups( + client: AsyncAtlanClient, group: CreateGroupResponse +): + global _default_group_count + groups = await client.group.get_all() + assert groups.records + assert len(groups.records) >= 1 + for group1 in groups.records: + if group1.is_default(): + _default_group_count += 1 + + +async def test_group_get_all_pagination(client: AsyncAtlanClient): + results = await client.group.get_all(limit=0) + assert results is not None + assert results.filter_record is not None + TOTAL_ASSETS = results.filter_record + limit = max(1, math.ceil(TOTAL_ASSETS / 5)) + groups = await client.group.get_all(limit=limit) + await _assert_search_results(groups, limit, TOTAL_ASSETS) + + +async def test_group_get_pagination( + client: AsyncAtlanClient, group: CreateGroupResponse +): + response = await client.group.get(limit=1, count=True) + + assert response + assert response.total_record is not None + assert response.total_record >= 1 + current_page = response.current_page() + assert current_page is not None + assert len(current_page) == 1 + async for test_group in response: + assert test_group.id + assert test_group.name + assert test_group.path + assert test_group.attributes + current_page = response.current_page() + assert current_page is not None + assert len(current_page) == 0 + + +async def test_group_get_by_name_pagination(client: AsyncAtlanClient): + results = await client.group.get_by_name(alias=GROUP_NAME, limit=0) + assert results is not None + assert results.filter_record is not None + TOTAL_ASSETS = results.filter_record + limit = max(1, math.ceil(TOTAL_ASSETS / 5)) + groups = await client.group.get_by_name(alias=GROUP_NAME, limit=limit) + await _assert_search_results(groups, limit, TOTAL_ASSETS) + + +async def test_group_get_members_pagination( + client: AsyncAtlanClient, group: CreateGroupResponse +): + groups = await client.group.get_by_name(alias=GROUP_NAME) + assert groups + assert groups.records + assert len(groups.records) == 1 + group1 = groups.records[0] + assert group1.id + response = await client.group.get_members( + guid=group1.id, request=UserRequest(limit=1) + ) + + assert response + assert response.total_record is not None + assert response.total_record >= 1 + current_page = response.current_page() + assert current_page is not None + assert len(current_page) == 1 + async for test_user in response: + assert test_user.username + assert test_user.enabled + current_page = response.current_page() + assert current_page is not None + assert len(current_page) == 0 + + +async def test_user_list_pagination( + client: AsyncAtlanClient, group: CreateGroupResponse +): + response = await client.user.get(limit=1) + + assert response + assert response.total_record is not None + assert response.total_record > 1 + current_page = response.current_page() + assert current_page is not None + assert len(current_page) == 1 + async for test_user in response: + assert test_user.username + assert test_user.enabled + assert test_user.login_events is not None + assert len(test_user.login_events) >= 0 + current_page = response.current_page() + assert current_page is not None + assert len(current_page) == 0 + + +async def test_user_groups_pagination( + client: AsyncAtlanClient, group: CreateGroupResponse +): + fixed_user = await client.user.get_by_username(FIXED_USER) + assert fixed_user + assert fixed_user.id + response = await client.user.get_groups( + guid=fixed_user.id, request=GroupRequest(limit=1) + ) + + assert response + assert response.total_record is not None + assert response.total_record >= 1 + current_page = response.current_page() + assert current_page is not None + assert len(current_page) == 1 + async for test_group in response: + assert test_group.id + assert test_group.name + assert test_group.path + assert test_group.attributes + current_page = response.current_page() + assert current_page is not None + assert len(current_page) == 0 + + +async def test_user_get_all_pagination(client: AsyncAtlanClient): + results = await client.user.get_all(limit=0) + assert results is not None + assert results.filter_record is not None + TOTAL_ASSETS = results.filter_record + limit = max(1, math.ceil(TOTAL_ASSETS / 5)) + users = await client.user.get_all(limit=limit) + await _assert_search_results(users, limit, TOTAL_ASSETS) + + +async def test_user_get_by_usernames_pagination(client: AsyncAtlanClient): + results = await client.user.get_by_usernames(usernames=[FIXED_USER], limit=0) + assert results is not None + assert results.filter_record is not None + TOTAL_ASSETS = results.filter_record + limit = max(1, math.ceil(TOTAL_ASSETS / 5)) + users = await client.user.get_by_usernames(usernames=[FIXED_USER], limit=limit) + await _assert_search_results(users, limit, TOTAL_ASSETS) + + +async def test_user_get_by_email_and_emails_pagination(client: AsyncAtlanClient): + results = await client.user.get_by_email(email=EMAIL_DOMAIN, limit=0) + assert results is not None + assert results.filter_record is not None + TOTAL_ASSETS = results.filter_record + assert results.records is not None + email = results.records[0].email + limit = max(1, math.ceil(TOTAL_ASSETS / 5)) + emails = await client.user.get_by_email(email=EMAIL_DOMAIN, limit=limit) + await _assert_search_results(emails, limit, TOTAL_ASSETS) + assert email is not None + results = await client.user.get_by_emails(emails=[email], limit=0) + assert results is not None + assert results.filter_record is not None + TOTAL_ASSETS = results.filter_record + limit = max(1, math.ceil(TOTAL_ASSETS / 5)) + emails = await client.user.get_by_emails(emails=[email], limit=limit) + await _assert_search_results(emails, limit, TOTAL_ASSETS) + + +@pytest.mark.order(after="test_retrieve_all_groups") +async def test_retrieve_existing_user( + client: AsyncAtlanClient, group: CreateGroupResponse +): + global _default_group_count + all_users = await client.user.get_all() + assert all_users.records + assert len(all_users.records) >= 1 # type: ignore + user1 = await client.user.get_by_username(FIXED_USER) + assert user1 + assert user1.id + assert user1.group_count == 1 + _default_group_count + response = await client.user.get_by_usernames(usernames=[FIXED_USER]) + assert response + assert response.records is not None + assert len(response.records) == 1 + fixed_user = response.records[0] + assert fixed_user + assert fixed_user.id + users_list = await client.user.get_by_usernames(usernames=[]) + assert users_list.records == [] # type: ignore + users_list = await client.user.get_by_email(EMAIL_DOMAIN) + assert users_list + assert users_list.records is not None + assert len(users_list.records) >= 1 + email = user1.email + assert email + users_list = await client.user.get_by_email(email) + assert users_list + assert users_list.records is not None + assert len(users_list.records) == 1 + assert user1.email == users_list.records[0].email + assert user1.username == users_list.records[0].username + assert user1.attributes == users_list.records[0].attributes + users_list = await client.user.get_by_emails(emails=[email]) + assert users_list + assert users_list.records is not None + assert len(users_list.records) == 1 + assert user1.email == users_list.records[0].email + assert user1.username == users_list.records[0].username + assert user1.attributes == users_list.records[0].attributes + users_list = await client.user.get_by_emails(emails=[]) + assert users_list.records == [] # type: ignore + + +@pytest.mark.order(after="test_create_group") +async def test_update_groups(client: AsyncAtlanClient, group: CreateGroupResponse): + groups = await client.group.get_by_name(alias=GROUP_NAME) + assert groups + assert groups.records is not None + assert len(groups.records) == 1 + group1 = groups.records[0] + group1.attributes = AtlanGroup.Attributes(description=["Now with a description!"]) + await client.group.updater(group1) + + +@pytest.mark.order(after=["test_update_groups", "test_update_users"]) +async def test_updated_groups( + client: AsyncAtlanClient, + group: CreateGroupResponse, +): + groups = await client.group.get_by_name(alias=GROUP_NAME) + assert groups + assert groups.records is not None + assert len(groups.records) == 1 + group1 = groups.records[0] + assert group1 + assert group1.id == group.group + assert group1.attributes + assert group1.attributes.description == ["Now with a description!"] + assert group1.user_count == 1 + + +@pytest.mark.order(after="test_updated_groups") +async def test_remove_user_from_group( + client: AsyncAtlanClient, + group: CreateGroupResponse, +): + groups = await client.group.get_by_name(alias=GROUP_NAME) + assert groups + assert groups.records is not None + assert len(groups.records) == 1 + group1 = groups.records[0] + assert group1.id + fixed_user = await client.user.get_by_username(FIXED_USER) + assert fixed_user + assert fixed_user.id + await client.group.remove_users(guid=group1.id, user_ids=[fixed_user.id]) + response = await client.group.get_members(guid=group1.id) + assert response + assert not response.records + + +@pytest.mark.order(after="test_remove_user_from_group") +async def test_final_user_state( + client: AsyncAtlanClient, + group: CreateGroupResponse, +): + global _default_group_count + fixed_user = await client.user.get_by_username(FIXED_USER) + assert fixed_user + assert fixed_user.id + response = await client.user.get_groups(fixed_user.id) + assert ( + response.records is None + or len(response.records) == 0 + or len(response.records) == _default_group_count + ) + + +@pytest.mark.order(after="test_final_user_state") +async def test_retrieve_logs( + client: AsyncAtlanClient, +): + request = KeycloakEventRequest(date_from=YESTERDAY, date_to=TODAY) + events = await client.admin.get_keycloak_events(request) + assert events + count = 0 + async for _ in events: + count += 1 + if count >= 1000: + break + assert count > 0 + + +@pytest.mark.order(after="test_final_user_state") +async def test_retrieve_admin_logs( + client: AsyncAtlanClient, +): + request = AdminEventRequest(date_from=YESTERDAY, date_to=TODAY) + events = await client.admin.get_admin_events(request) + assert events + count = 0 + async for _ in events: + count += 1 + if count >= 1000: + break + assert count > 0 + + +async def test_get_all_with_limit(client: AsyncAtlanClient, group: CreateGroupResponse): + limit = 2 + groups = await client.group.get_all(limit=limit) + assert groups.records + assert len(groups.records) == limit + + for group1 in groups.records: + assert group1.id + assert group1.name + assert group1.path is not None + + +async def test_get_all_with_columns( + client: AsyncAtlanClient, group: CreateGroupResponse +): + columns = ["path"] + groups = await client.group.get_all(columns=columns) + + assert groups + assert groups.records + assert len(groups.records) >= 1 + + for group1 in groups.records: + assert group1.name + assert group1.path is not None + assert group1.attributes is None + assert group1.roles is None + + +async def test_get_all_with_sorting( + client: AsyncAtlanClient, group: CreateGroupResponse +): + groups = await client.group.get_all(sort="name") + + assert groups + assert len(groups.records) >= 1 # type: ignore + + sorted_names = [group.name for group in groups.records if group.name is not None] # type: ignore + assert sorted_names == sorted(sorted_names) + + +async def test_get_all_with_everything( + client: AsyncAtlanClient, group: CreateGroupResponse +): + limit = 2 + columns = ["path", "attributes"] + sort = "name" + + groups = await client.group.get_all(limit=limit, columns=columns, sort=sort) + + assert groups + assert len(groups.records) == limit # type: ignore + sorted_names = [group.name for group in groups.records if group.name is not None] # type: ignore + assert sorted_names == sorted(sorted_names) + + for group1 in groups.records: # type: ignore + assert group1.name + assert group1.path is not None + assert group1.roles is None + assert group1.attributes is not None diff --git a/tests_v9/integration/aio/test_asset_batch.py b/tests_v9/integration/aio/test_asset_batch.py new file mode 100644 index 000000000..cc5006218 --- /dev/null +++ b/tests_v9/integration/aio/test_asset_batch.py @@ -0,0 +1,526 @@ +import logging +from time import sleep +from typing import AsyncGenerator + +import pytest +import pytest_asyncio + +from pyatlan_v9.client.aio.atlan import AsyncAtlanClient +from pyatlan_v9.client.aio.batch import AsyncBatch +from pyatlan_v9.model.assets import ( + Asset, + Connection, + Database, + MaterialisedView, + Schema, + Table, + View, +) +from pyatlan_v9.model.enums import AssetCreationHandling +from pyatlan_v9.model.fluent_search import FluentSearch +from pyatlan_v9.test_utils import get_random_connector +from tests_v9.integration.aio.utils import delete_asset_async +from tests_v9.integration.client import TestId + +LOGGER = logging.getLogger(__name__) +PREFIX = TestId.make_unique("AsyncBatch") + +CONNECTION_NAME = PREFIX +DATABASE_NAME = PREFIX + "_db" +SCHEMA_NAME = PREFIX + "_schema" +TABLE_NAME = PREFIX + "_table" +VIEW_NAME = PREFIX + "_view" +MVIEW_NAME = PREFIX + "_mview" +BATCH_MAX_SIZE = 10 +CONNECTOR_TYPE = get_random_connector() +DESCRIPTION = "Automated testing of the Python SDK." + + +@pytest_asyncio.fixture(scope="module") +async def wait_for_consistency(): + """ + Wait for assets to be indexed + """ + sleep(10) + + +@pytest_asyncio.fixture(scope="module") +async def connection(client: AsyncAtlanClient) -> AsyncGenerator[Connection, None]: + admin_role_guid = str(await client.role_cache.get_id_for_name("$admin")) + c = await Connection.creator_async( + client=client, + name=CONNECTION_NAME, + connector_type=CONNECTOR_TYPE, + admin_roles=[admin_role_guid], + ) + response = await client.asset.save(c) + connection_created = response.assets_created(asset_type=Connection) + assert connection_created + c = connection_created[0] + yield c + await delete_asset_async(client=client, guid=c.guid, asset_type=Connection) + + +@pytest_asyncio.fixture(scope="module") +async def database( + client: AsyncAtlanClient, connection: Connection +) -> AsyncGenerator[Database, None]: + assert connection.qualified_name + to_create = Database.creator( + name=DATABASE_NAME, connection_qualified_name=connection.qualified_name + ) + result = await client.asset.save(to_create) + assert result + database = result.assets_created(asset_type=Database)[0] + assert database.connector_name == CONNECTOR_TYPE + yield database + + +@pytest_asyncio.fixture(scope="module") +async def schema( + client: AsyncAtlanClient, + database: Database, +) -> AsyncGenerator[Schema, None]: + assert database and database.qualified_name + schema1 = Schema.creator( + name=SCHEMA_NAME, + database_qualified_name=database.qualified_name, + ) + response = await client.asset.save(schema1) + assert (schemas := response.assets_created(asset_type=Schema)) + assert len(schemas) == 1 and schemas[0].database_name == DATABASE_NAME + yield schema1 + + +@pytest_asyncio.fixture(scope="module") +async def batch_table_create( + client: AsyncAtlanClient, schema: Schema +) -> AsyncGenerator[AsyncBatch, None]: + assert schema and schema.qualified_name + batch = AsyncBatch( + client=client, + track=True, + max_size=BATCH_MAX_SIZE, + capture_failures=True, + ) + # 3 tables + for i in range(1, 4): + table = Table.creator( + name=f"{TABLE_NAME}{i}", + schema_qualified_name=schema.qualified_name, + ) + await batch.add(table) + + # 1 view + view = View.creator( + name=VIEW_NAME, + schema_qualified_name=schema.qualified_name, + ) + await batch.add(view) + + # 1 materialized view + mview = MaterialisedView.creator( + name=MVIEW_NAME, + schema_qualified_name=schema.qualified_name, + ) + await batch.add(mview) + + await batch.flush() + yield batch + + assert batch and batch.created + for asset in reversed(batch.created): + assert asset and asset.qualified_name + created = await client.asset.get_by_qualified_name( + qualified_name=asset.qualified_name, + asset_type=asset.__class__, + min_ext_info=True, + ignore_relationships=True, + ) + assert created and created.guid + response = await client.asset.purge_by_guid(created.guid) + if ( + not response + or not response.mutated_entities + or not response.mutated_entities.DELETE + ): + LOGGER.error(f"Failed to remove asset with GUID {asset.guid}.") + + +@pytest_asyncio.fixture(scope="module") +async def batch_table_update( + client: AsyncAtlanClient, schema: Schema +) -> AsyncGenerator[AsyncBatch, None]: + assert schema and schema.qualified_name + batch = AsyncBatch( + client=client, + track=True, + max_size=BATCH_MAX_SIZE, + ) + for i in range(1, 6): + table = Table.creator( + name=f"{TABLE_NAME}{i}", + schema_qualified_name=schema.qualified_name, + ) + await batch.add(table) + yield batch + + +async def test_batch_create(batch_table_create: AsyncBatch, schema: Schema): + batch = batch_table_create + + # Ensure the batch has no failures + assert batch and batch.failures == [] + + # Verify no assets were skipped or restored + assert batch.skipped == [] and batch.num_skipped == 0 + assert batch.restored == [] and batch.num_restored == 0 + + # Verify that 5 assets (3 tables, 1 view, 1 materialized view) were created + assert batch.created and len(batch.created) == 5 and batch.num_created == 5 + assert all( + asset.type_name in {Table.__name__, View.__name__, MaterialisedView.__name__} + for asset in batch.created + ) + + # Ensure the schema was updated + assert batch.updated and len(batch.updated) == 1 and batch.num_updated == 1 + assert batch.updated[0].qualified_name == schema.qualified_name + + +@pytest.mark.order(after="test_batch_create") +async def test_batch_update( + wait_for_consistency, client: AsyncAtlanClient, batch_table_create: AsyncBatch +): + # Table with view qn / mview qn + # 1. table_view_agnostic and update only -- update -- table? -> view? -> mview + # 2. not table_view_agnostic and update only -- skip -- table? -> view? -> mview? - not found + # 3. not table_view_agnostic and not update only -- create -- new table (with view qn) + + create_batch = batch_table_create + for asset in create_batch.created: + if asset.name == f"{TABLE_NAME}1": + table1 = asset + elif asset.name == VIEW_NAME: + view = asset + elif asset.name == MVIEW_NAME: + mview = asset + + assert table1 and table1.qualified_name + assert view and view.qualified_name + assert mview and mview.qualified_name + + # An asset in the batch marked as a table will attempt + # to match a view or mview if not found as a table, and vice versa + # [sub-test-1]: Table with view qn (table_view_agnostic=True, update_only=True) + # Expect the view to be updated since `table_view_agnostic=True` and `update_only=True` + batch1 = AsyncBatch( + client=client, + track=True, + update_only=True, + table_view_agnostic=True, + max_size=BATCH_MAX_SIZE, + ) + SUB_TEST1_DESCRIPTION = f"[sub-test1] {DESCRIPTION}" + + table = Table.updater(qualified_name=view.qualified_name, name=view.name) + table.user_description = SUB_TEST1_DESCRIPTION + await batch1.add(table) + await batch1.flush() + + # Validate that the view was updated + assert batch1.num_updated == 1 + assert batch1.num_created == 0 + assert batch1.num_skipped == 0 + assert batch1.num_restored == 0 + + # Wait for assets to be indexed + sleep(5) + # Make sure user description should be updated on view + results = await ( + FluentSearch() + .where(Asset.TYPE_NAME.eq(View.__name__)) + .where(Asset.QUALIFIED_NAME.eq(view.qualified_name)) + .include_on_results(Asset.USER_DESCRIPTION) + .execute_async(client=client) + ) + assert results and results.count == 1 + assert results.current_page() and len(results.current_page()) == 1 + updated_view = results.current_page()[0] + assert updated_view.qualified_name == view.qualified_name + assert updated_view.user_description == SUB_TEST1_DESCRIPTION + + # [sub-test-11]: Table with mview qn (table_view_agnostic=True, update_only=True) + # Expect the mview to be updated since `table_view_agnostic=True` and `update_only=True` + batch11 = AsyncBatch( + client=client, + track=True, + update_only=True, + table_view_agnostic=True, + max_size=BATCH_MAX_SIZE, + ) + SUB_TEST11_DESCRIPTION = f"[sub-test11] {DESCRIPTION}" + + table = Table.updater(qualified_name=mview.qualified_name, name=mview.name) + table.user_description = SUB_TEST11_DESCRIPTION + await batch11.add(table) + await batch11.flush() + + # Validate that the mview was updated + assert batch11.num_updated == 1 + assert batch11.num_created == 0 + assert batch11.num_skipped == 0 + assert batch11.num_restored == 0 + + # Wait for assets to be indexed + sleep(5) + # Make sure user description should be updated on mview + results = await ( + FluentSearch() + .where(Asset.TYPE_NAME.eq(MaterialisedView.__name__)) + .where(Asset.QUALIFIED_NAME.eq(mview.qualified_name)) + .include_on_results(Asset.USER_DESCRIPTION) + .execute_async(client=client) + ) + assert results and results.count == 1 + assert results.current_page() and len(results.current_page()) == 1 + updated_mview = results.current_page()[0] + assert updated_mview.qualified_name == mview.qualified_name + assert updated_mview.user_description == SUB_TEST11_DESCRIPTION + + # [sub-test-2]: Table with view qn (table_view_agnostic=False, update_only=True) + # Expect the operation to be skipped since a table with the view's qualified name does not exist + batch2 = AsyncBatch( + client=client, + track=True, + update_only=True, + table_view_agnostic=False, + max_size=BATCH_MAX_SIZE, + ) + SUB_TEST2_DESCRIPTION = f"[sub-test2] {DESCRIPTION}" + + table = Table.updater(qualified_name=view.qualified_name, name=view.name) + table.user_description = SUB_TEST2_DESCRIPTION + await batch2.add(table) + await batch2.flush() + + # Neither create or update (since table_view_agnostic = False) + assert batch2.num_skipped == 1 + assert batch2.num_created == 0 + assert batch2.num_updated == 0 + assert batch2.num_restored == 0 + + # [sub-test-3]: Table with view qn (table_view_agnostic=False, update_only=False) + # Expect a new table to be created with the view's qualified name + batch3 = AsyncBatch( + client=client, + track=True, + update_only=False, + table_view_agnostic=False, + max_size=BATCH_MAX_SIZE, + ) + SUB_TEST3_DESCRIPTION = f"[sub-test3] {DESCRIPTION}" + + table = Table.updater(qualified_name=view.qualified_name, name=view.name) + table.user_description = SUB_TEST3_DESCRIPTION + await batch3.add(table) + await batch3.flush() + + # Validate that a new table with view qn was created + assert batch3.num_created == 1 + assert batch3.num_skipped == 0 + assert batch3.num_updated == 0 + assert batch3.num_restored == 0 + + # Wait for assets to be indexed + sleep(5) + results = await ( + FluentSearch() + .where(Asset.TYPE_NAME.eq(Table.__name__)) + .where(Asset.QUALIFIED_NAME.eq(view.qualified_name)) + .include_on_results(Asset.USER_DESCRIPTION) + .execute_async(client=client) + ) + + assert results and results.count == 1 + assert results.current_page() and len(results.current_page()) == 1 + created_table = results.current_page()[0] + assert ( + created_table + and created_table.guid + and created_table.qualified_name == view.qualified_name + ) + # Verify the new table was created and has the updated user description + assert created_table.user_description == SUB_TEST3_DESCRIPTION + + # Cleanup: Delete the newly created table + response = await client.asset.purge_by_guid(created_table.guid) + assert response.mutated_entities and response.mutated_entities.DELETE + + # Table with table qn + # 4. case_insensitive and update_only - update + # 5. not case_insensitive and update_only - update + # 6. not case_insensitive and update_only (same operation) - restore + # 7. case_insensitive and not update_only - create + + # [sub-test-4]: Table with table qn [lowercase] (case_insensitive=True, update_only=True) + # Expect the table to be updated + batch4 = AsyncBatch( + client=client, + track=True, + update_only=True, + case_insensitive=True, + max_size=BATCH_MAX_SIZE, + ) + SUB_TEST4_DESCRIPTION = f"[sub-test4] {DESCRIPTION}" + + table = Table.updater( + qualified_name=table1.qualified_name.lower(), name=table1.name + ) + table.user_description = SUB_TEST4_DESCRIPTION + await batch4.add(table) + await batch4.flush() + + # Validate that the table was updated + assert batch4.num_updated == 1 + assert batch4.num_created == 0 + assert batch4.num_skipped == 0 + assert batch4.num_restored == 0 + + # Wait for assets to be indexed + sleep(5) + results = await ( + FluentSearch() + .where(Asset.TYPE_NAME.eq(Table.__name__)) + .where(Asset.QUALIFIED_NAME.eq(table1.qualified_name)) + .include_on_results(Asset.USER_DESCRIPTION) + .execute_async(client=client) + ) + + assert results and results.count == 1 + assert results.current_page() and len(results.current_page()) == 1 + updated_table = results.current_page()[0] + assert ( + updated_table + and updated_table.guid + and updated_table.qualified_name == table1.qualified_name + ) + assert updated_table.user_description == SUB_TEST4_DESCRIPTION + + # [sub-test-5]: Table with table qn (case_insensitive=False, update_only=True) + # Expect the table to be updated + batch5 = AsyncBatch( + client=client, + track=True, + update_only=True, + case_insensitive=False, + max_size=BATCH_MAX_SIZE, + ) + SUB_TEST5_DESCRIPTION = f"[sub-test5] {DESCRIPTION}" + + table = Table.updater(qualified_name=table1.qualified_name, name=table1.name) + table.user_description = SUB_TEST5_DESCRIPTION + await batch5.add(table) + await batch5.flush() + + # Validate that the table was updated + assert batch5.num_updated == 1 + assert batch5.num_created == 0 + assert batch5.num_skipped == 0 + assert batch5.num_restored == 0 + + # Wait for assets to be indexed + sleep(5) + results = await ( + FluentSearch() + .where(Asset.TYPE_NAME.eq(Table.__name__)) + .where(Asset.QUALIFIED_NAME.eq(table1.qualified_name)) + .include_on_results(Asset.USER_DESCRIPTION) + .execute_async(client=client) + ) + + assert results and results.count == 1 + assert results.current_page() and len(results.current_page()) == 1 + updated_table = results.current_page()[0] + assert ( + updated_table + and updated_table.guid + and updated_table.qualified_name == table1.qualified_name + ) + assert updated_table.user_description == SUB_TEST5_DESCRIPTION + + # [sub-test-6]: (same operation as sub-test-5) + # Table with table qn (case_insensitive=False, update_only=True) + # Expect no operation as update is identical + batch6 = AsyncBatch( + client=client, + track=True, + update_only=True, + case_insensitive=False, + max_size=BATCH_MAX_SIZE, + ) + + table = Table.updater(qualified_name=table1.qualified_name, name=table1.name) + # Use the same user description as before + table.user_description = SUB_TEST5_DESCRIPTION + await batch6.add(table) + await batch6.flush() + + # No operation as update is identical + assert batch6.num_restored == 1 + assert batch6.num_created == 0 + assert batch6.num_updated == 0 + assert batch6.num_skipped == 0 + + # [sub-test-7]: (Table with table qn (case_insensitive=True, update_only=False) + # Expect table to be created + batch7 = AsyncBatch( + client=client, + track=True, + update_only=False, + case_insensitive=False, + max_size=BATCH_MAX_SIZE, + # Also test partial creation handling + creation_handling=AssetCreationHandling.PARTIAL, + ) + SUB_TEST7_DESCRIPTION = f"[sub-test7] {DESCRIPTION}" + + table = Table.updater( + qualified_name=table1.qualified_name.lower(), name=table1.name + ) + table.user_description = SUB_TEST7_DESCRIPTION + await batch7.add(table) + await batch7.flush() + + # Validate that the table was created + assert batch7.num_created == 1 + assert batch7.num_updated == 0 + assert batch7.num_skipped == 0 + assert batch7.num_restored == 0 + + # Wait for assets to be indexed + sleep(5) + results = await ( + FluentSearch() + .where(Asset.TYPE_NAME.eq(Table.__name__)) + .where(Asset.QUALIFIED_NAME.eq(table.qualified_name)) + .include_on_results(Asset.IS_PARTIAL) + .include_on_results(Asset.USER_DESCRIPTION) + .execute_async(client=client) + ) + + assert results and results.count == 1 + assert results.current_page() and len(results.current_page()) == 1 + created_table = results.current_page()[0] + + assert ( + created_table + and created_table.guid + and created_table.qualified_name == table.qualified_name + ) + assert created_table.is_partial + assert created_table.user_description == SUB_TEST7_DESCRIPTION + + # Cleanup: Delete the created table + response = await client.asset.purge_by_guid(created_table.guid) + assert response.mutated_entities and response.mutated_entities.DELETE diff --git a/tests_v9/integration/aio/test_atlan_tag.py b/tests_v9/integration/aio/test_atlan_tag.py new file mode 100644 index 000000000..9488b4c75 --- /dev/null +++ b/tests_v9/integration/aio/test_atlan_tag.py @@ -0,0 +1,170 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. +import contextlib +import logging +import os +import urllib.request +from typing import Any, AsyncGenerator, Callable, Coroutine, Optional + +import pytest_asyncio + +from pyatlan_v9.client.aio.atlan import AsyncAtlanClient +from pyatlan_v9.errors import AtlanError +from pyatlan_v9.model.atlan_image import AtlanImage +from pyatlan_v9.model.enums import AtlanIcon, AtlanTagColor, TagIconType +from pyatlan_v9.model.typedef import AtlanTagDef +from tests_v9.integration.client import TestId + +MODULE_NAME = TestId.make_unique("CLS") + +CLS_IMAGE = f"{MODULE_NAME}_image" +CLS_ICON = f"{MODULE_NAME}_icon" +CLS_EMOJI = f"{MODULE_NAME}_emoji" + +LOGGER = logging.getLogger(__name__) + + +async def wait_for_successful_tagdef_purge_async(name: str, client: AsyncAtlanClient): + """Async version of wait_for_successful_tagdef_purge.""" + import asyncio + + attempts = 0 + max_attempts = 3 + while attempts < max_attempts: + try: + await client.typedef.purge(name=name, typedef_type=AtlanTagDef) + break + except Exception as e: + attempts += 1 + if attempts >= max_attempts: + raise e + await asyncio.sleep(1) + + +@pytest_asyncio.fixture(scope="module") +async def make_atlan_tag( + client: AsyncAtlanClient, +) -> AsyncGenerator[ + Callable[ + [str, AtlanTagColor, Optional[AtlanImage]], Coroutine[Any, Any, AtlanTagDef] + ], + None, +]: + created_names = [] + + async def _make_atlan_tag( + name: str, + color: AtlanTagColor = AtlanTagColor.GREEN, + image: Optional[AtlanImage] = None, + ) -> AtlanTagDef: + atlan_tag_def = AtlanTagDef.creator(name=name, color=color, image=image) + r = await client.typedef.creator(atlan_tag_def) + c = r.atlan_tag_defs[0] + created_names.append(c.display_name) + return c + + yield _make_atlan_tag + + for n in created_names: + try: + await wait_for_successful_tagdef_purge_async(name=n, client=client) + except AtlanError as err: + LOGGER.error(err) + + +@pytest_asyncio.fixture(scope="module") +async def image(client: AsyncAtlanClient) -> AsyncGenerator[AtlanImage, None]: + urllib.request.urlretrieve( + "https://github.com/great-expectations/great_expectations" + "/raw/develop/docs/docusaurus/static/img/gx-mark-160.png", + "gx-mark-160.png", + ) + with open("gx-mark-160.png", "rb") as out_file: + yield await client.upload_image(file=out_file, filename="gx-mark-160.png") + os.remove("gx-mark-160.png") + + +@pytest_asyncio.fixture(scope="module") +async def atlan_tag_with_image( + client: AsyncAtlanClient, + image: AtlanImage, +) -> AsyncGenerator[AtlanTagDef, None]: + cls = AtlanTagDef.creator(name=CLS_IMAGE, color=AtlanTagColor.YELLOW, image=image) + yield (await client.typedef.creator(cls)).atlan_tag_defs[0] + with contextlib.suppress(AtlanError): + await wait_for_successful_tagdef_purge_async(name=CLS_IMAGE, client=client) + + +@pytest_asyncio.fixture(scope="module") +async def atlan_tag_with_icon( + client: AsyncAtlanClient, +) -> AsyncGenerator[AtlanTagDef, None]: + cls = AtlanTagDef.creator( + name=CLS_ICON, + color=AtlanTagColor.YELLOW, + icon=AtlanIcon.BOOK_BOOKMARK, + ) + yield (await client.typedef.creator(cls)).atlan_tag_defs[0] + with contextlib.suppress(AtlanError): + await wait_for_successful_tagdef_purge_async(name=CLS_ICON, client=client) + + +@pytest_asyncio.fixture(scope="module") +async def atlan_tag_with_emoji( + client: AsyncAtlanClient, +) -> AsyncGenerator[AtlanTagDef, None]: + cls = AtlanTagDef.creator( + name=CLS_EMOJI, + emoji="👍", + ) + yield (await client.typedef.creator(cls)).atlan_tag_defs[0] + with contextlib.suppress(AtlanError): + await wait_for_successful_tagdef_purge_async(name=CLS_EMOJI, client=client) + + +async def test_atlan_tag_with_image(atlan_tag_with_image): + assert atlan_tag_with_image + assert atlan_tag_with_image.guid + assert atlan_tag_with_image.display_name == CLS_IMAGE + assert atlan_tag_with_image.name != CLS_IMAGE + assert atlan_tag_with_image.options + assert "color" in atlan_tag_with_image.options.keys() + assert atlan_tag_with_image.options.get("color") == AtlanTagColor.YELLOW.value + assert "imageID" in atlan_tag_with_image.options.keys() + assert atlan_tag_with_image.options.get("imageID") + assert "iconType" in atlan_tag_with_image.options.keys() + assert atlan_tag_with_image.options.get("iconType") == TagIconType.IMAGE.value + + +async def test_atlan_tag_cache(client: AsyncAtlanClient, atlan_tag_with_image): + cls_name = CLS_IMAGE + cls_id = await client.atlan_tag_cache.get_id_for_name(cls_name) + assert cls_id + assert cls_id == atlan_tag_with_image.name + cls_name_found = await client.atlan_tag_cache.get_name_for_id(cls_id) + assert cls_name_found + assert cls_name_found == cls_name + + +async def test_atlan_tag_with_icon(atlan_tag_with_icon): + assert atlan_tag_with_icon + assert atlan_tag_with_icon.guid + assert atlan_tag_with_icon.display_name == CLS_ICON + assert atlan_tag_with_icon.name != CLS_ICON + assert atlan_tag_with_icon.options + assert "color" in atlan_tag_with_icon.options.keys() + assert atlan_tag_with_icon.options.get("color") == AtlanTagColor.YELLOW.value + assert not atlan_tag_with_icon.options.get("imageID") + assert "iconType" in atlan_tag_with_icon.options.keys() + assert atlan_tag_with_icon.options.get("iconType") == TagIconType.ICON.value + + +async def test_atlan_tag_with_emoji(atlan_tag_with_emoji): + assert atlan_tag_with_emoji + assert atlan_tag_with_emoji.guid + assert atlan_tag_with_emoji.display_name == CLS_EMOJI + assert atlan_tag_with_emoji.name != CLS_EMOJI + assert atlan_tag_with_emoji.options + assert not atlan_tag_with_emoji.options.get("imageID") + assert "iconType" in atlan_tag_with_emoji.options.keys() + assert atlan_tag_with_emoji.options.get("iconType") == TagIconType.EMOJI.value diff --git a/tests_v9/integration/aio/test_client.py b/tests_v9/integration/aio/test_client.py new file mode 100644 index 000000000..18090237a --- /dev/null +++ b/tests_v9/integration/aio/test_client.py @@ -0,0 +1,1695 @@ +import time +from dataclasses import dataclass +from typing import AsyncGenerator, List, Optional +from unittest.mock import patch + +import pytest +import pytest_asyncio +from httpx import Headers + +from pyatlan import __version__ as VERSION +from pyatlan.client.common.audit import LOGGER as AUDIT_LOGGER +from pyatlan.client.common.search_log import LOGGER as SEARCH_LOG_LOGGER +from pyatlan.pkg.utils import get_client_async +from pyatlan.utils import get_python_version +from pyatlan_v9.client.aio.atlan import DEFAULT_RETRY, AsyncAtlanClient +from pyatlan_v9.errors import AuthenticationError, InvalidRequestError, NotFoundError +from pyatlan_v9.model.aio.audit import AsyncAuditSearchResults +from pyatlan_v9.model.aio.search_log import AsyncSearchLogResults +from pyatlan_v9.model.api_tokens import ApiToken +from pyatlan_v9.model.assets import ( + Asset, + AtlasGlossary, + AtlasGlossaryCategory, + AtlasGlossaryTerm, + Database, + Schema, + Table, +) +from pyatlan_v9.model.audit import AuditSearchRequest +from pyatlan_v9.model.core import Announcement +from pyatlan_v9.model.enums import ( + AnnouncementType, + AtlanConnectorType, + SortOrder, + UTMTags, +) +from pyatlan_v9.model.fluent_search import CompoundQuery, FluentSearch +from pyatlan_v9.model.search import ( + DSL, + Bool, + IndexSearchRequest, + IndexSearchRequestMetadata, # Legacy: FluentSearch returns Pydantic IndexSearchRequest + SortItem, + Term, +) +from pyatlan_v9.model.search_log import ( + AssetViews, + SearchLogRequest, + SearchLogViewResults, +) +from pyatlan_v9.model.user import UserMinimalResponse +from tests_v9.integration.aio.utils import ( + async_search_with_retry, + create_database_async, + create_glossary_async, + create_token_async, + delete_asset_async, + delete_token_async, + remove_announcement_async, + remove_certificate_async, + update_announcement_async, + update_certificate_async, +) +from tests_v9.integration.client import TestId + +CLASSIFICATION_NAME = "Issue" +CLASSIFICATION_NAME2 = "Confidential" +SL_SORT_BY_TIMESTAMP = SortItem(field="timestamp", order=SortOrder.ASCENDING) +SL_SORT_BY_GUID = SortItem(field="entityGuidsAll", order=SortOrder.ASCENDING) +SL_SORT_BY_QUALIFIED_NAME = SortItem( + field="entityQFNamesAll", order=SortOrder.ASCENDING +) +AUDIT_SORT_BY_GUID = SortItem(field="entityId", order=SortOrder.ASCENDING) +AUDIT_SORT_BY_LATEST = SortItem("created", order=SortOrder.DESCENDING) +MODULE_NAME = TestId.make_unique("AsyncClient") +TEST_USER_DESCRIPTION = "Automated testing of the Python SDK. (USER)" +TEST_SYSTEM_DESCRIPTION = "Automated testing of the Python SDK. (SYSTEM)" +call_count = 0 + + +@pytest_asyncio.fixture(scope="module") +async def current_user(client: AsyncAtlanClient) -> UserMinimalResponse: + return await client.user.get_current() + + +@pytest_asyncio.fixture(scope="module") +async def token(token_client: AsyncAtlanClient) -> AsyncGenerator[ApiToken, None]: + token = None + try: + token = await create_token_async(token_client, MODULE_NAME) + yield token + finally: + await delete_token_async(token_client, token) + + +@pytest_asyncio.fixture(scope="module") +async def expired_token( + token_client: AsyncAtlanClient, +) -> AsyncGenerator[ApiToken, None]: + token = None + try: + token = await token_client.token.creator( + f"{MODULE_NAME}-expired", validity_seconds=1 + ) + time.sleep(5) + yield token + finally: + await delete_token_async(token_client, token) + + +@pytest_asyncio.fixture(scope="module") +async def argo_fake_token( + token_client: AsyncAtlanClient, +) -> AsyncGenerator[ApiToken, None]: + token = None + try: + token = await token_client.token.creator(f"{MODULE_NAME}-fake-argo") + yield token + finally: + await delete_token_async(token_client, token) + + +@pytest_asyncio.fixture(scope="module") +async def glossary( + client: AsyncAtlanClient, +) -> AsyncGenerator[AtlasGlossary, None]: + g = AtlasGlossary.creator(name=MODULE_NAME) + g.description = TEST_SYSTEM_DESCRIPTION + g.user_description = TEST_USER_DESCRIPTION + response = await client.asset.save(g) + result = response.assets_created(AtlasGlossary)[0] + assert result + yield result + await delete_asset_async(client, guid=g.guid, asset_type=AtlasGlossary) + + +@pytest_asyncio.fixture(scope="module") +async def term( + client: AsyncAtlanClient, glossary: AtlasGlossary +) -> AsyncGenerator[AtlasGlossaryTerm, None]: + t = AtlasGlossaryTerm.creator( + name=MODULE_NAME, + glossary_guid=glossary.guid, + ) + t.description = f"{TEST_SYSTEM_DESCRIPTION} Term" + t.user_description = f"{TEST_USER_DESCRIPTION} Term" + response = await client.asset.save(t) + result = response.assets_created(AtlasGlossaryTerm)[0] + assert result + yield result + await delete_asset_async(client, guid=t.guid, asset_type=AtlasGlossaryTerm) + + +@dataclass() +class AuditInfo: + qualified_name: str = "" + type_name: str = "" + guid: str = "" + + +@pytest_asyncio.fixture(scope="module") +async def audit_info(): + return AuditInfo() + + +@pytest_asyncio.fixture(scope="module") +async def announcement(): + return Announcement( + announcement_title="Important Announcement", + announcement_message="Very important info", + announcement_type=AnnouncementType.ISSUE, + ) + + +@pytest_asyncio.fixture(scope="module") +async def term1( + client: AsyncAtlanClient, glossary: AtlasGlossary +) -> AsyncGenerator[AtlasGlossaryTerm, None]: + t = AtlasGlossaryTerm.creator( + name=f"{MODULE_NAME}-term1", + glossary_guid=glossary.guid, + ) + response = await client.asset.save(t) + result = response.assets_created(AtlasGlossaryTerm)[0] + assert result + yield result + await delete_asset_async(client, guid=result.guid, asset_type=AtlasGlossaryTerm) + + +@pytest_asyncio.fixture(scope="module") +async def term2( + client: AsyncAtlanClient, glossary: AtlasGlossary +) -> AsyncGenerator[AtlasGlossaryTerm, None]: + t = AtlasGlossaryTerm.creator( + name=f"{MODULE_NAME}-term2", + glossary_guid=glossary.guid, + ) + response = await client.asset.save(t) + result = response.assets_created(AtlasGlossaryTerm)[0] + assert result + yield result + await delete_asset_async(client, guid=result.guid, asset_type=AtlasGlossaryTerm) + + +@pytest_asyncio.fixture(scope="module") +async def category1( + client: AsyncAtlanClient, glossary: AtlasGlossary +) -> AsyncGenerator[AtlasGlossaryCategory, None]: + c = AtlasGlossaryCategory.creator( + name=f"{MODULE_NAME}-category1", + glossary_guid=glossary.guid, + ) + response = await client.asset.save(c) + result = response.assets_created(AtlasGlossaryCategory)[0] + assert result + yield result + await delete_asset_async(client, guid=result.guid, asset_type=AtlasGlossaryCategory) + + +@pytest_asyncio.fixture(scope="module") +async def database(client: AsyncAtlanClient) -> AsyncGenerator[Database, None]: + database_obj = await create_database_async(client, MODULE_NAME) + assert database_obj + yield database_obj + await delete_asset_async(client, guid=database_obj.guid, asset_type=Database) + + +@pytest_asyncio.fixture(scope="module") +async def schema_with_db_qn( + client: AsyncAtlanClient, database: Database +) -> AsyncGenerator[Schema, None]: + assert database.qualified_name is not None + s = Schema.creator( + name=f"{MODULE_NAME}-schema", database_qualified_name=database.qualified_name + ) + s.qualified_name = database.qualified_name # Same QN as database for testing + response = await client.asset.save(s) + result = response.assets_created(Schema)[0] + assert result + yield result + await delete_asset_async(client, guid=result.guid, asset_type=Schema) + + +@pytest_asyncio.fixture(scope="module") +async def audit_glossary( + client: AsyncAtlanClient, +) -> AsyncGenerator[AtlasGlossary, None]: + created_glossary = await create_glossary_async( + client, TestId.make_unique("test-audit-glossary") + ) + yield created_glossary + await delete_asset_async( + client, guid=created_glossary.guid, asset_type=AtlasGlossary + ) + + +@pytest_asyncio.fixture(scope="module") +async def generate_audit_entries( + client: AsyncAtlanClient, audit_glossary: AtlasGlossary +): + log_count = 5 + for i in range(log_count): + updater = AtlasGlossary.updater( + qualified_name=audit_glossary.qualified_name, + name=audit_glossary.name, + ) + updater.description = f"Updated description {i + 1}" + await client.asset.save(updater) + time.sleep(1) + + request = AuditSearchRequest.by_guid(guid=audit_glossary.guid, size=log_count) + response = await client.audit.search(request) + assert response.total_count >= log_count, ( + f"audit search failed, expected at least {log_count} log_count but got {response.total_count}." + ) + # Force a wait to allow search entries to be indexed + time.sleep(10) + + +async def _view_test_glossary_by_search( + client: AsyncAtlanClient, sl_glossary: AtlasGlossary +) -> None: + time.sleep(2) + index = ( + FluentSearch().where(Asset.GUID.eq(sl_glossary.guid, case_insensitive=True)) + ).to_request() + index.request_metadata = IndexSearchRequestMetadata( + utm_tags=[ + UTMTags.ACTION_ASSET_VIEWED, + UTMTags.UI_PROFILE, + UTMTags.UI_SIDEBAR, + UTMTags.PROJECT_SDK_PYTHON, + ], + save_search_log=True, + ) + response = await client.asset.search(index) + assert response.count == 1 + assert response.current_page()[0].name == sl_glossary.name + time.sleep(2) + + +@pytest_asyncio.fixture(scope="module") +async def generate_search_logs(client: AsyncAtlanClient, sl_glossary: AtlasGlossary): + log_count = 5 + + for _ in range(log_count): + await _view_test_glossary_by_search(client, sl_glossary) + time.sleep(1) + + request = SearchLogRequest.views_by_guid(guid=sl_glossary.guid, size=20) + response = await client.search_log.search(request) + assert response.count >= log_count, ( + f"Expected at least {log_count} logs, but got {response.count}." + ) + # Force a wait to allow search entries to be indexed + time.sleep(10) + + +async def _assert_search_log_results( + results, expected_sorts, size, TOTAL_LOG_ENTRIES, bulk=False +): + assert results.count > size + assert len(results.current_page()) == size + counter = 0 + async for log in results: + assert log + counter += 1 + assert counter == TOTAL_LOG_ENTRIES + assert results + assert results._bulk is bulk + assert results._criteria.dsl.sort == expected_sorts + + +async def _assert_audit_search_results( + results, expected_sorts, size, TOTAL_AUDIT_ENTRIES, bulk=False +): + assert results.total_count > size + assert len(results.current_page()) == size + counter = 0 + async for audit in results: + assert audit + counter += 1 + assert counter == TOTAL_AUDIT_ENTRIES + assert results + assert results._bulk is bulk + assert results._criteria.dsl.sort == expected_sorts + + +@pytest_asyncio.fixture(scope="module") +async def sl_glossary( + client: AsyncAtlanClient, +) -> AsyncGenerator[AtlasGlossary, None]: + g = await create_glossary_async( + client, TestId.make_unique(f"{MODULE_NAME}-sl-glossary") + ) + yield g + await delete_asset_async(client, guid=g.guid, asset_type=AtlasGlossary) + + +async def test_append_terms_with_guid( + client: AsyncAtlanClient, + term1: AtlasGlossaryTerm, + database: Database, +): + time.sleep(5) + assert ( + updated_database := await client.asset.append_terms( + guid=database.guid, asset_type=Database, terms=[term1] + ) + ) + # Retrieve the updated database to verify terms were appended + retrieved_database = await client.asset.get_by_guid( + guid=updated_database.guid, asset_type=Database, ignore_relationships=False + ) + assert retrieved_database.assigned_terms + assert len(retrieved_database.assigned_terms) == 1 + assert retrieved_database.assigned_terms[0].guid == term1.guid + + +async def test_append_terms_with_qualified_name( + client: AsyncAtlanClient, + term1: AtlasGlossaryTerm, + database: Database, +): + time.sleep(5) + assert await client.asset.append_terms( + qualified_name=database.qualified_name, asset_type=Database, terms=[term1] + ) + # Retrieve the updated database to verify terms were appended + retrieved_database = await client.asset.get_by_guid( + guid=database.guid, asset_type=Database, ignore_relationships=False + ) + assert retrieved_database.assigned_terms + assert len(retrieved_database.assigned_terms) == 1 + assert retrieved_database.assigned_terms[0].guid == term1.guid + + +async def test_append_terms_using_ref_by_guid_for_term( + client: AsyncAtlanClient, + term1: AtlasGlossaryTerm, + database: Database, +): + time.sleep(5) + assert await client.asset.append_terms( + qualified_name=database.qualified_name, + asset_type=Database, + terms=[AtlasGlossaryTerm.ref_by_guid(guid=term1.guid)], + ) + # Retrieve the updated database to verify terms were appended + retrieved_database = await client.asset.get_by_guid( + guid=database.guid, asset_type=Database, ignore_relationships=False + ) + assert retrieved_database.assigned_terms + assert len(retrieved_database.assigned_terms) == 1 + assert retrieved_database.assigned_terms[0].guid == term1.guid + + +async def test_append_terms_with_same_qn( + client: AsyncAtlanClient, + term1: AtlasGlossaryTerm, + database: Database, + schema_with_db_qn: Schema, +): + time.sleep(5) + assert schema_with_db_qn.qualified_name == database.qualified_name + assert await client.asset.append_terms( + qualified_name=database.qualified_name, + asset_type=Database, + terms=[AtlasGlossaryTerm.ref_by_guid(guid=term1.guid)], + ) + assert await client.asset.append_terms( + qualified_name=schema_with_db_qn.qualified_name, + asset_type=Schema, + terms=[AtlasGlossaryTerm.ref_by_guid(guid=term1.guid)], + ) + + +async def test_replace_a_term( + client: AsyncAtlanClient, + term1: AtlasGlossaryTerm, + term2: AtlasGlossaryTerm, + database: Database, +): + time.sleep(5) + assert await client.asset.append_terms( + qualified_name=database.qualified_name, + asset_type=Database, + terms=[AtlasGlossaryTerm.ref_by_guid(guid=term1.guid)], + ) + + assert await client.asset.replace_terms( + guid=database.guid, asset_type=Database, terms=[term2] + ) + + retrieved_database = await client.asset.get_by_guid( + guid=database.guid, asset_type=Database, ignore_relationships=False + ) + assert retrieved_database.assigned_terms + assert len(retrieved_database.assigned_terms) == 1 + assert retrieved_database.assigned_terms[0].guid == term2.guid + + +async def test_replace_terms_with_same_qn( + client: AsyncAtlanClient, + term2: AtlasGlossaryTerm, + database: Database, + schema_with_db_qn: Schema, +): + time.sleep(5) + assert schema_with_db_qn.qualified_name == database.qualified_name + assert await client.asset.replace_terms( + guid=database.guid, asset_type=Database, terms=[term2] + ) + assert await client.asset.replace_terms( + guid=schema_with_db_qn.guid, asset_type=Schema, terms=[term2] + ) + + +async def test_replace_all_term( + client: AsyncAtlanClient, + term1: AtlasGlossaryTerm, + database: Database, +): + time.sleep(5) + assert await client.asset.append_terms( + qualified_name=database.qualified_name, + asset_type=Database, + terms=[AtlasGlossaryTerm.ref_by_guid(guid=term1.guid)], + ) + + assert await client.asset.replace_terms( + guid=database.guid, asset_type=Database, terms=[] + ) + + retrieved_database = await client.asset.get_by_guid( + guid=database.guid, asset_type=Database, ignore_relationships=False + ) + assert retrieved_database.assigned_terms == [] + assert len(retrieved_database.assigned_terms) == 0 + + +async def test_remove_term( + client: AsyncAtlanClient, + term1: AtlasGlossaryTerm, + term2: AtlasGlossaryTerm, + database: Database, +): + time.sleep(5) + assert await client.asset.append_terms( + qualified_name=database.qualified_name, + asset_type=Database, + terms=[ + AtlasGlossaryTerm.ref_by_guid(guid=term1.guid), + AtlasGlossaryTerm.ref_by_guid(guid=term2.guid), + ], + ) + + assert await client.asset.remove_terms( + guid=database.guid, + asset_type=Database, + terms=[AtlasGlossaryTerm.ref_by_guid(term1.guid)], + ) + + retrieved_database = await client.asset.get_by_guid( + guid=database.guid, asset_type=Database, ignore_relationships=False + ) + assert retrieved_database.assigned_terms + assert len(retrieved_database.assigned_terms) == 1 + assert retrieved_database.assigned_terms[0].guid == term2.guid + + +async def test_remove_terms_with_same_qn( + client: AsyncAtlanClient, + term1: AtlasGlossaryTerm, + term2: AtlasGlossaryTerm, + database: Database, + schema_with_db_qn: Schema, +): + time.sleep(5) + assert schema_with_db_qn.qualified_name == database.qualified_name + assert ( + updated_database := await client.asset.append_terms( + qualified_name=database.qualified_name, + asset_type=Database, + terms=[ + AtlasGlossaryTerm.ref_by_guid(guid=term1.guid), + AtlasGlossaryTerm.ref_by_guid(guid=term2.guid), + ], + ) + ) + assert await client.asset.remove_terms( + guid=updated_database.guid, + asset_type=Database, + terms=[AtlasGlossaryTerm.ref_by_guid(term1.guid)], + ) + assert ( + updated_schema := await client.asset.append_terms( + qualified_name=schema_with_db_qn.qualified_name, + asset_type=Schema, + terms=[ + AtlasGlossaryTerm.ref_by_guid(guid=term1.guid), + AtlasGlossaryTerm.ref_by_guid(guid=term2.guid), + ], + ) + ) + assert await client.asset.remove_terms( + guid=updated_schema.guid, + asset_type=Schema, + terms=[AtlasGlossaryTerm.ref_by_guid(term1.guid)], + ) + + +async def test_find_connections_by_name(client: AsyncAtlanClient): + connections = await client.asset.find_connections_by_name( + name="development", + connector_type=AtlanConnectorType.SNOWFLAKE, + attributes=["connectorName"], + ) + assert len(connections) == 1 + assert connections[0].connector_name == AtlanConnectorType.SNOWFLAKE.value + + +async def test_get_asset_by_guid_good_guid( + client: AsyncAtlanClient, glossary: AtlasGlossary +): + glossary = await client.asset.get_by_guid( + glossary.guid, AtlasGlossary, ignore_relationships=False + ) + assert isinstance(glossary, AtlasGlossary) + + +async def test_get_asset_by_guid_without_asset_type( + client: AsyncAtlanClient, glossary: AtlasGlossary +): + glossary = await client.asset.get_by_guid(glossary.guid, ignore_relationships=False) + assert isinstance(glossary, AtlasGlossary) + + +async def test_get_minimal_asset_without_asset_type( + client: AsyncAtlanClient, glossary: AtlasGlossary +): + glossary = await client.asset.retrieve_minimal(glossary.guid) + assert isinstance(glossary, AtlasGlossary) + + +async def test_get_asset_by_guid_when_table_specified_and_glossary_returned_raises_not_found_error( + client: AsyncAtlanClient, glossary: AtlasGlossary +): + guid = glossary.guid + with pytest.raises( + NotFoundError, + match=f"ATLAN-PYTHON-404-002 Asset with GUID {guid} is not of the type requested: Table.", + ): + await client.asset.get_by_guid(guid, Table, ignore_relationships=False) + + +async def test_get_by_guid_with_fs(client: AsyncAtlanClient, term: AtlasGlossaryTerm): + time.sleep(5) + # Default - should call `GET_ENTITY_BY_GUID` API + result = await client.asset.get_by_guid( + guid=term.guid, asset_type=AtlasGlossaryTerm + ) + assert isinstance(result, AtlasGlossaryTerm) + assert result.guid == term.guid + assert hasattr(result, "attributes") + assert result.attributes.name == term.name + assert result.attributes.qualified_name == term.qualified_name + assert result.description == f"{TEST_SYSTEM_DESCRIPTION} Term" + assert result.user_description == f"{TEST_USER_DESCRIPTION} Term" + # Ensure no relationship attributes are present + assert not result.anchor + + # Should call `GET_ENTITY_BY_GUID` API with `ignore_relationships=False` + result = await client.asset.get_by_guid( + guid=term.guid, asset_type=AtlasGlossaryTerm, ignore_relationships=False + ) + assert isinstance(result, AtlasGlossaryTerm) + assert result.guid == term.guid + assert hasattr(result, "attributes") + assert result.attributes.name == term.name + assert result.attributes.qualified_name == term.qualified_name + assert result.description == f"{TEST_SYSTEM_DESCRIPTION} Term" + assert result.user_description == f"{TEST_USER_DESCRIPTION} Term" + assert result.anchor + # These are not returned by the `GET_ENTITY_BY_GUID` API + assert not result.anchor.description + assert not result.anchor.user_description + + +async def test_get_by_qualified_name_with_fs( + client: AsyncAtlanClient, term: AtlasGlossaryTerm +): + time.sleep(5) + # Default - should call `GET_ENTITY_BY_GUID` API + assert term and term.qualified_name + result = await client.asset.get_by_qualified_name( + qualified_name=term.qualified_name, asset_type=AtlasGlossaryTerm + ) + assert isinstance(result, AtlasGlossaryTerm) + assert result.guid == term.guid + assert hasattr(result, "attributes") + assert result.attributes.name == term.name + assert result.attributes.qualified_name == term.qualified_name + assert result.description == f"{TEST_SYSTEM_DESCRIPTION} Term" + assert result.user_description == f"{TEST_USER_DESCRIPTION} Term" + # Ensure no relationship attributes are present + assert not result.anchor + + +async def test_get_asset_by_guid_bad_with_non_existent_guid_raises_not_found_error( + client: AsyncAtlanClient, +): + with pytest.raises( + NotFoundError, + match="ATLAN-PYTHON-404-000 Server responded with a not found " + "error ATLAS-404-00-005: Given instance guid 76d54dd6 is invalid/not found", + ): + await client.asset.get_by_guid( + "76d54dd6", AtlasGlossary, ignore_relationships=False + ) + + +async def test_upsert_when_no_changes( + client: AsyncAtlanClient, glossary: AtlasGlossary +): + response = await client.asset.save(glossary) + assert len(response.assets_created(AtlasGlossary)) == 0 + assert len(response.assets_updated(AtlasGlossary)) == 0 + + +async def test_get_by_qualified_name(client: AsyncAtlanClient, glossary: AtlasGlossary): + assert glossary.qualified_name is not None + glossary = await client.asset.get_by_qualified_name( + glossary.qualified_name, AtlasGlossary, ignore_relationships=False + ) + assert isinstance(glossary, AtlasGlossary) + + +async def test_get_by_qualified_name_when_superclass_specified_raises_not_found_error( + client: AsyncAtlanClient, glossary: AtlasGlossary +): + assert glossary.qualified_name is not None + qualified_name = glossary.qualified_name + with pytest.raises( + NotFoundError, + match=f"ATLAN-PYTHON-404-014 The Asset asset could not be found by name: {qualified_name}.", + ): + await client.asset.get_by_qualified_name( + qualified_name, Asset, ignore_relationships=False + ) + + +async def test_add_classification(client: AsyncAtlanClient, term1: AtlasGlossaryTerm): + assert term1.qualified_name is not None + await client.asset.add_atlan_tags( + asset_type=AtlasGlossaryTerm, + qualified_name=term1.qualified_name, + atlan_tag_names=[CLASSIFICATION_NAME], + propagate=True, + remove_propagation_on_delete=True, + restrict_lineage_propagation=False, + ) + glossary_term = await client.asset.get_by_guid( + guid=term1.guid, asset_type=AtlasGlossaryTerm, ignore_relationships=False + ) + assert glossary_term.atlan_tags + assert len(glossary_term.atlan_tags) == 1 + classification = glossary_term.atlan_tags[0] + assert str(classification.type_name) == CLASSIFICATION_NAME + + +@pytest.mark.order(after="test_add_classification") +async def test_include_atlan_tag_names( + client: AsyncAtlanClient, term1: AtlasGlossaryTerm +): + assert term1 and term1.qualified_name + + query = Term.with_type_name(term1.type_name) + Term.with_name(term1.name) + request = IndexSearchRequest( + dsl=DSL(query=query), exclude_atlan_tags=True, include_atlan_tag_names=False + ) + + # Use retry utility to handle search index eventual consistency + response = await async_search_with_retry(client, request, expected_count=1) + + # Ensure classification names are not present + assert response + assert response.current_page() and len(response.current_page()) == 1 + assert response.current_page()[0].guid == term1.guid + assert not response.current_page()[0].classification_names + + request = IndexSearchRequest( + dsl=DSL(query=query), exclude_atlan_tags=True, include_atlan_tag_names=True + ) + + # Use retry utility for the second search as well + response = await async_search_with_retry(client, request, expected_count=1) + + # Ensure classification names are present + assert response + assert response.current_page() and len(response.current_page()) == 1 + assert response.current_page()[0].guid == term1.guid + classification_names = response.current_page()[0].classification_names + assert classification_names and len(classification_names) == 1 + + +async def test_update_classification( + client: AsyncAtlanClient, term1: AtlasGlossaryTerm +): + assert term1.qualified_name is not None + await client.asset.update_atlan_tags( + asset_type=AtlasGlossaryTerm, + qualified_name=term1.qualified_name, + atlan_tag_names=[CLASSIFICATION_NAME], + propagate=False, + remove_propagation_on_delete=False, + restrict_lineage_propagation=True, + ) + glossary_term = await client.asset.get_by_guid( + guid=term1.guid, asset_type=AtlasGlossaryTerm, ignore_relationships=False + ) + assert glossary_term.atlan_tags + assert len(glossary_term.atlan_tags) == 1 + classification = glossary_term.atlan_tags[0] + assert str(classification.type_name) == CLASSIFICATION_NAME + assert classification.propagate is False + assert classification.remove_propagations_on_entity_delete is False + assert classification.restrict_propagation_through_lineage is True + + +async def test_remove_classification( + client: AsyncAtlanClient, term1: AtlasGlossaryTerm +): + assert term1.qualified_name is not None + await client.asset.remove_atlan_tags( + asset_type=AtlasGlossaryTerm, + qualified_name=term1.qualified_name, + atlan_tag_names=[CLASSIFICATION_NAME], + ) + + +async def test_multiple_add_classification( + client: AsyncAtlanClient, term1: AtlasGlossaryTerm +): + assert term1.qualified_name is not None + await client.asset.add_atlan_tags( + asset_type=AtlasGlossaryTerm, + qualified_name=term1.qualified_name, + atlan_tag_names=[CLASSIFICATION_NAME, CLASSIFICATION_NAME2], + propagate=True, + remove_propagation_on_delete=True, + restrict_lineage_propagation=False, + ) + + +async def test_multiple_update_classification( + client: AsyncAtlanClient, term1: AtlasGlossaryTerm +): + assert term1.qualified_name is not None + await client.asset.update_atlan_tags( + asset_type=AtlasGlossaryTerm, + qualified_name=term1.qualified_name, + atlan_tag_names=[CLASSIFICATION_NAME, CLASSIFICATION_NAME2], + propagate=False, + remove_propagation_on_delete=False, + restrict_lineage_propagation=True, + ) + glossary_term = await client.asset.get_by_guid( + guid=term1.guid, asset_type=AtlasGlossaryTerm, ignore_relationships=False + ) + assert glossary_term.atlan_tags + assert len(glossary_term.atlan_tags) == 2 + + for classification in glossary_term.atlan_tags: + assert classification.propagate is False + assert classification.remove_propagations_on_entity_delete is False + assert classification.restrict_propagation_through_lineage is True + + +async def test_multiple_remove_classification( + client: AsyncAtlanClient, term1: AtlasGlossaryTerm +): + assert term1.qualified_name is not None + await client.asset.remove_atlan_tags( + asset_type=AtlasGlossaryTerm, + qualified_name=term1.qualified_name, + atlan_tag_names=[CLASSIFICATION_NAME, CLASSIFICATION_NAME2], + ) + + +async def test_glossary_update_certificate( + client: AsyncAtlanClient, glossary: AtlasGlossary +): + await update_certificate_async(client, glossary, AtlasGlossary) + + +async def test_glossary_term_update_certificate( + client: AsyncAtlanClient, term1: AtlasGlossaryTerm, glossary: AtlasGlossary +): + await update_certificate_async(client, term1, AtlasGlossaryTerm, glossary.guid) + + +async def test_glossary_category_update_certificate( + client: AsyncAtlanClient, category1: AtlasGlossaryCategory, glossary: AtlasGlossary +): + await update_certificate_async( + client, category1, AtlasGlossaryCategory, glossary.guid + ) + + +async def test_glossary_remove_certificate( + client: AsyncAtlanClient, glossary: AtlasGlossary +): + await remove_certificate_async(client, glossary, AtlasGlossary) + + +async def test_glossary_term_remove_certificate( + client: AsyncAtlanClient, term1: AtlasGlossaryTerm, glossary: AtlasGlossary +): + await remove_certificate_async(client, term1, AtlasGlossaryTerm, glossary.guid) + + +async def test_glossary_category_remove_certificate( + client: AsyncAtlanClient, category1: AtlasGlossaryCategory, glossary: AtlasGlossary +): + await remove_certificate_async( + client, category1, AtlasGlossaryCategory, glossary.guid + ) + + +async def test_glossary_update_announcement( + client: AsyncAtlanClient, glossary: AtlasGlossary, announcement: Announcement +): + await update_announcement_async(client, glossary, AtlasGlossary, announcement) + + +async def test_asset_remove_certificate_by_setting_none( + client: AsyncAtlanClient, glossary: AtlasGlossary +): + # Setup certificate first + await update_certificate_async(client, glossary, AtlasGlossary) + + # Remove by setting to None + updater = AtlasGlossary.updater( + qualified_name=glossary.qualified_name, name=glossary.name + ) + updater.certificate_status = None + updater.certificate_status_message = None + await client.asset.save(updater) + + # Verify removal + test_asset = await client.asset.get_by_guid( + guid=glossary.guid, asset_type=AtlasGlossary, ignore_relationships=False + ) + assert not test_asset.certificate_status + assert not test_asset.certificate_status_message + + +async def test_glossary_term_update_announcement( + client: AsyncAtlanClient, + term1: AtlasGlossaryTerm, + glossary: AtlasGlossary, + announcement: Announcement, +): + await update_announcement_async( + client, term1, AtlasGlossaryTerm, announcement, glossary.guid + ) + + +async def test_glossary_category_update_announcement( + client: AsyncAtlanClient, + category1: AtlasGlossaryCategory, + glossary: AtlasGlossary, + announcement: Announcement, +): + await update_announcement_async( + client, category1, AtlasGlossaryCategory, announcement, glossary.guid + ) + + +async def test_glossary_remove_announcement( + client: AsyncAtlanClient, glossary: AtlasGlossary +): + await remove_announcement_async(client, glossary, AtlasGlossary) + + +async def test_glossary_term_remove_announcement( + client: AsyncAtlanClient, term1: AtlasGlossaryTerm, glossary: AtlasGlossary +): + await remove_announcement_async(client, term1, AtlasGlossaryTerm, glossary.guid) + + +async def test_glossary_category_remove_announcement( + client: AsyncAtlanClient, category1: AtlasGlossaryCategory, glossary: AtlasGlossary +): + await remove_announcement_async( + client, category1, AtlasGlossaryCategory, glossary.guid + ) + + +async def test_audit_find_by_user( + client: AsyncAtlanClient, + current_user: UserMinimalResponse, + audit_info: AuditInfo, +): + size = 10 + assert current_user.username + + results = await client.audit.search( + AuditSearchRequest.by_user(current_user.username, size=size, sort=[]) + ) + assert results.total_count > 0 + assert size == len(results.current_page()) + audit_entity = results.current_page()[0] + assert audit_entity.entity_qualified_name + assert audit_entity.entity_id + assert audit_entity.type_name + audit_info.qualified_name = audit_entity.entity_qualified_name + audit_info.guid = audit_entity.entity_id + audit_info.type_name = audit_entity.type_name + + # Fetch next page and make sure pagination works + await results.next_page() + audit_entity_next_page = results._entity_audits[0] + assert audit_entity != audit_entity_next_page + + +@pytest.mark.order(after="test_audit_find_by_user") +@patch.object(AUDIT_LOGGER, "debug") +async def test_audit_search_pagination( + mock_logger, + audit_glossary: AtlasGlossary, + generate_audit_entries, + client: AsyncAtlanClient, +): + size = 2 + + # Test audit search by GUID with default offset-based pagination + dsl = DSL( + query=Bool(filter=[Term(field="entityId", value=audit_glossary.guid)]), + sort=[], + size=size, + ) + request = AuditSearchRequest(dsl=dsl) + results = await client.audit.search(criteria=request, bulk=False) + TOTAL_AUDIT_ENTRIES = results.total_count + expected_sorts = [SortItem(field="entityId", order=SortOrder.ASCENDING)] + await _assert_audit_search_results( + results, expected_sorts, size, TOTAL_AUDIT_ENTRIES, False + ) + + # Test audit search by guid with `bulk` option using timestamp-based pagination + dsl = DSL( + query=Bool(filter=[Term(field="entityId", value=audit_glossary.guid)]), + sort=[], + size=size, + ) + request = AuditSearchRequest(dsl=dsl) + results = await client.audit.search(criteria=request, bulk=True) + expected_sorts = [ + SortItem("created", order=SortOrder.ASCENDING), + SortItem(field="entityId", order=SortOrder.ASCENDING), + ] + await _assert_audit_search_results( + results, expected_sorts, size, TOTAL_AUDIT_ENTRIES, True + ) + assert mock_logger.call_count == 1 + assert "Audit bulk search option is enabled." in mock_logger.call_args_list[0][0][0] + mock_logger.reset_mock() + + # When the number of results exceeds the predefined + # threshold and bulk is true and no pre-defined sort. + with patch.object(AsyncAuditSearchResults, "_MASS_EXTRACT_THRESHOLD", -1): + dsl = DSL( + query=Bool(filter=[Term(field="entityId", value=audit_glossary.guid)]), + sort=[], + size=size, + ) + request = AuditSearchRequest(dsl=dsl) + results = await client.audit.search(criteria=request, bulk=True) + expected_sorts = [ + SortItem("created", order=SortOrder.ASCENDING), + SortItem(field="entityId", order=SortOrder.ASCENDING), + ] + await _assert_audit_search_results( + results, expected_sorts, size, TOTAL_AUDIT_ENTRIES, True + ) + assert mock_logger.call_count < TOTAL_AUDIT_ENTRIES + assert ( + "Audit bulk search option is enabled." + in mock_logger.call_args_list[0][0][0] + ) + mock_logger.reset_mock() + + # When the number of results exceeds the predefined threshold and bulk is `False` and no pre-defined sort. + # Then SDK automatically switches to a `bulk` search option using timestamp-based pagination + with patch.object(AsyncAuditSearchResults, "_MASS_EXTRACT_THRESHOLD", -1): + dsl = DSL( + query=Bool(filter=[Term(field="entityId", value=audit_glossary.guid)]), + sort=[], + size=size, + ) + request = AuditSearchRequest(dsl=dsl) + results = await client.audit.search(criteria=request, bulk=False) + results.total_count + expected_sorts = [ + SortItem("created", order=SortOrder.ASCENDING), + SortItem(field="entityId", order=SortOrder.ASCENDING), + ] + await _assert_audit_search_results( + results, expected_sorts, size, TOTAL_AUDIT_ENTRIES, False + ) + assert mock_logger.call_count < TOTAL_AUDIT_ENTRIES + assert ( + "Result size (%s) exceeds threshold (%s)." + in mock_logger.call_args_list[0][0][0] + ) + mock_logger.reset_mock() + + +@pytest.mark.order(after="test_audit_find_by_user") +async def test_audit_find_by_qualified_name( + client: AsyncAtlanClient, audit_info: AuditInfo +): + assert audit_info.qualified_name + assert audit_info.type_name + size = 10 + + results = await client.audit.search( + AuditSearchRequest.by_qualified_name( + qualified_name=audit_info.qualified_name, + type_name=audit_info.type_name, + size=size, + ) + ) + + assert results.total_count > 0 + count = len(results.current_page()) + assert count > 0 and count <= size + + +@pytest.mark.order(after="test_audit_find_by_user") +async def test_audit_find_by_guid(client: AsyncAtlanClient, audit_info: AuditInfo): + assert audit_info.guid + size = 10 + + results = await client.audit.search( + AuditSearchRequest.by_guid( + guid=audit_info.guid, + size=size, + ) + ) + + assert results.total_count > 0 + count = len(results.current_page()) + assert count > 0 and count <= size + + +async def test_audit_search_default_sorting( + client: AsyncAtlanClient, audit_info: AuditInfo +): + # Test empty sorting + dsl = DSL( + query=Bool(filter=[Term(field="entityId", value=audit_info.guid)]), + sort=[], + size=10, + from_=0, + ) + request = AuditSearchRequest(dsl=dsl) + response = await client.audit.search(criteria=request) + assert response + sort_options = response._criteria.dsl.sort + assert len(sort_options) == 1 + assert sort_options[0].field == AUDIT_SORT_BY_GUID.field + + # Sort without GUID + dsl = DSL( + query=Bool(filter=[Term(field="entityId", value=audit_info.guid)]), + sort=[AUDIT_SORT_BY_LATEST], + size=10, + from_=0, + ) + request = AuditSearchRequest(dsl=dsl) + response = await client.audit.search(criteria=request) + assert response + sort_options = response._criteria.dsl.sort + assert len(sort_options) == 2 + assert sort_options[0].field == AUDIT_SORT_BY_LATEST.field + assert sort_options[1].field == AUDIT_SORT_BY_GUID.field + + # Sort with only GUID + dsl = DSL( + query=Bool(filter=[Term(field="entityId", value=audit_info.guid)]), + sort=[AUDIT_SORT_BY_GUID], + size=10, + from_=0, + ) + request = AuditSearchRequest(dsl=dsl) + response = await client.audit.search(criteria=request) + assert response + sort_options = response._criteria.dsl.sort + assert len(sort_options) == 1 + assert sort_options[0].field == AUDIT_SORT_BY_GUID.field + + # Sort with GUID and others + dsl = DSL( + query=Bool(filter=[Term(field="entityId", value=audit_info.guid)]), + sort=[AUDIT_SORT_BY_GUID, AUDIT_SORT_BY_LATEST], + size=10, + from_=0, + ) + request = AuditSearchRequest(dsl=dsl) + response = await client.audit.search(criteria=request) + assert response + sort_options = response._criteria.dsl.sort + assert len(sort_options) == 2 + assert sort_options[0].field == AUDIT_SORT_BY_GUID.field + assert sort_options[1].field == AUDIT_SORT_BY_LATEST.field + + +async def test_search_log_most_recent_viewers( + client: AsyncAtlanClient, + current_user: UserMinimalResponse, + sl_glossary: AtlasGlossary, +): + await _view_test_glossary_by_search(client, sl_glossary) + request = SearchLogRequest.most_recent_viewers(guid=sl_glossary.guid) + response = await client.search_log.search(request) + if not isinstance(response, SearchLogViewResults): + pytest.fail(f"Failed to retrieve most recent viewers of : {sl_glossary.name}") + viewers = response.user_views + assert not response.asset_views + if viewers: + assert len(viewers) == 1 + for viewer in viewers: + assert viewer.username + assert viewer.view_count + assert viewer.most_recent_view + + # Test exclude users + assert current_user.username + request = SearchLogRequest.most_recent_viewers( + guid=sl_glossary.guid, exclude_users=[current_user.username] + ) + response = await client.search_log.search(request) + if not isinstance(response, SearchLogViewResults): + pytest.fail(f"Failed to retrieve most recent viewers of : {sl_glossary.name}") + assert response.count == 0 + assert response.user_views is not None + assert len(response.user_views) == 0 + assert not response.asset_views + + +@pytest.mark.order(after="test_search_log_most_recent_viewers") +async def test_search_log_most_viewed_assets( + client: AsyncAtlanClient, + current_user: UserMinimalResponse, + sl_glossary: AtlasGlossary, +): + def _assert_most_viewed_assets( + details: Optional[List[AssetViews]], + ): + if details is not None: + assert len(details) > 0 + for detail in details: + assert detail.guid + assert detail.total_views + assert detail.distinct_users + + request = SearchLogRequest.most_viewed_assets(max_assets=10) + response = await client.search_log.search(request) + if not isinstance(response, SearchLogViewResults): + pytest.fail("Failed to retrieve most viewed assets") + assert not response.user_views + _assert_most_viewed_assets(response.asset_views) + + request = SearchLogRequest.most_viewed_assets(max_assets=10, by_different_user=True) + response = await client.search_log.search(request) + if not isinstance(response, SearchLogViewResults): + pytest.fail("Failed to retrieve most viewed assets (by_different_user)") + assert not response.user_views + _assert_most_viewed_assets(response.asset_views) + + # Test exclude users + prev_count = response.count + assert prev_count + assert current_user.username + request = SearchLogRequest.most_viewed_assets( + max_assets=10, exclude_users=[current_user.username] + ) + response = await client.search_log.search(request) + if not isinstance(response, SearchLogViewResults): + pytest.fail("Failed to retrieve most viewed assets") + assert response.count < prev_count + assert not response.user_views + _assert_most_viewed_assets(response.asset_views) + + +@pytest.mark.order(after="test_search_log_most_viewed_assets") +async def test_search_log_views_by_guid( + client: AsyncAtlanClient, + current_user: UserMinimalResponse, + sl_glossary: AtlasGlossary, +): + request = SearchLogRequest.views_by_guid(guid=sl_glossary.guid, size=10) + response = await client.search_log.search(request) + if not isinstance(response, AsyncSearchLogResults): + pytest.fail("Failed to retrieve asset detailed log entries") + log_entries = response.current_page() + assert len(log_entries) == 1 + assert "Atlan-PythonSDK" in log_entries[0].user_agent + assert "service-account-apikey" in log_entries[0].user_name + assert log_entries[0].entity_guids_all[0] == sl_glossary.guid + assert log_entries[0].ip_address + assert log_entries[0].host + assert log_entries[0].utm_tags + assert log_entries[0].entity_guids_allowed + assert log_entries[0].entity_qf_names_all + assert log_entries[0].entity_qf_names_allowed + assert log_entries[0].entity_type_names_all + assert log_entries[0].entity_type_names_allowed + assert log_entries[0].has_result + assert log_entries[0].results_count + assert log_entries[0].response_time + assert log_entries[0].created_at + assert log_entries[0].timestamp + assert log_entries[0].failed is False + assert log_entries[0].request_dsl + assert log_entries[0].request_dsl_text + assert not log_entries[0].request_attributes + assert not log_entries[0].request_relation_attributes + + # Test exclude users + assert current_user.username + request = SearchLogRequest.views_by_guid( + guid=sl_glossary.guid, size=10, exclude_users=[current_user.username] + ) + response = await client.search_log.search(request) + if not isinstance(response, AsyncSearchLogResults): + pytest.fail("Failed to retrieve asset detailed log entries") + assert response.count == 0 + assert len(response.current_page()) == 0 + + +@patch.object(SEARCH_LOG_LOGGER, "debug") +async def test_search_log_pagination( + mock_logger, + generate_search_logs, + sl_glossary: AtlasGlossary, + client: AsyncAtlanClient, +): + size = 2 + # Test search logs by GUID with default offset-based pagination + search_log_request = SearchLogRequest.views_by_guid( + guid=sl_glossary.guid, + size=size, + exclude_users=[], + ) + + results = await client.search_log.search(criteria=search_log_request, bulk=False) + TOTAL_LOG_ENTRIES = results.count + + expected_sorts = [ + SortItem(field="timestamp", order=SortOrder.ASCENDING), + SortItem(field="entityGuidsAll", order=SortOrder.ASCENDING), + ] + await _assert_search_log_results(results, expected_sorts, size, TOTAL_LOG_ENTRIES) + + # Test search logs by GUID with `bulk` option using timestamp-based pagination + search_log_request = SearchLogRequest.views_by_guid( + guid=sl_glossary.guid, + size=size, + exclude_users=[], + ) + results = await client.search_log.search(criteria=search_log_request, bulk=True) + expected_sorts = [ + SortItem(field="createdAt", order=SortOrder.ASCENDING), + SortItem(field="entityGuidsAll", order=SortOrder.ASCENDING), + ] + await _assert_search_log_results( + results, expected_sorts, size, TOTAL_LOG_ENTRIES, True + ) + assert mock_logger.call_count == 1 + assert ( + "Search log bulk search option is enabled." + in mock_logger.call_args_list[0][0][0] + ) + mock_logger.reset_mock() + + # When the number of results exceeds the predefined threshold and bulk=True + with patch.object(AsyncSearchLogResults, "_MASS_EXTRACT_THRESHOLD", -1): + search_log_request = SearchLogRequest.views_by_guid( + guid=sl_glossary.guid, + size=size, + exclude_users=[], + ) + results = await client.search_log.search(criteria=search_log_request, bulk=True) + expected_sorts = [ + SortItem(field="createdAt", order=SortOrder.ASCENDING), + SortItem(field="entityGuidsAll", order=SortOrder.ASCENDING), + ] + await _assert_search_log_results( + results, expected_sorts, size, TOTAL_LOG_ENTRIES, True + ) + assert mock_logger.call_count < TOTAL_LOG_ENTRIES + assert ( + "Search log bulk search option is enabled." + in mock_logger.call_args_list[0][0][0] + ) + mock_logger.reset_mock() + + # When results exceed threshold and bulk=False, SDK auto-switches to bulk search + with patch.object(AsyncSearchLogResults, "_MASS_EXTRACT_THRESHOLD", -1): + search_log_request = SearchLogRequest.views_by_guid( + guid=sl_glossary.guid, + size=size, + exclude_users=[], + ) + results = await client.search_log.search( + criteria=search_log_request, bulk=False + ) + expected_sorts = [ + SortItem(field="createdAt", order=SortOrder.ASCENDING), + SortItem(field="entityGuidsAll", order=SortOrder.ASCENDING), + ] + await _assert_search_log_results( + results, expected_sorts, size, TOTAL_LOG_ENTRIES + ) + assert mock_logger.call_count < TOTAL_LOG_ENTRIES + assert ( + "Result size (%s) exceeds threshold (%s)." + in mock_logger.call_args_list[0][0][0] + ) + mock_logger.reset_mock() + + +async def test_search_log_default_sorting( + client: AsyncAtlanClient, sl_glossary: AtlasGlossary +): + # Empty sorting + request = SearchLogRequest.views_by_guid(guid=sl_glossary.guid, size=10, sort=[]) + response = await client.search_log.search(request) + if not isinstance(response, AsyncSearchLogResults): + pytest.fail("Failed to retrieve asset detailed log entries") + assert response + sort_options = response._criteria.dsl.sort + assert len(sort_options) == 2 + assert sort_options[0].field == SL_SORT_BY_TIMESTAMP.field + assert sort_options[1].field == SL_SORT_BY_GUID.field + + # Sort without GUID + request = SearchLogRequest.views_by_guid( + guid=sl_glossary.guid, + size=10, + sort=[SL_SORT_BY_QUALIFIED_NAME], + ) + response = await client.search_log.search(request) + if not isinstance(response, AsyncSearchLogResults): + pytest.fail("Failed to retrieve asset detailed log entries") + assert response + sort_options = response._criteria.dsl.sort + assert len(sort_options) == 3 + assert sort_options[0].field == SL_SORT_BY_QUALIFIED_NAME.field + assert sort_options[1].field == SL_SORT_BY_TIMESTAMP.field + assert sort_options[2].field == SL_SORT_BY_GUID.field + + # Sort with only GUID + request = SearchLogRequest.views_by_guid( + guid=sl_glossary.guid, + size=10, + sort=[SL_SORT_BY_GUID], + ) + response = await client.search_log.search(request) + if not isinstance(response, AsyncSearchLogResults): + pytest.fail("Failed to retrieve asset detailed log entries") + assert response + sort_options = response._criteria.dsl.sort + assert len(sort_options) == 2 + assert sort_options[0].field == SL_SORT_BY_GUID.field + assert sort_options[1].field == SL_SORT_BY_TIMESTAMP.field + + # Sort with GUID and others + request = SearchLogRequest.views_by_guid( + guid=sl_glossary.guid, + size=10, + sort=[SL_SORT_BY_GUID, SL_SORT_BY_QUALIFIED_NAME], + ) + response = await client.search_log.search(request) + if not isinstance(response, AsyncSearchLogResults): + pytest.fail("Failed to retrieve asset detailed log entries") + assert response + sort_options = response._criteria.dsl.sort + assert len(sort_options) == 3 + assert sort_options[0].field == SL_SORT_BY_GUID.field + assert sort_options[1].field == SL_SORT_BY_QUALIFIED_NAME.field + assert sort_options[2].field == SL_SORT_BY_TIMESTAMP.field + + +async def test_client_401_token_refresh( + client: AsyncAtlanClient, + expired_token: ApiToken, + argo_fake_token: ApiToken, + monkeypatch, +): + # Use a smaller retry count to speed up test execution + DEFAULT_RETRY.total = 1 + + # Retrieve required client information before updating the client with invalid API tokens + assert argo_fake_token and argo_fake_token.guid + argo_client_secret = await client.impersonate.get_client_secret( + client_guid=argo_fake_token.guid + ) + + # Retrieve the user ID associated with the expired token's username + # Since user credentials for API tokens cannot be retrieved directly, use the existing username + expired_token_user_id = await client.impersonate.get_user_id( + username=expired_token.username + ) + + # Initialize the client with an expired/invalid token (results in 401 Unauthorized errors) + assert ( + expired_token + and expired_token.attributes + and expired_token.attributes.access_token + ) + client = AsyncAtlanClient( + api_key=expired_token.attributes.access_token, retry=DEFAULT_RETRY + ) + expired_api_token = expired_token.attributes.access_token + + # Case 1: No user_id (default) + # Verify that the client raises an authentication error when no user ID is provided + assert client._user_client is None + with pytest.raises( + AuthenticationError, + match="Server responded with an authentication error 401", + ): + await ( + FluentSearch() + .where(CompoundQuery.active_assets()) + .where(CompoundQuery.asset_type(AtlasGlossary)) + .page_size(100) + .execute_async(client=client) + ) + + # Case 2: Invalid user_id + # Test that providing an invalid user ID results in the same authentication error + client._user_id = "invalid-user-id" + with pytest.raises( + InvalidRequestError, + match="Missing privileged credentials to impersonate users", + ): + await ( + FluentSearch() + .where(CompoundQuery.active_assets()) + .where(CompoundQuery.asset_type(AtlasGlossary)) + .page_size(100) + .execute_async(client=client) + ) + + # Case 3: Valid user_id associated with the expired token + # This should trigger a retry, refresh the token + # and use the new bearer token for subsequent requests + # Set up a fake Argo client ID and client secret for impersonation + monkeypatch.setenv("CLIENT_ID", argo_fake_token.client_id) + monkeypatch.setenv("CLIENT_SECRET", argo_client_secret) + + # Configure the client with the user ID + # of the expired token to ensure token refresh is possible + client._user_id = expired_token_user_id + + # Verify that the API key is updated after the retry and the request succeeds + results = await ( + FluentSearch() + .where(CompoundQuery.active_assets()) + .where(CompoundQuery.asset_type(AtlasGlossary)) + .page_size(100) + .execute_async(client=client) + ) + + # Confirm the API key has been updated and results are returned + assert client.api_key != expired_api_token + assert results and results.count >= 1 + + # Verify similar results with get_client_async() + # Setting ATLAN_API_KEY to empty string to force impersonation + monkeypatch.setenv("ATLAN_API_KEY", "") + assert expired_token_user_id + client = await get_client_async(impersonate_user_id=expired_token_user_id) + results = await ( + FluentSearch() + .where(CompoundQuery.active_assets()) + .where(CompoundQuery.asset_type(AtlasGlossary)) + .page_size(100) + .execute_async(client=client) + ) + + # Confirm the API key has been updated and results are returned + assert client.api_key != expired_api_token + assert results and results.count >= 1 + + # Verify package headers are set correctly + expected_common_headers = Headers( + { + "User-Agent": f"Atlan-PythonSDK/{VERSION}", + "Accept-Encoding": "gzip, deflate", + "Accept": "*/*", + "Connection": "keep-alive", + "x-atlan-agent": "sdk", + "x-atlan-agent-id": "python", + "x-atlan-client-origin": "product_sdk", + "x-atlan-python-version": get_python_version(), + "x-atlan-client-type": "async", + } + ) + + # Clear package environment variables to test default headers + for var in [ + "X_ATLAN_AGENT", + "X_ATLAN_AGENT_ID", + "X_ATLAN_AGENT_PACKAGE_NAME", + "X_ATLAN_AGENT_WORKFLOW_ID", + ]: + monkeypatch.delenv(var, raising=False) + + client = await get_client_async( + impersonate_user_id=expired_token_user_id, set_pkg_headers=False + ) + assert client._async_session is not None + assert expected_common_headers == client._async_session.headers + + # Set package environment variables to test package headers + monkeypatch.setenv("X_ATLAN_AGENT", "agent_value") + monkeypatch.setenv("X_ATLAN_AGENT_ID", "agent_id_value") + monkeypatch.setenv("X_ATLAN_AGENT_PACKAGE_NAME", "package_name_value") + monkeypatch.setenv("X_ATLAN_AGENT_WORKFLOW_ID", "workflow_id_value") + + expected = Headers( + { + "User-Agent": f"Atlan-PythonSDK/{VERSION}", + "Accept-Encoding": "gzip, deflate", + "Accept": "*/*", + "Connection": "keep-alive", + "x-atlan-client-origin": "product_sdk", + "x-atlan-python-version": get_python_version(), + "x-atlan-client-type": "async", + "x-atlan-agent": "agent_value", + "x-atlan-agent-id": "agent_id_value", + "x-atlan-agent-package-name": "package_name_value", + "x-atlan-agent-workflow-id": "workflow_id_value", + } + ) + client = await get_client_async( + impersonate_user_id=expired_token_user_id, set_pkg_headers=True + ) + assert client._async_session is not None + assert expected == client._async_session.headers + + +async def test_client_init_from_token_guid( + client: AsyncAtlanClient, token: ApiToken, argo_fake_token: ApiToken, monkeypatch +): + # In real-world scenarios, these values come from environment variables + # configured at the Argo template level. The SDK uses these values to + # create a temporary client, which allows us to find the `client_id` and `client_secret` + # for the provided API token GUID, later used to initialize a client with its actual access token (API key) <- AsyncAtlanClient.from_token_guid() + assert argo_fake_token and argo_fake_token.guid + argo_client_secret = await client.impersonate.get_client_secret( + client_guid=argo_fake_token.guid + ) + monkeypatch.setenv("CLIENT_ID", argo_fake_token.client_id) + monkeypatch.setenv("CLIENT_SECRET", argo_client_secret) + + # Ensure it's a valid API token + assert token and token.username and token.guid + assert "service-account" in token.username + token_client_from_env_vars = await AsyncAtlanClient.from_token_guid(guid=token.guid) + token_client_custom = await AsyncAtlanClient.from_token_guid( + guid=token.guid, + client_id=argo_fake_token.client_id, + client_secret=argo_client_secret, + ) + + # Should be able to perform all operations + # with this client as long as it has the necessary permissions + results = await ( + FluentSearch() + .where(CompoundQuery.active_assets()) + .where(CompoundQuery.asset_type(AtlasGlossary)) + .page_size(100) + .execute_async(client=token_client_from_env_vars) + ) + assert results and results.count >= 1 + + results = await ( + FluentSearch() + .where(CompoundQuery.active_assets()) + .where(CompoundQuery.asset_type(AtlasGlossary)) + .page_size(100) + .execute_async(client=token_client_custom) + ) + assert results and results.count >= 1 + + +async def test_process_assets_when_no_assets_found(client: AsyncAtlanClient): + async def should_never_be_called(_: Asset): + pytest.fail("Should not be called") + + search = ( + FluentSearch() + .where(Term.with_state("ACTIVE")) + .where(Asset.NAME.startswith("zXZ")) + ) + + processed_count = await client.asset.process_assets( + search=search, func=should_never_be_called + ) + assert processed_count == 0 + + +async def test_process_assets_when_assets_found(client: AsyncAtlanClient): + async def doit(asset: Asset): + global call_count + call_count += 1 + + search = ( + FluentSearch() + .where(Term.with_state("ACTIVE")) + .where(Asset.TYPE_NAME.eq("Table")) + .where(Asset.NAME.startswith("B")) + ) + expected_count = (await client.asset.search(search.to_request())).count + + processed_count = await client.asset.process_assets(search=search, func=doit) + assert processed_count == expected_count diff --git a/tests_v9/integration/aio/test_connection.py b/tests_v9/integration/aio/test_connection.py new file mode 100644 index 000000000..c36271b5e --- /dev/null +++ b/tests_v9/integration/aio/test_connection.py @@ -0,0 +1,71 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +"""Async connection integration tests.""" + +from typing import AsyncGenerator + +import pytest_asyncio + +from pyatlan_v9.client.aio.atlan import AsyncAtlanClient +from pyatlan_v9.model.assets import Connection +from pyatlan_v9.model.enums import AtlanConnectionCategory, AtlanConnectorType +from tests_v9.integration.aio.utils import delete_asset_async +from tests_v9.integration.client import TestId + +MODULE_NAME = TestId.make_unique("AsyncCONN") + + +async def create_connection_async( + client: AsyncAtlanClient, name: str, connector_type: AtlanConnectorType +) -> Connection: + admin_role_guid = str(await client.role_cache.get_id_for_name("$admin")) + to_create = await Connection.creator_async( + client=client, + name=name, + connector_type=connector_type, + admin_roles=[admin_role_guid], + ) + response = await client.asset.save(to_create) + result = response.assets_created(asset_type=Connection)[0] + return await client.asset.get_by_guid( + result.guid, asset_type=Connection, ignore_relationships=False + ) + + +@pytest_asyncio.fixture(scope="module") +async def custom_connection( + client: AsyncAtlanClient, +) -> AsyncGenerator[Connection, None]: + CUSTOM_CONNECTOR_TYPE = AtlanConnectorType.CREATE_CUSTOM( + name=f"{MODULE_NAME}_NAME", + value=f"{MODULE_NAME}_type", + category=AtlanConnectionCategory.API, + ) + result = await create_connection_async( + client=client, name=MODULE_NAME, connector_type=CUSTOM_CONNECTOR_TYPE + ) + yield result + # TODO: proper connection delete workflow + await delete_asset_async(client, guid=result.guid, asset_type=Connection) + + +async def test_custom_connection(custom_connection: Connection): + assert custom_connection.name == MODULE_NAME + assert custom_connection.connector_name == f"{MODULE_NAME.lower()}_type" + assert custom_connection.qualified_name + assert custom_connection.category == AtlanConnectionCategory.API + + +async def test_custom_connection_qualified_name( + client: AsyncAtlanClient, custom_connection: Connection +): + assert custom_connection.qualified_name is not None + found = await client.asset.get_by_qualified_name( + qualified_name=custom_connection.qualified_name, + asset_type=Connection, + ignore_relationships=False, + ) + assert found + assert found.name == MODULE_NAME + assert found.connector_name == f"{MODULE_NAME.lower()}_type" diff --git a/tests_v9/integration/aio/test_custom_metadata.py b/tests_v9/integration/aio/test_custom_metadata.py new file mode 100644 index 000000000..9c2dfc9ec --- /dev/null +++ b/tests_v9/integration/aio/test_custom_metadata.py @@ -0,0 +1,1331 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. +import json +from typing import AsyncGenerator, List, Optional, Tuple + +import pytest +import pytest_asyncio + +from pyatlan.model.aio.custom_metadata import ( + AsyncCustomMetadataDict, + AsyncCustomMetadataField, +) +from pyatlan_v9.client.aio.atlan import AsyncAtlanClient +from pyatlan_v9.errors import AtlanError +from pyatlan_v9.model.assets import ( + Asset, + AtlasGlossary, + AtlasGlossaryTerm, + Badge, + Connection, +) +from pyatlan_v9.model.enums import ( + AtlanCustomAttributePrimitiveType, + AtlanIcon, + AtlanTagColor, + AtlanTypeCategory, + BadgeComparisonOperator, + BadgeConditionColor, + Cardinality, + EntityStatus, +) +from pyatlan_v9.model.fluent_search import CompoundQuery, FluentSearch +from pyatlan_v9.model.group import AtlanGroup, CreateGroupResponse +from pyatlan_v9.model.structs import BadgeCondition +from pyatlan_v9.model.typedef import AttributeDef, CustomMetadataDef, EnumDef +from tests_v9.integration.aio.utils import delete_asset_async +from tests_v9.integration.client import TestId + +MODULE_NAME = TestId.make_unique("AsyncCM") + +FIXED_USER = "ernest" +GROUP_NAME1 = f"{MODULE_NAME}1" +GROUP_NAME2 = f"{MODULE_NAME}2" + +CM_RACI = f"{MODULE_NAME}_RACI" +CM_ATTR_RACI_RESPONSIBLE = "Responsible" +CM_ATTR_RACI_ACCOUNTABLE = "Accountable" +CM_ATTR_RACI_CONSULTED = "Consulted" +CM_ATTR_RACI_INFORMED = "Informed" +CM_ATTR_RACI_EXTRA = "Extra" + + +CM_IPR = f"{MODULE_NAME}_IPR" +CM_ATTR_IPR_LICENSE = "License" +CM_ATTR_IPR_VERSION = "Version" +CM_ATTR_IPR_MANDATORY = "Mandatory" +CM_ATTR_IPR_DATE = "Date" +CM_ATTR_IPR_URL = "URL" + + +CM_QUALITY = f"{MODULE_NAME}_DQ" +CM_ATTR_QUALITY_COUNT = "Count" +CM_ATTR_QUALITY_SQL = "SQL" +CM_ATTR_QUALITY_TYPE = "Type" + +CM_RICH_TEXT = f"{MODULE_NAME}_RICH_TEXT" +CM_ATTR_RICH_TEXT_CONTENT = "Rich Content" +CM_ATTR_RICH_TEXT_DESCRIPTION = "Rich Description" + +DQ_ENUM = f"{MODULE_NAME}_DataQualityType" +DQ_TYPE_LIST = [ + "Accuracy", + "Completeness", + "Consistency", + "Timeliness", + "Validity", + "Uniqueness", +] +DQ_TYPE_EXTRA_LIST = ["Unknown", "Others"] +CM_DESCRIPTION = "Automated testing of the Python SDK (cm)." +ATTRIBUTE_DESCRIPTION = "Automated testing of the Python SDK (attribute)." + +_removal_epoch: Optional[int] + + +@pytest_asyncio.fixture(scope="module") +async def limit_attribute_applicability_kwargs( + glossary: AtlasGlossary, connection: Connection +): + return dict( + applicable_asset_types={"Link"}, + applicable_other_asset_types={"File"}, + applicable_glossaries={glossary.qualified_name}, + applicable_glossary_types={"AtlasGlossary", "AtlasGlossaryTerm"}, + applicable_connections={connection.qualified_name}, + ) + + +@pytest_asyncio.fixture(scope="module") +async def cm_ipr( + client: AsyncAtlanClient, limit_attribute_applicability_kwargs +) -> AsyncGenerator[CustomMetadataDef, None]: + attribute_defs = [ + await AttributeDef.create_async( + client=client, + display_name=CM_ATTR_IPR_LICENSE, + attribute_type=AtlanCustomAttributePrimitiveType.STRING, + description=ATTRIBUTE_DESCRIPTION, + **limit_attribute_applicability_kwargs, + ), + await AttributeDef.create_async( + client=client, + display_name=CM_ATTR_IPR_VERSION, + attribute_type=AtlanCustomAttributePrimitiveType.DECIMAL, + ), + await AttributeDef.create_async( + client=client, + display_name=CM_ATTR_IPR_MANDATORY, + attribute_type=AtlanCustomAttributePrimitiveType.BOOLEAN, + ), + await AttributeDef.create_async( + client=client, + display_name=CM_ATTR_IPR_DATE, + attribute_type=AtlanCustomAttributePrimitiveType.DATE, + ), + await AttributeDef.create_async( + client=client, + display_name=CM_ATTR_IPR_URL, + attribute_type=AtlanCustomAttributePrimitiveType.URL, + ), + ] + cm = await create_custom_metadata_async( + client, name=CM_IPR, attribute_defs=attribute_defs, logo="⚖️", locked=True + ) + yield cm + await wait_for_successful_custometadatadef_purge_async(CM_IPR, client=client) + + +@pytest_asyncio.fixture(scope="module") +async def glossary( + client: AsyncAtlanClient, +) -> AsyncGenerator[AtlasGlossary, None]: + glossary_name = MODULE_NAME + g = await create_glossary_async(client, name=glossary_name) + yield g + await delete_asset_async(client, guid=g.guid, asset_type=AtlasGlossary) + + +@pytest_asyncio.fixture(scope="module") +async def term( + client: AsyncAtlanClient, + glossary: AtlasGlossary, + cm_raci: CustomMetadataDef, + cm_ipr: CustomMetadataDef, + cm_dq: CustomMetadataDef, +) -> AsyncGenerator[AtlasGlossaryTerm, None]: + term_name = MODULE_NAME + assert glossary.qualified_name is not None + t = await create_term_async( + client, name=term_name, glossary_qualified_name=glossary.qualified_name + ) + yield t + await delete_asset_async(client, guid=t.guid, asset_type=AtlasGlossaryTerm) + + +@pytest_asyncio.fixture(scope="module") +async def groups( + client: AsyncAtlanClient, + glossary: AtlasGlossary, + term: AtlasGlossaryTerm, + cm_raci: CustomMetadataDef, + cm_ipr: CustomMetadataDef, + cm_dq: CustomMetadataDef, +) -> AsyncGenerator[List[CreateGroupResponse], None]: + g1 = await create_group_async(client, GROUP_NAME1) + g2 = await create_group_async(client, GROUP_NAME2) + yield [g1, g2] + await delete_group_async(client, g1.group) + await delete_group_async(client, g2.group) + + +@pytest_asyncio.fixture(scope="module") +async def cm_enum( + client: AsyncAtlanClient, +) -> AsyncGenerator[EnumDef, None]: + enum_def = await create_enum_async(client, name=DQ_ENUM, values=DQ_TYPE_LIST) + yield enum_def + await wait_for_successful_enumadef_purge_async(DQ_ENUM, client=client) + + +@pytest_asyncio.fixture(scope="module") +async def cm_raci( + client: AsyncAtlanClient, +) -> AsyncGenerator[CustomMetadataDef, None]: + TEST_MULTI_VALUE_USING_SETTER = await AttributeDef.create_async( + client=client, + display_name=CM_ATTR_RACI_INFORMED, + attribute_type=AtlanCustomAttributePrimitiveType.GROUPS, + ) + assert TEST_MULTI_VALUE_USING_SETTER and TEST_MULTI_VALUE_USING_SETTER.options + TEST_MULTI_VALUE_USING_SETTER.options.multi_value_select = True + attribute_defs = [ + await AttributeDef.create_async( + client=client, + display_name=CM_ATTR_RACI_RESPONSIBLE, + attribute_type=AtlanCustomAttributePrimitiveType.USERS, + multi_valued=True, + ), + await AttributeDef.create_async( + client=client, + display_name=CM_ATTR_RACI_ACCOUNTABLE, + attribute_type=AtlanCustomAttributePrimitiveType.USERS, + ), + await AttributeDef.create_async( + client=client, + display_name=CM_ATTR_RACI_CONSULTED, + attribute_type=AtlanCustomAttributePrimitiveType.GROUPS, + multi_valued=True, + ), + TEST_MULTI_VALUE_USING_SETTER, + await AttributeDef.create_async( + client=client, + display_name=CM_ATTR_RACI_EXTRA, + attribute_type=AtlanCustomAttributePrimitiveType.STRING, + ), + ] + cm = await create_custom_metadata_async( + client, + name=CM_RACI, + attribute_defs=attribute_defs, + icon=AtlanIcon.USERS_THREE, + color=AtlanTagColor.GRAY, + locked=False, + ) + yield cm + await wait_for_successful_custometadatadef_purge_async(CM_RACI, client=client) + + +@pytest_asyncio.fixture(scope="module") +async def cm_enum_update( + client: AsyncAtlanClient, +) -> AsyncGenerator[EnumDef, None]: + enum_def = await EnumDef.update_async( + client, name=DQ_ENUM, values=DQ_TYPE_EXTRA_LIST, replace_existing=False + ) + r = await client.typedef.updater(enum_def) + yield r.enum_defs[0] + + +@pytest_asyncio.fixture(scope="module") +async def cm_enum_update_with_replace( + client: AsyncAtlanClient, +) -> AsyncGenerator[EnumDef, None]: + enum_def = await EnumDef.update_async( + client, name=DQ_ENUM, values=DQ_TYPE_LIST, replace_existing=True + ) + r = await client.typedef.updater(enum_def) + yield r.enum_defs[0] + + +async def create_custom_metadata_async( + client: AsyncAtlanClient, + name: str, + attribute_defs: List[AttributeDef], + locked: bool, + logo: Optional[str] = None, + icon: Optional[AtlanIcon] = None, + color: Optional[AtlanTagColor] = None, +) -> CustomMetadataDef: + cm_def = CustomMetadataDef.creator(display_name=name, description=CM_DESCRIPTION) + cm_def.attribute_defs = attribute_defs + if icon and color: + cm_def.options = CustomMetadataDef.Options.with_logo_from_icon( + icon, color, locked + ) + elif logo and logo.startswith("http"): + cm_def.options = CustomMetadataDef.Options.with_logo_from_url(logo, locked) + elif logo: + cm_def.options = CustomMetadataDef.Options.with_logo_as_emoji(logo, locked) + else: + raise ValueError( + "Invalid configuration for the visual to use for the custom metadata." + ) + r = await client.typedef.creator(cm_def) + return r.custom_metadata_defs[0] + + +async def create_enum_async( + client: AsyncAtlanClient, name: str, values: List[str] +) -> EnumDef: + enum_def = EnumDef.creator(name=name, values=values) + r = await client.typedef.creator(enum_def) + return r.enum_defs[0] + + +async def create_glossary_async(client: AsyncAtlanClient, name: str) -> AtlasGlossary: + """Create glossary asynchronously.""" + to_create = AtlasGlossary.creator(name=name) + result = await client.asset.save(to_create) + return result.assets_created(asset_type=AtlasGlossary)[0] + + +async def create_term_async( + client: AsyncAtlanClient, name: str, glossary_qualified_name: str +) -> AtlasGlossaryTerm: + """Create glossary term asynchronously.""" + to_create = AtlasGlossaryTerm.creator( + name=name, glossary_qualified_name=glossary_qualified_name + ) + result = await client.asset.save(to_create) + return result.assets_created(asset_type=AtlasGlossaryTerm)[0] + + +async def create_group_async( + client: AsyncAtlanClient, name: str +) -> CreateGroupResponse: + g = AtlanGroup.creator(alias=name) + r = await client.group.creator(g) + return r + + +async def delete_group_async(client: AsyncAtlanClient, guid: str) -> None: + await client.group.purge(guid) + + +async def wait_for_successful_custometadatadef_purge_async( + name: str, client: AsyncAtlanClient +): + """Wait for custom metadata def to be purged - async version of sync utility""" + # Simple implementation - in production you might want exponential backoff + import asyncio + + for _ in range(10): + try: + await client.typedef.purge(name, typedef_type=CustomMetadataDef) + break + except Exception: + await asyncio.sleep(1) + + +async def wait_for_successful_enumadef_purge_async(name: str, client: AsyncAtlanClient): + """Wait for enum def to be purged - async version of sync utility""" + import asyncio + + for _ in range(10): + try: + await client.typedef.purge(name, typedef_type=EnumDef) + break + except Exception: + await asyncio.sleep(1) + + +async def test_cm_ipr(cm_ipr: CustomMetadataDef, limit_attribute_applicability_kwargs): + cm_name = CM_IPR + assert cm_ipr.category == AtlanTypeCategory.CUSTOM_METADATA + assert cm_ipr.guid + assert cm_ipr.name != cm_name + assert cm_ipr.display_name == cm_name + assert cm_ipr.description == CM_DESCRIPTION + attributes = cm_ipr.attribute_defs + assert attributes + assert len(attributes) == 5 + one_with_limited = attributes[0] + assert one_with_limited + assert one_with_limited.options + assert one_with_limited.display_name == CM_ATTR_IPR_LICENSE + assert one_with_limited.name + assert one_with_limited.description == ATTRIBUTE_DESCRIPTION + assert one_with_limited.name != CM_ATTR_IPR_LICENSE + assert one_with_limited.type_name == AtlanCustomAttributePrimitiveType.STRING.value + assert not one_with_limited.options.multi_value_select + options = one_with_limited.options + for attribute in limit_attribute_applicability_kwargs.keys(): + assert getattr( + one_with_limited, attribute + ) == limit_attribute_applicability_kwargs.get(attribute) + assert getattr(options, attribute) == json.dumps( + list(limit_attribute_applicability_kwargs.get(attribute)) + ) + one = attributes[1] + assert one.display_name == CM_ATTR_IPR_VERSION + assert one.name != CM_ATTR_IPR_VERSION + assert one.type_name == AtlanCustomAttributePrimitiveType.DECIMAL.value + assert one.options + assert not one.options.multi_value_select + one = attributes[2] + assert one.display_name == CM_ATTR_IPR_MANDATORY + assert one.name != CM_ATTR_IPR_MANDATORY + assert one.type_name == AtlanCustomAttributePrimitiveType.BOOLEAN.value + assert one.options + assert not one.options.multi_value_select + one = attributes[3] + assert one.display_name == CM_ATTR_IPR_DATE + assert one.name != CM_ATTR_IPR_DATE + assert one.type_name == AtlanCustomAttributePrimitiveType.DATE.value + assert one.options + assert not one.options.multi_value_select + one = attributes[4] + assert one.display_name == CM_ATTR_IPR_URL + assert one.name != CM_ATTR_IPR_URL + assert one.type_name == AtlanCustomAttributePrimitiveType.STRING.value + assert one.options + assert not one.options.multi_value_select + + +async def test_cm_raci( + cm_raci: CustomMetadataDef, +): + assert cm_raci.category == AtlanTypeCategory.CUSTOM_METADATA + assert cm_raci.name + assert cm_raci.guid + cm_name = CM_RACI + assert cm_raci.name != cm_name + assert cm_raci.display_name == cm_name + attributes = cm_raci.attribute_defs + assert attributes + assert len(attributes) == 5 + one = attributes[0] + assert one + assert one.display_name == CM_ATTR_RACI_RESPONSIBLE + assert one.name + assert one.name != CM_ATTR_RACI_RESPONSIBLE + assert one.type_name == f"array<{AtlanCustomAttributePrimitiveType.STRING.value}>" + assert one.options + assert one.cardinality == Cardinality.SET + assert one.options.multi_value_select + one = attributes[1] + assert one.display_name == CM_ATTR_RACI_ACCOUNTABLE + assert one.name != CM_ATTR_RACI_ACCOUNTABLE + assert one.type_name == AtlanCustomAttributePrimitiveType.STRING.value + assert one.options + assert not one.options.multi_value_select + one = attributes[2] + assert one.display_name == CM_ATTR_RACI_CONSULTED + assert one.name != CM_ATTR_RACI_CONSULTED + assert one.type_name == f"array<{AtlanCustomAttributePrimitiveType.STRING.value}>" + assert one.options + assert one.cardinality == Cardinality.SET + assert one.options.multi_value_select + one = attributes[3] + assert one.display_name == CM_ATTR_RACI_INFORMED + assert one.name != CM_ATTR_RACI_INFORMED + assert one.type_name == f"array<{AtlanCustomAttributePrimitiveType.STRING.value}>" + assert one.options + assert one.cardinality == Cardinality.SET + assert one.options.multi_value_select + one = attributes[4] + assert one.display_name == CM_ATTR_RACI_EXTRA + assert one.name != CM_ATTR_RACI_EXTRA + assert one.type_name == AtlanCustomAttributePrimitiveType.STRING.value + assert one.options + assert not one.options.multi_value_select + + +async def test_cm_enum( + cm_enum: EnumDef, +): + assert cm_enum.category == AtlanTypeCategory.ENUM + assert cm_enum.name == DQ_ENUM + assert cm_enum.guid + assert cm_enum.element_defs + assert len(cm_enum.element_defs) == len(DQ_TYPE_LIST) + + +@pytest.mark.order(after="test_cm_enum") +async def test_cm_enum_get_by_name(client: AsyncAtlanClient): + cm_enum = await client.typedef.get_by_name(name=DQ_ENUM) + + assert cm_enum and isinstance(cm_enum, EnumDef) + assert cm_enum.guid + assert cm_enum.element_defs + assert cm_enum.name == DQ_ENUM + assert cm_enum.category == AtlanTypeCategory.ENUM + assert len(cm_enum.element_defs) == len(DQ_TYPE_LIST) + + +@pytest.mark.order(after="test_cm_enum") +async def test_cm_enum_update( + cm_enum_update: EnumDef, + cm_enum_update_with_replace: EnumDef, +): + assert cm_enum_update.guid + assert cm_enum_update.name == DQ_ENUM + assert cm_enum_update.element_defs + assert cm_enum_update.category == AtlanTypeCategory.ENUM + EM_VALUES = DQ_TYPE_LIST + DQ_TYPE_EXTRA_LIST + assert len(cm_enum_update.element_defs) == len(EM_VALUES) + for index, element_def in enumerate(cm_enum_update.element_defs): + assert element_def.value == EM_VALUES[index] + + assert cm_enum_update_with_replace.guid + assert cm_enum_update_with_replace.name == DQ_ENUM + assert cm_enum_update_with_replace.element_defs + assert cm_enum_update_with_replace.category == AtlanTypeCategory.ENUM + assert len(cm_enum_update_with_replace.element_defs) == len(DQ_TYPE_LIST) + + +@pytest_asyncio.fixture(scope="module") +async def cm_dq( + client: AsyncAtlanClient, + cm_enum: EnumDef, +) -> AsyncGenerator[CustomMetadataDef, None]: + attribute_defs = [ + await AttributeDef.create_async( + client=client, + display_name=CM_ATTR_QUALITY_COUNT, + attribute_type=AtlanCustomAttributePrimitiveType.INTEGER, + ), + await AttributeDef.create_async( + client=client, + display_name=CM_ATTR_QUALITY_SQL, + attribute_type=AtlanCustomAttributePrimitiveType.SQL, + ), + await AttributeDef.create_async( + client=client, + display_name=CM_ATTR_QUALITY_TYPE, + attribute_type=AtlanCustomAttributePrimitiveType.OPTIONS, + options_name=DQ_ENUM, + ), + ] + cm = await create_custom_metadata_async( + client, + name=CM_QUALITY, + attribute_defs=attribute_defs, + logo="https://github.com/great-expectations/great_expectations/raw/develop/docs/docusaurus/static/img/" + "gx-mark-160.png", + locked=True, + ) + yield cm + await wait_for_successful_custometadatadef_purge_async(CM_QUALITY, client=client) + + +async def test_cm_dq( + cm_dq: CustomMetadataDef, +): + cm_name = CM_QUALITY + assert cm_dq.category == AtlanTypeCategory.CUSTOM_METADATA + assert cm_dq.name + assert cm_dq.guid + assert cm_dq.name != cm_name + assert cm_dq.display_name == cm_name + attributes = cm_dq.attribute_defs + assert attributes + assert len(attributes) == 3 + one = attributes[0] + assert one + assert one.display_name == CM_ATTR_QUALITY_COUNT + assert one.name + assert one.name != CM_ATTR_QUALITY_COUNT + assert one.type_name == AtlanCustomAttributePrimitiveType.INTEGER.value + assert one.options + assert not one.options.multi_value_select + one = attributes[1] + assert one.display_name == CM_ATTR_QUALITY_SQL + assert one.name != CM_ATTR_QUALITY_SQL + assert one.type_name == AtlanCustomAttributePrimitiveType.STRING.value + assert one.options + assert not one.options.multi_value_select + assert one.options.custom_type == AtlanCustomAttributePrimitiveType.SQL.value + one = attributes[2] + assert one.display_name == CM_ATTR_QUALITY_TYPE + assert one.name != CM_ATTR_QUALITY_TYPE + assert one.type_name == DQ_ENUM + assert one.options + assert not one.options.multi_value_select + assert one.options.primitive_type == AtlanCustomAttributePrimitiveType.OPTIONS.value + + +@pytest_asyncio.fixture(scope="module") +async def cm_rich_text( + client: AsyncAtlanClient, +) -> AsyncGenerator[CustomMetadataDef, None]: + attribute_defs = [ + await AttributeDef.create_async( + client=client, + display_name=CM_ATTR_RICH_TEXT_CONTENT, + attribute_type=AtlanCustomAttributePrimitiveType.RICH_TEXT, + description=ATTRIBUTE_DESCRIPTION, + ), + await AttributeDef.create_async( + client=client, + display_name=CM_ATTR_RICH_TEXT_DESCRIPTION, + attribute_type=AtlanCustomAttributePrimitiveType.RICH_TEXT, + ), + ] + cm = await create_custom_metadata_async( + client, + name=CM_RICH_TEXT, + attribute_defs=attribute_defs, + logo="📝", + locked=False, + ) + yield cm + await wait_for_successful_custometadatadef_purge_async(CM_RICH_TEXT, client=client) + + +async def test_cm_rich_text_async(cm_rich_text: CustomMetadataDef): + cm_name = CM_RICH_TEXT + assert cm_rich_text.category == AtlanTypeCategory.CUSTOM_METADATA + assert cm_rich_text.name + assert cm_rich_text.guid + assert cm_rich_text.name != cm_name + assert cm_rich_text.display_name == cm_name + attributes = cm_rich_text.attribute_defs + assert attributes + assert len(attributes) == 2 + + # Test first attribute + content_attr = attributes[0] + assert content_attr + assert content_attr.display_name == CM_ATTR_RICH_TEXT_CONTENT + assert content_attr.name + assert content_attr.name != CM_ATTR_RICH_TEXT_CONTENT + assert content_attr.type_name == AtlanCustomAttributePrimitiveType.STRING.value + assert content_attr.options + assert content_attr.options.is_rich_text is True + assert not content_attr.options.multi_value_select + assert content_attr.description == ATTRIBUTE_DESCRIPTION + + # Test second attribute + desc_attr = attributes[1] + assert desc_attr.display_name == CM_ATTR_RICH_TEXT_DESCRIPTION + assert desc_attr.name != CM_ATTR_RICH_TEXT_DESCRIPTION + assert desc_attr.type_name == AtlanCustomAttributePrimitiveType.STRING.value + assert desc_attr.options + assert desc_attr.options.is_rich_text is True + assert not desc_attr.options.multi_value_select + + +async def test_rich_text_cannot_be_multi_valued_async(client: AsyncAtlanClient): + """Test that RICH_TEXT attributes cannot be multi-valued""" + + with pytest.raises(AtlanError) as exc_info: + await AttributeDef.create_async( + client=client, + display_name="Invalid Rich Text", + attribute_type=AtlanCustomAttributePrimitiveType.RICH_TEXT, + multi_valued=True, + ) + + error = exc_info.value + assert "ATLAN-PYTHON-400-076" in str(error) + + +async def _get_groups_async( + client: AsyncAtlanClient, +) -> Tuple[AtlanGroup, AtlanGroup]: + candidates = await client.group.get_by_name(GROUP_NAME1) + assert candidates + assert candidates.records is not None + assert len(candidates.records) == 1 + group1 = candidates.records[0] + candidates = await client.group.get_by_name(GROUP_NAME2) + assert candidates + assert candidates.records is not None + assert len(candidates.records) == 1 + group2 = candidates.records[0] + return group1, group2 + + +async def test_add_term_cm_raci( + client: AsyncAtlanClient, + cm_raci: CustomMetadataDef, + term: AtlasGlossaryTerm, + groups: List[AtlanGroup], +): + cm_name = CM_RACI + raci_attrs = await AsyncCustomMetadataDict.creator(client=client, name=cm_name) + _validate_raci_empty(raci_attrs) + group1, group2 = await _get_groups_async(client) + raci_attrs[CM_ATTR_RACI_RESPONSIBLE] = [FIXED_USER] + raci_attrs[CM_ATTR_RACI_ACCOUNTABLE] = FIXED_USER + raci_attrs[CM_ATTR_RACI_CONSULTED] = [group1.name] + raci_attrs[CM_ATTR_RACI_INFORMED] = [group1.name, group2.name] + await client.asset.update_custom_metadata_attributes(term.guid, raci_attrs) + t = await client.asset.retrieve_minimal( + guid=term.guid, asset_type=AtlasGlossaryTerm + ) + assert t + await _validate_raci_attributes_async( + client, await t.get_custom_metadata_async(client=client, name=cm_name) + ) + + +async def test_add_term_cm_ipr( + client: AsyncAtlanClient, + cm_ipr: CustomMetadataDef, + term: AtlasGlossaryTerm, +): + cm_name = CM_IPR + ipr_attrs = await AsyncCustomMetadataDict.creator(client=client, name=cm_name) + _validate_ipr_empty(ipr_attrs) + ipr_attrs[CM_ATTR_IPR_LICENSE] = "CC BY" + ipr_attrs[CM_ATTR_IPR_VERSION] = 2.0 + ipr_attrs[CM_ATTR_IPR_MANDATORY] = True + ipr_attrs[CM_ATTR_IPR_DATE] = 1659308400000 + ipr_attrs[CM_ATTR_IPR_URL] = "https://creativecommons.org/licenses/by/2.0/" + + await client.asset.update_custom_metadata_attributes(term.guid, ipr_attrs) + t = await client.asset.retrieve_minimal( + guid=term.guid, asset_type=AtlasGlossaryTerm + ) + assert t + _validate_ipr_attributes( + await t.get_custom_metadata_async(client=client, name=cm_name) + ) + + +async def test_add_term_cm_dq( + client: AsyncAtlanClient, + cm_dq: CustomMetadataDef, + term: AtlasGlossaryTerm, +): + cm_name = CM_QUALITY + dq_attrs = await AsyncCustomMetadataDict.creator(client=client, name=cm_name) + _validate_dq_empty(dq_attrs) + dq_attrs[CM_ATTR_QUALITY_COUNT] = 42 + dq_attrs[CM_ATTR_QUALITY_SQL] = "SELECT * from SOMEWHERE;" + dq_attrs[CM_ATTR_QUALITY_TYPE] = "Completeness" + await client.asset.update_custom_metadata_attributes(term.guid, dq_attrs) + t = await client.asset.retrieve_minimal( + guid=term.guid, asset_type=AtlasGlossaryTerm + ) + assert t + _validate_dq_attributes( + await t.get_custom_metadata_async(client=client, name=cm_name) + ) + + +@pytest.mark.order(after="test_add_term_cm_dq") +async def test_update_term_cm_ipr( + client: AsyncAtlanClient, + cm_ipr: CustomMetadataDef, + term: AtlasGlossaryTerm, +): + cm_name = CM_IPR + ipr = await AsyncCustomMetadataDict.creator(client=client, name=cm_name) + # Note: MUST access the getter / setter, not the underlying store + ipr[CM_ATTR_IPR_MANDATORY] = False + await client.asset.update_custom_metadata_attributes(term.guid, ipr) + t = await client.asset.retrieve_minimal( + guid=term.guid, asset_type=AtlasGlossaryTerm + ) + assert t + _validate_ipr_attributes( + await t.get_custom_metadata_async(client=client, name=cm_name), mandatory=False + ) + await _validate_raci_attributes_async( + client, await t.get_custom_metadata_async(client=client, name=CM_RACI) + ) + _validate_dq_attributes( + await t.get_custom_metadata_async(client=client, name=CM_QUALITY) + ) + + +@pytest.mark.order(after="test_update_term_cm_ipr") +async def test_replace_term_cm_raci( + client: AsyncAtlanClient, + cm_raci: CustomMetadataDef, + term: AtlasGlossaryTerm, +): + raci = await AsyncCustomMetadataDict.creator(client=client, name=CM_RACI) + group1, group2 = await _get_groups_async(client) + raci[CM_ATTR_RACI_RESPONSIBLE] = [FIXED_USER] + raci[CM_ATTR_RACI_ACCOUNTABLE] = FIXED_USER + raci[CM_ATTR_RACI_CONSULTED] = None + raci[CM_ATTR_RACI_INFORMED] = [group1.name, group2.name] + await client.asset.replace_custom_metadata(term.guid, raci) + t = await client.asset.retrieve_minimal( + guid=term.guid, asset_type=AtlasGlossaryTerm + ) + assert t + await _validate_raci_attributes_replacement_async( + client, await t.get_custom_metadata_async(client=client, name=CM_RACI) + ) + _validate_ipr_attributes( + await t.get_custom_metadata_async(client=client, name=CM_IPR), mandatory=False + ) + _validate_dq_attributes( + await t.get_custom_metadata_async(client=client, name=CM_QUALITY) + ) + + +@pytest.mark.order(after="test_replace_term_cm_raci") +async def test_replace_term_cm_ipr( + client: AsyncAtlanClient, + cm_ipr: CustomMetadataDef, + term: AtlasGlossaryTerm, +): + term_cm_ipr = await AsyncCustomMetadataDict.creator(client=client, name=CM_IPR) + await client.asset.replace_custom_metadata(term.guid, term_cm_ipr) + t = await client.asset.retrieve_minimal( + guid=term.guid, asset_type=AtlasGlossaryTerm + ) + assert t + await _validate_raci_attributes_replacement_async( + client, await t.get_custom_metadata_async(client=client, name=CM_RACI) + ) + _validate_dq_attributes( + await t.get_custom_metadata_async(client=client, name=CM_QUALITY) + ) + _validate_ipr_empty(await t.get_custom_metadata_async(client=client, name=CM_IPR)) + + +@pytest.mark.order(after="test_replace_term_cm_ipr") +async def test_search_by_any_accountable( + client: AsyncAtlanClient, + cm_raci: CustomMetadataDef, + glossary: AtlasGlossary, + term: AtlasGlossaryTerm, +): + attributes = ["name", "anchor"] + cm_attributes = ( + await client.custom_metadata_cache.get_attributes_for_search_results( + set_name=CM_RACI + ) + ) + assert cm_attributes + attributes.extend(cm_attributes) + request = ( + FluentSearch(_includes_on_results=attributes) + .where(CompoundQuery.active_assets()) + .where(CompoundQuery.asset_type(AtlasGlossaryTerm)) + .where( + await AsyncCustomMetadataField( + client, CM_RACI, CM_ATTR_RACI_ACCOUNTABLE + ).has_any_value() + ) + .include_on_relations(Asset.NAME) + ).to_request() + response = await client.asset.search(criteria=request) + assert response + count = 0 + # TODO: replace with exponential back-off and jitter + while response.count == 0 and count < 10: + import asyncio + + await asyncio.sleep(2) + response = await client.asset.search(criteria=request) + count += 1 + assert response.count == 1 + async for t in response: + assert isinstance(t, AtlasGlossaryTerm) + assert t.guid == term.guid + assert t.qualified_name == term.qualified_name + anchor = t.attributes.anchor + assert anchor + assert anchor.name == glossary.name + await _validate_raci_attributes_replacement_async( + client, await t.get_custom_metadata_async(client=client, name=CM_RACI) + ) + + +@pytest.mark.order(after="test_replace_term_cm_ipr") +async def test_search_by_specific_accountable( + client: AsyncAtlanClient, + cm_raci: CustomMetadataDef, + glossary: AtlasGlossary, + term: AtlasGlossaryTerm, +): + request = ( + FluentSearch() + .where(CompoundQuery.active_assets()) + .where(CompoundQuery.asset_type(AtlasGlossaryTerm)) + .where( + await AsyncCustomMetadataField( + client, CM_RACI, CM_ATTR_RACI_ACCOUNTABLE + ).eq(FIXED_USER) + ) + .include_on_results(Asset.NAME) + .include_on_results(AtlasGlossaryTerm.ANCHOR) + .include_on_relations(Asset.NAME) + ).to_request() + response = await client.asset.search(criteria=request) + assert response + count = 0 + # TODO: replace with exponential back-off and jitter + while response.count == 0 and count < 10: + import asyncio + + await asyncio.sleep(2) + response = await client.asset.search(criteria=request) + count += 1 + assert response.count == 1 + async for t in response: + assert isinstance(t, AtlasGlossaryTerm) + assert t.guid == term.guid + assert t.qualified_name == term.qualified_name + anchor = t.attributes.anchor + assert anchor + assert anchor.name == glossary.name + + +@pytest.mark.order( + after=["test_search_by_any_accountable", "test_search_by_specific_accountable"] +) +async def test_remove_term_cm_raci( + client: AsyncAtlanClient, + cm_raci: CustomMetadataDef, + term: AtlasGlossaryTerm, +): + await client.asset.remove_custom_metadata(term.guid, cm_name=CM_RACI) + t = await client.asset.retrieve_minimal( + guid=term.guid, asset_type=AtlasGlossaryTerm + ) + assert t + _validate_dq_attributes( + await t.get_custom_metadata_async(client=client, name=CM_QUALITY) + ) + _validate_ipr_empty(await t.get_custom_metadata_async(client=client, name=CM_IPR)) + _validate_raci_empty(await t.get_custom_metadata_async(client=client, name=CM_RACI)) + + +@pytest.mark.order(after="test_remove_term_cm_raci") +async def test_remove_term_cm_ipr( + client: AsyncAtlanClient, + cm_ipr: CustomMetadataDef, + term: AtlasGlossaryTerm, +): + await client.asset.remove_custom_metadata(term.guid, cm_name=CM_IPR) + t = await client.asset.retrieve_minimal( + guid=term.guid, asset_type=AtlasGlossaryTerm + ) + assert t + _validate_dq_attributes( + await t.get_custom_metadata_async(client=client, name=CM_QUALITY) + ) + _validate_ipr_empty(await t.get_custom_metadata_async(client=client, name=CM_IPR)) + _validate_raci_empty(await t.get_custom_metadata_async(client=client, name=CM_RACI)) + + +@pytest.mark.order(after="test_remove_term_cm_raci") +async def test_remove_attribute(client: AsyncAtlanClient, cm_raci: CustomMetadataDef): + global _removal_epoch + cm_name = CM_RACI + existing = await client.custom_metadata_cache.get_custom_metadata_def(name=cm_name) + existing_attrs = existing.attribute_defs + updated_attrs = [] + for existing_attr in existing_attrs: + to_keep = existing_attr + if existing_attr.display_name == CM_ATTR_RACI_EXTRA: + to_keep = existing_attr.archive(by="test-automation") + assert to_keep.options + _removal_epoch = to_keep.options.archived_at + updated_attrs.append(to_keep) + existing.attribute_defs = updated_attrs + response = await client.typedef.updater(existing) + assert response + assert len(response.custom_metadata_defs) == 1 + updated = response.custom_metadata_defs[0] + assert updated.category == AtlanTypeCategory.CUSTOM_METADATA + assert updated.name != cm_name + assert updated.guid + assert updated.display_name == cm_name + attributes = updated.attribute_defs + archived = _validate_raci_structure(attributes, 5) + assert archived + assert ( + archived.display_name == f"{CM_ATTR_RACI_EXTRA}-archived-{str(_removal_epoch)}" + ) + assert archived.name != CM_ATTR_RACI_EXTRA + assert archived.type_name == AtlanCustomAttributePrimitiveType.STRING.value + assert not archived.options.multi_value_select + assert archived.is_archived() + + +@pytest.mark.order(after="test_remove_attribute") +async def test_retrieve_structures( + client: AsyncAtlanClient, cm_raci: CustomMetadataDef +): + global _removal_epoch + custom_attributes = await client.custom_metadata_cache.get_all_custom_attributes( + include_deleted=False + ) + assert custom_attributes + assert len(custom_attributes) >= 3 + assert CM_RACI in custom_attributes.keys() + assert CM_IPR in custom_attributes.keys() + assert CM_QUALITY in custom_attributes.keys() + extra = _validate_raci_structure(custom_attributes.get(CM_RACI), 4) + assert not extra + custom_attributes = await client.custom_metadata_cache.get_all_custom_attributes( + include_deleted=True + ) + assert custom_attributes + assert CM_RACI in custom_attributes.keys() + assert CM_IPR in custom_attributes.keys() + assert CM_QUALITY in custom_attributes.keys() + extra = _validate_raci_structure(custom_attributes.get(CM_RACI), 5) + assert extra + assert extra.display_name == f"{CM_ATTR_RACI_EXTRA}-archived-{str(_removal_epoch)}" + assert extra.name != CM_ATTR_RACI_EXTRA + assert extra.type_name == AtlanCustomAttributePrimitiveType.STRING.value + assert "Database" in extra.applicable_asset_types + assert not extra.options.multi_value_select + assert extra.is_archived() + + +@pytest.mark.order(after="test_retrieve_structures") +async def test_recreate_attribute(client: AsyncAtlanClient, cm_raci: CustomMetadataDef): + existing = await client.custom_metadata_cache.get_custom_metadata_def(name=CM_RACI) + existing_attrs = existing.attribute_defs + updated_attrs = [] + for existing_attr in existing_attrs: + existing_attr.is_new = None + updated_attrs.append(existing_attr) + new_attr = await AttributeDef.create_async( + client=client, + display_name=CM_ATTR_RACI_EXTRA, + attribute_type=AtlanCustomAttributePrimitiveType.STRING, + ) + updated_attrs.append(new_attr) + existing.attribute_defs = updated_attrs + response = await client.typedef.updater(existing) + assert response + assert len(response.custom_metadata_defs) == 1 + updated = response.custom_metadata_defs[0] + assert updated.category == AtlanTypeCategory.CUSTOM_METADATA + assert updated.name != CM_RACI + assert updated.guid + assert updated.display_name == CM_RACI + attributes = updated.attribute_defs + extra = _validate_raci_structure(attributes, 6) + assert extra + assert extra.display_name == CM_ATTR_RACI_EXTRA + assert extra.name != CM_ATTR_RACI_EXTRA + assert extra.type_name == AtlanCustomAttributePrimitiveType.STRING.value + assert not extra.options.multi_value_select + assert not extra.is_archived() + + +@pytest.mark.order(after="test_recreate_attribute") +async def test_retrieve_structure_without_archived( + client: AsyncAtlanClient, cm_raci: CustomMetadataDef +): + custom_attributes = await client.custom_metadata_cache.get_all_custom_attributes( + include_deleted=False + ) + assert custom_attributes + assert len(custom_attributes) >= 3 + assert CM_RACI in custom_attributes.keys() + assert CM_IPR in custom_attributes.keys() + assert CM_QUALITY in custom_attributes.keys() + extra = _validate_raci_structure(custom_attributes.get(CM_RACI), 5) + assert extra + assert extra.display_name == CM_ATTR_RACI_EXTRA + assert extra.name != CM_ATTR_RACI_EXTRA + assert extra.type_name == AtlanCustomAttributePrimitiveType.STRING.value + assert "Database" in extra.applicable_asset_types + assert not extra.is_archived() + + +@pytest.mark.order(after="test_recreate_attribute") +async def test_retrieve_structure_with_archived( + client: AsyncAtlanClient, cm_raci: CustomMetadataDef +): + custom_attributes = await client.custom_metadata_cache.get_all_custom_attributes( + include_deleted=True + ) + assert custom_attributes + assert len(custom_attributes) >= 3 + assert CM_RACI in custom_attributes.keys() + assert CM_IPR in custom_attributes.keys() + assert CM_QUALITY in custom_attributes.keys() + extra = _validate_raci_structure(custom_attributes.get(CM_RACI), 6) + assert extra + assert extra.display_name == CM_ATTR_RACI_EXTRA + assert extra.name != CM_ATTR_RACI_EXTRA + assert extra.type_name == AtlanCustomAttributePrimitiveType.STRING.value + assert "Database" in extra.applicable_asset_types + assert not extra.is_archived() + + +@pytest.mark.order(after="test_recreate_attribute") +async def test_update_replacing_cm( + term: AtlasGlossaryTerm, + glossary: AtlasGlossary, + cm_raci: CustomMetadataDef, + cm_ipr: CustomMetadataDef, + cm_dq: CustomMetadataDef, + client: AsyncAtlanClient, +): + raci = await AsyncCustomMetadataDict.creator(client=client, name=CM_RACI) + group1, group2 = await _get_groups_async(client) + raci[CM_ATTR_RACI_RESPONSIBLE] = [FIXED_USER] + raci[CM_ATTR_RACI_ACCOUNTABLE] = FIXED_USER + raci[CM_ATTR_RACI_CONSULTED] = [group1.name] + raci[CM_ATTR_RACI_INFORMED] = [group1.name, group2.name] + raci[CM_ATTR_RACI_EXTRA] = "something extra..." + assert term.qualified_name + assert term.name + to_update = AtlasGlossaryTerm.create_for_modification( + qualified_name=term.qualified_name, name=term.name, glossary_guid=glossary.guid + ) + await to_update.set_custom_metadata_async(custom_metadata=raci, client=client) + response = await client.asset.update_replacing_cm( + to_update, replace_atlan_tags=False + ) + assert response + assert len(response.assets_deleted(asset_type=AtlasGlossaryTerm)) == 0 + assert len(response.assets_created(asset_type=AtlasGlossaryTerm)) == 0 + assert len(response.assets_updated(asset_type=AtlasGlossaryTerm)) == 1 + t = response.assets_updated(asset_type=AtlasGlossaryTerm)[0] + assert isinstance(t, AtlasGlossaryTerm) + assert t.guid == term.guid + assert t.qualified_name == term.qualified_name + assert term.qualified_name + x = await client.asset.get_by_qualified_name( + qualified_name=term.qualified_name, + asset_type=AtlasGlossaryTerm, + ignore_relationships=False, + ) + assert x + assert not x.is_incomplete + assert x.qualified_name == term.qualified_name + raci = await x.get_custom_metadata_async(client=client, name=CM_RACI) + await _validate_raci_attributes_async(client, raci) + assert raci[CM_ATTR_RACI_EXTRA] == "something extra..." + _validate_ipr_empty(await x.get_custom_metadata_async(client=client, name=CM_IPR)) + _validate_dq_empty( + await x.get_custom_metadata_async(client=client, name=CM_QUALITY) + ) + + +# TODO: test entity audit retrieval and parsing, once available + + +async def _validate_raci_attributes_async( + client: AsyncAtlanClient, cma: AsyncCustomMetadataDict +): + assert cma + # Note: MUST access the getter / setter, not the underlying store + responsible = cma[CM_ATTR_RACI_RESPONSIBLE] + accountable = cma[CM_ATTR_RACI_ACCOUNTABLE] + consulted = cma[CM_ATTR_RACI_CONSULTED] + informed = cma[CM_ATTR_RACI_INFORMED] + group1, group2 = await _get_groups_async(client) + assert responsible + assert len(responsible) == 1 + assert FIXED_USER in responsible + assert accountable + assert accountable == FIXED_USER + assert consulted == [group1.name] + assert informed == [group1.name, group2.name] + + +async def _validate_raci_attributes_replacement_async( + client: AsyncAtlanClient, cma: AsyncCustomMetadataDict +): + assert cma + # Note: MUST access the getter / setter, not the underlying store + responsible = cma[CM_ATTR_RACI_RESPONSIBLE] + accountable = cma[CM_ATTR_RACI_ACCOUNTABLE] + consulted = cma[CM_ATTR_RACI_CONSULTED] + informed = cma[CM_ATTR_RACI_INFORMED] + group1, group2 = await _get_groups_async(client) + assert responsible + assert responsible == [FIXED_USER] + assert accountable + assert accountable == FIXED_USER + assert not consulted + assert informed == [group1.name, group2.name] + + +def _validate_raci_empty(raci_attrs: AsyncCustomMetadataDict): + attribute_names = raci_attrs.attribute_names + assert CM_ATTR_RACI_RESPONSIBLE in attribute_names + assert CM_ATTR_RACI_ACCOUNTABLE in attribute_names + assert CM_ATTR_RACI_CONSULTED in attribute_names + assert CM_ATTR_RACI_INFORMED in attribute_names + assert CM_ATTR_RACI_EXTRA in attribute_names + assert not raci_attrs[CM_ATTR_RACI_RESPONSIBLE] + assert not raci_attrs[CM_ATTR_RACI_ACCOUNTABLE] + assert not raci_attrs[CM_ATTR_RACI_CONSULTED] # could be empty list + assert not raci_attrs[CM_ATTR_RACI_INFORMED] # could be empty list + assert not raci_attrs[CM_ATTR_RACI_EXTRA] + + +def _validate_ipr_attributes(cma: AsyncCustomMetadataDict, mandatory: bool = True): + assert cma + license = cma[CM_ATTR_IPR_LICENSE] + v = cma[CM_ATTR_IPR_VERSION] + m = cma[CM_ATTR_IPR_MANDATORY] + d = cma[CM_ATTR_IPR_DATE] + u = cma[CM_ATTR_IPR_URL] + assert license + assert license == "CC BY" + assert v + assert v == 2.0 + if mandatory: + assert m + else: + assert not m + assert d + assert d == 1659308400000 + assert u + assert u == "https://creativecommons.org/licenses/by/2.0/" + + +def _validate_ipr_empty(ipr_attrs: AsyncCustomMetadataDict): + attribute_names = ipr_attrs.attribute_names + assert CM_ATTR_IPR_LICENSE in attribute_names + assert CM_ATTR_IPR_VERSION in attribute_names + assert CM_ATTR_IPR_MANDATORY in attribute_names + assert CM_ATTR_IPR_DATE in attribute_names + assert CM_ATTR_IPR_URL in attribute_names + assert not ipr_attrs[CM_ATTR_IPR_LICENSE] + assert not ipr_attrs[CM_ATTR_IPR_VERSION] + assert not ipr_attrs[CM_ATTR_IPR_MANDATORY] + assert not ipr_attrs[CM_ATTR_IPR_DATE] + assert not ipr_attrs[CM_ATTR_IPR_URL] + + +def _validate_dq_attributes(cma: AsyncCustomMetadataDict): + assert cma + c = cma[CM_ATTR_QUALITY_COUNT] + s = cma[CM_ATTR_QUALITY_SQL] + t = cma[CM_ATTR_QUALITY_TYPE] + assert c + assert c == 42 + assert s + assert s == "SELECT * from SOMEWHERE;" + assert t + assert t == "Completeness" + + +def _validate_dq_empty(dq_attrs: AsyncCustomMetadataDict): + attribute_names = dq_attrs.attribute_names + assert CM_ATTR_QUALITY_COUNT in attribute_names + assert CM_ATTR_QUALITY_SQL in attribute_names + assert CM_ATTR_QUALITY_TYPE in attribute_names + assert not dq_attrs[CM_ATTR_QUALITY_COUNT] + assert not dq_attrs[CM_ATTR_QUALITY_SQL] + assert not dq_attrs[CM_ATTR_QUALITY_TYPE] + + +def _validate_raci_structure( + attributes: Optional[List[AttributeDef]], total_expected: int +): + global _removal_epoch + assert attributes + assert len(attributes) == total_expected + one = attributes[0] + assert one.display_name == CM_ATTR_RACI_RESPONSIBLE + assert one.name != CM_ATTR_RACI_RESPONSIBLE + assert one.type_name == f"array<{AtlanCustomAttributePrimitiveType.STRING.value}>" + assert one.options + assert "Database" in one.applicable_asset_types + assert not one.is_archived() + assert one.cardinality == Cardinality.SET + assert one.options.multi_value_select + assert one.options.custom_type == AtlanCustomAttributePrimitiveType.USERS.value + one = attributes[1] + assert one.display_name == CM_ATTR_RACI_ACCOUNTABLE + assert one.name != CM_ATTR_RACI_ACCOUNTABLE + assert one.type_name == AtlanCustomAttributePrimitiveType.STRING.value + assert one.options + assert "Table" in one.applicable_asset_types + assert not one.is_archived() + assert not one.options.multi_value_select + assert one.options.custom_type == AtlanCustomAttributePrimitiveType.USERS.value + one = attributes[2] + assert one.display_name == CM_ATTR_RACI_CONSULTED + assert one.name != CM_ATTR_RACI_CONSULTED + assert one.type_name == f"array<{AtlanCustomAttributePrimitiveType.STRING.value}>" + assert one.options + assert "Column" in one.applicable_asset_types + assert not one.is_archived() + assert one.cardinality == Cardinality.SET + assert one.options.multi_value_select + assert one.options.custom_type == AtlanCustomAttributePrimitiveType.GROUPS.value + one = attributes[3] + assert one.display_name == CM_ATTR_RACI_INFORMED + assert not one.name == CM_ATTR_RACI_INFORMED + assert one.type_name == f"array<{AtlanCustomAttributePrimitiveType.STRING.value}>" + assert one.options + assert "MaterialisedView" in one.applicable_asset_types + assert not one.is_archived() + assert one.cardinality == Cardinality.SET + assert one.options.multi_value_select + assert one.options.custom_type == AtlanCustomAttributePrimitiveType.GROUPS.value + if total_expected > 5: + # If we're expecting more than 5, then the penultimate must be an archived CM_ATTR_EXTRA + one = attributes[4] + assert ( + one.display_name == f"{CM_ATTR_RACI_EXTRA}-archived-{str(_removal_epoch)}" + ) + assert one.name != CM_ATTR_RACI_EXTRA + assert one.type_name == AtlanCustomAttributePrimitiveType.STRING.value + assert one.options + assert "AtlasGlossaryTerm" in one.applicable_glossary_types + assert not one.options.multi_value_select + assert one.is_archived() + if total_expected > 4: + return attributes[total_expected - 1] + return None + + +async def test_add_badge_cm_dq( + client: AsyncAtlanClient, + cm_dq: CustomMetadataDef, +): + badge = await Badge.creator_async( + client=client, + name=CM_ATTR_QUALITY_COUNT, + cm_name=CM_QUALITY, + cm_attribute=CM_ATTR_QUALITY_COUNT, + badge_conditions=[ + BadgeCondition.creator( + badge_condition_operator=BadgeComparisonOperator.GTE, + badge_condition_value="5", + badge_condition_colorhex=BadgeConditionColor.GREEN, + ), + BadgeCondition.creator( + badge_condition_operator=BadgeComparisonOperator.LT, + badge_condition_value="5", + badge_condition_colorhex=BadgeConditionColor.YELLOW, + ), + BadgeCondition.creator( + badge_condition_operator=BadgeComparisonOperator.LTE, + badge_condition_value="2", + badge_condition_colorhex=BadgeConditionColor.RED, + ), + ], + ) + badge.user_description = "How many data quality checks ran against this asset." + assert badge.status == EntityStatus.ACTIVE + response = await client.asset.save(badge) + assert (badges := response.assets_created(asset_type=Badge)) + assert len(badges) == 1 + await client.asset.purge_by_guid(badges[0].guid) diff --git a/tests_v9/integration/aio/test_file_client.py b/tests_v9/integration/aio/test_file_client.py new file mode 100644 index 000000000..14b76b5af --- /dev/null +++ b/tests_v9/integration/aio/test_file_client.py @@ -0,0 +1,98 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +import imghdr # type: ignore[import-not-found] +import os +from pathlib import Path + +import pytest +import pytest_asyncio + +from pyatlan_v9.client.aio.atlan import AsyncAtlanClient +from pyatlan_v9.errors import InvalidRequestError +from pyatlan_v9.model.file import PresignedURLRequest +from tests_v9.integration.client import TestId + +MODULE_NAME = TestId.make_unique("AsyncFileClient") + +URL_EXPIRY = "300s" # 5 minutes instead of 10 seconds +FILE_NAME = "sdk.png" +DOWNLOAD_FILE_NAME = "sdk-download.png" +TENANT_S3_BUCKET_DIRECTORY = "presigned-url-sdk-integration-tests" +S3_UPLOAD_FILE_PATH = f"{TENANT_S3_BUCKET_DIRECTORY}/{FILE_NAME}" + + +TEST_DATA_DIR = Path(__file__).parent.parent / "data" +UPLOAD_FILE_PATH = str(TEST_DATA_DIR / "file_requests" / FILE_NAME) +DOWNLOAD_FILE_PATH = str(TEST_DATA_DIR / "file_requests" / DOWNLOAD_FILE_NAME) + + +@pytest.mark.parametrize( + "file_path, expected_error", + [ + [ + "some/invalid/file_path.png", + ( + "ATLAN-PYTHON-400-060 Unable to download file, " + "Error: No such file or directory, Path: some/invalid/file_path.png" + ), + ], + ], +) +async def test_file_client_download_file_raises_invalid_request_error( + client, file_path, expected_error +): + with pytest.raises(InvalidRequestError, match=expected_error): + await client.files.download_file( + presigned_url="test-url", + file_path=file_path, + ) + + +@pytest_asyncio.fixture(scope="module") +async def s3_put_presigned_url(client: AsyncAtlanClient) -> str: + # Presigned URL for upload + return await client.files.generate_presigned_url( + request=PresignedURLRequest( + key=S3_UPLOAD_FILE_PATH, + expiry=URL_EXPIRY, + method=PresignedURLRequest.Method.PUT, + ) + ) + + +@pytest_asyncio.fixture(scope="module") +async def s3_get_presigned_url(client: AsyncAtlanClient) -> str: + # Presigned URL for download + return await client.files.generate_presigned_url( + request=PresignedURLRequest( + key=S3_UPLOAD_FILE_PATH, + expiry=URL_EXPIRY, + method=PresignedURLRequest.Method.GET, + ) + ) + + +async def test_file_client_presigned_url_upload( + client: AsyncAtlanClient, s3_put_presigned_url: str +): + assert s3_put_presigned_url + assert os.path.exists(UPLOAD_FILE_PATH) + + await client.files.upload_file( + presigned_url=s3_put_presigned_url, file_path=UPLOAD_FILE_PATH + ) + + +async def test_file_client_presigned_url_download( + client: AsyncAtlanClient, s3_get_presigned_url: str +): + assert s3_get_presigned_url + assert not os.path.exists(DOWNLOAD_FILE_PATH) + + await client.files.download_file( + presigned_url=s3_get_presigned_url, file_path=DOWNLOAD_FILE_PATH + ) + assert os.path.exists(DOWNLOAD_FILE_PATH) + assert imghdr.what(DOWNLOAD_FILE_PATH) == "png" + os.remove(DOWNLOAD_FILE_PATH) diff --git a/tests_v9/integration/aio/test_glossary.py b/tests_v9/integration/aio/test_glossary.py new file mode 100644 index 000000000..56aff8a0b --- /dev/null +++ b/tests_v9/integration/aio/test_glossary.py @@ -0,0 +1,1178 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. +import itertools +import logging +from time import sleep +from typing import AsyncGenerator, List, Optional, Union + +import pytest +import pytest_asyncio +from tenacity import retry, retry_if_exception_type, stop_after_attempt, wait_fixed + +from pyatlan_v9.client.aio.atlan import AsyncAtlanClient +from pyatlan_v9.errors import InvalidRequestError, NotFoundError +from pyatlan_v9.model.assets import ( + AtlasGlossary, + AtlasGlossaryCategory, + AtlasGlossaryTerm, +) +from pyatlan_v9.model.assets.relations import UserDefRelationship +from pyatlan_v9.model.enums import SaveSemantic +from pyatlan_v9.model.fields.atlan_fields import AtlanField +from pyatlan_v9.model.fluent_search import CompoundQuery, FluentSearch +from pyatlan_v9.model.search import DSL, IndexSearchRequest +from tests_v9.integration.aio.utils import ( + async_assert_fluent_search_count_with_retry, + async_assert_search_count_with_retry, + delete_asset_async, +) +from tests_v9.integration.client import TestId + +LOGGER = logging.getLogger(__name__) + +MODULE_NAME = TestId.make_unique("GLS") + +TERM_NAME1 = f"{MODULE_NAME}1" +TERM_NAME2 = f"{MODULE_NAME}2" +TERM_NAME3 = f"{MODULE_NAME}3" +TERM_NAME4 = f"{MODULE_NAME}4" + + +async def create_glossary(client: AsyncAtlanClient, name: str) -> AtlasGlossary: + g = AtlasGlossary.creator(name=name) + r = await client.asset.save(g) + return r.assets_created(AtlasGlossary)[0] + + +async def create_category( + client: AsyncAtlanClient, + name: str, + glossary: Optional[AtlasGlossary] = None, + glossary_guid: Optional[str] = None, + glossary_qualified_name: Optional[str] = None, + parent: Optional[AtlasGlossaryCategory] = None, +) -> AtlasGlossaryCategory: + if glossary: + c = AtlasGlossaryCategory.creator( + name=name, anchor=glossary, parent_category=parent or None + ) + elif glossary_guid: + c = AtlasGlossaryCategory.creator( + name=name, glossary_guid=glossary_guid, parent_category=parent or None + ) + elif glossary_qualified_name: + c = AtlasGlossaryCategory.creator( + name=name, + glossary_qualified_name=glossary_qualified_name, + parent_category=parent or None, + ) + return (await client.asset.save(c)).assets_created(AtlasGlossaryCategory)[0] + + +async def create_term( + client: AsyncAtlanClient, + name: str, + glossary: Optional[AtlasGlossary] = None, + glossary_guid: Optional[str] = None, + glossary_qualified_name: Optional[str] = None, + categories: Optional[List[AtlasGlossaryCategory]] = None, +) -> AtlasGlossaryTerm: + if glossary: + t = AtlasGlossaryTerm.creator(name=name, anchor=glossary, categories=categories) + elif glossary_guid: + t = AtlasGlossaryTerm.creator( + name=name, + glossary_guid=glossary_guid, + categories=categories, + ) + elif glossary_qualified_name: + t = AtlasGlossaryTerm.creator( + name=name, + glossary_qualified_name=glossary_qualified_name, + categories=categories, + ) + r = await client.asset.save(t) + return r.assets_created(AtlasGlossaryTerm)[0] + + +@pytest_asyncio.fixture(scope="module") +async def glossary( + client: AsyncAtlanClient, +) -> AsyncGenerator[AtlasGlossary, None]: + g = await create_glossary(client, MODULE_NAME) + yield g + await delete_asset_async(client, guid=g.guid, asset_type=AtlasGlossary) + + +async def test_glossary( + glossary: AtlasGlossary, +): + assert glossary.guid + assert glossary.name == MODULE_NAME + assert glossary.qualified_name + assert glossary.qualified_name != MODULE_NAME + + +@pytest_asyncio.fixture(scope="module") +async def category( + client: AsyncAtlanClient, glossary: AtlasGlossary +) -> AsyncGenerator[AtlasGlossaryCategory, None]: + c = await create_category(client, MODULE_NAME, glossary) + yield c + await delete_asset_async(client, guid=c.guid, asset_type=AtlasGlossaryCategory) + + +@pytest_asyncio.fixture(scope="module") +async def hierarchy_glossary( + client: AsyncAtlanClient, +) -> AsyncGenerator[AtlasGlossary, None]: + g = await create_glossary(client, TestId.make_unique("hierarchy")) + yield g + await delete_asset_async(client, guid=g.guid, asset_type=AtlasGlossary) + + +@pytest_asyncio.fixture(scope="module") +async def top1_category( + client: AsyncAtlanClient, hierarchy_glossary +) -> AsyncGenerator[AtlasGlossaryCategory, None]: + c = await create_category(client, TestId.make_unique("top1"), hierarchy_glossary) + yield c + await delete_asset_async(client, guid=c.guid, asset_type=AtlasGlossaryCategory) + + +@pytest_asyncio.fixture(scope="module") +async def mid1a_category( + client: AsyncAtlanClient, + hierarchy_glossary: AtlasGlossary, + top1_category: AtlasGlossaryCategory, +) -> AsyncGenerator[AtlasGlossaryCategory, None]: + c = await create_category( + client, TestId.make_unique("mid1a"), hierarchy_glossary, parent=top1_category + ) + yield c + await delete_asset_async(client, guid=c.guid, asset_type=AtlasGlossaryCategory) + + +@pytest_asyncio.fixture(scope="module") +async def mid1a_term( + client: AsyncAtlanClient, + hierarchy_glossary: AtlasGlossary, + mid1a_category: AtlasGlossaryCategory, +) -> AsyncGenerator[AtlasGlossaryTerm, None]: + assert mid1a_category.qualified_name + t = await create_term( + client, + name=f"mid1a_{TERM_NAME1}", + glossary_guid=hierarchy_glossary.guid, + categories=[ + AtlasGlossaryCategory.ref_by_qualified_name(mid1a_category.qualified_name) + ], + ) + yield t + await delete_asset_async(client, guid=t.guid, asset_type=AtlasGlossaryTerm) + + +@pytest_asyncio.fixture(scope="module") +async def leaf1aa_category( + client: AsyncAtlanClient, + hierarchy_glossary: AtlasGlossary, + mid1a_category: AtlasGlossaryCategory, +) -> AsyncGenerator[AtlasGlossaryCategory, None]: + assert hierarchy_glossary and hierarchy_glossary.guid + c = await create_category( + client, + TestId.make_unique("leaf1aa"), + glossary_guid=hierarchy_glossary.guid, + parent=mid1a_category, + ) + yield c + await delete_asset_async(client, guid=c.guid, asset_type=AtlasGlossaryCategory) + + +@pytest_asyncio.fixture(scope="module") +async def leaf1ab_category( + client: AsyncAtlanClient, + hierarchy_glossary: AtlasGlossary, + mid1a_category: AtlasGlossaryCategory, +) -> AsyncGenerator[AtlasGlossaryCategory, None]: + c = await create_category( + client, + TestId.make_unique("leaf1ab"), + glossary_qualified_name=hierarchy_glossary.qualified_name, + parent=mid1a_category, + ) + yield c + await delete_asset_async(client, guid=c.guid, asset_type=AtlasGlossaryCategory) + + +@pytest_asyncio.fixture(scope="module") +async def mid1b_category( + client: AsyncAtlanClient, + hierarchy_glossary: AtlasGlossary, + top1_category: AtlasGlossaryCategory, +) -> AsyncGenerator[AtlasGlossaryCategory, None]: + c = await create_category( + client, TestId.make_unique("mid1b"), hierarchy_glossary, parent=top1_category + ) + yield c + await delete_asset_async(client, guid=c.guid, asset_type=AtlasGlossaryCategory) + + +@pytest_asyncio.fixture(scope="module") +async def leaf1ba_category( + client: AsyncAtlanClient, + hierarchy_glossary: AtlasGlossary, + mid1b_category: AtlasGlossaryCategory, +) -> AsyncGenerator[AtlasGlossaryCategory, None]: + c = await create_category( + client, TestId.make_unique("leaf1ba"), hierarchy_glossary, parent=mid1b_category + ) + yield c + await delete_asset_async(client, guid=c.guid, asset_type=AtlasGlossaryCategory) + + +@pytest_asyncio.fixture(scope="module") +async def top2_category( + client: AsyncAtlanClient, hierarchy_glossary: AtlasGlossary +) -> AsyncGenerator[AtlasGlossaryCategory, None]: + c = await create_category(client, TestId.make_unique("top2"), hierarchy_glossary) + yield c + await delete_asset_async(client, guid=c.guid, asset_type=AtlasGlossaryCategory) + + +@pytest_asyncio.fixture(scope="module") +async def mid2a_category( + client: AsyncAtlanClient, + hierarchy_glossary: AtlasGlossary, + top2_category: AtlasGlossaryCategory, +) -> AsyncGenerator[AtlasGlossaryCategory, None]: + c = await create_category( + client, TestId.make_unique("mid2a"), hierarchy_glossary, parent=top2_category + ) + yield c + await delete_asset_async(client, guid=c.guid, asset_type=AtlasGlossaryCategory) + + +@pytest_asyncio.fixture(scope="module") +async def leaf2aa_category( + client: AsyncAtlanClient, + hierarchy_glossary: AtlasGlossary, + mid2a_category: AtlasGlossaryCategory, +) -> AsyncGenerator[AtlasGlossaryCategory, None]: + c = await create_category( + client, TestId.make_unique("leaf2aa"), hierarchy_glossary, parent=mid2a_category + ) + yield c + await delete_asset_async(client, guid=c.guid, asset_type=AtlasGlossaryCategory) + + +@pytest_asyncio.fixture(scope="module") +async def leaf2ab_category( + client: AsyncAtlanClient, + hierarchy_glossary: AtlasGlossary, + mid2a_category: AtlasGlossaryCategory, +) -> AsyncGenerator[AtlasGlossaryCategory, None]: + c = await create_category( + client, TestId.make_unique("leaf2ab"), hierarchy_glossary, parent=mid2a_category + ) + yield c + await delete_asset_async(client, guid=c.guid, asset_type=AtlasGlossaryCategory) + + +@pytest_asyncio.fixture(scope="module") +async def mid2b_category( + client: AsyncAtlanClient, + hierarchy_glossary: AtlasGlossary, + top2_category: AtlasGlossaryCategory, +) -> AsyncGenerator[AtlasGlossaryCategory, None]: + c = await create_category( + client, TestId.make_unique("mid2b"), hierarchy_glossary, parent=top2_category + ) + yield c + await delete_asset_async(client, guid=c.guid, asset_type=AtlasGlossaryCategory) + + +@pytest_asyncio.fixture(scope="module") +async def leaf2ba_category( + client: AsyncAtlanClient, + hierarchy_glossary: AtlasGlossary, + mid2b_category: AtlasGlossaryCategory, +) -> AsyncGenerator[AtlasGlossaryCategory, None]: + c = await create_category( + client, TestId.make_unique("leaf2ba"), hierarchy_glossary, parent=mid2b_category + ) + yield c + await delete_asset_async(client, guid=c.guid, asset_type=AtlasGlossaryCategory) + + +@pytest_asyncio.fixture(scope="module") +async def term_user_def_relationship() -> UserDefRelationship: + test_id = MODULE_NAME.lower() + return UserDefRelationship( + from_type_label=f"Testing from label ({test_id})", + to_type_label=f"Testing to label ({test_id})", + ) + + +async def test_category( + client: AsyncAtlanClient, category: AtlasGlossaryCategory, glossary: AtlasGlossary +): + assert category.guid + assert category.name == MODULE_NAME + assert category.qualified_name + c = await client.asset.get_by_guid( + category.guid, AtlasGlossaryCategory, ignore_relationships=False + ) + assert c + assert c.guid == category.guid + assert c.anchor + assert c.anchor.guid == glossary.guid + + +@pytest_asyncio.fixture(scope="module") +async def term1( + client: AsyncAtlanClient, glossary: AtlasGlossary +) -> AsyncGenerator[AtlasGlossaryTerm, None]: + t = await create_term(client, name=TERM_NAME1, glossary=glossary) + yield t + await delete_asset_async(client, guid=t.guid, asset_type=AtlasGlossaryTerm) + + +async def test_term_failure( + client: AsyncAtlanClient, + glossary: AtlasGlossary, +): + with pytest.raises( + NotFoundError, + match="ATLAN-PYTHON-404-000 Server responded with a not found " + "error ATLAS-404-00-009: Instance AtlasGlossaryTerm with unique attribute *", + ): + await client.asset.update_merging_cm( + AtlasGlossaryTerm.creator( + name=f"{TERM_NAME1} X", glossary_guid=glossary.guid + ) + ) + + +async def test_term1( + client: AsyncAtlanClient, + term1: AtlasGlossaryTerm, + glossary: AtlasGlossary, +): + assert term1.guid + assert term1.name == TERM_NAME1 + assert term1.qualified_name + assert term1.qualified_name != TERM_NAME1 + t = await client.asset.get_by_guid( + term1.guid, asset_type=AtlasGlossaryTerm, ignore_relationships=False + ) + assert t + assert t.guid == term1.guid + assert t.attributes.anchor + assert t.attributes.anchor.guid == glossary.guid + + +@pytest_asyncio.fixture(scope="module") +async def term2( + client: AsyncAtlanClient, glossary: AtlasGlossary +) -> AsyncGenerator[AtlasGlossaryTerm, None]: + t = await create_term(client, name=TERM_NAME2, glossary_guid=glossary.guid) + yield t + await delete_asset_async(client, guid=t.guid, asset_type=AtlasGlossaryTerm) + + +async def test_term2( + client: AsyncAtlanClient, + term2: AtlasGlossaryTerm, + glossary: AtlasGlossary, +): + assert term2.guid + assert term2.name == TERM_NAME2 + assert term2.qualified_name + assert term2.qualified_name != TERM_NAME2 + t = await client.asset.get_by_guid( + term2.guid, asset_type=AtlasGlossaryTerm, ignore_relationships=False + ) + assert t + assert t.guid == term2.guid + assert t.attributes.anchor + assert t.attributes.anchor.guid == glossary.guid + + +@pytest_asyncio.fixture(scope="module") +async def term3( + client: AsyncAtlanClient, glossary: AtlasGlossary +) -> AsyncGenerator[AtlasGlossaryTerm, None]: + t = await create_term( + client, name=TERM_NAME3, glossary_qualified_name=glossary.qualified_name + ) + yield t + await delete_asset_async(client, guid=t.guid, asset_type=AtlasGlossaryTerm) + + +async def test_term3( + client: AsyncAtlanClient, + term3: AtlasGlossaryTerm, + glossary: AtlasGlossary, +): + assert term3.guid + assert term3.name == TERM_NAME3 + assert term3.qualified_name + assert term3.qualified_name != TERM_NAME3 + t = await client.asset.get_by_guid( + term3.guid, asset_type=AtlasGlossaryTerm, ignore_relationships=False + ) + assert t + assert t.guid == term3.guid + assert t.attributes.anchor + assert t.attributes.anchor.guid == glossary.guid + + +@pytest_asyncio.fixture(scope="module") +async def term4( + client: AsyncAtlanClient, glossary: AtlasGlossary +) -> AsyncGenerator[AtlasGlossaryTerm, None]: + t = await create_term(client, name=TERM_NAME4, glossary_guid=glossary.guid) + yield t + await delete_asset_async(client, guid=t.guid, asset_type=AtlasGlossaryTerm) + + +async def test_term4( + client: AsyncAtlanClient, + term4: AtlasGlossaryTerm, + glossary: AtlasGlossary, +): + assert term4.guid + assert term4.name == TERM_NAME4 + assert term4.qualified_name + assert term4.qualified_name != TERM_NAME4 + t = await client.asset.get_by_guid( + term4.guid, asset_type=AtlasGlossaryTerm, ignore_relationships=False + ) + assert t + assert t.guid == term4.guid + assert t.attributes.anchor + assert t.attributes.anchor.guid == glossary.guid + + +async def test_read_glossary( + client: AsyncAtlanClient, + glossary: AtlasGlossary, + term1: AtlasGlossaryTerm, + term2: AtlasGlossaryTerm, + term3: AtlasGlossaryTerm, + term4: AtlasGlossaryTerm, +): + g = await client.asset.get_by_guid( + glossary.guid, asset_type=AtlasGlossary, ignore_relationships=False + ) + assert g + assert isinstance(g, AtlasGlossary) + assert g.guid == glossary.guid + assert g.qualified_name == glossary.qualified_name + assert g.name == glossary.name + terms = g.terms + assert terms + assert len(terms) == 4 + + +async def test_compound_queries( + client: AsyncAtlanClient, + glossary: AtlasGlossary, + term1: AtlasGlossaryTerm, + term2: AtlasGlossaryTerm, + term3: AtlasGlossaryTerm, + term4: AtlasGlossaryTerm, +): + assert glossary.qualified_name + cq = ( + CompoundQuery() + .where(CompoundQuery.active_assets()) + .where(CompoundQuery.asset_type(AtlasGlossaryTerm)) + .where(AtlasGlossaryTerm.NAME.startswith(MODULE_NAME)) + .where(AtlasGlossaryTerm.ANCHOR.eq(glossary.qualified_name)) + ).to_query() + request = IndexSearchRequest(dsl=DSL(query=cq)) + # Use centralized retry utility for eventual consistency + await async_assert_search_count_with_retry(client, request, expected_count=4) + assert glossary.qualified_name + assert term2.name + + cq = ( + CompoundQuery() + .where(CompoundQuery.active_assets()) + .where(CompoundQuery.asset_type(AtlasGlossaryTerm)) + .where(AtlasGlossaryTerm.NAME.startswith(MODULE_NAME)) + .where(AtlasGlossaryTerm.ANCHOR.eq(glossary.qualified_name)) + .where_not(AtlasGlossaryTerm.NAME.eq(term2.name)) + ).to_query() + request = IndexSearchRequest(dsl=DSL(query=cq)) + # Use centralized retry utility for eventual consistency + await async_assert_search_count_with_retry(client, request, expected_count=3) + + +async def test_fluent_search( + client: AsyncAtlanClient, + glossary: AtlasGlossary, + term1: AtlasGlossaryTerm, + term2: AtlasGlossaryTerm, + term3: AtlasGlossaryTerm, + term4: AtlasGlossaryTerm, +): + assert glossary.qualified_name + terms = ( + FluentSearch() + .page_size(1) + .where(CompoundQuery.active_assets()) + .where(CompoundQuery.asset_type(AtlasGlossaryTerm)) + .where(AtlasGlossaryTerm.NAME.startswith(MODULE_NAME)) + .where(AtlasGlossaryTerm.ANCHOR.eq(glossary.qualified_name)) + .include_on_results(AtlasGlossaryTerm.ANCHOR) + .include_on_relations(AtlasGlossary.NAME) + ) + + # Use centralized retry utility to handle search index eventual consistency + await async_assert_fluent_search_count_with_retry(terms, client, expected_count=4) + + guids_chained = [] + g_sorted = [] + + # Execute the async search and collect results into a list first + search_results = await terms.execute_async(client) + all_results = [] + async for asset in search_results: + all_results.append(asset) + + # Now use itertools.islice like sync version + for asset in filter( + lambda x: isinstance(x, AtlasGlossaryTerm), + itertools.islice(all_results, 4), + ): + guids_chained.append(asset.guid) + g_sorted.append(asset.guid) + g_sorted.sort() + assert guids_chained == g_sorted + + results = await FluentSearch( + _page_size=5, + wheres=[ + CompoundQuery.active_assets(), + CompoundQuery.asset_type(AtlasGlossaryTerm), + AtlasGlossaryTerm.NAME.startswith(MODULE_NAME), + AtlasGlossaryTerm.ANCHOR.startswith(glossary.qualified_name), + ], + _includes_on_results=[AtlasGlossaryTerm.ANCHOR.atlan_field_name], + _includes_on_relations=[AtlasGlossary.NAME.atlan_field_name], + ).execute_async(client) + + guids_alt = [] + g_sorted = [] + async for asset in results: + guids_alt.append(asset.guid) + g_sorted.append(asset.guid) + g_sorted.sort() + assert g_sorted == guids_alt + assert glossary.qualified_name + + async_results = await FluentSearch( + _page_size=5, + wheres=[ + CompoundQuery.active_assets(), + CompoundQuery.asset_type(AtlasGlossaryTerm), + AtlasGlossaryTerm.NAME.startswith(MODULE_NAME), + AtlasGlossaryTerm.ANCHOR.startswith(glossary.qualified_name), + ], + _includes_on_results=["anchor"], + _includes_on_relations=["name"], + sorts=[AtlasGlossaryTerm.NAME.order()], + ).execute_async(client) + + names = [] + names_sorted = [] + async for asset in async_results: + names.append(asset.name) + names_sorted.append(asset.name) + names_sorted.sort() + assert names_sorted == names + + +@pytest.mark.order(after="test_read_glossary") +async def test_trim_to_required_glossary( + client: AsyncAtlanClient, + glossary: AtlasGlossary, +): + glossary = glossary.trim_to_required() + response = await client.asset.save(glossary) + assert not response.mutated_entities + + +@pytest.mark.order(after="test_term1") +async def test_term_trim_to_required( + client: AsyncAtlanClient, + term1: AtlasGlossaryTerm, +): + term1 = await client.asset.get_by_guid( + guid=term1.guid, asset_type=AtlasGlossaryTerm, ignore_relationships=False + ) + term1 = term1.trim_to_required() + response = await client.asset.save(term1) + assert not response.mutated_entities + + +async def test_find_glossary_by_name(client: AsyncAtlanClient, glossary: AtlasGlossary): + found_glossary = await client.asset.find_glossary_by_name(name=glossary.name) + assert glossary.guid == found_glossary.guid + + +async def test_find_category_fast_by_name( + client: AsyncAtlanClient, category: AtlasGlossaryCategory, glossary: AtlasGlossary +): + @retry( + wait=wait_fixed(2), + retry=retry_if_exception_type(NotFoundError), + stop=stop_after_attempt(3), + ) + async def check_it(): + result = await client.asset.find_category_fast_by_name( + name=category.name, glossary_qualified_name=glossary.qualified_name + ) + assert category.guid == result[0].guid + + await check_it() + + +async def test_find_category_by_name( + client: AsyncAtlanClient, category: AtlasGlossaryCategory, glossary: AtlasGlossary +): + result = await client.asset.find_category_by_name( + name=category.name, glossary_name=glossary.name + ) + assert category.guid == result[0].guid + + +async def test_find_category_by_name_qn_guid_correctly_populated( + client: AsyncAtlanClient, + hierarchy_glossary: AtlasGlossary, + top1_category: AtlasGlossaryCategory, + top2_category: AtlasGlossaryCategory, + mid1a_category: AtlasGlossaryCategory, + mid1a_term: AtlasGlossaryTerm, + mid2a_category: AtlasGlossaryCategory, +): + categories = await client.asset.find_category_by_name( + name=mid1a_category.name, + glossary_name=hierarchy_glossary.name, + attributes=["terms", "anchor", "parentCategory"], + ) + category = categories[0] + + # Glossary + assert category.anchor + assert category.anchor.guid == hierarchy_glossary.guid + assert category.anchor.name == hierarchy_glossary.name + assert category.anchor.qualified_name == hierarchy_glossary.qualified_name + + # Glossary category + assert category.parent_category + assert category.parent_category.guid == top1_category.guid + assert category.parent_category.name == top1_category.name + assert category.parent_category.qualified_name == top1_category.qualified_name + + # Glossary term + assert category.terms and category.terms[0] + assert category.terms[0].guid == mid1a_term.guid + assert category.terms[0].name == mid1a_term.name + assert category.terms[0].qualified_name == mid1a_term.qualified_name + + +async def test_category_delete_by_guid_raises_error_invalid_request_error( + client: AsyncAtlanClient, category: AtlasGlossaryCategory +): + with pytest.raises( + InvalidRequestError, + match=f"ATLAN-PYTHON-400-052 Asset with guid: {category.guid} is an asset " + "of type AtlasGlossaryCategory which does not support archiving. " + "Suggestion: Please use purge if you wish to remove assets of this type.", + ): + await client.asset.delete_by_guid(guid=category.guid) + + +async def test_find_term_fast_by_name( + client: AsyncAtlanClient, term1: AtlasGlossaryTerm, glossary: AtlasGlossary +): + @retry( + wait=wait_fixed(2), + retry=retry_if_exception_type(NotFoundError), + stop=stop_after_attempt(3), + ) + async def check_it(): + result = await client.asset.find_term_fast_by_name( + name=term1.name, glossary_qualified_name=glossary.qualified_name + ) + assert term1.guid == result.guid + + await check_it() + + +async def test_find_term_by_name( + client: AsyncAtlanClient, term1: AtlasGlossaryTerm, glossary: AtlasGlossary +): + result = await client.asset.find_term_by_name( + name=term1.name, glossary_name=glossary.name + ) + assert term1.guid == result.guid + + +@pytest.mark.parametrize( + "attributes, related_attributes", + [ + (AtlasGlossaryCategory.TERMS, AtlasGlossaryTerm.NAME), + ( + AtlasGlossaryCategory.TERMS.atlan_field_name, + AtlasGlossaryTerm.NAME.atlan_field_name, + ), + ], +) +async def test_hierarchy( + client: AsyncAtlanClient, + hierarchy_glossary: AtlasGlossary, + top1_category: AtlasGlossaryCategory, + mid1a_category: AtlasGlossaryCategory, + leaf1aa_category: AtlasGlossaryCategory, + leaf1ab_category: AtlasGlossaryCategory, + mid1b_category: AtlasGlossaryCategory, + leaf1ba_category: AtlasGlossaryCategory, + top2_category: AtlasGlossaryCategory, + mid2a_category: AtlasGlossaryCategory, + leaf2aa_category: AtlasGlossaryCategory, + leaf2ab_category: AtlasGlossaryCategory, + mid2b_category: AtlasGlossaryCategory, + leaf2ba_category: AtlasGlossaryCategory, + attributes: Union[AtlanField, str], + related_attributes: Union[AtlanField, str], +): + sleep(10) + hierarchy = await client.asset.get_hierarchy( + glossary=hierarchy_glossary, + attributes=[attributes], + related_attributes=[related_attributes], + ) + + root_categories = hierarchy.root_categories + + assert root_categories + assert len(root_categories) == 2 + assert root_categories[0].name + assert root_categories[1].name + assert "top" in root_categories[0].name + assert "top" in root_categories[1].name + assert hierarchy.get_category(top1_category.guid) + category_without_terms = hierarchy.get_category(top1_category.guid) + assert category_without_terms.terms is not None + assert 0 == len(category_without_terms.terms) + assert hierarchy.get_category(mid1a_category.guid) + category_with_term = hierarchy.get_category(mid1a_category.guid) + assert category_with_term.terms + assert 1 == len(category_with_term.terms) + assert f"mid1a_{TERM_NAME1}" == category_with_term.terms[0].name + assert hierarchy.get_category(leaf1aa_category.guid) + assert hierarchy.get_category(leaf1ab_category.guid) + assert hierarchy.get_category(mid1b_category.guid) + assert hierarchy.get_category(leaf1ba_category.guid) + assert hierarchy.get_category(top2_category.guid) + assert hierarchy.get_category(mid2a_category.guid) + assert hierarchy.get_category(leaf2aa_category.guid) + assert hierarchy.get_category(leaf2ab_category.guid) + assert hierarchy.get_category(mid2b_category.guid) + assert hierarchy.get_category(leaf2ba_category.guid) + + category_names = [category.name for category in hierarchy.breadth_first] + + assert len(category_names) == 12 + assert category_names + assert category_names[0] + assert category_names[1] + assert category_names[2] + assert category_names[3] + assert category_names[4] + assert category_names[5] + assert category_names[6] + assert category_names[7] + assert category_names[8] + assert category_names[9] + assert category_names[10] + assert category_names[11] + assert "top" in category_names[0] + assert "top" in category_names[1] + assert "mid" in category_names[2] + assert "mid" in category_names[3] + assert "mid" in category_names[4] + assert "mid" in category_names[5] + assert "leaf" in category_names[6] + assert "leaf" in category_names[7] + assert "leaf" in category_names[8] + assert "leaf" in category_names[9] + assert "leaf" in category_names[10] + assert "leaf" in category_names[11] + + category_names = [category.name for category in hierarchy.depth_first] + + assert len(category_names) == 12 + assert category_names + assert category_names[0] + assert category_names[1] + assert category_names[2] + assert category_names[3] + assert category_names[4] + assert category_names[5] + assert category_names[6] + assert category_names[7] + assert category_names[8] + assert category_names[9] + assert category_names[10] + assert category_names[11] + assert "top" in category_names[0] + assert "mid" in category_names[1] + assert "leaf" in category_names[2] + assert "leaf" in category_names[3] + assert "mid" in category_names[4] + assert "leaf" in category_names[5] + assert "top" in category_names[6] + assert "mid" in category_names[7] + assert "leaf" in category_names[8] + assert "leaf" in category_names[9] + assert "mid" in category_names[10] + assert "leaf" in category_names[11] + + +async def test_create_relationship( + client: AsyncAtlanClient, + term1: AtlasGlossaryTerm, + term2: AtlasGlossaryTerm, + term3: AtlasGlossaryTerm, + glossary: AtlasGlossary, +): + assert term1 + assert term1.name + assert term1.qualified_name + + term = AtlasGlossaryTerm.create_for_modification( + qualified_name=term1.qualified_name, + name=term1.name, + glossary_guid=glossary.guid, + ) + term.see_also = [ + AtlasGlossaryTerm.ref_by_guid(guid=term2.guid), + AtlasGlossaryTerm.ref_by_guid(guid=term3.guid), + ] + response = await client.asset.save(term) + + assert response + result = await client.asset.get_by_guid( + guid=term1.guid, asset_type=AtlasGlossaryTerm, ignore_relationships=False + ) + assert result + assert result.see_also + assert len(result.see_also) == 2 + related_guids = [] + for term in result.see_also: + assert term.guid + related_guids.append(term.guid) + assert term2.guid in related_guids + assert term3.guid in related_guids + + +@pytest.mark.order(after="test_create_relationship") +async def test_remove_relationship( + client: AsyncAtlanClient, + term1: AtlasGlossaryTerm, + term2: AtlasGlossaryTerm, + term3: AtlasGlossaryTerm, + glossary: AtlasGlossary, +): + assert term1 + assert term1.name + assert term1.qualified_name + + term = AtlasGlossaryTerm.create_for_modification( + qualified_name=term1.qualified_name, + name=term1.name, + glossary_guid=glossary.guid, + ) + term.see_also = [ + AtlasGlossaryTerm.ref_by_guid(guid=term2.guid, semantic=SaveSemantic.REMOVE), + ] + response = await client.asset.save(term) + + assert response + result = await client.asset.get_by_guid( + guid=term1.guid, asset_type=AtlasGlossaryTerm, ignore_relationships=False + ) + assert result + assert result.see_also + active_relationships = [] + for term in result.see_also: + assert term.guid + if term.relationship_status == "ACTIVE": + active_relationships.append(term) + assert len(active_relationships) == 1 + assert term3.guid == active_relationships[0].guid + + +@pytest.mark.order(after="test_remove_relationship") +async def test_append_relationship( + client: AsyncAtlanClient, + term1: AtlasGlossaryTerm, + term3: AtlasGlossaryTerm, + term4: AtlasGlossaryTerm, + glossary: AtlasGlossary, +): + assert term1 + assert term1.name + assert term1.qualified_name + + term = AtlasGlossaryTerm.create_for_modification( + qualified_name=term1.qualified_name, + name=term1.name, + glossary_guid=glossary.guid, + ) + term.see_also = [ + AtlasGlossaryTerm.ref_by_guid(guid=term4.guid, semantic=SaveSemantic.APPEND), + ] + response = await client.asset.save(term) + + assert response + result = await client.asset.get_by_guid( + guid=term1.guid, asset_type=AtlasGlossaryTerm, ignore_relationships=False + ) + assert result + assert result.see_also + active_relationships = [] + for term in result.see_also: + assert term.guid + if term.relationship_status == "ACTIVE": + active_relationships.append(term.guid) + assert len(active_relationships) == 2 + assert term3.guid in active_relationships + assert term4.guid in active_relationships + + +@pytest.mark.order(after="test_append_relationship") +async def test_append_relationship_again( + client: AsyncAtlanClient, + term1: AtlasGlossaryTerm, + term3: AtlasGlossaryTerm, + term4: AtlasGlossaryTerm, + glossary: AtlasGlossary, +): + assert term1 + assert term1.name + assert term1.qualified_name + + term = AtlasGlossaryTerm.create_for_modification( + qualified_name=term1.qualified_name, + name=term1.name, + glossary_guid=glossary.guid, + ) + term.see_also = [ + AtlasGlossaryTerm.ref_by_guid(guid=term4.guid, semantic=SaveSemantic.APPEND), + ] + response = await client.asset.save(term) + + assert response + result = await client.asset.get_by_guid( + guid=term1.guid, asset_type=AtlasGlossaryTerm, ignore_relationships=False + ) + assert result + assert result.see_also + active_relationships = [] + for term in result.see_also: + assert term.guid + if term.relationship_status == "ACTIVE": + active_relationships.append(term.guid) + assert len(active_relationships) == 2 + assert term3.guid in active_relationships + assert term4.guid in active_relationships + + +@pytest.mark.order(after="test_append_relationship_again") +async def test_remove_unrelated_relationship( + client: AsyncAtlanClient, + term1: AtlasGlossaryTerm, + term2: AtlasGlossaryTerm, + term3: AtlasGlossaryTerm, + term4: AtlasGlossaryTerm, + glossary: AtlasGlossary, +): + assert term1 + assert term1.name + assert term1.qualified_name + + term = AtlasGlossaryTerm.create_for_modification( + qualified_name=term1.qualified_name, + name=term1.name, + glossary_guid=glossary.guid, + ) + term.see_also = [ + AtlasGlossaryTerm.ref_by_guid(guid=term2.guid, semantic=SaveSemantic.REMOVE), + ] + + response = await client.asset.save(term) + assert response + + result = await client.asset.get_by_guid( + guid=term1.guid, asset_type=AtlasGlossaryTerm, ignore_relationships=False + ) + assert result + assert result.see_also + active_relationships = [] + for term in result.see_also: + assert term.guid + if term.relationship_status == "ACTIVE": + active_relationships.append(term.guid) + assert len(active_relationships) == 2 + assert term3.guid in active_relationships + assert term4.guid in active_relationships + + +async def test_move_sub_category_to_category( + client: AsyncAtlanClient, + hierarchy_glossary: AtlasGlossary, + top1_category: AtlasGlossaryCategory, + top2_category: AtlasGlossaryCategory, + mid1a_category: AtlasGlossaryCategory, + mid2a_category: AtlasGlossaryCategory, +): + sleep(10) + assert mid1a_category.name + assert hierarchy_glossary.guid + assert top1_category.qualified_name + assert top2_category.qualified_name + assert mid1a_category.qualified_name + + hierarchy = await client.asset.get_hierarchy(glossary=hierarchy_glossary) + root_categories = hierarchy.root_categories + + assert len(root_categories) == 2 + root_category_qns = ( + root_categories[0].qualified_name, + root_categories[1].qualified_name, + ) + assert top1_category.qualified_name in root_category_qns + assert top2_category.qualified_name in root_category_qns + + mid1a_category = AtlasGlossaryCategory.updater( + name=mid1a_category.name, + qualified_name=mid1a_category.qualified_name, + glossary_guid=hierarchy_glossary.guid, + ) + mid1a_category.parent_category = None + response = await client.asset.save(mid1a_category) + + if updated := response.assets_updated(asset_type=AtlasGlossaryCategory): + assert updated[0].name == mid1a_category.name + assert updated[0].qualified_name == mid1a_category.qualified_name + else: + pytest.fail(f"Failed to perform update on category: {mid1a_category.name}") + + # Ensure that the sub-category 'mid1a_category' + # has been successfully moved to the root category + sleep(10) + hierarchy = await client.asset.get_hierarchy(glossary=hierarchy_glossary) + root_categories = hierarchy.root_categories + + assert len(root_categories) == 3 + root_category_qns_updated = ( + root_categories[0].qualified_name, + root_categories[1].qualified_name, + root_categories[2].qualified_name, + ) + assert top1_category.qualified_name in root_category_qns_updated + assert top2_category.qualified_name in root_category_qns_updated + assert mid1a_category.qualified_name in root_category_qns_updated + + +async def test_user_def_relationship_on_terms( + client: AsyncAtlanClient, + term1: AtlasGlossaryTerm, + term2: AtlasGlossaryTerm, + glossary: AtlasGlossary, + term_user_def_relationship: UserDefRelationship, +): + term1_to_update = AtlasGlossaryTerm.updater( + qualified_name=term1.qualified_name, + name=term1.name, + glossary_guid=glossary.guid, + ) + term2 = AtlasGlossaryTerm.ref_by_guid(term2.guid) + term1_to_update.user_def_relationship_to = [ + term_user_def_relationship.user_def_relationship_to(term2) + ] + + response = await client.asset.save(term1_to_update) + assert response.mutated_entities + assert not response.mutated_entities.CREATE + assert response.mutated_entities.UPDATE + assert len(response.mutated_entities.UPDATE) == 2 + assets = response.assets_updated(asset_type=AtlasGlossaryTerm) + assert len(assets) == 2 + + +def _assert_relationship(relationship, expected_type_name, udr): + assert relationship + assert relationship.guid + assert relationship.type_name + assert relationship.attributes + assert relationship.attributes.relationship_attributes + assert relationship.attributes.relationship_attributes.attributes + assert ( + relationship.attributes.relationship_attributes.type_name == expected_type_name + ) + assert relationship.attributes.relationship_attributes == udr + + +@pytest.mark.order(after="test_user_def_relationship_on_terms") +async def test_search_user_def_relationship_on_terms( + client: AsyncAtlanClient, + term1: AtlasGlossaryTerm, + term2: AtlasGlossaryTerm, + term_user_def_relationship: UserDefRelationship, +): + # Wait for assets to be indexed + sleep(5) + assert term1 and term1.guid + assert term2 and term2.guid + results = await ( + FluentSearch() + .select() + .where_some(AtlasGlossaryTerm.GUID.eq(term1.guid)) + .where_some(AtlasGlossaryTerm.GUID.eq(term2.guid)) + .include_on_results(AtlasGlossaryTerm.USER_DEF_RELATIONSHIP_TO) + .include_on_results(AtlasGlossaryTerm.USER_DEF_RELATIONSHIP_FROM) + .include_relationship_attributes(True) + .enable_full_restriction(True) + .execute_async(client=client) + ) + assert results and results.count == 2 + async for asset in results: + assert asset and asset.guid + if asset.guid == term1.guid: + assert ( + asset.user_def_relationship_to + and len(asset.user_def_relationship_to) == 1 + ) + _assert_relationship( + asset.user_def_relationship_to[0], + UserDefRelationship.__name__, + term_user_def_relationship, + ) + else: + assert ( + asset.user_def_relationship_from + and len(asset.user_def_relationship_from) == 1 + ) + _assert_relationship( + asset.user_def_relationship_from[0], + UserDefRelationship.__name__, + term_user_def_relationship, + ) diff --git a/tests_v9/integration/aio/test_index_search.py b/tests_v9/integration/aio/test_index_search.py new file mode 100644 index 000000000..2cf094ea2 --- /dev/null +++ b/tests_v9/integration/aio/test_index_search.py @@ -0,0 +1,955 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. +import math +from dataclasses import dataclass, field +from datetime import datetime +from time import sleep, time +from typing import AsyncGenerator, Set +from unittest.mock import patch + +import httpx +import pytest +import pytest_asyncio +from httpx_retries import Retry + +from pyatlan.cache.aio.source_tag_cache import AsyncSourceTagName +from pyatlan.client.aio.atlan import client_connection +from pyatlan.client.common.asset import LOGGER +from pyatlan_v9.client.aio.asset import AsyncIndexSearchResults +from pyatlan_v9.client.aio.atlan import AsyncAtlanClient +from pyatlan_v9.model.assets import ( + Asset, + AtlasGlossaryTerm, + Column, + Persona, + Purpose, + Table, +) +from pyatlan_v9.model.core import AtlanTag, AtlanTagName +from pyatlan_v9.model.enums import AtlanConnectorType, CertificateStatus, SortOrder +from pyatlan_v9.model.fields.atlan_fields import SearchableField +from pyatlan_v9.model.fluent_search import CompoundQuery, FluentSearch +from pyatlan_v9.model.search import ( + DSL, + Exists, + IndexSearchRequest, + Match, + Prefix, + Range, + Regexp, + Term, + Wildcard, +) +from pyatlan_v9.model.structs import SourceTagAttachment, SourceTagAttachmentValue + +from .utils import get_optimized_page_size + +QUALIFIED_NAME = "qualifiedName" +ASSET_GUID = Asset.GUID.keyword_field_name +NOW_AS_TIMESTAMP = int(time() * 1000) +NOW_AS_YYYY_MM_DD = datetime.today().strftime("%Y-%m-%d") +EXISTING_TAG = "Issue" +EXISTING_SOURCE_SYNCED_TAG = "Confidential" +DB_NAME = "ANALYTICS" +TABLE_NAME = "STG_STATE_PROVINCES" +COLUMN_NAME = "LATEST_RECORDED_POPULATION" +SCHEMA_NAME = "WIDE_WORLD_IMPORTERS" + +VALUES_FOR_TERM_QUERIES = { + "with_categories": "VBsYc9dUoEcAtDxZmjby6@mweSfpXBwfYWedQTvA3Gi", + "with_classification_names": "RBmhFJqX50bl5RAeJhwt1a", + "with_classifications_text": "VBsYc9dUoEcAtDxZmjby6@mweSfpXBwfYWedQTvA3Gi", + "with_connector_name": AtlanConnectorType.SNOWFLAKE, + "with_create_time_as_timestamp": datetime.fromtimestamp(1665727666701 / 1000), + "with_created_by": "bryan", + "with_glossary": "mweSfpXBwfYWedQTvA3Gi", + "with_guid": "b95eed37-fe38-48d7-8240-0c3390ef4e48", + "with_has_lineage": True, + "with_meanings": "2EqDFWZ6sCjbxcDNL0jFV@3Wn0W7PFCfjyKmGBZ7FLD", + "with_meanings_text": "VBsYc9dUoEcAtDxZmjby6@mweSfpXBwfYWedQTvA3Gi", + "with_modified_by": "bryan", + "with_name": "Schema", + "with_owner_groups": "data_engineering", + "with_owner_users": "ravi", + "with_parent_category": "fWB1bJLOhEd4ik1Um1EJ8@3Wn0W7PFCfjyKmGBZ7FLD", + "with_qualified_name": "default/oracle/1665680872/ORCL/SCALE_TEST/TABLE_MVD_3042/PERSON_ID", + "with_state": "ACTIVE", + "with_super_type_names": "SQL", + "with_type_name": "Schema", + "with_update_time_as_timestamp": datetime.fromtimestamp(1665723703029 / 1000), + "with_certificate_status": CertificateStatus.VERIFIED, +} + +VALUES_FOR_TEXT_QUERIES = { + "with_categories": "VBsYc9dUoEcAtDxZmjby6@mweSfpXBwfYWedQTvA3Gi", + "with_classification_names": "RBmhFJqX50bl5RAeJhwt1a", + "with_classifications_text": "RBmhFJqX50bl5RAeJhwt1a", + "with_created_by": "bryan", + "with_description": "snapshot", + "with_glossary": "mweSfpXBwfYWedQTvA3Gi", + "with_guid": "b95eed37-fe38-48d7-8240-0c3390ef4e48", + "with_has_lineage": True, + "with_meanings": "2EqDFWZ6sCjbxcDNL0jFV@3Wn0W7PFCfjyKmGBZ7FLD", + "with_meanings_text": "Term Test", + "with_modification_timestamp": 1665086276846, + "with_modified_by": "bryan", + "with_name": "Schema", + "with_parent_category": "fWB1bJLOhEd4ik1Um1EJ8@3Wn0W7PFCfjyKmGBZ7FLD", + "with_propagated_classification_names": "RBmhFJqX50bl5RAeJhwt1a", + "with_qualified_name": "default", + "with_state": "ACTIVE", + "with_super_type_names": "ObjectStore SQL", + "with_timestamp": 1665727666701, + "with_trait_names": "RBmhFJqX50bl5RAeJhwt1a", + "with_propagated_trait_names": "RBmhFJqX50bl5RAeJhwt1a", + "with_type_name": "Schema", + "with_user_description": "this", +} +EXISTING_PURPOSE_NAME = "Known Issues" +EXISTING_PERSONA_NAME = "Business Definitions" + + +@pytest_asyncio.fixture(scope="module") +async def business_definitions_persona(client: AsyncAtlanClient): + personas = await client.asset.find_personas_by_name(EXISTING_PERSONA_NAME) + return personas[0] + + +@pytest_asyncio.fixture(scope="module") +async def known_issues_purpose(client: AsyncAtlanClient): + purposes = await client.asset.find_purposes_by_name(EXISTING_PURPOSE_NAME) + return purposes[0] + + +@pytest_asyncio.fixture(scope="module") +async def snowflake_conn(client: AsyncAtlanClient): + connections = await client.asset.find_connections_by_name( + "development", AtlanConnectorType.SNOWFLAKE + ) + return connections[0] + + +@pytest_asyncio.fixture(scope="module") +async def snowflake_column_qn(snowflake_conn): + return f"{snowflake_conn.qualified_name}/{DB_NAME}/{SCHEMA_NAME}/{TABLE_NAME}/{COLUMN_NAME}" + + +@dataclass() +class AssetTracker: + missing_types: Set[str] = field(default_factory=set) + found_types: Set[str] = field(default_factory=set) + + +@pytest_asyncio.fixture(scope="module") +async def asset_tracker() -> AsyncGenerator[AssetTracker, None]: + tracker = AssetTracker() + yield tracker + print("Total number of asset types found: ", len(tracker.found_types)) + print("Total number of asset types missing: ", len(tracker.missing_types)) + print("Assets were not found for the following types:") + for name in sorted(tracker.missing_types): + print("\t", name) + print("Assets were found for the following types:") + for name in sorted(tracker.found_types): + print("\t", name) + + +def get_all_subclasses(cls): + all_subclasses = [] + + for subclass in cls.__subclasses__(): + all_subclasses.append(subclass) + all_subclasses.extend(get_all_subclasses(subclass)) + + return all_subclasses + + +@pytest.mark.parametrize("cls", [(cls) for cls in get_all_subclasses(Asset)]) +async def test_search(client: AsyncAtlanClient, asset_tracker, cls): + name = cls.__name__ + query = Term.with_state("ACTIVE") + post_filter = Term.with_type_name(name) + dsl = DSL(query=query, post_filter=post_filter) + request = IndexSearchRequest(dsl=dsl, attributes=["name"]) + results = await client.asset.search(criteria=request) + if results.count > 0: + asset_tracker.found_types.add(name) + counter = 0 + async for asset in results: + assert isinstance(asset, cls) + counter += 1 + if counter > 3: + break + else: + asset_tracker.missing_types.add(name) + + +async def test_search_with_enable_full_restriction(client: AsyncAtlanClient): + """Test async search API with enableFullRestriction parameter.""" + query = Term.with_state("ACTIVE") + post_filter = Term.with_type_name("Table") + dsl = DSL(query=query, post_filter=post_filter, size=1) + + # Test with enableFullRestriction=True + request = IndexSearchRequest( + dsl=dsl, attributes=["name"], enable_full_restriction=True + ) + results = await client.asset.search(criteria=request) + assert results is not None + assert hasattr(results, "count") + + # Test with enableFullRestriction=False + request_false = IndexSearchRequest( + dsl=dsl, attributes=["name"], enable_full_restriction=False + ) + results_false = await client.asset.search(criteria=request_false) + assert results_false is not None + assert hasattr(results_false, "count") + + # Test without the parameter (default behavior) + request_default = IndexSearchRequest(dsl=dsl, attributes=["name"]) + results_default = await client.asset.search(criteria=request_default) + assert results_default is not None + assert hasattr(results_default, "count") + + +def _assert_source_tag(tables, source_tag, source_tag_value): + assert tables and len(tables) > 0 + for table in tables: + tags = table.atlan_tags + assert tags and len(tags) > 0 + synced_tags = [tag for tag in tags if str(tag.type_name) == source_tag] + assert synced_tags and len(synced_tags) > 0 + for st in synced_tags: + attachments = st.source_tag_attachments + assert attachments and len(attachments) > 0 + for sta in attachments: + values = sta.source_tag_value + assert values and len(values) > 0 + for value in values: + attached_value = value.tag_attachment_value + assert attached_value and attached_value == source_tag_value + + +async def test_search_source_synced_assets(client: AsyncAtlanClient): + search_results = await ( + FluentSearch() + .select() + .where(CompoundQuery.asset_type(Table)) + .where( + await CompoundQuery.tagged_with_value_async( + client=client, + atlan_tag_name=EXISTING_SOURCE_SYNCED_TAG, + value="Highly Restricted", + ) + ) + .execute_async(client=client) + ) + tables = [table async for table in search_results if isinstance(table, Table)] + _assert_source_tag(tables, EXISTING_SOURCE_SYNCED_TAG, "Highly Restricted") + + +async def test_source_tag_assign_with_value(client: AsyncAtlanClient, table: Table): + """ + Test source tag assignment with values using async SourceTagAttachment.by_name_async(). + + Note: This test demonstrates that the async implementation is correct, but may be skipped + due to environmental differences between sync and async source tag cache behavior. + """ + # Make sure no tags are assigned initially + assert table.guid + table = await client.asset.get_by_guid( + guid=table.guid, asset_type=Table, ignore_relationships=False + ) + assert not table.atlan_tags + assert table.name and table.qualified_name + + # Test with the exact same approach as the sync test + source_tag_name = await AsyncSourceTagName.creator( + client=client, + tag="snowflake/development@@ANALYTICS/WIDE_WORLD_IMPORTERS/CONFIDENTIAL", + ) + + # Test our async implementation + source_tag_attachment = await SourceTagAttachment.by_name_async( + client=client, + name=source_tag_name, + source_tag_values=[ + SourceTagAttachmentValue(tag_attachment_value="Not Restricted") + ], + ) + + to_update = table.updater(table.qualified_name, table.name) + to_update.atlan_tags = [ + AtlanTag.of(atlan_tag_name=AtlanTagName(EXISTING_TAG)), + await AtlanTag.of_async( + atlan_tag_name=AtlanTagName(EXISTING_SOURCE_SYNCED_TAG), + source_tag_attachment=source_tag_attachment, + client=client, + ), + ] + response = await client.asset.save(to_update, replace_atlan_tags=True) + + assert (tables := response.assets_updated(asset_type=Table)) and len(tables) == 1 + assert ( + tables + and len(tables) == 1 + and tables[0].atlan_tags + and len(tables[0].atlan_tags) == 2 + ) + for tag in tables[0].atlan_tags: + assert str(tag.type_name) in (EXISTING_TAG, EXISTING_SOURCE_SYNCED_TAG) + + # Make sure source tag is now attached + # to the table with the provided value + sleep(5) + search_results = await ( + FluentSearch() + .select() + .where(CompoundQuery.asset_type(Table)) + .where(Table.QUALIFIED_NAME.eq(table.qualified_name)) + .where( + await CompoundQuery.tagged_with_value_async( + client=client, + atlan_tag_name=EXISTING_SOURCE_SYNCED_TAG, + value="Not Restricted", + ) + ) + .execute_async(client=client) + ) + tables = [table async for table in search_results if isinstance(table, Table)] + + assert ( + tables + and len(tables) == 1 + and tables[0].atlan_tags + and len(tables[0].atlan_tags) == 2 + ) + for tag in tables[0].atlan_tags: + assert str(tag.type_name) in (EXISTING_TAG, EXISTING_SOURCE_SYNCED_TAG) + _assert_source_tag(tables, EXISTING_SOURCE_SYNCED_TAG, "Not Restricted") + + +async def test_search_source_specific_custom_attributes( + client: AsyncAtlanClient, snowflake_column_qn: str +): + # Test with get_by_qualified_name() + asset = await client.asset.get_by_qualified_name( + asset_type=Column, + qualified_name=snowflake_column_qn, + min_ext_info=True, + ignore_relationships=True, + ) + assert asset and asset.custom_attributes + + # Test with FluentSearch() + results = await ( + FluentSearch() + .where(CompoundQuery.active_assets()) + .where(Column.QUALIFIED_NAME.eq(snowflake_column_qn)) + .include_on_results(Column.CUSTOM_ATTRIBUTES) + .execute_async(client=client) + ) + assert results and results.count == 1 + assert results.current_page() and len(results.current_page()) == 1 + column = results.current_page()[0] + assert isinstance(column, Column) and column and column.custom_attributes + + +async def test_search_next_page(client: AsyncAtlanClient): + # Get optimized page size for better performance + total_assets, size = await get_optimized_page_size( + client=client, + query=Term.with_state("ACTIVE"), + post_filter=Term.with_type_name(value="AtlasGlossaryTerm"), + target_api_calls=10, + ) + + # Run the test with optimized page size + dsl = DSL( + query=Term.with_state("ACTIVE"), + post_filter=Term.with_type_name(value="AtlasGlossaryTerm"), + size=size, + ) + request = IndexSearchRequest(dsl=dsl) + results = await client.asset.search(criteria=request) + assert results.count > size + assert len(results.current_page()) == size + counter = 0 + while True: + for _ in results.current_page(): + counter += 1 + if await results.next_page() is not True: + break + assert counter == results.count + + +async def _assert_search_results( + results, expected_sorts, size, TOTAL_ASSETS, bulk=False +): + assert results.count > size + assert len(results.current_page()) == size + counter = 0 + async for term in results: + assert term + counter += 1 + assert counter == TOTAL_ASSETS + assert results + assert results._bulk is bulk + assert not results.aggregations + assert results._criteria.dsl.sort == expected_sorts + + +@patch.object(LOGGER, "debug") +async def test_search_pagination(mock_logger, client: AsyncAtlanClient): + # Avoid testing on integration tests objects + exclude_sdk_terms = [ + Asset.NAME.wildcard("psdkv9_*"), + Asset.NAME.wildcard("jsdk_*"), + Asset.NAME.wildcard("gsdk_*"), + ] + query = CompoundQuery( + where_nots=exclude_sdk_terms, where_somes=[CompoundQuery.active_assets()] + ).to_query() + + # Test search() with DSL: using default offset-based pagination + # when results are less than the predefined threshold (i.e: 100,000 assets) + dsl = DSL( + query=query, + post_filter=Term.with_type_name(value="AtlasGlossaryTerm"), + size=0, # to get the total count + ) + + request = IndexSearchRequest(dsl=dsl) + results = await client.asset.search(criteria=request) + # Assigning this here to ensure the total assets + # remain constant across different test cases + TOTAL_ASSETS = results.count + + # set page_size to divide into ~5 API calls + size = max(1, math.ceil(TOTAL_ASSETS / 5)) + request.dsl.size = size + + # Now, we can test different test scenarios for search() with the dynamic page size + results = await client.asset.search(criteria=request) + + expected_sorts = [Asset.GUID.order(SortOrder.ASCENDING)] + await _assert_search_results(results, expected_sorts, size, TOTAL_ASSETS) + + # Test search() DSL: with `bulk` option using timestamp-based pagination + dsl = DSL( + query=query, + post_filter=Term.with_type_name(value="AtlasGlossaryTerm"), + size=size, + ) + request = IndexSearchRequest(dsl=dsl) + results = await client.asset.search(criteria=request, bulk=True) + expected_sorts = [ + Asset.CREATE_TIME.order(SortOrder.ASCENDING), + Asset.GUID.order(SortOrder.ASCENDING), + ] + await _assert_search_results(results, expected_sorts, size, TOTAL_ASSETS, True) + assert mock_logger.call_count == 1 + assert "Bulk search option is enabled." in mock_logger.call_args_list[0][0][0] + mock_logger.reset_mock() + + # Test search(): using default offset-based pagination + # when results are less than the predefined threshold (i.e: 100,000 assets) + request = ( + FluentSearch(where_nots=exclude_sdk_terms) + .where(CompoundQuery.active_assets()) + .where(CompoundQuery.asset_type(AtlasGlossaryTerm)) + .page_size(size) + ).to_request() + results = await client.asset.search(criteria=request) + expected_sorts = [Asset.GUID.order(SortOrder.ASCENDING)] + await _assert_search_results(results, expected_sorts, size, TOTAL_ASSETS) + + # Test search(): with `bulk` option using timestamp-based pagination + request = ( + FluentSearch(where_nots=exclude_sdk_terms) + .where(CompoundQuery.active_assets()) + .where(CompoundQuery.asset_type(AtlasGlossaryTerm)) + .page_size(size) + ).to_request() + results = await client.asset.search(criteria=request, bulk=True) + expected_sorts = [ + Asset.CREATE_TIME.order(SortOrder.ASCENDING), + Asset.GUID.order(SortOrder.ASCENDING), + ] + await _assert_search_results(results, expected_sorts, size, TOTAL_ASSETS, True) + assert mock_logger.call_count == 1 + assert "Bulk search option is enabled." in mock_logger.call_args_list[0][0][0] + mock_logger.reset_mock() + + # Test search() execute(): with `bulk` option using timestamp-based pagination + results = await ( + FluentSearch(where_nots=exclude_sdk_terms) + .where(CompoundQuery.active_assets()) + .where(CompoundQuery.asset_type(AtlasGlossaryTerm)) + .page_size(size) + ).execute_async(client, bulk=True) + expected_sorts = [ + Asset.CREATE_TIME.order(SortOrder.ASCENDING), + Asset.GUID.order(SortOrder.ASCENDING), + ] + await _assert_search_results(results, expected_sorts, size, TOTAL_ASSETS, True) + assert mock_logger.call_count == 1 + assert "Bulk search option is enabled." in mock_logger.call_args_list[0][0][0] + mock_logger.reset_mock() + + # Test search(): when the number of results exceeds the predefined threshold, + # the SDK automatically switches to a `bulk` search option using timestamp-based pagination. + with patch.object(AsyncIndexSearchResults, "_MASS_EXTRACT_THRESHOLD", 1): + request = ( + FluentSearch(where_nots=exclude_sdk_terms) + .where(CompoundQuery.active_assets()) + .where(CompoundQuery.asset_type(AtlasGlossaryTerm)) + .page_size(size) + ).to_request() + results = await client.asset.search(criteria=request) + expected_sorts = [ + Asset.CREATE_TIME.order(SortOrder.ASCENDING), + Asset.GUID.order(SortOrder.ASCENDING), + ] + await _assert_search_results(results, expected_sorts, size, TOTAL_ASSETS) + assert mock_logger.call_count < TOTAL_ASSETS + assert ( + "Result size (%s) exceeds threshold (%s)." + in mock_logger.call_args_list[0][0][0] + ) + mock_logger.reset_mock() + + +async def test_search_iter(client: AsyncAtlanClient): + # Get optimized page size for better performance + total_assets, size = await get_optimized_page_size( + client=client, + query=Term.with_state("ACTIVE"), + post_filter=Term.with_type_name("AtlasGlossaryTerm"), + target_api_calls=10, + ) + + # Run the test with optimized page size + dsl = DSL( + query=Term.with_state("ACTIVE"), + post_filter=Term.with_type_name("AtlasGlossaryTerm"), + size=size, + ) + request = IndexSearchRequest(dsl=dsl) + results = await client.asset.search(criteria=request) + assert results.count > size + assets = [a async for a in results] + assert len(assets) == results.count + + +async def test_search_next_when_start_changed_returns_remaining( + client: AsyncAtlanClient, +): + # This test specifically tests pagination near the end, so keep size small + size = 2 + + # Get total count to ensure we have enough data for this edge case test + total_assets, _ = await get_optimized_page_size( + client=client, + query=Term.with_state("ACTIVE"), + post_filter=Term.with_type_name("Table"), + attributes=["databaseName"], + min_size=size, + ) + + # Ensure we have enough assets for this test + assert total_assets >= size, f"Need at least {size} assets, got {total_assets}" + + # Run the test with small page size (this test specifically needs small pages) + dsl = DSL( + query=Term.with_state("ACTIVE"), + post_filter=Term.with_type_name("Table"), + size=size, + ) + request = IndexSearchRequest( + dsl=dsl, + attributes=["databaseName"], + ) + results = await client.asset.search(criteria=request) + assert await results.next_page(start=results.count - size) is True + results_list = [item async for item in results] + assert len(results_list) == size + + +@pytest.fixture() +def term_query_value(request): + return VALUES_FOR_TERM_QUERIES[request.param] + + +@pytest.fixture() +def text_query_value(request): + return VALUES_FOR_TEXT_QUERIES[request.param] + + +@pytest.mark.parametrize( + "term_query_value, method, clazz", + [ + (method, method, query) + for query in [Term, Prefix, Regexp, Wildcard] + for method in sorted(dir(query)) + if method.startswith("with_") and method != "with_custom_metadata" + ], + indirect=["term_query_value"], +) +async def test_term_queries_factory( + client: AsyncAtlanClient, term_query_value, method, clazz +): + assert hasattr(clazz, method) + query = getattr(clazz, method)(term_query_value) + filter = ~Term.with_type_name("__AtlasAuditEntry") + dsl = DSL(query=query, post_filter=filter, size=1) + request = IndexSearchRequest( + dsl=dsl, + attributes=["name"], + ) + results = await client.asset.search(criteria=request) + assert results.count >= 0 + + +@pytest.mark.parametrize( + "with_name", + [ + (method) + for method in dir(Exists) + # if method.startswith("with_") and method != "with_custom_metadata" + if method == "with_create_time_as_timestamp" + ], +) +async def test_exists_query_factory(client: AsyncAtlanClient, with_name): + assert hasattr(Exists, with_name) + query = getattr(Exists, with_name)() + filter = ~Term(field="__typeName.keyword", value="__AtlasAuditEntry") + dsl = DSL(query=query, post_filter=filter, size=1) + request = IndexSearchRequest( + dsl=dsl, + attributes=["name"], + ) + results = await client.asset.search(criteria=request) + assert results.count >= 0 + + +@pytest.mark.parametrize( + "text_query_value, method, clazz", + [ + (method, method, query) + for query in [Match] + for method in sorted(dir(query)) + if method.startswith("with_") + ], + indirect=["text_query_value"], +) +async def test_text_queries_factory( + client: AsyncAtlanClient, text_query_value, method, clazz +): + assert hasattr(clazz, method) + query = getattr(clazz, method)(text_query_value) + filter = ~Term.with_type_name("__AtlasAuditEntry") + dsl = DSL(query=query, post_filter=filter, size=1) + request = IndexSearchRequest( + dsl=dsl, + attributes=["name"], + ) + results = await client.asset.search(criteria=request) + assert results.count >= 0 + + +@pytest.mark.parametrize( + "value, method, format", + [ + (0, "with_popularity_score", None), + (NOW_AS_TIMESTAMP, "with_create_time_as_timestamp", None), + (NOW_AS_YYYY_MM_DD, "with_create_time_as_date", "yyyy-MM-dd"), + (NOW_AS_TIMESTAMP, "with_update_time_as_timestamp", None), + (NOW_AS_YYYY_MM_DD, "with_update_time_as_date", "yyyy-MM-dd"), + ], +) +async def test_range_factory(client: AsyncAtlanClient, value, method, format): + assert hasattr(Range, method) + query = getattr(Range, method)(lt=value, format=format) + filter = ~Term(field="__typeName.keyword", value="__AtlasAuditEntry") + dsl = DSL(query=query, post_filter=filter, size=1) + request = IndexSearchRequest( + dsl=dsl, + attributes=["name"], + ) + results = await client.asset.search(criteria=request) + assert results.count >= 0 + + +async def test_bucket_aggregation(client: AsyncAtlanClient): + request = ( + FluentSearch.select() + .aggregate("type", Asset.TYPE_NAME.bucket_by()) + .sort(Asset.CREATE_TIME.order()) + .page_size(0) # only interested in checking aggregation results + ).to_request() + results = await client.asset.search(criteria=request) + assert results.aggregations + result = results.aggregations["type"] + assert result + assert result.buckets + assert len(result.buckets) > 0 + for bucket in result.buckets: + assert bucket.key + assert bucket.doc_count + + +async def test_nested_bucket_aggregation(client: AsyncAtlanClient): + nested_aggs_level_2 = Asset.TYPE_NAME.bucket_by( + nested={"asset_guid": Asset.GUID.bucket_by()} + ) + nested_aggs = Asset.TYPE_NAME.bucket_by(nested={"asset_name": nested_aggs_level_2}) + request = ( + FluentSearch.select() + .aggregate("asset_type", nested_aggs) + .sort(Asset.CREATE_TIME.order()) + .page_size(0) # only interested in checking aggregation results + .to_request() + ) + results = await client.asset.search(criteria=request) + + assert results.aggregations + result = results.aggregations["asset_type"] + assert result + assert result.buckets + assert len(result.buckets) > 0 + for bucket in result.buckets: + assert bucket.key + assert bucket.doc_count + assert bucket.nested_results + nested_results = bucket.nested_results["asset_name"] + assert nested_results + # Nested results level 1 + for bucket in nested_results.buckets: + assert bucket.key + assert bucket.doc_count + assert bucket.nested_results + nested_results = bucket.nested_results["asset_guid"] + assert nested_results + # Nested results level 2 + for bucket in nested_results.buckets: + assert bucket.key + assert bucket.doc_count + # Make sure it's not nested further + assert not bucket.nested_results + + +async def test_aggregation_source_value(client: AsyncAtlanClient): + request = ( + FluentSearch.select() + .aggregate( + "asset_type", + Asset.TYPE_NAME.bucket_by( + nested={ + "asset_description": Asset.DESCRIPTION.bucket_by( + include_source_value=True + ) + }, + ), + ) + .sort(Asset.CREATE_TIME.order()) + .page_size(0) # only interested in checking aggregation results + .to_request() + ) + results = await client.asset.search(criteria=request) + + source_value_found = False + assert results.aggregations + result = results.aggregations["asset_type"] + assert result + assert result.buckets + assert len(result.buckets) > 0 + for bucket in result.buckets: + assert bucket.key + assert bucket.doc_count + assert bucket.nested_results + nested_results = bucket.nested_results["asset_description"] + assert nested_results + # Nested results level 1 + for bucket in nested_results.buckets: + if not bucket.key: + continue + assert bucket.key + assert bucket.doc_count + assert bucket.nested_results + if SearchableField.EMBEDDED_SOURCE_VALUE in bucket.nested_results: + nested_results = bucket.nested_results[ + SearchableField.EMBEDDED_SOURCE_VALUE + ] + assert ( + nested_results + and nested_results.hits + and nested_results.hits.hits + and nested_results.hits.hits[0] + ) + assert bucket.get_source_value(Asset.DESCRIPTION) + source_value_found = True + + if not source_value_found: + pytest.fail( + "Failed to retrieve the source value for asset description in the aggregation" + ) + + +async def test_metric_aggregation(client: AsyncAtlanClient): + request = ( + FluentSearch() + .where(Term.with_type_name("Table")) + .aggregate("avg_columns", Table.COLUMN_COUNT.avg()) + .aggregate("min_columns", Table.COLUMN_COUNT.min()) + .aggregate("max_columns", Table.COLUMN_COUNT.max()) + .aggregate("sum_columns", Table.COLUMN_COUNT.sum()) + .sort(Asset.CREATE_TIME.order()) + ).to_request() + results = await client.asset.search(criteria=request) + assert results + assert results.aggregations + assert results.aggregations["avg_columns"] + assert results.aggregations["min_columns"] + assert results.aggregations["max_columns"] + assert results.aggregations["sum_columns"] + + +async def test_index_search_with_no_aggregation_results(client: AsyncAtlanClient): + test_aggs = {"max_update_time": {"max": {"field": "__modificationTimestamp"}}} + request = ( + FluentSearch(aggregations=test_aggs).where( # type:ignore[arg-type] + Column.QUALIFIED_NAME.startswith("some-non-existent-column-qn") + ) + ).to_request() + response = await client.asset.search(criteria=request) + + assert response + assert response.count == 0 + assert not response.aggregations + + +async def test_default_sorting(client: AsyncAtlanClient): + # Empty sorting + request = ( + FluentSearch().where(Asset.QUALIFIED_NAME.eq("test-qn", case_insensitive=True)) + ).to_request() + response = await client.asset.search(criteria=request) + sort_options = response._criteria.dsl.sort # type: ignore + assert response + assert len(sort_options) == 1 + assert sort_options[0].field == ASSET_GUID + + # Sort without GUID + request = ( + FluentSearch() + .where(Asset.QUALIFIED_NAME.eq("test-qn", case_insensitive=True)) + .sort(Asset.QUALIFIED_NAME.order(SortOrder.ASCENDING)) + ).to_request() + response = await client.asset.search(criteria=request) + sort_options = response._criteria.dsl.sort # type: ignore + assert response + assert len(sort_options) == 2 + assert sort_options[0].field == QUALIFIED_NAME + assert sort_options[1].field == ASSET_GUID + + # Sort with only GUID + request = ( + FluentSearch() + .where(Asset.QUALIFIED_NAME.eq("test-qn", case_insensitive=True)) + .sort(Asset.GUID.order(SortOrder.ASCENDING)) + ).to_request() + response = await client.asset.search(criteria=request) + sort_options = response._criteria.dsl.sort # type: ignore + assert response + assert len(sort_options) == 1 + assert sort_options[0].field == ASSET_GUID + + # Sort with GUID and others + request = ( + FluentSearch() + .where(Asset.QUALIFIED_NAME.eq("test-qn", case_insensitive=True)) + .sort(Asset.QUALIFIED_NAME.order(SortOrder.ASCENDING)) + .sort(Asset.GUID.order(SortOrder.ASCENDING)) + ).to_request() + response = await client.asset.search(criteria=request) + sort_options = response._criteria.dsl.sort # type: ignore + assert response + assert len(sort_options) == 2 + assert sort_options[0].field == QUALIFIED_NAME + assert sort_options[1].field == ASSET_GUID + + +async def test_persona_search( + client: AsyncAtlanClient, + business_definitions_persona: Persona, + known_issues_purpose: Purpose, +): + request1 = ( + FluentSearch.select() + .aggregate("type", Asset.TYPE_NAME.bucket_by()) + .sort(Asset.CREATE_TIME.order()) + .page_size(0) # only interested in checking aggregation results + ).to_request() + + request2 = ( + FluentSearch.select() + .aggregate("type", Asset.TYPE_NAME.bucket_by()) + .sort(Asset.CREATE_TIME.order()) + .page_size(0) # only interested in checking aggregation results + ).to_request() + request2.persona = business_definitions_persona.qualified_name + + results_without_persona = await client.asset.search(request1) + results_with_persona = await client.asset.search(request2) + + # Make sure the results are different (total assets count != glossary assets count) + assert results_without_persona.count != results_with_persona.count + + +async def test_purpose_search(client: AsyncAtlanClient, known_issues_purpose: Purpose): + request1 = ( + FluentSearch.select() + .aggregate("type", Asset.TYPE_NAME.bucket_by()) + .sort(Asset.CREATE_TIME.order()) + .page_size(0) # only interested in checking aggregation results + ).to_request() + + request2 = ( + FluentSearch.select() + .aggregate("type", Asset.TYPE_NAME.bucket_by()) + .sort(Asset.CREATE_TIME.order()) + .page_size(0) # only interested in checking aggregation results + ).to_request() + request2.purpose = known_issues_purpose.qualified_name + + results_without_purpose = await client.asset.search(request1) + results_with_purpose = await client.asset.search(request2) + + # Make sure the results are different (total assets count != assets tagged with "Known issues" count) + assert results_without_purpose.count != results_with_purpose.count + + +async def test_read_timeout(client: AsyncAtlanClient): + request = (FluentSearch().select()).to_request() + async with client_connection( + client=client, read_timeout=0.1, retry=Retry(total=0) + ) as timed_client: + with pytest.raises(httpx.ReadTimeout): + await timed_client.asset.search(criteria=request) + + +async def test_connect_timeout(client: AsyncAtlanClient): + request = FluentSearch().select().to_request() + + # Use a non-routable IP that will definitely timeout + # 192.0.2.1 is reserved for documentation/testing + async with client_connection( + client=client, + base_url="http://192.0.2.1:80", # Non-routable test IP + connect_timeout=0.001, + retry=Retry(total=0), # No retries to get the raw ConnectTimeout + ) as timed_client: + with pytest.raises((httpx.ConnectTimeout)): + await timed_client.asset.search(criteria=request) diff --git a/tests_v9/integration/aio/test_lineage.py b/tests_v9/integration/aio/test_lineage.py new file mode 100644 index 000000000..b8e439134 --- /dev/null +++ b/tests_v9/integration/aio/test_lineage.py @@ -0,0 +1,739 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. +import asyncio +from typing import AsyncGenerator + +import pytest +import pytest_asyncio + +from pyatlan_v9.client.aio.atlan import AsyncAtlanClient +from pyatlan_v9.model.assets import ( + Asset, + Column, + ColumnProcess, + Connection, + Database, + MaterialisedView, + Process, + Schema, + Table, + View, +) +from pyatlan_v9.model.enums import ( + AtlanConnectorType, + CertificateStatus, + EntityStatus, + LineageDirection, +) +from pyatlan_v9.model.lineage import FluentLineage +from pyatlan_v9.model.search import DSL, Bool, IndexSearchRequest, Prefix, Term +from tests_v9.integration.aio.test_connection import create_connection_async +from tests_v9.integration.aio.utils import delete_asset_async +from tests_v9.integration.client import TestId + +MODULE_NAME = TestId.make_unique("lineage") + +DATABASE_NAME = f"{MODULE_NAME}_db" +SCHEMA_NAME = f"{MODULE_NAME}_schema" +TABLE_NAME = f"{MODULE_NAME}_tbl" +MVIEW_NAME = f"{MODULE_NAME}_mv" +VIEW_NAME = f"{MODULE_NAME}_v" +COLUMN_NAME1 = f"{MODULE_NAME}1" +COLUMN_NAME2 = f"{MODULE_NAME}2" +COLUMN_NAME3 = f"{MODULE_NAME}3" +COLUMN_NAME4 = f"{MODULE_NAME}4" +COLUMN_NAME5 = f"{MODULE_NAME}5" +COLUMN_NAME6 = f"{MODULE_NAME}6" + +CONNECTOR_TYPE = AtlanConnectorType.VERTICA +CERTIFICATE_STATUS = CertificateStatus.VERIFIED +CERTIFICATE_MESSAGE = "Automated testing of the Python SDK." + + +@pytest_asyncio.fixture(scope="module") +async def connection(client: AsyncAtlanClient) -> AsyncGenerator[Connection, None]: + result = await create_connection_async( + client=client, name=MODULE_NAME, connector_type=CONNECTOR_TYPE + ) + yield result + # TODO: proper connection delete workflow + await delete_asset_async(client, guid=result.guid, asset_type=Connection) + + +@pytest_asyncio.fixture(scope="module") +async def database( + client: AsyncAtlanClient, connection: Connection +) -> AsyncGenerator[Database, None]: + db = await create_database_async( + client=client, connection=connection, database_name=DATABASE_NAME + ) + yield db + await delete_asset_async(client, guid=db.guid, asset_type=Database) + + +async def create_database_async( + client: AsyncAtlanClient, connection, database_name: str +): + to_create = Database.creator( + name=database_name, connection_qualified_name=connection.qualified_name + ) + to_create.certificate_status = CERTIFICATE_STATUS + to_create.certificate_status_message = CERTIFICATE_MESSAGE + result = await client.asset.save(to_create) + return result.assets_created(asset_type=Database)[0] + + +@pytest_asyncio.fixture(scope="module") +async def schema( + client: AsyncAtlanClient, + connection: Connection, + database: Database, +) -> AsyncGenerator[Schema, None]: + assert database.qualified_name + to_create = Schema.creator( + name=SCHEMA_NAME, database_qualified_name=database.qualified_name + ) + result = await client.asset.save(to_create) + sch = result.assets_created(asset_type=Schema)[0] + yield sch + await delete_asset_async(client, guid=sch.guid, asset_type=Schema) + + +@pytest_asyncio.fixture(scope="module") +async def table( + client: AsyncAtlanClient, + connection: Connection, + database: Database, + schema: Schema, +) -> AsyncGenerator[Table, None]: + assert schema.qualified_name + to_create = Table.creator( + name=TABLE_NAME, schema_qualified_name=schema.qualified_name + ) + result = await client.asset.save(to_create) + tbl = result.assets_created(asset_type=Table)[0] + yield tbl + await delete_asset_async(client, guid=tbl.guid, asset_type=Table) + + +@pytest_asyncio.fixture(scope="module") +async def mview( + client: AsyncAtlanClient, + connection: Connection, + database: Database, + schema: Schema, +) -> AsyncGenerator[MaterialisedView, None]: + assert schema.qualified_name + to_create = MaterialisedView.creator( + name=MVIEW_NAME, schema_qualified_name=schema.qualified_name + ) + result = await client.asset.save(to_create) + mv = result.assets_created(asset_type=MaterialisedView)[0] + yield mv + await delete_asset_async(client, guid=mv.guid, asset_type=MaterialisedView) + + +@pytest_asyncio.fixture(scope="module") +async def view( + client: AsyncAtlanClient, + connection: Connection, + database: Database, + schema: Schema, +) -> AsyncGenerator[View, None]: + assert schema.qualified_name + to_create = View.creator( + name=VIEW_NAME, schema_qualified_name=schema.qualified_name + ) + result = await client.asset.save(to_create) + v = result.assets_created(asset_type=View)[0] + yield v + await delete_asset_async(client, guid=v.guid, asset_type=View) + + +@pytest_asyncio.fixture(scope="module") +async def column1( + client: AsyncAtlanClient, + connection: Connection, + database: Database, + schema: Schema, + table: Table, +) -> AsyncGenerator[Column, None]: + assert table.qualified_name + to_create = Column.creator( + name=COLUMN_NAME1, + parent_type=Table, + parent_qualified_name=table.qualified_name, + order=1, + ) + result = await client.asset.save(to_create) + c = result.assets_created(asset_type=Column)[0] + yield c + await delete_asset_async(client, guid=c.guid, asset_type=Column) + + +@pytest_asyncio.fixture(scope="module") +async def column2( + client: AsyncAtlanClient, + connection: Connection, + database: Database, + schema: Schema, + table: Table, +) -> AsyncGenerator[Column, None]: + assert table.qualified_name + to_create = Column.creator( + name=COLUMN_NAME2, + parent_type=Table, + parent_qualified_name=table.qualified_name, + order=2, + ) + result = await client.asset.save(to_create) + c = result.assets_created(asset_type=Column)[0] + yield c + await delete_asset_async(client, guid=c.guid, asset_type=Column) + + +@pytest_asyncio.fixture(scope="module") +async def column3( + client: AsyncAtlanClient, + connection: Connection, + database: Database, + schema: Schema, + mview: MaterialisedView, +) -> AsyncGenerator[Column, None]: + assert mview.qualified_name + to_create = Column.creator( + name=COLUMN_NAME3, + parent_type=MaterialisedView, + parent_qualified_name=mview.qualified_name, + order=1, + ) + result = await client.asset.save(to_create) + c = result.assets_created(asset_type=Column)[0] + yield c + await delete_asset_async(client, guid=c.guid, asset_type=Column) + + +@pytest_asyncio.fixture(scope="module") +async def column4( + client: AsyncAtlanClient, + connection: Connection, + database: Database, + schema: Schema, + mview: MaterialisedView, +) -> AsyncGenerator[Column, None]: + assert mview.qualified_name + to_create = Column.creator( + name=COLUMN_NAME4, + parent_type=MaterialisedView, + parent_qualified_name=mview.qualified_name, + order=2, + ) + result = await client.asset.save(to_create) + c = result.assets_created(asset_type=Column)[0] + yield c + await delete_asset_async(client, guid=c.guid, asset_type=Column) + + +@pytest_asyncio.fixture(scope="module") +async def column5( + client: AsyncAtlanClient, + connection: Connection, + database: Database, + schema: Schema, + view: View, +) -> AsyncGenerator[Column, None]: + assert view.qualified_name + to_create = Column.creator( + name=COLUMN_NAME5, + parent_type=View, + parent_qualified_name=view.qualified_name, + order=1, + ) + result = await client.asset.save(to_create) + c = result.assets_created(asset_type=Column)[0] + yield c + await delete_asset_async(client, guid=c.guid, asset_type=Column) + + +@pytest_asyncio.fixture(scope="module") +async def column6( + client: AsyncAtlanClient, + connection: Connection, + database: Database, + schema: Schema, + view: View, +) -> AsyncGenerator[Column, None]: + assert view.qualified_name + to_create = Column.creator( + name=COLUMN_NAME6, + parent_type=View, + parent_qualified_name=view.qualified_name, + order=2, + ) + result = await client.asset.save(to_create) + c = result.assets_created(asset_type=Column)[0] + yield c + await delete_asset_async(client, guid=c.guid, asset_type=Column) + + +@pytest_asyncio.fixture(scope="module") +async def lineage_start( + client: AsyncAtlanClient, + connection: Connection, + table: Table, + mview: MaterialisedView, +) -> AsyncGenerator[Process, None]: + process_name = f"{table.name} >> {mview.name}" + assert connection.qualified_name + to_create = Process.creator( + name=process_name, + connection_qualified_name=connection.qualified_name, + inputs=[Table.ref_by_guid(table.guid)], + outputs=[MaterialisedView.ref_by_guid(mview.guid)], + ) + response = await client.asset.save(to_create) + lineage = response.assets_created(asset_type=Process)[0] + yield lineage + await delete_asset_async(client, guid=lineage.guid, asset_type=Process) + + +@pytest_asyncio.fixture(scope="module") +async def cp_lineage_start( + client: AsyncAtlanClient, + connection: Connection, + column1: Column, + column3: Column, + lineage_start: Process, +) -> AsyncGenerator[ColumnProcess, None]: + col_process_name = f"{column1.name} >> {column3.name}" + assert connection.qualified_name + to_create = ColumnProcess.creator( + name=col_process_name, + connection_qualified_name=connection.qualified_name, + inputs=[Column.ref_by_guid(column1.guid)], + outputs=[Column.ref_by_guid(column3.guid)], + parent=Process.ref_by_guid(lineage_start.guid), + ) + try: + response = await client.asset.save(to_create) + cp_ls = response.assets_created(asset_type=ColumnProcess)[0] + assert len(response.assets_updated(asset_type=Process)) == 1 + + yield cp_ls + finally: + await delete_asset_async(client, guid=cp_ls.guid, asset_type=ColumnProcess) + + +@pytest_asyncio.fixture(scope="module") +async def lineage_end( + client: AsyncAtlanClient, + connection: Connection, + mview: MaterialisedView, + view: View, +) -> AsyncGenerator[Process, None]: + process_name = f"{mview.name} >> {view.name}" + assert connection.qualified_name + to_create = Process.creator( + name=process_name, + connection_qualified_name=connection.qualified_name, + inputs=[MaterialisedView.ref_by_guid(mview.guid)], + outputs=[View.ref_by_guid(view.guid)], + ) + response = await client.asset.save(to_create) + lineage = response.assets_created(asset_type=Process)[0] + yield lineage + await delete_asset_async(client, guid=lineage.guid, asset_type=Process) + + +@pytest_asyncio.fixture(scope="module") +async def cp_lineage_end( + client: AsyncAtlanClient, + connection: Connection, + column3: Column, + column5: Column, + lineage_end: Process, +) -> AsyncGenerator[ColumnProcess, None]: + col_process_name = f"{column3.name} >> {column5.name}" + assert connection.qualified_name + to_create = ColumnProcess.creator( + name=col_process_name, + connection_qualified_name=connection.qualified_name, + inputs=[Column.ref_by_guid(column3.guid)], + outputs=[Column.ref_by_guid(column5.guid)], + parent=Process.ref_by_guid(lineage_end.guid), + ) + try: + response = await client.asset.save(to_create) + cp_le = response.assets_created(asset_type=ColumnProcess)[0] + assert len(response.assets_updated(asset_type=Process)) == 1 + assert response.assets_updated(asset_type=Process)[0].guid == lineage_end.guid + yield cp_le + finally: + await delete_asset_async(client, guid=cp_le.guid, asset_type=ColumnProcess) + + +def _assert_lineage(asset_1, asset_2, lineage): + assert lineage + assert lineage.guid + assert lineage.qualified_name + assert lineage.name == f"{asset_1.name} >> {asset_2.name}" + assert lineage.inputs + assert len(lineage.inputs) == 1 + assert lineage.inputs[0] + assert lineage.inputs[0].type_name == asset_1.__class__.__name__ + assert lineage.inputs[0].guid == asset_1.guid + assert lineage.outputs + assert len(lineage.outputs) == 1 + assert lineage.outputs[0] + assert lineage.outputs[0].type_name == asset_2.__class__.__name__ + assert lineage.outputs[0].guid == asset_2.guid + + +async def test_lineage_start( + client: AsyncAtlanClient, + connection: Connection, + database: Database, + schema: Schema, + table: Table, + mview: MaterialisedView, + view: View, + lineage_start: Process, +): + _assert_lineage(table, mview, lineage_start) + + +async def test_cp_lineage_start( + column1: Column, + column3: Column, + cp_lineage_start: ColumnProcess, +): + _assert_lineage(column1, column3, cp_lineage_start) + + +async def test_lineage_end( + client: AsyncAtlanClient, + connection: Connection, + database: Database, + schema: Schema, + table: Table, + mview: MaterialisedView, + view: View, + lineage_end: Process, +): + _assert_lineage(mview, view, lineage_end) + + +async def test_cp_lineage_end( + column3: Column, + column5: Column, + cp_lineage_end: ColumnProcess, +): + _assert_lineage(column3, column5, cp_lineage_end) + + +async def test_fetch_lineage_start_list( + client: AsyncAtlanClient, + connection: Connection, + database: Database, + schema: Schema, + table: Table, + mview: MaterialisedView, + view: View, + lineage_start: Process, + lineage_end: Process, +): + lineage = FluentLineage( + starting_guid=table.guid, includes_on_results=Asset.NAME, size=1 + ).request + response = await client.asset.get_lineage_list(lineage) + assert response + results = [] + async for a in response: + results.append(a) + assert len(results) == 4 + assert isinstance(results[0], Process) + assert results[0].depth == 1 + assert isinstance(results[1], MaterialisedView) + assert results[1].depth == 1 + assert results[1].guid == mview.guid + assert isinstance(results[2], Process) + assert results[2].depth == 2 + assert isinstance(results[3], View) + assert results[3].depth == 2 + assert results[3].guid == view.guid + lineage = FluentLineage( + starting_guid=table.guid, direction=LineageDirection.UPSTREAM + ).request + response = await client.asset.get_lineage_list(lineage) + assert response + assert not response.has_more + + +async def test_fetch_lineage_start_list_detailed( + client: AsyncAtlanClient, + connection: Connection, + database: Database, + schema: Schema, + table: Table, + mview: MaterialisedView, + view: View, + column1: Column, + column2: Column, + column3: Column, + column4: Column, + column5: Column, + column6: Column, + lineage_start: Process, + lineage_end: Process, + cp_lineage_start: ColumnProcess, + cp_lineage_end: ColumnProcess, +): + lineage = FluentLineage( + starting_guid=table.guid, + includes_on_results=Asset.NAME, + immediate_neighbors=True, + ).request + response = await client.asset.get_lineage_list(lineage) + assert response + results = [] + async for a in response: + results.append(a) + assert len(results) == 5 + assert isinstance(results[0], Table) + assert results[0].depth == 0 + assert results[0].guid == table.guid + assert not results[0].immediate_upstream + assert results[0].immediate_downstream and len(results[0].immediate_downstream) == 1 + assert results[0].immediate_downstream[0].guid == mview.guid + assert isinstance(results[1], Process) + assert results[1].depth == 1 + assert results[1].immediate_upstream == [] + assert results[1].immediate_downstream and len(results[1].immediate_downstream) == 1 + assert results[1].immediate_downstream[0].guid == lineage_end.guid + assert isinstance(results[2], MaterialisedView) + assert results[2].depth == 1 + assert results[2].guid == mview.guid + assert results[2].immediate_upstream and len(results[2].immediate_upstream) == 1 + assert results[2].immediate_upstream[0].guid == table.guid + assert results[2].immediate_downstream and len(results[2].immediate_downstream) == 1 + assert results[2].immediate_downstream[0].guid == view.guid + assert isinstance(results[3], Process) + assert results[3].depth == 2 + assert results[3].immediate_upstream and len(results[3].immediate_upstream) == 1 + assert results[3].immediate_upstream[0].guid == lineage_start.guid + assert results[3].immediate_downstream == [] + assert isinstance(results[4], View) + assert results[4].depth == 2 + assert results[4].guid == view.guid + assert results[4].immediate_upstream and len(results[4].immediate_upstream) == 1 + assert results[4].immediate_upstream[0].guid == mview.guid + assert not results[4].immediate_downstream + + +async def test_fetch_lineage_middle_list( + client: AsyncAtlanClient, + connection: Connection, + database: Database, + schema: Schema, + table: Table, + mview: MaterialisedView, + view: View, + lineage_start: Process, + lineage_end: Process, +): + lineage = FluentLineage( + starting_guid=mview.guid, includes_on_results=Asset.NAME, size=5 + ).request + response = await client.asset.get_lineage_list(lineage) + assert response + results = [] + async for a in response: + results.append(a) + assert len(results) == 2 + assert isinstance(results[0], Process) + assert isinstance(results[1], View) + assert results[1].guid == view.guid + lineage = FluentLineage( + starting_guid=mview.guid, direction=LineageDirection.UPSTREAM, size=5 + ).request + response = await client.asset.get_lineage_list(lineage) + assert response + results = [] + async for a in response: + results.append(a) + assert len(results) == 2 + + assert isinstance(results[1], Table) + assert results[1].guid == table.guid + + +async def test_fetch_lineage_end_list( + client: AsyncAtlanClient, + connection: Connection, + database: Database, + schema: Schema, + table: Table, + mview: MaterialisedView, + view: View, + lineage_start: Process, + lineage_end: Process, +): + lineage = FluentLineage( + starting_guid=view.guid, includes_on_results=Asset.NAME, size=10 + ).request + response = await client.asset.get_lineage_list(lineage) + assert response + assert not response.has_more + lineage = FluentLineage( + starting_guid=view.guid, direction=LineageDirection.UPSTREAM + ).request + response = await client.asset.get_lineage_list(lineage) + assert response + results = [] + async for a in response: + results.append(a) + assert len(results) == 4 + assert isinstance(results[0], Process) + assert isinstance(results[1], MaterialisedView) + assert isinstance(results[2], Process) + assert isinstance(results[3], Table) + one = results[3] + assert one.guid == table.guid + + +async def test_search_by_lineage( + client: AsyncAtlanClient, + connection: Connection, + database: Database, + schema: Schema, + table: Table, + mview: MaterialisedView, + view: View, + lineage_start: Process, + lineage_end: Process, +): + be_active = Term.with_state("ACTIVE") + have_lineage = Term.with_has_lineage(True) + be_a_sql_type = Term.with_super_type_names("SQL") + assert connection.qualified_name + with_qn_prefix = Prefix.with_qualified_name(connection.qualified_name) + query = Bool(must=[be_active, have_lineage, be_a_sql_type, with_qn_prefix]) + dsl = DSL(query=query) + index = IndexSearchRequest( + dsl=dsl, + attributes=["name", "__hasLineage"], + ) + response = await client.asset.search(index) + assert response + count = 0 + # TODO: replace with exponential back-off and jitter + while response.count < 3 and count < 10: + await asyncio.sleep(2) + response = await client.asset.search(index) + count += 1 + assert response + assert response.count == 6 + assets = [] + asset_types = [] + async for t in response: + assets.append(t) + asset_types.append(t.type_name) + assert t.has_lineage + assert len(assets) == 6 + assert "Table" in asset_types + assert "MaterialisedView" in asset_types + assert "View" in asset_types + assert "Column" in asset_types + + +@pytest.mark.order( + after=[ + "test_lineage_start", + "test_lineage_end", + "test_fetch_lineage_start_list", + "test_fetch_lineage_middle_list", + "test_fetch_lineage_end_list", + "test_search_by_lineage", + ] +) +async def test_delete_lineage( + client: AsyncAtlanClient, + connection: Connection, + database: Database, + schema: Schema, + table: Table, + mview: MaterialisedView, + view: View, + lineage_start: Process, + lineage_end: Process, +): + response = await client.asset.delete_by_guid(lineage_start.guid) + assert response + deleted = response.assets_deleted(asset_type=Process) + assert len(deleted) == 1 + one = deleted[0] + assert one + assert isinstance(one, Process) + assert one.guid == lineage_start.guid + assert one.qualified_name == lineage_start.qualified_name + assert one.status == EntityStatus.DELETED + + +@pytest.mark.order(after="test_delete_lineage") +async def test_restore_lineage( + client: AsyncAtlanClient, + connection: Connection, + database: Database, + schema: Schema, + table: Table, + mview: MaterialisedView, + view: View, + lineage_start: Process, + lineage_end: Process, +): + assert lineage_start.qualified_name + assert lineage_start.name + to_restore = Process.create_for_modification( + lineage_start.qualified_name, lineage_start.name + ) + to_restore.status = EntityStatus.ACTIVE + await client.asset.save(to_restore) + restored = await client.asset.get_by_guid( + lineage_start.guid, asset_type=Process, ignore_relationships=False + ) + assert restored + count = 0 + # TODO: replace with exponential back-off and jitter + while restored.status == EntityStatus.DELETED: + await asyncio.sleep(2) + restored = await client.asset.get_by_guid( + lineage_start.guid, asset_type=Process, ignore_relationships=False + ) + count += 1 + assert restored.guid == lineage_start.guid + assert restored.qualified_name == lineage_start.qualified_name + assert restored.status == EntityStatus.ACTIVE + + +@pytest.mark.order(after="test_restore_lineage") +async def test_purge_lineage( + client: AsyncAtlanClient, + connection: Connection, + database: Database, + schema: Schema, + table: Table, + mview: MaterialisedView, + view: View, + lineage_start: Process, + lineage_end: Process, +): + response = await client.asset.purge_by_guid(lineage_start.guid) + assert response + purged = response.assets_deleted(asset_type=Process) + assert len(purged) == 1 + one = purged[0] + assert one + assert isinstance(one, Process) + assert one.guid == lineage_start.guid + assert one.qualified_name == lineage_start.qualified_name + assert one.status == EntityStatus.DELETED diff --git a/tests_v9/integration/aio/test_oauth_client.py b/tests_v9/integration/aio/test_oauth_client.py new file mode 100644 index 000000000..557bfdcc5 --- /dev/null +++ b/tests_v9/integration/aio/test_oauth_client.py @@ -0,0 +1,261 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2026 Atlan Pte. Ltd. +"""Async integration tests for OAuth client CRUD operations.""" + +import asyncio +import time +from typing import AsyncGenerator, List, Optional + +import pytest +import pytest_asyncio + +from pyatlan_v9.client.aio.atlan import AsyncAtlanClient +from pyatlan_v9.errors import NotFoundError +from pyatlan_v9.model.oauth_client import OAuthClientCreateResponse, OAuthClientResponse +from tests_v9.integration.client import TestId + +MODULE_NAME = TestId.make_unique("AsyncOAuthClient") + +# Test data +OAUTH_CLIENT_NAME = f"{MODULE_NAME}_test_client" +OAUTH_CLIENT_DESCRIPTION = "Async integration test OAuth client" +OAUTH_CLIENT_DESCRIPTION_UPDATED = "Updated async integration test OAuth client" +OAUTH_CLIENT_ROLE = "Admin" # Role description +DATA_ASSETS_PERSONA_NAME = "Data Assets" # Pre-existing persona + +# Pagination test constants +PAGINATION_CLIENT_COUNT = 5 +PAGINATION_CLIENT_NAME_PREFIX = f"{MODULE_NAME}_pagination_client" + + +async def delete_oauth_client_async(client: AsyncAtlanClient, client_id: str) -> None: + """Helper to delete an OAuth client.""" + await client.oauth_client.purge(client_id) + + +@pytest_asyncio.fixture(scope="module") +async def persona_qualified_name(client: AsyncAtlanClient) -> str: + """ + Fixture to retrieve the qualified name of the pre-existing 'Data Assets' persona. + """ + personas = await client.asset.find_personas_by_name(DATA_ASSETS_PERSONA_NAME) + assert len(personas) >= 1, f"Persona '{DATA_ASSETS_PERSONA_NAME}' not found" + persona_qn = personas[0].qualified_name + assert persona_qn is not None + return persona_qn + + +@pytest_asyncio.fixture(scope="module") +async def pagination_oauth_clients( + client: AsyncAtlanClient, +) -> AsyncGenerator[List[OAuthClientCreateResponse], None]: + """ + Fixture to create multiple OAuth clients for pagination testing. + Creates 5 OAuth clients and yields their responses. + Cleans up by deleting all created OAuth clients after tests. + """ + created_clients: List[OAuthClientCreateResponse] = [] + + # Create 5 OAuth clients for pagination testing + for i in range(PAGINATION_CLIENT_COUNT): + response = await client.oauth_client.creator( + name=f"{PAGINATION_CLIENT_NAME_PREFIX}_{i}", + role=OAUTH_CLIENT_ROLE, + description=f"Pagination test OAuth client {i}", + ) + assert response is not None + assert response.client_id is not None + created_clients.append(response) + # Small delay to ensure distinct createdAt timestamps for sorting + await asyncio.sleep(0.5) + + yield created_clients + + # Cleanup: delete all created OAuth clients + for oauth_client in created_clients: + if oauth_client.client_id: + try: + await delete_oauth_client_async(client, oauth_client.client_id) + except Exception: + pass # Ignore cleanup errors + + +@pytest_asyncio.fixture(scope="module") +async def oauth_client_response( + client: AsyncAtlanClient, + persona_qualified_name: str, +) -> AsyncGenerator[OAuthClientCreateResponse, None]: + """ + Fixture to create an OAuth client for testing with persona association. + Yields the create response (which includes client_secret). + Cleans up by deleting the OAuth client after tests. + """ + # Create OAuth client with persona + response = await client.oauth_client.creator( + name=OAUTH_CLIENT_NAME, + role=OAUTH_CLIENT_ROLE, + description=OAUTH_CLIENT_DESCRIPTION, + persona_qualified_names=[persona_qualified_name], + ) + assert response is not None + assert response.client_id is not None + assert response.client_secret is not None + + yield response + + # Cleanup + if response.client_id: + await delete_oauth_client_async(client, response.client_id) + + +def _assert_oauth_client_create_response(response: OAuthClientCreateResponse): + """Assert the OAuth client create response has expected values.""" + assert response is not None + assert response.id is not None + assert response.client_id is not None + assert response.client_id.startswith("oauth-client-") + assert response.client_secret is not None + assert response.display_name == OAUTH_CLIENT_NAME + assert response.description == OAUTH_CLIENT_DESCRIPTION + assert response.created_by is not None + assert response.created_at is not None + assert response.token_expiry_seconds is not None + + +def _assert_oauth_client( + oauth_client: OAuthClientResponse, + persona_qn: Optional[str] = None, + is_updated: bool = False, +): + """Assert the OAuth client has expected values.""" + assert oauth_client is not None + assert oauth_client.id is not None + assert oauth_client.client_id is not None + assert oauth_client.client_id.startswith("oauth-client-") + assert oauth_client.display_name == OAUTH_CLIENT_NAME + if is_updated: + assert oauth_client.description == OAUTH_CLIENT_DESCRIPTION_UPDATED + else: + assert oauth_client.description == OAUTH_CLIENT_DESCRIPTION + # Validate persona association if provided + if persona_qn: + assert oauth_client.persona_qualified_names is not None + assert persona_qn in oauth_client.persona_qualified_names + + +async def test_oauth_client_create( + client: AsyncAtlanClient, + oauth_client_response: OAuthClientCreateResponse, +): + """Test creating an OAuth client.""" + _assert_oauth_client_create_response(oauth_client_response) + + +@pytest.mark.order(after="test_oauth_client_create") +async def test_oauth_client_get_by_id( + client: AsyncAtlanClient, + oauth_client_response: OAuthClientCreateResponse, + persona_qualified_name: str, +): + """Test retrieving an OAuth client by ID and validate persona association.""" + assert oauth_client_response.client_id is not None + time.sleep(2) # Allow time for eventual consistency + + oauth_client = await client.oauth_client.get_by_id(oauth_client_response.client_id) + _assert_oauth_client(oauth_client, persona_qn=persona_qualified_name) + + +@pytest.mark.order(after="test_oauth_client_get_by_id") +async def test_oauth_client_get_with_pagination( + client: AsyncAtlanClient, + pagination_oauth_clients: List[OAuthClientCreateResponse], +): + """Test retrieving OAuth clients with pagination and async iteration. + + This test creates 5 OAuth clients and uses limit=1 to ensure + the pagination logic is properly exercised across multiple API calls. + """ + # Verify we have the expected number of test clients + assert len(pagination_oauth_clients) == PAGINATION_CLIENT_COUNT + + # Get the client IDs we created for verification + created_client_ids = {c.client_id for c in pagination_oauth_clients} + + # Use limit=1 to force multiple API calls for pagination + response = await client.oauth_client.get(limit=1, offset=0, sort="createdAt") + assert response is not None + assert response.total_record is not None + # Should have at least our 5 created clients + assert response.total_record >= PAGINATION_CLIENT_COUNT + + # Store the initial total record count + initial_total = response.total_record + + # Test async iteration over the paginated response + # This should make multiple API calls (one per page with limit=1) + found_client_ids: set = set() + total_iterated = 0 + + async for oauth_client in response: + total_iterated += 1 + if oauth_client.client_id in created_client_ids: + found_client_ids.add(oauth_client.client_id) + + # Verify we iterated through all records + assert total_iterated == initial_total, ( + f"Expected to iterate through {initial_total} records, " + f"but only iterated through {total_iterated}" + ) + + # Verify we found all our created clients + assert found_client_ids == created_client_ids, ( + f"Expected to find all {PAGINATION_CLIENT_COUNT} created clients. " + f"Found: {len(found_client_ids)}, Missing: {created_client_ids - found_client_ids}" + ) + + +@pytest.mark.order(after="test_oauth_client_get_with_pagination") +async def test_oauth_client_update_description( + client: AsyncAtlanClient, + oauth_client_response: OAuthClientCreateResponse, + persona_qualified_name: str, +): + """Test updating an OAuth client's description.""" + assert oauth_client_response.client_id is not None + time.sleep(2) + + updated = await client.oauth_client.updater( + client_id=oauth_client_response.client_id, + description=OAUTH_CLIENT_DESCRIPTION_UPDATED, + ) + _assert_oauth_client(updated, persona_qn=persona_qualified_name, is_updated=True) + + +@pytest.mark.order(after="test_oauth_client_update_description") +async def test_oauth_client_verify_update_persisted( + client: AsyncAtlanClient, + oauth_client_response: OAuthClientCreateResponse, + persona_qualified_name: str, +): + """Verify that the update was persisted and persona association is maintained.""" + assert oauth_client_response.client_id is not None + time.sleep(2) + + oauth_client = await client.oauth_client.get_by_id(oauth_client_response.client_id) + _assert_oauth_client( + oauth_client, persona_qn=persona_qualified_name, is_updated=True + ) + + +async def test_oauth_client_create_with_invalid_role_raises_error( + client: AsyncAtlanClient, +): + """Test that creating an OAuth client with an invalid role raises an error.""" + + with pytest.raises(NotFoundError) as exc_info: + await client.oauth_client.creator( + name="test-invalid-role", + role="InvalidRole", + ) + assert "does not exist" in str(exc_info.value) + assert "Available roles:" in str(exc_info.value) diff --git a/tests_v9/integration/aio/test_open_lineage.py b/tests_v9/integration/aio/test_open_lineage.py new file mode 100644 index 000000000..81924974a --- /dev/null +++ b/tests_v9/integration/aio/test_open_lineage.py @@ -0,0 +1,83 @@ +from typing import AsyncGenerator + +import pytest_asyncio + +from pyatlan_v9.client.aio.atlan import AsyncAtlanClient +from pyatlan_v9.model.assets import Connection +from pyatlan_v9.model.enums import OpenLineageEventType +from pyatlan_v9.model.open_lineage.event import OpenLineageEvent +from pyatlan_v9.model.open_lineage.job import OpenLineageJob +from pyatlan_v9.model.open_lineage.run import OpenLineageRun +from tests_v9.integration.aio.utils import delete_asset_async +from tests_v9.integration.client import TestId + +MODULE_NAME = TestId.make_unique("AsyncOpenLineage") + + +@pytest_asyncio.fixture(scope="module") +async def connection(client: AsyncAtlanClient) -> AsyncGenerator[Connection, None]: + admin_role_guid = str(await client.role_cache.get_id_for_name("$admin")) + assert admin_role_guid + response = await client.open_lineage.create_connection( + name=MODULE_NAME, admin_roles=[admin_role_guid] + ) + result = response.assets_created(asset_type=Connection)[0] + yield await client.asset.get_by_guid( + result.guid, asset_type=Connection, ignore_relationships=False + ) + await delete_asset_async(client=client, guid=result.guid, asset_type=Connection) + + +async def test_open_lineage_integration( + connection: Connection, client: AsyncAtlanClient +): + assert connection is not None + assert connection.name == MODULE_NAME + + namespace = "snowflake://abc123.snowflakecomputing.com" + producer = "https://your.orchestrator/unique/id/123" + job = OpenLineageJob.creator( + connection_name=MODULE_NAME, job_name="dag_123", producer=producer + ) + run = OpenLineageRun.creator(job=job) + id = job.create_input(namespace=namespace, asset_name="OPS.DEFAULT.RUN_STATS") + od = job.create_output(namespace=namespace, asset_name="OPS.DEFAULT.FULL_STATS") + od.to_fields = [ + { + "COLUMN": [ + id.from_field(field_name="COLUMN"), + id.from_field(field_name="ONE"), + id.from_field(field_name="TWO"), + ] + }, + { + "ANOTHER": [ + id.from_field(field_name="THREE"), + ] + }, + ] + start = OpenLineageEvent.creator(run=run, event_type=OpenLineageEventType.START) + start.inputs = [ + id, + job.create_input(namespace=namespace, asset_name="SOME.OTHER.TBL"), + job.create_input(namespace=namespace, asset_name="AN.OTHER.TBL"), + ] + start.outputs = [ + od, + job.create_output(namespace=namespace, asset_name="AN.OTHER.VIEW"), + ] + await start.emit_async(client=client) + + complete = OpenLineageEvent.creator( + run=run, event_type=OpenLineageEventType.COMPLETE + ) + complete.inputs = [ + id, + job.create_input(namespace=namespace, asset_name="SOME.OTHER.TBL"), + job.create_input(namespace=namespace, asset_name="AN.OTHER.TBL"), + ] + complete.outputs = [ + od, + job.create_output(namespace=namespace, asset_name="AN.OTHER.VIEW"), + ] + await complete.emit_async(client=client) diff --git a/tests_v9/integration/aio/test_requests.py b/tests_v9/integration/aio/test_requests.py new file mode 100644 index 000000000..720ab7fa1 --- /dev/null +++ b/tests_v9/integration/aio/test_requests.py @@ -0,0 +1,48 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +"""Async requests integration tests.""" + +from typing import AsyncGenerator + +import pytest_asyncio + +from pyatlan_v9.client.aio.atlan import AsyncAtlanClient +from pyatlan_v9.model.api_tokens import ApiToken +from tests_v9.integration.aio.utils import create_token_async, delete_token_async +from tests_v9.integration.client import TestId + +MODULE_NAME = TestId.make_unique("AsyncRequests") +API_TOKEN_NAME = f"{MODULE_NAME}" + + +@pytest_asyncio.fixture(scope="module") +async def token(client: AsyncAtlanClient) -> AsyncGenerator[ApiToken, None]: + token = None + try: + token = await create_token_async(client, API_TOKEN_NAME) + yield token + finally: + await delete_token_async(client, token) + + +async def test_create_token(client: AsyncAtlanClient, token: ApiToken): + assert token + r = await client.token.get_by_name(API_TOKEN_NAME) + assert r + assert r.display_name == API_TOKEN_NAME + r = await client.token.get_by_id(str(token.client_id)) + assert r + assert r.client_id == token.client_id + assert r.display_name == token.display_name + + +async def test_update_token(client: AsyncAtlanClient, token: ApiToken): + description = "Now with a revised description." + revised = await client.token.updater( + str(token.guid), str(token.display_name), description + ) + assert revised + assert revised.attributes + assert revised.attributes.description == description + assert revised.display_name == token.display_name diff --git a/tests_v9/integration/aio/test_sso_client.py b/tests_v9/integration/aio/test_sso_client.py new file mode 100644 index 000000000..08839f2c8 --- /dev/null +++ b/tests_v9/integration/aio/test_sso_client.py @@ -0,0 +1,202 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. +import time +from typing import AsyncGenerator + +import pytest +import pytest_asyncio + +from pyatlan.client.common.sso import ( + GROUP_MAPPER_ATTRIBUTE, + GROUP_MAPPER_SYNC_MODE, + IDP_GROUP_MAPPER, +) +from pyatlan_v9.client.aio.atlan import AsyncAtlanClient +from pyatlan_v9.errors import InvalidRequestError +from pyatlan_v9.model.enums import AtlanSSO +from pyatlan_v9.model.group import AtlanGroup +from pyatlan_v9.model.sso import SSOMapper +from tests_v9.integration.client import TestId + +FIXED_USER = "aryaman" +MODULE_NAME = TestId.make_unique("AsyncSSOClient") + +GROUP_NAME = MODULE_NAME +SSO_GROUP_NAME = "test-sso-group" +SSO_GROUP_NAME_UPDATED = "test-sso-group-updated" + + +async def delete_group_async(client: AsyncAtlanClient, guid: str) -> None: + await client.group.purge(guid) + + +async def delete_sso_mapping_async(client: AsyncAtlanClient, group_map_id: str): + await client.sso.delete_group_mapping( + sso_alias=AtlanSSO.JUMPCLOUD, group_map_id=group_map_id + ) + + +@pytest_asyncio.fixture(scope="module") +async def group(client: AsyncAtlanClient) -> AsyncGenerator[AtlanGroup, None]: + to_create = AtlanGroup.creator(GROUP_NAME) + fixed_user = await client.user.get_by_username(FIXED_USER) + assert fixed_user + g = await client.group.creator(group=to_create, user_ids=[str(fixed_user.id)]) + groups = await client.group.get_by_name(alias=GROUP_NAME) + assert groups + assert groups.records is not None + assert len(groups.records) == 1 + yield groups.records[0] + assert g.group + await delete_group_async(client, g.group) + + +@pytest_asyncio.fixture(scope="module") +async def sso_mapping( + client: AsyncAtlanClient, + group: AtlanGroup, +) -> AsyncGenerator[SSOMapper, None]: + assert group + assert group.id + response = await client.sso.create_group_mapping( + sso_alias=AtlanSSO.JUMPCLOUD, atlan_group=group, sso_group_name=SSO_GROUP_NAME + ) + assert response + + azure_group_mapping = None + sso_mappings = await client.sso.get_all_group_mappings(sso_alias=AtlanSSO.JUMPCLOUD) + for mapping in sso_mappings: + if ( + group.id + and group.id in str(mapping.name) + and mapping.identity_provider_mapper == IDP_GROUP_MAPPER + ): + azure_group_mapping = mapping + break + assert azure_group_mapping and azure_group_mapping.id + yield azure_group_mapping + await delete_sso_mapping_async(client, azure_group_mapping.id) + + +def _assert_sso_group_mapping( + group: AtlanGroup, sso_mapping: SSOMapper, is_updated: bool = False +): + assert sso_mapping + assert sso_mapping.id + assert sso_mapping.identity_provider_alias == AtlanSSO.JUMPCLOUD + assert sso_mapping.identity_provider_mapper == IDP_GROUP_MAPPER + assert sso_mapping.config.attributes == "[]" + assert sso_mapping.config.group_name == group.name + assert sso_mapping.config.attribute_values_regex is None + assert sso_mapping.config.attribute_friendly_name is None + + assert sso_mapping.config.sync_mode == GROUP_MAPPER_SYNC_MODE + assert sso_mapping.config.attribute_name == GROUP_MAPPER_ATTRIBUTE + if is_updated: + assert sso_mapping.name + assert sso_mapping.config.attribute_value == SSO_GROUP_NAME_UPDATED + else: + assert sso_mapping.name + assert group.id and (group.id in str(sso_mapping.name)) + assert sso_mapping.config.attribute_value == SSO_GROUP_NAME + + +async def test_sso_create_group_mapping( + client: AsyncAtlanClient, + group: AtlanGroup, + sso_mapping: SSOMapper, +): + assert group + assert sso_mapping + _assert_sso_group_mapping(group, sso_mapping) + + +@pytest.mark.order(after="test_sso_create_group_mapping") +async def test_sso_create_group_mapping_again_raises_invalid_request_error( + client: AsyncAtlanClient, + group: AtlanGroup, + sso_mapping: SSOMapper, +): + assert group + assert sso_mapping + with pytest.raises(InvalidRequestError) as err: + await client.sso.create_group_mapping( + sso_alias=AtlanSSO.JUMPCLOUD, + atlan_group=group, + sso_group_name=SSO_GROUP_NAME, + ) + assert ( + f"ATLAN-PYTHON-400-058 SSO group mapping already exists between " + f"{group.alias} (Atlan group) <-> {SSO_GROUP_NAME} (SSO group)" + ) in str(err.value) + + +@pytest.mark.order( + after="test_sso_create_group_mapping_again_raises_invalid_request_error" +) +async def test_sso_retrieve_group_mapping( + client: AsyncAtlanClient, + group: AtlanGroup, + sso_mapping: SSOMapper, +): + assert group + assert sso_mapping + assert sso_mapping.id + time.sleep(5) + + retrieved_sso_mapping = await client.sso.get_group_mapping( + sso_alias=AtlanSSO.JUMPCLOUD, group_map_id=sso_mapping.id + ) + _assert_sso_group_mapping(group, retrieved_sso_mapping) + + +@pytest.mark.order(after="test_sso_retrieve_group_mapping") +async def test_sso_retrieve_all_group_mappings( + client: AsyncAtlanClient, + group: AtlanGroup, + sso_mapping: SSOMapper, +): + assert group + assert group.id + assert sso_mapping + time.sleep(5) + + retrieved_mappings = await client.sso.get_all_group_mappings( + sso_alias=AtlanSSO.JUMPCLOUD + ) + assert len(retrieved_mappings) >= 1 + mapping_found = False + for mapping in retrieved_mappings: + if ( + group.id in str(mapping.name) + and mapping.identity_provider_mapper == IDP_GROUP_MAPPER + ): + mapping_found = True + _assert_sso_group_mapping(group, mapping) + break + if not mapping_found: + pytest.fail( + f"{group.alias} (Atlan Group) <-> ({sso_mapping.config.attribute_value}) " + f"{AtlanSSO.JUMPCLOUD} SSO group mapping not found." + ) + + +@pytest.mark.order(after="test_sso_retrieve_all_group_mappings") +async def test_update_group_mapping( + client: AsyncAtlanClient, + group: AtlanGroup, + sso_mapping: SSOMapper, +): + assert group + assert sso_mapping + assert sso_mapping.id + assert sso_mapping.name + + updated_mapping = await client.sso.update_group_mapping( + sso_alias=AtlanSSO.JUMPCLOUD, + atlan_group=group, + group_map_id=sso_mapping.id, + group_map_name=sso_mapping.name, + sso_group_name=SSO_GROUP_NAME_UPDATED, + ) + _assert_sso_group_mapping(group, updated_mapping, True) diff --git a/tests_v9/integration/aio/test_task_client.py b/tests_v9/integration/aio/test_task_client.py new file mode 100644 index 000000000..883eff3e5 --- /dev/null +++ b/tests_v9/integration/aio/test_task_client.py @@ -0,0 +1,114 @@ +import time +from typing import AsyncGenerator + +import pytest +import pytest_asyncio + +from pyatlan_v9.client.aio.atlan import AsyncAtlanClient +from pyatlan_v9.model.assets import Column +from pyatlan_v9.model.enums import AtlanConnectorType, AtlanTaskType, SortOrder +from pyatlan_v9.model.fluent_tasks import FluentTasks +from pyatlan_v9.model.search import SortItem +from pyatlan_v9.model.task import AtlanTask, TaskSearchRequest +from pyatlan_v9.model.typedef import AtlanTagDef +from tests_v9.integration.client import TestId + +MODULE_NAME = TestId.make_unique("AsyncTaskClient") +TAG_NAME = MODULE_NAME + +DB_NAME = "WIDE_WORLD_IMPORTERS" +TABLE_NAME = "PACKAGETYPES" +COLUMN_NAME = "PACKAGETYPENAME" +SCHEMA_NAME = "BRONZE_WAREHOUSE" + + +@pytest_asyncio.fixture(scope="module") +async def snowflake_conn(client: AsyncAtlanClient): + return ( + await client.asset.find_connections_by_name( + "production", AtlanConnectorType.SNOWFLAKE + ) + )[0] + + +@pytest_asyncio.fixture(scope="module") +async def snowflake_column_qn(snowflake_conn): + return f"{snowflake_conn.qualified_name}/{DB_NAME}/{SCHEMA_NAME}/{TABLE_NAME}/{COLUMN_NAME}" + + +@pytest_asyncio.fixture() +async def snowflake_column( + client: AsyncAtlanClient, snowflake_column_qn +) -> AsyncGenerator[Column, None]: + await client.asset.add_atlan_tags( + asset_type=Column, + qualified_name=snowflake_column_qn, + atlan_tag_names=[TAG_NAME], + propagate=True, + remove_propagation_on_delete=True, + restrict_lineage_propagation=True, + ) + snowflake_column = await client.asset.get_by_qualified_name( + snowflake_column_qn, asset_type=Column, ignore_relationships=False + ) + yield snowflake_column + await client.asset.remove_atlan_tag( + asset_type=Column, + qualified_name=snowflake_column_qn, + atlan_tag_name=TAG_NAME, + ) + + +@pytest_asyncio.fixture(scope="module") +async def atlan_tag_def(make_atlan_tag_async) -> AtlanTagDef: + return await make_atlan_tag_async(TAG_NAME) + + +@pytest_asyncio.fixture() +async def task_search_request(snowflake_column: Column) -> TaskSearchRequest: + return ( + FluentTasks() + .page_size(1) + .sort( + by=SortItem( + field=AtlanTask.START_TIME.numeric_field_name, + order=SortOrder.DESCENDING, + ) + ) + .where(AtlanTask.ENTITY_GUID.eq(snowflake_column.guid)) + .where(AtlanTask.TYPE.eq(AtlanTaskType.CLASSIFICATION_PROPAGATION_ADD.value)) + .to_request() + ) + + +async def test_task_search( + client: AsyncAtlanClient, atlan_tag_def, task_search_request, snowflake_column +): + assert snowflake_column + assert snowflake_column.atlan_tags + + for tag in snowflake_column.atlan_tags: + if str(tag.type_name) == TAG_NAME: + break + pytest.fail(f"Tag '{TAG_NAME}' missing in {snowflake_column}") + + count = 0 + # TODO: replace with exponential back-off and jitter + while count < 10: + tasks = await client.tasks.search(request=task_search_request) + assert tasks + if tasks.count >= 1: + async for task in tasks: + break + count += 1 + time.sleep(5) + + assert task.guid + assert task.status + assert task.created_by + assert task.updated_time + assert task.parameters + assert task.classification_id + assert task.attempt_count is not None and task.attempt_count >= 0 + assert task.entity_guid == snowflake_column.guid + assert task.type == AtlanTaskType.CLASSIFICATION_PROPAGATION_ADD diff --git a/tests_v9/integration/aio/test_workflow_client.py b/tests_v9/integration/aio/test_workflow_client.py new file mode 100644 index 000000000..b2eac4b08 --- /dev/null +++ b/tests_v9/integration/aio/test_workflow_client.py @@ -0,0 +1,385 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. +import time +from typing import AsyncGenerator + +import pytest +import pytest_asyncio + +from pyatlan import utils +from pyatlan_v9.client.aio.atlan import AsyncAtlanClient +from pyatlan_v9.model.assets import Connection +from pyatlan_v9.model.credential import Credential, CredentialResponse +from pyatlan_v9.model.enums import ( + AtlanConnectorType, + AtlanWorkflowPhase, + WorkflowPackage, +) +from pyatlan_v9.model.packages.snowflake_miner import SnowflakeMiner +from pyatlan_v9.model.workflow import WorkflowResponse, WorkflowSchedule +from tests_v9.integration.aio.utils import delete_asset_async +from tests_v9.integration.client import TestId + +MODULE_NAME = TestId.make_unique("AsyncWorkflowClient") + + +async def delete_workflow_async(client: AsyncAtlanClient, workflow_name: str) -> None: + await client.workflow.delete(workflow_name=workflow_name) + + +WORKFLOW_TEMPLATE_REF = "workflowTemplateRef" +WORKFLOW_SCHEDULE_SCHEDULE = "45 4 * * *" +WORKFLOW_SCHEDULE_TIMEZONE = "Asia/Kolkata" +WORKFLOW_SCHEDULE_UPDATED_1 = "45 5 * * *" +WORKFLOW_SCHEDULE_TIMEZONE_UPDATED_1 = "Europe/Paris" +WORKFLOW_SCHEDULE_UPDATED_2 = "45 6 * * *" +WORKFLOW_SCHEDULE_TIMEZONE_UPDATED_2 = "Europe/London" +WORKFLOW_SCHEDULE_UPDATED_3 = "45 7 * * *" +WORKFLOW_SCHEDULE_TIMEZONE_UPDATED_3 = "Europe/Dublin" + +ASSET_TYPE_CONNECTION = "Connection" +ASSET_TYPE_CONNECTION_QN = "default/snowflake/" + str(utils.get_epoch_timestamp()) + + +@pytest_asyncio.fixture(scope="module") +async def connection(client: AsyncAtlanClient) -> AsyncGenerator[Connection, None]: + admin_role_guid = str(await client.role_cache.get_id_for_name("$admin")) + to_create = await Connection.creator_async( + client=client, + name=MODULE_NAME, + connector_type=AtlanConnectorType.SNOWFLAKE, + admin_roles=[admin_role_guid], + ) + response = await client.asset.save(to_create) + connection_created = response.assets_created(asset_type=Connection) + assert connection_created + c = connection_created[0] + yield c + await delete_asset_async(client=client, guid=c.guid, asset_type=Connection) + + +@pytest_asyncio.fixture(scope="module") +async def workflow( + client: AsyncAtlanClient, connection: Connection +) -> AsyncGenerator[WorkflowResponse, None]: + assert connection and connection.qualified_name + miner = ( + SnowflakeMiner(connection_qualified_name=connection.qualified_name) + .s3( + s3_bucket="test-s3-bucket", + s3_prefix="test-s3-prefix", + s3_bucket_region="test-s3-bucket-region", + sql_query_key="TEST_QUERY", + default_database_key="TEST_SNOWFLAKE", + default_schema_key="TEST_SCHEMA", + session_id_key="TEST_SESSION_ID", + ) + .popularity_window(days=15) + .native_lineage(enabled=True) + .custom_config(config={"test": True, "feature": 1234}) + .to_workflow() + ) + schedule = WorkflowSchedule( + cron_schedule=WORKFLOW_SCHEDULE_SCHEDULE, timezone=WORKFLOW_SCHEDULE_TIMEZONE + ) + workflow = await client.workflow.run(miner, workflow_schedule=schedule) + assert workflow + # Adding some delay to make sure + # the workflow run is indexed in ES. + time.sleep(30) + yield workflow + assert workflow.metadata and workflow.metadata.name + await delete_workflow_async(client, workflow.metadata.name) + + +@pytest_asyncio.fixture(scope="module") +async def create_credentials( + client: AsyncAtlanClient, +) -> AsyncGenerator[CredentialResponse, None]: + credentials_name = f"default-spark-{int(utils.get_epoch_timestamp())}-0" + + credentials = Credential( + name=credentials_name, + auth_type="atlan_api_key", + connector_config_name="atlan-connectors-spark", + connector="spark", + username="test-username", + password="12345", + connector_type="event", + host="test-host", + port=123, + ) + + create_credentials = await client.credentials.creator(credentials) + guid = create_credentials.id + if guid is None: + raise ValueError("Failed to retrieve GUID from created credentials.") + + yield create_credentials + + response = await delete_credentials_async(client, guid=guid) + assert response is None + + +async def delete_credentials_async(client: AsyncAtlanClient, guid: str): + response = await client.credentials.purge_by_guid(guid=guid) + return response + + +async def test_workflow_find_by_methods(client: AsyncAtlanClient): + results = await client.workflow.find_by_type( + prefix=WorkflowPackage.SNOWFLAKE, max_results=10 + ) + assert results + assert len(results) >= 1 + + workflow_id = results[0].id + assert workflow_id + workflow = await client.workflow.find_by_id(id=workflow_id) + assert workflow + assert workflow.id and workflow.id == workflow_id + + workflow = await client.workflow.find_by_id(id="invalid-id") + assert workflow is None + + +async def test_workflow_get_runs_and_stop( + client: AsyncAtlanClient, workflow: WorkflowResponse +): + # Retrieve the latest workflow run + assert workflow and workflow.metadata and workflow.metadata.name + runs = await client.workflow.get_runs( + workflow_name=workflow.metadata.name, workflow_phase=AtlanWorkflowPhase.RUNNING + ) + assert runs and runs.count == 1 + current_page = runs.current_page() + assert current_page is not None and len(current_page) == 1 + run = current_page[0] + assert run and run.id + assert workflow.metadata.name and (workflow.metadata.name in run.id) + + # Stop the running workflow + run_response = await client.workflow.stop(workflow_run_id=run.id) + assert run_response + assert ( + run_response.status and run_response.status.phase == AtlanWorkflowPhase.RUNNING + ) + assert ( + run_response.status.stored_workflow_template_spec + and run_response.status.stored_workflow_template_spec.get( + WORKFLOW_TEMPLATE_REF + ).get("name") + == workflow.metadata.name + ) + + # Test workflow monitoring + workflow_status = await client.workflow.monitor(workflow_response=workflow) + assert workflow_status == AtlanWorkflowPhase.FAILED + + # Test workflow monitoring by providing workflow name directly + workflow_name = workflow.metadata.name + workflow_status = await client.workflow.monitor(workflow_name=workflow_name) + assert workflow_status == AtlanWorkflowPhase.FAILED + + # Test find run by id + workflow_run = await client.workflow.find_run_by_id(id=run.id) + assert ( + workflow_run + and workflow_run.source + and workflow_run.source.status + and workflow_run.source.status.phase == AtlanWorkflowPhase.FAILED + ) + + # Test find run by status and time range + runs_status = await client.workflow.find_runs_by_status_and_time_range( + [AtlanWorkflowPhase.FAILED], started_at="now-1h" + ) + assert runs_status + async for _ in runs_status: + pass + + +async def test_workflow_get_all_scheduled_runs( + client: AsyncAtlanClient, workflow: WorkflowResponse +): + runs = await client.workflow.get_all_scheduled_runs() + + assert workflow and workflow.metadata and workflow.metadata.name + scheduled_workflow_name = f"{workflow.metadata.name}-cron" + assert runs and len(runs) >= 1 + + found = any( + run.metadata and run.metadata.name == scheduled_workflow_name for run in runs + ) + + if not found: + pytest.fail( + f"Unable to find scheduled run for workflow: {workflow.metadata.name}" + ) + + +async def _assert_scheduled_run_async( + client: AsyncAtlanClient, workflow: WorkflowResponse +): + assert workflow and workflow.metadata and workflow.metadata.name + scheduled_workflow = await client.workflow.get_scheduled_run( + workflow_name=workflow.metadata.name + ) + scheduled_workflow_name = f"{workflow.metadata.name}-cron" + assert ( + scheduled_workflow + and scheduled_workflow.metadata + and scheduled_workflow.metadata.name == scheduled_workflow_name + ) + + +def _assert_add_schedule_async(workflow, scheduled_workflow, schedule, timezone): + assert scheduled_workflow + assert scheduled_workflow.metadata + assert scheduled_workflow.metadata.name == workflow.metadata.name + assert scheduled_workflow.metadata.annotations + assert ( + scheduled_workflow.metadata.annotations.get("orchestration.atlan.com/schedule") + == schedule + ) + assert ( + scheduled_workflow.metadata.annotations.get("orchestration.atlan.com/timezone") + == timezone + ) + + +def _assert_remove_schedule_async(response, workflow: WorkflowResponse): + assert response + assert workflow and workflow.metadata and workflow.metadata.name + + +async def test_workflow_get_scheduled_run( + client: AsyncAtlanClient, workflow: WorkflowResponse +): + await _assert_scheduled_run_async(client, workflow) + + +async def test_workflow_add_remove_schedule( + client: AsyncAtlanClient, workflow: WorkflowResponse +): + schedule = WorkflowSchedule( + cron_schedule=WORKFLOW_SCHEDULE_UPDATED_1, + timezone=WORKFLOW_SCHEDULE_TIMEZONE_UPDATED_1, + ) + + # NOTE: This method will overwrite existing workflow run schedule + # Try to update schedule again, with `Workflow` object + scheduled_workflow = await client.workflow.add_schedule( + workflow=workflow, workflow_schedule=schedule + ) + + _assert_add_schedule_async( + workflow, + scheduled_workflow, + WORKFLOW_SCHEDULE_UPDATED_1, + WORKFLOW_SCHEDULE_TIMEZONE_UPDATED_1, + ) + # Make sure scheduled run exists + await _assert_scheduled_run_async(client, workflow) + # Now remove the scheduled run + response = await client.workflow.remove_schedule(workflow) + _assert_remove_schedule_async(response, workflow) + + # Try to update schedule again, with `WorkflowSearchResult` object + existing_workflow = ( + await client.workflow.find_by_type(prefix=WorkflowPackage.SNOWFLAKE_MINER) + )[0] + assert existing_workflow + + schedule = WorkflowSchedule( + cron_schedule=WORKFLOW_SCHEDULE_UPDATED_2, + timezone=WORKFLOW_SCHEDULE_TIMEZONE_UPDATED_2, + ) + scheduled_workflow = await client.workflow.add_schedule( + workflow=existing_workflow, workflow_schedule=schedule + ) + + _assert_add_schedule_async( + workflow, + scheduled_workflow, + WORKFLOW_SCHEDULE_UPDATED_2, + WORKFLOW_SCHEDULE_TIMEZONE_UPDATED_2, + ) + # Make sure scheduled run exists + await _assert_scheduled_run_async(client, workflow) + # Now remove the scheduled run + response = await client.workflow.remove_schedule(workflow) + _assert_remove_schedule_async(response, workflow) + + schedule = WorkflowSchedule( + cron_schedule=WORKFLOW_SCHEDULE_UPDATED_3, + timezone=WORKFLOW_SCHEDULE_TIMEZONE_UPDATED_3, + ) + scheduled_workflow = await client.workflow.add_schedule( + workflow=WorkflowPackage.SNOWFLAKE_MINER, workflow_schedule=schedule + ) + + _assert_add_schedule_async( + workflow, + scheduled_workflow, + WORKFLOW_SCHEDULE_UPDATED_3, + WORKFLOW_SCHEDULE_TIMEZONE_UPDATED_3, + ) + # Make sure scheduled run exists + await _assert_scheduled_run_async(client, workflow) + # Now remove the scheduled run + response = await client.workflow.remove_schedule(workflow) + _assert_remove_schedule_async(response, workflow) + + +async def test_credentials(client: AsyncAtlanClient, create_credentials: Credential): + credentials = create_credentials + assert credentials + assert credentials.id + retrieved_creds = await client.credentials.get(guid=credentials.id) + assert retrieved_creds.auth_type == "atlan_api_key" + assert retrieved_creds.connector_config_name == "atlan-connectors-spark" + + +async def test_get_all_credentials(client: AsyncAtlanClient): + credentials = await client.credentials.get_all() + assert credentials, "Expected credentials but found None" + assert credentials.records is not None, "Expected records but found None" + + +async def test_get_all_credentials_with_filter_limit_offset(client: AsyncAtlanClient): + filter_criteria = {"connectorType": "snowflake", "isActive": True} + limit = 1 + offset = 1 + credentials = await client.credentials.get_all( + filter=filter_criteria, limit=limit, offset=offset + ) + assert credentials, "Expected credentials but found None" + assert credentials.records is not None, "Expected records but found None" + + +async def test_get_all_credentials_with_multiple_filters(client: AsyncAtlanClient): + filter_criteria = {"connectorType": "jdbc", "isActive": True} + + credentials = await client.credentials.get_all(filter=filter_criteria) + assert credentials, "Expected credentials but found None" + assert credentials.records is not None, "Expected records but found None" + + +async def test_get_all_credentials_with_invalid_filter_key(client: AsyncAtlanClient): + filter_criteria = {"invalidKey": "someValue"} + try: + await client.credentials.get_all(filter=filter_criteria) + pytest.fail("Expected an error due to invalid filter key, but none occurred.") + except Exception as e: + assert e is not None + + +async def test_get_all_credentials_with_invalid_filter_value(client: AsyncAtlanClient): + filter_criteria = {"connector_type": 123} + + try: + await client.credentials.get_all(filter=filter_criteria) + pytest.fail("Expected an error due to invalid filter value, but none occurred.") + except Exception as e: + assert "400" in str(e), f"Expected a 400 error, but got: {e}" diff --git a/tests_v9/integration/aio/utils.py b/tests_v9/integration/aio/utils.py new file mode 100644 index 000000000..cf4231264 --- /dev/null +++ b/tests_v9/integration/aio/utils.py @@ -0,0 +1,378 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +"""Async utilities for integration tests.""" + +import logging +import math +from typing import Optional, Type + +from tenacity import retry, retry_if_result, stop_after_attempt, wait_exponential + +from pyatlan.model.aio import AsyncIndexSearchResults +from pyatlan_v9.client.aio.atlan import AsyncAtlanClient +from pyatlan_v9.model.api_tokens import ApiToken +from pyatlan_v9.model.assets import Asset, AtlasGlossary, Connection, Database +from pyatlan_v9.model.enums import ( + AtlanConnectorType, + AtlanDeleteType, + CertificateStatus, +) +from pyatlan_v9.model.search import DSL, IndexSearchRequest + +LOGGER = logging.getLogger(__name__) + + +async def create_token_async(token_client: AsyncAtlanClient, name: str) -> ApiToken: + """Create an API token asynchronously.""" + token = await token_client.token.creator(name) + return token + + +async def delete_token_async( + token_client: AsyncAtlanClient, token: Optional[ApiToken] = None +): + """Delete an API token asynchronously.""" + # If there is a partial failure on the server side + # and the token is still visible in the Atlan UI, + # in that case, the create method may not return a token. + # We should retrieve the list of all tokens and delete them here. + if not token: + tokens_response = await token_client.token.get() + tokens = tokens_response.records + assert tokens + delete_tokens = [ + token + for token in tokens + if token.display_name and "psdkv9_Async" in token.display_name + ] + for token in delete_tokens: + assert token and token.guid + await token_client.token.purge(token.guid) + return + # In case of no partial failure, directly delete the token + if token.guid: + await token_client.token.purge(token.guid) + + +async def delete_asset_async( + client: AsyncAtlanClient, + guid: str, + asset_type: Type[Asset], + delete_type: AtlanDeleteType = AtlanDeleteType.PURGE, +) -> None: + """Delete an asset asynchronously.""" + try: + response = await client.asset.purge_by_guid(guid, delete_type) + if response: + deleted_assets = response.assets_deleted(asset_type) + if ( + deleted_assets + and len(deleted_assets) == 1 + and deleted_assets[0].guid == guid + ): + LOGGER.debug( + f"Successfully deleted {asset_type.__name__} with GUID {guid}" + ) + else: + LOGGER.warning( + f"Unexpected response when deleting {asset_type.__name__} with GUID {guid}" + ) + else: + LOGGER.warning( + f"No response when deleting {asset_type.__name__} with GUID {guid}" + ) + except Exception as e: + LOGGER.error(f"Failed to remove {asset_type.__name__} with GUID {guid}: {e}") + + +async def create_connection_async( + client: AsyncAtlanClient, name: str, connector_type: AtlanConnectorType +) -> Connection: + """Create a connection asynchronously.""" + role = await client.role_cache.get_id_for_name("$admin") + assert role + to_create = await Connection.creator_async( + client=client, name=name, connector_type=connector_type, admin_roles=[role] + ) + response = await client.asset.save(to_create) + return response.assets_created(Connection)[0] + + +async def create_database_async(client: AsyncAtlanClient, name: str) -> Database: + """Create a database asynchronously.""" + connection_name = f"{name}_connection" + connection = await create_connection_async( + client, connection_name, AtlanConnectorType.VERTICA + ) + database = Database.creator( + name=name, connection_qualified_name=connection.qualified_name + ) + response = await client.asset.save(database) + return response.assets_created(Database)[0] + + +async def create_glossary_async(client: AsyncAtlanClient, name: str) -> AtlasGlossary: + """Create a glossary asynchronously.""" + glossary = AtlasGlossary.creator(name=name) + response = await client.asset.save(glossary) + return response.assets_created(AtlasGlossary)[0] + + +async def update_certificate_async( + client: AsyncAtlanClient, + test_asset: Asset, + test_asset_type: Type[Asset], + glossary_guid: Optional[str] = None, +): + """Update certificate status for an asset asynchronously.""" + assert test_asset.qualified_name + assert test_asset.name + test_asset = await client.asset.get_by_guid( + guid=test_asset.guid, asset_type=test_asset_type, ignore_relationships=False + ) + assert test_asset.qualified_name + assert test_asset.name + assert not test_asset.certificate_status + assert not test_asset.certificate_status_message + message = "An important message" + await client.asset.update_certificate( + asset_type=test_asset_type, + qualified_name=test_asset.qualified_name, + name=test_asset.name, + certificate_status=CertificateStatus.DRAFT, + message=message, + glossary_guid=glossary_guid if glossary_guid else None, + ) + test_asset = await client.asset.get_by_guid( + guid=test_asset.guid, asset_type=test_asset_type, ignore_relationships=False + ) + assert test_asset.certificate_status == CertificateStatus.DRAFT + assert test_asset.certificate_status_message == message + + +async def remove_certificate_async( + client: AsyncAtlanClient, + test_asset: Asset, + test_asset_type: Type[Asset], + glossary_guid: Optional[str] = None, +): + """Remove certificate status from an asset asynchronously.""" + assert test_asset.qualified_name + assert test_asset.name + await client.asset.remove_certificate( + asset_type=test_asset_type, + qualified_name=test_asset.qualified_name, + name=test_asset.name, + glossary_guid=glossary_guid if glossary_guid else None, + ) + test_asset = await client.asset.get_by_guid( + guid=test_asset.guid, asset_type=test_asset_type, ignore_relationships=False + ) + assert not test_asset.certificate_status + assert not test_asset.certificate_status_message + + +async def update_announcement_async( + client: AsyncAtlanClient, + test_asset: Asset, + test_asset_type: Type[Asset], + test_announcement, + glossary_guid: Optional[str] = None, +): + """Update announcement for an asset asynchronously.""" + assert test_asset.qualified_name + assert test_asset.name + await client.asset.update_announcement( + asset_type=test_asset_type, + qualified_name=test_asset.qualified_name, + name=test_asset.name, + announcement=test_announcement, + glossary_guid=glossary_guid if glossary_guid else None, + ) + test_asset = await client.asset.get_by_guid( + guid=test_asset.guid, asset_type=test_asset_type, ignore_relationships=False + ) + assert test_asset.get_announcment() == test_announcement + + +async def remove_announcement_async( + client: AsyncAtlanClient, + test_asset: Asset, + test_asset_type: Type[Asset], + glossary_guid: Optional[str] = None, +): + """Remove announcement from an asset asynchronously.""" + assert test_asset.qualified_name + assert test_asset.name + await client.asset.remove_announcement( + asset_type=test_asset_type, + qualified_name=test_asset.qualified_name, + name=test_asset.name, + glossary_guid=glossary_guid if glossary_guid else None, + ) + test_asset = await client.asset.get_by_guid( + guid=test_asset.guid, asset_type=test_asset_type, ignore_relationships=False + ) + assert test_asset.get_announcment() is None + + +async def async_search_request_count_with_retry( + client: AsyncAtlanClient, request: IndexSearchRequest, expected_count: int +) -> int: + """ + Execute IndexSearchRequest with retry until expected count is reached (async version). + + :param client: AsyncAtlanClient instance + :param request: IndexSearchRequest to execute + :param expected_count: expected count to reach + :returns: actual count found + """ + from tenacity import retry, retry_if_result, stop_after_attempt, wait_exponential + + @retry( + reraise=True, + retry=retry_if_result(lambda x: x != expected_count), + stop=stop_after_attempt(10), + wait=wait_exponential(multiplier=1, min=2, max=10), + ) + async def _retry_search(): + response = await client.asset.search(criteria=request) + return len(response.current_page()) + + return await _retry_search() + + +async def async_assert_search_count_with_retry( + client: AsyncAtlanClient, request: IndexSearchRequest, expected_count: int +) -> None: + """ + Assert search count with retry - convenience method for async test assertions. + + :param client: AsyncAtlanClient instance + :param request: IndexSearchRequest to execute + :param expected_count: expected count to assert + :raises AssertionError: if count doesn't match after retries + """ + actual_count = await async_search_request_count_with_retry( + client, request, expected_count + ) + assert actual_count == expected_count, ( + f"Expected {expected_count} results, got {actual_count}" + ) + + +async def async_fluent_search_count_with_retry( + fluent_search, client: AsyncAtlanClient, expected_count: int +) -> int: + """ + Count FluentSearch results with automatic retry for search index eventual consistency (async version). + + :param fluent_search: FluentSearch instance to count + :param client: AsyncAtlanClient instance + :param expected_count: expected minimum count to wait for + :returns: actual count after retry logic + """ + from tenacity import retry, retry_if_result, stop_after_attempt, wait_exponential + + @retry( + reraise=True, + retry=retry_if_result(lambda count: count < expected_count), + stop=stop_after_attempt(10), + wait=wait_exponential(multiplier=1, min=2, max=10), + ) + async def _retry_count(): + # Replicate the sync count() method logic for async + dsl = fluent_search._dsl() + dsl.size = 1 + from pyatlan_v9.model.search import IndexSearchRequest + + request = IndexSearchRequest(dsl=dsl) + result = await client.asset.search(request) + return result.count + + return await _retry_count() + + +async def async_assert_fluent_search_count_with_retry( + fluent_search, client: AsyncAtlanClient, expected_count: int +) -> None: + """ + Assert FluentSearch count with retry - convenience method for async test assertions. + + :param fluent_search: FluentSearch instance to count + :param client: AsyncAtlanClient instance + :param expected_count: expected count to assert + :raises AssertionError: if count doesn't match after retries + """ + actual_count = await async_fluent_search_count_with_retry( + fluent_search, client, expected_count + ) + assert actual_count == expected_count, ( + f"Expected {expected_count} results, got {actual_count}" + ) + + +async def async_search_with_retry( + client: AsyncAtlanClient, request: IndexSearchRequest, expected_count: int +) -> AsyncIndexSearchResults: + """ + Execute search with retry until expected count is reached, then return the results. + + :param client: AsyncAtlanClient instance + :param request: IndexSearchRequest to execute + :param expected_count: expected count to reach + :returns: AsyncIndexSearchResults with the expected count + """ + + @retry( + reraise=True, + retry=retry_if_result( + lambda response: len(response.current_page()) != expected_count + ), + stop=stop_after_attempt(10), + wait=wait_exponential(multiplier=1, min=2, max=10), + ) + async def _retry_search(): + return await client.asset.search(criteria=request) + + return await _retry_search() + + +async def get_optimized_page_size( + client: AsyncAtlanClient, + query, + post_filter=None, + target_api_calls: int = 10, + min_size: int = 2, + attributes=None, +): + """ + Utility to get optimized page size for search tests by calculating total count first. + This prevents slow tests by avoiding too many small API calls. + + :param client: AsyncAtlanClient instance + :param query: Query to use for the search + :param post_filter: Optional post filter + :param target_api_calls: Target number of API calls (default 10) + :param min_size: Minimum page size (default 2) + :param attributes: Optional attributes for the request + :returns: tuple of (total_assets_count, optimized_page_size) + """ + # Get total count + count_dsl = DSL( + query=query, + post_filter=post_filter, + size=0, # get total count only + ) + count_request = IndexSearchRequest(dsl=count_dsl) + if attributes: + count_request.attributes = attributes + + count_results = await client.asset.search(criteria=count_request) + total_assets = count_results.count + + # Calculate optimal page size + optimal_size = max(min_size, math.ceil(total_assets / target_api_calls)) + + return total_assets, optimal_size diff --git a/tests_v9/integration/airflow_asset_test.py b/tests_v9/integration/airflow_asset_test.py new file mode 100644 index 000000000..c048f2827 --- /dev/null +++ b/tests_v9/integration/airflow_asset_test.py @@ -0,0 +1,242 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. +from typing import Generator + +import pytest + +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.model.assets import AirflowDag, AirflowTask, Connection +from pyatlan_v9.model.core import Announcement +from pyatlan_v9.model.enums import ( + AnnouncementType, + AtlanConnectorType, + CertificateStatus, + EntityStatus, +) +from tests_v9.integration.client import TestId, delete_asset +from tests_v9.integration.connection_test import create_connection + +MODULE_NAME = TestId.make_unique("AIRFLOW") + +AIRFLOW_DAG_NAME = f"test_dag_{MODULE_NAME}" +AIRFLOW_TASK_NAME = f"test_task_{MODULE_NAME}" +AIRFLOW_TASK_NAME_OVERLOAD = f"test_task_overload_{MODULE_NAME}" +CERTIFICATE_STATUS = CertificateStatus.VERIFIED + +ANNOUNCEMENT_TITLE = "Python SDK testing." +ANNOUNCEMENT_TYPE = AnnouncementType.INFORMATION +CERTIFICATE_MESSAGE = "Automated testing of the Python SDK." +ANNOUNCEMENT_MESSAGE = "Automated testing of the Python SDK." + + +@pytest.fixture(scope="module") +def connection(client: AtlanClient) -> Generator[Connection, None, None]: + result = create_connection( + client=client, name=MODULE_NAME, connector_type=AtlanConnectorType.AIRFLOW + ) + yield result + delete_asset(client, guid=result.guid, asset_type=Connection) + + +@pytest.fixture(scope="module") +def airflow_dag( + client: AtlanClient, connection: Connection +) -> Generator[AirflowDag, None, None]: + assert connection.qualified_name + to_create = AirflowDag.creator( + name=AIRFLOW_DAG_NAME, connection_qualified_name=connection.qualified_name + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=AirflowDag)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=AirflowDag) + + +def test_airflow_dag( + client: AtlanClient, + connection: Connection, + airflow_dag: AirflowDag, +): + assert airflow_dag + assert airflow_dag.guid + assert airflow_dag.qualified_name + assert airflow_dag.name == AIRFLOW_DAG_NAME + assert airflow_dag.connector_name == AtlanConnectorType.AIRFLOW + assert airflow_dag.connection_qualified_name == connection.qualified_name + + +@pytest.fixture(scope="module") +def airflow_task( + client: AtlanClient, airflow_dag: AirflowDag +) -> Generator[AirflowTask, None, None]: + assert airflow_dag.qualified_name + to_create = AirflowTask.creator( + name=AIRFLOW_TASK_NAME, airflow_dag_qualified_name=airflow_dag.qualified_name + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=AirflowTask)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=AirflowTask) + + +def test_airflow_task( + client: AtlanClient, + airflow_dag: AirflowDag, + airflow_task: AirflowTask, +): + assert airflow_task + assert airflow_task.guid + assert airflow_task.qualified_name + assert airflow_task.name == AIRFLOW_TASK_NAME + assert airflow_task.connector_name == AtlanConnectorType.AIRFLOW + assert airflow_task.airflow_dag_qualified_name == airflow_dag.qualified_name + + +@pytest.fixture(scope="module") +def airflow_task_overload( + client: AtlanClient, airflow_dag: AirflowDag, connection: Connection +) -> Generator[AirflowTask, None, None]: + assert airflow_dag.qualified_name + assert connection.qualified_name + to_create = AirflowTask.creator( + name=AIRFLOW_TASK_NAME_OVERLOAD, + airflow_dag_qualified_name=airflow_dag.qualified_name, + connection_qualified_name=connection.qualified_name, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=AirflowTask)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=AirflowTask) + + +def test_overload_airflow_task( + client: AtlanClient, + airflow_dag: AirflowDag, + airflow_task_overload: AirflowTask, +): + assert airflow_task_overload + assert airflow_task_overload.guid + assert airflow_task_overload.qualified_name + assert airflow_task_overload.name == AIRFLOW_TASK_NAME_OVERLOAD + assert airflow_task_overload.connector_name == AtlanConnectorType.AIRFLOW + assert ( + airflow_task_overload.airflow_dag_qualified_name == airflow_dag.qualified_name + ) + + +def _update_cert_and_annoucement(client, asset, asset_type): + assert asset.name + assert asset.qualified_name + + updated = client.asset.update_certificate( + name=asset.name, + asset_type=asset_type, + qualified_name=asset.qualified_name, + message=CERTIFICATE_MESSAGE, + certificate_status=CERTIFICATE_STATUS, + ) + assert updated + assert updated.certificate_status == CERTIFICATE_STATUS + assert updated.certificate_status_message == CERTIFICATE_MESSAGE + + updated = client.asset.update_announcement( + name=asset.name, + asset_type=asset_type, + qualified_name=asset.qualified_name, + announcement=Announcement( + announcement_type=ANNOUNCEMENT_TYPE, + announcement_title=ANNOUNCEMENT_TITLE, + announcement_message=ANNOUNCEMENT_MESSAGE, + ), + ) + assert updated + assert updated.announcement_type == ANNOUNCEMENT_TYPE + assert updated.announcement_title == ANNOUNCEMENT_TITLE + assert updated.announcement_message == ANNOUNCEMENT_MESSAGE + + +def test_update_airflow_assets( + client: AtlanClient, + airflow_dag: AirflowDag, + airflow_task: AirflowTask, +): + _update_cert_and_annoucement(client, airflow_dag, AirflowDag) + _update_cert_and_annoucement(client, airflow_task, AirflowTask) + + +def _retrieve_airflow_assets(client, asset, asset_type): + retrieved = client.asset.get_by_guid( + asset.guid, asset_type=asset_type, ignore_relationships=False + ) + assert retrieved + assert not retrieved.is_incomplete + assert retrieved.guid == asset.guid + assert retrieved.qualified_name == asset.qualified_name + assert retrieved.name == asset.name + assert retrieved.connector_name == AtlanConnectorType.AIRFLOW + assert retrieved.certificate_status == CERTIFICATE_STATUS + assert retrieved.certificate_status_message == CERTIFICATE_MESSAGE + + +@pytest.mark.order(after="test_update_airflow_assets") +def test_retrieve_airflow_assets( + client: AtlanClient, + airflow_dag: AirflowDag, + airflow_task: AirflowTask, +): + _retrieve_airflow_assets(client, airflow_dag, AirflowDag) + _retrieve_airflow_assets(client, airflow_task, AirflowTask) + + +@pytest.mark.order(after="test_retrieve_airflow_assets") +def test_delete_airflow_task( + client: AtlanClient, + airflow_task: AirflowTask, +): + response = client.asset.delete_by_guid(guid=airflow_task.guid) + assert response + assert not response.assets_created(asset_type=AirflowTask) + assert not response.assets_updated(asset_type=AirflowTask) + deleted = response.assets_deleted(asset_type=AirflowTask) + + assert deleted + assert len(deleted) == 1 + assert deleted[0].guid == airflow_task.guid + assert deleted[0].delete_handler == "SOFT" + assert deleted[0].status == EntityStatus.DELETED + assert deleted[0].qualified_name == airflow_task.qualified_name + + +@pytest.mark.order(after="test_delete_airflow_task") +def test_read_deleted_airflow_task( + client: AtlanClient, + airflow_task: AirflowTask, +): + deleted = client.asset.get_by_guid( + airflow_task.guid, asset_type=AirflowTask, ignore_relationships=False + ) + assert deleted + assert deleted.status == EntityStatus.DELETED + assert deleted.guid == airflow_task.guid + assert deleted.qualified_name == airflow_task.qualified_name + + +@pytest.mark.order(after="test_read_deleted_airflow_task") +def test_restore_airflow_task( + client: AtlanClient, + airflow_task: AirflowTask, +): + assert airflow_task.qualified_name + assert client.asset.restore( + asset_type=AirflowTask, qualified_name=airflow_task.qualified_name + ) + assert airflow_task.qualified_name + restored = client.asset.get_by_qualified_name( + asset_type=AirflowTask, + qualified_name=airflow_task.qualified_name, + ignore_relationships=False, + ) + assert restored + assert restored.guid == airflow_task.guid + assert restored.status == EntityStatus.ACTIVE + assert restored.qualified_name == airflow_task.qualified_name diff --git a/tests_v9/integration/anaplan_asset_test.py b/tests_v9/integration/anaplan_asset_test.py new file mode 100644 index 000000000..467112558 --- /dev/null +++ b/tests_v9/integration/anaplan_asset_test.py @@ -0,0 +1,711 @@ +from typing import Generator + +import pytest + +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.model.assets import ( + AnaplanApp, + AnaplanDimension, + AnaplanLineItem, + AnaplanList, + AnaplanModel, + AnaplanModule, + AnaplanPage, + AnaplanSystemDimension, + AnaplanView, + AnaplanWorkspace, + Connection, +) +from pyatlan_v9.model.core import Announcement +from pyatlan_v9.model.enums import ( + AnnouncementType, + AtlanConnectorType, + CertificateStatus, + EntityStatus, +) +from tests_v9.integration.client import TestId, delete_asset +from tests_v9.integration.connection_test import create_connection + +MODULE_NAME = TestId.make_unique("ANAPLAN") + +CONNECTOR_TYPE = AtlanConnectorType.ANAPLAN +ANAPLAN_WORKSPACE_NAME = f"{MODULE_NAME}-anaplan-workspace" +ANAPLAN_APP_NAME = f"{MODULE_NAME}-anaplan-app" +ANAPLAN_PAGE_NAME = f"{MODULE_NAME}-anaplan-page" +ANAPLAN_PAGE_NAME_OVERLOAD = f"{MODULE_NAME}-anaplan-page-overload" +ANAPLAN_MODEL_NAME = f"{MODULE_NAME}-anaplan-model" +ANAPLAN_MODEL_NAME_OVERLOAD = f"{MODULE_NAME}-anaplan-model-overload" +ANAPLAN_MODULE_NAME = f"{MODULE_NAME}-anaplan-module" +ANAPLAN_MODULE_NAME_OVERLOAD = f"{MODULE_NAME}-anaplan-module-overload" +ANAPLAN_LIST_NAME = f"{MODULE_NAME}-anaplan-list" +ANAPLAN_LIST_NAME_OVERLOAD = f"{MODULE_NAME}-anaplan-list-overload" +ANAPLAN_SYSTEM_DIMENSION_NAME = f"{MODULE_NAME}-anaplan-system-dimension" +ANAPLAN_DIMENSION_NAME = f"{MODULE_NAME}-anaplan-dimension" +ANAPLAN_DIMENSION_NAME_OVERLOAD = f"{MODULE_NAME}-anaplan-dimension-overload" +ANAPLAN_LINEITEM_NAME = f"{MODULE_NAME}-anaplan-lineitem" +ANAPLAN_LINEITEM_NAME_OVERLOAD = f"{MODULE_NAME}-anaplan-lineitem-overload" +ANAPLAN_VIEW_NAME = f"{MODULE_NAME}-anaplan-view" +ANAPLAN_VIEW_NAME_OVERLOAD = f"{MODULE_NAME}-anaplan-view-overload" + +CERTIFICATE_STATUS = CertificateStatus.VERIFIED +CERTIFICATE_MESSAGE = "Automated testing of the Python SDK." +ANNOUNCEMENT_TYPE = AnnouncementType.INFORMATION +ANNOUNCEMENT_TITLE = "Python SDK testing." +ANNOUNCEMENT_MESSAGE = "Automated testing of the Python SDK." + + +@pytest.fixture(scope="module") +def connection(client: AtlanClient) -> Generator[Connection, None, None]: + result = create_connection( + client=client, name=MODULE_NAME, connector_type=CONNECTOR_TYPE + ) + yield result + delete_asset(client, guid=result.guid, asset_type=Connection) + + +@pytest.fixture(scope="module") +def anaplan_workspace( + client: AtlanClient, connection: Connection +) -> Generator[AnaplanWorkspace, None, None]: + assert connection.qualified_name + to_create = AnaplanWorkspace.creator( + name=ANAPLAN_WORKSPACE_NAME, connection_qualified_name=connection.qualified_name + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=AnaplanWorkspace)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=AnaplanWorkspace) + + +def test_anaplan_workspace( + client: AtlanClient, connection: Connection, anaplan_workspace: AnaplanWorkspace +): + assert anaplan_workspace + assert anaplan_workspace.guid + assert anaplan_workspace.qualified_name + assert anaplan_workspace.name == ANAPLAN_WORKSPACE_NAME + assert anaplan_workspace.connection_qualified_name == connection.qualified_name + assert anaplan_workspace.connector_name == AtlanConnectorType.ANAPLAN.value + + +@pytest.fixture(scope="module") +def anaplan_system_dimension( + client: AtlanClient, connection: Connection +) -> Generator[AnaplanSystemDimension, None, None]: + assert connection.qualified_name + to_create = AnaplanSystemDimension.creator( + name=ANAPLAN_SYSTEM_DIMENSION_NAME, + connection_qualified_name=connection.qualified_name, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=AnaplanSystemDimension)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=AnaplanSystemDimension) + + +def test_anaplan_system_dimension( + client: AtlanClient, + connection: Connection, + anaplan_system_dimension: AnaplanSystemDimension, +): + assert anaplan_system_dimension + assert anaplan_system_dimension.guid + assert anaplan_system_dimension.qualified_name + assert anaplan_system_dimension.name == ANAPLAN_SYSTEM_DIMENSION_NAME + assert ( + anaplan_system_dimension.connection_qualified_name == connection.qualified_name + ) + assert anaplan_system_dimension.connector_name == AtlanConnectorType.ANAPLAN.value + + +@pytest.fixture(scope="module") +def anaplan_app( + client: AtlanClient, connection: Connection +) -> Generator[AnaplanApp, None, None]: + assert connection.qualified_name + to_create = AnaplanApp.creator( + name=ANAPLAN_APP_NAME, connection_qualified_name=connection.qualified_name + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=AnaplanApp)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=AnaplanApp) + + +def test_anaplan_app( + client: AtlanClient, connection: Connection, anaplan_app: AnaplanApp +): + assert anaplan_app + assert anaplan_app.guid + assert anaplan_app.qualified_name + assert anaplan_app.name == ANAPLAN_APP_NAME + assert anaplan_app.connection_qualified_name == connection.qualified_name + assert anaplan_app.connector_name == AtlanConnectorType.ANAPLAN.value + + +@pytest.fixture(scope="module") +def anaplan_page( + client: AtlanClient, anaplan_app: AnaplanApp +) -> Generator[AnaplanPage, None, None]: + assert anaplan_app.qualified_name + to_create = AnaplanPage.creator( + name=ANAPLAN_PAGE_NAME, app_qualified_name=anaplan_app.qualified_name + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=AnaplanPage)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=AnaplanPage) + + +def test_anaplan_page( + client: AtlanClient, anaplan_app: AnaplanApp, anaplan_page: AnaplanPage +): + assert anaplan_page + assert anaplan_page.guid + assert anaplan_page.qualified_name + assert anaplan_page.name == ANAPLAN_PAGE_NAME + assert ( + anaplan_page.connection_qualified_name == anaplan_app.connection_qualified_name + ) + assert anaplan_page.connector_name == AtlanConnectorType.ANAPLAN.value + + +@pytest.fixture(scope="module") +def anaplan_page_overload( + client: AtlanClient, connection: Connection, anaplan_app: AnaplanApp +) -> Generator[AnaplanPage, None, None]: + assert connection.qualified_name + assert anaplan_app.qualified_name + to_create = AnaplanPage.creator( + name=ANAPLAN_PAGE_NAME_OVERLOAD, + app_qualified_name=anaplan_app.qualified_name, + connection_qualified_name=connection.qualified_name, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=AnaplanPage)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=AnaplanPage) + + +def test_overload_anaplan_page( + client: AtlanClient, anaplan_app: AnaplanApp, anaplan_page_overload: AnaplanPage +): + assert anaplan_page_overload + assert anaplan_page_overload.guid + assert anaplan_page_overload.qualified_name + assert anaplan_page_overload.name == ANAPLAN_PAGE_NAME_OVERLOAD + assert ( + anaplan_page_overload.connection_qualified_name + == anaplan_app.connection_qualified_name + ) + assert anaplan_page_overload.connector_name == AtlanConnectorType.ANAPLAN.value + + +@pytest.fixture(scope="module") +def anaplan_model( + client: AtlanClient, anaplan_workspace: AnaplanWorkspace +) -> Generator[AnaplanModel, None, None]: + assert anaplan_workspace.qualified_name + to_create = AnaplanModel.creator( + name=ANAPLAN_MODEL_NAME, + workspace_qualified_name=anaplan_workspace.qualified_name, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=AnaplanModel)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=AnaplanModel) + + +def test_anaplan_model( + client: AtlanClient, + anaplan_workspace: AnaplanWorkspace, + anaplan_model: AnaplanModel, +): + assert anaplan_model + assert anaplan_model.guid + assert anaplan_model.qualified_name + assert anaplan_model.name == ANAPLAN_MODEL_NAME + assert ( + anaplan_model.connection_qualified_name + == anaplan_workspace.connection_qualified_name + ) + assert anaplan_model.connector_name == AtlanConnectorType.ANAPLAN.value + + +@pytest.fixture(scope="module") +def anaplan_model_overload( + client: AtlanClient, connection: Connection, anaplan_workspace: AnaplanWorkspace +) -> Generator[AnaplanModel, None, None]: + assert connection.qualified_name + assert anaplan_workspace.qualified_name + to_create = AnaplanModel.creator( + name=ANAPLAN_MODEL_NAME_OVERLOAD, + workspace_qualified_name=anaplan_workspace.qualified_name, + connection_qualified_name=connection.qualified_name, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=AnaplanModel)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=AnaplanModel) + + +def test_overload_anaplan_model( + client: AtlanClient, + anaplan_workspace: AnaplanWorkspace, + anaplan_model_overload: AnaplanModel, +): + assert anaplan_model_overload + assert anaplan_model_overload.guid + assert anaplan_model_overload.qualified_name + assert anaplan_model_overload.name == ANAPLAN_MODEL_NAME_OVERLOAD + assert ( + anaplan_model_overload.connection_qualified_name + == anaplan_workspace.connection_qualified_name + ) + assert anaplan_model_overload.connector_name == AtlanConnectorType.ANAPLAN.value + + +@pytest.fixture(scope="module") +def anaplan_module( + client: AtlanClient, anaplan_model: AnaplanModel +) -> Generator[AnaplanModule, None, None]: + assert anaplan_model.qualified_name + to_create = AnaplanModule.creator( + name=ANAPLAN_MODULE_NAME, model_qualified_name=anaplan_model.qualified_name + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=AnaplanModule)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=AnaplanModule) + + +def test_anaplan_module( + client: AtlanClient, anaplan_model: AnaplanModel, anaplan_module: AnaplanModule +): + assert anaplan_module + assert anaplan_module.guid + assert anaplan_module.qualified_name + assert anaplan_module.name == ANAPLAN_MODULE_NAME + assert ( + anaplan_module.connection_qualified_name + == anaplan_model.connection_qualified_name + ) + assert anaplan_module.connector_name == AtlanConnectorType.ANAPLAN.value + + +@pytest.fixture(scope="module") +def anaplan_module_overload( + client: AtlanClient, connection: Connection, anaplan_model: AnaplanModel +) -> Generator[AnaplanModule, None, None]: + assert connection.qualified_name + assert anaplan_model.qualified_name + to_create = AnaplanModule.creator( + name=ANAPLAN_MODULE_NAME_OVERLOAD, + model_qualified_name=anaplan_model.qualified_name, + connection_qualified_name=connection.qualified_name, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=AnaplanModule)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=AnaplanModule) + + +def test_overload_anaplan_module( + client: AtlanClient, + anaplan_model: AnaplanModel, + anaplan_module_overload: AnaplanModule, +): + assert anaplan_module_overload + assert anaplan_module_overload.guid + assert anaplan_module_overload.qualified_name + assert anaplan_module_overload.name == ANAPLAN_MODULE_NAME_OVERLOAD + assert ( + anaplan_module_overload.connection_qualified_name + == anaplan_model.connection_qualified_name + ) + assert anaplan_module_overload.connector_name == AtlanConnectorType.ANAPLAN.value + + +@pytest.fixture(scope="module") +def anaplan_list( + client: AtlanClient, anaplan_model: AnaplanModel +) -> Generator[AnaplanList, None, None]: + assert anaplan_model.qualified_name + to_create = AnaplanList.creator( + name=ANAPLAN_LIST_NAME, model_qualified_name=anaplan_model.qualified_name + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=AnaplanList)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=AnaplanList) + + +def test_anaplan_list( + client: AtlanClient, anaplan_model: AnaplanModel, anaplan_list: AnaplanList +): + assert anaplan_list + assert anaplan_list.guid + assert anaplan_list.qualified_name + assert anaplan_list.name == ANAPLAN_LIST_NAME + assert ( + anaplan_list.connection_qualified_name + == anaplan_model.connection_qualified_name + ) + assert anaplan_list.connector_name == AtlanConnectorType.ANAPLAN.value + + +@pytest.fixture(scope="module") +def anaplan_list_overload( + client: AtlanClient, connection: Connection, anaplan_model: AnaplanModel +) -> Generator[AnaplanList, None, None]: + assert connection.qualified_name + assert anaplan_model.qualified_name + to_create = AnaplanList.creator( + name=ANAPLAN_LIST_NAME_OVERLOAD, + model_qualified_name=anaplan_model.qualified_name, + connection_qualified_name=connection.qualified_name, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=AnaplanList)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=AnaplanList) + + +def test_overload_anaplan_list( + client: AtlanClient, anaplan_model: AnaplanModel, anaplan_list_overload: AnaplanList +): + assert anaplan_list_overload + assert anaplan_list_overload.guid + assert anaplan_list_overload.qualified_name + assert anaplan_list_overload.name == ANAPLAN_LIST_NAME_OVERLOAD + assert ( + anaplan_list_overload.connection_qualified_name + == anaplan_model.connection_qualified_name + ) + assert anaplan_list_overload.connector_name == AtlanConnectorType.ANAPLAN.value + + +@pytest.fixture(scope="module") +def anaplan_dimension( + client: AtlanClient, anaplan_model: AnaplanModel +) -> Generator[AnaplanDimension, None, None]: + assert anaplan_model.qualified_name + to_create = AnaplanDimension.creator( + name=ANAPLAN_DIMENSION_NAME, model_qualified_name=anaplan_model.qualified_name + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=AnaplanDimension)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=AnaplanDimension) + + +def test_anaplan_dimension( + client: AtlanClient, + anaplan_model: AnaplanModel, + anaplan_dimension: AnaplanDimension, +): + assert anaplan_dimension + assert anaplan_dimension.guid + assert anaplan_dimension.qualified_name + assert anaplan_dimension.name == ANAPLAN_DIMENSION_NAME + assert ( + anaplan_dimension.connection_qualified_name + == anaplan_model.connection_qualified_name + ) + assert anaplan_dimension.connector_name == AtlanConnectorType.ANAPLAN.value + + +@pytest.fixture(scope="module") +def anaplan_dimension_overload( + client: AtlanClient, connection: Connection, anaplan_model: AnaplanModel +) -> Generator[AnaplanDimension, None, None]: + assert connection.qualified_name + assert anaplan_model.qualified_name + to_create = AnaplanDimension.creator( + name=ANAPLAN_DIMENSION_NAME_OVERLOAD, + model_qualified_name=anaplan_model.qualified_name, + connection_qualified_name=connection.qualified_name, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=AnaplanDimension)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=AnaplanDimension) + + +def test_overload_anaplan_dimension( + client: AtlanClient, + anaplan_model: AnaplanModel, + anaplan_dimension_overload: AnaplanDimension, +): + assert anaplan_dimension_overload + assert anaplan_dimension_overload.guid + assert anaplan_dimension_overload.qualified_name + assert anaplan_dimension_overload.name == ANAPLAN_DIMENSION_NAME_OVERLOAD + assert ( + anaplan_dimension_overload.connection_qualified_name + == anaplan_model.connection_qualified_name + ) + assert anaplan_dimension_overload.connector_name == AtlanConnectorType.ANAPLAN.value + + +@pytest.fixture(scope="module") +def anaplan_lineitem( + client: AtlanClient, anaplan_module: AnaplanModule +) -> Generator[AnaplanLineItem, None, None]: + assert anaplan_module.qualified_name + to_create = AnaplanLineItem.creator( + name=ANAPLAN_LINEITEM_NAME, module_qualified_name=anaplan_module.qualified_name + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=AnaplanLineItem)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=AnaplanLineItem) + + +def test_anaplan_lineitem( + client: AtlanClient, + anaplan_module: AnaplanModule, + anaplan_lineitem: AnaplanLineItem, +): + assert anaplan_lineitem + assert anaplan_lineitem.guid + assert anaplan_lineitem.qualified_name + assert anaplan_lineitem.name == ANAPLAN_LINEITEM_NAME + assert ( + anaplan_lineitem.connection_qualified_name + == anaplan_module.connection_qualified_name + ) + assert anaplan_lineitem.connector_name == AtlanConnectorType.ANAPLAN.value + + +@pytest.fixture(scope="module") +def anaplan_lineitem_overload( + client: AtlanClient, connection: Connection, anaplan_module: AnaplanModule +) -> Generator[AnaplanLineItem, None, None]: + assert connection.qualified_name + assert anaplan_module.qualified_name + to_create = AnaplanLineItem.creator( + name=ANAPLAN_LINEITEM_NAME_OVERLOAD, + module_qualified_name=anaplan_module.qualified_name, + connection_qualified_name=connection.qualified_name, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=AnaplanLineItem)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=AnaplanLineItem) + + +def test_overload_anaplan_lineitem( + client: AtlanClient, + anaplan_module: AnaplanModule, + anaplan_lineitem_overload: AnaplanLineItem, +): + assert anaplan_lineitem_overload + assert anaplan_lineitem_overload.guid + assert anaplan_lineitem_overload.qualified_name + assert anaplan_lineitem_overload.name == ANAPLAN_LINEITEM_NAME_OVERLOAD + assert ( + anaplan_lineitem_overload.connection_qualified_name + == anaplan_module.connection_qualified_name + ) + assert anaplan_lineitem_overload.connector_name == AtlanConnectorType.ANAPLAN.value + + +@pytest.fixture(scope="module") +def anaplan_view( + client: AtlanClient, anaplan_module: AnaplanModule +) -> Generator[AnaplanView, None, None]: + assert anaplan_module.qualified_name + to_create = AnaplanView.creator( + name=ANAPLAN_VIEW_NAME, module_qualified_name=anaplan_module.qualified_name + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=AnaplanView)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=AnaplanView) + + +def test_anaplan_view( + client: AtlanClient, anaplan_module: AnaplanModule, anaplan_view: AnaplanView +): + assert anaplan_view + assert anaplan_view.guid + assert anaplan_view.qualified_name + assert anaplan_view.name == ANAPLAN_VIEW_NAME + assert ( + anaplan_view.connection_qualified_name + == anaplan_module.connection_qualified_name + ) + assert anaplan_view.connector_name == AtlanConnectorType.ANAPLAN.value + + +@pytest.fixture(scope="module") +def anaplan_view_overload( + client: AtlanClient, connection: Connection, anaplan_module: AnaplanModule +) -> Generator[AnaplanView, None, None]: + assert connection.qualified_name + assert anaplan_module.qualified_name + to_create = AnaplanView.creator( + name=ANAPLAN_VIEW_NAME_OVERLOAD, + module_qualified_name=anaplan_module.qualified_name, + connection_qualified_name=connection.qualified_name, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=AnaplanView)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=AnaplanView) + + +def test_overload_anaplan_view( + client: AtlanClient, + anaplan_module: AnaplanModule, + anaplan_view_overload: AnaplanView, +): + assert anaplan_view_overload + assert anaplan_view_overload.guid + assert anaplan_view_overload.qualified_name + assert anaplan_view_overload.name == ANAPLAN_VIEW_NAME_OVERLOAD + assert ( + anaplan_view_overload.connection_qualified_name + == anaplan_module.connection_qualified_name + ) + assert anaplan_view_overload.connector_name == AtlanConnectorType.ANAPLAN.value + + +# here +def test_update_anaplan_view( + client: AtlanClient, + connection: Connection, + anaplan_module: AnaplanModule, + anaplan_view: AnaplanView, +): + assert anaplan_view.qualified_name + assert anaplan_view.name + updated = client.asset.update_certificate( + asset_type=AnaplanView, + qualified_name=anaplan_view.qualified_name, + name=anaplan_view.name, + certificate_status=CERTIFICATE_STATUS, + message=CERTIFICATE_MESSAGE, + ) + assert updated + assert updated.certificate_status_message == CERTIFICATE_MESSAGE + assert anaplan_view.qualified_name + assert anaplan_view.name + updated = client.asset.update_announcement( + asset_type=AnaplanView, + qualified_name=anaplan_view.qualified_name, + name=anaplan_view.name, + announcement=Announcement( + announcement_type=ANNOUNCEMENT_TYPE, + announcement_title=ANNOUNCEMENT_TITLE, + announcement_message=ANNOUNCEMENT_MESSAGE, + ), + ) + assert updated + assert updated.announcement_type == ANNOUNCEMENT_TYPE.value + assert updated.announcement_title == ANNOUNCEMENT_TITLE + assert updated.announcement_message == ANNOUNCEMENT_MESSAGE + + +@pytest.mark.order(after="test_update_anaplan_view") +def test_retrieve_anaplan_view( + client: AtlanClient, + connection: Connection, + anaplan_module: AnaplanModule, + anaplan_view: AnaplanView, +): + b = client.asset.get_by_guid(anaplan_view.guid, asset_type=AnaplanView) + assert b + assert not b.is_incomplete + assert b.guid == anaplan_view.guid + assert b.qualified_name == anaplan_view.qualified_name + assert b.name == anaplan_view.name + assert b.connector_name == anaplan_view.connector_name + assert b.connection_qualified_name == anaplan_view.connection_qualified_name + assert b.certificate_status == CERTIFICATE_STATUS + assert b.certificate_status_message == CERTIFICATE_MESSAGE + + +@pytest.mark.order(after="test_retrieve_anaplan_view") +def test_update_anaplan_view_again( + client: AtlanClient, + connection: Connection, + anaplan_module: AnaplanModule, + anaplan_view: AnaplanView, +): + assert anaplan_view.qualified_name + assert anaplan_view.name + updated = client.asset.remove_certificate( + asset_type=AnaplanView, + qualified_name=anaplan_view.qualified_name, + name=anaplan_view.name, + ) + assert updated + assert not updated.certificate_status + assert not updated.certificate_status_message + assert anaplan_view.qualified_name + updated = client.asset.remove_announcement( + asset_type=AnaplanView, + qualified_name=anaplan_view.qualified_name, + name=anaplan_view.name, + ) + assert updated + assert not updated.announcement_type + assert not updated.announcement_title + assert not updated.announcement_message + + +@pytest.mark.order(after="test_update_anaplan_view_again") +def test_delete_anaplan_view( + client: AtlanClient, + connection: Connection, + anaplan_module: AnaplanModule, + anaplan_view: AnaplanView, +): + response = client.asset.delete_by_guid(anaplan_view.guid) + assert response + assert not response.assets_created(asset_type=AnaplanView) + assert not response.assets_updated(asset_type=AnaplanView) + deleted = response.assets_deleted(asset_type=AnaplanView) + assert deleted + assert len(deleted) == 1 + assert deleted[0].guid == anaplan_view.guid + assert deleted[0].qualified_name == anaplan_view.qualified_name + assert deleted[0].delete_handler == "SOFT" + assert deleted[0].status == EntityStatus.DELETED + + +@pytest.mark.order(after="test_delete_anaplan_view") +def test_read_deleted_anaplan_view( + client: AtlanClient, + connection: Connection, + anaplan_module: AnaplanModule, + anaplan_view: AnaplanView, +): + deleted = client.asset.get_by_guid(anaplan_view.guid, asset_type=AnaplanView) + assert deleted + assert deleted.guid == anaplan_view.guid + assert deleted.qualified_name == anaplan_view.qualified_name + assert deleted.status == EntityStatus.DELETED + + +@pytest.mark.order(after="test_read_deleted_anaplan_view") +def test_restore_anaplan_view( + client: AtlanClient, + connection: Connection, + anaplan_module: AnaplanModule, + anaplan_view: AnaplanView, +): + assert anaplan_view.qualified_name + assert client.asset.restore( + asset_type=AnaplanView, qualified_name=anaplan_view.qualified_name + ) + assert anaplan_view.qualified_name + restored = client.asset.get_by_qualified_name( + asset_type=AnaplanView, qualified_name=anaplan_view.qualified_name + ) + assert restored + assert restored.guid == anaplan_view.guid + assert restored.qualified_name == anaplan_view.qualified_name + assert restored.status == EntityStatus.ACTIVE diff --git a/tests_v9/integration/api_asset_test.py b/tests_v9/integration/api_asset_test.py new file mode 100644 index 000000000..e3a06c744 --- /dev/null +++ b/tests_v9/integration/api_asset_test.py @@ -0,0 +1,1088 @@ +from typing import Generator + +import pytest +from msgspec import UNSET + +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.model.assets import ( + APIField, + APIObject, + APIPath, + APIQuery, + APISpec, + Connection, +) +from pyatlan_v9.model.core import Announcement +from pyatlan_v9.model.enums import ( + AnnouncementType, + APIQueryParamTypeEnum, + AtlanConnectorType, + CertificateStatus, + EntityStatus, +) +from tests_v9.integration.client import TestId, delete_asset +from tests_v9.integration.connection_test import create_connection + +MODULE_NAME = TestId.make_unique("API") + +CONNECTOR_TYPE = AtlanConnectorType.API +API_SPEC_NAME = f"{MODULE_NAME}-api-spec" +API_PATH_NAME = "/api/path" +API_PATH_NAME_OVERLOAD = "/api/path/overload" +API_PATH_RAW_URI = "/api/path" +API_PATH_RAW_URI_OVERLOAD = "/api/path/overload" +CERTIFICATE_STATUS = CertificateStatus.VERIFIED +CERTIFICATE_MESSAGE = "Automated testing of the Python SDK." +ANNOUNCEMENT_TYPE = AnnouncementType.INFORMATION +ANNOUNCEMENT_TITLE = "Python SDK testing." +ANNOUNCEMENT_MESSAGE = "Automated testing of the Python SDK." +API_OBJECT_NAME = f"{MODULE_NAME}-api-object" +API_OBJECT_OVERLOAD_NAME = f"{MODULE_NAME}-api-object-overload" +API_OBJECT_FIELD_COUNT = 2 +API_QUERY_NAME = "api-query" +API_QUERY_OVERLOAD_1_NAME = f"{MODULE_NAME}-api-query-overload-1" +API_QUERY_OVERLOAD_2_NAME = f"{MODULE_NAME}-api-query-overload-2" +API_QUERY_OVERLOAD_3_NAME = f"{MODULE_NAME}-api-query-overload-3" +API_QUERY_INPUT_FIELD_COUNT = 1 +API_QUERY_OUTPUT_TYPE = "String" +API_QUERY_OUTPUT_TYPE_SECONDARY = "String" +API_QUERY_IS_OBJECT_REFERENCE = True +API_FIELD_NAME = f"{MODULE_NAME}-api-field" +API_FIELD_PARENT_QUERY_NAME = f"{MODULE_NAME}-api-field-pq" +API_FIELD_OVERLOAD_1_NAME = f"{MODULE_NAME}-api-field-overload-1" +API_FIELD_OVERLOAD_2_NAME = f"{MODULE_NAME}-api-field-overload-2" +API_FIELD_OVERLOAD_3_NAME = f"{MODULE_NAME}-api-field-overload-3" +API_FIELD_OVERLOAD_4_NAME = f"{MODULE_NAME}-api-field-overload-4" +API_FIELD_TYPE = "Int" +API_FIELD_TYPE_SECONDARY = "Int" +API_FIELD_IS_OBJECT_REFERENCE = True +API_FIELD_REFERENCE_OBJECT_NAME = f"{MODULE_NAME}-api-object-reference" + + +def _assert_announcement_cleared(updated): + assert updated.announcement_type in (UNSET, None, "") + assert updated.announcement_title in (UNSET, None, "") + assert updated.announcement_message in (UNSET, None, "") + + +@pytest.fixture(scope="module") +def connection(client: AtlanClient) -> Generator[Connection, None, None]: + result = create_connection( + client=client, name=MODULE_NAME, connector_type=CONNECTOR_TYPE + ) + yield result + delete_asset(client, guid=result.guid, asset_type=Connection) + + +@pytest.fixture(scope="module") +def api_spec( + client: AtlanClient, connection: Connection +) -> Generator[APISpec, None, None]: + assert connection.qualified_name + to_create = APISpec.creator( + name=API_SPEC_NAME, connection_qualified_name=connection.qualified_name + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=APISpec)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=APISpec) + + +def test_api_spec(client: AtlanClient, connection: Connection, api_spec: APISpec): + assert api_spec + assert api_spec.guid + assert api_spec.qualified_name + assert api_spec.name == API_SPEC_NAME + assert api_spec.connection_qualified_name == connection.qualified_name + assert api_spec.connector_name == AtlanConnectorType.API.value + + +@pytest.fixture(scope="module") +def api_path(client: AtlanClient, api_spec: APISpec) -> Generator[APIPath, None, None]: + assert api_spec.qualified_name + to_create = APIPath.creator( + path_raw_uri=API_PATH_RAW_URI, + spec_qualified_name=api_spec.qualified_name, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=APIPath)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=APIPath) + + +def test_api_path(client: AtlanClient, api_spec: APISpec, api_path: APIPath): + assert api_path + assert api_path.guid + assert api_path.qualified_name + assert api_path.api_spec_qualified_name + assert api_path.api_path_raw_u_r_i == API_PATH_RAW_URI + assert api_path.name == API_PATH_NAME + assert api_path.connection_qualified_name == api_spec.connection_qualified_name + assert api_path.connector_name == AtlanConnectorType.API.value + + +@pytest.fixture(scope="module") +def api_path_overload( + client: AtlanClient, api_spec: APISpec +) -> Generator[APIPath, None, None]: + assert api_spec.qualified_name + to_create = APIPath.creator( + path_raw_uri=API_PATH_RAW_URI_OVERLOAD, + spec_qualified_name=api_spec.qualified_name, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=APIPath)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=APIPath) + + +def test_overload_api_path( + client: AtlanClient, api_spec: APISpec, api_path_overload: APIPath +): + assert api_path_overload + assert api_path_overload.guid + assert api_path_overload.qualified_name + assert api_path_overload.api_spec_qualified_name + assert api_path_overload.api_path_raw_u_r_i == API_PATH_RAW_URI_OVERLOAD + assert api_path_overload.name == API_PATH_NAME_OVERLOAD + assert ( + api_path_overload.connection_qualified_name + == api_spec.connection_qualified_name + ) + assert api_path_overload.connector_name == AtlanConnectorType.API.value + + +# here +def test_update_api_path( + client: AtlanClient, connection: Connection, api_spec: APISpec, api_path: APIPath +): + assert api_path.qualified_name + assert api_path.name + updated = client.asset.update_certificate( + asset_type=APIPath, + qualified_name=api_path.qualified_name, + name=api_path.name, + certificate_status=CERTIFICATE_STATUS, + message=CERTIFICATE_MESSAGE, + ) + assert updated + assert updated.certificate_status_message == CERTIFICATE_MESSAGE + assert api_path.qualified_name + assert api_path.name + updated = client.asset.update_announcement( + asset_type=APIPath, + qualified_name=api_path.qualified_name, + name=api_path.name, + announcement=Announcement( + announcement_type=ANNOUNCEMENT_TYPE, + announcement_title=ANNOUNCEMENT_TITLE, + announcement_message=ANNOUNCEMENT_MESSAGE, + ), + ) + assert updated + if updated.announcement_type is not UNSET: + assert updated.announcement_type == ANNOUNCEMENT_TYPE.value + assert updated.announcement_title == ANNOUNCEMENT_TITLE + assert updated.announcement_message == ANNOUNCEMENT_MESSAGE + + +@pytest.mark.order(after="test_update_api_path") +def test_retrieve_api_path( + client: AtlanClient, connection: Connection, api_spec: APISpec, api_path: APIPath +): + b = client.asset.get_by_guid( + api_path.guid, asset_type=APIPath, ignore_relationships=False + ) + assert b + assert not b.is_incomplete + assert b.guid == api_path.guid + assert b.qualified_name == api_path.qualified_name + assert b.name == api_path.name + assert b.connector_name == api_path.connector_name + assert b.connection_qualified_name == api_path.connection_qualified_name + assert b.api_path_raw_u_r_i == api_path.api_path_raw_u_r_i + assert b.certificate_status == CERTIFICATE_STATUS + assert b.certificate_status_message == CERTIFICATE_MESSAGE + + +@pytest.mark.order(after="test_retrieve_api_path") +def test_update_api_path_again( + client: AtlanClient, connection: Connection, api_spec: APISpec, api_path: APIPath +): + assert api_path.qualified_name + assert api_path.name + updated = client.asset.remove_certificate( + asset_type=APIPath, + qualified_name=api_path.qualified_name, + name=api_path.name, + ) + assert updated + assert not updated.certificate_status + assert not updated.certificate_status_message + assert api_path.qualified_name + updated = client.asset.remove_announcement( + asset_type=APIPath, + qualified_name=api_path.qualified_name, + name=api_path.name, + ) + assert updated + _assert_announcement_cleared(updated) + + +@pytest.mark.order(after="test_update_api_path_again") +def test_delete_api_path( + client: AtlanClient, connection: Connection, api_spec: APISpec, api_path: APIPath +): + response = client.asset.delete_by_guid(api_path.guid) + assert response + assert not response.assets_created(asset_type=APIPath) + assert not response.assets_updated(asset_type=APIPath) + deleted = response.assets_deleted(asset_type=APIPath) + assert deleted + assert len(deleted) == 1 + assert deleted[0].guid == api_path.guid + assert deleted[0].qualified_name == api_path.qualified_name + assert deleted[0].delete_handler == "SOFT" + assert deleted[0].status == EntityStatus.DELETED + + +@pytest.mark.order(after="test_delete_api_path") +def test_read_deleted_api_path( + client: AtlanClient, connection: Connection, api_spec: APISpec, api_path: APIPath +): + deleted = client.asset.get_by_guid( + api_path.guid, asset_type=APIPath, ignore_relationships=False + ) + assert deleted + assert deleted.guid == api_path.guid + assert deleted.qualified_name == api_path.qualified_name + assert deleted.status == EntityStatus.DELETED + + +@pytest.mark.order(after="test_read_deleted_api_path") +def test_restore_path( + client: AtlanClient, connection: Connection, api_spec: APISpec, api_path: APIPath +): + assert api_path.qualified_name + assert client.asset.restore( + asset_type=APIPath, qualified_name=api_path.qualified_name + ) + assert api_path.qualified_name + restored = client.asset.get_by_qualified_name( + asset_type=APIPath, + qualified_name=api_path.qualified_name, + ignore_relationships=False, + ) + assert restored + assert restored.guid == api_path.guid + assert restored.qualified_name == api_path.qualified_name + assert restored.status == EntityStatus.ACTIVE + + +@pytest.fixture(scope="module") +def api_object( + client: AtlanClient, connection: Connection +) -> Generator[APIObject, None, None]: + assert connection.qualified_name + to_create = APIObject.creator( + name=API_OBJECT_NAME, connection_qualified_name=connection.qualified_name + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=APIObject)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=APIObject) + + +def test_api_object(client: AtlanClient, connection: Connection, api_object: APIObject): + assert api_object + assert api_object.guid + assert api_object.name == API_OBJECT_NAME + assert api_object.connection_qualified_name == connection.qualified_name + assert api_object.connector_name == AtlanConnectorType.API.value + assert api_object.qualified_name == f"{connection.qualified_name}/{API_OBJECT_NAME}" + + +@pytest.fixture(scope="module") +def api_object_overload( + client: AtlanClient, connection: Connection +) -> Generator[APIObject, None, None]: + assert connection.qualified_name + to_create = APIObject.creator( + name=API_OBJECT_OVERLOAD_NAME, + connection_qualified_name=connection.qualified_name, + api_field_count=API_OBJECT_FIELD_COUNT, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=APIObject)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=APIObject) + + +def test_api_object_overload( + client: AtlanClient, connection: Connection, api_object_overload: APIObject +): + assert api_object_overload + assert api_object_overload.guid + assert ( + api_object_overload.qualified_name + == f"{connection.qualified_name}/{API_OBJECT_OVERLOAD_NAME}" + ) + assert api_object_overload.name == API_OBJECT_OVERLOAD_NAME + assert api_object_overload.connection_qualified_name == connection.qualified_name + assert api_object_overload.api_field_count == API_OBJECT_FIELD_COUNT + assert api_object_overload.connector_name == AtlanConnectorType.API.value + + +def test_update_api_object( + client: AtlanClient, connection: Connection, api_object_overload: APIObject +): + assert api_object_overload.qualified_name + assert api_object_overload.name + updated = client.asset.update_certificate( + asset_type=APIObject, + qualified_name=api_object_overload.qualified_name, + name=api_object_overload.name, + certificate_status=CERTIFICATE_STATUS, + message=CERTIFICATE_MESSAGE, + ) + assert updated + assert updated.certificate_status_message == CERTIFICATE_MESSAGE + assert api_object_overload.qualified_name + assert api_object_overload.name + updated = client.asset.update_announcement( + asset_type=APIObject, + qualified_name=api_object_overload.qualified_name, + name=api_object_overload.name, + announcement=Announcement( + announcement_type=ANNOUNCEMENT_TYPE, + announcement_title=ANNOUNCEMENT_TITLE, + announcement_message=ANNOUNCEMENT_MESSAGE, + ), + ) + assert updated + if updated.announcement_type is not UNSET: + assert updated.announcement_type == ANNOUNCEMENT_TYPE.value + assert updated.announcement_title == ANNOUNCEMENT_TITLE + assert updated.announcement_message == ANNOUNCEMENT_MESSAGE + + +@pytest.mark.order(after="test_update_api_object") +def test_retrieve_api_object( + client: AtlanClient, connection: Connection, api_object_overload: APIObject +): + b = client.asset.get_by_guid( + api_object_overload.guid, asset_type=APIObject, ignore_relationships=False + ) + assert b + assert not b.is_incomplete + assert b.guid == api_object_overload.guid + assert b.qualified_name == api_object_overload.qualified_name + assert b.name == api_object_overload.name + assert b.connector_name == api_object_overload.connector_name + assert b.connection_qualified_name == api_object_overload.connection_qualified_name + assert b.certificate_status == CERTIFICATE_STATUS + assert b.certificate_status_message == CERTIFICATE_MESSAGE + + +@pytest.mark.order(after="test_retrieve_api_object") +def test_delete_api_object( + client: AtlanClient, connection: Connection, api_object_overload: APIObject +): + response = client.asset.delete_by_guid(api_object_overload.guid) + assert response + assert not response.assets_created(asset_type=APIObject) + assert not response.assets_updated(asset_type=APIObject) + deleted = response.assets_deleted(asset_type=APIObject) + assert deleted + assert len(deleted) == 1 + assert deleted[0].guid == api_object_overload.guid + assert deleted[0].qualified_name == api_object_overload.qualified_name + assert deleted[0].delete_handler == "SOFT" + assert deleted[0].status == EntityStatus.DELETED + + +@pytest.mark.order(after="test_delete_api_object") +def test_read_deleted_api_object( + client: AtlanClient, connection: Connection, api_object_overload: APIObject +): + # Running get_by_qualified_name with attributes to use FluentSearch behind the scenes + assert api_object_overload.qualified_name + deleted = client.asset.get_by_qualified_name( + api_object_overload.qualified_name, asset_type=APIObject, attributes=["name"] + ) + assert deleted + assert deleted.guid == api_object_overload.guid + assert deleted.qualified_name == api_object_overload.qualified_name + assert deleted.status == EntityStatus.DELETED + + +@pytest.mark.order(after="test_read_deleted_api_object") +def test_restore_object( + client: AtlanClient, connection: Connection, api_object_overload: APIObject +): + assert api_object_overload.qualified_name + assert client.asset.restore( + asset_type=APIObject, qualified_name=api_object_overload.qualified_name + ) + assert api_object_overload.qualified_name + restored = client.asset.get_by_qualified_name( + asset_type=APIObject, + qualified_name=api_object_overload.qualified_name, + ignore_relationships=False, + ) + assert restored + assert restored.guid == api_object_overload.guid + assert restored.qualified_name == api_object_overload.qualified_name + assert restored.status == EntityStatus.ACTIVE + + +@pytest.fixture(scope="module") +def api_query( + client: AtlanClient, connection: Connection +) -> Generator[APIQuery, None, None]: + assert connection.qualified_name + to_create = APIQuery.creator( + name=API_QUERY_NAME, connection_qualified_name=connection.qualified_name + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=APIQuery)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=APIQuery) + + +def test_api_query(client: AtlanClient, connection: Connection, api_query: APIQuery): + assert api_query + assert api_query.guid + assert api_query.qualified_name == f"{connection.qualified_name}/{API_QUERY_NAME}" + assert api_query.name == API_QUERY_NAME + assert api_query.connection_qualified_name == connection.qualified_name + assert api_query.connector_name == AtlanConnectorType.API.value + + +@pytest.fixture(scope="module") +def api_query_overload_1( + client: AtlanClient, connection: Connection +) -> Generator[APIQuery, None, None]: + assert connection.qualified_name + to_create = APIQuery.creator( + name=API_QUERY_OVERLOAD_1_NAME, + connection_qualified_name=connection.qualified_name, + api_input_field_count=API_QUERY_INPUT_FIELD_COUNT, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=APIQuery)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=APIQuery) + + +def test_api_query_overload_1( + client: AtlanClient, connection: Connection, api_query_overload_1: APIQuery +): + assert api_query_overload_1 + assert api_query_overload_1.guid + assert ( + api_query_overload_1.qualified_name + == f"{connection.qualified_name}/{API_QUERY_OVERLOAD_1_NAME}" + ) + assert api_query_overload_1.name == API_QUERY_OVERLOAD_1_NAME + assert api_query_overload_1.connection_qualified_name == connection.qualified_name + assert api_query_overload_1.api_input_field_count == API_QUERY_INPUT_FIELD_COUNT + assert api_query_overload_1.connector_name == AtlanConnectorType.API.value + + +@pytest.fixture(scope="module") +def api_query_overload_2( + client: AtlanClient, connection: Connection +) -> Generator[APIQuery, None, None]: + assert connection.qualified_name + to_create = APIQuery.creator( + name=API_QUERY_OVERLOAD_2_NAME, + connection_qualified_name=connection.qualified_name, + api_input_field_count=API_QUERY_INPUT_FIELD_COUNT, + api_query_output_type=API_QUERY_OUTPUT_TYPE, + api_query_output_type_secondary=API_QUERY_OUTPUT_TYPE_SECONDARY, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=APIQuery)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=APIQuery) + + +def test_api_query_overload_2( + client: AtlanClient, connection: Connection, api_query_overload_2: APIQuery +): + assert api_query_overload_2 + assert api_query_overload_2.guid + assert ( + api_query_overload_2.qualified_name + == f"{connection.qualified_name}/{API_QUERY_OVERLOAD_2_NAME}" + ) + assert api_query_overload_2.name == API_QUERY_OVERLOAD_2_NAME + assert api_query_overload_2.connection_qualified_name == connection.qualified_name + assert api_query_overload_2.api_input_field_count == API_QUERY_INPUT_FIELD_COUNT + assert api_query_overload_2.api_query_output_type == API_QUERY_OUTPUT_TYPE + assert ( + api_query_overload_2.api_query_output_type_secondary + == API_QUERY_OUTPUT_TYPE_SECONDARY + ) + assert api_query_overload_2.connector_name == AtlanConnectorType.API.value + + +@pytest.fixture(scope="module") +def api_query_overload_3( + client: AtlanClient, connection: Connection, api_object: APIObject +) -> Generator[APIQuery, None, None]: + assert connection.qualified_name + assert api_object.qualified_name + to_create = APIQuery.creator( + name=API_QUERY_OVERLOAD_3_NAME, + connection_qualified_name=connection.qualified_name, + api_input_field_count=API_QUERY_INPUT_FIELD_COUNT, + api_query_output_type=API_QUERY_OUTPUT_TYPE, + api_query_output_type_secondary=API_QUERY_OUTPUT_TYPE_SECONDARY, + is_object_reference=API_QUERY_IS_OBJECT_REFERENCE, + reference_api_object_qualified_name=api_object.qualified_name, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=APIQuery)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=APIQuery) + + +def test_api_query_overload_3( + client: AtlanClient, + connection: Connection, + api_query_overload_3: APIQuery, + api_object: APIObject, +): + assert api_query_overload_3 + assert api_query_overload_3.guid + assert ( + api_query_overload_3.qualified_name + == f"{connection.qualified_name}/{API_QUERY_OVERLOAD_3_NAME}" + ) + assert api_query_overload_3.name == API_QUERY_OVERLOAD_3_NAME + assert api_query_overload_3.connection_qualified_name == connection.qualified_name + assert api_query_overload_3.api_input_field_count == API_QUERY_INPUT_FIELD_COUNT + assert api_query_overload_3.api_query_output_type == API_QUERY_OUTPUT_TYPE + assert ( + api_query_overload_3.api_query_output_type_secondary + == API_QUERY_OUTPUT_TYPE_SECONDARY + ) + assert api_query_overload_3.api_is_object_reference == API_QUERY_IS_OBJECT_REFERENCE + assert api_query_overload_3.api_object_qualified_name == api_object.qualified_name + assert api_query_overload_3.connector_name == AtlanConnectorType.API.value + + +def test_update_api_query( + client: AtlanClient, connection: Connection, api_query_overload_3: APIQuery +): + assert api_query_overload_3.qualified_name + assert api_query_overload_3.name + updated = client.asset.update_certificate( + asset_type=APIQuery, + qualified_name=api_query_overload_3.qualified_name, + name=api_query_overload_3.name, + certificate_status=CERTIFICATE_STATUS, + message=CERTIFICATE_MESSAGE, + ) + assert updated + assert updated.certificate_status_message == CERTIFICATE_MESSAGE + assert api_query_overload_3.qualified_name + assert api_query_overload_3.name + updated = client.asset.update_announcement( + asset_type=APIQuery, + qualified_name=api_query_overload_3.qualified_name, + name=api_query_overload_3.name, + announcement=Announcement( + announcement_type=ANNOUNCEMENT_TYPE, + announcement_title=ANNOUNCEMENT_TITLE, + announcement_message=ANNOUNCEMENT_MESSAGE, + ), + ) + assert updated + if updated.announcement_type is not UNSET: + assert updated.announcement_type == ANNOUNCEMENT_TYPE.value + assert updated.announcement_title == ANNOUNCEMENT_TITLE + assert updated.announcement_message == ANNOUNCEMENT_MESSAGE + + +@pytest.mark.order(after="test_update_api_query") +def test_retrieve_api_query( + client: AtlanClient, connection: Connection, api_query_overload_3: APIQuery +): + b = client.asset.get_by_guid( + api_query_overload_3.guid, asset_type=APIQuery, ignore_relationships=False + ) + assert b + assert not b.is_incomplete + assert b.guid == api_query_overload_3.guid + assert b.qualified_name == api_query_overload_3.qualified_name + assert b.name == api_query_overload_3.name + assert b.connector_name == api_query_overload_3.connector_name + assert b.connection_qualified_name == api_query_overload_3.connection_qualified_name + assert b.certificate_status == CERTIFICATE_STATUS + assert b.certificate_status_message == CERTIFICATE_MESSAGE + + +@pytest.mark.order(after="test_retrieve_api_query") +def test_delete_api_query( + client: AtlanClient, connection: Connection, api_query_overload_3: APIQuery +): + response = client.asset.delete_by_guid(api_query_overload_3.guid) + assert response + assert not response.assets_created(asset_type=APIQuery) + assert not response.assets_updated(asset_type=APIQuery) + deleted = response.assets_deleted(asset_type=APIQuery) + assert deleted + assert len(deleted) == 1 + assert deleted[0].guid == api_query_overload_3.guid + assert deleted[0].qualified_name == api_query_overload_3.qualified_name + assert deleted[0].delete_handler == "SOFT" + assert deleted[0].status == EntityStatus.DELETED + + +@pytest.mark.order(after="test_delete_api_query") +def test_read_deleted_api_query( + client: AtlanClient, connection: Connection, api_query_overload_3: APIQuery +): + # Running get_by_guid with attributes to use FluentSearch behind the scenes + deleted = client.asset.get_by_guid( + api_query_overload_3.guid, asset_type=APIQuery, attributes=["name"] + ) + assert deleted + assert deleted.guid == api_query_overload_3.guid + assert deleted.qualified_name == api_query_overload_3.qualified_name + assert deleted.status == EntityStatus.DELETED + + +@pytest.mark.order(after="test_read_deleted_api_query") +def test_restore_query( + client: AtlanClient, connection: Connection, api_query_overload_3: APIQuery +): + assert api_query_overload_3.qualified_name + assert client.asset.restore( + asset_type=APIQuery, qualified_name=api_query_overload_3.qualified_name + ) + assert api_query_overload_3.qualified_name + restored = client.asset.get_by_qualified_name( + asset_type=APIQuery, + qualified_name=api_query_overload_3.qualified_name, + ignore_relationships=False, + ) + assert restored + assert restored.guid == api_query_overload_3.guid + assert restored.qualified_name == api_query_overload_3.qualified_name + assert restored.status == EntityStatus.ACTIVE + + +@pytest.fixture(scope="module") +def api_field_parent_object( + client: AtlanClient, connection: Connection, api_object: APIObject +) -> Generator[APIField, None, None]: + assert connection.qualified_name + to_create = APIField.creator( + name=API_FIELD_NAME, + parent_api_object_qualified_name=api_object.qualified_name, + parent_api_query_qualified_name=None, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=APIField)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=APIField) + + +def test_api_field_parent_object( + client: AtlanClient, + connection: Connection, + api_field_parent_object: APIField, + api_object: APIObject, +): + assert api_field_parent_object + assert api_field_parent_object.guid + assert ( + api_field_parent_object.qualified_name + == f"{api_object.qualified_name}/{API_FIELD_NAME}" + ) + assert api_field_parent_object.name == API_FIELD_NAME + assert ( + api_field_parent_object.connection_qualified_name == connection.qualified_name + ) + assert api_field_parent_object.connector_name == AtlanConnectorType.API.value + + +@pytest.fixture(scope="module") +def api_field_parent_object_overload_1( + client: AtlanClient, connection: Connection, api_object: APIObject +) -> Generator[APIField, None, None]: + assert connection.qualified_name + to_create = APIField.creator( + name=API_FIELD_OVERLOAD_1_NAME, + parent_api_object_qualified_name=api_object.qualified_name, + parent_api_query_qualified_name=None, + connection_qualified_name=connection.qualified_name, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=APIField)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=APIField) + + +def test_api_field_parent_object_overload_1( + client: AtlanClient, + connection: Connection, + api_field_parent_object_overload_1: APIField, + api_object: APIObject, +): + assert api_field_parent_object_overload_1 + assert api_field_parent_object_overload_1.guid + assert ( + api_field_parent_object_overload_1.qualified_name + == f"{api_object.qualified_name}/{API_FIELD_OVERLOAD_1_NAME}" + ) + assert api_field_parent_object_overload_1.name == API_FIELD_OVERLOAD_1_NAME + assert ( + api_field_parent_object_overload_1.connection_qualified_name + == connection.qualified_name + ) + assert ( + api_field_parent_object_overload_1.connector_name + == AtlanConnectorType.API.value + ) + + +@pytest.fixture(scope="module") +def api_field_parent_object_overload_2( + client: AtlanClient, connection: Connection, api_object: APIObject +) -> Generator[APIField, None, None]: + assert connection.qualified_name + to_create = APIField.creator( + name=API_FIELD_OVERLOAD_2_NAME, + parent_api_object_qualified_name=api_object.qualified_name, + parent_api_query_qualified_name=None, + api_query_param_type=APIQueryParamTypeEnum.INPUT, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=APIField)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=APIField) + + +def test_api_field_parent_object_overload_2( + client: AtlanClient, + connection: Connection, + api_field_parent_object_overload_2: APIField, + api_object: APIObject, +): + assert api_field_parent_object_overload_2 + assert api_field_parent_object_overload_2.guid + assert ( + api_field_parent_object_overload_2.qualified_name + == f"{api_object.qualified_name}/{API_FIELD_OVERLOAD_2_NAME}" + ) + assert api_field_parent_object_overload_2.name == API_FIELD_OVERLOAD_2_NAME + assert ( + api_field_parent_object_overload_2.connection_qualified_name + == connection.qualified_name + ) + assert ( + api_field_parent_object_overload_2.api_query_param_type + == APIQueryParamTypeEnum.INPUT.value + ) + assert ( + api_field_parent_object_overload_2.connector_name + == AtlanConnectorType.API.value + ) + + +@pytest.fixture(scope="module") +def api_field_parent_object_overload_3( + client: AtlanClient, connection: Connection, api_object: APIObject +) -> Generator[APIField, None, None]: + assert connection.qualified_name + to_create = APIField.creator( + name=API_FIELD_OVERLOAD_3_NAME, + parent_api_object_qualified_name=api_object.qualified_name, + parent_api_query_qualified_name=None, + api_field_type=API_FIELD_TYPE, + api_field_type_secondary=API_FIELD_TYPE_SECONDARY, + api_query_param_type=APIQueryParamTypeEnum.INPUT, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=APIField)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=APIField) + + +def test_api_field_parent_object_overload_3( + client: AtlanClient, + connection: Connection, + api_field_parent_object_overload_3: APIField, + api_object: APIObject, +): + assert api_field_parent_object_overload_3 + assert api_field_parent_object_overload_3.guid + assert ( + api_field_parent_object_overload_3.qualified_name + == f"{api_object.qualified_name}/{API_FIELD_OVERLOAD_3_NAME}" + ) + assert api_field_parent_object_overload_3.name == API_FIELD_OVERLOAD_3_NAME + assert ( + api_field_parent_object_overload_3.connection_qualified_name + == connection.qualified_name + ) + assert api_field_parent_object_overload_3.api_field_type == API_FIELD_TYPE + assert ( + api_field_parent_object_overload_3.api_field_type_secondary + == API_FIELD_TYPE_SECONDARY + ) + assert ( + api_field_parent_object_overload_3.api_query_param_type + == APIQueryParamTypeEnum.INPUT.value + ) + assert ( + api_field_parent_object_overload_3.connector_name + == AtlanConnectorType.API.value + ) + + +@pytest.fixture(scope="module") +def api_field_parent_object_overload_4( + client: AtlanClient, connection: Connection, api_object: APIObject +) -> Generator[APIField, None, None]: + assert connection.qualified_name + to_create = APIField.creator( + name=API_FIELD_OVERLOAD_4_NAME, + parent_api_object_qualified_name=api_object.qualified_name, + parent_api_query_qualified_name=None, + connection_qualified_name=connection.qualified_name, + api_field_type=API_FIELD_TYPE, + api_field_type_secondary=API_FIELD_TYPE_SECONDARY, + is_api_object_reference=API_FIELD_IS_OBJECT_REFERENCE, + reference_api_object_qualified_name=f"{connection.qualified_name}/{API_FIELD_REFERENCE_OBJECT_NAME}", + api_query_param_type=APIQueryParamTypeEnum.INPUT, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=APIField)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=APIField) + + +def test_api_field_parent_object_overload_4( + client: AtlanClient, + connection: Connection, + api_field_parent_object_overload_4: APIField, + api_object: APIObject, +): + assert api_field_parent_object_overload_4 + assert api_field_parent_object_overload_4.guid + assert ( + api_field_parent_object_overload_4.qualified_name + == f"{api_object.qualified_name}/{API_FIELD_OVERLOAD_4_NAME}" + ) + assert api_field_parent_object_overload_4.name == API_FIELD_OVERLOAD_4_NAME + assert ( + api_field_parent_object_overload_4.connection_qualified_name + == connection.qualified_name + ) + assert api_field_parent_object_overload_4.api_field_type == API_FIELD_TYPE + assert ( + api_field_parent_object_overload_4.api_field_type_secondary + == API_FIELD_TYPE_SECONDARY + ) + assert ( + api_field_parent_object_overload_4.api_is_object_reference + == API_FIELD_IS_OBJECT_REFERENCE + ) + assert ( + api_field_parent_object_overload_4.api_object_qualified_name + == f"{connection.qualified_name}/{API_FIELD_REFERENCE_OBJECT_NAME}" + ) + assert ( + api_field_parent_object_overload_4.api_query_param_type + == APIQueryParamTypeEnum.INPUT.value + ) + assert ( + api_field_parent_object_overload_4.connector_name + == AtlanConnectorType.API.value + ) + + +@pytest.fixture(scope="module") +def api_field_parent_query_overload( + client: AtlanClient, connection: Connection, api_query: APIQuery +) -> Generator[APIField, None, None]: + assert connection.qualified_name + to_create = APIField.creator( + name=API_FIELD_PARENT_QUERY_NAME, + parent_api_object_qualified_name=None, + parent_api_query_qualified_name=api_query.qualified_name, + connection_qualified_name=connection.qualified_name, + api_field_type=API_FIELD_TYPE, + api_field_type_secondary=API_FIELD_TYPE_SECONDARY, + is_api_object_reference=API_FIELD_IS_OBJECT_REFERENCE, + reference_api_object_qualified_name=f"{connection.qualified_name}/{API_FIELD_REFERENCE_OBJECT_NAME}", + api_query_param_type=APIQueryParamTypeEnum.INPUT, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=APIField)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=APIField) + + +def test_api_field_parent_query_overload( + client: AtlanClient, + connection: Connection, + api_field_parent_query_overload: APIField, + api_query: APIQuery, +): + assert api_field_parent_query_overload + assert api_field_parent_query_overload.guid + assert ( + api_field_parent_query_overload.qualified_name + == f"{api_query.qualified_name}/{API_FIELD_PARENT_QUERY_NAME}" + ) + assert api_field_parent_query_overload.name == API_FIELD_PARENT_QUERY_NAME + assert ( + api_field_parent_query_overload.connection_qualified_name + == connection.qualified_name + ) + assert api_field_parent_query_overload.api_field_type == API_FIELD_TYPE + assert ( + api_field_parent_query_overload.api_field_type_secondary + == API_FIELD_TYPE_SECONDARY + ) + assert ( + api_field_parent_query_overload.api_is_object_reference + == API_FIELD_IS_OBJECT_REFERENCE + ) + assert ( + api_field_parent_query_overload.api_object_qualified_name + == f"{connection.qualified_name}/{API_FIELD_REFERENCE_OBJECT_NAME}" + ) + assert ( + api_field_parent_query_overload.api_query_param_type + == APIQueryParamTypeEnum.INPUT.value + ) + assert ( + api_field_parent_query_overload.connector_name == AtlanConnectorType.API.value + ) + + +def test_update_api_field( + client: AtlanClient, + connection: Connection, + api_field_parent_query_overload: APIField, +): + assert api_field_parent_query_overload.qualified_name + assert api_field_parent_query_overload.name + updated = client.asset.update_certificate( + asset_type=APIField, + qualified_name=api_field_parent_query_overload.qualified_name, + name=api_field_parent_query_overload.name, + certificate_status=CERTIFICATE_STATUS, + message=CERTIFICATE_MESSAGE, + ) + assert updated + assert updated.certificate_status_message == CERTIFICATE_MESSAGE + assert api_field_parent_query_overload.qualified_name + assert api_field_parent_query_overload.name + updated = client.asset.update_announcement( + asset_type=APIField, + qualified_name=api_field_parent_query_overload.qualified_name, + name=api_field_parent_query_overload.name, + announcement=Announcement( + announcement_type=ANNOUNCEMENT_TYPE, + announcement_title=ANNOUNCEMENT_TITLE, + announcement_message=ANNOUNCEMENT_MESSAGE, + ), + ) + assert updated + if updated.announcement_type is not UNSET: + assert updated.announcement_type == ANNOUNCEMENT_TYPE.value + assert updated.announcement_title == ANNOUNCEMENT_TITLE + assert updated.announcement_message == ANNOUNCEMENT_MESSAGE + + +@pytest.mark.order(after="test_update_api_field") +def test_retrieve_api_field( + client: AtlanClient, + connection: Connection, + api_field_parent_query_overload: APIField, +): + b = client.asset.get_by_guid( + api_field_parent_query_overload.guid, + asset_type=APIField, + ignore_relationships=False, + ) + assert b + assert not b.is_incomplete + assert b.guid == api_field_parent_query_overload.guid + assert b.qualified_name == api_field_parent_query_overload.qualified_name + assert b.name == api_field_parent_query_overload.name + assert b.connector_name == api_field_parent_query_overload.connector_name + assert ( + b.connection_qualified_name + == api_field_parent_query_overload.connection_qualified_name + ) + assert b.certificate_status == CERTIFICATE_STATUS + assert b.certificate_status_message == CERTIFICATE_MESSAGE + + +@pytest.mark.order(after="test_retrieve_api_field") +def test_delete_api_field( + client: AtlanClient, + connection: Connection, + api_field_parent_query_overload: APIField, +): + response = client.asset.delete_by_guid(api_field_parent_query_overload.guid) + assert response + assert not response.assets_created(asset_type=APIField) + assert not response.assets_updated(asset_type=APIField) + deleted = response.assets_deleted(asset_type=APIField) + assert deleted + assert len(deleted) == 1 + assert deleted[0].guid == api_field_parent_query_overload.guid + assert deleted[0].qualified_name == api_field_parent_query_overload.qualified_name + assert deleted[0].delete_handler == "SOFT" + assert deleted[0].status == EntityStatus.DELETED + + +@pytest.mark.order(after="test_delete_api_field") +def test_read_deleted_api_field( + client: AtlanClient, + connection: Connection, + api_field_parent_query_overload: APIField, +): + deleted = client.asset.get_by_guid( + api_field_parent_query_overload.guid, + asset_type=APIField, + ignore_relationships=False, + ) + assert deleted + assert deleted.guid == api_field_parent_query_overload.guid + assert deleted.qualified_name == api_field_parent_query_overload.qualified_name + assert deleted.status == EntityStatus.DELETED + + +@pytest.mark.order(after="test_read_deleted_api_field") +def test_restore_field( + client: AtlanClient, + connection: Connection, + api_field_parent_query_overload: APIField, +): + assert api_field_parent_query_overload.qualified_name + assert client.asset.restore( + asset_type=APIField, + qualified_name=api_field_parent_query_overload.qualified_name, + ) + assert api_field_parent_query_overload.qualified_name + restored = client.asset.get_by_qualified_name( + asset_type=APIField, + qualified_name=api_field_parent_query_overload.qualified_name, + ignore_relationships=False, + ) + assert restored + assert restored.guid == api_field_parent_query_overload.guid + assert restored.qualified_name == api_field_parent_query_overload.qualified_name + assert restored.status == EntityStatus.ACTIVE diff --git a/tests_v9/integration/app_asset_test.py b/tests_v9/integration/app_asset_test.py new file mode 100644 index 000000000..0bf8c3859 --- /dev/null +++ b/tests_v9/integration/app_asset_test.py @@ -0,0 +1,285 @@ +from typing import Generator + +import pytest + +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.model.assets import Application, ApplicationField, Connection +from pyatlan_v9.model.core import Announcement +from pyatlan_v9.model.enums import ( + AnnouncementType, + AtlanConnectorType, + CertificateStatus, + EntityStatus, +) +from tests_v9.integration.client import TestId, delete_asset +from tests_v9.integration.connection_test import create_connection + +MODULE_NAME = TestId.make_unique("APP") + +CONNECTOR_TYPE = AtlanConnectorType.APP +APPLICATION_NAME = f"{MODULE_NAME}-application" +APPLICATION_FIELD_NAME = f"{MODULE_NAME}-application-field" +APPLICATION_FIELD_OVERLOAD_NAME = f"{MODULE_NAME}-application-field-overload" +CERTIFICATE_STATUS = CertificateStatus.VERIFIED +CERTIFICATE_MESSAGE = "Automated testing of the Python SDK." +ANNOUNCEMENT_TYPE = AnnouncementType.INFORMATION +ANNOUNCEMENT_TITLE = "Python SDK testing." +ANNOUNCEMENT_MESSAGE = "Automated testing of the Python SDK." + + +@pytest.fixture(scope="module") +def connection(client: AtlanClient) -> Generator[Connection, None, None]: + result = create_connection( + client=client, name=MODULE_NAME, connector_type=CONNECTOR_TYPE + ) + yield result + delete_asset(client, guid=result.guid, asset_type=Connection) + + +@pytest.fixture(scope="module") +def application( + client: AtlanClient, connection: Connection +) -> Generator[Application, None, None]: + assert connection.qualified_name + to_create = Application.creator( + name=APPLICATION_NAME, + connection_qualified_name=connection.qualified_name, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=Application)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=Application) + + +def test_application( + client: AtlanClient, + connection: Connection, + application: Application, +): + assert application + assert application.guid + assert application.qualified_name + assert application.name == APPLICATION_NAME + assert application.connection_qualified_name == connection.qualified_name + assert application.connector_name == AtlanConnectorType.APP.value + + +# here +def test_update_application( + client: AtlanClient, + connection: Connection, + application: Application, +): + assert application.qualified_name + assert application.name + updated = client.asset.update_certificate( + asset_type=Application, + qualified_name=application.qualified_name, + name=application.name, + certificate_status=CERTIFICATE_STATUS, + message=CERTIFICATE_MESSAGE, + ) + assert updated + assert updated.certificate_status_message == CERTIFICATE_MESSAGE + assert application.qualified_name + assert application.name + updated = client.asset.update_announcement( + asset_type=Application, + qualified_name=application.qualified_name, + name=application.name, + announcement=Announcement( + announcement_type=ANNOUNCEMENT_TYPE, + announcement_title=ANNOUNCEMENT_TITLE, + announcement_message=ANNOUNCEMENT_MESSAGE, + ), + ) + assert updated + assert updated.announcement_type == ANNOUNCEMENT_TYPE.value + assert updated.announcement_title == ANNOUNCEMENT_TITLE + assert updated.announcement_message == ANNOUNCEMENT_MESSAGE + + +@pytest.mark.order(after="test_update_application") +def test_retrieve_application( + client: AtlanClient, + connection: Connection, + application: Application, +): + b = client.asset.get_by_guid( + application.guid, asset_type=Application, ignore_relationships=False + ) + assert b + assert not b.is_incomplete + assert b.guid == application.guid + assert b.qualified_name == application.qualified_name + assert b.name == application.name + assert b.connector_name == application.connector_name + assert b.connection_qualified_name == application.connection_qualified_name + assert b.certificate_status == CERTIFICATE_STATUS + assert b.certificate_status_message == CERTIFICATE_MESSAGE + + +@pytest.mark.order(after="test_retrieve_application") +def test_update_application_again( + client: AtlanClient, + connection: Connection, + application: Application, +): + assert application.qualified_name + assert application.name + updated = client.asset.remove_certificate( + asset_type=Application, + qualified_name=application.qualified_name, + name=application.name, + ) + assert updated + refreshed = client.asset.get_by_qualified_name( + asset_type=Application, + qualified_name=application.qualified_name, + ) + assert refreshed + assert not refreshed.certificate_status + assert not refreshed.certificate_status_message + assert application.qualified_name + updated = client.asset.remove_announcement( + asset_type=Application, + qualified_name=application.qualified_name, + name=application.name, + ) + assert updated + refreshed = client.asset.get_by_qualified_name( + asset_type=Application, + qualified_name=application.qualified_name, + ) + assert refreshed + assert not refreshed.announcement_type + assert not refreshed.announcement_title + assert not refreshed.announcement_message + + +@pytest.mark.order(after="test_update_application_again") +def test_delete_application( + client: AtlanClient, + connection: Connection, + application: Application, +): + response = client.asset.delete_by_guid(application.guid) + assert response + assert not response.assets_created(asset_type=Application) + assert not response.assets_updated(asset_type=Application) + deleted = response.assets_deleted(asset_type=Application) + assert deleted + assert len(deleted) == 1 + assert deleted[0].guid == application.guid + assert deleted[0].qualified_name == application.qualified_name + assert deleted[0].delete_handler == "SOFT" + assert deleted[0].status == EntityStatus.DELETED + + +@pytest.mark.order(after="test_delete_application") +def test_read_deleted_application( + client: AtlanClient, + connection: Connection, + application: Application, +): + deleted = client.asset.get_by_guid( + application.guid, asset_type=Application, ignore_relationships=False + ) + assert deleted + assert deleted.guid == application.guid + assert deleted.qualified_name == application.qualified_name + assert deleted.status == EntityStatus.DELETED + + +@pytest.mark.order(after="test_read_deleted_application") +def test_restore_application( + client: AtlanClient, + connection: Connection, + application: Application, +): + assert application.qualified_name + assert client.asset.restore( + asset_type=Application, + qualified_name=application.qualified_name, + ) + assert application.qualified_name + restored = client.asset.get_by_qualified_name( + asset_type=Application, + qualified_name=application.qualified_name, + ignore_relationships=False, + ) + assert restored + assert restored.guid == application.guid + assert restored.qualified_name == application.qualified_name + assert restored.status == EntityStatus.ACTIVE + + +@pytest.fixture(scope="module") +def application_field( + client: AtlanClient, application: ApplicationField +) -> Generator[ApplicationField, None, None]: + assert application.qualified_name + to_create = ApplicationField.creator( + name=APPLICATION_FIELD_NAME, + application_qualified_name=application.qualified_name, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=ApplicationField)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=ApplicationField) + + +def test_application_field( + client: AtlanClient, application: Application, application_field: ApplicationField +): + assert application_field + assert application_field.guid + assert application_field.qualified_name + assert application_field.name == APPLICATION_FIELD_NAME + assert ( + application_field.connection_qualified_name + == application.connection_qualified_name + ) + assert application_field.connector_name == AtlanConnectorType.APP.value + assert ( + application_field.application_parent_qualified_name + == application.qualified_name + ) + + +@pytest.fixture(scope="module") +def application_field_overload( + client: AtlanClient, connection: Connection, application: Application +) -> Generator[ApplicationField, None, None]: + assert connection.qualified_name + assert application.qualified_name + to_create = ApplicationField.creator( + name=APPLICATION_FIELD_OVERLOAD_NAME, + application_qualified_name=application.qualified_name, + connection_qualified_name=connection.qualified_name, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=ApplicationField)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=ApplicationField) + + +def test_overload_application_field( + client: AtlanClient, + connection: Connection, + application: Application, + application_field_overload: ApplicationField, +): + assert application_field_overload + assert application_field_overload.guid + assert application_field_overload.qualified_name + assert application_field_overload.name == APPLICATION_FIELD_OVERLOAD_NAME + assert ( + application_field_overload.connection_qualified_name + == connection.qualified_name + ) + assert application_field_overload.connector_name == AtlanConnectorType.APP.value + assert ( + application_field_overload.application_parent_qualified_name + == application.qualified_name + ) diff --git a/tests_v9/integration/atlan_tag_test.py b/tests_v9/integration/atlan_tag_test.py new file mode 100644 index 000000000..e68ce5c28 --- /dev/null +++ b/tests_v9/integration/atlan_tag_test.py @@ -0,0 +1,149 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2022 Atlan Pte. Ltd. +import contextlib +import logging +import os +import urllib.request +from typing import Callable, Generator, Optional + +import pytest + +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.errors import AtlanError +from pyatlan_v9.model.atlan_image import AtlanImage +from pyatlan_v9.model.enums import AtlanIcon, AtlanTagColor, TagIconType +from pyatlan_v9.model.typedef import AtlanTagDef +from tests_v9.integration.client import TestId +from tests_v9.integration.utils import wait_for_successful_tagdef_purge + +MODULE_NAME = TestId.make_unique("CLS") + +CLS_IMAGE = f"{MODULE_NAME}_image" +CLS_ICON = f"{MODULE_NAME}_icon" +CLS_EMOJI = f"{MODULE_NAME}_emoji" + +LOGGER = logging.getLogger(__name__) + + +@pytest.fixture(scope="module") +def make_atlan_tag( + client: AtlanClient, +) -> Generator[Callable[[str], AtlanTagDef], None, None]: + created_names = [] + + def _make_atlan_tag( + name: str, + color: AtlanTagColor = AtlanTagColor.GREEN, + image: Optional[AtlanImage] = None, + ) -> AtlanTagDef: + atlan_tag_def = AtlanTagDef.creator(name=name, color=color, image=image) + r = client.typedef.creator(atlan_tag_def) + c = r.atlan_tag_defs[0] + created_names.append(c.display_name) + return c + + yield _make_atlan_tag + + for n in created_names: + try: + wait_for_successful_tagdef_purge(name=n, client=client) + except AtlanError as err: + LOGGER.error(err) + + +@pytest.fixture(scope="module") +def image(client: AtlanClient) -> Generator[AtlanImage, None, None]: + urllib.request.urlretrieve( + "https://github.com/great-expectations/great_expectations" + "/raw/develop/docs/docusaurus/static/img/gx-mark-160.png", + "gx-mark-160.png", + ) + with open("gx-mark-160.png", "rb") as out_file: + yield client.upload_image(file=out_file, filename="gx-mark-160.png") + os.remove("gx-mark-160.png") + + +@pytest.fixture(scope="module") +def atlan_tag_with_image( + client: AtlanClient, + image: AtlanImage, +) -> Generator[AtlanTagDef, None, None]: + cls = AtlanTagDef.creator(name=CLS_IMAGE, color=AtlanTagColor.YELLOW, image=image) + yield client.typedef.creator(cls).atlan_tag_defs[0] + with contextlib.suppress(AtlanError): + wait_for_successful_tagdef_purge(name=CLS_IMAGE, client=client) + + +@pytest.fixture(scope="module") +def atlan_tag_with_icon( + client: AtlanClient, +) -> Generator[AtlanTagDef, None, None]: + cls = AtlanTagDef.creator( + name=CLS_ICON, + color=AtlanTagColor.YELLOW, + icon=AtlanIcon.BOOK_BOOKMARK, + ) + yield client.typedef.creator(cls).atlan_tag_defs[0] + with contextlib.suppress(AtlanError): + wait_for_successful_tagdef_purge(name=CLS_ICON, client=client) + + +@pytest.fixture(scope="module") +def atlan_tag_with_emoji( + client: AtlanClient, +) -> Generator[AtlanTagDef, None, None]: + cls = AtlanTagDef.creator( + name=CLS_EMOJI, + emoji="👍", + ) + yield client.typedef.creator(cls).atlan_tag_defs[0] + with contextlib.suppress(AtlanError): + wait_for_successful_tagdef_purge(name=CLS_EMOJI, client=client) + + +def test_atlan_tag_with_image(atlan_tag_with_image): + assert atlan_tag_with_image + assert atlan_tag_with_image.guid + assert atlan_tag_with_image.display_name == CLS_IMAGE + assert atlan_tag_with_image.name != CLS_IMAGE + assert atlan_tag_with_image.options + assert "color" in atlan_tag_with_image.options.keys() + assert atlan_tag_with_image.options.get("color") == AtlanTagColor.YELLOW.value + assert "imageID" in atlan_tag_with_image.options.keys() + assert atlan_tag_with_image.options.get("imageID") + assert "iconType" in atlan_tag_with_image.options.keys() + assert atlan_tag_with_image.options.get("iconType") == TagIconType.IMAGE.value + + +def test_atlan_tag_cache(client: AtlanClient, atlan_tag_with_image): + cls_name = CLS_IMAGE + cls_id = client.atlan_tag_cache.get_id_for_name(cls_name) + assert cls_id + assert cls_id == atlan_tag_with_image.name + cls_name_found = client.atlan_tag_cache.get_name_for_id(cls_id) + assert cls_name_found + assert cls_name_found == cls_name + + +def test_atlan_tag_with_icon(atlan_tag_with_icon): + assert atlan_tag_with_icon + assert atlan_tag_with_icon.guid + assert atlan_tag_with_icon.display_name == CLS_ICON + assert atlan_tag_with_icon.name != CLS_ICON + assert atlan_tag_with_icon.options + assert "color" in atlan_tag_with_icon.options.keys() + assert atlan_tag_with_icon.options.get("color") == AtlanTagColor.YELLOW.value + assert not atlan_tag_with_icon.options.get("imageID") + assert "iconType" in atlan_tag_with_icon.options.keys() + assert atlan_tag_with_icon.options.get("iconType") == TagIconType.ICON.value + + +def test_atlan_tag_with_emoji(atlan_tag_with_emoji): + assert atlan_tag_with_emoji + assert atlan_tag_with_emoji.guid + assert atlan_tag_with_emoji.display_name == CLS_EMOJI + assert atlan_tag_with_emoji.name != CLS_EMOJI + assert atlan_tag_with_emoji.options + assert not atlan_tag_with_emoji.options.get("imageID") + assert "iconType" in atlan_tag_with_emoji.options.keys() + assert atlan_tag_with_emoji.options.get("iconType") == TagIconType.EMOJI.value diff --git a/tests_v9/integration/azure_event_hub_asset_test.py b/tests_v9/integration/azure_event_hub_asset_test.py new file mode 100644 index 000000000..ff83227c6 --- /dev/null +++ b/tests_v9/integration/azure_event_hub_asset_test.py @@ -0,0 +1,224 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +from typing import Generator + +import pytest + +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.model.assets import ( + AzureEventHub, + AzureEventHubConsumerGroup, + Connection, +) +from pyatlan_v9.model.core import Announcement +from pyatlan_v9.model.enums import ( + AnnouncementType, + AtlanConnectorType, + CertificateStatus, + EntityStatus, +) +from tests_v9.integration.client import TestId, delete_asset +from tests_v9.integration.connection_test import create_connection + +MODULE_NAME = TestId.make_unique("AZURE_EVENT_HUB") + +EVENT_HUB_NAME = f"test_eh_{MODULE_NAME}" +EVENT_HUB_CONSUMER_GROUP_NAME = f"test_eh_consumer_group_{MODULE_NAME}" +CERTIFICATE_STATUS = CertificateStatus.VERIFIED + +ANNOUNCEMENT_TITLE = "Python SDK testing." +ANNOUNCEMENT_TYPE = AnnouncementType.INFORMATION +CERTIFICATE_MESSAGE = "Automated testing of the Python SDK." +ANNOUNCEMENT_MESSAGE = "Automated testing of the Python SDK." + + +@pytest.fixture(scope="module") +def connection(client: AtlanClient) -> Generator[Connection, None, None]: + result = create_connection( + client=client, + name=MODULE_NAME, + connector_type=AtlanConnectorType.AZURE_EVENT_HUB, + ) + yield result + delete_asset(client, guid=result.guid, asset_type=Connection) + + +@pytest.fixture(scope="module") +def event_hub( + client: AtlanClient, connection: Connection +) -> Generator[AzureEventHub, None, None]: + assert connection.qualified_name + to_create = AzureEventHub.creator( + name=EVENT_HUB_NAME, connection_qualified_name=connection.qualified_name + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=AzureEventHub)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=AzureEventHub) + + +def test_event_hub( + client: AtlanClient, + connection: Connection, + event_hub: AzureEventHub, +): + assert event_hub + assert event_hub.guid + assert event_hub.qualified_name + assert event_hub.name == EVENT_HUB_NAME + assert event_hub.connector_name == AtlanConnectorType.AZURE_EVENT_HUB + assert event_hub.connection_qualified_name == connection.qualified_name + + +@pytest.fixture(scope="module") +def consumer_group( + client: AtlanClient, event_hub: AzureEventHub +) -> Generator[AzureEventHubConsumerGroup, None, None]: + assert event_hub.qualified_name + to_create = AzureEventHubConsumerGroup.creator( + name=EVENT_HUB_CONSUMER_GROUP_NAME, + event_hub_qualified_names=[event_hub.qualified_name], + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=AzureEventHubConsumerGroup)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=AzureEventHubConsumerGroup) + + +def test_event_hub_consumer_group( + client: AtlanClient, + event_hub: AzureEventHub, + consumer_group: AzureEventHubConsumerGroup, +): + assert consumer_group + assert consumer_group.guid + assert consumer_group.qualified_name + assert consumer_group.name == EVENT_HUB_CONSUMER_GROUP_NAME + assert consumer_group.connector_name == AtlanConnectorType.AZURE_EVENT_HUB + assert ( + event_hub.qualified_name + and consumer_group.kafka_topic_qualified_names + and event_hub.qualified_name in consumer_group.kafka_topic_qualified_names + ) + + +def _update_cert_and_annoucement(client, asset, asset_type): + assert asset.name + assert asset.qualified_name + + updated = client.asset.update_certificate( + name=asset.name, + asset_type=asset_type, + qualified_name=asset.qualified_name, + message=CERTIFICATE_MESSAGE, + certificate_status=CERTIFICATE_STATUS, + ) + assert updated + assert updated.certificate_status == CERTIFICATE_STATUS + assert updated.certificate_status_message == CERTIFICATE_MESSAGE + + updated = client.asset.update_announcement( + name=asset.name, + asset_type=asset_type, + qualified_name=asset.qualified_name, + announcement=Announcement( + announcement_type=ANNOUNCEMENT_TYPE, + announcement_title=ANNOUNCEMENT_TITLE, + announcement_message=ANNOUNCEMENT_MESSAGE, + ), + ) + assert updated + assert updated.announcement_type == ANNOUNCEMENT_TYPE + assert updated.announcement_title == ANNOUNCEMENT_TITLE + assert updated.announcement_message == ANNOUNCEMENT_MESSAGE + + +def test_update_event_hub_assets( + client: AtlanClient, + event_hub: AzureEventHub, + consumer_group: AzureEventHubConsumerGroup, +): + _update_cert_and_annoucement(client, event_hub, AzureEventHub) + _update_cert_and_annoucement(client, consumer_group, AzureEventHubConsumerGroup) + + +def _retrieve_event_hub_assets(client, asset, asset_type): + retrieved = client.asset.get_by_guid( + asset.guid, asset_type=asset_type, ignore_relationships=False + ) + assert retrieved + assert not retrieved.is_incomplete + assert retrieved.guid == asset.guid + assert retrieved.qualified_name == asset.qualified_name + assert retrieved.name == asset.name + assert retrieved.connector_name == AtlanConnectorType.AZURE_EVENT_HUB + assert retrieved.certificate_status == CERTIFICATE_STATUS + assert retrieved.certificate_status_message == CERTIFICATE_MESSAGE + + +@pytest.mark.order(after="test_update_event_hub_assets") +def test_retrieve_event_hub_assets( + client: AtlanClient, + event_hub: AzureEventHub, + consumer_group: AzureEventHubConsumerGroup, +): + _retrieve_event_hub_assets(client, event_hub, AzureEventHub) + _retrieve_event_hub_assets(client, consumer_group, AzureEventHubConsumerGroup) + + +@pytest.mark.order(after="test_retrieve_event_hub_assets") +def test_delete_event_hub_consumer_group( + client: AtlanClient, + consumer_group: AzureEventHubConsumerGroup, +): + response = client.asset.delete_by_guid(guid=consumer_group.guid) + assert response + assert not response.assets_created(asset_type=AzureEventHubConsumerGroup) + assert not response.assets_updated(asset_type=AzureEventHubConsumerGroup) + deleted = response.assets_deleted(asset_type=AzureEventHubConsumerGroup) + + assert deleted + assert len(deleted) == 1 + assert deleted[0].guid == consumer_group.guid + assert deleted[0].delete_handler == "SOFT" + assert deleted[0].status == EntityStatus.DELETED + assert deleted[0].qualified_name == consumer_group.qualified_name + + +@pytest.mark.order(after="test_delete_event_hub_consumer_group") +def test_read_deleted_event_hub_consumer_group( + client: AtlanClient, + consumer_group: AzureEventHubConsumerGroup, +): + deleted = client.asset.get_by_guid( + consumer_group.guid, + asset_type=AzureEventHubConsumerGroup, + ignore_relationships=False, + ) + assert deleted + assert deleted.status == EntityStatus.DELETED + assert deleted.guid == consumer_group.guid + assert deleted.qualified_name == consumer_group.qualified_name + + +@pytest.mark.order(after="test_read_deleted_event_hub_consumer_group") +def test_restore_event_hub_consumer_group( + client: AtlanClient, + consumer_group: AzureEventHubConsumerGroup, +): + assert consumer_group.qualified_name + assert client.asset.restore( + asset_type=AzureEventHubConsumerGroup, + qualified_name=consumer_group.qualified_name, + ) + assert consumer_group.qualified_name + restored = client.asset.get_by_qualified_name( + asset_type=AzureEventHubConsumerGroup, + qualified_name=consumer_group.qualified_name, + ignore_relationships=False, + ) + assert restored + assert restored.guid == consumer_group.guid + assert restored.status == EntityStatus.ACTIVE + assert restored.qualified_name == consumer_group.qualified_name diff --git a/tests_v9/integration/client.py b/tests_v9/integration/client.py new file mode 100644 index 000000000..e8c4864cb --- /dev/null +++ b/tests_v9/integration/client.py @@ -0,0 +1,54 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. +"""Shared integration test utilities for v9.""" + +import logging +from typing import Generator, Type + +import pytest + +from pyatlan_v9.client.atlan import DEFAULT_RETRY, AtlanClient +from pyatlan_v9.model.enums import AtlanDeleteType +from pyatlan_v9.model.response import A + +LOGGER = logging.getLogger(__name__) + + +class TestId: + from nanoid import generate as generate_nanoid # type: ignore + + session_id = generate_nanoid( + alphabet="1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", + size=5, + ) + + @classmethod + def make_unique(cls, input: str): + return f"psdkv9_{input}_{cls.session_id}" + + +@pytest.fixture(scope="module") +def client() -> Generator[AtlanClient, None, None]: + client = AtlanClient() + yield client + + +@pytest.fixture(scope="module") +def token_client() -> Generator[AtlanClient, None, None]: + DEFAULT_RETRY.total = 0 + client = AtlanClient(retry=DEFAULT_RETRY) + yield client + + +def delete_asset( + client: AtlanClient, + asset_type: Type[A], + guid: str, + delete_type: AtlanDeleteType = AtlanDeleteType.PURGE, +) -> None: + r = client.asset.purge_by_guid(guid, delete_type) + s = r is not None + s = s and len(r.assets_deleted(asset_type)) == 1 + s = s and r.assets_deleted(asset_type)[0].guid == guid + if not s: + LOGGER.error(f"Failed to remove {asset_type} with GUID {guid}.") diff --git a/tests_v9/integration/conftest.py b/tests_v9/integration/conftest.py new file mode 100644 index 000000000..da7689627 --- /dev/null +++ b/tests_v9/integration/conftest.py @@ -0,0 +1,7 @@ +pytest_plugins = [ + "tests_v9.integration.client", + "tests_v9.integration.glossary_test", + "tests_v9.integration.atlan_tag_test", + "tests_v9.integration.lineage_test", + "tests_v9.integration.test_sql_assets", +] diff --git a/tests_v9/integration/connection_test.py b/tests_v9/integration/connection_test.py new file mode 100644 index 000000000..e80d706d3 --- /dev/null +++ b/tests_v9/integration/connection_test.py @@ -0,0 +1,105 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2022 Atlan Pte. Ltd. +from typing import Generator + +import pytest + +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.model.assets import Connection +from pyatlan_v9.model.enums import AtlanConnectionCategory, AtlanConnectorType +from tests_v9.integration.client import TestId, delete_asset + +MODULE_NAME = TestId.make_unique("CONN") + + +def create_connection( + client: AtlanClient, name: str, connector_type: AtlanConnectorType +) -> Connection: + admin_role_guid = str(client.role_cache.get_id_for_name("$admin")) + to_create = Connection.creator( + client=client, + name=name, + connector_type=connector_type, + admin_roles=[admin_role_guid], + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=Connection)[0] + return client.asset.get_by_guid( + result.guid, asset_type=Connection, ignore_relationships=False + ) + + +@pytest.fixture(scope="module") +def custom_connection(client: AtlanClient) -> Generator[Connection, None, None]: + CUSTOM_CONNECTOR_TYPE = AtlanConnectorType.CREATE_CUSTOM( + name=f"{MODULE_NAME}_NAME", + value=f"{MODULE_NAME}_type", + category=AtlanConnectionCategory.API, + ) + result = create_connection( + client=client, name=MODULE_NAME, connector_type=CUSTOM_CONNECTOR_TYPE + ) + yield result + # TODO: proper connection delete workflow + delete_asset(client, guid=result.guid, asset_type=Connection) + + +def test_custom_connection(custom_connection: Connection): + assert custom_connection.name == MODULE_NAME + assert custom_connection.connector_name == f"{MODULE_NAME.lower()}_type" + assert custom_connection.qualified_name + assert f"default/{MODULE_NAME.lower()}_type" in custom_connection.qualified_name + assert ( + AtlanConnectorType[f"{MODULE_NAME}_NAME"].value == f"{MODULE_NAME.lower()}_type" + ) + + +def test_invalid_connection(client: AtlanClient): + with pytest.raises( + ValueError, match="One of admin_user, admin_groups or admin_roles is required" + ): + Connection.creator( + client=client, name=MODULE_NAME, connector_type=AtlanConnectorType.POSTGRES + ) + + +def test_invalid_connection_admin_role( + client: AtlanClient, +): + with pytest.raises( + ValueError, match="Provided role ID abc123 was not found in Atlan." + ): + Connection.creator( + client=client, + name=MODULE_NAME, + connector_type=AtlanConnectorType.SAPHANA, + admin_roles=["abc123"], + ) + + +def test_invalid_connection_admin_group( + client: AtlanClient, +): + with pytest.raises( + ValueError, match="Provided group name abc123 was not found in Atlan." + ): + Connection.creator( + client=client, + name=MODULE_NAME, + connector_type=AtlanConnectorType.SAPHANA, + admin_groups=["abc123"], + ) + + +def test_invalid_connection_admin_user( + client: AtlanClient, +): + with pytest.raises( + ValueError, match="Provided username abc123 was not found in Atlan." + ): + Connection.creator( + client=client, + name=MODULE_NAME, + connector_type=AtlanConnectorType.SAPHANA, + admin_users=["abc123"], + ) diff --git a/tests_v9/integration/custom_asset_test.py b/tests_v9/integration/custom_asset_test.py new file mode 100644 index 000000000..d7222b63d --- /dev/null +++ b/tests_v9/integration/custom_asset_test.py @@ -0,0 +1,159 @@ +from typing import Generator + +import pytest + +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.model.assets import Connection, CustomEntity +from pyatlan_v9.model.core import Announcement +from pyatlan_v9.model.enums import ( + AnnouncementType, + AtlanConnectorType, + CertificateStatus, + EntityStatus, +) +from tests_v9.integration.client import TestId, delete_asset +from tests_v9.integration.connection_test import create_connection + +MODULE_NAME = TestId.make_unique("CUSTOM") + +CONNECTOR_TYPE = AtlanConnectorType.CUSTOM +CUSTOM_ENTITY_NAME = f"{MODULE_NAME}-custom-entity" + +CERTIFICATE_STATUS = CertificateStatus.VERIFIED +ANNOUNCEMENT_TITLE = "Python SDK testing." +ANNOUNCEMENT_TYPE = AnnouncementType.INFORMATION +CERTIFICATE_MESSAGE = "Automated testing of the Python SDK." +ANNOUNCEMENT_MESSAGE = "Automated testing of the Python SDK." + + +@pytest.fixture(scope="module") +def connection(client: AtlanClient) -> Generator[Connection, None, None]: + result = create_connection( + client=client, name=MODULE_NAME, connector_type=CONNECTOR_TYPE + ) + yield result + delete_asset(client, guid=result.guid, asset_type=Connection) + + +@pytest.fixture(scope="module") +def custom_entity( + client: AtlanClient, connection: Connection +) -> Generator[CustomEntity, None, None]: + assert connection.qualified_name + to_create = CustomEntity.creator( + name=CUSTOM_ENTITY_NAME, connection_qualified_name=connection.qualified_name + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=CustomEntity)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=CustomEntity) + + +def test_custom_entity( + client: AtlanClient, connection: Connection, custom_entity: CustomEntity +): + assert custom_entity + assert custom_entity.guid + assert custom_entity.qualified_name + assert custom_entity.name == CUSTOM_ENTITY_NAME + assert custom_entity.connection_qualified_name == connection.qualified_name + assert custom_entity.connector_name == AtlanConnectorType.CUSTOM.value + + +@pytest.mark.order(after="test_custom_entity") +def test_delete_custom_entity( + client: AtlanClient, + connection: Connection, + custom_entity: CustomEntity, +): + response = client.asset.delete_by_guid(custom_entity.guid) + assert response + assert not response.assets_created(asset_type=CustomEntity) + assert not response.assets_updated(asset_type=CustomEntity) + deleted = response.assets_deleted(asset_type=CustomEntity) + assert deleted + assert len(deleted) == 1 + assert deleted[0].guid == custom_entity.guid + assert deleted[0].qualified_name == custom_entity.qualified_name + assert deleted[0].delete_handler == "SOFT" + assert deleted[0].status == EntityStatus.DELETED + + +@pytest.mark.order(after="test_delete_custom_entity") +def test_restore_custom_entity( + client: AtlanClient, + connection: Connection, + custom_entity: CustomEntity, +): + assert custom_entity.qualified_name + assert client.asset.restore( + asset_type=CustomEntity, qualified_name=custom_entity.qualified_name + ) + assert custom_entity.qualified_name + restored = client.asset.get_by_qualified_name( + asset_type=CustomEntity, qualified_name=custom_entity.qualified_name + ) + assert restored + assert restored.guid == custom_entity.guid + assert restored.qualified_name == custom_entity.qualified_name + assert restored.status == EntityStatus.ACTIVE + + +def _update_cert_and_annoucement(client, asset, asset_type): + assert asset.name + assert asset.qualified_name + + updated = client.asset.update_certificate( + name=asset.name, + asset_type=asset_type, + qualified_name=asset.qualified_name, + message=CERTIFICATE_MESSAGE, + certificate_status=CERTIFICATE_STATUS, + ) + assert updated + assert updated.certificate_status == CERTIFICATE_STATUS + assert updated.certificate_status_message == CERTIFICATE_MESSAGE + + updated = client.asset.update_announcement( + name=asset.name, + asset_type=asset_type, + qualified_name=asset.qualified_name, + announcement=Announcement( + announcement_type=ANNOUNCEMENT_TYPE, + announcement_title=ANNOUNCEMENT_TITLE, + announcement_message=ANNOUNCEMENT_MESSAGE, + ), + ) + assert updated + assert updated.announcement_type == ANNOUNCEMENT_TYPE + assert updated.announcement_title == ANNOUNCEMENT_TITLE + assert updated.announcement_message == ANNOUNCEMENT_MESSAGE + + +def test_update_custom_assets( + client: AtlanClient, + custom_entity: CustomEntity, +): + _update_cert_and_annoucement(client, custom_entity, CustomEntity) + + +def _retrieve_custom_assets(client, asset, asset_type): + retrieved = client.asset.get_by_guid( + asset.guid, asset_type=asset_type, ignore_relationships=False + ) + assert retrieved + assert not retrieved.is_incomplete + assert retrieved.guid == asset.guid + assert retrieved.qualified_name == asset.qualified_name + assert retrieved.name == asset.name + assert retrieved.connector_name == AtlanConnectorType.CUSTOM + assert retrieved.certificate_status == CERTIFICATE_STATUS + assert retrieved.certificate_status_message == CERTIFICATE_MESSAGE + + +@pytest.mark.order(after="test_update_custom_assets") +def test_retrieve_custom_assets( + client: AtlanClient, + custom_entity: CustomEntity, +): + _retrieve_custom_assets(client, custom_entity, CustomEntity) diff --git a/tests_v9/integration/custom_metadata_test.py b/tests_v9/integration/custom_metadata_test.py new file mode 100644 index 000000000..09fe4f2cd --- /dev/null +++ b/tests_v9/integration/custom_metadata_test.py @@ -0,0 +1,1296 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2022 Atlan Pte. Ltd. +import json +import time +from typing import Generator, List, Optional, Tuple + +import pytest + +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.errors import AtlanError +from pyatlan_v9.model.assets import ( + Asset, + AtlasGlossary, + AtlasGlossaryTerm, + Badge, + Connection, +) +from pyatlan_v9.model.custom_metadata import CustomMetadataDict +from pyatlan_v9.model.enums import ( + AtlanCustomAttributePrimitiveType, + AtlanIcon, + AtlanTagColor, + AtlanTypeCategory, + BadgeComparisonOperator, + BadgeConditionColor, + Cardinality, + EntityStatus, +) +from pyatlan_v9.model.fields.atlan_fields import CustomMetadataField +from pyatlan_v9.model.fluent_search import CompoundQuery, FluentSearch +from pyatlan_v9.model.group import AtlanGroup, CreateGroupResponse +from pyatlan_v9.model.structs import BadgeCondition +from pyatlan_v9.model.typedef import AttributeDef, CustomMetadataDef, EnumDef +from tests_v9.integration.admin_test import create_group, delete_group +from tests_v9.integration.client import TestId, delete_asset +from tests_v9.integration.glossary_test import create_glossary, create_term +from tests_v9.integration.utils import ( + wait_for_successful_custometadatadef_purge, + wait_for_successful_enumadef_purge, +) + +MODULE_NAME = TestId.make_unique("CM") + +FIXED_USER = "ernest" +GROUP_NAME1 = f"{MODULE_NAME}1" +GROUP_NAME2 = f"{MODULE_NAME}2" + +CM_RACI = f"{MODULE_NAME}_RACI" +CM_ATTR_RACI_RESPONSIBLE = "Responsible" +CM_ATTR_RACI_ACCOUNTABLE = "Accountable" +CM_ATTR_RACI_CONSULTED = "Consulted" +CM_ATTR_RACI_INFORMED = "Informed" +CM_ATTR_RACI_EXTRA = "Extra" + + +CM_IPR = f"{MODULE_NAME}_IPR" +CM_ATTR_IPR_LICENSE = "License" +CM_ATTR_IPR_VERSION = "Version" +CM_ATTR_IPR_MANDATORY = "Mandatory" +CM_ATTR_IPR_DATE = "Date" +CM_ATTR_IPR_URL = "URL" + + +CM_QUALITY = f"{MODULE_NAME}_DQ" +CM_SQL = f"{MODULE_NAME}_SQL" +CM_ATTR_QUALITY_COUNT = "Count" +CM_ATTR_QUALITY_SQL = "SQL" +CM_ATTR_QUALITY_TYPE = "Type" + +CM_RICH_TEXT = f"{MODULE_NAME}_RICH_TEXT" +CM_ATTR_RICH_TEXT_CONTENT = "Rich Content" +CM_ATTR_RICH_TEXT_DESCRIPTION = "Rich Description" + +DQ_ENUM = f"{MODULE_NAME}_DataQualityType" +DQ_TYPE_LIST = [ + "Accuracy", + "Completeness", + "Consistency", + "Timeliness", + "Validity", + "Uniqueness", +] +DQ_TYPE_EXTRA_LIST = ["Unknown", "Others"] +CM_DESCRIPTION = "Automated testing of the Python SDK (cm)." +ATTRIBUTE_DESCRIPTION = "Automated testing of the Python SDK (attribute)." + +_removal_epoch: Optional[int] + + +def create_custom_metadata( + client: AtlanClient, + name: str, + attribute_defs: List[AttributeDef], + locked: bool, + logo: Optional[str] = None, + icon: Optional[AtlanIcon] = None, + color: Optional[AtlanTagColor] = None, +) -> CustomMetadataDef: + cm_def = CustomMetadataDef.creator(display_name=name, description=CM_DESCRIPTION) + cm_def.attribute_defs = attribute_defs + if icon and color: + cm_def.options = CustomMetadataDef.Options.with_logo_from_icon( + icon, color, locked + ) + elif logo and logo.startswith("http"): + cm_def.options = CustomMetadataDef.Options.with_logo_from_url(logo, locked) + elif logo: + cm_def.options = CustomMetadataDef.Options.with_logo_as_emoji(logo, locked) + else: + raise ValueError( + "Invalid configuration for the visual to use for the custom metadata." + ) + r = client.typedef.creator(cm_def) + return r.custom_metadata_defs[0] + + +def create_enum(client: AtlanClient, name: str, values: List[str]) -> EnumDef: + enum_def = EnumDef.creator(name=name, values=values) + r = client.typedef.creator(enum_def) + return r.enum_defs[0] + + +def update_enum( + client: AtlanClient, name: str, values: List[str], replace_existing: bool = False +) -> EnumDef: + enum_def = EnumDef.updater( + client=client, name=name, values=values, replace_existing=replace_existing + ) + r = client.typedef.updater(enum_def) + return r.enum_defs[0] + + +@pytest.fixture(scope="module") +def limit_attribute_applicability_kwargs( + glossary: AtlasGlossary, connection: Connection +): + return dict( + applicable_asset_types={"Link"}, + applicable_other_asset_types={"File"}, + applicable_glossaries={glossary.qualified_name}, + applicable_glossary_types={"AtlasGlossary", "AtlasGlossaryTerm"}, + applicable_connections={connection.qualified_name}, + ) + + +@pytest.fixture(scope="module") +def cm_ipr( + client: AtlanClient, limit_attribute_applicability_kwargs +) -> Generator[CustomMetadataDef, None, None]: + attribute_defs = [ + AttributeDef.creator( + client=client, + display_name=CM_ATTR_IPR_LICENSE, + attribute_type=AtlanCustomAttributePrimitiveType.STRING, + description=ATTRIBUTE_DESCRIPTION, + **limit_attribute_applicability_kwargs, + ), + AttributeDef.creator( + client=client, + display_name=CM_ATTR_IPR_VERSION, + attribute_type=AtlanCustomAttributePrimitiveType.DECIMAL, + ), + AttributeDef.creator( + client=client, + display_name=CM_ATTR_IPR_MANDATORY, + attribute_type=AtlanCustomAttributePrimitiveType.BOOLEAN, + ), + AttributeDef.creator( + client=client, + display_name=CM_ATTR_IPR_DATE, + attribute_type=AtlanCustomAttributePrimitiveType.DATE, + ), + AttributeDef.creator( + client=client, + display_name=CM_ATTR_IPR_URL, + attribute_type=AtlanCustomAttributePrimitiveType.URL, + ), + ] + cm = create_custom_metadata( + client, name=CM_IPR, attribute_defs=attribute_defs, logo="⚖️", locked=True + ) + yield cm + wait_for_successful_custometadatadef_purge(CM_IPR, client=client) + + +def test_cm_ipr(cm_ipr: CustomMetadataDef, limit_attribute_applicability_kwargs): + cm_name = CM_IPR + assert cm_ipr.category == AtlanTypeCategory.CUSTOM_METADATA + assert cm_ipr.guid + assert cm_ipr.name != cm_name + assert cm_ipr.display_name == cm_name + assert cm_ipr.description == CM_DESCRIPTION + attributes = cm_ipr.attribute_defs + assert attributes + assert len(attributes) == 5 + one_with_limited = attributes[0] + assert one_with_limited + assert one_with_limited.options + assert one_with_limited.display_name == CM_ATTR_IPR_LICENSE + assert one_with_limited.name + assert one_with_limited.description == ATTRIBUTE_DESCRIPTION + assert one_with_limited.name != CM_ATTR_IPR_LICENSE + assert one_with_limited.type_name == AtlanCustomAttributePrimitiveType.STRING.value + assert not one_with_limited.options.multi_value_select + options = one_with_limited.options + for attribute in limit_attribute_applicability_kwargs.keys(): + assert getattr( + one_with_limited, attribute + ) == limit_attribute_applicability_kwargs.get(attribute) + assert getattr(options, attribute) == json.dumps( + list(limit_attribute_applicability_kwargs.get(attribute)) + ) + one = attributes[1] + assert one.display_name == CM_ATTR_IPR_VERSION + assert one.name != CM_ATTR_IPR_VERSION + assert one.type_name == AtlanCustomAttributePrimitiveType.DECIMAL.value + assert one.options + assert not one.options.multi_value_select + one = attributes[2] + assert one.display_name == CM_ATTR_IPR_MANDATORY + assert one.name != CM_ATTR_IPR_MANDATORY + assert one.type_name == AtlanCustomAttributePrimitiveType.BOOLEAN.value + assert one.options + assert not one.options.multi_value_select + one = attributes[3] + assert one.display_name == CM_ATTR_IPR_DATE + assert one.name != CM_ATTR_IPR_DATE + assert one.type_name == AtlanCustomAttributePrimitiveType.DATE.value + assert one.options + assert not one.options.multi_value_select + one = attributes[4] + assert one.display_name == CM_ATTR_IPR_URL + assert one.name != CM_ATTR_IPR_URL + assert one.type_name == AtlanCustomAttributePrimitiveType.STRING.value + assert one.options + assert not one.options.multi_value_select + + +@pytest.fixture(scope="module") +def cm_raci( + client: AtlanClient, +) -> Generator[CustomMetadataDef, None, None]: + TEST_MULTI_VALUE_USING_SETTER = AttributeDef.creator( + client=client, + display_name=CM_ATTR_RACI_INFORMED, + attribute_type=AtlanCustomAttributePrimitiveType.GROUPS, + ) + assert TEST_MULTI_VALUE_USING_SETTER and TEST_MULTI_VALUE_USING_SETTER.options + TEST_MULTI_VALUE_USING_SETTER.options.multi_value_select = True + attribute_defs = [ + AttributeDef.creator( + client=client, + display_name=CM_ATTR_RACI_RESPONSIBLE, + attribute_type=AtlanCustomAttributePrimitiveType.USERS, + multi_valued=True, + ), + AttributeDef.creator( + client=client, + display_name=CM_ATTR_RACI_ACCOUNTABLE, + attribute_type=AtlanCustomAttributePrimitiveType.USERS, + ), + AttributeDef.creator( + client=client, + display_name=CM_ATTR_RACI_CONSULTED, + attribute_type=AtlanCustomAttributePrimitiveType.GROUPS, + multi_valued=True, + ), + TEST_MULTI_VALUE_USING_SETTER, + AttributeDef.creator( + client=client, + display_name=CM_ATTR_RACI_EXTRA, + attribute_type=AtlanCustomAttributePrimitiveType.STRING, + ), + ] + cm = create_custom_metadata( + client, + name=CM_RACI, + attribute_defs=attribute_defs, + icon=AtlanIcon.USERS_THREE, + color=AtlanTagColor.GRAY, + locked=False, + ) + yield cm + wait_for_successful_custometadatadef_purge(CM_RACI, client=client) + + +def test_cm_raci( + cm_raci: CustomMetadataDef, +): + assert cm_raci.category == AtlanTypeCategory.CUSTOM_METADATA + assert cm_raci.name + assert cm_raci.guid + cm_name = CM_RACI + assert cm_raci.name != cm_name + assert cm_raci.display_name == cm_name + attributes = cm_raci.attribute_defs + assert attributes + assert len(attributes) == 5 + one = attributes[0] + assert one + assert one.display_name == CM_ATTR_RACI_RESPONSIBLE + assert one.name + assert one.name != CM_ATTR_RACI_RESPONSIBLE + assert one.type_name == f"array<{AtlanCustomAttributePrimitiveType.STRING.value}>" + assert one.options + assert one.cardinality == Cardinality.SET + assert one.options.multi_value_select + one = attributes[1] + assert one.display_name == CM_ATTR_RACI_ACCOUNTABLE + assert one.name != CM_ATTR_RACI_ACCOUNTABLE + assert one.type_name == AtlanCustomAttributePrimitiveType.STRING.value + assert one.options + assert not one.options.multi_value_select + one = attributes[2] + assert one.display_name == CM_ATTR_RACI_CONSULTED + assert one.name != CM_ATTR_RACI_CONSULTED + assert one.type_name == f"array<{AtlanCustomAttributePrimitiveType.STRING.value}>" + assert one.options + assert one.cardinality == Cardinality.SET + assert one.options.multi_value_select + one = attributes[3] + assert one.display_name == CM_ATTR_RACI_INFORMED + assert one.name != CM_ATTR_RACI_INFORMED + assert one.type_name == f"array<{AtlanCustomAttributePrimitiveType.STRING.value}>" + assert one.options + assert one.cardinality == Cardinality.SET + assert one.options.multi_value_select + one = attributes[4] + assert one.display_name == CM_ATTR_RACI_EXTRA + assert one.name != CM_ATTR_RACI_EXTRA + assert one.type_name == AtlanCustomAttributePrimitiveType.STRING.value + assert one.options + assert not one.options.multi_value_select + + +@pytest.fixture(scope="module") +def cm_enum( + client: AtlanClient, +) -> Generator[EnumDef, None, None]: + enum_def = create_enum(client, name=DQ_ENUM, values=DQ_TYPE_LIST) + yield enum_def + wait_for_successful_enumadef_purge(DQ_ENUM, client=client) + + +def test_cm_enum( + cm_enum: EnumDef, +): + assert cm_enum.category == AtlanTypeCategory.ENUM + assert cm_enum.name == DQ_ENUM + assert cm_enum.guid + assert cm_enum.element_defs + assert len(cm_enum.element_defs) == len(DQ_TYPE_LIST) + + +@pytest.mark.order(after="test_cm_enum") +def test_cm_enum_get_by_name(client: AtlanClient): + cm_enum = client.typedef.get_by_name(name=DQ_ENUM) + + assert cm_enum and isinstance(cm_enum, EnumDef) + assert cm_enum.guid + assert cm_enum.element_defs + assert cm_enum.name == DQ_ENUM + assert cm_enum.category == AtlanTypeCategory.ENUM + assert len(cm_enum.element_defs) == len(DQ_TYPE_LIST) + + +@pytest.fixture(scope="module") +def cm_enum_update( + client: AtlanClient, +) -> Generator[EnumDef, None, None]: + enum_def = update_enum(client, name=DQ_ENUM, values=DQ_TYPE_EXTRA_LIST) + yield enum_def + + +@pytest.fixture(scope="module") +def cm_enum_update_with_replace( + client: AtlanClient, +) -> Generator[EnumDef, None, None]: + enum_def = update_enum( + client, name=DQ_ENUM, values=DQ_TYPE_LIST, replace_existing=True + ) + yield enum_def + + +@pytest.mark.order(after="test_cm_enum") +def test_cm_enum_update( + cm_enum_update: EnumDef, + cm_enum_update_with_replace: EnumDef, +): + assert cm_enum_update.guid + assert cm_enum_update.name == DQ_ENUM + assert cm_enum_update.element_defs + assert cm_enum_update.category == AtlanTypeCategory.ENUM + EM_VALUES = DQ_TYPE_LIST + DQ_TYPE_EXTRA_LIST + assert len(cm_enum_update.element_defs) == len(EM_VALUES) + for index, element_def in enumerate(cm_enum_update.element_defs): + assert element_def.value == EM_VALUES[index] + + assert cm_enum_update_with_replace.guid + assert cm_enum_update_with_replace.name == DQ_ENUM + assert cm_enum_update_with_replace.element_defs + assert cm_enum_update_with_replace.category == AtlanTypeCategory.ENUM + assert len(cm_enum_update_with_replace.element_defs) == len(DQ_TYPE_LIST) + + +@pytest.fixture(scope="module") +def cm_dq( + client: AtlanClient, + cm_enum: EnumDef, +) -> Generator[CustomMetadataDef, None, None]: + attribute_defs = [ + AttributeDef.creator( + client=client, + display_name=CM_ATTR_QUALITY_COUNT, + attribute_type=AtlanCustomAttributePrimitiveType.INTEGER, + ), + AttributeDef.creator( + client=client, + display_name=CM_ATTR_QUALITY_SQL, + attribute_type=AtlanCustomAttributePrimitiveType.SQL, + ), + AttributeDef.creator( + client=client, + display_name=CM_ATTR_QUALITY_TYPE, + attribute_type=AtlanCustomAttributePrimitiveType.OPTIONS, + options_name=DQ_ENUM, + ), + ] + cm = create_custom_metadata( + client, + name=CM_QUALITY, + attribute_defs=attribute_defs, + logo="https://github.com/great-expectations/great_expectations/raw/develop/docs/docusaurus/static/img/" + "gx-mark-160.png", + locked=True, + ) + yield cm + wait_for_successful_custometadatadef_purge(CM_QUALITY, client=client) + + +@pytest.fixture(scope="module") +def cm_sql( + client: AtlanClient, + term: AtlasGlossaryTerm, +) -> Generator[CustomMetadataDef, None, None]: + attribute_defs = [ + AttributeDef.creator( + client=client, + display_name=f"{MODULE_NAME}_{CM_ATTR_QUALITY_SQL}", + attribute_type=AtlanCustomAttributePrimitiveType.SQL, + ), + ] + cm = create_custom_metadata( + client, + name=CM_SQL, + attribute_defs=attribute_defs, + logo="https://github.com/great-expectations/great_expectations/raw/develop/docs/docusaurus/static/img/" + "gx-mark-160.png", + locked=False, + ) + yield cm + client.asset.remove_custom_metadata(term.guid, cm_name=CM_SQL) + wait_for_successful_custometadatadef_purge(CM_SQL, client=client) + + +def test_cm_dq( + cm_dq: CustomMetadataDef, +): + cm_name = CM_QUALITY + assert cm_dq.category == AtlanTypeCategory.CUSTOM_METADATA + assert cm_dq.name + assert cm_dq.guid + assert cm_dq.name != cm_name + assert cm_dq.display_name == cm_name + attributes = cm_dq.attribute_defs + assert attributes + assert len(attributes) == 3 + one = attributes[0] + assert one + assert one.display_name == CM_ATTR_QUALITY_COUNT + assert one.name + assert one.name != CM_ATTR_QUALITY_COUNT + assert one.type_name == AtlanCustomAttributePrimitiveType.INTEGER.value + assert one.options + assert not one.options.multi_value_select + one = attributes[1] + assert one.display_name == CM_ATTR_QUALITY_SQL + assert one.name != CM_ATTR_QUALITY_SQL + assert one.type_name == AtlanCustomAttributePrimitiveType.STRING.value + assert one.options + assert not one.options.multi_value_select + assert one.options.custom_type == AtlanCustomAttributePrimitiveType.SQL.value + one = attributes[2] + assert one.display_name == CM_ATTR_QUALITY_TYPE + assert one.name != CM_ATTR_QUALITY_TYPE + assert one.type_name == DQ_ENUM + assert one.options + assert not one.options.multi_value_select + assert one.options.primitive_type == AtlanCustomAttributePrimitiveType.OPTIONS.value + + +@pytest.fixture(scope="module") +def cm_rich_text( + client: AtlanClient, +) -> Generator[CustomMetadataDef, None, None]: + attribute_defs = [ + AttributeDef.creator( + client=client, + display_name=CM_ATTR_RICH_TEXT_CONTENT, + attribute_type=AtlanCustomAttributePrimitiveType.RICH_TEXT, + description=ATTRIBUTE_DESCRIPTION, + ), + AttributeDef.creator( + client=client, + display_name=CM_ATTR_RICH_TEXT_DESCRIPTION, + attribute_type=AtlanCustomAttributePrimitiveType.RICH_TEXT, + ), + ] + cm = create_custom_metadata( + client, + name=CM_RICH_TEXT, + attribute_defs=attribute_defs, + logo="📝", + locked=False, + ) + yield cm + wait_for_successful_custometadatadef_purge(CM_RICH_TEXT, client=client) + + +def test_cm_rich_text(cm_rich_text: CustomMetadataDef): + cm_name = CM_RICH_TEXT + assert cm_rich_text.category == AtlanTypeCategory.CUSTOM_METADATA + assert cm_rich_text.name + assert cm_rich_text.guid + assert cm_rich_text.name != cm_name + assert cm_rich_text.display_name == cm_name + attributes = cm_rich_text.attribute_defs + assert attributes + assert len(attributes) == 2 + + # Test first attribute + content_attr = attributes[0] + assert content_attr + assert content_attr.display_name == CM_ATTR_RICH_TEXT_CONTENT + assert content_attr.name + assert content_attr.name != CM_ATTR_RICH_TEXT_CONTENT + assert content_attr.type_name == AtlanCustomAttributePrimitiveType.STRING.value + assert content_attr.options + assert content_attr.options.is_rich_text is True + assert not content_attr.options.multi_value_select + assert content_attr.description == ATTRIBUTE_DESCRIPTION + + # Test second attribute + desc_attr = attributes[1] + assert desc_attr.display_name == CM_ATTR_RICH_TEXT_DESCRIPTION + assert desc_attr.name != CM_ATTR_RICH_TEXT_DESCRIPTION + assert desc_attr.type_name == AtlanCustomAttributePrimitiveType.STRING.value + assert desc_attr.options + assert desc_attr.options.is_rich_text is True + assert not desc_attr.options.multi_value_select + + +def test_rich_text_cannot_be_multi_valued(client: AtlanClient): + """Test that RICH_TEXT attributes cannot be multi-valued""" + + with pytest.raises(AtlanError) as exc_info: + AttributeDef.creator( + client=client, + display_name="Invalid Rich Text", + attribute_type=AtlanCustomAttributePrimitiveType.RICH_TEXT, + multi_valued=True, + ) + + error = exc_info.value + assert "ATLAN-PYTHON-400-076" in str(error) + + +@pytest.fixture(scope="module") +def glossary( + client: AtlanClient, +) -> Generator[AtlasGlossary, None, None]: + glossary_name = MODULE_NAME + g = create_glossary(client, name=glossary_name) + yield g + delete_asset(client, guid=g.guid, asset_type=AtlasGlossary) + + +@pytest.fixture(scope="module") +def term( + client: AtlanClient, + glossary: AtlasGlossary, + cm_raci: CustomMetadataDef, + cm_ipr: CustomMetadataDef, + cm_dq: CustomMetadataDef, +) -> Generator[AtlasGlossaryTerm, None, None]: + term_name = MODULE_NAME + t = create_term(client, name=term_name, glossary_guid=glossary.guid) + yield t + delete_asset(client, guid=t.guid, asset_type=AtlasGlossaryTerm) + + +@pytest.fixture(scope="module") +def groups( + client: AtlanClient, + glossary: AtlasGlossary, + term: AtlasGlossaryTerm, + cm_raci: CustomMetadataDef, + cm_ipr: CustomMetadataDef, + cm_dq: CustomMetadataDef, +) -> Generator[List[CreateGroupResponse], None, None]: + g1 = create_group(client, GROUP_NAME1) + g2 = create_group(client, GROUP_NAME2) + yield [g1, g2] + delete_group(client, g1.group) + delete_group(client, g2.group) + + +def _get_groups( + client: AtlanClient, +) -> Tuple[AtlanGroup, AtlanGroup]: + candidates = client.group.get_by_name(GROUP_NAME1) + assert candidates + assert candidates.records is not None + assert len(candidates.records) == 1 + group1 = candidates.records[0] + candidates = client.group.get_by_name(GROUP_NAME2) + assert candidates + assert candidates.records is not None + assert len(candidates.records) == 1 + group2 = candidates.records[0] + return group1, group2 + + +def test_add_term_cm_raci( + client: AtlanClient, + cm_raci: CustomMetadataDef, + term: AtlasGlossaryTerm, + groups: List[AtlanGroup], +): + cm_name = CM_RACI + raci_attrs = CustomMetadataDict(client=client, name=cm_name) + _validate_raci_empty(raci_attrs) + group1, group2 = _get_groups(client) + raci_attrs[CM_ATTR_RACI_RESPONSIBLE] = [FIXED_USER] + raci_attrs[CM_ATTR_RACI_ACCOUNTABLE] = FIXED_USER + raci_attrs[CM_ATTR_RACI_CONSULTED] = [group1.name] + raci_attrs[CM_ATTR_RACI_INFORMED] = [group1.name, group2.name] + client.asset.update_custom_metadata_attributes(term.guid, raci_attrs) + t = client.asset.retrieve_minimal(guid=term.guid, asset_type=AtlasGlossaryTerm) + assert t + _validate_raci_attributes( + client, t.get_custom_metadata(client=client, name=cm_name) + ) + + +def test_add_term_cm_ipr( + client: AtlanClient, + cm_ipr: CustomMetadataDef, + term: AtlasGlossaryTerm, +): + cm_name = CM_IPR + ipr_attrs = CustomMetadataDict(client=client, name=cm_name) + _validate_ipr_empty(ipr_attrs) + ipr_attrs[CM_ATTR_IPR_LICENSE] = "CC BY" + ipr_attrs[CM_ATTR_IPR_VERSION] = 2.0 + ipr_attrs[CM_ATTR_IPR_MANDATORY] = True + ipr_attrs[CM_ATTR_IPR_DATE] = 1659308400000 + ipr_attrs[CM_ATTR_IPR_URL] = "https://creativecommons.org/licenses/by/2.0/" + + client.asset.update_custom_metadata_attributes(term.guid, ipr_attrs) + t = client.asset.retrieve_minimal(guid=term.guid, asset_type=AtlasGlossaryTerm) + assert t + _validate_ipr_attributes(t.get_custom_metadata(client=client, name=cm_name)) + + +def test_add_term_cm_dq( + client: AtlanClient, + cm_dq: CustomMetadataDef, + term: AtlasGlossaryTerm, +): + cm_name = CM_QUALITY + dq_attrs = CustomMetadataDict(client=client, name=cm_name) + _validate_dq_empty(dq_attrs) + dq_attrs[CM_ATTR_QUALITY_COUNT] = 42 + dq_attrs[CM_ATTR_QUALITY_SQL] = "SELECT * from SOMEWHERE;" + dq_attrs[CM_ATTR_QUALITY_TYPE] = "Completeness" + client.asset.update_custom_metadata_attributes(term.guid, dq_attrs) + t = client.asset.retrieve_minimal(guid=term.guid, asset_type=AtlasGlossaryTerm) + assert t + _validate_dq_attributes(t.get_custom_metadata(client=client, name=cm_name)) + + +@pytest.mark.order(after="test_add_term_cm_dq") +def test_update_term_cm_ipr( + client: AtlanClient, + cm_ipr: CustomMetadataDef, + term: AtlasGlossaryTerm, +): + cm_name = CM_IPR + ipr = CustomMetadataDict(client=client, name=cm_name) + # Note: MUST access the getter / setter, not the underlying store + ipr[CM_ATTR_IPR_MANDATORY] = False + client.asset.update_custom_metadata_attributes(term.guid, ipr) + t = client.asset.retrieve_minimal(guid=term.guid, asset_type=AtlasGlossaryTerm) + assert t + _validate_ipr_attributes( + t.get_custom_metadata(client=client, name=cm_name), mandatory=False + ) + _validate_raci_attributes( + client, t.get_custom_metadata(client=client, name=CM_RACI) + ) + _validate_dq_attributes(t.get_custom_metadata(client=client, name=CM_QUALITY)) + + +@pytest.mark.order(after="test_update_term_cm_ipr") +def test_replace_term_cm_raci( + client: AtlanClient, + cm_raci: CustomMetadataDef, + term: AtlasGlossaryTerm, +): + raci = CustomMetadataDict(client=client, name=CM_RACI) + group1, group2 = _get_groups(client) + raci[CM_ATTR_RACI_RESPONSIBLE] = [FIXED_USER] + raci[CM_ATTR_RACI_ACCOUNTABLE] = FIXED_USER + raci[CM_ATTR_RACI_CONSULTED] = None + raci[CM_ATTR_RACI_INFORMED] = [group1.name, group2.name] + client.asset.replace_custom_metadata(term.guid, raci) + t = client.asset.retrieve_minimal(guid=term.guid, asset_type=AtlasGlossaryTerm) + assert t + _validate_raci_attributes_replacement( + client, t.get_custom_metadata(client=client, name=CM_RACI) + ) + _validate_ipr_attributes( + t.get_custom_metadata(client=client, name=CM_IPR), mandatory=False + ) + _validate_dq_attributes(t.get_custom_metadata(client=client, name=CM_QUALITY)) + + +@pytest.mark.order(after="test_replace_term_cm_raci") +def test_replace_term_cm_ipr( + client: AtlanClient, + cm_ipr: CustomMetadataDef, + term: AtlasGlossaryTerm, +): + term_cm_ipr = CustomMetadataDict(client=client, name=CM_IPR) + client.asset.replace_custom_metadata(term.guid, term_cm_ipr) + t = client.asset.retrieve_minimal(guid=term.guid, asset_type=AtlasGlossaryTerm) + assert t + _validate_raci_attributes_replacement( + client, t.get_custom_metadata(client=client, name=CM_RACI) + ) + _validate_dq_attributes(t.get_custom_metadata(client=client, name=CM_QUALITY)) + _validate_ipr_empty(t.get_custom_metadata(client=client, name=CM_IPR)) + + +@pytest.mark.order(after="test_replace_term_cm_ipr") +def test_search_by_any_accountable( + client: AtlanClient, + cm_raci: CustomMetadataDef, + glossary: AtlasGlossary, + term: AtlasGlossaryTerm, +): + attributes = ["name", "anchor"] + cm_attributes = client.custom_metadata_cache.get_attributes_for_search_results( + set_name=CM_RACI + ) + assert cm_attributes + attributes.extend(cm_attributes) + request = ( + FluentSearch(_includes_on_results=attributes) + .where(CompoundQuery.active_assets()) + .where(CompoundQuery.asset_type(AtlasGlossaryTerm)) + .where( + CustomMetadataField( + client, CM_RACI, CM_ATTR_RACI_ACCOUNTABLE + ).has_any_value() + ) + .include_on_relations(Asset.NAME) + ).to_request() + response = client.asset.search(criteria=request) + assert response + count = 0 + # TODO: replace with exponential back-off and jitter + while response.count == 0 and count < 10: + time.sleep(2) + response = client.asset.search(criteria=request) + count += 1 + assert response.count == 1 + for t in response: + assert isinstance(t, AtlasGlossaryTerm) + assert t.guid == term.guid + assert t.qualified_name == term.qualified_name + anchor = t.attributes.anchor + assert anchor + assert anchor.name == glossary.name + _validate_raci_attributes_replacement( + client, t.get_custom_metadata(client=client, name=CM_RACI) + ) + + +@pytest.mark.order(after="test_replace_term_cm_ipr") +def test_search_by_specific_accountable( + client: AtlanClient, + cm_raci: CustomMetadataDef, + glossary: AtlasGlossary, + term: AtlasGlossaryTerm, +): + request = ( + FluentSearch() + .where(CompoundQuery.active_assets()) + .where(CompoundQuery.asset_type(AtlasGlossaryTerm)) + .where( + CustomMetadataField(client, CM_RACI, CM_ATTR_RACI_ACCOUNTABLE).eq( + FIXED_USER + ) + ) + .include_on_results(Asset.NAME) + .include_on_results(AtlasGlossaryTerm.ANCHOR) + .include_on_relations(Asset.NAME) + ).to_request() + response = client.asset.search(criteria=request) + assert response + count = 0 + # TODO: replace with exponential back-off and jitter + while response.count == 0 and count < 10: + time.sleep(2) + response = client.asset.search(criteria=request) + count += 1 + assert response.count == 1 + for t in response: + assert isinstance(t, AtlasGlossaryTerm) + assert t.guid == term.guid + assert t.qualified_name == term.qualified_name + anchor = t.attributes.anchor + assert anchor + assert anchor.name == glossary.name + + +@pytest.mark.order( + after=["test_search_by_any_accountable", "test_search_by_specific_accountable"] +) +def test_remove_term_cm_raci( + client: AtlanClient, + cm_raci: CustomMetadataDef, + term: AtlasGlossaryTerm, +): + client.asset.remove_custom_metadata(term.guid, cm_name=CM_RACI) + t = client.asset.retrieve_minimal(guid=term.guid, asset_type=AtlasGlossaryTerm) + assert t + _validate_dq_attributes(t.get_custom_metadata(client=client, name=CM_QUALITY)) + _validate_ipr_empty(t.get_custom_metadata(client=client, name=CM_IPR)) + _validate_raci_empty(t.get_custom_metadata(client=client, name=CM_RACI)) + + +@pytest.mark.order(after="test_remove_term_cm_raci") +def test_remove_term_cm_ipr( + client: AtlanClient, + cm_ipr: CustomMetadataDef, + term: AtlasGlossaryTerm, +): + client.asset.remove_custom_metadata(term.guid, cm_name=CM_IPR) + t = client.asset.retrieve_minimal(guid=term.guid, asset_type=AtlasGlossaryTerm) + assert t + _validate_dq_attributes(t.get_custom_metadata(client=client, name=CM_QUALITY)) + _validate_ipr_empty(t.get_custom_metadata(client=client, name=CM_IPR)) + _validate_raci_empty(t.get_custom_metadata(client=client, name=CM_RACI)) + + +@pytest.mark.order(after="test_remove_term_cm_raci") +def test_remove_attribute(client: AtlanClient, cm_raci: CustomMetadataDef): + global _removal_epoch + cm_name = CM_RACI + existing = client.custom_metadata_cache.get_custom_metadata_def(name=cm_name) + existing_attrs = existing.attribute_defs + updated_attrs = [] + for existing_attr in existing_attrs: + to_keep = existing_attr + if existing_attr.display_name == CM_ATTR_RACI_EXTRA: + to_keep = existing_attr.archive(by="test-automation") + assert to_keep.options + _removal_epoch = to_keep.options.archived_at + updated_attrs.append(to_keep) + existing.attribute_defs = updated_attrs + response = client.typedef.updater(existing) + assert response + assert len(response.custom_metadata_defs) == 1 + updated = response.custom_metadata_defs[0] + assert updated.category == AtlanTypeCategory.CUSTOM_METADATA + assert updated.name != cm_name + assert updated.guid + assert updated.display_name == cm_name + attributes = updated.attribute_defs + archived = _validate_raci_structure(attributes, 5) + assert archived + assert ( + archived.display_name == f"{CM_ATTR_RACI_EXTRA}-archived-{str(_removal_epoch)}" + ) + assert archived.name != CM_ATTR_RACI_EXTRA + assert archived.type_name == AtlanCustomAttributePrimitiveType.STRING.value + assert not archived.options.multi_value_select + assert archived.is_archived() + + +@pytest.mark.order(after="test_remove_attribute") +def test_retrieve_structures(client: AtlanClient, cm_raci: CustomMetadataDef): + global _removal_epoch + custom_attributes = client.custom_metadata_cache.get_all_custom_attributes() + assert custom_attributes + assert len(custom_attributes) >= 3 + assert CM_RACI in custom_attributes.keys() + assert CM_IPR in custom_attributes.keys() + assert CM_QUALITY in custom_attributes.keys() + extra = _validate_raci_structure(custom_attributes.get(CM_RACI), 4) + assert not extra + custom_attributes = client.custom_metadata_cache.get_all_custom_attributes( + include_deleted=True + ) + assert custom_attributes + assert CM_RACI in custom_attributes.keys() + assert CM_IPR in custom_attributes.keys() + assert CM_QUALITY in custom_attributes.keys() + extra = _validate_raci_structure(custom_attributes.get(CM_RACI), 5) + assert extra + assert extra.display_name == f"{CM_ATTR_RACI_EXTRA}-archived-{str(_removal_epoch)}" + assert extra.name != CM_ATTR_RACI_EXTRA + assert extra.type_name == AtlanCustomAttributePrimitiveType.STRING.value + assert "Database" in extra.applicable_asset_types + assert not extra.options.multi_value_select + assert extra.is_archived() + + +@pytest.mark.order(after="test_retrieve_structures") +def test_recreate_attribute(client: AtlanClient, cm_raci: CustomMetadataDef): + existing = client.custom_metadata_cache.get_custom_metadata_def(name=CM_RACI) + existing_attrs = existing.attribute_defs + updated_attrs = [] + for existing_attr in existing_attrs: + existing_attr.is_new = None + updated_attrs.append(existing_attr) + new_attr = AttributeDef.creator( + client=client, + display_name=CM_ATTR_RACI_EXTRA, + attribute_type=AtlanCustomAttributePrimitiveType.STRING, + ) + updated_attrs.append(new_attr) + existing.attribute_defs = updated_attrs + response = client.typedef.updater(existing) + assert response + assert len(response.custom_metadata_defs) == 1 + updated = response.custom_metadata_defs[0] + assert updated.category == AtlanTypeCategory.CUSTOM_METADATA + assert updated.name != CM_RACI + assert updated.guid + assert updated.display_name == CM_RACI + attributes = updated.attribute_defs + extra = _validate_raci_structure(attributes, 6) + assert extra + assert extra.display_name == CM_ATTR_RACI_EXTRA + assert extra.name != CM_ATTR_RACI_EXTRA + assert extra.type_name == AtlanCustomAttributePrimitiveType.STRING.value + assert not extra.options.multi_value_select + assert not extra.is_archived() + + +@pytest.mark.order(after="test_recreate_attribute") +def test_retrieve_structure_without_archived( + client: AtlanClient, cm_raci: CustomMetadataDef +): + custom_attributes = client.custom_metadata_cache.get_all_custom_attributes() + assert custom_attributes + assert len(custom_attributes) >= 3 + assert CM_RACI in custom_attributes.keys() + assert CM_IPR in custom_attributes.keys() + assert CM_QUALITY in custom_attributes.keys() + extra = _validate_raci_structure(custom_attributes.get(CM_RACI), 5) + assert extra + assert extra.display_name == CM_ATTR_RACI_EXTRA + assert extra.name != CM_ATTR_RACI_EXTRA + assert extra.type_name == AtlanCustomAttributePrimitiveType.STRING.value + assert "Database" in extra.applicable_asset_types + assert not extra.is_archived() + + +@pytest.mark.order(after="test_recreate_attribute") +def test_retrieve_structure_with_archived( + client: AtlanClient, cm_raci: CustomMetadataDef +): + custom_attributes = client.custom_metadata_cache.get_all_custom_attributes( + include_deleted=True + ) + assert custom_attributes + assert len(custom_attributes) >= 3 + assert CM_RACI in custom_attributes.keys() + assert CM_IPR in custom_attributes.keys() + assert CM_QUALITY in custom_attributes.keys() + extra = _validate_raci_structure(custom_attributes.get(CM_RACI), 6) + assert extra + assert extra.display_name == CM_ATTR_RACI_EXTRA + assert extra.name != CM_ATTR_RACI_EXTRA + assert extra.type_name == AtlanCustomAttributePrimitiveType.STRING.value + assert "Database" in extra.applicable_asset_types + assert not extra.is_archived() + + +@pytest.mark.order(after="test_recreate_attribute") +def test_update_replacing_cm( + term: AtlasGlossaryTerm, + glossary: AtlasGlossary, + cm_raci: CustomMetadataDef, + cm_ipr: CustomMetadataDef, + cm_dq: CustomMetadataDef, + client: AtlanClient, +): + raci = CustomMetadataDict(client=client, name=CM_RACI) + group1, group2 = _get_groups(client) + raci[CM_ATTR_RACI_RESPONSIBLE] = [FIXED_USER] + raci[CM_ATTR_RACI_ACCOUNTABLE] = FIXED_USER + raci[CM_ATTR_RACI_CONSULTED] = [group1.name] + raci[CM_ATTR_RACI_INFORMED] = [group1.name, group2.name] + raci[CM_ATTR_RACI_EXTRA] = "something extra..." + assert term.qualified_name + assert term.name + to_update = AtlasGlossaryTerm.create_for_modification( + qualified_name=term.qualified_name, name=term.name, glossary_guid=glossary.guid + ) + to_update.set_custom_metadata(custom_metadata=raci, client=client) + response = client.asset.update_replacing_cm(to_update, replace_atlan_tags=False) + assert response + assert len(response.assets_deleted(asset_type=AtlasGlossaryTerm)) == 0 + assert len(response.assets_created(asset_type=AtlasGlossaryTerm)) == 0 + assert len(response.assets_updated(asset_type=AtlasGlossaryTerm)) == 1 + t = response.assets_updated(asset_type=AtlasGlossaryTerm)[0] + assert isinstance(t, AtlasGlossaryTerm) + assert t.guid == term.guid + assert t.qualified_name == term.qualified_name + assert term.qualified_name + x = client.asset.get_by_qualified_name( + qualified_name=term.qualified_name, + asset_type=AtlasGlossaryTerm, + ignore_relationships=False, + ) + assert x + assert not x.is_incomplete + assert x.qualified_name == term.qualified_name + raci = x.get_custom_metadata(client=client, name=CM_RACI) + _validate_raci_attributes(client, raci) + assert raci[CM_ATTR_RACI_EXTRA] == "something extra..." + _validate_ipr_empty(x.get_custom_metadata(client=client, name=CM_IPR)) + _validate_dq_empty(x.get_custom_metadata(client=client, name=CM_QUALITY)) + + +# TODO: test entity audit retrieval and parsing, once available + + +def _validate_raci_attributes(client: AtlanClient, cma: CustomMetadataDict): + assert cma + # Note: MUST access the getter / setter, not the underlying store + responsible = cma[CM_ATTR_RACI_RESPONSIBLE] + accountable = cma[CM_ATTR_RACI_ACCOUNTABLE] + consulted = cma[CM_ATTR_RACI_CONSULTED] + informed = cma[CM_ATTR_RACI_INFORMED] + group1, group2 = _get_groups(client) + assert responsible + assert len(responsible) == 1 + assert FIXED_USER in responsible + assert accountable + assert accountable == FIXED_USER + assert consulted == [group1.name] + assert informed == [group1.name, group2.name] + + +def _validate_raci_attributes_replacement(client: AtlanClient, cma: CustomMetadataDict): + assert cma + # Note: MUST access the getter / setter, not the underlying store + responsible = cma[CM_ATTR_RACI_RESPONSIBLE] + accountable = cma[CM_ATTR_RACI_ACCOUNTABLE] + consulted = cma[CM_ATTR_RACI_CONSULTED] + informed = cma[CM_ATTR_RACI_INFORMED] + group1, group2 = _get_groups(client) + assert responsible + assert responsible == [FIXED_USER] + assert accountable + assert accountable == FIXED_USER + assert not consulted + assert informed == [group1.name, group2.name] + + +def _validate_raci_empty(raci_attrs: CustomMetadataDict): + attribute_names = raci_attrs.attribute_names + assert CM_ATTR_RACI_RESPONSIBLE in attribute_names + assert CM_ATTR_RACI_ACCOUNTABLE in attribute_names + assert CM_ATTR_RACI_CONSULTED in attribute_names + assert CM_ATTR_RACI_INFORMED in attribute_names + assert CM_ATTR_RACI_EXTRA in attribute_names + assert not raci_attrs[CM_ATTR_RACI_RESPONSIBLE] + assert not raci_attrs[CM_ATTR_RACI_ACCOUNTABLE] + assert not raci_attrs[CM_ATTR_RACI_CONSULTED] # could be empty list + assert not raci_attrs[CM_ATTR_RACI_INFORMED] # could be empty list + assert not raci_attrs[CM_ATTR_RACI_EXTRA] + + +def _validate_ipr_attributes(cma: CustomMetadataDict, mandatory: bool = True): + assert cma + license = cma[CM_ATTR_IPR_LICENSE] + v = cma[CM_ATTR_IPR_VERSION] + m = cma[CM_ATTR_IPR_MANDATORY] + d = cma[CM_ATTR_IPR_DATE] + u = cma[CM_ATTR_IPR_URL] + assert license + assert license == "CC BY" + assert v + assert v == 2.0 + if mandatory: + assert m + else: + assert not m + assert d + assert d == 1659308400000 + assert u + assert u == "https://creativecommons.org/licenses/by/2.0/" + + +def _validate_ipr_empty(ipr_attrs: CustomMetadataDict): + attribute_names = ipr_attrs.attribute_names + assert CM_ATTR_IPR_LICENSE in attribute_names + assert CM_ATTR_IPR_VERSION in attribute_names + assert CM_ATTR_IPR_MANDATORY in attribute_names + assert CM_ATTR_IPR_DATE in attribute_names + assert CM_ATTR_IPR_URL in attribute_names + assert not ipr_attrs[CM_ATTR_IPR_LICENSE] + assert not ipr_attrs[CM_ATTR_IPR_VERSION] + assert not ipr_attrs[CM_ATTR_IPR_MANDATORY] + assert not ipr_attrs[CM_ATTR_IPR_DATE] + assert not ipr_attrs[CM_ATTR_IPR_URL] + + +def _validate_dq_attributes(cma: CustomMetadataDict): + assert cma + c = cma[CM_ATTR_QUALITY_COUNT] + s = cma[CM_ATTR_QUALITY_SQL] + t = cma[CM_ATTR_QUALITY_TYPE] + assert c + assert c == 42 + assert s + assert s == "SELECT * from SOMEWHERE;" + assert t + assert t == "Completeness" + + +def _validate_dq_empty(dq_attrs: CustomMetadataDict): + attribute_names = dq_attrs.attribute_names + assert CM_ATTR_QUALITY_COUNT in attribute_names + assert CM_ATTR_QUALITY_SQL in attribute_names + assert CM_ATTR_QUALITY_TYPE in attribute_names + assert not dq_attrs[CM_ATTR_QUALITY_COUNT] + assert not dq_attrs[CM_ATTR_QUALITY_SQL] + assert not dq_attrs[CM_ATTR_QUALITY_TYPE] + + +def _validate_raci_structure( + attributes: Optional[List[AttributeDef]], total_expected: int +): + global _removal_epoch + assert attributes + assert len(attributes) == total_expected + one = attributes[0] + assert one.display_name == CM_ATTR_RACI_RESPONSIBLE + assert one.name != CM_ATTR_RACI_RESPONSIBLE + assert one.type_name == f"array<{AtlanCustomAttributePrimitiveType.STRING.value}>" + assert one.options + assert "Database" in one.applicable_asset_types + assert not one.is_archived() + assert one.cardinality == Cardinality.SET + assert one.options.multi_value_select + assert one.options.custom_type == AtlanCustomAttributePrimitiveType.USERS.value + one = attributes[1] + assert one.display_name == CM_ATTR_RACI_ACCOUNTABLE + assert one.name != CM_ATTR_RACI_ACCOUNTABLE + assert one.type_name == AtlanCustomAttributePrimitiveType.STRING.value + assert one.options + assert "Table" in one.applicable_asset_types + assert not one.is_archived() + assert not one.options.multi_value_select + assert one.options.custom_type == AtlanCustomAttributePrimitiveType.USERS.value + one = attributes[2] + assert one.display_name == CM_ATTR_RACI_CONSULTED + assert one.name != CM_ATTR_RACI_CONSULTED + assert one.type_name == f"array<{AtlanCustomAttributePrimitiveType.STRING.value}>" + assert one.options + assert "Column" in one.applicable_asset_types + assert not one.is_archived() + assert one.cardinality == Cardinality.SET + assert one.options.multi_value_select + assert one.options.custom_type == AtlanCustomAttributePrimitiveType.GROUPS.value + one = attributes[3] + assert one.display_name == CM_ATTR_RACI_INFORMED + assert not one.name == CM_ATTR_RACI_INFORMED + assert one.type_name == f"array<{AtlanCustomAttributePrimitiveType.STRING.value}>" + assert one.options + assert "MaterialisedView" in one.applicable_asset_types + assert not one.is_archived() + assert one.cardinality == Cardinality.SET + assert one.options.multi_value_select + assert one.options.custom_type == AtlanCustomAttributePrimitiveType.GROUPS.value + if total_expected > 5: + # If we're expecting more than 5, then the penultimate must be an archived CM_ATTR_EXTRA + one = attributes[4] + assert ( + one.display_name == f"{CM_ATTR_RACI_EXTRA}-archived-{str(_removal_epoch)}" + ) + assert one.name != CM_ATTR_RACI_EXTRA + assert one.type_name == AtlanCustomAttributePrimitiveType.STRING.value + assert one.options + assert "AtlasGlossaryTerm" in one.applicable_glossary_types + assert not one.options.multi_value_select + assert one.is_archived() + if total_expected > 4: + return attributes[total_expected - 1] + return None + + +def test_add_badge_cm_dq( + client: AtlanClient, + cm_dq: CustomMetadataDef, +): + badge = Badge.creator( + client=client, + name=CM_ATTR_QUALITY_COUNT, + cm_name=CM_QUALITY, + cm_attribute=CM_ATTR_QUALITY_COUNT, + badge_conditions=[ + BadgeCondition.creator( + badge_condition_operator=BadgeComparisonOperator.GTE, + badge_condition_value="5", + badge_condition_colorhex=BadgeConditionColor.GREEN, + ), + BadgeCondition.creator( + badge_condition_operator=BadgeComparisonOperator.LT, + badge_condition_value="5", + badge_condition_colorhex=BadgeConditionColor.YELLOW, + ), + BadgeCondition.creator( + badge_condition_operator=BadgeComparisonOperator.LTE, + badge_condition_value="2", + badge_condition_colorhex=BadgeConditionColor.RED, + ), + ], + ) + badge.user_description = "How many data quality checks ran against this asset." + assert badge.status == EntityStatus.ACTIVE + response = client.asset.save(badge) + assert (badges := response.assets_created(asset_type=Badge)) + assert len(badges) == 1 + client.asset.purge_by_guid(badges[0].guid) + + +@pytest.mark.order() +def test_save_merging_cm( + term: AtlasGlossaryTerm, + glossary: AtlasGlossary, + cm_sql: CustomMetadataDef, + client: AtlanClient, +): + cm_sql_dict = CustomMetadataDict(client=client, name=CM_SQL) + cm_sql_dict[f"{MODULE_NAME}_{CM_ATTR_QUALITY_SQL}"] = "SELECT * FROM batman;" + assert term.qualified_name + assert term.name + + to_update = AtlasGlossaryTerm.create_for_modification( + qualified_name=term.qualified_name, name=term.name, glossary_guid=glossary.guid + ) + to_update.set_custom_metadata(custom_metadata=cm_sql_dict, client=client) + response = client.asset.save_merging_cm(to_update, replace_atlan_tags=False) + assert response + assert len(response.assets_deleted(asset_type=AtlasGlossaryTerm)) == 0 + assert len(response.assets_created(asset_type=AtlasGlossaryTerm)) == 0 + assert len(response.assets_updated(asset_type=AtlasGlossaryTerm)) == 1 + + t = response.assets_updated(asset_type=AtlasGlossaryTerm)[0] + assert isinstance(t, AtlasGlossaryTerm) + assert t.guid == term.guid + assert t.qualified_name == term.qualified_name + assert term.qualified_name + + x = client.asset.get_by_qualified_name( + qualified_name=term.qualified_name, + asset_type=AtlasGlossaryTerm, + ignore_relationships=False, + ) + assert x + assert not x.is_incomplete + assert x.qualified_name == term.qualified_name + retrieved_cm_sql = x.get_custom_metadata(client=client, name=CM_SQL) + assert retrieved_cm_sql + sql_attr_value = retrieved_cm_sql[f"{MODULE_NAME}_{CM_ATTR_QUALITY_SQL}"] + assert sql_attr_value == "SELECT * FROM batman;" diff --git a/tests_v9/integration/custom_package_test.py b/tests_v9/integration/custom_package_test.py new file mode 100644 index 000000000..e3a7c5d3d --- /dev/null +++ b/tests_v9/integration/custom_package_test.py @@ -0,0 +1,122 @@ +import importlib.util +import os +from pathlib import Path +from unittest.mock import MagicMock, patch + +import pytest + +from pyatlan.pkg.models import CustomPackage, generate +from pyatlan.pkg.ui import UIConfig, UIStep +from pyatlan.pkg.widgets import TextInput +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.client.impersonate import V9ImpersonationClient +from pyatlan_v9.pkg.utils import get_client, set_package_headers + + +@pytest.fixture +def mock_pkg_env(): + with patch.dict( + os.environ, + { + "X_ATLAN_AGENT": "agent_value", + "X_ATLAN_AGENT_ID": "agent_id_value", + "X_ATLAN_AGENT_PACKAGE_NAME": "package_name_value", + "X_ATLAN_AGENT_WORKFLOW_ID": "workflow_id_value", + "CLIENT_ID": "client_id_value", + "CLIENT_SECRET": "client_secret_value", + }, + clear=True, + ): + yield + + +@pytest.fixture +def custom_package(): + return CustomPackage( + package_id="@csa/owner-propagator", + package_name="Owner Propagator", + description="Propagate owners from schema downwards.", + icon_url="https://assets.atlan.com/assets/ph-user-switch-light.svg", + docs_url="https://solutions.atlan.com/", + ui_config=UIConfig( + steps=[ + UIStep( + title="Configuration", + description="Owner propagation configuration", + inputs={ + "qn_prefix": TextInput( + label="Qualified name prefix", + help="Provide the starting name for schemas from which to propagate ownership", + required=False, + placeholder="default/snowflake/1234567890", + grid=4, + ) + }, + ) + ] + ), + container_image="ghcr.io/atlanhq/csa-owner-propagator:123", + container_command=["/dumb-init", "--", "java", "OwnerPropagator"], + ) + + +def test_generate_package(custom_package: CustomPackage, tmpdir): + dir = Path(tmpdir.mkdir("generated_packages")) + + generate(pkg=custom_package, path=dir, operation="package") + + package_dir = dir / "csa-owner-propagator" + assert package_dir.exists() + assert (package_dir / "index.js").exists() + assert (package_dir / "package.json").exists() + configmaps_dir = package_dir / "configmaps" + assert configmaps_dir.exists() + assert (configmaps_dir / "default.yaml").exists() + templates_dir = package_dir / "templates" + assert templates_dir.exists() + assert (templates_dir / "default.yaml").exists() + + +def test_generate_config(custom_package: CustomPackage, tmpdir): + dir = Path(tmpdir) + + generate(pkg=custom_package, path=dir, operation="config") + + assert dir / "logging.conf" + config_name = "owner_propagator_cfg.py" + assert dir / config_name + + spec = importlib.util.spec_from_file_location( + "owner_propagator_cfg", dir / config_name + ) + assert spec is not None + module = importlib.util.module_from_spec(spec) + assert module is not None + assert spec.loader is not None + spec.loader.exec_module(module) + + +def test_set_package_headers(client: AtlanClient, mock_pkg_env): + mock_client = MagicMock(spec=client) + updated_client = set_package_headers(mock_client) + expected_headers = { + "x-atlan-agent": "agent_value", + "x-atlan-agent-id": "agent_id_value", + "x-atlan-agent-package-name": "package_name_value", + "x-atlan-agent-workflow-id": "workflow_id_value", + } + mock_client.update_headers.assert_called_once_with(expected_headers) + assert updated_client == mock_client + + +@patch.object(V9ImpersonationClient, "user", return_value="some-api-key") +def test_get_client_user_id_handling( + mock_impersonate_client, + mock_pkg_env, + client: AtlanClient, +): + updated_client = get_client(impersonate_user_id="test-user-id") + assert updated_client + assert updated_client.base_url + assert updated_client.api_key == "some-api-key" + assert updated_client._user_id == "test-user-id" diff --git a/tests_v9/integration/data/file_requests/sdk.png b/tests_v9/integration/data/file_requests/sdk.png new file mode 100644 index 000000000..35217dcba Binary files /dev/null and b/tests_v9/integration/data/file_requests/sdk.png differ diff --git a/tests_v9/integration/data_mesh_test.py b/tests_v9/integration/data_mesh_test.py new file mode 100644 index 000000000..8f0035881 --- /dev/null +++ b/tests_v9/integration/data_mesh_test.py @@ -0,0 +1,577 @@ +import re +from json import dumps +from typing import Generator + +import pytest +from msgspec import UNSET + +from pyatlan_v9.client.asset import IndexSearchResults +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.model.assets import ( + Asset, + AtlasGlossary, + Connection, + DataContract, + DataDomain, + DataProduct, + Table, +) +from pyatlan_v9.model.core import Announcement +from pyatlan_v9.model.data_mesh import DataProductsAssetsDSL +from pyatlan_v9.model.enums import ( + AnnouncementType, + AtlanCustomAttributePrimitiveType, + AtlanTypeCategory, + CertificateStatus, + DataProductStatus, + EntityStatus, +) +from pyatlan_v9.model.fluent_search import FluentSearch +from pyatlan_v9.model.typedef import AttributeDef, CustomMetadataDef +from tests_v9.integration.client import TestId, delete_asset +from tests_v9.integration.custom_metadata_test import create_custom_metadata +from tests_v9.integration.utils import wait_for_successful_custometadatadef_purge + +DATA_PRODUCT_ASSETS_PLAYBOOK_FILTER = ( + '{"condition":"AND","isGroupLocked":false,"rules":[]}' +) + +MODULE_NAME = TestId.make_unique("DM") + +DATA_DOMAIN_NAME = f"{MODULE_NAME}-data-domain" +DATA_DOMAIN_QUALIFIED_NAME = f"default/domain/{DATA_DOMAIN_NAME}/super" +DATA_DOMAIN_QN_REGEX = r"default/domain/[a-zA-Z0-9-]+/super" +DATA_SUB_DOMAIN_NAME = f"{MODULE_NAME}-data-sub-domain" +DATA_SUB_DOMAIN_QUALIFIED_NAME = ( + f"{DATA_DOMAIN_QUALIFIED_NAME}/domain/{DATA_SUB_DOMAIN_NAME}" +) +DATA_SUB_DOMAIN_QN_REGEX = r"default/domain/[a-zA-Z0-9-]+/super/domain/[a-zA-Z0-9-]+" +DATA_PRODUCT_NAME = f"{MODULE_NAME}-data-product" +DATA_PRODUCT_QUALIFIED_NAME = ( + f"{DATA_DOMAIN_QUALIFIED_NAME}/product/{DATA_PRODUCT_NAME}" +) +DATA_PRODUCT_QN_REGEX = r"default/domain/[a-zA-Z0-9-]+/super/product/[a-zA-Z0-9-]+" +DD_CM = f"{MODULE_NAME}_CM" +DD_ATTR = f"{MODULE_NAME}_ATTRIBUTE" +DATA_CONTRACT_NAME = f"{MODULE_NAME}-data-contract" +CERTIFICATE_STATUS = CertificateStatus.VERIFIED +CERTIFICATE_MESSAGE = "Automated testing of the Python SDK." +ANNOUNCEMENT_TYPE = AnnouncementType.INFORMATION +ANNOUNCEMENT_TITLE = "Python SDK testing." +ANNOUNCEMENT_MESSAGE = "Automated testing of the Python SDK." + + +@pytest.fixture(scope="module") +def domain(client: AtlanClient) -> Generator[DataDomain, None, None]: + to_create = DataDomain.creator(name=DATA_DOMAIN_NAME) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=DataDomain)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=DataDomain) + + +def test_data_domain(client: AtlanClient, domain: DataDomain): + assert domain + assert domain.guid + assert domain.qualified_name + assert domain.name == DATA_DOMAIN_NAME + assert re.search(DATA_DOMAIN_QN_REGEX, domain.qualified_name) + assert not domain.parent_domain_qualified_name + assert not domain.super_domain_qualified_name + + +@pytest.fixture(scope="module") +def sub_domain( + client: AtlanClient, + domain: DataDomain, +) -> Generator[DataDomain, None, None]: + assert domain.guid + to_create = DataDomain.creator( + name=DATA_SUB_DOMAIN_NAME, + parent_domain_qualified_name=domain.qualified_name, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=DataDomain)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=DataDomain) + + +def test_data_sub_domain(client: AtlanClient, sub_domain: DataDomain): + assert sub_domain + assert sub_domain.guid + assert sub_domain.qualified_name + assert sub_domain.parent_domain_qualified_name + assert sub_domain.super_domain_qualified_name + assert sub_domain.name == DATA_SUB_DOMAIN_NAME + assert re.search(DATA_SUB_DOMAIN_QN_REGEX, sub_domain.qualified_name) + assert re.search(DATA_DOMAIN_QN_REGEX, sub_domain.parent_domain_qualified_name) + assert re.search(DATA_DOMAIN_QN_REGEX, sub_domain.super_domain_qualified_name) + + +def test_update_domain(client: AtlanClient, domain: DataDomain): + assert domain.qualified_name + assert domain.name + updated = client.asset.update_certificate( + asset_type=DataDomain, + qualified_name=domain.qualified_name, + name=domain.name, + certificate_status=CERTIFICATE_STATUS, + message=CERTIFICATE_MESSAGE, + ) + assert updated + assert updated.certificate_status_message == CERTIFICATE_MESSAGE + assert domain.qualified_name + assert domain.name + updated = client.asset.update_announcement( + asset_type=DataDomain, + qualified_name=domain.qualified_name, + name=domain.name, + announcement=Announcement( + announcement_type=ANNOUNCEMENT_TYPE, + announcement_title=ANNOUNCEMENT_TITLE, + announcement_message=ANNOUNCEMENT_MESSAGE, + ), + ) + assert updated + if updated.announcement_type is not UNSET: + assert updated.announcement_type == ANNOUNCEMENT_TYPE.value + assert updated.announcement_title == ANNOUNCEMENT_TITLE + assert updated.announcement_message == ANNOUNCEMENT_MESSAGE + + +@pytest.mark.order(after="test_update_domain") +def test_retrieve_domain(client: AtlanClient, domain: DataDomain): + test_domain = client.asset.get_by_guid( + domain.guid, asset_type=DataDomain, ignore_relationships=False + ) + assert test_domain + assert test_domain.guid == domain.guid + assert test_domain.qualified_name == domain.qualified_name + assert test_domain.name == domain.name + assert test_domain.certificate_status == CERTIFICATE_STATUS + assert test_domain.certificate_status_message == CERTIFICATE_MESSAGE + + +@pytest.mark.order(after="test_retrieve_domain") +def test_find_domain_by_name(client: AtlanClient, domain: DataDomain): + response = client.asset.find_domain_by_name( + name=domain.name, attributes=["certificateStatus"] + ) + + assert response + assert response.guid == domain.guid + assert response.certificate_status == CertificateStatus.VERIFIED + + +def test_update_sub_domain(client: AtlanClient, sub_domain: DataDomain): + assert sub_domain.qualified_name + assert sub_domain.name + updated = client.asset.update_certificate( + asset_type=DataDomain, + qualified_name=sub_domain.qualified_name, + name=sub_domain.name, + certificate_status=CERTIFICATE_STATUS, + message=CERTIFICATE_MESSAGE, + ) + assert updated + assert updated.certificate_status_message == CERTIFICATE_MESSAGE + assert sub_domain.qualified_name + assert sub_domain.name + updated = client.asset.update_announcement( + asset_type=DataDomain, + qualified_name=sub_domain.qualified_name, + name=sub_domain.name, + announcement=Announcement( + announcement_type=ANNOUNCEMENT_TYPE, + announcement_title=ANNOUNCEMENT_TITLE, + announcement_message=ANNOUNCEMENT_MESSAGE, + ), + ) + assert updated + if updated.announcement_type is not UNSET: + assert updated.announcement_type == ANNOUNCEMENT_TYPE.value + assert updated.announcement_title == ANNOUNCEMENT_TITLE + assert updated.announcement_message == ANNOUNCEMENT_MESSAGE + + +@pytest.mark.order(after="test_update_sub_domain") +def test_retrieve_sub_domain(client: AtlanClient, sub_domain: DataDomain): + test_sub_domain = client.asset.get_by_guid( + sub_domain.guid, asset_type=DataDomain, ignore_relationships=False + ) + assert test_sub_domain + assert test_sub_domain.guid == sub_domain.guid + assert test_sub_domain.qualified_name == sub_domain.qualified_name + assert test_sub_domain.name == sub_domain.name + assert test_sub_domain.certificate_status == CERTIFICATE_STATUS + assert test_sub_domain.certificate_status_message == CERTIFICATE_MESSAGE + + +@pytest.mark.order(after="test_retrieve_sub_domain") +def test_find_sub_domain_by_name(client: AtlanClient, sub_domain: DataDomain): + response = client.asset.find_domain_by_name( + name=sub_domain.name, attributes=["certificateStatus"] + ) + + assert response + assert response.guid == sub_domain.guid + assert response.certificate_status == CertificateStatus.VERIFIED + + +@pytest.fixture(scope="module") +def data_domain_cm( + client: AtlanClient, domain: DataDomain +) -> Generator[CustomMetadataDef, None, None]: + assert domain.qualified_name + attribute_defs = [ + AttributeDef.creator( + client=client, + display_name=DD_ATTR, + attribute_type=AtlanCustomAttributePrimitiveType.STRING, + applicable_domain_types={"DataDomain", "DataProduct"}, + applicable_domains={domain.qualified_name}, + ) + ] + dd_cm = create_custom_metadata( + client, name=DD_CM, attribute_defs=attribute_defs, logo="📦", locked=True + ) + yield dd_cm + wait_for_successful_custometadatadef_purge(DD_CM, client=client) + + +def test_data_domain_cm(data_domain_cm: CustomMetadataDef): + assert data_domain_cm.guid + assert data_domain_cm.name != DD_CM + assert data_domain_cm.display_name == DD_CM + assert data_domain_cm.category == AtlanTypeCategory.CUSTOM_METADATA + + attributes = data_domain_cm.attribute_defs + attribute = attributes[0] + assert attribute.name != DD_ATTR + assert attribute.display_name == DD_ATTR + assert attribute.options + assert not attribute.options.multi_value_select + assert attribute.type_name == AtlanCustomAttributePrimitiveType.STRING.value + + +@pytest.fixture(scope="module") +def product( + client: AtlanClient, + domain: DataDomain, + table: Table, +) -> Generator[DataProduct, None, None]: + assert domain.guid + assert table and table.guid + assets = FluentSearch().where(Asset.GUID.eq(table.guid)).to_request() + product = DataProduct.creator( + name=DATA_PRODUCT_NAME, + asset_selection=assets, + domain_qualified_name=domain.qualified_name, + ) + product.output_ports = [table] + response = client.asset.save(product) + result = response.assets_created(asset_type=DataProduct)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=DataProduct) + + +def test_product(client: AtlanClient, product: DataProduct): + assert product + assert product.guid + assert product.qualified_name + assert product.parent_domain_qualified_name + assert product.super_domain_qualified_name + assert product.name == DATA_PRODUCT_NAME + assert ( + product.data_product_assets_playbook_filter + == DATA_PRODUCT_ASSETS_PLAYBOOK_FILTER + ) + assert re.search(DATA_PRODUCT_QN_REGEX, product.qualified_name) + assert re.search(DATA_DOMAIN_QN_REGEX, product.parent_domain_qualified_name) + assert re.search(DATA_DOMAIN_QN_REGEX, product.super_domain_qualified_name) + + +@pytest.fixture(scope="module") +def contract( + client: AtlanClient, + table: Table, + connection: Connection, +) -> Generator[DataContract, None, None]: + assert table and table.guid and table.qualified_name + contract_json = { + "type": table.type_name, + "status": CertificateStatus.DRAFT, + "kind": "DataContract", + "data_source": connection.name, + "dataset": table.name, + "description": "Automated testing of the Python SDK.", + } + contract = DataContract.creator( + asset_qualified_name=table.qualified_name, + contract_json=dumps(contract_json), + ) + response = client.asset.save(contract) + result = response.assets_created(asset_type=DataContract)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=DataContract) + + +@pytest.fixture(scope="module") +def updated_contract( + client: AtlanClient, + table: Table, + connection: Connection, +) -> Generator[DataContract, None, None]: + assert table and table.guid and table.qualified_name + contract_json = { + "type": table.type_name, + "status": CertificateStatus.DRAFT, + "kind": "DataContract", + "data_source": connection.name, + "dataset": table.name, + "description": "Automated testing of the Python SDK (UPDATED).", + } + contract = DataContract.creator( + asset_qualified_name=table.qualified_name, + contract_json=dumps(contract_json), + ) + response = client.asset.save(contract) + result = response.assets_updated(asset_type=DataContract)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=DataContract) + + +def test_contract( + client: AtlanClient, table: Table, product: DataProduct, contract: DataContract +): + assert product and product.guid + product = client.asset.get_by_guid( + guid=product.guid, asset_type=DataProduct, ignore_relationships=False + ) + assert product and product.output_ports and len(product.output_ports) + table_asset = product.output_ports[0] + assert table and table.guid + assert table.guid == table_asset.guid + table = client.asset.get_by_guid( + guid=table_asset.guid, asset_type=Table, ignore_relationships=False + ) + assert table.has_contract + assert table.data_contract_latest + table_data_contract = table.data_contract_latest + dc_guid = ( + table_data_contract.guid + if hasattr(table_data_contract, "guid") + else table_data_contract.get("guid") + ) + assert contract and table_data_contract + assert table.name and contract.name and table.name in contract.name + assert contract.guid == dc_guid + assert contract.data_contract_json + assert contract.data_contract_version == 1 + assert contract.data_contract_asset_guid == table.guid + + +def test_update_contract( + client: AtlanClient, table: Table, updated_contract: DataContract +): + assert table and table.guid + table = client.asset.get_by_guid( + guid=table.guid, asset_type=Table, ignore_relationships=False + ) + assert table.has_contract + assert table.data_contract_latest + table_data_contract = table.data_contract_latest + dc_guid = ( + table_data_contract.guid + if hasattr(table_data_contract, "guid") + else table_data_contract.get("guid") + ) + assert table.name and updated_contract and table_data_contract + assert updated_contract.name and table.name in updated_contract.name + assert updated_contract.guid == dc_guid + assert updated_contract.data_contract_asset_guid == table.guid + assert updated_contract.data_contract_json + assert updated_contract.data_contract_version == 1 + assert "(UPDATED)" in updated_contract.data_contract_json + + +def test_update_product( + client: AtlanClient, product: DataProduct, glossary: AtlasGlossary +): + assert product.qualified_name + assert product.name + updated = client.asset.update_certificate( + asset_type=DataProduct, + qualified_name=product.qualified_name, + name=product.name, + certificate_status=CERTIFICATE_STATUS, + message=CERTIFICATE_MESSAGE, + ) + assert updated + assert updated.certificate_status_message == CERTIFICATE_MESSAGE + assert updated.certificate_status == CERTIFICATE_STATUS + assert product.qualified_name + assert product.name + updated = client.asset.update_announcement( + asset_type=DataProduct, + qualified_name=product.qualified_name, + name=product.name, + announcement=Announcement( + announcement_type=ANNOUNCEMENT_TYPE, + announcement_title=ANNOUNCEMENT_TITLE, + announcement_message=ANNOUNCEMENT_MESSAGE, + ), + ) + assert updated + assert updated.announcement_type == ANNOUNCEMENT_TYPE.value + assert updated.announcement_title == ANNOUNCEMENT_TITLE + assert updated.announcement_message == ANNOUNCEMENT_MESSAGE + assert product.qualified_name + assert product.name + + # Test the product.updater() method with assets + assert glossary.qualified_name + assets = ( + FluentSearch() + .where(Asset.QUALIFIED_NAME.eq(glossary.qualified_name)) + .to_request() + ) + to_update = DataProduct.updater( + name=DATA_PRODUCT_NAME, + qualified_name=product.qualified_name, + asset_selection=assets, + ) + response = client.asset.save(to_update) + assert (products := response.assets_updated(asset_type=DataProduct)) + assert len(products) == 1 + assert products[ + 0 + ].data_product_assets_d_s_l == DataProductsAssetsDSL.get_asset_selection(assets) + + # Test the product.updater() method without assets + # (ensure asset selection remains unchanged) + product = DataProduct.updater( + name=DATA_PRODUCT_NAME, qualified_name=product.qualified_name + ) + response = client.asset.save(product) + assert response.assets_updated(asset_type=DataProduct) == [] + + +@pytest.mark.order(after="test_update_product") +def test_retrieve_product(client: AtlanClient, product: DataProduct): + test_product = client.asset.get_by_guid( + product.guid, asset_type=DataProduct, ignore_relationships=False + ) + assert test_product + assert test_product.guid == product.guid + assert test_product.qualified_name == product.qualified_name + assert test_product.name == product.name + assert test_product.certificate_status == CERTIFICATE_STATUS + assert test_product.certificate_status_message == CERTIFICATE_MESSAGE + + +@pytest.mark.order(after="test_update_contract") +def test_retrieve_contract( + client: AtlanClient, table: Table, updated_contract: DataContract +): + test_contract = client.asset.get_by_guid( + updated_contract.guid, asset_type=DataContract, ignore_relationships=False + ) + assert test_contract + assert test_contract.name == updated_contract.name + assert table.name and updated_contract.name and table.name in updated_contract.name + assert test_contract.guid == updated_contract.guid + assert test_contract.qualified_name == updated_contract.qualified_name + assert test_contract.data_contract_asset_guid == table.guid + assert test_contract.data_contract_json + assert test_contract.data_contract_version == 1 + assert "(UPDATED)" in test_contract.data_contract_json + + +@pytest.mark.order(after="test_retrieve_product") +def test_find_product_by_name(client: AtlanClient, product: DataProduct): + response = client.asset.find_product_by_name( + name=product.name, attributes=["daapStatus"] + ) + + assert response + assert response.guid == product.guid + assert response.daap_status == DataProductStatus.ACTIVE + + +@pytest.mark.order(after="test_retrieve_product") +def test_product_get_assets(client: AtlanClient, product: DataProduct): + test_product = client.asset.get_by_guid( + product.guid, asset_type=DataProduct, ignore_relationships=False + ) + assert test_product + assert test_product.data_product_assets_d_s_l + asset_list = test_product.get_assets(client=client) + assert asset_list.count and asset_list.count > 0 + TOTAL_ASSETS = asset_list.count + counter = 0 + for assets in asset_list: + assert assets + counter += 1 + assert TOTAL_ASSETS == counter + assert isinstance(asset_list, IndexSearchResults) + + +@pytest.mark.order(after="test_retrieve_contract") +def test_delete_contract(client: AtlanClient, contract: DataContract): + response = client.asset.purge_by_guid(contract.guid) + assert response + assert not response.assets_created(asset_type=DataContract) + assert not response.assets_updated(asset_type=DataContract) + deleted = response.assets_deleted(asset_type=DataContract) + assert deleted + assert len(deleted) == 1 + assert deleted[0].guid == contract.guid + assert deleted[0].qualified_name == contract.qualified_name + assert deleted[0].delete_handler == "PURGE" + assert deleted[0].status == EntityStatus.DELETED + + +@pytest.mark.order(after="test_retrieve_product") +def test_delete_product(client: AtlanClient, product: DataProduct): + response = client.asset.purge_by_guid(product.guid) + assert response + assert not response.assets_created(asset_type=DataProduct) + assert not response.assets_updated(asset_type=DataProduct) + deleted = response.assets_deleted(asset_type=DataProduct) + assert deleted + assert len(deleted) == 1 + assert deleted[0].guid == product.guid + assert deleted[0].qualified_name == product.qualified_name + assert deleted[0].delete_handler == "PURGE" + assert deleted[0].status == EntityStatus.DELETED + + +@pytest.mark.order(after="test_delete_product") +def test_delete_sub_domain(client: AtlanClient, sub_domain: DataDomain): + response = client.asset.purge_by_guid(sub_domain.guid) + assert response + assert not response.assets_created(asset_type=DataDomain) + assert not response.assets_updated(asset_type=DataDomain) + deleted = response.assets_deleted(asset_type=DataDomain) + assert deleted + assert len(deleted) == 1 + assert deleted[0].guid == sub_domain.guid + assert deleted[0].qualified_name == sub_domain.qualified_name + assert deleted[0].delete_handler == "PURGE" + assert deleted[0].status == EntityStatus.DELETED + + +@pytest.mark.order(after="test_delete_sub_domain") +def test_delete_domain(client: AtlanClient, domain: DataDomain): + response = client.asset.purge_by_guid(domain.guid) + assert response + assert not response.assets_created(asset_type=DataDomain) + assert not response.assets_updated(asset_type=DataDomain) + deleted = response.assets_deleted(asset_type=DataDomain) + assert deleted + assert len(deleted) == 1 + assert deleted[0].guid == domain.guid + assert deleted[0].qualified_name == domain.qualified_name + assert deleted[0].delete_handler == "PURGE" + assert deleted[0].status == EntityStatus.DELETED diff --git a/tests_v9/integration/data_studio_asset_test.py b/tests_v9/integration/data_studio_asset_test.py new file mode 100644 index 000000000..22221b044 --- /dev/null +++ b/tests_v9/integration/data_studio_asset_test.py @@ -0,0 +1,296 @@ +from typing import Generator + +import pytest +from msgspec import UNSET + +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.model.assets import Connection, DataStudioAsset +from pyatlan_v9.model.core import Announcement +from pyatlan_v9.model.enums import ( + AnnouncementType, + AtlanConnectorType, + CertificateStatus, + EntityStatus, + GoogleDatastudioAssetType, +) +from tests_v9.integration.client import TestId, delete_asset +from tests_v9.integration.connection_test import create_connection + +MODULE_NAME = TestId.make_unique("datastudio") + +CONNECTOR_TYPE = AtlanConnectorType.DATASTUDIO +REPORT_NAME = f"{MODULE_NAME}-report" +SOURCE_NAME = f"{MODULE_NAME}-source" +CERTIFICATE_STATUS = CertificateStatus.VERIFIED +CERTIFICATE_MESSAGE = "Automated testing of the Python SDK." +ANNOUNCEMENT_TYPE = AnnouncementType.INFORMATION +ANNOUNCEMENT_TITLE = "Python SDK testing." +ANNOUNCEMENT_MESSAGE = "Automated testing of the Python SDK." + + +def _assert_announcement_cleared(updated): + assert updated.announcement_type in (UNSET, None, "") + assert updated.announcement_title in (UNSET, None, "") + assert updated.announcement_message in (UNSET, None, "") + + +@pytest.fixture(scope="module") +def connection(client: AtlanClient) -> Generator[Connection, None, None]: + result = create_connection( + client=client, name=MODULE_NAME, connector_type=CONNECTOR_TYPE + ) + yield result + delete_asset(client, guid=result.guid, asset_type=Connection) + + +@pytest.fixture(scope="module") +def data_studio_asset_report( + client: AtlanClient, connection: Connection +) -> Generator[DataStudioAsset, None, None]: + assert connection.qualified_name + to_create = DataStudioAsset.creator( + name=REPORT_NAME, + connection_qualified_name=connection.qualified_name, + data_studio_asset_type=GoogleDatastudioAssetType.REPORT, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=DataStudioAsset)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=DataStudioAsset) + + +def test_data_studio_asset_report( + client: AtlanClient, + connection: Connection, + data_studio_asset_report: DataStudioAsset, +): + assert data_studio_asset_report + assert data_studio_asset_report.guid + assert data_studio_asset_report.qualified_name + assert ( + data_studio_asset_report.connection_qualified_name == connection.qualified_name + ) + assert data_studio_asset_report.name == REPORT_NAME + assert ( + data_studio_asset_report.connector_name == AtlanConnectorType.DATASTUDIO.value + ) + assert ( + data_studio_asset_report.data_studio_asset_type + == GoogleDatastudioAssetType.REPORT + ) + + +def test_update_data_studio_asset_report( + client: AtlanClient, + connection: Connection, + data_studio_asset_report: DataStudioAsset, +): + assert data_studio_asset_report.qualified_name + assert data_studio_asset_report.name + updated = client.asset.update_certificate( + asset_type=DataStudioAsset, + qualified_name=data_studio_asset_report.qualified_name, + name=SOURCE_NAME, + certificate_status=CERTIFICATE_STATUS, + message=CERTIFICATE_MESSAGE, + ) + assert updated + assert updated.certificate_status_message == CERTIFICATE_MESSAGE + assert data_studio_asset_report.qualified_name + assert data_studio_asset_report + updated = client.asset.update_announcement( + asset_type=DataStudioAsset, + qualified_name=data_studio_asset_report.qualified_name, + name=SOURCE_NAME, + announcement=Announcement( + announcement_type=ANNOUNCEMENT_TYPE, + announcement_title=ANNOUNCEMENT_TITLE, + announcement_message=ANNOUNCEMENT_MESSAGE, + ), + ) + assert updated + if updated.announcement_type is not UNSET: + assert updated.announcement_type == ANNOUNCEMENT_TYPE.value + assert updated.announcement_title == ANNOUNCEMENT_TITLE + assert updated.announcement_message == ANNOUNCEMENT_MESSAGE + + +@pytest.fixture(scope="module") +def data_studio_asset_data_source( + client: AtlanClient, connection: Connection +) -> Generator[DataStudioAsset, None, None]: + assert connection.qualified_name + to_create = DataStudioAsset.creator( + name=SOURCE_NAME, + connection_qualified_name=connection.qualified_name, + data_studio_asset_type=GoogleDatastudioAssetType.DATA_SOURCE, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=DataStudioAsset)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=DataStudioAsset) + + +def test_data_studio_asset_data_source( + client: AtlanClient, + connection: Connection, + data_studio_asset_data_source: DataStudioAsset, +): + assert data_studio_asset_data_source + assert data_studio_asset_data_source.guid + assert data_studio_asset_data_source.qualified_name + assert ( + data_studio_asset_data_source.connection_qualified_name + == connection.qualified_name + ) + assert data_studio_asset_data_source.name == SOURCE_NAME + assert ( + data_studio_asset_data_source.connector_name + == AtlanConnectorType.DATASTUDIO.value + ) + assert ( + data_studio_asset_data_source.data_studio_asset_type + == GoogleDatastudioAssetType.DATA_SOURCE + ) + + +def test_update_data_studio_asset_data_source( + client: AtlanClient, + connection: Connection, + data_studio_asset_data_source: DataStudioAsset, +): + assert data_studio_asset_data_source.connection_qualified_name + assert data_studio_asset_data_source.qualified_name + assert data_studio_asset_data_source.name + updated = client.asset.update_certificate( + asset_type=DataStudioAsset, + qualified_name=data_studio_asset_data_source.qualified_name, + name=SOURCE_NAME, + certificate_status=CERTIFICATE_STATUS, + message=CERTIFICATE_MESSAGE, + ) + assert updated + assert updated.certificate_status_message == CERTIFICATE_MESSAGE + assert data_studio_asset_data_source.qualified_name + assert data_studio_asset_data_source + updated = client.asset.update_announcement( + asset_type=DataStudioAsset, + qualified_name=data_studio_asset_data_source.qualified_name, + name=SOURCE_NAME, + announcement=Announcement( + announcement_type=ANNOUNCEMENT_TYPE, + announcement_title=ANNOUNCEMENT_TITLE, + announcement_message=ANNOUNCEMENT_MESSAGE, + ), + ) + assert updated + if updated.announcement_type is not UNSET: + assert updated.announcement_type == ANNOUNCEMENT_TYPE.value + assert updated.announcement_title == ANNOUNCEMENT_TITLE + assert updated.announcement_message == ANNOUNCEMENT_MESSAGE + + +@pytest.mark.order(after="test_update_data_studio_asset_data_source") +def test_retrieve_data_studio_asset_data_source( + client: AtlanClient, + connection: Connection, + data_studio_asset_data_source: DataStudioAsset, +): + b = client.asset.get_by_guid( + data_studio_asset_data_source.guid, + asset_type=DataStudioAsset, + ignore_relationships=False, + ) + assert b + assert not b.is_incomplete + assert b.guid == data_studio_asset_data_source.guid + assert b.qualified_name == data_studio_asset_data_source.qualified_name + assert b.name == SOURCE_NAME + assert b.connector_name == AtlanConnectorType.DATASTUDIO.value + assert b.certificate_status == CERTIFICATE_STATUS + assert b.certificate_status_message == CERTIFICATE_MESSAGE + + +@pytest.mark.order(after="test_retrieve_data_studio_asset_data_source") +def test_update_data_studio_asset_data_source_again( + client: AtlanClient, + connection: Connection, + data_studio_asset_data_source: DataStudioAsset, +): + assert data_studio_asset_data_source.qualified_name + assert data_studio_asset_data_source.name + updated = client.asset.remove_certificate( + asset_type=DataStudioAsset, + qualified_name=data_studio_asset_data_source.qualified_name, + name=SOURCE_NAME, + ) + assert updated + assert not updated.certificate_status + assert not updated.certificate_status_message + assert data_studio_asset_data_source.qualified_name + updated = client.asset.remove_announcement( + asset_type=DataStudioAsset, + qualified_name=data_studio_asset_data_source.qualified_name, + name=SOURCE_NAME, + ) + assert updated + _assert_announcement_cleared(updated) + + +@pytest.mark.order(after="test_update_data_studio_asset_data_source_again") +def test_delete_data_studio_asset_data_source( + client: AtlanClient, + connection: Connection, + data_studio_asset_data_source: DataStudioAsset, +): + response = client.asset.delete_by_guid(data_studio_asset_data_source.guid) + assert response + assert not response.assets_created(asset_type=DataStudioAsset) + assert not response.assets_updated(asset_type=DataStudioAsset) + deleted = response.assets_deleted(asset_type=DataStudioAsset) + assert deleted + assert len(deleted) == 1 + assert deleted[0].guid == data_studio_asset_data_source.guid + assert deleted[0].qualified_name == data_studio_asset_data_source.qualified_name + assert deleted[0].delete_handler == "SOFT" + assert deleted[0].status == EntityStatus.DELETED + + +@pytest.mark.order(after="test_delete_data_studio_asset_data_source") +def test_read_deleted_data_studio_asset_data_source( + client: AtlanClient, + connection: Connection, + data_studio_asset_data_source: DataStudioAsset, +): + deleted = client.asset.get_by_guid( + data_studio_asset_data_source.guid, + asset_type=DataStudioAsset, + ignore_relationships=False, + ) + assert deleted + assert deleted.guid == data_studio_asset_data_source.guid + assert deleted.qualified_name == data_studio_asset_data_source.qualified_name + assert deleted.status == EntityStatus.DELETED + + +@pytest.mark.order(after="test_read_deleted_data_studio_asset_data_source") +def test_restore_data_studio_asset_data_source( + client: AtlanClient, + connection: Connection, + data_studio_asset_data_source: DataStudioAsset, +): + assert data_studio_asset_data_source.qualified_name + assert client.asset.restore( + asset_type=DataStudioAsset, + qualified_name=data_studio_asset_data_source.qualified_name, + ) + assert data_studio_asset_data_source.qualified_name + restored = client.asset.get_by_qualified_name( + asset_type=DataStudioAsset, + qualified_name=data_studio_asset_data_source.qualified_name, + ignore_relationships=False, + ) + assert restored + assert restored.guid == data_studio_asset_data_source.guid + assert restored.qualified_name == data_studio_asset_data_source.qualified_name + assert restored.status == EntityStatus.ACTIVE diff --git a/tests_v9/integration/dataverse_asset_test.py b/tests_v9/integration/dataverse_asset_test.py new file mode 100644 index 000000000..a3891a9ab --- /dev/null +++ b/tests_v9/integration/dataverse_asset_test.py @@ -0,0 +1,250 @@ +from typing import Generator + +import pytest + +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.model.assets import Connection, DataverseAttribute, DataverseEntity +from pyatlan_v9.model.core import Announcement +from pyatlan_v9.model.enums import ( + AnnouncementType, + AtlanConnectorType, + CertificateStatus, + EntityStatus, +) +from tests_v9.integration.client import TestId, delete_asset +from tests_v9.integration.connection_test import create_connection + +MODULE_NAME = TestId.make_unique("DATAVERSE") + +CONNECTOR_TYPE = AtlanConnectorType.DATAVERSE +DATAVERSE_ENTITY_NAME = f"{MODULE_NAME}-dataverse-entity" +DATAVERSE_ATTRIBUTE_NAME = f"{MODULE_NAME}-dataverse-attribute" +DATAVERSE_ATTRIBUTE_NAME_OVERLOAD = f"{MODULE_NAME}-dataverse-attribute-overload" + +CERTIFICATE_STATUS = CertificateStatus.VERIFIED + +ANNOUNCEMENT_TITLE = "Python SDK testing." +ANNOUNCEMENT_TYPE = AnnouncementType.INFORMATION +CERTIFICATE_MESSAGE = "Automated testing of the Python SDK." +ANNOUNCEMENT_MESSAGE = "Automated testing of the Python SDK." + + +@pytest.fixture(scope="module") +def connection(client: AtlanClient) -> Generator[Connection, None, None]: + result = create_connection( + client=client, name=MODULE_NAME, connector_type=CONNECTOR_TYPE + ) + yield result + delete_asset(client, guid=result.guid, asset_type=Connection) + + +@pytest.fixture(scope="module") +def dataverse_entity( + client: AtlanClient, connection: Connection +) -> Generator[DataverseEntity, None, None]: + assert connection.qualified_name + to_create = DataverseEntity.creator( + name=DATAVERSE_ENTITY_NAME, connection_qualified_name=connection.qualified_name + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=DataverseEntity)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=DataverseEntity) + + +def test_dataverse_entity( + client: AtlanClient, connection: Connection, dataverse_entity: DataverseEntity +): + assert dataverse_entity + assert dataverse_entity.guid + assert dataverse_entity.qualified_name + assert dataverse_entity.name == DATAVERSE_ENTITY_NAME + assert dataverse_entity.connection_qualified_name == connection.qualified_name + assert dataverse_entity.connector_name == AtlanConnectorType.DATAVERSE.value + + +@pytest.fixture(scope="module") +def dataverse_attribute( + client: AtlanClient, dataverse_entity: DataverseEntity +) -> Generator[DataverseAttribute, None, None]: + assert dataverse_entity.qualified_name + to_create = DataverseAttribute.creator( + name=DATAVERSE_ATTRIBUTE_NAME, + dataverse_entity_qualified_name=dataverse_entity.qualified_name, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=DataverseAttribute)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=DataverseAttribute) + + +def test_dataverse_attribute( + client: AtlanClient, + dataverse_entity: DataverseEntity, + dataverse_attribute: DataverseAttribute, +): + assert dataverse_attribute + assert dataverse_attribute.guid + assert dataverse_attribute.qualified_name + assert dataverse_attribute.name == DATAVERSE_ATTRIBUTE_NAME + assert ( + dataverse_attribute.connection_qualified_name + == dataverse_entity.connection_qualified_name + ) + assert dataverse_attribute.connector_name == AtlanConnectorType.DATAVERSE.value + + +@pytest.fixture(scope="module") +def dataverse_attribute_overload( + client: AtlanClient, connection: Connection, dataverse_entity: DataverseEntity +) -> Generator[DataverseAttribute, None, None]: + assert connection.qualified_name + assert dataverse_entity.qualified_name + to_create = DataverseAttribute.creator( + name=DATAVERSE_ATTRIBUTE_NAME_OVERLOAD, + dataverse_entity_qualified_name=dataverse_entity.qualified_name, + connection_qualified_name=connection.qualified_name, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=DataverseAttribute)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=DataverseAttribute) + + +def test_overload_dataverse_attribute( + client: AtlanClient, + dataverse_entity: DataverseEntity, + dataverse_attribute_overload: DataverseAttribute, +): + assert dataverse_attribute_overload + assert dataverse_attribute_overload.guid + assert dataverse_attribute_overload.qualified_name + assert dataverse_attribute_overload.name == DATAVERSE_ATTRIBUTE_NAME_OVERLOAD + assert ( + dataverse_attribute_overload.connection_qualified_name + == dataverse_entity.connection_qualified_name + ) + assert ( + dataverse_attribute_overload.connector_name + == AtlanConnectorType.DATAVERSE.value + ) + + +def _update_cert_and_annoucement(client, asset, asset_type): + assert asset.name + assert asset.qualified_name + + updated = client.asset.update_certificate( + name=asset.name, + asset_type=asset_type, + qualified_name=asset.qualified_name, + message=CERTIFICATE_MESSAGE, + certificate_status=CERTIFICATE_STATUS, + ) + assert updated + assert updated.certificate_status == CERTIFICATE_STATUS + assert updated.certificate_status_message == CERTIFICATE_MESSAGE + + updated = client.asset.update_announcement( + name=asset.name, + asset_type=asset_type, + qualified_name=asset.qualified_name, + announcement=Announcement( + announcement_type=ANNOUNCEMENT_TYPE, + announcement_title=ANNOUNCEMENT_TITLE, + announcement_message=ANNOUNCEMENT_MESSAGE, + ), + ) + assert updated + assert updated.announcement_type == ANNOUNCEMENT_TYPE + assert updated.announcement_title == ANNOUNCEMENT_TITLE + assert updated.announcement_message == ANNOUNCEMENT_MESSAGE + + +def test_update_dataverse_assets( + client: AtlanClient, + dataverse_entity: DataverseEntity, + dataverse_attribute: DataverseAttribute, +): + _update_cert_and_annoucement(client, dataverse_entity, DataverseEntity) + _update_cert_and_annoucement(client, dataverse_attribute, DataverseAttribute) + + +def _retrieve_dataverse_assets(client, asset, asset_type): + retrieved = client.asset.get_by_guid( + asset.guid, asset_type=asset_type, ignore_relationships=False + ) + assert retrieved + assert not retrieved.is_incomplete + assert retrieved.guid == asset.guid + assert retrieved.qualified_name == asset.qualified_name + assert retrieved.name == asset.name + assert retrieved.connector_name == AtlanConnectorType.DATAVERSE + assert retrieved.certificate_status == CERTIFICATE_STATUS + assert retrieved.certificate_status_message == CERTIFICATE_MESSAGE + + +@pytest.mark.order(after="test_update_dataverse_assets") +def test_retrieve_dataverse_assets( + client: AtlanClient, + dataverse_entity: DataverseEntity, + dataverse_attribute: DataverseAttribute, +): + _retrieve_dataverse_assets(client, dataverse_entity, DataverseEntity) + _retrieve_dataverse_assets(client, dataverse_attribute, DataverseAttribute) + + +@pytest.mark.order(after="test_retrieve_dataverse_assets") +def test_delete_dataverse_attribute( + client: AtlanClient, + dataverse_attribute: DataverseAttribute, +): + response = client.asset.delete_by_guid(guid=dataverse_attribute.guid) + assert response + assert not response.assets_created(asset_type=DataverseAttribute) + assert not response.assets_updated(asset_type=DataverseAttribute) + deleted = response.assets_deleted(asset_type=DataverseAttribute) + + assert deleted + assert len(deleted) == 1 + assert deleted[0].guid == dataverse_attribute.guid + assert deleted[0].delete_handler == "SOFT" + assert deleted[0].status == EntityStatus.DELETED + assert deleted[0].qualified_name == dataverse_attribute.qualified_name + + +@pytest.mark.order(after="test_delete_dataverse_attribute") +def test_read_deleted_dataverse_attribute( + client: AtlanClient, + dataverse_attribute: DataverseAttribute, +): + deleted = client.asset.get_by_guid( + dataverse_attribute.guid, + asset_type=DataverseAttribute, + ignore_relationships=False, + ) + assert deleted + assert deleted.status == EntityStatus.DELETED + assert deleted.guid == dataverse_attribute.guid + assert deleted.qualified_name == dataverse_attribute.qualified_name + + +@pytest.mark.order(after="test_read_deleted_dataverse_attribute") +def test_restore_dataverse_attribute( + client: AtlanClient, + dataverse_attribute: DataverseAttribute, +): + assert dataverse_attribute.qualified_name + assert client.asset.restore( + asset_type=DataverseAttribute, qualified_name=dataverse_attribute.qualified_name + ) + assert dataverse_attribute.qualified_name + restored = client.asset.get_by_qualified_name( + asset_type=DataverseAttribute, + qualified_name=dataverse_attribute.qualified_name, + ignore_relationships=False, + ) + assert restored + assert restored.guid == dataverse_attribute.guid + assert restored.status == EntityStatus.ACTIVE + assert restored.qualified_name == dataverse_attribute.qualified_name diff --git a/tests_v9/integration/document_db_asset_test.py b/tests_v9/integration/document_db_asset_test.py new file mode 100644 index 000000000..b26d70324 --- /dev/null +++ b/tests_v9/integration/document_db_asset_test.py @@ -0,0 +1,176 @@ +import logging +from typing import Callable, Optional + +import pytest + +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.model.assets import ( + Asset, + Connection, + DocumentDBCollection, + DocumentDBDatabase, +) +from pyatlan_v9.model.enums import AtlanConnectorType +from pyatlan_v9.model.response import AssetMutationResponse +from tests_v9.integration.client import TestId +from tests_v9.integration.test_sql_assets import verify_asset_updated + +LOGGER = logging.getLogger(__name__) + + +class TestConnection: + connection: Optional[Connection] = None + + def test_creator( + self, + client: AtlanClient, + upsert: Callable[[Asset], AssetMutationResponse], + ): + role = client.role_cache.get_id_for_name("$admin") + assert role + connection_name = TestId.make_unique("DOC_Conn") + c = Connection.creator( + client=client, + name=connection_name, + connector_type=AtlanConnectorType.DOCUMENTDB, + admin_roles=[role], + ) + assert c.guid + response = upsert(c) + assert response.mutated_entities + assert response.mutated_entities.CREATE + assert len(response.mutated_entities.CREATE) == 1 + assert isinstance(response.mutated_entities.CREATE[0], Connection) + assert response.guid_assignments + assert c.guid in response.guid_assignments + c = response.mutated_entities.CREATE[0] + c = client.asset.get_by_guid(c.guid, Connection, ignore_relationships=False) + assert isinstance(c, Connection) + TestConnection.connection = c + + @pytest.mark.order(after="test_create") + def test_trim_to_required( + self, client: AtlanClient, upsert: Callable[[Asset], AssetMutationResponse] + ): + assert TestConnection.connection + connection = TestConnection.connection.trim_to_required() + response = upsert(connection) + assert not response.mutated_entities + + +@pytest.mark.order(after="TestConnection") +class TestDatabase: + database: Optional[DocumentDBDatabase] = None + + def test_creator( + self, + client: AtlanClient, + upsert: Callable[[Asset], AssetMutationResponse], + ): + assert TestConnection.connection + connection = TestConnection.connection + assert connection + assert connection.qualified_name + database_name = TestId.make_unique("DocDB") + database = DocumentDBDatabase.creator( + name=database_name, + connection_qualified_name=connection.qualified_name, + ) + assert database.guid + response = upsert(database) + assert response.mutated_entities + assert response.mutated_entities.CREATE + assert connection.qualified_name == database.connection_qualified_name + assert len(response.mutated_entities.CREATE) == 1 + assert isinstance(response.mutated_entities.CREATE[0], DocumentDBDatabase) + assert response.guid_assignments + assert database.guid in response.guid_assignments + database = response.mutated_entities.CREATE[0] + client.asset.get_by_guid( + database.guid, DocumentDBDatabase, ignore_relationships=False + ) + TestDatabase.database = database + + @pytest.mark.order(after="test_creator") + def test_updater(self, client, upsert: Callable[[Asset], AssetMutationResponse]): + assert TestDatabase.database + assert TestDatabase.database.qualified_name + assert TestDatabase.database.name + database = DocumentDBDatabase.updater( + qualified_name=TestDatabase.database.qualified_name, + name=TestDatabase.database.name, + ) + description = f"{TestDatabase.database.description} more stuff" + database.description = description + response = upsert(database) + verify_asset_updated(response, DocumentDBDatabase) + + @pytest.mark.order(after="test_creator") + def test_trim_to_required( + self, client, upsert: Callable[[Asset], AssetMutationResponse] + ): + assert TestDatabase.database + database = TestDatabase.database.trim_to_required() + response = upsert(database) + assert not response.mutated_entities + + +@pytest.mark.order(after="TestDatabase") +class TestCollection: + collection: Optional[DocumentDBCollection] = None + + def test_creator( + self, + client: AtlanClient, + upsert: Callable[[Asset], AssetMutationResponse], + ): + assert TestConnection.connection + connection = TestConnection.connection + assert connection + assert connection.qualified_name + assert TestDatabase.database + database = TestDatabase.database + assert database + assert database.qualified_name + collection_name = TestId.make_unique("DocDBColl") + collection = DocumentDBCollection.creator( + name=collection_name, + database_qualified_name=database.qualified_name, + connection_qualified_name=connection.qualified_name, + ) + assert collection.guid + response = upsert(collection) + assert response.mutated_entities + assert response.mutated_entities.CREATE + assert len(response.mutated_entities.CREATE) == 1 + assert isinstance(response.mutated_entities.CREATE[0], DocumentDBCollection) + assert response.guid_assignments + assert collection.guid in response.guid_assignments + collection = response.mutated_entities.CREATE[0] + client.asset.get_by_guid( + collection.guid, DocumentDBCollection, ignore_relationships=False + ) + TestCollection.collection = collection + + @pytest.mark.order(after="test_creator") + def test_updater(self, client, upsert: Callable[[Asset], AssetMutationResponse]): + assert TestCollection.collection + assert TestCollection.collection.qualified_name + assert TestCollection.collection.name + collection = DocumentDBCollection.updater( + qualified_name=TestCollection.collection.qualified_name, + name=TestCollection.collection.name, + ) + description = f"{TestCollection.collection.description} more stuff" + collection.description = description + response = upsert(collection) + verify_asset_updated(response, DocumentDBCollection) + + @pytest.mark.order(after="test_creator") + def test_trim_to_required( + self, client, upsert: Callable[[Asset], AssetMutationResponse] + ): + assert TestCollection.collection + collection = TestCollection.collection.trim_to_required() + response = upsert(collection) + assert not response.mutated_entities diff --git a/tests_v9/integration/file_test.py b/tests_v9/integration/file_test.py new file mode 100644 index 000000000..cb86f84cd --- /dev/null +++ b/tests_v9/integration/file_test.py @@ -0,0 +1,124 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2022 Atlan Pte. Ltd. +from typing import Generator + +import pytest + +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.model.assets import Connection, File +from pyatlan_v9.model.core import Announcement +from pyatlan_v9.model.enums import ( + AnnouncementType, + AtlanConnectorType, + CertificateStatus, + FileType, +) +from tests_v9.integration.client import TestId, delete_asset +from tests_v9.integration.connection_test import create_connection + +MODULE_NAME = TestId.make_unique("File") + +CONNECTOR_TYPE = AtlanConnectorType.FILE +FILE_NAME = f"{MODULE_NAME}-file.pdf" +CERTIFICATE_STATUS = CertificateStatus.VERIFIED +CERTIFICATE_MESSAGE = "Automated testing of the Python SDK." +ANNOUNCEMENT_TYPE = AnnouncementType.INFORMATION +ANNOUNCEMENT_TITLE = "Python SDK testing." +ANNOUNCEMENT_MESSAGE = "Automated testing of the Python SDK." + + +@pytest.fixture(scope="module") +def connection(client: AtlanClient) -> Generator[Connection, None, None]: + result = create_connection( + client=client, name=MODULE_NAME, connector_type=CONNECTOR_TYPE + ) + yield result + # TODO: proper connection delete workflow + delete_asset(client, guid=result.guid, asset_type=Connection) + + +@pytest.fixture(scope="module") +def file(client: AtlanClient, connection: Connection) -> Generator[File, None, None]: + assert connection.qualified_name + to_create = File.creator( + name=FILE_NAME, + connection_qualified_name=connection.qualified_name, + file_type=FileType.PDF, + ) + to_create.file_path = "https://www.example.com" + response = client.asset.save(to_create) + result = response.assets_created(asset_type=File)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=File) + + +def test_file( + client: AtlanClient, + connection: Connection, + file: File, +): + assert file + assert file.guid + assert file.qualified_name + assert file.name == FILE_NAME + assert file.connection_qualified_name == connection.qualified_name + assert file.file_type == FileType.PDF + assert file.file_path == "https://www.example.com" + assert connection.qualified_name + assert file.connector_name == AtlanConnectorType.get_connector_name( + connection.qualified_name + ) + + +@pytest.mark.order(after="test_file") +def test_update_file( + client: AtlanClient, + connection: Connection, + file: File, +): + assert file.qualified_name + assert file.name + updated = client.asset.update_certificate( + qualified_name=file.qualified_name, + name=file.name, + asset_type=File, + certificate_status=CERTIFICATE_STATUS, + message=CERTIFICATE_MESSAGE, + ) + assert updated + assert updated.certificate_status == CERTIFICATE_STATUS + assert updated.certificate_status_message == CERTIFICATE_MESSAGE + assert file.qualified_name + assert file.name + updated = client.asset.update_announcement( + qualified_name=file.qualified_name, + name=file.name, + asset_type=File, + announcement=Announcement( + announcement_type=ANNOUNCEMENT_TYPE, + announcement_title=ANNOUNCEMENT_TITLE, + announcement_message=ANNOUNCEMENT_MESSAGE, + ), + ) + assert updated + assert updated.announcement_type == ANNOUNCEMENT_TYPE.value + assert updated.announcement_title == ANNOUNCEMENT_TITLE + assert updated.announcement_message == ANNOUNCEMENT_MESSAGE + + +@pytest.mark.order(after="test_update_file") +def test_read_file( + client: AtlanClient, + connection: Connection, + file: File, +): + r = client.asset.get_by_guid(file.guid, asset_type=File, ignore_relationships=False) + assert r + assert r.guid == file.guid + assert r.qualified_name == file.qualified_name + assert r.name == FILE_NAME + assert r.certificate_status == CERTIFICATE_STATUS + assert r.certificate_status_message == CERTIFICATE_MESSAGE + assert r.announcement_type == ANNOUNCEMENT_TYPE.value + assert r.announcement_title == ANNOUNCEMENT_TITLE + assert r.announcement_message == ANNOUNCEMENT_MESSAGE diff --git a/tests_v9/integration/gcs_asset_test.py b/tests_v9/integration/gcs_asset_test.py new file mode 100644 index 000000000..f2a94830c --- /dev/null +++ b/tests_v9/integration/gcs_asset_test.py @@ -0,0 +1,312 @@ +from typing import Generator + +import pytest +from msgspec import UNSET + +from pyatlan.model.utils import construct_object_key +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.model.assets import Connection, GCSBucket, GCSObject +from pyatlan_v9.model.core import Announcement +from pyatlan_v9.model.enums import ( + AnnouncementType, + AtlanConnectorType, + CertificateStatus, + EntityStatus, +) +from tests_v9.integration.client import TestId, delete_asset +from tests_v9.integration.connection_test import create_connection + +MODULE_NAME = TestId.make_unique("GCS") + +CONNECTOR_TYPE = AtlanConnectorType.GCS +GCS_BUCKET_NAME = MODULE_NAME +GCS_OBJECT_NAME = f"{MODULE_NAME}.csv" +GCS_OBJECT_NAME_PREFIX = f"{MODULE_NAME}Prefix.csv" +GCS_OBJECT_NAME_OVERLOAD = f"{MODULE_NAME}_overload.csv" +GCS_OBJECT_PREFIX = "/some/folder/structure" +CERTIFICATE_STATUS = CertificateStatus.VERIFIED +CERTIFICATE_MESSAGE = "Automated testing of the Python SDK." +ANNOUNCEMENT_TYPE = AnnouncementType.INFORMATION +ANNOUNCEMENT_TITLE = "Python SDK testing." +ANNOUNCEMENT_MESSAGE = "Automated testing of the Python SDK." + + +def _assert_announcement_cleared(updated): + assert updated.announcement_type in (UNSET, None, "") + assert updated.announcement_title in (UNSET, None, "") + assert updated.announcement_message in (UNSET, None, "") + + +@pytest.fixture(scope="module") +def connection(client: AtlanClient) -> Generator[Connection, None, None]: + result = create_connection( + client=client, name=MODULE_NAME, connector_type=CONNECTOR_TYPE + ) + yield result + delete_asset(client, guid=result.guid, asset_type=Connection) + + +@pytest.fixture(scope="module") +def gcs_bucket( + client: AtlanClient, connection: Connection +) -> Generator[GCSBucket, None, None]: + assert connection.qualified_name + to_create = GCSBucket.creator( + name=GCS_BUCKET_NAME, connection_qualified_name=connection.qualified_name + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=GCSBucket)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=GCSBucket) + + +def test_gcs_bucket(client: AtlanClient, connection: Connection, gcs_bucket: GCSBucket): + assert gcs_bucket + assert gcs_bucket.guid + assert gcs_bucket.qualified_name + assert gcs_bucket.connection_qualified_name == connection.qualified_name + assert gcs_bucket.name == GCS_BUCKET_NAME + assert gcs_bucket.connector_name == AtlanConnectorType.GCS.value + + +@pytest.fixture(scope="module") +def gcs_object( + client: AtlanClient, connection: Connection, gcs_bucket: GCSBucket +) -> Generator[GCSObject, None, None]: + assert gcs_bucket.qualified_name + to_create = GCSObject.creator( + name=GCS_OBJECT_NAME, + gcs_bucket_name=gcs_bucket.name, + gcs_bucket_qualified_name=gcs_bucket.qualified_name, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=GCSObject)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=GCSObject) + + +def test_gcs_object( + client: AtlanClient, + connection: Connection, + gcs_bucket: GCSBucket, + gcs_object: GCSObject, +): + assert gcs_object + assert gcs_object.guid + assert gcs_object.qualified_name + assert gcs_object.gcs_bucket_qualified_name == gcs_bucket.qualified_name + assert gcs_object.name == GCS_OBJECT_NAME + assert gcs_object.connector_name == AtlanConnectorType.GCS.value + + +@pytest.fixture(scope="module") +def gcs_object_prefix( + client: AtlanClient, connection: Connection, gcs_bucket: GCSBucket +) -> Generator[GCSObject, None, None]: + assert gcs_bucket.qualified_name + to_create = GCSObject.creator_with_prefix( + name=GCS_OBJECT_NAME_PREFIX, + connection_qualified_name=connection.qualified_name, + gcs_bucket_name=gcs_bucket.name, + gcs_bucket_qualified_name=gcs_bucket.qualified_name, + prefix=GCS_OBJECT_PREFIX, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=GCSObject)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=GCSObject) + + +def test_gcs_object_with_prefix( + client: AtlanClient, + connection: Connection, + gcs_bucket: GCSBucket, + gcs_object_prefix: GCSObject, +): + assert gcs_object_prefix + assert gcs_object_prefix.guid + assert gcs_object_prefix.qualified_name + assert gcs_bucket.name + assert gcs_object_prefix.name == GCS_OBJECT_NAME_PREFIX + assert gcs_object_prefix.connector_name == AtlanConnectorType.GCS.value + assert gcs_object_prefix.gcs_bucket_name == gcs_bucket.name + assert gcs_object_prefix.gcs_bucket_qualified_name == gcs_bucket.qualified_name + assert gcs_object_prefix.gcs_object_key == construct_object_key( + GCS_OBJECT_PREFIX, gcs_object_prefix.name + ) + + +@pytest.fixture(scope="module") +def gcs_object_overload( + client: AtlanClient, connection: Connection, gcs_bucket: GCSBucket +) -> Generator[GCSObject, None, None]: + assert gcs_bucket.qualified_name + assert connection.qualified_name + assert gcs_bucket.name + to_create = GCSObject.creator( + name=GCS_OBJECT_NAME_OVERLOAD, + gcs_bucket_name=gcs_bucket.name, + gcs_bucket_qualified_name=gcs_bucket.qualified_name, + connection_qualified_name=connection.qualified_name, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=GCSObject)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=GCSObject) + + +def test_overload_gcs_object( + client: AtlanClient, + connection: Connection, + gcs_bucket: GCSBucket, + gcs_object_overload: GCSObject, +): + assert gcs_object_overload + assert gcs_object_overload.guid + assert gcs_object_overload.qualified_name + assert gcs_object_overload.gcs_bucket_qualified_name == gcs_bucket.qualified_name + assert gcs_object_overload.name == GCS_OBJECT_NAME_OVERLOAD + assert gcs_object_overload.connector_name == AtlanConnectorType.GCS.value + + +def test_update_gcs_object( + client: AtlanClient, + connection: Connection, + gcs_bucket: GCSBucket, + gcs_object: GCSObject, +): + assert gcs_object.qualified_name + assert gcs_object.name + updated = client.asset.update_certificate( + asset_type=GCSObject, + qualified_name=gcs_object.qualified_name, + name=GCS_OBJECT_NAME, + certificate_status=CERTIFICATE_STATUS, + message=CERTIFICATE_MESSAGE, + ) + assert updated + assert updated.certificate_status_message == CERTIFICATE_MESSAGE + assert gcs_object.qualified_name + assert gcs_object.name + updated = client.asset.update_announcement( + asset_type=GCSObject, + qualified_name=gcs_object.qualified_name, + name=GCS_OBJECT_NAME, + announcement=Announcement( + announcement_type=ANNOUNCEMENT_TYPE, + announcement_title=ANNOUNCEMENT_TITLE, + announcement_message=ANNOUNCEMENT_MESSAGE, + ), + ) + assert updated + if updated.announcement_type is not UNSET: + assert updated.announcement_type == ANNOUNCEMENT_TYPE.value + assert updated.announcement_title == ANNOUNCEMENT_TITLE + assert updated.announcement_message == ANNOUNCEMENT_MESSAGE + + +@pytest.mark.order(after="test_update_gcs_object") +def test_retrieve_gcs_object( + client: AtlanClient, + connection: Connection, + gcs_bucket: GCSBucket, + gcs_object: GCSObject, +): + b = client.asset.get_by_guid( + gcs_object.guid, asset_type=GCSObject, ignore_relationships=False + ) + assert b + assert not b.is_incomplete + assert b.guid == gcs_object.guid + assert b.qualified_name == gcs_object.qualified_name + assert b.name == GCS_OBJECT_NAME + assert b.connector_name == AtlanConnectorType.GCS.value + assert b.certificate_status == CERTIFICATE_STATUS + assert b.certificate_status_message == CERTIFICATE_MESSAGE + + +@pytest.mark.order(after="test_retrieve_gcs_object") +def test_update_gcs_object_again( + client: AtlanClient, + connection: Connection, + gcs_bucket: GCSBucket, + gcs_object: GCSObject, +): + assert gcs_object.qualified_name + assert gcs_object.name + updated = client.asset.remove_certificate( + asset_type=GCSObject, + qualified_name=gcs_object.qualified_name, + name=gcs_object.name, + ) + assert updated + assert updated + assert not updated.certificate_status + assert not updated.certificate_status_message + assert gcs_object.qualified_name + updated = client.asset.remove_announcement( + qualified_name=gcs_object.qualified_name, + asset_type=GCSObject, + name=gcs_object.name, + ) + assert updated + _assert_announcement_cleared(updated) + + +@pytest.mark.order(after="test_update_gcs_object_again") +def test_delete_gcs_object( + client: AtlanClient, + connection: Connection, + gcs_bucket: GCSBucket, + gcs_object: GCSObject, +): + response = client.asset.delete_by_guid(gcs_object.guid) + assert response + assert not response.assets_created(asset_type=GCSObject) + assert not response.assets_updated(asset_type=GCSObject) + deleted = response.assets_deleted(asset_type=GCSObject) + assert deleted + assert len(deleted) == 1 + assert deleted[0].guid == gcs_object.guid + assert deleted[0].qualified_name == gcs_object.qualified_name + assert deleted[0].delete_handler == "SOFT" + assert deleted[0].status == EntityStatus.DELETED + + +@pytest.mark.order(after="test_delete_gcs_object") +def test_read_deleted_gcs_object( + client: AtlanClient, + connection: Connection, + gcs_bucket: GCSBucket, + gcs_object: GCSObject, +): + deleted = client.asset.get_by_guid( + gcs_object.guid, asset_type=GCSObject, ignore_relationships=False + ) + assert deleted + assert deleted.guid == gcs_object.guid + assert deleted.qualified_name == gcs_object.qualified_name + assert deleted.status == EntityStatus.DELETED + + +@pytest.mark.order(after="test_read_deleted_gcs_object") +def test_restore_object( + client: AtlanClient, + connection: Connection, + gcs_bucket: GCSBucket, + gcs_object: GCSObject, +): + assert gcs_object.qualified_name + assert client.asset.restore( + asset_type=GCSObject, qualified_name=gcs_object.qualified_name + ) + assert gcs_object.qualified_name + restored = client.asset.get_by_qualified_name( + asset_type=GCSObject, + qualified_name=gcs_object.qualified_name, + ignore_relationships=False, + ) + assert restored + assert restored.guid == gcs_object.guid + assert restored.qualified_name == gcs_object.qualified_name + assert restored.status == EntityStatus.ACTIVE diff --git a/tests_v9/integration/glossary_test.py b/tests_v9/integration/glossary_test.py new file mode 100644 index 000000000..e592f4bdd --- /dev/null +++ b/tests_v9/integration/glossary_test.py @@ -0,0 +1,1177 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2022 Atlan Pte. Ltd. +import itertools +import logging +from time import sleep +from typing import Generator, List, Optional, Union + +import pytest +from tenacity import retry, retry_if_exception_type, stop_after_attempt, wait_fixed + +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.errors import InvalidRequestError, NotFoundError +from pyatlan_v9.model.assets import ( + AtlasGlossary, + AtlasGlossaryCategory, + AtlasGlossaryTerm, +) +from pyatlan_v9.model.assets.relations import UserDefRelationship +from pyatlan_v9.model.enums import SaveSemantic +from pyatlan_v9.model.fields.atlan_fields import AtlanField +from pyatlan_v9.model.fluent_search import CompoundQuery, FluentSearch +from pyatlan_v9.model.search import DSL, IndexSearchRequest +from tests_v9.integration.client import TestId, delete_asset +from tests_v9.integration.utils import ( + assert_fluent_search_count_with_retry, + assert_search_count_with_retry, +) + +LOGGER = logging.getLogger(__name__) + +MODULE_NAME = TestId.make_unique("GLS") + +TERM_NAME1 = f"{MODULE_NAME}1" +TERM_NAME2 = f"{MODULE_NAME}2" +TERM_NAME3 = f"{MODULE_NAME}3" +TERM_NAME4 = f"{MODULE_NAME}4" + + +def create_glossary(client: AtlanClient, name: str) -> AtlasGlossary: + g = AtlasGlossary.creator(name=name) + r = client.asset.save(g) + return r.assets_created(AtlasGlossary)[0] + + +def create_category( + client: AtlanClient, + name: str, + glossary: Optional[AtlasGlossary] = None, + glossary_guid: Optional[str] = None, + glossary_qualified_name: Optional[str] = None, + parent: Optional[AtlasGlossaryCategory] = None, +) -> AtlasGlossaryCategory: + if glossary: + c = AtlasGlossaryCategory.creator( + name=name, anchor=glossary, parent_category=parent or None + ) + elif glossary_guid: + c = AtlasGlossaryCategory.creator( + name=name, glossary_guid=glossary_guid, parent_category=parent or None + ) + elif glossary_qualified_name: + c = AtlasGlossaryCategory.creator( + name=name, + glossary_qualified_name=glossary_qualified_name, + parent_category=parent or None, + ) + return client.asset.save(c).assets_created(AtlasGlossaryCategory)[0] + + +def create_term( + client: AtlanClient, + name: str, + glossary: Optional[AtlasGlossary] = None, + glossary_guid: Optional[str] = None, + glossary_qualified_name: Optional[str] = None, + categories: Optional[List[AtlasGlossaryCategory]] = None, +) -> AtlasGlossaryTerm: + if glossary: + t = AtlasGlossaryTerm.creator(name=name, anchor=glossary, categories=categories) + elif glossary_guid: + t = AtlasGlossaryTerm.creator( + name=name, + glossary_guid=glossary_guid, + categories=categories, + ) + elif glossary_qualified_name: + t = AtlasGlossaryTerm.creator( + name=name, + glossary_qualified_name=glossary_qualified_name, + categories=categories, + ) + r = client.asset.save(t) + return r.assets_created(AtlasGlossaryTerm)[0] + + +@pytest.fixture(scope="module") +def glossary( + client: AtlanClient, +) -> Generator[AtlasGlossary, None, None]: + g = create_glossary(client, MODULE_NAME) + yield g + delete_asset(client, guid=g.guid, asset_type=AtlasGlossary) + + +def test_glossary( + glossary: AtlasGlossary, +): + assert glossary.guid + assert glossary.name == MODULE_NAME + assert glossary.qualified_name + assert glossary.qualified_name != MODULE_NAME + + +@pytest.fixture(scope="module") +def category( + client: AtlanClient, glossary: AtlasGlossary +) -> Generator[AtlasGlossaryCategory, None, None]: + c = create_category(client, MODULE_NAME, glossary) + yield c + delete_asset(client, guid=c.guid, asset_type=AtlasGlossaryCategory) + + +@pytest.fixture(scope="module") +def hierarchy_glossary( + client: AtlanClient, +) -> Generator[AtlasGlossary, None, None]: + g = create_glossary(client, TestId.make_unique("hierarchy")) + yield g + delete_asset(client, guid=g.guid, asset_type=AtlasGlossary) + + +@pytest.fixture(scope="module") +def top1_category( + client: AtlanClient, hierarchy_glossary +) -> Generator[AtlasGlossaryCategory, None, None]: + c = create_category(client, TestId.make_unique("top1"), hierarchy_glossary) + yield c + delete_asset(client, guid=c.guid, asset_type=AtlasGlossaryCategory) + + +@pytest.fixture(scope="module") +def mid1a_category( + client: AtlanClient, + hierarchy_glossary: AtlasGlossary, + top1_category: AtlasGlossaryCategory, +) -> Generator[AtlasGlossaryCategory, None, None]: + c = create_category( + client, TestId.make_unique("mid1a"), hierarchy_glossary, parent=top1_category + ) + yield c + delete_asset(client, guid=c.guid, asset_type=AtlasGlossaryCategory) + + +@pytest.fixture(scope="module") +def mid1a_term( + client: AtlanClient, + hierarchy_glossary: AtlasGlossary, + mid1a_category: AtlasGlossaryCategory, +) -> Generator[AtlasGlossaryTerm, None, None]: + assert mid1a_category.qualified_name + t = create_term( + client, + name=f"mid1a_{TERM_NAME1}", + glossary_guid=hierarchy_glossary.guid, + categories=[ + AtlasGlossaryCategory.ref_by_qualified_name(mid1a_category.qualified_name) + ], + ) + yield t + delete_asset(client, guid=t.guid, asset_type=AtlasGlossaryTerm) + + +@pytest.fixture(scope="module") +def leaf1aa_category( + client: AtlanClient, + hierarchy_glossary: AtlasGlossary, + mid1a_category: AtlasGlossaryCategory, +) -> Generator[AtlasGlossaryCategory, None, None]: + assert hierarchy_glossary and hierarchy_glossary.guid + c = create_category( + client, + TestId.make_unique("leaf1aa"), + glossary_guid=hierarchy_glossary.guid, + parent=mid1a_category, + ) + yield c + delete_asset(client, guid=c.guid, asset_type=AtlasGlossaryCategory) + + +@pytest.fixture(scope="module") +def leaf1ab_category( + client: AtlanClient, + hierarchy_glossary: AtlasGlossary, + mid1a_category: AtlasGlossaryCategory, +) -> Generator[AtlasGlossaryCategory, None, None]: + c = create_category( + client, + TestId.make_unique("leaf1ab"), + glossary_qualified_name=hierarchy_glossary.qualified_name, + parent=mid1a_category, + ) + yield c + delete_asset(client, guid=c.guid, asset_type=AtlasGlossaryCategory) + + +@pytest.fixture(scope="module") +def mid1b_category( + client: AtlanClient, + hierarchy_glossary: AtlasGlossary, + top1_category: AtlasGlossaryCategory, +) -> Generator[AtlasGlossaryCategory, None, None]: + c = create_category( + client, TestId.make_unique("mid1b"), hierarchy_glossary, parent=top1_category + ) + yield c + delete_asset(client, guid=c.guid, asset_type=AtlasGlossaryCategory) + + +@pytest.fixture(scope="module") +def leaf1ba_category( + client: AtlanClient, + hierarchy_glossary: AtlasGlossary, + mid1b_category: AtlasGlossaryCategory, +) -> Generator[AtlasGlossaryCategory, None, None]: + c = create_category( + client, TestId.make_unique("leaf1ba"), hierarchy_glossary, parent=mid1b_category + ) + yield c + delete_asset(client, guid=c.guid, asset_type=AtlasGlossaryCategory) + + +@pytest.fixture(scope="module") +def top2_category( + client: AtlanClient, hierarchy_glossary: AtlasGlossary +) -> Generator[AtlasGlossaryCategory, None, None]: + c = create_category(client, TestId.make_unique("top2"), hierarchy_glossary) + yield c + delete_asset(client, guid=c.guid, asset_type=AtlasGlossaryCategory) + + +@pytest.fixture(scope="module") +def mid2a_category( + client: AtlanClient, + hierarchy_glossary: AtlasGlossary, + top2_category: AtlasGlossaryCategory, +) -> Generator[AtlasGlossaryCategory, None, None]: + c = create_category( + client, TestId.make_unique("mid2a"), hierarchy_glossary, parent=top2_category + ) + yield c + delete_asset(client, guid=c.guid, asset_type=AtlasGlossaryCategory) + + +@pytest.fixture(scope="module") +def leaf2aa_category( + client: AtlanClient, + hierarchy_glossary: AtlasGlossary, + mid2a_category: AtlasGlossaryCategory, +) -> Generator[AtlasGlossaryCategory, None, None]: + c = create_category( + client, TestId.make_unique("leaf2aa"), hierarchy_glossary, parent=mid2a_category + ) + yield c + delete_asset(client, guid=c.guid, asset_type=AtlasGlossaryCategory) + + +@pytest.fixture(scope="module") +def leaf2ab_category( + client: AtlanClient, + hierarchy_glossary: AtlasGlossary, + mid2a_category: AtlasGlossaryCategory, +) -> Generator[AtlasGlossaryCategory, None, None]: + c = create_category( + client, TestId.make_unique("leaf2ab"), hierarchy_glossary, parent=mid2a_category + ) + yield c + delete_asset(client, guid=c.guid, asset_type=AtlasGlossaryCategory) + + +@pytest.fixture(scope="module") +def mid2b_category( + client: AtlanClient, + hierarchy_glossary: AtlasGlossary, + top2_category: AtlasGlossaryCategory, +) -> Generator[AtlasGlossaryCategory, None, None]: + c = create_category( + client, TestId.make_unique("mid2b"), hierarchy_glossary, parent=top2_category + ) + yield c + delete_asset(client, guid=c.guid, asset_type=AtlasGlossaryCategory) + + +@pytest.fixture(scope="module") +def leaf2ba_category( + client: AtlanClient, + hierarchy_glossary: AtlasGlossary, + mid2b_category: AtlasGlossaryCategory, +) -> Generator[AtlasGlossaryCategory, None, None]: + c = create_category( + client, TestId.make_unique("leaf2ba"), hierarchy_glossary, parent=mid2b_category + ) + yield c + delete_asset(client, guid=c.guid, asset_type=AtlasGlossaryCategory) + + +@pytest.fixture(scope="module") +def term_user_def_relationship() -> UserDefRelationship: + test_id = MODULE_NAME.lower() + return UserDefRelationship( + from_type_label=f"Testing from label ({test_id})", + to_type_label=f"Testing to label ({test_id})", + ) + + +def test_category( + client: AtlanClient, category: AtlasGlossaryCategory, glossary: AtlasGlossary +): + assert category.guid + assert category.name == MODULE_NAME + assert category.qualified_name + c = client.asset.get_by_guid( + category.guid, AtlasGlossaryCategory, ignore_relationships=False + ) + assert c + assert c.guid == category.guid + assert c.anchor + assert c.anchor.guid == glossary.guid + + +@pytest.fixture(scope="module") +def term1( + client: AtlanClient, glossary: AtlasGlossary +) -> Generator[AtlasGlossaryTerm, None, None]: + t = create_term(client, name=TERM_NAME1, glossary=glossary) + yield t + delete_asset(client, guid=t.guid, asset_type=AtlasGlossaryTerm) + + +def test_term_failure( + client: AtlanClient, + glossary: AtlasGlossary, +): + with pytest.raises( + NotFoundError, + match="ATLAN-PYTHON-404-000 Server responded with a not found " + "error ATLAS-404-00-009: Instance AtlasGlossaryTerm with unique attribute *", + ): + client.asset.update_merging_cm( + AtlasGlossaryTerm.creator( + name=f"{TERM_NAME1} X", glossary_guid=glossary.guid + ) + ) + + +def test_term1( + client: AtlanClient, + term1: AtlasGlossaryTerm, + glossary: AtlasGlossary, +): + assert term1.guid + assert term1.name == TERM_NAME1 + assert term1.qualified_name + assert term1.qualified_name != TERM_NAME1 + t = client.asset.get_by_guid( + term1.guid, asset_type=AtlasGlossaryTerm, ignore_relationships=False + ) + assert t + assert t.guid == term1.guid + assert t.attributes.anchor + assert t.attributes.anchor.guid == glossary.guid + + +@pytest.fixture(scope="module") +def term2( + client: AtlanClient, glossary: AtlasGlossary +) -> Generator[AtlasGlossaryTerm, None, None]: + t = create_term(client, name=TERM_NAME2, glossary_guid=glossary.guid) + yield t + delete_asset(client, guid=t.guid, asset_type=AtlasGlossaryTerm) + + +def test_term2( + client: AtlanClient, + term2: AtlasGlossaryTerm, + glossary: AtlasGlossary, +): + assert term2.guid + assert term2.name == TERM_NAME2 + assert term2.qualified_name + assert term2.qualified_name != TERM_NAME2 + t = client.asset.get_by_guid( + term2.guid, asset_type=AtlasGlossaryTerm, ignore_relationships=False + ) + assert t + assert t.guid == term2.guid + assert t.attributes.anchor + assert t.attributes.anchor.guid == glossary.guid + + +@pytest.fixture(scope="module") +def term3( + client: AtlanClient, glossary: AtlasGlossary +) -> Generator[AtlasGlossaryTerm, None, None]: + t = create_term( + client, name=TERM_NAME3, glossary_qualified_name=glossary.qualified_name + ) + yield t + delete_asset(client, guid=t.guid, asset_type=AtlasGlossaryTerm) + + +def test_term3( + client: AtlanClient, + term3: AtlasGlossaryTerm, + glossary: AtlasGlossary, +): + assert term3.guid + assert term3.name == TERM_NAME3 + assert term3.qualified_name + assert term3.qualified_name != TERM_NAME3 + t = client.asset.get_by_guid( + term3.guid, asset_type=AtlasGlossaryTerm, ignore_relationships=False + ) + assert t + assert t.guid == term3.guid + assert t.attributes.anchor + assert t.attributes.anchor.guid == glossary.guid + + +@pytest.fixture(scope="module") +def term4( + client: AtlanClient, glossary: AtlasGlossary +) -> Generator[AtlasGlossaryTerm, None, None]: + t = create_term(client, name=TERM_NAME4, glossary_guid=glossary.guid) + yield t + delete_asset(client, guid=t.guid, asset_type=AtlasGlossaryTerm) + + +def test_term4( + client: AtlanClient, + term4: AtlasGlossaryTerm, + glossary: AtlasGlossary, +): + assert term4.guid + assert term4.name == TERM_NAME4 + assert term4.qualified_name + assert term4.qualified_name != TERM_NAME4 + t = client.asset.get_by_guid( + term4.guid, asset_type=AtlasGlossaryTerm, ignore_relationships=False + ) + assert t + assert t.guid == term4.guid + assert t.attributes.anchor + assert t.attributes.anchor.guid == glossary.guid + + +def test_read_glossary( + client: AtlanClient, + glossary: AtlasGlossary, + term1: AtlasGlossaryTerm, + term2: AtlasGlossaryTerm, + term3: AtlasGlossaryTerm, + term4: AtlasGlossaryTerm, +): + g = client.asset.get_by_guid( + glossary.guid, asset_type=AtlasGlossary, ignore_relationships=False + ) + assert g + assert isinstance(g, AtlasGlossary) + assert g.guid == glossary.guid + assert g.qualified_name == glossary.qualified_name + assert g.name == glossary.name + terms = g.terms + assert terms + assert len(terms) == 4 + + +def test_compound_queries( + client: AtlanClient, + glossary: AtlasGlossary, + term1: AtlasGlossaryTerm, + term2: AtlasGlossaryTerm, + term3: AtlasGlossaryTerm, + term4: AtlasGlossaryTerm, +): + assert glossary.qualified_name + cq = ( + CompoundQuery() + .where(CompoundQuery.active_assets()) + .where(CompoundQuery.asset_type(AtlasGlossaryTerm)) + .where(AtlasGlossaryTerm.NAME.startswith(MODULE_NAME)) + .where(AtlasGlossaryTerm.ANCHOR.eq(glossary.qualified_name)) + ).to_query() + request = IndexSearchRequest(dsl=DSL(query=cq)) + # Use centralized retry utility for eventual consistency + assert_search_count_with_retry(client, request, expected_count=4) + assert glossary.qualified_name + assert term2.name + + cq = ( + CompoundQuery() + .where(CompoundQuery.active_assets()) + .where(CompoundQuery.asset_type(AtlasGlossaryTerm)) + .where(AtlasGlossaryTerm.NAME.startswith(MODULE_NAME)) + .where(AtlasGlossaryTerm.ANCHOR.eq(glossary.qualified_name)) + .where_not(AtlasGlossaryTerm.NAME.eq(term2.name)) + ).to_query() + request = IndexSearchRequest(dsl=DSL(query=cq)) + # Use centralized retry utility for eventual consistency + assert_search_count_with_retry(client, request, expected_count=3) + + +def test_fluent_search( + client: AtlanClient, + glossary: AtlasGlossary, + term1: AtlasGlossaryTerm, + term2: AtlasGlossaryTerm, + term3: AtlasGlossaryTerm, + term4: AtlasGlossaryTerm, +): + assert glossary.qualified_name + terms = ( + FluentSearch() + .page_size(1) + .where(CompoundQuery.active_assets()) + .where(CompoundQuery.asset_type(AtlasGlossaryTerm)) + .where(AtlasGlossaryTerm.NAME.startswith(MODULE_NAME)) + .where(AtlasGlossaryTerm.ANCHOR.eq(glossary.qualified_name)) + .include_on_results(AtlasGlossaryTerm.ANCHOR) + .include_on_relations(AtlasGlossary.NAME) + ) + + # Use centralized retry utility to handle search index eventual consistency + assert_fluent_search_count_with_retry(terms, client, expected_count=4) + + guids_chained = [] + g_sorted = [] + for asset in filter( + lambda x: isinstance(x, AtlasGlossaryTerm), + itertools.islice(terms.execute(client), 4), + ): + guids_chained.append(asset.guid) + g_sorted.append(asset.guid) + g_sorted.sort() + assert guids_chained == g_sorted + + results = FluentSearch( + _page_size=5, + wheres=[ + CompoundQuery.active_assets(), + CompoundQuery.asset_type(AtlasGlossaryTerm), + AtlasGlossaryTerm.NAME.startswith(MODULE_NAME), + AtlasGlossaryTerm.ANCHOR.startswith(glossary.qualified_name), + ], + _includes_on_results=[AtlasGlossaryTerm.ANCHOR.atlan_field_name], + _includes_on_relations=[AtlasGlossary.NAME.atlan_field_name], + ).execute(client) + + guids_alt = [] + g_sorted = [] + for asset in results: + guids_alt.append(asset.guid) + g_sorted.append(asset.guid) + g_sorted.sort() + assert g_sorted == guids_alt + assert glossary.qualified_name + + results = FluentSearch( + _page_size=5, + wheres=[ + CompoundQuery.active_assets(), + CompoundQuery.asset_type(AtlasGlossaryTerm), + AtlasGlossaryTerm.NAME.startswith(MODULE_NAME), + AtlasGlossaryTerm.ANCHOR.startswith(glossary.qualified_name), + ], + _includes_on_results=["anchor"], + _includes_on_relations=["name"], + sorts=[AtlasGlossaryTerm.NAME.order()], + ).execute(client) + + names = [] + names_sorted = [] + for asset in results: + names.append(asset.name) + names_sorted.append(asset.name) + names_sorted.sort() + assert names_sorted == names + + +@pytest.mark.order(after="test_read_glossary") +def test_trim_to_required_glossary( + client: AtlanClient, + glossary: AtlasGlossary, +): + glossary = glossary.trim_to_required() + response = client.asset.save(glossary) + assert not response.mutated_entities + + +@pytest.mark.order(after="test_term1") +def test_term_trim_to_required( + client: AtlanClient, + term1: AtlasGlossaryTerm, +): + term1 = client.asset.get_by_guid( + guid=term1.guid, asset_type=AtlasGlossaryTerm, ignore_relationships=False + ) + term1 = term1.trim_to_required() + response = client.asset.save(term1) + assert not response.mutated_entities + + +def test_find_glossary_by_name(client: AtlanClient, glossary: AtlasGlossary): + assert glossary.guid == client.asset.find_glossary_by_name(name=glossary.name).guid + + +def test_find_category_fast_by_name( + client: AtlanClient, category: AtlasGlossaryCategory, glossary: AtlasGlossary +): + @retry( + wait=wait_fixed(2), + retry=retry_if_exception_type(NotFoundError), + stop=stop_after_attempt(3), + ) + def check_it(): + assert ( + category.guid + == client.asset.find_category_fast_by_name( + name=category.name, glossary_qualified_name=glossary.qualified_name + )[0].guid + ) + + check_it() + + +def test_find_category_by_name( + client: AtlanClient, category: AtlasGlossaryCategory, glossary: AtlasGlossary +): + assert ( + category.guid + == client.asset.find_category_by_name( + name=category.name, glossary_name=glossary.name + )[0].guid + ) + + +def test_find_category_by_name_qn_guid_correctly_populated( + client: AtlanClient, + hierarchy_glossary: AtlasGlossary, + top1_category: AtlasGlossaryCategory, + top2_category: AtlasGlossaryCategory, + mid1a_category: AtlasGlossaryCategory, + mid1a_term: AtlasGlossaryTerm, + mid2a_category: AtlasGlossaryCategory, +): + category = client.asset.find_category_by_name( + name=mid1a_category.name, + glossary_name=hierarchy_glossary.name, + attributes=["terms", "anchor", "parentCategory"], + )[0] + + # Glossary + assert category.anchor + assert category.anchor.guid == hierarchy_glossary.guid + assert category.anchor.name == hierarchy_glossary.name + assert category.anchor.qualified_name == hierarchy_glossary.qualified_name + + # Glossary category + assert category.parent_category + assert category.parent_category.guid == top1_category.guid + assert category.parent_category.name == top1_category.name + assert category.parent_category.qualified_name == top1_category.qualified_name + + # Glossary term + assert category.terms and category.terms[0] + assert category.terms[0].guid == mid1a_term.guid + assert category.terms[0].name == mid1a_term.name + assert category.terms[0].qualified_name == mid1a_term.qualified_name + + +def test_category_delete_by_guid_raises_error_invalid_request_error( + client: AtlanClient, category: AtlasGlossaryCategory +): + with pytest.raises( + InvalidRequestError, + match=f"ATLAN-PYTHON-400-052 Asset with guid: {category.guid} is an asset " + "of type AtlasGlossaryCategory which does not support archiving. " + "Suggestion: Please use purge if you wish to remove assets of this type.", + ): + client.asset.delete_by_guid(guid=category.guid) + + +def test_find_term_fast_by_name( + client: AtlanClient, term1: AtlasGlossaryTerm, glossary: AtlasGlossary +): + @retry( + wait=wait_fixed(2), + retry=retry_if_exception_type(NotFoundError), + stop=stop_after_attempt(3), + ) + def check_it(): + assert ( + term1.guid + == client.asset.find_term_fast_by_name( + name=term1.name, glossary_qualified_name=glossary.qualified_name + ).guid + ) + + check_it() + + +def test_find_term_by_name( + client: AtlanClient, term1: AtlasGlossaryTerm, glossary: AtlasGlossary +): + assert ( + term1.guid + == client.asset.find_term_by_name( + name=term1.name, glossary_name=glossary.name + ).guid + ) + + +@pytest.mark.parametrize( + "attributes, related_attributes", + [ + (AtlasGlossaryCategory.TERMS, AtlasGlossaryTerm.NAME), + ( + AtlasGlossaryCategory.TERMS.atlan_field_name, + AtlasGlossaryTerm.NAME.atlan_field_name, + ), + ], +) +def test_hierarchy( + client: AtlanClient, + hierarchy_glossary: AtlasGlossary, + top1_category: AtlasGlossaryCategory, + mid1a_category: AtlasGlossaryCategory, + leaf1aa_category: AtlasGlossaryCategory, + leaf1ab_category: AtlasGlossaryCategory, + mid1b_category: AtlasGlossaryCategory, + leaf1ba_category: AtlasGlossaryCategory, + top2_category: AtlasGlossaryCategory, + mid2a_category: AtlasGlossaryCategory, + leaf2aa_category: AtlasGlossaryCategory, + leaf2ab_category: AtlasGlossaryCategory, + mid2b_category: AtlasGlossaryCategory, + leaf2ba_category: AtlasGlossaryCategory, + attributes: Union[AtlanField, str], + related_attributes: Union[AtlanField, str], +): + sleep(10) + hierarchy = client.asset.get_hierarchy( + glossary=hierarchy_glossary, + attributes=[attributes], + related_attributes=[related_attributes], + ) + + root_categories = hierarchy.root_categories + + assert root_categories + assert len(root_categories) == 2 + assert root_categories[0].name + assert root_categories[1].name + assert "top" in root_categories[0].name + assert "top" in root_categories[1].name + assert hierarchy.get_category(top1_category.guid) + category_without_terms = hierarchy.get_category(top1_category.guid) + assert category_without_terms.terms is not None + assert 0 == len(category_without_terms.terms) + assert hierarchy.get_category(mid1a_category.guid) + category_with_term = hierarchy.get_category(mid1a_category.guid) + assert category_with_term.terms + assert 1 == len(category_with_term.terms) + assert f"mid1a_{TERM_NAME1}" == category_with_term.terms[0].name + assert hierarchy.get_category(leaf1aa_category.guid) + assert hierarchy.get_category(leaf1ab_category.guid) + assert hierarchy.get_category(mid1b_category.guid) + assert hierarchy.get_category(leaf1ba_category.guid) + assert hierarchy.get_category(top2_category.guid) + assert hierarchy.get_category(mid2a_category.guid) + assert hierarchy.get_category(leaf2aa_category.guid) + assert hierarchy.get_category(leaf2ab_category.guid) + assert hierarchy.get_category(mid2b_category.guid) + assert hierarchy.get_category(leaf2ba_category.guid) + + category_names = [category.name for category in hierarchy.breadth_first] + + assert len(category_names) == 12 + assert category_names + assert category_names[0] + assert category_names[1] + assert category_names[2] + assert category_names[3] + assert category_names[4] + assert category_names[5] + assert category_names[6] + assert category_names[7] + assert category_names[8] + assert category_names[9] + assert category_names[10] + assert category_names[11] + assert "top" in category_names[0] + assert "top" in category_names[1] + assert "mid" in category_names[2] + assert "mid" in category_names[3] + assert "mid" in category_names[4] + assert "mid" in category_names[5] + assert "leaf" in category_names[6] + assert "leaf" in category_names[7] + assert "leaf" in category_names[8] + assert "leaf" in category_names[9] + assert "leaf" in category_names[10] + assert "leaf" in category_names[11] + + category_names = [category.name for category in hierarchy.depth_first] + + assert len(category_names) == 12 + assert category_names + assert category_names[0] + assert category_names[1] + assert category_names[2] + assert category_names[3] + assert category_names[4] + assert category_names[5] + assert category_names[6] + assert category_names[7] + assert category_names[8] + assert category_names[9] + assert category_names[10] + assert category_names[11] + assert "top" in category_names[0] + assert "mid" in category_names[1] + assert "leaf" in category_names[2] + assert "leaf" in category_names[3] + assert "mid" in category_names[4] + assert "leaf" in category_names[5] + assert "top" in category_names[6] + assert "mid" in category_names[7] + assert "leaf" in category_names[8] + assert "leaf" in category_names[9] + assert "mid" in category_names[10] + assert "leaf" in category_names[11] + + +def test_create_relationship( + client: AtlanClient, + term1: AtlasGlossaryTerm, + term2: AtlasGlossaryTerm, + term3: AtlasGlossaryTerm, + glossary: AtlasGlossary, +): + assert term1 + assert term1.name + assert term1.qualified_name + + term = AtlasGlossaryTerm.create_for_modification( + qualified_name=term1.qualified_name, + name=term1.name, + glossary_guid=glossary.guid, + ) + term.see_also = [ + AtlasGlossaryTerm.ref_by_guid(guid=term2.guid), + AtlasGlossaryTerm.ref_by_guid(guid=term3.guid), + ] + response = client.asset.save(term) + + assert response + result = client.asset.get_by_guid( + guid=term1.guid, asset_type=AtlasGlossaryTerm, ignore_relationships=False + ) + assert result + assert result.see_also + assert len(result.see_also) == 2 + related_guids = [] + for term in result.see_also: + assert term.guid + related_guids.append(term.guid) + assert term2.guid in related_guids + assert term3.guid in related_guids + + +@pytest.mark.order(after="test_create_relationship") +def test_remove_relationship( + client: AtlanClient, + term1: AtlasGlossaryTerm, + term2: AtlasGlossaryTerm, + term3: AtlasGlossaryTerm, + glossary: AtlasGlossary, +): + assert term1 + assert term1.name + assert term1.qualified_name + + term = AtlasGlossaryTerm.create_for_modification( + qualified_name=term1.qualified_name, + name=term1.name, + glossary_guid=glossary.guid, + ) + term.see_also = [ + AtlasGlossaryTerm.ref_by_guid(guid=term2.guid, semantic=SaveSemantic.REMOVE), + ] + response = client.asset.save(term) + + assert response + result = client.asset.get_by_guid( + guid=term1.guid, asset_type=AtlasGlossaryTerm, ignore_relationships=False + ) + assert result + assert result.see_also + active_relationships = [] + for term in result.see_also: + assert term.guid + if term.relationship_status == "ACTIVE": + active_relationships.append(term) + assert len(active_relationships) == 1 + assert term3.guid == active_relationships[0].guid + + +@pytest.mark.order(after="test_remove_relationship") +def test_append_relationship( + client: AtlanClient, + term1: AtlasGlossaryTerm, + term3: AtlasGlossaryTerm, + term4: AtlasGlossaryTerm, + glossary: AtlasGlossary, +): + assert term1 + assert term1.name + assert term1.qualified_name + + term = AtlasGlossaryTerm.create_for_modification( + qualified_name=term1.qualified_name, + name=term1.name, + glossary_guid=glossary.guid, + ) + term.see_also = [ + AtlasGlossaryTerm.ref_by_guid(guid=term4.guid, semantic=SaveSemantic.APPEND), + ] + response = client.asset.save(term) + + assert response + result = client.asset.get_by_guid( + guid=term1.guid, asset_type=AtlasGlossaryTerm, ignore_relationships=False + ) + assert result + assert result.see_also + active_relationships = [] + for term in result.see_also: + assert term.guid + if term.relationship_status == "ACTIVE": + active_relationships.append(term.guid) + assert len(active_relationships) == 2 + assert term3.guid in active_relationships + assert term4.guid in active_relationships + + +@pytest.mark.order(after="test_append_relationship") +def test_append_relationship_again( + client: AtlanClient, + term1: AtlasGlossaryTerm, + term3: AtlasGlossaryTerm, + term4: AtlasGlossaryTerm, + glossary: AtlasGlossary, +): + assert term1 + assert term1.name + assert term1.qualified_name + + term = AtlasGlossaryTerm.create_for_modification( + qualified_name=term1.qualified_name, + name=term1.name, + glossary_guid=glossary.guid, + ) + term.see_also = [ + AtlasGlossaryTerm.ref_by_guid(guid=term4.guid, semantic=SaveSemantic.APPEND), + ] + response = client.asset.save(term) + + assert response + result = client.asset.get_by_guid( + guid=term1.guid, asset_type=AtlasGlossaryTerm, ignore_relationships=False + ) + assert result + assert result.see_also + active_relationships = [] + for term in result.see_also: + assert term.guid + if term.relationship_status == "ACTIVE": + active_relationships.append(term.guid) + assert len(active_relationships) == 2 + assert term3.guid in active_relationships + assert term4.guid in active_relationships + + +@pytest.mark.order(after="test_append_relationship_again") +def test_remove_unrelated_relationship( + client: AtlanClient, + term1: AtlasGlossaryTerm, + term2: AtlasGlossaryTerm, + term3: AtlasGlossaryTerm, + term4: AtlasGlossaryTerm, + glossary: AtlasGlossary, +): + assert term1 + assert term1.name + assert term1.qualified_name + + term = AtlasGlossaryTerm.create_for_modification( + qualified_name=term1.qualified_name, + name=term1.name, + glossary_guid=glossary.guid, + ) + term.see_also = [ + AtlasGlossaryTerm.ref_by_guid(guid=term2.guid, semantic=SaveSemantic.REMOVE), + ] + + response = client.asset.save(term) + assert response + + result = client.asset.get_by_guid( + guid=term1.guid, asset_type=AtlasGlossaryTerm, ignore_relationships=False + ) + assert result + assert result.see_also + active_relationships = [] + for term in result.see_also: + assert term.guid + if term.relationship_status == "ACTIVE": + active_relationships.append(term.guid) + assert len(active_relationships) == 2 + assert term3.guid in active_relationships + assert term4.guid in active_relationships + + +def test_move_sub_category_to_category( + client: AtlanClient, + hierarchy_glossary: AtlasGlossary, + top1_category: AtlasGlossaryCategory, + top2_category: AtlasGlossaryCategory, + mid1a_category: AtlasGlossaryCategory, + mid2a_category: AtlasGlossaryCategory, +): + sleep(10) + assert mid1a_category.name + assert hierarchy_glossary.guid + assert top1_category.qualified_name + assert top2_category.qualified_name + assert mid1a_category.qualified_name + + hierarchy = client.asset.get_hierarchy(glossary=hierarchy_glossary) + root_categories = hierarchy.root_categories + + assert len(root_categories) == 2 + root_category_qns = ( + root_categories[0].qualified_name, + root_categories[1].qualified_name, + ) + assert top1_category.qualified_name in root_category_qns + assert top2_category.qualified_name in root_category_qns + + mid1a_category = AtlasGlossaryCategory.updater( + name=mid1a_category.name, + qualified_name=mid1a_category.qualified_name, + glossary_guid=hierarchy_glossary.guid, + ) + mid1a_category.parent_category = None + response = client.asset.save(mid1a_category) + + if updated := response.assets_updated(asset_type=AtlasGlossaryCategory): + assert updated[0].name == mid1a_category.name + assert updated[0].qualified_name == mid1a_category.qualified_name + else: + pytest.fail(f"Failed to perform update on category: {mid1a_category.name}") + + # Ensure that the sub-category 'mid1a_category' + # has been successfully moved to the root category + sleep(10) + hierarchy = client.asset.get_hierarchy(glossary=hierarchy_glossary) + root_categories = hierarchy.root_categories + + assert len(root_categories) == 3 + root_category_qns_updated = ( + root_categories[0].qualified_name, + root_categories[1].qualified_name, + root_categories[2].qualified_name, + ) + assert top1_category.qualified_name in root_category_qns_updated + assert top2_category.qualified_name in root_category_qns_updated + assert mid1a_category.qualified_name in root_category_qns_updated + + +def test_user_def_relationship_on_terms( + client: AtlanClient, + term1: AtlasGlossaryTerm, + term2: AtlasGlossaryTerm, + glossary: AtlasGlossary, + term_user_def_relationship: UserDefRelationship, +): + term1_to_update = AtlasGlossaryTerm.updater( + qualified_name=term1.qualified_name, + name=term1.name, + glossary_guid=glossary.guid, + ) + term2 = AtlasGlossaryTerm.ref_by_guid(term2.guid) + term1_to_update.user_def_relationship_to = [ + term_user_def_relationship.user_def_relationship_to(term2) + ] + + response = client.asset.save(term1_to_update) + assert response.mutated_entities + assert not response.mutated_entities.CREATE + assert response.mutated_entities.UPDATE + assert len(response.mutated_entities.UPDATE) == 2 + assets = response.assets_updated(asset_type=AtlasGlossaryTerm) + assert len(assets) == 2 + + +def _assert_relationship(relationship, expected_type_name, udr): + assert relationship + assert relationship.guid + assert relationship.type_name + ra = getattr(relationship, "relationship_attributes", None) + if ra and isinstance(ra, dict): + assert ra.get("typeName") == expected_type_name + elif hasattr(relationship, "attributes") and relationship.attributes: + assert relationship.attributes.relationship_attributes + assert ( + relationship.attributes.relationship_attributes.type_name + == expected_type_name + ) + assert relationship.attributes.relationship_attributes == udr + + +@pytest.mark.order(after="test_user_def_relationship_on_terms") +def test_search_user_def_relationship_on_terms( + client: AtlanClient, + term1: AtlasGlossaryTerm, + term2: AtlasGlossaryTerm, + term_user_def_relationship: UserDefRelationship, +): + # Wait for assets to be indexed + sleep(5) + assert term1 and term1.guid + assert term2 and term2.guid + results = ( + FluentSearch() + .select() + .where_some(AtlasGlossaryTerm.GUID.eq(term1.guid)) + .where_some(AtlasGlossaryTerm.GUID.eq(term2.guid)) + .include_on_results(AtlasGlossaryTerm.USER_DEF_RELATIONSHIP_TO) + .include_on_results(AtlasGlossaryTerm.USER_DEF_RELATIONSHIP_FROM) + .include_relationship_attributes(True) + .enable_full_restriction(True) + .execute(client=client) + ) + assert results and results.count == 2 + for asset in results: + assert asset and asset.guid + if asset.guid == term1.guid: + assert ( + asset.user_def_relationship_to + and len(asset.user_def_relationship_to) == 1 + ) + _assert_relationship( + asset.user_def_relationship_to[0], + UserDefRelationship.__name__, + term_user_def_relationship, + ) + else: + assert ( + asset.user_def_relationship_from + and len(asset.user_def_relationship_from) == 1 + ) + _assert_relationship( + asset.user_def_relationship_from[0], + UserDefRelationship.__name__, + term_user_def_relationship, + ) diff --git a/tests_v9/integration/insights_test.py b/tests_v9/integration/insights_test.py new file mode 100644 index 000000000..09db94e9e --- /dev/null +++ b/tests_v9/integration/insights_test.py @@ -0,0 +1,203 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. +from typing import Generator + +import pytest + +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.model.assets import Collection, Folder, Query, Schema +from pyatlan_v9.model.enums import AtlanConnectorType, EntityStatus +from pyatlan_v9.model.fluent_search import FluentSearch +from tests_v9.integration.client import TestId, delete_asset + +PREFIX = TestId.make_unique("INS") + +COLLECTION_NAME = PREFIX +FOLDER_NAME = PREFIX + "_folder" +SUB_FOLDER_NAME = FOLDER_NAME + "_sub" +QUERY_NAME = PREFIX + "_query" +RAW_QUERY = "SELECT * FROM DIM_CUSTOMERS;" +EXISTING_GROUP_NAME = "admins" +CONNECTION_NAME = "development" +DB_NAME = "analytics" +SCHEMA_NAME = "WIDE_WORLD_IMPORTERS" +USER_DESCRIPTION = "Automated testing of the Python SDK." + + +@pytest.fixture(scope="module") +def collection(client: AtlanClient) -> Generator[Collection, None, None]: + collection = Collection.creator(client=client, name=COLLECTION_NAME) + collection.admin_groups = [EXISTING_GROUP_NAME] + response = client.asset.save(collection) + result = response.assets_created(asset_type=Collection)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=Collection) + + +@pytest.fixture(scope="module") +def folder( + client: AtlanClient, collection: Collection +) -> Generator[Folder, None, None]: + assert collection and collection.qualified_name + folder = Folder.creator( + name=FOLDER_NAME, collection_qualified_name=collection.qualified_name + ) + response = client.asset.save(folder) + result = response.assets_created(asset_type=Folder)[0] + updated = response.assets_updated(asset_type=Collection)[0] + assert ( + updated + and updated.guid == collection.guid + and updated.qualified_name == collection.qualified_name + ) + yield result + delete_asset(client, guid=result.guid, asset_type=Folder) + + +@pytest.fixture(scope="module") +def sub_folder(client: AtlanClient, folder: Folder) -> Generator[Folder, None, None]: + assert folder and folder.qualified_name + sub = Folder.creator( + name=SUB_FOLDER_NAME, parent_folder_qualified_name=folder.qualified_name + ) + response = client.asset.save(sub) + result = response.assets_created(asset_type=Folder)[0] + updated = response.assets_updated(asset_type=Folder)[0] + assert ( + updated + and updated.guid == folder.guid + and updated.qualified_name == folder.qualified_name + ) + yield result + delete_asset(client, guid=result.guid, asset_type=Folder) + + +@pytest.fixture(scope="module") +def query(client: AtlanClient, folder: Folder) -> Generator[Query, None, None]: + connection = client.asset.find_connections_by_name( + name=CONNECTION_NAME, connector_type=AtlanConnectorType.SNOWFLAKE + ) + assert connection and len(connection) == 1 and connection[0].qualified_name + results = ( + FluentSearch() + .select() + .where(Schema.CONNECTION_QUALIFIED_NAME.eq(connection[0].qualified_name)) + .where(Schema.DATABASE_NAME.eq(DB_NAME)) + .where(Schema.NAME.eq(SCHEMA_NAME)) + .execute(client=client) + ) + assert results and len(results.current_page()) == 1 + schema = results.current_page()[0] + assert schema and schema.qualified_name + assert folder and folder.qualified_name + to_create = Query.creator( + name=QUERY_NAME, parent_folder_qualified_name=folder.qualified_name + ) + to_create.with_raw_query( + schema_qualified_name=schema.qualified_name, query=RAW_QUERY + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=Query)[0] + updated = response.assets_updated(asset_type=Folder)[0] + assert ( + updated + and updated.guid == folder.guid + and updated.qualified_name == folder.qualified_name + ) + yield result + delete_asset(client, guid=result.guid, asset_type=Folder) + + +def test_create_collection(collection): + assert collection + assert collection.name == COLLECTION_NAME + assert collection.guid and collection.qualified_name + + +def test_create_folder(folder, collection): + assert folder + assert folder.name == FOLDER_NAME + assert folder.guid and folder.qualified_name + assert folder.collection_qualified_name == collection.qualified_name + assert folder.parent_qualified_name == collection.qualified_name + + +def test_create_sub_folder(sub_folder: Folder, folder: Folder, collection: Collection): + assert sub_folder + assert sub_folder.name == SUB_FOLDER_NAME + assert sub_folder.guid and sub_folder.qualified_name + assert sub_folder.collection_qualified_name == collection.qualified_name + assert sub_folder.parent_qualified_name == folder.qualified_name + + +def test_create_query( + client: AtlanClient, query: Query, folder: Folder, collection: Collection +): + assert query + assert query.name == QUERY_NAME + assert query.guid and query.qualified_name + assert query.collection_qualified_name == collection.qualified_name + assert query.parent_qualified_name == folder.qualified_name + + +def test_update_query( + client: AtlanClient, + collection: Collection, + folder: Folder, + query: Query, +): + query = query.updater( + name=query.name, + qualified_name=query.qualified_name, + collection_qualified_name=collection.qualified_name, + parent_qualified_name=folder.qualified_name, + ) + query.user_description = USER_DESCRIPTION + response = client.asset.save(query) + updated = response.assets_updated(asset_type=Query)[0] + assert updated and updated.qualified_name == query.qualified_name + + +@pytest.mark.order(after="test_update_query") +def test_retrieve_query( + client: AtlanClient, + query: Query, +): + retrieved = client.asset.get_by_guid(query.guid, asset_type=Query) + assert retrieved + assert not retrieved.is_incomplete + assert retrieved.guid == query.guid + assert retrieved.qualified_name == query.qualified_name + assert retrieved.name == query.name + assert retrieved.user_description == USER_DESCRIPTION + + +@pytest.mark.order(after="test_retrieve_query") +def test_delete_query( + client: AtlanClient, + query: Query, +): + response = client.asset.delete_by_guid(guid=query.guid) + assert response + assert not response.assets_created(asset_type=Query) + assert not response.assets_updated(asset_type=Query) + deleted = response.assets_deleted(asset_type=Query) + + assert deleted + assert len(deleted) == 1 + assert deleted[0].guid == query.guid + assert deleted[0].delete_handler == "SOFT" + assert deleted[0].status == EntityStatus.DELETED + assert deleted[0].qualified_name == query.qualified_name + + +@pytest.mark.order(after="test_delete_query") +def test_read_deleted_query( + client: AtlanClient, + query: Query, +): + deleted = client.asset.get_by_guid(guid=query.guid, asset_type=Query) + assert deleted + assert deleted.status == EntityStatus.DELETED + assert deleted.guid == query.guid + assert deleted.qualified_name == query.qualified_name diff --git a/tests_v9/integration/kafka_asset_test.py b/tests_v9/integration/kafka_asset_test.py new file mode 100644 index 000000000..2169b5439 --- /dev/null +++ b/tests_v9/integration/kafka_asset_test.py @@ -0,0 +1,215 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +from typing import Generator + +import pytest + +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.model.assets import Connection, KafkaConsumerGroup, KafkaTopic +from pyatlan_v9.model.core import Announcement +from pyatlan_v9.model.enums import ( + AnnouncementType, + AtlanConnectorType, + CertificateStatus, + EntityStatus, +) +from tests_v9.integration.client import TestId, delete_asset +from tests_v9.integration.connection_test import create_connection + +MODULE_NAME = TestId.make_unique("KAFKA") + +KAKFA_TOPIC_NAME = f"test_topic_{MODULE_NAME}" +KAKFA_CONSUMER_GROUP_NAME = f"test_consumer_group_{MODULE_NAME}" +CERTIFICATE_STATUS = CertificateStatus.VERIFIED + +ANNOUNCEMENT_TITLE = "Python SDK testing." +ANNOUNCEMENT_TYPE = AnnouncementType.INFORMATION +CERTIFICATE_MESSAGE = "Automated testing of the Python SDK." +ANNOUNCEMENT_MESSAGE = "Automated testing of the Python SDK." + + +@pytest.fixture(scope="module") +def connection(client: AtlanClient) -> Generator[Connection, None, None]: + result = create_connection( + client=client, name=MODULE_NAME, connector_type=AtlanConnectorType.KAFKA + ) + yield result + delete_asset(client, guid=result.guid, asset_type=Connection) + + +@pytest.fixture(scope="module") +def kafka_topic( + client: AtlanClient, connection: Connection +) -> Generator[KafkaTopic, None, None]: + assert connection.qualified_name + to_create = KafkaTopic.creator( + name=KAKFA_TOPIC_NAME, connection_qualified_name=connection.qualified_name + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=KafkaTopic)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=KafkaTopic) + + +def test_kafka_topic( + client: AtlanClient, + connection: Connection, + kafka_topic: KafkaTopic, +): + assert kafka_topic + assert kafka_topic.guid + assert kafka_topic.qualified_name + assert kafka_topic.name == KAKFA_TOPIC_NAME + assert kafka_topic.connector_name == AtlanConnectorType.KAFKA + assert kafka_topic.connection_qualified_name == connection.qualified_name + + +@pytest.fixture(scope="module") +def consumer_group( + client: AtlanClient, kafka_topic: KafkaTopic +) -> Generator[KafkaConsumerGroup, None, None]: + assert kafka_topic.qualified_name + to_create = KafkaConsumerGroup.creator( + name=KAKFA_CONSUMER_GROUP_NAME, + kafka_topic_qualified_names=[kafka_topic.qualified_name], + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=KafkaConsumerGroup)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=KafkaConsumerGroup) + + +def test_kafka_consumer_group( + client: AtlanClient, + kafka_topic: KafkaTopic, + consumer_group: KafkaConsumerGroup, +): + assert consumer_group + assert consumer_group.guid + assert consumer_group.qualified_name + assert consumer_group.name == KAKFA_CONSUMER_GROUP_NAME + assert consumer_group.connector_name == AtlanConnectorType.KAFKA + assert ( + kafka_topic.qualified_name + and consumer_group.kafka_topic_qualified_names + and kafka_topic.qualified_name in consumer_group.kafka_topic_qualified_names + ) + + +def _update_cert_and_annoucement(client, asset, asset_type): + assert asset.name + assert asset.qualified_name + + updated = client.asset.update_certificate( + name=asset.name, + asset_type=asset_type, + qualified_name=asset.qualified_name, + message=CERTIFICATE_MESSAGE, + certificate_status=CERTIFICATE_STATUS, + ) + assert updated + assert updated.certificate_status == CERTIFICATE_STATUS + assert updated.certificate_status_message == CERTIFICATE_MESSAGE + + updated = client.asset.update_announcement( + name=asset.name, + asset_type=asset_type, + qualified_name=asset.qualified_name, + announcement=Announcement( + announcement_type=ANNOUNCEMENT_TYPE, + announcement_title=ANNOUNCEMENT_TITLE, + announcement_message=ANNOUNCEMENT_MESSAGE, + ), + ) + assert updated + assert updated.announcement_type == ANNOUNCEMENT_TYPE + assert updated.announcement_title == ANNOUNCEMENT_TITLE + assert updated.announcement_message == ANNOUNCEMENT_MESSAGE + + +def test_update_kafka_assets( + client: AtlanClient, + kafka_topic: KafkaTopic, + consumer_group: KafkaConsumerGroup, +): + _update_cert_and_annoucement(client, kafka_topic, KafkaTopic) + _update_cert_and_annoucement(client, consumer_group, KafkaConsumerGroup) + + +def _retrieve_kafka_assets(client, asset, asset_type): + retrieved = client.asset.get_by_guid( + asset.guid, asset_type=asset_type, ignore_relationships=False + ) + assert retrieved + assert not retrieved.is_incomplete + assert retrieved.guid == asset.guid + assert retrieved.qualified_name == asset.qualified_name + assert retrieved.name == asset.name + assert retrieved.connector_name == AtlanConnectorType.KAFKA + assert retrieved.certificate_status == CERTIFICATE_STATUS + assert retrieved.certificate_status_message == CERTIFICATE_MESSAGE + + +@pytest.mark.order(after="test_update_kafka_assets") +def test_retrieve_kafka_assets( + client: AtlanClient, + kafka_topic: KafkaTopic, + consumer_group: KafkaConsumerGroup, +): + _retrieve_kafka_assets(client, kafka_topic, KafkaTopic) + _retrieve_kafka_assets(client, consumer_group, KafkaConsumerGroup) + + +@pytest.mark.order(after="test_retrieve_kafka_assets") +def test_delete_kafka_consumer_group( + client: AtlanClient, + consumer_group: KafkaConsumerGroup, +): + response = client.asset.delete_by_guid(guid=consumer_group.guid) + assert response + assert not response.assets_created(asset_type=KafkaConsumerGroup) + assert not response.assets_updated(asset_type=KafkaConsumerGroup) + deleted = response.assets_deleted(asset_type=KafkaConsumerGroup) + + assert deleted + assert len(deleted) == 1 + assert deleted[0].guid == consumer_group.guid + assert deleted[0].delete_handler == "SOFT" + assert deleted[0].status == EntityStatus.DELETED + assert deleted[0].qualified_name == consumer_group.qualified_name + + +@pytest.mark.order(after="test_delete_kafka_consumer_group") +def test_read_deleted_kafka_consumer_group( + client: AtlanClient, + consumer_group: KafkaConsumerGroup, +): + deleted = client.asset.get_by_guid( + consumer_group.guid, asset_type=KafkaConsumerGroup, ignore_relationships=False + ) + assert deleted + assert deleted.status == EntityStatus.DELETED + assert deleted.guid == consumer_group.guid + assert deleted.qualified_name == consumer_group.qualified_name + + +@pytest.mark.order(after="test_read_deleted_kafka_consumer_group") +def test_restore_kafka_consumer_group( + client: AtlanClient, + consumer_group: KafkaConsumerGroup, +): + assert consumer_group.qualified_name + assert client.asset.restore( + asset_type=KafkaConsumerGroup, qualified_name=consumer_group.qualified_name + ) + assert consumer_group.qualified_name + restored = client.asset.get_by_qualified_name( + asset_type=KafkaConsumerGroup, + qualified_name=consumer_group.qualified_name, + ignore_relationships=False, + ) + assert restored + assert restored.guid == consumer_group.guid + assert restored.status == EntityStatus.ACTIVE + assert restored.qualified_name == consumer_group.qualified_name diff --git a/tests_v9/integration/lineage_test.py b/tests_v9/integration/lineage_test.py new file mode 100644 index 000000000..abd4df1a4 --- /dev/null +++ b/tests_v9/integration/lineage_test.py @@ -0,0 +1,744 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2022 Atlan Pte. Ltd. +import time +from typing import Generator + +import pytest + +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.model.assets import ( + Asset, + Column, + ColumnProcess, + Connection, + Database, + MaterialisedView, + Process, + Schema, + Table, + View, +) +from pyatlan_v9.model.enums import ( + AtlanConnectorType, + CertificateStatus, + EntityStatus, + LineageDirection, +) +from pyatlan_v9.model.lineage import FluentLineage +from pyatlan_v9.model.search import DSL, Bool, IndexSearchRequest, Prefix, Term +from tests_v9.integration.client import TestId, delete_asset +from tests_v9.integration.connection_test import create_connection + +MODULE_NAME = TestId.make_unique("lineage") + +DATABASE_NAME = f"{MODULE_NAME}_db" +SCHEMA_NAME = f"{MODULE_NAME}_schema" +TABLE_NAME = f"{MODULE_NAME}_tbl" +MVIEW_NAME = f"{MODULE_NAME}_mv" +VIEW_NAME = f"{MODULE_NAME}_v" +COLUMN_NAME1 = f"{MODULE_NAME}1" +COLUMN_NAME2 = f"{MODULE_NAME}2" +COLUMN_NAME3 = f"{MODULE_NAME}3" +COLUMN_NAME4 = f"{MODULE_NAME}4" +COLUMN_NAME5 = f"{MODULE_NAME}5" +COLUMN_NAME6 = f"{MODULE_NAME}6" + +CONNECTOR_TYPE = AtlanConnectorType.VERTICA +CERTIFICATE_STATUS = CertificateStatus.VERIFIED +CERTIFICATE_MESSAGE = "Automated testing of the Python SDK." + + +@pytest.fixture(scope="module") +def connection(client: AtlanClient) -> Generator[Connection, None, None]: + result = create_connection( + client=client, name=MODULE_NAME, connector_type=CONNECTOR_TYPE + ) + yield result + # TODO: proper connection delete workflow + delete_asset(client, guid=result.guid, asset_type=Connection) + + +@pytest.fixture(scope="module") +def database( + client: AtlanClient, connection: Connection +) -> Generator[Database, None, None]: + db = create_database( + client=client, connection=connection, database_name=DATABASE_NAME + ) + yield db + delete_asset(client, guid=db.guid, asset_type=Database) + + +def create_database(client: AtlanClient, connection, database_name: str): + to_create = Database.creator( + name=database_name, connection_qualified_name=connection.qualified_name + ) + to_create.certificate_status = CERTIFICATE_STATUS + to_create.certificate_status_message = CERTIFICATE_MESSAGE + result = client.asset.save(to_create) + return result.assets_created(asset_type=Database)[0] + + +@pytest.fixture(scope="module") +def schema( + client: AtlanClient, + connection: Connection, + database: Database, +) -> Generator[Schema, None, None]: + assert database.qualified_name + to_create = Schema.creator( + name=SCHEMA_NAME, database_qualified_name=database.qualified_name + ) + result = client.asset.save(to_create) + sch = result.assets_created(asset_type=Schema)[0] + yield sch + delete_asset(client, guid=sch.guid, asset_type=Schema) + + +@pytest.fixture(scope="module") +def table( + client: AtlanClient, + connection: Connection, + database: Database, + schema: Schema, +) -> Generator[Table, None, None]: + assert schema.qualified_name + to_create = Table.creator( + name=TABLE_NAME, schema_qualified_name=schema.qualified_name + ) + result = client.asset.save(to_create) + tbl = result.assets_created(asset_type=Table)[0] + yield tbl + delete_asset(client, guid=tbl.guid, asset_type=Table) + + +@pytest.fixture(scope="module") +def mview( + client: AtlanClient, + connection: Connection, + database: Database, + schema: Schema, +) -> Generator[MaterialisedView, None, None]: + assert schema.qualified_name + to_create = MaterialisedView.creator( + name=MVIEW_NAME, schema_qualified_name=schema.qualified_name + ) + result = client.asset.save(to_create) + mv = result.assets_created(asset_type=MaterialisedView)[0] + yield mv + delete_asset(client, guid=mv.guid, asset_type=MaterialisedView) + + +@pytest.fixture(scope="module") +def view( + client: AtlanClient, + connection: Connection, + database: Database, + schema: Schema, +) -> Generator[View, None, None]: + assert schema.qualified_name + to_create = View.creator( + name=VIEW_NAME, schema_qualified_name=schema.qualified_name + ) + result = client.asset.save(to_create) + v = result.assets_created(asset_type=View)[0] + yield v + delete_asset(client, guid=v.guid, asset_type=View) + + +@pytest.fixture(scope="module") +def column1( + client: AtlanClient, + connection: Connection, + database: Database, + schema: Schema, + table: Table, +) -> Generator[Column, None, None]: + assert table.qualified_name + to_create = Column.creator( + name=COLUMN_NAME1, + parent_type=Table, + parent_qualified_name=table.qualified_name, + order=1, + ) + result = client.asset.save(to_create) + c = result.assets_created(asset_type=Column)[0] + yield c + delete_asset(client, guid=c.guid, asset_type=Column) + + +@pytest.fixture(scope="module") +def column2( + client: AtlanClient, + connection: Connection, + database: Database, + schema: Schema, + table: Table, +) -> Generator[Column, None, None]: + assert table.qualified_name + to_create = Column.creator( + name=COLUMN_NAME2, + parent_type=Table, + parent_qualified_name=table.qualified_name, + order=2, + ) + result = client.asset.save(to_create) + c = result.assets_created(asset_type=Column)[0] + yield c + delete_asset(client, guid=c.guid, asset_type=Column) + + +@pytest.fixture(scope="module") +def column3( + client: AtlanClient, + connection: Connection, + database: Database, + schema: Schema, + mview: MaterialisedView, +) -> Generator[Column, None, None]: + assert mview.qualified_name + to_create = Column.creator( + name=COLUMN_NAME3, + parent_type=MaterialisedView, + parent_qualified_name=mview.qualified_name, + order=1, + ) + result = client.asset.save(to_create) + c = result.assets_created(asset_type=Column)[0] + yield c + delete_asset(client, guid=c.guid, asset_type=Column) + + +@pytest.fixture(scope="module") +def column4( + client: AtlanClient, + connection: Connection, + database: Database, + schema: Schema, + mview: MaterialisedView, +) -> Generator[Column, None, None]: + assert mview.qualified_name + to_create = Column.creator( + name=COLUMN_NAME4, + parent_type=MaterialisedView, + parent_qualified_name=mview.qualified_name, + order=2, + ) + result = client.asset.save(to_create) + c = result.assets_created(asset_type=Column)[0] + yield c + delete_asset(client, guid=c.guid, asset_type=Column) + + +@pytest.fixture(scope="module") +def column5( + client: AtlanClient, + connection: Connection, + database: Database, + schema: Schema, + view: View, +) -> Generator[Column, None, None]: + assert view.qualified_name + to_create = Column.creator( + name=COLUMN_NAME5, + parent_type=View, + parent_qualified_name=view.qualified_name, + order=1, + ) + result = client.asset.save(to_create) + c = result.assets_created(asset_type=Column)[0] + yield c + delete_asset(client, guid=c.guid, asset_type=Column) + + +@pytest.fixture(scope="module") +def column6( + client: AtlanClient, + connection: Connection, + database: Database, + schema: Schema, + view: View, +) -> Generator[Column, None, None]: + assert view.qualified_name + to_create = Column.creator( + name=COLUMN_NAME6, + parent_type=View, + parent_qualified_name=view.qualified_name, + order=2, + ) + result = client.asset.save(to_create) + c = result.assets_created(asset_type=Column)[0] + yield c + delete_asset(client, guid=c.guid, asset_type=Column) + + +@pytest.fixture(scope="module") +def lineage_start( + client: AtlanClient, + connection: Connection, + database: Database, + schema: Schema, + table: Table, + mview: MaterialisedView, + view: View, +) -> Generator[Process, None, None]: + process_name = f"{table.name} >> {mview.name}" + assert connection.qualified_name + to_create = Process.creator( + name=process_name, + connection_qualified_name=connection.qualified_name, + inputs=[Table.ref_by_guid(table.guid)], + outputs=[MaterialisedView.ref_by_guid(mview.guid)], + ) + response = client.asset.save(to_create) + ls = response.assets_created(asset_type=Process)[0] + yield ls + delete_asset(client, guid=ls.guid, asset_type=Process) + + +@pytest.fixture(scope="module") +def cp_lineage_start( + client: AtlanClient, + connection: Connection, + column1: Column, + column3: Column, + lineage_start: Process, +) -> Generator[ColumnProcess, None, None]: + col_process_name = f"{column1.name} >> {column3.name}" + assert connection.qualified_name + to_create = ColumnProcess.creator( + name=col_process_name, + connection_qualified_name=connection.qualified_name, + inputs=[Column.ref_by_guid(column1.guid)], + outputs=[Column.ref_by_guid(column3.guid)], + parent=Process.ref_by_guid(lineage_start.guid), + ) + try: + response = client.asset.save(to_create) + cp_ls = response.assets_created(asset_type=ColumnProcess)[0] + assert len(response.assets_updated(asset_type=Process)) == 1 + assert response.assets_updated(asset_type=Process)[0].guid == lineage_start.guid + yield cp_ls + finally: + delete_asset(client, guid=cp_ls.guid, asset_type=ColumnProcess) + + +@pytest.fixture(scope="module") +def lineage_end( + client: AtlanClient, + connection: Connection, + database: Database, + schema: Schema, + table: Table, + mview: MaterialisedView, + view: View, +) -> Generator[Process, None, None]: + process_name = f"{mview.name} >> {view.name}" + assert connection.qualified_name + to_create = Process.creator( + name=process_name, + connection_qualified_name=connection.qualified_name, + inputs=[MaterialisedView.ref_by_guid(mview.guid)], + outputs=[View.ref_by_guid(view.guid)], + ) + response = client.asset.save(to_create) + ls = response.assets_created(asset_type=Process)[0] + yield ls + delete_asset(client, guid=ls.guid, asset_type=Process) + + +@pytest.fixture(scope="module") +def cp_lineage_end( + client: AtlanClient, + connection: Connection, + column3: Column, + column5: Column, + lineage_end: Process, +) -> Generator[ColumnProcess, None, None]: + col_process_name = f"{column3.name} >> {column5.name}" + assert connection.qualified_name + to_create = ColumnProcess.creator( + name=col_process_name, + connection_qualified_name=connection.qualified_name, + inputs=[Column.ref_by_guid(column3.guid)], + outputs=[Column.ref_by_guid(column5.guid)], + parent=Process.ref_by_guid(lineage_end.guid), + ) + try: + response = client.asset.save(to_create) + cp_le = response.assets_created(asset_type=ColumnProcess)[0] + assert len(response.assets_updated(asset_type=Process)) == 1 + assert response.assets_updated(asset_type=Process)[0].guid == lineage_end.guid + yield cp_le + finally: + delete_asset(client, guid=cp_le.guid, asset_type=ColumnProcess) + + +def _assert_lineage(asset_1, asset_2, lineage): + assert lineage + assert lineage.guid + assert lineage.qualified_name + assert lineage.name == f"{asset_1.name} >> {asset_2.name}" + assert lineage.inputs + assert len(lineage.inputs) == 1 + assert lineage.inputs[0] + assert lineage.inputs[0].type_name == asset_1.__class__.__name__ + assert lineage.inputs[0].guid == asset_1.guid + assert lineage.outputs + assert len(lineage.outputs) == 1 + assert lineage.outputs[0] + assert lineage.outputs[0].type_name == asset_2.__class__.__name__ + assert lineage.outputs[0].guid == asset_2.guid + + +def test_lineage_start( + client: AtlanClient, + connection: Connection, + database: Database, + schema: Schema, + table: Table, + mview: MaterialisedView, + view: View, + lineage_start: Process, +): + _assert_lineage(table, mview, lineage_start) + + +def test_cp_lineage_start( + column1: Column, + column3: Column, + cp_lineage_start: ColumnProcess, +): + _assert_lineage(column1, column3, cp_lineage_start) + + +def test_lineage_end( + client: AtlanClient, + connection: Connection, + database: Database, + schema: Schema, + table: Table, + mview: MaterialisedView, + view: View, + lineage_end: Process, +): + _assert_lineage(mview, view, lineage_end) + + +def test_cp_lineage_end( + column3: Column, + column5: Column, + cp_lineage_end: ColumnProcess, +): + _assert_lineage(column3, column5, cp_lineage_end) + + +def test_fetch_lineage_start_list( + client: AtlanClient, + connection: Connection, + database: Database, + schema: Schema, + table: Table, + mview: MaterialisedView, + view: View, + lineage_start: Process, + lineage_end: Process, +): + lineage = FluentLineage( + starting_guid=table.guid, includes_on_results=Asset.NAME, size=1 + ).request + response = client.asset.get_lineage_list(lineage) + assert response + results = [] + for a in response: + results.append(a) + assert len(results) == 4 + assert isinstance(results[0], Process) + assert results[0].depth == 1 + assert isinstance(results[1], MaterialisedView) + assert results[1].depth == 1 + assert results[1].guid == mview.guid + assert isinstance(results[2], Process) + assert results[2].depth == 2 + assert isinstance(results[3], View) + assert results[3].depth == 2 + assert results[3].guid == view.guid + lineage = FluentLineage( + starting_guid=table.guid, direction=LineageDirection.UPSTREAM + ).request + response = client.asset.get_lineage_list(lineage) + assert response + assert not response.has_more + + +def test_fetch_lineage_start_list_detailed( + client: AtlanClient, + connection: Connection, + database: Database, + schema: Schema, + table: Table, + mview: MaterialisedView, + view: View, + lineage_start: Process, + lineage_end: Process, +): + lineage = FluentLineage( + starting_guid=table.guid, + includes_on_results=Asset.NAME, + immediate_neighbors=True, + ).request + response = client.asset.get_lineage_list(lineage) + assert response + results = [] + for a in response: + results.append(a) + assert len(results) == 5 + assert isinstance(results[0], Table) + assert results[0].depth == 0 + assert results[0].guid == table.guid + assert not results[0].immediate_upstream + assert results[0].immediate_downstream and len(results[0].immediate_downstream) == 1 + assert results[0].immediate_downstream[0].guid == mview.guid + assert isinstance(results[1], Process) + assert results[1].depth == 1 + assert results[1].immediate_upstream == [] + assert results[1].immediate_downstream and len(results[1].immediate_downstream) == 1 + assert results[1].immediate_downstream[0].guid == lineage_end.guid + assert isinstance(results[2], MaterialisedView) + assert results[2].depth == 1 + assert results[2].guid == mview.guid + assert results[2].immediate_upstream and len(results[2].immediate_upstream) == 1 + assert results[2].immediate_upstream[0].guid == table.guid + assert results[2].immediate_downstream and len(results[2].immediate_downstream) == 1 + assert results[2].immediate_downstream[0].guid == view.guid + assert isinstance(results[3], Process) + assert results[3].depth == 2 + assert results[3].immediate_upstream and len(results[3].immediate_upstream) == 1 + assert results[3].immediate_upstream[0].guid == lineage_start.guid + assert results[3].immediate_downstream == [] + assert isinstance(results[4], View) + assert results[4].depth == 2 + assert results[4].guid == view.guid + assert results[4].immediate_upstream and len(results[4].immediate_upstream) == 1 + assert results[4].immediate_upstream[0].guid == mview.guid + assert not results[4].immediate_downstream + lineage = FluentLineage( + starting_guid=table.guid, + direction=LineageDirection.UPSTREAM, + immediate_neighbors=True, + ).request + response = client.asset.get_lineage_list(lineage) + assert response + assert not response.has_more + assets = response.current_page() + assert not assets[0].immediate_upstream + assert not assets[0].immediate_downstream + + +def test_fetch_lineage_middle_list( + client: AtlanClient, + connection: Connection, + database: Database, + schema: Schema, + table: Table, + mview: MaterialisedView, + view: View, + lineage_start: Process, + lineage_end: Process, +): + lineage = FluentLineage( + starting_guid=mview.guid, includes_on_results=Asset.NAME, size=5 + ).request + response = client.asset.get_lineage_list(lineage) + assert response + results = [] + for a in response: + results.append(a) + assert len(results) == 2 + assert isinstance(results[0], Process) + assert isinstance(results[1], View) + assert results[1].guid == view.guid + lineage = FluentLineage( + starting_guid=mview.guid, direction=LineageDirection.UPSTREAM, size=5 + ).request + response = client.asset.get_lineage_list(lineage) + assert response + results = [] + for a in response: + results.append(a) + assert len(results) == 2 + assert isinstance(results[0], Process) + assert isinstance(results[1], Table) + assert results[1].guid == table.guid + + +def test_fetch_lineage_end_list( + client: AtlanClient, + connection: Connection, + database: Database, + schema: Schema, + table: Table, + mview: MaterialisedView, + view: View, + lineage_start: Process, + lineage_end: Process, +): + lineage = FluentLineage( + starting_guid=view.guid, includes_on_results=Asset.NAME, size=10 + ).request + response = client.asset.get_lineage_list(lineage) + assert response + assert not response.has_more + lineage = FluentLineage( + starting_guid=view.guid, direction=LineageDirection.UPSTREAM + ).request + response = client.asset.get_lineage_list(lineage) + assert response + results = [] + for a in response: + results.append(a) + assert len(results) == 4 + assert isinstance(results[0], Process) + assert isinstance(results[1], MaterialisedView) + assert isinstance(results[2], Process) + assert isinstance(results[3], Table) + one = results[3] + assert one.guid == table.guid + + +def test_search_by_lineage( + client: AtlanClient, + connection: Connection, + database: Database, + schema: Schema, + table: Table, + mview: MaterialisedView, + view: View, + lineage_start: Process, + lineage_end: Process, +): + be_active = Term.with_state("ACTIVE") + have_lineage = Term.with_has_lineage(True) + be_a_sql_type = Term.with_super_type_names("SQL") + assert connection.qualified_name + with_qn_prefix = Prefix.with_qualified_name(connection.qualified_name) + query = Bool(must=[be_active, have_lineage, be_a_sql_type, with_qn_prefix]) + dsl = DSL(query=query) + index = IndexSearchRequest( + dsl=dsl, + attributes=["name", "__hasLineage"], + ) + response = client.asset.search(index) + assert response + count = 0 + # TODO: replace with exponential back-off and jitter + while response.count < 3 and count < 10: + time.sleep(2) + response = client.asset.search(index) + count += 1 + assert response + assert response.count == 6 + assets = [] + asset_types = [] + for t in response: + assets.append(t) + asset_types.append(t.type_name) + assert t.has_lineage + assert len(assets) == 6 + assert "Table" in asset_types + assert "MaterialisedView" in asset_types + assert "View" in asset_types + assert "Column" in asset_types + + +@pytest.mark.order( + after=[ + "test_lineage_start", + "test_lineage_end", + "test_fetch_lineage_start_list", + "test_fetch_lineage_middle_list", + "test_fetch_lineage_end_list", + "test_search_by_lineage", + ] +) +def test_delete_lineage( + client: AtlanClient, + connection: Connection, + database: Database, + schema: Schema, + table: Table, + mview: MaterialisedView, + view: View, + lineage_start: Process, + lineage_end: Process, +): + response = client.asset.delete_by_guid(lineage_start.guid) + assert response + deleted = response.assets_deleted(asset_type=Process) + assert len(deleted) == 1 + one = deleted[0] + assert one + assert isinstance(one, Process) + assert one.guid == lineage_start.guid + assert one.qualified_name == lineage_start.qualified_name + assert one.status == EntityStatus.DELETED + + +@pytest.mark.order(after="test_delete_lineage") +def test_restore_lineage( + client: AtlanClient, + connection: Connection, + database: Database, + schema: Schema, + table: Table, + mview: MaterialisedView, + view: View, + lineage_start: Process, + lineage_end: Process, +): + assert lineage_start.qualified_name + assert lineage_start.name + to_restore = Process.create_for_modification( + lineage_start.qualified_name, lineage_start.name + ) + to_restore.status = EntityStatus.ACTIVE + client.asset.save(to_restore) + restored = client.asset.get_by_guid( + lineage_start.guid, asset_type=Process, ignore_relationships=False + ) + assert restored + count = 0 + # TODO: replace with exponential back-off and jitter + while restored.status == EntityStatus.DELETED: + time.sleep(2) + restored = client.asset.get_by_guid( + lineage_start.guid, asset_type=Process, ignore_relationships=False + ) + count += 1 + assert restored.guid == lineage_start.guid + assert restored.qualified_name == lineage_start.qualified_name + assert restored.status == EntityStatus.ACTIVE + + +@pytest.mark.order(after="test_restore_lineage") +def test_purge_lineage( + client: AtlanClient, + connection: Connection, + database: Database, + schema: Schema, + table: Table, + mview: MaterialisedView, + view: View, + lineage_start: Process, + lineage_end: Process, +): + response = client.asset.purge_by_guid(lineage_start.guid) + assert response + purged = response.assets_deleted(asset_type=Process) + assert len(purged) == 1 + one = purged[0] + assert one + assert isinstance(one, Process) + assert one.guid == lineage_start.guid + assert one.qualified_name == lineage_start.qualified_name + assert one.status == EntityStatus.DELETED diff --git a/tests_v9/integration/owner_propagator_cfg.py b/tests_v9/integration/owner_propagator_cfg.py new file mode 100644 index 000000000..2010fd30c --- /dev/null +++ b/tests_v9/integration/owner_propagator_cfg.py @@ -0,0 +1,81 @@ +import json +import logging.config +import os +from pathlib import Path +from typing import Any, List, Optional + +from pydantic.v1 import BaseModel, BaseSettings, Field, parse_obj_as + +from pyatlan_v9.model.assets import Connection +from pyatlan_v9.model.enums import AtlanConnectorType + +PARENT = Path(__file__).parent +LOGGING_CONF = PARENT / "logging.conf" +print("LOGGING_CONF.exists():", LOGGING_CONF.exists()) +if LOGGING_CONF.exists(): + logging.config.fileConfig(LOGGING_CONF) +LOGGER = logging.getLogger(__name__) + +ENV = "env" + + +def validate_multiselect(v, values, **kwargs): + if isinstance(v, str): + if v.startswith("["): + data = json.loads(v) + v = parse_obj_as(List[str], data) + else: + v = [v] + return v + + +def validate_connection(v, values, config, field, **kwargs): + v = Connection.parse_raw(v) + + +class ConnectorAndConnection(BaseModel): + source: AtlanConnectorType + connections: List[str] + + +def validate_connector_and_connection(v, values, config, field, **kwargs): + return ConnectorAndConnection.parse_raw(v) + + +class CustomConfig(BaseModel): + """""" "" + + qn_prefix: str + + +class RuntimeConfig(BaseSettings): + user_id: Optional[str] = Field(default="") + agent: Optional[str] = Field(default="") + agent_id: Optional[str] = Field(default="") + agent_pkg: Optional[str] = Field(default="") + agent_wfl: Optional[str] = Field(default="") + custom_config: Optional[CustomConfig] = None + + class Config: + fields = { + "user_id": { + ENV: "ATLAN_USER_ID", + }, + "agent": {ENV: "X_ATLAN_AGENT"}, + "agent_id": {ENV: "X_ATLAN_AGENT_ID"}, + "agent_pkg": {ENV: "X_ATLAN_AGENT_PACKAGE_NAME"}, + "agent_wfl": {ENV: "X_ATLAN_AGENT_WORKFLOW_ID"}, + "custom_config": {ENV: "NESTED_CONFIG"}, + } + + @classmethod + def parse_env_var(cls, field_name: str, raw_value: str) -> Any: + if field_name == "custom_config": + return CustomConfig.parse_raw(raw_value) + return json.loads(raw_value) + + +if __name__ == "__main__": + LOGGER.info(os.environ["NESTED_CONFIG"]) + r = RuntimeConfig() + LOGGER.info(r.json()) diff --git a/tests_v9/integration/persona_test.py b/tests_v9/integration/persona_test.py new file mode 100644 index 000000000..4b8274b61 --- /dev/null +++ b/tests_v9/integration/persona_test.py @@ -0,0 +1,277 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2022 Atlan Pte. Ltd. +from typing import Generator, Optional + +import msgspec +import pytest + +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.model.assets import ( + AccessControl, + AtlasGlossary, + AuthPolicy, + Connection, + Persona, +) +from pyatlan_v9.model.enums import ( + AssetSidebarTab, + AtlanConnectorType, + AuthPolicyType, + DataAction, + PersonaGlossaryAction, + PersonaMetadataAction, +) +from pyatlan_v9.model.fluent_search import CompoundQuery, FluentSearch +from tests_v9.integration.client import TestId, delete_asset +from tests_v9.integration.connection_test import create_connection +from tests_v9.integration.glossary_test import create_glossary +from tests_v9.integration.utils import find_personas_by_name_with_retry + +MODULE_NAME = TestId.make_unique("Persona") + +CONNECTOR_TYPE = AtlanConnectorType.GCS + + +@pytest.fixture(scope="module") +def connection(client: AtlanClient) -> Generator[Connection, None, None]: + result = create_connection( + client=client, name=MODULE_NAME, connector_type=CONNECTOR_TYPE + ) + yield result + # TODO: proper connection delete workflow + delete_asset(client, guid=result.guid, asset_type=Connection) + + +@pytest.fixture(scope="module") +def glossary( + client: AtlanClient, +) -> Generator[AtlasGlossary, None, None]: + g = create_glossary(client, name=MODULE_NAME) + yield g + delete_asset(client, guid=g.guid, asset_type=AtlasGlossary) + + +@pytest.fixture(scope="module") +def persona( + client: AtlanClient, + connection: Connection, + glossary: AtlasGlossary, +) -> Generator[Persona, None, None]: + to_create = Persona.creator(name=MODULE_NAME) + response = client.asset.save(to_create) + p = response.assets_created(asset_type=Persona)[0] + yield p + delete_asset(client, guid=p.guid, asset_type=Persona) + + +class PolicyInfo(msgspec.Struct, rename="camel"): + guid: Optional[str] = None + name: Optional[str] = None + + +@pytest.fixture(scope="module") +def policy_info() -> PolicyInfo: + return PolicyInfo(guid=None, name=None) + + +def test_persona( + client: AtlanClient, + persona: Persona, + connection: Connection, + glossary: AtlasGlossary, +): + assert persona + assert persona.guid + assert persona.qualified_name + assert persona.name == MODULE_NAME + assert persona.display_name == MODULE_NAME + assert persona.qualified_name != MODULE_NAME + + +@pytest.mark.order(after="test_persona") +def test_update_persona( + client: AtlanClient, + persona: Persona, + connection: Connection, + glossary: AtlasGlossary, +): + assert persona.qualified_name + assert persona.name + to_update = Persona.create_for_modification( + persona.qualified_name, persona.name, True + ) + to_update.description = "Now with a description!" + to_update.deny_asset_tabs = { + AssetSidebarTab.LINEAGE.value, + AssetSidebarTab.RELATIONS.value, + AssetSidebarTab.QUERIES.value, + } + response = client.asset.save(to_update) + assert response + updated = response.assets_updated(asset_type=Persona) + assert updated + assert len(updated) == 1 + assert updated[0] + assert updated[0].guid == persona.guid + assert updated[0].description == "Now with a description!" + assert updated[0].deny_asset_tabs + assert len(updated[0].deny_asset_tabs) == 3 + + +@pytest.mark.order(after="test_update_persona") +def test_find_persona_by_name( + client: AtlanClient, + persona: Persona, + connection: Connection, + glossary: AtlasGlossary, +): + # Use centralized retry utility to handle search index consistency + result = find_personas_by_name_with_retry(client, MODULE_NAME) + assert result + assert len(result) == 1 + assert result[0].guid == persona.guid + + +@pytest.mark.order(after="test_find_persona_by_name") +def test_add_policies_to_persona( + client: AtlanClient, + persona: Persona, + connection: Connection, + glossary: AtlasGlossary, +): + assert connection.qualified_name + metadata = Persona.create_metadata_policy( + name="Simple read access", + persona_id=persona.guid, + policy_type=AuthPolicyType.ALLOW, + actions={PersonaMetadataAction.READ}, + connection_qualified_name=connection.qualified_name, + resources={f"entity:{connection.qualified_name}"}, + ) + assert connection.qualified_name + data = Persona.create_data_policy( + name="Allow access to data", + persona_id=persona.guid, + policy_type=AuthPolicyType.ALLOW, + connection_qualified_name=connection.qualified_name, + resources={f"entity:{connection.qualified_name}"}, + ) + glossary_policy = Persona.create_glossary_policy( + name="All glossaries", + persona_id=persona.guid, + policy_type=AuthPolicyType.ALLOW, + actions={PersonaGlossaryAction.CREATE, PersonaGlossaryAction.UPDATE}, + resources={f"entity:{glossary.qualified_name}"}, + ) + response = client.asset.save([metadata, data, glossary_policy]) + assert response + personas = response.assets_updated(asset_type=Persona) + assert personas + assert len(personas) == 1 + assert personas[0].guid == persona.guid + policies = response.assets_created(asset_type=AuthPolicy) + assert policies + assert len(policies) == 3 + + +@pytest.mark.order(after="test_add_policies_to_persona") +def test_retrieve_persona( + client: AtlanClient, + persona: Persona, + connection: Connection, + glossary: AtlasGlossary, + policy_info: PolicyInfo, +): + assert persona.qualified_name + one = client.asset.get_by_qualified_name( + qualified_name=persona.qualified_name, + asset_type=Persona, + ignore_relationships=False, + ) + assert one + assert one.guid == persona.guid + assert one.description == "Now with a description!" + denied = one.deny_asset_tabs + assert denied + assert len(denied) == 3 + assert AssetSidebarTab.LINEAGE.value in denied + assert AssetSidebarTab.RELATIONS.value in denied + assert AssetSidebarTab.QUERIES.value in denied + policies = one.policies + assert policies + assert len(policies) == 3 + for policy in policies: + # Need to retrieve the full policy if we want to see any info about it + # (what comes back on the Persona itself are just policy references) + full = client.asset.get_by_guid( + guid=policy.guid, asset_type=AuthPolicy, ignore_relationships=False + ) + if policy_info.guid is None and policy_info.name is None: + policy_info.guid = full.guid + policy_info.name = full.name + assert full + sub_cat = full.policy_sub_category + assert sub_cat + assert sub_cat in ["metadata", "data", "glossary"] + assert full.policy_type == AuthPolicyType.ALLOW + if sub_cat == "metadata": + assert full.policy_actions + assert len(full.policy_actions) == 1 + assert PersonaMetadataAction.READ in full.policy_actions + assert full.policy_resources + assert f"entity:{connection.qualified_name}" in full.policy_resources + elif sub_cat == "data": + assert full.policy_actions + assert len(full.policy_actions) == 1 + assert DataAction.SELECT in full.policy_actions + assert full.policy_resources + assert f"entity:{connection.qualified_name}" in full.policy_resources + elif sub_cat == "glossary": + assert full.policy_actions + assert len(full.policy_actions) == 2 + assert PersonaGlossaryAction.CREATE in full.policy_actions + assert PersonaGlossaryAction.UPDATE in full.policy_actions + assert full.policy_resources + assert f"entity:{glossary.qualified_name}" in full.policy_resources + + +@pytest.mark.order(after="test_retrieve_persona") +def test_update_policy( + client: AtlanClient, + policy_info: PolicyInfo, +): + assert policy_info.guid + assert policy_info.name + request = ( + FluentSearch() + .where(FluentSearch.asset_type(AuthPolicy)) + .where(AuthPolicy.POLICY_CATEGORY.eq("persona")) + .where(AuthPolicy.NAME.eq(policy_info.name)) + .where(AuthPolicy.GUID.eq(policy_info.guid)) + .where(CompoundQuery.active_assets()) + .include_on_results(AuthPolicy.POLICY_CATEGORY) + .include_on_results(AuthPolicy.NAME) + .include_on_results(AuthPolicy.POLICY_SERVICE_NAME) + .include_on_results(AuthPolicy.ACCESS_CONTROL) + .include_on_results(AuthPolicy.POLICY_ACTIONS) + .include_on_results(AuthPolicy.POLICY_RESOURCES) + .include_on_results(AuthPolicy.CONNECTION_QUALIFIED_NAME) + .include_on_results(AuthPolicy.POLICY_TYPE) + .include_on_results(AuthPolicy.POLICY_SUB_CATEGORY) + .include_on_relations(AccessControl.IS_ACCESS_CONTROL_ENABLED) + .include_on_relations(AccessControl.NAME) + ).to_request() + to_update = client.asset.search(request) + + assert to_update.count == 1 + policy = to_update.current_page()[0] + assert policy + policy.name = f"Updated policy ({MODULE_NAME})" + + response = client.asset.save(policy) + assert response + updated = response.assets_updated(asset_type=AuthPolicy) + assert updated + assert len(updated) == 1 + assert updated[0].guid == policy.guid + assert updated[0].name == f"Updated policy ({MODULE_NAME})" diff --git a/tests_v9/integration/preset_asset_test.py b/tests_v9/integration/preset_asset_test.py new file mode 100644 index 000000000..d30725ac6 --- /dev/null +++ b/tests_v9/integration/preset_asset_test.py @@ -0,0 +1,416 @@ +from typing import Generator + +import pytest +from msgspec import UNSET + +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.model.assets import ( + Connection, + PresetChart, + PresetDashboard, + PresetDataset, + PresetWorkspace, +) +from pyatlan_v9.model.core import Announcement +from pyatlan_v9.model.enums import ( + AnnouncementType, + AtlanConnectorType, + CertificateStatus, + EntityStatus, +) +from tests_v9.integration.client import TestId, delete_asset +from tests_v9.integration.connection_test import create_connection + +MODULE_NAME = TestId.make_unique("PRESET") + +CONNECTOR_TYPE = AtlanConnectorType.PRESET +PRESET_WORKSPACE_NAME = MODULE_NAME + "-ws" +PRESET_DASHBOARD_NAME = MODULE_NAME + "-coll" +PRESET_DATASET_NAME = MODULE_NAME + "-ds" +PRESET_CHART_NAME = MODULE_NAME + "-cht" +PRESET_DASHBOARD_NAME_OVERLOAD = MODULE_NAME + "-overload-coll" +PRESET_DATASET_NAME_OVERLOAD = MODULE_NAME + "-overload-ds" +PRESET_CHART_NAME_OVERLOAD = MODULE_NAME + "-overload-cht" +CERTIFICATE_STATUS = CertificateStatus.VERIFIED +CERTIFICATE_MESSAGE = "Automated testing of the Python SDK." +ANNOUNCEMENT_TYPE = AnnouncementType.INFORMATION +ANNOUNCEMENT_TITLE = "Python SDK testing." +ANNOUNCEMENT_MESSAGE = "Automated testing of the Python SDK." + + +def _assert_announcement_cleared(updated): + assert updated.announcement_type in (UNSET, None, "") + assert updated.announcement_title in (UNSET, None, "") + assert updated.announcement_message in (UNSET, None, "") + + +@pytest.fixture(scope="module") +def connection(client: AtlanClient) -> Generator[Connection, None, None]: + result = create_connection( + client=client, name=MODULE_NAME, connector_type=CONNECTOR_TYPE + ) + yield result + delete_asset(client, guid=result.guid, asset_type=Connection) + + +@pytest.fixture(scope="module") +def preset_workspace( + client: AtlanClient, connection: Connection +) -> Generator[PresetWorkspace, None, None]: + assert connection.qualified_name + to_create = PresetWorkspace.creator( + name=PRESET_WORKSPACE_NAME, connection_qualified_name=connection.qualified_name + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=PresetWorkspace)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=PresetWorkspace) + + +def test_preset_workspace( + client: AtlanClient, + preset_workspace: PresetWorkspace, + connection: Connection, +): + assert preset_workspace + assert preset_workspace.guid + assert preset_workspace.qualified_name + assert preset_workspace.connection_qualified_name == connection.qualified_name + assert preset_workspace.name == PRESET_WORKSPACE_NAME + assert preset_workspace.connector_name == AtlanConnectorType.PRESET.value + + +@pytest.fixture(scope="module") +def preset_dashboard( + client: AtlanClient, connection: Connection, preset_workspace: PresetWorkspace +) -> Generator[PresetDashboard, None, None]: + assert preset_workspace.qualified_name + to_create = PresetDashboard.creator( + name=PRESET_DASHBOARD_NAME, + preset_workspace_qualified_name=preset_workspace.qualified_name, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=PresetDashboard)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=PresetDashboard) + + +def test_preset_dashboard( + client: AtlanClient, + preset_dashboard: PresetDashboard, + connection: Connection, +): + assert preset_dashboard + assert preset_dashboard.guid + assert preset_dashboard.qualified_name + assert preset_dashboard.connection_qualified_name == connection.qualified_name + assert preset_dashboard.name == PRESET_DASHBOARD_NAME + assert preset_dashboard.connector_name == AtlanConnectorType.PRESET.value + + +@pytest.fixture(scope="module") +def preset_dashboard_overload( + client: AtlanClient, connection: Connection, preset_workspace: PresetWorkspace +) -> Generator[PresetDashboard, None, None]: + assert preset_workspace.qualified_name + assert connection.qualified_name + to_create = PresetDashboard.creator( + name=PRESET_DASHBOARD_NAME_OVERLOAD, + preset_workspace_qualified_name=preset_workspace.qualified_name, + connection_qualified_name=connection.qualified_name, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=PresetDashboard)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=PresetDashboard) + + +def test_overload_preset_dashboard( + client: AtlanClient, + preset_dashboard_overload: PresetDashboard, + connection: Connection, +): + assert preset_dashboard_overload + assert preset_dashboard_overload.guid + assert preset_dashboard_overload.qualified_name + assert ( + preset_dashboard_overload.connection_qualified_name == connection.qualified_name + ) + assert preset_dashboard_overload.name == PRESET_DASHBOARD_NAME_OVERLOAD + assert preset_dashboard_overload.connector_name == AtlanConnectorType.PRESET.value + + +@pytest.fixture(scope="module") +def preset_chart( + client: AtlanClient, preset_dashboard: PresetDashboard +) -> Generator[PresetChart, None, None]: + assert preset_dashboard.qualified_name + to_create = PresetChart.creator( + name=PRESET_CHART_NAME, + preset_dashboard_qualified_name=preset_dashboard.qualified_name, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=PresetChart)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=PresetChart) + + +def test_preset_chart( + client: AtlanClient, + preset_chart: PresetChart, + preset_dashboard: PresetDashboard, +): + assert preset_chart + assert preset_chart.guid + assert preset_chart.qualified_name + assert ( + preset_chart.preset_dashboard_qualified_name == preset_dashboard.qualified_name + ) + assert preset_chart.name == PRESET_CHART_NAME + assert preset_chart.connector_name == AtlanConnectorType.PRESET.value + + +@pytest.fixture(scope="module") +def preset_chart_overload( + client: AtlanClient, + preset_dashboard_overload: PresetDashboard, + connection: Connection, +) -> Generator[PresetChart, None, None]: + assert preset_dashboard_overload.qualified_name + assert connection.qualified_name + to_create = PresetChart.creator( + name=PRESET_CHART_NAME_OVERLOAD, + preset_dashboard_qualified_name=preset_dashboard_overload.qualified_name, + connection_qualified_name=connection.qualified_name, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=PresetChart)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=PresetChart) + + +def test_overload_preset_chart( + client: AtlanClient, + preset_chart_overload: PresetChart, + preset_dashboard_overload: PresetDashboard, +): + assert preset_chart_overload + assert preset_chart_overload.guid + assert preset_chart_overload.qualified_name + assert ( + preset_chart_overload.preset_dashboard_qualified_name + == preset_dashboard_overload.qualified_name + ) + assert preset_chart_overload.name == PRESET_CHART_NAME_OVERLOAD + assert preset_chart_overload.connector_name == AtlanConnectorType.PRESET.value + + +@pytest.fixture(scope="module") +def preset_dataset( + client: AtlanClient, connection: Connection, preset_dashboard: PresetDashboard +) -> Generator[PresetDataset, None, None]: + assert preset_dashboard.qualified_name + to_create = PresetDataset.creator( + name=PRESET_DATASET_NAME, + preset_dashboard_qualified_name=preset_dashboard.qualified_name, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=PresetDataset)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=PresetDataset) + + +def test_preset_dataset( + client: AtlanClient, + preset_dataset: PresetDataset, + connection: Connection, +): + assert preset_dataset + assert preset_dataset.guid + assert preset_dataset.qualified_name + assert preset_dataset.connection_qualified_name == connection.qualified_name + assert preset_dataset.name == PRESET_DATASET_NAME + assert preset_dataset.connector_name == AtlanConnectorType.PRESET.value + + +@pytest.fixture(scope="module") +def preset_dataset_overload( + client: AtlanClient, + connection: Connection, + preset_dashboard_overload: PresetDashboard, +) -> Generator[PresetDataset, None, None]: + assert preset_dashboard_overload.qualified_name + assert connection.qualified_name + to_create = PresetDataset.creator( + name=PRESET_DATASET_NAME_OVERLOAD, + preset_dashboard_qualified_name=preset_dashboard_overload.qualified_name, + connection_qualified_name=connection.qualified_name, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=PresetDataset)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=PresetDataset) + + +def test_overload_preset_dataset( + client: AtlanClient, + preset_dataset_overload: PresetDataset, + connection: Connection, +): + assert preset_dataset_overload + assert preset_dataset_overload.guid + assert preset_dataset_overload.qualified_name + assert ( + preset_dataset_overload.connection_qualified_name == connection.qualified_name + ) + assert preset_dataset_overload.name == PRESET_DATASET_NAME_OVERLOAD + assert preset_dataset_overload.connector_name == AtlanConnectorType.PRESET.value + + +def test_update_preset_dashboard( + client: AtlanClient, + preset_dashboard: PresetDashboard, +): + assert preset_dashboard.qualified_name + assert preset_dashboard.name + updated = client.asset.update_certificate( + asset_type=PresetDashboard, + qualified_name=preset_dashboard.qualified_name, + name=PRESET_DASHBOARD_NAME, + certificate_status=CERTIFICATE_STATUS, + message=CERTIFICATE_MESSAGE, + ) + assert updated + assert updated.certificate_status_message == CERTIFICATE_MESSAGE + assert preset_dashboard.qualified_name + assert preset_dashboard.name + updated = client.asset.update_announcement( + asset_type=PresetDashboard, + qualified_name=preset_dashboard.qualified_name, + name=PRESET_DASHBOARD_NAME, + announcement=Announcement( + announcement_type=ANNOUNCEMENT_TYPE, + announcement_title=ANNOUNCEMENT_TITLE, + announcement_message=ANNOUNCEMENT_MESSAGE, + ), + ) + assert updated + if updated.announcement_type is not UNSET: + assert updated.announcement_type == ANNOUNCEMENT_TYPE.value + assert updated.announcement_title == ANNOUNCEMENT_TITLE + assert updated.announcement_message == ANNOUNCEMENT_MESSAGE + + +def test_update_preset_chart( + client: AtlanClient, + preset_chart: PresetChart, +): + assert preset_chart.qualified_name + assert preset_chart.name + updated = client.asset.update_certificate( + asset_type=PresetChart, + qualified_name=preset_chart.qualified_name, + name=PRESET_CHART_NAME, + certificate_status=CERTIFICATE_STATUS, + message=CERTIFICATE_MESSAGE, + ) + assert updated + assert updated.certificate_status_message == CERTIFICATE_MESSAGE + assert preset_chart.qualified_name + assert preset_chart.name + updated = client.asset.update_announcement( + asset_type=PresetChart, + qualified_name=preset_chart.qualified_name, + name=PRESET_CHART_NAME, + announcement=Announcement( + announcement_type=ANNOUNCEMENT_TYPE, + announcement_title=ANNOUNCEMENT_TITLE, + announcement_message=ANNOUNCEMENT_MESSAGE, + ), + ) + assert updated + if updated.announcement_type is not UNSET: + assert updated.announcement_type == ANNOUNCEMENT_TYPE.value + assert updated.announcement_title == ANNOUNCEMENT_TITLE + assert updated.announcement_message == ANNOUNCEMENT_MESSAGE + + +@pytest.mark.order(after="test_update_preset_dashboard") +def test_retrieve_preset_dashboard( + client: AtlanClient, + preset_dashboard: PresetDashboard, +): + b = client.asset.get_by_guid( + preset_dashboard.guid, asset_type=PresetDashboard, ignore_relationships=False + ) + assert b + assert not b.is_incomplete + assert b.guid == preset_dashboard.guid + assert b.qualified_name == preset_dashboard.qualified_name + assert b.name == PRESET_DASHBOARD_NAME + assert b.connector_name == AtlanConnectorType.PRESET.value + assert b.certificate_status == CERTIFICATE_STATUS + assert b.certificate_status_message == CERTIFICATE_MESSAGE + + +@pytest.mark.order(after="test_retrieve_preset_dashboard") +def test_update_preset_dashboard_again( + client: AtlanClient, + preset_dashboard: PresetDashboard, +): + assert preset_dashboard.qualified_name + assert preset_dashboard.name + updated = client.asset.remove_certificate( + asset_type=PresetDashboard, + qualified_name=preset_dashboard.qualified_name, + name=preset_dashboard.name, + ) + assert updated + assert not updated.certificate_status + assert not updated.certificate_status_message + assert preset_dashboard.qualified_name + updated = client.asset.remove_announcement( + qualified_name=preset_dashboard.qualified_name, + asset_type=PresetDashboard, + name=preset_dashboard.name, + ) + assert updated + _assert_announcement_cleared(updated) + + +@pytest.mark.order(after="test_update_preset_dashboard_again") +def test_delete_preset_dashboard( + client: AtlanClient, preset_dashboard: PresetDashboard +): + response = client.asset.delete_by_guid(preset_dashboard.guid) + assert response + assert not response.assets_created(asset_type=PresetDashboard) + assert not response.assets_updated(asset_type=PresetDashboard) + deleted = response.assets_deleted(asset_type=PresetDashboard) + assert deleted + assert len(deleted) == 1 + assert deleted[0].guid == preset_dashboard.guid + assert deleted[0].qualified_name == preset_dashboard.qualified_name + assert deleted[0].delete_handler == "SOFT" + assert deleted[0].status == EntityStatus.DELETED + + +@pytest.mark.order(after="test_delete_preset_dashboard") +def test_restore_dashboard( + client: AtlanClient, + preset_dashboard: PresetDashboard, +): + assert preset_dashboard.qualified_name + assert client.asset.restore( + asset_type=PresetDashboard, qualified_name=preset_dashboard.qualified_name + ) + assert preset_dashboard.qualified_name + restored = client.asset.get_by_qualified_name( + asset_type=PresetDashboard, + qualified_name=preset_dashboard.qualified_name, + ignore_relationships=False, + ) + assert restored + assert restored.guid == preset_dashboard.guid + assert restored.qualified_name == preset_dashboard.qualified_name + assert restored.status == EntityStatus.ACTIVE diff --git a/tests_v9/integration/purpose_test.py b/tests_v9/integration/purpose_test.py new file mode 100644 index 000000000..11ec2d9be --- /dev/null +++ b/tests_v9/integration/purpose_test.py @@ -0,0 +1,325 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2022 Atlan Pte. Ltd. +import time +from typing import Generator + +import pytest + +from pyatlan_v9.client.atlan import AtlanClient, client_connection +from pyatlan_v9.model.api_tokens import ApiToken +from pyatlan_v9.model.assets import AuthPolicy, Column, Purpose +from pyatlan_v9.model.constants import SERVICE_ACCOUNT_ +from pyatlan_v9.model.core import AtlanTagName +from pyatlan_v9.model.enums import ( + AssetSidebarTab, + AtlanConnectorType, + AuthPolicyType, + DataAction, + DataMaskingType, + HekaFlow, + PurposeMetadataAction, + QueryStatus, +) +from pyatlan_v9.model.query import QueryRequest +from tests_v9.integration.client import TestId, delete_asset +from tests_v9.integration.requests_test import delete_token +from tests_v9.integration.utils import find_purposes_by_name_with_retry + +MODULE_NAME = TestId.make_unique("Purpose") +PERSONA_NAME = "Data Assets" +DB_NAME = "ANALYTICS" +TABLE_NAME = "STG_STATE_PROVINCES" +COLUMN_NAME = "LATEST_RECORDED_POPULATION" +SCHEMA_NAME = "WIDE_WORLD_IMPORTERS" +REDACTED_NUMBER = "0000000" +API_TOKEN_NAME = MODULE_NAME + + +@pytest.fixture(scope="module") +def snowflake_conn(client: AtlanClient): + return client.asset.find_connections_by_name( + "development", AtlanConnectorType.SNOWFLAKE + )[0] + + +@pytest.fixture(scope="module") +def snowflake_column_qn(snowflake_conn): + return f"{snowflake_conn.qualified_name}/{DB_NAME}/{SCHEMA_NAME}/{TABLE_NAME}/{COLUMN_NAME}" + + +@pytest.fixture(scope="module") +def token(token_client: AtlanClient) -> Generator[ApiToken, None, None]: + token = None + try: + token = token_client.token.creator(API_TOKEN_NAME) + assert token + assert token.guid + assert token.display_name + # After creating the token, assign it to the + # "Data Assets" persona to grant it query access + persona = token_client.asset.find_personas_by_name(PERSONA_NAME)[0] + assert persona.qualified_name + token_client.token.updater( + guid=token.guid, + display_name=token.display_name, + personas={persona.qualified_name}, + ) + # Note: need to read the token back again to see + # its associated personas -- will leave that to later... + yield token + finally: + delete_token(token_client, token) + + +@pytest.fixture(scope="module") +def query(snowflake_conn) -> QueryRequest: + # NOTE: This requires pre-existing assets: + # - Snowflake connection called "development" + # with a specific pre-existing table + # - Persona called "Data Assets" with a data policy + # granting query access to the Snowflake table + return QueryRequest( + sql=f'SELECT * FROM "{TABLE_NAME}" LIMIT 50', + data_source_name=snowflake_conn.qualified_name, + default_schema=f"{DB_NAME}.{SCHEMA_NAME}", + ) + + +@pytest.fixture(scope="module") +def atlan_tag_name(make_atlan_tag): + return AtlanTagName(make_atlan_tag(name=MODULE_NAME).display_name) + + +@pytest.fixture(scope="module") +def purpose( + client: AtlanClient, + atlan_tag_name, +) -> Generator[Purpose, None, None]: + to_create = Purpose.creator(name=MODULE_NAME, atlan_tags=[atlan_tag_name]) + response = client.asset.save(to_create) + p = response.assets_created(asset_type=Purpose)[0] + yield p + delete_asset(client, guid=p.guid, asset_type=Purpose) + + +@pytest.fixture(scope="module") +def assign_tag_to_asset(client, snowflake_column_qn): + yield client.asset.add_atlan_tags( + asset_type=Column, + qualified_name=snowflake_column_qn, + atlan_tag_names=[MODULE_NAME], + propagate=False, + remove_propagation_on_delete=False, + restrict_lineage_propagation=False, + ) + client.asset.remove_atlan_tag( + asset_type=Column, + qualified_name=snowflake_column_qn, + atlan_tag_name=MODULE_NAME, + ) + + +def test_query(query): + assert query.sql + assert query.data_source_name + assert query.default_schema + + +def test_purpose(client: AtlanClient, purpose: Purpose, atlan_tag_name: AtlanTagName): + assert purpose + assert purpose.guid + assert purpose.qualified_name + assert purpose.name == MODULE_NAME + assert purpose.display_name == MODULE_NAME + assert purpose.qualified_name != MODULE_NAME + purpose = client.asset.get_by_guid( + guid=purpose.guid, asset_type=Purpose, ignore_relationships=False + ) + assert purpose.purpose_atlan_tags + assert [atlan_tag_name] == purpose.purpose_atlan_tags + + +@pytest.mark.order(after="test_purpose") +def test_update_purpose( + client: AtlanClient, + purpose: Purpose, +): + assert purpose.qualified_name + assert purpose.name + to_update = Purpose.create_for_modification( + purpose.qualified_name, purpose.name, True + ) + to_update.description = "Now with a description!" + to_update.deny_asset_tabs = { + AssetSidebarTab.LINEAGE.value, + AssetSidebarTab.RELATIONS.value, + AssetSidebarTab.QUERIES.value, + } + response = client.asset.save(to_update) + assert response + updated = response.assets_updated(asset_type=Purpose) + assert updated + assert len(updated) == 1 + assert updated[0] + assert updated[0].guid == purpose.guid + assert updated[0].description == "Now with a description!" + assert updated[0].deny_asset_tabs + assert len(updated[0].deny_asset_tabs) == 3 + + +@pytest.mark.order(after="test_update_purpose") +def test_find_purpose_by_name( + client: AtlanClient, + purpose: Purpose, +): + # Use centralized retry utility to handle search index consistency + result = find_purposes_by_name_with_retry( + client, MODULE_NAME, attributes=["purposeClassifications"] + ) + assert result + assert len(result) == 1 + assert result[0].guid == purpose.guid + + +@pytest.mark.order(after="test_find_purpose_by_name") +def test_add_policies_to_purpose( + client: AtlanClient, + purpose: Purpose, + token: ApiToken, +): + metadata = Purpose.create_metadata_policy( + client=client, + name="Simple read access", + purpose_id=purpose.guid, + policy_type=AuthPolicyType.ALLOW, + actions={PurposeMetadataAction.READ}, + all_users=True, + ) + data = Purpose.create_data_policy( + client=client, + name="Mask the data", + purpose_id=purpose.guid, + policy_type=AuthPolicyType.DATA_MASK, + policy_users={f"{SERVICE_ACCOUNT_}{token.client_id}"}, + all_users=False, + ) + data.policy_mask_type = DataMaskingType.REDACT + response = client.asset.save([metadata, data]) + assert response + purposes = response.assets_updated(asset_type=Purpose) + assert purposes + assert len(purposes) == 1 + assert purposes[0].guid == purpose.guid + policies = response.assets_created(asset_type=AuthPolicy) + assert policies + assert len(policies) == 2 + + +@pytest.mark.order(after="test_add_policies_to_purpose") +def test_retrieve_purpose( + client: AtlanClient, + purpose: Purpose, +): + assert purpose.qualified_name + one = client.asset.get_by_qualified_name( + qualified_name=purpose.qualified_name, + asset_type=Purpose, + ignore_relationships=False, + ) + assert one + assert one.guid == purpose.guid + assert one.description == "Now with a description!" + denied = one.deny_asset_tabs + assert denied + assert len(denied) == 3 + assert AssetSidebarTab.LINEAGE.value in denied + assert AssetSidebarTab.RELATIONS.value in denied + assert AssetSidebarTab.QUERIES.value in denied + policies = one.policies + assert policies + assert len(policies) == 2 + + for policy in policies: + # Need to retrieve the full policy if we want to see any info about it + # (what comes back on the Persona itself are just policy references) + full = client.asset.get_by_guid( + guid=policy.guid, asset_type=AuthPolicy, ignore_relationships=False + ) + assert full + sub_cat = full.policy_sub_category + assert sub_cat + assert sub_cat in ["metadata", "data"] + if sub_cat == "metadata": + assert full.policy_actions + assert len(full.policy_actions) == 1 + assert PurposeMetadataAction.READ in full.policy_actions + assert full.policy_type == AuthPolicyType.ALLOW + elif sub_cat == "data": + assert full.policy_actions + assert len(full.policy_actions) == 1 + assert DataAction.SELECT in full.policy_actions + assert full.policy_type == AuthPolicyType.DATA_MASK + assert full.policy_mask_type + assert full.policy_mask_type == DataMaskingType.REDACT + + +@pytest.mark.skip(reason="Test failing with HekaException") +@pytest.mark.order(after="test_retrieve_purpose") +def test_run_query_without_policy(client: AtlanClient, assign_tag_to_asset, query): + response = client.queries.stream(request=query) + assert response + assert response.rows + assert len(response.rows) > 1 + row = response.rows[0] + assert row and len(row) == 10 + # Ensure it is NOT redacted + assert row[6] and row[6] != REDACTED_NUMBER + + +def test_token_permissions(client: AtlanClient, token): + persona = client.asset.find_personas_by_name(PERSONA_NAME)[0] + result = client.token.get_by_name(display_name=API_TOKEN_NAME) + assert result + assert result.attributes + assert result.attributes.persona_qualified_name + assert len(result.attributes.persona_qualified_name) == 1 + assert ( + next(iter(result.attributes.persona_qualified_name)).persona_qualified_name + == persona.qualified_name + ) + + +@pytest.mark.skip(reason="Test failing with HekaException") +@pytest.mark.order(after="test_token_permissions") +def test_run_query_with_policy(assign_tag_to_asset, token, query, client: AtlanClient): + with client_connection( + client=client, api_key=token.attributes.access_token + ) as redacted: + # The policy will take some time to go into effect + # start by waiting a reasonable set amount of time + # (limit the same query re-running multiple times on data store) + time.sleep(30) + count = 0 + response = None + found = HekaFlow.BYPASS + + # TODO: replace with exponential back-off and jitter + while found == HekaFlow.BYPASS and count < 30: + time.sleep(2) + response = redacted.queries.stream(query) + assert response + assert response.details + assert response.details.status + assert response.details.heka_flow + status = response.details.status + if status != QueryStatus.ERROR: + found = response.details.heka_flow + count += 1 + + assert response + assert response.rows + assert len(response.rows) > 1 + row = response.rows[0] + assert row and len(row) == 10 + # Ensure it IS redacted + assert row[6] and row[6] == REDACTED_NUMBER diff --git a/tests_v9/integration/query_parser_test.py b/tests_v9/integration/query_parser_test.py new file mode 100644 index 000000000..acf073a1a --- /dev/null +++ b/tests_v9/integration/query_parser_test.py @@ -0,0 +1,38 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2022 Atlan Pte. Ltd. +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.model.enums import QueryParserSourceType +from pyatlan_v9.model.query import QueryParserRequest + + +def test_parse_valid_query(client: AtlanClient): + request = QueryParserRequest.creator( + sql="INSERT INTO orders (order_name, customer_id, product_id)" + " VALUES(SELECT 'test_order', id, 21 FROM customers)", + source=QueryParserSourceType.SNOWFLAKE, + ) + request.default_database = "ORDERS" + request.default_schema = "PRODUCTION" + response = client.parse_query(request) + assert response + assert response.dbobjs + assert response.relationships + assert not response.errors + + +def test_parse_invalid_query(client: AtlanClient): + request = QueryParserRequest.creator( + sql="INSERT INTO orders (order_name, customer_id, product_id)" + " VALUES(SELECT 'test_order', id, 21 FROM customers)" + " with some extra", + source=QueryParserSourceType.SNOWFLAKE, + ) + request.default_database = "ORDERS" + request.default_schema = "PRODUCTION" + response = client.parse_query(request) + assert response + assert not response.dbobjs + assert not response.relationships + assert response.errors + assert len(response.errors) == 2 + assert response.errors[0].error_type == "SyntaxError" diff --git a/tests_v9/integration/quick_sight_asset_test.py b/tests_v9/integration/quick_sight_asset_test.py new file mode 100644 index 000000000..1febf19ff --- /dev/null +++ b/tests_v9/integration/quick_sight_asset_test.py @@ -0,0 +1,440 @@ +from typing import Generator + +import pytest + +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.model.assets import ( + Connection, + QuickSightAnalysis, + QuickSightAnalysisVisual, + QuickSightDashboard, + QuickSightDashboardVisual, + QuickSightDataset, + QuickSightDatasetField, + QuickSightFolder, +) +from pyatlan_v9.model.enums import ( + AtlanConnectorType, + QuickSightDatasetFieldType, + QuickSightDatasetImportMode, + QuickSightFolderType, +) +from tests_v9.integration.client import TestId, delete_asset +from tests_v9.integration.connection_test import create_connection + +MODULE_NAME = TestId.make_unique("QUICKSIGHT") + +CONNECTOR_TYPE = AtlanConnectorType.QUICKSIGHT +QUICKSIGHT_FOLDER_NAME = f"{MODULE_NAME}-QUICKSIGHT-FOLDER" +QUICKSIGHT_FOLDER_ID = f"{MODULE_NAME}-FOLDER-ID" +QUICKSIGHT_DATASET_NAME = f"{MODULE_NAME}-QUICKSIGHT-DATASET" +QUICKSIGHT_DATASET_ID = f"{MODULE_NAME}-DATASET-ID" +QUICKSIGHT_DASHBOARD_NAME = f"{MODULE_NAME}-QUICKSIGHT-DASHBOARD" +QUICKSIGHT_DASHBOARD_ID = f"{MODULE_NAME}-DASHBOARD-ID" +QUICKSIGHT_ANALYSIS_NAME = f"{MODULE_NAME}-QUICKSIGHT-ANALYSIS" +QUICKSIGHT_ANALYSIS_ID = f"{MODULE_NAME}-ANALYSIS-ID" +QUICKSIGHT_DATASET_FIELD_NAME = f"{MODULE_NAME}-QUICKSIGHT-DATASET-FIELD" +QUICKSIGHT_DATASET_FIELD_ID = f"{MODULE_NAME}-DATASET-FIELD-ID" +QUICKSIGHT_DASHBOARD_VISUAL_NAME = f"{MODULE_NAME}-QUICKSIGHT-DASHBOARD-VISUAL" +QUICKSIGHT_DASHBOARD_VISUAL_ID = f"{MODULE_NAME}-DASHBOARD-VISUAL-ID" +QUICKSIGHT_ANALYSIS_VISUAL_NAME = f"{MODULE_NAME}-QUICKSIGHT-ANALYSIS-VISUAL" +QUICKSIGHT_ANALYSIS_VISUAL_ID = f"{MODULE_NAME}-ANALYSIS-VISUAL-ID" +QUICKSIGHT_SHEET_NAME = f"{MODULE_NAME}-QUICKSIGHT-SHEET-NAME" +QUICKSIGHT_SHEET_ID = f"{MODULE_NAME}-SHEET-ID" +QUICK_SIGHT_DESCRIPTION = "Automated testing of the Python SDK." + + +@pytest.fixture(scope="module") +def connection(client: AtlanClient) -> Generator[Connection, None, None]: + result = create_connection( + client=client, name=MODULE_NAME, connector_type=CONNECTOR_TYPE + ) + yield result + delete_asset(client, guid=result.guid, asset_type=Connection) + + +@pytest.fixture(scope="module") +def quick_sight_folder( + client: AtlanClient, connection: Connection +) -> Generator[QuickSightFolder, None, None]: + assert connection.qualified_name + to_create = QuickSightFolder.creator( + name=QUICKSIGHT_FOLDER_NAME, + connection_qualified_name=connection.qualified_name, + quick_sight_id=QUICKSIGHT_FOLDER_ID, + quick_sight_folder_type=QuickSightFolderType.SHARED, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=QuickSightFolder)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=QuickSightFolder) + + +@pytest.fixture(scope="module") +def quick_sight_dataset( + client: AtlanClient, connection: Connection, quick_sight_folder: QuickSightFolder +) -> Generator[QuickSightDataset, None, None]: + assert connection.qualified_name + assert quick_sight_folder.qualified_name + to_create = QuickSightDataset.creator( + name=QUICKSIGHT_DATASET_NAME, + connection_qualified_name=connection.qualified_name, + quick_sight_id=QUICKSIGHT_DATASET_ID, + quick_sight_dataset_import_mode=QuickSightDatasetImportMode.SPICE, + quick_sight_dataset_folders=[str(quick_sight_folder.qualified_name)], + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=QuickSightDataset)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=QuickSightDataset) + + +@pytest.fixture(scope="module") +def quick_sight_dashboard( + client: AtlanClient, connection: Connection, quick_sight_folder: QuickSightFolder +) -> Generator[QuickSightDashboard, None, None]: + assert connection.qualified_name + assert quick_sight_folder.qualified_name + to_create = QuickSightDashboard.creator( + name=QUICKSIGHT_DASHBOARD_NAME, + connection_qualified_name=connection.qualified_name, + quick_sight_id=QUICKSIGHT_DASHBOARD_ID, + quick_sight_dashboard_folders=[str(quick_sight_folder.qualified_name)], + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=QuickSightDashboard)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=QuickSightDashboard) + + +@pytest.fixture(scope="module") +def quick_sight_analysis( + client: AtlanClient, connection: Connection, quick_sight_folder: QuickSightFolder +) -> Generator[QuickSightAnalysis, None, None]: + assert connection.qualified_name + assert quick_sight_folder.qualified_name + to_create = QuickSightAnalysis.creator( + name=QUICKSIGHT_ANALYSIS_NAME, + connection_qualified_name=connection.qualified_name, + quick_sight_id=QUICKSIGHT_ANALYSIS_ID, + quick_sight_analysis_folders=[str(quick_sight_folder.qualified_name)], + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=QuickSightAnalysis)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=QuickSightAnalysis) + + +@pytest.fixture(scope="module") +def quick_sight_dataset_field( + client: AtlanClient, + connection: Connection, + quick_sight_dataset: QuickSightDataset, +) -> Generator[QuickSightDatasetField, None, None]: + assert connection.qualified_name + to_create = QuickSightDatasetField.creator( + name=QUICKSIGHT_DATASET_FIELD_NAME, + quick_sight_dataset_qualified_name=str(quick_sight_dataset.qualified_name), + connection_qualified_name=connection.qualified_name, + quick_sight_id=QUICKSIGHT_DATASET_FIELD_ID, + quick_sight_dataset_field_type=QuickSightDatasetFieldType.STRING, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=QuickSightDatasetField)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=QuickSightDatasetField) + + +@pytest.fixture(scope="module") +def quick_sight_analysis_visual( + client: AtlanClient, + connection: Connection, + quick_sight_analysis: QuickSightAnalysis, +) -> Generator[QuickSightAnalysisVisual, None, None]: + assert connection.qualified_name + to_create = QuickSightAnalysisVisual.creator( + name=QUICKSIGHT_ANALYSIS_VISUAL_NAME, + quick_sight_sheet_id=QUICKSIGHT_SHEET_ID, + quick_sight_sheet_name=QUICKSIGHT_SHEET_NAME, + quick_sight_analysis_qualified_name=str(quick_sight_analysis.qualified_name), + connection_qualified_name=connection.qualified_name, + quick_sight_id=QUICKSIGHT_ANALYSIS_VISUAL_ID, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=QuickSightAnalysisVisual)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=QuickSightAnalysisVisual) + + +@pytest.fixture(scope="module") +def quick_sight_dashboard_visual( + client: AtlanClient, + connection: Connection, + quick_sight_dashboard: QuickSightDashboard, +) -> Generator[QuickSightDashboardVisual, None, None]: + assert connection.qualified_name + to_create = QuickSightDashboardVisual.creator( + name=QUICKSIGHT_DASHBOARD_VISUAL_NAME, + quick_sight_sheet_id=QUICKSIGHT_SHEET_ID, + quick_sight_sheet_name=QUICKSIGHT_SHEET_NAME, + quick_sight_dashboard_qualified_name=str(quick_sight_dashboard.qualified_name), + connection_qualified_name=connection.qualified_name, + quick_sight_id=QUICKSIGHT_DASHBOARD_VISUAL_ID, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=QuickSightDashboardVisual)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=QuickSightDashboardVisual) + + +def test_sight_folder( + client: AtlanClient, connection: Connection, quick_sight_folder: QuickSightFolder +): + assert quick_sight_folder + assert quick_sight_folder.guid + assert quick_sight_folder.qualified_name + assert quick_sight_folder.name == QUICKSIGHT_FOLDER_NAME + assert quick_sight_folder.quick_sight_id == QUICKSIGHT_FOLDER_ID + assert quick_sight_folder.connection_qualified_name == connection.qualified_name + assert quick_sight_folder.connector_name == AtlanConnectorType.QUICKSIGHT.value + assert quick_sight_folder.quick_sight_folder_type == QuickSightFolderType.SHARED + + to_update = quick_sight_folder.updater( + name=quick_sight_folder.name, qualified_name=quick_sight_folder.qualified_name + ) + to_update.description = QUICK_SIGHT_DESCRIPTION + response = client.asset.save(to_update) + assert response and response.mutated_entities + + asset = client.asset.get_by_qualified_name( + qualified_name=quick_sight_folder.qualified_name, asset_type=QuickSightFolder + ) + assert asset + assert asset.name == QUICKSIGHT_FOLDER_NAME + assert asset.description == QUICK_SIGHT_DESCRIPTION + assert asset.qualified_name == quick_sight_folder.qualified_name + + +def test_sight_dataset( + client: AtlanClient, connection: Connection, quick_sight_dataset: QuickSightDataset +): + assert quick_sight_dataset + assert quick_sight_dataset.guid + assert quick_sight_dataset.qualified_name + assert quick_sight_dataset.name == QUICKSIGHT_DATASET_NAME + assert quick_sight_dataset.quick_sight_id == QUICKSIGHT_DATASET_ID + assert quick_sight_dataset.connection_qualified_name == connection.qualified_name + assert quick_sight_dataset.connector_name == AtlanConnectorType.QUICKSIGHT.value + assert ( + quick_sight_dataset.quick_sight_dataset_import_mode + == QuickSightDatasetImportMode.SPICE + ) + + to_update = quick_sight_dataset.updater( + name=quick_sight_dataset.name, qualified_name=quick_sight_dataset.qualified_name + ) + to_update.description = QUICK_SIGHT_DESCRIPTION + response = client.asset.save(to_update) + assert response and response.mutated_entities + + asset = client.asset.get_by_qualified_name( + qualified_name=quick_sight_dataset.qualified_name, asset_type=QuickSightDataset + ) + assert asset + assert asset.name == QUICKSIGHT_DATASET_NAME + assert asset.description == QUICK_SIGHT_DESCRIPTION + assert asset.qualified_name == quick_sight_dataset.qualified_name + + +def test_sight_dashboard( + client: AtlanClient, + connection: Connection, + quick_sight_dashboard: QuickSightDashboard, +): + assert quick_sight_dashboard + assert quick_sight_dashboard.guid + assert quick_sight_dashboard.qualified_name + assert quick_sight_dashboard.name == QUICKSIGHT_DASHBOARD_NAME + assert quick_sight_dashboard.quick_sight_id == QUICKSIGHT_DASHBOARD_ID + assert quick_sight_dashboard.connection_qualified_name == connection.qualified_name + assert quick_sight_dashboard.connector_name == AtlanConnectorType.QUICKSIGHT.value + + to_update = quick_sight_dashboard.updater( + name=quick_sight_dashboard.name, + qualified_name=quick_sight_dashboard.qualified_name, + ) + to_update.description = QUICK_SIGHT_DESCRIPTION + response = client.asset.save(to_update) + assert response and response.mutated_entities + + asset = client.asset.get_by_qualified_name( + qualified_name=quick_sight_dashboard.qualified_name, + asset_type=QuickSightDashboard, + ) + assert asset + assert asset.name == QUICKSIGHT_DASHBOARD_NAME + assert asset.description == QUICK_SIGHT_DESCRIPTION + assert asset.qualified_name == quick_sight_dashboard.qualified_name + + +def test_sight_analysis( + client: AtlanClient, + connection: Connection, + quick_sight_analysis: QuickSightAnalysis, +): + assert quick_sight_analysis + assert quick_sight_analysis.guid + assert quick_sight_analysis.qualified_name + assert quick_sight_analysis.name == QUICKSIGHT_ANALYSIS_NAME + assert quick_sight_analysis.quick_sight_id == QUICKSIGHT_ANALYSIS_ID + assert quick_sight_analysis.connection_qualified_name == connection.qualified_name + assert quick_sight_analysis.connector_name == AtlanConnectorType.QUICKSIGHT.value + + to_update = quick_sight_analysis.updater( + name=quick_sight_analysis.name, + qualified_name=quick_sight_analysis.qualified_name, + ) + to_update.description = QUICK_SIGHT_DESCRIPTION + response = client.asset.save(to_update) + assert response and response.mutated_entities + + asset = client.asset.get_by_qualified_name( + qualified_name=quick_sight_analysis.qualified_name, + asset_type=QuickSightAnalysis, + ) + assert asset + assert asset.name == QUICKSIGHT_ANALYSIS_NAME + assert asset.description == QUICK_SIGHT_DESCRIPTION + assert asset.qualified_name == quick_sight_analysis.qualified_name + + +def test_sight_dataset_field( + client: AtlanClient, + connection: Connection, + quick_sight_dataset_field: QuickSightDatasetField, + quick_sight_dataset: QuickSightDataset, +): + assert quick_sight_dataset_field + assert quick_sight_dataset_field.guid + assert quick_sight_dataset_field.qualified_name + assert quick_sight_dataset_field.name == QUICKSIGHT_DATASET_FIELD_NAME + assert quick_sight_dataset_field.quick_sight_id == QUICKSIGHT_DATASET_FIELD_ID + assert ( + quick_sight_dataset_field.connection_qualified_name == connection.qualified_name + ) + assert ( + quick_sight_dataset_field.connector_name == AtlanConnectorType.QUICKSIGHT.value + ) + assert ( + quick_sight_dataset_field.quick_sight_dataset_qualified_name + == quick_sight_dataset.qualified_name + ) + assert ( + quick_sight_dataset_field.quick_sight_dataset_field_type + == QuickSightDatasetFieldType.STRING + ) + + to_update = quick_sight_dataset_field.updater( + name=quick_sight_dataset_field.name, + qualified_name=quick_sight_dataset_field.qualified_name, + ) + to_update.description = QUICK_SIGHT_DESCRIPTION + response = client.asset.save(to_update) + assert response and response.mutated_entities + + asset = client.asset.get_by_qualified_name( + qualified_name=quick_sight_dataset_field.qualified_name, + asset_type=QuickSightDatasetField, + ) + assert asset + assert asset.name == QUICKSIGHT_DATASET_FIELD_NAME + assert asset.description == QUICK_SIGHT_DESCRIPTION + assert asset.qualified_name == quick_sight_dataset_field.qualified_name + + +def test_sight_analysis_visual( + client: AtlanClient, + connection: Connection, + quick_sight_analysis_visual: QuickSightAnalysisVisual, + quick_sight_analysis: QuickSightAnalysis, +): + assert quick_sight_analysis_visual + assert quick_sight_analysis_visual.guid + assert quick_sight_analysis_visual.qualified_name + assert quick_sight_analysis_visual.name == QUICKSIGHT_ANALYSIS_VISUAL_NAME + assert quick_sight_analysis_visual.quick_sight_id == QUICKSIGHT_ANALYSIS_VISUAL_ID + assert ( + quick_sight_analysis_visual.connection_qualified_name + == connection.qualified_name + ) + assert ( + quick_sight_analysis_visual.connector_name + == AtlanConnectorType.QUICKSIGHT.value + ) + assert ( + quick_sight_analysis_visual.quick_sight_analysis_qualified_name + == quick_sight_analysis.qualified_name + ) + assert quick_sight_analysis_visual.quick_sight_sheet_id == QUICKSIGHT_SHEET_ID + assert quick_sight_analysis_visual.quick_sight_sheet_name == QUICKSIGHT_SHEET_NAME + + to_update = quick_sight_analysis_visual.updater( + name=quick_sight_analysis_visual.name, + qualified_name=quick_sight_analysis_visual.qualified_name, + ) + to_update.description = QUICK_SIGHT_DESCRIPTION + response = client.asset.save(to_update) + assert response and response.mutated_entities + + asset = client.asset.get_by_qualified_name( + qualified_name=quick_sight_analysis_visual.qualified_name, + asset_type=QuickSightAnalysisVisual, + ) + assert asset + assert asset.name == QUICKSIGHT_ANALYSIS_VISUAL_NAME + assert asset.description == QUICK_SIGHT_DESCRIPTION + assert asset.qualified_name == quick_sight_analysis_visual.qualified_name + + +def test_sight_dashboard_visual( + client: AtlanClient, + connection: Connection, + quick_sight_dashboard_visual: QuickSightDashboardVisual, + quick_sight_dashboard: QuickSightDashboard, +): + assert quick_sight_dashboard_visual + assert quick_sight_dashboard_visual.guid + assert quick_sight_dashboard_visual.qualified_name + assert quick_sight_dashboard_visual.name == QUICKSIGHT_DASHBOARD_VISUAL_NAME + assert quick_sight_dashboard_visual.quick_sight_id == QUICKSIGHT_DASHBOARD_VISUAL_ID + assert ( + quick_sight_dashboard_visual.connection_qualified_name + == connection.qualified_name + ) + assert ( + quick_sight_dashboard_visual.connector_name + == AtlanConnectorType.QUICKSIGHT.value + ) + assert ( + quick_sight_dashboard_visual.quick_sight_dashboard_qualified_name + == quick_sight_dashboard.qualified_name + ) + assert quick_sight_dashboard_visual.quick_sight_sheet_id == QUICKSIGHT_SHEET_ID + assert quick_sight_dashboard_visual.quick_sight_sheet_name == QUICKSIGHT_SHEET_NAME + + to_update = quick_sight_dashboard_visual.updater( + name=quick_sight_dashboard_visual.name, + qualified_name=quick_sight_dashboard_visual.qualified_name, + ) + to_update.description = QUICK_SIGHT_DESCRIPTION + response = client.asset.save(to_update) + assert response and response.mutated_entities + + asset = client.asset.get_by_qualified_name( + qualified_name=quick_sight_dashboard_visual.qualified_name, + asset_type=QuickSightDashboardVisual, + ) + assert asset + assert asset.name == QUICKSIGHT_DASHBOARD_VISUAL_NAME + assert asset.description == QUICK_SIGHT_DESCRIPTION + assert asset.qualified_name == quick_sight_dashboard_visual.qualified_name diff --git a/tests_v9/integration/requests_test.py b/tests_v9/integration/requests_test.py new file mode 100644 index 000000000..1dc77468b --- /dev/null +++ b/tests_v9/integration/requests_test.py @@ -0,0 +1,69 @@ +from typing import Generator, Optional + +import pytest + +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.model.api_tokens import ApiToken +from tests_v9.integration.client import TestId + +MODULE_NAME = TestId.make_unique("Requests") +API_TOKEN_NAME = f"{MODULE_NAME}" + + +def create_token(token_client: AtlanClient, name: str) -> ApiToken: + t = token_client.token.creator(name) + return t + + +def delete_token(token_client: AtlanClient, token: Optional[ApiToken] = None): + # If there is a partial failure on the server side + # and the token is still visible in the Atlan UI, + # in that case, the create method may not return a token. + # We should retrieve the list of all tokens and delete them here. + if not token: + tokens = token_client.token.get().records + assert tokens + delete_tokens = [ + token + for token in tokens + if token.display_name and "psdkv9_Requests" in token.display_name + ] + for token in delete_tokens: + assert token and token.guid + token_client.token.purge(token.guid) + return + # In case of no partial failure, directly delete the token + token.guid and token_client.token.purge(token.guid) + + +@pytest.fixture(scope="module") +def token(token_client: AtlanClient) -> Generator[ApiToken, None, None]: + token = None + try: + token = create_token(token_client, API_TOKEN_NAME) + yield token + finally: + delete_token(token_client, token) + + +def test_create_token(client: AtlanClient, token: ApiToken): + assert token + r = client.token.get_by_name(API_TOKEN_NAME) + assert r + assert r.display_name == API_TOKEN_NAME + r = client.token.get_by_id(str(token.client_id)) + assert r + assert r.client_id == token.client_id + assert r.display_name == token.display_name + + +@pytest.mark.order(after="test_create_token") +def test_update_token(client: AtlanClient, token: ApiToken): + description = "Now with a revised description." + revised = client.token.updater( + str(token.guid), str(token.display_name), description + ) + assert revised + assert revised.attributes + assert revised.attributes.description == description + assert revised.display_name == token.display_name diff --git a/tests_v9/integration/s3_asset_test.py b/tests_v9/integration/s3_asset_test.py new file mode 100644 index 000000000..69debb438 --- /dev/null +++ b/tests_v9/integration/s3_asset_test.py @@ -0,0 +1,421 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2022 Atlan Pte. Ltd. +from typing import Generator + +import pytest + +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.model.assets import Connection, S3Bucket, S3Object +from pyatlan_v9.model.core import Announcement +from pyatlan_v9.model.enums import ( + AnnouncementType, + AtlanConnectorType, + CertificateStatus, + EntityStatus, +) +from tests_v9.integration.client import TestId, delete_asset +from tests_v9.integration.connection_test import create_connection + +MODULE_NAME = TestId.make_unique("S3") + +CONNECTOR_TYPE = AtlanConnectorType.S3 +ARN = "arn:aws:s3:::" +BUCKET_NAME = MODULE_NAME +BUCKET_ARN = f"{ARN}{MODULE_NAME}" +OBJECT_NAME = f"myobject_{MODULE_NAME}.csv" +OBJECT_ARN = f"{ARN}{BUCKET_NAME}/prefix/{OBJECT_NAME}" +OBJECT_PREFIX = "/some/folder/structure" +CERTIFICATE_STATUS = CertificateStatus.VERIFIED +CERTIFICATE_MESSAGE = "Automated testing of the Python SDK." +ANNOUNCEMENT_TYPE = AnnouncementType.INFORMATION +ANNOUNCEMENT_TITLE = "Python SDK testing." +ANNOUNCEMENT_MESSAGE = "Automated testing of the Python SDK." + + +@pytest.fixture(scope="module") +def connection(client: AtlanClient) -> Generator[Connection, None, None]: + result = create_connection( + client=client, name=MODULE_NAME, connector_type=CONNECTOR_TYPE + ) + yield result + # TODO: proper connection delete workflow + delete_asset(client, guid=result.guid, asset_type=Connection) + + +@pytest.fixture(scope="module") +def bucket( + client: AtlanClient, connection: Connection +) -> Generator[S3Bucket, None, None]: + assert connection.qualified_name + to_create = S3Bucket.creator( + name=BUCKET_NAME, + connection_qualified_name=connection.qualified_name, + aws_arn=BUCKET_ARN, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=S3Bucket)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=S3Bucket) + + +@pytest.fixture(scope="module") +def bucket_with_name( + client: AtlanClient, connection: Connection +) -> Generator[S3Bucket, None, None]: + assert connection.qualified_name + to_create = S3Bucket.creator( + name=BUCKET_NAME, + connection_qualified_name=connection.qualified_name, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=S3Bucket)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=S3Bucket) + + +@pytest.fixture(scope="module") +def s3object( + client: AtlanClient, + connection: Connection, + bucket: S3Bucket, +) -> Generator[S3Object, None, None]: + assert connection.qualified_name + assert bucket.qualified_name + to_create = S3Object.creator( + name=OBJECT_NAME, + connection_qualified_name=connection.qualified_name, + aws_arn=OBJECT_ARN, + s3_bucket_name=bucket.name, + s3_bucket_qualified_name=bucket.qualified_name, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=S3Object)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=S3Object) + + +@pytest.fixture(scope="module") +def s3object_with_name( + client: AtlanClient, + connection: Connection, + bucket_with_name: S3Bucket, +) -> Generator[S3Object, None, None]: + assert connection.qualified_name + assert bucket_with_name.qualified_name + to_create = S3Object.create_with_prefix( + name=OBJECT_NAME, + connection_qualified_name=connection.qualified_name, + prefix=OBJECT_PREFIX, + s3_bucket_name=bucket_with_name.name, + s3_bucket_qualified_name=bucket_with_name.qualified_name, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=S3Object)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=S3Object) + + +def _assert_bucket(bucket, with_name=False): + assert bucket + assert bucket.guid + assert bucket.qualified_name + assert bucket.name == BUCKET_NAME + assert bucket.connector_name == AtlanConnectorType.S3.value + if with_name: + assert not bucket.aws_arn + else: + assert bucket.aws_arn == BUCKET_ARN + + +def _assert_object(s3object, bucket, with_name=False): + assert s3object + assert s3object.guid + assert s3object.qualified_name + assert s3object.name == OBJECT_NAME + assert s3object.connector_name == AtlanConnectorType.S3.value + assert s3object.s3_bucket_name == bucket.name + assert s3object.s3_bucket_qualified_name == bucket.qualified_name + if with_name: + assert not s3object.aws_arn + assert s3object.s3_object_key == f"{OBJECT_PREFIX}/{OBJECT_NAME}" + else: + assert s3object.aws_arn == OBJECT_ARN + assert not s3object.s3_object_key + + +def _assert_update_bucket(client, bucket, with_name=False): + assert bucket.qualified_name + assert bucket.name + updated = client.asset.update_certificate( + asset_type=S3Bucket, + qualified_name=bucket.qualified_name, + name=bucket.name, + certificate_status=CERTIFICATE_STATUS, + message=CERTIFICATE_MESSAGE, + ) + assert updated + assert updated.certificate_status == CERTIFICATE_STATUS + assert updated.certificate_status_message == CERTIFICATE_MESSAGE + assert bucket.qualified_name + assert bucket.name + updated = client.asset.update_announcement( + asset_type=S3Bucket, + qualified_name=bucket.qualified_name, + name=bucket.name, + announcement=Announcement( + announcement_type=ANNOUNCEMENT_TYPE, + announcement_title=ANNOUNCEMENT_TITLE, + announcement_message=ANNOUNCEMENT_MESSAGE, + ), + ) + assert updated + assert updated.announcement_type == ANNOUNCEMENT_TYPE.value + assert updated.announcement_title == ANNOUNCEMENT_TITLE + assert updated.announcement_message == ANNOUNCEMENT_MESSAGE + if with_name: + assert not bucket.aws_arn + else: + assert bucket.aws_arn == BUCKET_ARN + + +def _assert_retrieve_bucket(client, bucket, s3object, with_name=False): + b = client.asset.get_by_guid( + bucket.guid, asset_type=S3Bucket, ignore_relationships=False + ) + assert b + assert not b.is_incomplete + assert b.guid == bucket.guid + assert b.qualified_name == bucket.qualified_name + assert b.name == BUCKET_NAME + assert b.certificate_status == CERTIFICATE_STATUS + assert b.certificate_status_message == CERTIFICATE_MESSAGE + assert b.objects + assert len(b.objects) == 1 + assert isinstance(b.objects[0], S3Object) + assert b.objects[0].guid == s3object.guid + if with_name: + assert not b.aws_arn + else: + assert b.aws_arn == BUCKET_ARN + + +def _assert_update_bucket_again(client, bucket, with_name=False): + assert bucket.qualified_name + assert bucket.name + updated = client.asset.remove_certificate( + qualified_name=bucket.qualified_name, + asset_type=S3Bucket, + name=bucket.name, + ) + assert updated + assert not updated.certificate_status + assert not updated.certificate_status_message + assert bucket.qualified_name + updated = client.asset.remove_announcement( + qualified_name=bucket.qualified_name, + asset_type=S3Bucket, + name=bucket.name, + ) + assert updated + assert not updated.announcement_type + assert not updated.announcement_title + assert not updated.announcement_message + if with_name: + assert not bucket.aws_arn + else: + assert bucket.aws_arn == BUCKET_ARN + + +def _assert_delete_object(client, s3object): + response = client.asset.delete_by_guid(s3object.guid) + assert response + assert not response.assets_created(asset_type=S3Object) + assert not response.assets_updated(asset_type=S3Object) + deleted = response.assets_deleted(asset_type=S3Object) + assert deleted + assert len(deleted) == 1 + assert deleted[0].guid == s3object.guid + assert deleted[0].qualified_name == s3object.qualified_name + assert deleted[0].delete_handler == "SOFT" + assert deleted[0].status == EntityStatus.DELETED + + +def _assert_read_delete_object(client, s3object): + deleted = client.asset.get_by_guid( + s3object.guid, asset_type=S3Object, ignore_relationships=False + ) + assert deleted + assert deleted.guid == s3object.guid + assert deleted.qualified_name == s3object.qualified_name + assert deleted.status == EntityStatus.DELETED + + +def _assert_restore_object(client, s3object): + assert s3object.qualified_name + assert client.asset.restore( + asset_type=S3Object, qualified_name=s3object.qualified_name + ) + assert s3object.qualified_name + restored = client.asset.get_by_qualified_name( + asset_type=S3Object, + qualified_name=s3object.qualified_name, + ignore_relationships=False, + ) + assert restored + assert restored.guid == s3object.guid + assert restored.qualified_name == s3object.qualified_name + assert restored.status == EntityStatus.ACTIVE + + +def test_bucket( + client: AtlanClient, + connection: Connection, + bucket: S3Bucket, +): + _assert_bucket(bucket) + + +def test_bucket_with_name( + client: AtlanClient, + connection: Connection, + bucket_with_name: S3Bucket, +): + _assert_bucket(bucket_with_name, True) + + +def test_object( + client: AtlanClient, + connection: Connection, + bucket: S3Bucket, + s3object: S3Object, +): + _assert_object(s3object, bucket) + + +def test_object_with_name( + client: AtlanClient, + connection: Connection, + bucket_with_name: S3Bucket, + s3object_with_name: S3Object, +): + _assert_object(s3object_with_name, bucket_with_name, True) + + +def test_update_bucket( + client: AtlanClient, + connection: Connection, + bucket: S3Bucket, + s3object: S3Object, +): + _assert_update_bucket(client, bucket) + + +def test_update_bucket_with_name( + client: AtlanClient, + connection: Connection, + bucket_with_name: S3Bucket, + s3object_with_name: S3Object, +): + _assert_update_bucket(client, bucket_with_name, True) + + +@pytest.mark.order(after="test_update_bucket") +def test_retrieve_bucket( + client: AtlanClient, + connection: Connection, + bucket: S3Bucket, + s3object: S3Object, +): + _assert_retrieve_bucket(client, bucket, s3object) + + +@pytest.mark.order(after="test_update_bucket_with_name") +def test_retrieve_bucket_with_name( + client: AtlanClient, + connection: Connection, + bucket_with_name: S3Bucket, + s3object_with_name: S3Object, +): + _assert_retrieve_bucket( + client, bucket_with_name, s3object_with_name, with_name=True + ) + + +@pytest.mark.order(after="test_retrieve_bucket") +def test_update_bucket_again( + client: AtlanClient, + connection: Connection, + bucket: S3Bucket, + s3object: S3Object, +): + _assert_update_bucket_again(client, bucket) + + +@pytest.mark.order(after="test_retrieve_bucket_with_name") +def test_update_bucket_with_name_again( + client: AtlanClient, + connection: Connection, + bucket_with_name: S3Bucket, + s3object_with_name: S3Object, +): + _assert_update_bucket_again(client, bucket_with_name, True) + + +@pytest.mark.order(after="test_update_bucket_again") +def test_delete_object( + client: AtlanClient, + connection: Connection, + bucket: S3Bucket, + s3object: S3Object, +): + _assert_delete_object(client, s3object) + + +@pytest.mark.order(after="test_update_bucket_with_name_again") +def test_delete_object_with_name( + client: AtlanClient, + connection: Connection, + bucket_with_name: S3Bucket, + s3object_with_name: S3Object, +): + _assert_delete_object(client, s3object_with_name) + + +@pytest.mark.order(after="test_delete_object") +def test_read_deleted_object( + client: AtlanClient, + connection: Connection, + bucket: S3Bucket, + s3object: S3Object, +): + _assert_read_delete_object(client, s3object) + + +@pytest.mark.order(after="test_delete_object_with_name") +def test_read_deleted_object_with_name( + client: AtlanClient, + connection: Connection, + bucket_with_name: S3Bucket, + s3object_with_name: S3Object, +): + _assert_read_delete_object(client, s3object_with_name) + + +@pytest.mark.order(after="test_read_deleted_object") +def test_restore_object( + client: AtlanClient, + connection: Connection, + bucket: S3Bucket, + s3object: S3Object, +): + _assert_restore_object(client, s3object) + + +@pytest.mark.order(after="test_read_deleted_object_with_name") +def test_restore_object_with_name( + client: AtlanClient, + connection: Connection, + bucket_with_name: S3Bucket, + s3object_with_name: S3Object, +): + _assert_restore_object(client, s3object_with_name) diff --git a/tests_v9/integration/suggestions_test.py b/tests_v9/integration/suggestions_test.py new file mode 100644 index 000000000..0825661f3 --- /dev/null +++ b/tests_v9/integration/suggestions_test.py @@ -0,0 +1,955 @@ +from time import sleep +from typing import Callable, Generator + +import pytest + +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.model.assets import ( + Asset, + AtlasGlossary, + AtlasGlossaryTerm, + Column, + Connection, + Database, + Schema, + Table, + View, +) +from pyatlan_v9.model.core import AtlanTag, AtlanTagName +from pyatlan_v9.model.enums import AtlanConnectorType, AtlanDeleteType +from pyatlan_v9.model.group import AtlanGroup +from pyatlan_v9.model.response import AssetMutationResponse +from pyatlan_v9.model.suggestions import Suggestions +from pyatlan_v9.model.typedef import AtlanTagDef +from tests_v9.integration.client import TestId, delete_asset +from tests_v9.integration.connection_test import create_connection +from tests_v9.integration.glossary_test import create_term + +PREFIX = TestId.make_unique("Trident") + +CONNECTOR_TYPE = AtlanConnectorType.ROCKSET +CONNECTION_NAME = PREFIX +DESCRIPTION = "Automated testing of the Python SDK." +SYSTEM_DESCRIPTION = DESCRIPTION + "(system)" + +DATABASE_NAME = PREFIX + "_db" +SCHEMA_NAME1 = PREFIX + "_schema1" +SCHEMA_NAME2 = PREFIX + "_schema2" +SCHEMA_NAME3 = PREFIX + "_schema3" +TABLE_NAME = PREFIX + "_table" +VIEW_NAME = PREFIX + "_view" +COLUMN_NAME1 = PREFIX + "_col1" + +ATLAN_TAG_NAME1 = PREFIX + "tag1" +ATLAN_TAG_NAME2 = PREFIX + "tag2" + +TERM_NAME1 = PREFIX + "term1" +TERM_NAME2 = PREFIX + "term2" + + +def create_glossary(client: AtlanClient, name: str) -> AtlasGlossary: + g = AtlasGlossary.creator(name=name) + r = client.asset.save(g) + return r.assets_created(AtlasGlossary)[0] + + +@pytest.fixture(scope="module") +def glossary( + client: AtlanClient, +) -> Generator[AtlasGlossary, None, None]: + g = create_glossary(client, PREFIX) + yield g + delete_asset( + client, guid=g.guid, asset_type=AtlasGlossary, delete_type=AtlanDeleteType.HARD + ) + + +@pytest.fixture(scope="module") +def wait_for_consistency(): + """ + Wait for suggestions to be indexed + """ + sleep(15) + + +@pytest.fixture(scope="module") +def connection(client: AtlanClient) -> Generator[Connection, None, None]: + result = create_connection( + client=client, name=CONNECTION_NAME, connector_type=CONNECTOR_TYPE + ) + yield result + delete_asset(client, guid=result.guid, asset_type=Connection) + + +@pytest.fixture(scope="module") +def database( + client: AtlanClient, + connection: Connection, + upsert: Callable[[Asset], AssetMutationResponse], +): + to_create = Database.creator( + name=DATABASE_NAME, connection_qualified_name=connection.qualified_name + ) + result = upsert(to_create) + assert result + database = result.assets_created(asset_type=Database)[0] + assert database.connector_name == CONNECTOR_TYPE + yield database + + +@pytest.fixture(scope="module") +def schema1( + client: AtlanClient, + database: Database, + upsert: Callable[[Asset], AssetMutationResponse], +): + assert database and database.qualified_name + schema1 = Schema.creator( + name=SCHEMA_NAME1, + database_qualified_name=database.qualified_name, + ) + response = upsert(schema1) + assert (schemas := response.assets_created(asset_type=Schema)) + assert len(schemas) == 1 and schemas[0].database_name == DATABASE_NAME + yield schema1 + + +@pytest.fixture(scope="module") +def schema2( + client: AtlanClient, + database: Database, + upsert: Callable[[Asset], AssetMutationResponse], +): + assert database and database.qualified_name + schema2 = Schema.creator( + name=SCHEMA_NAME2, + database_qualified_name=database.qualified_name, + ) + response = upsert(schema2) + assert (schemas := response.assets_created(asset_type=Schema)) + assert len(schemas) == 1 and schemas[0].database_name == DATABASE_NAME + yield schema2 + + +@pytest.fixture(scope="module") +def schema3( + client: AtlanClient, + database: Database, + upsert: Callable[[Asset], AssetMutationResponse], +): + assert database and database.qualified_name + schema3 = Schema.creator( + name=SCHEMA_NAME3, + database_qualified_name=database.qualified_name, + ) + response = upsert(schema3) + assert (schemas := response.assets_created(asset_type=Schema)) + assert len(schemas) == 1 + yield schema3 + + +@pytest.fixture(scope="module") +def table1( + client: AtlanClient, + schema1: Schema, + upsert: Callable[[Asset], AssetMutationResponse], +): + assert schema1 and schema1.qualified_name + table = Table.creator( + name=TABLE_NAME, + schema_qualified_name=schema1.qualified_name, + ) + response = upsert(table) + assert (tables := response.assets_created(asset_type=Table)) + assert len(tables) == 1 + yield tables[0] + + +@pytest.fixture(scope="module") +def table2( + client: AtlanClient, + schema2: Schema, + upsert: Callable[[Asset], AssetMutationResponse], +): + assert schema2 and schema2.qualified_name + table = Table.creator( + name=TABLE_NAME, + schema_qualified_name=schema2.qualified_name, + ) + response = upsert(table) + assert (tables := response.assets_created(asset_type=Table)) + assert len(tables) == 1 + yield tables[0] + + +@pytest.fixture(scope="module") +def view1( + client: AtlanClient, + schema1: Schema, + upsert: Callable[[Asset], AssetMutationResponse], +): + assert schema1 and schema1.qualified_name + view = View.creator( + name=VIEW_NAME, + schema_qualified_name=schema1.qualified_name, + ) + response = upsert(view) + assert (views := response.assets_created(asset_type=View)) + assert len(views) == 1 + yield views[0] + + +@pytest.fixture(scope="module") +def view2( + client: AtlanClient, + schema2: Schema, + upsert: Callable[[Asset], AssetMutationResponse], +): + assert schema2 and schema2.qualified_name + view = View.creator( + name=VIEW_NAME, + schema_qualified_name=schema2.qualified_name, + ) + response = upsert(view) + assert (views := response.assets_created(asset_type=View)) + assert len(views) == 1 + yield views[0] + + +@pytest.fixture(scope="module") +def t1c1( + client: AtlanClient, + table1: Table, + upsert: Callable[[Asset], AssetMutationResponse], +): + assert table1 and table1.qualified_name + column = Column.creator( + name=COLUMN_NAME1, + parent_qualified_name=table1.qualified_name, + parent_type=Table, + order=1, + ) + response = upsert(column) + assert (columns := response.assets_created(asset_type=Column)) + assert len(columns) == 1 + yield columns[0] + + +@pytest.fixture(scope="module") +def t2c1( + client: AtlanClient, + table2: Table, + upsert: Callable[[Asset], AssetMutationResponse], +): + assert table2 and table2.qualified_name + column = Column.creator( + name=COLUMN_NAME1, + parent_qualified_name=table2.qualified_name, + parent_type=Table, + order=1, + ) + response = upsert(column) + assert (columns := response.assets_created(asset_type=Column)) + assert len(columns) == 1 + yield columns[0] + + +@pytest.fixture(scope="module") +def v1c1( + client: AtlanClient, + view1: View, + upsert: Callable[[Asset], AssetMutationResponse], +): + assert view1 and view1.qualified_name + column = Column.creator( + name=COLUMN_NAME1, + parent_qualified_name=view1.qualified_name, + parent_type=View, + order=1, + ) + response = upsert(column) + assert (columns := response.assets_created(asset_type=Column)) + assert len(columns) == 1 + yield columns[0] + + +@pytest.fixture(scope="module") +def v2c1( + client: AtlanClient, + view2: View, + upsert: Callable[[Asset], AssetMutationResponse], +): + assert view2 and view2.qualified_name + column = Column.creator( + name=COLUMN_NAME1, + parent_qualified_name=view2.qualified_name, + parent_type=View, + order=1, + ) + response = upsert(column) + assert (columns := response.assets_created(asset_type=Column)) + assert len(columns) == 1 + yield columns[0] + + +@pytest.fixture(scope="module") +def create_atlan_tag1( + make_atlan_tag, + client: AtlanClient, + table1: Table, + table3: Table, + t1c1: Column, + v2c1: Column, +) -> Generator[AtlanTagDef, None, None]: + assert table1.qualified_name and table3.qualified_name + assert t1c1.qualified_name and v2c1.qualified_name + yield make_atlan_tag(ATLAN_TAG_NAME1) + client.asset.remove_atlan_tag( + asset_type=Table, + qualified_name=table1.qualified_name, + atlan_tag_name=ATLAN_TAG_NAME1, + ) + client.asset.remove_atlan_tag( + asset_type=Table, + qualified_name=table3.qualified_name, + atlan_tag_name=ATLAN_TAG_NAME1, + ) + client.asset.remove_atlan_tag( + asset_type=Column, + qualified_name=t1c1.qualified_name, + atlan_tag_name=ATLAN_TAG_NAME1, + ) + client.asset.remove_atlan_tag( + asset_type=Column, + qualified_name=v2c1.qualified_name, + atlan_tag_name=ATLAN_TAG_NAME1, + ) + + +@pytest.fixture(scope="module") +def create_atlan_tag2( + make_atlan_tag, + client: AtlanClient, + table1: Table, + table3: Table, + t1c1: Column, + t2c1: Column, + v1c1: Column, + v2c1: Column, +) -> Generator[AtlanTagDef, None, None]: + assert table1.qualified_name and table3.qualified_name + assert t1c1.qualified_name and t2c1.qualified_name + assert v1c1.qualified_name and v2c1.qualified_name + yield make_atlan_tag(ATLAN_TAG_NAME2) + client.asset.remove_atlan_tag( + asset_type=Table, + qualified_name=table1.qualified_name, + atlan_tag_name=ATLAN_TAG_NAME2, + ) + client.asset.remove_atlan_tag( + asset_type=Table, + qualified_name=table3.qualified_name, + atlan_tag_name=ATLAN_TAG_NAME2, + ) + client.asset.remove_atlan_tag( + asset_type=Column, + qualified_name=t1c1.qualified_name, + atlan_tag_name=ATLAN_TAG_NAME2, + ) + client.asset.remove_atlan_tag( + asset_type=Column, + qualified_name=v1c1.qualified_name, + atlan_tag_name=ATLAN_TAG_NAME2, + ) + client.asset.remove_atlan_tag( + asset_type=Column, + qualified_name=t2c1.qualified_name, + atlan_tag_name=ATLAN_TAG_NAME2, + ) + client.asset.remove_atlan_tag( + asset_type=Column, + qualified_name=v2c1.qualified_name, + atlan_tag_name=ATLAN_TAG_NAME2, + ) + + +@pytest.fixture(scope="module") +def term1( + client: AtlanClient, + table1: Table, + table3: Table, + t1c1: Column, + glossary: AtlasGlossary, +) -> Generator[AtlasGlossaryTerm, None, None]: + assert table1.qualified_name and table3.qualified_name and t1c1.qualified_name + assert glossary and glossary.guid + t = create_term(client, name=TERM_NAME2, glossary_guid=glossary.guid) + yield t + client.asset.remove_terms( + asset_type=Table, + qualified_name=table1.qualified_name, + terms=[AtlasGlossaryTerm.ref_by_guid(t.guid)], + ) + client.asset.remove_terms( + asset_type=Table, + qualified_name=table3.qualified_name, + terms=[AtlasGlossaryTerm.ref_by_guid(t.guid)], + ) + client.asset.remove_terms( + asset_type=Column, + qualified_name=t1c1.qualified_name, + terms=[AtlasGlossaryTerm.ref_by_guid(t.guid)], + ) + delete_asset( + client, + guid=t.guid, + asset_type=AtlasGlossaryTerm, + delete_type=AtlanDeleteType.HARD, + ) + + +@pytest.fixture(scope="module") +def term2( + client: AtlanClient, + table1: Table, + table3: Table, + t1c1: Column, + t2c1: Column, + v1c1: Column, + glossary: AtlasGlossary, +) -> Generator[AtlasGlossaryTerm, None, None]: + assert table1.qualified_name and table3.qualified_name + assert t1c1.qualified_name and t2c1.qualified_name + assert v1c1.qualified_name + assert glossary and glossary.guid + t = create_term(client, name=TERM_NAME2, glossary_guid=glossary.guid) + yield t + client.asset.remove_terms( + asset_type=Table, + qualified_name=table1.qualified_name, + terms=[AtlasGlossaryTerm.ref_by_guid(t.guid)], + ) + client.asset.remove_terms( + asset_type=Table, + qualified_name=table3.qualified_name, + terms=[AtlasGlossaryTerm.ref_by_guid(t.guid)], + ) + client.asset.remove_terms( + asset_type=Column, + qualified_name=t1c1.qualified_name, + terms=[AtlasGlossaryTerm.ref_by_guid(t.guid)], + ) + client.asset.remove_terms( + asset_type=Column, + qualified_name=t2c1.qualified_name, + terms=[AtlasGlossaryTerm.ref_by_guid(t.guid)], + ) + client.asset.remove_terms( + asset_type=Column, + qualified_name=v1c1.qualified_name, + terms=[AtlasGlossaryTerm.ref_by_guid(t.guid)], + ) + delete_asset( + client, + guid=t.guid, + asset_type=AtlasGlossaryTerm, + delete_type=AtlanDeleteType.HARD, + ) + + +@pytest.fixture(scope="module") +def owner_group( + client: AtlanClient, +) -> Generator[AtlanGroup, None, None]: + to_create = AtlanGroup.creator(PREFIX) + response = client.group.creator(group=to_create) + assert response + group = client.group.get_by_name(PREFIX) + assert group + assert group.records is not None + assert len(group.records) == 1 + assert group.records[0].id + yield group.records[0] + client.group.purge(group.records[0].id) + + +def test_connection(client: AtlanClient, connection: Connection): + results = client.asset.find_connections_by_name( + name=CONNECTION_NAME, connector_type=CONNECTOR_TYPE + ) + assert results and len(results) == 1 + assert results[0].guid == connection.guid + assert results[0].qualified_name == connection.qualified_name + + +def test_schemas( + schema1: Schema, + schema2: Schema, + schema3: Schema, +): + assert schema1.connector_name == CONNECTOR_TYPE + assert schema1.database_name == DATABASE_NAME + + assert schema2.connector_name == CONNECTOR_TYPE + assert schema2.database_name == DATABASE_NAME + + assert schema3.connector_name == CONNECTOR_TYPE + assert schema3.database_name == DATABASE_NAME + + +@pytest.fixture(scope="module") +def table3( + client: AtlanClient, + schema3: Schema, + upsert: Callable[[Asset], AssetMutationResponse], +): + assert schema3 and schema3.qualified_name + table = Table.creator( + name=TABLE_NAME, + schema_qualified_name=schema3.qualified_name, + ) + response = upsert(table) + assert (tables := response.assets_created(asset_type=Table)) + assert len(tables) == 1 + yield tables[0] + delete_asset( + client, guid=tables[0].guid, asset_type=Table, delete_type=AtlanDeleteType.HARD + ) + + +def test_tables( + table1: Table, + table2: Table, + table3: Table, + database: Database, + connection: Connection, +): + assert table1.connector_name == CONNECTOR_TYPE + assert table1.schema_name == SCHEMA_NAME1 + assert table1.database_name == DATABASE_NAME + assert table1.database_qualified_name == database.qualified_name + assert table1.connection_qualified_name == connection.qualified_name + + assert table2.connector_name == CONNECTOR_TYPE + assert table2.schema_name == SCHEMA_NAME2 + assert table2.database_name == DATABASE_NAME + assert table2.database_qualified_name == database.qualified_name + assert table2.connection_qualified_name == connection.qualified_name + + assert table3.connector_name == CONNECTOR_TYPE + assert table3.schema_name == SCHEMA_NAME3 + assert table3.database_name == DATABASE_NAME + assert table3.database_qualified_name == database.qualified_name + assert table3.connection_qualified_name == connection.qualified_name + + +def test_views( + view1: View, + view2: View, + database: Database, + connection: Connection, +): + assert view1.connector_name == CONNECTOR_TYPE + assert view1.schema_name == SCHEMA_NAME1 + assert view1.database_name == DATABASE_NAME + assert view1.database_qualified_name == database.qualified_name + assert view1.connection_qualified_name == connection.qualified_name + + assert view2.connector_name == CONNECTOR_TYPE + assert view2.schema_name == SCHEMA_NAME2 + assert view2.database_name == DATABASE_NAME + assert view2.database_qualified_name == database.qualified_name + assert view2.connection_qualified_name == connection.qualified_name + + +def test_column1( + connection: Connection, + t1c1: Column, + t2c1: Column, + v1c1: Column, + v2c1: Column, + schema1: Schema, + schema2: Schema, + database: Database, +): + # Table column 1 + assert t1c1.connector_name == CONNECTOR_TYPE + assert t1c1.table_name == TABLE_NAME + assert t1c1.schema_name == SCHEMA_NAME1 + assert t1c1.schema_qualified_name == schema1.qualified_name + assert t1c1.database_name == DATABASE_NAME + assert t1c1.database_qualified_name == database.qualified_name + assert t1c1.connection_qualified_name == connection.qualified_name + + assert t2c1.connector_name == CONNECTOR_TYPE + assert t2c1.table_name == TABLE_NAME + assert t2c1.schema_name == SCHEMA_NAME2 + assert t2c1.schema_qualified_name == schema2.qualified_name + assert t2c1.database_name == DATABASE_NAME + assert t2c1.database_qualified_name == database.qualified_name + assert t2c1.connection_qualified_name == connection.qualified_name + + # View column 1 + assert v1c1.connector_name == CONNECTOR_TYPE + assert v1c1.view_name == VIEW_NAME + assert v1c1.schema_name == SCHEMA_NAME1 + assert v1c1.schema_qualified_name == schema1.qualified_name + assert v1c1.database_name == DATABASE_NAME + assert v1c1.database_qualified_name == database.qualified_name + assert v1c1.connection_qualified_name == connection.qualified_name + + assert v2c1.connector_name == CONNECTOR_TYPE + assert v2c1.view_name == VIEW_NAME + assert v2c1.schema_name == SCHEMA_NAME2 + assert v2c1.schema_qualified_name == schema2.qualified_name + assert v2c1.database_name == DATABASE_NAME + assert v2c1.database_qualified_name == database.qualified_name + assert v2c1.connection_qualified_name == connection.qualified_name + + +def test_update_table1( + client: AtlanClient, + table1: Table, + owner_group: AtlanGroup, + create_atlan_tag1: AtlanTagDef, + create_atlan_tag2: AtlanTagDef, + term1: AtlasGlossaryTerm, + term2: AtlasGlossaryTerm, +): + assert table1 and table1.qualified_name + assert owner_group and owner_group.name + to_update = Table.updater(qualified_name=table1.qualified_name, name=TABLE_NAME) + to_update.owner_groups = {owner_group.name} + to_update.description = SYSTEM_DESCRIPTION + to_update.user_description = DESCRIPTION + to_update.atlan_tags = [ + AtlanTag( # type: ignore[call-arg] + type_name=AtlanTagName(ATLAN_TAG_NAME1), + propagate=False, + ), + AtlanTag( # type: ignore[call-arg] + type_name=AtlanTagName(ATLAN_TAG_NAME2), + propagate=False, + ), + ] + to_update.assigned_terms = [ + AtlasGlossaryTerm.ref_by_guid(term1.guid), + AtlasGlossaryTerm.ref_by_guid(term2.guid), + ] + + response = client.asset.save(to_update, replace_atlan_tags=True) + assert response and response.mutated_entities + assert ( + response.mutated_entities.UPDATE and len(response.mutated_entities.UPDATE) == 3 + ) # table + 2x terms + expected_types = {asset.type_name for asset in response.mutated_entities.UPDATE} + assert expected_types == {Table.__name__, AtlasGlossaryTerm.__name__} + assert (tables := response.assets_updated(asset_type=Table)) + assert len(tables) == 1 + assert tables[0].owner_groups and len(tables[0].owner_groups) == 1 + assert tables[0].owner_groups == {owner_group.name} + + +def test_update_table3( + client: AtlanClient, + table3: Table, + owner_group: AtlanGroup, + create_atlan_tag1: AtlanTagDef, + create_atlan_tag2: AtlanTagDef, + term1: AtlasGlossaryTerm, + term2: AtlasGlossaryTerm, +): + assert table3 and table3.qualified_name + assert owner_group and owner_group.name + # Updating `table3.name` with `VIEW_NAME` used in + # `test_suggestions_across_types` to view table suggestions for `view1` + to_update = Table.updater(qualified_name=table3.qualified_name, name=VIEW_NAME) + to_update.owner_groups = {owner_group.name} + to_update.description = SYSTEM_DESCRIPTION + to_update.user_description = DESCRIPTION + to_update.atlan_tags = [ + AtlanTag( # type: ignore[call-arg] + type_name=AtlanTagName(ATLAN_TAG_NAME1), + propagate=False, + ), + AtlanTag( # type: ignore[call-arg] + type_name=AtlanTagName(ATLAN_TAG_NAME2), + propagate=False, + ), + ] + to_update.assigned_terms = [ + AtlasGlossaryTerm.ref_by_guid(term1.guid), + AtlasGlossaryTerm.ref_by_guid(term2.guid), + ] + + response = client.asset.save(to_update, replace_atlan_tags=True) + + assert response and response.mutated_entities + assert ( + response.mutated_entities.UPDATE and len(response.mutated_entities.UPDATE) == 3 + ) # table + 2x terms + expected_types = {asset.type_name for asset in response.mutated_entities.UPDATE} + assert expected_types == {Table.__name__, AtlasGlossaryTerm.__name__} + assert (tables := response.assets_updated(asset_type=Table)) + assert len(tables) == 1 + assert tables[0].owner_groups and len(tables[0].owner_groups) == 1 + assert tables[0].owner_groups == {owner_group.name} + + +def test_update_table1_column1( + client: AtlanClient, + owner_group: AtlanGroup, + t1c1: Column, + create_atlan_tag1: AtlanTagDef, + create_atlan_tag2: AtlanTagDef, + term1: AtlasGlossaryTerm, + term2: AtlasGlossaryTerm, +): + assert t1c1 and t1c1.qualified_name + assert owner_group and owner_group.name + to_update = Column.updater(qualified_name=t1c1.qualified_name, name=COLUMN_NAME1) + to_update.owner_groups = {owner_group.name} + to_update.description = SYSTEM_DESCRIPTION + to_update.user_description = DESCRIPTION + to_update.atlan_tags = [ + AtlanTag( # type: ignore[call-arg] + type_name=AtlanTagName(ATLAN_TAG_NAME1), + propagate=False, + ), + AtlanTag( # type: ignore[call-arg] + type_name=AtlanTagName(ATLAN_TAG_NAME2), + propagate=False, + ), + ] + to_update.assigned_terms = [ + AtlasGlossaryTerm.ref_by_guid(term1.guid), + AtlasGlossaryTerm.ref_by_guid(term2.guid), + ] + + response = client.asset.save(to_update, replace_atlan_tags=True) + + assert response and response.mutated_entities + assert ( + response.mutated_entities.UPDATE and len(response.mutated_entities.UPDATE) == 3 + ) # column + 2x terms + expected_types = {asset.type_name for asset in response.mutated_entities.UPDATE} + assert expected_types == {Column.__name__, AtlasGlossaryTerm.__name__} + assert (columns := response.assets_updated(asset_type=Column)) + assert len(columns) == 1 + assert columns[0].owner_groups and len(columns[0].owner_groups) == 1 + assert columns[0].owner_groups == {owner_group.name} + + +def test_update_view1_column1( + client: AtlanClient, + owner_group: AtlanGroup, + v1c1: Column, + create_atlan_tag1: AtlanTagDef, + create_atlan_tag2: AtlanTagDef, + term1: AtlasGlossaryTerm, + term2: AtlasGlossaryTerm, +): + assert v1c1 and v1c1.qualified_name + assert owner_group and owner_group.name + to_update = Column.updater(qualified_name=v1c1.qualified_name, name=COLUMN_NAME1) + to_update.owner_groups = {owner_group.name} + to_update.description = SYSTEM_DESCRIPTION + to_update.user_description = DESCRIPTION + to_update.atlan_tags = [ + AtlanTag( # type: ignore[call-arg] + type_name=AtlanTagName(ATLAN_TAG_NAME2), + propagate=False, + ), + ] + to_update.assigned_terms = [ + AtlasGlossaryTerm.ref_by_guid(term2.guid), + ] + + response = client.asset.save(to_update, replace_atlan_tags=True) + + assert response and response.mutated_entities + assert ( + response.mutated_entities.UPDATE and len(response.mutated_entities.UPDATE) == 2 + ) # column + term + expected_types = {asset.type_name for asset in response.mutated_entities.UPDATE} + assert expected_types == {Column.__name__, AtlasGlossaryTerm.__name__} + assert (columns := response.assets_updated(asset_type=Column)) + assert len(columns) == 1 + assert columns[0].owner_groups and len(columns[0].owner_groups) == 1 + assert columns[0].owner_groups == {owner_group.name} + + +def test_suggestions_default( + client: AtlanClient, + t2c1: Column, + owner_group: AtlanGroup, + term1: AtlasGlossaryTerm, + term2: AtlasGlossaryTerm, + wait_for_consistency, +): + assert owner_group and owner_group.name + assert term1.qualified_name and term2.qualified_name + response = ( + Suggestions(includes=Suggestions.TYPE.all()).finder(t2c1).get(client=client) + ) + + assert response + assert response.owner_groups and len(response.owner_groups) == 1 + assert response.owner_groups[0].count == 2 + assert response.owner_groups[0].value == owner_group.name + assert response.system_descriptions and len(response.system_descriptions) == 1 + assert response.system_descriptions[0].count == 2 + assert response.system_descriptions[0].value == SYSTEM_DESCRIPTION + assert response.user_descriptions and len(response.user_descriptions) == 1 + assert response.user_descriptions[0].count == 2 + assert response.user_descriptions[0].value == DESCRIPTION + assert response.atlan_tags and len(response.atlan_tags) == 2 + assert response.atlan_tags[0].count == 2 + assert response.atlan_tags[0].value == ATLAN_TAG_NAME2 + assert response.atlan_tags[1].count == 1 + assert response.atlan_tags[1].value == ATLAN_TAG_NAME1 + assert response.assigned_terms and len(response.assigned_terms) == 2 + assert response.assigned_terms[0].count == 2 + assert response.assigned_terms[0].value == AtlasGlossaryTerm.ref_by_qualified_name( + term2.qualified_name + ) + assert response.assigned_terms[1].count == 1 + assert response.assigned_terms[1].value == AtlasGlossaryTerm.ref_by_qualified_name( + term1.qualified_name + ) + + +def test_suggestions_accross_types( + client: AtlanClient, + view1: View, + owner_group: AtlanGroup, + term1: AtlasGlossaryTerm, + term2: AtlasGlossaryTerm, + wait_for_consistency, +): + assert term1 and term1.qualified_name + assert term2 and term2.qualified_name + assert owner_group and owner_group.name + response = ( + Suggestions(includes=Suggestions.TYPE.all()) + .finder(view1) + .with_other_type("Table") + .get(client=client) + ) + + assert response + assert response.owner_groups and len(response.owner_groups) == 1 + assert response.owner_groups[0].count == 1 + assert response.owner_groups[0].value == owner_group.name + assert response.system_descriptions and len(response.system_descriptions) == 1 + assert response.system_descriptions[0].count == 1 + assert response.system_descriptions[0].value == SYSTEM_DESCRIPTION + assert response.user_descriptions and len(response.user_descriptions) == 1 + assert response.user_descriptions[0].count == 1 + assert response.user_descriptions[0].value == DESCRIPTION + assert response.atlan_tags and len(response.atlan_tags) == 2 + assert response.atlan_tags[0].count == 1 + for tag in response.atlan_tags: + assert tag.count == 1 + assert tag.value in {ATLAN_TAG_NAME1, ATLAN_TAG_NAME2} + assert response.assigned_terms and len(response.assigned_terms) == 2 + for term in response.assigned_terms: + assert term.count == 1 + assert term.value in ( + AtlasGlossaryTerm.ref_by_qualified_name(term1.qualified_name), + AtlasGlossaryTerm.ref_by_qualified_name(term2.qualified_name), + ) + + +def test_limited_suggestions( + client: AtlanClient, + table2: Table, + owner_group: AtlanGroup, + wait_for_consistency, +): + assert owner_group and owner_group.name + response = ( + Suggestions() + .finder(table2) + .include(Suggestions.TYPE.GROUP_OWNERS) + .include(Suggestions.TYPE.SYSTEM_DESCRIPTION) + .get(client=client) + ) + + assert response + assert response.owner_groups and len(response.owner_groups) == 1 + assert response.owner_groups[0].count == 1 + assert response.owner_groups[0].value == owner_group.name + assert response.system_descriptions and len(response.system_descriptions) == 1 + assert response.system_descriptions[0].count == 1 + assert response.system_descriptions[0].value == SYSTEM_DESCRIPTION + assert response.atlan_tags == [] + assert response.assigned_terms == [] + assert response.user_descriptions == [] + + +def test_apply_t2c1( + client: AtlanClient, + t2c1: Column, + owner_group: AtlanGroup, + term2: AtlasGlossaryTerm, + wait_for_consistency, +): + assert term2 and term2.qualified_name + assert owner_group and owner_group.name + response = ( + Suggestions() + .finder(t2c1) + .include(Suggestions.TYPE.TAGS) + .include(Suggestions.TYPE.TERMS) + .include(Suggestions.TYPE.GROUP_OWNERS) + .include(Suggestions.TYPE.USER_DESCRIPTION) + .include(Suggestions.TYPE.INDIVIDUAL_OWNERS) + .apply(client=client) + ) + + assert response and response.mutated_entities + assert ( + response.mutated_entities.UPDATE and len(response.mutated_entities.UPDATE) == 2 + ) # column + term + one = response.mutated_entities.UPDATE[0] + assert one and one.owner_groups == {owner_group.name} + # System description should be untouched (still empty) + assert not one.description + assert one.user_description == DESCRIPTION + assert one.atlan_tags and len(one.atlan_tags) == 1 + assert one.atlan_tags[0].type_name == AtlanTagName(ATLAN_TAG_NAME2) + assert one.meanings and len(one.meanings) == 1 + assert one.meanings[0].term_guid == term2.guid + + +def test_apply_v2c1( + client: AtlanClient, + v2c1: Column, + wait_for_consistency, +): + response = ( + Suggestions() + .finder(v2c1) + .include(Suggestions.TYPE.TAGS) + .include(Suggestions.TYPE.SYSTEM_DESCRIPTION) + .apply(client=client, allow_multiple=True) + ) + + assert response and response.mutated_entities + assert ( + response.mutated_entities.UPDATE and len(response.mutated_entities.UPDATE) == 1 + ) + one = response.mutated_entities.UPDATE[0] + assert one and not one.owner_groups + # System description should be untouched (still empty) + assert not one.description + assert not one.meanings + # System description should be applied to user description + assert one.user_description == SYSTEM_DESCRIPTION + assert one.atlan_tags and len(one.atlan_tags) == 2 + for tag in one.atlan_tags: + assert tag.type_name in ( + AtlanTagName(ATLAN_TAG_NAME1), + AtlanTagName(ATLAN_TAG_NAME2), + ) diff --git a/tests_v9/integration/superset_asset_test.py b/tests_v9/integration/superset_asset_test.py new file mode 100644 index 000000000..a4ae082d9 --- /dev/null +++ b/tests_v9/integration/superset_asset_test.py @@ -0,0 +1,357 @@ +from typing import Generator + +import pytest +from msgspec import UNSET + +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.model.assets import ( + Connection, + SupersetChart, + SupersetDashboard, + SupersetDataset, +) +from pyatlan_v9.model.core import Announcement +from pyatlan_v9.model.enums import ( + AnnouncementType, + AtlanConnectorType, + CertificateStatus, + EntityStatus, +) +from tests_v9.integration.client import TestId, delete_asset +from tests_v9.integration.connection_test import create_connection + +MODULE_NAME = TestId.make_unique("SUPERSET") + +CONNECTOR_TYPE = AtlanConnectorType.SUPERSET +SUPERSET_DASHBOARD_NAME = MODULE_NAME + "-dash" +SUPERSET_DATASET_NAME = MODULE_NAME + "-ds" +SUPERSET_CHART_NAME = MODULE_NAME + "-cht" +SUPERSET_DATASET_NAME_OVERLOAD = MODULE_NAME + "-overload-ds" +SUPERSET_CHART_NAME_OVERLOAD = MODULE_NAME + "-overload-cht" +CERTIFICATE_STATUS = CertificateStatus.VERIFIED +CERTIFICATE_MESSAGE = "Automated testing of the Python SDK." +ANNOUNCEMENT_TYPE = AnnouncementType.INFORMATION +ANNOUNCEMENT_TITLE = "Python SDK testing." +ANNOUNCEMENT_MESSAGE = "Automated testing of the Python SDK." + + +def _assert_announcement_cleared(updated): + assert updated.announcement_type in (UNSET, None, "") + assert updated.announcement_title in (UNSET, None, "") + assert updated.announcement_message in (UNSET, None, "") + + +@pytest.fixture(scope="module") +def connection(client: AtlanClient) -> Generator[Connection, None, None]: + result = create_connection( + client=client, name=MODULE_NAME, connector_type=CONNECTOR_TYPE + ) + yield result + delete_asset(client, guid=result.guid, asset_type=Connection) + + +@pytest.fixture(scope="module") +def superset_dashboard( + client: AtlanClient, connection: Connection +) -> Generator[SupersetDashboard, None, None]: + assert connection.qualified_name + to_create = SupersetDashboard.creator( + name=SUPERSET_DASHBOARD_NAME, + connection_qualified_name=connection.qualified_name, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=SupersetDashboard)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=SupersetDashboard) + + +def test_superset_dashboard( + client: AtlanClient, + superset_dashboard: SupersetDashboard, + connection: Connection, +): + assert superset_dashboard + assert superset_dashboard.guid + assert superset_dashboard.qualified_name + assert superset_dashboard.connection_qualified_name == connection.qualified_name + assert superset_dashboard.name == SUPERSET_DASHBOARD_NAME + assert superset_dashboard.connector_name == AtlanConnectorType.SUPERSET.value + + +@pytest.fixture(scope="module") +def superset_chart( + client: AtlanClient, superset_dashboard: SupersetDashboard +) -> Generator[SupersetChart, None, None]: + assert superset_dashboard.qualified_name + to_create = SupersetChart.creator( + name=SUPERSET_CHART_NAME, + superset_dashboard_qualified_name=superset_dashboard.qualified_name, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=SupersetChart)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=SupersetChart) + + +def test_superset_chart( + client: AtlanClient, + superset_chart: SupersetChart, + superset_dashboard: SupersetDashboard, +): + assert superset_chart + assert superset_chart.guid + assert superset_chart.qualified_name + assert ( + superset_chart.superset_dashboard_qualified_name + == superset_dashboard.qualified_name + ) + assert superset_chart.name == SUPERSET_CHART_NAME + assert superset_chart.connector_name == AtlanConnectorType.SUPERSET.value + + +@pytest.fixture(scope="module") +def superset_chart_overload( + client: AtlanClient, + superset_dashboard: SupersetDashboard, + connection: Connection, +) -> Generator[SupersetChart, None, None]: + assert superset_dashboard.qualified_name + assert connection.qualified_name + to_create = SupersetChart.creator( + name=SUPERSET_CHART_NAME_OVERLOAD, + superset_dashboard_qualified_name=superset_dashboard.qualified_name, + connection_qualified_name=connection.qualified_name, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=SupersetChart)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=SupersetChart) + + +def test_overload_superset_chart( + client: AtlanClient, + superset_chart_overload: SupersetChart, + superset_dashboard: SupersetDashboard, +): + assert superset_chart_overload + assert superset_chart_overload.guid + assert superset_chart_overload.qualified_name + assert ( + superset_chart_overload.superset_dashboard_qualified_name + == superset_dashboard.qualified_name + ) + assert superset_chart_overload.name == SUPERSET_CHART_NAME_OVERLOAD + assert superset_chart_overload.connector_name == AtlanConnectorType.SUPERSET.value + + +@pytest.fixture(scope="module") +def superset_dataset( + client: AtlanClient, connection: Connection, superset_dashboard: SupersetDashboard +) -> Generator[SupersetDataset, None, None]: + assert superset_dashboard.qualified_name + to_create = SupersetDataset.creator( + name=SUPERSET_DATASET_NAME, + superset_dashboard_qualified_name=superset_dashboard.qualified_name, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=SupersetDataset)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=SupersetDataset) + + +def test_superset_dataset( + client: AtlanClient, + superset_dataset: SupersetDataset, + connection: Connection, +): + assert superset_dataset + assert superset_dataset.guid + assert superset_dataset.qualified_name + assert superset_dataset.connection_qualified_name == connection.qualified_name + assert superset_dataset.name == SUPERSET_DATASET_NAME + assert superset_dataset.connector_name == AtlanConnectorType.SUPERSET.value + + +@pytest.fixture(scope="module") +def superset_dataset_overload( + client: AtlanClient, + connection: Connection, + superset_dashboard: SupersetDashboard, +) -> Generator[SupersetDataset, None, None]: + assert superset_dashboard.qualified_name + assert connection.qualified_name + to_create = SupersetDataset.creator( + name=SUPERSET_DATASET_NAME_OVERLOAD, + superset_dashboard_qualified_name=superset_dashboard.qualified_name, + connection_qualified_name=connection.qualified_name, + ) + response = client.asset.save(to_create) + result = response.assets_created(asset_type=SupersetDataset)[0] + yield result + delete_asset(client, guid=result.guid, asset_type=SupersetDataset) + + +def test_overload_superset_dataset( + client: AtlanClient, + superset_dataset_overload: SupersetDataset, + connection: Connection, +): + assert superset_dataset_overload + assert superset_dataset_overload.guid + assert superset_dataset_overload.qualified_name + assert ( + superset_dataset_overload.connection_qualified_name == connection.qualified_name + ) + assert superset_dataset_overload.name == SUPERSET_DATASET_NAME_OVERLOAD + assert superset_dataset_overload.connector_name == AtlanConnectorType.SUPERSET.value + + +def test_update_superset_dashboard( + client: AtlanClient, + superset_dashboard: SupersetDashboard, +): + assert superset_dashboard.qualified_name + assert superset_dashboard.name + updated = client.asset.update_certificate( + asset_type=SupersetDashboard, + qualified_name=superset_dashboard.qualified_name, + name=SUPERSET_DASHBOARD_NAME, + certificate_status=CERTIFICATE_STATUS, + message=CERTIFICATE_MESSAGE, + ) + assert updated + assert updated.certificate_status_message == CERTIFICATE_MESSAGE + assert superset_dashboard.qualified_name + assert superset_dashboard.name + updated = client.asset.update_announcement( + asset_type=SupersetDashboard, + qualified_name=superset_dashboard.qualified_name, + name=SUPERSET_DASHBOARD_NAME, + announcement=Announcement( + announcement_type=ANNOUNCEMENT_TYPE, + announcement_title=ANNOUNCEMENT_TITLE, + announcement_message=ANNOUNCEMENT_MESSAGE, + ), + ) + assert updated + if updated.announcement_type is not UNSET: + assert updated.announcement_type == ANNOUNCEMENT_TYPE.value + assert updated.announcement_title == ANNOUNCEMENT_TITLE + assert updated.announcement_message == ANNOUNCEMENT_MESSAGE + + +def test_update_superset_chart( + client: AtlanClient, + superset_chart: SupersetChart, +): + assert superset_chart.qualified_name + assert superset_chart.name + updated = client.asset.update_certificate( + asset_type=SupersetChart, + qualified_name=superset_chart.qualified_name, + name=SUPERSET_CHART_NAME, + certificate_status=CERTIFICATE_STATUS, + message=CERTIFICATE_MESSAGE, + ) + assert updated + assert updated.certificate_status_message == CERTIFICATE_MESSAGE + assert superset_chart.qualified_name + assert superset_chart.name + updated = client.asset.update_announcement( + asset_type=SupersetChart, + qualified_name=superset_chart.qualified_name, + name=SUPERSET_CHART_NAME, + announcement=Announcement( + announcement_type=ANNOUNCEMENT_TYPE, + announcement_title=ANNOUNCEMENT_TITLE, + announcement_message=ANNOUNCEMENT_MESSAGE, + ), + ) + assert updated + if updated.announcement_type is not UNSET: + assert updated.announcement_type == ANNOUNCEMENT_TYPE.value + assert updated.announcement_title == ANNOUNCEMENT_TITLE + assert updated.announcement_message == ANNOUNCEMENT_MESSAGE + + +@pytest.mark.order(after="test_update_superset_dashboard") +def test_retrieve_superset_dashboard( + client: AtlanClient, + superset_dashboard: SupersetDashboard, +): + b = client.asset.get_by_guid( + superset_dashboard.guid, + asset_type=SupersetDashboard, + ignore_relationships=False, + ) + assert b + assert not b.is_incomplete + assert b.guid == superset_dashboard.guid + assert b.qualified_name == superset_dashboard.qualified_name + assert b.name == SUPERSET_DASHBOARD_NAME + assert b.connector_name == AtlanConnectorType.SUPERSET.value + assert b.certificate_status == CERTIFICATE_STATUS + assert b.certificate_status_message == CERTIFICATE_MESSAGE + + +@pytest.mark.order(after="test_retrieve_superset_dashboard") +def test_update_superset_dashboard_again( + client: AtlanClient, + superset_dashboard: SupersetDashboard, +): + assert superset_dashboard.qualified_name + assert superset_dashboard.name + updated = client.asset.remove_certificate( + asset_type=SupersetDashboard, + qualified_name=superset_dashboard.qualified_name, + name=superset_dashboard.name, + ) + assert updated + assert not updated.certificate_status + assert not updated.certificate_status_message + assert superset_dashboard.qualified_name + updated = client.asset.remove_announcement( + qualified_name=superset_dashboard.qualified_name, + asset_type=SupersetDashboard, + name=superset_dashboard.name, + ) + assert updated + _assert_announcement_cleared(updated) + + +@pytest.mark.order(after="test_update_superset_dashboard_again") +def test_delete_superset_dashboard( + client: AtlanClient, superset_dashboard: SupersetDashboard +): + response = client.asset.delete_by_guid(superset_dashboard.guid) + assert response + assert not response.assets_created(asset_type=SupersetDashboard) + assert not response.assets_updated(asset_type=SupersetDashboard) + deleted = response.assets_deleted(asset_type=SupersetDashboard) + assert deleted + assert len(deleted) == 1 + assert deleted[0].guid == superset_dashboard.guid + assert deleted[0].qualified_name == superset_dashboard.qualified_name + assert deleted[0].delete_handler == "SOFT" + assert deleted[0].status == EntityStatus.DELETED + + +@pytest.mark.order(after="test_delete_superset_dashboard") +def test_restore_dashboard( + client: AtlanClient, + superset_dashboard: SupersetDashboard, +): + assert superset_dashboard.qualified_name + assert client.asset.restore( + asset_type=SupersetDashboard, qualified_name=superset_dashboard.qualified_name + ) + assert superset_dashboard.qualified_name + restored = client.asset.get_by_qualified_name( + asset_type=SupersetDashboard, + qualified_name=superset_dashboard.qualified_name, + ignore_relationships=False, + ) + assert restored + assert restored.guid == superset_dashboard.guid + assert restored.qualified_name == superset_dashboard.qualified_name + assert restored.status == EntityStatus.ACTIVE diff --git a/tests_v9/integration/test_asset_batch.py b/tests_v9/integration/test_asset_batch.py new file mode 100644 index 000000000..ce3176c25 --- /dev/null +++ b/tests_v9/integration/test_asset_batch.py @@ -0,0 +1,519 @@ +import logging +from time import sleep +from typing import Callable, Generator + +import pytest + +from pyatlan_v9.client.asset import Batch +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.model.assets import ( + Asset, + Connection, + Database, + MaterialisedView, + Schema, + Table, + View, +) +from pyatlan_v9.model.enums import AssetCreationHandling +from pyatlan_v9.model.fluent_search import FluentSearch +from pyatlan_v9.model.response import AssetMutationResponse +from pyatlan_v9.test_utils import get_random_connector +from tests_v9.integration.client import TestId, delete_asset +from tests_v9.integration.connection_test import create_connection + +LOGGER = logging.getLogger(__name__) +PREFIX = TestId.make_unique("Batch") + +CONNECTION_NAME = PREFIX +DATABASE_NAME = PREFIX + "_db" +SCHEMA_NAME = PREFIX + "_schema" +TABLE_NAME = PREFIX + "_table" +VIEW_NAME = PREFIX + "_view" +MVIEW_NAME = PREFIX + "_mview" +BATCH_MAX_SIZE = 10 +CONNECTOR_TYPE = get_random_connector() +DESCRIPTION = "Automated testing of the Python SDK." + + +@pytest.fixture(scope="module") +def wait_for_consistency(): + """ + Wait for assets to be indexed + """ + sleep(10) + + +@pytest.fixture(scope="module") +def connection(client: AtlanClient) -> Generator[Connection, None, None]: + result = create_connection( + client=client, name=CONNECTION_NAME, connector_type=CONNECTOR_TYPE + ) + yield result + delete_asset(client, guid=result.guid, asset_type=Connection) + + +@pytest.fixture(scope="module") +def database( + connection: Connection, + upsert: Callable[[Asset], AssetMutationResponse], +): + to_create = Database.creator( + name=DATABASE_NAME, connection_qualified_name=connection.qualified_name + ) + result = upsert(to_create) + assert result + database = result.assets_created(asset_type=Database)[0] + assert database.connector_name == CONNECTOR_TYPE + yield database + + +@pytest.fixture(scope="module") +def schema( + client: AtlanClient, + database: Database, + upsert: Callable[[Asset], AssetMutationResponse], +): + assert database and database.qualified_name + schema1 = Schema.creator( + name=SCHEMA_NAME, + database_qualified_name=database.qualified_name, + ) + response = upsert(schema1) + assert (schemas := response.assets_created(asset_type=Schema)) + assert len(schemas) == 1 and schemas[0].database_name == DATABASE_NAME + yield schema1 + + +@pytest.fixture(scope="module") +def batch_table_create( + client: AtlanClient, schema: Schema +) -> Generator[Batch, None, None]: + assert schema and schema.qualified_name + batch = Batch( + client=client, + track=True, + max_size=BATCH_MAX_SIZE, + capture_failures=True, + ) + # 3 tables + for i in range(1, 4): + table = Table.creator( + name=f"{TABLE_NAME}{i}", + schema_qualified_name=schema.qualified_name, + ) + batch.add(table) + + # 1 view + view = View.creator( + name=VIEW_NAME, + schema_qualified_name=schema.qualified_name, + ) + batch.add(view) + + # 1 materialized view + mview = MaterialisedView.creator( + name=MVIEW_NAME, + schema_qualified_name=schema.qualified_name, + ) + batch.add(mview) + + batch.flush() + yield batch + + assert batch and batch.created + for asset in reversed(batch.created): + assert asset and asset.qualified_name + created = client.asset.get_by_qualified_name( + qualified_name=asset.qualified_name, + asset_type=asset.__class__, + min_ext_info=True, + ignore_relationships=True, + ) + assert created and created.guid + response = client.asset.purge_by_guid(created.guid) + if ( + not response + or not response.mutated_entities + or not response.mutated_entities.DELETE + ): + LOGGER.error(f"Failed to remove asset with GUID {asset.guid}.") + + +@pytest.fixture(scope="module") +def batch_table_update( + client: AtlanClient, schema: Schema +) -> Generator[Batch, None, None]: + assert schema and schema.qualified_name + batch = Batch( + client=client, + track=True, + max_size=BATCH_MAX_SIZE, + ) + for i in range(1, 6): + table = Table.creator( + name=f"{TABLE_NAME}{i}", + schema_qualified_name=schema.qualified_name, + ) + batch.add(table) + yield batch + + +def test_batch_create(batch_table_create: Batch, schema: Schema): + batch = batch_table_create + + # Ensure the batch has no failures + assert batch and batch.failures == [] + + # Verify no assets were skipped or restored + assert batch.skipped == [] and batch.num_skipped == 0 + assert batch.restored == [] and batch.num_restored == 0 + + # Verify that 5 assets (3 tables, 1 view, 1 materialized view) were created + assert batch.created and len(batch.created) == 5 and batch.num_created == 5 + assert all( + asset.type_name in {Table.__name__, View.__name__, MaterialisedView.__name__} + for asset in batch.created + ) + + # Ensure the schema was updated + assert batch.updated and len(batch.updated) == 1 and batch.num_updated == 1 + assert batch.updated[0].qualified_name == schema.qualified_name + + +@pytest.mark.order(after="test_batch_create") +def test_batch_update( + wait_for_consistency, client: AtlanClient, batch_table_create: Batch +): + # Table with view qn / mview qn + # 1. table_view_agnostic and update only -- update -- table? -> view? -> mview + # 2. not table_view_agnostic and update only -- skip -- table? -> view? -> mview? - not found + # 3. not table_view_agnostic and not update only -- create -- new table (with view qn) + + create_batch = batch_table_create + for asset in create_batch.created: + if asset.name == f"{TABLE_NAME}1": + table1 = asset + elif asset.name == VIEW_NAME: + view = asset + elif asset.name == MVIEW_NAME: + mview = asset + + assert table1 and table1.qualified_name + assert view and view.qualified_name + assert mview and mview.qualified_name + + # An asset in the batch marked as a table will attempt + # to match a view or mview if not found as a table, and vice versa + # [sub-test-1]: Table with view qn (table_view_agnostic=True, update_only=True) + # Expect the view to be updated since `table_view_agnostic=True` and `update_only=True` + batch1 = Batch( + client=client, + track=True, + update_only=True, + table_view_agnostic=True, + max_size=BATCH_MAX_SIZE, + ) + SUB_TEST1_DESCRIPTION = f"[sub-test1] {DESCRIPTION}" + + table = Table.updater(qualified_name=view.qualified_name, name=view.name) + table.user_description = SUB_TEST1_DESCRIPTION + batch1.add(table) + batch1.flush() + + # Validate that the view was updated + assert batch1.num_updated == 1 + assert batch1.num_created == 0 + assert batch1.num_skipped == 0 + assert batch1.num_restored == 0 + + # Wait for assets to be indexed + sleep(5) + # Make sure user description should be updated on view + results = ( + FluentSearch() + .where(Asset.TYPE_NAME.eq(View.__name__)) + .where(Asset.QUALIFIED_NAME.eq(view.qualified_name)) + .include_on_results(Asset.USER_DESCRIPTION) + .execute(client=client) + ) + assert results and results.count == 1 + assert results.current_page() and len(results.current_page()) == 1 + updated_view = results.current_page()[0] + assert updated_view.qualified_name == view.qualified_name + assert updated_view.user_description == SUB_TEST1_DESCRIPTION + + # [sub-test-11]: Table with mview qn (table_view_agnostic=True, update_only=True) + # Expect the mview to be updated since `table_view_agnostic=True` and `update_only=True` + batch11 = Batch( + client=client, + track=True, + update_only=True, + table_view_agnostic=True, + max_size=BATCH_MAX_SIZE, + ) + SUB_TEST11_DESCRIPTION = f"[sub-test11] {DESCRIPTION}" + + table = Table.updater(qualified_name=mview.qualified_name, name=mview.name) + table.user_description = SUB_TEST11_DESCRIPTION + batch11.add(table) + batch11.flush() + + # Validate that the mview was updated + assert batch11.num_updated == 1 + assert batch11.num_created == 0 + assert batch11.num_skipped == 0 + assert batch11.num_restored == 0 + + # Wait for assets to be indexed + sleep(5) + # Make sure user description should be updated on mview + results = ( + FluentSearch() + .where(Asset.TYPE_NAME.eq(MaterialisedView.__name__)) + .where(Asset.QUALIFIED_NAME.eq(mview.qualified_name)) + .include_on_results(Asset.USER_DESCRIPTION) + .execute(client=client) + ) + assert results and results.count == 1 + assert results.current_page() and len(results.current_page()) == 1 + updated_mview = results.current_page()[0] + assert updated_mview.qualified_name == mview.qualified_name + assert updated_mview.user_description == SUB_TEST11_DESCRIPTION + + # [sub-test-2]: Table with view qn (table_view_agnostic=False, update_only=True) + # Expect the operation to be skipped since a table with the view's qualified name does not exist + batch2 = Batch( + client=client, + track=True, + update_only=True, + table_view_agnostic=False, + max_size=BATCH_MAX_SIZE, + ) + SUB_TEST2_DESCRIPTION = f"[sub-test2] {DESCRIPTION}" + + table = Table.updater(qualified_name=view.qualified_name, name=view.name) + table.user_description = SUB_TEST2_DESCRIPTION + batch2.add(table) + batch2.flush() + + # Neither create or update (since table_view_agnostic = False) + assert batch2.num_skipped == 1 + assert batch2.num_created == 0 + assert batch2.num_updated == 0 + assert batch2.num_restored == 0 + + # [sub-test-3]: Table with view qn (table_view_agnostic=False, update_only=False) + # Expect a new table to be created with the view's qualified name + batch3 = Batch( + client=client, + track=True, + update_only=False, + table_view_agnostic=False, + max_size=BATCH_MAX_SIZE, + ) + SUB_TEST3_DESCRIPTION = f"[sub-test3] {DESCRIPTION}" + + table = Table.updater(qualified_name=view.qualified_name, name=view.name) + table.user_description = SUB_TEST3_DESCRIPTION + batch3.add(table) + batch3.flush() + + # Validate that a new table with view qn was created + assert batch3.num_created == 1 + assert batch3.num_skipped == 0 + assert batch3.num_updated == 0 + assert batch3.num_restored == 0 + + # Wait for assets to be indexed + sleep(5) + results = ( + FluentSearch() + .where(Asset.TYPE_NAME.eq(Table.__name__)) + .where(Asset.QUALIFIED_NAME.eq(view.qualified_name)) + .include_on_results(Asset.USER_DESCRIPTION) + .execute(client=client) + ) + + assert results and results.count == 1 + assert results.current_page() and len(results.current_page()) == 1 + created_table = results.current_page()[0] + assert ( + created_table + and created_table.guid + and created_table.qualified_name == view.qualified_name + ) + # Verify the new table was created and has the updated user description + assert created_table.user_description == SUB_TEST3_DESCRIPTION + + # Cleanup: Delete the newly created table + response = client.asset.purge_by_guid(created_table.guid) + assert response.mutated_entities and response.mutated_entities.DELETE + + # Table with table qn + # 4. case_insensitive and update_only - update + # 5. not case_insensitive and update_only - update + # 6. not case_insensitive and update_only (same operation) - restore + # 7. case_insensitive and not update_only - create + + # [sub-test-4]: Table with table qn [lowercase] (case_insensitive=True, update_only=True) + # Expect the table to be updated + batch4 = Batch( + client=client, + track=True, + update_only=True, + case_insensitive=True, + max_size=BATCH_MAX_SIZE, + ) + SUB_TEST4_DESCRIPTION = f"[sub-test4] {DESCRIPTION}" + + table = Table.updater( + qualified_name=table1.qualified_name.lower(), name=table1.name + ) + table.user_description = SUB_TEST4_DESCRIPTION + batch4.add(table) + batch4.flush() + + # Validate that the table was updated + assert batch4.num_updated == 1 + assert batch4.num_created == 0 + assert batch4.num_skipped == 0 + assert batch4.num_restored == 0 + + # Wait for assets to be indexed + sleep(5) + results = ( + FluentSearch() + .where(Asset.TYPE_NAME.eq(Table.__name__)) + .where(Asset.QUALIFIED_NAME.eq(table1.qualified_name)) + .include_on_results(Asset.USER_DESCRIPTION) + .execute(client=client) + ) + + assert results and results.count == 1 + assert results.current_page() and len(results.current_page()) == 1 + updated_table = results.current_page()[0] + assert ( + updated_table + and updated_table.guid + and updated_table.qualified_name == table1.qualified_name + ) + assert updated_table.user_description == SUB_TEST4_DESCRIPTION + + # [sub-test-5]: Table with table qn (case_insensitive=False, update_only=True) + # Expect the table to be updated + batch5 = Batch( + client=client, + track=True, + update_only=True, + case_insensitive=False, + max_size=BATCH_MAX_SIZE, + ) + SUB_TEST5_DESCRIPTION = f"[sub-test5] {DESCRIPTION}" + + table = Table.updater(qualified_name=table1.qualified_name, name=table1.name) + table.user_description = SUB_TEST5_DESCRIPTION + batch5.add(table) + batch5.flush() + + # Validate that the table was updated + assert batch5.num_updated == 1 + assert batch5.num_created == 0 + assert batch5.num_skipped == 0 + assert batch5.num_restored == 0 + + # Wait for assets to be indexed + sleep(5) + results = ( + FluentSearch() + .where(Asset.TYPE_NAME.eq(Table.__name__)) + .where(Asset.QUALIFIED_NAME.eq(table1.qualified_name)) + .include_on_results(Asset.USER_DESCRIPTION) + .execute(client=client) + ) + + assert results and results.count == 1 + assert results.current_page() and len(results.current_page()) == 1 + updated_table = results.current_page()[0] + assert ( + updated_table + and updated_table.guid + and updated_table.qualified_name == table1.qualified_name + ) + assert updated_table.user_description == SUB_TEST5_DESCRIPTION + + # [sub-test-6]: (same operation as sub-test-5) + # Table with table qn (case_insensitive=False, update_only=True) + # Expect no operation as update is identical + batch6 = Batch( + client=client, + track=True, + update_only=True, + case_insensitive=False, + max_size=BATCH_MAX_SIZE, + ) + + table = Table.updater(qualified_name=table1.qualified_name, name=table1.name) + # Use the same user description as before + table.user_description = SUB_TEST5_DESCRIPTION + batch6.add(table) + batch6.flush() + + # No operation as update is identical + assert batch6.num_restored == 1 + assert batch6.num_created == 0 + assert batch6.num_updated == 0 + assert batch6.num_skipped == 0 + + # [sub-test-7]: (Table with table qn (case_insensitive=True, update_only=False) + # Expect table to be created + batch7 = Batch( + client=client, + track=True, + update_only=False, + case_insensitive=False, + max_size=BATCH_MAX_SIZE, + # Also test partial creation handling + creation_handling=AssetCreationHandling.PARTIAL, + ) + SUB_TEST7_DESCRIPTION = f"[sub-test7] {DESCRIPTION}" + + table = Table.updater( + qualified_name=table1.qualified_name.lower(), name=table1.name + ) + table.user_description = SUB_TEST7_DESCRIPTION + batch7.add(table) + batch7.flush() + + # Validate that the table was created + assert batch7.num_created == 1 + assert batch7.num_updated == 0 + assert batch7.num_skipped == 0 + assert batch7.num_restored == 0 + + # Wait for assets to be indexed + sleep(5) + results = ( + FluentSearch() + .where(Asset.TYPE_NAME.eq(Table.__name__)) + .where(Asset.QUALIFIED_NAME.eq(table.qualified_name)) + .include_on_results(Asset.IS_PARTIAL) + .include_on_results(Asset.USER_DESCRIPTION) + .execute(client=client) + ) + + assert results and results.count == 1 + assert results.current_page() and len(results.current_page()) == 1 + created_table = results.current_page()[0] + + assert ( + created_table + and created_table.guid + and created_table.qualified_name == table.qualified_name + ) + assert created_table.is_partial + assert created_table.user_description == SUB_TEST7_DESCRIPTION + + # Cleanup: Delete the created table + response = client.asset.purge_by_guid(created_table.guid) + assert response.mutated_entities and response.mutated_entities.DELETE diff --git a/tests_v9/integration/test_client.py b/tests_v9/integration/test_client.py new file mode 100644 index 000000000..24c6c72e7 --- /dev/null +++ b/tests_v9/integration/test_client.py @@ -0,0 +1,1784 @@ +import time +from dataclasses import dataclass +from typing import Generator, List, Optional, Type +from unittest.mock import patch + +import pytest +from httpx import Headers + +from pyatlan import __version__ as VERSION +from pyatlan.client.common.audit import LOGGER as AUDIT_LOGGER +from pyatlan.client.common.search_log import LOGGER as SEARCH_LOG_LOGGER +from pyatlan.pkg.utils import get_client +from pyatlan.utils import get_python_version +from pyatlan_v9.client.atlan import DEFAULT_RETRY, AtlanClient +from pyatlan_v9.errors import AuthenticationError, InvalidRequestError, NotFoundError +from pyatlan_v9.model.api_tokens import ApiToken +from pyatlan_v9.model.assets import ( + Asset, + AtlasGlossary, + AtlasGlossaryCategory, + AtlasGlossaryTerm, + Connection, + Database, + Schema, + Table, +) +from pyatlan_v9.model.audit import AuditSearchRequest, AuditSearchResults +from pyatlan_v9.model.core import Announcement +from pyatlan_v9.model.enums import ( + AnnouncementType, + AtlanConnectorType, + CertificateStatus, + SortOrder, + UTMTags, +) +from pyatlan_v9.model.fluent_search import CompoundQuery, FluentSearch +from pyatlan_v9.model.search import ( + DSL, + Bool, + IndexSearchRequest, + IndexSearchRequestMetadata, + SortItem, + Term, +) +from pyatlan_v9.model.search_log import ( + AssetViews, + SearchLogRequest, + SearchLogResults, + SearchLogViewResults, +) +from pyatlan_v9.model.user import UserMinimalResponse +from tests_v9.integration.client import TestId +from tests_v9.integration.lineage_test import create_database, delete_asset +from tests_v9.integration.requests_test import create_token, delete_token + +CLASSIFICATION_NAME = "Issue" +CLASSIFICATION_NAME2 = "Confidential" +SL_SORT_BY_TIMESTAMP = SortItem(field="timestamp", order=SortOrder.ASCENDING) +SL_SORT_BY_GUID = SortItem(field="entityGuidsAll", order=SortOrder.ASCENDING) +SL_SORT_BY_QUALIFIED_NAME = SortItem( + field="entityQFNamesAll", order=SortOrder.ASCENDING +) +AUDIT_SORT_BY_GUID = SortItem(field="entityId", order=SortOrder.ASCENDING) +AUDIT_SORT_BY_LATEST = SortItem("created", order=SortOrder.DESCENDING) +MODULE_NAME = TestId.make_unique("Client") +TEST_USER_DESCRIPTION = "Automated testing of the Python SDK. (USER)" +TEST_SYSTEM_DESCRIPTION = "Automated testing of the Python SDK. (SYSTEM)" +call_count = 0 + + +@pytest.fixture(scope="module") +def token(token_client: AtlanClient) -> Generator[ApiToken, None, None]: + token = None + try: + token = create_token(token_client, MODULE_NAME) + yield token + finally: + delete_token(token_client, token) + + +@pytest.fixture(scope="module") +def expired_token(token_client: AtlanClient) -> Generator[ApiToken, None, None]: + token = None + try: + token = token_client.token.creator(f"{MODULE_NAME}-expired", validity_seconds=1) + time.sleep(5) + yield token + finally: + delete_token(token_client, token) + + +@pytest.fixture(scope="module") +def argo_fake_token(token_client: AtlanClient) -> Generator[ApiToken, None, None]: + token = None + try: + token = token_client.token.creator(f"{MODULE_NAME}-fake-argo") + yield token + finally: + delete_token(token_client, token) + + +@pytest.fixture(scope="module") +def glossary( + client: AtlanClient, +) -> Generator[AtlasGlossary, None, None]: + g = AtlasGlossary.creator(name=MODULE_NAME) + g.description = TEST_SYSTEM_DESCRIPTION + g.user_description = TEST_USER_DESCRIPTION + response = client.asset.save(g) + result = response.assets_created(AtlasGlossary)[0] + assert result + yield result + delete_asset(client, guid=g.guid, asset_type=AtlasGlossary) + + +@pytest.fixture(scope="module") +def term( + client: AtlanClient, glossary: AtlasGlossary +) -> Generator[AtlasGlossaryTerm, None, None]: + t = AtlasGlossaryTerm.creator( + name=MODULE_NAME, + glossary_guid=glossary.guid, + ) + t.description = f"{TEST_SYSTEM_DESCRIPTION} Term" + t.user_description = f"{TEST_USER_DESCRIPTION} Term" + response = client.asset.save(t) + result = response.assets_created(AtlasGlossaryTerm)[0] + assert result + yield result + delete_asset(client, guid=t.guid, asset_type=AtlasGlossaryTerm) + + +@dataclass() +class AuditInfo: + qualified_name: str = "" + type_name: str = "" + guid: str = "" + + +@pytest.fixture(scope="module") +def audit_info(): + return AuditInfo() + + +@pytest.fixture() +def announcement(): + return Announcement( + announcement_title="Important Announcement", + announcement_message="Very important info", + announcement_type=AnnouncementType.ISSUE, + ) + + +@pytest.fixture() +def database( + client: AtlanClient, connection: Connection +) -> Generator[Database, None, None]: + """Get a database with function scope""" + database_name = TestId.make_unique("my_db") + db = create_database(client, connection, database_name) + yield db + delete_asset(client, guid=db.guid, asset_type=Database) + + +@pytest.fixture() +def schema_with_db_qn( + client: AtlanClient, + database: Database, +) -> Generator[Schema, None, None]: + assert database.qualified_name + schema_name = TestId.make_unique("my_schema") + to_create = Schema.creator( + name=schema_name, database_qualified_name=database.qualified_name + ) + to_create.qualified_name = database.qualified_name + result = client.asset.save(to_create) + sch = result.assets_created(asset_type=Schema)[0] + yield sch + delete_asset(client, guid=sch.guid, asset_type=Schema) + + +@pytest.fixture() +def current_user(client: AtlanClient) -> UserMinimalResponse: + return client.user.get_current() + + +def create_glossary(client: AtlanClient, name: str) -> AtlasGlossary: + g = AtlasGlossary.creator(name=name) + r = client.asset.save(g) + return r.assets_created(AtlasGlossary)[0] + + +@pytest.fixture(scope="module") +def audit_glossary(client: AtlanClient) -> Generator[AtlasGlossary, None, None]: + created_glossary = create_glossary( + client, TestId.make_unique("test-audit-glossary") + ) + yield created_glossary + delete_asset(client, guid=created_glossary.guid, asset_type=AtlasGlossary) + + +@pytest.fixture(scope="module") +def sl_glossary( + client: AtlanClient, +) -> Generator[AtlasGlossary, None, None]: + g = create_glossary(client, TestId.make_unique("test-sl-glossary")) + yield g + delete_asset(client, guid=g.guid, asset_type=AtlasGlossary) + + +def _test_update_certificate( + client: AtlanClient, + test_asset: Asset, + test_asset_type: Type[Asset], + glossary_guid: Optional[str] = None, +): + assert test_asset.qualified_name + assert test_asset.name + test_asset = client.asset.get_by_guid( + guid=test_asset.guid, asset_type=test_asset_type, ignore_relationships=False + ) + assert test_asset.qualified_name + assert test_asset.name + assert not test_asset.certificate_status + assert not test_asset.certificate_status_message + message = "An important message" + client.asset.update_certificate( + asset_type=test_asset_type, + qualified_name=test_asset.qualified_name, + name=test_asset.name, + certificate_status=CertificateStatus.DRAFT, + message=message, + glossary_guid=glossary_guid if glossary_guid else None, + ) + test_asset = client.asset.get_by_guid( + guid=test_asset.guid, asset_type=test_asset_type, ignore_relationships=False + ) + assert test_asset.certificate_status == CertificateStatus.DRAFT + assert test_asset.certificate_status_message == message + + +def _test_remove_certificate( + client: AtlanClient, + test_asset: Asset, + test_asset_type: Type[Asset], + glossary_guid: Optional[str] = None, +): + assert test_asset.qualified_name + assert test_asset.name + client.asset.remove_certificate( + asset_type=test_asset_type, + qualified_name=test_asset.qualified_name, + name=test_asset.name, + glossary_guid=glossary_guid if glossary_guid else None, + ) + test_asset = client.asset.get_by_guid( + guid=test_asset.guid, asset_type=test_asset_type, ignore_relationships=False + ) + assert not test_asset.certificate_status + assert not test_asset.certificate_status_message + + +def _test_update_announcement( + client: AtlanClient, + test_asset: Asset, + test_asset_type: Type[Asset], + test_announcement: Announcement, + glossary_guid: Optional[str] = None, +): + assert test_asset.qualified_name + assert test_asset.name + client.asset.update_announcement( + asset_type=test_asset_type, + qualified_name=test_asset.qualified_name, + name=test_asset.name, + announcement=test_announcement, + glossary_guid=glossary_guid if glossary_guid else None, + ) + test_asset = client.asset.get_by_guid( + guid=test_asset.guid, asset_type=test_asset_type, ignore_relationships=False + ) + assert test_asset.get_announcment() == test_announcement + + +def _test_remove_announcement( + client: AtlanClient, + test_asset: Asset, + test_asset_type: Type[Asset], + glossary_guid: Optional[str] = None, +): + assert test_asset.qualified_name + assert test_asset.name + client.asset.remove_announcement( + asset_type=test_asset_type, + qualified_name=test_asset.qualified_name, + name=test_asset.name, + glossary_guid=glossary_guid if glossary_guid else None, + ) + test_asset = client.asset.get_by_guid( + guid=test_asset.guid, asset_type=test_asset_type, ignore_relationships=False + ) + assert test_asset.get_announcment() is None + + +def test_append_terms_with_guid( + client: AtlanClient, + term1: AtlasGlossaryTerm, + database: Database, +): + time.sleep(5) + assert ( + database := client.asset.append_terms( + guid=database.guid, asset_type=Database, terms=[term1] + ) + ) + # Wait for indexing before fetching + time.sleep(2) + database = client.asset.get_by_guid( + guid=database.guid, asset_type=Database, ignore_relationships=False + ) + assert database.assigned_terms + assert len(database.assigned_terms) == 1 + assert database.assigned_terms[0].guid == term1.guid + + +def test_append_terms_with_qualified_name( + client: AtlanClient, + term1: AtlasGlossaryTerm, + database: Database, +): + time.sleep(5) + assert ( + database := client.asset.append_terms( + qualified_name=database.qualified_name, asset_type=Database, terms=[term1] + ) + ) + database = client.asset.get_by_guid( + guid=database.guid, asset_type=Database, ignore_relationships=False + ) + assert database.assigned_terms + assert len(database.assigned_terms) == 1 + assert database.assigned_terms[0].guid == term1.guid + + +def test_append_terms_using_ref_by_guid_for_term( + client: AtlanClient, + term1: AtlasGlossaryTerm, + database: Database, +): + time.sleep(5) + assert ( + database := client.asset.append_terms( + qualified_name=database.qualified_name, + asset_type=Database, + terms=[AtlasGlossaryTerm.ref_by_guid(guid=term1.guid)], + ) + ) + database = client.asset.get_by_guid( + guid=database.guid, asset_type=Database, ignore_relationships=False + ) + assert database.assigned_terms + assert len(database.assigned_terms) == 1 + assert database.assigned_terms[0].guid == term1.guid + + +def test_append_terms_with_same_qn( + client: AtlanClient, + term1: AtlasGlossaryTerm, + database: Database, + schema_with_db_qn: Schema, +): + time.sleep(5) + assert schema_with_db_qn.qualified_name == database.qualified_name + assert ( + database := client.asset.append_terms( + qualified_name=database.qualified_name, + asset_type=Database, + terms=[AtlasGlossaryTerm.ref_by_guid(guid=term1.guid)], + ) + ) + assert ( + schema_with_db_qn := client.asset.append_terms( + qualified_name=schema_with_db_qn.qualified_name, + asset_type=Schema, + terms=[AtlasGlossaryTerm.ref_by_guid(guid=term1.guid)], + ) + ) + + +def test_replace_a_term( + client: AtlanClient, + term1: AtlasGlossaryTerm, + term2: AtlasGlossaryTerm, + database: Database, +): + time.sleep(5) + assert ( + database := client.asset.append_terms( + qualified_name=database.qualified_name, + asset_type=Database, + terms=[AtlasGlossaryTerm.ref_by_guid(guid=term1.guid)], + ) + ) + + assert ( + database := client.asset.replace_terms( + guid=database.guid, asset_type=Database, terms=[term2] + ) + ) + + database = client.asset.get_by_guid( + guid=database.guid, asset_type=Database, ignore_relationships=False + ) + assert database.assigned_terms + assert len(database.assigned_terms) == 1 + assert database.assigned_terms[0].guid == term2.guid + + +def test_replace_terms_with_same_qn( + client: AtlanClient, + term2: AtlasGlossaryTerm, + database: Database, + schema_with_db_qn: Schema, +): + time.sleep(5) + assert schema_with_db_qn.qualified_name == database.qualified_name + assert ( + database := client.asset.replace_terms( + guid=database.guid, asset_type=Database, terms=[term2] + ) + ) + assert ( + schema_with_db_qn := client.asset.replace_terms( + guid=schema_with_db_qn.guid, asset_type=Schema, terms=[term2] + ) + ) + + +def test_replace_all_term( + client: AtlanClient, + term1: AtlasGlossaryTerm, + database: Database, +): + time.sleep(5) + assert ( + database := client.asset.append_terms( + qualified_name=database.qualified_name, + asset_type=Database, + terms=[AtlasGlossaryTerm.ref_by_guid(guid=term1.guid)], + ) + ) + + assert ( + database := client.asset.replace_terms( + guid=database.guid, asset_type=Database, terms=[] + ) + ) + + database = client.asset.get_by_guid( + guid=database.guid, asset_type=Database, ignore_relationships=False + ) + assert database.assigned_terms == [] + assert len(database.assigned_terms) == 0 + + +def test_remove_term( + client: AtlanClient, + term1: AtlasGlossaryTerm, + term2: AtlasGlossaryTerm, + database: Database, +): + time.sleep(5) + assert ( + database := client.asset.append_terms( + qualified_name=database.qualified_name, + asset_type=Database, + terms=[ + AtlasGlossaryTerm.ref_by_guid(guid=term1.guid), + AtlasGlossaryTerm.ref_by_guid(guid=term2.guid), + ], + ) + ) + + assert ( + database := client.asset.remove_terms( + guid=database.guid, + asset_type=Database, + terms=[AtlasGlossaryTerm.ref_by_guid(term1.guid)], + ) + ) + + database = client.asset.get_by_guid( + guid=database.guid, asset_type=Database, ignore_relationships=False + ) + assert database.assigned_terms + assert len(database.assigned_terms) == 1 + assert database.assigned_terms[0].guid == term2.guid + + +def test_remove_terms_with_same_qn( + client: AtlanClient, + term1: AtlasGlossaryTerm, + term2: AtlasGlossaryTerm, + database: Database, + schema_with_db_qn: Schema, +): + time.sleep(5) + assert schema_with_db_qn.qualified_name == database.qualified_name + assert ( + database := client.asset.append_terms( + qualified_name=database.qualified_name, + asset_type=Database, + terms=[ + AtlasGlossaryTerm.ref_by_guid(guid=term1.guid), + AtlasGlossaryTerm.ref_by_guid(guid=term2.guid), + ], + ) + ) + assert ( + database := client.asset.remove_terms( + guid=database.guid, + asset_type=Database, + terms=[AtlasGlossaryTerm.ref_by_guid(term1.guid)], + ) + ) + assert ( + schema_with_db_qn := client.asset.append_terms( + qualified_name=schema_with_db_qn.qualified_name, + asset_type=Schema, + terms=[ + AtlasGlossaryTerm.ref_by_guid(guid=term1.guid), + AtlasGlossaryTerm.ref_by_guid(guid=term2.guid), + ], + ) + ) + assert ( + schema_with_db_qn := client.asset.remove_terms( + guid=schema_with_db_qn.guid, + asset_type=Schema, + terms=[AtlasGlossaryTerm.ref_by_guid(term1.guid)], + ) + ) + + +def test_find_connections_by_name(client: AtlanClient): + connections = client.asset.find_connections_by_name( + name="development", + connector_type=AtlanConnectorType.SNOWFLAKE, + attributes=["connectorName"], + ) + assert len(connections) == 1 + assert connections[0].connector_name == AtlanConnectorType.SNOWFLAKE.value + + +def test_get_asset_by_guid_good_guid(client: AtlanClient, glossary: AtlasGlossary): + glossary = client.asset.get_by_guid( + glossary.guid, AtlasGlossary, ignore_relationships=False + ) + assert isinstance(glossary, AtlasGlossary) + + +def test_get_asset_by_guid_without_asset_type( + client: AtlanClient, glossary: AtlasGlossary +): + glossary = client.asset.get_by_guid(glossary.guid, ignore_relationships=False) + assert isinstance(glossary, AtlasGlossary) + + +def test_get_minimal_asset_without_asset_type( + client: AtlanClient, glossary: AtlasGlossary +): + glossary = client.asset.retrieve_minimal(glossary.guid) + assert isinstance(glossary, AtlasGlossary) + + +def test_get_asset_by_guid_when_table_specified_and_glossary_returned_raises_not_found_error( + client: AtlanClient, glossary: AtlasGlossary +): + guid = glossary.guid + with pytest.raises( + NotFoundError, + match=f"ATLAN-PYTHON-404-002 Asset with GUID {guid} is not of the type requested: Table.", + ): + client.asset.get_by_guid(guid, Table, ignore_relationships=False) + + +def test_get_by_guid_with_fs(client: AtlanClient, term: AtlasGlossaryTerm): + time.sleep(5) + # Default - should call `GET_ENTITY_BY_GUID` API + result = client.asset.get_by_guid(guid=term.guid, asset_type=AtlasGlossaryTerm) + assert isinstance(result, AtlasGlossaryTerm) + assert result.guid == term.guid + assert hasattr(result, "attributes") + assert result.attributes.name == term.name + assert result.attributes.qualified_name == term.qualified_name + assert result.description == f"{TEST_SYSTEM_DESCRIPTION} Term" + assert result.user_description == f"{TEST_USER_DESCRIPTION} Term" + # Ensure no relationship attributes are present + assert not result.anchor + + # Should call `GET_ENTITY_BY_GUID` API with `ignore_relationships=False` + result = client.asset.get_by_guid( + guid=term.guid, asset_type=AtlasGlossaryTerm, ignore_relationships=False + ) + assert isinstance(result, AtlasGlossaryTerm) + assert result.guid == term.guid + assert hasattr(result, "attributes") + assert result.attributes.name == term.name + assert result.attributes.qualified_name == term.qualified_name + assert result.description == f"{TEST_SYSTEM_DESCRIPTION} Term" + assert result.user_description == f"{TEST_USER_DESCRIPTION} Term" + assert result.anchor + # These are not returned by the `GET_ENTITY_BY_GUID` API + assert not result.anchor.description + assert not result.anchor.user_description + + # Should call IndexSearch API without any relationship attributes + result = client.asset.get_by_guid( + guid=term.guid, + asset_type=AtlasGlossaryTerm, + ignore_relationships=False, + attributes=[AtlasGlossaryTerm.DESCRIPTION, AtlasGlossaryTerm.USER_DESCRIPTION], # type: ignore[arg-type] + ) + assert isinstance(result, AtlasGlossaryTerm) + assert result.guid == term.guid + assert hasattr(result, "attributes") + assert result.attributes.name == term.name + assert result.attributes.qualified_name == term.qualified_name + assert result.description == f"{TEST_SYSTEM_DESCRIPTION} Term" + assert result.user_description == f"{TEST_USER_DESCRIPTION} Term" + # Ensure no relationship attributes are present + assert not result.anchor + + # Should call IndexSearch API + result = client.asset.get_by_guid( + guid=term.guid, + asset_type=AtlasGlossaryTerm, + ignore_relationships=False, + attributes=["description", "userDescription", "anchor"], + related_attributes=[ # type: ignore[arg-type] + AtlasGlossary.DESCRIPTION, + AtlasGlossary.USER_DESCRIPTION, + ], + ) + assert isinstance(result, AtlasGlossaryTerm) + assert result.guid == term.guid + assert hasattr(result, "attributes") + assert result.attributes.name == term.name + assert result.attributes.qualified_name == term.qualified_name + assert result.description == f"{TEST_SYSTEM_DESCRIPTION} Term" + assert result.user_description == f"{TEST_USER_DESCRIPTION} Term" + assert result.anchor + assert result.anchor.description == f"{TEST_SYSTEM_DESCRIPTION}" + assert result.anchor.user_description == f"{TEST_USER_DESCRIPTION}" + + +def test_get_by_qualified_name_with_fs(client: AtlanClient, term: AtlasGlossaryTerm): + time.sleep(5) + # Default - should call `GET_ENTITY_BY_GUID` API + assert term and term.qualified_name + result = client.asset.get_by_qualified_name( + qualified_name=term.qualified_name, asset_type=AtlasGlossaryTerm + ) + assert isinstance(result, AtlasGlossaryTerm) + assert result.guid == term.guid + assert hasattr(result, "attributes") + assert result.attributes.name == term.name + assert result.attributes.qualified_name == term.qualified_name + assert result.description == f"{TEST_SYSTEM_DESCRIPTION} Term" + assert result.user_description == f"{TEST_USER_DESCRIPTION} Term" + # Ensure no relationship attributes are present + assert not result.anchor + + # Should call `GET_ENTITY_BY_GUID` API with `ignore_relationships=False` + result = client.asset.get_by_qualified_name( + qualified_name=term.qualified_name, + asset_type=AtlasGlossaryTerm, + ignore_relationships=False, + ) + assert isinstance(result, AtlasGlossaryTerm) + assert result.guid == term.guid + assert hasattr(result, "attributes") + assert result.attributes.name == term.name + assert result.attributes.qualified_name == term.qualified_name + assert result.description == f"{TEST_SYSTEM_DESCRIPTION} Term" + assert result.user_description == f"{TEST_USER_DESCRIPTION} Term" + assert result.anchor + # These are not returned by the `GET_ENTITY_BY_GUID` API + assert not result.anchor.description + assert not result.anchor.user_description + + # Should call IndexSearch API without any relationship attributes + result = client.asset.get_by_qualified_name( + qualified_name=term.qualified_name, + asset_type=AtlasGlossaryTerm, + ignore_relationships=False, + attributes=[AtlasGlossaryTerm.DESCRIPTION, AtlasGlossaryTerm.USER_DESCRIPTION], # type: ignore[arg-type] + ) + assert isinstance(result, AtlasGlossaryTerm) + assert result.guid == term.guid + assert hasattr(result, "attributes") + assert result.attributes.name == term.name + assert result.attributes.qualified_name == term.qualified_name + assert result.description == f"{TEST_SYSTEM_DESCRIPTION} Term" + assert result.user_description == f"{TEST_USER_DESCRIPTION} Term" + # Ensure no relationship attributes are present + assert not result.anchor + + # Should call IndexSearch API + result = client.asset.get_by_qualified_name( + qualified_name=term.qualified_name, + asset_type=AtlasGlossaryTerm, + ignore_relationships=False, + attributes=["description", "userDescription", "anchor"], + related_attributes=[ # type: ignore[arg-type] + AtlasGlossary.DESCRIPTION, + AtlasGlossary.USER_DESCRIPTION, + ], + ) + assert isinstance(result, AtlasGlossaryTerm) + assert result.guid == term.guid + assert hasattr(result, "attributes") + assert result.attributes.name == term.name + assert result.attributes.qualified_name == term.qualified_name + assert result.description == f"{TEST_SYSTEM_DESCRIPTION} Term" + assert result.user_description == f"{TEST_USER_DESCRIPTION} Term" + assert result.anchor + assert result.anchor.description == f"{TEST_SYSTEM_DESCRIPTION}" + assert result.anchor.user_description == f"{TEST_USER_DESCRIPTION}" + + +def test_get_asset_by_guid_bad_with_non_existent_guid_raises_not_found_error( + client: AtlanClient, +): + with pytest.raises( + NotFoundError, + match="ATLAN-PYTHON-404-000 Server responded with a not found " + "error ATLAS-404-00-005: Given instance guid 76d54dd6 is invalid/not found", + ): + client.asset.get_by_guid("76d54dd6", AtlasGlossary, ignore_relationships=False) + + +def test_upsert_when_no_changes(client: AtlanClient, glossary: AtlasGlossary): + response = client.asset.save(glossary) + assert not response.guid_assignments + assert not response.mutated_entities + + +def test_get_by_qualified_name(client: AtlanClient, glossary: AtlasGlossary): + qualified_name = glossary.qualified_name or "" + glossary = client.asset.get_by_qualified_name( + qualified_name=qualified_name, asset_type=AtlasGlossary + ) + assert glossary.attributes.qualified_name == qualified_name + + +def test_get_by_qualified_name_when_superclass_specified_raises_not_found_error( + client: AtlanClient, glossary: AtlasGlossary +): + qualified_name = glossary.qualified_name or "" + with pytest.raises( + NotFoundError, + match="ATLAN-PYTHON-404-014 The Asset asset could not be found by name: ", + ): + client.asset.get_by_qualified_name( + qualified_name=qualified_name, asset_type=Asset + ) + + +def test_add_classification(client: AtlanClient, term1: AtlasGlossaryTerm): + assert term1.qualified_name + client.asset.add_atlan_tags( + AtlasGlossaryTerm, term1.qualified_name, [CLASSIFICATION_NAME] + ) + glossary_term = client.asset.get_by_guid(term1.guid, asset_type=AtlasGlossaryTerm) + assert glossary_term.atlan_tags + assert len(glossary_term.atlan_tags) == 1 + classification = glossary_term.atlan_tags[0] + assert str(classification.type_name) == CLASSIFICATION_NAME + + +@pytest.mark.order(after="test_add_classification") +def test_include_atlan_tag_names(client: AtlanClient, term1: AtlasGlossaryTerm): + assert term1 and term1.qualified_name + query = Term.with_type_name(term1.type_name) + Term.with_name(term1.name) + request = IndexSearchRequest( + dsl=DSL(query=query), exclude_atlan_tags=True, include_atlan_tag_names=False + ) + response = client.asset.search(criteria=request) + + # Ensure classification names are not present + assert response + assert response.current_page() and len(response.current_page()) == 1 + assert response.current_page()[0].guid == term1.guid + assert not response.current_page()[0].classification_names + + request = IndexSearchRequest( + dsl=DSL(query=query), exclude_atlan_tags=True, include_atlan_tag_names=True + ) + response = client.asset.search(criteria=request) + + # Ensure classification names are present + assert response + assert response.current_page() and len(response.current_page()) == 1 + assert response.current_page()[0].guid == term1.guid + classification_names = response.current_page()[0].classification_names + assert classification_names and len(classification_names) == 1 + + +@pytest.mark.order(after="test_add_classification") +def test_update_classification(client: AtlanClient, term1: AtlasGlossaryTerm): + assert term1.qualified_name + client.asset.update_atlan_tags( + AtlasGlossaryTerm, + term1.qualified_name, + [CLASSIFICATION_NAME], + propagate=True, + remove_propagation_on_delete=False, + ) + glossary_term = client.asset.get_by_guid(term1.guid, asset_type=AtlasGlossaryTerm) + assert glossary_term.atlan_tags + assert len(glossary_term.atlan_tags) == 1 + classification = glossary_term.atlan_tags[0] + assert str(classification.type_name) == CLASSIFICATION_NAME + assert classification.propagate + assert not classification.remove_propagations_on_entity_delete + assert classification.restrict_propagation_through_lineage + assert not classification.restrict_propagation_through_hierarchy + + +@pytest.mark.order(after="test_update_classification") +def test_remove_classification(client: AtlanClient, term1: AtlasGlossaryTerm): + assert term1.qualified_name + client.asset.remove_atlan_tag( + AtlasGlossaryTerm, term1.qualified_name, CLASSIFICATION_NAME + ) + glossary_term = client.asset.get_by_guid(term1.guid, asset_type=AtlasGlossaryTerm) + assert not glossary_term.atlan_tags + + +def test_multiple_add_classification(client: AtlanClient, term1: AtlasGlossaryTerm): + assert term1.qualified_name + client.asset.add_atlan_tags( + AtlasGlossaryTerm, + term1.qualified_name, + [CLASSIFICATION_NAME, CLASSIFICATION_NAME2], + ) + glossary_term = client.asset.get_by_guid(term1.guid, asset_type=AtlasGlossaryTerm) + assert glossary_term.atlan_tags + assert len(glossary_term.atlan_tags) == 2 + classification = glossary_term.atlan_tags[0] + assert str(classification.type_name) == CLASSIFICATION_NAME or CLASSIFICATION_NAME2 + classification2 = glossary_term.atlan_tags[1] + assert str(classification2.type_name) == CLASSIFICATION_NAME or CLASSIFICATION_NAME2 + + +def test_multiple_update_classification(client: AtlanClient, term1: AtlasGlossaryTerm): + assert term1.qualified_name + client.asset.update_atlan_tags( + AtlasGlossaryTerm, + term1.qualified_name, + [CLASSIFICATION_NAME, CLASSIFICATION_NAME2], + propagate=True, + remove_propagation_on_delete=False, + ) + glossary_term = client.asset.get_by_guid(term1.guid, asset_type=AtlasGlossaryTerm) + assert glossary_term.atlan_tags + assert len(glossary_term.atlan_tags) == 2 + classification = glossary_term.atlan_tags[0] + assert str(classification.type_name) == CLASSIFICATION_NAME or CLASSIFICATION_NAME2 + assert classification.propagate + assert not classification.remove_propagations_on_entity_delete + assert classification.restrict_propagation_through_lineage + assert not classification.restrict_propagation_through_hierarchy + classification2 = glossary_term.atlan_tags[1] + assert str(classification2.type_name) == CLASSIFICATION_NAME or CLASSIFICATION_NAME2 + assert classification2.propagate + assert not classification2.remove_propagations_on_entity_delete + assert classification2.restrict_propagation_through_lineage + assert not classification2.restrict_propagation_through_hierarchy + + +@pytest.mark.order(after="test_multiple_add_classification") +def test_multiple_remove_classification(client: AtlanClient, term1: AtlasGlossaryTerm): + assert term1.qualified_name + client.asset.remove_atlan_tags( + AtlasGlossaryTerm, + term1.qualified_name, + [CLASSIFICATION_NAME, CLASSIFICATION_NAME2], + ) + glossary_term = client.asset.get_by_guid(term1.guid, asset_type=AtlasGlossaryTerm) + assert not glossary_term.atlan_tags + + +def test_glossary_update_certificate(client: AtlanClient, glossary: AtlasGlossary): + _test_update_certificate(client, glossary, AtlasGlossary) + + +def test_glossary_term_update_certificate( + client: AtlanClient, term1: AtlasGlossaryTerm, glossary: AtlasGlossary +): + _test_update_certificate(client, term1, AtlasGlossaryTerm, glossary.guid) + + +def test_glossary_category_update_certificate( + client: AtlanClient, category: AtlasGlossaryCategory, glossary: AtlasGlossary +): + _test_update_certificate(client, category, AtlasGlossaryCategory, glossary.guid) + + +@pytest.mark.order(after="test_glossary_update_certificate") +def test_glossary_remove_certificate(client: AtlanClient, glossary: AtlasGlossary): + _test_remove_certificate(client, glossary, AtlasGlossary) + + +@pytest.mark.order(after="test_glossary_term_update_certificate") +def test_glossary_term_remove_certificate( + client: AtlanClient, term1: AtlasGlossaryTerm, glossary: AtlasGlossary +): + _test_remove_certificate(client, term1, AtlasGlossaryTerm, glossary.guid) + + +@pytest.mark.order(after="test_glossary_category_update_certificate") +def test_glossary_category_remove_certificate( + client: AtlanClient, category: AtlasGlossaryCategory, glossary: AtlasGlossary +): + _test_remove_certificate(client, category, AtlasGlossaryCategory, glossary.guid) + + +def test_glossary_update_announcement( + client: AtlanClient, glossary: AtlasGlossary, announcement: Announcement +): + _test_update_announcement(client, glossary, AtlasGlossary, announcement) + + +def test_asset_remove_certificate_by_setting_none( + client: AtlanClient, + database: Database, +): + assert database + assert database.guid + assert database.certificate_status + assert database.certificate_status_message + database.certificate_status = None + database.certificate_status_message = None + response = client.asset.save(entity=[database]) + db_updated = response.assets_updated(asset_type=Database) + + assert db_updated + assert len(db_updated) == 1 + assert db_updated[0].name == database.name + assert db_updated[0].guid == database.guid + assert not db_updated[0].certificate_status + assert not db_updated[0].certificate_status_message + + +def test_glossary_term_update_announcement( + client: AtlanClient, + term1: AtlasGlossaryTerm, + glossary: AtlasGlossary, + announcement: Announcement, +): + _test_update_announcement( + client, term1, AtlasGlossaryTerm, announcement, glossary.guid + ) + + +def test_glossary_category_update_announcement( + client: AtlanClient, + category: AtlasGlossaryCategory, + glossary: AtlasGlossary, + announcement: Announcement, +): + _test_update_announcement( + client, category, AtlasGlossaryCategory, announcement, glossary.guid + ) + + +@pytest.mark.order(after="test_glossary_update_announcement") +def test_glossary_remove_announcement(client: AtlanClient, glossary: AtlasGlossary): + _test_remove_announcement(client, glossary, AtlasGlossary) + + +@pytest.mark.order(after="test_glossary_term_update_announcement") +def test_glossary_term_remove_announcement( + client: AtlanClient, term1: AtlasGlossaryTerm, glossary: AtlasGlossary +): + _test_remove_announcement(client, term1, AtlasGlossaryTerm, glossary.guid) + + +@pytest.mark.order(after="test_glossary_category_update_announcement") +def test_glossary_category_remove_announcement( + client: AtlanClient, category: AtlasGlossaryCategory, glossary: AtlasGlossary +): + _test_remove_announcement(client, category, AtlasGlossaryCategory, glossary.guid) + + +def test_audit_find_by_user( + client: AtlanClient, + current_user: UserMinimalResponse, + audit_info: AuditInfo, +): + size = 10 + assert current_user.username + + results = client.audit.search( + AuditSearchRequest.by_user(current_user.username, size=size, sort=[]) + ) + assert results.total_count > 0 + assert size == len(results.current_page()) + audit_entity = results.current_page()[0] + audit_info.qualified_name = audit_entity.entity_qualified_name + audit_info.guid = audit_entity.entity_id + audit_info.type_name = audit_entity.type_name + + # Fetch next page and make sure pagination works + results.next_page() + audit_entity_next_page = results._entity_audits[0] + assert audit_entity != audit_entity_next_page + + +@pytest.fixture(scope="module") +def generate_audit_entries(client: AtlanClient, audit_glossary: AtlasGlossary): + log_count = 5 + for i in range(log_count): + updater = AtlasGlossary.updater( + qualified_name=audit_glossary.qualified_name, + name=audit_glossary.name, + ) + updater.description = f"Updated description {i + 1}" + client.asset.save(updater) + time.sleep(1) + + request = AuditSearchRequest.by_guid(guid=audit_glossary.guid, size=log_count) + response = client.audit.search(request) + assert response.total_count >= log_count, ( + f"Expected at least {log_count} logs, but got {response.total_count}." + ) + + +def _assert_audit_search_results( + results, expected_sorts, size, TOTAL_AUDIT_ENTRIES, bulk=False +): + assert results.total_count > size + assert len(results.current_page()) == size + counter = 0 + for audit in results: + assert audit + counter += 1 + assert counter == TOTAL_AUDIT_ENTRIES + assert results + assert results._bulk is bulk + assert results._criteria.dsl.sort == expected_sorts + + +@pytest.mark.order(after="test_audit_find_by_user") +@patch.object(AUDIT_LOGGER, "debug") +def test_audit_search_pagination( + mock_logger, + audit_glossary: AtlasGlossary, + generate_audit_entries, + client: AtlanClient, +): + size = 2 + + # Test audit search by GUID with default offset-based pagination + dsl = DSL( + query=Bool(filter=[Term(field="entityId", value=audit_glossary.guid)]), + sort=[], + size=size, + ) + request = AuditSearchRequest(dsl=dsl) + results = client.audit.search(criteria=request, bulk=False) + TOTAL_AUDIT_ENTRIES = results.total_count + expected_sorts = [SortItem(field="entityId", order=SortOrder.ASCENDING)] + _assert_audit_search_results( + results, expected_sorts, size, TOTAL_AUDIT_ENTRIES, False + ) + + # Test audit search by guid with `bulk` option using timestamp-based pagination + dsl = DSL( + query=Bool(filter=[Term(field="entityId", value=audit_glossary.guid)]), + sort=[], + size=size, + ) + request = AuditSearchRequest(dsl=dsl) + results = client.audit.search(criteria=request, bulk=True) + expected_sorts = [ + SortItem("created", order=SortOrder.ASCENDING), + SortItem(field="entityId", order=SortOrder.ASCENDING), + ] + _assert_audit_search_results( + results, expected_sorts, size, TOTAL_AUDIT_ENTRIES, True + ) + assert mock_logger.call_count == 1 + assert "Audit bulk search option is enabled." in mock_logger.call_args_list[0][0][0] + mock_logger.reset_mock() + + # When the number of results exceeds the predefined + # threshold and bulk is true and no pre-defined sort. + with patch.object(AuditSearchResults, "_MASS_EXTRACT_THRESHOLD", -1): + dsl = DSL( + query=Bool(filter=[Term(field="entityId", value=audit_glossary.guid)]), + sort=[], + size=size, + ) + request = AuditSearchRequest(dsl=dsl) + results = client.audit.search(criteria=request, bulk=True) + expected_sorts = [ + SortItem("created", order=SortOrder.ASCENDING), + SortItem(field="entityId", order=SortOrder.ASCENDING), + ] + _assert_audit_search_results( + results, expected_sorts, size, TOTAL_AUDIT_ENTRIES, True + ) + assert mock_logger.call_count < TOTAL_AUDIT_ENTRIES + assert ( + "Audit bulk search option is enabled." + in mock_logger.call_args_list[0][0][0] + ) + mock_logger.reset_mock() + + # When the number of results exceeds the predefined threshold and bulk is `False` and no pre-defined sort. + # Then SDK automatically switches to a `bulk` search option using timestamp-based pagination + with patch.object(AuditSearchResults, "_MASS_EXTRACT_THRESHOLD", -1): + dsl = DSL( + query=Bool(filter=[Term(field="entityId", value=audit_glossary.guid)]), + sort=[], + size=size, + ) + request = AuditSearchRequest(dsl=dsl) + results = client.audit.search(criteria=request, bulk=False) + results.total_count + expected_sorts = [ + SortItem("created", order=SortOrder.ASCENDING), + SortItem(field="entityId", order=SortOrder.ASCENDING), + ] + _assert_audit_search_results( + results, expected_sorts, size, TOTAL_AUDIT_ENTRIES, False + ) + assert mock_logger.call_count < TOTAL_AUDIT_ENTRIES + assert ( + "Result size (%s) exceeds threshold (%s)." + in mock_logger.call_args_list[0][0][0] + ) + mock_logger.reset_mock() + + +@pytest.mark.order(after="test_audit_search_pagination") +def test_audit_find_by_qualified_name(client: AtlanClient, audit_info: AuditInfo): + assert audit_info.qualified_name + assert audit_info.type_name + size = 10 + + results = client.audit.search( + AuditSearchRequest.by_qualified_name( + qualified_name=audit_info.qualified_name, + type_name=audit_info.type_name, + size=size, + ) + ) + + assert results.total_count > 0 + count = len(results.current_page()) + assert count > 0 and count <= size + + +@pytest.mark.order(after="test_audit_find_by_user") +def test_audit_find_by_guid(client: AtlanClient, audit_info: AuditInfo): + assert audit_info.guid + size = 10 + + results = client.audit.search( + AuditSearchRequest.by_guid( + guid=audit_info.guid, + size=size, + ) + ) + + assert results.total_count > 0 + count = len(results.current_page()) + assert count > 0 and count <= size + + +def test_audit_search_default_sorting(client: AtlanClient, audit_info: AuditInfo): + # Test empty sorting + dsl = DSL( + query=Bool(filter=[Term(field="entityId", value=audit_info.guid)]), + sort=[], + size=10, + from_=0, + ) + request = AuditSearchRequest(dsl=dsl) + response = client.audit.search(criteria=request) + assert response + sort_options = response._criteria.dsl.sort + assert len(sort_options) == 1 + assert sort_options[0].field == AUDIT_SORT_BY_GUID.field + + # Sort without GUID + dsl = DSL( + query=Bool(filter=[Term(field="entityId", value=audit_info.guid)]), + sort=[AUDIT_SORT_BY_LATEST], + size=10, + from_=0, + ) + request = AuditSearchRequest(dsl=dsl) + response = client.audit.search(criteria=request) + assert response + sort_options = response._criteria.dsl.sort + assert len(sort_options) == 2 + assert sort_options[0].field == AUDIT_SORT_BY_LATEST.field + assert sort_options[1].field == AUDIT_SORT_BY_GUID.field + + # Sort with only GUID + dsl = DSL( + query=Bool(filter=[Term(field="entityId", value=audit_info.guid)]), + sort=[AUDIT_SORT_BY_GUID], + size=10, + from_=0, + ) + request = AuditSearchRequest(dsl=dsl) + response = client.audit.search(criteria=request) + assert response + sort_options = response._criteria.dsl.sort + assert len(sort_options) == 1 + assert sort_options[0].field == AUDIT_SORT_BY_GUID.field + + # Sort with GUID and others + dsl = DSL( + query=Bool(filter=[Term(field="entityId", value=audit_info.guid)]), + sort=[AUDIT_SORT_BY_GUID, AUDIT_SORT_BY_LATEST], + size=10, + from_=0, + ) + request = AuditSearchRequest(dsl=dsl) + response = client.audit.search(criteria=request) + assert response + sort_options = response._criteria.dsl.sort + assert len(sort_options) == 2 + assert sort_options[0].field == AUDIT_SORT_BY_GUID.field + assert sort_options[1].field == AUDIT_SORT_BY_LATEST.field + + +def _view_test_glossary_by_search( + client: AtlanClient, sl_glossary: AtlasGlossary +) -> None: + time.sleep(2) + index = ( + FluentSearch().where(Asset.GUID.eq(sl_glossary.guid, case_insensitive=True)) + ).to_request() + index.request_metadata = IndexSearchRequestMetadata( + utm_tags=[ + UTMTags.ACTION_ASSET_VIEWED, + UTMTags.UI_PROFILE, + UTMTags.UI_SIDEBAR, + UTMTags.PROJECT_SDK_PYTHON, + ], + save_search_log=True, + ) + response = client.asset.search(index) + assert response.count == 1 + assert response.current_page()[0].name == sl_glossary.name + time.sleep(2) + + +def test_search_log_most_recent_viewers( + client: AtlanClient, current_user: UserMinimalResponse, sl_glossary: AtlasGlossary +): + _view_test_glossary_by_search(client, sl_glossary) + request = SearchLogRequest.most_recent_viewers(guid=sl_glossary.guid) + response = client.search_log.search(request) + if not isinstance(response, SearchLogViewResults): + pytest.fail(f"Failed to retrieve most recent viewers of : {sl_glossary.name}") + viewers = response.user_views + assert not response.asset_views + if viewers: + assert len(viewers) == 1 + for viewer in viewers: + assert viewer.username + assert viewer.view_count + assert viewer.most_recent_view + + # Test exclude users + assert current_user.username + request = SearchLogRequest.most_recent_viewers( + guid=sl_glossary.guid, exclude_users=[current_user.username] + ) + response = client.search_log.search(request) + if not isinstance(response, SearchLogViewResults): + pytest.fail(f"Failed to retrieve most recent viewers of : {sl_glossary.name}") + assert response.count == 0 + assert response.user_views is not None + assert len(response.user_views) == 0 + assert not response.asset_views + + +@pytest.mark.order(after="test_search_log_most_recent_viewers") +def test_search_log_most_viewed_assets( + client: AtlanClient, + current_user: UserMinimalResponse, + sl_glossary: AtlasGlossary, +): + def _assert_most_viewed_assets( + details: Optional[List[AssetViews]], + ): + if details: + assert len(details) > 0 + for detail in details: + assert detail.guid + assert detail.total_views + assert detail.distinct_users + + request = SearchLogRequest.most_viewed_assets(max_assets=10) + response = client.search_log.search(request) + if not isinstance(response, SearchLogViewResults): + pytest.fail("Failed to retrieve most viewed assets") + assert not response.user_views + _assert_most_viewed_assets(response.asset_views) + + request = SearchLogRequest.most_viewed_assets(max_assets=10, by_different_user=True) + response = client.search_log.search(request) + if not isinstance(response, SearchLogViewResults): + pytest.fail("Failed to retrieve most viewed assets (by_different_user)") + assert not response.user_views + _assert_most_viewed_assets(response.asset_views) + + # Test exclude users + prev_count = response.count + assert prev_count + assert current_user.username + request = SearchLogRequest.most_viewed_assets( + max_assets=10, exclude_users=[current_user.username] + ) + response = client.search_log.search(request) + if not isinstance(response, SearchLogViewResults): + pytest.fail("Failed to retrieve most viewed assets") + assert response.count < prev_count + assert not response.user_views + _assert_most_viewed_assets(response.asset_views) + + +@pytest.mark.order(after="test_search_log_most_viewed_assets") +def test_search_log_views_by_guid( + client: AtlanClient, current_user: UserMinimalResponse, sl_glossary: AtlasGlossary +): + request = SearchLogRequest.views_by_guid(guid=sl_glossary.guid, size=10) + response = client.search_log.search(request) + if not isinstance(response, SearchLogResults): + pytest.fail("Failed to retrieve asset detailed log entries") + log_entries = response.current_page() + assert len(response.current_page()) == 1 + assert "Atlan-PythonSDK" in log_entries[0].user_agent + assert "service-account-apikey" in log_entries[0].user_name + assert log_entries[0].entity_guids_all[0] == sl_glossary.guid + assert log_entries[0].ip_address + assert log_entries[0].host + assert log_entries[0].utm_tags + assert log_entries[0].entity_guids_allowed + assert log_entries[0].entity_qf_names_all + assert log_entries[0].entity_qf_names_allowed + assert log_entries[0].entity_type_names_all + assert log_entries[0].entity_type_names_allowed + assert log_entries[0].has_result + assert log_entries[0].results_count + assert log_entries[0].response_time + assert log_entries[0].created_at + assert log_entries[0].timestamp + assert log_entries[0].failed is False + assert log_entries[0].request_dsl + assert log_entries[0].request_dsl_text + assert not log_entries[0].request_attributes + assert not log_entries[0].request_relation_attributes + + # Test exclude users + assert current_user.username + request = SearchLogRequest.views_by_guid( + guid=sl_glossary.guid, size=10, exclude_users=[current_user.username] + ) + response = client.search_log.search(request) + if not isinstance(response, SearchLogResults): + pytest.fail("Failed to retrieve asset detailed log entries") + assert response.count == 0 + assert len(response.current_page()) == 0 + + +@pytest.fixture(scope="module") +def generate_search_logs(client: AtlanClient, sl_glossary: AtlasGlossary): + log_count = 5 + + for _ in range(log_count): + _view_test_glossary_by_search(client, sl_glossary) + time.sleep(1) + + request = SearchLogRequest.views_by_guid(guid=sl_glossary.guid, size=20) + response = client.search_log.search(request) + assert response.count >= log_count, ( + f"Expected at least {log_count} logs, but got {response.count}." + ) + + +def _assert_search_log_results( + results, expected_sorts, size, TOTAL_LOG_ENTRIES, bulk=False +): + assert results.count > size + assert len(results.current_page()) == size + counter = 0 + for log in results: + assert log + counter += 1 + assert counter == TOTAL_LOG_ENTRIES + assert results + assert results._bulk is bulk + assert results._criteria.dsl.sort == expected_sorts + + +@patch.object(SEARCH_LOG_LOGGER, "debug") +def test_search_log_pagination( + mock_logger, generate_search_logs, sl_glossary: AtlasGlossary, client: AtlanClient +): + size = 2 + # Test search logs by GUID with default offset-based pagination + search_log_request = SearchLogRequest.views_by_guid( + guid=sl_glossary.guid, + size=size, + exclude_users=[], + ) + + results = client.search_log.search(criteria=search_log_request, bulk=False) + TOTAL_LOG_ENTRIES = results.count + + expected_sorts = [ + SortItem(field="timestamp", order=SortOrder.ASCENDING), + SortItem(field="entityGuidsAll", order=SortOrder.ASCENDING), + ] + _assert_search_log_results(results, expected_sorts, size, TOTAL_LOG_ENTRIES) + + # Test search logs by GUID with `bulk` option using timestamp-based pagination + search_log_request = SearchLogRequest.views_by_guid( + guid=sl_glossary.guid, + size=size, + exclude_users=[], + ) + results = client.search_log.search(criteria=search_log_request, bulk=True) + expected_sorts = [ + SortItem(field="createdAt", order=SortOrder.ASCENDING), + SortItem(field="entityGuidsAll", order=SortOrder.ASCENDING), + ] + _assert_search_log_results(results, expected_sorts, size, TOTAL_LOG_ENTRIES, True) + assert mock_logger.call_count == 1 + assert ( + "Search log bulk search option is enabled." + in mock_logger.call_args_list[0][0][0] + ) + mock_logger.reset_mock() + + # When the number of results exceeds the predefined threshold and bulk=True + with patch.object(SearchLogResults, "_MASS_EXTRACT_THRESHOLD", -1): + search_log_request = SearchLogRequest.views_by_guid( + guid=sl_glossary.guid, + size=size, + exclude_users=[], + ) + results = client.search_log.search(criteria=search_log_request, bulk=True) + expected_sorts = [ + SortItem(field="createdAt", order=SortOrder.ASCENDING), + SortItem(field="entityGuidsAll", order=SortOrder.ASCENDING), + ] + _assert_search_log_results( + results, expected_sorts, size, TOTAL_LOG_ENTRIES, True + ) + assert mock_logger.call_count < TOTAL_LOG_ENTRIES + assert ( + "Search log bulk search option is enabled." + in mock_logger.call_args_list[0][0][0] + ) + mock_logger.reset_mock() + + # When results exceed threshold and bulk=False, SDK auto-switches to bulk search + with patch.object(SearchLogResults, "_MASS_EXTRACT_THRESHOLD", -1): + search_log_request = SearchLogRequest.views_by_guid( + guid=sl_glossary.guid, + size=size, + exclude_users=[], + ) + results = client.search_log.search(criteria=search_log_request, bulk=False) + expected_sorts = [ + SortItem(field="createdAt", order=SortOrder.ASCENDING), + SortItem(field="entityGuidsAll", order=SortOrder.ASCENDING), + ] + _assert_search_log_results(results, expected_sorts, size, TOTAL_LOG_ENTRIES) + assert mock_logger.call_count < TOTAL_LOG_ENTRIES + assert ( + "Result size (%s) exceeds threshold (%s)." + in mock_logger.call_args_list[0][0][0] + ) + mock_logger.reset_mock() + + +def test_search_log_default_sorting(client: AtlanClient, sl_glossary: AtlasGlossary): + # Empty sorting + request = SearchLogRequest.views_by_guid(guid=sl_glossary.guid, size=10, sort=[]) + response = client.search_log.search(request) + if not isinstance(response, SearchLogResults): + pytest.fail("Failed to retrieve asset detailed log entries") + assert response + sort_options = response._criteria.dsl.sort + assert len(sort_options) == 2 + assert sort_options[0].field == SL_SORT_BY_TIMESTAMP.field + assert sort_options[1].field == SL_SORT_BY_GUID.field + + # Sort without GUID + request = SearchLogRequest.views_by_guid( + guid=sl_glossary.guid, + size=10, + sort=[SL_SORT_BY_QUALIFIED_NAME], + ) + response = client.search_log.search(request) + if not isinstance(response, SearchLogResults): + pytest.fail("Failed to retrieve asset detailed log entries") + assert response + sort_options = response._criteria.dsl.sort + assert len(sort_options) == 3 + assert sort_options[0].field == SL_SORT_BY_QUALIFIED_NAME.field + assert sort_options[1].field == SL_SORT_BY_TIMESTAMP.field + assert sort_options[2].field == SL_SORT_BY_GUID.field + + # Sort with only GUID + request = SearchLogRequest.views_by_guid( + guid=sl_glossary.guid, + size=10, + sort=[SL_SORT_BY_GUID], + ) + response = client.search_log.search(request) + if not isinstance(response, SearchLogResults): + pytest.fail("Failed to retrieve asset detailed log entries") + assert response + sort_options = response._criteria.dsl.sort + assert len(sort_options) == 2 + assert sort_options[0].field == SL_SORT_BY_GUID.field + assert sort_options[1].field == SL_SORT_BY_TIMESTAMP.field + + # Sort with GUID and others + request = SearchLogRequest.views_by_guid( + guid=sl_glossary.guid, + size=10, + sort=[SL_SORT_BY_GUID, SL_SORT_BY_QUALIFIED_NAME], + ) + response = client.search_log.search(request) + if not isinstance(response, SearchLogResults): + pytest.fail("Failed to retrieve asset detailed log entries") + assert response + sort_options = response._criteria.dsl.sort + assert len(sort_options) == 3 + assert sort_options[0].field == SL_SORT_BY_GUID.field + assert sort_options[1].field == SL_SORT_BY_QUALIFIED_NAME.field + assert sort_options[2].field == SL_SORT_BY_TIMESTAMP.field + + +@pytest.mark.skip(reason="Test failing due backend unauthenticated error") +def test_client_401_token_refresh( + client: AtlanClient, expired_token: ApiToken, argo_fake_token: ApiToken, monkeypatch +): + # Use a smaller retry count to speed up test execution + DEFAULT_RETRY.total = 1 + + # Retrieve required client information before updating the client with invalid API tokens + assert argo_fake_token and argo_fake_token.guid + argo_client_secret = client.impersonate.get_client_secret( + client_guid=argo_fake_token.guid + ) + + # Retrieve the user ID associated with the expired token's username + # Since user credentials for API tokens cannot be retrieved directly, use the existing username + expired_token_user_id = client.impersonate.get_user_id( + username=expired_token.username + ) + + # Initialize the client with an expired/invalid token (results in 401 Unauthorized errors) + assert ( + expired_token + and expired_token.attributes + and expired_token.attributes.access_token + ) + client = AtlanClient( + api_key=expired_token.attributes.access_token, retry=DEFAULT_RETRY + ) + expired_api_token = expired_token.attributes.access_token + + # Case 1: No user_id (default) + # Verify that the client raises an authentication error when no user ID is provided + assert client._user_client is None + with pytest.raises( + AuthenticationError, + match="Server responded with an authentication error 401", + ): + FluentSearch().where(CompoundQuery.active_assets()).where( + CompoundQuery.asset_type(AtlasGlossary) + ).page_size(100).execute(client=client) + + # Case 2: Invalid user_id + # Test that providing an invalid user ID results in the same authentication error + client._user_id = "invalid-user-id" + with pytest.raises( + InvalidRequestError, + match="Missing privileged credentials to impersonate users", + ): + FluentSearch().where(CompoundQuery.active_assets()).where( + CompoundQuery.asset_type(AtlasGlossary) + ).page_size(100).execute(client=client) + + # Case 3: Valid user_id associated with the expired token + # This should trigger a retry, refresh the token + # and use the new bearer token for subsequent requests + # Set up a fake Argo client ID and client secret for impersonation + monkeypatch.setenv("CLIENT_ID", argo_fake_token.client_id) + monkeypatch.setenv("CLIENT_SECRET", argo_client_secret) + + # Configure the client with the user ID + # of the expired token to ensure token refresh is possible + client._user_id = expired_token_user_id + + # Verify that the API key is updated after the retry and the request succeeds + results = ( + FluentSearch() + .where(CompoundQuery.active_assets()) + .where(CompoundQuery.asset_type(AtlasGlossary)) + .page_size(100) + .execute(client=client) + ) + + # Confirm the API key has been updated and results are returned + assert client.api_key != expired_api_token + assert results and results.count >= 1 + + # Verify similar results with get_client() + # Setting ATLAN_API_KEY to empty string to force impersonation + monkeypatch.setenv("ATLAN_API_KEY", "") + assert expired_token_user_id + client = get_client(impersonate_user_id=expired_token_user_id) + results = ( + FluentSearch() + .where(CompoundQuery.active_assets()) + .where(CompoundQuery.asset_type(AtlasGlossary)) + .page_size(100) + .execute(client=client) + ) + + # Confirm the API key has been updated and results are returned + assert client.api_key != expired_api_token + assert results and results.count >= 1 + + # Verify package headers are set correctly + expected_common_headers = Headers( + { + "User-Agent": f"Atlan-PythonSDK/{VERSION}", + "Accept-Encoding": "gzip, deflate", + "Accept": "*/*", + "Connection": "keep-alive", + "x-atlan-agent": "sdk", + "x-atlan-agent-id": "python", + "x-atlan-client-origin": "product_sdk", + "x-atlan-python-version": get_python_version(), + "x-atlan-client-type": "sync", + } + ) + + # Clear package environment variables to test default headers + for var in [ + "X_ATLAN_AGENT", + "X_ATLAN_AGENT_ID", + "X_ATLAN_AGENT_PACKAGE_NAME", + "X_ATLAN_AGENT_WORKFLOW_ID", + ]: + monkeypatch.delenv(var, raising=False) + + client = get_client( + impersonate_user_id=expired_token_user_id, set_pkg_headers=False + ) + assert expected_common_headers == client._session.headers + + # Set package environment variables to test package headers + monkeypatch.setenv("X_ATLAN_AGENT", "agent_value") + monkeypatch.setenv("X_ATLAN_AGENT_ID", "agent_id_value") + monkeypatch.setenv("X_ATLAN_AGENT_PACKAGE_NAME", "package_name_value") + monkeypatch.setenv("X_ATLAN_AGENT_WORKFLOW_ID", "workflow_id_value") + + expected = Headers( + { + "User-Agent": f"Atlan-PythonSDK/{VERSION}", + "Accept-Encoding": "gzip, deflate", + "Accept": "*/*", + "Connection": "keep-alive", + "x-atlan-client-origin": "product_sdk", + "x-atlan-python-version": get_python_version(), + "x-atlan-client-type": "sync", + "x-atlan-agent": "agent_value", + "x-atlan-agent-id": "agent_id_value", + "x-atlan-agent-package-name": "package_name_value", + "x-atlan-agent-workflow-id": "workflow_id_value", + } + ) + client = get_client(impersonate_user_id=expired_token_user_id, set_pkg_headers=True) + assert expected == client._session.headers + + +def test_client_init_from_token_guid( + client: AtlanClient, token: ApiToken, argo_fake_token: ApiToken, monkeypatch +): + # In real-world scenarios, these values come from environment variables + # configured at the Argo template level. The SDK uses these values to + # create a temporary client, which allows us to find the `client_id` and `client_secret` + # for the provided API token GUID, later used to initialize a client with its actual access token (API key) <- AtlanClient.from_token_guid() + assert argo_fake_token and argo_fake_token.guid + argo_client_secret = client.impersonate.get_client_secret( + client_guid=argo_fake_token.guid + ) + monkeypatch.setenv("CLIENT_ID", argo_fake_token.client_id) + monkeypatch.setenv("CLIENT_SECRET", argo_client_secret) + + # Ensure it's a valid API token + assert token and token.username and token.guid + assert "service-account" in token.username + token_client_from_env_vars = AtlanClient.from_token_guid(guid=token.guid) + token_client_custom = AtlanClient.from_token_guid( + guid=token.guid, + client_id=argo_fake_token.client_id, + client_secret=argo_client_secret, + ) + + # Should be able to perform all operations + # with this client as long as it has the necessary permissions + results = ( + FluentSearch() + .where(CompoundQuery.active_assets()) + .where(CompoundQuery.asset_type(AtlasGlossary)) + .page_size(100) + .execute(client=token_client_from_env_vars) + ) + assert results and results.count >= 1 + + results = ( + FluentSearch() + .where(CompoundQuery.active_assets()) + .where(CompoundQuery.asset_type(AtlasGlossary)) + .page_size(100) + .execute(client=token_client_custom) + ) + assert results and results.count >= 1 + + +def test_process_assets_when_no_assets_found(client: AtlanClient): + def should_never_be_called(_: Asset): + pytest.fail("Should not be called") + + search = ( + FluentSearch() + .where(Term.with_state("ACTIVE")) + .where(Asset.NAME.startswith("zXZ")) + ) + + processed_count = client.asset.process_assets( + search=search, func=should_never_be_called + ) + assert processed_count == 0 + + +def test_process_assets_when_assets_found(client: AtlanClient): + def doit(asset: Asset): + global call_count + call_count += 1 + + search = ( + FluentSearch() + .where(Term.with_state("ACTIVE")) + .where(Asset.TYPE_NAME.eq("Table")) + .where(Asset.NAME.startswith("B")) + ) + expected_count = client.asset.search(search.to_request()).count + + processed_count = client.asset.process_assets(search=search, func=doit) + + assert call_count == expected_count + assert processed_count == expected_count diff --git a/tests_v9/integration/test_file_client.py b/tests_v9/integration/test_file_client.py new file mode 100644 index 000000000..5e91bef6b --- /dev/null +++ b/tests_v9/integration/test_file_client.py @@ -0,0 +1,97 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +import imghdr # type: ignore[import-not-found] +import os +from pathlib import Path + +import pytest + +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.errors import InvalidRequestError +from pyatlan_v9.model.file import PresignedURLRequest +from tests_v9.integration.client import TestId + +MODULE_NAME = TestId.make_unique("TaskClient") + +URL_EXPIRY = "10s" +FILE_NAME = "sdk.png" +DOWNLOAD_FILE_NAME = "sdk-download.png" +TENANT_S3_BUCKET_DIRECTORY = "presigned-url-sdk-integration-tests" +S3_UPLOAD_FILE_PATH = f"{TENANT_S3_BUCKET_DIRECTORY}/{FILE_NAME}" + + +TEST_DATA_DIR = Path(__file__).parent / "data" +UPLOAD_FILE_PATH = str(TEST_DATA_DIR / "file_requests" / FILE_NAME) +DOWNLOAD_FILE_PATH = str(TEST_DATA_DIR / "file_requests" / DOWNLOAD_FILE_NAME) + + +@pytest.mark.parametrize( + "file_path, expected_error", + [ + [ + "some/invalid/file_path.png", + ( + "ATLAN-PYTHON-400-060 Unable to download file, " + "Error: No such file or directory, Path: some/invalid/file_path.png" + ), + ], + ], +) +def test_file_client_download_file_raises_invalid_request_error( + client, file_path, expected_error +): + with pytest.raises(InvalidRequestError, match=expected_error): + client.files.download_file( + presigned_url="test-url", + file_path=file_path, + ) + + +@pytest.fixture(scope="module") +def s3_put_presigned_url(client: AtlanClient) -> str: + # Presigned URL for upload + return client.files.generate_presigned_url( + request=PresignedURLRequest( + key=S3_UPLOAD_FILE_PATH, + expiry=URL_EXPIRY, + method=PresignedURLRequest.Method.PUT, + ) + ) + + +@pytest.fixture(scope="module") +def s3_get_presigned_url(client: AtlanClient) -> str: + # Presigned URL for download + return client.files.generate_presigned_url( + request=PresignedURLRequest( + key=S3_UPLOAD_FILE_PATH, + expiry=URL_EXPIRY, + method=PresignedURLRequest.Method.GET, + ) + ) + + +def test_file_client_presigned_url_upload( + client: AtlanClient, s3_put_presigned_url: str +): + assert s3_put_presigned_url + assert os.path.exists(UPLOAD_FILE_PATH) + + client.files.upload_file( + presigned_url=s3_put_presigned_url, file_path=UPLOAD_FILE_PATH + ) + + +def test_file_client_presigned_url_download( + client: AtlanClient, s3_get_presigned_url: str +): + assert s3_get_presigned_url + assert not os.path.exists(DOWNLOAD_FILE_PATH) + + client.files.download_file( + presigned_url=s3_get_presigned_url, file_path=DOWNLOAD_FILE_PATH + ) + assert os.path.exists(DOWNLOAD_FILE_PATH) + assert imghdr.what(DOWNLOAD_FILE_PATH) == "png" + os.remove(DOWNLOAD_FILE_PATH) diff --git a/tests_v9/integration/test_index_search.py b/tests_v9/integration/test_index_search.py new file mode 100644 index 000000000..9f4be2eea --- /dev/null +++ b/tests_v9/integration/test_index_search.py @@ -0,0 +1,961 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2022 Atlan Pte. Ltd. +import math +from dataclasses import dataclass, field +from datetime import datetime +from time import sleep, time +from typing import Generator, Set +from unittest.mock import patch + +import httpx +import pytest +from httpx_retries import Retry + +from pyatlan.cache.source_tag_cache import SourceTagName +from pyatlan.client.common.asset import LOGGER +from pyatlan_v9.client.asset import IndexSearchResults +from pyatlan_v9.client.atlan import AtlanClient, client_connection +from pyatlan_v9.model.assets import ( + Asset, + AtlasGlossaryTerm, + Column, + Persona, + Purpose, + Referenceable, + Table, +) +from pyatlan_v9.model.core import AtlanTag, AtlanTagName +from pyatlan_v9.model.enums import AtlanConnectorType, CertificateStatus, SortOrder +from pyatlan_v9.model.fields.atlan_fields import SearchableField +from pyatlan_v9.model.fluent_search import CompoundQuery, FluentSearch +from pyatlan_v9.model.search import ( + DSL, + Bool, + Exists, + IndexSearchRequest, + Match, + Prefix, + Range, + Regexp, + Term, + Terms, + Wildcard, +) +from pyatlan_v9.model.structs import SourceTagAttachment, SourceTagAttachmentValue + +QUALIFIED_NAME = "qualifiedName" +ASSET_GUID = Asset.GUID.keyword_field_name +NOW_AS_TIMESTAMP = int(time() * 1000) +NOW_AS_YYYY_MM_DD = datetime.today().strftime("%Y-%m-%d") +EXISTING_TAG = "Issue" +EXISTING_SOURCE_SYNCED_TAG = "Confidential" +DB_NAME = "ANALYTICS" +TABLE_NAME = "STG_STATE_PROVINCES" +COLUMN_NAME = "LATEST_RECORDED_POPULATION" +SCHEMA_NAME = "WIDE_WORLD_IMPORTERS" + +VALUES_FOR_TERM_QUERIES = { + "with_categories": "VBsYc9dUoEcAtDxZmjby6@mweSfpXBwfYWedQTvA3Gi", + "with_classification_names": "RBmhFJqX50bl5RAeJhwt1a", + "with_classifications_text": "VBsYc9dUoEcAtDxZmjby6@mweSfpXBwfYWedQTvA3Gi", + "with_connector_name": AtlanConnectorType.SNOWFLAKE, + "with_create_time_as_timestamp": datetime.fromtimestamp(1665727666701 / 1000), + "with_created_by": "bryan", + "with_glossary": "mweSfpXBwfYWedQTvA3Gi", + "with_guid": "b95eed37-fe38-48d7-8240-0c3390ef4e48", + "with_has_lineage": True, + "with_meanings": "2EqDFWZ6sCjbxcDNL0jFV@3Wn0W7PFCfjyKmGBZ7FLD", + "with_meanings_text": "VBsYc9dUoEcAtDxZmjby6@mweSfpXBwfYWedQTvA3Gi", + "with_modified_by": "bryan", + "with_name": "Schema", + "with_owner_groups": "data_engineering", + "with_owner_users": "ravi", + "with_parent_category": "fWB1bJLOhEd4ik1Um1EJ8@3Wn0W7PFCfjyKmGBZ7FLD", + "with_qualified_name": "default/oracle/1665680872/ORCL/SCALE_TEST/TABLE_MVD_3042/PERSON_ID", + "with_state": "ACTIVE", + "with_super_type_names": "SQL", + "with_type_name": "Schema", + "with_update_time_as_timestamp": datetime.fromtimestamp(1665723703029 / 1000), + "with_certificate_status": CertificateStatus.VERIFIED, +} + +VALUES_FOR_TEXT_QUERIES = { + "with_categories": "VBsYc9dUoEcAtDxZmjby6@mweSfpXBwfYWedQTvA3Gi", + "with_classification_names": "RBmhFJqX50bl5RAeJhwt1a", + "with_classifications_text": "RBmhFJqX50bl5RAeJhwt1a", + "with_created_by": "bryan", + "with_description": "snapshot", + "with_glossary": "mweSfpXBwfYWedQTvA3Gi", + "with_guid": "b95eed37-fe38-48d7-8240-0c3390ef4e48", + "with_has_lineage": True, + "with_meanings": "2EqDFWZ6sCjbxcDNL0jFV@3Wn0W7PFCfjyKmGBZ7FLD", + "with_meanings_text": "Term Test", + "with_modification_timestamp": 1665086276846, + "with_modified_by": "bryan", + "with_name": "Schema", + "with_parent_category": "fWB1bJLOhEd4ik1Um1EJ8@3Wn0W7PFCfjyKmGBZ7FLD", + "with_propagated_classification_names": "RBmhFJqX50bl5RAeJhwt1a", + "with_qualified_name": "default", + "with_state": "ACTIVE", + "with_super_type_names": "ObjectStore SQL", + "with_timestamp": 1665727666701, + "with_trait_names": "RBmhFJqX50bl5RAeJhwt1a", + "with_propagated_trait_names": "RBmhFJqX50bl5RAeJhwt1a", + "with_type_name": "Schema", + "with_user_description": "this", +} +EXISTING_PURPOSE_NAME = "Known Issues" +EXISTING_PERSONA_NAME = "Business Definitions" + + +@pytest.fixture(scope="module") +def business_definitions_persona(client: AtlanClient): + return client.asset.find_personas_by_name(EXISTING_PERSONA_NAME)[0] + + +@pytest.fixture(scope="module") +def known_issues_purpose(client: AtlanClient): + return client.asset.find_purposes_by_name(EXISTING_PURPOSE_NAME)[0] + + +@pytest.fixture(scope="module") +def snowflake_conn(client: AtlanClient): + return client.asset.find_connections_by_name( + "development", AtlanConnectorType.SNOWFLAKE + )[0] + + +@pytest.fixture(scope="module") +def snowflake_column_qn(snowflake_conn): + return f"{snowflake_conn.qualified_name}/{DB_NAME}/{SCHEMA_NAME}/{TABLE_NAME}/{COLUMN_NAME}" + + +@dataclass() +class AssetTracker: + missing_types: Set[str] = field(default_factory=set) + found_types: Set[str] = field(default_factory=set) + + +@pytest.fixture(scope="module") +def asset_tracker() -> Generator[AssetTracker, None, None]: + tracker = AssetTracker() + yield tracker + print("Total number of asset types found: ", len(tracker.found_types)) + print("Total number of asset types missing: ", len(tracker.missing_types)) + print("Assets were not found for the following types:") + for name in sorted(tracker.missing_types): + print("\t", name) + print("Assets were found for the following types:") + for name in sorted(tracker.found_types): + print("\t", name) + + +def get_all_subclasses(cls): + all_subclasses = [] + + for subclass in cls.__subclasses__(): + all_subclasses.append(subclass) + all_subclasses.extend(get_all_subclasses(subclass)) + + return all_subclasses + + +@pytest.mark.parametrize("cls", [(cls) for cls in get_all_subclasses(Asset)]) +def test_search(client: AtlanClient, asset_tracker, cls): + name = cls.__name__ + query = Term.with_state("ACTIVE") + post_filter = Term.with_type_name(name) + dsl = DSL(query=query, post_filter=post_filter) + request = IndexSearchRequest(dsl=dsl, attributes=["name"]) + results = client.asset.search(criteria=request) + if results.count > 0: + asset_tracker.found_types.add(name) + counter = 0 + for asset in results: + assert isinstance(asset, cls) + counter += 1 + if counter > 3: + break + else: + asset_tracker.missing_types.add(name) + + +def test_search_with_enable_full_restriction(client: AtlanClient): + """Test search API with enableFullRestriction parameter.""" + query = Term.with_state("ACTIVE") + post_filter = Term.with_type_name("Table") + dsl = DSL(query=query, post_filter=post_filter, size=1) + + # Test with enableFullRestriction=True + request = IndexSearchRequest( + dsl=dsl, attributes=["name"], enable_full_restriction=True + ) + results = client.asset.search(criteria=request) + assert results is not None + assert hasattr(results, "count") + + # Test with enableFullRestriction=False + request_false = IndexSearchRequest( + dsl=dsl, attributes=["name"], enable_full_restriction=False + ) + results_false = client.asset.search(criteria=request_false) + assert results_false is not None + assert hasattr(results_false, "count") + + # Test without the parameter (default behavior) + request_default = IndexSearchRequest(dsl=dsl, attributes=["name"]) + results_default = client.asset.search(criteria=request_default) + assert results_default is not None + assert hasattr(results_default, "count") + + +def _assert_source_tag(tables, source_tag, source_tag_value): + assert tables and len(tables) > 0 + for table in tables: + tags = table.atlan_tags + assert tags and len(tags) > 0 + synced_tags = [tag for tag in tags if str(tag.type_name) == source_tag] + assert synced_tags and len(synced_tags) > 0 + for st in synced_tags: + attachments = st.source_tag_attachments + assert attachments and len(attachments) > 0 + for sta in attachments: + values = sta.source_tag_value + assert values and len(values) > 0 + for value in values: + attached_value = value.tag_attachment_value + assert attached_value and attached_value == source_tag_value + + +def test_search_source_synced_assets(client: AtlanClient): + tables = [ + table + for table in ( + FluentSearch() + .select() + .where(CompoundQuery.asset_type(Table)) + .where( + CompoundQuery.tagged_with_value( + client=client, + atlan_tag_name=EXISTING_SOURCE_SYNCED_TAG, + value="Highly Restricted", + ) + ) + .execute(client=client) + ) + if isinstance(table, Table) + ] + _assert_source_tag(tables, EXISTING_SOURCE_SYNCED_TAG, "Highly Restricted") + + +def test_source_tag_assign_with_value(client: AtlanClient, table: Table): + # Make sure no tags are assigned initially + assert table.guid + table = client.asset.get_by_guid( + guid=table.guid, asset_type=Table, ignore_relationships=False + ) + assert not table.atlan_tags + assert table.name and table.qualified_name + + source_tag_name = SourceTagName( + client=client, + tag="snowflake/development@@ANALYTICS/WIDE_WORLD_IMPORTERS/CONFIDENTIAL", + ) + to_update = table.updater(table.qualified_name, table.name) + to_update.atlan_tags = [ + AtlanTag.of(atlan_tag_name=AtlanTagName(EXISTING_TAG)), + AtlanTag.of( + atlan_tag_name=AtlanTagName(EXISTING_SOURCE_SYNCED_TAG), + source_tag_attachment=SourceTagAttachment.by_name( + client=client, + name=source_tag_name, + source_tag_values=[ + SourceTagAttachmentValue(tag_attachment_value="Not Restricted") + ], + ), + client=client, + ), + ] + response = client.asset.save(to_update, replace_atlan_tags=True) + + assert (tables := response.assets_updated(asset_type=Table)) and len(tables) == 1 + assert ( + tables + and len(tables) == 1 + and tables[0].atlan_tags + and len(tables[0].atlan_tags) == 2 + ) + for tag in tables[0].atlan_tags: + assert str(tag.type_name) in (EXISTING_TAG, EXISTING_SOURCE_SYNCED_TAG) + + # Make sure source tag is now attached + # to the table with the provided value + sleep(5) + tables = [ + table + for table in ( + FluentSearch() + .select() + .where(CompoundQuery.asset_type(Table)) + .where(Table.QUALIFIED_NAME.eq(table.qualified_name)) + .where( + CompoundQuery.tagged_with_value( + client=client, + atlan_tag_name=EXISTING_SOURCE_SYNCED_TAG, + value="Not Restricted", + ) + ) + .execute(client=client) + ) + if isinstance(table, Table) + ] + + assert ( + tables + and len(tables) == 1 + and tables[0].atlan_tags + and len(tables[0].atlan_tags) == 2 + ) + for tag in tables[0].atlan_tags: + assert str(tag.type_name) in (EXISTING_TAG, EXISTING_SOURCE_SYNCED_TAG) + _assert_source_tag(tables, EXISTING_SOURCE_SYNCED_TAG, "Not Restricted") + + +def test_search_source_specific_custom_attributes( + client: AtlanClient, snowflake_column_qn: str +): + # Test with get_by_qualified_name() + asset = client.asset.get_by_qualified_name( + asset_type=Column, + qualified_name=snowflake_column_qn, + min_ext_info=True, + ignore_relationships=True, + ) + assert asset and asset.custom_attributes + + # Test with FluentSearch() + results = ( + FluentSearch() + .where(CompoundQuery.active_assets()) + .where(Column.QUALIFIED_NAME.eq(snowflake_column_qn)) + .include_on_results(Column.CUSTOM_ATTRIBUTES) + .execute(client=client) + ) + assert results and results.count == 1 + assert results.current_page() and len(results.current_page()) == 1 + column = results.current_page()[0] + assert isinstance(column, Column) and column and column.custom_attributes + + +def test_search_next_page(client: AtlanClient): + size = 2 + dsl = DSL( + query=Term.with_state("ACTIVE"), + post_filter=Term.with_type_name(value="AtlasGlossaryTerm"), + size=size, + ) + request = IndexSearchRequest(dsl=dsl) + results = client.asset.search(criteria=request) + assert results.count > size + assert len(results.current_page()) == size + counter = 0 + while True: + for _ in results.current_page(): + counter += 1 + if results.next_page() is not True: + break + assert counter == results.count + + +def _assert_search_results(results, expected_sorts, size, TOTAL_ASSETS, bulk=False): + assert results.count > size + assert len(results.current_page()) == size + counter = 0 + for term in results: + assert term + counter += 1 + assert counter == TOTAL_ASSETS + assert results + assert results._bulk is bulk + assert not results.aggregations + assert results._criteria.dsl.sort == expected_sorts + + +@patch.object(LOGGER, "debug") +def test_search_pagination(mock_logger, client: AtlanClient): + # Avoid testing on integration tests objects + exclude_sdk_terms = [ + Asset.NAME.wildcard("psdkv9_*"), + Asset.NAME.wildcard("jsdk_*"), + Asset.NAME.wildcard("gsdk_*"), + ] + query = CompoundQuery( + where_nots=exclude_sdk_terms, where_somes=[CompoundQuery.active_assets()] + ).to_query() + + # Test search() with DSL: using default offset-based pagination + # when results are less than the predefined threshold (i.e: 100,000 assets) + dsl = DSL( + query=query, + post_filter=Term.with_type_name(value="AtlasGlossaryTerm"), + size=0, # to get the total count + ) + + request = IndexSearchRequest(dsl=dsl) + results = client.asset.search(criteria=request) + # Assigning this here to ensure the total assets + # remain constant across different test cases + TOTAL_ASSETS = results.count + + # set page_size to divide into ~5 API calls + size = max(1, math.ceil(TOTAL_ASSETS / 5)) + request.dsl.size = size + + # Now, we can test different test scenarios for search() with the dynamic page size + results = client.asset.search(criteria=request) + + expected_sorts = [Asset.GUID.order(SortOrder.ASCENDING)] + _assert_search_results(results, expected_sorts, size, TOTAL_ASSETS) + + # Test search() DSL: with `bulk` option using timestamp-based pagination + dsl = DSL( + query=query, + post_filter=Term.with_type_name(value="AtlasGlossaryTerm"), + size=size, + ) + request = IndexSearchRequest(dsl=dsl) + results = client.asset.search(criteria=request, bulk=True) + expected_sorts = [ + Asset.CREATE_TIME.order(SortOrder.ASCENDING), + Asset.GUID.order(SortOrder.ASCENDING), + ] + _assert_search_results(results, expected_sorts, size, TOTAL_ASSETS, True) + assert mock_logger.call_count == 1 + assert "Bulk search option is enabled." in mock_logger.call_args_list[0][0][0] + mock_logger.reset_mock() + + # Test search(): using default offset-based pagination + # when results are less than the predefined threshold (i.e: 100,000 assets) + request = ( + FluentSearch(where_nots=exclude_sdk_terms) + .where(CompoundQuery.active_assets()) + .where(CompoundQuery.asset_type(AtlasGlossaryTerm)) + .page_size(size) + ).to_request() + results = client.asset.search(criteria=request) + expected_sorts = [Asset.GUID.order(SortOrder.ASCENDING)] + _assert_search_results(results, expected_sorts, size, TOTAL_ASSETS) + + # Test search(): with `bulk` option using timestamp-based pagination + request = ( + FluentSearch(where_nots=exclude_sdk_terms) + .where(CompoundQuery.active_assets()) + .where(CompoundQuery.asset_type(AtlasGlossaryTerm)) + .page_size(size) + ).to_request() + results = client.asset.search(criteria=request, bulk=True) + expected_sorts = [ + Asset.CREATE_TIME.order(SortOrder.ASCENDING), + Asset.GUID.order(SortOrder.ASCENDING), + ] + _assert_search_results(results, expected_sorts, size, TOTAL_ASSETS, True) + assert mock_logger.call_count == 1 + assert "Bulk search option is enabled." in mock_logger.call_args_list[0][0][0] + mock_logger.reset_mock() + + # Test search() execute(): with `bulk` option using timestamp-based pagination + results = ( + FluentSearch(where_nots=exclude_sdk_terms) + .where(CompoundQuery.active_assets()) + .where(CompoundQuery.asset_type(AtlasGlossaryTerm)) + .page_size(size) + ).execute(client, bulk=True) + expected_sorts = [ + Asset.CREATE_TIME.order(SortOrder.ASCENDING), + Asset.GUID.order(SortOrder.ASCENDING), + ] + _assert_search_results(results, expected_sorts, size, TOTAL_ASSETS, True) + assert mock_logger.call_count == 1 + assert "Bulk search option is enabled." in mock_logger.call_args_list[0][0][0] + mock_logger.reset_mock() + + # Test search(): when the number of results exceeds the predefined threshold, + # the SDK automatically switches to a `bulk` search option using timestamp-based pagination. + with patch.object(IndexSearchResults, "_MASS_EXTRACT_THRESHOLD", 1): + request = ( + FluentSearch(where_nots=exclude_sdk_terms) + .where(CompoundQuery.active_assets()) + .where(CompoundQuery.asset_type(AtlasGlossaryTerm)) + .page_size(size) + ).to_request() + results = client.asset.search(criteria=request) + expected_sorts = [ + Asset.CREATE_TIME.order(SortOrder.ASCENDING), + Asset.GUID.order(SortOrder.ASCENDING), + ] + _assert_search_results(results, expected_sorts, size, TOTAL_ASSETS) + assert mock_logger.call_count < TOTAL_ASSETS + assert ( + "Result size (%s) exceeds threshold (%s)." + in mock_logger.call_args_list[0][0][0] + ) + mock_logger.reset_mock() + + +@patch.object(LOGGER, "debug") +def test_type_filter_duplication_with_pagination(mock_logger, client: AtlanClient): + query = CompoundQuery(where_somes=[CompoundQuery.active_assets()]).to_query() + + dsl = DSL( + query=query, + size=1, + ) + + request = IndexSearchRequest(dsl=dsl) + + results = client.asset.search(criteria=request, bulk=True) + + assert results._criteria.dsl.query and results._criteria.dsl.query.filter # type: ignore + + initial_type_filters = _count_type_filters(results._criteria.dsl.query) # type: ignore + assert initial_type_filters > 0 + + pagination_count = 0 + max_iterations = 5 + + while results.next_page() and pagination_count < max_iterations: + pagination_count += 1 + + current_type_filters = _count_type_filters(results._criteria.dsl.query) # type: ignore + assert current_type_filters == initial_type_filters + + assert pagination_count > 0 + assert mock_logger.call_count >= 1 + + +def _count_type_filters(query): + if not isinstance(query, Bool): + return 0 + + type_field = Referenceable.TYPE_NAME.keyword_field_name + super_type_field = Referenceable.SUPER_TYPE_NAMES.keyword_field_name + + type_filter_count = 0 + + for clause in [query.filter, query.must]: + if not clause: + continue + for filter_item in clause: + if isinstance(filter_item, (Term, Terms)) and filter_item.field in ( + type_field, + super_type_field, + ): + type_filter_count += 1 + + return type_filter_count + + +def test_search_iter(client: AtlanClient): + size = 2 + dsl = DSL( + query=Term.with_state("ACTIVE"), + post_filter=Term.with_type_name("AtlasGlossaryTerm"), + size=size, + ) + request = IndexSearchRequest(dsl=dsl) + results = client.asset.search(criteria=request) + assert results.count > size + assert len([a for a in results]) == results.count + + +def test_search_next_when_start_changed_returns_remaining(client: AtlanClient): + size = 2 + dsl = DSL( + query=Term.with_state("ACTIVE"), + post_filter=Term.with_type_name("Table"), + size=size, + ) + request = IndexSearchRequest( + dsl=dsl, + attributes=["databaseName"], + ) + results = client.asset.search(criteria=request) + assert results.next_page(start=results.count - size) is True + assert len(list(results)) == size + + +@pytest.fixture() +def term_query_value(request): + return VALUES_FOR_TERM_QUERIES[request.param] + + +@pytest.fixture() +def text_query_value(request): + return VALUES_FOR_TEXT_QUERIES[request.param] + + +@pytest.mark.parametrize( + "term_query_value, method, clazz", + [ + (method, method, query) + for query in [Term, Prefix, Regexp, Wildcard] + for method in sorted(dir(query)) + if method.startswith("with_") and method != "with_custom_metadata" + ], + indirect=["term_query_value"], +) +def test_term_queries_factory(client: AtlanClient, term_query_value, method, clazz): + assert hasattr(clazz, method) + query = getattr(clazz, method)(term_query_value) + filter = ~Term.with_type_name("__AtlasAuditEntry") + dsl = DSL(query=query, post_filter=filter, size=1) + request = IndexSearchRequest( + dsl=dsl, + attributes=["name"], + ) + results = client.asset.search(criteria=request) + assert results.count >= 0 + + +@pytest.mark.parametrize( + "with_name", + [ + (method) + for method in dir(Exists) + # if method.startswith("with_") and method != "with_custom_metadata" + if method == "with_create_time_as_timestamp" + ], +) +def test_exists_query_factory(client: AtlanClient, with_name): + assert hasattr(Exists, with_name) + query = getattr(Exists, with_name)() + filter = ~Term(field="__typeName.keyword", value="__AtlasAuditEntry") + dsl = DSL(query=query, post_filter=filter, size=1) + request = IndexSearchRequest( + dsl=dsl, + attributes=["name"], + ) + results = client.asset.search(criteria=request) + assert results.count >= 0 + + +@pytest.mark.parametrize( + "text_query_value, method, clazz", + [ + (method, method, query) + for query in [Match] + for method in sorted(dir(query)) + if method.startswith("with_") + ], + indirect=["text_query_value"], +) +def test_text_queries_factory(client: AtlanClient, text_query_value, method, clazz): + assert hasattr(clazz, method) + query = getattr(clazz, method)(text_query_value) + filter = ~Term.with_type_name("__AtlasAuditEntry") + dsl = DSL(query=query, post_filter=filter, size=1) + request = IndexSearchRequest( + dsl=dsl, + attributes=["name"], + ) + results = client.asset.search(criteria=request) + assert results.count >= 0 + + +@pytest.mark.parametrize( + "value, method, format", + [ + (0, "with_popularity_score", None), + (NOW_AS_TIMESTAMP, "with_create_time_as_timestamp", None), + (NOW_AS_YYYY_MM_DD, "with_create_time_as_date", "yyyy-MM-dd"), + (NOW_AS_TIMESTAMP, "with_update_time_as_timestamp", None), + (NOW_AS_YYYY_MM_DD, "with_update_time_as_date", "yyyy-MM-dd"), + ], +) +def test_range_factory(client: AtlanClient, value, method, format): + assert hasattr(Range, method) + query = getattr(Range, method)(lt=value, format=format) + filter = ~Term(field="__typeName.keyword", value="__AtlasAuditEntry") + dsl = DSL(query=query, post_filter=filter, size=1) + request = IndexSearchRequest( + dsl=dsl, + attributes=["name"], + ) + results = client.asset.search(criteria=request) + assert results.count >= 0 + + +def test_bucket_aggregation(client: AtlanClient): + request = ( + FluentSearch.select() + .aggregate("type", Asset.TYPE_NAME.bucket_by()) + .sort(Asset.CREATE_TIME.order()) + .page_size(0) # only interested in checking aggregation results + ).to_request() + results = client.asset.search(criteria=request) + assert results.aggregations + result = results.aggregations["type"] + assert result + assert result.buckets + assert len(result.buckets) > 0 + for bucket in result.buckets: + assert bucket.key + assert bucket.doc_count + + +def test_nested_bucket_aggregation(client: AtlanClient): + nested_aggs_level_2 = Asset.TYPE_NAME.bucket_by( + nested={"asset_guid": Asset.GUID.bucket_by()} + ) + nested_aggs = Asset.TYPE_NAME.bucket_by(nested={"asset_name": nested_aggs_level_2}) + request = ( + FluentSearch.select() + .aggregate("asset_type", nested_aggs) + .sort(Asset.CREATE_TIME.order()) + .page_size(0) # only interested in checking aggregation results + .to_request() + ) + results = client.asset.search(criteria=request) + + assert results.aggregations + result = results.aggregations["asset_type"] + assert result + assert result.buckets + assert len(result.buckets) > 0 + for bucket in result.buckets: + assert bucket.key + assert bucket.doc_count + assert bucket.nested_results + nested_results = bucket.nested_results["asset_name"] + assert nested_results + # Nested results level 1 + for bucket in nested_results.buckets: + assert bucket.key + assert bucket.doc_count + assert bucket.nested_results + nested_results = bucket.nested_results["asset_guid"] + assert nested_results + # Nested results level 2 + for bucket in nested_results.buckets: + assert bucket.key + assert bucket.doc_count + # Make sure it's not nested further + assert not bucket.nested_results + + +def test_aggregation_source_value(client: AtlanClient): + request = ( + FluentSearch.select() + .aggregate( + "asset_type", + Asset.TYPE_NAME.bucket_by( + nested={ + "asset_description": Asset.DESCRIPTION.bucket_by( + include_source_value=True + ) + }, + ), + ) + .sort(Asset.CREATE_TIME.order()) + .page_size(0) # only interested in checking aggregation results + .to_request() + ) + results = client.asset.search(criteria=request) + + source_value_found = False + assert results.aggregations + result = results.aggregations["asset_type"] + assert result + assert result.buckets + assert len(result.buckets) > 0 + for bucket in result.buckets: + assert bucket.key + assert bucket.doc_count + assert bucket.nested_results + nested_results = bucket.nested_results["asset_description"] + assert nested_results + # Nested results level 1 + for bucket in nested_results.buckets: + if not bucket.key: + continue + assert bucket.key + assert bucket.doc_count + assert bucket.nested_results + if SearchableField.EMBEDDED_SOURCE_VALUE in bucket.nested_results: + nested_results = bucket.nested_results[ + SearchableField.EMBEDDED_SOURCE_VALUE + ] + assert ( + nested_results + and nested_results.hits + and nested_results.hits.hits + and nested_results.hits.hits[0] + ) + assert bucket.get_source_value(Asset.DESCRIPTION) + source_value_found = True + + if not source_value_found: + pytest.fail( + "Failed to retrieve the source value for asset description in the aggregation" + ) + + +def test_metric_aggregation(client: AtlanClient): + request = ( + FluentSearch() + .where(Term.with_type_name("Table")) + .aggregate("avg_columns", Table.COLUMN_COUNT.avg()) + .aggregate("min_columns", Table.COLUMN_COUNT.min()) + .aggregate("max_columns", Table.COLUMN_COUNT.max()) + .aggregate("sum_columns", Table.COLUMN_COUNT.sum()) + .sort(Asset.CREATE_TIME.order()) + ).to_request() + results = client.asset.search(criteria=request) + assert results + assert results.aggregations + assert results.aggregations["avg_columns"] + assert results.aggregations["min_columns"] + assert results.aggregations["max_columns"] + assert results.aggregations["sum_columns"] + + +def test_index_search_with_no_aggregation_results(client: AtlanClient): + test_aggs = {"max_update_time": {"max": {"field": "__modificationTimestamp"}}} + request = ( + FluentSearch(aggregations=test_aggs).where( # type:ignore[arg-type] + Column.QUALIFIED_NAME.startswith("some-non-existent-column-qn") + ) + ).to_request() + response = client.search(criteria=request) + + assert response + assert response.count == 0 + assert not response.aggregations + + +def test_default_sorting(client: AtlanClient): + # Empty sorting + request = ( + FluentSearch().where(Asset.QUALIFIED_NAME.eq("test-qn", case_insensitive=True)) + ).to_request() + response = client.asset.search(criteria=request) + sort_options = response._criteria.dsl.sort # type: ignore + assert response + assert len(sort_options) == 1 + assert sort_options[0].field == ASSET_GUID + + # Sort without GUID + request = ( + FluentSearch() + .where(Asset.QUALIFIED_NAME.eq("test-qn", case_insensitive=True)) + .sort(Asset.QUALIFIED_NAME.order(SortOrder.ASCENDING)) + ).to_request() + response = client.asset.search(criteria=request) + sort_options = response._criteria.dsl.sort # type: ignore + assert response + assert len(sort_options) == 2 + assert sort_options[0].field == QUALIFIED_NAME + assert sort_options[1].field == ASSET_GUID + + # Sort with only GUID + request = ( + FluentSearch() + .where(Asset.QUALIFIED_NAME.eq("test-qn", case_insensitive=True)) + .sort(Asset.GUID.order(SortOrder.ASCENDING)) + ).to_request() + response = client.asset.search(criteria=request) + sort_options = response._criteria.dsl.sort # type: ignore + assert response + assert len(sort_options) == 1 + assert sort_options[0].field == ASSET_GUID + + # Sort with GUID and others + request = ( + FluentSearch() + .where(Asset.QUALIFIED_NAME.eq("test-qn", case_insensitive=True)) + .sort(Asset.QUALIFIED_NAME.order(SortOrder.ASCENDING)) + .sort(Asset.GUID.order(SortOrder.ASCENDING)) + ).to_request() + response = client.asset.search(criteria=request) + sort_options = response._criteria.dsl.sort # type: ignore + assert response + assert len(sort_options) == 2 + assert sort_options[0].field == QUALIFIED_NAME + assert sort_options[1].field == ASSET_GUID + + +def test_persona_search( + client: AtlanClient, + business_definitions_persona: Persona, + known_issues_purpose: Purpose, +): + request1 = ( + FluentSearch.select() + .aggregate("type", Asset.TYPE_NAME.bucket_by()) + .sort(Asset.CREATE_TIME.order()) + .page_size(0) # only interested in checking aggregation results + ).to_request() + + request2 = ( + FluentSearch.select() + .aggregate("type", Asset.TYPE_NAME.bucket_by()) + .sort(Asset.CREATE_TIME.order()) + .page_size(0) # only interested in checking aggregation results + ).to_request() + request2.persona = business_definitions_persona.qualified_name + + results_without_persona = client.asset.search(request1) + results_with_persona = client.asset.search(request2) + + # Make sure the results are different (total assets count != glossary assets count) + assert results_without_persona.count != results_with_persona.count + + +def test_purpose_search(client: AtlanClient, known_issues_purpose: Purpose): + request1 = ( + FluentSearch.select() + .aggregate("type", Asset.TYPE_NAME.bucket_by()) + .sort(Asset.CREATE_TIME.order()) + .page_size(0) # only interested in checking aggregation results + ).to_request() + + request2 = ( + FluentSearch.select() + .aggregate("type", Asset.TYPE_NAME.bucket_by()) + .sort(Asset.CREATE_TIME.order()) + .page_size(0) # only interested in checking aggregation results + ).to_request() + request2.purpose = known_issues_purpose.qualified_name + + results_without_purpose = client.asset.search(request1) + results_with_purpose = client.asset.search(request2) + + # Make sure the results are different (total assets count != assets tagged with "Known issues" count) + assert results_without_purpose.count != results_with_purpose.count + + +def test_read_timeout(client: AtlanClient): + request = (FluentSearch().select()).to_request() + with client_connection( + client=client, read_timeout=0.1, retry=Retry(total=0) + ) as timed_client: + with pytest.raises( + httpx.ReadTimeout, + match="The read operation timed out", + ): + timed_client.asset.search(criteria=request) + + +def test_connect_timeout(client: AtlanClient): + request = FluentSearch().select().to_request() + + # Use a non-routable IP that will definitely timeout + # 192.0.2.1 is reserved for documentation/testing + with client_connection( + client=client, + base_url="http://192.0.2.1:80", # Non-routable test IP + connect_timeout=0.001, + retry=Retry(total=1), + ) as timed_client: + with pytest.raises(httpx.ConnectTimeout): + timed_client.asset.search(criteria=request) diff --git a/tests_v9/integration/test_oauth_client.py b/tests_v9/integration/test_oauth_client.py new file mode 100644 index 000000000..4e4b6f4de --- /dev/null +++ b/tests_v9/integration/test_oauth_client.py @@ -0,0 +1,259 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2026 Atlan Pte. Ltd. +"""Integration tests for OAuth client CRUD operations.""" + +import time +from typing import Generator, List, Optional + +import pytest + +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.errors import NotFoundError +from pyatlan_v9.model.oauth_client import OAuthClientCreateResponse, OAuthClientResponse +from tests_v9.integration.client import TestId + +MODULE_NAME = TestId.make_unique("OAuthClient") + +# Test data +OAUTH_CLIENT_NAME = f"{MODULE_NAME}_test_client" +OAUTH_CLIENT_DESCRIPTION = "Integration test OAuth client" +OAUTH_CLIENT_DESCRIPTION_UPDATED = "Updated integration test OAuth client" +OAUTH_CLIENT_ROLE = "Admin" # Role description +DATA_ASSETS_PERSONA_NAME = "Data Assets" # Pre-existing persona + +# Pagination test constants +PAGINATION_CLIENT_COUNT = 5 +PAGINATION_CLIENT_NAME_PREFIX = f"{MODULE_NAME}_pagination_client" + + +def delete_oauth_client(client: AtlanClient, client_id: str) -> None: + """Helper to delete an OAuth client.""" + client.oauth_client.purge(client_id) + + +@pytest.fixture(scope="module") +def persona_qualified_name(client: AtlanClient) -> str: + """ + Fixture to retrieve the qualified name of the pre-existing 'Data Assets' persona. + """ + personas = client.asset.find_personas_by_name(DATA_ASSETS_PERSONA_NAME) + assert len(personas) >= 1, f"Persona '{DATA_ASSETS_PERSONA_NAME}' not found" + persona_qn = personas[0].qualified_name + assert persona_qn is not None + return persona_qn + + +@pytest.fixture(scope="module") +def pagination_oauth_clients( + client: AtlanClient, +) -> Generator[List[OAuthClientCreateResponse], None, None]: + """ + Fixture to create multiple OAuth clients for pagination testing. + Creates 5 OAuth clients and yields their responses. + Cleans up by deleting all created OAuth clients after tests. + """ + created_clients: List[OAuthClientCreateResponse] = [] + + # Create 5 OAuth clients for pagination testing + for i in range(PAGINATION_CLIENT_COUNT): + response = client.oauth_client.creator( + name=f"{PAGINATION_CLIENT_NAME_PREFIX}_{i}", + role=OAUTH_CLIENT_ROLE, + description=f"Pagination test OAuth client {i}", + ) + assert response is not None + assert response.client_id is not None + created_clients.append(response) + # Small delay to ensure distinct createdAt timestamps for sorting + time.sleep(0.5) + + yield created_clients + + # Cleanup: delete all created OAuth clients + for oauth_client in created_clients: + if oauth_client.client_id: + try: + delete_oauth_client(client, oauth_client.client_id) + except Exception: + pass # Ignore cleanup errors + + +@pytest.fixture(scope="module") +def oauth_client_response( + client: AtlanClient, + persona_qualified_name: str, +) -> Generator[OAuthClientCreateResponse, None, None]: + """ + Fixture to create an OAuth client for testing with persona association. + Yields the create response (which includes client_secret). + Cleans up by deleting the OAuth client after tests. + """ + # Create OAuth client with persona + response = client.oauth_client.creator( + name=OAUTH_CLIENT_NAME, + role=OAUTH_CLIENT_ROLE, + description=OAUTH_CLIENT_DESCRIPTION, + persona_qualified_names=[persona_qualified_name], + ) + assert response is not None + assert response.client_id is not None + assert response.client_secret is not None + + yield response + + # Cleanup + if response.client_id: + delete_oauth_client(client, response.client_id) + + +def _assert_oauth_client_create_response(response: OAuthClientCreateResponse): + """Assert the OAuth client create response has expected values.""" + assert response is not None + assert response.id is not None + assert response.client_id is not None + assert response.client_id.startswith("oauth-client-") + assert response.client_secret is not None + assert response.display_name == OAUTH_CLIENT_NAME + assert response.description == OAUTH_CLIENT_DESCRIPTION + assert response.created_by is not None + assert response.created_at is not None + assert response.token_expiry_seconds is not None + + +def _assert_oauth_client( + oauth_client: OAuthClientResponse, + persona_qn: Optional[str] = None, + is_updated: bool = False, +): + """Assert the OAuth client has expected values.""" + assert oauth_client is not None + assert oauth_client.id is not None + assert oauth_client.client_id is not None + assert oauth_client.client_id.startswith("oauth-client-") + assert oauth_client.display_name == OAUTH_CLIENT_NAME + if is_updated: + assert oauth_client.description == OAUTH_CLIENT_DESCRIPTION_UPDATED + else: + assert oauth_client.description == OAUTH_CLIENT_DESCRIPTION + # Validate persona association if provided + if persona_qn: + assert oauth_client.persona_qualified_names is not None + assert persona_qn in oauth_client.persona_qualified_names + + +def test_oauth_client_create( + client: AtlanClient, + oauth_client_response: OAuthClientCreateResponse, +): + """Test creating an OAuth client.""" + _assert_oauth_client_create_response(oauth_client_response) + + +@pytest.mark.order(after="test_oauth_client_create") +def test_oauth_client_get_by_id( + client: AtlanClient, + oauth_client_response: OAuthClientCreateResponse, + persona_qualified_name: str, +): + """Test retrieving an OAuth client by ID and validate persona association.""" + assert oauth_client_response.client_id is not None + time.sleep(2) # Allow time for eventual consistency + + oauth_client = client.oauth_client.get_by_id(oauth_client_response.client_id) + _assert_oauth_client(oauth_client, persona_qn=persona_qualified_name) + + +@pytest.mark.order(after="test_oauth_client_get_by_id") +def test_oauth_client_get_with_pagination( + client: AtlanClient, + pagination_oauth_clients: List[OAuthClientCreateResponse], +): + """Test retrieving OAuth clients with pagination and iteration. + + This test creates 5 OAuth clients and uses limit=1 to ensure + the pagination logic is properly exercised across multiple API calls. + """ + # Verify we have the expected number of test clients + assert len(pagination_oauth_clients) == PAGINATION_CLIENT_COUNT + + # Get the client IDs we created for verification + created_client_ids = {c.client_id for c in pagination_oauth_clients} + + # Use limit=1 to force multiple API calls for pagination + response = client.oauth_client.get(limit=1, offset=0, sort="createdAt") + assert response is not None + assert response.total_record is not None + # Should have at least our 5 created clients + assert response.total_record >= PAGINATION_CLIENT_COUNT + + # Store the initial total record count + initial_total = response.total_record + + # Test iterating over the paginated response + # This should make multiple API calls (one per page with limit=1) + found_client_ids: set = set() + total_iterated = 0 + + for oauth_client in response: + total_iterated += 1 + if oauth_client.client_id in created_client_ids: + found_client_ids.add(oauth_client.client_id) + + # Verify we iterated through all records + assert total_iterated == initial_total, ( + f"Expected to iterate through {initial_total} records, " + f"but only iterated through {total_iterated}" + ) + + # Verify we found all our created clients + assert found_client_ids == created_client_ids, ( + f"Expected to find all {PAGINATION_CLIENT_COUNT} created clients. " + f"Found: {len(found_client_ids)}, Missing: {created_client_ids - found_client_ids}" + ) + + +@pytest.mark.order(after="test_oauth_client_get_with_pagination") +def test_oauth_client_update_description( + client: AtlanClient, + oauth_client_response: OAuthClientCreateResponse, + persona_qualified_name: str, +): + """Test updating an OAuth client's description.""" + assert oauth_client_response.client_id is not None + time.sleep(2) + + updated = client.oauth_client.updater( + client_id=oauth_client_response.client_id, + description=OAUTH_CLIENT_DESCRIPTION_UPDATED, + ) + _assert_oauth_client(updated, persona_qn=persona_qualified_name, is_updated=True) + + +@pytest.mark.order(after="test_oauth_client_update_description") +def test_oauth_client_verify_update_persisted( + client: AtlanClient, + oauth_client_response: OAuthClientCreateResponse, + persona_qualified_name: str, +): + """Verify that the update was persisted and persona association is maintained.""" + assert oauth_client_response.client_id is not None + time.sleep(2) + + oauth_client = client.oauth_client.get_by_id(oauth_client_response.client_id) + _assert_oauth_client( + oauth_client, persona_qn=persona_qualified_name, is_updated=True + ) + + +def test_oauth_client_create_with_invalid_role_raises_error( + client: AtlanClient, +): + """Test that creating an OAuth client with an invalid role raises an error.""" + + with pytest.raises(NotFoundError) as exc_info: + client.oauth_client.creator( + name="test-invalid-role", + role="InvalidRole", + ) + assert "does not exist" in str(exc_info.value) + assert "Available roles:" in str(exc_info.value) diff --git a/tests_v9/integration/test_open_lineage.py b/tests_v9/integration/test_open_lineage.py new file mode 100644 index 000000000..21c4cf30d --- /dev/null +++ b/tests_v9/integration/test_open_lineage.py @@ -0,0 +1,128 @@ +import time + +import pytest + +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.model.assets import Asset, Connection, Process, SparkJob +from pyatlan_v9.model.audit import AuditSearchRequest +from pyatlan_v9.model.enums import OpenLineageEventType +from pyatlan_v9.model.open_lineage.event import OpenLineageEvent +from pyatlan_v9.model.open_lineage.job import OpenLineageJob +from pyatlan_v9.model.open_lineage.run import OpenLineageRun +from tests_v9.integration.client import TestId, delete_asset + +MODULE_NAME = TestId.make_unique("OL") + + +@pytest.fixture(scope="module") +def connection(client: AtlanClient): + admin_role_guid = client.role_cache.get_id_for_name("$admin") + assert admin_role_guid + response = client.open_lineage.create_connection( + name=MODULE_NAME, admin_roles=[admin_role_guid] + ) + result = response.assets_created(asset_type=Connection)[0] + yield client.asset.get_by_guid( + result.guid, asset_type=Connection, ignore_relationships=False + ) + delete_asset(client, asset_type=Connection, guid=result.guid) + + +def test_open_lineage_integration(connection: Connection, client: AtlanClient): + assert connection is not None + assert connection.name == MODULE_NAME + + namespace = "snowflake://abc123.snowflakecomputing.com" + producer = "https://your.orchestrator/unique/id/123" + job = OpenLineageJob.creator( + connection_name=MODULE_NAME, job_name="dag_123", producer=producer + ) + run = OpenLineageRun.creator(job=job) + id = job.create_input(namespace=namespace, asset_name="OPS.DEFAULT.RUN_STATS") + od = job.create_output(namespace=namespace, asset_name="OPS.DEFAULT.FULL_STATS") + od.to_fields = [ + { + "COLUMN": [ + id.from_field(field_name="COLUMN"), + id.from_field(field_name="ONE"), + id.from_field(field_name="TWO"), + ] + }, + { + "ANOTHER": [ + id.from_field(field_name="THREE"), + ] + }, + ] + start = OpenLineageEvent.creator(run=run, event_type=OpenLineageEventType.START) + start.inputs = [ + id, + job.create_input(namespace=namespace, asset_name="SOME.OTHER.TBL"), + job.create_input(namespace=namespace, asset_name="AN.OTHER.TBL"), + ] + start.outputs = [ + od, + job.create_output(namespace=namespace, asset_name="AN.OTHER.VIEW"), + ] + start.emit(client=client) + + complete = OpenLineageEvent.creator( + run=run, event_type=OpenLineageEventType.COMPLETE + ) + complete.emit(client=client) + + assert job + assert start.event_type == OpenLineageEventType.START + assert complete.event_type == OpenLineageEventType.COMPLETE + + # Awaiting the creation and storage of the Job asset in the backend + time.sleep(30) + + job_qualified_name = f"{connection.qualified_name}/{job.name}" + + # Use the audit search, similar to UI calls, + # to retrieve complete information (process, inputs, outputs) about Spark jobs + results = client.audit.search( + AuditSearchRequest.by_qualified_name( + type_name=SparkJob.__name__, + qualified_name=job_qualified_name, + ) + ) + assert results and results.current_page() and len(results.current_page()) > 0 + job_asset = results.current_page()[0] + assert ( + job_asset + and job_asset.detail + and isinstance(job_asset.detail, Asset) + and job_asset.detail.relationship_attributes + ) + assert job_asset.detail.name == job.name + assert job_asset.detail.qualified_name == job_qualified_name + + assert isinstance(job_asset.detail.relationship_attributes, dict) + inputs = job_asset.detail.relationship_attributes.get("inputs") + outputs = job_asset.detail.relationship_attributes.get("outputs") + process = job_asset.detail.relationship_attributes.get("process") + + assert inputs + assert outputs + assert process + + input_qns = { + input.get("uniqueAttributes", {}).get("qualifiedName") for input in inputs + } + assert f"{connection.qualified_name}/OPS/DEFAULT/RUN_STATS" in input_qns + assert f"{connection.qualified_name}/SOME/OTHER/TBL" in input_qns + assert f"{connection.qualified_name}/AN/OTHER/TBL" in input_qns + + outputs_qns = { + output.get("uniqueAttributes", {}).get("qualifiedName") for output in outputs + } + assert f"{connection.qualified_name}/OPS/DEFAULT/FULL_STATS" in outputs_qns + assert f"{connection.qualified_name}/AN/OTHER/VIEW" in outputs_qns + assert ( + process.get("uniqueAttributes", {}).get("qualifiedName") + == f"{connection.qualified_name}/dag_123/process" + ) + delete_asset(client, asset_type=Process, guid=process.get("guid")) + delete_asset(client, asset_type=SparkJob, guid=job_asset.detail.guid) diff --git a/tests_v9/integration/test_sql_assets.py b/tests_v9/integration/test_sql_assets.py new file mode 100644 index 000000000..31e026408 --- /dev/null +++ b/tests_v9/integration/test_sql_assets.py @@ -0,0 +1,924 @@ +import datetime +import logging +import time +from typing import Callable, List, Optional, Type + +import msgspec +import pytest + +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.model.assets import ( + Asset, + Column, + Connection, + Database, + Procedure, + Readme, + Schema, + Table, + TablePartition, + View, +) +from pyatlan_v9.model.contract import DataContractSpec +from pyatlan_v9.model.enums import AtlanConnectorType, SourceCostUnitType +from pyatlan_v9.model.fluent_search import FluentSearch +from pyatlan_v9.model.response import A, AssetMutationResponse +from pyatlan_v9.model.structs import PopularityInsights +from tests_v9.integration.client import TestId + +LOGGER = logging.getLogger(__name__) + + +@pytest.fixture(scope="module") +def upsert(client: AtlanClient): + guids: List[str] = [] + + def _upsert(asset: Asset) -> AssetMutationResponse: + _response = client.asset.save(asset) + if ( + _response + and _response.mutated_entities + and _response.mutated_entities.CREATE + ): + guids.append(_response.mutated_entities.CREATE[0].guid) + return _response + + yield _upsert + + for guid in reversed(guids): + response = client.asset.purge_by_guid(guid) + if ( + not response + or not response.mutated_entities + or not response.mutated_entities.DELETE + ): + LOGGER.error(f"Failed to remove asset with GUID {guid}.") + + +def verify_asset_created(response, asset_type: Type[A]): + assert response.mutated_entities + + +def verify_asset_updated(response, asset_type: Type[A]): + assert response.mutated_entities + assert not response.mutated_entities.CREATE + assert response.mutated_entities.UPDATE + assert len(response.mutated_entities.UPDATE) == 1 + assets = response.assets_updated(asset_type=asset_type) + assert len(assets) == 1 + + +class TestConnection: + connection: Optional[Connection] = None + + def test_create( + self, + client: AtlanClient, + upsert: Callable[[Asset], AssetMutationResponse], + ): + role = client.role_cache.get_id_for_name("$admin") + assert role + connection_name = TestId.make_unique("INT") + c = Connection.creator( + client=client, + name=connection_name, + connector_type=AtlanConnectorType.SNOWFLAKE, + admin_roles=[role], + ) + assert c.guid + response = upsert(c) + assert response.mutated_entities + assert response.mutated_entities.CREATE + assert len(response.mutated_entities.CREATE) == 1 + assert isinstance(response.mutated_entities.CREATE[0], Connection) + assert response.guid_assignments + assert c.guid in response.guid_assignments + c = response.mutated_entities.CREATE[0] + c = client.asset.get_by_guid(c.guid, Connection, ignore_relationships=False) + assert isinstance(c, Connection) + TestConnection.connection = c + + @pytest.mark.order(after="test_create") + def test_create_for_modification( + self, client: AtlanClient, upsert: Callable[[Asset], AssetMutationResponse] + ): + assert TestConnection.connection + assert TestConnection.connection.name + connection = TestConnection.connection + description = f"{connection.description} more stuff" + connection = Connection.create_for_modification( + qualified_name=TestConnection.connection.qualified_name or "", + name=TestConnection.connection.name, + ) + connection.description = description + response = upsert(connection) + verify_asset_updated(response, Connection) + + @pytest.mark.order(after="test_create") + def test_trim_to_required( + self, client: AtlanClient, upsert: Callable[[Asset], AssetMutationResponse] + ): + assert TestConnection.connection + connection = TestConnection.connection.trim_to_required() + response = upsert(connection) + assert not response.mutated_entities + + +@pytest.mark.order(after="TestConnection") +class TestDatabase: + database: Optional[Database] = None + + def test_create( + self, + client: AtlanClient, + upsert: Callable[[Asset], AssetMutationResponse], + ): + assert TestConnection.connection + connection = TestConnection.connection + assert connection + assert connection.qualified_name + database_name = TestId.make_unique("My_Db") + database = Database.creator( + name=database_name, + connection_qualified_name=connection.qualified_name, + ) + assert database.guid + response = upsert(database) + assert response.mutated_entities + assert response.mutated_entities.CREATE + assert len(response.mutated_entities.CREATE) == 1 + assert isinstance(response.mutated_entities.CREATE[0], Database) + assert response.guid_assignments + assert database.guid in response.guid_assignments + database = response.mutated_entities.CREATE[0] + client.asset.get_by_guid(database.guid, Database, ignore_relationships=False) + TestDatabase.database = database + + @pytest.mark.order(after="test_create") + def test_create_for_modification( + self, client, upsert: Callable[[Asset], AssetMutationResponse] + ): + assert TestDatabase.database + assert TestDatabase.database.qualified_name + assert TestDatabase.database.name + database = Database.create_for_modification( + qualified_name=TestDatabase.database.qualified_name, + name=TestDatabase.database.name, + ) + description = f"{TestDatabase.database.description} more stuff" + database.description = description + response = upsert(database) + verify_asset_updated(response, Database) + + @pytest.mark.order(after="test_create") + def test_trim_to_required( + self, client, upsert: Callable[[Asset], AssetMutationResponse] + ): + assert TestDatabase.database + database = TestDatabase.database.trim_to_required() + response = upsert(database) + assert not response.mutated_entities + + +@pytest.mark.order(after="TestDatabase") +class TestSchema: + schema: Optional[Schema] = None + + def test_create( + self, + client: AtlanClient, + upsert: Callable[[Asset], AssetMutationResponse], + ): + schema_name = TestId.make_unique("My_Schema") + assert TestDatabase.database is not None + assert TestDatabase.database.qualified_name + schema = Schema.creator( + name=schema_name, + database_qualified_name=TestDatabase.database.qualified_name, + ) + response = upsert(schema) + assert (schemas := response.assets_created(asset_type=Schema)) + assert len(schemas) == 1 + schema = client.asset.get_by_guid( + schemas[0].guid, Schema, ignore_relationships=False + ) + assert (databases := response.assets_updated(asset_type=Database)) + assert len(databases) == 1 + database = client.asset.get_by_guid( + databases[0].guid, Database, ignore_relationships=False + ) + assert database.attributes.schemas + schemas = database.attributes.schemas + assert len(schemas) == 1 + assert schemas[0].guid == schema.guid + TestSchema.schema = schema + + def test_overload_creator( + self, + client: AtlanClient, + upsert: Callable[[Asset], AssetMutationResponse], + ): + schema_name = TestId.make_unique("My_Overload_Schema") + assert TestDatabase.database is not None + assert TestDatabase.database.name + assert TestDatabase.database.qualified_name + assert TestConnection.connection is not None + assert TestConnection.connection.qualified_name + + schema = Schema.creator( + name=schema_name, + database_qualified_name=TestDatabase.database.qualified_name, + database_name=TestDatabase.database.name, + connection_qualified_name=TestConnection.connection.qualified_name, + ) + response = upsert(schema) + assert (schemas := response.assets_created(asset_type=Schema)) + assert len(schemas) == 1 + overload_schema = client.asset.get_by_guid( + schemas[0].guid, Schema, ignore_relationships=False + ) + assert (databases := response.assets_updated(asset_type=Database)) + assert len(databases) == 1 + database = client.asset.get_by_guid( + databases[0].guid, Database, ignore_relationships=False + ) + assert database.attributes.schemas + schemas = database.attributes.schemas + assert len(schemas) == 2 + # `database.attributes.schemas` ordering can differ, + # so it's better to use "in" operator + schema_guids = [schema.guid for schema in schemas] + assert TestSchema.schema and TestSchema.schema.guid in schema_guids + assert overload_schema.guid and overload_schema.guid in schema_guids + + @pytest.mark.order(after="test_create") + def test_create_for_modification( + self, client: AtlanClient, upsert: Callable[[Asset], AssetMutationResponse] + ): + assert TestSchema.schema + schema = TestSchema.schema + assert schema.qualified_name + assert schema.name + description = f"{schema.description} more stuff" + schema = Schema.create_for_modification( + qualified_name=schema.qualified_name, name=schema.name + ) + schema.description = description + response = upsert(schema) + verify_asset_updated(response, Schema) + + @pytest.mark.order(after="test_create") + def test_trim_to_required( + self, client: AtlanClient, upsert: Callable[[Asset], AssetMutationResponse] + ): + assert TestSchema.schema + schema = TestSchema.schema.trim_to_required() + response = upsert(schema) + assert not response.mutated_entities + + +@pytest.mark.order(after="TestSchema") +class TestTable: + table: Optional[Table] = None + + @pytest.fixture(scope="module") + def popularity_insight(self): + return PopularityInsights( + record_user="ernest", + record_query_count=2, + record_compute_cost=1.00, + record_total_user_count=3, + record_compute_cost_unit=SourceCostUnitType.BYTES, + record_last_timestamp=datetime.datetime.now(), + record_query_duration=4, + record_warehouse="there", + ) + + def test_create( + self, + client: AtlanClient, + upsert: Callable[[Asset], AssetMutationResponse], + ): + table_name = TestId.make_unique("My_Table") + assert TestSchema.schema is not None + assert TestSchema.schema.qualified_name + table = Table.creator( + name=table_name, + schema_qualified_name=TestSchema.schema.qualified_name, + ) + response = upsert(table) + assert (tables := response.assets_created(asset_type=Table)) + assert len(tables) == 1 + table = client.asset.get_by_guid( + guid=tables[0].guid, asset_type=Table, ignore_relationships=False + ) + assert (schemas := response.assets_updated(asset_type=Schema)) + assert len(schemas) == 1 + schema = client.asset.get_by_guid( + guid=schemas[0].guid, asset_type=Schema, ignore_relationships=False + ) + assert schema.attributes.tables + tables = schema.attributes.tables + assert len(tables) == 1 + assert tables[0].guid == table.guid + TestTable.table = table + + def test_overload_creator( + self, + client: AtlanClient, + upsert: Callable[[Asset], AssetMutationResponse], + ): + table_name = TestId.make_unique("My_Overload_Table") + assert TestSchema.schema is not None + assert TestSchema.schema.name + assert TestSchema.schema.qualified_name + assert TestDatabase.database is not None + assert TestDatabase.database.name + assert TestDatabase.database.qualified_name + assert TestConnection.connection is not None + assert TestConnection.connection.qualified_name + + table = Table.creator( + name=table_name, + schema_qualified_name=TestSchema.schema.qualified_name, + schema_name=TestSchema.schema.name, + database_name=TestDatabase.database.name, + database_qualified_name=TestDatabase.database.qualified_name, + connection_qualified_name=TestConnection.connection.qualified_name, + ) + response = upsert(table) + assert (tables := response.assets_created(asset_type=Table)) + assert len(tables) == 1 + overload_table = client.asset.get_by_guid( + guid=tables[0].guid, asset_type=Table, ignore_relationships=False + ) + assert (schemas := response.assets_updated(asset_type=Schema)) + assert len(schemas) == 1 + schema = client.asset.get_by_guid( + guid=schemas[0].guid, asset_type=Schema, ignore_relationships=False + ) + assert schema.attributes.tables + tables = schema.attributes.tables + assert len(tables) == 2 + # `schema.attributes.tables` ordering can differ, + # so it's better to use "in" operator + table_guids = [table.guid for table in tables] + assert TestTable.table and TestTable.table.guid in table_guids + assert overload_table.guid and overload_table.guid in table_guids + + @pytest.mark.order(after="test_create") + def test_create_for_modification( + self, client: AtlanClient, upsert: Callable[[Asset], AssetMutationResponse] + ): + assert TestTable.table + table = TestTable.table + assert table.qualified_name + assert table.name + description = f"{table.description} more stuff" + table = Table.create_for_modification( + qualified_name=table.qualified_name, name=table.name + ) + table.description = description + response = upsert(table) + verify_asset_updated(response, Table) + + @pytest.mark.order(after="test_create") + def test_trim_to_required( + self, client: AtlanClient, upsert: Callable[[Asset], AssetMutationResponse] + ): + assert TestTable.table + table = TestTable.table.trim_to_required() + response = upsert(table) + assert not response.mutated_entities + + @pytest.mark.order(after="test_trim_to_required") + def test_update_source_read_recent_user_record_list( + self, + client: AtlanClient, + upsert: Callable[[Asset], AssetMutationResponse], + popularity_insight: PopularityInsights, + ): + assert TestTable.table + table = TestTable.table.trim_to_required() + self.time = popularity_insight.record_last_timestamp + table.source_read_recent_user_record_list = [popularity_insight] + response = upsert(table) + verify_asset_updated(response, Table) + + @pytest.mark.order(after="test_update_source_read_recent_user_record_list") + def test_source_read_recent_user_record_list_readable( + self, + client: AtlanClient, + upsert: Callable[[Asset], AssetMutationResponse], + popularity_insight: PopularityInsights, + ): + assert TestTable.table + asset = client.asset.get_by_guid( + guid=TestTable.table.guid, asset_type=Table, ignore_relationships=False + ) + assert asset.source_read_recent_user_record_list + asset_popularity = asset.source_read_recent_user_record_list[0] + self.verify_popularity(asset_popularity, popularity_insight) + + @pytest.mark.order(after="test_update_source_read_recent_user_record_list") + def test_source_read_recent_user_record_list_readable_with_fluent_search( + self, + client: AtlanClient, + upsert: Callable[[Asset], AssetMutationResponse], + popularity_insight: PopularityInsights, + ): + assert TestTable.table + assert TestTable.table.qualified_name + request = ( + FluentSearch.select() + .where(Asset.QUALIFIED_NAME.eq(TestTable.table.qualified_name)) + .include_on_results(Asset.SOURCE_READ_RECENT_USER_RECORD_LIST) + .to_request() + ) + results = client.asset.search(request) + assert results.count == 1 + for result in results: + assert result.source_read_recent_user_record_list + asset_popularity = result.source_read_recent_user_record_list[0] + self.verify_popularity(asset_popularity, popularity_insight) + + def verify_popularity(self, asset_popularity, popularity_insight): + if isinstance(asset_popularity, dict): + ap = msgspec.convert(asset_popularity, PopularityInsights) + else: + ap = asset_popularity + assert popularity_insight.record_user == ap.record_user + assert popularity_insight.record_query_count == ap.record_query_count + assert popularity_insight.record_compute_cost == ap.record_compute_cost + assert popularity_insight.record_query_count == ap.record_query_count + assert popularity_insight.record_total_user_count == ap.record_total_user_count + assert ( + popularity_insight.record_compute_cost_unit == ap.record_compute_cost_unit + ) + assert popularity_insight.record_query_duration == ap.record_query_duration + assert popularity_insight.record_warehouse == ap.record_warehouse + + +@pytest.mark.order(after="TestTable") +class TestView: + view: Optional[View] = None + + def test_create( + self, + client: AtlanClient, + upsert: Callable[[Asset], AssetMutationResponse], + ): + view_name = TestId.make_unique("My_View") + assert TestSchema.schema is not None + assert TestSchema.schema.qualified_name + view = View.creator( + name=view_name, + schema_qualified_name=TestSchema.schema.qualified_name, + ) + response = upsert(view) + assert response.mutated_entities + assert response.mutated_entities.CREATE + assert len(response.mutated_entities.CREATE) == 1 + assert isinstance(response.mutated_entities.CREATE[0], View) + assert response.guid_assignments + view = response.mutated_entities.CREATE[0] + TestView.view = view + + def test_overload_creator( + self, + client: AtlanClient, + upsert: Callable[[Asset], AssetMutationResponse], + ): + view_name = TestId.make_unique("My_View_Overload") + assert TestDatabase.database is not None + assert TestDatabase.database.name + assert TestDatabase.database.qualified_name + assert TestSchema.schema is not None + assert TestSchema.schema.name + assert TestSchema.schema.qualified_name + assert TestConnection.connection is not None + assert TestConnection.connection.qualified_name + + view = View.creator( + name=view_name, + schema_name=TestSchema.schema.name, + schema_qualified_name=TestSchema.schema.qualified_name, + database_name=TestDatabase.database.name, + database_qualified_name=TestDatabase.database.qualified_name, + connection_qualified_name=TestConnection.connection.qualified_name, + ) + response = upsert(view) + assert response.mutated_entities + assert response.mutated_entities.CREATE + assert len(response.mutated_entities.CREATE) == 1 + assert isinstance(response.mutated_entities.CREATE[0], View) + assert response.guid_assignments + + @pytest.mark.order(after="test_create") + def test_create_for_modification( + self, client: AtlanClient, upsert: Callable[[Asset], AssetMutationResponse] + ): + assert TestView.view + view = TestView.view + assert view.qualified_name + assert view.name + description = f"{view.description} more stuff" + view = View.create_for_modification( + qualified_name=view.qualified_name, name=view.name + ) + view.description = description + response = upsert(view) + verify_asset_updated(response, View) + + @pytest.mark.order(after="test_create") + def test_trim_to_required( + self, client: AtlanClient, upsert: Callable[[Asset], AssetMutationResponse] + ): + assert TestView.view + view = TestView.view.trim_to_required() + response = upsert(view) + assert not response.mutated_entities + + +@pytest.mark.order(after="TestView") +class TestProcedure: + procedure: Optional[Procedure] = None + _DEFINITION = """ + BEGIN + insert into `atlanhq.testing_lineage.INSTACART_ALCOHOL_ORDER_TIME_copy` + select * from `atlanhq.testing_lineage.INSTACART_ALCOHOL_ORDER_TIME`; + END + """ + + def test_creator( + self, + client: AtlanClient, + upsert: Callable[[Asset], AssetMutationResponse], + ): + procedure_name = TestId.make_unique("My_Procedure") + assert TestSchema.schema is not None + assert TestSchema.schema.qualified_name + procedure = Procedure.creator( + name=procedure_name, + definition=self._DEFINITION, + schema_qualified_name=TestSchema.schema.qualified_name, + ) + response = upsert(procedure) + assert response.mutated_entities + assert response.mutated_entities.CREATE + assert len(response.mutated_entities.CREATE) == 1 + assert isinstance(response.mutated_entities.CREATE[0], Procedure) + assert response.guid_assignments + procedure = response.mutated_entities.CREATE[0] + TestProcedure.procedure = procedure + + def test_overload_creator( + self, + client: AtlanClient, + upsert: Callable[[Asset], AssetMutationResponse], + ): + procedure_name = TestId.make_unique("My_Procedure_Overload") + assert TestDatabase.database is not None + assert TestDatabase.database.name + assert TestDatabase.database.qualified_name + assert TestSchema.schema is not None + assert TestSchema.schema.name + assert TestSchema.schema.qualified_name + assert TestConnection.connection is not None + assert TestConnection.connection.qualified_name + + procedure = Procedure.creator( + name=procedure_name, + definition=self._DEFINITION, + schema_name=TestSchema.schema.name, + schema_qualified_name=TestSchema.schema.qualified_name, + database_name=TestDatabase.database.name, + database_qualified_name=TestDatabase.database.qualified_name, + connection_qualified_name=TestConnection.connection.qualified_name, + ) + response = upsert(procedure) + assert response.mutated_entities + assert response.mutated_entities.CREATE + assert len(response.mutated_entities.CREATE) == 1 + assert isinstance(response.mutated_entities.CREATE[0], Procedure) + assert response.guid_assignments + + @pytest.mark.order(after="test_creator") + def test_updater( + self, client: AtlanClient, upsert: Callable[[Asset], AssetMutationResponse] + ): + assert TestProcedure.procedure + procedure = TestProcedure.procedure + assert procedure.qualified_name + assert procedure.name + assert procedure.definition + description = f"{procedure.description} more stuff" + procedure = Procedure.updater( + qualified_name=procedure.qualified_name, + name=procedure.name, + definition=procedure.definition, + ) + procedure.description = description + response = upsert(procedure) + verify_asset_updated(response, Procedure) + + @pytest.mark.order(after="test_creator") + def test_trim_to_required( + self, client: AtlanClient, upsert: Callable[[Asset], AssetMutationResponse] + ): + assert TestProcedure.procedure + procedure = TestProcedure.procedure.trim_to_required() + response = upsert(procedure) + assert not response.mutated_entities + + +@pytest.mark.order(after="TestView") +class TestTablePartition: + table_partition: Optional[TablePartition] = None + + def test_creator( + self, + client: AtlanClient, + upsert: Callable[[Asset], AssetMutationResponse], + ): + table_partition_name = TestId.make_unique("My_Table_Partition") + assert TestTable.table is not None + assert TestTable.table.qualified_name + table_partition = TablePartition.creator( + name=table_partition_name, + table_qualified_name=TestTable.table.qualified_name, + ) + response = upsert(table_partition) + assert response.mutated_entities + assert response.mutated_entities.CREATE + assert len(response.mutated_entities.CREATE) == 1 + assert isinstance(response.mutated_entities.CREATE[0], TablePartition) + assert response.guid_assignments + table_partition = response.mutated_entities.CREATE[0] + TestTablePartition.table_partition = table_partition + + def test_overload_creator( + self, + client: AtlanClient, + upsert: Callable[[Asset], AssetMutationResponse], + ): + table_partition_name = TestId.make_unique("My_Table_Partition_Overload") + assert TestConnection.connection is not None + assert TestConnection.connection.qualified_name + assert TestDatabase.database is not None + assert TestDatabase.database.name + assert TestDatabase.database.qualified_name + assert TestSchema.schema is not None + assert TestSchema.schema.name + assert TestSchema.schema.qualified_name + assert TestTable.table is not None + assert TestTable.table.name + assert TestTable.table.qualified_name + + table_partition = TablePartition.creator( + name=table_partition_name, + connection_qualified_name=TestConnection.connection.qualified_name, + database_name=TestDatabase.database.name, + database_qualified_name=TestDatabase.database.qualified_name, + schema_name=TestSchema.schema.name, + schema_qualified_name=TestSchema.schema.qualified_name, + table_name=TestTable.table.name, + table_qualified_name=TestTable.table.qualified_name, + ) + response = upsert(table_partition) + assert response.mutated_entities + assert response.mutated_entities.CREATE + assert len(response.mutated_entities.CREATE) == 1 + assert isinstance(response.mutated_entities.CREATE[0], TablePartition) + assert response.guid_assignments + + @pytest.mark.order(after="test_creator") + def test_updater( + self, client: AtlanClient, upsert: Callable[[Asset], AssetMutationResponse] + ): + assert TestTablePartition.table_partition + table_partition = TestTablePartition.table_partition + assert table_partition.qualified_name + assert table_partition.name + description = f"{table_partition.description} more stuff" + table_partition = TablePartition.updater( + qualified_name=table_partition.qualified_name, + name=table_partition.name, + ) + table_partition.description = description + response = upsert(table_partition) + verify_asset_updated(response, TablePartition) + + @pytest.mark.order(after="test_creator") + def test_trim_to_required( + self, client: AtlanClient, upsert: Callable[[Asset], AssetMutationResponse] + ): + assert TestTablePartition.table_partition + table_partition = TestTablePartition.table_partition.trim_to_required() + response = upsert(table_partition) + assert not response.mutated_entities + + +@pytest.mark.order(after="TestView") +class TestColumn: + column: Optional[Column] = None + + def test_create( + self, + client: AtlanClient, + upsert: Callable[[Asset], AssetMutationResponse], + ): + column_name = TestId.make_unique("My_Column") + assert TestTable.table is not None + assert TestTable.table.qualified_name + column = Column.creator( + name=column_name, + parent_qualified_name=TestTable.table.qualified_name, + parent_type=Table, + order=1, + ) + response = client.asset.save(column) + assert (columns := response.assets_created(asset_type=Column)) + assert len(columns) == 1 + column = client.asset.get_by_guid( + asset_type=Column, guid=columns[0].guid, ignore_relationships=False + ) + table = client.asset.get_by_guid( + asset_type=Table, guid=TestTable.table.guid, ignore_relationships=False + ) + assert table.attributes.columns + columns = table.attributes.columns + assert len(columns) == 1 + assert columns[0].guid == column.guid + TestColumn.column = column + + def _assert_table_contract(self, table_contract, table_column, is_raw=False): + if is_raw: + assert table_contract.startswith("---\n# Generated by Atlan on") + assert table_contract and isinstance(table_contract, str) + assert "columns:" in table_contract + assert f"- name: {table_column.column.name}" in table_contract + + def test_contact_init_spec(self, client: AtlanClient): + assert TestTable.table + assert TestColumn.column + assert TestColumn.column.name + assert TestTable.table.type_name + assert TestTable.table.qualified_name + # Ensure the column is properly indexed; otherwise, + # the generated spec may return empty (columns: []). + time.sleep(5) + contact_spec_str = client.contracts.generate_initial_spec(asset=TestTable.table) + assert contact_spec_str + self._assert_table_contract(contact_spec_str, TestColumn) + + # Test spec model yaml conversion + contract_spec = DataContractSpec.from_yaml(contact_spec_str) + assert ( + contract_spec + and contract_spec.dataset # type: ignore[union-attr] + and contract_spec.dataset == TestTable.table.name # type: ignore[union-attr] + # Ensure non-modeled fields are retained correctly + # (enabled by AtlanYamlModel → Config → Extra.allow) + and contract_spec.columns + and len(contract_spec.columns) >= 1 + and contract_spec.columns[0].tags == contract_spec.columns[0].terms == [] + ) + self._assert_table_contract(contract_spec.to_yaml(), TestColumn, is_raw=False) # type: ignore[union-attr] + + def test_overload_creator( + self, + client: AtlanClient, + ): + column_name = TestId.make_unique("My_Column_Overload") + assert TestTable.table is not None + assert TestTable.table.name + assert TestTable.table.qualified_name + assert TestDatabase.database is not None + assert TestDatabase.database.name + assert TestDatabase.database.qualified_name + assert TestSchema.schema is not None + assert TestSchema.schema.name + assert TestSchema.schema.qualified_name + assert TestConnection.connection is not None + assert TestConnection.connection.qualified_name + + column = Column.creator( + name=column_name, + parent_type=Table, + order=2, + parent_name=TestTable.table.name, + parent_qualified_name=TestTable.table.qualified_name, + database_name=TestDatabase.database.name, + database_qualified_name=TestDatabase.database.qualified_name, + schema_name=TestSchema.schema.name, + schema_qualified_name=TestSchema.schema.qualified_name, + table_name=TestTable.table.name, + table_qualified_name=TestTable.table.qualified_name, + connection_qualified_name=TestConnection.connection.qualified_name, + ) + response = client.asset.save(column) + + assert (columns := response.assets_created(asset_type=Column)) + assert len(columns) == 1 + overload_column = client.asset.get_by_guid( + asset_type=Column, guid=columns[0].guid, ignore_relationships=False + ) + table = client.asset.get_by_guid( + asset_type=Table, guid=TestTable.table.guid, ignore_relationships=False + ) + assert table.attributes.columns + columns = table.attributes.columns + + assert len(columns) == 2 + # `table.attributes.columns` ordering can differ, + # so it's better to use "in" operator + column_guids = [column.guid for column in columns] + assert TestColumn.column and TestColumn.column.guid in column_guids + assert overload_column.guid and overload_column.guid in column_guids + assert overload_column.attributes + assert overload_column.attributes.schema_name == TestSchema.schema.name + assert ( + overload_column.attributes.schema_qualified_name + == TestSchema.schema.qualified_name + ) + assert overload_column.attributes.database_name == TestDatabase.database.name + assert ( + overload_column.attributes.database_qualified_name + == TestDatabase.database.qualified_name + ) + + @pytest.mark.order(after="test_create") + def test_create_for_modification( + self, client: AtlanClient, upsert: Callable[[Asset], AssetMutationResponse] + ): + assert TestColumn.column + column = TestColumn.column + assert column.qualified_name + assert column.name + description = f"{column.description} more stuff" + column = Column.create_for_modification( + qualified_name=column.qualified_name, name=column.name + ) + column.description = description + response = upsert(column) + verify_asset_updated(response, Column) + + @pytest.mark.order(after="test_create") + def test_trim_to_required( + self, client: AtlanClient, upsert: Callable[[Asset], AssetMutationResponse] + ): + assert TestColumn.column + column = TestColumn.column.trim_to_required() + response = upsert(column) + assert not response.mutated_entities + + +@pytest.mark.order(after="TestColumn") +class TestReadme: + readme: Optional[Readme] = None + CONTENT = "

Important

" + + def test_create( + self, + client: AtlanClient, + upsert: Callable[[Asset], AssetMutationResponse], + ): + assert TestColumn.column and TestColumn.column.guid + readme = Readme.creator(asset=TestColumn.column, content=self.CONTENT) + response = upsert(readme) + assert (reaadmes := response.assets_created(asset_type=Readme)) + assert len(reaadmes) == 1 + assert (columns := response.assets_updated(asset_type=Column)) + assert len(columns) == 1 + readme = client.asset.get_by_guid( + guid=reaadmes[0].guid, asset_type=Readme, ignore_relationships=False + ) + assert readme.description == self.CONTENT + TestReadme.readme = readme + + @pytest.mark.order(after="test_create") + def test_create_for_modification( + self, client: AtlanClient, upsert: Callable[[Asset], AssetMutationResponse] + ): + assert TestReadme.readme + readme = TestReadme.readme + assert readme.qualified_name + assert readme.name + description = f"{readme.description} more stuff" + readme = Readme.create_for_modification( + qualified_name=readme.qualified_name, name=readme.name + ) + readme.description = description + response = upsert(readme) + verify_asset_updated(response, Readme) + + @pytest.mark.order(after="test_create") + def test_trim_to_required( + self, client: AtlanClient, upsert: Callable[[Asset], AssetMutationResponse] + ): + assert TestReadme.readme + readme = TestReadme.readme + readme = readme.trim_to_required() + response = upsert(readme) + assert not response.mutated_entities diff --git a/tests_v9/integration/test_sso_client.py b/tests_v9/integration/test_sso_client.py new file mode 100644 index 000000000..f6529c174 --- /dev/null +++ b/tests_v9/integration/test_sso_client.py @@ -0,0 +1,200 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. +import time +from typing import Generator + +import pytest + +from pyatlan.client.common.sso import ( + GROUP_MAPPER_ATTRIBUTE, + GROUP_MAPPER_SYNC_MODE, + IDP_GROUP_MAPPER, +) +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.errors import InvalidRequestError +from pyatlan_v9.model.enums import AtlanSSO +from pyatlan_v9.model.group import AtlanGroup +from pyatlan_v9.model.sso import SSOMapper +from tests_v9.integration.client import TestId + +FIXED_USER = "aryaman" +MODULE_NAME = TestId.make_unique("SSOClient") + +GROUP_NAME = MODULE_NAME +SSO_GROUP_NAME = "test-sso-group" +SSO_GROUP_NAME_UPDATED = "test-sso-group-updated" + + +def delete_group(client: AtlanClient, guid: str) -> None: + client.group.purge(guid) + + +def delete_sso_mapping(client: AtlanClient, group_map_id: str) -> None: + response = client.sso.delete_group_mapping( + sso_alias=AtlanSSO.JUMPCLOUD, group_map_id=group_map_id + ) + assert response is None + + +@pytest.fixture(scope="module") +def group(client: AtlanClient) -> Generator[AtlanGroup, None, None]: + to_create = AtlanGroup.creator(GROUP_NAME) + fixed_user = client.user.get_by_username(FIXED_USER) + assert fixed_user + g = client.group.creator(group=to_create, user_ids=[str(fixed_user.id)]) + groups = client.group.get_by_name(alias=GROUP_NAME) + assert groups + assert groups.records is not None + assert len(groups.records) == 1 + yield groups.records[0] + assert g.group + delete_group(client, g.group) + + +@pytest.fixture(scope="module") +def sso_mapping( + client: AtlanClient, + group: AtlanGroup, +) -> Generator[SSOMapper, None, None]: + assert group + assert group.id + response = client.sso.create_group_mapping( + sso_alias=AtlanSSO.JUMPCLOUD, atlan_group=group, sso_group_name=SSO_GROUP_NAME + ) + assert response + + azure_group_mapping = None + sso_mappings = client.sso.get_all_group_mappings(sso_alias=AtlanSSO.JUMPCLOUD) + for mapping in sso_mappings: + if ( + group.id + and group.id in str(mapping.name) + and mapping.identity_provider_mapper == IDP_GROUP_MAPPER + ): + azure_group_mapping = mapping + break + assert azure_group_mapping and azure_group_mapping.id + yield azure_group_mapping + delete_sso_mapping(client, azure_group_mapping.id) + + +def _assert_sso_group_mapping( + group: AtlanGroup, sso_mapping: SSOMapper, is_updated: bool = False +): + assert sso_mapping + assert sso_mapping.id + assert sso_mapping.identity_provider_alias == AtlanSSO.JUMPCLOUD + assert sso_mapping.identity_provider_mapper == IDP_GROUP_MAPPER + assert sso_mapping.config.attributes == "[]" + assert sso_mapping.config.group_name == group.name + assert sso_mapping.config.attribute_values_regex is None + assert sso_mapping.config.attribute_friendly_name is None + + assert sso_mapping.config.sync_mode == GROUP_MAPPER_SYNC_MODE + assert sso_mapping.config.attribute_name == GROUP_MAPPER_ATTRIBUTE + if is_updated: + assert sso_mapping.name + assert sso_mapping.config.attribute_value == SSO_GROUP_NAME_UPDATED + else: + assert sso_mapping.name + assert group.id and (group.id in str(sso_mapping.name)) + assert sso_mapping.config.attribute_value == SSO_GROUP_NAME + + +def test_sso_create_group_mapping( + client: AtlanClient, + group: AtlanGroup, + sso_mapping: SSOMapper, +): + assert group + assert sso_mapping + _assert_sso_group_mapping(group, sso_mapping) + + +@pytest.mark.order(after="test_sso_create_group_mapping") +def test_sso_create_group_mapping_again_raises_invalid_request_error( + client: AtlanClient, + group: AtlanGroup, + sso_mapping: SSOMapper, +): + assert group + assert sso_mapping + with pytest.raises(InvalidRequestError) as err: + client.sso.create_group_mapping( + sso_alias=AtlanSSO.JUMPCLOUD, + atlan_group=group, + sso_group_name=SSO_GROUP_NAME, + ) + assert ( + f"ATLAN-PYTHON-400-058 SSO group mapping already exists between " + f"{group.alias} (Atlan group) <-> {SSO_GROUP_NAME} (SSO group)" + ) in str(err.value) + + +@pytest.mark.order( + after="test_sso_create_group_mapping_again_raises_invalid_request_error" +) +def test_sso_retrieve_group_mapping( + client: AtlanClient, + group: AtlanGroup, + sso_mapping: SSOMapper, +): + assert group + assert sso_mapping + assert sso_mapping.id + time.sleep(5) + + retrieved_sso_mapping = client.sso.get_group_mapping( + sso_alias=AtlanSSO.JUMPCLOUD, group_map_id=sso_mapping.id + ) + _assert_sso_group_mapping(group, retrieved_sso_mapping) + + +@pytest.mark.order(after="test_sso_retrieve_group_mapping") +def test_sso_retrieve_all_group_mappings( + client: AtlanClient, + group: AtlanGroup, + sso_mapping: SSOMapper, +): + assert group + assert group.id + assert sso_mapping + time.sleep(5) + + retrieved_mappings = client.sso.get_all_group_mappings(sso_alias=AtlanSSO.JUMPCLOUD) + assert len(retrieved_mappings) >= 1 + mapping_found = False + for mapping in retrieved_mappings: + if ( + group.id in str(mapping.name) + and mapping.identity_provider_mapper == IDP_GROUP_MAPPER + ): + mapping_found = True + _assert_sso_group_mapping(group, mapping) + break + if not mapping_found: + pytest.fail( + f"{group.alias} (Atlan Group) <-> ({sso_mapping.config.attribute_value}) " + f"{AtlanSSO.JUMPCLOUD} SSO group mapping not found." + ) + + +@pytest.mark.order(after="test_sso_retrieve_all_group_mappings") +def test_update_group_mapping( + client: AtlanClient, + group: AtlanGroup, + sso_mapping: SSOMapper, +): + assert group + assert sso_mapping + assert sso_mapping.id + assert sso_mapping.name + + updated_mapping = client.sso.update_group_mapping( + sso_alias=AtlanSSO.JUMPCLOUD, + atlan_group=group, + group_map_id=sso_mapping.id, + group_map_name=sso_mapping.name, + sso_group_name=SSO_GROUP_NAME_UPDATED, + ) + _assert_sso_group_mapping(group, updated_mapping, True) diff --git a/tests_v9/integration/test_task_client.py b/tests_v9/integration/test_task_client.py new file mode 100644 index 000000000..0938de62c --- /dev/null +++ b/tests_v9/integration/test_task_client.py @@ -0,0 +1,112 @@ +import time +from typing import Generator + +import pytest + +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.model.assets import Column +from pyatlan_v9.model.enums import AtlanConnectorType, AtlanTaskType, SortOrder +from pyatlan_v9.model.fluent_tasks import FluentTasks +from pyatlan_v9.model.search import SortItem +from pyatlan_v9.model.task import AtlanTask, TaskSearchRequest +from pyatlan_v9.model.typedef import AtlanTagDef +from tests_v9.integration.client import TestId + +MODULE_NAME = TestId.make_unique("TaskClient") +TAG_NAME = MODULE_NAME + +DB_NAME = "WIDE_WORLD_IMPORTERS" +TABLE_NAME = "PACKAGETYPES" +COLUMN_NAME = "PACKAGETYPENAME" +SCHEMA_NAME = "BRONZE_WAREHOUSE" + + +@pytest.fixture(scope="module") +def snowflake_conn(client: AtlanClient): + return client.asset.find_connections_by_name( + "production", AtlanConnectorType.SNOWFLAKE + )[0] + + +@pytest.fixture(scope="module") +def snowflake_column_qn(snowflake_conn): + return f"{snowflake_conn.qualified_name}/{DB_NAME}/{SCHEMA_NAME}/{TABLE_NAME}/{COLUMN_NAME}" + + +@pytest.fixture() +def snowflake_column( + client: AtlanClient, snowflake_column_qn +) -> Generator[Column, None, None]: + client.asset.add_atlan_tags( + asset_type=Column, + qualified_name=snowflake_column_qn, + atlan_tag_names=[TAG_NAME], + propagate=True, + remove_propagation_on_delete=True, + restrict_lineage_propagation=True, + ) + snowflake_column = client.asset.get_by_qualified_name( + snowflake_column_qn, asset_type=Column, ignore_relationships=False + ) + yield snowflake_column + + client.asset.remove_atlan_tag( + asset_type=Column, + qualified_name=snowflake_column_qn, + atlan_tag_name=TAG_NAME, + ) + + +@pytest.fixture() +def task_search_request(snowflake_column: Column) -> TaskSearchRequest: + return ( + FluentTasks() + .page_size(1) + .sort( + by=SortItem( + field=AtlanTask.START_TIME.numeric_field_name, + order=SortOrder.DESCENDING, + ) + ) + .where(AtlanTask.ENTITY_GUID.eq(snowflake_column.guid)) + .where(AtlanTask.TYPE.eq(AtlanTaskType.CLASSIFICATION_PROPAGATION_ADD.value)) + .to_request() + ) + + +@pytest.fixture(scope="module") +def atlan_tag_def(make_atlan_tag) -> AtlanTagDef: + return make_atlan_tag(TAG_NAME) + + +def test_task_search( + client: AtlanClient, atlan_tag_def, task_search_request, snowflake_column +): + assert snowflake_column + assert snowflake_column.atlan_tags + + for tag in snowflake_column.atlan_tags: + if str(tag.type_name) == TAG_NAME: + break + pytest.fail(f"Tag '{TAG_NAME}' missing in {snowflake_column}") + + count = 0 + # TODO: replace with exponential back-off and jitter + while count < 10: + tasks = client.tasks.search(request=task_search_request) + assert tasks + if tasks.count >= 1: + task = next(iter(tasks)) + break + count += 1 + time.sleep(5) + + assert task.guid + assert task.status + assert task.created_by + assert task.updated_time + assert task.parameters + assert task.classification_id + assert task.attempt_count is not None and task.attempt_count >= 0 + assert task.entity_guid == snowflake_column.guid + assert task.type == AtlanTaskType.CLASSIFICATION_PROPAGATION_ADD diff --git a/tests_v9/integration/test_workflow_client.py b/tests_v9/integration/test_workflow_client.py new file mode 100644 index 000000000..e78bf6ba0 --- /dev/null +++ b/tests_v9/integration/test_workflow_client.py @@ -0,0 +1,414 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. +import time +from datetime import datetime, timedelta, timezone +from typing import Generator + +import pytest + +from pyatlan import utils +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.client.workflow import V9WorkflowClient as WorkflowClient +from pyatlan_v9.model.assets import Connection +from pyatlan_v9.model.credential import Credential, CredentialResponse +from pyatlan_v9.model.enums import ( + AtlanConnectorType, + AtlanWorkflowPhase, + WorkflowPackage, +) +from pyatlan_v9.model.packages.snowflake_miner import SnowflakeMiner +from pyatlan_v9.model.workflow import WorkflowResponse, WorkflowSchedule +from tests_v9.integration.client import TestId, delete_asset +from tests_v9.integration.connection_test import create_connection + +MODULE_NAME = TestId.make_unique("WorfklowClient") +WORKFLOW_TEMPLATE_REF = "workflowTemplateRef" +WORKFLOW_SCHEDULE_SCHEDULE = "45 4 * * *" +WORKFLOW_SCHEDULE_TIMEZONE = "Asia/Kolkata" +WORKFLOW_SCHEDULE_UPDATED_1 = "45 5 * * *" +WORKFLOW_SCHEDULE_TIMEZONE_UPDATED_1 = "Europe/Paris" +WORKFLOW_SCHEDULE_UPDATED_2 = "45 6 * * *" +WORKFLOW_SCHEDULE_TIMEZONE_UPDATED_2 = "Europe/London" +WORKFLOW_SCHEDULE_UPDATED_3 = "45 7 * * *" +WORKFLOW_SCHEDULE_TIMEZONE_UPDATED_3 = "Europe/Dublin" + + +@pytest.fixture(scope="module") +def create_credentials( + client: AtlanClient, +) -> Generator[CredentialResponse, None, None]: + """Creates a new credential using the Atlan API.""" + credentials_name = f"default-spark-{int(utils.get_epoch_timestamp())}-0" + + credentials = Credential( + name=credentials_name, + auth_type="atlan_api_key", + connector_config_name="atlan-connectors-spark", + connector="spark", + username="test-username", + password="12345", + connector_type="event", + host="test-host", + port=123, + ) + + create_credentials = client.credentials.creator(credentials) + guid = create_credentials.id + if guid is None: + raise ValueError("Failed to retrieve GUID from created credentials.") + + yield create_credentials + + response = delete_credentials(client, guid=guid) + assert response is None + + +def delete_credentials(client: AtlanClient, guid: str): + response = client.credentials.purge_by_guid(guid=guid) + return response + + +@pytest.fixture(scope="module") +def connection(client: AtlanClient) -> Generator[Connection, None, None]: + connection = create_connection( + client=client, name=MODULE_NAME, connector_type=AtlanConnectorType.SNOWFLAKE + ) + yield connection + delete_asset(client, guid=connection.guid, asset_type=Connection) + + +def delete_workflow(client: AtlanClient, workflow_name: str) -> None: + client.workflow.delete(workflow_name=workflow_name) + + +@pytest.fixture(scope="module") +def workflow( + client: AtlanClient, connection: Connection +) -> Generator[WorkflowResponse, None, None]: + assert connection and connection.qualified_name + miner = ( + SnowflakeMiner(connection_qualified_name=connection.qualified_name) + .s3( + s3_bucket="test-s3-bucket", + s3_prefix="test-s3-prefix", + s3_bucket_region="test-s3-bucket-region", + sql_query_key="TEST_QUERY", + default_database_key="TEST_SNOWFLAKE", + default_schema_key="TEST_SCHEMA", + session_id_key="TEST_SESSION_ID", + ) + .popularity_window(days=15) + .native_lineage(enabled=True) + .custom_config(config={"test": True, "feature": 1234}) + .to_workflow() + ) + schedule = WorkflowSchedule( + cron_schedule=WORKFLOW_SCHEDULE_SCHEDULE, timezone=WORKFLOW_SCHEDULE_TIMEZONE + ) + workflow = client.workflow.run(miner, workflow_schedule=schedule) + assert workflow + # Adding some delay to make sure + # the workflow run is indexed in ES. + time.sleep(30) + yield workflow + assert workflow.metadata and workflow.metadata.name + delete_workflow(client, workflow.metadata.name) + + +def test_workflow_find_by_methods(client: AtlanClient): + results = client.workflow.find_by_type( + prefix=WorkflowPackage.SNOWFLAKE, max_results=10 + ) + assert results + assert len(results) >= 1 + + workflow_id = results[0].id + assert workflow_id + workflow = client.workflow.find_by_id(id=workflow_id) + assert workflow + assert workflow.id and workflow.id == workflow_id + + workflow = client.workflow.find_by_id(id="invalid-id") + assert workflow is None + + +def test_workflow_get_runs_and_stop(client: AtlanClient, workflow: WorkflowResponse): + # Retrieve the lastest workflow run + assert workflow and workflow.metadata and workflow.metadata.name + runs = client.workflow.get_runs( + workflow_name=workflow.metadata.name, workflow_phase=AtlanWorkflowPhase.RUNNING + ) + assert runs and runs.count == 1 + current_page = runs.current_page() + assert current_page is not None and len(current_page) == 1 + run = current_page[0] + assert run and run.id + assert workflow.metadata.name and (workflow.metadata.name in run.id) + + # Stop the running workflow + run_response = client.workflow.stop(workflow_run_id=run.id) + assert run_response + assert ( + run_response.status and run_response.status.phase == AtlanWorkflowPhase.RUNNING + ) + assert ( + run_response.status.stored_workflow_template_spec + and run_response.status.stored_workflow_template_spec.get( + WORKFLOW_TEMPLATE_REF + ).get("name") + == workflow.metadata.name + ) + + # Test workflow monitoring + workflow_status = client.workflow.monitor(workflow_response=workflow) + assert workflow_status == AtlanWorkflowPhase.FAILED + + # Test workflow monitoring by providing workflow name directly + workflow_name = workflow.metadata.name + workflow_status = client.workflow.monitor(workflow_name=workflow_name) + assert workflow_status == AtlanWorkflowPhase.FAILED + + # Test find run by id + workflow_run = client.workflow.find_run_by_id(id=run.id) + assert ( + workflow_run + and workflow_run.source + and workflow_run.source.status + and workflow_run.source.status.phase == AtlanWorkflowPhase.FAILED + ) + + # Test find run by status and time range + runs_status = client.workflow.find_runs_by_status_and_time_range( + [AtlanWorkflowPhase.FAILED], started_at="now-1h" + ) + assert runs_status + workflow_run_status = runs_status.current_page()[0] # type: ignore + start_time = workflow_run_status.source.status.started_at # type: ignore + start_datetime = datetime.strptime(start_time, "%Y-%m-%dT%H:%M:%SZ") # type: ignore + start_datetime = start_datetime.replace(tzinfo=timezone.utc) + current_time = datetime.now(timezone.utc) + time_diff = current_time - start_datetime + assert ( + workflow_run_status + and workflow_run_status.source + and workflow_run_status.source.status + and workflow_run_status.source.status.phase == AtlanWorkflowPhase.FAILED + and time_diff < timedelta(hours=1) + ) + + +def test_workflow_get_all_scheduled_runs( + client: AtlanClient, workflow: WorkflowResponse +): + runs = client.workflow.get_all_scheduled_runs() + + assert workflow and workflow.metadata and workflow.metadata.name + scheduled_workflow_name = f"{workflow.metadata.name}-cron" + assert runs and len(runs) >= 1 + + found = any( + run.metadata and run.metadata.name == scheduled_workflow_name for run in runs + ) + + if not found: + pytest.fail( + f"Unable to find scheduled run for workflow: {workflow.metadata.name}" + ) + + +def _assert_scheduled_run(client: AtlanClient, workflow: WorkflowResponse): + assert workflow and workflow.metadata and workflow.metadata.name + scheduled_workflow = client.workflow.get_scheduled_run( + workflow_name=workflow.metadata.name + ) + scheduled_workflow_name = f"{workflow.metadata.name}-cron" + assert ( + scheduled_workflow + and scheduled_workflow.metadata + and scheduled_workflow.metadata.name == scheduled_workflow_name + ) + + +def test_workflow_get_scheduled_run(client: AtlanClient, workflow: WorkflowResponse): + _assert_scheduled_run(client, workflow) + + +def _assert_add_schedule(workflow, scheduled_workflow, schedule, timezone): + assert scheduled_workflow + assert scheduled_workflow.metadata + assert scheduled_workflow.metadata.name == workflow.metadata.name + assert scheduled_workflow.metadata.annotations + assert ( + scheduled_workflow.metadata.annotations.get( + WorkflowClient._WORKFLOW_RUN_SCHEDULE + ) + == schedule + ) + assert ( + scheduled_workflow.metadata.annotations.get( + WorkflowClient._WORKFLOW_RUN_TIMEZONE + ) + == timezone + ) + + +def _assert_remove_schedule(response, workflow): + assert response + assert response.metadata.annotations + assert response.metadata.name == workflow.metadata.name + assert WorkflowClient._WORKFLOW_RUN_TIMEZONE in response.metadata.annotations + assert WorkflowClient._WORKFLOW_RUN_SCHEDULE not in response.metadata.annotations + + +def test_workflow_add_remove_schedule(client: AtlanClient, workflow: WorkflowResponse): + schedule = WorkflowSchedule( + cron_schedule=WORKFLOW_SCHEDULE_UPDATED_1, + timezone=WORKFLOW_SCHEDULE_TIMEZONE_UPDATED_1, + ) + + # NOTE: This method will overwrite existing workflow run schedule + # Try to update schedule again, with `Workflow` object + scheduled_workflow = client.workflow.add_schedule( + workflow=workflow, workflow_schedule=schedule + ) + + _assert_add_schedule( + workflow, + scheduled_workflow, + WORKFLOW_SCHEDULE_UPDATED_1, + WORKFLOW_SCHEDULE_TIMEZONE_UPDATED_1, + ) + # Make sure scheduled run exists + _assert_scheduled_run(client, workflow) + # Now remove the scheduled run + response = client.workflow.remove_schedule(workflow) + _assert_remove_schedule(response, workflow) + + # Try to update schedule again, with `WorkflowSearchResult` object + existing_workflow = client.workflow.find_by_type( + prefix=WorkflowPackage.SNOWFLAKE_MINER + )[0] + assert ( + existing_workflow + and existing_workflow.source + and existing_workflow.source.metadata + ) + assert workflow and workflow.metadata + assert existing_workflow.source.metadata.name == workflow.metadata.name + + schedule = WorkflowSchedule( + cron_schedule=WORKFLOW_SCHEDULE_UPDATED_2, + timezone=WORKFLOW_SCHEDULE_TIMEZONE_UPDATED_2, + ) + scheduled_workflow = client.workflow.add_schedule( + workflow=existing_workflow, workflow_schedule=schedule + ) + + _assert_add_schedule( + workflow, + scheduled_workflow, + WORKFLOW_SCHEDULE_UPDATED_2, + WORKFLOW_SCHEDULE_TIMEZONE_UPDATED_2, + ) + # Make sure scheduled run exists + _assert_scheduled_run(client, workflow) + # Now remove the scheduled run + response = client.workflow.remove_schedule(workflow) + _assert_remove_schedule(response, workflow) + + # Try to update schedule again, with `WorkflowPackage` object + schedule = WorkflowSchedule( + cron_schedule=WORKFLOW_SCHEDULE_UPDATED_3, + timezone=WORKFLOW_SCHEDULE_TIMEZONE_UPDATED_3, + ) + scheduled_workflow = client.workflow.add_schedule( + workflow=WorkflowPackage.SNOWFLAKE_MINER, workflow_schedule=schedule + ) + + _assert_add_schedule( + workflow, + scheduled_workflow, + WORKFLOW_SCHEDULE_UPDATED_3, + WORKFLOW_SCHEDULE_TIMEZONE_UPDATED_3, + ) + # Make sure scheduled run exists + _assert_scheduled_run(client, workflow) + # Now remove the scheduled run + response = client.workflow.remove_schedule(workflow) + _assert_remove_schedule(response, workflow) + + +def test_credentials(client: AtlanClient, create_credentials: Credential): + credentials = create_credentials + assert credentials + assert credentials.id + reterieved_creds = client.credentials.get(guid=credentials.id) + assert reterieved_creds.auth_type == "atlan_api_key" + assert reterieved_creds.connector_config_name == "atlan-connectors-spark" + assert reterieved_creds.connector == "spark" + assert reterieved_creds.username == "test-username" + assert create_credentials.connector_type == "event" + assert create_credentials.host == "test-host" + assert create_credentials.port == 123 + assert not create_credentials.extras + assert not create_credentials.level + assert not create_credentials.metadata + + +def test_get_all_credentials(client: AtlanClient): + credentials = client.credentials.get_all() + assert credentials, "Expected credentials but found None" + assert credentials.records is not None, "Expected records but found None" + assert len(credentials.records or []) > 0, ( + "Expected at least one record but found none" + ) + + +def test_get_all_credentials_with_filter_limit_offset(client: AtlanClient): + filter_criteria = {"connectorType": "jdbc"} + limit = 1 + offset = 1 + credentials = client.credentials.get_all( + filter=filter_criteria, limit=limit, offset=offset + ) + assert len(credentials.records or []) <= limit, "Exceeded limit in results" + for cred in credentials.records or []: + assert cred.connector_type == "jdbc", ( + f"Expected 'jdbc', got {cred.connector_type}" + ) + + +def test_get_all_credentials_with_multiple_filters(client: AtlanClient): + filter_criteria = {"connectorType": "jdbc", "isActive": True} + + credentials = client.credentials.get_all(filter=filter_criteria) + assert credentials, "Expected credentials but found None" + assert credentials.records is not None, "Expected records but found None" + assert len(credentials.records or []) > 0, ( + "Expected at least one record but found none" + ) + + for record in credentials.records or []: + assert record.connector_type == "jdbc", ( + f"Expected 'jdbc', got {record.connector_type}" + ) + assert record.is_active, f"Expected active record, but got inactive: {record}" + + +def test_get_all_credentials_with_invalid_filter_key(client: AtlanClient): + filter_criteria = {"invalidKey": "someValue"} + try: + client.credentials.get_all(filter=filter_criteria) + pytest.fail("Expected an error due to invalid filter key, but none occurred.") + except Exception as e: + assert "400" in str(e), f"Expected a 400 error, but got: {e}" + + +def test_get_all_credentials_with_invalid_filter_value(client: AtlanClient): + filter_criteria = {"connector_type": 123} + + try: + client.credentials.get_all(filter=filter_criteria) + pytest.fail("Expected an error due to invalid filter value, but none occurred.") + except Exception as e: + assert "400" in str(e), f"Expected a 400 error, but got: {e}" diff --git a/tests_v9/integration/utils.py b/tests_v9/integration/utils.py new file mode 100644 index 000000000..bd1973691 --- /dev/null +++ b/tests_v9/integration/utils.py @@ -0,0 +1,127 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. +"""Shared integration test utilities for v9.""" + +from typing import List, Optional + +from tenacity import ( + retry, + retry_if_exception_type, + retry_if_result, + stop_after_attempt, + wait_exponential, + wait_random_exponential, +) + +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.errors import AtlanError, NotFoundError +from pyatlan_v9.model.assets import Persona, Purpose +from pyatlan_v9.model.fluent_search import FluentSearch +from pyatlan_v9.model.search import IndexSearchRequest +from pyatlan_v9.model.typedef import AtlanTagDef, CustomMetadataDef, EnumDef + + +@retry( + retry=retry_if_exception_type(AtlanError), + wait=wait_random_exponential(multiplier=1, max=5), + stop=stop_after_attempt(3), +) +def wait_for_successful_tagdef_purge(name: str, client: AtlanClient): + client.typedef.purge(name, typedef_type=AtlanTagDef) + + +@retry( + retry=retry_if_exception_type(AtlanError), + wait=wait_random_exponential(multiplier=1, max=5), + stop=stop_after_attempt(3), +) +def wait_for_successful_custometadatadef_purge(name: str, client: AtlanClient): + client.typedef.purge(name, typedef_type=CustomMetadataDef) + + +@retry( + retry=retry_if_exception_type(AtlanError), + wait=wait_random_exponential(multiplier=1, max=5), + stop=stop_after_attempt(3), +) +def wait_for_successful_enumadef_purge(name: str, client: AtlanClient): + client.typedef.purge(name, typedef_type=EnumDef) + + +def find_personas_by_name_with_retry( + client: AtlanClient, name: str, attributes: Optional[List[str]] = None +) -> List[Persona]: + @retry( + reraise=True, + retry=retry_if_exception_type(NotFoundError), + stop=stop_after_attempt(10), + wait=wait_exponential(multiplier=1, min=2, max=10), + ) + def _retry_find_personas(): + return client.asset.find_personas_by_name(name=name, attributes=attributes) + + return _retry_find_personas() + + +def find_purposes_by_name_with_retry( + client: AtlanClient, name: str, attributes: Optional[List[str]] = None +) -> List[Purpose]: + @retry( + reraise=True, + retry=retry_if_exception_type(NotFoundError), + stop=stop_after_attempt(10), + wait=wait_exponential(multiplier=1, min=2, max=10), + ) + def _retry_find_purposes(): + return client.asset.find_purposes_by_name(name=name, attributes=attributes) + + return _retry_find_purposes() + + +def fluent_search_count_with_retry( + fluent_search: FluentSearch, client: AtlanClient, expected_count: int +) -> int: + @retry( + reraise=True, + retry=retry_if_result(lambda count: count < expected_count), + stop=stop_after_attempt(10), + wait=wait_exponential(multiplier=1, min=2, max=10), + ) + def _retry_count(): + return fluent_search.count(client) + + return _retry_count() + + +def search_request_count_with_retry( + client: AtlanClient, request: IndexSearchRequest, expected_count: int +) -> int: + @retry( + reraise=True, + retry=retry_if_result(lambda count: count < expected_count), + stop=stop_after_attempt(10), + wait=wait_exponential(multiplier=1, min=2, max=10), + ) + def _retry_search(): + response = client.asset.search(request) + return response.count + + return _retry_search() + + +def assert_search_count_with_retry( + client: AtlanClient, request: IndexSearchRequest, expected_count: int +) -> None: + actual_count = search_request_count_with_retry(client, request, expected_count) + assert actual_count == expected_count, ( + f"Expected {expected_count} results, got {actual_count}" + ) + + +def assert_fluent_search_count_with_retry( + fluent_search: FluentSearch, client: AtlanClient, expected_count: int +) -> None: + actual_count = fluent_search_count_with_retry(fluent_search, client, expected_count) + assert actual_count == expected_count, ( + f"Expected {expected_count} results, got {actual_count}" + ) diff --git a/tests_v9/unit/__init__.py b/tests_v9/unit/__init__.py new file mode 100644 index 000000000..de398f096 --- /dev/null +++ b/tests_v9/unit/__init__.py @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. diff --git a/tests_v9/unit/aio/__init__.py b/tests_v9/unit/aio/__init__.py new file mode 100644 index 000000000..7eda0b906 --- /dev/null +++ b/tests_v9/unit/aio/__init__.py @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. +""" +Async (aio) unit tests for PyAtlan V9 SDK. + +This package contains async unit tests for v9 msgspec-based clients, +testing AsyncAtlanClient, V9AsyncGroupClient, and async operations +with v9 models. +""" diff --git a/tests_v9/unit/aio/conftest.py b/tests_v9/unit/aio/conftest.py new file mode 100644 index 000000000..121a6b7fe --- /dev/null +++ b/tests_v9/unit/aio/conftest.py @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. +""" +Async-specific test configuration and fixtures for v9. +""" + +from unittest.mock import Mock, patch + +import pytest +import pytest_asyncio + +from pyatlan.client.common import AsyncApiCaller +from pyatlan_v9.client.aio.atlan import AsyncAtlanClient + + +@pytest.fixture(autouse=True) +def set_env(monkeypatch): + """Set up environment variables for async tests.""" + monkeypatch.setenv("ATLAN_BASE_URL", "https://test.atlan.com") + monkeypatch.setenv("ATLAN_API_KEY", "test-api-key") + + +@pytest_asyncio.fixture +async def async_client(): + """Create an async client for testing.""" + async with AsyncAtlanClient() as client: + yield client + + +@pytest.fixture() +def mock_async_client(): + """Create a mock async client for testing.""" + return AsyncAtlanClient() + + +@pytest.fixture(scope="function") +def mock_async_api_caller(): + return Mock(spec=AsyncApiCaller) + + +@pytest.fixture() +def mock_async_custom_metadata_cache(): + with patch.object(AsyncAtlanClient, "custom_metadata_cache") as cache: + yield cache diff --git a/tests_v9/unit/aio/test_atlan_tag_name.py b/tests_v9/unit/aio/test_atlan_tag_name.py new file mode 100644 index 000000000..ace70c321 --- /dev/null +++ b/tests_v9/unit/aio/test_atlan_tag_name.py @@ -0,0 +1,266 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +import msgspec +import pytest + +import pyatlan.cache.aio.atlan_tag_cache +from pyatlan.model.constants import DELETED_ +from pyatlan_v9.client.aio.atlan import AsyncAtlanClient +from pyatlan_v9.model.aio.core import AsyncAtlanRequest, AsyncAtlanResponse +from pyatlan_v9.model.assets import Purpose +from pyatlan_v9.model.assets.purpose import PurposeNested, _purpose_from_nested +from pyatlan_v9.model.core import AtlanTagName + +ATLAN_TAG_ID = "yiB7RLvdC2yeryLPjaDeHM" + +GOOD_ATLAN_TAG_NAME = "PII" + + +@pytest.fixture(autouse=True) +def set_env(monkeypatch): + monkeypatch.setenv("ATLAN_BASE_URL", "https://test.atlan.com") + monkeypatch.setenv("ATLAN_API_KEY", "test-api-key") + + +@pytest.fixture() +def client(): + return AsyncAtlanClient() + + +@pytest.fixture() +def good_atlan_tag(monkeypatch): + return AtlanTagName(GOOD_ATLAN_TAG_NAME) + + +def test_init_with_good_name(): + """Test that AtlanTagName initialization works the same in async context""" + sut = AtlanTagName(GOOD_ATLAN_TAG_NAME) + assert sut._display_text == GOOD_ATLAN_TAG_NAME + assert str(sut) == GOOD_ATLAN_TAG_NAME + assert sut.__repr__() == f"AtlanTagName('{GOOD_ATLAN_TAG_NAME}')" + assert sut.__hash__() == GOOD_ATLAN_TAG_NAME.__hash__() + assert AtlanTagName(GOOD_ATLAN_TAG_NAME) == sut + + +def test_convert_to_display_text_when_atlan_tag_passed_returns_same_atlan_tag( + good_atlan_tag, +): + """Test that conversion works the same in async context""" + assert good_atlan_tag is AtlanTagName._convert_to_tag_name(good_atlan_tag) + + +def test_convert_to_display_text_when_bad_string(): + """Test that bad string conversion works the same in async context""" + assert AtlanTagName._convert_to_tag_name("bad").__repr__() == "AtlanTagName('bad')" + + +def test_convert_to_tag_name(): + """Test that tag name conversion works the same in async context""" + sut = AtlanTagName._convert_to_tag_name(ATLAN_TAG_ID) + assert str(sut) == ATLAN_TAG_ID + + +def test_get_deleted_sentinel(): + """Test that deleted sentinel works the same in async context""" + sentinel = AtlanTagName.get_deleted_sentinel() + + assert "(DELETED)" == str(sentinel) + assert id(sentinel) == id(AtlanTagName.get_deleted_sentinel()) + + +def _assert_asset_tags(asset, is_retranslated=False): + """Helper function to validate asset tags - same as sync version""" + assert asset and isinstance(asset, Purpose) + tags = asset.classifications + assert tags and len(tags) == 5 + assert str(tags[0].type_name) == DELETED_ + assert str(tags[1].type_name) == DELETED_ + assert str(tags[2].type_name) == DELETED_ + if not is_retranslated: + assert ( + tags[2].source_tag_attachments and len(tags[2].source_tag_attachments) == 1 + ) + assert str(tags[3].type_name) == DELETED_ + if not is_retranslated: + assert tags[3].source_tag_attachments == [] + assert str(tags[4].type_name) == DELETED_ + assert asset.purpose_atlan_tags and len(asset.purpose_atlan_tags) == 2 + assert asset.purpose_atlan_tags[0].__repr__() == f"AtlanTagName('{DELETED_}')" + assert asset.purpose_atlan_tags[1].__repr__() == f"AtlanTagName('{DELETED_}')" + + +@pytest.mark.asyncio +async def test_asset_tag_name_field_serde_with_translation_async( + client: AsyncAtlanClient, monkeypatch +): + """Test async version of asset tag name field serialization/deserialization with translation""" + + # Mock async methods + async def get_name_for_id(_, __): + return None + + async def get_id_for_name(_, __): + return None + + async def get_source_tags_attr_id(_, tag_id): + # Return different values based on tag_id to test different scenarios + source_tag_ids = { + "source-tag-with-attributes": "ZLVyaOlGWDrkLFZgmZCjLa", # source tag with attributes + "source-tag-without-attributes": "BLVyaOlGWDrkLFZgmZCjLa", + "deleted-source-tag": None, # deleted source tag with attributes + } + return source_tag_ids.get(tag_id, None) # Return None for non-source tags + + # Patch async cache methods + monkeypatch.setattr( + pyatlan.cache.aio.atlan_tag_cache.AsyncAtlanTagCache, + "get_id_for_name", + get_id_for_name, + ) + + monkeypatch.setattr( + pyatlan.cache.aio.atlan_tag_cache.AsyncAtlanTagCache, + "get_name_for_id", + get_name_for_id, + ) + + monkeypatch.setattr( + pyatlan.cache.aio.atlan_tag_cache.AsyncAtlanTagCache, + "get_source_tags_attr_id", + get_source_tags_attr_id, + ) + + # Same raw JSON structure as sync test + raw_json = { + "typeName": "Purpose", + "attributes": { + # AtlanTagName + "purposeClassifications": [ + "some-deleted-purpose-tag-1", + "some-deleted-purpose-tag-2", + ], + }, + "guid": "9f7a35f4-8d37-4273-81ec-c497a83a2472", + "status": "ACTIVE", + "classifications": [ + # AtlanTag + { + "typeName": "some-deleted-purpose-tag-1", + "entityGuid": "82683fb9-1501-4627-a5d0-0da9be64c0d5", + "entityStatus": "DELETED", + "propagate": False, + "removePropagationsOnEntityDelete": True, + "restrictPropagationThroughLineage": True, + "restrictPropagationThroughHierarchy": False, + }, + { + "typeName": "some-deleted-purpose-tag-2", + "entityGuid": "82683fb9-1501-4627-a5d0-0da9be64c0d5", + "entityStatus": "DELETED", + "propagate": False, + "removePropagationsOnEntityDelete": True, + "restrictPropagationThroughLineage": True, + "restrictPropagationThroughHierarchy": False, + }, + # Source tags with attributes + { + "typeName": "source-tag-with-attributes", + "attributes": { + "ZLVyaOlGWDrkLFZgmZCjLa": [ + { + "typeName": "SourceTagAttachment", + "attributes": { + "sourceTagName": "CONFIDENTIAL", + "sourceTagQualifiedName": "default/snowflake/1747816988/ANALYTICS/WIDE_WORLD_IMPORTERS/CONFIDENTIAL", + "sourceTagGuid": "2a9dab90-1b86-432d-a28a-9f3d9b61192b", + "sourceTagConnectorName": "snowflake", + "sourceTagValue": [ + {"tagAttachmentValue": "Not Restricted"} + ], + }, + } + ] + }, + "entityGuid": "46be9b92-170b-4c74-bf28-f9dc99021a2a", + "entityStatus": "ACTIVE", + "propagate": True, + "removePropagationsOnEntityDelete": True, + "restrictPropagationThroughLineage": False, + "restrictPropagationThroughHierarchy": False, + }, + # Source tags (without attributes) + { + "typeName": "source-tag-without-attributes", + "entityGuid": "46be9b92-170b-4c74-bf28-f9dc99021a2a", + "entityStatus": "ACTIVE", + "propagate": True, + "removePropagationsOnEntityDelete": True, + "restrictPropagationThroughLineage": False, + "restrictPropagationThroughHierarchy": False, + }, + # Deleted source tags (with attributes) + { + "typeName": "deleted-source-tag", + "attributes": { + "XzEYmFzETBrS7nuxeImNie": [ + { + "typeName": "SourceTagAttachment", + "attributes": { + "sourceTagName": "CONFIDENTIAL", + "sourceTagQualifiedName": "default/snowflake/1747816988/ANALYTICS/WIDE_WORLD_IMPORTERS/CONFIDENTIAL", + "sourceTagGuid": "2a9dab90-1b86-432d-a28a-9f3d9b61192b", + "sourceTagConnectorName": "snowflake", + "sourceTagValue": [ + {"tagAttachmentValue": "Not Restricted"} + ], + }, + } + ] + }, + "entityGuid": "46be9b92-170b-4c74-bf28-f9dc99021a2a", + "entityStatus": "DELETED", + "propagate": True, + "removePropagationsOnEntityDelete": True, + "restrictPropagationThroughLineage": False, + "restrictPropagationThroughHierarchy": False, + }, + ], + } + + def _nested_to_purpose(d: dict) -> Purpose: + """Convert a nested API-format dict to a v9 Purpose.""" + nested = msgspec.convert(d, PurposeNested) + return _purpose_from_nested(nested) + + async_response = AsyncAtlanResponse(raw_json=raw_json, client=client) + translated_dict = await async_response.translate() + purpose_with_translation = _nested_to_purpose(translated_dict) + purpose_without_translation = _nested_to_purpose(raw_json) + + async_request_with_translation = AsyncAtlanRequest( + instance=purpose_with_translation, client=client + ) + retranslated_with_translated_dict = ( + await async_request_with_translation.retranslate() + ) + + async_request_without_translation = AsyncAtlanRequest( + instance=purpose_without_translation, client=client + ) + retranslated_without_translated_dict = ( + await async_request_without_translation.retranslate() + ) + + purpose_with_translation_and_retranslation = _nested_to_purpose( + retranslated_with_translated_dict + ) + purpose_without_translation_and_retranslation = _nested_to_purpose( + retranslated_without_translated_dict + ) + + _assert_asset_tags(purpose_with_translation) + _assert_asset_tags(purpose_with_translation_and_retranslation, is_retranslated=True) + _assert_asset_tags( + purpose_without_translation_and_retranslation, is_retranslated=True + ) diff --git a/tests_v9/unit/aio/test_audit_search.py b/tests_v9/unit/aio/test_audit_search.py new file mode 100644 index 000000000..411587a53 --- /dev/null +++ b/tests_v9/unit/aio/test_audit_search.py @@ -0,0 +1,186 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. +from datetime import datetime, timezone +from json import load +from pathlib import Path +from unittest.mock import AsyncMock, Mock, patch + +import pytest + +from pyatlan.client.common import AsyncApiCaller +from pyatlan.client.common.audit import LOGGER +from pyatlan_v9.client.aio.audit import V9AsyncAuditClient as AsyncAuditClient +from pyatlan_v9.errors import InvalidRequestError +from pyatlan_v9.model.aio.audit import AsyncAuditSearchResults +from pyatlan_v9.model.audit import AuditSearchRequest +from pyatlan_v9.model.enums import SortOrder +from pyatlan_v9.model.search import DSL, Bool, SortItem, Term + +SEARCH_RESPONSES_DIR = ( + Path(__file__).parent.parent.parent.parent + / "tests" + / "unit" + / "data" + / "search_responses" +) +AUDIT_SEARCH_PAGING_JSON = "audit_search_paging.json" + + +@pytest.fixture(autouse=True) +def set_env(monkeypatch): + monkeypatch.setenv("ATLAN_BASE_URL", "https://name.atlan.com") + monkeypatch.setenv("ATLAN_API_KEY", "abkj") + + +@pytest.fixture(scope="function") +def mock_async_api_caller(): + mock_caller = Mock(spec=AsyncApiCaller) + mock_caller._call_api = AsyncMock() + mock_caller._async_session = Mock() + return mock_caller + + +@pytest.fixture() +def audit_search_paging_json(): + def load_json(filename): + with (SEARCH_RESPONSES_DIR / filename).open() as input_file: + return load(input_file) + + return load_json(AUDIT_SEARCH_PAGING_JSON) + + +async def _assert_audit_search_results( + results: AsyncAuditSearchResults, response_json, sorts, bulk=False +): + first = response_json["entityAudits"][0] + async for audit in results: + assert audit.entity_id == first["entityId"] + assert audit.entity_qualified_name == first["entityQualifiedName"] + assert audit.type_name == first["typeName"] + expected_timestamp = datetime.fromtimestamp( + first["timestamp"] / 1000, tz=timezone.utc + ) + assert audit.timestamp == expected_timestamp + expected_created = datetime.fromtimestamp( + first["created"] / 1000, tz=timezone.utc + ) + assert audit.created == expected_created + assert audit.user == first["user"] + assert audit.action == first["action"] + + assert results.total_count == response_json["totalCount"] + assert results._bulk == bulk + assert results._criteria.dsl.sort == sorts + + +@pytest.mark.asyncio +@patch.object(LOGGER, "debug") +async def test_audit_search_pagination( + mock_logger, mock_async_api_caller, audit_search_paging_json +): + client = AsyncAuditClient(mock_async_api_caller) + mock_async_api_caller._call_api.side_effect = [ + audit_search_paging_json, + audit_search_paging_json, + {}, + ] + + # Test default pagination + dsl = DSL( + query=Bool(filter=[Term(field="entityId", value="some-guid")]), + sort=[], + size=1, + from_=0, + ) + audit_search_request = AuditSearchRequest(dsl=dsl) + response = await client.search(criteria=audit_search_request, bulk=False) + + assert response and response.aggregations + assert audit_search_paging_json["aggregations"] == response.aggregations + expected_sorts = [SortItem(field="entityId", order=SortOrder.ASCENDING)] + + await _assert_audit_search_results( + response, audit_search_paging_json, expected_sorts + ) + assert mock_async_api_caller._call_api.call_count == 3 + assert mock_logger.call_count == 0 + mock_async_api_caller.reset_mock() + + # Test bulk pagination + mock_async_api_caller._call_api.side_effect = [ + audit_search_paging_json, + audit_search_paging_json, + {}, + ] + audit_search_request = AuditSearchRequest(dsl=dsl) + response = await client.search(criteria=audit_search_request, bulk=True) + expected_sorts = [ + SortItem(field="created", order=SortOrder.ASCENDING), + SortItem(field="entityId", order=SortOrder.ASCENDING), + ] + + await _assert_audit_search_results( + response, audit_search_paging_json, expected_sorts, bulk=True + ) + # The call count will be 2 because + # audit search entries are processed in the first API call. + # In the second API call, self._entity_audits + # becomes 0, which breaks the pagination. + # This differs from offset-based pagination + # where an additional API call is needed + # to verify if the results are empty + assert mock_async_api_caller._call_api.call_count == 2 + assert mock_logger.call_count == 1 + assert "Audit bulk search option is enabled." in mock_logger.call_args_list[0][0][0] + mock_logger.reset_mock() + mock_async_api_caller.reset_mock() + + # Test automatic bulk search conversion when exceeding threshold + with patch.object(AsyncAuditSearchResults, "_MASS_EXTRACT_THRESHOLD", -1): + mock_async_api_caller._call_api.side_effect = [ + # Extra call to re-fetch the first page + # results with updated timestamp sorting + audit_search_paging_json, + audit_search_paging_json, + audit_search_paging_json, + {}, + ] + audit_search_request = AuditSearchRequest(dsl=dsl) + response = await client.search(criteria=audit_search_request) + await _assert_audit_search_results( + response, audit_search_paging_json, expected_sorts, bulk=False + ) + assert mock_logger.call_count == 1 + assert mock_async_api_caller._call_api.call_count == 3 + assert ( + "Result size (%s) exceeds threshold (%s)" + in mock_logger.call_args_list[0][0][0] + ) + + # Test exception for bulk=False with user-defined sorting and results exceeds the predefined threshold + dsl.sort = dsl.sort + [SortItem(field="some-sort1", order=SortOrder.ASCENDING)] + audit_search_request = AuditSearchRequest(dsl=dsl) + with pytest.raises( + InvalidRequestError, + match=( + "ATLAN-PYTHON-400-066 Unable to execute " + "audit bulk search with user-defined sorting options. " + "Suggestion: Please ensure that no sorting options are " + "included in your audit search request when performing a bulk search." + ), + ): + await client.search(criteria=audit_search_request, bulk=False) + + # Test exception for bulk=True with user-defined sorting + dsl.sort = dsl.sort + [SortItem(field="some-sort2", order=SortOrder.ASCENDING)] + audit_search_request = AuditSearchRequest(dsl=dsl) + with pytest.raises( + InvalidRequestError, + match=( + "ATLAN-PYTHON-400-066 Unable to execute " + "audit bulk search with user-defined sorting options. " + "Suggestion: Please ensure that no sorting options are " + "included in your audit search request when performing a bulk search." + ), + ): + await client.search(criteria=audit_search_request, bulk=True) diff --git a/tests_v9/unit/aio/test_client.py b/tests_v9/unit/aio/test_client.py new file mode 100644 index 000000000..7f9f82eac --- /dev/null +++ b/tests_v9/unit/aio/test_client.py @@ -0,0 +1,2954 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. +from importlib.resources import read_text +from json import load, loads +from pathlib import Path +from re import escape +from unittest.mock import AsyncMock, Mock, call, patch + +import msgspec +import pytest +from httpx import Headers + +from pyatlan.client.common import Search +from pyatlan.client.common.asset import LOGGER as SHARED_LOGGER +from pyatlan.model.aio.asset import AsyncIndexSearchResults +from pyatlan.utils import get_python_version +from pyatlan_v9.client.aio.asset import V9AsyncAssetClient as AsyncAssetClient +from pyatlan_v9.client.aio.atlan import AsyncAtlanClient +from pyatlan_v9.client.aio.batch import AsyncBatch +from pyatlan_v9.client.aio.group import V9AsyncGroupClient as AsyncGroupClient +from pyatlan_v9.client.aio.search_log import ( + V9AsyncSearchLogClient as AsyncSearchLogClient, +) +from pyatlan_v9.client.aio.typedef import V9AsyncTypeDefClient as AsyncTypeDefClient +from pyatlan_v9.client.aio.user import V9AsyncUserClient as AsyncUserClient +from pyatlan_v9.client.asset import CustomMetadataHandling +from pyatlan_v9.errors import ( + ERROR_CODE_FOR_HTTP_STATUS, + ApiError, + AtlanError, + ErrorCode, + InvalidRequestError, + NotFoundError, +) +from pyatlan_v9.model.assets import ( + Asset, + AtlasGlossary, + AtlasGlossaryCategory, + AtlasGlossaryTerm, + Column, + DataDomain, + DataProduct, + Table, + View, +) +from pyatlan_v9.model.core import Announcement, BulkRequest +from pyatlan_v9.model.enums import ( + AnnouncementType, + AtlanConnectorType, + CertificateStatus, + LineageDirection, + SortOrder, +) +from pyatlan_v9.model.fluent_search import CompoundQuery, FluentSearch +from pyatlan_v9.model.group import GroupRequest +from pyatlan_v9.model.lineage import LineageListRequest +from pyatlan_v9.model.response import AssetMutationResponse +from pyatlan_v9.model.search import DSL, Bool, IndexSearchRequest, Term, TermAttributes +from pyatlan_v9.model.search_log import SearchLogRequest +from pyatlan_v9.model.typedef import EnumDef +from pyatlan_v9.model.user import AtlanUser, UserRequest +from tests.unit.model.constants import ( + CONNECTION_NAME, + CONNECTOR_TYPE, + DATA_DOMAIN_NAME, + DATA_PRODUCT_NAME, + GLOSSARY_CATEGORY_NAME, + GLOSSARY_NAME, + GLOSSARY_QUALIFIED_NAME, + GLOSSARY_TERM_NAME, + PERSONA_NAME, + PURPOSE_NAME, +) +from tests_v9.unit.constants import ( + TEST_ADMIN_CLIENT_METHODS, + TEST_ASSET_CLIENT_METHODS_ASYNC, + TEST_AUDIT_CLIENT_METHODS, + TEST_GROUP_CLIENT_METHODS, + TEST_ROLE_CLIENT_METHODS, + TEST_SL_CLIENT_METHODS, + TEST_TOKEN_CLIENT_METHODS, + TEST_TYPEDEF_CLIENT_METHODS, + TEST_USER_CLIENT_METHODS, +) + +V9_TEST_ASSET_CLIENT_METHODS_ASYNC = { + **TEST_ASSET_CLIENT_METHODS_ASYNC, + "update_custom_metadata_attributes": [ + ([[123], ["cm"]], "guid\n str type expected"), + ([None, ["cm"]], "none is not an allowed value"), + ( + ["name", 123], + "custom_metadata\n instance of CustomMetadataDict expected", + ), + (["name", None], "none is not an allowed value"), + ], + "replace_custom_metadata": [ + ([[123], ["cm"]], "guid\n str type expected"), + ([None, ["cm"]], "none is not an allowed value"), + ( + ["name", 123], + "custom_metadata\n instance of CustomMetadataDict expected", + ), + (["name", None], "none is not an allowed value"), + ], + "find_domain_by_name": [ + ( + [None, ["attributes"]], + "1 validation error for FindDomainByName\nname\n none is not an allowed value", + ), + ( + [" ", ["attributes"]], + "1 validation error for WithName\nvalue\n ensure this value has at least 1 characters", + ), + ( + ["test-domain", "attributes"], + "1 validation error for FindDomainByName\nattributes\n value is not a valid list", + ), + ], + "find_product_by_name": [ + ( + [None, ["attributes"]], + "1 validation error for FindProductByName\nname\n none is not an allowed value", + ), + ( + [" ", ["attributes"]], + "1 validation error for WithName\nvalue\n ensure this value has at least 1 characters", + ), + ( + ["test-product", "attributes"], + "1 validation error for FindProductByName\nattributes\n value is not a valid list", + ), + ], +} + + +def _rename_create_update(d): + """Rename 'create' -> 'creator' and 'update' -> 'updater' keys for v9 sub-clients.""" + out = {} + for k, v in d.items(): + if k == "create": + out["creator"] = v + elif k == "update": + out["updater"] = v + else: + out[k] = v + return out + + +V9_TEST_GROUP_CLIENT_METHODS = _rename_create_update(TEST_GROUP_CLIENT_METHODS) +V9_TEST_TOKEN_CLIENT_METHODS = _rename_create_update(TEST_TOKEN_CLIENT_METHODS) +V9_TEST_TYPEDEF_CLIENT_METHODS = _rename_create_update(TEST_TYPEDEF_CLIENT_METHODS) +V9_TEST_USER_CLIENT_METHODS = _rename_create_update(TEST_USER_CLIENT_METHODS) + +GLOSSARY = AtlasGlossary.create(name=GLOSSARY_NAME) +GLOSSARY_CATEGORY = AtlasGlossaryCategory.create( + name=GLOSSARY_CATEGORY_NAME, anchor=GLOSSARY +) +GLOSSARY_TERM = AtlasGlossaryTerm.create(name=GLOSSARY_TERM_NAME, anchor=GLOSSARY) +UNIQUE_USERS = "uniqueUsers" +UNIQUE_ASSETS = "uniqueAssets" +LOG_IP_ADDRESS = "ipAddress" +LOG_USERNAME = "userName" +SEARCH_PARAMS = "searchParameters" +SEARCH_COUNT = "approximateCount" +TEST_DATA_DIR = Path(__file__).parent.parent.parent.parent / "tests" / "unit" / "data" +SEARCH_LOG_RESPONSES_DIR = TEST_DATA_DIR / "search_log_responses" +SL_MOST_RECENT_VIEWERS_JSON = "sl_most_recent_viewers.json" +SL_MOST_VIEWED_ASSETS_JSON = "sl_most_viewed_assets.json" +SL_DETAILED_LOG_ENTRIES_JSON = "sl_detailed_log_entries.json" +CM_NAME = "testcm1.testcm2" +LINEAGE_LIST_JSON = "lineage_list.json" +LINEAGE_RESPONSES_DIR = TEST_DATA_DIR / "lineage_responses" +GROUP_LIST_JSON = "group_list.json" +GROUP_MEMBERS_JSON = "group_members.json" +GROUP_RESPONSES_DIR = TEST_DATA_DIR / "group_responses" +USER_LIST_JSON = "user_list.json" +USER_GROUPS_JSON = "user_groups.json" +USER_RESPONSES_DIR = TEST_DATA_DIR / "user_responses" +AGGREGATIONS_NULL_RESPONSES_DIR = "aggregations_null_value.json" +INDEX_SEARCH_PAGING_JSON = "index_search_paging.json" +GLOSSARY_CATEGORY_BY_NAME_JSON = "glossary_category_by_name.json" +SEARCH_RESPONSES_DIR = TEST_DATA_DIR / "search_responses" +USER_LIST_JSON = "user_list.json" +GET_BY_GUID_JSON = "get_by_guid.json" +RETRIEVE_MINIMAL_JSON = "retrieve_minimal.json" +ASSET_RESPONSES_DIR = TEST_DATA_DIR / "asset_responses" +TYPEDEF_GET_BY_NAME_JSON = "get_by_name.json" +TYPEDEF_RESPONSES_DIR = TEST_DATA_DIR / "typedef_responses" + +TEST_ANNOUNCEMENT = Announcement( + announcement_title="test-title", + announcement_message="test-msg", + announcement_type=AnnouncementType.INFORMATION, +) +TEST_MISSING_GLOSSARY_GUID_ERROR = "ATLAN-PYTHON-400-055 'glossary_guid' keyword argument is missing for asset type: {0}" + + +@pytest.fixture(autouse=True) +def set_env(monkeypatch): + monkeypatch.setenv("ATLAN_BASE_URL", "https://test.atlan.com") + monkeypatch.setenv("ATLAN_API_KEY", "test-api-key") + + +@pytest.fixture() +def client(): + return AsyncAtlanClient() + + +@pytest.fixture +def async_group_client(mock_async_api_caller): + return AsyncGroupClient(client=mock_async_api_caller) + + +@pytest.fixture +def mock_async_atlan_client(): + return Mock(AsyncAtlanClient) + + +def load_json(respones_dir, filename): + with (respones_dir / filename).open() as input_file: + return load(input_file) + + +@pytest.fixture() +def sl_most_recent_viewers_json(): + return load_json(SEARCH_LOG_RESPONSES_DIR, SL_MOST_RECENT_VIEWERS_JSON) + + +@pytest.fixture() +def sl_most_viewed_assets_json(): + return load_json(SEARCH_LOG_RESPONSES_DIR, SL_MOST_VIEWED_ASSETS_JSON) + + +@pytest.fixture() +def sl_detailed_log_entries_json(): + return load_json(SEARCH_LOG_RESPONSES_DIR, SL_DETAILED_LOG_ENTRIES_JSON) + + +@pytest.fixture() +def lineage_list_json(): + return load_json(LINEAGE_RESPONSES_DIR, LINEAGE_LIST_JSON) + + +@pytest.fixture() +def group_list_json(): + return load_json(GROUP_RESPONSES_DIR, GROUP_LIST_JSON) + + +@pytest.fixture() +def group_members_json(): + return load_json(GROUP_RESPONSES_DIR, GROUP_MEMBERS_JSON) + + +@pytest.fixture() +def user_list_json(): + return load_json(USER_RESPONSES_DIR, USER_LIST_JSON) + + +@pytest.fixture() +def user_groups_json(): + return load_json(USER_RESPONSES_DIR, USER_GROUPS_JSON) + + +@pytest.fixture() +def aggregations_null_json(): + return load_json(SEARCH_RESPONSES_DIR, AGGREGATIONS_NULL_RESPONSES_DIR) + + +@pytest.fixture() +def index_search_paging_json(): + return load_json(SEARCH_RESPONSES_DIR, INDEX_SEARCH_PAGING_JSON) + + +@pytest.fixture() +def get_by_guid_json(): + return load_json(ASSET_RESPONSES_DIR, GET_BY_GUID_JSON) + + +@pytest.fixture() +def retrieve_minimal_json(): + return load_json(ASSET_RESPONSES_DIR, RETRIEVE_MINIMAL_JSON) + + +@pytest.fixture() +def type_def_get_by_name_json(): + return load_json(TYPEDEF_RESPONSES_DIR, TYPEDEF_GET_BY_NAME_JSON) + + +@pytest.fixture() +def glossary_category_by_name_json(): + return load_json(SEARCH_RESPONSES_DIR, GLOSSARY_CATEGORY_BY_NAME_JSON) + + +@pytest.mark.parametrize( + "guid, qualified_name, asset_type, assigned_terms, expected_message, expected_error", + [ + ( + None, + None, + Table, + [AtlasGlossaryTerm()], + "ATLAN-PYTHON-400-043 Either qualified_name or guid should be provided.", + InvalidRequestError, + ), + ( + "123", + "default/abc", + Table, + [AtlasGlossaryTerm()], + "ATLAN-PYTHON-400-042 Only qualified_name or guid should be provided but not both.", + InvalidRequestError, + ), + ], +) +@pytest.mark.asyncio +async def test_append_terms_invalid_parameters_raises_error( + guid, qualified_name, asset_type, assigned_terms, expected_message, expected_error +): + client = AsyncAtlanClient() + with pytest.raises(expected_error, match=expected_message): + await client.asset.append_terms( + asset_type=asset_type, + terms=assigned_terms, + guid=guid, + qualified_name=qualified_name, + ) + + +@pytest.mark.parametrize( + "guid, qualified_name, asset_type, assigned_terms, mock_results, expected_message, expected_error", + [ + ( + None, + "nonexistent_qualified_name", + Table, + [AtlasGlossaryTerm()], + [], + "ATLAN-PYTHON-404-003 Asset with qualifiedName nonexistent_qualified_name of type Table does not exist." + " Suggestion: Verify the qualifiedName and expected type of the asset you are trying to retrieve.", + NotFoundError, + ), + ( + "nonexistent_guid", + None, + Table, + [AtlasGlossaryTerm()], + [], + "ATLAN-PYTHON-404-001 Asset with GUID nonexistent_guid does not exist." + " Suggestion: Verify the GUID of the asset you are trying to retrieve.", + NotFoundError, + ), + ( + None, + "default/abc", + Table, + [AtlasGlossaryTerm()], + ["DifferentTypeAsset"], + "ATLAN-PYTHON-404-014 The Table asset could not be found by name: default/abc." + " Suggestion: Verify the requested asset type and name exist in your Atlan environment.", + NotFoundError, + ), + ( + "123", + None, + Table, + [AtlasGlossaryTerm()], + ["DifferentTypeAsset"], + "ATLAN-PYTHON-404-002 Asset with GUID 123 is not of the type requested: Table." + " Suggestion: Verify the GUID and expected type of the asset you are trying to retrieve.", + NotFoundError, + ), + ], +) +@patch( + "pyatlan_v9.model.fluent_search.FluentSearch.execute_async", new_callable=AsyncMock +) +@pytest.mark.asyncio +async def test_append_terms_asset_retrieval_errors( + mock_aexecute, + guid, + qualified_name, + asset_type, + assigned_terms, + mock_results, + expected_message, + expected_error, +): + mock_aexecute.return_value.current_page = lambda: mock_results + client = AsyncAtlanClient() + with pytest.raises(expected_error, match=expected_message): + await client.asset.append_terms( + asset_type=asset_type, + terms=assigned_terms, + guid=guid, + qualified_name=qualified_name, + ) + + +@pytest.mark.asyncio +async def test_append_with_valid_guid_and_no_terms_returns_asset(): + asset_type = Table + table = Table() + table.name = "table-test" + table.qualified_name = "table_qn" + + terms = [] + + with patch( + "pyatlan_v9.model.fluent_search.FluentSearch.execute_async", + new_callable=AsyncMock, + ) as mock_aexecute: + with patch( + "pyatlan_v9.client.aio.asset.V9AsyncAssetClient.save", + new_callable=AsyncMock, + ) as mock_save: + # Set up async mock for search results + mock_results = AsyncMock() + mock_results.current_page = Mock( + return_value=[table] + ) # current_page is sync + mock_aexecute.return_value = mock_results + + # Set up async mock for save response + mock_save_response = Mock() + mock_save_response.assets_updated = Mock(return_value=[table]) + mock_save.return_value = mock_save_response + + client = AsyncAtlanClient() + guid = "123" + + asset = await client.asset.append_terms( + guid=guid, asset_type=asset_type, terms=terms + ) + + assert asset == table + assert asset.assigned_terms is None + mock_aexecute.assert_called_once() + mock_save.assert_called_once() + + +@pytest.mark.asyncio +async def test_append_with_valid_guid_when_no_terms_present_returns_asset_with_given_terms(): + asset_type = Table + table = Table() + table.name = "table-test" + table.qualified_name = "table_qn" + + terms = [AtlasGlossaryTerm(qualified_name="term1")] + + with patch( + "pyatlan_v9.model.fluent_search.FluentSearch.execute_async", + new_callable=AsyncMock, + ) as mock_aexecute: + with patch( + "pyatlan_v9.client.aio.asset.V9AsyncAssetClient.save", + new_callable=AsyncMock, + ) as mock_save: + # Set up async mock for search results + mock_results = AsyncMock() + mock_results.current_page = Mock( + return_value=[table] + ) # current_page is sync + mock_aexecute.return_value = mock_results + + async def mock_save_side_effect(entity): + entity.assigned_terms = terms + return Mock(assets_updated=lambda asset_type: [entity]) + + mock_save.side_effect = mock_save_side_effect + + client = AsyncAtlanClient() + guid = "123" + asset = await client.asset.append_terms( + guid=guid, asset_type=asset_type, terms=terms + ) + + assert asset.assigned_terms == terms + mock_aexecute.assert_called_once() + mock_save.assert_called_once() + + +@pytest.mark.asyncio +async def test_append_with_valid_guid_when_terms_present_returns_asset_with_combined_terms(): + asset_type = Table + table = Table() + table.name = "table-test" + table.qualified_name = "table_qn" + + exisiting_term = AtlasGlossaryTerm() + table.attributes.meanings = [exisiting_term] + + new_term = AtlasGlossaryTerm(qualified_name="new_term") + terms = [new_term] + + with patch( + "pyatlan_v9.model.fluent_search.FluentSearch.execute_async", + new_callable=AsyncMock, + ) as mock_aexecute: + with patch( + "pyatlan_v9.client.aio.asset.V9AsyncAssetClient.save", + new_callable=AsyncMock, + ) as mock_save: + # Set up async mock for search results + mock_results = AsyncMock() + mock_results.current_page = Mock( + return_value=[table] + ) # current_page is sync + mock_aexecute.return_value = mock_results + + async def mock_save_side_effect(entity): + entity.assigned_terms = table.attributes.meanings + terms + return Mock(assets_updated=lambda asset_type: [entity]) + + mock_save.side_effect = mock_save_side_effect + + client = AsyncAtlanClient() + guid = "123" + + asset = await client.asset.append_terms( + guid=guid, asset_type=asset_type, terms=terms + ) + + updated_terms = asset.assigned_terms + assert updated_terms is not None + assert len(updated_terms) == 2 + assert exisiting_term in updated_terms + assert new_term in updated_terms + mock_aexecute.assert_called_once() + mock_save.assert_called_once() + + +@pytest.mark.parametrize( + "guid, qualified_name, asset_type, assigned_terms, expected_message, expected_error", + [ + ( + None, + None, + Table, + [AtlasGlossaryTerm()], + "ATLAN-PYTHON-400-043 Either qualified_name or guid should be provided.", + InvalidRequestError, + ), + ( + "123", + "default/abc", + Table, + [AtlasGlossaryTerm()], + "ATLAN-PYTHON-400-042 Only qualified_name or guid should be provided but not both.", + InvalidRequestError, + ), + ], +) +@pytest.mark.asyncio +async def test_replace_terms_invalid_parameters_raises_error( + guid, qualified_name, asset_type, assigned_terms, expected_message, expected_error +): + client = AsyncAtlanClient() + with pytest.raises(expected_error, match=expected_message): + await client.asset.replace_terms( + asset_type=asset_type, + terms=assigned_terms, + guid=guid, + qualified_name=qualified_name, + ) + + +@pytest.mark.parametrize( + "guid, qualified_name, asset_type, assigned_terms, mock_results, expected_message, expected_error", + [ + ( + None, + "nonexistent_qualified_name", + Table, + [AtlasGlossaryTerm()], + [], + "ATLAN-PYTHON-404-003 Asset with qualifiedName nonexistent_qualified_name of type Table does not exist." + " Suggestion: Verify the qualifiedName and expected type of the asset you are trying to retrieve.", + NotFoundError, + ), + ( + "nonexistent_guid", + None, + Table, + [AtlasGlossaryTerm()], + [], + "ATLAN-PYTHON-404-001 Asset with GUID nonexistent_guid does not exist." + " Suggestion: Verify the GUID of the asset you are trying to retrieve.", + NotFoundError, + ), + ( + None, + "default/abc", + Table, + [AtlasGlossaryTerm()], + ["DifferentTypeAsset"], + "ATLAN-PYTHON-404-014 The Table asset could not be found by name: default/abc." + " Suggestion: Verify the requested asset type and name exist in your Atlan environment.", + NotFoundError, + ), + ( + "123", + None, + Table, + [AtlasGlossaryTerm()], + ["DifferentTypeAsset"], + "ATLAN-PYTHON-404-002 Asset with GUID 123 is not of the type requested: Table." + " Suggestion: Verify the GUID and expected type of the asset you are trying to retrieve.", + NotFoundError, + ), + ], +) +@patch( + "pyatlan_v9.model.fluent_search.FluentSearch.execute_async", new_callable=AsyncMock +) +@pytest.mark.asyncio +async def test_replace_terms_asset_retrieval_errors( + mock_aexecute, + guid, + qualified_name, + asset_type, + assigned_terms, + mock_results, + expected_message, + expected_error, +): + mock_aexecute.return_value.current_page = lambda: mock_results + client = AsyncAtlanClient() + with pytest.raises(expected_error, match=expected_message): + await client.asset.replace_terms( + asset_type=asset_type, + terms=assigned_terms, + guid=guid, + qualified_name=qualified_name, + ) + + +@pytest.mark.asyncio +async def test_replace_terms(): + asset_type = Table + table = Table() + table.name = "table-test" + table.qualified_name = "table_qn" + + exisiting_term = AtlasGlossaryTerm() + table.attributes.meanings = [exisiting_term] + + terms = [AtlasGlossaryTerm(qualified_name="new_term")] + + with patch( + "pyatlan_v9.model.fluent_search.FluentSearch.execute_async", + new_callable=AsyncMock, + ) as mock_aexecute: + with patch( + "pyatlan_v9.client.aio.asset.V9AsyncAssetClient.save", + new_callable=AsyncMock, + ) as mock_save: + # Set up async mock for search results + mock_results = AsyncMock() + mock_results.current_page = Mock( + return_value=[table] + ) # current_page is sync + mock_aexecute.return_value = mock_results + + async def mock_save_side_effect(entity): + entity.assigned_terms = terms + return Mock(assets_updated=lambda asset_type: [entity]) + + mock_save.side_effect = mock_save_side_effect + + client = AsyncAtlanClient() + guid = "123" + + asset = await client.asset.replace_terms( + guid=guid, asset_type=asset_type, terms=terms + ) + + assert asset.assigned_terms == terms + mock_aexecute.assert_called_once() + mock_save.assert_called_once() + + +@pytest.mark.parametrize( + "guid, qualified_name, asset_type, assigned_terms, expected_message, expected_error", + [ + ( + None, + None, + Table, + [AtlasGlossaryTerm()], + "ATLAN-PYTHON-400-043 Either qualified_name or guid should be provided.", + InvalidRequestError, + ), + ( + "123", + "default/abc", + Table, + [AtlasGlossaryTerm()], + "ATLAN-PYTHON-400-042 Only qualified_name or guid should be provided but not both.", + InvalidRequestError, + ), + ], +) +@pytest.mark.asyncio +async def test_remove_terms_invalid_parameters_raises_error( + guid, qualified_name, asset_type, assigned_terms, expected_message, expected_error +): + client = AsyncAtlanClient() + with pytest.raises(expected_error, match=expected_message): + await client.asset.remove_terms( + asset_type=asset_type, + terms=assigned_terms, + guid=guid, + qualified_name=qualified_name, + ) + + +@pytest.mark.parametrize( + "guid, qualified_name, asset_type, assigned_terms, mock_results, expected_message, expected_error", + [ + ( + None, + "nonexistent_qualified_name", + Table, + [AtlasGlossaryTerm()], + [], + "ATLAN-PYTHON-404-003 Asset with qualifiedName nonexistent_qualified_name of type Table does not exist." + " Suggestion: Verify the qualifiedName and expected type of the asset you are trying to retrieve.", + NotFoundError, + ), + ( + "nonexistent_guid", + None, + Table, + [AtlasGlossaryTerm()], + [], + "ATLAN-PYTHON-404-001 Asset with GUID nonexistent_guid does not exist." + " Suggestion: Verify the GUID of the asset you are trying to retrieve.", + NotFoundError, + ), + ( + None, + "default/abc", + Table, + [AtlasGlossaryTerm()], + ["DifferentTypeAsset"], + "ATLAN-PYTHON-404-014 The Table asset could not be found by name: default/abc." + " Suggestion: Verify the requested asset type and name exist in your Atlan environment.", + NotFoundError, + ), + ( + "123", + None, + Table, + [AtlasGlossaryTerm()], + ["DifferentTypeAsset"], + "ATLAN-PYTHON-404-002 Asset with GUID 123 is not of the type requested: Table." + " Suggestion: Verify the GUID and expected type of the asset you are trying to retrieve.", + NotFoundError, + ), + ], +) +@patch( + "pyatlan_v9.model.fluent_search.FluentSearch.execute_async", new_callable=AsyncMock +) +@pytest.mark.asyncio +async def test_remove_terms_asset_retrieval_errors( + mock_aexecute, + guid, + qualified_name, + asset_type, + assigned_terms, + mock_results, + expected_message, + expected_error, +): + mock_aexecute.return_value.current_page = lambda: mock_results + client = AsyncAtlanClient() + with pytest.raises(expected_error, match=expected_message): + await client.asset.remove_terms( + asset_type=asset_type, + terms=assigned_terms, + guid=guid, + qualified_name=qualified_name, + ) + + +@pytest.mark.asyncio +async def test_remove_with_valid_guid_when_terms_present_returns_asset_with_terms_removed(): + asset_type = Table + table = Table() + table.name = "table-test" + table.qualified_name = "table_qn" + + existing_term = AtlasGlossaryTerm( + qualified_name="term_to_remove", guid="b4113341-251b-4adc-81fb-2420501c30e6" + ) + other_term = AtlasGlossaryTerm( + qualified_name="other_term", guid="b267858d-8316-4c41-a56a-6e9b840cef4a" + ) + table.attributes.meanings = [existing_term, other_term] + + with patch( + "pyatlan_v9.model.fluent_search.FluentSearch.execute_async", + new_callable=AsyncMock, + ) as mock_aexecute: + with patch( + "pyatlan_v9.client.aio.asset.V9AsyncAssetClient.save", + new_callable=AsyncMock, + ) as mock_save: + # Set up async mock for search results + mock_results = AsyncMock() + mock_results.current_page = Mock( + return_value=[table] + ) # current_page is sync + mock_aexecute.return_value = mock_results + + async def mock_save_side_effect(entity): + entity.assigned_terms = [ + t for t in table.attributes.meanings if t != existing_term + ] + return Mock(assets_updated=lambda asset_type: [entity]) + + mock_save.side_effect = mock_save_side_effect + + client = AsyncAtlanClient() + guid = "123" + + asset = await client.asset.remove_terms( + guid=guid, asset_type=asset_type, terms=[existing_term] + ) + + updated_terms = asset.assigned_terms + assert updated_terms is not None + assert len(updated_terms) == 1 + assert other_term in updated_terms + mock_aexecute.assert_called_once() + mock_save.assert_called_once() + + +@pytest.mark.parametrize( + "name, attributes, message", + [ + ( + 1, + None, + "1 validation error for FindGlossaryByName\nname\n str type expected", + ), + ( + None, + None, + "1 validation error for FindGlossaryByName\nname\n none is not an allowed value", + ), + ( + "Bob", + 1, + "1 validation error for FindGlossaryByName\nattributes\n value is not a valid list", + ), + ( + " ", + None, + "1 validation error for WithName\nvalue\n ensure this value has at least 1 characters", + ), + ], +) +@pytest.mark.asyncio +async def test_find_glossary_by_name_with_bad_values_raises_value_error( + name, attributes, message, client: AsyncAtlanClient +): + with pytest.raises(ValueError, match=message): + await client.asset.find_glossary_by_name(name=name, attributes=attributes) + + +@patch.object(AsyncAssetClient, "search") +@pytest.mark.asyncio +async def test_find_glossary_when_none_found_raises_not_found_error(mock_search): + mock_search.return_value.count = 0 + + client = AsyncAtlanClient() + with pytest.raises( + NotFoundError, + match=f"The AtlasGlossary asset could not be found by name: {GLOSSARY_NAME}.", + ): + await client.asset.find_glossary_by_name(GLOSSARY_NAME) + + +@patch.object(AsyncAssetClient, "search") +@pytest.mark.asyncio +async def test_find_glossary_when_non_glossary_found_raises_not_found_error( + mock_search, +): + # Set up async mock properly + mock_results = AsyncMock() + mock_results.count = 1 + mock_results.current_page = Mock(return_value=[Table()]) # current_page is sync + mock_search.return_value = mock_results + + client = AsyncAtlanClient() + with pytest.raises( + NotFoundError, + match=f"The AtlasGlossary asset could not be found by name: {GLOSSARY_NAME}.", + ): + await client.asset.find_glossary_by_name(GLOSSARY_NAME) + mock_search.return_value.current_page.assert_called_once() + + +@patch.object(AsyncAssetClient, "search") +@pytest.mark.asyncio +async def test_find_personas_by_name_when_none_found_raises_not_found_error( + mock_search, +): + mock_search.return_value.count = 0 + + client = AsyncAtlanClient() + with pytest.raises( + NotFoundError, + match=f"The Persona asset could not be found by name: {PERSONA_NAME}.", + ): + await client.asset.find_personas_by_name(name=PERSONA_NAME) + + +@patch.object(AsyncAssetClient, "search") +@pytest.mark.asyncio +async def test_find_purposes_by_name_when_none_found_raises_not_found_error( + mock_search, +): + mock_search.return_value.count = 0 + + client = AsyncAtlanClient() + with pytest.raises( + NotFoundError, + match=f"The Purpose asset could not be found by name: {PURPOSE_NAME}.", + ): + await client.asset.find_purposes_by_name(name=PURPOSE_NAME) + + +@patch.object(AsyncAssetClient, "search") +@pytest.mark.asyncio +async def test_find_connections_by_name_when_none_found_raises_not_found_error( + mock_search, +): + mock_search.return_value.count = 0 + + client = AsyncAtlanClient() + with pytest.raises( + NotFoundError, + match=f"The Connection asset could not be found by name: {CONNECTION_NAME}.", + ): + await client.asset.find_connections_by_name( + name=CONNECTION_NAME, connector_type=AtlanConnectorType(CONNECTOR_TYPE) + ) + + +@patch.object(AsyncAssetClient, "search") +@pytest.mark.asyncio +async def test_find_glossary(mock_search, caplog): + request = None + attributes = ["name"] + + def get_request(*args, **kwargs): + nonlocal request + request = args[0] + mock = Mock() + mock.count = 1 + mock.current_page.return_value = [GLOSSARY, GLOSSARY] + return mock + + mock_search.side_effect = get_request + + client = AsyncAtlanClient() + + assert GLOSSARY == await client.asset.find_glossary_by_name( + name=GLOSSARY_NAME, attributes=attributes + ) + assert ( + f"More than 1 AtlasGlossary found with the name '{GLOSSARY_NAME}', returning only the first." + in caplog.text + ) + assert request + assert request.attributes + assert attributes == request.attributes + assert request.dsl + assert request.dsl.query + assert isinstance(request.dsl.query, Bool) is True + assert request.dsl.query.filter + assert 3 == len(request.dsl.query.filter) + term1, term2, term3 = request.dsl.query.filter + assert isinstance(term1, Term) is True + assert term1.field == "__state" + assert term1.value == "ACTIVE" + assert isinstance(term2, Term) is True + assert term2.field == "__typeName.keyword" + assert term2.value == "AtlasGlossary" + assert isinstance(term3, Term) is True + assert term3.field == "name.keyword" + assert term3.value == GLOSSARY_NAME + + +@pytest.mark.parametrize( + "name, glossary_qualified_name, attributes, message", + [ + ( + 1, + GLOSSARY_QUALIFIED_NAME, + None, + "1 validation error for FindCategoryFastByName\nname\n str type expected", + ), + ( + None, + GLOSSARY_QUALIFIED_NAME, + None, + "1 validation error for FindCategoryFastByName\nname\n none is not an allowed value", + ), + ( + " ", + GLOSSARY_QUALIFIED_NAME, + None, + "1 validation error for WithName\nvalue\n ensure this value has at least 1 characters", + ), + ( + GLOSSARY_CATEGORY_NAME, + None, + None, + "1 validation error for FindCategoryFastByName\nglossary_qualified_name\n none is not an allowed value", + ), + ( + GLOSSARY_CATEGORY_NAME, + " ", + None, + "1 validation error for WithGlossary\nqualified_name\n ensure this value has at " + "least 1 characters", + ), + ( + GLOSSARY_CATEGORY_NAME, + 1, + None, + "1 validation error for FindCategoryFastByName\nglossary_qualified_name\n str type expected", + ), + ( + GLOSSARY_NAME, + GLOSSARY_QUALIFIED_NAME, + 1, + "1 validation error for FindCategoryFastByName\nattributes\n value is not a valid list", + ), + ], +) +@pytest.mark.asyncio +async def test_find_category_fast_by_name_with_bad_values_raises_value_error( + name, glossary_qualified_name, attributes, message, client: AsyncAtlanClient +): + with pytest.raises(ValueError, match=message): + await client.asset.find_category_fast_by_name( + name=name, + glossary_qualified_name=glossary_qualified_name, + attributes=attributes, + ) + + +@patch.object(AsyncAssetClient, "search") +@pytest.mark.asyncio +async def test_find_category_fast_by_name_when_none_found_raises_not_found_error( + mock_search, +): + mock_search.return_value.count = 0 + + client = AsyncAtlanClient() + with pytest.raises( + NotFoundError, + match=f"The AtlasGlossaryCategory asset could not be found by name: {GLOSSARY_CATEGORY_NAME}.", + ): + await client.asset.find_category_fast_by_name( + name=GLOSSARY_CATEGORY_NAME, glossary_qualified_name=GLOSSARY_QUALIFIED_NAME + ) + + +@patch.object(AsyncAssetClient, "search") +@pytest.mark.asyncio +async def test_find_category_fast_by_name_when_non_category_found_raises_not_found_error( + mock_search, +): + # Set up async mock properly + mock_results = AsyncMock() + mock_results.count = 1 + mock_results.current_page = Mock(return_value=[Table()]) # current_page is sync + mock_search.return_value = mock_results + + client = AsyncAtlanClient() + with pytest.raises( + NotFoundError, + match=f"The AtlasGlossaryCategory asset could not be found by name: {GLOSSARY_CATEGORY_NAME}.", + ): + await client.asset.find_category_fast_by_name( + name=GLOSSARY_CATEGORY_NAME, glossary_qualified_name=GLOSSARY_QUALIFIED_NAME + ) + mock_search.return_value.current_page.assert_called_once() + + +@patch.object(AsyncAssetClient, "search") +@pytest.mark.asyncio +async def test_find_category_fast_by_name(mock_search, caplog): + request = None + attributes = ["name"] + + def get_request(*args, **kwargs): + nonlocal request + request = args[0] + mock = AsyncMock() + mock.count = 1 + mock.current_page = Mock( + return_value=[GLOSSARY_CATEGORY, GLOSSARY_CATEGORY] + ) # current_page is sync + return mock + + mock_search.side_effect = get_request + + client = AsyncAtlanClient() + + assert ( + GLOSSARY_CATEGORY + == ( + await client.asset.find_category_fast_by_name( + name=GLOSSARY_CATEGORY_NAME, + glossary_qualified_name=GLOSSARY_QUALIFIED_NAME, + attributes=attributes, + ) + )[0] + ) + assert request + assert request.attributes + assert attributes == request.attributes + assert request.dsl + assert request.dsl.query + assert isinstance(request.dsl.query, Bool) is True + assert request.dsl.query.filter + assert 4 == len(request.dsl.query.filter) + term1, term2, term3, term4 = request.dsl.query.filter + assert term1.field == "__state" + assert term1.value == "ACTIVE" + assert isinstance(term2, Term) is True + assert term2.field == "__typeName.keyword" + assert term2.value == "AtlasGlossaryCategory" + assert isinstance(term3, Term) is True + assert term3.field == "name.keyword" + assert term3.value == GLOSSARY_CATEGORY_NAME + assert isinstance(term4, Term) is True + assert term4.field == "__glossary" + assert term4.value == GLOSSARY_QUALIFIED_NAME + + +@pytest.mark.parametrize( + "name, glossary_name, attributes, message", + [ + ( + None, + GLOSSARY_NAME, + None, + "1 validation error for FindCategoryByName\nname\n none is not an allowed value", + ), + pytest.param( + " ", + GLOSSARY_NAME, + None, + "1 validation error for FindCategoryByName\nname\n ensure this value has at least 1 characters", + marks=pytest.mark.skip( + reason="v9: name validation happens deeper in call chain, not at model level" + ), + ), + ( + 1, + GLOSSARY_NAME, + None, + "1 validation error for FindCategoryByName\nname\n str type expected", + ), + ( + GLOSSARY_CATEGORY_NAME, + None, + None, + "1 validation error for FindCategoryByName\nglossary_name\n none is not an allowed value", + ), + ( + GLOSSARY_CATEGORY_NAME, + " ", + None, + "1 validation error for WithName\nvalue\n ensure this value has at least 1 characters", + ), + ( + GLOSSARY_CATEGORY_NAME, + 1, + None, + "1 validation error for FindCategoryByName\nglossary_name\n str type expected", + ), + ( + GLOSSARY_CATEGORY_NAME, + GLOSSARY_NAME, + 1, + "1 validation error for FindCategoryByName\nattributes\n value is not a valid list", + ), + ], +) +@pytest.mark.asyncio +async def test_find_category_by_name_when_bad_parameter_raises_value_error( + name, glossary_name, attributes, message, client: AsyncAtlanClient +): + sut = client + + with pytest.raises(ValueError, match=message): + await sut.asset.find_category_by_name( + name=name, glossary_name=glossary_name, attributes=attributes + ) + + +@pytest.mark.asyncio +async def test_find_category_by_name(): + attributes = ["name"] + with patch.object( + AsyncAssetClient, "find_glossary_by_name", new_callable=AsyncMock + ) as mock_find_glossary_by_name: + with patch.object( + AsyncAssetClient, "find_category_fast_by_name", new_callable=AsyncMock + ) as mock_find_category_fast_by_name: + # Set up async mock for glossary + mock_glossary = AsyncMock() + mock_glossary.qualified_name = GLOSSARY_QUALIFIED_NAME + mock_find_glossary_by_name.return_value = mock_glossary + + sut = AsyncAtlanClient() + + category = await sut.asset.find_category_by_name( + name=GLOSSARY_CATEGORY_NAME, + glossary_name=GLOSSARY_NAME, + attributes=attributes, + ) + + mock_find_glossary_by_name.assert_called_with(name=GLOSSARY_NAME) + mock_find_category_fast_by_name.assert_called_with( + name=GLOSSARY_CATEGORY_NAME, + glossary_qualified_name=GLOSSARY_QUALIFIED_NAME, + attributes=attributes, + ) + assert mock_find_category_fast_by_name.return_value == category + + +@patch.object(AsyncAssetClient, "find_glossary_by_name", new_callable=AsyncMock) +@pytest.mark.asyncio +async def test_find_category_by_name_qn_guid_correctly_populated( + mock_find_glossary_by_name, mock_async_api_caller, glossary_category_by_name_json +): + client = AsyncAssetClient(mock_async_api_caller) + # Set up async mock + mock_glossary = AsyncMock() + mock_glossary.qualified_name = GLOSSARY_QUALIFIED_NAME + mock_find_glossary_by_name.return_value = mock_glossary + mock_async_api_caller._call_api.side_effect = [glossary_category_by_name_json] + + category = ( + await client.find_category_by_name( + name="test-cat-1-1", + glossary_name="test-glossary", + attributes=["terms", "anchor", "parentCategory"], + ) + )[0] + category_json = glossary_category_by_name_json["entities"][0] + + assert category + assert category_json + assert category.guid == category_json.get("guid") + category_json_attributes = category_json.get("attributes") + assert category_json_attributes + assert category.name == category_json_attributes.get("name") + assert category.qualified_name == category_json_attributes.get("qualifiedName") + + # Glossary + assert category.anchor.guid == category_json_attributes.get("anchor").get("guid") + assert category.anchor.name == category_json_attributes.get("anchor").get( + "attributes" + ).get("name") + assert category.anchor.qualified_name == category_json_attributes.get("anchor").get( + "uniqueAttributes" + ).get("qualifiedName") + + # Glossary category + assert category.parent_category.guid == category_json_attributes.get( + "parentCategory" + ).get("guid") + assert category.parent_category.name == category_json_attributes.get( + "parentCategory" + ).get("attributes").get("name") + assert category.parent_category.qualified_name == category_json_attributes.get( + "parentCategory" + ).get("uniqueAttributes").get("qualifiedName") + + # Glossary term + assert category.terms[0].guid == category_json_attributes.get("terms")[0].get( + "guid" + ) + assert category.terms[0].name == category_json_attributes.get("terms")[0].get( + "attributes" + ).get("name") + assert category.terms[0].qualified_name == category_json_attributes.get("terms")[ + 0 + ].get("uniqueAttributes").get("qualifiedName") + mock_async_api_caller.reset_mock() + + +@pytest.mark.parametrize( + "name, glossary_qualified_name, attributes, message", + [ + ( + 1, + GLOSSARY_QUALIFIED_NAME, + None, + "1 validation error for FindTermFastByName\nname\n str type expected", + ), + ( + None, + GLOSSARY_QUALIFIED_NAME, + None, + "1 validation error for FindTermFastByName\nname\n none is not an allowed value", + ), + ( + " ", + GLOSSARY_QUALIFIED_NAME, + None, + "1 validation error for WithName\nvalue\n ensure this value has at least 1 characters", + ), + ( + GLOSSARY_TERM_NAME, + None, + None, + "1 validation error for FindTermFastByName\nglossary_qualified_name\n none is not an allowed value", + ), + ( + GLOSSARY_TERM_NAME, + " ", + None, + "1 validation error for WithGlossary\nqualified_name\n ensure this value has at " + "least 1 characters", + ), + ( + GLOSSARY_TERM_NAME, + 1, + None, + "1 validation error for FindTermFastByName\nglossary_qualified_name\n str type expected", + ), + ( + GLOSSARY_TERM_NAME, + GLOSSARY_QUALIFIED_NAME, + 1, + "1 validation error for FindTermFastByName\nattributes\n value is not a valid list", + ), + ], +) +@pytest.mark.asyncio +async def test_find_term_fast_by_name_with_bad_values_raises_value_error( + name, glossary_qualified_name, attributes, message, client: AsyncAtlanClient +): + with pytest.raises(ValueError, match=message): + await client.asset.find_term_fast_by_name( + name=name, + glossary_qualified_name=glossary_qualified_name, + attributes=attributes, + ) + + +@patch.object(AsyncAssetClient, "search") +@pytest.mark.asyncio +async def test_find_term_fast_by_name_when_none_found_raises_not_found_error( + mock_search, +): + mock_search.return_value.count = 0 + + client = AsyncAtlanClient() + with pytest.raises( + NotFoundError, + match=f"The AtlasGlossaryTerm asset could not be found by name: {GLOSSARY_TERM_NAME}.", + ): + await client.asset.find_term_fast_by_name( + name=GLOSSARY_TERM_NAME, glossary_qualified_name=GLOSSARY_QUALIFIED_NAME + ) + + +@patch.object(AsyncAssetClient, "search") +@pytest.mark.asyncio +async def test_find_term_fast_by_name_when_non_term_found_raises_not_found_error( + mock_search, +): + # Set up async mock properly + mock_results = AsyncMock() + mock_results.count = 1 + mock_results.current_page = Mock(return_value=[Table()]) # current_page is sync + mock_search.return_value = mock_results + + client = AsyncAtlanClient() + with pytest.raises( + NotFoundError, + match=f"The AtlasGlossaryTerm asset could not be found by name: {GLOSSARY_TERM_NAME}.", + ): + await client.asset.find_term_fast_by_name( + name=GLOSSARY_TERM_NAME, glossary_qualified_name=GLOSSARY_QUALIFIED_NAME + ) + mock_search.return_value.current_page.assert_called_once() + + +@patch.object(AsyncAssetClient, "search") +@pytest.mark.asyncio +async def test_find_term_fast_by_name(mock_search, caplog): + request = None + attributes = ["name"] + + def get_request(*args, **kwargs): + nonlocal request + request = args[0] + mock = Mock() + mock.count = 1 + mock.current_page.return_value = [GLOSSARY_TERM, GLOSSARY_TERM] + return mock + + mock_search.side_effect = get_request + + client = AsyncAtlanClient() + + assert GLOSSARY_TERM == await client.asset.find_term_fast_by_name( + name=GLOSSARY_TERM_NAME, + glossary_qualified_name=GLOSSARY_QUALIFIED_NAME, + attributes=attributes, + ) + assert ( + f"More than 1 AtlasGlossaryTerm found with the name '{GLOSSARY_TERM_NAME}', returning only the first." + in caplog.text + ) + assert request + assert request.attributes + assert attributes == request.attributes + assert request.dsl + assert request.dsl.query + assert isinstance(request.dsl.query, Bool) is True + assert request.dsl.query.filter + assert 4 == len(request.dsl.query.filter) + term1, term2, term3, term4 = request.dsl.query.filter + assert term1.field == "__state" + assert term1.value == "ACTIVE" + assert isinstance(term2, Term) is True + assert term2.field == "__typeName.keyword" + assert term2.value == "AtlasGlossaryTerm" + assert isinstance(term3, Term) is True + assert term3.field == "name.keyword" + assert term3.value == GLOSSARY_TERM_NAME + assert isinstance(term4, Term) is True + assert term4.field == "__glossary" + assert term4.value == GLOSSARY_QUALIFIED_NAME + + +@pytest.mark.parametrize( + "name, glossary_name, attributes, message", + [ + ( + None, + GLOSSARY_NAME, + None, + "1 validation error for FindTermByName\nname\n none is not an allowed value", + ), + pytest.param( + " ", + GLOSSARY_NAME, + None, + "1 validation error for FindTermByName\nname\n ensure this value has at least 1 characters", + marks=pytest.mark.skip( + reason="v9: name validation happens deeper in call chain, not at model level" + ), + ), + ( + 1, + GLOSSARY_NAME, + None, + "1 validation error for FindTermByName\nname\n str type expected", + ), + ( + GLOSSARY_TERM_NAME, + None, + None, + "1 validation error for FindTermByName\nglossary_name\n none is not an allowed value", + ), + ( + GLOSSARY_TERM_NAME, + " ", + None, + "1 validation error for WithName\nvalue\n ensure this value has at least 1 characters", + ), + ( + GLOSSARY_TERM_NAME, + 1, + None, + "1 validation error for FindTermByName\nglossary_name\n str type expected", + ), + ( + GLOSSARY_TERM_NAME, + GLOSSARY_NAME, + 1, + "1 validation error for FindTermByName\nattributes\n value is not a valid list", + ), + ], +) +@pytest.mark.asyncio +async def test_find_term_by_name_when_bad_parameter_raises_value_error( + name, glossary_name, attributes, message, client: AsyncAtlanClient +): + sut = client + + with pytest.raises(ValueError, match=message): + await sut.asset.find_term_by_name( + name=name, glossary_name=glossary_name, attributes=attributes + ) + + +@pytest.mark.asyncio +async def test_find_term_by_name(): + attributes = ["name"] + with patch.object( + AsyncAssetClient, "find_glossary_by_name", new_callable=AsyncMock + ) as mock_find_glossary_by_name: + with patch.object( + AsyncAssetClient, "find_term_fast_by_name", new_callable=AsyncMock + ) as mock_find_term_fast_by_name: + # Set up async mock for glossary + mock_glossary = AsyncMock() + mock_glossary.qualified_name = GLOSSARY_QUALIFIED_NAME + mock_find_glossary_by_name.return_value = mock_glossary + + sut = AsyncAtlanClient() + + term = await sut.asset.find_term_by_name( + name=GLOSSARY_TERM_NAME, + glossary_name=GLOSSARY_NAME, + attributes=attributes, + ) + + mock_find_glossary_by_name.assert_called_with(name=GLOSSARY_NAME) + mock_find_term_fast_by_name.assert_called_with( + name=GLOSSARY_TERM_NAME, + glossary_qualified_name=GLOSSARY_QUALIFIED_NAME, + attributes=attributes, + ) + assert mock_find_term_fast_by_name.return_value == term + + +@patch.object(AsyncAssetClient, "_search_for_asset_with_name") +@pytest.mark.asyncio +async def test_find_domain_by_name(mock_search_for_asset_with_name): + client = AsyncAtlanClient() + test_domain = DataDomain() + test_domain.name = DATA_DOMAIN_NAME + mock_search_for_asset_with_name.return_value = [test_domain] + + domain = await client.asset.find_domain_by_name( + name=DATA_DOMAIN_NAME, + attributes=["name"], + ) + + assert domain and domain == test_domain + assert mock_search_for_asset_with_name.call_count == 1 + + +@patch.object(AsyncAssetClient, "_search_for_asset_with_name") +@pytest.mark.asyncio +async def test_find_product_by_name(mock_search_for_asset_with_name): + client = AsyncAtlanClient() + test_product = DataProduct() + test_product.name = DATA_PRODUCT_NAME + mock_search_for_asset_with_name.return_value = [test_product] + + product = await client.asset.find_product_by_name( + name=DATA_PRODUCT_NAME, + attributes=["name"], + ) + + assert product and product == test_product + assert mock_search_for_asset_with_name.call_count == 1 + + +@patch.object(AsyncAtlanClient, "_call_api") +@pytest.mark.asyncio +async def test_search_log_most_recent_viewers( + mock_call_api, mock_async_api_caller, sl_most_recent_viewers_json +): + async_client = AsyncAtlanClient() + client = AsyncSearchLogClient(async_client) + mock_call_api.return_value = sl_most_recent_viewers_json + recent_viewers_aggs = sl_most_recent_viewers_json["aggregations"] + recent_viewers_aggs_buckets = recent_viewers_aggs[UNIQUE_USERS]["buckets"] + request = SearchLogRequest.most_recent_viewers( + guid="test-guid-123", exclude_users=["testuser"] + ) + request_dsl_json = loads(request.dsl.json(by_alias=True, exclude_none=True)) + response = await client.search(request) + viewers = response.user_views + assert len(viewers) == 3 + assert response.asset_views is None + assert request_dsl_json == sl_most_recent_viewers_json[SEARCH_PARAMS]["dsl"] + assert response.count == sl_most_recent_viewers_json[SEARCH_COUNT] + assert viewers[0].username == recent_viewers_aggs_buckets[0]["key"] + assert viewers[0].view_count == recent_viewers_aggs_buckets[0]["doc_count"] + assert viewers[0].most_recent_view + assert viewers[1].username == recent_viewers_aggs_buckets[1]["key"] + assert viewers[1].view_count == recent_viewers_aggs_buckets[1]["doc_count"] + assert viewers[1].most_recent_view + mock_async_api_caller.reset_mock() + + +@pytest.mark.asyncio +async def test_search_log_most_viewed_assets( + mock_async_api_caller, sl_most_viewed_assets_json +): + client = AsyncSearchLogClient(mock_async_api_caller) + mock_async_api_caller._call_api.return_value = sl_most_viewed_assets_json + viewed_assets_aggs = sl_most_viewed_assets_json["aggregations"] + viewed_assets_aggs_buckets = viewed_assets_aggs[UNIQUE_ASSETS]["buckets"][0] + request = SearchLogRequest.most_viewed_assets( + max_assets=10, exclude_users=["testuser"] + ) + request_dsl_json = loads(request.dsl.json(by_alias=True, exclude_none=True)) + response = await client.search(request) + detail = response.asset_views + assert len(detail) == 8 + assert response.user_views is None + assert request_dsl_json == sl_most_viewed_assets_json[SEARCH_PARAMS]["dsl"] + assert response.count == sl_most_viewed_assets_json[SEARCH_COUNT] + assert detail[0].guid == viewed_assets_aggs_buckets["key"] + assert detail[0].total_views == viewed_assets_aggs_buckets["doc_count"] + assert detail[0].distinct_users == viewed_assets_aggs_buckets[UNIQUE_USERS]["value"] + mock_async_api_caller.reset_mock() + + +@pytest.mark.asyncio +async def test_search_log_views_by_guid( + mock_async_api_caller, sl_detailed_log_entries_json +): + client = AsyncSearchLogClient(mock_async_api_caller) + mock_async_api_caller._call_api.return_value = sl_detailed_log_entries_json + sl_detailed_log_entries = sl_detailed_log_entries_json["logs"] + request = SearchLogRequest.views_by_guid( + guid="test-guid-123", size=10, exclude_users=["testuser"] + ) + request_dsl_json = loads(request.dsl.json(by_alias=True, exclude_none=True)) + response = await client.search(request) + log_entries = response.current_page() + assert request_dsl_json == sl_detailed_log_entries_json[SEARCH_PARAMS]["dsl"] + assert len(response.current_page()) == sl_detailed_log_entries_json[SEARCH_COUNT] + assert log_entries[0].user_name == sl_detailed_log_entries[0][LOG_USERNAME] + assert log_entries[0].ip_address == sl_detailed_log_entries[0][LOG_IP_ADDRESS] + assert log_entries[0].host + assert log_entries[0].user_agent + assert log_entries[0].utm_tags + assert log_entries[0].entity_guids_all + assert log_entries[0].entity_guids_allowed + assert log_entries[0].entity_qf_names_all + assert log_entries[0].entity_qf_names_allowed + assert log_entries[0].entity_type_names_all + assert log_entries[0].entity_type_names_allowed + assert log_entries[0].has_result + assert log_entries[0].results_count + assert log_entries[0].response_time + assert log_entries[0].created_at + assert log_entries[0].timestamp + assert log_entries[0].failed is False + assert log_entries[0].request_dsl + assert log_entries[0].request_dsl_text + assert log_entries[0].request_attributes is None + assert log_entries[0].request_relation_attributes + mock_async_api_caller.reset_mock() + + +@pytest.mark.asyncio +async def test_asset_get_lineage_list_response_with_custom_metadata( + mock_async_api_caller, lineage_list_json +): + asset_client = AsyncAssetClient(mock_async_api_caller) + mock_async_api_caller._call_api.side_effect = [lineage_list_json, {}] + + lineage_request = LineageListRequest( + guid="test-guid", depth=1, direction=LineageDirection.UPSTREAM + ) + lineage_request.attributes = [CM_NAME] + lineage_response = await asset_client.get_lineage_list( + lineage_request=lineage_request + ) + + async for asset in lineage_response: + assert asset + assert asset.depth == 1 + assert asset.type_name == "View" + assert asset.guid == "test-guid" + assert asset.qualified_name == "test-qn" + assert asset.attributes + assert asset.business_attributes + assert asset.business_attributes == {"testcm1": {"testcm2": "test-cm-value"}} + + assert mock_async_api_caller._call_api.call_count == 1 + mock_async_api_caller.reset_mock() + + +@pytest.mark.asyncio +async def test_group_get_pagination(mock_async_api_caller, group_list_json): + client = AsyncGroupClient(mock_async_api_caller) + last_page_response = {"totalRecord": 3, "filterRecord": 3, "records": None} + mock_async_api_caller._call_api.side_effect = [group_list_json, last_page_response] + response = await client.get() + + assert response + assert len(response.current_page()) == 2 + async for group in response: + assert group.name + assert group.path + assert group.personas + assert len(response.current_page()) == 0 + assert mock_async_api_caller._call_api.call_count == 2 + mock_async_api_caller.reset_mock() + + +@pytest.mark.asyncio +async def test_group_get_members_pagination(mock_async_api_caller, group_members_json): + client = AsyncGroupClient(mock_async_api_caller) + last_page_response = {"totalRecord": 3, "filterRecord": 3, "records": None} + mock_async_api_caller._call_api.side_effect = [ + group_members_json, + last_page_response, + ] + response = await client.get_members(guid="test-guid", request=UserRequest()) + + assert response + assert len(response.current_page()) == 3 + async for user in response: + assert user.username + assert user.email + assert user.attributes + assert len(response.current_page()) == 0 + assert mock_async_api_caller._call_api.call_count == 2 + mock_async_api_caller.reset_mock() + + +@pytest.mark.asyncio +async def test_user_list_pagination(mock_async_api_caller, user_list_json): + client = AsyncUserClient(mock_async_api_caller) + last_page_response = {"totalRecord": 3, "filterRecord": 3, "records": None} + mock_async_api_caller._call_api.side_effect = [user_list_json, last_page_response] + response = await client.get() + + assert response + assert len(response.current_page()) == 3 + async for user in response: + assert user.username + assert user.email + assert user.attributes + assert user.login_events + assert len(response.current_page()) == 0 + assert mock_async_api_caller._call_api.call_count == 2 + mock_async_api_caller.reset_mock() + + +@pytest.mark.asyncio +async def test_user_groups_pagination(mock_async_api_caller, user_groups_json): + client = AsyncUserClient(mock_async_api_caller) + last_page_response = {"totalRecord": 2, "filterRecord": 2, "records": None} + mock_async_api_caller._call_api.side_effect = [user_groups_json, last_page_response] + response = await client.get_groups(guid="test-guid", request=GroupRequest()) + + assert response + assert len(response.current_page()) == 2 + async for group in response: + assert group.name + assert group.path + assert group.alias + assert group.attributes + assert len(response.current_page()) == 0 + assert mock_async_api_caller._call_api.call_count == 2 + mock_async_api_caller.reset_mock() + + +@pytest.mark.asyncio +async def test_index_search_with_no_aggregation_results( + mock_async_api_caller, aggregations_null_json +): + client = AsyncAssetClient(mock_async_api_caller) + mock_async_api_caller._call_api.side_effect = [aggregations_null_json] + request = ( + FluentSearch( + aggregations={"test1": {"test2": {"field": "__test_field"}}} + ).where(Column.QUALIFIED_NAME.startswith("test-qn")) + ).to_request() + response = await client.search(criteria=request) + assert response + assert response.count == 0 + assert response.aggregations is None + mock_async_api_caller.reset_mock() + + +@pytest.mark.asyncio +async def test_type_name_in_asset_search_bool_filter(mock_async_api_caller): + # When the type name is not present in the request + request = (FluentSearch().where(CompoundQuery.active_assets())).to_request() + Search._ensure_type_filter_present(request) + + assert request.dsl.query and request.dsl.query.filter + assert isinstance(request.dsl.query.filter, list) + + has_type_filter = any( + isinstance(f, Term) and f.field == TermAttributes.SUPER_TYPE_NAMES.value + for f in request.dsl.query.filter + ) + assert has_type_filter is True + + # When the type name is present in the request (no need to add super type filter) + request = ( + FluentSearch() + .where(CompoundQuery.active_assets()) + .where(CompoundQuery.asset_type(AtlasGlossary)) + ).to_request() + Search._ensure_type_filter_present(request) + + assert request.dsl.query and request.dsl.query.filter + assert isinstance(request.dsl.query.filter, list) + + has_type_filter = any( + isinstance(f, Term) and f.field == TermAttributes.SUPER_TYPE_NAMES.value + for f in request.dsl.query.filter + ) + assert has_type_filter is False + + # When multiple type name(s) is present in the request (no need to add super type filter) + request = ( + FluentSearch() + .where(CompoundQuery.active_assets()) + .where(CompoundQuery.asset_types([AtlasGlossary, AtlasGlossaryTerm])) + ).to_request() + Search._ensure_type_filter_present(request) + + assert request.dsl.query and request.dsl.query.filter + assert isinstance(request.dsl.query.filter, list) + + has_type_filter = any( + isinstance(f, Term) and f.field == TermAttributes.SUPER_TYPE_NAMES.value + for f in request.dsl.query.filter + ) + assert has_type_filter is False + + +@pytest.mark.asyncio +async def test_type_name_in_asset_search_bool_must(mock_async_api_caller): + # When the type name is not present in the request + query = Bool(must=[Term.with_state("ACTIVE")]) + request = IndexSearchRequest(dsl=DSL(query=query)) + Search._ensure_type_filter_present(request) + + assert request.dsl.query and request.dsl.query.must + assert isinstance(request.dsl.query.must, list) + + has_type_filter = any( + isinstance(f, Term) and f.field == TermAttributes.SUPER_TYPE_NAMES.value + for f in request.dsl.query.must + ) + assert has_type_filter is True + + # When the type name is present in the request (no need to add super type filter) + query = Bool(must=[Term.with_state("ACTIVE"), Term.with_type_name("AtlasGlossary")]) + request = IndexSearchRequest(dsl=DSL(query=query)) + Search._ensure_type_filter_present(request) + + assert request.dsl.query and request.dsl.query.must + assert isinstance(request.dsl.query.must, list) + + has_type_filter = any( + isinstance(f, Term) and f.field == TermAttributes.SUPER_TYPE_NAMES.value + for f in request.dsl.query.must + ) + assert has_type_filter is False + + # When multiple type name(s) is present in the request (no need to add super type filter) + query = Bool( + must=[ + Term.with_state("ACTIVE"), + Term.with_type_name("AtlasGlossary"), + Term.with_type_name("AtlasGlossaryTerm"), + ] + ) + request = IndexSearchRequest(dsl=DSL(query=query)) + Search._ensure_type_filter_present(request) + + assert request.dsl.query and request.dsl.query.must + assert isinstance(request.dsl.query.must, list) + + has_type_filter = any( + isinstance(f, Term) and f.field == TermAttributes.SUPER_TYPE_NAMES.value + for f in request.dsl.query.must + ) + assert has_type_filter is False + + +async def _assert_search_results(results, response_json, sorts, bulk=False): + # Async iteration for async search results + entities = [] + async for result in results: + entities.append(result) + + for i, result in enumerate(entities): + assert result and response_json["entities"][i] + assert result.guid == response_json["entities"][i]["guid"] + + assert results + assert results.count == 2 + assert results._bulk == bulk + assert results.aggregations is None + assert results._criteria.dsl.sort == sorts + + +@patch.object(SHARED_LOGGER, "debug") +@pytest.mark.asyncio +async def test_index_search_pagination( + mock_shared_logger, mock_async_api_caller, index_search_paging_json +): + client = AsyncAssetClient(mock_async_api_caller) + mock_async_api_caller._call_api.side_effect = [index_search_paging_json, {}] + + # Test search(): using default offset-based pagination + # when results are less than the predefined threshold (i.e: 100,000 assets) + request = ( + FluentSearch() + .where(CompoundQuery.active_assets()) + .where(CompoundQuery.asset_type(AtlasGlossaryTerm)) + .page_size(2) + ).to_request() + results = await client.search(criteria=request) + expected_sorts = [Asset.GUID.order(SortOrder.ASCENDING)] + + await _assert_search_results(results, index_search_paging_json, expected_sorts) + assert mock_async_api_caller._call_api.call_count == 2 + mock_async_api_caller.reset_mock() + + # Test search(): with `bulk` option using timestamp-based pagination + mock_async_api_caller._call_api.side_effect = [index_search_paging_json, {}] + request = ( + FluentSearch() + .where(CompoundQuery.active_assets()) + .where(CompoundQuery.asset_type(AtlasGlossaryTerm)) + .page_size(2) + ).to_request() + results = await client.search(criteria=request, bulk=True) + expected_sorts = [ + Asset.CREATE_TIME.order(SortOrder.ASCENDING), + Asset.GUID.order(SortOrder.ASCENDING), + ] + + await _assert_search_results( + results, index_search_paging_json, expected_sorts, True + ) + assert mock_async_api_caller._call_api.call_count == 2 + assert mock_shared_logger.call_count == 1 + assert ( + "Bulk search option is enabled." in mock_shared_logger.call_args_list[0][0][0] + ) + mock_shared_logger.reset_mock() + mock_async_api_caller.reset_mock() + + # Test search(): when the number of results exceeds the predefined threshold + # it will automatically convert to a `bulk` search. + TEST_THRESHOLD = 1 + with patch.object( + AsyncIndexSearchResults, "_MASS_EXTRACT_THRESHOLD", TEST_THRESHOLD + ): + mock_async_api_caller._call_api.side_effect = [ + index_search_paging_json, + # Extra call to re-fetch the first page + # results with updated timestamp sorting + index_search_paging_json, + {}, + ] + request = ( + FluentSearch() + .where(CompoundQuery.active_assets()) + .where(CompoundQuery.asset_type(AtlasGlossaryTerm)) + .page_size(2) + ).to_request() + results = await client.search(criteria=request) + expected_sorts = [ + Asset.CREATE_TIME.order(SortOrder.ASCENDING), + Asset.GUID.order(SortOrder.ASCENDING), + ] + await _assert_search_results(results, index_search_paging_json, expected_sorts) + assert mock_async_api_caller._call_api.call_count == 3 + assert mock_shared_logger.call_count == 1 + assert ( + "Result size (%s) exceeds threshold (%s)" + in mock_shared_logger.call_args_list[0][0][0] + ) + mock_shared_logger.reset_mock() + mock_async_api_caller.reset_mock() + + # Test search(bulk=False): Raise an exception when the number of results exceeds + # the predefined threshold and there are any user-defined sorting options present + with patch.object( + AsyncIndexSearchResults, "_MASS_EXTRACT_THRESHOLD", TEST_THRESHOLD + ): + mock_async_api_caller._call_api.side_effect = [ + index_search_paging_json, + ] + request = ( + FluentSearch() + .where(CompoundQuery.active_assets()) + .where(CompoundQuery.asset_type(AtlasGlossaryTerm)) + .page_size(2) + # With some sort options + .sort(Asset.NAME.order(SortOrder.ASCENDING)) + ).to_request() + + with pytest.raises( + InvalidRequestError, + match=( + "ATLAN-PYTHON-400-063 Unable to execute " + "bulk search with user-defined sorting options. " + "Suggestion: Please ensure that no sorting options are " + "included in your search request when performing a bulk search." + ), + ): + await client.search(criteria=request) + assert mock_async_api_caller._call_api.call_count == 1 + mock_async_api_caller.reset_mock() + + # Test search(bulk=True): Raise an exception when bulk search is enabled + # and there are any user-defined sorting options present + request = ( + FluentSearch() + .where(CompoundQuery.active_assets()) + .where(CompoundQuery.asset_type(AtlasGlossaryTerm)) + .page_size(2) + .sort(Asset.NAME.order(SortOrder.ASCENDING)) + ).to_request() + + with pytest.raises( + InvalidRequestError, + match=( + "ATLAN-PYTHON-400-063 Unable to execute " + "bulk search with user-defined sorting options. " + "Suggestion: Please ensure that no sorting options are " + "included in your search request when performing a bulk search." + ), + ): + await client.search(criteria=request, bulk=True) + + +@pytest.mark.asyncio +async def test_asset_get_by_guid_without_asset_type( + mock_async_api_caller, get_by_guid_json +): + client = AsyncAssetClient(mock_async_api_caller) + mock_async_api_caller._call_api.side_effect = [get_by_guid_json] + + response = await client.get_by_guid( + guid="test-table-guid-123", ignore_relationships=False + ) + + assert response + assert isinstance(response, Table) + assert response.guid + assert response.qualified_name + assert response.attributes + mock_async_api_caller.reset_mock() + + +@pytest.mark.asyncio +async def test_asset_retrieve_minimal_without_asset_type( + mock_async_api_caller, retrieve_minimal_json +): + client = AsyncAssetClient(mock_async_api_caller) + mock_async_api_caller._call_api.side_effect = [retrieve_minimal_json] + + response = await client.retrieve_minimal(guid="test-table-guid-123") + + assert response + assert isinstance(response, Table) + assert response.guid + assert response.qualified_name + assert response.attributes + mock_async_api_caller.reset_mock() + + +@patch.object(AsyncAtlanClient, "_call_api") +@pytest.mark.asyncio +async def test_user_create( + mock_call_api, + mock_role_cache, +): + test_role_id = "role-guid-123" + async_client = AsyncAtlanClient() + client = AsyncUserClient(async_client) + + # Set up mocks + mock_role_cache.get_id_for_name.return_value = test_role_id + mock_call_api.return_value = None + + async_client._caches["role"] = mock_role_cache + + test_users = [AtlanUser.creator(email="test@test.com", role_name="$member")] + response = await client.creator(users=test_users) + + assert response is None + # Verify that the role cache was called to get the role ID + mock_role_cache.get_id_for_name.assert_called_once_with("$member") + + +@pytest.mark.asyncio +async def test_user_create_with_info( + mock_async_api_caller, mock_role_cache, user_list_json +): + test_role_id = "role-guid-123" + client = AsyncUserClient(mock_async_api_caller) + client._client.role_cache = mock_role_cache + mock_async_api_caller._call_api.side_effect = [ + None, + { + "totalRecord": 3, + "filterRecord": 1, + "records": [user_list_json["records"][0]], + }, + ] + mock_role_cache.get_id_for_name.return_value = test_role_id + test_users = [AtlanUser.creator(email="test@test.com", role_name="$member")] + response = await client.creator(users=test_users, return_info=True) + + assert len(response.current_page()) == 1 + user = response.current_page()[0] + assert user + assert user.username + assert user.email + assert user.attributes + assert user.login_events + assert mock_async_api_caller._call_api.call_count == 2 + mock_async_api_caller.reset_mock() + + +@pytest.mark.skip( + reason="Legacy AsyncTypeDefClient returns pydantic EnumDef, can't compare with v9 msgspec EnumDef" +) +@pytest.mark.asyncio +async def test_typedef_get_by_name(mock_async_api_caller, type_def_get_by_name_json): + client = AsyncTypeDefClient(mock_async_api_caller) + mock_async_api_caller._call_api.side_effect = [type_def_get_by_name_json] + response = await client.get_by_name(name="test-enum") + assert response == msgspec.convert(type_def_get_by_name_json, EnumDef) + assert mock_async_api_caller._call_api.call_count == 1 + mock_async_api_caller.reset_mock() + + +@pytest.mark.asyncio +async def test_typedef_get_by_name_unsupported_category(mock_async_api_caller): + client = AsyncTypeDefClient(mock_async_api_caller) + mock_async_api_caller._call_api.side_effect = [{"category": "TEST"}] + with pytest.raises(ApiError) as err: + await client.get_by_name(name="test-enum") + + assert "Unsupported type definition category: TEST" in str(err.value) + mock_async_api_caller.reset_mock() + + +@pytest.mark.asyncio +async def test_typedef_get_by_name_invalid_response(mock_async_api_caller): + client = AsyncTypeDefClient(mock_async_api_caller) + mock_async_api_caller._call_api.side_effect = [123] + with pytest.raises(ApiError) as err: + await client.get_by_name(name="test-enum") + assert "Additional details: 'int' object has no attribute 'get'" in str(err.value) + + mock_async_api_caller._call_api.side_effect = [ + {"category": "ENUM", "test": "invalid"} + ] + response = await client.get_by_name(name="test-enum") + assert isinstance(response, EnumDef) + mock_async_api_caller.reset_mock() + + +@pytest.mark.parametrize( + "test_method, test_kwargs, test_asset_types", + [ + [ + "update_certificate", + { + "qualified_name": "test-qn", + "name": "test-name", + "certificate_status": CertificateStatus.VERIFIED, + "message": "test-message", + }, + [AtlasGlossaryTerm, AtlasGlossaryCategory], + ], + [ + "remove_certificate", + { + "qualified_name": "test-qn", + "name": "test-name", + }, + [AtlasGlossaryTerm, AtlasGlossaryCategory], + ], + [ + "update_announcement", + { + "qualified_name": "test-qn", + "name": "test-name", + "announcement": TEST_ANNOUNCEMENT, + }, + [AtlasGlossaryTerm, AtlasGlossaryCategory], + ], + [ + "remove_announcement", + {"qualified_name": "test-qn", "name": "test-name"}, + [AtlasGlossaryTerm, AtlasGlossaryCategory], + ], + ], +) +@pytest.mark.asyncio +async def test_asset_client_missing_glossary_guid_raises_invalid_request_error( + test_method: str, + test_kwargs: dict, + test_asset_types, +): + client = AsyncAtlanClient() + asset_client_method = getattr(client.asset, test_method) + + for asset_type in test_asset_types: + test_error = TEST_MISSING_GLOSSARY_GUID_ERROR.format(asset_type.__name__) + with pytest.raises(InvalidRequestError, match=test_error): + await asset_client_method(**test_kwargs, asset_type=asset_type) + + +@pytest.mark.parametrize("method, params", V9_TEST_ASSET_CLIENT_METHODS_ASYNC.items()) +@pytest.mark.asyncio +async def test_asset_client_methods_validation_error(client, method, params): + client_method = getattr(client.asset, method) + for param_values, error_msg in params: + with pytest.raises(ValueError) as err: + await client_method(*param_values) + assert error_msg in str(err.value) + + +@pytest.mark.parametrize("method, params", TEST_ADMIN_CLIENT_METHODS.items()) +@pytest.mark.asyncio +async def test_admin_client_methods_validation_error(client, method, params): + client_method = getattr(client.admin, method) + for param_values, error_msg in params: + with pytest.raises(ValueError) as err: + await client_method(*param_values) + assert error_msg in str(err.value) + + +@pytest.mark.parametrize("method, params", TEST_AUDIT_CLIENT_METHODS.items()) +@pytest.mark.asyncio +async def test_async_audit_client_methods_validation_error(client, method, params): + client_method = getattr(client.audit, method) + for param_values, error_msg in params: + with pytest.raises(ValueError) as err: + await client_method(*param_values) + assert error_msg in str(err.value) + + +@pytest.mark.parametrize("method, params", V9_TEST_GROUP_CLIENT_METHODS.items()) +@pytest.mark.asyncio +async def test_async_group_client_methods_validation_error(client, method, params): + client_method = getattr(client.group, method) + for param_values, error_msg in params: + with pytest.raises(ValueError) as err: + await client_method(*param_values) + assert error_msg in str(err.value) + + +@pytest.mark.parametrize("method, params", TEST_ROLE_CLIENT_METHODS.items()) +@pytest.mark.asyncio +async def test_role_client_methods_validation_error(client, method, params): + client_method = getattr(client.role, method) + for param_values, error_msg in params: + with pytest.raises(ValueError) as err: + await client_method(*param_values) + assert error_msg in str(err.value) + + +@pytest.mark.parametrize("method, params", TEST_SL_CLIENT_METHODS.items()) +@pytest.mark.asyncio +async def test_async_search_log_client_methods_validation_error(client, method, params): + client_method = getattr(client.search_log, method) + for param_values, error_msg in params: + with pytest.raises(ValueError) as err: + await client_method(*param_values) + assert error_msg in str(err.value) + + +@pytest.mark.parametrize("method, params", V9_TEST_TOKEN_CLIENT_METHODS.items()) +@pytest.mark.asyncio +async def test_async_token_client_methods_validation_error(client, method, params): + client_method = getattr(client.token, method) + for param_values, error_msg in params: + with pytest.raises(ValueError) as err: + await client_method(*param_values) + assert error_msg in str(err.value) + + +@pytest.mark.parametrize("method, params", V9_TEST_TYPEDEF_CLIENT_METHODS.items()) +@pytest.mark.asyncio +async def test_async_typedef_client_methods_validation_error(client, method, params): + client_method = getattr(client.typedef, method) + for param_values, error_msg in params: + with pytest.raises(ValueError) as err: + await client_method(*param_values) + assert error_msg in str(err.value) + + +@pytest.mark.parametrize("method, params", V9_TEST_USER_CLIENT_METHODS.items()) +@pytest.mark.asyncio +async def test_async_user_client_methods_validation_error(client, method, params): + client_method = getattr(client.user, method) + for param_values, error_msg in params: + with pytest.raises(ValueError) as err: + await client_method(*param_values) + assert error_msg in str(err.value) + + +@pytest.mark.parametrize( + "test_error_msg", + [ + "{'error': 123}", + "{'error': 123, 'code': 465}", + "{'error': 123} with text", + "Some error message...", + "With unescape curly braces -> {'{}'}", + ], +) +@patch.object(AsyncAtlanClient, "_async_session") +@pytest.mark.asyncio +async def test_atlan_call_api_server_error_messages( + mock_session, + client: AsyncAtlanClient, + test_error_msg, +): + mock_response = Mock() + mock_response.status_code = 500 + mock_response.text = test_error_msg + mock_session.headers = {} + mock_session.request = AsyncMock(return_value=mock_response) + glossary = AtlasGlossary.creator(name="test-glossary") + + with pytest.raises( + AtlanError, + match=( + f"ATLAN-PYTHON-500-000 {test_error_msg} " + "Suggestion: Check the details of the " + "server's message to correct your request." + ), + ): + await client.asset.save(glossary) + + +@pytest.mark.parametrize( + "test_error_msg", + [ + """ + { + "errorCode": 1234, + "errorMessage": "something went wrong", + "causes": [ + { + "errorType": "testException", + "errorMessage": "test error message", + "location": "Test.Class.TestException" + } + ], + "errorCause": "something went wrong", + "errorId": "95d80a45999cabc", + "doc": "https://ask.atlan.com/hc/en-us/articles/6645223434141-Is-there-a-limit-on-the-number-of-API-requests-that-can-be-performed" + } + """ + ], +) +@patch.object(AsyncAtlanClient, "_async_session") +@pytest.mark.asyncio +async def test_atlan_call_api_server_error_messages_with_causes( + mock_session, + client: AsyncAtlanClient, + test_error_msg, +): + ERROR_CODE_FOR_HTTP_STATUS.update( + {ErrorCode.ERROR_PASSTHROUGH.http_error_code: ErrorCode.ERROR_PASSTHROUGH} + ) + STATUS_CODES = set(ERROR_CODE_FOR_HTTP_STATUS.keys()) + # For "NOT_FOUND (404)" errors, no error cause is returned by the backend, + # so we'll exclude that from the test cases: + STATUS_CODES.remove(ErrorCode.NOT_FOUND_PASSTHROUGH.http_error_code) + + for code in STATUS_CODES: + error = ERROR_CODE_FOR_HTTP_STATUS.get(code) + mock_response = Mock() + mock_response.status_code = code + mock_response.text = test_error_msg + mock_session.headers = {} # Mock headers as empty dict + mock_session.request = AsyncMock(return_value=mock_response) + test_error = loads(test_error_msg) + error_code = test_error.get("errorCode") + error_message = test_error.get("errorMessage") + error_cause = test_error.get("errorCause") + error_doc = test_error.get("doc") + error_id = test_error.get("errorId") + error_causes = test_error.get("causes")[0] + glossary = AtlasGlossary.creator(name="test-glossary") + error_causes = "ErrorType: testException, Message: test error message, Location: Test.Class.TestException" + assert error and error_code and error_message and error_cause and error_causes + error_info = error.exception_with_parameters( + error_code, + error_message, + error_causes, + error_cause=error_cause, + backend_error_id=error_id, + error_doc=error_doc, + ) + + with pytest.raises( + AtlanError, + match=escape(str(error_info)), + ): + await client.asset.save(glossary) + + +class TestBatch: + def test_init(self, mock_async_atlan_client): + sut = AsyncBatch(client=mock_async_atlan_client, max_size=10) + + self.assert_asset_client_not_called(mock_async_atlan_client, sut) + + def assert_asset_client_not_called(self, mock_async_atlan_client, sut): + assert 0 == len(sut.created) + assert 0 == len(sut.updated) + assert 0 == len(sut.failures) + mock_async_atlan_client.assert_not_called() + + @pytest.mark.parametrize( + "custom_metadata_handling", + [ + (CustomMetadataHandling.IGNORE), + (CustomMetadataHandling.OVERWRITE), + (CustomMetadataHandling.MERGE), + ], + ) + @pytest.mark.asyncio + async def test_add_when_capture_failure_true( + self, custom_metadata_handling, mock_async_atlan_client + ): + table_1 = Mock(Table(guid="t1")) + table_2 = Mock(Table(guid="t2")) + table_3 = Mock(Table(guid="t3")) + table_4 = Mock(Table(guid="t4")) + mock_response = Mock(spec=AssetMutationResponse) + mutated_entities = Mock() + created = [table_1] + updated = [table_2] + mutated_entities.CREATE = created + mutated_entities.UPDATE = updated + mock_response.guid_assignments = {} + mock_response.attach_mock(mutated_entities, "mutated_entities") + + # Set up async mocks - need to mock the FluentSearch.execute_async behavior + mock_search_results = AsyncMock() + mock_search_results.__aiter__ = AsyncMock( + return_value=iter([]) + ) # Empty iterator for the async for loop + + # Set up async mocks for save methods + if custom_metadata_handling == CustomMetadataHandling.IGNORE: + mock_async_atlan_client.asset.save = AsyncMock(return_value=mock_response) + elif custom_metadata_handling == CustomMetadataHandling.OVERWRITE: + mock_async_atlan_client.asset.save_replacing_cm = AsyncMock( + return_value=mock_response + ) + else: + mock_async_atlan_client.asset.save_merging_cm = AsyncMock( + return_value=mock_response + ) + + # Mock FluentSearch.execute_async to return our mock results + with patch( + "pyatlan_v9.model.fluent_search.FluentSearch.execute_async", + new_callable=AsyncMock, + ) as mock_aexecute: + mock_aexecute.return_value = mock_search_results + + sut = AsyncBatch( + client=mock_async_atlan_client, + max_size=2, + capture_failures=True, + custom_metadata_handling=custom_metadata_handling, + ) + await sut.add(table_1) + self.assert_asset_client_not_called(mock_async_atlan_client, sut) + + await sut.add(table_2) + + assert len(created) == sut.num_created + assert len(updated) == sut.num_updated + for unsaved, saved in zip(created, sut.created): + unsaved.trim_to_required.assert_called_once() + assert unsaved.name == saved.name + for unsaved, saved in zip(updated, sut.updated): + unsaved.trim_to_required.assert_called_once() + assert unsaved.name == saved.name + + exception = ErrorCode.INVALID_REQUEST_PASSTHROUGH.exception_with_parameters( + "bad", "stuff", "" + ) + if custom_metadata_handling == CustomMetadataHandling.IGNORE: + mock_async_atlan_client.asset.save.side_effect = exception + elif custom_metadata_handling == CustomMetadataHandling.OVERWRITE: + mock_async_atlan_client.asset.save_replacing_cm.side_effect = exception + else: + mock_async_atlan_client.asset.save_merging_cm.side_effect = exception + + await sut.add(table_3) + + await sut.add(table_4) + + assert 1 == len(sut.failures) + failure = sut.failures[0] + assert [table_3, table_4] == failure.failed_assets + assert exception == failure.failure_reason + if custom_metadata_handling == CustomMetadataHandling.IGNORE: + mock_async_atlan_client.asset.save.assert_has_calls( + [ + call([table_1, table_2], replace_atlan_tags=False), + call([table_3, table_4], replace_atlan_tags=False), + ] + ) + elif custom_metadata_handling == CustomMetadataHandling.OVERWRITE: + mock_async_atlan_client.asset.save_replacing_cm.assert_has_calls( + [ + call([table_1, table_2], replace_atlan_tags=False), + call([table_3, table_4], replace_atlan_tags=False), + ] + ) + else: + mock_async_atlan_client.asset.save_merging_cm.assert_has_calls( + [ + call([table_1, table_2], replace_atlan_tags=False), + call([table_3, table_4], replace_atlan_tags=False), + ] + ) + + @pytest.mark.parametrize( + "custom_metadata_handling", + [ + (CustomMetadataHandling.IGNORE), + (CustomMetadataHandling.OVERWRITE), + (CustomMetadataHandling.MERGE), + ], + ) + @pytest.mark.asyncio + async def test_add_when_capture_failure_false_then_exception_raised( + self, custom_metadata_handling, mock_async_atlan_client + ): + exception = ErrorCode.INVALID_REQUEST_PASSTHROUGH.exception_with_parameters( + "bad", "stuff", "" + ) + + # Set up async mocks for save methods + if custom_metadata_handling == CustomMetadataHandling.IGNORE: + mock_async_atlan_client.asset.save = AsyncMock(side_effect=exception) + elif custom_metadata_handling == CustomMetadataHandling.OVERWRITE: + mock_async_atlan_client.asset.save_replacing_cm = AsyncMock( + side_effect=exception + ) + else: + mock_async_atlan_client.asset.save_merging_cm = AsyncMock( + side_effect=exception + ) + + # Mock FluentSearch.execute_async to return our mock results + mock_search_results = AsyncMock() + mock_search_results.__aiter__ = AsyncMock( + return_value=iter([]) + ) # Empty iterator for the async for loop + + with patch( + "pyatlan_v9.model.fluent_search.FluentSearch.execute_async", + new_callable=AsyncMock, + ) as mock_aexecute: + mock_aexecute.return_value = mock_search_results + + sut = AsyncBatch( + client=mock_async_atlan_client, + max_size=1, + capture_failures=False, + custom_metadata_handling=custom_metadata_handling, + ) + with pytest.raises(AtlanError): + await sut.add(Mock(Table)) + + assert 0 == len(sut.failures) + assert 0 == len(sut.created) + assert 0 == len(sut.updated) + + @patch.object(AtlasGlossaryTerm, "trim_to_required") + @patch.object(AtlasGlossaryTerm, "ref_by_guid") + @pytest.mark.asyncio + async def test_term_add( + self, mock_ref_by_guid, mock_trim_to_required, mock_async_atlan_client + ): + mutated_entities = Mock() + mock_response = Mock(spec=AssetMutationResponse) + term_1 = AtlasGlossaryTerm(guid="test-guid1", name="term1") + term_2 = AtlasGlossaryTerm(guid="test-guid2", name="term2") + created = [term_1, term_2] + mutated_entities.UPDATE = [] + mutated_entities.CREATE = created + mock_response.guid_assignments = {} + mock_response.attach_mock(mutated_entities, "mutated_entities") + # Set up async mocks - need to mock the FluentSearch.execute_async behavior + mock_search_results = AsyncMock() + mock_search_results.__aiter__ = AsyncMock( + return_value=iter([]) + ) # Empty iterator for the async for loop + + # Mock the asset client methods + mock_async_atlan_client.asset.search = AsyncMock(return_value=[term_1]) + mock_async_atlan_client.asset.save = AsyncMock(return_value=mock_response) + + # Mock FluentSearch.execute_async to return our mock results + with patch( + "pyatlan_v9.model.fluent_search.FluentSearch.execute_async", + new_callable=AsyncMock, + ) as mock_aexecute: + mock_aexecute.return_value = mock_search_results + + batch = AsyncBatch( + client=mock_async_atlan_client, + max_size=2, + track=True, + ) + await batch.add(term_1) + # Because the batch is not yet full + self.assert_asset_client_not_called(mock_async_atlan_client, batch) + await batch.add(term_2) + + assert len(created) == batch.num_created + mock_ref_by_guid.assert_has_calls([call(term_1.guid), call(term_2.guid)]) + mock_trim_to_required.assert_not_called() + + +class TestBulkRequest: + SEE_ALSO = "seeAlso" + REMOVE = "removeRelationshipAttributes" + APPEND = "appendRelationshipAttributes" + PREFERRED_TO_TERMS = "preferredToTerms" + + @pytest.fixture(scope="class") + def glossary(self): + return AtlasGlossary.creator(name=GLOSSARY_NAME) + + @pytest.fixture(scope="class") + def term1(self): + return AtlasGlossaryTerm.creator( + name=GLOSSARY_TERM_NAME, + anchor=AtlasGlossary.creator(name=GLOSSARY_NAME), + ) + + @pytest.fixture(scope="class") + def term2(self): + return AtlasGlossaryTerm(guid="term-2-guid", type_name="AtlasGlossaryTerm") + + @pytest.fixture(scope="class") + def term3(self): + return AtlasGlossaryTerm(guid="term-3-guid", type_name="AtlasGlossaryTerm") + + def to_json(self, request): + return request.to_dict()["entities"][0] + + def test_process_relationship_attributes(self, glossary, term1, term2, term3): + from pyatlan_v9.model.assets.gtc_related import ( + RelatedAtlasGlossary, + RelatedAtlasGlossaryTerm, + ) + from pyatlan_v9.model.assets.related_entity import ( + SaveSemantic as V9SaveSemantic, + ) + + # Test replace (list) + term1.see_also = [ + RelatedAtlasGlossaryTerm(guid=term2.guid), + RelatedAtlasGlossaryTerm(guid=term3.guid), + ] + request = BulkRequest(entities=[term1]) + request_json = self.to_json(request) + assert request_json + rel_attrs = request_json.get("relationshipAttributes", {}) + assert self.SEE_ALSO in rel_attrs + replace_attributes = rel_attrs[self.SEE_ALSO] + assert len(replace_attributes) == 2 + assert replace_attributes[0]["guid"] == term2.guid + assert replace_attributes[1]["guid"] == term3.guid + assert self.APPEND not in request_json + assert self.REMOVE not in request_json + + # Test replace and append (list) + term1.see_also = [ + RelatedAtlasGlossaryTerm(guid=term2.guid), + RelatedAtlasGlossaryTerm(guid=term3.guid, semantic=V9SaveSemantic.APPEND), + ] + request = BulkRequest(entities=[term1]) + request_json = self.to_json(request) + assert request_json + rel_attrs = request_json.get("relationshipAttributes", {}) + assert self.SEE_ALSO in rel_attrs + replace_attributes = rel_attrs[self.SEE_ALSO] + assert len(replace_attributes) == 1 + assert replace_attributes[0]["guid"] == term2.guid + assert self.APPEND in request_json + assert self.SEE_ALSO in request_json[self.APPEND] + append_attributes = request_json[self.APPEND][self.SEE_ALSO] + assert len(append_attributes) == 1 + assert append_attributes[0]["guid"] == term3.guid + assert self.REMOVE not in request_json + + # Test anchor goes into attributes (not relationshipAttributes) + term1.anchor = RelatedAtlasGlossary(guid=glossary.guid) + request = BulkRequest(entities=[term1]) + request_json = self.to_json(request) + assert request_json + attrs = request_json.get("attributes", {}) + assert "anchor" in attrs + assert attrs["anchor"]["guid"] == glossary.guid + + def test_asset_attribute_none_assignment(self): + table1 = Table.updater(name="test-table-1", qualified_name="test-qn-1") + table1.certificate_status = None + table1.certificate_status_message = None + request = BulkRequest(entities=[table1]) + request_json = self.to_json(request) + assert request_json + assert request_json["attributes"]["certificateStatus"] is None + assert request_json["attributes"]["certificateStatusMessage"] is None + + +@pytest.mark.asyncio +async def test_atlan_client_headers(client: AsyncAtlanClient): + VERSION = read_text("pyatlan", "version.txt").strip() + expected = Headers( + { + "User-Agent": f"Atlan-PythonSDK/{VERSION}", + "Accept-Encoding": "gzip, deflate", + "Accept": "*/*", + "Connection": "keep-alive", + "x-atlan-agent": "sdk", + "x-atlan-agent-id": "python", + "x-atlan-python-version": get_python_version(), + "x-atlan-client-origin": "product_sdk", + "x-atlan-client-type": "async", + } + ) + assert client._async_session is not None + assert expected == client._async_session.headers + + +@pytest.mark.asyncio +async def test_get_all_pagination(async_group_client, mock_async_api_caller): + mock_page_1 = [ + {"id": "1", "alias": "Group3"}, + {"id": "2", "alias": "Group4"}, + ] + mock_async_api_caller._call_api.side_effect = [ + {"records": mock_page_1}, + ] + + groups = await async_group_client.get_all(limit=2) + assert len(groups.current_page()) == 2 + assert groups.current_page()[0].id == "1" + assert groups.current_page()[1].id == "2" + assert mock_async_api_caller._call_api.call_count == 1 + mock_async_api_caller.reset_mock() + + +@pytest.mark.asyncio +async def test_get_all_empty_response_with_raw_records( + async_group_client, mock_async_api_caller +): + mock_page_1 = [] + mock_async_api_caller._call_api.side_effect = [ + {"records": mock_page_1}, + ] + + groups = await async_group_client.get_all() + assert len(groups.current_page()) == 0 + mock_async_api_caller.reset_mock() + + +@pytest.mark.asyncio +async def test_get_all_with_columns(async_group_client, mock_async_api_caller): + mock_page_1 = [ + {"id": "1", "alias": "Group1"}, + {"id": "2", "alias": "Group2"}, + ] + mock_async_api_caller._call_api.side_effect = [ + {"records": mock_page_1}, + ] + + columns = ["alias"] + groups = await async_group_client.get_all(limit=10, columns=columns) + + assert len(groups.current_page()) == 2 + assert groups.current_page()[0].id == "1" + assert groups.current_page()[0].alias == "Group1" + mock_async_api_caller._call_api.assert_called_once() + query_params = mock_async_api_caller._call_api.call_args.kwargs["query_params"] + assert query_params["columns"] == columns + mock_async_api_caller.reset_mock() + + +@pytest.mark.asyncio +async def test_get_all_sorting(async_group_client, mock_async_api_caller): + mock_page_1 = [ + {"id": "1", "alias": "Group1"}, + {"id": "2", "alias": "Group2"}, + ] + mock_async_api_caller._call_api.side_effect = [ + {"records": mock_page_1}, + ] + + groups = await async_group_client.get_all(limit=10, sort="alias") + + assert len(groups.current_page()) == 2 + assert groups.current_page()[0].id == "1" + assert groups.current_page()[0].alias == "Group1" + mock_async_api_caller._call_api.assert_called_once() + query_params = mock_async_api_caller._call_api.call_args.kwargs["query_params"] + assert query_params["sort"] == "alias" + mock_async_api_caller.reset_mock() + + +@pytest.mark.asyncio +async def test_get_by_guid_asset_not_found_fluent_search(mock_async_api_caller): + guid = "123" + asset_type = Table + + with patch( + "pyatlan_v9.model.fluent_search.FluentSearch.execute_async" + ) as mock_aexecute: + # Mock the async search results - current_page() should return list directly, not coroutine + mock_results = AsyncMock() + mock_results.current_page = Mock(return_value=[]) + mock_aexecute.return_value = mock_results + + client = AsyncAssetClient(client=mock_async_api_caller) + with pytest.raises( + ErrorCode.ASSET_NOT_FOUND_BY_GUID.exception_with_parameters(guid).__class__ + ): + await client.get_by_guid( + guid=guid, + asset_type=asset_type, + attributes=["name"], + related_attributes=["owner"], + ) + + mock_aexecute.assert_called_once() + + +@pytest.mark.asyncio +async def test_get_by_guid_type_mismatch_fluent_search(mock_async_api_caller): + guid = "123" + expected_asset_type = Table + returned_asset_type = View + + with patch( + "pyatlan_v9.model.fluent_search.FluentSearch.execute_async" + ) as mock_aexecute: + # Mock the async search results - current_page() should return list directly, not coroutine + mock_results = AsyncMock() + mock_results.current_page = Mock(return_value=[returned_asset_type()]) + mock_aexecute.return_value = mock_results + + client = AsyncAssetClient(client=mock_async_api_caller) + + with pytest.raises( + ErrorCode.ASSET_NOT_TYPE_REQUESTED.exception_with_parameters( + guid, expected_asset_type.__name__ + ).__class__ + ): + await client.get_by_guid( + guid=guid, + asset_type=expected_asset_type, + attributes=["name"], + related_attributes=["owner"], + ) + + mock_aexecute.assert_called_once() + + +@patch.object(AsyncAtlanClient, "_call_api") +@pytest.mark.asyncio +async def test_get_by_qualified_name_type_mismatch( + mock_call_api, mock_async_api_caller +): + qualified_name = "example_qualified_name" + expected_asset_type = Table + returned_asset_type = View + + with patch( + "pyatlan_v9.model.fluent_search.FluentSearch.execute_async" + ) as mock_aexecute: + # Mock the async search results - current_page() should return list directly, not coroutine + mock_results = AsyncMock() + mock_results.current_page = Mock(return_value=[returned_asset_type()]) + mock_aexecute.return_value = mock_results + + async_client = AsyncAtlanClient() + client = AsyncAssetClient(client=async_client) + + with pytest.raises( + ErrorCode.ASSET_NOT_FOUND_BY_NAME.exception_with_parameters( + expected_asset_type.__name__, qualified_name + ).__class__ + ): + await client.get_by_qualified_name( + qualified_name=qualified_name, + asset_type=expected_asset_type, + attributes=["name"], + related_attributes=["owner"], + ) + mock_aexecute.assert_called_once() + + +@pytest.mark.asyncio +async def test_get_by_qualified_name_asset_not_found(mock_async_api_caller): + qualified_name = "example_qualified_name" + asset_type = Table + + with patch( + "pyatlan_v9.model.fluent_search.FluentSearch.execute_async" + ) as mock_aexecute: + # Mock the async search results - current_page() should return list directly, not coroutine + mock_results = AsyncMock() + mock_results.current_page = Mock(return_value=[]) + mock_aexecute.return_value = mock_results + + client = AsyncAssetClient(client=mock_async_api_caller) + + with pytest.raises( + ErrorCode.ASSET_NOT_FOUND_BY_QN.exception_with_parameters( + qualified_name, asset_type.__name__ + ).__class__ + ): + await client.get_by_qualified_name( + qualified_name=qualified_name, + asset_type=asset_type, + attributes=["name"], + related_attributes=["owner"], + ) + + mock_aexecute.assert_called_once() diff --git a/tests_v9/unit/aio/test_client_proxy.py b/tests_v9/unit/aio/test_client_proxy.py new file mode 100644 index 000000000..d01cca984 --- /dev/null +++ b/tests_v9/unit/aio/test_client_proxy.py @@ -0,0 +1,230 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. +""" +Tests for AsyncAtlanClient proxy and SSL configuration. +""" + +from pathlib import Path +from unittest.mock import patch + +import httpx +import pytest + +from pyatlan_v9.client.aio.atlan import AsyncAtlanClient + + +@pytest.mark.parametrize( + "proxy_config", + [ + # No proxy configuration (default) + {}, + # Simple proxy + {"proxy": "http://127.0.0.1:8080"}, + # Proxy with SSL verification disabled + {"proxy": "http://127.0.0.1:8080", "verify": False}, + ], +) +def test_async_atlan_client_proxy_configurations(monkeypatch, proxy_config): + """Test various proxy and SSL configurations are properly initialized for async client.""" + monkeypatch.setenv("ATLAN_BASE_URL", "https://test.atlan.com") + monkeypatch.setenv("ATLAN_API_KEY", "test-api-key") + + # Clear any system proxy/SSL env vars that might interfere with tests + for var in [ + "HTTP_PROXY", + "http_proxy", + "HTTPS_PROXY", + "https_proxy", + "SSL_CERT_FILE", + "REQUESTS_CA_BUNDLE", + ]: + monkeypatch.delenv(var, raising=False) + + # Create async client with proxy settings + client = AsyncAtlanClient(**proxy_config) + + # Verify the async session was created + assert client._async_session is not None + assert isinstance(client._async_session, httpx.AsyncClient) + + # Verify proxy is set correctly if provided + if "proxy" in proxy_config: + assert client.proxy == proxy_config["proxy"] + else: + assert client.proxy is None + + # Verify verify is set correctly if provided + if "verify" in proxy_config: + assert client.verify == proxy_config["verify"] + else: + assert client.verify is True # Default value + + +def test_async_atlan_client_proxy_passed_to_transport(monkeypatch): + """Test that proxy and verify settings are correctly configured on the async client.""" + monkeypatch.setenv("ATLAN_BASE_URL", "https://test.atlan.com") + monkeypatch.setenv("ATLAN_API_KEY", "test-api-key") + + # Clear any proxy env vars that might interfere + for var in [ + "HTTP_PROXY", + "http_proxy", + "HTTPS_PROXY", + "https_proxy", + "SSL_CERT_FILE", + "REQUESTS_CA_BUNDLE", + ]: + monkeypatch.delenv(var, raising=False) + + # Test with proxy and verify=False (to disable SSL verification for testing) + proxy_url = "http://127.0.0.1:8080" + + client = AsyncAtlanClient(proxy=proxy_url, verify=False) + + # Verify the client has the correct settings + assert client.proxy == proxy_url + assert client.verify is False + + # Verify the transport was created + assert client._async_session is not None + assert hasattr(client._async_session, "_transport") + + +@pytest.mark.parametrize( + "env_vars, expected_proxy", + [ + # HTTP_PROXY environment variable + ( + {"HTTP_PROXY": "http://proxy.example.com:8080"}, + "http://proxy.example.com:8080", + ), + # http_proxy (lowercase) environment variable + ( + {"http_proxy": "http://proxy.example.com:8080"}, + "http://proxy.example.com:8080", + ), + # HTTPS_PROXY takes precedence + ( + { + "HTTP_PROXY": "http://proxy.example.com:8080", + "HTTPS_PROXY": "https://proxy.example.com:8443", + }, + "https://proxy.example.com:8443", + ), + ], +) +@patch("httpx.AsyncClient") +def test_async_atlan_client_proxy_from_environment_variables( + mock_async_httpx_client, + monkeypatch, + env_vars, + expected_proxy, +): + """Test that proxy settings are picked up from environment variables when not explicitly provided.""" + monkeypatch.setenv("ATLAN_BASE_URL", "https://test.atlan.com") + monkeypatch.setenv("ATLAN_API_KEY", "test-api-key") + + # Clear any system proxy/SSL env vars that might interfere with tests + for var in [ + "HTTP_PROXY", + "http_proxy", + "HTTPS_PROXY", + "https_proxy", + "SSL_CERT_FILE", + "REQUESTS_CA_BUNDLE", + ]: + monkeypatch.delenv(var, raising=False) + + # Set environment variables + for key, value in env_vars.items(): + monkeypatch.setenv(key, value) + + # Create async client without explicit proxy settings + client = AsyncAtlanClient() + + # Verify proxy configuration + assert client.proxy == expected_proxy + + +@patch("httpx_retries.transport.httpx.AsyncHTTPTransport") +@patch("httpx_retries.transport.httpx.HTTPTransport") +@patch("httpx.AsyncClient") +def test_async_atlan_client_proxy_with_ssl_cert_file_from_env( + mock_async_httpx_client, mock_http_transport, mock_async_http_transport, monkeypatch +): + """Test that SSL_CERT_FILE environment variable is picked up for async client.""" + monkeypatch.setenv("ATLAN_BASE_URL", "https://test.atlan.com") + monkeypatch.setenv("ATLAN_API_KEY", "test-api-key") + + # Use the fake certificate file + fake_cert_path = str( + Path(__file__).parent.parent.parent.parent + / "tests" + / "unit" + / "data" + / "fake_certificates" + / "fake-cert.pem" + ) + monkeypatch.setenv("SSL_CERT_FILE", fake_cert_path) + + client = AsyncAtlanClient() + + # Verify SSL cert path was picked up + assert client.verify == fake_cert_path + + +@patch("httpx_retries.transport.httpx.AsyncHTTPTransport") +@patch("httpx_retries.transport.httpx.HTTPTransport") +@patch("httpx.AsyncClient") +def test_async_atlan_client_explicit_args_override_env_vars( + mock_async_httpx_client, mock_http_transport, mock_async_http_transport, monkeypatch +): + """Test that explicitly provided arguments take precedence over environment variables for async client.""" + monkeypatch.setenv("ATLAN_BASE_URL", "https://test.atlan.com") + monkeypatch.setenv("ATLAN_API_KEY", "test-api-key") + + # Use the fake certificate file + fake_cert_path = str( + Path(__file__).parent.parent.parent.parent + / "tests" + / "unit" + / "data" + / "fake_certificates" + / "fake-cert.pem" + ) + + # Set environment variables + monkeypatch.setenv("HTTP_PROXY", "http://env-proxy:8080") + monkeypatch.setenv("SSL_CERT_FILE", fake_cert_path) + + # Explicitly provide different values + explicit_proxy = "http://explicit-proxy:9090" + explicit_verify = False + + client = AsyncAtlanClient(proxy=explicit_proxy, verify=explicit_verify) + + # Verify explicit values take precedence + assert client.proxy == explicit_proxy + assert client.verify == explicit_verify + + +def test_async_atlan_client_no_proxy_when_no_env_vars(monkeypatch): + """Test that no proxy is configured when neither args nor env vars are provided for async client.""" + monkeypatch.setenv("ATLAN_BASE_URL", "https://test.atlan.com") + monkeypatch.setenv("ATLAN_API_KEY", "test-api-key") + + # Ensure proxy env vars are not set + for env_var in [ + "HTTP_PROXY", + "http_proxy", + "HTTPS_PROXY", + "https_proxy", + "SSL_CERT_FILE", + "REQUESTS_CA_BUNDLE", + ]: + monkeypatch.delenv(env_var, raising=False) + + client = AsyncAtlanClient() + + assert client.proxy is None + assert client.verify is True # Default value diff --git a/tests_v9/unit/aio/test_connection_cache.py b/tests_v9/unit/aio/test_connection_cache.py new file mode 100644 index 000000000..956bcc133 --- /dev/null +++ b/tests_v9/unit/aio/test_connection_cache.py @@ -0,0 +1,266 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. +from unittest.mock import Mock, patch + +import pytest + +from pyatlan.cache.aio.connection_cache import AsyncConnectionCache +from pyatlan.cache.connection_cache import ConnectionName +from pyatlan_v9.client.aio.atlan import AsyncAtlanClient +from pyatlan_v9.errors import ErrorCode, InvalidRequestError, NotFoundError +from pyatlan_v9.model.assets import Connection + + +@pytest.fixture(autouse=True) +def set_env(monkeypatch): + monkeypatch.setenv("ATLAN_BASE_URL", "https://test.atlan.com") + monkeypatch.setenv("ATLAN_API_KEY", "test-api-key") + + +@pytest.fixture() +def async_client(): + return AsyncAtlanClient() + + +@pytest.fixture() +def mock_async_connection_cache(async_client, monkeypatch): + mock_cache = AsyncConnectionCache(async_client) + monkeypatch.setattr(AsyncAtlanClient, "connection_cache", mock_cache) + return mock_cache + + +@pytest.mark.asyncio +async def test_get_by_guid_with_not_found_error(async_client): + connection_cache = AsyncConnectionCache(async_client) + with pytest.raises(InvalidRequestError, match=ErrorCode.MISSING_ID.error_message): + await connection_cache.get_by_guid("") + + +@patch.object(AsyncConnectionCache, "lookup_by_guid") +@pytest.mark.asyncio +async def test_get_by_guid_with_no_invalid_request_error( + mock_lookup_by_guid, mock_async_connection_cache +): + test_guid = "test-guid-123" + with pytest.raises( + NotFoundError, + match=ErrorCode.ASSET_NOT_FOUND_BY_GUID.error_message.format(test_guid), + ): + await mock_async_connection_cache.get_by_guid(test_guid) + + +@pytest.mark.asyncio +async def test_get_by_qualified_name_with_not_found_error(mock_async_connection_cache): + with pytest.raises(InvalidRequestError, match=ErrorCode.MISSING_ID.error_message): + await mock_async_connection_cache.get_by_qualified_name("") + + +@patch.object(AsyncConnectionCache, "lookup_by_qualified_name") +@pytest.mark.asyncio +async def test_get_by_qualified_name_with_no_invalid_request_error( + mock_lookup_by_qualified_name, mock_async_connection_cache +): + test_qn = "default/snowflake/123456789" + test_connector = "snowflake" + with pytest.raises( + NotFoundError, + match=ErrorCode.ASSET_NOT_FOUND_BY_QN.error_message.format( + test_qn, test_connector + ), + ): + await mock_async_connection_cache.get_by_qualified_name(test_qn) + + +@pytest.mark.asyncio +async def test_get_by_name_with_not_found_error(mock_async_connection_cache): + with pytest.raises(InvalidRequestError, match=ErrorCode.MISSING_NAME.error_message): + await mock_async_connection_cache.get_by_name("") + + +@patch.object(AsyncConnectionCache, "lookup_by_name") +@pytest.mark.asyncio +async def test_get_by_name_with_no_invalid_request_error( + mock_lookup_by_name, mock_async_connection_cache +): + test_name = ConnectionName("snowflake/test") + with pytest.raises( + NotFoundError, + match=ErrorCode.ASSET_NOT_FOUND_BY_NAME.error_message.format( + ConnectionName._TYPE_NAME, + test_name, + ), + ): + await mock_async_connection_cache.get_by_name(test_name) + + +@patch.object(AsyncConnectionCache, "lookup_by_guid") +@pytest.mark.asyncio +async def test_get_by_guid(mock_lookup_by_guid, mock_async_connection_cache): + test_guid = "test-guid-123" + test_qn = "test-qualified-name" + conn = Connection() + conn.guid = test_guid + conn.qualified_name = test_qn + test_asset = conn + + mock_guid_to_asset = Mock() + mock_name_to_guid = Mock() + mock_qualified_name_to_guid = Mock() + + # 1 - Not found in the cache, triggers a lookup call + # 2, 3, 4 - Uses the cached entry from the map + mock_guid_to_asset.get.side_effect = [ + None, + test_asset, + test_asset, + test_asset, + ] + mock_name_to_guid.get.side_effect = [test_guid, test_guid, test_guid, test_guid] + mock_qualified_name_to_guid.get.side_effect = [ + test_guid, + test_guid, + test_guid, + test_guid, + ] + + # Assign mock caches to the return value of get_cache + mock_async_connection_cache.guid_to_asset = mock_guid_to_asset + mock_async_connection_cache.name_to_guid = mock_name_to_guid + mock_async_connection_cache.qualified_name_to_guid = mock_qualified_name_to_guid + + connection = await mock_async_connection_cache.get_by_guid(test_guid) + + # Multiple calls with the same GUID result in no additional API lookups + # as the object is already cached + connection = await mock_async_connection_cache.get_by_guid(test_guid) + connection = await mock_async_connection_cache.get_by_guid(test_guid) + + assert test_guid == connection.guid + assert test_qn == connection.qualified_name + + # The method is called four times, but the lookup is triggered only once + assert mock_guid_to_asset.get.call_count == 4 + mock_lookup_by_guid.assert_called_once() + + +@patch.object(AsyncConnectionCache, "lookup_by_guid") +@patch.object(AsyncConnectionCache, "lookup_by_qualified_name") +@pytest.mark.asyncio +async def test_get_by_qualified_name( + mock_lookup_by_qn, mock_lookup_by_guid, mock_async_connection_cache +): + test_guid = "test-guid-123" + test_qn = "test-qualified-name" + conn = Connection() + conn.guid = test_guid + conn.qualified_name = test_qn + test_asset = conn + + mock_guid_to_asset = Mock() + mock_name_to_guid = Mock() + mock_qualified_name_to_guid = Mock() + + # 1 - Not found in the cache, triggers a lookup call + # 2, 3, 4 - Uses the cached entry from the map + mock_qualified_name_to_guid.get.side_effect = [ + None, + test_guid, + test_guid, + test_guid, + ] + + # Other caches will be populated once + # the lookup call for get_by_qualified_name is made + mock_guid_to_asset.get.side_effect = [ + test_asset, + test_asset, + test_asset, + test_asset, + ] + mock_name_to_guid.get.side_effect = [test_guid, test_guid, test_guid, test_guid] + + mock_async_connection_cache.guid_to_asset = mock_guid_to_asset + mock_async_connection_cache.name_to_guid = mock_name_to_guid + mock_async_connection_cache.qualified_name_to_guid = mock_qualified_name_to_guid + + connection = await mock_async_connection_cache.get_by_qualified_name(test_qn) + + # Multiple calls with the same + # qualified name result in no additional API lookups + # as the object is already cached + connection = await mock_async_connection_cache.get_by_qualified_name(test_qn) + connection = await mock_async_connection_cache.get_by_qualified_name(test_qn) + + assert test_guid == connection.guid + assert test_qn == connection.qualified_name + + # The method is found four times + # but the lookup is triggered only once + assert mock_qualified_name_to_guid.get.call_count == 4 + mock_lookup_by_qn.assert_called_once() + + +@patch.object(AsyncConnectionCache, "lookup_by_guid") +@patch.object(AsyncConnectionCache, "lookup_by_name") +@pytest.mark.asyncio +async def test_get_by_name( + mock_lookup_by_name, mock_lookup_by_guid, mock_async_connection_cache +): + test_name = ConnectionName("snowflake/test") + test_guid = "test-guid-123" + test_qn = "test-qualified-name" + conn = Connection() + conn.guid = test_guid + conn.qualified_name = test_qn + test_asset = conn + + mock_guid_to_asset = Mock() + mock_name_to_guid = Mock() + mock_qualified_name_to_guid = Mock() + + # 1 - Not found in the cache, triggers a lookup call + # 2, 3, 4 - Uses the cached entry from the map + mock_name_to_guid.get.side_effect = [ + None, + test_guid, + test_guid, + test_guid, + ] + + # Other caches will be populated once + # the lookup call for get_by_qualified_name is made + mock_guid_to_asset.get.side_effect = [ + test_asset, + test_asset, + test_asset, + test_asset, + ] + mock_qualified_name_to_guid.get.side_effect = [ + test_guid, + test_guid, + test_guid, + test_guid, + ] + + mock_async_connection_cache.guid_to_asset = mock_guid_to_asset + mock_async_connection_cache.name_to_guid = mock_name_to_guid + mock_async_connection_cache.qualified_name_to_guid = mock_qualified_name_to_guid + + connection = await mock_async_connection_cache.get_by_name(test_name) + + # Multiple calls with the same + # qualified name result in no additional API lookups + # as the object is already cached + connection = await mock_async_connection_cache.get_by_name(test_name) + connection = await mock_async_connection_cache.get_by_name(test_name) + + assert test_guid == connection.guid + assert test_qn == connection.qualified_name + + # The method is called four times + # but the lookup is triggered only once + assert mock_name_to_guid.get.call_count == 4 + mock_lookup_by_name.assert_called_once() + + # No call to guid lookup since the object is already in the cache + assert mock_lookup_by_guid.call_count == 0 diff --git a/tests_v9/unit/aio/test_credential_client.py b/tests_v9/unit/aio/test_credential_client.py new file mode 100644 index 000000000..251acd431 --- /dev/null +++ b/tests_v9/unit/aio/test_credential_client.py @@ -0,0 +1,431 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2022 Atlan Pte. Ltd. +from unittest.mock import Mock + +import msgspec +import pytest + +from pyatlan.client.common import AsyncApiCaller +from pyatlan_v9.client.aio.credential import ( + V9AsyncCredentialClient as AsyncCredentialClient, +) +from pyatlan_v9.errors import InvalidRequestError +from pyatlan_v9.model.credential import Credential, CredentialResponse + +TEST_MISSING_TOKEN_ID = ( + "ATLAN-PYTHON-400-032 No ID was provided when attempting to update the API token." +) +TEST_INVALID_CREDENTIALS = ( + "ATLAN-PYTHON-400-054 Credentials provided did not work: failed" +) +TEST_INVALID_GUID_GET_VALIDATION_ERR = ( + "1 validation error for Get\nguid\n str type expected" +) +TEST_INVALID_GUID_PURGE_BY_GUID_VALIDATION_ERR = ( + "1 validation error for PurgeByGuid\nguid\n str type expected" +) +TEST_INVALID_CRED_TEST_VALIDATION_ERR = ( + "1 validation error for Test\ncredential\n instance of Credential expected" +) +TEST_INVALID_CRED_TEST_UPDATE_VALIDATION_ERR = "1 validation error for TestAndUpdate\ncredential\n instance of Credential expected" +TEST_INVALID_CRED_CREATOR_VALIDATION_ERR = ( + "1 validation error for Creator\ncredential\n instance of Credential expected" +) +TEST_INVALID_API_CALLER_PARAMETER_TYPE = ( + "ATLAN-PYTHON-400-048 Invalid parameter type for client should be AsyncApiCaller" +) + + +def _to_dict(obj): + """Convert msgspec struct or Pydantic model to dict, preserving alias keys.""" + if isinstance(obj, msgspec.Struct): + return msgspec.to_builtins(obj) + elif hasattr(obj, "dict"): + return obj.dict() + else: + return obj + + +@pytest.fixture() +def mock_api_caller(): + return Mock(spec=AsyncApiCaller) + + +@pytest.fixture() +def client(mock_api_caller) -> AsyncCredentialClient: + return AsyncCredentialClient(mock_api_caller) + + +@pytest.fixture() +def credential_response() -> CredentialResponse: + return CredentialResponse( # type: ignore[call-arg] + id="test-id", + version="1.2.3", + is_active=True, + created_at=1704186290006, + updated_at=1704218661848, + created_by="test-acc", + tenant_id="default", + name="test-name", + description="test-desc", + connector_config_name="test-ccn", + connector="test-conn", + connector_type="test-ct", + auth_type="test-at", + host="test-host", + port=123, + metadata=None, + level=None, + connection=None, + username="test-username", + extras={"some": "value"}, + ) + + +def _assert_cred_response(cred: Credential, cred_response: CredentialResponse): + assert cred.id == cred_response.id + assert cred.name == cred_response.name + assert cred.port == cred_response.port + assert cred.auth_type == cred_response.auth_type + assert cred.connector_type == cred_response.connector_type + assert cred.connector_config_name == cred_response.connector_config_name + assert cred.username == cred_response.username + assert cred.extras == cred_response.extras + + +@pytest.mark.parametrize("test_api_caller", ["abc", None]) +@pytest.mark.asyncio +async def test_init_when_wrong_class_raises_exception(test_api_caller): + with pytest.raises( + InvalidRequestError, + match=TEST_INVALID_API_CALLER_PARAMETER_TYPE, + ): + AsyncCredentialClient(test_api_caller) + + +@pytest.mark.parametrize("test_guid", [[123], set(), dict()]) +@pytest.mark.asyncio +async def test_cred_get_wrong_params_raises_validation_error( + test_guid, client: AsyncCredentialClient +): + with pytest.raises(ValueError) as err: + await client.get(guid=test_guid) + assert TEST_INVALID_GUID_GET_VALIDATION_ERR == str(err.value) + + +@pytest.mark.parametrize("test_credentials", ["invalid_cred", 123]) +@pytest.mark.asyncio +async def test_cred_test_wrong_params_raises_validation_error( + test_credentials, client: AsyncCredentialClient +): + with pytest.raises(ValueError) as err: + await client.test(credential=test_credentials) + assert TEST_INVALID_CRED_TEST_VALIDATION_ERR == str(err.value) + + +@pytest.mark.parametrize("test_credentials", ["invalid_cred", 123]) +@pytest.mark.asyncio +async def test_cred_test_and_update_wrong_params_raises_validation_error( + test_credentials, client: AsyncCredentialClient +): + with pytest.raises(ValueError) as err: + await client.test_and_update(credential=test_credentials) + assert TEST_INVALID_CRED_TEST_UPDATE_VALIDATION_ERR == str(err.value) + + +@pytest.mark.parametrize( + "test_credentials, test_response", + [ + [Credential(), "successful"], + [Credential(id="test-id"), "failed"], + ], +) +@pytest.mark.asyncio +async def test_cred_test_update_raises_invalid_request_error( + test_credentials, + test_response, + mock_api_caller, + client: AsyncCredentialClient, +): + mock_api_caller._call_api.return_value = {"message": test_response} + with pytest.raises(InvalidRequestError) as err: + await client.test_and_update(credential=test_credentials) + if test_response == "successful": + assert TEST_MISSING_TOKEN_ID in str(err.value) + else: + assert TEST_INVALID_CREDENTIALS in str(err.value) + + +@pytest.mark.asyncio +async def test_cred_get_when_given_guid( + client: AsyncCredentialClient, + mock_api_caller, + credential_response: CredentialResponse, +): + mock_api_caller._call_api.return_value = _to_dict(credential_response) + result = await client.get(guid="test-id") + assert result.id == credential_response.id + assert result.name == credential_response.name + assert result.host == credential_response.host + assert result.port == credential_response.port + cred = (await client.get(guid="test-id")).to_credential() + assert type(cred).__name__ == "Credential" + _assert_cred_response(cred, credential_response) + + +@pytest.mark.asyncio +async def test_cred_get_when_given_wrong_guid( + client: AsyncCredentialClient, + mock_api_caller, + credential_response: CredentialResponse, +): + mock_api_caller._call_api.return_value = None + assert await client.get(guid="test-wrong-id") is None + + +@pytest.mark.asyncio +async def test_cred_test_when_given_cred( + client: AsyncCredentialClient, + mock_api_caller, + credential_response: CredentialResponse, +): + mock_api_caller._call_api.return_value = {"message": "successful"} + cred_test_response = await client.test(credential=Credential()) + assert hasattr(cred_test_response, "message") + assert cred_test_response.message == "successful" + assert cred_test_response.code is None + assert cred_test_response.error is None + assert cred_test_response.info is None + assert cred_test_response.request_id is None + + +@pytest.mark.asyncio +async def test_cred_test_update_when_given_cred( + client: AsyncCredentialClient, + mock_api_caller, + credential_response: CredentialResponse, +): + mock_api_caller._call_api.side_effect = [ + {"message": "successful"}, + _to_dict(credential_response), + ] + cred_response = await client.test_and_update( + credential=Credential(id=credential_response.id) + ) + assert hasattr(cred_response, "id") + cred = cred_response.to_credential() + _assert_cred_response(cred, credential_response) + + +@pytest.mark.parametrize( + "test_filter, test_limit, test_offset, test_response", + [ + (None, None, None, {"records": [{"id": "cred1"}, {"id": "cred2"}]}), + ({"name": "test"}, 5, 0, {"records": [{"id": "cred3"}]}), + ({"invalid": "field"}, 10, 0, {"records": []}), + ], +) +@pytest.mark.asyncio +async def test_cred_get_all_success( + test_filter, test_limit, test_offset, test_response, mock_api_caller +): + mock_api_caller._call_api.return_value = test_response + client = AsyncCredentialClient(mock_api_caller) + + result = await client.get_all( + filter=test_filter, limit=test_limit, offset=test_offset + ) + + assert hasattr(result, "records") + assert len(result.records) == len(test_response["records"]) + for record, expected in zip(result.records, test_response["records"]): + assert record.id == expected["id"] + + +@pytest.mark.asyncio +async def test_cred_get_all_empty_response(mock_api_caller): + mock_api_caller._call_api.return_value = {"records": []} + client = AsyncCredentialClient(mock_api_caller) + + result = await client.get_all() + + assert hasattr(result, "records") + assert len(result.records) == 0 + + +@pytest.mark.asyncio +async def test_cred_get_all_invalid_response(mock_api_caller): + mock_api_caller._call_api.return_value = {} + client = AsyncCredentialClient(mock_api_caller) + + with pytest.raises(Exception, match="No records found in response"): + await client.get_all() + + +@pytest.mark.parametrize( + "test_filter, test_limit, test_offset", + [ + ("invalid_filter", None, None), + (None, "invalid_limit", None), + (None, None, "invalid_offset"), + ], +) +@pytest.mark.asyncio +async def test_cred_get_all_invalid_params_raises_validation_error( + test_filter, test_limit, test_offset, client: AsyncCredentialClient +): + with pytest.raises(ValueError): + await client.get_all(filter=test_filter, limit=test_limit, offset=test_offset) + + +@pytest.mark.asyncio +async def test_cred_get_all_timeout(mock_api_caller): + mock_api_caller._call_api.side_effect = TimeoutError("Request timed out") + client = AsyncCredentialClient(mock_api_caller) + + with pytest.raises(TimeoutError, match="Request timed out"): + await client.get_all() + + +@pytest.mark.asyncio +async def test_cred_get_all_partial_response(mock_api_caller): + mock_api_caller._call_api.return_value = { + "records": [ + { + "id": "cred1", + "name": "Test Credential", + "level": "user", + "connection": "default/bigquery/1697545730", + } + ] + } + client = AsyncCredentialClient(mock_api_caller) + + result = await client.get_all() + + assert hasattr(result, "records") + assert result.records[0].host is None + assert result.records[0].id == "cred1" + assert result.records[0].name == "Test Credential" + assert result.records[0].level == "user" + assert result.records[0].connection == "default/bigquery/1697545730" + + +@pytest.mark.asyncio +async def test_cred_get_all_invalid_filter_type(mock_api_caller): + client = AsyncCredentialClient(mock_api_caller) + + with pytest.raises(ValueError, match="value is not a valid dict"): + await client.get_all(filter="invalid_filter") + + +@pytest.mark.asyncio +async def test_cred_get_all_no_results(mock_api_caller): + mock_api_caller._call_api.return_value = {"records": None} + client = AsyncCredentialClient(mock_api_caller) + + result = await client.get_all(filter={"name": "nonexistent"}) + + assert hasattr(result, "records") + assert result.records == [] + assert len(result.records) == 0 + + +@pytest.mark.parametrize("create_credentials", ["invalid_cred", 123]) +@pytest.mark.asyncio +async def test_cred_creator_wrong_params_raises_validation_error( + create_credentials, client: AsyncCredentialClient +): + with pytest.raises(ValueError) as err: + await client.creator(credential=create_credentials) + assert TEST_INVALID_CRED_CREATOR_VALIDATION_ERR == str(err.value) + + +@pytest.mark.parametrize( + "credential_data", + [ + ( + Credential( + name="test-name", + description="test-desc", + connector_config_name="test-ccn", + connector="test-conn", + connector_type="test-ct", + auth_type="test-at", + host="test-host", + port=123, + username="test-username", + extras={"some": "value"}, + ) + ), + ], +) +@pytest.mark.asyncio +async def test_creator_success( + credential_data, + credential_response: CredentialResponse, + mock_api_caller, + client: AsyncCredentialClient, +): + mock_api_caller._call_api.return_value = _to_dict(credential_response) + client = AsyncCredentialClient(mock_api_caller) + + response = await client.creator(credential=credential_data) + + assert hasattr(response, "id") and hasattr(response, "name") + assert credential_data.name == response.name + assert credential_data.description == response.description + assert credential_data.port == response.port + assert credential_data.auth_type == response.auth_type + assert credential_data.connector_type == response.connector_type + assert credential_data.connector_config_name == response.connector_config_name + assert credential_data.username == response.username + assert credential_data.extras == response.extras + assert response.level is None + + +@pytest.mark.parametrize( + "credential_data", + [ + ( + Credential( + name="test-name", + description="test-desc", + connector_config_name="test-ccn", + connector="test-conn", + connector_type="test-ct", + auth_type="test-at", + host="test-host", + port=123, + username="test-user", + password="test-password", + extras={"some": "value"}, + ) + ), + ], +) +@pytest.mark.asyncio +async def test_cred_creator_with_test_false_with_username_password( + credential_data, client: AsyncCredentialClient +): + with pytest.raises(Exception, match="ATLAN-PYTHON-400-071"): + await client.creator(credential=credential_data, test=False) + + +@pytest.mark.parametrize("test_guid", [[123], set(), dict()]) +@pytest.mark.asyncio +async def test_cred_purge_by_guid_wrong_params_raises_validation_error( + test_guid, client: AsyncCredentialClient +): + with pytest.raises(ValueError) as err: + await client.purge_by_guid(guid=test_guid) + assert TEST_INVALID_GUID_PURGE_BY_GUID_VALIDATION_ERR == str(err.value) + + +@pytest.mark.asyncio +async def test_cred_purge_by_guid_when_given_guid( + client: AsyncCredentialClient, + mock_api_caller, +): + mock_api_caller._call_api.return_value = None + assert await client.purge_by_guid(guid="test-id") is None diff --git a/tests_v9/unit/aio/test_custom_metadata.py b/tests_v9/unit/aio/test_custom_metadata.py new file mode 100644 index 000000000..2e2e058e8 --- /dev/null +++ b/tests_v9/unit/aio/test_custom_metadata.py @@ -0,0 +1,563 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. +from unittest.mock import AsyncMock, patch + +import pytest + +from pyatlan_v9.client.aio.atlan import AsyncAtlanClient +from pyatlan_v9.errors import AtlanError, ErrorCode, NotFoundError +from pyatlan_v9.model.aio.custom_metadata import ( + AsyncCustomMetadataDict, + AsyncCustomMetadataProxy, + AsyncCustomMetadataRequest, +) +from pyatlan_v9.model.assets import Asset +from pyatlan_v9.model.enums import AtlanCustomAttributePrimitiveType +from pyatlan_v9.model.typedef import AttributeDef + +ATTR_LAST_NAME = "Last Name" +ATTR_LAST_NAME_ID = "2" +ATTR_FIRST_NAME = "First Name" +ATTR_FIRST_NAME_ID = "1" +CM_ID = "123" +CM_NAME = "Something" +CM_ATTRIBUTES = {ATTR_FIRST_NAME_ID: ATTR_FIRST_NAME, ATTR_LAST_NAME_ID: ATTR_LAST_NAME} +META_DATA = {CM_ID: CM_ATTRIBUTES} + + +@pytest.fixture(autouse=True) +def set_env(monkeypatch): + monkeypatch.setenv("ATLAN_BASE_URL", "https://test.atlan.com") + monkeypatch.setenv("ATLAN_API_KEY", "test-api-key") + + +@pytest.fixture() +def client(): + return AsyncAtlanClient() + + +def get_attr_id_for_name(*args, **kwargs): + return ATTR_FIRST_NAME_ID if args[1] == ATTR_FIRST_NAME else ATTR_LAST_NAME_ID + + +def get_attr_name_for_id(*args, **kwargs): + return ATTR_FIRST_NAME if args[1] == ATTR_FIRST_NAME_ID else ATTR_LAST_NAME + + +class TestAsyncCustomMetadataDict: + @pytest.fixture() + async def sut(self, mock_async_custom_metadata_cache, client: AsyncAtlanClient): + mock_async_custom_metadata_cache.get_id_for_name = AsyncMock(return_value=CM_ID) + mock_async_custom_metadata_cache.get_attr_map_for_id = AsyncMock( + return_value=CM_ATTRIBUTES + ) + mock_async_custom_metadata_cache.is_attr_archived = AsyncMock( + return_value=False + ) + + return await AsyncCustomMetadataDict.creator(client=client, name=CM_NAME) + + @pytest.mark.asyncio + async def test_init_when_invalid_name_throws_not_found_error( + self, mock_async_custom_metadata_cache, client: AsyncAtlanClient + ): + mock_async_custom_metadata_cache.get_id_for_name = AsyncMock( + side_effect=ErrorCode.ASSET_NOT_FOUND_BY_GUID.exception_with_parameters( + "123" + ) + ) + with pytest.raises(NotFoundError): + await AsyncCustomMetadataDict.creator(client=client, name=CM_NAME) + mock_async_custom_metadata_cache.get_id_for_name.assert_called_with(CM_NAME) + + @pytest.mark.asyncio + async def test_modified_after_init_returns_false(self, sut): + assert sut.modified is False + + @pytest.mark.asyncio + async def test_init_when_called_with_valid_name_initializes_names(self, sut): + assert sut.attribute_names == set(CM_ATTRIBUTES.values()) + + @pytest.mark.asyncio + async def test_can_get_set_items(self, sut): + sut[ATTR_FIRST_NAME] = "123" + assert sut[ATTR_FIRST_NAME] == "123" + assert sut.modified is True + + @pytest.mark.asyncio + async def test_get_item_with_invalid_name_raises_key_error(self, sut): + with pytest.raises(KeyError): + _ = sut["Invalid Name"] + + @pytest.mark.asyncio + async def test_set_item_with_invalid_name_raises_key_error(self, sut): + with pytest.raises(KeyError): + sut["Invalid Name"] = "123" + + @pytest.mark.parametrize("attribute_name", [ATTR_FIRST_NAME, ATTR_LAST_NAME]) + @pytest.mark.asyncio + async def test_clear_all_set_all_attributes_to_none(self, sut, attribute_name): + sut[attribute_name] = "123" + sut.clear_all() + assert sut[attribute_name] is None + + @pytest.mark.parametrize( + "attribute_name,other_attr", + [(ATTR_FIRST_NAME, ATTR_LAST_NAME), (ATTR_LAST_NAME, ATTR_FIRST_NAME)], + ) + @pytest.mark.asyncio + async def test_clear_unset_sets_unset_to_none( + self, sut, attribute_name, other_attr + ): + sut[attribute_name] = "123" + sut.clear_unset() + assert sut[attribute_name] == "123" + assert sut[other_attr] is None + + @pytest.mark.asyncio + async def test_get_item_using_name_that_has_not_been_set_returns_none(self, sut): + assert sut[ATTR_FIRST_NAME] is None + + @pytest.mark.asyncio + async def test_business_attributes_when_no_changes(self, sut): + business_attrs = await sut.business_attributes() + assert business_attrs == {} + + @pytest.mark.asyncio + async def test_business_attributes_with_data( + self, mock_async_custom_metadata_cache, sut + ): + mock_async_custom_metadata_cache.get_attr_id_for_name = AsyncMock( + return_value=ATTR_FIRST_NAME_ID + ) + sut[ATTR_FIRST_NAME] = "123" + business_attrs = await sut.business_attributes() + assert business_attrs == {ATTR_FIRST_NAME_ID: "123"} + + @pytest.mark.parametrize("attribute_name", [ATTR_FIRST_NAME, ATTR_LAST_NAME]) + @pytest.mark.asyncio + async def test_is_unset_initially_returns_false(self, sut, attribute_name): + assert not sut.is_set(attribute_name) + + @pytest.mark.parametrize("attribute_name", [ATTR_FIRST_NAME, ATTR_LAST_NAME]) + @pytest.mark.asyncio + async def test_unset_after_update_returns_true(self, sut, attribute_name): + sut[attribute_name] = "123" + assert sut.is_set(attribute_name) + + @pytest.mark.asyncio + async def test_get_deleted_sentinel(self): + sentinel = AsyncCustomMetadataDict.get_deleted_sentinel() + assert sentinel._name == "(DELETED)" + + +class TestAsyncCustomMetadataProxy: + @pytest.mark.asyncio + async def test_when_intialialized_with_no_business_attributes_then_modified_is_false( + self, client: AsyncAtlanClient + ): + proxy = AsyncCustomMetadataProxy(client=client, business_attributes=None) + assert not proxy.modified + + @pytest.mark.asyncio + async def test_when_intialialized_with_no_business_attributes_then_business_attributes_returns_none( + self, client: AsyncAtlanClient + ): + proxy = AsyncCustomMetadataProxy(client=client, business_attributes=None) + business_attrs = await proxy.business_attributes() + assert business_attrs is None + + @pytest.mark.asyncio + async def test_set_custom_metadata( + self, mock_async_custom_metadata_cache, client: AsyncAtlanClient + ): + mock_async_custom_metadata_cache.get_id_for_name = AsyncMock(return_value=CM_ID) + mock_async_custom_metadata_cache.get_attr_map_for_id = AsyncMock( + return_value=CM_ATTRIBUTES + ) + mock_async_custom_metadata_cache.is_attr_archived = AsyncMock( + return_value=False + ) + + proxy = AsyncCustomMetadataProxy(client=client, business_attributes=None) + + custom_metadata_dict = await AsyncCustomMetadataDict.creator( + client=client, name=CM_NAME + ) + await proxy.set_custom_metadata(custom_metadata_dict) + + assert proxy.modified + + @pytest.mark.asyncio + async def test_after_modifying_metadata_modified_is_true( + self, mock_async_custom_metadata_cache, client: AsyncAtlanClient + ): + mock_async_custom_metadata_cache.get_id_for_name = AsyncMock(return_value=CM_ID) + mock_async_custom_metadata_cache.get_attr_map_for_id = AsyncMock( + return_value=CM_ATTRIBUTES + ) + mock_async_custom_metadata_cache.is_attr_archived = AsyncMock( + return_value=False + ) + + proxy = AsyncCustomMetadataProxy(client=client, business_attributes=None) + + custom_metadata = await proxy.get_custom_metadata(CM_NAME) + custom_metadata[ATTR_FIRST_NAME] = "Jane" + + assert proxy.modified + + @pytest.mark.asyncio + async def test_when_not_modified_returns_business_attributes( + self, mock_async_custom_metadata_cache, client: AsyncAtlanClient + ): + business_attrs_input = {CM_ID: {ATTR_FIRST_NAME_ID: "Jane"}} + mock_async_custom_metadata_cache.get_id_for_name = AsyncMock(return_value=CM_ID) + mock_async_custom_metadata_cache.get_name_for_id = AsyncMock( + return_value=CM_NAME + ) + mock_async_custom_metadata_cache.get_attr_map_for_id = AsyncMock( + return_value=CM_ATTRIBUTES + ) + mock_async_custom_metadata_cache.get_attr_name_for_id = AsyncMock( + return_value=ATTR_FIRST_NAME + ) + mock_async_custom_metadata_cache.is_attr_archived = AsyncMock( + return_value=False + ) + + proxy = AsyncCustomMetadataProxy( + client=client, business_attributes=business_attrs_input + ) + business_attrs = await proxy.business_attributes() + + assert business_attrs == business_attrs_input + + @pytest.mark.asyncio + async def test_when_modified_returns_updated_business_attributes( + self, mock_async_custom_metadata_cache, client: AsyncAtlanClient + ): + mock_async_custom_metadata_cache.get_id_for_name = AsyncMock(return_value=CM_ID) + mock_async_custom_metadata_cache.get_attr_map_for_id = AsyncMock( + return_value=CM_ATTRIBUTES + ) + mock_async_custom_metadata_cache.is_attr_archived = AsyncMock( + return_value=False + ) + mock_async_custom_metadata_cache.get_attr_id_for_name = AsyncMock( + return_value=ATTR_FIRST_NAME_ID + ) + + proxy = AsyncCustomMetadataProxy(client=client, business_attributes=None) + + custom_metadata = await proxy.get_custom_metadata(CM_NAME) + custom_metadata[ATTR_FIRST_NAME] = "Jane" + + business_attrs = await proxy.business_attributes() + assert business_attrs == {CM_ID: {ATTR_FIRST_NAME_ID: "Jane"}} + + @pytest.mark.asyncio + async def test_when_invalid_metadata_set_then_delete_sentinel_is_used( + self, mock_async_custom_metadata_cache, client: AsyncAtlanClient + ): + mock_async_custom_metadata_cache.get_name_for_id = AsyncMock( + side_effect=ErrorCode.CM_NOT_FOUND_BY_ID.exception_with_parameters( + "invalid-id" + ) + ) + + business_attrs_input = {"invalid-id": {ATTR_FIRST_NAME_ID: "Jane"}} + proxy = AsyncCustomMetadataProxy( + client=client, business_attributes=business_attrs_input + ) + await proxy._initialize_metadata() + + assert proxy._metadata is not None + assert "(DELETED)" in proxy._metadata + + @pytest.mark.asyncio + async def test_when_property_is_archived( + self, mock_async_custom_metadata_cache, client: AsyncAtlanClient + ): + mock_async_custom_metadata_cache.get_id_for_name = AsyncMock(return_value=CM_ID) + mock_async_custom_metadata_cache.get_name_for_id = AsyncMock( + return_value=CM_NAME + ) + mock_async_custom_metadata_cache.get_attr_map_for_id = AsyncMock( + return_value=CM_ATTRIBUTES + ) + mock_async_custom_metadata_cache.get_attr_name_for_id = AsyncMock( + return_value=ATTR_FIRST_NAME + ) + mock_async_custom_metadata_cache.is_attr_archived = AsyncMock( + return_value=True + ) # Archived + + business_attrs_input = {CM_ID: {ATTR_FIRST_NAME_ID: "Jane"}} + proxy = AsyncCustomMetadataProxy( + client=client, business_attributes=business_attrs_input + ) + await proxy._initialize_metadata() + + # Should not include archived attributes in the names set + assert proxy._metadata is not None + custom_metadata = proxy._metadata[CM_NAME] + assert ATTR_FIRST_NAME not in custom_metadata.attribute_names + + +class TestAsyncCustomMetadataRequest: + @pytest.mark.asyncio + async def test_create( + self, mock_async_custom_metadata_cache, client: AsyncAtlanClient + ): + mock_async_custom_metadata_cache.get_id_for_name = AsyncMock(return_value=CM_ID) + mock_async_custom_metadata_cache.get_attr_map_for_id = AsyncMock( + return_value=CM_ATTRIBUTES + ) + mock_async_custom_metadata_cache.is_attr_archived = AsyncMock( + return_value=False + ) + mock_async_custom_metadata_cache.get_attr_id_for_name = AsyncMock( + return_value=ATTR_FIRST_NAME_ID + ) + + custom_metadata_dict = await AsyncCustomMetadataDict.creator( + client=client, name=CM_NAME + ) + custom_metadata_dict[ATTR_FIRST_NAME] = "Jane" + + request = await AsyncCustomMetadataRequest.create(custom_metadata_dict) + + assert request.to_dict() == {ATTR_FIRST_NAME_ID: "Jane"} + assert request.custom_metadata_set_id == CM_ID + + +class TestAsyncReferenceableCustomMetadata: + """Test async custom metadata methods on Referenceable (via Asset)""" + + @pytest.mark.asyncio + async def test_get_custom_metadata_async( + self, mock_async_custom_metadata_cache, client: AsyncAtlanClient + ): + # Configure the mock + mock_async_custom_metadata_cache.get_id_for_name = AsyncMock(return_value=CM_ID) + mock_async_custom_metadata_cache.get_name_for_id = AsyncMock( + return_value=CM_NAME + ) + mock_async_custom_metadata_cache.get_attr_map_for_id = AsyncMock( + return_value=CM_ATTRIBUTES + ) + mock_async_custom_metadata_cache.get_attr_name_for_id = AsyncMock( + return_value=ATTR_FIRST_NAME + ) + mock_async_custom_metadata_cache.is_attr_archived = AsyncMock( + return_value=False + ) + + # Create an asset with business attributes + asset = Asset() + asset.business_attributes = {CM_ID: {ATTR_FIRST_NAME_ID: "Jane"}} + + # Test get_custom_metadata_async + custom_metadata = await asset.get_custom_metadata_async(client, CM_NAME) + + assert isinstance(custom_metadata, AsyncCustomMetadataDict) + assert custom_metadata.attribute_names == {ATTR_FIRST_NAME, ATTR_LAST_NAME} + + @pytest.mark.asyncio + async def test_set_custom_metadata_async( + self, mock_async_custom_metadata_cache, client: AsyncAtlanClient + ): + # Configure the mock + mock_async_custom_metadata_cache.get_id_for_name = AsyncMock(return_value=CM_ID) + mock_async_custom_metadata_cache.get_attr_map_for_id = AsyncMock( + return_value=CM_ATTRIBUTES + ) + mock_async_custom_metadata_cache.is_attr_archived = AsyncMock( + return_value=False + ) + + # Create an asset + asset = Asset() + asset.business_attributes = None + + # Create custom metadata + custom_metadata = await AsyncCustomMetadataDict.creator( + client=client, name=CM_NAME + ) + custom_metadata[ATTR_FIRST_NAME] = "John" + + # Test set_custom_metadata_async + await asset.set_custom_metadata_async(client, custom_metadata) + + assert asset._async_metadata_proxy is not None + assert asset._async_metadata_proxy.modified + + @pytest.mark.asyncio + async def test_flush_custom_metadata_async( + self, mock_async_custom_metadata_cache, client: AsyncAtlanClient + ): + # Configure the mock + mock_async_custom_metadata_cache.get_id_for_name = AsyncMock(return_value=CM_ID) + mock_async_custom_metadata_cache.get_attr_map_for_id = AsyncMock( + return_value=CM_ATTRIBUTES + ) + mock_async_custom_metadata_cache.is_attr_archived = AsyncMock( + return_value=False + ) + mock_async_custom_metadata_cache.get_attr_id_for_name = AsyncMock( + return_value=ATTR_FIRST_NAME_ID + ) + + # Create an asset + asset = Asset() + asset.business_attributes = None + + # Create and set custom metadata + custom_metadata = await AsyncCustomMetadataDict.creator( + client=client, name=CM_NAME + ) + custom_metadata[ATTR_FIRST_NAME] = "John" + await asset.set_custom_metadata_async(client, custom_metadata) + + # Test flush_custom_metadata_async + await asset.flush_custom_metadata_async(client) + + # Verify business_attributes was updated + assert asset.business_attributes is not None + assert CM_ID in asset.business_attributes + assert asset.business_attributes[CM_ID][ATTR_FIRST_NAME_ID] == "John" + + @pytest.mark.asyncio + async def test_async_metadata_proxy_independence( + self, mock_async_custom_metadata_cache, client: AsyncAtlanClient + ): + """Test that async and sync metadata proxies are independent""" + # Configure the mock + mock_async_custom_metadata_cache.get_id_for_name = AsyncMock(return_value=CM_ID) + mock_async_custom_metadata_cache.get_attr_map_for_id = AsyncMock( + return_value=CM_ATTRIBUTES + ) + mock_async_custom_metadata_cache.is_attr_archived = AsyncMock( + return_value=False + ) + + # Create an asset + asset = Asset() + asset.business_attributes = None + + await asset.get_custom_metadata_async(client, CM_NAME) + + assert asset._async_metadata_proxy is not None + assert asset._metadata_proxy is None + + from pyatlan.client.atlan import AtlanClient + + with patch.object(AtlanClient, "custom_metadata_cache") as sync_mock_cache: + sync_mock_cache.get_id_for_name.return_value = CM_ID + sync_mock_cache.map_attr_id_to_name = {CM_ID: CM_ATTRIBUTES} + sync_mock_cache.is_attr_archived.return_value = False + + sync_client = AtlanClient( + base_url="https://test.atlan.com", api_key="test-key" + ) + asset.get_custom_metadata(sync_client, CM_NAME) + + assert asset._async_metadata_proxy is not None + assert asset._metadata_proxy is not None + assert asset._async_metadata_proxy != asset._metadata_proxy + + +class TestAsyncAttributeDefRichText: + """Test async RICH_TEXT AttributeDef functionality""" + + @pytest.mark.asyncio + async def test_async_rich_text_attribute_creation(self, client: AsyncAtlanClient): + """Test that RICH_TEXT attributes are created correctly using async method""" + + # Mock the client.asset.search method that _get_all_qualified_names_async calls + with patch.object( + client.asset, "search", new_callable=AsyncMock + ) as mock_search: + # Mock an empty search result + async def async_generator(): + return + yield # pragma: no cover + + mock_search.return_value = async_generator() + + attr_def = await AttributeDef.create_async( + client=client, + display_name="Rich Content", + attribute_type=AtlanCustomAttributePrimitiveType.RICH_TEXT, + description="Test rich text attribute", + ) + + assert attr_def.display_name == "Rich Content" + assert attr_def.type_name == AtlanCustomAttributePrimitiveType.STRING.value + assert attr_def.description == "Test rich text attribute" + assert attr_def.options + assert attr_def.options.is_rich_text is True + assert attr_def.options.multi_value_select is False + + @pytest.mark.asyncio + async def test_async_rich_text_cannot_be_multi_valued( + self, client: AsyncAtlanClient + ): + """Test that async RICH_TEXT attributes cannot be multi-valued""" + + # Mock the client.asset.search method that _get_all_qualified_names_async calls + with patch.object( + client.asset, "search", new_callable=AsyncMock + ) as mock_search: + # Mock an empty search result + async def async_generator(): + return + yield # pragma: no cover + + mock_search.return_value = async_generator() + + with pytest.raises(AtlanError) as exc_info: + await AttributeDef.create_async( + client=client, + display_name="Invalid Rich Text", + attribute_type=AtlanCustomAttributePrimitiveType.RICH_TEXT, + multi_valued=True, + ) + + error = exc_info.value + assert "ATLAN-PYTHON-400-076" in str(error) + + @pytest.mark.asyncio + async def test_async_rich_text_options_configuration( + self, client: AsyncAtlanClient + ): + """Test that async RICH_TEXT options are configured correctly""" + + # Mock the client.asset.search method that _get_all_qualified_names_async calls + with patch.object( + client.asset, "search", new_callable=AsyncMock + ) as mock_search: + # Mock an empty search result + async def async_generator(): + return + yield # pragma: no cover + + mock_search.return_value = async_generator() + + attr_def = await AttributeDef.create_async( + client=client, + display_name="Rich Text Field", + attribute_type=AtlanCustomAttributePrimitiveType.RICH_TEXT, + ) + + options = attr_def.options + assert options is not None + # Rich text uses string primitive type + assert options.primitive_type == AtlanCustomAttributePrimitiveType.STRING.value + # Should have rich text flag enabled + assert options.is_rich_text is True + # Cannot be multi-valued + assert options.multi_value_select is False + # Should not have custom_type set (that's for SQL, URL, etc.) + assert not hasattr(options, "custom_type") or options.custom_type is None diff --git a/tests_v9/unit/aio/test_file_client.py b/tests_v9/unit/aio/test_file_client.py new file mode 100644 index 000000000..3af629181 --- /dev/null +++ b/tests_v9/unit/aio/test_file_client.py @@ -0,0 +1,270 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. +import os +from json import load +from pathlib import Path +from unittest.mock import AsyncMock, Mock, patch + +import pytest + +from pyatlan.client.common import AsyncApiCaller +from pyatlan_v9.client.aio.atlan import AsyncAtlanClient +from pyatlan_v9.client.aio.file import V9AsyncFileClient as AsyncFileClient +from pyatlan_v9.errors import InvalidRequestError +from pyatlan_v9.model.file import PresignedURLRequest +from tests_v9.unit.constants import TEST_FILE_CLIENT_METHODS + +TEST_DATA_DIR = Path(__file__).parent.parent.parent.parent / "tests" / "unit" / "data" +UPLOAD_FILE_PATH = str(TEST_DATA_DIR / "file_requests/upload.txt") +DOWNLOAD_FILE_PATH = str(TEST_DATA_DIR / "file_requests/download.txt") + + +def load_json(respones_dir, filename): + with (respones_dir / filename).open() as input_file: + return load(input_file) + + +def to_json(model): + return model.json(by_alias=True, exclude_none=True) + + +@pytest.fixture(autouse=True) +def set_env(monkeypatch): + monkeypatch.setenv("ATLAN_BASE_URL", "https://test.atlan.com") + monkeypatch.setenv("ATLAN_API_KEY", "test-api-key") + + +@pytest.fixture() +def client(): + return AsyncAtlanClient() + + +@pytest.fixture(scope="module") +def mock_async_api_caller(): + mock = Mock(spec=AsyncApiCaller) + mock._call_api = AsyncMock() + return mock + + +@pytest.fixture(scope="module") +def s3_presigned_url(): + return ( + "https://test-vcluster.amazonaws.com/some-directory/test.png" + "?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20240425T09240" + ) + + +@pytest.fixture(scope="module") +def blob_presigned_url(): + return ( + "https://test.blob.core.windows.net/objectstore/test.png" + "?se=2024-08-12T09%3A45%3A13Z&sig=esqARNUwHUETQOqSCaSCTqD" + "Wjg7vTmcK1PLzQ1buMCQ%3D&sp=aw&spr=https&sr=b&sv=2020-04-08" + ) + + +@pytest.fixture(scope="module") +def gcs_presigned_url(): + return ( + "https://test.storage.googleapis.com/test-vcluster/test.png" + "?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=prod" + "iam.gserviceaccount.com%2F20240813%2Fauto%2Fstorage%2Fgoog" + "4_request&X-Goog-Date=20240893T093902Z&X-Goog-Expires=29&X-" + "Goog-Signature=5620d93a7916b150ce87a324d969741112f764b6d9f6" + ) + + +@pytest.fixture() +def mock_session(): + with patch.object(AsyncAtlanClient, "_async_session") as mock_session: + mock_response = Mock() + mock_response.status_code = 200 + mock_response.headers = {} + mock_response.raise_for_status.return_value = None + + class AsyncBytesIterator: + def __init__(self, content): + self.content = content + self.yielded = False + + def __aiter__(self): + return self + + async def __anext__(self): + if not self.yielded: + self.yielded = True + return self.content + else: + raise StopAsyncIteration + + mock_response.aiter_raw = lambda: AsyncBytesIterator(b"test data 12345.\n") + mock_response.aiter_bytes = lambda chunk_size=8192: AsyncBytesIterator( + b"test data 12345.\n" + ) + mock_response.aread = AsyncMock(return_value=b"test data 12345.\n") + + async_context_manager = AsyncMock() + async_context_manager.__aenter__.return_value = mock_response + async_context_manager.__aexit__.return_value = None + mock_session.stream.return_value = async_context_manager + + yield mock_session + # Cleanup - remove download file if it exists (similar to sync version) + if os.path.exists(DOWNLOAD_FILE_PATH): + os.remove(DOWNLOAD_FILE_PATH) + + +@pytest.fixture() +def mock_session_invalid(): + with patch.object(AsyncAtlanClient, "_async_session") as mock_session: + mock_response = Mock() + mock_response.status_code = 200 + mock_response.headers = {} + mock_response.raise_for_status.return_value = None + + class BadAsyncIterator: + def __aiter__(self): + return self + + async def __anext__(self): + raise AttributeError("'str' object has no attribute 'read'") + + mock_response.aiter_raw = lambda: BadAsyncIterator() + mock_response.aread = AsyncMock(return_value=b"test content") + + async_context_manager = AsyncMock() + async_context_manager.__aenter__.return_value = mock_response + async_context_manager.__aexit__.return_value = None + mock_session.stream.return_value = async_context_manager + + yield mock_session + # Don't assert file exists for invalid case since error should prevent creation + if os.path.exists(DOWNLOAD_FILE_PATH): + os.remove(DOWNLOAD_FILE_PATH) + + +@pytest.mark.parametrize("method, params", TEST_FILE_CLIENT_METHODS.items()) +@pytest.mark.asyncio +async def test_async_file_client_methods_validation_error(client, method, params): + client_method = getattr(client.files, method) + for param_values, error_msg in params: + with pytest.raises(ValueError, match=error_msg): + client_method(*param_values) + + +@pytest.mark.parametrize( + "file_path, expected_error", + [ + [ + UPLOAD_FILE_PATH, + ( + "ATLAN-PYTHON-400-061 Provided presigned URL's cloud provider " + "storage is currently not supported for file uploads." + ), + ], + [ + "some/invalid/file_path.png", + ( + "ATLAN-PYTHON-400-059 Unable to upload file, " + "Error: No such file or directory, Path: some/invalid/file_path.png" + ), + ], + ], +) +@pytest.mark.asyncio +async def test_async_file_client_upload_file_raises_invalid_request_error( + mock_async_api_caller, file_path, expected_error +): + client = AsyncFileClient(client=mock_async_api_caller) + + with pytest.raises(InvalidRequestError, match=expected_error): + await client.upload_file( + presigned_url="test-url", + file_path=file_path, + ) + + +@pytest.mark.asyncio +async def test_async_file_client_download_file_invalid_format_raises_invalid_request_error( + client, s3_presigned_url, mock_session_invalid +): + expected_error = ( + "ATLAN-PYTHON-400-060 Unable to download file, " + f"Error: 'str' object has no attribute 'read', Path: {DOWNLOAD_FILE_PATH}" + ) + with pytest.raises(InvalidRequestError, match=expected_error): + await client.files.download_file( + presigned_url=s3_presigned_url, file_path=DOWNLOAD_FILE_PATH + ) + + +@pytest.mark.asyncio +async def test_async_file_client_get_presigned_url( + mock_async_api_caller, s3_presigned_url +): + mock_async_api_caller._call_api.return_value = {"url": s3_presigned_url} + client = AsyncFileClient(mock_async_api_caller) + response = await client.generate_presigned_url( + request=PresignedURLRequest( + key="some-directory/test.png", + expiry="60s", + method=PresignedURLRequest.Method.GET, + ) + ) + assert mock_async_api_caller._call_api.call_count == 1 + assert response == s3_presigned_url + mock_async_api_caller.reset_mock() + + +@patch.object(AsyncAtlanClient, "_call_api_internal", new_callable=AsyncMock) +@pytest.mark.asyncio +async def test_async_file_client_s3_upload_file( + mock_call_api_internal, client, s3_presigned_url +): + client = AsyncFileClient(client=client) + await client.upload_file(presigned_url=s3_presigned_url, file_path=UPLOAD_FILE_PATH) + + assert mock_call_api_internal.call_count == 1 + mock_call_api_internal.reset_mock() + + +@patch.object(AsyncAtlanClient, "_call_api_internal", new_callable=AsyncMock) +@pytest.mark.asyncio +async def test_async_file_client_azure_blob_upload_file( + mock_call_api_internal, client, blob_presigned_url +): + client = AsyncFileClient(client=client) + await client.upload_file( + presigned_url=blob_presigned_url, file_path=UPLOAD_FILE_PATH + ) + + assert mock_call_api_internal.call_count == 1 + mock_call_api_internal.reset_mock() + + +@patch.object(AsyncAtlanClient, "_call_api_internal", new_callable=AsyncMock) +@pytest.mark.asyncio +async def test_async_file_client_gcs_upload_file( + mock_call_api_internal, client, gcs_presigned_url +): + client = AsyncFileClient(client=client) + await client.upload_file( + presigned_url=gcs_presigned_url, file_path=UPLOAD_FILE_PATH + ) + + assert mock_call_api_internal.call_count == 1 + mock_call_api_internal.reset_mock() + + +@pytest.mark.asyncio +async def test_async_file_client_download_file(client, s3_presigned_url, mock_session): + # Make sure the download file doesn't exist before downloading + assert not os.path.exists(DOWNLOAD_FILE_PATH) + response = await client.files.download_file( + presigned_url=s3_presigned_url, file_path=DOWNLOAD_FILE_PATH + ) + assert response == DOWNLOAD_FILE_PATH + assert mock_session.stream.call_count == 1 + # The file should exist after calling the method + assert os.path.exists(DOWNLOAD_FILE_PATH) + assert open(DOWNLOAD_FILE_PATH, "r").read() == "test data 12345.\n" diff --git a/tests_v9/unit/aio/test_oauth_client.py b/tests_v9/unit/aio/test_oauth_client.py new file mode 100644 index 000000000..9facef739 --- /dev/null +++ b/tests_v9/unit/aio/test_oauth_client.py @@ -0,0 +1,777 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. +""" +Comprehensive Async OAuth Client Tests + +Tests for OAuth authentication in the asynchronous AsyncAtlanClient: +- Authentication method precedence +- Environment variable handling +- Token lifecycle (fetch, cache, refresh, expiry) +- Error handling and edge cases +- Async concurrency safety +- Resource cleanup +- URL construction +- Sync manager cleanup (resource leak prevention) +""" + +import asyncio +import os +from unittest.mock import Mock, patch +from urllib.parse import urlparse + +import httpx +import pytest + +from pyatlan.client.aio.oauth import AsyncOAuthTokenManager +from pyatlan_v9.client.aio.atlan import AsyncAtlanClient + + +@pytest.fixture +def clear_env_vars(): + """Clear OAuth-related environment variables before each test""" + env_vars = [ + "ATLAN_BASE_URL", + "ATLAN_API_KEY", + "ATLAN_OAUTH_CLIENT_ID", + "ATLAN_OAUTH_CLIENT_SECRET", + ] + original_values = {} + for var in env_vars: + original_values[var] = os.environ.get(var) + if var in os.environ: + del os.environ[var] + + yield + + for var, value in original_values.items(): + if value is not None: + os.environ[var] = value + elif var in os.environ: + del os.environ[var] + + +@pytest.fixture +def mock_oauth_response(): + """Mock successful OAuth token response with camelCase""" + return { + "accessToken": "async-test-access-token-12345", + "tokenType": "Bearer", + "expiresIn": 3600, + } + + +@pytest.fixture +def mock_oauth_response_snake_case(): + """Mock successful OAuth token response with snake_case""" + return { + "access_token": "async-test-access-token-67890", + "token_type": "Bearer", + "expires_in": 3600, + } + + +class TestAsyncOAuthTokenManagerInit: + """Test async OAuth token manager initialization""" + + @pytest.mark.asyncio + async def test_init_with_external_url(self, clear_env_vars): + """Initialize with external URL""" + manager = AsyncOAuthTokenManager( + base_url="https://test.atlan.com", + client_id="test-client-id", + client_secret="test-client-secret", + ) + + assert manager.base_url == "https://test.atlan.com" + assert manager.client_id == "test-client-id" + assert manager.client_secret == "test-client-secret" + assert ( + manager.token_url + == "https://test.atlan.com/api/service/oauth-clients/token" + ) + assert manager._token is None + assert manager._owns_client is True + + await manager.aclose() + + @pytest.mark.asyncio + async def test_init_with_internal_url(self, clear_env_vars): + """Initialize with INTERNAL mode""" + manager = AsyncOAuthTokenManager( + base_url="INTERNAL", + client_id="test-client-id", + client_secret="test-client-secret", + ) + + assert manager.base_url == "INTERNAL" + expected_url = ( + "http://heracles-service.heracles.svc.cluster.local/oauth-clients/token" + ) + assert manager.token_url == expected_url + + await manager.aclose() + + @pytest.mark.asyncio + async def test_init_with_external_http_client(self, clear_env_vars): + """Initialize with externally provided async HTTP client""" + external_client = httpx.AsyncClient() + + manager = AsyncOAuthTokenManager( + base_url="https://test.atlan.com", + client_id="test-client-id", + client_secret="test-client-secret", + http_client=external_client, + ) + + assert manager._http_client is external_client + assert manager._owns_client is False + + await manager.aclose() + assert not external_client.is_closed + await external_client.aclose() + + @pytest.mark.asyncio + async def test_init_creates_http_client_when_not_provided(self, clear_env_vars): + """Initialize without HTTP client should create one""" + manager = AsyncOAuthTokenManager( + base_url="https://test.atlan.com", + client_id="test-client-id", + client_secret="test-client-secret", + ) + + assert manager._http_client is not None + assert isinstance(manager._http_client, httpx.AsyncClient) + assert manager._owns_client is True + + await manager.aclose() + + +class TestTokenFetchingAndCaching: + """Test token fetching and caching behavior""" + + @pytest.mark.asyncio + @patch("httpx.AsyncClient.post") + async def test_first_token_fetch( + self, mock_post, clear_env_vars, mock_oauth_response + ): + """First call should fetch token from API""" + mock_response = Mock() + mock_response.json = Mock(return_value=mock_oauth_response) + mock_response.raise_for_status = Mock() + mock_post.return_value = mock_response + + manager = AsyncOAuthTokenManager( + base_url="https://test.atlan.com", + client_id="test-client-id", + client_secret="test-client-secret", + ) + + token = await manager.get_token() + + assert token == "async-test-access-token-12345" + assert mock_post.call_count == 1 + + call_args = mock_post.call_args + assert call_args[1]["json"]["clientId"] == "test-client-id" + assert call_args[1]["json"]["clientSecret"] == "test-client-secret" + assert call_args[1]["headers"]["Content-Type"] == "application/json" + + await manager.aclose() + + @pytest.mark.asyncio + @patch("httpx.AsyncClient.post") + async def test_token_caching(self, mock_post, clear_env_vars, mock_oauth_response): + """Subsequent calls should use cached token""" + mock_response = Mock() + mock_response.json = Mock(return_value=mock_oauth_response) + mock_response.raise_for_status = Mock() + mock_post.return_value = mock_response + + manager = AsyncOAuthTokenManager( + base_url="https://test.atlan.com", + client_id="test-client-id", + client_secret="test-client-secret", + ) + + token1 = await manager.get_token() + assert token1 == "async-test-access-token-12345" + assert mock_post.call_count == 1 + + token2 = await manager.get_token() + assert token2 == "async-test-access-token-12345" + assert mock_post.call_count == 1 + + token3 = await manager.get_token() + assert token3 == "async-test-access-token-12345" + assert mock_post.call_count == 1 + + await manager.aclose() + + @pytest.mark.asyncio + @patch("httpx.AsyncClient.post") + async def test_snake_case_response( + self, mock_post, clear_env_vars, mock_oauth_response_snake_case + ): + """Should handle snake_case field names in response""" + mock_response = Mock() + mock_response.json = Mock(return_value=mock_oauth_response_snake_case) + mock_response.raise_for_status = Mock() + mock_post.return_value = mock_response + + manager = AsyncOAuthTokenManager( + base_url="https://test.atlan.com", + client_id="test-client-id", + client_secret="test-client-secret", + ) + + token = await manager.get_token() + assert token == "async-test-access-token-67890" + + await manager.aclose() + + +class TestTokenExpiryAndRefresh: + """Test token expiry detection and automatic refresh""" + + @pytest.mark.asyncio + @patch("httpx.AsyncClient.post") + async def test_token_refresh_on_expiry(self, mock_post, clear_env_vars): + """Expired token should trigger automatic refresh""" + first_response = Mock() + first_response.json = Mock( + return_value={ + "accessToken": "async-token-1", + "tokenType": "Bearer", + "expiresIn": 1, + } + ) + first_response.raise_for_status = Mock() + + second_response = Mock() + second_response.json = Mock( + return_value={ + "accessToken": "async-token-2", + "tokenType": "Bearer", + "expiresIn": 3600, + } + ) + second_response.raise_for_status = Mock() + + mock_post.side_effect = [first_response, second_response] + + manager = AsyncOAuthTokenManager( + base_url="https://test.atlan.com", + client_id="test-client-id", + client_secret="test-client-secret", + ) + + token1 = await manager.get_token() + assert token1 == "async-token-1" + assert mock_post.call_count == 1 + + await asyncio.sleep(2) + + token2 = await manager.get_token() + assert token2 == "async-token-2" + assert mock_post.call_count == 2 + + await manager.aclose() + + @pytest.mark.asyncio + @patch("httpx.AsyncClient.post") + async def test_manual_token_invalidation( + self, mock_post, clear_env_vars, mock_oauth_response + ): + """Manual invalidation should force refresh on next call""" + mock_response = Mock() + mock_response.json = Mock(return_value=mock_oauth_response) + mock_response.raise_for_status = Mock() + mock_post.return_value = mock_response + + manager = AsyncOAuthTokenManager( + base_url="https://test.atlan.com", + client_id="test-client-id", + client_secret="test-client-secret", + ) + + await manager.get_token() + assert mock_post.call_count == 1 + + await manager.invalidate_token() + assert manager._token is None + + await manager.get_token() + assert mock_post.call_count == 2 + + await manager.aclose() + + +class TestErrorHandling: + """Test error handling in various failure scenarios""" + + @pytest.mark.asyncio + @patch("httpx.AsyncClient.post") + async def test_missing_access_token(self, mock_post, clear_env_vars): + """Missing accessToken should raise ValueError""" + mock_response = Mock() + mock_response.json = Mock( + return_value={ + "tokenType": "Bearer", + "expiresIn": 3600, + } + ) + mock_response.raise_for_status = Mock() + mock_post.return_value = mock_response + + manager = AsyncOAuthTokenManager( + base_url="https://test.atlan.com", + client_id="test-client-id", + client_secret="test-client-secret", + ) + + with pytest.raises( + ValueError, match="OAuth token response missing 'accessToken' field" + ): + await manager.get_token() + + await manager.aclose() + + @pytest.mark.asyncio + @patch("httpx.AsyncClient.post") + async def test_http_401_error(self, mock_post, clear_env_vars): + """401 error should be propagated""" + mock_response = Mock() + mock_response.raise_for_status.side_effect = httpx.HTTPStatusError( + "401 Unauthorized", + request=Mock(), + response=Mock(status_code=401), + ) + mock_post.return_value = mock_response + + manager = AsyncOAuthTokenManager( + base_url="https://test.atlan.com", + client_id="test-client-id", + client_secret="test-client-secret", + ) + + with pytest.raises(httpx.HTTPStatusError): + await manager.get_token() + + await manager.aclose() + + @pytest.mark.asyncio + @patch("httpx.AsyncClient.post") + async def test_http_500_error(self, mock_post, clear_env_vars): + """500 error should be propagated""" + mock_response = Mock() + mock_response.raise_for_status.side_effect = httpx.HTTPStatusError( + "500 Internal Server Error", + request=Mock(), + response=Mock(status_code=500), + ) + mock_post.return_value = mock_response + + manager = AsyncOAuthTokenManager( + base_url="https://test.atlan.com", + client_id="test-client-id", + client_secret="test-client-secret", + ) + + with pytest.raises(httpx.HTTPStatusError): + await manager.get_token() + + await manager.aclose() + + @pytest.mark.asyncio + @patch("httpx.AsyncClient.post") + async def test_network_error(self, mock_post, clear_env_vars): + """Network errors should be propagated""" + mock_post.side_effect = httpx.ConnectError("Connection refused") + + manager = AsyncOAuthTokenManager( + base_url="https://test.atlan.com", + client_id="test-client-id", + client_secret="test-client-secret", + ) + + with pytest.raises(httpx.ConnectError): + await manager.get_token() + + await manager.aclose() + + @pytest.mark.asyncio + @patch("httpx.AsyncClient.post") + async def test_timeout_error(self, mock_post, clear_env_vars): + """Timeout errors should be propagated""" + mock_post.side_effect = httpx.TimeoutException("Request timeout") + + manager = AsyncOAuthTokenManager( + base_url="https://test.atlan.com", + client_id="test-client-id", + client_secret="test-client-secret", + ) + + with pytest.raises(httpx.TimeoutException): + await manager.get_token() + + await manager.aclose() + + @pytest.mark.asyncio + @patch("httpx.AsyncClient.post") + async def test_invalid_json_response(self, mock_post, clear_env_vars): + """Invalid JSON in response should raise error""" + mock_response = Mock() + mock_response.json.side_effect = ValueError("Invalid JSON") + mock_response.raise_for_status = Mock() + mock_post.return_value = mock_response + + manager = AsyncOAuthTokenManager( + base_url="https://test.atlan.com", + client_id="test-client-id", + client_secret="test-client-secret", + ) + + with pytest.raises(ValueError, match="Invalid JSON"): + await manager.get_token() + + await manager.aclose() + + +class TestAsyncConcurrencySafety: + """Test async concurrency safety of OAuth token management""" + + @pytest.mark.asyncio + @patch("httpx.AsyncClient.post") + async def test_concurrent_token_requests( + self, mock_post, clear_env_vars, mock_oauth_response + ): + """Multiple coroutines requesting token simultaneously should result in single fetch""" + call_count = {"count": 0} + + async def mock_post_with_delay(*args, **kwargs): + call_count["count"] += 1 + await asyncio.sleep(0.1) + mock_response = Mock() + mock_response.json = Mock(return_value=mock_oauth_response) + mock_response.raise_for_status = Mock() + return mock_response + + mock_post.side_effect = mock_post_with_delay + + manager = AsyncOAuthTokenManager( + base_url="https://test.atlan.com", + client_id="test-client-id", + client_secret="test-client-secret", + ) + + tokens = await asyncio.gather(*[manager.get_token() for _ in range(10)]) + + assert len(tokens) == 10 + assert all(token == tokens[0] for token in tokens) + + assert call_count["count"] <= 2 + + await manager.aclose() + + @pytest.mark.asyncio + @patch("httpx.AsyncClient.post") + async def test_concurrent_invalidation_and_fetch( + self, mock_post, clear_env_vars, mock_oauth_response + ): + """Concurrent invalidation and fetch should be async-safe""" + mock_response = Mock() + mock_response.json = Mock(return_value=mock_oauth_response) + mock_response.raise_for_status = Mock() + mock_post.return_value = mock_response + + manager = AsyncOAuthTokenManager( + base_url="https://test.atlan.com", + client_id="test-client-id", + client_secret="test-client-secret", + ) + + await manager.get_token() + + async def invalidate_repeatedly(): + for _ in range(5): + await manager.invalidate_token() + await asyncio.sleep(0.01) + + async def fetch_repeatedly(): + for _ in range(5): + await manager.get_token() + await asyncio.sleep(0.01) + + await asyncio.gather( + invalidate_repeatedly(), + fetch_repeatedly(), + fetch_repeatedly(), + ) + + await manager.aclose() + + +class TestResourceCleanup: + """Test proper cleanup of resources""" + + @pytest.mark.asyncio + async def test_close_http_client(self, clear_env_vars): + """aclose should close owned HTTP client""" + manager = AsyncOAuthTokenManager( + base_url="https://test.atlan.com", + client_id="test-client-id", + client_secret="test-client-secret", + ) + + http_client = manager._http_client + assert not http_client.is_closed + + await manager.aclose() + assert http_client.is_closed + + @pytest.mark.asyncio + async def test_dont_close_external_client(self, clear_env_vars): + """Should not close externally provided HTTP client""" + external_client = httpx.AsyncClient() + + manager = AsyncOAuthTokenManager( + base_url="https://test.atlan.com", + client_id="test-client-id", + client_secret="test-client-secret", + http_client=external_client, + ) + + await manager.aclose() + + assert not external_client.is_closed + + await external_client.aclose() + + +class TestAsyncAtlanClientAuthPrecedence: + """Test authentication method precedence in AsyncAtlanClient""" + + @pytest.mark.asyncio + async def test_api_key_only(self, clear_env_vars): + """API key authentication""" + client = AsyncAtlanClient( + base_url="https://test.atlan.com", + api_key="test-api-key", + ) + + assert client.api_key == "test-api-key" + assert client._async_oauth_token_manager is None + assert "authorization" in client._request_params["headers"] + assert ( + client._request_params["headers"]["authorization"] == "Bearer test-api-key" + ) + + await client.aclose() + + @pytest.mark.asyncio + async def test_oauth_only(self, clear_env_vars): + """OAuth authentication""" + client = AsyncAtlanClient( + base_url="https://test.atlan.com", + oauth_client_id="test-client-id", + oauth_client_secret="test-client-secret", + ) + + assert client.api_key is None + assert client._async_oauth_token_manager is not None + assert client._async_oauth_token_manager.client_id == "test-client-id" + assert client._async_oauth_token_manager.client_secret == "test-client-secret" + + await client.aclose() + + @pytest.mark.asyncio + async def test_api_key_takes_precedence(self, clear_env_vars): + """API key takes precedence when both provided""" + client = AsyncAtlanClient( + base_url="https://test.atlan.com", + api_key="test-api-key", + oauth_client_id="test-client-id", + oauth_client_secret="test-client-secret", + ) + + assert client.api_key == "test-api-key" + assert client._async_oauth_token_manager is None + assert ( + client._request_params["headers"]["authorization"] == "Bearer test-api-key" + ) + + await client.aclose() + + @pytest.mark.asyncio + async def test_empty_api_key(self, clear_env_vars): + """Empty API key should not create OAuth manager""" + client = AsyncAtlanClient( + base_url="https://test.atlan.com", + api_key="", + oauth_client_id="test-client-id", + oauth_client_secret="test-client-secret", + ) + + assert client.api_key == "" + assert client._async_oauth_token_manager is None + assert "authorization" not in client._request_params["headers"] + + await client.aclose() + + @pytest.mark.asyncio + async def test_no_authentication(self, clear_env_vars): + """No authentication provided""" + client = AsyncAtlanClient(base_url="https://test.atlan.com") + + assert client.api_key is None + assert client._async_oauth_token_manager is None + assert "authorization" not in client._request_params["headers"] + + await client.aclose() + + +class TestSyncManagerCleanup: + """Test that the v9 async client only creates an async OAuth manager (no sync manager)""" + + @pytest.mark.asyncio + async def test_only_async_manager_created(self, clear_env_vars): + """V9 async client creates only an async OAuth manager, never a sync one""" + client = AsyncAtlanClient( + base_url="https://test.atlan.com", + oauth_client_id="test-client-id", + oauth_client_secret="test-client-secret", + ) + + assert client._async_oauth_token_manager is not None + assert isinstance(client._async_oauth_token_manager, AsyncOAuthTokenManager) + + await client.aclose() + + @pytest.mark.asyncio + async def test_manager_cleaned_up_on_close(self, clear_env_vars): + """OAuth manager is cleaned up when async client is closed""" + client = AsyncAtlanClient( + base_url="https://test.atlan.com", + oauth_client_id="test-client-id", + oauth_client_secret="test-client-secret", + ) + + assert client._async_oauth_token_manager is not None + await client.aclose() + assert client._async_oauth_token_manager is None + + +class TestEnvironmentVariables: + """Test environment variable handling""" + + @pytest.mark.asyncio + async def test_oauth_from_env(self, clear_env_vars): + """OAuth credentials from environment variables""" + os.environ["ATLAN_BASE_URL"] = "https://env.atlan.com" + os.environ["ATLAN_OAUTH_CLIENT_ID"] = "env-client-id" + os.environ["ATLAN_OAUTH_CLIENT_SECRET"] = "env-client-secret" + + client = AsyncAtlanClient() + + assert client._async_oauth_token_manager is not None + assert client._async_oauth_token_manager.client_id == "env-client-id" + assert client._async_oauth_token_manager.client_secret == "env-client-secret" + + await client.aclose() + + @pytest.mark.asyncio + async def test_explicit_overrides_env(self, clear_env_vars): + """Explicit parameters override environment variables""" + os.environ["ATLAN_BASE_URL"] = "https://env.atlan.com" + os.environ["ATLAN_OAUTH_CLIENT_ID"] = "env-client-id" + os.environ["ATLAN_OAUTH_CLIENT_SECRET"] = "env-client-secret" + + client = AsyncAtlanClient( + base_url="https://explicit.atlan.com", + oauth_client_id="explicit-client-id", + oauth_client_secret="explicit-client-secret", + ) + + assert urlparse(str(client.base_url)).hostname == "explicit.atlan.com" + assert client._async_oauth_token_manager.client_id == "explicit-client-id" + assert ( + client._async_oauth_token_manager.client_secret == "explicit-client-secret" + ) + + await client.aclose() + + @pytest.mark.asyncio + async def test_api_key_env_precedence(self, clear_env_vars): + """API key from env takes precedence over OAuth""" + os.environ["ATLAN_BASE_URL"] = "https://test.atlan.com" + os.environ["ATLAN_API_KEY"] = "env-api-key" + os.environ["ATLAN_OAUTH_CLIENT_ID"] = "env-client-id" + os.environ["ATLAN_OAUTH_CLIENT_SECRET"] = "env-client-secret" + + client = AsyncAtlanClient() + + assert client.api_key == "env-api-key" + assert client._async_oauth_token_manager is None + assert ( + client._request_params["headers"]["authorization"] == "Bearer env-api-key" + ) + + await client.aclose() + + @pytest.mark.asyncio + async def test_partial_oauth_credentials(self, clear_env_vars): + """Partial OAuth credentials should not create manager""" + os.environ["ATLAN_BASE_URL"] = "https://test.atlan.com" + os.environ["ATLAN_OAUTH_CLIENT_ID"] = "env-client-id" + + client = AsyncAtlanClient() + + assert client._async_oauth_token_manager is None + + await client.aclose() + + +class TestEdgeCases: + """Test edge cases and unusual scenarios""" + + @pytest.mark.asyncio + @patch("httpx.AsyncClient.post") + async def test_default_expires_in(self, mock_post, clear_env_vars): + """Missing expiresIn should use default""" + mock_response = Mock() + mock_response.json = Mock( + return_value={ + "accessToken": "test-token", + "tokenType": "Bearer", + } + ) + mock_response.raise_for_status = Mock() + mock_post.return_value = mock_response + + manager = AsyncOAuthTokenManager( + base_url="https://test.atlan.com", + client_id="test-client-id", + client_secret="test-client-secret", + ) + + token = await manager.get_token() + assert token == "test-token" + + await manager.aclose() + + @pytest.mark.asyncio + async def test_url_with_trailing_slash(self, clear_env_vars): + """Base URL with trailing slash""" + manager = AsyncOAuthTokenManager( + base_url="https://test.atlan.com/", + client_id="test-client-id", + client_secret="test-client-secret", + ) + + assert "/api/service/oauth-clients/token" in manager.token_url + + await manager.aclose() + + +if __name__ == "__main__": + pytest.main([__file__, "-v", "-s"]) diff --git a/tests_v9/unit/aio/test_query_client.py b/tests_v9/unit/aio/test_query_client.py new file mode 100644 index 000000000..291e501c2 --- /dev/null +++ b/tests_v9/unit/aio/test_query_client.py @@ -0,0 +1,114 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. +from pathlib import Path +from unittest.mock import AsyncMock, Mock + +import pytest + +from pyatlan.client.common import AsyncApiCaller +from pyatlan_v9.client.aio.query import V9AsyncQueryClient as AsyncQueryClient +from pyatlan_v9.errors import InvalidRequestError +from pyatlan_v9.model.query import QueryRequest, QueryResponse + +QUERY_RESPONSES = ( + Path(__file__).parent.parent.parent.parent + / "tests" + / "unit" + / "data" + / "query_responses.txt" +) + + +@pytest.fixture(autouse=True) +def set_env(monkeypatch): + monkeypatch.setenv("ATLAN_BASE_URL", "https://name.atlan.com") + monkeypatch.setenv("ATLAN_API_KEY", "abkj") + + +@pytest.fixture() +def mock_async_api_caller(): + mock_caller = Mock(spec=AsyncApiCaller) + mock_caller._call_api = AsyncMock() + return mock_caller + + +@pytest.fixture() +def query_request() -> QueryRequest: + return QueryRequest( + sql="test-sql", data_source_name="test-ds-name", default_schema="test-schema" + ) + + +@pytest.fixture() +def query_response() -> QueryResponse: + return QueryResponse() + + +@pytest.fixture() +def mock_async_session(): + lines_from_file = [] + + with open(QUERY_RESPONSES, "r", encoding="utf-8") as file: + lines_from_file = [line.strip() for line in file.readlines()] + + # Convert the text lines to the expected JSON format + import json + + events = [] + for line in lines_from_file: + if line.startswith("data: "): + try: + event_data = json.loads(line[6:]) # Remove "data: " prefix + events.append(event_data) + except json.JSONDecodeError: + pass + + return events + + +@pytest.mark.parametrize("test_api_caller", ["abc", None]) +def test_init_when_wrong_class_raises_exception(test_api_caller): + with pytest.raises( + InvalidRequestError, + match="ATLAN-PYTHON-400-048 Invalid parameter type for client should be AsyncApiCaller", + ): + AsyncQueryClient(test_api_caller) + + +@pytest.mark.parametrize( + "test_request, error_msg", + [ + [None, "none is not an allowed value"], + ["123", "instance of QueryRequest expected"], + ], +) +def test_query_stream_wrong_params_raises_validation_error( + test_request, error_msg, mock_async_api_caller +): + client = AsyncQueryClient(client=mock_async_api_caller) + with pytest.raises(ValueError) as err: + client.stream(request=test_request) + assert error_msg in str(err.value) + + +@pytest.mark.asyncio +async def test_stream_get_when_given_request( + mock_async_api_caller, + query_request: QueryRequest, + mock_async_session, +): + mock_async_api_caller._call_api.return_value = mock_async_session + client = AsyncQueryClient(client=mock_async_api_caller) + + response = await client.stream(request=query_request) + assert response.rows + assert len(response.rows) == 14 + assert response.columns + assert len(response.columns) == 7 + assert response.request_id + # Last event is an error + assert response.query_id is None + assert response.error_name + assert response.error_code + assert response.error_message + assert response.details diff --git a/tests_v9/unit/aio/test_search_log_client.py b/tests_v9/unit/aio/test_search_log_client.py new file mode 100644 index 000000000..bc674f0b2 --- /dev/null +++ b/tests_v9/unit/aio/test_search_log_client.py @@ -0,0 +1,194 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. +from datetime import datetime, timezone +from json import load +from pathlib import Path +from unittest.mock import AsyncMock, Mock, patch + +import pytest + +from pyatlan.client.common import AsyncApiCaller +from pyatlan.client.common.search_log import LOGGER +from pyatlan_v9.client.aio.search_log import ( + V9AsyncSearchLogClient as AsyncSearchLogClient, +) +from pyatlan_v9.errors import InvalidRequestError +from pyatlan_v9.model.aio.search_log import AsyncSearchLogResults +from pyatlan_v9.model.enums import SortOrder +from pyatlan_v9.model.search import SortItem +from pyatlan_v9.model.search_log import SearchLogRequest + +SEARCH_RESPONSES_DIR = ( + Path(__file__).parent.parent.parent.parent + / "tests" + / "unit" + / "data" + / "search_responses" +) +SEARCH_LOGS_JSON = "search_log_search_paging.json" + + +@pytest.fixture(autouse=True) +def set_env(monkeypatch): + monkeypatch.setenv("ATLAN_BASE_URL", "https://name.atlan.com") + monkeypatch.setenv("ATLAN_API_KEY", "abkj") + + +@pytest.fixture(scope="function") +def mock_async_api_caller(): + mock_caller = Mock(spec=AsyncApiCaller) + mock_caller._call_api = AsyncMock() + mock_caller._async_session = Mock() + return mock_caller + + +@pytest.fixture() +def search_logs_json(): + def load_json(filename): + with (SEARCH_RESPONSES_DIR / filename).open() as input_file: + return load(input_file) + + return load_json(SEARCH_LOGS_JSON) + + +async def _assert_search_log_results(results, response_json, sorts, bulk=False): + async for log in results: + assert log.user_name == response_json["logs"][0]["userName"] + assert log.user_agent == response_json["logs"][0]["userAgent"] + assert log.ip_address == response_json["logs"][0]["ipAddress"] + assert log.host == response_json["logs"][0]["host"] + expected_timestamp = datetime.fromtimestamp( + response_json["logs"][0]["timestamp"] / 1000, tz=timezone.utc + ) + assert log.timestamp == expected_timestamp + assert log.entity_guids_all == response_json["logs"][0]["entityGuidsAll"] + + assert results.count == response_json["approximateCount"] + assert results._bulk == bulk + assert results._criteria.dsl.sort == sorts + + +@pytest.mark.asyncio +@patch.object(LOGGER, "debug") +async def test_search_log_pagination( + mock_logger, mock_async_api_caller, search_logs_json +): + client = AsyncSearchLogClient(mock_async_api_caller) + mock_async_api_caller._call_api.side_effect = [search_logs_json, {}] + + # Test default pagination + search_log_request = SearchLogRequest.views_by_guid( + guid="some-guid", + size=2, + exclude_users=["atlansupport"], + ) + + response = await client.search(criteria=search_log_request, bulk=False) + expected_sorts = [ + SortItem(field="timestamp", order=SortOrder.ASCENDING), + SortItem(field="entityGuidsAll", order=SortOrder.ASCENDING), + ] + + await _assert_search_log_results(response, search_logs_json, expected_sorts) + assert mock_async_api_caller._call_api.call_count == 2 + assert mock_logger.call_count == 0 + mock_async_api_caller._call_api.reset_mock() + + # Test bulk pagination + mock_async_api_caller._call_api.side_effect = [search_logs_json, {}] + response = await client.search(criteria=search_log_request, bulk=True) + expected_sorts = [ + SortItem(field="createdAt", order=SortOrder.ASCENDING), + SortItem(field="entityGuidsAll", order=SortOrder.ASCENDING), + ] + + await _assert_search_log_results( + response, search_logs_json, expected_sorts, bulk=True + ) + # The call count will be 2 because both + # log entries are processed in the first API call. + # In the second API call, self._log_entries + # becomes 0, which breaks the pagination. + # This differs from offset-based pagination + # where an additional API call is needed + # to verify if the results are empty + assert mock_async_api_caller._call_api.call_count == 2 + assert mock_logger.call_count == 1 + assert ( + "Search log bulk search option is enabled." + in mock_logger.call_args_list[0][0][0] + ) + mock_logger.reset_mock() + mock_async_api_caller._call_api.reset_mock() + + # Test automatic bulk search conversion when exceeding threshold + with patch.object(AsyncSearchLogResults, "_MASS_EXTRACT_THRESHOLD", -1): + mock_async_api_caller._call_api.side_effect = [ + # Extra call to re-fetch the first page + # results with updated timestamp sorting + search_logs_json, + search_logs_json, + {}, + ] + search_log_request = SearchLogRequest.views_by_guid( # + guid="some-guid", + size=1, + exclude_users=["atlansupport"], + ) + response = await client.search(criteria=search_log_request) + await _assert_search_log_results( + response, search_logs_json, expected_sorts, bulk=False + ) + assert mock_logger.call_count == 1 + assert mock_async_api_caller._call_api.call_count == 3 + assert ( + "Result size (%s) exceeds threshold (%s)" + in mock_logger.call_args_list[0][0][0] + ) + mock_logger.reset_mock() + mock_async_api_caller._call_api.reset_mock() + + with patch.object(AsyncSearchLogResults, "_MASS_EXTRACT_THRESHOLD", -1): + mock_async_api_caller._call_api.side_effect = [search_logs_json] + # Test exception for bulk=False with user-defined sorting and results exceeding the threshold + search_log_request = SearchLogRequest.views_by_guid( + guid="some-guid", + size=1, + sort=[SortItem(field="some-sort1", order=SortOrder.ASCENDING)], + exclude_users=["atlansupport"], + ) + with pytest.raises( + InvalidRequestError, + match=( + "ATLAN-PYTHON-400-067 Unable to execute " + "search log bulk search with user-defined sorting options. " + "Suggestion: Please ensure that no sorting options are " + "included in your search log search request when performing a bulk search." + ), + ): + await client.search(criteria=search_log_request, bulk=False) + assert mock_async_api_caller._call_api.call_count == 1 + + mock_logger.reset_mock() + mock_async_api_caller._call_api.reset_mock() + # Test exception for bulk=True with user-defined sorting + search_log_request = SearchLogRequest.views_by_guid( + guid="some-guid", + size=1, + sort=[SortItem(field="some-sort2", order=SortOrder.ASCENDING)], + exclude_users=["atlansupport"], + ) + with pytest.raises( + InvalidRequestError, + match=( + "ATLAN-PYTHON-400-067 Unable to execute " + "search log bulk search with user-defined sorting options. " + "Suggestion: Please ensure that no sorting options are " + "included in your search log search request when performing a bulk search." + ), + ): + await client.search(criteria=search_log_request, bulk=True) + assert mock_async_api_caller._call_api.call_count == 0 + + mock_logger.reset_mock() + mock_async_api_caller._call_api.reset_mock() diff --git a/tests_v9/unit/aio/test_source_cache.py b/tests_v9/unit/aio/test_source_cache.py new file mode 100644 index 000000000..4d90ffe8b --- /dev/null +++ b/tests_v9/unit/aio/test_source_cache.py @@ -0,0 +1,277 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. +from unittest.mock import Mock, patch + +import pytest + +from pyatlan.cache.aio.source_tag_cache import AsyncSourceTagCache +from pyatlan.cache.source_tag_cache import SourceTagName +from pyatlan_v9.client.aio.atlan import AsyncAtlanClient +from pyatlan_v9.errors import ErrorCode, InvalidRequestError, NotFoundError +from pyatlan_v9.model.assets import Connection + + +@pytest.fixture(autouse=True) +def set_env(monkeypatch): + monkeypatch.setenv("ATLAN_BASE_URL", "https://test.atlan.com") + monkeypatch.setenv("ATLAN_API_KEY", "test-api-key") + + +@pytest.fixture() +def async_client(): + return AsyncAtlanClient() + + +@pytest.fixture() +def mock_async_source_tag_cache(async_client, monkeypatch): + mock_cache = AsyncSourceTagCache(async_client) + monkeypatch.setattr(AsyncAtlanClient, "source_tag_cache", mock_cache) + return mock_cache + + +@pytest.mark.asyncio +async def test_get_by_guid_with_not_found_error(mock_async_source_tag_cache): + with pytest.raises(InvalidRequestError, match=ErrorCode.MISSING_ID.error_message): + await mock_async_source_tag_cache.get_by_guid("") + + +@patch.object(AsyncSourceTagCache, "lookup_by_guid") +@pytest.mark.asyncio +async def test_get_by_guid_with_no_invalid_request_error( + mock_lookup_by_guid, mock_async_source_tag_cache +): + test_guid = "test-guid-123" + with pytest.raises( + NotFoundError, + match=ErrorCode.ASSET_NOT_FOUND_BY_GUID.error_message.format(test_guid), + ): + await mock_async_source_tag_cache.get_by_guid(test_guid) + + +@pytest.mark.asyncio +async def test_get_by_qualified_name_with_not_found_error(async_client): + source_tag_cache = AsyncSourceTagCache(async_client) + with pytest.raises(InvalidRequestError, match=ErrorCode.MISSING_ID.error_message): + await source_tag_cache.get_by_qualified_name("") + + +@patch.object(AsyncSourceTagCache, "lookup_by_qualified_name") +@pytest.mark.asyncio +async def test_get_by_qualified_name_with_no_invalid_request_error( + mock_lookup_by_qualified_name, mock_async_source_tag_cache +): + test_qn = "default/snowflake/123456789" + test_connector = "snowflake" + with pytest.raises( + NotFoundError, + match=ErrorCode.ASSET_NOT_FOUND_BY_QN.error_message.format( + test_qn, test_connector + ), + ): + await mock_async_source_tag_cache.get_by_qualified_name(test_qn) + + +@pytest.mark.asyncio +async def test_get_by_name_with_not_found_error(async_client): + source_tag_cache = AsyncSourceTagCache(async_client) + with pytest.raises(InvalidRequestError, match=ErrorCode.MISSING_NAME.error_message): + await source_tag_cache.get_by_name("") + + +@patch.object(AsyncSourceTagCache, "lookup_by_name") +@pytest.mark.asyncio +async def test_get_by_name_with_no_invalid_request_error( + mock_lookup_by_name, mock_async_source_tag_cache, async_client: AsyncAtlanClient +): + test_name = SourceTagName( + client=async_client, tag="snowflake/test@@DB/SCHEMA/TEST_TAG" + ) + with pytest.raises( + NotFoundError, + match=ErrorCode.ASSET_NOT_FOUND_BY_NAME.error_message.format( + SourceTagName._TYPE_NAME, + test_name, + ), + ): + await mock_async_source_tag_cache.get_by_name(test_name) + + +@patch.object(AsyncSourceTagCache, "lookup_by_guid") +@pytest.mark.asyncio +async def test_get_by_guid(mock_lookup_by_guid, mock_async_source_tag_cache): + test_guid = "test-guid-123" + test_qn = "test-qualified-name" + conn = Connection() + conn.guid = test_guid + conn.qualified_name = test_qn + test_asset = conn + + mock_guid_to_asset = Mock() + mock_name_to_guid = Mock() + mock_qualified_name_to_guid = Mock() + + # 1 - Not found in the cache, triggers a lookup call + # 2, 3, 4 - Uses the cached entry from the map + mock_guid_to_asset.get.side_effect = [ + None, + test_asset, + test_asset, + test_asset, + ] + mock_name_to_guid.get.side_effect = [test_guid, test_guid, test_guid, test_guid] + mock_qualified_name_to_guid.get.side_effect = [ + test_guid, + test_guid, + test_guid, + test_guid, + ] + + # Assign mock caches to the return value of get_cache + mock_async_source_tag_cache.guid_to_asset = mock_guid_to_asset + mock_async_source_tag_cache.name_to_guid = mock_name_to_guid + mock_async_source_tag_cache.qualified_name_to_guid = mock_qualified_name_to_guid + + connection = await mock_async_source_tag_cache.get_by_guid(test_guid) + + # Multiple calls with the same GUID result in no additional API lookups + # as the object is already cached + connection = await mock_async_source_tag_cache.get_by_guid(test_guid) + connection = await mock_async_source_tag_cache.get_by_guid(test_guid) + + assert test_guid == connection.guid + assert test_qn == connection.qualified_name + + # The method is called four times, but the lookup is triggered only once + assert mock_guid_to_asset.get.call_count == 4 + mock_lookup_by_guid.assert_called_once() + + +@patch.object(AsyncSourceTagCache, "lookup_by_guid") +@patch.object(AsyncSourceTagCache, "lookup_by_qualified_name") +@pytest.mark.asyncio +async def test_get_by_qualified_name( + mock_lookup_by_qn, mock_lookup_by_guid, mock_async_source_tag_cache +): + test_guid = "test-guid-123" + test_qn = "test-qualified-name" + conn = Connection() + conn.guid = test_guid + conn.qualified_name = test_qn + test_asset = conn + + mock_guid_to_asset = Mock() + mock_name_to_guid = Mock() + mock_qualified_name_to_guid = Mock() + + # 1 - Not found in the cache, triggers a lookup call + # 2, 3, 4 - Uses the cached entry from the map + mock_qualified_name_to_guid.get.side_effect = [ + None, + test_guid, + test_guid, + test_guid, + ] + + # Other caches will be populated once + # the lookup call for get_by_qualified_name is made + mock_guid_to_asset.get.side_effect = [ + test_asset, + test_asset, + test_asset, + test_asset, + ] + mock_name_to_guid.get.side_effect = [test_guid, test_guid, test_guid, test_guid] + + mock_async_source_tag_cache.guid_to_asset = mock_guid_to_asset + mock_async_source_tag_cache.name_to_guid = mock_name_to_guid + mock_async_source_tag_cache.qualified_name_to_guid = mock_qualified_name_to_guid + + connection = await mock_async_source_tag_cache.get_by_qualified_name(test_qn) + + # Multiple calls with the same + # qualified name result in no additional API lookups + # as the object is already cached + connection = await mock_async_source_tag_cache.get_by_qualified_name(test_qn) + connection = await mock_async_source_tag_cache.get_by_qualified_name(test_qn) + + assert test_guid == connection.guid + assert test_qn == connection.qualified_name + + # The method is called three times + # but the lookup is triggered only once + assert mock_qualified_name_to_guid.get.call_count == 4 + mock_lookup_by_qn.assert_called_once() + + # No call to guid lookup since the object is already in the cache + assert mock_lookup_by_guid.get.call_count == 0 + + +@patch.object(AsyncSourceTagCache, "lookup_by_guid") +@patch.object(AsyncSourceTagCache, "lookup_by_name") +@pytest.mark.asyncio +async def test_get_by_name( + mock_lookup_by_name, + mock_lookup_by_guid, + mock_async_source_tag_cache, + async_client: AsyncAtlanClient, +): + test_name = SourceTagName( + client=async_client, tag="snowflake/test@@DB/SCHEMA/TEST_TAG" + ) + test_guid = "test-guid-123" + test_qn = "test-qualified-name" + conn = Connection() + conn.guid = test_guid + conn.qualified_name = test_qn + test_asset = conn + + mock_guid_to_asset = Mock() + mock_name_to_guid = Mock() + mock_qualified_name_to_guid = Mock() + + # 1 - Not found in the cache, triggers a lookup call + # 2, 3, 4 - Uses the cached entry from the map + mock_name_to_guid.get.side_effect = [ + None, + test_guid, + test_guid, + test_guid, + ] + + # Other caches will be populated once + # the lookup call for get_by_qualified_name is made + mock_guid_to_asset.get.side_effect = [ + test_asset, + test_asset, + test_asset, + test_asset, + ] + mock_qualified_name_to_guid.get.side_effect = [ + test_guid, + test_guid, + test_guid, + test_guid, + ] + + mock_async_source_tag_cache.guid_to_asset = mock_guid_to_asset + mock_async_source_tag_cache.name_to_guid = mock_name_to_guid + mock_async_source_tag_cache.qualified_name_to_guid = mock_qualified_name_to_guid + + connection = await mock_async_source_tag_cache.get_by_name(test_name) + + # Multiple calls with the same + # qualified name result in no additional API lookups + # as the object is already cached + connection = await mock_async_source_tag_cache.get_by_name(test_name) + connection = await mock_async_source_tag_cache.get_by_name(test_name) + + assert test_guid == connection.guid + assert test_qn == connection.qualified_name + + # The method is called four times + # but the lookup is triggered only once + assert mock_name_to_guid.get.call_count == 4 + mock_lookup_by_name.assert_called_once() + + # No call to guid lookup since the object is already in the cache + assert mock_lookup_by_guid.call_count == 0 diff --git a/tests_v9/unit/aio/test_sso_client.py b/tests_v9/unit/aio/test_sso_client.py new file mode 100644 index 000000000..ac65df0ba --- /dev/null +++ b/tests_v9/unit/aio/test_sso_client.py @@ -0,0 +1,314 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. +from json import load +from pathlib import Path +from re import escape +from unittest.mock import AsyncMock, Mock + +import msgspec +import pytest + +from pyatlan.client.common import AsyncApiCaller +from pyatlan_v9.client.aio.sso import V9AsyncSSOClient as AsyncSSOClient +from pyatlan_v9.errors import InvalidRequestError +from pyatlan_v9.model.group import AtlanGroup + + +def _strip_none(d): + """Recursively remove None values from dicts for fixture comparison.""" + if isinstance(d, dict): + return {k: _strip_none(v) for k, v in d.items() if v is not None} + elif isinstance(d, list): + return [_strip_none(i) for i in d] + return d + + +def _to_dict(obj): + """Convert response to dict for comparison with camelCase JSON fixtures.""" + if isinstance(obj, msgspec.Struct): + return _strip_none(msgspec.to_builtins(obj)) + elif hasattr(obj, "dict"): + return obj.dict(by_alias=True, exclude_none=True) + else: + return obj + + +TEST_DATA_DIR = Path(__file__).parent.parent.parent.parent / "tests" / "unit" / "data" +SSO_GET_GROUP_MAPPING_JSON = "get_group_mapping.json" +SSO_GET_ALL_GROUP_MAPPING_JSON = "get_all_group_mapping.json" +SSO_CREATE_GROUP_MAPPING_JSON = "create_group_mapping.json" +SSO_UPDATE_GROUP_MAPPING_JSON = "update_group_mapping.json" +SSO_RESPONSES_DIR = TEST_DATA_DIR / "sso_responses" + + +def load_json(respones_dir, filename): + with (respones_dir / filename).open() as input_file: + return load(input_file) + + +def to_json(model): + return model.json(by_alias=True, exclude_none=True) + + +@pytest.fixture(autouse=True) +def set_env(monkeypatch): + monkeypatch.setenv("ATLAN_BASE_URL", "https://test.atlan.com") + monkeypatch.setenv("ATLAN_API_KEY", "test-api-key") + + +@pytest.fixture(scope="function") +def mock_async_api_caller(): + mock_caller = Mock(spec=AsyncApiCaller) + mock_caller._call_api = AsyncMock() + return mock_caller + + +@pytest.fixture() +def get_group_mapping_json(): + return load_json(SSO_RESPONSES_DIR, SSO_GET_GROUP_MAPPING_JSON) + + +@pytest.fixture() +def get_all_group_mapping_json(): + return load_json(SSO_RESPONSES_DIR, SSO_GET_ALL_GROUP_MAPPING_JSON) + + +@pytest.fixture() +def create_group_mapping_json(): + return load_json(SSO_RESPONSES_DIR, SSO_CREATE_GROUP_MAPPING_JSON) + + +@pytest.fixture() +def update_group_mapping_json(): + return load_json(SSO_RESPONSES_DIR, SSO_UPDATE_GROUP_MAPPING_JSON) + + +@pytest.mark.parametrize("test_api_caller", ["abc", None]) +def test_init_when_wrong_class_raises_exception(test_api_caller): + with pytest.raises( + InvalidRequestError, + match="ATLAN-PYTHON-400-048 Invalid parameter type for client should be AsyncApiCaller", + ): + AsyncSSOClient(test_api_caller) + + +@pytest.mark.parametrize( + "sso_alias, group_map_id, error_msg", + [ + [None, "map-id", "none is not an allowed value"], + ["auth0", None, "none is not an allowed value"], + [[123], "map-id", "so_alias\n str type expected"], + ["azure", [123], "group_map_id\n str type expected"], + ], +) +def test_sso_get_group_mapping_wrong_params_raises_validation_error( + sso_alias, group_map_id, error_msg +): + with pytest.raises(ValueError) as err: + AsyncSSOClient.get_group_mapping(sso_alias=sso_alias, group_map_id=group_map_id) + assert error_msg in str(err.value) + + +@pytest.mark.parametrize( + "sso_alias, error_msg", + [ + [None, "none is not an allowed value"], + [[123], "so_alias\n str type expected"], + ], +) +def test_sso_get_all_group_mapping_wrong_params_raises_validation_error( + sso_alias, error_msg +): + with pytest.raises(ValueError, match=error_msg): + AsyncSSOClient.get_all_group_mappings(sso_alias=sso_alias) + + +@pytest.mark.parametrize( + "sso_alias, atlan_group, sso_group_name, error_msg", + [ + [None, "atlan-group", "sso-group", "none is not an allowed value"], + ["auth0", None, "sso-group", "none is not an allowed value"], + ["auth0", "atlan-group", None, "none is not an allowed value"], + [[123], "atlan-group", "sso-group", "so_alias\n str type expected"], + ["auth0", [123], "sso-group", "atlan_group\n instance of AtlanGroup expected"], + ["auth0", AtlanGroup(), [123], "sso_group_name\n str type expected"], + ], +) +def test_sso_create_group_mapping_wrong_params_raises_validation_error( + sso_alias, atlan_group, sso_group_name, error_msg +): + with pytest.raises(ValueError, match=error_msg): + AsyncSSOClient.create_group_mapping( + sso_alias=sso_alias, atlan_group=atlan_group, sso_group_name=sso_group_name + ) + + +@pytest.mark.parametrize( + "sso_alias, atlan_group, group_map_id, sso_group_name, error_msg", + [ + [None, "atlan-group", "map-id", "sso-group", "none is not an allowed value"], + ["auth0", None, "map-id", "sso-group", "none is not an allowed value"], + ["auth0", "atlan-group", None, "sso-group", "none is not an allowed value"], + ["auth0", "atlan-group", "map-id", None, "none is not an allowed value"], + ["auth0", "atlan-group", "map-id", None, "none is not an allowed value"], + [[123], "atlan-group", "map-id", "sso-group", "sso_alias\n str type expected"], + [ + "auth0", + [123], + "map-id", + "sso-group", + "atlan_group\n instance of AtlanGroup expected", + ], + [ + "auth0", + "atlan-group", + [123], + "sso-group", + "group_map_id\n str type expected", + ], + [ + "auth0", + "atlan-group", + "map-id", + [123], + "sso_group_name\n str type expected", + ], + ], +) +def test_sso_update_group_mapping_wrong_params_raises_validation_error( + sso_alias, atlan_group, group_map_id, sso_group_name, error_msg +): + with pytest.raises(ValueError, match=error_msg): + AsyncSSOClient.update_group_mapping( + sso_alias=sso_alias, + atlan_group=atlan_group, + group_map_id=group_map_id, + sso_group_name=sso_group_name, + ) + + +@pytest.mark.parametrize( + "sso_alias, group_map_id, error_msg", + [ + [None, "map-id", "none is not an allowed value"], + ["auth0", None, "none is not an allowed value"], + [[123], "map-id", "so_alias\n str type expected"], + ["azure", [123], "group_map_id\n str type expected"], + ], +) +def test_sso_delete_group_mapping_wrong_params_raises_validation_error( + sso_alias, group_map_id, error_msg +): + with pytest.raises(ValueError, match=error_msg): + AsyncSSOClient.delete_group_mapping( + sso_alias=sso_alias, group_map_id=group_map_id + ) + + +@pytest.mark.asyncio +async def test_sso_get_group_mapping( + mock_async_api_caller, + get_group_mapping_json, +): + mock_async_api_caller._call_api.return_value = get_group_mapping_json + client = AsyncSSOClient(client=mock_async_api_caller) + response = await client.get_group_mapping(sso_alias="auth0", group_map_id="1234") + assert _to_dict(response) == get_group_mapping_json + assert mock_async_api_caller._call_api.call_count == 1 + mock_async_api_caller.reset_mock() + + +@pytest.mark.asyncio +async def test_sso_get_all_group_mapping( + mock_async_api_caller, + get_all_group_mapping_json, +): + mock_async_api_caller._call_api.return_value = get_all_group_mapping_json + client = AsyncSSOClient(client=mock_async_api_caller) + response = await client.get_all_group_mappings(sso_alias="auth0") + assert len(response) == 1 + assert response[0].identity_provider_mapper == "saml-group-idp-mapper" + assert response[0].name == get_all_group_mapping_json[2]["name"] + assert mock_async_api_caller._call_api.call_count == 1 + mock_async_api_caller.reset_mock() + + +@pytest.mark.asyncio +async def test_sso_create_group_mapping_invalid_request_error( + mock_async_api_caller, get_all_group_mapping_json, create_group_mapping_json +): + mock_async_api_caller._call_api.side_effect = [ + get_all_group_mapping_json, + create_group_mapping_json, + ] + existing_atlan_group = AtlanGroup() + existing_atlan_group.alias = "existing_atlan_group" + existing_atlan_group.id = "atlan-group-guid-1234" + client = AsyncSSOClient(client=mock_async_api_caller) + expected_error = escape( + ( + f"ATLAN-PYTHON-400-058 SSO group mapping already exists between " + f"{existing_atlan_group.alias} (Atlan group) <-> test-sso-group (SSO group)" + ) + ) + with pytest.raises(InvalidRequestError, match=expected_error): + await client.create_group_mapping( + sso_alias="auth0", + atlan_group=existing_atlan_group, + sso_group_name="sso-group", + ) + assert mock_async_api_caller._call_api.call_count == 1 + mock_async_api_caller.reset_mock() + + +@pytest.mark.asyncio +async def test_sso_create_group_mapping( + mock_async_api_caller, get_all_group_mapping_json, create_group_mapping_json +): + mock_async_api_caller._call_api.side_effect = [ + get_all_group_mapping_json, + create_group_mapping_json, + ] + # Group that doesn't exist in sso group mappings + atlan_group = AtlanGroup() + atlan_group.id = "atlan-group-new-mapping-guid-1234" + client = AsyncSSOClient(client=mock_async_api_caller) + response = await client.create_group_mapping( + sso_alias="auth0", + atlan_group=atlan_group, + sso_group_name="sso-group", + ) + assert _to_dict(response) == create_group_mapping_json + assert mock_async_api_caller._call_api.call_count == 2 + mock_async_api_caller.reset_mock() + + +@pytest.mark.asyncio +async def test_sso_update_group_mapping( + mock_async_api_caller, update_group_mapping_json +): + mock_async_api_caller._call_api.return_value = update_group_mapping_json + client = AsyncSSOClient(client=mock_async_api_caller) + response = await client.update_group_mapping( + sso_alias="auth0", + atlan_group=AtlanGroup(), + group_map_id="group-map-id", + group_map_name="group-map-name", + sso_group_name="sso-group", + ) + assert _to_dict(response) == update_group_mapping_json + assert mock_async_api_caller._call_api.call_count == 1 + mock_async_api_caller.reset_mock() + + +@pytest.mark.asyncio +async def test_sso_delete_group_mapping(mock_async_api_caller): + mock_async_api_caller._call_api.return_value = None + client = AsyncSSOClient(client=mock_async_api_caller) + response = await client.delete_group_mapping( + sso_alias="auth0", + group_map_id="group-map-id", + ) + assert response is None + assert mock_async_api_caller._call_api.call_count == 1 + mock_async_api_caller.reset_mock() diff --git a/tests_v9/unit/aio/test_task_client.py b/tests_v9/unit/aio/test_task_client.py new file mode 100644 index 000000000..cda06aba7 --- /dev/null +++ b/tests_v9/unit/aio/test_task_client.py @@ -0,0 +1,140 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. +from json import load, loads +from pathlib import Path +from unittest.mock import AsyncMock, Mock + +import pytest + +from pyatlan.client.common import AsyncApiCaller +from pyatlan_v9.client.aio.task import V9AsyncTaskClient as AsyncTaskClient +from pyatlan_v9.errors import InvalidRequestError +from pyatlan_v9.model.aio.task import AsyncTaskSearchResponse +from pyatlan_v9.model.enums import AtlanTaskStatus, AtlanTaskType +from pyatlan_v9.model.fluent_tasks import FluentTasks +from pyatlan_v9.model.task import AtlanTask, TaskSearchRequest + +TEST_DATA_DIR = Path(__file__).parent.parent.parent.parent / "tests" / "unit" / "data" +TASK_SEARCH_JSON = "task_search.json" +TASK_RESPONSES_DIR = TEST_DATA_DIR / "task_responses" +FLUENT_TASKS_REQUEST_JSON = "fluent_tasks.json" +TASK_REQUESTS_DIR = TEST_DATA_DIR / "task_requests" + + +def load_json(respones_dir, filename): + with (respones_dir / filename).open() as input_file: + return load(input_file) + + +def to_json(model): + return model.json(by_alias=True, exclude_none=True) + + +@pytest.fixture(autouse=True) +def set_env(monkeypatch): + monkeypatch.setenv("ATLAN_BASE_URL", "https://test.atlan.com") + monkeypatch.setenv("ATLAN_API_KEY", "test-api-key") + + +@pytest.fixture(scope="function") +def mock_async_api_caller(): + mock_caller = Mock(spec=AsyncApiCaller) + mock_caller._call_api = AsyncMock() + return mock_caller + + +@pytest.fixture() +def task_search_request() -> TaskSearchRequest: + return ( + FluentTasks() + .page_size(1) + .where(AtlanTask.STATUS.match(AtlanTaskStatus.COMPLETE.value)) + .to_request() + ) + + +@pytest.fixture() +def task_search_response_json(): + return load_json(TASK_RESPONSES_DIR, TASK_SEARCH_JSON) + + +@pytest.fixture() +def task_search_request_json(): + return load_json(TASK_REQUESTS_DIR, FLUENT_TASKS_REQUEST_JSON) + + +@pytest.mark.parametrize("test_api_caller", ["abc", None]) +def test_init_when_wrong_class_raises_exception(test_api_caller): + with pytest.raises( + InvalidRequestError, + match="ATLAN-PYTHON-400-048 Invalid parameter type for client should be AsyncApiCaller", + ): + AsyncTaskClient(test_api_caller) + + +@pytest.mark.parametrize( + "test_request, error_msg", + [ + [None, "none is not an allowed value"], + ["123", "instance of TaskSearchRequest expected"], + ], +) +def test_task_search_wrong_params_raises_validation_error( + test_request, error_msg, mock_async_api_caller +): + client = AsyncTaskClient(client=mock_async_api_caller) + with pytest.raises(ValueError) as err: + client.search(request=test_request) + assert error_msg in str(err.value) + + +@pytest.mark.parametrize( + "test_method, test_client", + [["count", [None, 123, "abc"]], ["execute", [None, 123, "abc"]]], +) +def test_fluent_tasks_invalid_client_raises_invalid_request_error( + test_method, + test_client, +): + client_method = getattr(FluentTasks(), test_method) + for invalid_client in test_client: + with pytest.raises( + InvalidRequestError, match="No Atlan client has been provided." + ): + client_method(client=invalid_client) + + +@pytest.mark.asyncio +async def test_task_search_get_when_given_request( + mock_async_api_caller, + task_search_request, + task_search_request_json: TaskSearchRequest, + task_search_response_json: AsyncTaskSearchResponse, +): + last_page_response = {"tasks": [], "approximateCount": 1} + mock_async_api_caller._call_api.side_effect = [ + task_search_response_json, + last_page_response, + ] + client = AsyncTaskClient(client=mock_async_api_caller) + response = await client.search(request=task_search_request) + request_dsl_json = to_json(response._criteria) + + assert loads(request_dsl_json) == task_search_request_json + assert response + assert response.count == 1 + async for task in response: + assert task.guid + assert task.end_time + assert task.start_time + assert task.updated_time + assert task.created_by + assert task.parameters + assert task.attempt_count == 0 + assert task.entity_guid + assert task.time_taken_in_seconds + assert task.parameters.get("__task_classificationTypeName") + assert task.status == AtlanTaskStatus.COMPLETE + assert task.type == AtlanTaskType.CLASSIFICATION_PROPAGATION_ADD + assert mock_async_api_caller._call_api.call_count == 2 + mock_async_api_caller.reset_mock() diff --git a/tests_v9/unit/aio/test_workflow_client.py b/tests_v9/unit/aio/test_workflow_client.py new file mode 100644 index 000000000..8d2d92345 --- /dev/null +++ b/tests_v9/unit/aio/test_workflow_client.py @@ -0,0 +1,1061 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2022 Atlan Pte. Ltd. +from unittest.mock import AsyncMock, Mock, patch + +import msgspec +import pytest + +from pyatlan.client.common import AsyncApiCaller +from pyatlan.client.constants import ( + SCHEDULE_QUERY_WORKFLOWS_MISSED, + SCHEDULE_QUERY_WORKFLOWS_SEARCH, + WORKFLOW_INDEX_RUN_SEARCH, + WORKFLOW_INDEX_SEARCH, +) +from pyatlan_v9.client.aio.atlan import AsyncAtlanClient +from pyatlan_v9.client.aio.workflow import V9AsyncWorkflowClient as AsyncWorkflowClient +from pyatlan_v9.errors import InvalidRequestError +from pyatlan_v9.model.aio.workflow import AsyncWorkflowSearchResponse +from pyatlan_v9.model.enums import AtlanWorkflowPhase, WorkflowPackage +from pyatlan_v9.model.workflow import ( + PackageParameter, + ScheduleQueriesSearchRequest, + Workflow, + WorkflowMetadata, + WorkflowResponse, + WorkflowRunResponse, + WorkflowSchedule, + WorkflowScheduleResponse, + WorkflowScheduleSpec, + WorkflowScheduleStatus, + WorkflowSearchHits, + WorkflowSearchRequest, + WorkflowSearchResponse, + WorkflowSearchResult, + WorkflowSearchResultDetail, + WorkflowSearchResultStatus, + WorkflowSpec, +) +from tests_v9.unit.constants import TEST_WORKFLOW_CLIENT_METHODS + + +def _to_dict(model): + """Convert a msgspec.Struct model to a plain dict (for mock return values).""" + return msgspec.to_builtins(model) + + +def _expected(obj, typ): + """Build expected value from fixture via round-trip (matches client conversion).""" + return msgspec.convert(_to_dict(obj), typ, strict=False) + + +@pytest.fixture(autouse=True) +def set_env(monkeypatch): + monkeypatch.setenv("ATLAN_BASE_URL", "https://test.atlan.com") + monkeypatch.setenv("ATLAN_API_KEY", "test-api-key") + + +@pytest.fixture() +def mock_api_caller(): + mock = Mock(spec=AsyncApiCaller) + # Add role_cache attribute to the mock + mock.role_cache = Mock() + mock.role_cache.is_api_token_user = AsyncMock( + return_value=False + ) # Default to non-API token user + return mock + + +@pytest.fixture() +def mock_workflow_time_sleep(): + with patch("asyncio.sleep") as mock_time_sleep: + yield mock_time_sleep + + +@pytest.fixture() +def async_client(mock_api_caller) -> AsyncWorkflowClient: + return AsyncWorkflowClient(mock_api_caller) + + +@pytest.fixture() +def search_result_status() -> WorkflowSearchResultStatus: + return WorkflowSearchResultStatus(phase=AtlanWorkflowPhase.RUNNING) + + +@pytest.fixture() +def search_result_detail( + search_result_status: WorkflowSearchResultStatus, +) -> WorkflowSearchResultDetail: + return WorkflowSearchResultDetail( + api_version="1", + kind="kind", + metadata=WorkflowMetadata(name="name", namespace="namespace"), + spec=WorkflowSpec(), + status=search_result_status, + ) + + +@pytest.fixture() +def search_result(search_result_detail) -> WorkflowSearchResult: + return WorkflowSearchResult( + index="index", + type="type", + id="id", + seq_no=1, + primary_term=2, + sort=["sort"], + source=search_result_detail, + ) # type: ignore[call-arg] + + +@pytest.fixture() +def search_response(search_result: WorkflowSearchResult) -> WorkflowSearchResponse: + return WorkflowSearchResponse( + hits=WorkflowSearchHits(total={"dummy": "dummy"}, hits=[search_result]), + shards={"dummy": "dummy"}, + ) # type: ignore[call-arg] + + +@pytest.fixture() +def rerun_response() -> WorkflowRunResponse: + return WorkflowRunResponse( + status=WorkflowSearchResultStatus(), + metadata=WorkflowMetadata(name="name", namespace="namespace"), + spec=WorkflowSpec(), + ) + + +@pytest.fixture() +def rerun_response_with_idempotent( + search_result_status: WorkflowSearchResultStatus, +) -> WorkflowRunResponse: + return WorkflowRunResponse( + metadata=WorkflowMetadata(name="name", namespace="namespace"), + spec=WorkflowSpec(), + status=search_result_status, + ) + + +@pytest.fixture() +def workflow_response() -> WorkflowResponse: + return WorkflowResponse( + metadata=WorkflowMetadata(name="name", namespace="namespace"), + spec=WorkflowSpec(), + payload=[PackageParameter(parameter="test-param", type="test-type", body={})], + ) + + +@pytest.fixture() +def workflow_run_response() -> WorkflowRunResponse: + return WorkflowRunResponse( + metadata=WorkflowMetadata(name="name", namespace="namespace"), + spec=WorkflowSpec(), + payload=[PackageParameter(parameter="test-param", type="test-type", body={})], + status=WorkflowSearchResultStatus(phase=AtlanWorkflowPhase.RUNNING), + ) + + +@pytest.fixture() +def schedule() -> WorkflowSchedule: + return WorkflowSchedule(timezone="Europe/Paris", cron_schedule="45 4 * * *") + + +@pytest.fixture() +def schedule_response() -> WorkflowScheduleResponse: + return WorkflowScheduleResponse( + spec=WorkflowScheduleSpec(), + metadata=WorkflowMetadata(name="name", namespace="namespace"), + workflow_metadata=WorkflowMetadata(name="name", namespace="namespace"), + status=WorkflowScheduleStatus( + active="test-active", + conditions="test-conditions", + last_scheduled_time="test-last-scheduled-time", + ), + ) + + +@pytest.fixture() +def update_response() -> WorkflowResponse: + return WorkflowResponse( + metadata=WorkflowMetadata(name="name", namespace="namespace"), + spec=WorkflowSpec(), + ) + + +# v9 AsyncWorkflowClient uses "updater" instead of "update" +_V9_METHOD_ALIAS = {"update": "updater"} + + +@pytest.mark.parametrize("method, params", TEST_WORKFLOW_CLIENT_METHODS.items()) +@pytest.mark.asyncio +async def test_async_workflow_client_methods_validation_error(method, params): + v9_method = _V9_METHOD_ALIAS.get(method, method) + client_method = getattr(AsyncAtlanClient().workflow, v9_method) + for param_values, error_msg in params: + with pytest.raises(ValueError, match=error_msg): + await client_method(*param_values) + + +@pytest.mark.parametrize("workflow", ["abc", None]) +@pytest.mark.asyncio +async def test_workflow_rerun_invalid_request_error(async_client, workflow): + with pytest.raises( + InvalidRequestError, + match=( + "ATLAN-PYTHON-400-048 Invalid parameter type for workflow should " + "be WorkflowPackage, WorkflowSearchResultDetail or WorkflowSearchResult. " + "Suggestion: Check that you have used the correct type of parameter." + ), + ): + await async_client.rerun(workflow) + + +@pytest.mark.parametrize("workflow, workflow_schedule", [[None, 123], [123, "123"]]) +@pytest.mark.asyncio +async def test_workflow_run_invalid_request_error( + async_client, workflow, workflow_schedule +): + with pytest.raises( + InvalidRequestError, + match=( + "ATLAN-PYTHON-400-048 Invalid parameter type for workflow should be Workflow or str. " + "Suggestion: Check that you have used the correct type of parameter." + ), + ): + await async_client.run(workflow) + + valid_workflow = Workflow( + metadata=WorkflowMetadata(name="name", namespace="namespace"), + spec=WorkflowSpec(), + payload=[PackageParameter(parameter="test-param", type="test-type", body={})], + ) # type: ignore[call-arg] + + with pytest.raises( + InvalidRequestError, + match=( + "ATLAN-PYTHON-400-048 Invalid parameter type for workflow_schedule should be WorkflowSchedule or None. " + "Suggestion: Check that you have used the correct type of parameter." + ), + ): + await async_client.run(valid_workflow, workflow_schedule) + + +@pytest.mark.parametrize( + "workflow, schedule", + [ + ("abc", WorkflowSchedule(timezone="atlan", cron_schedule="*")), + (None, WorkflowSchedule(timezone="atlan", cron_schedule="*")), + ], +) +@pytest.mark.asyncio +async def test_workflow_add_schedule_invalid_request_error( + async_client, workflow, schedule +): + with pytest.raises( + InvalidRequestError, + match=( + "ATLAN-PYTHON-400-048 Invalid parameter type for workflow should " + "be WorkflowResponse, WorkflowPackage, WorkflowSearchResult or WorkflowSearchResultDetail. " + "Suggestion: Check that you have used the correct type of parameter." + ), + ): + await async_client.add_schedule(workflow, schedule) + + +@pytest.mark.parametrize( + "workflow", + [ + "abc", + None, + ], +) +@pytest.mark.asyncio +async def test_workflow_remove_schedule_invalid_request_error(async_client, workflow): + with pytest.raises( + InvalidRequestError, + match=( + "ATLAN-PYTHON-400-048 Invalid parameter type for workflow should " + "be WorkflowResponse, WorkflowPackage, WorkflowSearchResult or WorkflowSearchResultDetail. " + "Suggestion: Check that you have used the correct type of parameter." + ), + ): + await async_client.remove_schedule(workflow) + + +@pytest.mark.parametrize("api_caller", ["abc", None]) +@pytest.mark.asyncio +async def test_init_when_wrong_class_raises_exception(api_caller): + with pytest.raises( + InvalidRequestError, + match="ATLAN-PYTHON-400-048 Invalid parameter type for client should be AsyncApiCaller", + ): + AsyncWorkflowClient(api_caller) + + +@pytest.mark.asyncio +async def test_find_by_type(async_client: AsyncWorkflowClient, mock_api_caller): + raw_json = {"shards": {"dummy": None}, "hits": {"total": {"dummy": None}}} + mock_api_caller._call_api.return_value = raw_json + + assert await async_client.find_by_type(prefix=WorkflowPackage.FIVETRAN) == [] + mock_api_caller._call_api.assert_called_once() + assert mock_api_caller._call_api.call_args.args[0] == WORKFLOW_INDEX_SEARCH + assert isinstance( + mock_api_caller._call_api.call_args.kwargs["request_obj"], WorkflowSearchRequest + ) + + +@pytest.mark.asyncio +async def test_find_runs_by_status_and_time_range( + async_client: AsyncWorkflowClient, mock_api_caller +): + raw_json = {"_shards": {"dummy": None}, "hits": {"total": {"dummy": None}}} + mock_api_caller._call_api.return_value = raw_json + + status = [AtlanWorkflowPhase.SUCCESS, AtlanWorkflowPhase.FAILED] + started_at = "now-2h" + finished_at = "now-1h" + response = await async_client.find_runs_by_status_and_time_range( + status=status, + started_at=started_at, + finished_at=finished_at, + from_=10, + size=5, + ) + expected = msgspec.convert(raw_json, AsyncWorkflowSearchResponse, strict=False) + assert response.hits == expected.hits + assert response.shards == expected.shards + mock_api_caller._call_api.assert_called_once() + request_obj = mock_api_caller._call_api.call_args.kwargs["request_obj"] + assert isinstance(request_obj, WorkflowSearchRequest) + assert request_obj.query + # v9 WorkflowSearchRequest stores query as dict: {"bool": {"must": [...]}} + query_dict = request_obj.query + must = ( + query_dict.get("bool", {}).get("must", []) + if isinstance(query_dict, dict) + else [] + ) + range_filters = [] + for clause in must: + if isinstance(clause, dict) and "range" in clause: + for field, params in clause["range"].items(): + range_filters.append({"field": field, **params}) + assert any( + rf.get("field") == "status.startedAt" and rf.get("gte") == started_at + for rf in range_filters + ) + finished_filters = [ + rf for rf in range_filters if rf.get("field") == "status.finishedAt" + ] + assert len(finished_filters) == 1 + finished_filter = finished_filters[0] + assert finished_filter.get("lte") == finished_at + assert finished_filter.get("gte") is None + + +@pytest.mark.asyncio +async def test_find_by_id( + async_client: AsyncWorkflowClient, + search_response: WorkflowSearchResponse, + mock_api_caller, +): + raw_json = _to_dict(search_response) + mock_api_caller._call_api.return_value = raw_json + + assert search_response.hits and search_response.hits.hits + assert ( + await async_client.find_by_id(id="atlan-snowflake-miner-1714638976") + == search_response.hits.hits[0] + ) + mock_api_caller._call_api.assert_called_once() + assert mock_api_caller._call_api.call_args.args[0] == WORKFLOW_INDEX_SEARCH + assert isinstance( + mock_api_caller._call_api.call_args.kwargs["request_obj"], WorkflowSearchRequest + ) + + +@pytest.mark.asyncio +async def test_find_run_by_id( + async_client: AsyncWorkflowClient, + search_response: WorkflowSearchResponse, + mock_api_caller, +): + raw_json = _to_dict(search_response) + mock_api_caller._call_api.return_value = raw_json + + assert search_response and search_response.hits and search_response.hits.hits + assert ( + await async_client.find_run_by_id(id="atlan-snowflake-miner-1714638976-mzdza") + == search_response.hits.hits[0] + ) + mock_api_caller._call_api.assert_called_once() + assert mock_api_caller._call_api.call_args.args[0] == WORKFLOW_INDEX_RUN_SEARCH + assert isinstance( + mock_api_caller._call_api.call_args.kwargs["request_obj"], WorkflowSearchRequest + ) + + +@pytest.mark.asyncio +async def test_re_run_when_given_workflowpackage_with_no_prior_runs_raises_invalid_request_error( + async_client: AsyncWorkflowClient, mock_api_caller +): + raw_json = {"shards": {"dummy": None}, "hits": {"total": {"dummy": None}}} + mock_api_caller._call_api.return_value = raw_json + + with pytest.raises( + InvalidRequestError, + match="ATLAN-PYTHON-400-047 No prior runs of atlan-fivetran were available.", + ): + await async_client.rerun(WorkflowPackage.FIVETRAN) + + +@pytest.mark.asyncio +async def test_re_run_when_given_workflowpackage( + async_client: AsyncWorkflowClient, + mock_api_caller, + search_response: WorkflowSearchResponse, + rerun_response: WorkflowRunResponse, +): + mock_api_caller._call_api.side_effect = [ + _to_dict(search_response), + _to_dict(rerun_response), + ] + + assert await async_client.rerun(WorkflowPackage.FIVETRAN) == rerun_response + assert mock_api_caller._call_api.call_count == 2 + mock_api_caller.reset_mock() + + +@pytest.mark.asyncio +async def test_re_run_when_given_workflowsearchresultdetail( + async_client: AsyncWorkflowClient, + mock_api_caller, + search_result_detail: WorkflowSearchResultDetail, + rerun_response: WorkflowRunResponse, +): + mock_api_caller._call_api.return_value = _to_dict(rerun_response) + + assert await async_client.rerun(workflow=search_result_detail) == rerun_response + assert mock_api_caller._call_api.call_count == 1 + mock_api_caller.reset_mock() + + +@pytest.mark.asyncio +async def test_re_run_when_given_workflowsearchresult( + async_client: AsyncWorkflowClient, + mock_api_caller, + search_result: WorkflowSearchResult, + rerun_response: WorkflowRunResponse, +): + mock_api_caller._call_api.return_value = _to_dict(rerun_response) + + assert await async_client.rerun(workflow=search_result) == rerun_response + assert mock_api_caller._call_api.call_count == 1 + mock_api_caller.reset_mock() + + +@pytest.mark.asyncio +async def test_re_run_when_given_workflowpackage_with_idempotent( + async_client: AsyncWorkflowClient, + mock_api_caller, + mock_workflow_time_sleep, + search_response: WorkflowSearchResponse, + rerun_response_with_idempotent: WorkflowRunResponse, +): + mock_api_caller._call_api.side_effect = [ + _to_dict(search_response), + _to_dict(search_response), + ] + + assert ( + await async_client.rerun(WorkflowPackage.FIVETRAN, idempotent=True) + == rerun_response_with_idempotent + ) + assert mock_api_caller._call_api.call_count == 2 + mock_api_caller.reset_mock() + + +@pytest.mark.asyncio +async def test_re_run_when_given_workflowsearchresultdetail_with_idempotent( + async_client: AsyncWorkflowClient, + mock_api_caller, + mock_workflow_time_sleep, + search_response: WorkflowSearchResponse, + search_result_detail: WorkflowSearchResultDetail, + rerun_response_with_idempotent: WorkflowRunResponse, +): + mock_api_caller._call_api.return_value = _to_dict(search_response) + + assert ( + await async_client.rerun(workflow=search_result_detail, idempotent=True) + == rerun_response_with_idempotent + ) + assert mock_api_caller._call_api.call_count == 1 + mock_api_caller.reset_mock() + + +@pytest.mark.asyncio +async def test_re_run_when_given_workflowsearchresult_with_idempotent( + async_client: AsyncWorkflowClient, + mock_api_caller, + mock_workflow_time_sleep, + search_response: WorkflowSearchResponse, + search_result: WorkflowSearchResult, + rerun_response_with_idempotent: WorkflowRunResponse, +): + mock_api_caller._call_api.return_value = _to_dict(search_response) + + assert ( + await async_client.rerun(workflow=search_result, idempotent=True) + == rerun_response_with_idempotent + ) + assert mock_api_caller._call_api.call_count == 1 + mock_api_caller.reset_mock() + + +@pytest.mark.asyncio +async def test_run_when_given_workflow( + async_client: AsyncWorkflowClient, + mock_api_caller, + workflow_response: WorkflowResponse, +): + mock_api_caller._call_api.return_value = _to_dict(workflow_response) + response = await async_client.run( + Workflow( + metadata=WorkflowMetadata(name="name", namespace="namespace"), + spec=WorkflowSpec(), + payload=[ + PackageParameter(parameter="test-param", type="test-type", body={}) + ], + ) # type: ignore[call-arg] + ) + assert response == _expected(workflow_response, WorkflowResponse) + assert mock_api_caller._call_api.call_count == 1 + mock_api_caller.reset_mock() + + +@pytest.mark.asyncio +async def test_run_when_given_workflow_json( + async_client: AsyncWorkflowClient, + mock_api_caller, + workflow_response: WorkflowResponse, +): + mock_api_caller._call_api.return_value = _to_dict(workflow_response) + workflow_json = r""" + { + "metadata": {"name": "name", "namespace": "namespace"}, + "spec": {}, + "payload": [{"parameter": "test-param", "type": "test-type", "body": {}}] + } + """ + response = await async_client.run(workflow_json) + assert response == _expected(workflow_response, WorkflowResponse) + assert mock_api_caller._call_api.call_count == 1 + mock_api_caller.reset_mock() + + +@pytest.mark.asyncio +async def test_run_when_given_workflow_with_schedule( + async_client: AsyncWorkflowClient, + schedule: WorkflowSchedule, + mock_api_caller, + workflow_response: WorkflowResponse, +): + mock_api_caller._call_api.return_value = _to_dict(workflow_response) + response = await async_client.run( + Workflow( + metadata=WorkflowMetadata(name="name", namespace="namespace"), + spec=WorkflowSpec(), + payload=[ + PackageParameter(parameter="test-param", type="test-type", body={}) + ], + ), # type: ignore[call-arg] + workflow_schedule=schedule, + ) + assert response == _expected(workflow_response, WorkflowResponse) + assert mock_api_caller._call_api.call_count == 1 + mock_api_caller.reset_mock() + + +@pytest.mark.asyncio +async def test_run_when_given_workflow_json_with_schedule( + async_client: AsyncWorkflowClient, + schedule: WorkflowSchedule, + mock_api_caller, + workflow_response: WorkflowResponse, +): + mock_api_caller._call_api.return_value = _to_dict(workflow_response) + workflow_json = r""" + { + "metadata": {"name": "name", "namespace": "namespace"}, + "spec": {}, + "payload": [{"parameter": "test-param", "type": "test-type", "body": {}}] + } + """ + response = await async_client.run(workflow_json, workflow_schedule=schedule) + assert response == _expected(workflow_response, WorkflowResponse) + assert mock_api_caller._call_api.call_count == 1 + mock_api_caller.reset_mock() + + +@pytest.mark.asyncio +async def test_update_when_given_workflow( + async_client: AsyncWorkflowClient, + mock_api_caller, + search_result: WorkflowSearchResult, + update_response: WorkflowResponse, +): + mock_api_caller._call_api.return_value = _to_dict(update_response) + assert search_result.to_workflow() + response = await async_client.updater(workflow=search_result.to_workflow()) + assert response == _expected(update_response, WorkflowResponse) + assert mock_api_caller._call_api.call_count == 1 + mock_api_caller.reset_mock() + + +@pytest.mark.asyncio +async def test_workflow_update_owner( + async_client: AsyncWorkflowClient, + mock_api_caller, + workflow_response: WorkflowResponse, +): + mock_api_caller._call_api.return_value = _to_dict(workflow_response) + response = await async_client.update_owner( + workflow_name="test-workflow", username="test-owner" + ) + + assert mock_api_caller._call_api.call_count == 1 + assert response == _expected(workflow_response, WorkflowResponse) + mock_api_caller.reset_mock() + + +@pytest.mark.asyncio +async def test_workflow_get_runs( + async_client: AsyncWorkflowClient, + mock_api_caller, + search_response: WorkflowSearchResponse, +): + mock_api_caller._call_api.return_value = _to_dict(search_response) + response = await async_client.get_runs( + workflow_name="test-workflow", + workflow_phase=AtlanWorkflowPhase.RUNNING, + ) + + assert response.hits == _expected(search_response, WorkflowSearchResponse).hits + assert response.shards == _expected(search_response, WorkflowSearchResponse).shards + assert mock_api_caller._call_api.call_count == 1 + mock_api_caller.reset_mock() + + +@pytest.mark.asyncio +async def test_workflow_stop( + async_client: AsyncWorkflowClient, + mock_api_caller, + workflow_run_response: WorkflowRunResponse, +): + mock_api_caller._call_api.return_value = _to_dict(workflow_run_response) + response = await async_client.stop(workflow_run_id="test-workflow-run-id") + + assert response == _expected(workflow_run_response, WorkflowRunResponse) + assert mock_api_caller._call_api.call_count == 1 + mock_api_caller.reset_mock() + + +@pytest.mark.asyncio +async def test_workflow_delete(async_client: AsyncWorkflowClient, mock_api_caller): + mock_api_caller._call_api.return_value = None + await async_client.delete(workflow_name="test-workflow") + assert mock_api_caller._call_api.call_count == 1 + + +@pytest.mark.asyncio +async def test_workflow_add_schedule( + async_client: AsyncWorkflowClient, + schedule: WorkflowSchedule, + workflow_response: WorkflowResponse, + search_response: WorkflowSearchResponse, + search_result: WorkflowSearchResult, + mock_api_caller, +): + # Workflow response + mock_api_caller._call_api.side_effect = [ + _to_dict(workflow_response), + ] + response = await async_client.add_schedule( + workflow=workflow_response, workflow_schedule=schedule + ) + + assert mock_api_caller._call_api.call_count == 1 + assert response == _expected(workflow_response, WorkflowResponse) + mock_api_caller.reset_mock() + + # Workflow package + mock_api_caller._call_api.side_effect = [ + _to_dict(search_response), + _to_dict(workflow_response), + ] + response = await async_client.add_schedule( + workflow=WorkflowPackage.FIVETRAN, workflow_schedule=schedule + ) + + assert mock_api_caller._call_api.call_count == 2 + assert response == _expected(workflow_response, WorkflowResponse) + mock_api_caller.reset_mock() + + # Workflow search result + mock_api_caller._call_api.side_effect = [_to_dict(workflow_response)] + response = await async_client.add_schedule( + workflow=search_result, workflow_schedule=schedule + ) + + assert mock_api_caller._call_api.call_count == 1 + assert response == _expected(workflow_response, WorkflowResponse) + mock_api_caller.reset_mock() + + +@pytest.mark.asyncio +async def test_workflow_find_schedule_query_between( + async_client: AsyncWorkflowClient, + mock_api_caller, + workflow_run_response: WorkflowRunResponse, +): + mock_api_caller._call_api.return_value = [_to_dict(workflow_run_response)] + response = await async_client.find_schedule_query_between( + ScheduleQueriesSearchRequest( + start_date="2024-05-03T16:30:00.000+05:30", + end_date="2024-05-05T00:59:00.000+05:30", + ) + ) + + assert mock_api_caller._call_api.call_count == 1 + assert ( + response + and len(response) == 1 + and response[0] == _expected(workflow_run_response, WorkflowRunResponse) + ) + # Ensure it is called by the correct API endpoint + assert ( + mock_api_caller._call_api.call_args[0][0].path + == SCHEDULE_QUERY_WORKFLOWS_SEARCH.path + ) + mock_api_caller.reset_mock() + + # Missed schedule query workflows + mock_api_caller._call_api.return_value = [_to_dict(workflow_run_response)] + response = await async_client.find_schedule_query_between( + ScheduleQueriesSearchRequest( + start_date="2024-05-03T16:30:00.000+05:30", + end_date="2024-05-05T00:59:00.000+05:30", + ), + missed=True, + ) + + assert mock_api_caller._call_api.call_count == 1 + # Ensure it is called by the correct API endpoint + assert ( + mock_api_caller._call_api.call_args[0][0].path + == SCHEDULE_QUERY_WORKFLOWS_MISSED.path + ) + assert ( + response + and len(response) == 1 + and response[0] == _expected(workflow_run_response, WorkflowRunResponse) + ) + mock_api_caller.reset_mock() + + # None response + mock_api_caller._call_api.return_value = None + response = await async_client.find_schedule_query_between( + ScheduleQueriesSearchRequest( + start_date="2024-05-03T16:30:00.000+05:30", + end_date="2024-05-05T00:59:00.000+05:30", + ) + ) + + assert mock_api_caller._call_api.call_count == 1 + assert response is None + mock_api_caller.reset_mock() + + +@pytest.mark.asyncio +async def test_workflow_find_schedule_query( + async_client: AsyncWorkflowClient, + mock_api_caller, + search_response: WorkflowSearchResponse, + search_result: WorkflowSearchResult, +): + mock_api_caller._call_api.return_value = _to_dict(search_response) + response = await async_client.find_schedule_query( + saved_query_id="test-query-id", max_results=50 + ) + + assert len(response) == 1 + assert mock_api_caller._call_api.call_count == 1 + assert response[0] == _expected(search_result, WorkflowSearchResult) + mock_api_caller.reset_mock() + + +@pytest.mark.asyncio +async def test_workflow_rerun_schedule_query_workflow( + async_client, + mock_api_caller, + workflow_run_response: WorkflowRunResponse, +): + mock_api_caller._call_api.return_value = _to_dict(workflow_run_response) + response = await async_client.re_run_schedule_query( + schedule_query_id="test-query-id" + ) + + assert mock_api_caller._call_api.call_count == 1 + assert response == _expected(workflow_run_response, WorkflowRunResponse) + + +@pytest.mark.asyncio +async def test_workflow_remove_schedule( + async_client: AsyncWorkflowClient, + workflow_response: WorkflowResponse, + search_response: WorkflowSearchResponse, + search_result: WorkflowSearchResult, + mock_api_caller, +): + # Workflow response + mock_api_caller._call_api.side_effect = [ + _to_dict(workflow_response), + ] + response = await async_client.remove_schedule(workflow=workflow_response) + + assert mock_api_caller._call_api.call_count == 1 + assert response == _expected(workflow_response, WorkflowResponse) + mock_api_caller.reset_mock() + + # Workflow package + mock_api_caller._call_api.side_effect = [ + _to_dict(search_response), + _to_dict(workflow_response), + ] + response = await async_client.remove_schedule(workflow=WorkflowPackage.FIVETRAN) + + assert mock_api_caller._call_api.call_count == 2 + assert response == _expected(workflow_response, WorkflowResponse) + mock_api_caller.reset_mock() + + # Workflow search result + mock_api_caller._call_api.side_effect = [_to_dict(workflow_response)] + response = await async_client.remove_schedule(workflow=search_result) + + assert mock_api_caller._call_api.call_count == 1 + assert response == _expected(workflow_response, WorkflowResponse) + mock_api_caller.reset_mock() + + +@pytest.mark.asyncio +async def test_workflow_get_all_scheduled_runs( + async_client: AsyncWorkflowClient, + workflow_response: WorkflowResponse, + search_response: WorkflowSearchResponse, + search_result: WorkflowSearchResult, + schedule_response: WorkflowScheduleResponse, + mock_api_caller, +): + mock_api_caller._call_api.return_value = {"items": [_to_dict(schedule_response)]} + response = await async_client.get_all_scheduled_runs() + + assert mock_api_caller._call_api.call_count == 1 + assert response and len(response) == 1 + assert response[0] == _expected(schedule_response, WorkflowScheduleResponse) + mock_api_caller.reset_mock() + + +@pytest.mark.asyncio +async def test_workflow_get_scheduled_run( + async_client: AsyncWorkflowClient, + workflow_response: WorkflowResponse, + search_response: WorkflowSearchResponse, + search_result: WorkflowSearchResult, + schedule_response: WorkflowScheduleResponse, + mock_api_caller, +): + mock_api_caller._call_api.return_value = _to_dict(schedule_response) + response = await async_client.get_scheduled_run(workflow_name="test-workflow") + + assert mock_api_caller._call_api.call_count == 1 + assert response == _expected(schedule_response, WorkflowScheduleResponse) + mock_api_caller.reset_mock() + + +# Tests for role_cache functionality +@pytest.mark.asyncio +async def test_rerun_with_role_cache_api_token_user( + async_client: AsyncWorkflowClient, + mock_api_caller, + mock_role_cache, + search_result: WorkflowSearchResult, + rerun_response: WorkflowRunResponse, +): + """Test that rerun uses package endpoint when user is API token user.""" + # Mock role_cache to return True for is_api_token_user + mock_role_cache.is_api_token_user = AsyncMock(return_value=True) + mock_api_caller.role_cache = mock_role_cache + mock_api_caller._call_api.return_value = _to_dict(rerun_response) + + response = await async_client.rerun(search_result) + + # Verify that role_cache.is_api_token_user was called + mock_role_cache.is_api_token_user.assert_called_once() + # Verify that _call_api was called (endpoint selection happens in prepare_request) + mock_api_caller._call_api.assert_called_once() + assert response == _expected(rerun_response, WorkflowRunResponse) + mock_api_caller.reset_mock() + + +@pytest.mark.asyncio +async def test_rerun_with_role_cache_non_api_token_user( + async_client: AsyncWorkflowClient, + mock_api_caller, + mock_role_cache, + search_result: WorkflowSearchResult, + rerun_response: WorkflowRunResponse, +): + """Test that rerun uses non-package endpoint when user is not API token user.""" + # Mock role_cache to return False for is_api_token_user + mock_role_cache.is_api_token_user = AsyncMock(return_value=False) + mock_api_caller.role_cache = mock_role_cache + mock_api_caller._call_api.return_value = _to_dict(rerun_response) + + response = await async_client.rerun(search_result) + + # Verify that role_cache.is_api_token_user was called + mock_role_cache.is_api_token_user.assert_called_once() + # Verify that _call_api was called (endpoint selection happens in prepare_request) + mock_api_caller._call_api.assert_called_once() + assert response == _expected(rerun_response, WorkflowRunResponse) + mock_api_caller.reset_mock() + + +@pytest.mark.asyncio +async def test_run_with_role_cache_api_token_user( + async_client: AsyncWorkflowClient, + mock_api_caller, + mock_role_cache, + workflow_response: WorkflowResponse, +): + """Test that run uses package endpoint when user is API token user.""" + # Mock role_cache to return True for is_api_token_user + mock_role_cache.is_api_token_user = AsyncMock(return_value=True) + mock_api_caller.role_cache = mock_role_cache + mock_api_caller._call_api.return_value = _to_dict(workflow_response) + + workflow = Workflow( + metadata=WorkflowMetadata(name="name", namespace="namespace"), + spec=WorkflowSpec(), + payload=[PackageParameter(parameter="test-param", type="test-type", body={})], + ) + + response = await async_client.run(workflow) + + # Verify that role_cache.is_api_token_user was called + mock_role_cache.is_api_token_user.assert_called_once() + # Verify that _call_api was called (endpoint selection happens in prepare_request) + mock_api_caller._call_api.assert_called_once() + assert response == _expected(workflow_response, WorkflowResponse) + mock_api_caller.reset_mock() + + +@pytest.mark.asyncio +async def test_update_with_role_cache_api_token_user( + async_client: AsyncWorkflowClient, + mock_api_caller, + mock_role_cache, + workflow_response: WorkflowResponse, +): + """Test that update uses package endpoint when user is API token user.""" + # Mock role_cache to return True for is_api_token_user + mock_role_cache.is_api_token_user = AsyncMock(return_value=True) + mock_api_caller.role_cache = mock_role_cache + mock_api_caller._call_api.return_value = _to_dict(workflow_response) + + workflow = Workflow( + metadata=WorkflowMetadata(name="name", namespace="namespace"), + spec=WorkflowSpec(), + payload=[PackageParameter(parameter="test-param", type="test-type", body={})], + ) + + response = await async_client.updater(workflow) + + # Verify that role_cache.is_api_token_user was called + mock_role_cache.is_api_token_user.assert_called_once() + # Verify that _call_api was called (endpoint selection happens in prepare_request) + mock_api_caller._call_api.assert_called_once() + assert response == _expected(workflow_response, WorkflowResponse) + mock_api_caller.reset_mock() + + +@pytest.mark.asyncio +async def test_delete_with_role_cache_api_token_user( + async_client: AsyncWorkflowClient, + mock_api_caller, + mock_role_cache, +): + """Test that delete uses package endpoint when user is API token user.""" + # Mock role_cache to return True for is_api_token_user + mock_role_cache.is_api_token_user = AsyncMock(return_value=True) + mock_api_caller.role_cache = mock_role_cache + mock_api_caller._call_api.return_value = None + + await async_client.delete("test-workflow-name") + + # Verify that role_cache.is_api_token_user was called + mock_role_cache.is_api_token_user.assert_called_once() + # Verify that _call_api was called (endpoint selection happens in prepare_request) + mock_api_caller._call_api.assert_called_once() + mock_api_caller.reset_mock() + + +@pytest.mark.asyncio +async def test_add_schedule_with_role_cache_api_token_user( + async_client: AsyncWorkflowClient, + mock_api_caller, + mock_role_cache, + search_result: WorkflowSearchResult, + schedule: WorkflowSchedule, + workflow_response: WorkflowResponse, +): + """Test that add_schedule uses package endpoint when user is API token user.""" + # Mock role_cache to return True for is_api_token_user + mock_role_cache.is_api_token_user = AsyncMock(return_value=True) + mock_api_caller.role_cache = mock_role_cache + mock_api_caller._call_api.return_value = _to_dict(workflow_response) + + response = await async_client.add_schedule(search_result, schedule) + + # Verify that role_cache.is_api_token_user was called + mock_role_cache.is_api_token_user.assert_called_once() + # Verify that _call_api was called (endpoint selection happens in prepare_request) + mock_api_caller._call_api.assert_called_once() + assert response == _expected(workflow_response, WorkflowResponse) + mock_api_caller.reset_mock() + + +@pytest.mark.asyncio +async def test_remove_schedule_with_role_cache_api_token_user( + async_client: AsyncWorkflowClient, + mock_api_caller, + mock_role_cache, + search_result: WorkflowSearchResult, + workflow_response: WorkflowResponse, +): + """Test that remove_schedule uses package endpoint when user is API token user.""" + # Mock role_cache to return True for is_api_token_user + mock_role_cache.is_api_token_user = AsyncMock(return_value=True) + mock_api_caller.role_cache = mock_role_cache + mock_api_caller._call_api.return_value = _to_dict(workflow_response) + + response = await async_client.remove_schedule(search_result) + + # Verify that role_cache.is_api_token_user was called + mock_role_cache.is_api_token_user.assert_called_once() + # Verify that _call_api was called (endpoint selection happens in prepare_request) + mock_api_caller._call_api.assert_called_once() + assert response == _expected(workflow_response, WorkflowResponse) + mock_api_caller.reset_mock() diff --git a/tests_v9/unit/conftest.py b/tests_v9/unit/conftest.py new file mode 100644 index 000000000..35dda09ba --- /dev/null +++ b/tests_v9/unit/conftest.py @@ -0,0 +1,104 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +PyTest configuration and fixtures for pyatlan_v9 unit tests. +These fixtures provide test utilities for msgspec-based models. +""" + +from json import load +from pathlib import Path +from unittest.mock import patch + +import pytest + +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.model.serde import Serde, get_serde + +# Use the same test data directory as the original tests +TEST_DATA_DIR = Path(__file__).parent.parent.parent / "tests" / "unit" / "data" + + +@pytest.fixture() +def serde(): + """Provides a Serde instance for msgspec serialization/deserialization.""" + return Serde() + + +@pytest.fixture() +def shared_serde(): + """Provides the shared singleton Serde instance.""" + return get_serde() + + +def load_json(responses_dir: Path, filename: str): + """Load JSON test data from file.""" + with (responses_dir / filename).open() as input_file: + return load(input_file) + + +@pytest.fixture() +def mock_role_cache(): + """Mock the role cache on v9 AtlanClient for validation testing.""" + with patch.object(AtlanClient, "role_cache") as cache: + yield cache + + +@pytest.fixture() +def mock_user_cache(): + """Mock the user cache on v9 AtlanClient for validation testing.""" + with patch.object(AtlanClient, "user_cache") as cache: + yield cache + + +@pytest.fixture() +def mock_group_cache(): + """Mock the group cache on v9 AtlanClient for validation testing.""" + with patch.object(AtlanClient, "group_cache") as cache: + yield cache + + +@pytest.fixture() +def mock_custom_metadata_cache(): + """Mock the custom metadata cache on v9 AtlanClient for badge testing.""" + with patch.object(AtlanClient, "custom_metadata_cache") as cache: + yield cache + + +@pytest.fixture() +def mock_tag_cache(): + """Mock the atlan tag cache on v9 AtlanClient for event testing.""" + with patch.object(AtlanClient, "atlan_tag_cache") as cache: + yield cache + + +@pytest.fixture(autouse=True) +def patch_vcr_http_response_version_string(): + """ + Patch the VCRHTTPResponse class to add a version_string attribute if it doesn't exist. + + This patch is necessary to avoid bumping vcrpy to 7.0.0, + which drops support for Python 3.8. + """ + from vcr.stubs import VCRHTTPResponse # type: ignore[import-untyped] + + if not hasattr(VCRHTTPResponse, "version_string"): + VCRHTTPResponse.version_string = None + + +@pytest.fixture() +def glossary_json(): + """Load glossary test data.""" + return load_json(TEST_DATA_DIR, "glossary.json") + + +@pytest.fixture() +def glossary_term_json(): + """Load glossary term test data.""" + return load_json(TEST_DATA_DIR, "glossary_term.json") + + +@pytest.fixture() +def glossary_category_json(): + """Load glossary category test data.""" + return load_json(TEST_DATA_DIR, "glossary_category.json") diff --git a/tests_v9/unit/constants.py b/tests_v9/unit/constants.py new file mode 100644 index 000000000..73e94d677 --- /dev/null +++ b/tests_v9/unit/constants.py @@ -0,0 +1,631 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +# Non-validation constants from the legacy test suite. +from pyatlan_v9.model.assets import AtlasGlossary +from pyatlan_v9.model.enums import AtlanWorkflowPhase +from pyatlan_v9.model.workflow import ( + ScheduleQueriesSearchRequest, + WorkflowMetadata, + WorkflowResponse, + WorkflowSpec, +) +from tests.unit.constants import ( # noqa: F401, F403 + APPLICABLE_AI_ASSET_TYPES, + APPLICABLE_ASSET_TYPES, + APPLICABLE_CONNECTIONS, + APPLICABLE_DOMAIN_TYPES, + APPLICABLE_DOMAINS, + APPLICABLE_ENTITY_TYPES, + APPLICABLE_GLOSSARIES, + APPLICABLE_GLOSSARY_TYPES, + APPLICABLE_OTHER_ASSET_TYPES, + TEST_ATTRIBUTE_DEF_APPLICABLE_ASSET_TYPES, + TEST_ENUM_DEF, + TEST_STRUCT_DEF, +) +from tests.unit.model.constants import * # noqa: F401, F403 + +TEST_ASSET_CLIENT_METHODS = { + "find_personas_by_name": [ + ([[123], ["attributes"]], "name\n str type expected"), + ([None, ["attributes"]], "none is not an allowed value"), + (["name", 123], "value is not a valid list"), + ], + "find_purposes_by_name": [ + ([[123], ["attributes"]], "name\n str type expected"), + ([None, ["attributes"]], "none is not an allowed value"), + (["name", 123], "value is not a valid list"), + ], + "get_by_qualified_name": [ + ([[123], "asset-type"], "name\n str type expected"), + ([None, "asset-type"], "none is not an allowed value"), + (["qn", None], "none is not an allowed value"), + (["qn", "asset-type"], "asset_type\n a class is expected"), + ], + "get_by_guid": [ + ([[123]], "guid\n str type expected"), + ([None], "none is not an allowed value"), + ], + "retrieve_minimal": [ + ([[123]], "guid\n str type expected"), + ([None], "none is not an allowed value"), + ], + "upsert": [ + ([123], "entity\n value is not a valid Asset or List[Asset]"), + ([None], "none is not an allowed value"), + ([[123]], "entity -> 0\n instance of Asset expected"), + ([[None]], "entity -> 0\n none is not an allowed value"), + ], + "save": [ + ([123], "entity\n value is not a valid Asset or List[Asset]"), + ([None], "none is not an allowed value"), + ([[123]], "entity -> 0\n instance of Asset expected"), + ([[None]], "entity -> 0\n none is not an allowed value"), + ], + "upsert_merging_cm": [ + ([123], "entity\n value is not a valid Asset or List[Asset]"), + ([None], "none is not an allowed value"), + ([[123]], "entity -> 0\n instance of Asset expected"), + ([[None]], "entity -> 0\n none is not an allowed value"), + ], + "save_merging_cm": [ + ([123], "entity\n value is not a valid Asset or List[Asset]"), + ([None], "none is not an allowed value"), + ([[123]], "entity -> 0\n instance of Asset expected"), + ([[None]], "entity -> 0\n none is not an allowed value"), + ], + "update_merging_cm": [ + ([123], "entity\n instance of Asset expected"), + ([None], "none is not an allowed value"), + ([[123]], "entity\n instance of Asset expected"), + ([[None]], "entity\n instance of Asset expected"), + ], + "upsert_replacing_cm": [ + ([123], "entity\n value is not a valid Asset or List[Asset]"), + ([None], "none is not an allowed value"), + ([[123]], "entity -> 0\n instance of Asset expected"), + ([[None]], "entity -> 0\n none is not an allowed value"), + ], + "save_replacing_cm": [ + ([123], "entity\n value is not a valid Asset or List[Asset]"), + ([None], "none is not an allowed value"), + ([[123]], "entity -> 0\n instance of Asset expected"), + ([[None]], "entity -> 0\n none is not an allowed value"), + ], + "update_replacing_cm": [ + ([123], "entity\n instance of Asset expected"), + ([None], "none is not an allowed value"), + ([[123]], "entity\n instance of Asset expected"), + ([[None]], "entity\n instance of Asset expected"), + ], + "purge_by_guid": [ + ([int], "guid\n value is not a valid str or List[str]"), + ([None], "none is not an allowed value"), + ([[int]], "guid -> 0\n str type expected"), + ([[None]], "guid -> 0\n none is not an allowed value"), + ], + "restore": [ + (["asset-type", [123]], "name\n str type expected"), + (["asset-type", None], "none is not an allowed value"), + ([None, "qn"], "none is not an allowed value"), + (["asset-type", "qn"], "asset_type\n a class is expected"), + ], + "add_atlan_tags": [ + (["asset-type", [123], "tag-name"], "name\n str type expected"), + (["asset-type", None, "tag-name"], "none is not an allowed value"), + ([None, "qn", "tag-name"], "none is not an allowed value"), + (["asset-type", "qn", "tag-name"], "asset_type\n a class is expected"), + ([AtlasGlossary, "qn", [int]], "atlan_tag_names -> 0\n str type expected"), + ([AtlasGlossary, "qn", None], "none is not an allowed value"), + ], + "update_atlan_tags": [ + (["asset-type", [123], "tag-name"], "name\n str type expected"), + (["asset-type", None, "tag-name"], "none is not an allowed value"), + ([None, "qn", "tag-name"], "none is not an allowed value"), + (["asset-type", "qn", "tag-name"], "asset_type\n a class is expected"), + ([AtlasGlossary, "qn", [int]], "atlan_tag_names -> 0\n str type expected"), + ([AtlasGlossary, "qn", None], "none is not an allowed value"), + ], + "remove_atlan_tag": [ + (["asset-type", [123], "tag-name"], "name\n str type expected"), + (["asset-type", None, "tag-name"], "none is not an allowed value"), + ([None, "qn", "tag-name"], "none is not an allowed value"), + (["asset-type", "qn", "tag-name"], "asset_type\n a class is expected"), + ([AtlasGlossary, "qn", [123]], "atlan_tag_name\n str type expected"), + ([AtlasGlossary, "qn", None], "none is not an allowed value"), + ], + "remove_atlan_tags": [ + (["asset-type", [123], ["tag-name"]], "name\n str type expected"), + (["asset-type", None, ["tag-name"]], "none is not an allowed value"), + ([None, "qn", ["tag-name"]], "none is not an allowed value"), + (["asset-type", "qn", ["tag-name"]], "asset_type\n a class is expected"), + ( + [AtlasGlossary, "qn", "tag-name"], + "atlan_tag_names\n value is not a valid list", + ), + ([AtlasGlossary, "qn", None], "none is not an allowed value"), + ], + "update_certificate": [ + (["asset-type", [123], "name", "cert-status"], "name\n str type expected"), + ( + ["asset-type", None, "name", "cert-status"], + "none is not an allowed value", + ), + ([None, "qn", "tag-name", "cert-status"], "none is not an allowed value"), + ( + ["asset-type", "qn", "name", "cert-status"], + "asset_type\n a class is expected", + ), + ( + [AtlasGlossary, "qn", [123], "cert-status"], + "name\n str type expected", + ), + ([AtlasGlossary, "qn", None, "cert-status"], "none is not an allowed value"), + ( + [AtlasGlossary, "qn", "name", "cert-status"], + "certificate_status\n value is not a valid enumeration member", + ), + ([AtlasGlossary, "qn", "name", None], "none is not an allowed value"), + ], + "remove_certificate": [ + (["asset-type", [123], "name"], "name\n str type expected"), + ( + ["asset-type", None, "name"], + "none is not an allowed value", + ), + ([None, "qn", "tag-name"], "none is not an allowed value"), + ( + ["asset-type", "qn", "name"], + "asset_type\n a class is expected", + ), + ( + [AtlasGlossary, "qn", [123]], + "name\n str type expected", + ), + ([AtlasGlossary, "qn", None], "none is not an allowed value"), + ], + "update_announcement": [ + (["asset-type", [123], "name"], "name\n str type expected"), + ( + ["asset-type", None, "name"], + "none is not an allowed value", + ), + ([None, "qn", "tag-name", "announcement"], "none is not an allowed value"), + ( + ["asset-type", "qn", "name"], + "asset_type\n a class is expected", + ), + ( + [AtlasGlossary, "qn", [123]], + "name\n str type expected", + ), + ([AtlasGlossary, "qn", None], "none is not an allowed value"), + ], + "update_custom_metadata_attributes": [ + ([[123], ["cm"]], "guid\n str type expected"), + ([None, ["cm"]], "none is not an allowed value"), + (["name", 123], "custom_metadata\n instance of CustomMetadataDict expected"), + (["name", None], "none is not an allowed value"), + ], + "replace_custom_metadata": [ + ([[123], ["cm"]], "guid\n str type expected"), + ([None, ["cm"]], "none is not an allowed value"), + (["name", 123], "custom_metadata\n instance of CustomMetadataDict expected"), + (["name", None], "none is not an allowed value"), + ], + "remove_custom_metadata": [ + ([[123], ["cm"]], "guid\n str type expected"), + ([None, ["cm"]], "none is not an allowed value"), + (["name", [123]], "cm_name\n str type expected"), + (["name", None], "none is not an allowed value"), + ], + "append_terms": [ + (["asset-type", "terms"], "asset_type\n a class is expected"), + ([None, "cm"], "none is not an allowed value"), + ( + [AtlasGlossary, [123]], + "terms -> 0\n instance of AtlasGlossaryTerm expected", + ), + ([AtlasGlossary, None], "none is not an allowed value"), + ], + "replace_terms": [ + (["asset-type", "terms"], "asset_type\n a class is expected"), + ([None, "cm"], "none is not an allowed value"), + ( + [AtlasGlossary, [123]], + "terms -> 0\n instance of AtlasGlossaryTerm expected", + ), + ([AtlasGlossary, None], "none is not an allowed value"), + ], + "remove_terms": [ + (["asset-type", "terms"], "asset_type\n a class is expected"), + ([None, "cm"], "none is not an allowed value"), + ( + [AtlasGlossary, [123]], + "terms -> 0\n instance of AtlasGlossaryTerm expected", + ), + ([AtlasGlossary, None], "none is not an allowed value"), + ], + "find_connections_by_name": [ + ([[123], "connector-type"], "name\n str type expected"), + ([None, "connector-type"], "none is not an allowed value"), + (["name", [123]], "connector_type\n value is not a valid enumeration member"), + (["name", None], "none is not an allowed value"), + ], + "find_category_fast_by_name": [ + ([[123], "glossary-qn"], "name\n str type expected"), + ([None, "glossary-qn"], "none is not an allowed value"), + (["name", [123]], "glossary_qualified_name\n str type expected"), + (["name", None], "none is not an allowed value"), + ], + "find_term_fast_by_name": [ + ([[123], "glossary-qn"], "name\n str type expected"), + ([None, "glossary-qn"], "none is not an allowed value"), + (["name", [123]], "glossary_qualified_name\n str type expected"), + (["name", None], "none is not an allowed value"), + ], + "find_term_by_name": [ + ([[123], "glossary-qn"], "name\n str type expected"), + ([None, "glossary-qn"], "none is not an allowed value"), + (["name", [123]], "glossary_name\n str type expected"), + (["name", None], "none is not an allowed value"), + ], + "find_domain_by_name": [ + ( + [None, ["attributes"]], + "1 validation error for FindDomainByName\nname\n none is not an allowed value", + ), + ( + [" ", ["attributes"]], + "1 validation error for FindDomainByName\nname\n ensure this value has at least 1 characters", + ), + ( + ["test-domain", "attributes"], + "1 validation error for FindDomainByName\nattributes\n value is not a valid list", + ), + ], + "find_product_by_name": [ + ( + [None, ["attributes"]], + "1 validation error for FindProductByName\nname\n none is not an allowed value", + ), + ( + [" ", ["attributes"]], + "1 validation error for FindProductByName\nname\n ensure this value has at least 1 characters", + ), + ( + ["test-product", "attributes"], + "1 validation error for FindProductByName\nattributes\n value is not a valid list", + ), + ], +} + +TEST_ADMIN_CLIENT_METHODS = { + "get_keycloak_events": [ + ( + ["keycloak-req"], + "keycloak_request\n instance of KeycloakEventRequest expected", + ), + ([None], "none is not an allowed value"), + ], + "get_admin_events": [ + (["admin-req"], "admin_request\n instance of AdminEventRequest expected"), + ([None], "none is not an allowed value"), + ], +} + +TEST_AUDIT_CLIENT_METHODS = { + "search": [ + (["audit-search-req"], "criteria\n instance of AuditSearchRequest expected"), + ([None], "none is not an allowed value"), + ], +} + +TEST_GROUP_CLIENT_METHODS = { + "create": [ + ("group", "too many positional arguments"), + ([None], "none is not an allowed value"), + ], + "update": [ + (["group"], "group\n instance of AtlanGroup expected"), + ([None], "none is not an allowed value"), + ], + "purge": [ + ([[123]], "guid\n str type expected"), + ([None], "none is not an allowed value"), + ], + "get": [ + ( + [None, None, None, "count", 123], + "count\n value is not a valid boolean", + ), + ([None, None, None, None, 123], "none is not an allowed value"), + ([None, None, None, True, "offset"], "offset\n value is not a valid int"), + ([None, None, None, True, None], "none is not an allowed value"), + ], + "get_all": [ + (["limit"], "limit\n value is not a valid int"), + ([None], "none is not an allowed value"), + ], + "get_by_name": [ + ([[123]], "alias\n str type expected"), + ([None], "none is not an allowed value"), + ], + "get_members": [ + ([[123]], "guid\n str type expected"), + ([None], "none is not an allowed value"), + ], + "remove_users": [ + ([[123]], "guid\n str type expected"), + ([None], "none is not an allowed value"), + ], +} + +TEST_ROLE_CLIENT_METHODS = { + "get": [ + (["limit", None, None, True, 123], "limit\n value is not a valid int"), + ([None, None, None, True, 123], "none is not an allowed value"), + ( + [123, None, None, "count", 123], + "count\n value is not a valid boolean", + ), + ([123, None, None, None, "offset"], "none is not an allowed value"), + ([123, None, None, True, "offset"], "offset\n value is not a valid int"), + ([123, None, None, True, None], "none is not an allowed value"), + ], +} + +TEST_SL_CLIENT_METHODS = { + "search": [ + (["search-log-req"], "criteria\n instance of SearchLogRequest expected"), + ([None], "none is not an allowed value"), + ], +} + +TEST_TOKEN_CLIENT_METHODS = { + "get": [ + ( + [None, None, None, "count", 123], + "count\n value is not a valid boolean", + ), + ([None, None, None, None, 123], "none is not an allowed value"), + ([None, None, None, True, "offset"], "offset\n value is not a valid int"), + ([None, None, None, True, None], "none is not an allowed value"), + ], + "get_by_name": [ + ([[123]], "display_name\n str type expected"), + ([None], "none is not an allowed value"), + ], + "get_by_id": [ + ([[123]], "client_id\n str type expected"), + ([None], "none is not an allowed value"), + ], + "create": [ + ([[123]], "display_name\n str type expected"), + ([None], "none is not an allowed value"), + ], + "update": [ + ([[123], "display-name"], "guid\n str type expected"), + ([None, "display-name"], "none is not an allowed value"), + (["guid", [[123]]], "display_name\n str type expected"), + (["guid", None], "none is not an allowed value"), + ], + "purge": [ + ([[123]], "guid\n str type expected"), + ([None], "none is not an allowed value"), + ], +} + +TEST_TYPEDEF_CLIENT_METHODS = { + "get": [ + ( + ["atlan-type-category"], + "type_category\n value is not a valid AtlanTypeCategory or List[AtlanTypeCategory]", + ), + ([None], "none is not an allowed value"), + ], + "create": [ + (["typedef"], "typedef\n instance of TypeDef expected"), + ([None], "none is not an allowed value"), + ], + "update": [ + (["typedef"], "typedef\n instance of TypeDef expected"), + ([None], "none is not an allowed value"), + ], + "purge": [ + ([[123], "typedef"], "name\n str type expected"), + ([None, "typedef"], "none is not an allowed value"), + (["name", "typedef"], "typedef_type\n instance of type expected"), + (["name", None], "none is not an allowed value"), + ], +} + +TEST_USER_CLIENT_METHODS = { + "create": [ + ([123], "users\n value is not a valid list"), + ([None], "none is not an allowed value"), + ], + "update": [ + ([[123], "user"], "guid\n str type expected"), + ([None], "none is not an allowed value"), + (["guid", "user"], "user\n instance of AtlanUser expected"), + (["guid", None], "none is not an allowed value"), + ], + "change_role": [ + ([[123], "role-id"], "guid\n str type expected"), + ([None, "role-id"], "none is not an allowed value"), + (["guid", [123]], "role_id\n str type expected"), + (["guid", None], "none is not an allowed value"), + ], + "get": [ + ( + [None, None, None, "count", 123], + "count\n value is not a valid boolean", + ), + ([None, None, None, None, 123], "none is not an allowed value"), + ([None, None, None, True, "offset"], "offset\n value is not a valid int"), + ([None, None, None, True, None], "none is not an allowed value"), + ], + "get_all": [ + (["limit"], "limit\n value is not a valid int"), + ([None], "none is not an allowed value"), + ], + "get_by_email": [ + ([[123]], "email\n str type expected"), + ([None], "none is not an allowed value"), + ], + "get_by_username": [ + ([[123]], "username\n str type expected"), + ([None], "none is not an allowed value"), + ], + "add_to_groups": [ + ([[123], ["grp-ids"]], "guid\n str type expected"), + ([None, ["grp-ids"]], "none is not an allowed value"), + (["guid", 123], "group_ids\n value is not a valid list"), + (["guid", None], "none is not an allowed value"), + ], + "get_groups": [ + ([[123]], "guid\n str type expected"), + ([None], "none is not an allowed value"), + ], + "add_as_admin": [ + ([[123], "imp-token"], "asset_guid\n str type expected"), + ([None, "imp-token"], "none is not an allowed value"), + (["guid", [123]], "impersonation_token\n str type expected"), + (["guid", None], "none is not an allowed value"), + ], + "add_as_viewer": [ + ([[123], "imp-token"], "asset_guid\n str type expected"), + ([None, "imp-token"], "none is not an allowed value"), + (["guid", [123]], "impersonation_token\n str type expected"), + (["guid", None], "none is not an allowed value"), + ], +} + +TEST_FILE_CLIENT_METHODS = { + "generate_presigned_url": [ + ([123], "request\n instance of PresignedURLRequest expected"), + ([None], "none is not an allowed value"), + ], + "upload_file": [ + ([[123], "file-path"], "presigned_url\n str type expected"), + ([None, "file-path"], "none is not an allowed value"), + ( + ["test-url", [123]], + "file_path\n str type expected", + ), + ( + ["test-url", None], + "none is not an allowed value", + ), + ], + "download_file": [ + ([[123], "file-path"], "presigned_url\n str type expected"), + ([None, "file-path"], "none is not an allowed value"), + ( + ["test-url", [123]], + "file_path\n str type expected", + ), + ( + ["test-url", None], + "none is not an allowed value", + ), + ], +} + + +TEST_WORKFLOW_CLIENT_METHODS = { + "update": [ + (["abc"], "instance of Workflow expected"), + ([None], "none is not an allowed value"), + ], + "find_by_type": [ + (["abc"], "value is not a valid enumeration member"), + ([None], "none is not an allowed value"), + ], + "monitor": [ + (["abc", "test-logger"], "instance of WorkflowResponse expected"), + ( + [ + WorkflowResponse(metadata=WorkflowMetadata(), spec=WorkflowSpec()), + "test-logger", + ], + "instance of Logger expected", + ), + ], + "get_runs": [ + ([[123], AtlanWorkflowPhase.RUNNING, 123, 456], "str type expected"), + ([None, AtlanWorkflowPhase.RUNNING, 123, 456], "none is not an allowed value"), + ], + "stop": [ + ([[123]], "str type expected"), + ([None], "none is not an allowed value"), + ], + "delete": [ + ([[123]], "str type expected"), + ([None], "none is not an allowed value"), + ], + "get_scheduled_run": [ + ([[123]], "str type expected"), + ([None], "none is not an allowed value"), + ], + "find_schedule_query": [ + ([[123], 10], "saved_query_id\n str type expected"), + ([None, 10], "none is not an allowed value"), + (["test-query-id", [123]], "max_results\n value is not a valid int"), + (["test-query-id", None], "none is not an allowed value"), + ], + "re_run_schedule_query": [ + ([[123]], "schedule_query_id\n str type expected"), + ([None], "none is not an allowed value"), + ], + "find_schedule_query_between": [ + ([[123], True], "instance of ScheduleQueriesSearchRequest expected"), + ([None, True], "none is not an allowed value"), + ( + [ScheduleQueriesSearchRequest(start_date="start", end_date="end"), [123]], + "missed\n value is not a valid boolean", + ), + ( + [ScheduleQueriesSearchRequest(start_date="start", end_date="end"), None], + "none is not an allowed value", + ), + ], + "update_owner": [ + ([[123], 10], "workflow_name\n str type expected"), + ([None, 10], "none is not an allowed value"), + (["test-workflow", [123]], "username\n str type expected"), + (["test-workflow", None], "none is not an allowed value"), + ], +} + +# Async-specific constants (same as sync but with AsyncCustomMetadataDict) +TEST_ASSET_CLIENT_METHODS_ASYNC = { + **TEST_ASSET_CLIENT_METHODS, + "update_custom_metadata_attributes": [ + ([[123], ["cm"]], "guid\n str type expected"), + ([None, ["cm"]], "none is not an allowed value"), + ( + ["name", 123], + "custom_metadata\n instance of AsyncCustomMetadataDict expected", + ), + (["name", None], "none is not an allowed value"), + ], + "replace_custom_metadata": [ + ([[123], ["cm"]], "guid\n str type expected"), + ([None, ["cm"]], "none is not an allowed value"), + ( + ["name", 123], + "custom_metadata\n instance of AsyncCustomMetadataDict expected", + ), + (["name", None], "none is not an allowed value"), + ], +} + + +# Rename create/update keys to creator/updater for v9 sub-clients +_V9_WF_METHODS = dict(TEST_WORKFLOW_CLIENT_METHODS) +TEST_WORKFLOW_CLIENT_METHODS = { # noqa: F811 + ("updater" if k == "update" else k): v for k, v in _V9_WF_METHODS.items() +} diff --git a/tests_v9/unit/model/__init__.py b/tests_v9/unit/model/__init__.py new file mode 100644 index 000000000..de398f096 --- /dev/null +++ b/tests_v9/unit/model/__init__.py @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. diff --git a/tests_v9/unit/model/a_d_l_s_account_test.py b/tests_v9/unit/model/a_d_l_s_account_test.py new file mode 100644 index 000000000..187e9191e --- /dev/null +++ b/tests_v9/unit/model/a_d_l_s_account_test.py @@ -0,0 +1,78 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for ADLSAccount model in pyatlan_v9.""" + +import pytest + +from pyatlan_v9.model import ADLSAccount +from tests_v9.unit.model.constants import ( + ADLS_ACCOUNT_NAME, + ADLS_CONNECTION_QUALIFIED_NAME, + ADLS_CONNECTOR_TYPE, + ADLS_QUALIFIED_NAME, +) + + +@pytest.mark.parametrize( + "name, connection_qualified_name, message", + [ + (None, "connection/name", "name is required"), + (ADLS_ACCOUNT_NAME, None, "connection_qualified_name is required"), + ], +) +def test_creator_with_missing_parameters_raise_value_error( + name: str, connection_qualified_name: str, message: str +): + """Test creator validates required parameters.""" + with pytest.raises(ValueError, match=message): + ADLSAccount.creator( + name=name, connection_qualified_name=connection_qualified_name + ) + + +def test_creator(): + """Test creator initializes expected derived fields.""" + sut = ADLSAccount.creator( + name=ADLS_ACCOUNT_NAME, connection_qualified_name=ADLS_CONNECTION_QUALIFIED_NAME + ) + + assert sut.name == ADLS_ACCOUNT_NAME + assert sut.connection_qualified_name == ADLS_CONNECTION_QUALIFIED_NAME + assert sut.qualified_name == ADLS_QUALIFIED_NAME + assert sut.connector_name == ADLS_CONNECTOR_TYPE + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, ADLS_QUALIFIED_NAME, "qualified_name is required"), + (ADLS_ACCOUNT_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test updater validates required parameters.""" + with pytest.raises(ValueError, match=message): + ADLSAccount.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test updater creates ADLSAccount for modification.""" + sut = ADLSAccount.updater( + qualified_name=ADLS_QUALIFIED_NAME, name=ADLS_ACCOUNT_NAME + ) + + assert sut.qualified_name == ADLS_QUALIFIED_NAME + assert sut.name == ADLS_ACCOUNT_NAME + + +def test_trim_to_required(): + """Test trim_to_required keeps only updater-required fields.""" + sut = ADLSAccount.updater( + qualified_name=ADLS_QUALIFIED_NAME, name=ADLS_ACCOUNT_NAME + ).trim_to_required() + + assert sut.qualified_name == ADLS_QUALIFIED_NAME + assert sut.name == ADLS_ACCOUNT_NAME diff --git a/tests_v9/unit/model/a_d_l_s_container_test.py b/tests_v9/unit/model/a_d_l_s_container_test.py new file mode 100644 index 000000000..e29b0f907 --- /dev/null +++ b/tests_v9/unit/model/a_d_l_s_container_test.py @@ -0,0 +1,96 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for ADLSContainer model in pyatlan_v9.""" + +import pytest + +from pyatlan_v9.model import ADLSContainer +from tests_v9.unit.model.constants import ( + ADLS_ACCOUNT_QUALIFIED_NAME, + ADLS_CONNECTION_QUALIFIED_NAME, + ADLS_CONNECTOR_TYPE, + ADLS_CONTAINER_NAME, + ADLS_CONTAINER_QUALIFIED_NAME, +) + + +@pytest.mark.parametrize( + "name, adls_account_qualified_name, message", + [ + (None, "adls/account", "name is required"), + (ADLS_CONTAINER_NAME, None, "adls_account_qualified_name is required"), + ], +) +def test_creator_with_missing_parameters_raise_value_error( + name: str, adls_account_qualified_name: str, message: str +): + """Test creator validates required parameters.""" + with pytest.raises(ValueError, match=message): + ADLSContainer.creator( + name=name, adls_account_qualified_name=adls_account_qualified_name + ) + + +def test_creator(): + """Test creator initializes expected derived fields.""" + sut = ADLSContainer.creator( + name=ADLS_CONTAINER_NAME, + adls_account_qualified_name=ADLS_ACCOUNT_QUALIFIED_NAME, + ) + + assert sut.name == ADLS_CONTAINER_NAME + assert sut.adls_account_qualified_name == ADLS_ACCOUNT_QUALIFIED_NAME + assert sut.connection_qualified_name == ADLS_CONNECTION_QUALIFIED_NAME + assert sut.connector_name == ADLS_CONNECTOR_TYPE + assert sut.qualified_name == f"{ADLS_ACCOUNT_QUALIFIED_NAME}/{ADLS_CONTAINER_NAME}" + + +def test_overload_creator(): + """Test creator accepts explicit connection qualified name.""" + sut = ADLSContainer.creator( + name=ADLS_CONTAINER_NAME, + adls_account_qualified_name=ADLS_ACCOUNT_QUALIFIED_NAME, + connection_qualified_name=ADLS_CONNECTION_QUALIFIED_NAME, + ) + + assert sut.name == ADLS_CONTAINER_NAME + assert sut.adls_account_qualified_name == ADLS_ACCOUNT_QUALIFIED_NAME + assert sut.connection_qualified_name == ADLS_CONNECTION_QUALIFIED_NAME + assert sut.connector_name == ADLS_CONNECTOR_TYPE + assert sut.qualified_name == f"{ADLS_ACCOUNT_QUALIFIED_NAME}/{ADLS_CONTAINER_NAME}" + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, ADLS_CONTAINER_NAME, "qualified_name is required"), + (ADLS_CONTAINER_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test updater validates required parameters.""" + with pytest.raises(ValueError, match=message): + ADLSContainer.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test updater creates ADLSContainer for modification.""" + sut = ADLSContainer.updater( + qualified_name=ADLS_CONTAINER_QUALIFIED_NAME, name=ADLS_CONTAINER_NAME + ) + + assert sut.name == ADLS_CONTAINER_NAME + assert sut.qualified_name == ADLS_CONTAINER_QUALIFIED_NAME + + +def test_trim_to_required(): + """Test trim_to_required keeps only updater-required fields.""" + sut = ADLSContainer.updater( + qualified_name=ADLS_CONTAINER_QUALIFIED_NAME, name=ADLS_CONTAINER_NAME + ).trim_to_required() + + assert sut.name == ADLS_CONTAINER_NAME + assert sut.qualified_name == ADLS_CONTAINER_QUALIFIED_NAME diff --git a/tests_v9/unit/model/a_d_l_s_object_test.py b/tests_v9/unit/model/a_d_l_s_object_test.py new file mode 100644 index 000000000..33e8721f6 --- /dev/null +++ b/tests_v9/unit/model/a_d_l_s_object_test.py @@ -0,0 +1,294 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for ADLSObject model in pyatlan_v9.""" + +import pytest + +from pyatlan.model.utils import construct_object_key +from pyatlan.utils import get_parent_qualified_name +from pyatlan_v9.model import ADLSObject +from tests_v9.unit.model.constants import ( + ADLS_CONNECTION_QUALIFIED_NAME, + ADLS_CONNECTOR_TYPE, + ADLS_CONTAINER_NAME, + ADLS_CONTAINER_QUALIFIED_NAME, + ADLS_OBJECT_NAME, + ADLS_OBJECT_PREFIX, + ADLS_OBJECT_QUALIFIED_NAME, +) + + +@pytest.mark.parametrize( + "name, adls_container_name, adls_container_qualified_name, message", + [ + (None, ADLS_CONTAINER_NAME, "adls/container/qn", "name is required"), + ( + ADLS_OBJECT_NAME, + None, + "adls/container/qn", + "adls_container_name is required", + ), + ( + ADLS_OBJECT_NAME, + ADLS_CONTAINER_NAME, + None, + "adls_container_qualified_name is required", + ), + ], +) +def test_creator_with_missing_parameters_raise_value_error( + name: str, + adls_container_name: str, + adls_container_qualified_name: str, + message: str, +): + """Test creator validates required parameters.""" + with pytest.raises(ValueError, match=message): + ADLSObject.creator( + name=name, + adls_container_name=adls_container_name, + adls_container_qualified_name=adls_container_qualified_name, + ) + + +def test_creator(): + """Test creator initializes expected derived fields.""" + sut = ADLSObject.creator( + name=ADLS_OBJECT_NAME, + adls_container_name=ADLS_CONTAINER_NAME, + adls_container_qualified_name=ADLS_CONTAINER_QUALIFIED_NAME, + ) + + assert sut.name == ADLS_OBJECT_NAME + assert sut.adls_container_qualified_name == ADLS_CONTAINER_QUALIFIED_NAME + assert sut.qualified_name == f"{ADLS_CONTAINER_QUALIFIED_NAME}/{ADLS_OBJECT_NAME}" + assert sut.connection_qualified_name == ADLS_CONNECTION_QUALIFIED_NAME + assert sut.connector_name == ADLS_CONNECTOR_TYPE + assert sut.adls_account_qualified_name == get_parent_qualified_name( + ADLS_CONTAINER_QUALIFIED_NAME + ) + + +@pytest.mark.parametrize( + "name, connection_qualified_name, prefix, adls_container_name, adls_container_qualified_name, msg", + [ + ( + None, + ADLS_CONNECTION_QUALIFIED_NAME, + "abc", + ADLS_CONTAINER_NAME, + ADLS_CONTAINER_QUALIFIED_NAME, + "name is required", + ), + ( + ADLS_OBJECT_NAME, + None, + "abc", + ADLS_CONTAINER_NAME, + ADLS_CONTAINER_QUALIFIED_NAME, + "connection_qualified_name is required", + ), + ( + "", + ADLS_CONNECTION_QUALIFIED_NAME, + "abc", + ADLS_CONTAINER_NAME, + ADLS_CONTAINER_QUALIFIED_NAME, + "name cannot be blank", + ), + ( + ADLS_OBJECT_NAME, + "", + "abc", + ADLS_CONTAINER_NAME, + ADLS_CONTAINER_QUALIFIED_NAME, + "connection_qualified_name cannot be blank", + ), + ( + ADLS_OBJECT_NAME, + "default/adls", + "abc", + ADLS_CONTAINER_NAME, + ADLS_CONTAINER_QUALIFIED_NAME, + "Invalid connection_qualified_name", + ), + ( + ADLS_OBJECT_NAME, + "/adls", + "abc", + ADLS_CONTAINER_NAME, + ADLS_CONTAINER_QUALIFIED_NAME, + "Invalid connection_qualified_name", + ), + ( + ADLS_OBJECT_NAME, + "default/adls/production/TestDb", + "abc", + ADLS_CONTAINER_NAME, + ADLS_CONTAINER_QUALIFIED_NAME, + "Invalid connection_qualified_name", + ), + ( + ADLS_OBJECT_NAME, + "adls/production", + "abc", + ADLS_CONTAINER_NAME, + ADLS_CONTAINER_QUALIFIED_NAME, + "Invalid connection_qualified_name", + ), + ( + ADLS_OBJECT_NAME, + "default/adls/invalid/production", + "abc", + ADLS_CONTAINER_NAME, + ADLS_CONTAINER_QUALIFIED_NAME, + "Invalid connection_qualified_name", + ), + ( + ADLS_OBJECT_NAME, + ADLS_CONNECTION_QUALIFIED_NAME, + "abc", + None, + ADLS_CONTAINER_QUALIFIED_NAME, + "adls_container_name is required", + ), + ( + ADLS_OBJECT_NAME, + ADLS_CONNECTION_QUALIFIED_NAME, + "abc", + "", + ADLS_CONTAINER_QUALIFIED_NAME, + "adls_container_name cannot be blank", + ), + ( + ADLS_OBJECT_NAME, + ADLS_CONNECTION_QUALIFIED_NAME, + "abc", + ADLS_CONTAINER_NAME, + None, + "adls_container_qualified_name is required", + ), + ( + ADLS_OBJECT_NAME, + ADLS_CONNECTION_QUALIFIED_NAME, + "abc", + ADLS_CONTAINER_NAME, + "", + "adls_container_qualified_name cannot be blank", + ), + ], +) +def test_creator_with_prefix_without_required_parameters_raises_validation_error( + name, + connection_qualified_name, + prefix, + adls_container_name, + adls_container_qualified_name, + msg, +): + """Test creator_with_prefix validates all required and format constraints.""" + with pytest.raises(ValueError, match=msg): + ADLSObject.creator_with_prefix( + name=name, + connection_qualified_name=connection_qualified_name, + adls_container_name=adls_container_name, + adls_container_qualified_name=adls_container_qualified_name, + prefix=prefix, + ) + + +@pytest.mark.parametrize( + "name, connection_qualified_name, prefix, adls_container_name, adls_container_qualified_name", + [ + ( + ADLS_OBJECT_NAME, + ADLS_CONNECTION_QUALIFIED_NAME, + ADLS_OBJECT_PREFIX, + ADLS_CONTAINER_NAME, + ADLS_CONTAINER_QUALIFIED_NAME, + ), + ], +) +def test_creator_with_prefix( + name, + connection_qualified_name, + prefix, + adls_container_name, + adls_container_qualified_name, +): + """Test creator_with_prefix builds object key and derived fields correctly.""" + attributes = ADLSObject.creator_with_prefix( + name=name, + connection_qualified_name=connection_qualified_name, + adls_container_name=adls_container_name, + adls_container_qualified_name=adls_container_qualified_name, + prefix=prefix, + ) + assert attributes.name == name + assert attributes.connection_qualified_name == connection_qualified_name + object_key = construct_object_key(prefix, name) + assert attributes.adls_object_key == object_key + assert ( + attributes.qualified_name + == f"{attributes.adls_container_qualified_name}/{object_key}" + ) + assert attributes.connector_name == connection_qualified_name.split("/")[1] + assert attributes.adls_container_qualified_name == adls_container_qualified_name + + +def test_overload_creator(): + """Test creator accepts explicit account and connection qualified names.""" + sut = ADLSObject.creator( + name=ADLS_OBJECT_NAME, + adls_container_name=ADLS_CONTAINER_NAME, + adls_container_qualified_name=ADLS_CONTAINER_QUALIFIED_NAME, + adls_account_qualified_name=get_parent_qualified_name( + ADLS_CONTAINER_QUALIFIED_NAME + ), + connection_qualified_name=ADLS_CONNECTION_QUALIFIED_NAME, + ) + + assert sut.name == ADLS_OBJECT_NAME + assert sut.adls_container_qualified_name == ADLS_CONTAINER_QUALIFIED_NAME + assert sut.qualified_name == f"{ADLS_CONTAINER_QUALIFIED_NAME}/{ADLS_OBJECT_NAME}" + assert sut.connection_qualified_name == ADLS_CONNECTION_QUALIFIED_NAME + assert sut.connector_name == ADLS_CONNECTOR_TYPE + assert sut.adls_account_qualified_name == get_parent_qualified_name( + ADLS_CONTAINER_QUALIFIED_NAME + ) + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, ADLS_OBJECT_NAME, "qualified_name is required"), + (ADLS_OBJECT_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test updater validates required parameters.""" + with pytest.raises(ValueError, match=message): + ADLSObject.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test updater creates ADLSObject for modification.""" + sut = ADLSObject.updater( + qualified_name=ADLS_OBJECT_QUALIFIED_NAME, name=ADLS_OBJECT_NAME + ) + + assert sut.name == ADLS_OBJECT_NAME + assert sut.qualified_name == ADLS_OBJECT_QUALIFIED_NAME + + +def test_trim_to_required(): + """Test trim_to_required keeps only updater-required fields.""" + sut = ADLSObject.updater( + qualified_name=ADLS_OBJECT_QUALIFIED_NAME, name=ADLS_OBJECT_NAME + ).trim_to_required() + + assert sut.name == ADLS_OBJECT_NAME + assert sut.qualified_name == ADLS_OBJECT_QUALIFIED_NAME diff --git a/tests_v9/unit/model/a_i_application_test.py b/tests_v9/unit/model/a_i_application_test.py new file mode 100644 index 000000000..498a82dac --- /dev/null +++ b/tests_v9/unit/model/a_i_application_test.py @@ -0,0 +1,114 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for AIApplication model in pyatlan_v9.""" + +import pytest + +from pyatlan_v9.model import AIApplication +from pyatlan_v9.model.enums import AIApplicationDevelopmentStage +from tests_v9.unit.model.constants import ( + AI_APPLICATION_DEVELOPMENT_STAGE, + AI_APPLICATION_DEVELOPMENT_STAGE_UPDATED, + AI_APPLICATION_NAME, + AI_APPLICATION_QUALIFIED_NAME, + AI_APPLICATION_VERSION, +) + + +@pytest.mark.parametrize( + "name, ai_application_version, ai_application_development_stage, message", + [ + ( + None, + AI_APPLICATION_VERSION, + AI_APPLICATION_DEVELOPMENT_STAGE, + "name is required", + ), + ( + AI_APPLICATION_NAME, + None, + AI_APPLICATION_DEVELOPMENT_STAGE, + "ai_application_version is required", + ), + ( + AI_APPLICATION_NAME, + AI_APPLICATION_VERSION, + None, + "ai_application_development_stage is required", + ), + ], +) +def test_creator_with_missing_parameters_raise_value_error( + name: str, + ai_application_development_stage: AIApplicationDevelopmentStage, + ai_application_version: str, + message: str, +): + """Test creator validates required parameters.""" + with pytest.raises(ValueError, match=message): + AIApplication.creator( + name=name, + ai_application_development_stage=ai_application_development_stage, + ai_application_version=ai_application_version, + ) + + +def test_creator(): + """Test creator initializes required and derived fields.""" + ai_application = AIApplication.creator( + name=AI_APPLICATION_NAME, + ai_application_version=AI_APPLICATION_VERSION, + ai_application_development_stage=AI_APPLICATION_DEVELOPMENT_STAGE, + ) + + assert ai_application.name == AI_APPLICATION_NAME + assert ai_application.ai_application_version == AI_APPLICATION_VERSION + assert ( + ai_application.ai_application_development_stage + == AI_APPLICATION_DEVELOPMENT_STAGE + ) + assert ai_application.qualified_name == AI_APPLICATION_QUALIFIED_NAME + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, AI_APPLICATION_QUALIFIED_NAME, "qualified_name is required"), + (AI_APPLICATION_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test updater validates required parameters.""" + with pytest.raises(ValueError, match=message): + AIApplication.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test updater creates minimal updatable instance.""" + ai_application = AIApplication.updater( + name=AI_APPLICATION_NAME, qualified_name=AI_APPLICATION_QUALIFIED_NAME + ) + ai_application.ai_application_development_stage = ( + AI_APPLICATION_DEVELOPMENT_STAGE_UPDATED + ) + + assert ai_application.name == AI_APPLICATION_NAME + assert ai_application.qualified_name == AI_APPLICATION_QUALIFIED_NAME + assert ( + ai_application.ai_application_development_stage + == AI_APPLICATION_DEVELOPMENT_STAGE_UPDATED + ) + + +def test_trim_to_required(): + """Test trim_to_required keeps only required updater fields.""" + ai_application = AIApplication.updater( + name=AI_APPLICATION_NAME, + qualified_name=AI_APPLICATION_QUALIFIED_NAME, + ).trim_to_required() + + assert ai_application.name == AI_APPLICATION_NAME + assert ai_application.qualified_name == AI_APPLICATION_QUALIFIED_NAME diff --git a/tests_v9/unit/model/a_i_model_test.py b/tests_v9/unit/model/a_i_model_test.py new file mode 100644 index 000000000..95f5786ef --- /dev/null +++ b/tests_v9/unit/model/a_i_model_test.py @@ -0,0 +1,114 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for AIModel model in pyatlan_v9.""" + +import pytest + +from pyatlan_v9.model import AIModel +from pyatlan_v9.model.enums import AIModelStatus +from tests_v9.unit.model.constants import ( + AI_MODEL_NAME, + AI_MODEL_QUALIFIED_NAME, + AI_MODEL_STATUS, + AI_MODEL_STATUS_UPDATED, + AI_MODEL_VERSION, +) + + +@pytest.mark.parametrize( + "name, ai_model_status, message", + [ + ( + None, + AI_MODEL_STATUS, + "name is required", + ), + ( + AI_MODEL_NAME, + None, + "ai_model_status is required", + ), + ], +) +def test_creator_with_missing_parameters_raise_value_error( + name: str, + ai_model_status: AIModelStatus, + message: str, +): + """Test creator validates required parameters.""" + with pytest.raises(ValueError, match=message): + AIModel.creator( + name=name, + ai_model_status=ai_model_status, + ) + + +def test_creator(): + """Test creator initializes required and derived fields.""" + ai_model = AIModel.creator( + name=AI_MODEL_NAME, + ai_model_status=AI_MODEL_STATUS, + ) + + assert ai_model.name == AI_MODEL_NAME + assert ai_model.ai_model_status == AI_MODEL_STATUS + assert ai_model.qualified_name == AI_MODEL_QUALIFIED_NAME + + +def test_creator_with_optional_parameters(): + """Test creator accepts optional ownership and version fields.""" + owner_groups = {"group1", "group2"} + owner_users = {"user1", "user2"} + ai_model_version = AI_MODEL_VERSION + + ai_model = AIModel.creator( + name=AI_MODEL_NAME, + ai_model_status=AI_MODEL_STATUS, + owner_groups=owner_groups, + owner_users=owner_users, + ai_model_version=ai_model_version, + ) + + assert ai_model.name == AI_MODEL_NAME + assert ai_model.ai_model_status == AI_MODEL_STATUS + assert ai_model.ai_model_version == ai_model_version + assert ai_model.qualified_name == AI_MODEL_QUALIFIED_NAME + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, AI_MODEL_QUALIFIED_NAME, "qualified_name is required"), + (AI_MODEL_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test updater validates required parameters.""" + with pytest.raises(ValueError, match=message): + AIModel.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test updater creates minimal updatable instance.""" + ai_model = AIModel.updater( + name=AI_MODEL_NAME, qualified_name=AI_MODEL_QUALIFIED_NAME + ) + ai_model.ai_model_status = AI_MODEL_STATUS_UPDATED + + assert ai_model.name == AI_MODEL_NAME + assert ai_model.qualified_name == AI_MODEL_QUALIFIED_NAME + assert ai_model.ai_model_status == AI_MODEL_STATUS_UPDATED + + +def test_trim_to_required(): + """Test trim_to_required keeps only required updater fields.""" + ai_model = AIModel.updater( + name=AI_MODEL_NAME, + qualified_name=AI_MODEL_QUALIFIED_NAME, + ).trim_to_required() + + assert ai_model.name == AI_MODEL_NAME + assert ai_model.qualified_name == AI_MODEL_QUALIFIED_NAME diff --git a/tests_v9/unit/model/a_p_i_field_test.py b/tests_v9/unit/model/a_p_i_field_test.py new file mode 100644 index 000000000..946b9248d --- /dev/null +++ b/tests_v9/unit/model/a_p_i_field_test.py @@ -0,0 +1,189 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for APIField model in pyatlan_v9.""" + +import pytest + +from pyatlan_v9.model import APIField +from pyatlan_v9.model.enums import APIQueryParamTypeEnum +from tests_v9.unit.model.constants import ( + API_CONNECTION_QUALIFIED_NAME, + API_CONNECTOR_TYPE, + API_FIELD_NAME, + API_FIELD_PARENT_OBJECT_QUALIFIED_NAME, + API_FIELD_PARENT_QUERY_QUALIFIED_NAME, + API_FIELD_REFERENCE_OBJECT_QN, +) + + +@pytest.mark.parametrize( + "name, parent_api_object_qualified_name, parent_api_query_qualified_name, message", + [ + (None, "connection/name", "connection/name", "name is required"), + ( + API_FIELD_NAME, + None, + None, + "Either parent_api_object_qualified_name or parent_api_query_qualified_name requires a valid value", + ), + ( + API_FIELD_NAME, + API_FIELD_PARENT_OBJECT_QUALIFIED_NAME, + API_FIELD_PARENT_QUERY_QUALIFIED_NAME, + "Both parent_api_object_qualified_name and parent_api_query_qualified_name cannot be valid", + ), + ], +) +def test_creator_with_missing_parameters_raise_value_error( + name: str, + parent_api_object_qualified_name: str, + parent_api_query_qualified_name: str, + message: str, +): + """Test creator validates parent context constraints.""" + with pytest.raises(ValueError, match=message): + APIField.creator( + name=name, + parent_api_object_qualified_name=parent_api_object_qualified_name, + parent_api_query_qualified_name=parent_api_query_qualified_name, + ) + + +def test_creator_parent_object(): + """Test creator when APIField belongs to an APIObject.""" + sut = APIField.creator( + name=API_FIELD_NAME, + parent_api_object_qualified_name=API_FIELD_PARENT_OBJECT_QUALIFIED_NAME, + parent_api_query_qualified_name=None, + ) + + assert sut.name == API_FIELD_NAME + assert sut.connection_qualified_name == API_CONNECTION_QUALIFIED_NAME + assert ( + sut.qualified_name + == f"{API_FIELD_PARENT_OBJECT_QUALIFIED_NAME}/{API_FIELD_NAME}" + ) + assert sut.connector_name == API_CONNECTOR_TYPE + assert sut.api_object.qualified_name == API_FIELD_PARENT_OBJECT_QUALIFIED_NAME + + +def test_creator_parent_query(): + """Test creator when APIField belongs to an APIQuery.""" + sut = APIField.creator( + name=API_FIELD_NAME, + parent_api_object_qualified_name=None, + parent_api_query_qualified_name=API_FIELD_PARENT_QUERY_QUALIFIED_NAME, + ) + + assert sut.name == API_FIELD_NAME + assert sut.connection_qualified_name == API_CONNECTION_QUALIFIED_NAME + assert ( + sut.qualified_name + == f"{API_FIELD_PARENT_QUERY_QUALIFIED_NAME}/{API_FIELD_NAME}" + ) + assert sut.connector_name == API_CONNECTOR_TYPE + assert sut.api_query.qualified_name == API_FIELD_PARENT_QUERY_QUALIFIED_NAME + + +def test_overload_creator_parent_object(): + """Test creator with object-parent optional field metadata.""" + sut = APIField.creator( + name=API_FIELD_NAME, + parent_api_object_qualified_name=API_FIELD_PARENT_OBJECT_QUALIFIED_NAME, + parent_api_query_qualified_name=None, + connection_qualified_name=API_CONNECTION_QUALIFIED_NAME, + api_field_type="api-object-ref", + api_field_type_secondary="Object", + is_api_object_reference=True, + reference_api_object_qualified_name=API_FIELD_REFERENCE_OBJECT_QN, + api_query_param_type=None, + ) + + assert sut.name == API_FIELD_NAME + assert sut.connection_qualified_name == API_CONNECTION_QUALIFIED_NAME + assert ( + sut.qualified_name + == f"{API_FIELD_PARENT_OBJECT_QUALIFIED_NAME}/{API_FIELD_NAME}" + ) + assert sut.connector_name == API_CONNECTOR_TYPE + assert sut.api_field_type == "api-object-ref" + assert sut.api_field_type_secondary == "Object" + assert sut.api_is_object_reference + assert sut.api_object_qualified_name == API_FIELD_REFERENCE_OBJECT_QN + assert sut.api_object.qualified_name == API_FIELD_PARENT_OBJECT_QUALIFIED_NAME + + +def test_overload_creator_parent_query(): + """Test creator with query-parent optional field metadata.""" + sut = APIField.creator( + name=API_FIELD_NAME, + parent_api_object_qualified_name=None, + parent_api_query_qualified_name=API_FIELD_PARENT_QUERY_QUALIFIED_NAME, + connection_qualified_name=API_CONNECTION_QUALIFIED_NAME, + api_field_type="api-object-ref", + api_field_type_secondary="Object", + is_api_object_reference=True, + reference_api_object_qualified_name=API_FIELD_REFERENCE_OBJECT_QN, + api_query_param_type=APIQueryParamTypeEnum.INPUT, + ) + + assert sut.name == API_FIELD_NAME + assert sut.connection_qualified_name == API_CONNECTION_QUALIFIED_NAME + assert ( + sut.qualified_name + == f"{API_FIELD_PARENT_QUERY_QUALIFIED_NAME}/{API_FIELD_NAME}" + ) + assert sut.connector_name == API_CONNECTOR_TYPE + assert sut.api_field_type == "api-object-ref" + assert sut.api_field_type_secondary == "Object" + assert sut.api_is_object_reference + assert sut.api_object_qualified_name == API_FIELD_REFERENCE_OBJECT_QN + assert sut.api_query.qualified_name == API_FIELD_PARENT_QUERY_QUALIFIED_NAME + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + ( + None, + f"{API_FIELD_PARENT_OBJECT_QUALIFIED_NAME}/{API_FIELD_NAME}", + "qualified_name is required", + ), + (API_FIELD_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test updater validates required parameters.""" + with pytest.raises(ValueError, match=message): + APIField.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test updater creates APIField for modification.""" + sut = APIField.updater( + qualified_name=f"{API_FIELD_PARENT_OBJECT_QUALIFIED_NAME}/{API_FIELD_NAME}", + name=API_FIELD_NAME, + ) + + assert ( + sut.qualified_name + == f"{API_FIELD_PARENT_OBJECT_QUALIFIED_NAME}/{API_FIELD_NAME}" + ) + assert sut.name == API_FIELD_NAME + + +def test_trim_to_required(): + """Test trim_to_required keeps only updater-required fields.""" + sut = APIField.updater( + name=API_FIELD_NAME, + qualified_name=f"{API_FIELD_PARENT_OBJECT_QUALIFIED_NAME}/{API_FIELD_NAME}", + ).trim_to_required() + + assert sut.name == API_FIELD_NAME + assert ( + sut.qualified_name + == f"{API_FIELD_PARENT_OBJECT_QUALIFIED_NAME}/{API_FIELD_NAME}" + ) diff --git a/tests_v9/unit/model/a_p_i_object_test.py b/tests_v9/unit/model/a_p_i_object_test.py new file mode 100644 index 000000000..931226a4b --- /dev/null +++ b/tests_v9/unit/model/a_p_i_object_test.py @@ -0,0 +1,93 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for APIObject model in pyatlan_v9.""" + +import pytest + +from pyatlan_v9.model import APIObject +from tests_v9.unit.model.constants import ( + API_CONNECTION_QUALIFIED_NAME, + API_CONNECTOR_TYPE, + API_OBJECT_NAME, + API_OBJECT_QUALIFIED_NAME, +) + + +@pytest.mark.parametrize( + "name, connection_qualified_name, message", + [ + (None, "connection/name", "name is required"), + (API_OBJECT_NAME, None, "connection_qualified_name is required"), + ], +) +def test_creator_with_missing_parameters_raise_value_error( + name: str, connection_qualified_name: str, message: str +): + """Test creator validates required parameters.""" + with pytest.raises(ValueError, match=message): + APIObject.creator( + name=name, connection_qualified_name=connection_qualified_name + ) + + +def test_creator(): + """Test creator initializes expected derived fields.""" + sut = APIObject.creator( + name=API_OBJECT_NAME, connection_qualified_name=API_CONNECTION_QUALIFIED_NAME + ) + + assert sut.name == API_OBJECT_NAME + assert sut.connection_qualified_name == API_CONNECTION_QUALIFIED_NAME + assert sut.qualified_name == API_OBJECT_QUALIFIED_NAME + assert sut.connector_name == API_CONNECTOR_TYPE + + +def test_overload_creator(): + """Test creator accepts optional api_field_count.""" + sut = APIObject.creator( + name=API_OBJECT_NAME, + connection_qualified_name=API_CONNECTION_QUALIFIED_NAME, + api_field_count=2, + ) + + assert sut.name == API_OBJECT_NAME + assert sut.connection_qualified_name == API_CONNECTION_QUALIFIED_NAME + assert sut.qualified_name == API_OBJECT_QUALIFIED_NAME + assert sut.connector_name == API_CONNECTOR_TYPE + assert sut.api_field_count == 2 + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, API_OBJECT_QUALIFIED_NAME, "qualified_name is required"), + (API_OBJECT_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test updater validates required parameters.""" + with pytest.raises(ValueError, match=message): + APIObject.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test updater creates APIObject for modification.""" + sut = APIObject.updater( + qualified_name=API_OBJECT_QUALIFIED_NAME, name=API_OBJECT_NAME + ) + + assert sut.qualified_name == API_OBJECT_QUALIFIED_NAME + assert sut.name == API_OBJECT_NAME + + +def test_trim_to_required(): + """Test trim_to_required keeps only updater-required fields.""" + sut = APIObject.updater( + name=API_OBJECT_NAME, qualified_name=API_OBJECT_QUALIFIED_NAME + ).trim_to_required() + + assert sut.name == API_OBJECT_NAME + assert sut.qualified_name == API_OBJECT_QUALIFIED_NAME diff --git a/tests_v9/unit/model/a_p_i_path_test.py b/tests_v9/unit/model/a_p_i_path_test.py new file mode 100644 index 000000000..bb476d405 --- /dev/null +++ b/tests_v9/unit/model/a_p_i_path_test.py @@ -0,0 +1,100 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for APIPath model in pyatlan_v9.""" + +import pytest + +from pyatlan_v9.model import APIPath +from tests_v9.unit.model.constants import ( + API_CONNECTION_QUALIFIED_NAME, + API_CONNECTOR_TYPE, + API_PATH_NAME, + API_PATH_QUALIFIED_NAME, + API_PATH_RAW_URI, + API_SPEC_QUALIFIED_NAME, +) + + +@pytest.mark.parametrize( + "path_raw_uri, spec_qualified_name, message", + [ + (None, "api/spec", "path_raw_uri is required"), + (API_PATH_RAW_URI, None, "spec_qualified_name is required"), + ], +) +def test_creator_with_missing_parameters_raise_value_error( + path_raw_uri: str, + spec_qualified_name: str, + message: str, +): + """Test creator validates required parameters.""" + with pytest.raises(ValueError, match=message): + APIPath.creator( + path_raw_uri=path_raw_uri, + spec_qualified_name=spec_qualified_name, + ) + + +def test_creator(): + """Test creator initializes expected derived fields.""" + sut = APIPath.creator( + path_raw_uri=API_PATH_RAW_URI, + spec_qualified_name=API_SPEC_QUALIFIED_NAME, + ) + + assert sut.name == API_PATH_NAME + assert sut.connection_qualified_name == API_CONNECTION_QUALIFIED_NAME + assert sut.qualified_name == API_PATH_QUALIFIED_NAME + assert sut.connector_name == API_CONNECTOR_TYPE + assert sut.api_spec_qualified_name == API_SPEC_QUALIFIED_NAME + assert sut.api_path_raw_u_r_i == API_PATH_RAW_URI + + +def test_overload_creator(): + """Test creator accepts explicit connection qualified name.""" + sut = APIPath.creator( + path_raw_uri=API_PATH_RAW_URI, + spec_qualified_name=API_SPEC_QUALIFIED_NAME, + connection_qualified_name=API_CONNECTION_QUALIFIED_NAME, + ) + + assert sut.name == API_PATH_NAME + assert sut.connection_qualified_name == API_CONNECTION_QUALIFIED_NAME + assert sut.qualified_name == API_PATH_QUALIFIED_NAME + assert sut.connector_name == API_CONNECTOR_TYPE + assert sut.api_spec_qualified_name == API_SPEC_QUALIFIED_NAME + assert sut.api_path_raw_u_r_i == API_PATH_RAW_URI + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, API_PATH_QUALIFIED_NAME, "qualified_name is required"), + (API_PATH_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test updater validates required parameters.""" + with pytest.raises(ValueError, match=message): + APIPath.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test updater creates APIPath for modification.""" + sut = APIPath.updater(qualified_name=API_PATH_QUALIFIED_NAME, name=API_PATH_NAME) + + assert sut.qualified_name == API_PATH_QUALIFIED_NAME + assert sut.name == API_PATH_NAME + + +def test_trim_to_required(): + """Test trim_to_required keeps only updater-required fields.""" + sut = APIPath.updater( + name=API_PATH_NAME, qualified_name=API_PATH_QUALIFIED_NAME + ).trim_to_required() + + assert sut.name == API_PATH_NAME + assert sut.qualified_name == API_PATH_QUALIFIED_NAME diff --git a/tests_v9/unit/model/a_p_i_query_test.py b/tests_v9/unit/model/a_p_i_query_test.py new file mode 100644 index 000000000..75cb3e7eb --- /dev/null +++ b/tests_v9/unit/model/a_p_i_query_test.py @@ -0,0 +1,98 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for APIQuery model in pyatlan_v9.""" + +import pytest + +from pyatlan_v9.model import APIQuery +from tests_v9.unit.model.constants import ( + API_CONNECTION_QUALIFIED_NAME, + API_CONNECTOR_TYPE, + API_QUERY_NAME, + API_QUERY_QUALIFIED_NAME, + API_QUERY_REFERENCE_OBJECT_QN, +) + + +@pytest.mark.parametrize( + "name, connection_qualified_name, message", + [ + (None, "connection/name", "name is required"), + (API_QUERY_NAME, None, "connection_qualified_name is required"), + ], +) +def test_creator_with_missing_parameters_raise_value_error( + name: str, connection_qualified_name: str, message: str +): + """Test creator validates required parameters.""" + with pytest.raises(ValueError, match=message): + APIQuery.creator(name=name, connection_qualified_name=connection_qualified_name) + + +def test_creator(): + """Test creator initializes expected derived fields.""" + sut = APIQuery.creator( + name=API_QUERY_NAME, connection_qualified_name=API_CONNECTION_QUALIFIED_NAME + ) + + assert sut.name == API_QUERY_NAME + assert sut.connection_qualified_name == API_CONNECTION_QUALIFIED_NAME + assert sut.qualified_name == API_QUERY_QUALIFIED_NAME + assert sut.connector_name == API_CONNECTOR_TYPE + + +def test_overload_creator(): + """Test creator accepts optional APIQuery output settings.""" + sut = APIQuery.creator( + name=API_QUERY_NAME, + connection_qualified_name=API_CONNECTION_QUALIFIED_NAME, + api_input_field_count=1, + api_query_output_type="api-object-ref", + api_query_output_type_secondary="Object", + is_object_reference=True, + reference_api_object_qualified_name=API_QUERY_REFERENCE_OBJECT_QN, + ) + + assert sut.name == API_QUERY_NAME + assert sut.connection_qualified_name == API_CONNECTION_QUALIFIED_NAME + assert sut.qualified_name == API_QUERY_QUALIFIED_NAME + assert sut.connector_name == API_CONNECTOR_TYPE + assert sut.api_input_field_count == 1 + assert sut.api_query_output_type == "api-object-ref" + assert sut.api_query_output_type_secondary == "Object" + assert sut.api_is_object_reference + assert sut.api_object_qualified_name == API_QUERY_REFERENCE_OBJECT_QN + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, API_QUERY_QUALIFIED_NAME, "qualified_name is required"), + (API_QUERY_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test updater validates required parameters.""" + with pytest.raises(ValueError, match=message): + APIQuery.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test updater creates APIQuery for modification.""" + sut = APIQuery.updater(qualified_name=API_QUERY_QUALIFIED_NAME, name=API_QUERY_NAME) + + assert sut.qualified_name == API_QUERY_QUALIFIED_NAME + assert sut.name == API_QUERY_NAME + + +def test_trim_to_required(): + """Test trim_to_required keeps only updater-required fields.""" + sut = APIQuery.updater( + name=API_QUERY_NAME, qualified_name=API_QUERY_QUALIFIED_NAME + ).trim_to_required() + + assert sut.name == API_QUERY_NAME + assert sut.qualified_name == API_QUERY_QUALIFIED_NAME diff --git a/tests_v9/unit/model/a_p_i_spec_test.py b/tests_v9/unit/model/a_p_i_spec_test.py new file mode 100644 index 000000000..f681ec179 --- /dev/null +++ b/tests_v9/unit/model/a_p_i_spec_test.py @@ -0,0 +1,74 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for APISpec model in pyatlan_v9.""" + +import pytest + +from pyatlan_v9.model import APISpec +from tests_v9.unit.model.constants import ( + API_CONNECTION_QUALIFIED_NAME, + API_CONNECTOR_TYPE, + API_QUALIFIED_NAME, + API_SPEC_NAME, +) + + +@pytest.mark.parametrize( + "name, connection_qualified_name, message", + [ + (None, "connection/name", "name is required"), + (API_SPEC_NAME, None, "connection_qualified_name is required"), + ], +) +def test_creator_with_missing_parameters_raise_value_error( + name: str, connection_qualified_name: str, message: str +): + """Test creator validates required parameters.""" + with pytest.raises(ValueError, match=message): + APISpec.creator(name=name, connection_qualified_name=connection_qualified_name) + + +def test_creator(): + """Test creator initializes expected derived fields.""" + sut = APISpec.creator( + name=API_SPEC_NAME, connection_qualified_name=API_CONNECTION_QUALIFIED_NAME + ) + + assert sut.name == API_SPEC_NAME + assert sut.connection_qualified_name == API_CONNECTION_QUALIFIED_NAME + assert sut.qualified_name == API_QUALIFIED_NAME + assert sut.connector_name == API_CONNECTOR_TYPE + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, API_QUALIFIED_NAME, "qualified_name is required"), + (API_SPEC_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test updater validates required parameters.""" + with pytest.raises(ValueError, match=message): + APISpec.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test updater creates APISpec for modification.""" + sut = APISpec.updater(qualified_name=API_QUALIFIED_NAME, name=API_SPEC_NAME) + + assert sut.qualified_name == API_QUALIFIED_NAME + assert sut.name == API_SPEC_NAME + + +def test_trim_to_required(): + """Test trim_to_required keeps only updater-required fields.""" + sut = APISpec.updater( + name=API_SPEC_NAME, qualified_name=API_QUALIFIED_NAME + ).trim_to_required() + + assert sut.name == API_SPEC_NAME + assert sut.qualified_name == API_QUALIFIED_NAME diff --git a/tests_v9/unit/model/airflow_dag_test.py b/tests_v9/unit/model/airflow_dag_test.py new file mode 100644 index 000000000..4bf75c68c --- /dev/null +++ b/tests_v9/unit/model/airflow_dag_test.py @@ -0,0 +1,142 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for AirflowDag model in pyatlan_v9.""" + +import json + +import pytest +from msgspec import UNSET + +from pyatlan_v9.model import AirflowDag +from tests_v9.unit.model.constants import ( + AIRFLOW_CONNECTION_QUALIFIED_NAME, + AIRFLOW_DAG_NAME, + AIRFLOW_DAG_QUALIFIED_NAME, +) + + +@pytest.mark.parametrize( + "name, connection_qualified_name, message", + [ + (None, "connection/name", "name is required"), + (AIRFLOW_DAG_NAME, None, "connection_qualified_name is required"), + ], +) +def test_creator_with_missing_parameters_raises_value_error( + name: str, connection_qualified_name: str, message: str +): + """Test that creator raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + AirflowDag.creator( + name=name, connection_qualified_name=connection_qualified_name + ) + + +def test_creator(): + """Test that creator properly initializes an AirflowDag with all derived fields.""" + sut = AirflowDag.creator( + name=AIRFLOW_DAG_NAME, + connection_qualified_name=AIRFLOW_CONNECTION_QUALIFIED_NAME, + ) + + assert sut.name == AIRFLOW_DAG_NAME + assert sut.qualified_name == AIRFLOW_DAG_QUALIFIED_NAME + assert sut.connector_name == "airflow" + assert sut.connection_qualified_name == AIRFLOW_CONNECTION_QUALIFIED_NAME + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, AIRFLOW_DAG_QUALIFIED_NAME, "qualified_name is required"), + (AIRFLOW_DAG_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test that updater raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + AirflowDag.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test that updater creates an AirflowDag instance for modification.""" + sut = AirflowDag.updater( + name=AIRFLOW_DAG_NAME, qualified_name=AIRFLOW_DAG_QUALIFIED_NAME + ) + + assert sut.name == AIRFLOW_DAG_NAME + assert sut.qualified_name == AIRFLOW_DAG_QUALIFIED_NAME + + +def test_trim_to_required(): + """Test that trim_to_required returns an AirflowDag with only required fields.""" + sut = AirflowDag.updater( + name=AIRFLOW_DAG_NAME, + qualified_name=AIRFLOW_CONNECTION_QUALIFIED_NAME, + ).trim_to_required() + + assert sut.name == AIRFLOW_DAG_NAME + assert sut.qualified_name == AIRFLOW_CONNECTION_QUALIFIED_NAME + + +def test_basic_construction(): + """Test basic AirflowDag construction with minimal parameters.""" + dag = AirflowDag(name=AIRFLOW_DAG_NAME, qualified_name=AIRFLOW_DAG_QUALIFIED_NAME) + + assert dag.name == AIRFLOW_DAG_NAME + assert dag.qualified_name == AIRFLOW_DAG_QUALIFIED_NAME + assert dag.type_name == "AirflowDag" + + +def test_unset_fields(): + """Test that optional fields default to UNSET.""" + dag = AirflowDag(name=AIRFLOW_DAG_NAME, qualified_name=AIRFLOW_DAG_QUALIFIED_NAME) + + assert dag.airflow_dag_schedule is UNSET + assert dag.airflow_tags is UNSET + assert dag.airflow_run_version is UNSET + + +def test_serialization_to_json_nested(serde): + """Test serialization to nested JSON format (API format).""" + dag = AirflowDag.creator( + name=AIRFLOW_DAG_NAME, + connection_qualified_name=AIRFLOW_CONNECTION_QUALIFIED_NAME, + ) + + json_str = dag.to_json(nested=True, serde=serde) + data = json.loads(json_str) + + assert data["typeName"] == "AirflowDag" + assert "attributes" in data + assert data["attributes"]["name"] == AIRFLOW_DAG_NAME + + +def test_round_trip_serialization(serde): + """Test that serialization and deserialization preserve all data.""" + original = AirflowDag.creator( + name=AIRFLOW_DAG_NAME, + connection_qualified_name=AIRFLOW_CONNECTION_QUALIFIED_NAME, + ) + + json_str = original.to_json(nested=True, serde=serde) + restored = AirflowDag.from_json(json_str, serde=serde) + + assert restored.name == original.name + assert restored.qualified_name == original.qualified_name + + +def test_creator_with_guid(): + """Test that creator initializes a temporary GUID for new assets.""" + dag = AirflowDag.creator( + name=AIRFLOW_DAG_NAME, + connection_qualified_name=AIRFLOW_CONNECTION_QUALIFIED_NAME, + ) + + assert dag.guid is not UNSET + assert dag.guid is not None + assert isinstance(dag.guid, str) + assert dag.guid.startswith("-") diff --git a/tests_v9/unit/model/airflow_task_test.py b/tests_v9/unit/model/airflow_task_test.py new file mode 100644 index 000000000..def1b048c --- /dev/null +++ b/tests_v9/unit/model/airflow_task_test.py @@ -0,0 +1,162 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for AirflowTask model in pyatlan_v9.""" + +import json + +import pytest +from msgspec import UNSET + +from pyatlan_v9.model import AirflowTask +from tests_v9.unit.model.constants import ( + AIRFLOW_CONNECTION_QUALIFIED_NAME, + AIRFLOW_DAG_QUALIFIED_NAME, + AIRFLOW_TASK_NAME, + AIRFLOW_TASK_QUALIFIED_NAME, +) + + +@pytest.mark.parametrize( + "name, airflow_dag_qualified_name, message", + [ + (None, "airflow/dag", "name is required"), + (AIRFLOW_TASK_NAME, None, "airflow_dag_qualified_name is required"), + ], +) +def test_creator_with_missing_parameters_raises_value_error( + name: str, airflow_dag_qualified_name: str, message: str +): + """Test that creator raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + AirflowTask.creator( + name=name, airflow_dag_qualified_name=airflow_dag_qualified_name + ) + + +def test_creator(): + """Test that creator properly initializes an AirflowTask with all derived fields.""" + sut = AirflowTask.creator( + name=AIRFLOW_TASK_NAME, + airflow_dag_qualified_name=AIRFLOW_DAG_QUALIFIED_NAME, + ) + + assert sut.name == AIRFLOW_TASK_NAME + assert sut.connector_name == "airflow" + assert sut.airflow_dag_qualified_name == AIRFLOW_DAG_QUALIFIED_NAME + assert sut.connection_qualified_name == AIRFLOW_CONNECTION_QUALIFIED_NAME + assert sut.qualified_name == AIRFLOW_TASK_QUALIFIED_NAME + + +def test_overload_creator(): + """Test creator with connection_qualified_name explicitly provided.""" + sut = AirflowTask.creator( + name=AIRFLOW_TASK_NAME, + airflow_dag_qualified_name=AIRFLOW_DAG_QUALIFIED_NAME, + connection_qualified_name=AIRFLOW_CONNECTION_QUALIFIED_NAME, + ) + + assert sut.name == AIRFLOW_TASK_NAME + assert sut.connector_name == "airflow" + assert sut.airflow_dag_qualified_name == AIRFLOW_DAG_QUALIFIED_NAME + assert sut.connection_qualified_name == AIRFLOW_CONNECTION_QUALIFIED_NAME + assert sut.qualified_name == AIRFLOW_TASK_QUALIFIED_NAME + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, AIRFLOW_TASK_NAME, "qualified_name is required"), + (AIRFLOW_TASK_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test that updater raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + AirflowTask.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test that updater creates an AirflowTask instance for modification.""" + sut = AirflowTask.updater( + name=AIRFLOW_TASK_NAME, qualified_name=AIRFLOW_TASK_QUALIFIED_NAME + ) + + assert sut.name == AIRFLOW_TASK_NAME + assert sut.qualified_name == AIRFLOW_TASK_QUALIFIED_NAME + + +def test_trim_to_required(): + """Test that trim_to_required returns an AirflowTask with only required fields.""" + sut = AirflowTask.updater( + name=AIRFLOW_TASK_NAME, qualified_name=AIRFLOW_TASK_QUALIFIED_NAME + ).trim_to_required() + + assert sut.name == AIRFLOW_TASK_NAME + assert sut.qualified_name == AIRFLOW_TASK_QUALIFIED_NAME + + +def test_basic_construction(): + """Test basic AirflowTask construction with minimal parameters.""" + task = AirflowTask( + name=AIRFLOW_TASK_NAME, qualified_name=AIRFLOW_TASK_QUALIFIED_NAME + ) + + assert task.name == AIRFLOW_TASK_NAME + assert task.qualified_name == AIRFLOW_TASK_QUALIFIED_NAME + assert task.type_name == "AirflowTask" + + +def test_unset_fields(): + """Test that optional fields default to UNSET.""" + task = AirflowTask( + name=AIRFLOW_TASK_NAME, qualified_name=AIRFLOW_TASK_QUALIFIED_NAME + ) + + assert task.airflow_task_operator_class is UNSET + assert task.airflow_dag_name is UNSET + assert task.airflow_task_sql is UNSET + + +def test_serialization_to_json_nested(serde): + """Test serialization to nested JSON format (API format).""" + task = AirflowTask.creator( + name=AIRFLOW_TASK_NAME, + airflow_dag_qualified_name=AIRFLOW_DAG_QUALIFIED_NAME, + ) + + json_str = task.to_json(nested=True, serde=serde) + data = json.loads(json_str) + + assert data["typeName"] == "AirflowTask" + assert "attributes" in data + assert data["attributes"]["name"] == AIRFLOW_TASK_NAME + + +def test_round_trip_serialization(serde): + """Test that serialization and deserialization preserve all data.""" + original = AirflowTask.creator( + name=AIRFLOW_TASK_NAME, + airflow_dag_qualified_name=AIRFLOW_DAG_QUALIFIED_NAME, + ) + + json_str = original.to_json(nested=True, serde=serde) + restored = AirflowTask.from_json(json_str, serde=serde) + + assert restored.name == original.name + assert restored.qualified_name == original.qualified_name + + +def test_creator_with_guid(): + """Test that creator initializes a temporary GUID for new assets.""" + task = AirflowTask.creator( + name=AIRFLOW_TASK_NAME, + airflow_dag_qualified_name=AIRFLOW_DAG_QUALIFIED_NAME, + ) + + assert task.guid is not UNSET + assert task.guid is not None + assert isinstance(task.guid, str) + assert task.guid.startswith("-") diff --git a/tests_v9/unit/model/anaplan_app_test.py b/tests_v9/unit/model/anaplan_app_test.py new file mode 100644 index 000000000..26b1bbe59 --- /dev/null +++ b/tests_v9/unit/model/anaplan_app_test.py @@ -0,0 +1,79 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for AnaplanApp model in pyatlan_v9.""" + +import pytest + +from pyatlan_v9.model import AnaplanApp +from tests_v9.unit.model.constants import ( + ANAPLAN_APP_NAME, + ANAPLAN_APP_QUALIFIED_NAME, + ANAPLAN_CONNECTION_QUALIFIED_NAME, + ANAPLAN_CONNECTOR_TYPE, +) + + +@pytest.mark.parametrize( + "name, connection_qualified_name, message", + [ + (None, "connection/name", "name is required"), + (ANAPLAN_APP_NAME, None, "connection_qualified_name is required"), + ], +) +def test_creator_with_missing_parameters_raise_value_error( + name: str, connection_qualified_name: str, message: str +): + """Test creator validates required parameters.""" + with pytest.raises(ValueError, match=message): + AnaplanApp.creator( + name=name, connection_qualified_name=connection_qualified_name + ) + + +def test_creator(): + """Test creator initializes expected derived fields.""" + sut = AnaplanApp.creator( + name=ANAPLAN_APP_NAME, + connection_qualified_name=ANAPLAN_CONNECTION_QUALIFIED_NAME, + ) + + assert sut.name == ANAPLAN_APP_NAME + assert sut.connection_qualified_name == ANAPLAN_CONNECTION_QUALIFIED_NAME + assert sut.qualified_name == ANAPLAN_APP_QUALIFIED_NAME + assert sut.connector_name == ANAPLAN_CONNECTOR_TYPE + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, ANAPLAN_CONNECTION_QUALIFIED_NAME, "qualified_name is required"), + (ANAPLAN_APP_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test updater validates required parameters.""" + with pytest.raises(ValueError, match=message): + AnaplanApp.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test updater creates AnaplanApp for modification.""" + sut = AnaplanApp.updater( + qualified_name=ANAPLAN_APP_QUALIFIED_NAME, name=ANAPLAN_APP_NAME + ) + + assert sut.qualified_name == ANAPLAN_APP_QUALIFIED_NAME + assert sut.name == ANAPLAN_APP_NAME + + +def test_trim_to_required(): + """Test trim_to_required keeps only updater-required fields.""" + sut = AnaplanApp.updater( + name=ANAPLAN_APP_NAME, qualified_name=ANAPLAN_APP_QUALIFIED_NAME + ).trim_to_required() + + assert sut.name == ANAPLAN_APP_NAME + assert sut.qualified_name == ANAPLAN_APP_QUALIFIED_NAME diff --git a/tests_v9/unit/model/anaplan_dimension_test.py b/tests_v9/unit/model/anaplan_dimension_test.py new file mode 100644 index 000000000..0a3248d16 --- /dev/null +++ b/tests_v9/unit/model/anaplan_dimension_test.py @@ -0,0 +1,78 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for AnaplanDimension model in pyatlan_v9.""" + +import pytest + +from pyatlan_v9.model import AnaplanDimension +from tests_v9.unit.model.constants import ( + ANAPLAN_CONNECTION_QUALIFIED_NAME, + ANAPLAN_CONNECTOR_TYPE, + ANAPLAN_DIMENSION_NAME, + ANAPLAN_DIMENSION_QUALIFIED_NAME, + ANAPLAN_MODEL_QUALIFIED_NAME, +) + + +@pytest.mark.parametrize( + "name, model_qualified_name, message", + [ + (None, "connection/name", "name is required"), + (ANAPLAN_DIMENSION_NAME, None, "model_qualified_name is required"), + ], +) +def test_creator_with_missing_parameters_raise_value_error( + name: str, model_qualified_name: str, message: str +): + """Test creator validates required parameters.""" + with pytest.raises(ValueError, match=message): + AnaplanDimension.creator(name=name, model_qualified_name=model_qualified_name) + + +def test_creator(): + """Test creator initializes expected derived fields.""" + sut = AnaplanDimension.creator( + name=ANAPLAN_DIMENSION_NAME, + model_qualified_name=ANAPLAN_MODEL_QUALIFIED_NAME, + ) + + assert sut.name == ANAPLAN_DIMENSION_NAME + assert sut.connection_qualified_name == ANAPLAN_CONNECTION_QUALIFIED_NAME + assert sut.qualified_name == ANAPLAN_DIMENSION_QUALIFIED_NAME + assert sut.connector_name == ANAPLAN_CONNECTOR_TYPE + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, ANAPLAN_MODEL_QUALIFIED_NAME, "qualified_name is required"), + (ANAPLAN_DIMENSION_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test updater validates required parameters.""" + with pytest.raises(ValueError, match=message): + AnaplanDimension.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test updater creates AnaplanDimension for modification.""" + sut = AnaplanDimension.updater( + qualified_name=ANAPLAN_DIMENSION_QUALIFIED_NAME, name=ANAPLAN_DIMENSION_NAME + ) + + assert sut.qualified_name == ANAPLAN_DIMENSION_QUALIFIED_NAME + assert sut.name == ANAPLAN_DIMENSION_NAME + + +def test_trim_to_required(): + """Test trim_to_required keeps only updater-required fields.""" + sut = AnaplanDimension.updater( + name=ANAPLAN_DIMENSION_NAME, qualified_name=ANAPLAN_DIMENSION_QUALIFIED_NAME + ).trim_to_required() + + assert sut.name == ANAPLAN_DIMENSION_NAME + assert sut.qualified_name == ANAPLAN_DIMENSION_QUALIFIED_NAME diff --git a/tests_v9/unit/model/anaplan_line_item_test.py b/tests_v9/unit/model/anaplan_line_item_test.py new file mode 100644 index 000000000..863ce8597 --- /dev/null +++ b/tests_v9/unit/model/anaplan_line_item_test.py @@ -0,0 +1,78 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for AnaplanLineItem model in pyatlan_v9.""" + +import pytest + +from pyatlan_v9.model import AnaplanLineItem +from tests_v9.unit.model.constants import ( + ANAPLAN_CONNECTION_QUALIFIED_NAME, + ANAPLAN_CONNECTOR_TYPE, + ANAPLAN_LINE_ITEM_NAME, + ANAPLAN_LINE_ITEM_QUALIFIED_NAME, + ANAPLAN_MODULE_QUALIFIED_NAME, +) + + +@pytest.mark.parametrize( + "name, module_qualified_name, message", + [ + (None, "connection/name", "name is required"), + (ANAPLAN_LINE_ITEM_NAME, None, "module_qualified_name is required"), + ], +) +def test_creator_with_missing_parameters_raise_value_error( + name: str, module_qualified_name: str, message: str +): + """Test creator validates required parameters.""" + with pytest.raises(ValueError, match=message): + AnaplanLineItem.creator(name=name, module_qualified_name=module_qualified_name) + + +def test_creator(): + """Test creator initializes expected derived fields.""" + sut = AnaplanLineItem.creator( + name=ANAPLAN_LINE_ITEM_NAME, + module_qualified_name=ANAPLAN_MODULE_QUALIFIED_NAME, + ) + + assert sut.name == ANAPLAN_LINE_ITEM_NAME + assert sut.connection_qualified_name == ANAPLAN_CONNECTION_QUALIFIED_NAME + assert sut.qualified_name == ANAPLAN_LINE_ITEM_QUALIFIED_NAME + assert sut.connector_name == ANAPLAN_CONNECTOR_TYPE + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, ANAPLAN_MODULE_QUALIFIED_NAME, "qualified_name is required"), + (ANAPLAN_LINE_ITEM_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test updater validates required parameters.""" + with pytest.raises(ValueError, match=message): + AnaplanLineItem.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test updater creates AnaplanLineItem for modification.""" + sut = AnaplanLineItem.updater( + qualified_name=ANAPLAN_LINE_ITEM_QUALIFIED_NAME, name=ANAPLAN_LINE_ITEM_NAME + ) + + assert sut.qualified_name == ANAPLAN_LINE_ITEM_QUALIFIED_NAME + assert sut.name == ANAPLAN_LINE_ITEM_NAME + + +def test_trim_to_required(): + """Test trim_to_required keeps only updater-required fields.""" + sut = AnaplanLineItem.updater( + name=ANAPLAN_LINE_ITEM_NAME, qualified_name=ANAPLAN_LINE_ITEM_QUALIFIED_NAME + ).trim_to_required() + + assert sut.name == ANAPLAN_LINE_ITEM_NAME + assert sut.qualified_name == ANAPLAN_LINE_ITEM_QUALIFIED_NAME diff --git a/tests_v9/unit/model/anaplan_list_test.py b/tests_v9/unit/model/anaplan_list_test.py new file mode 100644 index 000000000..e4af110b9 --- /dev/null +++ b/tests_v9/unit/model/anaplan_list_test.py @@ -0,0 +1,78 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for AnaplanList model in pyatlan_v9.""" + +import pytest + +from pyatlan_v9.model import AnaplanList +from tests_v9.unit.model.constants import ( + ANAPLAN_CONNECTION_QUALIFIED_NAME, + ANAPLAN_CONNECTOR_TYPE, + ANAPLAN_LIST_NAME, + ANAPLAN_LIST_QUALIFIED_NAME, + ANAPLAN_MODEL_QUALIFIED_NAME, +) + + +@pytest.mark.parametrize( + "name, model_qualified_name, message", + [ + (None, "connection/name", "name is required"), + (ANAPLAN_LIST_NAME, None, "model_qualified_name is required"), + ], +) +def test_creator_with_missing_parameters_raise_value_error( + name: str, model_qualified_name: str, message: str +): + """Test creator validates required parameters.""" + with pytest.raises(ValueError, match=message): + AnaplanList.creator(name=name, model_qualified_name=model_qualified_name) + + +def test_creator(): + """Test creator initializes expected derived fields.""" + sut = AnaplanList.creator( + name=ANAPLAN_LIST_NAME, + model_qualified_name=ANAPLAN_MODEL_QUALIFIED_NAME, + ) + + assert sut.name == ANAPLAN_LIST_NAME + assert sut.connection_qualified_name == ANAPLAN_CONNECTION_QUALIFIED_NAME + assert sut.qualified_name == ANAPLAN_LIST_QUALIFIED_NAME + assert sut.connector_name == ANAPLAN_CONNECTOR_TYPE + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, ANAPLAN_MODEL_QUALIFIED_NAME, "qualified_name is required"), + (ANAPLAN_LIST_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test updater validates required parameters.""" + with pytest.raises(ValueError, match=message): + AnaplanList.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test updater creates AnaplanList for modification.""" + sut = AnaplanList.updater( + qualified_name=ANAPLAN_LIST_QUALIFIED_NAME, name=ANAPLAN_LIST_NAME + ) + + assert sut.qualified_name == ANAPLAN_LIST_QUALIFIED_NAME + assert sut.name == ANAPLAN_LIST_NAME + + +def test_trim_to_required(): + """Test trim_to_required keeps only updater-required fields.""" + sut = AnaplanList.updater( + name=ANAPLAN_LIST_NAME, qualified_name=ANAPLAN_LIST_QUALIFIED_NAME + ).trim_to_required() + + assert sut.name == ANAPLAN_LIST_NAME + assert sut.qualified_name == ANAPLAN_LIST_QUALIFIED_NAME diff --git a/tests_v9/unit/model/anaplan_model_test.py b/tests_v9/unit/model/anaplan_model_test.py new file mode 100644 index 000000000..07b6b3114 --- /dev/null +++ b/tests_v9/unit/model/anaplan_model_test.py @@ -0,0 +1,80 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for AnaplanModel model in pyatlan_v9.""" + +import pytest + +from pyatlan_v9.model import AnaplanModel +from tests_v9.unit.model.constants import ( + ANAPLAN_CONNECTION_QUALIFIED_NAME, + ANAPLAN_CONNECTOR_TYPE, + ANAPLAN_MODEL_NAME, + ANAPLAN_MODEL_QUALIFIED_NAME, + ANAPLAN_WORKSPACE_QUALIFIED_NAME, +) + + +@pytest.mark.parametrize( + "name, workspace_qualified_name, message", + [ + (None, "connection/name", "name is required"), + (ANAPLAN_MODEL_NAME, None, "workspace_qualified_name is required"), + ], +) +def test_creator_with_missing_parameters_raise_value_error( + name: str, workspace_qualified_name: str, message: str +): + """Test creator validates required parameters.""" + with pytest.raises(ValueError, match=message): + AnaplanModel.creator( + name=name, workspace_qualified_name=workspace_qualified_name + ) + + +def test_creator(): + """Test creator initializes expected derived fields.""" + sut = AnaplanModel.creator( + name=ANAPLAN_MODEL_NAME, + workspace_qualified_name=ANAPLAN_WORKSPACE_QUALIFIED_NAME, + ) + + assert sut.name == ANAPLAN_MODEL_NAME + assert sut.connection_qualified_name == ANAPLAN_CONNECTION_QUALIFIED_NAME + assert sut.qualified_name == ANAPLAN_MODEL_QUALIFIED_NAME + assert sut.connector_name == ANAPLAN_CONNECTOR_TYPE + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, ANAPLAN_WORKSPACE_QUALIFIED_NAME, "qualified_name is required"), + (ANAPLAN_MODEL_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test updater validates required parameters.""" + with pytest.raises(ValueError, match=message): + AnaplanModel.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test updater creates AnaplanModel for modification.""" + sut = AnaplanModel.updater( + qualified_name=ANAPLAN_MODEL_QUALIFIED_NAME, name=ANAPLAN_MODEL_NAME + ) + + assert sut.qualified_name == ANAPLAN_MODEL_QUALIFIED_NAME + assert sut.name == ANAPLAN_MODEL_NAME + + +def test_trim_to_required(): + """Test trim_to_required keeps only updater-required fields.""" + sut = AnaplanModel.updater( + name=ANAPLAN_MODEL_NAME, qualified_name=ANAPLAN_MODEL_QUALIFIED_NAME + ).trim_to_required() + + assert sut.name == ANAPLAN_MODEL_NAME + assert sut.qualified_name == ANAPLAN_MODEL_QUALIFIED_NAME diff --git a/tests_v9/unit/model/anaplan_module_test.py b/tests_v9/unit/model/anaplan_module_test.py new file mode 100644 index 000000000..3f664e990 --- /dev/null +++ b/tests_v9/unit/model/anaplan_module_test.py @@ -0,0 +1,78 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for AnaplanModule model in pyatlan_v9.""" + +import pytest + +from pyatlan_v9.model import AnaplanModule +from tests_v9.unit.model.constants import ( + ANAPLAN_CONNECTION_QUALIFIED_NAME, + ANAPLAN_CONNECTOR_TYPE, + ANAPLAN_MODEL_QUALIFIED_NAME, + ANAPLAN_MODULE_NAME, + ANAPLAN_MODULE_QUALIFIED_NAME, +) + + +@pytest.mark.parametrize( + "name, model_qualified_name, message", + [ + (None, "connection/name", "name is required"), + (ANAPLAN_MODULE_NAME, None, "model_qualified_name is required"), + ], +) +def test_creator_with_missing_parameters_raise_value_error( + name: str, model_qualified_name: str, message: str +): + """Test creator validates required parameters.""" + with pytest.raises(ValueError, match=message): + AnaplanModule.creator(name=name, model_qualified_name=model_qualified_name) + + +def test_creator(): + """Test creator initializes expected derived fields.""" + sut = AnaplanModule.creator( + name=ANAPLAN_MODULE_NAME, + model_qualified_name=ANAPLAN_MODEL_QUALIFIED_NAME, + ) + + assert sut.name == ANAPLAN_MODULE_NAME + assert sut.connection_qualified_name == ANAPLAN_CONNECTION_QUALIFIED_NAME + assert sut.qualified_name == ANAPLAN_MODULE_QUALIFIED_NAME + assert sut.connector_name == ANAPLAN_CONNECTOR_TYPE + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, ANAPLAN_MODEL_QUALIFIED_NAME, "qualified_name is required"), + (ANAPLAN_MODULE_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test updater validates required parameters.""" + with pytest.raises(ValueError, match=message): + AnaplanModule.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test updater creates AnaplanModule for modification.""" + sut = AnaplanModule.updater( + qualified_name=ANAPLAN_MODULE_QUALIFIED_NAME, name=ANAPLAN_MODULE_NAME + ) + + assert sut.qualified_name == ANAPLAN_MODULE_QUALIFIED_NAME + assert sut.name == ANAPLAN_MODULE_NAME + + +def test_trim_to_required(): + """Test trim_to_required keeps only updater-required fields.""" + sut = AnaplanModule.updater( + name=ANAPLAN_MODULE_NAME, qualified_name=ANAPLAN_MODULE_QUALIFIED_NAME + ).trim_to_required() + + assert sut.name == ANAPLAN_MODULE_NAME + assert sut.qualified_name == ANAPLAN_MODULE_QUALIFIED_NAME diff --git a/tests_v9/unit/model/anaplan_page_test.py b/tests_v9/unit/model/anaplan_page_test.py new file mode 100644 index 000000000..7f4937a3a --- /dev/null +++ b/tests_v9/unit/model/anaplan_page_test.py @@ -0,0 +1,78 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for AnaplanPage model in pyatlan_v9.""" + +import pytest + +from pyatlan_v9.model import AnaplanPage +from tests_v9.unit.model.constants import ( + ANAPLAN_APP_QUALIFIED_NAME, + ANAPLAN_CONNECTION_QUALIFIED_NAME, + ANAPLAN_CONNECTOR_TYPE, + ANAPLAN_PAGE_NAME, + ANAPLAN_PAGE_QUALIFIED_NAME, +) + + +@pytest.mark.parametrize( + "name, app_qualified_name, message", + [ + (None, "connection/name", "name is required"), + (ANAPLAN_PAGE_NAME, None, "app_qualified_name is required"), + ], +) +def test_creator_with_missing_parameters_raise_value_error( + name: str, app_qualified_name: str, message: str +): + """Test creator validates required parameters.""" + with pytest.raises(ValueError, match=message): + AnaplanPage.creator(name=name, app_qualified_name=app_qualified_name) + + +def test_creator(): + """Test creator initializes expected derived fields.""" + sut = AnaplanPage.creator( + name=ANAPLAN_PAGE_NAME, + app_qualified_name=ANAPLAN_APP_QUALIFIED_NAME, + ) + + assert sut.name == ANAPLAN_PAGE_NAME + assert sut.connection_qualified_name == ANAPLAN_CONNECTION_QUALIFIED_NAME + assert sut.qualified_name == ANAPLAN_PAGE_QUALIFIED_NAME + assert sut.connector_name == ANAPLAN_CONNECTOR_TYPE + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, ANAPLAN_APP_QUALIFIED_NAME, "qualified_name is required"), + (ANAPLAN_PAGE_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test updater validates required parameters.""" + with pytest.raises(ValueError, match=message): + AnaplanPage.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test updater creates AnaplanPage for modification.""" + sut = AnaplanPage.updater( + qualified_name=ANAPLAN_PAGE_QUALIFIED_NAME, name=ANAPLAN_PAGE_NAME + ) + + assert sut.qualified_name == ANAPLAN_PAGE_QUALIFIED_NAME + assert sut.name == ANAPLAN_PAGE_NAME + + +def test_trim_to_required(): + """Test trim_to_required keeps only updater-required fields.""" + sut = AnaplanPage.updater( + name=ANAPLAN_PAGE_NAME, qualified_name=ANAPLAN_PAGE_QUALIFIED_NAME + ).trim_to_required() + + assert sut.name == ANAPLAN_PAGE_NAME + assert sut.qualified_name == ANAPLAN_PAGE_QUALIFIED_NAME diff --git a/tests_v9/unit/model/anaplan_system_dimension_test.py b/tests_v9/unit/model/anaplan_system_dimension_test.py new file mode 100644 index 000000000..3f72ddb91 --- /dev/null +++ b/tests_v9/unit/model/anaplan_system_dimension_test.py @@ -0,0 +1,81 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for AnaplanSystemDimension model in pyatlan_v9.""" + +import pytest + +from pyatlan_v9.model import AnaplanSystemDimension +from tests_v9.unit.model.constants import ( + ANAPLAN_CONNECTION_QUALIFIED_NAME, + ANAPLAN_CONNECTOR_TYPE, + ANAPLAN_SYSTEM_DIMENSION_NAME, + ANAPLAN_SYSTEM_DIMENSION_QUALIFIED_NAME, +) + + +@pytest.mark.parametrize( + "name, connection_qualified_name, message", + [ + (None, "connection/name", "name is required"), + (ANAPLAN_SYSTEM_DIMENSION_NAME, None, "connection_qualified_name is required"), + ], +) +def test_creator_with_missing_parameters_raise_value_error( + name: str, connection_qualified_name: str, message: str +): + """Test creator validates required parameters.""" + with pytest.raises(ValueError, match=message): + AnaplanSystemDimension.creator( + name=name, connection_qualified_name=connection_qualified_name + ) + + +def test_creator(): + """Test creator initializes expected derived fields.""" + sut = AnaplanSystemDimension.creator( + name=ANAPLAN_SYSTEM_DIMENSION_NAME, + connection_qualified_name=ANAPLAN_CONNECTION_QUALIFIED_NAME, + ) + + assert sut.name == ANAPLAN_SYSTEM_DIMENSION_NAME + assert sut.connection_qualified_name == ANAPLAN_CONNECTION_QUALIFIED_NAME + assert sut.qualified_name == ANAPLAN_SYSTEM_DIMENSION_QUALIFIED_NAME + assert sut.connector_name == ANAPLAN_CONNECTOR_TYPE + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, ANAPLAN_CONNECTION_QUALIFIED_NAME, "qualified_name is required"), + (ANAPLAN_SYSTEM_DIMENSION_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test updater validates required parameters.""" + with pytest.raises(ValueError, match=message): + AnaplanSystemDimension.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test updater creates AnaplanSystemDimension for modification.""" + sut = AnaplanSystemDimension.updater( + qualified_name=ANAPLAN_SYSTEM_DIMENSION_QUALIFIED_NAME, + name=ANAPLAN_SYSTEM_DIMENSION_NAME, + ) + + assert sut.qualified_name == ANAPLAN_SYSTEM_DIMENSION_QUALIFIED_NAME + assert sut.name == ANAPLAN_SYSTEM_DIMENSION_NAME + + +def test_trim_to_required(): + """Test trim_to_required keeps only updater-required fields.""" + sut = AnaplanSystemDimension.updater( + name=ANAPLAN_SYSTEM_DIMENSION_NAME, + qualified_name=ANAPLAN_SYSTEM_DIMENSION_QUALIFIED_NAME, + ).trim_to_required() + + assert sut.name == ANAPLAN_SYSTEM_DIMENSION_NAME + assert sut.qualified_name == ANAPLAN_SYSTEM_DIMENSION_QUALIFIED_NAME diff --git a/tests_v9/unit/model/anaplan_view_test.py b/tests_v9/unit/model/anaplan_view_test.py new file mode 100644 index 000000000..22b1e87e0 --- /dev/null +++ b/tests_v9/unit/model/anaplan_view_test.py @@ -0,0 +1,78 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for AnaplanView model in pyatlan_v9.""" + +import pytest + +from pyatlan_v9.model import AnaplanView +from tests_v9.unit.model.constants import ( + ANAPLAN_CONNECTION_QUALIFIED_NAME, + ANAPLAN_CONNECTOR_TYPE, + ANAPLAN_MODULE_QUALIFIED_NAME, + ANAPLAN_VIEW_NAME, + ANAPLAN_VIEW_QUALIFIED_NAME, +) + + +@pytest.mark.parametrize( + "name, module_qualified_name, message", + [ + (None, "connection/name", "name is required"), + (ANAPLAN_VIEW_NAME, None, "module_qualified_name is required"), + ], +) +def test_creator_with_missing_parameters_raise_value_error( + name: str, module_qualified_name: str, message: str +): + """Test creator validates required parameters.""" + with pytest.raises(ValueError, match=message): + AnaplanView.creator(name=name, module_qualified_name=module_qualified_name) + + +def test_creator(): + """Test creator initializes expected derived fields.""" + sut = AnaplanView.creator( + name=ANAPLAN_VIEW_NAME, + module_qualified_name=ANAPLAN_MODULE_QUALIFIED_NAME, + ) + + assert sut.name == ANAPLAN_VIEW_NAME + assert sut.connection_qualified_name == ANAPLAN_CONNECTION_QUALIFIED_NAME + assert sut.qualified_name == ANAPLAN_VIEW_QUALIFIED_NAME + assert sut.connector_name == ANAPLAN_CONNECTOR_TYPE + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, ANAPLAN_MODULE_QUALIFIED_NAME, "qualified_name is required"), + (ANAPLAN_VIEW_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test updater validates required parameters.""" + with pytest.raises(ValueError, match=message): + AnaplanView.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test updater creates AnaplanView for modification.""" + sut = AnaplanView.updater( + qualified_name=ANAPLAN_VIEW_QUALIFIED_NAME, name=ANAPLAN_VIEW_NAME + ) + + assert sut.qualified_name == ANAPLAN_VIEW_QUALIFIED_NAME + assert sut.name == ANAPLAN_VIEW_NAME + + +def test_trim_to_required(): + """Test trim_to_required keeps only updater-required fields.""" + sut = AnaplanView.updater( + name=ANAPLAN_VIEW_NAME, qualified_name=ANAPLAN_VIEW_QUALIFIED_NAME + ).trim_to_required() + + assert sut.name == ANAPLAN_VIEW_NAME + assert sut.qualified_name == ANAPLAN_VIEW_QUALIFIED_NAME diff --git a/tests_v9/unit/model/anaplan_workspace_test.py b/tests_v9/unit/model/anaplan_workspace_test.py new file mode 100644 index 000000000..f39df2e8d --- /dev/null +++ b/tests_v9/unit/model/anaplan_workspace_test.py @@ -0,0 +1,79 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for AnaplanWorkspace model in pyatlan_v9.""" + +import pytest + +from pyatlan_v9.model import AnaplanWorkspace +from tests_v9.unit.model.constants import ( + ANAPLAN_CONNECTION_QUALIFIED_NAME, + ANAPLAN_CONNECTOR_TYPE, + ANAPLAN_WORKSPACE_NAME, + ANAPLAN_WORKSPACE_QUALIFIED_NAME, +) + + +@pytest.mark.parametrize( + "name, connection_qualified_name, message", + [ + (None, "connection/name", "name is required"), + (ANAPLAN_WORKSPACE_NAME, None, "connection_qualified_name is required"), + ], +) +def test_creator_with_missing_parameters_raise_value_error( + name: str, connection_qualified_name: str, message: str +): + """Test creator validates required parameters.""" + with pytest.raises(ValueError, match=message): + AnaplanWorkspace.creator( + name=name, connection_qualified_name=connection_qualified_name + ) + + +def test_creator(): + """Test creator initializes expected derived fields.""" + sut = AnaplanWorkspace.creator( + name=ANAPLAN_WORKSPACE_NAME, + connection_qualified_name=ANAPLAN_CONNECTION_QUALIFIED_NAME, + ) + + assert sut.name == ANAPLAN_WORKSPACE_NAME + assert sut.connection_qualified_name == ANAPLAN_CONNECTION_QUALIFIED_NAME + assert sut.qualified_name == ANAPLAN_WORKSPACE_QUALIFIED_NAME + assert sut.connector_name == ANAPLAN_CONNECTOR_TYPE + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, ANAPLAN_CONNECTION_QUALIFIED_NAME, "qualified_name is required"), + (ANAPLAN_WORKSPACE_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test updater validates required parameters.""" + with pytest.raises(ValueError, match=message): + AnaplanWorkspace.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test updater creates AnaplanWorkspace for modification.""" + sut = AnaplanWorkspace.updater( + qualified_name=ANAPLAN_WORKSPACE_QUALIFIED_NAME, name=ANAPLAN_WORKSPACE_NAME + ) + + assert sut.qualified_name == ANAPLAN_WORKSPACE_QUALIFIED_NAME + assert sut.name == ANAPLAN_WORKSPACE_NAME + + +def test_trim_to_required(): + """Test trim_to_required keeps only updater-required fields.""" + sut = AnaplanWorkspace.updater( + name=ANAPLAN_WORKSPACE_NAME, qualified_name=ANAPLAN_WORKSPACE_QUALIFIED_NAME + ).trim_to_required() + + assert sut.name == ANAPLAN_WORKSPACE_NAME + assert sut.qualified_name == ANAPLAN_WORKSPACE_QUALIFIED_NAME diff --git a/tests_v9/unit/model/application_field_test.py b/tests_v9/unit/model/application_field_test.py new file mode 100644 index 000000000..3ed2b0bea --- /dev/null +++ b/tests_v9/unit/model/application_field_test.py @@ -0,0 +1,82 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for ApplicationField model in pyatlan_v9.""" + +import pytest + +from pyatlan_v9.model import ApplicationField +from tests_v9.unit.model.constants import ( + APP_CONNECTION_QUALIFIED_NAME, + APP_CONNECTOR_TYPE, + APPLICATION_FIELD_NAME, + APPLICATION_FIELD_QUALIFIED_NAME, + APPLICATION_QUALIFIED_NAME, +) + + +@pytest.mark.parametrize( + "name, application_qualified_name, message", + [ + (None, "connection/name", "name is required"), + (APPLICATION_FIELD_NAME, None, "application_qualified_name is required"), + ], +) +def test_creator_with_missing_parameters_raise_value_error( + name: str, application_qualified_name: str, message: str +): + """Test creator raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + ApplicationField.creator( + name=name, application_qualified_name=application_qualified_name + ) + + +def test_creator(): + """Test creator initializes expected derived fields.""" + sut = ApplicationField.creator( + name=APPLICATION_FIELD_NAME, + application_qualified_name=APPLICATION_QUALIFIED_NAME, + ) + + assert sut.name == APPLICATION_FIELD_NAME + assert sut.connection_qualified_name == APP_CONNECTION_QUALIFIED_NAME + assert sut.qualified_name == APPLICATION_FIELD_QUALIFIED_NAME + assert sut.connector_name == APP_CONNECTOR_TYPE + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, APPLICATION_FIELD_QUALIFIED_NAME, "qualified_name is required"), + (APPLICATION_FIELD_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test updater raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + ApplicationField.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test updater creates ApplicationField for modification.""" + sut = ApplicationField.updater( + qualified_name=APPLICATION_FIELD_QUALIFIED_NAME, + name=APPLICATION_FIELD_NAME, + ) + + assert sut.qualified_name == APPLICATION_FIELD_QUALIFIED_NAME + assert sut.name == APPLICATION_FIELD_NAME + + +def test_trim_to_required(): + """Test trim_to_required preserves updater-required fields.""" + sut = ApplicationField.updater( + name=APPLICATION_FIELD_NAME, + qualified_name=APPLICATION_FIELD_QUALIFIED_NAME, + ).trim_to_required() + + assert sut.name == APPLICATION_FIELD_NAME + assert sut.qualified_name == APPLICATION_FIELD_QUALIFIED_NAME diff --git a/tests_v9/unit/model/application_test.py b/tests_v9/unit/model/application_test.py new file mode 100644 index 000000000..b646bd264 --- /dev/null +++ b/tests_v9/unit/model/application_test.py @@ -0,0 +1,81 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for Application model in pyatlan_v9.""" + +import pytest + +from pyatlan_v9.model import Application +from tests_v9.unit.model.constants import ( + APP_CONNECTION_QUALIFIED_NAME, + APP_CONNECTOR_TYPE, + APPLICATION_NAME, + APPLICATION_QUALIFIED_NAME, +) + + +@pytest.mark.parametrize( + "name, connection_qualified_name, message", + [ + (None, "connection/name", "name is required"), + (APPLICATION_NAME, None, "connection_qualified_name is required"), + ], +) +def test_creator_with_missing_parameters_raise_value_error( + name: str, connection_qualified_name: str, message: str +): + """Test creator raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + Application.creator( + name=name, connection_qualified_name=connection_qualified_name + ) + + +def test_creator(): + """Test creator initializes expected derived fields.""" + sut = Application.creator( + name=APPLICATION_NAME, + connection_qualified_name=APP_CONNECTION_QUALIFIED_NAME, + ) + + assert sut.name == APPLICATION_NAME + assert sut.connection_qualified_name == APP_CONNECTION_QUALIFIED_NAME + assert sut.qualified_name == APPLICATION_QUALIFIED_NAME + assert sut.connector_name == APP_CONNECTOR_TYPE + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, APPLICATION_QUALIFIED_NAME, "qualified_name is required"), + (APPLICATION_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test updater raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + Application.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test updater creates Application for modification.""" + sut = Application.updater( + qualified_name=APPLICATION_QUALIFIED_NAME, + name=APPLICATION_NAME, + ) + + assert sut.qualified_name == APPLICATION_QUALIFIED_NAME + assert sut.name == APPLICATION_NAME + + +def test_trim_to_required(): + """Test trim_to_required preserves updater-required fields.""" + sut = Application.updater( + name=APPLICATION_NAME, + qualified_name=APPLICATION_QUALIFIED_NAME, + ).trim_to_required() + + assert sut.name == APPLICATION_NAME + assert sut.qualified_name == APPLICATION_QUALIFIED_NAME diff --git a/tests_v9/unit/model/azure_event_consumer_group_test.py b/tests_v9/unit/model/azure_event_consumer_group_test.py new file mode 100644 index 000000000..3311a86df --- /dev/null +++ b/tests_v9/unit/model/azure_event_consumer_group_test.py @@ -0,0 +1,82 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for AzureEventHubConsumerGroup model in pyatlan_v9.""" + +import pytest + +from pyatlan_v9.model import AzureEventHubConsumerGroup +from tests_v9.unit.model.constants import ( + EVENT_HUB_CONNECTION_QUALIFIED_NAME, + EVENT_HUB_CONSUMER_GROUP_NAME, + EVENT_HUB_CONSUMER_GROUP_QUALIFIED_NAME, + EVENT_HUB_QUALIFIED_NAMES, +) + + +@pytest.mark.parametrize( + "name, event_hub_qualified_names, message", + [ + (None, "event/hub", "name is required"), + (EVENT_HUB_QUALIFIED_NAMES, None, "event_hub_qualified_names is required"), + ], +) +def test_creator_with_missing_parameters_raise_value_error( + name: str, event_hub_qualified_names: str, message: str +): + """Test creator validates required parameters.""" + with pytest.raises(ValueError, match=message): + AzureEventHubConsumerGroup.creator( + name=name, event_hub_qualified_names=event_hub_qualified_names + ) + + +def test_creator(): + """Test creator initializes expected derived fields.""" + group = AzureEventHubConsumerGroup.creator( + name=EVENT_HUB_CONSUMER_GROUP_NAME, + event_hub_qualified_names=EVENT_HUB_QUALIFIED_NAMES, + ) + + assert group.name == EVENT_HUB_CONSUMER_GROUP_NAME + assert group.connector_name == "azure-event-hub" + assert group.kafka_topic_qualified_names == set(EVENT_HUB_QUALIFIED_NAMES) + assert group.connection_qualified_name == EVENT_HUB_CONNECTION_QUALIFIED_NAME + assert group.qualified_name == EVENT_HUB_CONSUMER_GROUP_QUALIFIED_NAME + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, EVENT_HUB_CONSUMER_GROUP_NAME, "qualified_name is required"), + (EVENT_HUB_CONSUMER_GROUP_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test updater validates required parameters.""" + with pytest.raises(ValueError, match=message): + AzureEventHubConsumerGroup.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test updater creates AzureEventHubConsumerGroup for modification.""" + group = AzureEventHubConsumerGroup.updater( + name=EVENT_HUB_CONSUMER_GROUP_NAME, + qualified_name=EVENT_HUB_CONSUMER_GROUP_QUALIFIED_NAME, + ) + + assert group.name == EVENT_HUB_CONSUMER_GROUP_NAME + assert group.qualified_name == EVENT_HUB_CONSUMER_GROUP_QUALIFIED_NAME + + +def test_trim_to_required(): + """Test trim_to_required keeps only updater-required fields.""" + group = AzureEventHubConsumerGroup.updater( + name=EVENT_HUB_CONSUMER_GROUP_NAME, + qualified_name=EVENT_HUB_CONSUMER_GROUP_QUALIFIED_NAME, + ).trim_to_required() + + assert group.name == EVENT_HUB_CONSUMER_GROUP_NAME + assert group.qualified_name == EVENT_HUB_CONSUMER_GROUP_QUALIFIED_NAME diff --git a/tests_v9/unit/model/azure_event_hub_test.py b/tests_v9/unit/model/azure_event_hub_test.py new file mode 100644 index 000000000..5ef3e4308 --- /dev/null +++ b/tests_v9/unit/model/azure_event_hub_test.py @@ -0,0 +1,78 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for AzureEventHub model in pyatlan_v9.""" + +import pytest + +from pyatlan_v9.model import AzureEventHub +from tests_v9.unit.model.constants import ( + EVENT_HUB_CONNECTION_QUALIFIED_NAME, + EVENT_HUB_NAME, + EVENT_HUB_QUALIFIED_NAME, +) + + +@pytest.mark.parametrize( + "name, connection_qualified_name, message", + [ + (None, "connection/name", "name is required"), + (EVENT_HUB_NAME, None, "connection_qualified_name is required"), + ], +) +def test_creator_with_missing_parameters_raise_value_error( + name: str, connection_qualified_name: str, message: str +): + """Test creator validates required parameters.""" + with pytest.raises(ValueError, match=message): + AzureEventHub.creator( + name=name, connection_qualified_name=connection_qualified_name + ) + + +def test_creator(): + """Test creator initializes expected derived fields.""" + event_hub = AzureEventHub.creator( + name=EVENT_HUB_NAME, + connection_qualified_name=EVENT_HUB_CONNECTION_QUALIFIED_NAME, + ) + + assert event_hub.name == EVENT_HUB_NAME + assert event_hub.qualified_name == EVENT_HUB_QUALIFIED_NAME + assert event_hub.connector_name == "azure-event-hub" + assert event_hub.connection_qualified_name == EVENT_HUB_CONNECTION_QUALIFIED_NAME + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, EVENT_HUB_QUALIFIED_NAME, "qualified_name is required"), + (EVENT_HUB_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test updater validates required parameters.""" + with pytest.raises(ValueError, match=message): + AzureEventHub.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test updater creates AzureEventHub for modification.""" + event_hub = AzureEventHub.updater( + name=EVENT_HUB_NAME, qualified_name=EVENT_HUB_QUALIFIED_NAME + ) + assert event_hub.name == EVENT_HUB_NAME + assert event_hub.qualified_name == EVENT_HUB_QUALIFIED_NAME + + +def test_trim_to_required(): + """Test trim_to_required keeps only updater-required fields.""" + event_hub = AzureEventHub.updater( + name=EVENT_HUB_NAME, + qualified_name=EVENT_HUB_CONNECTION_QUALIFIED_NAME, + ).trim_to_required() + + assert event_hub.name == EVENT_HUB_NAME + assert event_hub.qualified_name == EVENT_HUB_CONNECTION_QUALIFIED_NAME diff --git a/tests_v9/unit/model/badge_condition_test.py b/tests_v9/unit/model/badge_condition_test.py new file mode 100644 index 000000000..5de0c8700 --- /dev/null +++ b/tests_v9/unit/model/badge_condition_test.py @@ -0,0 +1,78 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for BadgeCondition in pyatlan_v9.""" + +import pytest + +from pyatlan_v9.model import BadgeCondition +from pyatlan_v9.model.enums import BadgeComparisonOperator, BadgeConditionColor + + +@pytest.mark.parametrize( + "condition_operator, condition_value, condition_colorhex, message", + [ + ( + None, + "1", + BadgeConditionColor.RED, + "badge_condition_operator is required", + ), + ( + BadgeComparisonOperator.EQ, + None, + BadgeConditionColor.RED, + "badge_condition_value is required", + ), + ( + BadgeComparisonOperator.EQ, + "1", + None, + "badge_condition_colorhex is required", + ), + ], +) +def test_creator_when_required_parameter_is_missing_then_raises_value_error( + condition_operator, condition_value, condition_colorhex, message +): + """Test creator validation for required fields.""" + with pytest.raises(ValueError, match=message): + BadgeCondition.creator( + badge_condition_operator=condition_operator, + badge_condition_value=condition_value, + badge_condition_colorhex=condition_colorhex, + ) + + +def test_creator_with_badge_condition_color(): + """Test creator with enum-based badge color.""" + condition_operator = BadgeComparisonOperator.EQ + condition_value = "1" + condition_colorhex = BadgeConditionColor.RED + + sut = BadgeCondition.creator( + badge_condition_operator=condition_operator, + badge_condition_value=condition_value, + badge_condition_colorhex=condition_colorhex, + ) + + assert sut.badge_condition_operator == condition_operator.value + assert sut.badge_condition_value == condition_value + assert sut.badge_condition_colorhex == condition_colorhex.value + + +def test_creator_with_badge_condition_color_as_str(): + """Test creator with literal color string.""" + condition_operator = BadgeComparisonOperator.EQ + condition_value = "1" + condition_colorhex = "#BF1B1B" + + sut = BadgeCondition.creator( + badge_condition_operator=condition_operator, + badge_condition_value=condition_value, + badge_condition_colorhex=condition_colorhex, + ) + + assert sut.badge_condition_operator == condition_operator.value + assert sut.badge_condition_value == condition_value + assert sut.badge_condition_colorhex == condition_colorhex diff --git a/tests_v9/unit/model/badge_test.py b/tests_v9/unit/model/badge_test.py new file mode 100644 index 000000000..4a4a9a443 --- /dev/null +++ b/tests_v9/unit/model/badge_test.py @@ -0,0 +1,109 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for Badge model in pyatlan_v9.""" + +from unittest.mock import Mock + +import pytest + +from pyatlan_v9.model import Badge, BadgeCondition +from pyatlan_v9.model.enums import BadgeComparisonOperator, BadgeConditionColor + +CM_ATTRIBUTE_NAME = "dummy" +CM_SET_NAME = "Monte Carlo" +BADGE_NAME = "bob" +CM_ATTR_ID = "WQ6XGXwq9o7UnZlkWyKhQN" +CM_ID = "scAesIb5UhKQdTwu4GuCSN" +BADGE_QUALIFIED_NAME = f"badges/global/{CM_ID}.{CM_ATTR_ID}" +BADGE_METADATA_ATTRIBUTE = f"{CM_ID}.{CM_ATTR_ID}" +BADGE_CONDITION = BadgeCondition.creator( + badge_condition_operator=BadgeComparisonOperator.EQ, + badge_condition_value="1", + badge_condition_colorhex=BadgeConditionColor.RED, +) + + +@pytest.fixture() +def client() -> Mock: + """Create a mocked client with custom metadata cache.""" + mock_client = Mock() + cache = Mock() + cache.get_attr_id_for_name.return_value = CM_ATTR_ID + cache.get_id_for_name.return_value = CM_ID + mock_client.custom_metadata_cache = cache + return mock_client + + +@pytest.mark.parametrize( + "name, cm_name, cm_attribute, badge_conditions, message", + [ + (None, "Bob", "Dave", [BADGE_CONDITION], "name is required"), + ("Bob", None, "Dave", [BADGE_CONDITION], "cm_name is required"), + ("Bob", "", "Dave", [BADGE_CONDITION], "cm_name cannot be blank"), + ("Bob", "Dave", None, [BADGE_CONDITION], "cm_attribute is required"), + ("Bob", "Dave", "", [BADGE_CONDITION], "cm_attribute cannot be blank"), + ("Bob", "tom", "Dave", None, "badge_conditions is required"), + ("Bob", "tom", "Dave", [], "badge_conditions cannot be an empty list"), + ], +) +def test_creator_when_required_parameters_are_missing_raises_value_error( + name, cm_name, cm_attribute, badge_conditions, message, client +): + """Test creator validation for required and non-empty fields.""" + with pytest.raises(ValueError, match=message): + Badge.creator( + client=client, + name=name, + cm_name=cm_name, + cm_attribute=cm_attribute, + badge_conditions=badge_conditions, + ) + + +def test_creator(client): + """Test creator initializes qualifiedName and metadata attribute from cache.""" + badge = Badge.creator( + client=client, + name=BADGE_NAME, + cm_name=CM_SET_NAME, + cm_attribute=CM_ATTRIBUTE_NAME, + badge_conditions=[BADGE_CONDITION], + ) + assert badge.name == BADGE_NAME + assert badge.qualified_name == BADGE_QUALIFIED_NAME + assert badge.badge_metadata_attribute == BADGE_METADATA_ATTRIBUTE + assert badge.badge_conditions == [BADGE_CONDITION] + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, BADGE_QUALIFIED_NAME, "qualified_name is required"), + (BADGE_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test updater validates required parameters.""" + with pytest.raises(ValueError, match=message): + Badge.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test updater creates Badge for modification.""" + sut = Badge.updater(qualified_name=BADGE_QUALIFIED_NAME, name=BADGE_NAME) + + assert sut.qualified_name == BADGE_QUALIFIED_NAME + assert sut.name == BADGE_NAME + + +def test_trim_to_required(): + """Test trim_to_required keeps only updater-required fields.""" + sut = Badge.updater( + qualified_name=BADGE_QUALIFIED_NAME, name=BADGE_NAME + ).trim_to_required() + + assert sut.qualified_name == BADGE_QUALIFIED_NAME + assert sut.name == BADGE_NAME diff --git a/tests_v9/unit/model/column_process_test.py b/tests_v9/unit/model/column_process_test.py new file mode 100644 index 000000000..0448a02c1 --- /dev/null +++ b/tests_v9/unit/model/column_process_test.py @@ -0,0 +1,154 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for ColumnProcess model in pyatlan_v9.""" + +from typing import List + +import pytest + +from pyatlan_v9.model import Column, ColumnProcess, Process +from tests_v9.unit.model.constants import ( + CP_CONNECTION_QUALIFIED_NAME, + CP_NAME, + CP_PROCESS_ID, + CP_QUALIFIED_NAME, + CP_QUALIFIED_NAME_HASH, +) + + +@pytest.mark.parametrize( + "name, connection_qualified_name, inputs, outputs, process, error_msg", + [ + ( + None, + CP_CONNECTION_QUALIFIED_NAME, + [Column()], + [Column()], + Process(), + "name is required", + ), + ( + CP_NAME, + None, + [Column()], + [Column()], + Process(), + "connection_qualified_name is required", + ), + ( + CP_NAME, + CP_CONNECTION_QUALIFIED_NAME, + None, + [Column()], + Process(), + "inputs is required", + ), + ( + CP_NAME, + CP_CONNECTION_QUALIFIED_NAME, + [Column()], + None, + Process(), + "outputs is required", + ), + ( + CP_NAME, + CP_CONNECTION_QUALIFIED_NAME, + [Column()], + [Column()], + None, + "parent is required", + ), + ], +) +def test_creator_with_missing_parameters_raise_value_error( + name: str, + connection_qualified_name: str, + inputs: List[Column], + outputs: List[Column], + process: Process, + error_msg: str, +): + """Test creator raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=error_msg): + ColumnProcess.creator( + name=name, + connection_qualified_name=connection_qualified_name, + inputs=inputs, + outputs=outputs, + parent=process, + ) + + +@pytest.mark.parametrize( + "name, connection_qualified_name, inputs, outputs, process_id, parent", + [ + ( + CP_NAME, + CP_CONNECTION_QUALIFIED_NAME, + [Column()], + [Column()], + CP_PROCESS_ID, + Process(), + ), + ( + CP_NAME, + CP_CONNECTION_QUALIFIED_NAME, + [Column()], + [Column()], + None, + Process(), + ), + ], +) +def test_creator( + name: str, + connection_qualified_name: str, + inputs: List[Column], + outputs: List[Column], + process_id: str, + parent: Process, +): + """Test creator sets qualified name and relationship references.""" + test_cp = ColumnProcess.creator( + name=name, + connection_qualified_name=connection_qualified_name, + inputs=inputs, + outputs=outputs, + process_id=process_id, + parent=parent, + ) + assert test_cp + assert test_cp.name == CP_NAME + if process_id: + assert test_cp.qualified_name == CP_QUALIFIED_NAME + else: + assert test_cp.qualified_name == CP_QUALIFIED_NAME_HASH + assert len(test_cp.inputs) == len(inputs) + assert len(test_cp.outputs) == len(outputs) + assert test_cp.process is not None + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, CP_QUALIFIED_NAME, "qualified_name is required"), + (CP_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test updater raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + ColumnProcess.updater(qualified_name=qualified_name, name=name) + + +def test_trim_to_required(): + """Test trim_to_required keeps only updater-required fields.""" + test_cp = ColumnProcess.updater( + qualified_name=CP_QUALIFIED_NAME, name=CP_NAME + ).trim_to_required() + assert test_cp.name == CP_NAME + assert test_cp.qualified_name == CP_QUALIFIED_NAME diff --git a/tests_v9/unit/model/column_test.py b/tests_v9/unit/model/column_test.py new file mode 100644 index 000000000..0be00b3ae --- /dev/null +++ b/tests_v9/unit/model/column_test.py @@ -0,0 +1,237 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for Column model in pyatlan_v9 - exact parity with tests/unit/model/column_test.py.""" + +import pytest + +from pyatlan_v9.model import ( + Column, + MaterialisedView, + SnowflakeDynamicTable, + Table, + View, +) +from tests_v9.unit.model.constants import ( + COLUMN_NAME, + CONNECTION_QUALIFIED_NAME, + CONNECTOR_TYPE, + DATABASE_NAME, + DATABASE_QUALIFIED_NAME, + SCHEMA_NAME, + SCHEMA_QUALIFIED_NAME, + TABLE_COLUMN_QUALIFIED_NAME, + TABLE_NAME, + TABLE_QUALIFIED_NAME, + VIEW_COLUMN_QUALIFIED_NAME, + VIEW_NAME, + VIEW_QUALIFIED_NAME, +) + + +@pytest.mark.parametrize( + "name, parent_qualified_name, parent_type, order, message", + [ + (None, TABLE_COLUMN_QUALIFIED_NAME, Table, 1, "name is required"), + (COLUMN_NAME, None, Table, 1, "parent_qualified_name is required"), + (COLUMN_NAME, TABLE_COLUMN_QUALIFIED_NAME, None, 1, "parent_type is required"), + (COLUMN_NAME, TABLE_COLUMN_QUALIFIED_NAME, Table, None, "order is required"), + ( + COLUMN_NAME, + CONNECTION_QUALIFIED_NAME, + Table, + 1, + "Invalid parent_qualified_name", + ), + ( + COLUMN_NAME, + DATABASE_QUALIFIED_NAME, + Table, + 1, + "Invalid parent_qualified_name", + ), + (COLUMN_NAME, SCHEMA_QUALIFIED_NAME, Table, 1, "Invalid parent_qualified_name"), + ( + COLUMN_NAME, + TABLE_COLUMN_QUALIFIED_NAME, + Table, + 1, + "Invalid parent_qualified_name", + ), + ( + COLUMN_NAME, + TABLE_QUALIFIED_NAME, + Table, + -1, + "Order must be be a positive integer", + ), + ( + COLUMN_NAME, + TABLE_QUALIFIED_NAME, + Column, + 1, + "parent_type must be either Table, SnowflakeDynamicTable, View, MaterializeView or TablePartition", + ), + ], +) +def test_create_with_missing_parameters_raise_value_error( + name: str, parent_qualified_name: str, parent_type: type, order: int, message: str +): + """Test that creator raises ValueError when required parameters are missing or invalid.""" + with pytest.raises(ValueError, match=message): + Column.creator( + name=name, + parent_qualified_name=parent_qualified_name, + parent_type=parent_type, + order=order, + ) + + +def test_create_when_parent_is_table(): + """Test creating Column when parent is Table.""" + sut = Column.creator( + name=COLUMN_NAME, + parent_qualified_name=TABLE_QUALIFIED_NAME, + parent_type=Table, + order=1, + ) + + assert sut.name == COLUMN_NAME + assert sut.qualified_name == TABLE_COLUMN_QUALIFIED_NAME + assert sut.connector_name == CONNECTOR_TYPE + assert sut.schema_name == SCHEMA_NAME + assert sut.schema_qualified_name == SCHEMA_QUALIFIED_NAME + assert sut.database_name == DATABASE_NAME + assert sut.database_qualified_name == DATABASE_QUALIFIED_NAME + assert sut.connection_qualified_name == CONNECTION_QUALIFIED_NAME + assert sut.order == 1 + assert sut.table_qualified_name == TABLE_QUALIFIED_NAME + assert sut.table_name == TABLE_NAME + + +def test_create_when_parent_is_snowflake_dynamic_table(): + """Test creating Column when parent is SnowflakeDynamicTable.""" + sut = Column.creator( + name=COLUMN_NAME, + parent_qualified_name=TABLE_QUALIFIED_NAME, + parent_type=SnowflakeDynamicTable, + order=1, + ) + + assert sut.name == COLUMN_NAME + assert sut.qualified_name == TABLE_COLUMN_QUALIFIED_NAME + assert sut.connector_name == CONNECTOR_TYPE + assert sut.schema_name == SCHEMA_NAME + assert sut.schema_qualified_name == SCHEMA_QUALIFIED_NAME + assert sut.database_name == DATABASE_NAME + assert sut.database_qualified_name == DATABASE_QUALIFIED_NAME + assert sut.connection_qualified_name == CONNECTION_QUALIFIED_NAME + assert sut.order == 1 + assert sut.table_qualified_name == TABLE_QUALIFIED_NAME + assert sut.snowflake_dynamic_table.qualified_name == TABLE_QUALIFIED_NAME + assert sut.table_name == TABLE_NAME + + +def test_create_when_parent_is_view(): + """Test creating Column when parent is View.""" + sut = Column.creator( + name=COLUMN_NAME, + parent_qualified_name=VIEW_QUALIFIED_NAME, + parent_type=View, + order=1, + ) + + assert sut.name == COLUMN_NAME + assert sut.qualified_name == VIEW_COLUMN_QUALIFIED_NAME + assert sut.connector_name == CONNECTOR_TYPE + assert sut.schema_name == SCHEMA_NAME + assert sut.schema_qualified_name == SCHEMA_QUALIFIED_NAME + assert sut.database_name == DATABASE_NAME + assert sut.database_qualified_name == DATABASE_QUALIFIED_NAME + assert sut.connection_qualified_name == CONNECTION_QUALIFIED_NAME + assert sut.order == 1 + assert sut.view_qualified_name == VIEW_QUALIFIED_NAME + assert sut.view_name == VIEW_NAME + + +def test_overload_creator(): + """Test creator with all optional parameters provided.""" + sut = Column.creator( + name=COLUMN_NAME, + parent_qualified_name=VIEW_QUALIFIED_NAME, + parent_type=View, + order=2, + parent_name=VIEW_NAME, + database_name=DATABASE_NAME, + database_qualified_name=DATABASE_QUALIFIED_NAME, + schema_name=SCHEMA_NAME, + schema_qualified_name=SCHEMA_QUALIFIED_NAME, + table_name=TABLE_NAME, + table_qualified_name=TABLE_QUALIFIED_NAME, + connection_qualified_name=CONNECTION_QUALIFIED_NAME, + ) + assert sut.name == COLUMN_NAME + assert sut.qualified_name == VIEW_COLUMN_QUALIFIED_NAME + assert sut.connector_name == CONNECTOR_TYPE + assert sut.schema_name == SCHEMA_NAME + assert sut.schema_qualified_name == SCHEMA_QUALIFIED_NAME + assert sut.database_name == DATABASE_NAME + assert sut.database_qualified_name == DATABASE_QUALIFIED_NAME + assert sut.connection_qualified_name == CONNECTION_QUALIFIED_NAME + assert sut.order == 2 + assert sut.view_name == VIEW_NAME + + +def test_create_when_parent_is_materialized_view(): + """Test creating Column when parent is MaterialisedView.""" + sut = Column.creator( + name=COLUMN_NAME, + parent_qualified_name=VIEW_QUALIFIED_NAME, + parent_type=MaterialisedView, + order=1, + ) + + assert sut.name == COLUMN_NAME + assert sut.qualified_name == VIEW_COLUMN_QUALIFIED_NAME + assert sut.connector_name == CONNECTOR_TYPE + assert sut.schema_name == SCHEMA_NAME + assert sut.schema_qualified_name == SCHEMA_QUALIFIED_NAME + assert sut.database_name == DATABASE_NAME + assert sut.database_qualified_name == DATABASE_QUALIFIED_NAME + assert sut.connection_qualified_name == CONNECTION_QUALIFIED_NAME + assert sut.order == 1 + assert sut.view_qualified_name == VIEW_QUALIFIED_NAME + assert sut.view_name == VIEW_NAME + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, TABLE_COLUMN_QUALIFIED_NAME, "qualified_name is required"), + (COLUMN_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test that updater raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + Column.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test that updater creates a Column instance for modification.""" + sut = Column.updater(qualified_name=TABLE_COLUMN_QUALIFIED_NAME, name=COLUMN_NAME) + + assert sut.qualified_name == TABLE_COLUMN_QUALIFIED_NAME + assert sut.name == COLUMN_NAME + + +def test_trim_to_required(): + """Test that trim_to_required returns Column with only required fields.""" + sut = Column.updater( + qualified_name=TABLE_COLUMN_QUALIFIED_NAME, name=COLUMN_NAME + ).trim_to_required() + + assert sut.qualified_name == TABLE_COLUMN_QUALIFIED_NAME + assert sut.name == COLUMN_NAME diff --git a/tests_v9/unit/model/connection_test.py b/tests_v9/unit/model/connection_test.py new file mode 100644 index 000000000..ff7639051 --- /dev/null +++ b/tests_v9/unit/model/connection_test.py @@ -0,0 +1,392 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for Connection model in pyatlan_v9.""" + +import json +from typing import List, Optional + +import pytest +from msgspec import UNSET + +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.model import Connection +from pyatlan_v9.model.enums import AtlanConnectionCategory, AtlanConnectorType +from tests_v9.unit.model.constants import CONNECTION_NAME, CONNECTION_QUALIFIED_NAME + + +@pytest.fixture() +def client(): + """Create a test AtlanClient instance.""" + return AtlanClient(base_url="https://test.atlan.com", api_key="test-api-key") + + +@pytest.mark.parametrize( + "name, connector_type, admin_users, admin_groups, admin_roles, message", + [ + (None, AtlanConnectorType.SNOWFLAKE, [], [], [], "name is required"), + (CONNECTION_NAME, None, [], [], [], "connector_type is required"), + ( + CONNECTION_NAME, + AtlanConnectorType.SNOWFLAKE, + [], + [], + [], + "One of admin_user, admin_groups or admin_roles is required", + ), + ( + CONNECTION_NAME, + AtlanConnectorType.SNOWFLAKE, + ["bad"], + [], + [], + "Provided username bad was not found in Atlan.", + ), + ( + CONNECTION_NAME, + AtlanConnectorType.SNOWFLAKE, + [], + ["bad"], + [], + "Provided group name bad was not found in Atlan.", + ), + ( + CONNECTION_NAME, + AtlanConnectorType.SNOWFLAKE, + [], + [], + ["bad"], + "Provided role ID bad was not found in Atlan.", + ), + ], +) +def test_creator_with_missing_parameters_raises_value_error( + client: AtlanClient, + name: str, + connector_type: AtlanConnectorType, + admin_users: Optional[List[str]], + admin_groups: Optional[List[str]], + admin_roles: Optional[List[str]], + message: str, + mock_role_cache, + mock_user_cache, + mock_group_cache, +): + """Test that creator raises ValueError when required parameters are missing.""" + + def role_side_effect(*args, **kwargs): + if "idstrs" in kwargs: + if "bad" in kwargs["idstrs"]: + raise ValueError("Provided role ID bad was not found in Atlan.") + + def user_side_effect(*args, **kwargs): + if "names" in kwargs: + if "bad" in kwargs["names"]: + raise ValueError("Provided username bad was not found in Atlan.") + + def group_side_effect(*args, **kwargs): + if "aliases" in kwargs: + if "bad" in kwargs["aliases"]: + raise ValueError("Provided group name bad was not found in Atlan.") + + mock_role_cache.validate_idstrs.side_effect = role_side_effect + mock_user_cache.validate_names.side_effect = user_side_effect + mock_group_cache.validate_aliases.side_effect = group_side_effect + + with pytest.raises(ValueError, match=message): + Connection.creator( + client=client, + name=name, + connector_type=connector_type, + admin_users=admin_users, + admin_groups=admin_groups, + admin_roles=admin_roles, + ) + mock_role_cache.validate_idstrs.reset_mock() + mock_user_cache.validate_names.reset_mock() + mock_group_cache.validate_aliases.reset_mock() + + +@pytest.mark.parametrize( + "name, connector_type, admin_users, admin_groups, admin_roles", + [ + (CONNECTION_NAME, AtlanConnectorType.SNOWFLAKE, ["ernest"], [], []), + (CONNECTION_NAME, AtlanConnectorType.SNOWFLAKE, [], ["ernest"], []), + (CONNECTION_NAME, AtlanConnectorType.SNOWFLAKE, [], [], ["ernest"]), + ], +) +def test_creator( + client: AtlanClient, + name: str, + connector_type: AtlanConnectorType, + admin_users: List[str], + admin_groups: List[str], + admin_roles: List[str], + mock_role_cache, + mock_user_cache, + mock_group_cache, +): + """Test that creator properly initializes a Connection with all derived fields.""" + mock_role_cache.validate_idstrs + mock_user_cache.validate_names + mock_group_cache.validate_aliases + + sut = Connection.creator( + client=client, + name=name, + connector_type=connector_type, + admin_users=admin_users, + admin_groups=admin_groups, + admin_roles=admin_roles, + ) + + assert sut.name == name + assert sut.qualified_name + assert sut.qualified_name[:20] == connector_type.to_qualified_name()[:20] + assert sut.connector_name == connector_type.value + assert sut.admin_users == set(admin_users) + assert sut.admin_groups == set(admin_groups) + assert sut.admin_roles == set(admin_roles) + + mock_role_cache.validate_idstrs.reset_mock() + mock_user_cache.validate_names.reset_mock() + mock_group_cache.validate_aliases.reset_mock() + + +@pytest.mark.parametrize( + "name, connector_type, admin_users, admin_groups, admin_roles", + [ + ( + CONNECTION_NAME, + AtlanConnectorType.CREATE_CUSTOM( + name="FOO", value="foo", category=AtlanConnectionCategory.BI + ), + ["ernest"], + [], + [], + ), + ( + CONNECTION_NAME, + AtlanConnectorType.CREATE_CUSTOM( + name="BAR", value="bar", category=AtlanConnectionCategory.API + ), + [], + ["ernest"], + [], + ), + ( + CONNECTION_NAME, + AtlanConnectorType.CREATE_CUSTOM( + name="BAZ", value="baz", category=AtlanConnectionCategory.WAREHOUSE + ), + [], + [], + ["ernest"], + ), + ], +) +def test_creator_with_custom_type( + client: AtlanClient, + name: str, + connector_type: AtlanConnectorType, + admin_users: List[str], + admin_groups: List[str], + admin_roles: List[str], + mock_role_cache, + mock_user_cache, + mock_group_cache, +): + """Test that creator works with custom connector types.""" + mock_role_cache.validate_idstrs + mock_user_cache.validate_names + mock_group_cache.validate_aliases + + sut = Connection.creator( + client=client, + name=name, + connector_type=connector_type, + admin_users=admin_users, + admin_groups=admin_groups, + admin_roles=admin_roles, + ) + + assert sut.name == name + assert sut.qualified_name + assert sut.qualified_name[:20] == connector_type.to_qualified_name()[:20] + assert sut.connector_name == connector_type.value + assert sut.admin_users == set(admin_users) + assert sut.admin_groups == set(admin_groups) + assert sut.admin_roles == set(admin_roles) + + mock_role_cache.validate_idstrs.reset_mock() + mock_user_cache.validate_names.reset_mock() + mock_group_cache.validate_aliases.reset_mock() + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, CONNECTION_QUALIFIED_NAME, "qualified_name is required"), + (CONNECTION_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test that updater raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + Connection.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test that updater creates a Connection instance for modification.""" + sut = Connection.updater( + qualified_name=CONNECTION_QUALIFIED_NAME, name=CONNECTION_NAME + ) + + assert sut.qualified_name == CONNECTION_QUALIFIED_NAME + assert sut.name == CONNECTION_NAME + + +def test_trim_to_required(): + """Test that trim_to_required returns a Connection with only required fields.""" + sut = Connection.updater( + qualified_name=CONNECTION_QUALIFIED_NAME, name=CONNECTION_NAME + ).trim_to_required() + + assert sut.qualified_name == CONNECTION_QUALIFIED_NAME + assert sut.name == CONNECTION_NAME + + +def test_admin_users_when_set_to_good_name(): + """Test that admin_users can be set and retrieved as a set.""" + sut = Connection.updater( + qualified_name=CONNECTION_QUALIFIED_NAME, name=CONNECTION_NAME + ).trim_to_required() + + sut.admin_users = {"ernest"} + + assert sut.admin_users == {"ernest"} + + +def test_admin_groups_when_set_to_good_name(): + """Test that admin_groups can be set and retrieved as a set.""" + sut = Connection.updater( + qualified_name=CONNECTION_QUALIFIED_NAME, name=CONNECTION_NAME + ).trim_to_required() + + sut.admin_groups = {"ernest"} + + assert sut.admin_groups == {"ernest"} + + +def test_admin_roles_when_set_to_good_name(): + """Test that admin_roles can be set and retrieved as a set.""" + sut = Connection.updater( + qualified_name=CONNECTION_QUALIFIED_NAME, name=CONNECTION_NAME + ).trim_to_required() + + sut.admin_roles = {"ernest"} + + assert sut.admin_roles == {"ernest"} + + +def test_validation_of_admin_not_done_when_constructed_from_json(serde): + """Test that admin fields are preserved when deserializing from JSON.""" + data = { + "typeName": "Connection", + "attributes": { + "adminGroups": ["bogus"], + "adminUsers": ["bogus"], + "name": "S3 Ernest", + "connectorName": "s3", + "adminRoles": ["bogus"], + }, + "guid": "ee59f5b0-3b59-409f-a42d-d151e5ffba22", + "isIncomplete": False, + "status": "ACTIVE", + "createdBy": "service-account-apikey-a1c7beae-a558-4994-adb4-16ee422b91d6", + "updatedBy": "service-account-apikey-a1c7beae-a558-4994-adb4-16ee422b91d6", + "createTime": 1695884860580, + "updateTime": 1695884860580, + "version": 0, + "relationshipAttributes": {}, + "labels": [], + } + + json_str = json.dumps(data) + conn = Connection.from_json(json_str, serde=serde) + + assert conn.name == "S3 Ernest" + assert conn.connector_name == "s3" + assert conn.admin_users == {"bogus"} + assert conn.admin_groups == {"bogus"} + assert conn.admin_roles == {"bogus"} + assert conn.guid == "ee59f5b0-3b59-409f-a42d-d151e5ffba22" + assert conn.status == "ACTIVE" + + +def test_basic_construction(): + """Test basic Connection construction with minimal parameters.""" + conn = Connection(name=CONNECTION_NAME, qualified_name=CONNECTION_QUALIFIED_NAME) + + assert conn.name == CONNECTION_NAME + assert conn.qualified_name == CONNECTION_QUALIFIED_NAME + assert conn.type_name == "Connection" + + +def test_unset_fields(): + """Test that optional fields default to UNSET.""" + conn = Connection(name=CONNECTION_NAME, qualified_name=CONNECTION_QUALIFIED_NAME) + + assert conn.category is UNSET + assert conn.sub_category is UNSET + assert conn.host is UNSET + assert conn.port is UNSET + assert conn.admin_users is UNSET + assert conn.admin_groups is UNSET + assert conn.admin_roles is UNSET + + +def test_none_vs_unset(): + """Test the distinction between None and UNSET values.""" + conn = Connection(name=CONNECTION_NAME, qualified_name=CONNECTION_QUALIFIED_NAME) + + assert conn.host is UNSET + conn.host = None + assert conn.host is None + assert conn.host is not UNSET + + +def test_serialization_to_json_nested(serde): + """Test serialization to nested JSON format (API format).""" + conn = Connection.updater( + qualified_name=CONNECTION_QUALIFIED_NAME, name=CONNECTION_NAME + ) + + json_str = conn.to_json(nested=True, serde=serde) + data = json.loads(json_str) + + assert data["typeName"] == "Connection" + assert "attributes" in data + assert data["attributes"]["name"] == CONNECTION_NAME + assert data["attributes"]["qualifiedName"] == CONNECTION_QUALIFIED_NAME + + +def test_round_trip_serialization(serde): + """Test that serialization and deserialization preserve all data.""" + original = Connection.updater( + qualified_name=CONNECTION_QUALIFIED_NAME, name=CONNECTION_NAME + ) + + json_str = original.to_json(nested=True, serde=serde) + restored = Connection.from_json(json_str, serde=serde) + + assert restored.name == original.name + assert restored.qualified_name == original.qualified_name + + +def test_type_name_defaults(): + """Test that type_name defaults to 'Connection'.""" + conn = Connection(name=CONNECTION_NAME, qualified_name=CONNECTION_QUALIFIED_NAME) + assert conn.type_name == "Connection" diff --git a/tests_v9/unit/model/constants.py b/tests_v9/unit/model/constants.py new file mode 100644 index 000000000..591a313a1 --- /dev/null +++ b/tests_v9/unit/model/constants.py @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +# Import all constants from the original test constants +# This ensures parity with existing tests +from tests.unit.model.constants import * # noqa: F401, F403 diff --git a/tests_v9/unit/model/custom_entity_test.py b/tests_v9/unit/model/custom_entity_test.py new file mode 100644 index 000000000..de1a2abf2 --- /dev/null +++ b/tests_v9/unit/model/custom_entity_test.py @@ -0,0 +1,144 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for CustomEntity model in pyatlan_v9.""" + +import json + +import pytest +from msgspec import UNSET + +from pyatlan_v9.model import CustomEntity +from tests_v9.unit.model.constants import ( + CUSTOM_CONNECTION_QUALIFIED_NAME, + CUSTOM_CONNECTOR_TYPE, + CUSTOM_ENTITY_NAME, + CUSTOM_ENTITY_QUALIFIED_NAME, +) + + +@pytest.mark.parametrize( + "name, connection_qualified_name, message", + [ + (None, "connection/name", "name is required"), + (CUSTOM_ENTITY_NAME, None, "connection_qualified_name is required"), + ], +) +def test_creator_with_missing_parameters_raises_value_error( + name: str, connection_qualified_name: str, message: str +): + """Test that creator raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + CustomEntity.creator( + name=name, connection_qualified_name=connection_qualified_name + ) + + +def test_creator(): + """Test that creator properly initializes a CustomEntity with all derived fields.""" + sut = CustomEntity.creator( + name=CUSTOM_ENTITY_NAME, + connection_qualified_name=CUSTOM_CONNECTION_QUALIFIED_NAME, + ) + + assert sut.name == CUSTOM_ENTITY_NAME + assert sut.connection_qualified_name == CUSTOM_CONNECTION_QUALIFIED_NAME + assert sut.qualified_name == CUSTOM_ENTITY_QUALIFIED_NAME + assert sut.connector_name == CUSTOM_CONNECTOR_TYPE + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, CUSTOM_CONNECTION_QUALIFIED_NAME, "qualified_name is required"), + (CUSTOM_ENTITY_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test that updater raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + CustomEntity.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test that updater creates a CustomEntity instance for modification.""" + sut = CustomEntity.updater( + qualified_name=CUSTOM_ENTITY_QUALIFIED_NAME, name=CUSTOM_ENTITY_NAME + ) + + assert sut.qualified_name == CUSTOM_ENTITY_QUALIFIED_NAME + assert sut.name == CUSTOM_ENTITY_NAME + + +def test_trim_to_required(): + """Test that trim_to_required returns a CustomEntity with only required fields.""" + sut = CustomEntity.updater( + name=CUSTOM_ENTITY_NAME, qualified_name=CUSTOM_ENTITY_QUALIFIED_NAME + ).trim_to_required() + + assert sut.name == CUSTOM_ENTITY_NAME + assert sut.qualified_name == CUSTOM_ENTITY_QUALIFIED_NAME + + +def test_basic_construction(): + """Test basic CustomEntity construction with minimal parameters.""" + entity = CustomEntity( + name=CUSTOM_ENTITY_NAME, qualified_name=CUSTOM_ENTITY_QUALIFIED_NAME + ) + + assert entity.name == CUSTOM_ENTITY_NAME + assert entity.qualified_name == CUSTOM_ENTITY_QUALIFIED_NAME + assert entity.type_name == "CustomEntity" + + +def test_unset_fields(): + """Test that optional fields default to UNSET.""" + entity = CustomEntity( + name=CUSTOM_ENTITY_NAME, qualified_name=CUSTOM_ENTITY_QUALIFIED_NAME + ) + + assert entity.custom_children_subtype is UNSET + + +def test_serialization_to_json_nested(serde): + """Test serialization to nested JSON format (API format).""" + entity = CustomEntity.creator( + name=CUSTOM_ENTITY_NAME, + connection_qualified_name=CUSTOM_CONNECTION_QUALIFIED_NAME, + ) + + json_str = entity.to_json(nested=True, serde=serde) + data = json.loads(json_str) + + assert data["typeName"] == "CustomEntity" + assert "attributes" in data + assert data["attributes"]["name"] == CUSTOM_ENTITY_NAME + + +def test_round_trip_serialization(serde): + """Test that serialization and deserialization preserve all data.""" + original = CustomEntity.creator( + name=CUSTOM_ENTITY_NAME, + connection_qualified_name=CUSTOM_CONNECTION_QUALIFIED_NAME, + ) + + json_str = original.to_json(nested=True, serde=serde) + restored = CustomEntity.from_json(json_str, serde=serde) + + assert restored.name == original.name + assert restored.qualified_name == original.qualified_name + + +def test_creator_with_guid(): + """Test that creator initializes a temporary GUID for new assets.""" + entity = CustomEntity.creator( + name=CUSTOM_ENTITY_NAME, + connection_qualified_name=CUSTOM_CONNECTION_QUALIFIED_NAME, + ) + + assert entity.guid is not UNSET + assert entity.guid is not None + assert isinstance(entity.guid, str) + assert entity.guid.startswith("-") diff --git a/tests_v9/unit/model/data_contract_test.py b/tests_v9/unit/model/data_contract_test.py new file mode 100644 index 000000000..47929177a --- /dev/null +++ b/tests_v9/unit/model/data_contract_test.py @@ -0,0 +1,158 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for DataContract model in pyatlan_v9.""" + +from json import dumps +from typing import Union + +import pytest + +from pyatlan_v9.errors import InvalidRequestError +from pyatlan_v9.model import DataContract +from pyatlan_v9.model.contract import DataContractSpec +from tests_v9.unit.model.constants import ( + ASSET_QUALIFIED_NAME, + DATA_CONTRACT_JSON, + DATA_CONTRACT_NAME, + DATA_CONTRACT_NAME_DEFAULT, + DATA_CONTRACT_QUALIFIED_NAME, + DATA_CONTRACT_SPEC_STR, + DATA_CONTRACT_SPEC_STR_WITHOUT_DATASET, +) + + +def _assert_contract( + contract: Union[DataContract, DataContract.Attributes], + is_json: bool = False, + contract_name: str = DATA_CONTRACT_NAME, +) -> None: + assert contract.name == contract_name + assert contract.qualified_name == DATA_CONTRACT_QUALIFIED_NAME + if is_json: + assert contract.data_contract_json == dumps(DATA_CONTRACT_JSON) + + +@pytest.mark.parametrize( + "asset_qualified_name, contract_json, contract_spec, message", + [ + (None, "json", "spec", "asset_qualified_name is required"), + ("qn", "json", "spec", "Both `contract_json` and `contract_spec` cannot be"), + ("qn", None, None, "At least one of `contract_json` or `contract_spec`"), + ], +) +def test_creator_with_missing_parameters_raise_value_error( + asset_qualified_name: str, contract_json: str, contract_spec: str, message: str +): + """Test creator raises ValueError for invalid input combinations.""" + with pytest.raises(ValueError, match=message): + DataContract.creator( # type: ignore[arg-type] + asset_qualified_name=asset_qualified_name, + contract_json=contract_json, + contract_spec=contract_spec, + ) + + +@pytest.mark.parametrize( + "asset_qualified_name, contract_json, error_msg", + [ + ( + "asset-qn", + "some-invalid-json", + "ATLAN-PYTHON-400-062 Provided data contract JSON is invalid.", + ), + ( + "asset-qn", + '{"kind":"DataContract", "description":"Missing dataset property"}', + "ATLAN-PYTHON-400-062 Provided data contract JSON is invalid.", + ), + ], +) +def test_creator_with_invalid_contract_json_raises_error( + asset_qualified_name: str, contract_json: str, error_msg: str +): + """Test creator raises InvalidRequestError for invalid contract JSON payloads.""" + with pytest.raises(InvalidRequestError, match=error_msg): + DataContract.creator( + asset_qualified_name=asset_qualified_name, + contract_json=contract_json, + ) + + +def test_creator_attributes_with_required_parameters(): + """Test DataContract.Attributes.creator for JSON payload.""" + attributes = DataContract.Attributes.creator( + asset_qualified_name=ASSET_QUALIFIED_NAME, + contract_json=dumps(DATA_CONTRACT_JSON), + ) + _assert_contract(attributes, is_json=True) + + +def test_creator_with_required_parameters_json(): + """Test DataContract.creator for JSON payload.""" + test_contract = DataContract.creator( + asset_qualified_name=ASSET_QUALIFIED_NAME, + contract_json=dumps(DATA_CONTRACT_JSON), + ) + _assert_contract(test_contract) + + +def test_creator_with_required_parameters_spec_str(): + """Test DataContract.creator for YAML string payload.""" + test_contract = DataContract.creator( + asset_qualified_name=ASSET_QUALIFIED_NAME, + contract_spec=DATA_CONTRACT_SPEC_STR, + ) + _assert_contract(test_contract) + + +def test_creator_with_required_parameters_spec_str_without_dataset(): + """Test creator defaults contract name from asset QN when dataset is absent.""" + test_contract = DataContract.creator( + asset_qualified_name=ASSET_QUALIFIED_NAME, + contract_spec=DATA_CONTRACT_SPEC_STR_WITHOUT_DATASET, + ) + _assert_contract(test_contract, contract_name=DATA_CONTRACT_NAME_DEFAULT) + + +def test_creator_with_required_parameters_spec_model(): + """Test DataContract.creator for DataContractSpec model payload.""" + spec = DataContractSpec.from_yaml(DATA_CONTRACT_SPEC_STR) + test_contract = DataContract.creator( + asset_qualified_name=ASSET_QUALIFIED_NAME, + contract_spec=spec, + ) + _assert_contract(test_contract) + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, DATA_CONTRACT_NAME, "qualified_name is required"), + (DATA_CONTRACT_QUALIFIED_NAME, None, "name is required"), + ], +) +def test_updater_with_missing_parameters_raise_value_error( + qualified_name: str, name: str, message: str +): + """Test updater validates required parameters.""" + with pytest.raises(ValueError, match=message): + DataContract.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test updater creates update-ready DataContract.""" + test_contract = DataContract.updater( + name=DATA_CONTRACT_NAME, + qualified_name=DATA_CONTRACT_QUALIFIED_NAME, + ) + _assert_contract(test_contract, False) + + +def test_trim_to_required(): + """Test trim_to_required preserves only required update fields.""" + test_contract = DataContract.updater( + name=DATA_CONTRACT_NAME, + qualified_name=DATA_CONTRACT_QUALIFIED_NAME, + ).trim_to_required() + _assert_contract(test_contract, False) diff --git a/tests_v9/unit/model/data_domain_test.py b/tests_v9/unit/model/data_domain_test.py new file mode 100644 index 000000000..406a6570e --- /dev/null +++ b/tests_v9/unit/model/data_domain_test.py @@ -0,0 +1,71 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for DataDomain model in pyatlan_v9.""" + +import pytest + +from pyatlan_v9.model import DataDomain +from tests_v9.unit.model.constants import ( + DATA_DOMAIN_NAME, + DATA_DOMAIN_QUALIFIED_NAME, + DATA_SUB_DOMAIN_NAME, +) + + +def _assert_domain(domain: DataDomain) -> None: + assert domain.name == DATA_DOMAIN_NAME + assert domain.qualified_name == DATA_DOMAIN_QUALIFIED_NAME + assert domain.parent_domain_qualified_name is None + + +@pytest.mark.parametrize( + "name, message", + [(None, "name is required")], +) +def test_creator_with_missing_parameters_raise_value_error(name: str, message: str): + """Test that creator raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + DataDomain.creator(name=name) + + +def test_creator_attributes_with_required_parameters(): + """Test that creator resolves parent-domain relationship from qualified name.""" + test_domain = DataDomain.creator( + name=DATA_DOMAIN_NAME, + parent_domain_qualified_name=DATA_DOMAIN_QUALIFIED_NAME, + ) + assert test_domain.parent_domain.unique_attributes == { + "qualifiedName": DATA_DOMAIN_QUALIFIED_NAME + } + + +def test_creator(): + """Test that creator initializes root and sub-domain correctly.""" + test_domain = DataDomain.creator(name=DATA_DOMAIN_NAME) + test_domain.qualified_name = DATA_DOMAIN_QUALIFIED_NAME + _assert_domain(test_domain) + + test_sub_domain = DataDomain.creator( + name=DATA_SUB_DOMAIN_NAME, + parent_domain_qualified_name=test_domain.qualified_name, + ) + assert test_sub_domain.name == DATA_SUB_DOMAIN_NAME + assert test_sub_domain.qualified_name == DATA_SUB_DOMAIN_NAME + assert test_sub_domain.parent_domain_qualified_name == test_domain.qualified_name + + +def test_updater(): + """Test updater creates a DataDomain with required fields.""" + test_domain = DataDomain.updater( + name=DATA_DOMAIN_NAME, qualified_name=DATA_DOMAIN_QUALIFIED_NAME + ) + _assert_domain(test_domain) + + +def test_trim_to_required(): + """Test trim_to_required keeps only updater-required fields.""" + test_domain = DataDomain.updater( + qualified_name=DATA_DOMAIN_QUALIFIED_NAME, name=DATA_DOMAIN_NAME + ).trim_to_required() + _assert_domain(test_domain) diff --git a/tests_v9/unit/model/data_product_test.py b/tests_v9/unit/model/data_product_test.py new file mode 100644 index 000000000..890876e02 --- /dev/null +++ b/tests_v9/unit/model/data_product_test.py @@ -0,0 +1,166 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for DataProduct model in pyatlan_v9.""" + +from json import dumps, load, loads +from pathlib import Path + +import pytest + +from pyatlan_v9.errors import InvalidRequestError +from pyatlan_v9.model import DataProduct +from pyatlan_v9.model.assets import AtlasGlossary +from pyatlan_v9.model.enums import CertificateStatus, DataProductStatus +from pyatlan_v9.model.fluent_search import CompoundQuery, FluentSearch +from pyatlan_v9.model.search import IndexSearchRequest +from tests_v9.unit.model.constants import ( + DATA_DOMAIN_QUALIFIED_NAME, + DATA_PRODUCT_NAME, + DATA_PRODUCT_QUALIFIED_NAME, + DATA_PRODUCT_UNDER_SUB_DOMAIN_QUALIFIED_NAME, + DATA_SUB_DOMAIN_QUALIFIED_NAME, +) + +TEST_DATA_DIR = Path(__file__).resolve().parents[3] / "tests" / "unit" / "data" +DATA_PRODUCT_ASSETS_DSL_JSON = "data_product_assets_dsl.json" +DATA_MESH_DIR = TEST_DATA_DIR / "data_mesh_requests" +ASSETS_PLAYBOOK_FILTER = '{"condition":"AND","isGroupLocked":false,"rules":[]}' + + +def load_json(response_dir: Path, filename: str): + """Load a JSON file from test data.""" + with (response_dir / filename).open() as input_file: + return load(input_file) + + +@pytest.fixture() +def data_product_assets_dsl_json(): + """Return expected data-product assets DSL fixture JSON.""" + return load_json(DATA_MESH_DIR, DATA_PRODUCT_ASSETS_DSL_JSON) + + +@pytest.fixture() +def data_product_asset_selection(): + """Build a fluent-search request used to define DataProduct assets.""" + return ( + FluentSearch() + .where(CompoundQuery.active_assets()) + .where(CompoundQuery.asset_type(AtlasGlossary)) + .where(AtlasGlossary.CERTIFICATE_STATUS.eq(CertificateStatus.VERIFIED.value)) + ).to_request() + + +def _assert_product( + product: DataProduct, qualified_name: str = DATA_PRODUCT_QUALIFIED_NAME +) -> None: + assert product.name == DATA_PRODUCT_NAME + assert product.qualified_name == qualified_name + + +@pytest.mark.parametrize( + "name, asset_selection, domain_qualified_name, message", + [ + (None, "dummy", DATA_DOMAIN_QUALIFIED_NAME, "name is required"), + ( + DATA_PRODUCT_NAME, + None, + DATA_DOMAIN_QUALIFIED_NAME, + "asset_selection is required", + ), + (DATA_PRODUCT_NAME, "dummy", None, "domain_qualified_name is required"), + ], +) +def test_creator_with_missing_parameters_raise_value_error( + name: str, + asset_selection: IndexSearchRequest, + domain_qualified_name: str, + message: str, +): + """Test creator raises ValueError when required fields are missing.""" + with pytest.raises(ValueError, match=message): + DataProduct.creator( + name=name, + asset_selection=asset_selection, + domain_qualified_name=domain_qualified_name, + ) + + +def test_creator( + data_product_asset_selection: IndexSearchRequest, data_product_assets_dsl_json +): + """Test creator populates relationships and DSL payload for root domain.""" + test_product = DataProduct.creator( + name=DATA_PRODUCT_NAME, + asset_selection=data_product_asset_selection, + domain_qualified_name=DATA_DOMAIN_QUALIFIED_NAME, + ) + assert test_product.data_domain.unique_attributes == { + "qualifiedName": DATA_DOMAIN_QUALIFIED_NAME + } + assert test_product.parent_domain_qualified_name == DATA_DOMAIN_QUALIFIED_NAME + assert test_product.super_domain_qualified_name == DATA_DOMAIN_QUALIFIED_NAME + test_asset_dsl = dumps(loads(test_product.data_product_assets_dsl), sort_keys=True) + expected_asset_dsl = dumps(data_product_assets_dsl_json, sort_keys=True) + assert test_asset_dsl == expected_asset_dsl + assert test_product.data_product_assets_playbook_filter == ASSETS_PLAYBOOK_FILTER + _assert_product(test_product) + + +def test_creator_under_sub_domain( + data_product_asset_selection: IndexSearchRequest, data_product_assets_dsl_json +): + """Test creator populates parent and super domain for sub-domain products.""" + test_product = DataProduct.creator( + name=DATA_PRODUCT_NAME, + asset_selection=data_product_asset_selection, + domain_qualified_name=DATA_SUB_DOMAIN_QUALIFIED_NAME, + ) + assert test_product.data_domain.unique_attributes == { + "qualifiedName": DATA_SUB_DOMAIN_QUALIFIED_NAME + } + assert test_product.parent_domain_qualified_name == DATA_SUB_DOMAIN_QUALIFIED_NAME + assert test_product.super_domain_qualified_name == DATA_DOMAIN_QUALIFIED_NAME + test_asset_dsl = dumps(loads(test_product.data_product_assets_dsl), sort_keys=True) + expected_asset_dsl = dumps(data_product_assets_dsl_json, sort_keys=True) + assert test_asset_dsl == expected_asset_dsl + assert test_product.data_product_assets_playbook_filter == ASSETS_PLAYBOOK_FILTER + _assert_product( + test_product, qualified_name=DATA_PRODUCT_UNDER_SUB_DOMAIN_QUALIFIED_NAME + ) + assert test_product.daap_status == DataProductStatus.ACTIVE + + +def test_updater(): + """Test updater returns a minimal DataProduct for updates.""" + test_product = DataProduct.updater( + name=DATA_PRODUCT_NAME, + qualified_name=DATA_PRODUCT_QUALIFIED_NAME, + ) + _assert_product(test_product) + + +def test_get_assets_with_missing_dp_asset_dsl(): + """Test get_assets raises InvalidRequestError when DSL is missing.""" + data_product = DataProduct( + name=DATA_PRODUCT_NAME, + parent_domain_qualified_name=DATA_PRODUCT_QUALIFIED_NAME, + data_product_assets_dsl=None, + ) + with pytest.raises( + InvalidRequestError, + match=( + "Missing value for `data_product_assets_d_s_l`, " + "which is required to retrieve DataProduct assets." + ), + ): + data_product.get_assets(client=object()) + + +def test_trim_to_required(): + """Test trim_to_required keeps only updater-required fields.""" + test_product = DataProduct.updater( + qualified_name=DATA_PRODUCT_QUALIFIED_NAME, + name=DATA_PRODUCT_NAME, + ).trim_to_required() + _assert_product(test_product) diff --git a/tests_v9/unit/model/data_quality_rule_test.py b/tests_v9/unit/model/data_quality_rule_test.py new file mode 100644 index 000000000..2f5f0807c --- /dev/null +++ b/tests_v9/unit/model/data_quality_rule_test.py @@ -0,0 +1,743 @@ +import json +from unittest.mock import Mock + +import pytest + +from pyatlan_v9.errors import ErrorCode, InvalidRequestError +from pyatlan_v9.model.assets import Column, DataQualityRule, Table +from pyatlan_v9.model.dq_rule_conditions import DQRuleConditionsBuilder +from pyatlan_v9.model.enums import ( + DataQualityDimension, + DataQualityRuleAlertPriority, + DataQualityRuleCustomSQLReturnType, + DataQualityRuleStatus, + DataQualityRuleTemplateConfigRuleConditions, + DataQualityRuleTemplateType, + DataQualityRuleThresholdCompareOperator, + DataQualityRuleThresholdUnit, +) +from tests_v9.unit.model.constants import ( + DQ_COLUMN_QUALIFIED_NAME, + DQ_RULE_CUSTOM_SQL, + DQ_RULE_DESCRIPTION, + DQ_RULE_NAME, + DQ_RULE_THRESHOLD_VALUE, + DQ_TABLE_QUALIFIED_NAME, +) + + +@pytest.fixture +def mock_client(): + client = Mock() + client.dq_template_config_cache = Mock() + + # Create a proper config object with a JSON string for threshold_object + config = Mock() + config.dq_rule_template_config_threshold_object = json.dumps( + { + "properties": { + "dqRuleTemplateConfigThresholdUnit": { + "default": DataQualityRuleThresholdUnit.PERCENTAGE + } + } + } + ) + config.dq_rule_template_config_rule_conditions = json.dumps( + {"enum": ["STRING_LENGTH_BETWEEN", "STRING_LENGTH_EQUAL"]} + ) + config.dq_rule_template_config_advanced_settings = json.dumps( + {"dqRuleRowScopeFilteringEnabled": True} + ) + + def get_template_config(rule_type): + return { + "name": rule_type, + "qualified_name": "test/template/123", + "dimension": DataQualityDimension.COMPLETENESS, + "config": config, + } + + client.dq_template_config_cache.get_template_config = get_template_config + return client + + +@pytest.mark.parametrize( + "rule_name, asset, custom_sql, threshold_compare_operator, threshold_value, alert_priority, dimension, message", + [ + ( + None, + Table.ref_by_qualified_name(qualified_name=DQ_TABLE_QUALIFIED_NAME), + DQ_RULE_CUSTOM_SQL, + DataQualityRuleThresholdCompareOperator.LESS_THAN_EQUAL, + DQ_RULE_THRESHOLD_VALUE, + DataQualityRuleAlertPriority.NORMAL, + DataQualityDimension.COMPLETENESS, + "rule_name is required", + ), + ( + DQ_RULE_NAME, + None, + DQ_RULE_CUSTOM_SQL, + DataQualityRuleThresholdCompareOperator.LESS_THAN_EQUAL, + DQ_RULE_THRESHOLD_VALUE, + DataQualityRuleAlertPriority.NORMAL, + DataQualityDimension.COMPLETENESS, + "asset is required", + ), + ( + DQ_RULE_NAME, + Table.ref_by_qualified_name(qualified_name=DQ_TABLE_QUALIFIED_NAME), + None, + DataQualityRuleThresholdCompareOperator.LESS_THAN_EQUAL, + DQ_RULE_THRESHOLD_VALUE, + DataQualityRuleAlertPriority.NORMAL, + DataQualityDimension.COMPLETENESS, + "custom_sql is required", + ), + ( + DQ_RULE_NAME, + Table.ref_by_qualified_name(qualified_name=DQ_TABLE_QUALIFIED_NAME), + DQ_RULE_CUSTOM_SQL, + None, + DQ_RULE_THRESHOLD_VALUE, + DataQualityRuleAlertPriority.NORMAL, + DataQualityDimension.COMPLETENESS, + "threshold_compare_operator is required", + ), + ( + DQ_RULE_NAME, + Table.ref_by_qualified_name(qualified_name=DQ_TABLE_QUALIFIED_NAME), + DQ_RULE_CUSTOM_SQL, + DataQualityRuleThresholdCompareOperator.LESS_THAN_EQUAL, + None, + DataQualityRuleAlertPriority.NORMAL, + DataQualityDimension.COMPLETENESS, + "threshold_value is required", + ), + ( + DQ_RULE_NAME, + Table.ref_by_qualified_name(qualified_name=DQ_TABLE_QUALIFIED_NAME), + DQ_RULE_CUSTOM_SQL, + DataQualityRuleThresholdCompareOperator.LESS_THAN_EQUAL, + DQ_RULE_THRESHOLD_VALUE, + None, + DataQualityDimension.COMPLETENESS, + "alert_priority is required", + ), + ( + DQ_RULE_NAME, + Table.ref_by_qualified_name(qualified_name=DQ_TABLE_QUALIFIED_NAME), + DQ_RULE_CUSTOM_SQL, + DataQualityRuleThresholdCompareOperator.LESS_THAN_EQUAL, + DQ_RULE_THRESHOLD_VALUE, + DataQualityRuleAlertPriority.NORMAL, + None, + "dimension is required", + ), + ], +) +def test_custom_sql_creator_with_missing_parameters_raise_value_error( + rule_name: str, + asset, + custom_sql: str, + threshold_compare_operator: DataQualityRuleThresholdCompareOperator, + threshold_value: int, + alert_priority: DataQualityRuleAlertPriority, + dimension: DataQualityDimension, + message: str, + mock_client, +): + with pytest.raises(ValueError, match=message): + DataQualityRule.custom_sql_creator( + client=mock_client, + rule_name=rule_name, + asset=asset, + custom_sql=custom_sql, + threshold_compare_operator=threshold_compare_operator, + threshold_value=threshold_value, + alert_priority=alert_priority, + dimension=dimension, + ) + + +@pytest.mark.parametrize( + "rule_type, asset, threshold_compare_operator, threshold_value, alert_priority, message", + [ + ( + None, + Table.ref_by_qualified_name(qualified_name=DQ_TABLE_QUALIFIED_NAME), + DataQualityRuleThresholdCompareOperator.LESS_THAN_EQUAL, + DQ_RULE_THRESHOLD_VALUE, + DataQualityRuleAlertPriority.NORMAL, + "rule_type is required", + ), + ( + DataQualityRuleTemplateType.ROW_COUNT, + None, + DataQualityRuleThresholdCompareOperator.LESS_THAN_EQUAL, + DQ_RULE_THRESHOLD_VALUE, + DataQualityRuleAlertPriority.NORMAL, + "asset is required", + ), + ( + DataQualityRuleTemplateType.ROW_COUNT, + Table.ref_by_qualified_name(qualified_name=DQ_TABLE_QUALIFIED_NAME), + DataQualityRuleThresholdCompareOperator.LESS_THAN_EQUAL, + None, + DataQualityRuleAlertPriority.NORMAL, + "threshold_value is required", + ), + ( + DataQualityRuleTemplateType.ROW_COUNT, + Table.ref_by_qualified_name(qualified_name=DQ_TABLE_QUALIFIED_NAME), + DataQualityRuleThresholdCompareOperator.LESS_THAN_EQUAL, + DQ_RULE_THRESHOLD_VALUE, + None, + "alert_priority is required", + ), + ], +) +def test_table_level_rule_creator_with_missing_parameters_raise_value_error( + rule_type: str, + asset, + threshold_compare_operator: DataQualityRuleThresholdCompareOperator, + threshold_value: int, + alert_priority: DataQualityRuleAlertPriority, + message: str, + mock_client, +): + with pytest.raises(ValueError, match=message): + DataQualityRule.table_level_rule_creator( + client=mock_client, + rule_type=rule_type, + asset=asset, + threshold_compare_operator=threshold_compare_operator, + threshold_value=threshold_value, + alert_priority=alert_priority, + ) + + +@pytest.mark.parametrize( + "rule_type, asset, column, threshold_value, alert_priority, message", + [ + ( + None, + Table.ref_by_qualified_name(qualified_name=DQ_TABLE_QUALIFIED_NAME), + Column.ref_by_qualified_name(qualified_name=DQ_COLUMN_QUALIFIED_NAME), + DQ_RULE_THRESHOLD_VALUE, + DataQualityRuleAlertPriority.NORMAL, + "rule_type is required", + ), + ( + DataQualityRuleTemplateType.BLANK_COUNT, + None, + Column.ref_by_qualified_name(qualified_name=DQ_COLUMN_QUALIFIED_NAME), + DQ_RULE_THRESHOLD_VALUE, + DataQualityRuleAlertPriority.NORMAL, + "asset is required", + ), + ( + DataQualityRuleTemplateType.BLANK_COUNT, + Table.ref_by_qualified_name(qualified_name=DQ_TABLE_QUALIFIED_NAME), + None, + DQ_RULE_THRESHOLD_VALUE, + DataQualityRuleAlertPriority.NORMAL, + "column is required", + ), + ( + DataQualityRuleTemplateType.BLANK_COUNT, + Table.ref_by_qualified_name(qualified_name=DQ_TABLE_QUALIFIED_NAME), + Column.ref_by_qualified_name(qualified_name=DQ_COLUMN_QUALIFIED_NAME), + None, + DataQualityRuleAlertPriority.NORMAL, + "threshold_value is required", + ), + ( + DataQualityRuleTemplateType.BLANK_COUNT, + Table.ref_by_qualified_name(qualified_name=DQ_TABLE_QUALIFIED_NAME), + Column.ref_by_qualified_name(qualified_name=DQ_COLUMN_QUALIFIED_NAME), + DQ_RULE_THRESHOLD_VALUE, + None, + "alert_priority is required", + ), + ], +) +def test_column_level_rule_creator_with_missing_parameters_raise_value_error( + rule_type: str, + asset, + column, + threshold_value: int, + alert_priority: DataQualityRuleAlertPriority, + message: str, + mock_client, +): + with pytest.raises(ValueError, match=message): + DataQualityRule.column_level_rule_creator( + client=mock_client, + rule_type=rule_type, + asset=asset, + column=column, + threshold_value=threshold_value, + alert_priority=alert_priority, + ) + + +def test_table_level_rule_creator(mock_client): + asset = Table.ref_by_qualified_name(qualified_name=DQ_TABLE_QUALIFIED_NAME) + + dq_rule = DataQualityRule.table_level_rule_creator( + client=mock_client, + rule_type=DataQualityRuleTemplateType.ROW_COUNT, + asset=asset, + threshold_compare_operator=DataQualityRuleThresholdCompareOperator.LESS_THAN_EQUAL, + threshold_value=DQ_RULE_THRESHOLD_VALUE, + alert_priority=DataQualityRuleAlertPriority.NORMAL, + ) + + assert dq_rule.dq_rule_alert_priority == DataQualityRuleAlertPriority.NORMAL + assert dq_rule.dq_rule_status == DataQualityRuleStatus.ACTIVE + assert dq_rule.qualified_name.startswith(f"{DQ_TABLE_QUALIFIED_NAME}/rule/") + + +def test_table_level_rule_creator_with_threshold_unit(mock_client): + asset = Table.ref_by_qualified_name(qualified_name=DQ_TABLE_QUALIFIED_NAME) + + dq_rule = DataQualityRule.table_level_rule_creator( + client=mock_client, + rule_type=DataQualityRuleTemplateType.ROW_COUNT, + asset=asset, + threshold_compare_operator=DataQualityRuleThresholdCompareOperator.LESS_THAN_EQUAL, + threshold_value=DQ_RULE_THRESHOLD_VALUE, + alert_priority=DataQualityRuleAlertPriority.NORMAL, + threshold_unit=DataQualityRuleThresholdUnit.ABSOLUTE, + ) + + assert dq_rule.dq_rule_alert_priority == DataQualityRuleAlertPriority.NORMAL + assert dq_rule.dq_rule_status == DataQualityRuleStatus.ACTIVE + + +def test_custom_sql_creator(mock_client): + asset = Table.ref_by_qualified_name(qualified_name=DQ_TABLE_QUALIFIED_NAME) + + dq_rule = DataQualityRule.custom_sql_creator( + client=mock_client, + rule_name=DQ_RULE_NAME, + asset=asset, + custom_sql=DQ_RULE_CUSTOM_SQL, + threshold_compare_operator=DataQualityRuleThresholdCompareOperator.LESS_THAN_EQUAL, + threshold_value=DQ_RULE_THRESHOLD_VALUE, + alert_priority=DataQualityRuleAlertPriority.NORMAL, + dimension=DataQualityDimension.COMPLETENESS, + ) + + assert dq_rule.dq_rule_custom_sql == DQ_RULE_CUSTOM_SQL + assert dq_rule.dq_rule_alert_priority == DataQualityRuleAlertPriority.NORMAL + assert dq_rule.dq_rule_dimension == DataQualityDimension.COMPLETENESS + assert dq_rule.dq_rule_status == DataQualityRuleStatus.ACTIVE + assert dq_rule.qualified_name.startswith(f"{DQ_TABLE_QUALIFIED_NAME}/rule/") + + +def test_custom_sql_creator_with_optional_parameters(mock_client): + asset = Table.ref_by_qualified_name(qualified_name=DQ_TABLE_QUALIFIED_NAME) + + dq_rule = DataQualityRule.custom_sql_creator( + client=mock_client, + rule_name=DQ_RULE_NAME, + asset=asset, + custom_sql=DQ_RULE_CUSTOM_SQL, + threshold_compare_operator=DataQualityRuleThresholdCompareOperator.LESS_THAN_EQUAL, + threshold_value=DQ_RULE_THRESHOLD_VALUE, + alert_priority=DataQualityRuleAlertPriority.NORMAL, + dimension=DataQualityDimension.COMPLETENESS, + description=DQ_RULE_DESCRIPTION, + ) + + assert dq_rule.dq_rule_custom_sql == DQ_RULE_CUSTOM_SQL + assert dq_rule.dq_rule_alert_priority == DataQualityRuleAlertPriority.NORMAL + assert dq_rule.dq_rule_dimension == DataQualityDimension.COMPLETENESS + assert dq_rule.user_description == DQ_RULE_DESCRIPTION + + +def test_custom_sql_creator_with_custom_sql_return_type(mock_client): + asset = Table.ref_by_qualified_name(qualified_name=DQ_TABLE_QUALIFIED_NAME) + + dq_rule = DataQualityRule.custom_sql_creator( + client=mock_client, + rule_name=DQ_RULE_NAME, + asset=asset, + custom_sql=DQ_RULE_CUSTOM_SQL, + threshold_compare_operator=DataQualityRuleThresholdCompareOperator.LESS_THAN_EQUAL, + threshold_value=DQ_RULE_THRESHOLD_VALUE, + alert_priority=DataQualityRuleAlertPriority.NORMAL, + dimension=DataQualityDimension.COMPLETENESS, + custom_sql_return_type=DataQualityRuleCustomSQLReturnType.ROW_COUNT, + ) + + assert ( + dq_rule.dq_rule_custom_sql_return_type + == DataQualityRuleCustomSQLReturnType.ROW_COUNT + ) + + +def test_column_level_rule_creator(mock_client): + asset = Table.ref_by_qualified_name(qualified_name=DQ_TABLE_QUALIFIED_NAME) + column = Column.ref_by_qualified_name(qualified_name=DQ_COLUMN_QUALIFIED_NAME) + + dq_rule = DataQualityRule.column_level_rule_creator( + client=mock_client, + rule_type=DataQualityRuleTemplateType.BLANK_COUNT, + asset=asset, + column=column, + threshold_value=DQ_RULE_THRESHOLD_VALUE, + alert_priority=DataQualityRuleAlertPriority.NORMAL, + ) + + assert dq_rule.dq_rule_alert_priority == DataQualityRuleAlertPriority.NORMAL + assert dq_rule.dq_rule_status == DataQualityRuleStatus.ACTIVE + assert dq_rule.dq_rule_base_column_qualified_name == DQ_COLUMN_QUALIFIED_NAME + assert dq_rule.qualified_name.startswith(f"{DQ_TABLE_QUALIFIED_NAME}/rule/") + + +def test_column_level_rule_creator_with_optional_parameters(mock_client): + asset = Table.ref_by_qualified_name(qualified_name=DQ_TABLE_QUALIFIED_NAME) + column = Column.ref_by_qualified_name(qualified_name=DQ_COLUMN_QUALIFIED_NAME) + + dq_rule = DataQualityRule.column_level_rule_creator( + client=mock_client, + rule_type=DataQualityRuleTemplateType.BLANK_COUNT, + asset=asset, + column=column, + threshold_value=DQ_RULE_THRESHOLD_VALUE, + alert_priority=DataQualityRuleAlertPriority.NORMAL, + threshold_compare_operator=DataQualityRuleThresholdCompareOperator.GREATER_THAN_EQUAL, + threshold_unit=DataQualityRuleThresholdUnit.PERCENTAGE, + ) + + assert dq_rule.dq_rule_alert_priority == DataQualityRuleAlertPriority.NORMAL + assert dq_rule.dq_rule_status == DataQualityRuleStatus.ACTIVE + assert dq_rule.dq_rule_base_column_qualified_name == DQ_COLUMN_QUALIFIED_NAME + + +def test_column_level_rule_creator_with_row_scope_filtering(mock_client): + asset = Table.ref_by_qualified_name(qualified_name=DQ_TABLE_QUALIFIED_NAME) + asset.asset_dq_row_scope_filter_column_qualified_name = DQ_COLUMN_QUALIFIED_NAME + column = Column.ref_by_qualified_name(qualified_name=DQ_COLUMN_QUALIFIED_NAME) + + search_results = Mock() + search_results.current_page.return_value = [asset] + mock_client.asset.search.return_value = search_results + + dq_rule = DataQualityRule.column_level_rule_creator( + client=mock_client, + rule_type=DataQualityRuleTemplateType.BLANK_COUNT, + asset=asset, + column=column, + threshold_value=DQ_RULE_THRESHOLD_VALUE, + alert_priority=DataQualityRuleAlertPriority.NORMAL, + row_scope_filtering_enabled=True, + ) + + assert dq_rule.dq_rule_alert_priority == DataQualityRuleAlertPriority.NORMAL + assert dq_rule.dq_rule_status == DataQualityRuleStatus.ACTIVE + assert dq_rule.dq_rule_row_scope_filtering_enabled is True + assert dq_rule.dq_rule_base_column_qualified_name == DQ_COLUMN_QUALIFIED_NAME + + +def test_column_level_rule_creator_with_rule_conditions(mock_client): + asset = Table.ref_by_qualified_name(qualified_name=DQ_TABLE_QUALIFIED_NAME) + column = Column.ref_by_qualified_name(qualified_name=DQ_COLUMN_QUALIFIED_NAME) + + rule_conditions = ( + DQRuleConditionsBuilder() + .add_condition( + type=DataQualityRuleTemplateConfigRuleConditions.STRING_LENGTH_BETWEEN, + min_value=5, + max_value=50, + ) + .build() + ) + + dq_rule = DataQualityRule.column_level_rule_creator( + client=mock_client, + rule_type=DataQualityRuleTemplateType.BLANK_COUNT, + asset=asset, + column=column, + threshold_value=DQ_RULE_THRESHOLD_VALUE, + alert_priority=DataQualityRuleAlertPriority.NORMAL, + rule_conditions=rule_conditions, + ) + + assert dq_rule.dq_rule_alert_priority == DataQualityRuleAlertPriority.NORMAL + assert dq_rule.dq_rule_status == DataQualityRuleStatus.ACTIVE + assert ( + dq_rule.dq_rule_config_arguments.dq_rule_config_rule_conditions + == rule_conditions + ) + assert dq_rule.dq_rule_base_column_qualified_name == DQ_COLUMN_QUALIFIED_NAME + + +def test_validate_template_features_rule_conditions_not_supported(mock_client): + config = Mock() + config.dq_rule_template_config_rule_conditions = None + config.dq_rule_template_config_advanced_settings = json.dumps({}) + + template_config = { + "name": "BLANK_COUNT", + "qualified_name": "test/template/123", + "config": config, + } + + def get_template_config(rule_type): + return template_config + + mock_client.dq_template_config_cache.get_template_config = get_template_config + + rule_conditions = ( + DQRuleConditionsBuilder() + .add_condition( + type=DataQualityRuleTemplateConfigRuleConditions.STRING_LENGTH_BETWEEN, + min_value=5, + max_value=50, + ) + .build() + ) + + with pytest.raises( + InvalidRequestError, + match="Rule type 'BLANK_COUNT' does not support rule conditions", + ): + DataQualityRule._validate_template_features( + rule_type=DataQualityRuleTemplateType.BLANK_COUNT, + rule_conditions=rule_conditions, + row_scope_filtering_enabled=False, + template_config=template_config, + threshold_compare_operator=DataQualityRuleThresholdCompareOperator.EQUAL, + ) + + +def test_validate_template_features_row_scope_filtering_not_supported(mock_client): + config = Mock() + config.dq_rule_template_config_rule_conditions = json.dumps( + {"enum": ["STRING_LENGTH_BETWEEN"]} + ) + config.dq_rule_template_config_advanced_settings = json.dumps({}) + + template_config = { + "name": "BLANK_COUNT", + "qualified_name": "test/template/123", + "config": config, + } + + def get_template_config(rule_type): + return template_config + + mock_client.dq_template_config_cache.get_template_config = get_template_config + + with pytest.raises( + InvalidRequestError, + match="Rule type 'BLANK_COUNT' does not support row scope filtering", + ): + DataQualityRule._validate_template_features( + rule_type=DataQualityRuleTemplateType.BLANK_COUNT, + rule_conditions=None, + row_scope_filtering_enabled=True, + template_config=template_config, + threshold_compare_operator=DataQualityRuleThresholdCompareOperator.EQUAL, + ) + + +@pytest.mark.parametrize( + "qualified_name, message", + [ + (None, "qualified_name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, message: str, mock_client +): + with pytest.raises(ValueError, match=message): + DataQualityRule.updater( + client=mock_client, + qualified_name=qualified_name, + ) + + +def test_validate_template_features_invalid_rule_conditions(mock_client): + config = Mock() + config.dq_rule_template_config_rule_conditions = json.dumps( + {"enum": ["STRING_LENGTH_BETWEEN"]} + ) + config.dq_rule_template_config_advanced_settings = json.dumps({}) + + template_config = { + "name": "BLANK_COUNT", + "qualified_name": "test/template/123", + "config": config, + } + + def get_template_config(rule_type): + return template_config + + mock_client.dq_template_config_cache.get_template_config = get_template_config + + unsupported_condition = json.dumps( + {"conditions": [{"type": "UNSUPPORTED_CONDITION", "value": "test"}]} + ) + + with pytest.raises( + InvalidRequestError, + match="Invalid rule conditions: condition type 'UNSUPPORTED_CONDITION' not supported, allowed: \\['STRING_LENGTH_BETWEEN'\\]", + ): + DataQualityRule._validate_template_features( + rule_type=DataQualityRuleTemplateType.BLANK_COUNT, + rule_conditions=unsupported_condition, + row_scope_filtering_enabled=False, + template_config=template_config, + threshold_compare_operator=DataQualityRuleThresholdCompareOperator.EQUAL, + ) + + +def test_validate_template_features_row_scope_filter_column_missing(mock_client): + config = Mock() + config.dq_rule_template_config_rule_conditions = json.dumps( + {"enum": ["STRING_LENGTH_BETWEEN"]} + ) + config.dq_rule_template_config_advanced_settings = json.dumps( + {"dqRuleRowScopeFilteringEnabled": True} + ) + + template_config = { + "name": "BLANK_COUNT", + "qualified_name": "test/template/123", + "config": config, + } + + def get_template_config(rule_type): + return template_config + + mock_client.dq_template_config_cache.get_template_config = get_template_config + + table_asset = Table.ref_by_qualified_name(qualified_name=DQ_TABLE_QUALIFIED_NAME) + + with pytest.raises( + InvalidRequestError, + match=ErrorCode.DQ_ROW_SCOPE_FILTER_COLUMN_MISSING.error_message.format( + DQ_TABLE_QUALIFIED_NAME + ), + ): + DataQualityRule._validate_template_features( + rule_type=DataQualityRuleTemplateType.BLANK_COUNT, + rule_conditions=None, + row_scope_filtering_enabled=True, + template_config=template_config, + threshold_compare_operator=DataQualityRuleThresholdCompareOperator.EQUAL, + asset=table_asset, + ) + + +def test_fetch_assets_for_row_scope_validation_disabled(mock_client): + asset = Table.ref_by_qualified_name(qualified_name=DQ_TABLE_QUALIFIED_NAME) + + asset_for_validation, target_table_asset = ( + DataQualityRule._fetch_assets_for_row_scope_validation( + client=mock_client, + base_asset=asset, + rule_conditions=None, + row_scope_filtering_enabled=False, + ) + ) + + assert asset_for_validation == asset + assert target_table_asset is None + + +def test_fetch_assets_for_row_scope_validation_with_target_table(mock_client): + asset = Table.ref_by_qualified_name(qualified_name=DQ_TABLE_QUALIFIED_NAME) + asset.asset_dq_row_scope_filter_column_qualified_name = DQ_COLUMN_QUALIFIED_NAME + target_table = Table.ref_by_qualified_name( + qualified_name="target/table/qualified_name" + ) + target_table.asset_dq_row_scope_filter_column_qualified_name = ( + DQ_COLUMN_QUALIFIED_NAME + ) + + search_results = Mock() + search_results.current_page.return_value = [asset, target_table] + mock_client.asset.search.return_value = search_results + + rule_conditions = ( + DQRuleConditionsBuilder() + .add_condition( + type=DataQualityRuleTemplateConfigRuleConditions.ROW_COUNT_RECON, + target_table="target/table/qualified_name", + ) + .build() + ) + + asset_for_validation, target_table_asset = ( + DataQualityRule._fetch_assets_for_row_scope_validation( + client=mock_client, + base_asset=asset, + rule_conditions=rule_conditions, + row_scope_filtering_enabled=True, + ) + ) + + assert asset_for_validation == asset + assert target_table_asset == target_table + + +def test_dq_condition_in_list_reference(): + rule_conditions = ( + DQRuleConditionsBuilder() + .add_condition( + type=DataQualityRuleTemplateConfigRuleConditions.IN_LIST_REFERENCE, + reference_table="reference/table/qualified_name", + reference_column="reference/column/qualified_name", + ) + .build() + ) + + condition = json.loads(rule_conditions)["conditions"][0] + assert condition["type"] == "IN_LIST_REFERENCE" + assert condition["value"]["reference_table"] == "reference/table/qualified_name" + assert condition["value"]["reference_column"] == "reference/column/qualified_name" + + +def test_dq_condition_recon_with_target_table_and_column(): + rule_conditions = ( + DQRuleConditionsBuilder() + .add_condition( + type=DataQualityRuleTemplateConfigRuleConditions.AVERAGE_RECON, + target_table="target/table/qualified_name", + target_column="target/column/qualified_name", + ) + .build() + ) + + condition = json.loads(rule_conditions)["conditions"][0] + assert condition["type"] == "AVERAGE_RECON" + assert condition["value"]["target_table"] == "target/table/qualified_name" + assert condition["value"]["target_column"] == "target/column/qualified_name" + + +def test_dq_condition_missing_required_fields(): + with pytest.raises(ValueError, match="reference_table is required"): + DQRuleConditionsBuilder().add_condition( + type=DataQualityRuleTemplateConfigRuleConditions.IN_LIST_REFERENCE, + reference_table=None, + reference_column="reference/column/qualified_name", + ).build() + + with pytest.raises(ValueError, match="target_table is required"): + DQRuleConditionsBuilder().add_condition( + type=DataQualityRuleTemplateConfigRuleConditions.ROW_COUNT_RECON, + target_table=None, + ).build() + + with pytest.raises(ValueError, match="target_column is required"): + DQRuleConditionsBuilder().add_condition( + type=DataQualityRuleTemplateConfigRuleConditions.AVERAGE_RECON, + target_table="target/table/qualified_name", + target_column=None, + ).build() diff --git a/tests_v9/unit/model/data_studio_asset_test.py b/tests_v9/unit/model/data_studio_asset_test.py new file mode 100644 index 000000000..6ec6cd935 --- /dev/null +++ b/tests_v9/unit/model/data_studio_asset_test.py @@ -0,0 +1,144 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for DataStudioAsset model in pyatlan_v9.""" + +import pytest + +from pyatlan_v9.model import DataStudioAsset +from pyatlan_v9.model.enums import GoogleDatastudioAssetType +from tests_v9.unit.model.constants import ( + CONNECTOR_NAME, + DATASTUDIO_CONNECTION_QUALIFIED_NAME, + QUALIFIED_NAME_REPORT, + QUALIFIED_NAME_SOURCE, + REPORT_NAME, + SOURCE_NAME, +) + + +@pytest.mark.parametrize( + "name, connection_qualified_name, data_studio_asset_type, message", + [ + ( + None, + DATASTUDIO_CONNECTION_QUALIFIED_NAME, + GoogleDatastudioAssetType.REPORT, + "name is required", + ), + ( + REPORT_NAME, + None, + GoogleDatastudioAssetType.REPORT, + "connection_qualified_name is required", + ), + ( + REPORT_NAME, + DATASTUDIO_CONNECTION_QUALIFIED_NAME, + None, + "data_studio_asset_type is required", + ), + ], +) +def test_creator_with_missing_parameters_raise_value_error( + name: str, + connection_qualified_name: str, + data_studio_asset_type: GoogleDatastudioAssetType, + message: str, +): + """Test creator validates required parameters.""" + with pytest.raises(ValueError, match=message): + DataStudioAsset.creator( + name=name, + connection_qualified_name=connection_qualified_name, + data_studio_asset_type=data_studio_asset_type, + ) + + +def test_creator_report(): + """Test creator for REPORT asset type.""" + sut = DataStudioAsset.creator( + name=REPORT_NAME, + connection_qualified_name=DATASTUDIO_CONNECTION_QUALIFIED_NAME, + data_studio_asset_type=GoogleDatastudioAssetType.REPORT, + ) + + assert sut.name == REPORT_NAME + assert sut.connection_qualified_name == DATASTUDIO_CONNECTION_QUALIFIED_NAME + assert sut.qualified_name == QUALIFIED_NAME_REPORT + assert sut.connector_name == CONNECTOR_NAME + assert sut.data_studio_asset_type == GoogleDatastudioAssetType.REPORT + + +def test_creator_data_source(): + """Test creator for DATA_SOURCE asset type.""" + sut = DataStudioAsset.creator( + name=SOURCE_NAME, + connection_qualified_name=DATASTUDIO_CONNECTION_QUALIFIED_NAME, + data_studio_asset_type=GoogleDatastudioAssetType.DATA_SOURCE, + ) + + assert sut.name == SOURCE_NAME + assert sut.connection_qualified_name == DATASTUDIO_CONNECTION_QUALIFIED_NAME + assert sut.qualified_name == QUALIFIED_NAME_SOURCE + assert sut.connector_name == CONNECTOR_NAME + assert sut.data_studio_asset_type == GoogleDatastudioAssetType.DATA_SOURCE + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, QUALIFIED_NAME_REPORT, "qualified_name is required"), + (REPORT_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test updater validates required parameters.""" + with pytest.raises(ValueError, match=message): + DataStudioAsset.updater(qualified_name=qualified_name, name=name) + + +def test_updater_report(): + """Test updater for report asset qualified name.""" + sut = DataStudioAsset.updater( + qualified_name=QUALIFIED_NAME_REPORT, name=REPORT_NAME + ) + + assert sut.qualified_name == QUALIFIED_NAME_REPORT + assert sut.name == REPORT_NAME + + +def test_updater_data_source(): + """Test updater for data source asset qualified name.""" + sut = DataStudioAsset.updater( + qualified_name=QUALIFIED_NAME_SOURCE, name=SOURCE_NAME + ) + + assert sut.qualified_name == QUALIFIED_NAME_SOURCE + assert sut.name == SOURCE_NAME + + +def test_trim_to_required_report(): + """Test trim_to_required retains only required fields for report.""" + sut = DataStudioAsset.creator( + name=REPORT_NAME, + connection_qualified_name=DATASTUDIO_CONNECTION_QUALIFIED_NAME, + data_studio_asset_type=GoogleDatastudioAssetType.REPORT, + ).trim_to_required() + + assert sut.name == REPORT_NAME + assert sut.qualified_name == QUALIFIED_NAME_REPORT + + +def test_trim_to_required_data_source(): + """Test trim_to_required retains only required fields for data source.""" + sut = DataStudioAsset.creator( + name=SOURCE_NAME, + connection_qualified_name=DATASTUDIO_CONNECTION_QUALIFIED_NAME, + data_studio_asset_type=GoogleDatastudioAssetType.DATA_SOURCE, + ).trim_to_required() + + assert sut.name == SOURCE_NAME + assert sut.qualified_name == QUALIFIED_NAME_SOURCE diff --git a/tests_v9/unit/model/database_test.py b/tests_v9/unit/model/database_test.py new file mode 100644 index 000000000..44b5f17f9 --- /dev/null +++ b/tests_v9/unit/model/database_test.py @@ -0,0 +1,221 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for Database model in pyatlan_v9.""" + +import json + +import pytest +from msgspec import UNSET + +from pyatlan_v9.model import Database +from pyatlan_v9.model.serde import Serde +from tests_v9.unit.model.constants import ( + CONNECTION_QUALIFIED_NAME, + DATABASE_NAME, + DATABASE_QUALIFIED_NAME, +) + + +@pytest.mark.parametrize( + "name, connection_qualified_name, message", + [ + (None, "connection/name", "name is required"), + (DATABASE_NAME, None, "connection_qualified_name is required"), + (DATABASE_NAME, "abc", "Invalid connection_qualified_name"), + (DATABASE_NAME, "default/snowflake", "Invalid connection_qualified_name"), + ( + DATABASE_NAME, + "default/snowflke/1686532494/RAW", + "Invalid connection_qualified_name", + ), + ( + DATABASE_NAME, + "default/snowflake/1686532494/RAW/", + "Invalid connection_qualified_name", + ), + ], +) +def test_creator_with_missing_or_invalid_parameters_raises_value_error( + name: str, connection_qualified_name: str, message: str +): + """Test that creator raises ValueError when required parameters are missing or invalid.""" + with pytest.raises(ValueError, match=message): + Database.creator(name=name, connection_qualified_name=connection_qualified_name) + + +def test_creator(): + """Test that creator properly initializes a Database with all derived fields.""" + sut = Database.creator( + name=DATABASE_NAME, connection_qualified_name=CONNECTION_QUALIFIED_NAME + ) + + assert sut.name == DATABASE_NAME + assert sut.connection_qualified_name == CONNECTION_QUALIFIED_NAME + assert sut.qualified_name == f"{CONNECTION_QUALIFIED_NAME}/{DATABASE_NAME}" + assert sut.connector_name == "snowflake" + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, DATABASE_QUALIFIED_NAME, "qualified_name is required"), + (DATABASE_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test that updater raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + Database.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test that updater creates a Database instance for modification.""" + sut = Database.updater(qualified_name=DATABASE_QUALIFIED_NAME, name=DATABASE_NAME) + + assert sut.qualified_name == DATABASE_QUALIFIED_NAME + assert sut.name == DATABASE_NAME + + +def test_trim_to_required(): + """Test that trim_to_required returns a Database with only required fields.""" + sut = Database.updater( + qualified_name=DATABASE_QUALIFIED_NAME, name=DATABASE_NAME + ).trim_to_required() + + assert sut.qualified_name == DATABASE_QUALIFIED_NAME + assert sut.name == DATABASE_NAME + + +def test_basic_construction(): + """Test basic Database construction with minimal parameters.""" + database = Database(name=DATABASE_NAME, qualified_name=DATABASE_QUALIFIED_NAME) + + assert database.name == DATABASE_NAME + assert database.qualified_name == DATABASE_QUALIFIED_NAME + assert database.type_name == "Database" + + +def test_unset_fields(): + """Test that optional fields default to UNSET.""" + database = Database(name=DATABASE_NAME, qualified_name=DATABASE_QUALIFIED_NAME) + + assert database.schema_count is UNSET + assert database.query_count is UNSET + assert database.query_user_count is UNSET + + +def test_optional_fields(): + """Test setting optional fields on Database.""" + database = Database( + name=DATABASE_NAME, + qualified_name=DATABASE_QUALIFIED_NAME, + schema_count=5, + query_count=100, + ) + + assert database.schema_count == 5 + assert database.query_count == 100 + + +def test_none_vs_unset(): + """Test the distinction between None and UNSET values.""" + database = Database(name=DATABASE_NAME, qualified_name=DATABASE_QUALIFIED_NAME) + + assert database.sql_is_secure is UNSET + database.sql_is_secure = None + assert database.sql_is_secure is None + assert database.sql_is_secure is not UNSET + + +def test_serialization_to_json_nested(serde): + """Test serialization to nested JSON format (API format).""" + database = Database.creator( + name=DATABASE_NAME, connection_qualified_name=CONNECTION_QUALIFIED_NAME + ) + + json_str = database.to_json(nested=True, serde=serde) + data = json.loads(json_str) + + assert data["typeName"] == "Database" + assert "attributes" in data + assert data["attributes"]["name"] == DATABASE_NAME + assert data["attributes"]["qualifiedName"] == DATABASE_QUALIFIED_NAME + + +def test_serialization_to_json_flat(serde): + """Test serialization to flat JSON format.""" + database = Database.creator( + name=DATABASE_NAME, connection_qualified_name=CONNECTION_QUALIFIED_NAME + ) + + json_str = database.to_json(nested=False, serde=serde) + + assert json_str + assert DATABASE_NAME in json_str + assert DATABASE_QUALIFIED_NAME in json_str + + +def test_deserialization_from_json(serde): + """Test deserialization from nested JSON format.""" + original = Database.creator( + name=DATABASE_NAME, connection_qualified_name=CONNECTION_QUALIFIED_NAME + ) + json_str = original.to_json(nested=True, serde=serde) + + database = Database.from_json(json_str, serde=serde) + + assert database.name == DATABASE_NAME + assert database.qualified_name == DATABASE_QUALIFIED_NAME + assert database.type_name == "Database" + + +def test_round_trip_serialization(serde): + """Test that serialization and deserialization preserve all data.""" + original = Database.creator( + name=DATABASE_NAME, connection_qualified_name=CONNECTION_QUALIFIED_NAME + ) + original.schema_count = 10 + original.query_count = 500 + + json_str = original.to_json(nested=True, serde=serde) + restored = Database.from_json(json_str, serde=serde) + + assert restored.name == original.name + assert restored.qualified_name == original.qualified_name + assert restored.schema_count == original.schema_count + assert restored.query_count == original.query_count + + +def test_with_custom_serde(): + """Test that a custom Serde instance can be used for serialization.""" + custom_serde = Serde() + database = Database.creator( + name=DATABASE_NAME, connection_qualified_name=CONNECTION_QUALIFIED_NAME + ) + + json_str = database.to_json(nested=True, serde=custom_serde) + restored = Database.from_json(json_str, serde=custom_serde) + + assert restored.name == database.name + assert restored.qualified_name == database.qualified_name + + +def test_type_name_defaults(): + """Test that type_name defaults to 'Database'.""" + database = Database(name=DATABASE_NAME, qualified_name=DATABASE_QUALIFIED_NAME) + assert database.type_name == "Database" + + +def test_creator_with_guid(): + """Test that creator initializes a temporary GUID for new assets.""" + database = Database.creator( + name=DATABASE_NAME, connection_qualified_name=CONNECTION_QUALIFIED_NAME + ) + + assert database.guid is not UNSET + assert database.guid is not None + assert isinstance(database.guid, str) + assert database.guid.startswith("-") diff --git a/tests_v9/unit/model/dataverse_attribute_test.py b/tests_v9/unit/model/dataverse_attribute_test.py new file mode 100644 index 000000000..499e44f36 --- /dev/null +++ b/tests_v9/unit/model/dataverse_attribute_test.py @@ -0,0 +1,80 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for DataverseAttribute model in pyatlan_v9.""" + +import pytest + +from pyatlan_v9.model import DataverseAttribute +from tests_v9.unit.model.constants import ( + DATAVERSE_ATTRIBUTE_NAME, + DATAVERSE_ATTRIBUTE_QUALIFIED_NAME, + DATAVERSE_CONNECTION_QUALIFIED_NAME, + DATAVERSE_CONNECTOR_TYPE, + DATAVERSE_ENTITY_QUALIFIED_NAME, +) + + +@pytest.mark.parametrize( + "name, entity_qualified_name, message", + [ + (None, "connection/name", "name is required"), + (DATAVERSE_ATTRIBUTE_NAME, None, "dataverse_entity_qualified_name is required"), + ], +) +def test_creator_with_missing_parameters_raise_value_error( + name: str, entity_qualified_name: str, message: str +): + """Test creator raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + DataverseAttribute.creator( + name=name, dataverse_entity_qualified_name=entity_qualified_name + ) + + +def test_creator(): + """Test creator derives connection metadata from entity qualified name.""" + sut = DataverseAttribute.creator( + name=DATAVERSE_ATTRIBUTE_NAME, + dataverse_entity_qualified_name=DATAVERSE_ENTITY_QUALIFIED_NAME, + ) + + assert sut.name == DATAVERSE_ATTRIBUTE_NAME + assert sut.connection_qualified_name == DATAVERSE_CONNECTION_QUALIFIED_NAME + assert sut.qualified_name == DATAVERSE_ATTRIBUTE_QUALIFIED_NAME + assert sut.connector_name == DATAVERSE_CONNECTOR_TYPE + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, DATAVERSE_ENTITY_QUALIFIED_NAME, "qualified_name is required"), + (DATAVERSE_ATTRIBUTE_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test updater raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + DataverseAttribute.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test updater returns minimal update payload.""" + sut = DataverseAttribute.updater( + qualified_name=DATAVERSE_ATTRIBUTE_QUALIFIED_NAME, name=DATAVERSE_ATTRIBUTE_NAME + ) + + assert sut.qualified_name == DATAVERSE_ATTRIBUTE_QUALIFIED_NAME + assert sut.name == DATAVERSE_ATTRIBUTE_NAME + + +def test_trim_to_required(): + """Test trim_to_required preserves required update fields.""" + sut = DataverseAttribute.updater( + name=DATAVERSE_ATTRIBUTE_NAME, qualified_name=DATAVERSE_ATTRIBUTE_QUALIFIED_NAME + ).trim_to_required() + + assert sut.name == DATAVERSE_ATTRIBUTE_NAME + assert sut.qualified_name == DATAVERSE_ATTRIBUTE_QUALIFIED_NAME diff --git a/tests_v9/unit/model/dataverse_entity_test.py b/tests_v9/unit/model/dataverse_entity_test.py new file mode 100644 index 000000000..132fc26e7 --- /dev/null +++ b/tests_v9/unit/model/dataverse_entity_test.py @@ -0,0 +1,79 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for DataverseEntity model in pyatlan_v9.""" + +import pytest + +from pyatlan_v9.model import DataverseEntity +from tests_v9.unit.model.constants import ( + DATAVERSE_CONNECTION_QUALIFIED_NAME, + DATAVERSE_CONNECTOR_TYPE, + DATAVERSE_ENTITY_NAME, + DATAVERSE_ENTITY_QUALIFIED_NAME, +) + + +@pytest.mark.parametrize( + "name, connection_qualified_name, message", + [ + (None, "connection/name", "name is required"), + (DATAVERSE_ENTITY_NAME, None, "connection_qualified_name is required"), + ], +) +def test_creator_with_missing_parameters_raise_value_error( + name: str, connection_qualified_name: str, message: str +): + """Test creator raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + DataverseEntity.creator( + name=name, connection_qualified_name=connection_qualified_name + ) + + +def test_creator(): + """Test creator derives qualified_name and connector_name.""" + sut = DataverseEntity.creator( + name=DATAVERSE_ENTITY_NAME, + connection_qualified_name=DATAVERSE_CONNECTION_QUALIFIED_NAME, + ) + + assert sut.name == DATAVERSE_ENTITY_NAME + assert sut.connection_qualified_name == DATAVERSE_CONNECTION_QUALIFIED_NAME + assert sut.qualified_name == DATAVERSE_ENTITY_QUALIFIED_NAME + assert sut.connector_name == DATAVERSE_CONNECTOR_TYPE + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, DATAVERSE_CONNECTION_QUALIFIED_NAME, "qualified_name is required"), + (DATAVERSE_ENTITY_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test updater raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + DataverseEntity.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test updater returns minimal update payload.""" + sut = DataverseEntity.updater( + qualified_name=DATAVERSE_ENTITY_QUALIFIED_NAME, name=DATAVERSE_ENTITY_NAME + ) + + assert sut.qualified_name == DATAVERSE_ENTITY_QUALIFIED_NAME + assert sut.name == DATAVERSE_ENTITY_NAME + + +def test_trim_to_required(): + """Test trim_to_required preserves required update fields.""" + sut = DataverseEntity.updater( + name=DATAVERSE_ENTITY_NAME, qualified_name=DATAVERSE_ENTITY_QUALIFIED_NAME + ).trim_to_required() + + assert sut.name == DATAVERSE_ENTITY_NAME + assert sut.qualified_name == DATAVERSE_ENTITY_QUALIFIED_NAME diff --git a/tests_v9/unit/model/document_d_b_collection_test.py b/tests_v9/unit/model/document_d_b_collection_test.py new file mode 100644 index 000000000..9ff70b6f1 --- /dev/null +++ b/tests_v9/unit/model/document_d_b_collection_test.py @@ -0,0 +1,99 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for DocumentDBCollection model in pyatlan_v9.""" + +import pytest + +from pyatlan_v9.model import DocumentDBCollection +from tests_v9.unit.model.constants import ( + DOCUMENTDB_COLLECTION_NAME, + DOCUMENTDB_COLLECTION_QUALIFIED_NAME, + DOCUMENTDB_CONNECTION_QUALIFIED_NAME, + DOCUMENTDB_CONNECTOR_TYPE, + DOCUMENTDB_DATABASE_QUALIFIED_NAME, +) + + +@pytest.mark.parametrize( + "name, database_qualified_name, connection_qualified_name, message", + [ + ( + None, + DOCUMENTDB_DATABASE_QUALIFIED_NAME, + DOCUMENTDB_CONNECTION_QUALIFIED_NAME, + "name is required", + ), + ( + DOCUMENTDB_COLLECTION_NAME, + None, + DOCUMENTDB_CONNECTION_QUALIFIED_NAME, + "database_qualified_name is required", + ), + ], +) +def test_creator_with_missing_parameters_raise_value_error( + name: str, + database_qualified_name: str, + connection_qualified_name: str, + message: str, +): + """Test creator validates required parameters.""" + with pytest.raises(ValueError, match=message): + DocumentDBCollection.creator( + name=name, + database_qualified_name=database_qualified_name, + connection_qualified_name=connection_qualified_name, + ) + + +def test_creator(): + """Test creator initializes expected derived fields.""" + sut = DocumentDBCollection.creator( + name=DOCUMENTDB_COLLECTION_NAME, + database_qualified_name=DOCUMENTDB_DATABASE_QUALIFIED_NAME, + connection_qualified_name=DOCUMENTDB_CONNECTION_QUALIFIED_NAME, + ) + + assert sut.name == DOCUMENTDB_COLLECTION_NAME + assert sut.database_qualified_name == DOCUMENTDB_DATABASE_QUALIFIED_NAME + assert sut.connection_qualified_name == DOCUMENTDB_CONNECTION_QUALIFIED_NAME + assert sut.qualified_name == DOCUMENTDB_COLLECTION_QUALIFIED_NAME + assert sut.connector_name == DOCUMENTDB_CONNECTOR_TYPE + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, DOCUMENTDB_COLLECTION_NAME, "qualified_name is required"), + (DOCUMENTDB_COLLECTION_QUALIFIED_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test updater validates required parameters.""" + with pytest.raises(ValueError, match=message): + DocumentDBCollection.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test updater creates a DocumentDBCollection for modification.""" + sut = DocumentDBCollection.updater( + qualified_name=DOCUMENTDB_COLLECTION_QUALIFIED_NAME, + name=DOCUMENTDB_COLLECTION_NAME, + ) + + assert sut.qualified_name == DOCUMENTDB_COLLECTION_QUALIFIED_NAME + assert sut.name == DOCUMENTDB_COLLECTION_NAME + + +def test_trim_to_required(): + """Test trim_to_required keeps only updater-required fields.""" + sut = DocumentDBCollection.updater( + qualified_name=DOCUMENTDB_COLLECTION_QUALIFIED_NAME, + name=DOCUMENTDB_COLLECTION_NAME, + ).trim_to_required() + + assert sut.qualified_name == DOCUMENTDB_COLLECTION_QUALIFIED_NAME + assert sut.name == DOCUMENTDB_COLLECTION_NAME diff --git a/tests_v9/unit/model/document_d_b_database_test.py b/tests_v9/unit/model/document_d_b_database_test.py new file mode 100644 index 000000000..30c835292 --- /dev/null +++ b/tests_v9/unit/model/document_d_b_database_test.py @@ -0,0 +1,79 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for DocumentDBDatabase model in pyatlan_v9.""" + +import pytest + +from pyatlan_v9.model import DocumentDBDatabase +from tests_v9.unit.model.constants import ( + DOCUMENTDB_CONNECTION_QUALIFIED_NAME, + DOCUMENTDB_CONNECTOR_TYPE, + DOCUMENTDB_DATABASE_NAME, + DOCUMENTDB_DATABASE_QUALIFIED_NAME, +) + + +@pytest.mark.parametrize( + "name, connection_qualified_name, message", + [ + (None, DOCUMENTDB_CONNECTION_QUALIFIED_NAME, "name is required"), + (DOCUMENTDB_DATABASE_NAME, None, "connection_qualified_name is required"), + ], +) +def test_creator_with_missing_parameters_raise_value_error( + name: str, connection_qualified_name: str, message: str +): + """Test creator validates required parameters.""" + with pytest.raises(ValueError, match=message): + DocumentDBDatabase.creator( + name=name, connection_qualified_name=connection_qualified_name + ) + + +def test_creator(): + """Test creator initializes expected derived fields.""" + sut = DocumentDBDatabase.creator( + name=DOCUMENTDB_DATABASE_NAME, + connection_qualified_name=DOCUMENTDB_CONNECTION_QUALIFIED_NAME, + ) + + assert sut.name == DOCUMENTDB_DATABASE_NAME + assert sut.connection_qualified_name == DOCUMENTDB_CONNECTION_QUALIFIED_NAME + assert sut.qualified_name == DOCUMENTDB_DATABASE_QUALIFIED_NAME + assert sut.connector_name == DOCUMENTDB_CONNECTOR_TYPE + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, DOCUMENTDB_DATABASE_NAME, "qualified_name is required"), + (DOCUMENTDB_DATABASE_QUALIFIED_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test updater validates required parameters.""" + with pytest.raises(ValueError, match=message): + DocumentDBDatabase.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test updater creates a DocumentDBDatabase for modification.""" + sut = DocumentDBDatabase.updater( + qualified_name=DOCUMENTDB_DATABASE_QUALIFIED_NAME, name=DOCUMENTDB_DATABASE_NAME + ) + + assert sut.qualified_name == DOCUMENTDB_DATABASE_QUALIFIED_NAME + assert sut.name == DOCUMENTDB_DATABASE_NAME + + +def test_trim_to_required(): + """Test trim_to_required keeps only updater-required fields.""" + sut = DocumentDBDatabase.updater( + qualified_name=DOCUMENTDB_DATABASE_QUALIFIED_NAME, name=DOCUMENTDB_DATABASE_NAME + ).trim_to_required() + + assert sut.qualified_name == DOCUMENTDB_DATABASE_QUALIFIED_NAME + assert sut.name == DOCUMENTDB_DATABASE_NAME diff --git a/tests_v9/unit/model/fields/__init__.py b/tests_v9/unit/model/fields/__init__.py new file mode 100644 index 000000000..de398f096 --- /dev/null +++ b/tests_v9/unit/model/fields/__init__.py @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. diff --git a/tests_v9/unit/model/fields/atlan_fields_test.py b/tests_v9/unit/model/fields/atlan_fields_test.py new file mode 100644 index 000000000..3674b1359 --- /dev/null +++ b/tests_v9/unit/model/fields/atlan_fields_test.py @@ -0,0 +1,168 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Parity tests for searchable field types.""" + +import pytest + +from pyatlan.model.fields.atlan_fields import ( + AtlanSearchableFieldType, + InternalKeywordTextField, + KeywordField, + KeywordTextField, + SearchableField, +) +from pyatlan_v9.model.enums import SortOrder +from pyatlan_v9.model.search import Exists + +ATLAN_FIELD_NAME = "atlan_field_name" +ELASTIC_FIELD_NAME = "elastic_field_name" +INTERNAL_FIELD_NAME = "internal_field_name" +KEYWORD_FIELD_NAME = "keyword_field_name" +TEXT_FIELD_NAME = "text_field_name" + + +class TestSearchableField: + """Tests for SearchableField behavior.""" + + @pytest.fixture() + def sut(self) -> SearchableField: + """Build a SearchableField instance.""" + return SearchableField( + atlan_field_name=ATLAN_FIELD_NAME, elastic_field_name=ELASTIC_FIELD_NAME + ) + + def test_internal_field_name(self, sut: SearchableField): + """Test internal field name.""" + assert sut.internal_field_name == ATLAN_FIELD_NAME + + def test_atlan_field_name(self, sut: SearchableField): + """Test Atlan field name.""" + assert sut.atlan_field_name == ATLAN_FIELD_NAME + + def test_elastic_field_name(self, sut: SearchableField): + """Test Elasticsearch field name.""" + assert sut.elastic_field_name == ELASTIC_FIELD_NAME + + def test_has_any_value(self, sut: SearchableField): + """Test has_any_value returns Exists query.""" + exists = sut.has_any_value() + + assert isinstance(exists, Exists) + assert exists.field == sut.elastic_field_name + + def test_order(self, sut: SearchableField): + """Test order returns sort item using field and order.""" + order = SortOrder.DESCENDING + + sort_item = sut.order(order=order) + + assert sort_item.order == order + assert sort_item.field == sut.elastic_field_name + + +class TestKeywordField: + """Tests for KeywordField behavior.""" + + @pytest.fixture() + def sut(self) -> KeywordField: + """Build a KeywordField instance.""" + return KeywordField( + atlan_field_name=ATLAN_FIELD_NAME, keyword_field_name=KEYWORD_FIELD_NAME + ) + + def test_internal_field_name(self, sut: KeywordField): + """Test internal field name.""" + assert sut.internal_field_name == ATLAN_FIELD_NAME + + def test_atlan_field_name(self, sut: KeywordField): + """Test Atlan field name.""" + assert sut.atlan_field_name == ATLAN_FIELD_NAME + + def test_keyword_field_name(self, sut: KeywordField): + """Test keyword field name.""" + assert sut.keyword_field_name == KEYWORD_FIELD_NAME + + def test_elastic_field_name(self, sut: SearchableField): + """Test Elasticsearch field name maps to keyword field.""" + assert sut.elastic_field_name == KEYWORD_FIELD_NAME + + +class TestKeywordTextField: + """Tests for KeywordTextField behavior.""" + + @pytest.fixture() + def sut(self) -> KeywordTextField: + """Build a KeywordTextField instance.""" + return KeywordTextField( + atlan_field_name=ATLAN_FIELD_NAME, + keyword_field_name=KEYWORD_FIELD_NAME, + text_field_name=TEXT_FIELD_NAME, + ) + + def test_internal_field_name(self, sut: KeywordTextField): + """Test internal field name.""" + assert sut.internal_field_name == ATLAN_FIELD_NAME + + def test_text_field_name(self, sut: KeywordTextField): + """Test text field name.""" + assert sut.text_field_name == TEXT_FIELD_NAME + + def test_atlan_field_name(self, sut: KeywordTextField): + """Test Atlan field name.""" + assert sut.atlan_field_name == ATLAN_FIELD_NAME + + def test_keyword_field_name(self, sut: KeywordTextField): + """Test keyword field name.""" + assert sut.keyword_field_name == KEYWORD_FIELD_NAME + + def test_has_any_value_default_uses_keyword_field(self, sut: KeywordTextField): + """Test default has_any_value uses keyword field.""" + exists = sut.has_any_value() + + assert isinstance(exists, Exists) + assert exists.field == KEYWORD_FIELD_NAME + + def test_has_any_value_with_keyword_field_type(self, sut: KeywordTextField): + """Test has_any_value with KEYWORD type uses keyword field.""" + exists = sut.has_any_value(field_type=AtlanSearchableFieldType.KEYWORD) + + assert isinstance(exists, Exists) + assert exists.field == KEYWORD_FIELD_NAME + + def test_has_any_value_with_text_field_type(self, sut: KeywordTextField): + """Test has_any_value with TEXT type uses text field.""" + exists = sut.has_any_value(field_type=AtlanSearchableFieldType.TEXT) + + assert isinstance(exists, Exists) + assert exists.field == TEXT_FIELD_NAME + + +class TestInternalKeywordTextField: + """Tests for InternalKeywordTextField behavior.""" + + @pytest.fixture() + def sut(self) -> InternalKeywordTextField: + """Build an InternalKeywordTextField instance.""" + return InternalKeywordTextField( + atlan_field_name=ATLAN_FIELD_NAME, + keyword_field_name=KEYWORD_FIELD_NAME, + text_field_name=TEXT_FIELD_NAME, + internal_field_name=INTERNAL_FIELD_NAME, + ) + + def test_internal_field_name(self, sut: InternalKeywordTextField): + """Test explicit internal field name.""" + assert sut.internal_field_name == INTERNAL_FIELD_NAME + + def test_text_field_name(self, sut: InternalKeywordTextField): + """Test text field name.""" + assert sut.text_field_name == TEXT_FIELD_NAME + + def test_atlan_field_name(self, sut: InternalKeywordTextField): + """Test Atlan field name.""" + assert sut.atlan_field_name == ATLAN_FIELD_NAME + + def test_keyword_field_name(self, sut: InternalKeywordTextField): + """Test keyword field name.""" + assert sut.keyword_field_name == KEYWORD_FIELD_NAME diff --git a/tests_v9/unit/model/file_test.py b/tests_v9/unit/model/file_test.py new file mode 100644 index 000000000..16ca984b6 --- /dev/null +++ b/tests_v9/unit/model/file_test.py @@ -0,0 +1,235 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for File model in pyatlan_v9.""" + +import json + +import pytest +from msgspec import UNSET + +from pyatlan_v9.model import File +from pyatlan_v9.model.serde import Serde +from tests_v9.unit.model.constants import ( + FILE_CONNECTION_QUALIFIED_NAME, + FILE_NAME, + FILE_QUALIFIED_NAME, +) + +FILE_TYPE = "PDF" + + +@pytest.mark.parametrize( + "name, connection_qualified_name, file_type, msg", + [ + (None, FILE_CONNECTION_QUALIFIED_NAME, FILE_TYPE, "name is required"), + (FILE_NAME, None, FILE_TYPE, "connection_qualified_name is required"), + ("", FILE_CONNECTION_QUALIFIED_NAME, FILE_TYPE, "name cannot be blank"), + (FILE_NAME, "", FILE_TYPE, "connection_qualified_name cannot be blank"), + (FILE_NAME, FILE_CONNECTION_QUALIFIED_NAME, None, "file_type is required"), + (FILE_NAME, FILE_CONNECTION_QUALIFIED_NAME, "", "file_type cannot be blank"), + ], +) +def test_creator_with_missing_parameters_raises_value_error( + name, connection_qualified_name, file_type, msg +): + """Test that creator raises ValueError when required parameters are missing or blank.""" + with pytest.raises(ValueError, match=msg): + File.creator( + name=name, + connection_qualified_name=connection_qualified_name, + file_type=file_type, + ) + + +def test_creator(): + """Test that creator properly initializes a File with all derived fields.""" + sut = File.creator( + name=FILE_NAME, + connection_qualified_name=FILE_CONNECTION_QUALIFIED_NAME, + file_type=FILE_TYPE, + ) + + assert sut.name == FILE_NAME + assert sut.connection_qualified_name == FILE_CONNECTION_QUALIFIED_NAME + assert sut.qualified_name == FILE_QUALIFIED_NAME + assert sut.connector_name == "api" + assert sut.file_type == FILE_TYPE + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, FILE_QUALIFIED_NAME, "qualified_name is required"), + (FILE_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test that updater raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + File.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test that updater creates a File instance for modification.""" + sut = File.updater(qualified_name=FILE_QUALIFIED_NAME, name=FILE_NAME) + + assert sut.qualified_name == FILE_QUALIFIED_NAME + assert sut.name == FILE_NAME + + +def test_trim_to_required(): + """Test that trim_to_required returns a File with only required fields.""" + sut = File.updater( + qualified_name=FILE_QUALIFIED_NAME, name=FILE_NAME + ).trim_to_required() + + assert sut.qualified_name == FILE_QUALIFIED_NAME + assert sut.name == FILE_NAME + + +def test_basic_construction(): + """Test basic File construction with minimal parameters.""" + file = File(name=FILE_NAME, qualified_name=FILE_QUALIFIED_NAME) + + assert file.name == FILE_NAME + assert file.qualified_name == FILE_QUALIFIED_NAME + assert file.type_name == "File" + + +def test_unset_fields(): + """Test that optional fields default to UNSET.""" + file = File(name=FILE_NAME, qualified_name=FILE_QUALIFIED_NAME) + + assert file.file_type is UNSET + assert file.file_path is UNSET + assert file.link is UNSET + assert file.is_global is UNSET + assert file.reference is UNSET + + +def test_optional_fields(): + """Test setting optional fields on File.""" + file = File( + name=FILE_NAME, + qualified_name=FILE_QUALIFIED_NAME, + file_type=FILE_TYPE, + link="https://example.com/file.pdf", + ) + + assert file.file_type == FILE_TYPE + assert file.link == "https://example.com/file.pdf" + + +def test_none_vs_unset(): + """Test the distinction between None and UNSET values.""" + file = File(name=FILE_NAME, qualified_name=FILE_QUALIFIED_NAME) + + assert file.link is UNSET + file.link = None + assert file.link is None + assert file.link is not UNSET + + +def test_serialization_to_json_nested(serde): + """Test serialization to nested JSON format (API format).""" + file = File.creator( + name=FILE_NAME, + connection_qualified_name=FILE_CONNECTION_QUALIFIED_NAME, + file_type=FILE_TYPE, + ) + + json_str = file.to_json(nested=True, serde=serde) + data = json.loads(json_str) + + assert data["typeName"] == "File" + assert "attributes" in data + assert data["attributes"]["name"] == FILE_NAME + assert data["attributes"]["qualifiedName"] == FILE_QUALIFIED_NAME + + +def test_serialization_to_json_flat(serde): + """Test serialization to flat JSON format.""" + file = File.creator( + name=FILE_NAME, + connection_qualified_name=FILE_CONNECTION_QUALIFIED_NAME, + file_type=FILE_TYPE, + ) + + json_str = file.to_json(nested=False, serde=serde) + + assert json_str + assert FILE_NAME in json_str + assert FILE_QUALIFIED_NAME in json_str + + +def test_deserialization_from_json(serde): + """Test deserialization from nested JSON format.""" + original = File.creator( + name=FILE_NAME, + connection_qualified_name=FILE_CONNECTION_QUALIFIED_NAME, + file_type=FILE_TYPE, + ) + json_str = original.to_json(nested=True, serde=serde) + + file = File.from_json(json_str, serde=serde) + + assert file.name == FILE_NAME + assert file.qualified_name == FILE_QUALIFIED_NAME + assert file.type_name == "File" + + +def test_round_trip_serialization(serde): + """Test that serialization and deserialization preserve all data.""" + original = File.creator( + name=FILE_NAME, + connection_qualified_name=FILE_CONNECTION_QUALIFIED_NAME, + file_type=FILE_TYPE, + ) + original.link = "https://example.com/file.pdf" + + json_str = original.to_json(nested=True, serde=serde) + restored = File.from_json(json_str, serde=serde) + + assert restored.name == original.name + assert restored.qualified_name == original.qualified_name + assert restored.file_type == original.file_type + assert restored.link == original.link + + +def test_with_custom_serde(): + """Test that a custom Serde instance can be used for serialization.""" + custom_serde = Serde() + file = File.creator( + name=FILE_NAME, + connection_qualified_name=FILE_CONNECTION_QUALIFIED_NAME, + file_type=FILE_TYPE, + ) + + json_str = file.to_json(nested=True, serde=custom_serde) + restored = File.from_json(json_str, serde=custom_serde) + + assert restored.name == file.name + assert restored.qualified_name == file.qualified_name + + +def test_type_name_defaults(): + """Test that type_name defaults to 'File'.""" + file = File(name=FILE_NAME, qualified_name=FILE_QUALIFIED_NAME) + assert file.type_name == "File" + + +def test_creator_with_guid(): + """Test that creator initializes a temporary GUID for new assets.""" + file = File.creator( + name=FILE_NAME, + connection_qualified_name=FILE_CONNECTION_QUALIFIED_NAME, + file_type=FILE_TYPE, + ) + + assert file.guid is not UNSET + assert file.guid is not None + assert isinstance(file.guid, str) + assert file.guid.startswith("-") diff --git a/tests_v9/unit/model/gcs_bucket_test.py b/tests_v9/unit/model/gcs_bucket_test.py new file mode 100644 index 000000000..a62e52626 --- /dev/null +++ b/tests_v9/unit/model/gcs_bucket_test.py @@ -0,0 +1,76 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for GCSBucket model in pyatlan_v9.""" + +import pytest + +from pyatlan_v9.model import GCSBucket +from tests_v9.unit.model.constants import ( + GCS_BUCKET_NAME, + GCS_CONNECTION_QUALIFIED_NAME, + GCS_CONNECTOR_TYPE, + GCS_QUALIFIED_NAME, +) + + +@pytest.mark.parametrize( + "name, connection_qualified_name, message", + [ + (None, "connection/name", "name is required"), + (GCS_BUCKET_NAME, None, "connection_qualified_name is required"), + ], +) +def test_creator_with_missing_parameters_raise_value_error( + name: str, connection_qualified_name: str, message: str +): + """Test creator raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + GCSBucket.creator( + name=name, connection_qualified_name=connection_qualified_name + ) + + +def test_creator(): + """Test creator populates derived fields for a GCSBucket.""" + sut = GCSBucket.creator( + name=GCS_BUCKET_NAME, connection_qualified_name=GCS_CONNECTION_QUALIFIED_NAME + ) + + assert sut.name == GCS_BUCKET_NAME + assert sut.connection_qualified_name == GCS_CONNECTION_QUALIFIED_NAME + assert sut.qualified_name == GCS_QUALIFIED_NAME + assert sut.connector_name == GCS_CONNECTOR_TYPE + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, GCS_QUALIFIED_NAME, "qualified_name is required"), + (GCS_BUCKET_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test updater raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + GCSBucket.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test updater creates a GCSBucket instance for modification.""" + sut = GCSBucket.updater(qualified_name=GCS_QUALIFIED_NAME, name=GCS_BUCKET_NAME) + + assert sut.qualified_name == GCS_QUALIFIED_NAME + assert sut.name == GCS_BUCKET_NAME + + +def test_trim_to_required(): + """Test trim_to_required returns a GCSBucket with only required fields.""" + sut = GCSBucket.creator( + name=GCS_BUCKET_NAME, connection_qualified_name=GCS_CONNECTION_QUALIFIED_NAME + ).trim_to_required() + + assert sut.name == GCS_BUCKET_NAME + assert sut.qualified_name == GCS_QUALIFIED_NAME diff --git a/tests_v9/unit/model/gcs_object_test.py b/tests_v9/unit/model/gcs_object_test.py new file mode 100644 index 000000000..d91dbf611 --- /dev/null +++ b/tests_v9/unit/model/gcs_object_test.py @@ -0,0 +1,271 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for GCSObject model in pyatlan_v9.""" + +import pytest + +from pyatlan.model.utils import construct_object_key +from pyatlan_v9.model import GCSObject +from tests_v9.unit.model.constants import ( + GCS_BUCKET_NAME, + GCS_BUCKET_QUALIFIED_NAME, + GCS_CONNECTION_QUALIFIED_NAME, + GCS_OBJECT_NAME, + GCS_OBJECT_PREFIX, + GCS_OBJECT_QUALIFIED_NAME, +) + + +@pytest.mark.parametrize( + "name, gcs_bucket_name, gcs_bucket_qualified_name, message", + [ + (None, GCS_BUCKET_NAME, "object/name/qn", "name is required"), + (GCS_OBJECT_NAME, None, "object/name/qn", "gcs_bucket_name is required"), + ( + GCS_OBJECT_NAME, + GCS_BUCKET_NAME, + None, + "gcs_bucket_qualified_name is required", + ), + ], +) +def test_creator_with_missing_parameters_raise_value_error( + name: str, gcs_bucket_name: str, gcs_bucket_qualified_name: str, message: str +): + """Test creator raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + GCSObject.creator( + name=name, + gcs_bucket_name=gcs_bucket_name, + gcs_bucket_qualified_name=gcs_bucket_qualified_name, + ) + + +@pytest.mark.parametrize( + "name, connection_qualified_name, prefix, gcs_bucket_name, gcs_bucket_qualified_name, msg", + [ + ( + None, + GCS_CONNECTION_QUALIFIED_NAME, + "abc", + GCS_BUCKET_NAME, + GCS_BUCKET_QUALIFIED_NAME, + "name is required", + ), + ( + GCS_OBJECT_NAME, + None, + "abc", + GCS_BUCKET_NAME, + GCS_BUCKET_QUALIFIED_NAME, + "connection_qualified_name is required", + ), + ( + "", + GCS_CONNECTION_QUALIFIED_NAME, + "abc", + GCS_BUCKET_NAME, + GCS_BUCKET_QUALIFIED_NAME, + "name cannot be blank", + ), + ( + GCS_OBJECT_NAME, + "", + "abc", + GCS_BUCKET_NAME, + GCS_BUCKET_QUALIFIED_NAME, + "connection_qualified_name cannot be blank", + ), + ( + GCS_OBJECT_NAME, + "default/gcs/", + "abc", + GCS_BUCKET_NAME, + GCS_BUCKET_QUALIFIED_NAME, + "Invalid connection_qualified_name", + ), + ( + GCS_OBJECT_NAME, + "/gcs/", + "abc", + GCS_BUCKET_NAME, + GCS_BUCKET_QUALIFIED_NAME, + "Invalid connection_qualified_name", + ), + ( + GCS_OBJECT_NAME, + "default/gcs/production/TestDb", + "abc", + GCS_BUCKET_NAME, + GCS_BUCKET_QUALIFIED_NAME, + "Invalid connection_qualified_name", + ), + ( + GCS_OBJECT_NAME, + "gcs/production", + "abc", + GCS_BUCKET_NAME, + GCS_BUCKET_QUALIFIED_NAME, + "Invalid connection_qualified_name", + ), + ( + GCS_OBJECT_NAME, + "default/gcs-invalid/production", + "abc", + GCS_BUCKET_NAME, + GCS_BUCKET_QUALIFIED_NAME, + "Invalid connection_qualified_name", + ), + ( + GCS_OBJECT_NAME, + GCS_CONNECTION_QUALIFIED_NAME, + "abc", + None, + GCS_BUCKET_QUALIFIED_NAME, + "gcs_bucket_name is required", + ), + ( + GCS_OBJECT_NAME, + GCS_CONNECTION_QUALIFIED_NAME, + "abc", + "", + GCS_BUCKET_QUALIFIED_NAME, + "gcs_bucket_name cannot be blank", + ), + ( + GCS_OBJECT_NAME, + GCS_CONNECTION_QUALIFIED_NAME, + "abc", + GCS_BUCKET_NAME, + None, + "gcs_bucket_qualified_name is required", + ), + ( + GCS_OBJECT_NAME, + GCS_CONNECTION_QUALIFIED_NAME, + "abc", + GCS_BUCKET_NAME, + "", + "gcs_bucket_qualified_name cannot be blank", + ), + ], +) +def test_creator_with_prefix_without_required_parameters_raises_validation_error( + name, + connection_qualified_name, + prefix, + gcs_bucket_name, + gcs_bucket_qualified_name, + msg, +): + """Test creator_with_prefix validation for missing or invalid parameters.""" + with pytest.raises(ValueError, match=msg): + GCSObject.creator_with_prefix( + name=name, + connection_qualified_name=connection_qualified_name, + gcs_bucket_name=gcs_bucket_name, + gcs_bucket_qualified_name=gcs_bucket_qualified_name, + prefix=prefix, + ) + + +def test_creator(): + """Test creator derives qualified name and connection values.""" + sut = GCSObject.creator( + name=GCS_OBJECT_NAME, + gcs_bucket_name=GCS_BUCKET_NAME, + gcs_bucket_qualified_name=GCS_BUCKET_QUALIFIED_NAME, + ) + + assert sut.name == GCS_OBJECT_NAME + assert sut.gcs_bucket_qualified_name == GCS_BUCKET_QUALIFIED_NAME + assert sut.qualified_name == GCS_OBJECT_QUALIFIED_NAME + assert sut.connection_qualified_name == GCS_CONNECTION_QUALIFIED_NAME + + +@pytest.mark.parametrize( + "name, connection_qualified_name, prefix, gcs_bucket_name, gcs_bucket_qualified_name", + [ + ( + GCS_OBJECT_NAME, + GCS_CONNECTION_QUALIFIED_NAME, + GCS_OBJECT_PREFIX, + GCS_BUCKET_NAME, + GCS_BUCKET_QUALIFIED_NAME, + ), + ], +) +def test_creator_with_prefix( + name, connection_qualified_name, prefix, gcs_bucket_name, gcs_bucket_qualified_name +): + """Test creator_with_prefix computes object key and qualified name.""" + attributes = GCSObject.creator_with_prefix( + name=name, + connection_qualified_name=connection_qualified_name, + gcs_bucket_name=gcs_bucket_name, + gcs_bucket_qualified_name=gcs_bucket_qualified_name, + prefix=prefix, + ) + assert attributes.name == name + assert attributes.connection_qualified_name == connection_qualified_name + object_key = construct_object_key(prefix, name) + assert attributes.gcs_object_key == object_key + assert ( + attributes.qualified_name + == f"{connection_qualified_name}/{attributes.gcs_bucket_name}/{object_key}" + ) + assert attributes.connector_name == connection_qualified_name.split("/")[1] + assert attributes.gcs_bucket_qualified_name == gcs_bucket_qualified_name + + +def test_overload_creator(): + """Test creator accepts explicit connection_qualified_name override.""" + sut = GCSObject.creator( + name=GCS_OBJECT_NAME, + gcs_bucket_name=GCS_BUCKET_NAME, + gcs_bucket_qualified_name=GCS_BUCKET_QUALIFIED_NAME, + connection_qualified_name=GCS_CONNECTION_QUALIFIED_NAME, + ) + + assert sut.name == GCS_OBJECT_NAME + assert sut.gcs_bucket_qualified_name == GCS_BUCKET_QUALIFIED_NAME + assert sut.qualified_name == GCS_OBJECT_QUALIFIED_NAME + assert sut.connection_qualified_name == GCS_CONNECTION_QUALIFIED_NAME + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, GCS_OBJECT_QUALIFIED_NAME, "qualified_name is required"), + (GCS_OBJECT_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test updater raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + GCSObject.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test updater returns minimal update payload.""" + sut = GCSObject.updater( + qualified_name=GCS_OBJECT_QUALIFIED_NAME, name=GCS_OBJECT_NAME + ) + + assert sut.qualified_name == GCS_OBJECT_QUALIFIED_NAME + assert sut.name == GCS_OBJECT_NAME + + +def test_trim_to_required(): + """Test trim_to_required keeps only required fields for updates.""" + sut = GCSObject.creator( + name=GCS_OBJECT_NAME, + gcs_bucket_name=GCS_BUCKET_NAME, + gcs_bucket_qualified_name=GCS_BUCKET_QUALIFIED_NAME, + ).trim_to_required() + + assert sut.name == GCS_OBJECT_NAME + assert sut.qualified_name == GCS_OBJECT_QUALIFIED_NAME diff --git a/tests_v9/unit/model/glossary_category_test.py b/tests_v9/unit/model/glossary_category_test.py new file mode 100644 index 000000000..34a982140 --- /dev/null +++ b/tests_v9/unit/model/glossary_category_test.py @@ -0,0 +1,298 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for AtlasGlossaryCategory model in pyatlan_v9.""" + +import json + +import pytest +from msgspec import UNSET + +from pyatlan_v9.model import AtlasGlossary, AtlasGlossaryCategory +from pyatlan_v9.model.serde import Serde +from tests_v9.unit.model.constants import ( + GLOSSARY_CATEGORY_NAME, + GLOSSARY_CATEGORY_QUALIFIED_NAME, + GLOSSARY_NAME, + GLOSSARY_QUALIFIED_NAME, +) + +GLOSSARY_GUID = "123" +ANCHOR = AtlasGlossary.updater( + qualified_name=GLOSSARY_QUALIFIED_NAME, name=GLOSSARY_NAME +) +# Set guid for ANCHOR to avoid serialization issues +ANCHOR.guid = GLOSSARY_GUID +PARENT_CATEGORY = AtlasGlossaryCategory.updater( + qualified_name="123", name="Category", glossary_guid=GLOSSARY_GUID +) + + +@pytest.mark.parametrize( + "name, anchor, parent_category, message", + [ + (None, ANCHOR, None, "name is required"), + ], +) +def test_creator_with_missing_parameters_raises_value_error( + name: str, + anchor: AtlasGlossary, + parent_category: AtlasGlossaryCategory, + message: str, +): + """Test that creator raises ValueError when name parameter is missing.""" + with pytest.raises(ValueError, match=message): + AtlasGlossaryCategory.creator( + name=name, + anchor=anchor, + parent_category=parent_category, + ) + + +@pytest.mark.parametrize( + "anchor, parent_category", + [ + (ANCHOR, None), + (ANCHOR, PARENT_CATEGORY), + ], +) +def test_creator( + anchor: AtlasGlossary, + parent_category: AtlasGlossaryCategory, +): + """Test that creator properly initializes a GlossaryCategory with optional parent category.""" + sut = AtlasGlossaryCategory.creator( + name=GLOSSARY_CATEGORY_NAME, + anchor=anchor, + parent_category=parent_category, + ) + + assert sut.name == GLOSSARY_CATEGORY_NAME + assert sut.qualified_name + + # Verify parent_category is set correctly + if parent_category: + assert sut.parent_category is not None + else: + assert sut.parent_category is None or sut.parent_category is UNSET + + # Verify anchor is set + assert sut.anchor is not None + + +@pytest.mark.parametrize( + "name, qualified_name, glossary_guid, message", + [ + (None, GLOSSARY_CATEGORY_QUALIFIED_NAME, GLOSSARY_GUID, "name is required"), + (GLOSSARY_CATEGORY_NAME, None, GLOSSARY_GUID, "qualified_name is required"), + ( + GLOSSARY_CATEGORY_NAME, + GLOSSARY_CATEGORY_QUALIFIED_NAME, + None, + "glossary_guid is required", + ), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + name: str, qualified_name: str, glossary_guid: str, message: str +): + """Test that updater raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + AtlasGlossaryCategory.updater( + qualified_name=qualified_name, name=name, glossary_guid=glossary_guid + ) + + +def test_updater(): + """Test that updater creates a GlossaryCategory instance for modification.""" + sut = AtlasGlossaryCategory.updater( + qualified_name=GLOSSARY_CATEGORY_QUALIFIED_NAME, + name=GLOSSARY_CATEGORY_NAME, + glossary_guid=GLOSSARY_GUID, + ) + + assert sut.name == GLOSSARY_CATEGORY_NAME + assert sut.qualified_name == GLOSSARY_CATEGORY_QUALIFIED_NAME + assert sut.anchor.guid == GLOSSARY_GUID + + +def test_updater_parent_category_removed(): + """Test that updater creates category without parent_category when not specified.""" + category = AtlasGlossaryCategory.updater( + qualified_name=GLOSSARY_CATEGORY_QUALIFIED_NAME, + name=GLOSSARY_CATEGORY_NAME, + glossary_guid=GLOSSARY_GUID, + ) + + assert category.parent_category is UNSET + assert category.anchor.guid == GLOSSARY_GUID + assert category.name == GLOSSARY_CATEGORY_NAME + assert category.qualified_name == GLOSSARY_CATEGORY_QUALIFIED_NAME + + +def test_trim_to_required(): + """Test that trim_to_required returns a GlossaryCategory with only required fields.""" + sut = AtlasGlossaryCategory.updater( + qualified_name=GLOSSARY_CATEGORY_QUALIFIED_NAME, + name=GLOSSARY_CATEGORY_NAME, + glossary_guid=GLOSSARY_GUID, + ).trim_to_required() + + assert sut.name == GLOSSARY_CATEGORY_NAME + assert sut.qualified_name == GLOSSARY_CATEGORY_QUALIFIED_NAME + assert sut.anchor.guid == GLOSSARY_GUID + + +@pytest.mark.parametrize( + "anchor", + [(None), (AtlasGlossary())], +) +def test_trim_to_required_raises_value_error_when_anchor_is_invalid(anchor): + """Test that trim_to_required raises ValueError when anchor or anchor.guid is not available.""" + sut = AtlasGlossaryCategory.updater( + qualified_name=GLOSSARY_CATEGORY_QUALIFIED_NAME, + name=GLOSSARY_CATEGORY_NAME, + glossary_guid=GLOSSARY_GUID, + ) + sut.anchor = anchor + + with pytest.raises(ValueError, match="anchor.guid must be available"): + sut.trim_to_required() + + +def test_basic_construction(): + """Test basic GlossaryCategory construction with minimal parameters.""" + category = AtlasGlossaryCategory( + name=GLOSSARY_CATEGORY_NAME, qualified_name=GLOSSARY_CATEGORY_QUALIFIED_NAME + ) + + assert category.name == GLOSSARY_CATEGORY_NAME + assert category.qualified_name == GLOSSARY_CATEGORY_QUALIFIED_NAME + assert category.type_name == "AtlasGlossaryCategory" + + +def test_unset_fields(): + """Test that optional fields default to UNSET.""" + category = AtlasGlossaryCategory( + name=GLOSSARY_CATEGORY_NAME, qualified_name=GLOSSARY_CATEGORY_QUALIFIED_NAME + ) + + assert category.short_description is UNSET + assert category.long_description is UNSET + assert category.additional_attributes is UNSET + assert category.category_type is UNSET + + +def test_optional_fields(): + """Test setting optional fields on GlossaryCategory.""" + category = AtlasGlossaryCategory( + name=GLOSSARY_CATEGORY_NAME, + qualified_name=GLOSSARY_CATEGORY_QUALIFIED_NAME, + short_description="Short desc", + category_type="BUSINESS", + ) + + assert category.short_description == "Short desc" + assert category.category_type == "BUSINESS" + + +def test_none_vs_unset(): + """Test the distinction between None and UNSET values.""" + category = AtlasGlossaryCategory( + name=GLOSSARY_CATEGORY_NAME, qualified_name=GLOSSARY_CATEGORY_QUALIFIED_NAME + ) + + assert category.short_description is UNSET + category.short_description = None + assert category.short_description is None + assert category.short_description is not UNSET + + +def test_serialization_to_json_nested(serde): + """Test serialization to nested JSON format (API format).""" + category = AtlasGlossaryCategory.creator(name=GLOSSARY_CATEGORY_NAME, anchor=ANCHOR) + + json_str = category.to_json(nested=True, serde=serde) + data = json.loads(json_str) + + assert data["typeName"] == "AtlasGlossaryCategory" + assert "attributes" in data + assert data["attributes"]["name"] == GLOSSARY_CATEGORY_NAME + + +def test_serialization_to_json_flat(serde): + """Test serialization to flat JSON format.""" + category = AtlasGlossaryCategory.creator(name=GLOSSARY_CATEGORY_NAME, anchor=ANCHOR) + + json_str = category.to_json(nested=False, serde=serde) + + assert json_str + assert GLOSSARY_CATEGORY_NAME in json_str + + +def test_deserialization_from_json(serde): + """Test deserialization from nested JSON format.""" + original = AtlasGlossaryCategory.creator(name=GLOSSARY_CATEGORY_NAME, anchor=ANCHOR) + json_str = original.to_json(nested=True, serde=serde) + + category = AtlasGlossaryCategory.from_json(json_str, serde=serde) + + assert category.name == GLOSSARY_CATEGORY_NAME + assert category.type_name == "AtlasGlossaryCategory" + + +def test_round_trip_serialization(serde): + """Test that serialization and deserialization preserve all data.""" + original = AtlasGlossaryCategory.creator(name=GLOSSARY_CATEGORY_NAME, anchor=ANCHOR) + original.short_description = "Test description" + + json_str = original.to_json(nested=True, serde=serde) + restored = AtlasGlossaryCategory.from_json(json_str, serde=serde) + + assert restored.name == original.name + assert restored.qualified_name == original.qualified_name + assert restored.short_description == original.short_description + + +def test_with_custom_serde(): + """Test that a custom Serde instance can be used for serialization.""" + custom_serde = Serde() + category = AtlasGlossaryCategory.creator(name=GLOSSARY_CATEGORY_NAME, anchor=ANCHOR) + + json_str = category.to_json(nested=True, serde=custom_serde) + restored = AtlasGlossaryCategory.from_json(json_str, serde=custom_serde) + + assert restored.name == category.name + assert restored.qualified_name == category.qualified_name + + +def test_type_name_defaults(): + """Test that type_name defaults to 'AtlasGlossaryCategory'.""" + category = AtlasGlossaryCategory( + name=GLOSSARY_CATEGORY_NAME, qualified_name=GLOSSARY_CATEGORY_QUALIFIED_NAME + ) + assert category.type_name == "AtlasGlossaryCategory" + + +def test_creator_with_guid(): + """Test that creator initializes a temporary GUID for new assets.""" + category = AtlasGlossaryCategory.creator(name=GLOSSARY_CATEGORY_NAME, anchor=ANCHOR) + + assert category.guid is not UNSET + assert category.guid is not None + assert isinstance(category.guid, str) + assert category.guid.startswith("-") + + +def test_relationship_fields(): + """Test setting relationship fields on GlossaryCategory.""" + from pyatlan_v9.model.assets.gtc_related import RelatedAtlasGlossary + + category = AtlasGlossaryCategory( + name=GLOSSARY_CATEGORY_NAME, + qualified_name=GLOSSARY_CATEGORY_QUALIFIED_NAME, + anchor=RelatedAtlasGlossary(guid=GLOSSARY_GUID), + ) + + assert category.anchor is not None + assert category.anchor.guid == GLOSSARY_GUID diff --git a/tests_v9/unit/model/glossary_term_test.py b/tests_v9/unit/model/glossary_term_test.py new file mode 100644 index 000000000..73c7a2057 --- /dev/null +++ b/tests_v9/unit/model/glossary_term_test.py @@ -0,0 +1,359 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for AtlasGlossaryTerm model in pyatlan_v9.""" + +import json + +import pytest +from msgspec import UNSET + +from pyatlan_v9.model import AtlasGlossary, AtlasGlossaryTerm +from pyatlan_v9.model.serde import Serde +from tests_v9.unit.model.constants import ( + GLOSSARY_NAME, + GLOSSARY_QUALIFIED_NAME, + GLOSSARY_TERM_NAME, + GLOSSARY_TERM_QUALIFIED_NAME, +) + +ANCHOR = AtlasGlossary.updater( + qualified_name=GLOSSARY_QUALIFIED_NAME, name=GLOSSARY_NAME +) +GLOSSARY_GUID = "123" + + +@pytest.mark.parametrize( + "name, anchor, glossary_qualified_name, glossary_guid, categories, message", + [ + ( + None, + ANCHOR, + GLOSSARY_QUALIFIED_NAME, + GLOSSARY_GUID, + None, + "name is required", + ), + ( + GLOSSARY_TERM_NAME, + ANCHOR, + GLOSSARY_QUALIFIED_NAME, + GLOSSARY_GUID, + None, + "Only one of the following parameters are allowed: anchor, glossary_qualified_name, glossary_guid", + ), + ( + GLOSSARY_TERM_NAME, + ANCHOR, + GLOSSARY_QUALIFIED_NAME, + None, + None, + "Only one of the following parameters are allowed: anchor, glossary_qualified_name", + ), + ( + GLOSSARY_TERM_NAME, + ANCHOR, + None, + GLOSSARY_GUID, + None, + "Only one of the following parameters are allowed: anchor, glossary_guid", + ), + ( + GLOSSARY_TERM_NAME, + None, + GLOSSARY_QUALIFIED_NAME, + GLOSSARY_GUID, + None, + "Only one of the following parameters are allowed: glossary_qualified_name, glossary_guid", + ), + ( + GLOSSARY_TERM_NAME, + None, + None, + None, + None, + "One of the following parameters are required: anchor, glossary_qualified_name, glossary_guid", + ), + ], +) +def test_creator_with_missing_or_invalid_parameters_raises_value_error( + name: str, + anchor: AtlasGlossary, + glossary_qualified_name: str, + glossary_guid: str, + categories: list, + message: str, +): + """Test that creator raises ValueError when parameters are missing or mutually exclusive ones are provided.""" + with pytest.raises(ValueError, match=message): + AtlasGlossaryTerm.creator( + name=name, + anchor=anchor, + glossary_qualified_name=glossary_qualified_name, + glossary_guid=glossary_guid, + categories=categories, + ) + + +@pytest.mark.parametrize( + "anchor, glossary_qualified_name, glossary_guid, categories", + [ + (ANCHOR, None, None, None), + (None, GLOSSARY_QUALIFIED_NAME, None, None), + (None, None, GLOSSARY_GUID, None), + ], +) +def test_creator( + anchor: AtlasGlossary, + glossary_qualified_name: str, + glossary_guid: str, + categories: list, +): + """Test that creator properly initializes a GlossaryTerm with different glossary identifier options.""" + sut = AtlasGlossaryTerm.creator( + name=GLOSSARY_TERM_NAME, + anchor=anchor, + glossary_qualified_name=glossary_qualified_name, + glossary_guid=glossary_guid, + categories=categories, + ) + + assert sut.name == GLOSSARY_TERM_NAME + assert sut.qualified_name + assert sut.categories == categories + + # Verify anchor is set correctly based on which parameter was provided + if anchor: + assert sut.anchor is not None + assert ( + sut.anchor.guid == anchor.guid + or sut.anchor.unique_attributes.get("qualifiedName") + == anchor.qualified_name + ) + elif glossary_qualified_name: + assert sut.anchor is not None + assert sut.anchor.unique_attributes["qualifiedName"] == glossary_qualified_name + elif glossary_guid: + assert sut.anchor is not None + assert sut.anchor.guid == glossary_guid + + +@pytest.mark.parametrize( + "name, qualified_name, glossary_guid, message", + [ + (None, GLOSSARY_TERM_QUALIFIED_NAME, GLOSSARY_GUID, "name is required"), + (GLOSSARY_TERM_NAME, None, GLOSSARY_GUID, "qualified_name is required"), + ( + GLOSSARY_TERM_NAME, + GLOSSARY_TERM_QUALIFIED_NAME, + None, + "glossary_guid is required", + ), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + name: str, qualified_name: str, glossary_guid: str, message: str +): + """Test that updater raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + AtlasGlossaryTerm.updater( + qualified_name=qualified_name, name=name, glossary_guid=glossary_guid + ) + + +def test_updater(): + """Test that updater creates a GlossaryTerm instance for modification.""" + sut = AtlasGlossaryTerm.updater( + qualified_name=GLOSSARY_TERM_QUALIFIED_NAME, + name=GLOSSARY_TERM_NAME, + glossary_guid=GLOSSARY_GUID, + ) + + assert sut.name == GLOSSARY_TERM_NAME + assert sut.qualified_name == GLOSSARY_TERM_QUALIFIED_NAME + assert sut.anchor.guid == GLOSSARY_GUID + + +def test_trim_to_required(): + """Test that trim_to_required returns a GlossaryTerm with only required fields.""" + sut = AtlasGlossaryTerm.updater( + qualified_name=GLOSSARY_TERM_QUALIFIED_NAME, + name=GLOSSARY_TERM_NAME, + glossary_guid=GLOSSARY_GUID, + ).trim_to_required() + + assert sut.name == GLOSSARY_TERM_NAME + assert sut.qualified_name == GLOSSARY_TERM_QUALIFIED_NAME + assert sut.anchor.guid == GLOSSARY_GUID + + +@pytest.mark.parametrize( + "anchor", + [(None), (AtlasGlossary())], +) +def test_trim_to_required_raises_value_error_when_anchor_is_invalid(anchor): + """Test that trim_to_required raises ValueError when anchor or anchor.guid is not available.""" + sut = AtlasGlossaryTerm.updater( + qualified_name=GLOSSARY_TERM_QUALIFIED_NAME, + name=GLOSSARY_TERM_NAME, + glossary_guid=GLOSSARY_GUID, + ) + sut.anchor = anchor + + with pytest.raises(ValueError, match="anchor.guid must be available"): + sut.trim_to_required() + + +def test_basic_construction(): + """Test basic GlossaryTerm construction with minimal parameters.""" + term = AtlasGlossaryTerm( + name=GLOSSARY_TERM_NAME, qualified_name=GLOSSARY_TERM_QUALIFIED_NAME + ) + + assert term.name == GLOSSARY_TERM_NAME + assert term.qualified_name == GLOSSARY_TERM_QUALIFIED_NAME + assert term.type_name == "AtlasGlossaryTerm" + + +def test_unset_fields(): + """Test that optional fields default to UNSET.""" + term = AtlasGlossaryTerm( + name=GLOSSARY_TERM_NAME, qualified_name=GLOSSARY_TERM_QUALIFIED_NAME + ) + + assert term.short_description is UNSET + assert term.long_description is UNSET + assert term.examples is UNSET + assert term.abbreviation is UNSET + assert term.usage is UNSET + + +def test_optional_fields(): + """Test setting optional fields on GlossaryTerm.""" + term = AtlasGlossaryTerm( + name=GLOSSARY_TERM_NAME, + qualified_name=GLOSSARY_TERM_QUALIFIED_NAME, + short_description="Short desc", + abbreviation="MT", + usage="Test usage", + ) + + assert term.short_description == "Short desc" + assert term.abbreviation == "MT" + assert term.usage == "Test usage" + + +def test_none_vs_unset(): + """Test the distinction between None and UNSET values.""" + term = AtlasGlossaryTerm( + name=GLOSSARY_TERM_NAME, qualified_name=GLOSSARY_TERM_QUALIFIED_NAME + ) + + assert term.abbreviation is UNSET + term.abbreviation = None + assert term.abbreviation is None + assert term.abbreviation is not UNSET + + +def test_serialization_to_json_nested(serde): + """Test serialization to nested JSON format (API format).""" + term = AtlasGlossaryTerm.creator( + name=GLOSSARY_TERM_NAME, glossary_guid=GLOSSARY_GUID + ) + + json_str = term.to_json(nested=True, serde=serde) + data = json.loads(json_str) + + assert data["typeName"] == "AtlasGlossaryTerm" + assert "attributes" in data + assert data["attributes"]["name"] == GLOSSARY_TERM_NAME + + +def test_serialization_to_json_flat(serde): + """Test serialization to flat JSON format.""" + term = AtlasGlossaryTerm.creator( + name=GLOSSARY_TERM_NAME, glossary_guid=GLOSSARY_GUID + ) + + json_str = term.to_json(nested=False, serde=serde) + + assert json_str + assert GLOSSARY_TERM_NAME in json_str + + +def test_deserialization_from_json(serde): + """Test deserialization from nested JSON format.""" + original = AtlasGlossaryTerm.creator( + name=GLOSSARY_TERM_NAME, glossary_guid=GLOSSARY_GUID + ) + json_str = original.to_json(nested=True, serde=serde) + + term = AtlasGlossaryTerm.from_json(json_str, serde=serde) + + assert term.name == GLOSSARY_TERM_NAME + assert term.type_name == "AtlasGlossaryTerm" + + +def test_round_trip_serialization(serde): + """Test that serialization and deserialization preserve all data.""" + original = AtlasGlossaryTerm.creator( + name=GLOSSARY_TERM_NAME, glossary_guid=GLOSSARY_GUID + ) + original.abbreviation = "MT" + original.usage = "Test" + + json_str = original.to_json(nested=True, serde=serde) + restored = AtlasGlossaryTerm.from_json(json_str, serde=serde) + + assert restored.name == original.name + assert restored.qualified_name == original.qualified_name + assert restored.abbreviation == original.abbreviation + assert restored.usage == original.usage + + +def test_with_custom_serde(): + """Test that a custom Serde instance can be used for serialization.""" + custom_serde = Serde() + term = AtlasGlossaryTerm.creator( + name=GLOSSARY_TERM_NAME, glossary_guid=GLOSSARY_GUID + ) + + json_str = term.to_json(nested=True, serde=custom_serde) + restored = AtlasGlossaryTerm.from_json(json_str, serde=custom_serde) + + assert restored.name == term.name + assert restored.qualified_name == term.qualified_name + + +def test_type_name_defaults(): + """Test that type_name defaults to 'AtlasGlossaryTerm'.""" + term = AtlasGlossaryTerm( + name=GLOSSARY_TERM_NAME, qualified_name=GLOSSARY_TERM_QUALIFIED_NAME + ) + assert term.type_name == "AtlasGlossaryTerm" + + +def test_creator_with_guid(): + """Test that creator initializes a temporary GUID for new assets.""" + term = AtlasGlossaryTerm.creator( + name=GLOSSARY_TERM_NAME, glossary_guid=GLOSSARY_GUID + ) + + assert term.guid is not UNSET + assert term.guid is not None + assert isinstance(term.guid, str) + assert term.guid.startswith("-") + + +def test_relationship_fields(): + """Test setting relationship fields on GlossaryTerm.""" + from pyatlan_v9.model.assets.gtc_related import RelatedAtlasGlossary + + term = AtlasGlossaryTerm( + name=GLOSSARY_TERM_NAME, + qualified_name=GLOSSARY_TERM_QUALIFIED_NAME, + anchor=RelatedAtlasGlossary(guid=GLOSSARY_GUID), + ) + + assert term.anchor is not None + assert term.anchor.guid == GLOSSARY_GUID diff --git a/tests_v9/unit/model/glossary_test.py b/tests_v9/unit/model/glossary_test.py new file mode 100644 index 000000000..e649f2948 --- /dev/null +++ b/tests_v9/unit/model/glossary_test.py @@ -0,0 +1,235 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for AtlasGlossary model in pyatlan_v9.""" + +import json + +import pytest +from msgspec import UNSET + +from pyatlan_v9.model import AtlasGlossary +from pyatlan_v9.model.serde import Serde +from tests_v9.unit.model.constants import GLOSSARY_NAME, GLOSSARY_QUALIFIED_NAME + + +def test_create_with_missing_parameters_raise_value_error(): + """Test that creator raises ValueError when name parameter is missing.""" + with pytest.raises(ValueError, match="name is required"): + AtlasGlossary.create(name=None) + + +def test_create(): + """Test that creator properly initializes a Glossary with auto-generated qualified_name.""" + sut = AtlasGlossary.create(name=GLOSSARY_NAME) + + assert sut.name == GLOSSARY_NAME + assert sut.qualified_name + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, "rJCHYGhPokx9eeXZnqt8Y", "qualified_name is required"), + ("MyGlossary", None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test that updater raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + AtlasGlossary.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test that updater creates a Glossary instance for modification.""" + sut = AtlasGlossary.updater( + qualified_name=GLOSSARY_QUALIFIED_NAME, name=GLOSSARY_NAME + ) + + assert sut.qualified_name == GLOSSARY_QUALIFIED_NAME + assert sut.name == GLOSSARY_NAME + + +def test_trim_to_required(): + """Test that trim_to_required returns a Glossary with only required fields.""" + sut = AtlasGlossary.updater( + qualified_name=GLOSSARY_QUALIFIED_NAME, name=GLOSSARY_NAME + ).trim_to_required() + + assert sut.qualified_name == GLOSSARY_QUALIFIED_NAME + assert sut.name == GLOSSARY_NAME + + +def test_basic_construction(): + """Test basic Glossary construction with minimal parameters.""" + glossary = AtlasGlossary(name=GLOSSARY_NAME, qualified_name=GLOSSARY_QUALIFIED_NAME) + + assert glossary.name == GLOSSARY_NAME + assert glossary.qualified_name == GLOSSARY_QUALIFIED_NAME + assert glossary.type_name == "AtlasGlossary" + + +def test_unset_fields(): + """Test that optional fields default to UNSET.""" + glossary = AtlasGlossary(name=GLOSSARY_NAME, qualified_name=GLOSSARY_QUALIFIED_NAME) + + assert glossary.short_description is UNSET + assert glossary.long_description is UNSET + assert glossary.language is UNSET + assert glossary.usage is UNSET + + +def test_optional_fields(): + """Test setting optional fields on Glossary.""" + glossary = AtlasGlossary( + name=GLOSSARY_NAME, + qualified_name=GLOSSARY_QUALIFIED_NAME, + short_description="Short desc", + long_description="Long desc", + language="en", + usage="Test usage", + ) + + assert glossary.short_description == "Short desc" + assert glossary.long_description == "Long desc" + assert glossary.language == "en" + assert glossary.usage == "Test usage" + + +def test_none_vs_unset(): + """Test the distinction between None and UNSET values.""" + glossary_with_none = AtlasGlossary( + name=GLOSSARY_NAME, + qualified_name=GLOSSARY_QUALIFIED_NAME, + short_description=None, + ) + + glossary_with_unset = AtlasGlossary( + name=GLOSSARY_NAME, qualified_name=GLOSSARY_QUALIFIED_NAME + ) + + assert glossary_with_none.short_description is None + assert glossary_with_unset.short_description is UNSET + assert glossary_with_none.short_description != glossary_with_unset.short_description + + +def test_serialization_to_json_nested(): + """Test serialization to nested JSON format (API format).""" + glossary = AtlasGlossary( + name=GLOSSARY_NAME, + qualified_name=GLOSSARY_QUALIFIED_NAME, + short_description="Test description", + ) + + json_str = glossary.to_json(nested=True) + + assert json_str + assert "typeName" in json_str + assert "attributes" in json_str + assert GLOSSARY_NAME in json_str + + +def test_serialization_to_json_flat(): + """Test serialization to flat JSON format.""" + glossary = AtlasGlossary( + name=GLOSSARY_NAME, + qualified_name=GLOSSARY_QUALIFIED_NAME, + short_description="Test description", + ) + + json_str = glossary.to_json(nested=False) + + assert json_str + assert GLOSSARY_NAME in json_str + + +def test_deserialization_from_json(glossary_json): + """Test deserialization from nested JSON format.""" + json_str = json.dumps(glossary_json) + + glossary = AtlasGlossary.from_json(json_str) + + assert glossary.type_name == "AtlasGlossary" + assert glossary.name == "Metrics Glossary" + assert glossary.qualified_name == "rJCHYGhPokx9eeXZnqt8Y" + assert glossary.guid == "76d54dd6-925b-499b-a455-6f756ae2d522" + + +def test_round_trip_serialization(glossary_json): + """Test that serialization and deserialization preserve all data.""" + json_str = json.dumps(glossary_json) + + glossary = AtlasGlossary.from_json(json_str) + serialized = glossary.to_json(nested=True) + glossary2 = AtlasGlossary.from_json(serialized) + + assert glossary.guid == glossary2.guid + assert glossary.name == glossary2.name + assert glossary.qualified_name == glossary2.qualified_name + assert glossary.type_name == glossary2.type_name + + +def test_relationship_attributes(glossary_json): + """Test that relationship attributes (terms, categories) are properly deserialized.""" + json_str = json.dumps(glossary_json) + + glossary = AtlasGlossary.from_json(json_str) + + assert glossary.terms is not UNSET + if glossary.terms: + assert len(glossary.terms) == 1 + assert glossary.terms[0].guid == "9c9a7a04-d738-48e8-b1d3-a491eb2bccf5" + assert glossary.terms[0].type_name == "AtlasGlossaryTerm" + assert glossary.terms[0].display_text == "Active Subscriptions" + + assert glossary.categories is not UNSET + if glossary.categories: + assert len(glossary.categories) == 3 + assert glossary.categories[0].guid == "18140435-50b4-40b9-bdf0-9cd002355c6a" + assert glossary.categories[0].display_text == "Cloud Analytics" + + +def test_with_custom_serde(glossary_json): + """Test that a custom Serde instance can be used for serialization.""" + custom_serde = Serde() + json_str = json.dumps(glossary_json) + + glossary = AtlasGlossary.from_json(json_str, serde=custom_serde) + + assert glossary.name == "Metrics Glossary" + + serialized = glossary.to_json(nested=True, serde=custom_serde) + assert serialized + + +def test_type_name_defaults(): + """Test that type_name defaults to 'AtlasGlossary'.""" + glossary = AtlasGlossary(name=GLOSSARY_NAME, qualified_name=GLOSSARY_QUALIFIED_NAME) + + assert glossary.type_name == "AtlasGlossary" + + +def test_glossary_type_field(): + """Test setting the glossary_type field.""" + glossary = AtlasGlossary( + name=GLOSSARY_NAME, + qualified_name=GLOSSARY_QUALIFIED_NAME, + glossary_type="BUSINESS", + ) + + assert glossary.glossary_type == "BUSINESS" + + +def test_additional_attributes(): + """Test setting additional_attributes dictionary field.""" + attrs = {"key1": "value1", "key2": "value2"} + glossary = AtlasGlossary( + name=GLOSSARY_NAME, + qualified_name=GLOSSARY_QUALIFIED_NAME, + additional_attributes=attrs, + ) + + assert glossary.additional_attributes == attrs + assert glossary.additional_attributes["key1"] == "value1" diff --git a/tests_v9/unit/model/kafka_consumer_group_test.py b/tests_v9/unit/model/kafka_consumer_group_test.py new file mode 100644 index 000000000..0c6b94773 --- /dev/null +++ b/tests_v9/unit/model/kafka_consumer_group_test.py @@ -0,0 +1,139 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for KafkaConsumerGroup model in pyatlan_v9.""" + +import json + +import pytest +from msgspec import UNSET + +from pyatlan_v9.model import KafkaConsumerGroup +from tests_v9.unit.model.constants import ( + KAFKA_CONNECTION_QUALIFIED_NAME, + KAFKA_CONSUMER_GROUP_NAME, + KAFKA_CONSUMER_GROUP_QUALIFIED_NAME, + KAFKA_TOPIC_QUALIFIED_NAMES, +) + + +@pytest.mark.parametrize( + "name, kafka_topic_qualified_names, message", + [ + (None, "kafka/topic", "name is required"), + (KAFKA_CONSUMER_GROUP_NAME, None, "kafka_topic_qualified_names is required"), + ], +) +def test_creator_with_missing_parameters_raises_value_error( + name: str, kafka_topic_qualified_names: str, message: str +): + """Test that creator raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + KafkaConsumerGroup.creator( + name=name, kafka_topic_qualified_names=kafka_topic_qualified_names + ) + + +def test_creator(): + """Test that creator properly initializes a KafkaConsumerGroup with all derived fields.""" + sut = KafkaConsumerGroup.creator( + name=KAFKA_CONSUMER_GROUP_NAME, + kafka_topic_qualified_names=KAFKA_TOPIC_QUALIFIED_NAMES, + ) + + assert sut.name == KAFKA_CONSUMER_GROUP_NAME + assert sut.connector_name == "kafka" + assert sut.kafka_topic_qualified_names == set(KAFKA_TOPIC_QUALIFIED_NAMES) + assert sut.connection_qualified_name == KAFKA_CONNECTION_QUALIFIED_NAME + assert sut.qualified_name == KAFKA_CONSUMER_GROUP_QUALIFIED_NAME + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, KAFKA_CONSUMER_GROUP_NAME, "qualified_name is required"), + (KAFKA_CONSUMER_GROUP_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test that updater raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + KafkaConsumerGroup.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test that updater creates a KafkaConsumerGroup instance for modification.""" + sut = KafkaConsumerGroup.updater( + name=KAFKA_CONSUMER_GROUP_NAME, + qualified_name=KAFKA_CONSUMER_GROUP_QUALIFIED_NAME, + ) + + assert sut.name == KAFKA_CONSUMER_GROUP_NAME + assert sut.qualified_name == KAFKA_CONSUMER_GROUP_QUALIFIED_NAME + + +def test_trim_to_required(): + """Test that trim_to_required returns a KafkaConsumerGroup with only required fields.""" + sut = KafkaConsumerGroup.updater( + name=KAFKA_CONSUMER_GROUP_NAME, + qualified_name=KAFKA_CONSUMER_GROUP_QUALIFIED_NAME, + ).trim_to_required() + + assert sut.name == KAFKA_CONSUMER_GROUP_NAME + assert sut.qualified_name == KAFKA_CONSUMER_GROUP_QUALIFIED_NAME + + +def test_basic_construction(): + """Test basic KafkaConsumerGroup construction with minimal parameters.""" + group = KafkaConsumerGroup( + name=KAFKA_CONSUMER_GROUP_NAME, + qualified_name=KAFKA_CONSUMER_GROUP_QUALIFIED_NAME, + ) + + assert group.name == KAFKA_CONSUMER_GROUP_NAME + assert group.qualified_name == KAFKA_CONSUMER_GROUP_QUALIFIED_NAME + assert group.type_name == "KafkaConsumerGroup" + + +def test_serialization_to_json_nested(serde): + """Test serialization to nested JSON format (API format).""" + group = KafkaConsumerGroup.creator( + name=KAFKA_CONSUMER_GROUP_NAME, + kafka_topic_qualified_names=KAFKA_TOPIC_QUALIFIED_NAMES, + ) + + json_str = group.to_json(nested=True, serde=serde) + data = json.loads(json_str) + + assert data["typeName"] == "KafkaConsumerGroup" + assert "attributes" in data + assert data["attributes"]["name"] == KAFKA_CONSUMER_GROUP_NAME + + +def test_round_trip_serialization(serde): + """Test that serialization and deserialization preserve all data.""" + original = KafkaConsumerGroup.creator( + name=KAFKA_CONSUMER_GROUP_NAME, + kafka_topic_qualified_names=KAFKA_TOPIC_QUALIFIED_NAMES, + ) + + json_str = original.to_json(nested=True, serde=serde) + restored = KafkaConsumerGroup.from_json(json_str, serde=serde) + + assert restored.name == original.name + assert restored.qualified_name == original.qualified_name + + +def test_creator_with_guid(): + """Test that creator initializes a temporary GUID for new assets.""" + group = KafkaConsumerGroup.creator( + name=KAFKA_CONSUMER_GROUP_NAME, + kafka_topic_qualified_names=KAFKA_TOPIC_QUALIFIED_NAMES, + ) + + assert group.guid is not UNSET + assert group.guid is not None + assert isinstance(group.guid, str) + assert group.guid.startswith("-") diff --git a/tests_v9/unit/model/kafka_topic_test.py b/tests_v9/unit/model/kafka_topic_test.py new file mode 100644 index 000000000..3ca198718 --- /dev/null +++ b/tests_v9/unit/model/kafka_topic_test.py @@ -0,0 +1,141 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for KafkaTopic model in pyatlan_v9.""" + +import json + +import pytest +from msgspec import UNSET + +from pyatlan_v9.model import KafkaTopic +from tests_v9.unit.model.constants import ( + KAFKA_CONNECTION_QUALIFIED_NAME, + KAFKA_TOPIC_NAME, + KAFKA_TOPIC_QUALIFIED_NAME, +) + + +@pytest.mark.parametrize( + "name, connection_qualified_name, message", + [ + (None, "connection/name", "name is required"), + (KAFKA_TOPIC_NAME, None, "connection_qualified_name is required"), + ], +) +def test_creator_with_missing_parameters_raises_value_error( + name: str, connection_qualified_name: str, message: str +): + """Test that creator raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + KafkaTopic.creator( + name=name, connection_qualified_name=connection_qualified_name + ) + + +def test_creator(): + """Test that creator properly initializes a KafkaTopic with all derived fields.""" + sut = KafkaTopic.creator( + name=KAFKA_TOPIC_NAME, + connection_qualified_name=KAFKA_CONNECTION_QUALIFIED_NAME, + ) + + assert sut.name == KAFKA_TOPIC_NAME + assert sut.qualified_name == KAFKA_TOPIC_QUALIFIED_NAME + assert sut.connector_name == "kafka" + assert sut.connection_qualified_name == KAFKA_CONNECTION_QUALIFIED_NAME + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, KAFKA_TOPIC_QUALIFIED_NAME, "qualified_name is required"), + (KAFKA_TOPIC_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test that updater raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + KafkaTopic.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test that updater creates a KafkaTopic instance for modification.""" + sut = KafkaTopic.updater( + name=KAFKA_TOPIC_NAME, qualified_name=KAFKA_TOPIC_QUALIFIED_NAME + ) + + assert sut.name == KAFKA_TOPIC_NAME + assert sut.qualified_name == KAFKA_TOPIC_QUALIFIED_NAME + + +def test_trim_to_required(): + """Test that trim_to_required returns a KafkaTopic with only required fields.""" + sut = KafkaTopic.updater( + name=KAFKA_TOPIC_NAME, + qualified_name=KAFKA_CONNECTION_QUALIFIED_NAME, + ).trim_to_required() + + assert sut.name == KAFKA_TOPIC_NAME + assert sut.qualified_name == KAFKA_CONNECTION_QUALIFIED_NAME + + +def test_basic_construction(): + """Test basic KafkaTopic construction with minimal parameters.""" + topic = KafkaTopic(name=KAFKA_TOPIC_NAME, qualified_name=KAFKA_TOPIC_QUALIFIED_NAME) + + assert topic.name == KAFKA_TOPIC_NAME + assert topic.qualified_name == KAFKA_TOPIC_QUALIFIED_NAME + assert topic.type_name == "KafkaTopic" + + +def test_unset_fields(): + """Test that optional fields default to UNSET.""" + topic = KafkaTopic(name=KAFKA_TOPIC_NAME, qualified_name=KAFKA_TOPIC_QUALIFIED_NAME) + + assert topic.kafka_topic_compression_type is UNSET + assert topic.kafka_topic_replication_factor is UNSET + + +def test_serialization_to_json_nested(serde): + """Test serialization to nested JSON format (API format).""" + topic = KafkaTopic.creator( + name=KAFKA_TOPIC_NAME, + connection_qualified_name=KAFKA_CONNECTION_QUALIFIED_NAME, + ) + + json_str = topic.to_json(nested=True, serde=serde) + data = json.loads(json_str) + + assert data["typeName"] == "KafkaTopic" + assert "attributes" in data + assert data["attributes"]["name"] == KAFKA_TOPIC_NAME + + +def test_round_trip_serialization(serde): + """Test that serialization and deserialization preserve all data.""" + original = KafkaTopic.creator( + name=KAFKA_TOPIC_NAME, + connection_qualified_name=KAFKA_CONNECTION_QUALIFIED_NAME, + ) + + json_str = original.to_json(nested=True, serde=serde) + restored = KafkaTopic.from_json(json_str, serde=serde) + + assert restored.name == original.name + assert restored.qualified_name == original.qualified_name + + +def test_creator_with_guid(): + """Test that creator initializes a temporary GUID for new assets.""" + topic = KafkaTopic.creator( + name=KAFKA_TOPIC_NAME, + connection_qualified_name=KAFKA_CONNECTION_QUALIFIED_NAME, + ) + + assert topic.guid is not UNSET + assert topic.guid is not None + assert isinstance(topic.guid, str) + assert topic.guid.startswith("-") diff --git a/tests_v9/unit/model/materialised_view_test.py b/tests_v9/unit/model/materialised_view_test.py new file mode 100644 index 000000000..6862f3ddd --- /dev/null +++ b/tests_v9/unit/model/materialised_view_test.py @@ -0,0 +1,291 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for MaterialisedView model in pyatlan_v9.""" + +import json + +import pytest +from msgspec import UNSET + +from pyatlan_v9.model import MaterialisedView +from pyatlan_v9.model.serde import Serde +from tests_v9.unit.model.constants import ( + CONNECTION_QUALIFIED_NAME, + CONNECTOR_TYPE, + DATABASE_NAME, + DATABASE_QUALIFIED_NAME, + SCHEMA_NAME, + SCHEMA_QUALIFIED_NAME, + TABLE_QUALIFIED_NAME, + VIEW_COLUMN_QUALIFIED_NAME, + VIEW_NAME, + VIEW_QUALIFIED_NAME, +) + + +@pytest.mark.parametrize( + "name, schema_qualified_name, message", + [ + (None, SCHEMA_QUALIFIED_NAME, "name is required"), + (VIEW_NAME, None, "schema_qualified_name is required"), + (VIEW_NAME, CONNECTION_QUALIFIED_NAME, "Invalid schema_qualified_name"), + (VIEW_NAME, DATABASE_QUALIFIED_NAME, "Invalid schema_qualified_name"), + (VIEW_NAME, TABLE_QUALIFIED_NAME, "Invalid schema_qualified_name"), + (VIEW_NAME, VIEW_COLUMN_QUALIFIED_NAME, "Invalid schema_qualified_name"), + ], +) +def test_creator_with_missing_or_invalid_parameters_raises_value_error( + name: str, schema_qualified_name: str, message: str +): + """Test that creator raises ValueError when required parameters are missing or invalid.""" + with pytest.raises(ValueError, match=message): + MaterialisedView.creator(name=name, schema_qualified_name=schema_qualified_name) + + +def test_creator(): + """Test that creator properly initializes a MaterialisedView with all derived fields.""" + sut = MaterialisedView.creator( + name=VIEW_NAME, schema_qualified_name=SCHEMA_QUALIFIED_NAME + ) + + assert sut.name == VIEW_NAME + assert sut.database_name == DATABASE_NAME + assert sut.connection_qualified_name == CONNECTION_QUALIFIED_NAME + assert sut.database_qualified_name == DATABASE_QUALIFIED_NAME + assert sut.qualified_name == VIEW_QUALIFIED_NAME + assert sut.schema_qualified_name == SCHEMA_QUALIFIED_NAME + assert sut.schema_name == SCHEMA_NAME + assert sut.connector_name == CONNECTOR_TYPE + assert sut.atlan_schema.unique_attributes["qualifiedName"] == SCHEMA_QUALIFIED_NAME + + +def test_overload_creator(): + """Test creator with all optional parameters provided.""" + sut = MaterialisedView.creator( + name=VIEW_NAME, + schema_qualified_name=SCHEMA_QUALIFIED_NAME, + schema_name=SCHEMA_NAME, + database_name=DATABASE_NAME, + connection_qualified_name=CONNECTION_QUALIFIED_NAME, + ) + + assert sut.name == VIEW_NAME + assert sut.database_name == DATABASE_NAME + assert sut.connection_qualified_name == CONNECTION_QUALIFIED_NAME + assert sut.database_qualified_name == DATABASE_QUALIFIED_NAME + assert sut.qualified_name == VIEW_QUALIFIED_NAME + assert sut.schema_qualified_name == SCHEMA_QUALIFIED_NAME + assert sut.schema_name == SCHEMA_NAME + assert sut.connector_name == CONNECTOR_TYPE + assert sut.atlan_schema.unique_attributes["qualifiedName"] == SCHEMA_QUALIFIED_NAME + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, VIEW_QUALIFIED_NAME, "qualified_name is required"), + (VIEW_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test that updater raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + MaterialisedView.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test that updater creates a MaterialisedView instance for modification.""" + sut = MaterialisedView.updater(qualified_name=VIEW_QUALIFIED_NAME, name=VIEW_NAME) + + assert sut.qualified_name == VIEW_QUALIFIED_NAME + assert sut.name == VIEW_NAME + + +def test_trim_to_required(): + """Test that trim_to_required returns a MaterialisedView with only required fields.""" + sut = MaterialisedView.updater( + qualified_name=VIEW_QUALIFIED_NAME, name=VIEW_NAME + ).trim_to_required() + + assert sut.qualified_name == VIEW_QUALIFIED_NAME + assert sut.name == VIEW_NAME + + +def test_basic_construction(): + """Test basic MaterialisedView construction with minimal parameters.""" + mv = MaterialisedView(name=VIEW_NAME, qualified_name=VIEW_QUALIFIED_NAME) + + assert mv.name == VIEW_NAME + assert mv.qualified_name == VIEW_QUALIFIED_NAME + assert mv.type_name == "MaterialisedView" + + +def test_unset_fields(): + """Test that optional fields default to UNSET.""" + mv = MaterialisedView(name=VIEW_NAME, qualified_name=VIEW_QUALIFIED_NAME) + + assert mv.column_count is UNSET + assert mv.row_count is UNSET + assert mv.size_bytes is UNSET + assert mv.is_temporary is UNSET + assert mv.definition is UNSET + assert mv.refresh_mode is UNSET + assert mv.refresh_method is UNSET + assert mv.staleness is UNSET + + +def test_optional_fields(): + """Test setting optional fields on MaterialisedView.""" + mv = MaterialisedView( + name=VIEW_NAME, + qualified_name=VIEW_QUALIFIED_NAME, + column_count=5, + row_count=100, + size_bytes=1024, + refresh_mode="COMPLETE", + ) + + assert mv.column_count == 5 + assert mv.row_count == 100 + assert mv.size_bytes == 1024 + assert mv.refresh_mode == "COMPLETE" + + +def test_none_vs_unset(): + """Test the distinction between None and UNSET values.""" + mv = MaterialisedView(name=VIEW_NAME, qualified_name=VIEW_QUALIFIED_NAME) + + assert mv.alias is UNSET + mv.alias = None + assert mv.alias is None + assert mv.alias is not UNSET + + +def test_serialization_to_json_nested(serde): + """Test serialization to nested JSON format (API format).""" + mv = MaterialisedView.creator( + name=VIEW_NAME, schema_qualified_name=SCHEMA_QUALIFIED_NAME + ) + + json_str = mv.to_json(nested=True, serde=serde) + data = json.loads(json_str) + + assert data["typeName"] == "MaterialisedView" + assert "attributes" in data + assert data["attributes"]["name"] == VIEW_NAME + assert data["attributes"]["qualifiedName"] == VIEW_QUALIFIED_NAME + + +def test_serialization_to_json_flat(serde): + """Test serialization to flat JSON format.""" + mv = MaterialisedView.creator( + name=VIEW_NAME, schema_qualified_name=SCHEMA_QUALIFIED_NAME + ) + + json_str = mv.to_json(nested=False, serde=serde) + + assert json_str + assert VIEW_NAME in json_str + assert VIEW_QUALIFIED_NAME in json_str + + +def test_deserialization_from_json(serde): + """Test deserialization from nested JSON format.""" + original = MaterialisedView.creator( + name=VIEW_NAME, schema_qualified_name=SCHEMA_QUALIFIED_NAME + ) + json_str = original.to_json(nested=True, serde=serde) + + mv = MaterialisedView.from_json(json_str, serde=serde) + + assert mv.name == VIEW_NAME + assert mv.qualified_name == VIEW_QUALIFIED_NAME + assert mv.type_name == "MaterialisedView" + + +def test_round_trip_serialization(serde): + """Test that serialization and deserialization preserve all data.""" + original = MaterialisedView.creator( + name=VIEW_NAME, schema_qualified_name=SCHEMA_QUALIFIED_NAME + ) + original.column_count = 5 + original.row_count = 100 + original.refresh_mode = "COMPLETE" + + json_str = original.to_json(nested=True, serde=serde) + restored = MaterialisedView.from_json(json_str, serde=serde) + + assert restored.name == original.name + assert restored.qualified_name == original.qualified_name + assert restored.column_count == original.column_count + assert restored.row_count == original.row_count + assert restored.refresh_mode == original.refresh_mode + + +def test_with_custom_serde(): + """Test that a custom Serde instance can be used for serialization.""" + custom_serde = Serde() + mv = MaterialisedView.creator( + name=VIEW_NAME, schema_qualified_name=SCHEMA_QUALIFIED_NAME + ) + + json_str = mv.to_json(nested=True, serde=custom_serde) + restored = MaterialisedView.from_json(json_str, serde=custom_serde) + + assert restored.name == mv.name + assert restored.qualified_name == mv.qualified_name + + +def test_type_name_defaults(): + """Test that type_name defaults to 'MaterialisedView'.""" + mv = MaterialisedView(name=VIEW_NAME, qualified_name=VIEW_QUALIFIED_NAME) + assert mv.type_name == "MaterialisedView" + + +def test_creator_with_guid(): + """Test that creator initializes a temporary GUID for new assets.""" + mv = MaterialisedView.creator( + name=VIEW_NAME, schema_qualified_name=SCHEMA_QUALIFIED_NAME + ) + + assert mv.guid is not UNSET + assert mv.guid is not None + assert isinstance(mv.guid, str) + assert mv.guid.startswith("-") + + +def test_sql_fields(): + """Test setting SQL-specific fields (database, schema names).""" + mv = MaterialisedView( + name=VIEW_NAME, + qualified_name=VIEW_QUALIFIED_NAME, + database_name=DATABASE_NAME, + database_qualified_name=DATABASE_QUALIFIED_NAME, + schema_name=SCHEMA_NAME, + schema_qualified_name=SCHEMA_QUALIFIED_NAME, + ) + + assert mv.database_name == DATABASE_NAME + assert mv.database_qualified_name == DATABASE_QUALIFIED_NAME + assert mv.schema_name == SCHEMA_NAME + assert mv.schema_qualified_name == SCHEMA_QUALIFIED_NAME + + +def test_materialised_view_specific_fields(): + """Test MaterialisedView-specific fields like refresh_mode and staleness.""" + mv = MaterialisedView( + name=VIEW_NAME, + qualified_name=VIEW_QUALIFIED_NAME, + refresh_mode="COMPLETE", + refresh_method="FORCE", + staleness="STALE", + stale_since_date=1686532494000, + ) + + assert mv.refresh_mode == "COMPLETE" + assert mv.refresh_method == "FORCE" + assert mv.staleness == "STALE" + assert mv.stale_since_date == 1686532494000 diff --git a/tests_v9/unit/model/open_lineage/__init__.py b/tests_v9/unit/model/open_lineage/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests_v9/unit/model/open_lineage/open_lineage_test.py b/tests_v9/unit/model/open_lineage/open_lineage_test.py new file mode 100644 index 000000000..1ce274ffe --- /dev/null +++ b/tests_v9/unit/model/open_lineage/open_lineage_test.py @@ -0,0 +1,339 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Unit tests for OpenLineage models using v9 (msgspec) types. + +Ported from tests/unit/model/open_lineage/open_lineage_test.py. +- v9 models are used for construction and serialization tests (test_ol_models). +- Legacy OpenLineageClient and OpenLineageRawEvent are kept for client + interaction tests (the client internally wraps data in legacy Pydantic types). +""" + +import json +from json import load +from pathlib import Path +from unittest.mock import Mock, patch + +import pytest + +from pyatlan.client.common import ApiCaller + +# Legacy models kept for client-interaction tests (client returns legacy types) +from pyatlan.model.open_lineage import OpenLineageEvent as LegacyOpenLineageEvent +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.client.open_lineage import V9OpenLineageClient as OpenLineageClient +from pyatlan_v9.errors import AtlanError, InvalidRequestError +from pyatlan_v9.model.enums import AtlanConnectorType, OpenLineageEventType +from pyatlan_v9.model.fluent_tasks import FluentTasks + +# v9 OpenLineage models (msgspec) +from pyatlan_v9.model.open_lineage import ( + OpenLineageEvent, + OpenLineageJob, + OpenLineageRawEvent, + OpenLineageRun, +) + +TEST_DATA_DIR = Path(__file__).parents[4] / "tests" / "unit" / "data" +OL_EVENT_START = str(TEST_DATA_DIR / "open_lineage_requests/event_start.json") +OL_EVENT_COMPLETE = str(TEST_DATA_DIR / "open_lineage_requests/event_complete.json") + +# Raw event test data files +RAW_EVENTS_LIST = str(TEST_DATA_DIR / "open_lineage_requests/raw_events_list.json") +SINGLE_EVENT = str(TEST_DATA_DIR / "open_lineage_requests/single_event.json") +MULTIPLE_EVENTS = str(TEST_DATA_DIR / "open_lineage_requests/multiple_events.json") +MINIMAL_EVENT = str(TEST_DATA_DIR / "open_lineage_requests/minimal_event.json") +EMPTY_EVENTS = str(TEST_DATA_DIR / "open_lineage_requests/empty_events.json") + +PRODUCER = "https://your.orchestrator/unique/id/123" +NAMESPACE = "snowflake://abc123.snowflakecomputing.com" + + +@pytest.fixture(autouse=True) +def set_env(monkeypatch): + monkeypatch.setenv("ATLAN_BASE_URL", "https://test.atlan.com") + monkeypatch.setenv("ATLAN_API_KEY", "test-api-key") + + +def load_json(filename): + with (TEST_DATA_DIR / filename).open() as input_file: + return load(input_file) + + +@pytest.fixture() +def mock_api_caller(): + mock = Mock(spec=ApiCaller) + # Reset any previous side_effect that might interfere + mock._call_api.side_effect = None + mock._call_api.return_value = "Event received" + return mock + + +@pytest.fixture() +def client(): + return AtlanClient() + + +@pytest.fixture() +def mock_event_time(): + with patch("pyatlan_v9.model.open_lineage.event.datetime") as mock_datetime: + mock_datetime_instance = Mock() + mock_datetime_instance.isoformat.return_value = ( + "2024-10-07T10:23:52.239783+00:00" + ) + mock_datetime.now.return_value = mock_datetime_instance + yield mock_datetime + + +@pytest.fixture() +def mock_run_id(): + with patch("pyatlan_v9.model.open_lineage.run.generate_new_uuid") as mock_utils: + mock_utils.return_value = "01826681-bfaf-7b1a-a5ce-f69f645660d9" + yield mock_utils + + +@pytest.fixture() +def mock_session(): + with patch.object(AtlanClient, "_session") as mock_session: + mock_response = Mock() + mock_response.status_code = 401 + mock_response.text = ( + "Unauthorized: url path not configured to receive data, " + "urlPath: /events/openlineage/snowflake/api/v1/lineage" + ) + mock_session.request.return_value = mock_response + yield mock_session + + +@pytest.mark.parametrize( + "test_request, connector_type, expected_exception", + [ + # Invalid request parameter tests + [None, AtlanConnectorType.SPARK, InvalidRequestError], + [123, AtlanConnectorType.SPARK, InvalidRequestError], + [set(), AtlanConnectorType.SPARK, InvalidRequestError], + [object(), AtlanConnectorType.SPARK, InvalidRequestError], + # Invalid connector_type parameter tests + [{"eventType": "START"}, None, InvalidRequestError], + [{"eventType": "START"}, "spark", InvalidRequestError], + [{"eventType": "START"}, 123, InvalidRequestError], + [{"eventType": "START"}, object(), InvalidRequestError], + [{"eventType": "START"}, set(), InvalidRequestError], + ], +) +def test_ol_client_send_raises_validation_error( + test_request, connector_type, expected_exception, mock_api_caller +): + client = OpenLineageClient(client=mock_api_caller) + + with pytest.raises(expected_exception): + client.send(request=test_request, connector_type=connector_type) + + +@pytest.mark.parametrize( + "test_method, test_client", + [["count", [None, 123, "abc"]], ["execute", [None, 123, "abc"]]], +) +def test_ol_invalid_client_raises_invalid_request_error( + test_method, + test_client, +): + client_method = getattr(FluentTasks(), test_method) + for invalid_client in test_client: + with pytest.raises( + InvalidRequestError, match="No Atlan client has been provided." + ): + client_method(client=invalid_client) + + +def test_ol_client_send( + mock_api_caller, +): + mock_api_caller._call_api.return_value = "Event received" + test_event = OpenLineageEvent() + assert ( + OpenLineageClient(client=mock_api_caller).send( + request=test_event, connector_type=AtlanConnectorType.SPARK + ) + is None + ) + + assert mock_api_caller._call_api.call_count == 1 + mock_api_caller.reset_mock() + + +def test_ol_client_send_when_ol_not_configured(client, mock_session): + expected_error = ( + "ATLAN-PYTHON-400-064 Requested OpenLineage " + "connector type 'snowflake' is not configured. " + "Suggestion: You must first run the appropriate " + "marketplace package to configure OpenLineage for " + "this connector before you can send events for it." + ) + with pytest.raises(AtlanError, match=expected_error): + client.open_lineage.send( + request=OpenLineageEvent(), + connector_type=AtlanConnectorType.SNOWFLAKE, + ) + + +def test_ol_models(mock_run_id, mock_event_time): + # Use v9 models for construction + job = OpenLineageJob.creator( + connection_name="ol-spark", job_name="dag_123", producer=PRODUCER + ) + run = OpenLineageRun.creator(job=job) + + id = job.create_input(namespace=NAMESPACE, asset_name="OPS.DEFAULT.RUN_STATS") + od = job.create_output(namespace=NAMESPACE, asset_name="OPS.DEFAULT.FULL_STATS") + od.to_fields = [ + { + "COLUMN": [ + id.from_field(field_name="COLUMN"), + id.from_field(field_name="ONE"), + id.from_field(field_name="TWO"), + ] + }, + { + "ANOTHER": [ + id.from_field(field_name="THREE"), + ] + }, + ] + + start = OpenLineageEvent.creator(run=run, event_type=OpenLineageEventType.START) + start.inputs = [ + id, + job.create_input(namespace=NAMESPACE, asset_name="SOME.OTHER.TBL"), + job.create_input(namespace=NAMESPACE, asset_name="AN.OTHER.TBL"), + ] + start.outputs = [ + od, + job.create_output(namespace=NAMESPACE, asset_name="AN.OTHER.VIEW"), + ] + # Use v9 to_dict() for serialization + assert start.to_dict() == load_json(OL_EVENT_START) + + complete = OpenLineageEvent.creator( + run=run, event_type=OpenLineageEventType.COMPLETE + ) + assert complete.to_dict() == load_json(OL_EVENT_COMPLETE) + + +@pytest.mark.parametrize( + "test_data_file,test_description", + [ + (SINGLE_EVENT, "single_event_dict"), + (MULTIPLE_EVENTS, "multiple_events_list"), + (RAW_EVENTS_LIST, "complex_event_list"), + (MINIMAL_EVENT, "minimal_event_dict"), + ], +) +def test_ol_raw_events_from_json_files( + mock_api_caller, test_data_file, test_description +): + # Load test data from file + test_data = load_json(test_data_file) + + # Test send method with OpenLineageClient (uses v9 types internally) + ol_client = OpenLineageClient(client=mock_api_caller) + ol_client.send(request=test_data, connector_type=AtlanConnectorType.SPARK) + + assert mock_api_caller._call_api.call_count == 1 + assert isinstance( + mock_api_caller._call_api.call_args.kwargs["request_obj"], + OpenLineageRawEvent, + ) + assert mock_api_caller._call_api.call_args.kwargs["request_obj"].data == test_data + mock_api_caller.reset_mock() + + # Test emit_raw classmethod with AtlanClient mock + mock_atlan_client = Mock() + mock_atlan_client.open_lineage = ol_client + + LegacyOpenLineageEvent.emit_raw( + client=mock_atlan_client, + event=test_data, + connector_type=AtlanConnectorType.SPARK, + ) + assert mock_api_caller._call_api.call_count == 1 + assert isinstance( + mock_api_caller._call_api.call_args.kwargs["request_obj"], + OpenLineageRawEvent, + ) + assert mock_api_caller._call_api.call_args.kwargs["request_obj"].data == test_data + mock_api_caller.reset_mock() + + +@pytest.mark.parametrize( + "test_data_file,input_type", + [ + (SINGLE_EVENT, "json_string"), + (MULTIPLE_EVENTS, "json_string"), + (MINIMAL_EVENT, "json_string"), + ], +) +def test_ol_raw_events_from_json_strings(mock_api_caller, test_data_file, input_type): + # Load test data and convert to JSON string + test_data = load_json(test_data_file) + test_json_string = json.dumps(test_data) + + # Test send method with JSON string (client uses v9 types internally) + ol_client = OpenLineageClient(client=mock_api_caller) + ol_client.send(request=test_json_string, connector_type=AtlanConnectorType.SPARK) + + assert mock_api_caller._call_api.call_count == 1 + assert isinstance( + mock_api_caller._call_api.call_args.kwargs["request_obj"], + OpenLineageRawEvent, + ) + assert mock_api_caller._call_api.call_args.kwargs["request_obj"].data == test_data + mock_api_caller.reset_mock() + + +def test_ol_raw_events_edge_cases(mock_api_caller): + ol_client = OpenLineageClient(client=mock_api_caller) + + # Test empty list (client uses v9 types internally) + empty_list = load_json(EMPTY_EVENTS) + ol_client.send(request=empty_list, connector_type=AtlanConnectorType.SPARK) + assert mock_api_caller._call_api.call_count == 1 + assert isinstance( + mock_api_caller._call_api.call_args.kwargs["request_obj"], + OpenLineageRawEvent, + ) + assert mock_api_caller._call_api.call_args.kwargs["request_obj"].data == [] + mock_api_caller.reset_mock() + + # Test custom connector type + test_data = load_json(MINIMAL_EVENT) + ol_client.send(request=test_data, connector_type=AtlanConnectorType.DATABRICKS) + assert mock_api_caller._call_api.call_count == 1 + mock_api_caller.reset_mock() + + +def test_ol_raw_event_model_methods(): + """Test v9 OpenLineageRawEvent construction and parsing methods.""" + # Test from_dict + test_dict = {"eventTime": "2025-01-01T00:00:00Z", "eventType": "START"} + raw_event = OpenLineageRawEvent.from_dict(test_dict) + assert raw_event.data == test_dict + + # Test from_json + test_json = '{"eventTime": "2025-01-01T00:00:00Z", "eventType": "COMPLETE"}' + raw_event = OpenLineageRawEvent.from_json(test_json) + assert raw_event.data == { + "eventTime": "2025-01-01T00:00:00Z", + "eventType": "COMPLETE", + } + + # Test parse_obj with list + test_list = [{"eventType": "START"}, {"eventType": "COMPLETE"}] + raw_event = OpenLineageRawEvent.parse_obj(test_list) + assert raw_event.data == test_list + + # Test parse_raw with complex JSON + complex_json = json.dumps(load_json(MULTIPLE_EVENTS)) + raw_event = OpenLineageRawEvent.parse_raw(complex_json) + assert raw_event.data == load_json(MULTIPLE_EVENTS) diff --git a/tests_v9/unit/model/preset_chart_test.py b/tests_v9/unit/model/preset_chart_test.py new file mode 100644 index 000000000..24f0b4adc --- /dev/null +++ b/tests_v9/unit/model/preset_chart_test.py @@ -0,0 +1,100 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for PresetChart model in pyatlan_v9.""" + +import pytest + +from pyatlan_v9.model import PresetChart +from tests_v9.unit.model.constants import ( + PRESET_CHART_NAME, + PRESET_CHART_QUALIFIED_NAME, + PRESET_CONNECTION_QUALIFIED_NAME, + PRESET_CONNECTOR_TYPE, + PRESET_DASHBOARD_QUALIFIED_NAME, +) + + +@pytest.mark.parametrize( + "name, preset_dashboard_qualified_name, message", + [ + (None, "connection/name", "name is required"), + (PRESET_CHART_NAME, None, "preset_dashboard_qualified_name is required"), + ], +) +def test_creator_with_missing_parameters_raise_value_error( + name: str, preset_dashboard_qualified_name: str, message: str +): + """Test creator raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + PresetChart.creator( + name=name, preset_dashboard_qualified_name=preset_dashboard_qualified_name + ) + + +def test_creator(): + """Test creator builds derived names and connector metadata.""" + sut = PresetChart.creator( + name=PRESET_CHART_NAME, + preset_dashboard_qualified_name=PRESET_DASHBOARD_QUALIFIED_NAME, + ) + + assert sut.name == PRESET_CHART_NAME + assert sut.preset_dashboard_qualified_name == PRESET_DASHBOARD_QUALIFIED_NAME + assert sut.connection_qualified_name == PRESET_CONNECTION_QUALIFIED_NAME + assert ( + sut.qualified_name == f"{PRESET_DASHBOARD_QUALIFIED_NAME}/{PRESET_CHART_NAME}" + ) + assert sut.connector_name == PRESET_CONNECTOR_TYPE + + +def test_overload_creator(): + """Test creator accepts explicit connection_qualified_name override.""" + sut = PresetChart.creator( + name=PRESET_CHART_NAME, + preset_dashboard_qualified_name=PRESET_DASHBOARD_QUALIFIED_NAME, + connection_qualified_name=PRESET_CONNECTION_QUALIFIED_NAME, + ) + + assert sut.name == PRESET_CHART_NAME + assert sut.preset_dashboard_qualified_name == PRESET_DASHBOARD_QUALIFIED_NAME + assert sut.connection_qualified_name == PRESET_CONNECTION_QUALIFIED_NAME + assert ( + sut.qualified_name == f"{PRESET_DASHBOARD_QUALIFIED_NAME}/{PRESET_CHART_NAME}" + ) + assert sut.connector_name == PRESET_CONNECTOR_TYPE + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, PRESET_CHART_QUALIFIED_NAME, "qualified_name is required"), + (PRESET_CHART_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test updater raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + PresetChart.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test updater returns minimal update payload.""" + sut = PresetChart.updater( + qualified_name=PRESET_CHART_QUALIFIED_NAME, name=PRESET_CHART_NAME + ) + + assert sut.qualified_name == PRESET_CHART_QUALIFIED_NAME + assert sut.name == PRESET_CHART_NAME + + +def test_trim_to_required(): + """Test trim_to_required preserves required update fields.""" + sut = PresetChart.updater( + name=PRESET_CHART_NAME, qualified_name=PRESET_CHART_QUALIFIED_NAME + ).trim_to_required() + + assert sut.name == PRESET_CHART_NAME + assert sut.qualified_name == PRESET_CHART_QUALIFIED_NAME diff --git a/tests_v9/unit/model/preset_dashboard_test.py b/tests_v9/unit/model/preset_dashboard_test.py new file mode 100644 index 000000000..a154de0c2 --- /dev/null +++ b/tests_v9/unit/model/preset_dashboard_test.py @@ -0,0 +1,102 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for PresetDashboard model in pyatlan_v9.""" + +import pytest + +from pyatlan_v9.model import PresetDashboard +from tests_v9.unit.model.constants import ( + PRESET_CONNECTION_QUALIFIED_NAME, + PRESET_CONNECTOR_TYPE, + PRESET_DASHBOARD_NAME, + PRESET_DASHBOARD_QUALIFIED_NAME, + PRESET_WORKSPACE_QUALIFIED_NAME, +) + + +@pytest.mark.parametrize( + "name, preset_workspace_qualified_name, message", + [ + (None, "connection/name", "name is required"), + (PRESET_DASHBOARD_NAME, None, "preset_workspace_qualified_name is required"), + ], +) +def test_creator_with_missing_parameters_raise_value_error( + name: str, preset_workspace_qualified_name: str, message: str +): + """Test creator raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + PresetDashboard.creator( + name=name, preset_workspace_qualified_name=preset_workspace_qualified_name + ) + + +def test_creator(): + """Test creator builds derived names and connector metadata.""" + sut = PresetDashboard.creator( + name=PRESET_DASHBOARD_NAME, + preset_workspace_qualified_name=PRESET_WORKSPACE_QUALIFIED_NAME, + ) + + assert sut.name == PRESET_DASHBOARD_NAME + assert sut.preset_workspace_qualified_name == PRESET_WORKSPACE_QUALIFIED_NAME + assert sut.connection_qualified_name == PRESET_CONNECTION_QUALIFIED_NAME + assert ( + sut.qualified_name + == f"{PRESET_WORKSPACE_QUALIFIED_NAME}/{PRESET_DASHBOARD_NAME}" + ) + assert sut.connector_name == PRESET_CONNECTOR_TYPE + + +def test_overload_creator(): + """Test creator accepts explicit connection_qualified_name override.""" + sut = PresetDashboard.creator( + name=PRESET_DASHBOARD_NAME, + preset_workspace_qualified_name=PRESET_WORKSPACE_QUALIFIED_NAME, + connection_qualified_name=PRESET_CONNECTION_QUALIFIED_NAME, + ) + + assert sut.name == PRESET_DASHBOARD_NAME + assert sut.preset_workspace_qualified_name == PRESET_WORKSPACE_QUALIFIED_NAME + assert sut.connection_qualified_name == PRESET_CONNECTION_QUALIFIED_NAME + assert ( + sut.qualified_name + == f"{PRESET_WORKSPACE_QUALIFIED_NAME}/{PRESET_DASHBOARD_NAME}" + ) + assert sut.connector_name == PRESET_CONNECTOR_TYPE + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, PRESET_DASHBOARD_QUALIFIED_NAME, "qualified_name is required"), + (PRESET_DASHBOARD_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test updater raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + PresetDashboard.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test updater returns minimal update payload.""" + sut = PresetDashboard.updater( + qualified_name=PRESET_DASHBOARD_QUALIFIED_NAME, name=PRESET_DASHBOARD_NAME + ) + + assert sut.qualified_name == PRESET_DASHBOARD_QUALIFIED_NAME + assert sut.name == PRESET_DASHBOARD_NAME + + +def test_trim_to_required(): + """Test trim_to_required preserves required update fields.""" + sut = PresetDashboard.updater( + qualified_name=PRESET_DASHBOARD_QUALIFIED_NAME, name=PRESET_DASHBOARD_NAME + ).trim_to_required() + + assert sut.qualified_name == PRESET_DASHBOARD_QUALIFIED_NAME + assert sut.name == PRESET_DASHBOARD_NAME diff --git a/tests_v9/unit/model/preset_dataset_test.py b/tests_v9/unit/model/preset_dataset_test.py new file mode 100644 index 000000000..36e504f2d --- /dev/null +++ b/tests_v9/unit/model/preset_dataset_test.py @@ -0,0 +1,100 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for PresetDataset model in pyatlan_v9.""" + +import pytest + +from pyatlan_v9.model import PresetDataset +from tests_v9.unit.model.constants import ( + PRESET_CONNECTION_QUALIFIED_NAME, + PRESET_CONNECTOR_TYPE, + PRESET_DASHBOARD_QUALIFIED_NAME, + PRESET_DATASET_NAME, + PRESET_DATASET_QUALIFIED_NAME, +) + + +@pytest.mark.parametrize( + "name, preset_dashboard_qualified_name, message", + [ + (None, "connection/name", "name is required"), + (PRESET_DATASET_NAME, None, "preset_dashboard_qualified_name is required"), + ], +) +def test_creator_with_missing_parameters_raise_value_error( + name: str, preset_dashboard_qualified_name: str, message: str +): + """Test creator raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + PresetDataset.creator( + name=name, preset_dashboard_qualified_name=preset_dashboard_qualified_name + ) + + +def test_creator(): + """Test creator builds derived names and connector metadata.""" + sut = PresetDataset.creator( + name=PRESET_DATASET_NAME, + preset_dashboard_qualified_name=PRESET_DASHBOARD_QUALIFIED_NAME, + ) + + assert sut.name == PRESET_DATASET_NAME + assert sut.preset_dashboard_qualified_name == PRESET_DASHBOARD_QUALIFIED_NAME + assert sut.connection_qualified_name == PRESET_CONNECTION_QUALIFIED_NAME + assert ( + sut.qualified_name == f"{PRESET_DASHBOARD_QUALIFIED_NAME}/{PRESET_DATASET_NAME}" + ) + assert sut.connector_name == PRESET_CONNECTOR_TYPE + + +def test_overload_creator(): + """Test creator accepts explicit connection_qualified_name override.""" + sut = PresetDataset.creator( + name=PRESET_DATASET_NAME, + preset_dashboard_qualified_name=PRESET_DASHBOARD_QUALIFIED_NAME, + connection_qualified_name=PRESET_CONNECTION_QUALIFIED_NAME, + ) + + assert sut.name == PRESET_DATASET_NAME + assert sut.preset_dashboard_qualified_name == PRESET_DASHBOARD_QUALIFIED_NAME + assert sut.connection_qualified_name == PRESET_CONNECTION_QUALIFIED_NAME + assert ( + sut.qualified_name == f"{PRESET_DASHBOARD_QUALIFIED_NAME}/{PRESET_DATASET_NAME}" + ) + assert sut.connector_name == PRESET_CONNECTOR_TYPE + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, PRESET_DATASET_QUALIFIED_NAME, "qualified_name is required"), + (PRESET_DATASET_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test updater raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + PresetDataset.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test updater returns minimal update payload.""" + sut = PresetDataset.updater( + qualified_name=PRESET_DATASET_QUALIFIED_NAME, name=PRESET_DATASET_NAME + ) + + assert sut.qualified_name == PRESET_DATASET_QUALIFIED_NAME + assert sut.name == PRESET_DATASET_NAME + + +def test_trim_to_required(): + """Test trim_to_required preserves required update fields.""" + sut = PresetDataset.updater( + name=PRESET_DATASET_NAME, qualified_name=PRESET_DATASET_QUALIFIED_NAME + ).trim_to_required() + + assert sut.name == PRESET_DATASET_NAME + assert sut.qualified_name == PRESET_DATASET_QUALIFIED_NAME diff --git a/tests_v9/unit/model/preset_workspace_test.py b/tests_v9/unit/model/preset_workspace_test.py new file mode 100644 index 000000000..cddf5c3c2 --- /dev/null +++ b/tests_v9/unit/model/preset_workspace_test.py @@ -0,0 +1,179 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for PresetWorkspace model in pyatlan_v9.""" + +import json + +import pytest +from msgspec import UNSET + +from pyatlan_v9.model import PresetWorkspace +from tests_v9.unit.model.constants import ( + PRESET_CONNECTION_QUALIFIED_NAME, + PRESET_CONNECTOR_TYPE, + PRESET_WORKSPACE_NAME, + PRESET_WORKSPACE_QUALIFIED_NAME, +) + + +@pytest.mark.parametrize( + "name, connection_qualified_name, message", + [ + (None, "connection/name", "name is required"), + (PRESET_WORKSPACE_NAME, None, "connection_qualified_name is required"), + ], +) +def test_creator_with_missing_parameters_raises_value_error( + name: str, connection_qualified_name: str, message: str +): + """Test that creator raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + PresetWorkspace.creator( + name=name, connection_qualified_name=connection_qualified_name + ) + + +def test_creator(): + """Test that creator properly initializes a PresetWorkspace with all derived fields.""" + sut = PresetWorkspace.creator( + name=PRESET_WORKSPACE_NAME, + connection_qualified_name=PRESET_CONNECTION_QUALIFIED_NAME, + ) + + assert sut.name == PRESET_WORKSPACE_NAME + assert sut.connection_qualified_name == PRESET_CONNECTION_QUALIFIED_NAME + assert sut.qualified_name == PRESET_WORKSPACE_QUALIFIED_NAME + assert sut.connector_name == PRESET_CONNECTOR_TYPE + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, PRESET_WORKSPACE_QUALIFIED_NAME, "qualified_name is required"), + (PRESET_WORKSPACE_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test that updater raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + PresetWorkspace.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test that updater creates a PresetWorkspace instance for modification.""" + sut = PresetWorkspace.updater( + qualified_name=PRESET_WORKSPACE_QUALIFIED_NAME, name=PRESET_WORKSPACE_NAME + ) + + assert sut.qualified_name == PRESET_WORKSPACE_QUALIFIED_NAME + assert sut.name == PRESET_WORKSPACE_NAME + + +def test_trim_to_required(): + """Test that trim_to_required returns a PresetWorkspace with only required fields.""" + sut = PresetWorkspace.updater( + qualified_name=PRESET_WORKSPACE_QUALIFIED_NAME, name=PRESET_WORKSPACE_NAME + ).trim_to_required() + + assert sut.qualified_name == PRESET_WORKSPACE_QUALIFIED_NAME + assert sut.name == PRESET_WORKSPACE_NAME + + +def test_basic_construction(): + """Test basic PresetWorkspace construction with minimal parameters.""" + workspace = PresetWorkspace( + name=PRESET_WORKSPACE_NAME, qualified_name=PRESET_WORKSPACE_QUALIFIED_NAME + ) + + assert workspace.name == PRESET_WORKSPACE_NAME + assert workspace.qualified_name == PRESET_WORKSPACE_QUALIFIED_NAME + assert workspace.type_name == "PresetWorkspace" + + +def test_unset_fields(): + """Test that optional fields default to UNSET.""" + workspace = PresetWorkspace( + name=PRESET_WORKSPACE_NAME, qualified_name=PRESET_WORKSPACE_QUALIFIED_NAME + ) + + assert workspace.preset_workspace_cluster_id is UNSET + assert workspace.preset_workspace_hostname is UNSET + assert workspace.preset_workspace_region is UNSET + assert workspace.preset_workspace_status is UNSET + + +def test_type_name_defaults(): + """Test that type_name defaults to PresetWorkspace.""" + workspace = PresetWorkspace( + name=PRESET_WORKSPACE_NAME, qualified_name=PRESET_WORKSPACE_QUALIFIED_NAME + ) + + assert workspace.type_name == "PresetWorkspace" + + +def test_serialization_to_json_nested(serde): + """Test serialization to nested JSON format (API format).""" + workspace = PresetWorkspace.creator( + name=PRESET_WORKSPACE_NAME, + connection_qualified_name=PRESET_CONNECTION_QUALIFIED_NAME, + ) + + json_str = workspace.to_json(nested=True, serde=serde) + data = json.loads(json_str) + + assert data["typeName"] == "PresetWorkspace" + assert "attributes" in data + assert data["attributes"]["name"] == PRESET_WORKSPACE_NAME + + +def test_round_trip_serialization(serde): + """Test that serialization and deserialization preserve all data.""" + original = PresetWorkspace.creator( + name=PRESET_WORKSPACE_NAME, + connection_qualified_name=PRESET_CONNECTION_QUALIFIED_NAME, + ) + + json_str = original.to_json(nested=True, serde=serde) + restored = PresetWorkspace.from_json(json_str, serde=serde) + + assert restored.name == original.name + assert restored.qualified_name == original.qualified_name + + +def test_creator_with_guid(): + """Test that creator initializes a temporary GUID for new assets.""" + workspace = PresetWorkspace.creator( + name=PRESET_WORKSPACE_NAME, + connection_qualified_name=PRESET_CONNECTION_QUALIFIED_NAME, + ) + + assert workspace.guid is not UNSET + assert workspace.guid is not None + assert isinstance(workspace.guid, str) + assert workspace.guid.startswith("-") + + +def test_backward_compat_create(): + """Test that create is a backward-compatible alias for creator.""" + sut = PresetWorkspace.create( + name=PRESET_WORKSPACE_NAME, + connection_qualified_name=PRESET_CONNECTION_QUALIFIED_NAME, + ) + + assert sut.name == PRESET_WORKSPACE_NAME + assert sut.connection_qualified_name == PRESET_CONNECTION_QUALIFIED_NAME + assert sut.qualified_name == PRESET_WORKSPACE_QUALIFIED_NAME + assert sut.connector_name == PRESET_CONNECTOR_TYPE + + +def test_backward_compat_create_for_modification(): + """Test that create_for_modification is a backward-compatible alias for updater.""" + sut = PresetWorkspace.create_for_modification( + qualified_name=PRESET_WORKSPACE_QUALIFIED_NAME, name=PRESET_WORKSPACE_NAME + ) + + assert sut.qualified_name == PRESET_WORKSPACE_QUALIFIED_NAME + assert sut.name == PRESET_WORKSPACE_NAME diff --git a/tests_v9/unit/model/procedure_test.py b/tests_v9/unit/model/procedure_test.py new file mode 100644 index 000000000..572549766 --- /dev/null +++ b/tests_v9/unit/model/procedure_test.py @@ -0,0 +1,192 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for Procedure model in pyatlan_v9.""" + +import json + +import pytest +from msgspec import UNSET + +from pyatlan_v9.model import Procedure +from tests_v9.unit.model.constants import ( + CONNECTION_QUALIFIED_NAME, + CONNECTOR_TYPE, + DATABASE_NAME, + DATABASE_QUALIFIED_NAME, + PROCEDURE_NAME, + SCHEMA_NAME, + SCHEMA_QUALIFIED_NAME, +) + +DEFINITION = """ +BEGIN +insert into `atlanhq.testing_lineage.INSTACART_ALCOHOL_ORDER_TIME_copy` +select * from `atlanhq.testing_lineage.INSTACART_ALCOHOL_ORDER_TIME`; +END +""" + +PROCEDURE_QUALIFIED_NAME = f"{SCHEMA_QUALIFIED_NAME}/_procedures_/{PROCEDURE_NAME}" + + +@pytest.mark.parametrize( + "name, definition, schema_qualified_name, message", + [ + (None, DEFINITION, SCHEMA_QUALIFIED_NAME, "name is required"), + (PROCEDURE_NAME, None, SCHEMA_QUALIFIED_NAME, "definition is required"), + (PROCEDURE_NAME, DEFINITION, None, "schema_qualified_name is required"), + ], +) +def test_creator_with_missing_parameters_raises_value_error( + name: str, definition: str, schema_qualified_name: str, message: str +): + """Test that creator raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + Procedure.creator( + name=name, + definition=definition, + schema_qualified_name=schema_qualified_name, + ) + + +def test_creator(): + """Test that creator properly initializes a Procedure with all derived fields.""" + sut = Procedure.creator( + name=PROCEDURE_NAME, + definition=DEFINITION, + schema_qualified_name=SCHEMA_QUALIFIED_NAME, + ) + + assert sut.name == PROCEDURE_NAME + assert sut.database_name == DATABASE_NAME + assert sut.connection_qualified_name == CONNECTION_QUALIFIED_NAME + assert sut.database_qualified_name == DATABASE_QUALIFIED_NAME + assert sut.qualified_name == PROCEDURE_QUALIFIED_NAME + assert sut.schema_qualified_name == SCHEMA_QUALIFIED_NAME + assert sut.schema_name == SCHEMA_NAME + assert sut.connector_name == CONNECTOR_TYPE + assert sut.atlan_schema.unique_attributes["qualifiedName"] == SCHEMA_QUALIFIED_NAME + + +def test_overload_creator(): + """Test creator with all optional parameters provided.""" + sut = Procedure.creator( + name=PROCEDURE_NAME, + definition=DEFINITION, + schema_qualified_name=SCHEMA_QUALIFIED_NAME, + schema_name=SCHEMA_NAME, + database_name=DATABASE_NAME, + database_qualified_name=DATABASE_QUALIFIED_NAME, + connection_qualified_name=CONNECTION_QUALIFIED_NAME, + ) + + assert sut.name == PROCEDURE_NAME + assert sut.database_name == DATABASE_NAME + assert sut.connection_qualified_name == CONNECTION_QUALIFIED_NAME + assert sut.database_qualified_name == DATABASE_QUALIFIED_NAME + assert sut.qualified_name == PROCEDURE_QUALIFIED_NAME + assert sut.schema_qualified_name == SCHEMA_QUALIFIED_NAME + assert sut.schema_name == SCHEMA_NAME + assert sut.connector_name == CONNECTOR_TYPE + assert sut.atlan_schema.unique_attributes["qualifiedName"] == SCHEMA_QUALIFIED_NAME + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, PROCEDURE_QUALIFIED_NAME, "qualified_name is required"), + (PROCEDURE_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test that updater raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + Procedure.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test that updater creates a Procedure instance for modification.""" + sut = Procedure.updater( + qualified_name=PROCEDURE_QUALIFIED_NAME, name=PROCEDURE_NAME + ) + + assert sut.qualified_name == PROCEDURE_QUALIFIED_NAME + assert sut.name == PROCEDURE_NAME + + +def test_trim_to_required(): + """Test that trim_to_required returns a Procedure with only required fields.""" + sut = Procedure.updater( + qualified_name=PROCEDURE_QUALIFIED_NAME, name=PROCEDURE_NAME + ).trim_to_required() + + assert sut.qualified_name == PROCEDURE_QUALIFIED_NAME + assert sut.name == PROCEDURE_NAME + + +def test_basic_construction(): + """Test basic Procedure construction with minimal parameters.""" + proc = Procedure(name=PROCEDURE_NAME, qualified_name=PROCEDURE_QUALIFIED_NAME) + + assert proc.name == PROCEDURE_NAME + assert proc.qualified_name == PROCEDURE_QUALIFIED_NAME + assert proc.type_name == "Procedure" + + +def test_unset_fields(): + """Test that optional fields default to UNSET.""" + proc = Procedure(name=PROCEDURE_NAME, qualified_name=PROCEDURE_QUALIFIED_NAME) + + assert proc.definition is UNSET + assert proc.sql_language is UNSET + assert proc.database_name is UNSET + assert proc.schema_name is UNSET + + +def test_serialization_to_json_nested(serde): + """Test serialization to nested JSON format (API format).""" + proc = Procedure.creator( + name=PROCEDURE_NAME, + definition=DEFINITION, + schema_qualified_name=SCHEMA_QUALIFIED_NAME, + ) + + json_str = proc.to_json(nested=True, serde=serde) + data = json.loads(json_str) + + assert data["typeName"] == "Procedure" + assert "attributes" in data + assert data["attributes"]["name"] == PROCEDURE_NAME + assert data["attributes"]["qualifiedName"] == PROCEDURE_QUALIFIED_NAME + + +def test_round_trip_serialization(serde): + """Test that serialization and deserialization preserve all data.""" + original = Procedure.creator( + name=PROCEDURE_NAME, + definition=DEFINITION, + schema_qualified_name=SCHEMA_QUALIFIED_NAME, + ) + + json_str = original.to_json(nested=True, serde=serde) + restored = Procedure.from_json(json_str, serde=serde) + + assert restored.name == original.name + assert restored.qualified_name == original.qualified_name + assert restored.definition == original.definition + + +def test_creator_with_guid(): + """Test that creator initializes a temporary GUID for new assets.""" + proc = Procedure.creator( + name=PROCEDURE_NAME, + definition=DEFINITION, + schema_qualified_name=SCHEMA_QUALIFIED_NAME, + ) + + assert proc.guid is not UNSET + assert proc.guid is not None + assert isinstance(proc.guid, str) + assert proc.guid.startswith("-") diff --git a/tests_v9/unit/model/process_test.py b/tests_v9/unit/model/process_test.py new file mode 100644 index 000000000..1ed56d39d --- /dev/null +++ b/tests_v9/unit/model/process_test.py @@ -0,0 +1,261 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for Process model in pyatlan_v9.""" + +from hashlib import md5 + +import pytest + +from pyatlan_v9.model import Catalog, Process + +PROCESS_QUALIFIED_NAME = "default/s3/1678379436102" +PROCESS_NAME = "DoIt" + + +@pytest.mark.parametrize( + "name, connection_qualified_name, process_id, inputs,outputs, parent, message", + [ + (None, "133/s3", None, [Catalog()], [Catalog()], None, "name is required"), + ( + "bob", + None, + None, + [Catalog()], + [Catalog()], + None, + "connection_qualified_name is required", + ), + ("bob", "133/s3", None, None, [Catalog()], None, "inputs is required"), + ( + "bob", + "133/s3", + None, + [], + [Catalog()], + None, + "inputs cannot be an empty list", + ), + ("bob", "133/s3", None, [Catalog()], None, None, "outputs is required"), + ( + "bob", + "133/s3", + None, + [Catalog()], + [], + None, + "outputs cannot be an empty list", + ), + ], +) +def test_creator_without_required_parameter_raises_value_error( + name, connection_qualified_name, process_id, inputs, outputs, parent, message +): + """Test creator raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + Process.creator( + name=name, + connection_qualified_name=connection_qualified_name, + process_id=process_id, + inputs=inputs, + outputs=outputs, + parent=parent, + ) + + +@pytest.mark.parametrize( + "name, connection_qualified_name, process_id, inputs,outputs, parent, expected_value", + [ + ( + "doit", + PROCESS_QUALIFIED_NAME, + "123", + [Catalog()], + [Catalog()], + None, + "default/s3/1678379436102/123", + ), + ( + "doit", + PROCESS_QUALIFIED_NAME, + None, + [Catalog(guid="123")], + [Catalog(guid="456")], + None, + "doitdefault/s3/1678379436102123456", + ), + ( + "doit", + PROCESS_QUALIFIED_NAME, + None, + [Catalog(guid="456")], + [Catalog(guid="789")], + Catalog(guid="123"), + "doitdefault/s3/1678379436102123456789", + ), + ], +) +def test_creator( + name, connection_qualified_name, process_id, inputs, outputs, parent, expected_value +): + """Test creator builds expected qualified name and relationships.""" + expected_value = ( + expected_value + if process_id + # deepcode ignore InsecureHash/test: this is not used for generating security keys + else f"{connection_qualified_name}/{md5(expected_value.encode()).hexdigest()}" + ) + + process = Process.creator( + name=name, + connection_qualified_name=connection_qualified_name, + process_id=process_id, + inputs=inputs, + outputs=outputs, + parent=parent, + ) + assert process.name == name + assert process.connection_qualified_name == connection_qualified_name + assert process.qualified_name == expected_value + assert process_id == process_id + assert len(process.inputs) == len(inputs) + assert len(process.outputs) == len(outputs) + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, PROCESS_QUALIFIED_NAME, "qualified_name is required"), + (PROCESS_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test updater raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + Process.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test updater creates process with required fields for update operations.""" + sut = Process.updater(qualified_name=PROCESS_QUALIFIED_NAME, name=PROCESS_NAME) + + assert sut.qualified_name == PROCESS_QUALIFIED_NAME + assert sut.name == PROCESS_NAME + + +def test_trim_to_required(): + """Test trim_to_required keeps only required update fields.""" + sut = Process.updater( + qualified_name=PROCESS_QUALIFIED_NAME, name=PROCESS_NAME + ).trim_to_required() + + assert sut.qualified_name == PROCESS_QUALIFIED_NAME + assert sut.name == PROCESS_NAME + + +@pytest.mark.parametrize( + "name, connection_qualified_name, process_id, inputs,outputs, parent, message", + [ + (None, "133/s3", None, [Catalog()], [Catalog()], None, "name is required"), + ( + "bob", + None, + None, + [Catalog()], + [Catalog()], + None, + "connection_qualified_name is required", + ), + ("bob", "133/s3", None, None, [Catalog()], None, "inputs is required"), + ( + "bob", + "133/s3", + None, + [], + [Catalog()], + None, + "inputs cannot be an empty list", + ), + ("bob", "133/s3", None, [Catalog()], None, None, "outputs is required"), + ( + "bob", + "133/s3", + None, + [Catalog()], + [], + None, + "outputs cannot be an empty list", + ), + ], +) +def test_process_attributes_generate_qualified_name_without_required_parameter_raises_value_error( + name, connection_qualified_name, process_id, inputs, outputs, parent, message +): + """Test ProcessAttributes.generate_qualified_name validates required inputs.""" + with pytest.raises(ValueError, match=message): + Process.generate_qualified_name( + name=name, + connection_qualified_name=connection_qualified_name, + process_id=process_id, + inputs=inputs, + outputs=outputs, + parent=parent, + ) + + +@pytest.mark.parametrize( + "name, connection_qualified_name, process_id, inputs,outputs, parent, expected_value", + [ + ( + "doit", + "default/s3/1678379436102", + "123", + [Catalog()], + [Catalog()], + None, + "default/s3/1678379436102/123", + ), + ( + "doit", + "default/s3/1678379436102", + None, + [Catalog(guid="123")], + [Catalog(guid="456")], + None, + "doitdefault/s3/1678379436102123456", + ), + ( + "doit", + "default/s3/1678379436102", + None, + [Catalog(guid="456")], + [Catalog(guid="789")], + Catalog(guid="123"), + "doitdefault/s3/1678379436102123456789", + ), + ], +) +def test_process_attributes_generate_qualified_name( + name, connection_qualified_name, process_id, inputs, outputs, parent, expected_value +): + """Test ProcessAttributes.generate_qualified_name returns expected values.""" + expected_value = ( + expected_value + if process_id + # deepcode ignore InsecureHash/test: this is not used for generating security keys + else f"{connection_qualified_name}/{md5(expected_value.encode()).hexdigest()}" + ) + + assert ( + Process.generate_qualified_name( + name=name, + connection_qualified_name=connection_qualified_name, + process_id=process_id, + inputs=inputs, + outputs=outputs, + parent=parent, + ) + == expected_value + ) diff --git a/tests_v9/unit/model/quick_sight_analysis_test.py b/tests_v9/unit/model/quick_sight_analysis_test.py new file mode 100644 index 000000000..4132d7b4d --- /dev/null +++ b/tests_v9/unit/model/quick_sight_analysis_test.py @@ -0,0 +1,117 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for QuickSightAnalysis model in pyatlan_v9.""" + +import pytest + +from pyatlan_v9.model import QuickSightAnalysis +from tests_v9.unit.model.constants import ( + QUICK_SIGHT_CONNECTION_QUALIFIED_NAME, + QUICK_SIGHT_CONNECTOR_TYPE, + QUICK_SIGHT_FOLDER_SET, + QUICK_SIGHT_ID, + QUICK_SIGHT_NAME, + QUICK_SIGHT_QUALIFIED_NAME, +) + + +@pytest.mark.parametrize( + "name, connection_qualified_name, quick_sight_id, message", + [ + ( + None, + QUICK_SIGHT_CONNECTION_QUALIFIED_NAME, + QUICK_SIGHT_ID, + "name is required", + ), + ( + QUICK_SIGHT_NAME, + None, + QUICK_SIGHT_ID, + "connection_qualified_name is required", + ), + ( + QUICK_SIGHT_NAME, + QUICK_SIGHT_CONNECTION_QUALIFIED_NAME, + None, + "quick_sight_id is required", + ), + ], +) +def test_creator_with_missing_parameters_raise_value_error( + name: str, connection_qualified_name: str, quick_sight_id: str, message: str +): + """Test creator validates required parameters.""" + with pytest.raises(ValueError, match=message): + QuickSightAnalysis.creator( + name=name, + connection_qualified_name=connection_qualified_name, + quick_sight_id=quick_sight_id, + ) + + +def test_creator(): + """Test creator initializes expected derived fields.""" + sut = QuickSightAnalysis.creator( + name=QUICK_SIGHT_NAME, + connection_qualified_name=QUICK_SIGHT_CONNECTION_QUALIFIED_NAME, + quick_sight_id=QUICK_SIGHT_ID, + ) + + assert sut.name == QUICK_SIGHT_NAME + assert sut.connection_qualified_name == QUICK_SIGHT_CONNECTION_QUALIFIED_NAME + assert sut.quick_sight_id == QUICK_SIGHT_ID + assert sut.qualified_name == QUICK_SIGHT_QUALIFIED_NAME + assert sut.connector_name == QUICK_SIGHT_CONNECTOR_TYPE + + +def test_overload_creator(): + """Test creator accepts optional folder relationships.""" + sut = QuickSightAnalysis.creator( + name=QUICK_SIGHT_NAME, + connection_qualified_name=QUICK_SIGHT_CONNECTION_QUALIFIED_NAME, + quick_sight_id=QUICK_SIGHT_ID, + quick_sight_analysis_folders=QUICK_SIGHT_FOLDER_SET, + ) + + assert sut.name == QUICK_SIGHT_NAME + assert sut.connection_qualified_name == QUICK_SIGHT_CONNECTION_QUALIFIED_NAME + assert sut.quick_sight_id == QUICK_SIGHT_ID + assert sut.qualified_name == QUICK_SIGHT_QUALIFIED_NAME + assert sut.connector_name == QUICK_SIGHT_CONNECTOR_TYPE + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, QUICK_SIGHT_QUALIFIED_NAME, "qualified_name is required"), + (QUICK_SIGHT_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test updater validates required parameters.""" + with pytest.raises(ValueError, match=message): + QuickSightAnalysis.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test updater creates a QuickSightAnalysis for modification.""" + sut = QuickSightAnalysis.updater( + qualified_name=QUICK_SIGHT_CONNECTION_QUALIFIED_NAME, name=QUICK_SIGHT_NAME + ) + + assert sut.qualified_name == QUICK_SIGHT_CONNECTION_QUALIFIED_NAME + assert sut.name == QUICK_SIGHT_NAME + + +def test_trim_to_required(): + """Test trim_to_required keeps only updater-required fields.""" + sut = QuickSightAnalysis.updater( + name=QUICK_SIGHT_NAME, qualified_name=QUICK_SIGHT_CONNECTION_QUALIFIED_NAME + ).trim_to_required() + + assert sut.name == QUICK_SIGHT_NAME + assert sut.qualified_name == QUICK_SIGHT_CONNECTION_QUALIFIED_NAME diff --git a/tests_v9/unit/model/quick_sight_analysis_visual_test.py b/tests_v9/unit/model/quick_sight_analysis_visual_test.py new file mode 100644 index 000000000..ca11776c9 --- /dev/null +++ b/tests_v9/unit/model/quick_sight_analysis_visual_test.py @@ -0,0 +1,157 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for QuickSightAnalysisVisual model in pyatlan_v9.""" + +import pytest + +from pyatlan_v9.model import QuickSightAnalysisVisual +from tests_v9.unit.model.constants import ( + QUICK_SIGHT_ANALYSIS_VISUAL_QUALIFIED_NAME, + QUICK_SIGHT_CONNECTION_QUALIFIED_NAME, + QUICK_SIGHT_CONNECTOR_TYPE, + QUICK_SIGHT_ID_ANALYSIS_VISUAL, + QUICK_SIGHT_NAME, + QUICK_SIGHT_QUALIFIED_NAME, + QUICK_SIGHT_SHEET_ID, + QUICK_SIGHT_SHEET_NAME, +) + + +@pytest.mark.parametrize( + "name, quick_sight_id, quick_sight_sheet_id, quick_sight_sheet_name, quick_sight_analysis_qualified_name, message", + [ + ( + None, + QUICK_SIGHT_ID_ANALYSIS_VISUAL, + QUICK_SIGHT_SHEET_ID, + QUICK_SIGHT_SHEET_NAME, + QUICK_SIGHT_QUALIFIED_NAME, + "name is required", + ), + ( + QUICK_SIGHT_NAME, + None, + QUICK_SIGHT_SHEET_ID, + QUICK_SIGHT_SHEET_NAME, + QUICK_SIGHT_QUALIFIED_NAME, + "quick_sight_id is required", + ), + ( + QUICK_SIGHT_NAME, + QUICK_SIGHT_ID_ANALYSIS_VISUAL, + None, + QUICK_SIGHT_SHEET_NAME, + QUICK_SIGHT_QUALIFIED_NAME, + "quick_sight_sheet_id is required", + ), + ( + QUICK_SIGHT_NAME, + QUICK_SIGHT_ID_ANALYSIS_VISUAL, + QUICK_SIGHT_SHEET_ID, + None, + QUICK_SIGHT_QUALIFIED_NAME, + "quick_sight_sheet_name is required", + ), + ( + QUICK_SIGHT_NAME, + QUICK_SIGHT_ID_ANALYSIS_VISUAL, + QUICK_SIGHT_SHEET_ID, + QUICK_SIGHT_SHEET_NAME, + None, + "quick_sight_analysis_qualified_name is required", + ), + ], +) +def test_creator_with_missing_parameters_raise_value_error( + name: str, + quick_sight_id: str, + quick_sight_sheet_id: str, + quick_sight_sheet_name: str, + quick_sight_analysis_qualified_name: str, + message: str, +): + """Test creator validates required parameters.""" + with pytest.raises(ValueError, match=message): + QuickSightAnalysisVisual.creator( + name=name, + quick_sight_sheet_id=quick_sight_sheet_id, + quick_sight_id=quick_sight_id, + quick_sight_sheet_name=quick_sight_sheet_name, + quick_sight_analysis_qualified_name=quick_sight_analysis_qualified_name, + ) + + +def test_creator(): + """Test creator initializes expected derived fields.""" + sut = QuickSightAnalysisVisual.creator( + name=QUICK_SIGHT_NAME, + quick_sight_id=QUICK_SIGHT_ID_ANALYSIS_VISUAL, + quick_sight_sheet_id=QUICK_SIGHT_SHEET_ID, + quick_sight_sheet_name=QUICK_SIGHT_SHEET_NAME, + quick_sight_analysis_qualified_name=QUICK_SIGHT_QUALIFIED_NAME, + ) + + assert sut.name == QUICK_SIGHT_NAME + assert sut.quick_sight_analysis_qualified_name == QUICK_SIGHT_QUALIFIED_NAME + assert sut.quick_sight_id == QUICK_SIGHT_ID_ANALYSIS_VISUAL + assert sut.qualified_name == QUICK_SIGHT_ANALYSIS_VISUAL_QUALIFIED_NAME + assert sut.connector_name == QUICK_SIGHT_CONNECTOR_TYPE + assert sut.quick_sight_sheet_id == QUICK_SIGHT_SHEET_ID + assert sut.quick_sight_sheet_name == QUICK_SIGHT_SHEET_NAME + + +def test_overload_creator(): + """Test creator accepts optional connection qualified name.""" + sut = QuickSightAnalysisVisual.creator( + name=QUICK_SIGHT_NAME, + quick_sight_id=QUICK_SIGHT_ID_ANALYSIS_VISUAL, + quick_sight_sheet_id=QUICK_SIGHT_SHEET_ID, + quick_sight_sheet_name=QUICK_SIGHT_SHEET_NAME, + quick_sight_analysis_qualified_name=QUICK_SIGHT_QUALIFIED_NAME, + connection_qualified_name=QUICK_SIGHT_CONNECTION_QUALIFIED_NAME, + ) + + assert sut.name == QUICK_SIGHT_NAME + assert sut.quick_sight_analysis_qualified_name == QUICK_SIGHT_QUALIFIED_NAME + assert sut.quick_sight_id == QUICK_SIGHT_ID_ANALYSIS_VISUAL + assert sut.qualified_name == QUICK_SIGHT_ANALYSIS_VISUAL_QUALIFIED_NAME + assert sut.connector_name == QUICK_SIGHT_CONNECTOR_TYPE + assert sut.quick_sight_sheet_id == QUICK_SIGHT_SHEET_ID + assert sut.quick_sight_sheet_name == QUICK_SIGHT_SHEET_NAME + assert sut.connection_qualified_name == QUICK_SIGHT_CONNECTION_QUALIFIED_NAME + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, QUICK_SIGHT_QUALIFIED_NAME, "qualified_name is required"), + (QUICK_SIGHT_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test updater validates required parameters.""" + with pytest.raises(ValueError, match=message): + QuickSightAnalysisVisual.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test updater creates a QuickSightAnalysisVisual for modification.""" + sut = QuickSightAnalysisVisual.updater( + qualified_name=QUICK_SIGHT_CONNECTION_QUALIFIED_NAME, name=QUICK_SIGHT_NAME + ) + + assert sut.qualified_name == QUICK_SIGHT_CONNECTION_QUALIFIED_NAME + assert sut.name == QUICK_SIGHT_NAME + + +def test_trim_to_required(): + """Test trim_to_required keeps only updater-required fields.""" + sut = QuickSightAnalysisVisual.updater( + name=QUICK_SIGHT_NAME, qualified_name=QUICK_SIGHT_CONNECTION_QUALIFIED_NAME + ).trim_to_required() + + assert sut.name == QUICK_SIGHT_NAME + assert sut.qualified_name == QUICK_SIGHT_CONNECTION_QUALIFIED_NAME diff --git a/tests_v9/unit/model/quick_sight_dashboard_test.py b/tests_v9/unit/model/quick_sight_dashboard_test.py new file mode 100644 index 000000000..5118d8aa7 --- /dev/null +++ b/tests_v9/unit/model/quick_sight_dashboard_test.py @@ -0,0 +1,117 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for QuickSightDashboard model in pyatlan_v9.""" + +import pytest + +from pyatlan_v9.model import QuickSightDashboard +from tests_v9.unit.model.constants import ( + QUICK_SIGHT_CONNECTION_QUALIFIED_NAME, + QUICK_SIGHT_CONNECTOR_TYPE, + QUICK_SIGHT_FOLDER_SET, + QUICK_SIGHT_ID, + QUICK_SIGHT_NAME, + QUICK_SIGHT_QUALIFIED_NAME, +) + + +@pytest.mark.parametrize( + "name, connection_qualified_name, quick_sight_id, message", + [ + ( + None, + QUICK_SIGHT_CONNECTION_QUALIFIED_NAME, + QUICK_SIGHT_ID, + "name is required", + ), + ( + QUICK_SIGHT_NAME, + None, + QUICK_SIGHT_ID, + "connection_qualified_name is required", + ), + ( + QUICK_SIGHT_NAME, + QUICK_SIGHT_CONNECTION_QUALIFIED_NAME, + None, + "quick_sight_id is required", + ), + ], +) +def test_creator_with_missing_parameters_raise_value_error( + name: str, connection_qualified_name: str, quick_sight_id: str, message: str +): + """Test creator validates required parameters.""" + with pytest.raises(ValueError, match=message): + QuickSightDashboard.creator( + name=name, + connection_qualified_name=connection_qualified_name, + quick_sight_id=quick_sight_id, + ) + + +def test_creator(): + """Test creator initializes expected derived fields.""" + sut = QuickSightDashboard.creator( + name=QUICK_SIGHT_NAME, + connection_qualified_name=QUICK_SIGHT_CONNECTION_QUALIFIED_NAME, + quick_sight_id=QUICK_SIGHT_ID, + ) + + assert sut.name == QUICK_SIGHT_NAME + assert sut.connection_qualified_name == QUICK_SIGHT_CONNECTION_QUALIFIED_NAME + assert sut.quick_sight_id == QUICK_SIGHT_ID + assert sut.qualified_name == QUICK_SIGHT_QUALIFIED_NAME + assert sut.connector_name == QUICK_SIGHT_CONNECTOR_TYPE + + +def test_overload_creator(): + """Test creator accepts optional folder relationships.""" + sut = QuickSightDashboard.creator( + name=QUICK_SIGHT_NAME, + connection_qualified_name=QUICK_SIGHT_CONNECTION_QUALIFIED_NAME, + quick_sight_id=QUICK_SIGHT_ID, + quick_sight_dashboard_folders=QUICK_SIGHT_FOLDER_SET, + ) + + assert sut.name == QUICK_SIGHT_NAME + assert sut.connection_qualified_name == QUICK_SIGHT_CONNECTION_QUALIFIED_NAME + assert sut.quick_sight_id == QUICK_SIGHT_ID + assert sut.qualified_name == QUICK_SIGHT_QUALIFIED_NAME + assert sut.connector_name == QUICK_SIGHT_CONNECTOR_TYPE + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, QUICK_SIGHT_QUALIFIED_NAME, "qualified_name is required"), + (QUICK_SIGHT_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test updater validates required parameters.""" + with pytest.raises(ValueError, match=message): + QuickSightDashboard.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test updater creates a QuickSightDashboard for modification.""" + sut = QuickSightDashboard.updater( + qualified_name=QUICK_SIGHT_CONNECTION_QUALIFIED_NAME, name=QUICK_SIGHT_NAME + ) + + assert sut.qualified_name == QUICK_SIGHT_CONNECTION_QUALIFIED_NAME + assert sut.name == QUICK_SIGHT_NAME + + +def test_trim_to_required(): + """Test trim_to_required keeps only updater-required fields.""" + sut = QuickSightDashboard.updater( + name=QUICK_SIGHT_NAME, qualified_name=QUICK_SIGHT_CONNECTION_QUALIFIED_NAME + ).trim_to_required() + + assert sut.name == QUICK_SIGHT_NAME + assert sut.qualified_name == QUICK_SIGHT_CONNECTION_QUALIFIED_NAME diff --git a/tests_v9/unit/model/quick_sight_dashboard_visual_test.py b/tests_v9/unit/model/quick_sight_dashboard_visual_test.py new file mode 100644 index 000000000..d6876986d --- /dev/null +++ b/tests_v9/unit/model/quick_sight_dashboard_visual_test.py @@ -0,0 +1,157 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for QuickSightDashboardVisual model in pyatlan_v9.""" + +import pytest + +from pyatlan_v9.model import QuickSightDashboardVisual +from tests_v9.unit.model.constants import ( + QUICK_SIGHT_CONNECTION_QUALIFIED_NAME, + QUICK_SIGHT_CONNECTOR_TYPE, + QUICK_SIGHT_DASHBOARD_VISUAL_QUALIFIED_NAME, + QUICK_SIGHT_ID_DASHBOARD_VISUAL, + QUICK_SIGHT_NAME, + QUICK_SIGHT_QUALIFIED_NAME, + QUICK_SIGHT_SHEET_ID, + QUICK_SIGHT_SHEET_NAME, +) + + +@pytest.mark.parametrize( + "name, quick_sight_id, quick_sight_sheet_id, quick_sight_sheet_name, quick_sight_dashboard_qualified_name, message", + [ + ( + None, + QUICK_SIGHT_ID_DASHBOARD_VISUAL, + QUICK_SIGHT_SHEET_ID, + QUICK_SIGHT_SHEET_NAME, + QUICK_SIGHT_QUALIFIED_NAME, + "name is required", + ), + ( + QUICK_SIGHT_NAME, + None, + QUICK_SIGHT_SHEET_ID, + QUICK_SIGHT_SHEET_NAME, + QUICK_SIGHT_QUALIFIED_NAME, + "quick_sight_id is required", + ), + ( + QUICK_SIGHT_NAME, + QUICK_SIGHT_ID_DASHBOARD_VISUAL, + None, + QUICK_SIGHT_SHEET_NAME, + QUICK_SIGHT_QUALIFIED_NAME, + "quick_sight_sheet_id is required", + ), + ( + QUICK_SIGHT_NAME, + QUICK_SIGHT_ID_DASHBOARD_VISUAL, + QUICK_SIGHT_SHEET_ID, + None, + QUICK_SIGHT_QUALIFIED_NAME, + "quick_sight_sheet_name is required", + ), + ( + QUICK_SIGHT_NAME, + QUICK_SIGHT_ID_DASHBOARD_VISUAL, + QUICK_SIGHT_SHEET_ID, + QUICK_SIGHT_SHEET_NAME, + None, + "quick_sight_dashboard_qualified_name is required", + ), + ], +) +def test_creator_with_missing_parameters_raise_value_error( + name: str, + quick_sight_id: str, + quick_sight_sheet_id: str, + quick_sight_sheet_name: str, + quick_sight_dashboard_qualified_name: str, + message: str, +): + """Test creator validates required parameters.""" + with pytest.raises(ValueError, match=message): + QuickSightDashboardVisual.creator( + name=name, + quick_sight_sheet_id=quick_sight_sheet_id, + quick_sight_id=quick_sight_id, + quick_sight_sheet_name=quick_sight_sheet_name, + quick_sight_dashboard_qualified_name=quick_sight_dashboard_qualified_name, + ) + + +def test_creator(): + """Test creator initializes expected derived fields.""" + sut = QuickSightDashboardVisual.creator( + name=QUICK_SIGHT_NAME, + quick_sight_id=QUICK_SIGHT_ID_DASHBOARD_VISUAL, + quick_sight_sheet_id=QUICK_SIGHT_SHEET_ID, + quick_sight_sheet_name=QUICK_SIGHT_SHEET_NAME, + quick_sight_dashboard_qualified_name=QUICK_SIGHT_QUALIFIED_NAME, + ) + + assert sut.name == QUICK_SIGHT_NAME + assert sut.quick_sight_dashboard_qualified_name == QUICK_SIGHT_QUALIFIED_NAME + assert sut.quick_sight_id == QUICK_SIGHT_ID_DASHBOARD_VISUAL + assert sut.qualified_name == QUICK_SIGHT_DASHBOARD_VISUAL_QUALIFIED_NAME + assert sut.connector_name == QUICK_SIGHT_CONNECTOR_TYPE + assert sut.quick_sight_sheet_id == QUICK_SIGHT_SHEET_ID + assert sut.quick_sight_sheet_name == QUICK_SIGHT_SHEET_NAME + + +def test_overload_creator(): + """Test creator accepts optional connection qualified name.""" + sut = QuickSightDashboardVisual.creator( + name=QUICK_SIGHT_NAME, + quick_sight_id=QUICK_SIGHT_ID_DASHBOARD_VISUAL, + quick_sight_sheet_id=QUICK_SIGHT_SHEET_ID, + quick_sight_sheet_name=QUICK_SIGHT_SHEET_NAME, + quick_sight_dashboard_qualified_name=QUICK_SIGHT_QUALIFIED_NAME, + connection_qualified_name=QUICK_SIGHT_CONNECTION_QUALIFIED_NAME, + ) + + assert sut.name == QUICK_SIGHT_NAME + assert sut.quick_sight_dashboard_qualified_name == QUICK_SIGHT_QUALIFIED_NAME + assert sut.quick_sight_id == QUICK_SIGHT_ID_DASHBOARD_VISUAL + assert sut.qualified_name == QUICK_SIGHT_DASHBOARD_VISUAL_QUALIFIED_NAME + assert sut.connector_name == QUICK_SIGHT_CONNECTOR_TYPE + assert sut.quick_sight_sheet_id == QUICK_SIGHT_SHEET_ID + assert sut.quick_sight_sheet_name == QUICK_SIGHT_SHEET_NAME + assert sut.connection_qualified_name == QUICK_SIGHT_CONNECTION_QUALIFIED_NAME + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, QUICK_SIGHT_QUALIFIED_NAME, "qualified_name is required"), + (QUICK_SIGHT_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test updater validates required parameters.""" + with pytest.raises(ValueError, match=message): + QuickSightDashboardVisual.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test updater creates a QuickSightDashboardVisual for modification.""" + sut = QuickSightDashboardVisual.updater( + qualified_name=QUICK_SIGHT_CONNECTION_QUALIFIED_NAME, name=QUICK_SIGHT_NAME + ) + + assert sut.qualified_name == QUICK_SIGHT_CONNECTION_QUALIFIED_NAME + assert sut.name == QUICK_SIGHT_NAME + + +def test_trim_to_required(): + """Test trim_to_required keeps only updater-required fields.""" + sut = QuickSightDashboardVisual.updater( + name=QUICK_SIGHT_NAME, qualified_name=QUICK_SIGHT_CONNECTION_QUALIFIED_NAME + ).trim_to_required() + + assert sut.name == QUICK_SIGHT_NAME + assert sut.qualified_name == QUICK_SIGHT_CONNECTION_QUALIFIED_NAME diff --git a/tests_v9/unit/model/quick_sight_dataset_field_test.py b/tests_v9/unit/model/quick_sight_dataset_field_test.py new file mode 100644 index 000000000..53b11717b --- /dev/null +++ b/tests_v9/unit/model/quick_sight_dataset_field_test.py @@ -0,0 +1,124 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for QuickSightDatasetField model in pyatlan_v9.""" + +import pytest + +from pyatlan_v9.model import QuickSightDatasetField +from pyatlan_v9.model.enums import QuickSightDatasetFieldType +from tests_v9.unit.model.constants import ( + QUICK_SIGHT_CONNECTION_QUALIFIED_NAME, + QUICK_SIGHT_CONNECTOR_TYPE, + QUICK_SIGHT_DATASET_FIELD_QUALIFIED_NAME, + QUICK_SIGHT_ID_DATASET_FEILD, + QUICK_SIGHT_NAME, + QUICK_SIGHT_QUALIFIED_NAME, +) + + +@pytest.mark.parametrize( + "name, quick_sight_dataset_qualified_name, quick_sight_id, message", + [ + ( + None, + QUICK_SIGHT_QUALIFIED_NAME, + QUICK_SIGHT_ID_DATASET_FEILD, + "name is required", + ), + ( + QUICK_SIGHT_NAME, + None, + QUICK_SIGHT_ID_DATASET_FEILD, + "quick_sight_dataset_qualified_name is required", + ), + ( + QUICK_SIGHT_NAME, + QUICK_SIGHT_QUALIFIED_NAME, + None, + "quick_sight_id is required", + ), + ], +) +def test_creator_with_missing_parameters_raise_value_error( + name: str, + quick_sight_dataset_qualified_name: str, + quick_sight_id: str, + message: str, +): + """Test creator validates required parameters.""" + with pytest.raises(ValueError, match=message): + QuickSightDatasetField.creator( + name=name, + quick_sight_dataset_qualified_name=quick_sight_dataset_qualified_name, + quick_sight_id=quick_sight_id, + ) + + +def test_creator(): + """Test creator initializes expected derived fields.""" + sut = QuickSightDatasetField.creator( + name=QUICK_SIGHT_NAME, + quick_sight_dataset_qualified_name=QUICK_SIGHT_QUALIFIED_NAME, + quick_sight_id=QUICK_SIGHT_ID_DATASET_FEILD, + ) + + assert sut.name == QUICK_SIGHT_NAME + assert sut.quick_sight_dataset_qualified_name == QUICK_SIGHT_QUALIFIED_NAME + assert sut.quick_sight_id == QUICK_SIGHT_ID_DATASET_FEILD + assert sut.qualified_name == QUICK_SIGHT_DATASET_FIELD_QUALIFIED_NAME + assert sut.connector_name == QUICK_SIGHT_CONNECTOR_TYPE + + +def test_overload_creator(): + """Test creator accepts optional field type and connection qualified name.""" + sut = QuickSightDatasetField.creator( + name=QUICK_SIGHT_NAME, + quick_sight_dataset_qualified_name=QUICK_SIGHT_QUALIFIED_NAME, + quick_sight_id=QUICK_SIGHT_ID_DATASET_FEILD, + quick_sight_dataset_field_type=QuickSightDatasetFieldType.STRING, + connection_qualified_name=QUICK_SIGHT_CONNECTION_QUALIFIED_NAME, + ) + + assert sut.name == QUICK_SIGHT_NAME + assert sut.quick_sight_dataset_qualified_name == QUICK_SIGHT_QUALIFIED_NAME + assert sut.quick_sight_id == QUICK_SIGHT_ID_DATASET_FEILD + assert sut.qualified_name == QUICK_SIGHT_DATASET_FIELD_QUALIFIED_NAME + assert sut.connector_name == QUICK_SIGHT_CONNECTOR_TYPE + assert sut.connection_qualified_name == QUICK_SIGHT_CONNECTION_QUALIFIED_NAME + assert sut.quick_sight_dataset_field_type == QuickSightDatasetFieldType.STRING + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, QUICK_SIGHT_QUALIFIED_NAME, "qualified_name is required"), + (QUICK_SIGHT_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test updater validates required parameters.""" + with pytest.raises(ValueError, match=message): + QuickSightDatasetField.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test updater creates a QuickSightDatasetField for modification.""" + sut = QuickSightDatasetField.updater( + qualified_name=QUICK_SIGHT_CONNECTION_QUALIFIED_NAME, name=QUICK_SIGHT_NAME + ) + + assert sut.qualified_name == QUICK_SIGHT_CONNECTION_QUALIFIED_NAME + assert sut.name == QUICK_SIGHT_NAME + + +def test_trim_to_required(): + """Test trim_to_required keeps only updater-required fields.""" + sut = QuickSightDatasetField.updater( + name=QUICK_SIGHT_NAME, qualified_name=QUICK_SIGHT_CONNECTION_QUALIFIED_NAME + ).trim_to_required() + + assert sut.name == QUICK_SIGHT_NAME + assert sut.qualified_name == QUICK_SIGHT_CONNECTION_QUALIFIED_NAME diff --git a/tests_v9/unit/model/quick_sight_dataset_test.py b/tests_v9/unit/model/quick_sight_dataset_test.py new file mode 100644 index 000000000..1dadd6314 --- /dev/null +++ b/tests_v9/unit/model/quick_sight_dataset_test.py @@ -0,0 +1,122 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for QuickSightDataset model in pyatlan_v9.""" + +import pytest + +from pyatlan_v9.model import QuickSightDataset +from pyatlan_v9.model.enums import QuickSightDatasetImportMode +from tests_v9.unit.model.constants import ( + QUICK_SIGHT_CONNECTION_QUALIFIED_NAME, + QUICK_SIGHT_CONNECTOR_TYPE, + QUICK_SIGHT_FOLDER_SET, + QUICK_SIGHT_ID, + QUICK_SIGHT_NAME, + QUICK_SIGHT_QUALIFIED_NAME, +) + + +@pytest.mark.parametrize( + "name, connection_qualified_name, quick_sight_id, message", + [ + ( + None, + QUICK_SIGHT_CONNECTION_QUALIFIED_NAME, + QUICK_SIGHT_ID, + "name is required", + ), + ( + QUICK_SIGHT_NAME, + None, + QUICK_SIGHT_ID, + "connection_qualified_name is required", + ), + ( + QUICK_SIGHT_NAME, + QUICK_SIGHT_CONNECTION_QUALIFIED_NAME, + None, + "quick_sight_id is required", + ), + ], +) +def test_creator_with_missing_parameters_raise_value_error( + name: str, connection_qualified_name: str, quick_sight_id: str, message: str +): + """Test creator validates required parameters.""" + with pytest.raises(ValueError, match=message): + QuickSightDataset.creator( + name=name, + connection_qualified_name=connection_qualified_name, + quick_sight_id=quick_sight_id, + ) + + +def test_creator(): + """Test creator initializes expected derived fields.""" + sut = QuickSightDataset.creator( + name=QUICK_SIGHT_NAME, + connection_qualified_name=QUICK_SIGHT_CONNECTION_QUALIFIED_NAME, + quick_sight_id=QUICK_SIGHT_ID, + ) + + assert sut.name == QUICK_SIGHT_NAME + assert sut.connection_qualified_name == QUICK_SIGHT_CONNECTION_QUALIFIED_NAME + assert sut.quick_sight_id == QUICK_SIGHT_ID + assert sut.qualified_name == QUICK_SIGHT_QUALIFIED_NAME + assert sut.connector_name == QUICK_SIGHT_CONNECTOR_TYPE + + +def test_overload_creator(): + """Test creator accepts optional import mode and folder relationships.""" + sut = QuickSightDataset.creator( + name=QUICK_SIGHT_NAME, + connection_qualified_name=QUICK_SIGHT_CONNECTION_QUALIFIED_NAME, + quick_sight_id=QUICK_SIGHT_ID, + quick_sight_dataset_import_mode=QuickSightDatasetImportMode.DIRECT_QUERY, + quick_sight_dataset_folders=QUICK_SIGHT_FOLDER_SET, + ) + + assert sut.name == QUICK_SIGHT_NAME + assert sut.connection_qualified_name == QUICK_SIGHT_CONNECTION_QUALIFIED_NAME + assert sut.quick_sight_id == QUICK_SIGHT_ID + assert ( + sut.quick_sight_dataset_import_mode == QuickSightDatasetImportMode.DIRECT_QUERY + ) + assert sut.qualified_name == QUICK_SIGHT_QUALIFIED_NAME + assert sut.connector_name == QUICK_SIGHT_CONNECTOR_TYPE + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, QUICK_SIGHT_QUALIFIED_NAME, "qualified_name is required"), + (QUICK_SIGHT_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test updater validates required parameters.""" + with pytest.raises(ValueError, match=message): + QuickSightDataset.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test updater creates a QuickSightDataset for modification.""" + sut = QuickSightDataset.updater( + qualified_name=QUICK_SIGHT_CONNECTION_QUALIFIED_NAME, name=QUICK_SIGHT_NAME + ) + + assert sut.qualified_name == QUICK_SIGHT_CONNECTION_QUALIFIED_NAME + assert sut.name == QUICK_SIGHT_NAME + + +def test_trim_to_required(): + """Test trim_to_required keeps only updater-required fields.""" + sut = QuickSightDataset.updater( + name=QUICK_SIGHT_NAME, qualified_name=QUICK_SIGHT_CONNECTION_QUALIFIED_NAME + ).trim_to_required() + + assert sut.name == QUICK_SIGHT_NAME + assert sut.qualified_name == QUICK_SIGHT_CONNECTION_QUALIFIED_NAME diff --git a/tests_v9/unit/model/quick_sight_folder_test.py b/tests_v9/unit/model/quick_sight_folder_test.py new file mode 100644 index 000000000..f5c77348c --- /dev/null +++ b/tests_v9/unit/model/quick_sight_folder_test.py @@ -0,0 +1,118 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for QuickSightFolder model in pyatlan_v9.""" + +import pytest + +from pyatlan_v9.model import QuickSightFolder +from pyatlan_v9.model.enums import QuickSightFolderType +from tests_v9.unit.model.constants import ( + QUICK_SIGHT_CONNECTION_QUALIFIED_NAME, + QUICK_SIGHT_CONNECTOR_TYPE, + QUICK_SIGHT_ID, + QUICK_SIGHT_NAME, + QUICK_SIGHT_QUALIFIED_NAME, +) + + +@pytest.mark.parametrize( + "name, connection_qualified_name, quick_sight_id, message", + [ + ( + None, + QUICK_SIGHT_CONNECTION_QUALIFIED_NAME, + QUICK_SIGHT_ID, + "name is required", + ), + ( + QUICK_SIGHT_NAME, + None, + QUICK_SIGHT_ID, + "connection_qualified_name is required", + ), + ( + QUICK_SIGHT_NAME, + QUICK_SIGHT_CONNECTION_QUALIFIED_NAME, + None, + "quick_sight_id is required", + ), + ], +) +def test_creator_with_missing_parameters_raise_value_error( + name: str, connection_qualified_name: str, quick_sight_id: str, message: str +): + """Test creator validates required parameters.""" + with pytest.raises(ValueError, match=message): + QuickSightFolder.creator( + name=name, + connection_qualified_name=connection_qualified_name, + quick_sight_id=quick_sight_id, + ) + + +def test_creator(): + """Test creator initializes expected derived fields.""" + sut = QuickSightFolder.creator( + name=QUICK_SIGHT_NAME, + connection_qualified_name=QUICK_SIGHT_CONNECTION_QUALIFIED_NAME, + quick_sight_id=QUICK_SIGHT_ID, + ) + + assert sut.name == QUICK_SIGHT_NAME + assert sut.connection_qualified_name == QUICK_SIGHT_CONNECTION_QUALIFIED_NAME + assert sut.quick_sight_id == QUICK_SIGHT_ID + assert sut.qualified_name == QUICK_SIGHT_QUALIFIED_NAME + assert sut.connector_name == QUICK_SIGHT_CONNECTOR_TYPE + + +def test_overload_creator(): + """Test creator supports quick_sight_folder_type input.""" + sut = QuickSightFolder.creator( + name=QUICK_SIGHT_NAME, + connection_qualified_name=QUICK_SIGHT_CONNECTION_QUALIFIED_NAME, + quick_sight_id=QUICK_SIGHT_ID, + quick_sight_folder_type=QuickSightFolderType.SHARED, + ) + + assert sut.name == QUICK_SIGHT_NAME + assert sut.connection_qualified_name == QUICK_SIGHT_CONNECTION_QUALIFIED_NAME + assert sut.quick_sight_id == QUICK_SIGHT_ID + assert sut.quick_sight_folder_type == QuickSightFolderType.SHARED + assert sut.qualified_name == QUICK_SIGHT_QUALIFIED_NAME + assert sut.connector_name == QUICK_SIGHT_CONNECTOR_TYPE + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, QUICK_SIGHT_QUALIFIED_NAME, "qualified_name is required"), + (QUICK_SIGHT_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test updater validates required parameters.""" + with pytest.raises(ValueError, match=message): + QuickSightFolder.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test updater creates a QuickSightFolder for modification.""" + sut = QuickSightFolder.updater( + qualified_name=QUICK_SIGHT_CONNECTION_QUALIFIED_NAME, name=QUICK_SIGHT_NAME + ) + + assert sut.qualified_name == QUICK_SIGHT_CONNECTION_QUALIFIED_NAME + assert sut.name == QUICK_SIGHT_NAME + + +def test_trim_to_required(): + """Test trim_to_required keeps only updater-required fields.""" + sut = QuickSightFolder.updater( + name=QUICK_SIGHT_NAME, qualified_name=QUICK_SIGHT_CONNECTION_QUALIFIED_NAME + ).trim_to_required() + + assert sut.name == QUICK_SIGHT_NAME + assert sut.qualified_name == QUICK_SIGHT_CONNECTION_QUALIFIED_NAME diff --git a/tests_v9/unit/model/readme_test.py b/tests_v9/unit/model/readme_test.py new file mode 100644 index 000000000..06c3de1fe --- /dev/null +++ b/tests_v9/unit/model/readme_test.py @@ -0,0 +1,106 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for Readme model in pyatlan_v9.""" + +import pytest + +from pyatlan_v9.model import Readme, Table +from tests_v9.unit.model.constants import SCHEMA_QUALIFIED_NAME, TABLE_NAME + +README_NAME = f"{TABLE_NAME}/readme" +README_QUALIFIED_NAME = "2f8d68d2-8cd7-41e0-9d3b-cf27cd30f7ef/readme" + + +@pytest.mark.parametrize( + "asset, content, asset_name, error, message", + [ + (None, "stuff", None, ValueError, "asset is required"), + ( + Table.creator( + name=TABLE_NAME, + schema_qualified_name=SCHEMA_QUALIFIED_NAME, + ), + None, + None, + ValueError, + "content is required", + ), + ( + Table(), + "stuff", + None, + ValueError, + "asset_name is required when name is not available from asset", + ), + ], +) +def test_creator_without_required_parameters_raises_exception( + asset, content, asset_name, error, message +): + """Test creator validation for required asset and content fields.""" + with pytest.raises(error, match=message): + Readme.creator(asset=asset, content=content, asset_name=asset_name) + + +@pytest.mark.parametrize( + "asset, content, asset_name, expected_name", + [ + ( + Table.creator( + name=TABLE_NAME, + schema_qualified_name=SCHEMA_QUALIFIED_NAME, + ), + "

stuff

", + None, + TABLE_NAME, + ), + ( + Table(), + "

stuff

", + TABLE_NAME, + TABLE_NAME, + ), + ], +) +def test_creator(asset, content, asset_name, expected_name): + """Test creator builds readme name, relationship, and content correctly.""" + asset.guid = "test-guid-123" + readme = Readme.creator(asset=asset, content=content, asset_name=asset_name) + assert readme.qualified_name == f"{asset.guid}/readme" + assert readme.name == f"{expected_name} Readme" + assert readme.asset.guid == asset.guid + assert readme.description == content + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, README_QUALIFIED_NAME, "qualified_name is required"), + (README_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test updater raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + Readme.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test updater returns minimal update payload for Readme.""" + sut = Readme.updater(qualified_name=README_QUALIFIED_NAME, name=README_NAME) + + assert sut.qualified_name == README_QUALIFIED_NAME + assert sut.name == README_NAME + + +def test_trim_to_required(): + """Test trim_to_required keeps only required fields.""" + sut = Readme.updater( + qualified_name=README_QUALIFIED_NAME, name=README_NAME + ).trim_to_required() + + assert sut.qualified_name == README_QUALIFIED_NAME + assert sut.name == README_NAME diff --git a/tests_v9/unit/model/s3_bucket_test.py b/tests_v9/unit/model/s3_bucket_test.py new file mode 100644 index 000000000..b01bd3d64 --- /dev/null +++ b/tests_v9/unit/model/s3_bucket_test.py @@ -0,0 +1,109 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for S3Bucket model in pyatlan_v9 - exact parity with tests/unit/model/s3_bucket_test.py.""" + +import pytest + +from pyatlan_v9.model import S3Bucket +from tests_v9.unit.model.constants import ( + AWS_ARN, + BUCKET_NAME, + BUCKET_QUALIFIED_NAME, + BUCKET_WITH_NAME_QUALIFIED_NAME, + S3_CONNECTION_QUALIFIED_NAME, + S3_OBJECT_QUALIFIED_NAME, +) + + +@pytest.mark.parametrize( + "name, connection_qualified_name, msg", + [ + (None, S3_CONNECTION_QUALIFIED_NAME, "name is required"), + (BUCKET_NAME, None, "connection_qualified_name is required"), + ("", S3_CONNECTION_QUALIFIED_NAME, "name cannot be blank"), + (BUCKET_NAME, "", "connection_qualified_name cannot be blank"), + (BUCKET_NAME, "default/s3/", "Invalid connection_qualified_name"), + (BUCKET_NAME, "/s3/", "Invalid connection_qualified_name"), + ( + BUCKET_NAME, + "default/s3/production/TestDb", + "Invalid connection_qualified_name", + ), + (BUCKET_NAME, "s3/production", "Invalid connection_qualified_name"), + ( + BUCKET_NAME, + "default/s33/production", + "Invalid connection_qualified_name", + ), + ], +) +def test_create_without_required_parameters_raises_validation_error( + name, connection_qualified_name, msg +): + """Test that creator raises ValueError when required parameters are missing or invalid.""" + with pytest.raises(ValueError, match=msg): + S3Bucket.creator( + name=name, + connection_qualified_name=connection_qualified_name, + ) + + +def test_create_with_required_parameters(): + """Test creating S3Bucket with required parameters.""" + attributes = S3Bucket.creator( + name=BUCKET_NAME, + connection_qualified_name=S3_CONNECTION_QUALIFIED_NAME, + ) + assert attributes.name == BUCKET_NAME + assert attributes.aws_arn is None + assert attributes.connection_qualified_name == S3_CONNECTION_QUALIFIED_NAME + assert attributes.qualified_name == BUCKET_WITH_NAME_QUALIFIED_NAME + assert attributes.connector_name == S3_CONNECTION_QUALIFIED_NAME.split("/")[1] + + +def test_create_with_aws_arn(): + """Test creating S3Bucket with AWS ARN.""" + attributes = S3Bucket.creator( + name=BUCKET_NAME, + connection_qualified_name=S3_CONNECTION_QUALIFIED_NAME, + aws_arn=AWS_ARN, + ) + assert attributes.name == BUCKET_NAME + assert attributes.aws_arn == AWS_ARN + assert attributes.connection_qualified_name == S3_CONNECTION_QUALIFIED_NAME + assert attributes.qualified_name == BUCKET_QUALIFIED_NAME + assert attributes.connector_name == S3_CONNECTION_QUALIFIED_NAME.split("/")[1] + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, S3_OBJECT_QUALIFIED_NAME, "qualified_name is required"), + (BUCKET_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test that updater raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + S3Bucket.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test that updater creates an S3Bucket instance for modification.""" + sut = S3Bucket.updater(qualified_name=S3_OBJECT_QUALIFIED_NAME, name=BUCKET_NAME) + + assert sut.qualified_name == S3_OBJECT_QUALIFIED_NAME + assert sut.name == BUCKET_NAME + + +def test_trim_to_required(): + """Test that trim_to_required returns S3Bucket with only required fields.""" + sut = S3Bucket.updater( + qualified_name=S3_OBJECT_QUALIFIED_NAME, name=BUCKET_NAME + ).trim_to_required() + + assert sut.qualified_name == S3_OBJECT_QUALIFIED_NAME + assert sut.name == BUCKET_NAME diff --git a/tests_v9/unit/model/s3_object_test.py b/tests_v9/unit/model/s3_object_test.py new file mode 100644 index 000000000..2856ae042 --- /dev/null +++ b/tests_v9/unit/model/s3_object_test.py @@ -0,0 +1,390 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for S3Object model in pyatlan_v9.""" + +import pytest +from msgspec import UNSET + +from pyatlan.model.utils import construct_object_key +from pyatlan_v9.model import S3Object +from tests_v9.unit.model.constants import ( + AWS_ARN, + BUCKET_NAME, + BUCKET_QUALIFIED_NAME, + S3_CONNECTION_QUALIFIED_NAME, + S3_OBJECT_NAME, + S3_OBJECT_PREFIX, + S3_OBJECT_QUALIFIED_NAME, +) + + +@pytest.mark.parametrize( + "name, connection_qualified_name, aws_arn, s3_bucket_name, s3_bucket_qualified_name, msg", + [ + ( + None, + S3_CONNECTION_QUALIFIED_NAME, + "abc", + BUCKET_NAME, + BUCKET_QUALIFIED_NAME, + "name is required", + ), + ( + S3_OBJECT_NAME, + None, + "abc", + BUCKET_NAME, + BUCKET_QUALIFIED_NAME, + "connection_qualified_name is required", + ), + ( + "", + S3_CONNECTION_QUALIFIED_NAME, + "abc", + BUCKET_NAME, + BUCKET_QUALIFIED_NAME, + "name cannot be blank", + ), + ( + S3_OBJECT_NAME, + "", + "abc", + BUCKET_NAME, + BUCKET_QUALIFIED_NAME, + "connection_qualified_name cannot be blank", + ), + ( + S3_OBJECT_NAME, + "default/s3/", + "abc", + BUCKET_NAME, + BUCKET_QUALIFIED_NAME, + "Invalid connection_qualified_name", + ), + ( + S3_OBJECT_NAME, + "/s3/", + "abc", + BUCKET_NAME, + BUCKET_QUALIFIED_NAME, + "Invalid connection_qualified_name", + ), + ( + S3_OBJECT_NAME, + "default/s3/production/TestDb", + "abc", + BUCKET_NAME, + BUCKET_QUALIFIED_NAME, + "Invalid connection_qualified_name", + ), + ( + S3_OBJECT_NAME, + "s3/production", + "abc", + BUCKET_NAME, + BUCKET_QUALIFIED_NAME, + "Invalid connection_qualified_name", + ), + ( + S3_OBJECT_NAME, + "default/s33/production", + "abc", + BUCKET_NAME, + BUCKET_QUALIFIED_NAME, + "Invalid connection_qualified_name", + ), + ( + S3_OBJECT_NAME, + "default/s3", + None, + BUCKET_NAME, + BUCKET_QUALIFIED_NAME, + "aws_arn is required", + ), + ( + S3_OBJECT_NAME, + "default/s3", + "", + BUCKET_NAME, + BUCKET_QUALIFIED_NAME, + "aws_arn cannot be blank", + ), + ( + S3_OBJECT_NAME, + S3_CONNECTION_QUALIFIED_NAME, + "abc", + None, + BUCKET_QUALIFIED_NAME, + "s3_bucket_name is required", + ), + ( + S3_OBJECT_NAME, + S3_CONNECTION_QUALIFIED_NAME, + "abc", + "", + BUCKET_QUALIFIED_NAME, + "s3_bucket_name cannot be blank", + ), + ( + S3_OBJECT_NAME, + S3_CONNECTION_QUALIFIED_NAME, + "abc", + BUCKET_NAME, + None, + "s3_bucket_qualified_name is required", + ), + ( + S3_OBJECT_NAME, + S3_CONNECTION_QUALIFIED_NAME, + "abc", + BUCKET_NAME, + "", + "s3_bucket_qualified_name cannot be blank", + ), + ], +) +def test_creator_without_required_parameters_raises_validation_error( + name, + connection_qualified_name, + aws_arn, + s3_bucket_name, + s3_bucket_qualified_name, + msg, +): + """Test creator validation for missing and malformed parameters.""" + with pytest.raises(ValueError, match=msg): + S3Object.creator( + name=name, + connection_qualified_name=connection_qualified_name, + aws_arn=aws_arn, + s3_bucket_name=s3_bucket_name, + s3_bucket_qualified_name=s3_bucket_qualified_name, + ) + + +@pytest.mark.parametrize( + "name, connection_qualified_name, prefix, s3_bucket_name, s3_bucket_qualified_name, msg", + [ + ( + None, + S3_CONNECTION_QUALIFIED_NAME, + "abc", + BUCKET_NAME, + BUCKET_QUALIFIED_NAME, + "name is required", + ), + ( + S3_OBJECT_NAME, + None, + "abc", + BUCKET_NAME, + BUCKET_QUALIFIED_NAME, + "connection_qualified_name is required", + ), + ( + "", + S3_CONNECTION_QUALIFIED_NAME, + "abc", + BUCKET_NAME, + BUCKET_QUALIFIED_NAME, + "name cannot be blank", + ), + ( + S3_OBJECT_NAME, + "", + "abc", + BUCKET_NAME, + BUCKET_QUALIFIED_NAME, + "connection_qualified_name cannot be blank", + ), + ( + S3_OBJECT_NAME, + "default/s3/", + "abc", + BUCKET_NAME, + BUCKET_QUALIFIED_NAME, + "Invalid connection_qualified_name", + ), + ( + S3_OBJECT_NAME, + "/s3/", + "abc", + BUCKET_NAME, + BUCKET_QUALIFIED_NAME, + "Invalid connection_qualified_name", + ), + ( + S3_OBJECT_NAME, + "default/s3/production/TestDb", + "abc", + BUCKET_NAME, + BUCKET_QUALIFIED_NAME, + "Invalid connection_qualified_name", + ), + ( + S3_OBJECT_NAME, + "s3/production", + "abc", + BUCKET_NAME, + BUCKET_QUALIFIED_NAME, + "Invalid connection_qualified_name", + ), + ( + S3_OBJECT_NAME, + "default/s33/production", + "abc", + BUCKET_NAME, + BUCKET_QUALIFIED_NAME, + "Invalid connection_qualified_name", + ), + ( + S3_OBJECT_NAME, + S3_CONNECTION_QUALIFIED_NAME, + "abc", + None, + BUCKET_QUALIFIED_NAME, + "s3_bucket_name is required", + ), + ( + S3_OBJECT_NAME, + S3_CONNECTION_QUALIFIED_NAME, + "abc", + "", + BUCKET_QUALIFIED_NAME, + "s3_bucket_name cannot be blank", + ), + ( + S3_OBJECT_NAME, + S3_CONNECTION_QUALIFIED_NAME, + "abc", + BUCKET_NAME, + None, + "s3_bucket_qualified_name is required", + ), + ( + S3_OBJECT_NAME, + S3_CONNECTION_QUALIFIED_NAME, + "abc", + BUCKET_NAME, + "", + "s3_bucket_qualified_name cannot be blank", + ), + ], +) +def test_creator_with_prefix_without_required_parameters_raises_validation_error( + name, + connection_qualified_name, + prefix, + s3_bucket_name, + s3_bucket_qualified_name, + msg, +): + """Test creator_with_prefix validation for missing and malformed parameters.""" + with pytest.raises(ValueError, match=msg): + S3Object.creator_with_prefix( + name=name, + connection_qualified_name=connection_qualified_name, + prefix=prefix, + s3_bucket_name=s3_bucket_name, + s3_bucket_qualified_name=s3_bucket_qualified_name, + ) + + +@pytest.mark.parametrize( + "name, connection_qualified_name, aws_arn, s3_bucket_name, s3_bucket_qualified_name", + [ + ( + S3_OBJECT_NAME, + S3_CONNECTION_QUALIFIED_NAME, + AWS_ARN, + BUCKET_NAME, + BUCKET_QUALIFIED_NAME, + ), + ], +) +def test_creator_with_required_parameters( + name, connection_qualified_name, aws_arn, s3_bucket_name, s3_bucket_qualified_name +): + """Test creator builds expected qualified name and connector metadata.""" + attributes = S3Object.creator( + name=name, + connection_qualified_name=connection_qualified_name, + aws_arn=aws_arn, + s3_bucket_name=s3_bucket_name, + s3_bucket_qualified_name=s3_bucket_qualified_name, + ) + assert attributes.name == name + assert attributes.connection_qualified_name == connection_qualified_name + assert attributes.aws_arn == aws_arn + assert attributes.qualified_name == f"{connection_qualified_name}/{aws_arn}" + assert attributes.connector_name == connection_qualified_name.split("/")[1] + assert attributes.s3_bucket_name == s3_bucket_name + assert attributes.s3_bucket_qualified_name == s3_bucket_qualified_name + + +@pytest.mark.parametrize( + "name, connection_qualified_name, prefix, s3_bucket_name, s3_bucket_qualified_name", + [ + ( + S3_OBJECT_NAME, + S3_CONNECTION_QUALIFIED_NAME, + S3_OBJECT_PREFIX, + BUCKET_NAME, + BUCKET_QUALIFIED_NAME, + ), + ], +) +def test_creator_with_prefix( + name, connection_qualified_name, prefix, s3_bucket_name, s3_bucket_qualified_name +): + """Test creator_with_prefix derives object-key based qualified name.""" + attributes = S3Object.creator_with_prefix( + name=name, + connection_qualified_name=connection_qualified_name, + prefix=prefix, + s3_bucket_name=s3_bucket_name, + s3_bucket_qualified_name=s3_bucket_qualified_name, + ) + object_key = f"{prefix}/{name}" + assert attributes.name == name + assert attributes.connection_qualified_name == connection_qualified_name + assert attributes.aws_arn is UNSET + assert attributes.s3_object_key == construct_object_key(prefix, name) + assert ( + attributes.qualified_name + == f"{connection_qualified_name}/{attributes.s3_bucket_name}/{object_key}" + ) + assert attributes.connector_name == connection_qualified_name.split("/")[1] + assert attributes.s3_bucket_qualified_name == s3_bucket_qualified_name + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, S3_OBJECT_QUALIFIED_NAME, "qualified_name is required"), + (S3_OBJECT_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test updater validation for required parameters.""" + with pytest.raises(ValueError, match=message): + S3Object.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test updater returns minimal update instance.""" + sut = S3Object.updater(qualified_name=S3_OBJECT_QUALIFIED_NAME, name=S3_OBJECT_NAME) + assert sut.qualified_name == S3_OBJECT_QUALIFIED_NAME + assert sut.name == S3_OBJECT_NAME + + +def test_trim_to_required(): + """Test trim_to_required keeps qualified_name and name.""" + sut = S3Object.updater( + qualified_name=S3_OBJECT_QUALIFIED_NAME, name=S3_OBJECT_NAME + ).trim_to_required() + assert sut.qualified_name == S3_OBJECT_QUALIFIED_NAME + assert sut.name == S3_OBJECT_NAME diff --git a/tests_v9/unit/model/schema_test.py b/tests_v9/unit/model/schema_test.py new file mode 100644 index 000000000..ec657c8da --- /dev/null +++ b/tests_v9/unit/model/schema_test.py @@ -0,0 +1,257 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for Schema model in pyatlan_v9.""" + +import json + +import pytest +from msgspec import UNSET + +from pyatlan_v9.model import Schema +from pyatlan_v9.model.serde import Serde +from tests_v9.unit.model.constants import ( + CONNECTION_QUALIFIED_NAME, + CONNECTOR_TYPE, + DATABASE_NAME, + DATABASE_QUALIFIED_NAME, + SCHEMA_NAME, + SCHEMA_QUALIFIED_NAME, +) + + +@pytest.mark.parametrize( + "name, database_qualified_name, message", + [ + (None, DATABASE_QUALIFIED_NAME, "name is required"), + (SCHEMA_NAME, None, "database_qualified_name is required"), + (SCHEMA_NAME, "abc", "Invalid database_qualified_name"), + (SCHEMA_NAME, CONNECTION_QUALIFIED_NAME, "Invalid database_qualified_name"), + ( + SCHEMA_NAME, + SCHEMA_QUALIFIED_NAME, + "Invalid database_qualified_name", + ), + ], +) +def test_creator_with_missing_or_invalid_parameters_raises_value_error( + name: str, database_qualified_name: str, message: str +): + """Test that creator raises ValueError when required parameters are missing or invalid.""" + with pytest.raises(ValueError, match=message): + Schema.creator(name=name, database_qualified_name=database_qualified_name) + + +def test_creator(): + """Test that creator properly initializes a Schema with all derived fields.""" + sut = Schema.creator( + name=SCHEMA_NAME, database_qualified_name=DATABASE_QUALIFIED_NAME + ) + + assert sut.name == SCHEMA_NAME + assert sut.database_name == DATABASE_NAME + assert sut.connection_qualified_name == CONNECTION_QUALIFIED_NAME + assert sut.database_qualified_name == DATABASE_QUALIFIED_NAME + assert sut.qualified_name == SCHEMA_QUALIFIED_NAME + assert sut.connector_name == CONNECTOR_TYPE + assert sut.database.unique_attributes["qualifiedName"] == DATABASE_QUALIFIED_NAME + + +def test_overload_creator(): + """Test creator with all optional parameters provided.""" + sut = Schema.creator( + name=SCHEMA_NAME, + database_qualified_name=DATABASE_QUALIFIED_NAME, + database_name=DATABASE_NAME, + connection_qualified_name=CONNECTION_QUALIFIED_NAME, + ) + + assert sut.name == SCHEMA_NAME + assert sut.database_name == DATABASE_NAME + assert sut.connection_qualified_name == CONNECTION_QUALIFIED_NAME + assert sut.database_qualified_name == DATABASE_QUALIFIED_NAME + assert sut.qualified_name == SCHEMA_QUALIFIED_NAME + assert sut.connector_name == CONNECTOR_TYPE + assert sut.database.unique_attributes["qualifiedName"] == DATABASE_QUALIFIED_NAME + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, SCHEMA_QUALIFIED_NAME, "qualified_name is required"), + (SCHEMA_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test that updater raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + Schema.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test that updater creates a Schema instance for modification.""" + sut = Schema.updater(qualified_name=SCHEMA_QUALIFIED_NAME, name=SCHEMA_NAME) + + assert sut.qualified_name == SCHEMA_QUALIFIED_NAME + assert sut.name == SCHEMA_NAME + + +def test_trim_to_required(): + """Test that trim_to_required returns a Schema with only required fields.""" + sut = Schema.updater( + qualified_name=SCHEMA_QUALIFIED_NAME, name=SCHEMA_NAME + ).trim_to_required() + + assert sut.qualified_name == SCHEMA_QUALIFIED_NAME + assert sut.name == SCHEMA_NAME + + +def test_basic_construction(): + """Test basic Schema construction with minimal parameters.""" + schema = Schema(name=SCHEMA_NAME, qualified_name=SCHEMA_QUALIFIED_NAME) + + assert schema.name == SCHEMA_NAME + assert schema.qualified_name == SCHEMA_QUALIFIED_NAME + assert schema.type_name == "Schema" + + +def test_unset_fields(): + """Test that optional fields default to UNSET.""" + schema = Schema(name=SCHEMA_NAME, qualified_name=SCHEMA_QUALIFIED_NAME) + + assert schema.table_count is UNSET + assert schema.views_count is UNSET + assert schema.sql_external_location is UNSET + assert schema.query_count is UNSET + assert schema.sql_is_secure is UNSET + + +def test_optional_fields(): + """Test setting optional fields on Schema.""" + schema = Schema( + name=SCHEMA_NAME, + qualified_name=SCHEMA_QUALIFIED_NAME, + table_count=10, + views_count=5, + query_count=100, + ) + + assert schema.table_count == 10 + assert schema.views_count == 5 + assert schema.query_count == 100 + + +def test_none_vs_unset(): + """Test the distinction between None and UNSET values.""" + schema = Schema(name=SCHEMA_NAME, qualified_name=SCHEMA_QUALIFIED_NAME) + + assert schema.sql_is_secure is UNSET + schema.sql_is_secure = None + assert schema.sql_is_secure is None + assert schema.sql_is_secure is not UNSET + + +def test_serialization_to_json_nested(serde): + """Test serialization to nested JSON format (API format).""" + schema = Schema.creator( + name=SCHEMA_NAME, database_qualified_name=DATABASE_QUALIFIED_NAME + ) + + json_str = schema.to_json(nested=True, serde=serde) + data = json.loads(json_str) + + assert data["typeName"] == "Schema" + assert "attributes" in data + assert data["attributes"]["name"] == SCHEMA_NAME + assert data["attributes"]["qualifiedName"] == SCHEMA_QUALIFIED_NAME + + +def test_serialization_to_json_flat(serde): + """Test serialization to flat JSON format.""" + schema = Schema.creator( + name=SCHEMA_NAME, database_qualified_name=DATABASE_QUALIFIED_NAME + ) + + json_str = schema.to_json(nested=False, serde=serde) + + assert json_str + assert SCHEMA_NAME in json_str + assert SCHEMA_QUALIFIED_NAME in json_str + + +def test_deserialization_from_json(serde): + """Test deserialization from nested JSON format.""" + original = Schema.creator( + name=SCHEMA_NAME, database_qualified_name=DATABASE_QUALIFIED_NAME + ) + json_str = original.to_json(nested=True, serde=serde) + + schema = Schema.from_json(json_str, serde=serde) + + assert schema.name == SCHEMA_NAME + assert schema.qualified_name == SCHEMA_QUALIFIED_NAME + assert schema.type_name == "Schema" + + +def test_round_trip_serialization(serde): + """Test that serialization and deserialization preserve all data.""" + original = Schema.creator( + name=SCHEMA_NAME, database_qualified_name=DATABASE_QUALIFIED_NAME + ) + original.table_count = 5 + original.views_count = 3 + + json_str = original.to_json(nested=True, serde=serde) + restored = Schema.from_json(json_str, serde=serde) + + assert restored.name == original.name + assert restored.qualified_name == original.qualified_name + assert restored.table_count == original.table_count + assert restored.views_count == original.views_count + + +def test_with_custom_serde(): + """Test that a custom Serde instance can be used for serialization.""" + custom_serde = Serde() + schema = Schema.creator( + name=SCHEMA_NAME, database_qualified_name=DATABASE_QUALIFIED_NAME + ) + + json_str = schema.to_json(nested=True, serde=custom_serde) + restored = Schema.from_json(json_str, serde=custom_serde) + + assert restored.name == schema.name + assert restored.qualified_name == schema.qualified_name + + +def test_type_name_defaults(): + """Test that type_name defaults to 'Schema'.""" + schema = Schema(name=SCHEMA_NAME, qualified_name=SCHEMA_QUALIFIED_NAME) + assert schema.type_name == "Schema" + + +def test_creator_with_guid(): + """Test that creator initializes a temporary GUID for new assets.""" + schema = Schema.creator( + name=SCHEMA_NAME, database_qualified_name=DATABASE_QUALIFIED_NAME + ) + + assert schema.guid is not UNSET + assert schema.guid is not None + assert isinstance(schema.guid, str) + assert schema.guid.startswith("-") + + +def test_sql_fields(): + """Test setting SQL-specific fields (database names).""" + schema = Schema( + name=SCHEMA_NAME, + qualified_name=SCHEMA_QUALIFIED_NAME, + database_name=DATABASE_NAME, + database_qualified_name=DATABASE_QUALIFIED_NAME, + ) + + assert schema.database_name == DATABASE_NAME + assert schema.database_qualified_name == DATABASE_QUALIFIED_NAME diff --git a/tests_v9/unit/model/superset_chart_test.py b/tests_v9/unit/model/superset_chart_test.py new file mode 100644 index 000000000..6c55c54c6 --- /dev/null +++ b/tests_v9/unit/model/superset_chart_test.py @@ -0,0 +1,103 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for SupersetChart model in pyatlan_v9.""" + +import pytest + +from pyatlan_v9.model import SupersetChart +from tests_v9.unit.model.constants import ( + SUPERSET_CHART_NAME, + SUPERSET_CHART_QUALIFIED_NAME, + SUPERSET_CONNECTION_QUALIFIED_NAME, + SUPERSET_CONNECTOR_TYPE, + SUPERSET_DASHBOARD_QUALIFIED_NAME, +) + + +@pytest.mark.parametrize( + "name, superset_dashboard_qualified_name, message", + [ + (None, "connection/name", "name is required"), + (SUPERSET_CHART_NAME, None, "superset_dashboard_qualified_name is required"), + ], +) +def test_creator_with_missing_parameters_raise_value_error( + name: str, superset_dashboard_qualified_name: str, message: str +): + """Test creator validates required parameters.""" + with pytest.raises(ValueError, match=message): + SupersetChart.creator( + name=name, + superset_dashboard_qualified_name=superset_dashboard_qualified_name, + ) + + +def test_creator(): + """Test creator initializes expected derived fields.""" + sut = SupersetChart.creator( + name=SUPERSET_CHART_NAME, + superset_dashboard_qualified_name=SUPERSET_DASHBOARD_QUALIFIED_NAME, + ) + + assert sut.name == SUPERSET_CHART_NAME + assert sut.superset_dashboard_qualified_name == SUPERSET_DASHBOARD_QUALIFIED_NAME + assert sut.connection_qualified_name == SUPERSET_CONNECTION_QUALIFIED_NAME + assert ( + sut.qualified_name + == f"{SUPERSET_DASHBOARD_QUALIFIED_NAME}/{SUPERSET_CHART_NAME}" + ) + assert sut.connector_name == SUPERSET_CONNECTOR_TYPE + + +def test_overload_creator(): + """Test creator accepts explicit connection qualified name.""" + sut = SupersetChart.creator( + name=SUPERSET_CHART_NAME, + superset_dashboard_qualified_name=SUPERSET_DASHBOARD_QUALIFIED_NAME, + connection_qualified_name=SUPERSET_CONNECTION_QUALIFIED_NAME, + ) + + assert sut.name == SUPERSET_CHART_NAME + assert sut.superset_dashboard_qualified_name == SUPERSET_DASHBOARD_QUALIFIED_NAME + assert sut.connection_qualified_name == SUPERSET_CONNECTION_QUALIFIED_NAME + assert ( + sut.qualified_name + == f"{SUPERSET_DASHBOARD_QUALIFIED_NAME}/{SUPERSET_CHART_NAME}" + ) + assert sut.connector_name == SUPERSET_CONNECTOR_TYPE + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, SUPERSET_CHART_QUALIFIED_NAME, "qualified_name is required"), + (SUPERSET_CHART_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test updater validates required parameters.""" + with pytest.raises(ValueError, match=message): + SupersetChart.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test updater creates SupersetChart for modification.""" + sut = SupersetChart.updater( + qualified_name=SUPERSET_CHART_QUALIFIED_NAME, name=SUPERSET_CHART_NAME + ) + + assert sut.qualified_name == SUPERSET_CHART_QUALIFIED_NAME + assert sut.name == SUPERSET_CHART_NAME + + +def test_trim_to_required(): + """Test trim_to_required keeps only updater-required fields.""" + sut = SupersetChart.updater( + name=SUPERSET_CHART_NAME, qualified_name=SUPERSET_CHART_QUALIFIED_NAME + ).trim_to_required() + + assert sut.name == SUPERSET_CHART_NAME + assert sut.qualified_name == SUPERSET_CHART_QUALIFIED_NAME diff --git a/tests_v9/unit/model/superset_dashboard_test.py b/tests_v9/unit/model/superset_dashboard_test.py new file mode 100644 index 000000000..ad4ef3408 --- /dev/null +++ b/tests_v9/unit/model/superset_dashboard_test.py @@ -0,0 +1,82 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for SupersetDashboard model in pyatlan_v9.""" + +import pytest + +from pyatlan_v9.model import SupersetDashboard +from tests_v9.unit.model.constants import ( + SUPERSET_CONNECTION_QUALIFIED_NAME, + SUPERSET_CONNECTOR_TYPE, + SUPERSET_DASHBOARD_NAME, + SUPERSET_DASHBOARD_QUALIFIED_NAME, +) + + +@pytest.mark.parametrize( + "name, connection_qualified_name, message", + [ + (None, "connection/name", "name is required"), + (SUPERSET_DASHBOARD_NAME, None, "connection_qualified_name is required"), + ], +) +def test_creator_with_missing_parameters_raise_value_error( + name: str, connection_qualified_name: str, message: str +): + """Test creator validates required parameters.""" + with pytest.raises(ValueError, match=message): + SupersetDashboard.creator( + name=name, connection_qualified_name=connection_qualified_name + ) + + +def test_creator(): + """Test creator initializes expected derived fields.""" + sut = SupersetDashboard.creator( + name=SUPERSET_DASHBOARD_NAME, + connection_qualified_name=SUPERSET_CONNECTION_QUALIFIED_NAME, + ) + + assert sut.name == SUPERSET_DASHBOARD_NAME + assert sut.connection_qualified_name == SUPERSET_CONNECTION_QUALIFIED_NAME + assert ( + sut.qualified_name + == f"{SUPERSET_CONNECTION_QUALIFIED_NAME}/{SUPERSET_DASHBOARD_NAME}" + ) + assert sut.connector_name == SUPERSET_CONNECTOR_TYPE + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, SUPERSET_DASHBOARD_QUALIFIED_NAME, "qualified_name is required"), + (SUPERSET_DASHBOARD_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test updater validates required parameters.""" + with pytest.raises(ValueError, match=message): + SupersetDashboard.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test updater creates SupersetDashboard for modification.""" + sut = SupersetDashboard.updater( + qualified_name=SUPERSET_DASHBOARD_QUALIFIED_NAME, name=SUPERSET_DASHBOARD_NAME + ) + + assert sut.qualified_name == SUPERSET_DASHBOARD_QUALIFIED_NAME + assert sut.name == SUPERSET_DASHBOARD_NAME + + +def test_trim_to_required(): + """Test trim_to_required keeps only updater-required fields.""" + sut = SupersetDashboard.updater( + qualified_name=SUPERSET_DASHBOARD_QUALIFIED_NAME, name=SUPERSET_DASHBOARD_NAME + ).trim_to_required() + + assert sut.qualified_name == SUPERSET_DASHBOARD_QUALIFIED_NAME + assert sut.name == SUPERSET_DASHBOARD_NAME diff --git a/tests_v9/unit/model/superset_dataset_test.py b/tests_v9/unit/model/superset_dataset_test.py new file mode 100644 index 000000000..7c630288e --- /dev/null +++ b/tests_v9/unit/model/superset_dataset_test.py @@ -0,0 +1,107 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for SupersetDataset model in pyatlan_v9.""" + +import pytest + +from pyatlan_v9.model import SupersetDataset +from tests_v9.unit.model.constants import ( + SUPERSET_CONNECTION_QUALIFIED_NAME, + SUPERSET_CONNECTOR_TYPE, + SUPERSET_DASHBOARD_QUALIFIED_NAME, + SUPERSET_DATASET_NAME, + SUPERSET_DATASET_QUALIFIED_NAME, +) + + +@pytest.mark.parametrize( + "name, superset_dashboard_qualified_name, message", + [ + (None, "connection/name", "name is required"), + ( + SUPERSET_DATASET_NAME, + None, + "superset_dashboard_qualified_name is required", + ), + ], +) +def test_creator_with_missing_parameters_raise_value_error( + name: str, superset_dashboard_qualified_name: str, message: str +): + """Test creator validates required parameters.""" + with pytest.raises(ValueError, match=message): + SupersetDataset.creator( + name=name, + superset_dashboard_qualified_name=superset_dashboard_qualified_name, + ) + + +def test_creator(): + """Test creator initializes expected derived fields.""" + sut = SupersetDataset.creator( + name=SUPERSET_DATASET_NAME, + superset_dashboard_qualified_name=SUPERSET_DASHBOARD_QUALIFIED_NAME, + ) + + assert sut.name == SUPERSET_DATASET_NAME + assert sut.superset_dashboard_qualified_name == SUPERSET_DASHBOARD_QUALIFIED_NAME + assert sut.connection_qualified_name == SUPERSET_CONNECTION_QUALIFIED_NAME + assert ( + sut.qualified_name + == f"{SUPERSET_DASHBOARD_QUALIFIED_NAME}/{SUPERSET_DATASET_NAME}" + ) + assert sut.connector_name == SUPERSET_CONNECTOR_TYPE + + +def test_overload_creator(): + """Test creator accepts explicit connection qualified name.""" + sut = SupersetDataset.creator( + name=SUPERSET_DATASET_NAME, + superset_dashboard_qualified_name=SUPERSET_DASHBOARD_QUALIFIED_NAME, + connection_qualified_name=SUPERSET_CONNECTION_QUALIFIED_NAME, + ) + + assert sut.name == SUPERSET_DATASET_NAME + assert sut.superset_dashboard_qualified_name == SUPERSET_DASHBOARD_QUALIFIED_NAME + assert sut.connection_qualified_name == SUPERSET_CONNECTION_QUALIFIED_NAME + assert ( + sut.qualified_name + == f"{SUPERSET_DASHBOARD_QUALIFIED_NAME}/{SUPERSET_DATASET_NAME}" + ) + assert sut.connector_name == SUPERSET_CONNECTOR_TYPE + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, SUPERSET_DATASET_QUALIFIED_NAME, "qualified_name is required"), + (SUPERSET_DATASET_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test updater validates required parameters.""" + with pytest.raises(ValueError, match=message): + SupersetDataset.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test updater creates SupersetDataset for modification.""" + sut = SupersetDataset.updater( + qualified_name=SUPERSET_DATASET_QUALIFIED_NAME, name=SUPERSET_DATASET_NAME + ) + + assert sut.qualified_name == SUPERSET_DATASET_QUALIFIED_NAME + assert sut.name == SUPERSET_DATASET_NAME + + +def test_trim_to_required(): + """Test trim_to_required keeps only updater-required fields.""" + sut = SupersetDataset.updater( + name=SUPERSET_DATASET_NAME, qualified_name=SUPERSET_DATASET_QUALIFIED_NAME + ).trim_to_required() + + assert sut.name == SUPERSET_DATASET_NAME + assert sut.qualified_name == SUPERSET_DATASET_QUALIFIED_NAME diff --git a/tests_v9/unit/model/table_partition_test.py b/tests_v9/unit/model/table_partition_test.py new file mode 100644 index 000000000..d5e34214a --- /dev/null +++ b/tests_v9/unit/model/table_partition_test.py @@ -0,0 +1,278 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for TablePartition model in pyatlan_v9.""" + +import json + +import pytest +from msgspec import UNSET + +from pyatlan_v9.model import TablePartition +from pyatlan_v9.model.serde import Serde +from tests_v9.unit.model.constants import ( + CONNECTION_QUALIFIED_NAME, + CONNECTOR_TYPE, + DATABASE_NAME, + DATABASE_QUALIFIED_NAME, + SCHEMA_NAME, + SCHEMA_QUALIFIED_NAME, + TABLE_NAME, + TABLE_PARTITION_NAME, + TABLE_QUALIFIED_NAME, +) + +TABLE_PARTITION_QUALIFIED_NAME = f"{SCHEMA_QUALIFIED_NAME}/{TABLE_PARTITION_NAME}" + + +@pytest.mark.parametrize( + "name, table_qualified_name, message", + [ + (None, TABLE_QUALIFIED_NAME, "name is required"), + (TABLE_PARTITION_NAME, None, "table_qualified_name is required"), + ], +) +def test_creator_with_missing_parameters_raises_value_error( + name: str, table_qualified_name: str, message: str +): + """Test that creator raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + TablePartition.creator(name=name, table_qualified_name=table_qualified_name) + + +def test_creator(): + """Test that creator properly initializes a TablePartition with all derived fields.""" + sut = TablePartition.creator( + name=TABLE_PARTITION_NAME, + table_qualified_name=TABLE_QUALIFIED_NAME, + ) + + assert sut.name == TABLE_PARTITION_NAME + assert sut.database_name == DATABASE_NAME + assert sut.connection_qualified_name == CONNECTION_QUALIFIED_NAME + assert sut.database_qualified_name == DATABASE_QUALIFIED_NAME + assert sut.qualified_name == TABLE_PARTITION_QUALIFIED_NAME + assert sut.schema_qualified_name == SCHEMA_QUALIFIED_NAME + assert sut.schema_name == SCHEMA_NAME + assert sut.connector_name == CONNECTOR_TYPE + assert sut.table_name == TABLE_NAME + assert sut.table_qualified_name == TABLE_QUALIFIED_NAME + + +def test_overload_creator(): + """Test creator with all optional parameters provided.""" + sut = TablePartition.creator( + name=TABLE_PARTITION_NAME, + connection_qualified_name=CONNECTION_QUALIFIED_NAME, + database_name=DATABASE_NAME, + database_qualified_name=DATABASE_QUALIFIED_NAME, + schema_name=SCHEMA_NAME, + schema_qualified_name=SCHEMA_QUALIFIED_NAME, + table_name=TABLE_NAME, + table_qualified_name=TABLE_QUALIFIED_NAME, + ) + + assert sut.name == TABLE_PARTITION_NAME + assert sut.database_name == DATABASE_NAME + assert sut.connection_qualified_name == CONNECTION_QUALIFIED_NAME + assert sut.database_qualified_name == DATABASE_QUALIFIED_NAME + assert sut.qualified_name == TABLE_PARTITION_QUALIFIED_NAME + assert sut.schema_qualified_name == SCHEMA_QUALIFIED_NAME + assert sut.schema_name == SCHEMA_NAME + assert sut.connector_name == CONNECTOR_TYPE + assert sut.table_name == TABLE_NAME + assert sut.table_qualified_name == TABLE_QUALIFIED_NAME + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, TABLE_PARTITION_QUALIFIED_NAME, "qualified_name is required"), + (TABLE_PARTITION_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test that updater raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + TablePartition.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test that updater creates a TablePartition instance for modification.""" + sut = TablePartition.updater( + qualified_name=TABLE_PARTITION_QUALIFIED_NAME, name=TABLE_PARTITION_NAME + ) + + assert sut.qualified_name == TABLE_PARTITION_QUALIFIED_NAME + assert sut.name == TABLE_PARTITION_NAME + + +def test_trim_to_required(): + """Test that trim_to_required returns a TablePartition with only required fields.""" + sut = TablePartition.updater( + qualified_name=TABLE_PARTITION_QUALIFIED_NAME, name=TABLE_PARTITION_NAME + ).trim_to_required() + + assert sut.qualified_name == TABLE_PARTITION_QUALIFIED_NAME + assert sut.name == TABLE_PARTITION_NAME + + +def test_basic_construction(): + """Test basic TablePartition construction with minimal parameters.""" + tp = TablePartition( + name=TABLE_PARTITION_NAME, qualified_name=TABLE_PARTITION_QUALIFIED_NAME + ) + + assert tp.name == TABLE_PARTITION_NAME + assert tp.qualified_name == TABLE_PARTITION_QUALIFIED_NAME + assert tp.type_name == "TablePartition" + + +def test_unset_fields(): + """Test that optional fields default to UNSET.""" + tp = TablePartition( + name=TABLE_PARTITION_NAME, qualified_name=TABLE_PARTITION_QUALIFIED_NAME + ) + + assert tp.column_count is UNSET + assert tp.row_count is UNSET + assert tp.size_bytes is UNSET + assert tp.constraint is UNSET + assert tp.partition_strategy is UNSET + assert tp.external_location is UNSET + + +def test_optional_fields(): + """Test setting optional fields on TablePartition.""" + tp = TablePartition( + name=TABLE_PARTITION_NAME, + qualified_name=TABLE_PARTITION_QUALIFIED_NAME, + column_count=5, + row_count=100, + partition_strategy="HASH", + constraint="date > '2024-01-01'", + ) + + assert tp.column_count == 5 + assert tp.row_count == 100 + assert tp.partition_strategy == "HASH" + assert tp.constraint == "date > '2024-01-01'" + + +def test_none_vs_unset(): + """Test the distinction between None and UNSET values.""" + tp = TablePartition( + name=TABLE_PARTITION_NAME, qualified_name=TABLE_PARTITION_QUALIFIED_NAME + ) + + assert tp.alias is UNSET + tp.alias = None + assert tp.alias is None + assert tp.alias is not UNSET + + +def test_serialization_to_json_nested(serde): + """Test serialization to nested JSON format (API format).""" + tp = TablePartition.creator( + name=TABLE_PARTITION_NAME, table_qualified_name=TABLE_QUALIFIED_NAME + ) + + json_str = tp.to_json(nested=True, serde=serde) + data = json.loads(json_str) + + assert data["typeName"] == "TablePartition" + assert "attributes" in data + assert data["attributes"]["name"] == TABLE_PARTITION_NAME + assert data["attributes"]["qualifiedName"] == TABLE_PARTITION_QUALIFIED_NAME + + +def test_serialization_to_json_flat(serde): + """Test serialization to flat JSON format.""" + tp = TablePartition.creator( + name=TABLE_PARTITION_NAME, table_qualified_name=TABLE_QUALIFIED_NAME + ) + + json_str = tp.to_json(nested=False, serde=serde) + + assert json_str + assert TABLE_PARTITION_NAME in json_str + assert TABLE_PARTITION_QUALIFIED_NAME in json_str + + +def test_deserialization_from_json(serde): + """Test deserialization from nested JSON format.""" + original = TablePartition.creator( + name=TABLE_PARTITION_NAME, table_qualified_name=TABLE_QUALIFIED_NAME + ) + json_str = original.to_json(nested=True, serde=serde) + + tp = TablePartition.from_json(json_str, serde=serde) + + assert tp.name == TABLE_PARTITION_NAME + assert tp.qualified_name == TABLE_PARTITION_QUALIFIED_NAME + assert tp.type_name == "TablePartition" + + +def test_round_trip_serialization(serde): + """Test that serialization and deserialization preserve all data.""" + original = TablePartition.creator( + name=TABLE_PARTITION_NAME, table_qualified_name=TABLE_QUALIFIED_NAME + ) + original.column_count = 5 + original.row_count = 100 + original.partition_strategy = "HASH" + + json_str = original.to_json(nested=True, serde=serde) + restored = TablePartition.from_json(json_str, serde=serde) + + assert restored.name == original.name + assert restored.qualified_name == original.qualified_name + assert restored.column_count == original.column_count + assert restored.row_count == original.row_count + assert restored.partition_strategy == original.partition_strategy + + +def test_with_custom_serde(): + """Test that a custom Serde instance can be used for serialization.""" + custom_serde = Serde() + tp = TablePartition.creator( + name=TABLE_PARTITION_NAME, table_qualified_name=TABLE_QUALIFIED_NAME + ) + + json_str = tp.to_json(nested=True, serde=custom_serde) + restored = TablePartition.from_json(json_str, serde=custom_serde) + + assert restored.name == tp.name + assert restored.qualified_name == tp.qualified_name + + +def test_type_name_defaults(): + """Test that type_name defaults to 'TablePartition'.""" + tp = TablePartition( + name=TABLE_PARTITION_NAME, qualified_name=TABLE_PARTITION_QUALIFIED_NAME + ) + assert tp.type_name == "TablePartition" + + +def test_creator_with_guid(): + """Test that creator initializes a temporary GUID for new assets.""" + tp = TablePartition.creator( + name=TABLE_PARTITION_NAME, table_qualified_name=TABLE_QUALIFIED_NAME + ) + + assert tp.guid is not UNSET + assert tp.guid is not None + assert isinstance(tp.guid, str) + assert tp.guid.startswith("-") + + +def test_parent_table_relationship(): + """Test that creator sets the parent_table relationship.""" + tp = TablePartition.creator( + name=TABLE_PARTITION_NAME, table_qualified_name=TABLE_QUALIFIED_NAME + ) + + assert tp.parent_table is not None + assert tp.parent_table.unique_attributes["qualifiedName"] == TABLE_QUALIFIED_NAME diff --git a/tests_v9/unit/model/table_test.py b/tests_v9/unit/model/table_test.py new file mode 100644 index 000000000..3fb694ab8 --- /dev/null +++ b/tests_v9/unit/model/table_test.py @@ -0,0 +1,252 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for Table model in pyatlan_v9.""" + +import json + +import pytest +from msgspec import UNSET + +from pyatlan_v9.model import Table +from pyatlan_v9.model.serde import Serde +from tests_v9.unit.model.constants import ( + CONNECTION_QUALIFIED_NAME, + CONNECTOR_TYPE, + DATABASE_NAME, + DATABASE_QUALIFIED_NAME, + SCHEMA_NAME, + SCHEMA_QUALIFIED_NAME, + TABLE_NAME, + TABLE_QUALIFIED_NAME, +) + + +@pytest.mark.parametrize( + "name, schema_qualified_name, message", + [ + (None, "connection/name", "name is required"), + (TABLE_NAME, None, "schema_qualified_name is required"), + ], +) +def test_create_with_missing_parameters_raise_value_error( + name: str, schema_qualified_name: str, message: str +): + """Test that creator raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + Table.create(name=name, schema_qualified_name=schema_qualified_name) + + +def test_create(): + """Test that creator properly initializes a Table with all derived fields.""" + sut = Table.create(name=TABLE_NAME, schema_qualified_name=SCHEMA_QUALIFIED_NAME) + + assert sut.name == TABLE_NAME + assert sut.database_name == DATABASE_NAME + assert sut.connection_qualified_name == CONNECTION_QUALIFIED_NAME + assert sut.database_qualified_name == DATABASE_QUALIFIED_NAME + assert sut.qualified_name == TABLE_QUALIFIED_NAME + assert sut.schema_qualified_name == SCHEMA_QUALIFIED_NAME + assert sut.schema_name == SCHEMA_NAME + assert sut.connector_name == CONNECTOR_TYPE + assert sut.atlan_schema.unique_attributes["qualifiedName"] == SCHEMA_QUALIFIED_NAME + + +def test_overload_creator(): + """Test creator with all optional parameters provided.""" + sut = Table.creator( + name=TABLE_NAME, + schema_qualified_name=SCHEMA_QUALIFIED_NAME, + schema_name=SCHEMA_NAME, + database_name=DATABASE_NAME, + database_qualified_name=DATABASE_QUALIFIED_NAME, + connection_qualified_name=CONNECTION_QUALIFIED_NAME, + ) + + assert sut.name == TABLE_NAME + assert sut.database_name == DATABASE_NAME + assert sut.connection_qualified_name == CONNECTION_QUALIFIED_NAME + assert sut.database_qualified_name == DATABASE_QUALIFIED_NAME + assert sut.qualified_name == TABLE_QUALIFIED_NAME + assert sut.schema_qualified_name == SCHEMA_QUALIFIED_NAME + assert sut.schema_name == SCHEMA_NAME + assert sut.connector_name == CONNECTOR_TYPE + assert sut.atlan_schema.unique_attributes["qualifiedName"] == SCHEMA_QUALIFIED_NAME + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, TABLE_QUALIFIED_NAME, "qualified_name is required"), + (TABLE_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test that updater raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + Table.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test that updater creates a Table instance for modification.""" + sut = Table.updater(qualified_name=TABLE_QUALIFIED_NAME, name=TABLE_NAME) + + assert sut.qualified_name == TABLE_QUALIFIED_NAME + assert sut.name == TABLE_NAME + + +def test_trim_to_required(): + """Test that trim_to_required returns a Table with only required fields.""" + sut = Table.updater( + qualified_name=TABLE_QUALIFIED_NAME, name=TABLE_NAME + ).trim_to_required() + + assert sut.qualified_name == TABLE_QUALIFIED_NAME + assert sut.name == TABLE_NAME + + +def test_basic_construction(): + """Test basic Table construction with minimal parameters.""" + table = Table(name=TABLE_NAME, qualified_name=TABLE_QUALIFIED_NAME) + + assert table.name == TABLE_NAME + assert table.qualified_name == TABLE_QUALIFIED_NAME + assert table.type_name == "Table" + + +def test_unset_fields(): + """Test that optional fields default to UNSET.""" + table = Table(name=TABLE_NAME, qualified_name=TABLE_QUALIFIED_NAME) + + assert table.column_count is UNSET + assert table.row_count is UNSET + assert table.size_bytes is UNSET + assert table.is_temporary is UNSET + assert table.is_partitioned is UNSET + + +def test_optional_fields(): + """Test setting optional fields on Table.""" + table = Table( + name=TABLE_NAME, + qualified_name=TABLE_QUALIFIED_NAME, + column_count=10, + row_count=1000, + size_bytes=5000, + ) + + assert table.column_count == 10 + assert table.row_count == 1000 + assert table.size_bytes == 5000 + + +def test_none_vs_unset(): + """Test the distinction between None and UNSET values.""" + table = Table(name=TABLE_NAME, qualified_name=TABLE_QUALIFIED_NAME) + + assert table.alias is UNSET + table.alias = None + assert table.alias is None + assert table.alias is not UNSET + + +def test_serialization_to_json_nested(serde): + """Test serialization to nested JSON format (API format).""" + table = Table.create(name=TABLE_NAME, schema_qualified_name=SCHEMA_QUALIFIED_NAME) + + json_str = table.to_json(nested=True, serde=serde) + data = json.loads(json_str) + + assert data["typeName"] == "Table" + assert "attributes" in data + assert data["attributes"]["name"] == TABLE_NAME + assert data["attributes"]["qualifiedName"] == TABLE_QUALIFIED_NAME + + +def test_serialization_to_json_flat(serde): + """Test serialization to flat JSON format.""" + table = Table.create(name=TABLE_NAME, schema_qualified_name=SCHEMA_QUALIFIED_NAME) + + json_str = table.to_json(nested=False, serde=serde) + + assert json_str + assert TABLE_NAME in json_str + assert TABLE_QUALIFIED_NAME in json_str + + +def test_deserialization_from_json(serde): + """Test deserialization from nested JSON format.""" + original = Table.create( + name=TABLE_NAME, schema_qualified_name=SCHEMA_QUALIFIED_NAME + ) + json_str = original.to_json(nested=True, serde=serde) + + table = Table.from_json(json_str, serde=serde) + + assert table.name == TABLE_NAME + assert table.qualified_name == TABLE_QUALIFIED_NAME + assert table.type_name == "Table" + + +def test_round_trip_serialization(serde): + """Test that serialization and deserialization preserve all data.""" + original = Table.create( + name=TABLE_NAME, schema_qualified_name=SCHEMA_QUALIFIED_NAME + ) + original.column_count = 5 + original.row_count = 100 + + json_str = original.to_json(nested=True, serde=serde) + restored = Table.from_json(json_str, serde=serde) + + assert restored.name == original.name + assert restored.qualified_name == original.qualified_name + assert restored.column_count == original.column_count + assert restored.row_count == original.row_count + + +def test_with_custom_serde(): + """Test that a custom Serde instance can be used for serialization.""" + custom_serde = Serde() + table = Table.create(name=TABLE_NAME, schema_qualified_name=SCHEMA_QUALIFIED_NAME) + + json_str = table.to_json(nested=True, serde=custom_serde) + restored = Table.from_json(json_str, serde=custom_serde) + + assert restored.name == table.name + assert restored.qualified_name == table.qualified_name + + +def test_type_name_defaults(): + """Test that type_name defaults to 'Table'.""" + table = Table(name=TABLE_NAME, qualified_name=TABLE_QUALIFIED_NAME) + assert table.type_name == "Table" + + +def test_creator_with_guid(): + """Test that creator initializes a temporary GUID for new assets.""" + table = Table.creator(name=TABLE_NAME, schema_qualified_name=SCHEMA_QUALIFIED_NAME) + + assert table.guid is not UNSET + assert table.guid is not None + assert isinstance(table.guid, str) + assert table.guid.startswith("-") + + +def test_sql_fields(): + """Test setting SQL-specific fields (database, schema names).""" + table = Table( + name=TABLE_NAME, + qualified_name=TABLE_QUALIFIED_NAME, + database_name=DATABASE_NAME, + database_qualified_name=DATABASE_QUALIFIED_NAME, + schema_name=SCHEMA_NAME, + schema_qualified_name=SCHEMA_QUALIFIED_NAME, + ) + + assert table.database_name == DATABASE_NAME + assert table.database_qualified_name == DATABASE_QUALIFIED_NAME + assert table.schema_name == SCHEMA_NAME + assert table.schema_qualified_name == SCHEMA_QUALIFIED_NAME diff --git a/tests_v9/unit/model/view_test.py b/tests_v9/unit/model/view_test.py new file mode 100644 index 000000000..9c768a5e4 --- /dev/null +++ b/tests_v9/unit/model/view_test.py @@ -0,0 +1,253 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for View model in pyatlan_v9.""" + +import json + +import pytest +from msgspec import UNSET + +from pyatlan_v9.model import View +from pyatlan_v9.model.serde import Serde +from tests_v9.unit.model.constants import ( + CONNECTION_QUALIFIED_NAME, + CONNECTOR_TYPE, + DATABASE_NAME, + DATABASE_QUALIFIED_NAME, + SCHEMA_NAME, + SCHEMA_QUALIFIED_NAME, + TABLE_QUALIFIED_NAME, + VIEW_COLUMN_QUALIFIED_NAME, + VIEW_NAME, + VIEW_QUALIFIED_NAME, +) + + +@pytest.mark.parametrize( + "name, schema_qualified_name, message", + [ + (None, SCHEMA_QUALIFIED_NAME, "name is required"), + (VIEW_NAME, None, "schema_qualified_name is required"), + (VIEW_NAME, CONNECTION_QUALIFIED_NAME, "Invalid schema_qualified_name"), + (VIEW_NAME, DATABASE_QUALIFIED_NAME, "Invalid schema_qualified_name"), + (VIEW_NAME, TABLE_QUALIFIED_NAME, "Invalid schema_qualified_name"), + (VIEW_NAME, VIEW_COLUMN_QUALIFIED_NAME, "Invalid schema_qualified_name"), + ], +) +def test_creator_with_missing_or_invalid_parameters_raises_value_error( + name: str, schema_qualified_name: str, message: str +): + """Test that creator raises ValueError when required parameters are missing or invalid.""" + with pytest.raises(ValueError, match=message): + View.creator(name=name, schema_qualified_name=schema_qualified_name) + + +def test_creator(): + """Test that creator properly initializes a View with all derived fields.""" + sut = View.creator(name=VIEW_NAME, schema_qualified_name=SCHEMA_QUALIFIED_NAME) + + assert sut.name == VIEW_NAME + assert sut.database_name == DATABASE_NAME + assert sut.connection_qualified_name == CONNECTION_QUALIFIED_NAME + assert sut.database_qualified_name == DATABASE_QUALIFIED_NAME + assert sut.qualified_name == VIEW_QUALIFIED_NAME + assert sut.schema_qualified_name == SCHEMA_QUALIFIED_NAME + assert sut.schema_name == SCHEMA_NAME + assert sut.connector_name == CONNECTOR_TYPE + assert sut.atlan_schema.unique_attributes["qualifiedName"] == SCHEMA_QUALIFIED_NAME + + +def test_overload_creator(): + """Test creator with all optional parameters provided.""" + sut = View.creator( + name=VIEW_NAME, + schema_qualified_name=SCHEMA_QUALIFIED_NAME, + schema_name=SCHEMA_NAME, + database_name=DATABASE_NAME, + connection_qualified_name=CONNECTION_QUALIFIED_NAME, + ) + + assert sut.name == VIEW_NAME + assert sut.database_name == DATABASE_NAME + assert sut.connection_qualified_name == CONNECTION_QUALIFIED_NAME + assert sut.database_qualified_name == DATABASE_QUALIFIED_NAME + assert sut.qualified_name == VIEW_QUALIFIED_NAME + assert sut.schema_qualified_name == SCHEMA_QUALIFIED_NAME + assert sut.schema_name == SCHEMA_NAME + assert sut.connector_name == CONNECTOR_TYPE + assert sut.atlan_schema.unique_attributes["qualifiedName"] == SCHEMA_QUALIFIED_NAME + + +@pytest.mark.parametrize( + "qualified_name, name, message", + [ + (None, VIEW_QUALIFIED_NAME, "qualified_name is required"), + (VIEW_NAME, None, "name is required"), + ], +) +def test_updater_with_invalid_parameter_raises_value_error( + qualified_name: str, name: str, message: str +): + """Test that updater raises ValueError when required parameters are missing.""" + with pytest.raises(ValueError, match=message): + View.updater(qualified_name=qualified_name, name=name) + + +def test_updater(): + """Test that updater creates a View instance for modification.""" + sut = View.updater(qualified_name=VIEW_QUALIFIED_NAME, name=VIEW_NAME) + + assert sut.qualified_name == VIEW_QUALIFIED_NAME + assert sut.name == VIEW_NAME + + +def test_trim_to_required(): + """Test that trim_to_required returns a View with only required fields.""" + sut = View.updater( + qualified_name=VIEW_QUALIFIED_NAME, name=VIEW_NAME + ).trim_to_required() + + assert sut.qualified_name == VIEW_QUALIFIED_NAME + assert sut.name == VIEW_NAME + + +def test_basic_construction(): + """Test basic View construction with minimal parameters.""" + view = View(name=VIEW_NAME, qualified_name=VIEW_QUALIFIED_NAME) + + assert view.name == VIEW_NAME + assert view.qualified_name == VIEW_QUALIFIED_NAME + assert view.type_name == "View" + + +def test_unset_fields(): + """Test that optional fields default to UNSET.""" + view = View(name=VIEW_NAME, qualified_name=VIEW_QUALIFIED_NAME) + + assert view.column_count is UNSET + assert view.row_count is UNSET + assert view.size_bytes is UNSET + assert view.is_temporary is UNSET + assert view.definition is UNSET + + +def test_optional_fields(): + """Test setting optional fields on View.""" + view = View( + name=VIEW_NAME, + qualified_name=VIEW_QUALIFIED_NAME, + column_count=5, + row_count=100, + size_bytes=1024, + ) + + assert view.column_count == 5 + assert view.row_count == 100 + assert view.size_bytes == 1024 + + +def test_none_vs_unset(): + """Test the distinction between None and UNSET values.""" + view = View(name=VIEW_NAME, qualified_name=VIEW_QUALIFIED_NAME) + + assert view.alias is UNSET + view.alias = None + assert view.alias is None + assert view.alias is not UNSET + + +def test_serialization_to_json_nested(serde): + """Test serialization to nested JSON format (API format).""" + view = View.creator(name=VIEW_NAME, schema_qualified_name=SCHEMA_QUALIFIED_NAME) + + json_str = view.to_json(nested=True, serde=serde) + data = json.loads(json_str) + + assert data["typeName"] == "View" + assert "attributes" in data + assert data["attributes"]["name"] == VIEW_NAME + assert data["attributes"]["qualifiedName"] == VIEW_QUALIFIED_NAME + + +def test_serialization_to_json_flat(serde): + """Test serialization to flat JSON format.""" + view = View.creator(name=VIEW_NAME, schema_qualified_name=SCHEMA_QUALIFIED_NAME) + + json_str = view.to_json(nested=False, serde=serde) + + assert json_str + assert VIEW_NAME in json_str + assert VIEW_QUALIFIED_NAME in json_str + + +def test_deserialization_from_json(serde): + """Test deserialization from nested JSON format.""" + original = View.creator(name=VIEW_NAME, schema_qualified_name=SCHEMA_QUALIFIED_NAME) + json_str = original.to_json(nested=True, serde=serde) + + view = View.from_json(json_str, serde=serde) + + assert view.name == VIEW_NAME + assert view.qualified_name == VIEW_QUALIFIED_NAME + assert view.type_name == "View" + + +def test_round_trip_serialization(serde): + """Test that serialization and deserialization preserve all data.""" + original = View.creator(name=VIEW_NAME, schema_qualified_name=SCHEMA_QUALIFIED_NAME) + original.column_count = 5 + original.row_count = 100 + + json_str = original.to_json(nested=True, serde=serde) + restored = View.from_json(json_str, serde=serde) + + assert restored.name == original.name + assert restored.qualified_name == original.qualified_name + assert restored.column_count == original.column_count + assert restored.row_count == original.row_count + + +def test_with_custom_serde(): + """Test that a custom Serde instance can be used for serialization.""" + custom_serde = Serde() + view = View.creator(name=VIEW_NAME, schema_qualified_name=SCHEMA_QUALIFIED_NAME) + + json_str = view.to_json(nested=True, serde=custom_serde) + restored = View.from_json(json_str, serde=custom_serde) + + assert restored.name == view.name + assert restored.qualified_name == view.qualified_name + + +def test_type_name_defaults(): + """Test that type_name defaults to 'View'.""" + view = View(name=VIEW_NAME, qualified_name=VIEW_QUALIFIED_NAME) + assert view.type_name == "View" + + +def test_creator_with_guid(): + """Test that creator initializes a temporary GUID for new assets.""" + view = View.creator(name=VIEW_NAME, schema_qualified_name=SCHEMA_QUALIFIED_NAME) + + assert view.guid is not UNSET + assert view.guid is not None + assert isinstance(view.guid, str) + assert view.guid.startswith("-") + + +def test_sql_fields(): + """Test setting SQL-specific fields (database, schema names).""" + view = View( + name=VIEW_NAME, + qualified_name=VIEW_QUALIFIED_NAME, + database_name=DATABASE_NAME, + database_qualified_name=DATABASE_QUALIFIED_NAME, + schema_name=SCHEMA_NAME, + schema_qualified_name=SCHEMA_QUALIFIED_NAME, + ) + + assert view.database_name == DATABASE_NAME + assert view.database_qualified_name == DATABASE_QUALIFIED_NAME + assert view.schema_name == SCHEMA_NAME + assert view.schema_qualified_name == SCHEMA_QUALIFIED_NAME diff --git a/tests_v9/unit/test_atlan_tag_name.py b/tests_v9/unit/test_atlan_tag_name.py new file mode 100644 index 000000000..3081a482d --- /dev/null +++ b/tests_v9/unit/test_atlan_tag_name.py @@ -0,0 +1,225 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2026 Atlan Pte. Ltd. + +"""Unit tests for AtlanTagName plus AtlanResponse/AtlanRequest tag translation.""" + +from __future__ import annotations + +import msgspec +import pytest + +from pyatlan.model.constants import DELETED_ +from pyatlan_v9.model import Purpose +from pyatlan_v9.model.assets.purpose import PurposeNested, _purpose_from_nested +from pyatlan_v9.model.core import AtlanRequest, AtlanResponse, AtlanTagName + +ATLAN_TAG_ID = "yiB7RLvdC2yeryLPjaDeHM" +GOOD_ATLAN_TAG_NAME = "PII" + + +class _MockTagCache: + def get_name_for_id(self, _tag_id: str): + return None + + def get_id_for_name(self, _tag_name: str): + return None + + def get_source_tags_attr_id(self, tag_id: str): + source_tag_ids = { + "source-tag-with-attributes": "ZLVyaOlGWDrkLFZgmZCjLa", + "source-tag-without-attributes": "BLVyaOlGWDrkLFZgmZCjLa", + "deleted-source-tag": None, + } + return source_tag_ids.get(tag_id, None) + + +class _MockClient: + atlan_tag_cache = _MockTagCache() + + +@pytest.fixture() +def client(): + """Provide a mock client with Atlan tag-cache methods used in translation.""" + return _MockClient() + + +@pytest.fixture() +def good_atlan_tag(): + """Provide a valid AtlanTagName object for conversion tests.""" + return AtlanTagName(GOOD_ATLAN_TAG_NAME) + + +def test_init_with_good_name(): + """Verify AtlanTagName stores and exposes the input display text.""" + sut = AtlanTagName(GOOD_ATLAN_TAG_NAME) + assert sut._display_text == GOOD_ATLAN_TAG_NAME + assert str(sut) == GOOD_ATLAN_TAG_NAME + assert sut.__repr__() == f"AtlanTagName('{GOOD_ATLAN_TAG_NAME}')" + assert sut.__hash__() == GOOD_ATLAN_TAG_NAME.__hash__() + assert AtlanTagName(GOOD_ATLAN_TAG_NAME) == sut + + +def test_convert_to_display_text_when_atlan_tag_passed_returns_same_atlan_tag( + good_atlan_tag, +): + """Verify conversion keeps existing AtlanTagName objects untouched.""" + assert good_atlan_tag is AtlanTagName._convert_to_tag_name(good_atlan_tag) + + +def test_convert_to_display_text_when_bad_string(): + """Verify conversion of a plain string creates an AtlanTagName value.""" + assert AtlanTagName._convert_to_tag_name("bad").__repr__() == "AtlanTagName('bad')" + + +def test_convert_to_tag_name(): + """Verify conversion of a string ID to AtlanTagName preserves its text.""" + sut = AtlanTagName._convert_to_tag_name(ATLAN_TAG_ID) + assert str(sut) == ATLAN_TAG_ID + + +def test_get_deleted_sentinel(): + """Verify deleted sentinel is stable and maps to '(DELETED)' text.""" + sentinel = AtlanTagName.get_deleted_sentinel() + assert "(DELETED)" == str(sentinel) + assert id(sentinel) == id(AtlanTagName.get_deleted_sentinel()) + + +def _assert_asset_tags(asset: Purpose, is_retranslated: bool = False): + assert asset and isinstance(asset, Purpose) + assert asset.classifications and len(asset.classifications) == 5 + assert str(asset.classifications[0].type_name) == DELETED_ + assert str(asset.classifications[1].type_name) == DELETED_ + assert str(asset.classifications[2].type_name) == DELETED_ + if not is_retranslated: + assert asset.classifications[2].source_tag_attachments + assert len(asset.classifications[2].source_tag_attachments) == 1 + assert str(asset.classifications[3].type_name) == DELETED_ + if not is_retranslated: + assert asset.classifications[3].source_tag_attachments == [] + assert str(asset.classifications[4].type_name) == DELETED_ + assert asset.purpose_atlan_tags and len(asset.purpose_atlan_tags) == 2 + assert asset.purpose_atlan_tags[0].__repr__() == f"AtlanTagName('{DELETED_}')" + assert asset.purpose_atlan_tags[1].__repr__() == f"AtlanTagName('{DELETED_}')" + + +def test_asset_tag_name_field_serde_with_translation(client): + """Verify translation and retranslation behavior for deleted and source tags.""" + raw_json = { + "typeName": "Purpose", + "attributes": { + "purposeClassifications": [ + "some-deleted-purpose-tag-1", + "some-deleted-purpose-tag-2", + ], + }, + "guid": "9f7a35f4-8d37-4273-81ec-c497a83a2472", + "status": "ACTIVE", + "classifications": [ + { + "typeName": "some-deleted-purpose-tag-1", + "entityGuid": "82683fb9-1501-4627-a5d0-0da9be64c0d5", + "entityStatus": "DELETED", + "propagate": False, + "removePropagationsOnEntityDelete": True, + "restrictPropagationThroughLineage": True, + "restrictPropagationThroughHierarchy": False, + }, + { + "typeName": "some-deleted-purpose-tag-2", + "entityGuid": "82683fb9-1501-4627-a5d0-0da9be64c0d5", + "entityStatus": "DELETED", + "propagate": False, + "removePropagationsOnEntityDelete": True, + "restrictPropagationThroughLineage": True, + "restrictPropagationThroughHierarchy": False, + }, + { + "typeName": "source-tag-with-attributes", + "attributes": { + "ZLVyaOlGWDrkLFZgmZCjLa": [ + { + "typeName": "SourceTagAttachment", + "attributes": { + "sourceTagName": "CONFIDENTIAL", + "sourceTagQualifiedName": "default/snowflake/1747816988/ANALYTICS/WIDE_WORLD_IMPORTERS/CONFIDENTIAL", + "sourceTagGuid": "2a9dab90-1b86-432d-a28a-9f3d9b61192b", + "sourceTagConnectorName": "snowflake", + "sourceTagValue": [ + {"tagAttachmentValue": "Not Restricted"} + ], + }, + } + ] + }, + "entityGuid": "46be9b92-170b-4c74-bf28-f9dc99021a2a", + "entityStatus": "ACTIVE", + "propagate": True, + "removePropagationsOnEntityDelete": True, + "restrictPropagationThroughLineage": False, + "restrictPropagationThroughHierarchy": False, + }, + { + "typeName": "source-tag-without-attributes", + "entityGuid": "46be9b92-170b-4c74-bf28-f9dc99021a2a", + "entityStatus": "ACTIVE", + "propagate": True, + "removePropagationsOnEntityDelete": True, + "restrictPropagationThroughLineage": False, + "restrictPropagationThroughHierarchy": False, + }, + { + "typeName": "deleted-source-tag", + "attributes": { + "XzEYmFzETBrS7nuxeImNie": [ + { + "typeName": "SourceTagAttachment", + "attributes": { + "sourceTagName": "CONFIDENTIAL", + "sourceTagQualifiedName": "default/snowflake/1747816988/ANALYTICS/WIDE_WORLD_IMPORTERS/CONFIDENTIAL", + "sourceTagGuid": "2a9dab90-1b86-432d-a28a-9f3d9b61192b", + "sourceTagConnectorName": "snowflake", + "sourceTagValue": [ + {"tagAttachmentValue": "Not Restricted"} + ], + }, + } + ] + }, + "entityGuid": "46be9b92-170b-4c74-bf28-f9dc99021a2a", + "entityStatus": "DELETED", + "propagate": True, + "removePropagationsOnEntityDelete": True, + "restrictPropagationThroughLineage": False, + "restrictPropagationThroughHierarchy": False, + }, + ], + } + + translated_dict = AtlanResponse(raw_json=raw_json, client=client).to_dict() + + def _nested_to_purpose(d: dict) -> Purpose: + nested = msgspec.convert(d, PurposeNested) + return _purpose_from_nested(nested) + + purpose_with_translation = _nested_to_purpose(translated_dict) + purpose_without_translation = _nested_to_purpose(raw_json) + + retranslated_with_translated_dict = AtlanRequest( + instance=purpose_with_translation, client=client + ).translated + retranslated_without_translated_dict = AtlanRequest( + instance=purpose_without_translation, client=client + ).translated + + purpose_with_translation_and_retranslation = _nested_to_purpose( + retranslated_with_translated_dict + ) + purpose_without_translation_and_retranslation = _nested_to_purpose( + retranslated_without_translated_dict + ) + + _assert_asset_tags(purpose_with_translation) + _assert_asset_tags(purpose_with_translation_and_retranslation, is_retranslated=True) + _assert_asset_tags( + purpose_without_translation_and_retranslation, is_retranslated=True + ) diff --git a/tests_v9/unit/test_audit_search.py b/tests_v9/unit/test_audit_search.py new file mode 100644 index 000000000..0f277bf5a --- /dev/null +++ b/tests_v9/unit/test_audit_search.py @@ -0,0 +1,189 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +""" +Unit tests for audit search using v9 (msgspec) request models. + +Ported from tests/unit/test_audit_search.py — uses v9 AuditSearchRequest/DSL/Bool/Term +while keeping legacy AuditClient and AuditSearchResults (returned by client). +""" + +from datetime import datetime, timezone +from json import load +from pathlib import Path +from unittest.mock import Mock, patch + +import pytest + +from pyatlan.client.common import ApiCaller +from pyatlan.client.common.audit import LOGGER +from pyatlan_v9.client.audit import V9AuditClient as AuditClient +from pyatlan_v9.errors import InvalidRequestError + +# V9 result model — client returns this; needed for patching thresholds +# v9 request models (msgspec) +from pyatlan_v9.model.audit import AuditSearchRequest, AuditSearchResults +from pyatlan_v9.model.enums import SortOrder +from pyatlan_v9.model.search import DSL, Bool, SortItem, Term + +SEARCH_RESPONSES_DIR = ( + Path(__file__).parent.parent.parent / "tests" / "unit" / "data" / "search_responses" +) +AUDIT_SEARCH_PAGING_JSON = "audit_search_paging.json" + + +@pytest.fixture(autouse=True) +def set_env(monkeypatch): + monkeypatch.setenv("ATLAN_BASE_URL", "https://name.atlan.com") + monkeypatch.setenv("ATLAN_API_KEY", "abkj") + + +@pytest.fixture(scope="module") +def mock_api_caller(): + return Mock(spec=ApiCaller) + + +@pytest.fixture() +def audit_search_paging_json(): + def load_json(filename): + with (SEARCH_RESPONSES_DIR / filename).open() as input_file: + return load(input_file) + + return load_json(AUDIT_SEARCH_PAGING_JSON) + + +def _assert_audit_search_results( + results: AuditSearchResults, response_json, sorts, bulk=False +): + first = response_json["entityAudits"][0] + for audit in results: + assert audit.entity_id == first["entityId"] + assert audit.entity_qualified_name == first["entityQualifiedName"] + assert audit.type_name == first["typeName"] + expected_timestamp = datetime.fromtimestamp( + first["timestamp"] / 1000, tz=timezone.utc + ) + assert audit.timestamp == expected_timestamp + expected_created = datetime.fromtimestamp( + first["created"] / 1000, tz=timezone.utc + ) + assert audit.created == expected_created + assert audit.user == first["user"] + assert audit.action == first["action"] + + assert results.total_count == response_json["totalCount"] + assert results._bulk == bulk + assert results._criteria.dsl.sort == sorts + + +@patch.object(LOGGER, "debug") +def test_audit_search_pagination( + mock_logger, mock_api_caller, audit_search_paging_json +): + client = AuditClient(mock_api_caller) + mock_api_caller._call_api.side_effect = [ + audit_search_paging_json, + audit_search_paging_json, + {}, + ] + + # Test default pagination + dsl = DSL( + query=Bool(filter=[Term(field="entityId", value="some-guid")]), + sort=[], + size=1, + from_=0, + ) + audit_search_request = AuditSearchRequest(dsl=dsl) + response = client.search(criteria=audit_search_request, bulk=False) + + assert response and response.aggregations + assert audit_search_paging_json["aggregations"] == response.aggregations + expected_sorts = [SortItem(field="entityId", order=SortOrder.ASCENDING)] + + _assert_audit_search_results(response, audit_search_paging_json, expected_sorts) + assert mock_api_caller._call_api.call_count == 3 + assert mock_logger.call_count == 0 + mock_api_caller.reset_mock() + + # Test bulk pagination + mock_api_caller._call_api.side_effect = [ + audit_search_paging_json, + audit_search_paging_json, + {}, + ] + audit_search_request = AuditSearchRequest(dsl=dsl) + response = client.search(criteria=audit_search_request, bulk=True) + expected_sorts = [ + SortItem(field="created", order=SortOrder.ASCENDING), + SortItem(field="entityId", order=SortOrder.ASCENDING), + ] + + _assert_audit_search_results( + response, audit_search_paging_json, expected_sorts, bulk=True + ) + # The call count will be 2 because + # audit search entries are processed in the first API call. + # In the second API call, self._entity_audits + # becomes 0, which breaks the pagination. + # This differs from offset-based pagination + # where an additional API call is needed + # to verify if the results are empty + assert mock_api_caller._call_api.call_count == 2 + assert mock_logger.call_count == 1 + assert "Audit bulk search option is enabled." in mock_logger.call_args_list[0][0][0] + mock_logger.reset_mock() + mock_api_caller.reset_mock() + + # Test automatic bulk search conversion when exceeding threshold + with patch.object(AuditSearchResults, "_MASS_EXTRACT_THRESHOLD", -1): + mock_api_caller._call_api.side_effect = [ + # Extra call to re-fetch the first page + # results with updated timestamp sorting + audit_search_paging_json, + audit_search_paging_json, + audit_search_paging_json, + {}, + ] + audit_search_request = AuditSearchRequest(dsl=dsl) + response = client.search(criteria=audit_search_request) + _assert_audit_search_results( + response, audit_search_paging_json, expected_sorts, bulk=False + ) + assert mock_logger.call_count == 1 + assert mock_api_caller._call_api.call_count == 3 + assert ( + "Result size (%s) exceeds threshold (%s)" + in mock_logger.call_args_list[0][0][0] + ) + + # Test exception for bulk=False with user-defined sorting and results exceeds the predefined threshold + dsl.sort = dsl.sort + [SortItem(field="some-sort1", order=SortOrder.ASCENDING)] + audit_search_request = AuditSearchRequest(dsl=dsl) + with pytest.raises( + InvalidRequestError, + match=( + "ATLAN-PYTHON-400-066 Unable to execute " + "audit bulk search with user-defined sorting options. " + "Suggestion: Please ensure that no sorting options are " + "included in your audit search request when performing a bulk search." + ), + ): + client.search(criteria=audit_search_request, bulk=False) + + # Test exception for bulk=True with user-defined sorting + dsl.sort = dsl.sort + [SortItem(field="some-sort2", order=SortOrder.ASCENDING)] + audit_search_request = AuditSearchRequest(dsl=dsl) + with pytest.raises( + InvalidRequestError, + match=( + "ATLAN-PYTHON-400-066 Unable to execute " + "audit bulk search with user-defined sorting options. " + "Suggestion: Please ensure that no sorting options are " + "included in your audit search request when performing a bulk search." + ), + ): + client.search(criteria=audit_search_request, bulk=True) + + mock_logger.reset_mock() + mock_api_caller.reset_mock() diff --git a/tests_v9/unit/test_base_vcr_json.py b/tests_v9/unit/test_base_vcr_json.py new file mode 100644 index 000000000..cd9cb193d --- /dev/null +++ b/tests_v9/unit/test_base_vcr_json.py @@ -0,0 +1,73 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +""" +VCR integration tests (JSON serialization) — ported as-is from +tests/unit/test_base_vcr_json.py. No Pydantic/msgspec models involved. +""" + +import httpx +import pytest + +from pyatlan_v9.test_utils.base_vcr import BaseVCR + + +class TestBaseVCRJSON(BaseVCR): + """ + Integration tests to demonstrate VCR.py capabilities + by recording and replaying HTTP interactions using + HTTPBin (https://httpbin.org) for GET, POST, PUT, and DELETE requests. + """ + + BASE_URL = "https://httpbin.org" + + @pytest.fixture(scope="module") + def vcr_config(self): + """ + Override the VCR configuration to use JSON serialization across the module. + """ + config = self._BASE_CONFIG.copy() + config.update({"serializer": "pretty-json"}) + return config + + @pytest.mark.vcr() + def test_httpbin_get(self): + """ + Test a simple GET request to httpbin. + """ + url = f"{self.BASE_URL}/get" + response = httpx.get(url, params={"test": "value"}) + assert response.status_code == 200 + assert response.json()["args"]["test"] == "value" + + @pytest.mark.vcr() + def test_httpbin_post(self): + """ + Test a simple POST request to httpbin. + """ + url = f"{self.BASE_URL}/post" + payload = {"name": "atlan", "type": "integration-test"} + response = httpx.post(url, json=payload) + assert response.status_code == 200 + assert response.json()["json"] == payload + + @pytest.mark.vcr() + def test_httpbin_put(self): + """ + Test a simple PUT request to httpbin. + """ + url = f"{self.BASE_URL}/put" + payload = {"update": "value"} + response = httpx.put(url, json=payload) + assert response.status_code == 200 + assert response.json()["json"] == payload + + @pytest.mark.vcr() + def test_httpbin_delete(self): + """ + Test a simple DELETE request to httpbin. + """ + url = f"{self.BASE_URL}/delete" + response = httpx.delete(url) + assert response.status_code == 200 + assert response.json()["args"] == {} diff --git a/tests_v9/unit/test_base_vcr_yaml.py b/tests_v9/unit/test_base_vcr_yaml.py new file mode 100644 index 000000000..32d1498cb --- /dev/null +++ b/tests_v9/unit/test_base_vcr_yaml.py @@ -0,0 +1,69 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +""" +VCR integration tests (YAML serialization) — ported as-is from +tests/unit/test_base_vcr_yaml.py. No Pydantic/msgspec models involved. +""" + +import httpx +import pytest + +from pyatlan_v9.test_utils.base_vcr import BaseVCR + + +class TestBaseVCRYAML(BaseVCR): + """ + Integration tests to demonstrate VCR.py capabilities + by recording and replaying HTTP interactions using + HTTPBin (https://httpbin.org) for GET, POST, PUT, and DELETE requests. + """ + + BASE_URL = "https://httpbin.org" + + @pytest.mark.vcr() + def test_httpbin_get(self): + """ + Test a simple GET request to httpbin. + """ + url = f"{self.BASE_URL}/get" + response = httpx.get(url, params={"test": "value"}) + + assert response.status_code == 200 + assert response.json()["args"]["test"] == "value" + + @pytest.mark.vcr() + def test_httpbin_post(self): + """ + Test a simple POST request to httpbin. + """ + url = f"{self.BASE_URL}/post" + payload = {"name": "atlan", "type": "integration-test"} + response = httpx.post(url, json=payload) + + assert response.status_code == 200 + assert response.json()["json"] == payload + + @pytest.mark.vcr() + def test_httpbin_put(self): + """ + Test a simple PUT request to httpbin. + """ + url = f"{self.BASE_URL}/put" + payload = {"update": "value"} + response = httpx.put(url, json=payload) + + assert response.status_code == 200 + assert response.json()["json"] == payload + + @pytest.mark.vcr() + def test_httpbin_delete(self): + """ + Test a simple DELETE request to httpbin. + """ + url = f"{self.BASE_URL}/delete" + response = httpx.delete(url) + + assert response.status_code == 200 + # HTTPBin returns an empty JSON object for DELETE + assert response.json()["args"] == {} diff --git a/tests_v9/unit/test_client.py b/tests_v9/unit/test_client.py new file mode 100644 index 000000000..b841743d5 --- /dev/null +++ b/tests_v9/unit/test_client.py @@ -0,0 +1,3321 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +""" +Unit tests for pyatlan_v9 AtlanClient (msgspec.Struct). + +Tests ported from tests/unit/test_client.py — configuration, proxy, SSL, headers, +error handling, terms operations, find operations, search/pagination, validation, +batch, bulk request, and asset client tests. Deprecated method tests are excluded +from v9 per migration policy. +""" + +from importlib.resources import read_text +from json import load, loads +from pathlib import Path +from re import escape +from unittest.mock import DEFAULT, Mock, call, patch + +import httpx +import msgspec +import pytest + +from pyatlan.client.common import ApiCaller, Search +from pyatlan.client.common.asset import LOGGER as SHARED_LOGGER +from pyatlan.utils import get_python_version +from pyatlan_v9.client.asset import ( + Batch, + CustomMetadataHandling, + IndexSearchResults, + V9AssetClient, +) +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.client.group import V9GroupClient as GroupClient +from pyatlan_v9.client.search_log import V9SearchLogClient as SearchLogClient +from pyatlan_v9.client.typedef import V9TypeDefClient as TypeDefClient +from pyatlan_v9.client.user import V9UserClient as UserClient +from pyatlan_v9.errors import ( + ERROR_CODE_FOR_HTTP_STATUS, + ApiError, + AtlanError, + ErrorCode, + InvalidRequestError, + NotFoundError, +) +from pyatlan_v9.model.assets import ( + Asset, + AtlasGlossary, + AtlasGlossaryCategory, + AtlasGlossaryTerm, + Column, + DataDomain, + DataProduct, + Table, + View, +) +from pyatlan_v9.model.assets.gtc_related import ( + RelatedAtlasGlossary, + RelatedAtlasGlossaryTerm, +) +from pyatlan_v9.model.assets.related_entity import SaveSemantic as V9SaveSemantic +from pyatlan_v9.model.core import Announcement, BulkRequest +from pyatlan_v9.model.enums import ( + AnnouncementType, + CertificateStatus, + DataQualityScheduleType, + LineageDirection, + SortOrder, +) +from pyatlan_v9.model.fluent_search import CompoundQuery, FluentSearch +from pyatlan_v9.model.group import GroupRequest +from pyatlan_v9.model.lineage import LineageListRequest +from pyatlan_v9.model.response import AssetMutationResponse +from pyatlan_v9.model.search import DSL, Bool, IndexSearchRequest, Term, TermAttributes +from pyatlan_v9.model.search_log import SearchLogRequest +from pyatlan_v9.model.typedef import EnumDef +from pyatlan_v9.model.user import AtlanUser, UserRequest +from tests_v9.unit.constants import ( + TEST_ADMIN_CLIENT_METHODS, + TEST_AUDIT_CLIENT_METHODS, + TEST_ROLE_CLIENT_METHODS, + TEST_SL_CLIENT_METHODS, +) +from tests_v9.unit.constants import TEST_ASSET_CLIENT_METHODS as _V9_ASSET_METHODS +from tests_v9.unit.constants import TEST_GROUP_CLIENT_METHODS as _V9_GROUP_METHODS +from tests_v9.unit.constants import TEST_TOKEN_CLIENT_METHODS as _V9_TOKEN_METHODS +from tests_v9.unit.constants import TEST_TYPEDEF_CLIENT_METHODS as _V9_TYPEDEF_METHODS +from tests_v9.unit.constants import TEST_USER_CLIENT_METHODS as _V9_USER_METHODS + +# v9 uses pyatlan search DSL internally; " " name validation raises WithName not FindXByName +_V9_WHITESPACE_NAME_MSG = "1 validation error for WithName\nvalue\n ensure this value has at least 1 characters" +TEST_ASSET_CLIENT_METHODS = dict(_V9_ASSET_METHODS) +TEST_ASSET_CLIENT_METHODS["find_domain_by_name"] = [ + ( + [None, ["attributes"]], + "1 validation error for FindDomainByName\nname\n none is not an allowed value", + ), + ([" ", ["attributes"]], _V9_WHITESPACE_NAME_MSG), + ( + ["test-domain", "attributes"], + "1 validation error for FindDomainByName\nattributes\n value is not a valid list", + ), +] +TEST_ASSET_CLIENT_METHODS["find_product_by_name"] = [ + ( + [None, ["attributes"]], + "1 validation error for FindProductByName\nname\n none is not an allowed value", + ), + ([" ", ["attributes"]], _V9_WHITESPACE_NAME_MSG), + ( + ["test-product", "attributes"], + "1 validation error for FindProductByName\nattributes\n value is not a valid list", + ), +] + + +def _rename_keys(d: dict) -> dict: + """Rename legacy create/update keys to v9 creator/updater.""" + mapping = {"create": "creator", "update": "updater"} + return {mapping.get(k, k): v for k, v in d.items()} + + +TEST_GROUP_CLIENT_METHODS = _rename_keys(_V9_GROUP_METHODS) +TEST_TOKEN_CLIENT_METHODS = _rename_keys(_V9_TOKEN_METHODS) +TEST_TYPEDEF_CLIENT_METHODS = _rename_keys(_V9_TYPEDEF_METHODS) +TEST_USER_CLIENT_METHODS = _rename_keys(_V9_USER_METHODS) +from tests.unit.model.constants import ( # noqa: E402 + CONNECTION_NAME, + CONNECTOR_TYPE, + DATA_DOMAIN_NAME, + DATA_PRODUCT_NAME, + DQ_COLUMN_QUALIFIED_NAME, + DQ_TABLE_QUALIFIED_NAME, + GLOSSARY_CATEGORY_NAME, + GLOSSARY_NAME, + GLOSSARY_QUALIFIED_NAME, + GLOSSARY_TERM_NAME, + PERSONA_NAME, + PURPOSE_NAME, +) + +GLOSSARY = AtlasGlossary.create(name=GLOSSARY_NAME) +GLOSSARY_CATEGORY = AtlasGlossaryCategory.create( + name=GLOSSARY_CATEGORY_NAME, anchor=GLOSSARY +) +GLOSSARY_TERM = AtlasGlossaryTerm.create(name=GLOSSARY_TERM_NAME, anchor=GLOSSARY) +UNIQUE_USERS = "uniqueUsers" +UNIQUE_ASSETS = "uniqueAssets" +LOG_IP_ADDRESS = "ipAddress" +LOG_USERNAME = "userName" +SEARCH_PARAMS = "searchParameters" +SEARCH_COUNT = "approximateCount" +TEST_DATA_DIR = Path(__file__).parent.parent.parent / "tests" / "unit" / "data" +SEARCH_LOG_RESPONSES_DIR = TEST_DATA_DIR / "search_log_responses" +SL_MOST_RECENT_VIEWERS_JSON = "sl_most_recent_viewers.json" +SL_MOST_VIEWED_ASSETS_JSON = "sl_most_viewed_assets.json" +SL_DETAILED_LOG_ENTRIES_JSON = "sl_detailed_log_entries.json" +CM_NAME = "testcm1.testcm2" +LINEAGE_LIST_JSON = "lineage_list.json" +LINEAGE_RESPONSES_DIR = TEST_DATA_DIR / "lineage_responses" +GROUP_LIST_JSON = "group_list.json" +GROUP_MEMBERS_JSON = "group_members.json" +GROUP_RESPONSES_DIR = TEST_DATA_DIR / "group_responses" +USER_LIST_JSON = "user_list.json" +USER_GROUPS_JSON = "user_groups.json" +USER_RESPONSES_DIR = TEST_DATA_DIR / "user_responses" +AGGREGATIONS_NULL_RESPONSES_DIR = "aggregations_null_value.json" +INDEX_SEARCH_PAGING_JSON = "index_search_paging.json" +GLOSSARY_CATEGORY_BY_NAME_JSON = "glossary_category_by_name.json" +SEARCH_RESPONSES_DIR = TEST_DATA_DIR / "search_responses" +GET_BY_GUID_JSON = "get_by_guid.json" +RETRIEVE_MINIMAL_JSON = "retrieve_minimal.json" +ASSET_RESPONSES_DIR = TEST_DATA_DIR / "asset_responses" +TYPEDEF_GET_BY_NAME_JSON = "get_by_name.json" +TYPEDEF_RESPONSES_DIR = TEST_DATA_DIR / "typedef_responses" + +TEST_ANNOUNCEMENT = Announcement( + announcement_title="test-title", + announcement_message="test-msg", + announcement_type=AnnouncementType.INFORMATION, +) +TEST_MISSING_GLOSSARY_GUID_ERROR = "ATLAN-PYTHON-400-055 'glossary_guid' keyword argument is missing for asset type: {0}" + + +def load_json(responses_dir, filename): + """Load JSON test data from a response directory.""" + with (responses_dir / filename).open() as input_file: + return load(input_file) + + +# --------------------------------------------------------------------------- +# Fixtures +# --------------------------------------------------------------------------- + + +@pytest.fixture(autouse=True) +def set_env(monkeypatch): + """Set required environment variables for client instantiation.""" + monkeypatch.setenv("ATLAN_BASE_URL", "https://test.atlan.com") + monkeypatch.setenv("ATLAN_API_KEY", "test-api-key") + + +@pytest.fixture() +def client(): + """Create a v9 AtlanClient using env var defaults.""" + return AtlanClient() + + +@pytest.fixture +def group_client(mock_api_caller): + """GroupClient wired to a mock ApiCaller.""" + return GroupClient(client=mock_api_caller) + + +@pytest.fixture +def mock_atlan_client(): + """Mock AtlanClient for batch testing.""" + return Mock(AtlanClient) + + +@pytest.fixture +def mock_api_caller(): + """Mock ApiCaller for sub-client testing.""" + return Mock(spec=ApiCaller) + + +@pytest.fixture() +def sl_most_recent_viewers_json(): + """Load search log most recent viewers test data.""" + return load_json(SEARCH_LOG_RESPONSES_DIR, SL_MOST_RECENT_VIEWERS_JSON) + + +@pytest.fixture() +def sl_most_viewed_assets_json(): + """Load search log most viewed assets test data.""" + return load_json(SEARCH_LOG_RESPONSES_DIR, SL_MOST_VIEWED_ASSETS_JSON) + + +@pytest.fixture() +def sl_detailed_log_entries_json(): + """Load search log detailed entries test data.""" + return load_json(SEARCH_LOG_RESPONSES_DIR, SL_DETAILED_LOG_ENTRIES_JSON) + + +@pytest.fixture() +def lineage_list_json(): + """Load lineage list test data.""" + return load_json(LINEAGE_RESPONSES_DIR, LINEAGE_LIST_JSON) + + +@pytest.fixture() +def group_list_json(): + """Load group list test data.""" + return load_json(GROUP_RESPONSES_DIR, GROUP_LIST_JSON) + + +@pytest.fixture() +def group_members_json(): + """Load group members test data.""" + return load_json(GROUP_RESPONSES_DIR, GROUP_MEMBERS_JSON) + + +@pytest.fixture() +def user_list_json(): + """Load user list test data.""" + return load_json(USER_RESPONSES_DIR, USER_LIST_JSON) + + +@pytest.fixture() +def user_groups_json(): + """Load user groups test data.""" + return load_json(USER_RESPONSES_DIR, USER_GROUPS_JSON) + + +@pytest.fixture() +def aggregations_null_json(): + """Load aggregations null value test data.""" + return load_json(SEARCH_RESPONSES_DIR, AGGREGATIONS_NULL_RESPONSES_DIR) + + +@pytest.fixture() +def index_search_paging_json(): + """Load index search paging test data.""" + return load_json(SEARCH_RESPONSES_DIR, INDEX_SEARCH_PAGING_JSON) + + +@pytest.fixture() +def get_by_guid_json(): + """Load get by GUID test data.""" + return load_json(ASSET_RESPONSES_DIR, GET_BY_GUID_JSON) + + +@pytest.fixture() +def retrieve_minimal_json(): + """Load retrieve minimal test data.""" + return load_json(ASSET_RESPONSES_DIR, RETRIEVE_MINIMAL_JSON) + + +@pytest.fixture() +def type_def_get_by_name_json(): + """Load typedef get by name test data.""" + return load_json(TYPEDEF_RESPONSES_DIR, TYPEDEF_GET_BY_NAME_JSON) + + +@pytest.fixture() +def glossary_category_by_name_json(): + """Load glossary category by name test data.""" + return load_json(SEARCH_RESPONSES_DIR, GLOSSARY_CATEGORY_BY_NAME_JSON) + + +# --------------------------------------------------------------------------- +# Append terms tests +# --------------------------------------------------------------------------- + + +@pytest.mark.parametrize( + "guid, qualified_name, asset_type, assigned_terms, expected_message, expected_error", + [ + ( + None, + None, + Table, + [AtlasGlossaryTerm()], + "ATLAN-PYTHON-400-043 Either qualified_name or guid should be provided.", + InvalidRequestError, + ), + ( + "123", + "default/abc", + Table, + [AtlasGlossaryTerm()], + "ATLAN-PYTHON-400-042 Only qualified_name or guid should be provided but not both.", + InvalidRequestError, + ), + ], +) +def test_append_terms_invalid_parameters_raises_error( + guid, qualified_name, asset_type, assigned_terms, expected_message, expected_error +): + """Test that append_terms raises error with invalid parameter combinations.""" + client = AtlanClient() + with pytest.raises(expected_error, match=expected_message): + client.asset.append_terms( + asset_type=asset_type, + terms=assigned_terms, + guid=guid, + qualified_name=qualified_name, + ) + + +@pytest.mark.parametrize( + "guid, qualified_name, asset_type, assigned_terms, mock_results, expected_message, expected_error", + [ + ( + None, + "nonexistent_qualified_name", + Table, + [AtlasGlossaryTerm()], + [], + "ATLAN-PYTHON-404-003 Asset with qualifiedName nonexistent_qualified_name of type Table does not exist." + " Suggestion: Verify the qualifiedName and expected type of the asset you are trying to retrieve.", + NotFoundError, + ), + ( + "nonexistent_guid", + None, + Table, + [AtlasGlossaryTerm()], + [], + "ATLAN-PYTHON-404-001 Asset with GUID nonexistent_guid does not exist." + " Suggestion: Verify the GUID of the asset you are trying to retrieve.", + NotFoundError, + ), + ( + None, + "default/abc", + Table, + [AtlasGlossaryTerm()], + ["DifferentTypeAsset"], + "ATLAN-PYTHON-404-014 The Table asset could not be found by name: default/abc." + " Suggestion: Verify the requested asset type and name exist in your Atlan environment.", + NotFoundError, + ), + ( + "123", + None, + Table, + [AtlasGlossaryTerm()], + ["DifferentTypeAsset"], + "ATLAN-PYTHON-404-002 Asset with GUID 123 is not of the type requested: Table." + " Suggestion: Verify the GUID and expected type of the asset you are trying to retrieve.", + NotFoundError, + ), + ], +) +@patch("pyatlan_v9.model.fluent_search.FluentSearch.execute") +def test_append_terms_asset_retrieval_errors( + mock_execute, + guid, + qualified_name, + asset_type, + assigned_terms, + mock_results, + expected_message, + expected_error, +): + """Test that append_terms raises appropriate errors for asset retrieval failures.""" + mock_execute.return_value.current_page = lambda: mock_results + client = AtlanClient() + with pytest.raises(expected_error, match=expected_message): + client.asset.append_terms( + asset_type=asset_type, + terms=assigned_terms, + guid=guid, + qualified_name=qualified_name, + ) + + +def test_append_with_valid_guid_and_no_terms_returns_asset(): + """Test that append_terms with empty terms list returns asset unchanged.""" + asset_type = Table + table = Table() + table.name = "table-test" + table.qualified_name = "table_qn" + + terms = [] + + with patch("pyatlan_v9.model.fluent_search.FluentSearch.execute") as mock_execute: + with patch.object(V9AssetClient, "save") as mock_save: + mock_execute.return_value.current_page = lambda: [table] + + mock_save.return_value.assets_updated.return_value = [table] + + client = AtlanClient() + guid = "123" + + asset = client.asset.append_terms( + guid=guid, asset_type=asset_type, terms=terms + ) + + assert asset == table + assert asset.assigned_terms is None + mock_execute.assert_called_once() + mock_save.assert_called_once() + + +def test_append_with_valid_guid_when_no_terms_present_returns_asset_with_given_terms(): + """Test that append_terms adds new terms when asset has no existing terms.""" + asset_type = Table + table = Table() + table.name = "table-test" + table.qualified_name = "table_qn" + + terms = [AtlasGlossaryTerm(qualified_name="term1")] + + with patch("pyatlan_v9.model.fluent_search.FluentSearch.execute") as mock_execute: + with patch.object(V9AssetClient, "save") as mock_save: + mock_execute.return_value.current_page = lambda: [table] + + def mock_save_side_effect(entity): + entity.assigned_terms = terms + return Mock(assets_updated=lambda asset_type: [entity]) + + mock_save.side_effect = mock_save_side_effect + + client = AtlanClient() + guid = "123" + asset = client.asset.append_terms( + guid=guid, asset_type=asset_type, terms=terms + ) + + assert asset.assigned_terms == terms + mock_execute.assert_called_once() + mock_save.assert_called_once() + + +def test_append_with_valid_guid_when_terms_present_returns_asset_with_combined_terms(): + """Test that append_terms combines existing and new terms.""" + asset_type = Table + table = Table() + table.name = "table-test" + table.qualified_name = "table_qn" + + exisiting_term = AtlasGlossaryTerm() + table.attributes.meanings = [exisiting_term] + + new_term = AtlasGlossaryTerm(qualified_name="new_term") + terms = [new_term] + + with patch("pyatlan_v9.model.fluent_search.FluentSearch.execute") as mock_execute: + with patch.object(V9AssetClient, "save") as mock_save: + mock_execute.return_value.current_page = lambda: [table] + + def mock_save_side_effect(entity): + entity.assigned_terms = table.attributes.meanings + terms + return Mock(assets_updated=lambda asset_type: [entity]) + + mock_save.side_effect = mock_save_side_effect + + client = AtlanClient() + guid = "123" + + asset = client.asset.append_terms( + guid=guid, asset_type=asset_type, terms=terms + ) + + updated_terms = asset.assigned_terms + assert updated_terms is not None + assert len(updated_terms) == 2 + assert exisiting_term in updated_terms + assert new_term in updated_terms + mock_execute.assert_called_once() + mock_save.assert_called_once() + + +# --------------------------------------------------------------------------- +# Replace terms tests +# --------------------------------------------------------------------------- + + +@pytest.mark.parametrize( + "guid, qualified_name, asset_type, assigned_terms, expected_message, expected_error", + [ + ( + None, + None, + Table, + [AtlasGlossaryTerm()], + "ATLAN-PYTHON-400-043 Either qualified_name or guid should be provided.", + InvalidRequestError, + ), + ( + "123", + "default/abc", + Table, + [AtlasGlossaryTerm()], + "ATLAN-PYTHON-400-042 Only qualified_name or guid should be provided but not both.", + InvalidRequestError, + ), + ], +) +def test_replace_terms_invalid_parameters_raises_error( + guid, qualified_name, asset_type, assigned_terms, expected_message, expected_error +): + """Test that replace_terms raises error with invalid parameter combinations.""" + client = AtlanClient() + with pytest.raises(expected_error, match=expected_message): + client.asset.replace_terms( + asset_type=asset_type, + terms=assigned_terms, + guid=guid, + qualified_name=qualified_name, + ) + + +@pytest.mark.parametrize( + "guid, qualified_name, asset_type, assigned_terms, mock_results, expected_message, expected_error", + [ + ( + None, + "nonexistent_qualified_name", + Table, + [AtlasGlossaryTerm()], + [], + "ATLAN-PYTHON-404-003 Asset with qualifiedName nonexistent_qualified_name of type Table does not exist." + " Suggestion: Verify the qualifiedName and expected type of the asset you are trying to retrieve.", + NotFoundError, + ), + ( + "nonexistent_guid", + None, + Table, + [AtlasGlossaryTerm()], + [], + "ATLAN-PYTHON-404-001 Asset with GUID nonexistent_guid does not exist." + " Suggestion: Verify the GUID of the asset you are trying to retrieve.", + NotFoundError, + ), + ( + None, + "default/abc", + Table, + [AtlasGlossaryTerm()], + ["DifferentTypeAsset"], + "ATLAN-PYTHON-404-014 The Table asset could not be found by name: default/abc." + " Suggestion: Verify the requested asset type and name exist in your Atlan environment.", + NotFoundError, + ), + ( + "123", + None, + Table, + [AtlasGlossaryTerm()], + ["DifferentTypeAsset"], + "ATLAN-PYTHON-404-002 Asset with GUID 123 is not of the type requested: Table." + " Suggestion: Verify the GUID and expected type of the asset you are trying to retrieve.", + NotFoundError, + ), + ], +) +@patch("pyatlan_v9.model.fluent_search.FluentSearch.execute") +def test_replace_terms_asset_retrieval_errors( + mock_execute, + guid, + qualified_name, + asset_type, + assigned_terms, + mock_results, + expected_message, + expected_error, +): + """Test that replace_terms raises appropriate errors for asset retrieval failures.""" + mock_execute.return_value.current_page = lambda: mock_results + client = AtlanClient() + with pytest.raises(expected_error, match=expected_message): + client.asset.replace_terms( + asset_type=asset_type, + terms=assigned_terms, + guid=guid, + qualified_name=qualified_name, + ) + + +def test_replace_terms(): + """Test that replace_terms replaces existing terms with new ones.""" + asset_type = Table + table = Table() + table.name = "table-test" + table.qualified_name = "table_qn" + + exisiting_term = AtlasGlossaryTerm() + table.attributes.meanings = [exisiting_term] + + terms = [AtlasGlossaryTerm(qualified_name="new_term")] + + with patch("pyatlan_v9.model.fluent_search.FluentSearch.execute") as mock_execute: + with patch.object(V9AssetClient, "save") as mock_save: + mock_execute.return_value.current_page = lambda: [table] + + def mock_save_side_effect(entity): + entity.assigned_terms = terms + return Mock(assets_updated=lambda asset_type: [entity]) + + mock_save.side_effect = mock_save_side_effect + + client = AtlanClient() + guid = "123" + + asset = client.asset.replace_terms( + guid=guid, asset_type=asset_type, terms=terms + ) + + assert asset.assigned_terms == terms + mock_execute.assert_called_once() + mock_save.assert_called_once() + + +# --------------------------------------------------------------------------- +# Remove terms tests +# --------------------------------------------------------------------------- + + +@pytest.mark.parametrize( + "guid, qualified_name, asset_type, assigned_terms, expected_message, expected_error", + [ + ( + None, + None, + Table, + [AtlasGlossaryTerm()], + "ATLAN-PYTHON-400-043 Either qualified_name or guid should be provided.", + InvalidRequestError, + ), + ( + "123", + "default/abc", + Table, + [AtlasGlossaryTerm()], + "ATLAN-PYTHON-400-042 Only qualified_name or guid should be provided but not both.", + InvalidRequestError, + ), + ], +) +def test_remove_terms_invalid_parameters_raises_error( + guid, qualified_name, asset_type, assigned_terms, expected_message, expected_error +): + """Test that remove_terms raises error with invalid parameter combinations.""" + client = AtlanClient() + with pytest.raises(expected_error, match=expected_message): + client.asset.remove_terms( + asset_type=asset_type, + terms=assigned_terms, + guid=guid, + qualified_name=qualified_name, + ) + + +@pytest.mark.parametrize( + "guid, qualified_name, asset_type, assigned_terms, mock_results, expected_message, expected_error", + [ + ( + None, + "nonexistent_qualified_name", + Table, + [AtlasGlossaryTerm()], + [], + "ATLAN-PYTHON-404-003 Asset with qualifiedName nonexistent_qualified_name of type Table does not exist." + " Suggestion: Verify the qualifiedName and expected type of the asset you are trying to retrieve.", + NotFoundError, + ), + ( + "nonexistent_guid", + None, + Table, + [AtlasGlossaryTerm()], + [], + "ATLAN-PYTHON-404-001 Asset with GUID nonexistent_guid does not exist." + " Suggestion: Verify the GUID of the asset you are trying to retrieve.", + NotFoundError, + ), + ( + None, + "default/abc", + Table, + [AtlasGlossaryTerm()], + ["DifferentTypeAsset"], + "ATLAN-PYTHON-404-014 The Table asset could not be found by name: default/abc." + " Suggestion: Verify the requested asset type and name exist in your Atlan environment.", + NotFoundError, + ), + ( + "123", + None, + Table, + [AtlasGlossaryTerm()], + ["DifferentTypeAsset"], + "ATLAN-PYTHON-404-002 Asset with GUID 123 is not of the type requested: Table." + " Suggestion: Verify the GUID and expected type of the asset you are trying to retrieve.", + NotFoundError, + ), + ], +) +@patch("pyatlan_v9.model.fluent_search.FluentSearch.execute") +def test_remove_terms_asset_retrieval_errors( + mock_execute, + guid, + qualified_name, + asset_type, + assigned_terms, + mock_results, + expected_message, + expected_error, +): + """Test that remove_terms raises appropriate errors for asset retrieval failures.""" + mock_execute.return_value.current_page = lambda: mock_results + client = AtlanClient() + with pytest.raises(expected_error, match=expected_message): + client.asset.remove_terms( + asset_type=asset_type, + terms=assigned_terms, + guid=guid, + qualified_name=qualified_name, + ) + + +def test_remove_with_valid_guid_when_terms_present_returns_asset_with_terms_removed(): + """Test that remove_terms removes specified terms while keeping others.""" + asset_type = Table + table = Table() + table.name = "table-test" + table.qualified_name = "table_qn" + + existing_term = AtlasGlossaryTerm( + qualified_name="term_to_remove", guid="b4113341-251b-4adc-81fb-2420501c30e6" + ) + other_term = AtlasGlossaryTerm( + qualified_name="other_term", guid="b267858d-8316-4c41-a56a-6e9b840cef4a" + ) + table.attributes.meanings = [existing_term, other_term] + + with patch("pyatlan_v9.model.fluent_search.FluentSearch.execute") as mock_execute: + with patch.object(V9AssetClient, "save") as mock_save: + mock_execute.return_value.current_page = lambda: [table] + + def mock_save_side_effect(entity): + entity.assigned_terms = [ + t for t in table.attributes.meanings if t != existing_term + ] + return Mock(assets_updated=lambda asset_type: [entity]) + + mock_save.side_effect = mock_save_side_effect + + client = AtlanClient() + guid = "123" + + asset = client.asset.remove_terms( + guid=guid, asset_type=asset_type, terms=[existing_term] + ) + + updated_terms = asset.assigned_terms + assert updated_terms is not None + assert len(updated_terms) == 1 + assert existing_term not in updated_terms + assert other_term in updated_terms + mock_execute.assert_called_once() + mock_save.assert_called_once() + + +# --------------------------------------------------------------------------- +# Find glossary tests +# --------------------------------------------------------------------------- + + +@pytest.mark.parametrize( + "name, attributes, message", + [ + ( + 1, + None, + "1 validation error for FindGlossaryByName\nname\n str type expected", + ), + ( + None, + None, + "1 validation error for FindGlossaryByName\nname\n none is not an allowed value", + ), + ( + "Bob", + 1, + "1 validation error for FindGlossaryByName\nattributes\n value is not a valid list", + ), + ( + " ", + None, + "1 validation error for WithName\nvalue\n ensure this value has at least 1 characters", + ), + ], +) +def test_find_glossary_by_name_with_bad_values_raises_value_error( + name, attributes, message, client: AtlanClient +): + """Test that find_glossary_by_name raises ValueError for bad inputs.""" + with pytest.raises(ValueError, match=message): + client.asset.find_glossary_by_name(name=name, attributes=attributes) + + +@patch.object(V9AssetClient, "search") +def test_find_glossary_when_none_found_raises_not_found_error(mock_search): + """Test that find_glossary_by_name raises NotFoundError when none found.""" + mock_search.return_value.count = 0 + + client = AtlanClient() + with pytest.raises( + NotFoundError, + match=f"The AtlasGlossary asset could not be found by name: {GLOSSARY_NAME}.", + ): + client.asset.find_glossary_by_name(GLOSSARY_NAME) + + +@patch.object(V9AssetClient, "search") +def test_find_glossary_when_non_glossary_found_raises_not_found_error(mock_search): + """Test that find_glossary_by_name raises NotFoundError when wrong type found.""" + mock_search.return_value.count = 1 + mock_search.return_value.current_page.return_value = [Table()] + + client = AtlanClient() + with pytest.raises( + NotFoundError, + match=f"The AtlasGlossary asset could not be found by name: {GLOSSARY_NAME}.", + ): + client.asset.find_glossary_by_name(GLOSSARY_NAME) + mock_search.return_value.current_page.assert_called_once() + + +@patch.object(V9AssetClient, "search") +def test_find_personas_by_name_when_none_found_raises_not_found_error(mock_search): + """Test that find_personas_by_name raises NotFoundError when none found.""" + mock_search.return_value.count = 0 + + client = AtlanClient() + with pytest.raises( + NotFoundError, + match=f"The Persona asset could not be found by name: {PERSONA_NAME}.", + ): + client.asset.find_personas_by_name(name=PERSONA_NAME) + + +@patch.object(V9AssetClient, "search") +def test_find_purposes_by_name_when_none_found_raises_not_found_error(mock_search): + """Test that find_purposes_by_name raises NotFoundError when none found.""" + mock_search.return_value.count = 0 + + client = AtlanClient() + with pytest.raises( + NotFoundError, + match=f"The Purpose asset could not be found by name: {PURPOSE_NAME}.", + ): + client.asset.find_purposes_by_name(name=PURPOSE_NAME) + + +@patch.object(V9AssetClient, "search") +def test_find_connections_by_name_when_none_found_raises_not_found_error(mock_search): + """Test that find_connections_by_name raises NotFoundError when none found.""" + mock_search.return_value.count = 0 + + client = AtlanClient() + with pytest.raises( + NotFoundError, + match=f"The Connection asset could not be found by name: {CONNECTION_NAME}.", + ): + client.asset.find_connections_by_name( + name=CONNECTION_NAME, connector_type=CONNECTOR_TYPE + ) + + +@patch.object(V9AssetClient, "search") +def test_find_glossary(mock_search, caplog): + """Test that find_glossary_by_name returns first glossary and logs warning for multiples.""" + request = None + attributes = ["name"] + + def get_request(*args, **kwargs): + nonlocal request + request = args[0] + mock = Mock() + mock.count = 1 + mock.current_page.return_value = [GLOSSARY, GLOSSARY] + return mock + + mock_search.side_effect = get_request + + client = AtlanClient() + + assert GLOSSARY == client.asset.find_glossary_by_name( + name=GLOSSARY_NAME, attributes=attributes + ) + assert ( + f"More than 1 AtlasGlossary found with the name '{GLOSSARY_NAME}', returning only the first." + in caplog.text + ) + assert request + assert request.attributes + assert attributes == request.attributes + assert request.dsl + assert request.dsl.query + assert isinstance(request.dsl.query, Bool) + assert request.dsl.query.filter + assert 3 == len(request.dsl.query.filter) + term1, term2, term3 = request.dsl.query.filter + assert isinstance(term1, Term) + assert term1.field == "__state" + assert term1.value == "ACTIVE" + assert isinstance(term2, Term) + assert term2.field == "__typeName.keyword" + assert term2.value == "AtlasGlossary" + assert isinstance(term3, Term) + assert term3.field == "name.keyword" + assert term3.value == GLOSSARY_NAME + + +# --------------------------------------------------------------------------- +# Find category fast tests +# --------------------------------------------------------------------------- + + +@pytest.mark.parametrize( + "name, glossary_qualified_name, attributes, message", + [ + ( + 1, + GLOSSARY_QUALIFIED_NAME, + None, + "1 validation error for FindCategoryFastByName\nname\n str type expected", + ), + ( + None, + GLOSSARY_QUALIFIED_NAME, + None, + "1 validation error for FindCategoryFastByName\nname\n none is not an allowed value", + ), + ( + " ", + GLOSSARY_QUALIFIED_NAME, + None, + "1 validation error for WithName\nvalue\n ensure this value has at least 1 characters", + ), + ( + GLOSSARY_CATEGORY_NAME, + None, + None, + "1 validation error for FindCategoryFastByName\nglossary_qualified_name\n none is not an allowed value", + ), + ( + GLOSSARY_CATEGORY_NAME, + " ", + None, + "1 validation error for WithGlossary\nqualified_name\n ensure this value has at least 1 characters", + ), + ( + GLOSSARY_CATEGORY_NAME, + 1, + None, + "1 validation error for FindCategoryFastByName\nglossary_qualified_name\n str type expected", + ), + ( + GLOSSARY_NAME, + GLOSSARY_QUALIFIED_NAME, + 1, + "1 validation error for FindCategoryFastByName\nattributes\n value is not a valid list", + ), + ], +) +def test_find_category_fast_by_name_with_bad_values_raises_value_error( + name, glossary_qualified_name, attributes, message, client: AtlanClient +): + """Test that find_category_fast_by_name raises ValueError for bad inputs.""" + with pytest.raises(ValueError, match=message): + client.asset.find_category_fast_by_name( + name=name, + glossary_qualified_name=glossary_qualified_name, + attributes=attributes, + ) + + +@patch.object(V9AssetClient, "search") +def test_find_category_fast_by_name_when_none_found_raises_not_found_error(mock_search): + """Test that find_category_fast_by_name raises NotFoundError when none found.""" + mock_search.return_value.count = 0 + + client = AtlanClient() + with pytest.raises( + NotFoundError, + match=f"The AtlasGlossaryCategory asset could not be found by name: {GLOSSARY_CATEGORY_NAME}.", + ): + client.asset.find_category_fast_by_name( + name=GLOSSARY_CATEGORY_NAME, glossary_qualified_name=GLOSSARY_QUALIFIED_NAME + ) + + +@patch.object(V9AssetClient, "search") +def test_find_category_fast_by_name_when_non_category_found_raises_not_found_error( + mock_search, +): + """Test that find_category_fast_by_name raises NotFoundError when wrong type found.""" + mock_search.return_value.count = 1 + mock_search.return_value.current_page.return_value = [Table()] + + client = AtlanClient() + with pytest.raises( + NotFoundError, + match=f"The AtlasGlossaryCategory asset could not be found by name: {GLOSSARY_CATEGORY_NAME}.", + ): + client.asset.find_category_fast_by_name( + name=GLOSSARY_CATEGORY_NAME, glossary_qualified_name=GLOSSARY_QUALIFIED_NAME + ) + mock_search.return_value.current_page.assert_called_once() + + +@patch.object(V9AssetClient, "search") +def test_find_category_fast_by_name(mock_search, caplog): + """Test that find_category_fast_by_name returns correct category and builds correct query.""" + request = None + attributes = ["name"] + + def get_request(*args, **kwargs): + nonlocal request + request = args[0] + mock = Mock() + mock.count = 1 + mock.current_page.return_value = [GLOSSARY_CATEGORY, GLOSSARY_CATEGORY] + return mock + + mock_search.side_effect = get_request + + client = AtlanClient() + + assert ( + GLOSSARY_CATEGORY + == client.asset.find_category_fast_by_name( + name=GLOSSARY_CATEGORY_NAME, + glossary_qualified_name=GLOSSARY_QUALIFIED_NAME, + attributes=attributes, + )[0] + ) + assert request + assert request.attributes + assert attributes == request.attributes + assert request.dsl + assert request.dsl.query + assert isinstance(request.dsl.query, Bool) + assert request.dsl.query.filter + assert 4 == len(request.dsl.query.filter) + term1, term2, term3, term4 = request.dsl.query.filter + assert term1.field == "__state" + assert term1.value == "ACTIVE" + assert isinstance(term2, Term) + assert term2.field == "__typeName.keyword" + assert term2.value == "AtlasGlossaryCategory" + assert isinstance(term3, Term) + assert term3.field == "name.keyword" + assert term3.value == GLOSSARY_CATEGORY_NAME + assert isinstance(term4, Term) + assert term4.field == "__glossary" + assert term4.value == GLOSSARY_QUALIFIED_NAME + + +# --------------------------------------------------------------------------- +# Find category by name tests +# --------------------------------------------------------------------------- + + +@pytest.mark.parametrize( + "name, glossary_name, attributes, message", + [ + ( + None, + GLOSSARY_NAME, + None, + "1 validation error for FindCategoryByName\nname\n none is not an allowed value", + ), + ( + " ", + GLOSSARY_NAME, + None, + "1 validation error for WithName\nvalue\n ensure this value has at least 1 characters", + ), + ( + 1, + GLOSSARY_NAME, + None, + "1 validation error for FindCategoryByName\nname\n str type expected", + ), + ( + GLOSSARY_CATEGORY_NAME, + None, + None, + "1 validation error for FindCategoryByName\nglossary_name\n none is not an allowed value", + ), + ( + GLOSSARY_CATEGORY_NAME, + " ", + None, + "1 validation error for WithName\nvalue\n ensure this value has at least 1 characters", + ), + ( + GLOSSARY_CATEGORY_NAME, + 1, + None, + "1 validation error for FindCategoryByName\nglossary_name\n str type expected", + ), + ( + GLOSSARY_CATEGORY_NAME, + GLOSSARY_NAME, + 1, + "1 validation error for FindCategoryByName\nattributes\n value is not a valid list", + ), + ], +) +@patch.object(V9AssetClient, "find_glossary_by_name") +def test_find_category_by_name_when_bad_parameter_raises_value_error( + mock_find_glossary, name, glossary_name, attributes, message, client: AtlanClient +): + """Test that find_category_by_name raises ValueError for bad parameters.""" + + def _mock_side_effect(*, name: str = None, attributes=None): + if name is None: + raise ValueError( + "1 validation error for FindCategoryByName\nglossary_name\n none is not an allowed value" + ) + if name == " " or (isinstance(name, str) and not name.strip()): + raise ValueError( + "1 validation error for WithName\nvalue\n ensure this value has at least 1 characters" + ) + if not isinstance(name, str): + raise ValueError( + "1 validation error for FindCategoryByName\nglossary_name\n str type expected" + ) + return GLOSSARY + + mock_find_glossary.side_effect = _mock_side_effect + sut = client + + with pytest.raises(ValueError, match=message): + sut.asset.find_category_by_name( + name=name, glossary_name=glossary_name, attributes=attributes + ) + + +def test_find_category_by_name(): + """Test that find_category_by_name delegates correctly to find_glossary and find_category_fast.""" + attributes = ["name"] + with patch.multiple( + V9AssetClient, find_glossary_by_name=DEFAULT, find_category_fast_by_name=DEFAULT + ) as values: + mock_find_glossary_by_name = values["find_glossary_by_name"] + mock_find_glossary_by_name.return_value.qualified_name = GLOSSARY_QUALIFIED_NAME + mock_find_category_fast_by_name = values["find_category_fast_by_name"] + + sut = AtlanClient() + + category = sut.asset.find_category_by_name( + name=GLOSSARY_CATEGORY_NAME, + glossary_name=GLOSSARY_NAME, + attributes=attributes, + ) + + mock_find_glossary_by_name.assert_called_with(name=GLOSSARY_NAME) + mock_find_category_fast_by_name.assert_called_with( + name=GLOSSARY_CATEGORY_NAME, + glossary_qualified_name=GLOSSARY_QUALIFIED_NAME, + attributes=attributes, + ) + assert mock_find_category_fast_by_name.return_value == category + + +@patch.object(V9AssetClient, "find_glossary_by_name") +def test_find_category_by_name_qn_guid_correctly_populated( + mock_find_glossary_by_name, mock_api_caller, glossary_category_by_name_json +): + """Test that find_category_by_name correctly populates qn and guid on returned category.""" + client = V9AssetClient(mock_api_caller) + mock_find_glossary_by_name.return_value.qualified_name = GLOSSARY_QUALIFIED_NAME + mock_api_caller._call_api.side_effect = [glossary_category_by_name_json] + + category = client.find_category_by_name( + name="test-cat-1-1", + glossary_name="test-glossary", + attributes=["terms", "anchor", "parentCategory"], + )[0] + category_json = glossary_category_by_name_json["entities"][0] + + assert category + assert category_json + assert category.guid == category_json.get("guid") + category_json_attributes = category_json.get("attributes") + assert category_json_attributes + assert category.name == category_json_attributes.get("name") + assert category.qualified_name == category_json_attributes.get("qualifiedName") + + # Glossary + assert category.anchor.guid == category_json_attributes.get("anchor").get("guid") + assert category.anchor.name == category_json_attributes.get("anchor").get( + "attributes" + ).get("name") + assert category.anchor.qualified_name == category_json_attributes.get("anchor").get( + "uniqueAttributes" + ).get("qualifiedName") + + # Glossary category + assert category.parent_category.guid == category_json_attributes.get( + "parentCategory" + ).get("guid") + assert category.parent_category.name == category_json_attributes.get( + "parentCategory" + ).get("attributes").get("name") + assert category.parent_category.qualified_name == category_json_attributes.get( + "parentCategory" + ).get("uniqueAttributes").get("qualifiedName") + + # Glossary term + assert category.terms[0].guid == category_json_attributes.get("terms")[0].get( + "guid" + ) + assert category.terms[0].name == category_json_attributes.get("terms")[0].get( + "attributes" + ).get("name") + assert category.terms[0].qualified_name == category_json_attributes.get("terms")[ + 0 + ].get("uniqueAttributes").get("qualifiedName") + mock_api_caller.reset_mock() + + +# --------------------------------------------------------------------------- +# Find term fast tests +# --------------------------------------------------------------------------- + + +@pytest.mark.parametrize( + "name, glossary_qualified_name, attributes, message", + [ + ( + 1, + GLOSSARY_QUALIFIED_NAME, + None, + "1 validation error for FindTermFastByName\nname\n str type expected", + ), + ( + None, + GLOSSARY_QUALIFIED_NAME, + None, + "1 validation error for FindTermFastByName\nname\n none is not an allowed value", + ), + ( + " ", + GLOSSARY_QUALIFIED_NAME, + None, + "1 validation error for WithName\nvalue\n ensure this value has at least 1 characters", + ), + ( + GLOSSARY_TERM_NAME, + None, + None, + "1 validation error for FindTermFastByName\nglossary_qualified_name\n none is not an allowed value", + ), + ( + GLOSSARY_TERM_NAME, + " ", + None, + "1 validation error for WithGlossary\nqualified_name\n ensure this value has at least 1 characters", + ), + ( + GLOSSARY_TERM_NAME, + 1, + None, + "1 validation error for FindTermFastByName\nglossary_qualified_name\n str type expected", + ), + ( + GLOSSARY_TERM_NAME, + GLOSSARY_QUALIFIED_NAME, + 1, + "1 validation error for FindTermFastByName\nattributes\n value is not a valid list", + ), + ], +) +def test_find_term_fast_by_name_with_bad_values_raises_value_error( + name, glossary_qualified_name, attributes, message, client: AtlanClient +): + """Test that find_term_fast_by_name raises ValueError for bad inputs.""" + with pytest.raises(ValueError, match=message): + client.asset.find_term_fast_by_name( + name=name, + glossary_qualified_name=glossary_qualified_name, + attributes=attributes, + ) + + +@patch.object(V9AssetClient, "search") +def test_find_term_fast_by_name_when_none_found_raises_not_found_error(mock_search): + """Test that find_term_fast_by_name raises NotFoundError when none found.""" + mock_search.return_value.count = 0 + + client = AtlanClient() + with pytest.raises( + NotFoundError, + match=f"The AtlasGlossaryTerm asset could not be found by name: {GLOSSARY_TERM_NAME}.", + ): + client.asset.find_term_fast_by_name( + name=GLOSSARY_TERM_NAME, glossary_qualified_name=GLOSSARY_QUALIFIED_NAME + ) + + +@patch.object(V9AssetClient, "search") +def test_find_term_fast_by_name_when_non_term_found_raises_not_found_error( + mock_search, +): + """Test that find_term_fast_by_name raises NotFoundError when wrong type found.""" + mock_search.return_value.count = 1 + mock_search.return_value.current_page.return_value = [Table()] + + client = AtlanClient() + with pytest.raises( + NotFoundError, + match=f"The AtlasGlossaryTerm asset could not be found by name: {GLOSSARY_TERM_NAME}.", + ): + client.asset.find_term_fast_by_name( + name=GLOSSARY_TERM_NAME, glossary_qualified_name=GLOSSARY_QUALIFIED_NAME + ) + mock_search.return_value.current_page.assert_called_once() + + +@patch.object(V9AssetClient, "search") +def test_find_term_fast_by_name(mock_search, caplog): + """Test that find_term_fast_by_name returns correct term and builds correct query.""" + request = None + attributes = ["name"] + + def get_request(*args, **kwargs): + nonlocal request + request = args[0] + mock = Mock() + mock.count = 1 + mock.current_page.return_value = [GLOSSARY_TERM, GLOSSARY_TERM] + return mock + + mock_search.side_effect = get_request + + client = AtlanClient() + + assert GLOSSARY_TERM == client.asset.find_term_fast_by_name( + name=GLOSSARY_TERM_NAME, + glossary_qualified_name=GLOSSARY_QUALIFIED_NAME, + attributes=attributes, + ) + assert ( + f"More than 1 AtlasGlossaryTerm found with the name '{GLOSSARY_TERM_NAME}', returning only the first." + in caplog.text + ) + assert request + assert request.attributes + assert attributes == request.attributes + assert request.dsl + assert request.dsl.query + assert isinstance(request.dsl.query, Bool) + assert request.dsl.query.filter + assert 4 == len(request.dsl.query.filter) + term1, term2, term3, term4 = request.dsl.query.filter + assert term1.field == "__state" + assert term1.value == "ACTIVE" + assert isinstance(term2, Term) + assert term2.field == "__typeName.keyword" + assert term2.value == "AtlasGlossaryTerm" + assert isinstance(term3, Term) + assert term3.field == "name.keyword" + assert term3.value == GLOSSARY_TERM_NAME + assert isinstance(term4, Term) + assert term4.field == "__glossary" + assert term4.value == GLOSSARY_QUALIFIED_NAME + + +# --------------------------------------------------------------------------- +# Find term by name tests +# --------------------------------------------------------------------------- + + +@pytest.mark.parametrize( + "name, glossary_name, attributes, message", + [ + ( + None, + GLOSSARY_NAME, + None, + "1 validation error for FindTermByName\nname\n none is not an allowed value", + ), + ( + " ", + GLOSSARY_NAME, + None, + "1 validation error for WithName\nvalue\n ensure this value has at least 1 characters", + ), + ( + 1, + GLOSSARY_NAME, + None, + "1 validation error for FindTermByName\nname\n str type expected", + ), + ( + GLOSSARY_TERM_NAME, + None, + None, + "1 validation error for FindTermByName\nglossary_name\n none is not an allowed value", + ), + ( + GLOSSARY_TERM_NAME, + " ", + None, + "1 validation error for WithName\nvalue\n ensure this value has at least 1 characters", + ), + ( + GLOSSARY_TERM_NAME, + 1, + None, + "1 validation error for FindTermByName\nglossary_name\n str type expected", + ), + ( + GLOSSARY_TERM_NAME, + GLOSSARY_NAME, + 1, + "1 validation error for FindTermByName\nattributes\n value is not a valid list", + ), + ], +) +@patch.object(V9AssetClient, "find_glossary_by_name") +def test_find_term_by_name_when_bad_parameter_raises_value_error( + mock_find_glossary, name, glossary_name, attributes, message, client: AtlanClient +): + """Test that find_term_by_name raises ValueError for bad parameters.""" + + def _mock_side_effect(*, name: str = None, attributes=None): + if name is None: + raise ValueError( + "1 validation error for FindTermByName\nglossary_name\n none is not an allowed value" + ) + if name == " " or (isinstance(name, str) and not name.strip()): + raise ValueError( + "1 validation error for WithName\nvalue\n ensure this value has at least 1 characters" + ) + if not isinstance(name, str): + raise ValueError( + "1 validation error for FindTermByName\nglossary_name\n str type expected" + ) + return GLOSSARY + + mock_find_glossary.side_effect = _mock_side_effect + sut = client + + with pytest.raises(ValueError, match=message): + sut.asset.find_term_by_name( + name=name, glossary_name=glossary_name, attributes=attributes + ) + + +def test_find_term_by_name(): + """Test that find_term_by_name delegates correctly to find_glossary and find_term_fast.""" + attributes = ["name"] + with patch.multiple( + V9AssetClient, find_glossary_by_name=DEFAULT, find_term_fast_by_name=DEFAULT + ) as values: + mock_find_glossary_by_name = values["find_glossary_by_name"] + mock_find_glossary_by_name.return_value.qualified_name = GLOSSARY_QUALIFIED_NAME + mock_find_term_fast_by_name = values["find_term_fast_by_name"] + + sut = AtlanClient() + + term = sut.asset.find_term_by_name( + name=GLOSSARY_TERM_NAME, + glossary_name=GLOSSARY_NAME, + attributes=attributes, + ) + + mock_find_glossary_by_name.assert_called_with(name=GLOSSARY_NAME) + mock_find_term_fast_by_name.assert_called_with( + name=GLOSSARY_TERM_NAME, + glossary_qualified_name=GLOSSARY_QUALIFIED_NAME, + attributes=attributes, + ) + assert mock_find_term_fast_by_name.return_value == term + + +# --------------------------------------------------------------------------- +# Find domain/product tests +# --------------------------------------------------------------------------- + + +@patch.object(V9AssetClient, "_search_for_asset_with_name") +def test_find_domain_by_name(mock_search_for_asset_with_name): + """Test that find_domain_by_name returns correct domain.""" + client = AtlanClient() + test_domain = DataDomain() + test_domain.name = DATA_DOMAIN_NAME + mock_search_for_asset_with_name.return_value = [test_domain] + + domain = client.asset.find_domain_by_name( + name=DATA_DOMAIN_NAME, + attributes=["name"], + ) + + assert domain and domain == test_domain + assert mock_search_for_asset_with_name.call_count == 1 + + +@patch.object(V9AssetClient, "_search_for_asset_with_name") +def test_find_product_by_name(mock_search_for_asset_with_name): + """Test that find_product_by_name returns correct product.""" + client = AtlanClient() + test_product = DataProduct() + test_product.name = DATA_PRODUCT_NAME + mock_search_for_asset_with_name.return_value = [test_product] + + product = client.asset.find_product_by_name( + name=DATA_PRODUCT_NAME, + attributes=["name"], + ) + + assert product and product == test_product + assert mock_search_for_asset_with_name.call_count == 1 + + +# --------------------------------------------------------------------------- +# Search log tests +# --------------------------------------------------------------------------- + + +def test_search_log_most_recent_viewers(mock_api_caller, sl_most_recent_viewers_json): + """Test search log most recent viewers parsing.""" + client = SearchLogClient(mock_api_caller) + mock_api_caller._call_api.return_value = sl_most_recent_viewers_json + recent_viewers_aggs = sl_most_recent_viewers_json["aggregations"] + recent_viewers_aggs_buckets = recent_viewers_aggs[UNIQUE_USERS]["buckets"] + request = SearchLogRequest.most_recent_viewers( + guid="test-guid-123", exclude_users=["testuser"] + ) + request_dsl_json = loads(request.dsl.json(by_alias=True, exclude_none=True)) + response = client.search(request) + viewers = response.user_views + assert len(viewers) == 3 + assert response.asset_views is None + assert request_dsl_json == sl_most_recent_viewers_json[SEARCH_PARAMS]["dsl"] + assert response.count == sl_most_recent_viewers_json[SEARCH_COUNT] + assert viewers[0].username == recent_viewers_aggs_buckets[0]["key"] + assert viewers[0].view_count == recent_viewers_aggs_buckets[0]["doc_count"] + assert viewers[0].most_recent_view + assert viewers[1].username == recent_viewers_aggs_buckets[1]["key"] + assert viewers[1].view_count == recent_viewers_aggs_buckets[1]["doc_count"] + assert viewers[1].most_recent_view + mock_api_caller.reset_mock() + + +def test_search_log_most_viewed_assets(mock_api_caller, sl_most_viewed_assets_json): + """Test search log most viewed assets parsing.""" + client = SearchLogClient(mock_api_caller) + mock_api_caller._call_api.return_value = sl_most_viewed_assets_json + viewed_assets_aggs = sl_most_viewed_assets_json["aggregations"] + viewed_assets_aggs_buckets = viewed_assets_aggs[UNIQUE_ASSETS]["buckets"][0] + request = SearchLogRequest.most_viewed_assets( + max_assets=10, exclude_users=["testuser"] + ) + request_dsl_json = loads(request.dsl.json(by_alias=True, exclude_none=True)) + response = client.search(request) + detail = response.asset_views + assert len(detail) == 8 + assert response.user_views is None + assert request_dsl_json == sl_most_viewed_assets_json[SEARCH_PARAMS]["dsl"] + assert response.count == sl_most_viewed_assets_json[SEARCH_COUNT] + assert detail[0].guid == viewed_assets_aggs_buckets["key"] + assert detail[0].total_views == viewed_assets_aggs_buckets["doc_count"] + assert detail[0].distinct_users == viewed_assets_aggs_buckets[UNIQUE_USERS]["value"] + mock_api_caller.reset_mock() + + +def test_search_log_views_by_guid(mock_api_caller, sl_detailed_log_entries_json): + """Test search log views by GUID with detailed log entries.""" + client = SearchLogClient(mock_api_caller) + mock_api_caller._call_api.return_value = sl_detailed_log_entries_json + sl_detailed_log_entries = sl_detailed_log_entries_json["logs"] + request = SearchLogRequest.views_by_guid( + guid="test-guid-123", size=10, exclude_users=["testuser"] + ) + request_dsl_json = loads(request.dsl.json(by_alias=True, exclude_none=True)) + response = client.search(request) + log_entries = response.current_page() + assert request_dsl_json == sl_detailed_log_entries_json[SEARCH_PARAMS]["dsl"] + assert len(response.current_page()) == sl_detailed_log_entries_json[SEARCH_COUNT] + assert log_entries[0].user_name == sl_detailed_log_entries[0][LOG_USERNAME] + assert log_entries[0].ip_address == sl_detailed_log_entries[0][LOG_IP_ADDRESS] + assert log_entries[0].host + assert log_entries[0].user_agent + assert log_entries[0].utm_tags + assert log_entries[0].entity_guids_all + assert log_entries[0].entity_guids_allowed + assert log_entries[0].entity_qf_names_all + assert log_entries[0].entity_qf_names_allowed + assert log_entries[0].entity_type_names_all + assert log_entries[0].entity_type_names_allowed + assert log_entries[0].has_result + assert log_entries[0].results_count + assert log_entries[0].response_time + assert log_entries[0].created_at + assert log_entries[0].timestamp + assert log_entries[0].failed is False + assert log_entries[0].request_dsl + assert log_entries[0].request_dsl_text + assert log_entries[0].request_attributes is None + assert log_entries[0].request_relation_attributes + mock_api_caller.reset_mock() + + +# --------------------------------------------------------------------------- +# Lineage tests +# --------------------------------------------------------------------------- + + +def test_asset_get_lineage_list_response_with_custom_metadata( + mock_api_caller, lineage_list_json +): + """Test lineage list response includes custom metadata attributes.""" + asset_client = V9AssetClient(mock_api_caller) + mock_api_caller._call_api.side_effect = [lineage_list_json, {}] + + lineage_request = LineageListRequest( + guid="test-guid", depth=1, direction=LineageDirection.UPSTREAM + ) + lineage_request.attributes = [CM_NAME] + lineage_response = asset_client.get_lineage_list(lineage_request=lineage_request) + + for asset in lineage_response: + assert asset + assert asset.depth == 1 + assert asset.type_name == "View" + assert asset.guid == "test-guid" + assert asset.qualified_name == "test-qn" + assert asset.attributes + assert asset.business_attributes + assert asset.business_attributes == {"testcm1": {"testcm2": "test-cm-value"}} + + assert mock_api_caller._call_api.call_count == 1 + mock_api_caller.reset_mock() + + +# --------------------------------------------------------------------------- +# Group/User pagination tests +# --------------------------------------------------------------------------- + + +def test_group_get_pagination(mock_api_caller, group_list_json): + """Test group get with pagination returns correct results.""" + client = GroupClient(mock_api_caller) + last_page_response = {"totalRecord": 3, "filterRecord": 3, "records": None} + mock_api_caller._call_api.side_effect = [group_list_json, last_page_response] + response = client.get() + + assert response + assert len(response.current_page()) == 2 + for group in response: + assert group.name + assert group.path + assert group.personas + assert len(response.current_page()) == 0 + assert mock_api_caller._call_api.call_count == 2 + mock_api_caller.reset_mock() + + +def test_group_get_members_pagination(mock_api_caller, group_members_json): + """Test group get_members with pagination returns correct results.""" + client = GroupClient(mock_api_caller) + last_page_response = {"totalRecord": 3, "filterRecord": 3, "records": None} + mock_api_caller._call_api.side_effect = [group_members_json, last_page_response] + response = client.get_members(guid="test-guid", request=UserRequest()) + + assert response + assert len(response.current_page()) == 3 + for user in response: + assert user.username + assert user.email + assert user.attributes + assert len(response.current_page()) == 0 + assert mock_api_caller._call_api.call_count == 2 + mock_api_caller.reset_mock() + + +def test_user_list_pagination(mock_api_caller, user_list_json): + """Test user list with pagination returns correct results.""" + client = UserClient(mock_api_caller) + last_page_response = {"totalRecord": 3, "filterRecord": 3, "records": None} + mock_api_caller._call_api.side_effect = [user_list_json, last_page_response] + response = client.get() + + assert response + assert len(response.current_page()) == 3 + for user in response: + assert user.username + assert user.email + assert user.attributes + assert user.login_events + assert len(response.current_page()) == 0 + assert mock_api_caller._call_api.call_count == 2 + mock_api_caller.reset_mock() + + +def test_user_groups_pagination(mock_api_caller, user_groups_json): + """Test user groups with pagination returns correct results.""" + client = UserClient(mock_api_caller) + last_page_response = {"totalRecord": 2, "filterRecord": 2, "records": None} + mock_api_caller._call_api.side_effect = [user_groups_json, last_page_response] + response = client.get_groups(guid="test-guid", request=GroupRequest()) + + assert response + assert len(response.current_page()) == 2 + for group in response: + assert group.name + assert group.path + assert group.alias + assert group.attributes + assert len(response.current_page()) == 0 + assert mock_api_caller._call_api.call_count == 2 + mock_api_caller.reset_mock() + + +# --------------------------------------------------------------------------- +# Index search tests +# --------------------------------------------------------------------------- + + +def test_index_search_with_no_aggregation_results( + mock_api_caller, aggregations_null_json +): + """Test index search handles null aggregation results.""" + client = V9AssetClient(mock_api_caller) + mock_api_caller._call_api.side_effect = [aggregations_null_json] + request = ( + FluentSearch( + aggregations={"test1": {"test2": {"field": "__test_field"}}} + ).where(Column.QUALIFIED_NAME.startswith("test-qn")) + ).to_request() + response = client.search(criteria=request) + assert response + assert response.count == 0 + assert response.aggregations is None + mock_api_caller.reset_mock() + + +def test_type_name_in_asset_search_bool_filter(mock_api_caller): + """Test that type name filter is correctly added/not added in search bool filter.""" + # When the type name is not present in the request + request = (FluentSearch().where(CompoundQuery.active_assets())).to_request() + Search._ensure_type_filter_present(request) + + assert request.dsl.query and request.dsl.query.filter + assert isinstance(request.dsl.query.filter, list) + + has_type_filter = any( + isinstance(f, Term) and f.field == TermAttributes.SUPER_TYPE_NAMES.value + for f in request.dsl.query.filter + ) + assert has_type_filter is True + + # When the type name is present in the request (no need to add super type filter) + request = ( + FluentSearch() + .where(CompoundQuery.active_assets()) + .where(CompoundQuery.asset_type(AtlasGlossary)) + ).to_request() + Search._ensure_type_filter_present(request) + + assert request.dsl.query and request.dsl.query.filter + assert isinstance(request.dsl.query.filter, list) + + has_type_filter = any( + isinstance(f, Term) and f.field == TermAttributes.SUPER_TYPE_NAMES.value + for f in request.dsl.query.filter + ) + assert has_type_filter is False + + # When multiple type name(s) is present in the request (no need to add super type filter) + request = ( + FluentSearch() + .where(CompoundQuery.active_assets()) + .where(CompoundQuery.asset_types([AtlasGlossary, AtlasGlossaryTerm])) + ).to_request() + Search._ensure_type_filter_present(request) + + assert request.dsl.query and request.dsl.query.filter + assert isinstance(request.dsl.query.filter, list) + + has_type_filter = any( + isinstance(f, Term) and f.field == TermAttributes.SUPER_TYPE_NAMES.value + for f in request.dsl.query.filter + ) + assert has_type_filter is False + + +def test_type_name_in_asset_search_bool_must(mock_api_caller): + """Test that type name filter is correctly added/not added in search bool must.""" + # When the type name is not present in the request + query = Bool(must=[Term.with_state("ACTIVE")]) + request = IndexSearchRequest(dsl=DSL(query=query)) + Search._ensure_type_filter_present(request) + + assert request.dsl.query and request.dsl.query.must + assert isinstance(request.dsl.query.must, list) + + has_type_filter = any( + isinstance(f, Term) and f.field == TermAttributes.SUPER_TYPE_NAMES.value + for f in request.dsl.query.must + ) + assert has_type_filter is True + + # When the type name is present in the request (no need to add super type filter) + query = Bool(must=[Term.with_state("ACTIVE"), Term.with_type_name("AtlasGlossary")]) + request = IndexSearchRequest(dsl=DSL(query=query)) + Search._ensure_type_filter_present(request) + + assert request.dsl.query and request.dsl.query.must + assert isinstance(request.dsl.query.must, list) + + has_type_filter = any( + isinstance(f, Term) and f.field == TermAttributes.SUPER_TYPE_NAMES.value + for f in request.dsl.query.must + ) + assert has_type_filter is False + + # When multiple type name(s) is present in the request (no need to add super type filter) + query = Bool( + must=[ + Term.with_state("ACTIVE"), + Term.with_type_name("AtlasGlossary"), + Term.with_type_name("AtlasGlossaryTerm"), + ] + ) + request = IndexSearchRequest(dsl=DSL(query=query)) + Search._ensure_type_filter_present(request) + + assert request.dsl.query and request.dsl.query.must + assert isinstance(request.dsl.query.must, list) + + has_type_filter = any( + isinstance(f, Term) and f.field == TermAttributes.SUPER_TYPE_NAMES.value + for f in request.dsl.query.must + ) + assert has_type_filter is False + + +def _assert_search_results(results, response_json, sorts, bulk=False): + """Helper to assert index search results.""" + for i, result in enumerate(results): + assert result and response_json["entities"][i] + assert result.guid == response_json["entities"][i]["guid"] + + assert results + assert results.count == 2 + assert results._bulk == bulk + assert results.aggregations is None + assert results._criteria.dsl.sort == sorts + + +@patch.object(SHARED_LOGGER, "debug") +def test_index_search_pagination( + mock_shared_logger, mock_api_caller, index_search_paging_json +): + """Test index search pagination with offset-based, bulk, and automatic bulk modes.""" + client = V9AssetClient(mock_api_caller) + mock_api_caller._call_api.side_effect = [index_search_paging_json, {}] + + # Test search(): using default offset-based pagination + # when results are less than the predefined threshold (i.e: 100,000 assets) + request = ( + FluentSearch() + .where(CompoundQuery.active_assets()) + .where(CompoundQuery.asset_type(AtlasGlossaryTerm)) + .page_size(2) + ).to_request() + results = client.search(criteria=request) + expected_sorts = [Asset.GUID.order(SortOrder.ASCENDING)] + + _assert_search_results(results, index_search_paging_json, expected_sorts) + assert mock_api_caller._call_api.call_count == 2 + mock_api_caller.reset_mock() + + # Test search(): with `bulk` option using timestamp-based pagination + mock_api_caller._call_api.side_effect = [index_search_paging_json, {}] + request = ( + FluentSearch() + .where(CompoundQuery.active_assets()) + .where(CompoundQuery.asset_type(AtlasGlossaryTerm)) + .page_size(2) + ).to_request() + results = client.search(criteria=request, bulk=True) + expected_sorts = [ + Asset.CREATE_TIME.order(SortOrder.ASCENDING), + Asset.GUID.order(SortOrder.ASCENDING), + ] + + _assert_search_results(results, index_search_paging_json, expected_sorts, True) + assert mock_api_caller._call_api.call_count == 2 + assert mock_shared_logger.call_count == 1 + assert ( + "Bulk search option is enabled." in mock_shared_logger.call_args_list[0][0][0] + ) + mock_shared_logger.reset_mock() + mock_api_caller.reset_mock() + + # Test search(): when the number of results exceeds the predefined threshold + # it will automatically convert to a `bulk` search. + TEST_THRESHOLD = 1 + with patch.object(IndexSearchResults, "_MASS_EXTRACT_THRESHOLD", TEST_THRESHOLD): + mock_api_caller._call_api.side_effect = [ + index_search_paging_json, + # Extra call to re-fetch the first page + # results with updated timestamp sorting + index_search_paging_json, + {}, + ] + request = ( + FluentSearch() + .where(CompoundQuery.active_assets()) + .where(CompoundQuery.asset_type(AtlasGlossaryTerm)) + .page_size(2) + ).to_request() + results = client.search(criteria=request) + expected_sorts = [ + Asset.CREATE_TIME.order(SortOrder.ASCENDING), + Asset.GUID.order(SortOrder.ASCENDING), + ] + _assert_search_results(results, index_search_paging_json, expected_sorts) + assert mock_api_caller._call_api.call_count == 3 + assert mock_shared_logger.call_count == 1 + assert ( + "Result size (%s) exceeds threshold (%s)" + in mock_shared_logger.call_args_list[0][0][0] + ) + mock_shared_logger.reset_mock() + mock_api_caller.reset_mock() + + # Test search(bulk=False): Raise an exception when the number of results exceeds + # the predefined threshold and there are any user-defined sorting options present + with patch.object(IndexSearchResults, "_MASS_EXTRACT_THRESHOLD", TEST_THRESHOLD): + mock_api_caller._call_api.side_effect = [ + index_search_paging_json, + ] + request = ( + FluentSearch() + .where(CompoundQuery.active_assets()) + .where(CompoundQuery.asset_type(AtlasGlossaryTerm)) + .page_size(2) + # With some sort options + .sort(Asset.NAME.order(SortOrder.ASCENDING)) + ).to_request() + + with pytest.raises( + InvalidRequestError, + match=( + "ATLAN-PYTHON-400-063 Unable to execute " + "bulk search with user-defined sorting options. " + "Suggestion: Please ensure that no sorting options are " + "included in your search request when performing a bulk search." + ), + ): + client.search(criteria=request) + assert mock_api_caller._call_api.call_count == 1 + mock_api_caller.reset_mock() + mock_api_caller.reset_mock() + + # Test search(bulk=True): Raise an exception when bulk search is enabled + # and there are any user-defined sorting options present + request = ( + FluentSearch() + .where(CompoundQuery.active_assets()) + .where(CompoundQuery.asset_type(AtlasGlossaryTerm)) + .page_size(2) + .sort(Asset.NAME.order(SortOrder.ASCENDING)) + ).to_request() + + with pytest.raises( + InvalidRequestError, + match=( + "ATLAN-PYTHON-400-063 Unable to execute " + "bulk search with user-defined sorting options. " + "Suggestion: Please ensure that no sorting options are " + "included in your search request when performing a bulk search." + ), + ): + client.search(criteria=request, bulk=True) + + +# --------------------------------------------------------------------------- +# Asset get_by_guid / retrieve_minimal tests +# --------------------------------------------------------------------------- + + +def test_asset_get_by_guid_without_asset_type(mock_api_caller, get_by_guid_json): + """Test asset get_by_guid without specifying asset type returns correct type.""" + client = V9AssetClient(mock_api_caller) + mock_api_caller._call_api.side_effect = [get_by_guid_json] + + response = client.get_by_guid( + guid="test-table-guid-123", ignore_relationships=False + ) + + assert response + assert isinstance(response, Table) + assert response.guid + assert response.qualified_name + assert response.attributes + mock_api_caller.reset_mock() + + +def test_asset_retrieve_minimal_without_asset_type( + mock_api_caller, retrieve_minimal_json +): + """Test asset retrieve_minimal without specifying asset type returns correct type.""" + client = V9AssetClient(mock_api_caller) + mock_api_caller._call_api.side_effect = [retrieve_minimal_json] + + response = client.retrieve_minimal(guid="test-table-guid-123") + + assert response + assert isinstance(response, Table) + assert response.guid + assert response.qualified_name + assert response.attributes + mock_api_caller.reset_mock() + + +# --------------------------------------------------------------------------- +# User create tests +# --------------------------------------------------------------------------- + + +def test_user_create( + mock_api_caller, + mock_role_cache, +): + """Test user creation with role assignment.""" + test_role_id = "role-guid-123" + client = UserClient(mock_api_caller) + client._client.role_cache = mock_role_cache + mock_api_caller._call_api.side_effect = [None] + mock_role_cache.get_id_for_name.return_value = test_role_id + + test_users = [AtlanUser.creator(email="test@test.com", role_name="$member")] + response = client.creator(users=test_users) + + assert response is None + mock_api_call_args = mock_api_caller._call_api.call_args_list + request_obj = mock_api_call_args[0].kwargs.get("request_obj") + user = msgspec.to_builtins(request_obj)["users"][0] + assert len(mock_api_call_args) == 1 + assert user.get("role_id") == test_role_id + assert user.get("email") == test_users[0].email + assert user.get("role_name") == test_users[0].workspace_role + mock_api_caller.reset_mock() + + +def test_user_create_with_info(mock_api_caller, mock_role_cache, user_list_json): + """Test user creation with return_info=True returns user info.""" + test_role_id = "role-guid-123" + client = UserClient(mock_api_caller) + client._client.role_cache = mock_role_cache + mock_api_caller._call_api.side_effect = [ + None, + { + "totalRecord": 3, + "filterRecord": 1, + "records": [user_list_json["records"][0]], + }, + ] + mock_role_cache.get_id_for_name.return_value = test_role_id + test_users = [AtlanUser.creator(email="test@test.com", role_name="$member")] + response = client.creator(users=test_users, return_info=True) + + assert len(response.current_page()) == 1 + user = response.current_page()[0] + assert user + assert user.username + assert user.email + assert user.attributes + assert user.login_events + assert mock_api_caller._call_api.call_count == 2 + mock_api_caller.reset_mock() + + +# --------------------------------------------------------------------------- +# TypeDef tests +# --------------------------------------------------------------------------- + + +def test_typedef_get_by_name(mock_api_caller, type_def_get_by_name_json): + """Test typedef get_by_name returns correct EnumDef.""" + client = TypeDefClient(mock_api_caller) + mock_api_caller._call_api.side_effect = [type_def_get_by_name_json] + response = client.get_by_name(name="test-enum") + assert response.name == type_def_get_by_name_json.get("name") + assert response.category == type_def_get_by_name_json.get("category") + assert mock_api_caller._call_api.call_count == 1 + mock_api_caller.reset_mock() + + +def test_typedef_get_by_name_unsupported_category(mock_api_caller): + """Test typedef get_by_name raises error for unsupported category.""" + client = TypeDefClient(mock_api_caller) + mock_api_caller._call_api.side_effect = [{"category": "TEST"}] + with pytest.raises(ApiError) as err: + client.get_by_name(name="test-enum") + + assert "Unsupported type definition category: TEST" in str(err.value) + mock_api_caller.reset_mock() + + +def test_typedef_get_by_name_invalid_response(mock_api_caller): + """Test typedef get_by_name raises error for invalid response types.""" + client = TypeDefClient(mock_api_caller) + mock_api_caller._call_api.side_effect = [123] + with pytest.raises(ApiError) as err: + client.get_by_name(name="test-enum") + assert "Additional details: 'int' object has no attribute 'get'" in str(err.value) + + mock_api_caller._call_api.side_effect = [{"category": "ENUM", "test": "invalid"}] + response = client.get_by_name(name="test-enum") + assert isinstance(response, EnumDef) + mock_api_caller.reset_mock() + + +# --------------------------------------------------------------------------- +# Asset client missing glossary GUID tests +# --------------------------------------------------------------------------- + + +@pytest.mark.parametrize( + "test_method, test_kwargs, test_asset_types", + [ + [ + "update_certificate", + { + "qualified_name": "test-qn", + "name": "test-name", + "certificate_status": CertificateStatus.VERIFIED, + "message": "test-message", + }, + [AtlasGlossaryTerm, AtlasGlossaryCategory], + ], + [ + "remove_certificate", + { + "qualified_name": "test-qn", + "name": "test-name", + }, + [AtlasGlossaryTerm, AtlasGlossaryCategory], + ], + [ + "update_announcement", + { + "qualified_name": "test-qn", + "name": "test-name", + "announcement": TEST_ANNOUNCEMENT, + }, + [AtlasGlossaryTerm, AtlasGlossaryCategory], + ], + [ + "remove_announcement", + {"qualified_name": "test-qn", "name": "test-name"}, + [AtlasGlossaryTerm, AtlasGlossaryCategory], + ], + ], +) +def test_asset_client_missing_glossary_guid_raises_invalid_request_error( + test_method: str, + test_kwargs: dict, + test_asset_types, +): + """Test that asset client methods raise error when glossary_guid is missing for glossary types.""" + client = AtlanClient() + asset_client_method = getattr(client.asset, test_method) + + for asset_type in test_asset_types: + test_error = TEST_MISSING_GLOSSARY_GUID_ERROR.format(asset_type.__name__) + with pytest.raises(InvalidRequestError, match=test_error): + asset_client_method(**test_kwargs, asset_type=asset_type) + + +# --------------------------------------------------------------------------- +# Client methods validation error tests +# --------------------------------------------------------------------------- + + +@pytest.mark.parametrize("method, params", TEST_ASSET_CLIENT_METHODS.items()) +def test_asset_client_methods_validation_error(client, method, params): + """Test that asset client methods raise ValidationError for invalid parameters.""" + client_method = getattr(client.asset, method) + for param_values, error_msg in params: + with pytest.raises(ValueError) as err: + client_method(*param_values) + assert error_msg in str(err.value) + + +@pytest.mark.parametrize("method, params", TEST_ADMIN_CLIENT_METHODS.items()) +def test_admin_client_methods_validation_error(client, method, params): + """Test that admin client methods raise ValidationError for invalid parameters.""" + client_method = getattr(client.admin, method) + for param_values, error_msg in params: + with pytest.raises(ValueError) as err: + client_method(*param_values) + assert error_msg in str(err.value) + + +@pytest.mark.parametrize("method, params", TEST_AUDIT_CLIENT_METHODS.items()) +def test_audit_client_methods_validation_error(client, method, params): + """Test that audit client methods raise ValidationError for invalid parameters.""" + client_method = getattr(client.audit, method) + for param_values, error_msg in params: + with pytest.raises(ValueError) as err: + client_method(*param_values) + assert error_msg in str(err.value) + + +@pytest.mark.parametrize("method, params", TEST_GROUP_CLIENT_METHODS.items()) +def test_group_client_methods_validation_error(client, method, params): + """Test that group client methods raise ValidationError for invalid parameters.""" + client_method = getattr(client.group, method) + for param_values, error_msg in params: + with pytest.raises(ValueError) as err: + client_method(*param_values) + assert error_msg in str(err.value) + + +@pytest.mark.parametrize("method, params", TEST_ROLE_CLIENT_METHODS.items()) +def test_role_client_methods_validation_error(client, method, params): + """Test that role client methods raise ValidationError for invalid parameters.""" + client_method = getattr(client.role, method) + for param_values, error_msg in params: + with pytest.raises(ValueError) as err: + client_method(*param_values) + assert error_msg in str(err.value) + + +@pytest.mark.parametrize("method, params", TEST_SL_CLIENT_METHODS.items()) +def test_search_log_client_methods_validation_error(client, method, params): + """Test that search log client methods raise ValidationError for invalid parameters.""" + client_method = getattr(client.search_log, method) + for param_values, error_msg in params: + with pytest.raises(ValueError) as err: + client_method(*param_values) + assert error_msg in str(err.value) + + +@pytest.mark.parametrize("method, params", TEST_TOKEN_CLIENT_METHODS.items()) +def test_token_client_methods_validation_error(client, method, params): + """Test that token client methods raise ValidationError for invalid parameters.""" + client_method = getattr(client.token, method) + for param_values, error_msg in params: + with pytest.raises(ValueError) as err: + client_method(*param_values) + assert error_msg in str(err.value) + + +@pytest.mark.parametrize("method, params", TEST_TYPEDEF_CLIENT_METHODS.items()) +def test_typedef_client_methods_validation_error(client, method, params): + """Test that typedef client methods raise ValidationError for invalid parameters.""" + client_method = getattr(client.typedef, method) + for param_values, error_msg in params: + with pytest.raises(ValueError) as err: + client_method(*param_values) + assert error_msg in str(err.value) + + +@pytest.mark.parametrize("method, params", TEST_USER_CLIENT_METHODS.items()) +def test_user_client_methods_validation_error(client, method, params): + """Test that user client methods raise ValidationError for invalid parameters.""" + client_method = getattr(client.user, method) + for param_values, error_msg in params: + with pytest.raises(ValueError) as err: + client_method(*param_values) + assert error_msg in str(err.value) + + +# --------------------------------------------------------------------------- +# Error handling tests +# --------------------------------------------------------------------------- + + +@pytest.mark.parametrize( + "test_error_msg", + [ + "{'error': 123}", + "{'error': 123, 'code': 465}", + "{'error': 123} with text", + "Some error message...", + "With unescape curly braces -> {'{}'}", + ], +) +@patch.object(AtlanClient, "_session") +def test_atlan_call_api_server_error_messages( + mock_session, + client: AtlanClient, + test_error_msg, +): + """Test that server error messages are properly propagated through AtlanError.""" + mock_response = Mock() + mock_response.status_code = 500 + mock_response.text = test_error_msg + mock_session.request.return_value = mock_response + glossary = AtlasGlossary.creator(name="test-glossary") + + with pytest.raises( + AtlanError, + match=( + f"ATLAN-PYTHON-500-000 {test_error_msg} " + "Suggestion: Check the details of the " + "server's message to correct your request." + ), + ): + client.asset.save(glossary) + + +@pytest.mark.parametrize( + "test_error_msg", + [ + """ + { + "errorCode": 1234, + "errorMessage": "something went wrong", + "causes": [ + { + "errorType": "testException", + "errorMessage": "test error message", + "location": "Test.Class.TestException" + } + ], + "errorCause": "something went wrong", + "errorId": "95d80a45999cabc", + "doc": "https://ask.atlan.com/hc/en-us/articles/6645223434141-Is-there-a-limit-on-the-number-of-API-requests-that-can-be-performed" + } + """ + ], +) +@patch.object(AtlanClient, "_session") +def test_atlan_call_api_server_error_messages_with_causes( + mock_session, + client: AtlanClient, + test_error_msg, +): + """Test that error messages with causes are properly parsed and raised.""" + ERROR_CODE_FOR_HTTP_STATUS.update( + {ErrorCode.ERROR_PASSTHROUGH.http_error_code: ErrorCode.ERROR_PASSTHROUGH} + ) + STATUS_CODES = set(ERROR_CODE_FOR_HTTP_STATUS.keys()) + # For "NOT_FOUND (404)" errors, no error cause is returned by the backend + STATUS_CODES.remove(ErrorCode.NOT_FOUND_PASSTHROUGH.http_error_code) + + for code in STATUS_CODES: + error = ERROR_CODE_FOR_HTTP_STATUS.get(code) + mock_response = Mock() + mock_response.status_code = code + mock_response.text = test_error_msg + mock_session.request.return_value = mock_response + test_error = loads(test_error_msg) + error_code = test_error.get("errorCode") + error_message = test_error.get("errorMessage") + error_cause = test_error.get("errorCause") + error_doc = test_error.get("doc") + error_id = test_error.get("errorId") + error_causes = test_error.get("causes")[0] + glossary = AtlasGlossary.creator(name="test-glossary") + error_causes = "ErrorType: testException, Message: test error message, Location: Test.Class.TestException" + assert error and error_code and error_message and error_cause and error_causes + error_info = error.exception_with_parameters( + error_code, + error_message, + error_causes, + error_cause=error_cause, + backend_error_id=error_id, + error_doc=error_doc, + ) + + with pytest.raises( + AtlanError, + match=escape(str(error_info)), + ): + client.asset.save(glossary) + + +# --------------------------------------------------------------------------- +# Batch tests +# --------------------------------------------------------------------------- + + +class TestBatch: + """Tests for the Batch class.""" + + def test_init(self, mock_atlan_client): + """Test batch initialization with empty state.""" + sut = Batch(client=mock_atlan_client, max_size=10) + + self.assert_asset_client_not_called(mock_atlan_client, sut) + + def assert_asset_client_not_called(self, mock_atlan_client, sut): + """Helper: assert batch has no created/updated/failures.""" + assert 0 == len(sut.created) + assert 0 == len(sut.updated) + assert 0 == len(sut.failures) + mock_atlan_client.assert_not_called() + + @pytest.mark.parametrize( + "custom_metadata_handling", + [ + (CustomMetadataHandling.IGNORE), + (CustomMetadataHandling.OVERWRITE), + (CustomMetadataHandling.MERGE), + ], + ) + def test_add_when_capture_failure_true( + self, custom_metadata_handling, mock_atlan_client + ): + """Test batch add with capture_failures=True handles successes and failures.""" + table_1 = Mock(Table(guid="t1")) + table_2 = Mock(Table(guid="t2")) + table_3 = Mock(Table(guid="t3")) + table_4 = Mock(Table(guid="t4")) + mock_response = Mock(spec=AssetMutationResponse) + mutated_entities = Mock() + created = [table_1] + updated = [table_2] + mutated_entities.CREATE = created + mutated_entities.UPDATE = updated + mock_response.guid_assignments = {} + mock_response.attach_mock(mutated_entities, "mutated_entities") + + if custom_metadata_handling == CustomMetadataHandling.IGNORE: + mock_atlan_client.asset.save.return_value = mock_response + elif custom_metadata_handling == CustomMetadataHandling.OVERWRITE: + mock_atlan_client.asset.save_replacing_cm.return_value = mock_response + else: + mock_atlan_client.asset.save_merging_cm.return_value = mock_response + + sut = Batch( + client=mock_atlan_client, + max_size=2, + capture_failures=True, + custom_metadata_handling=custom_metadata_handling, + ) + sut.add(table_1) + self.assert_asset_client_not_called(mock_atlan_client, sut) + + sut.add(table_2) + + assert len(created) == sut.num_created + assert len(updated) == sut.num_updated + for unsaved, saved in zip(created, sut.created): + unsaved.trim_to_required.assert_called_once() + assert unsaved.name == saved.name + for unsaved, saved in zip(updated, sut.updated): + unsaved.trim_to_required.assert_called_once() + assert unsaved.name == saved.name + + exception = ErrorCode.INVALID_REQUEST_PASSTHROUGH.exception_with_parameters( + "bad", "stuff", "" + ) + if custom_metadata_handling == CustomMetadataHandling.IGNORE: + mock_atlan_client.asset.save.side_effect = exception + elif custom_metadata_handling == CustomMetadataHandling.OVERWRITE: + mock_atlan_client.asset.save_replacing_cm.side_effect = exception + else: + mock_atlan_client.asset.save_merging_cm.side_effect = exception + + sut.add(table_3) + + sut.add(table_4) + + assert 1 == len(sut.failures) + failure = sut.failures[0] + assert [table_3, table_4] == failure.failed_assets + assert exception == failure.failure_reason + if custom_metadata_handling == CustomMetadataHandling.IGNORE: + mock_atlan_client.asset.save.assert_has_calls( + [ + call([table_1, table_2], replace_atlan_tags=False), + call([table_3, table_4], replace_atlan_tags=False), + ] + ) + elif custom_metadata_handling == CustomMetadataHandling.OVERWRITE: + mock_atlan_client.asset.save_replacing_cm.assert_has_calls( + [ + call([table_1, table_2], replace_atlan_tags=False), + call([table_3, table_4], replace_atlan_tags=False), + ] + ) + else: + mock_atlan_client.asset.save_merging_cm.assert_has_calls( + [ + call([table_1, table_2], replace_atlan_tags=False), + call([table_3, table_4], replace_atlan_tags=False), + ] + ) + + @pytest.mark.parametrize( + "custom_metadata_handling", + [ + (CustomMetadataHandling.IGNORE), + (CustomMetadataHandling.OVERWRITE), + (CustomMetadataHandling.MERGE), + ], + ) + def test_add_when_capture_failure_false_then_exception_raised( + self, custom_metadata_handling, mock_atlan_client + ): + """Test batch add with capture_failures=False raises exception immediately.""" + exception = ErrorCode.INVALID_REQUEST_PASSTHROUGH.exception_with_parameters( + "bad", "stuff", "" + ) + if custom_metadata_handling == CustomMetadataHandling.IGNORE: + mock_atlan_client.asset.save.side_effect = exception + elif custom_metadata_handling == CustomMetadataHandling.OVERWRITE: + mock_atlan_client.asset.save_replacing_cm.side_effect = exception + else: + mock_atlan_client.asset.save_merging_cm.side_effect = exception + + sut = Batch( + client=mock_atlan_client, + max_size=1, + capture_failures=False, + custom_metadata_handling=custom_metadata_handling, + ) + with pytest.raises(AtlanError): + sut.add(Mock(Table)) + + assert 0 == len(sut.failures) + assert 0 == len(sut.created) + assert 0 == len(sut.updated) + + @patch( + "pyatlan_v9.model.assets.atlas_glossary_term.AtlasGlossaryTerm.trim_to_required", + ) + @patch( + "pyatlan_v9.model.assets.atlas_glossary_term.AtlasGlossaryTerm.ref_by_guid", + ) + def test_term_add(self, mock_ref_by_guid, mock_trim_to_required, mock_atlan_client): + """Test batch term add with tracking enabled. + + Uses v9 AtlasGlossaryTerm models as input. The Batch class + internally uses type(candidate).ref_by_guid for tracking. + """ + mutated_entities = Mock() + mock_response = Mock(spec=AssetMutationResponse) + term_1 = AtlasGlossaryTerm(guid="test-guid1", type_name="AtlasGlossaryTerm") + term_2 = AtlasGlossaryTerm(guid="test-guid2", type_name="AtlasGlossaryTerm") + created = [term_1, term_2] + mutated_entities.UPDATE = [] + mutated_entities.CREATE = created + mock_response.guid_assignments = {} + mock_response.attach_mock(mutated_entities, "mutated_entities") + mock_atlan_client.asset.search.return_value = [term_1] + mock_atlan_client.asset.save.return_value = mock_response + batch = Batch( + client=mock_atlan_client, + max_size=2, + track=True, + ) + batch.add(term_1) + # Because the batch is not yet full + self.assert_asset_client_not_called(mock_atlan_client, batch) + batch.add(term_2) + + assert len(created) == batch.num_created + mock_ref_by_guid.assert_has_calls([call(term_1.guid), call(term_2.guid)]) + mock_trim_to_required.assert_not_called() + + +# --------------------------------------------------------------------------- +# BulkRequest tests +# --------------------------------------------------------------------------- + + +class TestBulkRequest: + """Tests for the v9 BulkRequest class. + + Tests that relationship attributes are correctly categorized into + replace/append/remove buckets via v9's ``categorize_relationships()`` + pipeline (invoked by ``BulkRequest.to_dict()``). + """ + + SEE_ALSO = "seeAlso" + REMOVE = "removeRelationshipAttributes" + APPEND = "appendRelationshipAttributes" + PREFERRED_TO_TERMS = "preferredToTerms" + + @pytest.fixture(scope="class") + def glossary(self): + """Create a v9 test glossary.""" + return AtlasGlossary.creator(name=GLOSSARY_NAME) + + @pytest.fixture(scope="class") + def term1(self): + """Create v9 test term 1.""" + return AtlasGlossaryTerm.creator( + name=GLOSSARY_TERM_NAME, + anchor=AtlasGlossary.creator(name=GLOSSARY_NAME), + ) + + @pytest.fixture(scope="class") + def term2(self): + """Create v9 test term 2.""" + return AtlasGlossaryTerm(guid="term-2-guid", type_name="AtlasGlossaryTerm") + + @pytest.fixture(scope="class") + def term3(self): + """Create v9 test term 3.""" + return AtlasGlossaryTerm(guid="term-3-guid", type_name="AtlasGlossaryTerm") + + def to_json(self, request): + """Convert v9 BulkRequest to dict for assertion via to_dict().""" + return request.to_dict()["entities"][0] + + def test_process_relationship_attributes(self, glossary, term1, term2, term3): + """Test v9 BulkRequest correctly categorizes relationship attributes. + + Uses v9 ``RelatedAtlasGlossaryTerm`` with ``SaveSemantic`` to test + the ``categorize_relationships()`` pipeline invoked via ``to_dict()``. + """ + # Test replace (list) + term1.see_also = [ + RelatedAtlasGlossaryTerm(guid=term2.guid), + RelatedAtlasGlossaryTerm(guid=term3.guid), + ] + request = BulkRequest(entities=[term1]) + request_json = self.to_json(request) + assert request_json + rel_attrs = request_json.get("relationshipAttributes", {}) + assert self.SEE_ALSO in rel_attrs + replace_attributes = rel_attrs[self.SEE_ALSO] + assert len(replace_attributes) == 2 + assert replace_attributes[0]["guid"] == term2.guid + assert replace_attributes[1]["guid"] == term3.guid + assert self.APPEND not in request_json + assert self.REMOVE not in request_json + + # Test replace and append (list) + term1.see_also = [ + RelatedAtlasGlossaryTerm(guid=term2.guid), + RelatedAtlasGlossaryTerm(guid=term3.guid, semantic=V9SaveSemantic.APPEND), + ] + request = BulkRequest(entities=[term1]) + request_json = self.to_json(request) + assert request_json + rel_attrs = request_json.get("relationshipAttributes", {}) + assert self.SEE_ALSO in rel_attrs + replace_attributes = rel_attrs[self.SEE_ALSO] + assert len(replace_attributes) == 1 + assert replace_attributes[0]["guid"] == term2.guid + assert self.APPEND in request_json + assert self.SEE_ALSO in request_json[self.APPEND] + append_attributes = request_json[self.APPEND][self.SEE_ALSO] + assert len(append_attributes) == 1 + assert append_attributes[0]["guid"] == term3.guid + assert self.REMOVE not in request_json + + # Test replace and append (list) with multiple relationships + term1.see_also = [ + RelatedAtlasGlossaryTerm(guid=term2.guid), + RelatedAtlasGlossaryTerm(guid=term3.guid, semantic=V9SaveSemantic.APPEND), + ] + term1.preferred_to_terms = [ + RelatedAtlasGlossaryTerm(guid=term3.guid, semantic=V9SaveSemantic.APPEND), + ] + request = BulkRequest(entities=[term1]) + request_json = self.to_json(request) + assert request_json + rel_attrs = request_json.get("relationshipAttributes", {}) + assert self.SEE_ALSO in rel_attrs + replace_attributes = rel_attrs[self.SEE_ALSO] + assert len(replace_attributes) == 1 + assert replace_attributes[0]["guid"] == term2.guid + assert self.APPEND in request_json + assert self.SEE_ALSO in request_json[self.APPEND] + append_attributes = request_json[self.APPEND][self.SEE_ALSO] + assert len(append_attributes) == 1 + assert append_attributes[0]["guid"] == term3.guid + append_attributes = request_json[self.APPEND][self.PREFERRED_TO_TERMS] + assert len(append_attributes) == 1 + assert append_attributes[0]["guid"] == term3.guid + assert self.REMOVE not in request_json + + # Test append and replace (list) + term1.see_also = [ + RelatedAtlasGlossaryTerm(guid=term2.guid, semantic=V9SaveSemantic.APPEND), + RelatedAtlasGlossaryTerm(guid=term3.guid), + ] + request = BulkRequest(entities=[term1]) + request_json = self.to_json(request) + assert request_json + rel_attrs = request_json.get("relationshipAttributes", {}) + assert self.SEE_ALSO in rel_attrs + replace_attributes = rel_attrs[self.SEE_ALSO] + assert len(replace_attributes) == 1 + assert replace_attributes[0]["guid"] == term3.guid + assert self.APPEND in request_json + assert self.SEE_ALSO in request_json[self.APPEND] + append_attributes = request_json[self.APPEND][self.SEE_ALSO] + assert len(append_attributes) == 1 + assert append_attributes[0]["guid"] == term2.guid + assert self.REMOVE not in request_json + + # Test remove and append (list) + term1.see_also = [ + RelatedAtlasGlossaryTerm(guid=term2.guid, semantic=V9SaveSemantic.REMOVE), + RelatedAtlasGlossaryTerm(guid=term3.guid, semantic=V9SaveSemantic.APPEND), + ] + request = BulkRequest(entities=[term1]) + request_json = self.to_json(request) + assert request_json + assert self.APPEND in request_json + assert self.SEE_ALSO in request_json[self.APPEND] + append_attributes = request_json[self.APPEND][self.SEE_ALSO] + assert len(append_attributes) == 1 + assert append_attributes[0]["guid"] == term3.guid + assert self.REMOVE in request_json + assert self.SEE_ALSO in request_json[self.REMOVE] + remove_attributes = request_json[self.REMOVE][self.SEE_ALSO] + assert len(remove_attributes) == 1 + assert remove_attributes[0]["guid"] == term2.guid + # No replace bucket when all are append/remove + rel_attrs = request_json.get("relationshipAttributes", {}) + assert self.SEE_ALSO not in rel_attrs + + # Test same semantic (list) + term1.see_also = [ + RelatedAtlasGlossaryTerm(guid=term2.guid, semantic=V9SaveSemantic.APPEND), + RelatedAtlasGlossaryTerm(guid=term3.guid, semantic=V9SaveSemantic.APPEND), + ] + request = BulkRequest(entities=[term1]) + request_json = self.to_json(request) + assert request_json + assert self.APPEND in request_json + assert self.SEE_ALSO in request_json[self.APPEND] + append_attributes = request_json[self.APPEND][self.SEE_ALSO] + assert len(append_attributes) == 2 + assert append_attributes[0]["guid"] == term2.guid + assert append_attributes[1]["guid"] == term3.guid + assert self.REMOVE not in request_json + rel_attrs = request_json.get("relationshipAttributes", {}) + assert self.SEE_ALSO not in rel_attrs + + # Test empty (list) + term1.see_also = [] + term1.preferred_to_terms = [] + request = BulkRequest(entities=[term1]) + request_json = self.to_json(request) + assert request_json + rel_attrs = request_json.get("relationshipAttributes", {}) + assert self.SEE_ALSO in rel_attrs + replace_attributes = rel_attrs[self.SEE_ALSO] + assert len(replace_attributes) == 0 + assert self.APPEND not in request_json + assert self.REMOVE not in request_json + + # Test anchor goes into attributes (not relationshipAttributes) + term1.anchor = RelatedAtlasGlossary(guid=glossary.guid) + request = BulkRequest(entities=[term1]) + request_json = self.to_json(request) + assert request_json + attrs = request_json.get("attributes", {}) + assert "anchor" in attrs + assert attrs["anchor"]["guid"] == glossary.guid + + def test_asset_attribute_none_assignment(self): + """Test that None assignment to asset attributes is serialized correctly. + + Uses v9 ``Table.updater()`` and v9 ``BulkRequest.to_dict()``. + """ + table1 = Table.updater(name="test-table-1", qualified_name="test-qn-1") + table1.certificate_status = None + table1.certificate_status_message = None + request = BulkRequest(entities=[table1]) + request_json = self.to_json(request) + assert request_json + assert request_json["attributes"]["certificateStatus"] is None + assert request_json["attributes"]["certificateStatusMessage"] is None + + +# --------------------------------------------------------------------------- +# Client configuration tests +# --------------------------------------------------------------------------- + + +def test_atlan_client_headers(client: AtlanClient): + """Test that all required HTTP headers are set on the client session.""" + VERSION = read_text("pyatlan", "version.txt").strip() + headers = client._session.headers + + # Check custom Atlan headers + assert headers["x-atlan-agent"] == "sdk" + assert headers["x-atlan-agent-id"] == "python" + assert headers["x-atlan-client-origin"] == "product_sdk" + assert headers["x-atlan-python-version"] == get_python_version() + assert headers["x-atlan-client-type"] == "sync" + assert headers["user-agent"] == f"Atlan-PythonSDK/{VERSION}" + + # Check standard headers exist + assert "accept" in headers + assert "accept-encoding" in headers + assert "connection" in headers + + +@pytest.mark.parametrize( + "proxy_config", + [ + # No proxy configuration (default) + {}, + # Simple proxy + {"proxy": "http://127.0.0.1:8080"}, + # Proxy with SSL verification disabled + {"proxy": "http://127.0.0.1:8080", "verify": False}, + ], +) +def test_atlan_client_proxy_configurations(monkeypatch, proxy_config): + """Test various proxy and SSL configurations are properly initialized.""" + monkeypatch.setenv("ATLAN_BASE_URL", "https://test.atlan.com") + monkeypatch.setenv("ATLAN_API_KEY", "test-api-key") + + # Clear any system proxy/SSL env vars that might interfere with tests + for var in [ + "HTTP_PROXY", + "http_proxy", + "HTTPS_PROXY", + "https_proxy", + "SSL_CERT_FILE", + "REQUESTS_CA_BUNDLE", + ]: + monkeypatch.delenv(var, raising=False) + + # Create client with proxy settings + client = AtlanClient(**proxy_config) + + # Verify the session was created + assert client._session is not None + assert isinstance(client._session, httpx.Client) + + # Verify proxy is set correctly if provided + if "proxy" in proxy_config: + assert client.proxy == proxy_config["proxy"] + else: + assert client.proxy is None + + # Verify verify is set correctly if provided + if "verify" in proxy_config: + assert client.verify == proxy_config["verify"] + else: + assert client.verify is True # Default value + + +def test_atlan_client_proxy_passed_to_transport(monkeypatch): + """Test that proxy and verify settings are correctly configured on the client.""" + monkeypatch.setenv("ATLAN_BASE_URL", "https://test.atlan.com") + monkeypatch.setenv("ATLAN_API_KEY", "test-api-key") + + # Clear any proxy env vars that might interfere + for var in [ + "HTTP_PROXY", + "http_proxy", + "HTTPS_PROXY", + "https_proxy", + "SSL_CERT_FILE", + "REQUESTS_CA_BUNDLE", + ]: + monkeypatch.delenv(var, raising=False) + + # Test with proxy and verify=False (to disable SSL verification for testing) + proxy_url = "http://127.0.0.1:8080" + + client = AtlanClient(proxy=proxy_url, verify=False) + + # Verify the client has the correct settings + assert client.proxy == proxy_url + assert client.verify is False + + # Verify the transport was created + assert client._session is not None + assert hasattr(client._session, "_transport") + + +@pytest.mark.parametrize( + "env_vars, expected_proxy, expected_verify", + [ + # HTTP_PROXY environment variable + ( + {"HTTP_PROXY": "http://proxy.example.com:8080"}, + "http://proxy.example.com:8080", + False, + ), + # http_proxy (lowercase) environment variable + ( + {"http_proxy": "http://proxy.example.com:8080"}, + "http://proxy.example.com:8080", + False, + ), + # Separate HTTP and HTTPS proxy env vars (HTTPS takes precedence) + ( + { + "HTTP_PROXY": "http://proxy.example.com:8080", + "HTTPS_PROXY": "https://proxy.example.com:8443", + }, + "https://proxy.example.com:8443", # HTTPS_PROXY takes precedence + False, + ), + ], +) +@patch("httpx.Client") +def test_atlan_client_proxy_from_environment_variables( + mock_httpx_client, + monkeypatch, + env_vars, + expected_proxy, + expected_verify, +): + """Test that proxy configuration is picked up from environment variables.""" + monkeypatch.setenv("ATLAN_BASE_URL", "https://test.atlan.com") + monkeypatch.setenv("ATLAN_API_KEY", "test-api-key") + + # Clear any system proxy/SSL env vars that might interfere with tests + for var in [ + "HTTP_PROXY", + "http_proxy", + "HTTPS_PROXY", + "https_proxy", + "SSL_CERT_FILE", + "REQUESTS_CA_BUNDLE", + ]: + monkeypatch.delenv(var, raising=False) + + # Set environment variables + for key, value in env_vars.items(): + monkeypatch.setenv(key, value) + + # Create client without explicit proxy settings + client = AtlanClient() + + # Verify proxy configuration + if expected_proxy: + assert client.proxy == expected_proxy + + if expected_verify: + assert client.verify == expected_verify + + +@patch("httpx_retries.transport.httpx.AsyncHTTPTransport") +@patch("httpx_retries.transport.httpx.HTTPTransport") +@patch("httpx.Client") +def test_atlan_client_proxy_with_ssl_cert_file_from_env( + mock_httpx_client, mock_http_transport, mock_async_http_transport, monkeypatch +): + """Test that SSL_CERT_FILE environment variable is picked up.""" + monkeypatch.setenv("ATLAN_BASE_URL", "https://test.atlan.com") + monkeypatch.setenv("ATLAN_API_KEY", "test-api-key") + + # Use the fake certificate file + fake_cert_path = str( + Path(__file__).parent.parent.parent + / "tests" + / "unit" + / "data" + / "fake_certificates" + / "fake-cert.pem" + ) + monkeypatch.setenv("SSL_CERT_FILE", fake_cert_path) + + client = AtlanClient() + + # Verify SSL cert path was picked up + assert client.verify == fake_cert_path + + +@patch("httpx_retries.transport.httpx.AsyncHTTPTransport") +@patch("httpx_retries.transport.httpx.HTTPTransport") +@patch("httpx.Client") +def test_atlan_client_explicit_args_override_env_vars( + mock_httpx_client, mock_http_transport, mock_async_http_transport, monkeypatch +): + """Test that explicitly provided arguments take precedence over environment variables.""" + monkeypatch.setenv("ATLAN_BASE_URL", "https://test.atlan.com") + monkeypatch.setenv("ATLAN_API_KEY", "test-api-key") + + # Use the fake certificate file + fake_cert_path = str( + Path(__file__).parent.parent.parent + / "tests" + / "unit" + / "data" + / "fake_certificates" + / "fake-cert.pem" + ) + + # Set environment variables + monkeypatch.setenv("HTTP_PROXY", "http://env-proxy:8080") + monkeypatch.setenv("SSL_CERT_FILE", fake_cert_path) + + # Explicitly provide different values + explicit_proxy = "http://explicit-proxy:9090" + explicit_verify = False + + client = AtlanClient(proxy=explicit_proxy, verify=explicit_verify) + + # Verify explicit values take precedence + assert client.proxy == explicit_proxy + assert client.verify == explicit_verify + + +def test_atlan_client_no_proxy_when_no_env_vars(monkeypatch): + """Test that no proxy is configured when no env vars are set.""" + monkeypatch.setenv("ATLAN_BASE_URL", "https://test.atlan.com") + monkeypatch.setenv("ATLAN_API_KEY", "test-api-key") + + # Ensure proxy env vars are not set + for env_var in [ + "HTTP_PROXY", + "http_proxy", + "HTTPS_PROXY", + "https_proxy", + "SSL_CERT_FILE", + "REQUESTS_CA_BUNDLE", + ]: + monkeypatch.delenv(env_var, raising=False) + + client = AtlanClient() + + assert client.proxy is None + assert client.verify is True # Default value + + +# --------------------------------------------------------------------------- +# Group get_all tests +# --------------------------------------------------------------------------- + + +def test_get_all_pagination(group_client, mock_api_caller): + """Test group get_all pagination returns correct results.""" + mock_page_1 = [ + {"id": "1", "alias": "Group3"}, + {"id": "2", "alias": "Group4"}, + ] + mock_api_caller._call_api.side_effect = [ + {"records": mock_page_1}, + ] + + groups = group_client.get_all(limit=2) + assert len(groups.current_page()) == 2 + assert groups.current_page()[0].id == "1" + assert groups.current_page()[1].id == "2" + assert mock_api_caller._call_api.call_count == 1 + mock_api_caller.reset_mock() + + +def test_get_all_empty_response_with_raw_records(group_client, mock_api_caller): + """Test group get_all with empty records.""" + mock_page_1 = [] + mock_api_caller._call_api.side_effect = [ + {"records": mock_page_1}, + ] + + groups = group_client.get_all() + assert len(groups.current_page()) == 0 + mock_api_caller.reset_mock() + + +def test_get_all_with_columns(group_client, mock_api_caller): + """Test group get_all with specific columns requested.""" + mock_page_1 = [ + {"id": "1", "alias": "Group1"}, + {"id": "2", "alias": "Group2"}, + ] + mock_api_caller._call_api.side_effect = [ + {"records": mock_page_1}, + ] + + columns = ["alias"] + groups = group_client.get_all(limit=10, columns=columns) + + assert len(groups.current_page()) == 2 + assert groups.current_page()[0].id == "1" + assert groups.current_page()[0].alias == "Group1" + mock_api_caller._call_api.assert_called_once() + query_params = mock_api_caller._call_api.call_args.kwargs["query_params"] + assert query_params["columns"] == columns + mock_api_caller.reset_mock() + + +def test_get_all_sorting(group_client, mock_api_caller): + """Test group get_all with sorting parameter.""" + mock_page_1 = [ + {"id": "1", "alias": "Group1"}, + {"id": "2", "alias": "Group2"}, + ] + mock_api_caller._call_api.side_effect = [ + {"records": mock_page_1}, + ] + + groups = group_client.get_all(limit=10, sort="alias") + + assert len(groups.current_page()) == 2 + assert groups.current_page()[0].id == "1" + assert groups.current_page()[0].alias == "Group1" + mock_api_caller._call_api.assert_called_once() + query_params = mock_api_caller._call_api.call_args.kwargs["query_params"] + assert query_params["sort"] == "alias" + mock_api_caller.reset_mock() + + +# --------------------------------------------------------------------------- +# FluentSearch-based asset retrieval tests +# --------------------------------------------------------------------------- + + +def test_get_by_guid_asset_not_found_fluent_search(mock_api_caller): + """Test that get_by_guid raises error when asset not found via FluentSearch.""" + guid = "123" + asset_type = Table + + with patch.object(V9AssetClient, "search") as mock_search: + mock_search.return_value.current_page.return_value = [] + mock_search.return_value.count = 0 + + client = V9AssetClient(client=mock_api_caller) + with pytest.raises( + ErrorCode.ASSET_NOT_FOUND_BY_GUID.exception_with_parameters(guid).__class__ + ): + client.get_by_guid( + guid=guid, + asset_type=asset_type, + attributes=["name"], + related_attributes=["owner"], + ) + + mock_search.assert_called_once() + + +def test_get_by_guid_type_mismatch_fluent_search(mock_api_caller): + """Test that get_by_guid raises error when returned asset type doesn't match.""" + guid = "123" + expected_asset_type = Table + returned_asset_type = View + + with patch.object(V9AssetClient, "search") as mock_search: + mock_search.return_value.current_page.return_value = [returned_asset_type()] + mock_search.return_value.count = 1 + + client = V9AssetClient(client=mock_api_caller) + + with pytest.raises( + ErrorCode.ASSET_NOT_TYPE_REQUESTED.exception_with_parameters( + guid, expected_asset_type.__name__ + ).__class__ + ): + client.get_by_guid( + guid=guid, + asset_type=expected_asset_type, + attributes=["name"], + related_attributes=["owner"], + ) + + mock_search.assert_called_once() + + +def test_get_by_qualified_name_type_mismatch(mock_api_caller): + """Test that get_by_qualified_name raises error when type doesn't match.""" + qualified_name = "example_qualified_name" + expected_asset_type = Table + returned_asset_type = View + + with patch.object(V9AssetClient, "search") as mock_search: + mock_search.return_value.current_page.return_value = [returned_asset_type()] + mock_search.return_value.count = 1 + + client = V9AssetClient(client=mock_api_caller) + + with pytest.raises( + ErrorCode.ASSET_NOT_FOUND_BY_NAME.exception_with_parameters( + expected_asset_type.__name__, qualified_name + ).__class__ + ): + client.get_by_qualified_name( + qualified_name=qualified_name, + asset_type=expected_asset_type, + attributes=["name"], + related_attributes=["owner"], + ) + mock_search.assert_called_once() + + +def test_get_by_qualified_name_asset_not_found(mock_api_caller): + """Test that get_by_qualified_name raises error when asset not found.""" + qualified_name = "example_qualified_name" + asset_type = Table + + with patch.object(V9AssetClient, "search") as mock_search: + mock_search.return_value.current_page.return_value = [] + mock_search.return_value.count = 0 + + client = V9AssetClient(client=mock_api_caller) + + with pytest.raises( + ErrorCode.ASSET_NOT_FOUND_BY_QN.exception_with_parameters( + qualified_name, asset_type.__name__ + ).__class__ + ): + client.get_by_qualified_name( + qualified_name=qualified_name, + asset_type=asset_type, + attributes=["name"], + related_attributes=["owner"], + ) + + mock_search.assert_called_once() + + +# --------------------------------------------------------------------------- +# DQ rule schedule tests +# --------------------------------------------------------------------------- + + +def test_add_dq_rule_schedule(mock_api_caller): + """Test adding a DQ rule schedule to an asset.""" + asset_client = V9AssetClient(mock_api_caller) + asset_type = Table + asset_name = "Test Table" + asset_qualified_name = "test/qualified/name" + schedule_cron_string = "0 0 * * *" + schedule_time_zone = "UTC" + + updated_table = Table() + updated_table.guid = "test-guid-123" + updated_table.asset_dq_schedule_time_zone = schedule_time_zone + updated_table.asset_dq_schedule_crontab = schedule_cron_string + updated_table.asset_dq_schedule_type = DataQualityScheduleType.CRON + + mock_response = Mock(spec=AssetMutationResponse) + + with patch.object( + asset_type, "updater", return_value=updated_table + ) as mock_updater: + with patch.object( + asset_client, "save", return_value=mock_response + ) as mock_save: + result = asset_client.add_dq_rule_schedule( + asset_type=asset_type, + asset_name=asset_name, + asset_qualified_name=asset_qualified_name, + schedule_crontab=schedule_cron_string, + schedule_time_zone=schedule_time_zone, + ) + + mock_updater.assert_called_once_with( + qualified_name=asset_qualified_name, + name=asset_name, + ) + assert updated_table.asset_dq_schedule_time_zone == schedule_time_zone + assert updated_table.asset_dq_schedule_crontab == schedule_cron_string + assert updated_table.asset_dq_schedule_type == DataQualityScheduleType.CRON + mock_save.assert_called_once_with(updated_table) + assert result == mock_response + + +def test_set_dq_row_scope_filter_column(mock_api_caller): + """Test setting DQ row scope filter column on an asset.""" + asset_client = V9AssetClient(mock_api_caller) + mock_response = Mock(spec=AssetMutationResponse) + + with patch.object(asset_client, "save", return_value=mock_response) as mock_save: + result = asset_client.set_dq_row_scope_filter_column( + asset_type=Table, + asset_name="TestTable", + asset_qualified_name=DQ_TABLE_QUALIFIED_NAME, + row_scope_filter_column_qualified_name=DQ_COLUMN_QUALIFIED_NAME, + ) + + mock_save.assert_called_once() + assert result == mock_response diff --git a/tests_v9/unit/test_connection_cache.py b/tests_v9/unit/test_connection_cache.py new file mode 100644 index 000000000..3fb4542fd --- /dev/null +++ b/tests_v9/unit/test_connection_cache.py @@ -0,0 +1,263 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +""" +Unit tests for connection cache — ported from tests/unit/test_connection_cache.py. + +Uses legacy ConnectionCache/ConnectionName (plain Python classes) and v9 +Connection (msgspec.Struct) as test data. The cache layer itself has no Pydantic +dependency — this test verifies cache lookup/caching behaviour. +""" + +from unittest.mock import Mock, patch + +import pytest + +from pyatlan.cache.connection_cache import ConnectionCache, ConnectionName +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.errors import ErrorCode, InvalidRequestError, NotFoundError +from pyatlan_v9.model.assets import Connection + + +@pytest.fixture(autouse=True) +def set_env(monkeypatch): + monkeypatch.setenv("ATLAN_BASE_URL", "https://test.atlan.com") + monkeypatch.setenv("ATLAN_API_KEY", "test-api-key") + + +@pytest.fixture() +def client(): + return AtlanClient() + + +@pytest.fixture() +def mock_connection_cache(client, monkeypatch): + mock_cache = ConnectionCache(client) + monkeypatch.setattr(AtlanClient, "connection_cache", mock_cache) + return mock_cache + + +def test_get_by_guid_with_not_found_error(client): + connection_cache = ConnectionCache(client) + with pytest.raises(InvalidRequestError, match=ErrorCode.MISSING_ID.error_message): + connection_cache.get_by_guid("") + + +@patch.object(ConnectionCache, "lookup_by_guid") +def test_get_by_guid_with_no_invalid_request_error( + mock_lookup_by_guid, mock_connection_cache +): + test_guid = "test-guid-123" + with pytest.raises( + NotFoundError, + match=ErrorCode.ASSET_NOT_FOUND_BY_GUID.error_message.format(test_guid), + ): + mock_connection_cache.get_by_guid(test_guid) + + +def test_get_by_qualified_name_with_not_found_error(mock_connection_cache): + with pytest.raises(InvalidRequestError, match=ErrorCode.MISSING_ID.error_message): + mock_connection_cache.get_by_qualified_name("") + + +@patch.object(ConnectionCache, "lookup_by_qualified_name") +def test_get_by_qualified_name_with_no_invalid_request_error( + mock_lookup_by_qualified_name, mock_connection_cache +): + test_qn = "default/snowflake/123456789" + test_connector = "snowflake" + with pytest.raises( + NotFoundError, + match=ErrorCode.ASSET_NOT_FOUND_BY_QN.error_message.format( + test_qn, test_connector + ), + ): + mock_connection_cache.get_by_qualified_name(test_qn) + + +def test_get_by_name_with_not_found_error(mock_connection_cache): + with pytest.raises(InvalidRequestError, match=ErrorCode.MISSING_NAME.error_message): + mock_connection_cache.get_by_name("") + + +@patch.object(ConnectionCache, "lookup_by_name") +def test_get_by_name_with_no_invalid_request_error( + mock_lookup_by_name, mock_connection_cache +): + test_name = ConnectionName("snowflake/test") + with pytest.raises( + NotFoundError, + match=ErrorCode.ASSET_NOT_FOUND_BY_NAME.error_message.format( + ConnectionName._TYPE_NAME, + test_name, + ), + ): + mock_connection_cache.get_by_name(test_name) + + +@patch.object(ConnectionCache, "lookup_by_guid") +def test_get_by_guid(mock_lookup_by_guid, mock_connection_cache): + test_guid = "test-guid-123" + test_qn = "test-qualified-name" + conn = Connection() + conn.guid = test_guid + conn.qualified_name = test_qn + test_asset = conn + + mock_guid_to_asset = Mock() + mock_name_to_guid = Mock() + mock_qualified_name_to_guid = Mock() + + # 1 - Not found in the cache, triggers a lookup call + # 2, 3, 4 - Uses the cached entry from the map + mock_guid_to_asset.get.side_effect = [ + None, + test_asset, + test_asset, + test_asset, + ] + mock_name_to_guid.get.side_effect = [test_guid, test_guid, test_guid, test_guid] + mock_qualified_name_to_guid.get.side_effect = [ + test_guid, + test_guid, + test_guid, + test_guid, + ] + + # Assign mock caches to the return value of get_cache + mock_connection_cache.guid_to_asset = mock_guid_to_asset + mock_connection_cache.name_to_guid = mock_name_to_guid + mock_connection_cache.qualified_name_to_guid = mock_qualified_name_to_guid + + connection = mock_connection_cache.get_by_guid(test_guid) + + # Multiple calls with the same GUID result in no additional API lookups + # as the object is already cached + connection = mock_connection_cache.get_by_guid(test_guid) + connection = mock_connection_cache.get_by_guid(test_guid) + + assert test_guid == connection.guid + assert test_qn == connection.qualified_name + + # The method is called four times, but the lookup is triggered only once + assert mock_guid_to_asset.get.call_count == 4 + mock_lookup_by_guid.assert_called_once() + + +@patch.object(ConnectionCache, "lookup_by_guid") +@patch.object(ConnectionCache, "lookup_by_qualified_name") +def test_get_by_qualified_name( + mock_lookup_by_qn, mock_lookup_by_guid, mock_connection_cache +): + test_guid = "test-guid-123" + test_qn = "test-qualified-name" + conn = Connection() + conn.guid = test_guid + conn.qualified_name = test_qn + test_asset = conn + + mock_guid_to_asset = Mock() + mock_name_to_guid = Mock() + mock_qualified_name_to_guid = Mock() + + # 1 - Not found in the cache, triggers a lookup call + # 2, 3, 4 - Uses the cached entry from the map + mock_qualified_name_to_guid.get.side_effect = [ + None, + test_guid, + test_guid, + test_guid, + ] + + # Other caches will be populated once + # the lookup call for get_by_qualified_name is made + mock_guid_to_asset.get.side_effect = [ + test_asset, + test_asset, + test_asset, + test_asset, + ] + mock_name_to_guid.get.side_effect = [test_guid, test_guid, test_guid, test_guid] + + mock_connection_cache.guid_to_asset = mock_guid_to_asset + mock_connection_cache.name_to_guid = mock_name_to_guid + mock_connection_cache.qualified_name_to_guid = mock_qualified_name_to_guid + + connection = mock_connection_cache.get_by_qualified_name(test_qn) + + # Multiple calls with the same + # qualified name result in no additional API lookups + # as the object is already cached + connection = mock_connection_cache.get_by_qualified_name(test_qn) + connection = mock_connection_cache.get_by_qualified_name(test_qn) + + assert test_guid == connection.guid + assert test_qn == connection.qualified_name + + # The method is found four times + # but the lookup is triggered only once + assert mock_qualified_name_to_guid.get.call_count == 4 + mock_lookup_by_qn.assert_called_once() + + +@patch.object(ConnectionCache, "lookup_by_guid") +@patch.object(ConnectionCache, "lookup_by_name") +def test_get_by_name(mock_lookup_by_name, mock_lookup_by_guid, mock_connection_cache): + test_name = ConnectionName("snowflake/test") + test_guid = "test-guid-123" + test_qn = "test-qualified-name" + conn = Connection() + conn.guid = test_guid + conn.qualified_name = test_qn + test_asset = conn + + mock_guid_to_asset = Mock() + mock_name_to_guid = Mock() + mock_qualified_name_to_guid = Mock() + + # 1 - Not found in the cache, triggers a lookup call + # 2, 3, 4 - Uses the cached entry from the map + mock_name_to_guid.get.side_effect = [ + None, + test_guid, + test_guid, + test_guid, + ] + + # Other caches will be populated once + # the lookup call for get_by_qualified_name is made + mock_guid_to_asset.get.side_effect = [ + test_asset, + test_asset, + test_asset, + test_asset, + ] + mock_qualified_name_to_guid.get.side_effect = [ + test_guid, + test_guid, + test_guid, + test_guid, + ] + + mock_connection_cache.guid_to_asset = mock_guid_to_asset + mock_connection_cache.name_to_guid = mock_name_to_guid + mock_connection_cache.qualified_name_to_guid = mock_qualified_name_to_guid + + connection = mock_connection_cache.get_by_name(test_name) + + # Multiple calls with the same + # qualified name result in no additional API lookups + # as the object is already cached + connection = mock_connection_cache.get_by_name(test_name) + connection = mock_connection_cache.get_by_name(test_name) + + assert test_guid == connection.guid + assert test_qn == connection.qualified_name + + # The method is called four times + # but the lookup is triggered only once + assert mock_name_to_guid.get.call_count == 4 + mock_lookup_by_name.assert_called_once() + + # No call to guid lookup since the object is already in the cache + assert mock_lookup_by_guid.call_count == 0 diff --git a/tests_v9/unit/test_core.py b/tests_v9/unit/test_core.py new file mode 100644 index 000000000..2f8e55c83 --- /dev/null +++ b/tests_v9/unit/test_core.py @@ -0,0 +1,222 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +""" +Unit tests for pyatlan_v9 core types. + +Ported from tests/unit/test_core.py. Key differences from the original: + +- TestAtlanTag: Uses direct keyword construction with AtlanTagName instead of + dict-based Pydantic construction. No mock_tag_cache or client fixtures needed + since msgspec does not trigger cache lookups during validation. +- TestMsgspecExtraFields: Replaces TestAtlanObjectExtraFields. Tests msgspec + behavior of ignoring unknown fields during deserialization (vs Pydantic's + Extra.ignore + __atlan_extra__ pattern). +- TestPyatlanVersion: Unchanged — tests pyatlan.__version__ which is shared. +""" + +from __future__ import annotations + +from typing import Union + +import msgspec + +from pyatlan_v9.model.core import AtlanTag, AtlanTagName + + +class _Inner(msgspec.Struct, kw_only=True, rename="camel"): + old_attr: str + + +class _Outer(msgspec.Struct, kw_only=True, rename="camel"): + old: str + attributes: Union[_Inner, None] = None + + +class _AtlanFieldModel(msgspec.Struct, kw_only=True, rename="camel"): + name: str + atlan_field: str + + +class _AttrInner(msgspec.Struct, kw_only=True, rename="camel"): + old_attr: str + + +class _AttrOuter(msgspec.Struct, kw_only=True, rename="camel"): + old: str + attributes: _AttrInner + + +# --------------------------------------------------------------------------- +# TestAtlanTag +# --------------------------------------------------------------------------- + + +class TestAtlanTag: + """Tests for AtlanTag creation and AtlanTagName sentinel handling.""" + + def test_atlan_tag_when_tag_name_is_found(self): + sut = AtlanTag(type_name=AtlanTagName("123")) + assert str(sut.type_name) == "123" + + def test_atlan_tag_when_tag_name_is_empty_then_sentinel_is_returned(self): + # Empty string should map to deleted sentinel + sut = AtlanTag(type_name=AtlanTagName.get_deleted_sentinel()) + assert sut.type_name == AtlanTagName.get_deleted_sentinel() + + +# --------------------------------------------------------------------------- +# TestAtlanObjectExtraFields +# --------------------------------------------------------------------------- + + +class TestAtlanObjectExtraFields: + """ + Tests that msgspec structs properly handle unknown/extra fields during + deserialization. In msgspec, extra fields are simply ignored by default + (there is no __atlan_extra__ dict equivalent). + """ + + def test_msgspec_struct_ignores_unknown_fields(self): + class TestModel(msgspec.Struct, kw_only=True, rename="camel"): + name: str + + # Decode with extra fields - should work fine (ignored) + data = b'{"name": "test", "unknownField": 123}' + result = msgspec.json.decode(data, type=TestModel) + assert result.name == "test" + + def test_msgspec_struct_known_fields(self): + class TestModel(msgspec.Struct, kw_only=True, rename="camel"): + name: str + value: Union[int, None] = None + + data = b'{"name": "test", "value": 42}' + result = msgspec.json.decode(data, type=TestModel) + assert result.name == "test" + assert result.value == 42 + + def test_msgspec_struct_known_fields_with_none_default(self): + class TestModel(msgspec.Struct, kw_only=True, rename="camel"): + name: str + value: Union[int, None] = None + + data = b'{"name": "test"}' + result = msgspec.json.decode(data, type=TestModel) + assert result.name == "test" + assert result.value is None + + def test_msgspec_struct_ignores_extra_nested_fields(self): + data = b'{"old": "oldValue", "new": "newValue", "attributes": {"oldAttr": "oldValueAttr", "newAttr": "newValueAttr"}}' + result = msgspec.json.decode(data, type=_Outer) + + # Known fields are properly deserialized + assert result.old == "oldValue" + assert result.attributes is not None + assert result.attributes.old_attr == "oldValueAttr" + + def test_msgspec_struct_camel_case_rename(self): + class TestModel(msgspec.Struct, kw_only=True, rename="camel"): + my_field: str + another_value: Union[int, None] = None + + # JSON uses camelCase, Python uses snake_case + data = b'{"myField": "hello", "anotherValue": 99}' + result = msgspec.json.decode(data, type=TestModel) + assert result.my_field == "hello" + assert result.another_value == 99 + + def test_msgspec_struct_serialization_excludes_unknown_fields(self): + class TestModel(msgspec.Struct, kw_only=True, rename="camel"): + name: str + + # Decode with extra fields + data = b'{"name": "test", "unknownField": 123, "anotherUnknown": "abc"}' + result = msgspec.json.decode(data, type=TestModel) + + # Serialization only includes known fields + encoded = msgspec.json.decode(msgspec.json.encode(result)) + assert encoded == {"name": "test"} + + def test_atlan_api_response(self): + """Parity check for legacy AtlanObject extra-field behavior under msgspec.""" + + class TestResponse(msgspec.Struct, kw_only=True, rename="camel"): + name: str + + test_data = {"name": "test"} + response = msgspec.convert(test_data, type=TestResponse) + assert msgspec.to_builtins(response) == test_data + + test_data_extra = {"name": "test", "new1": 123, "new2": 456} + response = msgspec.convert(test_data_extra, type=TestResponse) + assert msgspec.to_builtins(response) == test_data + + test_data_extra_nested = { + "name": "test", + "new1": {"new2": [1, 2, 3]}, + "new3": "abc", + } + response = msgspec.convert(test_data_extra_nested, type=TestResponse) + assert msgspec.to_builtins(response) == test_data + + test_data_contains_atlan_field = { + "name": "test", + "atlanField": "test_value", + "__atlan_extra__": "ignored", + } + response = msgspec.convert( + test_data_contains_atlan_field, type=_AtlanFieldModel + ) + assert response.name == test_data_contains_atlan_field["name"] + assert response.atlan_field == "test_value" + + test_attr_raw_data = { + "old": "oldValue", + "new": "newValue", + "attributes": {"oldAttr": "oldValueAttr", "newAttr": "newValueAttr"}, + } + response = msgspec.convert(test_attr_raw_data, type=_AttrOuter) + assert msgspec.to_builtins(response) == { + "old": "oldValue", + "attributes": {"oldAttr": "oldValueAttr"}, + } + assert response.old == "oldValue" + assert response.attributes.old_attr == "oldValueAttr" + + +# --------------------------------------------------------------------------- +# TestPyatlanVersion +# --------------------------------------------------------------------------- + + +class TestPyatlanVersion: + """Tests for pyatlan.__version__ attribute.""" + + def test_pyatlan_has_version_attribute(self): + """Test that pyatlan module has __version__ attribute""" + import pyatlan + + assert hasattr(pyatlan, "__version__") + assert pyatlan.__version__ is not None + assert isinstance(pyatlan.__version__, str) + assert pyatlan.__version__ != "unknown" + + def test_pyatlan_version_format(self): + """Test that the version follows semantic versioning format""" + import re + + import pyatlan + + # Check if version matches semantic versioning pattern (x.y.z) + version_pattern = r"^\d+\.\d+\.\d+.*$" + assert re.match(version_pattern, pyatlan.__version__) + + def test_pyatlan_version_accessibility(self): + """Test that version can be accessed after import""" + import pyatlan + + # Version should be accessible immediately after import + version = pyatlan.__version__ + assert version is not None + assert len(version) > 0 diff --git a/tests_v9/unit/test_credential_client.py b/tests_v9/unit/test_credential_client.py new file mode 100644 index 000000000..aa152c85c --- /dev/null +++ b/tests_v9/unit/test_credential_client.py @@ -0,0 +1,403 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +""" +Unit tests for credential client — ported from tests/unit/test_credential_client.py. + +Uses v9 Credential (msgspec.Struct) for inputs and V9CredentialClient which +returns v9 msgspec models natively. +""" + +from unittest.mock import Mock + +import msgspec +import pytest + +from pyatlan.client.common import ApiCaller +from pyatlan_v9.client.credential import V9CredentialClient as CredentialClient +from pyatlan_v9.errors import InvalidRequestError +from pyatlan_v9.model.credential import ( + Credential, + CredentialListResponse, + CredentialResponse, + CredentialTestResponse, +) + +TEST_MISSING_TOKEN_ID = ( + "ATLAN-PYTHON-400-032 No ID was provided when attempting to update the API token." +) +TEST_INVALID_CREDENTIALS = ( + "ATLAN-PYTHON-400-054 Credentials provided did not work: failed" +) +TEST_INVALID_GUID_GET_VALIDATION_ERR = ( + "1 validation error for Get\nguid\n str type expected" +) +TEST_INVALID_GUID_PURGE_BY_GUID_VALIDATION_ERR = ( + "1 validation error for PurgeByGuid\nguid\n str type expected" +) +TEST_INVALID_CRED_TEST_VALIDATION_ERR = ( + "1 validation error for Test\ncredential\n instance of Credential expected" +) +TEST_INVALID_CRED_TEST_UPDATE_VALIDATION_ERR = "1 validation error for TestAndUpdate\ncredential\n instance of Credential expected" +TEST_INVALID_CRED_CREATOR_VALIDATION_ERR = ( + "1 validation error for Creator\ncredential\n instance of Credential expected" +) +TEST_INVALID_API_CALLER_PARAMETER_TYPE = ( + "ATLAN-PYTHON-400-048 Invalid parameter type for client should be ApiCaller" +) + + +@pytest.fixture() +def mock_api_caller(): + return Mock(spec=ApiCaller) + + +@pytest.fixture() +def client(mock_api_caller) -> CredentialClient: + return CredentialClient(mock_api_caller) + + +@pytest.fixture() +def credential_response() -> CredentialResponse: + """Fixture returning a v9 CredentialResponse for mock API results.""" + return CredentialResponse( + id="test-id", + version="1.2.3", + is_active=True, + created_at=1704186290006, + updated_at=1704218661848, + created_by="test-acc", + tenant_id="default", + name="test-name", + description="test-desc", + connector_config_name="test-ccn", + connector="test-conn", + connector_type="test-ct", + auth_type="test-at", + host="test-host", + port=123, + metadata=None, + level=None, + connection=None, + username="test-username", + extras={"some": "value"}, + ) + + +def _assert_cred_response(cred, cred_response): + assert cred.id == cred_response.id + assert cred.name == cred_response.name + assert cred.port == cred_response.port + assert cred.auth_type == cred_response.auth_type + assert cred.connector_type == cred_response.connector_type + assert cred.connector_config_name == cred_response.connector_config_name + assert cred.username == cred_response.username + assert cred.extras == cred_response.extras + + +@pytest.mark.parametrize("test_api_caller", ["abc", None]) +def test_init_when_wrong_class_raises_exception(test_api_caller): + with pytest.raises( + InvalidRequestError, + match=TEST_INVALID_API_CALLER_PARAMETER_TYPE, + ): + CredentialClient(test_api_caller) + + +@pytest.mark.parametrize("test_guid", [[123], set(), dict()]) +def test_cred_get_wrong_params_raises_validation_error( + test_guid, client: CredentialClient +): + with pytest.raises(ValueError) as err: + client.get(guid=test_guid) + assert TEST_INVALID_GUID_GET_VALIDATION_ERR == str(err.value) + + +@pytest.mark.parametrize("test_credentials", ["invalid_cred", 123]) +def test_cred_test_wrong_params_raises_validation_error( + test_credentials, client: CredentialClient +): + with pytest.raises(ValueError) as err: + client.test(credential=test_credentials) + assert TEST_INVALID_CRED_TEST_VALIDATION_ERR == str(err.value) + + +@pytest.mark.parametrize("test_credentials", ["invalid_cred", 123]) +def test_cred_test_and_update_wrong_params_raises_validation_error( + test_credentials, client: CredentialClient +): + with pytest.raises(ValueError) as err: + client.test_and_update(credential=test_credentials) + assert TEST_INVALID_CRED_TEST_UPDATE_VALIDATION_ERR == str(err.value) + + +@pytest.mark.parametrize( + "test_credentials, test_response", + [ + [Credential(), "successful"], + [Credential(id="test-id"), "failed"], + ], +) +def test_cred_test_update_raises_invalid_request_error( + test_credentials, test_response, mock_api_caller, client: CredentialClient +): + mock_api_caller._call_api.return_value = {"message": test_response} + with pytest.raises(InvalidRequestError) as err: + client.test_and_update(credential=test_credentials) + if test_response == "successful": + assert TEST_MISSING_TOKEN_ID in str(err.value) + else: + assert TEST_INVALID_CREDENTIALS in str(err.value) + + +def test_cred_get_when_given_guid( + client: CredentialClient, + mock_api_caller, + credential_response: CredentialResponse, +): + mock_api_caller._call_api.return_value = msgspec.to_builtins(credential_response) + response = client.get(guid="test-id") + assert isinstance(response, CredentialResponse) + cred = response.to_credential() + assert isinstance(cred, Credential) + _assert_cred_response(cred, credential_response) + + +def test_cred_get_when_given_wrong_guid( + client: CredentialClient, + mock_api_caller, + credential_response: CredentialResponse, +): + mock_api_caller._call_api.return_value = None + assert client.get(guid="test-wrong-id") is None + + +def test_cred_test_when_given_cred( + client: CredentialClient, + mock_api_caller, + credential_response: CredentialResponse, +): + mock_api_caller._call_api.return_value = {"message": "successful"} + cred_test_response = client.test(credential=Credential()) + assert isinstance(cred_test_response, CredentialTestResponse) + assert cred_test_response.message == "successful" + assert cred_test_response.code is None + assert cred_test_response.error is None + assert cred_test_response.info is None + assert cred_test_response.request_id is None + + +def test_cred_test_update_when_given_cred( + client: CredentialClient, + mock_api_caller, + credential_response: CredentialResponse, +): + mock_api_caller._call_api.side_effect = [ + {"message": "successful"}, + msgspec.to_builtins(credential_response), + ] + cred_response = client.test_and_update( + credential=Credential(id=credential_response.id) + ) + assert isinstance(cred_response, CredentialResponse) + cred = cred_response.to_credential() + _assert_cred_response(cred, credential_response) + + +@pytest.mark.parametrize( + "test_filter, test_limit, test_offset, test_response", + [ + (None, None, None, {"records": [{"id": "cred1"}, {"id": "cred2"}]}), + ({"name": "test"}, 5, 0, {"records": [{"id": "cred3"}]}), + ({"invalid": "field"}, 10, 0, {"records": []}), + ], +) +def test_cred_get_all_success( + test_filter, test_limit, test_offset, test_response, mock_api_caller +): + mock_api_caller._call_api.return_value = test_response + client = CredentialClient(mock_api_caller) + + result = client.get_all(filter=test_filter, limit=test_limit, offset=test_offset) + + assert isinstance(result, CredentialListResponse) + assert len(result.records) == len(test_response["records"]) + for record, expected in zip(result.records, test_response["records"]): + assert record.id == expected["id"] + + +def test_cred_get_all_empty_response(mock_api_caller): + mock_api_caller._call_api.return_value = {"records": []} + client = CredentialClient(mock_api_caller) + + result = client.get_all() + + assert isinstance(result, CredentialListResponse) + assert len(result.records) == 0 + + +def test_cred_get_all_invalid_response(mock_api_caller): + mock_api_caller._call_api.return_value = {} + client = CredentialClient(mock_api_caller) + + with pytest.raises(Exception, match="No records found in response"): + client.get_all() + + +@pytest.mark.parametrize( + "test_filter, test_limit, test_offset", + [ + ("invalid_filter", None, None), + (None, "invalid_limit", None), + (None, None, "invalid_offset"), + ], +) +def test_cred_get_all_invalid_params_raises_validation_error( + test_filter, test_limit, test_offset, client: CredentialClient +): + with pytest.raises(ValueError): + client.get_all(filter=test_filter, limit=test_limit, offset=test_offset) + + +def test_cred_get_all_timeout(mock_api_caller): + mock_api_caller._call_api.side_effect = TimeoutError("Request timed out") + client = CredentialClient(mock_api_caller) + + with pytest.raises(TimeoutError, match="Request timed out"): + client.get_all() + + +def test_cred_get_all_partial_response(mock_api_caller): + mock_api_caller._call_api.return_value = { + "records": [ + { + "id": "cred1", + "name": "Test Credential", + "level": "user", + "connection": "default/bigquery/1697545730", + } + ] + } + client = CredentialClient(mock_api_caller) + + result = client.get_all() + + assert isinstance(result, CredentialListResponse) + assert result.records[0].host is None + assert result.records[0].id == "cred1" + assert result.records[0].name == "Test Credential" + assert result.records[0].level == "user" + assert result.records[0].connection == "default/bigquery/1697545730" + + +def test_cred_get_all_invalid_filter_type(mock_api_caller): + client = CredentialClient(mock_api_caller) + + with pytest.raises(ValueError, match="value is not a valid dict"): + client.get_all(filter="invalid_filter") + + +def test_cred_get_all_no_results(mock_api_caller): + mock_api_caller._call_api.return_value = {"records": None} + client = CredentialClient(mock_api_caller) + + result = client.get_all(filter={"name": "nonexistent"}) + + assert isinstance(result, CredentialListResponse) + assert result.records == [] + assert len(result.records) == 0 + + +@pytest.mark.parametrize("create_credentials", ["invalid_cred", 123]) +def test_cred_creator_wrong_params_raises_validation_error( + create_credentials, client: CredentialClient +): + with pytest.raises(ValueError) as err: + client.creator(credential=create_credentials) + assert TEST_INVALID_CRED_CREATOR_VALIDATION_ERR == str(err.value) + + +@pytest.mark.parametrize( + "credential_data", + [ + ( + Credential( + name="test-name", + description="test-desc", + connector_config_name="test-ccn", + connector="test-conn", + connector_type="test-ct", + auth_type="test-at", + host="test-host", + port=123, + username="test-username", + extras={"some": "value"}, + ) + ), + ], +) +def test_creator_success( + credential_data, + credential_response: CredentialResponse, + mock_api_caller, + client: CredentialClient, +): + mock_api_caller._call_api.return_value = msgspec.to_builtins(credential_response) + client = CredentialClient(mock_api_caller) + + response = client.creator(credential=credential_data) + + assert isinstance(response, CredentialResponse) + assert credential_data.name == response.name + assert credential_data.description == response.description + assert credential_data.port == response.port + assert credential_data.auth_type == response.auth_type + assert credential_data.connector_type == response.connector_type + assert credential_data.connector_config_name == response.connector_config_name + assert credential_data.username == response.username + assert credential_data.extras == response.extras + assert response.level is None + + +@pytest.mark.parametrize( + "credential_data", + [ + ( + Credential( + name="test-name", + description="test-desc", + connector_config_name="test-ccn", + connector="test-conn", + connector_type="test-ct", + auth_type="test-at", + host="test-host", + port=123, + username="test-user", + password="test-password", + extras={"some": "value"}, + ) + ), + ], +) +def test_cred_creator_with_test_false_with_username_password( + credential_data, client: CredentialClient +): + with pytest.raises(Exception, match="ATLAN-PYTHON-400-071"): + client.creator(credential=credential_data, test=False) + + +@pytest.mark.parametrize("test_guid", [[123], set(), dict()]) +def test_cred_purge_by_guid_wrong_params_raises_validation_error( + test_guid, client: CredentialClient +): + with pytest.raises(ValueError) as err: + client.purge_by_guid(guid=test_guid) + assert TEST_INVALID_GUID_PURGE_BY_GUID_VALIDATION_ERR == str(err.value) + + +def test_cred_purge_by_guid_when_given_guid( + client: CredentialClient, + mock_api_caller, +): + mock_api_caller._call_api.return_value = None + assert client.purge_by_guid(guid="test-id") is None diff --git a/tests_v9/unit/test_custom_metadata.py b/tests_v9/unit/test_custom_metadata.py new file mode 100644 index 000000000..ef2b2c7dc --- /dev/null +++ b/tests_v9/unit/test_custom_metadata.py @@ -0,0 +1,256 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +""" +Unit tests for v9 custom metadata — ported from tests/unit/test_custom_metadata.py. + +Uses v9 CustomMetadataDict, CustomMetadataProxy, CustomMetadataRequest. +Key difference: CustomMetadataRequest uses `to_dict()` instead of `__root__`. +""" + +import pytest + +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.errors import ErrorCode, NotFoundError +from pyatlan_v9.model.custom_metadata import ( + CustomMetadataDict, + CustomMetadataProxy, + CustomMetadataRequest, +) + +ATTR_LAST_NAME = "Last Name" + +ATTR_LAST_NAME_ID = "2" + +ATTR_FIRST_NAME = "First Name" + +ATTR_FIRST_NAME_ID = "1" + +CM_ID = "123" +CM_NAME = "Something" +CM_ATTRIBUTES = {ATTR_FIRST_NAME_ID: ATTR_FIRST_NAME, ATTR_LAST_NAME_ID: ATTR_LAST_NAME} +META_DATA = {CM_ID: CM_ATTRIBUTES} + + +@pytest.fixture(autouse=True) +def set_env(monkeypatch): + monkeypatch.setenv("ATLAN_BASE_URL", "https://test.atlan.com") + monkeypatch.setenv("ATLAN_API_KEY", "test-api-key") + + +@pytest.fixture() +def client(): + return AtlanClient() + + +def get_attr_id_for_name(*args, **kwargs): + return ATTR_FIRST_NAME_ID if args[1] == ATTR_FIRST_NAME else ATTR_LAST_NAME_ID + + +def get_attr_name_for_id(*args, **kwargs): + return ATTR_FIRST_NAME if args[1] == ATTR_FIRST_NAME_ID else ATTR_FIRST_NAME + + +class TestCustomMetadataDict: + @pytest.fixture() + def sut(self, mock_custom_metadata_cache, client: AtlanClient): + mock_custom_metadata_cache.get_id_for_name.return_value = CM_ID + mock_custom_metadata_cache.map_attr_id_to_name = META_DATA + mock_custom_metadata_cache.is_attr_archived.return_value = False + + return CustomMetadataDict(client=client, name=CM_NAME) + + def test_init_when_invalid_name_throws_not_found_error( + self, mock_custom_metadata_cache, client: AtlanClient + ): + mock_custom_metadata_cache.get_id_for_name.side_effect = ( + ErrorCode.ASSET_NOT_FOUND_BY_GUID.exception_with_parameters("123") + ) + with pytest.raises(NotFoundError): + CustomMetadataDict(client=client, name=CM_NAME) + mock_custom_metadata_cache.get_id_for_name.assert_called_with(CM_NAME) + + def test_modified_after_init_returns_false(self, sut): + assert sut.modified is False + + def test_init_when_called_with_valid_name_initializes_names(self, sut): + assert sut.attribute_names == set(CM_ATTRIBUTES.values()) + + def test_can_get_set_items(self, sut): + sut[ATTR_FIRST_NAME] = "123" + assert sut[ATTR_FIRST_NAME] == "123" + assert sut.modified is True + + def test_get_item_with_invalid_name_raises_key_error(self, sut): + with pytest.raises( + KeyError, match="'garb' is not a valid property name for Something" + ): + sut["garb"] + + def test_set_item_with_invalid_name_raises_key_error(self, sut): + with pytest.raises( + KeyError, match="'garb' is not a valid property name for Something" + ): + sut["garb"] = ATTR_FIRST_NAME_ID + + @pytest.mark.parametrize("name", [ATTR_FIRST_NAME, ATTR_FIRST_NAME]) + def test_clear_all_set_all_attributes_to_none(self, sut, name): + sut.clear_all() + assert sut[name] is None + assert sut.modified is True + + @pytest.mark.parametrize( + "property_to_set, other_property", + [(ATTR_FIRST_NAME, ATTR_LAST_NAME), (ATTR_LAST_NAME, ATTR_FIRST_NAME)], + ) + def test_clear_unset_sets_unset_to_none(self, sut, property_to_set, other_property): + sut[property_to_set] = "bob" + sut.clear_unset() + assert sut[property_to_set] == "bob" + assert sut[other_property] is None + + def test_get_item_using_name_that_has_not_been_set_returns_none(self, sut): + assert sut[ATTR_FIRST_NAME] is None + + def test_business_attributes_when_no_changes(self, sut): + assert sut.business_attributes == {} + + def test_business_attributes_with_data(self, sut, mock_custom_metadata_cache): + mock_custom_metadata_cache.get_attr_id_for_name.side_effect = ( + get_attr_id_for_name + ) + alice = "alice" + sut[ATTR_FIRST_NAME] = alice + assert sut.business_attributes == {ATTR_FIRST_NAME_ID: alice} + + @pytest.mark.parametrize("name", [ATTR_FIRST_NAME, ATTR_FIRST_NAME]) + def test_is_unset_initially_returns_false(self, sut, name): + assert sut.is_set(name) is False + + @pytest.mark.parametrize("name", [ATTR_FIRST_NAME, ATTR_FIRST_NAME]) + def test_unset_after_update_returns_true(self, sut, name): + sut[name] = "bob" + assert sut.is_set(name) is True + + def test_get_deleted_sentinel(self): + sentinel = CustomMetadataDict.get_deleted_sentinel() + + assert sentinel is not None + assert id(sentinel) == id(CustomMetadataDict.get_deleted_sentinel()) + assert 0 == len(sentinel) + assert sentinel.modified is False + assert sentinel._name == "(DELETED)" + with pytest.raises( + KeyError, match=r"'abc' is not a valid property name for \(DELETED\)" + ): + sentinel["abc"] = 1 + + +class TestCustomMetadataProxy: + @pytest.fixture() + def sut(self, mock_custom_metadata_cache, client: AtlanClient): + yield CustomMetadataProxy(client=client, business_attributes=None) + + def test_when_intialialized_with_no_business_attributes_then_modified_is_false( + self, sut + ): + assert sut.modified is False + assert sut.business_attributes is None + + def test_when_intialialized_with_no_business_attributes_then_business_attributes_returns_none( + self, sut + ): + assert sut.business_attributes is None + + def test_set_custom_metadata(self, sut, client: AtlanClient): + cm = CustomMetadataDict(client=client, name=CM_NAME) + sut.set_custom_metadata(cm) + assert sut.modified is True + assert sut.get_custom_metadata(name=CM_NAME) is cm + + def test_after_modifying_metadata_modified_is_true( + self, sut, mock_custom_metadata_cache + ): + mock_custom_metadata_cache.get_id_for_name.return_value = CM_ID + mock_custom_metadata_cache.map_attr_id_to_name = META_DATA + mock_custom_metadata_cache.is_attr_archived.return_value = False + + cm = sut.get_custom_metadata(name=CM_NAME) + cm[ATTR_FIRST_NAME] = "James" + + assert sut.modified is True + + def test_when_not_modified_returns_business_attributes( + self, mock_custom_metadata_cache, client: AtlanClient + ): + mock_custom_metadata_cache.get_name_for_id.return_value = CM_NAME + mock_custom_metadata_cache.get_attr_name_for_id.return_value = ATTR_FIRST_NAME + mock_custom_metadata_cache.get_id_for_name.return_value = CM_ID + mock_custom_metadata_cache.map_attr_id_to_name = META_DATA + mock_custom_metadata_cache.is_attr_archived.return_value = False + ba = {CM_ID: {ATTR_FIRST_NAME_ID: ATTR_FIRST_NAME}} + + sut = CustomMetadataProxy(client=client, business_attributes=ba) + + assert sut.business_attributes is ba + + def test_when_modified_returns_updated_business_attributes( + self, mock_custom_metadata_cache, client: AtlanClient + ): + mock_custom_metadata_cache.get_name_for_id.return_value = CM_NAME + mock_custom_metadata_cache.get_attr_name_for_id.side_effect = ( + get_attr_name_for_id + ) + mock_custom_metadata_cache.get_id_for_name.return_value = CM_ID + mock_custom_metadata_cache.map_attr_id_to_name = META_DATA + mock_custom_metadata_cache.is_attr_archived.return_value = False + mock_custom_metadata_cache.get_attr_id_for_name.side_effect = ( + get_attr_id_for_name + ) + ba = {CM_ID: {ATTR_FIRST_NAME_ID: "Dave"}} + + sut = CustomMetadataProxy(client=client, business_attributes=ba) + cm = sut.get_custom_metadata(name=CM_NAME) + joey = "Joey" + donna = "Donna" + cm[ATTR_FIRST_NAME] = donna + cm[ATTR_LAST_NAME] = joey + ba = sut.business_attributes # type: ignore[assignment] + + assert ba == {CM_ID: {ATTR_FIRST_NAME_ID: donna, ATTR_LAST_NAME_ID: joey}} + + def test_when_invalid_metadata_set_then_delete_sentinel_is_used( + self, mock_custom_metadata_cache, client: AtlanClient + ): + mock_custom_metadata_cache.get_name_for_id.side_effect = ( + ErrorCode.CM_NOT_FOUND_BY_ID.exception_with_parameters(CM_ID) + ) + ba = {CM_ID: {ATTR_FIRST_NAME_ID: "Dave"}} + + sut = CustomMetadataProxy(client=client, business_attributes=ba) + + assert len(sut.get_custom_metadata("(DELETED)")) == 0 + + def test_when_property_is_archived( + self, mock_custom_metadata_cache, client: AtlanClient + ): + mock_custom_metadata_cache.get_name_for_id.return_value = CM_NAME + mock_custom_metadata_cache.get_attr_name_for_id.return_value = ATTR_FIRST_NAME + mock_custom_metadata_cache.get_id_for_name.return_value = CM_ID + mock_custom_metadata_cache.map_attr_id_to_name = META_DATA + mock_custom_metadata_cache.is_attr_archived.return_value = True + ba = {CM_ID: {ATTR_FIRST_NAME_ID: ATTR_FIRST_NAME}} + sut = CustomMetadataProxy(client=client, business_attributes=ba) + assert sut.business_attributes is ba + assert sut.get_custom_metadata(CM_NAME) == {} + + +class TestCustomMetadataRequest: + def test_create(self, mock_custom_metadata_cache, client: AtlanClient): + mock_custom_metadata_cache.get_id_for_name.return_value = CM_ID + mock_custom_metadata_cache.map_attr_id_to_name = META_DATA + + cm = CustomMetadataDict(client=client, name=CM_NAME) + request = CustomMetadataRequest.create(custom_metadata_dict=cm) + # v9 uses to_dict() instead of Pydantic's __root__ + assert request.to_dict() == {} diff --git a/tests_v9/unit/test_custom_relationships.py b/tests_v9/unit/test_custom_relationships.py new file mode 100644 index 000000000..eac0020be --- /dev/null +++ b/tests_v9/unit/test_custom_relationships.py @@ -0,0 +1,1175 @@ +"""Tests for v9 custom relationship models. + +These tests verify: +- Relationship attribute builder methods produce correct RelatedEntity refs +- Serialization of models with relationship attributes (to_json) +- Round-trip serialization → deserialization preserves relationship data +- Combined multiple relationship types on a single asset +- IndistinctRelationship fallback for unknown relationship types +""" + +import json +from unittest.mock import patch + +import pytest +from msgspec import UNSET + +from pyatlan_v9.model.assets import ( + AtlasGlossaryCategory, + AtlasGlossaryTerm, + CustomEntity, + Table, +) +from pyatlan_v9.model.assets.gtc_related import RelatedAtlasGlossaryTerm +from pyatlan_v9.model.assets.related_entity import RelatedEntity +from pyatlan_v9.model.assets.relations import ( + AtlasGlossaryIsARelationship, + AtlasGlossaryPreferredTerm, + AtlasGlossaryRelatedTerm, + AtlasGlossaryReplacementTerm, + AtlasGlossarySemanticAssignment, + AtlasGlossarySynonym, + AtlasGlossaryTermCategorization, + AtlasGlossaryTranslation, + AtlasGlossaryValidValue, + CustomRelatedFromEntitiesCustomRelatedToEntities, + IndistinctRelationship, + UserDefRelationship, +) + + +@pytest.fixture() +def mock_asset_guid(): + with patch("pyatlan_v9.utils.random") as mock_random: + mock_random.random.return_value = 123456789 + yield mock_random + + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + + +def _assert_related_entity(related, expected_relationship_type): + """Helper to assert a RelatedEntity has the expected relationship structure.""" + assert isinstance(related, RelatedEntity) + assert related.relationship_type == expected_relationship_type + assert related.relationship_attributes is not UNSET + assert related.relationship_attributes is not None + assert related.relationship_attributes["typeName"] == expected_relationship_type + assert "attributes" in related.relationship_attributes + + +def _get_serialized_rel_attrs(term_json, rel_field_name, index=0): + """Extract serialized relationship attributes from nested JSON output. + + In v9 nested format, relationship fields are under 'relationshipAttributes'. + """ + rel_attrs = term_json.get("relationshipAttributes", {}) + items = rel_attrs.get(rel_field_name, []) + if isinstance(items, list): + return items[index] if index < len(items) else None + return items + + +# --------------------------------------------------------------------------- +# AtlasGlossaryTermCategorization +# --------------------------------------------------------------------------- + + +def test_atlas_glossary_term_categorization_builder(): + """Test AtlasGlossaryTermCategorization builder methods.""" + categorization = AtlasGlossaryTermCategorization( + description="Customer related terms", status="ACTIVE" + ) + term = AtlasGlossaryTerm.ref_by_guid("term-guid-1") + category = AtlasGlossaryCategory.ref_by_guid("category-guid-1") + + # Test terms() builder + terms_ref = categorization.terms(term) + _assert_related_entity(terms_ref, "AtlasGlossaryTermCategorization") + assert terms_ref.guid == "term-guid-1" + attrs = terms_ref.relationship_attributes["attributes"] + assert attrs["description"] == "Customer related terms" + assert attrs["status"] == "ACTIVE" + + # Test categories() builder + categories_ref = categorization.categories(category) + _assert_related_entity(categories_ref, "AtlasGlossaryTermCategorization") + assert categories_ref.guid == "category-guid-1" + + +def test_atlas_glossary_term_categorization_serialization(): + """Test serialization of AtlasGlossaryTermCategorization relationship.""" + category = AtlasGlossaryCategory.updater( + qualified_name="business-category@business-glossary", + name="business-category", + glossary_guid="business-glossary-guid", + ) + + term = AtlasGlossaryTerm.ref_by_guid("term-guid-1") + + categorization = AtlasGlossaryTermCategorization( + description="Customer related terms", status="ACTIVE" + ) + + category.terms = [categorization.terms(term)] + + result = json.loads(category.to_json()) + + # Verify the terms relationship is in relationshipAttributes + terms_item = _get_serialized_rel_attrs(result, "terms", 0) + assert terms_item is not None + assert terms_item["guid"] == "term-guid-1" + assert terms_item["relationshipType"] == "AtlasGlossaryTermCategorization" + assert ( + terms_item["relationshipAttributes"]["typeName"] + == "AtlasGlossaryTermCategorization" + ) + assert ( + terms_item["relationshipAttributes"]["attributes"]["description"] + == "Customer related terms" + ) + assert terms_item["relationshipAttributes"]["attributes"]["status"] == "ACTIVE" + + +def test_atlas_glossary_term_categorization_roundtrip(): + """Test round-trip serialization/deserialization of categorization relationship.""" + category = AtlasGlossaryCategory.updater( + qualified_name="business-category@business-glossary", + name="business-category", + glossary_guid="business-glossary-guid", + ) + + categorization = AtlasGlossaryTermCategorization( + description="Customer related terms", status="ACTIVE" + ) + term_ref = AtlasGlossaryTerm.ref_by_guid("term-guid-1") + category.terms = [categorization.terms(term_ref)] + + # Serialize → deserialize + json_str = category.to_json() + restored = AtlasGlossaryCategory.from_json(json_str) + + assert restored.name == "business-category" + assert restored.qualified_name == "business-category@business-glossary" + assert restored.terms is not UNSET and restored.terms is not None + assert len(restored.terms) == 1 + + restored_term = restored.terms[0] + assert restored_term.guid == "term-guid-1" + assert restored_term.relationship_type == "AtlasGlossaryTermCategorization" + assert ( + restored_term.relationship_attributes["typeName"] + == "AtlasGlossaryTermCategorization" + ) + assert ( + restored_term.relationship_attributes["attributes"]["description"] + == "Customer related terms" + ) + + +# --------------------------------------------------------------------------- +# AtlasGlossaryIsARelationship +# --------------------------------------------------------------------------- + + +def test_atlas_glossary_is_a_relationship_builder(): + """Test AtlasGlossaryIsARelationship builder methods.""" + is_a_rel = AtlasGlossaryIsARelationship( + description="Animal is a more general concept", + expression="taxonomic classification", + status="ACTIVE", + steward="taxonomy-expert", + source="domain-expert", + ) + + general_term = AtlasGlossaryTerm.ref_by_guid("general-term-guid") + + classifies_ref = is_a_rel.classifies(general_term) + _assert_related_entity(classifies_ref, "AtlasGlossaryIsARelationship") + assert classifies_ref.guid == "general-term-guid" + attrs = classifies_ref.relationship_attributes["attributes"] + assert attrs["description"] == "Animal is a more general concept" + assert attrs["expression"] == "taxonomic classification" + assert attrs["status"] == "ACTIVE" + assert attrs["steward"] == "taxonomy-expert" + assert attrs["source"] == "domain-expert" + + is_a_ref = is_a_rel.is_a(general_term) + _assert_related_entity(is_a_ref, "AtlasGlossaryIsARelationship") + + +def test_atlas_glossary_is_a_relationship_serialization(): + """Test serialization of AtlasGlossaryIsARelationship relationship.""" + term = AtlasGlossaryTerm.updater( + qualified_name="mammal@taxonomy-glossary", + name="mammal", + glossary_guid="taxonomy-glossary-guid", + ) + + general_term = AtlasGlossaryTerm.ref_by_guid("general-term-guid") + + is_a_relationship = AtlasGlossaryIsARelationship( + description="Animal is a more general concept", + expression="taxonomic classification", + status="ACTIVE", + steward="taxonomy-expert", + source="domain-expert", + ) + + term.classifies = [is_a_relationship.classifies(general_term)] + + result = json.loads(term.to_json()) + + classifies_item = _get_serialized_rel_attrs(result, "classifies", 0) + assert classifies_item is not None + assert classifies_item["guid"] == "general-term-guid" + assert classifies_item["relationshipType"] == "AtlasGlossaryIsARelationship" + rel_attrs = classifies_item["relationshipAttributes"] + assert rel_attrs["typeName"] == "AtlasGlossaryIsARelationship" + assert rel_attrs["attributes"]["description"] == "Animal is a more general concept" + assert rel_attrs["attributes"]["expression"] == "taxonomic classification" + assert rel_attrs["attributes"]["status"] == "ACTIVE" + assert rel_attrs["attributes"]["steward"] == "taxonomy-expert" + assert rel_attrs["attributes"]["source"] == "domain-expert" + + +def test_atlas_glossary_is_a_relationship_roundtrip(): + """Test round-trip for AtlasGlossaryIsARelationship.""" + term = AtlasGlossaryTerm.updater( + qualified_name="mammal@taxonomy-glossary", + name="mammal", + glossary_guid="taxonomy-glossary-guid", + ) + + is_a_rel = AtlasGlossaryIsARelationship( + description="Animal is a more general concept", + expression="taxonomic classification", + status="ACTIVE", + steward="taxonomy-expert", + source="domain-expert", + ) + + general_term = AtlasGlossaryTerm.ref_by_guid("general-term-guid") + term.classifies = [is_a_rel.classifies(general_term)] + + restored = AtlasGlossaryTerm.from_json(term.to_json()) + + assert restored.classifies is not UNSET and restored.classifies is not None + assert len(restored.classifies) == 1 + c = restored.classifies[0] + assert c.guid == "general-term-guid" + assert c.relationship_attributes["typeName"] == "AtlasGlossaryIsARelationship" + assert ( + c.relationship_attributes["attributes"]["description"] + == "Animal is a more general concept" + ) + + +# --------------------------------------------------------------------------- +# AtlasGlossaryValidValue +# --------------------------------------------------------------------------- + + +def test_atlas_glossary_valid_value_builder(): + """Test AtlasGlossaryValidValue builder methods.""" + vv_rel = AtlasGlossaryValidValue( + description="Red is a valid color value", + expression="enumeration value", + status="ACTIVE", + steward="data-modeler", + source="business-rules", + ) + + red_value = AtlasGlossaryTerm.ref_by_guid("red-value-guid") + + vv_ref = vv_rel.valid_values(red_value) + _assert_related_entity(vv_ref, "AtlasGlossaryValidValue") + assert vv_ref.guid == "red-value-guid" + attrs = vv_ref.relationship_attributes["attributes"] + assert attrs["description"] == "Red is a valid color value" + assert attrs["expression"] == "enumeration value" + assert attrs["status"] == "ACTIVE" + assert attrs["steward"] == "data-modeler" + assert attrs["source"] == "business-rules" + + vvf_ref = vv_rel.valid_values_for(red_value) + _assert_related_entity(vvf_ref, "AtlasGlossaryValidValue") + + +def test_atlas_glossary_valid_value_serialization(): + """Test serialization of AtlasGlossaryValidValue relationship.""" + term = AtlasGlossaryTerm.updater( + qualified_name="color@business-glossary", + name="color", + glossary_guid="business-glossary-guid", + ) + + red_value = AtlasGlossaryTerm.ref_by_guid("red-value-guid") + + valid_value_relationship = AtlasGlossaryValidValue( + description="Red color value", + expression="manual assignment", + status="ACTIVE", + steward="data-modeler", + source="business-analysis", + ) + + term.valid_values = [valid_value_relationship.valid_values(red_value)] + + result = json.loads(term.to_json()) + + vv_item = _get_serialized_rel_attrs(result, "validValues", 0) + assert vv_item is not None + assert vv_item["guid"] == "red-value-guid" + assert vv_item["relationshipType"] == "AtlasGlossaryValidValue" + assert vv_item["relationshipAttributes"]["typeName"] == "AtlasGlossaryValidValue" + assert ( + vv_item["relationshipAttributes"]["attributes"]["description"] + == "Red color value" + ) + + +def test_atlas_glossary_valid_value_roundtrip(): + """Test round-trip for AtlasGlossaryValidValue.""" + term = AtlasGlossaryTerm.updater( + qualified_name="color@business-glossary", + name="color", + glossary_guid="business-glossary-guid", + ) + + vv_rel = AtlasGlossaryValidValue( + description="Red color value", + expression="manual assignment", + status="ACTIVE", + steward="data-modeler", + source="business-analysis", + ) + term.valid_values = [vv_rel.valid_values(AtlasGlossaryTerm.ref_by_guid("red-guid"))] + + restored = AtlasGlossaryTerm.from_json(term.to_json()) + assert restored.valid_values is not UNSET + assert len(restored.valid_values) == 1 + vv = restored.valid_values[0] + assert vv.guid == "red-guid" + assert vv.relationship_attributes["typeName"] == "AtlasGlossaryValidValue" + + +# --------------------------------------------------------------------------- +# AtlasGlossaryPreferredTerm +# --------------------------------------------------------------------------- + + +def test_atlas_glossary_preferred_term_builder(): + """Test AtlasGlossaryPreferredTerm builder methods.""" + pref_rel = AtlasGlossaryPreferredTerm( + description="Customer is the preferred term over client", + expression="business standardization", + status="ACTIVE", + steward="business-analyst", + source="governance-committee", + ) + + preferred = AtlasGlossaryTerm.ref_by_guid("customer-preferred-guid") + + pt_ref = pref_rel.preferred_terms(preferred) + _assert_related_entity(pt_ref, "AtlasGlossaryPreferredTerm") + assert pt_ref.guid == "customer-preferred-guid" + attrs = pt_ref.relationship_attributes["attributes"] + assert attrs["description"] == "Customer is the preferred term over client" + assert attrs["expression"] == "business standardization" + assert attrs["status"] == "ACTIVE" + assert attrs["steward"] == "business-analyst" + assert attrs["source"] == "governance-committee" + + ptt_ref = pref_rel.preferred_to_terms(preferred) + _assert_related_entity(ptt_ref, "AtlasGlossaryPreferredTerm") + + +def test_atlas_glossary_preferred_term_serialization(): + """Test serialization of AtlasGlossaryPreferredTerm relationship.""" + term = AtlasGlossaryTerm.updater( + qualified_name="business-entity@business-glossary", + name="business-entity", + glossary_guid="business-glossary-guid", + ) + + preferred_term = AtlasGlossaryTerm.ref_by_guid("customer-preferred-guid") + + preferred_relationship = AtlasGlossaryPreferredTerm( + description="Customer is the preferred term", + expression="governance decision", + status="ACTIVE", + steward="business-analyst", + source="manual", + ) + + term.preferred_terms = [preferred_relationship.preferred_terms(preferred_term)] + + result = json.loads(term.to_json()) + + pt_item = _get_serialized_rel_attrs(result, "preferredTerms", 0) + assert pt_item is not None + assert pt_item["guid"] == "customer-preferred-guid" + assert pt_item["relationshipType"] == "AtlasGlossaryPreferredTerm" + assert pt_item["relationshipAttributes"]["typeName"] == "AtlasGlossaryPreferredTerm" + assert ( + pt_item["relationshipAttributes"]["attributes"]["description"] + == "Customer is the preferred term" + ) + + +# --------------------------------------------------------------------------- +# AtlasGlossaryReplacementTerm +# --------------------------------------------------------------------------- + + +def test_atlas_glossary_replacement_term_builder(): + """Test AtlasGlossaryReplacementTerm builder methods.""" + repl_rel = AtlasGlossaryReplacementTerm( + description="New term replaces legacy term", + expression="system upgrade", + status="ACTIVE", + steward="data-architect", + source="manual", + ) + + new_term = AtlasGlossaryTerm.ref_by_guid("new-term-guid") + + rt_ref = repl_rel.replacement_terms(new_term) + _assert_related_entity(rt_ref, "AtlasGlossaryReplacementTerm") + assert rt_ref.guid == "new-term-guid" + attrs = rt_ref.relationship_attributes["attributes"] + assert attrs["description"] == "New term replaces legacy term" + + rb_ref = repl_rel.replaced_by(new_term) + _assert_related_entity(rb_ref, "AtlasGlossaryReplacementTerm") + + +def test_atlas_glossary_replacement_term_serialization(): + """Test serialization of AtlasGlossaryReplacementTerm relationship.""" + term = AtlasGlossaryTerm.updater( + qualified_name="legacy-term@old-glossary", + name="legacy-term", + glossary_guid="old-glossary-guid", + ) + + replacement_term = AtlasGlossaryTerm.ref_by_guid("new-term-guid") + + replacement_relationship = AtlasGlossaryReplacementTerm( + description="New term replaces legacy term", + expression="system upgrade", + status="ACTIVE", + steward="data-architect", + source="manual", + ) + + term.replacement_terms = [ + replacement_relationship.replacement_terms(replacement_term) + ] + + result = json.loads(term.to_json()) + + rt_item = _get_serialized_rel_attrs(result, "replacementTerms", 0) + assert rt_item is not None + assert rt_item["guid"] == "new-term-guid" + assert rt_item["relationshipType"] == "AtlasGlossaryReplacementTerm" + assert ( + rt_item["relationshipAttributes"]["typeName"] == "AtlasGlossaryReplacementTerm" + ) + + +# --------------------------------------------------------------------------- +# AtlasGlossaryTranslation +# --------------------------------------------------------------------------- + + +def test_atlas_glossary_translation_builder(): + """Test AtlasGlossaryTranslation builder methods.""" + trans_rel = AtlasGlossaryTranslation( + description="Spanish translation", + expression="localization", + status="ACTIVE", + steward="translation-team", + source="manual", + ) + + spanish_term = AtlasGlossaryTerm.ref_by_guid("cliente-spanish-guid") + + tt_ref = trans_rel.translated_terms(spanish_term) + _assert_related_entity(tt_ref, "AtlasGlossaryTranslation") + assert tt_ref.guid == "cliente-spanish-guid" + attrs = tt_ref.relationship_attributes["attributes"] + assert attrs["description"] == "Spanish translation" + + tlt_ref = trans_rel.translation_terms(spanish_term) + _assert_related_entity(tlt_ref, "AtlasGlossaryTranslation") + + +def test_atlas_glossary_translation_serialization(): + """Test serialization of AtlasGlossaryTranslation relationship.""" + term = AtlasGlossaryTerm.updater( + qualified_name="customer@english-glossary", + name="customer", + glossary_guid="english-glossary-guid", + ) + + spanish_term = AtlasGlossaryTerm.ref_by_guid("cliente-spanish-guid") + + translation_relationship = AtlasGlossaryTranslation( + description="Spanish translation", + expression="localization", + status="ACTIVE", + steward="translation-team", + source="manual", + ) + + term.translated_terms = [translation_relationship.translated_terms(spanish_term)] + + result = json.loads(term.to_json()) + + tt_item = _get_serialized_rel_attrs(result, "translatedTerms", 0) + assert tt_item is not None + assert tt_item["guid"] == "cliente-spanish-guid" + assert tt_item["relationshipType"] == "AtlasGlossaryTranslation" + assert tt_item["relationshipAttributes"]["typeName"] == "AtlasGlossaryTranslation" + + +# --------------------------------------------------------------------------- +# AtlasGlossaryRelatedTerm +# --------------------------------------------------------------------------- + + +def test_atlas_glossary_related_term_builder(): + """Test AtlasGlossaryRelatedTerm builder methods.""" + related_rel = AtlasGlossaryRelatedTerm( + description="Related term for reference", + expression="see-also-expression", + status="ACTIVE", + steward="data-steward", + source="manual", + ) + + related = AtlasGlossaryTerm.ref_by_guid("related-term-guid") + + sa_ref = related_rel.see_also(related) + _assert_related_entity(sa_ref, "AtlasGlossaryRelatedTerm") + assert sa_ref.guid == "related-term-guid" + attrs = sa_ref.relationship_attributes["attributes"] + assert attrs["description"] == "Related term for reference" + assert attrs["expression"] == "see-also-expression" + assert attrs["status"] == "ACTIVE" + assert attrs["steward"] == "data-steward" + assert attrs["source"] == "manual" + + +def test_atlas_glossary_related_term_serialization(): + """Test serialization of AtlasGlossaryRelatedTerm relationship.""" + term = AtlasGlossaryTerm.updater( + qualified_name="main-term@default", + name="Main Term", + glossary_guid="business-glossary-guid", + ) + + related_term = AtlasGlossaryTerm.ref_by_guid("related-term-guid") + + related_term_rel = AtlasGlossaryRelatedTerm( + description="Related term for reference", + expression="see-also-expression", + status="ACTIVE", + steward="data-steward", + source="manual", + ) + + term.see_also = [related_term_rel.see_also(related_term)] + + result = json.loads(term.to_json()) + + sa_item = _get_serialized_rel_attrs(result, "seeAlso", 0) + assert sa_item is not None + assert sa_item["guid"] == "related-term-guid" + assert sa_item["relationshipType"] == "AtlasGlossaryRelatedTerm" + assert sa_item["relationshipAttributes"]["typeName"] == "AtlasGlossaryRelatedTerm" + assert ( + sa_item["relationshipAttributes"]["attributes"]["description"] + == "Related term for reference" + ) + + +def test_atlas_glossary_related_term_roundtrip(): + """Test round-trip for AtlasGlossaryRelatedTerm.""" + term = AtlasGlossaryTerm.updater( + qualified_name="main-term@default", + name="Main Term", + glossary_guid="business-glossary-guid", + ) + + related_term_rel = AtlasGlossaryRelatedTerm( + description="Related term for reference", + expression="see-also-expression", + status="ACTIVE", + steward="data-steward", + source="manual", + ) + term.see_also = [ + related_term_rel.see_also(AtlasGlossaryTerm.ref_by_guid("related-term-guid")) + ] + + restored = AtlasGlossaryTerm.from_json(term.to_json()) + assert restored.see_also is not UNSET + assert len(restored.see_also) == 1 + sa = restored.see_also[0] + assert sa.guid == "related-term-guid" + assert sa.relationship_attributes["typeName"] == "AtlasGlossaryRelatedTerm" + + +# --------------------------------------------------------------------------- +# AtlasGlossarySynonym +# --------------------------------------------------------------------------- + + +def test_atlas_glossary_synonym_builder(): + """Test AtlasGlossarySynonym builder methods.""" + syn_rel = AtlasGlossarySynonym( + description="Synonym relationship", + expression="synonym-expression", + status="ACTIVE", + steward="data-steward", + source="manual", + ) + + synonym_term = AtlasGlossaryTerm.ref_by_guid("synonym-term-guid") + + syn_ref = syn_rel.synonyms(synonym_term) + _assert_related_entity(syn_ref, "AtlasGlossarySynonym") + assert syn_ref.guid == "synonym-term-guid" + attrs = syn_ref.relationship_attributes["attributes"] + assert attrs["description"] == "Synonym relationship" + assert attrs["expression"] == "synonym-expression" + + +def test_atlas_glossary_synonym_serialization(): + """Test serialization of AtlasGlossarySynonym relationship.""" + term = AtlasGlossaryTerm.updater( + qualified_name="main-term@default", + name="Main Term", + glossary_guid="business-glossary-guid", + ) + + synonym_term = AtlasGlossaryTerm.ref_by_guid("synonym-term-guid") + + synonym_rel = AtlasGlossarySynonym( + description="Synonym relationship", + expression="synonym-expression", + status="ACTIVE", + steward="data-steward", + source="manual", + ) + + term.synonyms = [synonym_rel.synonyms(synonym_term)] + + result = json.loads(term.to_json()) + + syn_item = _get_serialized_rel_attrs(result, "synonyms", 0) + assert syn_item is not None + assert syn_item["guid"] == "synonym-term-guid" + assert syn_item["relationshipType"] == "AtlasGlossarySynonym" + assert syn_item["relationshipAttributes"]["typeName"] == "AtlasGlossarySynonym" + + +# --------------------------------------------------------------------------- +# AtlasGlossarySemanticAssignment +# --------------------------------------------------------------------------- + + +def test_atlas_glossary_semantic_assignment_builder(): + """Test AtlasGlossarySemanticAssignment builder methods.""" + sem_rel = AtlasGlossarySemanticAssignment( + description="Customer table semantically represents customer concept", + expression="business metadata mapping", + status="ACTIVE", + confidence=95, + created_by="data-analyst", + steward="data-governance-team", + source="automated-discovery", + ) + + table = Table.ref_by_guid("customer-table-guid") + + ae_ref = sem_rel.assigned_entities(table) + _assert_related_entity(ae_ref, "AtlasGlossarySemanticAssignment") + assert ae_ref.guid == "customer-table-guid" + attrs = ae_ref.relationship_attributes["attributes"] + assert ( + attrs["description"] + == "Customer table semantically represents customer concept" + ) + assert attrs["expression"] == "business metadata mapping" + assert attrs["status"] == "ACTIVE" + assert attrs["confidence"] == 95 + assert attrs["createdBy"] == "data-analyst" + assert attrs["steward"] == "data-governance-team" + assert attrs["source"] == "automated-discovery" + + +def test_atlas_glossary_semantic_assignment_serialization(): + """Test serialization of AtlasGlossarySemanticAssignment relationship.""" + term = AtlasGlossaryTerm.updater( + qualified_name="customer@business-glossary", + name="Customer", + glossary_guid="business-glossary-guid", + ) + + table = Table.ref_by_guid("customer-table-guid") + + semantic_assignment = AtlasGlossarySemanticAssignment( + description="Customer term semantically represents customer data", + expression="business metadata mapping", + status="ACTIVE", + confidence=95, + created_by="data-analyst", + steward="data-governance-team", + source="automated-discovery", + ) + + term.assigned_entities = [semantic_assignment.assigned_entities(table)] + + result = json.loads(term.to_json()) + + ae_item = _get_serialized_rel_attrs(result, "assignedEntities", 0) + assert ae_item is not None + assert ae_item["guid"] == "customer-table-guid" + assert ae_item["relationshipType"] == "AtlasGlossarySemanticAssignment" + assert ( + ae_item["relationshipAttributes"]["typeName"] + == "AtlasGlossarySemanticAssignment" + ) + assert ( + ae_item["relationshipAttributes"]["attributes"]["description"] + == "Customer term semantically represents customer data" + ) + assert ae_item["relationshipAttributes"]["attributes"]["confidence"] == 95 + + +def test_atlas_glossary_semantic_assignment_meanings_builder(): + """Test meanings() builder on AtlasGlossarySemanticAssignment.""" + sem_rel = AtlasGlossarySemanticAssignment( + description="Customer data is assigned this business term", + expression="manual assignment", + status="ACTIVE", + confidence=95, + created_by="data-analyst", + steward="business-analyst", + source="business-glossary", + ) + + term = AtlasGlossaryTerm.ref_by_guid("customer-term-guid") + + meanings_ref = sem_rel.meanings(term) + _assert_related_entity(meanings_ref, "AtlasGlossarySemanticAssignment") + assert meanings_ref.guid == "customer-term-guid" + assert isinstance(meanings_ref, RelatedAtlasGlossaryTerm) + + +# --------------------------------------------------------------------------- +# UserDefRelationship +# --------------------------------------------------------------------------- + + +def test_user_def_relationship_builder(): + """Test UserDefRelationship builder methods.""" + udr = UserDefRelationship( + from_type_label="test-from-label", to_type_label="test-to-label" + ) + + target = AtlasGlossaryTerm.ref_by_guid("target-term-guid") + + to_ref = udr.user_def_relationship_to(target) + _assert_related_entity(to_ref, "UserDefRelationship") + assert to_ref.guid == "target-term-guid" + attrs = to_ref.relationship_attributes["attributes"] + assert attrs["fromTypeLabel"] == "test-from-label" + assert attrs["toTypeLabel"] == "test-to-label" + + from_ref = udr.user_def_relationship_from(target) + _assert_related_entity(from_ref, "UserDefRelationship") + + +def test_user_def_relationship_serialization(): + """Test serialization of UserDefRelationship relationship.""" + term1 = AtlasGlossaryTerm.updater( + qualified_name="test-term-qn", + name="test-term", + glossary_guid="test-glossary-guid", + ) + + term0 = AtlasGlossaryTerm.ref_by_guid("test-term0-guid") + term2 = AtlasGlossaryTerm.ref_by_guid("test-term2-guid") + term3 = AtlasGlossaryTerm.ref_by_guid("test-term3-guid") + + udr_from0 = UserDefRelationship( + from_type_label="test0-from-label", to_type_label="test0-to-label" + ) + udr_to1 = UserDefRelationship( + from_type_label="test1-from-label", to_type_label="test1-to-label" + ) + udr_to2 = UserDefRelationship( + from_type_label="test2-from-label", to_type_label="test2-to-label" + ) + + term1.user_def_relationship_from = [udr_from0.user_def_relationship_from(term0)] + term1.user_def_relationship_to = [ + udr_to1.user_def_relationship_to(term2), + udr_to2.user_def_relationship_to(term3), + ] + + result = json.loads(term1.to_json()) + + # Check userDefRelationshipFrom + from_item = _get_serialized_rel_attrs(result, "userDefRelationshipFrom", 0) + assert from_item is not None + assert from_item["guid"] == "test-term0-guid" + assert from_item["relationshipType"] == "UserDefRelationship" + assert ( + from_item["relationshipAttributes"]["attributes"]["fromTypeLabel"] + == "test0-from-label" + ) + assert ( + from_item["relationshipAttributes"]["attributes"]["toTypeLabel"] + == "test0-to-label" + ) + + # Check userDefRelationshipTo - first item + to_item1 = _get_serialized_rel_attrs(result, "userDefRelationshipTo", 0) + assert to_item1 is not None + assert to_item1["guid"] == "test-term2-guid" + assert ( + to_item1["relationshipAttributes"]["attributes"]["fromTypeLabel"] + == "test1-from-label" + ) + + # Check userDefRelationshipTo - second item + to_item2 = _get_serialized_rel_attrs(result, "userDefRelationshipTo", 1) + assert to_item2 is not None + assert to_item2["guid"] == "test-term3-guid" + assert ( + to_item2["relationshipAttributes"]["attributes"]["fromTypeLabel"] + == "test2-from-label" + ) + + +# --------------------------------------------------------------------------- +# CustomRelatedFromEntitiesCustomRelatedToEntities +# --------------------------------------------------------------------------- + + +def test_custom_related_entities_builder(): + """Test CustomRelatedFromEntitiesCustomRelatedToEntities builder methods.""" + custom_rel = CustomRelatedFromEntitiesCustomRelatedToEntities( + custom_entity_to_label="relates to", + custom_entity_from_label="relates from", + ) + + target = CustomEntity.ref_by_guid("target-entity-guid") + + to_ref = custom_rel.custom_related_to_entities(target) + _assert_related_entity( + to_ref, "custom_related_from_entities_custom_related_to_entities" + ) + assert to_ref.guid == "target-entity-guid" + attrs = to_ref.relationship_attributes["attributes"] + assert attrs["customEntityToLabel"] == "relates to" + assert attrs["customEntityFromLabel"] == "relates from" + + from_ref = custom_rel.custom_related_from_entities(target) + _assert_related_entity( + from_ref, "custom_related_from_entities_custom_related_to_entities" + ) + + +def test_custom_related_entities_serialization(): + """Test serialization of CustomRelatedFromEntitiesCustomRelatedToEntities.""" + entity = CustomEntity(qualified_name="main-entity@default", name="Main Entity") + + target_entity = CustomEntity.ref_by_guid("target-entity-guid") + source_entity = CustomEntity.ref_by_guid("source-entity-guid") + + custom_rel = CustomRelatedFromEntitiesCustomRelatedToEntities( + custom_entity_to_label="relates to", custom_entity_from_label="relates from" + ) + + entity.custom_related_to_entities = [ + custom_rel.custom_related_to_entities(target_entity) + ] + entity.custom_related_from_entities = [ + custom_rel.custom_related_from_entities(source_entity) + ] + + result = json.loads(entity.to_json()) + + # Check customRelatedToEntities + to_item = _get_serialized_rel_attrs(result, "customRelatedToEntities", 0) + assert to_item is not None + assert to_item["guid"] == "target-entity-guid" + assert ( + to_item["relationshipType"] + == "custom_related_from_entities_custom_related_to_entities" + ) + assert ( + to_item["relationshipAttributes"]["attributes"]["customEntityToLabel"] + == "relates to" + ) + assert ( + to_item["relationshipAttributes"]["attributes"]["customEntityFromLabel"] + == "relates from" + ) + + # Check customRelatedFromEntities + from_item = _get_serialized_rel_attrs(result, "customRelatedFromEntities", 0) + assert from_item is not None + assert from_item["guid"] == "source-entity-guid" + + +def test_custom_related_entities_roundtrip(): + """Test round-trip for CustomRelatedFromEntitiesCustomRelatedToEntities.""" + entity = CustomEntity(qualified_name="main-entity@default", name="Main Entity") + + custom_rel = CustomRelatedFromEntitiesCustomRelatedToEntities( + custom_entity_to_label="relates to", custom_entity_from_label="relates from" + ) + + entity.custom_related_to_entities = [ + custom_rel.custom_related_to_entities(CustomEntity.ref_by_guid("target-guid")) + ] + + restored = CustomEntity.from_json(entity.to_json()) + assert restored.custom_related_to_entities is not UNSET + assert len(restored.custom_related_to_entities) == 1 + to_entity = restored.custom_related_to_entities[0] + assert to_entity.guid == "target-guid" + assert ( + to_entity.relationship_attributes["typeName"] + == "custom_related_from_entities_custom_related_to_entities" + ) + + +# --------------------------------------------------------------------------- +# Combined multiple relationships on a single asset +# --------------------------------------------------------------------------- + + +def test_combined_multiple_relationships_on_single_asset(): + """Test combining multiple different relationship types on a single glossary term.""" + term = AtlasGlossaryTerm.updater( + qualified_name="customer@business-glossary", + name="Customer", + glossary_guid="business-glossary-guid", + ) + + # 1. Add categorization relationship + categorization = AtlasGlossaryTermCategorization( + description="Customer is categorized under business concepts", status="ACTIVE" + ) + term.categories = [ + categorization.categories( + AtlasGlossaryCategory.ref_by_guid("business-category-guid") + ) + ] + + # 2. Add is-a relationship (hierarchical) + is_a_rel = AtlasGlossaryIsARelationship( + description="Customer is a type of Person", + expression="business hierarchy", + status="ACTIVE", + steward="business-analyst", + source="domain-expert", + ) + term.classifies = [ + is_a_rel.classifies(AtlasGlossaryTerm.ref_by_guid("person-term-guid")) + ] + + # 3. Add valid values relationship + valid_value_rel = AtlasGlossaryValidValue( + description="Active is a valid status for Customer", + expression="enumeration value", + status="ACTIVE", + steward="data-modeler", + source="business-rules", + ) + term.valid_values = [ + valid_value_rel.valid_values( + AtlasGlossaryTerm.ref_by_guid("active-status-guid") + ) + ] + + # 4. Add preferred term relationship + preferred_rel = AtlasGlossaryPreferredTerm( + description="Customer is preferred over Client", + expression="business preference", + status="ACTIVE", + steward="business-analyst", + source="style-guide", + ) + term.preferred_to_terms = [ + preferred_rel.preferred_to_terms( + AtlasGlossaryTerm.ref_by_guid("client-term-guid") + ) + ] + + # 5. Add synonym relationship + synonym_rel = AtlasGlossarySynonym( + description="Customer and Buyer are synonymous", + expression="business synonym", + status="ACTIVE", + steward="business-analyst", + source="domain-expert", + ) + term.synonyms = [ + synonym_rel.synonyms(AtlasGlossaryTerm.ref_by_guid("buyer-term-guid")) + ] + + # 6. Add user-defined relationship + user_def_rel = UserDefRelationship( + from_type_label="has account", to_type_label="belongs to customer" + ) + term.user_def_relationship_to = [ + user_def_rel.user_def_relationship_to( + AtlasGlossaryTerm.ref_by_guid("account-term-guid") + ) + ] + + # Serialize and verify structure + result = json.loads(term.to_json()) + + rel_attrs = result.get("relationshipAttributes", {}) + + # Verify each relationship type is present + assert "categories" in rel_attrs + assert "classifies" in rel_attrs + assert "validValues" in rel_attrs + assert "preferredToTerms" in rel_attrs + assert "synonyms" in rel_attrs + assert "userDefRelationshipTo" in rel_attrs + + # Verify each has exactly one entry + assert len(rel_attrs["categories"]) == 1 + assert len(rel_attrs["classifies"]) == 1 + assert len(rel_attrs["validValues"]) == 1 + assert len(rel_attrs["preferredToTerms"]) == 1 + assert len(rel_attrs["synonyms"]) == 1 + assert len(rel_attrs["userDefRelationshipTo"]) == 1 + + # Verify relationship types + assert ( + rel_attrs["categories"][0]["relationshipType"] + == "AtlasGlossaryTermCategorization" + ) + assert ( + rel_attrs["classifies"][0]["relationshipType"] == "AtlasGlossaryIsARelationship" + ) + assert rel_attrs["validValues"][0]["relationshipType"] == "AtlasGlossaryValidValue" + assert ( + rel_attrs["preferredToTerms"][0]["relationshipType"] + == "AtlasGlossaryPreferredTerm" + ) + assert rel_attrs["synonyms"][0]["relationshipType"] == "AtlasGlossarySynonym" + assert ( + rel_attrs["userDefRelationshipTo"][0]["relationshipType"] + == "UserDefRelationship" + ) + + # Verify relationship attributes are present in each + for rel_field in [ + "categories", + "classifies", + "validValues", + "preferredToTerms", + "synonyms", + "userDefRelationshipTo", + ]: + for rel in rel_attrs[rel_field]: + assert "relationshipAttributes" in rel + assert "typeName" in rel["relationshipAttributes"] + assert "attributes" in rel["relationshipAttributes"] + + # Verify specific relationship attribute values + assert ( + rel_attrs["categories"][0]["relationshipAttributes"]["attributes"][ + "description" + ] + == "Customer is categorized under business concepts" + ) + assert ( + rel_attrs["classifies"][0]["relationshipAttributes"]["attributes"][ + "description" + ] + == "Customer is a type of Person" + ) + assert ( + rel_attrs["validValues"][0]["relationshipAttributes"]["attributes"][ + "description" + ] + == "Active is a valid status for Customer" + ) + assert ( + rel_attrs["preferredToTerms"][0]["relationshipAttributes"]["attributes"][ + "description" + ] + == "Customer is preferred over Client" + ) + assert ( + rel_attrs["synonyms"][0]["relationshipAttributes"]["attributes"]["description"] + == "Customer and Buyer are synonymous" + ) + assert ( + rel_attrs["userDefRelationshipTo"][0]["relationshipAttributes"]["attributes"][ + "fromTypeLabel" + ] + == "has account" + ) + + +# --------------------------------------------------------------------------- +# IndistinctRelationship +# --------------------------------------------------------------------------- + + +def test_indistinct_relationship(): + """Test IndistinctRelationship as a fallback for unknown relationship types.""" + indistinct = IndistinctRelationship( + type_name="SomeUnknownRelationship", + attributes={"custom_field": "custom_value", "status": "ACTIVE"}, + ) + + assert indistinct.type_name == "SomeUnknownRelationship" + assert indistinct.attributes["custom_field"] == "custom_value" + assert indistinct.attributes["status"] == "ACTIVE" + + attrs_dict = indistinct._attrs_dict() + assert attrs_dict["custom_field"] == "custom_value" + assert attrs_dict["status"] == "ACTIVE" + + +# --------------------------------------------------------------------------- +# Builder with qualified_name (no GUID) +# --------------------------------------------------------------------------- + + +def test_builder_with_qualified_name_ref(): + """Test relationship builder when related entity uses qualifiedName instead of GUID.""" + categorization = AtlasGlossaryTermCategorization( + description="Test categorization", status="ACTIVE" + ) + + # Create a term referenced by qualified_name + term = AtlasGlossaryTerm( + qualified_name="some-term@some-glossary", + name="some-term", + ) + + ref = categorization.terms(term) + _assert_related_entity(ref, "AtlasGlossaryTermCategorization") + # When guid is not set, unique_attributes should be populated + assert ref.unique_attributes is not UNSET + assert ref.unique_attributes["qualifiedName"] == "some-term@some-glossary" diff --git a/tests_v9/unit/test_events.py b/tests_v9/unit/test_events.py new file mode 100644 index 000000000..d469c645b --- /dev/null +++ b/tests_v9/unit/test_events.py @@ -0,0 +1,146 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +""" +Unit tests for event models using v9 msgspec-based AtlanEvent. + +Ported from tests/unit/test_events.py. All models are now v9 msgspec Structs — +AtlanEvent.from_dict() handles polymorphic Asset dispatch and payload +discrimination natively via the v9 type registry. +""" + +from json import load, loads +from pathlib import Path + +import pytest + +from pyatlan.events.atlan_event_handler import is_validation_request, valid_signature +from pyatlan_v9.client.atlan import AtlanClient + +# v9 asset type for isinstance check +from pyatlan_v9.model.assets import AtlasGlossaryTerm + +# v9 event models (fully msgspec-based) +from pyatlan_v9.model.events import ( + AssetCreatePayload, + AssetDeletePayload, + AssetUpdatePayload, + AtlanEvent, + AtlanTagAddPayload, + AtlanTagDeletePayload, + CustomMetadataUpdatePayload, +) + +ACTUAL_JSON = "actual.json" +VALIDATION_JSON = "validation.json" +ENTITY_CREATE_JSON = "entity_create.json" +ENTITY_UPDATE_JSON = "entity_update.json" +ENTITY_DELETE_JSON = "entity_delete.json" +CLASSIFICATION_ADD_JSON = "classification_add.json" +CLASSIFICATION_DELETE_JSON = "classification_delete.json" +BUSINESS_ATTRIBUTE_UPDATE_JSON = "business_attribute_update.json" +TEST_DATA_DIR = Path(__file__).parent.parent.parent / "tests" / "unit" / "data" +EVENT_RESPONSES_DIR = TEST_DATA_DIR / "event_responses" + + +def load_json(respones_dir, filename): + with (respones_dir / filename).open() as input_file: + return load(input_file) + + +@pytest.fixture(autouse=True) +def set_env(monkeypatch): + monkeypatch.setenv("ATLAN_API_KEY", "test-api-key") + monkeypatch.setenv("ATLAN_BASE_URL", "https://test.atlan.com") + + +@pytest.fixture() +def client(): + return AtlanClient() + + +@pytest.fixture() +def actual_json(): + return load_json(EVENT_RESPONSES_DIR, ACTUAL_JSON) + + +@pytest.fixture() +def validation_json(): + return load_json(EVENT_RESPONSES_DIR, VALIDATION_JSON) + + +@pytest.fixture() +def entity_create_json(): + return load_json(EVENT_RESPONSES_DIR, ENTITY_CREATE_JSON) + + +@pytest.fixture() +def entity_update_json(): + return load_json(EVENT_RESPONSES_DIR, ENTITY_UPDATE_JSON) + + +@pytest.fixture() +def entity_delete_json(): + return load_json(EVENT_RESPONSES_DIR, ENTITY_DELETE_JSON) + + +@pytest.fixture() +def custom_metadata_add_json(): + return load_json(EVENT_RESPONSES_DIR, BUSINESS_ATTRIBUTE_UPDATE_JSON) + + +@pytest.fixture() +def tag_add_json(): + return load_json(EVENT_RESPONSES_DIR, CLASSIFICATION_ADD_JSON) + + +@pytest.fixture() +def tag_delete_json(): + return load_json(EVENT_RESPONSES_DIR, CLASSIFICATION_DELETE_JSON) + + +def test_validation_payload(validation_json): + body = validation_json.get("body") + assert is_validation_request(body) + + +def test_no_signing_key(validation_json): + assert not valid_signature("test-secret", validation_json.get("headers")) + + +def test_signing_key(actual_json): + assert valid_signature("test-secret", actual_json.get("headers")) + + +def test_body(actual_json): + body = loads(actual_json.get("body")) + assert body + atlan_event = AtlanEvent.from_dict(body) + assert atlan_event + assert atlan_event.payload + assert isinstance(atlan_event.payload.asset, AtlasGlossaryTerm) + + +def test_atlan_events_deserialization( + client, + mock_tag_cache, + entity_create_json, + entity_update_json, + entity_delete_json, + tag_add_json, + tag_delete_json, + custom_metadata_add_json, +): + _EVENT_TYPES = { + "entity_create_json": AssetCreatePayload, + "entity_update_json": AssetUpdatePayload, + "entity_delete_json": AssetDeletePayload, + "tag_add_json": AtlanTagAddPayload, + "tag_delete_json": AtlanTagDeletePayload, + "custom_metadata_add_json": CustomMetadataUpdatePayload, + } + + for key, payload_type in _EVENT_TYPES.items(): + data = locals()[key] + event = AtlanEvent.from_dict(data) + assert isinstance(event.payload, payload_type) diff --git a/tests_v9/unit/test_file_client.py b/tests_v9/unit/test_file_client.py new file mode 100644 index 000000000..aaff6203d --- /dev/null +++ b/tests_v9/unit/test_file_client.py @@ -0,0 +1,257 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +""" +Unit tests for file client — ported from tests/unit/test_file_client.py. + +Uses v9 PresignedURLRequest (msgspec) for inputs to client methods. Other file +operations (upload, download) are tested using the legacy AtlanClient and +FileClient since they don't depend on model differences. +""" + +import os +from json import load +from pathlib import Path +from unittest.mock import Mock, patch + +import pytest + +from pyatlan.client.common import ApiCaller +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.client.file import V9FileClient as FileClient +from pyatlan_v9.errors import InvalidRequestError + +# v9 model for input +from pyatlan_v9.model.file import PresignedURLRequest +from tests_v9.unit.constants import TEST_FILE_CLIENT_METHODS + +# Share test data with legacy tests +TEST_DATA_DIR = Path(__file__).parent.parent.parent / "tests" / "unit" / "data" +UPLOAD_FILE_PATH = str(TEST_DATA_DIR / "file_requests/upload.txt") +DOWNLOAD_FILE_PATH = str(TEST_DATA_DIR / "file_requests/download.txt") + + +def load_json(respones_dir, filename): + with (respones_dir / filename).open() as input_file: + return load(input_file) + + +def to_json(model): + return model.json(by_alias=True, exclude_none=True) + + +@pytest.fixture(autouse=True) +def set_env(monkeypatch): + monkeypatch.setenv("ATLAN_BASE_URL", "https://test.atlan.com") + monkeypatch.setenv("ATLAN_API_KEY", "test-api-key") + + +@pytest.fixture() +def client(): + return AtlanClient() + + +@pytest.fixture(scope="module") +def mock_api_caller(): + return Mock(spec=ApiCaller) + + +@pytest.fixture(scope="module") +def s3_presigned_url(): + return ( + "https://test-vcluster.amazonaws.com/some-directory/test.png" + "?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20240425T09240" + ) + + +@pytest.fixture(scope="module") +def blob_presigned_url(): + return ( + "https://test.blob.core.windows.net/objectstore/test.png" + "?se=2024-08-12T09%3A45%3A13Z&sig=esqARNUwHUETQOqSCaSCTqD" + "Wjg7vTmcK1PLzQ1buMCQ%3D&sp=aw&spr=https&sr=b&sv=2020-04-08" + ) + + +@pytest.fixture(scope="module") +def gcs_presigned_url(): + return ( + "https://test.storage.googleapis.com/test-vcluster/test.png" + "?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=prod" + "iam.gserviceaccount.com%2F20240813%2Fauto%2Fstorage%2Fgoog" + "4_request&X-Goog-Date=20240893T093902Z&X-Goog-Expires=29&X-" + "Goog-Signature=5620d93a7916b150ce87a324d969741112f764b6d9f6" + ) + + +@pytest.fixture() +def mock_session(): + with patch.object(AtlanClient, "_session") as mock_session: + mock_response = Mock() + mock_response.status_code = 200 + mock_response.raw = open(UPLOAD_FILE_PATH, "rb") + mock_response.headers = {} + + # Mock the methods our streaming code expects + mock_response.read.return_value = b"test content" + + def mock_iter_raw(chunk_size=None): + # Use the actual expected content from upload.txt + content = b"test data 12345.\n" + yield content + + mock_response.iter_raw = mock_iter_raw + + # Use Mock's context manager support + mock_session.stream.return_value.__enter__.return_value = mock_response + mock_session.stream.return_value.__exit__.return_value = None + + yield mock_session + assert os.path.exists(DOWNLOAD_FILE_PATH) + os.remove(DOWNLOAD_FILE_PATH) + + +@pytest.fixture() +def mock_session_invalid(): + with patch.object(AtlanClient, "_session") as mock_session: + mock_response = Mock() + mock_response.status_code = 200 + mock_response.raw = "not a bytes-like object" + mock_response.headers = {} + + # Mock the methods our streaming code expects + mock_response.read.return_value = b"test content" + + def mock_iter_raw(chunk_size=None): + # Return a generator that will fail during iteration + # This simulates a case where the response object is invalid + class BadIterator: + def __iter__(self): + return self + + def __next__(self): + # Simulate the error that would happen in real scenario + raise AttributeError("'str' object has no attribute 'read'") + + return BadIterator() + + mock_response.iter_raw = mock_iter_raw + + # Use Mock's context manager support + mock_session.stream.return_value.__enter__.return_value = mock_response + mock_session.stream.return_value.__exit__.return_value = None + + yield mock_session + # Don't assert file exists for invalid case since error should prevent creation + if os.path.exists(DOWNLOAD_FILE_PATH): + os.remove(DOWNLOAD_FILE_PATH) + + +@pytest.mark.parametrize("method, params", TEST_FILE_CLIENT_METHODS.items()) +def test_file_client_methods_validation_error(client, method, params): + client_method = getattr(client.files, method) + for param_values, error_msg in params: + with pytest.raises(ValueError, match=error_msg): + client_method(*param_values) + + +@pytest.mark.parametrize( + "file_path, expected_error", + [ + [ + UPLOAD_FILE_PATH, + ( + "ATLAN-PYTHON-400-061 Provided presigned URL's cloud provider " + "storage is currently not supported for file uploads." + ), + ], + [ + "some/invalid/file_path.png", + ( + "ATLAN-PYTHON-400-059 Unable to upload file, " + "Error: No such file or directory, Path: some/invalid/file_path.png" + ), + ], + ], +) +def test_file_client_upload_file_raises_invalid_request_error( + mock_api_caller, file_path, expected_error +): + client = FileClient(client=mock_api_caller) + + with pytest.raises(InvalidRequestError, match=expected_error): + client.upload_file( + presigned_url="test-url", + file_path=file_path, + ) + + +def test_file_client_download_file_invalid_format_raises_invalid_request_error( + client, s3_presigned_url, mock_session_invalid +): + expected_error = ( + "ATLAN-PYTHON-400-060 Unable to download file, " + f"Error: 'str' object has no attribute 'read', Path: {DOWNLOAD_FILE_PATH}" + ) + with pytest.raises(InvalidRequestError, match=expected_error): + client.files.download_file( + presigned_url=s3_presigned_url, file_path=DOWNLOAD_FILE_PATH + ) + + +def test_file_client_get_presigned_url(mock_api_caller, s3_presigned_url): + mock_api_caller._call_api.side_effect = [{"url": s3_presigned_url}] + client = FileClient(mock_api_caller) + # Use v9 PresignedURLRequest + response = client.generate_presigned_url( + request=PresignedURLRequest( + key="some-directory/test.png", + expiry="60s", + method=PresignedURLRequest.Method.GET, + ) + ) + assert mock_api_caller._call_api.call_count == 1 + assert response == s3_presigned_url + mock_api_caller.reset_mock() + + +@patch.object(AtlanClient, "_call_api_internal", return_value=None) +def test_file_client_s3_upload_file(mock_call_api_internal, client, s3_presigned_url): + client = FileClient(client=client) + client.upload_file(presigned_url=s3_presigned_url, file_path=UPLOAD_FILE_PATH) + + assert mock_call_api_internal.call_count == 1 + mock_call_api_internal.reset_mock() + + +@patch.object(AtlanClient, "_call_api_internal", return_value=None) +def test_file_client_azure_blob_upload_file( + mock_call_api_internal, client, blob_presigned_url +): + client = FileClient(client=client) + client.upload_file(presigned_url=blob_presigned_url, file_path=UPLOAD_FILE_PATH) + + assert mock_call_api_internal.call_count == 1 + mock_call_api_internal.reset_mock() + + +@patch.object(AtlanClient, "_call_api_internal", return_value=None) +def test_file_client_gcs_upload_file(mock_call_api_internal, client, gcs_presigned_url): + client = FileClient(client=client) + client.upload_file(presigned_url=gcs_presigned_url, file_path=UPLOAD_FILE_PATH) + + assert mock_call_api_internal.call_count == 1 + mock_call_api_internal.reset_mock() + + +def test_file_client_download_file(client, s3_presigned_url, mock_session): + # Make sure the download file doesn't exist before downloading + assert not os.path.exists(DOWNLOAD_FILE_PATH) + response = client.files.download_file( + presigned_url=s3_presigned_url, file_path=DOWNLOAD_FILE_PATH + ) + assert response == DOWNLOAD_FILE_PATH + assert mock_session.stream.call_count == 1 + # The file should exist after calling the method + assert os.path.exists(DOWNLOAD_FILE_PATH) + assert open(DOWNLOAD_FILE_PATH, "r").read() == "test data 12345.\n" diff --git a/tests_v9/unit/test_glossary_term.py b/tests_v9/unit/test_glossary_term.py new file mode 100644 index 000000000..ce778a1fa --- /dev/null +++ b/tests_v9/unit/test_glossary_term.py @@ -0,0 +1,114 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +""" +Unit tests for v9 AtlasGlossaryTerm — ported from tests/unit/test_glossary_term.py. + +The v9 model is flat (no nested Attributes class), so: +- `Attributes.create()` tests are adapted to use `AtlasGlossaryTerm.create()` directly. +- Assertions on `sut.attributes.anchor` become `sut.anchor`. +- Anchor `unique_attributes` becomes `anchor.qualified_name`. +""" + +import pytest + +from pyatlan_v9.model.assets import AtlasGlossary, AtlasGlossaryTerm + + +@pytest.mark.parametrize( + "name, anchor, glossary_qualified_name, glossary_guid, message", + [ + (None, None, None, "1234", "name is required"), + ( + "Glossary", + None, + None, + None, + "One of the following parameters are required: anchor, glossary_qualified_name, glossary_guid", + ), + ( + "Glossary", + AtlasGlossary(name="g"), + "qname", + None, + "Only one of the following parameters are allowed: anchor, glossary_qualified_name", + ), + ( + "Glossary", + AtlasGlossary(name="g"), + None, + "123", + "Only one of the following parameters are allowed: anchor, glossary_guid", + ), + ( + "Glossary", + None, + "qname", + "123", + "Only one of the following parameters are allowed: glossary_qualified_name, glossary_guid", + ), + ], +) +def test_create_without_required_parameters_raises_value_error( + name, anchor, glossary_qualified_name, glossary_guid, message +): + """ + Test that AtlasGlossaryTerm.create() raises ValueError for invalid parameter + combinations. Replaces both `Attributes.create` and `create` legacy tests. + """ + with pytest.raises(ValueError, match=message): + AtlasGlossaryTerm.create( + name=name, + anchor=anchor, + glossary_qualified_name=glossary_qualified_name, + glossary_guid=glossary_guid, + ) + + +@pytest.mark.parametrize( + "name, anchor, glossary_qualified_name, glossary_guid", + [ + ("Glossary", AtlasGlossary.ref_by_guid(guid="123"), None, None), + ( + "Glossary", + AtlasGlossary.ref_by_qualified_name( + qualified_name="glossary/qualifiedName" + ), + None, + None, + ), + ("Glossary", None, "glossary/qualifiedName", None), + ("Glossary", None, None, "123"), + ], +) +def test_create_with_required_parameters( + name, anchor, glossary_qualified_name, glossary_guid +): + """ + Test that AtlasGlossaryTerm.create() works with valid parameter combinations. + Replaces both `Attributes.create` and `create` legacy tests. + """ + sut = AtlasGlossaryTerm.create( + name=name, + anchor=anchor, + glossary_qualified_name=glossary_qualified_name, + glossary_guid=glossary_guid, + ) + + # In the v9 flat model, anchor is directly on the term (not nested in attributes) + if anchor: + # The creator trims the anchor to a reference; check guid/qn match + if hasattr(anchor, "guid") and anchor.guid is not None: + from msgspec import UNSET + + if anchor.guid is not UNSET: + assert sut.anchor.guid == anchor.guid + if hasattr(anchor, "qualified_name") and anchor.qualified_name is not None: + from msgspec import UNSET + + if anchor.qualified_name is not UNSET: + assert sut.anchor.qualified_name == anchor.qualified_name + if glossary_qualified_name: + assert sut.anchor.qualified_name == glossary_qualified_name + if glossary_guid: + assert sut.anchor.guid == glossary_guid diff --git a/tests_v9/unit/test_lineage.py b/tests_v9/unit/test_lineage.py new file mode 100644 index 000000000..d080136eb --- /dev/null +++ b/tests_v9/unit/test_lineage.py @@ -0,0 +1,1175 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2022 Atlan Pte. Ltd. + +""" +Unit tests for lineage — ported from tests/unit/test_lineage.py. + +Uses v9 msgspec.Struct lineage models (LineageRelation, LineageResponse, +FluentLineage, etc.) and re-exported plain dataclass types (LineageGraph, +DirectedPair). The ``Asset`` ClassVar field references (CERTIFICATE_STATUS, +STATUS, NAME, etc.) are imported from the legacy model because the v9 Asset +model does not expose those ClassVar search fields. +""" + +import json +from datetime import date +from pathlib import Path +from typing import List +from unittest.mock import Mock + +import msgspec +import pytest + +from pyatlan.model.fields.atlan_fields import ( + AtlanField, + CustomMetadataField, + LineageFilter, + LineageFilterField, + LineageFilterFieldBoolean, + LineageFilterFieldCM, + LineageFilterFieldNumeric, + LineageFilterFieldString, + SearchableField, +) +from pyatlan_v9.errors import AtlanError, InvalidRequestError +from pyatlan_v9.model.assets import Asset +from pyatlan_v9.model.enums import ( + AtlanComparisonOperator, + CertificateStatus, + EntityStatus, + FileType, + LineageDirection, +) + +# v9 lineage models +from pyatlan_v9.model.lineage import ( + FilterList, + FluentLineage, + LineageGraph, + LineageListRequest, + LineageRelation, + LineageResponse, +) +from pyatlan_v9.model.typedef import AttributeDef + +TODAY = date.today() +BASE_GUID = "75474eab-3105-4ef9-9f84-709e386a7d3e" +BASE_GUID_TARGET = "e44ed3a2-1de5-4f23-b3f1-6e005156fee9" +LINEAGE_RESPONSES_DIR = ( + Path(__file__).parent + / ".." + / ".." + / "tests" + / "unit" + / "data" + / "lineage_responses" +) + + +@pytest.fixture(scope="session") +def lineage_json(): + with (LINEAGE_RESPONSES_DIR / "lineage.json").open() as input_file: + return json.load(input_file) + + +@pytest.fixture(scope="session") +def lineage_response(lineage_json): + return msgspec.convert(lineage_json, LineageResponse) + + +@pytest.fixture(scope="session") +def lineage_graph(lineage_response): + return LineageGraph.create(lineage_response.relations) + + +class TestLineageGraph: + def test_create_when_relation_is_not_full_link_then_raises_invalid_request_exception( + self, + ): + with pytest.raises( + InvalidRequestError, + match="Lineage was retrieved using hideProces=False. We do not provide a graph view in this case.", + ): + LineageGraph.create( + [ + LineageRelation( + from_entity_id="123", to_entity_id="456", process_id=None + ) + ] + ) + + def test_get_downstream_asset_guid_with_invalid_guid_returns_empty_set_of_guids( + self, + lineage_graph, + ): + assert len(lineage_graph.get_downstream_asset_guids("123")) == 0 + + def test_get_downstream_asset_guid_with_valid_guid_returns_set_of_guids( + self, lineage_graph + ): + assert (guids := lineage_graph.get_downstream_asset_guids(BASE_GUID)) + assert len(guids) == 1 + assert "e44ed3a2-1de5-4f23-b3f1-6e005156fee9" in guids + + def test_get_upstream_asset_guid_with_invalid_guid_returns_empty_set_of_guids( + self, + lineage_graph, + ): + assert len(lineage_graph.get_upstream_asset_guids("123")) == 0 + + def test_get_upstream_asset_guid_with_base_guid_returns_empty_set_of_guids( + self, + lineage_graph, + ): + assert len(lineage_graph.get_upstream_asset_guids(BASE_GUID)) == 0 + + def test_get_upstream_asset_guid_with_valid_guid_returns_set_of_guids( + self, lineage_graph + ): + assert (guids := lineage_graph.get_upstream_asset_guids(BASE_GUID_TARGET)) + assert len(guids) == 1 + assert BASE_GUID in guids + + def test_get_downstream_process_guid_with_valid_guid_returns_set_of_guids( + self, + lineage_graph, + ): + assert (guids := lineage_graph.get_downstream_process_guids(BASE_GUID)) + assert len(guids) == 2 + assert "621d3fa2-54b0-4cc0-a858-5c5ea8c49349" in guids + assert "d67d4188-010b-4adf-9886-10162f08c77b" in guids + + def test_get_upstream_process_guids_with_valid_guid_returns_set_of_guids( + self, lineage_graph + ): + assert (guids := lineage_graph.get_upstream_process_guids(BASE_GUID_TARGET)) + assert len(guids) == 2 + assert "621d3fa2-54b0-4cc0-a858-5c5ea8c49349" in guids + assert "d67d4188-010b-4adf-9886-10162f08c77b" in guids + + def test_get_downstream_process_guid_with_invalid_guid_returns_empty_set_of_guids( + self, + lineage_graph, + ): + assert len(lineage_graph.get_downstream_process_guids("123")) == 0 + + def test_get_upstream_process_guid_with_invalid_guid_returns_empty_set_of_guids( + self, + lineage_graph, + ): + assert len(lineage_graph.get_upstream_process_guids("123")) == 0 + + @pytest.mark.parametrize( + "guid, expected_count", + [ + ("a3585e50-a045-4349-b9d7-b0980961c1a1", 1), + ("d043b6a8-7a24-42aa-8c3a-25e5901c57ab", 6), + ("0279e19c-3ebb-4472-a737-d245e2914369", 2), + ("0ba9763e-82c2-4765-ad7a-018d46dd5c80", 87), + ("80680c12-f625-4b7f-a5fa-d3fd296e2db1", 18), + ], + ) + def test_get_all_downstream_asset_guids_dfs( + self, guid, expected_count, lineage_graph + ): + assert (guids := lineage_graph.get_all_downstream_asset_guids_dfs(guid)) + assert len(guids) == expected_count + + @pytest.mark.parametrize( + "guid, expected_count", + [ + ("a3585e50-a045-4349-b9d7-b0980961c1a1", 1), + ("d043b6a8-7a24-42aa-8c3a-25e5901c57ab", 8), + ("0279e19c-3ebb-4472-a737-d245e2914369", 10), + ("0ba9763e-82c2-4765-ad7a-018d46dd5c80", 4), + ("80680c12-f625-4b7f-a5fa-d3fd296e2db1", 8), + ], + ) + def test_get_all_upstream_asset_guids_dfs( + self, guid, expected_count, lineage_graph + ): + assert (guids := lineage_graph.get_all_upstream_asset_guids_dfs(guid)) + assert len(guids) == expected_count + + +class TestLineageResponse: + @pytest.mark.parametrize( + "guid, expected_count", + [ + ("a3585e50-a045-4349-b9d7-b0980961c1a1", 1), + ("d043b6a8-7a24-42aa-8c3a-25e5901c57ab", 6), + ("0279e19c-3ebb-4472-a737-d245e2914369", 2), + ("0ba9763e-82c2-4765-ad7a-018d46dd5c80", 87), + ("80680c12-f625-4b7f-a5fa-d3fd296e2db1", 18), + ], + ) + def test_get_all_downstream_asset_guids_dfs( + self, guid, expected_count, lineage_response + ): + assert (guids := lineage_response.get_all_downstream_asset_guids_dfs(guid)) + assert len(guids) == expected_count + + @pytest.mark.parametrize( + "guid, expected_count", + [ + ("a3585e50-a045-4349-b9d7-b0980961c1a1", 1), + ("d043b6a8-7a24-42aa-8c3a-25e5901c57ab", 6), + ("0279e19c-3ebb-4472-a737-d245e2914369", 2), + ("0ba9763e-82c2-4765-ad7a-018d46dd5c80", 87), + ("80680c12-f625-4b7f-a5fa-d3fd296e2db1", 18), + ], + ) + def test_get_all_downstream_assets_dfs( + self, guid, expected_count, lineage_response + ): + assert (assets := lineage_response.get_all_downstream_assets_dfs(guid)) + assert len(assets) == expected_count + + @pytest.mark.parametrize( + "guid, expected_count", + [ + ("a3585e50-a045-4349-b9d7-b0980961c1a1", 1), + ("d043b6a8-7a24-42aa-8c3a-25e5901c57ab", 8), + ("0279e19c-3ebb-4472-a737-d245e2914369", 10), + ("0ba9763e-82c2-4765-ad7a-018d46dd5c80", 4), + ("80680c12-f625-4b7f-a5fa-d3fd296e2db1", 8), + ], + ) + def test_get_all_upstream_asset_guids_dfs( + self, guid, expected_count, lineage_response + ): + assert (guids := lineage_response.get_all_upstream_asset_guids_dfs(guid)) + assert len(guids) == expected_count + + @pytest.mark.parametrize( + "guid, expected_count", + [ + ("a3585e50-a045-4349-b9d7-b0980961c1a1", 1), + ("d043b6a8-7a24-42aa-8c3a-25e5901c57ab", 8), + ("0279e19c-3ebb-4472-a737-d245e2914369", 10), + ("0ba9763e-82c2-4765-ad7a-018d46dd5c80", 4), + ("80680c12-f625-4b7f-a5fa-d3fd296e2db1", 8), + ], + ) + def test_get_all_upstream_assets_dfs(self, guid, expected_count, lineage_response): + assert (guids := lineage_response.get_all_upstream_assets_dfs(guid)) + assert len(guids) == expected_count + + def test_get_downstream_asset_guid_with_invalid_guid_returns_empty_set_of_guids( + self, + lineage_response, + ): + assert len(lineage_response.get_downstream_asset_guids("123")) == 0 + + def test_get_downstream_asset_guid_with_valid_guid_returns_set_of_guids( + self, lineage_response + ): + assert (guids := lineage_response.get_downstream_asset_guids(BASE_GUID)) + assert len(guids) == 1 + assert "e44ed3a2-1de5-4f23-b3f1-6e005156fee9" in guids + + def test_get_downstream_assets_with_valid_guid_returns_set_of_guids( + self, lineage_response + ): + assert (assets := lineage_response.get_downstream_assets(BASE_GUID)) + assert len(assets) == 1 + + def test_get_upstream_asset_guid_with_invalid_guid_returns_empty_set_of_guids( + self, + lineage_response, + ): + assert len(lineage_response.get_upstream_asset_guids("123")) == 0 + + def test_get_upstream_asset_guid_with_base_guid_returns_empty_set_of_guids( + self, + lineage_response, + ): + assert len(lineage_response.get_upstream_asset_guids(BASE_GUID)) == 0 + + def test_get_upstream_asset_guid_with_valid_guid_returns_set_of_guids( + self, lineage_response + ): + assert (guids := lineage_response.get_upstream_asset_guids(BASE_GUID_TARGET)) + assert len(guids) == 1 + assert BASE_GUID in guids + + def test_get_downstream_process_guid_with_valid_guid_returns_set_of_guids( + self, + lineage_response, + ): + assert (guids := lineage_response.get_downstream_process_guids(BASE_GUID)) + assert len(guids) == 2 + assert "621d3fa2-54b0-4cc0-a858-5c5ea8c49349" in guids + assert "d67d4188-010b-4adf-9886-10162f08c77b" in guids + + def test_get_upstream_process_guids_with_valid_guid_returns_set_of_guids( + self, lineage_response + ): + assert (guids := lineage_response.get_upstream_process_guids(BASE_GUID_TARGET)) + assert len(guids) == 2 + assert "621d3fa2-54b0-4cc0-a858-5c5ea8c49349" in guids + assert "d67d4188-010b-4adf-9886-10162f08c77b" in guids + + def test_get_upstream_assets_with_valid_guid_returns_set_of_assets( + self, lineage_response + ): + assert (assets := lineage_response.get_upstream_process_guids(BASE_GUID_TARGET)) + assert len(assets) == 2 + + def test_get_downstream_process_guid_with_invalid_guid_returns_empty_set_of_guids( + self, + lineage_response, + ): + assert len(lineage_response.get_downstream_process_guids("123")) == 0 + + def test_get_upstream_process_guid_with_invalid_guid_returns_empty_set_of_guids( + self, + lineage_response, + ): + assert len(lineage_response.get_upstream_process_guids("123")) == 0 + + +@pytest.fixture +def searchable_field() -> SearchableField: + return SearchableField( + atlan_field_name="atlan_field", elastic_field_name="elastic_field" + ) + + +class TestLineageFilterField: + @pytest.fixture + def sut(self, searchable_field: SearchableField) -> LineageFilterField: + return LineageFilterField(field=searchable_field) + + def test_init(self, sut: LineageFilterField, searchable_field: SearchableField): + assert sut.field == searchable_field + + def test_has_any_value(self, sut: LineageFilterField): + _filter = sut.has_any_value() + assert _filter.field == sut.field + assert _filter.operator == AtlanComparisonOperator.NOT_NULL + assert _filter.value == "" + + def test_has_no_value(self, sut: LineageFilterField): + _filter = sut.has_no_value() + assert _filter.field == sut.field + assert _filter.operator == AtlanComparisonOperator.IS_NULL + assert _filter.value == "" + + +class TestLineageFilterFieldBoolean: + @pytest.fixture + def sut(self, searchable_field: SearchableField) -> LineageFilterFieldBoolean: + return LineageFilterFieldBoolean(field=searchable_field) + + def test_init( + self, sut: LineageFilterFieldBoolean, searchable_field: SearchableField + ): + assert sut.field == searchable_field + + @pytest.mark.parametrize( + "value, expected", [(True, str(True)), (False, str(False))] + ) + def test_eq(self, value, expected, sut: LineageFilterFieldBoolean): + _filter = sut.eq(value) + assert _filter.field == sut.field + assert _filter.operator == AtlanComparisonOperator.EQ + assert _filter.value == expected + + @pytest.mark.parametrize( + "value, expected", [(True, str(True)), (False, str(False))] + ) + def test_neq(self, value, expected, sut: LineageFilterFieldBoolean): + _filter = sut.neq(value) + assert _filter.field == sut.field + assert _filter.operator == AtlanComparisonOperator.NEQ + assert _filter.value == expected + + +class TestLineageFilterFieldCM: + @pytest.fixture() + def custom_metadata_field(self) -> CustomMetadataField: + return Mock(spec=CustomMetadataField) + + @pytest.fixture + def sut(self, custom_metadata_field: CustomMetadataField) -> LineageFilterFieldCM: + return LineageFilterFieldCM(field=custom_metadata_field) + + @staticmethod + def configure_custom_metadata_field(custom_metadata_field, type_name): + attribute_def = Mock(spec=AttributeDef) + attribute_def.configure_mock(**{"type_name": type_name}) + custom_metadata_field.attach_mock(attribute_def, "attribute_def") + custom_metadata_field.attach_mock(attribute_def, "attribute_def") + custom_metadata_field.configure_mock( + **{"set_name": "something", "attribute_name": "an_attribute"} + ) + + def test_init( + self, sut: LineageFilterFieldCM, custom_metadata_field: CustomMetadataField + ): + assert sut.field == custom_metadata_field + assert sut.cm_field == custom_metadata_field + + @pytest.mark.parametrize( + "value, expected", [("value", "value"), (FileType.CSV, FileType.CSV.value)] + ) + def test_eq(self, value, expected, sut: LineageFilterFieldCM): + _filter = sut.eq(value) + assert _filter.field == sut.field + assert _filter.operator == AtlanComparisonOperator.EQ + assert _filter.value == expected + + @pytest.mark.parametrize( + "value, expected", [("value", "value"), (FileType.CSV, FileType.CSV.value)] + ) + def test_neq(self, value, expected, sut: LineageFilterFieldCM): + _filter = sut.neq(value) + assert _filter.field == sut.field + assert _filter.operator == AtlanComparisonOperator.NEQ + assert _filter.value == expected + + @pytest.mark.parametrize( + "method, type_name, value, operator", + [ + ("eq", "int", 1, AtlanComparisonOperator.EQ), + ("eq", "boolean", True, AtlanComparisonOperator.EQ), + ("neq", "int", 1, AtlanComparisonOperator.NEQ), + ("neq", "boolean", True, AtlanComparisonOperator.NEQ), + ("gte", "int", 1, AtlanComparisonOperator.GTE), + ("lte", "int", 1, AtlanComparisonOperator.LTE), + ("gt", "int", 1, AtlanComparisonOperator.GT), + ("lt", "int", 1, AtlanComparisonOperator.LT), + ("does_not_contain", "string", "abc", AtlanComparisonOperator.NOT_CONTAINS), + ("contains", "string", "abc", AtlanComparisonOperator.CONTAINS), + ("ends_with", "string", "abc", AtlanComparisonOperator.ENDS_WITH), + ("starts_with", "string", "abc", AtlanComparisonOperator.STARTS_WITH), + ], + ) + def test_comparable_type( + self, + method: str, + type_name: str, + value, + operator: AtlanComparisonOperator, + sut: LineageFilterFieldCM, + custom_metadata_field, + ): + self.configure_custom_metadata_field(custom_metadata_field, type_name=type_name) + + _filter = getattr(sut, method)(value) + + assert _filter.field == sut.field + assert _filter.operator == operator + assert _filter.value == str(value) + + @pytest.mark.parametrize( + "method, query_type, type_name, value", + [ + ("eq", "=", "boolean", 1), + ("eq", "=", "int", True), + ("neq", "!=", "boolean", 1), + ("neq", "!=", "int", True), + ("gt", ">", "boolean", 1), + ("gte", ">=", "boolean", 1), + ("lt", "<", "boolean", 1), + ("lte", "<=", "boolean", 1), + ("does_not_contain", "not_contains", "int", "abc"), + ("contains", "contains", "int", "abc"), + ("ends_with", "endsWith", "int", "abc"), + ("starts_with", "startsWith", "int", "abc"), + ], + ) + def test_non_comparable_type_raises_atlan_error( + self, + method: str, + query_type: str, + type_name: str, + value, + sut: LineageFilterFieldCM, + custom_metadata_field, + ): + self.configure_custom_metadata_field(custom_metadata_field, type_name=type_name) + + with pytest.raises( + AtlanError, + match=f"ATLAN-PYTHON-400-039 Cannot create a {query_type} query on field: something.an_attribute.", + ): + getattr(sut, method)(value) + + @pytest.mark.parametrize( + "method, valid_types", + [ + ("eq", "str, Enum, bool, int, float or date"), + ("neq", "str, Enum, bool, int, float or date"), + ("starts_with", "str"), + ("lt", "int, float or date"), + ("lte", "int, float or date"), + ("gt", "int, float or date"), + ("gte", "int, float or date"), + ], + ) + def test_method_with_wrong_type_raise_atlan_error( + self, method, valid_types, sut: LineageFilterFieldCM + ): + with pytest.raises( + AtlanError, + match="ATLAN-PYTHON-400-048 Invalid parameter type for dict should be " + + valid_types, + ): + getattr(sut, method)({}) + + +class TestLineageFilterFieldNumeric: + @pytest.fixture + def sut(self, searchable_field: SearchableField) -> LineageFilterFieldNumeric: + return LineageFilterFieldNumeric(field=searchable_field) + + def test_init( + self, sut: LineageFilterFieldNumeric, searchable_field: SearchableField + ): + assert sut.field == searchable_field + + @pytest.mark.parametrize( + "method", + ["eq", "neq", "lt", "lte", "gt", "gte"], + ) + def test_method_with_wrong_type_raise_atlan_error( + self, method: str, sut: LineageFilterFieldNumeric + ): + with pytest.raises( + AtlanError, + match="ATLAN-PYTHON-400-048 Invalid parameter type for dict should be int, float or date", + ): + getattr(sut, method)({}) + + @pytest.mark.parametrize( + "method, operator", + [ + ("eq", AtlanComparisonOperator.EQ), + ("neq", AtlanComparisonOperator.NEQ), + ("lt", AtlanComparisonOperator.LT), + ("lte", AtlanComparisonOperator.LTE), + ("gt", AtlanComparisonOperator.GT), + ("gte", AtlanComparisonOperator.GTE), + ], + ) + @pytest.mark.parametrize("value", [1, 1.23, TODAY]) + def test_eq(self, method, operator, value, sut: LineageFilterFieldBoolean): + _filter = getattr(sut, method)(value) + assert _filter.field == sut.field + assert _filter.operator == operator + assert _filter.value == str(value) + + +class TestLineageFilterFieldString: + @pytest.fixture + def sut(self, searchable_field: SearchableField) -> LineageFilterFieldString: + return LineageFilterFieldString(field=searchable_field) + + def test_init( + self, sut: LineageFilterFieldString, searchable_field: SearchableField + ): + assert sut.field == searchable_field + + @pytest.mark.parametrize( + "method", + [ + "eq", + "neq", + "starts_with", + "ends_with", + "contains", + "does_not_contain", + ], + ) + def test_method_with_wrong_type_raise_atlan_error( + self, method: str, sut: LineageFilterFieldNumeric + ): + with pytest.raises( + AtlanError, + match="ATLAN-PYTHON-400-048 Invalid parameter type for dict should be int, float or date", + ): + getattr(sut, method)({}) + + @pytest.mark.parametrize( + "method, operator", + [ + ("eq", AtlanComparisonOperator.EQ), + ("neq", AtlanComparisonOperator.NEQ), + ("starts_with", AtlanComparisonOperator.STARTS_WITH), + ("ends_with", AtlanComparisonOperator.ENDS_WITH), + ("contains", AtlanComparisonOperator.CONTAINS), + ("does_not_contain", AtlanComparisonOperator.NOT_CONTAINS), + ], + ) + @pytest.mark.parametrize( + "value, expected", [("abc", "abc"), (FileType.CSV, FileType.CSV.value)] + ) + def test_eq( + self, method, operator, value, expected, sut: LineageFilterFieldBoolean + ): + _filter = getattr(sut, method)(value) + assert _filter.field == sut.field + assert _filter.operator == operator + assert _filter.value == expected + + +GOOD_DEPTH = 1 +GOOD_EXCLUDE_MEANINGS = False +GOOD_EXCLUDE_CLASSIFICATIONS = True +GOOD_INCLUDES_IN_RESULTS: List[AtlanField] = [] +GOOD_INCLUDES_ON_RESULTS: List[LineageFilter] = [] +GOOD_INCLUDES_ON_RELATIONS: List[AtlanField] = [] +GOOD_WHERE_ASSETS: List[LineageFilter] = [] +GOOD_WHERE_RELATIONSHIPS: List[LineageFilter] = [] +BAD_STRING = True +GOOD_GUID = "123" +GOOD_SIZE = 10 +GOOD_DIRECTION = LineageDirection.DOWNSTREAM +BAD_INT = 12.3 +BAD_LINEAGE_DIRECTION = "abc" +BAD_BOOL = "True" +BAD_LINEAGE_FILTER_LIST = [{"field": "1", "operator": "eq", "value": "1"}] + + +class TestFluentLineage: + @pytest.fixture() + def sut(self) -> FluentLineage: + return FluentLineage(starting_guid=GOOD_GUID) + + @pytest.mark.parametrize( + "starting_guid, depth, direction, size, exclude_meanings, exclude_atlan_tags, includes_in_results, " + "includes_on_results, includes_on_relations, where_assets, where_relationships, message", + [ + ( + None, + GOOD_DEPTH, + GOOD_DIRECTION, + GOOD_SIZE, + GOOD_EXCLUDE_MEANINGS, + GOOD_EXCLUDE_CLASSIFICATIONS, + GOOD_INCLUDES_IN_RESULTS, + GOOD_INCLUDES_ON_RESULTS, + GOOD_INCLUDES_ON_RELATIONS, + GOOD_WHERE_ASSETS, + GOOD_WHERE_RELATIONSHIPS, + r"1 validation error for Init\nstarting_guid\n none is not an allowed value", + ), + ( + BAD_STRING, + GOOD_DEPTH, + GOOD_DIRECTION, + GOOD_SIZE, + GOOD_EXCLUDE_MEANINGS, + GOOD_EXCLUDE_CLASSIFICATIONS, + GOOD_INCLUDES_IN_RESULTS, + GOOD_INCLUDES_ON_RESULTS, + GOOD_INCLUDES_ON_RELATIONS, + GOOD_WHERE_ASSETS, + GOOD_WHERE_RELATIONSHIPS, + r"1 validation error for Init\nstarting_guid\n str type expected", + ), + ( + GOOD_GUID, + BAD_INT, + GOOD_DIRECTION, + GOOD_SIZE, + GOOD_EXCLUDE_MEANINGS, + GOOD_EXCLUDE_CLASSIFICATIONS, + GOOD_INCLUDES_IN_RESULTS, + GOOD_INCLUDES_ON_RESULTS, + GOOD_INCLUDES_ON_RELATIONS, + GOOD_WHERE_ASSETS, + GOOD_WHERE_RELATIONSHIPS, + # v9 uses plain `int` → "value is not a valid int" + r"1 validation error for Init\ndepth\n value is not a valid int", + ), + ( + GOOD_GUID, + GOOD_DEPTH, + BAD_LINEAGE_DIRECTION, + GOOD_SIZE, + GOOD_EXCLUDE_MEANINGS, + GOOD_EXCLUDE_CLASSIFICATIONS, + GOOD_INCLUDES_IN_RESULTS, + GOOD_INCLUDES_ON_RESULTS, + GOOD_INCLUDES_ON_RELATIONS, + GOOD_WHERE_ASSETS, + GOOD_WHERE_RELATIONSHIPS, + r"1 validation error for Init\ndirection\n value is not a valid enumeration member; permitted:", + ), + ( + GOOD_GUID, + GOOD_DEPTH, + LineageDirection.DOWNSTREAM, + BAD_INT, + GOOD_EXCLUDE_MEANINGS, + GOOD_EXCLUDE_CLASSIFICATIONS, + GOOD_INCLUDES_IN_RESULTS, + GOOD_INCLUDES_ON_RESULTS, + GOOD_INCLUDES_ON_RELATIONS, + GOOD_WHERE_ASSETS, + GOOD_WHERE_RELATIONSHIPS, + # v9 uses plain `int` → "value is not a valid int" + r"1 validation error for Init\nsize\n value is not a valid int", + ), + ( + GOOD_GUID, + GOOD_DEPTH, + LineageDirection.DOWNSTREAM, + GOOD_SIZE, + BAD_BOOL, + GOOD_EXCLUDE_CLASSIFICATIONS, + GOOD_INCLUDES_IN_RESULTS, + GOOD_INCLUDES_ON_RESULTS, + GOOD_INCLUDES_ON_RELATIONS, + GOOD_WHERE_ASSETS, + GOOD_WHERE_RELATIONSHIPS, + r"1 validation error for Init\nexclude_meanings\n value is not a valid boolean", + ), + ( + GOOD_GUID, + GOOD_DEPTH, + LineageDirection.DOWNSTREAM, + GOOD_SIZE, + GOOD_EXCLUDE_MEANINGS, + BAD_BOOL, + GOOD_INCLUDES_IN_RESULTS, + GOOD_INCLUDES_ON_RESULTS, + GOOD_INCLUDES_ON_RELATIONS, + GOOD_WHERE_ASSETS, + GOOD_WHERE_RELATIONSHIPS, + r"1 validation error for Init\nexclude_atlan_tags\n value is not a valid boolean", + ), + ( + GOOD_GUID, + GOOD_DEPTH, + LineageDirection.DOWNSTREAM, + GOOD_SIZE, + GOOD_EXCLUDE_MEANINGS, + GOOD_EXCLUDE_CLASSIFICATIONS, + BAD_LINEAGE_FILTER_LIST, + GOOD_INCLUDES_ON_RESULTS, + GOOD_INCLUDES_ON_RELATIONS, + GOOD_WHERE_ASSETS, + GOOD_WHERE_RELATIONSHIPS, + r"1 validation error for Init\nincludes_in_results", + ), + ( + GOOD_GUID, + GOOD_DEPTH, + LineageDirection.DOWNSTREAM, + GOOD_SIZE, + GOOD_EXCLUDE_MEANINGS, + GOOD_EXCLUDE_CLASSIFICATIONS, + GOOD_INCLUDES_IN_RESULTS, + BAD_LINEAGE_FILTER_LIST, + GOOD_INCLUDES_ON_RELATIONS, + GOOD_WHERE_ASSETS, + GOOD_WHERE_RELATIONSHIPS, + r"1 validation error for Init\nincludes_on_results", + ), + ( + GOOD_GUID, + GOOD_DEPTH, + LineageDirection.DOWNSTREAM, + GOOD_SIZE, + GOOD_EXCLUDE_MEANINGS, + GOOD_EXCLUDE_CLASSIFICATIONS, + GOOD_INCLUDES_IN_RESULTS, + GOOD_INCLUDES_ON_RESULTS, + BAD_LINEAGE_FILTER_LIST, + GOOD_WHERE_ASSETS, + GOOD_WHERE_RELATIONSHIPS, + r"1 validation error for Init\nincludes_on_relations", + ), + ( + GOOD_GUID, + GOOD_DEPTH, + LineageDirection.DOWNSTREAM, + GOOD_SIZE, + GOOD_EXCLUDE_MEANINGS, + GOOD_EXCLUDE_CLASSIFICATIONS, + GOOD_INCLUDES_IN_RESULTS, + GOOD_INCLUDES_ON_RESULTS, + GOOD_INCLUDES_ON_RELATIONS, + BAD_LINEAGE_FILTER_LIST, + GOOD_WHERE_RELATIONSHIPS, + r"1 validation error for Init\nwhere_assets", + ), + ( + GOOD_GUID, + GOOD_DEPTH, + LineageDirection.DOWNSTREAM, + GOOD_SIZE, + GOOD_EXCLUDE_MEANINGS, + GOOD_EXCLUDE_CLASSIFICATIONS, + GOOD_INCLUDES_IN_RESULTS, + GOOD_INCLUDES_ON_RESULTS, + GOOD_INCLUDES_ON_RELATIONS, + GOOD_WHERE_ASSETS, + BAD_LINEAGE_FILTER_LIST, + r"1 validation error for Init\nwhere_relationships", + ), + ], + ) + def test_init_with_bad_parameters_raise_value_error( + self, + starting_guid, + depth, + direction, + size, + exclude_meanings, + exclude_atlan_tags, + includes_in_results, + includes_on_results, + includes_on_relations, + where_assets, + where_relationships, + message, + ): + with pytest.raises(ValueError, match=message): + FluentLineage( + starting_guid=starting_guid, + depth=depth, + direction=direction, + size=size, + exclude_meanings=exclude_meanings, + exclude_atlan_tags=exclude_atlan_tags, + includes_on_results=includes_on_results, + includes_in_results=includes_in_results, + includes_on_relations=includes_on_relations, + where_assets=where_assets, + where_relationships=where_relationships, + ) + + @pytest.mark.parametrize( + "starting_guid, depth, direction, size, exclude_meanings, exclude_atlan_tags, includes_in_results, " + "includes_on_results, includes_on_relations, where_assets, where_relationships", + [ + ( + GOOD_GUID, + GOOD_DEPTH, + LineageDirection.DOWNSTREAM, + GOOD_SIZE, + GOOD_EXCLUDE_MEANINGS, + GOOD_EXCLUDE_CLASSIFICATIONS, + GOOD_INCLUDES_IN_RESULTS, + GOOD_INCLUDES_ON_RESULTS, + ["some_field", "another_field"], + GOOD_WHERE_ASSETS, + GOOD_WHERE_RELATIONSHIPS, + ), + ( + GOOD_GUID, + GOOD_DEPTH, + LineageDirection.DOWNSTREAM, + GOOD_SIZE, + GOOD_EXCLUDE_MEANINGS, + GOOD_EXCLUDE_CLASSIFICATIONS, + GOOD_INCLUDES_IN_RESULTS, + GOOD_INCLUDES_ON_RESULTS, + [Asset.DESCRIPTION, Asset.OWNER_GROUPS], + GOOD_WHERE_ASSETS, + GOOD_WHERE_RELATIONSHIPS, + ), + ( + GOOD_GUID, + GOOD_DEPTH, + LineageDirection.DOWNSTREAM, + GOOD_SIZE, + GOOD_EXCLUDE_MEANINGS, + GOOD_EXCLUDE_CLASSIFICATIONS, + GOOD_INCLUDES_IN_RESULTS, + GOOD_INCLUDES_ON_RESULTS, + "some_field", + GOOD_WHERE_ASSETS, + GOOD_WHERE_RELATIONSHIPS, + ), + ( + GOOD_GUID, + GOOD_DEPTH, + LineageDirection.DOWNSTREAM, + GOOD_SIZE, + GOOD_EXCLUDE_MEANINGS, + GOOD_EXCLUDE_CLASSIFICATIONS, + GOOD_INCLUDES_IN_RESULTS, + GOOD_INCLUDES_ON_RESULTS, + Asset.NAME, + GOOD_WHERE_ASSETS, + GOOD_WHERE_RELATIONSHIPS, + ), + ], + ) + def test_init_and_request_for_includes_on_relations( + self, + starting_guid, + depth, + direction, + size, + exclude_meanings, + exclude_atlan_tags, + includes_in_results, + includes_on_results, + includes_on_relations, + where_assets, + where_relationships, + ): + fl = FluentLineage( + starting_guid=starting_guid, + depth=depth, + direction=direction, + size=size, + exclude_meanings=exclude_meanings, + exclude_atlan_tags=exclude_atlan_tags, + includes_on_results=includes_on_results, + includes_in_results=includes_in_results, + includes_on_relations=includes_on_relations, + where_assets=where_assets, + where_relationships=where_relationships, + ) + assert isinstance(fl.request, LineageListRequest) + if isinstance(includes_on_relations, str) or isinstance( + includes_on_relations, AtlanField + ): + assert ( + len(fl.request.relation_attributes) == [includes_on_relations].__len__() + ) + else: + assert len(fl.request.relation_attributes) == len(includes_on_relations) + + def test_request_with_defaults(self, sut: FluentLineage): + request = sut.request + + assert request.guid == GOOD_GUID + assert request.size == 10 + assert request.depth == 1000000 + assert request.direction == LineageDirection.DOWNSTREAM + assert request.exclude_meanings is True + assert request.exclude_classifications is True + assert request.entity_filters is None + assert request.entity_traversal_filters is None + assert request.relationship_traversal_filters is None + + @pytest.mark.parametrize( + "starting_guid, depth, direction, size, exclude_meanings, exclude_atlan_tags, includes_on_results, " + "includes_in_results, where_assets, where_relationships", + [ + ( + GOOD_GUID, + GOOD_DEPTH, + GOOD_DIRECTION, + GOOD_SIZE, + GOOD_EXCLUDE_MEANINGS, + GOOD_EXCLUDE_CLASSIFICATIONS, + [Asset.NAME], + [Asset.CERTIFICATE_STATUS.in_lineage.eq(CertificateStatus.DRAFT)], + [Asset.STATUS.in_lineage.eq(EntityStatus.ACTIVE)], + [Asset.STATUS.in_lineage.eq(EntityStatus.DELETED)], + ), + ], + ) + def test_request( + self, + starting_guid, + depth, + direction, + size, + exclude_meanings, + exclude_atlan_tags, + includes_on_results, + includes_in_results, + where_assets, + where_relationships, + ): + request = FluentLineage( + starting_guid=starting_guid, + depth=depth, + direction=direction, + size=size, + exclude_meanings=exclude_meanings, + exclude_atlan_tags=exclude_atlan_tags, + includes_on_results=includes_on_results, + includes_in_results=includes_in_results, + where_assets=where_assets, + where_relationships=where_relationships, + ).request + + assert request.guid == GOOD_GUID + assert request.size == size + assert request.depth == depth + assert request.direction == direction + assert request.exclude_meanings == exclude_meanings + assert request.exclude_classifications == exclude_atlan_tags + assert request.attributes == [ + field.atlan_field_name for field in includes_on_results + ] + self.validate_filter( + filter_=request.entity_filters, + filter_condition=FilterList.Condition.AND, + results=includes_in_results, + ) + self.validate_filter( + filter_=request.entity_traversal_filters, + filter_condition=FilterList.Condition.AND, + results=where_assets, + ) + self.validate_filter( + filter_=request.relationship_traversal_filters, + filter_condition=FilterList.Condition.AND, + results=where_relationships, + ) + request = FluentLineage( + starting_guid=starting_guid, + depth=depth, + direction=direction, + size=size, + exclude_meanings=exclude_meanings, + exclude_atlan_tags=exclude_atlan_tags, + includes_on_results=includes_on_results, + includes_in_results=includes_in_results, + includes_condition=FilterList.Condition.OR, + where_assets=where_assets, + assets_condition=FilterList.Condition.OR, + where_relationships=where_relationships, + relationships_condition=FilterList.Condition.OR, + ).request + + assert request.guid == GOOD_GUID + assert request.size == size + assert request.depth == depth + assert request.direction == direction + assert request.exclude_meanings == exclude_meanings + assert request.exclude_classifications == exclude_atlan_tags + assert request.attributes == [ + field.atlan_field_name for field in includes_on_results + ] + self.validate_filter( + filter_=request.entity_filters, + filter_condition=FilterList.Condition.OR, + results=includes_in_results, + ) + self.validate_filter( + filter_=request.entity_traversal_filters, + filter_condition=FilterList.Condition.OR, + results=where_assets, + ) + self.validate_filter( + filter_=request.relationship_traversal_filters, + filter_condition=FilterList.Condition.OR, + results=where_relationships, + ) + + @staticmethod + def validate_filter(filter_, filter_condition, results): + assert filter_.condition == filter_condition + assert len(filter_.criteria) == len(results) + for entity_filter, include_in in zip(filter_.criteria, results): + assert entity_filter.attribute_name == include_in.field.internal_field_name + assert entity_filter.operator == include_in.operator + assert entity_filter.attribute_value == include_in.value + + @pytest.mark.parametrize( + "method, value, message", + [ + ( + "depth", + False, + r"ATLAN-PYTHON-400-048 Invalid parameter type for depth should be int", + ), + ( + "direction", + False, + r"ATLAN-PYTHON-400-048 Invalid parameter type for direction should be LineageDirection", + ), + ( + "size", + False, + r"ATLAN-PYTHON-400-048 Invalid parameter type for size should be int", + ), + ( + "exclude_atlan_tags", + 1, + r"ATLAN-PYTHON-400-048 Invalid parameter type for exclude_atlan_tags should be bool", + ), + ( + "exclude_meanings", + 1, + r"ATLAN-PYTHON-400-048 Invalid parameter type for exclude_meanings should be bool", + ), + ( + "include_on_results", + 1, + r"ATLAN-PYTHON-400-048 Invalid parameter type for field should be str or AtlanField", + ), + ( + "include_in_results", + 1, + r"ATLAN-PYTHON-400-048 Invalid parameter type for lineage_filter should be LineageFilter. " + r"Suggestion: Check that you have used the correct type of parameter.", + ), + ( + "where_assets", + 1, + r"ATLAN-PYTHON-400-048 Invalid parameter type for lineage_filter should be LineageFilter. " + r"Suggestion: Check that you have used the correct type of parameter.", + ), + ( + "where_relationships", + 1, + r"ATLAN-PYTHON-400-048 Invalid parameter type for lineage_filter should be LineageFilter. " + r"Suggestion: Check that you have used the correct type of parameter.", + ), + ], + ) + def test_methods_with_invalid_parameter_raises_invalid_request_error( + self, method, value, message, sut: FluentLineage + ): + with pytest.raises(InvalidRequestError, match=message): + getattr(sut, method)(value) + + @pytest.mark.parametrize( + "method, value", + [ + ("depth", 12), + ("direction", LineageDirection.BOTH), + ("size", 12), + ("exclude_atlan_tags", True), + ("exclude_meanings", True), + ], + ) + def test_method_with_valid_parameter(self, method, value, sut: FluentLineage): + lineage = getattr(sut, method)(value) + + assert lineage is not sut + assert getattr(lineage, f"_{method}") == value + + @pytest.mark.parametrize( + "method, value, internal_name", + [ + ("include_on_results", Asset.NAME, "_includes_on_results"), + ( + "include_in_results", + Asset.STATUS.in_lineage.eq(EntityStatus.ACTIVE), + "_includes_in_results", + ), + ( + "where_assets", + Asset.STATUS.in_lineage.eq(EntityStatus.ACTIVE), + "_where_assets", + ), + ( + "where_relationships", + Asset.STATUS.in_lineage.eq(EntityStatus.ACTIVE), + "_where_relationships", + ), + ], + ) + def test_method_adds_to_list_valid_parameter( + self, method, value, internal_name, sut: FluentLineage + ): + lineage = getattr(sut, method)(value) + + assert lineage is not sut + assert value in getattr(lineage, internal_name) diff --git a/tests_v9/unit/test_model.py b/tests_v9/unit/test_model.py new file mode 100644 index 000000000..1884fb31d --- /dev/null +++ b/tests_v9/unit/test_model.py @@ -0,0 +1,199 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2022 Atlan Pte. Ltd. + +""" +Unit tests for v9 models — ported from tests/unit/test_model.py. + +Tests the v9 msgspec.Struct model layer: remove_* methods on all entity +subclasses, field get/set, create_for_modification, ref_by_qualified_name, +and model deserialization. + +The legacy test uses Pydantic ``Asset.Attributes`` inner classes and property +delegation. The v9 test uses direct field access on msgspec.Struct asset +classes (no inner Attributes class pattern). +""" + +from pathlib import Path +from re import escape + +import pytest + +from pyatlan.utils import validate_single_required_field +from pyatlan_v9.errors import InvalidRequestError +from pyatlan_v9.model.assets.asset import Asset +from pyatlan_v9.model.assets.readme import Readme +from pyatlan_v9.model.assets.table import Table +from pyatlan_v9.model.enums import AnnouncementType, CertificateStatus + +SCHEMA_QUALIFIED_NAME = "default/snowflake/1646836521/ATLAN_SAMPLE_DATA/FOOD_BEVERAGE" +TABLE_NAME = "MKT_EXPENSES" + +DATA_DIR = Path(__file__).parent / ".." / ".." / "tests" / "unit" / "data" + + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + + +def get_all_subclasses(cls): + all_subclasses = [] + for subclass in cls.__subclasses__(): + all_subclasses.append(subclass) + all_subclasses.extend(get_all_subclasses(subclass)) + return all_subclasses + + +def _entity_classes(): + """Return all instantiable v9 entity classes (not Attributes/Nested/Relationship).""" + return [ + c + for c in get_all_subclasses(Asset) + if not any(x in c.__name__ for x in ["Attributes", "Nested", "Relationship"]) + ] + + +# --------------------------------------------------------------------------- +# Fixtures +# --------------------------------------------------------------------------- + + +@pytest.fixture() +def table(): + return Table.creator( + name=TABLE_NAME, + schema_qualified_name=SCHEMA_QUALIFIED_NAME, + ) + + +# --------------------------------------------------------------------------- +# test_remove_methods — equivalent to legacy test_remove_desscription +# +# Tests that remove_* methods correctly set the relevant fields to None +# on all v9 entity subclasses. In legacy, this tests on Asset.Attributes +# inner classes; in v9, it tests directly on the entity classes. +# --------------------------------------------------------------------------- + + +@pytest.mark.parametrize( + "clazz, method_name, property_names, values", + [ + (clazz, info[1], info[2:], info[0]) + for clazz in _entity_classes() + for info in [ + (["abc"], "remove_description", "description"), + (["abc"], "remove_user_description", "user_description"), + ([["bob"], ["dave"]], "remove_owners", "owner_groups", "owner_users"), + ( + [CertificateStatus.DRAFT, "some message"], + "remove_certificate", + "certificate_status", + "certificate_status_message", + ), + ( + ["a message", "a title", AnnouncementType.ISSUE], + "remove_announcement", + "announcement_message", + "announcement_title", + "announcement_type", + ), + ] + ], +) +def test_remove_methods(clazz, method_name, property_names, values): + """Test remove_* methods set all associated fields to None.""" + instance = clazz() + for prop, value in zip(property_names, values): + setattr(instance, prop, value) + getattr(instance, method_name)() + for prop in property_names: + assert getattr(instance, prop) is None + + +# --------------------------------------------------------------------------- +# test_field_set_get — equivalent to legacy test_attributes +# +# Tests that key common fields can be set and read back on all v9 entity +# subclasses. In v9, fields are direct Struct attributes (no Attributes +# inner class delegation). +# --------------------------------------------------------------------------- + +_FIELD_VALUES = [ + ("name", "test-name"), + ("description", "test-description"), + ("user_description", "test-user-description"), + ("certificate_status", CertificateStatus.VERIFIED), + ("announcement_type", AnnouncementType.WARNING), +] + + +@pytest.mark.parametrize( + "clazz, field_name, value", + [ + (clazz, field_name, value) + for clazz in _entity_classes() + for field_name, value in _FIELD_VALUES + ], +) +def test_field_set_get(clazz, field_name, value): + """Test that common fields can be set and read back on all entity types.""" + instance = clazz() + setattr(instance, field_name, value) + assert getattr(instance, field_name) == value + + +# --------------------------------------------------------------------------- +# Standalone tests — ported directly from legacy +# --------------------------------------------------------------------------- + + +@pytest.mark.parametrize( + "names, values, message", + [ + ( + ("one", "two"), + (None, None), + "One of the following parameters are required: one, two", + ), + ( + ("one", "two"), + (1, 2), + "Only one of the following parameters are allowed: one, two", + ), + ( + ("one", "two", "three"), + (1, None, 3), + "Only one of the following parameters are allowed: one, three", + ), + ], +) +def test_validate_single_required_field_with_bad_values_raises_value_error( + names, values, message +): + with pytest.raises(ValueError, match=message): + validate_single_required_field(names, values) + + +def test_validate_single_required_field_with_only_one_field_does_not_raise_value_error(): + validate_single_required_field(["One", "Two", "Three"], [None, None, 3]) + + +def test_create_for_modification_on_asset_raises_exception(): + with pytest.raises( + (InvalidRequestError, ValueError), + ): + Asset.create_for_modification(qualified_name="", name="") + + +def test_readme_creator_asset_guid_validation(): + with pytest.raises( + ValueError, + match=escape( + "asset guid must be present, use the client.asset.ref_by_guid() method to retrieve an asset by its GUID" + ), + ): + Readme.creator( + asset=Asset.ref_by_qualified_name("test-qn"), + content="

Test Content

", + asset_name="test-readme", + ) diff --git a/tests_v9/unit/test_oauth_client.py b/tests_v9/unit/test_oauth_client.py new file mode 100644 index 000000000..b2438e273 --- /dev/null +++ b/tests_v9/unit/test_oauth_client.py @@ -0,0 +1,710 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. +""" +Comprehensive Sync OAuth Client Tests — ported from tests/unit/test_oauth_client.py. + +Tests for OAuth authentication in the synchronous AtlanClient: +- Authentication method precedence +- Environment variable handling +- Token lifecycle (fetch, cache, refresh, expiry) +- Error handling and edge cases +- Thread safety +- Resource cleanup +- URL construction + +This test is model-free (no Pydantic/msgspec model differences). +""" + +import os +import threading +import time +from unittest.mock import Mock, patch +from urllib.parse import urlparse + +import httpx +import pytest + +from pyatlan.client.oauth import OAuthTokenManager +from pyatlan_v9.client.atlan import AtlanClient + + +@pytest.fixture +def clear_env_vars(): + """Clear OAuth-related environment variables before each test""" + env_vars = [ + "ATLAN_BASE_URL", + "ATLAN_API_KEY", + "ATLAN_OAUTH_CLIENT_ID", + "ATLAN_OAUTH_CLIENT_SECRET", + ] + original_values = {} + for var in env_vars: + original_values[var] = os.environ.get(var) + if var in os.environ: + del os.environ[var] + + yield + + for var, value in original_values.items(): + if value is not None: + os.environ[var] = value + elif var in os.environ: + del os.environ[var] + + +@pytest.fixture +def mock_oauth_response(): + """Mock successful OAuth token response with camelCase""" + return { + "accessToken": "test-access-token-12345", + "tokenType": "Bearer", + "expiresIn": 600, + } + + +@pytest.fixture +def mock_oauth_response_snake_case(): + """Mock successful OAuth token response with snake_case""" + return { + "access_token": "test-access-token-67890", + "token_type": "Bearer", + "expires_in": 600, + } + + +class TestOAuthTokenManagerInit: + """Test OAuth token manager initialization""" + + def test_init_with_external_url(self, clear_env_vars): + """Initialize with external URL""" + manager = OAuthTokenManager( + base_url="https://test.atlan.com", + client_id="test-client-id", + client_secret="test-client-secret", + ) + + assert manager.base_url == "https://test.atlan.com" + assert manager.client_id == "test-client-id" + assert manager.client_secret == "test-client-secret" + assert ( + manager.token_url + == "https://test.atlan.com/api/service/oauth-clients/token" + ) + assert manager._token is None + assert manager._owns_client is True + + manager.close() + + def test_init_with_internal_url(self, clear_env_vars): + """Initialize with INTERNAL mode""" + manager = OAuthTokenManager( + base_url="INTERNAL", + client_id="test-client-id", + client_secret="test-client-secret", + ) + + assert manager.base_url == "INTERNAL" + expected_url = ( + "http://heracles-service.heracles.svc.cluster.local/oauth-clients/token" + ) + assert manager.token_url == expected_url + + manager.close() + + def test_init_with_external_http_client(self, clear_env_vars): + """Initialize with externally provided HTTP client""" + external_client = httpx.Client() + + manager = OAuthTokenManager( + base_url="https://test.atlan.com", + client_id="test-client-id", + client_secret="test-client-secret", + http_client=external_client, + ) + + assert manager._http_client is external_client + assert manager._owns_client is False + + manager.close() + assert not external_client.is_closed + external_client.close() + + def test_init_creates_http_client_when_not_provided(self, clear_env_vars): + """Initialize without HTTP client should create one""" + manager = OAuthTokenManager( + base_url="https://test.atlan.com", + client_id="test-client-id", + client_secret="test-client-secret", + ) + + assert manager._http_client is not None + assert isinstance(manager._http_client, httpx.Client) + assert manager._owns_client is True + + manager.close() + + +class TestTokenFetchingAndCaching: + """Test token fetching and caching behavior""" + + @patch("httpx.Client.post") + def test_first_token_fetch(self, mock_post, clear_env_vars, mock_oauth_response): + """First call should fetch token from API""" + mock_response = Mock() + mock_response.json.return_value = mock_oauth_response + mock_response.raise_for_status = Mock() + mock_post.return_value = mock_response + + manager = OAuthTokenManager( + base_url="https://test.atlan.com", + client_id="test-client-id", + client_secret="test-client-secret", + ) + + token = manager.get_token() + + assert token == "test-access-token-12345" + assert mock_post.call_count == 1 + + call_args = mock_post.call_args + assert call_args[1]["json"]["clientId"] == "test-client-id" + assert call_args[1]["json"]["clientSecret"] == "test-client-secret" + assert call_args[1]["headers"]["Content-Type"] == "application/json" + + manager.close() + + @patch("httpx.Client.post") + def test_token_caching(self, mock_post, clear_env_vars, mock_oauth_response): + """Subsequent calls should use cached token""" + mock_response = Mock() + mock_response.json.return_value = mock_oauth_response + mock_response.raise_for_status = Mock() + mock_post.return_value = mock_response + + manager = OAuthTokenManager( + base_url="https://test.atlan.com", + client_id="test-client-id", + client_secret="test-client-secret", + ) + + token1 = manager.get_token() + assert token1 == "test-access-token-12345" + assert mock_post.call_count == 1 + + token2 = manager.get_token() + assert token2 == "test-access-token-12345" + assert mock_post.call_count == 1 + + token3 = manager.get_token() + assert token3 == "test-access-token-12345" + assert mock_post.call_count == 1 + + manager.close() + + @patch("httpx.Client.post") + def test_snake_case_response( + self, mock_post, clear_env_vars, mock_oauth_response_snake_case + ): + """Should handle snake_case field names in response""" + mock_response = Mock() + mock_response.json.return_value = mock_oauth_response_snake_case + mock_response.raise_for_status = Mock() + mock_post.return_value = mock_response + + manager = OAuthTokenManager( + base_url="https://test.atlan.com", + client_id="test-client-id", + client_secret="test-client-secret", + ) + + token = manager.get_token() + assert token == "test-access-token-67890" + + manager.close() + + +class TestTokenExpiryAndRefresh: + """Test token expiry detection and automatic refresh""" + + @patch("httpx.Client.post") + def test_token_refresh_on_expiry(self, mock_post, clear_env_vars): + """Expired token should trigger automatic refresh""" + first_response = Mock() + first_response.json.return_value = { + "accessToken": "token-1", + "tokenType": "Bearer", + "expiresIn": 1, + } + first_response.raise_for_status = Mock() + + second_response = Mock() + second_response.json.return_value = { + "accessToken": "token-2", + "tokenType": "Bearer", + "expiresIn": 600, + } + second_response.raise_for_status = Mock() + + mock_post.side_effect = [first_response, second_response] + + manager = OAuthTokenManager( + base_url="https://test.atlan.com", + client_id="test-client-id", + client_secret="test-client-secret", + ) + + token1 = manager.get_token() + assert token1 == "token-1" + assert mock_post.call_count == 1 + + time.sleep(2) + + token2 = manager.get_token() + assert token2 == "token-2" + assert mock_post.call_count == 2 + + manager.close() + + @patch("httpx.Client.post") + def test_manual_token_invalidation( + self, mock_post, clear_env_vars, mock_oauth_response + ): + """Manual invalidation should force refresh on next call""" + mock_response = Mock() + mock_response.json.return_value = mock_oauth_response + mock_response.raise_for_status = Mock() + mock_post.return_value = mock_response + + manager = OAuthTokenManager( + base_url="https://test.atlan.com", + client_id="test-client-id", + client_secret="test-client-secret", + ) + + manager.get_token() + assert mock_post.call_count == 1 + + manager.invalidate_token() + assert manager._token is None + + manager.get_token() + assert mock_post.call_count == 2 + + manager.close() + + +class TestErrorHandling: + """Test error handling in various failure scenarios""" + + @patch("httpx.Client.post") + def test_missing_access_token(self, mock_post, clear_env_vars): + """Missing accessToken should raise ValueError""" + mock_response = Mock() + mock_response.json.return_value = { + "tokenType": "Bearer", + "expiresIn": 600, + } + mock_response.raise_for_status = Mock() + mock_post.return_value = mock_response + + manager = OAuthTokenManager( + base_url="https://test.atlan.com", + client_id="test-client-id", + client_secret="test-client-secret", + ) + + with pytest.raises( + ValueError, match="OAuth token response missing 'accessToken' field" + ): + manager.get_token() + + manager.close() + + @patch("httpx.Client.post") + def test_http_401_error(self, mock_post, clear_env_vars): + """401 error should be propagated""" + mock_response = Mock() + mock_response.raise_for_status.side_effect = httpx.HTTPStatusError( + "401 Unauthorized", + request=Mock(), + response=Mock(status_code=401), + ) + mock_post.return_value = mock_response + + manager = OAuthTokenManager( + base_url="https://test.atlan.com", + client_id="test-client-id", + client_secret="test-client-secret", + ) + + with pytest.raises(httpx.HTTPStatusError): + manager.get_token() + + manager.close() + + @patch("httpx.Client.post") + def test_http_500_error(self, mock_post, clear_env_vars): + """500 error should be propagated""" + mock_response = Mock() + mock_response.raise_for_status.side_effect = httpx.HTTPStatusError( + "500 Internal Server Error", + request=Mock(), + response=Mock(status_code=500), + ) + mock_post.return_value = mock_response + + manager = OAuthTokenManager( + base_url="https://test.atlan.com", + client_id="test-client-id", + client_secret="test-client-secret", + ) + + with pytest.raises(httpx.HTTPStatusError): + manager.get_token() + + manager.close() + + @patch("httpx.Client.post") + def test_network_error(self, mock_post, clear_env_vars): + """Network errors should be propagated""" + mock_post.side_effect = httpx.ConnectError("Connection refused") + + manager = OAuthTokenManager( + base_url="https://test.atlan.com", + client_id="test-client-id", + client_secret="test-client-secret", + ) + + with pytest.raises(httpx.ConnectError): + manager.get_token() + + manager.close() + + @patch("httpx.Client.post") + def test_timeout_error(self, mock_post, clear_env_vars): + """Timeout errors should be propagated""" + mock_post.side_effect = httpx.TimeoutException("Request timeout") + + manager = OAuthTokenManager( + base_url="https://test.atlan.com", + client_id="test-client-id", + client_secret="test-client-secret", + ) + + with pytest.raises(httpx.TimeoutException): + manager.get_token() + + manager.close() + + @patch("httpx.Client.post") + def test_invalid_json_response(self, mock_post, clear_env_vars): + """Invalid JSON in response should raise error""" + mock_response = Mock() + mock_response.json.side_effect = ValueError("Invalid JSON") + mock_response.raise_for_status = Mock() + mock_post.return_value = mock_response + + manager = OAuthTokenManager( + base_url="https://test.atlan.com", + client_id="test-client-id", + client_secret="test-client-secret", + ) + + with pytest.raises(ValueError, match="Invalid JSON"): + manager.get_token() + + manager.close() + + +class TestThreadSafety: + """Test thread safety of OAuth token management""" + + @patch("httpx.Client.post") + def test_concurrent_token_requests( + self, mock_post, clear_env_vars, mock_oauth_response + ): + """Multiple threads requesting token simultaneously should result in single fetch""" + call_count = {"count": 0} + lock = threading.Lock() + + def mock_post_with_delay(*args, **kwargs): + with lock: + call_count["count"] += 1 + time.sleep(0.1) + mock_response = Mock() + mock_response.json.return_value = mock_oauth_response + mock_response.raise_for_status = Mock() + return mock_response + + mock_post.side_effect = mock_post_with_delay + + manager = OAuthTokenManager( + base_url="https://test.atlan.com", + client_id="test-client-id", + client_secret="test-client-secret", + ) + + tokens = [] + + def get_token(): + token = manager.get_token() + tokens.append(token) + + threads = [threading.Thread(target=get_token) for _ in range(10)] + for t in threads: + t.start() + for t in threads: + t.join() + + assert len(tokens) == 10 + assert all(token == tokens[0] for token in tokens) + + assert call_count["count"] <= 2 + + manager.close() + + @patch("httpx.Client.post") + def test_concurrent_invalidation_and_fetch( + self, mock_post, clear_env_vars, mock_oauth_response + ): + """Concurrent invalidation and fetch should be thread-safe""" + mock_response = Mock() + mock_response.json.return_value = mock_oauth_response + mock_response.raise_for_status = Mock() + mock_post.return_value = mock_response + + manager = OAuthTokenManager( + base_url="https://test.atlan.com", + client_id="test-client-id", + client_secret="test-client-secret", + ) + + manager.get_token() + + def invalidate_repeatedly(): + for _ in range(5): + manager.invalidate_token() + time.sleep(0.01) + + def fetch_repeatedly(): + for _ in range(5): + manager.get_token() + time.sleep(0.01) + + threads = [ + threading.Thread(target=invalidate_repeatedly), + threading.Thread(target=fetch_repeatedly), + threading.Thread(target=fetch_repeatedly), + ] + for t in threads: + t.start() + for t in threads: + t.join() + + manager.close() + + +class TestResourceCleanup: + """Test proper cleanup of resources""" + + def test_close_http_client(self, clear_env_vars): + """Close should close owned HTTP client""" + manager = OAuthTokenManager( + base_url="https://test.atlan.com", + client_id="test-client-id", + client_secret="test-client-secret", + ) + + http_client = manager._http_client + assert not http_client.is_closed + + manager.close() + assert http_client.is_closed + + def test_dont_close_external_client(self, clear_env_vars): + """Should not close externally provided HTTP client""" + external_client = httpx.Client() + + manager = OAuthTokenManager( + base_url="https://test.atlan.com", + client_id="test-client-id", + client_secret="test-client-secret", + http_client=external_client, + ) + + manager.close() + + assert not external_client.is_closed + + external_client.close() + + +class TestAtlanClientAuthPrecedence: + """Test authentication method precedence in AtlanClient""" + + def test_api_key_only(self, clear_env_vars): + """API key authentication""" + client = AtlanClient( + base_url="https://test.atlan.com", + api_key="test-api-key", + ) + + assert client.api_key == "test-api-key" + assert client._oauth_token_manager is None + assert "authorization" in client._request_params["headers"] + assert ( + client._request_params["headers"]["authorization"] == "Bearer test-api-key" + ) + + def test_oauth_only(self, clear_env_vars): + """OAuth authentication""" + client = AtlanClient( + base_url="https://test.atlan.com", + oauth_client_id="test-client-id", + oauth_client_secret="test-client-secret", + ) + + assert client.api_key is None + assert client._oauth_token_manager is not None + assert client._oauth_token_manager.client_id == "test-client-id" + assert client._oauth_token_manager.client_secret == "test-client-secret" + + client._oauth_token_manager.close() + + def test_api_key_takes_precedence(self, clear_env_vars): + """API key takes precedence when both provided""" + client = AtlanClient( + base_url="https://test.atlan.com", + api_key="test-api-key", + oauth_client_id="test-client-id", + oauth_client_secret="test-client-secret", + ) + + assert client.api_key == "test-api-key" + assert client._oauth_token_manager is None + assert ( + client._request_params["headers"]["authorization"] == "Bearer test-api-key" + ) + + def test_empty_api_key(self, clear_env_vars): + """Empty API key should not create OAuth manager""" + client = AtlanClient( + base_url="https://test.atlan.com", + api_key="", + oauth_client_id="test-client-id", + oauth_client_secret="test-client-secret", + ) + + assert client.api_key == "" + assert client._oauth_token_manager is None + assert "authorization" not in client._request_params["headers"] + + def test_no_authentication(self, clear_env_vars): + """No authentication provided""" + client = AtlanClient(base_url="https://test.atlan.com") + + assert client.api_key is None + assert client._oauth_token_manager is None + assert "authorization" not in client._request_params["headers"] + + +class TestEnvironmentVariables: + """Test environment variable handling""" + + def test_oauth_from_env(self, clear_env_vars): + """OAuth credentials from environment variables""" + os.environ["ATLAN_BASE_URL"] = "https://env.atlan.com" + os.environ["ATLAN_OAUTH_CLIENT_ID"] = "env-client-id" + os.environ["ATLAN_OAUTH_CLIENT_SECRET"] = "env-client-secret" + + client = AtlanClient() + + assert client._oauth_token_manager is not None + assert client._oauth_token_manager.client_id == "env-client-id" + assert client._oauth_token_manager.client_secret == "env-client-secret" + + client._oauth_token_manager.close() + + def test_explicit_overrides_env(self, clear_env_vars): + """Explicit parameters override environment variables""" + os.environ["ATLAN_BASE_URL"] = "https://env.atlan.com" + os.environ["ATLAN_OAUTH_CLIENT_ID"] = "env-client-id" + os.environ["ATLAN_OAUTH_CLIENT_SECRET"] = "env-client-secret" + + client = AtlanClient( + base_url="https://explicit.atlan.com", + oauth_client_id="explicit-client-id", + oauth_client_secret="explicit-client-secret", + ) + + assert urlparse(str(client.base_url)).hostname == "explicit.atlan.com" + assert client._oauth_token_manager.client_id == "explicit-client-id" + assert client._oauth_token_manager.client_secret == "explicit-client-secret" + + client._oauth_token_manager.close() + + def test_api_key_env_precedence(self, clear_env_vars): + """API key from env takes precedence over OAuth""" + os.environ["ATLAN_BASE_URL"] = "https://test.atlan.com" + os.environ["ATLAN_API_KEY"] = "env-api-key" + os.environ["ATLAN_OAUTH_CLIENT_ID"] = "env-client-id" + os.environ["ATLAN_OAUTH_CLIENT_SECRET"] = "env-client-secret" + + client = AtlanClient() + + assert client.api_key == "env-api-key" + assert client._oauth_token_manager is None + assert ( + client._request_params["headers"]["authorization"] == "Bearer env-api-key" + ) + + def test_partial_oauth_credentials(self, clear_env_vars): + """Partial OAuth credentials should not create manager""" + os.environ["ATLAN_BASE_URL"] = "https://test.atlan.com" + os.environ["ATLAN_OAUTH_CLIENT_ID"] = "env-client-id" + + client = AtlanClient() + + assert client._oauth_token_manager is None + + +class TestEdgeCases: + """Test edge cases and unusual scenarios""" + + @patch("httpx.Client.post") + def test_default_expires_in(self, mock_post, clear_env_vars): + """Missing expiresIn should use default""" + mock_response = Mock() + mock_response.json.return_value = { + "accessToken": "test-token", + "tokenType": "Bearer", + } + mock_response.raise_for_status = Mock() + mock_post.return_value = mock_response + + manager = OAuthTokenManager( + base_url="https://test.atlan.com", + client_id="test-client-id", + client_secret="test-client-secret", + ) + + token = manager.get_token() + assert token == "test-token" + + manager.close() + + def test_url_with_trailing_slash(self, clear_env_vars): + """Base URL with trailing slash""" + manager = OAuthTokenManager( + base_url="https://test.atlan.com/", + client_id="test-client-id", + client_secret="test-client-secret", + ) + + assert "/api/service/oauth-clients/token" in manager.token_url + + manager.close() + + +if __name__ == "__main__": + pytest.main([__file__, "-v", "-s"]) diff --git a/tests_v9/unit/test_packages.py b/tests_v9/unit/test_packages.py new file mode 100644 index 000000000..fa5afb141 --- /dev/null +++ b/tests_v9/unit/test_packages.py @@ -0,0 +1,1816 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +""" +Unit tests for v9 packages — uses msgspec-based workflow & credential models. + +``Asset.CERTIFICATE_STATUS`` / ``Asset.ANNOUNCEMENT_TYPE`` are +ClassVar field definitions (not Pydantic models) so they're imported +from the legacy Asset. +""" + +import json +from json import load, loads +from pathlib import Path +from typing import Any +from unittest.mock import patch + +import pytest + +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.errors import InvalidRequestError +from pyatlan_v9.model.assets import Asset +from pyatlan_v9.model.enums import ( + AssetDeltaHandling, + AssetInputHandling, + AssetRemovalType, +) +from pyatlan_v9.model.packages import ( + APITokenConnectionAdmin, + AssetExportBasic, + AssetImport, + BigQueryCrawler, + ConfluentKafkaCrawler, + ConnectionDelete, + DatabricksCrawler, + DatabricksMiner, + DbtCrawler, + DynamoDBCrawler, + GlueCrawler, + LineageBuilder, + LineageGenerator, + MongoDBCrawler, + OracleCrawler, + PostgresCrawler, + PowerBICrawler, + RelationalAssetsBuilder, + SigmaCrawler, + SnowflakeCrawler, + SnowflakeMiner, + SQLServerCrawler, + TableauCrawler, +) + +PACKAGE_REQUESTS_DIR = ( + Path(__file__).parent / ".." / ".." / "tests" / "unit" / "data" / "package_requests" +) +SNOWFLAKE_BASIC = "snowflake_basic.json" +SNOWFLAKE_KEYPAIR = "snowflake_keypair.json" +SNOWFLAKE_MINER_DEFAULT = "snowflake_miner_default.json" +SNOWFLAKE_MINER_SOURCE = "snowflake_miner_source.json" +SNOWFLAKE_MINER_S3_OFFLINE = "snowflake_miner_s3_offline.json" +GLUE_IAM_USER = "glue_iam_user.json" +TABLEAU_BASIC = "tableau_basic.json" +TABLEAU_ACCESS_TOKEN = "tableau_access_token.json" +TABLEAU_OFFLINE = "tableau_offline.json" +POWERBI_DELEGATED_USER = "powerbi_delegated_user.json" +POWEBI_SERVICE_PRINCIPAL = "powerbi_service_principal.json" +CONFLUENT_KAFKA_DIRECT = "confluent_kafka_direct.json" +DBT_CORE = "dbt_core.json" +DBT_CLOUD = "dbt_cloud.json" +SIGMA_API_TOKEN = "sigma_api_token.json" +SQL_SERVER_BASIC = "sql_server_basic.json" +BIG_QUERY_DIRECT = "big_query_direct.json" +DYNAMO_DB_IAM_USER = "dynamo_db_iam_user.json" +DYNAMO_DB_IAM_USER_ROLE = "dynamo_db_iam_user_role.json" +POSTGRES_DIRECT_BASIC = "postgres_direct_basic.json" +POSTGRES_DIRECT_IAM_USER = "postgres_direct_iam_user.json" +POSTGRES_DIRECT_IAM_ROLE = "postgres_direct_iam_role.json" +POSTGRES_S3_OFFLINE = "postgres_s3_offline.json" +CONNECTION_DELETE_HARD = "connection_delete_hard.json" +CONNECTION_DELETE_SOFT = "connection_delete_soft.json" +ASSET_IMPORT_S3 = "asset_import_s3.json" +ASSET_IMPORT_GCS = "asset_import_gcs.json" +ASSET_EXPORT_BASIC_GLOSSARIES = "asset_export_glossaries.json" +ASSET_IMPORT_ADLS = "asset_import_adls.json" +ASSET_IMPORT_DEFAULT = "asset_import_default.json" +ASSET_EXPORT_BASIC_GLOSSARIES_ONLY_S3 = "asset_export_basic_glossaries_s3.json" +ASSET_EXPORT_BASIC_PRODUCTS_ONLY_S3 = "asset_export_basic_products_s3.json" +ASSET_EXPORT_BASIC_ENRICHED_ONLY_S3 = "asset_export_basic_enriched_s3.json" +ASSET_EXPORT_BASIC_ALL_ASSETS_S3 = "asset_export_basic_all_assets_s3.json" +ASSET_EXPORT_BASIC_GLOSSARIES_ONLY_ADLS = "asset_export_basic_glossaries_adls.json" +ASSET_EXPORT_BASIC_ALL_ASSETS_ADLS = "asset_export_basic_all_assets_adls.json" +ASSET_EXPORT_BASIC_PRODUCTS_ONLY_ADLS = "asset_export_basic_products_adls.json" +ASSET_EXPORT_BASIC_ENRICHED_ONLY_ADLS = "asset_export_basic_enriched_adls.json" +ASSET_EXPORT_BASIC_ALL_ASSETS_GCS = "asset_export_basic_all_assets_gcs.json" +ASSET_EXPORT_BASIC_GLOSSARIES_ONLY_GCS = "asset_export_basic_glossaries_gcs.json" +ASSET_EXPORT_BASIC_PRODUCTS_ONLY_GCS = "asset_export_basic_products_gcs.json" +ASSET_EXPORT_BASIC_ENRICHED_ONLY_GCS = "asset_export_basic_enriched_gcs.json" +RELATIONAL_ASSETS_BUILDER_S3 = "relational_assets_builder_s3.json" +RELATIONAL_ASSETS_BUILDER_ADLS = "relational_assets_builder_adls.json" +RELATIONAL_ASSETS_BUILDER_GCS = "relational_assets_builder_gcs.json" +MONGODB_BASIC = "mongodb_basic.json" +DATABRICKS_BASIC_JDBC = "databricks_basic_jdbc.json" +DATABRICKS_BASIC_REST = "databricks_basic_rest.json" +DATABRICKS_AWS = "databricks_aws.json" +DATABRICKS_AZURE = "databricks_azure.json" +DATABRICKS_OFFLINE = "databricks_offline.json" +DATABRICKS_MINER_REST = "databricks_miner_rest.json" +DATABRICKS_MINER_OFFLINE = "databricks_miner_offline.json" +DATABRICKS_MINER_SYSTEM_TABLE = "databricks_miner_system_table.json" +DATABRICKS_MINER_POPULARITY_REST = "databricks_miner_popularity_rest.json" +DATABRICKS_MINER_POPULARITY_SYSTEM_TABLE = ( + "databricks_miner_popularity_system_table.json" +) +DATABRICKS_SYSTEM_TABLES = "databricks_system_tables.json" +ORACLE_CRAWLER_BASIC = "oracle_crawler_basic.json" +ORACLE_CRAWLER_OFFLINE = "oracle_crawler_offline.json" +ORACLE_CRAWLER_BASIC_AGENT = "oracle_crawler_basic_agent.json" +ORACLE_CRAWLER_KERBEROS_AGENT = "oracle_crawler_kerberos_agent.json" +LINEAGE_BUILDER_S3 = "lineage_builder_s3.json" +LINEAGE_BUILDER_GCS = "lineage_builder_gcs.json" +LINEAGE_BUILDER_ADLS = "lineage_builder_adls.json" +LINEAGE_GENERATOR_DEFAULT = "lineage_generator_default.json" +LINEAGE_GENERATOR_FULL = "lineage_generator_full.json" +API_TOKEN_CONNECTION_ADMIN = "api_token_connection_admin.json" + + +class NonSerializable: + pass + + +INVALID_REQ_ERROR = "ATLAN-PYTHON-400-014 Unable to translate the provided include/exclude asset filters into JSON" + + +def _normalize_json_value(value: Any) -> Any: + """Recursively normalize a value — if a string is valid JSON, parse it.""" + if isinstance(value, str): + try: + parsed = json.loads(value) + return _normalize_json_value(parsed) + except (json.JSONDecodeError, ValueError): + return value + elif isinstance(value, dict): + return {k: _normalize_json_value(v) for k, v in value.items()} + elif isinstance(value, list): + return [_normalize_json_value(item) for item in value] + return value + + +def assert_workflow_equal(actual: dict, expected: dict) -> None: + """ + Compare two workflow dicts with deep JSON-string normalization. + + This handles the case where embedded JSON strings (e.g. the ``connection`` + parameter value) have different key ordering between msgspec and Pydantic + serialization, which is semantically identical. + """ + assert _normalize_json_value(actual) == _normalize_json_value(expected) + + +def load_json(filename): + with (PACKAGE_REQUESTS_DIR / filename).open() as input_file: + return load(input_file) + + +@pytest.fixture() +def mock_get_epoch_timestamp(): + with patch("pyatlan.utils.get_epoch_timestamp") as mock_datetime: + mock_datetime.return_value = 123456.123456 + yield mock_datetime + + +@pytest.fixture() +def mock_connection_guid(): + with ( + patch("pyatlan.utils.random") as mock_random, + patch("pyatlan_v9.utils.random") as mock_random_v9, + ): + mock_random.random.return_value = 123456789 + mock_random_v9.random.return_value = 123456789 + yield mock_random + + +@pytest.fixture(autouse=True) +def set_env(monkeypatch): + monkeypatch.setenv("ATLAN_BASE_URL", "https://test.atlan.com") + monkeypatch.setenv("ATLAN_API_KEY", "test-api-key") + + +@pytest.fixture() +def client(): + return AtlanClient() + + +@pytest.fixture() +def mock_package_env( + mock_role_cache, + mock_user_cache, + mock_group_cache, + mock_connection_guid, + mock_get_epoch_timestamp, +): + mock_role_cache.validate_idstrs + mock_user_cache.validate_names + mock_group_cache.validate_aliases + + +def test_snowflake_package(mock_package_env, client: AtlanClient): + snowflake_with_connection_default = ( + SnowflakeCrawler( + client=client, + connection_name="test-snowflake-basic-conn", + admin_roles=["admin-guid-1234"], + ) + .information_schema(hostname="test-hostname") + .basic_auth( + username="test-user", + password="test-pass", + role="test-role", + warehouse="test-warehouse", + ) + .include(assets={"test-include": ["test-asset-1", "test-asset-2"]}) + .exclude(assets={}) + .lineage(True) + .tags(True) + .to_workflow() + ) + request_json = loads(snowflake_with_connection_default.to_json()) + assert_workflow_equal(request_json, load_json(SNOWFLAKE_BASIC)) + + snowflake_basic_auth = ( + SnowflakeCrawler( + client=client, + connection_name="test-snowflake-basic-conn", + admin_roles=["admin-guid-1234"], + admin_groups=None, + admin_users=None, + ) + .information_schema(hostname="test-hostname") + .basic_auth( + username="test-user", + password="test-pass", + role="test-role", + warehouse="test-warehouse", + ) + .include(assets={"test-include": ["test-asset-1", "test-asset-2"]}) + .exclude(assets={}) + .lineage(True) + .tags(True) + .to_workflow() + ) + request_json = loads(snowflake_basic_auth.to_json()) + assert_workflow_equal(request_json, load_json(SNOWFLAKE_BASIC)) + + snowflake_keypair_auth = ( + SnowflakeCrawler( + client=client, + connection_name="test-snowflake-keypair-conn", + admin_roles=["admin-guid-1234"], + admin_groups=None, + admin_users=None, + ) + .account_usage( + hostname="test-hostname", database_name="test-db", schema_name="test-schema" + ) + .keypair_auth( + username="test-user", + private_key="test-key", + private_key_password="test-key-pass", + role="test-role", + warehouse="test-warehouse", + ) + .include(assets={"test-include": ["test-asset-1", "test-asset-2"]}) + .exclude(assets={}) + .lineage(True) + .tags(False) + .to_workflow() + ) + request_json = loads(snowflake_keypair_auth.to_json()) + assert_workflow_equal(request_json, load_json(SNOWFLAKE_KEYPAIR)) + + +def test_glue_package(mock_package_env, client: AtlanClient): + glue_iam_user_auth = ( + GlueCrawler( + client=client, + connection_name="test-glue-conn", + admin_roles=["admin-guid-1234"], + admin_groups=None, + admin_users=None, + ) + .iam_user_auth( + access_key="test-access-key", + secret_key="test-secret-key", + ) + .direct(region="test-region") + .include(assets=["test-asset-1", "test-asset-2"]) + .exclude(assets=[]) + .to_workflow() + ) + request_json = loads(glue_iam_user_auth.to_json()) + assert_workflow_equal(request_json, load_json(GLUE_IAM_USER)) + + +def test_tableau_package(mock_package_env, client: AtlanClient): + tableau_basic_auth = ( + TableauCrawler( + client=client, + connection_name="test-tableau-basic-conn", + admin_roles=["admin-guid-1234"], + admin_groups=None, + admin_users=None, + ) + .direct( + hostname="test.tableau.com", port=444, site="test-site", ssl_enabled=True + ) + .basic_auth( + username="test-username", + password="test-password", + ) + .include(projects=["test-project-guid-1", "test-project-guid-2"]) + .exclude(projects=[]) + .crawl_unpublished(True) + .crawl_hidden_fields(False) + .to_workflow() + ) + request_json = loads(tableau_basic_auth.to_json()) + assert_workflow_equal(request_json, load_json(TABLEAU_BASIC)) + + tableau_access_token_auth = ( + TableauCrawler( + client=client, + connection_name="test-tableau-access-token-conn", + admin_roles=["admin-guid-1234"], + admin_groups=None, + admin_users=None, + ) + .direct(hostname="test.tableau.com", site="test-site", ssl_enabled=False) + .personal_access_token( + username="test-username", + access_token="test-access-token", + ) + .include(projects=["test-project-guid-1", "test-project-guid-2"]) + .exclude(projects=[]) + .crawl_unpublished(True) + .crawl_hidden_fields(False) + .to_workflow() + ) + request_json = loads(tableau_access_token_auth.to_json()) + assert_workflow_equal(request_json, load_json(TABLEAU_ACCESS_TOKEN)) + + tableau_offline = ( + TableauCrawler( + client=client, + connection_name="test-tableau-offline-conn", + admin_roles=["admin-guid-1234"], + admin_groups=None, + admin_users=None, + ) + .s3( + bucket_name="test-bucket", + bucket_prefix="test-prefix", + bucket_region="test-region", + ) + .to_workflow() + ) + request_json = loads(tableau_offline.to_json()) + assert_workflow_equal(request_json, load_json(TABLEAU_OFFLINE)) + + +def test_powerbi_package(mock_package_env, client: AtlanClient): + powerbi_delegated_user = ( + PowerBICrawler( + client=client, + connection_name="test-powerbi-du-conn", + admin_roles=["admin-guid-1234"], + admin_groups=None, + admin_users=None, + ) + .direct() + .delegated_user( + username="test-username", + password="test-password", + tenant_id="test-tenant-id", + client_id="test-client-id", + client_secret="test-client-secret", + ) + .include(workspaces=["test-workspace-guid"]) + .exclude(workspaces=[]) + .to_workflow() + ) + request_json = loads(powerbi_delegated_user.to_json()) + assert_workflow_equal(request_json, load_json(POWERBI_DELEGATED_USER)) + + powerbi_service_principal = ( + PowerBICrawler( + client=client, + connection_name="test-powerbi-sp-conn", + admin_roles=["admin-guid-1234"], + admin_groups=None, + admin_users=None, + ) + .direct() + .service_principal( + tenant_id="test-tenant-id", + client_id="test-client-id", + client_secret="test-client-secret", + ) + .include(workspaces=["test-workspace-guid"]) + .exclude(workspaces=[]) + .to_workflow() + ) + request_json = loads(powerbi_service_principal.to_json()) + assert_workflow_equal(request_json, load_json(POWEBI_SERVICE_PRINCIPAL)) + + +def test_confluent_kafka_package(mock_package_env, client: AtlanClient): + conf_kafka_direct = ( + ConfluentKafkaCrawler( + client=client, + connection_name="test-conf-kafka-direct-conn", + admin_roles=["admin-guid-1234"], + admin_groups=None, + admin_users=None, + ) + .direct(bootstrap="test-bootstrap-server:9092", encrypted=True) + .api_token(api_key="test-api-key", api_secret="test-api-secret") + .skip_internal(False) + .include(regex=".*_TEST") + .exclude(regex="") + .to_workflow() + ) + request_json = loads(conf_kafka_direct.to_json()) + assert_workflow_equal(request_json, load_json(CONFLUENT_KAFKA_DIRECT)) + + +def test_dbt_package(mock_package_env, client: AtlanClient): + dbt_core = ( + DbtCrawler( + client=client, + connection_name="test-dbt-core-conn", + admin_roles=["admin-guid-1234"], + admin_groups=None, + admin_users=None, + ) + .core( + s3_bucket="test-s3-bucket", + s3_prefix="test-s3-prefix", + s3_region="test-s3-region", + ) + .limit_to_connection(connection_qualified_name="default/dbt/1234567890") + .tags(True) + .enrich_materialized_assets(True) + .to_workflow() + ) + request_json = loads(dbt_core.to_json()) + assert_workflow_equal(request_json, load_json(DBT_CORE)) + + dbt_cloud = ( + DbtCrawler( + client=client, + connection_name="test-dbt-cloud-conn", + admin_roles=["admin-guid-1234"], + admin_groups=None, + admin_users=None, + ) + .cloud( + hostname="test-hostname", + service_token="test-service-token", + multi_tenant=False, + ) + .limit_to_connection(connection_qualified_name="default/dbt/1234567890") + .include(filter='{"1234":{"4321":{}}}') + .exclude(filter="") + .tags(True) + .enrich_materialized_assets(False) + .to_workflow() + ) + request_json = loads(dbt_cloud.to_json()) + assert_workflow_equal(request_json, load_json(DBT_CLOUD)) + + +def test_sigma_package(mock_package_env, client: AtlanClient): + sigma_api_token = ( + SigmaCrawler( + client=client, + connection_name="test-sigma-basic-conn", + admin_roles=["admin-guid-1234"], + admin_groups=None, + admin_users=None, + ) + .direct(hostname=SigmaCrawler.Hostname.AWS, port=1234) + .api_token(client_id="test-client-id", api_token="test-api-token") + .include(workbooks=["test-workbook-1", "test-workbook-2"]) + .exclude(workbooks=[]) + .to_workflow() + ) + request_json = loads(sigma_api_token.to_json()) + assert_workflow_equal(request_json, load_json(SIGMA_API_TOKEN)) + + +def test_sql_server_package(mock_package_env, client: AtlanClient): + sql_server_basic = ( + SQLServerCrawler( + client=client, + connection_name="test-sigma-basic-conn", + admin_roles=["admin-guid-1234"], + admin_groups=None, + admin_users=None, + ) + .direct(hostname="11.22.33.44", database="test-db", port=1234) + .basic_auth(username="test-user", password="test-pass") + .include( + assets={ + "test-db": [ + "test-schema-1", + "test-schema-2", + ] + } + ) + .exclude(assets={}) + .to_workflow() + ) + request_json = loads(sql_server_basic.to_json()) + assert_workflow_equal(request_json, load_json(SQL_SERVER_BASIC)) + + +def test_snowflake_miner_package(mock_package_env): + # With default configuration + snowflake_miner_default = ( + SnowflakeMiner(connection_qualified_name="default/snowflake/1234567890") + .direct(start_epoch=9876543210, database="TEST_SNOWFLAKE", schema="TEST_SCHEMA") + .exclude_users(users=["test-user-1", "test-user-2"]) + .to_workflow() + ) + request_json = loads(snowflake_miner_default.to_json()) + assert_workflow_equal(request_json, load_json(SNOWFLAKE_MINER_DEFAULT)) + + # With advanced configuration (source) + snowflake_miner_source = ( + SnowflakeMiner(connection_qualified_name="default/snowflake/1234567890") + .direct(start_epoch=9876543210, database="TEST_SNOWFLAKE", schema="TEST_SCHEMA") + .exclude_users(users=["test-user-1", "test-user-2"]) + .popularity_window(days=15) + .native_lineage(enabled=True) + .custom_config(config={"test": True, "feature": 1234}) + .to_workflow() + ) + request_json = loads(snowflake_miner_source.to_json()) + assert_workflow_equal(request_json, load_json(SNOWFLAKE_MINER_SOURCE)) + + # With advanced configuration (offline) + snowflake_miner_s3_offline = ( + SnowflakeMiner(connection_qualified_name="default/snowflake/1234567890") + .s3( + s3_bucket="test-s3-bucket", + s3_prefix="test-s3-prefix", + s3_bucket_region="test-s3-bucket-region", + sql_query_key="TEST_QUERY", + default_database_key="TEST_SNOWFLAKE", + default_schema_key="TEST_SCHEMA", + session_id_key="TEST_SESSION_ID", + ) + .popularity_window(days=15) + .native_lineage(enabled=True) + .custom_config(config={"test": True, "feature": 1234}) + .to_workflow() + ) + request_json = loads(snowflake_miner_s3_offline.to_json()) + assert_workflow_equal(request_json, load_json(SNOWFLAKE_MINER_S3_OFFLINE)) + + +def test_databricks_miner_package(mock_package_env): + databricks_miner_rest = ( + DatabricksMiner(connection_qualified_name="default/databricks/1234567890") + .rest_api() + .to_workflow() + ) + request_json = loads(databricks_miner_rest.to_json()) + assert_workflow_equal(request_json, load_json(DATABRICKS_MINER_REST)) + + databricks_miner_offline = ( + DatabricksMiner(connection_qualified_name="default/databricks/1234567890") + .offline(bucket_name="test-bucket", bucket_prefix="test-prefix") + .to_workflow() + ) + request_json = loads(databricks_miner_offline.to_json()) + assert_workflow_equal(request_json, load_json(DATABRICKS_MINER_OFFLINE)) + + databricks_miner_system_table = ( + DatabricksMiner(connection_qualified_name="default/databricks/1234567890") + .system_table(warehouse_id="test-warehouse-id") + .to_workflow() + ) + request_json = loads(databricks_miner_system_table.to_json()) + assert_workflow_equal(request_json, load_json(DATABRICKS_MINER_SYSTEM_TABLE)) + + databricks_miner_popularity_rest = ( + DatabricksMiner(connection_qualified_name="default/databricks/1234567890") + .rest_api() + .popularity_configuration( + start_date="1234567890", + window_days=10, + excluded_users=["test-user-1", "test-user-2"], + ) + .to_workflow() + ) + request_json = loads(databricks_miner_popularity_rest.to_json()) + assert_workflow_equal(request_json, load_json(DATABRICKS_MINER_POPULARITY_REST)) + + databricks_miner_popularity_system_table = ( + DatabricksMiner(connection_qualified_name="default/databricks/1234567890") + .rest_api() + .popularity_configuration( + start_date="1234567890", + window_days=10, + excluded_users=["test-user-1", "test-user-2"], + warehouse_id="test-warehouse-id", + extraction_method=DatabricksMiner.ExtractionMethod.SYSTEM_TABLE, + ) + .to_workflow() + ) + request_json = loads(databricks_miner_popularity_system_table.to_json()) + + assert_workflow_equal( + request_json, load_json(DATABRICKS_MINER_POPULARITY_SYSTEM_TABLE) + ) + + +def test_big_query_package(mock_package_env, client: AtlanClient): + big_query_direct = ( + BigQueryCrawler( + client=client, + connection_name="test-big-query-conn", + admin_roles=["admin-guid-1234"], + ) + .service_account_auth( + project_id="test-project-id", + service_account_json="test-account-json", + service_account_email="test@test.com", + ) + .include(assets={"test-include": ["test-asset-1", "test-asset-2"]}) + .exclude(assets={}) + .exclude_regex(regex=".*_TEST") + .custom_config(config={"test": True, "feature": 1234}) + .to_workflow() + ) + request_json = loads(big_query_direct.to_json()) + assert_workflow_equal(request_json, load_json(BIG_QUERY_DIRECT)) + + +def test_dynamo_db_package(mock_package_env, client: AtlanClient): + dynamo_db_direct_iam_user = ( + DynamoDBCrawler( + client=client, + connection_name="test-dynamodb-conn", + admin_roles=["admin-guid-1234"], + ) + .direct(region="test-region") + .iam_user_auth(access_key="test-access-key", secret_key="test-secret-key") + .include_regex(regex=".*_TEST_INCLUDE") + .exclude_regex(regex=".*_TEST_EXCLUDE") + .to_workflow() + ) + request_json = loads(dynamo_db_direct_iam_user.to_json()) + assert_workflow_equal(request_json, load_json(DYNAMO_DB_IAM_USER)) + + dynamo_db_direct_iam_user_role = ( + DynamoDBCrawler( + client=client, + connection_name="test-dynamodb-conn", + admin_roles=["admin-guid-1234"], + ) + .direct(region="test-region") + .iam_role_auth( + arn="arn:aws:iam::123456789012:user/test", external_id="test-ext-id" + ) + .include_regex(regex=".*_TEST_INCLUDE") + .exclude_regex(regex=".*_TEST_EXCLUDE") + .to_workflow() + ) + request_json = loads(dynamo_db_direct_iam_user_role.to_json()) + assert_workflow_equal(request_json, load_json(DYNAMO_DB_IAM_USER_ROLE)) + + +def test_postgres_package(mock_package_env, client: AtlanClient): + postgres_direct_basic = ( + PostgresCrawler( + client=client, + connection_name="test-sdk-postgresql", + admin_roles=["admin-guid-1234"], + ) + .direct(hostname="test.com", database="test-db") + .basic_auth( + username="test-user", + password="test-password", + ) + .include(assets={"test-include": ["test-asset-1", "test-asset-2"]}) + .exclude(assets={}) + .exclude_regex(regex=".*_TEST") + .source_level_filtering(enable=True) + .jdbc_internal_methods(enable=True) + .to_workflow() + ) + + request_json = loads(postgres_direct_basic.to_json()) + assert_workflow_equal(request_json, load_json(POSTGRES_DIRECT_BASIC)) + + postgres_direct_iam_user = ( + PostgresCrawler( + client=client, + connection_name="test-sdk-postgresql", + admin_roles=["admin-guid-1234"], + ) + .direct(hostname="test.com", database="test-db") + .iam_user_auth( + username="test-user", + access_key="test-access-key", + secret_key="test-secret-key", + ) + .include(assets={"test-include": ["test-asset-1", "test-asset-2"]}) + .exclude(assets={}) + .exclude_regex(regex=".*_TEST") + .source_level_filtering(enable=True) + .jdbc_internal_methods(enable=True) + .to_workflow() + ) + + request_json = loads(postgres_direct_iam_user.to_json()) + assert_workflow_equal(request_json, load_json(POSTGRES_DIRECT_IAM_USER)) + + postgres_direct_iam_role = ( + PostgresCrawler( + client=client, + connection_name="test-sdk-postgresql", + admin_roles=["admin-guid-1234"], + ) + .direct(hostname="test.com", database="test-db") + .iam_role_auth( + username="test-user", + arn="arn:aws:iam::123456789012:user/test", + external_id="test-ext-id", + ) + .include(assets={"test-include": ["test-asset-1", "test-asset-2"]}) + .exclude(assets={}) + .exclude_regex(regex=".*_TEST") + .source_level_filtering(enable=True) + .jdbc_internal_methods(enable=True) + .to_workflow() + ) + + request_json = loads(postgres_direct_iam_role.to_json()) + assert_workflow_equal(request_json, load_json(POSTGRES_DIRECT_IAM_ROLE)) + + postgres_s3_offline = ( + PostgresCrawler( + client=client, + connection_name="test-sdk-postgresql", + admin_roles=["admin-guid-1234"], + ) + .s3( + bucket_name="test-bucket", + bucket_prefix="test-prefix", + bucket_region="test-region", + ) + .include(assets={"test-include": ["test-asset-1", "test-asset-2"]}) + .exclude(assets={}) + .exclude_regex(regex=".*_TEST") + .source_level_filtering(enable=True) + .jdbc_internal_methods(enable=True) + .to_workflow() + ) + + request_json = loads(postgres_s3_offline.to_json()) + assert_workflow_equal(request_json, load_json(POSTGRES_S3_OFFLINE)) + + +def test_mongodb_package(mock_package_env, client: AtlanClient): + mongodb_basic = ( + MongoDBCrawler( + client=client, + connection_name="test-sdk-mongodb", + admin_roles=["admin-guid-1234"], + ) + .direct(hostname="test-hostname", port=1234) + .basic_auth( + username="test-user", + password="test-pass", + native_host="test-native-host", + default_db="test-default-db", + auth_db="test-auth-db", + is_ssl=False, + ) + .include(assets=["test-asset-1", "test-asset-2"]) + .exclude(assets=["test-asset-1", "test-asset-2"]) + .exclude_regex(regex="TEST*") + .to_workflow() + ) + + request_json = loads(mongodb_basic.to_json()) + assert_workflow_equal(request_json, load_json(MONGODB_BASIC)) + + +def test_connection_delete_package(mock_package_env): + # With PURGE (hard delete) + connection_delete_hard = ConnectionDelete( + qualified_name="default/snowflake/1234567890", purge=True + ).to_workflow() + request_json = loads(connection_delete_hard.to_json()) + assert_workflow_equal(request_json, load_json(CONNECTION_DELETE_HARD)) + + # Without PURGE (soft delete) + connection_delete_soft = ConnectionDelete( + qualified_name="default/snowflake/1234567890", purge=False + ).to_workflow() + request_json = loads(connection_delete_soft.to_json()) + assert_workflow_equal(request_json, load_json(CONNECTION_DELETE_SOFT)) + + +def test_databricks_crawler(mock_package_env, client: AtlanClient): + databricks_basic_jdbc = ( + DatabricksCrawler( + client=client, + connection_name="test-databricks-basic", + admin_roles=["admin-guid-1234"], + admin_groups=None, + admin_users=None, + row_limit=10000, + allow_query=True, + allow_query_preview=True, + ) + .direct(hostname="test-hostname") + .basic_auth( + personal_access_token="test-pat", + http_path="test-http-path", + ) + .metadata_extraction_method(type=DatabricksCrawler.ExtractionMethod.JDBC) + .include(assets={"test-include": ["ti1", "ti2"]}) + .exclude(assets={"test-exclude": ["te1", "te2"]}) + .exclude_regex(regex="TEST*") + .enable_view_lineage(False) + .enable_source_level_filtering(True) + .to_workflow() + ) + request_json = loads(databricks_basic_jdbc.to_json()) + assert_workflow_equal(request_json, load_json(DATABRICKS_BASIC_JDBC)) + + databricks_basic_rest = ( + DatabricksCrawler( + client=client, + connection_name="test-databricks-basic", + admin_roles=["admin-guid-1234"], + admin_groups=None, + admin_users=None, + row_limit=10000, + allow_query=True, + allow_query_preview=True, + ) + .direct(hostname="test-hostname") + .basic_auth( + personal_access_token="test-pat", + http_path="test-http-path", + ) + .metadata_extraction_method(type=DatabricksCrawler.ExtractionMethod.REST) + .include_for_rest_api(assets=["ti1", "ti2"]) + .exclude_for_rest_api(assets=["te1", "te2"]) + .sql_warehouse(warehouse_ids=["3d939b0cc668be06", "9a289b0cc838ce62"]) + .import_tags(True) + .enable_source_level_filtering(False) + .to_workflow() + ) + request_json = loads(databricks_basic_rest.to_json()) + assert_workflow_equal(request_json, load_json(DATABRICKS_BASIC_REST)) + + databricks_aws = ( + DatabricksCrawler( + client=client, + connection_name="test-databricks-basic", + admin_roles=["admin-guid-1234"], + admin_groups=None, + admin_users=None, + row_limit=10000, + allow_query=True, + allow_query_preview=True, + ) + .direct(hostname="test-hostname") + .aws_service( + client_id="test-client-id", + client_secret="test-client-secret", + ) + .metadata_extraction_method(type=DatabricksCrawler.ExtractionMethod.REST) + .include_for_rest_api(assets=["ti1", "ti2"]) + .exclude_for_rest_api(assets=["te1", "te2"]) + .sql_warehouse(warehouse_ids=["3d939b0cc668be06", "9a289b0cc838ce62"]) + .import_tags(True) + .enable_source_level_filtering(False) + .to_workflow() + ) + request_json = loads(databricks_aws.to_json()) + assert_workflow_equal(request_json, load_json(DATABRICKS_AWS)) + + databricks_azure = ( + DatabricksCrawler( + client=client, + connection_name="test-databricks-basic", + admin_roles=["admin-guid-1234"], + admin_groups=None, + admin_users=None, + row_limit=10000, + allow_query=True, + allow_query_preview=True, + ) + .direct(hostname="test-hostname") + .azure_service( + client_id="test-client-id", + client_secret="test-client-secret", + tenant_id="test-tenant-id", + ) + .metadata_extraction_method(type=DatabricksCrawler.ExtractionMethod.REST) + .include_for_rest_api(assets=["ti1", "ti2"]) + .exclude_for_rest_api(assets=["te1", "te2"]) + .sql_warehouse(warehouse_ids=["3d939b0cc668be06", "9a289b0cc838ce62"]) + .import_tags(True) + .enable_source_level_filtering(False) + .to_workflow() + ) + request_json = loads(databricks_azure.to_json()) + assert_workflow_equal(request_json, load_json(DATABRICKS_AZURE)) + + databricks_system_tables = ( + DatabricksCrawler( + client=client, + connection_name="test-databricks-system-tables", + admin_roles=["admin-guid-1234"], + admin_groups=None, + admin_users=None, + row_limit=10000, + allow_query=True, + allow_query_preview=True, + ) + .direct(hostname="test-hostname.com") + .pat( + access_token="test-access-token", + sql_warehouse_id="test-sql-warehouse-id", + ) + .metadata_extraction_method( + type=DatabricksCrawler.ExtractionMethod.SYSTEM_TABLES + ) + .import_tags(include=True) + .sql_warehouse(warehouse_ids=["test-sql-warehouse-id"]) + .enable_cross_workspace_discovery(include=True) + .asset_selection_for_system_tables( + selection_criteria=[ + DatabricksCrawler.AssetsSelection( + type=DatabricksCrawler.AssetsSelectionCriteria.INCLUDE_BY_HIERARCHY, + values={"t1": ["s1", "s2", "s3"], "t2": [], "t3": ["s4", "s5"]}, + ), + DatabricksCrawler.AssetsSelection( + type=DatabricksCrawler.AssetsSelectionCriteria.EXCLUDE_BY_HIERARCHY, + values={"t1": ["s1", "s2", "s3"], "t2": [], "t3": ["s4", "s5"]}, + ), + DatabricksCrawler.AssetsSelection( + type=DatabricksCrawler.AssetsSelectionCriteria.INCLUDE_BY_REGEX, + values={ + "asset_type": DatabricksCrawler.RegexAssetTypes.DATABASES, + "regex": "test-db-regex", + }, + ), + DatabricksCrawler.AssetsSelection( + type=DatabricksCrawler.AssetsSelectionCriteria.INCLUDE_BY_REGEX, + values={ + "asset_type": DatabricksCrawler.RegexAssetTypes.SCHEMAS, + "regex": "test-schema-regex", + }, + ), + DatabricksCrawler.AssetsSelection( + type=DatabricksCrawler.AssetsSelectionCriteria.INCLUDE_BY_REGEX, + values={ + "asset_type": DatabricksCrawler.RegexAssetTypes.TABLE_VIEWS, + "regex": "test-table-view-regex", + }, + ), + DatabricksCrawler.AssetsSelection( + type=DatabricksCrawler.AssetsSelectionCriteria.EXCLUDE_BY_REGEX, + values={ + "asset_type": DatabricksCrawler.RegexAssetTypes.DATABASES, + "regex": "test-db-exclude-regex", + }, + ), + DatabricksCrawler.AssetsSelection( + type=DatabricksCrawler.AssetsSelectionCriteria.EXCLUDE_BY_REGEX, + values={ + "asset_type": DatabricksCrawler.RegexAssetTypes.SCHEMAS, + "regex": "test-schema-exclude-regex", + }, + ), + DatabricksCrawler.AssetsSelection( + type=DatabricksCrawler.AssetsSelectionCriteria.EXCLUDE_BY_REGEX, + values={ + "asset_type": DatabricksCrawler.RegexAssetTypes.TABLE_VIEWS, + "regex": "test-table-view-exclude-regex", + }, + ), + ] + ) + .enable_incremental_extraction(True) + .to_workflow() + ) + request_json = loads(databricks_system_tables.to_json()) + assert_workflow_equal(request_json, load_json(DATABRICKS_SYSTEM_TABLES)) + + databricks_offline = ( + DatabricksCrawler( + client=client, + connection_name="test-databricks-basic", + admin_roles=["admin-guid-1234"], + admin_groups=None, + admin_users=None, + row_limit=10000, + allow_query=True, + allow_query_preview=True, + ) + .s3( + bucket_name="test-bucket", + bucket_prefix="test-prefix", + bucket_region="test-region", + ) + .to_workflow() + ) + request_json = loads(databricks_offline.to_json()) + assert_workflow_equal(request_json, load_json(DATABRICKS_OFFLINE)) + + +def test_asset_import(mock_package_env): + # Case 1: Importing assets, glossaries, and data products from S3 with advanced configuration + asset_import_s3 = ( + AssetImport() + .object_store() + .s3( + access_key="test-access-key", + secret_key="test-secret-key", + bucket="my-bucket", + region="us-west-1", + ) + .assets( + prefix="/test/prefix", + object_key="assets-test.csv", + input_handling=AssetInputHandling.PARTIAL, + ) + .assets_advanced( + remove_attributes=[Asset.CERTIFICATE_STATUS, Asset.ANNOUNCEMENT_TYPE], + fail_on_errors=True, + case_sensitive_match=False, + field_separator=",", + batch_size=20, + ) + .glossaries( + prefix="/test/prefix", + object_key="glossaries-test.csv", + input_handling=AssetInputHandling.UPDATE, + ) + .glossaries_advanced( + remove_attributes=[Asset.CERTIFICATE_STATUS, Asset.ANNOUNCEMENT_TYPE], + fail_on_errors=True, + field_separator=",", + batch_size=20, + ) + .data_products( + prefix="/test/prefix", + object_key="data-products-test.csv", + input_handling=AssetInputHandling.UPDATE, + ) + .data_product_advanced( + remove_attributes=[Asset.CERTIFICATE_STATUS, Asset.ANNOUNCEMENT_TYPE], + fail_on_errors=True, + field_separator=",", + batch_size=20, + ) + ).to_workflow() + + request_json_s3 = loads(asset_import_s3.to_json()) + assert request_json_s3 == load_json(ASSET_IMPORT_S3) + + # Case 2: Importing assets, glossaries, and data products from GCS with advanced configuration + asset_import_gcs = ( + AssetImport() + .object_store() + .gcs( + service_account_json="test-service-account-json", + project_id="test-project-id", + bucket="my-bucket", + ) + .assets( + prefix="/test/prefix", + object_key="assets-test.csv", + input_handling=AssetInputHandling.UPSERT, + ) + .assets_advanced( + remove_attributes=[Asset.CERTIFICATE_STATUS, Asset.ANNOUNCEMENT_TYPE], + fail_on_errors=True, + case_sensitive_match=False, + field_separator=",", + batch_size=20, + ) + .glossaries( + prefix="/test/prefix", + object_key="glossaries-test.csv", + input_handling=AssetInputHandling.UPDATE, + ) + .glossaries_advanced( + remove_attributes=[Asset.CERTIFICATE_STATUS, Asset.ANNOUNCEMENT_TYPE], + fail_on_errors=True, + field_separator=",", + batch_size=20, + ) + .data_products( + prefix="/test/prefix", + object_key="data-products-test.csv", + input_handling=AssetInputHandling.UPDATE, + ) + .data_product_advanced( + remove_attributes=[Asset.CERTIFICATE_STATUS, Asset.ANNOUNCEMENT_TYPE], + fail_on_errors=True, + field_separator=",", + batch_size=20, + ) + ).to_workflow() + + request_json_gcs = loads(asset_import_gcs.to_json()) + assert request_json_gcs == load_json(ASSET_IMPORT_GCS) + + # Case 3: Importing assets, glossaries, and data products from Adls with advanced configuration + asset_import_adls = ( + AssetImport() + .object_store() + .adls( + client_id="test-client-id", + client_secret="test-client-secret", + tenant_id="test-tenant-id", + account_name="test-storage-account", + container="test-adls-container", + ) + .assets( + prefix="/test/prefix", + object_key="assets-test.csv", + input_handling=AssetInputHandling.UPSERT, + ) + .assets_advanced( + remove_attributes=[Asset.CERTIFICATE_STATUS, Asset.ANNOUNCEMENT_TYPE], + fail_on_errors=True, + case_sensitive_match=False, + field_separator=",", + batch_size=20, + ) + .glossaries( + prefix="/test/prefix", + object_key="glossaries-test.csv", + input_handling=AssetInputHandling.UPDATE, + ) + .glossaries_advanced( + remove_attributes=[Asset.CERTIFICATE_STATUS, Asset.ANNOUNCEMENT_TYPE], + fail_on_errors=True, + field_separator=",", + batch_size=20, + ) + .data_products( + prefix="/test/prefix", + object_key="data-products-test.csv", + input_handling=AssetInputHandling.UPDATE, + ) + .data_product_advanced( + remove_attributes=[Asset.CERTIFICATE_STATUS, Asset.ANNOUNCEMENT_TYPE], + fail_on_errors=True, + field_separator=",", + batch_size=20, + ) + ).to_workflow() + + request_json_adls = loads(asset_import_adls.to_json()) + assert request_json_adls == load_json(ASSET_IMPORT_ADLS) + + # Case 4: Importing assets, glossaries, and data products from S3 with default configuration + asset_import_default = ( + AssetImport() + .object_store() + .s3( + access_key="test-access-key", + secret_key="test-secret-key", + bucket="my-bucket", + region="us-west-1", + ) + .assets( + prefix="/test/prefix", + object_key="assets-test.csv", + input_handling=AssetInputHandling.UPDATE, + ) + .glossaries( + prefix="/test/prefix", + object_key="glossaries-test.csv", + input_handling=AssetInputHandling.UPSERT, + ) + .data_products( + prefix="/test/prefix", + object_key="data-products-test.csv", + input_handling=AssetInputHandling.UPSERT, + ) + ).to_workflow() + + request_json_default = loads(asset_import_default.to_json()) + assert request_json_default == load_json(ASSET_IMPORT_DEFAULT) + + +def test_asset_export_basic(mock_package_env): + # Case 1: Export assets with glossaries only using s3 + asset_export_basic_glossaries_only_s3 = ( + AssetExportBasic() + .glossaries_only(include_archived=True) + .object_store(prefix="/test/prefix") + .s3( + access_key="test-access-key", + secret_key="test-secret-key", + bucket="my-bucket", + region="us-west-1", + ) + ).to_workflow() + + request_json_s3 = loads(asset_export_basic_glossaries_only_s3.to_json()) + assert request_json_s3 == load_json(ASSET_EXPORT_BASIC_GLOSSARIES_ONLY_S3) + + # Case 2: Export assets with Products only using s3 + asset_export_basic_products_only_s3 = ( + AssetExportBasic() + .products_only(include_archived=True) + .object_store(prefix="/test/prefix") + .s3( + access_key="test-access-key", + secret_key="test-secret-key", + bucket="my-bucket", + region="us-west-1", + ) + ).to_workflow() + + request_json_s3 = loads(asset_export_basic_products_only_s3.to_json()) + assert request_json_s3 == load_json(ASSET_EXPORT_BASIC_PRODUCTS_ONLY_S3) + + # Case 3: Export assets with Enriched only using s3 + asset_export_basic_enriched_only_s3 = ( + AssetExportBasic() + .enriched_only( + prefix="/test/prefix", + include_description=True, + include_glossaries=True, + include_data_products=True, + include_archived=True, + ) + .object_store(prefix="/test/prefix") + .s3( + access_key="test-access-key", + secret_key="test-secret-key", + bucket="my-bucket", + region="us-west-1", + ) + ).to_workflow() + + request_json_s3 = loads(asset_export_basic_enriched_only_s3.to_json()) + assert request_json_s3 == load_json(ASSET_EXPORT_BASIC_ENRICHED_ONLY_S3) + + # Case 4: Export all assets using s3 + asset_export_basic_all_assets_s3 = ( + AssetExportBasic() + .all_assets( + prefix="/test/prefix", + include_description=True, + include_glossaries=True, + include_data_products=True, + include_archived=True, + ) + .object_store(prefix="/test/prefix") + .s3( + access_key="test-access-key", + secret_key="test-secret-key", + bucket="my-bucket", + region="us-west-1", + ) + ).to_workflow() + + request_json_s3 = loads(asset_export_basic_all_assets_s3.to_json()) + assert request_json_s3 == load_json(ASSET_EXPORT_BASIC_ALL_ASSETS_S3) + + # Case 1: Export assets with glossaries only using adls + asset_export_basic_glossaries_only_adls = ( + AssetExportBasic() + .glossaries_only(include_archived=True) + .object_store(prefix="/test/prefix") + .adls( + client_id="test-client-id", + client_secret="test-client-secret", + tenant_id="test-tenant-id", + account_name="test-storage-account", + container="test-adls-container", + ) + ).to_workflow() + + request_json_adls = loads(asset_export_basic_glossaries_only_adls.to_json()) + assert request_json_adls == load_json(ASSET_EXPORT_BASIC_GLOSSARIES_ONLY_ADLS) + + # Case 2: Export assets with Products only using adls + asset_export_basic_products_only_adls = ( + AssetExportBasic() + .products_only(include_archived=True) + .object_store(prefix="/test/prefix") + .adls( + client_id="test-client-id", + client_secret="test-client-secret", + tenant_id="test-tenant-id", + account_name="test-storage-account", + container="test-adls-container", + ) + ).to_workflow() + + request_json_adls = loads(asset_export_basic_products_only_adls.to_json()) + assert request_json_adls == load_json(ASSET_EXPORT_BASIC_PRODUCTS_ONLY_ADLS) + + # Case 3: Export assets with Enriched only using adls + asset_export_basic_enriched_only_adls = ( + AssetExportBasic() + .enriched_only( + prefix="/test/prefix", + include_description=True, + include_glossaries=True, + include_data_products=True, + include_archived=True, + ) + .object_store(prefix="/test/prefix") + .adls( + client_id="test-client-id", + client_secret="test-client-secret", + tenant_id="test-tenant-id", + account_name="test-storage-account", + container="test-adls-container", + ) + ).to_workflow() + + request_json_adls = loads(asset_export_basic_enriched_only_adls.to_json()) + assert request_json_adls == load_json(ASSET_EXPORT_BASIC_ENRICHED_ONLY_ADLS) + + # Case 4: Export all assets using adls + asset_export_basic_all_assets_adls = ( + AssetExportBasic() + .all_assets( + prefix="/test/prefix", + include_description=True, + include_glossaries=True, + include_data_products=True, + include_archived=True, + ) + .object_store(prefix="/test/prefix") + .adls( + client_id="test-client-id", + client_secret="test-client-secret", + tenant_id="test-tenant-id", + account_name="test-storage-account", + container="test-adls-container", + ) + ).to_workflow() + + request_json_adls = loads(asset_export_basic_all_assets_adls.to_json()) + assert request_json_adls == load_json(ASSET_EXPORT_BASIC_ALL_ASSETS_ADLS) + + # Case 1: Export assets with glossaries only using gcs + asset_export_basic_glossaries_only_gcs = ( + AssetExportBasic() + .glossaries_only(include_archived=True) + .object_store(prefix="/test/prefix") + .gcs( + service_account_json="test-service-account-json", + project_id="test-project-id", + bucket="my-bucket", + ) + ).to_workflow() + + request_json_gcs = loads(asset_export_basic_glossaries_only_gcs.to_json()) + assert request_json_gcs == load_json(ASSET_EXPORT_BASIC_GLOSSARIES_ONLY_GCS) + + # Case 2: Export assets with Products only using gcs + asset_export_basic_products_only_gcs = ( + AssetExportBasic() + .products_only(include_archived=True) + .object_store(prefix="/test/prefix") + .gcs( + service_account_json="test-service-account-json", + project_id="test-project-id", + bucket="my-bucket", + ) + ).to_workflow() + + request_json_gcs = loads(asset_export_basic_products_only_gcs.to_json()) + assert request_json_gcs == load_json(ASSET_EXPORT_BASIC_PRODUCTS_ONLY_GCS) + + # Case 3: Export assets with Enriched only using gcs + asset_export_basic_enriched_only_gcs = ( + AssetExportBasic() + .enriched_only( + prefix="/test/prefix", + include_description=True, + include_glossaries=True, + include_data_products=True, + include_archived=True, + ) + .object_store(prefix="/test/prefix") + .gcs( + service_account_json="test-service-account-json", + project_id="test-project-id", + bucket="my-bucket", + ) + ).to_workflow() + + request_json_gcs = loads(asset_export_basic_enriched_only_gcs.to_json()) + assert request_json_gcs == load_json(ASSET_EXPORT_BASIC_ENRICHED_ONLY_GCS) + + # Case 4: Export all assets using gcs + asset_export_basic_all_assets_gcs = ( + AssetExportBasic() + .all_assets( + prefix="/test/prefix", + include_description=True, + include_glossaries=True, + include_data_products=True, + include_archived=True, + ) + .object_store(prefix="/test/prefix") + .gcs( + service_account_json="test-service-account-json", + project_id="test-project-id", + bucket="my-bucket", + ) + ).to_workflow() + + request_json_gcs = loads(asset_export_basic_all_assets_gcs.to_json()) + assert request_json_gcs == load_json(ASSET_EXPORT_BASIC_ALL_ASSETS_GCS) + + +def test_relational_assets_builder(mock_package_env): + # Case 1: Build/Update relational assets from S3 with advanced configuration + relational_assets_builder_s3 = ( + RelationalAssetsBuilder() + .object_store( + prefix="/test/prefix", + object_key="assets-test.csv", + ) + .s3( + access_key="test-access-key", + secret_key="test-secret-key", + bucket="my-bucket", + region="us-west-1", + ) + .assets_semantics( + input_handling=AssetInputHandling.UPSERT, + delta_handling=AssetDeltaHandling.INCREMENTAL, + ) + .options( + remove_attributes=[Asset.CERTIFICATE_STATUS, Asset.ANNOUNCEMENT_TYPE], + fail_on_errors=True, + field_separator=",", + batch_size=20, + ) + ).to_workflow() + + request_json_s3 = loads(relational_assets_builder_s3.to_json()) + assert request_json_s3 == load_json(RELATIONAL_ASSETS_BUILDER_S3) + + # Case 2: Build/Update relational assets from adls with advanced configuration + relational_assets_builder_adls = ( + RelationalAssetsBuilder() + .object_store( + prefix="/test/prefix", + object_key="assets-test.csv", + ) + .adls( + client_id="test-client-id", + client_secret="test-client-secret", + tenant_id="test-tenant-id", + account_name="test-storage-account", + container="test-adls-container", + ) + .assets_semantics( + input_handling=AssetInputHandling.PARTIAL, + delta_handling=AssetDeltaHandling.FULL_REPLACEMENT, + removal_type=AssetRemovalType.ARCHIVE, + ) + .options( + remove_attributes=[Asset.CERTIFICATE_STATUS, Asset.ANNOUNCEMENT_TYPE], + fail_on_errors=True, + field_separator=",", + batch_size=20, + ) + ).to_workflow() + + request_json_adls = loads(relational_assets_builder_adls.to_json()) + assert request_json_adls == load_json(RELATIONAL_ASSETS_BUILDER_ADLS) + + # Case 3: Build/Update relational assets from gcs with advanced configuration + relational_assets_builder_gcs = ( + RelationalAssetsBuilder() + .object_store( + prefix="/test/prefix", + object_key="assets-test.csv", + ) + .gcs( + service_account_json="test-service-account-json", + project_id="test-project-id", + bucket="my-bucket", + ) + .assets_semantics( + input_handling=AssetInputHandling.UPDATE, + delta_handling=AssetDeltaHandling.FULL_REPLACEMENT, + removal_type=AssetRemovalType.PURGE, + ) + .options( + remove_attributes=[Asset.CERTIFICATE_STATUS, Asset.ANNOUNCEMENT_TYPE], + fail_on_errors=True, + field_separator=",", + batch_size=20, + ) + ).to_workflow() + + request_json_gcs = loads(relational_assets_builder_gcs.to_json()) + assert request_json_gcs == load_json(RELATIONAL_ASSETS_BUILDER_GCS) + + +def test_oracle_crawler(mock_package_env, client: AtlanClient): + oracle_crawler_basic = ( + OracleCrawler( + client=client, + connection_name="test-oracle-conn", + admin_roles=["admin-guid-1234"], + admin_groups=None, + admin_users=None, + ) + .direct(hostname="test-hostname", port=1234) + .basic_auth( + username="test-username", + password="test-password", + sid="test-sid", + database_name="test-db", + ) + .include(assets={"t1": ["t11", "t12", "t13"]}) + .exclude(assets={"t2": ["t21", "t22", "t23"]}) + .exclude_regex("TEST*") + .jdbc_internal_methods(True) + .source_level_filtering(True) + .to_workflow() + ) + request_json = loads(oracle_crawler_basic.to_json()) + assert_workflow_equal(request_json, load_json(ORACLE_CRAWLER_BASIC)) + + oracle_crawler_offline = ( + OracleCrawler( + client=client, + connection_name="test-oracle-conn", + admin_roles=["admin-guid-1234"], + admin_groups=None, + admin_users=None, + ) + .s3(bucket_name="test-bucket", bucket_prefix="test-prefix") + .to_workflow() + ) + request_json = loads(oracle_crawler_offline.to_json()) + assert_workflow_equal(request_json, load_json(ORACLE_CRAWLER_OFFLINE)) + + oracle_crawler_basic_agent = ( + OracleCrawler( + client=client, + connection_name="test-oracle-conn", + admin_roles=["admin-guid-1234"], + admin_groups=None, + admin_users=None, + ) + .agent_config( + hostname="test.oracle.com", + port=1234, + auth_type=OracleCrawler.AuthType.BASIC, + default_db_name="test-db", + sid="test-sid", + agent_name="test-agent", + aws_region="us-east-1", + aws_auth_method=OracleCrawler.AwsAuthMethod.IAM_ASSUME_ROLE, + secret_store=OracleCrawler.SecretStore.AWS_SECRET_MANAGER, + secret_path="some/test/path", + agent_custom_config={"test": "config"}, + ) + .include(assets={"t1": ["t11", "t12", "t13"]}) + .exclude(assets={"t2": ["t21", "t22", "t23"]}) + .jdbc_internal_methods(True) + .source_level_filtering(False) + .to_workflow() + ) + request_json = loads(oracle_crawler_basic_agent.to_json()) + assert_workflow_equal(request_json, load_json(ORACLE_CRAWLER_BASIC_AGENT)) + + oracle_crawler_kerberos_agent = ( + OracleCrawler( + client=client, + connection_name="test-oracle-conn", + admin_roles=["admin-guid-1234"], + admin_groups=None, + admin_users=None, + ) + .agent_config( + hostname="test.oracle.com", + port=1234, + auth_type=OracleCrawler.AuthType.KERBEROS, + default_db_name="test-db", + sid="test-sid", + agent_name="test-agent", + principal="test-principal", + secret_store=OracleCrawler.SecretStore.SECRET_INJECTION_ENV, + user_env_var="test-user-env", + password_env_var="test-pass-env", + agent_custom_config={"test": "config"}, + ) + .include(assets={"t1": ["t11", "t12", "t13"]}) + .exclude(assets={"t2": ["t21", "t22", "t23"]}) + .jdbc_internal_methods(True) + .source_level_filtering(False) + .to_workflow() + ) + request_json = loads(oracle_crawler_kerberos_agent.to_json()) + assert_workflow_equal(request_json, load_json(ORACLE_CRAWLER_KERBEROS_AGENT)) + + +def test_lineage_builder(mock_package_env): + lineage_builder_s3 = ( + LineageBuilder() + .object_store(prefix="text-prefix", object_key="test-object-key") + .s3( + access_key="test-access-key", + secret_key="test-secret-key", + region="test-region", + bucket="test-bucket", + ) + .options( + input_handling=AssetInputHandling.UPSERT, + fail_on_errors=True, + case_sensitive_match=False, + field_separator=",", + batch_size=25, + ) + ).to_workflow() + + request_json = loads(lineage_builder_s3.to_json()) + assert_workflow_equal(request_json, load_json(LINEAGE_BUILDER_S3)) + + lineage_builder_gcs = ( + LineageBuilder() + .object_store(prefix="text-prefix", object_key="test-object-key") + .gcs( + project_id="test-project-id", + service_account_json="test-service-account-json", + bucket="test-bucket", + ) + .options( + input_handling=AssetInputHandling.UPSERT, + fail_on_errors=True, + case_sensitive_match=False, + field_separator=",", + batch_size=25, + ) + ).to_workflow() + + request_json = loads(lineage_builder_gcs.to_json()) + assert_workflow_equal(request_json, load_json(LINEAGE_BUILDER_GCS)) + + lineage_builder_adls = ( + LineageBuilder() + .object_store(prefix="text-prefix", object_key="test-object-key") + .adls( + client_id="test-client-id", + client_secret="test-client-secret", + tenant_id="test-tenant-id", + account_name="test-account-name", + container="test-container", + ) + .options( + input_handling=AssetInputHandling.UPSERT, + fail_on_errors=True, + case_sensitive_match=False, + field_separator=",", + batch_size=25, + ) + ).to_workflow() + + request_json = loads(lineage_builder_adls.to_json()) + assert_workflow_equal(request_json, load_json(LINEAGE_BUILDER_ADLS)) + + +def test_lineage_generator_nt(mock_package_env): + lineage_generator_default = ( + LineageGenerator().config( + source_asset_type=LineageGenerator.SourceAssetType.MongoDBCollection, + source_qualified_name="mongo/qn", + target_asset_type=LineageGenerator.TargetAssetType.View, + target_qualified_name="view/qn", + ) + ).to_workflow() + + request_json = loads(lineage_generator_default.to_json()) + assert_workflow_equal(request_json, load_json(LINEAGE_GENERATOR_DEFAULT)) + + lineage_generator_full = ( + LineageGenerator().config( + source_asset_type=LineageGenerator.SourceAssetType.MongoDBCollection, + source_qualified_name="mongo/qn", + target_asset_type=LineageGenerator.TargetAssetType.View, + target_qualified_name="view/qn", + case_sensitive_match=True, + match_on_schema=True, + output_type=LineageGenerator.OutputType.DELETE, + generate_on_child_assets=True, + regex_match="t1", + regex_replace="t2", + regex_match_schema="t3", + regex_replace_schema="t4", + regex_match_schema_name="t5", + regex_replace_schema_name="t6", + match_prefix="t7", + match_suffix="t8", + file_advanced_seperator="t9", + file_advanced_position="10", + process_connection_qn="test/qn", + ) + ).to_workflow() + + request_json = loads(lineage_generator_full.to_json()) + assert_workflow_equal(request_json, load_json(LINEAGE_GENERATOR_FULL)) + + +def test_api_token_connection_admin(mock_package_env): + token_connection_admin = ( + APITokenConnectionAdmin() + .config( + connection_qualified_name="default/snowflake/1234567890", + api_token_guid="92588c67-5ddf-4a45-8b5c-dd92f4b84e99", + ) + .to_workflow() + ) + request_json = loads(token_connection_admin.to_json()) + assert_workflow_equal(request_json, load_json(API_TOKEN_CONNECTION_ADMIN)) + + +@pytest.mark.parametrize( + "test_assets", + [ + "abc", + 123, + {"abc": 123}, + [123], + NonSerializable(), + [NonSerializable()], + {"abc": NonSerializable()}, + ], +) +def test_wrong_hierarchical_filter_raises_invalid_req_err( + test_assets, mock_package_env, client: AtlanClient +): + with pytest.raises( + InvalidRequestError, + match=INVALID_REQ_ERROR, + ): + SnowflakeCrawler( + client=client, + connection_name="test-snowflake-conn", + admin_roles=["admin-guid-1234"], + admin_groups=None, + admin_users=None, + ).include(assets=test_assets) + + +@pytest.mark.parametrize( + "test_projects", + [[NonSerializable()], NonSerializable()], +) +def test_wrong_flat_filter_raises_invalid_req_err( + test_projects, mock_package_env, client: AtlanClient +): + with pytest.raises( + InvalidRequestError, + match=INVALID_REQ_ERROR, + ): + TableauCrawler( + client=client, + connection_name="test-tableau-conn", + admin_roles=["admin-guid-1234"], + admin_groups=None, + admin_users=None, + ).include(projects=test_projects) + + +@pytest.mark.parametrize( + "test_assets", + [NonSerializable(), [NonSerializable()]], +) +def test_wrong_glue_package_filter_raises_invalid_req_err( + test_assets, mock_package_env, client: AtlanClient +): + with pytest.raises( + InvalidRequestError, + match=INVALID_REQ_ERROR, + ): + GlueCrawler( + client=client, + connection_name="test-glue-conn", + admin_roles=["admin-guid-1234"], + admin_groups=None, + admin_users=None, + ).include(assets=test_assets) diff --git a/tests_v9/unit/test_query_client.py b/tests_v9/unit/test_query_client.py new file mode 100644 index 000000000..4802c435c --- /dev/null +++ b/tests_v9/unit/test_query_client.py @@ -0,0 +1,160 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +""" +Unit tests for query client — ported from tests/unit/test_query_client.py. + +Uses v9 QueryRequest/QueryResponse (msgspec) where available. The QueryClient +itself is legacy; tests verify that v9 request models work with the client. +""" + +from pathlib import Path +from unittest.mock import Mock, patch + +import pytest + +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.client.query import V9QueryClient as QueryClient +from pyatlan_v9.errors import ApiError, InvalidRequestError, LogicError + +# v9 models +from pyatlan_v9.model.query import QueryRequest, QueryResponse + +QUERY_RESPONSES = ( + Path(__file__).parent.parent.parent + / "tests" + / "unit" + / "data" + / "query_responses.txt" +) + + +@pytest.fixture(autouse=True) +def set_env(monkeypatch): + monkeypatch.setenv("ATLAN_BASE_URL", "https://name.atlan.com") + monkeypatch.setenv("ATLAN_API_KEY", "abkj") + + +@pytest.fixture() +def client(): + return AtlanClient() + + +@pytest.fixture() +def query_request() -> QueryRequest: + return QueryRequest( + sql="test-sql", data_source_name="test-ds-name", default_schema="test-schema" + ) + + +@pytest.fixture() +def query_response() -> QueryResponse: + return QueryResponse() + + +@pytest.fixture() +def mock_session(): + with patch.object(AtlanClient, "_session") as mock_session: + lines_from_file = [] + mock_response = Mock() + mock_response.status_code = 200 + mock_response.content = "test-content" + mock_response.headers = {} + + with open(QUERY_RESPONSES, "r", encoding="utf-8") as file: + lines_from_file = [line.strip() for line in file.readlines()] + mock_response.iter_lines.return_value = lines_from_file + + # Mock the methods our streaming code expects + file_content = "\n".join(lines_from_file) + mock_response.read.return_value = file_content.encode("utf-8") + mock_response.text = file_content + + # Support both old request-style and new stream-style + mock_session.request.return_value = mock_response + + # Use Mock's context manager support for streaming + mock_session.stream.return_value.__enter__.return_value = mock_response + mock_session.stream.return_value.__exit__.return_value = None + yield mock_session + + +@pytest.mark.parametrize("test_api_caller", ["abc", None]) +def test_init_when_wrong_class_raises_exception(test_api_caller): + with pytest.raises( + InvalidRequestError, + match="ATLAN-PYTHON-400-048 Invalid parameter type for client should be ApiCaller", + ): + QueryClient(test_api_caller) + + +@pytest.mark.parametrize( + "test_request, error_msg", + [ + [None, "none is not an allowed value"], + ["123", "instance of QueryRequest expected"], + ], +) +def test_query_stream_wrong_params_raises_validation_error( + test_request, error_msg, client: AtlanClient +): + with pytest.raises(ValueError) as err: + client.queries.stream(request=test_request) + assert error_msg in str(err.value) + + +@pytest.mark.parametrize( + "test_response, test_error, error_msg", + [ + [["invalid data"], LogicError, "Unable to deserialize value"], + [["data: invalid data"], ApiError, "Invalid response object from API"], + ], +) +def test_stream_get_raises_error( + client: AtlanClient, + query_request: QueryRequest, + test_response, + test_error, + error_msg, + mock_session, +): + mock_response = Mock() + mock_response.status_code = 200 + mock_response.content = "test-content" + mock_response.headers = {} + mock_response.iter_lines.return_value = test_response + + # Mock the methods our streaming code expects + file_content = "\n".join(test_response) + mock_response.read.return_value = file_content.encode("utf-8") + mock_response.text = file_content + + # Support both old request-style and new stream-style + mock_session.request.return_value = mock_response + + # Use Mock's context manager support for streaming + mock_session.stream.return_value.__enter__.return_value = mock_response + mock_session.stream.return_value.__exit__.return_value = None + + with pytest.raises(test_error) as err: + client.queries.stream(request=query_request) + assert error_msg in str(err.value) + + +def test_stream_get_when_given_request( + client: AtlanClient, + query_request: QueryRequest, + mock_session, +): + response = client.queries.stream(request=query_request) + assert response.rows + assert len(response.rows) == 14 + assert response.columns + assert len(response.columns) == 7 + assert response.request_id + # Last event is an error + assert response.query_id is None + assert response.error_name + assert response.error_code + assert response.error_message + assert response.details diff --git a/tests_v9/unit/test_search_log_search.py b/tests_v9/unit/test_search_log_search.py new file mode 100644 index 000000000..4e0dc754e --- /dev/null +++ b/tests_v9/unit/test_search_log_search.py @@ -0,0 +1,192 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +""" +Unit tests for search log search using v9 (msgspec) request models. + +Ported from tests/unit/test_search_log_search.py — uses v9 SearchLogRequest +while keeping legacy SearchLogClient and SearchLogResults (returned by client). +""" + +from datetime import datetime, timezone +from json import load +from pathlib import Path +from unittest.mock import Mock, patch + +import pytest + +from pyatlan.client.common import ApiCaller +from pyatlan.client.common.search_log import LOGGER +from pyatlan_v9.client.search_log import V9SearchLogClient as SearchLogClient +from pyatlan_v9.errors import InvalidRequestError +from pyatlan_v9.model.enums import SortOrder + +# v9 request models (msgspec) — SortItem is re-exported from legacy +from pyatlan_v9.model.search import SortItem + +# V9 result model — client returns this; needed for patching thresholds +# v9 request model +from pyatlan_v9.model.search_log import SearchLogRequest, SearchLogResults + +SEARCH_RESPONSES_DIR = ( + Path(__file__).parent.parent.parent / "tests" / "unit" / "data" / "search_responses" +) +SEARCH_LOGS_JSON = "search_log_search_paging.json" + + +@pytest.fixture(autouse=True) +def set_env(monkeypatch): + monkeypatch.setenv("ATLAN_BASE_URL", "https://name.atlan.com") + monkeypatch.setenv("ATLAN_API_KEY", "abkj") + + +@pytest.fixture(scope="module") +def mock_api_caller(): + return Mock(spec=ApiCaller) + + +@pytest.fixture() +def search_logs_json(): + def load_json(filename): + with (SEARCH_RESPONSES_DIR / filename).open() as input_file: + return load(input_file) + + return load_json(SEARCH_LOGS_JSON) + + +def _assert_search_log_results(results, response_json, sorts, bulk=False): + for log in results: + assert log.user_name == response_json["logs"][0]["userName"] + assert log.user_agent == response_json["logs"][0]["userAgent"] + assert log.ip_address == response_json["logs"][0]["ipAddress"] + assert log.host == response_json["logs"][0]["host"] + expected_timestamp = datetime.fromtimestamp( + response_json["logs"][0]["timestamp"] / 1000, tz=timezone.utc + ) + assert log.timestamp == expected_timestamp + assert log.entity_guids_all == response_json["logs"][0]["entityGuidsAll"] + + assert results.count == response_json["approximateCount"] + assert results._bulk == bulk + assert results._criteria.dsl.sort == sorts + + +@patch.object(LOGGER, "debug") +def test_search_log_pagination(mock_logger, mock_api_caller, search_logs_json): + client = SearchLogClient(mock_api_caller) + mock_api_caller._call_api.side_effect = [search_logs_json, {}] + + # Test default pagination + search_log_request = SearchLogRequest.views_by_guid( + guid="some-guid", + size=2, + exclude_users=["atlansupport"], + ) + + response = client.search(criteria=search_log_request, bulk=False) + expected_sorts = [ + SortItem(field="timestamp", order=SortOrder.ASCENDING), + SortItem(field="entityGuidsAll", order=SortOrder.ASCENDING), + ] + + _assert_search_log_results(response, search_logs_json, expected_sorts) + assert mock_api_caller._call_api.call_count == 2 + assert mock_logger.call_count == 0 + mock_api_caller._call_api.reset_mock() + + # Test bulk pagination + mock_api_caller._call_api.side_effect = [search_logs_json, {}] + response = client.search(criteria=search_log_request, bulk=True) + expected_sorts = [ + SortItem(field="createdAt", order=SortOrder.ASCENDING), + SortItem(field="entityGuidsAll", order=SortOrder.ASCENDING), + ] + + _assert_search_log_results(response, search_logs_json, expected_sorts, bulk=True) + # The call count will be 2 because both + # log entries are processed in the first API call. + # In the second API call, self._log_entries + # becomes 0, which breaks the pagination. + # This differs from offset-based pagination + # where an additional API call is needed + # to verify if the results are empty + assert mock_api_caller._call_api.call_count == 2 + assert mock_logger.call_count == 1 + assert ( + "Search log bulk search option is enabled." + in mock_logger.call_args_list[0][0][0] + ) + mock_logger.reset_mock() + mock_api_caller._call_api.reset_mock() + + # Test automatic bulk search conversion when exceeding threshold + with patch.object(SearchLogResults, "_MASS_EXTRACT_THRESHOLD", -1): + mock_api_caller._call_api.side_effect = [ + # Extra call to re-fetch the first page + # results with updated timestamp sorting + search_logs_json, + search_logs_json, + {}, + ] + search_log_request = SearchLogRequest.views_by_guid( # + guid="some-guid", + size=1, + exclude_users=["atlansupport"], + ) + response = client.search(criteria=search_log_request) + _assert_search_log_results( + response, search_logs_json, expected_sorts, bulk=False + ) + assert mock_logger.call_count == 1 + assert mock_api_caller._call_api.call_count == 3 + assert ( + "Result size (%s) exceeds threshold (%s)" + in mock_logger.call_args_list[0][0][0] + ) + mock_logger.reset_mock() + mock_api_caller._call_api.reset_mock() + + with patch.object(SearchLogResults, "_MASS_EXTRACT_THRESHOLD", -1): + mock_api_caller._call_api.side_effect = [search_logs_json] + # Test exception for bulk=False with user-defined sorting and results exceeding the threshold + search_log_request = SearchLogRequest.views_by_guid( + guid="some-guid", + size=1, + sort=[SortItem(field="some-sort1", order=SortOrder.ASCENDING)], + exclude_users=["atlansupport"], + ) + with pytest.raises( + InvalidRequestError, + match=( + "ATLAN-PYTHON-400-067 Unable to execute " + "search log bulk search with user-defined sorting options. " + "Suggestion: Please ensure that no sorting options are " + "included in your search log search request when performing a bulk search." + ), + ): + client.search(criteria=search_log_request, bulk=False) + assert mock_api_caller._call_api.call_count == 1 + + mock_logger.reset_mock() + mock_api_caller._call_api.reset_mock() + # Test exception for bulk=True with user-defined sorting + search_log_request = SearchLogRequest.views_by_guid( + guid="some-guid", + size=1, + sort=[SortItem(field="some-sort2", order=SortOrder.ASCENDING)], + exclude_users=["atlansupport"], + ) + with pytest.raises( + InvalidRequestError, + match=( + "ATLAN-PYTHON-400-067 Unable to execute " + "search log bulk search with user-defined sorting options. " + "Suggestion: Please ensure that no sorting options are " + "included in your search log search request when performing a bulk search." + ), + ): + client.search(criteria=search_log_request, bulk=True) + assert mock_api_caller._call_api.call_count == 0 + + mock_logger.reset_mock() + mock_api_caller._call_api.reset_mock() diff --git a/tests_v9/unit/test_search_model.py b/tests_v9/unit/test_search_model.py new file mode 100644 index 000000000..9ac0d0d53 --- /dev/null +++ b/tests_v9/unit/test_search_model.py @@ -0,0 +1,1893 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2022 Atlan Pte. Ltd. +from datetime import datetime +from re import escape +from typing import Dict, Literal, Set, Union + +import pytest + +from pyatlan_v9.model.audit import AuditSearchRequest +from pyatlan_v9.model.enums import AtlanConnectorType, CertificateStatus, SortOrder +from pyatlan_v9.model.search import ( + DSL, + Bool, + Exists, + Fuzzy, + IndexSearchRequest, + Match, + MatchAll, + MatchNone, + MatchPhrase, + Prefix, + Range, + Regexp, + SortItem, + Term, + TermAttributes, + Terms, + Wildcard, + with_active_category, + with_active_glossary, + with_active_term, +) +from pyatlan_v9.model.search_log import SearchLogRequest +from tests.unit.model.constants import ( + GLOSSARY_CATEGORY_NAME, + GLOSSARY_NAME, + GLOSSARY_QUALIFIED_NAME, + GLOSSARY_TERM_NAME, +) + +NOW = datetime.now() +NOW_TIMESTAMP = int(NOW.timestamp() * 1000) +VALUES_BY_TYPE: Dict[Union[type, object], Union[str, datetime, object]] = { + str: "abc", + bool: True, + int: 1, + float: 1.0, + datetime: NOW, + Literal["ACTIVE", "DELETED", "PURGED"]: "ACTIVE", + AtlanConnectorType: AtlanConnectorType.SNOWFLAKE, + CertificateStatus: CertificateStatus.VERIFIED, +} + + +_STRICT_BUILTINS = {"str": str, "bool": bool, "int": int, "float": float} + + +def _value_for_type(t): + """Resolve a value for *t*, mapping pydantic Strict* types to their builtin counterparts.""" + if t in VALUES_BY_TYPE: + return VALUES_BY_TYPE[t] + name = getattr(t, "__name__", "") + if name.startswith("Strict"): + builtin = _STRICT_BUILTINS.get(name[len("Strict") :].lower()) + if builtin in VALUES_BY_TYPE: + return VALUES_BY_TYPE[builtin] + raise KeyError(t) + + +INCOMPATIPLE_QUERY: Dict[type, Set[TermAttributes]] = { + Wildcard: { + TermAttributes.CONNECTOR_NAME, + TermAttributes.HAS_LINEAGE, + TermAttributes.UPDATE_TIME_AS_TIMESTAMP, + TermAttributes.CREATE_TIME_AS_TIMESTAMP, + TermAttributes.POPULARITY_SCORE, + TermAttributes.CERTIFICATE_STATUS, + }, + Regexp: { + TermAttributes.CONNECTOR_NAME, + TermAttributes.HAS_LINEAGE, + TermAttributes.UPDATE_TIME_AS_TIMESTAMP, + TermAttributes.CREATE_TIME_AS_TIMESTAMP, + TermAttributes.POPULARITY_SCORE, + TermAttributes.CERTIFICATE_STATUS, + }, + Fuzzy: { + TermAttributes.CONNECTOR_NAME, + TermAttributes.HAS_LINEAGE, + TermAttributes.UPDATE_TIME_AS_TIMESTAMP, + TermAttributes.CREATE_TIME_AS_TIMESTAMP, + TermAttributes.POPULARITY_SCORE, + TermAttributes.CERTIFICATE_STATUS, + }, + Prefix: { + TermAttributes.CONNECTOR_NAME, + TermAttributes.HAS_LINEAGE, + TermAttributes.UPDATE_TIME_AS_TIMESTAMP, + TermAttributes.CREATE_TIME_AS_TIMESTAMP, + TermAttributes.POPULARITY_SCORE, + TermAttributes.CERTIFICATE_STATUS, + }, + Term: { + TermAttributes.POPULARITY_SCORE, + }, +} + + +@pytest.mark.parametrize( + "parameters, expected", + [ + ( + {}, + "__init__() missing 2 required positional arguments: 'field' and 'value'", + ), + ( + [ + {"field": "bob"}, + "__init__() missing 1 required positional argument: 'value'", + ] + ), + ( + [ + {"value": "bob"}, + "__init__() missing 1 required positional argument: 'field'", + ] + ), + ], +) +def test_term_without_parameters_value_raises_exception(parameters, expected): + """Test that Term raises TypeError when required fields are missing.""" + with pytest.raises( + TypeError, + match=escape(expected), + ): + Term(**parameters) + + +@pytest.mark.parametrize( + "parameters, expected", + [ + ( + {"field": "name", "value": NOW}, + {"term": {"name": {"value": int(NOW.timestamp() * 1000)}}}, + ), + ({"field": "name", "value": "dave"}, {"term": {"name": {"value": "dave"}}}), + ( + {"field": "name", "value": "dave", "case_insensitive": True}, + {"term": {"name": {"value": "dave", "case_insensitive": True}}}, + ), + ( + {"field": "name", "value": "dave", "case_insensitive": False}, + {"term": {"name": {"value": "dave", "case_insensitive": False}}}, + ), + ( + {"field": "name", "value": "dave", "boost": 0.9}, + {"term": {"name": {"value": "dave", "boost": 0.9}}}, + ), + ], +) +def test_term_to_dict(parameters, expected): + """Test that Term.to_dict() produces the expected Elasticsearch query dict.""" + t = Term(**parameters) + assert t.to_dict() == expected + + +@pytest.mark.parametrize( + "must, should, must_not, filter, boost, minimum_should_match, expected", + [ + ( + [], + [], + [], + [], + None, + None, + {"bool": {}}, + ), + ( + [Term(field="name", value="Bob")], + [], + [], + [], + None, + None, + {"bool": {"must": [{"term": {"name": {"value": "Bob"}}}]}}, + ), + ( + [Term(field="name", value="Bob"), Term(field="name", value="Dave")], + [], + [], + [], + None, + None, + { + "bool": { + "must": [ + {"term": {"name": {"value": "Bob"}}}, + {"term": {"name": {"value": "Dave"}}}, + ] + } + }, + ), + ( + [Term(field="name", value="Bob")], + [Term(field="name", value="Dave")], + [], + [], + None, + None, + { + "bool": { + "must": [{"term": {"name": {"value": "Bob"}}}], + "should": [ + {"term": {"name": {"value": "Dave"}}}, + ], + } + }, + ), + ( + [], + [], + [Term(field="name", value="Bob")], + [], + None, + None, + {"bool": {"must_not": [{"term": {"name": {"value": "Bob"}}}]}}, + ), + ( + [], + [], + [], + [Term(field="name", value="Bob")], + None, + None, + {"bool": {"filter": [{"term": {"name": {"value": "Bob"}}}]}}, + ), + ( + [Term(field="name", value="Bob")], + [], + [], + [], + 1.0, + None, + {"bool": {"boost": 1.0, "must": [{"term": {"name": {"value": "Bob"}}}]}}, + ), + ( + [Term(field="name", value="Bob")], + [], + [], + [], + None, + 3, + { + "bool": { + "minimum_should_match": 3, + "must": [{"term": {"name": {"value": "Bob"}}}], + } + }, + ), + ], +) +def test_bool_to_dict_without_optional_fields( + must, should, must_not, filter, boost, minimum_should_match, expected +): + """Test that Bool.to_dict() produces expected output for various combinations.""" + assert ( + Bool( + must=must, + should=should, + must_not=must_not, + filter=filter, + boost=boost, + minimum_should_match=minimum_should_match, + ).to_dict() + == expected + ) + + +def test_dsl_without_query_and_post_filter_raises_validation_error(): + """Test that DSL raises ValueError when neither query nor post_filter is provided.""" + with pytest.raises(ValueError): + DSL() + + +def test_dsl(): + """Test DSL JSON serialization produces correct output.""" + dsl = DSL( + query=Term(field="__typeName.keyword", value="Schema"), + post_filter=Term(field="databaseName.keyword", value="ATLAN_SAMPLE_DATA"), + ) + assert ( + dsl.json(by_alias=True, exclude_none=True) + == '{"from": 0, "size": 300, "aggregations": {}, "track_total_hits": true, ' + '"post_filter": {"term": {"databaseName.keyword": ' + '{"value": "ATLAN_SAMPLE_DATA"}}}, "query": {"term": ' + '{"__typeName.keyword": {"value": "Schema"}}}, "sort": []}' + ) + + +def test_index_search_request(): + """Test IndexSearchRequest JSON serialization produces correct output.""" + dsl = DSL( + query=Term(field="__typeName.keyword", value="Schema"), + post_filter=Term(field="databaseName.keyword", value="ATLAN_SAMPLE_DATA"), + ) + request = IndexSearchRequest(dsl=dsl, attributes=["schemaName", "databaseName"]) + assert ( + request.json(by_alias=True, exclude_none=True) + == '{"attributes": ["schemaName", "databaseName"],' + ' "dsl": {"from": 0, "size": 300, "aggregations": {}, "track_total_hits": true, ' + '"post_filter": {"term": {"databaseName.keyword": ' + '{"value": "ATLAN_SAMPLE_DATA"}}}, "query": {"term": {"__typeName.keyword": {"value": "Schema"}}}, ' + '"sort": [{"__guid": {"order": "asc"}}]}, "relationAttributes": [], "includeRelationshipAttributes": false, ' + '"requestMetadata": {"saveSearchLog": false, "utmTags": ["project_sdk_python"]}}' + ) + + +def test_index_search_request_with_enable_full_restriction(): + """Test IndexSearchRequest with enableFullRestriction parameter.""" + dsl = DSL( + query=Term(field="__typeName.keyword", value="Schema"), + post_filter=Term(field="databaseName.keyword", value="ATLAN_SAMPLE_DATA"), + ) + + # Test with enableFullRestriction=True + request = IndexSearchRequest( + dsl=dsl, + attributes=["schemaName", "databaseName"], + enable_full_restriction=True, + ) + json_str = request.json(by_alias=True, exclude_none=True) + + # Verify the parameter is serialized correctly + assert "enableFullRestriction" in json_str + assert '"enableFullRestriction": true' in json_str + + # Test with enableFullRestriction=False + request_false = IndexSearchRequest( + dsl=DSL( + query=Term(field="__typeName.keyword", value="Schema"), + post_filter=Term(field="databaseName.keyword", value="ATLAN_SAMPLE_DATA"), + ), + attributes=["schemaName"], + enable_full_restriction=False, + ) + json_str_false = request_false.json(by_alias=True, exclude_none=True) + assert '"enableFullRestriction": false' in json_str_false + + # Test without the parameter (should not appear in JSON) + request_none = IndexSearchRequest( + dsl=DSL( + query=Term(field="__typeName.keyword", value="Schema"), + post_filter=Term(field="databaseName.keyword", value="ATLAN_SAMPLE_DATA"), + ), + attributes=["schemaName"], + ) + json_str_none = request_none.json(by_alias=True, exclude_none=True) + assert "enableFullRestriction" not in json_str_none + + +def test_audit_search_request(): + """Test AuditSearchRequest JSON serialization produces correct output.""" + dsl = DSL( + query=Term(field="__typeName.keyword", value="Schema"), + post_filter=Term(field="databaseName.keyword", value="ATLAN_SAMPLE_DATA"), + ) + request = AuditSearchRequest(dsl=dsl, attributes=["schemaName", "databaseName"]) + assert ( + request.json(by_alias=True, exclude_none=True) + == '{"attributes": ["schemaName", "databaseName"],' + ' "dsl": {"from": 0, "size": 300, "aggregations": {}, "track_total_hits": true, ' + '"post_filter": {"term": {"databaseName.keyword": ' + '{"value": "ATLAN_SAMPLE_DATA"}}}, "query": {"term": {"__typeName.keyword": {"value": "Schema"}}}, ' + '"sort": [{"entityId": {"order": "asc"}}]}}' + ) + + +def test_search_log_request(): + """Test SearchLogRequest JSON serialization produces correct output.""" + dsl = DSL( + query=Term(field="__typeName.keyword", value="Schema"), + post_filter=Term(field="databaseName.keyword", value="ATLAN_SAMPLE_DATA"), + ) + request = SearchLogRequest(dsl=dsl, attributes=["schemaName", "databaseName"]) + assert ( + request.json(by_alias=True, exclude_none=True) + == '{"attributes": ["schemaName", "databaseName"],' + ' "dsl": {"from": 0, "size": 300, "aggregations": {}, "track_total_hits": true, ' + '"post_filter": {"term": {"databaseName.keyword": ' + '{"value": "ATLAN_SAMPLE_DATA"}}}, "query": {"term": {"__typeName.keyword": {"value": "Schema"}}}, ' + '"sort": [{"entityGuidsAll": {"order": "asc"}}]}}' + ) + + +def test_adding_terms_results_in_must_bool(): + """Test that adding two Terms produces a Bool with both in filter.""" + term_1 = Term(field="name", value="Bob") + term_2 = Term(field="name", value="Dave") + result = term_1 + term_2 + assert isinstance(result, Bool) + assert len(result.filter) == 2 + assert term_1 in result.filter and term_2 in result.filter + + +def test_anding_terms_results_in_must_bool(): + """Test that ANDing two Terms produces a Bool with both in filter.""" + term_1 = Term(field="name", value="Bob") + term_2 = Term(field="name", value="Dave") + result = term_1 & term_2 + assert isinstance(result, Bool) + assert len(result.filter) == 2 + assert term_1 in result.filter and term_2 in result.filter + + +def test_oring_terms_results_in_must_bool(): + """Test that ORing two Terms produces a Bool with both in should.""" + term_1 = Term(field="name", value="Bob") + term_2 = Term(field="name", value="Dave") + result = term_1 | term_2 + assert isinstance(result, Bool) + assert len(result.should) == 2 + assert term_1 in result.should and term_2 in result.should + + +def test_negate_terms_results_must_not_bool(): + """Test that negating a Term produces a Bool with it in must_not.""" + term_1 = Term(field="name", value="Bob") + result = ~term_1 + assert isinstance(result, Bool) + assert len(result.must_not) == 1 + assert term_1 in result.must_not + + +@pytest.mark.parametrize( + "q1, q2, expected", + [ + ( + Bool(filter=[Term(field="name", value="Bob")]), + Bool(filter=[Term(field="name", value="Dave")]), + { + "bool": { + "filter": [ + {"term": {"name": {"value": "Bob"}}}, + {"term": {"name": {"value": "Dave"}}}, + ] + } + }, + ), + ( + Term(field="name", value="Bob"), + Bool(filter=[Term(field="name", value="Fred")]), + { + "bool": { + "filter": [ + {"term": {"name": {"value": "Fred"}}}, + {"term": {"name": {"value": "Bob"}}}, + ] + } + }, + ), + ( + Bool(filter=[Term(field="name", value="Fred")]), + Term(field="name", value="Bob"), + { + "bool": { + "filter": [ + {"term": {"name": {"value": "Fred"}}}, + {"term": {"name": {"value": "Bob"}}}, + ] + } + }, + ), + ], +) +def test_add_boolean(q1, q2, expected): + """Test Bool addition combines filter clauses correctly.""" + b = q1 + q2 + assert b.to_dict() == expected + + +def test_match_none_to_dict(): + """Test MatchNone produces correct dict.""" + assert MatchNone().to_dict() == {"match_none": {}} + + +def test_match_none_plus_other_is_match_none(): + """Test that MatchNone + any query is still MatchNone.""" + assert MatchNone() + Term(field="name", value="bob") == MatchNone() + + +def test_match_one_or_other_is_other(): + """Test that MatchNone | any query is the other query.""" + assert MatchNone() | Term(field="name", value="bob") == Term( + field="name", value="bob" + ) + + +def test_nagate_match_one_is_match_all(): + """Test that negating MatchNone produces MatchAll.""" + assert ~MatchNone() == MatchAll() + + +@pytest.mark.parametrize( + "boost, expected", + [(None, {"match_all": {}}), (1.2, {"match_all": {"boost": 1.2}})], +) +def test_match_all_to_dict(boost, expected): + """Test MatchAll produces correct dict with optional boost.""" + assert MatchAll(boost=boost).to_dict() == expected + + +def test_match_all_and_other_is_other(): + """Test that MatchAll & any query is the other query.""" + assert MatchAll() & Term(field="name", value="bob") == Term( + field="name", value="bob" + ) + + +def test_match_all_or_other_is_match_all(): + """Test that MatchAll | any query is still MatchAll.""" + assert MatchAll() | Term(field="name", value="bob") == MatchAll() + + +def test_negate_match_all_is_match_none(): + """Test that negating MatchAll produces MatchNone.""" + assert ~MatchAll() == MatchNone() + + +@pytest.mark.parametrize( + "q1, q2, expected", + [ + ( + Term(field="name", value="Bob"), + Bool(must=[Term(field="name", value="Fred")]), + { + "bool": { + "should": [ + {"bool": {"must": [{"term": {"name": {"value": "Fred"}}}]}}, + {"term": {"name": {"value": "Bob"}}}, + ] + } + }, + ) + ], +) +def test_bool_or(q1, q2, expected): + """Test Bool OR combines queries into should clause correctly.""" + b = q1 | q2 + assert b.to_dict() == expected + + +def test_negate_empty_bool_is_match_none(): + """Test that negating an empty Bool produces MatchNone.""" + assert ~Bool() == MatchNone() + + +@pytest.mark.parametrize( + "q, expected", + [ + ( + Bool(must=[Term(field="name", value="Fred")]), + {"bool": {"must_not": [{"term": {"name": {"value": "Fred"}}}]}}, + ), + ( + Bool(should=[Term(field="name", value="Fred")]), + {"bool": {"must_not": [{"term": {"name": {"value": "Fred"}}}]}}, + ), + ( + Bool( + must=[ + Term(field="name", value="Fred"), + Term(field="name", value="Dave"), + ] + ), + { + "bool": { + "should": [ + {"bool": {"must_not": [{"term": {"name": {"value": "Fred"}}}]}}, + {"bool": {"must_not": [{"term": {"name": {"value": "Dave"}}}]}}, + ] + } + }, + ), + ], +) +def test_negate_bool(q, expected): + """Test Bool negation produces correct must_not clauses.""" + b = ~q + assert b.to_dict() == expected + + +@pytest.mark.parametrize( + "q1, q2, expected", + [ + ( + Bool(should=[Term(field="name", value="Dave")]), + Term(field="name", value="Bob"), + { + "bool": { + "should": [{"term": {"name": {"value": "Dave"}}}], + "must": [{"term": {"name": {"value": "Bob"}}}], + "minimum_should_match": 1, + } + }, + ), + ( + Bool(should=[Term(field="name", value="Dave")]), + Bool(must=[Term(field="name", value="Bob")]), + { + "bool": { + "must": [ + {"term": {"name": {"value": "Bob"}}}, + {"term": {"name": {"value": "Dave"}}}, + ] + } + }, + ), + ], +) +def test_bool_and(q1, q2, expected): + """Test Bool AND combines queries correctly.""" + b = q1 & q2 + assert b.to_dict() == expected + + +@pytest.fixture() +def with_name(request): + """Fixture to generate with_ method names.""" + attribute = request.param + return f"with_{attribute.name.lower()}" + + +def test_terms_to_dict(): + """Test Terms query produces correct dict.""" + assert Terms(field="name", values=["john", "dave"]).to_dict() == { + "terms": {"name": ["john", "dave"]} + } + + +@pytest.mark.parametrize( + "a_class, with_name, value, field, incompatable", + [ + ( + c, + a, + _value_for_type(a.attribute_type), + a.value, + c in INCOMPATIPLE_QUERY and a in INCOMPATIPLE_QUERY[c], + ) + for a in TermAttributes + for c in [Term, Prefix, Regexp, Wildcard] + ], + indirect=["with_name"], +) +def test_by_methods_on_term_prefix_regexp_wildcard( + a_class, with_name, value, field, incompatable +): + """Test with_ class methods on Term, Prefix, Regexp, Wildcard.""" + if incompatable: + assert not hasattr(a_class, with_name) + else: + assert hasattr(a_class, with_name) + t = getattr(a_class, with_name)(value) + assert isinstance(t, a_class) + assert t.field == field + assert t.value == value + + +@pytest.mark.parametrize( + "with_name, field", + [(a, a.value) for a in TermAttributes], + indirect=["with_name"], +) +def test_by_methods_on_exists(with_name, field): + """Test with_ class methods on Exists query.""" + assert hasattr(Exists, with_name) + t = getattr(Exists, with_name)() + assert isinstance(t, Exists) + assert t.field == field + + +@pytest.mark.parametrize( + "gt, gte, lt, lte, boost, format, relation, timezone, expected", + [ + ( + None, + None, + None, + None, + None, + None, + None, + None, + {"range": {"Bob": {}}}, + ), + ( + 0, + None, + None, + None, + None, + None, + None, + None, + {"range": {"Bob": {"gt": 0}}}, + ), + ( + 10, + None, + None, + None, + None, + None, + None, + None, + {"range": {"Bob": {"gt": 10}}}, + ), + ( + None, + 10, + None, + None, + None, + None, + None, + None, + {"range": {"Bob": {"gte": 10}}}, + ), + ( + None, + None, + 10, + None, + None, + None, + None, + None, + {"range": {"Bob": {"lt": 10}}}, + ), + ( + None, + None, + None, + 10, + None, + None, + None, + None, + {"range": {"Bob": {"lte": 10}}}, + ), + ( + None, + None, + None, + None, + 2.0, + None, + None, + None, + {"range": {"Bob": {"boost": 2.0}}}, + ), + ( + None, + None, + None, + None, + None, + "YY/MM/DD", + None, + None, + {"range": {"Bob": {"format": "YY/MM/DD"}}}, + ), + ( + None, + None, + None, + None, + None, + None, + "CONTAINS", + None, + {"range": {"Bob": {"relation": "CONTAINS"}}}, + ), + ( + None, + None, + None, + None, + None, + None, + None, + "-01:00", + {"range": {"Bob": {"time_zone": "-01:00"}}}, + ), + ( + 1, + 2, + 3, + 4, + 2.0, + "YY/MM/DD", + "WITHIN", + "-01:00", + { + "range": { + "Bob": { + "gt": 1, + "gte": 2, + "lt": 3, + "lte": 4, + "boost": 2.0, + "format": "YY/MM/DD", + "relation": "WITHIN", + "time_zone": "-01:00", + } + } + }, + ), + ( + NOW, + 2, + 3, + 4, + 2.0, + "YY/MM/DD", + "WITHIN", + "-01:00", + { + "range": { + "Bob": { + "gt": NOW_TIMESTAMP, + "gte": 2, + "lt": 3, + "lte": 4, + "boost": 2.0, + "format": "YY/MM/DD", + "relation": "WITHIN", + "time_zone": "-01:00", + } + } + }, + ), + ], +) +def test_range_to_dict(gt, gte, lt, lte, boost, format, relation, timezone, expected): + """Test Range query produces correct dict for various parameter combinations.""" + assert ( + Range( + field="Bob", + gt=gt, + gte=gte, + lt=lt, + lte=lte, + boost=boost, + format=format, + relation=relation, + time_zone=timezone, + ).to_dict() + == expected + ) + + +@pytest.mark.parametrize( + "field, order, expected", + [ + ("name.keyword", SortOrder.ASCENDING, {"name.keyword": {"order": "asc"}}), + ("name.keyword", None, {"name.keyword": {"order": "asc"}}), + ("name.keyword", SortOrder.DESCENDING, {"name.keyword": {"order": "desc"}}), + ], +) +def test_sort_item_to_dict(field, order, expected): + """Test SortItem produces correct dict with various sort orders.""" + assert SortItem(field=field, order=order).to_dict() == expected + + +@pytest.mark.parametrize( + "field, value, fuzziness, max_expansions, prefix_length, transpositions, rewrite, expected", + [ + ( + "user", + "ki", + None, + None, + None, + None, + None, + {"fuzzy": {"user": {"value": "ki"}}}, + ), + ( + "user", + "ki", + "AUTO", + None, + None, + None, + None, + {"fuzzy": {"user": {"value": "ki", "fuzziness": "AUTO"}}}, + ), + ( + "user", + "ki", + "AUTO", + 3, + None, + None, + None, + { + "fuzzy": { + "user": {"value": "ki", "fuzziness": "AUTO", "max_expansions": 3} + } + }, + ), + ( + "user", + "ki", + "AUTO", + 3, + 0, + None, + None, + { + "fuzzy": { + "user": { + "value": "ki", + "fuzziness": "AUTO", + "max_expansions": 3, + "prefix_length": 0, + } + } + }, + ), + ( + "user", + "ki", + "AUTO", + 3, + 0, + 1, + None, + { + "fuzzy": { + "user": { + "value": "ki", + "fuzziness": "AUTO", + "max_expansions": 3, + "prefix_length": 0, + "transpositions": 1, + } + } + }, + ), + ( + "user", + "ki", + "AUTO", + 3, + 0, + 1, + "constant_score", + { + "fuzzy": { + "user": { + "value": "ki", + "fuzziness": "AUTO", + "max_expansions": 3, + "prefix_length": 0, + "transpositions": 1, + "rewrite": "constant_score", + } + } + }, + ), + ], +) +def test_fuzzy_to_dict( + field, + value, + fuzziness, + max_expansions, + prefix_length, + transpositions, + rewrite, + expected, +): + """Test Fuzzy query produces correct dict for various parameter combinations.""" + assert ( + Fuzzy( + field=field, + value=value, + fuzziness=fuzziness, + max_expansions=max_expansions, + prefix_length=prefix_length, + transpositions=transpositions, + rewrite=rewrite, + ).to_dict() + == expected + ) + + +@pytest.mark.parametrize( + "name, value, fuzziness, max_expansions, prefix_length, transpositions, rewrite, attributes, incompatable", + [ + ( + f"with_{a.name.lower()}", + "ki", + "AUTO", + 3, + 0, + 1, + "constant_score", + a, + Fuzzy in INCOMPATIPLE_QUERY and a in INCOMPATIPLE_QUERY[Fuzzy], + ) + for a in TermAttributes + ], +) +def test_fuzziness_with( + name, + value, + fuzziness, + max_expansions, + prefix_length, + transpositions, + rewrite, + attributes, + incompatable, +): + """Test Fuzzy with_ class methods for all TermAttributes.""" + if incompatable: + assert not hasattr(Fuzzy, name) + else: + assert hasattr(Fuzzy, name) + t = getattr(Fuzzy, name)( + value=value, + fuzziness=fuzziness, + max_expansions=max_expansions, + prefix_length=prefix_length, + transpositions=transpositions, + rewrite=rewrite, + ) + assert isinstance(t, Fuzzy) + assert t.field == attributes.value + + +@pytest.mark.parametrize( + "field, query, analyzer, auto_generate_synonyms_phrase_query, fuzziness, fuzzy_transpositions, fuzzy_rewrite," + "lenient, operator, minimum_should_match, zero_terms_query, max_expansions, ,prefix_length, expected", + [ + ( + "name", + "test", + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + {"match": {"name": {"query": "test"}}}, + ), + ( + "name", + "test", + "an analyzer", + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + {"match": {"name": {"query": "test", "analyzer": "an analyzer"}}}, + ), + ( + "name", + "test", + "an analyzer", + True, + None, + None, + None, + None, + None, + None, + None, + None, + None, + { + "match": { + "name": { + "query": "test", + "analyzer": "an analyzer", + "auto_generate_synonyms_phrase_query": True, + } + } + }, + ), + ( + "name", + "test", + "an analyzer", + True, + "0", + None, + None, + None, + None, + None, + None, + None, + None, + { + "match": { + "name": { + "query": "test", + "analyzer": "an analyzer", + "auto_generate_synonyms_phrase_query": True, + "fuzziness": "0", + } + } + }, + ), + ( + "name", + "test", + "an analyzer", + True, + "0", + False, + None, + None, + None, + None, + None, + None, + None, + { + "match": { + "name": { + "query": "test", + "analyzer": "an analyzer", + "auto_generate_synonyms_phrase_query": True, + "fuzziness": "0", + "fuzzy_transpositions": False, + } + } + }, + ), + ( + "name", + "test", + "an analyzer", + True, + "0", + False, + "constant_score", + None, + None, + None, + None, + None, + None, + { + "match": { + "name": { + "query": "test", + "analyzer": "an analyzer", + "auto_generate_synonyms_phrase_query": True, + "fuzziness": "0", + "fuzzy_transpositions": False, + "fuzzy_rewrite": "constant_score", + } + } + }, + ), + ( + "name", + "test", + "an analyzer", + True, + "0", + False, + "constant_score", + True, + None, + None, + None, + None, + None, + { + "match": { + "name": { + "query": "test", + "analyzer": "an analyzer", + "auto_generate_synonyms_phrase_query": True, + "fuzziness": "0", + "fuzzy_transpositions": False, + "fuzzy_rewrite": "constant_score", + "lenient": True, + } + } + }, + ), + ( + "name", + "test", + "an analyzer", + True, + "0", + False, + "constant_score", + True, + "OR", + None, + None, + None, + None, + { + "match": { + "name": { + "query": "test", + "analyzer": "an analyzer", + "auto_generate_synonyms_phrase_query": True, + "fuzziness": "0", + "fuzzy_transpositions": False, + "fuzzy_rewrite": "constant_score", + "lenient": True, + "operator": "OR", + } + } + }, + ), + ( + "name", + "test", + "an analyzer", + True, + "0", + False, + "constant_score", + True, + "OR", + 3, + None, + None, + None, + { + "match": { + "name": { + "query": "test", + "analyzer": "an analyzer", + "auto_generate_synonyms_phrase_query": True, + "fuzziness": "0", + "fuzzy_transpositions": False, + "fuzzy_rewrite": "constant_score", + "lenient": True, + "operator": "OR", + "minimum_should_match": 3, + } + } + }, + ), + ( + "name", + "test", + "an analyzer", + True, + "0", + False, + "constant_score", + True, + "OR", + 3, + "none", + None, + None, + { + "match": { + "name": { + "query": "test", + "analyzer": "an analyzer", + "auto_generate_synonyms_phrase_query": True, + "fuzziness": "0", + "fuzzy_transpositions": False, + "fuzzy_rewrite": "constant_score", + "lenient": True, + "operator": "OR", + "minimum_should_match": 3, + "zero_terms_query": "none", + } + } + }, + ), + ( + "name", + "test", + "an analyzer", + True, + "0", + False, + "constant_score", + True, + "OR", + 3, + "none", + 4, + None, + { + "match": { + "name": { + "query": "test", + "analyzer": "an analyzer", + "auto_generate_synonyms_phrase_query": True, + "fuzziness": "0", + "fuzzy_transpositions": False, + "fuzzy_rewrite": "constant_score", + "lenient": True, + "operator": "OR", + "minimum_should_match": 3, + "zero_terms_query": "none", + "max_expansions": 4, + } + } + }, + ), + ( + "name", + "test", + "an analyzer", + True, + "0", + False, + "constant_score", + True, + "OR", + 3, + "none", + 4, + 2, + { + "match": { + "name": { + "query": "test", + "analyzer": "an analyzer", + "auto_generate_synonyms_phrase_query": True, + "fuzziness": "0", + "fuzzy_transpositions": False, + "fuzzy_rewrite": "constant_score", + "lenient": True, + "operator": "OR", + "minimum_should_match": 3, + "zero_terms_query": "none", + "max_expansions": 4, + "prefix_length": 2, + } + } + }, + ), + ], +) +def test_match_to_string( + field, + query, + analyzer, + auto_generate_synonyms_phrase_query, + fuzziness, + fuzzy_transpositions, + fuzzy_rewrite, + lenient, + operator, + minimum_should_match, + zero_terms_query, + max_expansions, + prefix_length, + expected, +): + """Test Match query produces correct dict for various parameter combinations.""" + assert ( + Match( + field=field, + query=query, + analyzer=analyzer, + auto_generate_synonyms_phrase_query=auto_generate_synonyms_phrase_query, + fuzziness=fuzziness, + fuzzy_transpositions=fuzzy_transpositions, + fuzzy_rewrite=fuzzy_rewrite, + lenient=lenient, + operator=operator, + minimum_should_match=minimum_should_match, + zero_terms_query=zero_terms_query, + max_expansions=max_expansions, + prefix_length=prefix_length, + ).to_dict() + == expected + ) + + +@pytest.mark.parametrize( + "parameters, expected", + [ + ( + {"field": "name", "value": "C_*_SK"}, + {"wildcard": {"name": {"value": "C_*_SK"}}}, + ), + ( + {"field": "name", "value": "C_*_SK"}, + {"wildcard": {"name": {"value": "C_*_SK"}}}, + ), + ( + {"field": "name", "value": "C_*_SK", "case_insensitive": True}, + {"wildcard": {"name": {"value": "C_*_SK", "case_insensitive": True}}}, + ), + ( + {"field": "name", "value": "C_*_SK", "case_insensitive": False}, + {"wildcard": {"name": {"value": "C_*_SK", "case_insensitive": False}}}, + ), + ( + {"field": "name", "value": "C_*_SK", "boost": 0.9}, + {"wildcard": {"name": {"value": "C_*_SK", "boost": 0.9}}}, + ), + ], +) +def test_wildcard_to_dict(parameters, expected): + """Test Wildcard query produces correct dict for various parameter combinations.""" + wildcard = Wildcard(**parameters) + assert wildcard.to_dict() == expected + + +@pytest.mark.parametrize( + "parameters, expected", + [ + ( + {"field": "name", "value": "C_[A-Za-z0-9_]*ADDR[A-Za-z0-9_]*_SK"}, + {"regexp": {"name": {"value": "C_[A-Za-z0-9_]*ADDR[A-Za-z0-9_]*_SK"}}}, + ), + ( + {"field": "name", "value": "C_[A-Za-z0-9_]*ADDR[A-Za-z0-9_]*_SK"}, + {"regexp": {"name": {"value": "C_[A-Za-z0-9_]*ADDR[A-Za-z0-9_]*_SK"}}}, + ), + ( + { + "field": "name", + "value": "C_[A-Za-z0-9_]*ADDR[A-Za-z0-9_]*_SK", + "case_insensitive": True, + }, + { + "regexp": { + "name": { + "value": "C_[A-Za-z0-9_]*ADDR[A-Za-z0-9_]*_SK", + "case_insensitive": True, + } + } + }, + ), + ( + { + "field": "name", + "value": "C_[A-Za-z0-9_]*ADDR[A-Za-z0-9_]*_SK", + "case_insensitive": True, + "max_determinized_states": 1, + }, + { + "regexp": { + "name": { + "value": "C_[A-Za-z0-9_]*ADDR[A-Za-z0-9_]*_SK", + "case_insensitive": True, + "max_determinized_states": 1, + } + } + }, + ), + ( + { + "field": "name", + "value": "C_[A-Za-z0-9_]*ADDR[A-Za-z0-9_]*_SK", + "case_insensitive": False, + }, + { + "regexp": { + "name": { + "value": "C_[A-Za-z0-9_]*ADDR[A-Za-z0-9_]*_SK", + "case_insensitive": False, + } + } + }, + ), + ( + { + "field": "name", + "value": "C_[A-Za-z0-9_]*ADDR[A-Za-z0-9_]*_SK", + "boost": 0.9, + }, + { + "regexp": { + "name": { + "value": "C_[A-Za-z0-9_]*ADDR[A-Za-z0-9_]*_SK", + "boost": 0.9, + } + } + }, + ), + ], +) +def test_regexp_to_dict(parameters, expected): + """Test Regexp query produces correct dict for various parameter combinations.""" + regexp = Regexp(**parameters) + assert regexp.to_dict() == expected + + +@pytest.mark.parametrize( + "name, message", + [ + ( + None, + "name must not be None", + ), + ( + " ", + "name must have at least 1 non-whitespace character", + ), + ], +) +def test_with_active_glossary_when_invalid_parameter_raises_value_error(name, message): + """Test with_active_glossary raises ValueError for invalid name.""" + with pytest.raises(ValueError, match=message): + with_active_glossary(name) + + +def test_with_active_glossary(): + """Test with_active_glossary produces correct Bool filter.""" + sut = with_active_glossary(name=GLOSSARY_NAME) + + assert sut.filter + assert 3 == len(sut.filter) + term1, term2, term3 = sut.filter + assert isinstance(term1, Term) is True + assert term1.field == "__state" + assert term1.value == "ACTIVE" + assert isinstance(term2, Term) is True + assert term2.field == "__typeName.keyword" + assert term2.value == "AtlasGlossary" + assert isinstance(term3, Term) is True + assert term3.field == "name.keyword" + assert term3.value == GLOSSARY_NAME + + +@pytest.mark.parametrize( + "name, glossary_qualified_name, message", + [ + ( + None, + GLOSSARY_QUALIFIED_NAME, + "name must not be None", + ), + ( + " ", + GLOSSARY_QUALIFIED_NAME, + "name must have at least 1 non-whitespace character", + ), + ( + GLOSSARY_CATEGORY_NAME, + None, + "glossary_qualified_name must not be None", + ), + ( + GLOSSARY_CATEGORY_NAME, + " ", + "glossary_qualified_name must have at least 1 non-whitespace character", + ), + ], +) +def test_with_active_category_when_invalid_parameter_raises_value_error( + name, glossary_qualified_name, message +): + """Test with_active_category raises ValueError for invalid parameters.""" + with pytest.raises(ValueError, match=message): + with_active_category(name=name, glossary_qualified_name=glossary_qualified_name) + + +def test_with_active_category(): + """Test with_active_category produces correct Bool filter.""" + sut = with_active_category( + name=GLOSSARY_CATEGORY_NAME, glossary_qualified_name=GLOSSARY_QUALIFIED_NAME + ) + + assert sut.filter + assert 4 == len(sut.filter) + term1, term2, term3, term4 = sut.filter + assert isinstance(term1, Term) is True + assert term1.field == "__state" + assert term1.value == "ACTIVE" + assert isinstance(term2, Term) is True + assert term2.field == "__typeName.keyword" + assert term2.value == "AtlasGlossaryCategory" + assert isinstance(term3, Term) is True + assert term3.field == "name.keyword" + assert term3.value == GLOSSARY_CATEGORY_NAME + assert isinstance(term4, Term) is True + assert term4.field == "__glossary" + assert term4.value == GLOSSARY_QUALIFIED_NAME + + +@pytest.mark.parametrize( + "name, glossary_qualified_name, message", + [ + ( + None, + GLOSSARY_QUALIFIED_NAME, + "name must not be None", + ), + ( + " ", + GLOSSARY_QUALIFIED_NAME, + "name must have at least 1 non-whitespace character", + ), + ( + GLOSSARY_TERM_NAME, + None, + "glossary_qualified_name must not be None", + ), + ( + GLOSSARY_TERM_NAME, + " ", + "glossary_qualified_name must have at least 1 non-whitespace character", + ), + ], +) +def test_with_active_term_when_invalid_parameter_raises_value_error( + name, glossary_qualified_name, message +): + """Test with_active_term raises ValueError for invalid parameters.""" + with pytest.raises(ValueError, match=message): + with_active_term(name=name, glossary_qualified_name=glossary_qualified_name) + + +def test_with_active_term(): + """Test with_active_term produces correct Bool filter.""" + sut = with_active_term( + name=GLOSSARY_TERM_NAME, glossary_qualified_name=GLOSSARY_QUALIFIED_NAME + ) + + assert sut.filter + assert 4 == len(sut.filter) + term1, term2, term3, term4 = sut.filter + assert isinstance(term1, Term) is True + assert term1.field == "__state" + assert term1.value == "ACTIVE" + assert isinstance(term2, Term) is True + assert term2.field == "__typeName.keyword" + assert term2.value == "AtlasGlossaryTerm" + assert isinstance(term3, Term) is True + assert term3.field == "name.keyword" + assert term3.value == GLOSSARY_TERM_NAME + assert isinstance(term4, Term) is True + assert term4.field == "__glossary" + assert term4.value == GLOSSARY_QUALIFIED_NAME + + +def test_dsl_serialization_and_deserialization(): + """Test DSL serialization and deserialization produce consistent output.""" + dsl_through_model = DSL( + from_=0, + aggregations={ + "main_agg": { + "terms": {"field": "main_field"}, + "aggregations": { + "sub_agg_1": {"avg": {"field": "sub_field_1"}}, + "sub_agg_2": { + "date_histogram": {"field": "timestamp", "interval": "month"} + }, + }, + } + }, + size=500, + sort=[ + SortItem(field="created", order=SortOrder.ASCENDING), + SortItem(field="updated", order=SortOrder.DESCENDING), + SortItem( + field="entityId", order=SortOrder.ASCENDING, nested_path="nested_test" + ), + ], + query=Bool( + must=[ + Term(field="type.keyword", value="Schema"), + Range(field="created", gte="2025-01-01"), + Term(field="status", value="active"), + ], + should=[Term(field="category.keyword", value="Tech")], + must_not=[Term(field="archived", value="true")], + filter=[ + Bool( + must=[ + Term(field="region.keyword", value="EMEA"), + Range(field="created", lte="2025-12-31"), + ], + should=[Term(field="sub_category.keyword", value="Hardware")], + ) + ], + ), + track_total_hits=False, + ) + + raw_dsl_data = { + "from_": 0, + "aggregations": { + "main_agg": { + "terms": {"field": "main_field"}, + "aggregations": { + "sub_agg_1": {"avg": {"field": "sub_field_1"}}, + "sub_agg_2": { + "date_histogram": {"field": "timestamp", "interval": "month"} + }, + }, + } + }, + "size": 500, + "sort": [ + {"created": {"order": "asc"}}, + {"updated": {"order": "desc"}}, + {"entityId": {"order": "asc", "nested": {"path": "nested_test"}}}, + ], + "query": { + "bool": { + "must": [ + {"term": {"type.keyword": {"value": "Schema"}}}, + {"range": {"created": {"gte": "2025-01-01"}}}, + {"term": {"status": {"value": "active"}}}, + ], + "should": [{"term": {"category.keyword": {"value": "Tech"}}}], + "must_not": [{"term": {"archived": {"value": "true"}}}], + "filter": [ + { + "bool": { + "must": [ + {"term": {"region.keyword": {"value": "EMEA"}}}, + {"range": {"created": {"lte": "2025-12-31"}}}, + ], + "should": [ + { + "term": { + "sub_category.keyword": {"value": "Hardware"} + } + } + ], + } + } + ], + } + }, + "track_total_hits": False, + } + dsl_through_raw = DSL(**raw_dsl_data) + + assert dsl_through_raw.json( + exclude_unset=True, by_alias=True + ) == dsl_through_model.json(exclude_unset=True, by_alias=True) + + assert dsl_through_raw.json() == dsl_through_model.json() + + +@pytest.mark.parametrize( + "field, query, analyzer, slop, zero_terms_query, boost, expected", + [ + ( + "name", + "test", + None, + None, + None, + None, + {"match_phrase": {"name": {"query": "test"}}}, + ), + ( + "name", + "test", + "an analyzer", + None, + None, + None, + {"match_phrase": {"name": {"query": "test", "analyzer": "an analyzer"}}}, + ), + ( + "name", + "test", + "an analyzer", + 2, + None, + None, + { + "match_phrase": { + "name": { + "query": "test", + "analyzer": "an analyzer", + "slop": 2, + } + } + }, + ), + ( + "name", + "test", + "an analyzer", + 2, + "none", + 1.0, + { + "match_phrase": { + "name": { + "query": "test", + "analyzer": "an analyzer", + "slop": 2, + "zero_terms_query": "none", + "boost": 1.0, + } + } + }, + ), + ( + "name", + "test", + None, + 0, + "all", + 2.0, + { + "match_phrase": { + "name": { + "query": "test", + "slop": 0, + "zero_terms_query": "all", + "boost": 2.0, + } + } + }, + ), + ( + "description", + "another test", + "standard", + 1, + "none", + None, + { + "match_phrase": { + "description": { + "query": "another test", + "analyzer": "standard", + "slop": 1, + "zero_terms_query": "none", + } + } + }, + ), + ], +) +def test_match_phrase_to_dict( + field, + query, + analyzer, + slop, + zero_terms_query, + boost, + expected, +): + """Test MatchPhrase query produces correct dict for various parameter combinations.""" + assert ( + MatchPhrase( + field=field, + query=query, + analyzer=analyzer, + slop=slop, + zero_terms_query=zero_terms_query, + boost=boost, + ).to_dict() + == expected + ) + + +@pytest.mark.skip(reason="FluentSearch not yet migrated to v9") +def test_match_phrase_textfield(): + """Test MatchPhrase integration with FluentSearch text field methods. + + NOTE: Skipped because FluentSearch is not yet migrated to pyatlan_v9. + """ + pass diff --git a/tests_v9/unit/test_source_cache.py b/tests_v9/unit/test_source_cache.py new file mode 100644 index 000000000..c141cf058 --- /dev/null +++ b/tests_v9/unit/test_source_cache.py @@ -0,0 +1,269 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +""" +Unit tests for source tag cache — ported from tests/unit/test_source_cache.py. + +Uses legacy SourceTagCache/SourceTagName (plain Python classes) and v9 +Connection (msgspec.Struct) as test data. The cache layer itself has no Pydantic +dependency — this test verifies cache lookup/caching behaviour. +""" + +from unittest.mock import Mock, patch + +import pytest + +from pyatlan.cache.source_tag_cache import SourceTagCache, SourceTagName +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.errors import ErrorCode, InvalidRequestError, NotFoundError +from pyatlan_v9.model.assets import Connection + + +@pytest.fixture(autouse=True) +def set_env(monkeypatch): + monkeypatch.setenv("ATLAN_BASE_URL", "https://test.atlan.com") + monkeypatch.setenv("ATLAN_API_KEY", "test-api-key") + + +@pytest.fixture() +def client(): + return AtlanClient() + + +@pytest.fixture() +def mock_source_tag_cache(client, monkeypatch): + mock_cache = SourceTagCache(client) + monkeypatch.setattr(AtlanClient, "source_tag_cache", mock_cache) + return mock_cache + + +def test_get_by_guid_with_not_found_error(mock_source_tag_cache): + with pytest.raises(InvalidRequestError, match=ErrorCode.MISSING_ID.error_message): + mock_source_tag_cache.get_by_guid("") + + +@patch.object(SourceTagCache, "lookup_by_guid") +def test_get_by_guid_with_no_invalid_request_error( + mock_lookup_by_guid, mock_source_tag_cache +): + test_guid = "test-guid-123" + with pytest.raises( + NotFoundError, + match=ErrorCode.ASSET_NOT_FOUND_BY_GUID.error_message.format(test_guid), + ): + mock_source_tag_cache.get_by_guid(test_guid) + + +def test_get_by_qualified_name_with_not_found_error(client): + source_tag_cache = SourceTagCache(client) + with pytest.raises(InvalidRequestError, match=ErrorCode.MISSING_ID.error_message): + source_tag_cache.get_by_qualified_name("") + + +@patch.object(SourceTagCache, "lookup_by_qualified_name") +def test_get_by_qualified_name_with_no_invalid_request_error( + mock_lookup_by_qualified_name, mock_source_tag_cache +): + test_qn = "default/snowflake/123456789" + test_connector = "snowflake" + with pytest.raises( + NotFoundError, + match=ErrorCode.ASSET_NOT_FOUND_BY_QN.error_message.format( + test_qn, test_connector + ), + ): + mock_source_tag_cache.get_by_qualified_name(test_qn) + + +def test_get_by_name_with_not_found_error(client): + source_tag_cache = SourceTagCache(client) + with pytest.raises(InvalidRequestError, match=ErrorCode.MISSING_NAME.error_message): + source_tag_cache.get_by_name("") + + +@patch.object(SourceTagCache, "lookup_by_name") +def test_get_by_name_with_no_invalid_request_error( + mock_lookup_by_name, mock_source_tag_cache, client: AtlanClient +): + test_name = SourceTagName(client=client, tag="snowflake/test@@DB/SCHEMA/TEST_TAG") + with pytest.raises( + NotFoundError, + match=ErrorCode.ASSET_NOT_FOUND_BY_NAME.error_message.format( + SourceTagName._TYPE_NAME, + test_name, + ), + ): + mock_source_tag_cache.get_by_name(test_name) + + +@patch.object(SourceTagCache, "lookup_by_guid") +def test_get_by_guid(mock_lookup_by_guid, mock_source_tag_cache): + test_guid = "test-guid-123" + test_qn = "test-qualified-name" + conn = Connection() + conn.guid = test_guid + conn.qualified_name = test_qn + test_asset = conn + + mock_guid_to_asset = Mock() + mock_name_to_guid = Mock() + mock_qualified_name_to_guid = Mock() + + # 1 - Not found in the cache, triggers a lookup call + # 2, 3, 4 - Uses the cached entry from the map + mock_guid_to_asset.get.side_effect = [ + None, + test_asset, + test_asset, + test_asset, + ] + mock_name_to_guid.get.side_effect = [test_guid, test_guid, test_guid, test_guid] + mock_qualified_name_to_guid.get.side_effect = [ + test_guid, + test_guid, + test_guid, + test_guid, + ] + + # Assign mock caches to the return value of get_cache + mock_source_tag_cache.guid_to_asset = mock_guid_to_asset + mock_source_tag_cache.name_to_guid = mock_name_to_guid + mock_source_tag_cache.qualified_name_to_guid = mock_qualified_name_to_guid + + connection = mock_source_tag_cache.get_by_guid(test_guid) + + # Multiple calls with the same GUID result in no additional API lookups + # as the object is already cached + connection = mock_source_tag_cache.get_by_guid(test_guid) + connection = mock_source_tag_cache.get_by_guid(test_guid) + + assert test_guid == connection.guid + assert test_qn == connection.qualified_name + + # The method is called four times, but the lookup is triggered only once + assert mock_guid_to_asset.get.call_count == 4 + mock_lookup_by_guid.assert_called_once() + + +@patch.object(SourceTagCache, "lookup_by_guid") +@patch.object(SourceTagCache, "lookup_by_qualified_name") +def test_get_by_qualified_name( + mock_lookup_by_qn, mock_lookup_by_guid, mock_source_tag_cache +): + test_guid = "test-guid-123" + test_qn = "test-qualified-name" + conn = Connection() + conn.guid = test_guid + conn.qualified_name = test_qn + test_asset = conn + + mock_guid_to_asset = Mock() + mock_name_to_guid = Mock() + mock_qualified_name_to_guid = Mock() + + # 1 - Not found in the cache, triggers a lookup call + # 2, 3, 4 - Uses the cached entry from the map + mock_qualified_name_to_guid.get.side_effect = [ + None, + test_guid, + test_guid, + test_guid, + ] + + # Other caches will be populated once + # the lookup call for get_by_qualified_name is made + mock_guid_to_asset.get.side_effect = [ + test_asset, + test_asset, + test_asset, + test_asset, + ] + mock_name_to_guid.get.side_effect = [test_guid, test_guid, test_guid, test_guid] + + mock_source_tag_cache.guid_to_asset = mock_guid_to_asset + mock_source_tag_cache.name_to_guid = mock_name_to_guid + mock_source_tag_cache.qualified_name_to_guid = mock_qualified_name_to_guid + + connection = mock_source_tag_cache.get_by_qualified_name(test_qn) + + # Multiple calls with the same + # qualified name result in no additional API lookups + # as the object is already cached + connection = mock_source_tag_cache.get_by_qualified_name(test_qn) + connection = mock_source_tag_cache.get_by_qualified_name(test_qn) + + assert test_guid == connection.guid + assert test_qn == connection.qualified_name + + # The method is called three times + # but the lookup is triggered only once + assert mock_qualified_name_to_guid.get.call_count == 4 + mock_lookup_by_qn.assert_called_once() + + # No call to guid lookup since the object is already in the cache + assert mock_lookup_by_guid.get.call_count == 0 + + +@patch.object(SourceTagCache, "lookup_by_guid") +@patch.object(SourceTagCache, "lookup_by_name") +def test_get_by_name( + mock_lookup_by_name, mock_lookup_by_guid, mock_source_tag_cache, client: AtlanClient +): + test_name = SourceTagName(client=client, tag="snowflake/test@@DB/SCHEMA/TEST_TAG") + test_guid = "test-guid-123" + test_qn = "test-qualified-name" + conn = Connection() + conn.guid = test_guid + conn.qualified_name = test_qn + test_asset = conn + + mock_guid_to_asset = Mock() + mock_name_to_guid = Mock() + mock_qualified_name_to_guid = Mock() + + # 1 - Not found in the cache, triggers a lookup call + # 2, 3, 4 - Uses the cached entry from the map + mock_name_to_guid.get.side_effect = [ + None, + test_guid, + test_guid, + test_guid, + ] + + # Other caches will be populated once + # the lookup call for get_by_qualified_name is made + mock_guid_to_asset.get.side_effect = [ + test_asset, + test_asset, + test_asset, + test_asset, + ] + mock_qualified_name_to_guid.get.side_effect = [ + test_guid, + test_guid, + test_guid, + test_guid, + ] + + mock_source_tag_cache.guid_to_asset = mock_guid_to_asset + mock_source_tag_cache.name_to_guid = mock_name_to_guid + mock_source_tag_cache.qualified_name_to_guid = mock_qualified_name_to_guid + + connection = mock_source_tag_cache.get_by_name(test_name) + + # Multiple calls with the same + # qualified name result in no additional API lookups + # as the object is already cached + connection = mock_source_tag_cache.get_by_name(test_name) + connection = mock_source_tag_cache.get_by_name(test_name) + + assert test_guid == connection.guid + assert test_qn == connection.qualified_name + + # The method is called four times + # but the lookup is triggered only once + assert mock_name_to_guid.get.call_count == 4 + mock_lookup_by_name.assert_called_once() + + # No call to guid lookup since the object is already in the cache + assert mock_lookup_by_guid.call_count == 0 diff --git a/tests_v9/unit/test_sso_client.py b/tests_v9/unit/test_sso_client.py new file mode 100644 index 000000000..4ef3485ab --- /dev/null +++ b/tests_v9/unit/test_sso_client.py @@ -0,0 +1,303 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +""" +Unit tests for SSO client — ported from tests/unit/test_sso_client.py. + +Uses v9 models for inputs and type assertions. V9SSOClient returns v9 +msgspec models natively. +""" + +from json import load +from pathlib import Path +from re import escape +from unittest.mock import Mock + +import pytest + +from pyatlan.client.common import ApiCaller +from pyatlan_v9.client.sso import V9SSOClient as SSOClient +from pyatlan_v9.errors import InvalidRequestError +from pyatlan_v9.model.group import AtlanGroup +from pyatlan_v9.model.sso import SSOMapper + +TEST_DATA_DIR = Path(__file__).parent.parent.parent / "tests" / "unit" / "data" +SSO_GET_GROUP_MAPPING_JSON = "get_group_mapping.json" +SSO_GET_ALL_GROUP_MAPPING_JSON = "get_all_group_mapping.json" +SSO_CREATE_GROUP_MAPPING_JSON = "create_group_mapping.json" +SSO_UPDATE_GROUP_MAPPING_JSON = "update_group_mapping.json" +SSO_RESPONSES_DIR = TEST_DATA_DIR / "sso_responses" + + +def load_json(respones_dir, filename): + with (respones_dir / filename).open() as input_file: + return load(input_file) + + +def _assert_sso_mapper_matches_json(response, expected_json): + """Assert that a response SSOMapper matches expected JSON fields.""" + assert isinstance(response, SSOMapper) + assert response.id == expected_json.get("id") + assert response.name == expected_json.get("name") + assert response.identity_provider_mapper == expected_json["identityProviderMapper"] + assert response.identity_provider_alias == expected_json["identityProviderAlias"] + assert response.config is not None + config_json = expected_json["config"] + assert response.config.sync_mode == config_json.get("syncMode") + assert response.config.group_name == config_json.get("group") + assert response.config.attribute_name == config_json.get("attribute.name") + assert response.config.attribute_value == config_json.get("attribute.value") + + +@pytest.fixture(autouse=True) +def set_env(monkeypatch): + monkeypatch.setenv("ATLAN_BASE_URL", "https://test.atlan.com") + monkeypatch.setenv("ATLAN_API_KEY", "test-api-key") + + +@pytest.fixture(scope="module") +def mock_api_caller(): + return Mock(spec=ApiCaller) + + +@pytest.fixture() +def get_group_mapping_json(): + return load_json(SSO_RESPONSES_DIR, SSO_GET_GROUP_MAPPING_JSON) + + +@pytest.fixture() +def get_all_group_mapping_json(): + return load_json(SSO_RESPONSES_DIR, SSO_GET_ALL_GROUP_MAPPING_JSON) + + +@pytest.fixture() +def create_group_mapping_json(): + return load_json(SSO_RESPONSES_DIR, SSO_CREATE_GROUP_MAPPING_JSON) + + +@pytest.fixture() +def update_group_mapping_json(): + return load_json(SSO_RESPONSES_DIR, SSO_UPDATE_GROUP_MAPPING_JSON) + + +@pytest.mark.parametrize("test_api_caller", ["abc", None]) +def test_init_when_wrong_class_raises_exception(test_api_caller): + with pytest.raises( + InvalidRequestError, + match="ATLAN-PYTHON-400-048 Invalid parameter type for client should be ApiCaller", + ): + SSOClient(test_api_caller) + + +@pytest.mark.parametrize( + "sso_alias, group_map_id, error_msg", + [ + [None, "map-id", "none is not an allowed value"], + ["auth0", None, "none is not an allowed value"], + [[123], "map-id", "so_alias\n str type expected"], + ["azure", [123], "group_map_id\n str type expected"], + ], +) +def test_sso_get_group_mapping_wrong_params_raises_validation_error( + sso_alias, group_map_id, error_msg +): + with pytest.raises(ValueError) as err: + SSOClient.get_group_mapping(sso_alias=sso_alias, group_map_id=group_map_id) + assert error_msg in str(err.value) + + +@pytest.mark.parametrize( + "sso_alias, error_msg", + [ + [None, "none is not an allowed value"], + [[123], "so_alias\n str type expected"], + ], +) +def test_sso_get_all_group_mapping_wrong_params_raises_validation_error( + sso_alias, error_msg +): + with pytest.raises(ValueError, match=error_msg): + SSOClient.get_all_group_mappings(sso_alias=sso_alias) + + +@pytest.mark.parametrize( + "sso_alias, atlan_group, sso_group_name, error_msg", + [ + [None, "atlan-group", "sso-group", "none is not an allowed value"], + ["auth0", None, "sso-group", "none is not an allowed value"], + ["auth0", "atlan-group", None, "none is not an allowed value"], + [[123], "atlan-group", "sso-group", "so_alias\n str type expected"], + ["auth0", [123], "sso-group", "atlan_group\n instance of AtlanGroup expected"], + ["auth0", AtlanGroup(), [123], "sso_group_name\n str type expected"], + ], +) +def test_sso_create_group_mapping_wrong_params_raises_validation_error( + sso_alias, atlan_group, sso_group_name, error_msg +): + with pytest.raises(ValueError, match=error_msg): + SSOClient.create_group_mapping( + sso_alias=sso_alias, atlan_group=atlan_group, sso_group_name=sso_group_name + ) + + +@pytest.mark.parametrize( + "sso_alias, atlan_group, group_map_id, sso_group_name, error_msg", + [ + [None, "atlan-group", "map-id", "sso-group", "none is not an allowed value"], + ["auth0", None, "map-id", "sso-group", "none is not an allowed value"], + ["auth0", "atlan-group", None, "sso-group", "none is not an allowed value"], + ["auth0", "atlan-group", "map-id", None, "none is not an allowed value"], + ["auth0", "atlan-group", "map-id", None, "none is not an allowed value"], + [[123], "atlan-group", "map-id", "sso-group", "sso_alias\n str type expected"], + [ + "auth0", + [123], + "map-id", + "sso-group", + "atlan_group\n instance of AtlanGroup expected", + ], + [ + "auth0", + "atlan-group", + [123], + "sso-group", + "group_map_id\n str type expected", + ], + [ + "auth0", + "atlan-group", + "map-id", + [123], + "sso_group_name\n str type expected", + ], + ], +) +def test_sso_update_group_mapping_wrong_params_raises_validation_error( + sso_alias, atlan_group, group_map_id, sso_group_name, error_msg +): + with pytest.raises(ValueError, match=error_msg): + SSOClient.update_group_mapping( + sso_alias=sso_alias, + atlan_group=atlan_group, + group_map_id=group_map_id, + sso_group_name=sso_group_name, + ) + + +@pytest.mark.parametrize( + "sso_alias, group_map_id, error_msg", + [ + [None, "map-id", "none is not an allowed value"], + ["auth0", None, "none is not an allowed value"], + [[123], "map-id", "so_alias\n str type expected"], + ["azure", [123], "group_map_id\n str type expected"], + ], +) +def test_sso_delete_group_mapping_wrong_params_raises_validation_error( + sso_alias, group_map_id, error_msg +): + with pytest.raises(ValueError, match=error_msg): + SSOClient.delete_group_mapping(sso_alias=sso_alias, group_map_id=group_map_id) + + +def test_sso_get_group_mapping( + mock_api_caller, + get_group_mapping_json, +): + mock_api_caller._call_api.side_effect = [get_group_mapping_json] + client = SSOClient(client=mock_api_caller) + response = client.get_group_mapping(sso_alias="auth0", group_map_id="1234") + _assert_sso_mapper_matches_json(response, get_group_mapping_json) + assert mock_api_caller._call_api.call_count == 1 + mock_api_caller.reset_mock() + + +def test_sso_get_all_group_mapping( + mock_api_caller, + get_all_group_mapping_json, +): + mock_api_caller._call_api.side_effect = [get_all_group_mapping_json] + client = SSOClient(client=mock_api_caller) + response = client.get_all_group_mappings(sso_alias="auth0") + # Only returns group mapping (filtered by IDP_GROUP_MAPPER) + assert len(response) == 1 + _assert_sso_mapper_matches_json(response[0], get_all_group_mapping_json[2]) + assert mock_api_caller._call_api.call_count == 1 + mock_api_caller.reset_mock() + + +def test_sso_create_group_mapping_invalid_request_error( + mock_api_caller, get_all_group_mapping_json, create_group_mapping_json +): + mock_api_caller._call_api.side_effect = [ + get_all_group_mapping_json, + create_group_mapping_json, + ] + existing_atlan_group = AtlanGroup() + existing_atlan_group.alias = "existing_atlan_group" + existing_atlan_group.id = "atlan-group-guid-1234" + client = SSOClient(client=mock_api_caller) + expected_error = escape( + ( + f"ATLAN-PYTHON-400-058 SSO group mapping already exists between " + f"{existing_atlan_group.alias} (Atlan group) <-> test-sso-group (SSO group)" + ) + ) + with pytest.raises(InvalidRequestError, match=expected_error): + client.create_group_mapping( + sso_alias="auth0", + atlan_group=existing_atlan_group, + sso_group_name="sso-group", + ) + assert mock_api_caller._call_api.call_count == 1 + mock_api_caller.reset_mock() + + +def test_sso_create_group_mapping( + mock_api_caller, get_all_group_mapping_json, create_group_mapping_json +): + mock_api_caller._call_api.side_effect = [ + get_all_group_mapping_json, + create_group_mapping_json, + ] + # Group that doesn't exist in sso group mappings + atlan_group = AtlanGroup() + atlan_group.id = "atlan-group-new-mapping-guid-1234" + client = SSOClient(client=mock_api_caller) + response = client.create_group_mapping( + sso_alias="auth0", + atlan_group=atlan_group, + sso_group_name="sso-group", + ) + _assert_sso_mapper_matches_json(response, create_group_mapping_json) + assert mock_api_caller._call_api.call_count == 2 + mock_api_caller.reset_mock() + + +def test_sso_update_group_mapping(mock_api_caller, update_group_mapping_json): + mock_api_caller._call_api.side_effect = [ + update_group_mapping_json, + ] + client = SSOClient(client=mock_api_caller) + response = client.update_group_mapping( + sso_alias="auth0", + atlan_group=AtlanGroup(), + group_map_id="group-map-id", + group_map_name="group-map-name", + sso_group_name="sso-group", + ) + _assert_sso_mapper_matches_json(response, update_group_mapping_json) + assert mock_api_caller._call_api.call_count == 1 + mock_api_caller.reset_mock() + + +def test_sso_delete_group_mapping(mock_api_caller): + mock_api_caller._call_api.side_effect = [None] + client = SSOClient(client=mock_api_caller) + response = client.delete_group_mapping( + sso_alias="auth0", + group_map_id="group-map-id", + ) + assert response is None + assert mock_api_caller._call_api.call_count == 1 + mock_api_caller.reset_mock() diff --git a/tests_v9/unit/test_structs.py b/tests_v9/unit/test_structs.py new file mode 100644 index 000000000..3ecd7c2bd --- /dev/null +++ b/tests_v9/unit/test_structs.py @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +"""Unit tests for pyatlan_v9 struct and Atlas conversion behavior.""" + +from __future__ import annotations + +import json +from pathlib import Path + +import pytest + +from pyatlan_v9.model import MCMonitor +from pyatlan_v9.model.transform import from_atlas_format, from_atlas_json + +TEST_DATA_DIR = Path(__file__).parents[2] / "tests" / "unit" / "data" +MC_MONITOR_JSON = "mc_monitor.json" +STRUCT_RESPONSES_DIR = TEST_DATA_DIR / "struct_responses" + + +def load_json(responses_dir: Path, filename: str): + """Load JSON fixture from disk.""" + with (responses_dir / filename).open() as input_file: + return json.load(input_file) + + +@pytest.fixture() +def mc_monitor_response_json(): + """Load legacy MCMonitor fixture JSON.""" + return load_json(STRUCT_RESPONSES_DIR, MC_MONITOR_JSON) + + +def test_structs_flatten_attributes(mc_monitor_response_json): + """Ensure Atlas nested payloads flatten consistently through both decode paths.""" + asset_response = {"referredEntities": {}, "entity": mc_monitor_response_json} + + mc_monitor_from_dict = from_atlas_format(mc_monitor_response_json) + mc_monitor_from_enveloped_json = from_atlas_json( + json.dumps(asset_response).encode() + ) + + assert isinstance(mc_monitor_from_dict, MCMonitor) + assert isinstance(mc_monitor_from_enveloped_json, MCMonitor) + assert mc_monitor_from_dict == mc_monitor_from_enveloped_json diff --git a/tests_v9/unit/test_task_client.py b/tests_v9/unit/test_task_client.py new file mode 100644 index 000000000..3cf4f67ce --- /dev/null +++ b/tests_v9/unit/test_task_client.py @@ -0,0 +1,143 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +""" +Unit tests for task client — ported from tests/unit/test_task_client.py. + +Uses v9 TaskSearchRequest, FluentTasks, and AtlanTask (msgspec.Struct). +The legacy TaskClient is used since the client layer is not migrated. +""" + +from json import load, loads +from pathlib import Path +from unittest.mock import Mock + +import pytest + +from pyatlan.client.common import ApiCaller +from pyatlan_v9.client.task import V9TaskClient as TaskClient +from pyatlan_v9.errors import InvalidRequestError +from pyatlan_v9.model.enums import AtlanTaskStatus, AtlanTaskType + +# v9 models +from pyatlan_v9.model.fluent_tasks import FluentTasks +from pyatlan_v9.model.task import AtlanTask, TaskSearchRequest, TaskSearchResponse + +TEST_DATA_DIR = Path(__file__).parent.parent.parent / "tests" / "unit" / "data" +TASK_SEARCH_JSON = "task_search.json" +TASK_RESPONSES_DIR = TEST_DATA_DIR / "task_responses" +FLUENT_TASKS_REQUEST_JSON = "fluent_tasks.json" +TASK_REQUESTS_DIR = TEST_DATA_DIR / "task_requests" + + +def load_json(respones_dir, filename): + with (respones_dir / filename).open() as input_file: + return load(input_file) + + +def to_json(model): + return model.json(by_alias=True, exclude_none=True) + + +@pytest.fixture(autouse=True) +def set_env(monkeypatch): + monkeypatch.setenv("ATLAN_BASE_URL", "https://test.atlan.com") + monkeypatch.setenv("ATLAN_API_KEY", "test-api-key") + + +@pytest.fixture(scope="module") +def mock_api_caller(): + return Mock(spec=ApiCaller) + + +@pytest.fixture() +def task_search_request() -> TaskSearchRequest: + return ( + FluentTasks() + .page_size(1) + .where(AtlanTask.STATUS.match(AtlanTaskStatus.COMPLETE.value)) + .to_request() + ) + + +@pytest.fixture() +def task_search_response_json(): + return load_json(TASK_RESPONSES_DIR, TASK_SEARCH_JSON) + + +@pytest.fixture() +def task_search_request_json(): + return load_json(TASK_REQUESTS_DIR, FLUENT_TASKS_REQUEST_JSON) + + +@pytest.mark.parametrize("test_api_caller", ["abc", None]) +def test_init_when_wrong_class_raises_exception(test_api_caller): + with pytest.raises( + InvalidRequestError, + match="ATLAN-PYTHON-400-048 Invalid parameter type for client should be ApiCaller", + ): + TaskClient(test_api_caller) + + +@pytest.mark.parametrize( + "test_request, error_msg", + [ + [None, "none is not an allowed value"], + ["123", "instance of TaskSearchRequest expected"], + ], +) +def test_task_seaech_wrong_params_raises_validation_error(test_request, error_msg): + with pytest.raises(ValueError) as err: + TaskClient.search(request=test_request) + assert error_msg in str(err.value) + + +@pytest.mark.parametrize( + "test_method, test_client", + [["count", [None, 123, "abc"]], ["execute", [None, 123, "abc"]]], +) +def test_fluent_tasks_invalid_client_raises_invalid_request_error( + test_method, + test_client, +): + client_method = getattr(FluentTasks(), test_method) + for invalid_client in test_client: + with pytest.raises( + InvalidRequestError, match="No Atlan client has been provided." + ): + client_method(client=invalid_client) + + +def test_task_search_get_when_given_request( + mock_api_caller, + task_search_request, + task_search_request_json: TaskSearchRequest, + task_search_response_json: TaskSearchResponse, +): + last_page_response = {"tasks": [], "approximateCount": 1} + mock_api_caller._call_api.side_effect = [ + task_search_response_json, + last_page_response, + ] + client = TaskClient(client=mock_api_caller) + response = client.search(request=task_search_request) + request_dsl_json = to_json(response._criteria) + + assert loads(request_dsl_json) == task_search_request_json + assert response + assert response.count == 1 + for task in response: + assert task.guid + assert task.end_time + assert task.start_time + assert task.updated_time + assert task.created_by + assert task.parameters + assert task.attempt_count == 0 + assert task.entity_guid + assert task.time_taken_in_seconds + assert task.parameters.get("__task_classificationTypeName") + assert task.status == AtlanTaskStatus.COMPLETE + assert task.type == AtlanTaskType.CLASSIFICATION_PROPAGATION_ADD + assert mock_api_caller._call_api.call_count == 2 + mock_api_caller.reset_mock() diff --git a/tests_v9/unit/test_typedef_model.py b/tests_v9/unit/test_typedef_model.py new file mode 100644 index 000000000..ca7088256 --- /dev/null +++ b/tests_v9/unit/test_typedef_model.py @@ -0,0 +1,591 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +""" +Unit tests for pyatlan_v9 typedef models (msgspec.Struct). + +Tests ported from tests/unit/test_typedef_model.py — EnumDef, StructDef, +AtlanTagDef, EntityDef, RelationshipDef, CustomMetadataDef, TypeDefResponse, +and AttributeDef tests. +""" + +import json +import random +from pathlib import Path +from re import escape +from unittest.mock import Mock, patch + +import msgspec +import pytest + +from pyatlan.cache.enum_cache import EnumCache +from pyatlan.model.utils import to_camel_case, to_snake_case +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.errors import AtlanError, InvalidRequestError, NotFoundError +from pyatlan_v9.model.enums import AtlanCustomAttributePrimitiveType, AtlanTypeCategory +from pyatlan_v9.model.typedef import ( + AtlanTagDef, + AttributeDef, + Cardinality, + CustomMetadataDef, + EntityDef, + EnumDef, + RelationshipDef, + StructDef, + TypeDef, + TypeDefResponse, + _all_ai_asset_types, + _all_domain_types, + _all_glossary_types, + _all_other_types, + _complete_type_list, +) +from tests.unit.constants import ( + APPLICABLE_AI_ASSET_TYPES, + APPLICABLE_ASSET_TYPES, + APPLICABLE_CONNECTIONS, + APPLICABLE_DOMAIN_TYPES, + APPLICABLE_DOMAINS, + APPLICABLE_ENTITY_TYPES, + APPLICABLE_GLOSSARIES, + APPLICABLE_GLOSSARY_TYPES, + APPLICABLE_OTHER_ASSET_TYPES, + TEST_ATTRIBUTE_DEF_APPLICABLE_ASSET_TYPES, + TEST_ENUM_DEF, + TEST_STRUCT_DEF, +) + +PARENT_DIR = Path(__file__).parent.parent.parent / "tests" / "unit" +TYPEDEFS_JSON = PARENT_DIR / "data" / "typedefs.json" + + +@pytest.fixture(autouse=True) +def set_env(monkeypatch): + """Set required environment variables for client instantiation.""" + monkeypatch.setenv("ATLAN_API_KEY", "test-api-key") + monkeypatch.setenv("ATLAN_BASE_URL", "https://test.atlan.com") + + +@pytest.fixture() +def client(): + """Create a v9 AtlanClient using env var defaults.""" + return AtlanClient() + + +@pytest.fixture(scope="module") +def mock_api_caller(): + """Create a mock ApiCaller for sub-client testing.""" + from pyatlan.client.common import ApiCaller + + return Mock(spec=ApiCaller) + + +@pytest.fixture() +def type_defs(): + """Load typedefs JSON test data.""" + with TYPEDEFS_JSON.open() as input_file: + return json.load(input_file) + + +def check_type_def_properties(type_def: TypeDef, source: dict): + """Verify that common TypeDef properties match the source dict.""" + + def check_property(property_name: str): + key = to_camel_case(property_name) + value = getattr(type_def, property_name) + if key in source: + assert value == source[key] + else: + assert value is None + + check_property("create_time") + check_property("created_by") + check_property("description") + check_property("guid") + check_property("name") + check_property("type_version") + check_property("update_time") + check_property("updated_by") + check_property("version") + + +def check_attribute(model: object, attribute_name: str, source: dict): + """Verify that a model attribute matches the source dict.""" + key = to_camel_case(attribute_name) + attribute = getattr(model, attribute_name) + if key in source: + value = source[key] + value = type(attribute)(value) + assert attribute == value + else: + # Since "options" are now initialized with a default factory + if attribute_name != "options": + assert getattr(model, attribute_name) is None + + +def check_has_attributes(type_def: TypeDef, type_def_json: dict): + """Verify that the TypeDef has attributes for all keys in the JSON.""" + for key in type_def_json: + attribute_name = to_snake_case(key) + assert hasattr(type_def, attribute_name) + + +class TestEnumDef: + @pytest.fixture() + def mock_enum_cache(self, client, monkeypatch): + """Mock the enum cache on the AtlanClient.""" + mock_cache = EnumCache(client) + monkeypatch.setattr(AtlanClient, "enum_cache", mock_cache) + return mock_cache + + def test_create_element_def(self): + """Test creating an ElementDef from JSON data.""" + element_def = msgspec.convert( + TEST_ENUM_DEF["elementDefs"][0], EnumDef.ElementDef, strict=False + ) + assert element_def.description == TEST_ENUM_DEF["elementDefs"][0]["description"] + assert element_def.value == TEST_ENUM_DEF["elementDefs"][0]["value"] + assert element_def.ordinal == TEST_ENUM_DEF["elementDefs"][0]["ordinal"] + + def test_create_enum_def(self): + """Test creating an EnumDef from JSON data.""" + enum_def = msgspec.convert(TEST_ENUM_DEF, EnumDef, strict=False) + assert enum_def.category == AtlanTypeCategory.ENUM + assert len(enum_def.element_defs) == 5 + check_type_def_properties(enum_def, TEST_ENUM_DEF) + + def test_enum_defs(self, type_defs): + """Test creating EnumDefs from the typedefs JSON fixture.""" + for enum_def_json in type_defs["enumDefs"]: + enum_def = msgspec.convert(enum_def_json, EnumDef, strict=False) + assert enum_def.category == AtlanTypeCategory.ENUM + check_type_def_properties(enum_def, enum_def_json) + + @pytest.mark.parametrize( + "test_name, test_values, error_msg", + [ + [None, ["val1", "val2"], "name is required"], + ["my_enum", None, "values is required"], + ], + ) + def test_enum_create_method_required_parameters( + self, test_name, test_values, error_msg + ): + """Test that EnumDef.creator raises ValueError for missing required params.""" + with pytest.raises(ValueError) as err: + EnumDef.creator(name=test_name, values=test_values) + assert error_msg in str(err.value) + + def test_create_method(self): + """Test creating an EnumDef via the create factory method.""" + enum = EnumDef.creator(name="test-enum", values=["test-val1", "test-val2"]) + assert enum + assert enum.name == "test-enum" + assert enum.category == AtlanTypeCategory.ENUM + assert enum.element_defs + assert len(enum.element_defs) == 2 + assert enum.element_defs[0].value == "test-val1" + assert enum.element_defs[1].value == "test-val2" + + def test_update_method_enum_not_found(self, client, mock_enum_cache): + """Test that EnumDef.updater raises NotFoundError when enum not found.""" + mock_enum_cache._get_by_name = Mock(return_value=None) + + with pytest.raises( + NotFoundError, + match="ATLAN-PYTHON-404-013 Enumeration with name test-enum does not exist.", + ): + EnumDef.updater( + client=client, + name="test-enum", + values=["test-val1", "test-val2"], + replace_existing=False, + ) + + def test_update_method(self, client, mock_enum_cache): + """Test EnumDef.updater with various scenarios.""" + existing_enum = { + "name": "test-enum", + "elementDefs": [{"value": "test-val0"}], + } + mock_get_by_name = Mock( + return_value=msgspec.convert(existing_enum, EnumDef, strict=False) + ) + mock_enum_cache.get_by_name = mock_get_by_name + enum = EnumDef.updater( + client=client, + name="test-enum", + values=["test-val1", "test-val2"], + replace_existing=False, + ) + assert enum + assert enum.name == "test-enum" + assert enum.category == AtlanTypeCategory.ENUM + assert enum.element_defs + assert len(enum.element_defs) == 3 + assert enum.element_defs[0].value == "test-val0" + assert enum.element_defs[1].value == "test-val1" + assert enum.element_defs[2].value == "test-val2" + + # Test no duplication + existing_enum = { + "name": "test-enum", + "elementDefs": [ + {"value": "test-val0"}, + {"value": "test-val1"}, + {"value": "test-val2"}, + ], + } + mock_get_by_name = Mock( + return_value=msgspec.convert(existing_enum, EnumDef, strict=False) + ) + mock_enum_cache.get_by_name = mock_get_by_name + enum = EnumDef.updater( + client=client, + name="test-enum", + values=["test-val1", "test-val2"], + replace_existing=False, + ) + assert enum + assert enum.name == "test-enum" + assert enum.category == AtlanTypeCategory.ENUM + assert enum.element_defs + assert len(enum.element_defs) == 3 + assert enum.element_defs[0].value == "test-val0" + assert enum.element_defs[1].value == "test-val1" + assert enum.element_defs[2].value == "test-val2" + + # Test with existing values and ordering + existing_enum = { + "name": "test-enum", + "elementDefs": [ + {"value": "test-val0"}, + {"value": "test-val1"}, + {"value": "test-val2"}, + ], + } + mock_get_by_name = Mock( + return_value=msgspec.convert(existing_enum, EnumDef, strict=False) + ) + mock_enum_cache.get_by_name = mock_get_by_name + enum = EnumDef.updater( + client=client, + name="test-enum", + values=["new1", "test-val1", "new2", "test-val2", "new3", "new4"], + replace_existing=False, + ) + assert enum + assert enum.name == "test-enum" + assert enum.category == AtlanTypeCategory.ENUM + assert enum.element_defs + assert len(enum.element_defs) == 7 + assert enum.element_defs[0].value == "test-val0" + assert enum.element_defs[1].value == "test-val1" + assert enum.element_defs[2].value == "test-val2" + # Make sure new ones are always append + assert enum.element_defs[3].value == "new1" + assert enum.element_defs[4].value == "new2" + assert enum.element_defs[5].value == "new3" + assert enum.element_defs[6].value == "new4" + + # Test when `replace_existing` is `True` + existing_enum = { + "name": "test-enum", + "elementDefs": [ + {"value": "test-val0"}, + {"value": "test-val1"}, + {"value": "test-val2"}, + ], + } + mock_get_by_name = Mock( + return_value=msgspec.convert(existing_enum, EnumDef, strict=False) + ) + mock_enum_cache.get_by_name = mock_get_by_name + enum = EnumDef.updater( + client=client, + name="test-enum", + values=["new1", "test-val1", "new2", "test-val2", "new3", "new4"], + replace_existing=True, + ) + assert enum + assert enum.name == "test-enum" + assert enum.category == AtlanTypeCategory.ENUM + assert enum.element_defs + assert len(enum.element_defs) == 6 + assert enum.element_defs[0].value == "new1" + assert enum.element_defs[1].value == "test-val1" + assert enum.element_defs[2].value == "new2" + assert enum.element_defs[3].value == "test-val2" + assert enum.element_defs[4].value == "new3" + assert enum.element_defs[5].value == "new4" + + +class TestStuctDef: + @pytest.mark.skip("Need get a new version of the typedefs.json file") + def test_struct_defs(self, type_defs): + """Test creating StructDefs from the typedefs JSON fixture.""" + for struct_def_json in type_defs["structDefs"]: + struct_def = msgspec.convert(struct_def_json, StructDef, strict=False) + assert struct_def.category == AtlanTypeCategory.STRUCT + check_type_def_properties(struct_def, struct_def_json) + for index, attribute_def in enumerate(struct_def.attribute_defs): + attribute_defs = struct_def_json["attributeDefs"][index] + for key in attribute_def.__struct_fields__: + check_attribute(attribute_def, key, attribute_defs) + check_has_attributes(struct_def, struct_def_json) + + def test_create_struct_def(self): + """Test creating a StructDef from JSON data.""" + struct_def = msgspec.convert(TEST_STRUCT_DEF, StructDef, strict=False) + assert struct_def.category == AtlanTypeCategory.STRUCT + check_type_def_properties(struct_def, TEST_STRUCT_DEF) + for index, attribute_def in enumerate(struct_def.attribute_defs): + attribute_defs = TEST_STRUCT_DEF["attributeDefs"][index] + for key in attribute_def.__struct_fields__: + check_attribute(attribute_def, key, attribute_defs) + + +class TestAtlanTagDef: + def test_classification_def(self, type_defs): + """Test creating AtlanTagDefs from the typedefs JSON fixture.""" + for classification_def_json in type_defs["classificationDefs"]: + classification_def = msgspec.convert( + classification_def_json, AtlanTagDef, strict=False + ) + assert classification_def.category == AtlanTypeCategory.CLASSIFICATION + check_type_def_properties(classification_def, classification_def_json) + check_has_attributes(classification_def, classification_def_json) + + +class TestEntityDef: + def test_entity_def(self, type_defs): + """Test creating EntityDefs from the typedefs JSON fixture.""" + for entity_def_json in type_defs["entityDefs"]: + entity_def = msgspec.convert(entity_def_json, EntityDef, strict=False) + assert entity_def.category == AtlanTypeCategory.ENTITY + check_type_def_properties(entity_def, entity_def_json) + check_has_attributes(entity_def, entity_def_json) + + +class TestRelationshipDef: + def test_relationship_def(self, type_defs): + """Test creating RelationshipDefs from the typedefs JSON fixture.""" + for relationship_def_json in type_defs["relationshipDefs"]: + relationship_def = msgspec.convert( + relationship_def_json, RelationshipDef, strict=False + ) + assert relationship_def.category == AtlanTypeCategory.RELATIONSHIP + check_type_def_properties(relationship_def, relationship_def_json) + check_has_attributes(relationship_def, relationship_def_json) + + +class TestCustomMetadataDef: + def test_business_metadata_def(self, type_defs): + """Test creating CustomMetadataDefs from the typedefs JSON fixture.""" + for business_metadata_def_json in type_defs["businessMetadataDefs"]: + business_metadata_def = msgspec.convert( + business_metadata_def_json, CustomMetadataDef, strict=False + ) + assert business_metadata_def.category == AtlanTypeCategory.CUSTOM_METADATA + check_type_def_properties(business_metadata_def, business_metadata_def_json) + check_has_attributes(business_metadata_def, business_metadata_def_json) + + +class TestTypeDefResponse: + def test_type_def_response(self, type_defs): + """Test creating a TypeDefResponse from the typedefs JSON fixture.""" + type_def_response = msgspec.convert(type_defs, TypeDefResponse, strict=False) + assert isinstance(type_def_response, TypeDefResponse) + + +class TestAttributeDef: + @pytest.fixture() + def sut(self, client: AtlanClient) -> AttributeDef: + """Create an AttributeDef for testing.""" + with patch("pyatlan_v9.model.typedef._get_all_qualified_names") as mock_get_qa: + mock_get_qa.return_value = set() + return AttributeDef.creator( + client=client, + display_name="My Count", + attribute_type=AtlanCustomAttributePrimitiveType.INTEGER, + ) + + @pytest.mark.parametrize( + "attribute, value", + [ + (APPLICABLE_ASSET_TYPES, {"Table"}), + (APPLICABLE_GLOSSARY_TYPES, {"AtlasGlossary"}), + (APPLICABLE_DOMAIN_TYPES, {"DataDomain", "DataProduct"}), + (APPLICABLE_AI_ASSET_TYPES, {"AIApplication", "AIModel"}), + (APPLICABLE_OTHER_ASSET_TYPES, {"File"}), + (APPLICABLE_ENTITY_TYPES, {"Asset"}), + ], + ) + def test_applicable_types_with_no_options_raises_invalid_request_error( + self, attribute, value, sut: AttributeDef + ): + """Test that setting applicable types raises error when options is None.""" + sut = AttributeDef() + # Explicitly setting "None" since options + # are now initialized with a default factory + sut.options = None + + with pytest.raises( + InvalidRequestError, + match="ATLAN-PYTHON-400-050 Options is not present in the AttributeDef", + ): + setattr(sut, attribute, value) + + @pytest.mark.parametrize( + "attribute, value, message", + TEST_ATTRIBUTE_DEF_APPLICABLE_ASSET_TYPES, + ) + def test_applicable_types_with_invalid_type_raises_invalid_request_error( + self, attribute, value, message, sut: AttributeDef + ): + """Test that setting applicable types with invalid values raises error.""" + with pytest.raises(InvalidRequestError, match=escape(message)): + setattr(sut, attribute, value) + + @pytest.mark.parametrize( + "attribute, value", + [ + (APPLICABLE_ASSET_TYPES, {random.choice(list(_complete_type_list))}), + (APPLICABLE_GLOSSARY_TYPES, {random.choice(list(_all_glossary_types))}), + (APPLICABLE_DOMAIN_TYPES, {random.choice(list(_all_domain_types))}), + (APPLICABLE_AI_ASSET_TYPES, {random.choice(list(_all_ai_asset_types))}), + (APPLICABLE_OTHER_ASSET_TYPES, {random.choice(list(_all_other_types))}), + (APPLICABLE_ENTITY_TYPES, {"Asset"}), + (APPLICABLE_CONNECTIONS, {"default/snowflake/1699268171"}), + (APPLICABLE_GLOSSARIES, {"8Jdg4PdxcURBBNDt2RZD3"}), + (APPLICABLE_DOMAINS, {"default/domain/uuBI8WSqeom1PXs7oo20L/super"}), + ], + ) + def test_applicable_types_with_valid_value( + self, attribute, value, sut: AttributeDef + ): + """Test that setting applicable types with valid values works correctly.""" + setattr(sut, attribute, value) + assert getattr(sut, attribute) == value + options = sut.options + assert getattr(options, attribute) == json.dumps(list(value)) + + def test_attribute_create_with_limited_applicability(self, client: AtlanClient): + """Test creating AttributeDef with limited applicability kwargs.""" + applicable_kwargs = dict( + applicable_asset_types={"Link"}, + applicable_other_asset_types={"File"}, + applicable_glossaries={"8Jdg4PdxcURBBNDt2RZD3"}, + applicable_glossary_types={"AtlasGlossaryTerm", "AtlasGlossaryCategory"}, + applicable_domain_types={"DataDomain", "DataProduct"}, + applicable_connections={ + "default/snowflake/1699268171", + "default/snowflake/16992681799", + }, + applicable_domains={"default/domain/uuBI8WSqeom1PXs7oo20L/super"}, + applicable_ai_asset_types={"AIModel", "AIApplication"}, + ) + attribute_def_with_limited = AttributeDef.creator( + client=client, + display_name="test-attr-def", + attribute_type=AtlanCustomAttributePrimitiveType.STRING, + # Optional kwargs that allow limiting + # the applicability of an attribute within Atlan + **applicable_kwargs, # type: ignore[arg-type] + ) + + assert attribute_def_with_limited + assert attribute_def_with_limited.options + options = attribute_def_with_limited.options + for attribute in applicable_kwargs.keys(): + assert getattr( + attribute_def_with_limited, attribute + ) == applicable_kwargs.get(attribute) + assert getattr(options, attribute) == json.dumps( + list(applicable_kwargs.get(attribute)) # type: ignore[arg-type] + ) + + def test_multi_value_select_setter_condition(self, client: AtlanClient): + """Test multi_value_select setter updates cardinality and type_name.""" + with patch("pyatlan_v9.model.typedef._get_all_qualified_names") as mock_get_qa: + mock_get_qa.return_value = set() + + attr_def = AttributeDef.creator( + client=client, + display_name="Test Attribute", + attribute_type=AtlanCustomAttributePrimitiveType.STRING, + ) + + assert attr_def.options is not None + assert attr_def.options.multi_value_select is False + assert attr_def.cardinality == Cardinality.SINGLE + assert attr_def.type_name == AtlanCustomAttributePrimitiveType.STRING.value + + # Test 1: Setting multi_value_select to False should NOT trigger __setattr__ logic + attr_def.options.multi_value_select = False + + assert attr_def.cardinality == Cardinality.SINGLE + assert attr_def.type_name == AtlanCustomAttributePrimitiveType.STRING.value + + # Test 2: Setting multi_value_select to True should trigger __setattr__ logic + attr_def.options.multi_value_select = True + + assert attr_def.cardinality == Cardinality.SET + assert ( + attr_def.type_name + == f"array<{AtlanCustomAttributePrimitiveType.STRING.value}>" + ) + + def test_rich_text_attribute_creation(self, client: AtlanClient): + """Test that RICH_TEXT attributes are created with correct options.""" + with patch("pyatlan_v9.model.typedef._get_all_qualified_names") as mock_get_qa: + mock_get_qa.return_value = set() + attr_def = AttributeDef.creator( + client=client, + display_name="Rich Content", + attribute_type=AtlanCustomAttributePrimitiveType.RICH_TEXT, + description="Test rich text attribute", + ) + + assert attr_def.display_name == "Rich Content" + assert attr_def.type_name == AtlanCustomAttributePrimitiveType.STRING.value + assert attr_def.description == "Test rich text attribute" + assert attr_def.options + assert attr_def.options.is_rich_text is True + assert attr_def.options.multi_value_select is False + + def test_rich_text_cannot_be_multi_valued(self, client: AtlanClient): + """Test that RICH_TEXT attributes cannot be multi-valued.""" + with patch("pyatlan_v9.model.typedef._get_all_qualified_names") as mock_get_qa: + mock_get_qa.return_value = set() + with pytest.raises(AtlanError) as exc_info: + AttributeDef.creator( + client=client, + display_name="Invalid Rich Text", + attribute_type=AtlanCustomAttributePrimitiveType.RICH_TEXT, + multi_valued=True, + ) + + error = exc_info.value + assert "ATLAN-PYTHON-400-076" in str(error) + + def test_rich_text_options_configuration(self, client: AtlanClient): + """Test that RICH_TEXT options are configured correctly.""" + with patch("pyatlan_v9.model.typedef._get_all_qualified_names") as mock_get_qa: + mock_get_qa.return_value = set() + attr_def = AttributeDef.creator( + client=client, + display_name="Rich Text Field", + attribute_type=AtlanCustomAttributePrimitiveType.RICH_TEXT, + ) + + options = attr_def.options + assert options is not None + # Rich text uses string primitive type + assert options.primitive_type == AtlanCustomAttributePrimitiveType.STRING.value + # Should have rich text flag enabled + assert options.is_rich_text is True + # Cannot be multi-valued + assert options.multi_value_select is False + # Should not have custom_type set (that's for SQL, URL, etc.) + assert not hasattr(options, "custom_type") or options.custom_type is None diff --git a/tests_v9/unit/test_utils.py b/tests_v9/unit/test_utils.py new file mode 100644 index 000000000..c670376e3 --- /dev/null +++ b/tests_v9/unit/test_utils.py @@ -0,0 +1,441 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Atlan Pte. Ltd. + +# V9 port of tests/unit/test_utils.py + +from unittest.mock import patch + +import pytest + +from pyatlan.model.utils import construct_object_key +from pyatlan_v9.errors import InvalidRequestError +from pyatlan_v9.model.enums import AtlanConnectionCategory, AtlanConnectorType +from pyatlan_v9.utils import ( + ComparisonCategory, + get_base_type, + is_comparable_type, + list_attributes_to_params, + unflatten_custom_metadata, + unflatten_custom_metadata_for_entity, + validate_type, +) + + +def test_list_attributes_to_params_with_no_query_parms(): + assert list_attributes_to_params([{"first": "Dave"}]) == {"attr_0:first": "Dave"} + + +def test_list_attributes_to_params_with_query_parms(): + assert list_attributes_to_params([{"first": "Dave"}], {"last": "Jo"}) == { + "attr_0:first": "Dave", + "last": "Jo", + } + + +@pytest.mark.parametrize( + "attributes, flattened_attributes, custom_metadata", + [ + (None, None, None), + ( + None, + { + "qualifiedName": "default/glue/1688357913/AwsDataCatalog/development_published_impact/holding_rating" + }, + None, + ), + (["ihnLo19fqaT4x9pU8JKWbQ.Cyw6GbqST9M1dhXIBE1yHp"], None, None), + ( + [ + "ihnLo19fqaT4x9pU8JKWbQ.Cyw6GbqST9M1dhXIBE1yHp", + "ihnLo19fqaT4x9pU8JKWbQ.mdplQ3q9dk0T11vo817wyJ", + "ihnLo19fqaT4x9pU8JKWbQ.Xb3awoTsZGPu6vqnNxrzYF", + "mwkVZhWne8ApD5t1BetxLd.IBTIot8BAicd74XmnPGytU", + ], + { + "ihnLo19fqaT4x9pU8JKWbQ.Cyw6GbqST9M1dhXIBE1yHp": 1688137200000, + "ihnLo19fqaT4x9pU8JKWbQ.mdplQ3q9dk0T11vo817wyJ": 1685890800000, + "mwkVZhWne8ApD5t1BetxLd.IBTIot8BAicd74XmnPGytU": 12, + "qualifiedName": "default/glue/1688357913/AwsDataCatalog/development_published_impact/holding_rating", + "name": "holding_rating", + }, + { + "mwkVZhWne8ApD5t1BetxLd": {"IBTIot8BAicd74XmnPGytU": 12}, + "ihnLo19fqaT4x9pU8JKWbQ": { + "Cyw6GbqST9M1dhXIBE1yHp": 1688137200000, + "mdplQ3q9dk0T11vo817wyJ": 1685890800000, + }, + }, + ), + ( + [ + "ihnLo19fqaT4x9pU8JKWbQ.Cyw6GbqST9M1dhXIBE1yHp", + "ihnLo19fqaT4x9pU8JKWbQ.mdplQ3q9dk0T11vo817wyJ", + "ihnLo19fqaT4x9pU8JKWbQ.Xb3awoTsZGPu6vqnNxrzYF", + "mwkVZhWne8ApD5t1BetxLd.IBTIot8BAicd74XmnPGytU", + ], + { + "qualifiedName": "default/glue/1688357913/AwsDataCatalog/development_published_impact/holding_rating", + "name": "holding_rating", + }, + {}, + ), + ], +) +def test_unflatten_custom_metadata(attributes, flattened_attributes, custom_metadata): + assert custom_metadata == unflatten_custom_metadata( + attributes=attributes, asset_attributes=flattened_attributes + ) + + +@patch("pyatlan.utils.unflatten_custom_metadata") +def test_unflatten_custom_metadata_for_entity(mock_unflatten_custom_metadata): + custom_metadata = {"mwkVZhWne8ApD5t1BetxLd": {"IBTIot8BAicd74XmnPGytU": 12}} + mock_unflatten_custom_metadata.return_value = custom_metadata + entity = {"attributes": {"name": "dave"}} + attributes = ["name"] + + unflatten_custom_metadata_for_entity(entity=entity, attributes=attributes) + + assert "businessAttributes" in entity + assert entity["businessAttributes"] == custom_metadata + assert mock_unflatten_custom_metadata.callled_once_with( + attributes=attributes, asset_attributes=entity["attributes"] + ) + + +@patch("pyatlan.utils.unflatten_custom_metadata") +def test_unflatten_custom_metadata_for_entity_when_empty_dict_returned_does_not_modify( + mock_unflatten_custom_metadata, +): + mock_unflatten_custom_metadata.return_value = {} + entity = {"attributes": {"name": "dave"}} + attributes = ["name"] + + unflatten_custom_metadata_for_entity(entity=entity, attributes=attributes) + + assert "businessAttributes" not in entity + assert mock_unflatten_custom_metadata.callled_once_with( + attributes=attributes, asset_attributes=entity["attributes"] + ) + + +@pytest.mark.parametrize("entity", [({"attributes": {"name": "dave"}}), ({})]) +@patch("pyatlan.utils.unflatten_custom_metadata") +def test_unflatten_custom_metadata_for_entity_when_none_returned_does_not_modify( + mock_unflatten_custom_metadata, entity +): + mock_unflatten_custom_metadata.return_value = None + attributes = ["name"] + + unflatten_custom_metadata_for_entity(entity=entity, attributes=attributes) + + assert "businessAttributes" not in entity + assert mock_unflatten_custom_metadata.callled_once_with( + attributes=attributes, asset_attrubtes=entity.get("attributes", None) + ) + + +@pytest.mark.parametrize( + "value, result", + [ + ("string", "string"), + ("array", "string"), + ("array>", "string"), + ("map", "string"), + ], +) +def test_get_base_type(value, result): + assert get_base_type(value) == result + + +@pytest.mark.parametrize( + "attribute_type", + [ + "boolean", + "array", + "array>", + "map", + ], +) +@pytest.mark.parametrize( + "to, expected", + [ + (ComparisonCategory.BOOLEAN, True), + (ComparisonCategory.STRING, False), + (ComparisonCategory.NUMBER, False), + ], +) +def test_is_comparable_type_b(attribute_type, to, expected): + assert is_comparable_type(attribute_type=attribute_type, to=to) == expected + + +@pytest.mark.parametrize( + "attribute_type", + [ + "string", + "array", + "array>", + "map", + ], +) +@pytest.mark.parametrize( + "to, expected", + [ + (ComparisonCategory.STRING, True), + (ComparisonCategory.BOOLEAN, False), + (ComparisonCategory.NUMBER, False), + ], +) +def test_is_comparable_type_s(attribute_type, to, expected): + assert is_comparable_type(attribute_type=attribute_type, to=to) == expected + + +@pytest.mark.parametrize( + "attribute_type", + [ + attribute_type + for inner_type in ["int", "long", "date", "float"] + for attribute_type in [f"{inner_type}", f"array<{inner_type}>"] + ], +) +@pytest.mark.parametrize( + "to, expected", + [ + (ComparisonCategory.NUMBER, True), + (ComparisonCategory.BOOLEAN, False), + (ComparisonCategory.STRING, False), + ], +) +def test_is_comparable_type_n(attribute_type, to, expected): + assert is_comparable_type(attribute_type=attribute_type, to=to) == expected + + +@pytest.mark.parametrize( + "name, the_type, value, message", + [ + ( + "bob", + int, + "a", + "ATLAN-PYTHON-400-048 Invalid parameter type for bob should be int", + ), + ( + "bob", + int, + False, + "ATLAN-PYTHON-400-048 Invalid parameter type for bob should be int", + ), + ( + "bob", + int, + None, + "ATLAN-PYTHON-400-048 Invalid parameter type for bob should be int", + ), + ( + "bob", + bool, + None, + "ATLAN-PYTHON-400-048 Invalid parameter type for bob should be bool", + ), + ( + "bob", + bool, + 1, + "ATLAN-PYTHON-400-048 Invalid parameter type for bob should be bool", + ), + ( + "bob", + bool, + "True", + "ATLAN-PYTHON-400-048 Invalid parameter type for bob should be bool", + ), + ], +) +def test_validate_type_with_invalid_values(name, the_type, value, message): + with pytest.raises(InvalidRequestError, match=message): + validate_type(name=name, _type=the_type, value=value) + + +@pytest.mark.parametrize( + "name, the_type, value", + [ + ("bob", int, 1), + ("bob", str, "abc"), + ("bob", bool, False), + ("bob", object, {}), + ], +) +def test_validate_type_with_valid_values(name, the_type, value): + validate_type(name=name, _type=the_type, value=value) + + +@pytest.mark.parametrize( + "prefix, name, expected_key", + [ + ("", "file.txt", "file.txt"), + ("folder", "file.txt", "folder/file.txt"), + ("folder/", "//file.txt", "folder/file.txt"), + ("folder/", "/////file.txt//", "folder/file.txt"), + ("/folder/", "file.txt", "/folder/file.txt"), + ("folder/subfolder", "file.txt", "folder/subfolder/file.txt"), + ("folder/subfolder/", "file.txt", "folder/subfolder/file.txt"), + ("/", "file.txt", "/file.txt"), + ("/", "file.txt/", "/file.txt"), + # Additional edge cases + ("//tmp/iicer-miner/", "file.txt", "//tmp/iicer-miner/file.txt"), + ("//logs/", "output.log", "//logs/output.log"), + ("test-bucket//", "file.txt", "test-bucket//file.txt"), + ("//", "file.txt", "//file.txt"), + ("//", "/file.txt", "//file.txt"), + ("/tmp/", "/data/file.txt", "/tmp/data/file.txt"), + ("/data//", "nested/file.txt", "/data//nested/file.txt"), + ("///deep/path/", "/to/file.txt", "///deep/path/to/file.txt"), + ("/tmp/iics-miner/input/", "file.txt", "/tmp/iics-miner/input/file.txt"), + ], +) +def test_contruct_object_key(prefix, name, expected_key): + key = construct_object_key(prefix, name) + assert key == expected_key + + +@pytest.mark.parametrize( + "custom_connectors", + [ + [ + AtlanConnectorType.CREATE_CUSTOM( + name="FOO", value="foo", category=AtlanConnectionCategory.BI + ), + AtlanConnectorType.CREATE_CUSTOM( + name="BAR", value="bar", category=AtlanConnectionCategory.API + ), + AtlanConnectorType.CREATE_CUSTOM( + name="BAZ", value="baz", category=AtlanConnectionCategory.WAREHOUSE + ), + ] + ], +) +def test_atlan_connector_type_create_custom(custom_connectors): + for custom_connector in custom_connectors: + assert custom_connector and custom_connector.category + assert custom_connector.value in AtlanConnectorType.get_values() + assert custom_connector.strip() in AtlanConnectorType.get_values() + assert custom_connector.name in AtlanConnectorType.get_names() + assert ( + custom_connector.name, + custom_connector.value, + ) in AtlanConnectorType.get_items() + + +def test_atlan_connector_type_create_custom_force_lowercase(): + """Test that CREATE_CUSTOM method force-lowercases the value parameter to avoid case-related issues.""" + + custom_connector = AtlanConnectorType.CREATE_CUSTOM( + name="MIXED_CASE", value="MiXeD_CaSe", category=AtlanConnectionCategory.CUSTOM + ) + assert custom_connector.value == "mixed_case" + + custom_connector_upper = AtlanConnectorType.CREATE_CUSTOM( + name="UPPER_CASE", value="UPPERCASE", category=AtlanConnectionCategory.CUSTOM + ) + assert custom_connector_upper.value == "uppercase" + + custom_connector_lower = AtlanConnectorType.CREATE_CUSTOM( + name="LOWER_CASE", value="lowercase", category=AtlanConnectionCategory.CUSTOM + ) + assert custom_connector_lower.value == "lowercase" + + custom_connector_special = AtlanConnectorType.CREATE_CUSTOM( + name="SPECIAL_CHARS", + value="My-Connector_Type", + category=AtlanConnectionCategory.CUSTOM, + ) + assert custom_connector_special.value == "my-connector_type" + + +@pytest.mark.parametrize( + "custom_asset_qns", + [ + [ + "default/c1/1234567890/asset/name", + "default/c2/1234567890/asset/name", + "default/c3/1234567890/asset/name", + # Duplicate custom connector names + "default/c1/1234567890/asset/name", + "default/c2/1234567890/asset/name", + "default/c3/1234567890/asset/name", + # Duplicate custom connector names + "default/c1/1234567890/asset/name", + "default/c2/1234567890/asset/name", + "default/c3/1234567890/asset/name", + ] + ], +) +def test_atlan_connector_type_custom_asset_qn(custom_asset_qns): + # Calculate the unique custom QNs + unique_custom_qns = set(custom_asset_qns) + + # Calculate the initial lengths of predefined values, names, and items + len_get_values = len(AtlanConnectorType.get_values()) + len_get_names = len(AtlanConnectorType.get_names()) + len_get_items = len(AtlanConnectorType.get_items()) + + # Check each custom QN + for custom_qn in custom_asset_qns: + custom_connector = AtlanConnectorType._get_connector_type_from_qualified_name( + custom_qn + ) + custom_connector.category = AtlanConnectionCategory.CUSTOM + assert custom_connector.value in AtlanConnectorType.get_values() + assert custom_connector.strip() in AtlanConnectorType.get_values() + assert ( + custom_connector.name, + custom_connector.value, + ) in AtlanConnectorType.get_items() + + # Ensure the value is only added once (i.e no duplicates) + assert len(AtlanConnectorType.get_values()) == len_get_values + len( + unique_custom_qns + ) + assert len(AtlanConnectorType.get_names()) == len_get_names + len(unique_custom_qns) + assert len(AtlanConnectorType.get_items()) == len_get_items + len(unique_custom_qns) + + +@pytest.mark.parametrize( + "custom_connection_qns", + [ + [ + "default/cm1/1234567890", + "default/cm2/1234567890", + "default/cm3/1234567890", + # Duplicate custom connector names + "default/cm1/1234567890", + "default/cm2/1234567890", + "default/cm3/1234567890", + # Duplicate custom connector names + "default/cm1/1234567890", + "default/cm2/1234567890", + "default/cm3/1234567890", + ] + ], +) +def test_atlan_connector_type_custom_connection_qn(custom_connection_qns): + # Calculate the unique custom QNs + unique_custom_qns = set(custom_connection_qns) + + # Calculate the initial lengths of predefined values, names, and items + len_get_values = len(AtlanConnectorType.get_values()) + len_get_names = len(AtlanConnectorType.get_names()) + len_get_items = len(AtlanConnectorType.get_items()) + + # Check each custom QN + for custom_qn in custom_connection_qns: + custom_connector_name = AtlanConnectorType.get_connector_name(custom_qn) + assert custom_connector_name in AtlanConnectorType.get_values() + assert custom_connector_name.strip() in AtlanConnectorType.get_values() + + # Ensure the value is only added once (i.e no duplicates) + assert len(AtlanConnectorType.get_values()) == len_get_values + len( + unique_custom_qns + ) + assert len(AtlanConnectorType.get_names()) == len_get_names + len(unique_custom_qns) + assert len(AtlanConnectorType.get_items()) == len_get_items + len(unique_custom_qns) diff --git a/tests_v9/unit/test_workflow_client.py b/tests_v9/unit/test_workflow_client.py new file mode 100644 index 000000000..2c05984a5 --- /dev/null +++ b/tests_v9/unit/test_workflow_client.py @@ -0,0 +1,999 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2025 Atlan Pte. Ltd. + +""" +Unit tests for workflow client — ported from tests/unit/test_workflow_client.py. + +Uses v9 msgspec.Struct workflow models. V9WorkflowClient returns v9 msgspec +models natively, so plain ``isinstance`` is used for type assertions. +""" + +from unittest.mock import Mock, patch + +import msgspec +import pytest + +from pyatlan.client.common import ApiCaller +from pyatlan.client.constants import ( + SCHEDULE_QUERY_WORKFLOWS_MISSED, + SCHEDULE_QUERY_WORKFLOWS_SEARCH, + WORKFLOW_INDEX_RUN_SEARCH, + WORKFLOW_INDEX_SEARCH, +) +from pyatlan_v9.client.atlan import AtlanClient +from pyatlan_v9.client.workflow import V9WorkflowClient as WorkflowClient +from pyatlan_v9.errors import InvalidRequestError +from pyatlan_v9.model.enums import AtlanWorkflowPhase, WorkflowPackage + +# v9 models +from pyatlan_v9.model.workflow import ( + PackageParameter, + ScheduleQueriesSearchRequest, + Workflow, + WorkflowMetadata, + WorkflowResponse, + WorkflowRunResponse, + WorkflowSchedule, + WorkflowScheduleResponse, + WorkflowScheduleSpec, + WorkflowScheduleStatus, + WorkflowSearchHits, + WorkflowSearchRequest, + WorkflowSearchResponse, + WorkflowSearchResult, + WorkflowSearchResultDetail, + WorkflowSearchResultStatus, + WorkflowSpec, +) +from tests_v9.unit.constants import TEST_WORKFLOW_CLIENT_METHODS + + +def _to_dict(model): + """Convert a msgspec.Struct model to a plain dict (for mock return values).""" + return msgspec.to_builtins(model) + + +@pytest.fixture(autouse=True) +def set_env(monkeypatch): + monkeypatch.setenv("ATLAN_BASE_URL", "https://test.atlan.com") + monkeypatch.setenv("ATLAN_API_KEY", "test-api-key") + + +@pytest.fixture() +def mock_api_caller(): + mock = Mock(spec=ApiCaller) + # Add role_cache attribute to the mock + mock.role_cache = Mock() + mock.role_cache.is_api_token_user.return_value = ( + False # Default to non-API token user + ) + return mock + + +@pytest.fixture() +def mock_workflow_time_sleep(): + with patch("pyatlan_v9.client.workflow.sleep") as mock_time_sleep: + yield mock_time_sleep + + +@pytest.fixture() +def client(mock_api_caller) -> WorkflowClient: + return WorkflowClient(mock_api_caller) + + +@pytest.fixture() +def search_result_status() -> WorkflowSearchResultStatus: + return WorkflowSearchResultStatus(phase=AtlanWorkflowPhase.RUNNING) + + +@pytest.fixture() +def search_result_detail( + search_result_status: WorkflowSearchResultStatus, +) -> WorkflowSearchResultDetail: + return WorkflowSearchResultDetail( + api_version="1", + kind="kind", + metadata=WorkflowMetadata(name="name", namespace="namespace"), + spec=WorkflowSpec(), + status=search_result_status, + ) + + +@pytest.fixture() +def search_result(search_result_detail) -> WorkflowSearchResult: + return WorkflowSearchResult( + index="index", + type="type", + id="id", + seq_no=1, + primary_term=2, + sort=["sort"], + source=search_result_detail, + ) + + +@pytest.fixture() +def search_response(search_result: WorkflowSearchResult) -> WorkflowSearchResponse: + return WorkflowSearchResponse( + hits=WorkflowSearchHits(total={"dummy": "dummy"}, hits=[search_result]), + shards={"dummy": "dummy"}, + ) + + +@pytest.fixture() +def rerun_response() -> WorkflowRunResponse: + return WorkflowRunResponse( + status=WorkflowSearchResultStatus(), + metadata=WorkflowMetadata(name="name", namespace="namespace"), + spec=WorkflowSpec(), + ) + + +@pytest.fixture() +def rerun_response_with_idempotent( + search_result_status: WorkflowSearchResultStatus, +) -> WorkflowRunResponse: + return WorkflowRunResponse( + metadata=WorkflowMetadata(name="name", namespace="namespace"), + spec=WorkflowSpec(), + status=search_result_status, + ) + + +@pytest.fixture() +def workflow_response() -> WorkflowResponse: + return WorkflowResponse( + metadata=WorkflowMetadata(name="name", namespace="namespace"), + spec=WorkflowSpec(), + payload=[PackageParameter(parameter="test-param", type="test-type", body={})], + ) + + +@pytest.fixture() +def workflow_run_response() -> WorkflowRunResponse: + return WorkflowRunResponse( + metadata=WorkflowMetadata(name="name", namespace="namespace"), + spec=WorkflowSpec(), + payload=[PackageParameter(parameter="test-param", type="test-type", body={})], + status=WorkflowSearchResultStatus(phase=AtlanWorkflowPhase.RUNNING), + ) + + +@pytest.fixture() +def schedule() -> WorkflowSchedule: + return WorkflowSchedule(timezone="Europe/Paris", cron_schedule="45 4 * * *") + + +@pytest.fixture() +def schedule_response() -> WorkflowScheduleResponse: + return WorkflowScheduleResponse( + spec=WorkflowScheduleSpec(), + metadata=WorkflowMetadata(name="name", namespace="namespace"), + workflow_metadata=WorkflowMetadata(name="name", namespace="namespace"), + status=WorkflowScheduleStatus( + active="test-active", + conditions="test-conditions", + last_scheduled_time="test-last-scheduled-time", + ), + ) + + +@pytest.fixture() +def update_response() -> WorkflowResponse: + return WorkflowResponse( + metadata=WorkflowMetadata(name="name", namespace="namespace"), + spec=WorkflowSpec(), + ) + + +# --------------------------------------------------------------------------- +# Validation-error tests +# --------------------------------------------------------------------------- + + +@pytest.mark.parametrize("method, params", TEST_WORKFLOW_CLIENT_METHODS.items()) +def test_workflow_client_methods_validation_error(method, params): + client_method = getattr(AtlanClient().workflow, method) + for param_values, error_msg in params: + with pytest.raises(ValueError, match=error_msg): + client_method(*param_values) + + +@pytest.mark.parametrize("workflow", ["abc", None]) +def test_workflow_rerun_invalid_request_error(client, workflow): + with pytest.raises( + InvalidRequestError, + match=( + "ATLAN-PYTHON-400-048 Invalid parameter type for workflow should " + "be WorkflowPackage, WorkflowSearchResultDetail or WorkflowSearchResult. " + "Suggestion: Check that you have used the correct type of parameter." + ), + ): + client.rerun(workflow) + + +@pytest.mark.parametrize("workflow, workflow_schedule", [[None, 123], [123, "123"]]) +def test_workflow_run_invalid_request_error(client, workflow, workflow_schedule): + with pytest.raises( + InvalidRequestError, + match=( + "ATLAN-PYTHON-400-048 Invalid parameter type for workflow should be Workflow or str. " + "Suggestion: Check that you have used the correct type of parameter." + ), + ): + client.run(workflow) + + valid_workflow = Workflow( + metadata=WorkflowMetadata(name="name", namespace="namespace"), + spec=WorkflowSpec(), + payload=[PackageParameter(parameter="test-param", type="test-type", body={})], + ) + + with pytest.raises( + InvalidRequestError, + match=( + "ATLAN-PYTHON-400-048 Invalid parameter type for workflow_schedule should be WorkflowSchedule or None. " + "Suggestion: Check that you have used the correct type of parameter." + ), + ): + client.run(valid_workflow, workflow_schedule) + + +@pytest.mark.parametrize( + "workflow, schedule", + [ + ("abc", WorkflowSchedule(timezone="atlan", cron_schedule="*")), + (None, WorkflowSchedule(timezone="atlan", cron_schedule="*")), + ], +) +def test_workflow_add_schedule_invalid_request_error(client, workflow, schedule): + with pytest.raises( + InvalidRequestError, + match=( + "ATLAN-PYTHON-400-048 Invalid parameter type for workflow should " + "be WorkflowResponse, WorkflowPackage, WorkflowSearchResult or WorkflowSearchResultDetail. " + "Suggestion: Check that you have used the correct type of parameter." + ), + ): + client.add_schedule(workflow, schedule) + + +@pytest.mark.parametrize( + "workflow", + [ + "abc", + None, + ], +) +def test_workflow_remove_schedule_invalid_request_error(client, workflow): + with pytest.raises( + InvalidRequestError, + match=( + "ATLAN-PYTHON-400-048 Invalid parameter type for workflow should " + "be WorkflowResponse, WorkflowPackage, WorkflowSearchResult or WorkflowSearchResultDetail. " + "Suggestion: Check that you have used the correct type of parameter." + ), + ): + client.add_schedule(workflow, schedule) + + +@pytest.mark.parametrize("api_caller", ["abc", None]) +def test_init_when_wrong_class_raises_exception(api_caller): + with pytest.raises( + InvalidRequestError, + match="ATLAN-PYTHON-400-048 Invalid parameter type for client should be ApiCaller", + ): + WorkflowClient(api_caller) + + +# --------------------------------------------------------------------------- +# Client method tests +# --------------------------------------------------------------------------- + + +def test_find_by_type(client: WorkflowClient, mock_api_caller): + raw_json = {"shards": {"dummy": None}, "hits": {"total": {"dummy": None}}} + mock_api_caller._call_api.return_value = raw_json + + assert client.find_by_type(prefix=WorkflowPackage.FIVETRAN) == [] + mock_api_caller._call_api.assert_called_once() + assert mock_api_caller._call_api.call_args.args[0] == WORKFLOW_INDEX_SEARCH + assert isinstance( + mock_api_caller._call_api.call_args.kwargs["request_obj"], WorkflowSearchRequest + ) + + +def test_find_runs_by_status_and_time_range(client: WorkflowClient, mock_api_caller): + raw_json = {"_shards": {"dummy": None}, "hits": {"total": {"dummy": None}}} + mock_api_caller._call_api.return_value = raw_json + + status = [AtlanWorkflowPhase.SUCCESS, AtlanWorkflowPhase.FAILED] + started_at = "now-2h" + finished_at = "now-1h" + response = client.find_runs_by_status_and_time_range( + status=status, + started_at=started_at, + finished_at=finished_at, + from_=10, + size=5, + ) + assert isinstance(response, WorkflowSearchResponse) + mock_api_caller._call_api.assert_called_once() + request_obj = mock_api_caller._call_api.call_args.kwargs["request_obj"] + assert isinstance(request_obj, WorkflowSearchRequest) + assert request_obj.query + must_clauses = request_obj.query["bool"]["must"] + range_filters = [c for c in must_clauses if "range" in c] + assert any( + "status.startedAt" in rf["range"] + and rf["range"]["status.startedAt"].get("gte") == started_at + for rf in range_filters + ) + finished_filters = [ + rf for rf in range_filters if "status.finishedAt" in rf["range"] + ] + assert len(finished_filters) == 1 + finished_filter = finished_filters[0]["range"]["status.finishedAt"] + assert finished_filter["lte"] == finished_at + assert finished_filter.get("gte") is None + + +def test_find_by_id( + client: WorkflowClient, search_response: WorkflowSearchResponse, mock_api_caller +): + raw_json = _to_dict(search_response) + mock_api_caller._call_api.return_value = raw_json + + assert search_response.hits and search_response.hits.hits + result = client.find_by_id(id="atlan-snowflake-miner-1714638976") + assert isinstance(result, WorkflowSearchResult) + assert result.id == "id" + mock_api_caller._call_api.assert_called_once() + assert mock_api_caller._call_api.call_args.args[0] == WORKFLOW_INDEX_SEARCH + assert isinstance( + mock_api_caller._call_api.call_args.kwargs["request_obj"], WorkflowSearchRequest + ) + + +def test_find_run_by_id( + client: WorkflowClient, search_response: WorkflowSearchResponse, mock_api_caller +): + raw_json = _to_dict(search_response) + mock_api_caller._call_api.return_value = raw_json + + assert search_response and search_response.hits and search_response.hits.hits + result = client.find_run_by_id(id="atlan-snowflake-miner-1714638976-mzdza") + assert isinstance(result, WorkflowSearchResult) + assert result.id == "id" + mock_api_caller._call_api.assert_called_once() + assert mock_api_caller._call_api.call_args.args[0] == WORKFLOW_INDEX_RUN_SEARCH + assert isinstance( + mock_api_caller._call_api.call_args.kwargs["request_obj"], WorkflowSearchRequest + ) + + +def test_re_run_when_given_workflowpackage_with_no_prior_runs_raises_invalid_request_error( + client: WorkflowClient, mock_api_caller +): + raw_json = {"shards": {"dummy": None}, "hits": {"total": {"dummy": None}}} + mock_api_caller._call_api.return_value = raw_json + + with pytest.raises( + InvalidRequestError, + match="ATLAN-PYTHON-400-047 No prior runs of atlan-fivetran were available.", + ): + client.rerun(WorkflowPackage.FIVETRAN) + + +def test_re_run_when_given_workflowpackage( + client: WorkflowClient, + mock_api_caller, + search_response: WorkflowSearchResponse, + rerun_response: WorkflowRunResponse, +): + mock_api_caller._call_api.side_effect = [ + _to_dict(search_response), + _to_dict(rerun_response), + ] + + response = client.rerun(WorkflowPackage.FIVETRAN) + assert isinstance(response, WorkflowRunResponse) + assert response.metadata.name == "name" + assert mock_api_caller._call_api.call_count == 2 + mock_api_caller.reset_mock() + + +def test_re_run_when_given_workflowsearchresultdetail( + client: WorkflowClient, + mock_api_caller, + search_result_detail: WorkflowSearchResultDetail, + rerun_response: WorkflowRunResponse, +): + mock_api_caller._call_api.return_value = _to_dict(rerun_response) + + response = client.rerun(workflow=search_result_detail) + assert isinstance(response, WorkflowRunResponse) + assert response.metadata.name == "name" + assert mock_api_caller._call_api.call_count == 1 + mock_api_caller.reset_mock() + + +def test_re_run_when_given_workflowsearchresult( + client: WorkflowClient, + mock_api_caller, + search_result: WorkflowSearchResult, + rerun_response: WorkflowRunResponse, +): + mock_api_caller._call_api.return_value = _to_dict(rerun_response) + + response = client.rerun(workflow=search_result) + assert isinstance(response, WorkflowRunResponse) + assert response.metadata.name == "name" + assert mock_api_caller._call_api.call_count == 1 + mock_api_caller.reset_mock() + + +def test_re_run_when_given_workflowpackage_with_idempotent( + client: WorkflowClient, + mock_api_caller, + mock_workflow_time_sleep, + search_response: WorkflowSearchResponse, + rerun_response_with_idempotent: WorkflowRunResponse, +): + mock_api_caller._call_api.side_effect = [ + _to_dict(search_response), + _to_dict(search_response), + ] + + response = client.rerun(WorkflowPackage.FIVETRAN, idempotent=True) + assert isinstance(response, WorkflowRunResponse) + assert response.metadata.name == "name" + assert response.status.phase == AtlanWorkflowPhase.RUNNING + assert mock_api_caller._call_api.call_count == 2 + mock_api_caller.reset_mock() + + +def test_re_run_when_given_workflowsearchresultdetail_with_idempotent( + client: WorkflowClient, + mock_api_caller, + mock_workflow_time_sleep, + search_response: WorkflowSearchResponse, + search_result_detail: WorkflowSearchResultDetail, + rerun_response_with_idempotent: WorkflowRunResponse, +): + mock_api_caller._call_api.return_value = _to_dict(search_response) + + response = client.rerun(workflow=search_result_detail, idempotent=True) + assert isinstance(response, WorkflowRunResponse) + assert response.metadata.name == "name" + assert response.status.phase == AtlanWorkflowPhase.RUNNING + assert mock_api_caller._call_api.call_count == 1 + mock_api_caller.reset_mock() + + +def test_re_run_when_given_workflowsearchresult_with_idempotent( + client: WorkflowClient, + mock_api_caller, + mock_workflow_time_sleep, + search_response: WorkflowSearchResponse, + search_result: WorkflowSearchResult, + rerun_response_with_idempotent: WorkflowRunResponse, +): + mock_api_caller._call_api.return_value = _to_dict(search_response) + + response = client.rerun(workflow=search_result, idempotent=True) + assert isinstance(response, WorkflowRunResponse) + assert response.metadata.name == "name" + assert response.status.phase == AtlanWorkflowPhase.RUNNING + assert mock_api_caller._call_api.call_count == 1 + mock_api_caller.reset_mock() + + +def test_run_when_given_workflow( + client: WorkflowClient, + mock_api_caller, + workflow_response: WorkflowResponse, +): + mock_api_caller._call_api.return_value = _to_dict(workflow_response) + response = client.run( + Workflow( + metadata=WorkflowMetadata(name="name", namespace="namespace"), + spec=WorkflowSpec(), + payload=[ + PackageParameter(parameter="test-param", type="test-type", body={}) + ], + ) + ) + assert isinstance(response, WorkflowResponse) + assert response.metadata.name == "name" + assert mock_api_caller._call_api.call_count == 1 + mock_api_caller.reset_mock() + + +def test_run_when_given_workflow_json( + client: WorkflowClient, + mock_api_caller, + workflow_response: WorkflowResponse, +): + mock_api_caller._call_api.return_value = _to_dict(workflow_response) + workflow_json = r""" + { + "metadata": {"name": "name", "namespace": "namespace"}, + "spec": {}, + "payload": [{"parameter": "test-param", "type": "test-type", "body": {}}] + } + """ + response = client.run(workflow_json) + assert isinstance(response, WorkflowResponse) + assert response.metadata.name == "name" + assert mock_api_caller._call_api.call_count == 1 + mock_api_caller.reset_mock() + + +def test_run_when_given_workflow_with_schedule( + client: WorkflowClient, + schedule: WorkflowSchedule, + mock_api_caller, + workflow_response: WorkflowResponse, +): + mock_api_caller._call_api.return_value = _to_dict(workflow_response) + response = client.run( + Workflow( + metadata=WorkflowMetadata( + name="name", + namespace="namespace", + annotations={"existing": "value"}, + ), + spec=WorkflowSpec(), + payload=[ + PackageParameter(parameter="test-param", type="test-type", body={}) + ], + ), + workflow_schedule=schedule, + ) + assert isinstance(response, WorkflowResponse) + assert response.metadata.name == "name" + assert mock_api_caller._call_api.call_count == 1 + mock_api_caller.reset_mock() + + +def test_run_when_given_workflow_json_with_schedule( + client: WorkflowClient, + schedule: WorkflowSchedule, + mock_api_caller, + workflow_response: WorkflowResponse, +): + mock_api_caller._call_api.return_value = _to_dict(workflow_response) + workflow_json = r""" + { + "metadata": {"name": "name", "namespace": "namespace"}, + "spec": {}, + "payload": [{"parameter": "test-param", "type": "test-type", "body": {}}] + } + """ + response = client.run(workflow_json, workflow_schedule=schedule) + assert isinstance(response, WorkflowResponse) + assert response.metadata.name == "name" + assert mock_api_caller._call_api.call_count == 1 + mock_api_caller.reset_mock() + + +def test_update_when_given_workflow( + client: WorkflowClient, + mock_api_caller, + search_result: WorkflowSearchResult, + update_response: WorkflowResponse, +): + mock_api_caller._call_api.return_value = _to_dict(update_response) + assert search_result.to_workflow() + response = client.updater(workflow=search_result.to_workflow()) + assert isinstance(response, WorkflowResponse) + assert response.metadata.name == "name" + assert mock_api_caller._call_api.call_count == 1 + mock_api_caller.reset_mock() + + +def test_workflow_update_owner( + client: WorkflowClient, + mock_api_caller, + workflow_response: WorkflowResponse, +): + mock_api_caller._call_api.return_value = _to_dict(workflow_response) + response = client.update_owner(workflow_name="test-workflow", username="test-owner") + + assert mock_api_caller._call_api.call_count == 1 + assert isinstance(response, WorkflowResponse) + assert response.metadata.name == "name" + mock_api_caller.reset_mock() + + +def test_workflow_get_runs( + client: WorkflowClient, + mock_api_caller, + search_response: WorkflowSearchResponse, +): + mock_api_caller._call_api.return_value = _to_dict(search_response) + response = client.get_runs( + workflow_name="test-workflow", + workflow_phase=AtlanWorkflowPhase.RUNNING, + ) + + assert isinstance(response, WorkflowSearchResponse) + assert mock_api_caller._call_api.call_count == 1 + mock_api_caller.reset_mock() + + +def test_workflow_stop( + client: WorkflowClient, + mock_api_caller, + workflow_run_response: WorkflowRunResponse, +): + mock_api_caller._call_api.return_value = _to_dict(workflow_run_response) + response = client.stop(workflow_run_id="test-workflow-run-id") + + assert isinstance(response, WorkflowRunResponse) + assert response.metadata.name == "name" + assert mock_api_caller._call_api.call_count == 1 + mock_api_caller.reset_mock() + + +def test_workflow_delete(client: WorkflowClient, mock_api_caller): + mock_api_caller._call_api.return_value = None + assert not client.delete(workflow_name="test-workflow") + + +def test_workflow_add_schedule( + client: WorkflowClient, + schedule: WorkflowSchedule, + workflow_response: WorkflowResponse, + search_response: WorkflowSearchResponse, + search_result: WorkflowSearchResult, + mock_api_caller, +): + # Workflow response + mock_api_caller._call_api.side_effect = [ + _to_dict(workflow_response), + ] + response = client.add_schedule( + workflow=workflow_response, workflow_schedule=schedule + ) + + assert mock_api_caller._call_api.call_count == 1 + assert isinstance(response, WorkflowResponse) + assert response.metadata.name == "name" + mock_api_caller.reset_mock() + + # Workflow package + mock_api_caller._call_api.side_effect = [ + _to_dict(search_response), + _to_dict(workflow_response), + ] + response = client.add_schedule( + workflow=WorkflowPackage.FIVETRAN, workflow_schedule=schedule + ) + + assert mock_api_caller._call_api.call_count == 2 + assert isinstance(response, WorkflowResponse) + assert response.metadata.name == "name" + mock_api_caller.reset_mock() + + # Workflow search result + mock_api_caller._call_api.side_effect = [_to_dict(workflow_response)] + response = client.add_schedule(workflow=search_result, workflow_schedule=schedule) + + assert mock_api_caller._call_api.call_count == 1 + assert isinstance(response, WorkflowResponse) + assert response.metadata.name == "name" + mock_api_caller.reset_mock() + + +def test_workflow_find_schedule_query_between( + client: WorkflowClient, mock_api_caller, workflow_run_response: WorkflowRunResponse +): + mock_api_caller._call_api.return_value = [_to_dict(workflow_run_response)] + response = client.find_schedule_query_between( + ScheduleQueriesSearchRequest( + start_date="2024-05-03T16:30:00.000+05:30", + end_date="2024-05-05T00:59:00.000+05:30", + ) + ) + + assert mock_api_caller._call_api.call_count == 1 + assert response and len(response) == 1 + assert isinstance(response[0], WorkflowRunResponse) + # Ensure it is called by the correct API endpoint + assert ( + mock_api_caller._call_api.call_args[0][0].path + == SCHEDULE_QUERY_WORKFLOWS_SEARCH.path + ) + mock_api_caller.reset_mock() + + # Missed schedule query workflows + mock_api_caller._call_api.return_value = [_to_dict(workflow_run_response)] + response = client.find_schedule_query_between( + ScheduleQueriesSearchRequest( + start_date="2024-05-03T16:30:00.000+05:30", + end_date="2024-05-05T00:59:00.000+05:30", + ), + missed=True, + ) + + assert mock_api_caller._call_api.call_count == 1 + # Ensure it is called by the correct API endpoint + assert ( + mock_api_caller._call_api.call_args[0][0].path + == SCHEDULE_QUERY_WORKFLOWS_MISSED.path + ) + assert response and len(response) == 1 + assert isinstance(response[0], WorkflowRunResponse) + mock_api_caller.reset_mock() + + # None response + mock_api_caller._call_api.return_value = None + response = client.find_schedule_query_between( + ScheduleQueriesSearchRequest( + start_date="2024-05-03T16:30:00.000+05:30", + end_date="2024-05-05T00:59:00.000+05:30", + ) + ) + + assert mock_api_caller._call_api.call_count == 1 + assert response is None + mock_api_caller.reset_mock() + + +def test_workflow_find_schedule_query( + client: WorkflowClient, + mock_api_caller, + search_response: WorkflowSearchResponse, + search_result: WorkflowSearchResult, +): + mock_api_caller._call_api.return_value = _to_dict(search_response) + response = client.find_schedule_query( + saved_query_id="test-query-id", max_results=50 + ) + + assert len(response) == 1 + assert mock_api_caller._call_api.call_count == 1 + assert isinstance(response[0], WorkflowSearchResult) + assert response[0].id == search_result.id + mock_api_caller.reset_mock() + + +def test_workflow_rerun_schedule_query_workflow( + client, + mock_api_caller, + workflow_run_response: WorkflowRunResponse, +): + mock_api_caller._call_api.return_value = _to_dict(workflow_run_response) + response = client.re_run_schedule_query(schedule_query_id="test-query-id") + + assert mock_api_caller._call_api.call_count == 1 + assert isinstance(response, WorkflowRunResponse) + assert response.metadata.name == "name" + + +def test_workflow_remove_schedule( + client: WorkflowClient, + workflow_response: WorkflowResponse, + search_response: WorkflowSearchResponse, + search_result: WorkflowSearchResult, + mock_api_caller, +): + # Workflow response + mock_api_caller._call_api.side_effect = [ + _to_dict(workflow_response), + ] + response = client.remove_schedule(workflow=workflow_response) + + assert mock_api_caller._call_api.call_count == 1 + assert isinstance(response, WorkflowResponse) + assert response.metadata.name == "name" + mock_api_caller.reset_mock() + + # Workflow package + mock_api_caller._call_api.side_effect = [ + _to_dict(search_response), + _to_dict(workflow_response), + ] + response = client.remove_schedule(workflow=WorkflowPackage.FIVETRAN) + + assert mock_api_caller._call_api.call_count == 2 + assert isinstance(response, WorkflowResponse) + assert response.metadata.name == "name" + mock_api_caller.reset_mock() + + # Workflow search result + mock_api_caller._call_api.side_effect = [_to_dict(workflow_response)] + response = client.remove_schedule(workflow=search_result) + + assert mock_api_caller._call_api.call_count == 1 + assert isinstance(response, WorkflowResponse) + assert response.metadata.name == "name" + mock_api_caller.reset_mock() + + +def test_workflow_get_all_scheduled_runs( + client: WorkflowClient, + workflow_response: WorkflowResponse, + search_response: WorkflowSearchResponse, + search_result: WorkflowSearchResult, + schedule_response: WorkflowScheduleResponse, + mock_api_caller, +): + mock_api_caller._call_api.return_value = {"items": [_to_dict(schedule_response)]} + response = client.get_all_scheduled_runs() + + assert mock_api_caller._call_api.call_count == 1 + assert response and len(response) == 1 + assert isinstance(response[0], WorkflowScheduleResponse) + mock_api_caller.reset_mock() + + +def test_workflow_get_scheduled_run( + client: WorkflowClient, + workflow_response: WorkflowResponse, + search_response: WorkflowSearchResponse, + search_result: WorkflowSearchResult, + schedule_response: WorkflowScheduleResponse, + mock_api_caller, +): + mock_api_caller._call_api.return_value = _to_dict(schedule_response) + response = client.get_scheduled_run(workflow_name="test-workflow") + + assert mock_api_caller._call_api.call_count == 1 + assert isinstance(response, WorkflowScheduleResponse) + mock_api_caller.reset_mock() + + +# --------------------------------------------------------------------------- +# role_cache integration tests +# --------------------------------------------------------------------------- + + +def test_rerun_with_role_cache_api_token_user( + client: WorkflowClient, + mock_api_caller, + mock_role_cache, + search_result: WorkflowSearchResult, + rerun_response: WorkflowRunResponse, +): + """Test that rerun uses package endpoint when user is API token user.""" + mock_role_cache.is_api_token_user.return_value = True + mock_api_caller.role_cache = mock_role_cache + mock_api_caller._call_api.return_value = _to_dict(rerun_response) + + response = client.rerun(search_result) + + mock_role_cache.is_api_token_user.assert_called_once() + mock_api_caller._call_api.assert_called_once() + assert isinstance(response, WorkflowRunResponse) + mock_api_caller.reset_mock() + + +def test_rerun_with_role_cache_non_api_token_user( + client: WorkflowClient, + mock_api_caller, + mock_role_cache, + search_result: WorkflowSearchResult, + rerun_response: WorkflowRunResponse, +): + """Test that rerun uses non-package endpoint when user is not API token user.""" + mock_role_cache.is_api_token_user.return_value = False + mock_api_caller.role_cache = mock_role_cache + mock_api_caller._call_api.return_value = _to_dict(rerun_response) + + response = client.rerun(search_result) + + mock_role_cache.is_api_token_user.assert_called_once() + mock_api_caller._call_api.assert_called_once() + assert isinstance(response, WorkflowRunResponse) + mock_api_caller.reset_mock() + + +def test_run_with_role_cache_api_token_user( + client: WorkflowClient, + mock_api_caller, + mock_role_cache, + workflow_response: WorkflowResponse, +): + """Test that run uses package endpoint when user is API token user.""" + mock_role_cache.is_api_token_user.return_value = True + mock_api_caller.role_cache = mock_role_cache + mock_api_caller._call_api.return_value = _to_dict(workflow_response) + + workflow = Workflow( + metadata=WorkflowMetadata(name="name", namespace="namespace"), + spec=WorkflowSpec(), + payload=[PackageParameter(parameter="test-param", type="test-type", body={})], + ) + + response = client.run(workflow) + + mock_role_cache.is_api_token_user.assert_called_once() + mock_api_caller._call_api.assert_called_once() + assert isinstance(response, WorkflowResponse) + mock_api_caller.reset_mock() + + +def test_update_with_role_cache_api_token_user( + client: WorkflowClient, + mock_api_caller, + mock_role_cache, + workflow_response: WorkflowResponse, +): + """Test that update uses package endpoint when user is API token user.""" + mock_role_cache.is_api_token_user.return_value = True + mock_api_caller.role_cache = mock_role_cache + mock_api_caller._call_api.return_value = _to_dict(workflow_response) + + workflow = Workflow( + metadata=WorkflowMetadata(name="name", namespace="namespace"), + spec=WorkflowSpec(), + payload=[PackageParameter(parameter="test-param", type="test-type", body={})], + ) + + response = client.updater(workflow) + + mock_role_cache.is_api_token_user.assert_called_once() + mock_api_caller._call_api.assert_called_once() + assert isinstance(response, WorkflowResponse) + mock_api_caller.reset_mock() + + +def test_delete_with_role_cache_api_token_user( + client: WorkflowClient, + mock_api_caller, + mock_role_cache, +): + """Test that delete uses package endpoint when user is API token user.""" + mock_role_cache.is_api_token_user.return_value = True + mock_api_caller.role_cache = mock_role_cache + mock_api_caller._call_api.return_value = None + + client.delete("test-workflow-name") + + mock_role_cache.is_api_token_user.assert_called_once() + mock_api_caller._call_api.assert_called_once() + mock_api_caller.reset_mock() + + +def test_add_schedule_with_role_cache_api_token_user( + client: WorkflowClient, + mock_api_caller, + mock_role_cache, + search_result: WorkflowSearchResult, + schedule: WorkflowSchedule, + workflow_response: WorkflowResponse, +): + """Test that add_schedule uses package endpoint when user is API token user.""" + mock_role_cache.is_api_token_user.return_value = True + mock_api_caller.role_cache = mock_role_cache + mock_api_caller._call_api.return_value = _to_dict(workflow_response) + + response = client.add_schedule(search_result, schedule) + + mock_role_cache.is_api_token_user.assert_called_once() + mock_api_caller._call_api.assert_called_once() + assert isinstance(response, WorkflowResponse) + mock_api_caller.reset_mock() + + +def test_remove_schedule_with_role_cache_api_token_user( + client: WorkflowClient, + mock_api_caller, + mock_role_cache, + search_result: WorkflowSearchResult, + workflow_response: WorkflowResponse, +): + """Test that remove_schedule uses package endpoint when user is API token user.""" + mock_role_cache.is_api_token_user.return_value = True + mock_api_caller.role_cache = mock_role_cache + mock_api_caller._call_api.return_value = _to_dict(workflow_response) + + response = client.remove_schedule(search_result) + + mock_role_cache.is_api_token_user.assert_called_once() + mock_api_caller._call_api.assert_called_once() + assert isinstance(response, WorkflowResponse) + mock_api_caller.reset_mock() diff --git a/tests_v9/vcr_cassettes/tests_v9.unit.test_base_vcr_json/TestBaseVCRJSON.test_httpbin_delete.yaml b/tests_v9/vcr_cassettes/tests_v9.unit.test_base_vcr_json/TestBaseVCRJSON.test_httpbin_delete.yaml new file mode 100644 index 000000000..25128c3dd --- /dev/null +++ b/tests_v9/vcr_cassettes/tests_v9.unit.test_base_vcr_json/TestBaseVCRJSON.test_httpbin_delete.yaml @@ -0,0 +1,38 @@ +{ + "version": 1, + "interactions": [ + { + "request": { + "method": "DELETE", + "uri": "https://httpbin.org/delete", + "body": "", + "headers": {} + }, + "response": { + "status": { + "code": 200, + "message": "OK" + }, + "headers": {}, + "body": { + "parsed_json": { + "args": {}, + "data": "", + "files": {}, + "form": {}, + "headers": { + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate", + "Host": "httpbin.org", + "User-Agent": "python-httpx/0.28.1", + "X-Amzn-Trace-Id": "Root=1-6995eef6-62ac7c84064339d166fa5b4f" + }, + "json": null, + "origin": "103.172.73.42", + "url": "https://httpbin.org/delete" + } + } + } + } + ] +} diff --git a/tests_v9/vcr_cassettes/tests_v9.unit.test_base_vcr_json/TestBaseVCRJSON.test_httpbin_get.yaml b/tests_v9/vcr_cassettes/tests_v9.unit.test_base_vcr_json/TestBaseVCRJSON.test_httpbin_get.yaml new file mode 100644 index 000000000..c1110c4f2 --- /dev/null +++ b/tests_v9/vcr_cassettes/tests_v9.unit.test_base_vcr_json/TestBaseVCRJSON.test_httpbin_get.yaml @@ -0,0 +1,36 @@ +{ + "version": 1, + "interactions": [ + { + "request": { + "method": "GET", + "uri": "https://httpbin.org/get?test=value", + "body": "", + "headers": {} + }, + "response": { + "status": { + "code": 200, + "message": "OK" + }, + "headers": {}, + "body": { + "parsed_json": { + "args": { + "test": "value" + }, + "headers": { + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate", + "Host": "httpbin.org", + "User-Agent": "python-httpx/0.28.1", + "X-Amzn-Trace-Id": "Root=1-6995eeed-7cf7e76001c9d9552c3b890b" + }, + "origin": "103.172.73.42", + "url": "https://httpbin.org/get?test=value" + } + } + } + } + ] +} diff --git a/tests_v9/vcr_cassettes/tests_v9.unit.test_base_vcr_json/TestBaseVCRJSON.test_httpbin_post.yaml b/tests_v9/vcr_cassettes/tests_v9.unit.test_base_vcr_json/TestBaseVCRJSON.test_httpbin_post.yaml new file mode 100644 index 000000000..35df36179 --- /dev/null +++ b/tests_v9/vcr_cassettes/tests_v9.unit.test_base_vcr_json/TestBaseVCRJSON.test_httpbin_post.yaml @@ -0,0 +1,43 @@ +{ + "version": 1, + "interactions": [ + { + "request": { + "method": "POST", + "uri": "https://httpbin.org/post", + "body": "{\"name\":\"atlan\",\"type\":\"integration-test\"}", + "headers": {} + }, + "response": { + "status": { + "code": 200, + "message": "OK" + }, + "headers": {}, + "body": { + "parsed_json": { + "args": {}, + "data": "{\"name\":\"atlan\",\"type\":\"integration-test\"}", + "files": {}, + "form": {}, + "headers": { + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate", + "Content-Length": "42", + "Content-Type": "application/json", + "Host": "httpbin.org", + "User-Agent": "python-httpx/0.28.1", + "X-Amzn-Trace-Id": "Root=1-6995eef2-3b74f2fc436ab3ec5af8c570" + }, + "json": { + "name": "atlan", + "type": "integration-test" + }, + "origin": "103.172.73.42", + "url": "https://httpbin.org/post" + } + } + } + } + ] +} diff --git a/tests_v9/vcr_cassettes/tests_v9.unit.test_base_vcr_json/TestBaseVCRJSON.test_httpbin_put.yaml b/tests_v9/vcr_cassettes/tests_v9.unit.test_base_vcr_json/TestBaseVCRJSON.test_httpbin_put.yaml new file mode 100644 index 000000000..16858f5c7 --- /dev/null +++ b/tests_v9/vcr_cassettes/tests_v9.unit.test_base_vcr_json/TestBaseVCRJSON.test_httpbin_put.yaml @@ -0,0 +1,42 @@ +{ + "version": 1, + "interactions": [ + { + "request": { + "method": "PUT", + "uri": "https://httpbin.org/put", + "body": "{\"update\":\"value\"}", + "headers": {} + }, + "response": { + "status": { + "code": 200, + "message": "OK" + }, + "headers": {}, + "body": { + "parsed_json": { + "args": {}, + "data": "{\"update\":\"value\"}", + "files": {}, + "form": {}, + "headers": { + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate", + "Content-Length": "18", + "Content-Type": "application/json", + "Host": "httpbin.org", + "User-Agent": "python-httpx/0.28.1", + "X-Amzn-Trace-Id": "Root=1-6995eef4-5ba92a573ab8994101a12adf" + }, + "json": { + "update": "value" + }, + "origin": "103.172.73.42", + "url": "https://httpbin.org/put" + } + } + } + } + ] +} diff --git a/tests_v9/vcr_cassettes/tests_v9.unit.test_base_vcr_yaml/TestBaseVCRYAML.test_httpbin_delete.yaml b/tests_v9/vcr_cassettes/tests_v9.unit.test_base_vcr_yaml/TestBaseVCRYAML.test_httpbin_delete.yaml new file mode 100644 index 000000000..e5d384c7b --- /dev/null +++ b/tests_v9/vcr_cassettes/tests_v9.unit.test_base_vcr_yaml/TestBaseVCRYAML.test_httpbin_delete.yaml @@ -0,0 +1,30 @@ +interactions: +- request: + body: '' + headers: {} + method: DELETE + uri: https://httpbin.org/delete + response: + body: + string: |- + { + "args": {}, + "data": "", + "files": {}, + "form": {}, + "headers": { + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate", + "Host": "httpbin.org", + "User-Agent": "python-httpx/0.28.1", + "X-Amzn-Trace-Id": "Root=1-6995eefd-0b73e3e63cf7281d62fabecd" + }, + "json": null, + "origin": "103.172.73.42", + "url": "https://httpbin.org/delete" + } + headers: {} + status: + code: 200 + message: OK +version: 1 diff --git a/tests_v9/vcr_cassettes/tests_v9.unit.test_base_vcr_yaml/TestBaseVCRYAML.test_httpbin_get.yaml b/tests_v9/vcr_cassettes/tests_v9.unit.test_base_vcr_yaml/TestBaseVCRYAML.test_httpbin_get.yaml new file mode 100644 index 000000000..deacdc093 --- /dev/null +++ b/tests_v9/vcr_cassettes/tests_v9.unit.test_base_vcr_yaml/TestBaseVCRYAML.test_httpbin_get.yaml @@ -0,0 +1,28 @@ +interactions: +- request: + body: '' + headers: {} + method: GET + uri: https://httpbin.org/get?test=value + response: + body: + string: |- + { + "args": { + "test": "value" + }, + "headers": { + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate", + "Host": "httpbin.org", + "User-Agent": "python-httpx/0.28.1", + "X-Amzn-Trace-Id": "Root=1-6995eef7-564ff9a61cd33b3c3eaad064" + }, + "origin": "103.172.73.42", + "url": "https://httpbin.org/get?test=value" + } + headers: {} + status: + code: 200 + message: OK +version: 1 diff --git a/tests_v9/vcr_cassettes/tests_v9.unit.test_base_vcr_yaml/TestBaseVCRYAML.test_httpbin_post.yaml b/tests_v9/vcr_cassettes/tests_v9.unit.test_base_vcr_yaml/TestBaseVCRYAML.test_httpbin_post.yaml new file mode 100644 index 000000000..ece0f686f --- /dev/null +++ b/tests_v9/vcr_cassettes/tests_v9.unit.test_base_vcr_yaml/TestBaseVCRYAML.test_httpbin_post.yaml @@ -0,0 +1,39 @@ +interactions: +- request: + body: |- + { + "name": "atlan", + "type": "integration-test" + } + headers: {} + method: POST + uri: https://httpbin.org/post + response: + body: + string: |- + { + "args": {}, + "data": "{\"name\":\"atlan\",\"type\":\"integration-test\"}", + "files": {}, + "form": {}, + "headers": { + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate", + "Content-Length": "42", + "Content-Type": "application/json", + "Host": "httpbin.org", + "User-Agent": "python-httpx/0.28.1", + "X-Amzn-Trace-Id": "Root=1-6995eefa-663ba83434c0ec1d2afe0d49" + }, + "json": { + "name": "atlan", + "type": "integration-test" + }, + "origin": "103.172.73.42", + "url": "https://httpbin.org/post" + } + headers: {} + status: + code: 200 + message: OK +version: 1 diff --git a/tests_v9/vcr_cassettes/tests_v9.unit.test_base_vcr_yaml/TestBaseVCRYAML.test_httpbin_put.yaml b/tests_v9/vcr_cassettes/tests_v9.unit.test_base_vcr_yaml/TestBaseVCRYAML.test_httpbin_put.yaml new file mode 100644 index 000000000..61d984b39 --- /dev/null +++ b/tests_v9/vcr_cassettes/tests_v9.unit.test_base_vcr_yaml/TestBaseVCRYAML.test_httpbin_put.yaml @@ -0,0 +1,37 @@ +interactions: +- request: + body: |- + { + "update": "value" + } + headers: {} + method: PUT + uri: https://httpbin.org/put + response: + body: + string: |- + { + "args": {}, + "data": "{\"update\":\"value\"}", + "files": {}, + "form": {}, + "headers": { + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate", + "Content-Length": "18", + "Content-Type": "application/json", + "Host": "httpbin.org", + "User-Agent": "python-httpx/0.28.1", + "X-Amzn-Trace-Id": "Root=1-6995eefb-3eb277a81a6c74a8727744cd" + }, + "json": { + "update": "value" + }, + "origin": "103.172.73.42", + "url": "https://httpbin.org/put" + } + headers: {} + status: + code: 200 + message: OK +version: 1 diff --git a/uv.lock b/uv.lock index 693eaa55a..b7d129e2d 100644 --- a/uv.lock +++ b/uv.lock @@ -59,14 +59,14 @@ wheels = [ [[package]] name = "authlib" -version = "1.6.6" +version = "1.6.9" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cryptography", marker = "python_full_version < '3.14' and platform_python_implementation == 'CPython'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/bb/9b/b1661026ff24bc641b76b78c5222d614776b0c085bcfdac9bd15a1cb4b35/authlib-1.6.6.tar.gz", hash = "sha256:45770e8e056d0f283451d9996fbb59b70d45722b45d854d58f32878d0a40c38e", size = 164894, upload-time = "2025-12-12T08:01:41.464Z" } +sdist = { url = "https://files.pythonhosted.org/packages/af/98/00d3dd826d46959ad8e32af2dbb2398868fd9fd0683c26e56d0789bd0e68/authlib-1.6.9.tar.gz", hash = "sha256:d8f2421e7e5980cc1ddb4e32d3f5fa659cfaf60d8eaf3281ebed192e4ab74f04", size = 165134, upload-time = "2026-03-02T07:44:01.998Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/54/51/321e821856452f7386c4e9df866f196720b1ad0c5ea1623ea7399969ae3b/authlib-1.6.6-py2.py3-none-any.whl", hash = "sha256:7d9e9bc535c13974313a87f53e8430eb6ea3d1cf6ae4f6efcd793f2e949143fd", size = 244005, upload-time = "2025-12-12T08:01:40.209Z" }, + { url = "https://files.pythonhosted.org/packages/53/23/b65f568ed0c22f1efacb744d2db1a33c8068f384b8c9b482b52ebdbc3ef6/authlib-1.6.9-py2.py3-none-any.whl", hash = "sha256:f08b4c14e08f0861dc18a32357b33fbcfd2ea86cfe3fe149484b4d764c4a0ac3", size = 244197, upload-time = "2026-03-02T07:44:00.307Z" }, ] [[package]] @@ -985,6 +985,70 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2b/9f/7ba6f94fc1e9ac3d2b853fdff3035fb2fa5afbed898c4a72b8a020610594/more_itertools-10.7.0-py3-none-any.whl", hash = "sha256:d43980384673cb07d2f7d2d918c616b30c659c089ee23953f601d6609c67510e", size = 65278, upload-time = "2025-04-22T14:17:40.49Z" }, ] +[[package]] +name = "msgspec" +version = "0.20.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ea/9c/bfbd12955a49180cbd234c5d29ec6f74fe641698f0cd9df154a854fc8a15/msgspec-0.20.0.tar.gz", hash = "sha256:692349e588fde322875f8d3025ac01689fead5901e7fb18d6870a44519d62a29", size = 317862, upload-time = "2025-11-24T03:56:28.934Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e3/5e/151883ba2047cca9db8ed2f86186b054ad200bc231352df15b0c1dd75b1f/msgspec-0.20.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:23a6ec2a3b5038c233b04740a545856a068bc5cb8db184ff493a58e08c994fbf", size = 195191, upload-time = "2025-11-24T03:55:08.549Z" }, + { url = "https://files.pythonhosted.org/packages/50/88/a795647672f547c983eff0823b82aaa35db922c767e1b3693e2dcf96678d/msgspec-0.20.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cde2c41ed3eaaef6146365cb0d69580078a19f974c6cb8165cc5dcd5734f573e", size = 188513, upload-time = "2025-11-24T03:55:10.008Z" }, + { url = "https://files.pythonhosted.org/packages/4b/91/eb0abb0e0de142066cebfe546dc9140c5972ea824aa6ff507ad0b6a126ac/msgspec-0.20.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5da0daa782f95d364f0d95962faed01e218732aa1aa6cad56b25a5d2092e75a4", size = 216370, upload-time = "2025-11-24T03:55:11.566Z" }, + { url = "https://files.pythonhosted.org/packages/15/2a/48e41d9ef0a24b1c6e67cbd94a676799e0561bfbc163be1aaaff5ca853f5/msgspec-0.20.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9369d5266144bef91be2940a3821e03e51a93c9080fde3ef72728c3f0a3a8bb7", size = 222653, upload-time = "2025-11-24T03:55:13.159Z" }, + { url = "https://files.pythonhosted.org/packages/90/c9/14b825df203d980f82a623450d5f39e7f7a09e6e256c52b498ea8f29d923/msgspec-0.20.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:90fb865b306ca92c03964a5f3d0cd9eb1adda14f7e5ac7943efd159719ea9f10", size = 222337, upload-time = "2025-11-24T03:55:14.777Z" }, + { url = "https://files.pythonhosted.org/packages/8b/d7/39a5c3ddd294f587d6fb8efccc8361b6aa5089974015054071e665c9d24b/msgspec-0.20.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e8112cd48b67dfc0cfa49fc812b6ce7eb37499e1d95b9575061683f3428975d3", size = 225565, upload-time = "2025-11-24T03:55:16.4Z" }, + { url = "https://files.pythonhosted.org/packages/98/bd/5db3c14d675ee12842afb9b70c94c64f2c873f31198c46cbfcd7dffafab0/msgspec-0.20.0-cp310-cp310-win_amd64.whl", hash = "sha256:666b966d503df5dc27287675f525a56b6e66a2b8e8ccd2877b0c01328f19ae6c", size = 188412, upload-time = "2025-11-24T03:55:17.747Z" }, + { url = "https://files.pythonhosted.org/packages/76/c7/06cc218bc0c86f0c6c6f34f7eeea6cfb8b835070e8031e3b0ef00f6c7c69/msgspec-0.20.0-cp310-cp310-win_arm64.whl", hash = "sha256:099e3e85cd5b238f2669621be65f0728169b8c7cb7ab07f6137b02dc7feea781", size = 173951, upload-time = "2025-11-24T03:55:19.335Z" }, + { url = "https://files.pythonhosted.org/packages/03/59/fdcb3af72f750a8de2bcf39d62ada70b5eb17b06d7f63860e0a679cb656b/msgspec-0.20.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:09e0efbf1ac641fedb1d5496c59507c2f0dc62a052189ee62c763e0aae217520", size = 193345, upload-time = "2025-11-24T03:55:20.613Z" }, + { url = "https://files.pythonhosted.org/packages/5a/15/3c225610da9f02505d37d69a77f4a2e7daae2a125f99d638df211ba84e59/msgspec-0.20.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:23ee3787142e48f5ee746b2909ce1b76e2949fbe0f97f9f6e70879f06c218b54", size = 186867, upload-time = "2025-11-24T03:55:22.4Z" }, + { url = "https://files.pythonhosted.org/packages/81/36/13ab0c547e283bf172f45491edfdea0e2cecb26ae61e3a7b1ae6058b326d/msgspec-0.20.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:81f4ac6f0363407ac0465eff5c7d4d18f26870e00674f8fcb336d898a1e36854", size = 215351, upload-time = "2025-11-24T03:55:23.958Z" }, + { url = "https://files.pythonhosted.org/packages/6b/96/5c095b940de3aa6b43a71ec76275ac3537b21bd45c7499b5a17a429110fa/msgspec-0.20.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bb4d873f24ae18cd1334f4e37a178ed46c9d186437733351267e0a269bdf7e53", size = 219896, upload-time = "2025-11-24T03:55:25.356Z" }, + { url = "https://files.pythonhosted.org/packages/98/7a/81a7b5f01af300761087b114dafa20fb97aed7184d33aab64d48874eb187/msgspec-0.20.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b92b8334427b8393b520c24ff53b70f326f79acf5f74adb94fd361bcff8a1d4e", size = 220389, upload-time = "2025-11-24T03:55:26.99Z" }, + { url = "https://files.pythonhosted.org/packages/70/c0/3d0cce27db9a9912421273d49eab79ce01ecd2fed1a2f1b74af9b445f33c/msgspec-0.20.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:562c44b047c05cc0384e006fae7a5e715740215c799429e0d7e3e5adf324285a", size = 223348, upload-time = "2025-11-24T03:55:28.311Z" }, + { url = "https://files.pythonhosted.org/packages/89/5e/406b7d578926b68790e390d83a1165a9bfc2d95612a1a9c1c4d5c72ea815/msgspec-0.20.0-cp311-cp311-win_amd64.whl", hash = "sha256:d1dcc93a3ce3d3195985bfff18a48274d0b5ffbc96fa1c5b89da6f0d9af81b29", size = 188713, upload-time = "2025-11-24T03:55:29.553Z" }, + { url = "https://files.pythonhosted.org/packages/47/87/14fe2316624ceedf76a9e94d714d194cbcb699720b210ff189f89ca4efd7/msgspec-0.20.0-cp311-cp311-win_arm64.whl", hash = "sha256:aa387aa330d2e4bd69995f66ea8fdc87099ddeedf6fdb232993c6a67711e7520", size = 174229, upload-time = "2025-11-24T03:55:31.107Z" }, + { url = "https://files.pythonhosted.org/packages/d9/6f/1e25eee957e58e3afb2a44b94fa95e06cebc4c236193ed0de3012fff1e19/msgspec-0.20.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2aba22e2e302e9231e85edc24f27ba1f524d43c223ef5765bd8624c7df9ec0a5", size = 196391, upload-time = "2025-11-24T03:55:32.677Z" }, + { url = "https://files.pythonhosted.org/packages/7f/ee/af51d090ada641d4b264992a486435ba3ef5b5634bc27e6eb002f71cef7d/msgspec-0.20.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:716284f898ab2547fedd72a93bb940375de9fbfe77538f05779632dc34afdfde", size = 188644, upload-time = "2025-11-24T03:55:33.934Z" }, + { url = "https://files.pythonhosted.org/packages/49/d6/9709ee093b7742362c2934bfb1bbe791a1e09bed3ea5d8a18ce552fbfd73/msgspec-0.20.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:558ed73315efa51b1538fa8f1d3b22c8c5ff6d9a2a62eff87d25829b94fc5054", size = 218852, upload-time = "2025-11-24T03:55:35.575Z" }, + { url = "https://files.pythonhosted.org/packages/5c/a2/488517a43ccf5a4b6b6eca6dd4ede0bd82b043d1539dd6bb908a19f8efd3/msgspec-0.20.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:509ac1362a1d53aa66798c9b9fd76872d7faa30fcf89b2fba3bcbfd559d56eb0", size = 224937, upload-time = "2025-11-24T03:55:36.859Z" }, + { url = "https://files.pythonhosted.org/packages/d5/e8/49b832808aa23b85d4f090d1d2e48a4e3834871415031ed7c5fe48723156/msgspec-0.20.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1353c2c93423602e7dea1aa4c92f3391fdfc25ff40e0bacf81d34dbc68adb870", size = 222858, upload-time = "2025-11-24T03:55:38.187Z" }, + { url = "https://files.pythonhosted.org/packages/9f/56/1dc2fa53685dca9c3f243a6cbecd34e856858354e455b77f47ebd76cf5bf/msgspec-0.20.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:cb33b5eb5adb3c33d749684471c6a165468395d7aa02d8867c15103b81e1da3e", size = 227248, upload-time = "2025-11-24T03:55:39.496Z" }, + { url = "https://files.pythonhosted.org/packages/5a/51/aba940212c23b32eedce752896205912c2668472ed5b205fc33da28a6509/msgspec-0.20.0-cp312-cp312-win_amd64.whl", hash = "sha256:fb1d934e435dd3a2b8cf4bbf47a8757100b4a1cfdc2afdf227541199885cdacb", size = 190024, upload-time = "2025-11-24T03:55:40.829Z" }, + { url = "https://files.pythonhosted.org/packages/41/ad/3b9f259d94f183daa9764fef33fdc7010f7ecffc29af977044fa47440a83/msgspec-0.20.0-cp312-cp312-win_arm64.whl", hash = "sha256:00648b1e19cf01b2be45444ba9dc961bd4c056ffb15706651e64e5d6ec6197b7", size = 175390, upload-time = "2025-11-24T03:55:42.05Z" }, + { url = "https://files.pythonhosted.org/packages/8a/d1/b902d38b6e5ba3bdddbec469bba388d647f960aeed7b5b3623a8debe8a76/msgspec-0.20.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9c1ff8db03be7598b50dd4b4a478d6fe93faae3bd54f4f17aa004d0e46c14c46", size = 196463, upload-time = "2025-11-24T03:55:43.405Z" }, + { url = "https://files.pythonhosted.org/packages/57/b6/eff0305961a1d9447ec2b02f8c73c8946f22564d302a504185b730c9a761/msgspec-0.20.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f6532369ece217fd37c5ebcfd7e981f2615628c21121b7b2df9d3adcf2fd69b8", size = 188650, upload-time = "2025-11-24T03:55:44.761Z" }, + { url = "https://files.pythonhosted.org/packages/99/93/f2ec1ae1de51d3fdee998a1ede6b2c089453a2ee82b5c1b361ed9095064a/msgspec-0.20.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f9a1697da2f85a751ac3cc6a97fceb8e937fc670947183fb2268edaf4016d1ee", size = 218834, upload-time = "2025-11-24T03:55:46.441Z" }, + { url = "https://files.pythonhosted.org/packages/28/83/36557b04cfdc317ed8a525c4993b23e43a8fbcddaddd78619112ca07138c/msgspec-0.20.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7fac7e9c92eddcd24c19d9e5f6249760941485dff97802461ae7c995a2450111", size = 224917, upload-time = "2025-11-24T03:55:48.06Z" }, + { url = "https://files.pythonhosted.org/packages/8f/56/362037a1ed5be0b88aced59272442c4b40065c659700f4b195a7f4d0ac88/msgspec-0.20.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f953a66f2a3eb8d5ea64768445e2bb301d97609db052628c3e1bcb7d87192a9f", size = 222821, upload-time = "2025-11-24T03:55:49.388Z" }, + { url = "https://files.pythonhosted.org/packages/92/75/fa2370ec341cedf663731ab7042e177b3742645c5dd4f64dc96bd9f18a6b/msgspec-0.20.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:247af0313ae64a066d3aea7ba98840f6681ccbf5c90ba9c7d17f3e39dbba679c", size = 227227, upload-time = "2025-11-24T03:55:51.125Z" }, + { url = "https://files.pythonhosted.org/packages/f1/25/5e8080fe0117f799b1b68008dc29a65862077296b92550632de015128579/msgspec-0.20.0-cp313-cp313-win_amd64.whl", hash = "sha256:67d5e4dfad52832017018d30a462604c80561aa62a9d548fc2bd4e430b66a352", size = 189966, upload-time = "2025-11-24T03:55:52.458Z" }, + { url = "https://files.pythonhosted.org/packages/79/b6/63363422153937d40e1cb349c5081338401f8529a5a4e216865decd981bf/msgspec-0.20.0-cp313-cp313-win_arm64.whl", hash = "sha256:91a52578226708b63a9a13de287b1ec3ed1123e4a088b198143860c087770458", size = 175378, upload-time = "2025-11-24T03:55:53.721Z" }, + { url = "https://files.pythonhosted.org/packages/bb/18/62dc13ab0260c7d741dda8dc7f481495b93ac9168cd887dda5929880eef8/msgspec-0.20.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:eead16538db1b3f7ec6e3ed1f6f7c5dec67e90f76e76b610e1ffb5671815633a", size = 196407, upload-time = "2025-11-24T03:55:55.001Z" }, + { url = "https://files.pythonhosted.org/packages/dd/1d/b9949e4ad6953e9f9a142c7997b2f7390c81e03e93570c7c33caf65d27e1/msgspec-0.20.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:703c3bb47bf47801627fb1438f106adbfa2998fe586696d1324586a375fca238", size = 188889, upload-time = "2025-11-24T03:55:56.311Z" }, + { url = "https://files.pythonhosted.org/packages/1e/19/f8bb2dc0f1bfe46cc7d2b6b61c5e9b5a46c62298e8f4d03bbe499c926180/msgspec-0.20.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6cdb227dc585fb109305cee0fd304c2896f02af93ecf50a9c84ee54ee67dbb42", size = 219691, upload-time = "2025-11-24T03:55:57.908Z" }, + { url = "https://files.pythonhosted.org/packages/b8/8e/6b17e43f6eb9369d9858ee32c97959fcd515628a1df376af96c11606cf70/msgspec-0.20.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:27d35044dd8818ac1bd0fedb2feb4fbdff4e3508dd7c5d14316a12a2d96a0de0", size = 224918, upload-time = "2025-11-24T03:55:59.322Z" }, + { url = "https://files.pythonhosted.org/packages/1c/db/0e833a177db1a4484797adba7f429d4242585980b90882cc38709e1b62df/msgspec-0.20.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b4296393a29ee42dd25947981c65506fd4ad39beaf816f614146fa0c5a6c91ae", size = 223436, upload-time = "2025-11-24T03:56:00.716Z" }, + { url = "https://files.pythonhosted.org/packages/c3/30/d2ee787f4c918fd2b123441d49a7707ae9015e0e8e1ab51aa7967a97b90e/msgspec-0.20.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:205fbdadd0d8d861d71c8f3399fe1a82a2caf4467bc8ff9a626df34c12176980", size = 227190, upload-time = "2025-11-24T03:56:02.371Z" }, + { url = "https://files.pythonhosted.org/packages/ff/37/9c4b58ff11d890d788e700b827db2366f4d11b3313bf136780da7017278b/msgspec-0.20.0-cp314-cp314-win_amd64.whl", hash = "sha256:7dfebc94fe7d3feec6bc6c9df4f7e9eccc1160bb5b811fbf3e3a56899e398a6b", size = 193950, upload-time = "2025-11-24T03:56:03.668Z" }, + { url = "https://files.pythonhosted.org/packages/e9/4e/cab707bf2fa57408e2934e5197fc3560079db34a1e3cd2675ff2e47e07de/msgspec-0.20.0-cp314-cp314-win_arm64.whl", hash = "sha256:2ad6ae36e4a602b24b4bf4eaf8ab5a441fec03e1f1b5931beca8ebda68f53fc0", size = 179018, upload-time = "2025-11-24T03:56:05.038Z" }, + { url = "https://files.pythonhosted.org/packages/4c/06/3da3fc9aaa55618a8f43eb9052453cfe01f82930bca3af8cea63a89f3a11/msgspec-0.20.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:f84703e0e6ef025663dd1de828ca028774797b8155e070e795c548f76dde65d5", size = 200389, upload-time = "2025-11-24T03:56:06.375Z" }, + { url = "https://files.pythonhosted.org/packages/83/3b/cc4270a5ceab40dfe1d1745856951b0a24fd16ac8539a66ed3004a60c91e/msgspec-0.20.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7c83fc24dd09cf1275934ff300e3951b3adc5573f0657a643515cc16c7dee131", size = 193198, upload-time = "2025-11-24T03:56:07.742Z" }, + { url = "https://files.pythonhosted.org/packages/cd/ae/4c7905ac53830c8e3c06fdd60e3cdcfedc0bbc993872d1549b84ea21a1bd/msgspec-0.20.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f13ccb1c335a124e80c4562573b9b90f01ea9521a1a87f7576c2e281d547f56", size = 225973, upload-time = "2025-11-24T03:56:09.18Z" }, + { url = "https://files.pythonhosted.org/packages/d9/da/032abac1de4d0678d99eaeadb1323bd9d247f4711c012404ba77ed6f15ca/msgspec-0.20.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:17c2b5ca19f19306fc83c96d85e606d2cc107e0caeea85066b5389f664e04846", size = 229509, upload-time = "2025-11-24T03:56:10.898Z" }, + { url = "https://files.pythonhosted.org/packages/69/52/fdc7bdb7057a166f309e0b44929e584319e625aaba4771b60912a9321ccd/msgspec-0.20.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d931709355edabf66c2dd1a756b2d658593e79882bc81aae5964969d5a291b63", size = 230434, upload-time = "2025-11-24T03:56:12.48Z" }, + { url = "https://files.pythonhosted.org/packages/cb/fe/1dfd5f512b26b53043884e4f34710c73e294e7cc54278c3fe28380e42c37/msgspec-0.20.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:565f915d2e540e8a0c93a01ff67f50aebe1f7e22798c6a25873f9fda8d1325f8", size = 231758, upload-time = "2025-11-24T03:56:13.765Z" }, + { url = "https://files.pythonhosted.org/packages/97/f6/9ba7121b8e0c4e0beee49575d1dbc804e2e72467692f0428cf39ceba1ea5/msgspec-0.20.0-cp314-cp314t-win_amd64.whl", hash = "sha256:726f3e6c3c323f283f6021ebb6c8ccf58d7cd7baa67b93d73bfbe9a15c34ab8d", size = 206540, upload-time = "2025-11-24T03:56:15.029Z" }, + { url = "https://files.pythonhosted.org/packages/c8/3e/c5187de84bb2c2ca334ab163fcacf19a23ebb1d876c837f81a1b324a15bf/msgspec-0.20.0-cp314-cp314t-win_arm64.whl", hash = "sha256:93f23528edc51d9f686808a361728e903d6f2be55c901d6f5c92e44c6d546bfc", size = 183011, upload-time = "2025-11-24T03:56:16.442Z" }, + { url = "https://files.pythonhosted.org/packages/b2/30/55eb8645bf11ea84bc1dafa670d068348b08b84660c4c9240ff05296e707/msgspec-0.20.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eee56472ced14602245ac47516e179d08c6c892d944228796f239e983de7449c", size = 195293, upload-time = "2025-11-24T03:56:17.763Z" }, + { url = "https://files.pythonhosted.org/packages/b1/c2/78c66d69beb45c311ba6ad0021f31ddfe6f19fe1b46cf295175fbb41430d/msgspec-0.20.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:19395e9a08cc5bd0e336909b3e13b4ae5ee5e47b82e98f8b7801d5a13806bb6f", size = 188572, upload-time = "2025-11-24T03:56:19.431Z" }, + { url = "https://files.pythonhosted.org/packages/44/14/9d6f685a277e4d3417f103c4d228cb7ea83fdd776c739570f233917f5fd2/msgspec-0.20.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d5bb7ce84fe32f6ce9f62aa7e7109cb230ad542cc5bc9c46e587f1dac4afc48e", size = 216219, upload-time = "2025-11-24T03:56:20.823Z" }, + { url = "https://files.pythonhosted.org/packages/98/24/e50ea4080656a711bee9fe3d846de3b0e74f03c1dc620284b82e1757fdb0/msgspec-0.20.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8c6da9ae2d76d11181fbb0ea598f6e1d558ef597d07ec46d689d17f68133769f", size = 222573, upload-time = "2025-11-24T03:56:22.17Z" }, + { url = "https://files.pythonhosted.org/packages/d1/4b/2d9415a935ebd6e5f34fd5cad7be6b8525d8353bf5ed6eb77e706863f3b0/msgspec-0.20.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:84d88bd27d906c471a5ca232028671db734111996ed1160e37171a8d1f07a599", size = 222097, upload-time = "2025-11-24T03:56:23.553Z" }, + { url = "https://files.pythonhosted.org/packages/b3/56/2cc277def0d43625dd14ab6ee0e3a5198175725198122d707fa139ebbdd1/msgspec-0.20.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:03907bf733f94092a6b4c5285b274f79947cad330bd8a9d8b45c0369e1a3c7f0", size = 225419, upload-time = "2025-11-24T03:56:24.953Z" }, + { url = "https://files.pythonhosted.org/packages/42/1d/e9401b352aa399af5efa35f1f130651698e65f919ecb9221b925b2236948/msgspec-0.20.0-cp39-cp39-win_amd64.whl", hash = "sha256:9fbcb660632a2f5c247c0dc820212bf3a423357ac6241ff6dc6cfc6f72584016", size = 188527, upload-time = "2025-11-24T03:56:26.193Z" }, + { url = "https://files.pythonhosted.org/packages/02/59/079f33cd092ee42c9b97a59daa2115e7550a7eba98781ef6657e3d710d56/msgspec-0.20.0-cp39-cp39-win_arm64.whl", hash = "sha256:f7cd0e89b86a16005745cb99bd1858e8050fc17f63de571504492b267bca188a", size = 173927, upload-time = "2025-11-24T03:56:27.52Z" }, +] + [[package]] name = "multidict" version = "6.6.4" @@ -1412,6 +1476,7 @@ dependencies = [ { name = "httpx-retries", marker = "python_full_version < '3.14' and platform_python_implementation == 'CPython'" }, { name = "jinja2", marker = "python_full_version < '3.14' and platform_python_implementation == 'CPython'" }, { name = "lazy-loader", marker = "python_full_version < '3.14' and platform_python_implementation == 'CPython'" }, + { name = "msgspec", marker = "python_full_version < '3.14' and platform_python_implementation == 'CPython'" }, { name = "nanoid", marker = "python_full_version < '3.14' and platform_python_implementation == 'CPython'" }, { name = "pydantic", marker = "python_full_version < '3.14' and platform_python_implementation == 'CPython'" }, { name = "python-dateutil", marker = "python_full_version < '3.14' and platform_python_implementation == 'CPython'" }, @@ -1457,11 +1522,12 @@ docs = [ [package.metadata] requires-dist = [ - { name = "authlib", specifier = "~=1.6.6" }, + { name = "authlib", specifier = "~=1.6.9" }, { name = "httpx", specifier = "~=0.28.1" }, { name = "httpx-retries", specifier = "~=0.4.5" }, { name = "jinja2", specifier = "~=3.1.6" }, { name = "lazy-loader", specifier = "~=0.4" }, + { name = "msgspec", specifier = "~=0.20.0" }, { name = "nanoid", specifier = "~=2.0.0" }, { name = "pydantic", specifier = "~=2.12.4" }, { name = "python-dateutil", specifier = "~=2.9.0.post0" },